* fix(dictation): give each cleanup its own cancel lane (STA-15095)
* test(dictation): one explicit case per overlapping-cleanup behavior
* test(dictation): follow main's text-generation/execution moves (STA-15095)
Main (#41838) moved the lanes, agent-launch, agent-environment and test
harness modules under text-generation/execution/ and renamed the
commit-message settings export to resolveCommitMessageSettings. Point the
new dictation cleanup test's imports and mocks at the moved modules.
* feat(dictation): optional AI cleanup of dictated text, with a correction word
Off by default. When on, a dictation's text goes through the user's agent
CLI (the AI text actions' runner, Haiku by default for Claude) before it
lands: the pill says "Cleaning up…" and the text is inserted once. Failure
or a 10 s timeout inserts it as heard with a warning; the pill's X skips
cleanup. A consent dialog asks before text first goes to an agent. The
correction word ("scratch that") survives filler removal for cleanup to act
on. The microphone test gets "Try cleanup", and phone dictation the computer
transcribes is cleaned on the host.
* fix(dictation): keep finals that arrive during cleanup and tighten cleanup edges
* refactor: separate execution route and child exit owners
* refactor: move browser data into defining feature owners
* refactor: give runtime RPC entry its shutdown implementation
* refactor: use defining process types and colocate host telemetry test
* docs(process-host): point piped child contract to Node owner
* docs(process-host): clarify both piped contract owners
* Start OpenCode 2 with a picked model through its launch config
OpenCode 2's root command has no --model, so launches that carried one
exited with 'Unrecognized flag'. On supported 2.x releases the PTY launch
hook now moves a literal root --model into OPENCODE_CONFIG_CONTENT and
starts a private server so the TUI reads it. Anything it can't apply is
left as typed.
* Point the sync-fs ratchet baseline at the moved agent-hooks files
#39969 moved the agent-hooks installer files after #39930 wrote the
baseline, so the ratchet saw every moved file as new and failed lint.
Same counts, new paths; installer-utils' 20 split into its two new owners.
* Rewrite only one plain OpenCode command with a real --standalone
A --prompt or --session value spelled --standalone no longer counts as
the option, the cut starts at the previous token's end so its escaped
trailing characters stay, and a newline or shell operator leaves the
command as typed.
* Leave a command with an operator outside quotes as typed
A token that only starts with a quote, such as '--auto'&&echo, could hide
a shell operator; scan each token outside its quotes instead.
* Leave comments and explicit servers as typed
A word starting with # comments out the rest of the line, and --server
means another server that never reads this launch's config.
* Scan for shell operators with each shell's own quoting
cmd quotes only with double quotes and PowerShell escapes with a backtick,
so a posix-only scan could miss an operator they would split on.
* Rewrite only the command shape Orca itself emits
Replace the shell scanners with one check: every argument after the
executable must be exactly quoteStartupArg's output or a bare flag the
builders append, and the prompt must match the body Orca put in the env.
cmd also rejects any divergent token or raw newline. Anything else stays
as typed.
* Test that the launch hook hands the prompt body to the model rewrite
* refactor(main): organize SSH transport and managed server owners
* Remove orphan deploy readiness fixture and shrink test baselines
* refactor(wsl): import lock records from their defining owner
* refactor(wsl): use the canonical deferred update contract
* chore(lint): bump oxlint to 1.91, add perf lint rules and perf audits
* fix(lint): await promises returned inside try/catch/finally for return-await
Prepare typescript/return-await with error-handling-correctness-only: await
returns whose catch or finally was meant to cover them, and suppress the eight
sites where the catch must not re-handle the returned promise.
* chore(lint): enable return-await (error-handling-correctness-only); exempt secondary-scan directives in the changed gate
* refactor(mobile): move file preview discard modal into its own module
Keeps MobileFilePreviewScreen under the 400-line limit after merging main.
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* refactor(main): organize runtime implementation owners
* test: retire wall-clock CLI title recovery case
* refactor: route new folder mutation to runtime store owner
* test: import folder mutation store contract from its owner
* Preserve native model choice when restoring ACP chats
* Forward startup observations through the OpenCode connection
* Use fixed time in the connection regression fixture
* Complete the model-command fixture session identity
* Stop retrying final native agent startup refusals
* Retire startup opens after exit and keep final answers across exit orders
- A start flight cancels its outstanding provider open on every ended path; a direct attach keeps the child-bound abort.
- The post-exit answer join is bounded by the remaining startup ceiling.
- An elapsed initialize deadline keeps its words when the exit wins the race.
- Codex cloud config temporary declarations retry on thread/resume as on thread/start.
- A Claude exit before acquisition publishes waits briefly for initialize's answer, so a rejected initialize stays final.
- The Claude startup stdout observer parses only failed root results, within the shared line budget, and detaches once it has one.
* Keep saving the last turn when a Claude process exits after startup
The exit path skipped saving the resume cursor whenever Orca had not
dispatched a message, but turns can settle without an Orca dispatch and an
already-started idle session still owns its exit save. Only a start that
died before initialize answered and before any turn now skips the save and
gets the short cleanup bound. A failed result captured before the first send
counts as a startup failure only when no turn ran in that process.
* Merge duplicate test imports
* Bound resumed Claude starts that exit during catch-up and keep untyped refusals final
Exit-time cursor writes and the short close bound now key on the host-awaited
open actually returning, so a resume that exits while transcript catch-up is
pending publishes ended at once instead of waiting on a cursor write. The
startup observer no longer reads an answer from an oversized line's prefix.
A typed acquisition refusal without a dedicated failure kind keeps its
refusal marker, so an exit-only start stays final.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: pin the fake clock in the resumed-exit start test
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: bound startup answer parsing by candidates at N and 10N records
Drive the startup observer through the shared framer at 100 and 1000
irrelevant hook and assistant records, and assert that only the failed
root result is parsed, one capture happens, the data listener retires,
and no parse follows capture or finish, with identical counts at both
populations.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: shape startup fixtures like the real providers
Held opens in the launch test now reject when their child exits, as a
real connection does, and the temporary open failures use the provider's
actual overload envelope. Also fixes the test-only type errors CI found.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: use the real Codex canonical-filename refusal wire capture
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(claude): align merged startup tests with answer-wins and final generic results
An exit with no stderr leaves the start to Claude's buffered result, so that result is the start's one failure. A generic startup failure result is final after one start; only an unanswered exit retries, and the recovery case now recovers from such an exit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(claude): let a final initialize answer outrank a physical exit reported fault-first
The connection reports one physical root exit through onFault and then
onExit. It now marks that error as an observed exit before either
callback, and acquisition's onFault leaves a proven exit to onExit's
bounded initialize join, so a final initialize rejection that arrives
just after the exit owns the start instead of a retryable bare exit.
Typed refusals, live-root faults and processless spawn faults still end
the start at once.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(claude): await a macrotask instead of real time in the exit-answer start test
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(claude): replace casts in the exit-answer start test with an annotation and guard
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(agent-session): let each adapter decide a failed start, and give the start one owner
A start that the agent refused could still be replayed: the host joined a
child's exit with its open's error and rebuilt the meaning from provider error
classes, and an answer written just before exit could lose to the exit itself.
Each adapter now answers its own acquire, open and required startup reads with
one typed result (final, or safe to run once more), keeping a final answer
beneath the exit proofs cleanup adds. Transports keep reading answers already
written after the agent stops reading or exits, within the existing one-second
drain, and only a drained exit of the agent's own process counts as unanswered.
The child's open is the single owner of the start's outcome: it awaits the
adapter's result and required settlement, records the verdict once, writes a
direct attach's failure row, and stops the child. The start flight reads that
verdict instead of joining exit and open. A start whose adapter gives no outcome
within five seconds of its own process exit fails through a timer that no
bookkeeping can hold, and the original start deadline stays in force until the
start is decided.
Tracked in STA-12982.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(agent-session): bound a failed start's lifetime and keep Orca's own refusals final
- A start's ending is kept only while its owner can read it: admitted when the
acquisition starts, retired when the owner finishes, the acquisition fails, or
the session is released or closed. An exit after ready records nothing.
- Codex takes its own exit as the start's wake from the moment the process is
seen, before spawn bookkeeping, and a failed acquire keeps that exit as cause.
- Once the handle or rewind write began, an unanswered death no longer decides
the start: the write's refusal stays final, and the exit grace bounds the wait.
- Stop, the deadline and the exit grace release the start's callers at once; the
failure row is queued first but never awaited by them.
- An ACP connection Orca closed before the exit keeps that cause, so cleanup's
exit is not read as the agent ending unanswered.
- A request deadline keeps its words when an exit is seen around it.
- A direct attach whose exit settled first keeps the exit's words.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(agent-session): let a start's owner, not its live route, keep and retire its ending
Exit acknowledgment drops a session's route while the start's owner may
still be reading how its acquisition ended, so the router now reaches the
adapters by exact generation for both the read and the retirement. Claude
admits its acquisition before publication can observe an exit.
An ACP start Orca refused before the agent exited now fails with that
original refusal as the adapter's final verdict, through every cleanup
wrapper, instead of a generic closed-while-starting error.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(agent-session): give the worker retry fake the Codex adapter's typed start verdict
The host now reads only the adapter's verdict on a failed start, so the
fake open rejects with the verdict the Codex adapter returns for its
declared overload instead of the raw request error.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(agent-session): let Claude's own exit record decide a start the SDK reports as exited
A Claude CLI missing from the launch PATH makes the supervisor exit 127 with the spawn error on
stderr. The SDK rejects initialize with only "process exited with code 127", and the start took
that report as its cause, so the missing CLI was lost: the chat said Claude stopped before it
finished starting and the start was retried as an unanswered exit. The SDK's exit report is no
answer; the start now reads Orca's own record of that exit, which carries the missing CLI.
Also brings two ACP tests in line with the typed start verdict (a signed-out open rejects with
the adapter's notSignedIn verdict) and the bounded drain after the agent stops reading.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf(agent-session): stop parsing Claude's startup output once its failure is captured, and pin start and drain work at N and 10N
A failed root result captured mid-chunk still let the rest of that chunk be framed and parsed;
nothing reads those records once the start has its answer. The observer now accepts no further
line once it has captured the failure, and drops what that chunk left in its framing state.
Two contract tests drive the production start owner and transports at N and 10N:
- starts through the host, router and Codex adapter (ready, final refusal, safe replay, deadline
expiry): each start's owner, abort handles, tracked attempt, flight, buffered exits, adapter
receipts, root-exit subscription and deadline timer return to baseline, a waiting start holds
a fixed set, and spawns, opens, router reads and retirements, timers and failure rows grow
exactly linearly;
- ACP, JSON-lines and Codex connections with a pending call, answered at exit or abandoned after
the agent stops reading (with duplicate terminal signals): each call settles once, each output
drain finishes once and leaves no listener or timer;
- an ACP start refused at sign-in keeps its own first cause and leaves no receipt or child.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(agent-session): decide a failed start once from everything its adapter saw
A failed start was decided from whichever error reached the decision first, so a later or
poorer report could replace the one that explained it: the SDK's exit report hid the missing
CLI Orca's own exit record carried, and an output drain let a load deadline give way to an
unanswered exit. One shared rule now weighs every observation an adapter kept. The start may
run again only if every observation allows it, and the most specific one explains it: what the
user can act on, then a request deadline, then the agent's answer, then Orca's record of its
exit, then an SDK's report of that exit. Claude, Codex and Pi pass their exit records alongside
the operation's error instead of choosing between them.
An ACP agent that exited while its output drained no longer gets a replacement session after
its load fails. JSON-lines output that reports both its end and its close now starts one wait,
so the agent's exit can always cancel it.
Contracts at N and 10N: JSON-lines output ending while its root lives, real Claude and Pi
adapters retiring every start's receipt, and a user Stop whose cleanup is held then released.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(acp): keep a missed saved-session load deadline instead of opening a replacement
A live agent that let session/load run past its deadline fell through to the
takeover path, and the replacement session/new answer then replaced the
deadline the user had to see. The takeover predicate now propagates any
provider request deadline unchanged, read through the existing typed timeout.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Show the native default effort for OpenCode models
* Show the effort OpenCode 1.x actually runs when none is picked
OpenCode 1.x ACP runs a listed default variant, else the first listed
one, so declare that same variant instead of a no-override Default. The
draft and launch default stay as on main; the resting fallback now shows
it too. Shared declared-effort consumers expect no effort for a readback
without that model's menu.
* Name the recorded OpenCode listing test for what it checks
* test: remove 462 low-value test declarations
* Shrink flaky test pattern baseline after audit896 test removal
* Remove two empty hook-only suites after audit896 test removal
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>