Repository navigation
Sync upstream 1.4.214 - #6
Merged
Merged
Conversation
…yai#24851) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…tablyai#24721) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…tablyai#24805) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…tablyai#24833) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…red Orca servers (stablyai#24204) * fix(native-chat): a host admits structured sessions by client capability, not its own chat setting A host's experimentalStructuredNativeChat decided whether any paired client could reach agentSession.* at all, and whether session.tabs.* showed it structured tabs. That setting is the host user's own launch preference: whether a new agent opens as a chat or a terminal is decided by whoever launches it. Using it as admission control meant a client whose own preference was "structured chat" was refused on a host whose preference was "terminal", and chats opened while the setting was on were withheld from mobile once it was turned off. The gate now asks one thing: did the client advertise agent-session.structured.v1 (in-process callers negotiate nothing and are always admitted). Tab projection and restore follow the same rule. With the setting no longer gating anything, the separate cleanup gate (close, cancel, unsubscribe, release), which existed only so those kept working after the setting was switched off, is identical to the main gate and is folded into it. The settings listener that republished tabs when the setting changed is removed, since projection no longer depends on it. The host setting still picks the default for launches that start on the host itself (agent.launch from mobile, orchestration worker-start). * fix(native-chat): the desktop declares structured chat support to paired hosts The desktop renderer advertised agent-session.structured.v1 (and the Claude, turn-item and background-task capabilities that go with it) to its own main process but not to a paired Orca server. The server therefore refused every agentSession.* call from the desktop and stripped structured chat tabs out of the tab list it published to it, so a structured chat running on a paired server never appeared on the desktop, even though the renderer already mirrors a host's agent-session tabs and drives each one against the server that owns its workspace. The same renderer reads structured chats on either host, so the remote Electron list now carries the same structured-session capabilities as the local one, and the capability test pins that nothing is advertised only locally. * fix(native-chat): negotiate client-chosen launch mode so released phones and old servers keep terminals Hosts advertise agent-session.structured.client-launch-mode.v1: they admit structured sessions by client capability alone. A remote client that does not advertise it (phones released before agent.launch) asks createSupport to pick the launch mode, so the host keeps answering that with its own setting, exactly as before. Cleanup methods keep their own named gate so a future admission condition cannot make close or cancel refusable. * refactor(runtime): keep the Electron client capability list in its own module protocol-version.ts is at its line budget; the list is what the desktop advertises to paired hosts, not the host's own contract. * fix(native-chat): the desktop declares it picks each launch mode itself Paired hosts and the desktop's own main process then answer createSupport by the workspace rather than by their own chat setting. * chore(native-chat): justify the two type assertions this change's lines touch * fix(native-chat): chats that already exist keep showing whatever the chat setting says The structured chat setting decides only what new agents open as. With it off, this machine's structured chats used to be hidden while the host, which no longer reads the setting, still reported them to the workspace activation gate, so a workspace holding only a chat opened empty. The local chat mirror and its startup restore now run whatever the setting says, the continue-after-restart offer follows the chats that exist, and the setting's copy says it applies to new agents. * test(native-chat): pin that a host advertises the client-chosen launch mode * fix(native-chat): mirror this machine's chats only where it holds them Round 1 ran the local chat mirror for everyone so existing chats show whatever the setting says. That gave every desktop a permanent session-tabs listener, which turns on the runtime's phone replication paths, plus two full session-tab censuses at startup, and made the browser client mirror its remote host a second time. The runtime now says whether it holds structured chats: its structured host is built only when saved chats were restored at startup or a client created one here, and it announces the moment one is built. The mirror, the startup restore and the continue-after-restart offer run only when the setting launches chats or the host holds some, and never in the browser client. A chat a paired client creates here with the setting off still appears at once. The chat behaviour settings show wherever chats exist, and the setting's copy says it picks what new agents open as. The toggle-off teardown this made dead is removed. * test(native-chat): record install listeners without a cast * fix(native-chat): mirror this machine's chats only once it holds one, not once its host is built Session history, resume preparation, terminal resume commands and replay-safe phone launches all build the structured host for users who never had a chat, which turned on the chat mirror and the structured-only settings rows until the next restart. The signal is now derived from the host's records (or a records file still owed its import) and pushed when the first chat is restored or created. A throwing listener no longer fails the install that fired it. * feat(native-chat): createSupport reports the saved selection a new chat on this host starts with A chat on a paired server starts with the server's saved model and options, which the desktop could not read, so its picker showed a guess. createSupport's answer, which the desktop already waits for before a paired launch, now also carries that seed as a new optional field (older clients ignore it). Create and createSupport read it through one resolver so they cannot drift. * refactor(protocol): move the Electron remote client capability list into its own module Merging main left protocol-version.ts one line over the max-lines limit on this branch. The list of capabilities the desktop advertises to a paired host moves, unchanged, into electron-remote-runtime-client-capabilities.ts, the module the next PR in the stack already uses for it; importers point there. * test(cross-version): stub the launch seed resolver createSupport now reads * test(protocol): pin the desktop capability divergence against what a paired server receives Every paired transport sends the shared remote base plus the Electron list, so the divergence test now compares that union with the renderer's local list instead of the declared Electron list. A capability added only to the shared base can no longer slip past it. The two base-only capabilities it surfaced are recorded: skills.install-result.v2 has no local caller; the authoritative-inventory label is read by the local tabs sync but dropped by main, and is marked unsettled. The turn-item and both background-task-stop capabilities were already sent through the shared base, so the Electron list no longer repeats them. The wire set is unchanged; this PR's real change on the wire is structured.v1, the Claude structured capability and the client launch-mode capability. * fix(native-chat): the desktop tells its own host it picks each launch mode, so retrying an existing chat works with the setting off * docs(native-chat): name the real exit for the released-phone createSupport rule * test(cross-version): a released client still gets the host-setting createSupport answer; a launch-mode client gets supported plus the seed
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…tablyai#24781) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Orca Worker <orca-worker@localhost>
…#24625) * fix(mobile): release callbacks retained by canceled streams * test(mobile): cover delayed frames after stream cancellation Preserve late-ready cleanup while rejecting canceled scrollback and terminal routing; cover unsent cancellation across browser, client events and session tabs. * test(mobile): model unavailable queued stream transport The encrypted sender rejects writes until connection setup completes. Keep existing session-tab unsubscribe attempts and assert canceled queued openers are never replayed. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> Co-authored-by: Neil <neil@stably.ai> Co-authored-by: m4air <m4air@Mac.localdomain>
…blyai#24665) * fix(mobile): reduce base64 allocations for encrypted text frames * Correct screencast encoder comment after byte-codec extraction --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> Co-authored-by: m4air <m4air@Mac.localdomain>
…tablyai#24670) * fix(cursor): restore exact conversation after startup snapshot * fix(terminal): preserve ready snapshot reattach and isolate bridge fixtures * test(cursor): seed the real startup bridge after module resets * test(terminal): settle startup snapshots in remote restore fixtures Signed-off-by: Neil <neil@stably.ai> --------- Signed-off-by: Neil <neil@stably.ai>
…tablyai#24707) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> Co-authored-by: m4air <m4air@Mac.localdomain>
…chat launch (stablyai#24564) * fix(native-chat): remove the "still starting" notice that flashed on chat launch Every structured chat passes through a starting phase, and the pane showed "<agent> is still starting. Messages wait until it is ready; close this chat to give up on it." for it. A 5 s grace period only narrowed the flash to starts that finish just past it. A message sent while the agent starts already counts as working, so the chat's working indicator and Stop cover that state; the notice only repeated it. Removes the notice, its copy in every locale, the delayed-status hook that existed only for it, and the per-child key the chat read only to reset it. * docs(agents): no pop-up notices for transient or internal states Records the rule the removed startup notice broke, so agents building UI show transient states through existing surfaces instead of new messages. * docs(agents): allow the common delayed loading placeholder The rule against delaying a flashing message should not forbid a quiet skeleton or spinner that waits a moment before appearing. * fix(native-chat): stop publishing which provider child is starting hostExecutionChild existed only to re-key the removed starting notice's delay. Older clients read it as optional, so omitting it only changes when their notice appears after a still-starting child is replaced. * docs(agents): narrow the status-notice rule's wording Ban 'execution host' rather than ordinary host copy, scope 'confirming' to a connection, and keep the no-delay rule where the common pattern delays. * docs(agents): let a control's own busy state wait, per the style guide The status-notice rule covers notices and status lines; a delayed in-place label swap like "Saving…" stays as STYLEGUIDE.md describes. * docs(agents): keep the status-message guideline out of the repo
…tart, no "Not reported" caption (stablyai#24588) * fix(native-chat): stop the loading flash, the doubled launch failure, and the "Not reported" caption - A structured chat no longer paints "Start a chat" or the full-pane "Loading conversation..." text before its first read settles; the pane stays blank until the history (or its absence) is known. A chat this view just started keeps its empty state through publish. - A failed chat start is said once, by the chat's own Retry line. The "Could not open <agent> chat" toast that fired beside it, and again on every failed Retry, is removed; the raw error still goes to the log. - The Fast mode switch in the options menu no longer carries a "Not reported" / "Default" caption. * fix(native-chat): show a quiet spinner if a chat's first read lasts The blank pane stayed blank with no cue for as long as the first read took, including a slow remote read or a pending resume. The pane now holds a textless spinner (announced as "Loading chat") that a CSS animation delay keeps invisible for the first 250 ms, so a quick read still paints nothing. A cancelled launch no longer holds the pane in that state; it shows what the chat shows when not launching. * test(native-chat): a refused new-tab chat start reports in its chat, not a toast The new-tab launch opens (or reuses) the chat tab before the start can fail, so its Retry line is the one report. The guard tests now pin that the chat tab exists and no toast fires, for both a refused start and one whose outcome could not be confirmed. * fix(native-chat): leave a failed resume's pane blank beside its Retry line A resumed chat whose start failed or could not be confirmed kept the loading spinner turning forever above "Chat could not be started.", though nothing was reading its history. The spinner now shows only while a read or the resuming launch is in flight. Also log a chat tab that throws while opening during worktree creation, and drop a comment that still described the removed launch-failure toast.
…lyai#24594) * fix(native-chat): plain wording for chat errors and status rows Replaces Orca-internal words (host, journal, transcript, outbox, process, unverifiable, "no contact", byte budgets) in native chat refusals, status rows, the skills menu, subagent and background-task state labels and the history-load error with plain language, and routes the two hard-coded English composer errors through translate(). Copy only; no behaviour change. * fix(native-chat): match chat copy to what happens and to the sidebar's words - The held-message row says Orca keeps checking, which it does: the idle sweep retries the unproven stop and a landed retry sends what waited. - An unsettled earlier message reads as unconfirmed, not undelivered. - The skills-unavailable line names SSH chats, its only cause. - Subagent and background-task rows say "no recent update", the sidebar's words for the same state, and "status unavailable" after a count; the row no longer repeats the state as a reason. * test(native-chat): find the repair row by its own text, not the old wording * fix(native-chat): word the history-repair and too-large rows in the reader's language Both rows were finished English the host wrote into the chat, so nothing could translate them. Each now names itself with a presentation, the way the compaction row does, and the chat says it through translate(). The English text stays on the row for clients that predate these presentations and for the phone. * fix(native-chat): say composer send errors with the chat's notice sentences The composer's two errors had their own wording file beside the sentence table every other chat notice uses. The send outcome now carries notice parts, worded by the same function as the rest. A refused redelivery says "Orca couldn't confirm your message reached the agent. Check the chat, then send it again if needed." (the same sentence the failed-send rework uses), and a message this client couldn't store says "Couldn't save your message. Try again." * fix(native-chat): drop the retry line, keep one name for a lost task, and say only true causes - The history error pane no longer adds "Orca keeps trying to load this chat." under its title: the read still retries on its own, but the pane says only that the chat didn't load. - A write refused as unsupported asks for an Orca update only when no reason came back, which means the host is older. A named reason (a location or agent that can't run there, no chat host, a client missing the capability) now reads "This isn't available in this chat.", since updating doesn't fix it. - A task Orca lost track of is "no recent update" everywhere; after a count it reads "2 agents with no recent update" instead of a second name. - The skills menu announces the same sentence it shows, including in SSH chats. - The row for messages held behind a previous agent reads "{{agent}} from before may still be running. Your messages will send once it stops." * fix(native-chat): a chat whose host can't run it says its history didn't load A history read refused as unsupported with a named reason left the error pane saying only "This isn't available in this chat.", which never said the chat failed to load and named nothing the reader asked for. A read now says "This chat's history couldn't be loaded."; other writes keep the shorter sentence.
Normalize OpenCode cache reads and writes consistently across usage views, persistence and telemetry. Continues stablyai#20187. Co-authored-by: griffinmartin <griffinmartin@users.noreply.github.com>
Retain complete status plugin files during replacement, symlinks and existing permissions, including legacy Windows directory permission recovery. Fixes stablyai#24121. Continues stablyai#24131; permission-recovery review credited to pullfrog. Co-authored-by: Ahmed Nagy <ahmednagy25t@gmail.com>
Track the attested tmux selected pane in the canonical status store and fence retired refreshes after asynchronous root resolution. Fixes stablyai#10039.
* feat(accounts): link standalone GLM Coding Plans Adapt the reviewed GLM accounts contribution to current main, retain Antigravity behavior, guard late credential results, expose storage protection, and redact quota errors. Co-authored-by: Luchong <lu740528977@gmail.com> * fix(accounts): retain GLM credential results during quota refresh * fix(accounts): make GLM credential editing desktop-only * fix(accounts): mirror the host GLM site in paired clients * fix(accounts): report unknown GLM host details and split web settings tests Apply the independently reviewed Accounts correction from697284a without the v2 adapter commits. Preserve the saved-key store and serialized write behavior. * fix(zcode): ship required GLM account translation entries * chore: record GLM reconciliation hook validation * chore: validate installed GLM commit hooks * test: complete GLM account fixtures and web API inventory --------- Co-authored-by: Luchong <lu740528977@gmail.com>
…ablyai#24776) * fix(ci): run mobile typechecks without concurrent dependency refresh * test(ci): check effective Linux E2E package list * test(ci): preserve the mobile production compiler barrier --------- Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
…#24947) A restored pane waits for the initial status replay before subscribing to PTY output. This fixture never settled that replay, so fish printed its mode-2031 arm before the renderer connected. Its PTY API also omitted the reset-input listener required by the serializer, aborting attachment. Settle and dispose the existing startup-snapshot registration and provide the same reset-listener mock used by the other PTY tests. The real fish child-stdin assertions and timeouts remain unchanged. No production change.
…lyai#24640) Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes. Original-commit: f707cde fix(shortcuts): defer TUI editing chords in terminal-first mode Original-commit: 0c6348e docs(shortcuts): describe deferred preview terminal chords Original-commit: be62c1b Align worktree history shortcut metadata with terminal conflict policy Restacked-from: be62c1b Restacked-onto: f7b1f9d
* Add Qoder session history and search with real CLI coverage * Allow the real Qoder marker file to end with a newline * Keep Qoder tool output out of history previews and search * Keep Qoder search pages readable by older clients * Verify persisted Qoder history after a real generated and resumed task * Negotiate Qoder filters before searching an older execution host * Combine search client imports for the CI plugin gate * Keep the relay search oracle aligned with legacy agent filtering * Register supervised Qoder China and Qwen lifecycle integration * Cover Qoder China mobile assets and mixed-host resume gates * Verify Qoder provider tags against the older released wire parser * Verify China and Qwen keep independent Windows hook scripts * Verify Qoder registrations against the installed older Windows release * test(qoder): align search capability contracts and pin old-host fencing * fix(qoder): rank exact picker identities and command aliases first * test(qoder): preserve the regional CLI shared icon expectation Keep the full bundled-asset and no-remote-image checks, with an explicit shared-logo basename for Qoder China. The map also works with older catalog type unions. * fix(qoder): align China catalog entry with fallback order --------- Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
…tablyai#24951) * Select lookup automatically for the measured hosted root-install profile * Record hosted automatic-mode cold cache publication proof
…ablyai#24692) * feat(antigravity): bridge IDE history into new CLI conversations * fix(antigravity): preserve fresh-launch model and environment for IDE references * fix(antigravity): forward IDE history opt-in through desktop IPC * fix(antigravity): rebuild remote IDE reference startup on its host * fix(antigravity): register IDE continuation action labels * fix(antigravity): confine IDE references and bound metadata reads * fix(antigravity): localize IDE continuation badges * Preserve scanner service cache assertions and refresh Antigravity opening metadata * Preserve Antigravity opening joins and target folder runtime authority
Preserve bounded retry behavior and the current-main status-envelope fields. Original-PR: stablyai#24124 Reviewed-source: 103144f Co-authored-by: Justas Brazauskas <brazauskasjustas@gmail.com>
…tablyai#24615) Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth. Original-topic-commit: 7903f1c Original-topic-commit: 588117b Original-topic-commit: dedd4f8 Original-topic-commit: 6645dae Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276 Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276 Restacked-onto: b032867 Co-authored-by: kespineira <kespineira@users.noreply.github.com> Co-authored-by: kevimux <kevimux@users.noreply.github.com> Reported-by: pullfrog Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f Native-helper-source: 80dbe23 Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
…blyai#24530) An identical forced refresh previously returned an empty Zustand patch, creating a new root state and notifying every subscriber. It now returns the current state after renewing the existing freshness timestamp. Real row or metadata changes still publish once.
Reuse the existing pure recent rank once per actual entry tuple within each synchronous sort; discard the lookup after sorting.
…blyai#24917) * fix(native-chat): start a new chat after an earlier start failed A chat whose start the host refused stayed registered under its agent and workspace, so the + menu and new-tab search kept that agent disabled with a spinner, and any later "new chat" for that agent (for example "Send notes to > New agent") restarted the failed chat instead and dropped its own prompt. Only launches in flight now hold the agent/workspace slot that later starts join. The registry keeps every launch by session id and derives the holder, so a failed chat keeps its own Retry while a new start makes a new chat. * test: import the launch status from its own module * test: name the owning host on the launch intents the new tests build * fix(native-chat): a new start never joins an unconfirmed or retried chat A new start for an agent joined a chat whose create answer was lost, or a failed chat whose Retry was in flight, and dropped its own text: the source-control "Fix with AI" action reported success with nothing sent, "Send notes to > New agent" sent nothing, and after a reload the text became an unsent draft in the old chat. Only a new start's own first create now coalesces later starts (double click, two callers at once). Each attempt records whether it is that first create or a Retry/re-check of an existing chat; an unconfirmed or retried blank chat keeps its own Retry and a new start opens a new chat with its text. A resume still re-checks or joins its conversation's launch, since the host refuses a second adoption. * fix(native-chat): a different new request always opens its own chat A new start used to join any blank chat still in its first create or still sending its opening text, so a second "Fix with AI" for another check, or notes sent to a new agent, landed in that first chat. Each first attempt now keeps the request it was started with (its text and whether it is sent or drafted). Only a repeat of that request (a double click, a retried call) joins it, and it shares the text already staged, so the text is sent once. Any other request opens a new chat with its own text. The + menu, new-tab search and send-notes menus disable an agent only when their own pick would join. Resume launches are unchanged. * fix(native-chat): an empty chat still starting takes the first text sent to it A blank chat that is still starting (a + pick, the empty-workspace default chat) is empty, so the first request with text, such as notes sent to a new agent, now goes into it instead of opening a second chat beside it. That request becomes the chat's own request: an identical repeat joins and is sent once, and any other request opens a new chat. Move the logic that decides which launch a start joins out of the launch registry into its own module, so the registry stays under the line limit once stablyai#24904 lands beside it. Pin that a repeat arriving while the opening text is still sending is sent once. * test: an empty chat delivers the claiming text the way its request asked * fix(native-chat): only a chat its user has not used yet takes another request's text A blank chat that is still starting was claimable by the first request with text even after its user had sent a message into it or typed into its composer, so notes or a Fix with AI prompt could land in a conversation the user had already started. Emptiness is now read from what the chat holds: nothing in its outbox and no text in its composer draft. A chat its user has used stays theirs, and the request opens a new chat. If the claiming text cannot be saved, the claim is not recorded: the request falls through to a new launch, whose save failure shows on that chat as for any new launch, instead of reporting a failure nothing showed. * fix(notes): notes handed to a send leave the next send until it settles Notes sent to an agent stayed in the notes shelf until their chat delivered them, so a second "Send notes" made while the first new chat was still starting collected the first notes again. With every different request now opening its own chat, those notes reached two chats. When notes or browser annotations are handed to a send (a new agent, a running agent from the menu, or a sidebar agent row), they are held in memory against that send's own delivery result and left out of the next send. Delivered notes are removed as before; a failed, refused or undelivered send releases the hold, so they come back for the next send. The notes menu now builds each scope's prompt from the notes it will send. * test: give the annotation tray fixture its hand-off callback * fix(notes): offer no send when every note is already on its way When every note or annotation in a send is held by an earlier send still in flight, the built prompt is empty. The notes menu already disabled its trigger then, but its New agent rows still started an agent with no text, the annotation Send buttons still opened, and a sidebar agent row could be sent an empty prompt. The New agent rows, the annotation Send buttons and the sidebar send now offer nothing when there is nothing to send. * fix(notes): a new chat's staged prompt decides when its notes leave the shelf Notes sent to a new agent were released back to the shelf as soon as the chat's start failed, while that failed chat kept the same text staged for its own Retry. Re-sending and pressing Retry put the notes in two chats, and Retry alone left delivered notes listed as unsent. For a new chat, the notes now follow the prompt it staged: held while that message is in the chat's outbox, cleared from the shelf when any dispatch (Retry or re-check included) sends it on, and back on the shelf when the chat is closed and its outbox thrown away. A paired server's chat is found once its start settles. Running-agent sends keep their own result. While notes are only on their way, the send button reads "Sending…" rather than "All notes sent". * fix(notes): a new chat's saved message keeps its notes out of another send across a reload The hold that keeps notes sent to a new agent out of the next send lived only in memory. A reload while that chat had not yet sent them put the notes back on the shelf while the chat's saved message still carried their text, so a second "Send notes" sent them twice. The staged message now saves the send keys of the notes it was built from. The shelf treats a note as on its way while any saved message carries its key, read from the saved outboxes on first use and kept current by the outbox's one write funnel. When a message carrying notes is sent on (its own start, a Retry or the re-check), those notes are cleared from their shelf; when it is thrown away with its chat, they come back. Browser annotations sent to a new chat use the same keys while the app runs. Running-agent sends keep their in-memory hold. This replaces the per-message watch and outcome promise from the previous change. * fix(notes): release a gone chat's notes, and clear sent ones in the web client too Two gaps in keeping notes out of another send while a saved chat message carries them: - The web client never installed the clearing that removes notes once a chat sends them on, so after a Retry there the notes stayed listed. It is now installed, once per renderer, by the background services both the desktop and the web client load with App. - A saved message carrying notes was thrown away only by this window's own close. A chat closed from the phone, another window or while Orca was off, or closed here without a known host, kept its notes held for good. A chat the host stops listing (affirmed, and not a launch still starting or failed) now has its queued messages thrown away once that sync lands, as does a close that can name no host, so the notes come back. * Revert "fix(notes): release a gone chat's notes, and clear sent ones in the web client too" This reverts commit ae0b9f3. * Revert "fix(notes): a new chat's saved message keeps its notes out of another send across a reload" This reverts commit b88d0dc. * fix(native-chat): a new chat joins only a re-delivery of the same user action "The same request" was recognised by its content (agent, workspace, trimmed text, sent or drafted), which split one action whose text changes between deliveries ("Fix with AI" re-fetches logs) and merged two different actions that happen to carry the same text. Each user action now mints one request id where it is handled (the click, menu pick, notes send, shortcut, Fix with AI press, quick command; a worktree create uses its creation id; programmatic starts mint once at their entry) and passes it through the launch plan, its verdict and the structured launch options, where it is required. A new start joins a chat only when its first attempt carries the same id: a double click or a caller retrying its own call makes one chat and sends the first delivery's text once; any other action opens its own chat, whatever its text. An empty chat that is still starting is still claimed once by the first action with text, and then belongs to that action's id. Resume launches keep joining their conversation's launch. The + menu and new-tab search no longer grey out an agent while one of its chats starts: every pick is a new action and opens its own chat. The id lives in memory only; nothing reads it after a reload (a restored launch is reached by its session id through Retry, never joined). * fix(native-chat): a new chat with no text reuses an empty one instead of stacking another Two bare "+ > Claude" picks (or new-tab search, the new-agent shortcut, the dashboard) opened two empty chats. A request with no text now focuses an existing empty chat for that agent in that workspace: one still starting (joined in the launch, as a re-delivery is) or one that published and sits idle (its host's journal holds no request, read live from the status feed). Empty also means nothing queued, no composer text or images, and no launch draft its composer has not taken. Failed, unconfirmed, resumed and other-agent/workspace chats are never reused. A request with text still opens its own chat, or claims an empty starting one as before; request-id dedupe is unchanged. * fix(native-chat): a new chat with no text reuses an empty chat only in the split it was opened from Pressing + in the right-hand split focused an empty chat sitting in the left-hand split. The reuse now looks only in the tab group the pick targets (the caller's group, else the workspace's active group, which is where its tab would open); a pick in another split opens a new chat there. Applies to both an empty chat still starting and one that published and sits idle. * fix(native-chat): text sent to a new agent claims an empty starting chat only in its own split Notes sent from the right-hand split could land in, and focus, an empty chat still starting in the left-hand one. A request with text now claims an empty starting chat only in the tab group it opens in, the same rule a request without text follows; elsewhere it opens its own chat there.
…ablyai#25357) stablyai#25176 and stablyai#24489 landed together and collided in insertRichMarkdownImageFromPath. stablyai#25176 added three test files whose @/store mock returns { settings, folderWorkspaces, worktreesByRepo }. stablyai#24489 made the same function look up the open document with state.openFiles.find(...) to pass document-folder access to the import. Under the mock, openFiles is undefined, so the lookup throws, the catch shows "Failed to insert image", and no image is inserted -- 38 tests fail on main. The real store always carries openFiles, so production behavior of both PRs is intact; the mocks were just written against the pre-stablyai#24489 shape. Add openFiles: [] to the three mocks.
…yai#24768) * feat(orchestration): support OpenCode worker model selection Allow supervised OpenCode workers to use per-launch model overrides through the existing launch-preference and receipt path. Preserve existing OpenCode agent arguments while replacing only model flags, and reject unsupported effort values explicitly. Keep Native Chat option exposure unchanged and update the worker CLI and orchestration guidance. * fix: wait for OpenCode worker composer before first dispatch Reuse captured composer readiness on local and paired execution hosts and revoke launching-shell paste anchors. Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> * feat(opencode): probe execution-host CLI capabilities * fix(opencode): select plugin default for execution host loader * fix(opencode): limit prompt prefill capability to verified release * feat(orchestration): gate OpenCode worker model preferences by host capability Co-authored-by: user141514 <user141514@users.noreply.github.com> * feat(opencode): probe launch capabilities on the execution host * feat(opencode): probe execution-host CLI capabilities * feat(opencode): probe launch capabilities on the execution host * feat(orchestration): resolve explicitly configured command aliases * fix(opencode): select plugin loader for the launched host binary * fix(opencode): match WSL probe cwd and declared guest environment * fix(orchestration): verify available OpenCode model on execution host * Add host-owned OpenCode and Devin account profiles * fix(orchestration): inspect OpenCode models with selected account environment * fix(orchestration): bind model validation to direct existing-workspace launch * fix(opencode): preserve launch environment deletion boundaries * fix(orchestration): limit effective model contract to verified OpenCode release * wip(opencode): authorize native startup prompt intent at execution owner * fix(opencode): atomically replace status plugin entrypoints * fix(opencode): retain plugin permissions across restrictive umasks * test(opencode): resolve permission fixture from primary cwd * feat(opencode): install startup prompt plugin independently of status hooks * Restore inherited account environment and preserve cleanup retries * fix(opencode): select plugin loader for the launched host binary * fix(opencode): match WSL probe cwd and declared guest environment * fix(opencode): preserve launch environment deletion boundaries * wip(opencode): authorize native startup prompt intent at execution owner * fix(opencode): atomically replace status plugin entrypoints * fix(opencode): retain plugin permissions across restrictive umasks * test(opencode): resolve permission fixture from primary cwd * feat(opencode): install startup prompt plugin independently of status hooks * Check relay environment values before merging * fix(opencode): wait for admitted startup intent and preserve failed-launch briefs * fix(opencode): confine overlay manifest cleanup to owned directories Co-authored-by: Adnan Khan <adnank11427@gmail.com> * fix: wait for OpenCode worker composer before first dispatch Reuse captured composer readiness on local and paired execution hosts and revoke launching-shell paste anchors. Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> * fix(orchestration): validate configured aliases with target shell grammar * fix(orchestration): use actual shell and refuse assignment-only aliases * fix(opencode): wait for admitted startup intent and preserve failed-launch briefs * Support verified OpenCode v2 model selection in fresh native workers * fix(opencode): refuse unsupported startup preferences before model probing * feat(opencode): probe execution-host CLI capabilities * fix(opencode): select plugin default for execution host loader * fix(opencode): limit prompt prefill capability to verified release * feat(opencode): probe launch capabilities on the execution host * fix(opencode): select plugin loader for the launched host binary * fix(opencode): match WSL probe cwd and declared guest environment * fix(opencode): preserve launch environment deletion boundaries * wip(opencode): authorize native startup prompt intent at execution owner * fix(opencode): atomically replace status plugin entrypoints * fix(opencode): retain plugin permissions across restrictive umasks * test(opencode): resolve permission fixture from primary cwd * feat(opencode): install startup prompt plugin independently of status hooks * fix(opencode): wait for admitted startup intent and preserve failed-launch briefs * fix(opencode): unsubscribe hook settings during async host shutdown * fix(opencode): confine overlay manifest cleanup to owned directories Co-authored-by: Adnan Khan <adnank11427@gmail.com> * test: preserve typed calls in configured worker target checks Replace Reflect.apply with the existing typed prototype call pattern so the unchanged regression cases pass the anti-slop lint gate. * test: check typed model host calls and terminal delay state Replace Reflect dispatch and property reads with checked access and typed calls. Complete the existing resume request fixture with its required identity fields without changing the rejection boundary or assertions. * test(readiness): census recorded OpenCode composer boots * fix(opencode): reject redirected overlay parents before cleanup * fix(orcad): retain runtime cleanup when subscribing to hook settings * refactor(launch): extract OpenCode config and attachment authority * fix(opencode): retain host version selection across relay restarts * fix(opencode): pass run prompts as positional messages Preserve run flags and use the existing shell quoting and run-command detector to append the initial message after --, reusing an existing separator. TUI launches retain their version-selected prompt transport and draft behavior. Original run-order work: @coelho-doti (stablyai#13065, tracked in stablyai#17551). * fix(opencode): keep wrapped run tasks positional Recognize supported environment prefixes and PowerShell call operators without mistaking prompt arguments for executables. Keep environment and run separators separate, preserve the task text and exclude run commands from native submission. Source-parent: 23fc08b Related-to: stablya/orca#17551 Credits: @coelho-doti (stablya/orca#13065) * Prepare complete private OpenCode launch validation source Integrate the complete reviewed readiness, capability, native prompt, overlay and positional-run source onto frozen main. Preserve canonical atomic ACL retry, status generator/disposal, restrictive-umask fixtures and unowned source. Keep supported wrapped run commands positional. Private-validation-source: a44345c Original-full-source: 23fc08b Original-core-base: 8186ded Frozen-main: 08ee7ba Owned-source-paths: 111 Publication-policy: private validation only; preserve the six separate PR boundaries and held model/provider drafts Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Co-authored-by: Adnan Khan <adnank11427@gmail.com> Credits: juli-gonzalez readiness contribution; Ahmed Nagy atomic plugin writer; coelho-doti positional run contribution * Prepare private complete 111-path launch validation on current main Private validation only. Preserve main credential additions and original launch ownership. Held model and provider topics remain excluded. * Recognize env options before positional OpenCode run messages * STRICT launch CI contract correction * CAPS launch CI contract correction * INTENT launch CI contract correction * test(opencode): wait for malformed claim retries before expiring intent Observe real endpoint I/O completion under fake timers before forcing expiry. * test: initialize Claude prompt state in output retention fixture * Verify OpenCode catalog model launches and preserve current launch behavior * Verify OpenCode catalog model launches and preserve current launch behavior * Refuse unverified new-worktree OpenCode models and complete host audits * Wait for OpenCode location hydration in intent startup * Refuse unverified new-worktree OpenCode model launches and record startup attribution * fix(opencode): bind startup readiness to the composer location * Bind OpenCode startup readiness to the current location in intent startup * Restore the owning Orca CLI path after shell profiles * Use a literal marker for the Bash lookup regression * Preserve plain panes and initialize zsh after prompt hook replacement * Preserve user line-editor dispatchers during deferred startup * fix: retain CLI startup when global Zsh replaces prompt hooks * test: replay global Zsh hook replacement after host startup * test: isolate controlled Zsh widgets from distro keyboard setup * fix(shell): preserve user hooks during deferred zsh initialization * Retry interrupted OpenCode startup prompt claims * Keep completed Zsh startup hooks retired when the wrapper is sourced again * Reject truncated OpenCode catalogs and explain worktree model limits * Use a template literal in truncated-catalog coverage * Refuse unfinished OpenCode model catalogs --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Co-authored-by: user141514 <user141514@users.noreply.github.com> Co-authored-by: Ahmed Nagy <ahmednagy25t@gmail.com> Co-authored-by: Adnan Khan <adnank11427@gmail.com> Co-authored-by: Orca startup hydration review <agents@stably.ai> Co-authored-by: Codex <codex@openai.com> Co-authored-by: Orca maintenance <orca-maintenance@users.noreply.github.com> Co-authored-by: Orca <dev@stably.ai> Co-authored-by: Orca campaign <orca-campaign@local.invalid> Co-authored-by: Orca OpenCode issue campaign <opencode-campaign@stably.ai> Co-authored-by: OpenCode issue campaign <codex@localhost> Co-authored-by: Nathan Parker <nwparker@users.noreply.github.com>
* test(config): reproduce rejected repository OpenCode config * Add repository OpenCode permissions and allow its reviewed root config * fix: preserve sensitive OpenCode confirmation prompts --------- Co-authored-by: Orca campaign recovery <campaign-recovery@example.invalid> Co-authored-by: Orca OpenCode Campaign <opencode-campaign@local.invalid>
…es (stablyai#25350) evacuateDeadCells ran one query that joined every assignment to its cell's liveness and fence state, ordered by primary key with LIMIT 100. In production no row ever matched (the only dead cells are stale existing-only cells without a committed fence), so the planner walked the whole relay_assignments primary key every call: 551 ms mean over 85k calls, ~12% of relay database time. A cell-level pre-check now evaluates the predicate's cell-only half over the ~33-row cell tables (3 ms in production). It is a superset of the cells the host query can act on, so the sweep returns early when it is empty and otherwise restricts the host query to those cells. The host predicate is unchanged.
… batches (stablyai#25352) relay_control_connection_reservations was never deleted: 13.8M rows and 9.1 GB in production, 99.999% of them in state 'released' and ~400k more a day. Nothing reads a released row (every reader filters it out by state), but each placement still locks all of its host's rows, ~160 on average. A new director sweep step deletes released rows older than a day. It walks the heap in TID ranges of 16 pages, because without an index on released_at a `LIMIT n` delete plans as a sequential scan from page 0 that gets slower as the head of the heap empties (production EXPLAIN). Each statement selects its rows FOR UPDATE SKIP LOCKED, so a row a request holds is skipped rather than waited on, and deletes them by `ctid = ANY(ARRAY(...))` so the delete is always a TID scan. A tick stops at 400 rows, 128 pages or 250 ms, which drains the backlog over about three days at five directors.
…after promotion (stablyai#25367) Both were promoted to general on 2026-10-01 (selector 344 shows them general), but the same-cap job still classed them migration-only. Rollback mode on either would isolate (general -> migration-only) before its no-op check failed, demoting a live cell. They stay out of the fleet pool list (pool 10, not 16). Claude-Session: 1145a80d-dec4-4a9b-9373-bbbb876b9041
Start and resume Qoder through the existing execution-host selector when only the documented qoder command exists. Preserve legacy qodercli preference, explicit commands, quoting and session identity; disconnected SSH execution refuses without local fallback. Mobile history uses command-at-create only with the optional owned-create capability and an existing stable mutation identity. Reconcile authoritative execution-host inventory before retrying creation, adopt the same surviving operation, preserve WSL/incarnation metadata and restore only missing original launch metadata. Changed retry settings cannot replace original capture. Bounded evidence expires after 15 minutes; unavailable inventory or original evidence refuses recovery and leaves surviving execution untouched. Authoritative inventory proving absence preserves the existing recreation behavior; this is not a durable exactly-once ledger for completed one-shot side effects. Older hosts retain the acknowledged create-then-send path. Scope mobile launch authority to the current committed host/client generation, and recheck operation ownership after asynchronous preparation before later sends. Retire the mutation once the host acknowledges the resume; later ownership changes stop navigation without reporting a completed resume as failed or reusing a cached legacy pane. Preserve genuinely interrupted mutation identity. Preserve current-main OpenCode validation and merged Pi/Cursor behavior. Correct unchanged-main editor test fixtures to their production insertion-range and store contracts while retaining original assertions and production behavior. Credit: Neil Parker (@nwparker); Soperf and jyang for Qoder integration/history groundwork in stablyai#24614; actual Pullfrog and CodeRabbit reviews and the independent Source reviewer for the mobile retry, launch capture, evidence lifetime and connection ownership findings. Conservative positional process recognition and existing folder-history matching-worktree limitations remain documented.
… days (stablyai#25353) * fix(relay): retain confirm results for a week and audit events for 90 days relay_confirm_results (8.3M rows, 5.8 GB) and relay_audit_events (8.4M rows, 3.6 GB) were never deleted. The director's credential cleanup now reaps both after its existing passes: - Confirm results older than 7 days. The only reader replays a stored result for a retry of the same request on the same connection basis; a different basis is already refused as a tuple mismatch. committed_at has no index, so this uses the TID-window reaper from the reservation prune, capped at 250 rows a tick (the 7.4M-row backlog drains over 2-3 days). - Audit events older than 90 days, ordered by `at` so the batch walks relay_audit_events_at. Nothing in the relay reads them back. The oldest row is from 2026-07-14, so this deletes nothing until 2026-10-12. reapBatch now deletes by `ctid = ANY(ARRAY(...))` on Postgres: with `ctid IN (...)` the planner can choose a hash join over a sequential scan of the whole table. * chore(relay): record the decided audit retention
* Measure remaining CI import, diagnostic and checkout savings * Qualify remaining CI candidates on hosted runners * Qualify independent mobile typecheck overlap on Actions * Keep explicit RPC test registries from loading unused methods * Qualify complete RPC registry cohort and mobile cancellation * Promote measured CI setup and typecheck savings * Recognize the shared RPC test guard in lint policy * Align the mobile barrier contract with independent typechecks
…25354) * refactor(relay): stop creating three tables nothing writes relay_confirmable_splices, relay_cell_drain_attempts and relay_migration_leases are created at every boot and referenced nowhere else on main except tests. In production all three hold 0 rows and pg_stat_user_tables shows 0 inserts, 0 sequential and 0 index scans since the 2026-09-28 stats reset. This removes them from the schema and the transaction-phase table, and drops the test references. It does not drop them: an older image still runs CREATE TABLE IF NOT EXISTS at boot, and a drop racing that create can fail the older boot's schema step. A follow-up can drop them once no image that creates them can be rolled back to. * docs(relay): note the account-erasure prerequisite for dropping retired tables
… does not list yet (stablyai#25375) Codex models newer than Orca's built-in list (gpt-6.x) were capped at xhigh, so worker-start refused --effort max/ultra that the installed Codex supports. Unknown Codex models now accept the full known effort vocabulary; unknown effort names are still refused. Refs STA-9312
…ablyai#25301) * fix(mobile): honour the desktop pinned-worktree placement setting Mobile always kept pinned rows in their groups as well as in Pinned. Desktop's showPinnedWorktreesInGroups (default off) shows them only in Pinned. Read that setting with its own settings.get operation, refreshed with the list's view settings, and leave host and device-local pins out of their groups unless it is on. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep lineage children with a pinned parent Under single-location a pinned parent left its group while its unpinned child stayed behind as a root. Visible descendants now follow a pinned ancestor into Pinned, nested under it, as on desktop. Drop the inert host-id half of the pinned-policy stale-reply guard; a host switch replaces the client. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): fold pinned placement into the desktop view-settings sync One "sync from desktop" callback now also reads settings.get for pinned placement, unawaited so a slow read never holds the ui.get merge; the separate callback and its plumbing are gone. Re-record the three host.view-settings goldens, which gain only that settings.get send. The pin expansion walks the lineage children index the renderer now shares, over visible rows only. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): one stale-reply rule for the desktop view-settings sync Both reads in syncViewSettingsFromDesktop now drop a reply only when the client was replaced; the host-id half compared a captured value with itself. Pin the no-wait invariant: the ui.get merge applies while settings.get never answers. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): follow a pin through rows hidden by search Desktop's Pinned section walks lineage over every worktree and keeps the visible descendants, so a grandchild still follows a pinned root when search hides the middle row. Mobile walked visible rows only and left it in its group. Build the children index from the unfiltered list; membership stays visible-only. Drop the hostId arg the view-settings sync no longer reads. Pinned-lineage tests move to their own file to stay under max-lines. * refactor(mobile): read pinned placement in its own hook The placement read was folded into the desktop view-settings sync, so that callback juggled an awaited and an unawaited read and three goldens recorded a settings.get that never answers. useHostShowPinnedInGroups now owns the setting and the catalog refreshes it beside the view-settings sync on connect, focus and mount; the sync, screen state and goldens are back to main. The reader returns desktop's boolean, as its siblings do, so transport no longer imports a worktree type and the two-literal policy union is gone. makeSection always applies lineage, the pinned walk is a Set worklist, and the lineage children index drops a set that duplicated its map's keys. * fix(mobile): key pinned placement to the client that reported it The host screen is reused across hosts, so the previous host's "show in groups" value survived a switch until the new read landed, or for good if it failed. The setting now carries the client that reported it and counts only for that client, which also makes a late reply from a replaced client inert and drops the clientRef plumbing. * fix(mobile): drop a replaced client's late placement reply Keying the value to its client hid a previous host's value but still let that host's late reply overwrite the current host's, reverting the list to the default until the next refresh. Restore the clientRef write guard; the client key still covers the effect-long window before clientRef follows a switch. * refactor(mobile): treat pinned placement like the screen's other host state The placement hook kept its own host-switch handling (a client-keyed value plus a clientRef guard) beside the screen's existing one. showPinnedInGroups now lives in HostScreenState, resets with the other host-scoped values in useHostScreenIdentity, and is read in the catalog refresh behind the same clientRef check as the catalog fetch. The hook and the catalog argument go.
…25336) Adds a sixth asia-east2 cell at the C31 shape (cap 3000, 6000 request units, pool 16, e2-standard-4) in asia-east2-c, pinned to the f30b5cb1 cell image. It gets its own topology and registration wave but no promotion wave, so the admission script and workflow refuse to promote it; it stays a migration-only landing zone and out of the fleet pool list. Claude-Session: 1145a80d-dec4-4a9b-9373-bbbb876b9041
…tablyai#25381) * Bound CSV preview memory and virtualize resizable linked cells * Fix dense CSV previews and retain bounded viewport pages * Align CSV record limits across BOMs and line endings * Respect headful mode in CSV visibility checks
…lyai#25382) * Fix duplicate session option flags and preserve argument values * improve tests
…oll boxes (stablyai#25410) The activity row previews the agent's last reply with the compact markdown renderer, which draws fenced code blocks and tables as their own scrollable boxes. Those boxes escaped the row's line clamp, so a reply with a code block put a tall grey box with scrollbars in the middle of the list. Row-scoped overrides now flatten code blocks and tables into plain wrapping text, so the existing clamp cuts them like any other line. Other markdown surfaces are unchanged.
* Add translations for terminal shell settings and search Wrap Terminal Pane shell configuration strings and terminal search keywords with translation calls to enable localization across supported languages. Add translated strings to all locale files and update the localization coverage allowlist to reflect properly translated content. * fix translation
…a finished turn, with one explanation (stablyai#25043) * fix(native-chat): read a crash-cut reply like a finished turn, with one explanation A reply that an Orca crash or restart cut off said it failed three times: the turn bar read "Failed after N", the chat's notice row said Claude stopped, and the sidebar dot stayed red after the chat was read. The turn bar now reads "Worked for N" for a proven crash/restart cut, the notice row stays the one explanation, and the sidebar card's dot and agent rows read failed only until the user has visited the chat (the same acknowledgement that un-bolds the row), then read done. A failure, a user's Stop, a replaced turn and an unconfirmed end keep their labels. The stored outcome is unchanged. * fix(native-chat): explain a turn a quit or eviction cut, once A turn cut off by quitting Orca, an idle eviction or a teardown recorded the same outcome as a crash-cut turn but wrote no notice row, so with the turn bar now reading "Worked for N" nothing in the chat said it stopped. The host stop's settle now writes the existing providerExited notice for the latest turn when it ends as news (no person's Stop decided it): in the same write as the host's own turn end, or right after the adapter's. It is keyed by the turn, and skipped when an error row already explains that turn, so a retry or an earlier exit row never leaves two. * test(native-chat): narrow the turn scope and add the seen map in the crash-cut tests * fix(native-chat): derive a cut turn's one notice on read instead of writing it at stop time A turn cut short when the agent stopped without anyone asking now reads "Worked for N", so it needs a row saying it stopped. Writing that row only on the quit and eviction paths missed journals written before this change, a quit that died between its drain and its settle, and any future stop cause. The transcript now derives it from the journal on desktop and phone alike: a root turn that ended interrupted with no verdict and no row about the stop gets one notice in the provider-exit row's words. A stored exit row, matched by its exit fact or its writer's identity rather than its tone, stays the explanation, and so does the restart continuation's own outcome note, so a refused resume says it once. The host's stop path is back to what it was. * fix(sidebar): read a cut-short turn's red mark from its acknowledgement on every surface A turn cut short with nobody asking reads failed only until the user has seen it. The previous revision passed an optional "seen" flag to each caller, so any surface that did not pass it, the chat's own tab dot among them, stayed red beside a sidebar that read Done. The verdict's display now takes the acknowledgement as part of what it reads: the entry's stateStartedAt beside the time the user last acknowledged it, both required, judged by one shared rule. Each surface joins it once where it builds its rows: the sidebar's agent rows (and so the notes send menu), the workspace card summary, the terminal tab bar, Cmd-J recent rows, and Activity threads. Failures, Stops, replaced turns and unproven ends keep their marks as before; the phone, which has no acknowledgement record, keeps the unseen reading. * refactor(attention): use the one acknowledgement rule where it was copied Auto-acknowledgement, the Activity unread count, dashboard row buckets and notification acknowledgement each spelled the same "acknowledged at or after the current state began" comparison; they now call the shared rule. * test(mobile): type the cut-turn notice test's client and hook holder * test: pin an older host's exit row by its writer and the sidebar rows' acknowledgement join * test(sidebar): re-read the card and the tab when only an acknowledgement changes * fix(native-chat): keep a cut turn's notice through a resume that carries on A resume after a quit writes its "asked this agent to continue" note after its own message, so counting that note as the cut's explanation removed the notice once the resume went on, and made it flash away under automatic resume. Only the notes that say the chat was not carried on (refused, not connected, not confirmed) stand in for the notice now. A row about the whole conversation now explains a cut turn only when no other turn lies between them, and a failed start's row, which is about a start, never does. The host writers and the rule share the row identity prefixes, with the contract that a new row explaining a stop carries the provider-exited fact or one of them. * fix(sidebar): judge a cut as seen by the main agent's clock when a subagent holds the row A subagent can keep a row working after its main agent was cut, and the row's clock then predates the cut, so a look at the working chat counted as having seen the cut and no red mark showed. The mark now compares the acknowledgement with the later of the row's and the main agent's clocks; auto-acknowledgement of the chat on screen reads the same clock, and its stamp covers it, so looking at the chat still clears the mark. Bold rows, dashboard buckets, notifications and unread counts keep the row's clock. * fix(native-chat): tie a conversation-wide exit row to a cut only with no message sent since A send after a quit's cut whose new agent died before its turn opened leaves a provider-exit row about the whole conversation. That row is about the send, not the earlier cut, so the cut kept no explanation. An exit row now explains a preceding cut only when no message was sent in between; the restart notes, which follow the continuation's own message, still need only no turn between. The notes that say a resume did not carry the chat on are now told apart from the continued note by their tone ('error' or 'warning'), which every host that wrote them has set, instead of by their words. * fix(native-chat): keep an owner's proven death the explanation of its cut after a send A chat read before the startup reconcile settles its cut turn unverifiable; if the user then sends a message, the reconcile proves the old agent dead and writes its row about the conversation after that message. The row names the old owner's death, never the send, so a message since no longer detaches it from the cut. Only a provider-exit row, which a later start can write, still needs no message sent since. * test(sidebar): give the activity-status store mock the acknowledgement map The card summary now reads acknowledgedAgentsByPaneKey; this test's hand-built store state lacked it, so every summary read threw. * refactor: move the seen-gated red mark out of this change This change now keeps only the cut turn's label and its one derived notice. The red mark that clears once the user has seen a cut turn moves to its own change, so each can land alone; until it lands, the sidebar, tab bar, Cmd-J, Activity and the notes send menu read a cut turn as failed, as before. * test(native-chat): pin that a restart note after a continuation's turn leaves the cut's notice * test(native-chat): keep a cut turn's notice beside a later message drawn as not sent
Display provider credit and currency balances separately from rate limits. Preserve quotas when balance data is unavailable and translate complete labels. Supersedes stablyai#9363; credit to @mmarabel for the original implementation. Co-authored-by: mmarabel <166927047+mmarabel@users.noreply.github.com>
# Conflicts: # src/main/index.ts # src/main/startup/main-process-ipc-bootstrap.ts # src/main/updater/updater-download-install.ts # src/renderer/src/app-shell/use-app-shell-services.ts # src/renderer/src/components/editor/DiffViewer.tsx # src/renderer/src/components/editor/rich-markdown-extensions.ts # src/renderer/src/components/right-sidebar/FileExplorerFilesTreePane.tsx # src/renderer/src/components/ui/textarea.tsx # src/renderer/src/i18n/locales/en.json # src/renderer/src/store/slices/ui/ui-slice-hydration-sanitizers.ts
…d-menu API after the 1.4.214 sync - InlinePRCommentCard hands NotesSendMenu a delivery snapshot and a formatPrompt, the shape upstream's in-flight tracking now needs. - DiffViewer's comment popover moves into useDiffViewerCommentPopover; the directional group-focus chord, the temporary-checkout reveal and the Linear title / PR author card options move into their own modules, so upstream's files carry one-line calls. - web-preload-api-composition.test.ts expects the settingsTransfer namespace the share-settings feature added.
…ngs after the 1.4.214 sync
frbarbre
pushed a commit
that referenced
this pull request
Oct 21, 2026
…tablyai#31066) * fix(cli): worktree create sends the host --project/--host selected Codex review 6 #6: the CLI picked the right setup but sent only its repo id, so a repo id set up on two hosts failed as selector_ambiguous; and --project-host-setup ignored --host when setup ids repeated. worktree.create gains optional repoHostId (older hosts drop it and keep the old behaviour); the runtime narrows the repo selector to that host. * refactor(runtime): the repo selector carries its host through every lookup Review follow-up (Codex + Opus on stablyai#31066): a separate repoHostId was dropped by the startup-agent lookup, the CLI's GitLab link check and the create dedupe key. RuntimeRepoSelector now carries the host with the selector, so showRepo/resolveRepoSelector and startup-agent resolution keep it; the create dedupe key includes it; repo.show gains the same optional repoHostId; the CLI spreads one CliRepoTarget into every repo lookup. Handler-level tests cover worktree.create (plain, startupAgent, startupDraft), agent.launch, repo.show and the dedupe key. * test(runtime): drop a needless cast in the agent.launch host test; fold an import
frbarbre
pushed a commit
that referenced
this pull request
Oct 21, 2026
…p ones (stablyai#31096) * feat(ci): ratchet raw repo connectionId host reads; one drop-in resolver Codex review 6 class fix (#4/#6/#7): host decisions read off a repo row's legacy connectionId miss rows that name SSH only as executionHostId. - getRepoRoutingConnectionId (shared/execution-host.ts): drop-in for a raw repo.connectionId routing check that also finds executionHostId-only SSH. - check-repo-host-read-ratchet: per-file count of raw repo connectionId reads in src/main, src/shared, src/cli; counts may only shrink. - Migrated 66 cheap reads: runtime GitHub/GitLab commands, PR/MR head ref fetches, preserved-branch cleanup, ref queries, base-ref IPC, worktree preparation, trash sweep, scan cache TTL, usage metadata. * fix(ipc): PR base IPC routes executionHostId-only SSH rows; ratchet is a stated lower bound Review follow-up (Opus on stablyai#31096): - worktrees:resolvePrBase decided git/gh routing on the raw connectionId while its head-ref fetches already used the resolver; one resolved connectionId now drives all of it (test fails on main). - getRepoRoutingConnectionId's contract is stated exactly: never local when the raw field is set; on a contradictory ssh row executionHostId wins. - The ratchet regex no longer matches report.connectionId; header and docs say it is a lower bound. - Regression tests for a runtime GitHub and GitLab command site. * test(ci): pin the repo host-read ratchet step to the static phase * fix(ci): repo host-read ratchet skips verbatim field copies into projected rows A copy of connectionId into a projected row decides nothing; migrating it to the resolver would change what the projection publishes. The sidebar feed catalog's copy (added after the baseline) and two older projections no longer count; baseline pruned to 91.
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 (
433986fa3b..2b0ce17514, 780 upstream commits, ten conflicts)package.jsonstill says 1.4.214 after 780 commits, so the branch name repeats a third time.src/main/index.ts,main-process-ipc-bootstrap.ts— upstream renamedos-opened-markdown-filesto
os-opened-documents(resolveOsOpenedDocuments,state.osOpenedDocuments). Took the rename,kept
armForkUpdateChannelafter the capture and every fork import in the bootstrap.updater/updater-download-install.ts— upstream addedisMacInstallRequested/setMacInstallPreflightInProgressto the import. Took it and kept the fork'sappimport; thefork's self-install and manual-install early returns auto-merged above upstream's new guards.
app-shell/use-app-shell-services.ts— both sides added a hook import. Kept both.editor/DiffViewer.tsx— upstream replacedresolveDocumentTheme(...)with theuseDocumentDarkTheme()hook (isDark), so the theme line is noweditorThemeName(isDark)(
MonacoEditor.tsxalready merged to the same). Upstream also moved the diff editor's disposecleanup into the guarded
modifiedEditor.onDidDispose; took that and added the fork'ssetPopover(null)to it, dropping the fork's olddiffEditor.onDidDisposecopy.editor/rich-markdown-extensions.ts— upstream reshaped the local-image load into a guardedcallback. Took it and re-attached the fork's
isGitHubAttachmentAssetUrlbranch after it.right-sidebar/FileExplorerFilesTreePane.tsx— upstream added imports and went toimport type React. Kept the fork'sReact, { useEffect }(the open-in selection effect) plusupstream's new imports.
ui/textarea.tsx— upstream moved variants ontocva(default,code). The fork'sseamlessis now a third
cvavariant. Nothing in the renderer passesseamlessany more (the composersbecame TipTap), so a later sync may drop it.
ui-slice-hydration-sanitizers.ts— both sides appended a function. Kept both.en.json— took upstream's, thensync:localization-catalogre-added the fork's 160 keys,identical to main's. That also needs
pnpm run sync:localization-runtime-catalog: upstream'sen-runtime-required.jsondid not cover five fork composer keys, andpnpm lintstops on it.DiffViewer.tsx,use-global-keybindings.ts,worktree-activation.ts,sidebar-workspace-option-items.ts): upstream grew each to within a fewlines of it. The fork's code there now lives in
useDiffViewerCommentPopover.ts,tab-group-focus/directional-group-focus-chord.ts,sidebar/temporary-checkout-reveal.tsandsidebar/fork-card-property-options.ts, leaving one-line calls in upstream's files. Upstream'sNotesSendMenunow takesformatPromptand notes shaped likeDiffCommentDeliverySnapshot, soInlinePRCommentCardpasses a snapshot of the thread's root comment. Andweb-preload-api-composition.test.tshad never listed the fork'ssettingsTransfernamespace.DiffViewer.word-wrap-lifecycle.test.tsxfails on the fork: the fork'suseInlinePRCommentActionsneeds aConfirmationDialogProviderthe test does not mount. Verifydoes not run upstream tests, so it is left as upstream wrote it.
lib/open-in-selection.tsand its explorer effect) isfork work not yet listed in What this fork owns.