* feat(orchestration): deny a worker's permission prompt, and say when mail is held
A chat worker that stops at a permission prompt now escalates with the prompt
itself as structured data (approval id, state, worker, summary, allowed
actions). The coordinator can deny it with a reason through
`orchestration deny`; the worker carries on and gets the reason as mail.
Only a person can allow a prompt: the answer method decides "person" from
the transport, never from params. The stop report keys one escalation per
approval and sends one closing line when it is answered, and no longer tells
the coordinator that `orchestration send` answers a prompt.
`orchestration send` now warns when the delivery gate would hold the mail
right now (an open prompt, a blocked status, typed text), and says delivery
state is unknown for a worker on a paired server.
* fix(orchestration): only the desktop app counts as a person for worker approvals
A paired device can carry a remote CLI, so it is treated as an agent: it can deny with a reason but never allow.
* fix(orchestration): approval answers and held receipts follow the real gates
- A same-option answer counts as Orca's only when Orca recorded it; a retry returns the same
receipt with no second reason mail.
- An unreadable chat is not a closed prompt: the answer refuses, and the closer keeps watching.
- A closing line tells a reason only when it went to the worker as mail.
- The terminal held receipt prints the delivery gate's own refusal; the chat one prints what the
pointer's admission and queue gate would do (not attached, refused, or behind an open prompt).
* fix(orchestration): read the held receipt only after the mail is stored
An await before the insert let a worker settle meanwhile, so the mail could land on a settled
Dispatch after its follow-up scan. The closer also removes only the approval map it read.
* fix(orchestration): tell the coordinator not to block on a worker's permission prompt
The approval escalation now says the worker is waiting for permission, tells a chat
coordinator the user has been shown Allow/Deny (a terminal coordinator: a person must
answer it in Orca or the worker), asks it to carry on with other work and not message
the worker until the answer arrives, and offers deny only as the way out when the
prompt is holding up the task. The guide and CLI docs say the same.
* fix(orchestration): a paired coordinator without an Orca session id mails a deny reason from its Run
* feat(orchestration): answer a Claude terminal worker's permission prompt
A worker pane's PermissionRequest hook now waits for Orca's answer, so the coordinator can deny
it with a reason (the worker continues) and a person can allow it. One record per waiting hook,
keyed by Claude's prompt and tool call; it is answerable only while the hook waits and the dialog
is on screen, and anything else gets `{}` as before.
* test: annotate the fake screen instead of casting
* test: wait on the hang-up instead of sleeping
* fix(orchestration): claim a terminal answer only from what Claude shows
An answer is written only while a caught-up screen still shows the dialog
in the same process run, and claimed only once Claude shows it applied: the
hook's deny line for a deny, no other close for an allow. A Yes, a No or the
2:00 auto-block that wins the race is refused and named from the screen.
A hook that hangs up is not an answer: with the dialog still up the
coordinator is told a person must answer it in the worker's terminal.
A PermissionRequest entry the user made async stays async: it prints {}
first and never waits for a decision.
* fix(orchestration): name a terminal prompt's close from its tool_result
The screen could show an older prompt's close, so who answered is now read
from the tool_result Claude's transcript records for that tool call: Orca's
own deny text credits Orca, the user's No and the 2:00 safety-check block
name themselves, and a run names an allow. Hook events cannot say this: a
deny or a No fires no PostToolUse. The screen only says whether the dialog
is still up before a write, and a PostToolUse seen mid-answer is kept.
* fix(orchestration): read a prompt's tool_result without blocking main
The transcript read is async, reads only its tail, and skips any path that
is not a regular file, so a FIFO or device cannot stall the main process.
A closed prompt still awaiting its result gets one last read when the lead's
turn stops, so a result written late still names the close.
* feat(native-chat): answer workers' approvals from the coordinator chat
A worker's open permission prompt appears as a card at the end of its coordinator's chat, drawn
from the escalation payload as soon as it is stored. The person allows or denies it there; the
card's buttons answer the worker's own prompt over the desktop app's IPC, so only a person can
allow. Once answered, the card turns into a receipt saying who answered.
* fix(orchestration): read a terminal prompt's countdown when it is first escalated
* refactor(orchestration): give a terminal prompt's hold state its own module
* fix(native-chat): let a worker's approval card answer, and drop it once it lapses
The window's runtime calls never carried the orchestration contract
version, so the card's Allow and Deny were refused as an obsolete client.
The renderer ships with its main process, so desktop IPC states the current
contract. An open card whose agent's own countdown has passed, with no
closing message (the app restarted while it was open), is no longer shown.
* test: say why the desktop fence test's runtime stub is enough
* fix(orchestration): a chat worker's approval asks to allow its tool, like a terminal worker's
* fix(native-chat): pause the approval countdown while the window is hidden
* chore(i18n): protect the approval card's worker line copy
* fix(orchestration): close a terminal prompt's card as soon as Orca can't answer it
When the dialog leaves without an answer through Orca, the closing line is
published at once, so the coordinator's card becomes a receipt; a tool
result that arrives later adds a second line naming how it closed, without
waking the coordinator. On startup, open terminal prompts left by an earlier
run are closed the same way, since their holds lived in memory. A Claude
terminal card's reason is the warning Claude prints under the command.
* fix(orchestration): keep the dialog-warning patterns ASCII for the orcad bundle
* fix(native-chat): say when a deny reason reached the worker as a separate message
A coordinator's deny reason for a chat worker whose prompt takes no message is sent
as mail. The receipt showed it the same as a reason that went with the denial, so it
now labels it 'Reason, sent as a message'.
* chore(native-chat): follow main's moved window-activity and agent-session modules
* fix(orchestration): only Orca's own reports draw approval cards, and every card closes
- Cards and the startup sweep read approvals only from rows whose id is the stop report's own
stable id for that approval, through one shared parser; a worker's send cannot draw a card.
- A chat worker's card closes as soon as its own chat answers it (a journal watch replaces the
turn-edge check), when an answer finds the prompt already gone, and after a restart.
- A terminal coordinator is told nobody was shown the prompt and to tell the user; the
terminal-only close asks the same and goes out as an escalation.
- A chat coordinator's line names only the answers its card offers; a prompt with none is a
plain prompt with no card.
- A person's Deny on a chat worker mails no extra reason.
- A record a quit or budget end closed stays closed.
- One closed-approval builder; `waitingOn` becomes `awaitsReply`.
* fix(orchestration): a failing read cannot hang an answer, fail a stored send, or fail the database open
* refactor(orchestration): add the held-send line in send-delivery-hold, keeping send-methods under the size cap
* test(orchestration): read the worker's mail with a schema, not a cast
* fix(orchestration): an open approval stays visible and closes even behind many closed ones or a missing database
* fix(session-history): index Codex rollouts archived as .jsonl.zst
Codex compresses rollouts idle for 7+ days to rollout-*.jsonl.zst. The
session scanner only discovered .jsonl files, so every Codex chat older
than about a week disappeared from Agent Session History. Discover .zst
rollouts, decode them through a shared zstd stream wrapper above
openTranscriptReadStream (native, WSL UNC, and SSH alike), never resume
a compressed archive from an offset, and drop a .jsonl.zst candidate
when its plain .jsonl twin is listed, matching Codex's own rule.
* fix(session-history): admit only rollout-named zst archives and close raw zstd streams early
Review follow-up on #40593: a filePredicate limits compressed discovery
to rollout-*.jsonl.zst so unrelated .zst files are never decompressed,
and zstdTranscriptStream links the raw and decoded streams with pipeline
so an early close of the decoded stream releases the raw stream's fd.
* fix(session-history): cover archived Codex rollouts by compressed size and collapse .zst twins in every listing
Archived rollouts keep the bounded resumable reader but never resume, and report their compressed stat size as the read cursor, so search coverage checks match. The .zst suffix is dropped from Codex alias keys with the plain file preferred, which also dedupes scoped walks and rollouts archived in only one home.
* fix(session-history): prefer the live rollout over an archive in another Codex home
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* fix(codex): keep the reset time when a usage limit rejects before the report reaches 100%
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(codex): read the usage-limit error code from the renamed error info
* fix(codex): skip windows that already reset when blaming a nearly spent one
---------
Co-authored-by: moncha3 <moncha3@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Native chat strips pane env, so Orca's Codex hooks only spawn a shell per
event. On Windows with Store PowerShell first on PATH, Codex fails every
command-hook spawn with os error 5 (openai/codex#47810), which native chat
showed after each tool call. Disable just Orca's hooks.json entries for the
app-server launch with a session -c hooks.state override; user hooks keep
running.
* fix(native-chat): list Codex models in the chat's folder when its command line needs that folder
A Codex command such as `node node_modules/@openai/codex/bin/codex.js` failed from Orca's empty probe folder, so the picker fell back to the built-in list. Like Claude's probe, a command line that may name files relative to the chat's folder now lists there, with that folder untrusted so its project config never changes the account's list.
* fix(native-chat): untrust the lowercased folder key Windows Codex looks up first
The settled-turn fold kept only the last prose row, so a completed Codex image generation folded behind the final answer. Mark generated-image rows and keep them out of the fold, like plans and turn notices.
* perf(agents): release published child payloads and index live work
* perf(codex): preserve live task order on membership changes
* fix(codex): preserve parent-first helper interruption
* test(codex): assert public helper task identities
* fix(codex): keep helper roots in original stop order
* fix(codex): retain complete live helper ancestry for Stop
* Preserve sibling order when reasserting helper ownership
* Fork Codex replies from the current history after rewind
* Strengthen Codex fork preparation failure coverage
* Preserve Codex fork compatibility and original chat guidance
* Explain when Codex cannot start for a fork
* Source Control lists Codex models from app-server model/list instead of debug models
The Codex text-action picker read 'codex debug models', a dump of Codex's whole
catalog with prompt templates (40-90 KB per model). Once it passed the 4 MiB
output cap discovery failed and the picker kept Orca's built-in list, and the
dump's hidden entries were selectable. List models with app-server model/list
(hidden rows excluded), over Source Control's own launch, lock and teardown,
with the same 4 MiB input budget and 60 s deadline.
* Bound Codex model discovery input in bytes, key its cache case-insensitively on Windows, and stop the app-server only when it ignores stdin EOF
- Count raw bytes from both streams against the 4 MiB budget.
- Read CODEX_HOME (and other account dirs) case-insensitively on Windows for the host cache key.
- Skip the post-listing kill when the app-server already exited; fail waiting listing requests once discovery settles.
- Report an incomplete model list as such.
- Tests: fake-timer grace and successor admission after a successful listing, method-not-found and unreadable-page fallbacks.
* Key the Codex discovery cache on the env spelling Windows spawn uses
Windows env names ignore case and Node's spawn takes the lexicographically first spelling, so
CODEX_HOME wins over codex_home; the cache key now picks the same one.
* Narrow Codex app-server records with the provider record guard instead of type assertions
* Read Source Control action model picks under the repo's host key, as the picker saves them
Local repos resolved generation under the 'unknown' host key because their connection id is undefined, so the saved pick was skipped and the agent default ran.
* Key Source Control model picks by connection until the repo record loads, and test with a real local repo
* Key a native Codex listing's cache on process.env when no agent env was resolved
* fix(native-chat): apply Windows environment overrides consistently
Use the same case-aware composition for native chat launches, preflight,
model discovery and naming. Canonicalize configured Windows names before
account readers and preserve account pins and POSIX case distinctions.
Tracked in #39075.
Co-authored-by: 김태겸 <oharapass@gmail.com>
Co-authored-by: Vitalii Kulish <mancunianez@gmail.com>
* test(native-chat): cover effective Windows catalog changes
* test(native-chat): retain child environment test import after sync
* test(native-chat): verify Windows child environment precedence
---------
Co-authored-by: 김태겸 <oharapass@gmail.com>
Co-authored-by: Vitalii Kulish <mancunianez@gmail.com>
The Codex config line scanner only treated exactly three quotes as the
multiline closer, so a value like '''printf 'ok'''' left the scanner
inside the string and later tables (e.g. [projects."..."]) were missed
when writing project trust. Match toml_parser: a run of 3-5 quotes
closes the string, with up to two quotes belonging to the content.
Co-authored-by: Neil <neil@stably.ai>
* refactor: move atomic publication to its filesystem owner
* refactor: group shared browser definitions by feature
* refactor: bind proxy process and session contracts to defining owners
* refactor: consolidate direct session contract type imports
* refactor: retain atomic publication test timing under moved owner
* fix(benchmark): resolve historical tunnel framing source before loading
* Fix the speed button's unknown, Flex and composer Escape cases
Follow-up to #40052 from its review:
- An unknown speed names no tier instead of claiming Standard.
- Escape in the speed list inside the chat options popover closes only the
list, not the whole popover.
- Only Codex's Fast tier gets the on/off chip, bolt and pressed fill; Flex
and other tiers are picked from the list. The Fast test is shared with
the main process.
- The list trigger takes the chip's pressed fill from the shared variant,
and the list has an accessible name.
* Show every agent's speed as the same Speed button in every picker
Claude's and Cursor's Fast mode now use the ⚡ Fast button under a Speed
label in the chat options popover and the select-mode pickers too, instead
of a Fast mode switch, matching Codex: one button for two modes, a list for
more.
* refactor: separate execution route and child exit owners
* refactor: move browser data into defining feature owners
* refactor: give runtime RPC entry its shutdown implementation
* refactor: use defining process types and colocate host telemetry test
* docs(process-host): point piped child contract to Node owner
* docs(process-host): clarify both piped contract owners
A Codex chat launched codex app-server on the account home saved when the
chat was created, and nothing compared it with the selection, so after a
switch every message kept going to the old account. A chat that had hit
its usage limit kept failing with "Codex hit its usage limit" even though
the new account had quota.
Codex chats now follow the selection the way Claude chats do: the launch
prepares the selected account's home, the started process reports that
home, and the host replaces an idle chat whose account no longer matches
before its next message. Codex resumes a thread only from a rollout in its
own CODEX_HOME, so the launch links the thread's rollout into the new home
first. The chat's usage card reads the account its next turn runs on.
A process in Claude's group that ignored SIGTERM held /clear for the full
3 s grace; on /clear the supervisor now reaps the group after 1 s.
Linear: STA-14361
* fix(native-chat): show newly installed skills in the Codex $ menu before the first message
Resting Codex chats served the background probe's skills/list from a
5-minute cache, so a skill installed in that window showed "No matching
skills", even in a new chat. Each menu open now compares a cheap stamp
of Codex's skill folders and skill/plugin config with the one taken
before the last scan, and rescans only when it changed.
Probe requests always force a reload, and probe pools are keyed by the
catalog's launch generation instead of the whole env, whose 10 s refresh
started a second Codex on every rescan.
Fixes STA-14359
* fix(native-chat): keep the structured session hook within its line limit
* fix(codex): recognize the 16 app-server notifications added through Codex 0.167
Users run their own Codex, so 0.167 frames already reach Orca. Unlisted notifications fell back to visible raw rows in the chat timeline; classify each one from the 0.167.0 schema instead.
* test(codex): journal no row for app-server notifications that carry provider text
* chore(lint): bump oxlint to 1.91, add perf lint rules and perf audits
* fix(lint): await promises returned inside try/catch/finally for return-await
Prepare typescript/return-await with error-handling-correctness-only: await
returns whose catch or finally was meant to cover them, and suppress the eight
sites where the catch must not re-handle the returned promise.
* chore(lint): enable return-await (error-handling-correctness-only); exempt secondary-scan directives in the changed gate
* refactor(mobile): move file preview discard modal into its own module
Keeps MobileFilePreviewScreen under the 400-line limit after merging main.
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
When a link attempt finds the target taken, check what is there. A
dangling symlink another writer created after the directory listing is
not readable history, so the thread stays out of the bridged set, as
on main.
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: AmethystLiang <6427696+AmethystLiang@users.noreply.github.com>
Account selection and every Codex launch walked all rollouts in every
source home and ran a synchronous existsSync per rollout on the main
process, then linked missing ones with sync mkdir/link/symlink. A CPU
profile of an installed build attributed ~6.4s of sampled existsSync time
to this bridge during typing lag.
The bridge now walks all source homes together and diffs directory
listings asynchronously, so a target directory is read once per pass and
history the target already holds costs no per-rollout synchronous call.
Small directories use per-path checks instead of listing a wide target.
Name matching yields every 256 entries.
Behavior matches the previous bridge:
- An existing target name is never overwritten.
- Earlier sources are tried first, and a later source still gets an
attempt when an earlier one cannot be linked.
- Hardlink-only targets skip and leave no folders behind.
- Readiness, index healing and quit handling are unchanged.
* Preserve the chat's history when forking
* Preserve fork receipts and finished conversation history
Save native-copy receipts on the real create path, retain standalone Codex copies and Claude compaction bridges, and omit unfinished helper output. Restore rewind read-error refusals and update the affected provider fixtures.
* Keep fork coverage tied to OpenCode's copied messages
* Preserve the actionable refusal when chat creation stops
* Keep completed helper answers when copying chat history
Follow spawning tools and active helpers to their descendants without making an omitted goal row invalidate a settled helper's independent output. Reuse the public copy-error distinction so private transcript exceptions stay behind the generic create refusal while authored fork errors remain actionable.
* Keep fork history stable with OpenCode 1.x
* Exclude later continuation notices from fork snapshots
* Keep fork continuation notices tied to retained work
* Keep fork catch-up recoverable and preserve copy cancellation
* Keep OpenCode coverage timeouts on the read refusal path
* test(pty): run codex --no-daemon shell cases concurrently
Each case still gets its own fresh shell and sandbox; spawning async lets
pwsh startups overlap instead of running back to back.
* test(daemon): skip raw-API serialize variants outside differential runs
Only the differential (old vs new build) I1 check reads variants 1 and 2;
a single-build replay compares variant 0, the production snapshot path.
* test(codex): shorten app-server start budgets and batch teardown races
Stand-ins arm in under 100ms on a loaded host, so a 3s start budget still
leaves wide margin; the 40 stdin-close/root-exit races run 4 at a time.
* test(codex): keep 5s start budgets for slow CI runners
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* Prepare queue rows to preserve accepted delivery intent
* Keep waiting native chat input in one ordered queue
* Keep a typed view of admitted queue records
* Keep the removed queue caption in the runtime catalog
* Remove the unused queue barrier caption
* Keep queue writes local and preserve accepted send provenance
* Read the sender from the queued admission request
* Keep continuation fixture clock within its operation window
* Remove deleted queue-barrier test from ratchet baseline
* Keep ratchet baseline change scoped to the deleted test
* Filter historical acceptance reads before loading payloads
* Instrument cached acceptance cursor once per fixture
* Restore original personal provenance after legacy card cleanup
* Complete legacy sender fixture metadata
* Narrow admitted history record types
* Reconstruct accepted personal proof after restoring original admissions
* Use the fixed clock for historical queue fixtures
* Consolidate queue source and type imports
* Preserve admission refusals and failed startup ownership
* Keep manual queue holds independent of later turn endings
* Remove obsolete mobile queue barrier expectations
* Keep journal format initialization independent of its decoder
* Assert returned-row recovery for released send clients
* Preserve Stop assertion across handoff-only publication
* fix(native-chat): list Claude and Codex models per account, not per folder
Opening the chat model picker rebuilt every worktree row to work out which
folder the read was for, because Claude and Codex model lists were keyed by
folder. Only the default model/effort depends on the project, so lists are now
keyed by account, WSL and the resolved CLI invocation and listed from the
account folder; the CLI is still resolved where the chat would launch. The
selected workspace is derived directly from its selector, and its own project
settings decide whether the account's default model and effort are shown.
The worktree projection and its cache are removed.
* test: merge duplicate node:os import
* fix(native-chat): list from Orca's probe folder, keep cwd-relative CLIs in their folder, one project-settings pass
- List Claude/Codex models from Orca's empty probe folder so a home folder's settings never read as another account's project settings.
- A command line naming files relative to the chat's folder lists there and is keyed to it.
- Read project settings once per binding for both the model and effort answers; per-model effort-only Claude settings keep the account model.
- A running chat's catalog withholds the account's configured choice where its project may pick its own.
- Bound the picker's workspace resolution so a stalled floating folder cannot hold it.
* fix(native-chat): catch quoted ./ arguments, list prewarm from Orca's probe folder, fake-timer the shared-listing test
* perf(native-chat): parse each project settings file once for both model and effort
* test: catalog e2e expectations list from Orca's probe folder; phone saturation keys per folder
* fix(native-chat): share a listing while any reader wants it; list custom command lines in the chat's folder; resolve a floating folder within the read's own wait
- A model listing shared by several folders keeps running while any reader still waits on it, queued or running, and stops (never starts) once the last one gives up.
- A Claude or Codex command line with user arguments or a relative command lists in the chat's folder; Claude reads only user settings there so project settings never narrow the account's list.
- A waiting read resolves a floating folder within its own deadline; a read that doesn't wait returns at once and starts the listing when the folder resolves.
* fix(native-chat): every reader of a shared model listing owns it until its own deadline; floating reads list their folder within their own budget
- Readers of the same request share one lookup that lives until the last reader's deadline, so later readers neither repeat CLI/settings setup nor lose the listing when an earlier one ends or is cancelled.
- A shared listing's requests carry no single reader's deadline: the provider timeout bounds them and the last reader leaving stops them.
- A floating picker read that doesn't wait starts its folder's listing under its own deadline and retirement, and says the listing is in progress so the picker follows with a waiting read.
* fix(native-chat): a shared model listing survives the chat that started it; unreadable workspaces list as the account
- Each reader of a shared lookup fences its own request, so one chat closing never ends another chat's listing; the lookup's budget is the latest deadline among readers still waiting, and its listeners are released when it ends.
- A picker for a workspace this host can't inspect (remote, WSL, ambiguous, stalled) lists as the account does from Orca's probe folder, never from wherever Orca itself runs.
* refactor(native-chat): readers of one model request share an ownerless setup and each own only their claim and the listing
- Same-request readers share one setup (folder, account, CLI scope and the folder's own default settings) that belongs to no reader: its budget is fixed when it starts, it ends with the service or the agent's retirement, and it is reused while it runs and while the listing it found runs.
- Each reader keeps its own claim, deadline and cancellation; a binding holds scope facts only and never cancels another reader's claim or a shared listing, so one chat closing never ends another chat's listing.
- A floating picker read that doesn't wait no longer asks to list the unresolved folder.
* fix(native-chat): shared catalog setups end on their budget or a workspace change, readers settle on cancel, and defaults and account seeding happen once
- A shared setup that runs out of its budget, or whose workspace or agent is retired, answers nothing at once and leaves the cache, so a reader with time left resolves afresh (a moved folder never reuses the old path).
- Cancelling a reader, or stopping the service, ends its wait at once.
- A folder's own default settings apply to its binding whenever they arrive; the account list is seeded once per shared setup, not per reader.
- A reader shares a setup only with readers whose own chat agrees on whether it may list.
* fix(native-chat): a settled catalog setup serves its whole listing, and a folder's default settings stay bounded and reach every reader
- A setup that has answered stays reusable while the listing it found runs, past its first reader's deadline, until it is ended or its agent retired, so later readers don't redo CLI and project-settings work.
- The setup's budget holds until the folder's default settings have answered too; settings that outlive it are read again while a reader still has time, and apply to the request's current binding whichever reader asked.
* refactor(native-chat): the catalog service owns each model refresh within one fixed budget; pickers only wait
A refresh (account, folder and CLI resolution, the folder's default settings,
admission and the listing) now belongs to the catalog service with one 30s
budget fixed before setup. Readers join it and wait only until their own
deadline or cancellation; none extends, shortens or ends it. Expiry withdraws
queued listings, stops the process and drops late answers. Defaults apply only
to the refresh's own binding, so a moved folder's late settings never describe
the new folder. A waiting read that used its whole budget is final.
Replaces per-reader claims, shared setups and reader-counted listing demand.
* fix(native-chat): retiring an agent or a chat's folder ends its catalog refresh at once, and a shared setup never rechecks the chat that started it
- Retiring an agent ends a refresh still stalled in setup, so a waiting picker
answers at once instead of after the refresh's budget.
- A listing that runs in a chat's own folder stops, and its answer is dropped,
when that folder's command context retires; listings shared across folders
stay independent.
- Scope resolution uses only the passivity its readers had when they asked,
so another chat's record going away mid-setup no longer turns it saved-only.
- A reader that stops waiting unsubscribes from the listing at once.
* fix(native-chat): one workspace's retirement no longer stops a catalog listing another workspace shares
A listing is keyed by the folder it lists in and the CLI invocation, so moving
one workspace never makes it describe the wrong folder; stopping it there
cancelled a listing another workspace on the same folder still needed. Agent
retirement and shutdown still stop listings at once.
* test(native-chat): a folder change keeps another workspace's shared catalog listing, and never answers the new folder with the old one's
* test(native-chat): a folder agent's listing survives another workspace on its folder moving
* test(native-chat): type the shared-setup harness's held stages without casts
* fix(native-chat): Claude and Codex pickers show the account's default in every folder
The picker no longer reads a repo's own model or effort settings for Claude and
Codex: they show the account's list and default everywhere, and the CLI still
applies the repo's settings at launch. OpenCode, OMP and Pi keep their project
checks. The model/effort split and the peek withholding go back to main's shapes.
* fix(native-chat): a Codex listing in the chat's folder never loads that folder's project config
A Codex command line that may name files in the chat's folder still lists there,
but now marks that folder and its parents untrusted for the listing, so a repo's
own model catalog can't replace the account's. The exported project-settings
check also answers no for Claude and Codex.
* fix(native-chat): Codex lists models only from Orca's probe folder
A Codex listing no longer starts in the chat's folder for custom or relative
command lines, so a repo's own config can never change the picker's list, as
other Codex hosts do. Chats still launch with the user's command line and folder.
Drops the untrusted-folder override.
* fix(native-chat): Codex listings never read project config above Orca's folder
With no project-root markers, Codex reads project config only from the listing's
own folder, so a trusted repo that contains Orca's profile can't supply the
account's model list.
* docs(native-chat): say what Codex listing isolation covers, and what managed config can still restore
* Add session Build and Plan choices to native chat
* Refresh planning choices across connected composers
* Give composer catalog tests a complete transport fixture
* Preserve planning choices through fresh turns and older hosts
* Type planning delivery fixture choices explicitly
* Honor mode capability for retained queued command picks
* Update existing native mode contract expectations
* Keep proposed plans visible in completed chat turns
* Group chat mode with permissions and goal controls
* Keep chat composer controls accessible in narrow panes
* Preserve composer overflow ownership and editor focus
* Complete composer localization catalog and subscription checks
* Keep chat composer controls contained in narrow panes
* Use the button spacing for compact Save
* Place chat mode immediately before Goal
* Restore Plan status test dependencies after main sync
* Show an exit pill only while chat Plan mode is active
* Remove obsolete composer tooltip translations
* Remove unused Build toggle catalog entries
* Apply Plan commands immediately from composer picks
* Preserve picked skill origin in composer admission
* Supply phone platform to composer regression fixture
* Preserve mode picks until their writer admits them
* Narrow the phone mode command before its draft transform
* Document the option fixture mutation shape
* Match phone Plan controls to the composer mode pills
* Assert optimistic mode feedback in the phone fixture
* Retain the phone mode pick recovery regression proof
* Use the relocated observation imports after the main merge
* Restore the composer IME module import after merging
* Read the queue assertion helper from its relocated owner
* Point the mode fixture at the relocated selection controller
* Register the phone Plan pill localization keys
* Import canonical Codex execution history
* Fix Codex history import lint warnings
* Preserve Codex imported prompt content and item lifecycle
* Preserve Codex root order after history fact eviction
* Order imported Codex turns by recorded chronology
* Preserve retained Codex lifecycle and history row placement
* refactor: group structured session modules by owner
* test: retire status history work-count oracle suite
* refactor(main): organize journal storage and projection owners
* Consolidate restart recovery fixture imports
* Consolidate concrete session option model imports
* fix(codex): remove Orca-created pre-trust entries when a worktree is removed
Records which [projects."<path>"] tables Orca's preflight trust created in a
managed Codex config (with the exact bytes written), and on worktree removal
deletes only those, inside the existing trust-config mutation queue. Removal
paths await the purge so a removal is never reported before the entry is gone.
Ported from #24803 (commits 88d5469c, c1ffc5a4, 11b0bd74, f85606b3) onto
current main: conflicts resolved, stray re-export wrappers dropped, and tests
trimmed to AGENTS.md rules (ordering/timing, private-state, Cartesian and
lower-layer duplicate cases removed).
Co-authored-by: Neil <neil@stably.ai>
* fix(antigravity): drop Orca-added trustedWorkspaces entries on worktree removal
Generalizes the Codex pre-trust ownership ledger into a small factory
(agent-trust-ownership-ledger.ts) and uses it for agy: when Orca appends a
worktree path to ~/.gemini/antigravity-cli/settings.json trustedWorkspaces, it
records the exact entry, and worktree removal removes only that recorded entry.
Entries that already existed before Orca's grant are never recorded or removed.
* fix(trust): keep folder-workspace trust on session removal; isolate trust test userData
Only checkout-deleting removal paths drop Orca-created agent trust now.
Folder-workspace sessions and forgotten orphans leave the directory on
disk, so removing one must not strip the grant siblings still rely on.
Also point the execution-host trust test at a temp userData so the new
ownership ledger never writes to the real Orca config, and make the e2e
removeWorktreeMetadataAndHistory stub return its Promise.
---------
Co-authored-by: ggbdpq <ggbdpq@gmail.com>
* feat(chat): shared contract for forking a chat from a response
Adds the create-time fork source, the host fork capability, the agent fork
level, the forked-from record relationship and the forked-from divider row.
* feat(chat): advertise each agent's fork level and a chat's fork support
* feat(chat): carry a fork source through chat launch, reload and retry
A launch with forkFrom {sessionId, throughItemId} keeps it on the create call,
the saved launch record, reload, retry and its holders, sends no new-chat
options, and is refused before create on a host that does not advertise forks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(chat): fork a native chat from a finished response on desktop
Finished root responses offer "Fork from this response" where the host and
agent can fork: disabled while support is unknown or a fork is opening, opened
in a new tab right after the source, with one toast naming the agent on
failure. The forked-from row draws as a divider linking to its source.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(mobile): fork a chat from a response in the message actions sheet
The sheet offers "Fork from this response" with the desktop's eligibility and
host capability, creates the fork through the phone's chat create, and draws
the forked-from divider with a link to the source chat.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(chat): a fork create replays only as the same fork
* Add native conversation copy adapters
* Create native chat forks with derived source markers
* fix(chat): offer a fork only on rows that show the agent's own message
A displayed row can be a tool call or a status projected as a plan while
reading as a response; both clients now check the journal row's own kind.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(chat): open phone and paired-desktop forks right after their source
The phone names its source tab as the create's placement and the host takes
that tab's group; a paired desktop sends the address captured at the click
when the fork has no tab of its own yet.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(chat): float a mid-turn response's fork action instead of adding a line
The action on a response the turn continues past hangs from a line of no
height, as the reply controls hang into the gap, so tool-heavy turns keep
their spacing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(chat): follow the chat's own fork verdict on both clients
The options read already says whether the installed agent forks; an observed
no hides the action, an unanswered read keeps it disabled, and yes enables it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Preserve native copy boundaries and rewind behavior
* Preserve imported fork tips and serialize create replays
* fix(mobile): open a fork's source through the session's own chat opener
The forked-from link now asks the controller's existing opener for the source
by history id, so it follows that opener's host, selection and reveal rules
instead of navigating on its own.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Project fork markers from touched journal groups
* Retain fork group restoration for delayed readers
* fix(chat): keep a mid-turn response's fork in its own right gutter
The floated action covered the start of the next row. It now sits beside the
response's last line, in a gutter as tall as the prose, so it adds no line and
never reaches the following row.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Preserve native fork chain boundaries and durable streamed copies
* Place phone forks beside their source in window publications
* fix(tabs): adopt a host's new tab where the host placed it
First adoption put every unseen tab at the end of its group, so a fork the host
published after its source landed last. A new tab now follows its nearest host
predecessor when that tab is here; tabs already adopted keep the user's order.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(chat): reserve the fork gutter on every root response from the first frame
The gutter came and went with the fork offer, so prose reflowed when a turn
finished or support resolved. A pane that draws forks now keeps it on all root
responses, and the fork sits only there, not also beside Copy.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Return Codex copies without automatic provider renaming
* Resolve chat placement once at the current layout owner
* Preserve chat publication when saved opens are skipped
* Retry chat opens when queued placement changes
* Bound stale chat placement retries
* test: expect the OpenCode reader's imported fork tip
* perf(chat): keep the fork offer stable while a chat streams
Every stream commit made a new forkable-id set, so the fork context changed and
every offered response re-rendered. The set is reused while its members hold,
and rows get an action object that changes only with support or pending.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep the fork params and Claude fork worker inside main's new lint limits
* Test fork copy and source refresh scaling
* fix: merge duplicate imports and stringify the OpenCode fork tip id for main's lint rules
* Keep fork placement behind session tab projection boundaries
* test: annotate the Claude copy test's hoisted account homes instead of asserting
* fix: add the fork action and forked-from strings to the English catalogs
* test: give fixtures the fork copy their agents declare; add the forked-from string to the boot catalog
* test: keep registry fixtures consistent with the fork copy their agents declare
* fix(orcad): ship the Claude fork worker with the server; update tests for fork fields
* Test saved fork selections through import and first send
* Preserve ordinary chat publication while saving placement
* Show fork beside the copy button on phone chat responses
Long-press opens text selection on every current phone, so the actions sheet that held fork never opened. The fork button now sits next to each response's copy button.
* Keep the phone fork slot fixed and offer it on every forkable response
The fork slot is now held by the row's own kind, as desktop's gutter is, so finishing a turn or learning the host's support never moves the timestamp. Responses with no copyable text (a visual only) still get fork, and each button re-renders only when its own offer changes.
* Follow main's moved runtime modules in fork code and tests
* test: build the phone response-actions fixture without type casts
* Let the phone read the host's agents so its fork action can turn on
Phone QA found the fork button always disabled: the phone asks the host which agents can fork, and the host refused that read from paired phones.
* Retry the phone's agent-list read, and read again after a reconnect
A read lost to a dropped connection or a timeout left Fork disabled until the chat was reopened.
* Own the phone's agent-list retry timer in its own effect
CI's effect-cleanup check could not see a timer set inside the read's callback; each retry is now a state change with an effect that sets and clears one timer.
* Give each phone connection its own agent-list retries
A count left over from an earlier connection could leave a new one with no retries, so one failed read kept Fork disabled after the transport moved.
* Scope the phone's agent-list retries to each spell of discovery
When the host's fork support dropped and came back on the same connection, the earlier failures carried over, so one more failure got no retry. Each stretch of discovery now owns its reads, failures and retry timer.
* Keep the forked-from source link reachable by screen readers
The divider's separator role and row label hid the source chat button inside it; the rules are now decorative and the text and button read normally.
* Keep fork names owned by adopted chat records
* Use the fork icon for Fork from this response
* Show the phone meta actions only when a row has one
An empty actions group still shifted the time by the icons' edge margin on rows with no copy, fork or rewind.
* fix(chat): put Fork beside Copy in the response's hover row
The fork button sat alone in a right gutter far from the other row actions.
It now joins the response's existing hover controls next to Copy, where those
controls show, and the gutter is gone.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(chat): keep a finished turn's actions on its last words and hold the Fork slot
A turn whose last prose was followed by tool or thought rows showed no Copy
and no Fork anywhere until it was collapsed. The actions now belong to the
turn's last prose row, decided when the transcript's rows are built, and keep
the gap below it whole so they never cover the next row. Root responses also
keep an empty Fork slot when not offered, so resolving support never moves
the row's other actions.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(chat): give a turn's actions only to a reply with words
An image-only reply after the turn's text took the response actions but could
not draw them, so the text lost Copy and Fork. Slot construction now picks the
turn's last reply by the same words the row draws and copies.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(chat): let an image-only response hold a turn's actions when its text is folded
A collapsed turn that ends in an image hides its earlier text, so no drawn row
could show the response actions and Fork vanished. Each turn's actions now go
to its last drawn response with words, else its last with images. Copy and its
context-menu target appear only where there are words to copy.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Fix Claude fork cuts after local commands
* Extract the conversation copy input contract
* Preserve caller results at Claude fork boundaries
* test: check the copied fork for the wake answer without a type assertion
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Show Codex file-change proposals in approval cards and keep verbose operation details collapsed by default on desktop and phone, with reason, access and answer choices visible.
Preserve approval identity and answer behavior, keyboard focus, and scroll position when details expand or collapse.
* feat(native-chat): show the chat account usage for /usage
* fix(native-chat): derive usage card lifetime from its owning turn
* test(native-chat): keep runtime manifest coverage additive
* fix(native-chat): advertise usage from the host options reader
* fix(native-chat): read cold chat usage through the chat's own Codex launch
- Cold /usage starts a short-lived app-server the way the chat launches
(configured command, args, environment, pinned home), as Codex rename does;
the status-bar probe returns to its original path.
- Report each reset outcome inline after the card refreshes.
- Send the chat's account as a typed kind plus email; the client owns copy.
- API-key accounts are recognised by a typed error, not message text.
- Keep the caret after kept text; drop unreachable WSL matching.
* refactor(native-chat): share the at-rest Codex app-server launch between rename and usage
* fix(native-chat): keep a reset's outcome when the usage refresh fails; label accounts by kind
* fix(native-chat): drop stale usage replies by request id instead of a render-time ref
* test(native-chat): keep the usage card test inside the web project
* fix(native-chat): clear the usage card when a waiting prompt changes; retire its callbacks on unmount
* chore: drop accidental pnpm lockfile churn from the main merge
* fix(native-chat): put the caret after kept text on /usage; offer the reset once the host's first Codex read lands
* fix(native-chat): type the usage snapshot wait and its test state
* fix(native-chat): re-read the usage offer when the chat host's Codex state changes, instead of forcing a host read
* fix(native-chat): watch the usage host from the card's opening and keep one background re-read at a time
* fix(native-chat): bind the usage card's background re-read to its opening
* feat(native-chat): open /usage as a modal like /resume
The chat's Codex usage card now opens in a root-hosted dialog with the
same surface, width and dismissal as /resume's Session History, instead of
a full-width card docked above the composer. Closing the modal retires its
reads; docked-only clearing on turn, prompt and session changes is gone.
* fix(native-chat): give the /usage modal /resume's fixed height and drop a replayed mount's first read
* fix(native-chat): size the /usage modal to its card and keep the card's layout while loading
* fix(native-chat): pin the /usage modal's top where /resume's sits so loading never moves its title
* chore(native-chat): describe the /usage loading placeholder accurately