Commit Graph
5 Commits
Author SHA1 Message Date
Neil 26721bd632 fix(codex): stop blocking the main thread on trust grants (#16441) (#16594)
* fix(codex): stop blocking the main thread on trust grants (#16441)

Codex hook trust was granted by blocking the Electron main thread on
`spawnSync` of a bundled ELECTRON_RUN_AS_NODE entry for the whole
app-server deadline: 15s native, 35s WSL, ~45s on the real-home path
(rebase inspect + repair + grant). Cold start and every Codex pane
launch showed "Not Responding"; the reported event-loop gap was
15,049 ms.

The subprocess only ever existed to donate an event loop to a
deliberately blocked parent — `runCodexHookTrustGrantSession` was
already the real async implementation. Make the callers async and the
fork is unnecessary, so the bridge, the forked entry and its envelope
are deleted along with their build/knip/tsconfig registrations. The CLI
`agent hooks prepare-codex` handler is already async, so it awaits the
in-process session and saves a process spawn per managed-home shell.

`resolveCodexTrustGrantHost` is async too; the WSL identity probe moves
from `execFileSync` to `runProcess`, dropping that file from the
child-process import allowlist. Status reads keep a synchronous
native-only stamp path.

Two invariants that held only because the lane blocked:

- Overlapping capability probes were impossible by construction.
  `GitCapabilityCache`'s dedupe engine is extracted to a shared
  `CapabilityProbeCache` and `CodexAppServerCapabilityCache` now
  inherits it, so concurrent launches against a cold host share one
  app-server session instead of one each.
- Two grants on one `config.toml` could not interleave capture and
  restore. A reentrant per-file lane now serializes the whole install
  sequence (managed, WSL runtime, real-home ensure, legacy sweep) and
  the grant and rebase inside it.

Cold-start work moves off the critical path: retained-home
reconciliation (N sequential sessions) is fire-and-forget behind the
daemon provider, and the startup real-home ensure chains into managed
hook reconciliation instead of blocking app init.

Every preserved semantic is unchanged: never throws, the
ORCA_DISABLE_CODEX_TRUST_RPC kill switch, ledger hits, backfill-pending
and cooldown fallbacks, config rollback on every failure path,
pre-grant self-computed trust removal, the verify-failure taxonomy,
diagnostics and telemetry.

* fix(codex): widen the trust-config lane to every config.toml writer

Review follow-ups on #16441's async trust grant:

- `markCodexProjectTrusted` now runs inside the runtime+system config.toml
  lanes, so a project-trust write can no longer land inside a hook grant's
  capture->restore window and be silently reverted. Its callers await it.
- `install`/`refreshRuntimeUserHooks`/`remove` hold the system config.toml
  lane as well as the runtime one — they promote approvals into
  ~/.codex/config.toml and mirror it back. Lock order is runtime-before-system
  everywhere.
- The real-home ensure chain resumes after a rejection instead of returning
  the same rejected promise to every later pane launch, and resolving the real
  home is now inside the module's never-throws boundary.
- `buildSpawnEnv` awaits inside a cancelable pending-spawn registration, so
  shutdown during the (now long) env build stops the PTY from launching.
  `prepareLocalPtySpawn` generalizes into `awaitCancelableLocalPtySpawn`.
- CapabilityProbeCache drops the test-only `nowMs` passthrough; its probe
  backstop comment now describes what it actually guards.
- Preflight is a plain async function; the trust dispatch in orca-runtime
  collapses into one `markWorkspaceTrustedForAgent`.

* test(codex): exercise the trust-config lane under real concurrency

The async grant makes two pane launches overlap for the first time. These
drive the real modules end to end on real files: a rollback swallowing a
sibling's grant, a markCodexProjectTrusted write landing inside a capture
-> restore window, shared capability-probe dedupe on a cold host, the
host-scoped transient cooldown, and reentrancy from inside an installer.

Each was verified to fail against a deliberately broken implementation
(lane removed, dedupe disabled, cooldown made global, reentrancy pass-
through disabled).

* test(codex): stop hook-service suites spawning the developer's real codex

The forked grant bundle never existed under vitest, so the RPC lane was
unreachable in tests on main. Running it in-process makes these suites
spawn a real `codex app-server` when one is installed: 38 spawns and two
failures in hook-service-runtime-trust-repair on a machine with codex,
green in CI where there is none. Stand in for the missing binary so both
environments exercise the same fallback lane.

* docs(codex): scope the trust-RPC kill switch comment to what it actually gates

The comment read as though the flag forces the fallback lane everywhere. It
gates the managed grant only: the real-home rebase still runs its own
inspect/repair app-server sessions when Orca's insertion shifts a user's hook
positions, and never reads the flag.

Verified by exercise, not by reading — with the flag set, both
inspect-user-hook-trust and repair-user-hook-trust still ran. Pre-existing:
main has no check there either, it just blocked the main thread while doing it.

Widening the flag to cover the rebase is a follow-up; this only stops the
comment promising something the constant does not do.
2026-08-26 16:44:55 -07:00
Brennan Benson 5df2ddbc9c perf(ai-vault): isolate tab title resolution (#13377)
* perf(ai-vault): isolate tab title resolution

* fix(ai-vault): preserve background scan caches

* fix(ai-vault): resolve nested worker from chunks
2026-08-09 16:45:52 -07:00
Jinjing fde816e4ee move folders (#12758) 2026-08-05 12:09:24 -07:00
2548b816c0 Keep the app responsive when security software slows process creation (#12217)
* fix(ports): keep the app responsive when security software slows process creation

Orca ran the workspace port scan's probe commands (lsof/ps on macOS,
netstat + powershell.exe on Windows) directly in the Electron main process.
libuv performs process creation inline on the calling event loop, which in
the main process is the browser UI thread, so an endpoint-security module
hooking CreateProcessW froze the whole window for the length of the spawn.

The same stall also produced a false diagnosis: the 4s command watchdog was
armed before execFile (local-workspace-port-scanner.ts:389 -> :410), so its
deadline had already passed by the time the command started. Every scan on a
hooked host reported a command timeout, tripping the 60s -> 5min backoff and
the "Port scanning is temporarily paused after a command timeout" banner even
though the commands themselves were healthy.

Probe commands now run on a lazily created, unref'd worker thread with FIFO
one-at-a-time dispatch, and the watchdog is armed after execFile returns so it
measures the command rather than the spawn. Node's own execFile timeout kill
(killed: true) is classified as a command timeout, keeping the backoff working
for genuine hangs. A scan that observes a stalled spawn skips its optional
metadata commands for that cycle, capping a hooked-host scan at roughly one
stall instead of three.

Closes #11161

* fix(ports): keep advertised URLs when a stalled spawn skips port metadata

Review follow-up on #11161. The stalled-spawn early return handed
scanWorkspacePorts raw ports with no cwd/commandLine, so every port failed
attribution and reconcileAdvertisedUrls told the watcher each worktree's
listeners had vanished. shouldEvictAfterScan then deleted every cached
advertised URL and broadcast a removal event; those URLs are only ever
captured from live PTY output, so the dev-server link was gone until the
server restarted.

The scanners now report metadataAvailable, and reconciliation is skipped for
a scan that never gathered attribution evidence. The skip is also no longer
self-perpetuating: on an EDR-hooked host every spawn stalls, so gating purely
on the current scan's spawnMs made every port permanently external (Stop
refused with 'Only workspace-owned local processes can be stopped here.').
Metadata is now re-probed on the scan after a skip, matching what the comment
and test name already claimed.

Co-authored-by: Orca <help@stably.ai>

* test(windows): stop a temp-dir lock from failing the CLI launcher smoke test

The native launcher assertions passed on windows-latest, but teardown's
rmSync raced Windows' release of the image handle on the exe the test had
just executed and threw EPERM, failing the job.

Cleanup now retries and, on Windows only, tolerates a residual lock code
instead of reporting it as a launcher regression.

Co-authored-by: Orca <help@stably.ai>

* fix(ports): scope the metadata skip away from attribution-dependent scans

The metadata skip was a process-wide parity flag, so Stop and the
localhost-label allowlist could land on a degraded cycle and reject a
port the panel had just shown as workspace-owned. Give those callers an
explicit requireMetadata option, and carry the previous cycle's listener
metadata forward so a skipped background scan no longer republishes
workspace ports as external.

Also pin the watchdog ordering: the stall in the execution test was
shorter than the watchdog budget, so a watchdog armed before execFile
still passed.

* build: guard worker-thread entries against electron imports (#11161)

Electron's module is not registered on worker threads, so
require("electron") throws "Cannot find module 'electron'" inside a
main-process worker and kills it at startup (verified on Electron 43.1.0).
plain-node-entry-guard covered only forked plain-Node entries, so the five
worker entries relied on hand-written "must stay electron-free" comments.

The port-scan probe worker is one import away from
port-scan-command-client.ts, which deliberately contains require('electron').
A violation there fails closed at runtime while every unit test still passes,
because the client's require is try/caught on the main thread.

Covers stt-worker, warp-theme-parser-worker,
session-scanner-opencode-sqlite-worker-entry, main-thread-hang-watchdog-entry
and port-scan-command-worker-entry. The scan is transitive over the emitted
chunk graph, so a shared chunk that reaches electron is caught too.

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>

* test(windows): retry teardown for main's duplicate-PATH launcher fixture

Main's new csc-compiled harness runs an exe from the temp tree, which is
exactly the image-handle/AV lock the merged-in removeFixtureTree retry exists
for; its bare rmSync would report a teardown lock as a launcher failure.

Co-authored-by: Orca <help@stably.ai>

* test(ports): pin the packaged-asar worker entry path

resolveWorkerEntryPath's packaged branch never runs in dev or e2e, so the path construction had no coverage. Split the electron read out of it and unit-test both layouts.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-08-04 02:03:40 -07:00
NeilandOrca 73c5009b82 chore(dead-code): drop ~2k lines of unreachable exports and orphan modules (#12077)
* chore(dead-code): drop 2k lines of unreachable exports and orphan modules

Ran knip across every build entry (main, preload, renderer, popout, web,
cli, relay, workers, forked sidecars, config scripts) and removed what no
entry graph can reach.

- 11 orphan modules nothing imported, plus one test that only covered them
- 159 unused exports/types, with their now-dead helpers, imports and tests

Each candidate was verified against dynamic references before deletion.
42 knip hits were false positives and are kept: shared modules consumed by
the mobile/ workspace, the src/shared/plugins/** public API, vendored
shadcn primitives, and relay wire-protocol constants held for compatibility.

Adds knip.json + `pnpm audit:dead-code` so this stays measurable.

Verified: pnpm typecheck, pnpm lint, and 2081 tests across the 73 affected
test files all pass.

* chore(dead-code): move knip config under config/

Root-level additions are blocked by the root directory guard.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-08-02 00:33:57 -07:00