When a structured worker's dispatch preamble is definitively rejected, the
dispatch_preamble_undelivered error now appends the provider's failure detail
("Details: ...") unless the rejection sentence already quotes it. The quoted
text is bounded and normalized by the same quotedProviderDetail helper the
rejection writer uses.
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
A background read of a \\wsl.localhost folder workspace's status (sidebar
polling, runtime status RPC, a passive terminal restore) stat'ed the folder
and booted its stopped distro. The status now reports 'unavailable' without
touching it; a passive restore holds with Start; a spawn the user asked for
checks the folder as their act, which may start the distro as before.
The passive-restore hold lived in the daemon adapter, so a degraded or
off daemon (LocalPtyProvider) never applied it, and even with the daemon
the startup cwd probe (wsl.exe --exec test -d) ran first and could boot
the distro. The hold now runs in the shared startup-target resolution,
before that probe and before any provider spawns (E9).
The explorer's workspace-open read and the first git status took a pending
user switch with no age limit, so a switch no consumer used (sidebar closed)
could be redeemed long after the distro stopped. They now use the same 10 s
window as pane restore and git history, through one shared
takeRecentWorkspaceSwitchWake.
When a host restart restores a pane by starting a new shell, the pane kept
the dead process's title and the client's xterm kept its input modes. A
pane that had Claude open came back as bash but still read as Claude, so
the reattach picked the agent-preserving reset and mouse moves and focus
changes were typed into the shell as escape codes.
Two process-bound facts now follow the respawn verdict:
- Title: the cold-restore payload no longer carries the checkpoint's last
title into the new process's records, and durable checkpoints of the new
process take the title from the live terminal only. The title, agent
identity and title-derived status reflect the new shell.
- Input modes: a pane records which process incarnation its xterm's modes
belong to. When the host binds a different known incarnation, the pane
grounds the process boundary before the new process's replay, the same
ground a fresh spawn already writes, so only modes the new process set
are armed.
Older servers still report incarnations, so new clients ground their modes
against them; older clients get the neutral title from new servers and
take the shell reset path.
Fixes#35559
* 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(wsl): Codex WSL hook installs and host hook checks never start a stopped distro (E9)
(a) installManagedHooksIntoWslRuntime checks the settled running gate inside
its trust queue right before the first UNC read, and again before the
post-hash fallback trust write; a later reconcile checks before revoking or
reinstalling. A stopped or unverifiable distro skips (null, as with no WSL
plan). The canonical-path probe runs through process-host behind
blockedWslDistroForExec; a refused probe settles as unavailable and keeps the
known identity. The guest shell preflight and a pane launch's home preparation
run as user acts on their distro.
(b) repo.hooksCheck takes an optional background flag (older hosts ignore it);
the renderer sends it, and the host refuses a stopped distro's background
check the same way the desktop's hooks:check does.
* fix(wsl): a pane launch is not a user act for the Codex hook install
A passive restore or remount reaches launch preparation too (#35813), so it no
longer runs as a user act; a stopped distro skips the install and the relay
installs once the distro runs. A skipped WSL home now returns not_installed
instead of falling through to the host writer, which would have written the
WSL home over UNC.
* fix(wsl): a fallback grant skipped mid-install reports trust pending
hooks.json is written before the hash wait, so a skip after it is not a clean
skip: return an error status saying the trust is pending instead of null.
* chore: drop the stale process-host direct-import baseline entry
* fix(wsl): Commits history wakes a stopped distro only on the user's own act
Showing Commits woke the distro on every mount, remount and re-show. Now
only the user expanding Commits in the same worktree, or the user's own
switch to the workspace (once per switch), may start it; the Refresh
button keeps its explicit wake (E9).
* test: a mount and a base-ref move read Commits history in the background
#35778's base-move refetch test expected every shown-history read to
wake a stopped WSL distro; only the user's own act does now.
Warm start (an app relaunch or reconnect with the distro and orcad
running) to first terminal output is the flag-on gate at 2.5 s p50; cold
start (distro restart) is tracked against 3.0 s p50. Five warm reconnects
follow the cold runs, and the receipt reports n, p50 and p90 of first
output, ensure, create and send-to-output for each, with the runs.
Moving a desktop's SSH state onto the host's Orca server refused with
orcad_migration_dormant_focus_conflict whenever the host already had a
focused project from another desktop. Focus is each desktop's own
selection, not saved state, so two desktops always differ and the second
could never move, on every retry.
The host now keeps its own focus as a unit and takes a desktop's focus
only when it has none, which is what the merge already did for the
fields it borrowed. Older servers still refuse until they update.
Fixes#35585
A first WSL deploy fetches the pinned Node archive, from this computer
and then from inside the distro. Behind a firewall that drops packets it
retried four times and then waited 20 s per host source, about two
minutes before failing. WSL deploys now give the desktop one 15 s attempt
with no retry on a missing response, and the host 15 s per source, so a
dead network fails in about 45 s. The host fallback stays, since the
distro's own route often succeeds when this computer's proxy refuses.
SSH keeps its timing.
* fix(ssh): flag a config host whose route is already registered under another name
The ssh-config picker compared names only, so a machine added by IP showed up
again under its config alias. It now also compares the endpoint: hostname
(case-insensitive), validated port, case-sensitive user, jump host and proxy
command. Entries without HostName still match by alias only.
Squashed from #15750 (3c3a3c689c, a5575ce209, 8daf21370b, a29d91ea4a).
* fix(ssh): skip importing a config alias whose ssh -G route is already in Orca
Each config import now builds one snapshot: the config it read, and which new
aliases resolve, through ssh -G, to a route an existing target already has.
The duplicate check and the inserts both read only that snapshot. Imports and
picker refreshes run one at a time through a single queue, so none can race
another, and the picker reads the latest snapshot for its In Orca flags and
Add all count without waiting (alias-only until one exists).
A route is keyed only when ssh2's rules and system ssh's (OpenSSH run with
system ssh's own destination args, including an unclaimed alias's stored
endpoint) agree on host, port, user and proxy, with each transport's token
rules. Anything uncertain, or a failed ssh -G, gives no key, so nothing is
matched by guess. At most four ssh -G run at once. Restore brings back the
hosts it lists, previouslyRemoved stays alias-only, and DNS isn't consulted.
Co-authored-by: vam <zengyan@88.com>
* fix(ssh): rebuild the route snapshot after an import changes targets
An import that inserts or updates a target outdates its own snapshot, which
left the picker alias-only until the next refresh. It now queues a rebuild
behind itself. The picker also ignores a snapshot past its reuse window, and
the system-route shortcut requires a config alias.
Co-authored-by: vam <zengyan@88.com>
---------
Co-authored-by: vam <zengyan@88.com>
* fix(wsl): Settings' issue-command and path checks never start a stopped distro
Opening the repository and MCP settings sections read .orca/issue-command,
orca.yaml and stat'ed the repo root over \\wsl.localhost, which starts a
stopped distro (E9). The issue-command read (IPC and runtime) now returns
the unreachable result for a stopped distro unless a launch passes
wakeWslDistro, and shell:pathExists is gated like shell:pathsExist, with the
two link-click callers passing wakeWslDistro.
* fix(wsl): never save over an issue command a stopped distro kept unread
A refused read showed an empty, editable field, and typing then saved over
the hidden local override and started the distro. The editor now stays
read-only with a stopped-distro note, skips both saves, and re-reads when
main signals the distro running. Writes are gated too unless the user is
saving their own edit (wakeWslDistro). The MCP section's stopped message no
longer offers a Retry that cannot start the distro.
* fix(wsl): translate the stopped-distro issue command note; listen before reading
Adds the note in every shipped locale, and attaches the editor's running
listener before its read so a distro that starts while the refused read is
in flight still triggers a re-read.
* fix(wsl): keep the issue-command editor and writer tests on their seams
The editor reads window.api optionally, as hosts without a preload do, and
the writer tests spy on the gated write that the handlers now call.
The explorer's workspace-open read and the first git status woke WSL on every
mount, so a remount, launch restore or host loss after the user stopped the
distro started it again. Both now wake only when takeWorkspaceSwitchWake
reports a new user switch to that workspace, once per switch per consumer;
an expanded folder wakes only when it is newly expanded in the same
workspace, not when a mount or switch re-runs with the same folders.
* fix(i18n): translate updater failure messages shown in the renderer
* fix(i18n): localize the dev-channel platform list and paired-server updater errors
* fix(i18n): sync runtime catalog and coded updater status expectations
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* plugins: make Try it do the thing instead of opening the plugin page
* plugins: address review on Try it (row's icon theme, language switch result, pending plugins)
* fix(wsl): every local Git call follows its project's runtime unless it says why not (D1c-6)
* fix(wsl): rebuild Git routes on the store's own writes, never on a timer, and route a new worktree by its link
* fix(wsl): route WSL-made worktree links, aliased checkouts and sync Git; host Git only where marked; no wsl.exe storms
* fix(wsl): map WSL-made worktree links through each project distro's automount root
* fix(wsl): arm the missing-distro cache only with the Git router on, and feed it spawn failures
* fix(wsl): keep synchronous Git host-only, read the setup runner's path from the link, and keep the sync launch under the ratchet
* chore(wsl): move the sync Git runner's process-host import allowance with it
* fix(wsl): read only per-worktree git paths from the link, and only from a git dir that still exists
* fix(wsl): name which Git ran on stream and spawn spans too, so status shows its route
* test(wsl): setup runner lookups declare host Git; cover the link read with the router on and off
The hooks tests expected the setup runner's `rev-parse --git-path` call to
carry `wslDistro: 'Ubuntu'` for a WSL-runtime project. On main that field
never reached Git: main's gitExecFileSync options have no wslDistro and it
calls resolveCommand('git', args, cwd) with no distro override, so the
lookup ran host Git (WSL only when the cwd itself is a WSL path). The tests
pinned call arguments, not what ran. The sync runner now states that with
`hostGit: true` and resolves exactly as main did, so the tests assert that.
New worktree-runner-script-link tests cover the router-gated link read:
router on reads the link; router on with no usable link falls back to host
Git and converts its Linux path; router off makes main's host Git call and
never reads the link.
* test(git): read src/main once in the host-Git routing guard, so it holds under load
* test(wsl): pin the setup runner's sync Git spawn to main's exact launch on POSIX
The setup runner's path lookup now passes hostGit: true where it passed a
wslDistro that gitExecFileSync never read, so the binary, args, cwd and env
it spawns are unchanged. Update the call-shape assertions and pin the POSIX
execFileSync call itself.
(cherry picked from commit 6aae6d4d090e13b75c1d9ad2734f2f717dde7570)
* fix(wsl): await WSL Git admission in spawns only when there is one
With the Git router off admitWslGit returns null; awaiting it anyway added a
microtask to every spawn's admission path that main doesn't have.
(cherry picked from commit 883aaeb90bc23e89cafb039708a565ab3d72620e)
* fix(wsl): keep main's Git scheduling when the D1c Git router is off
The remote-identity sweep moved every user's probes to the background
admission tier, which only the router reads; it now keeps its tier unless the
router is on. Admission wrapping and the spawn stderr tail also step aside
without the router, so those calls run exactly as on main.
(cherry picked from commit 151a3091307c03078d0c0b98487bbe3bf467ec27)
* test(wsl): point the distro allowlist test at the sync runner's new home, as host Git
Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 255e85fc93deba83addc4370f889c1b721180188)
* test(wsl): installing the Git router with the gate closed or off Windows leaves none installed
Co-Authored-By: Claude <noreply@anthropic.com>
* perf(wsl): skip the span-only Git resolution on every spawn while the Git router is off
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): read a distro's automount root only from a running list probed after its stop settled, so it never boots a terminated distro
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
* perf(stats): stop retaining and rewriting the raw stats event log (#35544)
The collector kept up to 10K raw events (~203 B each since #14657 switched
meta to pane keys and added worktree ids) and re-serialized the whole ~2 MB
file every 5 s. No consumer reads events; persist aggregates only, keep the
v1 file shape with an empty events array, and compact legacy files on load.
* test(stats): use a small event burst for the retained-size bound
* fix(i18n): localize remote server pairing, host sign-in and Bitbucket connect failures
* fix(i18n): map pairing guard codes without a partial switch
* fix(i18n): type the Bitbucket connect bridge with its coded result
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
A deferred re-watch drained right after a distro boots can stat the
root before its file share serves it, and the root was then remembered
unwatchable for good. A WSL root's stat failure now retries after 1, 2,
4 and 8 s, through the same running-distro gate, before giving up.
* fix(i18n): localize Orca's agent output and Kimi failure messages in AI text generation
* fix(i18n): keep Kimi option connectives inside the translated sentence
* fix(i18n): narrow Kimi empty-input diagnosis without an undefined case
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(wsl): a passive restore never starts a stopped WSL distro; its bridge panes wait with Start
* fix(wsl): a restore may start the distro only on the user's own open, once; an unanswered read holds too
* 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>