Commit Graph
13370 Commits
Author SHA1 Message Date
Jinwoo Hong 2f267889e8 feat(relay): step-5 reserve core: the cell's booking book and the director's placer (inert) (#27038)
Rebased onto main as one commit after its parent squash-merged.
2026-10-10 02:03:26 -04:00
OrcaWinandm4air 2b864272d4 test(tabs): mock the Copy icon the tab context menu now imports (#27162)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 23:03:08 -07:00
Jinwoo Hong 9c41d9e8d8 feat(relay): per-cell switches for the rejection fence and the lost-reply margin (#27057)
* feat(relay): per-cell switches for the rejection fence and the lost-reply margin, and report ignored keys

rejectionFence (default on) turns the unhandled-rejection fence off without a roll.
readTimeoutMarginMs (1-60 s) replaces the boot margin per query through pg's query_timeout.
The applied log line and flagsApplied name keys this image does not know.

* feat(relay): runtime-status names the switches and values this image accepts

The flag tool checks a write against it, so it can refuse ticketCheck=enforce (or any key)
on an image that would void or drop it.
2026-10-10 01:54:00 -04:00
OrcaWinandm4air df54bbb5ed fix(mobile): a new pairing scan supersedes a stale relay pairing journal (#27157)
* fix(mobile): a new pairing scan supersedes a stale relay pairing journal

* fix(mobile): a late recovery cannot overwrite a newer pairing; only a relay-hello 4401 abandons a journal

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 22:47:08 -07:00
Jinwoo Hong 94b7936b8b fix(relay): a switch-file read cut off mid-body is asked for again (#27049)
The flag channel recorded a generation as seen before reading its body, so a
read that failed mid-body sent ifGenerationNotMatch for it on every later poll
and got 304 forever: that write was stranded until the object changed again.
The generation now counts as seen only once its body has been read and parsed.
2026-10-10 01:43:03 -04:00
OrcaWinandm4air 7a1d73576e fix(cli): orca file open and diff open name their owner (#27154)
* fix(cli): orca file open and diff open name their owner

The desktop's own catalog resolves the worktree for files.open/files.openDiff,
so the renderer notification now says so (null) instead of leaving the owner
unset, which let the focused server claim a tab the renderer could not place yet.

* fix(cli): keep a paired server's ownership on diff open

A catalog row stamped runtime:<env> names that server; null would route its diff through this desktop's git.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 22:42:34 -07:00
OrcaWinandm4air 1f5e8df421 fix(relay): wait through a scheduled reconnect and name why Relay is not ready (#27141)
* fix(relay): wait through a scheduled reconnect and name why Relay is not ready

* fix(relay): add the new mint-failure notice strings to the English catalog

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 22:35:19 -07:00
Neil c527344685 fix(agent-hooks): throttle and background last-status.json writes (#26822)
The agent status recovery snapshot was rewritten whole, synchronously on the
main thread, after a 250 ms trailing debounce on almost every hook event
(about 53 new files a minute on the reporter's Windows machine).

- Leading-edge throttle: the first change after idle is written on the next
  turn; a stream gets at most one write per 5 s and cannot postpone it.
- Changes to persisted launch-token authority skip the window, since spool
  replay and restored-terminal attestation read them after a crash.
- Writes run off the event loop through the stats snapshot writer, now
  generalized as AtomicSnapshotWriter. Quit and hook-disable still flush
  synchronously; the file format is unchanged.

Fixes #26720
2026-10-09 22:33:10 -07:00
OrcaWinandm4air c0f18c2def refactor(shell): the local-open guard takes an owner, not settings (#27144)
isLocalPathOpenBlocked now takes the path's owning host (ExecutionHostId,
an ExecutionHostOwnerMatch, or 'unresolved') and allows only 'local'. The
settings parameter is gone, so every caller names its owner. Markdown link
callers resolve the document's host from the catalog instead of falling back
to the focused server; reveal keeps the focus check explicitly because the
main process still refuses reveals while a server is focused.

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 22:27:40 -07:00
Neil 915c4b74a2 Update Claude usage without rereading the whole conversation (#26967)
* Verify pinned transcript reads and stream Codex usage aggregation

* Resume Claude usage scans with compact token checkpoints

* Harden transcript snapshots and reject incomplete discovery

* Keep Codex race fixtures inside their temporary data home

* Verify pinned transcript rotation with portable file moves

* test: keep Claude usage mock registration in test suites

* Compress saved Claude usage without dropping history

* Keep Claude cache writes bound to their original request

* Register Claude contracts without conflicting with chat contracts

* Assert unchanged Claude sources skip the actual cache writer
2026-10-09 22:26:46 -07:00
OrcaWinandm4air 36d106a8bc fix(mobile-pairing): preselect LAN for a signed-out desktop (#27145)
* fix(mobile-pairing): preselect LAN for a signed-out desktop; sign-out clears the Relay region cache

* fix(mobile-pairing): give the changed Relay sign-in copy its own localization key

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 22:23:10 -07:00
Neil 15717a4e53 Add CLI commands to manage and find workspace references (#26807)
* Add CLI commands to manage and find workspace references

* Fix reference review findings: source-context twins, legacy link collapse, closed chats, old-host errors

- Keep same-URL links with different source contexts separate; CLI add/remove no longer drops a twin
- Restore merging a bare legacy link into its single matching URL link (no duplicate PR rows, no resurrection on older-client removal)
- Carry absorbed data through collapse chains; resolve repoId conflicts deterministically
- Identity: ignore www., case-insensitive GitHub/GitLab/Gitea paths (stored URL keeps case), no Jira match on git hosts; find accepts self-hosted issue URLs
- reference find: skip closed native chats, require a session id for linked, include an explicitly named archived workspace, warm-cache fallback for --worktree
- Selector parity: collapse same-host duplicate paths, issue: ignores unlinked rows
- CLI: clear update-host error for hosts without reference.*, local flag checks before host probe, help text fixes

* Fix CI: add reference rm alias for verb policy; expect linkedItems in worktree ps summary

* Simplify reference CLI: share identifier/legacy-match helpers, drop redundant dedupe
2026-10-09 22:22:01 -07:00
fabricedgeandNeil 4b258b487b feat(tabs): copy stable tab references (#26881)
Add Copy Tab ID to terminal, editor and browser context menus.
Copy the clicked tab's existing workspace ID as "orcaTabId: <id>"
so agents can distinguish tab references from content IDs.

Keep inactive and pinned tabs identifiable without activating,
renaming or closing them. Reuse the native clipboard API and show
translated success or failure feedback after the write completes.

Cover clipboard failures, inactive and pinned tabs, separate editor
views, and browser content IDs that differ from workspace tab IDs.

Co-authored-by: Neil <neil@stably.ai>
2026-10-09 22:17:19 -07:00
OrcaWinandm4air 3822ad8b3a refactor(relay): share one retry schedule and identity key; extract the linger timer and pairing authorization (#27139)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 22:15:37 -07:00
Neil 71733ccc04 Speed up process-host import enforcement in CI (#27124)
Reduce import-scanner work by omitting unused AST parents and the duplicate traversal. Run the mandatory process-host import check alongside static work and explicitly join it before CI can succeed.

Update both workflow phase/enforcement contracts and migrate the newer benchmark to the public subprocess package API. Keep the original package build path, manifests, runtime, test selection, and shard count. Independent native review is clean; validation and timing evidence are in the PR.
2026-10-09 22:12:37 -07:00
OrcaWinandm4air 16e4d32978 fix(hosts): a server whose status answers is selectable while shared control connects (#27137)
A runtime host whose status probe verified is reachable even while its
shared-control socket is still connecting; the status owner already withdraws
that verification whenever a ready socket drops, so a status retained from a
previous socket still reads as connecting.

Fixes #10704

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 21:59:12 -07:00
Neil 8452fc3315 Keep tool results with the right command and reveal all results on mobile (#26968)
* Pair native chat tool results in linear time with provider identity

* Preserve orphan tool output and independent disclosure identity

* Keep mobile tool detail attached to its command after row changes

* Keep named mobile results open across copied history refreshes
2026-10-09 21:47:51 -07:00
OrcaWinandm4air a95006dc08 fix(persistence): a session write cannot bring back a tab its partition recorded as closed (#27125)
* fix(persistence): a session write cannot bring back a tab its partition recorded as closed

Main records every terminal close, but only PTY bindings and orphan
adoption honoured the record. A renderer save that still listed the tab
(when no topology revision fenced it yet), a relay write, or a fast
unified-tabs patch could write the closed tab back. Every full write and
unified-tabs patch now removes tabs the same partition has a live close
record for, scoped to the record's worktree. Rollback stays exempt.

* fix(persistence): the close fence accepts host slices that omit terminal maps

A host slice with no terminal rows omits tabsByWorktree, and the forced
close transform indexed it, so a browser-only save to a partition with a
live close record threw and never persisted. Close only tabs actually
listed, with the terminal maps supplied.

* refactor(persistence): the close fence reuses the shared tab-listing check

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 21:38:20 -07:00
OrcaWinandm4air 7178c7ed47 fix(web): Jira reaches the server (#27136)
The web client had no Jira namespace, so every Jira call fell through to
the silent fallback and returned undefined. Forward each Jira method to the
server's jira.* RPCs, stream issue details, and warn once per namespace when
a call lands on the fallback.

Builds on #16803 by @JasonJarvan.

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 21:37:03 -07:00
9a6b5d8753 fix(web): usage and account switching come from the paired server (#27135)
The web client's usage and Claude/Codex account bridges were stubs, so the
status-bar usage bars never appeared and switching accounts did nothing.
Back them with the server's existing accounts.list / accounts.subscribe
snapshot (which already carries rate limits) and its select/remove RPCs.
Adding or re-signing accounts now fails with a reason instead of silently
returning an empty roster.

Harvests the approach and tests from #25133 by @gustavobragac.

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: gustavobragac <gustavobragac@users.noreply.github.com>
2026-10-09 21:36:28 -07:00
OrcaWinandm4air 274e2f2294 fix(terminal): closing a tab is never a silent no-op (#27132)
Clicking x did nothing when the worktree catalog had not hydrated yet or
the tab's group record was missing. The close now routes by the host the
tab's own PTY ids (or unified tab) name, and with no host at all still
prunes locally and reaches main's close record; per-PTY teardown already
fails closed for an unproven host. closeUnifiedTab closes a tab whose
group record is gone instead of returning null.

Fixes #11308

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 21:33:19 -07:00
OrcaWinandm4air 7bd3928159 fix(runtime): a terminal close commits in the session partition that holds the tab (#27119)
* fix(runtime): a terminal close commits in the session partition that holds the tab

One worktree's tabs can be split across the local and runtime:<id> saved
partitions. The host close searched only the routed partition, so a tab
in the other one was reported tab_not_found (or recorded nowhere useful)
and the next snapshot brought it back. The close now resolves every
partition that holds that exact tab id and commits there.

Fixes #18202

* fix(runtime): refuse a close when a second copy of the tab was rebound since it was asked

The owner fence checks only the first partition holding the tab, so a
second legacy copy rebound before admission was retired and its PTY
collected for kill. Owner-fenced closes now capture every holder and
refuse with terminal_pane_owner_changed when a later copy is new or
changed.

* fix(runtime): fence a close's second copies on snapshots, not live sessions

The binding writer mutates a session in place, so comparing a captured
session reference with the current one compared a replacement with
itself. Capture an immutable copy of each holder's row, bindings and
incarnations when the close is asked.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 21:20:11 -07:00
OrcaWinandm4air 63b3f15cc4 fix(repos): every repo catalog uses the host-catalog fence (#27116)
* fix(repos): every repo catalog uses the host-catalog fence

The all-host and active-target repo loaders fenced only by fetch
generation, so a catalog fetched over a runtime connection that was
replaced mid-fetch still overwrote the current one. All three repo
loaders now claim the shared host-catalog fence (generation, SSH-state
and runtime-connection generations), which replaces the repo-only
per-host generation map and the Connect flow's inline checks. A dropped
catalog is not an answer, so the all-host load skips repo-scoped UI
validation.

* fix(repos): a superseded all-host load claims no remote fences

Its late claim would void the newer load's in-flight catalog, so neither
would apply.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 21:17:01 -07:00
Neil 7c22f6f045 [CI] Consolidate tests, retire static scans, and update evidence (#27109)
* [CI] Consolidate renderer test modules and retire static scans

* [CI] Keep consolidated test within the existing line limit

* [CI] Refresh evidence for the preserved turn-duration gate
2026-10-09 21:16:00 -07:00
OrcaWinandm4air 8ba3cf46ab fix(runtime): worktree create enforces the lineage host boundary (#27114)
Update and hydrate refuse a parent worktree on another execution host and
the renderer drops such an edge, but create recorded it anyway. Create now
checks the new worktree's host before anything is created: an explicit
parent on another host fails with LINEAGE_PARENT_CONTEXT_CONFLICT, and an
inferred one is dropped with a warning. An unknown host on either side
stays permissive, as in the projection.

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 20:59:59 -07:00
OrcaWinandm4air b23623681b fix(hosts): the unresolved-owner sentinel is never routable (#27111)
* fix(hosts): the unresolved-owner sentinel is never routable

When a worktree's owner publications conflict, the renderer marks it with
runtime:unresolved-owner so nothing treats it as local. That id parses as
a paired runtime, and AI Vault and the port scanner dialed environment
'unresolved-owner'. The sentinel is now a named shared constant with a
routable-host parser that rejects it; AI Vault list/titles/resume and the
renderer's runtime target builder use it, and the scattered string
compares collapse onto the same helpers.

* fix(worktrees): refuse a create captured on the unresolved owner

Without a routable target, the direct-SSH desktop IPC fallback would have
created the workspace on this machine.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 20:59:55 -07:00
Jinwoo Hong 3563270132 ci(release): read the watcher child from the supervisor's slot in the fault harness (#27120) 2026-10-09 23:55:13 -04:00
OrcaWinandm4air 4ec973f588 fix(cli): implicit worktree selection is scoped to the caller's host (#27108)
* fix(cli): implicit worktree selection is scoped to the caller's host

A CLI run from a shell on SSH host D matched its cwd against every host's
worktrees, so a same-path twin on host A could win. Candidates are now
limited to the caller's execution host (the relay's SSH stamp, else this
machine), and a pane's forwarded ORCA_WORKTREE_ID is scoped the same way.

* fix(cli): a local caller keeps runtime-stamped checkouts this runtime holds

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 20:36:36 -07:00
Jinwoo Hong 03408d8570 ci: skip build:packages on release tags that predate it (#27110) 2026-10-09 23:33:41 -04:00
Neil 440cde0f74 Reduce memory used to count Codex usage and avoid partial totals (#26966) 2026-10-09 20:23:25 -07:00
OrcaWinandm4air f1a09aa9c2 fix(session-tabs): a returning paired publisher is repaired by an authoritative census (#27094)
* fix(session-tabs): a returning paired publisher is repaired by an authoritative census, not dropped

* fix(session-tabs): a census never outranks a stream frame received while it was in flight

* fix(session-tabs): a coalesced census uses the newest subscription and is forgotten with its worktree

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 20:07:39 -07:00
OrcaWinandm4air b4d5b7de7a fix(terminal): honor the OpenSSH DefaultShell on Windows SSH hosts running orcad (#27085)
* fix(terminal): honor the OpenSSH DefaultShell on Windows SSH hosts running orcad (#9327)

* fix(terminal): honor only a PowerShell-family DefaultShell so agent launch quoting still matches

* fix(terminal): import the registry reader's runner from @orca/process-host

* test(terminal): type the spawnSync stub's options

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:53:58 -07:00
OrcaWinandm4air 80992a5df7 fix(session-tabs): a restored terminal row is retired only by its own host PTY (#27101)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:51:58 -07:00
OrcaWinandm4air 0969f60c8e fix(native-chat): retire a first send's echo in transcript time, not this client's (#27099)
* fix(native-chat): retire a first send's echo in transcript time, not this client's

A send into an empty transcript had no row to anchor on, so its echo was
matched against rows newer than the renderer's send time. Row timestamps
come from the host that writes the transcript; with that host's clock
behind, the echo's own turn looked older than the send and the queued
bubble never retired.

A send made after the transcript read has settled empty now records that
every row is after it. A send made while the read is still loading keeps
the existing rule, so older history arriving later cannot retire it.

* fix(native-chat): bind the empty-transcript claim to the session that was read

'ready' with no rows is also the placeholder before a provider session is
known, which read nothing; a resumed transcript can then hold an older
identical turn that retired the new send, and with it a rejected or
unconfirmed notice. The claim now needs a provider session id, records it,
and is released for any other session, falling back to the existing rule.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:40:51 -07:00
OrcaWinandm4air 310c5d02e7 fix(ssh): a direct-SSH host republish no longer steals this view's tab selection (#27100)
* fix(ssh): a direct-SSH host republish no longer steals this view's tab selection

* fix(ssh): keep the home screen out of the kept selection

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:40:02 -07:00
OrcaWinandm4air 33d90bb066 refactor(session-tabs): one authority-repair lane shared by every session-tabs mirror (#27093)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:39:00 -07:00
OrcaWinandm4air 746e4b42a0 fix(agents): a session dropped on a pane edge splits the pane in paired-runtime workspaces (#27102)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:38:57 -07:00
OrcaWinandm4air dc12495a35 fix(runtime): automatic tab activation no longer wakes a worktree the host put to sleep (#27081)
* fix(runtime): automatic tab activation no longer wakes a worktree the host put to sleep

* fix(pty): a joined user wake no longer inherits an automatic sleep refusal

* refactor(runtime): pass caller spawn guards through one helper

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:38:54 -07:00
OrcaWinandlianshuang-photo 13d8ca87dd fix(native-chat): reconcile attachment paths with pending messages (#27095)
Co-authored-by: lianshuang-photo <188543100+lianshuang-photo@users.noreply.github.com>
2026-10-09 19:28:37 -07:00
OrcaWinandm4air b9abfa9116 fix(session-tabs): a tab stream the host ends resubscribes on the retry backoff (#27098)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:28:10 -07:00
OrcaWinandm4air 20c0c6a8fc fix(ai-vault): resume a WSL session only in its own namespace, with a Linux Pi path (#27092)
On Windows a WSL workspace and a Windows workspace are both 'local', so the
resume check let a WSL session resume in PowerShell (and a Windows session
inside WSL), opening a tab that does nothing. Local resumes now also require
the session's namespace (its WSL distro, or native) to match the target's;
an unknown target namespace still refuses only what it can prove.

Pi and Prime Agent resume by transcript path, and got the raw
\\wsl.localhost UNC spelling inside WSL, so they silently started a new
session. Their provider session now uses the same normalized path OMP
already used.

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:27:34 -07:00
OrcaWinandm4air c9b7244bb9 fix(native-chat): lazy WSL tier for Claude, Grok and omp lookup by id (#27082)
Only Codex had a WSL fallback when a transcript is looked up by session id,
so Claude running inside WSL with no hook path was searched only on the
Windows profile and chat showed "Transcript unavailable". Every id lookup
now searches this host's roots first and, only on a miss, each running
distro's roots for the same agent, through the shared per-root WSL scan.

One layout table names how each agent files a session (claude, codex, grok,
omp) for both host and WSL lookups.

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:27:30 -07:00
OrcaWinandm4air d724de9d46 fix(ai-vault): bounded OpenClaw discovery; title filter when a host's search is off (#27089)
OpenClaw discovery walked every directory under <state>/agents and accepted
any JSONL under a 'sessions' segment, so an embedded <agent>/agent/codex-home
was walked (thousands of serialized SSH readDirs) and listed as OpenClaw.
Both the local and remote source tables now enter only 'sessions' below each
agent dir; the OpenClaw source entry moves next to that rule.

When a single host answers that its search is off (relays keep their index
off by design) or unavailable, the History box falls back to the title
filter for that host with a notice, instead of an empty wall.

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:17:13 -07:00
OrcaWinandm4air 905a1c7e23 fix(runtime): keep a floating terminal floating when its cwd is inside a workspace (#27088)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:16:01 -07:00
OrcaWinandm4air 1726a1079d refactor(ai-vault, native-chat): one per-call agent root table (#27079)
* refactor(ai-vault, native-chat): one per-call agent root table

The native-chat transcript resolver kept its own copy of each agent's
session roots, and it had drifted from the AI Vault source table. Both now
read session-scanner-roots: host roots computed per call, and one table of
where each agent's tree sits under a home dir (host or WSL guest).

AI Vault now lists Claude sessions under CLAUDE_CONFIG_DIR, which the CLI
writes to and native chat already read.

* fix(ai-vault): forward CLAUDE_CONFIG_DIR to the scanner child

The desktop scanner runs with an allowlisted env, so the new config-dir
root was resolved in the parent but stripped in the child that lists
History.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:05:36 -07:00
OrcaWinandm4air 6f8dc43288 fix(agent-hooks): route the first-work branch rename by the repo's execution host (#27080)
* fix(agent-hooks): route the first-work branch rename by the repo's execution host

* fix(agent-hooks): state runtime branch ownership without overclaiming server wiring

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 19:00:46 -07:00
Brennan Benson cbc565d804 Let a Codex chat stop a sub-agent from the background-tasks strip (#27026)
* Let a Codex chat stop a sub-agent from the background-tasks strip

A Codex sub-agent showed in the background-tasks strip with no Stop, and
the chat's own Stop interrupts only the main turn, so a helper ran on until
it finished. Codex's app-server takes `turn/interrupt` for any loaded thread,
sub-agent threads included, so a sub-agent row's Stop now interrupts the
turn Orca saw that sub-agent run, through the same background Stop route
Codex commands use.

Codex's -32600 refusal (no active turn, another turn, thread not found)
means the run the row showed is over, so the Stop is quiet; any other
refusal says the task wasn't stopped; a timeout or lost contact is
unconfirmed. A live run showed Codex holds an interrupt that lands as the
turn ends until the sub-agent's next turn ends, so a timeout on a run the
session has since seen end is quiet too. Older clients' `codex-agent:` rows
name no host record and stay unstoppable.

* Stop a Codex sub-agent's own sub-agents and commands with it, and hold Stopping per run

A sub-agent Stop interrupted only that helper's turn, so the commands it was
running (Codex keeps a turn's processes when the turn is interrupted) and the
helpers it had spawned kept going, and its row stayed in the strip. The Stop
now interrupts the helper's run and every descendant's run, then terminates
the processes those threads hold where the app-server has background
terminals. The interrupt goes through interruptCodexTurn, and a small
codex-background-task-stops module composes the sub-agent and terminal stops,
so the terminals module is terminals only again.

A Codex helper keeps one row id (its thread id) across runs, so a confirmed
Stop's held button carried over to the helper's next run. Both clients now
hold it for the run it was pressed on, read from the row's invocation.

* Test that a sub-agent Stop leaves siblings alone and skips terminates after a timeout

Adds direct tests of the per-run Stop hold (including an older host's roster),
matches the desktop hold comment to mobile's, and reuses the one 10 s bound.
2026-10-09 18:54:15 -07:00
Brennan Benson c8b8997762 feat(native-chat): offer Codex Ultrafast as a speed choice (#26962)
* feat(native-chat): offer Codex Ultrafast as a speed choice

Codex lists Ultrafast as a third service tier for eligible models and
accounts (GPT-6.1 Sol). Codex chats saved Fast as on/off, which cannot
hold a third tier. The Codex speed is now a Standard/Fast/Ultrafast
choice built from the tiers the model lists; the tier id is still
resolved from the catalog at each send. Older clients keep their Fast
toggle: the host still reports Fast on/off and accepts it as a speed.

* refactor(native-chat): save the Codex tier id instead of a speed name

The chat now keeps the tier id Codex lists (serviceTier: default,
priority, ultrafast) and sends it as-is on turn/start; Codex drops a
tier the model no longer lists with its own warning. This removes the
saved per-model tier map, the send-time lookup and the reconcile code.
The unused fastModeTierByModel catalog field is deleted. Older clients
still get Fast on/off, derived from the model's tier named Fast. Also
drops the pnpm-lock.yaml churn the first commit picked up.

* test(pi): drop the removed fastModeTierByModel catalog field

* fix(native-chat): review round 1 for Codex service tiers

- Accept an older client's fastMode write through the Codex adapter; it
  was refused before reaching the tier alias.
- Bump the saved model catalog schema so catalogs from before
  serviceTiers re-list instead of offering a Fast toggle with no tier.
- Keep only the reported tier on the record, not the Fast it implies.
- Drop a reported tier, not only a picked one, when the next model does
  not list it.
- Localize Standard inside the speed choice only.
- Return the options answer directly instead of a one-field wrapper.

* fix(native-chat): review round 2 for Codex service tiers

Keep a tier Codex reports for the thread (it accepts unlisted ones such
as flex); read the legacy Fast tier beside an empty serviceTiers list.

* fix(native-chat): review round 3 for Codex service tiers

A reported tier the old model listed and the new one does not resets to
Standard on a model switch, and the reported tier itself stays pickable.

* fix(native-chat): keep a re-picked reported Codex tier

The thread's reported tier is never reset by the unlisted-tier drop, so a
re-picked Flex survives the next read, a model switch, and the turn.

* refactor(native-chat): one rule for which Codex tier a model offers

Standard, a tier the model lists, or the thread's reported tier that no
model lists. The setter and the unlisted-tier reset share it, replacing
the separate reported-tier and model-switch guards.

* test(native-chat): drop the removed fastModeTierByModel field from main's new catalog test
2026-10-09 18:45:03 -07:00
Kelvin AmoabaandBrennan Benson cf5dda642d feat(native-chat): show attached files as filename pills in the composer (#26409)
A dropped or picked file used to land as its raw @path text. The pill
still serializes to that reference, so what the agent reads is unchanged.

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-09 18:42:28 -07:00
d3e3b173e6 fix(agents): one host-routed agent-detection entry point (#27054)
* fix(agents): one host-routed agent-detection entry point

preflight.detectAgents / refreshAgents take an optional { worktreeId } and the
host resolves that workspace's project runtime (WSL included) or SSH target.
Gated by preflight.workspace-scoped.v1; old peers keep the host default.

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

* fix(agents): route detection by the worktree's own host record

A repo id shared by local and SSH rows made the first row pick the detection
host. Resolve the worktree from the host's list and apply the shared
execution-host routing rule; ambiguous rows refuse. Dashboard falls back to the
host-default list until a workspace's own list loads.

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

* fix(agents): refuse an ambiguous workspace instead of probing the host

Only selector_not_found keeps the host default; selector_ambiguous now fails
closed. Adds runtime-level coverage of the lookup.

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

* fix(agents): refuse an ambiguously owned folder workspace

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

* test(agents): quick-launch runtime target carries the workspace

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

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-09 18:39:13 -07:00