* Reuse resolved executable paths for Mac process launches
* Keep synchronous launch wrappers covered by the ratchet
* Explain executable launch type assertions
* Remove resolved Git imports from the direct-launch baseline
* Keep native spawn cancellation on the native lookup path
* refactor: move atomic publication to its filesystem owner
* refactor: group shared browser definitions by feature
* refactor: bind proxy process and session contracts to defining owners
* refactor: consolidate direct session contract type imports
* refactor: retain atomic publication test timing under moved owner
* fix(benchmark): resolve historical tunnel framing source before loading
* refactor: separate execution route and child exit owners
* refactor: move browser data into defining feature owners
* refactor: give runtime RPC entry its shutdown implementation
* refactor: use defining process types and colocate host telemetry test
* docs(process-host): point piped child contract to Node owner
* docs(process-host): clarify both piped contract owners
* fix(speech): run the on-device recognizer in its own process
A native crash in sherpa-onnx took Orca down with it, and a decode stuck
in native code could not be stopped. The recognizer now runs in an
Electron utility process (a forked child under orcad), is killed when a
stop or cancel goes unanswered, and a dead process ends the dictation
with an error instead of a silent stop.
* fix(speech): let a loading model finish when its dictation is canceled
* fix(speech): never leave a speech child behind or crash main on its fatal error
* refactor(speech): drop the teardown message now that terminate kills the process
* fix(speech): fork the orcad speech child through process-host with a typed request
* Upload GitHub images when pasted or dropped
Keep image bytes out of issue and review bodies by uploading from the owning composer and inserting hosted attachment URLs.
Co-authored-by: lydiassssz <60514233+lydiassssz@users.noreply.github.com>
* Preserve GitHub image intake across editor and thread states
* Keep image placeholder boundaries and ordinary HTML paste intact
* Match the native replace overload in the image projection fixture
* Use typed element queries in the image intake fixture
* Narrow textarea elements at the fixture boundary
* Show failed GitHub draft images as filename links
* Keep GitHub draft attachment links current across updates
* Supply the repository address in the attachment preview fixture
---------
Co-authored-by: lydiassssz <60514233+lydiassssz@users.noreply.github.com>
When the native exit watch fails to register, the child never reports an
exit, so a later kill() could signal a pid Windows has since reused. Mark
such a child untracked: kill() refuses it and the warning says so. No exit
is faked; the WSL holder already releases through its own pid and
creation-time check.
* fix(windows): start the WSL holder outside Orca's jobs so a crash doesn't take it down
* test(windows): prove the job closed with a non-breakaway control before checking the holder
* test(e2e): on the real WSL lane, background work never starts the stopped distro (E9 guard)
The Windows real-WSL lane opens a project inside its own imported distro, terminates that distro,
and runs a window of background work elsewhere (focus/blur/reveal, workspace switches, a linked
issue/PR card, Settings panes, Tasks, account switches). Every child the main process starts is
logged; no wsl.exe may run anything in the distro (listings are fine), and `wsl --list --running`,
watched each second, must never show it, which also catches UNC touches. Opening a terminal in the
WSL workspace then starts it. A participation check fails the lane if the guard skipped.
* test(e2e): actually render each Settings pane in the WSL lane's E9 background window
openSettingsTarget alone only set the navigation target, so no pane mounted and none of its reads
(servers, accounts, agents, integrations, CLI status, the repo pane's orca.yaml check) ran. Open
Settings, wait for each pane's section to mount, then close it.
* test(e2e): harden the WSL lane's E9 guard per review
- process-host logs every spawn to ORCA_E2E_SPAWN_LOG when set, so the guard sees worker-thread
and inheriting-child spawns (e.g. the Codex rate-limit fetch), sync ones included.
- The spawn baseline is taken before `wsl --terminate`, and the running-list watcher starts right
after it, so work in the stopping window counts.
- A wsl.exe launch counts as starting the distro unless it names another or only lists/terminates.
- The watcher is async with a 5 s timeout and non-overlapping ticks; a failed read is retried and
fails the guard after three, never reads as "stopped". The recorder throws if it cannot hook,
and the spawn log must be non-empty. The doc names the watcher as the authoritative assertion.
* test(e2e): bound the terminate grace, keep the spawn log out of shipped runs, tolerate torn lines
- The watcher forgives running reads only within 3 s of `--terminate` and before its first
not-running read, which the rounds now await; any running read after it fails the guard.
- Main drops ORCA_E2E_SPAWN_LOG outside e2e mode (no ORCA_E2E_USER_DATA_DIR) before anything
spawns, so no daemon, PTY or agent inherits it; the log holds full argv, tests only.
- readSpawnLog skips a line torn by a concurrent append.
* test(e2e): no blocking wsl.exe in the WSL lane's E9 guard, and a timeline for failures
The first lane run was cancelled at the 30-minute job limit while the guard step had run 25
minutes: past three 5-minute test timeouts, which only a blocked worker survives. Every wsl.exe
call (setup, terminate, cleanup) is now async with a timeout, so a hung one fails the test. The
guard logs a timestamped timeline (each running-list read, terminate, rounds) for diagnosis, and
the job limit goes to 50 minutes for the three added runs.
* test(e2e): let the setup's own WSL terminal launch settle before the terminate; 10 s grace
The lane run failed with "Ubuntu was still listed 3000 ms after --terminate" in all three runs.
The timeline showed why: focusing the WSL workspace in setup is a user act that opens a WSL
terminal, and its launch (codex trust probe, PTY, hook relay) landed about a second after the
terminate and restarted the distro. Setup now waits for that terminal's PTY and for wsl.exe
launches to go quiet for 3 s before terminating. The grace is two running-list reads (10 s).
* test(e2e): interleave wsl.exe launches into the E9 guard's timeline
The lane saw the distro boot about two seconds after the terminate with nothing naming the
launcher; each running-list tick now also prints the wsl.exe launches logged since the last one.
* test(e2e): time and stack every logged spawn and main's WSL UNC touches
The E9 guard printed spawns at its poll time, up to a second late, and with
no caller. Each record now carries when it happened and the caller's stack,
and main's fs touches of \\wsl.localhost paths are logged too, so a boot
with no wsl.exe launch before it can still be attributed.
* test(e2e): keep spawn-log stacks and main's fs hooks strictly to e2e mode
process-host now writes the spawn log, and captures caller stacks, only when
ORCA_E2E_USER_DATA_DIR is set as well, so a stray log variable in a CLI or
daemon environment does nothing. The main-process fs/spawn hook refuses to
install outside an e2e-mode app, and a source scan pins that no shipped code
imports or installs it.
* test(e2e): guard a focused WSL workspace left idle through the stop
Device runs saw a stopped distro restart with its bridge workspace focused
and nothing touched: the stop's own fallout re-ran renderer reads as user
acts. The new case keeps that workspace focused with a folder expanded in
the Explorer and Source Control shown, terminates, and idles. Shared guard
helpers move to tests/e2e/helpers/wsl-boot-guard.ts.
* test(e2e): capture caller stacks deep enough to reach the IPC handler
* test(e2e): give the focused-idle guard's timeout bump its reason
* fix(windows): start the WSL holder with only a NUL handle to inherit
libuv starts every Windows child with bInheritHandles=TRUE and no handle
list, so a detached holder that outlives Orca kept any inheritable handle
Orca had open, such as a launcher's pipe or a DevTools socket. Start it
through CreateProcessW with PROC_THREAD_ATTRIBUTE_HANDLE_LIST naming one NUL
handle for its stdio. The build probes now check the newest export without
calling it, and the lane receipt labels ConPTY's own startup output.
* fix(windows): report isolated holder exits and kills the way libuv does
* fix: bound local iteration checks and show live progress
* fix: harden check lifecycles and cached test evidence
* fix: preserve process instrumentation and fixture lifecycles
* fix: give fixture unref spy a portable function type
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
resolveCommand only checks what it resolves, so a file that also builds its
own wsl.exe exec no longer passes the import check on a resolveCommand call
alone. Note editor --remote wsl+<distro> launches as out of scope.
* feat(wsl): internal distro allowlist for WSL execs on shared test boxes
ORCA_INTERNAL_WSL_DISTRO_ALLOWLIST (comma-separated distro names) keeps every
wsl.exe call out of distros not on the list: process-host's resolveSpawn,
the main and daemon node-pty spawns, and the two codex-accounts calls that
bypass process-host. Pure list/status/version calls stay allowed; any other
form must name an allowlisted distro, and --shutdown or a default-distro exec
is refused. A malformed value refuses every distro and logs once. Unset, the
check reads the variable and returns, so behavior is identical to today.
* fix(wsl): cover every direct wsl.exe exec with the internal distro allowlist
- Restore #34525's worktree-create payload and captured-host test, reverted by
accident in the first commit.
- Check the allowlist in execFileCapture, the choke point for WSL Git, gh and
worker-thread captures, and before the WSL git-read probe, the Codex hook
path canonicalization and the CLI login spawn. The WSL watcher now spawns
through process-host.
- Stop scanning wsl.exe argv at the first positional argument, which wsl.exe
runs in the default distro.
- check-process-host-imports fails on any direct wsl.exe exec outside
process-host that does not call assertWslDistroAllowed.
* fix(wsl): check the distro allowlist where resolveCommand produces the wsl.exe form
Every Git, gh and glab path into a distro resolves through resolveCommand, so
gitExecFileSync, gitSpawn, git stream reads and wslAwareSpawn are covered too;
the execFileCapture check is now redundant and removed. The import check also
treats buildWslExecArgs as a wsl.exe exec and accepts resolveCommand as covered.
* perf(daemon): cap the terminal daemon's young generation
Idle terminal polling in the daemon allocates a few MB/s of short-lived
strings (process-table captures), and V8 answers by growing the young
generation to its 64 MB semispace maximum. Nothing is retained (heapUsed
returns to ~12 MB after GC), but the committed semispaces held ~120 MB of
resident memory per daemon. Launch the daemon with --max-semi-space-size=4
on the fork, direct and extracted-AppImage paths. The flag goes on the
command line, not NODE_OPTIONS, because PTYs inherit the daemon's env.
* perf(daemon): pass the young-generation cap through the macOS launchd job
Release macOS builds start the terminal daemon from the signed helper bundle
through a launchd job, not the fork path, so the flag must be in its
ProgramArguments too.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* test: remove redundant cases and repair behavioral assertions
* docs: reconcile audit records with current main
* fix(deps): apply security updates from #32101
Apply the six-file DOMPurify 3.4.17 and Next.js 16.4.1 patch from PR #32101 to resolve the dependency security gate for this test audit. Original patch by Brennan Benson.
* test: satisfy changed-line quality gates
Add required braces and replace filter indexing with find. Rerun both CI quality gates and the 27 affected relay tests; refresh audit hashes and record the corrections.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(process-host): let a slow Windows taskkill finish instead of orphaning the grandchild
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(process-host): abandon taskkill shortly after the root exits so it cannot walk a reissued pid
Co-Authored-By: Claude <noreply@anthropic.com>
* test(process-host): log the taskkill outcome and caller settle time under load
Co-Authored-By: Claude <noreply@anthropic.com>
* ci: register the late-taskkill Windows test in the package-windows path list
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(process-host): release the git admission slot on root close again; cap the background taskkill at 30 s
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
An optional onStderr hook receives every raw chunk, so long commands can
stream progress while still getting the bounded result. Capture,
truncation and termination are unchanged; existing callers pass no hook.
* fix(ssh): follow the managed server's port after activation and match quoted Windows forward argv
An update, rollback or recovery that launches a managed server now rebuilds the
tunnel so it follows the port the new server bound. Windows orphaned-forward
recovery now parses the process table's quoted command line instead of
comparing it to a space-joined argv, so a key path with spaces still matches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ssh): keep a successful activation when the tunnel rebuild fails; strip one LogLevel pair
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(ssh): soften the tunnel rebuild failure comment
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ssh): tokenize posix expected argv like the process table before normalizing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Give Windows taskkill a 5s budget before tree cleanup is reported unveri
On busy Windows machines, `taskkill` could take longer than the 2s budget
used for other subprocesses. Tree termination then gave up before the
process tree was confirmed gone. The taskkill timeout is now 5s, matching
the other Windows tree-cleanup paths.
Tests cover a slow taskkill that still verifies termination, and a hung
taskkill that is bounded and falls back to SIGKILL. The batch-shim recovery
test gets a longer hang and a 30s timeout so it has room on slow runners.
* Pass the context clock into structured session option writes
- Expose the injected `now` clock to the structured agent session option writer, so timestamps come from the same source as the rest of the context.
- Update the main and renderer layout-writer baselines to match the reduced line counts in `src/main/ipc/session.ts` and the removed `set-active-folder-workspace.ts` entry.
fix(process-host): accept string stdin in synchronous runs
Request raw subprocess output with null encoding so Node can encode string stdin as UTF-8. Preserve explicit text decoding and byte capture, with real-runner coverage for both output modes.
Co-authored-by: Joe Esposito <joeespo@gmail.com>
* ci: ratchet synchronous child-process launches in main and shared code
The Electron main process freezes every window while a synchronous child
process runs, and on Windows each launch is slow because antivirus scans
it. Nothing stopped new synchronous launches from landing in main-process
code, and the spawn observer behind ORCA_MAIN_THREAD_DIAGNOSTICS only saw
async spawns, so the worst blocks were invisible to it.
Ratchet: check:main-sync-spawn-ratchet parses production src/main and
src/shared files (test-only paths excluded) and fails when a file not in
config/main-sync-spawn-baseline.txt reaches execFileSync, spawnSync or
execSync from child_process, runProcessSync from @orca/process-host, or
one of the synchronous secure-file writers (they run icacls on Windows).
Named, aliased, namespace, default, require() and import() forms count.
A baseline entry whose file no longer launches synchronously fails as
stale, so the list can only shrink. It runs in lint and in the PR
static-analysis job next to the process-host import check.
Observer: runProcessSync now reports to the spawn observer, timing the
whole call (that is how long the thread was stuck) and reporting from a
finally so failing, throwing and timed-out children are counted too.
Runtime behavior is unchanged except that the diagnostics observer now
sees synchronous launches; with no observer registered the sync path
pays one extra branch.
* ci: count sync launches per file and gate the repo's sync wrappers
The sync-spawn ratchet only tracked which files launched synchronously, so a
file already on the list (wsl.ts, win32-utils.ts, git-exec-file.ts) could add
more launches unnoticed, and new callers of the repo's own synchronous
wrappers passed CI.
The baseline now stores a per-file count of uses, in the same format as the
focus-read ratchet: a rise or a new file fails, a lower or zero count fails
until `pnpm check:main-sync-spawn-ratchet --prune` lowers it, and --prune
never raises an entry. Every value reference to a gated function counts
(call, hand-off, alias and its uses, re-export); imports alone count nothing.
Gated wrappers: gitExecFileSync (and the git/runner re-export),
restrictWindowsPathSync, hardenSecurePath when called with sync: true,
grantDirAcl, getWslHome, listWslDistros, wslUncDirectoryExists,
isWslAvailable (and the wsl.ts re-export), isPwshAvailable. Their 18
current caller files join the baseline.
Failure messages now give advice per family: the async process-host API for
child-process launches, the async twin for wrappers, and for the secure
writers a note that no async secure writer exists yet, so the sync one stays
and must never become a plain write (that drops the Windows ACL hardening).
Also refresh two stale blockMs descriptions and drop a no-op line from the
spawn-observer "stays silent" test.
* ci: count hardenSecurePath unless visibly async, and tighten ratchet tests
hardenSecurePath was counted only when `{ sync: true }` was written inline,
so moving the options into a variable or a spread dropped the count and the
check then asked for a prune. It now counts every use except a call with no
options or a spread-free object literal whose `sync` is absent or false.
Also: resolve backslash paths in the importing file as well as the
specifier; still visit heritage and instantiation expressions
(`extends mixin(fn)`, `fn<T>`); state the reference-only counting limit;
and point the secure-writer advice at raising this file's baseline count.
Tests cover the hardenSecurePath shapes, the alias chains that need the
fixpoint loop, and a prune that must not raise a grown row.
* test(ci): pin the main sync-spawn ratchet background step
* fix: count secure hardening calls with spread arguments
Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging.
Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan.
Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews.
Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations.