* ci: fail when a checked-in test file has no runner
Two node:test files under .github/actions lost their runner when #31792
trimmed cloud pretest, and the two monaco .bench.ts budgets were never run
by any CI job. Wire both back in and add a static check that every
test-like file is collected by a declared runner or imported by a
collected entry (the src .spec.* suite-module convention).
* ci: ignore type-only imports and quote the lease glob for cmd.exe
* ci: parse type-only imports, cover UNIT_EXCLUDE, route cloud runner edits
Type-only imports are now dropped with the TypeScript parser, so a type
alias before a runtime re-export no longer hides it. UNIT_EXCLUDE files
must be named by a pr.yml or unit-tests.yml step. A cloud-only diff that
adds a test or edits a runner config now runs the static job. The monaco
benchmarks stay manual (pnpm test:perf:benchmarks) until a CI runner
proves their wall-clock budgets.
* ci: require UNIT_INCLUDE names in dedicated jobs and erase typeof import()
* ci: resolve each runtime import to the one file Vite loads
The unit dependency graph keeps every plausible candidate so it can
invalidate conservatively, which cannot prove a suite runs: an import of
./case.spec counted both case.spec.ts and case.spec.tsx. Resolve with
Vite's extension order instead.
* ci: take runtime imports from transpiled output
Imports used only as types are erased without a type modifier, so
parsing for type-only syntax still followed them. Derive edges from
ts.transpileModule output, which erases what Vitest's transform erases,
and only transpile files that can reach an uncollected candidate.
* ci: fix the agent-status benchmark config and route all cloud code
The agent-status benchmark config inherited the unit projects, whose
includes override its own, so it ran the whole unit suite and never the
benchmark. Clear projects as the performance config does. Route every
cloud code file to the static job, since content-detected tests can have
any name.
* A Claude chat shows the turns made outside it
When a Claude chat's agent next opens, turns written to the same
conversation elsewhere (claude --resume in a terminal) are appended once,
in order, under the existing "Continued outside this chat." divider.
Streamed reply and thinking rows are now keyed by their source block
(message id and block index), which Claude also saves on each transcript
row, so the chat can tell which saved rows it already shows. The read goes
back from the end of the transcript only to the chat's saved turn-end leaf,
replays the rows through Claude's own translator in an isolated sink, and
commits them in one settlement that writes nothing if the chat moved.
Imported prompts are display-only and never settle a send; any failure
opens the chat normally.
* test(claude): name the transcript row shapes the catch-up test builds
* fix(claude): read a catch-up row's uuid once as a string
* fix(claude): parse transcript tail rows without type assertions
* refactor(native-chat): share the catch-up open runner across agents
The Codex catch-up's agent-neutral parts move to one module: the open-time
envelope (its own deadline and cancellation, never gating the open), the
bounded staged batch, the single settlement commit that re-checks the
journal before writing, and the "Continued outside this chat." divider.
Codex keeps only how it reads its saved turns and what the chat already
holds. No behavior change; the other agents' catch-up builds on this
instead of copying it.
* refactor(claude): catch up through the shared history catch-up runner
* chore: remove unrelated pnpm executable metadata
* refactor(native-chat): the catch-up runner reads the chat's view only when the agent asks
* chore: remove regenerated pnpm executable metadata
* fix(native-chat): the catch-up deadline also bounds waiting for its commit to be written
A journal write barrier that never settled after the import was admitted held
the provider's open past the catch-up deadline. The wait now ends with the
deadline or the open's cancellation, like every other step.
* test: type the history catch-up sink fixture without assertions
* fix(native-chat): bound and revoke history preparation
* test(claude): expect source block identity in stream cadence
* fix(claude): preserve final output and history coverage
* feat(native-chat): validate expected saved items during history catch-up
* fix(claude): integrate conditional history settlement
* fix(claude): use new content and provider prompt identities for catch-up
* fix(claude): retain qualified completed leaves through session exit
* fix(claude): preserve unresolved membership for recovered follow-ups
* test(claude): use fork epoch reason for rewind preservation
* fix(claude): consolidate translator sink type imports
* fix(claude): preserve source message times during catch-up
* fix(claude): narrow staged history mutations to items
* test: bound Claude retained-leaf catch-up work
* fix(claude): settle imported work at source boundaries
* test(claude): keep shared journal harness portable
* test(mobile): await editor listener cleanup before snapshot
* test: isolate retained slice measurements and accept closed socket EPIPE
* 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
* 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
* fix(mobile): preserve agent icons across stacked routes and tab updates
Closes#31939
* fix(renderer): address review feedback on mobile agent icon stability
* fix(renderer): fall back to customTitle when saved leafTitle is blank
* Keep mobile agent icons painted across retained routes
Use instance-specific OMP gradients and preserve bundled icon fallback during web image reload failures. Add a real mobile-web route-retention pixel regression.
Adapted from zhangxaochen’s scoped rendering fixes in #31948; unproven session equality and title changes are excluded.
Co-authored-by: zhangxaochen <zhangxaochen@163.com>
---------
Co-authored-by: Neil <neil@stably.ai>
* test: strengthen hook, build, and mobile lifecycle coverage
* fix: use relocated Jira types in issue user field
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(acp): ACP agents can declare a managed runtime installed on first chat start
An ACP launch spec may name a pinned runtime Orca installs under its locked
data folder. The stock lookup searches it last (Command, PATH, install
directories, managed folder); a start whose lookup missed installs it under
the acquisition's signal, then resolves the launch again. A Command setting,
restart-recovery reads and catalog probes never install.
The pinned-runtime materializer can now publish a verified multi-member
folder: extract once into staging, check each member's sha256, rename into
place, with a per-asset download size limit. Node's runtime path is unchanged.
* Fix ACP managed runtime lookup precedence
* feat(acp): ACP agents can run with a private per-process temp folder
* feat(acp): old managed runtime versions are removed at host start
Once per host start, the ACP registration removes every version beside an
agent's current managed runtime folder, and staging left by an earlier
process. One host owns the data root and ACP servers are never reattached
across host runs, so nothing runs from them. This process's own staging is
kept (a publish may be in flight). Best effort: a failure is logged and never
holds up a launch.
* feat(acp): a sign-in prompt on an ACP agent's stderr ends the start or turn signed out
An agent that waits for a browser sign-in prints a link on stderr and blocks for
minutes. A launch spec can now name that line (signInPrompt); the connection then
closes with a typed sign-in failure through the existing process owner, so a start
reads notSignedIn and a running turn fails signed out instead of settling unknown.
The matched line, which carries the sign-in URL, never enters the retained stderr
tail, errors or diagnostics.
* Prevent sign-in URLs leaking from long stderr lines
* feat(acp): an agent can enforce the permission mode itself through a session config option
An optional launch-spec field names the session config option and its ask/full values. Every new or
reopened session gets the value for the chat's full-access posture before it is exposed, and a refusal
fails the start. With the field present Orca no longer answers permission requests for the person.
No agent sets it yet.
* fix(acp): reject unconfirmed permission mode during acquisition
* feat(acp): an agent enforcing the permission mode itself runs the chat's own mode
The launch spec maps each chat permission mode to its session option value; a start sets the
value for the mode the host launches with (ask when it names none), replacing the global Agent
Permissions full-access read. A mid-chat change takes effect through the host's existing restart
of a child launched with another mode before the next send.
* Keep ACP session acquisition within the line limit
* feat(acp): open Antigravity in the Chat UI over its ACP server
With the experimental chat setting on, Antigravity chats run Google's
Antigravity ACP server, which Orca installs on the first chat start: a
pinned 1.3.0 release whose archive and both executables are verified by
hash (macOS arm64/x64, Linux x64/arm64).
Each host keeps one private sign-in folder for these chats under Orca's
data folder, made owner-only before every start; the server keeps its
sign-in there as a file. A chat signs in with the server's Google method
through the existing auth-required retry; with no saved sign-in the
server's sign-in prompt ends the chat signed out. The Agent Permissions
setting maps to the server's own mode, and a question the server asks
shows as a question that takes one of its choices, never auto-answered.
A chat reopens its session with session/resume.
* fix(acp): separate Antigravity chat command and require pinned host
* test(acp): assert Antigravity launch arguments across hosts
* feat(acp): Antigravity chats offer Ask, Accept edits and Full access per chat
Each mode maps to the server's own (default, auto_edit, yolo). No Approve for me:
the server has no automatic reviewer. Accept edits gets its own description,
since auto_edit approves file edit tools only.
* feat(native-chat): offer host-provided chats without their CLI detected
A host now marks each agentSession.agents row whose chat program Orca provides there
(an ACP managed runtime with a pinned build on this host). With Chat UI on, the tab-bar
picker, the empty-workspace default chat and the new-agent shortcut offer those agents
beside the detected ones when the launch opens as a chat. Terminal launches still follow
detection, and an older host that sends no mark keeps today's detection-only offer.
* Fix CLI-free chat fallback and picker updates
* Fix admission refusal reason test expectation
* feat(native-chat): the host health snapshot says whether each chat's program is on the machine
Antigravity's chat runs Orca's own download of Google's ACP program, not the agy CLI, so clients
could not tell from CLI detection whether it can open as a chat. Each snapshot row now carries an
optional chatProgram (available, installable, missing, unknown), found through the agent's own
launch resolver without spawning anything, and re-derived after a managed runtime install or
removal. Health state and reason are unchanged, so older clients decode the frame as before.
* feat(native-chat): one renderer reading of whether an agent can open as a chat on its host
Pickers asked CLI detection whether an agent can open as a chat, which is wrong for Antigravity,
whose chat runs a program Orca downloads. This module answers from the host's published chat
program fact, and for hosts that publish none, from the host's managed-runtime mark and install
capability or else CLI detection. Facts not yet learned read as unlearned, not as no.
* fix(native-chat): a declined chat opens its agent's terminal only where the CLI is installed
Once a chat can be offered without its CLI, a host refusal used to fall back to a terminal that
runs a CLI that is not there. The one admission boundary now reports the host's refusal instead
when the workspace host's detected agents lack the CLI, for new-tab, work-item and workspace
creation launches alike. A caller's own fallback, like the empty workspace's shell, still opens.
* feat(native-chat): the + menu and Settings offer a chat wherever its program runs
With Chat UI on and no agy CLI, Antigravity had no chat row in the + menu (or a disabled
'Not installed' one when switched on), because both read CLI detection. The menu now reads the
host's chat-program answer: a chat row where the program runs or installs from the chat, no
'(CLI)' row or terminal button without the CLI. A failed CLI check no longer keeps the menu from
learning that answer. Settings shows an unchosen provider on when its CLI or the program Orca
runs for it (downloaded or found outside Orca) is installed, and holds the switch until that is
known. A saved default that opens as a chat no longer says it is not detected.
* feat(native-chat): a default agent that opens as a chat starts without its CLI
An empty workspace and the new-agent shortcut skipped a default agent whose CLI is not detected,
even when it opens as a chat on that host (Antigravity without agy). Both now honor that default,
waiting briefly for the host's chat facts when they are not known yet, while the automatic pick
with no default still chooses only detected agents. Blank and the shell fallback are unchanged.
* feat(native-chat): new workspaces, work items, continuations and the dashboard offer chat-only agents
The new-workspace composer, work-item launches, session continuation and the dashboard launcher
listed only agents whose CLI is detected, so an agent that opens as a chat without its CLI could
not be picked, and an explicit pick of it was dropped. They now also offer agents that open as a
chat on the workspace's host, keep such an explicit choice, and the dashboard republishes when
the host's chat facts arrive. Automatic picks still choose only detected agents.
* feat(native-chat): Source Control and Fix Checks accept a chosen agent that opens as a chat
The Source Control action dialog, its saved auto-start, the checks panel, recovery launches and
Fix Checks (before and after its workspace exists) rejected a saved or default agent whose CLI
is not detected, even where it opens as a chat. They now accept it there and list it, through
the shared source-control pick, while the automatic pick still chooses only detected agents.
* fix(native-chat): an empty workspace does not wait on chat facts for a Claude or Codex default
Claude and Codex have no program Orca downloads, so without their CLI only the host's own answer
could offer them as a chat; holding the workspace empty for the host's agent list could not
change that.
* test(native-chat): pin the chat-program fact, cold explicit defaults and the CLI-less decline
Covers the host's program lookup and its re-read after an install, older and unknown snapshot
values falling back to the managed mark and install capability of the same host, an explicit
default opening as a chat with CLI detection failed while automatic and blank picks stay on
detected agents, kept composer and saved Source Control choices, and a declined chat reporting
the refusal instead of opening a terminal for a missing CLI on both launch paths.
* refactor(source-control): the action dialog's agent list lives in its own hook
The dialog hook was over the file-length limit after it learned which agents
open as a chat without their CLI; detection and that offer now live together
in use-source-control-launchable-agents.
* fix: typed test mocks and record the dialog's pre-existing reset effect
The Source Control dialog's open-cycle reset effect was already on main; the
changed-code gate reported it because the effect now also learns which agents
open as a chat.
* fix(native-chat): a declined chat refuses its terminal only when the CLI is known to be missing
When a host declines a chat, Orca falls back to that agent's terminal. A chat can now be offered
without its CLI, so the fallback must not open a terminal that cannot start. But a failed or
still-pending CLI check is not proof the CLI is missing: treating it that way blocked a working
terminal fallback (for example an OpenCode version that runs in a terminal but not as a chat).
Each detection producer (local, paired server, SSH) now records the outcome of its latest attempt
for the exact target, next to the IDs it already keeps. The decline check refuses the terminal
only when the same host's latest attempt succeeded and did not list the CLI; pending, failed or
unresolved outcomes keep the existing fallback, and a caller's own shell fallback is untouched.
* fix(native-chat): every launch surface learns whether a chat opens before it decides
A chat whose program runs on the host without its CLI (for example Antigravity) could be missing,
greyed out or swapped for another agent depending on which surface launched it: the composer
dropped an explicit pick while the host was still answering, Fix Checks and the Checks panel
refused a saved chat agent, an inactive paired host was never asked about its chats, the Providers
switch flashed off, and session continuation auto-picked an agent that was only offered.
One lease per host, runtime and pairing now acquires the host's agent list and health stream
for whoever needs it: the + menu, composers, Settings, the dashboard and its pop-out, Source
Control and Checks, Fix Checks, work items, shortcuts and continuation. Actions wait for those
facts within one bounded deadline (ordinary CLI detection keeps its own wait), cancel when the
workspace's host is genuinely replaced, and re-read settings before launching. A saved, default
or explicit choice may open as a chat without its CLI; automatic picks still come only from
detected agents. Pending or unknown facts never erase a choice or flash a switch off.
The dashboard's holds are reconciled by the publishers that read them, and a hold that notifies
while it is being created no longer leaks a second stream holder.
* fix: the new-workspace composer memoizes its launch workspace from its own fields
* fix: a declined chat reads the launch host's own agent inventory; test fixtures match their types
* fix: work-item launches detect before waiting on chat facts; the composer and Source Control list agents from one owner
The composer card no longer holds its own chat-facts lease beside its modal's, Source Control reads agent
inventory only while its dialog is open, and a work-item launch keeps the new workspace's own detection
when the store has not loaded that workspace yet.
* fix(native-chat): keep acquisition tied to the current host resource
* fix(native-chat): verify the current acquisition before returning choices
* fix(i18n): retain legacy terminal-list English entries in the runtime catalog
* fix(native-chat): the dashboard republishes launch options when the active workspace's host changes
Both dashboard publishers now watch the active workspace and its selected host, which own its launch
options. Two QuickLaunchButton tests assert the current "couldn't list terminal agents" wording.
Restores base forms and drops a duplicate workspace helper to stay inside the change budget.
* test: the dashboard publisher test reads its watched state from the function it tests
* fix(native-chat): dashboards watch one checked route-input view; saved Source Control discovery settles every outcome
Both dashboard publishers observe and derive launch options from one exhaustive, compiler-checked view of
the store slices the launch-route readers take, so a route input can no longer be missed by one list. A
saved Source Control action's discovery now ends as success, cancelled or failed; only success can auto-start,
and a cancelled or failed attempt reveals the editable dialog instead of launching on a replacement runtime.
Restores base forms and shares one worktree workspace helper to stay inside the change budget.
* style: format the runtime metadata resolver
* fix: merge duplicate imports the code-quality gate rejects
* test: the dashboard watch fixture seeds every route-input field
* test: the dashboard watch fixture is fully typed without a cast
* fix: an unknown agent scan stays unknown for Fix Checks and recovery, and a saved Checks agent keeps its message
* fix: the action-first chat path asks the same missing-CLI check before opening a declined terminal; compact to the size cap
* refactor: compact the chat-program changes to the size cap after merging main
* Pickers list an agent that can't open here as disabled with its reason instead of hiding it
The Create workspace and continuation pickers only searched the agents the caller could launch, so an enabled agent with no chat on this host (for example Antigravity when the host can't run it) lost its disabled row, while the + menu showed it.
* Continuation test accepts more than one disabled subfolder row now that disabled rows are listed
* Re-run CI: the code scan was cancelled across the repository
* test: cover chat selection without terminal CLI
* test: compact host chat program fixtures
* ci: move routine checks and unsigned builds to Blacksmith
* ci: retain GitHub Mac images for isolated SSH fixtures
* ci: keep shared terminal perf preparation on Blacksmith
* test: allow encrypted slow-link fixture to finish on ARM CI
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>