Repository navigation
feat(source-control): start the AI buttons' agents through agent.launch (step 2 of 2) - #23748
Conversation
Review status: head 2228786, still a draft, not ready to merge yetVerdict. The review is clean at the pushed head. It went through six review loops (the last two only verified and came back clean), an architecture review of tab placement, a readiness check at the start and end, and live QA on macOS and Windows. CI is green at 2228786. Two things must happen before this merges; see "Before merge". What the review changed (all pushed except the last item)
Before merge
Live QA
Found, not caused by this PR
Accepted and disclosed in the body: the three lost-reply replays have no delay between them, and they never start a second agent. Worker start now needs name-only-title agents to go quiet. On a remote server, a chat is focused but not placed in the button's group. |
|
| settle: (result) => { | ||
| releaseGroup() | ||
| if (result?.outcome.kind === 'terminal') { | ||
| return |
There was a problem hiding this comment.
Paired launch loses target split
If a paired host's terminal launch reply arrives before the tab appears in the desktop mirror, settle() releases the reservation that keeps the button's empty split alive. Tab reconciliation can then remove that split. Placement also stops waiting after 10 seconds and forgets its record, so a delayed tab can land in a different group instead of the one the user launched from. Keep the reservation until the tab appears or the launch is known not to have created one.
There was a problem hiding this comment.
ℹ️ No critical issues — one scope note and a minor consistency question.
Reviewed changes
- Host owns prompt delivery for the source-control buttons. Fix checks, Resolve conflicts, commit/push-failure recovery and Explain commit now launch through
agent.launchReplay; the host pastes after readiness, and a prompt it cannot deliver produces a "prompt wasn't sent" notice with a Copy action rather than a blind paste. - Typed-line prompt-carry rule.
startup-line-prompt-carry.tsmeasures the quoted line (launcher + args included) and only lets the prompt ride it when it has no control byte and is ≤512 bytes; otherwise the agent starts clean and the surface reportspromptRodeLaunchCommand: falseso delivery pastes it. Hermes keeps its env transport and starts clean over budget. - One shared launch-readiness wait.
waitForLaunchedAgentComposernow serves bothagent.launchand local worker start; composer-marker agents (ZCode/DSH/Grok) use their captured marker, everyone else usestui-idlewithlaunchReadiness: true, and blocking dialogs are re-checked for up to 60 s. - Tab placement survives the reveal. Callers mint the pane, reserve the tab's group and focus intent, and the local reveal claims/consumes the reservation; reconciliation keeps a reserved empty group until the tab arrives.
- New capability + replay identity.
agent.launch.prompt-carry.v1gates prompted launches (older host → tab paste), and the desktop renderer gets a host-fixedtrustedLocalCallerIdso replay-protected launches have an identity.
ℹ️ The typed-line guard doesn't cover the prompt paths outside these buttons
The 512-byte/control-byte rule is applied only on the lanes this PR wires (agent.launch, and createManagedWorktree when the new callback is supplied). worktree.create --prompt (CLI) and headless automations still fold the prompt into the typed startup command with no post-start paste and no notice, so the failure mode this PR fixes — a long single line truncated at MAX_CANON, or a newline written raw on a shell without bracketed paste — is unchanged there. This looks deliberate and is pinned by runtime-worktree-agent-startup.test.ts ("keeps folding the prompt for a caller that delivers nothing afterwards"), so it may simply be out of scope; flagging it so the boundary is an explicit decision rather than an oversight.
Technical details
# The typed-line guard is not reached by CLI/automation prompt paths
## Affected sites
- `src/main/runtime/runtime-worktree-agent-startup.ts:163-171` — when `onPromptCarry` is absent, falls back to `buildAgentStartupPlan({ ...planInputs, prompt, ... })`, folding the prompt into `launchCommand` with no length/control-byte check.
- `src/main/runtime/rpc/methods/worktree.ts` (`buildManagedWorktreeCreateArgs`) / `src/cli/handlers/worktree.ts` — `orca worktree create --prompt` reaches that fallback with no callback.
- `src/main/automations/headless-workspace-create.ts:43` — `startupPrompt: automation.prompt`, same fallback.
- `src/main/runtime/orca-runtime-terminal-create-deduplication.ts:161-171` — automation into an existing workspace spawns a command built by `buildStartupForAgent` with no callback.
## Required outcome
- Confirm whether the buttons' path is the intended scope of the guard, or whether the CLI/automation prompt lanes should also stop folding an uncarriable prompt.
## Open questions for the human
- Is a follow-up planned to route these lanes through `planStartupWithPromptCandidate` (with a paste/notice owner), or is the fold behaviour there intentionally frozen for compatibility?DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (53)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review. 📝 WalkthroughWalkthroughThe change adds host-based source-control agent launches with replay protection and capability checks. Startup planning reports whether it carried a prompt on the launch command. When it did not, terminal delivery waits for agent readiness before sending the prompt. The renderer coordinates tab placement and prompt-delivery results across host and new-tab launch paths. The change also adds terminal view-mode support, launch-readiness handling, caller identity propagation, and translated notices for undelivered prompts. Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to Source-control agent launches now go through the host and paste prompts only after the agent is ready; if readiness is never detected, the prompt is not sent and the user is shown a notice to copy it. The only remaining known item is a potential inconsistency that no current caller reaches. This review found no current issue that should block merging. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to The change makes startup safer, but delayed automatic input is not consistently checked against current permissions and lifecycle state. This leaves a bounded risk on the selected computer. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 41.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 103 functions across 80 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Main now ships daemon protocol 38 for pushed host colours, so launch-line staging moves to 39 and 38 joins the attachable previous versions: a v38 daemon keeps its sessions and never gets a staged line it cannot run.
Review status at e8a19c7Verdict: code review clean; not ready to leave draft until the live re-run below passes. Five review loops plus two architecture reviews covered everything since the last clean review (6889642). The last loop found nothing to fix, and the closing release-readiness pass found no release blockers. Its one new finding is fixed in e8a19c7. What the review found and fixed (all pushed)The problem these fixes address: the host must paste a button's prompt only once the launched agent, not the shell that started it, is ready, and must never type it into a shell. The ownership logic added after 6889642 had several holes, each with a user-visible effect:
Every fix has a test that fails without it (checked by removing each guard). 29 named test files pass (1775 tests), and the node typecheck, oxlint and oxfmt pass. The PR body is updated to match the code and labels each difference from main as Intended or Temporary. What still needs doing before ready
Known limits, labelled Temporary in the body (each is the same as main)
|
Live QA re-run at 9b2ae19: three of the four "worse than main" items are fixed; the tab still appears later on a first launch and over SSHWhat was re-run. The final run at Result.
Open observation, not verified as a product issue: in macOS runs where a timestamping wrapper ( Windows: Qwen Code with the prompt submitted once and the input box empty afterwards (the error line comes from the fake endpoint the test used). macOS crash stub at 9b2ae19: nothing typed into zsh, and the Copy prompt notice is up. SSH: the Explain prompt submitted to Claude on the remote host. Build proof at 9b2ae19: the app reported branch Earlier: final live QA at 27d5943: prompts delivered once and never typed into a shell; four items worse than main, none lose a promptWhat was tested. This PR at Result. On every machine, every prompt the PR delivered arrived exactly once, and nothing was ever typed into a shell. Chat mode is not regressed. Four timing items are worse than main (below), but none of them loses or doubles a prompt. Three cases are better than main:
Worse than main (read first)
Results
Blocked or not run:
ScreenshotsmacOS, crash stub: zsh is clean after the stub's error, and the Copy prompt notice is up. macOS, a ready agent run from a bash wrapper that doesn't macOS, macOS, chat mode: the chat opens in the focused split with one user message. SSH: the 21-line commit-fix prompt submitted to Claude on the remote host. SSH, crash stub: nothing typed into the remote bash, and the Copy prompt notice is up. Windows, main: Claude is ready and its composer is still empty 90 s after the click. Windows, this PR: the Explain prompt is Claude's first message. Windows, this PR: the prompt pasted once into Qwen Code and left unsubmitted (item 4). How it was run.
Update: re-run at b57ed11 (launch paste bytes now match main)Why. At 9b2ae19, runs where a timestamping wrapper ( Result.
This PR, with the wrapper, after Claude's answer: mis-wrapped text and the stale footer. Main, with the wrapper, after Claude's answer: clean. Follow-up: same build without the wrapper. Explain changes with normal Claude (no
This PR, without the wrapper, after Claude's answer (runs 1 and 2): |
Review and QA historyThe full PR description as it stood at b57ed11 (2026-09-30), moved here so the PR body can stay short. It keeps the round-by-round history, the design alternatives, the per-row QA tables and every screenshot. The current summary is in the PR body; the latest live QA is in the status comment #23748 (comment). ELI5The problem. The source-control AI buttons generate long, multi-line prompts (a failing hook's output, CI log tails, a conflict list). They are moving onto the host's What this fixes and where. The desktop's source-control AI buttons (Fix checks, Resolve conflicts, commit/push-failure recovery, Explain commit) now start their agent through the host's The source-control AI buttons (Fix checks, Resolve conflicts and the other source-control actions, commit-failure recovery, Explain commit) used to start their agent by themselves in the desktop window and then paste the long prompt they generate. They now ask the Orca host to start the agent and deliver the prompt, the same way every other launch is moving to. The host is careful about long prompts: it never types them into your shell, and it only pastes once the agent is really ready. If the agent never shows it is ready, the prompt is not pasted anywhere; you get a message with a Copy prompt button instead. Terminal timing: final live run at 27d5943; its flagged rows fixed and re-run at 9b2ae19; paste bytes matched to main at b57ed11Final live run at 27d5943 (macOS, SSH to Ubuntu, Windows PowerShell; main at The first live run of this PR measured a terminal Claude getting its prompt about 6 seconds later than main: 8.4–10.8 s after the click, against 2.4–6.3 s on main. Main's buttons were not broken; this PR had made them slower.
Before/after: main vs this PR, terminal mode and native chatA live run on a separate Mac (2026-09-29): current main against this PR, the same four buttons, the same start state (Terminal 1 on the left, a focused empty split on the right), real Claude 2.1.285, once with native chat off (terminal mode) and once with structured native chat on. Every row that differed was run twice on both sides.
Same on both sides in every row: the prompt was the agent's first message, arrived exactly once, and was byte-identical between main and this PR. It was never on the agent's command line in terminal mode. Native chat was already working on main (the prompt goes in as the chat's first message and the chat takes focus), so for chat mode the bar is "nothing regressed". The chat rows are not affected by 19a0ff0, which changes only the terminal wait. Fixed in this PR since that run: a chat from a button left the focused empty split empty. The button reserved its tab group only under the terminal tab id it minted. A chat the host starts arrives through the session-tabs mirror (the renderer code that turns the host's list of tabs into window tabs) under a different id, Pre-existing, not caused by this PR: Fix checks from a focused empty split. On both main and this PR, in both modes, the window's focused group moves off the empty split about 0.1 s after the click, before either side reads which group to use. Fix checks then reveals its workspace before launching (the only button that does), and that reveal removes the empty split, so the agent opens next to Terminal 1. The reservation cannot help, because by then the focused group is already the other one. What moves the focus at the click is not yet known (the QA set the split's focus through app state only, which a real click might not reproduce). The earlier "split kept" results for Fix checks below are corrected accordingly. Intended: the success toast now waits for the prompt. "Started an AI agent for …" after commit-failure recovery and Fix checks now appears once the host reports the prompt delivered, not at the click. Main showed it before the prompt was sent, so it could claim success for a prompt that never arrived; this PR shows it only once it is true, and shows the Copy prompt notice instead when the prompt was not delivered. Measured at e6d62dd: the toast appears 0.05–0.19 s after the prompt, which itself arrives at or slightly before main's time. What ChangedBefore. Each button built a generated prompt (a failing hook's output, CI log tails, a conflict list), opened a tab itself, and pasted the prompt as soon as the agent turned on bracketed paste and drew its screen (2.4–6.3 s for Claude in the before/after run), waiting at most 8 seconds. If the agent had not shown it was ready by then, the desktop pasted anyway once the agent's process existed, readiness or not. Separately, the host's own launch command ( After, for the user.
Mechanism.
WhyThe goal is that every agent launch, terminal or chat, goes through the host's Alternatives considered:
Linked IssueN/A (internal agent-launch unification series). Visual ProofAll from live builds of this branch, driven through the app over the browser debugging protocol with an isolated home and agent config: macOS on a separate machine, and Windows 11 on a separate machine. The real Codex and Claude settings files on both machines were hashed before and after and did not change. These screenshots predate the timing and ownership changes; the delivery they show is unchanged, and the times in them are from before the timing fix. Commit-failure recovery launched from a focused, empty split (macOS): the Claude tab lands in that split, the 21-line prompt from the failing hook is pasted into Claude (not typed into the shell), and the success toast follows. Resolve conflicts (macOS): the generated prompt reaches Claude and the dialog closes after it. Fix checks from a focused empty split on a failing test PR (macOS), from an earlier run: the split was kept and the agent opened in it. The later before/after run did not reproduce this (0 of 2 on this PR, 0 of 2 on main); see "Pre-existing" above. Desktop connected to a remote Orca server: Explain commit from a focused empty split opens the tab in that split, focused, with no warning toast. Same desktop, Fix checks from a focused empty split: split kept, tab in it, prompt delivered on the server. An agent that never shows readiness: nothing is pasted, and the notice with Copy prompt appears at 60 s and is still up 90 s later. Windows, real Claude 2.1.284 through Explain changes: Claude's ready title reaches the host through the Windows console and the prompt is delivered at about 10.7 s. Windows, AI Fix from a focused empty split: tab in that split at 1.1 s, 21-line prompt pasted at 5.1 s, success toast after it. Testing
Automated (each new rule was removed once to show its test fails with an assertion, then restored):
Live QA, macOS (separate machine). The first rows are from 6889642, before the timing fix; their "about 8.5 s" times are the slow timing that "Terminal timing" above describes, and the current timing is in that section. The terminal wait rewritten at 19a0ff0 needs a re-run: the four buttons with Claude (and Codex and OpenCode if allowed), an agent that exits at startup, an agent that never enables bracketed paste, a quiet agent, a shell startup file that runs a program, and one orchestration worker start.
Live QA, Windows 11 (separate machine, build of this branch at aeb975a, before the ownership changes):
Not measured / not built:
ReviewCodex readiness: #23745 (merged, now in this branch) made Orca read Codex 0.157/0.158's live chat and stop at its startup dialogs. The button path's own wait is pinned against the captured 0.157 and 0.158 screens in Which agents get their prompt pasted: any agent that turns on bracketed paste gets it on the same signal main's desktop paste used, within the same budget, including those with no other readiness evidence in code (Trae, Prime Agent, Command Code, Droid, Autohand, Ante, Goose, Amp, Kilo, Kiro, Crush, Aug, Cline, Freebuff, Codebuff, Continue, Kimi, Mistral Vibe, Qwen Code, Rovo, Copilot, Aider, OpenClaw, OpenClaude, OpenCode 2, Mimo Code). An agent whose signal does not settle in its budget gets the Local orchestration worker start waits on Overlap with a planned PR (agent-status work, not yet open): it moves OpenCode and OpenCode 2 worker starts onto the composer-marker-only lane (#13911), fixes a seam bug in Found in QA or review, not caused by this PR: (1) on Windows, a short one-line prompt typed into a cmd pane gets stray Known limits: the three replays of a lost launch reply run back to back, so a dropped paired connection that is still down ends at "Couldn't confirm the agent started" (never a second agent). On a paired host, a chat the host starts is focused; the button's group is now reserved under the chat's id as well, which should put it in the button's group while the terminal placement wait (up to 10 s) is running, but this is covered by tests only, not verified live on a paired host; and a terminal the host takes longer than 10 s to create lands in the host's group (still focused). A new worktree whose setup must finish before its agent starts decides prompt carry before the setup wrap; nothing that ships sends a prompt with a create launch today. Agent skill upstream boundary
Notes
Checklist
|
| if (cleared?.ptyId === ptyId && !cleared.shellMayHaveReturned) { | ||
| return |
There was a problem hiding this comment.
Stale foreground check permits shell input
If an agent exits after the guard checks which process owns the pane, and the returning shell emits neither watched marker, the guard skips its next check. The launch prompt or a later Enter can then be written to the user's shell instead of the agent. A marker arriving during the initial check also does not trigger another check before the first write.
How this was verified: The guard reuses a negative foreground read unless it observes one of two output markers, while the prompt writer calls it immediately before writing to the PTY.
There was a problem hiding this comment.
ℹ️ No critical issues — one latent consistency note inline.
Reviewed changes — re-read the full diff at f564d7a8ea (55 commits, 105 files) since the earlier snapshot, focusing on the production paths and the commits landed after the prior read.
- Prompt carry decided on the built line —
src/shared/startup-line-prompt-carry.tsmeasures the quoted typed line and selects the clean plan when the text is multi-line, control-bearing, or over 512 bytes;agent-launch-executor.ts/surfaces reportpromptRodeLaunchCommandinstead of predicting it, and an uncarried prompt is pasted after readiness. - One launch readiness wait —
launched-agent-composer-readiness.tsis shared byagent.launchand worker start;launchReadinessholds a name-only OSC title to a quiet stream (tui-idle-evidence.ts,agent-title-status.ts), with atui-idlefallback through trust/update dialogs. - Shell-in-front guard —
launched-agent-shell-foreground.ts+launched-agent-write-guard.tsrefuse the paste/Enter only when a shell is proven in front, reusing the paste's read until a shell-return marker. - Byte parity with the desktop paste —
wrapTerminalBracketedPasteTextmoved tosrc/shared/terminal-bracketed-paste-text.ts; the host's launch prompt now sends the draft paste's exact bytes. - Replay hardening — the desktop renderer gets
trustedLocalCallerId, admit+claim collapses into one store transaction, andclassifyAgentLaunchReplayRefusalis shared with mobile. - Tab placement — reservations + placement + terminal-presentation bridge + chat mirror + reconciliation keep a button's launch in the focused group (including an empty split), with the reservation consumed/expired on every exit.
- Capability gate —
agent.launch.prompt-carry.v1routes older hosts to the existing tab-paste fallback.
I traced the hazards carried forward from the prior review and confirmed each is not a regression: direct-SSH worktrees correctly target { kind: 'local' } (the in-process main runtime is the host that spawns SSH PTYs, matching launchAgentInNewTab); the waitForWorktreeStartupDraft handoff bookkeeping cannot false-settle or permanently drop a signal; and the write guard's mark-less-shell Enter reuse is the narrow deviation the PR body already documents. The remaining inline note is a latent consistency gap with no current caller.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
The desktop's source-control AI buttons (Fix checks, Resolve conflicts, commit/push-failure recovery, Explain commit) opened a tab themselves and pasted their generated prompt. They now ask the host's agent.launch, which step 1 (#24257) taught to keep a long prompt off the launch line and paste it after readiness. The buttons record where their tab goes under every id the tab can arrive as, so the host's terminal reveal and the session-tabs mirror put the terminal or chat in the split the button was used from; an empty target group is kept until its tab arrives; a lost reply is replayed under the same operation id; and a prompt the host could not deliver shows a notice with Copy prompt. Rebuilt on step 1; the combined tree is identical to f564d7a (backup/23748-pre-split).
f564d7a to
5867ca6
Compare
… send paired hosts an explicit terminal view A refused or unconfirmed source-control launch showed the host's raw error text or a hard-coded English line in place of the translated failure toast. A paired host also got no viewMode for a terminal launch, so it applied its own default instead of this window's, as main's paired path avoids.
|
Review status (one pass after the split, 2026-09-30) No blockers. Fixed in 0ff385f:
Gates at 0ff385f:
The body is corrected:
Open, not fixed (design calls):
|
…kspace you left The four source-control buttons' agent tab appears when the host spawns its terminal, seconds after the click. The reveal forced focus, so a user who had switched to another workspace or an app view in that gap was pulled back to the launching one. It now follows #23974's rule for finished creates: still on the workspace, the tab is placed and focused as before; moved on, the tab joins its reserved group without switching workspaces, mounts in the background, and a toast offers Go to, which opens the workspace and focuses the tab. The in-view check and the Go-to toast are #23974's, shared rather than copied.
Re-run on the combined build 6171ba9 (#23748 + #24257 at e463aea), M4Air (zsh), 2026-10-01What changed. #24257's e463aea makes a local zsh host carry a multi-line prompt on the launch line, as main's Short answer.
Setup.
Screenshots (combined build). Explain changes: the 4-line prompt is Claude's first message in the right split, and the answer is complete: Commit failure → Fix with AI: the 198-line lint prompt is pasted once, and Claude is working in the right split: Safety.
Live QA: switching away right after a source-control AI launch, at 48e8dd7What was tested. #23748 moves the four source-control AI buttons onto the host's Short answer: PASS. You are never pulled back. The prompt arrives once. The toast appears, and Go to opens the workspace with the agent tab focused. Without a switch, the tab lands in the focused split as before. Build. Head re-checked with Method. Each run focuses an empty split in the launching workspace, commits, and clicks Fix commit failure with AI. One in-page script then clicks another workspace's sidebar row the way a user would: a pointer/mouse/click sequence, fired 20 ms after the Fix click so it lands before the tab exists. It then samples the active workspace, the agent tab's group, the focused element and the toasts every 50 ms for 10 s. Prompt evidence comes from the rig Claude transcript (user-message count) and zsh (a) Switch to a different workspace right after the click: 4 of 4 valid runs PASSTimes are ms after the Fix click.
(b) Same click, no switch: 2 of 2 PASS
Main control (1b0ee96, 1 run of (a))On main the button's agent tab exists before a user's switch can land: it was already there at +231 ms. So main does not pull you back in this flow either. When you leave, main says nothing about where the agent went beyond "Started an AI agent for the commit failure." I did not build #23748 without 48e8dd7, so I have not reproduced the pull-back itself here. These rows show only that the fix behaves as described. Worth knowing (not failures)
Screenshots (full window, right sidebar collapsed)(a) run a6, 1 s after the toast. You are on (a) run a6 after Go to: repo › main is active, with the Claude tab in the right split, focused and working on the delivered prompt: (b) run b2, no switch: the Claude tab is in the right split and focused, with only "Started an AI agent for the commit failure.": Main control at 10 s: you are still on Safety. The M4Air's real |
…rompt-delivery' into brennanb2025/desktop-action-buttons-agent-launch-2
…rompt-delivery' into brennanb2025/desktop-action-buttons-agent-launch-2
…rompt-delivery' into brennanb2025/desktop-action-buttons-agent-launch-2
…heir own module, as step 1 now requires
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — initial review of this PR's current scope: it is step 2 of 2, now branched on brennanb2025/agent-launch-host-prompt-delivery, so the 35-file diff is just the caller-side change (8368b0e).
- Four source-control buttons launch through the host — Fix checks, Resolve conflicts, commit/push-failure recovery and Explain commit call
agent.launchReplayvia the newlaunchSourceControlAgent, and fall back tolaunchAgentInNewTabonly when the host isunsupported. - Caller-side placement reservations — new
agent-launch-tab-reservations+agent-launch-tab-placementrecord the target group (and the chat focus intent) under every id the tab may arrive as; the terminal reveal claims/consumes it, the chat mirror reads it, and reconciliation keeps a reserved empty split alive. - Workspace-switch handling — a tab that appears after the user left keeps their place and offers a Go-to toast, reusing
workspace-go-to-toast/workspace-terminal-view, which the worktree-creation ready toast and creation-flow view check now share. - Result notices —
settleSourceControlAgentLaunchmaps the receipt tostarted/promptDelivered/failureNotified, with a persistent "prompt wasn't sent" Copy-prompt notice and a localized failure/unconfirmed toast.
I traced the reservation lifecycle (local and paired; reveal, reused-tab, reply, refusal and lost-reply exits) and the replay classifier against the host's agent.launchReplay ledger. No exit leaves live placement state keyed to another tab, and {kind:'failed'} is only returned where the host proved no effect — recoverable paired errors replay, agent_session_operation_unknown maps to unknown, and pane/session-already-live is failed on the first send but unknown after a replay, so a retry cannot silently start a second agent. Mixed-version, folder-workspace and SSH-to-local routing are covered. Nothing blocking; the remaining gaps are the temporary deviations the description already calls out (paired-server chat-vs-terminal follows the server's settings, and the paired Orca-server path is not QA'd live).
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
A launch line that is long or has several lines could lose lines or wedge a shell that is still starting, because it was typed into the terminal before the shell could read it whole. The terminal host (local daemon, and the SSH relay) now writes such a line to a script file and types one short line that runs it, for every POSIX shell; a shell that cannot read Orca's quoting runs Orca's own agent lines through /bin/sh. If the script cannot be written, the terminal says so. The SSH background launch path no longer types the line from the renderer; the relay stages it like any other terminal. Bumps the terminal daemon protocol to 40 (main took 39); a v39 daemon keeps its sessions and gets no staged line. Restacked onto #24257 from the version reviewed on top of #23748; carries none of #23748's changes.
A launch prompt for an agent whose CLI takes one could still be pasted into its screen once it looked ready: the host pasted any prompt whose typed line was over 512 bytes or held a newline (outside short-lined zsh), and the desktop's generated-prompt launches started the agent empty and pasted. A paste can lose text, land its Enter on a startup dialog (Codex's update prompt), or wait seconds for the agent to look ready. Where a launch prompt rides is now decided in one place, the startup-plan builder (carryLaunchPrompt), for every launch path: - on the agent's command line, which the previous step stages when the line is too long or multi-line to type; - in a private launch file the line points at, when the prompt is past 16,384 characters, sensitive, or one a Windows shell would damage (a control byte; on PowerShell a ", a %NAME% pair or a trailing \; past cmd's 8,191-character cap). The execution host writes it before typing the line, quoted for its shell, and grants Claude a read of its directory. A failed write refuses the spawn with a Copy-prompt notice; - pasted once the agent is ready only for an agent that takes text after start, a prompt needing a launch file its agent is not measured reading (only Claude and Codex are), a reused terminal, and a paired host, which is sent a command and never a file. This replaces the host's typed-line check (512 B, or zsh with every line <=512 B and <=8 KiB), which existed because nothing staged a long line. WSL writes its staged line and launch file into the distro and runs the line in the account's own login shell (sourced in bash, zsh and sh, eval'd in fish, through /bin/sh in ksh), with ~/.cache/orca made 0700 first. The "prompt wasn't sent" notice with Copy prompt lives here, shared by the new agent tab, background draft, paste timeout, work item and new workspace. Rebuilt on the restacked staging step; the earlier version sat on a base that carried #23748.
…hem after readiness (step 1 of 7) (#24257) * feat(agent-launch): host-side prompt delivery for agent.launch The host's agent.launch typed any launch prompt into the shell as part of the launch command. A long or multi-line prompt then ran line by line in the shell, and an agent that never showed readiness or crashed at startup had nothing guarding where its text went. agent.launch now carries a prompt on the typed line only when the line stays one line, control-free and at most 512 bytes; otherwise the agent starts clean and the host pastes the prompt once the agent's own ready signal fires (bracketed paste plus its composer marker or a quiet render, read only after the shell's last hand-off, never while the pane's own shell is proven in front), with main's draft-paste bytes and an Enter 50 ms later. Orchestration worker starts wait on tui-idle as before. A replay-safe launch admits and claims its ledger row in one write, Qwen Code gets a second Enter, the desktop and phone share one launch-refusal classifier, and hosts advertise agent.launch.prompt-carry.v1. Split out of #23748, which moves the desktop source-control buttons onto this path. * fix(agent-launch): keep a short-lined multi-line prompt on a local zsh launch line, as main did #24257 moved every multi-line or over-512-byte prompt off the typed launch line and pasted it after readiness. The phone's AI buttons and review notes, whose multi-line prompts main typed whole into zsh, then reached Claude 0.5-3 s later and their RPC reply waited for the paste. The host now names the shell a local macOS or Linux line is typed into, the way the spawn picks it, and a multi-line prompt rides a zsh line when every line is at most 512 bytes and the whole line at most 8 KB. A real-zsh test types such a line through Orca's own ready barrier and startup write, including when a slow user config makes the write land early. Elsewhere the measured unsafe cases keep the paste: bash 3.2 runs multi-line lines piecemeal, fish drops an early multi-line write, and any shell loses a line over 1 KB written early. * test(agent-launch): keep the real-zsh launch-line test out of the Windows lane's gate scan The Windows lane registration check read `const ZSH_PATH = process.platform === 'win32'` (the head of a multi-line ternary) as a Windows-true flag, so `describe.skipIf(!ZSH_PATH)` looked like a Windows-only suite. The file is POSIX-only; the zsh lookup is now a function. * refactor(protocol): move the agent.launch capabilities into their own module Main's protocol-version.ts sits at the 300-line cap, so the prompt-carry capability pushed it over. The four agent.launch capabilities and their doc move to agent-launch-runtime-capability.ts, re-exported by name and spread into RUNTIME_CAPABILITIES at the same position; the advertised lists and every export are unchanged. * refactor(protocol): import the agent.launch capabilities from their own module `export *` from protocol-version left the four names undefined under the mobile recording loader, which resolves a relative import through a Proxy with no own keys, so 37 phone recordings lost agent.launchReplay. Importers now name agent-launch-runtime-capability directly; protocol-version only spreads its list. * refactor(agent-launch): drop the unshipped viewMode field and trusted local caller id Both were inert in step 1 and existed only for step 2. agent.launch will become a public plugin API, so every wire field is permanent once shipped; a top-level viewMode reads as "choose terminal vs chat", which the host decides. Step 2 introduces placement and view intent under a placement object instead. * fix(agent-launch): read Codex's provisional startup from the rule files' hold anchor Main (#24375) moved Codex's provisional-header check into codex.json's provisional_startup hold anchor and deleted codex-terminal-readiness.ts, so the launch readiness hold now asks showsHoldAnchor, as main's own settled check does. * fix(agent-launch): hold rule-file name titles to quiet for a launch, and census the zsh fixture Main (#24375) answers a name-only title from each agent's rule file ahead of the sustained-title lane, so gemini.json's name_title settled a launch readiness wait on the shell's auto-title while Gemini was still booting. A launch now asks quiet of every weak idle verdict, as that lane did. Main's readiness census requires a recorder for every runtime fixture; the zsh prompt recording is a non-agent control. Gemini's synthetic baseline is regenerated for this PR's stated change: a bare gemini title is no longer its rest mark, so name-only rows settle weak, and a fresh working or blocked status is no longer overridden. * fix(agent-launch): paste a launch prompt only when the launched agent is proven in front A launch pasted its prompt unless a shell was proven in the terminal's foreground, so any read that could not prove one let the prompt through. After an agent exited at startup, its shell turned bracketed paste on at the next prompt, readiness fired on it, and the prompt was typed into the shell: - macOS: a pane runs its shell under login, so the process-group fence's root was never the shell's group and never proved it; the cached foreground name could also still name the exited process. - Windows Git Bash and WSL: the shell-alone-in-its-job check never answers. Now one fresh read of the terminal's foreground decides: agent, shell or unknown. Only 'agent' lets a write through (paste, Enter, second Enter, reused panes too); 'shell' still drops a ready signal. A Windows host never proves the agent, so there the launch line carries the prompt at any size, as on main. * test(agent-launch): cover the Windows QA stub, a grok override that exits at once * fix(agent-launch): keep the local socket alive while a prompted launch waits for its agent A launch with a prompt now waits up to 60 s for the terminal agent to be ready before it writes the prompt, and reports not-delivered when the agent never is. The local runtime socket closes a connection idle for 30 s unless the request is a long poll, so a launch whose agent exited at startup lost its reply and the caller saw 'runtime closed the connection' instead of not-delivered. Classify a prompted agent.launch and agent.launchReplay as a long poll, as orchestration.workerStart already is for the same wait. * refactor(agent-launch): narrow the launch params by 'in' instead of a cast * fix(agent-launch): find a launched agent behind a wrapper that leads its process group A tcsh or nu launch line runs the agent from /bin/sh '<script>', and a wrapper script that does not exec its agent does the same: the wrapper leads the terminal's foreground process group and the agent is a member of it. The fresh foreground read names the group's leader, sh, so a prompted launch was refused or pasted late (M4Air tcsh: 2 of 4 not delivered, 2 pasted ~9 s late). Before that read, take the host's process-group observation as positive proof when it names the launched agent among the foreground group's members and is younger than a ready signal's quiet window. It never proves a shell. * fix(agent-launch): judge the foreground-group proof by when its capture began, not how long ps took The age the host stamps on a process-group observation runs from the start of its whole-machine ps, so on a loaded Mac a capture begun after the read was asked for still read as older than 1 s and the proof was dropped. Count an observation whose capture began after the read was asked for, less the window a shared capture is reused across. * test(agent-launch): keep the crash-guard live test out of the Windows lane's gate scan The Windows-lane registration scan read the const assigned from a platform check as a Windows-only gate, though the suite runs everywhere but Windows; find zsh in a function instead, as the real-zsh typed-line test does. Under load the fresh foreground scan can fail to answer, which lets the shell's prompt settle readiness (2 of 4 paired runs). The guard still refuses that write, so assert the refused write, the property that must always hold. * perf(agent-launch): read a local pane's foreground from its own terminal, not the whole process table The foreground read that gates every launch paste ran the daemon's inspectProcess capture and then a fresh scan, each a whole-machine ps; the fresh one also waits for any capture already running before it starts its own. Measured here at load 5: 1.2 s a read (M4Air QA: 3.4-5.0 s, and worker starts 17.6-32 s against main's 9-12 s at load 25-84). On a local macOS or Linux host, take the pane's root pid from the provider's session inventory and run one ps limited to that pane's terminal. Its foreground process group decides: the launched agent or any non-shell member is the agent (a wrapper that did not exec its agent leads the group), a group of shells alone is the shell. Same pane, same verdict: 2.7 ms a read. SSH hosts keep the relay's observation and name. * test(mobile): re-measure the web app's script sweep after agent.launch's capabilities moved out of protocol-version The mobile web bundle check failed at 124 assets against a ceiling of 123. Main already sat exactly on that ceiling: its sweep table read 69 scripts at 16 routes while the tree builds 73, the whole margin of 4. This branch imports the agent.launch capabilities from their own module, so protocol-version is no longer pulled into the root layout and four other routes. That moves which routes share which modules, and the Qoder capability module, imported by protocol-version and the AI-vault resume path, no longer shares an importer set with anything, so it gets a chunk of its own: 74 scripts. The fence says to re-derive the bound rather than raise it, so the sweep is re-measured on this head (every prefix of the sorted route list). The worst route now adds 10 scripts (session), not 9, which moves the pinned shell crossing from 32 to 30 routes; main re-measured on its own lands on the same crossing. * fix(agent-launch): a worker's brief needs its agent found in front, and Grok's start answers on its composer A paired-server worker start whose agent exited at startup typed its brief into the server's shell, which ran it: the idle wait can settle on a shell back at its prompt, and the brief was written with no foreground read. Both worker-start paths now check before each brief write, as a launch prompt is checked: on a host that can find the agent in front it must be there; on one that cannot (Windows) a shell proven in front still refuses, and anything else writes as before. A Grok worker start waited ~10 s more than main: its only rest signal is its bare name, which a launch holds to quiet output, and Grok animates its logo for ten seconds after its composer glyph. A worker start for an agent whose rest signal is its bare name and whose composer draws a marker (Grok, DSH, mimo-code) now also answers on that marker, whichever comes first.
…ole (step 2 of 7) (#23962) * feat(agent-launch): host-side prompt delivery for agent.launch The host's agent.launch typed any launch prompt into the shell as part of the launch command. A long or multi-line prompt then ran line by line in the shell, and an agent that never showed readiness or crashed at startup had nothing guarding where its text went. agent.launch now carries a prompt on the typed line only when the line stays one line, control-free and at most 512 bytes; otherwise the agent starts clean and the host pastes the prompt once the agent's own ready signal fires (bracketed paste plus its composer marker or a quiet render, read only after the shell's last hand-off, never while the pane's own shell is proven in front), with main's draft-paste bytes and an Enter 50 ms later. Orchestration worker starts wait on tui-idle as before. A replay-safe launch admits and claims its ledger row in one write, Qwen Code gets a second Enter, the desktop and phone share one launch-refusal classifier, and hosts advertise agent.launch.prompt-carry.v1. Split out of #23748, which moves the desktop source-control buttons onto this path. * fix(agent-launch): keep a short-lined multi-line prompt on a local zsh launch line, as main did #24257 moved every multi-line or over-512-byte prompt off the typed launch line and pasted it after readiness. The phone's AI buttons and review notes, whose multi-line prompts main typed whole into zsh, then reached Claude 0.5-3 s later and their RPC reply waited for the paste. The host now names the shell a local macOS or Linux line is typed into, the way the spawn picks it, and a multi-line prompt rides a zsh line when every line is at most 512 bytes and the whole line at most 8 KB. A real-zsh test types such a line through Orca's own ready barrier and startup write, including when a slow user config makes the write land early. Elsewhere the measured unsafe cases keep the paste: bash 3.2 runs multi-line lines piecemeal, fish drops an early multi-line write, and any shell loses a line over 1 KB written early. * test(agent-launch): keep the real-zsh launch-line test out of the Windows lane's gate scan The Windows lane registration check read `const ZSH_PATH = process.platform === 'win32'` (the head of a multi-line ternary) as a Windows-true flag, so `describe.skipIf(!ZSH_PATH)` looked like a Windows-only suite. The file is POSIX-only; the zsh lookup is now a function. * refactor(protocol): move the agent.launch capabilities into their own module Main's protocol-version.ts sits at the 300-line cap, so the prompt-carry capability pushed it over. The four agent.launch capabilities and their doc move to agent-launch-runtime-capability.ts, re-exported by name and spread into RUNTIME_CAPABILITIES at the same position; the advertised lists and every export are unchanged. * refactor(protocol): import the agent.launch capabilities from their own module `export *` from protocol-version left the four names undefined under the mobile recording loader, which resolves a relative import through a Proxy with no own keys, so 37 phone recordings lost agent.launchReplay. Importers now name agent-launch-runtime-capability directly; protocol-version only spreads its list. * refactor(agent-launch): drop the unshipped viewMode field and trusted local caller id Both were inert in step 1 and existed only for step 2. agent.launch will become a public plugin API, so every wire field is permanent once shipped; a top-level viewMode reads as "choose terminal vs chat", which the host decides. Step 2 introduces placement and view intent under a placement object instead. * fix(agent-launch): read Codex's provisional startup from the rule files' hold anchor Main (#24375) moved Codex's provisional-header check into codex.json's provisional_startup hold anchor and deleted codex-terminal-readiness.ts, so the launch readiness hold now asks showsHoldAnchor, as main's own settled check does. * fix(agent-launch): hold rule-file name titles to quiet for a launch, and census the zsh fixture Main (#24375) answers a name-only title from each agent's rule file ahead of the sustained-title lane, so gemini.json's name_title settled a launch readiness wait on the shell's auto-title while Gemini was still booting. A launch now asks quiet of every weak idle verdict, as that lane did. Main's readiness census requires a recorder for every runtime fixture; the zsh prompt recording is a non-agent control. Gemini's synthetic baseline is regenerated for this PR's stated change: a bare gemini title is no longer its rest mark, so name-only rows settle weak, and a fresh working or blocked status is no longer overridden. * fix(launch): stage long or multi-line launch lines so they arrive whole A launch line that is long or has several lines could lose lines or wedge a shell that is still starting, because it was typed into the terminal before the shell could read it whole. The terminal host (local daemon, and the SSH relay) now writes such a line to a script file and types one short line that runs it, for every POSIX shell; a shell that cannot read Orca's quoting runs Orca's own agent lines through /bin/sh. If the script cannot be written, the terminal says so. The SSH background launch path no longer types the line from the renderer; the relay stages it like any other terminal. Bumps the terminal daemon protocol to 40 (main took 39); a v39 daemon keeps its sessions and gets no staged line. Restacked onto #24257 from the version reviewed on top of #23748; carries none of #23748's changes. * fix(launch): run a staged launch line as its own job in fish and ksh fish and ksh run the commands of a sourced file in the shell's own process group, so a staged agent was not a job: Ctrl-Z was dropped (a typed line stops), and the shell-in-front check saw the shell's group in the foreground while the agent ran. fish now runs the script with `eval (string collect < '<path>')`, which runs it as if typed and leaves the shell's job-control mode alone. ksh and mksh run a long Orca-built line through `/bin/sh '<path>'`, a real job; a long command the user wrote is typed as before. The real-shell test now asserts the agent's process group is its own and is the terminal's foreground group. The staging rule also reuses hasControlByte and the 512-byte budget from startup-line-prompt-carry so the carry and staging limits cannot drift. * fix(agent-launch): paste a launch prompt only when the launched agent is proven in front A launch pasted its prompt unless a shell was proven in the terminal's foreground, so any read that could not prove one let the prompt through. After an agent exited at startup, its shell turned bracketed paste on at the next prompt, readiness fired on it, and the prompt was typed into the shell: - macOS: a pane runs its shell under login, so the process-group fence's root was never the shell's group and never proved it; the cached foreground name could also still name the exited process. - Windows Git Bash and WSL: the shell-alone-in-its-job check never answers. Now one fresh read of the terminal's foreground decides: agent, shell or unknown. Only 'agent' lets a write through (paste, Enter, second Enter, reused panes too); 'shell' still drops a ready signal. A Windows host never proves the agent, so there the launch line carries the prompt at any size, as on main. * test(agent-launch): cover the Windows QA stub, a grok override that exits at once * fix(agent-launch): keep the local socket alive while a prompted launch waits for its agent A launch with a prompt now waits up to 60 s for the terminal agent to be ready before it writes the prompt, and reports not-delivered when the agent never is. The local runtime socket closes a connection idle for 30 s unless the request is a long poll, so a launch whose agent exited at startup lost its reply and the caller saw 'runtime closed the connection' instead of not-delivered. Classify a prompted agent.launch and agent.launchReplay as a long poll, as orchestration.workerStart already is for the same wait. * refactor(agent-launch): narrow the launch params by 'in' instead of a cast * fix(agent-launch): find a launched agent behind a wrapper that leads its process group A tcsh or nu launch line runs the agent from /bin/sh '<script>', and a wrapper script that does not exec its agent does the same: the wrapper leads the terminal's foreground process group and the agent is a member of it. The fresh foreground read names the group's leader, sh, so a prompted launch was refused or pasted late (M4Air tcsh: 2 of 4 not delivered, 2 pasted ~9 s late). Before that read, take the host's process-group observation as positive proof when it names the launched agent among the foreground group's members and is younger than a ready signal's quiet window. It never proves a shell. * fix(launch): type a plain Orca agent line as is in tcsh and nu In tcsh and nu, every Orca-built agent line ran through `/bin/sh '<script>'`, so the agent was a child of sh in sh's process group. When the prompt was pasted after the agent was ready, the paste guard read sh in front and, on a loaded Mac, reported the prompt not delivered (4/4 live runs in tcsh). Now only a line that needs it goes through `/bin/sh`: one over 512 bytes, with a control byte, or with a character those shells would not read literally (`!`, backslash, `"`, `$`, backtick). A plain line such as `claude '--dangerously-skip-permissions'` is typed as on main, so the agent is the shell's own job. * fix(agent-launch): judge the foreground-group proof by when its capture began, not how long ps took The age the host stamps on a process-group observation runs from the start of its whole-machine ps, so on a loaded Mac a capture begun after the read was asked for still read as older than 1 s and the proof was dropped. Count an observation whose capture began after the read was asked for, less the window a shared capture is reused across. * test(agent-launch): keep the crash-guard live test out of the Windows lane's gate scan The Windows-lane registration scan read the const assigned from a platform check as a Windows-only gate, though the suite runs everywhere but Windows; find zsh in a function instead, as the real-zsh typed-line test does. Under load the fresh foreground scan can fail to answer, which lets the shell's prompt settle readiness (2 of 4 paired runs). The guard still refuses that write, so assert the refused write, the property that must always hold. * perf(agent-launch): read a local pane's foreground from its own terminal, not the whole process table The foreground read that gates every launch paste ran the daemon's inspectProcess capture and then a fresh scan, each a whole-machine ps; the fresh one also waits for any capture already running before it starts its own. Measured here at load 5: 1.2 s a read (M4Air QA: 3.4-5.0 s, and worker starts 17.6-32 s against main's 9-12 s at load 25-84). On a local macOS or Linux host, take the pane's root pid from the provider's session inventory and run one ps limited to that pane's terminal. Its foreground process group decides: the launched agent or any non-shell member is the agent (a wrapper that did not exec its agent leads the group), a group of shells alone is the shell. Same pane, same verdict: 2.7 ms a read. SSH hosts keep the relay's observation and name. * test(mobile): re-measure the web app's script sweep after agent.launch's capabilities moved out of protocol-version The mobile web bundle check failed at 124 assets against a ceiling of 123. Main already sat exactly on that ceiling: its sweep table read 69 scripts at 16 routes while the tree builds 73, the whole margin of 4. This branch imports the agent.launch capabilities from their own module, so protocol-version is no longer pulled into the root layout and four other routes. That moves which routes share which modules, and the Qoder capability module, imported by protocol-version and the AI-vault resume path, no longer shares an importer set with anything, so it gets a chunk of its own: 74 scripts. The fence says to re-derive the bound rather than raise it, so the sweep is re-measured on this head (every prefix of the sorted route list). The worst route now adds 10 scripts (session), not 9, which moves the pinned shell crossing from 32 to 30 routes; main re-measured on its own lands on the same crossing. * fix(agent-launch): a worker's brief needs its agent found in front, and Grok's start answers on its composer A paired-server worker start whose agent exited at startup typed its brief into the server's shell, which ran it: the idle wait can settle on a shell back at its prompt, and the brief was written with no foreground read. Both worker-start paths now check before each brief write, as a launch prompt is checked: on a host that can find the agent in front it must be there; on one that cannot (Windows) a shell proven in front still refuses, and anything else writes as before. A Grok worker start waited ~10 s more than main: its only rest signal is its bare name, which a launch holds to quiet output, and Grok animates its logo for ten seconds after its composer glyph. A worker start for an agent whose rest signal is its bare name and whose composer draws a marker (Grok, DSH, mimo-code) now also answers on that marker, whichever comes first. * test(startup-staging): expect the CR that submits a typed launch line since #23672
|
Closing as superseded: a merged PR already covers this change. Merged #25624 (47f4d3f) body: 'supersedes #23748, which routed the same buttons with its own tab reservations'; desktop AI buttons now start agents through agent.launch. #23962 also notes it no longer carries (Stale-PR cleanup 2026-10-20, approved by Brennan. Reopen if something here is still missing on main.) |
































ELI5
The problem. The desktop's source-control AI buttons (Fix checks, Resolve conflicts, commit/push-failure recovery, Explain commit) opened their agent tab themselves and pasted their long, generated prompt into it. On main that paste went in blind after 8 s if the agent never looked ready, and was typed into the shell if the agent crashed at startup. Every launch is moving onto the host's
agent.launch, and these buttons were the last ones pasting on their own.What this changes. The four buttons now start their agent through
agent.launch, which step 1 taught to keep a prompt on the command line only where Orca measured that the typed line arrives whole, and otherwise to paste it after readiness. The tab still opens in the split you used the button from, focused; the prompt arrives once, submitted, at main's timing; and a prompt the host could not deliver shows a notice with Copy prompt instead of being typed anywhere.The plan, and where this PR fits. This PR is step 2 of 2. Step 1, #24257, changes the host's
agent.launchpath and the shared launch code (prompt carry, readiness on the agent's own signal, the shell checks, main's paste bytes, the one-write ledger, Qwen Code's second Enter). This PR moves the desktop buttons onto it and places their tabs. After that, #23962 → #24080 → #24081 put long prompts for Claude and Codex on the agent's own command line through a staged launch file.What Changed
Terminal mode. The agent tab opens in the tab group you used the button from, focused. If you moved to another workspace or an app view before the tab appeared, you stay where you are: the tab joins that group without switching workspaces, and a toast " started in " offers Go to, which opens the workspace and focuses the tab. This is the rule #23974 set for a finished workspace create, and it reuses that check and toast. On a local zsh host (the macOS default), a prompt whose lines are each 512 bytes or shorter, up to 8 KB, rides the agent's launch line, so the agent starts with it. That rule is step 1's, added after the QA below, and has not been QA'd through these buttons yet. On the phone it was re-run on step 1: review notes now match main. The phone's commit-failure Fix still pastes, because its hook-output line is 2,163 bytes, over the 512-byte per-line size measured safe. That is Intended, per the measurement, and #24080 removes the delay. The desktop's commit-failure recovery also carries hook output, so a line over 512 bytes there pastes the same way. Any other prompt is pasted once, submitted, about 1.6 s after the agent looks ready, as on main. If the agent never looks ready within 60 s, or exits at startup, nothing is typed anywhere and a notice offers Copy prompt. The Fix checks and commit-failure spinner and success toast now follow delivery.
Chat mode. With a native-chat default for Claude or Codex, the chat opens in the split you used, focused, with the prompt as its first message, as on main.
Latest live QA of the buttons, main vs this PR on top of #24257 (the QA'd build is their combined tree; QA status comment):
Every run: one message, submitted, byte-identical to main. The tab rows are the one gap left.
How it works now
agent.launchReplay(the replay-safe form ofagent.launch) with the pane id it minted (and, for a native chat, the chat session id), after checking the host advertises bothagent.launch.replay-required.v1andagent.launch.prompt-carry.v1; an older host keeps today's tab paste. A paired host always gets an explicitviewMode(terminalwhen the button would open a terminal), as main's paired path does, so it never applies its own default.Why. The host already owns the agent, its terminal and its readiness, so delivery belongs there; the buttons only decide where the tab goes, which only the window can know.
Deviations from main
viewModeonly picks the terminal's view). Follow-up not filed yet.Known issues, the same as main
Linked Issue
N/A (internal agent-launch unification series).
Visual Proof
Commit-failure recovery from a focused empty split (macOS): the tab lands in the split and the 21-line prompt goes into Claude, not the shell.
Chat mode, Explain changes (macOS): the chat opens in the focused split with the prompt as its one user message.
An agent that exits at startup (macOS): zsh is clean and the not-sent notice with Copy prompt is up.
History, alternatives, full QA tables and more screenshots: Review and QA history.
Testing
I manually tested these changes (main vs this PR on macOS, SSH to Ubuntu and Windows; QA status comment)
Automated tests added/updated
The combined tree of feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 and this PR is identical to the QA'd head f564d7a (
git diff f564d7a8ea7 5867ca69592is empty; tagbackup/23748-pre-split), where CI passed in full (unit shards 1–5, packaging, cross-version wire, typecheck, static analysis).This PR's own checks against feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257: the changed-code quality gate, the three localization checks, and its 12 renderer test files (137 tests) pass.
At 8368b0e, after merging step 1 at 8dc5516, this PR's two files import the
agent.launchcapabilities from step 1's new module. Gates: node and web typechecks, oxlint, the changed-code quality gate against feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257, and 25 test files (209 tests, includingwin32-test-lane-registration.test.mjs) pass.After merging step 1 again (merge f21abf8; step 1 had merged main): node and web typechecks, oxlint, the changed-code quality gate against feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257, and 25 test files (209 tests, including
win32-test-lane-registration.test.mjs) pass.After merging step 1's zsh launch-line rule (merge 6171ba9): node and web typechecks, the changed-code quality gate against feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257, and the same 24 test files (178 tests) pass.
After the workspace-switch fix at 48e8dd7: node and web typechecks, oxlint, React Doctor and the changed-code quality gate against feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257, the localization checks, and 24 test files (178 tests: this PR's,
runSourceControlAgentActionStart.test.tsand fix(worktrees): a finished create no longer pulls you off the workspace you switched to #23974's create-flow tests) pass. The four new reveal and toast tests fail without the fix. Not QA'd live yet.After the review fix at 0ff385f (translated failure toasts, explicit paired
viewMode): node and web typechecks, oxlint, the changed-code quality gate against feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257, the localization checks, and this PR's 10 test files plusrunSourceControlAgentActionStart.test.ts(11 files, 103 tests) pass. The combined tree is no longer identical to the QA'd head; the differences are only these two fixes.The paired remote Orca server path (not SSH) has not been QA'd live.
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
viewModeis sent only to hosts advertising the capability. Folder workspaces work as worktrees do.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)