* fix(rate-limits): spawn the Codex usage probe off the main thread
On Windows, creating a cold standalone codex.exe can spend seconds in code
integrity checks on the calling thread. The usage probe runs on window focus,
so that froze the UI. The probe's child is now created and piped on a worker
thread and proxied back to the main thread.
Fixes#27362
* refactor(rate-limits): type the worker's children through @orca/process-host
* fix(rate-limits): kill an orphaned Codex probe by pid when its worker dies
Once the worker thread that owns the probe's child is gone, kill() can no
longer reach the child through it. The proxy now falls back to a pid-addressed
kill; the Codex probe uses the app-server tree kill so a node launcher's
codex.exe child goes too, while SIGTERM stays a graceful root signal.
* refactor(build): move the main build's entry map out of electron.vite.config.ts
The new worker entry pushed electron.vite.config.ts past the 300-line limit.
The Rollup input map for the main build now lives in its own module.
* refactor(rate-limits): drop the orphan pid kill and trim the worker child-process stand-in
A worker thread's death closes its children's pipes, so an abandoned Codex
probe already gets stdin EOF, its graceful stop. The pid-addressed orphan kill
was redundant with that and with terminateCodexProbeChild's own gated Windows
tree kill. Its hard kill also bypassed the tree-kill gate's recycled-pid refusal
by turning the handle-addressed fallback into a pid kill.
- Remove orphan-pid-kill, killOrphanedCodexProbe, the injected killOrphan, and
the orphaned flag. kill() now reports false once a child is no longer tracked.
- Process-creation failure is one 'spawn-error' event that the stand-in expands
into error/exit/close, replacing the duplicated triple and the exported exit
code. Stdin errors arrive as child errors, as the probe already treated them.
- Fold both suites into one over a real MessageChannel and real children. Add a
real-worker test showing that terminating or crashing the worker lets the child
exit on stdin EOF.
* test(rate-limits): run the worker child-process suite under Node
The suite asserts Node behavior that Electron main relies on: a failed spawn
closes with a null signal, and a dead worker thread closes its children's
pipes. Bun reports an undefined signal and, on Linux, leaves the pipes open,
so the Bun lane failed on runtime differences, not on the product code.
* test(rate-limits): count a reaped-by-nobody zombie as an exited child
On Linux and macOS a child whose worker thread died gets stdin EOF and exits,
but libuv reaps a child only through the handle on the loop that spawned it.
Once that loop is gone, nothing waits on the pid, so it stays a zombie and
kill(pid, 0) still succeeds. The real-worker tests now treat a zombie as
exited, and still require the child's EOF marker.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* perf(hooks): register direct .cmd hooks for Gemini, DSH, Kiro, Droid and Command Code on Windows
On a cmd-safe profile path these agents now register the managed .cmd path
itself instead of a `powershell.exe -EncodedCommand` launcher, so each hook
event no longer pays a second PowerShell start-up on top of the runner's own
shell. Profiles the bare path cannot carry (spaces, non-ASCII, UNC) keep the
encoded launcher. Same pattern as Claude (#18875) and Muse (#26755).
Refs #20913
* fix(kimi): register a cmd.exe-runnable hook on Windows
Kimi runs hooks with asyncio.create_subprocess_shell, which is cmd.exe on
Windows. Orca registered a POSIX `if [ -f ... ]` command there, so cmd failed
with "-f was unexpected at this time" before reaching the script and, since
Kimi fails open, no status event was ever sent.
On win32 write a cmd+curl kimi-hook.cmd and register it the way the other
direct .cmd agents do: the bare forward-slash path on a cmd-safe profile,
the encoded launcher otherwise. The ownership matcher already covers every
extension of the stem, so the old kimi-hook.sh entry is replaced on the next
install. SSH remotes keep the POSIX script.
Kimi joins the direct .cmd runner suite as a row run the way CPython's
shell=True does, and every row now also runs from a profile with a space
(the encoded launcher) and outside an Orca pane.
* fix(copilot): run the Windows status hook as a direct curl .cmd
Copilot runs a Windows hook as `pwsh|powershell -nop -nol -c <command>`. Orca
registered an encoded `powershell.exe -EncodedCommand` launcher there that
started a second PowerShell to run copilot-hook.ps1, which posted with
Invoke-WebRequest: two PowerShell starts per event (~850-900 ms median), and
the .ps1 read stdin through the console code page, so non-ASCII prompt text
arrived as "caf?? ...".
On win32 write copilot-hook.cmd (answer `{}`, refresh the endpoint, env guard
before stdin, curl form POST with hookEventName) and register its bare path
with the event as its argument; non-cmd-safe profile paths keep the encoded
launcher, which now sets ORCA_COPILOT_HOOK_EVENT for the .cmd. The managed
matcher covers every extension of the stem, so old .ps1 entries are replaced
on the next install while user hooks stay. SSH remotes keep the POSIX script.
Copilot joins the direct .cmd runner suite as a row run under both
PowerShells, checking the byte-exact payload and the posted hookEventName.
Refs #20913
* refactor(hooks): one Windows batch-hook helper and one payload-delivery table
- Add wrapWindowsBatchHookCommand (bare .cmd path, encoded launcher
otherwise) and use it for Muse, Gemini, DSH, Kiro, Droid and Command Code.
- Fold the five agents into windows-hook-payload-delivery.test.ts as rows
run under each vendor's shell, from a plain and a spaced profile, with a
stale encoded registration seeded per row; drop the separate runner suite
and its CI list entries.
- Replace the Windows-only per-agent registration tests with ones that stub
process.platform, so they run on every CI OS.
- Fix stale launcher, helper and DSH comments.
Refs #20913
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* perf(hooks): register direct .cmd hooks for Gemini, DSH, Kiro, Droid and Command Code on Windows
On a cmd-safe profile path these agents now register the managed .cmd path
itself instead of a `powershell.exe -EncodedCommand` launcher, so each hook
event no longer pays a second PowerShell start-up on top of the runner's own
shell. Profiles the bare path cannot carry (spaces, non-ASCII, UNC) keep the
encoded launcher. Same pattern as Claude (#18875) and Muse (#26755).
Refs #20913
* fix(kimi): register a cmd.exe-runnable hook on Windows
Kimi runs hooks with asyncio.create_subprocess_shell, which is cmd.exe on
Windows. Orca registered a POSIX `if [ -f ... ]` command there, so cmd failed
with "-f was unexpected at this time" before reaching the script and, since
Kimi fails open, no status event was ever sent.
On win32 write a cmd+curl kimi-hook.cmd and register it the way the other
direct .cmd agents do: the bare forward-slash path on a cmd-safe profile,
the encoded launcher otherwise. The ownership matcher already covers every
extension of the stem, so the old kimi-hook.sh entry is replaced on the next
install. SSH remotes keep the POSIX script.
Kimi joins the direct .cmd runner suite as a row run the way CPython's
shell=True does, and every row now also runs from a profile with a space
(the encoded launcher) and outside an Orca pane.
* refactor(hooks): one Windows batch-hook helper and one payload-delivery table
- Add wrapWindowsBatchHookCommand (bare .cmd path, encoded launcher
otherwise) and use it for Muse, Gemini, DSH, Kiro, Droid and Command Code.
- Fold the five agents into windows-hook-payload-delivery.test.ts as rows
run under each vendor's shell, from a plain and a spaced profile, with a
stale encoded registration seeded per row; drop the separate runner suite
and its CI list entries.
- Replace the Windows-only per-agent registration tests with ones that stub
process.platform, so they run on every CI OS.
- Fix stale launcher, helper and DSH comments.
Refs #20913
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* feat(native-chat): wrap long lines in code blocks
Code blocks in native chat now have a wrap-lines toggle next to Copy.
It is off by default, per block, and not persisted; when on, the block
wraps long lines instead of scrolling sideways.
* fix(native-chat): give each document code block its own component
The document pre renderer called the code-block renderer as a function after
an early return, so a renderer with hooks ran them as the pre's own hooks and
React saw the hook order change when a fence switched between diagram and code.
* fix(native-chat): keep a block's line wrap when its row scrolls away
Wrap lived in the code block's own state, so a row that windowing unmounted,
or a growing reply re-cutting its top-level blocks, came back unwrapped and
shorter than its measured height. The flag now lives in the transcript's
disclosure store, keyed by the row and the fence's offset in the whole
message, which appended text never moves. Outside a transcript the block
keeps local state as before.
* fix(native-chat): key a block's line wrap by its line, not its character offset
While a reply streams, its last block is parsed after the streaming fixes
escape characters such as x~y mid-line, so a later fence's character offset
moved by one when the block was next parsed as written (the reply finishing,
another block following, or the reply merging into one block), and the wrap
reset. Those fixes never add or remove a line break before a fence, so the
block splitter now records each block's starting line and the wrap key uses
the fence's line in the whole message.
* fix(native-chat): keep line wrap in plan, reasoning and approval cards too
Plan cards and reasoning bodies rendered their markdown without a row key,
so their code blocks fell back to local wrap state and lost it when the row
scrolled away. Reasoning text now uses its existing disclosure key, shared by
the live line and the finished row, and a plan card uses plan:<message id>, so
neither can share a key with the reply of the same message. The approval card
sits in the composer slot, outside the transcript and its store, and is never
windowed, so it keeps local state.
* fix(native-chat): keep a 'chats resumed' bottom-bar entry until the result is seen
After every chat resumed, the bottom-bar entry vanished the moment the run
finished, leaving a fading toast as the only way back to the list. Show
'N chats resumed' while a finished run's summary has not been shown yet,
reusing the run's existing finishedViewShown flag so the entry ends exactly
when the summary is viewed and closed.
* Keep resume results until their rows are seen
* Retire viewed empty resume runs
* Show finished resume rows after in-flight view settles
* fix(chat): name orchestration worker chats after their task title
Worker chats were skipped by chat naming and stayed "Claude Chat".
Name them from the task they were started for instead, with no model call.
* Fix legacy worker chat titles without prompt generation
* Remove unrelated lockfile dependency
* [CI] Remove wall-clock waits and stabilize GC fairness check
* [CI] Include the final GC timer gap in the fairness check
* [CI] Advance test clocks and preserve cleanup checks
* fix(native-chat): light the tick of a message sent mid-turn on the message rail
A message sent while the agent is working joins the running turn's key, and the
rail lit ticks by turn key, so that message's tick never lit: not at the bottom,
not after clicking it. Light the newest tick at or above the read row within its
turn instead.
* fix(native-chat): light the nearest tick above the read row, and keep a clicked tick lit
Review round 1. The within-turn walk stopped at a row of no turn landing in the
running turn, and could return a tick from the turn above. The lit tick is now
the newest loaded rail item at or above the row at the fold, falling back to its
turn key above the first loaded one (that prompt is in the outline).
A jump that cannot bring its row to the top lands clamped at the bottom, and the
settled position report then lit the last tick instead of the clicked one. A
clicked tick now stays lit until the reader scrolls or starts another navigation
(the existing reader-input path), or the set of prompts changes.
Rail picks and the start of every navigation move into their own hook, as the
list is at its line limit.
* fix(native-chat): hold a clicked rail tick by position, not by reader input
Review round 2. Releasing the clicked tick on reader input needed every scroll
source to report itself, and several do not (scroll-to-top button, inline diff
reveal, prompt refold, selection-drag autoscroll); releasing it whenever the
prompts changed dropped it on every head trim of a long live session.
The hold now records where the jump settled, at the first idle read after the
click, and keeps the clicked tick lit while the view rests there. Any movement,
whatever caused it, releases it, as does the clicked message leaving the rail.
Drops the navigation wiring and the hook it was moved into.
* fix(native-chat): anchor a clicked rail tick's hold to its row on screen
Review round 3. Holding while scrollTop stayed put recorded the reader's spot when
they kept scrolling within the idle period of the jump, kept an off-screen row lit
after the viewport shrank, and let go when a row above re-measured late and shifted
scrollTop with the content.
The hold now records how far below the viewport top the clicked row settled, and
keeps the tick lit while the row is still on screen at that offset.
* feat(terminal-pane): header button resumes the CLI session in a new native chat
The pane header's "Show chat view" toggle is replaced by the tab menu's
"Resume in New Native Chat" move. The Session History lookup runs on click,
so an always-visible header never scans in the background. A pane already
in chat view keeps its "Show terminal" way back.
* fix(terminal-pane): fold the resume gate into the pane's existing store subscription
Keeps a mounted terminal pane inside its pinned store-listener budget.
* fix(terminal-pane): resume the pane's own conversation, open the chat that already holds it
The header "Resume in New Native Chat" button gated on chat-view eligibility but looked up the
tab's best conversation, so a Grok pane beside a Claude pane resumed the Claude conversation, and
Grok/OpenCode panes showed a button that always failed with a "try again" toast.
- Map one pane to its conversation with the tab-title mapping narrowed to that pane (same sources,
same Claude/Codex rule), for both the button's visibility and its click; this also keeps the
button after the agent exits, as the tab menu does.
- A conversation a native chat already holds now matches its history row; the move's gate still
refuses it (tab menu unchanged, pinned in the parity test), and the button opens that chat.
- Distinct messages: nothing saved yet, cannot be resumed in a native chat, or the existing
generic failure. The click forces a fresh scan like the panel's Refresh does.
- Call the plain resume launch directly; drop the shared actions module and restore the tab menu.
- A tab closed during the lookup no longer opens a chat.
* fix(terminal-pane): hide Resume in New Native Chat where the chat route is refused
On a local workspace whose project runs in WSL (or needs runtime repair), the header button showed
on Claude/Codex panes, but native chat cannot launch there, so every click said the conversation
can't be resumed while the tab menu offered nothing. The button's visibility now also asks the
same launch-route check the Session History row gate uses (structuredAgentSessionLaunchFeasible).
* fix(terminal-pane): offer the header resume only while the pane's agent is running
The tab menu now offers Resume in New Native Chat only while the tab's agent
runs (#27520). The header button still found its pane's conversation from the
retained or sleeping record an exited agent leaves behind, so it stayed after
exit. The pane lookup now honours the same runningAgentsOnly option, and both
the button's visibility and its click pass it.
* refactor(terminal-pane): the header's pane lookup reads only the running agent
Every caller of resolvePaneAiVaultTitleRequest asked for running agents only,
so its retained and sleeping sources were never read. The pane lookup now maps
just the pane's live agent, and collectAiVaultTitleRequests returns to main's
shape, sharing only the live-agent mapping with it.
* perf(hooks): register direct .cmd hooks for Gemini, DSH, Kiro, Droid and Command Code on Windows
On a cmd-safe profile path these agents now register the managed .cmd path
itself instead of a `powershell.exe -EncodedCommand` launcher, so each hook
event no longer pays a second PowerShell start-up on top of the runner's own
shell. Profiles the bare path cannot carry (spaces, non-ASCII, UNC) keep the
encoded launcher. Same pattern as Claude (#18875) and Muse (#26755).
Refs #20913
* refactor(hooks): one Windows batch-hook helper and one payload-delivery table
- Add wrapWindowsBatchHookCommand (bare .cmd path, encoded launcher
otherwise) and use it for Muse, Gemini, DSH, Kiro, Droid and Command Code.
- Fold the five agents into windows-hook-payload-delivery.test.ts as rows
run under each vendor's shell, from a plain and a spaced profile, with a
stale encoded registration seeded per row; drop the separate runner suite
and its CI list entries.
- Replace the Windows-only per-agent registration tests with ones that stub
process.platform, so they run on every CI OS.
- Fix stale launcher, helper and DSH comments.
Refs #20913
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* fix(browser): settle WSL tunnel reads so responses past 256 KiB complete
The WSL execution route hands the deferred socket a credit-accounted tunnel
duplex, but the deferred socket forwarded its data without settling it, so the
relay's 256 KiB receive window was never replenished and larger responses
stalled. Settle each chunk once tunnel-socket listeners have taken it; their
pause() still holds the source, so unsettled bytes stay bounded by the window.
Fixes#27356
* test(browser): bound the WSL backpressure wait so a regression fails instead of hanging
* test(browser): keep unchanged WSL route test lines in their original form
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* fix(browser): end a background page's mount admission after its first load
A page opened in the background is admitted so its guest starts, but the
admission was only released on close. Its overlay stayed laid out (invisible)
in the visible workspace and its browser chrome never unmounted in hidden
workspaces. Release the admission when the guest's first load settles, so the
normal active-page and paint-retention rules own the page afterwards.
STA-7435
* test(browser): pin how a released mount admission parks a background page
Covers the overlay contract the release relies on: in a hidden worktree the
page's browser chrome unmounts once the admission ends; in the visible worktree
it stays mounted and only its slot stops being laid out. Also corrects the
page-record comment that still said background tabs sit idle until shown.
* ci: ratchet synchronous child-process launches in main and shared code
The Electron main process freezes every window while a synchronous child
process runs, and on Windows each launch is slow because antivirus scans
it. Nothing stopped new synchronous launches from landing in main-process
code, and the spawn observer behind ORCA_MAIN_THREAD_DIAGNOSTICS only saw
async spawns, so the worst blocks were invisible to it.
Ratchet: check:main-sync-spawn-ratchet parses production src/main and
src/shared files (test-only paths excluded) and fails when a file not in
config/main-sync-spawn-baseline.txt reaches execFileSync, spawnSync or
execSync from child_process, runProcessSync from @orca/process-host, or
one of the synchronous secure-file writers (they run icacls on Windows).
Named, aliased, namespace, default, require() and import() forms count.
A baseline entry whose file no longer launches synchronously fails as
stale, so the list can only shrink. It runs in lint and in the PR
static-analysis job next to the process-host import check.
Observer: runProcessSync now reports to the spawn observer, timing the
whole call (that is how long the thread was stuck) and reporting from a
finally so failing, throwing and timed-out children are counted too.
Runtime behavior is unchanged except that the diagnostics observer now
sees synchronous launches; with no observer registered the sync path
pays one extra branch.
* ci: count sync launches per file and gate the repo's sync wrappers
The sync-spawn ratchet only tracked which files launched synchronously, so a
file already on the list (wsl.ts, win32-utils.ts, git-exec-file.ts) could add
more launches unnoticed, and new callers of the repo's own synchronous
wrappers passed CI.
The baseline now stores a per-file count of uses, in the same format as the
focus-read ratchet: a rise or a new file fails, a lower or zero count fails
until `pnpm check:main-sync-spawn-ratchet --prune` lowers it, and --prune
never raises an entry. Every value reference to a gated function counts
(call, hand-off, alias and its uses, re-export); imports alone count nothing.
Gated wrappers: gitExecFileSync (and the git/runner re-export),
restrictWindowsPathSync, hardenSecurePath when called with sync: true,
grantDirAcl, getWslHome, listWslDistros, wslUncDirectoryExists,
isWslAvailable (and the wsl.ts re-export), isPwshAvailable. Their 18
current caller files join the baseline.
Failure messages now give advice per family: the async process-host API for
child-process launches, the async twin for wrappers, and for the secure
writers a note that no async secure writer exists yet, so the sync one stays
and must never become a plain write (that drops the Windows ACL hardening).
Also refresh two stale blockMs descriptions and drop a no-op line from the
spawn-observer "stays silent" test.
* ci: count hardenSecurePath unless visibly async, and tighten ratchet tests
hardenSecurePath was counted only when `{ sync: true }` was written inline,
so moving the options into a variable or a spread dropped the count and the
check then asked for a prune. It now counts every use except a call with no
options or a spread-free object literal whose `sync` is absent or false.
Also: resolve backslash paths in the importing file as well as the
specifier; still visit heritage and instantiation expressions
(`extends mixin(fn)`, `fn<T>`); state the reference-only counting limit;
and point the secure-writer advice at raising this file's baseline count.
Tests cover the hardenSecurePath shapes, the alias chains that need the
fixpoint loop, and a prune that must not raise a grown row.
* test(ci): pin the main sync-spawn ratchet background step
* fix: count secure hardening calls with spread arguments
* perf(notifications): save mobile dismissal history off the main thread
Every agent notification recorded its id in mobile-notification-dismissals.json
through the secure-file writer. On Windows that writer runs about five
synchronous icacls launches per write, so each notification froze every Orca
window (STA-9692).
The file holds notification ids, sequence numbers and session ids, not
credentials, so it now goes through the existing AtomicSnapshotWriter: async
temp-plus-rename, mode 0600, bursts coalesced into one write of the latest
entries. Memory stays authoritative, so delivery and retirement are unchanged.
The pending write is flushed in the quit barrier after chat and RPC teardown,
and in the pre-relaunch hook, since renderer relaunch exits without the quit
pipeline.
* perf(notifications): flush dismissal history when a headless host shuts down
orcad never runs the desktop quit barrier, so its cleanup list awaits the
pending dismissal write after RPC stops.
* fix(persistence): drain a snapshot write requested just after a drain ends
AtomicSnapshotWriter cleared its pending-write promise one microtask after
the drain loop saw no more requests. A write() in that gap set
writeRequested, got the settling promise back, and nothing drained it, so
the newest bytes stayed unwritten and waitForPendingWrite() (the dismissal
store's flush) resolved without them. The drain now clears the pending
promise in the same tick its loop exits, on success and failure, so a later
write() always starts a new drain.
* test(notifications): flush dismissal writes before deleting temp state dirs
Dismissal history now saves asynchronously, and the writer recreates its
directory before each write. Tests that deleted their temp dir with a write
still pending left recreated dirs behind in the system temp folder and could
race the delete on Windows. They now await the controller's flush first.
* perf(notifications): close dismissal history at committed exits
The shutdown flush only waited for writes already queued. A notification
recorded after it (orcad cleanups still running, or the 150 ms before a
renderer relaunch calls app.exit) started an async write the exit dropped,
so the phone could keep an alert the desktop could no longer withdraw.
The store's flush() becomes close(): it waits for the pending write, and
every later record saves synchronously through the snapshot writer's
existing writeSync (plain fs, no icacls; a failure only warns). It is called
only where the exit can no longer be vetoed: the will-quit teardown, a new
exit-only hook that app:relaunch runs before app.exit(0) (restart and
profile switches share onBeforeRelaunch but quit through will-quit), and
orcad's cleanup after RPC stops.
The quit test now holds chat teardown and RPC stop and proves the close
starts only after both settle, in either order. The orcad push-startup
runtime mock gains the method the entry now calls.
* fix(notifications): bound the dismissal close before a renderer relaunch
app:relaunch waited on the dismissal store's close with no limit, so a
stalled userData disk left Restart stuck on "Restarting…" with a half-quit
app saving synchronously. The close now races a 2 s cap through the
existing settleWithinMs helper, the same cap the restart path's auth
preservation uses, and logs when it gives up.
* test(notifications): keep async dismissal writes under test between reads
Mid-test disk reads used closeDismissals() as a barrier, which switched
every later record to the exit-only synchronous path. The store gains a
non-closing flush() (close() = mark closed + flush), exposed on the
controller as flushDismissals for tests; mid-test barriers use it, and
close stays only where a test simulates an exit or teardown. Also notes why
the writer's pendingWrite assignment is safe, renames a store test to what
it covers, and groups the orcad cleanup-order test under a describe.
* fix(browser): end a page's mount admission whenever it leaves the store
A background-opened page's start admission was released only by the two
close actions, so a bulk worktree purge (listing refresh, repo or folder
removal) dropped the page while its admission stayed in the set. Release it
in the one cleanup every page removal already runs through
(destroyRemovedBrowserWebview), and drop the per-path copies.
STA-7436
* refactor(browser): one removed-page sweep shared by the app and its test
Moves the store sweep that destroys removed pages into
destroyWebviewsRemovedFromStore so the admission regression test runs the
same code the app runs, and adds an eviction case.
* test(browser): assert pages were admitted before the eviction case
CodeBuddy spawns hooks registered with shell: 'powershell' as a bare
`pwsh`, so on Windows without PowerShell 7 every hook failed with
spawn pwsh ENOENT and the failed UserPromptSubmit hook blocked prompts.
Register CodeBuddy's hooks the way Claude's are: the managed .cmd path
on cmd-safe profiles (runs under CodeBuddy's Git Bash or Windows
PowerShell default shell) and the absolute powershell.exe encoded
launcher otherwise. The next install rewrites existing registrations.
Fixes#27182
Co-authored-by: Orca Worker <orca-worker@localhost>
* fix(daemon): stop giving new terminals to a daemon whose AppImage files are gone
On Linux AppImage, a terminal daemon can outlive the mount it runs from and
keep answering health checks until it SIGBUSes. Orca now reads the daemon's
/proc exe link and mount table at adoption: an idle severed daemon is
replaced; a busy one is kept for its sessions but quarantined, so fresh
terminals use the local provider and a healthy RPC never re-admits them.
Status and a verified restart are available over RPC and as
`orca terminal-daemon status|restart`.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(daemon): lift the severed quarantine once a new daemon owns the endpoint
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(daemon): a severed-daemon restart counts fallback terminals and never stops a replacement
The restart re-checks the authenticated endpoint owner before any teardown
and again at shutdown, and never falls back to a pid kill when targeted.
Status counts the local fallback terminals the restart also ends.
Co-Authored-By: Claude <noreply@anthropic.com>
* chore(daemon): merge duplicate import
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(runtime): classify the terminalDaemon RPC namespace as host-owned
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
Direct AppImage serve now unpacks its stable copy once, under one overall
deadline that also bounds the registration lock wait and both extraction
attempts, prints progress to stderr, and refuses fast when the private
cache is not writable, not executable, or too full.
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
* feat(serve): run the --supervise supervisor from a copy outside the app
* fix(serve): record supervisor copy users beside the copies so a cleanup never races a fresh copy
* fix(serve): remove a partial supervisor copy when copying fails
* test(serve): skip the unreadable-copy case where mode 000 does not block reads
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(updater): clear a cancelled macOS install request before staging the next update
* fix(updater): match only Orca's ShipIt and recheck owners right before clearing
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(linux): keep extracted AppImage copies that something still runs from
New builds extract into a namespace that pre-lease builds never prune.
Pruners now delete a generation only when no same-user process runs from
it and no lease names a live process (pid, start ticks, boot id).
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(linux): skip non-dumpable processes in the AppImage residency scan
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(serve): explain what to run when an SSH host's orca cannot start or install a server
* fix(serve): keep an app launcher on PATH usable from a server terminal; share the app-CLI check
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(serve): save Codex auth before a headless serve update restart
* refactor(updater): keep the update-restart step in its own module so auth mocks still apply
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(ssh): never start a hidden orcad shell for a pane the legacy relay bridge serves
* fix(ssh): a skipped bridged activation still supersedes an earlier tab's pending focus
* fix(ssh): a click on a whole mirrored tab never activates its bridged pane on orcad
* fix(ssh): load the bridge pane list lazily from tab activation
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(ssh): a bridged pane reattaches after a disconnect instead of going deaf
* fix(ssh): a bridged pane asks again when it turns live during an earlier ask
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Publish Linux Wayland text copies with an independent clipboard owner, then clear Chromium's stale source only after publication succeeds. Use data control where available and GTK's clipboard bridge on GNOME. Keep macOS, Windows and X11 on their existing Electron writers.
Temporary workaround for https://github.com/electron/electron/issues/54645, with removal checks documented in the native README.
Verified full build, macOS arm64 packaging, Linux x64/arm64 library floors, clipboard/platform tests, real Wayland copy/paste and CI packaging.
Fixes#26917Fixes#23229
* wip: legacy relay bridge client
* Revert "fix(ssh): end an older Orca's relay terminals from the connect refusal (STA-10166) (#27748)"
This reverts commit d4a55f583e.
* feat(ssh): bridge terminals an older relay still runs into their old tabs
* test(ssh): cover the legacy relay bridge client, target bridge, transport and gate
* test(ssh): type the bridge test fakes without assertions
* test(e2e): a 1.4.224 relay's live terminal survives the upgrade on a Docker SSH host
* ci(e2e): route legacy relay bridge changes to its Docker spec
* fix(ssh): bridge delta moves too, and never read a non-exhaustive relay listing as exit
* fix(ssh): keep retrying a bridge for panes that mount before connect; end panes proven gone
* fix(ssh): bridge 1.4.224's detached leases through export preflight; publish every leased pane before the first look
* test(e2e): build the released relay out of process so Playwright never loads ESM build tooling
* fix(renderer): drop the recovery hook main removed from PtyTransport
* test(e2e): show the converted worktree before reading the bridged tab
* test(e2e): reconnect and wait for the converted host before opening the bridged tab
* fix(renderer): match bridged leases to tabs mirrored from the managed server
* test(e2e): find the mirrored relay-era tab under any worktree key
* test(e2e): open the converted worktree before looking for its mirrored tab
* test(e2e): a retired lease is terminated or pruned
* fix(ssh): back off attaches a listed terminal refuses; one pane per bridged shell
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(native-chat): show a finished resume as a result, not a picker
Once a resume starts and no chat is left to choose, the dialog drops the
Select all row and the checkbox column, so each chat's check reads as its
status instead of a ticked box. The Don't-ask-again box only shows while
choosing; a run started by the launch's auto-resume says so and links to
the setting. The finished description is past tense.
* fix(i18n): keep Agent untranslated in the ja resume copy
* fix(native-chat): keep resume results actionable and keyboard accessible
* Fix hidden resume preference replay after run starts
* Consume resume picker preference after applying it
* fix(native-chat): keep Don't ask again after Resume while auto-resume is off
The box now shows on the result view too whenever the setting is off and the
user started the resume, and saves only while it is visible, so a hidden tick
still never replays. Automatic launches keep their 'Resumed automatically'
note instead.
In shells that send no command-finished mark (cmd.exe, Git Bash), quitting
Claude or Codex left the pane's live agent row behind: the pane's process
monitor confirmed the exit, but only the OSC 133;D path ever dropped the row.
The monitor's confirmed exit now arms the same drop and main-side reconcile
pair a confirmed 133;D uses, settled behind the existing host-verifiability
gate: a pane whose owner process the host can verify keeps the host's
decision; every other pane drops the row and keeps its resume identity.
Skipped when a 133;D drop is already armed, for remote panes, and when the
row belongs to another agent.