* perf(wsl): fold a connect's remaining read-only guest probes
Three folds, each WSL-only:
- the connect snapshot also reads the lock holder's guest identity, so
adoption's liveness check needs no second exec for that pid;
- one exec asks whether the pinned runtime is cached and the slot complete,
under the existing WSL-only recordedNodeRuntimeProbe option;
- the active slot's liveness, readiness and pid are read in one exec after
launch, judged by the same functions SSH's separate probes use.
SSH sends exactly the commands it did. The lane now restarts five times per
family so round-trip counts and ms medians are both readable.
* test(managed-server): measure the cached-slot probe against the host-op byte budget
It is sent only on WSL (wsl.exe argv, never SSH-framed), so like the bash
stdio bridge it carries a measured ceiling above the SSH budget.
After a watch or holder exit, a distro the running list still names is
usually stopping. Liveness now re-reads only the running list (never
boots) once a second for up to 5 s before any identity exec, and
publishes distro-stopped as soon as the list drops it. An exit that
arrives during a recovery backoff ends that recovery so the exit
re-checks at once (E9).
* perf(editor): compare worktree owner rows by identity instead of JSON signature
Activity/unread/rank patches to worktreesByRepo still stringified every
worktree's owner fields on each write to decide the route token. Walk
buckets and rows identity-first against the last tokenized map instead;
same order-sensitive owner-field semantics, no per-write allocation.
300 worktrees, 200 open files: route check per activity write ~40-70us -> ~0.8us.
* perf(editor): hold the last owner-rows baseline weakly and pin token semantics
A wake whose launch reply was lost but whose server did start left its
fence held; the next wake saw the slot serving and returned, so deploys
saw the fence busy until it aged out. When the slot is proven LIVE, the
fence names this app's interrupted-wake token and no journal exists,
release it through the owner-guarded release command.
* fix(wsl-host): say which WSL distro an agent isn't installed in
* i18n: translate the WSL-distro missing-agent sentence
* test(wsl-host): reach the missing-agent words through a chat whose agent left the host
#32919 gave typed workspace names a creature placeholder branch so
first-work naming could rename it. When generation fails or never runs,
users are left on branches like geoduck instead of the name they typed.
Typed names are valid branches in any script, so use them directly;
auto-rename keeps applying to Orca-generated creature names only.
* fix(wsl-host): a stop published after the host came back says ready again; retry the card's panes
- e9-reviewer: a runtime call's stop could land after liveness had already reconnected and said
ready, leaving Start on a working host. Once the distro runs, the deferred un-park re-reads the
host and says ready from the live connection, or reconnects open work as before.
- fix-reviewer-2: the return retry picks panes by the Start card's rule, so a pane whose create
failed before it reached the host is retried too.
- Pins that the refusal stays a plain, final Error with the same words for the move and automation
callers.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl-host): the stopped dot keeps panes on another, running server live
e9-reviewer: match the Start card's rule; only a pane with no server or on the stopped host ended.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(terminal): pin that the host-refusal unwrap leaves SSH words and other channels as they were
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
OpenCode/ZCode transcript database lookups read inside the WSL homes
they get, so they take only confirmed homes. A WSL session delete runs
as a user act on its distro, so background readers stop trusting an
older stopped read. The carryover notice gets a stable code and
catalog copy in every locale.
A paired host's repo.baseRefDefault now names the stopped distro that refused
it. Clients that cannot hear that host's running signal re-ask on window
focus and every 30 s while visible, until it answers. Locally, the running
listener is attached before the first read, so a signal that lands while main
is answering triggers a re-read instead of being lost.
* perf(terminal): stop re-reading the fit grid on every foreground write once it matches (#35273)
After a grid-drift probe finds xterm matching the fit, foreground output waits
for a geometry signal (pane ResizeObserver, xterm resize, visibility resume,
window wake) or a 2s backstop before forcing another proposeDimensions() read.
The first probe after connect and after any signal still runs at the 250ms
cadence, so drift without a pane resize is still repaired on output.
* fix(terminal): trailing grid-drift probe, unknown fit never settles, 1s backstop (#35273)
- A foreground write skipped by the probe interval arms one trailing probe
(per session, cleared on probe and dispose) so drift can't stick when output
stops right after a throttled write.
- measureTerminalGridFitDrift() returns 'drifted' | 'matched' | 'unknown';
only a valid matching proposal settles the probe.
- Settled backstop shortened from 2s to 1s.
- Probe moved to foreground-grid-drift-probe.ts (pty-input-forward max-lines).
- Specs use fake timers and cover backstop, trailing probe, and unknown fit.
* fix(terminal): pull an armed grid-drift trailing probe forward when the cadence speeds up
* fix(wsl-host): a terminal that meets its stopped distro shows Start, and comes back with it
After an external `wsl --terminate`, the first keystroke's runtime call found the distro stopped and
parked the host, but only liveness ever published a stopped status, and here liveness saw a quick
reboot instead. So no Start card appeared: the pane kept its old prompt, typing did nothing, and a
red toast showed Electron's raw "Error invoking remote method 'runtimeEnvironments:call'" text that
stayed after the distro ran again.
- The runtime-call gate (moved to wsl-host-runtime-call.ts) publishes distro-stopped when it parks
a host, as liveness does, so every pane on it shows the Start card and its error gives way.
- The park lifts once a running list confirms the distro runs again (never booting it), and a
host with open work reconnects.
- Panes that gave up on the stop retry when their host is ready again, not only after Start.
- A host refusal's toast drops Electron's IPC wrapper.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl-host): a workspace on a stopped WSL host shows no live terminals
Its panes keep their bindings for Start, so the card dot stayed green over dead terminals.
Co-Authored-By: Claude <noreply@anthropic.com>
* perf(wsl-host): no new store listener per card or surface for the stopped host
The activity status folds the stopped-host check into its existing pty selector, and the pane
recovery portals derive both the Start card and the return retry from one host-status
subscription. The live-capture listener budget (8,000) is back under, with no budget change.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* perf(terminal): stop HTTP link backward scans at rows that cannot continue a URL
Framed (hard-wrapped) detection walked back up to 2048 rows per hovered line,
building column maps for every URL row; edge-wrapped walked ~54 rows at 80 cols.
Stop once a later row cannot be a continuation (borders, prose, blank rows).
Table-heavy buffer: provideLinks 1.3-2.1ms -> 36-79us per call.
* test(terminal): pin the edge-wrapped HTTP link backward-scan bound
* fix(native-chat): turn a big paste into a file in terminal-backed chats and goal mode
A paste of 32 KiB or more already became a "Pasted text" file in
structured chats, but terminal-backed chats and goal mode put the whole
text in the editor, where one long line can stall the window for seconds
(issue #32355). Every composer that can store the file now follows the
same rule.
- Terminal-backed chats on this machine save the file in the paste
folder, where their pasted images already live; their sends claim
nothing, so the attachment store's 30-day sweep must not reach it.
- Terminal-backed chats never get the structured 256 KB refusal.
- Setting a goal claims the uploads its objective names in the same
journal write, so a goal's pasted file is not swept; a file already
gone refuses the goal before the agent starts.
* Preserve paste sources until submitted and retire refused goal claims
* Recover staged paste reads and cancel preparation before option commands
* Stabilize paste ownership test setup and sweep clock
* Keep large-paste folding scoped to structured chats
* Restore journal prototype forwarding methods
* Prepare attachment receipts from actual journal output
* Align goal and deferred paste fixtures with current contracts
* feat(wsl): default a new Windows-drive project under a WSL default to Git for Windows
* fix(wsl): judge a new project's worktree links with the prune guard's test, so a //wsl.localhost link counts as Windows Git's
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(wsl): don't start a stopped distro to read a repo's default base ref
Opening a repo's Settings pane ran git for-each-ref and git remote through
the repo's WSL distro, which started it after the user had stopped it (E9).
Background reads now return the existing unknown result while the distro is
stopped; an explicit refresh can pass wakeWslDistro. The runtime RPC that
serves the same read to paired clients is gated the same way.
* fix(wsl): re-read a refused default base ref once its distro runs
A refused read now names the distro and defers a preflight:wslDistroRunning
broadcast; Settings and Source Control re-ask on it, so base comparison
returns without a repo switch. Drops the unused wakeWslDistro option.
The walker that proves bundled rule files need no parsing missed value
rewriting checks (trim, toLowerCase, overwrite) and never entered
intersections, object catchalls, maps or records. It now reports each,
with a detection test per case.
* fix(claude): report a CLI the supervisor could not spawn as missing, not a failed start
The connection already flags a supervised exit 127 with a spawn ENOENT as a
missing executable, but the session's unexpected-exit settlement turned every
observed exit into providerExited, which startup then worded as
providerStartFailed. Route spawn absences through the startup failure fact so
they surface as cliMissing or launchFolderMissing.
* fix(codex): report a CLI missing during acquisition as missing, not an exit
Codex's acquisition-time exit had the same drop as Claude's unexpected exit:
a spawn ENOENT already marked on the error became providerExited. Share one
providerSpawnAbsent predicate and route marked errors through the startup
failure fact in both.
Boot the built orcad the way a WSL host launch does, open a repo and two
terminals, idle 90 s, and assert the whole process tree stays under
400 MB of RSS (about 251 MB today). It catches a new always-on worker or
a retained buffer between device runs of the 1 GB memory gate.
* Unify dedicated agent launch choices and argument badges
* Enable action model and effort menus with argument migration
* Bound argument choice edit range processing
* Preserve independent action effort through launch and migration
* Preserve literal config arguments and explicit launch permissions
* Respect repository action argument groups and share launch controls
* Remove duplicate action launch command coverage
* Unify Source Control action choices and repository overrides
* Use precise types in action choice contracts
* Consolidate action choice test imports
* Correct action choice types and integration test boundaries
* Complete prepared terminal test result contract
* Align action integration expectations with canonical choices
* test(mobile): type the file preview retry test helpers so the test typecheck passes (#35431)
(cherry picked from commit 0774fc9373)
* Commit edited action arguments before planning and launch
Reconcile deliberate raw edits once into the submitted action recipe so replacement model and effort flags reach Save and Start together. Preserve captured sources and untouched load conflicts.
* Name portable Source Control dialog fixture results
* fix(wsl): close the session scanner's post-stop boot gaps
An OpenCode/ZCode WSL reader that exits notes its distro may have
stopped, so a respawn needs a settled running read. The Devin
sessions.db opens only on a running list from the transcript lease.
Queued transcript reads re-admit right before they run, and reads on
open 9P handles re-admit too. WSL roots and extra Codex homes come
from a confirmed running list (E9).
* fix(wsl): a failed running-list probe keeps WSL sessions and delete roots
Background discovery keeps confirmed-only WSL roots, but an unconfirmed
probe now carries over the last confirmed WSL rows (marked partial)
instead of dropping them. Path checks for user acts (delete, subagents,
transcript lookup) use last-known-good roots, since checking a path
against a root reads nothing.
The live updater wrote JSON.stringify(parsed.policy), and parsing strips
fields this build doesn't know. Since an equal revision never rewrites the
cache, an older build that fetched a revision first would hide a field
such as movedProjects from a newer build on the same profile until the
server bumped the revision. It now writes the validated downloaded text
verbatim; the monotonic revision rule is unchanged.
Co-authored-by: Claude <noreply@anthropic.com>
#35667 pruned src/shared/wsl-host-internal-gate.test.ts as a lone suite,
dropping #35543's child-side cases. Two non-redundant ones return inside
wsl-host-gate.test.ts: a child of a blocked run trusts the published
closed over ORCA_INTERNAL_WSL_HOSTS=1 (and serve-only with no env), and
falls back to the env when nothing valid was published. The main-side
cases were already there.
Co-authored-by: Claude <noreply@anthropic.com>
* test(runtime): give each start-retry spawn wait room for the retry backoff
* test(runtime): drop the redundant first-spawn wait the retry loop already covers
* fix(plugins): load installed plugins whose engines floor predates their fields
The engines.orca floor rule is an authoring check. At runtime (install,
discovery, update, rollback) a manifest now loads when this host parses
every field and meets engines.orca; dev folders, validate:plugin and
catalog CI stay strict. Installed copies that can't load show user-facing
copy and offer Reinstall when the catalog has a loadable release with
different bytes.
* fix(plugins): give installed copies user-facing copy for every load failure
* fix(plugins): installing a one-mode theme now visibly applies it
Most official themes are dark only. Installing one while the app was light
filled the unseen dark slot and nothing on screen changed. Switch the app to
the theme's mode (Switch back restores it), and put a lone terminal theme for
the other mode in the terminal slot that is showing.
* fix(plugins): lone terminal theme shows in the visible slot and reads as selected
A wake that finds the slot serving returned before the fence branch that
clears an interrupted wake's token, so a stale token kept WSL's one-fence
wake answering 'fenced' until restart. Before that early return, forget
the token when its fence is gone or names another owner.
orcad spent ~25 ms of every start zod-parsing the 14 checked-in rule files. A test now proves their parse output deep-equals the raw JSON and that the schema can't change output, so the runtime uses them as bundled. Downloaded and override rules keep full validation. Also corrects the Codex-deferral comment: every orcad host defers it, not only WSL.
* fix(keybindings): show the ⇧ glyph for Shift on Windows and Linux
Spelled-out Shift made non-mac shortcut labels long (Ctrl+Shift+Backspace).
* test(menu): expect the ⇧ glyph in non-mac app menu hints; align double-tap hint
* fix(windows): keep Orca's std handles out of every spawned child
libuv spawns with bInheritHandles=TRUE, so an inheritable std handle reached
long-lived children such as the WSL holder and the daemon and held a
launcher's pipe open after Orca exited. Clear HANDLE_FLAG_INHERIT on the std
handles at the top of main startup; stdio 'inherit' still works because
libuv hands the child its own inheritable duplicate.
* test(windows): model Orca's Electron main, which keeps std handles inheritable
Plain Node clears its std handles' inherit flag at startup, so a Node child
never reproduced the leak. Electron main opts out of that, which is why Orca
leaks and why the control arm now runs Electron; a plain-Node arm pins the
difference.
* test(windows): say why the std-handle tests need longer timeouts
The cached policy gains an optional movedProjects: keep | bring-back, read
only with a block. An unknown value reads as keep, so it never drops the
block, and older builds ignore the field. The gate records the choice for
the run (wslHostsBringBackMovedProjects) and telemetry reports it as
wsl_hosts_moved_projects. Nothing acts on it yet; bringing projects back
follows.
Co-authored-by: Claude <noreply@anthropic.com>
* feat(wsl): offer the drive-path conversion in project settings, and convert the window's stores (D1c-4d)
* fix(wsl): tell the user why saved state blocked the drive-path conversion
A refusal over saved state now names the cause: a folder with different details under both its WSL
path and its drive path, or a project that also holds a repository that isn't converting. An older
host's refusal without a cause keeps the general message.
* fix(wsl): explain a conversion refused because the distro's /mnt/<letter> is not that Windows drive
Co-Authored-By: Claude <noreply@anthropic.com>
* test(wsl): give the renderer-rewrite journal fixture the profile the journal now records
* test(wsl): give the e2e conversion journal the profile field the journal now requires
* test(wsl): carry the #33135 startup tests onto the window-store journal: it waits for the window, and finishes on any platform once the gate is open
* test(wsl): off Windows the conversion IPC still reports startup's outcomes, as the journal can finish on any platform
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(cli): never register the bundled launcher as a link to itself
orca serve registers the CLI at startup. On macOS the installer walks
PATH for an existing orca command, and Orca's own terminals put the
bundled Resources/bin first on PATH. The bundled launcher reads as a
managed Orca launcher, so it was picked as the command slot, moved
aside, and replaced with a symlink to itself. The app's CLI then broke
until reinstalled.
Skip the launcher when scanning PATH, and refuse any symlink install
whose command path is the launcher.
Fixes#33872
* fix(cli): compare the launcher by directory entry, not path spelling
The self-link guards compared path strings, so a PATH entry or
ORCA_CLI_INSTALL_PATH that reached Resources/bin through a symlinked or
differently cased directory still replaced the bundled launcher with a
link to itself. Compare entry and parent-directory identity instead.
Co-authored-by: Hotragn <103170876+Hotragn@users.noreply.github.com>
* fix(cli): keep differently named hard links to the launcher replaceable
* fix(cli): treat a case-variant name as the launcher only when it is an alias, not a second hard link
* refactor(cli): compare the launcher by inode via existing entry snapshots
Replace the speculative isSameDirectoryEntry helper with isSameEntry beside
readEntrySnapshot/hasSameIdentity, and drop the hard-link test that only
pinned the removed behavior.
Co-authored-by: Hotragn <103170876+Hotragn@users.noreply.github.com>
* Revert "refactor(cli): compare the launcher by inode via existing entry snapshots"
This reverts commit f02f8c2a2ba0263fd611170af7c3f76d22e48ea7.
* test(cli): fold the bundled-launcher cases into the macOS command-path suite
* refactor(cli): move the launcher entry check beside the entry snapshots
Reuse readEntrySnapshot/hasSameIdentity for the entries, keep the parent
identity check (a hard link in another dir such as /usr/local/bin must stay
replaceable, which is why f02f8c2a2ba was reverted), and drop the readdir
case-folding branch. Pin the cross-directory hard link instead of the
speculative sibling one.
Co-authored-by: Hotragn <103170876+Hotragn@users.noreply.github.com>
* fix(cli): keep a case-variant hard link beside the launcher replaceable
---------
Co-authored-by: Neil <neil@stably.ai>
* fix(i18n): code AI Vault scan issues and chat auto-name failures for the renderer
* fix(i18n): read scan issue params through the wire type
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix: copy local Windows paths with native separators
Copy Path for a proven-local absolute Windows path now writes backslash
separators, the form native Open/Attach dialogs accept (#26204). Applied at
the clipboard boundary of the editor tab menu and header, File Explorer Copy
Path(s) and its shortcut, source control Copy Path, Monaco Copy Path to Line,
HTML document preview Copy file path, workspace sidebar and activity Copy
Path, skills Copy path, and AI Vault Copy Log Path, each with the owner its
Reveal/Open in action already resolves. Remote, unresolved, web-client, URL,
relative and non-Windows output is unchanged.
* fix: sync Copy Path owner refs after commit
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* test(ssh): let Linux finish releasing upload fds before asserting they closed
* test(ssh): pin abort teardown confirmation and reword the fd-release note