Commit Graph
16002 Commits
Author SHA1 Message Date
Brennan Benson 8a3fefd026 Keep the saved model when reopening ACP chats (#38815)
* Preserve native model choice when restoring ACP chats

* Forward startup observations through the OpenCode connection

* Use fixed time in the connection regression fixture

* Complete the model-command fixture session identity
2026-11-06 10:35:12 -08:00
Brennan Benson 7033f778e8 Keep native chat responsive after reading older messages (#38920)
* Bound native chat history after returning to latest

* Keep reader return evidence aligned with measured content

* Correct transcript regression fixtures and type annotations

* Preserve reader gestures through smaller row measurements

* Keep transcript regression tests focused on behavior

* Pin transcript passthrough and send admission expectations

* Preserve source correspondence in transcript projection reuse

* Keep reader intent separate from observed scroll geometry

* Preserve pending reader movement through no-op pins
2026-11-06 10:20:16 -08:00
Kelvin Amoaba 7fc3ca44e5 fix(dictation): keep the last word when you stop dictating (#39109)
* fix(dictation): keep the last word when dictation stops

Streaming models got no trailing audio before inputFinished, so the
encoder never saw the lookahead frames it needs to emit the final word.
Pad 0.66 s of silence at stop, as sherpa-onnx's own decoders do.

The renderer also tore the mic down the instant stop was pressed,
dropping the unread processor buffer. Keep capturing 250 ms after a
user stop before closing the stream.

* refactor(dictation): let the stop tail rely on the capture generation check

* refactor(dictation): keep the capture hook's stop-tail import on one line

* fix(dictation): leave a session an error tore down during the stop tail
2026-11-06 18:19:28 +00:00
Kelvin Amoaba 25e6bc4be3 perf(runtime): cache OMP and Grok model discovery (#38977)
* perf(runtime): cache OMP and Grok model discovery

* perf(runtime): share a slow model probe for its whole timeout

* refactor(runtime): key and probe model discovery from one input
2026-11-06 18:18:53 +00:00
Kelvin Amoaba fb2277e72f fix(speech): post a streaming partial only when its text changes (#39108)
The streaming worker posted the current hypothesis after every 100 ms feed,
so desktop IPC and the phone's text version churned about ten times a second
with unchanged text. A 5 s clip now sends 11 partials instead of 42.
2026-11-06 18:18:34 +00:00
Kelvin Amoaba fff906dc65 refactor(agent-session): share the model-snapshot rules with the phone (#39023)
* refactor(agent-session): share the model-snapshot rules with the phone

Move the pure snapshot selection and record rules, the account settings that make held lists stale, and the saved-only preload decision into src/shared so the phone can seed its model picker from the same rules. The renderer keeps its per-host store and sync.

* refactor(agent-session): tidy the shared snapshot rules
2026-11-06 18:18:20 +00:00
Kelvin Amoaba a0da1caafb perf(runtime): stat the device registry at most once a second (#38979)
* perf(runtime): stat the device registry at most once a second

* test(runtime): restore real timers after each revocation test

* test(runtime): drop the registry read-count test

* refactor(runtime): trim the registry throttle comments

* fix(runtime): pairing and delegation planning read the device file unthrottled
2026-11-06 18:16:45 +00:00
Vyacheslav ScherbininandKelvin Amoaba b67fbb42ee feat(emulator): remember the last successful device (#36558)
* fix: restore last successful emulator selection

* fix: preserve successful emulator attach when preference storage fails

* Share emulator recency metadata and valid-selection guard

* Align PR 36558 with current checked types and component contracts

* fix(i18n): remove keys from replaced controls

* fix(emulator): preserve workspace and CLI device choices

* ci: refresh emulator checks after main repairs

---------

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-06 18:14:12 +00:00
Brennan Benson 4cccf7b04a perf(native-chat): reuse unchanged phone transcript rows (#38956)
* perf(native-chat): reuse unchanged phone transcript rows

* fix(native-chat): narrow retained projection output reuse

* refactor(native-chat): isolate phone session status type

* Document source comparison key safety

* Use last-member array accessor

* test(native-chat): check phone captures across source boundaries

* ci(mobile): route recorded transcript decoder inputs
2026-11-06 10:14:07 -08:00
Kelvin Amoaba 6239efb34a perf(agent-session): stream chat text as appends (#39051)
* perf(agent-session): stream chat text as appends

* perf(mobile): read text-append support from the connection's status answer

* perf(mobile): open the chat stream once the host's append support is known

* refactor(mobile): seed text-append support from the shell's status

* fix(agent-session): stop asking for appends after one is refused
2026-11-06 18:12:19 +00:00
Kelvin Amoaba ef3d9bbdc2 perf(mobile): seal and decode socket frames through native bindings (#39053)
* perf(mobile): seal and decode socket frames through native bindings

* refactor(mobile): decode base64 socket messages with the canonical primitive

* refactor(mobile): share the canonical base64 decoder with socket payloads
2026-11-06 18:11:41 +00:00
Kelvin Amoaba 586d214eda fix(speech): stop streaming dictation dropping its first second (#39107)
A new streaming stream starts with empty encoder caches, and zipformer
en-20M then drops most of the first second of speech: the first word was
wrong on 71 of 73 LibriSpeech clips. Each session's stream now takes a
second of silence before its first audio. That cuts per-utterance WER from
27.0% to 16.1%. The priming runs on the first feed so stop never waits for it.
2026-11-06 18:09:02 +00:00
Kelvin Amoaba 60ced6e411 perf(speech): decode offline dictation on up to 4 threads (#39104)
Offline models decode the whole utterance after stop, so their thread count
is the user's wait. On the shipped sherpa-onnx, 4 threads cut a 5 s
utterance from about 190 ms to 122 ms with the same transcript and memory.

The recognizer configs move from stt-worker.ts into stt-worker-model-config.ts
so the worker has room under its line cap.
2026-11-06 17:59:19 +00:00
Vyacheslav ScherbininandKelvin Amoaba 1cdf9b8022 feat(emulator): group and search the device picker (#36548)
* fix(emulator): distinguish used and unavailable devices in picker

* fix(emulator): retain older usage timestamps and English review fixtures

* Share emulator recency metadata and valid-selection guard

* Align PR 36548 with current checked types and component contracts

* fix(i18n): remove keys from replaced controls

* fix(emulator): reset picker search and keep retries usable

Clear the picker search after choosing a device and keep Connect available
when the host should choose its default device on a retry.

* ci: refresh emulator picker checks after main repairs

---------

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-06 17:55:32 +00:00
Naveen Chatlapalli eed02e976f fix(test): follow the terminal RPC module move (#39097) 2026-11-06 17:40:35 +00:00
Kelvin Amoaba 02afb4edab perf(runtime): bound phone terminal output with end-to-end credit (#38983)
terminal.subscribe accepts capabilities.outputAck and echoes outputAck with a device-bound streamToken. Output beyond a fixed 512 Ki-unit window waits for terminal.ackOutput; held output past the pending budget is replaced by one recovery snapshot once the link drains.
2026-11-06 16:56:53 +00:00
Kelvin Amoaba 8ab385187e feat(mobile): let the host page open a target inside its own document (#39062)
* feat(mobile): let the host page open a target inside its own document

* fix(mobile): skip a duplicate open, re-render the stack only for a waiting route

* fix(mobile): resolve the open target through Expo and open a target that changed before mount

* test(mobile): keep Back in one open-target case

* refactor(mobile): hold routes under the top with one released flag

* refactor(mobile): own the navigator ready listener in the effect

* fix(mobile): leave single-route stacks drawn from the start
2026-11-06 16:55:45 +00:00
Neil 0314da7ed8 refactor(main): group RPC and orchestration modules by owner (#38813) 2026-11-06 08:21:33 -08:00
NeilandClaude a6c78e3de3 fix(terminal): report focus-out to ?1004 programs when their last viewer leaves (#38730)
* fix(terminal): report focus-out to ?1004 programs when their last viewer leaves

A viewer that vanishes (laptop sleep, dropped link, app quit) never sends the
ESC [ O its xterm would send on blur, so programs that armed focus reporting
kept believing they were focused. The daemon now remembers the last focus
report each PTY heard and sends one ESC [ O when its last client detaches.
orcad asks it to when the last device that viewed a PTY disconnects; a
resubscribe from a still-connected device is not a departure. A daemon that
predates the request rejects it, and the adapter stops asking that client.

Fixes #28350

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

* fix(terminal): keep the daemon router under its line cap and justify focus-loss test casts

Refs #28350

The subscribe-buffer test now expects the viewer device argument.

* fix(terminal): let a hidden remaining pane yield focus-out on device departure

Refs #28350

Build on main's pane-visibility signal: a remaining remote view whose
pane is hidden already blurred, so it no longer holds back the focus-out
for a departed device. Views that cannot report a hidden pane still do.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-06 06:58:11 -08:00
Neil 0f6448adf6 fix(remote): let Remove drop a managed server saved under two SSH aliases (#39048)
* fix(remote): let Remove drop a managed server saved under two SSH aliases

When one managed SSH server was saved twice, Settings said to remove one
entry, but Remove refused any pairing that has a managed deployment. When
another saved entry still manages the same server (same host key), Remove now
unlinks this entry through the managed forget path: it closes this entry's
tunnel and releases its SSH claims without contacting the host, so the server,
its projects and live terminals stay with the other entry. A managed server
with no other managing entry keeps the guard.

Fixes #38604

* fix(remote): recheck the managed twin inside the lifecycle queue

Codex review: a Remove queued behind another lifecycle action could forget
the last managed entry if the twin was removed meanwhile, and the twin match
compared raw Base64 text. Removal now goes through a forgetRedundant action
that re-finds the twin inside the target's lifecycle queue and unlinks in the
same tick, and matches host keys with the canonical matcher.

Refs #38604

* test: cover redundant managed removal after the dispatch suite was removed

Refs #38604
2026-11-06 06:53:14 -08:00
Neilandggbdpq a53c00219d fix(workspace): wake a sleeping recipe VM when a client opens its workspace (#39038)
A recipe VM workspace runs on its VM's own Orca server, so a phone, the CLI or
the desktop reaching it while the VM slept dialed a server that was not there
and failed. Only the host window's click ran the recipe's resume.

- The VM wake owner (WorkspaceSleepCoordinator) tracks the VM operations it
  was asked for and answers where the VM stands: a pending wake or a running
  suspend outranks the stored record.
- The host holds a request that opens the workspace (worktree.activate,
  defined once in shared code) until the VM is up: three minutes for the
  desktop and the CLI, and for a phone only as long as still lets the relay
  connect inside the phone's 30-second deadline. A VM still up keeps a sleep
  on its way from suspending it; a VM asleep or suspending wakes after what is
  running, joining a wake already pending. The open answers waking only when
  the hold runs out, wake_failed when the wake fails, and asleep when a Sleep
  asked for while it was held won; that open then wakes nothing more. Other
  requests answer waking or asleep instead of dialing nothing. A Sleep asked
  for during a wake keeps its place in the lifecycle queue and starts once the
  wake is done. A caller that gives up stops waiting and frees its queue
  place; the wake goes on.
- An open of a server the user disconnected is refused before anything wakes
  its VM, and cancelled if they disconnect while it waits. A wake carries the
  SSH host's user hold from when it was accepted and every Disconnect since,
  so its connect never lifts a hold; the VM still resumes and its new SSH
  address is saved for the user's own Connect.
- A wake re-pairs a paired VM's server. The request that waited for it is
  held to its caller's pairing as of before the wake (a stale caller is
  refused before it can wake anything), then to exactly the pairing revision
  the resume produced (including a re-pair its SSH reconnect made), whatever
  process now serves it; any other change still
  refuses it. A phone link still preparing for an open survives only the
  re-pair its own wake made; any other retirement, or a user Disconnect, ends
  it, and a link preparing for anything else is retired at once.
- The hold lives in the desktop's environment preparation, which its own calls
  and subscriptions, the phone relay and the CLI all go through. A phone open
  on a link to a server whose VM is not up gets a fresh link; the old link's
  requests are answered waking or asleep. Further opens join a link already
  waiting on the wake. A phone link's whole open (server preparation with any
  hold, grant sync and the socket) shares one deadline inside the phone's
  own, for every server.
- The CLI dials a VM directly, so `orca worktree activate` asks the desktop
  (runtimeEnvironment.wakeRecipeVm) to open the VM and reads the server's
  pairing again; the refusals (including runtime_manually_disconnected) keep
  their codes over RPC, and a host without recipe VMs answers that one RPC
  with method_not_found. Hosts that do this advertise
  host.recipe-vm-wake.v1.
- A workspace this host runs on a recipe VM over its SSH target wakes on this
  host's own activation, through the resume's explicit connect
  (wakeWorkspaceVm -> resumeVm -> connectRuntimeOwnedSshTarget). That connect
  runs whenever the VM is in SSH mode, even when its recipe had no resume step,
  since suspend tore down the transport.

Fixes #36923

Co-authored-by: ggbdpq <ggbdpq@gmail.com>
2026-11-06 06:51:09 -08:00
Neil 6e7f39ba55 fix(terminal): read lines that end in CR CR LF instead of returning them empty (#38838)
* fix(terminal): end tail lines at CR CR LF instead of reading them as empty

Raw-mode programs end lines with CR CR LF. The output normalizer folded only
the CR directly before LF, so the leftover CR made each completed line read
as the empty text after it, and terminal.read returned blank lines. A run of
CRs lands on the same column as one CR, so treat the whole run before LF as
part of the line ending.

Fixes #38667

* chore: shorten the CR run comment

Refs #38667

* fix(terminal): defer CR until kept output decides it

A CR followed only by discarded escapes and then LF (text CR SGR CR LF)
still read as an empty line, and a restore cut landing just before CR CR LF
seeded a blank first row. The normalizer now holds each CR until the next
kept output: before LF it ends the line, and runs or escape-split CRs act as
one. The restore anchor skips a whole CR-run line ending.

Refs #38667

* test: size the restore-cut case from the exported seed limit

Refs #38667
2026-11-06 06:43:14 -08:00
Neil c5383afb6c Open a granted host file the way a workspace file opens (#38742)
* fix(files): open a granted host file the way a workspace file opens

A plain click on a host file outside every workspace opened it in the
editor through the grant's whole-file text read, so a video, a large
image or any text past 512 KiB failed with "too large to open from its
host". Granted files now go through the same reads as workspace files:
media streams to its viewer by range, images and PDFs page past one
reply, and large text pages into the editor. A host file is addressed
either by workspace path or by its grant, and the host gains
files.statTerminalArtifact to pair with files.readTerminalArtifactChunk.
The host names a large binary as binary before refusing its size, as
files.read does, and a streaming player renews a lapsed grant the way
the editor's reads do. Hosts without these methods answer
method_not_found, which the paged reads already turn into an
update-the-host message.

Refs #35968

# Conflicts:
#	src/main/media/media-preview-protocol.test.ts

* fix(files): page a granted binary with the grant that served it

A content read renews a lapsed grant without telling the tab, so a bytes
editor paged the file with the dead grant and failed on every reload. The
read now reports the grant that served it; the bytes editor uses that grant
and the tab keeps it, as a save that renews one already does.

Refs #35968

* fix(files): renew a granted preview on every host call and keep unsavable grants read-only

Review: renewal ran only before a media response returned, so a grant that
lapsed under a streamed chunk or a pdf.js range read over IPC failed instead.
Every host call for a remote preview now goes through one helper that renews a
lapsed grant once. A grant saves only text within one write, so a granted
binary or text past 512 KB now opens read-only instead of failing to save.

Refs #35968

* fix(editor): show an unsavable granted copy read-only in every view

Review: read-only reached only the edit surface, so a large granted notebook
or markdown file still opened its editable rich view. The editor panel now
treats the copy as a read-only tab, so rich views fall back to the raw source
view as they do for other read-only tabs. A renewed read-only grant also reads
as read-only and stays off the tab.

Refs #35968

* fix(files): renew a granted bytes read whole and never splice versions mid-stream

Review: a bytes editor opened after its grant expired kept failing, since its
paged read never renewed. It now renews and restarts the whole read. A stream
or PDF range that renews mid-read now continues only if the renewed grant sees
the same file version, so a rewritten file can't splice into one response.

Refs #35968

# Conflicts:
#	src/main/media/media-preview-protocol.test.ts

* fix(files): keep each preview response on its own grant and pin a bytes read's pairing

Review: a stream crossed to a grant another request had renewed without
checking the file version, and a bytes read that renewed during a re-pair
could read from the new host. Each response now keeps the grant it started
on and moves to a renewed one only after checking the version it serves, and
a bytes read pins its pairing before the first call.

Refs #35968

* fix(files): bind a preview's stat to the grant that saw it

Review: a stat cached from the old grant could start a response on a grant
another request had just renewed onto a rewritten file. The cache now keeps
each stat with the address that produced it, a response starts on that
address, and a renewal checks the version by stating the renewed grant
itself.

Refs #35968

* fix(files): keep a pinned read's pairing on its paged address

Review: the paged-read address dropped a caller's pinned pairing, so a re-pair
between a grant renewal and its retry could send the retry to the new host.
The address now carries the pinned revision.

Refs #35968

* test(e2e): give the granted video spec's timeout its reason

Refs #35968

* test(media): keep the granted preview renewal tests in their own file

Main trimmed media-preview-protocol.test.ts to local previews, so the tests
for renewing a granted preview's lapsed grant (burst, streamed chunk, rewritten
file, PDF range over IPC) now live in media-preview-protocol-granted.test.ts.

Refs #35968
2026-11-06 06:36:41 -08:00
NeilandFlorin Timbuc 239e17f84f feat(cli): add orca host rm, and clean up after every SSH target removal in main (#38774)
* feat(cli): add orca host rm for saved SSH targets

orca host rm --host ssh:<target-id> calls the runtime RPC ssh.removeTarget,
the same removeSshTarget the Settings UI reaches, so the tombstone keeps the
passive ~/.ssh/config import from adding the alias back.

Also document how the import treats Match blocks and Include files.

* fix(ssh): clear a removed target's live state and rename in main for every removal path

Removing an SSH target through ssh.removeTarget (orca host rm, paired clients)
left open tabs with stale reconnect state and kept the host's rename, because
only the Settings dialogs cleaned up after their own IPC call. The shared
removal mutation now drops the display-label override and tells the renderer,
which clears the target's state once for all callers.

Fixes #37949

* fix(ssh): clean up after a removed target in the teardown every removal ends in

A recipe VM's cleanup removes its SSH target through removeRegisteredSshTarget
directly, so the window kept that target's live state. The broadcast and the
rename drop now run there, after the target is gone.

Fixes #37949

* test(cli): keep host rm's retarget-flag refusal next to its other cases

Its old file was removed on main.

Fixes #37949

* refactor(cli): give the host, managed-server and VM handler groups their own module

Keeps the handler manifest under the file-length limit.

Fixes #37949

---------

Co-authored-by: Florin Timbuc <florin@sowild.design>
2026-11-06 06:23:46 -08:00
Neil 26741256dc fix(sidebar): keep the selected card's host pill outline visible in dim themes (#39054)
* fix(sidebar): keep the selected card's host pill outline visible in dim themes

#38841 tinted the host pill outline 40% of the sidebar text into the
sidebar. A theme whose sidebar text is only 4.5:1 leaves that outline at
about 1.6:1 against the selected wash, which matches the pill's fill, so
the pill loses its shape.

When an app theme is painted, its colors are now concrete, so compute
once per theme the smallest mix (from 40%) that gives the outline 2:1
against the selected wash, the same floor focus rings use, and paint it
as --worktree-host-pill-outline-mix. main.css falls back to 40%, so
Orca's built-in themes and any theme bright enough keep today's look.
The tinted sidebar resets it, like the row wash. The wash's CSS fallback
joins the fallback table so the check (and the token reference) read
what main.css paints.

Refs #38641

* fix(sidebar): floor the host pill outline in the tinted left sidebar too

The tinted sidebar paints the app's text on its tint mixed into the app
background, so the theme's measured outline mix doesn't apply there and
it fell back to 40%: a dim theme (#919191 on #2a2a2a) at the default 8%
tint left the outline at 1.6:1 on the selected wash. Measure the tinted
colors once whenever the app theme or tint settings change, paint the
result as a root variable, and have the tinted sidebar read it. Share the
measurement (hostPillOutlineMixOver) with the theme path, and make its
comment name the actual invariant instead of the pill label's contrast.

Refs #38641
2026-11-06 06:06:50 -08:00
Neil 1c950baf33 refactor(main): group session lifecycle and journal modules by owner (#38638)
* 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
2026-11-06 06:00:08 -08:00
Neil 55697aec91 fix(cli): keep remote read-only commands working when environment usage can't be recorded (#39052)
* fix(cli): keep read-only commands working when environment usage can't be recorded

The CLI recorded each environment's last-used time with a strict store write,
so a profile whose directory refuses chmod (EPERM) failed commands whose reply
had already arrived. The desktop already skipped such failures in one path.
Move that best-effort recorder to shared code and route every desktop and CLI
usage write through it.

Fixes #38673

* test: assert the usage write failure, not call counts

Refs #38673

* test: skip the chmod EPERM case on Windows and drop a cast

Refs #38673
2026-11-06 05:53:10 -08:00
3704d01615 fix(terminal): a host with a window keeps a pane a paired client moved to a new tab (#38840)
* fix(terminal): a host with a window keeps a pane a client moved to a new tab

Dragging a split pane onto the tab strip in a paired worktree whose host runs
the desktop app with a window flickered and snapped back. The client draws the
new tab and calls session.tabs.movePaneToNewTab, but the host answered
not_held whenever it had a window, because the window owns the layout and only
headless hosts committed the move. The client reverted, and even without the
revert the host window's next snapshot would have rebuilt the old tab.

Main now asks its window to make the move and waits for the answer, the way
other client requests reach a window-owned host. The window moves the pane
with its own drag-out path under the tab id the client already drew, at the
client's drop position, mounting a parked source tab first. A pane the window
cannot reach is refused, so the client puts it back; a host without the
window bridge still answers not_held. Older clients and hosts are unaffected.

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

* fix(web): rely on the optional pane-move bridge instead of no-op stubs

The web client never receives host pane-move requests, and the bridge
already skips registration when these members are absent; dropping the
stubs keeps web-ui-api.ts under the line limit after rebasing on main.

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

* test(e2e): a window host keeps a pane a paired client moved to a new tab

The window-host case asserted the old refusal, where the client drew the new
tab and put the pane back. It now drags a pane out on a paired client and
checks the pane stays in the tab the client drew: the host window holds the
leaf alone under that tab id, no snapshot pulls it back, and the source keeps
one pane. It fails on main, where the tab count falls back after the drop.

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

* fix(terminal): refuse a pane move the host window cannot answer

When a paired client asks a host with a window to move a pane to a new
tab, the host relays the request to that window. If the window is gone,
closes, or does not reply within 10 seconds, the relay rejects. The
host returned that rejection as an RPC error instead of a move result.

The host now answers { status: 'refused', reason: 'pane_unavailable' }
in that case. The client already puts the pane back on a refusal, and
this is the reply the relay timeout was sized for.

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

* fix(terminal): fence a window's pane move once the client heard it timed out

The relay told the client "refused" after 10s but left the window's move
running, so a late commit moved the pane on the host after the client had
put it back. Main now fences the commit: a move the client was told timed
out commits nothing, and a commit answers the client at once, before the
deadline can turn it into a refusal.

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

* fix(terminal): keep the pane-move fence Electron-free and let a claimed commit answer

The leaf-move IPC is shared with the plain-Node runtime, so the fence moves to its own
Electron-free module. A window that goes away mid-commit no longer refuses a move its commit
landed, and a second move cannot replace a fence a timed-out move still holds.

Fixes #37082

* chore(terminal): follow moved shared modules after rebasing the pane-move relay

Refs #37082

* fix(terminal): hold a client's pane move's source tab mounted until the move settles

The timed split-mount lease could expire while the window was still moving the
pane, so cold parking could unmount the source tab after main had already told
the client the move landed. The move now holds the tab mounted for its whole
duration and releases it when it settles.

Fixes #37082

* fix(terminal): don't toast the host window for a pane move another client asked for

Fixes #37082

* fix(terminal): relay a client's pane move only to a window that owns the layout

Where main owns the layout (the dark ORCA_LAYOUT_OWNER=main path), the window's
move goes back through main, which names its own tab, so the client's tab id
could not be kept. Main answers not_held there, as before this change.

Fixes #37082

* fix(terminal): name the renderer a relayed pane move belongs to

Fixes #37082

---------

Co-authored-by: Seunghwan <hyunsh.cycle@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 05:30:17 -08:00
Neil 6316943a6e feat(antigravity): report last-known quota for every saved account (#39005)
* feat(antigravity): report last-known quota for every saved account

agy has one shared login, so Orca never switches it to read another
account. Each successful usage read is now verified against the native
login before and after the fetch, attributed to the matching saved
account, and kept in the vault per runtime with its original read time.
A login change mid-read discards the reading. Settings, the status bar
usage popover, and orca account list --json show each saved account's
last reading, or unknown when it was never read.

Fixes #31664
STA-11623

* chore(antigravity): add saved-account quota copy and status bar fixture

* fix(antigravity): clear the sign-in hold when agy signs in again to the same account

Comparing logins by Google subject meant a fresh sign-in to the same
account never reported a credential change, so the meter stayed on
"Sign in with agy". A new refresh token now counts as a change; agy's own
token refresh keeps the refresh token and still doesn't.

* test(antigravity): attribute a WSL runtime's reading to that runtime's saved accounts
2026-11-06 05:04:34 -08:00
Neil 71420af173 fix(antigravity): write workspace trust inside the WSL distro an agent runs in (#38981)
Antigravity launched in a WSL guest reads ~/.gemini/antigravity-cli/settings.json
inside the distro, but trust preflight skipped every non-Claude WSL launch, so
first-launch agy workers stopped at the trust menu. Write the guest's Linux
path into the distro home's settings (through its cached UNC home), and record
ownership under the host workspace path so worktree removal prunes it.

STA-8154 (CR-P2-002)
2026-11-06 05:04:30 -08:00
mmarabelandNeil 5178068674 fix(terminal): keep per-pane zoom when a terminal pane remounts (#8588)
Per-pane terminal zoom lived in a ref owned by a single TerminalPane mount
and keyed by the runtime pane id, so any remount (hidden-tab parking, the
former Agents view) dropped it back to the global font size.

Store the override in a session-only map keyed by the durable leaf id and
make it the only per-pane font size store. The zoom handler and the
appearance pass read it by leaf id, and it is cleared on a real pane close
or when the tab is gone at mount cleanup, so detached/moved leaves keep
their zoom.

Co-authored-by: Neil <neil@stably.ai>
2026-11-06 05:01:13 -08:00
Brennan Benson 3a6bb347b5 Apply supported read-only controls to hidden text helpers (#38558)
* Keep background text generation read-only

* Align runtime MIME dependency in frozen lockfile

* Bundle MIME lookup data for background text tasks

* Preserve text task cancellation and process ownership during policy probes

* Update streamed generation fixture for permission probe

* fix: preserve naming recipes with compatible restrictions

* test: align naming contracts with single-attempt behavior

* fix: preserve older OpenCode helper restrictions

* test: complete OpenCode capability fixtures

* Separate supported read-only controls from naming retries
2026-11-06 04:15:29 -08:00
Brennan Benson 1ee8df1cb3 Show machine, workspace and branch below the chat composer (#38253)
* Show workspace and branch identity below the chat composer

* Use chat context for composer workspace identity

* Read composer hosts from unified tab records

* Keep composer behavior fixtures focused on input flows

* fix: reuse guarded workspace reader in chat footer

* fix(chat): preserve folder identity and composer focus ring

* Show machine and workspace identity below the chat composer

* Avoid unrelated host focus invalidating composer identity

* Correct composer context and runtime fixture types

* Isolate machine-name lookup in composer flow fixtures

* Simplify composer submission bindings

* Keep the composer session options result together

* Match the composer machine icon to the sidebar

* Keep composer types and session options in their owning modules
2026-11-06 04:05:44 -08:00
Brennan BensonandJurassic Knights b039d4fe5b Keep published active tabs aligned with current selection (#38844)
Paired clients now publish the chat actually selected on the desktop as the active tab; saved layouts, CLI-created chats, closes and restarts keep it consistent. Carries community PR #34526.

Co-authored-by: Jurassic Knights <jurassicknights@gmail.com>
2026-11-06 04:00:10 -08:00
566b52ec68 fix(antigravity): read quota from the selected WSL runtime (#38985)
When Orca's local-account runtime is WSL on Windows, the Antigravity usage
meter still ran the host `agy`, so it showed the wrong account or "not
installed". Route the probe through runWslProcess for the selected distro
(never booting a stopped one), key the unsupported-CLI latch per runtime,
tag readings with the runtime as provenance, and drop another runtime's
last reading instead of keeping it as stale. Settings changes retarget
the fetcher and reread usage.

A WSL target without a distro now names the default through
resolveWslDefaultDistro (registry DefaultDistribution) rather than
listWslDistrosAsync()[0], which skips docker-desktop, falls back to
registry order, and caches the list for the process lifetime.

Ported from #31542 onto current main: reuses main's antigravity fetch
generation (#34165) instead of adding a second one, follows moved WSL and
provider-account-services paths, and keeps only the tests that cover the
new behavior.

Co-authored-by: CAVIN <35207870+zzz163519@users.noreply.github.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-11-06 03:32:10 -08:00
Neilandggbdpq a2661f93ff fix(trust): remove only the Codex and agy workspace trust entries Orca created when a worktree is removed (#38969)
* 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>
2026-11-06 03:32:02 -08:00
Neil 95c6a04133 fix(antigravity): switch agy accounts through the Linux Secret Service keyring (#38996)
* fix(antigravity): switch agy accounts through the Linux Secret Service keyring

agy stores its Linux login with go-keyring's Secret Service provider
(service=gemini, username=antigravity, login collection or default alias)
and falls back to its credential file without a session bus, in containers,
SSH and WSL. Add a secret-tool/gdbus adapter that mirrors that collection
choice, refuses locked keyrings with a clear error, and keep the file store
for headless hosts.

Fixes #32818 (STA-11911)

* test(antigravity): avoid casts in Linux keyring adapter test
2026-11-06 03:31:58 -08:00
Neil cd0111fa1e fix(terminal): paste copied files as full paths instead of their names (#38970)
Fixes #28869
2026-11-06 03:24:00 -08:00
3e122692a3 fix(editor): avoid Unicode punctuation warnings in Markdown (#33861)
Share one language-aware unicodeHighlight builder across the file editor,
the standalone diff viewer, and combined diff sections. Markdown skips
ambiguous-character highlighting so full-width punctuation no longer
triggers the warning banner; invisible-character detection stays on
everywhere. includeStrings is pinned to true because Monaco otherwise
resolves it to false in trusted workspaces.

Fixes #33860

Co-authored-by: QTom <qt@truesightai.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-11-06 03:15:52 -08:00
18e99273d7 fix(acp): keep the ACP run temp folder short enough for unix sockets (#38968)
An ACP agent's TMPDIR sat inside the data folder. On macOS that path made
a socket such as datacloud-mcp-dataAgentKit-antigravityide.sock 131 bytes
long, over the 103-byte limit, so child programs could not bind it.

When a socket under the data-folder root would not fit, use a short
per-user folder in /tmp (mode 0700, owner checked via ensureOwnedPrivateDir,
keyed by a hash of the data root so Orca copies never wipe each other), and
clear the old data root once. Roots that fit, and Windows, keep the current
behavior.

Ported from #33948 onto current main (acp-private-temp now uses
launchPreparationFailure; daemon-socket-endpoint-path moved under
daemon/endpoint/publication; the launch-parity test it touched was removed).

Co-authored-by: Manan Chataut <thefrikidude@thefrikidude.local>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 03:03:56 -08:00
352e69f09c feat(cli): select and remove any saved agent account from orca account (#38944)
* feat(cli): select and remove any saved agent account from `orca account`

`orca account select` and `orca account rm` accepted only OpenCode and
Devin. They now cover every provider with saved accounts — Claude, Codex,
OpenCode, Devin and Antigravity — through the runtime methods the
status-bar switcher and Settings already use (accounts.selectClaude,
selectCodex/selectCodexForTarget, selectData, antigravitySelect and the
matching remove methods). No runtime or wire change.

`--account` takes an id, an email/label (case-insensitive) or `system`.
A name shared by several accounts is refused with their ids rather than
guessing which subscription to bill. `system` is refused for Antigravity
(no system default), for Claude from the WSL bridge (the RPC has no
target and would reset the Windows host), and always for `rm`.

`orca account list` now prints each account's id and a `system` row,
accepts `--agent` for every provider, and adds the Antigravity block when
the host can read it.

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

* test(cli): assert the host refusal for Antigravity accounts from WSL

The host rejects every client-selected WSL target for Antigravity account
management, reads included, so the test now mirrors that refusal and checks
that no select call is made instead of asserting an impossible success path.

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

* test(cli): keep only the account-selection tests that guard this change

Moves the guards into account.test.ts (the handler layer that owns them) and
drops layer-duplicating, copy-pinning, Cartesian and mock-only cases per
AGENTS.md test rules; un-exports resolveAccountId (test-only export).

---------

Co-authored-by: Maxime AUBURTIN <m@hellomax.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 03:03:51 -08:00
Neil f0b71c9368 Fail Antigravity worker readiness fast when agy is signed out (#38901)
* fix(runtime): fail agent readiness fast when the program reports a sign-in block

agy shows its login menu when signed out and reports OSC 7501
state=blocked:kind=auth. tui-idle treated that as pending, so worker-start
waited out its whole readiness timeout and reported a vague
'Agent did not become ready'. Rank a fresh program sign-in block as a
blocked verdict (agent-sign-in-prompt) so worker-start fails at once with
Agent startup blocked: agent-sign-in-prompt and never types the task into
the login menu.

Fixes #37056 (STA-13300)

* fix(runtime): require an agy sign-in block to persist before failing readiness

agy flashes its sign-in splash at startup and then signs itself in (#28348), so a fresh OSC 7501 blocked kind=auth is not yet final. Hold the verdict pending for 3s from the first auth report (repeats keep the first stamp; idle/working supersede it) and recheck when the window ends.
2026-11-06 03:03:46 -08:00
OrcaWinandOrcaWin 28a84e0770 test: remove 507 low-value session and browser tests (#38973)
* test: remove 507 low-value session and browser tests

* fix: keep transcript slot expressions within the line limit

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-06 02:56:28 -08:00
OrcaWinandOrcaWin 0e5c4a6685 test: remove 548 low-value usage and lifecycle tests (#38965)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-06 02:31:54 -08:00
Brennan Benson e8b2b9618c feat(github): filter Tasks PRs by state and by who, like GitLab MRs (#38903)
* feat(github): filter Tasks PRs by state and by who, like GitLab MRs

GitHub PRs in Tasks get the same two chip groups as GitLab MRs: state (Open, Merged, Closed, All) and who (Everyone, Mine, Reviewing). The chips read and write the search query, so labels and typed text survive a click and nothing new is saved. A combination equal to one of the old presets is applied as that preset, so saved views, prefetch and the phone see the same strings as before.

Tracked in STA-14084.

* fix(github): keep right-click on Open saving the open-PRs default view

* fix(github): wrap the PR state and who chips as two groups
2026-11-06 02:28:12 -08:00
Brennan Benson e0e8ecf592 feat(native-chat): show details on link cards for any repo on a known host (#38892)
* feat(native-chat): show details on link cards for any repo on a known host

A chat link card for a pull request or issue in a repository other than the
workspace's own showed only its name, because the lookup answered only for the
workspace's configured remotes. Previews now read any repository on a host those
remotes use, with that host's sign-in, as the common pattern does. Other lookups
keep the exact-repository check, and an unknown host still gets no credentials.

* fix(native-chat): open link cards for other repos on the chat repo's host

The hover card still admitted only the chat repo's own links, so the wider
lookup was never reached. Links to any repo on a host the chat repo uses now
open the GitHub card. When an older host or a repo the sign-in can't read
leaves it unanswered, the card falls back to the web preview, or just the
link's name, as such a link showed before, instead of a failure.

* fix(native-chat): keep another repo's web preview until its details arrive

A link card for another repo showed the GitHub skeleton first and only fell back
once the lookup failed, so older hosts got two loading stages and a change of
shape. It now shows the web preview it always showed until the details arrive,
and keeps it when none do.

* fix(native-chat): show just the name for another repo's link while its details load

With web previews off, a link card for another repo showed the GitHub skeleton
and collapsed to the name when no details came. It now shows the name from the
start, so nothing collapses when an older host or an unreadable repo answers
with nothing.
2026-11-06 02:26:58 -08:00
Brennan Benson ea05861c91 Show a card in chat for pull requests the agent opens (#38750)
* feat(native-chat): show a card for pull requests a chat turn opened

When a turn's own command runs gh pr create and prints the new URL, the chat
shows an "Opened orca#3" card under that turn with the PR's state, title and
changed lines, read live from GitHub. The eye-slash control hides the card kind
and a toast links to the new Settings switch.

* fix(native-chat): satisfy lint in the opened pull request card

* fix(native-chat): keep opened pull request cards through tool runs and failed rereads

The card now rides a turn's work run, a clipped create output is always read
whole (retried when the chat is shown, never while hidden), older Codex JSON
output is decoded, rereads keep the last answer and restart past an in-flight
read, failing lookups back off, and a failed hide save says so.

* fix(native-chat): detect pull requests Codex opens through PowerShell, and wait for clipped output

Codex on Windows runs commands as pwsh -Command '...', which detection now
unwraps (and cmd /c), with tests on Claude's and Codex's recorded calls. A
clipped create output is decided from its whole text, each message is scanned
once, rereads keep their bytes counted, a merged card rereads after an edit,
and the header names another repo's pull request in full.

* fix(native-chat): retry each clipped pull request output until its host answers

Each create output cut short on the wire keeps its own backoff while the chat is
visible; a host whose support is unknown stays pending, and only one that says
it can't give outputs whole is decided from the head. PowerShell options and
backtick escapes are read as PowerShell does.

* fix(native-chat): read PowerShell create commands in PowerShell's own quoting

Decoding a tool call's script moves to its own module. PowerShell launches are
lexed with its quoting (single quotes keep backticks literal, double quotes and
bare text escape with them, here-strings and comments are text) and launch
options follow the common pattern's tables; what can't be read whole opens
nothing. A cancelled retry no longer records a failure.

* fix(native-chat): decode a launch wrapper inside a PowerShell tool call

* fix(native-chat): read Codex's escaped quotes in a Windows PowerShell command

Codex on Windows writes a double quote inside a double-quoted -Command as \",
so a create with an apostrophe in its title was skipped. Tests use the three
calls recorded from Codex on Windows.

* fix(native-chat): read a PowerShell launch line in the quoting of what ran it

Codex joins argv in POSIX quoting even on Windows, splitting one word into
differently quoted pieces, so a script ending in a backslash or holding both
$ and an apostrophe showed no card. The launch line now goes through the same
POSIX word decoder as the login-shell preview, and a PowerShell tool's own
line through PowerShell's quoting.

* fix(native-chat): find a PowerShell or cmd launch anywhere in a command list

A Bash call such as `pwsh -Command 'gh pr create --fill' && echo done` lost its
card once launch lines were read as POSIX words, and Codex's joined
`cmd.exe /c 'script'` never had one. Each command in a POSIX list is now read on
its own, through one launch reader shared with argv, while Node's Windows
`cmd.exe /d /s /c "script"` form keeps cmd's own quoting. PowerShell lexing
moves to its own module.

* fix(native-chat): unwrap a PowerShell or cmd launch only when it is the whole line

Rebuilding a command list around a nested launch could pull one out of a comment
or heredoc into a false card, and an unreadable launch hid a real create beside
it. As the common pattern does, only a line that is one launch is unwrapped;
any other line is analyzed as written. A PowerShell tool's line and Node's cmd
form must likewise be a single launch.

* fix(native-chat): unwrap cmd and PowerShell-tool lines only when they are one launch

Node's cmd form now unwraps only a /c script that is one quoted string with no
quote inside, since cmd escapes none, and a PowerShell tool's line ends at a
bare line break. Text after either launch can no longer pass for its script.

* fix(native-chat): read a PowerShell line continuation as a word break

A backtick before a line break in a PowerShell tool's line joined the next line
into the current word, so an argument on the next line could pass for a
command after the launch.

* fix(native-chat): unwrap only the launch shapes agents are recorded writing

Hand-written readers for cmd launches and for a launch nested in a PowerShell
tool's script kept letting printed text pass for a create. Only argv, a shell's
whole -c line and Codex's Windows pwsh -Command line are unwrapped now; argv is
never space-joined, and detection reads a shell line as whole POSIX words rather
than through the preview's lenient prefix. cmd launches and nested launches are
intended misses until an agent is recorded using them.

* fix(native-chat): read an escaped separator as text when finding a create

An escaped \; stayed in the analyzed commands and passed for a separator, so
printf '%s\n' \; gh pr create showed a card though only printf ran. Escaped
characters are now text, like quoted strings and comments.

* fix(native-chat): remove a line continuation when finding a create

Blanking escapes also turned a backslash-newline into a word, so a create on
the line after a continuation no longer started a command and lost its card.
A continuation is now removed, as the shell removes it.

* fix(native-chat): treat only backslash-LF as a line continuation

Bash removes a backslash before LF but not before CRLF, where the backslash
escapes CR and LF still ends the command. Removing both lost the card for a
create on the line after a CRLF continuation.
2026-11-06 02:26:37 -08:00
Neil fb13022e0d fix(editors): launch Open In editors by id from settings (#37934)
* fix(editors): launch Open In editors by id from settings

* test(editors): drop settings casts by narrowing the shell store type

* fix(editors): import shell-open types from their moved path

* test(editors): type the shell test settings instead of casting
2026-11-06 02:23:35 -08:00
Neil bed099c195 fix(web): confirm before a pairing link replaces a saved server (#37936)
* fix(web): confirm before a pairing link replaces a saved server

* test(web): drop the stored-environment cast by narrowing the pairing decision input

* i18n(web): translate the replace-saved-server prompt
2026-11-06 02:22:46 -08:00
Neil 4f1124d03d fix(hooks): ask before running a committed .orca/issue-command (#37931) 2026-11-06 02:22:12 -08:00