Retain reveal sequence coverage until a parsed snapshot covers it, preventing a duplicate text-only replay from clearing newer terminal images. Keep conservative fallback recovery for unknown episodes, missing metadata and different terminal processes.
Verified against current main: 406 tests, full application typecheck, changed-code quality and four fresh hidden rendered restore checks pass. No xterm patches or dependency changes.
Removes the re-export lines in relay-region-preference, relay-session-broker,
browser-host-page-placement and browser-host-page-reconciliation-plan (no barrels).
* fix(runtime): show Re-pair required for revoked servers and re-pair in place
A server that revokes this computer's pairing token was labeled "Update
required" because auth refusals and protocol mismatches shared one blocked
state. Carry the blocked reason locally, label revoked hosts "Re-pair
required", and add a Re-pair action (sidebar host menu + Settings server
row) that verifies a new pairing link reaches the same server before
replacing the saved pairing.
* fix(sidebar): stop host header menu clicks from toggling or dragging the row
* fix(sidebar): vertically center host header label and menu button
* refactor: move the contested-workspace primary-host rule to src/shared (move only)
* fix(layout-owner): main routes a twin repo's workspace to the same primary host the window saves to
* fix(layout-owner): route a twin repo's single-host worktree to the host that holds it; hydrate twins the same way
* fix(layout-owner): twin-repo routing counts only runtime owners from the catalog, matching the window; one table checks both sides
* fix: drop the duplicate layout-owner switch import after merging main
* fix: type the twin-routing fixture and partition set
OMP ends every user bubble with OSC 133 B C D;0 to close its own prompt zone.
Orca read that D as the shell's command finishing, retired the pane's hook
authority mid-turn and deleted its status row, so the phone's chat view lost the
provider session it reads the transcript by. A D;0 directly after B C is now an
app's zone close, not a command exit.
Deletes remote-layout-snapshot/index.ts and workspace-layout/old-client/index.ts. The compat-boundary
check now lists each folder's entry modules, so deleting the folder plus its listed importers'
import lines still removes the path.
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.
Parity test: one string-skip helper, no infinite loop on an unterminated block comment, no internal shorthand. Serve test: orcadServeArgs takes a bindHost instead of the test splicing argv, typed readiness, and stderr back in the daemon failure. Watcher test: a sentinel at both watched levels.
Deletes orcad-runtime-lifecycle.ts and the re-export blocks in orcad-managed-tunnel,
orcad-local-build-hash, orcad-remote-launch, orcad-remote-node-runtime, orcad-update-plan,
relay-protocol and shared/orcad-migration-manifest; importers and test mocks name the real modules.
Importers call worktreeIdsEqual from shared/worktree/id directly (the alias was a renamed
re-export). isRemoteAgentHooksEnabled becomes isWslAgentHookRelayEnabled; the env var and its
parsing are unchanged. ws-transport inlines its one-line HTTP server factory, and a duplicated
acquisition-id doc comment goes.
* fix(session-history): find every agent's older sessions in Workspace and Project views
Session History kept only the newest sessions across every agent, then filtered
by workspace; only Claude, Pi and CodeBuddy got their older in-scope sessions
back. Every agent now declares how its cwd is found without a full parse, and
the scan selects in-scope sessions before the recency cap: Codex and other
header agents by their opening record, SQLite stores by their directory column,
the rest by their cached parse. The scan and the cached-list cut share one
scope rule.
* fix(session-history): keep discovery under the file length limit
* fix(session-history): type the scope walk's file list
* Keep session history scope complete before applying the display cap
* test: align session history scope fixtures
* perf(session-history): settle known out-of-scope transcripts without a per-file read
A scoped scan walks every transcript, and each one cost an await, a parse-cache
database lookup and, for Codex, two head reads every scan. Remember each file
version's settled cwd (from its opening records or its parse), drop settled
out-of-scope files synchronously before the async walk, and read Codex's
roughly 20 KB first record in one 64 KB read.
* fix(session-history): let the parse cache decide scope before transcript heads
The settled-cwd prefilter let a transcript-only verdict reject files whose
current parse (hot or cold) says otherwise: a Cursor chat whose meta.json
appeared later, or a Codex session whose parsed latest cwd moved after its
row left memory. Restore parse-cache-first selection; keep Codex's single
64 KB first read.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* feat(runtime): remove an agent's downloaded runtime only once nothing on the host can still run it
managedRuntime.remove {agent} (host-admin, capability managed-runtime.remove.v1)
deletes the agent's releases inside the launch gate. Before anything is deleted the
gate aborts starts under way (chat starts and conversation copies), stops the
agent's loaded chats on this host, and proves every retained child gone: failed
chat starts and sign-in children whose close was never proven. Every close settles
before the gate releases; if any owner stays unproven, Remove fails and keeps the
files. Stopping chats by agent is now one generic host helper that sign-in shares.
Install refuses while a removal runs, Remove refuses while an install runs, and a
removal cannot be cancelled.
* fix(runtime): status says whether a copy is there to remove, damaged copies included
canRemove is set whenever Orca's copy is present, whole or damaged, so Settings offers Remove for a
damaged copy too. The failed-start retain is dropped: the launch gate already holds every child it
admitted until it exits.
Since #30232 it accepts null and covers both remote: and app SSH ids, so the extra isRemoteRuntimePtyId / parseAppSshPtyId / null guards never change the result.
* 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
Its private POSIX copy predates areWorktreePathsEqual's POSIX guard and WSL UNC fold, which now give the same answers; the one difference is macOS treating /tmp and /private/tmp as one directory, which they are. Also reuse looksLikePosixAbsolutePath in the frozen comparison key.
failedBefore read the recorded failure before the host lock, so a check waiting behind one that failed still tried the update and noted a stale reason. Rename retryRecordedFailure to failedThisLaunch and read both inside the lock. Also share withDetail and drop a test that could not fail.
Every split pane's tab strip stays mounted, so focusing a pane used to add or remove its action buttons and reflow the tabs. Quick-command shortcuts also fired from hidden or unfocused groups.
- Keep the Pane Actions and quick-command buttons mounted in every strip. Unfocused strips dim instead of disappearing, so focus no longer reflows the tabs.
- Hide the button labels by strip width with container queries, so narrow split groups stay icon-only.
- Only the focused, visible group registers quick-command keyboard and Electron shortcut listeners. An open menu closes when its group loses focus.
- Add "Close split pane" to the tab context menus, shown only when the group is one of several splits.
- Stop Radix from restoring focus to a stale group when the pane actions menu closes.
Worktree ids built as `${repo.id}::${path}` by hand are how one worktree got two
spellings (#30150). New code composes them through src/shared/worktree/id.ts; the
ratchet counts the remaining hand-built ids per file (18) and may only go down. Six
byte-identical `${repo.id}::${repo.path}` sites move to getRepoMainWorktreeId.
A manifest under the 768 KiB validation cap can still overflow the host's 1 MiB inbound
frame once sealed (base64 adds a third), more so when a commit carries its blob list. The
host answers such a frame by closing the socket, so the move failed with a lost answer.
The catalog client now measures the request against REMOTE_RUNTIME_MAX_INBOUND_REQUEST_JSON_BYTES
and refuses it with the existing 'too large' guidance, unsent.
* feat(antigravity): sign this host's Antigravity chats in with the server's own Google sign-in
Start spawns the Antigravity ACP server exactly as a chat start would (pinned
runtime, private temp, owner-only chat folder), behind the launch gate that
first stops this host's running Antigravity chats, and asks it to authenticate
with oauth-personal. A host without a pinned server build refuses Start, as it
refuses chats. The server's sign-in line now yields its link (validated as
Google's sign-in page back to a loopback port here) instead of ending the
connection signed out. Link long-polls the flow's state (25 s, early on change),
Cancel ends it, a new Start replaces it, and it expires after 300 s. All run on
the execution host behind accounts.antigravity-chat-sign-in.v1 and need
accounts-admin. List reports chatSignIn {saved, signingIn}; saved is a stat of
the server's own token file, which Orca never reads.
* Retain unclosed sign-in children and detach ended polls
* Narrow sign-in regression launch fields
* Keep fake sign-in fixtures independent of host support
* fix(antigravity): chat sign-in never installs the server and keeps its flow on the shared revisioned state
Sign-in resolves the server exactly as a chat start does and fails with the
not-installed reason when no copy is found, instead of downloading one. Its
flow state is a RevisionedState, the same publish-and-wait primitive the
runtime installer uses; replacement, close proof and the Link wire are unchanged.
* fix(antigravity): the chat sign-in status names the running sign-in, so a client that comes back can follow and cancel it
chatSignIn gains an optional flowId while a sign-in runs. Before, a sign-in nobody watched held the
chats' fence for up to 5 minutes and no client could see or cancel it.
* feat(antigravity): clients follow this host's chat sign-in by revision, as they follow an install
* fix(antigravity): name the sign-in state apart from the sign-in function so the module parses
* fix(antigravity): chat sign-in status is pushed to subscribers, failures read as plain sentences
accounts.antigravityChatSignInSubscribe (no params) sends the full sign-in
status as a snapshot on subscribe, then one frame per state revision, and
closes with the transport. It replaces the accounts.antigravityChatSignInStatus
long-poll, its long-wait lane and the bounded revision wait, as the install
status already did.
A failed sign-in now tells the person what happened in plain words (no
subscription, account not eligible, not approved, not installed, or a
generic retry) and logs the raw error instead of showing it.
Stopping the running chats before a sign-in waits for every close to settle
before it reports, so the fence never lifts while a close is still running.
* feat(antigravity): finish a chat sign-in from the address pasted by hand
When the browser runs on another computer than the host (SSH or a paired remote host), Google's last page points at a port on the host that the browser cannot reach, so the sign-in never finished. accounts.antigravityChatSignInComplete takes that pasted address, checks it is Google's answer to this flow's own link (same loopback origin and path, one matching state, one code or error, no userinfo or fragment, a Google issuer if any), and sends it once to the server's listener on the host with a single direct GET. The flow shows a new verifying phase while the server finishes, and refuses a second paste.
* fix(antigravity): Cancel answers once the sign-in has unwound, so a chat sent right after it is not refused
* fix(antigravity): the sign-in reads its launch sources through the shared helper, keeping the session create support within its line limit
Main's file already sits at the limit, and the runtime method only forwarded to the helper.
* fix(antigravity): a rejected pasted sign-in address says what is wrong with it
Every bad address used to get one sentence. It now gets the specific one:
too long, not a complete address, not this sign-in's, not exactly one
Google answer, or not from Google. The checks themselves are unchanged.
* fix(antigravity): chat sign-in follows the server's answers and belongs to the client that started it
The host's chat sign-in status was a check that the server's sign-in file
existed, which says nothing about whether the sign-in still works. The
link came only from one printed line, a new Start from any device
silently replaced a running sign-in, and a second link replaced the first.
- The server's BROWSER is now a one-line helper run by Orca's own
executable in Node mode, which prints the link on error output; it is
checked before every server start, and the printed line is still read,
on standard output as well as error output. No browser opens on the
host, Windows included.
- A sign-in belongs to the client that started it. Others see it in
progress without its link and cannot start, cancel or complete it; it
ends on its own after five minutes.
- After Google answers, a session on the same server must start before
the sign-in reads as signed in.
- The host's status is the server's latest answer: signed in when a chat
session or the sign-in's session starts, signed out when a chat hits
the sign-in prompt or is refused for sign-in, and unknown until then.
Nothing is read from the sign-in file and nothing is saved.
- A second, different sign-in link fails the sign-in.
* test(antigravity): the launch parity record shows the sign-in link helper as the server's browser
* fix(antigravity): a sign-in owns its work until it has unwound, and reads only whole, valid sign-in lines
Problems in the previous round: a sign-in could read as finished while its
server or pasted-address request still ran, a server refusing a sign-in
after a chat started left the host shown as signed in, the checking
session stayed saved in the server's own store, and a broken sign-in line
could count as the server asking to sign in.
- The browser helper is POSIX only, with Node mode set inside the BROWSER
command; Antigravity chats are not offered on Windows.
- Sign-in lines are read by source: standard output carries the printed
line, error output the printed line and the helper's. Only a whole,
valid link is an answer; a malformed one on error output is dropped, and
on standard output it ends the server. Standard output goes through the
protocol's own line reader, including a last unterminated line, and
error output keeps a whole link while its diagnostic tail stays small.
- Any request the server refuses for sign-in marks the host signed out.
- A sign-in is the host's one operation from Start until its work has
unwound; a Start before then is refused, and Cancel or expiry also stops
a pasted address on its way to the server.
- A different second link fails the sign-in at any point, including while
it is being checked.
- The checking session's saved conversation is removed once the server is
proven gone, only when its metadata names the sign-in's own folder.
- A pasted address is checked for length before it is trimmed.
* Preserve ACP auth answer ordering and sign-in cleanup
* Fix ACP settlement lint and await sign-in cleanup in fixtures
* test: update Antigravity browser launch snapshots
The Disconnect is recorded synchronously but the teardown that disposes the connection waits
behind the target's lifecycle; until then a drop's backoff, a wake retry or reconnect() could dial
the host again. SshConnection now consults the same per-host record the connection manager does.
* fix(native-chat): Claude, Pi and ACP chats are Working only while the agent holds the send
Each adapter reports the sends it still holds (Claude's dispatch waiters, Pi's sends until
settlement, ACP's unsettled prompt and steers). A late settlement's write runs inside the
per-send handover window, so a send reads held until its row lands, and a failed write
reads as doubt at once. Removes the idle-release API and its callers.
* Fix interruption reporting from held send ownership
* Simplify ACP reading control cleanup registration
* Preserve provider child identity when reattaching
* Replay original attach in recovery overlap regression
Gate evidence prose and evidence runs record what ran then, so the check reads only each gate's
commands and test files (baseline 53 -> 14). Four evidence-pinned historical commands that named
deleted tests are removed from their gates' commands and their runs point at the successor via
currentCommand, the manifest's existing mechanism. One live command drops a deleted updater test.
The managed-SSH catch-all route is renamed remote-sources and its test joins the route contracts.
* fix(session-history): the scan strip and the lines under it stop jumping during a refresh
The strip under the Session History search box dropped out for a moment on
every rescan (main cleared the entry before the new scan took over, and the
panel hid a finished scan until its list landed), on every worktree switch
(the new scan key had nothing yet), and the warning and error lines under it
hid while a refresh ran. Each blink moved the whole list up and back down.
Now a new scan takes over its key at once, a failed one puts back the last
finished scan, the panel keeps following the previous key until the new one
has progress, banners stay while the list they describe stays, and the error
line clears only when a refresh succeeds.
* style(session-history): collapse the banner line
* fix(session-history): keep the refresh hook under its line limit
* test(session-history): scan listeners no longer have a sent hook
* fix(session-history): retain truthful scan presentation through retries
* test(session-history): brace scan cache eviction loop
ui-slice-hydration-sanitizers kept its own 7-day literal while its comment requires it to track main's HYDRATE_MAX_AGE_MS; both now read AGENT_STATUS_HYDRATE_MAX_AGE_MS.
* feat(native-chat): a chat whose host has no program to run shows one clear install message, with the message box staying in place, disabled
A new chat replaces its Start-a-chat copy with an install message (what it is, its size, one Install button, then steps, progress and Cancel). A chat with messages gets a slim strip above the composer instead. Settings' row is unchanged.
* fix(native-chat): the install message drops its Settings hint until that Settings row exists
* fix(native-chat): satisfy the not-found lint on the install card's phase lookup
* fix(native-chat): the install message follows the design system: a still full bar once the download is done, and a plain ghost Cancel
* fix(native-chat): the install card's size caption uses the download-size string main already has
Main's Antigravity work added the same "downloadSize" key to the install strings, so each locale
held it twice with slightly different text. The card now reads main's one string, so the hero's
caption under its button ends in a full stop like the Settings row's.
* refactor(native-chat): the install row keeps only its Settings layout
A chat now shows its own install card, so nothing renders the row's chat layout any more: Settings
> Chat > Providers and the Antigravity accounts section are its only users. The row and its view
lose the surface choice, the chat-only heading string goes from every locale, and the view always
says and offers what Settings did.
* fix(native-chat): the install message points to Settings › Chat › Providers again, now that main has that row
Only chats on this computer show it: that row installs here, not on an SSH or paired host.
* fix(native-chat): a disabled chat box keeps its placeholder and dims, so it says why it can't take a message
The editor drew its placeholder only while editable, so an install-blocked or held box looked
empty and enabled. The plain text box already dims and keeps its placeholder.
* fix(native-chat): keep setup reachable and honor composer input availability
* fix(native-chat): update picker availability after commit
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
Each list that decides delivered / undelivered / recoverable / definitive by code now has a
test naming its codes, the message fragments it relies on, and nearby codes it must not
treat the same. Includes the #30146 shape: a client-minted runtime_manually_disconnected
keeps a user's close for replay. Tests only; no production change.
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.
Render committed branch changes as virtualized SectionList rows for clean and dirty working trees, keeping all files accessible without delaying Back. Share the existing sleek scrollbar rules with mobile web pages and retain explicit hidden-indicator settings.
Fixes#30170. Review reference: #30171; thanks to @zhangxaochen for the diagnosis and initial proposed fix.
Co-authored-by: zhangxaochen <zhangxaochen@163.com>
* 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
The shared copy parsed remote: ids fully, so a malformed or newer-format id read as local
to pty-running-work-probe. The renderer copy already used the prefix check; it is now the
only definition, in src/shared.
It flaked on loaded CI shards when a single write never reached the watcher. Files are now rewritten until seen, readiness is proven before the storm, and a sentinel written after the storm replaces the 300 ms debounce sleep. Disabling the ignores still fails it.
The legacy relay bridge counted viewers per PTY id and released them by PTY id. A pane whose
attach answered after it left (or after a reconnect replaced the bridge) handed its viewer back
by PTY id, so the release landed on the current bridge and dropped a successor pane's viewer:
that pane stopped receiving output. Each attach now returns an acquisition id (in-app IPC only;
the relay wire is unchanged) and detach releases exactly that acquisition.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ci: ratchet src/ path literals in CI config, workflows and e2e code that name no file
* ci(path-literal): resolve relative imports exactly
* ci(path-literal): resolve against tracked files only; read ./src imports
* ci(path-literal): scan src/packages and src/types references too
* ci(path-literal): skip paths quoted in comments
* ci(path-literal): keep a recursive glob's boundary
* ci(path-literal): strip comments with a string-aware scan; any import occurrence is exact
* ci(path-literal): imports need a module or index; relative globs and folders stay prefixes
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.
Removes modules with no importers on main (and their tests), the unused
RELAY_DEPLOY_TEARDOWN_TIMEOUT_MS constant, and the ssh-connection-utils
re-export of ssh-auth-resolution that only its own test used.