* fix(provider-process): kill a provider group its SIGKILLed supervisor leaves behind
* fix(provider-process): re-read a provider start in its read second; poll instead of sleeping in tests
* fix(provider-process): keep the report channel through the Codex session adapter; re-prove same-second starts
* fix(provider-process): never kill a leaderless provider group; list guard files in the CLI project
* fix(layout): restored terminals named only on their tab reattach after a restart (dark, layout-owner build only)
In a layout-owner build, a terminal recorded only on its tab (an older session, or an SSH tab
whose relay session was never visited) started a new shell after a restart and orphaned the old
one, and an unvisited SSH tab lost its relay session id on the next save.
- Main's Loader binds a one-pane tab's own terminal to its unbound pane in a workspace that was
live at shutdown, as the window's reconnect does, so main's frames and saves hold it.
- The window's reconnect writes the restored row terminal again: ptyId is a field the frame merge
leaves to the window, so main's frames never set it on a restored row.
* fix(layout): main binds a restored tab's own terminal once at load, not on every Loader run (dark, layout-owner build only)
The Loader re-derived the binding on each load from fields the window rewrites live, so an exit
main recorded could be undone. A switch-gated Store load repair now binds a one-pane tab's own
terminal to its unbound pane, as the window's reconnect does, and main's saves keep it. It skips
a terminal another mounted pane or a tab-bar tab holds, and a paired server's partition.
* fix(layout): an SSH workspace's restored tab binds only its relay session id; repair runs after the load purge (dark, layout-owner build only)
* test(layout): the reconnect gate test expects the row ptyId write; cover the repair's purge order and SSH repo predicate
---------
Co-authored-by: E2E Test <e2e@test.local>
Report the execution host's accepted, launching, started, skipped, failed, or completed status and print the available run/workspace/terminal identities. Add safe same-target follow-up guidance and complete UUID examples. Preserve the existing JSON envelope and run fields while adding result.runId.
Keep scheduling, persistence, request count, and SSH owner fencing unchanged. Isolate CLI output tests from inherited connection selectors.
Fixes#27542
* fix(runtime): a moved shell history waits only on this machine's shells, and a phone clears a server-owned copy's own unread flag
The migration's live-shell check counted a same-id SSH twin's pane, so a local history could wait
indefinitely. readAndHoldPhoneSession read a runtime: host as this machine and cleared the local
twin's record instead.
* fix(runtime): a phone clears neither copy of a twinned workspace; the history wait reuses the PTY host fence
* fix(native-chat): name the request a chat start ran out of time on, and log the original start error
A start that failed because a provider request hit Orca's deadline (e.g. Codex
thread/start after 30s) now says so instead of the generic couldn't-start
banner. The deadline marks its error with the method and timeout; the start
failure fact carries them as an optional requestTimeout field, and the host
logs the original error, method and elapsed open time.
* fix(native-chat): neutral start-timeout wording; log only the current child's failure, without timing in the repeat key
* wip(status-bar): remote empty usage state names the host and opens its accounts
* fix(status-bar): keep this computer's providers visible as "Not set up on <host>" under a remote workspace
* test(e2e): paired client keeps a this-computer-only provider as a host-named placeholder
* fix(status-bar): place usage placeholders only on the host's own 'unavailable' answer and open Settings on the owning host
* fix(status-bar): label the not-set-up placeholder chip "Not set up" instead of "--"
* fix(status-bar): placeholder only on a not-set-up reason; compact chip keeps "--"
* fix(status-bar): word the placeholder "Not configured on remote host"
* fix(status-bar): name the host in placeholder chips and keep Claude as 'No usage from <host>'
Only the host name truncates, so the phrase stays readable in every locale.
Claude set up here shows a placeholder when the host reports no OAuth login,
which reads the same as API-key billing, so it never says 'not configured'.
* test(status-bar): cover the Claude 'No usage from <host>' chip end to end
Pins USER on both apps so the runner's real Claude Keychain login cannot leak into
the host or client, seeds a refresh-token-only login on the client, and checks the
chip, its tooltip, the popover row, and that the chip goes away without Claude here.
* refactor(status-bar): share the owner placeholder snapshot and drop a dead Claude check
* fix(status-bar): build the owner placeholder tooltip in the current language
---------
Co-authored-by: E2E Test <e2e@test.local>
A hidden worktree is display:none, so ResizeObserver reported every mounted
diff row as 0px and the virtualizer cached that. Rows above the viewport
collapsed, and the unchanged scrollTop showed files two rows further down
after each switch back. Keep the last real row size while the scroller has
no layout box, and skip Monaco height reports from a hidden (0-width) editor.
Tab-scoped combined-diff view keys include the tab id, which is fresh for
every opened tab, so closing "All Changes" and reopening it (View all or
reopen-closed-tab) always started at the top. A fresh tab now inherits the
latest view a closed tab left for the same diff in the same workspace.
Views of mounted viewers or still-open inactive tabs are never taken, the
entry is moved rather than copied, and older closed views of that diff are
dropped.
* fix(codex): keep Orca's terminal identity in Codex commands under a filtering shell_environment_policy (#25775)
With inherit = "core" in a user's Codex config, every command Codex runs lost
ORCA_TERMINAL_HANDLE and the other ORCA_* values, so orca orchestration commands
failed with no_active_sender_terminal. Orca's codex shell function (bash, zsh,
fish, PowerShell) now passes those identity keys as per-launch
shell_environment_policy.set overrides, which Codex applies after its filter.
Skipped when the user opted into Codex's shared server, since Codex treats any
-c as opting out of it. The CLI error now names Codex's policy when it runs
inside Codex without an Orca identity. Daemon protocol 49 -> 50.
* test(windows): keep the node-as-codex Git Bash fixture free of forwarded identity
* fix(ci): keep the PowerShell wrapper inside its command-line budget; move the build contract to protocol 50; correct include_only advice
* refactor(codex): name the shared-server args for what they are; shorten the Codex identity error
* fix(codex): skip identity values holding an apostrophe instead of passing a truncated path
A TOML literal can't carry ', and Codex's fallback for the failed parse
truncates values such as /a' or /a' #b. Also drop the reversed key list
(order of distinct keys is irrelevant; tests now compare unordered) and
rename the host-side skip set to NO_DAEMON_SKIP_ARGS.
---------
Co-authored-by: E2E Test <e2e@test.local>
* feat(ssh): let a nodejs.org mirror serve the pinned Node on both machines
* fix(ssh): share one deadline across every host download URL
* fix(ssh): give each host download URL its share of the deadline and cap its size
* refactor(ssh): one tool per host, labelled sources and a portable wget watchdog
* fix(ssh): quote this computer's short download reason in the combined error
* fix(ssh): fetch only validated mirror URLs on the host
* fix(ssh): drop an empty query or fragment from the mirror URL
* fix(ssh): keep a cancel or a local error as it is after the mirror fails
* feat(accounts): save MiniMax and GLM Coding Plan keys on the Settings host
The usage poller on an SSH, `orca serve` or paired server reads these keys from
that machine's ~/.orca, but Settings could only write them on this computer, so
those hosts never showed the meters. New accounts.* RPC methods (advertised as
accounts.usage-credentials.v1) save, clear and report the keys on the host and
refresh its usage. Settings routes the MiniMax and GLM sections, and their
endpoint/site settings, to the host picked in the page; an older host is asked
to update instead of the key being saved locally.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(accounts): a host whose desktop owns its accounts refuses usage keys instead of storing them unread
A WSL host's server has no usage poller, so a key saved there was reported
saved yet never read. Every usage-credential method now throws the same
desktop-owns-these-accounts message as the other account methods, and the
host stops advertising accounts.usage-credentials.v1.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(accounts): a paired client never overwrites a usage key the other Orca app on that machine sealed
The desktop app (safeStorage) and orcad (its own host key) share ~/.orca but
cannot open each other's sealed files. A remote save or forget of a MiniMax or
GLM key that this host cannot read is now refused, and the status reports it so
Settings locks that key and says to change it in the other app. Blank keys now
fail parameter validation.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(accounts): only blame the other Orca app for a key it really sealed, and always allow forgetting
Any read failure was treated as "saved by another Orca app", so a corrupt file,
a permission error or a host that cannot decrypt at all locked the key with a
false message and refused Forget, the only recovery. The host now reads the file
itself: only a well-formed encrypted envelope that a working store cannot open
counts as sealed elsewhere and blocks Replace; anything else is reported as
unreadable and stays editable. Forget is always allowed, and the check logs
nothing per status poll.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): fail clearly when the orcad slot is missing instead of passing null as the program
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(settings): name the host in remote key descriptions, give the MiniMax API key its own failure toast, and treat malformed sealed files as unreadable
---------
Co-authored-by: Jinwoo-H <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(native-chat): link Antigravity sign-in notices to settings
* test(native-chat): use typed failure sentences in settings link coverage
* fix(native-chat): put Antigravity settings link in sign-in sentence
* fix(worktrees): a sort order and a minted push remote land on the copy that named them, not skipped for a twin
The renderer already knows each id's host when it splits a sort order; it now sends it
(an optional parallel hostIds; older peers ignore it and keep the skip-twins rule). An IPC
git handler knows the host it required, so a minted push remote records on that copy.
* fix(worktrees): a twinned repo re-lists instead of an id-keyed sort patch; client hosts must name a registered repo
* fix(worktrees): a sort order naming an unregistered host skips that id instead of falling back
* fix(worktrees): a moving project's sort order is fenced before any host-qualified read
* fix(worktrees): a client's local sort-order host matches this server's own runtime stamp
* fix(worktrees): an exact host match wins before the own-runtime alias
* fix(worktrees): terminal-spawn materialization serves only a local row and records on the local copy
* test: drop casts from the spawn twin tests
* fix(worktrees): adopt evidence and the spawn hook stay on their host; an unmatched local keeps the id-only rule
* fix(worktrees): adopt evidence reads the host's own metadata rows
* fix(worktrees): reusing a same-URL remote reads ownership from this host only
* fix(worktrees): a server's own runtime-stamped row still materializes its push remote
* fix(runtime): a failed orphan adoption write rolls back instead of keeping the adopted tabs
The adoption rollback ran outside the store's durable transaction, so the store
rebased it like a stale renderer write below the topology fence and kept the
adopted tabs, layouts and incarnations; the next save then persisted them.
Run the adoption through runDurableMutation, whose rollback phase skips that
rebase, and refuse if a queued write changed the session it was checked against.
Co-authored-by: Claude <noreply@anthropic.com>
* fix(runtime): an adoption into an unwritten host partition is not refused as a conflict
Co-authored-by: Claude <noreply@anthropic.com>
* test: run the orphan adoption rollback test in the SQLite runtime project
Co-authored-by: Claude <noreply@anthropic.com>
* refactor(runtime): check and write an orphan adoption inside the store transaction
A snapshot comparison could not see a queued binding write that edits the
session in place. Plan the adoption against the session the transaction
reads instead, so queued writes are validated against rather than guessed at.
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): a port forward never starts on a connection its teardown already disposed
* fix(ssh): a Disconnect-cancelled edit still resyncs the renderer, and tests model a disposed connection faithfully
* fix(ssh): let the host download the pinned Node when this computer can't
* fix(ssh): keep both host download tools inside one time budget
* fix(ssh): use timeout for wget only where it runs as expected
* fix(notifications): a host with no window pushes phones when a terminal agent finishes or needs input
* test: name the cast rationale in the host-delivered notification test
* fix(notifications): wait out a finish's quiet window and match the window's request by its news id
* fix(notifications): announce a lead agent's finish while background work runs, not its all-clear
* fix(notifications): announce a lead agent's finish at once and keep it through the all-clear
* fix(notifications): the host pushes only while no window runs, with the window's rules; a session boundary is no finish
* chore: drop formatter churn in files this change does not touch
* test(e2e): record the window's own notification requests in main
* fix(notifications): a desktop whose window has not shown stays silent; seed restored panes as the window does
* fix(notifications): seed a restored pane fully, and read a listed worktree's own repo
Fresh sandboxed iframes run in their own renderer and can finish loading
before the browser routes pointer input to them, so Playwright's click
landed on the parent and no popup opened. Hover-poll until the new frame
sees trusted input before clicking, in one helper shared by the adapter,
native and background popup probes.
Share the cold first-frame budget from #30854 with popup-origin-chrome,
which still waited only 10s for the same one-time GPU startup.
* fix(ssh): a legacy relay pane stays with the relay that runs it when another lists the same id
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): keep an exit reported before a pane's owner is known, and re-list a contested pane
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): a legacy relay id's owner survives reconnects, is adopted only once every relay answered, and a contested id is never served
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): pin a legacy relay id's owner only on a listing that accounts for every relay
Co-authored-by: Claude <noreply@anthropic.com>
* test(ssh): the cross-version bridge test passes the target's owner ledger
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): an epoch-scoped relay id keeps its sole lister as owner on an incomplete listing
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(wsl): reconcile server reachability when work needs a host
* fix(wsl): settle a stopped host status after reconnect
* fix(wsl): reuse live transport before reconnecting hosts
* fix(wsl): follow main's tunnel-identity move and keep host service under the line cap
* test(wsl): keep the moved tunnel-identity module's other exports in the connector test mock
* test(wsl): stub the holder's running-distro list in the connector test's gate mock
Its CI ran before #30863 added the check. Formatting pushed wsl-orcad-connection.ts past its line
limit, so its port-fallback listen moves next to the stdio bridge it wraps.
* fix(wsl): retain the paired desktop identity across host restarts
* refactor(wsl): resolve enrolled pairing in the identity module
* test(wsl): supply the required host readiness state
* fix(wsl): resolve registered pairings through the saved authority
* fix(runtime): main's relisting for a close carries main's tab layout, so a pane close stays a pane close (dark, layout-owner build only)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(layout-owner): the relist touches only a row it added, with its own copy of the layout
* fix(layout-owner): store the relist's layout only when it changed anything
---------
Co-authored-by: E2E Test <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
* feat(agents): configure Auto terminal permissions per supported agent
* feat(agents): restore global permission switch with Auto, per-agent exceptions
Replace the Set defaults menu and inline per-row dropdowns with a global
Manual | Auto | Yolo switch. Auto reveals an Other agents control beneath it,
mixed states badge exceptions, and each row's expanded panel owns its
per-agent permission choice.
* test(e2e): fail loudly when the store is unavailable in the Auto permissions spec
* test(browser): temporary timing probe for the popup first-frame flake
* test(browser): extend the temporary popup flake probe to cold caches and the main process
* test(browser): wait for the popup fixture's first paint during startup, not in the first test
* test(browser): name the page whose first frame never arrived
---------
Co-authored-by: E2E Test <e2e@test.local>
* fix(diff-comments): a review note on a twin worktree saves to the open copy and names its host
* fix(diff-comments): rendered notes and queued saves follow the open twin
* fix(diff-comments): one host-qualified copy per change; async delivery finds its copy by note id
* fix(diff-comments): a copy is its row's own host; unstamped rows keep the owner their repo names
* fix(diff-comments): the open host picks a twin by the owner index and never falls back to another host's copy
* fix(diff-comments): a worktree's saves stay in one queue when its row gains a host stamp
* fix(diff-comments): one queue and floor per worktree id; paired servers stay separate copies
* refactor(diff-comments): keep main's queue and floors; scope rows only when two hosts publish the id
* fix(diff-comments): per-twin rollback floors that follow the mutation chain; write module split out
* fix(diff-comments): a queued save finds its copy by the list its chain produced
* fix(diff-comments): name the row's own host on every save, and treat a refused save as failed
* fix(diff-comments): a queued save keeps its rows' identity even before a twin appears
* fix(diff-comments): a queued save follows its row when a listing stamps it with the write's host
* fix(diff-comments): refuse a write when the open host's copy isn't listed and another host's is
* fix(diff-comments): a lone unstamped row's queued save survives a stamping refresh
* fix(ssh): move the managed tunnel's is-current check beside its record (fix red main)
orcad-managed-tunnel-manager.ts reached 301 counted lines when #30891 landed on
#30888, failing every PR's static analysis. The six-field check that the recorded
tunnel already matches the connection and deployment moves, unchanged, into
orcad-managed-tunnel-active.ts, which owns that record.
* style: format the 22 files main merged unformatted (fix red main)
CI's format check fails every PR on these files. Formatting pushes
NativeChatStructuredSession.tsx one line past its 400-line limit, so its one-use
question list is inlined where questionBody is already non-null (identical
behavior: agentSessionPromptQuestions is pure and ran on every render).
* fix(renderer): host writes route only through the strict owner, and twin projections stay on their host (review-5 #2#9#11)
A selected ssh: host that two paired servers proxy now resolves ambiguous at the one
selected-host resolver, so no caller can fall back to this app's own SSH connection.
File Explorer and SSH mutation expectations derive the full {endpoint, place} pair from
resolveOwner, so a server folder's nested SSH target stays the write's host. A local
lineage write projects onto its own host's rows only. The id-only store-write ratchet
now also covers persistence repo-row lookups by id alone (review-5 #10's shape).
* fix(ratchet): the id-only lookup scan also sees optional chaining and destructured ids
* fix(renderer): an unstamped folder keeps reading its host from its repos; editor focus fallback left for a follow-up
* docs(renderer): the File Explorer owner comment names its folder fallback
* fix(linear): show a saved Linear credential that cannot be decrypted
The new-workspace Linear search treated a decrypt failure as an empty result, so a connected workspace still said to start typing. Record the existing decrypt message and offer Settings.
* fix(linear): open Linear settings without an Active Server read
The settings helper read activeRuntimeEnvironmentId, which the focus-read check counts as the Active Server setting. Resolve the source row's host id instead, which is the same host the helper already used.
* fix(linear): carry a Linear read's credential error to the composer
The composer showed "Start typing to search Linear issues." when the saved Linear credential could not be decrypted. The previous attempt wrote the read failure into the host-wide Linear status, which describes the Active Server and every workspace on it, while composer reads run on the repo's host and selected workspace. That caused search loops, blocked healthy hosts and workspaces, and a latch that stopped retrying.
Now Linear search and list reads reject with the plain decrypt message instead of resolving empty, the composer's URL lookup asks issue reads to do the same, and the composer keeps that error per request: it clears when the next read starts and shows in the Linear and Smart tabs (and the attach-a-link search) with a Settings link that opens the Linear card on the read's host. Nothing writes read failures into the shared status any more. The Tasks page keeps showing no rows plus the error, as before.
* fix(linear): keep attach actions and a steady error on Linear read failures
- Type the composer test's repo fixture so CI's typecheck passes.
- Attach a link: a Linear read error no longer hides the status row. The Jira-URL mode switch and the Linear-URL Retry stay; only the duplicate sentence is dropped when the empty list already shows it.
- Composer: the status refresh after a first credential failure re-runs the same read. Keep the error on screen through that re-read instead of flashing a loading skeleton.
* fix(linear): keep the attach status row in place; share the integrations Settings path
- Attach a link: keep the status row mounted (empty) when the result list already shows the Linear read error, so the Reference type button doesn't jump across the row.
- Composer: Jira and Linear Settings buttons now share one path that picks the source's host with getTaskSourceRuntimeSettings, so the Linear one no longer re-implements it. No behavior change.
* fix(linear): make the Linear Settings action a list row; keep Jira URLs on the Linear tab quiet
- Composer: the credential error and its Settings action are one list row (red sentence plus "Settings"). It fits the capped results popover and is reached with the arrow keys like every other row; Enter on it opens Settings. It is never highlighted by default, so a plain Enter still moves to the agent picker.
- Attach a link: on the Linear tab, a pasted Jira URL shows only the existing "Select Smart or Jira" status, as before; the Linear search of the URL text no longer adds a red Linear error beside it.
* fix(linear): show the Linear read error as one plain line with no Settings action
The Settings action inside the results list needed its own highlight and Enter rules and still could not fit the capped popover. The list now shows one compact red line, in the Linear and Smart tabs and in attach-a-link: "Orca can't read your saved Linear login. Reconnect Linear in Settings to search issues." It names the fix in the same words as the neighbouring not-connected hint. The highlight resolver, the input's Enter handling, the composer's Settings handlers and the prop plumbing are back to main.
* fix(linear): show an unreadable Linear login as a Reconnect Linear empty state on the Linear tab only
The composer's Smart tab no longer mentions Linear's login; the Linear tab, Edit workspace links and the Tasks page show a calm title and sentence where results would be, matching the not-connected state.
* fix(linear): drive the Tasks Reconnect card from the selected scope's read; plain words in workspace notices
Host-wide status flags any broken workspace, so a healthy selected workspace with no results no longer shows Reconnect Linear. Projects and Views notices say the unreadable login in plain words.
* fix(linear): keep the Tasks Reconnect card up while the same list re-reads
The list effect cleared its error on every re-run, so a refresh flashed the raw status bar until the read failed again. Clear it on success instead.
* refactor(linear): drop the unreachable typed-row padding from the Linear tab's Reconnect state
The Linear tab never has a typed-text row, so the state always uses the roomy padding.
* fix(linear): derive the Tasks Reconnect card from the plain list's read error in the board
Avoids widening the selection model's existing type assertion; project and view tabs report errors inside their results.
* fix(linear): say the unreadable Linear login in one short line
* fix(orcad-migration): a source fenced on a manifest no request can carry can be aborted by its identity
A desktop that fenced a manifest just under the export cap before #30836
could never send its abort: every catalog request carries the whole
manifest and refuses it before sending. The abort now asks the server by
migration id and digest instead (orcad.migration.abortCatalogByReference,
behind a new capability), and releases only on the server's durable
absent answer. A committed server still refuses the release.
Co-authored-by: Claude <noreply@anthropic.com>
* test(orcad-migration): cover the durable abort by reference and its RPC method
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
setWorktreesPinnedAndReveal took bare worktree ids and looked each up with
getKnownWorktreeById(id) and no host, so pinning one card of a twin (the same
worktree published by a local and an SSH host) could pin the other copy.
The action now takes host-qualified targets and skips a host-less id that two
hosts publish. The sidebar Pinned drop resolves its ids through the drop
resolver (dropWriteRows), so it pins the dragged copies; the drop-commit
context hands the active drag's copies to the pin commit. The context-menu pin
passes the clicked row.
* fix(runtime): a terminal spawn on an SSH twin no longer wakes or is refused by this computer's sleep
The host's sleep state was keyed by the bare worktree id, which a repo
registered on two hosts publishes twice. A spawn on the SSH copy found the
local copy's sleep, so an automatic probe was refused and any other spawn
woke the local sleep. Key both sleep maps by host plus workspace, with the
sleep side deriving its host the way a spawn does.
Co-authored-by: Claude <noreply@anthropic.com>
* fix(runtime): a runtime-stamped project's sleep keys on its nested SSH target, as its spawns do
Co-authored-by: Claude <noreply@anthropic.com>
* fix(runtime): derive the sleep key from the launch-host resolver the launch scope uses
Predicting the host from one repo row disagreed with the launch path for a
runtime-stamped project with a nested SSH target. Reuse resolveWorktreeLaunchHost
so a sleep and the spawns of the same workspace always name one key.
Co-authored-by: Claude <noreply@anthropic.com>
* fix(runtime): a sleep stops and files only its own host's terminals, and refuses an unresolvable host
Sleeping this computer's copy also stopped the SSH twin's live PTYs and filed
them under this host's key, where the twin's spawn could never wake them.
Resolve one sleep scope (key and PTY fence) per sleep from the launch-host
resolver, fence the live sets with it, and refuse the sleep when rival rows
disagree about the host instead of guessing local. The maps are typed and
named by host-scoped key.
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(native-chat): keep transcript text clear of the message rail
* fix(native-chat): measure the rail's width before first paint so a narrow chat opens without a text shift
* fix: synchronize native chat rail resize updates
* test: give rail navigation viewport a measured width
Settings > Chat > Providers showed Antigravity's sign-in as a filled grey box holding only a
bright primary "Sign in" button. The host learns whether its chats are signed in only when a
chat starts, so after launch the status line had nothing to say and the box sat empty.
The area is now a plain row under the provider's name: "Account" on one line, the status on the
next, and an outline "Sign in" button. Until the host knows, the status line says chats need an
Antigravity sign-in separate from the agy terminal account.
* fix(ssh): two overlapping bridged attaches hand back the viewer they replace
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): the newest bridged attach keeps its viewer even when an older one answers last
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): an older bridged connect still paints its screen when a newer attach keeps the viewer
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>