Repository navigation
Conversation
…arts re-read nothing (stablyai#29401) * Persist session parse results beyond the memory cache limit * Expire Session History parse rows by idle time instead of a count cap The parse cache dropped its oldest row once it held 4,096, so a scan over more transcripts evicted its own upcoming hits and re-parsed every file on every scan. Rows now stay until no reader has used them for 24 hours, swept after each completed History scan and each search-index pass. Only the live parser state keeps a 4,096 count bound; dropping it costs one whole re-read of a file that changed, never a re-parse of one that did not. Removes the overflow directory and the orcad persistence wiring from the earlier revision of this change. * Expire only rows History lists, and keep the newest transcripts under bounds The search pass ran the idle sweep every 20 s but never re-stores History's unchanged rows, so with search on a day away from History dropped them all and reopening it re-parsed everything. Only a completed History scan sweeps now, and it expires only rows History listed (on a monotonic clock). Rows only the search index or a title lookup created keep main's 4,096 count bound, so an index build can't grow the scanner process without limit. Scans store newest transcripts first, so every store-order bound dropped the newest, most active files: the saved snapshot kept the oldest ~11k rows and the resume bound stripped the newest files' parser state. The snapshot is now ordered by transcript mtime, and both count bounds drop the tenth of their rows with the oldest transcripts. * Drop the 24 h idle sweep; History rows live for the process The sweep let whichever listing ran last decide when a row died, and listings differ in scope: a smaller one (the phone's Agent History, a lower desktop limit) expired rows only a larger one used, so the next Unlimited scan re-parsed them. Rows History lists now live as long as the scanner process; the snapshot's newest-transcripts cap bounds what survives a reload, and rows other readers made keep the 4,096 bound. Also clears the resume bookkeeping for a row the unlisted bound drops in the same store. * Skip resume bookkeeping for a row the unlisted bound just dropped Running the resume trim first let a row the unlisted trim then deleted push the resume set over its bound and cost 409 live resume points. * perf(ai-vault): store the Session History parse cache one row per transcript The parse cache was persisted as one JSON file, rewritten whole after any change and capped at 64 MB / 1M tokens (about 11k rows). Past that cap, a restart re-read the oldest unchanged transcripts, and an agent writing while History was open rewrote 10-27 MB every few seconds. The scanner child now keeps the rows in their own SQLite file. A miss in the in-memory map reads the row by path; changes are written per row, debounced and chunked; deletes run immediately. A used_at column, refreshed at most daily, expires rows nothing has used for 30 days, so rows for transcripts deleted outside Orca have a way to die. The old JSON file is imported once and removed. Electron main only records the path for the child. The rebuild-on-corruption open moves out of the search index into a shared versioned cache database helper used by both. * fix(ai-vault): import the old parse cache at open and expire rows only for missing transcripts Review round 1 on the per-row parse cache store: - The old JSON was imported only by a History scan or search pass, and only into an empty database, but deleted either way. Tab-title sync writes a row first, so the import was skipped and the first History open after updating re-read everything. The import now runs when the database opens, before any lookup, write or delete can reach it, keeping rows already present. - A 30-day last-use expiry also dropped rows for transcripts that still exist, so opening History less than monthly re-read them. Rows unparsed for 30 days are now checked on disk and dropped only when the transcript is gone, then the freed pages are reclaimed. The daily last-use rewrite goes away. - 8 KB pages (a ~2.3 KB row filled a 4 KB page), busy_timeout before the WAL switch, and lookups served from rows still waiting to be written. - Revert accidental pnpm-lock.yaml churn. * chore: revert accidental pnpm-lock.yaml churn * fix(ai-vault): never stat WSL transcripts from the parse cache recheck A stat on a \\wsl.localhost path outside the scanner's running-distro filter and gated WSL lane could boot a stopped distro or hang on a stuck one. WSL rows now skip the existence check and age out after 180 days unparsed. SQLite-backed session keys use the shared OpenCode path splitter. * fix(ai-vault): register the parse cache database with the WSL mover census Each schema keeps its own meta table again, so the shared open helper creates none and the search schema's SQL is unchanged. The parse cache's tables are recorded as not moved: a cache rebuilt from transcripts. * fix(ai-vault): a forced whole read never reuses a row a failed database delete left behind
…pen attached pictures full size (stablyai#29075)
…g the payload (stablyai#29092) * fix(native-chat): decide provider failures by kind, never by searching the payload A Claude chat opened with a red "claude · message:system:init" card whenever one of the user's MCP servers failed to connect. The classifier searched every nested field of a frame for failure-looking values and let that outrank the per-kind catalogue, so the session-start roster's `status: "failed"` turned the whole frame into an error row. The same search caused the earlier failed-background-task opcode cards. A frame is now a failure only because its kind is one, or because a field its kind declares as its outcome says so (Claude `hook_response.outcome`, Codex `hook/completed` `run.status`). A Claude turn result is a failure when the result journaling says so, since that depends on whether the user stopped the turn. With nothing left to override, the typed-translator coverage list, its option and the task-frame sentence seam go. Failed MCP servers write no row for Claude or Codex. * test(claude): an uncatalogued frame keeps its sentence without becoming a failure * fix(native-chat): keep Claude authentication errors visible
… permission tests (stablyai#29891) stablyai#29335 gave Grok chats their own permission modes, so on a paired host that predates per-chat modes a Grok chat is now checked like Claude and Codex; that is intended. stablyai#29442's tests, merged a day earlier, used Grok as the agent without modes. Use Gemini, which still has none.
…yai#29860) * fix(mobile): stop rewriting the cached manifest on a same-build hit A same-build cache hit rewrote manifest.json (write next, delete, move) while the native view was reading it. A read inside the gap reported generation-unreadable, which discarded the cache and redownloaded the same build. A matching build id now writes nothing to the generation directory; the fresh routes still apply to the current session. * refactor(mobile): drop code the manifest rewrite left behind Opens a same-build hit straight from session.cached, folds the manifest file name and parser back into the generation store, removes the unused moveFile adapter and fake hooks, and trims tests that passed on main.
…tablyai#29874) A server-pushed recovery the host marks unavailable carries an empty image that is not the buffer. Applying it cleared screen and scrollback. Classify recovery replies like tagged requests and retry instead. Co-authored-by: Claude <noreply@anthropic.com>
…fore typing (stablyai#29892) * fix(terminal): replay a viewport claim made during remote recovery before typing Recovery released typing at the host's subscribed ack, but claims and resizes still returned early until the snapshot finished, and nothing replayed them. Typing then reached the host at another viewer's grid. Defer the intent and replay it on the stream before releasing input; send claims directly once the subscription accepts input. Co-Authored-By: Claude <noreply@anthropic.com> * fix(terminal): spend a deferred claim only once the host acks the subscribe Co-Authored-By: Claude <noreply@anthropic.com> * fix(terminal): stop the ackless replay from marking a closed stream healthy Co-Authored-By: Claude <noreply@anthropic.com> * fix(terminal): drop a queued resize when recovery defers the viewport Co-Authored-By: Claude <noreply@anthropic.com> * fix(terminal): keep a deferred viewport claim when its replay write fails Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
…pt running agents on windowless hosts (stablyai#29885) orcad built its runtime without the persistent claim signer, so each restart minted a new key and the surviving terminal daemon could not match the old owner: resuming the same conversation started a second writer. Load the same signer the desktop uses from orcad's own user data. A workspace-slept running agent's record was left for a window wake that a windowless host never has, so opening its tab started a fresh agent. Such a host now resumes it in the pane, under the existing duplicate-writer checks and recorded-flags-only rule.
…hichever machine connects first (stablyai#29883) * fix(ssh): leave a stopped pre-claim install on a shared home unclaimed until its machine is known * fix(ssh): judge a pre-claim install by its activation record; the printed claim creates its directory
…riment was turned off during (stablyai#29889) A failed or deferred first add kept its detached distro holder with no record to find it; an add in flight when WSL hosts were turned off still finished as a ready host.
…s the migration lock (stablyai#29875) The test raced two prepare() calls for the migration lock; when the failing-keyring start won, it correctly started a fresh key and the old credential could not open.
…-owner build only) (stablyai#29886) * feat(layout): report window saves that still change main's layout (dark) In a layout-owner build, each window session save (session:set, session:patch, the shutdown checkpoint) is projected to main's published layout before and after it is written. Any workspace whose layout the save changed is logged, and appended to ORCA_LAYOUT_SAVE_DIVERGENCE_LOG when set, so evidence runs can show which tab changes still reach main only through the save. * feat(layout): name the fields a diverging save changed * fix(layout): the save check diffs main's whole layout model and never fails the save Review: a failed log write could reject a save that already landed; and the published layout omits editor views, pane records (sleeping agents, incarnations, titles), closed-tab tombstones and the workspace records, all of which a later save trim would drop. * fix(layout): the save check covers record-only workspaces and row owners, and counts rebinding last * fix(layout): the save check counts first binds as rebinds and keeps the count past the cut
…tablyai#29873) A visible repo on another host sharing the retained source's repo id un-hid the retired SSH host and its worktrees in CLI and phone listings.
stablyai#29853) * fix(wsl): run wsl.exe probes from the system root, never the profile WSL holds wsl.exe's working directory busy for about its idle timeout after the probe exits. Since stablyai#29209 the orcad startup probe ran in the profile, so a temporary orca serve profile could not be deleted for about a minute (diagnosed in stablyai#29845). * test(orcad): drop the WSL profile-hold wait from serve parity Reverts stablyai#29845's extended EBUSY retry: wsl.exe no longer runs in the profile, so nothing outside the test's process tree holds it. * build(cli): list the WSL spawn-directory module in the CLI typecheck project
… is selected again (stablyai#29893) * fix(session-tabs): seed the first terminal when a warm empty worktree is selected again A warm stream that got its only empty answer while its worktree was unselected correctly held the bootstrap, but selecting the worktree again reused the stream without re-deciding it, so the workspace stayed empty. Keep the held verdict and re-decide it when the warm entry is selected. Co-Authored-By: Claude <noreply@anthropic.com> * fix(session-tabs): drop a held first-terminal verdict superseded by newer host evidence Co-Authored-By: Claude <noreply@anthropic.com> * fix(session-tabs): hold only single-worktree frames as deferred seeds Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
…d-start scan (stablyai#29894) On cold start the conversion skips its own worktree fetch and waits for startup's scan, so the one rehome attempt found no server row and left the active workspace on the retired SSH owner. Retry the move once the startup scan completes, while the conversion is still current. Co-authored-by: Claude <noreply@anthropic.com>
…adless host (stablyai#29881) * fix(orcad): resolve folder workspaces for scoped browser commands Both orcad browser adapters resolved --worktree with the git-only selector, so a folder workspace failed with selector_not_found before reaching the provider. * test(orcad): parse browser results instead of asserting their types
…and cancel (stablyai#29023) * feat(acp): a managed runtime installs only when asked, with progress and cancel One installation per runtime owns every change to its releases: it reclaims old ones once, first, then installs one at a time. An install checks free space, downloads with byte progress, extracts, hashes each member (abortable), writes the receipt, then publishes by rename, the commit point: a cancel after it reports success. Reinstall rehashes the pinned release without downloading. A status read waits, bounded, for a newer revision and rereads the installed release from disk. * fix(runtime): Install repairs a damaged copy, says when unzip is missing before downloading, and reads a failed download plainly - Install over a damaged pinned copy downloads and verifies a fresh one, then swaps the damaged folder into staging, so there is no dead end; status reports the damaged fact. - A host that cannot unpack the archive (no unzip) reports it in status and refuses Install without downloading. - A network failure while downloading reads "Couldn't download …"; the raw error goes to the log. - Reclamation skips the whole pass when a release folder could not be read. * fix(runtime): a status read that can't read the installed files fails instead of saying not installed, and extraction uses the extractor the preflight found * fix(runtime): a host that can't report free disk space still installs, and says so in the log * fix(runtime): a download that can't be fixed by retrying says so instead of blaming the network * fix(runtime): Install unpacks the release itself, checks it starts before publishing, and Cancel answers with the outcome - The managed install unzips in-process (fflate, bundled into main), so a host without unzip can install; the unzip preflight and the missingProgram status field are gone. The Node runtime download keeps its own extractor. - A runtime may provide validate(): Install starts the downloaded copy once before publishing it, and an existing copy before reusing it; a failure reads "<agent> couldn't start on this host". - No 'cancelling' phase: Cancel resolves once the work has unwound and returns cancelled, or succeeded when it had already committed. - Byte progress every 250 ms; status rereads disk and PATH only on a first read or a phase change. - The declared download size is checked only for an uncompressed body. - An existing copy whose files hash correctly but whose receipt is missing gets its receipt rewritten instead of reading as damaged forever. * refactor(runtime): move the steps that put one release on disk out of the installer The installer class now holds only its lifecycle: the shared operation state, the one-at-a-time queue, install, cancel, status, reclaim and progress publishing. Reusing a whole copy, the free space check, the download, unpacking, checking each file, the start check, the receipt, the publishing rename and the failure sentences move unchanged into managed-runtime-release-install.ts, which gets the pin, folder, label, signal and two callbacks (progress, and the commit point right after the rename) instead of the class. No behavior change; this keeps the installer well under the file-length limit once Remove lands in it. * chore(deps): lock fflate for the in-process runtime unzip * fix(runtime): a failed download blames the release only for a proven mismatch, so a refused proxy reads as a network problem * fix(runtime): runtime downloads skip the HTTP cache, so Remove frees the space and nothing is stored twice
…out, never a same-path project on another host (stablyai#29896)
* fix(native-chat): retire acknowledged send echoes after catch-up * test: follow phone lint rules in reconnect regression * test: format reconnect message expectation * Keep current chat requests independent of loaded history * Read current chat requests in bounded cursor pieces * Bound desktop catch-up after independent request support * Fix current request subscription static checks * Match generated request schema import ordering * fix(native-chat): correct current request type boundaries * Accept readonly client capability fixtures * Exercise current request reads in wire compatibility inventory * Align current request fixtures with provider commit and RPC surface * test(native-chat): include current request browser capability * Keep the mobile chat session hook within its line budget * Format mobile current request readers * fix(native-chat): reopen once for a current-request version whose read keeps failing A deterministic read failure reopened the subscription, whose first frame re-read the same version and reset the backoff, so the pane resubscribed every 750 ms forever. * test(native-chat): type the current-request snapshot fixture * test(native-chat): wait for the hydrated open before driving the subscription
* feat(mobile): Resume in New CLI from a phone chat tab A chat tab on the phone had only Close in its long-press menu, so a chat that would not load left no way to reach its conversation. The phone now offers Resume in New CLI, like the desktop tab menu: the chat's host prepares the conversation and the phone opens a copy in a new terminal tab. The resume and fork startup builders move to src/shared so the desktop and the phone build the same command. * test(mobile): type the fake terminal create instead of casting its calls * fix(mobile): pass the host's fork source as is; match the quoted args in the test * test(mobile): give the fake host client the untyped mock the other client fakes use * style: format the shared resume builder and phone fork files * Fix mobile chat CLI fork creation on owning host * Fix mobile chat fork lint and split create owner helpers * Keep chat fork beside phone anchor across tab groups * Combine mobile terminal runtime type imports * Repair mobile create CI contracts after chat fork extraction * Classify chat fork preflight refusals and preserve mobile create recordings * Keep route-owned create cleanup quiet after unmount * Preserve typed runtime repair refusal in chat fork preflight * Preserve checked store through mobile chat fork preflight * Register mobile chat fork runtime fixture before test setup * Remove unused mobile create recording value * style(mobile): format the chat fork create files; move fork reply messages into the fork feedback module Formatting pushed the create hook past its line limit, so the fork-only reply message, partial-success warning and host capability check now live beside the other fork messages. Same order and copy as before. * Keep quick command mutation aligned with formatted request * Keep mobile chat CLI forks in terminal view * Pass mobile chat resolution its existing controller arguments * Match the mobile create fixture mock layout * Keep mobile chat CLI forks open when placement is unavailable * Brace placement contract fixture checks * test(mobile): keep the prompt controller mock under its line limit after the main merge * Expose pane admission in the runtime test fixture type * Complete the chat fork contract controller fixture
…ver a stale lock without displacing a live one (stablyai#29902) * fix(orcad): never claim or reclaim another machine's orcad, and serialize stale-lock recovery * fix(orcad): judge data-root locality without a synchronous spawn * fix(orcad): break a recovery lock only once its owner provably exited * fix(orcad): publish the recovery lock with its owner in one rename * fix(orcad): recover a stale lock in generations so no live reclaimer is displaced * fix(orcad): keep a released recovery generation until the stale lock is replaced * fix(orcad): fail closed on an unreadable recovery owner, and never let cleanup fail a won lock * fix(orcad): key recovery locks by the stale record alone
stablyai#29268) * fix(native-chat): a dot between the queue header's label and its count * style(mobile): wrap the queue header dot's props * refactor(mobile): move the queued card's text into its own file The queue file sat at the 400-line cap, so the header dot had no room.
…whose program Orca provides (stablyai#29024) * feat(acp): hosts install a managed runtime over RPC, and mark agents whose program Orca provides managedRuntime.status / managedRuntime.install / managedRuntime.cancel, negotiated by managed-runtime.install.v1, act on the host that receives them, the one that runs the agent's chats, with no workspace needed. A status read naming the revision it shows is a long-poll on its own call-queue lane, so every client sees another's start, progress and outcome. agentSession.agents marks an agent whose program Orca provides on that host. * fix(runtime): install status says a copy outside Orca's runs only when a start would really run it * fix(runtime): install status is pushed to subscribers instead of long-polled, and Cancel answers with the outcome managedRuntime.subscribe {agent} sends a full snapshot on subscribe, then one snapshot per install state change, and closes with the transport. It replaces the managedRuntime.status long-poll and its slot registrations.
…stablyai#29148) * fix(mobile): a streaming reply reveals word by word, like the desktop The phone drew each streamed chunk as it landed, so a reply arrived in a few jumps. Move the desktop's paced reveal into src/shared and use it on both surfaces: the phone paces the growing block of the newest reply, remembers progress per chat list so a recycled row resumes, and draws everything at once with reduced motion. The terminal-backed chat's live row now counts as growing. * fix(mobile): hold the reply reveals in the chat overlay The list's turn resolver must not scan terminal-backed transcripts, and the overlay outlives the list across chat and terminal toggles, so it owns the reveal memory and the reduced-motion read. Also fix the moved hook's import in the onboarding route, and mock the OS read where tests render the overlay. * test(mobile): mock the message row's other children in the reveal test Their real modules reach React Native's Flow sources, which Bun cannot load; the other message row tests mock them the same way. * style: format the desktop paced-text hook * Keep phone reply reveal progress owned by its message * Add braces around phone reveal test assertion * Start phone prose streaming only at a text block * Find the mocked phone tool row with a node predicate * refactor(mobile): move chat prose rendering into its own file Keeps MobileNativeChatMessage.tsx under the 400-line limit after the main merge.
…SH server (stablyai#29908) A folder workspace has no repo row, only a synthetic repoId, so the panel's repo lookup came back empty and it said "No workspace selected", hiding the SSH forwards and the server's listeners.
…scoped by identity (stablyai#29909) When two hosts share a worktree id, the caller's cwd resolves to an identity: selector, which carries no repo id, so worktree create reported "Missing repo selector". Keep the selected worktree id alongside the selector and infer the repo from it.
…ared by every surface (stablyai#29048) * feat(acp): the renderer follows a host's managed runtime install state, shared by every surface One entry per host runtime instance and agent holds the host's answer. While any shown surface observes it, one managedRuntime.status long-poll follows the host, idle included, so another client's install shows up. A failed first read is its own state, never "not installed"; a reply older than the one shown is dropped. Observed only for agents the host marks as Orca-provided, on hosts that advertise managed-runtime.install.v1. * fix(settings): keep one install status wait per host, retry it when the host is back, and wait for the host's agent list - Hiding and showing a chat no longer leaves a status wait behind on the host for each cycle: the wait is never aborted, and a surface shown again reuses the one in flight. - The host answering again after an outage retries a failed status read at once. - While the host's capabilities or agent list are still loading, the install state is pending rather than "not provided", so a chat doesn't flash its composer; a failed list read reads as a failed status read, so Settings says so instead of hiding Install. * fix(chat): only a reachable host's outstanding agent list holds the Install slot; an offline host or a list without the managed mark keeps the composer * fix(acp): the renderer follows install status over one host subscription instead of a long-poll Each host and agent shown on screen holds one managedRuntime.subscribe stream, ended when the last surface hides it. A lost stream resubscribes with backoff, or at once when the host is back; every subscription is a new generation, so a restarted host's snapshot replaces the old state even though its revision count restarted, and a late frame or install reply from a superseded subscription is dropped. Cancel's reply is no longer applied: the stream shows its outcome.
…en, and an exited or abandoned pane never comes back (stablyai#29910) A reattach applies the relay's screen from main's output stream instead of the invoke reply, so its clear never erases output printed after the snapshot. An endpoint refresh re-reads leases after asking its relays, so a pane that exited meanwhile is not republished live. An attach answered after its pane left hands its viewer back to main. Co-authored-by: Claude <noreply@anthropic.com>
…ther desktop's restart can clear it (stablyai#29915) * fix(ssh): re-check a macOS host's ended launch-session warning so another desktop's restart can clear it The ended verdict was cached for the whole SSH session, so when another desktop restarted the server under a live session, this desktop kept the warning. It now expires like every other verdict: a live re-probe clears the note, and an unanswered one keeps the proven verdict. Co-Authored-By: Claude <noreply@anthropic.com> * fix(ssh): only a live probe clears a proven launch-session end, and a failed recheck republishes it Co-Authored-By: Claude <noreply@anthropic.com> * fix(ssh): a failed launch-session recheck restores the warning only for the same live connection Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
… agent holds the send (stablyai#30134) * 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
…ore its teardown runs (stablyai#30245) 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.
…ogle sign-in (stablyai#28650) * 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
…frame before sending it (stablyai#30249) 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.
…ree ids (stablyai#30238) Worktree ids built as `${repo.id}::${path}` by hand are how one worktree got two spellings (stablyai#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.
…lyai#30258) 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.
…st one just failed on (stablyai#30253) 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.
…yai#30263) 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.
…cks any more (dark) (stablyai#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
… already covers (stablyai#30256) Since stablyai#30232 it accepts null and covers both remote: and app SSH ids, so the extra isRemoteRuntimePtyId / parseAppSshPtyId / null guards never change the result.
…t and clone tests (stablyai#30268)
… on the host can still run it (stablyai#29139) * 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.
…roject views (stablyai#29977) * fix(session-history): find every agent's older sessions in Workspace and Project views Session History kept only the newest sessions across every agent, then filtered by workspace; only Claude, Pi and CodeBuddy got their older in-scope sessions back. Every agent now declares how its cwd is found without a full parse, and the scan selects in-scope sessions before the recency cap: Codex and other header agents by their opening record, SQLite stores by their directory column, the rest by their cached parse. The scan and the cached-list cut share one scope rule. * fix(session-history): keep discovery under the file length limit * fix(session-history): type the scope walk's file list * Keep session history scope complete before applying the display cap * test: align session history scope fixtures * perf(session-history): settle known out-of-scope transcripts without a per-file read A scoped scan walks every transcript, and each one cost an await, a parse-cache database lookup and, for Codex, two head reads every scan. Remember each file version's settled cwd (from its opening records or its parse), drop settled out-of-scope files synchronously before the async walk, and read Codex's roughly 20 KB first record in one 64 KB read. * fix(session-history): let the parse cache decide scope before transcript heads The settled-cwd prefilter let a transcript-only verdict reject files whose current parse (hot or cold) says otherwise: a Cursor chat whose meta.json appeared later, or a Codex session whose parsed latest cwd moved after its row left memory. Restore parse-cache-first selection; keep Codex's single 64 KB first read. --------- Co-authored-by: Orca Worker <orca-worker@localhost>
…k relay gate for what it gates (stablyai#30264) 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.
…re-export shims (stablyai#30265) Deletes orcad-runtime-lifecycle.ts and the re-export blocks in orcad-managed-tunnel, orcad-local-build-hash, orcad-remote-launch, orcad-remote-node-runtime, orcad-update-plan, relay-protocol and shared/orcad-migration-manifest; importers and test mocks name the real modules.
…0267) 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.
…tablyai#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.
…n index barrel (stablyai#30269) Deletes remote-layout-snapshot/index.ts and workspace-layout/old-client/index.ts. The compat-boundary check now lists each folder's entry modules, so deleting the folder plus its listed importers' import lines still removes the path.
…nt's turn (stablyai#10923) (stablyai#30273) 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.
…e window saves to (dark, layout-owner build only) (stablyai#30196) * 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
…ost/ (stablyai#30275) git mv plus import and path-literal rewrites only.
… in place (stablyai#30271) * fix(runtime): show Re-pair required for revoked servers and re-pair in place A server that revokes this computer's pairing token was labeled "Update required" because auth refusals and protocol mismatches shared one blocked state. Carry the blocked reason locally, label revoked hosts "Re-pair required", and add a Re-pair action (sidebar host menu + Settings server row) that verifies a new pairing link reaches the same server before replacing the saved pairing. * fix(sidebar): stop host header menu clicks from toggling or dragging the row * fix(sidebar): vertically center host header label and menu button
… from their owning modules (stablyai#30279) Removes the re-export lines in relay-region-preference, relay-session-broker, browser-host-page-placement and browser-host-page-reconciliation-plan (no barrels).
…i#30205) 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.
…rk, layout-owner build only) (stablyai#30281) * 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 beb2275) * test(layout): the SSH binding test uses the shared spawn commit dependencies (cherry picked from commit 672b641) * 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 ee27a0b) * chore(layout): one binding write for the partition choice; shipped builds keep their exact replaced-binding read * 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.
… through re-export shims (stablyai#30255) Deletes ssh-system-fallback.ts and the re-export lines in ssh-config-parser.ts, ssh-connection.ts, system-ssh-file-binary-transfer.ts and ssh-remote-powershell.ts; importers name the real modules. SshConnection tests mock each real module, keeping the parser stub that holds ~/.ssh/config out.
…oticed (stablyai#30286) tests/ is typechecked by nothing in CI, so the Windows CLI matrix spec imported a helper that never existed (stablyai#30231). Per-file error counts under tests/ (tsc -p config/tsconfig.e2e.json) may now only go down.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstream: stablyai#30318
ELI5
The input dock reserves the bottom safe-area inset but its container is transparent. The home-indicator strip therefore shows the terminal background instead of the adjacent input panel color.
What Changed
Give the dock container the existing panel background token so its safe-area padding blends with the accessory/input surface.
Why
Paint the surface that already owns the safe-area padding without changing keyboard lift, padding or terminal viewport dimensions.
Linked Issue
Fixes stablyai#30308
Visual Proof
The actual before/after dock styles rendered with the same inset and panel content.
Isolated browser surface rendering; keyboard lift and native safe-area timing are not measured.
Testing
Application typecheck and changed-file lint passed. This is a one-property surface fix; component rendering is checked separately.
Changed-file formatting and lint passed. The full mobile suite and a release build were not completed locally; the focused checks above are the local evidence. Physical-device behavior remains unverified. The existing host protocol is retained; remote-host interaction has not been exercised on a physical device.
AI Disclosure
Prepared with OpenAI Codex.
Review
One independently reviewable user behavior.
Agent skill upstream boundary
Checklist