Commit Graph
15292 Commits
Author SHA1 Message Date
Neil ba6599eabb fix(ssh): pair again over SSH when a managed server drops this desktop, instead of reconnecting forever (#30775)
* fix(ssh): say the host dropped this desktop's pairing, not that another Orca holds the port

* fix(ssh): pair again over SSH when our own managed server refuses this desktop's token

* fix(ssh): key re-pairs by the refused revision, cap attempts, retry on explicit connect

* fix(ssh): keep a newer pairing stored mid-repair, count only completed reads, and offer Connect, not an access link, for managed servers

* fix(ssh): judge a re-pair only against the pairing still stored; Check connection retries a managed server's pairing

* fix(ssh): a pane whose managed server refuses this desktop says why and offers Reconnect, instead of spinning

* test(e2e): live failure path for a refused managed pairing

* fix(ssh): a Connect that re-paired answers from the new pairing, not the retired one

* fix(ssh): only an explicit Connect lifts a managed re-pair's hold and cap; say SSH access can pair again

Startup restore reaches ssh:connect with automatic: true and went through the same
ensureTunnel that cleared the hold, so repeated automatic reconnects could bypass the
three-attempt cap. The reset now lives in connectTarget's explicit branch.

The Shared Server Access footnote now says revoking there doesn't stop someone with
SSH access to this computer from pairing again.

* ci(e2e): route the managed pairing-repair spec with the managed-host remote sources

* fix(ssh): the repair gate decides whether a managed tunnel builds, not the build

A refused shared-control open rebuilt the tunnel (SSH execs, forward, handshake, wake)
before the held failure or the attempt cap was consulted, so reopens on a blocked
server rebuilt without limit. buildWithPairingRepair now asks the gate first: a failure
stands for the refused pairing revision for 15 s, or through the window once the cap is
spent, and every caller gets it without reaching the host.

- A connect during an in-flight repair keeps its reset (per-environment reset epoch).
- An unpaired answer fails at once; it is not a moved port, so no re-read or wake.
- A refusal that only rebuilt the tunnel no longer reports "pairing changed".
- WSL and cloud VMs no longer say Orca tried to pair over SSH, and Check connection
  only re-pairs servers managed over SSH (runtimePairingRepairRoute).
- Translate the new strings; route the pane-banner files to the managed-SSH e2e lane.

* fix(ssh): fence a status re-pair follow-up to the same server, and hold a refused repair under its own revision

- getRuntimeEnvironmentStatus followed any pairing change mid-probe, past a caller's
  expectedEnvironmentPairingRevision fence. It now follows only for an unpinned caller and only
  a new pairing of the same SSH-managed server under its pinned host key.
- recordRefusedAfterRepair stood under whatever revision was stored now, so a newer pairing
  stored mid-refusal was blocked untried; it stands under the repaired revision.
- The WSL restart test expects the refused-pairing classification main's WSL path now reaches.

* fix(ssh): a re-pair hands its callers the new credentials, and a failed one judges only the pairing it tried

- ensureOrcadManagedTunnel returns the environment as stored once the tunnel stands. The
  terminal census and a deploy's already-current result use it, so after a re-pair they
  no longer authenticate with the refused token, which read as an unverifiable census and
  refused restarts, updates and stops.
- The shared-control refusal handler blocks the status owner only while the failure stands
  for the pairing stored now (managedOrcadPairingFailureStands). A newer pairing stored
  meanwhile keeps its owner and retries.

* fix(ssh): migrations commit with the pairing the tunnel stands on, and a Connect mid-handshake stays reset

- Conversion binds the environment ensureOrcadManagedTunnel returns, so the destination, the
  server project reads and the result use the re-paired credentials (the migration mark is kept).
- A delta move gets its destination only from connectDestination, which opens the tunnel and
  builds the destination from what it returns; a pre-ensure destination can no longer exist. A
  tunnel that cannot open keeps the delta journaled and the host marked for the next move.
- buildWithPairingRepair takes the reset epoch when it starts, so a refusal after the re-pair
  cannot reinstate a failure that a Connect pressed during the handshake cleared.

* refactor(ssh): one module owns a shared control socket's environment policy, keeping request-connections under its line limit
2026-10-19 22:45:25 -07:00
Neil e278bc9a17 feat(orcad-migration): unsaved drafts stay on this desktop and are renamed to the server on commit (#30894)
* feat(orcad-migration): unsaved drafts stay on this desktop and are renamed to the server on commit

* fix(orcad-migration): refuse clashing drafts from the rename's own collection; stricter live spec

* fix(orcad-migration): stamp renamed drafts with the server, rename at startup after a cut-off commit, keep draft refusals

* fix(orcad-migration): retain only after a startup rename is durable; startup survives an unreadable journal

* fix(orcad-migration): a cut-off commit is renamed and retained at startup, so the rename runs once

* fix(orcad-migration): one retain per migration at a time; no rollback that could drop newer edits

* test(e2e): check the visible draft in whichever editor a large file opens in

* fix(orcad-migration): startup retains only migrations of hosts this profile owns

* ci(e2e): route the editor-drafts migration spec to the sources it covers
2026-10-19 22:44:43 -07:00
Jinwoo HongandE2E Test d47e3de088 Three small fixes in the layout-owner build: repo reads, dragged-out pane folder, cancelled chat reader (dark, layout-owner build only) (#31003)
* Main routes a floating terminal or removed project without reading every project (dark, layout-owner build only)

In a layout-owner build, finding a worktree's save partition checked for a
project registered on two machines by listing every project on every call,
including ids that cannot belong to a project. Skip that list for ids with no
registered project, and reuse the list a hydration pass already read.

* A dragged-out pane with no shell yet opens its new tab in the pane's folder (dark, layout-owner build only)

In a layout-owner build, main builds the dragged-out pane's new tab and the
window shows main's tab, so the window's folder for the pane never reached it
and the new shell started in the source tab's folder. The window now sends the
pane's folder with the move as an optional field, and main uses it when the
moved pane has no terminal yet. An older main drops the field and keeps today's
folder.

* A chat cancelled while it is being created stays closed when the late reply arrives (dark, layout-owner build only)

In a layout-owner build, cancelling a new chat on this computer closes it on
the host without writing a cancellation record, and the host still answers
the in-flight create as successful. That answer then rebuilt the closed chat's
conversation reader, which nothing would ever close. The cancel now notes the
closed chat in memory, and the create answer skips the reader for it. No
message between the window and the host changes.

---------

Co-authored-by: E2E Test <e2e@test.local>
2026-10-20 01:39:20 -04:00
JinjingandClaude 7b1e73f3ea Show short Antigravity pool labels in status bar (#31007)
* Show short Antigravity pool labels in the status bar

Antigravity pools rendered their full "<group> · <agy window name>" in the
status bar. The host now publishes structured group/windowName fields on each
bucket, and the renderer labels pools as short group + window length
("Gemini 5h", "Claude/GPT wk"), with a reset countdown once a pool is urgent.
Old hosts without the fields keep today's full names; the click panel and
tooltips are unchanged.

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

* Keep Antigravity status bar reset countdowns current

* Preserve rate-limit pool identities in status labels

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 22:32:04 -07:00
Neil 937d2d3806 Share installed desktop and browser assets (#31010)
* Share installed browser assets with the desktop renderer

* Update packaged runtime fixtures for shared renderer assets

* Verify retained cloud clients in the shared renderer layout
2026-10-19 22:31:59 -07:00
Neil 84a4526508 Overlap macOS notarization with serial architecture staging (#31006) 2026-10-19 22:31:50 -07:00
Neil 8b70b726e9 fix(sidebar): a pin's reveal follows only the focused copy, by its own host (#31017)
* fix(sidebar): a pin's reveal and a native drag's source lane follow the card's own host

Pinning a same-id twin no longer scrolls and flashes the focused card, and the reveal names the
focused copy's host. A native drag reads its source lane from the card's own row instead of the
first lane holding the id.

* fix(sidebar): the lineage reveal walks the focused copy's host; drop the inert native-drag change

* fix(sidebar): a server-proxied focused row still follows its pin, revealed by its own host
2026-10-19 22:28:16 -07:00
Neil 1466deae45 Share identical packaged ripgrep binaries across server targets (#31005) 2026-10-19 22:16:37 -07:00
Jinwoo Hong 0059d062ea fix(relay): keep a host in its region when the database lane is full and a reserve cell has room (#30974)
* fix(relay): keep a host in its region when the database lane is full and a reserve cell has room

A host that lost the reserve share draw went to the database lane, which
spills to another region when its own region's database cells are full,
even while an in-region reserve cell had free seats. The database lane now
requires the region when an in-region reserve cell has room; if the region
is full it books that reserve cell, and only spills when neither has room.

* fix(relay): pace a throttled in-region reserve cell instead of spilling, and decide reserve room at plan time

With the region's database cells full, a reserve cell with seats but no intake
budget fell through the placer's database-cells shortcut and the host went
abroad. That placement now answers pace (503 with Retry-After). regionReserve
is a single callback, present only while an in-region reserve cell has a seat.

* fix(relay): pace only on a reserve cell that lacked budget, not one that refused

A cell that refused this placement (full, unreachable, seated-newer) got its
token back and drove a 1 s Retry-After forever while the map still showed a
seat. Pacing now skips cells refused for anything but intake, so those
placements fall to the database lane's cross-region fallback as before.
2026-10-20 01:16:26 -04:00
Neil acc6354363 Shrink feature tour animations without changing pixels or timing (#31012)
* Shrink feature tour animations without changing pixels or timing

* Update README media links for lossless animation assets
2026-10-19 22:11:56 -07:00
Neil 09fd0bec5a Exclude cloud service sources from desktop packages (#31004) 2026-10-19 22:03:36 -07:00
Neil 85e690ff60 fix(provider-process): kill a provider group its SIGKILLed supervisor leaves behind (#30997)
* fix(provider-process): kill a provider group its SIGKILLed supervisor leaves behind

* fix(provider-process): re-read a provider start in its read second; poll instead of sleeping in tests

* fix(provider-process): keep the report channel through the Codex session adapter; re-prove same-second starts

* fix(provider-process): never kill a leaderless provider group; list guard files in the CLI project
2026-10-19 22:03:22 -07:00
Neil d6707ff090 Give Space rows a dedicated actions menu (#31000)
* fix(space): separate workspace actions from row state

* fix(space): remove retired inline-delete label
2026-10-19 21:49:07 -07:00
Jinwoo HongandE2E Test 23d68112df Restored terminals named only on their tab reattach after a restart (dark, layout-owner build only) (#30986)
* fix(layout): restored terminals named only on their tab reattach after a restart (dark, layout-owner build only)

In a layout-owner build, a terminal recorded only on its tab (an older session, or an SSH tab
whose relay session was never visited) started a new shell after a restart and orphaned the old
one, and an unvisited SSH tab lost its relay session id on the next save.

- Main's Loader binds a one-pane tab's own terminal to its unbound pane in a workspace that was
  live at shutdown, as the window's reconnect does, so main's frames and saves hold it.
- The window's reconnect writes the restored row terminal again: ptyId is a field the frame merge
  leaves to the window, so main's frames never set it on a restored row.

* fix(layout): main binds a restored tab's own terminal once at load, not on every Loader run (dark, layout-owner build only)

The Loader re-derived the binding on each load from fields the window rewrites live, so an exit
main recorded could be undone. A switch-gated Store load repair now binds a one-pane tab's own
terminal to its unbound pane, as the window's reconnect does, and main's saves keep it. It skips
a terminal another mounted pane or a tab-bar tab holds, and a paired server's partition.

* fix(layout): an SSH workspace's restored tab binds only its relay session id; repair runs after the load purge (dark, layout-owner build only)

* test(layout): the reconnect gate test expects the row ptyId write; cover the repair's purge order and SSH repo predicate

---------

Co-authored-by: E2E Test <e2e@test.local>
2026-10-20 00:41:41 -04:00
Neil 00d25d1ee5 Clarify automation run CLI results and follow-up targets (#28262)
Report the execution host's accepted, launching, started, skipped, failed, or completed status and print the available run/workspace/terminal identities. Add safe same-target follow-up guidance and complete UUID examples. Preserve the existing JSON envelope and run fields while adding result.runId.

Keep scheduling, persistence, request count, and SSH owner fencing unchanged. Isolate CLI output tests from inherited connection selectors.

Fixes #27542
2026-10-19 21:31:13 -07:00
Neil a267d9f843 fix(runtime): a moved shell history waits only on this machine's shells; a phone never clears a twin's unread flag (#30991)
* fix(runtime): a moved shell history waits only on this machine's shells, and a phone clears a server-owned copy's own unread flag

The migration's live-shell check counted a same-id SSH twin's pane, so a local history could wait
indefinitely. readAndHoldPhoneSession read a runtime: host as this machine and cleared the local
twin's record instead.

* fix(runtime): a phone clears neither copy of a twinned workspace; the history wait reuses the PTY host fence
2026-10-19 21:29:44 -07:00
Neil 67eb94a918 perf(release): overlap server checks and share the mobile bundle (#30990)
* perf(release): overlap server qualification and reuse mobile bundle

* fix(ci): preserve draft suppression for independent compatibility checks

* test(release): update template download cache policy contract
2026-10-19 21:29:11 -07:00
Neil c09af9707b fix(native-chat): say which request a chat start ran out of time on, and log the original error (#30992)
* fix(native-chat): name the request a chat start ran out of time on, and log the original start error

A start that failed because a provider request hit Orca's deadline (e.g. Codex
thread/start after 30s) now says so instead of the generic couldn't-start
banner. The deadline marks its error with the method and timeout; the start
failure fact carries them as an optional requestTimeout field, and the host
logs the original error, method and elapsed open time.

* fix(native-chat): neutral start-timeout wording; log only the current child's failure, without timing in the repeat key
2026-10-19 21:20:58 -07:00
Jinwoo HongandE2E Test 920d1e9118 fix(status-bar): keep this computer's providers visible as "Not configured on <host>" under a remote workspace (#30858)
* wip(status-bar): remote empty usage state names the host and opens its accounts

* fix(status-bar): keep this computer's providers visible as "Not set up on <host>" under a remote workspace

* test(e2e): paired client keeps a this-computer-only provider as a host-named placeholder

* fix(status-bar): place usage placeholders only on the host's own 'unavailable' answer and open Settings on the owning host

* fix(status-bar): label the not-set-up placeholder chip "Not set up" instead of "--"

* fix(status-bar): placeholder only on a not-set-up reason; compact chip keeps "--"

* fix(status-bar): word the placeholder "Not configured on remote host"

* fix(status-bar): name the host in placeholder chips and keep Claude as 'No usage from <host>'

Only the host name truncates, so the phrase stays readable in every locale.
Claude set up here shows a placeholder when the host reports no OAuth login,
which reads the same as API-key billing, so it never says 'not configured'.

* test(status-bar): cover the Claude 'No usage from <host>' chip end to end

Pins USER on both apps so the runner's real Claude Keychain login cannot leak into
the host or client, seeds a refresh-token-only login on the client, and checks the
chip, its tooltip, the popover row, and that the chip goes away without Claude here.

* refactor(status-bar): share the owner placeholder snapshot and drop a dead Claude check

* fix(status-bar): build the owner placeholder tooltip in the current language

---------

Co-authored-by: E2E Test <e2e@test.local>
2026-10-20 00:15:37 -04:00
Neil 0e7cefbb72 fix(diff): keep combined diff position stable across worktree switches (#30950)
A hidden worktree is display:none, so ResizeObserver reported every mounted
diff row as 0px and the virtualizer cached that. Rows above the viewport
collapsed, and the unchanged scrollTop showed files two rows further down
after each switch back. Keep the last real row size while the scroller has
no layout box, and skip Monaco height reports from a hidden (0-width) editor.
2026-10-19 21:13:46 -07:00
Neil 81b8bfb890 fix(space): speed up selection and support reviewed workspace cleanup (#30989)
* fix(space): speed up workspace rows and enable reviewed cleanup

* fix(space): resolve change inspection by workspace owner

* fix(space): register changed-file and cleanup labels
2026-10-19 21:05:49 -07:00
Neil 4b6fd9283a fix(diff): restore the combined diff view when its tab is reopened (#30943)
Tab-scoped combined-diff view keys include the tab id, which is fresh for
every opened tab, so closing "All Changes" and reopening it (View all or
reopen-closed-tab) always started at the top. A fresh tab now inherits the
latest view a closed tab left for the same diff in the same workspace.
Views of mounted viewers or still-open inactive tabs are never taken, the
entry is moved rather than copied, and older closed views of that diff are
dropped.
2026-10-19 21:00:58 -07:00
Jinwoo HongandE2E Test fe6a2a0924 fix(codex): keep Orca's terminal identity in Codex commands when the user's Codex config strips environment variables (#30857)
* fix(codex): keep Orca's terminal identity in Codex commands under a filtering shell_environment_policy (#25775)

With inherit = "core" in a user's Codex config, every command Codex runs lost
ORCA_TERMINAL_HANDLE and the other ORCA_* values, so orca orchestration commands
failed with no_active_sender_terminal. Orca's codex shell function (bash, zsh,
fish, PowerShell) now passes those identity keys as per-launch
shell_environment_policy.set overrides, which Codex applies after its filter.
Skipped when the user opted into Codex's shared server, since Codex treats any
-c as opting out of it. The CLI error now names Codex's policy when it runs
inside Codex without an Orca identity. Daemon protocol 49 -> 50.

* test(windows): keep the node-as-codex Git Bash fixture free of forwarded identity

* fix(ci): keep the PowerShell wrapper inside its command-line budget; move the build contract to protocol 50; correct include_only advice

* refactor(codex): name the shared-server args for what they are; shorten the Codex identity error

* fix(codex): skip identity values holding an apostrophe instead of passing a truncated path

A TOML literal can't carry ', and Codex's fallback for the failed parse
truncates values such as /a' or /a' #b. Also drop the reversed key list
(order of distinct keys is irrelevant; tests now compare unordered) and
rename the host-side skip set to NO_DAEMON_SKIP_ARGS.

---------

Co-authored-by: E2E Test <e2e@test.local>
2026-10-19 23:54:57 -04:00
Neil 66d37178da feat(ssh): let a nodejs.org mirror serve the pinned Node on this computer and the host (#30981)
* feat(ssh): let a nodejs.org mirror serve the pinned Node on both machines

* fix(ssh): share one deadline across every host download URL

* fix(ssh): give each host download URL its share of the deadline and cap its size

* refactor(ssh): one tool per host, labelled sources and a portable wget watchdog

* fix(ssh): quote this computer's short download reason in the combined error

* fix(ssh): fetch only validated mirror URLs on the host

* fix(ssh): drop an empty query or fragment from the mirror URL

* fix(ssh): keep a cancel or a local error as it is after the mirror fails
2026-10-19 20:52:44 -07:00
0ee0290cc3 feat(accounts): save MiniMax and GLM Coding Plan keys on the Settings host (#30862)
* feat(accounts): save MiniMax and GLM Coding Plan keys on the Settings host

The usage poller on an SSH, `orca serve` or paired server reads these keys from
that machine's ~/.orca, but Settings could only write them on this computer, so
those hosts never showed the meters. New accounts.* RPC methods (advertised as
accounts.usage-credentials.v1) save, clear and report the keys on the host and
refresh its usage. Settings routes the MiniMax and GLM sections, and their
endpoint/site settings, to the host picked in the page; an older host is asked
to update instead of the key being saved locally.

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

* fix(accounts): a host whose desktop owns its accounts refuses usage keys instead of storing them unread

A WSL host's server has no usage poller, so a key saved there was reported
saved yet never read. Every usage-credential method now throws the same
desktop-owns-these-accounts message as the other account methods, and the
host stops advertising accounts.usage-credentials.v1.

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

* fix(accounts): a paired client never overwrites a usage key the other Orca app on that machine sealed

The desktop app (safeStorage) and orcad (its own host key) share ~/.orca but
cannot open each other's sealed files. A remote save or forget of a MiniMax or
GLM key that this host cannot read is now refused, and the status reports it so
Settings locks that key and says to change it in the other app. Blank keys now
fail parameter validation.

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

* fix(accounts): only blame the other Orca app for a key it really sealed, and always allow forgetting

Any read failure was treated as "saved by another Orca app", so a corrupt file,
a permission error or a host that cannot decrypt at all locked the key with a
false message and refused Forget, the only recovery. The host now reads the file
itself: only a well-formed encrypted envelope that a working store cannot open
counts as sealed elsewhere and blocks Replace; anything else is reported as
unreadable and stays editable. Forget is always allowed, and the check logs
nothing per status poll.

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

* test(e2e): fail clearly when the orcad slot is missing instead of passing null as the program

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

* fix(settings): name the host in remote key descriptions, give the MiniMax API key its own failure toast, and treat malformed sealed files as unreadable

---------

Co-authored-by: Jinwoo-H <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 23:46:28 -04:00
Neil 972d6dcb0a Speed up macOS updates by avoiding background installer throttling (#30944)
* fix(updater): avoid macOS background installer throttling

* fix(updater): harden installer scheduling build and diagnostics
2026-10-19 20:41:54 -07:00
Brennan Benson 036fc58ecb fix(native-chat): link Antigravity sign-in instructions to settings (#30970)
* fix(native-chat): link Antigravity sign-in notices to settings

* test(native-chat): use typed failure sentences in settings link coverage

* fix(native-chat): put Antigravity settings link in sign-in sentence
2026-10-19 20:39:10 -07:00
Neil c4a60815b2 fix(worktrees): a sort order and a minted push remote land on the copy that named them (#30976)
* fix(worktrees): a sort order and a minted push remote land on the copy that named them, not skipped for a twin

The renderer already knows each id's host when it splits a sort order; it now sends it
(an optional parallel hostIds; older peers ignore it and keep the skip-twins rule). An IPC
git handler knows the host it required, so a minted push remote records on that copy.

* fix(worktrees): a twinned repo re-lists instead of an id-keyed sort patch; client hosts must name a registered repo

* fix(worktrees): a sort order naming an unregistered host skips that id instead of falling back

* fix(worktrees): a moving project's sort order is fenced before any host-qualified read

* fix(worktrees): a client's local sort-order host matches this server's own runtime stamp

* fix(worktrees): an exact host match wins before the own-runtime alias

* fix(worktrees): terminal-spawn materialization serves only a local row and records on the local copy

* test: drop casts from the spawn twin tests

* fix(worktrees): adopt evidence and the spawn hook stay on their host; an unmatched local keeps the id-only rule

* fix(worktrees): adopt evidence reads the host's own metadata rows

* fix(worktrees): reusing a same-URL remote reads ownership from this host only

* fix(worktrees): a server's own runtime-stamped row still materializes its push remote
2026-10-19 20:20:23 -07:00
Kelvin Amoaba 99379d1458 fix(mobile): let an add or removal replace corrupt stored terminal keys (#30977) 2026-10-20 03:00:27 +00:00
NeilandClaude 8de37199e2 fix(runtime): a failed orphan adoption write rolls back instead of keeping the adopted tabs (#30942)
* fix(runtime): a failed orphan adoption write rolls back instead of keeping the adopted tabs

The adoption rollback ran outside the store's durable transaction, so the store
rebased it like a stale renderer write below the topology fence and kept the
adopted tabs, layouts and incarnations; the next save then persisted them.
Run the adoption through runDurableMutation, whose rollback phase skips that
rebase, and refuse if a queued write changed the session it was checked against.

Co-authored-by: Claude <noreply@anthropic.com>

* fix(runtime): an adoption into an unwritten host partition is not refused as a conflict

Co-authored-by: Claude <noreply@anthropic.com>

* test: run the orphan adoption rollback test in the SQLite runtime project

Co-authored-by: Claude <noreply@anthropic.com>

* refactor(runtime): check and write an orphan adoption inside the store transaction

A snapshot comparison could not see a queued binding write that edits the
session in place. Plan the adoption against the session the transaction
reads instead, so queued writes are validated against rather than guessed at.

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 19:58:14 -07:00
Neil 9a79de59a5 feat(native-chat): apply per-agent Auto permission defaults (#30853)
* feat(native-chat): sync per-agent permission defaults with explicit host support

* test: include native permission capability in host settings mock
2026-10-19 19:58:00 -07:00
Neil c822123b43 fix(ssh): a forward edit cut short by Disconnect keeps the host's saved port forwards (#30932)
* fix(ssh): a port forward never starts on a connection its teardown already disposed

* fix(ssh): a Disconnect-cancelled edit still resyncs the renderer, and tests model a disposed connection faithfully
2026-10-19 19:57:47 -07:00
Neil b6eee3e981 fix(ssh): download the pinned Node on the host when this computer can't reach it (#30945)
* fix(ssh): let the host download the pinned Node when this computer can't

* fix(ssh): keep both host download tools inside one time budget

* fix(ssh): use timeout for wget only where it runs as expected
2026-10-19 19:56:11 -07:00
Neil 912c13036b test(e2e): measure typing-time renderer stalls with long tasks, not interval drift (#30971)
In the hidden E2E renderer the 16ms interval probe measured timer
scheduling: a CI trace shows a 268ms interval gap while every keystroke
echoed within 45ms. Long-task timing reports how long the main thread was
actually blocked, which is what the 250ms and 3.5s budgets are meant to
bound. The report field keeps its maxTimerDrift name so the perf report
pipeline still reads and budgets it.
2026-10-19 19:41:41 -07:00
Jinwoo Hong 3ce80124c8 fix(notifications): a server with no window pushes phones when a terminal agent finishes or needs input (#30602)
* fix(notifications): a host with no window pushes phones when a terminal agent finishes or needs input

* test: name the cast rationale in the host-delivered notification test

* fix(notifications): wait out a finish's quiet window and match the window's request by its news id

* fix(notifications): announce a lead agent's finish while background work runs, not its all-clear

* fix(notifications): announce a lead agent's finish at once and keep it through the all-clear

* fix(notifications): the host pushes only while no window runs, with the window's rules; a session boundary is no finish

* chore: drop formatter churn in files this change does not touch

* test(e2e): record the window's own notification requests in main

* fix(notifications): a desktop whose window has not shown stays silent; seed restored panes as the window does

* fix(notifications): seed a restored pane fully, and read a listed worktree's own repo
2026-10-19 22:37:43 -04:00
Jinwoo Hong db89ced0f5 fix(relay-ops): stop the reserve watch cleanly when the cell leaves reserve (#30972)
evaluateWatchSample's two early returns (waiting for reserve to apply, and
a cell flipped back to db by hand) carried no pages, so watchSegment threw
'result.pages is not iterable' and the run ended as failed instead of
stopped (c29, 10-20 02:25Z, run 39354895994).
2026-10-19 22:36:42 -04:00
Neil c0202b460c test(node-runtime): skip retry pauses in the refused-download test instead of faking timers (#30967)
Under CI's bun runner the fake-timer loop ran out of turns before the retries finished, and the
test hung to its 30 s timeout.
2026-10-19 19:32:44 -07:00
Neil 929dededc5 test(e2e): give the ACK-backpressured typing scenario the under-load timer-drift budget (#30968)
The main-pressure scenario holds background PTYs behind the ACK gate on
purpose, but it passed the unloaded 250ms drift budget while every other
under-load scenario uses 3.5s. Two unrelated PRs failed it today at
252ms. The 75ms median key-latency check still guards responsiveness.
2026-10-19 19:32:13 -07:00
Neil 8aa5d281f8 test(browser): route input into fresh sandboxed popup iframes before clicking them (#30966)
Fresh sandboxed iframes run in their own renderer and can finish loading
before the browser routes pointer input to them, so Playwright's click
landed on the parent and no popup opened. Hover-poll until the new frame
sees trusted input before clicking, in one helper shared by the adapter,
native and background popup probes.

Share the cold first-frame budget from #30854 with popup-origin-chrome,
which still waited only 10s for the same one-time GPU startup.
2026-10-19 19:23:07 -07:00
NeilandClaude 11b925d5a1 fix(ssh): a legacy relay terminal stays with the relay that runs it when another relay lists the same id (#30870)
* fix(ssh): a legacy relay pane stays with the relay that runs it when another lists the same id

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ssh): keep an exit reported before a pane's owner is known, and re-list a contested pane

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ssh): a legacy relay id's owner survives reconnects, is adopted only once every relay answered, and a contested id is never served

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ssh): pin a legacy relay id's owner only on a listing that accounts for every relay

Co-authored-by: Claude <noreply@anthropic.com>

* test(ssh): the cross-version bridge test passes the target's owner ledger

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ssh): an epoch-scoped relay id keeps its sole lister as owner on an incomplete listing

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 19:13:05 -07:00
Kelvin Amoaba b2e678a391 fix(native-chat): keep /model, /effort, /clear and /compact in the / menu after an agent reports its commands (#30956) 2026-10-20 02:09:30 +00:00
Brennan Benson 577df19269 fix(wsl): reconnect hosts when a retained connection stops answering (#29005)
* fix(wsl): reconcile server reachability when work needs a host

* fix(wsl): settle a stopped host status after reconnect

* fix(wsl): reuse live transport before reconnecting hosts

* fix(wsl): follow main's tunnel-identity move and keep host service under the line cap

* test(wsl): keep the moved tunnel-identity module's other exports in the connector test mock

* test(wsl): stub the holder's running-distro list in the connector test's gate mock
2026-10-19 19:01:29 -07:00
Neil bfdf16b253 style: format three files #29042 landed before the format check (#30948)
Its CI ran before #30863 added the check. Formatting pushed wsl-orcad-connection.ts past its line
limit, so its port-fallback listen moves next to the stdio bridge it wraps.
2026-10-19 18:46:58 -07:00
Brennan Benson 907694dcbb fix(hosts): compare complete renderer status snapshots (#29033) 2026-10-19 18:31:11 -07:00
Brennan Benson 65d671ecc9 fix(wsl): keep the desktop pairing across host restarts (#29042)
* fix(wsl): retain the paired desktop identity across host restarts

* refactor(wsl): resolve enrolled pairing in the identity module

* test(wsl): supply the required host readiness state

* fix(wsl): resolve registered pairings through the saved authority
2026-10-19 18:27:48 -07:00
af6fe23623 A pane close by its terminal handle closes only that pane, even before the window lists it (dark, layout-owner build only) (#30920)
* fix(runtime): main's relisting for a close carries main's tab layout, so a pane close stays a pane close (dark, layout-owner build only)

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

* fix(layout-owner): the relist touches only a row it added, with its own copy of the layout

* fix(layout-owner): store the relist's layout only when it changed anything

---------

Co-authored-by: E2E Test <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 21:23:52 -04:00
Neil f1135dd4ca fix(ssh): say when this computer can't download Node for a managed server, and where an offline copy goes (#30911) 2026-10-19 18:22:24 -07:00
Neil 28d9f959de Run selected SSH tests instead of the whole suite on PRs (#30928)
* fix(ci): run only selected managed SSH specs on pull requests

* fix(ci): retain full SSH coverage for routed shared helpers
2026-10-19 18:21:53 -07:00
Neil 8d0501622a feat(agents): add Auto to the global agent permission switch (#30677)
* feat(agents): configure Auto terminal permissions per supported agent

* feat(agents): restore global permission switch with Auto, per-agent exceptions

Replace the Set defaults menu and inline per-row dropdowns with a global
Manual | Auto | Yolo switch. Auto reveals an Other agents control beneath it,
mixed states badge exceptions, and each row's expanded panel owns its
per-agent permission choice.

* test(e2e): fail loudly when the store is unavailable in the Auto permissions spec
2026-10-19 18:15:07 -07:00
Kelvin Amoaba 883583b4c8 fix(native-chat): hide history before /clear (#30902) 2026-10-20 01:09:38 +00:00