Repository navigation
Phase 3.7: sync main into phase37 - #26828
Merged
Merged
Conversation
* ci: reduce unit runner demand and reuse the loaded preview frame * test: remove source spelling probes without behavioral assertions
…d is full (#26543) * fix(native-chat): never refuse a user's write because the retry record is full The record that lets a retried chat write or agent launch replay instead of running twice refused every new write once it held 512 ids for one caller or 4,096 in total. Background agent traffic fills that in a normal day, so the user's own message was refused with "too many requests in the last day". Remove the count limit from the durable record, the in-memory launch record and the SSH relay's launch record; each entry still lives exactly as long as the retry window it protects. Clients still understand the refusal from older hosts, and its message now says to update Orca on that computer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(native-chat): drop leftover cap wording and dead launch fallback; cover create records past the old cap * fix(native-chat): keep refusal and mailbox comments true after merge --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… and keep the queue from covering the chat (#26553) * fix(orchestration): keep automatic mail out of the user's message queue Orchestration mail reached a busy chat as ordinary queued message cards, and the queue refused at 20 unsettled cards or 1 MiB with a bare invalid refusal, so automatic notices could fill it and refuse the user's own next queued message. Mail now stays unread while the chat is working, waiting on a prompt or holding unsettled cards, and is coalesced into one delivery when it becomes free; batch identity is recorded so nothing is delivered twice. Human drafts no longer share an aggregate quota, and a failed settled receipt keeps its code, details and message for diagnosis. Co-Authored-By: OpenAI Codex gpt-6.1-sol <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(native-chat): never refuse a message because automatic mail filled the queue Drop the 20-card queue limit and the mail-delivery rework; keep only a byte bound on what queued cards publish (it rides every opening frame beside the history page), refused with a reason that says what to do. The desktop queue list now scrolls instead of squeezing the transcript. * fix(native-chat): keep room for the person's message in a queue other agents fill Other agents' cards stop one maximal message short of the queued-bytes bound, so background work alone can never refuse a person's send. The person's newly queued card scrolls into view, and the refusal's step and host message now name only what the person can do. * fix(native-chat): open a loaded queue on its next card and scroll before paint --------- Co-authored-by: OpenAI Codex gpt-6.1-sol <noreply@openai.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Native chat: give Grok, OpenCode and OMP chats inline visuals Each ACP agent loads the chat-visuals skill its own way for one launch, without touching the user's agent config: Grok through agent --plugin-dir, OpenCode 2.x through inline skills.paths, OMP through a PI_CONFIG_FILES overlay that keeps the user's own skill folders. A chat whose agent can't load it starts without visuals. * Native chat: load OMP chat visuals through --plugin-dir OMP's settings layers replace lists, so the PI_CONFIG_FILES overlay hid a repo's or profile's own skill folders in Orca's OMP chats, and its files were never removed. omp acp takes --plugin-dir like Grok, which adds the skill beside the user's own plugins and skill folders and leaves their config untouched. * Native chat: note OMP releases that skip --plugin-dir skills
…ore at once (#26361) * feat(native-chat): show each chat's resume as it happens, and start more at once Resuming interrupted chats sent one request for the whole batch, and the host answered only after every agent had replied, so "Resuming 21 chats" stayed up until the slowest chat answered and the dialog could only say "Resuming…" for every row. The host also started 3 agents at a time. The desktop app now sends one request per chat, so each chat's result lands as soon as it has one. The host keeps one start limit across all of those requests, sized to the machine's cores instead of a fixed 3. The status bar reads "Resuming chats 9/21". Reopening the dialog mid-run shows a gray spinner where each resuming chat's checkbox was, with its step (waiting to start, starting, waiting for a reply) and time in the tooltip, then a check or the failure row when it settles. A line above the list counts where the chats stand, with filters for In progress, Resumed and Need you. * fix(native-chat): publish restart progress from one host action * Keep chat resume progress stable through reply handoff * Fix restart progress ownership after skipped requests * Keep completed chat resume summaries until viewed and closed * chore(i18n): drop status-bar keys the merge restored
* Fix Mermaid diagram recovery after streaming replies * Clarify when the Mermaid render queue releases tasks
…se (#26538) * Preserve native chat skills and uploads when composers close * Own menu image pastes before saving begins * Keep paste ownership independent of image presentation * Show pending pastes so stalled saves can be removed * Register confirmed image pastes and preserve preview ownership * Keep clipboard image presence and saving on one source selector * Describe clipboard image formats in upload routing fixtures
…o Another Agent" (#26375) * feat(session-history): resume a native chat's conversation in a new CLI session Agent Session History offered "Resume in New Chat" to move a terminal session into native chat, but nothing went the other way: once a native chat owned a conversation, its row only reopened the chat, and Orca refused any terminal resume of it so two programs never write one conversation. Rows a native chat owns (Claude, Codex) now offer "Resume in New CLI". It forks: the terminal runs `claude --resume <id> --fork-session` or `codex fork <id>`, so the CLI gets the full history under a new id and the chat is unchanged. The host's resume guard lets a Claude fork through, and the fork tab records no provider session, so a restart never re-enters the chat's conversation. "Resume in New Chat" is renamed "Resume in New Native Chat". * fix(session-history): size the Resume in New CLI tooltip to its text The tooltip balanced one wrapped string inside a box sized before balancing, leaving a gap beside its lines. It is now two block lines with no fixed width. * feat(agent-sessions): rename "Continue in New Session" to "Hand Off to Another Agent" Agent Session History rows now offer "Resume in New Native Chat", "Resume in New CLI" and "Continue in New Session…", which read as three versions of one action. Only the first two carry the same conversation; the third starts a new session, often with another agent, seeded with a summary or the transcript. Rename it everywhere it appears (history sidebar, terminal pane header and menu, native chat menu, the dialog title) and, in the history row menus, move it below "Copy Resume Command" behind a separator so the Resume items read as one group. * fix(session-history): run a CLI fork under the selected Codex account and tighten the fork guard - Resume in New CLI now asks the host for the same Codex account home plain Resume uses, so a fork no longer starts under whichever account's copy of the rollout sorted first. The preparation call carries an optional `fork` flag that skips only the owned-conversation refusal; plain resume still refuses. - An older paired host that refuses the fork now shows "Update Orca on the host that runs this chat to resume it in a new CLI." instead of a raw error code. - The host guard admits `--fork-session` only before a `--` terminator and never alongside `--session-id`, since either shape lets Claude write the owned conversation. - Tests for the click path, the menu item's eligibility, the fork home preparation, and the tightened guard. * fix(session-history): read a Codex rollout's identity from its first session record A fork of an older (non-paginated) Codex thread writes its own session record first and then copies the parent's history, the parent's session record included. The history scanner kept the last one it saw, so the fork's row took the parent's id, title, folder and branch: on a native chat's fork it looked like the chat itself, and Resume on it opened the chat instead of the fork. Codex itself treats the first session record as the thread's own and ignores later ones; the scanner now does the same, as Orca's duplicate-rollout check already did. * fix(session-history): set up status hooks in the Codex home a CLI fork keeps When Resume in New CLI can't move a Codex fork to the selected account (that account has no copy of the conversation yet, or the system default is selected), the fork runs in the account home the history row came from. A new Codex tab only gets Orca's status hooks in the selected account's home, and the fork carries no provider session, so nothing set up the hooks in that row home. In a home only native chat had used, the fork tab could show no working or idle status. The fork's home preparation now readies that home the same way a resumed session's home is readied (hook install or refresh, plus the daemon-socket guard). That code is moved into prepareCodexPinnedLaunchHome and shared by both paths. The fork tab still carries no provider session, so it never reopens the chat's conversation on restart. * fix(session-history): ready only a host-verified Codex home for a CLI fork The fork's hook setup ran in whatever Codex home the request named, so a paired client could have Orca write hook and config files into any directory. Plain Resume only readies a home it has verified: one this host lists for Codex sessions that actually holds the rollout. The fork now uses that same check, through a shared trustedCodexResumeHomes list, and readies the home only when the verified home is the one the fork will run in. With no Codex runtime home service it does nothing, as plain Resume does. A failed check or setup is logged and never stops the fork from launching. Tests cover the untrusted home, a missing runtime home service, a rollout on another host, a WSL home, a fork moved into the real home, and a failed setup. * fix(session-history): show the host-update message when the desktop wraps the fork refusal When the local preparation step for Resume in New CLI is refused, Electron hands the renderer the error as "Error invoking remote method '…': Error: agent_session_conflict". The fork's failure text matched only the bare code, so the toast showed that raw line instead of asking the user to update Orca on the host. It now unwraps the message with the shared IPC error helper before matching. * test(agent-session-history): cover Hand Off placement and separator; rename Codex docs heading * fix(agent-session-history): drop the ellipsis from the Hand Off to Another Agent label
…nk menu (#26511) * Native chat: open tool-row files through the terminal's file link popover Tool rows that name a file (Read, Write, Edit, edit cards) now link the file name. Chat file links, in tool rows and in reply text, show the same popover the terminal shows for file paths, built from one shared function. * Fix native chat filename activation and path resolution
* Stop timing out slow profile-state writes; warn instead Profile-state writes used to fault after 30 seconds. A stalled main loop or a slow disk could then drop the writer and show a "could not confirm your save" dialog, even when the write was still in progress. Slow requests now record a diagnostic breadcrumb and keep waiting for the worker's reply. Close waits for the worker to actually exit, and a termination request alone no longer counts as proof. Pane-alias and migration-listener changes now save only their own sections, not a full state rebuild. Quit teardown also keeps the profile admission held until the final flush finishes, so a pending write is no longer released early at the deadline. * fix tests * Warn when profile saves run slow Slow profile-state writes used to leave only a diagnostic log entry, so users had no way to tell that recent changes were not yet confirmed saved. The writer now reports when a save write passes the slow-warning threshold and reports recovery when the reply arrives. Writes are never failed or retried because of the delay. - Main keeps the current delayed/healthy status and publishes changes to the renderer, so a reopened window gets the correct state. - The renderer shows one persistent warning toast while a save is delayed and dismisses it on recovery. - Observer errors are logged and do not interrupt saving. - The web build reports saving as healthy because it does not use the desktop profile writer. * Fix save-delay toast dismissal racing with a re-shown warning The save-delay warning used one fixed toast ID. Sonner applies a dismissal a frame late, so a dismissal could remove a warning that had just been shown again with the same ID. The hook now tracks the ID of each delayed-save warning it shows and dismisses only that toast. A new delay gets a fresh ID. Add Sonner-level tests for remount, StrictMode cleanup, and recover-then- delay races. Harden the e2e spec with a paused clock and a retry around main-process evaluate. * Report slow profile revision checks and exports as save delays Revision checks and JSON exports run on the same profile-state worker as saves, so a slow one holds up later writes just like a slow write does. The save-delay warning only tracked write-* commands, so those stalls went unreported. Every command except initialize and close now raises the warning. Tests cover the export-latest path and assert the delay signal fires once with true before clearing. * Clarify slow-save toast to name profile storage The delayed-save warning said recent changes were not yet confirmed saved, which overstated the risk while Orca was still retrying. The real condition is a slow profile-state write, so the toast now names profile storage and says later saves may wait until that operation finishes. The copy is updated in every locale, and the unit and e2e tests that assert it are updated to match.
* fix(omp): show exit 0 for a command that finished normally * fix(omp): require foreground timing before inferring success
…6638) Users with a saved Claude account were signed out of every Claude feature in Orca until they signed in again, and proxy setups (API key plus ANTHROPIC_BASE_URL) got 401s after signing in. Reverting before release to rework the upgrade path. The terminal daemon protocol moves forward to 43 so dev builds' v42 daemons with the claude function are not reused; v41 and v42 stay attachable. Nothing is deleted: per-account folders are left unused and the old saved-account store was never touched.
* refactor(native-chat): make structured chat the only Chat UI runtime Retire the experimentalStructuredNativeChat opt-in. Chat UI on is now the one gate for structured chat: new agent tabs that default to Chat UI start a structured session wherever the launch is supported, and unsupported launches keep falling back to terminal-backed chat. - Shared isNativeChatEnabled predicate replaces prefersStructuredNativeChatByDefault; host admission, renderer routing, orchestration mode, startup projection, tab sync and the resume offer all read it. - The retired key is stripped on load and on save, dropped from the settings type, defaults, the published client-settings projection and telemetry. - Settings no longer offer the structured toggle; resume-after-restart and shell-environment rows show under the Chat UI default view. - Startup structured-tab projection moves beside its sync hook so both gates read the same predicate. * docs(native-chat): describe structured chat as the Chat UI runtime New local Claude and Codex chats no longer sit on a PTY, so the page stops saying the terminal is always the source of truth and scopes the chat/terminal toggle to terminal-backed chats. * fix(native-chat): stop promising a Windows fallback in the Chat UI scope note A host refusal after the route picks structured chat still ends as a failed launch. * fix(native-chat): keep open structured chats reachable when Chat UI is off Turning Chat UI off hid open Claude/Codex chat tabs while their provider processes kept running, because the host and the desktop mirror gated serving existing sessions on the same setting that decides how new launches open. The host now asks two separate questions. Creating a new structured session (agentSession.create, createSupport, ensure) needs the negotiated capability plus the Chat UI setting. Serving a session that already exists (listing, reading, sending, resuming, closing, and the mobile projection and restore) needs the capability only. The desktop mirror, startup restore and resume-on-restart offer no longer read the setting, and the toggle-off teardown and the mobile re-publish on setting change are removed as dead. * fix(native-chat): run the local structured tab sync only where chats may exist The previous commit started the every-worktree session-tab census and subscription, the startup restore, and the resume-offer host read for every desktop user. Gate them on a derived fact instead: Chat UI is on, or the local host holds at least one structured session record. With Chat UI off nothing can create a session, so the answer can only go from yes to no. The host answers from its installed record store, or from whether the store file exists when the host is not installed, so a machine that never had a structured chat pays one existence check and never starts the host. Chat UI turning on starts the sync; turning it off never stops it. * fix(native-chat): answer an empty restart offer without starting the chat host With every Chat UI user now on structured chat, the launch-time resume offer read built the host even on machines with no stored chat, which on Linux scans every process environment. No store on disk means no offer, so the listing answers empty without installing the host. * fix(native-chat): release the structured tab sync when it never subscribed A failed capability probe reads as unsupported, so the sync returned without subscribing while still holding the one-sync slot. Nothing could start another, and chat tabs stopped live-updating until relaunch. The start now reports whether it subscribed, and a sync that did not releases the slot so the next Chat UI change retries. * fix(native-chat): let the host answer the restart offer whatever Chat UI says The renderer asked main whether a structured store existed before reading the restart offer, so it would not start the chat host for users without one. The host now answers an empty offer without starting itself, which made that probe an extra round trip. In paired web it was also wrong: the probe is hard-coded to no and the gate read the browser's own Chat UI setting, so a paired host's chats were never offered back once that was off. * refactor(native-chat): drop the structured tab sync generation fence The fence existed so a Chat UI toggle-off could retire in-flight inventory replies before it cleared the mirror. That teardown is gone: turning Chat UI off keeps open chats, and only a test reset still bumped the generation, so every production check compared equal. The sync's own disposal still fences its subscription, and the startup restore latch and publication cursors stay. * fix(native-chat): retry the structured tab sync when the capability probe fails A failed runtime status probe maps to an empty capability list, which the sync read as "unsupported" and gave up on. The release only let a later Chat UI change try again, so one transient startup failure left chat tabs without live updates for the whole session. The sync now tells an unknown answer apart from a host that answered without the surface, re-asks unknown answers on its existing subscribe backoff, and stops only on a definite no. * refactor(native-chat): begin single-switch structured launch routing * Make Chat UI the single structured launch default * Remove retired chat settings copy from localization catalogs * Keep locale checks aligned with the single Chat UI switch * Keep explicit chats accessible and older client defaults terminal * Align newly landed Settings tests with the Chat UI switch * Expect terminal fallback on older paired structured hosts * Correct terminal fallback launch expectation * Update tests that still expected the retired chat settings * Keep chat composer options off terminal launches and restore route coverage Chat composer model and effort no longer become CLI flags on terminal launches, since a new launch can no longer open in the terminal chat view; structured launches seed their own options. Restores dropped route coverage, including that an explicit resume-in-new-chat request stays available while Chat UI is off. * Replace the stale remembered-options test with terminal fallback coverage The test expected chat composer model and effort on a codex launch with Chat UI on, but its mocked host has no structured capability, so that launch is the terminal fallback that must not carry them. It now checks the fallback with Chat UI on and off, with shipped and stored arguments; structured creates are covered where the seed is applied. * Lower the terminal pane listener budget to the measured 15 The explicit-chat change retired the per-pane Chat UI setting read, so each mounted pane opens one fewer store listener. Pin the budget to the measured count and count that read among the saved listeners.
#26447 pushed src/main/ipc/app.ts one line over the 300-line limit, so main's oxlint is red and merges from main fail the commit hook. Move the keyboard input-source probe and its bounded command runner, unchanged, into macos-keyboard-input-source.ts beside the other macOS keyboard probes.
…26416) * feat(native-chat): host-owned startup attempt and hold-and-deliver barrier The host now mints one startup attempt before any adapter acquires (lease identity, spawn token, pinned launch context, cancellation, a 60 s deadline from the mint that activity never renews, option intent, and the event and child-work sinks), and the shared delivery loop hands nothing to a child still proving its start. A send is still accepted and journaled at once; readiness (the normalized `started` settlement) wakes delivery, which re-checks the child, fence, close and Stop before handover. A start that fails, crashes, or misses its deadline leaves held messages definitely unsent; a dispatch that throws after handover is `unknown`, never inferred unsent from the child's phase. * fix(native-chat): time agent starts from spawn by progress, and release Claude on initialize Review round 1 on the host-owned startup attempt. - The startup clock now starts when the process exists and fails a start only after 60 s with no stdout frame or stderr line, or 10 minutes after spawn however chatty it is. Every agent's child reports output through the one managed-process spawn, so a slow migration or history replay is no longer killed mid-way. Each start's spawn-to-outcome time is recorded as an `agentSession.startup` span for tuning. - Claude reports `started` on its initialize answer; the settings read follows and is applied and persisted as a later `options-reported` update, so messages no longer wait on it. - A started child's reported options are persisted in their own step after the held messages are handed over, and only if no pick changed the record's options since. - An attempt now ends whenever its child ends or an expiry stopped it. - Removed `optionsAsOf`, which was not an option revision, and comments that described the old publish-and-write-at-once behaviour. * fix(native-chat): keep the startup attempt's contract free of host and process code The adapter contract imported the attempt type from the module that also runs its clock and imports the host types, which pulled the model catalog and process-scan code into the mobile test typecheck. The attempt's shape and limits now live in a dependency-free contract module. * fix(native-chat): never let a stale option report undo a later pick A report of the child's options is now persisted only if no pick, no model-not-found heal and no newer report reached the chat since its read began. The host keeps one option revision per chat; the startup attempt hands the adapter a reader for it, and each report carries the revision it was read at. This replaces the record-snapshot check `started` had and covers the settings report, which had none, so a settings read answered just after a pick can no longer write the older model back. Also: the option read a ready child does inside its start now ends with the start (limit, Stop or close); a start that turns ready as the limit fires is recorded as ready; the startup-attempt host tests run under the Node SQLite runtime; and the retired-model test follows the new startup order. * fix(native-chat): require the option revision and order reports by arrival The host can no longer mint a startup attempt without the conversation's option revision, so no host path can stamp a report at a stale zero and drop every report after the first pick. Adapter tests may still omit it. A report is now dropped only when a pick or a value the child showed it cannot run came after its read. Reports are ordered among themselves by when the host takes them, and only the newest admitted one is persisted. The settings read's stamp therefore holds however late the host takes the start's report, instead of relying on that report being handled synchronously before the read begins. Tests: a settings read stamped before the host took the start's report is kept; a report read after a failed pick is persisted; the minted attempt's revision reader answers the host's current revision.
#26416 pushed structured-agent-session-host.ts one line over the 300-line limit, so main's oxlint is red and merges from main fail the commit hook. Move the pure knownAgentIds logic into structured-agent-session-provider-support.ts beside the other agent-support checks.
* Keep foreground Codex commands out of the background strip * Run the command-strip host regression in Node * Exercise retained commands after their source turns end
* fix(editor): route zoom gestures and app zoom to the PDF viewer Ctrl-wheel / trackpad pinch over a PDF scrolled it instead of zooming, and Cmd/Ctrl +/-/0 changed the (invisible) editor font zoom because the main process intercepts zoom shortcuts before the PDF's keydown listener runs. - The pdf.js session owns a non-passive ctrl-wheel listener for its container and zooms to an absolute scale anchored at the pointer. - App zoom commands on an editor tab are first offered to the PDF in the command-owning (focused) editor pane; unclaimed commands keep editor font zoom. - Wheel, toolbar and shortcut zoom all record the persisted scale. Co-authored-by: Infinity_X <Infinity_X@sinomo.net> * fix(editor): make pinch zoom track the trackpad more closely Halve the wheel-zoom divisor so pinch zoom in PDFs and images moves twice as fast, and halve the per-event delta cap so a single event still zooms less than 2x. Co-authored-by: Infinity_X <Infinity_X@sinomo.net> * test(editor): type the PDF ownership test's document fixture * fix(editor): keep fast pinch bursts anchored to the pointer pdf.js refreshes its saved location on the next frame, so a second wheel event in the same frame zoomed around the old location and drifted. Call viewer.update() after the anchor scroll. Also make the ownership test's reset assertion depend on reset running. --------- Co-authored-by: Infinity_X <Infinity_X@sinomo.net>
Radix restores focus to the trigger on close, so the model and effort pills kept it. Escape and Tab still land there; only a choice moves on.
…es (#26596) Placement only measured the gap below the target, so a tall target or a short pane sent the card to a side with no room and Radix slid it back over the selection.
…keep a streamed foreground tab live (#26657) * fix(window): re-hide a covered window when the renderer-throttle lease is released Restoring background throttling only clears Electron's disable_hidden flag; the occlusion or minimize hide swallowed while the lease was held is never replayed, so a covered window kept compositing at ~60 rAF/s until the next cover cycle. After the last release, a discarded empty-rect stayHidden capturePage makes Chromium's capturer-count decrement re-apply the current visibility, so the pending hide lands without the page ever reporting visible. Skipped for a focused renderer, best-effort and never awaited. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(window): let a broken re-hide capture surface instead of swallowing it Drops the synchronous try/catch around the stayHidden capture so a missing capturePage fails loudly; only the promise rejection is swallowed. Test doubles that reach a final lease release now provide capturePage. Keeps the focused-window skip (a covered window loses focus) and the empty rect. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(browser): keep a streamed active tab painting, and re-hide it when the stream ends When the streamed tab is the foreground desktop tab, the guest's own widget is what a cover hides, and lifting the window throttle does not reach it, so the phone froze after one or two frames. The page stream now re-applies the guest's setBackgroundThrottling(false) at start and on the window's hide/minimize while live. At stream end it drops those listeners, re-throttles the guest and replays its swallowed hide with the same stayHidden capture the window lease uses (now exported as replaySwallowedHide), so the guest stops painting. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(browser): hold the streamed guest through the renderer-throttle lease The page stream now takes a rendererPublicationThrottle lease on the guest instead of a bespoke guest-paint helper. The start unthrottle sets the guest widget's disable_hidden flag (copied onto new widgets), so the window hide/minimize re-apply listeners were no-ops and are gone. The guest gets the lease's end-of-stream re-throttle and stayHidden re-hide, including the focus skip, which holds for a guest because its isFocused requires the embedder's window to be key and its root view focused. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(runtime): give the mobile-session window doubles a capturePage Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
… instead of being refused (#25704) * fix(native-chat): a /compact sent while the agent works waits in line instead of being refused * test(native-chat): build the admission test's submissions without a type assertion * fix(native-chat): a /clear refused while the agent works says so plainly; a /compact behind an unsent message waits for it quietly * test(native-chat): name the admission test's cases for the sends they model * fix(native-chat): a /compact refused while the agent works reads plainly too; a command card offers no Edit * test(native-chat): a refused /compact reads the plain sentence * fix(native-chat): a refused command card is spent and said once; a command card offers Send while the agent is idle; /compact behind an unsent message is not taken * test(native-chat): keep the queued receipt's narrowing inside the wait * fix(native-chat): spend a command card only when its own turn says why; refuse a queued command without a hand-off, with its reason; a queue send on its way reads as sending * fix(native-chat): a recorded send is no sending card; queue lists land at once; a spent card's wait says to run it again; only /compact or /clear give way to returned text * refactor(native-chat): derive the sending cards inside the queued-messages hook * fix(native-chat): keep protocol-version and the agent-session params under the line limit; the send capabilities get their own module * fix(native-chat): with the agent idle, a command behind this window's own unsent message says it is still being sent * fix(native-chat): the queued-commands capability stays a plain constant beside the others; main's send capabilities back to its text * test(native-chat): keep the rewind suite under its line limit after main's growth * fix(native-chat): one import per module in the command send; a rewind on its way holds commands, tested * test(native-chat): type the rewind module mock with a type import * test(native-chat): a command card cut short by a restart waits or is kept like any card; kept-card captions name only controls shown * fix(native-chat): a held command card, which the queue skips, no longer forces later sends to queue * fix(native-chat): a command's refusal line goes once what it waits on is gone A refusal that names something the chat shows (the agent working, a pending prompt, background tasks, an unsent message) is said only while that still holds, derived each render, and dropped once it ends. Other composer errors are unchanged. * fix(mobile): a command's refusal banner goes once what it waits on is gone The phone's banner keeps a refusal naming the agent working or a pending prompt only while the chat still shows it, dropped each render once it ends. Background tasks, which the phone can't see, and generic errors behave as before. The host-reason mapping is shared with desktop. * fix(native-chat): a refusal behind an earlier message names whether it is being sent or offers Retry Each line now goes when its own state ends: a send that fails stops being sent, and a resend stops offering Retry. * test(native-chat): a Stop behind a typed /compact keeps the taken-back message on screen; the in-doubt case says so * fix(native-chat): a command refused while the queue is about to send reads working, as the pane does * Update held command fixture for the merged queue model * Restart one host at a time in the recovery ordering test * fix: preserve person queue reserve for chat commands
* Let automations pass extra agent arguments on each run Automations could only use the host's default agent Arguments, so a per-automation choice such as a different model needed a settings change that affected every run. Automations can now save extra arguments that are appended to the host's defaults on each fresh run. Extras are allowlisted to model, effort, and Claude's --add-dir, and are refused for permission, config, prompt, and session options. - Add the field through the CLI (`automations create/edit --extra-agent-args`), the RPC contract, the automation store, and the editor's Advanced section, with validation at save and at launch. - Merge extras over the defaults by replacing a model or effort option the defaults already set, then re-tokenize to confirm the launch argv matches what was validated. - Gate writes behind a new runtime capability, and refuse to save or migrate extras to a host that would silently drop them. - Require a fresh session, since reused sessions cannot take new arguments. * Simplify automation argument validation and launch handling * improve the extra args display * fix translation * Validate automation extra args during ORCA migration Dormant automations imported from the migration manifest skipped the extra-args check. A disallowed flag, or Reuse combined with extra args, could persist and then fail every later run. The manifest parser now rejects those rows. Also move the automation runtime capability constants into `automation-runtime-capabilities.ts`. `protocol-version.ts` re-exports them, so existing imports keep working. * add more demo with effort
* Recognize native chat sign-in failures across structured agents * Fix authentication failures and chat sign-in guidance * Preserve sign-in diagnostics and command retry context * fix(native-chat): keep visible sign-in guidance and localize startup rows * fix(native-chat): separate auth diagnostics from retry guidance * test(native-chat): align startup guidance expectations * fix(native-chat): start a new agent for the send after it reports not signed in * fix(native-chat): scope the signed-out check by fence and read without a snapshot * fix(native-chat): keep a signed-out agent while it owes work, and read only its own rows * fix(native-chat): keep a signed-out agent that may owe a wake-up turn, and skip the check during a Stop
* Recognize native chat sign-in failures across structured agents * Fix authentication failures and chat sign-in guidance * Preserve sign-in diagnostics and command retry context * fix(native-chat): keep visible sign-in guidance and localize startup rows * fix(native-chat): separate auth diagnostics from retry guidance * test(native-chat): align startup guidance expectations * fix(native-chat): treat OMP's signed-out 'No model selected' as not signed in * fix(native-chat): start a new agent for the send after it reports not signed in * fix(native-chat): scope the signed-out check by fence and read without a snapshot * fix(native-chat): keep a signed-out agent while it owes work, and read only its own rows * fix(native-chat): keep a signed-out agent that may owe a wake-up turn, and skip the check during a Stop
* Recover installed forge CLIs after launcher timeouts * Preserve healthy CLI timeouts and add native platform checks * Resolve Windows tree termination outside PATH * Use native absolute paths in the relative PATH fixture * Expect the absolute System32 taskkill in tree-kill tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…t glide when following (#26238) * feat(native-chat): pace and fade streamed reply text Snapshots land a clause at a time, 50-700 ms apart. Drawing behind them at an eased speed is what makes a reply read as flowing. * feat(native-chat): glide the transcript when following and jumping to latest The offset is still pinned at once; only what the pin moved is eased, so nothing races the virtualizer or a reply that keeps growing. * chore: list remend with the other bundled renderer packages dependencies is for packages the main process loads at runtime. * Bound streamed reply reveal and preserve chat interactions * Smooth overlapping chunks without postponing reveal deadlines * Fix first-reply following and pin settled layout changes * Pin concurrent media layout changes without a follow glide * Keep native chat layout stable when replies complete * Model delayed browser scroll events in navigation tests --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…e, clickable on the queued card too (#26357) * feat(native-chat): name an agent's message by its current title, and open it from the queued card The sender line on another agent's message and on its queued card now shows the name Orca shows for that agent right now (its sidebar agent row or chat tab name), falling back to the name recorded when it was sent. The queued card's names open the sender, as the transcript row's already do. * fix(orchestration): name a CLI agent sender by its tab's own name, not just its agent A terminal agent's recorded sender name used only a manual tab rename, else the agent type, so mail from CLI agents read as "Codex" or "Claude". The host now names every sender the way Orca shows it: a chat by its rename or saved name, a terminal agent by its sidebar agent row's name (rename, quick command, generated or meaningful live title; a split pane only by its own title). The task title and generic agent label are fallbacks. The renderer shows the live own name, else the recorded one. * fix(orchestration): preserve known sender names in message snapshots * Preserve sender names across chat clears and terminal reuse * fix: keep sender links on the recipient host across older runtimes * test: type sender naming fixtures with their declared identities * Fix sender ownership publication after clearing a chat * Drop /clear sender lineage now that /clear keeps the same conversation Main's #26579 keeps a native chat in the same session across /clear, so a message's sender id still names the live chat. Remove the host's orchestration-root projection, the optional status field, and the renderer's lineage lookup and feed capability probe that only served it.
* Start worktree.create's agent through the launch executor The host's worktree.create handler now starts a requested agent (startupAgent, or a linked startupDraft) through executeAgentLaunch instead of calling the create directly. Nothing on the wire changes: the request, the reply and agentTerminalHandle are the same, the agent is still the workspace's startup terminal whatever the chat default says, and the create still delivers the text itself (folded into the command at any length, a follow-up for agents that read it after start, and the draft pasted unsent). Adds three host-internal executor inputs: terminalOnly, the legacy-host prompt policy, and a shared entry (createWorktreeWithStartupAgent) that host producers can call in place of createManagedWorktree. * Keep a linked-draft create recording only the agent it actually started Problem: after worktree.create moved onto the launch executor, a create from a linked issue (phone "create from issue", or a raw worktree.create with startupDraft) could differ from before in two narrow cases. When the draft's agent came from the settings default or from detection and its launch command could not be built (for example unbalanced quotes in the saved agent arguments), the workspace was recorded as created with that agent even though it never started, and a background create opened no terminal at all instead of its plain shell. When no agent was found, detection ran twice (an extra SSH round trip on remote repos). User-facing change: none intended; both cases now behave exactly as before the executor move. Mechanism: - The entry hands the draft's chosen agent to the create as a main-internal startupDraftAgent, which steers only the draft; createdWithAgent stays what the caller sent. A draft that resolves no agent is created without the draft, so the create does not detect again. - AgentLaunchExecution is a discriminated union: every normal launch must bring its surface factory, and a legacy-host launch must be terminal-only, bring a workspace factory, and cannot bring surfaces. The runtime guards the types replace are gone; the target kind is checked before any create. - Legacy-host receipts follow the delivery module's rule: a blank prompt is no prompt, a submit folded into the launch command is handed-to-terminal, a post-start send the create does not await is unconfirmed (host-internal), a draft is not-delivered. - The no-agent error lives with the legacy-host policy and is caught only by the shared entry. - Tests: a real-runtime parity test (stored agent, terminal spawns, detection count vs the create alone, including the unbuildable plan), exact-argument checks over every non-agent startup field, compile-time checks for the union, and direct tests of the draft agent resolver. * Say which creates still bypass the shared agent entry Two comments stated that headless automations and federation already go through createWorktreeWithStartupAgent; both still call createManagedWorktree directly. Reword them as the plan. Comment-only.
* feat(runtime): default to bundled Node 24 and require modern hosts * Update SSH deployment expectations for Node 24 guidance * Expect Node 24 in Windows runtime installation guidance
…at always fits (#26664) * fix(native-chat): settle a dead agent's leftover work in one write that always fits A dead generation's cleanup was several separately committed writes, and a single leftover item larger than one batch row was rejected on every try, so the chat stayed stuck. Plan every row first and commit them in one transaction: bounded batch rows, an oversize item as an ordinary row with its full body, and the settlement's own send verdicts. Readers see it only after the commit. Settlement rows are rebuilt only from schema-known fields, address saved items by their stored key, never invent an end time, and new writes require safe revisions, so cleanup never builds a row it cannot save. * fix(native-chat): settle Codex exits in one write and keep turn fields a newer build saved - Codex's own exit cleanup hands the whole settlement to the journal's one-transaction planner instead of committing chunk by chunk. - One shared planner rejects an item named twice, on both the direct and resolved paths. - Rewriting a turn keeps fields this build does not know, as the journal schema promises; the legacy carrier's kind still cannot override it. - Drop the write-only revision admission and the planner skip that worked around it: insert admission is the reader's again, and a saved revision that cannot advance is skipped by the reducer while the rest settles, as before. - Remove two dead-generation journal members settlement no longer calls. * fix(native-chat): advance repeated items across a settlement's rows and keep nested turn fields Repeated Codex question ids in one write fold in order again instead of failing the write, and a saved turn's context usage keeps fields a newer build stored inside it. * test(native-chat): run the new Codex journal SQLite tests in the Node runtime project
* Guard desktop open actions with the target workspace owner * Wait for the folder catalog only in the folder menu exercise * Resolve the row owner only while its Open in menu is open --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…vidence (#26763) Enable the independent hang watchdog on Windows and preserve the previous run's marker until the startup trace sink is ready. Retain local consent, heartbeat timing, worker cleanup and bounded marker validation. Operation tracking is deferred. Adapt the startup-order regression coverage from community PR #26468. Co-authored-by: fanzzzd <fanzzzd@outlook.com>
Run Muse Windows hooks through the existing direct launcher on safe profile paths and refresh managed installations. Keep the PowerShell compatibility fallback for paths requiring special quoting. Verified on Windows; CI passed. Adapted the direct-launcher change from community PR #26734. Co-authored-by: Naveen Chatlapalli <naveen.aifanatic@gmail.com>
…26775) The host header lays out as a 32px card in 4px top padding (36px), but estimateRenderRowSize sized the row at 32px. The group tier's 36px pin offset then fired 4px early, so a project header could pin while its in-flow copy was still on screen — two headers at one coordinate. Size the host row 36px so it matches the pinned slot, and resolve each sticky handoff from the row's real start offset instead of only the lagging range start, so a header pins exactly when it reaches the slot.
* feat(grok): show Grok's subagents as subagent rows * fix(grok): retain live subagent roster ownership * fix(grok): retain child outcomes during shutdown * fix(grok): show live subagents in running task surfaces * fix(grok): settle owned child session completion notices * refactor(grok): fold child-session guards under the translator line limit
* fix(remote): retry failed SSH conversion catalog loads * fix(remote): keep the loaded catalog across ordinary disconnects * fix(remote): replay a reconnect that lands while a failed catalog read is pending --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…cutor (#26724) * Start worktree.create's agent through the launch executor The host's worktree.create handler now starts a requested agent (startupAgent, or a linked startupDraft) through executeAgentLaunch instead of calling the create directly. Nothing on the wire changes: the request, the reply and agentTerminalHandle are the same, the agent is still the workspace's startup terminal whatever the chat default says, and the create still delivers the text itself (folded into the command at any length, a follow-up for agents that read it after start, and the draft pasted unsent). Adds three host-internal executor inputs: terminalOnly, the legacy-host prompt policy, and a shared entry (createWorktreeWithStartupAgent) that host producers can call in place of createManagedWorktree. * Keep a linked-draft create recording only the agent it actually started Problem: after worktree.create moved onto the launch executor, a create from a linked issue (phone "create from issue", or a raw worktree.create with startupDraft) could differ from before in two narrow cases. When the draft's agent came from the settings default or from detection and its launch command could not be built (for example unbalanced quotes in the saved agent arguments), the workspace was recorded as created with that agent even though it never started, and a background create opened no terminal at all instead of its plain shell. When no agent was found, detection ran twice (an extra SSH round trip on remote repos). User-facing change: none intended; both cases now behave exactly as before the executor move. Mechanism: - The entry hands the draft's chosen agent to the create as a main-internal startupDraftAgent, which steers only the draft; createdWithAgent stays what the caller sent. A draft that resolves no agent is created without the draft, so the create does not detect again. - AgentLaunchExecution is a discriminated union: every normal launch must bring its surface factory, and a legacy-host launch must be terminal-only, bring a workspace factory, and cannot bring surfaces. The runtime guards the types replace are gone; the target kind is checked before any create. - Legacy-host receipts follow the delivery module's rule: a blank prompt is no prompt, a submit folded into the launch command is handed-to-terminal, a post-start send the create does not await is unconfirmed (host-internal), a draft is not-delivered. - The no-agent error lives with the legacy-host policy and is caught only by the shared entry. - Tests: a real-runtime parity test (stored agent, terminal spawns, detection count vs the create alone, including the unbuildable plan), exact-argument checks over every non-agent startup field, compile-time checks for the union, and direct tests of the draft agent resolver. * Say which creates still bypass the shared agent entry Two comments stated that headless automations and federation already go through createWorktreeWithStartupAgent; both still call createManagedWorktree directly. Reword them as the plan. Comment-only. * Start orchestration workers and remote workers through the launch executor A fresh worker (new worktree or existing, terminal or structured) and a federated worker on its receiving host now start their agent through executeAgentLaunch with the worker's own workspace and surface factories. The worker hands in the mode it decided before its dispatch record existed (decidedMode), so the host is asked once and the stored receipt is the one that ran. Effects, setup receipt, stage rows and their order are unchanged; a refused structured worker still fails rather than becoming a terminal. * Tighten the worker launch contract and discard a worker session a later launch step strands - decidedMode is now exclusive with terminalOnly and callerRendersStructured in the type, labelled temporary until one planner settles every launch. - connectionId's doc says what is true: the executor carries it and nothing reads it yet. - A worker launch that throws after its structured session exists discards that session itself, since the caller only tears down a returned placement. - A federated create refuses a launch with no startup agent and narrows repo/name before recording its first stage. - Tests: exact federated existing-path stages, a created-worktree host refusal that starts a worker terminal, and the stranded-session discard. * Say exactly which inputs a decided launch mode refuses, and pin the refusal at compile time
…#26665) Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(acp): start chats for agents that report no model * fix(acp): omit unknown models from resting session reads * test(acp): expect absent models for resting Claude chats * refactor(native-chat): move known agent discovery out of session host
* Preserve host Git identity cache when reimporting repositories * Keep the destination's automatic GitHub avatar when reimporting a renamed remote --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…ts (#26407) * feat(native-chat): one saved model catalog for every structured agent Every agent registration now declares how its models are listed without a session (a probe, or explicitly none). Probes share one supervised runner (15 s budget, bounded output, teardown on every path). Grok lists from initialize (or x.ai/models/list), OpenCode from models --verbose, Pi from --list-models; Claude moves onto the shared runner. ACP option reads no longer copy the running model's effort menu or selection onto every model. Desktop and phone pickers read the same catalog for every agent, with a non-pickable provider-default model pill before any list exists. * fix(native-chat): host names a listing's configured model; live listings saved for every agent - The catalog answer carries an optional listingNamesConfiguredModel field, set per registration (Codex, Grok) and per workspace; pickers name the listed default from it instead of a Codex-only client flag (older hosts fall back). - Grok, OpenCode and Pi live option reads save their model list through one host step, keeping the account's known default and per-model efforts. - Phone reads the host catalog like the desktop and seeds every agent. - Saved picks accept any known agent id (OpenCode, Pi). - ACP agents with no session-free listing register as unavailable; Grok's live effort menu falls back to the session's when the advertised one is empty. * fix(native-chat): discovery and live listings kept apart; every agent's live listing saved by the host; phone-created chats name their model * fix(native-chat): phone new-chat mark clears on the chat's first live answer; Claude's startup listing reaches the host catalog * fix(native-chat): live-listing type lives beside the catalog entry so wire types don't pull the store's persistence into the phone * fix(native-chat): the model picker never waits on a listing; a first listing lands in place * feat(native-chat): the host lists every agent's catalog ahead of use and traces each listing * feat(native-chat): a new Claude chat names the model and effort its config resolves to * test(native-chat): catalog service fakes gain prewarm * test(native-chat): prewarm span recorder without a type assertion * fix(native-chat): project agent config overrides the account default only when it sets a model or effort A committed .claude/settings.json for permissions or hooks, or the settings.local.json Claude writes on always-allow, no longer hides the configured Claude model in that project. Unreadable config still counts; Codex config.toml follows the same rule. * test(native-chat): the Codex picker read and the launch it starts resolve one account key, through the real resolvers Runs resolveHostCodexHomePathForLaunchReadOnly and prepareCodexRuntimeHomeForLaunch as the runtime service wires them, for the system account on the real home, on the shared runtime home, and a managed account. Also scopes the prewarm suite to agents in use. * fix(native-chat): Claude's configured model and effort are saved together behind the project check; prewarm lists only agents in use and stops with its host A live Claude chat no longer stamps its effort onto the account catalog; the effort travels with the configured default, which a no-pick chat whose config resolves to no listed model now forgets. Prewarm lists agents with a saved catalog or a chat, and host teardown (quit included) starts no queued listing and stops running ones through an abort signal the listing runners honor. * test(native-chat): new catalog tests use typed hoisted state and type-only namespace imports * test(native-chat): a prewarm probe sets and clears the sign-in verdict like a read's, and re-checks an aged one beside a fresh catalog * fix(native-chat): an aged sign-in verdict re-probes only from probe paths, not a Codex chat's picker The main merge put the held-verdict check into store.shouldRefresh, which also gates the live Codex picker. A chat's own listing never clears the verdict, so a signed-out Codex whose chat works re-listed model/list on every picker read. shouldRefresh is back to main's rule; the catalog read's probe and prewarm ask probeDue instead. * fix(native-chat): Claude project settings with an effort cap, a main-thread agent or an Anthropic cloud provider count as an override maxEffortLevel clamps every effort including the model default, and agent applies that agent's model (both from the bundled SDK settings type). CLAUDE_CODE_USE_ANTHROPIC_AWS and CLAUDE_CODE_USE_ANTHROPIC_GOOGLE_CLOUD sit in the SDK's provider-switch list beside Bedrock and Vertex. A project that sets only these no longer shows, or saves as the account default, the account's model or effort. * refactor(native-chat): the catalog store's picker waiters live in their own module No behavior change: pendingListing and the waiter notifications move as they were, which keeps the store under its line limit for the fixes that follow. * fix(native-chat): changing an agent's command or environment re-lists its catalogs and re-checks its notice The settings change called prewarm, but prewarm skipped any catalog still fresh (the catalog key is agent + account, not the command), and a held "not installed" verdict kept its 30-second window. The change now marks that agent's catalogs due and ages its held verdicts, so the prewarm it triggers re-lists them and re-checks the notice. Only agents whose command or env actually changed are touched. * test(native-chat): the sign-in notice tests check the model picker is really usable They checked only that a model entry existed, which a disabled or empty pill also passes. They now check it is settable, carries no disabled reason, and offers the catalog's or the seed's model. * fix(native-chat): a probe the host stops keeps the held notice reason, and a stopped prewarm reads nothing A probe aborted because the host is going away says nothing about the account, so it no longer replaces a held sign-in or not-installed reason with a plain failure. A prewarm after stop returns before reading chat records. * test(codex): the catalog account-key test uses the runtime's real Codex launch-home resolvers The resolvers move to codex-launch-preparation as one export the runtime service spreads, so the test runs exactly what the runtime is handed instead of a copy. * feat(native-chat): keep each host's saved model lists in the renderer A chat picker could only learn the host's saved model list by reading it after it mounted, so its first frame was a guess. The renderer now keeps the host's answers per host and agent: this machine's lists are read once its runtime answers, for the agents its chats were used with; a paired host's again when this client regains contact; and an account or launch setting change drops this machine's lists. Every entry is a host answer. * fix(native-chat): render the model pills once, from the host's list A new chat's composer painted its first frame from the built-in model list: a saved pick that list lacks (Claude's `opus[1m]`) showed as a raw id with no effort pill, then jumped to "Opus (1M context) · High" when the host's list landed. Every new chat painted a guess and then corrected it. Now the first frame reads the host's saved list the renderer already holds, so it is the final frame. Without one, the quiet "Model" placeholder stands until the host answers; a host with nothing saved makes the built-in list usable, naming nothing, until its listing lands. A saved pick is named only from a host or session list that names it. The phone follows the same rule and keeps the host's list across a new fence. * feat(native-chat): answer a saved-only model catalog read The app could preload a host's saved model lists only for agents with a saved pick, because the catalog read starts a listing (spawns the agent's CLI) when nothing is saved. Grok, OpenCode and OMP chats with no saved pick were never preloaded, so their pickers showed "Model" before the host's list. `agentSession.modelCatalog` now takes `savedOnly`: the host answers from what it saved (the entry, configured default and held reason) and starts, joins or re-checks no listing. Its params are strict, so the host advertises `agent-session.model-catalog.saved-only.v1` and clients send it only to hosts that do. An answer also says when its named default holds in every workspace (Grok reads no project config for its model), so a new chat in any workspace can use it on its first frame. OMP stays without a session-free listing: `omp models --json` lists every model without the `enabledModels` filter its chats apply. * fix(native-chat): preload every registered agent's saved model list Grok, OpenCode and OMP chats painted the "Model" placeholder, then the host's pills, because the app preloaded only agents with a saved pick. Now the app reads every agent a host registered through the saved-only read: this machine once its runtime and agents are known, a paired host at connect and on each reconnect, and this machine again after an account or agent-setting change. A host that does not advertise the saved-only read keeps the earlier rule (saved picks only; a paired host's answered agents). An agent the host saved nothing for stays unknown. An answer sent before the host's lists were dropped is not recorded. * test(native-chat): drop a type assertion from the snapshot sync test * fix(native-chat): learn an ACP agent's default from a chat started with no pick Grok's session-free listing named the model `initialize` computed (grok-4.7), but chats ran another (grok-4.6), so a new chat painted 4.7 and then jumped. The listing no longer names a default; a new ACP session with no model pick reports what it runs, and the host saves that as the account's configured default behind the workspace check. A catalog read waits for a recording still in flight, so the chat's own follow-up read names it. * feat(native-chat): check OpenCode and OMP project config before naming their default OpenCode reads opencode.json(c) and .opencode/ in every ancestor of the workspace; OMP merges its .omp settings and other agents' project settings. A workspace whose config sets a model, effort or provider key (or can't be parsed) names no default; any other lets a no-pick chat's model become the account's default, so the next new chat names it on its first frame. * fix(native-chat): re-read the host's list once a no-pick chat reports its model The renderer kept the answer it read before the chat started, so the next new chat's first frame still named no model. A new chat that named no model reads the host's list again once it reports what it runs; a default no workspace can replace also serves chats in other worktrees.
…one host operation (#26083) * feat(agent-launch): accept a create-folder-workspace target agent.launch can now create a folder workspace and start its agent there, the same operation it already runs for a new git worktree. The host creates the folder workspace (metadata only, no startup terminal), resolves it the way an existing target is resolved, then settles the mode and builds the surface as for any existing folder workspace. Hosts advertise agent.launch.create-folder-workspace.v1 so a client offers the target only to a host that accepts it. * fix(agent-launch): authorize, classify and navigate folder creates like their create method A create-folder-workspace launch could do three things its two-call equivalent never did: - A phone could create folder workspaces. The mobile allowlist admits agent.launch but not folderWorkspace.create, and is checked by method name only. A create target now needs the authorization of the create method it stands for, read from the same allowlist, and a phone is refused with agent_launch_target_forbidden before admission, so no ledger row and no create. create-worktree maps to worktree.create, which phones already may call, so nothing changes for it. - A create refused before anything was stored (group gone, folder missing, SSH host unreachable) replayed as agent_session_operation_unknown, and a retry under the same operation id was refused until the row expired. runtime.createFolderWorkspace now raises those refusals as FolderWorkspaceCreateRefusedError, with the same message text, and the launch records it as failed for any target. A failure after the create still replays unknown. - A paired caller's folder create activated the new chat on the host window and did not select the caller's own tab. Only create-worktree, whose host activation runs setup, keeps the host navigation now; a folder create navigates like an existing workspace. Tests cover the phone refusal, replay of a success, a refused create settling failed and replaying that failure, an after-create failure staying unknown, an SSH connection reaching the agent's view mode, paired navigation, and an older host refusing the target at parse. * fix(agent-launch): replay a refused folder create with its whole path A folder create refused before anything was stored is recorded as the launch's failure, and a retry under the same operation id answers from that record. The record went through the 128-character cap every launch failure code gets, and folder refusals carry the folder's absolute path, so a retry could show the user a path cut short: a different folder. The refusal is now recorded whole when it fits a path as long as Linux's PATH_MAX after its code. Past that, which no folder a stat accepts reaches, only the code before the path is kept, so the replay degrades to the bare code instead of a partial path. Also: the controller test asserts the exact refusal message, the create-folder-workspace capability comment says a phone needs its own gate, and the forbidden-target code says why it is not `forbidden`. * test(folder-workspace): type the controller test's store stub without an assertion The changed-code quality gate refuses the `as never` cast on the stub store. It is now a RuntimeStore whose unreached members throw. * test(agent-launch): run the folder-create launch tests in the Node runtime project The operationId replay tests open the real SQLite record store, so the file belongs with the other ledger-backed agent.launch tests. * test(agent-launch): pin that a paired folder create's terminal stays off the host window The folder-create test now asserts surfaceOwner: false on the terminal a paired caller's launch creates, rather than matching any options. The caller-selection header now says only an in-process worktree create keeps worktree.create's navigation. * test(agent-launch): drop the per-connection view-mode expectation main removed
…26823) This reverts commit 46460d4 (#26171). Pause the Node 24 requirement so existing SSH and WSL users are not surprised by a host-runtime upgrade requirement in the next release. This is a precautionary rollout hold requested to avoid near-term user disruption, not a cancellation of the Node 24 transition. Restore the prior Node 18 host minimum and companion build targets. The previously shipped bundled Node 24 runtime and compatibility launcher remain in place. Preserve the original change in a separate draft PR for reintroduction after compatibility and rollout validation.
…scard (#26677) Single and bulk discard quiesced editor autosaves for the globally focused runtime, so a managed repo's in-flight save could land after git restored the file and bring the discarded edits back. Use the repo-owner-routed settings the discard itself already uses.
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
# Conflicts: # src/main/ipc/runtime-environment-managed-tunnel.ts # src/renderer/src/components/right-sidebar/source-control-too-many-changes-banner.test.tsx # src/shared/wsl-login-shell-command.ts
Collaborator
Author
|
CI notes for this sync:
|
9 tasks done
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.
ELI5
Phase 3.7 has been waiting on its own branch while main moved ahead by 90 commits. This brings main into the Phase 3.7 branch so the landing PR shows only the Phase 3.7 work and is tested against current main.
What Changed
A merge of
origin/main(e43997c) intoOrcaWin/phase37(64d3f55). No new behavior. Three files conflicted, and a few more were changed on both sides and merged on their own. Each one keeps what both sides meant.Conflicts resolved by hand
src/main/ipc/runtime-environment-managed-tunnel.ts: keeps Phase 3.7's redeploy of a server another desktop stopped (redeployStoppedManagedOrcad,MANAGED_ORCAD_NOT_ACTIVATED_DETAIL,OrcadManagedServing,rebuildOrcadManagedTunnel). Also keeps main'sgetSshHostServerStatus, which backs the newclearManagedServerNotes.publishHostServerStatusnow takes a target id (main's version), and the Phase 3.7 callers type-check against it.src/shared/wsl-login-shell-command.ts: main reverted the per-account Claude folders (Revert Claude per-account folders (#24434, #26405) before release #26638), which removed the fishclaudefunction import. Phase 3.7's stdout fence import (buildPosixStdoutFence, HH-1) is kept.source-control-too-many-changes-banner.test.tsx: main movedTooManyChangesBannerintosource-control/listing/too-many-changes-banner.tsx. The test now imports it from there and keeps Phase 3.7'sSourceControlContentStatusimport for the Stage All cases. The moved banner keeps theonStageAllprop from fix(source-control): Stage All stages every change when the listing hit the 1,000 cap (HH-4) #26618.Changed on both sides, merged automatically and checked by hand
managed-orcad-actions.ts: main's "clear the stale update/serving notes after an update" and Phase 3.7'sforgetaction with the shared unlink policy both stay.orcad-managed-tunnel-manager.ts: main's superseded-tunnel check aftercheckServingand Phase 3.7'srebuild()sit in separate methods.runtime-environments.ts, the i18n catalogs,en-runtime-required.json,rpc-params-catalog.generated.ts,reliability-gates.jsonc: these are additions from both sides. The catalogs pass their verifiers.RPC permissions: the only new RPC method in Phase 3.7,
managedServer.forget, declarespermission: 'host-admin'.defineMethodrequires the field, and a scan of every method finds none without it.Why
A merge, not a rebase, because phase37 is a shared branch that already has squash-merged PRs on it. Bringing main in before the landing PR means the final review and CI run against what will actually ship.
Linked Issue
N/A (maintainer sync of an integration branch)
Visual Proof
N/A: a merge with no behavior change of its own.
Testing
Local, macOS, one worker:
pnpm install --frozen-lockfile,pnpm tc: passoxlint,pnpm run check:code-quality:changed(0 new findings across 224 files),pnpm run audit:anti-slop: passverify:rpc-params-catalog,verify:localization-catalog,verify:localization-runtime-catalog,verify:localization-extraction,verify:localization-coverage,check:max-lines-ratchet,check:reliability-gates,check:ts-nocheck-ratchet,check:runtime-electron-ratchet: passRuntime boundary tests (
vitest-node-runtime-boundary,vitest-runtime-boundary,vitest-sqlite-runtime-boundary,plain-node-entry-guard): 46/46 passEvery test file Phase 3.7 touched, plus the tests for the overlapping files: 99 files, 1069 tests pass. The mobile capped Stage All test passes 4/4.
check-runtime-launcher-protocol-ratchet --base origin/main: OK. Against the phase37 base it flags main's own protocol 43 bump and launcher change, which come in through this merge. They are not new in this PR.I manually tested these changes locally
Automated tests added/updated, or explained why not below (a merge; the existing tests cover it)
Review
Conflicts were resolved by hand, and each file changed on both sides was read.
Agent skill upstream boundary
Notes
SSH/remote: the conflicting code is the managed-server status line and the WSL login shell. Both sides' behavior is kept as described above.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)