Commit Graph
1391 Commits
Author SHA1 Message Date
Kelvin Amoaba 162e07cf97 fix(browser): refuse agent clicks while the user is mid-IME composition (#30358) 2026-10-19 11:45:29 +00:00
Neil 148944c020 refactor: de-barrel remote-runtime-client, runtime-terminal-stream and runtime-terminal-inspection (D2, D3) (#30684)
* refactor: de-barrel remote-runtime-client and runtime-terminal-stream (D2)

importers name the defining modules; the two vi.mock factories that named a re-export move to
the defining module (pty-id spy) or drop the dead key (RemoteRuntimeClientError)

* refactor(renderer): de-barrel runtime-terminal-inspection (D3)

importers name runtime-terminal-input-recording, runtime-terminal-verified-input and
shared/remote-runtime-pty-id; 22 vi.mock factories move the re-exported keys to those modules
(pty-id keeps its other exports via importOriginal)

* test: drop the duplicated verified-input mock (Opus P3)
2026-10-19 03:58:26 -07:00
Jinwoo Hong 9def13dde2 ci: run the layout-owner switch-on specs as an advisory PR check (dark, layout-owner build only) (#30582)
* ci: run the layout-owner switch-on specs as an advisory PR job

Build the e2e app with ORCA_LAYOUT_OWNER=main through the existing e2e.yml
(new layout_owner input, read only by the app and orcad builds), run the
layout oracle, its positive control and the layout-owner acceptance specs
with the save-divergence log on, and upload a summary by kind and spec.
Not in the PR aggregate's needs.

* ci: summarize divergence logs without a test report, and drop file-path ids from kinds

* ci: switch-on job adds the stream-loss spec and the managed-SSH Docker lane, drops diff-word-wrap (fails switch off), and names each looped test in the summary

* test(e2e): editor-model-host-isolation opens both files through the store's open action, so a layout-owner build's main holds the tabs

* ci: the divergence summary skips a cut-short test report instead of failing

* ci: re-run PR checks on a merge ref that includes the main typecheck repair (#30680)
2026-10-19 06:41:26 -04:00
Jinwoo HongandClaude 78bad24d33 test(e2e): nine sidebar specs create workspaces and links through main so they pass with the switch on (#30667)
* test(e2e): create reveal-spec workspaces through main so they pass with the sidebar switch on

The agent-send picker and host-section reveal specs injected fake repos and
workspaces into the window store. With ORCA_SIDEBAR_INPUTS=main the sidebar
lists main's rows, so those workspaces never appeared. The SSH host is now an
unreachable target seeded into the stopped profile, and the paired-server host
is a real headless orca serve host; pins, lineage and notes go through main.

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

* test(e2e): type the seeded hosts as execution host ids

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

* test(e2e): poll the reveal specs' page setup instead of awaiting it across CDP

An evaluate that awaits a preload promise sometimes fails with "Resulting
promise was garbage collected" in switch-on runs. evaluatePolled runs the work
in the page, polls its outcome, and still fails on a rejected or failed write.

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

* test(e2e): parent reorder spec creates its workspaces and links through main

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

* test(e2e): lineage specs create their workspaces and links through main

seedLineageScenario names and orders the two seeded workspaces and links them
with assignWorktreeParent. Extra families and surrounding rows come from a
fresh repo with real worktrees; pins and stale instances are main writes.

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

* test(e2e): default-branch and downward-drag specs create their workspaces through main

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

* test(e2e): reveal and active-delete specs create workspaces through main

The 60-row reveal, the folder-workspace reveal and both active-delete tests
now use real workspaces and folder workspaces added through main, ordered with
metadata writes. The deletes go through the real removal path, so the row
removal sampler waits by time rather than a frame count.

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

* test(e2e): address review on the switch-on spec follow-up

writeWorkspaceMeta now fails unless main lists the written values; the sleep
test's parent is a real worktree instead of a store-faked one; the legacy
inline-lineage case stays window-only and is skipped with the switch on;
the parent-reorder collapse key is built from the row's host.

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

* test(e2e): read-back reports an unlisted workspace and compares values structurally

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

* test(e2e): wait for the added repo before showing its worktrees

updateRepo returns false without writing when the store does not list the repo
yet. On CI the add's own refresh superseded the helper's fetch, so the visibility
write was skipped and every discovered worktree stayed hidden. The helper now
waits until the store lists the repo and fails if the write is refused.

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

* test(e2e): name the repo wait in its failure message

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

* test(e2e): size the setup poll budget to the number of main writes

evaluatePolled waited a fixed 10 s, sized for one write. Linking 53 children takes
about 8 s locally and 36 folder workspaces about 4.5 s, so CI ran past it ("page
work never settled"). Bulk setup now passes a budget of 10 s plus 500 ms per write.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 06:29:17 -04:00
Jinwoo Hong 0e1d0ad4a8 A CLI split that times out leaves no pane (dark, layout-owner build only) (#30563)
* fix(layout): a pane main closed stays closed when its terminal starts late (dark, layout-owner build only)

* fix(layout): main takes back a CLI split pane whose terminal does not start within 10 s (dark, layout-owner build only)

* fix(layout): the timeout take-back keeps a pane whose terminal started or is starting (dark, layout-owner build only)

* fix(layout): one take-back call keeps the layout-writer count

* fix(layout): a split take-back is decided in the write queue, and a spawn for a taken-back pane starts nothing (dark, layout-owner build only)

* test(layout): a spawn for a taken-back pane is refused before the provider spawns

* test: typed store fake in the closed-pane spawn test, no casts
2026-10-19 05:21:06 -04:00
Jinwoo Hong 9294b0ae9c fix(sidebar): the window raises the unread flag exactly when its host does not (dark switch) (#30638)
* fix(sidebar): the window raises the unread flag exactly when its host does not

With ORCA_SIDEBAR_INPUTS=main the host raises the workspace unread flag. A host
now advertises workspace-unread.host-raises.v1 in its status; a switch-on window
raises the flag itself only for workspaces whose paired server lacks it, waiting
for that server's status first. The desktop advertises the same capability to the
hosts it pairs with, and a host leaves raising to an older desktop (one that hosts
client pages without the capability) while it is connected.

* fix(sidebar): every desktop socket advertises the unread capability; window answers from cached status

The browser host lease and network tunnel build their capabilities on the shared
remote base, not the Electron list, so a current desktop's lease looked like an
older desktop and silenced the host. The capability now lives in the shared base.
The window reads the cached status first and probes only before the first one.

* test(sidebar): pin the cached-status path and both older desktop socket shapes

* test(e2e): narrow the server row's host before Mark as read
2026-10-19 05:14:00 -04:00
Jinwoo HongandClaude 0c3807e73b test(e2e): reveal specs create workspaces through main so they pass with the sidebar switch on (#30642)
* test(e2e): create reveal-spec workspaces through main so they pass with the sidebar switch on

The agent-send picker and host-section reveal specs injected fake repos and
workspaces into the window store. With ORCA_SIDEBAR_INPUTS=main the sidebar
lists main's rows, so those workspaces never appeared. The SSH host is now an
unreachable target seeded into the stopped profile, and the paired-server host
is a real headless orca serve host; pins, lineage and notes go through main.

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

* test(e2e): type the seeded hosts as execution host ids

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

* test(e2e): poll the reveal specs' page setup instead of awaiting it across CDP

An evaluate that awaits a preload promise sometimes fails with "Resulting
promise was garbage collected" in switch-on runs. evaluatePolled runs the work
in the page, polls its outcome, and still fails on a rejected or failed write.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 05:10:40 -04:00
NeilandClaude d600658b84 fix(ssh): closing a bridged relay tab stops its shell, even across a restart (#30587)
* fix(ssh): stop a closed bridged relay tab's shell from every close route, across restarts

The renderer's tab-close kill tombstoned a bridged lease before the bridge's
own close ran, so the old relay's shell kept running. Route it through the
bridge like the runtime kill. Persist a pending bridged stop on the lease's
pendingKill so a restart before reconnecting still sends it, and list such
leases for the stop without serving them as panes.

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

* fix(ssh): end a closed bridged tab's lease when its stop can't be proven

Clearing the stop left the lease bridgeable, so a process that took the
recycled relay id was served under the closed tab. Retire it as any closed
tab's lease is.

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

* fix(ssh): keep a closed bridged tab closed across restarts even when its stop can't be fenced

An unfenced legacy stop now gets one try and ends the lease, a repeated close
keeps the first record's age, and the Docker bridge spec covers a close while
disconnected followed by a restart.

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

* fix(ssh): re-list after abandoning a bridged stop so the closed pane drops

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

* fix(ssh): route CLI and worktree stops of a bridged tab through the bridge, and keep a repeated close fenced to the first identity

* fix(ssh): refuse sleeping a bridged pane, settle bridged stop-and-wait on the relay's exit proof, and never attach a pane closed mid-attach

* fix(ssh): settle a bridged stop-and-wait on the exit proof alone, for every overlapping waiter

* fix(ssh): keep an unfenced bridged stop's pane tracked for its trailing exit report, never serving it

* fix(ssh): keep awaiting a bridged stop's exit after a failed reply, never name a retired pane to the relay CLI, and require a real empty listing in e2e

* fix(ssh): tell the runtime a bridged terminal exited once its stop-and-wait is proven

* fix(ssh): keep an unfenced bridged stop's exit observable after its reply fails

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 01:59:23 -07:00
Neil 7ce58ec47e refactor(terminal): one PTY attribution for teardown, close and identity; delete the deprecated owner resolver (#30626)
* refactor(terminal): one PTY attribution for teardown, close and identity; delete the deprecated resolver pair

- strict ssh: route: a proven direct listing beats a same-id server proxy; other contradictions refuse
- terminal spawn's git branch takes the strict route when a host is named (rivals refuse)
- resolveTerminalPtyOwner + isUnattributedTwinWorktree replace resolveRuntimeOwnerForWorktree in
  shutdown, pane and agent hibernation, and PTY identity; close logs unroutable runtime ids
- delete getRuntimeEnvironmentIdForWorktree/resolveRuntimeOwnerForWorktree (legacy branch moved
  into the adapter), the alias golden test, the importer ratchet, its baseline and pr.yml step
- hasHostEvidence reads the owner indexes instead of scanning repos and groups

* fix(terminal): close and identity read spawn's attribution for unscoped remote ids (Opus P2)

- an unattributed twin's unscoped remote: id is skipped and logged on close, never closed locally
- setup terminals: identity and close follow spawn's runtime; raw-id teardown stays local
- drop the last stale getRuntimeEnvironmentIdForWorktree mock (tests/e2e unit suite)

* refactor(terminal): one ownership read in resolveTerminalPtyOwner

* test(terminal): pin the close-path twin skip and the two-proxy direct listing (Opus P3)

* test(terminal): keep the close-provider owner fixture's full union type (Codex P1)
2026-10-19 01:50:57 -07:00
NeilandClaude 36ed236394 fix(persistence): repo writes take the row's identity, never a bare id (#30616)
* fix(persistence): repo writes take the row's identity, never a bare id

updateRepo(target, updates) now takes Pick<Repo,'id'|'connectionId'|'executionHostId'>
(RepoHostTarget) and resolves the host from it, so a caller that holds the row can no
longer drop its host and write a same-id sibling on another host. The id-only callers
(project host setup controller and IPC handler, folder-to-Git upgrade watch, both
clone paths, registration adoption, fork backfill) pass the row they resolved.

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

* test: migrate the remaining Store.updateRepo callers to row targets

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-19 00:33:50 -07:00
Brennan Benson 7dcb7d4439 Sleep stops a workspace's native chat agents (#28445)
* fix(sidebar): Sleep stops a workspace's native chat agents

Sleeping a workspace stopped terminals and browsers but never its structured (native)
chats, so their claude/codex process kept running, and Sleep was disabled for a
workspace whose only content was a chat.

Sleep now stops each chat's agent through a new agentSession.sleep (the host's close,
keeping the tab), routed to the host recorded on the chat's tab; an older host without
the capability fails Sleep with a clear message. The Sleep menu counts a chat.

* Fix native chat Sleep ownership and delayed teardown

* Stop native chat sessions when sleeping their workspace

* Fix sleep teardown wake and host-qualified receipt races

* Verify mobile Sleep sends the selected desktop host

* Keep worktree Sleep RPC within method file limit

* Use sleep intent as attempt identity

* Gate workspace sleep on structured client support

* fix(native-chat): preserve sleep stop receipts and send timing

* fix(native-chat): keep superseded workspace sleeps quiet

* fix(native-chat): ignore superseded failed sleep stops

* Fix sleep lifetime test resolver types

* Accept workspace Sleep for already ended chats

* Sleep native chats through the shared no-toast sleep core

Main split runSleepWorktrees into sleepWorktreesReportingFailures (no toast) and
a toasting wrapper. Move the native chat stop, host routing and typed failures
into the shared core so the WSL move's Sleep request also stops chats, and
report every workspace not verifiably slept (an unconfirmed chat stop included)
in failedWorktreeIds. Fold one ACP adapter field onto a line so main's file plus
this PR's ownership check stays within the file length limit.

* Import isStructuredTab from its shared module

* fix(native-chat): leave chat rows to their host through Sleep

Sleep's worktree status drop deleted the chat's row, which the host feed then
rewrote, so the outcome depended on which write landed last. Open chat tabs
own their rows (same rule as terminal-tab close), and a chat only makes a
workspace sleepable while its host holds the agent's process.

* fix(native-chat): judge the Sleep receipt by facts, not lane occupancy

A running chat's stop queues the provider's own requested-close end report
onto the chat's lane, so the lane-occupancy check read every proven stop as
superseded and dropped its receipt. Later work now counts only once it
exists: a moved fence, a child, delivery, child work, a pending submission,
a pending create or attach, or an open dispatch.

* test(native-chat): report a fake close's end only for an acquired child

* Preserve canonical workspace identity when sleeping

* Propagate optional sleep host through file command contract

* Move the deliberate-sleep PTY tests to main's spec file

Main grouped the PTY connection suites (#29193) and moved this file to a .spec.ts registered from pty-connection-spawn-and-startup.test.ts. Taking main's copy here keeps the following main merge free of a modify/delete conflict; this PR's test changes are reapplied after the merge.

* Reapply Sleep's teardown waits to the moved PTY spec; keep acquisition under its line limit

The deliberate-sleep spec moved in #29193; this carries the PR's changes into it: a pane mounted after wake waits for issued teardown, an unqualified pane waits for every same-id host's teardown, and a queued startup waits too.

The merge left claude-structured-session-acquisition.ts over 300 lines. resolveClaudeAcquisitionError, which already builds the exit-unproven error, now decides whether to release the acquisition.

* test: keep the Sleep receipt checks on the closed handle and kept record

* fix(sidebar): a single-workspace Sleep failure says workspace, not workspaces

* fix(sidebar): Sleep stops a workspace's chats on every host that has a copy

* test: drop redundant casts from the Sleep flow test state

* fix(sidebar): stop a workspace's chats after its terminals, leaving main's teardown owner check as is

* fix: admit workspace chat stops before teardown waits

* fix: retain browser teardown caller sequencing

* fix: retire failed acquisitions after observed root exit
2026-10-19 00:33:01 -07:00
Brennan Benson 503d919e64 feat(native-chat): + menu shows chat, disabled chat and CLI rows (#29074)
* feat(native-chat): choose which agents open as a chat

Settings → Chat gets a Providers list. Each agent the host can open as a chat
has a switch; off opens that agent in a terminal instead. Only choices the user
makes are saved. Unchosen: Claude and Codex on, others on when their CLI is
installed on this computer.

The renderer's route and the host's launch-mode decision both read one rule, so
the tab menu, shortcuts, CLI, orchestration and relayed phone launches agree.

* fix: keep chat provider settings with the other chat settings types

* test: route input carries this computer's detected agents

* feat(native-chat): the + menu shows chat, disabled chat and CLI rows

With Chat UI on, each agent whose chat provider is on gets a chat row, disabled
with the reason where the workspace's machine can't run that chat (not
installed, SSH, WSL, not on this host). Every other installed agent gets a row
marked (CLI) that opens its terminal. With Chat UI off, or on an older server
that opens no chats, the menu is unchanged.

* fix: new tab menu rows use a launch variant instead of restyling the menu item

* fix: name every route blocker the menu rows map

* fix: menu row subscriptions render on the server too

* feat(native-chat): open a chat agent's CLI from beside its + menu row

A chat row whose CLI is installed on the workspace's machine gets a › beside
it. Its side menu offers '<Agent> (CLI)', which opens that agent's terminal for
this launch only; nothing is saved and the row itself still opens a chat.

* fix: keep new tab agent surfaces authoritative

* fix: handle unknown host support reasons

* fix(chat): resolve provider choices from their owning computer

* fix: include chat launch feedback translations

* test(chat): use checked native inventory fixtures

* chore: retrigger PR checks

* test: pin pending chat row behavior

* chore: retrigger PR checks after cancellation

* Fix launch preparation lifetime and execution host startup

* fix: keep agent menu choices bound to their launch

* Simplify agent menu support reads and preserve explicit surfaces

* Keep focused menu behavior coverage within its budget

* Keep menu host fixtures typed as execution hosts

* Assert notes payload on the current launch action

* Combine agent session launch type imports

* fix(chat): keep unchosen providers on without discovery

* fix(chat): use workspace installation for menu defaults

* test(chat): type provider routing fixtures against store contracts

* fix(tab-bar): keep + menu chat rows clickable while the host answers

Each menu open discarded the host's answers, so every open showed the chat
rows disabled with 'Checking chat support' until the host replied. Answers
now outlive the menu and refresh in the background, rows use this client's
facts until the host answers, and a slow host no longer reads as a refusal.

* test(chat): align shared launch fixture with private settings reader

* fix(tab-bar): keep known + menu refusals and never treat a silent host as one

Closing the menu dropped the workspace's agent list, which changed which agents
the menu asks about and threw away the host's earlier answers, so a reopen showed
a refused chat as clickable. A support read that could not answer was stored as
"No chat on this host", disabling chats the client knows are supported.

The menu now keeps the stored agent list while closed (only fetching waits for
it to open) and only a real host answer relabels a row. The workspace's
detection target is resolved where main already resolves it (QuickLaunchButton
and the tab-bar runtime model) and passed in, so no new focus read is added.
Tab-bar test mocks now stub the QuickLaunchButton exports the menu imports.

* fix: sync runtime English catalog for agent menu

* test(chat): give the structured-session RPC fixture the private settings reader

The host now reads saved chat choices through requireStore().getSettings(); the
shared RPC fixture only had getClientSettings, so the launch-mode reader saw no
settings and create support refused Claude. The fixture now follows each test's
client settings.

* test(chat): pin create support's options argument and kept decline reason

* fix(native-chat): + menu chat rows wait disabled for the host and share its answer across tab groups

A chat the host refuses showed clickable for up to the 10s host read, then flipped to disabled,
once per tab group. Rows now stay quietly disabled until their host answers (or the read times
out), and every tab group on the same workspace host reads the same answers.

* fix(native-chat): + menu rows also wait before the host's chat list loads, and on every reopen of a host that could not answer

Asking the host no longer waits for its chat list, so Claude and Codex no longer show clickable,
then disabled, while it loads. A read that times out releases rows for that open only, so the
next open waits again instead of flipping on a late refusal. Agents not installed skip the read.

* fix(native-chat): typed + menu search puts a launchable row ahead of a disabled chat

In a WSL workspace, typing an agent's name and pressing Enter highlighted its disabled chat row,
so nothing launched where main opened the CLI. Search now ranks rows that can launch first.

* fix: preserve provider choice and cached native inventory

* feat(native-chat): hosts publish each workspace's chat availability as one stream

A new negotiated stream, agentSession.subscribeAvailability, advertised as
agent-session.structured.availability.v1. The host keeps one in-memory resource per
workspace, shared by every reader, that asks the existing createSupport check for every
registered agent in that workspace and publishes only complete generations. It answers
again when a launch setting changes, after an explicit agent refresh, and (for answers
that could not be checked) when a workspace appears; the last reader leaving retires it.
A check that could not answer is unknown, never a refusal. agentSession.agents stays
static and unchanged.

* feat(native-chat): + menus read a publishing host's workspace availability

The renderer holds the workspace's availability stream while its tab bar is mounted
(not while a menu is open), shared by every tab group, with runtime and contact fences.
It exposes a readiness model, { ready: false } | { ready: true; byAgent }, from
useHostStructuredAgentAvailability and the menu hook. On a host advertising the
capability the menu reads its answers from that model and makes no per-agent
createSupport reads; an older host keeps the per-agent reads, answer cache and deadline
unchanged. Row building and presentation are unchanged.

* fix(native-chat): workspace availability checks only what the shown workspace needs

Only the shown worktree (or an open menu) holds its host's availability stream, and only with Chat
UI on. Display checks no longer force a fresh login shell for an agent that isn't installed, so
Claude and Codex answer without waiting on it. A change to the Windows runtime default or a project's
WSL choice re-checks, and a workspace still on its first check answers again when one appears.

* fix: keep main's bytes for the create-support file the merge reformatted

The pre-commit formatter rewrapped main's unformatted lines during the merge and pushed the file past its line limit.

* test: type the + menu rows hook mocks without assertions

* test: match the availability stream's types in three tests

* test(cross-version): declare the workspace availability stream in the agent-session surface

* feat(native-chat): + menu holds a placeholder until its agents are known, and learns them while the workspace is shown

* feat(native-chat): + menu reads one agent health snapshot per machine

The + menu no longer checks chat availability per workspace. Each Orca
runtime keeps one agent health snapshot: every agent's location rule on
this machine plus a typed observation of its installed release, probed
in the server's own home. It is taken on first use and replaced only on
a launch setting change, an explicit agent Refresh (after a fresh shell
capture), or a restart, and kept without readers.

- The version probe reports supported, unsupported or inconclusive; a
  launch still refuses an inconclusive run, while the snapshot reads it
  as unknown rather than a refusal.
- agentSession.subscribeAvailability takes no params and publishes that
  snapshot; a replay that closes the transport releases its reader.
- The renderer keeps the snapshot per host, runtime and pairing, through
  lost contact and across workspace switches on the same host.
- The menu hook drops the per-open createSupport display reads, their
  deadline and answer cache; older hosts show client facts and the
  click's admission, unchanged, states any refusal.

* fix: a failed shell recapture still publishes the agent health snapshot

* fix(native-chat): capture fresh shell facts for every health snapshot

* fix(tab-menu): share settled agent actions across menu surfaces

* fix(tab-menu): preserve loading row line height

* feat(native-chat): the whole chat row opens its CLI submenu

Hovering a chat row in the + menu, or pressing ArrowRight on it, now opens
the "<Agent> (CLI)" submenu to the right instead of needing a separate
chevron button. Clicking the row, or Enter/Space, still starts the chat
and closes the menu; hosts pass closeMenu since a submenu trigger never
closes the menu on its own.

* fix(tab-menu): line the CLI submenu up with its chat row

* feat(native-chat): a chat row's terminal button opens its CLI

Clicking a chat row in the + menu starts a chat. Hovering the row (or
pressing ArrowRight on it) reveals a terminal button at its right edge that
opens the agent in a terminal instead. This replaces the submenu, whose
chevron made the row look like it only opened a submenu. The button's click
bubbles into the row, so the menu closes as for any item and the hosts no
longer need a closeMenu callback.

* fix(native-chat): the chat row's terminal button handles drags, arrows and screen readers

A press on + dragged onto a chat row's terminal button and released there
now opens the CLI: the row reads whether the pointer or focus is on its
button instead of the click target, which a drag-release never sets.
Arrow, Home and End keys pressed on the focused button move between rows
as they do on the row. The row is announced as "Start a <Agent> chat"
instead of including the button's label.

* fix(native-chat): chat rows read "Start a chat with <Agent>"

"Start a OpenCode chat" was ungrammatical for agents whose name starts with a vowel.
2026-10-19 00:27:17 -07:00
Neil 767b55b1b6 fix(terminal): client pane names reach a desktop host's window, and open panes follow the host's (#30496)
* fix(terminal): client pane names reach a desktop host's window, and open panes follow the host's

- A host with its own desktop window ignored client pane-layout writes, so a
  client's pane rename never reached the window. updateMobileSessionPaneLayout now
  passes the client's pane names to the window one pane at a time through the
  set-pane-title path, and still leaves the window's geometry and the tab's name
  alone. A single pane named after its own tab is dropped, not forwarded.
- Open panes on a client only read host pane names when they mounted. They now
  follow the host's published titlesByLeafId live. A local rename is held until
  the host echoes it, so a snapshot taken before the host saw it cannot undo it.

No wire change: hosts already publish titlesByLeafId, and old peers keep their
current behaviour.

* fix(terminal): hold a client's pane rename over host snapshots until echoed, and fail a dropped window delivery

* test(e2e): read the created host tab id without a type assertion

* fix(terminal): only an explicit client rename reaches a desktop host's window, and local rename holds are bounded

- A client push now names the panes it renamed (optional `renamedLeafIds`), and
  the host forwards only those. A resent title map, which older clients and every
  divider drag send, can no longer revert or clear a name set on the host.
  Older hosts strip the field. Older clients send none, so their renames stay
  local as they do today.
- A held local rename now gives way to a newer host name, a failed push, or a
  15s expiry, and expired holds are pruned. A host that never echoes (an older
  desktop host, or a single-pane name dropped as the tab's own) can no longer
  hide host titles forever.

* fix(terminal): a failed rename push retries with its rename, and an earlier echo cannot undo a newer rename

* fix(terminal): a pane rename bypasses the unchanged-layout push dedupe
2026-10-18 23:03:00 -07:00
Jinwoo Hong 7395d30dc7 The host raises the shared unread dot whatever each client's Filters say (dark) (#30581)
* fix(sidebar): the host raises the shared unread dot regardless of Filters (dark)

* fix(sidebar): tighten the shared-dot comment and tests after review
2026-10-19 01:58:52 -04:00
Jinwoo Hong e8d7da029c An e2e test cuts the window's layout feed mid-session and checks it recovers and survives a restart (dark, layout-owner build only) (#30574)
* test(layout): an e2e ends the window's layout stream mid-session and checks the reopen and a relaunch (dark, layout-owner build only)

* test(layout): the stream-loss e2e covers main's change during the gap deterministically and reuses the shared pane-tree and frame readers (dark, layout-owner build only)

* test(layout): the stream-loss e2e names how long main's create took when it misses the reopen window (dark, layout-owner build only)
2026-10-19 01:45:27 -04:00
Jinwoo Hong 3051b7b671 One background-process read of "Notify About Hidden Workspaces" for phone pushes and the host unread flag (#30571)
* refactor(notifications): one host read of Notify About Hidden Workspaces, used by phone pushes and the host unread flag

* fix(notifications): folder chat phone pushes read the folder's name; skip the hidden check with no Filters switch on
2026-10-19 01:30:52 -04:00
Jinjing 4d65c7617d Open HTML file URLs as document previews (#30512)
* Open pasted HTML file:// URLs as document previews

Pasting a file:// URL to an HTML file into the new-tab box used to fall through to the generic URL path, which does not show the document's relative assets. It now routes to the workspace document preview, so the file renders with its local scripts and styles.

- Classify HTML file URLs before the file list loads, so the "Open file" option appears immediately.
- Resolve file URLs and absolute paths only against workspaces on the same host, so an SSH target is never matched to a sibling on another host.
- Reuse an open document on any page of its workspace instead of opening a duplicate preview.
- Keep the preview in the group the user targeted, including for remote hosts.

* Open HTML file:// previews without resolving file-operation context

Opening an HTML file:// URL used to need the worktree and file-operation context before the preview could open. In remote or not-yet-loaded states that lookup can fail, so the preview never appeared. Pass the context as a lazy getter, so it is read only when a file operation actually needs it. The preview path now runs without it, and the dispatch in openTabBarEntry no longer special-cases HTML before the network and file paths.
2026-10-18 20:57:50 -07:00
NeilandClaude 6328a104bd refactor(ssh): split ssh-connection-utils into concretely named modules (#30488)
* refactor(ssh): split ssh-connection-utils into concretely named modules

- classifiers -> ssh-reconnect-error-classification.ts
- retry/timeout constants and sleep -> connection/ssh-connection-timing.ts
- buildConnectConfig -> connection/ssh-connect-config.ts
- credential callbacks -> connection/ssh-connection-callbacks.ts
- exec options and abort error -> connection/ssh-exec-options.ts
- wrapRemoteCommandForPosixShell -> ssh-posix-command-wrapper.ts
- shellEscape and shared/wsl-login-shell-command quotePosixShell (identical) -> shared/shell-argument-quoting.ts quotePosixShell

No re-exports are left behind.

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

* fix: point the WSL git benchmark and the WSL lane route at shell-argument-quoting

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 20:48:05 -07:00
Jinjing 5ee118a908 fix(source-control): clear unavailable branch line totals (#30475) 2026-10-18 20:44:00 -07:00
Jinwoo Hong 19b7a6c838 A first visit resumes a sleeping agent once, in its own pane (dark, layout-owner build only) (#30468)
* fix(terminal): a first visit resumes a sleeping agent once, in its own pane (dark, layout-owner build only)

Main retires a gone terminal's binding before the pane's fresh start, so on
main-owned layouts the activation sweep read the unbound pane as unable to
resume and launched the agent again in a new tab while the pane resumed it in
place. On main's layout the pane that holds the record owns its resume.

* fix(terminal): the sweep defers only to a pane whose own resume is in flight (dark, layout-owner build only)

Counting every unbound pane in main's layout as the record's owner also
covered a mounted pane whose agent died: it never restarts on its own, so
its agent was never resumed. The pane now marks its in-place resume while
the reattach or fresh start that carries it runs, and the sweep defers only
to that mark.

* test(terminal): the in-place resume hold reads only whether the pane is disposed

* refactor(terminal): the in-place resume hold takes the pane's liveness, not the session

* test(e2e): skip the single-resume spec on Windows, as its sibling resume specs do

* test(terminal): both starts that resume a record set the in-place mark

The e2e that would catch a dropped call needs a layout-owner build CI never
makes, so a switch-on unit test drives each start and checks the mark while it
runs and after it settles. Also corrects the mark's comment: a hidden pane's
background wake sets it too.
2026-10-18 23:41:55 -04:00
Jinwoo Hong 88241bcd1e feat(sidebar): the phone reads its workspace list from the sidebar inputs (dark) (#30462)
* feat(mobile): the phone page lists workspaces from the host's sidebar feed (dark)

* fix(mobile): settle pending option changes by latest intent; feed carries the desktop's focused host and host names (dark)

* fix(sidebar): the feed carries the desktop's pinned placement; e2e compares the phone list to the desktop sidebar (dark)

* test(sidebar): the phone list e2e also expands and unpins

* fix(sidebar): phone feed list review fixes (dark)

A collapsed or worktree.ps-failing feed list no longer shows an empty or error state; a queued worktree read uses the latest client; a superseded option change does not report a refusal; the window shadow leaves the focused host to main's test (focus-read ratchet).

* fix(sidebar): phone feed tests typecheck; e2e helper no longer pulls the native runtime types

Mobile test fixtures fill the fields the test project's types require; the feed trigger type lives with the feed reader, so the e2e helper's imports stop dragging react-native globals into the e2e program; React Doctor's two blocking findings.
2026-10-18 23:41:32 -04:00
Jinwoo HongandClaude 61b30a4933 Main keeps an agent's resume record from the status it receives (dark, layout-owner build only) (#30455)
* Main keeps an agent's resume record from the status its hook server accepts (dark, layout-owner build only)

In a layout-owner build, the window's recorder and quit capture kept only
records of tabs the window holds, so an agent in a workspace the window
never opened lost its resume record at quit. Main now applies the window's
rule (moved into src/shared) to every hook status for a pane it holds, and
captures its own panes' agents at quit; the window's quit capture is gone.

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

* A snapshot replay looks up a pane's tab only when its status can leave a resume record

Restores the lookup order the window had before the record builder moved to shared code, and
updates the two handler tests for the quit-records option.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 23:18:51 -04:00
Jinwoo HongandClaude 19e45b7921 feat(sidebar): the desktop window lists main's sidebar inputs behind the build switch (dark) (#30461)
* feat(sidebar): the desktop window lists main's sidebar inputs, with its own writes shown until main does (dark)

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

* fix(sidebar): a newer write wins in the pending layer, created rows hold the stamped record, writes name their host (dark)

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

* chore(sidebar): move the focus-read allowance with the window list model it moved into (dark)

* perf(sidebar): main's sidebar feed publishes a change after a quiet spell at once, not 50 ms later (dark)

* test(e2e): the desktop reads-main spec skips on a switch-off build and waits for a frame after collapse

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 22:49:43 -04:00
NeilandClaude 139bac88b6 refactor(ssh): move the ssh-connection and ssh-target cluster into ssh/connection/ and ssh/targets/ (#30479)
* refactor(ssh): move the ssh-connection and ssh-target cluster into ssh/connection/ and ssh/targets/

Pure git mv plus import and path-literal rewrites. ssh-connection-utils stays put; the next PR splits it.

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

* test(e2e): keep the release-checkout import at the v1.4.218 path

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 19:23:21 -07:00
NeilandOliver Mee cf460aba5a fix(browser): switch client-hosted profiles by staging the replacement guest (#30452)
* fix(browser): switch client-hosted profiles with staged guest replacement

Squashed from #30351 by @oliver-mee onto the current main.

Fixes #30350

* fix(browser): fence staged profile guests, release switch grants, carry host profile metadata

- A fence during automation retirement now revokes and stops the staged guest.
- A failed or late-settling switch releases its execution-host grant so the page can be recreated.
- Staging cleanup failures before handover stay visible to executor close.
- createPage/restorePage carry host-owned profile metadata to profile-switch-v1 desktops,
  so recovery and restore can rebuild a switched page.

* fix(browser): roll back a switch whose replacement died, and report a lost old guest

* fix(browser): resolve client-hosted pages before the server-local target path when switching profiles

* fix(browser): a staged profile-switch guest owns downloads it starts while loading

* fix(browser): keep the original placement until a timed-out switch settles

* fix(browser): rebind the original guest before releasing a rolled-back staged guest

* test(browser): type the harness guest release mock

* fix(browser): release the replaced generation's download transfers when a switch commits

* fix(browser): admit file transfers from a staged profile-switch guest while its switch is pending

* fix(browser): release only the replaced generation's download transfers after a switch

* refactor(shared): import BrowserNetworkExecutionHost from its own module, no re-export

* fix(browser): adopt a profile switch the client finished after the caller timed out

---------

Co-authored-by: Oliver Mee <102673257+oliver-mee@users.noreply.github.com>
2026-10-18 17:48:57 -07:00
Jinwoo Hong 273068f969 test(e2e): seed the gone-pane resume record on disk so the CLI retention control holds in both layout builds (#30432)
The control record named a pane no window or main holds. A layout-owner build keeps resume
records on main's panes, so a record for no pane only exists when loaded from a profile.
Relaunch instead of reload, seed that record into the stopped profile, and read the CLI
tab's PTY from main's inventory, which both builds report.
2026-10-18 20:37:59 -04:00
Jinwoo Hong 3ad036551e fix(native-chat): clicking Send or Stop keeps focus in the message box (#30456)
* fix(native-chat): clicking Send or Stop keeps focus in the composer

A click focused the Send/Stop button; when it disabled (empty draft after the
turn, or while a Stop is in flight) Chromium dropped focus to <body>. A pointer
press on it no longer moves focus, matching Enter. Adds an e2e regression with
a turn-capable Claude stub. Fixes STA-10897.

* Tighten comments
2026-10-18 20:21:59 -04:00
Jinwoo Hong 062e06d982 feat(sidebar): the owning host raises the workspace unread flag (dark) (#30450)
* feat(sidebar): the owning host raises the workspace unread flag (dark)

* fix(sidebar): phone session open clears and spares the flag; refresh views; blur rescan

* fix(sidebar): throttle failed view retries, resend failed empty views, hold presence one refresh longer

* test(sidebar): switch-on window writes neither the unread flag nor a stamp
2026-10-18 20:01:28 -04:00
Jinwoo Hong 687328ae41 A CLI split whose pane is closed before it shows answers at once (dark, layout-owner build only) (#30439)
* fix(layout): a CLI split whose pane main closes before it mounts answers at once (dark, layout-owner build only)

* test(layout): the closed-before-mount split fails because of the close
2026-10-18 18:40:32 -04:00
Jinwoo Hong eaa1ee131d A CLI-started terminal shows once in its workspace, and a pane close main has no pane for closes in the window (dark, layout-owner build only) (#30391)
* fix(layout): a workspace only main held shows main's tab on the window's first visit (dark, layout-owner build only)

* fix(layout): a live terminal no pane shows goes through main, which names the tab it holds; a pane close main has no pane for closes here (dark, layout-owner build only)

* test: no type assertions in the CLI-first tests

* refactor: the switch-off build keeps today's live-terminal mint as it was

* review: frame sync under the line limit; the first-visit test names exactly main's tabs

* fix(layout): adopt takes the recorded pane only while it is unbound

* fix(layout): a refused live-terminal adoption is declined, not counted as shown
2026-10-18 16:25:49 -04:00
Jinwoo HongandClaude 91b696b6cf refactor(sidebar): main keeps sleeping retention only, no order of its own (dark) (#30404)
* refactor(sidebar): the stamp-sorted window list runs no settle, latch or saved order (dark)

With ORCA_SIDEBAR_INPUTS=main the window's sort hook is a pure read of the
rows' stamps: no settled-epoch store listener, no Smart latch, no saved Smart
order writes, no class telemetry over the old attention map. Switch off: the
existing hook, unchanged.

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

* test(sidebar): stamp sort hook reads no live sources; stronger switch-on tests

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

* test(sidebar): clear the order-save mock between tests

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

* wip: main keeps sleeping retention only

* fix(sidebar): stamp sort reads a shared empty-sources constant, not a renderer layout literal

The layout-writer ratchet counts layout field keys written in the renderer;
the empty sources object now lives beside SmartSortSources in shared code.

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

* refactor(sidebar): main keeps sleeping retention only; no order of its own (dark)

Clients sort by the owning host's stamps, so main's temporal owner loses its
settle window, Smart latch, create grace and catalog-listing classifier, and
the shadow check stops comparing order. Retention, its clock and its shadow
comparison stay.

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

* fix(sidebar): every retention listing shares one sequence; publish after the first

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

* test(sidebar): the first listing bumps the retention revision

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 15:51:52 -04:00
Brennan BensonandOrca Worker c7942252b5 test(e2e): align title-strip drops with pane selection rules (#29903)
* fix(terminal): a file dropped on a pane makes that pane active

Dropping a file on an inactive pane's title strip pasted the path but left the
other pane selected, because the drop only focused the terminal. Activate the
pane instead (which also focuses it). Tracked in STA-9893.

* test(e2e): give the title-strip drop the source owner real drags carry

* test(e2e): preserve pane selection when checking title-strip drops

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-18 12:11:36 -07:00
Jinwoo HongandClaude 3a2f162911 feat(sidebar): sort Recent and Smart by the owning host's stamps, hold order under the pointer (dark) (#30284)
* feat(sidebar): sort stamps reconcile stored tiers at startup against live hook rows and terminals (dark)

* test(sidebar): a restart clears an unbacked local tier and keeps an SSH host's

* fix(sidebar): run the sort tier reconcile once hook rows and PTYs settle, seeding hydrated rows

* fix(sidebar): clear a stored tier only on proof its terminals died; a rest seed adds no clear

* chore(sidebar): merge origin/main; locality helper moved to shared

* feat(sidebar): sort Recent and Smart by host stamps behind the sidebar-inputs switch

With ORCA_SIDEBAR_INPUTS=main, the window, main and the phone order Recent by
lastActivityAt and Smart by smartTier then smartAt (Needs you, Done, Working,
then the rest by Recent). The window holds its displayed order while the
pointer is over the list or a drag runs. Switch off: today's sort path.

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

* chore: restore files the merge commit's formatter rewrote

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

* fix(sidebar): stamp-order review fixes behind the sidebar-inputs switch

The window writes no Recent stamps with the switch on (terminal start and
exit, unread marks, comment edits), the hover hold covers only Recent and
Smart and ends with a drag, main follows only explicit metadata saves, and
the phone picks its sort from the unfiltered rows.

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

* test(sidebar): folder writers and a Manual drag under the stamp switch

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

* test(sidebar): name the stamp type in the ps summaries test

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 14:16:34 -04:00
Jinjing a8d7151b49 Require a full hover delay before each tab preview (#30388)
* Require a full hover delay before each tab preview

Tab previews opened almost immediately after a previous preview had been shown, so sweeping across the tab strip caused a flurry of popups. Raise the tab tooltip delay to 800 ms and drop the warm-start chain: the shared open-state context, the 300 ms skip delay, and the slide animation that carried position between cards. Every tab now waits its full dwell time, and keyboard focus still shows the preview immediately.

* Test that tab hover cards wait a full dwell on every hover

The tab hover card no longer slides instantly between neighboring tabs. Each hover now waits for the full delay before showing a preview. The e2e test still expected instant sliding, so it was rewritten to match.

- Remove the frame-recording helpers and the slide and reversal assertions.
- Check that no card appears before the dwell, and that the card appears after it.
- Check that leaving a tab cancels a pending preview, and that time spent on other tabs does not count toward the dwell.
- Check that re-entering the same tab requires a fresh dwell.
2026-10-18 11:14:08 -07:00
Neil 9905da80cc refactor(runtime): import runtime helpers from their owning modules, not re-export shims (#30373)
Removes the re-export lines in 22 src/main/runtime modules (rpc schemas, skill surface, worker
identity, terminal viewport, teardown, git, e2ee crypto and more); importers and test mocks name
the real modules.
2026-10-18 09:28:51 -07:00
Neil 658953dc23 refactor(ssh): group system-ssh transport, ssh config and remote exec into ssh/{system-transport,config,remote-exec}/ (#30355)
git mv plus import, vi.mock and path-literal rewrites; one e2e route regex follows the moved
dynamic-forward process.
2026-10-18 07:26:10 -07:00
Neil c82cf2b35d fix(mobile): a session resumed from the phone's history shows its conversation before the first new turn (#10923) (#30283) 2026-10-18 04:00:18 -07:00
Neil 2d1ec53533 refactor(ssh): fence the legacy relay in ssh/legacy-relay/ and drop the relay name from live deploy primitives (#30292)
Moves legacy-relay-bridge/ plus the relay-only census and endpoint files into ssh/legacy-relay/,
so the 2027-01-15 retirement deletes one folder. Live install, exec and framing modules that only
carried the relay name become remote-*. git mv plus import, path-literal, routing and workflow
path-filter rewrites only; no remote file name or path changes.
2026-10-18 03:17:41 -07:00
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 2c12c1edf1 refactor(runtime): move runtime/relay to runtime/mobile-cloud-relay (#30274)
The folder is the cloud relay that reaches phones; under its old name it read as the legacy SSH
relay. git mv plus import and path-literal rewrites only.
2026-10-18 01:35:40 -07:00
Jinwoo Hong 9bd47de3cf feat(sidebar): after a restart, the host clears sort tiers nothing backs any more (dark) (#30259)
* feat(sidebar): sort stamps reconcile stored tiers at startup against live hook rows and terminals (dark)

* test(sidebar): a restart clears an unbacked local tier and keeps an SSH host's

* fix(sidebar): run the sort tier reconcile once hook rows and PTYs settle, seeding hydrated rows

* fix(sidebar): clear a stored tier only on proof its terminals died; a rest seed adds no clear

* chore(sidebar): merge origin/main; locality helper moved to shared
2026-10-18 01:09:06 -07:00
Neil 1de30d8235 test(e2e): the Windows CLI matrix convert cell calls the convert helper that exists (#30231)
convertThenRetire was never exported, so the spec failed to load; convertAndRetain is what the other upgraded-profile cells use. Also narrows the deployment union so the helper typechecks.
2026-10-18 00:24:22 -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 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 9a86d271dc chore(updater): delete the rollout-flag module nothing reads (#30211)
The nudge request parsed and stored a rollout block, but no build ever read a flag from it.
Drops the module, its call site, its tests, and the e2e env that fed it.
2026-10-17 23:38:45 -07:00
Jinwoo Hong 31f766515d Main records when a workspace's default tabs were applied (dark, layout-owner build only) (#30176)
* feat(layout): main records a workspace's default-tabs mark from the window (dark, layout-owner build only)

* fix(layout): record the default-tabs mark after the tab creates it covers (dark, layout-owner build only)

* test(e2e): strict default-tabs mark and exact tab count across a crash (dark, layout-owner build only)
2026-10-17 23:02:05 -07:00
Neil dbb84f4ad9 fix(ssh): move parked terminal history through the snapshot channel so a big saved buffer no longer blocks a host's move (#30126) (#30188) 2026-10-17 22:41:41 -07:00
Neil c359d09f22 chore(deps): remove unused desktop and mobile declarations (#30182)
* chore(deps): remove unused desktop and mobile declarations

* fix(deps): retain parser used by historical-release tests
2026-10-17 22:34:51 -07:00
Jinwoo Hong aff4ee7ac7 feat(sidebar): main produces this host's workspace rows, checked against the window (dark) (#30184)
* feat(sidebar): main produces the local workspace rows, checked against the window (dark)

* fix(sidebar): workspace rows invalidate the listing only on metadata saves; prove it on the real cache

* fix(sidebar): workspace rows merge the listing fresh instead of invalidating the shared cache

* chore(sidebar): workspace rows comments say what the fresh merge reads
2026-10-17 22:10:09 -07:00
Jinwoo Hong bb0f229aa5 test(e2e): wait for git status to show the seeded file before the discard confirmation test acts (#30165) 2026-10-17 20:59:19 -07:00