* 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>
* fix(native-chat): an open chat moves to the account you switch to before its next message
A running chat's agent read its account only when it started, and nothing compared it with the
account a start would use now, so open chats kept answering on the old account after a switch.
The agent a chat runs now records the account its launch used, and the host's replace-before-the-
next-send check replaces an idle one whose account is no longer what a start would use. A chat
mid-reply finishes on the old account first. Claude chats follow; agents whose chats keep the
account they were created on are never compared.
A chat's model-list reads and saves now use the account it runs on, so a moved chat no longer
writes its new account's list under its first account (STA-11281).
Tracked in STA-11211.
* fix(native-chat): declare the launch-account read for the runtime layer that wires the host
* fix(claude): find custom chat history before managed-account resume
* test(claude): assign the complete custom account home
* feat(native-chat): offer account switching and hand-off at usage limits
* fix: track the provider in account-list reload effects
* fix(native-chat): target recovery actions at the chat host and transcript
* fix(native-chat): verify account recovery for each chat on its host
* refactor(native-chat): retain the existing image-support result
* fix(native-chat): withdraw account recovery after failed eligibility reads
* fix(native-chat): leave unavailable recovery facts absent
* Align Japanese usage-limit recovery copy with catalog convention
* Preserve complete chat context through usage-limit recovery
* Keep Continue callback focus optional
* Keep recovery fixtures and account effects type safe
* Keep chat recovery on native sources and native accounts
* Validate native recovery files after path admission
* Use the optional cursor shape in recovery fixture
* fix(native-chat): exclude source CLI family from limit handoff
* Resolve recovery context in the owning workspace directory
* Preserve unknown account model facts and legacy option replay
* Clarify terminal account-switch feedback in translated locales
* Offer usage-limit Continue Now only once the limit resets
* Keep usage-limit reset timers current and bounded
* Keep option mutation plans checked against their contract
* Cover unknown model choices across chat consumers
* Keep consumer fixtures aligned with canonical types
* Match the mobile picker label in its consumer test
* fix: clarify terminal scope in translated Claude account settings
* Update integration tests for current session and remote client contracts
* Keep native worker replies on their receiving account
* Preserve receiving accounts across cold starts and nested reports
* Preserve the delivery receiver when reading chat commands
* Clarify native reply matching and consolidate imports
* Resolve catalog scope without re-entering conversation queues
Give owned option mutations and delivery settlement a resolved catalog input while external reads acquire scope once. Preserve account routing, catalog lifetime and current response obligations.
* Align catalog and mobile test fixtures with current contracts
Explain the existing timeout budgets and provide the idle turn identity and truthful model-only picker label in the mobile fixture.
* Align receiving-account readers and fixtures with typed contracts
* Repair account-switch fixtures and measure receiving work
* Use the checked account-home path in catalog tests
* Restore executed-option recorder context
* Control receiving catalog deadline test clock
* Align unknown-model consumer fixtures with send capture
* Consolidate session host type imports
* Align cross-version catalog fixtures with host read contracts
* test(native-chat): bind Runs through the current start authority in receiving fixtures
Main now resolves a worker start lazily through bindRun with objective and task references, and creating a Run no longer unbinds a coordinator's earlier Runs; run-use does. The receiving fixtures follow the same production owners so every original scenario, input and bound is exercised unchanged. The receiving runtime subclass moves verbatim into its own support module.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(native-chat): follow the selected account before its folder exists
A live Claude chat compared its account against the selected one only after that account's folder passed an availability check, so an account still being set up (or whose setup failed) left the chat silently answering on the previous account, and a resting chat refused its start before preparation could run. The retirement comparison and the start pre-read now ask the router for the selected route as identity, through the existing resolver purposes; catalog, create and probe reads keep checking availability. Preparation, typed launch failure and parking are unchanged.
Session History details show the account a chat was last launched under when the host recorded one, else its founding home.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(ai-vault): build the stale acquisition receipt without a type assertion
The History account case now reads the fixture receipt through the typed acquisition reader instead of spreading the record field through a cast; the three assertions and their inputs are unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): narrow the rig acquisition link before relinking it
The account-setup cases spread an optional link, which typed its id as possibly undefined; the rig always links a handle, so a guard that cannot fire narrows it. No assertion or input changes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): follow main's chat admission in fixtures and bound the History account projection
Chat admission no longer reads an experimental host setting, so the continuation-context test drops the spy main dropped in its own fixtures. The two History consumers that install a minimal chat host give their fake store the record accessor and host id the real store has, since the projection now reads the acquisition receipt. An operation-count contract holds the production History projection to one roster read and one record lookup per owned row at 10 and 100 rows.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): give the declared-effort catalog fake its capture port
The declared-effort fixture builds a catalog service without the capture member the options reader asks every catalog for, which failed its desktop, catalog and phone consumers; it now supplies the inert test operation like the other fakes. The History projection contract also drops an unused map parameter.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Consume main's outcome change feed in the receiving-account index
Main's keyed journal records deliver each change after an epoch's frozen prefix as an
outcome sequence naming touched keys instead of typed rows. The receiving-account index
now re-reads those keys at their current values: queue cards by id, a deleted submission
retires its exact notice, and a deleted item drives the existing recovery rebuild, with
Stop marks still excluded. The in-process read row exposes the already-persisted
queue.cards keys and per-key deletion facts; itemIds and submissionIds keep listing every
touched key. The mobile unknown-consumer test mocks MobileSwitch as main's picker test does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test: validate historical usage-limit fixture before rendering
* fix: scope handoff choices and share account observation
* fix: align account menu lifetime and local projections
* test: preserve account observation contracts in quality checks
* fix: preserve typed account observation project coverage
* fix: restore handoff feedback for empty host inventory
* test: name exported handoff mocks with public types
* test: follow settings navigation consumption in feature tip adapter
* Keep usage-limit recovery actions beside their lead-in
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* test: remove low-value suites and cases
* test: shrink flaky-pattern counts after case removals
* test: remove empty suite and stale release workflow fixture
* fix(native-chat): a kill that reached the agent ends it, so the next message goes through
After Stop, tab close, idle rest or workspace sleep, Orca force-kills the
agent. It used to accept the old agent as gone only when it saw the exit
report within about a second; otherwise every later message re-ran the kill
and was refused with "Couldn't stop {agent} from before." A kill delivered to
the agent itself now ends it and releases the chat with kill-delivered
evidence; the refusal remains only when the kill could not reach the agent.
When writing the chat's journal fails and the following kill can't be
confirmed, recovery now records the close with its fault instead of silently
returning, so the turn no longer stays Working and later sends retry the stop.
Tracked in STA-12354 and STA-12355.
* Preserve provider kill evidence across cleanup retries
* Exercise ACP setup gate with real managed teardown
* fix: preserve Claude writer termination evidence through settlement
* test: consolidate termination evidence contracts
* fix: restore Pi connection exit listener contract
* test: load actual ACP teardown before timed cases
* fix: correct lifecycle contract typing and test boundaries
* test: make Claude piped fixture mock types portable
* test: expect supervised OpenCode probe teardown
* test: align remaining supervised teardown expectations
* fix: align Stop feedback with terminal writer evidence
* test: remove redundant mocked Codex continuation case
* fix: keep adapter declaration and Stop fixture checks portable
* test: keep Stop fixture clock unchanged
* test: read canonical provider handle when reopening Stop journal
* fix(opencode): run managed OpenCode 2 accounts on a private server
OpenCode 2's background service binds one fixed port per machine, so a
second managed account's service could never start. Launch managed
accounts with --standalone, which picks its own port.
Fixes#33780
* chore(opencode): tighten standalone rewrite comments
* fix(opencode): use a private OpenCode 2 server only when another data dir holds the service port
Users with one managed account keep the shared background service. Orca
adds --standalone only when a live upstream registration
($XDG_STATE_HOME/opencode/service.json) belongs to a different data dir,
plus a short in-process claim for launches that start together.
* fix(opencode): judge resumed accounts by their applied data dir and claim the port only for real launches
Daemon spawns consume the resume request before the private-server check runs, so
derive the launch's own data dir from the applied account env when present. Only
take the shared-port claim when the --standalone rewrite can apply.
* Unify terminal starts under one host operation
* Pin winning geometry across a late terminal join
* Use terminal creation options type in launch race test
* Check closed panes after acquiring the terminal spawn lease
* Preserve terminal start resource and join boundaries
* Keep terminal attach regression within project boundaries
* Capture terminal spawn authority and preserve image coverage
* Use checked fixtures for terminal image replay regression
* test: expose persisted pane to main layout in attach race
* test: contract terminal spawn scaling and retention
* test: correct spawn scaling fixture types and CI selection
* test(terminal): cover exited prompt claims and adopted team cleanup
* test(pty): consolidate mock registry imports
* fix(pty): retain untyped IPC resume inputs in common contract
* test: remove low-value inventories, passthroughs and redundant cases
* style: format test suites after pruning redundant cases
* test: remove obsolete workflow and flaky-test references
* test: prune deleted builder test from Windows shim allowlist
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Managed OpenCode accounts launch with XDG_DATA_HOME=<account>/data, but
history discovery only read the app process's XDG data dir, so sessions
created after selecting an account never reached History or the vault index.
OpenCode 2.x model-selection launches require 2.0.19+ (upstream #51938 waits for agent/model before auto-submitting --prompt) or a native-prompt version (2.0.12, 2.0.16), fixing a regression from #33307 where 2.0.14/15/17/18 accepted a picked model but silently dropped the first prompt.
* chore(lint): remove disable directives that suppress nothing
Each was reported unused by the oxlint config that owns its rule (root,
casting, react-doctor, anti-slop, native-plugins), including 17 file-level
react-doctor blanket disables whose rule no longer fires. An unused
react-hooks disable was masking a react/purity finding in
useAgentBucketCounts (compiler rules skip functions with hook
suppressions); suppress that one explicitly where the code explains it.
* test(tab-bar): use typed tab/file fixtures instead of @ts-expect-error partials
* chore(lint): fix misc suppressed lint violations instead of disabling
- consistent-type-imports: the "vi.importActual requires inline import()"
rationale was wrong; use typed vi.mock(import(...), importOriginal) or a
type-only namespace import.
- no-control-regex: source-control commit/push failure summaries, shell PATH
hydration, and ipynb ANSI parsing reuse the shared ansi-escape-sequences
stripper and control-character pattern instead of private copies.
- no-explicit-any / no-useless-spread / no-this-alias: typed `this` in
bridge test mocks, drop blanket file-level disables, drop a this-alias.
* refactor(main): replace avoidable type assertions with checked narrowing
17 production cast suppressions in main/wsl-guest removed: untrusted IPC
and RPC payloads (Jira issue update, WSL guest plugin install, handshake
type) now narrow through isRecord/type guards and build typed results;
redundant casts dropped; hand-rolled object checks and errno casts use the
existing isRecord / hasErrorCode helpers.
* refactor(renderer,mobile): replace avoidable type assertions with checked types
13 production cast suppressions removed: RPC failures read through the
`ok` discriminant instead of casting to RpcFailure; react-native-web host
refs narrowed with instanceof HTMLElement; redundant reply/diff casts
become checked annotations; the TUI agent param uses z.custom(isTuiAgent)
like its sibling schemas; the agent-status slice cast that hid missing
actions is gone so the creator's return type checks the whole slice.
* chore(lint): drop dead tiptap wrapper; scope anti-slop mocking rule to test support
- safe-react-node-view-renderer patched handleSelectionUpdate, which
@tiptap/react 3.31.3 no longer has, so it always returned early. Use the
stock ReactNodeViewRenderer.
- The anti-slop test override only matched *.test.{ts,tsx}, forcing
file-level no-module-mocking disables in test harnesses and fixtures.
Match *-test-harness, *-test-fixture(s), .test-fixture and *.test.mjs,
then drop those disables and the now-redundant per-file exemptions.
* test: replace repeated cast suppressions with typed fixtures (main/shared/e2e)
Net -119 cast directives. Typed fixtures replace recurring casts:
unusedSshConnection/unusedOrcadHostTransport placeholders, a typed
readPersistedState harness read (exposed a test treating optional
httpProxyUrl as always set), real Response objects in fetch spies, an
updater main-window fixture, a CDP webContents adapter, one shared
releaseModuleExport for cross-version tests, and requireOverlayDir in
place of `as string` on possibly-undefined overlay paths.
* fix(runtime): validate loaded device rows and host scope instead of casting
Two cast suppressions claimed checks that did not happen:
- device-registry-file-read: "normalizeLoadedDeviceRow re-validates every
field" was false; deviceId, token, name and timestamps were never
checked. Rows without an identity are now dropped and the file is set
aside through the existing corrupt-file path (registry reads as
doubtful, so no revocation verdicts); a non-string token is stripped
so the row stays listed but cannot authenticate.
- client-ui: "only optionality differs" was false; any string reached
persisted workspaceHostScope. Narrow it with isWorkspaceHostScope and
ignore unknown scopes (wire schema unchanged for mixed versions). The
remaining cast now states what it actually covers.
* test(runtime,ipc): drop redundant cast suppressions; typed runtime store fixture
-98 cast directives across runtime/ipc tests: 61 casts that typecheck
without the assertion; a typed vi.fn for scoped invalidation; a
partialRuntimeStore fixture in place of `store as never` for
OrcaRuntimeService; typed worker terminal/authority builders in the
incarnation-fallback suite (the old stub lacked runtimeId, ptyId,
worktreeId and launchTokenHash) plus removal of stacked duplicate
directives there.
* chore(lint): typed vi.mock imports and explicit listener snapshot in shared-bucket tests
Remaining files from the misc-rule pass that overlapped other buckets:
codex-accounts mocks use the typed vi.mock(import(...)) form instead of
a consistent-type-imports disable; the daemon late-exit fixture copies
its listener set with Array.from instead of a no-useless-spread disable.
* refactor(github,mobile): narrow routing settings type; drop remaining avoidable casts
- work-item-routing cast a one-field object to AppState['settings'] under
a claim that readers only consume activeRuntimeEnvironmentId. Return the
existing RuntimeFocusSettings pick instead and narrow the cache-key
chain's parameters, so the compiler enforces that claim.
- mobile file preview reads refusals through the RpcResponse `ok`
discriminant rather than a cast.
- terminal document host-notify accepts `unknown` errors, removing the
message-bridge cast.
- native chat readability drops a redundant repo-list cast.
* test(renderer): replace repeated cast suppressions with typed fixtures
-140 cast directives (and ~150 unsuppressed casts of the same shapes):
- mockPanePtyArgs owns the connectPanePty fixture casts, so a wrong
argument order or fixture is now a type error; drop binding casts for
members PanePtyBinding already declares.
- Seed AppState tab rows with the typed makeTerminalTab; this exposed a
split direction 'row' that is not a TerminalPaneSplitDirection.
- Partial GlobalSettings use getDefaultSettings overrides; Jira-only
stores use createTestStore; runtime environment rows/statuses come from
a typed paired-runtime-environment fixture.
* chore(lint): drop no-module-mocking disable now covered by the test-fixture glob
* test(e2e): fix all 221 grandfathered e2e type errors; baseline to zero
The e2e typecheck baseline grandfathered 221 errors in 119 files. About a
third were tests drifted from the current API, several of which would
fail or mislead at runtime:
- terminal-shortcuts called a nonexistent xterm buffer method;
- the daemon-lifecycle fake lacked terminateOwnedTree, hidden by importing
its type from a module that does not export it;
- arguments in the wrong position (listProcesses(hostId, opts), a dropped
pty.write inputKind, an extra toWebTerminalSurfaceTabId argument);
- removed enum values ('drawer', 'idle', 'editor',
'session-insecure-origin') and a launch_source the telemetry schema
rejects;
- missing required fields (turnScope, childWorkOpen, providerIdentity,
reviewLookupOutcome, runtimeServiceAccount behind an `as RelayConfig`);
- dead reads (tab.customTitle, bucket.targets, changedPixels).
Also lifts variables that narrowed to `never` after poll-callback
assignment, and adds a typed xterm render-dimensions accessor. Includes
the cross-version suites' shared releaseModuleExport adoption.
* chore(typecheck): gate the now-clean e2e typecheck in local lint
The e2e baseline is empty, so the ratchet now rejects any tests/ type
error. CI already runs it via run-ratchets; add it to pnpm lint's
explicit ratchet list and replace the stale 'not yet clean' note.
* test(mobile): typecheck every mobile test; baseline 105 files -> 0
mobile/tests-typecheck-baseline.txt grandfathered 105 test files (531
errors) whose types CI never checked. Contract changes had landed without
these tests following, and type pins in them silently checked nothing.
- Root cause: salvagedOptional (src/shared/zod-salvage.ts) cast away
zod's optional marker, so every reply type built on it claimed
salvaged fields were always present. Return a real ZodOptional;
runtime is unchanged (the outer optional short-circuits undefined).
- Production now takes the port it uses: RpcOperationSender instead of
RpcClient for send-only callers, a narrow fetch signature, and only the
request fields push-receive reads.
- findAllByHostType/findByHostType replace `'Text' as never` lookups;
stubRpcClient gives tests a typed RpcClient.
- Real test bugs: settings-host-client-lifecycle "relay" hosts were
direct hosts (relayHostId no longer exists); two toEqualTypeOf pins
could never hold; a recovery hook ran with an undefined scope key;
a create-PR test cast to a never-imported type; "no URL" assertions
passed on null; plus stale required props, args and enum values.
* test(e2e): parse workerStart result instead of casting it
* test(runtime): typed partial runtime stores with production-shaped fixture data
Route every partial store passed to OrcaRuntimeService through
partialRuntimeStore with typed row builders, removing 60 cast directives
that hid fixture data production never produces:
- paired-client-navigation-test-harness keyed setWorktreeMeta writes by
`[object Object]` (it took an id where the store takes a target), so
reads never saw them;
- invalid worktree lineage origin/capture and incomplete workspace lineage;
- getWorkspaceSession returning null where the store returns the default
session; getGitHubCache returning undefined;
- orcad migration states missing migrationId/manifest/receipt, with a
toEqual pinned to that incomplete shape;
- partial repos, metadata, folders, project groups and notification rows.
* refactor(renderer): resolve suppressed React hook lint instead of disabling it
109 of 187 react-hooks/react-doctor suppressions fixed:
- ~95 exhaustive-deps disables only hid stable refs and setters passed
through controller objects; list them (identity never changes, so
nothing re-runs), plus a few real stale closures (tabId/worktreeId).
- Effects keyed on one value that read the latest props now use
useEffectEvent (WebConnect, skill freshness, Bitbucket dialog seeding,
Landing preflight, automation catalog, structured-session recovery,
file explorer).
- The conditional layout-owner hook is selected once at module load from
the build constant instead of a rules-of-hooks disable that also hid
the whole controller from compiler and react-doctor rules.
- A hook disable was masking a react/purity Date.now() in the palette's
recent-tabs memo; sample it when the snapshot is captured.
- Three identical debounced-search effects share
useTaskSearchDebouncedApply; layout snapshot merge helpers move out to
stay under max-lines.
Remaining suppressions are mount-once resources, invalidation epochs,
deliberately keyed fields, and test harnesses that mock the dispatcher.
* chore(ratchet): exempt test harnesses and fixtures from the focus-read ratchet
The ratchet skipped *.test-fixture.ts but scanned *-test-harness,
*-test-fixture(s) and *.test-support files. Those seed settings with
getDefaultSettings (which the ratchet discovers as a focus reader) and
never route host work, so 17 baseline entries were false positives, and
replacing a test's `settings: {...} as never` with typed defaults tripped
it. Match the repo's test-support names and prune (which also drops one
stale entry), and rename the frame-ordering harness to *-test-harness like
its siblings. A blanket "-harness" match would also exempt
e2e-terminal-pty-harness, which production imports, so the names stay
explicit.
* test: merge duplicate shared/constants imports
* revert(file-explorer): restore original tree load effects pending SSH e2e investigation
* test(runtime): drop RuntimeStore cast now that the fixture is typed
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
OpenCode model selection accepts per-major floors (1.x >= 1.18.30, 2.x >= 2.0.14; prereleases and 3.x refused) instead of exact version allowlists, so current releases launch with a picked model again. Fixes#26611. Supersedes #26683, #31985.
Co-authored-by: ggbdpq <67929842+ggbdpq@users.noreply.github.com>
* 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
* fix(provider-process): kill a provider group its SIGKILLed supervisor leaves behind
* fix(provider-process): re-read a provider start in its read second; poll instead of sleeping in tests
* fix(provider-process): keep the report channel through the Codex session adapter; re-prove same-second starts
* fix(provider-process): never kill a leaderless provider group; list guard files in the CLI project
* fix(native-chat): identify the owner when resume create is refused
* test(native-chat): consolidate transcript refusal cases
* test(native-chat): keep ownership coverage at the launch boundary
* test(native-chat): update adoption fixture for early ownership admission
* test(native-chat): align resume admission assertions with host ownership
* test(native-chat): keep RPC coverage focused on ownership proof
* test(native-chat): assert blank request presence before optional source
* fix(native-chat): admit the actual CLI source during chat creation
* fix(native-chat): satisfy source admission lint checks
* test(native-chat): provide the waiting hook prompt
* test(native-chat): isolate source admission fixtures from host homes
* test(native-chat): canonicalize adoption fixture directories
* test(native-chat): preserve early ownership admission in source stack
* fix(native-chat): preserve resume refusal behavior and honest transcript feedback
* fix(native-chat): retain import read evidence in typed result
* fix(native-chat): preserve admitted source and prepared execution home
* fix(chat): retain source evidence for dispatched adoption
* fix(chat): keep source capture within module limits
* test(chat): allow host evidence in create routing
* fix(native-chat): admit an unsaved transcript under a linked account folder
A missing transcript stayed unresolved while its account root resolved through
symlinks, so containment failed and a not-yet-saved conversation showed a load
error instead of the unsaved notice.
* refactor(native-chat): move the newest-item import window into its own module
Keeps journal-legacy-import.ts under the line limit after the source
identity observer landed on top of the streaming import.
* fix(native-chat): restore the refusal error import lost in the main merge
* fix(native-chat): admit Pi transcripts against the root Session History walks
A PI_CODING_AGENT_DIR that already ends in sessions, or any custom folder,
made admission expect <dir>/sessions while the lookup searched <dir>, so a
found conversation was refused.
* refactor(native-chat): admit and look up CLI transcripts in one store per account
Each agent's import described where an account keeps its conversations twice,
once for the path check and once for the lookup, and the two could disagree
(the linked-folder and Pi custom-root refusals). The import now names one
conversation store per account; the create path resolves it once and both the
hook-path check and the fallback lookup judge that same store. OpenCode's
store is its exact database.
* ci: remove Windows build and persistence test overhead
* ci: collect unit phase diagnostics during PR checks
* test(ci): seed hook reuse fixture from the canonical prebuild
The hook reuse regression first built into a missing private --output, so it
always reached Cargo (74s warm on Windows). Build through the default output
instead, which reuses a warm canonical prebuild, then copy its executable and
fingerprint into the private fixture root.
* fix(lint): clear inherited max-lines and broad object tab parameter after main merge
Combine the duplicate shared/execution-host imports in runtime-host-services
(301 -> 300 lines) and type the overlap test's tab mapper with the
RuntimeMobileSessionClientTab owner type instead of object.
* fix(ci): align inherited runtime and workflow contracts
* test: establish changed runtime probe signatures deterministically
* test: await asynchronous daemon profile cleanup
* test: shut down owned daemons before deleting profiles
* test: clean up oracle daemons during final app close
* test: recognize verified Linux zombie exit during daemon teardown
* fix: preserve folder SSH ownership when project groups conflict
* fix(orcad): seal integration credentials on the headless server instead of storing them in plaintext
The Node SecretStore always reported encryption unavailable, so every
credential writer on orcad fell back to plaintext. orcad now seals with
AES-256-GCM using a host-owned master key kept in the OS keyring (macOS
Keychain, Linux Secret Service) when reachable at first start, else in a
0600 profile file bound to the machine ID. A key that cannot load fails
closed instead of downgrading. Existing plaintext credentials are sealed
on startup without loss.
* docs: describe how the headless server seals credentials and its limits
* fix(orcad): unique credential staging files, no keyring items for temp profiles, isolate startup test
* fix(orcad): keep the credential key with the credentials, one record per machine
* fix(orcad): never re-key over an unreadable record, serialize first-start key creation, refuse an unrestricted key file
* fix(orcad): verify a key record's Windows ACL and wait out a concurrent first start's keyring write
* fix(orcad): keep a keyring claim and fail closed when its store result is uncertain
* fix(credentials): restrict the staged file's Windows ACL before it replaces a credential
* fix(orcad): a temporary profile never creates a key in, or re-seals, the real credential home
* ci: baseline the two single-file credential ACL restrictions
* fix(credentials): stage Windows credentials born restricted and verify the key record asynchronously; document rollback
* test(bitbucket): inject the write failure into the Windows staging writer too
* fix(orcad): orca serve owns its Claude and Codex accounts again (#28962)
orcad never wired account services, so every accounts.* RPC failed once
orca serve moved onto orcad. Build the same account services the desktop
builds (now in provider-account-services.ts), made Electron-free, and
install them in orcad except on a WSL host, whose desktop owns its accounts.
* test(orcad): keep account-services test off the real userData when XDG_CONFIG_HOME is set
* fix(orcad): structured Codex chats and session discovery follow this host's managed accounts
Create orcad's account services before the runtime so its structured Codex launch/read resolvers and catalog-probe prep are constructor deps, as on the desktop. A client-sent account home still wins, and a WSL host keeps them unset so the desktop-pushed CODEX_HOME applies. AI Vault session discovery now includes this host's managed Codex homes.
* fix(orcad): automatic Codex resumes follow the account home their session was recorded in
Share the desktop's resume launch prep as a factory and wire it into orcad's PTY registration and AI Vault resume, so a pane restored after an account switch resumes from its origin home. A client-sent account home keeps resuming from itself.
---------
Co-authored-by: Neil <neil@stably.ai>