* feat(cli): set the title of one terminal pane
Adds `orca terminal set-pane-title --terminal <handle> --title <text>`; `--title ""`
clears it. The runtime resolves the live handle to its saved leaf and asks the
desktop window to name that pane through a new optional notifier hook
(`ui:setPaneTitle`). Mounted panes apply it through the same local setter the GUI
uses; parked panes get it in the saved layout their next mount reads. The tab's
own name is never touched.
Rebuilt on current main from #24755.
Co-authored-by: stevelliu <stevelliu@tencent.com>
* fix(renderer): a main-owned layout takes a CLI pane title before the window shows it
* fix(renderer): a CLI pane title reaches a never-mounted tab with a rootless layout
---------
Co-authored-by: stevelliu <stevelliu@tencent.com>
Thirteen read-routing callers (#4, #5, #10-#20 of the round-3 caller list) read the owner through
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin. A workspace two hosts publish is no longer
searched on either host. The focus-read ratchet now counts the legacy-fallback adapter, which
reads the focused runtime inside an object literal its discovery skipped; the five F1 entries
that blind spot pruned come back. Deprecated importers 53 -> 40.
* fix(layout): editor file records keyed by path and owner, so two hosts' files at one path stay apart (layout-owner build only)
Opening a runtime-environment file at a path a local file already held replaced the local record,
the stored-session patch and unsaved drafts matched by path alone, a move took every owner's record
of the path, and closing a bare-path tab removed both owners' records.
* fix(mobile): a windowless host's editor tab under its owned id shows as active, and retiring it clears the active file (layout-owner build only)
* fix(layout): a layout change's tab and pane holds end 15 s after it was sent when main never answers (layout-owner build only)
* fix(layout): main's view reads and prunes keep an active file under its owned id (layout-owner build only)
Open-in owner, execution-host platform facts, doc-preview grants and the activation PTY inventory
read the owner through resolveOwnerWithLegacyFallback. Open in now refuses a twin instead of
launching it as this desktop's checkout; the others keep reading a twin as not server-owned. A row
whose only repo row is a server proxying its SSH target routes through that server. The
creation-action policy stays until browser creation folds with it. Deprecated importers 57 -> 53.
* fix(browser): switch client-hosted profiles with staged guest replacement
Squashed from #30351 by @oliver-mee onto the current main.
Fixes#30350
* fix(browser): fence staged profile guests, release switch grants, carry host profile metadata
- A fence during automation retirement now revokes and stops the staged guest.
- A failed or late-settling switch releases its execution-host grant so the page can be recreated.
- Staging cleanup failures before handover stay visible to executor close.
- createPage/restorePage carry host-owned profile metadata to profile-switch-v1 desktops,
so recovery and restore can rebuild a switched page.
* fix(browser): roll back a switch whose replacement died, and report a lost old guest
* fix(browser): resolve client-hosted pages before the server-local target path when switching profiles
* fix(browser): a staged profile-switch guest owns downloads it starts while loading
* fix(browser): keep the original placement until a timed-out switch settles
* fix(browser): rebind the original guest before releasing a rolled-back staged guest
* test(browser): type the harness guest release mock
* fix(browser): release the replaced generation's download transfers when a switch commits
* fix(browser): admit file transfers from a staged profile-switch guest while its switch is pending
* fix(browser): release only the replaced generation's download transfers after a switch
* refactor(shared): import BrowserNetworkExecutionHost from its own module, no re-export
* fix(browser): adopt a profile switch the client finished after the caller timed out
---------
Co-authored-by: Oliver Mee <102673257+oliver-mee@users.noreply.github.com>
* refactor(renderer): one owner adapter for the deprecated-resolver folds, pinned by an acceptance table
resolveOwnerWithLegacyFallback answers as resolveOwner for a named host and for rows that carry a
host stamp, and keeps the deprecated answer (single focused runtime included) for missing and
unstamped rows. A full-owner acceptance table pins all matrix cases and asserts the only changes
from the deprecated resolver are the designed ones: twins and contested SSH hosts become ambiguous,
and a stale hub stamp routes through the server that names the target.
* refactor(renderer): the fold adapter shares resolveOwner's named-host rule
Clean-code pass: one namedHostForOwnerRef for both resolvers, floating counts as host evidence,
and the acceptance test drops non-null assertions.
* fix(native-chat): clicking Send or Stop keeps focus in the composer
A click focused the Send/Stop button; when it disabled (empty draft after the
turn, or while a Stop is in flight) Chromium dropped focus to <body>. A pointer
press on it no longer moves focus, matching Enter. Adds an e2e regression with
a turn-capable Claude stub. Fixes STA-10897.
* Tighten comments
* feat(sidebar): the owning host raises the workspace unread flag (dark)
* fix(sidebar): phone session open clears and spares the flag; refresh views; blur rescan
* fix(sidebar): throttle failed view retries, resend failed empty views, hold presence one refresh longer
* test(sidebar): switch-on window writes neither the unread flag nor a stamp
normalizeModelForPricing was an ordered chain of regexes in which a new point
release fell through to its major: claude-sonnet-5-5 was billed as Sonnet 5
(cache reads at $0.20 instead of $0.10), and any future opus-5-x / fable-5-x
would be too. claude-haiku-5-5 matched nothing and silently added $0 to a
total that showed no caveat.
The resolver now parses family + major + optional minor (family-first and
legacy version-first IDs) and only accepts an exact MODEL_PRICING key, so an
unknown version is unpriced rather than borrowing another model's rates. The
deliberate opus-4.x -> 4.8 and sonnet-4.x -> 4.6 fallbacks go with it.
Adds Sonnet 5.5 and Mythos 5/5.1 rows. Haiku 5.5 stays unpriced: it reprices a
whole request above 100K prompt tokens, which daily totals cannot express.
The Claude summary now carries hasUnpricedModels (as Codex has since #22073),
and both panes share one unpricedCostLabel helper for the caveat.
Fixes#30338
* fix(cloud-auth): show the browser sign-in wait and offer its link from Relay sign-in
Clicking 'Sign in for Relay' handed the sign-in page to the OS browser and
then showed nothing, so a browser that never came forward looked like a dead
button, and the 5-minute wait ended silently. The Relay sign-in row now shows
the wait with a Copy sign-in link button, and a wait that times out toasts.
STA-10940
* fix(cloud-auth): show the sign-in wait on every Orca sign-in button
The waiting text and Copy sign-in link now come from one shared notice used by
Mobile Relay, Orca Account, Artifacts (settings, page and publish popover),
Share Skills, the unexpected sign-out card and dev tools. The copyable link is
dropped as soon as the browser answers, and a copy with no waiting link
reports a failure instead of doing nothing.
STA-10940
* refactor(cloud-auth): main owns the waiting sign-in link; renderer only mirrors it
The notice now shows exactly while main holds a live link: main announces the
link when the browser wait starts and clears it the moment the browser
answers, so 'Waiting for sign-in in your browser' no longer outlives the wait
during the token exchange. Only the newest attempt may publish its link, so an
older attempt reporting late cannot replace or later wipe it. The renderer's
separate pending flag and its resets are gone.
STA-10940
* fix(cloud-auth): tolerate a sign-in link read that returns no promise
The app-wide IPC tests stub every bridge method as returning nothing, and the
one-time link read called .then on that result.
STA-10940
* fix(cloud-auth): a torn-down link subscription ignores its late snapshot; restore auth bridge tests
A window's one-time sign-in link read could land after its subscription was
disposed and overwrite a newer value from the replacement subscription. The
auth-status refresh tests the earlier rewrite dropped are back, alongside the
link cases.
STA-10940
* feat(cloud-auth): sign-in buttons show Signing in… with copy-link and cancel links
Every Orca sign-in button greys out to 'Signing in…' with a spinner until the
sign-in ends. While the browser has not answered, a line offers 'Copy sign-in
link' and 'Cancel'; Cancel aborts the browser wait so a lost tab never locks
the button. Main owns the in-flight sign-in (waiting on the browser, then
finishing) and every window mirrors it.
STA-10940
* fix(open-in): open a managed server's workspace in VS Code over Remote-SSH (#30332)
* fix(open-in): only open a server workspace over SSH when its files are on that host; validate the SSH link; let process-host quote code.cmd
* test(open-in): expect the workspace ref in the source-control Open in payload
* fix(open-in): pin an SSH-access server's editor host to the settings it was verified on
applyCreatedWorktree merged a create into any same-id row, so a local create could inherit a
server twin's stamp (or wipe it). Lineage refresh stamped a server's reply with the focused host and
replaced every same-id row, overwriting the local twin. Both now match id and host, and the lineage
reply is stamped with the transport it came from.
* fix(browser): a remote viewer larger than the host's browser pane sees the whole page and clicks land under the cursor
Fixes#30341
* test: note why the screencast double is cast
* fix(browser): drop guest frames until the fitted size is applied; forget the cached CSS size when the frame size changes
* fix(browser): keep a press's release and markup inside the letterboxed remote frame
* fix(browser): resume live frames once fitted metrics land; remeasure the page after its frame size changes
A tab restored before its worktree catalog loads gets a placeholder row. SSH placeholders had no
host, and a server's nested-SSH placeholder carried only runtime:E, so an ssh: host selection found
no matching owner and routed the terminal as a direct dial from this desktop. Stamp the SSH target,
and the server proxying it, from the repo row; leave a repo id that lives on several hosts unstamped.
* Open file links beside their source tab's group
Clicking a file link in a terminal or agent chat now opens the file in an editor next to that tab, not in whichever group happens to have focus. A new "Open in this group" row pins the file to the source group when that group has neighbors.
- Capture the source tab at click time, so focus changes during path resolution don't redirect the file.
- Rank candidate groups by adjacency: a neighbor already showing the file, then a neighbor showing an editor, then any neighbor, then the source group.
- "Open in this group" never falls back to a browser, system app, or other group. If the group is gone or the target is not an editor, it shows a toast and does nothing.
- Line reveals target the exact view that was opened, and are dropped if that view closes before the editor applies them.
* rm scratch files
* Preserve empty split panes during tab reconciliation
Reconciliation removed every empty group, so an empty split pane the user had made beside a tab was collapsed on the next pass. A file link opened from that tab then landed in the source group instead of the empty pane. Reconciliation now removes only groups that stale-tab cleanup emptied, and keeps panes that were already empty. Tests cover terminal and agent-session sources in both worktree and folder workspaces.
* Preserve runtime owner for local file links
* fix(drops): name extensionless dropped images by their bytes
A file dropped onto a remote workspace with no extension (some drag sources
hand images over as `noname`) now gets the extension its PNG/JPEG/GIF/WebP
signature declares, so the agent sees `noname.png`. Repo icon detection
reuses the same signature check.
Fixes#30378
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(drops): sniff extensionless images only for .orca/drops and chat attachments
Explorer imports into a user-picked remote folder keep the name, matching
local imports. Chat attachments on a paired server now use the staged name
so they get the extension too.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
A client's pane rename already lands in the host's saved layout (titlesByLeafId),
but terminal.list/show never read it, and clearing a remote tab's last pane title
sent no titles at all, which every host reads as 'unchanged'.
- terminal summaries carry an optional paneTitle from the host's saved layout;
the CLI list prints it ahead of the terminal title. Older hosts omit it.
- clearing the last pane title of a remote tab pushes titlesByLeafId: {}, which
old and new hosts both persist as cleared.
Supersedes #30333, which renamed the whole tab via terminal.rename.
Co-authored-by: wangshifeng <shifeng.wang@magic-shield.com>
Every renderer sender of layout.apply (sendLayoutApply, recordLayoutApply, the activation
terminal create and the live terminal adoption) returns before the call for a worktree or
folder workspace a paired server owns, and a source census pins that no other renderer file
sends layout.apply.
* fix(native-chat): identify the owner when resume create is refused
* test(native-chat): consolidate transcript refusal cases
* test(native-chat): keep ownership coverage at the launch boundary
* test(native-chat): update adoption fixture for early ownership admission
* test(native-chat): align resume admission assertions with host ownership
* test(native-chat): keep RPC coverage focused on ownership proof
* test(native-chat): assert blank request presence before optional source
* fix(native-chat): admit the actual CLI source during chat creation
* fix(native-chat): satisfy source admission lint checks
* test(native-chat): provide the waiting hook prompt
* test(native-chat): isolate source admission fixtures from host homes
* test(native-chat): canonicalize adoption fixture directories
* test(native-chat): preserve early ownership admission in source stack
* Resume CLI conversations directly in native chat
* fix(native-chat): preserve resume refusal behavior and honest transcript feedback
* fix(native-chat): retain import read evidence in typed result
* Use the header test fixture record type
* Consolidate launch owner imports and correct feedback assertion
* Keep existing resume translation in runtime catalog
* fix(native-chat): preserve admitted source and prepared execution home
* fix(chat): retain source evidence for dispatched adoption
* fix(chat): keep source capture within module limits
* Keep deferred chat publication in the shared launch owner
* test(chat): allow host evidence in create routing
* Satisfy launch publication checks and scope slot cleanup
* Preserve canonical tab membership on projected reorders
* Use recorded launch ownership for surface recovery
* Reserve deferred chat order through existing group actions
* Pass the active layout owner into launch cancellation
* fix(native-chat): admit an unsaved transcript under a linked account folder
A missing transcript stayed unresolved while its account root resolved through
symlinks, so containment failed and a not-yet-saved conversation showed a load
error instead of the unsaved notice.
* refactor(native-chat): move the newest-item import window into its own module
Keeps journal-legacy-import.ts under the line limit after the source
identity observer landed on top of the streaming import.
* fix(native-chat): restore the refusal error import lost in the main merge
* test(native-chat): pass the layout owner in the layout-owner cancel test
main's new test predates launch cancellation taking the active layout owner.
* fix(native-chat): admit Pi transcripts against the root Session History walks
A PI_CODING_AGENT_DIR that already ends in sessions, or any custom folder,
made admission expect <dir>/sessions while the lookup searched <dir>, so a
found conversation was refused.
* refactor(native-chat): admit and look up CLI transcripts in one store per account
Each agent's import described where an account keeps its conversations twice,
once for the path check and once for the lookup, and the two could disagree
(the linked-folder and Pi custom-root refusals). The import now names one
conversation store per account; the create path resolves it once and both the
hook-path check and the fallback lookup judge that same store. OpenCode's
store is its exact database.
* fix(native-chat): keep a History resume that joins a terminal resume alive when the terminal closes
A History click that joins a pending terminal-tab resume now takes the
launch over: the terminal hand-off runs at join time and the create
resumes by conversation id, so closing the terminal before create no
longer abandons the shared launch.
* refactor(native-chat): move the launch source hand-off into its own module
* fix: pick the chat that adopts a header resume the way History does
* test(sidebar): the desktop window and a feed reader draw the same list (dark)
* fix(sidebar): a feed reader lists a paired server's folders and nested workspaces as the window does (dark)
* fix(sidebar): the paired-server shadow compares the lineage the server's check keeps (dark)
* fix(sidebar): paired-server rows carry the unread flag Smart Done reads (dark)
* feat(sidebar): Smart Done means finished and unread (dark)
Owner decision Q1: with ORCA_SIDEBAR_INPUTS=main, a Done row holds its tier only
while the workspace unread flag is set, so opening the workspace or sending the
next prompt ends it. Reuses the existing flag; no new state or timer.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): reading a workspace drops its Done row in the live list
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(layout): a workspace only main held shows main's tab on the window's first visit (dark, layout-owner build only)
* fix(layout): a live terminal no pane shows goes through main, which names the tab it holds; a pane close main has no pane for closes here (dark, layout-owner build only)
* test: no type assertions in the CLI-first tests
* refactor: the switch-off build keeps today's live-terminal mint as it was
* review: frame sync under the line limit; the first-visit test names exactly main's tabs
* fix(layout): adopt takes the recorded pane only while it is unbound
* fix(layout): a refused live-terminal adoption is declined, not counted as shown
* refactor(native-chat): move the slash picker builder and upload chunking to shared
Mobile builds the same skill and command list as desktop.
* feat(mobile): redesign the chat composer with file attachments and inline skills
* fix(mobile): the composer field owns its input style
The page's font-size census cannot follow a style passed in as a prop.
* refactor(sidebar): the stamp-sorted window list runs no settle, latch or saved order (dark)
With ORCA_SIDEBAR_INPUTS=main the window's sort hook is a pure read of the
rows' stamps: no settled-epoch store listener, no Smart latch, no saved Smart
order writes, no class telemetry over the old attention map. Switch off: the
existing hook, unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): stamp sort hook reads no live sources; stronger switch-on tests
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): clear the order-save mock between tests
Co-Authored-By: Claude <noreply@anthropic.com>
* wip: main keeps sleeping retention only
* fix(sidebar): stamp sort reads a shared empty-sources constant, not a renderer layout literal
The layout-writer ratchet counts layout field keys written in the renderer;
the empty sources object now lives beside SmartSortSources in shared code.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(sidebar): main keeps sleeping retention only; no order of its own (dark)
Clients sort by the owning host's stamps, so main's temporal owner loses its
settle window, Smart latch, create grace and catalog-listing classifier, and
the shadow check stops comparing order. Retention, its clock and its shadow
comparison stay.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(sidebar): every retention listing shares one sequence; publish after the first
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): the first listing bumps the retention revision
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* refactor(sidebar): the stamp-sorted window list runs no settle, latch or saved order (dark)
With ORCA_SIDEBAR_INPUTS=main the window's sort hook is a pure read of the
rows' stamps: no settled-epoch store listener, no Smart latch, no saved Smart
order writes, no class telemetry over the old attention map. Switch off: the
existing hook, unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): stamp sort hook reads no live sources; stronger switch-on tests
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): clear the order-save mock between tests
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(sidebar): stamp sort reads a shared empty-sources constant, not a renderer layout literal
The layout-writer ratchet counts layout field keys written in the renderer;
the empty sources object now lives beside SmartSortSources in shared code.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(cleanup): count visits as last activity once terminals stop stamping Recent (dark)
With ORCA_SIDEBAR_INPUTS=main, terminal start and exit no longer write
lastActivityAt, so a workspace used only through plain shells could look idle
to workspace cleanup and old in the jump palette. Both now read the later of
the Recent stamp and the last visit. Switch off: unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(cleanup): stamp the workspace you leave; space table reads visits too (dark)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(cleanup): every reveal stamps the workspace you leave, host-aware (dark)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(sidebar): sort stamps reconcile stored tiers at startup against live hook rows and terminals (dark)
* test(sidebar): a restart clears an unbacked local tier and keeps an SSH host's
* fix(sidebar): run the sort tier reconcile once hook rows and PTYs settle, seeding hydrated rows
* fix(sidebar): clear a stored tier only on proof its terminals died; a rest seed adds no clear
* chore(sidebar): merge origin/main; locality helper moved to shared
* feat(sidebar): sort Recent and Smart by host stamps behind the sidebar-inputs switch
With ORCA_SIDEBAR_INPUTS=main, the window, main and the phone order Recent by
lastActivityAt and Smart by smartTier then smartAt (Needs you, Done, Working,
then the rest by Recent). The window holds its displayed order while the
pointer is over the list or a drag runs. Switch off: today's sort path.
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: restore files the merge commit's formatter rewrote
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(sidebar): stamp-order review fixes behind the sidebar-inputs switch
The window writes no Recent stamps with the switch on (terminal start and
exit, unread marks, comment edits), the hover hold covers only Recent and
Smart and ends with a drag, main follows only explicit metadata saves, and
the phone picks its sort from the unfiltered rows.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): folder writers and a Manual drag under the stamp switch
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): name the stamp type in the ps summaries test
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Require a full hover delay before each tab preview
Tab previews opened almost immediately after a previous preview had been shown, so sweeping across the tab strip caused a flurry of popups. Raise the tab tooltip delay to 800 ms and drop the warm-start chain: the shared open-state context, the 300 ms skip delay, and the slide animation that carried position between cards. Every tab now waits its full dwell time, and keyboard focus still shows the preview immediately.
* Test that tab hover cards wait a full dwell on every hover
The tab hover card no longer slides instantly between neighboring tabs. Each hover now waits for the full delay before showing a preview. The e2e test still expected instant sliding, so it was rewritten to match.
- Remove the frame-recording helpers and the slide and reversal assertions.
- Check that no card appears before the dwell, and that the card appears after it.
- Check that leaving a tab cancels a pending preview, and that time spent on other tabs does not count toward the dwell.
- Check that re-entering the same tab requires a fresh dwell.
* chore(layout): the drift check's ignore comment names every main-only kind (dark, layout-owner build only)
* fix(layout): a quit stage retried after a failed one still sends the resume records (dark, layout-owner build only)
* feat(layout): the window's local layout stream reopens after a loss, and its snapshot redraws every workspace (dark, layout-owner build only)
* fix(layout): a command whose target main's copy lacks is a logged refusal, not the window's own change (dark, layout-owner build only)
* feat(layout): frames keep each tab the window drew, closed or moved as drawn until main answers; the global frame hold goes (dark, layout-owner build only)
A refused draw-first terminal create is closed (its terminal stopped) at the refusal. The
activation create no longer draws main's tab itself while the stream is down: the reopened
stream's snapshot does.
* fix(layout): a tab move or drag-out the frame already drew is not placed again by the window (dark, layout-owner build only)
Also holds the group sizes the window drew until main saves them.
* test(layout): with frames flowing, a pin promotes a preview tab once and tab-change bookkeeping runs once (dark, layout-owner build only)
Also states that the held-tab overlay builds frame slices only.
* test(layout): read the rename's title without a cast
* chore(layout): the drift check's header lists the same ignored records as its inline comment
* feat(settings): remove a downloaded agent runtime from its Settings row
When the host offers managed-runtime.remove.v1 and an Orca-installed copy is
there with nothing running, the Settings install row shows a trash button
("Remove downloaded runtime") that asks first: "Remove {agent} from this
host? Running {agent} chats stop. Chat history is kept." While the host removes
it the row says "Removing…"; a refused removal shows the host's reason and keeps
the button. Chats never show it.
* fix(settings): offer Remove for a damaged copy too, and say which host the downloaded runtime leaves
The row offers Remove whenever the host says Orca's copy is there (canRemove), damaged included;
an older host without it keeps today's rule. The confirmation now reads "Remove the downloaded
{agent} runtime from {host}? Running {agent} chats there stop. Chat history is kept.", naming the
paired host, or this computer.
* fix(settings): Remove follows the host's canRemove alone
* fix(settings): the Remove dialog says the sign-in is kept too
* fix(settings): show the downloaded runtime's install state as the provider's status line
The Antigravity row in Settings > Chat > Providers drew its install state as a
bordered box nested inside the row. It now reads like the other rows: one grey
status line under the name, with its actions as inline links joined by ' · '
(Installed · Remove, Not installed · Install · size, a thin progress bar with
Cancel while downloading, Reinstall · Remove for a damaged copy, Try again
after a failure). A healthy install no longer offers Reinstall. The Remove
confirmation now says running chats stop and their history is kept.
* fix(settings): make Antigravity's first Install a primary button beside the switch
Not installed now shows a high-contrast Install button at the row's right end in
Settings > Chat > Providers and Settings > AI Provider Accounts > Antigravity; the
status line keeps the sentences (Not installed · 112 MB download.). Every other
action stays a link in the line.
* chore(layout): the drift check's ignore comment names every main-only kind (dark, layout-owner build only)
* fix(layout): a quit stage retried after a failed one still sends the resume records (dark, layout-owner build only)
* feat(layout): the window's local layout stream reopens after a loss, and its snapshot redraws every workspace (dark, layout-owner build only)
Removes the re-export lines in runtime-rpc-result (hasRuntimeRpcErrorCode), sync-runtime-graph,
sync-key, the terminal orphan-recovery modules, orcad-idle-exit, remote-runtime-subscription-transport,
two orcad modules and the ssh-connection test harness; importers and test mocks name the real modules.
Moves legacy-relay-bridge/ plus the relay-only census and endpoint files into ssh/legacy-relay/,
so the 2027-01-15 retirement deletes one folder. Live install, exec and framing modules that only
carried the relay name become remote-*. git mv plus import, path-literal, routing and workflow
path-filter rewrites only; no remote file name or path changes.
* fix(settings): show one coherent status for servers that revoked this computer
* fix(settings): drop the redundant warning icon on revoked server rows
* fix(status-bar): show access revoked / update required for paired servers
A server that revoked this computer's pairing (or refused it as incompatible)
read as a plain "Disconnected" row with a futile Connect action, and the
aggregate segment stayed idle grey. Rows now take the execution-host registry's
blocked verdict: "Access revoked" with a Re-pair… action opening the existing
RuntimeRepairDialog, or "Update required" with the compat detail. A blocked
host turns the aggregate indicator destructive instead of hiding as idle.
* fix(layout): an SSH pane's terminal binds in its own session partition (dark, layout-owner build only)
Main keeps its own layout over a window save, so a binding only a save carries is lost. A window
or runtime spawn of an SSH pane bound in the local partition; it now binds where the pane's layout
lives.
(cherry picked from commit beb2275b45)
* test(layout): the SSH binding test uses the shared spawn commit dependencies
(cherry picked from commit 672b641897)
* fix(layout): main moves a tab between a local and an SSH workspace itself (dark, layout-owner build only)
moveTabToWorkspace across two partitions was refused as other_partition and left to the window's
save. Main now reads both partitions, moves the tabs as within one, and writes both in one durable
mutation.
(cherry picked from commit ee27a0bb07)
* chore(layout): one binding write for the partition choice; shipped builds keep their exact replaced-binding read
* fix(layout): window saves keep main's layout and give it only the window's view and facts (dark, layout-owner build only)
windowViewSaveForMain/windowViewPatchForMain replace windowSaveForMain: main's own layout joined
with the window's view, facts and carried fields, written only into the records that change. A
paired server's partition passes through. A visited workspace main holds keeps its terminal row
(D1). The P10 pending case is on.
(cherry picked from commit 0c2fc15974)
* fix(layout): the save divergence detector becomes a report-only drift check behind its log (dark, layout-owner build only)
It no longer wraps the write: it compares the window's raw save with main's layout model, and
only when ORCA_LAYOUT_SAVE_DIVERGENCE_LOG is set.
(cherry picked from commit 6bc1e5556f)
* fix(layout): tab reconcile and terminal reconnect stop writing tab structure (dark, layout-owner build only)
Main's frames draw the tab bar and rows: reconcile still returns its counts but writes nothing,
and reconnect still advertises restored terminals but leaves the rows to main.
(cherry picked from commit ef8eb625ca)
* fix(layout): a layout command main cannot apply is a logged refusal, not the window's own change (dark, layout-owner build only)
other_partition, a thrown call, and host_not_owned for a workspace that is not a paired server's
are refused and logged: a window save no longer carries the change, so drawing it would last only
until the next frame. A twin main may route to a paired server keeps today's path, as does the
first terminal's host_not_owned fallback.
(cherry picked from commit 2c513b2d10)
* fix(layout): the window's refusal code type keeps main's host refusals
(cherry picked from commit 36d46bdcc2)
* fix(layout): the quit checkpoint records only the quit capture's resume records in main (dark, layout-owner build only)
Main keeps its own resume records over a window save, so the quit capture's changes, which reach
main as async commands, could miss the quit. The capture now sends only what it changed with the
synchronous stage, and main writes them through the record-only sleep command before staging; a
wake main recorded that the window had not seen stays.
(cherry picked from commit 2a0f433c34)
* test(layout): the quit capture test builds a typed resume record
(cherry picked from commit d92d1246ee)
* chore(layout): one layout write per save handler
* chore(layout): shipped builds keep their exact quit capture
* fix(layout): the drift check compares structure only, not records main writes itself (dark, layout-owner build only)
Pane bindings and resume records, default-tab marks, closed-tab records and terminal row owners
are main's own writes; a save that has not caught up with them asks main for nothing, so they are
no longer reported.
(cherry picked from commit 93e8b2627c)
* test(layout): relaunch first-terminal cases, and the side-by-side resume case moves its tab the way the window does
(cherry picked from commit ab381332b1)
* test(layout): the relaunch case also checks a reopen that cleared read-only
(cherry picked from commit bbf3d040c8)
* docs(layout): the drift check names the record kinds it ignores
* revert(layout): drop the SSH binding partition change; SSH terminal starts are refused before any binding is saved
getProvider refuses every non-local host, so neither spawn path reaches persistPtyBinding for an
SSH pane, with the switch on or off.
* test: run A2's two real-SQLite session tests in the Node runtime project
Retain reveal sequence coverage until a parsed snapshot covers it, preventing a duplicate text-only replay from clearing newer terminal images. Keep conservative fallback recovery for unknown episodes, missing metadata and different terminal processes.
Verified against current main: 406 tests, full application typecheck, changed-code quality and four fresh hidden rendered restore checks pass. No xterm patches or dependency changes.
* fix(runtime): show Re-pair required for revoked servers and re-pair in place
A server that revokes this computer's pairing token was labeled "Update
required" because auth refusals and protocol mismatches shared one blocked
state. Carry the blocked reason locally, label revoked hosts "Re-pair
required", and add a Re-pair action (sidebar host menu + Settings server
row) that verifies a new pairing link reaches the same server before
replacing the saved pairing.
* fix(sidebar): stop host header menu clicks from toggling or dragging the row
* fix(sidebar): vertically center host header label and menu button
* refactor: move the contested-workspace primary-host rule to src/shared (move only)
* fix(layout-owner): main routes a twin repo's workspace to the same primary host the window saves to
* fix(layout-owner): route a twin repo's single-host worktree to the host that holds it; hydrate twins the same way
* fix(layout-owner): twin-repo routing counts only runtime owners from the catalog, matching the window; one table checks both sides
* fix: drop the duplicate layout-owner switch import after merging main
* fix: type the twin-routing fixture and partition set
* fix(session-history): find every agent's older sessions in Workspace and Project views
Session History kept only the newest sessions across every agent, then filtered
by workspace; only Claude, Pi and CodeBuddy got their older in-scope sessions
back. Every agent now declares how its cwd is found without a full parse, and
the scan selects in-scope sessions before the recency cap: Codex and other
header agents by their opening record, SQLite stores by their directory column,
the rest by their cached parse. The scan and the cached-list cut share one
scope rule.
* fix(session-history): keep discovery under the file length limit
* fix(session-history): type the scope walk's file list
* Keep session history scope complete before applying the display cap
* test: align session history scope fixtures
* perf(session-history): settle known out-of-scope transcripts without a per-file read
A scoped scan walks every transcript, and each one cost an await, a parse-cache
database lookup and, for Codex, two head reads every scan. Remember each file
version's settled cwd (from its opening records or its parse), drop settled
out-of-scope files synchronously before the async walk, and read Codex's
roughly 20 KB first record in one 64 KB read.
* fix(session-history): let the parse cache decide scope before transcript heads
The settled-cwd prefilter let a transcript-only verdict reject files whose
current parse (hot or cold) says otherwise: a Cursor chat whose meta.json
appeared later, or a Codex session whose parsed latest cwd moved after its
row left memory. Restore parse-cache-first selection; keep Codex's single
64 KB first read.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
Since #30232 it accepts null and covers both remote: and app SSH ids, so the extra isRemoteRuntimePtyId / parseAppSshPtyId / null guards never change the result.
failedBefore read the recorded failure before the host lock, so a check waiting behind one that failed still tried the update and noted a stale reason. Rename retryRecordedFailure to failedThisLaunch and read both inside the lock. Also share withDetail and drop a test that could not fail.
Every split pane's tab strip stays mounted, so focusing a pane used to add or remove its action buttons and reflow the tabs. Quick-command shortcuts also fired from hidden or unfocused groups.
- Keep the Pane Actions and quick-command buttons mounted in every strip. Unfocused strips dim instead of disappearing, so focus no longer reflows the tabs.
- Hide the button labels by strip width with container queries, so narrow split groups stay icon-only.
- Only the focused, visible group registers quick-command keyboard and Electron shortcut listeners. An open menu closes when its group loses focus.
- Add "Close split pane" to the tab context menus, shown only when the group is one of several splits.
- Stop Radix from restoring focus to a stale group when the pane actions menu closes.
Worktree ids built as `${repo.id}::${path}` by hand are how one worktree got two
spellings (#30150). New code composes them through src/shared/worktree/id.ts; the
ratchet counts the remaining hand-built ids per file (18) and may only go down. Six
byte-identical `${repo.id}::${repo.path}` sites move to getRepoMainWorktreeId.