* fix(terminal): honour a paired-client close for every sleeping record shape
Refs #21235. Fixes#29421.
* fix(terminal): keep foreign-host records out of the claim election
* fix(runtime): resolve session tab workspace path aliases
An explicit worktree ID that differs from the host's stored spelling only
by Windows path separators (or another worktreeIdsEqual-equivalent form)
returned an empty session tab snapshot, hiding a workspace's existing
tabs from paired clients.
Resolve explicit session selectors against the IDs the host already
knows (published snapshots, saved session tabs, surviving renderer-owned
PTYs) with the shared worktreeIdsEqual comparison, keep the host's
stored spelling, reject ambiguous aliases, and refuse alias adoption
across execution hosts. Session-tab subscriptions match and clean up by
the normalized key.
Fixes#27260
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(runtime): keep exact live PTY spellings and reject foreign PTYs under local owners
Addresses Codex review of the #27264 alias resolver.
---------
Co-authored-by: Simon Cho <40405860+SimonSQLML@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ssh): start a managed server whose slot has no launch artifacts
A recorded-active orcad slot with neither a PID file nor a readiness file
answers NEVER_LAUNCHED from the liveness probe. The parser folded that into
UNKNOWN, so every reader treated it as unprovable: the wake never started the
slot, the deploy path reported "process state is unverifiable", and the host
stayed unreachable until someone moved orcad-active.json aside by hand.
A launch creates the readiness file before anything else, so a slot with
neither file provably has nothing running from it; GC already relies on that
to remove such directories. Keep NEVER_LAUNCHED as its own liveness value and
read it as proven exit wherever DEAD is: the wake, ensureOrcadSlotServing,
probeActiveOrcadReadiness, interrupted-candidate resume, decommission
recovery and GC. UNKNOWN stays untouched and still never starts anything.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(ssh): one-line docstrings for the liveness readers this change touches
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(ssh): the shell-probe integration test expects NEVER_LAUNCHED for a slot with no launch artifacts
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
When the agent-first attempt skips the host's keyboard-interactive challenge
so the deferred ~/.ssh key can go first, a host with MaxAuthTries=1
disconnects after rejecting that key, so the key retry never reaches the
challenge. Record when an attempt held the challenge back and when an
attempt actually offered it; if the key retry fails with an auth error
without offering it, run one attempt with no agent and no key that offers
only the challenge.
Fixes#29443
* feat(space): open disk usage in a separate window
* perf(space): move local disk scan off Electron main thread
* fix(space): satisfy full PR quality gates
* ci(wsl-orcad): skip the WSL MSI when the image already has 3.0.1
windows-2025 image 20261007.264.1 ships WSL 3.0.1.0, and reinstalling the
pinned 3.0.1 MSI over it exits 1603, failing every run. Install only when the
image is older, print the msiexec log on failure, and gate on >= 3.0.1 after.
* fix(orcad): let a managed launch fall back when its default port is taken
#29432 made any --port mandatory, but managed SSH/WSL launches pass the
default 6768 as a preference and read the bound port from readiness. WSL2
distros share one VM loopback, so a second distro's orcad now failed with
pinned_port_unavailable. Only an operator --port is required now.
* feat(layout): add the dark layout.apply channel and route pane splits through main behind ORCA_LAYOUT_OWNER
A build made with ORCA_LAYOUT_OWNER=main registers layout.apply: main applies the
window's layout command to the stored partition through the shared applyLayoutCommand,
minting new ids. A user split then waits for main's answer and mounts the pane id main
minted. Every other build substitutes 'window', so neither branch is compiled in.
* fix(layout): write only the records a layout.apply command touched
A load-then-save rewrite also normalizes records the command never touched. The command's
effect is now the diff between the save before and after it, written onto the stored
partition record by record. A split whose main reply says a paired server holds the tab
splits as today.
* fix(layout): write a load-supplied record whole and check that patched partitions reload to the command's result
* feat(layout): close, rename and equalize panes through main behind ORCA_LAYOUT_OWNER
In a layout-owner build the window's pane close, title change and equalize go through
layout.apply and the window shows main's answer. A window pane close keeps the legacy
close's rules: the tab's last pane is refused, a pane restarted since main read the close is
left alone, every partition holding the tab closes it, and the tab's remote session id follows
the remaining pane. The rules sit behind a command field only layout.apply sets.
* fix(layout): close the tab when a racing pane close leaves the window one pane
* fix(layout): leave an unmounted tab alone when main refuses its last pane
* feat(layout): move panes and save divider ratios through main behind ORCA_LAYOUT_OWNER
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): a taken pane drop whose pane closed does nothing
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): pin the window's pane move to main's saved tree, and the drops main cannot take
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* fix(layout): close a user's pane even if its terminal restarted, as today
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): pin the restarted-pane close directly, not through a made-up profile state
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* fix(layout): leave a split to today's path only when the workspace's row names a paired server
The split switch point asked which host to route to with a helper that falls back to the focused
server, so a focused server could claim a workspace whose row names no owner. Use the same owner
test the tab mirror uses.
* test(layout): type the split owner mock without a cast
* test(layout): type the layout-owner command mocks with the real signature
* test(layout): type the gesture command mock with the real signature
* fix(layout): close the dark path's gaps: unmounted split panes, refused or failed splits and moves, ratios during a move, release guard
- A pane main saved but the window could not mount (source pane or manager gone during the await)
is closed again in main, so it does not come back on restart.
- A split main refuses because its copy lacks the workspace, tab or pane, or a failed call, splits
as today; a refused or failed move moves as today.
- Divider ratios committed while a move is in flight wait for the window's move, then go to main.
- The orcad bundle gets the ORCA_LAYOUT_OWNER define when the switch is on.
- Packaging and the orcad template refuse ORCA_LAYOUT_OWNER=main and any output a dark build marked.
- workspace-layout-record-patch.ts: no non-null assertions; plain comment for the map depth.
* test(layout): count a split pane as mounted from the split mock's own results
* fix(layout): act as today whenever main lacks the target or the call throws, not just for splits
- sendPaneCommandToLayoutOwner runs asToday for workspace/tab/pane_not_found and a thrown call, so
close, rename and equalize fall back like split and move; a handler that throws no longer runs the
fallback too.
- A close main refuses as its last pane, while this window still has others, closes as today.
- A move whose local step throws still releases held divider ratios.
- Drop the orcad output marker: nothing read it; the template build refuses the switch itself.
* test(layout): plain names for the last-pane fallback test and the marker comment
* docs(layout): say plainly what an indeterminate write can leave behind
* fix(layout): finish the main merge with this branch's versions of the layout-owner files
* test(build): copy the layout-owner build guard into the compiler-cache checkout fixture
* feat(layout): send tab rename, color and pin through main behind the layout-owner build switch
In an ORCA_LAYOUT_OWNER=main build, the tab bar's rename, color and pin ask main's layout.apply
first; the window then runs today's action. When main lacks the tab or the call fails the
window acts as today; a paired server's workspace keeps today's path. Pinning a tab now also
ends its preview in main's copy, as the tab bar's pin does. Other builds compile the switch out.
* fix(layout): leave a pinned tab's preview mark to the window until main opens files
Changing the shared pin command would change what a paired client's pin does in every build.
* docs(layout): wrap the layout-owner sender's doc comment
The host projected a title-only working row stamped with the spinner frame,
then republished the older Stop-time done once the title went plain, so a
paired client kept the newer working row forever (#28102).
* fix(ssh): hidden relay-era panes stop sending every output chunk to the window; a reveal re-reads the relay's screen
* fix(ssh): reveal replays at the relay's snapshot boundary and hidden panes still answer terminal queries
* fix(ssh): hidden bridged panes still deliver split queries, OSC marks and bells
* fix(ssh): hidden bridged panes answer cursor queries from a current screen and keep sequences split at visibility changes
* fix(ssh): hidden bridged panes keep kitty, window-size and DCS queries, and reopen an OSC cut at reveal
* Bound and sanitize CI failure excerpts with linear ANSI stripping
* Fix excerpt regression checks and reduce bounded marker overhead
* Require signal proof for benchmark timeouts and load consumer fixtures portably
* Validate benchmark timeout framing and run required cases with a pinned CI baseline
* Strip dense generic ANSI escapes with bounded forward scanning
* Use the standard UTF8 encoder for byte-cap test oracles
* Gate CI log performance suites behind a dedicated job
The wall-clock performance suites run about 1k child workloads up to 63 MiB, so they were sharing runners with unit tests and could fail on noise. They now run only in their own `check_log_performance` job, opted in with `ORCA_CHECK_LOG_PERF=1`.
- Remove the pinned-baseline checkout and `ORCA_CHECK_LOG_BASELINE_DIR`. The baseline comparison is now a manual step that reads the pinned ref from local Git history.
- Run the current-implementation checks with `compareBaseline: false`, which skips baseline builds and audits.
* Inline check-log fixture and drop dedicated perf CI job
- Replace the captured failure-log JSON with a synthetic log built by `createCheckLogFixture()`, so no scraped job output or provenance metadata is kept in the repo.
- Remove the `check_log_performance` CI job. The log performance suites now run only when `ORCA_CHECK_LOG_PERF=1` is set, as a manual benchmark.
- Update the benchmark scripts and tests to use the new fixture helper and drop the `fixturePath` parameter.
* feat(checks): add optional job log outcome
* fix(checks): preserve host capture lifetimes and safe outcomes
* fix(checks): retrieve and retry bounded failed-job logs
* test(checks): cover queued snapshots and captured error safety
* test(checks): verify sanitized host excerpts and paired failures
* test(checks): identify measurements and preserve paired cleanup
* Keep check details load failures safe and localized
* Describe failed check logs as bounded excerpt context
* Normalize check log excerpts once at renderer ingress
* fix(checks): prioritize failure excerpts and retain reading position
* fix(checks): show truthful log outcomes and explicit refresh guidance
* fix(checks): fit failure context in narrow panes and skip generic headings
* fix(checks): prioritize captured failure diagnostics over inline loggers
* fix(checks): stop stack association at a new diagnostic
* feat(checks): add optional job log outcome
* fix(checks): preserve host capture lifetimes and safe outcomes
* fix(checks): retrieve and retry bounded failed-job logs
* test(checks): cover queued snapshots and captured error safety
* test(checks): verify sanitized host excerpts and paired failures
* test(checks): identify measurements and preserve paired cleanup
* Use the generated check-log fixture in host retrieval tests
* Keep check details load failures safe and localized
* Describe failed check logs as bounded excerpt context
* Normalize check log excerpts once at renderer ingress
* Repair ingestion test fixture and benchmark names after dependency merge
* fix(checks): prioritize failure excerpts and retain reading position
* Use the replacement generated fixture for check-details presentation tests
* fix(checks): show truthful log outcomes and explicit refresh guidance
* fix(checks): fit failure context in narrow panes and skip generic headings
* fix(checks): prioritize captured failure diagnostics over inline loggers
* fix(checks): stop stack association at a new diagnostic
* ci: rerun UI checks after restack
* fix(checks): classify sidebar job failures consistently
* test(runtime-environments): pin connect, remove, call readiness, hint and phone relay per host kind
* refactor(runtime-environments): dispatch on one exhaustive host-kind classifier
* test: avoid type assertions in host-kind characterization fakes
* refactor(runtime-environments): keep service-owned connect/remove in one module; tighten characterization tests
* style: break, not bare return, in host service ops switches
* fix(terminal): send modifyOtherKeys Enter chords to apps that armed it, so pi's Shift/Option+Enter stop drifting (STA-7701)
* fix(terminal): keep an observed modifyOtherKeys level across snapshots an older host cannot restate
* fix(terminal): restate a disarmed modifyOtherKeys level and recover it at command end
* feat(layout): add the dark layout.apply channel and route pane splits through main behind ORCA_LAYOUT_OWNER
A build made with ORCA_LAYOUT_OWNER=main registers layout.apply: main applies the
window's layout command to the stored partition through the shared applyLayoutCommand,
minting new ids. A user split then waits for main's answer and mounts the pane id main
minted. Every other build substitutes 'window', so neither branch is compiled in.
* fix(layout): write only the records a layout.apply command touched
A load-then-save rewrite also normalizes records the command never touched. The command's
effect is now the diff between the save before and after it, written onto the stored
partition record by record. A split whose main reply says a paired server holds the tab
splits as today.
* fix(layout): write a load-supplied record whole and check that patched partitions reload to the command's result
* feat(layout): close, rename and equalize panes through main behind ORCA_LAYOUT_OWNER
In a layout-owner build the window's pane close, title change and equalize go through
layout.apply and the window shows main's answer. A window pane close keeps the legacy
close's rules: the tab's last pane is refused, a pane restarted since main read the close is
left alone, every partition holding the tab closes it, and the tab's remote session id follows
the remaining pane. The rules sit behind a command field only layout.apply sets.
* fix(layout): close the tab when a racing pane close leaves the window one pane
* fix(layout): leave an unmounted tab alone when main refuses its last pane
* feat(layout): move panes and save divider ratios through main behind ORCA_LAYOUT_OWNER
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): a taken pane drop whose pane closed does nothing
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): pin the window's pane move to main's saved tree, and the drops main cannot take
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* fix(layout): close a user's pane even if its terminal restarted, as today
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): pin the restarted-pane close directly, not through a made-up profile state
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* fix(layout): leave a split to today's path only when the workspace's row names a paired server
The split switch point asked which host to route to with a helper that falls back to the focused
server, so a focused server could claim a workspace whose row names no owner. Use the same owner
test the tab mirror uses.
* test(layout): type the split owner mock without a cast
* test(layout): type the layout-owner command mocks with the real signature
* test(layout): type the gesture command mock with the real signature
* fix(layout): close the dark path's gaps: unmounted split panes, refused or failed splits and moves, ratios during a move, release guard
- A pane main saved but the window could not mount (source pane or manager gone during the await)
is closed again in main, so it does not come back on restart.
- A split main refuses because its copy lacks the workspace, tab or pane, or a failed call, splits
as today; a refused or failed move moves as today.
- Divider ratios committed while a move is in flight wait for the window's move, then go to main.
- The orcad bundle gets the ORCA_LAYOUT_OWNER define when the switch is on.
- Packaging and the orcad template refuse ORCA_LAYOUT_OWNER=main and any output a dark build marked.
- workspace-layout-record-patch.ts: no non-null assertions; plain comment for the map depth.
* test(layout): count a split pane as mounted from the split mock's own results
* fix(layout): act as today whenever main lacks the target or the call throws, not just for splits
- sendPaneCommandToLayoutOwner runs asToday for workspace/tab/pane_not_found and a thrown call, so
close, rename and equalize fall back like split and move; a handler that throws no longer runs the
fallback too.
- A close main refuses as its last pane, while this window still has others, closes as today.
- A move whose local step throws still releases held divider ratios.
- Drop the orcad output marker: nothing read it; the template build refuses the switch itself.
* test(layout): plain names for the last-pane fallback test and the marker comment
* docs(layout): say plainly what an indeterminate write can leave behind
* fix(layout): finish the main merge with this branch's versions of the layout-owner files
* test(build): copy the layout-owner build guard into the compiler-cache checkout fixture
* feat(layout): create terminal tabs through main behind ORCA_LAYOUT_OWNER
Main writes each window-created terminal tab under the window's ids before any later command names
its pane, and decides a workspace's first terminal on activation. Main refuses a tab id another tab
holds; the same create again changes nothing.
* fix(layout): create a revealed tab under main's pane id, and open the activation fallback only in the shown workspace
* fix(layout): open the activation fallback only while the workspace is still empty
* fix(layout): show a frame-drawn tab when a revealed terminal's stale records leave its owner unclear
* perf: allow opting out of main source maps in development
* Use the session tab type in the inherited terminal regression test
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* Bound and sanitize CI failure excerpts with linear ANSI stripping
* Fix excerpt regression checks and reduce bounded marker overhead
* Require signal proof for benchmark timeouts and load consumer fixtures portably
* Validate benchmark timeout framing and run required cases with a pinned CI baseline
* Strip dense generic ANSI escapes with bounded forward scanning
* Use the standard UTF8 encoder for byte-cap test oracles
* Gate CI log performance suites behind a dedicated job
The wall-clock performance suites run about 1k child workloads up to 63 MiB, so they were sharing runners with unit tests and could fail on noise. They now run only in their own `check_log_performance` job, opted in with `ORCA_CHECK_LOG_PERF=1`.
- Remove the pinned-baseline checkout and `ORCA_CHECK_LOG_BASELINE_DIR`. The baseline comparison is now a manual step that reads the pinned ref from local Git history.
- Run the current-implementation checks with `compareBaseline: false`, which skips baseline builds and audits.
* Inline check-log fixture and drop dedicated perf CI job
- Replace the captured failure-log JSON with a synthetic log built by `createCheckLogFixture()`, so no scraped job output or provenance metadata is kept in the repo.
- Remove the `check_log_performance` CI job. The log performance suites now run only when `ORCA_CHECK_LOG_PERF=1` is set, as a manual benchmark.
- Update the benchmark scripts and tests to use the new fixture helper and drop the `fixturePath` parameter.
* feat(checks): add optional job log outcome
* fix(checks): preserve host capture lifetimes and safe outcomes
* fix(checks): retrieve and retry bounded failed-job logs
* test(checks): cover queued snapshots and captured error safety
* test(checks): verify sanitized host excerpts and paired failures
* test(checks): identify measurements and preserve paired cleanup
* Keep check details load failures safe and localized
* Describe failed check logs as bounded excerpt context
* Normalize check log excerpts once at renderer ingress
* feat(checks): add optional job log outcome
* fix(checks): preserve host capture lifetimes and safe outcomes
* fix(checks): retrieve and retry bounded failed-job logs
* test(checks): cover queued snapshots and captured error safety
* test(checks): verify sanitized host excerpts and paired failures
* test(checks): identify measurements and preserve paired cleanup
* Use the generated check-log fixture in host retrieval tests
* Keep check details load failures safe and localized
* Describe failed check logs as bounded excerpt context
* Normalize check log excerpts once at renderer ingress
* Repair ingestion test fixture and benchmark names after dependency merge
* fix(agent-hooks): retained agent exits survive restart, late PID-less hooks, and same-second attaches
- Persist proven exits with the hook store's status file and restore them on hydrate (#29359).
- Clear a retained exit only on evidence of a successor agent: a different process identity
or a new turn/session, never a PID-less late hook from the ended session (#29356).
- Forget a pane's earlier exits when a remote worker attaches, so the attach fence no longer
rounds up and drops exits in the creation second (#29293).
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agent-hooks): provider-aware successor turns clear a retained exit; older spool replays keep it
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agent-hooks): another provider reporting clears a retained exit; same-second spool stamps count as successors
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agent-hooks): judge a successor claim by the sender's own identity
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agent-hooks): provider switches clear an exit only from hooks; drop pre-exit spool replays
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): an opted-in desktop serves its browser to a managed SSH host's orca CLI again
* fix(ssh): scope bridged pages through the renderer, render them as desktop pages, and route the e2e
* fix(ssh): bridge refuses file URLs and clipboard reads, revokes on disconnect, closes desktop tabs
* fix(ssh): judge bridged paste chords as the page sees them; accept long workspace ids
* fix(ssh): bridged clipboard denial follows popups; revoked commands never leave the browser queue
* fix(ssh): refuse option-like host values bound for agent-browser argv; report undeliverable results
* fix(ssh): bound bridged error results and fall back when the host refuses one
* fix(ssh): bridge only routed host tabs, and bound bridged page scripts by a deadline and revocation
* fix(ssh): cap waiting bridged commands below the desktop socket's result capacity
* fix(ssh): bridge revokes on routing changes, checks each page's route at run time, and cancels host-driven downloads
* fix(ssh): name the bridge registry's owner type
* fix(ssh): bridge refuses pages showing a local document, and never starts a command past orcad's deadline
* fix(ssh): bridged commands may leave Chromium error pages, and stop starting before orcad's deadline
* fix(ssh): ask for a fresh opt-in to host CLI control saved before it drove the desktop browser
* test(ssh): type the bridge registry deadline test
* fix(agents): stop the wake sweep from forking sessions that live SSH panes still own
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(agents): only treat an ssh-bound leaf as owned while its target is unreachable
A connected target's live-PTY map is authoritative: sleep kills the PTY but keeps
its id. Cross-worktree session dedupe ignores finished and invalid records, the
same records claim-key dedupe skips.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(agents): use the tab pty only for a single-leaf layout when judging ssh ownership
In a split tab the tab-level pty can belong to another leaf.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: route wake-sweep fixtures through one checked store setter
* fix(agents): a record whose host moved can't claim a session in the wake sweep
* fix(agents): a cross-workspace claim must name the same machine as the record
* fix(agents): resolve each record's machine from its own catalog or stamp evidence
* fix(agents): judge a cross-workspace claim's host from owner rows, not the focused server
The focus-read ratchet rejects new reads of the routing default; comparing two
records' machines needs only each record's own workspace owner row.
* fix(agents): resolve a folder workspace's host before it can claim a session
---------
Co-authored-by: Robert Nisipeanu <github@nisipeanu.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(ssh): finish the macOS launch-session warning's gaps (#29382)
Check it on automatic restores, once per SSH connection; create the data root
before recording; inherit a session only from this root's verified daemon; and
let only a verified restart clear the warning.
* fix(ssh): recheck the launch session per reconnect generation, after the restore update
* fix(ssh): reapply a known ended session to a replaced status; leave an unreadable daemon's attribution unknown
* fix(ssh): expire live launch-session verdicts, and drop an answer outlived by its connection or server
* [CI] Keep transport-only tests outside the app store graph
* [CI] Advance the updater install clock in failure tests
* [CI] Avoid descriptor formatting in replay clip counts
* [CI] Advance native-chat test observation clocks
* [CI] Restore observation timers after failed test setup
* [CI] Clean up final headless daemons during public close
* [CI] Keep serializer test oracle out of production smoke triggers
* [CI] Keep three unit fixtures out of production smoke triggers
* [CI] Bound frozen-clock close callbacks in tests
* [CI] Avoid the app-store graph in feedback dialog tests
* [CI] Keep hidden popup fixture compositor frames active
* test(ci): reuse mobile runtime fixture for pairing listeners
* test: restore native chat clocks before rig cleanup
getTerminalInteractiveWait read only the line tail, which drops the rows
below the cursor Claude parks inside its dialog, so the query answered
"no wait" while the trust dialog owned the screen. For Claude panes it
now reads the rendered screen (bounded, discarded if newer output or a
pane exit overtakes the read) and lets a dialog seen there stand unless
a newer working or explicit idle signal clears it. Other agents keep the
synchronous path, so concurrent queries still share one hook probe.
Ported from #28187 onto the split runtime modules.
Co-authored-by: JMensch <james@jamesmensch.com>
A sortOrder-only change now bumps a separate sortOrderEpoch. The sidebar sort
recomputes on it only while the active mode ranks by persisted sortOrder (Smart
before a live signal, or Manual), neither of which saves the order, so live
Smart still cannot loop on its own saves.
Fixes#29336
The relay caches one status row per pane, so a hook that lands while the
desktop is away overwrites the PostToolUse that ended the request the
desktop still shows as waiting. On reconnect the desktop gets only the
newer working row and its permission hold keeps the stale wait.
The relay now keeps a bounded list of Claude requests it saw end
(PostToolUse/PostToolUseFailure tool_use_id plus child owner) for the
same launch and session, and sends it beside the replayed row as a new
optional field. The desktop releases a held wait only when a replay
lists that exact request from the same host, launch and session. Older
peers ignore the field and keep today's behaviour.
Validated against hooks captured from a real Claude Code 2.1.301 session.
Part of #28186.
Co-authored-by: JMensch <james@jamesmensch.com>
Detach the page's debugger before destroying its offscreen window, so a live
screencast closes before its in-flight snapshot can call capturePage on the
half-destroyed page (an Electron segfault). Also wait for the paired workspace's
first terminal in the link-routing e2e so its late focus cannot hide the pane.
The desktop keeps the toggle and its dismissed-suggestion flag in its own
profile, but the host read its own copy on create, so local main never
fast-forwarded and the suggestion toast kept returning. Send both with
worktree.create, like nestWorkspaces; older clients keep the host's value.
Fixes STA-6997
* fix(remote): send again a tab close the paired host never received
Closing a paired host's tab prunes the local mirror at once and asks the host
to close it. When the connection dropped mid-close the renderer cleared its
close intent, so the host's next snapshot after reconnect brought the tab
back. A user close that fails without reaching the host is now held: the tab
stays hidden, and the close is sent again when a snapshot proves the host is
reachable and still lists it, up to three times before the tab is shown.
* fix(remote): replay held closes at snapshot receipt, pinned to their pairing
Codex review: an unchanged reconnect snapshot is filtered before apply, so the
replay now fires when any snapshot is received; a replay is refused once the
environment is re-paired; replays survive a subscription reinstall and
re-assert their hide; giving up re-accepts the current snapshot so the tab
actually returns.
* fix(remote): consume held closes only on authoritative snapshots, and unhide evicted ones
Codex review: an unpublished or retired-epoch frame could drop a pending
replay, and an evicted replay kept its tab hidden with nothing left to send.
* fix(remote): keep held closes through incomplete inventories, and re-hide in-flight ones
Codex review: a real-epoch snapshot can omit tabs it is unsure of, so absence
no longer drops a held close; and a snapshot listing a tab whose close is in
flight re-asserts its hide after a subscription reinstall.
* fix(remote): re-list the worktree after a failed close replay
Codex review: subscriptions are change-driven, so an idle worktree might never
publish the snapshot that makes the next attempt due.
* fix(remote): re-list the worktree as soon as a close is held
Codex review: an idle host may publish nothing after a held close, so the first
replay also needs a scheduled re-list.
terminal send/read/wait listed --environment and --pairing-code with no
description, and a paired server's handle sent without --environment failed
as terminal_handle_stale with no hint that a server flag was missing.
Refs STA-6812
Main refuses to remove an SSH host its managed server still holds, so the
host card's Remove confirmed and then showed a raw IPC error. Detect the
managed host before confirming and offer Managed servers, whose Stop and
Forget release the host; strip the IPC wrapper from other removal errors.
* fix(agent-resume): quote resume commands for the remote host's reported OS
A Windows SSH host's resume line was always quoted for POSIX because any
connectionId resolved to linux. Use the platform the host reported, and
honour a remote cmd.exe tab's own shell override.
Fixes STA-3779
* fix(agent-resume): keep WSL quoting on a Windows SSH host; seed host state in quoting tests
* fix(agent-resume): apply reported host OS to SSH hosts only
* fix(agent-resume): read the SSH host's platform from the resolved host id
Keeps the focus-read ratchet flat: no second worktree-routing lookup.
The execution host upgrades a remote worker's fleet/show verdict to exited
only on its own proof: the hook store retains a proven agent exit (fenced to
the attachment, cleared by a replacement agent), and after a reboot the
process table proves the PTY gone. Lost contact stays unverifiable.
Fixes#24353
Co-authored-by: Claude <noreply@anthropic.com>
* fix(terminal): log a failed pane reveal instead of rethrowing it
The bridge already replies false when the reveal throws, so the rethrow only
surfaced as an uncaught renderer error after the caller had its answer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(terminal): refuse terminal.reveal from paired clients
It moves this host's window. terminal.focus lets a paired client do that only
by asking for host navigation, and the callers this method serves (the CLI and
companion apps on the same machine) never arrive as paired clients.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(terminal): show a paired server's terminal in this desktop on terminal switch
A paired `orca serve` host has no window, so `orca terminal switch --environment <name>`
resolved the handle there, answered `navigated: false`, and the desktop stayed put (#24688).
Add `terminal.reveal`, a desktop runtime method that names a pane by worktree, tab and leaf, as
the owning host knows them, and runs the Agent Dashboard's click-to-focus sequence before raising
the window. When a paired server cannot navigate, `terminal switch` asks the desktop on this
machine to reveal the terminal instead.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(e2e): terminal switch on a paired server moves the desktop on this machine
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: update terminal manifest count after main and assert the revealed pane renders
Main added a terminal method since this branch was cut, so the characterization
count is 39 with terminal.reveal. The e2e now proves the target pane is visible
in the DOM, not only selected in the store.
Co-authored-by: Velm14 <Velm14@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Velm14 <Velm14@users.noreply.github.com>