Skip to content

Preserve desktop startup input in host agent launches - #26364

Closed
brennanb2025 wants to merge 15 commits into
brennanb2025/host-owns-first-prompt-follow-upsfrom
brennanb2025/step4-new-tab-unify
Closed

brennanb2025 wants to merge 15 commits into
brennanb2025/host-owns-first-prompt-follow-upsfrom
brennanb2025/step4-new-tab-unify

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 16 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2354 $\color{#cf222e}{\Huge{\mathbf{−}}}$​3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2351
Prod 28 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​719 $\color{#cf222e}{\Huge{\mathbf{−}}}$​367 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​352

ELI5

When you start an agent from the "+" menu or Quick Launch, the window itself types the startup command and your prompt into the new terminal. The AI buttons already work differently: they ask the computer that runs the workspace (the "host", which may be this Mac or an SSH server) to start the agent. We want every launch to go through the host. But the host types prompts a little differently than the window does, so moving the menus over as-is would change what reaches the agent. This PR teaches the host to start a new-tab agent exactly the way the window does today. Nothing you see changes in this PR; the next PR (#26489) moves the menus onto it.

What Changed

Problem. The window and the host each had their own way to start an agent and deliver the first prompt: different startup command rules, different handling of drafts that must not be submitted, different timing for Enter, and no protection against typing a prompt into an agent's startup trust/security dialog in some cases. Moving the "+" menus to the host without closing those gaps would have changed prompt bytes, editable drafts and Enter behaviour for users.

User-facing change. None in this PR. It adds a capability the next PR uses. Existing callers keep their current behaviour.

Mechanism.

  • The window can now mark a launch as a desktop new tab. The host then builds the startup command with the same shared planner the window uses (moved to src/shared, unchanged), so agent arguments, environment and quoting (including Windows shells) match.
  • The host delivers the first input the way the window does: empty launches, native draft prefill on the command line, pasted drafts without Enter, and submitted prompts with the same bracketed-paste bytes, the same chunking, and the same 50 ms pause before Enter.
  • Every write waits for the terminal transport (including SSH) to accept it and re-checks right before writing that the original terminal is still the one being launched and the user hasn't closed it. A prompt is written once; replaying the same launch never writes it again.
  • The host never types a first prompt or Enter into an agent's startup trust/security dialog. It waits for the dialog to clear for as long as the window's own paste waits for the agent today (20 seconds for Codex and OpenCode, 8 seconds for other agents), then sends once, or gives up and the window shows its existing "wasn't sent" notice. Today's window paste could land in that dialog.
  • Only the local desktop window may use this, identified by its own IPC connection, never by a request field. The capability is learned over that local connection only; the host does not advertise it to paired devices, phones, web or remote servers, and refuses the marker from them with a specific error code (agent_launch_desktop_new_tab_unsupported) that now passes through to the caller.
  • A malformed request (unknown agent plus the desktop marker) is now an ordinary validation error instead of an internal error.

Base. Stacked on #26173. That base removed restart re-delivery of prompts and made desktop launches wait the same per-agent time as the window's paste. Like every launch prompt on that base, this one is never re-sent after a restart; replay never sends it twice.

Why

One owner (the host) should start agents for every caller, local and SSH, so the "+" menus can stop running their own startup queue. Reusing the window's existing planner and byte rules, instead of writing new ones, is what keeps the move invisible to users. The common pattern is a single host-side launch path with the client only placing the tab; this PR gets the host to parity before any caller moves.

Alternatives considered: moving the menus first and fixing differences after (would ship changed prompt bytes and Enter timing); a second, desktop-only launch RPC (two launch paths to keep in sync). A second prompt writer next to the host's existing one is accepted for parity now; merging the two writers is the planned follow-up.

Linked Issue

Launch unification, after #26168 and #26173. Targets #26173's branch until it merges. Next: #26489.

Visual Proof

N/A: no UI or behaviour change in this PR (no caller uses the new capability until #26489). Runtime evidence for the migrated menus, which exercise this code, is on #26489.

Testing

Automated: 843 tests across 50 named files (this PR's own plus the neighbouring launch tests) cover startup commands and environment, per-write bytes and the separate Enter, empty/native/pasted drafts, the original-terminal fence, SSH transport backpressure (real async multiplexer), replay, follow-ups, capability negotiation over local IPC, and the validation/refusal fixes. Each fix from review was checked by removing it and seeing its test fail, then restoring it. tc-gate pnpm tc:node, tc:web and tc:cli pass; all 22 orca-ci-checks pass; CI green at fac392b7af3 (one Windows packaging test in a file this PR doesn't touch timed out once and passed on rerun).

Not verified: a real Windows or WSL machine, and a real (non-stand-in) agent consuming the prompt.

Review

Adversarial review rounds plus a full readiness checklist (SSH/network, crash and retry, security, performance, functional, compatibility, cross-platform, code organisation). Fixed during review: follow-up settlement ownership, SSH write backpressure, a write-reservation race, a validation crash on an unknown agent, the capability being advertised to remote clients that are always refused, the refusal code being hidden, and an unused full copy of large prompts. No open P0/P1/P2. Deferred (P3): the per-chunk "agent still in front" check costs one terminal round trip per 16 KiB chunk for prompts over 64 KiB (most noticeable on Windows); it goes away when the two prompt writers merge.

Agent skill upstream boundary

  • Not applicable.

Notes

  • Intended difference: a first prompt is never typed into a startup trust/security dialog (see above).
  • Intended limitation, same as main: a refused first prompt for a workspace you are not viewing gets no notice when you return, and is not kept across reload.
  • Temporary: a desktop paired to another Orca keeps its current path until the next launch-unification step.
  • Known limit: if a write fails partway through a prompt over 64 KiB, the closing paste marker is not sent (the window's old path did send it). No realistic startup trigger was found.
  • Compatibility: the only wire change is one new optional prompt shape; it is accepted only from the local desktop, which ships with its host. Saved launch records keep their shape. No mobile-facing surface changes.

Author: @BrennanKB5.

Checklist

  • This PR is small and focused (host contract only; no caller moves)
  • I explained what changed and why
  • N/A visual proof with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • Typecheck, named tests, static checks and CI pass

# Conflicts:
#	src/main/agent-launch/agent-launch-executor.ts
#	src/main/agent-launch/agent-launch-surface-factories.ts
#	src/main/runtime/agent-launch-follow-up-record.test.ts
#	src/main/runtime/rpc/methods/agent-launch-replay.ts
#	src/main/runtime/rpc/methods/agent-launch-surfaces.ts
#	src/main/runtime/rpc/methods/agent-launch-terminal-prompt.ts
No desktop new-tab caller sends session options, since main keeps remembered model and effort off terminal launches. The launch contract goes back to string-only options, the startup planner stops reading a desktop options field, and a follow-up test that repeated the base's own case is removed.
zod keeps running refinements after the agent check fails, so the
desktop prompt check looked up the unknown agent's config and threw.
The caller got internal_error instead of the validation issue.
Only the desktop window's own connection may send a desktop new-tab
prompt, so publishing agent.launch.desktop-new-tab.v1 to every paired,
mobile and remote client advertised something each of them is refused.
The desktop renderer already names it on its own IPC connection, which
is what the host checks. Renamed the constant to the client-capability
form; the wire string is unchanged. Also drops the check of the host's
own constant list, which was always true.
agent_launch_desktop_new_tab_unsupported was not on the runtime error
pass-through list, so a refused caller saw a generic runtime_error.
The desktop writer pastes the prompt itself, so the full bracketed-paste
copy built beforehand (up to 16 MiB, on the main process) was only used
for the byte count. The writer now counts the bytes it actually wrote.
The bytes sent to the terminal are unchanged.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review, readiness and QA summary (head 5e435066d0a, on #26173 c6dbbe4bf19)

Problem. The host and the window had different rules for starting an agent and delivering its first prompt. Moving the "+" menus to the host as-is would have changed prompt bytes, editable drafts and Enter timing.

User-facing change. None on its own. This PR lets the host start a desktop new-tab launch exactly the way the window does. #26489 moves the menus onto it, and its live QA exercises this code.

Fixed during review (each confirmed by a test that fails without the fix):

  • Follow-up settlement ownership.
  • SSH write backpressure.
  • A write-reservation race.
  • An unknown agent combined with the desktop prompt marker caused an internal error. It is now a normal validation error.
  • The new capability was advertised to every remote client, although only the local desktop may use it. It is now learned over local IPC only, and it had never shipped.
  • The refusal code was hidden behind a generic error. It now reaches the caller.
  • An unused full copy of large prompts.
  • A boolean session-option widening of the wire format that nothing sent any more. It never shipped and is removed.

Deferred:

  • For prompts over 64 KiB, the "agent still in front" check runs once per 16 KiB chunk. It will go away when the two prompt writers are merged in the planned follow-up.
  • A helper file placement nit.

Verified:

Not verified:

  • Windows and WSL machines.
  • Real (non-stand-in) agents.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 9, 2026 05:30
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Restack onto the final #26173 (head fac392b7af3)

What changed: merged brennanb2025/host-owns-first-prompt-follow-ups at 0a15ac4a8b2, which brings in the shrunk #26168/#26173 and main up to 7b1242ba944. Five conflicts were resolved keeping both sides:

  • The base's split of launch kinds and its legacy create path are kept.
  • The base's per-agent wait is kept, and desktop new-tab launches use it: 20 s for Codex and OpenCode, 8 s for other agents. The old 60 s budget is gone.
  • agent.launch now checks authorization first, then the desktop-prompt check.
  • One test file that main deleted ([CI] Reduce repeated test work and skip unrelated server matrices #26711) stays deleted.

Nothing from the removed restart recovery came back. Against its base, the merge touches exactly this PR's own files. One follow-up commit retypes a test to the base's new launch kind. The PR description no longer claims a 60-second wait.

Checks:

  • tc-gate pnpm tc:node, tc:web and tc:cli pass.
  • 843/843 tests pass across 50 named files: this PR's own plus the neighbouring launch tests.
  • orca-ci-checks --no-typecheck passes 22/22.
  • CI is green at fac392b7af3. One Windows packaging test, preflight-runnable-local-cli.test.ts "recovers after a hung batch launcher", timed out once and passed on rerun. This PR doesn't touch that file.

QA: this PR has no caller of its own, so it's covered by the re-QA on #26489's final head, which sits on this one. That run found no prompt-delivery difference from main.

  • The never-ready Codex launch gives up at 20.14 s (main 20.25 s), with nothing typed into the agent.
  • The saved Quick Command prompt arrives once, locally and over SSH.

Not verified: Windows/WSL machines and real (non-stand-in) agents.

Author: @BrennanKB5

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing this as superseded. Brennan asked for strict parity first ("just parity for the migration to agent.launch please and then we can do bug fixes after"), and #26168/#26173, which this was stacked on, are being closed for the same reason.

At strict parity this PR isn't needed. It taught the host to type the first prompt the way the window does. In the rebuilt #26489, the window keeps delivering prompts exactly as main does, and only plain no-prompt new tabs start through the host's existing agent.launch, the same route main's AI-button launches already use. No new wire contract, capability or host prompt writer is required.

The old head is kept on this branch (fac392b7af3) for reference.

Author: @BrennanKB5

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