Commit Graph
1968 Commits
Author SHA1 Message Date
Jinwoo Hong fc24ac3715 Window saves stop carrying tab and pane layout; main keeps its own (dark, layout-owner build only) (#30282)
* fix(layout): an SSH pane's terminal binds in its own session partition (dark, layout-owner build only)

Main keeps its own layout over a window save, so a binding only a save carries is lost. A window
or runtime spawn of an SSH pane bound in the local partition; it now binds where the pane's layout
lives.

(cherry picked from commit beb2275b45)

* test(layout): the SSH binding test uses the shared spawn commit dependencies

(cherry picked from commit 672b641897)

* fix(layout): main moves a tab between a local and an SSH workspace itself (dark, layout-owner build only)

moveTabToWorkspace across two partitions was refused as other_partition and left to the window's
save. Main now reads both partitions, moves the tabs as within one, and writes both in one durable
mutation.

(cherry picked from commit ee27a0bb07)

* chore(layout): one binding write for the partition choice; shipped builds keep their exact replaced-binding read

* fix(layout): window saves keep main's layout and give it only the window's view and facts (dark, layout-owner build only)

windowViewSaveForMain/windowViewPatchForMain replace windowSaveForMain: main's own layout joined
with the window's view, facts and carried fields, written only into the records that change. A
paired server's partition passes through. A visited workspace main holds keeps its terminal row
(D1). The P10 pending case is on.

(cherry picked from commit 0c2fc15974)

* fix(layout): the save divergence detector becomes a report-only drift check behind its log (dark, layout-owner build only)

It no longer wraps the write: it compares the window's raw save with main's layout model, and
only when ORCA_LAYOUT_SAVE_DIVERGENCE_LOG is set.

(cherry picked from commit 6bc1e5556f)

* fix(layout): tab reconcile and terminal reconnect stop writing tab structure (dark, layout-owner build only)

Main's frames draw the tab bar and rows: reconcile still returns its counts but writes nothing,
and reconnect still advertises restored terminals but leaves the rows to main.

(cherry picked from commit ef8eb625ca)

* fix(layout): a layout command main cannot apply is a logged refusal, not the window's own change (dark, layout-owner build only)

other_partition, a thrown call, and host_not_owned for a workspace that is not a paired server's
are refused and logged: a window save no longer carries the change, so drawing it would last only
until the next frame. A twin main may route to a paired server keeps today's path, as does the
first terminal's host_not_owned fallback.

(cherry picked from commit 2c513b2d10)

* fix(layout): the window's refusal code type keeps main's host refusals

(cherry picked from commit 36d46bdcc2)

* fix(layout): the quit checkpoint records only the quit capture's resume records in main (dark, layout-owner build only)

Main keeps its own resume records over a window save, so the quit capture's changes, which reach
main as async commands, could miss the quit. The capture now sends only what it changed with the
synchronous stage, and main writes them through the record-only sleep command before staging; a
wake main recorded that the window had not seen stays.

(cherry picked from commit 2a0f433c34)

* test(layout): the quit capture test builds a typed resume record

(cherry picked from commit d92d1246ee)

* chore(layout): one layout write per save handler

* chore(layout): shipped builds keep their exact quit capture

* fix(layout): the drift check compares structure only, not records main writes itself (dark, layout-owner build only)

Pane bindings and resume records, default-tab marks, closed-tab records and terminal row owners
are main's own writes; a save that has not caught up with them asks main for nothing, so they are
no longer reported.

(cherry picked from commit 93e8b2627c)

* test(layout): relaunch first-terminal cases, and the side-by-side resume case moves its tab the way the window does

(cherry picked from commit ab381332b1)

* test(layout): the relaunch case also checks a reopen that cleared read-only

(cherry picked from commit bbf3d040c8)

* docs(layout): the drift check names the record kinds it ignores

* revert(layout): drop the SSH binding partition change; SSH terminal starts are refused before any binding is saved

getProvider refuses every non-local host, so neither spawn path reaches persistPtyBinding for an
SSH pane, with the switch on or off.

* test: run A2's two real-SQLite session tests in the Node runtime project
2026-10-18 02:56:38 -07:00
Neil 019417bd86 refactor(ssh): import the system-ssh and ssh -G modules directly, not through re-export shims (#30255)
Deletes ssh-system-fallback.ts and the re-export lines in ssh-config-parser.ts, ssh-connection.ts,
system-ssh-file-binary-transfer.ts and ssh-remote-powershell.ts; importers name the real modules.
SshConnection tests mock each real module, keeping the parser stub that holds ~/.ssh/config out.
2026-10-18 02:34:30 -07:00
Neil c30753c739 fix(runtime): show "Re-pair required" for revoked servers and re-pair in place (#30271)
* fix(runtime): show Re-pair required for revoked servers and re-pair in place

A server that revokes this computer's pairing token was labeled "Update
required" because auth refusals and protocol mismatches shared one blocked
state. Carry the blocked reason locally, label revoked hosts "Re-pair
required", and add a Re-pair action (sidebar host menu + Settings server
row) that verifies a new pairing link reaches the same server before
replacing the saved pairing.

* fix(sidebar): stop host header menu clicks from toggling or dragging the row

* fix(sidebar): vertically center host header label and menu button
2026-10-18 01:55:08 -07:00
Neil ecb1d6859c refactor: compatibility folders name their entry modules instead of an index barrel (#30269)
Deletes remote-layout-snapshot/index.ts and workspace-layout/old-client/index.ts. The compat-boundary
check now lists each folder's entry modules, so deleting the folder plus its listed importers'
import lines still removes the path.
2026-10-18 01:36:12 -07:00
Neil 5229ef804e test: tighten the round-2 parity, serve and watcher tests (#30267)
Parity test: one string-skip helper, no infinite loop on an unterminated block comment, no internal shorthand. Serve test: orcadServeArgs takes a bindHost instead of the test splicing argv, typed readiness, and stderr back in the daemon failure. Watcher test: a sentinel at both watched levels.
2026-10-18 01:30:22 -07:00
Neil 8721b9c755 refactor(orcad): import orcad helpers from their owning modules, not re-export shims (#30265)
Deletes orcad-runtime-lifecycle.ts and the re-export blocks in orcad-managed-tunnel,
orcad-local-build-hash, orcad-remote-launch, orcad-remote-node-runtime, orcad-update-plan,
relay-protocol and shared/orcad-migration-manifest; importers and test mocks name the real modules.
2026-10-18 01:29:57 -07:00
Brennan BensonandOrca Worker 407bf039c1 Session History: find every agent's older sessions in Workspace and Project views (#29977)
* fix(session-history): find every agent's older sessions in Workspace and Project views

Session History kept only the newest sessions across every agent, then filtered
by workspace; only Claude, Pi and CodeBuddy got their older in-scope sessions
back. Every agent now declares how its cwd is found without a full parse, and
the scan selects in-scope sessions before the recency cap: Codex and other
header agents by their opening record, SQLite stores by their directory column,
the rest by their cached parse. The scan and the cached-list cut share one
scope rule.

* fix(session-history): keep discovery under the file length limit

* fix(session-history): type the scope walk's file list

* Keep session history scope complete before applying the display cap

* test: align session history scope fixtures

* perf(session-history): settle known out-of-scope transcripts without a per-file read

A scoped scan walks every transcript, and each one cost an await, a parse-cache
database lookup and, for Codex, two head reads every scan. Remember each file
version's settled cwd (from its opening records or its parse), drop settled
out-of-scope files synchronously before the async walk, and read Codex's
roughly 20 KB first record in one 64 KB read.

* fix(session-history): let the parse cache decide scope before transcript heads

The settled-cwd prefilter let a transcript-only verdict reject files whose
current parse (hot or cold) says otherwise: a Cursor chat whose meta.json
appeared later, or a Codex session whose parsed latest cwd moved after its
row left memory. Restore parse-cache-first selection; keep Codex's single
64 KB first read.

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-18 01:19:14 -07:00
Neil a2946f0a65 chore(worktree): composeWorktreeId plus a ratchet on hand-built worktree ids (#30238)
Worktree ids built as `${repo.id}::${path}` by hand are how one worktree got two
spellings (#30150). New code composes them through src/shared/worktree/id.ts; the
ratchet counts the remaining hand-built ids per file (18) and may only go down. Six
byte-identical `${repo.id}::${repo.path}` sites move to getRepoMainWorktreeId.
2026-10-18 00:51:41 -07:00
Brennan Benson bf56095bdd Session History: the scan line under the search box no longer jumps during a refresh (#30158)
* fix(session-history): the scan strip and the lines under it stop jumping during a refresh

The strip under the Session History search box dropped out for a moment on
every rescan (main cleared the entry before the new scan took over, and the
panel hid a finished scan until its list landed), on every worktree switch
(the new scan key had nothing yet), and the warning and error lines under it
hid while a refresh ran. Each blink moved the whole list up and back down.

Now a new scan takes over its key at once, a failed one puts back the last
finished scan, the panel keeps following the previous key until the new one
has progress, banners stay while the list they describe stays, and the error
line clears only when a refresh succeeds.

* style(session-history): collapse the banner line

* fix(session-history): keep the refresh hook under its line limit

* test(session-history): scan listeners no longer have a sent hook

* fix(session-history): retain truthful scan presentation through retries

* test(session-history): brace scan cache eviction loop
2026-10-18 00:30:34 -07:00
Brennan BensonandOrca Worker a54fd3830e Antigravity install: one clear message in the chat, with the message box staying in place (#30001)
* feat(native-chat): a chat whose host has no program to run shows one clear install message, with the message box staying in place, disabled

A new chat replaces its Start-a-chat copy with an install message (what it is, its size, one Install button, then steps, progress and Cancel). A chat with messages gets a slim strip above the composer instead. Settings' row is unchanged.

* fix(native-chat): the install message drops its Settings hint until that Settings row exists

* fix(native-chat): satisfy the not-found lint on the install card's phase lookup

* fix(native-chat): the install message follows the design system: a still full bar once the download is done, and a plain ghost Cancel

* fix(native-chat): the install card's size caption uses the download-size string main already has

Main's Antigravity work added the same "downloadSize" key to the install strings, so each locale
held it twice with slightly different text. The card now reads main's one string, so the hero's
caption under its button ends in a full stop like the Settings row's.

* refactor(native-chat): the install row keeps only its Settings layout

A chat now shows its own install card, so nothing renders the row's chat layout any more: Settings
> Chat > Providers and the Antigravity accounts section are its only users. The row and its view
lose the surface choice, the chat-only heading string goes from every locale, and the view always
says and offers what Settings did.

* fix(native-chat): the install message points to Settings › Chat › Providers again, now that main has that row

Only chats on this computer show it: that row installs here, not on an SSH or paired host.

* fix(native-chat): a disabled chat box keeps its placeholder and dims, so it says why it can't take a message

The editor drew its placeholder only while editable, so an install-blocked or held box looked
empty and enabled. The plain text box already dims and keeps its placeholder.

* fix(native-chat): keep setup reachable and honor composer input availability

* fix(native-chat): update picker availability after commit

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-18 00:28:19 -07:00
Jinwoo Hong 559ece08d3 feat(sidebar): the host that owns a workspace stamps its Recent and Smart keys (dark) (#30235)
* feat(sidebar): the owning host stamps sort keys on its workspace records (dark)

* fix(sidebar): sort stamps title phase switch is exhaustive

* fix(sidebar): sort stamps reconcile seeded tiers, respect host and WSL move fence, follow PTY swaps

* fix(sidebar): one clock read per sort stamp event

* fix(sidebar): a first idle title is not Done; the WSL fence spares SSH records
2026-10-18 00:16:28 -07:00
Neil e6eaabb587 test(files): the native-ignore watcher test waits on delivered events, not a fixed sleep (#30236)
It flaked on loaded CI shards when a single write never reached the watcher. Files are now rewritten until seen, readiness is proven before the storm, and a sentinel written after the storm replaces the 300 ms debounce sleep. Disabling the ignores still fails it.
2026-10-18 00:10:44 -07:00
Neil f47337d852 refactor(worktree): one worktree path equality; the ipc copy folded WSL ext4 tails (#30227)
src/main/ipc/worktree-path-comparison.ts kept its own areWorktreePathsEqual and
dedupeWorktreesByPath. Its Windows branch lowercased a WSL UNC path's ext4 tail, so two
case-twin WSL checkouts merged into one row in removal safety, folder-upgrade dedupe and
the SSH listing fallback. Every caller now uses git/worktree-path-comparison, which gains
the macOS /private/tmp fold the ipc copy had. worktreePathComparisonKey keeps its exact
bytes (retirement namespace keys persist it) in worktree-path-comparison-key.ts, and the
worktree-logic re-export goes.
2026-10-18 00:02:29 -07:00
Neil 993fd20223 test(preflight): freeze the clock in the explicit-binary timeout test (#30221)
The budget is deadline - Date.now(), so a millisecond tick between the two reads made it 4999.
2026-10-17 23:45:27 -07:00
Neil 9810a5cc92 refactor(worktree): one comparison-only worktreeIdentityKey replaces three key wrappers (#30217)
worktreeIdComparisonKey (nullable '::' key), runtimeWorktreeIdentityKey ('\0' key) and
runtimeWorktreeLookupKey (JSON tuple) all folded the same path spelling. They become one total
key in shared/worktree/id.ts; a malformed id keys as itself. Also fixes id.test.ts literals that
were missing a colon, which made the WSL/UNC parity assertions compare null to null.
2026-10-17 23:43:55 -07:00
Neil d4aa9dcadc revert: restore pinned npx skills installer (#30207)
Reverts #30185 so the separate installer project can proceed. The restored shared command already pins skills@1.7.2 for CLI, desktop updates, and setup commands.
2026-10-17 23:24:02 -07:00
Neil ffb818bf61 Ship the locked skills installer instead of executing npx (#30185)
* security: ship a locked skills installer instead of running npx

* chore: remove retired skills process import exception

* test: include the locked installer in packaged layout fixtures

* test: reuse the real installer builder for package fixtures
2026-10-17 22:51:14 -07:00
Neil b5fa888a93 fix(worktrees): stop CLI-created worktrees flashing into a filtered sidebar (#30194)
* fix(worktrees): keep a CLI-created worktree out of listings until its metadata is saved

A watcher-driven listing during `git worktree add` surfaced the new checkout as a bare
external row. The host's default-terminal request then made that stale row active, so it
slipped past Hide sleeping and Hide CLI-created until the next refresh hid it (~1s flash).

* fix(worktrees): hold canonical checkout paths during creation
2026-10-17 22:38:23 -07:00
Jinwoo Hong b8264e860b Main drops the layout of workspaces the catalog no longer validates (dark, layout-owner build only) (#30177)
* feat(layout): main drops the layout of workspaces the catalog no longer validates (dark, layout-owner build only)

* rename the paired-host check so the switch-off bundle keeps its names

* a repo id two hosts register skips the listing purge
2026-10-17 22:19:35 -07:00
Kelvin Amoaba 5c4c1edc64 feat(terminal): tell users once that the diff panel in a Claude pane is Claude's own (#29818) 2026-10-18 01:49:10 +00:00
Jinwoo Hong ce2d60834c test(pty): the launch race join test no longer waits on real disk I/O (#30132) 2026-10-17 16:54:24 -07:00
Neil ba9c7eadbf Add Reveal in Finder to the file header menu (#30129)
* Add file location actions to the editor header

* Wait for the terminal spawn join before releasing race-test gates
2026-10-17 16:52:27 -07:00
Jinwoo Hong 45f7429600 Save-divergence detector: skip empty workspaces, see dropped tab-bar entries and kept pane trees (dark, layout-owner build only) (#30125)
* Save-divergence detector: skip empty workspaces, see dropped tab-bar entries and kept pane trees (dark, layout-owner build only)

- A workspace key that names nothing (or only places a stored closed-tab record) is no longer
  reported as "workspace added/removed" or "terminal row owner changed".
- A save that drops a terminal tab's tab-bar entry while keeping its row is reported, read from
  the stored session before the Loader rebuilds the entry.
- A pane tree main keeps over the window's (windowSaveForMain) is reported, so a window-side
  pane repair shows up.

* Detector: keep reporting empty-row flips, cover every overridden pane tree and one-sided closed tabs (dark, layout-owner build only)

* Detector: a save with no pane tree for a tab main keeps one for is reported too (dark, layout-owner build only)

* Detector: a closed tab record moved to another worktree in one save is reported (dark, layout-owner build only)
2026-10-17 16:49:24 -07:00
Neil 27cfee6f7e fix(ssh): a Disconnect stays disconnected, and Settings and the status bar agree (#30130)
A user's Disconnect of an SSH host is now recorded for the session, and background
dials (a managed server's tunnel and status checks, census, host-resume recovery,
pane and automation restores) refuse it until an explicit Connect. Disconnecting a
managed host from Settings also stops its server's status checks, and the status
bar's Disconnect of a managed server also disconnects the SSH host it runs on, so
both surfaces read the same verdict.

Fixes #30086
Fixes #30085
2026-10-17 16:25:42 -07:00
Brennan Benson 6287ff6f62 One spawn step writes a terminal's pane identity for both ways Orca opens terminals (#29884)
* refactor(pty): one spawn step stamps a terminal's pane identity for both spawn lanes

The window spawn controller and the host runtime's caller each wrote
ORCA_PANE_KEY/ORCA_TAB_ID/ORCA_WORKTREE_ID and jcode's runtime dir, in
different layers, so per-pane env behavior drifted (the jcode daemon
pre-warm exists only on the window lane). stampPtySpawnPaneEnv now writes
them in both controllers from the spawn's own tab/leaf/worktree ids;
buildTerminalWorkspaceEnv no longer stamps them. No behavior change: a
TEMPORARY prewarmsJcodeOnDaemonHost input keeps host daemon spawns from
pre-warming until its own PR.

* test(runtime): read a fake spawn's pane env without type assertions

* test(pty): host serializer-settlement spawns name their pane by tab and leaf, as the runtime does

* fix(pty): preserve caller runtime directory after managed preparation fails
2026-10-17 13:40:44 -07:00
Jinwoo Hong cd29f5f3c2 feat(sidebar): main keeps a registry of workspace creations in progress, checked against the window in dev and test (#30021)
* feat(sidebar): main keeps a registry of workspace creations in progress, checked against the window in dev and test

* fix(sidebar): open the runtime creation entry after the create's own repo resolve

* refactor(sidebar): one creation handle opened after the create's repo lookup; move create startup resolution to its own module

* feat(sidebar): put the creation registry behind the ORCA_SIDEBAR_INPUTS dark build switch

* test(sidebar): cover the creation registry with the switch off and the gated creation id
2026-10-17 12:43:12 -07:00
Jinwoo Hong 03cefcb84c feat(sidebar): main keeps one live set of paired-server rows, checked against the window in dev and test (#30027)
* feat(sidebar): main keeps one live set of paired-server rows, checked against the window in dev and test

* fix(sidebar): settle review findings; main also publishes paired-server host facts
2026-10-17 01:13:34 -07:00
NeilandClaude 98d8257b02 fix(ssh): a password SSH host waits for you at launch instead of prompting (#30025)
* fix(ssh): a host that needs a password waits for the user at launch instead of prompting

Restores the persisted 'last sign-in needed a typed credential' flag that
the relay removal (#27214) stopped writing, and at launch marks those hosts
as awaiting sign-in so background reconnects (startup restore, managed-server
status checks and tunnels) refuse without dialing instead of opening the
password dialog. The user's own Connect lifts it and prompts. The terminal
toast shows the refusal as an SSH state, not a bug to report.

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

* test(ssh): give the raw-connected fixture the cache query; annotate the harness-shaped registration

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

* refactor(terminal): keep the sign-in branch out of the toast component

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-17 01:10:48 -07:00
Neil 8f2810ab23 fix(ssh): link a folder workspace the server kept when a re-created SSH host moves again (#27285) (#30017)
* fix(ssh): link a folder workspace the server kept from an earlier move instead of conflicting (#27285)

* fix(ssh): resolve a server folder's host before linking it, and read folder lease owners

* fix(ssh): name a linked folder workspace in the connect's left-behind warning
2026-10-17 00:50:27 -07:00
Brennan Benson 32a53d3ec6 feat(native-chat): image messages wait in the queue like text (#28655)
* feat(native-chat): image messages wait in the queue like text

A message with images now queues behind the running turn and the cards
ahead of it, instead of skipping the queue. Cards show an image icon and
count, an image-only card names its image, and editing a card's text keeps
its images. The host publishes no card holding an image to a client that
has not advertised agent-session.queued-image-cards.v1, since released
clients edit by copying a card's text and deleting the card.

* test(native-chat): an image follow-up queues like text

* fix(native-chat): project queued images before phone frame limits

* fix(native-chat): preserve image sends from older clients

* Clarify direct text recovery at send admission

* fix(native-chat): expose queued image names to keyboard users
2026-10-17 00:07:29 -07:00
NeilandClaude ed147d5d68 fix(ssh): re-check a macOS host's ended launch-session warning so another desktop's restart can clear it (#29915)
* fix(ssh): re-check a macOS host's ended launch-session warning so another desktop's restart can clear it

The ended verdict was cached for the whole SSH session, so when another desktop restarted the server under a live session, this desktop kept the warning. It now expires like every other verdict: a live re-probe clears the note, and an unanswered one keeps the proven verdict.

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

* fix(ssh): only a live probe clears a proven launch-session end, and a failed recheck republishes it

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

* fix(ssh): a failed launch-session recheck restores the warning only for the same live connection

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-16 19:18:09 -07:00
Brennan Benson dd3bbf680e Catch up desktop chats from the newest page (#29125)
* fix(native-chat): retire acknowledged send echoes after catch-up

* test: follow phone lint rules in reconnect regression

* test: format reconnect message expectation

* Keep current chat requests independent of loaded history

* Read current chat requests in bounded cursor pieces

* Bound desktop catch-up after independent request support

* Fix current request subscription static checks

* Match generated request schema import ordering

* fix(native-chat): correct current request type boundaries

* Accept readonly client capability fixtures

* Exercise current request reads in wire compatibility inventory

* Align current request fixtures with provider commit and RPC surface

* test(native-chat): include current request browser capability

* Keep the mobile chat session hook within its line budget

* Format mobile current request readers

* fix(native-chat): reopen once for a current-request version whose read keeps failing

A deterministic read failure reopened the subscription, whose first frame re-read the same version and reset the backoff, so the pane resubscribed every 750 ms forever.

* test(native-chat): type the current-request snapshot fixture

* test(native-chat): wait for the hydrated open before driving the subscription
2026-10-16 17:46:54 -07:00
Jinwoo Hong 9aa537286d Report window saves that still change main's tab layout (dark, layout-owner build only) (#29886)
* feat(layout): report window saves that still change main's layout (dark)

In a layout-owner build, each window session save (session:set, session:patch, the shutdown
checkpoint) is projected to main's published layout before and after it is written. Any workspace
whose layout the save changed is logged, and appended to ORCA_LAYOUT_SAVE_DIVERGENCE_LOG when set,
so evidence runs can show which tab changes still reach main only through the save.

* feat(layout): name the fields a diverging save changed

* fix(layout): the save check diffs main's whole layout model and never fails the save

Review: a failed log write could reject a save that already landed; and the published layout omits
editor views, pane records (sleeping agents, incarnations, titles), closed-tab tombstones and the
workspace records, all of which a later save trim would drop.

* fix(layout): the save check covers record-only workspaces and row owners, and counts rebinding last

* fix(layout): the save check counts first binds as rebinds and keeps the count past the cut
2026-10-16 17:06:52 -07:00
Neil dd70294f0c fix(ssh): turn a host's orca CLI control on by default and stop resetting saved choices (#29856)
* fix(ssh): allow a host's orca CLI to control Orca by default, and stop resetting saved choices

An absent allowRemoteCliControl now means on; only an explicit false turns the
desktop browser bridge and relay-era orchestration commands off. Removes the
one-time reset of saved true values, the consent stamp writes and the reset
notice, which would have silently turned off hosts upgraded from stable.

* fix(ssh): keep the CLI control choice when adding a host from the sidebar

* fix(ssh): read an older client's omitted CLI control switch as off over RPC

* fix(ssh): check the managed fence before routing settings in browser bridge candidates
2026-10-16 16:08:37 -07:00
Brennan Benson 8c1ade9e44 Keep pending chat decisions available outside loaded history (#29121)
* fix(native-chat): retire acknowledged send echoes after catch-up

* test: follow phone lint rules in reconnect regression

* test: format reconnect message expectation

* Keep current chat requests independent of loaded history

* Read current chat requests in bounded cursor pieces

* Fix current request subscription static checks

* Match generated request schema import ordering

* fix(native-chat): correct current request type boundaries

* Exercise current request reads in wire compatibility inventory

* Align current request fixtures with provider commit and RPC surface

* test(native-chat): include current request browser capability

* Keep the mobile chat session hook within its line budget

* Format mobile current request readers

* fix(native-chat): reopen once for a current-request version whose read keeps failing

A deterministic read failure reopened the subscription, whose first frame re-read the same version and reset the backoff, so the pane resubscribed every 750 ms forever.

* test(native-chat): type the current-request snapshot fixture

* test(native-chat): wait for the hydrated open before driving the subscription
2026-10-16 16:01:58 -07:00
Brennan Benson fa923c67e3 fix(projects): a manual folder-to-Git change proves the folder is the Git root first (#29282)
* fix(projects): refuse a folder-to-Git type change that would hide folder workspaces (STA-10638)

A folder project's extra workspaces exist only as metadata rows that the folder listing shows. Changing the project to Git switches it to `git worktree list`, so those workspaces drop out of the next refresh and their saved tabs are purged as if they had been deleted. Only the automatic upgrade checked for this; `orca project setup-update --kind git`, repo updates and clone-into-folder did not.

The check now lives in the one repo write every type change goes through, so any caller gets the same refusal and nothing is written. The automatic upgrade and both clone paths use the same check and keep the project as a folder project.

* test(repos): give the repo handler store mock the metadata read the clone path now uses

* fix(projects): a manual folder-to-Git change proves the folder is the Git root first (STA-10638)

Only the automatic upgrade checked that a folder project's path is the top of a Git repository and recorded Git's spelling of that root. A manual change (orca project setup-update --kind git, repo updates, clone into a folder project) flipped the type without either, so a project inside another repo became a broken Git project, and a project whose path Git spells differently lost its main workspace's id and tabs on the next refresh. All of them now run the same proof and write the proven root with the type change. SSH projects keep the unverified change because their Git runs on a host this process can't probe.

* Revalidate folder-to-Git conversion targets after Git proof

* Explain when a project changes during its Git check

* Fix clone conversion for replaced folder project

* test(runtime): create real Git root for clone upgrade fixture

* test: normalize runtime clone root stamp expectation

* test: check clone registration identity without unknown access

* chore: restore main's pnpm-lock.yaml that the main merge reverted
2026-10-16 15:53:12 -07:00
Brennan Benson 69d877e272 fix(pty): window daemon spawns wait for a WSL guest's Pi/OMP relay, like host spawns (#29780)
A Pi or OMP agent started from the window inside WSL, with the terminal
daemon on, could start before the distro's hook relay was up. It then
reported no working/idle status for that session. Host starts and
local-provider starts already waited (#21721); the window's daemon path
was the one left out.

The shared host-env step now always runs the wait on the daemon spawns
it builds, so the lane input that skipped it for window starts is gone.
The wait is a no-op for anything but an explicit Pi/OMP launch in WSL.
2026-10-16 15:50:04 -07:00
Jinwoo HongandClaude afcca77a5e Window pane trees drawn from main's layout frames (dark, build switch off by default) (#29788)
* Window pane trees drawn from main's layout frames (dark, build switch off by default)

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

* Review fixes: paired-server saves keep their trees, last-pane guards count pending closes, draw main's answer when no frame is coming, redraw main's tree after a zoom

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

* Delta review: a closed stream's frozen cache never redraws pane trees; the frame reconciler never closes a tab's last pane

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

* Second delta review: draw the activation tab only when no frame showed it; a stream not yet open counts as coming; a redraw re-queues only its tab's workspace

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

* Independent review: a late record of a split main asked for restores it only if main took it back; a closed stream never redraws pane trees

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

* Delta review: deny a late record only for a split main wrote and did not take back itself; pane_closed only where the tab is; a reply before the stream opens waits for its first frame or end

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

* Re-review: replies wait only once the window opened its stream; a record that lands while main takes its split back waits for the outcome

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

* A record waits only while main is taking its split back

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

* refactor(layout): main keeps the pane of a split it asked for

Drops the timed withdraw and the window's late record of a main-asked split: main writes the pane and keeps it, a refused write fails the split, and a tab's first mount leaves queued main-asked splits to their request so the startup command still runs.

* refactor(layout): drop the start-without-pane option no caller uses

* refactor(layout): drop an always-false check in the queued main-split filter

* test(layout): main-asked splits into an unmounted tab, with the stream down, and with no start

* test(layout): type-safe withdraw stub

* fix(layout): equalize draws itself when no frame is coming

* refactor(layout): name the frame-tree pane moves for their role

* refactor(layout): frames' pane trees reach the store through the frame store patch

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-16 15:24:12 -07:00
NeilandClaude e0e2001701 refactor(legacy-relay): one ask-wait clamp and no needless exports (#29835)
optionalAskTimeout computed exactly what cappedWait does (an undefined timeout stays undefined;
anything else clamps to 0..ORCHESTRATION_ASK_MAX_TIMEOUT_MS), so the ask call uses cappedWait.
Three functions used only inside their own file lose their export, and the per-host switch's
doc names the orchestration commands it has gated since #29571.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-16 14:11:36 -07:00
Jinjing 005f06bbc7 Run automated workspace setup in a dedicated Setup tab (#29819)
* Run automated workspace setup in a dedicated tab

* Preserve native-chat setup route coverage after placement retirement

* Preserve Setup tab design and independent review evidence

* Classify activation test fixtures consistently for routing checks

* Sync runtime English catalog after removing setup preferences

* Update listener savings model after retiring setup action
2026-10-16 14:05:50 -07:00
Neil 1e39482f9d fix(wsl-cli): keep the WSL CLI status check from timing out on a cold or slow-profile distro (#29814)
* fix(wsl-cli): keep the WSL CLI status check from timing out on a cold or slow-profile distro (#29497)

* fix(wsl-cli): run install/remove without the login PATH; a PATH-check timeout reports PATH unknown
2026-10-16 12:45:01 -07:00
Brennan Benson 6a760682f7 fix(projects): refuse a folder-to-Git type change that would hide folder workspaces (#29257)
* fix(projects): refuse a folder-to-Git type change that would hide folder workspaces (STA-10638)

A folder project's extra workspaces exist only as metadata rows that the folder listing shows. Changing the project to Git switches it to `git worktree list`, so those workspaces drop out of the next refresh and their saved tabs are purged as if they had been deleted. Only the automatic upgrade checked for this; `orca project setup-update --kind git`, repo updates and clone-into-folder did not.

The check now lives in the one repo write every type change goes through, so any caller gets the same refusal and nothing is written. The automatic upgrade and both clone paths use the same check and keep the project as a folder project.

* test(repos): give the repo handler store mock the metadata read the clone path now uses
2026-10-16 12:41:56 -07:00
Brennan Benson 2fa848395b refactor(pty): one host-env step for both terminal spawn lanes (#29297)
* test(pty): pin where the two spawn lanes build a daemon spawn's host env differently

Characterization on main's code before the host-env step merges: which failed env builds sweep
per-PTY state (minted ids only, both lanes), the empty-id split, the host-only WSL Pi/OMP guest
readiness wait, and that a host create operation still waits on a managed Codex login.

* refactor(pty): one host-env step for both terminal spawn lanes

The window (pty:spawn) and host (runtime controller) lanes each carried a copy of Codex resume,
Codex home selection, the managed-auth wait, backfill recovery and the daemon host-env build.
Both now call resolvePtySpawnHostEnv. Lane differences are explicit inputs: two TEMPORARY ones
(the host-only WSL Pi/OMP guest readiness wait; the host skipping host env for an empty id) and
three that mean the same in both lanes. The resume step's launchEnv was never read and is gone.

* test(pty): pin that both lanes hand Codex resume prep the request's CODEX_HOME

orca serve (#29209) skips resume prep for a launch whose env names a client-sent CODEX_HOME.

* test(pty): type the new lane-pin requests instead of asserting them

* test: pin selected Codex home on terminal restart
2026-10-16 10:32:03 -07:00
Brennan Benson 85254f9ecd refactor(runtime): plan paired agent sessions with the host planner (#29041)
* test(launch): pin what a paired agent session spawns today

Characterization on main's code before createAgentSession moves onto the
host planner: plain launch, short/long/multi-line prompts on macOS, a
stdin-prompt agent starting clean, Windows PowerShell and cmd quoting, and
the identity-required refusal when no launch can be built.

* refactor(runtime): plan paired agent sessions with the host planner

terminal.createAgentSession built its own launch command beside the planner
every other host launch uses, and the two had drifted. It now hands the
request to createTerminal as a startupAgent, so one planner builds it.

The planner gains a draft delivery input, and one TEMPORARY input
(startupPromptCarry: 'force-line') keeps what createAgentSession did: the
prompt always rides the typed line, and an agent that reads its prompt
after start launches clean. The later change that delivers uncarried
prompts after start removes it. A launch that can't be built still refuses
with agent_session_identity_required.

* test(accounts): pin that the selected account wins over Settings agent env

A paired agent session now layers the host-selected account env the way
every other host launch does.

* fix(runtime): report prompt carry truthfully on the planner's direct path

Review round 1: a stdin-prompt agent's text never rides its launch line, a
draft comment that overstated the carry rule, the error class's name, and
formatting the cherry-picked commit skipped.

* docs(runtime): scope the account-order and draft comments accurately

Review round 2.

* test(launch): drop paired-session parity rows that pin nothing new

The no-prompt and short-prompt rows plan the same with or without
force-line, and the Windows cmd row repeats the OpenCode-model Windows
row's createAgentSession runtime and cmd quoting. The long-prompt,
aider, PowerShell, refusal and account-env pins keep the coverage.

* docs(runtime): label the draft's carry-rule exemption temporary

Skipping the typed-line carry rule for drafts is createAgentSession
parity, not design: a draft on the line has the same hazards as a
submitted prompt. The one prompt/draft delivery policy removes it with
force-line.

* fix(runtime): keep main's error name for an unbuildable agent launch

Overriding the name made every other planner caller's stringified
error read 'AgentLaunchUnbuildableError: ...' instead of main's
'Error: ...', which renderer code that strips an 'Error:' prefix
would show raw. createAgentSession matches by instanceof, so the
override bought nothing.
2026-10-16 10:29:23 -07:00
Neil 469ae09c97 fix(agents): a resumed agent's chat is readable on the phone before its first new turn (#29664)
* fix(agents): a resumed agent's chat is readable on the phone before its first new turn (#10923)

* fix(agents): skip a dropped Codex resume and keep the seeded identity across restart

* fix(agents): seed resume identity before the spawn's first await and keep it through terminal evidence

* fix(agents): seed after the spawn commits, and name the session on host-built resumes
2026-10-16 05:39:25 -07:00
Neil 6622b8298c fix(terminal): tell panes their daemon died at once and say when typed input was lost (#29660)
* fix(terminal): tell panes their daemon died at once and say when typed input was lost

* fix(terminal): use the info toast the terminal suites already stub
2026-10-16 05:39:07 -07:00
NeilandGideon c8ad8ea313 fix(projects): list symlinked folders as folders in the remote Add Project browser (#29658)
A symlink to a directory was listed as a file on SSH and managed hosts, so
projects behind a linked path could not be reached. Follow links when
classifying entries; broken links and loops stay files.

Co-authored-by: Gideon <jjin6573@naver.com>
2026-10-16 05:21:44 -07:00
Neil 62bf1d68c3 fix(projects): adding a project through a symlink alias reuses the existing project (#29651)
* fix(projects): adding a project through a symlink alias reuses the existing project

On a managed host, adding repo-link (-> repo) created a second project for the
same checkout while re-adding repo was deduped. Compare the real path, resolved
on the host that owns the files, so an alias finds the tracked project; distinct
checkouts keep distinct real paths and stay separate. Local folder workspaces
get the same check.

* test: run fixture git through process-host
2026-10-16 05:15:32 -07:00
NeilandClaude 52554b303f fix(ssh): keep desktop 2 connected with its live relay terminal when desktop 1 already moved the host (STA-10604) (#29625)
* fix(ssh): keep a host connected for its bridged relay terminals when another desktop already moved their project (STA-10604)

Desktop 2's move onto the server desktop 1 already runs refused to link a
project its own relay-era terminal still runs in, and the connect then failed
outright: this version has no relay to fall back to. The decision now keeps
that host connected and unmoved, starts the legacy relay bridge, and the old
tab reattaches its shell through it. The SSH host card names the terminals and
how to clear them; the next connect after they exit links the project.

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

* fix(ssh): release a bridged ssh: row pane's claim on detach too

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

* fix(ssh): admit the bridged mark in the renderer, and remount bridged tabs even when the connect reply lands first

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

* fix(ssh): keep the unverifiable line for a bridged host whose terminal can't be confirmed

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

* fix(ssh): keep the linked-project terminal guidance neutral; only the live bridged line reassures

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-16 04:21:24 -07:00
Neil b051ba90e6 fix(orcad): a restarted managed server keeps the tab titles of terminals it adopts (#29623) 2026-10-16 04:14:00 -07:00