The phone gave a direct socket 5 s from open to authenticated, while
the desktop keeps an unauthenticated socket for 10 s. On a slow link
(a handshake is two round trips, so ~2.5 s RTT is enough) the phone
dropped the socket the desktop would have accepted, then redialed into
the same deadline forever: the 'Can't connect' loop a test documented.
Both ends now share PAIRED_E2EE_HANDSHAKE_DEADLINE_MS (10 s). A link
that needs 6 s to authenticate now connects on the first dial; one that
never authenticates still escalates the same way, 5 s later. Relay
dials are unaffected: they keep their own stage budgets and race the
direct dial after 2.5 s.
The very first time a host is opened there is no cached page, so a
single failed dial (a 12 s connect deadline or a 5 s handshake on a
slow link) put up the offline screen, 'Connect to this host to download
the latest app', while the phone was still redialing and often seconds
from connecting.
The shell's gates now carry whether the dial is still within the
attempts the connection label calls connecting (the same three it
waits before warning). A first open keeps checking through them; when
they run out the gate verdict changes, the flow restarts and the
offline screen shows as before. A cached page still opens at once, as
today.
The download-failure decision moves to mobile-web-shell-download-failure.ts
unchanged, to keep the session reducer under the size cap.
Opening a host with nothing cached waited for the whole worktree
catalog before drawing a row. On a real desktop with 1,084 workspaces
that is ~153 KB compressed, about 1.2 s at 1 Mbps.
The first read now also asks for the host's first 60 rows by its own
ranking (pinned, unread, active, most recent output), ~17 KB on the same
desktop, and draws them while the list is still empty. The full catalog
replaces them as soon as it lands, as it replaces cached rows today. The
first page is never marked loaded or cached, and a failed full read
clears it so the screen still reports the failure instead of a partial
host. Hosts of every version accept the limit parameter.
Opening a host ran status.get twice in a row: once for the shell's
compatibility gate, then again from the page's own gate, which held the
host screen (and with it the first worktree.ps) for one more round trip.
Over relay that round trip is the slowest part of opening a host.
The shell now hands the page the status its gate proved, in init. The
page's gate starts from it, computing its own compatibility verdict from
those fields, and still reads the host itself; its own answer replaces
the shell's when it lands. Reconnects keep today's behavior: the page
re-reads before trusting the host again.
The shell's gate handling moves to use-mobile-web-shell-gates.ts so the
session hook stays under the size cap.
Mixed versions: init's hostStatus is optional and salvaged, so older
shells omit it and the page asks as before; older pages ignore it.
The host screen re-read repo.list every minute and on every repo or SSH
event. Each reply carried every repo's uploaded icon inline as a data URL
(up to 400 KB each, barely compressible), although repos rarely change.
repo.list now takes an optional afterSnapshotId and answers unchanged
when the caller already holds the exact reply. The id is content-derived
(the hashing moves to content-snapshot-id.ts, shared with worktree.ps).
The phone keeps the last catalog per client, reuses it on unchanged
replies and still refreshes SSH host labels and health.
repo.list declared no params, so older hosts ignore the field and answer
the full catalog as before; callers that send nothing get the byte-exact
legacy reply. Icons still render from the same data, at the same size.
The eight native Settings screens use the shared NavBar and grouped rows,
Source Control's tabs move into the bar's segmented control, and Accounts,
Troubleshoot, the custom key editor, the Tasks pickers and drawers and the
workspace details sheet drop their hand-rolled rows for ListRow.
* fix(agent-picker): render the agent setup panel above the new tab menu
The setup panel opened from an agent row's arrow used the base popover
layer (z-60), below the menu it opens from (z-70), so the menu's drop
shadow fell across the panel and it looked tucked behind the menu.
Sharing the menu layer stacks it by open order, like a submenu, and
keeps the panel's own permission dropdown above it.
* test(agent-picker): pin the setup panel to the menu layer
The host screen re-read every paired server's full worktree catalog on
each 3 s poll, through the desktop's mobileRelay.hosts.worktrees, even
when nothing changed: the same cost worktree.ps had before row deltas.
mobileRelay.hosts.worktrees now answers snapshot and row-delta reads with
the same resolver as worktree.ps (made generic over row type). The phone
keeps one WorktreeCatalogSnapshotClient per server, which now takes the
catalog request it snapshots, so server rows reuse the same token,
rebuild and copy-on-admit logic as desktop rows.
The params schema is strict, so the phone sends the snapshot fields only
to a desktop advertising mobile.relay-host-worktree-deltas.v1; older
desktops keep getting plain reads. Callers that send neither field, and
servers whose rows are not fetched yet, get the plain reply.
#36653 added five lines to federated-worker-start.ts, which was already
near the 300-line cap, so main's Lint step fails (305 lines) and every
PR based on main fails static analysis.
Recording a ready remote dispatch's resources and its input-accepted
stage moves, unchanged, into federated-worker-ready-record.ts.
An app theme may declare one validated raster image (png/jpeg/webp, <=2 MB, >=640x360, inside the plugin) served through orca-plugin-image, painted as one static body layer under translucent terminals. Older Orca ignores the field; a bad image drops only the image.
* feat(mobile): polish press feedback, sheet motion, states, spacing and layout jumps
Every control presses and disables the same way, with one haptic rule.
Sheets follow the finger and settle without bounce, pickers stack instead of
swapping, and screens slide over a dimmed, shifted parent. Loading, empty,
error and offline states share one view with plain-language errors, rows and
labels line up on one grid, and screens stop jumping as data arrives.
* fix(mobile): settle voice, tasks and accounts states; keep host snapshot current
Voice settings with no paired desktop shows the connect state instead of
loading forever. A failed task settings read after a successful probe retries
hydration. The shared host-list snapshot drops on invalidation and ignores
stale passes. Accounts shows a re-pair state on auth failure, and busy
action-sheet rows keep their disabled state for screen readers.
* Agent message cards keep their sender's agent icon when the agent can't be found
A card names its sender by a snapshot written with the message, but drew the
icon only from a live lookup, so a sender the lookup missed (a worker on
another host, a terminal after restart, a closed agent) showed "?". Every
writer (worker start, mail, task, federated start) now saves the sender's
agent kind beside its name, and the card draws it when no live row names
the agent. Live rows stay the source of icon and status whenever they match.
* Preserve known worker agents on federated cards and mail
* Retain acquired worker launch identity for later sender snapshots
* Prune repeated worker sender contract checks
* Combine worker command contract projection imports
* Use the sender snapshot resolver in main's new receiving-account tests
Two paths showed Disconnected while the phone was still connecting:
- A relay dial started from idle (after a background suspend, or when no
session was live) left the client disconnected until the relay hello,
because the replacement session starts in connecting without announcing
it. The migration now forwards the replacement's initial dial state.
- The host screen page never learned which path the shell was dialing, so
a direct dial that failed over to relay read as Reconnecting, then Can't
connect and the unreachable empty list, while relay was still working.
Bridge connection snapshots now carry the active and pending paths and
the relay verdict, and the shell sends a connectionPath frame when only
those change, so the page shows Connecting via Relay like the native
screens. Path frames fire path listeners, never state listeners, so
nothing on the page mistakes them for a reconnect.
Mixed versions: the snapshot fields are optional, so older shells omit
them and the page keeps its previous defaults. Pages announce
connectionPathFrames in ready; older pages never receive the new frame.
* Show agent message cards in phone chats
* test: type the phone markdown mock with its component props
* Fix phone agent message cards and sender navigation
* Open native chats on the phone in place
* Preserve desktop host and agent when opening chats
* Separate phone cards from shared chat opening foundation
* Show phone agent cards through shared sender opening
* Preserve chat opening and captured lifecycle contracts
* test: cover sender focus and unconfirmed chat close outcomes
* test: exercise structured worker CLI lifecycle through runtime socket
* test: use checked parked mail observation in CLI integration
* test: validate provider notification correlation identities
* test: construct checked lifecycle and navigation fixtures
* Keep cross-program session proofs in their test program
* Localize mobile chat opening feedback and complete runtime contracts
* Use the mocked text component identity in History feedback proof
* Temporarily expose grown E2E type diagnostics
* Keep E2E timer and fetch fixtures in their runtime domains
* Reconcile decreased E2E wait pattern counts
* Preserve live test clocks and expose remaining type diagnostics
* Keep mobile session data types independent of native views
* Complete structured session wire surface fixtures
* Expose existing grown E2E type diagnostics temporarily
* Preserve checked wire fixture tab mock signatures
* Preserve worker redrive when automatic close is refused
* Honor automatic close admission before settlement and retirement
* test: retain History reveal and close publication race
* Match automation reveal callback to activation result
* Align CLI integration fixture with current settings and output
* Prevent retained chats from reopening removed workspaces
* Import notification types directly from their owner
* Unify local removal fences for accepted worktree aliases
* Keep cross-surface authority tests in the combined test project
* Initialize coordinator mail test workspace ownership
* Register the retained automation chat test workspace
* Use checked inputs in worker start failure test
* Preserve the automation test repository host literal
* Open phone card senders through shared caller navigation
* Carry the phone sender deadline through shared admission
* Finish empty phone card measurement and consumer coverage
* test(mobile): target native hosts in image-only card regression
* fix(mobile): fade folded agent message edges
Unchecked boxes used --input (15% white) on dark surfaces, leaving the outline nearly invisible. Use 60% muted-foreground for the unchecked border and fill indeterminate boxes like checked ones.
The connection snapshot schema, the shell's snapshot builder and the
page's not-ready guard move to bridge-connection-snapshot.ts and
BridgeConnectionCache.require(). No behavior change: the envelope
re-exports the schema, so every frame parses and serializes as before.
Prepares the next change, which adds connection path fields to the
snapshot, without pushing the bridge modules over the size cap.
* feat(themes): public tokens for diffs, git status, tabs, dividers, rows, links
Adds 29 public app theme tokens (diff, git decorations, tab bar, split
divider, current workspace row, host badge, chat bubble, text link), each
falling back to today's built-in color or CSS chain so existing themes and
built-ins render the same. Monaco's diff editor now paints from the diff
tokens, with sharper default diff tints (from #34698) and a readable dark
ignored-file color. The contrast gate learns one-sided drops, translucent
surfaces checked over their base, and main.css chains for half-set pairs.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(themes): check omitted diff tints, chat bubbles and dark host badges as painted
The contrast gate now derives an omitted diff tint from the theme's hue (and
drops the hue when its tint hides code), checks chat bubbles against chat's
mixed foreground on the chat canvas, and models the dark host badge at 80%
accent. The main.css fallback table moves to plugin-theme-css-fallbacks.ts.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(themes): blame a diff hue only for tints it painted, and check chat diff rows
A diff tint the theme set itself no longer drops the theme's hue when it
fails, and chat diff rows (hue-mixed text on the line tint over the chat
canvas) are now checked too.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(themes): new theme tokens only expose colors; unthemed surfaces paint exactly as before
Every new token now falls back to the exact value its surface used before:
- Diff hues default to the git decoration colors at the previous per-side
percentages, and chat diff text keeps its 70% mix.
- Monaco's diff view, overview ruler and links keep Monaco's stock colors
unless the theme painted a diff or link token. The colors go through the
derived Monaco theme instead of a global CSS override.
- --text-link has no :root default; each link rule keeps its old literal as
the fallback, including the markdown-light/-dark preview split and the
always-#58a6ff Monaco doc link.
- Tab bar, tab text and indicator fall back per element (bg-card,
text-foreground, ...) instead of through :root aliases.
- Dark --git-decoration-ignored goes back to #6e6e6e (#35910 ships separately).
The contrast gate skips checks that have no theme token to drop, so themes
that set only older tokens pay no extra probes.
* perf(themes): contrast check skips ring checks the theme didn't set and precomputes drop lists
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(editor): route editor typography through one resolver and add opt-in weight, line height, ligatures, size and macOS smoothing
Monaco sites built font options ad hoc and never received fontWeight,
fontLigatures or lineHeight; the editor size was tied to the terminal.
editor-typography.ts now feeds every Monaco and DOM code surface. New
optional settings default to today's rendering. A macOS-only Font
Smoothing setting rebuilds WebGL terminal renderers so the change
reaches their shared glyph atlases.
* fix: satisfy changed-code quality gates
* fix(terminal): cancel WebGL atlas warm-up on dispose so rebuilt renderers stop leaking canvases
The warm-up queue kept drawing into a disposed atlas, which re-appended its
scratch canvas to the terminal element. Every WebGL renderer rebuild (GPU
toggle, context-loss retry, font smoothing toggle) leaked one canvas per pane.
* feat(editor): read pixel and zero line heights like code editors and let markdown code follow an explicit editor font
#36315 added two lines to runtime-identity-state.ts, which was already at
the 300-line cap, so main's Lint step fails (302 lines) and every PR
based on main fails static analysis.
Mobile session snapshot version stamping moves, unchanged, into
mobile-session-snapshot-version.ts.
* fix(tab-bar): new tab menu orders agents by default and agent order, not by Chat or CLI
Since the menu merged each agent's chat and CLI rows into one entry, entries
kept the rows' surface grouping, so an agent set to open as its CLI (Claude)
sank below every agent set to open as a chat.
* fix(tab-bar): refresh new tab menu order when the default agent changes
The picker inventory cache key left out the default agent, which now decides entry order, so changing the default with the same rows kept the old order.
* test(agent): expect the standard agent order in the shared-reads inventory test
The new tab menu now orders entries by default then standard agent order, so Claude is the first entry here.
* fix(native-chat): an open chat moves to the account you switch to before its next message
A running chat's agent read its account only when it started, and nothing compared it with the
account a start would use now, so open chats kept answering on the old account after a switch.
The agent a chat runs now records the account its launch used, and the host's replace-before-the-
next-send check replaces an idle one whose account is no longer what a start would use. A chat
mid-reply finishes on the old account first. Claude chats follow; agents whose chats keep the
account they were created on are never compared.
A chat's model-list reads and saves now use the account it runs on, so a moved chat no longer
writes its new account's list under its first account (STA-11281).
Tracked in STA-11211.
* fix(native-chat): declare the launch-account read for the runtime layer that wires the host
* fix(claude): find custom chat history before managed-account resume
* test(claude): assign the complete custom account home
* feat(native-chat): offer account switching and hand-off at usage limits
* fix: track the provider in account-list reload effects
* fix(native-chat): target recovery actions at the chat host and transcript
* fix(native-chat): verify account recovery for each chat on its host
* refactor(native-chat): retain the existing image-support result
* fix(native-chat): withdraw account recovery after failed eligibility reads
* fix(native-chat): leave unavailable recovery facts absent
* Align Japanese usage-limit recovery copy with catalog convention
* Preserve complete chat context through usage-limit recovery
* Keep Continue callback focus optional
* Keep recovery fixtures and account effects type safe
* Keep chat recovery on native sources and native accounts
* Validate native recovery files after path admission
* Use the optional cursor shape in recovery fixture
* fix(native-chat): exclude source CLI family from limit handoff
* Resolve recovery context in the owning workspace directory
* Preserve unknown account model facts and legacy option replay
* Clarify terminal account-switch feedback in translated locales
* Offer usage-limit Continue Now only once the limit resets
* Keep usage-limit reset timers current and bounded
* Keep option mutation plans checked against their contract
* Cover unknown model choices across chat consumers
* Keep consumer fixtures aligned with canonical types
* Match the mobile picker label in its consumer test
* fix: clarify terminal scope in translated Claude account settings
* Update integration tests for current session and remote client contracts
* Keep native worker replies on their receiving account
* Preserve receiving accounts across cold starts and nested reports
* Preserve the delivery receiver when reading chat commands
* Clarify native reply matching and consolidate imports
* Resolve catalog scope without re-entering conversation queues
Give owned option mutations and delivery settlement a resolved catalog input while external reads acquire scope once. Preserve account routing, catalog lifetime and current response obligations.
* Align catalog and mobile test fixtures with current contracts
Explain the existing timeout budgets and provide the idle turn identity and truthful model-only picker label in the mobile fixture.
* Align receiving-account readers and fixtures with typed contracts
* Repair account-switch fixtures and measure receiving work
* Use the checked account-home path in catalog tests
* Restore executed-option recorder context
* Control receiving catalog deadline test clock
* Align unknown-model consumer fixtures with send capture
* Consolidate session host type imports
* Align cross-version catalog fixtures with host read contracts
* test(native-chat): bind Runs through the current start authority in receiving fixtures
Main now resolves a worker start lazily through bindRun with objective and task references, and creating a Run no longer unbinds a coordinator's earlier Runs; run-use does. The receiving fixtures follow the same production owners so every original scenario, input and bound is exercised unchanged. The receiving runtime subclass moves verbatim into its own support module.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(native-chat): follow the selected account before its folder exists
A live Claude chat compared its account against the selected one only after that account's folder passed an availability check, so an account still being set up (or whose setup failed) left the chat silently answering on the previous account, and a resting chat refused its start before preparation could run. The retirement comparison and the start pre-read now ask the router for the selected route as identity, through the existing resolver purposes; catalog, create and probe reads keep checking availability. Preparation, typed launch failure and parking are unchanged.
Session History details show the account a chat was last launched under when the host recorded one, else its founding home.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(ai-vault): build the stale acquisition receipt without a type assertion
The History account case now reads the fixture receipt through the typed acquisition reader instead of spreading the record field through a cast; the three assertions and their inputs are unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): narrow the rig acquisition link before relinking it
The account-setup cases spread an optional link, which typed its id as possibly undefined; the rig always links a handle, so a guard that cannot fire narrows it. No assertion or input changes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): follow main's chat admission in fixtures and bound the History account projection
Chat admission no longer reads an experimental host setting, so the continuation-context test drops the spy main dropped in its own fixtures. The two History consumers that install a minimal chat host give their fake store the record accessor and host id the real store has, since the projection now reads the acquisition receipt. An operation-count contract holds the production History projection to one roster read and one record lookup per owned row at 10 and 100 rows.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): give the declared-effort catalog fake its capture port
The declared-effort fixture builds a catalog service without the capture member the options reader asks every catalog for, which failed its desktop, catalog and phone consumers; it now supplies the inert test operation like the other fakes. The History projection contract also drops an unused map parameter.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Consume main's outcome change feed in the receiving-account index
Main's keyed journal records deliver each change after an epoch's frozen prefix as an
outcome sequence naming touched keys instead of typed rows. The receiving-account index
now re-reads those keys at their current values: queue cards by id, a deleted submission
retires its exact notice, and a deleted item drives the existing recovery rebuild, with
Stop marks still excluded. The in-process read row exposes the already-persisted
queue.cards keys and per-key deletion facts; itemIds and submissionIds keep listing every
touched key. The mobile unknown-consumer test mocks MobileSwitch as main's picker test does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test: validate historical usage-limit fixture before rendering
* fix: scope handoff choices and share account observation
* fix: align account menu lifetime and local projections
* test: preserve account observation contracts in quality checks
* fix: preserve typed account observation project coverage
* fix: restore handoff feedback for empty host inventory
* test: name exported handoff mocks with public types
* test: follow settings navigation consumption in feature tip adapter
* Keep usage-limit recovery actions beside their lead-in
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Every JSON reply and stream event reached the phone as base64 of sealed
UTF-8, a third larger than the JSON itself, and terminal snapshots (up to
512 KB per attach) went out uncompressed. Opening a host on a real desktop
moved ~2 MB, about 17 s on a 1 Mbps link.
Phones now advertise transport.compressed-frames.v1. For those sockets the
desktop gzips text frames of 1 KiB or more, and terminal frames of that
size, and seals them as binary frames tagged with a new kind byte, the
payload kind and the inflated length. The phone inflates them (native
gunzip when available) back into the same text or binary frame before
routing. Screencast frames are never compressed, and the relaying desktop
never offers the capability to paired servers, whose frames it rewrites.
On the same desktop, opening a host now moves ~180 KB.
Desktop CPU is unchanged: gzip then seal of the 1.5 MB catalog takes the
same time as seal then base64 of the plain one. Setting
ORCA_DISABLE_MOBILE_COMPRESSED_FRAMES=1 on a desktop turns compression off
without a release. Older phones never advertise the capability and older
desktops ignore it, so both keep plain frames.
* Open native chats on the phone in place
* Preserve desktop host and agent when opening chats
* Preserve chat opening and captured lifecycle contracts
* test: cover sender focus and unconfirmed chat close outcomes
* test: exercise structured worker CLI lifecycle through runtime socket
* test: use checked parked mail observation in CLI integration
* test: validate provider notification correlation identities
* test: construct checked lifecycle and navigation fixtures
* Keep cross-program session proofs in their test program
* Localize mobile chat opening feedback and complete runtime contracts
* Use the mocked text component identity in History feedback proof
* Temporarily expose grown E2E type diagnostics
* Keep E2E timer and fetch fixtures in their runtime domains
* Reconcile decreased E2E wait pattern counts
* Preserve live test clocks and expose remaining type diagnostics
* Keep mobile session data types independent of native views
* Complete structured session wire surface fixtures
* Expose existing grown E2E type diagnostics temporarily
* Preserve checked wire fixture tab mock signatures
* Preserve worker redrive when automatic close is refused
* Honor automatic close admission before settlement and retirement
* test: retain History reveal and close publication race
* Match automation reveal callback to activation result
* Align CLI integration fixture with current settings and output
* Prevent retained chats from reopening removed workspaces
* Import notification types directly from their owner
* Unify local removal fences for accepted worktree aliases
* Keep cross-surface authority tests in the combined test project
* Initialize coordinator mail test workspace ownership
* Register the retained automation chat test workspace
* Use checked inputs in worker start failure test
* Preserve the automation test repository host literal
Home re-read the full worktree catalog for every connected host on each
focus, reconnect and change event just to show counts and a resume row.
It now keeps a snapshot per host and client, so repeat reads are
unchanged replies or row deltas. One read runs per host at a time, and a
refresh asked mid-read joins a single trailing read, so concurrent reads
cannot discard each other's delta and no refresh settles with older rows.
Home ranks and shows exact activity, so it asks for it: worktree.ps takes
exactActivity to skip poll coarsening, and acceptRowDelta alone now opts a
first read into snapshots. Home's first read sends no snapshot id, so a
host without row deltas answers its exact legacy catalog and every later
read stays legacy; home's data is unchanged on every host version.
* Stop only the identified Windows pane agent before chat continuation
* Stop terminal agents before continuing their conversations in chat
* Continue in chat without confirmation while stopping the old agent
* Wire Windows stopping through the execution host and preflight permissions
* Scope chat continuation admission to proven terminal survivors
* Continue in chat after ordinary terminal close and verified exit
* Continue in chat without a stop prompt for the agent and keep the user on the new chat
Only work in other panes of the tab asks before the close; the worktree stays selected so the chat shows when it opens; an open chat for the conversation is opened instead of closing the terminal; the pre-close check skips the rows the host skips and counts restored ones; the preparation deadline no longer runs while a close dialog is open; failure messages say what happened once and point to Session History.
* Remove terminal-writer bookkeeping nothing reads after store-only admission
The hook server no longer tags OpenCode and tmux conversation bindings with a writer topology, the admission check drops its unused source pane argument, and the hook that clears a saved launch result is named for what it does.
* Keep the hook server's writer-topology tagging unchanged in this change
Another open change still reads it, so its removal waits for that one.
* Keep the terminal when its agent already quit, and refuse only an agent that is still running
Continue in Chat UI closes the terminal tab only while the clicked pane still shows its agent on this conversation; otherwise the chat opens and the terminal stays, as before. The host now refuses a takeover only when the agent's process is proven running: an agent with no recorded process, one that reported its own end, or one that can't be checked no longer blocks Session History or a chat.
* Prove a paired terminal's exit even when the close reaches the host first
A terminal handle a pane issued now keeps naming its process after the pane is removed, until that process record is pruned or replaced, so a late exit wait reads the recorded exit instead of failing. Before closing for Continue in Chat UI, only an open chat for the conversation is answered; whether another terminal still holds it is left to the host. A run that was already retired says nothing when its old confirmation is answered.
* Shrink the flaky-test pattern baseline for two cleaned-up test files
* Merge a duplicate import in the chat-continue run
* Keep unshown terminals usable and say how to recover when another terminal holds the conversation
A terminal that no pane shows is listed, read and controlled through its own handle again, as before; the closed pane's handle still answers a late exit wait and nothing else, and only for a known process incarnation. After Continue in Chat UI closes the terminal, a conversation still running in another terminal names that terminal and the remedy. A continuation retired with no newer one for the same tab says once that the terminal changed when its confirmed close is refused.
* Fix test types and retire translations whose English changed
* Keep the terminal when its agent's status is only restored from before a restart
Continue in Chat UI closes the terminal only while its agent is confirmed running there; a status row carried over a restart, with no live update since, now takes the keep-the-terminal path.
* Revert "Keep the terminal when its agent's status is only restored from before a restart"
This reverts commit c19d708ecb.
* Expect the new other-terminal copy in the Session History continue test
* Point a still-running terminal refusal to Session History, which old hosts still offer
git status honors status.renames/diff.renames, but the numstat pass that
attaches line counts always forced -M. With rename detection off, status
listed an added and a deleted row while numstat reported one rename, so
the added row showed +0 and the deleted row had no counts.
Each staged/unstaged numstat pass now uses -M only when that area's
parsed status rows contain a rename, and --no-renames otherwise.
Fixes#36689
Co-authored-by: Neil <neil@stably.ai>
* fix(plugins): wire the plugin service on a headless serve, and turn plugins on from the CLI
* test(plugins): build the switch test's RPC context inline after main dropped the shared helper
---------
Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
orcad gives its Electron browser sidecar a fresh mkdtemp /tmp/orcad-browser-*
profile on every start and removed it only on a clean stop, so every crash,
kill or forced restart leaked one more profile in /tmp, and sometimes a live
sidecar too. orcad now records the profile it creates in its own data root.
On the next start it stops a sidecar still running on the recorded profile,
once that sidecar proves who it is with its own auth token, and then removes
the profile, but only if it is still a private directory this user owns. The
profile stays a short, unpredictable /tmp name, because the sidecar binds its
runtime socket inside it and the macOS updater matches that name.
Refs STA-12725
A local reattach that the terminal daemon answers with
SessionNotFoundError now resolves as sessionExpired, matching the SSH
expiry path, so the pane retires its binding and respawns without the
"couldn't reattach" toast.
Fixes#36618
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
* fix(files): keep staged saves reserved through commit
* fix(files): close staged-save commit races
A chunk after commit starts recreated the temp once commit deleted it. Abort during that commit also freed the capacity before the guarded write finished.
* fix(files): hold a staged save until abort cleanup
A disconnect during begin released the reservation before setup created the temp. Another begin could take that slot while both temps existed.
* fix(files): release a failed setup after temp cleanup
A rejected create freed the reservation before the temp was removed. Another begin could take that slot while the file was still there.
---------
Co-authored-by: Adolanium <94890352+Adolanium@users.noreply.github.com>
A working agent's heartbeat changes the catalog snapshot id on nearly every
3 s poll, so the host screen re-downloaded the whole catalog each time
(1.5 MB for ~1000 workspaces, ~26 MB/min on a real desktop). Callers that
hold the base snapshot now opt in with acceptRowDelta and receive only the
changed rows; unchanged rows arrive as indexes into the base catalog.
The host keeps only row hashes for the 16 most recent snapshots, so an
evicted base falls back to the full catalog. Older hosts ignore the
parameter and older phones never send it.
The manifest schema requires `title` (and rejects `label`) on theme and
syntax contributions, so the docs examples now match it.
Fixes#36696
Co-authored-by: Neil <neil@stably.ai>
A repo id registered on two hosts could still open the composer on the
wrong host from some single-host views: create pinned the host only for
headers inside host sections. One predicate now decides it everywhere: a
project header with rows that all sit on its own repo's host pins that
host. A header still spanning hosts, or one with no rows, whose repo is
just the registration repoMap kept, keeps the id path.
The host label read the owner from repoMap, which keeps one registration
per id, so a twin's header could name the other host on a name
collision. A host copy now names its own host, and a header whose rows
span two machines stays unlabelled; aliases of one machine count once.
Refs #35741
* fix(wsl): a stopped distro's messages say Start or a new terminal starts it, not switching projects
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): translate the WSL hosts description, and say Start for a stopped host when it isn't connected
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): a project switch never boots a stopped WSL host; Start does, once
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(editor): copy blob-URL SVGs with HTML labels as PNG
Chromium taints a canvas drawn from a blob-URL SVG that contains foreignObject,
so Copy as PNG failed for local Markdown SVG images with HTML labels. Rasterize
those through a data URL, which Chromium does not taint.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(editor): drop type assertions from the PNG taint test
---------
Co-authored-by: Claude <noreply@anthropic.com>
`serve service install` copied the caller's PATH into the systemd unit or
LaunchAgent. Run from the AppImage, that PATH starts with the launch's
temporary /tmp/.mount_* directory (and $APPDIR/usr/sbin), which is gone by
the service's next start; another process's mount could later take the
same name.
The unit PATH now drops every directory the service command lookup
already refuses as tied to one Orca version or AppImage launch: profile
and orcad artifact dirs, the AppImage FUSE mount, and extract-and-run
copies (appimage_extracted_*), plus relative entries. AppImage mount
detection now lives in one shared helper used by the service command
check, the unit PATH and the daemon host-image probe; the command check
previously missed extract-and-run copies.
Fixes#36000
* End multiple PTYs without blocking confirmation dialogs
- Add virtualized multi-selection and bounded concurrent termination
- Show per-terminal errors while ending continues in the background
* Batch PTY termination updates to keep the Resource Manager responsive
- Share four termination slots across all End actions
- Coalesce completion and exit publications to reduce render churn
- Preserve focus when virtualized terminal rows are ended
* Defer PTY inventory refresh until termination batches drain
* Remove obsolete terminal end confirmation translations
* Allow PTY end strings to be localized
* Refine terminal list row spacing and remove redundant icon
* Remove extra left padding from PTY details button
* fix(editor): render formatted Mermaid labels safely
Enable Mermaid HTML labels everywhere the desktop renders diagrams and
sanitize with foreignObject XHTML kept, form controls and SVG filters removed.
* fix(editor): copy HTML-label diagrams as PNG
Chromium taints a canvas drawn from a blob-URL SVG that holds foreignObject labels; the viewer now loads the diagram from a data URL.
* test(editor): share one Chromium across the Mermaid sanitizer suite instead of per-test timeouts