Commit Graph
4310 Commits
Author SHA1 Message Date
Neil ff0b418e31 fix(wsl-host): a pane parked by a stopped host reopens once Start brings the host back (#36784)
* fix(wsl-host): a pane parked by a stopped host reopens once Start brings the host back

* fix(wsl-host): clear only the returned host's stopped refusal; test the strip and the bridge
2026-10-31 21:38:36 -07:00
Neil fbb3381dfe fix(mobile): give a direct handshake as long as the desktop allows it (#36864)
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.
2026-10-31 21:22:55 -07:00
OrcaWin de74d6b47a test: remove 317 low-value tests, including 31 suites (#36877)
* test: remove 317 low-value tests, including 31 suites

* test: prune obsolete wall-clock baseline after removals
2026-11-01 04:20:23 +00:00
Neil 5913a1c663 refactor: group shared agent-session options and catalogs (#36878) 2026-10-31 20:45:48 -07:00
Neil 56eb859595 perf(mobile): answer the host screen's repo poll unchanged when repos stay the same (#36860)
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.
2026-10-31 20:31:11 -07:00
Neil b2842603e4 refactor: finish shared native-chat model and transcript organization (#36843) 2026-10-31 20:19:28 -07:00
Neil ace65c1868 perf(mobile): send paired-server workspace polls as row deltas (#36859)
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.
2026-10-31 20:14:02 -07:00
OrcaWin 7ec9a6b1fc test: remove 247 low-value tests, including 22 suites (#36857)
* test: remove low-value suites and cases

* test: update wall-clock ratchet after low-value test removals
2026-11-01 02:54:00 +00:00
Neil 14d29b8d24 feat(themes): background images for app themes (#36589)
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.
2026-10-31 19:45:04 -07:00
Brennan Benson aa3979d240 Agent message cards keep their sender's agent icon when the agent can't be found (#36653)
* 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
2026-10-31 19:42:38 -07:00
Jinwoo Hong 0668ba1a33 Orchestration: fix agent instructions that misdirect workers and coordinators (#36803) 2026-10-31 22:40:10 -04:00
NeilandClaude daa11df406 feat(themes): public tokens for diffs, git status, tabs, dividers, rows, links (#36578)
* 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>
2026-10-31 19:20:51 -07:00
Neil 82ac952ad7 feat(editor): one typography resolver for every Monaco surface + opt-in weight, line height, ligatures, size and macOS smoothing (#36577)
* 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
2026-10-31 19:20:48 -07:00
Brennan Benson 3601c5ac15 New tab menu: agents keep their usual order whether set to Chat or CLI (#36840)
* 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.
2026-10-31 19:08:37 -07:00
Brennan BensonandClaude Fable 5.1 2dcb0b70d0 Continue a usage-limited chat with another account or agent (#31241)
* 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>
2026-10-31 19:06:53 -07:00
Neil df2ddd9d4b perf(mobile): gzip large frames for phones that read them (#36801)
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.
2026-10-31 19:02:33 -07:00
Brennan Benson 7e2f29aaf1 Open native chats on the phone in place (#36315)
* 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
2026-10-31 18:57:24 -07:00
Neil 5c3938ce76 perf(mobile): read home worktree counts through catalog deltas (#36800)
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.
2026-10-31 18:51:45 -07:00
Brennan Benson af211d6b82 Close the terminal before continuing its conversation in chat (#36732)
* 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
2026-10-31 18:47:18 -07:00
BingZandNeil 2fbeb96129 fix(git): keep line counts aligned with status rename detection (#36698)
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>
2026-10-31 18:31:05 -07:00
NeilandJustas Brazauskas 5913998402 fix(plugins): wire the plugin service on a headless serve, and turn plugins on from the CLI (#36838)
* 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>
2026-10-31 18:26:07 -07:00
Neil 135b72c8ba refactor: colocate native-chat reasoning-order contract spec (#36837) 2026-10-31 18:21:38 -07:00
Neil a2a81cbf0a fix(theme): built-in color pairs meet WCAG AA and dark-mode surfaces stay themed (#36561) 2026-10-31 17:57:30 -07:00
Neil 2270f7b08c refactor: colocate native-chat noise filtering (#36824) 2026-10-31 17:55:46 -07:00
Neil f193548367 perf(mobile): send worktree catalog polls as row deltas (#36799)
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.
2026-10-31 17:53:27 -07:00
Neil 31276a446a fix(sidebar): resolve a header's create host and host label from the header itself (#36826)
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
2026-10-31 17:45:22 -07:00
Neil 51f157ed18 refactor: colocate native-chat agent profiles (#36820) 2026-10-31 17:21:42 -07:00
Neil d56fdaa4a1 fix(serve): a service installed from the AppImage keeps its launch out of PATH (#36819)
`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
2026-10-31 17:15:16 -07:00
Jinjing 21accb2316 End multiple PTYs without blocking the Resource Manager (#36753)
* 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
2026-10-31 17:12:51 -07:00
Neil 6fb044b51d feat(mobile): draw SVG files and pinch-zoom every image preview, Android included (#36608)
* feat(mobile): draw SVG files with a Preview/Source toggle; pinch, drag and double-tap to zoom images

* test(mobile): render an SVG preview under the shipped shell policy and pinch it

* fix(mobile): double-tap after a drag at fit zooms in instead of re-fitting

* fix(mobile): pinch follows the fingers, Back resets zoom, and writable SVG artifacts get a preview

* fix(mobile): keep a writable SVG's save failure visible in Preview
2026-10-31 16:52:05 -07:00
Neil bbbe81be93 refactor: colocate native-chat agent support (#36816) 2026-10-31 16:51:05 -07:00
Neil 94835244a2 fix(wsl): fail closed on an unknown default distro, stop no-start connects on a stop, and say unknown when WSL can't answer (E9) (#36797)
* fix(wsl): fail closed on an unknown default distro, fence no-boot deploys, and say unverifiable when WSL can't answer

- A background wsl.exe run with no distro named reads the current default from the registry; when it
  can't be read, the run is refused as unverifiable instead of allowed.
- A no-boot connect stops before its next guest command once a stop of the distro is heard mid-deploy.
- Background UNC touches trust a running read for 15 s, not 60 s.
- An unreadable running list is a retryable "couldn't confirm" on connect, and "Unknown" in the Add
  dialog, never "not running" or "stopped".

* fix(wsl): one default distro per user act, cached registry read, adopted-holder watch, locales

- The default distro comes from one resolver: Lxss first, `wsl --list` when the registry is blocked,
  cached for 5 s and single-flight, invalidated by a list change. A user act pins its answer, so the
  hook relay and every gate inside the act name the same distro.
- A fenced no-boot connect that adopted a previous run's holder polls its process-table row (no
  wsl.exe); the holder found gone counts as a stop.
- The Add dialog's request is a tested helper: Unknown and stopped distros are added with an explicit start.
- New strings translated into the ten locales.

* test(wsl): work an unnamed act leaves behind reads the default fresh after the act settles
2026-10-31 16:32:00 -07:00
NeilandClaude 91c9200613 feat(editor): SVG preview + source, and a drag-to-pan image viewer (#36576)
* feat(editor): open SVG as editable source with a vector preview; pan images by dragging

- .svg opens as text with Preview | Source | Changes. Preview sizes the drawing
  from its width/height/viewBox instead of Chromium's 300px default, fits up to
  fill the pane, and stays sharp at any zoom.
- The image viewer pans by dragging (grab cursor) and by wheel, zooms at the
  cursor, and gains Fit and Actual size. Clicking no longer opens a popup.
- Combined-diff images expand into the shared pan and zoom viewer.
- Text plugin editors for .svg now receive the markup.
- The plugin page warns when an editor opens files in place of Orca's viewer.

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

* fix(editor): measure the pan-zoom viewport from the observer's first report

* i18n: translate the image viewer and plugin takeover strings

* feat(editor): copy expanded images and diagrams as PNG

* i18n: drop strings for the removed image popup and reset zoom

* fix(editor): keep a zoomed image inside the pane after a resize

* fix(editor): address cross-review of the SVG and image viewer

- A host that can only decode an SVG lossily keeps the image path, so non-UTF-8 bytes survive.
- Bytes custom editors stay available on SVG files, now that they open as text.
- A reminted media URL keeps the image's size and zoom.
- A vector fitted far above its natural size can zoom out to actual size.
- Zoom commands that arrive before an image loads are applied once it does, keeping diff sides in step.
- Copy as PNG requests CORS for web images so the canvas is not tainted.

* fix(editor): drop the SVG bytes-editor route and lossy-text fallback

Keeping a bytes editor beside the Source text let two drafts compete, and the
U+FFFD check could not tell lossy decoding from a real replacement character.
SVG now reads like every other text file.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-31 16:26:24 -07:00
Neil 7dbe93149d refactor: colocate structured session live reads and reconnect scheduling (#36807) 2026-10-31 16:20:18 -07:00
Neil 964fe5b73c fix(wsl): a restored chat or a web hooks check never starts a stopped distro (E9) (#36779)
* fix(wsl): a restored chat or a web hooks check never starts a stopped distro (E9)

A chat pane mounting on launch restore or a remount read its transcript as a
user open and booted the transcript's stopped WSL distro. Only the mount that
the user's own switch caused, and paging, now ask main to start it; otherwise
the read refuses and the pane shows the distro's stopped copy at once (a
stopped UNC transcript was reported as missing and spun as pending).

A web client sent repo.hooksCheck without background, which a server treated
as a launch that may open the distro. The server now opens it only for an
explicit wakeWslDistro; clients send it for launches and keep background for
older servers.

* fix(native-chat): key a chat's open wake on its own workspace; update wake assertions

A chat in another workspace mounting inside the switch window no longer takes the
switch's wake. The paging and worktree-removal assertions now expect the explicit
wakeWslDistro flag.

* fix(native-chat): set the pane-key ref in the layout effect, not during render
2026-10-31 16:17:55 -07:00
Neil 1526c7e041 refactor: group agent-session background-task modules (#36798) 2026-10-31 15:52:18 -07:00
NeilandClaude f2731a2cfc feat(wsl): a WSL hosts block learned mid-run stops new work at once (#36776)
* feat(wsl): a WSL hosts block learned mid-run stops new work at once

The live policy fetch caches a newly published block, but the gate is
fixed for the run, so the add-host and move entry points and the
in-context offer stayed until the next launch. A block the fetch learns
now stops new work in-session through one predicate,
wslHostsAcceptNewWork(): adding hosts, starting or previewing moves,
automatic and opened-project moves, and Retry. Main pushes the capability
to the renderer, which hides the offer and entry points. The gate and
what is served are unchanged until the next launch, so saved hosts and
moved projects keep working, and cancel, Restore and Not now stay.

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

* fix(wsl): a live block also ends Stage 1 opt-in and stops mid-step host adds

Review follow-ups:
- wslHostsOptedIn now also needs wslHostsAcceptNewWork, and the Git router
  re-applies its opt-in when a block lands, so the D1c router and the
  background WSL Git gate uninstall at once, as turning the setting off does.
- D1c conversions (wslDriveProject preview/convert, wslDriveUnc:convert)
  gate on the opt-in; the new-project Git default already did.
- The orchestrator re-checks before adding a host, so a step already past
  its start gate adds nothing after a block.
- The window broadcast and capability push move to
  wsl-host-window-broadcast.ts, keeping wsl-host-handlers.ts under max-lines.

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

* test(wsl): keep gate mocks whole, and pin cancel after a live block

The visible-work lifetime suite mocked the gate with one export, so the
handlers' new wslHostsAcceptNewWork and onWslHostsNewWorkChanged calls
failed it; it now spreads the real module and overrides only what it
needs. A new case pins that cancelling a running move still works after
a live block while a new move is refused.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-31 15:45:48 -07:00
Neil ca07c135f9 fix(wsl): Stage 1 is opt-in: the offer moves only its project, and D1c Git follows the setting (#36773)
* fix(wsl): Stage 1 is opt-in: the offer moves only its project, and D1c Git follows the setting

* fix(wsl): the WSL path access-denied explanation follows the setting too
2026-10-31 14:39:27 -07:00
NeilandClaude 19a73ff0b4 fix(wsl-host): keep a host's launch markers out of its shells; a corrupt WSL store hides only WSL hosts (#36772)
- ORCA_WSL_ORCAD_HOST and ORCA_WSL_DISTRO_GUID joined the server launch env both pane spawn
  paths scrub (one shared list with the activation root and idle window), so a user's own orcad
  started in a host shell no longer acts as a WSL host.
- An unreadable orca-wsl-host-deployments.json (corrupt, or a newer envelope) used to fail the
  whole server list closed while hosts were served, hiding every SSH and paired server. It now
  hides only the servers that could be WSL hosts, logs why, and writes still refuse the file so
  a newer build's copy is kept.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-31 14:39:16 -07:00
Neil 76e5e94b23 refactor: colocate structured session tool-call projection (#36770) 2026-10-31 14:25:20 -07:00
Neil f0a1ae3fe6 refactor(shared): colocate compaction and command entry owners (#36738) 2026-10-31 14:00:35 -07:00
Neil ee44140fb9 fix(terminal): scope terminal color overrides to their light/dark theme slot (#36581) 2026-10-31 13:12:56 -07:00
OrcaWin 19d6957127 test: remove 272 low-value tests (#36750) 2026-10-31 19:36:52 +00:00
Jinjing 474e48a306 fix(shortcuts): avoid Plugins strikethrough shortcut conflict (#36741)
* Change Plugins shortcut defaults by platform

* Test Plugins shortcut matching on all platforms

* Test current Plugins default in browser shortcut forwarding
2026-10-31 12:27:32 -07:00
Brennan Benson cca9ed5a7a fix(github): make Checks refresh start a new PR read (#36651)
* fix(github): start explicit Checks refreshes after the action

* fix(github): give explicit Checks refreshes fresh checks and comments

The same Refresh or Link click that now reads PR metadata after the
action also reads checks and comments after it: an explicit request no
longer joins or waits on a read begun before the click, in the renderer
or (for checks) the host coalescer. A superseded read's callers show the
owner's answer, so a late pre-click reply cannot repaint the panel, and
comments posted during either read are kept.

A lost IPC/RPC reply is reported only to the click that asked; polls stay
silent as before, and a later answer clears a failure older than itself.
The manual hook takes its verdict from its own lookup, the link flow's
card-read failure no longer skips checks, and the web shim forwards the
intent.

* fix(github): land the older Checks reply when a newer explicit read fails

When a click's checks or comments read fails while the read it replaced
succeeds, the click's read now lands that older reply through its owner
path instead of leaving the cache behind it. Post-write comments
refreshes (mutation events, bulk resolve) carry the explicit intent so
they cannot be answered by a read begun before the write.

* test(github): drop type assertions from the explicit-refresh tests

The RPC tests call the handlers with a real runtime service and spies,
and the panel test passes typed fixtures straight to the hooks.

* test(github): type the Checks panel fixtures for the renderer project

The renderer tests build their PR fixture locally instead of importing a
main-process harness, and the panel setter mocks take the hooks'
SetStateAction form.
2026-10-31 11:10:38 -07:00
Neil df9ad9da84 refactor(shared): colocate session failure detail readers (#36727) 2026-10-31 11:05:39 -07:00
Neil 24d1d10da8 refactor(shared): colocate session thread-goal contracts (#36720) 2026-10-31 10:30:42 -07:00
Brennan Benson c64e1f615a Preserve queued command and card order through recovery (#36430)
* Advertise mid-turn queueing as queued-messages.v2

Released clients read the v1 name with an unfinished queue UI; they never
see v2 and keep sending immediately.

* Let each desktop send choose queue or steer while the agent works

The follow-up choice is now a Queue / Steer setting saved under
nativeChatFollowUpBehavior, Steer by default; the old
nativeChatQueueFollowUps switch, which every install saved as true
while queueing was dark, is retired on load. Every reader goes through
native-chat-follow-up-behavior.

While the agent works, Enter follows the setting and Cmd/Ctrl+Enter
sends the draft the other way; the alternate flips the setting before
the queue's own rules (a waiting command still queues, prompts no one
here can answer still send now) and never applies while Stopping.
Cmd/Ctrl+Shift+Enter steers the head card only and leaves the draft.
An idle agent's card reads Send, and the card menu no longer offers
"Turn off queueing".

* Hold Send on the phone to queue or steer a message mid-turn

While the agent works on a host that queues, holding Send opens a
Queue / Steer sheet; a choice other than the host's setting sends the
alternate, resolved against the chat as it is when it lands, by the
desktop's rule. Release and dismiss send nothing. The phone reads the
host's new follow-up setting (absent reads as Steer), and an idle
agent's card reads Send.

* Read the follow-up default from one place on desktop and phone

* Keep phone follow-up choices aligned with live host state

* Update stream and wire fixtures for live follow-up preferences

* Document minimal runtime test fixture assertions

* Keep mobile preference tests in the mobile compiler project

* Preserve queued input order through handover and recovery

* Consolidate input selection type imports

* Keep queue request capability types in the shared compiler graph

* Preserve reopen admission context and bound queue projection work

* feat(native-chat): queue follow-ups by default with a toggle

* fix(tests): preserve projection contracts in the node compiler

* fix(native-chat): retain receipt lookup after journal replacement

* test(mobile): assert shared preference renders on real changes

* Remove obsolete follow-up option translations

* Preserve input places across journal recovery without predecessor snapshots

* Keep recovered card placement tied to the exact handoff

* Preserve replay source types and correct verification fixtures

* Complete indexed queue selection and recovery ordering

* Preserve queued input order across recovery and source replay

* Fix legacy queue recovery fixture types

* Use a specific name for queue pause inputs

* Preserve fallback queue identity across journal recovery

* Place nested storage negative directive on its constrained call

* Constrain journal draft property type parameter

* Remove journal input order import cycle

* Preserve lazy journal counter reads within line limit

* test: preserve legacy conversion and display work contracts

* test: align journal refusal and restore accounting contracts
2026-10-31 10:17:56 -07:00
Brennan Benson de192f1e96 Native chat: Stop turns into Send while you type a follow-up, with a shortcut hint (#36655)
* Turn Stop into Send while the agent works and a draft has text

While the agent works, the desktop composer's Stop button becomes the
ordinary Send button once the draft holds text or images, and returns to
Stop when the draft is empty. It sends like Enter, so it follows the queue
setting; Esc still stops. Its tooltip names what Cmd/Ctrl+Enter does
instead (steer when queueing is on, queue when it is off), read from the
same resolver the send uses, so it is absent wherever the shortcut would
send no differently.

The Send/Stop decision moves into native-chat-composer-send-state so the
pane stays within its size limit.

* Tie alternate delivery hints to the composer submission

* Combine duplicate composer test imports

* Return snapshot results from composer option fixture
2026-10-31 10:17:31 -07:00
Neil 4365547899 refactor(shared): colocate legacy session child-work projection (#36711) 2026-10-31 09:48:29 -07:00