Skip to content

feat(agent-launch): the desktop AI buttons start their agent through agent.launch - #25624

Merged
brennanb2025 merged 14 commits into
mainfrom
brennanb2025/agent-launch-desktop-buttons
Oct 7, 2026
Merged

brennanb2025 merged 14 commits into
mainfrom
brennanb2025/agent-launch-desktop-buttons

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 14 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​809 $\color{#cf222e}{\Huge{\mathbf{−}}}$​130 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​679
Prod 24 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​633 $\color{#cf222e}{\Huge{\mathbf{−}}}$​330 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​303

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:

  • Tab: made by the window at the click, in the split you used → the same.
  • Agent start: the window queued the agent's launch line into its own new terminal → the window asks the host's agent.launch to start the agent, with no prompt, into the tab it just made; the host records the launch under this click's operation id.
  • Prompt: pasted by the window once the agent is ready, and submitted → the same code (pasteAgentLaunchPromptOnceReady, moved out of launch-agent-in-new-tab.ts verbatim). 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).
  • Follow-ups (resolve threads, post replies, mark notes sent, "Started an AI agent…"): unchanged, on the same paste result as today.
  • A launch the host refuses before the agent runs: the tab goes and one notice says why, with Copy prompt, in the words the pane uses ("The agent couldn't start."). Today the window starts the agent itself, so this case had no counterpart.
  • Closing the tab while the agent starts: silent, as the phone's "+" is.
  • Source-control agent dialog: its box previewing the launch command is gone (the host builds the command now); its errors you can fix stay.

How it works:

  1. launch-agent-in-new-tab.ts: a launch whose prompt is pasted once the agent is ready (submit-after-ready, the AI buttons) goes through launchNewTabPromptThroughHost when 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.
  2. agent-launch-through-host.ts mints a tab and pane id per click, holds that pane's spawn (agent-launch-pane-spawn-hold), makes the tab, and calls agent.launchReplay with 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.
  3. The window starts main's paste at the click. Its readiness wait binds to the tab's terminal the moment that terminal exists, as main's does, so an agent that is ready before the host answers is still seen. The paste (and the chat view's copy of the prompt) is written only once the host answers that the agent started. Only the host's agent can fill that terminal, because the window's own spawn is held.
  4. Bookkeeping never blocks the click: if the desktop's launch record is full (512 per day), the same launch goes out without an id into the same pane; the refused launch's tab closes and returns at spawn.

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):

  • Windows (PowerShell and cmd; Claude-shaped and Qwen Code agents; 5-line prompt): the PR delivers the prompt once, as a bracketed paste and Enter, where main delivered nothing on any of them. The agent starts 0.1–0.9 s after the click on the PR, against about 12 s on main. Likely cause, not traced: the host starts the agent directly, while main's window started a shell first and typed the launch line, which outlasted its paste's wait.
  • Windows startup crash: nothing typed on either side; the PR shows "Your prompt wasn't sent — paste it once the agent is ready." where main showed nothing.
  • Intended: the prompt can land later than on main, but never before the agent is ready.
    • Main: the window starts a shell and types the agent's launch line into it. The shell's own prompt turns bracketed paste on (?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.
    • This PR: the host starts the agent with no shell in front, so the first ?2004h is the agent's own.
    • Measured on macOS: Resolve conflicts, direct and dialog, 6 runs per side at load 4–6, with a stub that draws at once and enables bracketed paste 2 s later.
      • Main pasted 1.52 s after the agent's first output, about 0.5 s before the agent was ready.
      • This PR pasted 3.51 s after it (2 s + the 1.5 s quiet window), so the prompt and the "Started an AI agent" toast came about 1.7 s later.
      • Bytes were identical, and the pane was drawn earlier on the PR (median 277/303 ms vs 457/360 ms).
    • An agent that keeps drawing until it enables bracketed paste (as Claude Code does) should paste at the same time on both. Main's early paste is pre-existing main behaviour this PR doesn't carry.

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

  • I manually tested these changes locally (live main-vs-PR QA is dispatched after this push)
  • Automated tests added/updated

What the tests pin (each broken on purpose and seen red):

  • Readiness is watched from the tab's first output: an agent that is ready before the host answers is pasted at its own ready signal plus the quiet window, not after the timeout. Nothing is written, and no chat copy seeded, before the host says its agent started; the paste targets the tab the window made.
  • A launch refused before its agent ran never pastes and says so once; a user close or a pane that explains says nothing.
  • The tab is made at the click, in its split, with its pane's spawn held until the host takes it; every click is a new operation; no prompt is sent to the host; a refusal takes the tab back; a pane the host never took is never left to open as a shell; a full record still starts the agent in the same pane.
  • Which launches go through the host: AI-button launches only, never a typed prompt or a chat-default launch.
  • Every call site's prompt still reaches its agent the way main sends it (paste content and Enter, per agent mode), and its arguments, placement and telemetry source reach the host request.

Checks run locally: see the latest comment.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and 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

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@brennanb2025

brennanb2025 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

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):

  • Delivery: every prompt the PR delivered arrived exactly once; nothing was ever typed into zsh, bash over SSH or PowerShell.
  • Faster than main at low load: the agent is drawn at 0.37–0.56 s (main 0.58–0.72 s) and its prompt arrives at 0.51–0.64 s (main 2.19–2.26 s).
  • Better than main: long prompts under load are submitted (main left them unsubmitted); a crashed paste agent types nothing (main ran its prompt in zsh); Windows Claude and Qwen Code get their prompt once (main never delivered either); a worker brief arrives once in 0.5–2 s (main 8.7–9.7 s).
  • Startup crash: "exited at startup" with Copy prompt at 0.3–0.6 s on macOS (main: "wasn't sent" at ~20 s). On Windows: "The agent started, but your prompt wasn't sent" with Copy prompt at 60 s (main showed nothing).
  • A healthy Codex behind Orca's codex wrapper is never called exited; its prompt arrives once.
  • The follow-up ("Started an AI agent…") now comes about 2 s after the agent starts, after its prompt, never at the click and never lost.
  • Native chat: unchanged.
  • Final spot checks on this head: on Windows PowerShell an AI button with Qwen Code delivers once at 4.7 s (main never pasted), and a startup crash types nothing and shows Copy prompt; orca worktree create --agent --prompt behaves as on main; on macOS a healthy Codex behind Orca's wrapper gets its prompt once and is not called exited.

