Commit Graph
2468 Commits
Author SHA1 Message Date
NeilandClaude 44f97fa958 fix(agent-status): keep a running Codex turn on paired and mobile views while /side is shown (#33308)
* fix(agent-status): keep a running Codex turn on paired and mobile views while /side is shown

Codex's terminal title follows the thread it displays, so opening /side idles the title while the main turn runs. The paired/mobile session projection read that idle title, newer than the main turn's last hook, as the turn ending and published a title-only done row with no prompt. A Codex row whose hooks report the main turn running now owns the title interval; the turn's end still arrives by hook or rollout.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(agent-status): keep a Codex main turn waiting on approval through /side on paired views

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(agent-status): leave restored Codex rows to the existing title rules

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(agent-status): stop a running Codex turn at a PTY process replacement

A replacement process on the same PTY id invalidates its predecessor's title-interval evidence; record when that happened so a predecessor's running Codex hook cannot outlive it.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 17:01:30 -07:00
Neil ba43f8539c fix(runtime): a cold-restored terminal keeps its agent session without showing the old Done (STA-12043) (#33302)
* fix(runtime): a pane's new process does not wear its previous run's Done (STA-12043)

* fix(runtime): keep the superseded turn's segment and date process start at spawn request

* fix(runtime): take the identity row's segment from the turn clients were shown
2026-10-24 17:00:48 -07:00
Neil d778369541 refactor: group runtime session ownership (#33301) 2026-10-24 16:40:25 -07:00
NeilandBen Grynhaus e18752acea fix(terminal): answer a background pane's startup queries while its paired view is paused (#33273)
* fix(terminal): answer a background pane's startup queries while its paired view is paused

A paired desktop pauses the output of a terminal it is not showing. The host
drops that output, but it still counted the paused view as the one answering
terminal queries, so main's query responder stayed silent too. A fish started
in such a pane (for example by `orca terminal create`) sent DA1, got no
answer, and stalled 10 s before warning.

A multiplex stream now registers with its pause state, and main answers the
queries of output a paused view drops, exactly as it does for output the
hidden-pane gate drops. A view that receives the output still answers it, so
each query is answered once.

Fixes #32944

Co-authored-by: Ben Grynhaus <ben.grynhaus@wiz.io>

* fix(terminal): answer queries in output a pausing remote view discards unsent

A chunk ingested while the view was still receiving yields its queries to
that view. If the view then pauses, the host drops the chunk from the
output batcher, or clears it from the ack backlog, before the view ever
parses it. Nothing answered those queries.

The pause now carves the query bytes out of what it discards, with the
same extraction the hidden-pane gate uses, and hands them to main's
existing responder. The responder answers only while no other view is
receiving that terminal's output.

Refs #32944

Co-authored-by: Ben Grynhaus <ben.grynhaus@wiz.io>

* revert: answer queries in output a pausing remote view discards unsent

This reverts commit 3fc500fe09. Answering those queries later through
main's emulator injects bytes into its live parser. That can cancel a
partial escape sequence, it answers cursor and mode queries against
state that has since moved on, and it can answer twice when two views
pause one after the other. An exact fix has to capture the replies when
the bytes are first parsed and track delivery per view, so it is left as
a follow-up.

Also renames a test helper to say what it reads.

---------

Co-authored-by: Ben Grynhaus <ben.grynhaus@wiz.io>
2026-10-24 16:14:02 -07:00
Neilandoutof-place c788a071d4 fix(terminal): fold Codex's inner thread-name spinner frame so session tabs stop republishing per tick (#33291)
Fold a standalone copy of the leading spinner frame in agent titles (Codex 0.155–0.163 `⠙ renaming... ⠙ | x`) so the title tracker stops republishing session tabs every 100ms. Fixes #27598. Supersedes #27599.

Co-authored-by: outof-place <69466055+outof-place@users.noreply.github.com>
2026-10-24 16:08:28 -07:00
Neilandzhangxaochen 70d9c97b98 fix(rate-limits): find agy for Antigravity usage in the env agent launches use (#33289)
Antigravity usage probe uses the same host shell env agent launches use, so agy is found under Git Bash on Windows. Fixes #31459, #28036. Supersedes #32032, #31809.

Co-authored-by: zhangxaochen <3416667+zhangxaochen@users.noreply.github.com>
2026-10-24 16:08:07 -07:00
Neil 293bfe795d fix(browser): closing a host page retires a window-saved tab whose ids differ (STA-11998) (#33266) 2026-10-24 14:55:44 -07:00
81a6cea193 fix(cli): say a headless terminal create found no workspace, not that the window is missing (#33264)
* fix(cli): say a headless terminal create found no workspace, not that the window is missing (#33044)

On a windowless `orca serve` host, `orca terminal create` from a folder
outside any workspace failed with "No renderer window available". The
create falls through to the window-only path because no workspace was
resolved, so name that and point at --worktree instead. Behavior is
unchanged; only the error message for this already-failing create.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(runtime): pass the no-workspace message to the window lookup instead of relabeling every error

A catch-all would rename any other window-lookup failure as a missing workspace.

---------

Co-authored-by: tnfhrnsss <tnfhrns@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:46:49 -07:00
NeilandClaude ad991175da fix(runtime): a split agent tab's shell panes restart as shells, not fresh agents (#32418)
Starting a saved pane with no live terminal (a paired client opening it, or a restore)
fell back to the tab's launchAgent for every pane with no recorded session, so a shell
split beside an agent pane came back as a fresh agent. The host's tab projection now
publishes the tab-wide launchAgent only on a sole pane, as the window already does, so
the pane start and every reader of that projection see the same per-pane evidence.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 14:35:02 -07:00
NeilandBearMett 917899bf8d fix(runtime): stop paired desktops republishing mirrored editor tabs (#33259)
* fix(runtime): stop paired desktops republishing mirrored editor tabs

When two desktops pair both ways, B mirrored A's editor tabs and then
published them in its own session inventory as if B had opened them. A
then applied B's copy, which took over A's own tab.

isMobilePublishableOpenFile now skips rows marked
mirroredFromRuntimeSession, the marker is part of the open-files sync
projection so a flip republishes, and the marker is saved with the
session and restored so a relaunched client does not republish its
restored mirrors before the host's first snapshot.

Based on #25877 by BearMett.

Co-authored-by: BearMett <agomdole@gmail.com>

* fix(runtime): keep restored mirrors through unpublished frames and out of headless publication

A restored mirror now carries its marker, so an unpublished host frame
must not cull it (and its recovered draft). The windowless publication
skips persisted mirrored rows for the same reason the window does.

Co-authored-by: BearMett <agomdole@gmail.com>

---------

Co-authored-by: BearMett <agomdole@gmail.com>
2026-10-24 14:28:33 -07:00
Neil ceec67b8ec fix(wsl): no synchronous distro-home probe at startup, and no 5 s kill of a booting distro (#33179)
* fix(wsl): no synchronous distro-home probe on the main process at startup, and no 5 s kill of a booting distro

Follow-up to #33175, from the startup wsl.exe trace:
- getAllowedRoots (file-access authorization) resolved a WSL-mirrored workspace
  root with the synchronous home probe; it and isPathAllowed are now async (every
  caller already was).
- skills:discover resolved a WSL target with the synchronous probe on load; the
  skill target resolution is now async.
- the async home probe waits up to 60 s instead of killing wsl.exe at 5 s, since a
  kill mid-boot leaves the distro failing every call with E_UNEXPECTED.

* fix(wsl): bound file authorization's wait for a WSL home; no sync probe over an async one

Review follow-ups: a hung distro must not hold every file check for the async
probe's 60 s, so authorization waits at most 5 s for a mirrored root's home and
falls back while the probe keeps running and fills the cache. And a synchronous
getWslHome no longer starts its 5 s-bounded probe while an async one is waiting
out the same distro's boot.

* chore: prune the main-process sync launch ratchet (one fewer sync wsl.exe)
2026-10-24 13:13:55 -07:00
Neil 8733ec4033 refactor(persistence): colocate test fixtures and harness (#33233) 2026-10-24 11:56:22 -07:00
Jinjing 42f59f3460 Allow saving quick command lists beyond forty (#32972)
* Allow saving quick command lists beyond forty

Preserve compatibility by limiting older clients to readable commands

* Cover quick command persistence across client capabilities

* Reject edits to hidden quick commands from stale clients
2026-10-24 10:42:55 -07:00
Neil df6bb97c14 refactor(codex): colocate app-server protocol owners (#33176) 2026-10-24 08:40:26 -07:00
Neil b900d9843f refactor(shared): colocate Jira rules and direct imports (#33164) 2026-10-24 06:46:07 -07:00
Neil ca4f4b4611 refactor(runtime): group session teardown and tab ownership (#33142) 2026-10-24 04:42:57 -07:00
OrcaWinandOrcaWin 7868c30ce7 test: trim redundant cases and repair session and hook assertions (#33121)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 03:19:34 -07:00
OrcaWinandOrcaWin abc5737cc1 test: remove duplicate mobile coverage and strengthen regression assertions (#33106)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 02:29:47 -07:00
NeilandClaude 0b4028ae99 fix(worktrees): don't prune worktrees the other Git (Windows or WSL) created (#33103)
* fix(worktrees): don't prune worktrees the other Git (Windows or WSL) created

On Windows one repo can hold linked worktrees written by both Git for Windows
(link C:/...) and WSL Git (/mnt/c/..., /home/...), e.g. after the project runtime
toggle switched it. Each Git reads the other's links as missing, and the
`git worktree prune` Orca runs after removing a worktree (and before deleting a branch
held by one) deleted those worktrees' metadata, leaving checkouts git no longer knows.
All five prune sites now go through pruneGitWorktrees, which reads the link files first
and refuses (through each site's existing failure path) when any link is foreign to the
Git that would run.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(worktrees): lock the other Git's entries for the prune instead of skipping it

Review: refusing the whole prune broke stale-registration recovery and left branches
undeletable in mixed repos, and reading <cwd>/.git/worktrees missed bare and linked
checkouts. Now the other Git's entries get a marked lock file for the prune (git never
prunes a locked entry; a user's existing lock is kept), the rest prune as before, and
the admin dir comes from rev-parse --git-common-dir once routing is settled.
//server/share links read as Windows Git's.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(worktrees): drop a type assertion from the prune guard test

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(worktrees): serialize guarded prunes per repo and reclaim stale guard locks

Re-review: two prunes in one repo could interleave so the first's unlock let the
second prune the other Git's entry, and a crash between lock and unlock left a lock
read as the user's forever. Prunes now chain per admin dir, and a lock carrying the
guard's own reason is reclaimed.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(worktrees): assert the overlapping prune waits, not only its outcome

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 02:26:50 -07:00
Neil 1e341b6edf fix(terminal): reject creates for durably closed tab identities (#32190) 2026-10-24 02:16:29 -07:00
Neil 1af7bc6c77 fix(runtime): preserve working hooks across idle title repaints (#32195) 2026-10-24 02:11:07 -07:00
Neil b33ad1bb2c feat(remote): trace why each remote-runtime connection closed (#33094)
Client control-socket closes and server heartbeat reaps only reached the
console, which a packaged app never shows. Each control-socket close now
writes a remote-runtime.control-socket-close span (cause, close code,
error code, last keepalive RTT, socket and ready uptime). Each server reap
writes remote-runtime.server-socket-reaped. Both land in the main trace
file that Send app diagnostics collects. Keepalive pongs now measure RTT,
exposed as lastRttMs and lastCloseCause on the shared-control diagnostics.
2026-10-24 01:43:08 -07:00
OrcaWinandOrcaWin 0c06f1f082 test: consolidate duplicates and strengthen behavioral boundaries (#33088)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 01:22:18 -07:00
Neil 28258c0ba9 fix(terminal): keep host-retired panes out of every layout a paired client writes (#33083)
Exiting a paired host tab's original pane moved the host's active leaf to the
surviving split. The client's reconciliation effect followed it with
setActivePane, and that persisted every still-mounted pane, writing the retired
leaf (whose remote PTY had not ended yet) back into the layout. The leaf was then
no longer retired, so its pane stayed forever. Any other persist in that window
(insertions, clicks, title edits, divider drags) could do the same.

persistLayoutSnapshot now drops the leaves the host retired, using the
retireLeavesFromTerminalLayout main already uses, moved to shared. The retired
set it reads is the one the reconciliation effect tracks.
2026-10-24 01:08:08 -07:00
f0a399d143 fix(linear): accept normalized Markdown save confirmations (#30076)
* fix(linear): tolerate documented Markdown normalization

* fix(linear): preserve fenced Markdown literals

* docs(linear): describe Markdown fence matching

* fix(linear): close nested Markdown fences

* fix(linear): tolerate documented Markdown normalization

* fix(linear): preserve fenced Markdown literals

* docs(linear): describe Markdown fence matching

* fix(linear): close nested Markdown fences

* fix(linear): compare only parsed list marker rewrites

* style(linear): satisfy save confirmation quality gates

* fix(linear): preserve list structure and load parser on demand

* fix(linear): bundle save Markdown comparator for every runtime

The comparator resolved unified/remark at runtime via createRequire, which
orcad's esbuild bundle does not ship. Import them statically from a lazily
imported module so Electron and orcad both inline the parser.

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:34:01 -07:00
f4a3860073 fix(runtime): retain completion subtitles for paired workspaces (#9930)
* fix(runtime): keep lastAssistantMessage when demoting idle agent status

Paired clients mirror the mobile/session terminal projection. Demoting a
stale working status under a neutral title dropped lastAssistantMessage, so
remote workspace cards lost the completion subtitle that local cards still
show from the in-process hook row.

Fixes #9914

* Format completion field projection consistently

* Preserve completion classification and bound interrupted verdicts

* Keep completed replies out of new live title intervals

* Restrict retained completion previews to completed title status

* Keep mobile session projection within lint file budget

* fix(runtime): keep only completed-turn replies in demoted mobile status

Mid-turn hook snapshots carry tool stdout as lastAssistantMessage; a shell or idle
title must not surface that as the final reply.

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 00:33:41 -07:00
Brennan Benson 6059a5f7fa Native chat: a message is never marked not delivered after a crash, and reopening a chat stops reading the transcript (#30580)
* Native chat: never mark a message not delivered after a crash; reopening a chat stops reading the transcript

After a crash or restart, attach read a window of the agent's saved log and marked any
unconfirmed message missing from it as rejected "not delivered". Absence from a log Orca
does not own is not proof: the window's anchor moves every turn (STA-7416) and its text
filters drop tagged prompts (STA-7453), so messages the agent received were marked
undelivered and retyping them duplicated the question. The read also ran on every attach
(STA-7434).

Delete the inference: the provider history window and its adapter hooks (Claude window and
branch proof, OpenCode stored-messages reader), the submission matcher, and the attach step.
A send the crash boundary leaves unconfirmed stays an ordinary sent message; the interrupted
turn still ends through the existing settlement. Explicit live refusals are unchanged.

A queued card whose hand-off a restart left unconfirmed no longer counts as able to return
to the queue, so it stops keeping an idle chat loaded.

* test(native-chat): update restart fixture calls after history removal

* Hide rewind targets without a saved native boundary

* Run rewind boundary regression across host and renderer

* Keep incoming main package-manager lock unchanged

* Test recovered chat consumer outcomes without delivery inference

* Assign the complete cursor in recovered phone regression fixture

* Supply presentation capture in rewind boundary fixture
2026-10-24 00:27:01 -07:00
OrcaWinandOrcaWin 70c4c55db9 test: remove duplicate coverage and strengthen boundary assertions (#33047)
* test: strengthen launch, subject identity, and release witnesses

* test: strengthen approval paths and prompt answer boundaries

* test: strengthen conversation names and mobile log persistence

* refactor: group background task stop contract with stop operations

* test: strengthen question, title, and release validation

* test: distinguish row text and generated dispatch titles

* test: verify mobile revival events and retry exclusivity

* test: reject incomplete child row output assertions

* test: verify copied modules and option selection contracts

* test: verify mobile host state and status retry boundaries

* test: observe connector refusals and real diagnostic delivery

* test: consolidate platform memory coverage and verify bounded work

* test: allow cached legacy failure reads within work bounds

* test: verify packaging boundaries with independent fixtures

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 00:13:25 -07:00
Neil 156f3f4ef0 fix(ports): say direct SSH hosts don't support port detection yet (#33055)
The host-scoped port scan reported 'Remote connection dropped' for every
direct-SSH workspace even with a healthy connection: the relay has no port
scan, so the connection was never the problem. Name the real limitation
and point at an Orca server on the host, which scans its own ports.
2026-10-24 00:09:58 -07:00
Neil 461d6b44c3 test: expect legacy failure reads to reuse the published display (#33061) 2026-10-23 23:55:54 -07:00
Brennan Benson 8fd32eccd7 Show retained chat messages after reconnect without reopening (#32255)
* fix(native-chat): publish retained history corrections to capable clients

* test(native-chat): expose canonical reads in phone history fixture

* test(native-chat): migrate remaining history reader fixtures

* fix(native-chat): preserve expanded history on late acceptance

* fix(native-chat): share lazy correction metadata by journal source

* fix(native-chat): reuse raw legacy display selections
2026-10-23 23:19:26 -07:00
Brennan Benson f04b831a61 Record Stop immediately and settle subagents from exit proof (#28051)
* Observe Claude readiness through the host status

* Keep optional readback behind host readiness

* Recheck launch permission after host readiness

* Preserve the awaited child during permission revalidation

* Await Codex protocol open under host ownership

* Await ACP protocol open in the host

* Derive ACP opening from its protocol session facts

* Exercise unanswered ACP options with an explicit silent route

* Await Pi open and fail signed-out construction

* Read Pi catalog facts from awaited opening

* Keep the Pi fixture exit observer

* Record failed owned opening before the first message

* Abort a matching provider open when its child exits

* Cover provider exit during an unanswered host open

* Cover explicit failure facts on a host-stopped child

* Capture message choices per accepted send and preserve picker state

* Apply accepted Claude message choices at prompt handover

* Apply captured message choices at provider dispatch

* Bind native chat choices to accepted message dispatch

* Reset Codex turn choices for each captured message

* Resolve Codex message defaults from native facts

* Align message choice and Pi default fixtures with provider dispatch

* Align option fixtures with saved message choices

* Match Codex message choices to the active turn origin

* fix: let pending commands reach handover refusal

* Abort pre-prompt message choice writes with host-owned wait

* Verify message choices through provider input boundaries

* Keep legacy mobile test hosts explicit about message choices

* Align Stop close causes and request-note fixtures

* test: align ACP Stop note fixtures with settled behavior

* Verify close intent reaches the native adapter

* Record the Stop request before waiting for interruption

* Keep Stop cleanup independent of request-note persistence

* Keep startup waits cancellable and preserve operation order

* Derive Codex startup exits from host phase

* Open Codex protocol in background-task test fixtures

* Keep ACP startup ownership until handshake completes

* Run ACP opening catalog fixtures with Node SQLite

* Verify saved ACP picks without live option writes

* Keep ACP catalog bookkeeping from failing an options read

* Restore signed-out restart and fail Pi startup faults

* Run Pi opening catalog fixtures with Node SQLite

* Preserve message choices across cold clients and kept cards

* Remove the unused provider option restore ledger

* Clarify launch choices and legacy option hydration

* Remove remaining restore-ledger forwarding and assertions

* Remove unused ACP and Pi restore bookkeeping

* Report late Codex exits and keep card Cancel notes scoped

* Remove the Pi close wrapper left by unused cause plumbing

* Update Stop tests for the simplified close contract and Node journals

* Update remaining close and interrupt assertions after cause removal

* Keep live choices and delivery order while agents start

* Drop retired startup revision from Codex opening

* Keep live-option cancellation until message choices and clarify catalog reads

* Hold ACP option replies in live-write cancellation regressions

* Remove retired Pi startup revision and match saved-pick behavior

* Check running choices and retained rewind messages after delivery integration

* Use observed root exit in Codex close reports

* Apply startup picks and let parked messages release operations [skip ci]

* Remove unused Codex opening report field [skip ci]

* Keep ACP restoration facts local to the provider [skip ci]

* Apply Pi startup picks after readiness and remove unused open facts [skip ci]

* Cover goal changes during the held Codex message wait [skip ci]

* Save a waiting startup pick after Stop ends its opening [skip ci]

* Save choices when startup ends or a replacement is still opening

* Check saved choices separately from Claude startup reports

* Release scripted Codex startup before awaiting queued picks

* Check the saved picker selection in per-message choice fixtures

* Exercise current choices and eager client capabilities in runtime fixtures

* Keep accepted Stop requests in runtime settlement assertions

* Reuse runtime fixture reads and type the fake connection directly

* Type the Codex child-proof fixture connection directly

* Align startup fixtures and preserve observed Claude exits

* Honor queued send authorization in Claude runtime fixtures

* Open Codex protocol sessions before Stop fixture dispatch

* Wait for the opened Codex child before runtime notifications

* Keep repeated permission picks usable and align Codex fixtures

* Validate pending picks against the live option state

* Order startup picks before later messages and retain flow regressions

* Keep reviewed lifecycle contracts while pruning duplicate tests

* Save picks after a proven exit without requiring a child

* Type the startup reply fixture against the Claude control contract

* Keep Codex startup flow contracts and remove overlapping adapter tests

* Restore the committed dependency lockfile after test tooling

* Keep startup recovery flows and remove duplicate Pi handshake cases

* Cover both Pi startup choices in the delivery flow

* Match the Pi prompt entry recorded by the scripted wire

* Honor the before-input callback in the startup reply fixture

* Keep message choice flows and remove overlapping selector tests

* Retain Stop flows and remove repeated reducer and adapter cases

* Keep Stop note writes independent of full session context

* Keep startup option menus independent and show the saved choice

* Exercise startup picks across real menus and the runtime

* Match startup picker fixture to saved message choices

* Keep saved effort visible when picking a model during startup

* Include the running-turn scope in repeated Stop fixture

* Use the current attach contract in import fixtures

* Keep the Codex journal translator within its line limit

* Match ACP startup fixtures to saved picks and late Stop behavior

* Check startup effort after saved picker choices are acknowledged

* Complete Stop target types and keep the effort regression concise

* Use requested picker values in the effort regression acknowledgement

* Exercise Stop receipt replay across a resumed provider child

* Require owned provider spawn and one host-awaited open (from #28055)

* Restore the live checks the startup cleanup's deleted tests pinned

One assertion each, in the surviving suites: a stale-generation exit is
ignored, a failed create lets its sink go, a readback before open is saved,
an idle stop that fails is retried, a no-pick Pi start teaches its default,
an older client's pre-spawn failure and packed answer, an undrivable chat
stays readable, the resting slash menu stays on its own host, the main agent
reads apart from subagents, restore publishes status, naming runs off the
host's status feed, and a throwing logger never reaches the operation.

Also corrects the reliability gate note that claimed the send-idempotency
suite replaced the refusal-retry tests.

* Read a rewind in doubt outside the chat's serialize and apply it in the commit

The started settle and the attach's ready-child path read the provider's
history first, then apply what it proves in a short commit that re-checks
the rewind it read.

* Accept messages while the agent starts: run the start outside the chat's serialize

The chat's serialize was both where a send was admitted and where its agent
started, so every slow step of a start (closing-child join, lease
reconcile, recovery probes, resolvers, history read, spawn, sink drain,
provider dispatch) held every later send.

A start now registers in one short step before its first wait: its abort
controller (which Stop, close and quit already reach outside the lock),
its startup clock (the 10-minute ceiling now counts from here) and quit's
drain. Every wait after runs outside the lock, raced with that signal, and
every write is a commit that first re-checks the start is still the
session's. A start that loses the check is late: its spawn is closed
through the post-acquisition failure path and nothing is handed to it.

- The delivery loop's prepare returns a start handle; the loop awaits the
  start outside, then re-prepares. Handover writes the pending row and
  begins the dispatch abort handle in one commit, calls the provider
  outside after re-checking the child, and resolves in a second commit.
- Ready operations claim a start instead of running one in their step.
- A client attach runs the same steps under one serialize, as before.
- An exit reported between spawn and publish is held for the publish
  commit, which ends the child it indexes.
- An unproven close still fails only what was queued when its start was
  asked for.

* Test that a send is accepted at every wait an agent start makes

One whole-flow test holds the start at each wait in turn (closing-child
join, reconcile probe, recovery re-probe, resolver, spawn, sink drain,
open, started recovery, pending row, provider call): a second send is
accepted at once, a Stop is recorded without waiting and nothing reaches
the provider, and what was sent goes to the published child once, in
order. Also quit before spawn, concurrent sends, a lease left recovering,
and an idle sweep racing a send.

* Bound each startup step on its own and drop the startup silence rule

A start no longer ends after 60 s without output: output is not protocol
progress. Each protocol step now has its own bound where its handshake
lives, and one 10-minute ceiling from registration stays as the outer bound.

- Codex: initialize 15 s and thread start/resume on the connection's 30 s
  default again, instead of the startup ceiling.
- ACP: initialize 60 s, authenticate 120 s, session new/load/resume/fork
  300 s; turns stay unbounded and option writes keep their caller's bound.
- Claude: the initialize answer has a 60 s bound; past it the open fails
  with the ordinary startup-failure words and the host ends the child.
- The onOutput plumbing that only fed the silence rule is removed.

* Count Pi ready once it reports its state

Pi's start no longer waits for its model listing and slash commands.
The open returns after get_state and the unknown-provider check; the
listing then arrives as an options report (with the catalog listing the
start resolved) and the commands as a later publish. An empty model list
no longer fails the start as signed out: a signed-out Pi runs the unknown
provider, which still fails it, and a prompt it cannot run is refused
with the sign-in words by the existing prompt path.

get_state keeps the connection's 30 s request bound instead of the
startup ceiling.

* Test that model and effort picked while a chat is created ride its first message

A slow host's create reply arrives after the user picked a model and effort. The picker's view
layers the launch's held picks over its seed and the host's saved selection, and every send
carries what the picker shows, so the first message carries the picks whether the host's status
names its selection or not, and while the last pick is still on its way to the host.

* Start a phone's new empty chat on its first message

The phone's new chat is an agent.launch with no prompt, which always created the chat with its
agent started, while desktop chats start on the first message. The phone now advertises the
create-without-start capability, and a launch with no prompt from a caller advertising it creates
the chat at rest, keyed in the ledger by its create intent as a client's create is. Launches with
a prompt, the CLI (which advertises nothing), orchestration workers and ensure still start at once;
an older host ignores the capability and stays eager.

* Take main's lockfile; the branch changes no dependencies

* Restore main's exact text in files the merge only reformatted

The pre-commit formatter rewrote 37 of main's files staged by the merge, though this branch never changed them, pushing two past the line limit. Committed without the hook because the hook would reformat main's text again; CI lints the result.

* Fit the startup tests to the owned-spawn, host-awaited-open contract

Test doubles gain the mandatory open and an acquisition generation, the
removed started event gives way to the rig's proveStart, and two literal
types are tightened for CI's typecheck. A Stop during a held start writes
no host note on this branch: the taken-back send draws its own
stopped-before-start row, which the startup-attempt test now asserts.

* Fit the remaining CI checks to the startup limits and current releases

- Run agent-launch-at-rest.test.ts in the SQLite Node runtime project.
- Pin the send-options cross-version case to v1.4.228, the first published release with agent-session.send-options.v1; v1.4.226 was never pushed.
- Load the baseline's isStructuredTab from src/shared (moved in v1.4.228), falling back to the renderer path for older baselines.
- End the never-proven start with a short startup ceiling: the silence limit it relied on is gone, and the ceiling counts from registration.

* Read the option stamp from the chat's one host-execution selection

* fix: preserve startup authority and accepted agent choices

* Retain dispatch authority and reconcile accepted options

* fix(native-chat): preserve accepted choices and cancellation authority

* Restore established client selection flow

* Preserve provider output observation and typed startup fixtures

* fix: preserve initial Claude launch choices and effort readback

* test: complete Claude catalog session fixture

* Preserve incoming main files after merge formatting

* Preserve compact handover through startup merge

* Type instant startup fixtures before their open is supplied

* Keep browser sign-in under its existing expiry owner

* Finish the main merge: retired-model display, start rewind cut, new tests

A model the Claude CLI retired is reported again, so the picker shows what the child runs and the
next send moves the chat off it. The start reads a rewind in doubt against the handle it answered
with, the record its commit then applies to. Main's tests follow the start's open: the admission
fixture holds a real turn, and a sign-in close during setup fails the open.

* Type the start's rewind record as the store returns it

* Type the Sleep test's held open as the provider open it replaces

* Sleep counts a chat stopped once the start it aborted leaves it at rest

A start runs outside the chat's lane, so Sleep's step can run before the start it aborted has
settled; it then re-reads the chat once that start is done, instead of reporting it unverified.

* Keep Sleep's start-abort bookkeeping beside the rest of Sleep's stop

* Keep the listed background Stop lifetime cases

The two #29905 cases lived in the turns test this branch removed. They pin that a listed Stop never ends a helper lifetime it did not capture: a same-id restart during acceptance, or a refused helper that appears mid Stop-all, which must not close the agent.

* Reconcile test registries and queued provider work accessor

* Update CI scope fixtures after removed connection test

* Bound native chat Stop protocol steps and omit empty startup feedback

* Supply current session fence in empty-start Stop test

* Align goal cancellation test with accepted-message feedback

* test(native-chat): cover distinct repeated Stop through Codex adapter

* fix(native-chat): save dispatch proof before Stop closes the child

* ci: retrigger checks for Stop dispatch repair

* Keep accepted Stop effects through dispatch settlement

* Capture earlier Stop evidence before writing request feedback

* Preserve named Stop runtime effects across turn advancement

* Combine projection imports in named Stop regression
2026-10-23 22:10:55 -07:00
Kelvin Amoaba 785daec1e1 fix(browser): show profiles changed outside the window in the profile menu without a reload (#32723) 2026-10-24 05:06:34 +00:00
Brennan Benson 2e278adeac Chat worker errors alert the orchestration coordinator (#31941)
* feat(orchestration): a chat worker's turn-ending error alerts its coordinator

A native-chat agent working a Dispatch whose turn ends in an error (usage limit, signed out,
provider refused, agent died mid-turn, failed start) now sends one high-priority escalation to
the Run that dispatched it, carrying the error sentence the chat recorded plus the agent's own
detail. The task stays assigned to the chat. Terminal workers already escalated on exit; both
now share one escalation helper.

On by default; a new switch on the Agent Orchestration settings page turns it off. It only
works for agents in Chat UI, so it is disabled while Chat UI is off.

* fix(native-chat): observe committed worker request failures

* Fix chat error alert fixture types and catalogs

* Keep chat failure alerts scoped to their request and journal lifetime

* fix: capture pending chat failure diagnostics before escalation

* fix: type chat failure observation fixtures and journal reads

* fix(orchestration): use checked database during worker exit

* test: bound chat failure observation and diagnostic work

* test: check ACP acquisition generation before capture

* test: bound native worker end history traversal

* test: type native exit fixture record lookup

* test: follow the journal context clear signature

* test: bound legacy display work for worker failure alerts

* test: type shared request failure rig mocks explicitly

* test: cover paired web chat error preference hiding
2026-10-23 22:05:46 -07:00
NeilandBingxin Xu cd2eeebe3e perf(ssh): opt-in proxy connection reuse for system SSH on Windows (#33030)
* perf(ssh): reuse one proxy connection for system SSH on Windows

Windows OpenSSH cannot multiplex (ControlMaster fails with "getsockname
failed: Not a socket"), so every system-SSH exec for a ProxyCommand or
ProxyJump target re-ran the proxy handshake. Hold one `ssh -N -L` through
the real proxy per session and dial later execs over 127.0.0.1 with
HostName/Port/ProxyCommand=none/ProxyJump=none/HostKeyAlias overrides.

The route starts after connect, is proven with a login before use, falls
back to per-command proxying on any failure, honors
systemSshConnectionReuse: false, and is torn down with its proxy child.

Rebased from #31731 (commits b1cdd46, 3835c2d, 6268f2f) onto current main,
including the pending-route cancellation fix from b301b6a.

Co-authored-by: Neil <neil@stably.ai>

* refactor(ssh): stop the loopback forward through process-host's tree kill

Reuse signalProcessTree instead of a hand-rolled taskkill-then-kill
fallback: it already joins an in-flight tree kill, refuses a reaped PID,
and falls back to killing the root, so the route keeps no parallel copy
of that logic.

* fix(ssh): harden the Windows loopback route against review findings

- Manual targets: take the forward's sshd port and the HostKeyAlias from
  the stored endpoint; `ssh -G` ran on the label and named another host.
- Hold the forward with ServerAlive probes so a silently dead proxy ends
  it and commands fall back instead of stalling on 127.0.0.1.
- Never route forwards: their argv must stay stable for orphan recovery,
  and the route's own forward must take the real proxy.
- Upload sessions read the route per file, so an exited route's port is
  not dialled after it closes.
- Skip the route for security-key identities, whose forward and probe
  logins would each ask for an unprompted touch.

* fix(ssh): keep the loopback route for manual targets no Host block claims

The unclaimed-alias endpoint rewrite only applies to config-backed targets,
so manual targets with a configHost no block names stay eligible.

* fix(ssh): keep the held loopback forward non-interactive

The forward is an optional background login; with BatchMode=no a password or
passphrase login raised an extra prompt right after connect, which the
non-interactive probe then discarded anyway.

* fix(ssh): admit the loopback route only when it changes nothing but the route

Overriding HostName to 127.0.0.1 changes which ssh_config blocks match, so a
`Match host <real host>` block could silently switch the remote User (or
identities, certificates, known-hosts files) for every routed command. Run
`ssh -G` with and without the route options and refuse the route on any
difference outside HostName/Port/HostKeyAlias/ProxyCommand/ProxyJump.

Also ask the agent with `ssh-add -L` and refuse the route when it may hold a
security key, so the background forward and probe never request a touch.
Both checks run through process-host and fail closed.

* feat(ssh): make Windows proxy connection reuse an opt-in per-target setting

The loopback route rewrites HostName, which ssh_config can key settings on,
so it no longer turns on by itself: a new optional systemSshProxyConnectionReuse
field (default off, Windows only) enables it from the SSH host's Advanced
settings. Old clients and servers ignore or omit the field.

Admission stays as the safety net and now:
- takes the forward's sshd port and HostKeyAlias from the direct `ssh -G`,
  so a manual host's own Host block cannot send the route to another sshd;
- refuses any non-routing value with a `%` token, which `ssh -G` can leave
  unexpanded while a routed login expands `%h` to 127.0.0.1;
- checks the security-key and agent identities of every ProxyJump hop;
- asserts the routed config really dials 127.0.0.1 with no proxy left.

* fix(ssh): offer proxy reuse only for hosts this desktop dials, refuse ${VAR} paths

- The switch now appears only for this desktop's own SSH hosts. A paired
  server's add-host dialog never shows or sends it, so an older server's
  strict schema is never handed the new field, and a client's platform no
  longer decides a server's setting.
- Admission also refuses `${VAR}` references, which `ssh -G` prints
  unexpanded while a login expands them.
- Advanced no longer auto-opens just for the opt-in.

---------

Co-authored-by: Bingxin Xu <xubingxin.xbx@gmail.com>
2026-10-23 21:22:08 -07:00
Neil 82534d0183 refactor: colocate remote runtime passthrough socket (#33009) 2026-10-23 19:46:31 -07:00
Kelvin Amoaba 33bd67e057 fix(notifications): tapping a phone push opens the agent that sent it (#31773) 2026-10-24 02:34:14 +00:00
NeilandCubatica c1d980c111 fix(runtime): retire predecessor exit evidence on replacement (#32341)
* fix(runtime): retire predecessor exit evidence on incarnation change

When a PTY id is re-registered with a different, known incarnation, the handle was already invalidated but lastExitCode/lastExitCause (pty and leaves) still carried the predecessor exit, so a live replacement reported the old stop. Clear them at that same proven-replacement point. Same or unknown incarnation keeps the evidence (fail closed).

* fix(runtime): retire exit evidence for replacement incarnations

Clear predecessor exit evidence on the PTY and its indexed leaves when
registration or provider spawn proves a different known incarnation owns it.
Cover both paths so an earlier spawn cannot hide the transition from later
registration. Keep same and uncertain incarnation evidence conservative.

Adapted from Cubatica's original fix and regression cases in #24513; extend
the coverage to spawn-first replacement on the current runtime modules.

Co-authored-by: Cubatica <4414418+Cubatica@users.noreply.github.com>

---------

Co-authored-by: Cubatica <4414418+Cubatica@users.noreply.github.com>
2026-10-23 19:21:40 -07:00
Neil b46be798ed fix(orchestration): refuse sends to a Run whose home is another computer (#33005)
* fix(orchestration): refuse send to a Run whose home is another computer

A remote Dispatch's Run id also names a mirror row on the worker's host. Once the
worker lost its link to the home, send --to run:<id> wrote into that mirror and the
message reached no one. Now the host refuses with run_home_unreachable, and a linked
worker that names its own Run home is delivered there.

Reported by @cookapps-jhlee8 in #32292.

* fix(orchestration): refuse only mirror Runs, and answer coordinator mail at its Run home

A loopback runtime's attachment names a real local Run, so the refusal now keys on the
Run row's remote home, not on any attachment. A reply to coordinator mail imported under
a remote Run relays to the home through the replying pane's Dispatch link, and is refused
with run_home_unreachable when that link is gone.

* fix(orchestration): a remote reply answers only mail sent to the replier's own Dispatch
2026-10-23 19:09:54 -07:00
Jinwoo Hong 15c3f0866d feat(connectors): cancel a conversation's queued work and stop its agent, behind experimentalConnectors (B4) (#32961)
* feat(connectors): cancel a conversation's queued work and stop its agent, behind experimentalConnectors (B4)

* test(connectors): drop a type assertion from the interrupt test

* fix(connectors): interrupt only a running main-agent turn, never background-only work

* chore(connectors): shorten the interrupt comment

* fix(connectors): fence cancel and prompt keys at transport handoff

* fix(connectors): clear the prompt Claude restores after our cancel, and end a turn our cancel rejected

* fix(connectors): require stronger evidence for cancel cleanup and settlement

* fix(connectors): cancel waits for a published agent's link, keeps cancel headroom in a full backlog, and pastes nothing after an unsettled clear

* fix(connectors): bound cancellation settlement and preserve unresolved lanes

* refactor(connectors): drop the leftover-prompt clear after cancel; keep the rejected-turn end
2026-10-23 21:34:48 -04:00
Neil d31deaa39f fix(terminal): verify an exact stop through an overlapping liveness inventory (#32948)
* fix(terminal): verify an exact stop through an overlapping liveness inventory

terminal.stopExact re-read the PTY inventory once after the stop. When a newer
inventory started meanwhile (the exit's own session republish triggers one),
the older read was voided as stale and the confirmed stop came back as
terminal_liveness_unavailable, so hibernating a remote pane on a busy host
rolled back. Retry the post-stop read within one list budget.

* test(terminal): keep the post-stop liveness failure fixture unavailable across retries
2026-10-23 17:42:59 -07:00
OrcaWinandOrcaWin 02d7214c1a refactor: remove four unreachable preload IPC bridges (#32950)
* refactor(repos): remove unreachable repos:getGitUsername IPC

* refactor(mobile): remove unreachable mobile:isWebSocketReady IPC

* refactor(skills): remove unreachable skills:deletePackageVersion IPC

* refactor(space): remove unreachable workspaceSpace:closeWindow IPC

* chore: prune repo-host-read baseline for removed git username handler

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 17:39:44 -07:00
OrcaWinandOrcaWin d1aef1eee9 refactor(worktrees): remove unreachable legacy worktree list IPC (#32942)
* refactor(worktrees): remove unreachable legacy worktree list IPC

The desktop and web preloads both always provide `worktrees.listDetected`,
so the renderer's fallback to `worktrees.list` never ran, and nothing calls
`worktrees.listAll`. Remove both IPC handlers, their preload/web wrappers,
the renderer fallback, and helpers only they used. Tests that observed
behaviour through the removed handlers now go through `worktrees:listDetected`.

* test(worktrees): drop removed list channels from SSH listing matrix

Address review: the canonical SSH listing test built channel names dynamically
and still invoked worktrees:list/listAll. Also fold the detected-result reader
into the shared worktree test fixtures, refresh stale comments, and assert the
backfill no-op test listed authoritatively.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 17:30:59 -07:00
Neil cd59239927 fix(workspaces): add main-owned project move pause barrier (#32940)
* fix(workspaces): add main-owned project move pause barrier

* test(workspaces): type project move pause reply fixtures
2026-10-23 17:25:22 -07:00
Neil 1cc2a2afd8 refactor(shared): group remote runtime shared-control lifecycle (#32977) 2026-10-23 17:04:58 -07:00
Brennan Benson fb1f4b3e6f Agents: store each agent's permission itself; terminals and new chats read it (#31315)
* feat(agents): store each agent's permission itself instead of typing flags into Arguments

Terminals and new chats read one stored choice per agent (Use the agent's own
settings, Ask for approval, Accept edits, Approve for me, Full access). Orca adds
the flag at launch; Arguments hold only the user's own text. A one-time move
lifts Orca's flags out of saved Arguments. Older clients still see and write
the flag as part of Arguments; the host translates.

* fix(agents): say the all-agents switch resets agents set separately; keep primitives unstyled

* test(agents): the one-time move returns every launch-profile field

* Fix permission carrier ownership and surface-specific defaults

* Preserve uncertain permission operands and name the option inventory

* Check env entries without indexing typed agent maps

* Combine Chat permission capability imports

* Use folder execution-host platform for permission environment

* Preserve launch map replacement and acknowledge permission saves

* Keep permission feedback fallbacks statically discoverable

* Provide preload platform to Agents settings render tests

* Preserve Gemini approval arguments under Full access

* fix(settings): retain complete environment edits through save outcomes

* fix(settings): narrow local failure scope and preserve host messages

* fix(settings): retire argument drafts after acknowledged saves

* fix(settings): keep Goose environment editable after reset

* fix(settings): show resolved arguments for legacy hosts

* Preserve pending environment resets and bound launch settings work

* Keep optional settings fallback in repository metadata reads

* Read repository visibility defaults through the raw settings fixtures

* Prepare whole Environment edits and retire unchanged drafts

* Keep Environment settings transitions in an acyclic module

* Update raw settings reader test fixtures

* Place test fixture safety notes on assertion starts

* test: pin permission settings work and recovery contracts

* test: type permission settings work fixtures

* test: cover migrated permission inputs and captured CLI grammar

* test: preserve typed permissions in WSL Claude restarts
2026-10-23 16:20:55 -07:00
Neil 9a5da64fdd refactor: group remote runtime requests and subscriptions (#32952) 2026-10-23 15:52:57 -07:00
Jinwoo Hong 2c53a550ff feat(connectors): pending prompts, answers and turn events behind experimentalConnectors (B3) (#32160) 2026-10-23 15:31:49 -07:00
OrcaWinandOrcaWin bd0261d147 test: verify terminal chunk yields without waiting for Enter delay (#29752)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 15:03:46 -07:00
OrcaWinandOrcaWin ef91e89faf test: reduce mobile relay integration startup imports (#29624)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 14:56:32 -07:00