Repository navigation
Sync upstream 1.4.214 - #30
Merged
Merged
Conversation
…es ago (stablyai#26355) A host that wakes briefly and sleeps again never acts on the move, so it expires after 5 minutes and holds one of the concurrency slots. The source cell now answers busy until the control socket is 180 s old.
… 5xx (stablyai#26353) A single orca-cloud-auth 500 on the desktop sign-in callback stopped the c13 and c28 same-cap waves before any drain. Allow up to two auth 5xx per five-minute window while the error ratio over all auth requests stays at or below 0.1%; a burst, or errors against near-idle auth, still freezes.
…ts a DB stall (stablyai#26371) A single 5-7 s stall of the shared database pushed the fleet sql failure sum to 396 (bar 250) on 2026-10-06 and a source cell latched regional rehome off fleet-wide. Source cells now only pause the claim on a sql-only breach; the director's regular poll latches once the breach has been watched for 150 s. Other safety reasons still latch at once on the cell.
…tall (stablyai#26372) * fix(relay): let the same-cap roll's post-roll verify outlast one DB stall The verify step's admin reads gave up after three 2 s retries; on 2026-10-07 c18 failed its roll on 503 'no healthy upstream' during a DB stall and was healthy 16 s later. The step now retries 5xx for up to 60 s, which covers a stall plus readiness grace and two LB health checks, and still fails a cell that stays down. 4xx stays final. * test(relay): surface curl stderr when the verify retry test fails * fix(relay): retry the post-roll verify reads in bash, since curl 7.81 ignores --retry under --fail-with-body * fix(relay): never report a stale body after a refused verify read; stop pinning inert curl retry flags
…25996) * feat(native-chat): fold long sent messages behind a toggle * fix(native-chat): a work run never folds as a user message --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…tablyai#26363) * feat(relay): alert on cell disconnect bursts and database pool herds Two alert policies that separate a load-balancer mass disconnect from a database stall, after the 2026-10-06 18:34Z asia-east2 drop: - Disconnect burst: over 120 code-1006 desktop control closes on one cell in a minute, counted from the cell's own close line. The LB's internal_error log entries carry the stream start as their timestamp (median 2.4 h before the drop), so an LB-log rate never shows the pulse. - Pool herd: a cell runtime sample with more than 50 requests queued for a PostgreSQL connection (interval maximum, bar in the filter). Also publishes orca_relay_host_hellos_shed for the refusal counter from stablyai#26362, and pins both filters to the relay's text in a contract test. * fix(relay): raise the cell pool-herd alert bar to 200 waiters asia-east2 cells reach 184-196 waiters outside any herd, so >50 would have fired in 84 episodes since 2026-10-03. >200 fires in 10, covering every known herd. Drops the false 'healthy peak is 2 waiters' baseline from the policy text. * docs(relay): describe pool-herd baseline as stalls, not routine load A healthy cell peaks at 2 waiters (p99); the 50-196 readings are single-cell stalls that already time out. The 200 bar is unchanged.
…them out; renewals jump the queue (stablyai#26362) * fix(relay): refuse host hellos fast when the database pool is saturated; renewals jump the queue A reconnect herd (2026-10-06 18:34Z, 1,046 asia-east2 hosts dropped by the load balancer at once) queued hundreds of host hellos behind each cell's 16-connection pool. They timed out after 2 s, the desktops redialled into the same queue, and control-lease renewals starved behind them. - Host control upgrades get an immediate 503 (Retry-After: 2) once 32 callers wait for a pooled connection. Shipped desktops already treat that as a connect error and back off with jittered exponential retry. - General work now queues in PostgresPoolPressure instead of pg-pool, so renewal statements (priority lane) take the next released connection. - A renewal batch that never got a connection no longer fans out into one statement per host. - hostHellosShedDelta counts refusals in the runtime metrics. * fix(relay): shed host hellos on the pool's oldest wait, not its queue length asia-east2 queues of 50-196 waiters are routine and keep moving, so a 32-waiter limit would refuse hellos that were going to succeed. Shed only while the oldest pool waiter has waited 1.5 s of the 2 s acquire timeout, when a new hello would time out anyway; a refused desktop gets the same connect error and backoff it gets today, 2 s sooner. Rebinds over a live control (lease rotation) are never refused. * fix(relay): shed hellos only when a full pool queue has aged 1 s Per review: require both an oldest waiter of at least 1 s and a pool's worth of waiters, so one slow waiter cannot trip it, and state the real baseline (p99 of 2 waiters on 10-07).
…S file drops (STA-6940 PR5) (stablyai#26133) * feat(file-drop): add element owner preparation plumbing * Fix terminal and chat file drop destination ownership * fix runtime terminal drop ownership and queued chat retries * fix(file-drop): preserve feedback acceptance and destination ordering * fix(file-drop): attach chat and composer files at the drop surface * fix(file-drop): preserve project destinations and live chat availability * fix(file-drop): keep path resolution in filesystem namespace * test(file-drop): use filesystem path bridge in PR3 fixtures * fix(file-drop): bind terminal drops to pane elements * fix(file-drop): compare terminal pane identity across public views * test(file-drop): align composer lifecycle with element-owned drops * Fix interrupted chat composition and refuse unused quick-create drops * Type the quick-create drop regression transfer * fix(file-drop): let the explorer, project sidebar, tab strip and editor own OS drops STA-6940 PR5. The file explorer tree, the project sidebar, each editor group's tab strip and its editor area now take OS file drops on their own element through the shared owner hook, with identity from their own render (the explorer's shown workspace and target row, the tab strip's and editor group's worktree and group) instead of the active worktree. The explorer and sidebar broadcast subscribers and their legacy drop markers are deleted; editor-open moves out of useGlobalFileDrop into editor-dropped-file-open, which the legacy unmarked-chrome route still uses until PR6. * fix(file-drop): keep editor delivery tied to its destination group * fix(file-drop): guard delayed opens and share floating ownership
…ai#26288) * feat(native-chat): number keys and auto-advance for questions A single-select pick moves on after a short beat, on desktop and phone. Structured chats hold the card while a response is with the host. * test(mobile): compare mocked host components by name so the ask test typechecks --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…ablyai#26374) * feat(runtime): isolate the server behind a compatibility launcher * fix(runtime): preserve server crash exit semantics
…ped" (stablyai#26381) * fix(native-chat): label a message stopped before the agent began on it plainly as "Stopped" * fix(native-chat): word the row after a stopped message "Stopped manually"
…Step 4 of 4) (stablyai#24434) Each saved Claude account now runs in its own CLAUDE_CONFIG_DIR folder, so Claude renews each login itself and switching no longer replays a copied credential. A claude shell function routes every launch to the selected account; the terminal daemon protocol moves to 42 so new terminals get it. After updating, each saved account signs in once: typed claude shows Claude's own sign-in, old terminals show an in-app banner, chats and the status-bar menu offer Sign in, and a one-time toast explains it. Orca prints nothing into terminals.
…poser (stablyai#26018) * feat(native-chat): quote a selection from an agent reply into the composer * fix(native-chat): label the selection action "Add to chat" --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…ame the right Settings page (stablyai#26405)
* Allow large markdown documents with HTML to open in rich mode Documents over 50,000 characters that contained HTML were always blocked from rich mode, because the round-trip check that proves the HTML survives is too slow to run synchronously on large files. Large documents now get a cheaper check that encodes HTML as passthrough nodes and validates the result without DOM parsing, so anchors, tables and other HTML in big notes stay editable. Large documents that need DOM parsing, such as editable toggles, still fall back to the block. - Move the raw HTML node definitions into raw-markdown-html-nodes.ts so the passthrough check can reuse them without an import cycle. - Add getRichMarkdownPassthroughOutput, which parses and serializes without creating an EditorView. - Compute markdown fence ranges once per document and share them across details matches, instead of rescanning for each block. - Keep line-start tracking correct after inline and details passthrough, so block HTML that follows them is still recognized. * improve code
* test: load GitHub operations from their public modules * fix: sync required English entries after branch toast removal
* fix(ssh): abort runtime download requests after failure * Keep region correction fixtures past initial control grace --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
…r's nested target (stablyai#26952) Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(native-chat): the host owns the send queue; the window keeps no saved outbox The desktop kept each structured chat's unsent messages in localStorage and sent them in order, so one message whose fate was unknown froze every later send, Retry dropped it silently, failed sends could not be discarded, and an offline chat could send hours later. The host already records every message and owns the queue; the window now only sends. - One in-memory sender for composer, launch prompts and messages sent from outside the chat. One send in flight per chat; a transport failure resends the same id for up to 30 s; a refusal that proves nothing was recorded puts the text back in the composer with the reason; a send that went out and was never answered shows an in-doubt line with Send again and holds nothing up. - The host's "unknown" rows get the same in-doubt line and Send again, from the journal, in every window. - A message an older build left in localStorage is never sent: the host's conversation outline decides what goes back to the composer, and the copy is deleted once that is saved. * fix(native-chat): send through the structured chat RPC wrapper, with its timeouts The sender called the runtime RPC directly, skipping the per-method timeouts every other structured chat call gets. Only a remote host's request skips the compatibility check the sender already ran. * fix(native-chat): an unconfirmed send goes back to the composer, with no new row line The common pattern draws nothing extra on a message whose delivery is in doubt once its turn is over, and puts a failed send's text back in the composer with the reason. So a send nobody answered in time, or one a host answers in a way that proves nothing, goes back to the composer worded as unconfirmed, and a host "unknown" row shows nothing extra. Removes the in-doubt phase, Send again, and its strings. A host's made-up row for an id its journal lost now reads as unconfirmed, not as recorded, so that message comes back instead of vanishing. * test(native-chat): pin that nothing resends a send given back as unconfirmed * fix(native-chat): sends survive a tab close, never resend after a Stop, and keep remote images - A normal tab close lets sends on their way settle; what the host never took goes back to the conversation's draft. Only a cancelled launch or a worktree purge drops them. - After any Stop, a send already on its way is never sent again under its id: a doubtful answer, or a resend that was due, hands its text back worded as unconfirmed. - A returned image keeps the SSH connection it lives on; the connection never goes to the host. - An older build's saved message the host recorded and then rejected is left to the host's own row, never handed back. * fix(native-chat): a Stop or a tab close never resends a send already out, and a kept card is the card's A Stop that landed while a same-id resend was being readied (its timer fired, its request not out yet) let that resend go out after the Stop. The sender now tracks whether a request is awaiting its answer: a Stop hands back every send between attempts as unconfirmed, lets one whose request is out settle from its answer, and never issues a request after it. A normal tab close withdraws the same way instead of doing nothing, so nothing more goes out and nothing is dropped. A send the host rejected but kept as a card (keptAsQueuedMessageId) is the card's, from its reply or the journal, and never goes back to the composer. Adds the freeze tests: a send whose fate is unknown holds later sends no longer than its deadline, and one the host can neither confirm nor deny releases the next at once. * fix(native-chat): read a resend's turned-away call or reused id as unproven Ports the send-answer proof contract. A call the host turned away before running it (method_not_found, invalid_argument, unauthorized) proves only that this request wrote nothing, so it reads as never sent on a first attempt only; on a resend an earlier attempt may have landed, and it goes again under the same id. A resent id the host says was already used for other content (messageIdReused) proves nothing either, like an expired or conflicting id. Pins the rest of the contract: any row the host returns is its own, a thrown error is no answer whatever its code, and an older build's refused, held or outlived-Stop copy is handed back once and never sent. * refactor(native-chat): type the structured composer's send with its attachment type Keeps NativeChatStructuredSession.tsx within the file length limit. * refactor(native-chat): drop the kept-card guards the sender never needed A kept send's row is a rejection that was never a Stop's, so the sender already reads it as recorded, from its reply or the journal. The test that pins it stays; the two extra checks only covered a kept row that is also a withdrawal, which the host never writes. * test(native-chat): route launch tests' sends through the client wrapper the sender calls The sender sends through callStructuredAgentSession, but these launch tests replaced that module with a factory that answered nothing (and still named a probe that no longer exists), so every launch prompt resent until its 30 s deadline and the tests timed out. Each factory now hands sends to the runtime RPC mock the tests already answer, and expectations of a local send no longer ask for the remote-only compatibility option. * fix(native-chat): hand a message back without importing the composer's attachment hook The worktree purge reaches the launch prompt, which hands text back, and the attachment hook's imports reach the store. A test that builds the real store behind a mocked one then waited on itself and hung. Handing back now writes images to the draft store directly, as the hook's helper did, and a test pins that each returned image keeps its SSH connection. * fix(native-chat): one send per chat, with no line of sends behind it A chat with a send out took further messages into an in-memory line and sent them one by one. A message typed behind one in doubt then hit its own 30 s deadline and came back as not sent without ever going out. Now, as the common pattern does, a chat takes one send at a time: while it is out, Send is disabled and Enter leaves the text in the box. The sender refuses a second send instead of lining it up, so the 30 s deadline always runs from the send itself. A Stop or a tab close stops the one send: settled from its answer if its request is out, handed back as unconfirmed between attempts, or silently if it never went out. Notes sent from outside the chat while its send is out stay with their sender (not ready); a launch prompt that meets the person's own first message waits in the composer instead of being lost. * fix(native-chat): give a Stop-withdrawn note back to its chat once its notes were cleared Notes sent from outside a chat clear once their message is recorded. A message the host recorded as pending and a Stop then withdrew came back only to its sender, which had already let go of it, so the text was lost. The chat's draft now takes it, as an earlier build's outbox did. * test(native-chat): type the composer-actions probe without a cast * chore(native-chat): drop outbox wording left in comments and an empty locale group * fix(native-chat): pace failed checks, keep the host's reason, and hold the chat for its launch prompt - A send whose checks failed before its request went out (an unreachable or incompatible host, an unreadable history) was tried again at once, about a thousand times a second for 30 s. Attempts are now paced by the attempts made, whether or not their request went out. - A host that refuses every resend by throwing (native chat turned off, a journal that won't open) gave back only "couldn't confirm". The host's reason now comes first, still without claiming not sent. - A launch's prompt now holds the chat's one send from the click, drawn as sending, so a message typed while the chat starts can't overtake it; it goes out once the chat exists, and a cancelled launch frees it. - Notes whose send nobody could confirm say so instead of "did not accept", and notes launched into a new chat let go of their text once that chat's composer holds it. - An open chat keeps drawing a recorded send until its row arrives, so a reply that beats the history no longer makes the message flicker out. * fix(native-chat): hold a chat's sends until it has started, and send a failed chat's message with its restart A message sent to a chat still starting went out at once to a host that had no record of the chat yet, read for a fence it could not get, and came back as not sent. A message sent to a chat whose start failed restarted it but no longer went with the restart. While a chat starts, Send stays off and Enter leaves the text in the box, as with a send already out. A text message sent to a chat whose start failed restarts it and goes as the restart's first message, through the same staged-prompt path a launch prompt takes: it holds the chat's one send slot, drawn as sending, until the chat exists, and comes back to the composer if the restart fails again. Notes wait while a chat starts and ride a failed chat's restart, keeping their text until it is sent. * chore(native-chat): test the queue request, rejection words and card hand-offs; drop an unused clear - Pin which sends ask the host to queue, the moved rejection wording, and that a queue send whose card was handed off and then refused or withdrawn, or whose replay names a withdrawn card, is never handed back. - The send-at-most-once gate now says what the desktop promises after a reload: it never sends the id again, and hands the text back. - The legacy read names when it goes, and drops the notice clear nothing called. * fix(native-chat): keep a sent launch prompt's entry, and give back at once what can't go out - Cleaning up a launch prompt released its send slot even after the prompt had gone out, which deleted the entry the sender keeps once the host records it. A launch prompt recorded as pending and then withdrawn by a Stop was lost from both the chat and the box, and an open chat dropped the new chat's first message before its row arrived. The slot now gives back only a reservation that was never sent. - A send stopped before its request went out by something trying again won't clear (this client and the server can't talk, or the host refused the history read) kept Send off for 30 s and then said Orca couldn't reach the agent. It now comes back at once with its own cause: not sent, since nothing went out, or unconfirmed if an earlier attempt did. Transport errors keep the paced retries. * fix(native-chat): notes keep their own text through a new agent's launch Notes sent to a New agent whose start failed came back to the notes and also sat in the new chat's composer, so sending both delivered the text twice. The notes keep their text until it goes out (they hold it from the click), so the launch now says so and no composer gets a copy, on a failed start or a refused prompt alike. The tests that asserted the copy in the composer pinned the old double ownership and now assert the notes are its only owner. Notes that rode a failed chat's restart and ended unconfirmed now say Orca couldn't confirm them, as notes sent directly do, instead of that the agent did not accept them. * chore(native-chat): the send-once gate says what the desktop keeps across a reload The desktop keeps a send's id in memory only: a send still unsettled at a reload or crash is not resent and not handed back. The coverage notes no longer credit the renderer tests with durable identity across a remount. * fix(native-chat): say once why notes sent to a new agent did not go Since notes keep their own text through a new agent's launch, a prompt the host refused, nobody could confirm, or that found the chat's send taken came back to the notes with nothing said anywhere: the chat shows no notice for text its caller keeps. The notes menu now reports it once, with the toast a send to an existing chat already uses: not accepted, couldn't confirm, or not ready. A start that failed still says so in the new chat instead. * fix(native-chat): retry a history refusal the host says clears, and name it at the deadline A send stopped before its request went out came back at once for any refusal of the history read, including ones the host names as clearing (its journal briefly unavailable, a chat detached while the host quits), so the automatic retry was lost. Only a version mismatch and refusals that won't clear come back at once now; the rest go again on the paced schedule, and if the deadline still finds nothing sent, the words are the host's refusal rather than Orca couldn't reach the agent. * feat(native-chat): a send makes one request, and nothing ever sends it again The desktop resent a message under its own id for up to 30 s when the answer was lost. A send now makes exactly one request, as the common pattern's clients do: - An answer that is lost, dropped or proves nothing hands the text back at once with "couldn't confirm… check the chat". - A failure before the request goes out (the environment check, the history read for the fence, a refused connection) means nothing went out: the text comes back at once with its own reason, or as not sent. - The 30 s cap stays on the one request, so a host that never answers can't hold Send. Nothing ever resends, so nothing can go out after a Stop: a Stop only takes back a send still in its pre-send checks. The resend state goes with it (tries, generation, awaiting, stopped, the resend timer, the send-answers-proof probe, and the first-attempt/resend split in the evidence), and the send-once gate says the desktop never resends. * fix(native-chat): notes keep the chat's line, and a send held behind a rewind says it was not sent - Only a send whose text belongs to the chat's composer clears the chat's line. Notes sent from outside the chat no longer wipe a "couldn't confirm... Check the chat" line that explains text already back in the box. - A send the host turns away behind a rewind it could not confirm went back as "not sent" but said "couldn't confirm what happened. Check the chat". It now gives the rewind's reason and says the message was not sent. - Reliability gate names the single-request test and records a fresh evidence run; the sender test drops its leftover resend mocks and a duplicate Stop test. * fix(native-chat): a send ends even when putting its text back fails If writing the returned text into the chat's draft threw, the send never settled: it stayed "sending", the chat refused every later send until a reload. The send now always ends after a hand-back, the failure is logged, and the chat's line adds "Couldn't save your message." * fix(native-chat): a message typed during /clear stays in the box and follows the chat A /clear moves the chat to a new conversation, and its host refuses any send while it runs. A message sent then went out, came back refused into the old conversation's draft with its line, and vanished when the chat moved on: the box and the line now belong to the new conversation. - A /clear holds the chat's one send slot while it runs: Enter does nothing, Send shows busy and the text stays in the box, as for a send that is out. Notes sent from outside get "busy". - Once it moves the chat, the old conversation keeps taking no send until the view leaves it, and what is left of its draft moves into the new conversation's draft, after anything there: when the composer's /clear settles, and again when the view moves. * fix(native-chat): no "Send message?" or queue clear while the chat's send is out While a send is out (or a /clear runs) the chat takes no message, yet Enter over a held queue still opened "Send message?", and Clear queue deleted every card before its message was refused. Enter now does nothing there and the text stays; Clear queue re-checks and deletes nothing if a send went out after the question opened. * refactor(native-chat): take the /clear draft carry out of this change Moving the old conversation's draft into the one a /clear replaces it with fixes a bug main has too (text left in the box during a /clear stays with the old conversation), so it goes in its own change. Kept here: a /clear holds the chat's send slot while it runs, and the conversation it moved away from takes no send until the view leaves it. * fix(native-chat): a /clear's hold on sends always ends - A view that unmounted while a /clear that moves the chat was out left the old conversation holding its sends until a reload: the late reply kept the hold for a view that was gone. The reply now releases it. - The local call for a conversation command has no deadline of its own, so a /clear that never answers kept Send off for good. The hold now also ends at the command's deadline (195 s, the one the remote call already uses), whichever comes first. * test(native-chat): opening a chat an older build left stuck; hand back its copy in send order Pins, through the real chat hook, sends, legacy recovery and draft store, what opening such a chat does: the queued messages behind a message the host recorded in doubt come back to the composer once, in order, with the "not sent" or "couldn't confirm" wording; the chat is free to send under its read's fence; nothing happens while the chat has no fence here. The legacy reader now hands entries back in the order they were sent (queuedAt), as the older build's reader did, instead of array order. * fix(native-chat): a send this window turned away for a re-paired server comes back as not sent A managed server's update rotates its pairing, and this window's main process then answers the next call itself, before forwarding anything, with runtime_environment_changed. The send read that thrown answer as proving nothing, so the person was told Orca couldn't confirm a message that never left. It is now handed back as not sent, with the reason. Every other thrown answer still reads as unconfirmed once the request may have gone out. * Save a new chat's first message during create * Align opening-message types with the user-message wire * Keep chat publication and cleanup outside store initialization * Complete the create refusal fixture ledger * test(native-chat): prove a created first message waits for the agent's start and is handed over once * refactor(native-chat): keep the host and session hook under the line limit after the main sync * test(native-chat): a chat created with its first message and no pick still teaches its agent's default After the main sync, a created chat's agent starts from the delivery loop rather than the create call. Grok (through the real ACP adapter and model catalog) and Claude (through the real adapter) each still report the model their own config resolved to once the chat reads its options.
…i#26821) * refactor(native-chat): one rule for what a no-pick session says of its configured default ACP's rule (only a new session that restored no model pick and was sent none runs its config's default; an effort it picked says nothing) moves beside the catalog entry types so other agents' adapters report through the same step. An empty listing (a signed-out agent) now says nothing instead of "no listed model", so it can never retire a saved default. * feat(native-chat): check Pi's project config before naming its default model Pi reads `.pi/settings.json` in the chat's folder and loads project extensions from `.pi/extensions` (and the `extensions`/`packages` lists). A settings file that sets defaultProvider, defaultModel, defaultThinkingLevel, modelThinkingLevels or enabledModels, loads extension code, or can't be parsed counts as the project picking the model; extension code may register providers or set the model itself. Key names come from Pi 1.0.4's own settings manager and docs, read without running Pi. * feat(native-chat): a new Pi chat names the model and thinking level Pi starts on Pi's model listing marks no model as the one it runs, so a new Pi chat with no saved pick showed "Model" and then changed to the model its session reported. A new Pi session (not one resumed or forked from a saved conversation) that restored no model pick and was sent none now reports the model and, unless it picked one, the thinking level it started on, through the same host step as Grok, OpenCode and OMP. The host saves them as the account's default behind the project-config check, so the next new Pi chat names them on its first frame. A signed-out Pi, which lists no model, saves nothing. * fix(native-chat): a newer, coarser effort menu keeps the effort a chat ran The saved list takes each model's effort menu from the newer listing. Pi's `--list-models` menu stops at high, while a chat's own menu adds xhigh or max for models whose thinking map allows them. After a background re-list, a learned xhigh was no longer in the menu, so the first frame dropped it. When the configured effort is only in the other listing's menu, that menu is kept. * fix(native-chat): Pi teaches only the model and thinking level it started on A Pi chat re-derived its configured default from Pi's current state on every options read, so a model an extension switched to mid-chat (extensions can call setModel) was saved as the account default, and the next new chat named the wrong model. The choice is now computed once, after start-up restores the chat's saved picks, and every later read reports that snapshot. A pick made in the chat no longer matters to it. Tests cover an extension switch, a pick made in the chat, a forked chat, and a learned xhigh surviving a newer re-list.
…native chat (stablyai#26776) * fix(orchestration): start native-chat workers for every registered native-chat agent * chore(orchestration): correct the restored worker agent fallback comment
…yai#26955) * refactor(providers): local git provider behind the host router Adds createLocalGitProvider, which binds this machine's git free functions to the IGitProvider contract with the admission tiers and WSL options the local call sites use today. The router's local git route now carries it as a per-call factory. No caller changes yet. * refactor(providers): read shared link paths lazily in the local git provider --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…#26343) * feat(native-chat): highlight code blocks and shell commands Uses Shiki rather than the highlight.js already in the preview: it can resume mid-block while a reply streams. * fix(native-chat): leave the tool row command unhighlighted * refactor(native-chat): highlight code through a shared TextMate interface Chat code now goes through one renderer highlighting interface built on Shiki core and the Oniguruma WASM the editor already loads for Nim and Typst, with VS Code Light+/Dark+. Lines that are too long or too slow leave the rest of the block plain, failed grammar loads retry from a mounted block, every block shares one time slice, finished-block caching counts retained text, each theme keeps its own formatting, Vue template expressions are colored, and tool-row commands take the grammar of the shell they ran in. * chore(deps): drop pnpm self-install entries from the lockfile Keeps the lockfile change to Shiki alone; the earlier commit picked up the @pnpm/exe entries pnpm 12 writes when it runs in a fresh checkout. * test(native-chat): give cold grammar loads time in highlighting tests The first colored block in a test file loads its grammar and the regex engine, which took over a second on a busy CI runner. --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
) * Open a chat visual in its own tab instead of the sidebar * Keep chat visual tabs alive across tab switches and finish editor-tab consolidation - Render chat visual tabs from a worktree-level retained overlay so switching tabs or moving the tab to another split never reloads the page. - Reopening a visual focuses it in whichever split holds it, takes its newest title, and opening from the floating panel no longer takes the main window's editor selection (check-details opener uses the same helper). - Visual tabs return to the reopen-closed stack and show in the tab palette. - One editor-family list and one virtual-tab predicate: no Copy Path, Copy Relative Path, Reveal or git status decoration for check-details and chat visual tabs. * Give chat visuals one open path and re-read them when shown - Reopen-closed (Cmd/Ctrl+Shift+T) of a chat visual goes through the visual opener, so it focuses the visual in whichever split holds it and keeps its id when a runtime environment is active. - A visual tab re-asks its host each time it is shown or reopened; an unchanged revision keeps the page as is, a changed one shows the new page, and an unavailable tab recovers. - The tab palette no longer shows or matches a virtual tab's internal id; the new-tab search shows the chart icon for visuals. - Cmd/Ctrl +/- uses app zoom and Alt+Z leaves word wrap alone while a virtual tab (CI details, chat visual) is active. - A tab dragged over a visible visual passes through the frame, using the existing drag passthrough. * Slim the chat visual tab back to the minimum Remove the retained overlay, the visibility re-read, the zoom and word-wrap rerouting, palette inclusion, reopen-closed routing and the check-details opener change. The visual tab renders through the editor area again, stays off the reopen-closed stack, and keeps: one tab per visual across splits, no main-window selection change from the floating panel, the newest title on reopen, and no Reveal or git status letter on virtual tabs. * Re-read an open chat visual when it is opened again; no watcher for virtual tabs - "Open in new tab" on a visual whose tab is already on screen in another split now re-asks the host, so a rewritten visual shows its new page. - CI details and chat visual tabs no longer start a workspace file watcher. - Drop the unreachable chat-visual case from the restore-time transient check, use the shared virtual-tab check in the header path, and say why visuals stay off the reopen-closed stack. * Let tab drags pass over chat visuals; hide path copies on virtual tabs - A chat visual's frame joins the existing tab-drag pointer pass-through, so a tab dragged over a visual (in its tab or inline in a chat) can drop or split there instead of the frame swallowing the drag. - The tab menu no longer offers Copy Path / Copy Relative Path for CI details and chat visual tabs, whose path is an internal id; no stray separator. * Reuse the hover icon for editor tab icons, so a chat visual shows its icon in both
…stablyai#26965) requireRuntimeGitProvider now returns a provider for the local host too (the local git provider, built per call from the worktree's WSL options and shared link paths), so the runtime git commands drop their "SSH provider, else local free function" branches. Push-target materialization stays host-specific and moves into one helper. Branch-diff keeps its two arms because the local git function returns one pinned diff, not the contract's list. Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
…sen (stablyai#26961) Blank Terminal is now opt-in on AgentCombobox. Only the new-workspace composer opts in; automations, source-control agent actions, and chat continuation offer agents only. The props are a discriminated union so agent-only callers' onValueChange never receives null. Fixes STA-9868
…26958) * fix(folders): folder workspaces can answer "paired server" * fix(folders): name a server folder's nested target only when its id is unambiguous * fix(folders): read the folder group only when the answer needs it --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
…pty cell (stablyai#26802) * fix(relay): spread placement over a load band so a drain does not land on one empty cell Placement took the single least-loaded cell, so a just-rolled cell took a whole drain and every follow-up serialized on its relay_cells row. Directors now pick at random among cells within 5% of capacity of the minimum, and skip a cell that already took over 40% of its pool's placements in the last 10 s (floor 4). It only reorders: the least-loaded cell is the fallback, the hard cap and region preference are unchanged. * fix(relay): size the load band's intake window for the 1,200 s drain pace At ~0.5 picks/s per director, a 10 s window with a floor of 4 let the floor decide and the empty cell took ~65% of a c27-sized drain. A 60 s window and a floor of 2 keep it at 40% at both the 300 s and 1,200 s paces across five directors. Adds a once-a-minute orca_relay_placement_load_band line (picks, budget skips, fallbacks, band size, picks per cell). * chore(relay): log the load-band summary at info, not warning
…lls the process (stablyai#26817) * fix(relay): a database fault reaching unhandledRejection no longer kills the process A rejection is fenced only when the relay's database layer threw or rethrew it and it is a pool acquire failure, pg's read timeout, a dropped connection, or a server error in classes 08/23/40/53/55/57/58. Everything else stays fatal and is named by an orca_relay_process_fatal line first. Every pooled client also keeps a permanent error listener. Fix level 2. * fix(relay): name class 23 in the fence comment; move the fix-level bump to the read-timeout fix R1 alone does not close the lost-reply stall, so the level rises with R2. The fix-level comment now describes the fixed alert floor. * fix(relay): survive and answer 503 for 25P03, the idle-in-transaction timeout a VM stall triggers A whole-VM database stall of 6-7 s outlasts the 5 s idle-in-transaction limit for a backend caught idle mid-transaction, and the server ends that session with FATAL 25P03 on resume. The fence now survives that one class-25 code (the rest of class 25 stays fatal), and routes answer it as transient. * style(relay): one transient SQLSTATE per line * fix(relay): fence a socket reset or broken pipe the database layer rethrew pg 8.22 rejects the active query with the raw socket error ('read ECONNRESET'), which the message-only check let through to a fatal exit: the 09-28 crash shape. Match ECONNRESET and EPIPE by code, gated by the database-layer mark.
…irectory (stablyai#26803) * feat(relay): read-only seat feed on cells for the directors' shadow directory Cells keep an in-memory log of host joins, leaves and drain notices and serve it at GET /v1/admin/cell-seats, authenticated with the directors' existing rehome identity. Nothing reads it yet. * test(relay): read the seat feed through its published shape so the tests typecheck * fix(relay): serve the seat feed's full snapshot from the change log's own seats counts.controls drops when a socket starts closing, but the leave lands only at its close event, so a full snapshot built from open sockets could disagree with a reader that applied every change. The log now keeps its own seats under the readers' generation rule and reports counts.seats. * fix(relay): report auth-expiry drain-only and its refresh as seat-feed changes The heartbeat's auth-expiry drain-only and an auth refresh back to active changed session state without a feed entry, so a reader's seat state drifted from the cell's. Both now go through the log; the refresh is a new 'active' kind. * refactor(relay): cut unread seat-feed fields; verify a feed poll once fixLevel, counts.splices, counts.enforcedUnits and counts.hardCap had no reader, so they leave before they become a cross-version contract. The admin middleware no longer verifies /v1/admin/cell-seats polls, which the route already gates on the rehome identity.
…es (targeted apply only) (stablyai#26835) * feat(relay): Cloud Storage bucket for the per-cell runtime switch files <project_id>-relay-control, versioned, uniform access, public access prevented. Cells and directors read; only the flag workflow's deploy identity writes. Apply with a targeted apply, never a root plan. * fix(relay): scope the switch-file writer to cells/ with a minimal custom role; soft-delete keeps history The deploy identity is shared, so its grant now reaches only cells/<cellId>.json and carries only get, create and delete. A 90-day soft-delete window keeps every deleted generation recoverable, since overwrite needs delete and delete also reaches old versions. * refactor(relay): drop the speculative directors.json read grant from the control bucket
…wap and read-back (stablyai#26837) * feat(relay): one-cell flag write workflow with dry run, compare-and-swap and read-back Operate Relay Production Cell Flags writes cells/<cellId>.json for one cell: it checks the origin answers as that cell with a flag channel, refuses a stale expected generation, writes with ifGenerationMatch, stamps the actor and run on the object, and waits up to 15 s for the cell's runtime status to report the new generation applied. The deploy Workload Identity condition admits the new workflow. * fix(relay): flag workflow retries a transient 5xx, shows the plan before the generation check, and names the failure Runtime-status reads go through the shared one-retry admin helper, and a failed read-back poll no longer ends the wait early. The plan line is logged before the generation check, a failure is logged as one JSON line the job summary carries, and the object and summary name the triggering actor.
…at mode switch in the upgrade tip (stablyai#26915) * Keep Chat UI users who opened new tabs in the terminal on the terminal Before the chat upgrade, Experimental Chat UI had a separate Default view choice, and it defaulted to Terminal. The upgrade retired that choice so Chat UI on now means new agent tabs open as chat, which moved people who had kept the terminal default into chat with no obvious way back. On load, a profile saved by a pre-upgrade build with Chat UI on and Default view not set to Chat now gets Chat UI turned off, and is left out of the one-time native chat upgrade tip. The old key is stripped on the first save, so the migration runs once and a later opt-in sticks. * Update the saved-settings test for Chat UI on with Default view Terminal * Show the upgrade tip to terminal-default users, with a switch to turn on chat mode People who had Chat UI on with new tabs opening in the terminal now keep the terminal on upgrade, but they still need the tip's directions for moving between chat and CLI. They now get the same upgrade tip, plus a note that new agent tabs still open in the terminal and a "Turn on chat mode" switch. The switch shows when Chat UI is off as the tip opens and stays put while it is flipped. The tip is taller in that case so nothing scrolls. * Decide the chat mode switch from the saved tip audience, not the live setting The upgrade tip's 'Turn on chat mode' switch showed whenever Chat UI was off when the tip opened, so a chat-view member who had turned Chat UI off also got it. Main now answers with one tip variant (none, standard, keep-terminal) from the saved audience, and only keep-terminal shows the note and switch. Also moves the feature tip panel styling into a DialogContent surface variant and drops the Label restyle, and tests that the switch stays put after flipping. * Let the upgrade tip decide the chat mode switch from its variant The modal now passes the saved tip variant to the dialog, which decides the switch itself, so the dialog tests cover that decision.
…heck (stablyai#26964) * feat(layout): read-only layout.subscribe stream and the round-trip self-check (inert) - layout.subscribe streams each workspace's layout as an explicit published projection (tabs, groups, panes and their terminals), never the model itself: no runtime records, closed-tab records, spawn inputs or legacy persistence values. It is re-projected from the Store's sessions after each write, only while someone subscribes, and only from the partition that owns each workspace. Not advertised as a capability and not on the mobile allowlist, so nothing calls it yet. - Replaces the unused terminal-topology publisher (stablyai#25682) it generalizes. - The round-trip self-check: a partition must load and save to a fixed point, changing only the kinds of value the Loader's rules change. It runs on Store writes in dev and e2e (logged, never thrown) and as the oracle's new roundTrip check. * fix(layout): know the spawn handoff main's minimal row mint stores * fix(layout): name-based invented ids, no partition on the wire, isolated listeners - Every id the Loader invents is derived from what it stands in for (a workspace's group from its key, a pane from its row or tab and position, a repeated tab id from its terminal), so removing an earlier workspace or tab no longer re-ids later ones. - The published layout no longer names its partition: the workspace key is the address and routing stays with the host; each tab still names the host that runs it. - One throwing stream listener no longer costs the others the event. - The self-check says plainly it is an idempotence check, and logs each new kind of first-load change with its count.
…ion (stablyai#26925) Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
…t timeout + 10 s (stablyai#26829) * fix(relay): a lost database reply ends its connection within statement timeout + 2 s The request pool sets query_timeout to the statement timeout plus 2 s. A read timeout poisons its transaction: later statements fail at once, COMMIT never runs (even when the operation caught the error), no ROLLBACK queues behind the dead statement, and the client is released with the error so pg-pool destroys it. Autocommit statements destroy their client too. The server bounds an orphaned idle backend with idle_session_timeout 30 s. A read timeout is answered 503 and never retried, and a renewal batch that loses its reply fails whole instead of fanning out. * fix(relay): read timeout margin 10 s, configurable, so routine 5-7 s database stalls never trip it A whole-VM stall delays replies the server already timed. With a 2 s margin the read timeout fired inside routine stalls, destroying healthy clients and failing requests that succeed today. The margin is now 10 s (ORCA_RELAY_POSTGRES_READ_TIMEOUT_MARGIN_MS), and a real-server test holds a reply 6.5 s and requires it delivered on the same connection. The fix level rises to 2 here, with the lost-reply fix. * docs(relay): name the startup retry that can see a read timeout, and why it is safe Boot open retries only an unreachable database. The director's startup cell reconcile does retry a lost reply, which is safe: it re-runs upserts and limit-change deletes that converge on the same rows. * fix(relay): answer 503, not 500, when Postgres ends the session mid-request Staging B1: pg_terminate_backend during /v1/assign returned 500 29 times. 57P01/57P02 and 08000/08003 join the transient SQLSTATEs; pg's 'Connection terminated' and ECONNRESET count only when the database layer threw them. transaction() still never retries them. * fix(relay): answer 503 when the pool acquire fails on a dead socket Staging B1 re-test: a Cloud SQL restart failed PostgresDatabase.transaction's acquire with 'write EPIPE', answered 500. An acquire failure with EPIPE, ECONNRESET, ECONNREFUSED, ETIMEDOUT or EHOSTUNREACH is now a pool connect failure (no statement ran), and acquire failures count as database-layer errors. EPIPE mid-statement is treated like ECONNRESET.
# Conflicts: # src/cli/bundled-skill-guides.ts # src/main/menu/register-app-menu.ts # src/main/providers/ssh-git-working-tree-provider.ts # src/main/runtime/rpc/methods/git-params.ts # src/main/runtime/runtime-git-status-commands.ts # src/preload/api/app-api.ts # src/preload/preload-runtime-support.ts # src/renderer/src/components/native-chat/use-native-chat-context-menu.tsx # src/renderer/src/components/settings/NativeChatExperimentalSetting.tsx # src/renderer/src/components/sidebar/CommentMarkdown.tsx # src/renderer/src/components/sidebar/WorktreeOpenInMenu.tsx # src/renderer/src/components/sidebar/use-worktree-card-controller.ts # src/renderer/src/components/tab-bar/SortableTabContextMenu.tsx # src/renderer/src/components/terminal-pane/TerminalPaneSurface.tsx # src/renderer/src/i18n/en-runtime-required.json # src/renderer/src/i18n/locales/en.json # src/renderer/src/main.tsx # src/renderer/src/runtime/runtime-git-client.ts # src/renderer/src/web/preload-api/web-app-api.ts # src/shared/default-global-settings.ts # src/shared/global-settings-types.ts # src/shared/rpc-contract/rpc-params-catalog.generated.ts # src/shared/rpc-contract/worktree-params.ts
…removed the Default view select
…t-visual tabs and line caps
…log check now needs
frbarbre
pushed a commit
that referenced
this pull request
Oct 21, 2026
…nce off the deprecated owner resolver (stablyai#30484) The eight mirroring callers (#23-#30) read the owner through runtimeEnvironmentIdWithLegacyFallbackUnlessTwin, so a twin is still not mirrored. Sort-order persistence (#2) leaves out ids that two hosts publish, and ids whose only server proof is a repo row (isUnprovenServerSshRoute), instead of writing them to this desktop's or a guessed server's meta. Deprecated importers 40 -> 31.
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.
1.4.214 → 1.4.214 (
cc6006779f..e72044a936, 387 upstream commits, twenty-three conflicts)package.jsonstill says 1.4.214, so the branch name repeats a fifth time.bundled-skill-guides.ts,rpc-params-catalog.generated.ts,en.json,en-runtime-required.json: took upstream's and regenerated (generate:bundled-skill-guides,generate:rpc-params-catalog,sync:localization-catalog,sync:localization-runtime-catalog).This needed
pnpm install.src/main/runtime/rpc/methods/git-params.ts— upstream deleted the re-export file;git.tsnowimports from
shared/rpc-contract/git-params.ts, which still carriesGitCheckReviewAttributes.runtime-git-status-commands.ts— upstream moved local git behindcreateLocalGitProvider(
providers/local-git-provider.ts), sorequireRuntimeGitProvideralways returns a provider.checkRuntimeGitReviewAttributesnow calls the provider, and the local provider gainedcheckReviewAttributes.runtime-git-client.ts— upstream turned the re-exports intoexport { … } from. Took it andadded
getRuntimeGitReviewAttributes.preload-runtime-support.ts— upstream removed the preload's document drop handler ([STA-6940] Prepare file drops for element owners (PR 2 of 6) stablyai/orca#25748) infavour of element owners and a renderer guard. Took the removal; the fork's
[data-review-asset-drop]skip went with it, andreview-asset-drop-claim.ts(fork fix commit)claims drops over the comment boxes instead.
NativeChatExperimentalSetting.tsx,global-settings-types.ts,default-global-settings.ts—upstream dropped
openAgentTabsInChatByDefault,experimentalStructuredNativeChatand theDefault view select (Chat UI is now one switch). Took upstream's;
openClaudeTabsInWebViewstays,and a fork fix commit gives it its own switch (
ClaudeWebDefaultViewSetting.tsx).SortableTabContextMenu.tsx— upstream hides "switch into chat view" (only a chat tab gets"Switch to terminal view") and adds
TabSessionSurfaceSwitchMenuItems. Kept the fork'sAgentViewSubmenuwithcanChatset to upstream'sshowTerminalViewSwitch, plus upstream's newitems.
CommentMarkdown.tsx— upstream renders block by block (MemoizedMarkdownper block) and movedcomponent selection to
selectCommentMarkdownComponents. Wrapped the blocks in the fork'sMediaGalleryProviderand dropped upstream'sexpandImagesagain (every image expands here).worktree-params.ts— upstream validatesdiffCommentswithDiffCommentSchema, which stripsunknown keys; kept
pendingReviewComments, and a fork fix commit addsagentAuthor,githubCommentUrlandreplyToNoteIdto the schema so agent notes and replies survive.register-app-menu.ts,ssh-git-working-tree-provider.ts,app-api.ts,web-app-api.ts,main.tsx,use-native-chat-context-menu.tsx,TerminalPaneSurface.tsx,use-worktree-card-controller.ts(showLinearTitlebeside upstream'snew Linear context),
WorktreeOpenInMenu.tsx(the Mod+O shortcut label beside upstream's newavailability arguments).