Release dictation window callbacks by their owner and identity when sessions finish or are replaced. Clean superseded, rejected and coalesced starts without removing a newer session callback or changing audio handling.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* feat(wsl-mover): decide every piece of project state and add a losslessness projection (D1b-1)
* fix(wsl-mover): replace casts with checked reads
* fix(wsl-mover): tighten the projection after review
* fix(wsl-mover): second review round on the projection
* refactor(wsl-mover): keep the disposition tables in shared, out of the layout-writer scan
Count unreadable cookies separately from related cookies withheld by existing import safeguards. Keep import and clearing decisions unchanged and retain the legacy warning for older or invalid summaries.
Co-authored-by: oliver-mee <102673257+oliver-mee@users.noreply.github.com>
Return optional warnings from the owning host when a requested workspace status does not match its configured columns. Preserve the requested write and older-peer behavior; show warnings separately from normal CLI output.
Co-authored-by: Kalvin Mizzi <kalvin@mizzi.com>
Guard empty argument-array expansion so the bundled setup recipe runs under Bash 3.2 with unset-variable checks. Preserve exact arguments and cleanup exit behavior; normalize only test fixture line endings.
Co-authored-by: outof-place <contact@outofplace.space>
* feat(windows): add administrator PowerShell terminal setting
* fix(settings): localize administrator terminal search keywords
* fix(windows): restrict elevated terminal host to Orca's runtime and requesting account
* fix(windows): harden administrator terminal routing, inventory and restore
* fix(build): build the Windows agent hook when electron-builder packages Windows
* fix(build): build the packaged Windows agent hook for the target architecture
* fix(windows): accept the development terminal runtime and test real elevation in CI
* feat(windows): show administrator status and restart instructions
* fix(settings): localize administrator mode search keywords
* test(settings): isolate the desktop permission section in shell picker tests
* Add Windows administrator restart and simplify Terminal settings
* Keep administrator restart silent and settings actions within narrow panes
* Keep Windows shell choices and permission guides usable at minimum width
* Prevent later restarts from replaying administrator terminal reset
* Fix CI route resolution and transient Windows fixture cleanup
* fix(orcad): put Codex status hooks into a client-sent account home and list its sessions
* fix(orcad): keep account-home-only hooks, check wire paths lexically, find account transcripts by id
* fix(orcad): keep Orca's Codex hook in an account home in place, as in ~/.codex
* fix(native-chat): an unlistable account home never blocks a transcript lookup
* test(codex): type the account-home hook test's hashes without a cast
fix(terminal): retain CRLF split across output chunks
Carry a final CR until the next chunk determines whether it is a line
ending or a redraw. Preserve normalized line content, cursor counts,
existing standalone redraw behavior, and bounded terminal tails.
Fixes#27245
Co-authored-by: Joe Esposito <joeespo@gmail.com>
- The WSL command channel and command loop keep the typed result of Object.assign instead of
casting the bare Duplex through unknown to RemoteCommandChannel.
- The distro holder serializes per distro with runKeyedSerializedOperation instead of its own
copy of the same never-rejecting-tail queue.
* fix(wsl-host): orcad's own git sees the login shell's SSH agent, proxy and signing env
A WSL host's orcad starts under wsl.exe --exec sh -c, so no rc file runs and git, gh and glab
calls orcad makes miss what the user's shell exports. Adopt an allowlist of those variables from
the login shell before the terminal daemon starts. The real-distro lane now proves orcad's git
sees an rc export, and a terminal reaches Windows interop, long after the launching wsl.exe exited.
* fix(wsl-host): adopt login env before hook reconcile; honor the shell probe marker; cover git config and glab variables
* fix(wsl-host): give the login-env probe the PATH probe's 10 s budget
* fix(usage): usage requests answer authenticated-proxy challenges on a cold start
Claude, Cursor, Gemini, Grok, Kimi, OpenCode Go (API key) and MiniMax (API key)
usage fetchers called Electron's net.fetch directly, which cancels every proxy
login challenge. They now go through the shared desktop HTTP client, which
answers the challenge with the configured proxy credentials (#26957), like
Codex and ZCode already do.
* test(cursor): route the wire fixture through the desktop HTTP client
* perf(session-history): end a scan only when it stops making progress
A cold Session History scan of a large history was killed 130 seconds in
every time, however healthy it was, and every Refresh died at the same place.
The scanner child now reports progress for a running scan, at most once a
second and only when real work advanced: a directory listed, a batch of
files statted, a transcript chunk read, a candidate file parsed. Progress is
keyed to the scan's own cancel signal, so the search index or an
interactive read in the same child never extends it. The parent re-arms the
130 second window only for an active, uncancelled scan whose counter grew.
When a request deadline does fire, the child gets the shutdown frame and the
existing two-second grace to abort and save its parse cache before the kill.
Exit, disconnect, malformed-message and cancel-timeout faults still kill at
once. Startup, cancel and interactive bounds are unchanged.
* fix(session-history): keep the deadline module off child_process and let the orcad smoke skip progress frames
fix(process-host): accept string stdin in synchronous runs
Request raw subprocess output with null encoding so Node can encode string stdin as UTF-8. Preserve explicit text decoding and byte capture, with real-runner coverage for both output modes.
Co-authored-by: Joe Esposito <joeespo@gmail.com>
* refactor(wsl): register WSL host IPC only when the WSL host service exists
With the internal gate closed (or off Windows) the preload exposes no WSL host API, so the
seven handlers and the settings listener were registered only to refuse. Register nothing
in that case and drop the per-handler gate checks that a live service makes unreachable.
* test(wsl): cover the settings listener, re-registration and non-Windows gate-open cases
fix: refresh status bar and tray labels when language changes
Subscribe the memoized status bar to language changes and rebuild the existing tray menu after language or language-pack updates. Keep tray actions and attention state, and defer native menu updates off AppKit callbacks.
Co-authored-by: Eddie <tw199501@elf.tw>
fix(automations): format the jobs-file limit consistently
Use English number formatting in the English jobs-file limit error so non-English host locales still report 10,000 jobs.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
A session row decided "can ▶ resume this" twice: once for the button's
disabled state and again inside its click handler, and the chat/CLI and
switch-to-pane offers were each gated inline. A second caller of those
offers would have had to copy the same conditions again.
- One projection, resolveAiVaultSessionRowChatActions, now decides every
open/resume offer a row makes; ▶'s greyed-out state and its target come
from the same value.
- The panel's host-scope menu and per-session resume lookups move into
their own hooks unchanged, and the session-list request reads the
panel's workspace through one selector.
The sidebar behaves exactly as before.
* fix(ssh): a cancelled sign-in stays cancelled until the user reconnects
Co-Authored-By: Claude <noreply@anthropic.com>
* test(ssh): drive the credential cancel through the IPC harness
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ssh): an explicit server connect lifts the cancelled sign-in of its SSH host
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ssh): the cancelled attempt may still offer a password; a successful sign-in lifts the decline
Co-Authored-By: Claude <noreply@anthropic.com>
* test(ssh): build the managed-server link without the newer kind field
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
* feat(native-chat): show context usage in Codex chats
Codex turn-row writes become journal revisions so the reading survives a turn end.
* test(codex): run the context-usage journal suite under Node
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(notifications): remove read macOS notifications from Notification Center by pane thread
Orca could only take a delivered notification out of Notification Center
while main still held its Notification object, which it releases after five
minutes and loses on restart. Older notifications for a pane then stayed
forever.
On macOS each pane notification now goes in a thread named after its pane,
and reading the pane removes that thread. A native-chat alert has its own
identifier instead, and is removed only by a read that covers it. Windows
and Linux are unchanged.
Fixes#26732
Co-authored-by: Max Hsu <3001335+maxmilian@users.noreply.github.com>
* refactor(notifications): remove by pane thread inside the dismiss loop; only structured alerts by id
Only alerts posted with an identifier are removed by id, so renderer-rebuilt
ids no longer reach Notification.remove. Drops the extra pane-key accumulator,
the try/catch wrapper and redundant tests.
* docs(notifications): place the macOS removal rationale on the removal function
---------
Co-authored-by: Max Hsu <3001335+maxmilian@users.noreply.github.com>
A Codex usage read that failed on the network ("failed to fetch codex rate
limits: error sending request ...") was labelled "Limited" because only
Claude mapped failureKind 'network' and other providers fell through to a
rate-limit regex on the error text.
- The status bar shows "Network issue" for failureKind 'network' on every
provider; a structured kind beats the message regex, which now only
labels errors with no kind.
- Codex classifies app-server transport errors at the probe boundary;
Claude, Gemini, Grok, Kimi and OpenCode Go tag requests that throw before a
response through one shared classifier.
- A kept stale snapshot carries only the latest error's failure kind.
* feat(native-chat): add a chat-owned command receipt store
Adds the agent_session_command_receipts table and an insert-if-absent
receipt write that runs inside a command's own journal transaction. A
receipt records the command's identity, canonical hash, status and a
pointer to the row holding its result; it dies with its chat record.
Nothing uses it yet: the existing operation ledger stays authoritative.
* fix(native-chat): key command receipts like today's ledger ids
Rows are keyed by caller and operation id; a send looks across callers and
other methods look up only their caller's row, matching the existing ledger.
Bad rows and newer table shapes read as unreadable instead of throwing.
* fix(native-chat): order receipt lookups by insertion and key by operation id
Global lookups now take rows in insertion order, so a read and an insert
agree on which receipt answers an id regardless of the host clock. The
primary key leads with the operation id, so its own index serves both
lookups.
* fix(native-chat): accept sends through their own receipt
Send (direct, queued, steer) and /compact no longer commit a separate
ledger reservation before the message is saved. Their command receipt is
inserted-if-absent in the same transaction as the submission or queued
draft; a repeated id replays from it, a changed request conflicts.
Operation ids are checked for syntax only, so age, clock skew, pruning
and the reservation's own storage can no longer refuse a send.
The ledger success row they already co-committed stays as a temporary
insert-if-absent write so families not yet switched keep seeing these
ids; it is never read on this path.
* fix(native-chat): answer a send's receipt one way, share /compact's id space with Send, wake delivery once
- After its commit, a send whose publication fails answers through the same receipt replay a retry takes, so one id gets one answer; the in-memory copy and journal reload are gone.
- /compact ids are global like Send's: a retry under a new connection replays instead of needing a second acknowledgment write.
- The receipt route calls its runner directly; the runner wakes delivery when a new message row commits.
- The compatibility ledger row expires from host time; each temporary site is labelled.
- Receipt read errors and post-commit failures are logged.
* test(native-chat): expect receipt answers where tests assumed the send ledger
Restart-continuation tests now model the case Send cannot vouch for (saved but
not loaded into memory) and keep it unconfirmed; a later throw after the message
is in the chat answers continued, as a resend would. Drop /compact's unreachable
legacy replay branch and fix stale comments.
* test: run the queued tests that open the journal database in the Node runtime project
* fix(native-chat): queued-message actions and prompt answers accept through their own receipt
Send Now, Delete, Resume and prompt answers move onto the command-receipt route
Send uses: the receipt is inserted with the command's own write. A no-op answers
only once its receipt commits (otherwise outcome unknown), and a prompt answer's
replay reads the exact revision it was accepted at.
* fix(native-chat): replay a queued action from what its receipt recorded
Send Now's acknowledgement points at the submission it handed off; a question
answer's receipt keeps the accepted resolution; a no-op that cannot save its
receipt answers outcome unknown and holds nothing in memory; classified refusals
pass through.
* docs(native-chat): receipt comments say what a replay answers from
* fix(native-chat): Stop saves first, captures its target, and holds queued messages
Stop accepts through its own receipt: one transaction saves the Stop record
(target turn, agent generation, cutoff), returns unsent messages to the queue as
paused cards, and writes the receipt; only then does it interrupt the captured
target. A failed save leaves the agent running and says so. A late Stop is a
recorded no-op, and a retry is answered from the receipt.
* fix(native-chat): Stop round 1: late Stop is a no-op, saved-then-failed refuses, hold only what the Stop did not stop
- A Stop naming a turn that already ended is an accepted no-op.
- A card's Cancel saves its receipt with the dismissal row.
- A Stop saved but not carried out is refused as stopFailed ("Couldn't stop <agent>. Try again.") and its receipt is rewritten as rejected.
- A Stop during a start is saved first, then aborts that start; the abort-before-admission path is removed.
- The message the Stop stops is withdrawn as before; messages behind it are held as paused cards. Non-text messages are still withdrawn (TEMPORARY).
- Cleanups: one receipt commit path, dead fields and helpers removed, the returned-to-queue text names the agent.
* fix(native-chat): Stop round 2: narrow to receipts and save-first; message fate as on main
- Stop's withdrawal of queued sends is main's own rows (rejected, cancelled), written in the same
transaction as the Stop event and its receipt; a failed save withdraws nothing.
- A Stop arriving during a start aborts that wait on arrival, as on main (admits-now guard), then
captures its target once in the session lane. The out-of-lane admission is removed.
- A committed receipt is never rewritten: a failure after the save answers from the receipt and
writes the existing 'Cancellation was not confirmed.' note. 'Couldn't stop <agent>' only when
the save fails; a card Cancel that can't be saved says the question or approval wasn't cancelled.
- A Stop naming a turn a rewind removed is late (an accepted no-op).
- Removes the held-card settlement and returnedToQueue: what happens to a stopped message stays as
on main.
* test(native-chat): name the Stop row comparison for what it drops
* fix(native-chat): Stop round 3: a committed Stop always acts; notes only for unconfirmed interrupts
- A Stop whose write throws after its receipt committed still interrupts (never 'Couldn't stop').
- An interrupt that worked answers stopped even when only its note fails to write.
- A strict card interrupt that throws after its receipt committed gets the 'Cancellation was not
confirmed.' note, through one shared helper with the turn path.
- A target-less Stop stands while the main agent works, so a turn that opened while its save waited
(a send made before the Stop) is interrupted, as on main.
- A card Cancel that can't be saved says 'Couldn't cancel this question or approval. Try again.'
* fix(native-chat): Stop round 4: name the turn the Stop actually stops
- The Stop event's turn is resolved when its write runs (the named turn, else the live turn), so a
turn that opened while the save waited is the one marked interrupted and shown stopping.
- The unconfirmed note is keyed by that turn; tests cover it and its own write failing.
- Whether a Stop withdrew anything comes from its capture, so a throw after the commit can't flip it.
- A Stop counts as saved anyway only when its own write committed the receipt.
* fix(native-chat): a new chat's first send gets the Stop-aware operation receipt
Main's create path (from #26332) hands performSend the founding receipt; the
Stop changes widened the turn context's receipt to track its commit. Wrap it
with the existing observedReceipt.
* fix(native-chat): a background task the agent won't stop still says not done
A background-task Stop now saves its receipt before it asks the agent. A
refusal after that save (task still running, or a timeout / lost connection)
was being replaced by the receipt's replay, so the person never heard the Stop
failed. That refusal is now the first answer, as on main; the receipt stays
saved and a retry of the same id is answered from it without asking again.
* test(acp): a Grok Stop naming an ended turn leaves the unechoed follow-up alone
The late-Stop rule makes a Stop naming a turn that has ended an accepted no-op
for every agent. The Claude case was rewritten with it; this is Grok's.
* test(native-chat): Stop tests exercise what they name after the late-Stop rule
- The "leaves the child alone" Stop now reaches the provider (handed over, a
turn with no row yet); the at-rest refusal case was a duplicate of the late
no-op test and is removed.
- The adapter comment and two test names say the gap rule covers a named turn
with no journal row yet.
- The Grok late-Stop test checks no status row and that the follow-up completes.
- Name the two refusals refusalAnswers passes through.
* fix(native-chat): a background-task Stop answers its acceptance and notes what happened
A background-task Stop is saved before it asks the agent. The answer is
that saved acceptance: a stopped task answers cancelled:true as before
(the phone keeps the row "stopping" on it), and anything else answers
cancelled:false. A retry answers from the receipt without asking again.
What the agent then says comes back as a note keyed by the Stop's own id,
written through the existing Stop-note path, and never as a refusal:
- still running: "The background task wasn't stopped." (or "The
background tasks weren't stopped." for Stop all), the new status-row
failure kind backgroundTaskNotStopped;
- timed out or lost the agent: "Cancellation was not confirmed."
The note is the thread's, since tasks outlive their turn. A note that
cannot be written is logged and changes no answer.
The background-task body moves out of performCancel into the targeted
cancel, and the refusalAnswers flag and its receipt are deleted.
* test(runtime): the Codex strip's Stop notes a command it could not stop
A background-task Stop now answers its saved acceptance and says what happened in a note: "The background task wasn't stopped." when the command still runs, "Cancellation was not confirmed." when Codex was lost.
* test(native-chat): keep the Stop tests that pin behaviour, cut the rest
Merges the receipt, save-first, failed-after-save and background-task Stop
tests into one flow file; drops unit and per-branch tests already covered
by a flow test.
* fix(orchestration): stop retried failed worker starts from piling up live agents
A failed worker-start keeps the terminal it created (a pane the user typed
into is theirs), so a coordinator retrying a start that can never succeed
left one more live agent behind on every retry, with nothing to stop it.
- A --retry-of start now releases the failed attempt's still-owned terminal
through the normal worker-release path (output archived, user takeover
kept, recovery finishes it after a restart).
- A failed start now counts toward the Task's existing three-failure circuit
breaker, carried across retries; a retry of a circuit-broken Dispatch is
refused with a plain error.
Linear: STA-7113
* test(orchestration): type the failed-start retry fixtures without casts
* fix(orchestration): carry failed-start count only along --retry-of; plain breaker refusal
- Only a --retry-of start inherits its predecessor's failure count, so worker
exits across separate starts keep the old behaviour.
- The circuit-broken refusal carries failureCount and next steps that say to
stop retrying, read the last attempt, and that its pane stays open.
- A repeated failed report on a circuit-broken Dispatch is a duplicate.
Linear: STA-7113
* fix(orchestration): breaker refusal only points at a terminal that is still open
Linear: STA-7113
* fix(orchestration): log a retry's release that could not prove the old terminal closed
Linear: STA-7113
* Preserve worker diagnostics before terminal release and mask wrapped tokens
* feat(agent-launch): host plans agent launches with the window's runtime rule and one runtime fact
Host-started agent launches now pick their platform with the same rule the
window uses (project runtime, else a \\wsl$ path, else the host), and resolve
one runtime fact that supplies both the command quoting and the PTY shell.
Adds the agent.launch.terminal-inputs.v1 capability and its optional
title/extraAgentArgs/requestKind/terminalOnly fields; no sender yet.
OpenCode's createAgentSession stays on today's rules until C2 (temporary).
* fix(agent-launch): one shared runtime rule, a derived fact, and only quoted commands carry it
Round-1 review fixes for C1:
- Window and host read a workspace's runtime through one shared resolver
(local-workspace-execution-runtime.ts); a repo-less \\wsl$ folder gets no
runtime, as in the window.
- The fact is { platform, remote, pty } with the spawn's own PTY type; the
quoting shell is derived from it, never stored.
- createAgentSession uses the same fact (its pre-C1 exception is deleted).
- Worktree startups quote from a fact for the repo and pass it on; a runtime
that needs repair fails the startup, not the create. `shell` is required
when building a launch command.
- The host planner stamps a fact only on commands quoted with it; a
caller-quoted command keeps the spawn's own rule.
- Qoder probes where the process runs (\\wsl$ cwd, then a WSL shell).
- wslShellRule 'host-legacy' keeps today's WSL-shell outcome where no
project chose a runtime (temporary, removed by C8).
- terminalOnly reaches the early-tab decision with an accurate receipt
sentence; the unused client gate is removed.
- Adds one host C0 row for a \\wsl$ repo with no saved runtime.
* fix(agent-launch): round 2: keep request fields for C2, extend main's resolver, one answer per workspace
Problem: the first round still carried request fields and window-parity
switches that only the next step sends, added a new shared resolver that
also changed window code, and left some background launches using a
different runtime answer than the one they spawn with.
- Move the agent.launch request fields, their capability and fingerprint
handling, and the prompt/Codex parity switches out to the next step;
today's host behaviour is kept.
- Drop the shared resolver: main's resolveLocalWorkspaceRuntime now finds
the project by repo id or source repos, folders borrow their single
local repo, and a \\wsl$ path is not refused on a stale distro list.
Window code is back to main.
- Agents started in an existing worktree use that worktree's answer; the
phone's fallback paths spawn with the answer they quoted for, and a
runtime that needs repair shows its message instead of a plain shell.
- The OpenCode model check looks where the agent will run.
- A Windows --shell in a \\wsl$ workspace names the path, not a runtime
setting that cannot change it.
- The temporary folder rule names its follow-up, STA-10231.
* fix(agent-launch): OpenCode model picks in WSL folders keep today's Windows runtime
Problem: once a folder follows its repo's WSL runtime, an OpenCode launch
that picks a model would be checked and started inside WSL, where Orca
cannot yet verify OpenCode models, so picks that work today could be
refused.
Where main had no runtime for the scope (only folders), a model-picked
OpenCode launch keeps main's answer. Start, resume and the worker's model
check read the same answer. Temporary until OpenCode models are verified
inside WSL.
* fix(agent-launch): round 3: OpenCode keyed on the agent, new worktrees quoted for where they land
Problem: round 2's OpenCode exception keyed on a model pick, so a resumed
chat could run somewhere other than where it started, and the worker's
model check read WSL from the Terminal shell and refused folders that work
today. A new worktree's startup used the repo's answer even when the
worktree lands in a WSL share.
- In a folder whose repo runs in WSL, every OpenCode launch keeps main's
Windows answer: create, resume and the worker's model check agree. The
model check reads WSL from the path alone, as main did. STA-10251.
- A new worktree's startup is planned for its WSL-share base folder when
one is set (newWorktreeLaunchScope).
- A \\wsl$ working directory runs in WSL whatever the project says, so
non-OpenCode agents there are written for Linux.
- The planner test derives the answer instead of passing it in.
- Smaller shapes for the PTY settings type, the cache-less resolve,
executionRuntimePlatform, spawnShellInputs and two comments.
* fix(agent-launch): round 3c: one labelled rule for where the path decides; OpenCode follows the WSL-share fix
Problem: OpenCode in a repo on a WSL share whose project is saved as
Windows host still got PowerShell quoting typed into WSL, because the
WSL-share fix skipped OpenCode. The fix and the two temporary parity
rules were also split across two returns, so deleting the temporary
ones could take the permanent fix with them.
- One expression decides when the path places the agent, each term
labelled: WSL share + Windows host (permanent fix), folder + Windows
host (STA-10231), OpenCode in a folder (STA-10251).
- OpenCode now gets the WSL-share fix too; a model pick there is refused,
as main's model check already did.
- The OpenCode entry-point tests set a WSL Terminal shell, so the model
check test fails if it reads WSL from that shell again.
- Tests trimmed with no coverage loss; the spawn-precedence unit test is
dropped (the C0 WSL-share row covers the reachable case).
* fix(agent-launch): keep the window's WSL answer for panes the window spawns
Live Windows QA (Terminal shell wsl.exe, global default Windows host, repo
with no project setting) saw an agent command written with PowerShell
quoting typed into a WSL pane, so codex lost the apostrophes in its prompt.
The host wrote the command for its own answer (Windows, PowerShell), but on
routes where the window spawns the pane (the phone while a window is open,
a focused desktop chat create, `orca terminal create --command` in a repo)
the window spawned it by the Terminal shell, which is WSL. Quoting and the
pane came from two different answers.
Those routes ran in WSL with Linux-style quoting on main and worked, so the
temporary host-legacy term (STA-10231) now also covers them: when the window
spawns the pane, the path and Terminal shell decide, as on main. Background
routes keep the approved change (PowerShell instead of cmd.exe with
Linux-style quoting, which was broken on main). createTerminal and the
pre-launch planning share one `windowSpawnsTerminal` rule so they can't
disagree about who spawns the pane.
* Revert "fix(agent-launch): keep the window's WSL answer for panes the window spawns"
This reverts commit 3e4311db19.
* fix(agent-launch): report a runtime that needs repair as a refusal, not an unknown outcome
`orca agent launch` into a folder whose one repo is set to a WSL distro that
is no longer installed printed "Orca could not confirm the launch outcome;
the agent may already be running", although nothing had started. The host
knew the launch was refused before any spawn, but the refusal went out as
the generic `runtime_error`, which the CLI must treat as possibly started.
The launch planner now raises that refusal under its own code,
`project_runtime_repair_required`, with the same message, and the RPC layer
passes the code through. The CLI then prints the repair message as a plain
failure. Older CLIs read the unrecognised code as a failure too; a newer CLI
against an older host still gets `runtime_error`, as before.
* fix(agent-launch): say a repair refusal in plain words, and repeat them on a retry
The repair refusal from the last commit read "Project runtime requires repair
before terminal spawn: wsl-distro-missing", an internal reason code rather
than what happened and what to do. It now says, for example, "No agent was
started. Ubuntu is not installed in WSL. Choose an installed distro or switch
this project to Windows.", adding "This project follows the default in
Settings > Project Runtime." when the runtime came from the default.
Those sentences are the project settings page's own. They now live in one
shared module that maps each repair reason to the page's existing
translation key; the settings page passes its translator, and the host writes
English, since it also runs headless where the desktop's translations can't
load. The planner checks for a repair before the shell lookup, so the shared
spawn message, which window spawns still use, is untouched.
A retried launch (`--retry-launch`, or a phone resend) used to answer with
the bare error code. The failed launch record now keeps the refusal's words
and the retry repeats them.
* fix(agent-launch): test that a retry repeats a refusal's words, and keep the host's English equal to the window's
A retried launch now repeats a coded refusal's own words, but nothing tested
it. A new case refuses a launch with a coded error and checks the retry
answers with the same words.
The repair sentences live in one shared module whose English the host
writes and whose keys the settings page translates. The localization checks
can't see call sites outside the window and main, so the module's test now
checks each sentence against the window's English catalog for its key.
Smaller, behaviour-identical tidy-ups: the reason-to-words mapping is a
table, the refusal type fits on one line, and the length cap the retry
words share with folder refusals has a neutral name.
* refactor(agent-launch): keep main's behaviour where the stacked fixes change it
The repair refusal's own code and words, the \\wsl$ path's distro in the agent list,
--shell refusals for agents (decision and words), OpenCode's model check in a \\wsl$ repo,
and finding a repo's project by its id move to their own stacked PRs. Until they land,
each keeps main's behaviour with temporary code and a test that pins it.
A disabled agent is refused as disabled before its runtime is looked at again, as on main.
* fix(agent-launch): keep main's folder agent list when only the group names another host
The agent list for a folder reads the folder's own host pin, as on main; only launches also
read the containing group's host tag.
* ci: re-run checks now that main's capacity-capture replay fix (#28286) has landed
* test(agent-launch): give main's account-env startup test the launch runtime its build now returns
* fix(terminal): send only the text a Linux IME commits, not the candidate key
fcitx4 with Sogou reports a single-character candidate pick as a plain
Space/digit keydown whose keypress and insertText carry the candidate.
xterm encoded the keydown instead: under kitty keyboard mode the Space
leaked and the candidate was lost, and the old Linux digit window
cancelled the keydown that carried the commit.
Install the native text forwarder on desktop Linux (terminal pane and
dashboard preview) so printable bytes come only from committed text, and
delete the Linux timer heuristics it replaces. Pair an XIM pick's keyup
with its code-less press, and stop dropping unclaimed input while an
xterm composition transaction settles.
Replay tests use real fcitx4 recordings (Sogou via GTK module and XIM,
Google Pinyin in-client preedit).
Fixes#28213Fixes#26352
* test(terminal): replay Chromium's text insertion for uncancelled keys in the Hangul sequence
The observed IBus Hangul replay dispatched a bare `a` keydown with no text insertion,
which only matched reality while xterm cancelled that keydown. Now that printable
Linux text comes from the committed insertText, supply it when the keydown is not
cancelled and open the next session at the caret that follows it.
Point the IME reliability gate at the fcitx4 replay test that replaced the deleted
Linux candidate-state test.
* feat(chat): Resume in New Native Chat for Grok and OMP terminal conversations
A Grok or OMP conversation started in the terminal now offers "Resume in New Native Chat" in its
Session History row menu and its tab's right-click menu, like Claude and Codex: a new chat of that
agent takes the conversation over, showing its history and continuing it. The host finds the
conversation through the agent's transcript import and fills the chat from it, the same adoption
path Claude and Codex use; the adopted handle is now any agent's, not only theirs. Both agents keep
conversations per folder, so the action shows only in the workspace the conversation was recorded
in, as for Claude.
* fix(chat): hide Grok 1.0's runtime context and rules in a chat that takes over its conversation
Grok 1.0 appends the user's rules to its runtime-context row; the decoder only knew the older shape,
so a Grok chat taking over a terminal conversation showed that row as the first message.
* Fix OMP and Grok transcript lookup for chat adoption
* Fix scoped Grok and OMP chat history lookup
* refactor(browser): remove the annotation tray's unused open flag
Nothing ever set it to false.
* feat(browser): collapse the annotation tray to its header
Fixes#20969
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
---------
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
* perf(native-chat): read the composer's text and caret straight from the editor
Every composer read (text, caret, selection) rebuilt a per-character text-to-position
map of the whole draft: 8 times per typed character and 3 per caret key. Reads now ask
the editor directly (ProseMirror textBetween) and convert a text offset to an editor
position with one node-level walk only when a caller sets the selection or edits.
STA-7911
* test(native-chat): check a pill edit through the value setter keeps the exact text
* fix(native-chat): preserve newline before selected paragraph
A Session History scan only checked for cancellation between batches of
files. A cancel that landed while it was reading a huge transcript let the
read run to the end, so the scanner process missed its two-second
cancel acknowledgement and was killed, losing its in-memory parse cache.
Pass the scan's existing abort signal down to every expensive read: the
resumable transcript reader and its JSONL loop (checked per chunk, also
while skipping an over-budget record), the resume-point newline probe,
Cursor sidecar enrichment, generic file discovery and its stat batches,
and the search index pass. A cancelled read stores no cache entry and
keeps the earlier resume point.
A cancel is not a failure: the read outcome now says it was cancelled,
so search leaves the file's row as it was instead of counting it toward
the three-strikes hold-out, discovery records no issue for stats the
cancel cut short, a cancelled sidecar read is not cached as "no
metadata", and the scan drops a cut-short batch before recording issues.
Cursor parsed Retry-After with a bare Number(), so a huge header could block
usage refreshes for years and an HTTP-date was ignored. Claude and Cursor now
both call parseUsageRetryAfterMs, which applies the 24h usage cap to the shared
Retry-After parser, replacing Claude's private copy of the same algorithm.
The shared parser is renamed from relay-retry-after-header/parseRelayRetryAfterMs
to retry-after-header/parseRetryAfterHeaderMs: relay, the updater, mobile and
usage all use it.
* fix(terminal): don't hold a startup command for the grid settle while the window is hidden
A startup pane waits for its grid to stop changing, counted in animation
frames, so a TUI isn't drawn at a transient size. A covered or minimized
window produces no frames, so the launch waited until Orca was visible
again. Skip the settle while the window is hidden, as hidden panes already
do, and end an in-flight settle when the window becomes hidden.
* test(terminal): pass connectPanePty doubles through one checked cast
* ci(relay): use the runner's preinstalled gcloud instead of downloading the SDK
dl.google.com downloads of google-cloud-sdk time out on the runners, failing every relay
operation at setup-gcloud. skip_install keeps the WIF cred-file login.
* ci: drop the temporary gcloud skip_install proof workflow
Behavior-preserving refactor. Eleven sites built the same
`signal ? AbortSignal.any([signal, AbortSignal.timeout(ms)]) : AbortSignal.timeout(ms)`
inline or behind a local wrapper (Codex `createCodexBackendRequestSignal`,
`probeDeadline`, `boundedSignal`). They now call one
`abortSignalWithTimeout(signal, timeoutMs)` in
src/main/lib/abort-signal-with-timeout.ts:
- usage requests: Claude OAuth, Cursor, Grok, OpenCode Go API key, ZCode,
Codex backend usage + reset-credit read
- local worktree path presence, fork sync, retarget divergence budget,
upstream-ref watch resolution
The Codex reset-credit redeem, which never had a caller signal, uses plain
AbortSignal.timeout. Transports, request options, timeout values, error
mapping, and Retry-After handling are unchanged.
On Windows and Linux, Orca's app menu is attached to every window, including web popups, so it permanently takes up vertical space above the popup content. Popups now hide that menu by default, so they match a normal browser window. Alt still reveals it, but revealing it shrinks the content area without resizing the window. The origin bar and content view kept their old heights, which left a gap or clipped content. The popup now listens for `bounds-changed` on its content view and re-runs its layout, so the content fills the space under the revealed menu bar.
* fix(native-chat): chats saved before permission modes run the new-chat default
A Claude or Codex chat created before chats had their own permission mode has
none saved, and it ran Ask, so every existing chat of a Yolo user started asking
for approval after the update. A chat with no saved mode now runs Settings ->
Chat -> Permissions, read from the host's settings on each launch, read and
restart check.
* fix(native-chat): an older chat keeps the new-chat mode its first start ran with
A chat saved before chats had permission modes ran the new-chat default on every
start, so it kept following that setting. Its first start now writes the mode it
ran into the chat's saved options, through the same write that already carries the
chat's other options over a start; from then on it is a chat with its own mode.
* fix(native-chat): save older chats' permission mode once at host startup
Replace the save-at-first-start path with one startup pass: before the chat
host is exposed, every Claude and Codex record with no saved permissionMode
gets the host's new-chat default, in one store transaction. Saved values,
including invalid ones, and every other option are left alone; a read-only
store is skipped and a failure is logged and retried next start.
The runtime fallback in launch, acquisition, provider-started and attach is
restored to main's behaviour. A client-supplied attach that creates a new
record now gets the same new-chat seed as every other create path.
* fix(native-chat): fill each older chat from its own agent's arguments
An older Claude or Codex chat ran with that agent's own Arguments before
permission modes existed, so the startup pass now saves full access or ask
per agent instead of the combined new-chat default. A user with Claude on
bypass and Codex on Manual keeps Claude chats on full access.
The attach create path always passes its permission seed; the reservation
already applies options only when it creates the record.
* refactor(native-chat): read each chat's agent arguments directly in the permission backfill
* test(native-chat): give stub runtimes the launch seed the attach path reads
* fix(terminal): start CLI-created workspace terminals in main so a slow window cannot duplicate them
A focused or interactive-agent `orca terminal create` asked the window to make the
terminal and reported failure after 10 s, while the window still made it later; a
retry then made a second one. Main now starts every terminal that names a workspace
and shows it in the window best-effort, as it already did for every other create.
* fix(terminal): start a main-created terminal at its worktree's measured pane size
An unfocused CLI agent opens as an inactive tab that is not resized until the user
opens it, so starting it at 120x40 left its output wrapped to 120 columns. Start it at
the size of the pane active in the worktree's active group, where the new tab opens;
fall back to 120x40 only when that worktree has no measured pane.
* fix(cli): keep sending rendererBacked for hosts before v1.4.211
Hosts without #22384 leave a background-started agent's startup queries unanswered,
so a new CLI talking to an old, still-running host must keep routing interactive agents
to the window. Newer hosts ignore the flag.
* fix(terminal): start host-created terminals at the measured size of their tab group
The window publishes, on its graph sync to main only, the grid an unsplit terminal
would get in each shown tab group, computed by one pure function that mirrors
FitAddon.proposeDimensions and is pinned against it; a ResizeObserver on each group
body republishes when that grid changes. Main starts a new terminal at the size of the
group it opens in (the named group, else the active one) and falls back to 120x40 only
when no window measured it. This replaces the active-pane lookup, which mis-sized split
groups and could read a phone's fitted size.
* fix(terminal): measure group sizes from an untitled default-font pane and use the live active group
A pane with a title or banner pushes its xterm host down, which skewed every group's
row count; skip it like a zoomed pane. Size from the window's latest published active
group, which is where its reveal places the tab, before the saved session's.
* fix(terminal): size a CLI agent in a workspace the window has not shown
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(terminal): learn the single-group inset only from bodies shown in the content area
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: rerun PR checks against main with #28297
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: rerun PR checks against main with #28301
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): a disconnect keeps an old relay terminal's pane on the bridge instead of a new shell
* fix(ssh): release held panes when a reconnect finds their leases retired
* test(ssh): let the node typecheck see the renderer's bridged-pane selection
* fix(remote-runtime): wait for the host's terminal list before opening a terminal
A paired client opening a workspace whose terminals it had not heard about
yet read "no answer from the host" as "zero terminals" and asked the host for
one. When the host's list then arrived with terminals, the workspace gained an
extra terminal; when it arrived empty, the first-frame bootstrap created a
second one.
The client now treats an unknown host count as unknown. Activation without
explicit startup work waits, and the active subscription's first real answer
seeds the workspace if it is empty. An "ask me later" frame from a host that
has not published yet no longer records a count of zero, and the host's later
answer (sent as an update) is accepted as the first answer for seeding.
* fix(remote-runtime): count an unknown host as empty unless it answers later
Hosts before #25736 answer an empty workspace with "ask me later" and
never follow it with a real answer, so waiting for that answer left a
blank workspace with no terminal against them. Wait only on hosts that
advertise session-tabs.empty-worktree-answer.v1; older hosts keep
main's behaviour. Workspaces this client just created are known empty
and open their first terminal without waiting.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(remote-runtime): log a failed delayed terminal open; drop an inert flag
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(remote): keep a paired pane when the host has not mounted it yet
A paired client read the host's `no_connected_pty` answer as a clean
exit and closed the pane. The host also sends it when its window has
not mounted the pane within its wait, so it proves nothing about the
terminal. Back off and resubscribe within the existing recovery budget
instead; only an explicit exit verdict closes the pane.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(remote): a pane the host stops listing after no_connected_pty ends as lost
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>