What QA and review found, and what was fixed:

  • QA round 1: the new tab stayed blank until the host answered (a base-PR cause, fixed in feat(agent-launch): show an agent's tab at once, where the caller asked #25430); the follow-up waited for Claude's whole first answer with status hooks off; a millisecond crash read as "couldn't confirm" at 61 s; Qwen Code's paste was refused on Windows PowerShell. All fixed.
  • QA round 2: a crash under load could go without any notice; the launch could start seconds late (measured at low load: host bookkeeping 0–23 ms). Fixed and re-measured.
  • QA round 3: Qwen Code still refused on Windows. The real cause: the host recorded which shell a pane runs only when a prompt was offered to the launch line, and Qwen Code takes its prompt only after it starts. Fixed; round 4 delivered it 3 of 3.
  • Review (correctness, receipt contract, an architecture challenger, then a narrow round on the fixes) found a healthy Codex could be called exited (Orca's wrapper runs a probe first), a crash on shells that send no finished-command report went unnoticed, and the startup check could overrun its 10 s budget. All fixed.
  • Readiness checklist (8 areas): no P0 or P1. Fixed: the Windows paste rule had also reached callers without the guard (orca worktree create --prompt, the window's composer), now scoped to the AI buttons' guarded paste only; a paired desktop's failed paste gave no feedback, now the "wasn't sent" notice.
  • Every fix was broken on purpose and its test went red: 48 ablations.

Deferred, known (none worse than main unless stated):

  • Intended: on Windows Git Bash and WSL panes, an agent that takes its prompt only after it starts gets Copy prompt and no follow-up, where main pasted it blind. Nothing there can prove the shell is not in front.
  • On Windows cmd/PowerShell, the paste goes ahead when the shell-alone check cannot answer. Tightening it needs the terminal's spawner to report "alone / not alone / can't tell".
  • Unfiled follow-up: the spawner reports whether a pane can prove its shell alone, and the paste guard reads that for every terminal. That deletes this PR's per-launch host facts and covers launches that create their workspace (today they keep main's path) and worker briefs.
  • With chat as your default, a failed first message shows no toast; the chat shows it.
  • If the launch record is full (512 launches a day), the tab closes and returns at spawn.
  • On Windows a startup crash gets the generic "wasn't sent" wording after 60 s, not "exited at startup".
  • The source-control dialog's code still uses "delivery plan" names for what is now just its error state.

Found, pre-existing on main, not this PR:

  • orca worktree create --agent claude --prompt with a multi-line prompt on Windows PowerShell delivers only the first line to Claude (3 of 3 runs, main and this PR alike). The other lines are dropped with no notice; nothing reaches the shell. Where they are lost was not traced.

Not verified:

  • fish and other shells without a finished-command report (fish not installed on the QA Mac).
  • SSH with live answers (the QA SSH host's Claude login was refused; delivery over SSH was measured).
  • A real Codex (a stub stood in), and real aider.
  • Git Bash and cmd panes on Windows (the QA box's 128-console limit blocks Git Bash; whether Git Bash could take the guarded paste needs a measurement).
  • The phone: no phone code changed; its answers are unchanged except that on Windows cmd/PowerShell its prompt is now pasted (guarded) where it was refused.
  • A follow-up button that doesn't post to GitHub (notes, annotations) was not driven live.

Base update. After this comment, #25430's final head (8f62858: the phone landing race and the tab-closed wording) was merged in. That put agent-launch-executor.ts over its line limit, so a terminal's launch time now stays with its host facts in the surface factory instead of riding the executor (no behaviour change). Final head 13a4383. A tab you close while its agent starts ends silently for every desktop button, as the phone's "+" does. Type checks, oxlint over every changed file, and 423 test files pass on it; CI run 37485648192 is green except the cross-version job below (one unrelated daemon shell test timed out on the first attempt and passed on rerun).

CI. Run 37481054788: every job is green except the cross-version wire job (and the verify roll-up that requires it). The cross-version wire job fails on every PR in this stack for a reason outside it: the newest release, v1.4.221, imports @streamparser/json, which main removed; main's fix (#25731) reaches this branch when the stack takes main or is retargeted.

… 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.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-launch-instant-tab branch from 8f62858 to ea4a899 Compare October 6, 2026 17:53
…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.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-launch-desktop-buttons branch from 13a4383 to c63d9ec Compare October 6, 2026 18:42
@brennanb2025 brennanb2025 changed the title feat(agent-launch): the desktop AI buttons start through agent.launch (step 7 of 7) feat(agent-launch): the desktop AI buttons start their agent through agent.launch Oct 6, 2026
@brennanb2025
brennanb2025 marked this pull request as draft October 6, 2026 18:44
…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.
@brennanb2025

brennanb2025 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

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 agent.launch, the one way the phone, the CLI and orchestration start an agent. They had no launch record and no say from the host.

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 agent.launch under one id per click. This is part 3 of 3 of the agent-launch unification on main (#24934, #25430, this PR); delivery stays main's until the parked #24080 returns with host-owned delivery.

Live QA, main vs this PR, scripted hidden rigs with stub agents, terminal as the default view (M4Air macOS and Windows high spec; main 2f001fe880b, PR c63d9ec16e4).

macOS (zsh):

Row main PR Verdict
D1 Fix checks, Claude 4,383 B, 1 bracketed paste, 1 Enter; "Started an AI agent for the broken checks." identical bytes, paste and Enter; same toast identical
D1b Explain commit (no follow-up) 403 B, 1 paste, 1 Enter identical identical
D2 / recovery, AI Fix, 13.5 KB 198-line prompt 13,582 B, 1 paste, 1 Enter; toast +186 ms 13,580 B (path 2 B shorter), 1 paste, 1 Enter; toast +205 ms identical
Resolve conflicts, dialog and direct 1,838 B, 1 paste, 1 Enter each; toast 1,836 B, same; toast 0.7–1.1 s later (load 14–41) identical delivery
Close the tab right after the click 0 bytes, no notice 0 bytes, no notice identical
Chat as the default chat session; toast +251 ms main's path exactly; toast +257 ms identical
D9 argv agent crashes at once nothing typed nothing typed identical
D10 paste agent crashes, paste pending the prompt was run by zsh at +2.43 s nothing typed; "Your prompt wasn't sent — paste it once the agent is ready." at +9.8 s PR safer
D3 Codex-shaped stub via Orca's wrapper prompt not delivered (argv flag only, no stdin) the same equal (pre-existing)

Windows (PowerShell and cmd, 5-line prompt):

Row main PR Verdict
D6 Claude on PowerShell (Fix checks, Explain) nothing delivered; agent up ~12 s delivered once (bracketed paste + Enter); agent up 0.4 s PR better
D7 Claude on cmd nothing delivered; agent up ~12 s delivered once; agent up 0.1 s PR better
D8 Qwen Code on PowerShell nothing delivered delivered once, with its retry Enter PR better
D11 crash nothing typed, no notice nothing typed; "wasn't sent" notice PR better

On both machines the PR route was confirmed: the PR serves agent-launch-through-host.ts and launch-agent-tab-prompt-paste.ts (main doesn't), and the new tab's pane stays empty until the host's agent attaches (e.g. set +28 ms, attached +306 ms). Why Windows now delivers is not traced; the likely cause is that the host starts the agent directly, while main's window started a shell first and outlasted its paste's wait. Real config files on both boxes were unchanged by the rigs.

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:

  • D3: the Codex-shaped stub got no prompt on either arm, so "delivered once, second Enter" for Codex is not shown.
  • D12: a slow host spawn (the paste's 8 s budget) has no rig mechanism.
  • The new notice for a launch the host refuses before the agent runs ("The agent couldn't start." with Copy prompt): both live triggers failed to produce it; unit-tested only.
  • Notes and annotation sends end to end (same code path as the quick-launch button; not driven).
  • The prompt itself in chat mode (only the launch and toast were compared).
  • Linux.

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).

  • What QA saw: a low-load re-run (macOS, 6 runs per side, direct and dialog) found the PR's paste and "Started an AI agent" toast about 1.7 s later than main's. The pane is drawn earlier on the PR, and the bytes are identical.
  • Cause (verified against every run to ±10 ms): main pastes before the agent is ready.
    • Main's window types the launch line into a shell, and that shell's prompt turns bracketed paste on. Main's readiness check takes that as the agent's signal, so with the test agent (quiet for 2 s before enabling bracketed paste) main pasted 1.5 s after the agent's first output, about 0.5 s before the agent was ready.
    • This PR has no shell in front of the agent, so it waits for the agent's own signal plus the 1.5 s quiet window.
    • Intended, now stated in the body: main's early paste is pre-existing main behaviour this PR doesn't carry.
  • Fix found while tracing it: the PR only started watching for readiness after the host's reply, so an agent that was ready before that reply would have waited out the readiness budget. Readiness is now watched from the tab's first output, as main does. The paste, and the chat view's copy of the prompt, are written only once the host says the agent started.
    • New test: an agent that enables bracketed paste before the host answers is pasted at its own ready signal plus the quiet window. A refused launch writes nothing and seeds nothing.
    • Two ablations of the fix both went red.
    • Local checks: web typecheck, full lint, changed-lines gate, and 68 related test files (922 tests) pass.
  • QA will re-run Resolve conflicts and AI Fix against main with two stubs: one that draws continuously until it is ready (Claude-like), and one that is ready about 100 ms after start.

Two-stub re-check on 23af42c vs main 67dc092 (macOS, Resolve conflicts direct + AI Fix, 3 runs each per stub per side, 24 runs).

  • Stub that draws until it's ready at ~2 s (Claude-like): both sides paste 1.5 s after the agent's own bracketed-paste signal. The PR is slightly earlier (median 3,610 vs 3,893 ms for Resolve conflicts; 3,625 vs 3,820 ms for AI Fix) because the host starts the agent without a shell first.
  • Stub that's ready ~100 ms after start: the PR pastes 1,537 ms (Resolve conflicts) and 1,524 ms (AI Fix) after the agent's signal, against main's 1,508 and 1,514 ms. That is the quiet window on both sides, nowhere near the 8 s timeout, and no "wasn't sent" notice appeared.
  • Delivery: once and submitted in all 24 runs, with identical bytes and nothing typed into a shell. In one PR run the submit Enter reached the stub in the same read as the end of the paste.
  • Same on both sides: on AI Fix, "Started an AI agent for the commit failure." shows at launch, before the paste. That is main's existing behaviour, unchanged here.
  • Not covered: only Claude-shaped stubs ran, not real agents and not Codex.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 6, 2026 22:00
…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.
@brennanb2025
brennanb2025 changed the base branch from brennanb2025/agent-launch-instant-tab to main October 7, 2026 01:02
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

Walkthrough

The 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 23af4

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)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.55% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 47 functions across 30 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the main change: desktop AI-button launches now start through agent.launch.
Description check ✅ Passed The description is detailed and covers the change, rationale, testing, visual proof, and compatibility considerations. It references related PRs but does not provide an explicit Fixes #... issue lin…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@brennanb2025

brennanb2025 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

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 (zsh: command not found: Fix, …: Worktree:, …).

main: crashed agent's prompt run by zsh

This PR: the agent starts through agent.launch, crashes, and nothing is typed into the shell. "Your prompt wasn't sent" + Copy prompt appears at +9.8 s (main's own timeout notice).

PR: nothing typed into the shell after the crash

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."

Main:
main: long prompt delivered

This PR, tab opened through agent.launch (agent drawn, prompt pasted once it's ready):
PR: long prompt, agent ready

Found in QA, fixed (head 23af42c)

  • Resolve conflicts timing: a low-load re-check found the prompt landing ~1.7 s after main's. The window started watching for the agent's ready signal only after the host replied. It now watches from the moment the tab's terminal exists, and pastes once the host confirms the agent started.
  • Re-measured after the fix (24 runs, load 3-6, two stand-in agents): the PR's first paste is as early as main's or slightly earlier (Resolve conflicts: 3.61 s vs 3.89 s for an agent busy until 2 s; 1.74 s vs 2.07 s for an agent ready at once). Delivered once, identical bytes (1,840 B), nothing typed into the shell, never near the 8 s readiness timeout.
  • Intended difference from main: on main, the window can paste before the agent's own ready signal, because it reads the launching shell's bracketed-paste switch as the agent's. This PR waits for the agent's own signal.
  • Prompt bytes, resolved: an earlier 1,836 B vs 1,838 B gap was the QA folder names (pr-… vs main-…) embedded in the prompt's worktree path. With equal-length names, the bytes are identical.

Agent ready at once, 3 s after the click. Both have the full prompt submitted and show the toast.

Main:
main: prompt delivered at 3 s

This PR:
PR: prompt delivered at 3 s

Windows (PowerShell, cmd, Qwen Code): this PR delivers the prompt once where main delivers nothing; see the QA comment above.

…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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c95eabc1-99d8-4e1d-81b6-b461e43dd003
📥 Commits

Reviewing files that changed from the base of the PR and between 0102437 and 23af42c.

📒 Files selected for processing (6)
  • src/renderer/src/lib/agent-launch-prompt-delivery.ts
  • src/renderer/src/lib/agent-paste-draft.ts
  • src/renderer/src/lib/launch-agent-new-tab-host-route-readiness.test.ts
  • src/renderer/src/lib/launch-agent-new-tab-host-route.test.ts
  • src/renderer/src/lib/launch-agent-new-tab-host-route.ts
  • src/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({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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 -240

Repository: 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' src

Repository: 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.ts

Repository: 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 -240

Repository: 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/runtime

Repository: 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.ts

Repository: 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.

@brennanb2025
brennanb2025 merged commit 47f4d3f into main Oct 7, 2026
32 checks passed
luisgonzaleznf added a commit to luisgonzaleznf/orca that referenced this pull request Oct 7, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant