m4air run 3: same tab, same leaf, new PTY, and state 1 after, but the relaunch was a new conversation
(no --resume; new session id). Orca Cloud tabs are host-surface tabs: the desktop reconnect only
attaches, and the host woke the slept leaf with its original launch. After the reversible stop, the
desktop now asks the host to ensure the agent on the pane's own provider session, placed in the same
host tab and leaf, then reconnects. A pane with no session keeps the host's fresh launch ('Starts a
new conversation'). A host that does not stop or resume shows the failure with the reason.
Strip (design-squad): 'Restarting Claude…' is the spinner and words only, with no button and no ×.
The failure says it once: the body names the reason when known ('The workspace didn't respond.')
and is otherwise empty; Try again and × stay.
m4air run 2: a plain stop (keepHistory false) is a close, and the host dropped the whole Claude tab, so
the reconnect had nowhere to relaunch. The stop is now the reversible one the pane-sleep path uses
(terminal.stopExact, keepHistory + targetOnly): the leaf stays as a sleeping surface and the
reconnect wakes it with --resume.
If the pane is gone anyway, the attempt ends with a toast naming the workspace, 'Couldn't restart
Claude in {workspace}.' with Open workspace, instead of vanishing with the strip. Tests assert the
same tab and leaf before and after, the reversible single-target stop, and the toast.
m4air (clean run, no other client): Restart showed 'Restarting Claude…', the pane took the queued
restart, but its PTY never changed and the old Claude kept running; the strip then cleared on the
'quiet relaunch counts' rule, a false success. An Orca Cloud pane is a paired web-host pane: the
desktop reconnect asks the host to ensure the pane's agent session, and the host hands back the live
terminal still running that session. Nothing on the remote path stopped it (local panes pass
replacesPtyId; remote ones had nothing).
Now a strip Restart on a remote-runtime pane resolves that pane's host PTY and stops exactly it
(terminal.stopExact, targetOnly), suppressing its exit, and only then reconnects with the resume
startup. A stop that fails shows 'Couldn't restart Claude' and never reconnects. Success needs proof:
the pane bound to a new PTY. The 'relaunched but quiet counts as restarted' rule is gone; anything
short of proof is 'Couldn't restart' at 10 s. Account-switch restarts are unchanged.
On click the strip stays as 'Restarting Claude…' (spinner, button disabled, no ×). It settles on the
relaunched agent's first status, after which it follows the account (state 1 again if the sign-in is
still bad). No relaunch within 10 s, no startup to launch, or a startup that throws shows
'Couldn't restart Claude · Try again.' with Try again and Dismiss. A relaunch that stays quiet past
10 s counts as restarted. The forced past-the-mid-turn mark now clears on launch, failure or timeout,
so a stale queued restart never jumps the account-switch wait rule.
The pane runner is extracted (runReadyPaneAccountRestarts) and covered end to end through the real
store: a 'blocked' Claude relaunches in place with its resume startup, an account-switch restart still
waits, and each failure path reports.
m4air: the strip never appeared in a normal session. The per-workspace Source read settled true,
but useWorkspaceUsesAccount returned null: the React Compiler memoized its plain map read on the key,
so the first (unread) answer stuck. It now comes from a useSyncExternalStore snapshot. The strip
decision also takes the store's revision as an input, so a dismiss or restart re-decides.
m4air: after Renew, Restart Claude queued an account restart that never ran. A 'Not logged in'
Claude reports 'blocked', which the account-restart path reads as mid-turn and waits on. A Restart
the person clicks on the Orca Cloud strip is marked and may run now; account-switch restarts keep the
mid-turn rule.
Real-app m4air: per-workspace Sources (#287's workspaceSources, used by RA-11's D13 gate, the
build-sheet Source menu and cloud secrets) answered not_found for every workspace the current
composer creates, because its runtime record's recipe carries no provider. orcaCloudSandboxIdFor now
accepts a missing provider, like cloud-desktop-keys does; the hosted-sandbox handle remains the
proof. Tests cover provider-less, explicit, other-provider, handle-less and unknown runtimes.
design-squad's state 2 promises 'Starts a new conversation.' when there is nothing to resume, but the
account-restart path returned null for Claude without a session (Restart did nothing). On an Orca
Cloud pane it now starts Claude fresh (same builder Codex already used, generalised), after the
session-env refresh. Off Orca Cloud nothing changes. Tests cover resume, fresh, and off-cloud.
m4air: after Renew, Restart Claude relaunched with --resume in the pane's existing shell, whose env
predates the renewed account, so the agent came back 'Not logged in' (no placeholder at all). On an
Orca Cloud pane the account-restart command is now prefixed with the image's
eval "$(orca-env)", which loads the current session env (placeholders only) into that shell. Other
hosts are unchanged. One call site in agent-account-restart-startup.ts; logic and tests in
lib/orca-cloud/cloud-restart-session-env.ts.
Real-app m4air: the first per-workspace Source read ran right after start, failed, and the cached
null hid state 1 for 10 minutes. Only a good answer is cached now; a failed read retries when the
account is read again (focus / the visible-window interval).
The pane's workspace Source is read once (GET /v1/agent-sources?workspaceId via workspaceSources,
cached 10 min) when the account flips; a shared workspace or a key-sourced one shows nothing, and an
unread or failed read shows nothing (fail quiet). Unit tests cover shared, key-sourced, nothing
chosen with and without a covering key, and the unread case.
- When the Orca account goes from active/expiring to expired/revoked, every running Orca Cloud
Claude/Codex pane shows the strip (same component, place, height as #315): 'Your Claude sign-in
expired' / 'Renew to keep working. Your conversation is kept.' / Renew (compact: 'Claude sign-in
expired' + Renew). The process keeps running; nothing is parsed from terminal output.
- One Renew clears state 1 everywhere; the panes that showed it move to state 2: 'Claude sign-in
renewed' / 'Restart Claude to pick up where you left off.' (or 'Starts a new conversation.') /
Restart Claude / Dismiss. Restart reuses the account-restart path (queuePaneAccountRestarts), which
resumes the pane's own session. Never automatic; a turn that ends cleanly after the renewal clears it.
- Kill switch / never offered: nothing new on a running pane. A held launch keeps #315's strip.
- Account reads while a running cloud agent pane is mounted: on window focus and at most once a
minute (one timer per window).
- #315 P2: the strip is a pane-top banner (useReservePaneTopSpace), so the terminal refits below it
and no row is covered; a spacer keeps it under a chromeless pane's title-row buttons.
- Spec: terminal starts below the strip and its last row stays visible (held and running); state 2's
Dismiss sits a full title row below the pane's ×. Removes the debug screenshot left in #315's spec
and polls the resize assertion (it raced the layout).
From the m4air real-app shots:
- The width class was frozen at mount, so a pane narrowed after mount collapsed the text to 'Y.'/'S.'.
The strip now lays itself out from its own width (ResizeObserver): wide / narrow / tiny, plus a
compact class below 360 px with one line ('Claude sign-in expired') and Renew; the detail stays the
region's aria-describedby and tooltip. Height stays 56 px.
- The strip sat under the pane's split and close buttons. CloudStripLayer (shared by the pane and the
fixture) starts it below the pane title row (--orca-pane-title-height), and the renew strip drops
its own × (Renew is the action; Escape still cancels).
Specs: resize after mount keeps the title readable at 520 and 320 px; the pane's split and close
buttons are hit-testable above the strip in three states.
- interrupted-turn-continue reads its delivery helpers at call time: the cloud workspace store
imports it, so launch-agent-picker-prompt.test's partial paste mock failed at module load
- plugin-shot-editor test marks its temp cwd as a repo root so the default shots dir (which walks up
to the nearest plugin folder / repo root) can't be redirected by anything above the shared temp dir
renderAccountsSettingsSection now takes additionalParts ({ id, title, description, content }) and
titles each with AgentAccountsSubsection. When the page has more than one part, a jump row under the
description links to each one ('This computer · …'); a link scrolls the settings pane to the part and
moves focus to its heading.
Drops the separate cloud 'agent-accounts' settings entry. The upstream 'accounts' page (now
Agent accounts) gets Hosts (S3) and Orca Cloud (S0) through renderAccountsSettingsSection's
additionalSections; deep links move to pane 'accounts' with sectionId orca-cloud / accounts-hosts.
In Orca Cloud builds the entry loses its Optional badge and gains the cloud search entries.
AI provider accounts becomes Agent accounts: a page whose first part is This computer (or the
selected host), holding the existing provider accounts, switching and usage. renderAccountsSettingsSection
takes additionalSections so other parts can be added below it with AgentAccountsSubsection. The
section id stays 'accounts', so every deep link keeps working.
The Settings nav, its group labels and the matching section titles move to sentence case
(Orca account, Computer use, SSH hosts, ...). In-app, CLI and docs references to
'AI Provider Accounts' now say 'Agent accounts'.
- S3 host column: the name keeps a 6rem minimum so 'Orca Cloud' never truncates; the muted detail
gives way first.
- 'Needs attention · 0' shows 'Nothing needs attention.' instead of a header-only table.
- An expired Orca Cloud row offers Renew plus a ⋯ with Remove…, which confirms like the active row
(shared RemoveConfirmRow).
- 'Renew' everywhere for an expired account: the build-sheet row no longer says 'Sign in again'.
- The not-set-up Claude row offers 'Paste a token instead' (S0's existing paste path) under its detail.
Fixtures: OrcaCloudAccounts:expiredRemoveConfirm, AgentAccountsPane:nothingNeedsAttention.
A sandbox destroyed out of band (the team list's Delete for a workspace
this desktop doesn't run, or the control plane) left its runtime record,
server and project here. After a restart its clone came back as a stray
folder project with a "Couldn't start" row.
The team filing's list now runs at startup and every 10 minutes too, and
hands each successful list to a prune. A record filed under that team
that the list omits is retired only on proof: a second consecutive list
without it, or GET /v1/sandboxes/:id answering not found or destroyed.
Errors, offline, a team switch while confirming, a create in flight, or
another or unknown team's record never retire anything, and asleep
(suspended/archived) sandboxes stay listed so they are never touched.
Retiring follows the app's own delete: the record is cleaned and its
server released and retired, and the window's catalog refresh purges the
host's project and setup.