Repository navigation
feat(agent-launch): the desktop AI buttons start their agent through agent.launch - #25624
Conversation
…top-buttons An early tab's pane now attaches when its agent runs, not after its prompt lands (QA F1 on #25624).
Where this PR stands (head de6044a)The problem. The desktop's AI buttons (Fix checks, Resolve conflicts and the other source-control actions, commit/push-failure recovery, Explain commit, notes and annotation sends) started their agent from the window. The window planned the agent's launch line, pasted the prompt when the agent looked ready, and ran each button's follow-up (resolve threads, post replies, mark notes sent, "Started an AI agent…") on its own guess that the prompt landed. Under load main left long prompts unsubmitted, it typed a crashed agent's prompt into zsh, and on Windows it never delivered Claude's prompt. What changes for you. Each button still opens its tab at the click, in the split you used. The machine that runs the agent now starts it and delivers the prompt once, and the button's follow-up runs only once the agent has the prompt. If the agent exits at startup, you see "The agent exited at startup, before it received your prompt." with Copy prompt, and nothing is ever typed into a shell. Live QA, main vs this PR (4 rounds plus spot checks, scripted hidden rigs on a Mac and a Windows PC):
What QA and review found, and what was fixed:
Deferred, known (none worse than main unless stated):
Found, pre-existing on main, not this PR:
Not verified:
Base update. After this comment, #25430's final head (8f62858: the phone landing race and the tab-closed wording) was merged in. That put CI. Run 37481054788: every job is green except the cross-version wire job (and the |
… written when the surface exists Step 1 of the agent-launch unification, on main. - The dispatcher stamps every request's caller from what its connection proved (runtime socket: the local CLI; the desktop's IPC: the desktop; a paired socket: its device). Params never set it. - The launch record is written twice: once when the tab exists (what creation settled: on the launch command, a draft, or a submit still `unconfirmed`), and again once the prompt's fate is known. A restart in between finds the running agent instead of answering "unknown". - A replay re-derives its terminal handle from the pane key in the running host, and shows `unconfirmed` only to callers that advertise agent.launch.prompt-unconfirmed.v1. - The record store opens in its own slot, without building the chat host; the chat host is built on that same store. Rebuilt from this PR's own commits (b9adf88, 0d7d9b5, c83f44d, 39d15d5) onto main, without #24080/#24081. Conflicts: the delivery doc table (main's "line fits" row plus the `unconfirmed` row), and main's journal-database open in install(), which now goes through the record-store slot.
…ed for it An agent.launch now shows its terminal tab before admission and spawn, in the requested placement (group and/or anchor tab), and the pane attaches to the agent as soon as it runs. A pane whose agent can't start, or whose start can't be confirmed, says so instead of becoming a plain shell, and keeps saying so across restarts. A user's close of the tab or its pane during the launch stops it and answers agent_launch_tab_closed. Whose view moves is unchanged from main for every caller. Rebuilt on main (with #24934) from the previous branch head 8f62858.
8f62858 to
ea4a899
Compare
…agent.launch Part 3 of 3 of the agent-launch unification, on main. The AI buttons (Fix checks, the source-control actions, commit/push recovery, Explain commit, notes and annotation sends, session continuation) started their agent from the window. They now ask the host's agent.launch to start it, with no prompt, into the tab the window makes at the click, under one operation id per click; the window's pane waits for the host's agent instead of spawning a shell. The window then pastes the prompt with main's own paste, moved out verbatim and started only once the host's agent holds the tab, so delivery and follow-ups are exactly main's. A typed new-tab prompt and launches while chat is the default keep main's path. The source-control dialog drops its launch-command preview, which the host builds now.
13a4383 to
c63d9ec
Compare
…t-launch-instant-tab Conflicts resolved against #24934's rebuild (82544cb), which this branch was built on, so each keeps main's squashed #24934 plus this branch's change. agent-launch-floating-workspace.test.ts takes main's #22302 routing (a structured preference opens a Codex chat) with this branch's host-derived viewMode on the terminal branch. The record-store slot test gets the resolveLaunchArgs dep #25721 made required, the same line as main's fix.
Where this PR stands (head e2e0f2e)The problem. The desktop's AI buttons (Fix checks, the source-control actions, commit/push-failure recovery, Explain commit, notes and annotation sends, session continuation) started their agent from the window, outside What changes for you. Nothing you're meant to see. The tab still opens at the click in your split, and the prompt is pasted and submitted with main's own code. Underneath, the agent is started by the host's Live QA, main vs this PR, scripted hidden rigs with stub agents, terminal as the default view (M4Air macOS and Windows high spec; main macOS (zsh):
Windows (PowerShell and cmd, 5-line prompt):
On both machines the PR route was confirmed: the PR serves Known pre-existing behaviour, kept: as on main, a paste agent that crashes at startup can still receive its prompt in the shell; on this PR's D10 run it didn't, but nothing here guarantees that. #24080's guarded host paste fixes it when it returns. Not verified:
Tests. No readiness wait or paste starts before the host has its agent in the tab; the paste targets the tab the window made; a refused launch never pastes and says so once; a user close is silent; every call site's prompt reaches its agent as main sends it. Each guarantee was broken on purpose and its test went red. CI. Run 37535660946 on e2e0f2e is green: 12 jobs passed, 11 skipped by path filter (this PR is renderer-only), none failed; Mobile Checks and PR test LoC green. The cross-version wire suite was run locally on this head: 25 files, all passed. QA ran on c63d9ec; e2e0f2e only merges #25430's update (main with #24934) on top, with no change to this PR's code. Base update. #25430's head moved twice after this comment (main cc60067 merged in, then a formatting-only fix); both are merged here, final head c475c43, with no change to this PR's code. Type checks, 140 test files (cross-version included), lint and localization pass; CI run 37540105874 is green (attempt 2: on attempt 1 a profile-state writer stall test, which this PR doesn't touch, timed out on the Windows runner). Retargeted to main. #25430 squash-merged into main, so this PR now targets main, and current main is merged in (latest 7c9c3d4; final head 0102437), with no change to this PR's code. Type checks, the full lint, the changed-lines quality gate and this PR's 12 test files (308 tests) pass. CI run 37566315508 is green: all five test shards passed. Two earlier reds on the way were main's, fixed on main: a SQLite test-runtime list missing #25430's test (#26028) and a file over the line limit (#26050). Locally, CI's shard-2 file list fails the same files on this PR and on its merge base with main; the only tests that failed on the PR alone are two real-process deadline tests in main-process code this PR doesn't load, and one of them fails on main too. Resolve conflicts timing re-check, and one fix (head 23af42c).
Two-stub re-check on 23af42c vs main 67dc092 (macOS, Resolve conflicts direct + AI Fix, 3 runs each per stub per side, 24 runs).
|
…bs runtime stays under its line limit The merge of main (#25724's emitMobileSessionTabsSnapshot metadata) plus this branch's close mark put the file one line over max-lines. Formatting only.
…ch-desktop-buttons
📝 WalkthroughWalkthroughThe renderer adds host-mediated agent launches, launch-pane spawn holds, and host routing for eligible prompt launches. Prompt delivery waits for the host launch outcome when required. The source-control agent dialog now validates launch inputs and displays errors instead of successful delivery-plan details. Tests cover host launch outcomes, prompt delivery, pane-spawn coordination, and launch validation. Priority: ➖ Normal Merge Risk: 🟡 Moderate · up to If an agent's host takes longer than eight seconds to start, the prompt may never be delivered even though the launch succeeds. This affects only slow spawns, but it should be resolved or explicitly accepted before merge. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
Visual proof: live QA, main vs this PR (macOS, M4Air, hidden app, stand-in agents)An agent that crashes at startup, with its prompt pending (commit-failure AI Fix)Main: the crashed agent's prompt is typed into zsh, which runs it line by line ( This PR: the agent starts through Long multi-line prompt (13.5 KB commit-failure fix)Both deliver the prompt once (one bracketed paste, one Enter) into the agent's tab, and the toast reads "Started an AI agent for the commit failure." This PR, tab opened through Found in QA, fixed (head 23af42c)
Agent ready at once, 3 s after the click. Both have the full prompt submitted and show the toast. Windows (PowerShell, cmd, Qwen Code): this PR delivers the prompt once where main delivers nothing; see the QA comment above. |
…unch-desktop-buttons
…first output The window started watching for the agent's ready signal only after the host answered agent.launch, so an agent that had already turned on bracketed paste by then was never seen ready and its prompt waited out the readiness budget. Readiness is now watched from the moment the tab's terminal exists, as main watches it; the paste is written, and the chat copy seeded, only once the host says its agent started in that tab.
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
c95eabc1-99d8-4e1d-81b6-b461e43dd003
📒 Files selected for processing (6)
src/renderer/src/lib/agent-launch-prompt-delivery.tssrc/renderer/src/lib/agent-paste-draft.tssrc/renderer/src/lib/launch-agent-new-tab-host-route-readiness.test.tssrc/renderer/src/lib/launch-agent-new-tab-host-route.test.tssrc/renderer/src/lib/launch-agent-new-tab-host-route.tssrc/renderer/src/lib/launch-agent-tab-prompt-paste.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.
| const settings = getSettingsForAgentTabRuntimeOwner(tabId) | ||
| const readinessTimeoutMs = resolveDraftPasteReadyTimeoutMs(agent, timeoutMs) | ||
| const readiness = await waitForAgentDraftInputReadyOnTab({ | ||
| const readinessResult = waitForAgentDraftInputReadyOnTab({ |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- PR diff for target file ---'
git diff --unified=50 7c9c3d431a7854c1f488c35b1bc0f16b97c18aae 23af42cac24eaeb611e5b89c1ef543cfbdc0b53a -- src/renderer/src/lib/agent-paste-draft.ts
printf '%s\n' '--- target source ---'
nl -ba src/renderer/src/lib/agent-paste-draft.ts | sed -n '1,260p'
printf '%s\n' '--- readiness helper declarations/usages ---'
rg -n -F -- 'waitForAgentDraftInputReadyOnTab' src || test "$?" -eq 1
printf '%s\n' '--- PTY attachment/spawn terms ---'
rg -n -i -- 'attach.*pty|pty.*attach|spawn.*pty|pty.*spawn|PTY_SPAWN_TIMEOUT_MS' src | head -240Repository: stablyai/orca
Length of output: 41601
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- readiness helper ---'
nl -ba src/renderer/src/lib/agent-paste-draft.ts | sed -n '265,375p'
printf '%s\n' '--- direct paste helper references ---'
rg -n -F -- 'pasteDraftWhenAgentReady' src/renderer
printf '%s\n' '--- sendGate references ---'
rg -n -F -- 'sendGate' srcRepository: stablyai/orca
Length of output: 13285
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- host route ---'
nl -ba src/renderer/src/lib/launch-agent-new-tab-host-route.ts | sed -n '1,210p'
printf '%s\n' '--- related launch routing callers ---'
rg -n -F -- 'launchAgentNewTabHostRoute' src/renderer/src/lib src/renderer/src
printf '%s\n' '--- host startup result/outcome references ---'
rg -n -- 'started|outcome' src/renderer/src/lib/launch-agent-new-tab* src/renderer/src/lib/launch-agent-tab-prompt-paste.tsRepository: stablyai/orca
Length of output: 7076
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- launch adapter ---'
rg --files src | rg 'agent-launch-through-host'
printf '%s\n' '--- adapter source ---'
nl -ba src/renderer/src/lib/agent-launch-through-host.ts | sed -n '1,300p'
printf '%s\n' '--- host launch symbols and RPC wiring ---'
rg -n -- 'agent\\.launch|HostAgentLaunchOutcome|launchAgentThroughHost|agentLaunch' src/main src/renderer/src src/shared | head -240Repository: stablyai/orca
Length of output: 41537
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- runtime RPC method registration ---'
rg -n -F -- 'agent.launchReplay' src/main src/renderer/src/runtime src/shared
printf '%s\n' '--- host launch handler ---'
nl -ba src/main/runtime/rpc/methods/agent-launch.ts | sed -n '70,370p'
printf '%s\n' '--- RPC timeout implementation ---'
rg -n -- 'function callRuntimeRpc|const callRuntimeRpc|callRuntimeRpc<' src/renderer/src/runtime/runtime-rpc-client.ts src/renderer/src/runtimeRepository: stablyai/orca
Length of output: 28531
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- runtime RPC client ---'
nl -ba src/renderer/src/runtime/runtime-rpc-client.ts | sed -n '1,145p'
printf '%s\n' '--- long-poll transport ---'
nl -ba src/main/runtime/runtime-rpc/runtime-rpc-long-poll.ts | sed -n '1,150p'
printf '%s\n' '--- host route readiness test ---'
nl -ba src/renderer/src/lib/launch-agent-new-tab-host-route-readiness.test.ts | sed -n '70,175p'
printf '%s\n' '--- terminal surface launch factory ---'
nl -ba src/main/runtime/rpc/methods/agent-launch-surfaces.ts | sed -n '1,210p'Repository: stablyai/orca
Length of output: 25559
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- createTerminal declarations and key callers ---'
rg -n -- 'createTerminal\\(' src/main src/renderer/src | head -120
printf '%s\n' '--- runtime terminal creation implementation candidates ---'
rg -n -- 'async createTerminal|createTerminal:|createTerminal\\s*=|function createTerminal' src/main
printf '%s\n' '--- targeted readiness tests around delayed outcomes ---'
nl -ba src/renderer/src/lib/agent-paste-draft.test.ts | sed -n '500,675p'
printf '%s\n' '--- host launch tests with delayed spawn/outcome ---'
rg -n -i -- 'delay|slow|timeout|spawn|started' src/renderer/src/lib/agent-launch-through-host.test.ts src/renderer/src/lib/launch-agent-new-tab-host-route-readiness.test.tsRepository: stablyai/orca
Length of output: 22985
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- terminal creation implementation ---'
nl -ba src/main/runtime/orca-runtime-create-terminal.ts | sed -n '1,260p'
printf '%s\n' '--- terminal spawn/registration path ---'
nl -ba src/main/runtime/runtime-local-worktree-terminal-startup.ts | sed -n '1,220p'
nl -ba src/main/runtime/runtime-worktree-terminal-provisioning.ts | sed -n '1,180p'Repository: stablyai/orca
Length of output: 30919
Keep PTY observation active until the host outcome settles.
When the host outcome settles as started after the eight-second spawn budget, the PTY may already have been missed and readinessResult remains null. Continue observing until the host outcome settles, while preserving early PTY observation.
…ions Since stablyai#25624 a desktop "Continue in New Session" with Codex starts through the host's `agent.launch`, which builds its own command and never sees the window's startup plan. The launch-only `-c check_for_update_on_startup=false` therefore never reached the default route, and "Update now" still exited Codex before the handoff was pasted. The window now asks the host with a new optional `suppressStartupUpdatePrompt` field on `agent.launch`. The host carries it through the launch intent and the existing-workspace terminal surface into `createTerminal`, where `resolveAgentStartupPlanInputs` turns it into the same transient args the window uses, so they ride the typed command but not the resume config. Both sides read the per-agent args from one shared helper. The field is part of the launch fingerprint like `agentArgs`, and absent keys leave existing digests unchanged. An older host drops the field and behaves as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>






ELI5
The problem. The desktop's AI buttons (Fix checks, Resolve conflicts and the other source-control actions, commit/push-failure recovery, Explain commit, notes and annotation sends, session continuation) each started their agent from the window, on their own. They never went through
agent.launch, the one way the phone, the CLI and orchestration start an agent, so they had no launch record, no replay after a restart, and no say from the host about where the tab goes.What changes for you. Nothing you see, by design. Each button still opens its agent's tab at the click, in the split you used, and its prompt is pasted and submitted exactly as today. Underneath, the agent is now started by the host's
agent.launch, under one id for that click, like every other caller.What Changed
This is part 3 of 3 of the agent-launch unification, rebuilt on main: #24934 (one caller identity and the launch record), #25430 (the instant tab and placement), then this PR. The prompt-on-the-command-line work (#24080, #24081) is parked and comes back on top later.
User-facing, before → after:
agent.launchto start the agent, with no prompt, into the tab it just made; the host records the launch under this click's operation id.pasteAgentLaunchPromptOnceReady, moved out oflaunch-agent-in-new-tab.tsverbatim). It watches for readiness from the tab's first output, as main does, but writes only once the host says its agent started in the tab. Because no shell runs in front of the agent any more, the paste waits for the agent's own ready signal (see the timing difference below).How it works:
launch-agent-in-new-tab.ts: a launch whose prompt is pasted once the agent is ready (submit-after-ready, the AI buttons) goes throughlaunchNewTabPromptThroughHostwhen the window makes the terminal (terminal is the default). A typed prompt (auto-submit,draft), and any launch while chat is the default, keep main's path unchanged.agent-launch-through-host.tsmints a tab and pane id per click, holds that pane's spawn (agent-launch-pane-spawn-hold), makes the tab, and callsagent.launchReplaywith an operation id, the pane,placement: { groupId }and no prompt. feat(agent-launch): show an agent's tab at once, where the caller asked #25430's early publish takes the window's tab as this launch's (created), and the reveal releases the hold, so the pane attaches to the host's agent and never spawns a shell of its own.Why
Brennan: "I honestly just thought that here we were unifying the launch point, not fixing bugs. I don't want preexisting bugs to delay the launch point agent unification." So this PR moves only where the agent is started, and keeps delivery exactly as main has it.
Alternatives considered:
Differences from the common pattern:
Measured differences from main (live QA):
?2004h) before the agent runs (terminal-agent-paste-bracketing.ts). Main's readiness check counts that as the agent's signal and never clears it on the shell's?2004l. So with an agent that stays quiet for 1.5 s or more before enabling bracketed paste, main pastes before the agent is ready.?2004his the agent's own.Known pre-existing behaviour, kept:
Linked Issue
Part of the agent-launch unification (#24934, #25430, this PR); supersedes #23748, which routed the same buttons with its own tab reservations (not closed here).
Visual Proof
N/A: no visible change by design. The tab, its placement, the paste and the follow-ups are main's; live main-vs-PR QA of every delivery row follows this push.
Testing
What the tests pin (each broken on purpose and seen red):
Checks run locally: see the latest comment.
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
Author: @BrennanKB5.
SSH: SSH workspaces start through the same local host, whose relay runs the pane; the window's paste is main's. Folder workspaces: the launch scope resolves worktrees and folder workspaces alike. Mobile: no phone change. Windows: delivery is main's on every shell.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)