Skip to content

Carry an agent's first prompt on its command line instead of pasting it (step 3 of 7) - #24080

Closed
brennanb2025 wants to merge 77 commits into
mainfrom
brennanb2025/launch-prompt-argv-carry
Closed

brennanb2025 wants to merge 77 commits into
mainfrom
brennanb2025/launch-prompt-argv-carry

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 87 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​4785 $\color{#cf222e}{\Huge{\mathbf{−}}}$​886 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​3899
Prod 159 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​3994 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1007 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2987

The problem

When Orca starts a terminal agent with a first prompt, it often starts the agent empty and pastes the prompt into its screen once the agent looks ready. That paste can lose part of the text, can land its Enter on a startup dialog, and makes you wait for the agent to look ready before your prompt shows up.

Today that happens in two places. The desktop's prompt launches that wait for readiness (Fix checks, commit/push-failure fix, Explain changes, Resolve conflicts, notes and annotation sends, session continuation, work items) always start the agent empty and paste. The host's agent.launch (used by the phone, the CLI and orchestration) pastes any prompt whose typed launch line is over 512 bytes or contains a line break, except a short-lined prompt in zsh up to 8 KB, because until the previous step nothing could deliver a long typed line intact.

On Windows, a prompt typed on the launch line has its own damage: cmd adds stray ^ and a line break submits its line early, and Windows PowerShell 5.1 drops every " from a prompt it hands to the agent's program, so on main today the agent gets your text with its quotes missing. (Main now writes a multi-line PowerShell prompt as one line with `n escapes, #23672, so PowerShell no longer submits it early.)

What changes for you

  • On every platform the order is the same: the agent's own command line when it carries your exact text; otherwise the delivery main uses on that path; a private launch file only where neither exists.
  • On a Mac or Linux machine, over SSH to one, and in WSL, the line carries up to 100,000 UTF-8 bytes. Step 2 stages a long or multi-line line where it is typed, so it arrives byte for byte. There is no wait for the agent to look ready and no paste. A 20 KB Fix-checks prompt arrives as your text. A workspace in a WSL path, or a pane whose Windows shell is WSL, runs the distro's POSIX shell, so its line is judged and quoted for that shell whatever the Windows shell setting.
  • On a Windows host, the line carries a prompt wherever that shell was measured to deliver it exactly, judged per shell rather than for Windows as a whole:
    • cmd: one line up to cmd's 8,191-character limit; ", %NAME% and a trailing \ arrive.
    • PowerShell (5.1 and 7): one line up to about 32,000 characters (CreateProcess's limit; 20 KB measured exact), and a multi-line prompt up to 512 bytes (a 5-line prompt measured exact, a 9 KB one damaged). A " or trailing \ rides the line on PowerShell 7, which carried it exactly, but not on 5.1, which drops every " on main today: there it gets a launch file, so the agent reads your quotes exactly. A multi-line prompt is written as one PowerShell line (fix(terminal): submit startup commands with Enter on every platform #23672); its limits are still the measured ones, counted from the prompt's line breaks. Orca knows which one the pane is, because it picks the executable itself: the requested shell, then the PowerShell setting, then whether pwsh.exe is installed.
    • Git Bash: up to 9,089 characters, line breaks included (measured exact; 20 KB never started).
    • A Tab or a bare carriage return rides the line on PowerShell, which carried both exactly; cmd and Git Bash read them as completion or Enter, so there they count as damage. Other control characters were not measured and do what main does on that path.
  • When the line can't carry the prompt, the AI buttons, Fix checks, source-control actions, notes and annotation sends paste it once the agent is running, as main does, so the agent receives your real text. These generated prompts never get a launch file, on any platform. On a Windows host they are always pasted, as on main: nothing there can prove the agent is in front, so nothing could confirm a prompt carried on its line, and these actions run on that confirmation.
  • A launch file is left only for Claude and Codex, and only on the paths where main typed the prompt on the line: agent.launch and workspace creation with an agent (phone, CLI, orchestration), agentSession.create, phone quick commands, the new-workspace composer, background drafts, and a prompt typed into a new agent tab. There it takes a prompt over 100,000 bytes on macOS, Linux, WSL and SSH, and on Windows only a prompt that shell was measured to damage or lose: cmd over 8,191 characters or with a line break; PowerShell over about 32,000 characters or a multi-line prompt of 9 KB or more, and on 5.1 a " or trailing \; Git Bash at 20 KB or more. A pointer is better there than a damaged prompt.
  • Where a Windows line was not measured, or the PowerShell is not Orca's to know, each path does exactly what main does: the AI buttons and notes sends paste, and the typed paths type the line. That covers PowerShell multi-line between 512 bytes and 9 KB, and Git Bash between 9 KB and 20 KB. It also covers a " or trailing \ when the PowerShell is unknown: on an SSH Windows host (its relay picks the shell) or a paired Orca, and on this machine with the "auto" PowerShell setting before the pwsh.exe probe has answered.
  • An agent spelled as a .cmd or .bat shim in its command setting goes through cmd.exe, so cmd's %NAME% expansion and PowerShell's legacy handling of " and a trailing \ also count as damage there. A bare claude or codex may be either a shim or a native executable, so it is judged as typed.
  • On a Windows host, other agents get the same paste on the desktop's AI-button paths, and keep the prompt on the line on the others, as on main: the host cannot prove the agent is in front, so feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 refuses its own paste there.
  • Only Claude and Codex, the two agents measured reading the launch file without asking, get one. Claude is granted the file's folder (--add-dir) so it does not stop to ask. Elsewhere, any other agent keeps the paste after ready for a prompt that would need the file; a caller that has no paste after ready (agentSession.create, phone quick commands) keeps it on the line, as main did.
  • The file sits on the machine running the agent, in a 0700 folder as a 0600 file. In WSL it is written over the Windows share and takes the distro's default modes, inside ~/.cache/orca, which Orca makes 0700 when it first finds it; if that folder is deleted later, Orca recreates it over the share with the distro's default mode until Orca restarts. It stays as long as its terminal does and is removed when the terminal closes. The line carries one sentence pointing at it: The full task is in the file <path>. Read it and complete the task it describes.
  • When a desktop action waits for its prompt to be delivered before it acts (resolving review threads, posting replies, marking notes sent), "delivered" now means the agent got the prompt, proven the way feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 proves an agent before a paste (details under How it works). If the agent never starts, the action does not run and you get the "prompt wasn't sent" notice with Copy prompt. If the agent had the prompt on its command line but exited at startup (the shell is back in front, or the terminal exited), the notice says so instead: "The agent exited at startup, before it received your prompt. Copy the prompt and start the agent again."
  • Where nothing can prove it either way, the action does not run and a notice says "Orca couldn't confirm the agent received your prompt. Check the agent before sending it again." A launch nothing waits on (a prompt typed into a new agent tab) shows nothing then, as on main, which reported nothing for it. On a Windows host or a paired one the waiting actions never get here: their prompts are pasted.
  • A host Orca knows can't take a launch file or stage a long line never gets either: the prompt rides the line within its typed budget where main typed it, or main's paste. That covers a paired Orca, an SSH Windows host (whose panes may run in WSL), and a host whose staging folder can't be written: this machine's temp folder, or the WSL distro's ~/.cache/orca. Orca checks that folder before planning, including before the first launch into a WSL distro; a probe failure, or a refused write, counts for 30 seconds. Where main typed the line (a Windows host cannot prove the agent is in front, so this includes agent.launch and worker start into WSL), the host types it as is, as main does: a 5-line prompt arrives exactly, and a 25 KB one leaves bash waiting for more input, as on main.
  • Only a write that fails in a folder that looked usable (the WSL probe found it, or the temp folder passed its check) stops the launch. A notice says so once, with a Copy prompt button; the phone, CLI and orchestration get that reason at once as a launch that failed without effects; and the desktop closes the empty tab it opened for it. The next launch then sees the broken folder and is planned without it.
  • When a prompt that was to be pasted is not sent, you get one notice, "The agent started, but your prompt wasn't sent", with a Copy prompt button that stays until you dismiss it. It serves the new agent tab, a background draft, a paste that timed out, a work item and a new workspace.
  • Orca's status row, dashboard, phone, tab label and sidebar agent row show the prompt a launch file carries, not the pointer sentence. The branch is still named from your prompt.

Scope

Affected: every way Orca starts a terminal agent with a first prompt: the new agent tab and the desktop AI buttons that use it, quick launch, quick commands, session continuation, work items, new workspace with an agent, background drafts, agentSession.create, and agent.launch from desktop, phone and CLI. Hosts: macOS, Linux, Windows, WSL and SSH.

Not affected: agents that only take text after they start (still pasted once ready), follow-up messages, reused terminals, structured chat sessions, and orchestration worker start (step 4).

A desktop driving a paired remote Orca keeps the line to the 512-byte typed budget and pastes anything longer, because that host may be an older build that neither stages a line nor writes a file. A paired host's prompt that a desktop action waits on is always pasted, because this client cannot read a paired host's terminal to prove the agent received a carried prompt.

The plan, and where this PR sits

This is step 3 of 7 of the agent launch plan. Steps 1 and 2 are on main, so this PR is now based on main.

  1. feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 (merged): the host owns prompt delivery; a prompt is pasted only once the agent is proven in front, and nothing is typed after a startup crash.
  2. Stage long agent launch lines where they are typed, so they arrive whole (step 2 of 7) #23962 (merged): long or multi-line launch lines are staged where they are typed, so they arrive whole.
  3. This PR (Carry an agent's first prompt on its command line instead of pasting it (step 3 of 7) #24080): the first prompt rides the agent's own command line, through a private launch file when the line can't carry it.
  4. Start orchestration workers with their brief on the launch command line (step 4 of 7) #24081: orchestration workers get their brief on the launch line.
  5. Launch identity and record: one host-assigned caller identity and a two-write launch record, so a restart mid-launch finds the running agent.
  6. Placement and the instant tab: the tab appears at once, in the split you clicked from.
  7. feat(source-control): start the AI buttons' agents through agent.launch (step 2 of 2) #23748, reworked: the desktop AI buttons go through agent.launch.

How it works

  1. One rule, carryLaunchPrompt (src/shared/launch-prompt-carry.ts), answers where a prompt rides: none, on-line, launch-file or paste-after-ready.
    • Every prompted launch plans through planLaunchPrompt, and only the carried outcomes hold the plan to launch, so a caller cannot launch without switching on the outcome.
    • buildAgentStartupPlan now builds only a prompt-less launch, and the repo's exhaustive-switch lint fails a caller that skips an outcome.
  2. The rule takes two inputs besides the prompt:
    • the launch host's facts, from one function (describeLaunchHost in src/shared/launch-host.ts): whether it is a paired Orca; whether it can prove the agent is in front (any host but Windows; an SSH or paired host is judged by its own platform); which PowerShell a local Windows pane is spawned as (spawnedWindowsPowerShell: the same requested shell, PowerShell setting and pwsh.exe probe the spawn uses; unknown for an SSH or paired host); and whether it takes a launch file (not a paired Orca, which may be older, and not an SSH Windows host, whose relay may run the pane in WSL, set by the host's OpenSSH default shell, which this client cannot see). The desktop and the host each call it with where the launch runs; no caller passes these as flags.
    • which paste the caller has for a prompt left until the agent is ready (paste): never; when-host-proves-agent, feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257's guarded paste; or once-agent-runs, the desktop paste main uses for submit-after-ready prompts (AI buttons, notes sends), written once the agent's process owns the terminal. Each caller passes the kind main used on its path.
  3. The rule:
    • The line carries the prompt when it is exact: on a host that stages (macOS, Linux, WSL, SSH), up to 100,000 UTF-8 bytes; on a Windows host, per shell from measured lines (windowsLaunchLineVerdict in src/shared/windows-launch-line.ts: exact, damaged, or not measured); on a paired Orca, feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257's 512-byte typed budget.
    • A Windows line not measured either way does what main does on the caller's path: its paste, or the line it typed.
    • Otherwise a caller with the once-agent-runs paste gets that paste; any other caller's prompt rides a launch file.
    • 100,000 bytes is measured: a multi-line prompt of that size with two-, three- and four-byte characters and shell-hostile characters reaches a stub agent's argv byte for byte through staging in bash 3.2, zsh, dash, sh, ksh, tcsh and fish on macOS (130,000 and 400,000 bytes did too). It is held under Linux's 131,072-byte cap on a single argument, which is documented, not measured here.
    • A paired Orca never gets a launch file, so past its budget the prompt goes the way of an agent that cannot read one, below.
    • A launch file goes only to Claude and Codex, on a host that writes it. Otherwise the agent starts clean and the prompt is pasted once it is ready, if the caller's paste reaches the agent on this host; if not, the line carries it, as on main.
    • An agent that takes text only after it starts is always pasted.
    • This replaces feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257's typed-line check, which existed because a long typed line was lost. The checks that sat in front of the rule (an early refusal for after-start agents, a copy of the rule's paste branch in the source-control preview) are gone; the paired-host check above is the one that remains, labelled Temporary below.
  4. A prompt the new agent tab's line carries reports delivery through waitForLaunchPromptReceipt, with one read: feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257's crash-guard predicate (judgeTerminalForeground), which now tells launched-agent (a process in the terminal's foreground group named as the launched agent on its command line) from other (any other process that is not a shell, such as an agent behind a wrapper or a command override, or a startup file's own command). feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257's paste guard counts both as the agent, exactly as before.
    • Delivered when the agent's own hook reports a turn on that tab (except Command Code, whose launch seeds a working row from the prompt itself), or when a fresh read finds a process other than the shell in front once the agent looks ready.
    • The agent looking ready never counts on its own: it only times that read, and the agent turning bracketed paste off again (ESC[?2004l) revokes it.
    • Not delivered when the terminal never spawns (a refused launch file included) or the tab closes.
    • The read starts at the spawn, every 250 ms and backing off to every 2 seconds (each read is one ps of the pane's terminal), and runs once more the moment the agent looks ready. A shell in front is not an exit on its own: a slow shell startup draws its prompt, and looks ready, before it runs the launch line (stack QA saw a false "exited" notice 3 of 3 times that way). Agent exited needs proof the agent ran: launched-agent read in front, then the shell back or the terminal gone.
    • With no such proof, the reads go on until the agent is in front (delivered) or 30 seconds after the ready signal (unconfirmed), about 18 reads in all; a terminal that ends before the agent was seen is unconfirmed too.
    • Unconfirmed when the read cannot tell and no hook turn follows within 2 seconds.
  5. Launch-file writing is terminal-daemon protocol v42 (v41 is step 2's staging, now on main). A spawn routed to an older daemon only ever attaches to a session that daemon still runs, so it is sent with neither the launch file nor its command: nothing is typed, and nothing is refused.
  6. A pane spawn a host refuses with that reason is recorded against its tab.
    • agent.launch, waiting on the tab's handle, fails at once with the code launch_file_unavailable, recorded as a launch that failed without effects.
    • A desktop terminal create that hit the refusal closes the tab it opened.
    • The pane hands back the prompt with Copy prompt, whether a launch file or a staged line was refused. The notice shows once per tab: the empty pane's recovery spawns it again and is refused again. The refusal is not also shown as the pane's error panel.
    • A tab the refusal left with no live terminal closes, as the desktop terminal create above does. A tab that still holds a live terminal stays: that terminal is the user's.
    • While a tab's spawn is still running (a cold WSL distro being probed, then the spawn), the 10-second handle wait keeps waiting, up to 60 seconds, so the caller does not get a timeout for an agent that then starts.
  7. A spawn carries the caller's wish for a line its host cannot stage, unstageableLine (type, the default, or refuse), from the desktop through main's pty spawn to the terminal daemon, which reads it in one place (unstagedAgentLineRefusal). It is optional: a daemon that predates it types the line, as before, so there is no protocol bump. The local terminal host used when the daemon is unavailable, and the SSH relay, do not read it.
  8. WSL asks the distro for its home folder and login shell (and makes ~/.cache/orca 0700) only when a launch needs it: before planning a prompt too long to type (probedThisOrcaLaunchHost, which the spawn would await anyway), and at the spawn for a launch file or an Orca-built line long enough to stage. A failed probe is remembered for 30 seconds. With no folder, the host refuses a launch file (its path would mean nothing in the distro) and types a line as is. The terminal daemon refuses a line whose staging write failed only when the folder looked usable; with this machine's temp folder unwritable it types the line, which is what the plan chose. One predicate (startupLineNeedsStaging) decides both "stage this line" and "this spawn needs the distro".
  9. Launch-file folders are named after the process that wrote them (the terminal daemon, the local terminal host or the SSH relay), whose terminals read them. That process removes a folder when its terminal ends. The startup sweep that cleans up after a crash never touches a folder whose writer is still running, however old; it removes one whose writer is gone, and falls back to a 24-hour age only for a folder that names no writer.
  10. Every hook status row goes through launchPromptShownForPane, which maps a launch file's pointer back to the prompt it carried. The branch rename uses the same function, and so does the session title the tab label and sidebar agent row show.
  • That title comes from the agent's transcript, whose first message is the pointer, cut to 96 characters. So the mapping also recognises a pointer by its opening words, for a pane that has a kept prompt.
  • Each title request names its pane. The host that spawned the pane maps the title: this desktop for local and SSH panes, the host itself for a paired client or the phone. The pane is never sent to an SSH relay.

Why this approach

  • A command-line argument arrives whole or the agent does not start: there is no readiness guess to lose and no wait.
  • One outcome that callers must switch on removes the bug class where a launch path read "a plan exists" as "the prompt was carried", instead of guarding each path. Host facts from one function remove the class where two callers fed the rule different facts about the same host and got different outcomes for the same prompt.
  • Alternatives considered:
    • Always using the file on Windows: every launch would depend on the agent reading an outside file.
    • Passing the prompt in an environment variable: children inherit it, each shell spells it differently, and Windows endpoint security flags it.
    • Making the agent the terminal's first process: it skips Orca's shell wrappers and setup sequencing.

Differences from the common pattern:

  • Intended: long prompts go to a file the agent is pointed at, but a failed write refuses the launch rather than falling back onto the command line; the Copy-prompt notice keeps the user able to act.
  • Temporary: only Claude gets a folder grant and only Claude and Codex get the file; other agents keep the paste (or, on a Windows host, the line) for file-sized prompts until each is measured reading the file. Owner: this stack, after step 7; no issue filed yet.
  • Temporary: paired remote Orcas keep the typed budget and the paste until they advertise staging, file writing and a receipt this client can see. Owner: this stack, after step 7, with the remote wire work; no issue filed yet.
  • Intended: WSL is treated as a POSIX host whose files are written through the Windows share, instead of a separate WSL path.
  • Intended: when the staging folder looked usable but the write then failed (the folder broke between the check and the spawn), the launch is refused with the reason and Copy prompt rather than typed in full, and the folder counts as broken so the next launch is planned for main's delivery. A folder already unusable is not refused: the spawn reads the same answer the plan read and types main's line.
  • Temporary: the new agent tab, the AI buttons, notes sends and Fix checks are planned on the desktop, which does not read the staging-folder check. With that folder unusable (rare: this machine's temp folder unwritable, or a WSL distro's ~/.cache/orca broken), a line too long or multi-line to type:
    • from the new agent tab is typed as is, which is main's delivery there;
    • from an AI button, a note or Fix checks, where main pasted the prompt, is refused with Copy prompt (once, and the empty tab closes) instead of being typed raw. The launch asks for that with one optional spawn field, unstageableLine: 'refuse', which the terminal daemon alone reads. Main pasted it, so this is still worse than main: the user pastes it themselves.
    • A Claude or Codex prompt over 100,000 bytes there gets a launch file, which is refused with Copy prompt the same way.
    • Steps 6 and 7 (feat(source-control): start the AI buttons' agents through agent.launch (step 2 of 2) #23748, reworked) move desktop launches onto agent.launch, where the host's check applies and main's paste is planned up front, and remove this.
  • Temporary: the local terminal host used when the daemon is unavailable, and the SSH relay, type an agent line whose staging write failed in full with a notice, as step 2 does, even when the folder looked usable; only the terminal daemon refuses that case. Follow-up: stage before the spawn there too. Owner: this stack, after step 7; no issue filed yet.
  • Temporary: on a Windows host, Claude and Codex still get a launch file (a pointer in their history) on the paths where main typed the prompt and that shell was measured to damage or lose it. The better fix is to carry the real text as the agent's own argument on Windows, by resolving the agent's npm shim to node.exe or its native executable so no cmd hop re-parses the line. Owner: this stack, after step 7, together with the next entry; no issue filed yet.
  • Temporary: a bare agent name on Windows is judged as typed, because Orca does not know whether it resolves to a .cmd shim. Through a shim, PowerShell ", a trailing \ and %NAME% can be damaged on the typed paths, as on main. Follow-up: the execution host reports where each agent's command resolves. Owner: this stack, after step 7; no issue filed yet.
  • Intended: an SSH Windows host gets no launch file, because its relay may run the pane in WSL, where it writes none; such a launch falls back safely to the line or the path's paste, as main does. Follow-up: the relay writes the file into the distro and reports its default shell, so those hosts can take one. Owner: this stack, after step 7; no issue filed yet.
  • Intended: on a Windows host an AI button's, a note's or Fix checks' prompt is always pasted, as on main, even where the shell would carry the line exactly: the host cannot prove the agent is in front, so a carried prompt could never be confirmed, and these actions run on that confirmation. The new agent tab keeps the exact line there; its "couldn't confirm" result is silent, as main reported nothing.
  • Intended: a non-ASCII prompt typed into cmd is left to main's delivery (the line where main typed it, the paste where main pasted). Orca's cmd panes run chcp 65001 as main's do, but no QA row carried such a prompt into cmd.
  • Temporary: a WSL pane in a Windows workspace (the Windows shell setting is WSL, the workspace a Windows path) is not probed before planning, so with a broken distro folder a Claude or Codex prompt over 100,000 bytes gets a launch file that is refused, not planned around. Main never starts the agent at that size. Owner: this stack, after step 7; no issue filed yet.
  • Temporary: an agent that exits within about a second of starting gets no "exited at startup" notice, because the delivery check backs off to reads 2 s apart and can miss a process that short-lived (final QA: no notice 4 of 4 for an immediate or 1 s exit on zsh and tcsh; the notice appears at 8.3–8.6 s for an agent that lives 5 s). Nothing is typed, and the user sees the agent's own error in the pane, as on main, which never shows a notice here. Owner: this stack; a terminal-foreground change event instead of polling would remove it (no issue filed).

What the agent itself still shows

The agent receives the pointer sentence as its first message. So its own screen, its session history and resume list (Claude's /resume, Codex's history), and any title it writes from that message all show "The full task is in the file …".

Orca's views of that same history show it too, because they read the agent's transcript after the file is gone: the chat view of a terminal tab, and AI Vault's session list. The tab label and the sidebar agent row do not: they title the session with the prompt the launch file carried.

That happens for Claude and Codex only:

  • Only on the paths where main typed the prompt on the line (listed above), never for an AI button, Fix checks, a source-control action, or a notes or annotation send.
  • On macOS, Linux, WSL and SSH, for a prompt over 100,000 UTF-8 bytes.
  • On a Windows host, for a prompt that shell was measured to damage or lose (listed above).

How this differs from the previous version of this PR

  • Merged main with Stage long agent launch lines where they are typed, so they arrive whole (step 2 of 7) #23962 squashed (7f25413), then main b58f819:
  • Final review (ae59440):
    • On a Windows host, AI buttons, notes and Fix checks paste every prompt, as main did; previously a prompt the shell carried exactly rode the line, and an agent with no hook then never got its action run (P1-1). A new agent tab's "couldn't confirm" result is silent, as on main.
    • The receipt makes one read, feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257's predicate split into launched-agent and other, instead of a second read by process name; the read backs off from 250 ms to 2 seconds (P2-1).
    • unstageableLine comes from the rule's outcome for every caller whose paste main used, so a work item now sends it too (P2-2).
    • A draft is judged by the one measured Windows verdict, so only a measured damage pastes it (P3-2); Command Code's seeded status row no longer reads as delivered (P3-3); the comment sweep, the race and 0700 wording, and an owner on every Temporary entry.
    • Windows CI: three tests in cmd-startup-arg-quoting.win32.test.ts had failed on every run since d8cfcf3. Two expected one argument, but Claude's launch-file line has two (the folder grant, then the pointer, whole). One piped a non-ASCII prompt into cmd, which decodes piped input in its startup code page; the rule now leaves non-ASCII on cmd to main's delivery, and the harness no longer claims to measure it.
  • A host whose staging folder is unusable now gets main's line typed as is, instead of the spawn refusing the line the plan chose (stack QA 2a: agent.launch and worker start into a WSL workspace with a broken ~/.cache/orca, before or after its first use, were all refused, a 5-line prompt worse than main). The WSL folder is probed before planning, so the first launch plans like later ones. Only a write that fails in a folder that looked usable is refused.
  • The receipt no longer reports "The agent exited at startup" for a shell in front before the launch line ran (stack QA, slow shell startup, 3 of 3); it needs the agent itself seen in front first, and otherwise keeps reading.
  • An AI button, a note or Fix checks whose line the host cannot stage is refused with Copy prompt instead of typed raw (coordinator follow-up to stack QA 2a), through one optional spawn field the terminal daemon reads.
  • A refused new agent tab shows its notice once, without the red error panel, and its empty tab closes (stack QA 2: the notice showed twice and the tab stayed open).
  • A workspace in a WSL path is planned for the distro's shell, whatever the Windows shell setting (stack QA P8-6). Under the cmd setting a 25 KB Claude prompt used to get a cmd-quoted pointer that bash broke, losing the prompt while reporting success; it now stages on the line. A pane whose Windows shell is WSL is judged the same way, not by Git Bash's limits.
  • A Tab or bare carriage return rides the PowerShell line, where main delivered it exactly.
  • A host that can't write its staging folder is planned for main's delivery up front (the paste where main pasted, the line within the typed budget where main typed it), so a worker brief there is pasted as on main rather than refused. Only a folder that breaks between plan and spawn is refused with Copy prompt (stack QA P8-2), and the next launch plans around it.
  • The tab label and the sidebar agent row show a launch file's prompt, not the pointer sentence (stack QA 1.2). They read the agent's session title, which now goes through the same mapping as the status row.
  • When the agent exits at startup with the prompt on its command line, the notice says that, not "The agent started, but your prompt wasn't sent" (stack QA 1.3). The new message is in every language catalog.
  • The launch file's sensitive flag is removed. Its only producer was the orchestration worker brief, which Start orchestration workers with their brief on the launch command line (step 4 of 7) #24081 now sends through the same rule as ordinary text, so every file was minted with it off. It was never in a released build, so removing the wire field is safe.
  • Whether a host takes a launch file is now a host fact the one rule derives. An SSH Windows host and a paired Orca never get one, so a multi-line Claude or Codex prompt there falls back to the line or the paste instead of being refused.
  • A spawn routed to a daemon older than launch files is no longer refused; it attaches without the launch file or its command.
  • "Delivered" for a carried prompt now needs the agent's hook turn or a fresh read finding the agent in front. Previously the agent's composer looking ready counted on its own, so a shell's prompt could read as delivered.
  • A result that cannot be proven either way is reported as unconfirmed, with its own notice, instead of delivered.
  • The PowerShell a local pane is spawned as is a host fact, so on Windows PowerShell 5.1 a " or trailing \ gets a launch file (main broke it), and on 7 it rides the line (main carried it).
  • A Windows line is judged per shell from the stack QA's measured rows, not as one family held to cmd's limits. Previously PowerShell and Git Bash prompts that main delivered exactly (5-line, 20 KB, 8,191 characters, %NAME%, and on PowerShell 7 " and a trailing \) got a launch-file pointer.
  • A prompt the line can't carry exactly is pasted where main pasted it (AI buttons, notes and annotation sends, Fix checks, source-control actions) instead of riding a launch file, on every platform, so the agent's history shows your text. Previously nearly every such prompt on Windows, and any over 16,384 characters elsewhere, became a pointer.
  • The line's ceiling is 100,000 UTF-8 bytes, measured through staging, instead of 16,384 characters.
  • Host facts come from one function, and each caller names the paste it has, matching what main does on its path. Previously agentSession.create and phone quick commands refused a prompt they could not paste on a Windows host; they now keep it on the line, as main did.
  • A launch-file refusal reaches agent.launch as launch_file_unavailable, a launch that failed without effects; the desktop closes the tab it opened.
  • Launch-file folders name their writer, and the crash sweep never removes one a running writer owns. Previously any folder over 24 hours old was removed, including one a day-old agent was still reading.
  • The handle wait no longer times out while that tab's spawn is still running.
  • Removed: the early refusal for after-start agents in agent.launch, and the source-control preview's copy of the paste branch.
  • Merged Stage long agent launch lines where they are typed, so they arrive whole (step 2 of 7) #23962's heads e2a732e (feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 4523a4b: the agent-in-front read is scoped to the pane, so it no longer scans the whole machine), 2126cee (feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 f02309f, test-only), 57e7710 (feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257's long-poll agent.launch and origin/main 66799f7) and ef62954 (feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 now also proves the agent in front when it runs behind a wrapper such as /bin/sh; the receipt uses that same check), b865c7d (in tcsh and nu, a plain Orca agent line is typed as is instead of going through /bin/sh), and 22c4f3e (feat(agent-launch): keep long prompts off the launch line and paste them after readiness (step 1 of 7) #24257 judges that wrapper proof by when its capture began).

Size

240 files, +8,579/−1,787 against step 2: production 154 files +3,812/−935, tests 86 files +4,767/−852.

Testing

  • This merge (04559df): tc:node, tc:web and tc:cli pass through tc-gate; oxlint and the changed-code gate are clean; the reliability manifest, RPC catalog and localization catalogs pass; the daemon crossing check adopts release v1.4.221 (v40) under v42. The 155 test files this PR changes or that import its conflicted modules pass (2,049 tests), as do the protocol and build-contract tests. Cross-version wire: 20 of 21 files pass; the remaining one is test(cross-version): load releases that import a package main dropped #25731's new test expecting @streamparser/json to be uninstalled, which main's own package.json installs again since fix(feedback): optimize oversized screenshots without losing detail #23664; Carry an agent's first prompt on its command line instead of pasting it (step 3 of 7) #24080 touches neither, so I expect the same red on main (not yet run there).

  • Final-review commit (f29abc5): tc:node, tc:web and tc:cli pass through tc-gate; oxlint on its 43 changed files and the changed-code gate are clean. The 1,850 test files that import its changed modules up to three levels deep (899 renderer), run with a 120-second test timeout at load 83 to 153: 1,844 pass (20,037 tests), 3 are skipped by their own guards and 1 is a runtime spec that runs inside orca-runtime.test.ts; 2 failed. One was a load flake that passed on rerun. The other held two real failures: work-item tests that still expected the pre-review Windows behaviour. They now expect a submit-after-ready prompt pasted on a Windows host (as on the base) and a 3-line draft prefilled on PowerShell's line (measured exact); all 14 in that file pass.

  • Re-broken and watched fail, one at a time: the rule keeping the line for an AI button on Windows (6 tests), an unconfirmed new-tab result raising the notice, other counted as the agent having run, no back-off (the 30-second wait's read count), no read at the ready signal, the judge not split, the rule never asking to refuse an unstageable line, the work item or the new-tab plan dropping it, Command Code's seeded row counted, the draft judged by nothing, and non-ASCII on cmd counted exact.

  • Windows CI (package (windows)) had failed three cases in cmd-startup-arg-quoting.win32.test.ts on every run since d8cfcf3. On 0a43c0a it passes, with the whole PR Checks run green. Two cases expected one argument where Claude's launch-file line has two (the folder grant, then the whole pointer). The other two failures were the harness: it pipes the line into cmd, which decodes it in its startup code page whatever chcp says, so accents and CJK, in a prompt or in the home folder's path, arrived garbled while cmd split the line correctly. Those cases now keep to ASCII (the apostrophe, the space and % that cmd's quoting must survive). A non-ASCII prompt or path in a real cmd pane is not measured, and the rule leaves a non-ASCII prompt on cmd to main's delivery.

  • tc:node, tc:web and tc:cli pass.

  • oxlint on the changed code files and check:code-quality:changed against step 2 (212 files) are clean. This version adds no strings; the five localization checks passed on the previous version (the coverage audit crashed once when run straight after the others, and passed alone 4 of 4).

  • Main run, 610 named test files: 599 pass (7,881 tests), 4 are skipped by their own platform guards, and 7 are runtime spec files that run inside orca-runtime.test.ts.

  • Second run, the 229 further test files that import this version's changed modules (launch host, staging, launch-file writing, daemon session create, local spawn, the four main planners, the pane connect) up to three levels deep: all pass (2,367 tests). The earlier versions' wider sweep (1,231 further files) passed on the previous head.

  • That first run caught an import cycle: the new tab-close step imported the store from the terminal transport, which the store's terminal slice imports, so 4 renderer test files failed to load. The close now loads the store lazily; all 371 renderer test files in both lists then passed, and tc:web passed again.

  • The unstageableLine field (latest commit) has a test per hop: the new agent tab asks to refuse only for a submit-after-ready prompt; the pane's queued startup hands it to the transport; the transport sends it with the spawn; main's pty spawn keeps it only with the renderer's own command and a known value; and through a real in-process terminal daemon (client request, admission, session create) a refused line is refused with nothing typed, while the same spawn without the field creates the session to type it, as a daemon that predates the field does. A WSL case refuses a 25 KB line for a refusing caller and still types a short one.

  • For that commit: tc:node, tc:web and tc:cli pass, oxlint and the changed-code gate are clean, and the 1,525 test files that import its runtime changes up to three levels deep (755 of them renderer) ran: 1,517 passed (16,506 tests), 3 are skipped by their own guards, 1 is a runtime spec that runs inside orca-runtime.test.ts, and 4 that failed under load (load 38 to 53: a 15-second timeout in the sidebar card tests, and a pane count in the paired-reconnect test, which was in the previous round's load reruns too) passed on rerun (the paired one 3 of 3). Type-only additions to the store and contract types were left to the typecheck.

  • The receipt commit (latest) drives the real receipt through a slow shell startup (a startup sleep in front, the shell's prompt looking ready, the shell in front for three reads, then the agent): delivered, never exited. Re-breaking it (a shell read after readiness counted as exited, any non-shell process counted as the agent, a terminal exit counted as exited without proof, no deadline) fails its tests. tc:web, oxlint and the gate pass; the 24 test files that import the receipt pass (9 of them first timed out under load 69 and passed on rerun).

  • src/main/runtime/wsl-broken-launch-folder.test.ts drives the real path for stack QA 2a: agent.launch's plan with the WSL probe, the spawn's probe, and the terminal daemon's session create, with the distro folder broken before first use (Claude 25 KB, Claude 5-line, Grok 25 KB, Claude 120 KB: each typed as is) and after (the launch whose write fails after the probe found the folder is refused; the next ones are typed as is).

  • Each Windows row of the stack QA (cmd, PowerShell, Git Bash; Claude and Codex; built with the rig's own node stub.js command) is a test case in src/shared/windows-launch-line.test.ts: rows main delivered exactly ride the line or main's paste, and only rows main was measured to damage or lose get a launch file.

  • Neither run had an unhandled error. The first main run had one failure: under load, the real-shell test's 100,000-byte case read a truncated argv in zsh, dash and fish. The harness read the stub agent's capture 100 ms after it appeared, before the write finished. The stub now renames the capture into place once written; the same 24-file batch then passed twice. That failure depends on load and did not reproduce on demand with the old harness, so the fix is by construction, not shown by a re-break.

  • Each fix in this version was checked by re-breaking it and watching its test fail: readiness counting on its own, ESC[?2004l not revoking it, the refusal code, the tab close, a live writer's folder swept, the in-flight wait, the old 512-byte Windows budget, a no-paste caller refusing, a paired host judged by the client's platform, a Windows AI-button prompt filed instead of pasted (rule and caller), main's desktop paste ignored on a WSL host, the old 16,384 ceiling, the paste-first step limited to Windows, the ceiling counted in characters, the rule ignoring whether a host takes a launch file, an SSH Windows host taking one, an older daemon refusing or being sent the command, the wire parser still requiring the removed sensitive field, the Windows-wide rule put back (16 QA-row tests fail), PowerShell " counted as damaged, an AI button typing an unmeasured line, a Tab or carriage return typed, Git Bash held to cmd's limit, a spelled-out shim not recognised, the PowerShell ignored or pwsh counted as 5.1, a requested --shell ignored, the renderer leaving the PowerShell unknown, "auto" ignoring the pwsh.exe probe, session titles left unmapped on this desktop or over RPC, only a whole pointer recognised, the renderer sending no pane, a shell read counted as "not delivered", an exited agent getting the "wasn't sent" notice, a WSL path planned by the Windows setting (main and desktop), a WSL pane judged by Git Bash's limits, a Tab or carriage return counted as damage on PowerShell, an unstaged agent line typed in full, a WSL folder kept after a refusal, the rule ignoring a host that cannot stage, the host fact ignoring the folder check, and a refused write not remembered (WSL and this machine), the write site refusing a WSL line with no folder (5 tests fail), no probe before planning (the 120 KB row fails), the daemon refusing a staging failure in an unusable folder, the notice shown per spawn instead of per tab, a tab holding a live terminal closed, and the unstageableLine field dropped at each of its seven places (the new tab not asking, the pane startup, the transport request, main's pty spawn, the daemon client, the daemon's admission, and the daemon ignoring it): each makes its test fail.

  • tests/e2e, mobile/ and config reference none of the removed names.

  • Not measured (live runs were out of scope for this round): click to "delivered" against main. Expected, not proven: with hooks on, "delivered" fires at the agent's first turn, which needs no quiet window, while main's paste could start only after the agent turned bracketed paste on and then printed nothing for 1.5 seconds. Without hooks, an agent already working on its prompt may not go quiet, so the receipt can wait up to the readiness timeout (8 seconds by default) before its read, later than main.

  • Not run: live QA of this head. The plan's acceptance rows for this step need the coordinator's QA lanes:

    • click to prompt visible no later than main;
    • byte-exact prompts on cmd, PowerShell 5.1 (" and trailing \ now a launch file), PowerShell 7 and Git Bash, rerun against this head, with the "auto" PowerShell setting both with and without pwsh.exe installed, plus the unmeasured bands (PowerShell multi-line 512 bytes to 9 KB, Git Bash 9 KB to 20 KB) and a real npm-installed claude.cmd;
    • a multi-line AI-button prompt on Windows pasted after ready, as on main;
    • a 20 KB and a 100 KB Fix-checks prompt on macOS and Linux arriving as the user's text;
    • Claude and Codex accepting a 100,000-byte argv prompt (measured only with a stub agent);
    • a failed file write shows the notice, and the phone gets the reason;
    • each notice surface against main.

Visual Proof

Pending the coordinator's live QA for this step.

Notes

Author X handle: @BrennanKB5

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.
…h 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.
…unch-host-prompt-delivery

# Conflicts:
#	src/main/runtime/tui-idle-evidence.ts
…dows 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.
… 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.
…wn 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.
… 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.
…unch-host-prompt-delivery

# Conflicts:
#	src/main/ipc/desktop-renderer-runtime-capabilities.test.ts
…es' 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.
…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.
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.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/launch-prompt-on-command-line branch from 1245810 to 55fd9fe Compare October 2, 2026 06:40
@brennanb2025
brennanb2025 force-pushed the brennanb2025/launch-prompt-argv-carry branch from 9e24b10 to cb0d0fa Compare October 2, 2026 07:11
@brennanb2025 brennanb2025 changed the title Carry an agent's first prompt on its command line instead of pasting it (step 2 of 3) Carry an agent's first prompt on its command line instead of pasting it (step 3 of 7) Oct 2, 2026
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.
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.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/launch-prompt-argv-carry branch from cb0d0fa to de94a21 Compare October 2, 2026 07:33
… 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.
…ivery mean the agent got it

Round-1 review of the rebuilt step 3.

- One outcome, switched on by every caller. planLaunchPrompt answers none,
  on-line, launch-file or paste-after-ready; only the carried outcomes hold
  the plan to launch, so no caller can read "a plan exists" as "carried".
  buildAgentStartupPlan now builds only a prompt-less launch. The new agent
  tab and work items had dropped a prompt an agent could not take in a file
  (any agent but Claude and Codex) and reported it delivered; they now paste
  it. The composer paths, background sessions, agent.launch, agentSession.create
  and phone quick commands switch on the same outcome.
- One rule for hosts that type the line raw. A Windows host or a paired Orca
  keeps a line to #24257's 512-byte typed budget; anything longer, multi-line
  or damaged by the shell rides a launch file for Claude and Codex and is
  pasted for others. A paired host is one predicate (launchHostIsPaired).
- Delivered means the agent got it. A prompt the new tab's line carried
  resolves its delivery when the agent's own hook reports a turn or the
  agent shows its composer, and false when the PTY never spawns (a refused
  launch file) or exits first, so callers that resolve review threads or
  mark notes delivered wait for that, not for the tab.
- Daemon protocol v41 writes launch files. A spawn routed to an older daemon
  is refused with the launch-file reason instead of typing a pointer to a
  file nobody wrote.
- A refused launch file or WSL staged line reaches agent.launch as its reason
  instead of a 10 s handle timeout, and hands the prompt back with Copy prompt
  for a staged line too.
- A WSL distro is probed only when the spawn needs its directory, and a
  failed probe is remembered for 30 s.
- Every hook status row shows the prompt a launch file carries instead of its
  pointer sentence.
A host that cannot prove the launched agent holds its terminal (a Windows
host, a local WSL pane on one, SSH to Windows) refuses a paste, so the one
carry rule keeps such a prompt on the line, as main typed it; Claude and
Codex still take a launch file for a long or shell-damaged prompt.
launchHostProvesAgentInFront moves to its own module, since the typed-line
shell naming beside it is gone with #24257's typed-line check.
…nto brennanb2025/launch-prompt-on-command-line
…nto brennanb2025/launch-prompt-argv-carry

The foreground read keeps this branch's four-way verdict, on the shared launched-agent host the
worker brief check also reads.
brennanb2025 added a commit that referenced this pull request Oct 5, 2026
…nto brennanb2025/worker-brief-on-launch-line

A brief that rode the launch line still skips the paste; a pasted brief takes #24257's new check.
The local brief-check test uses Amp, whose brief is pasted here since an argv agent's rides the line.
…unch-host-prompt-delivery

Brings main to e2da3a1, which fixes main's own localization coverage audit; no conflicts.
brennanb2025 added a commit that referenced this pull request Oct 5, 2026
brennanb2025 added a commit that referenced this pull request Oct 5, 2026
…o the stack is one chain

#24934 was stacked on #24257; it now sits on #24081 (24257 -> 23962 -> 24080 -> 24081 -> 24934).
The prompt-delivery table keeps #24080's argv row and this branch's unconfirmed row. The restart
replay test pins its stub to the paste path, since #24080's fixture now reports an offered prompt
carried unless told otherwise.
…rompt-on-command-line

Main now carries #24257 (squashed as 761d63a) and #25579, so this PR's diff is its own changes
again. #24257's files are identical on both sides; no conflicts.
brennanb2025 added a commit that referenced this pull request Oct 5, 2026
@brennanb2025
brennanb2025 changed the base branch from brennanb2025/launch-prompt-on-command-line to main October 6, 2026 03:54
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e769b65a-fa5d-4010-b87e-d946052f3b4d
📥 Commits

Reviewing files that changed from the base of the PR and between 04559df and ce8fb5b.

📒 Files selected for processing (11)
  • .github/workflows/pr.yml
  • src/main/runtime/runtime-pty-controller-contract.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/lib/tui-agent-startup.test.ts
  • src/shared/runtime-types.ts
  • src/shared/startup-command-staging.node-pty.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/fr.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The pull request adds host-aware prompt delivery through command arguments, launch files, or post-ready paste. It adds launch-file validation, secure writing and cleanup, WSL launch-directory probing, daemon protocol support, PTY and relay propagation, and prompt-receipt tracking. Renderer paths now display localized notices for delivery failures. Session titles and agent status prompts resolve launch-file pointers back to remembered prompt text. Related tests cover Windows quoting, WSL behavior, protocol compatibility, staging failures, and delivery outcomes.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to ce8fb

Some managed-worktree launches that require file transport can pass a pointer to a file that was never written, so the initial prompt may not reach the agent. Fix this before merging.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 04559

Task text previously delivered after startup can now become visible through process listings on shared machines. Concurrent launches can also leave an unused private task file behind. Private-file protections limit exposure, but the confidentiality and cleanup implications warrant attention.

Retained concerns

  • Medium · security · inferred: Previously paste-only submission paths now place task text in process arguments when command-line delivery is selected. On shared execution hosts where other users can inspect arguments, or where monitoring records command lines, sensitive task content gains an additional reader surface. Shell quoting and self-deleting startup scripts preserve execution integrity but do not conceal the resulting arguments. This is a conditional confidentiality concern; existing argv-based launch paths predate the PR.
  • Low · security · inferred: A local spawn creates its prompt file before acquiring the per-ID spawn reservation. If another same-ID request wins during preflight, the losing request returns a successful reattachment without deleting its file or registering its own exit cleanup. The new private prompt file consequently remains without a session owner, and stale sweeping preserves it while the writer process is alive. This introduces unnecessary sensitive-data retention; private permissions bound the exposure rather than fixing the ownership failure.
Security review details

Security Blast Radius

  • inferred — The identified exposure is execution-host-local: task text in visible process arguments and unused task files retained on that host. Argument disclosure may require only an unprivileged local account where OS policy allows inspection; reading protected leftover files requires access permitted by the file or directory controls. Deployed account isolation and monitoring policies were not supplied.

Security Findings and Attack Paths

  • inferred — A submitted work-item task can flow from draftContent through host-aware planning into a quoted argv argument. On a host exposing command lines, another permitted reader can obtain that text without accessing the terminal or private launch files. The brief establishes the prior paste-only behavior for this path; source confirms the new routing. Existing background argv launches are counterevidence against treating every command-line exposure as newly introduced.

Trust Boundaries and Controls

  • observed — The inspected relay path preserves its existing terminal ownership boundary: owner identity comes from request context, while launch-file input is data rather than an ownership claim. The relay writes a generated host-local path and substitutes it into the existing caller-controlled command. Unsupported WSL launch-file delivery is refused. The outer transport authentication implementation was not independently verified.

Resilience and Maintainability Implications

  • inferred — Concurrent same-ID local requests are serialized only after each may have created a prompt file. The successful reattachment terminal state does not establish a cleanup owner for the losing request's file. Ordinary exception and physical-exit cleanup therefore do not cover that file, and live-PID recovery rules prolong retention even after the winning terminal exits.

Hardening Proposals

  • proposed — Provide a confidentiality-aware delivery policy for sensitive task text, selecting guarded post-start delivery or a supported private-file transport rather than relying on command-line exactness as the sole selection criterion.
  • proposed — Create the local prompt file only after winning the reservation, or retain cleanup ownership until session activation explicitly accepts it. Successful reattachment should release any file created by the losing request.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.48% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 69 functions across 73 files. (7 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: carrying an agent’s first prompt on its command line instead of pasting it. It is specific and relevant.
Description check ✅ Passed The description explains the problem, behavior changes, rationale, scope, implementation, and extensive testing. Visual proof is still marked pending, and the description does not use several template…
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 43.48% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 69 functions across 73 files. (7 skipped: 7 unsupported.)

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

@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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Forward startup.startup.launchFile to… · orca-runtime-terminal-create-deduplication.ts:163-172

src/main/runtime/orca-runtime-terminal-create-deduplication.ts:163-172
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Forward startup.startup.launchFile to createTerminal.

buildStartupForAgent now returns a launchFile when the prompt exceeds the line ceiling. The command then names only a placeholder pointer. launchAgentTerminal does not pass launchFile, so the host never writes the file. The agent receives a pointer to a nonexistent path, and the prompt is lost. Also, a followup returned for a paste-after-ready plan is dropped here.

Proposed fix
       startupCommandDelivery: startup.startup.startupCommandDelivery,
+      ...(startup.startup.launchFile ? { launchFile: startup.startup.launchFile } : {}),
       telemetry: startup.startup.telemetry,

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: df9e31f8-60ca-4e3a-a6b2-7090d8ce7106
📥 Commits

Reviewing files that changed from the base of the PR and between 7f25413 and f5ed44e.

📒 Files selected for processing (256)
  • .github/workflows/pr.yml
  • config/scripts/pr-code-change-scope.mjs
  • src/main/agent-hooks/first-work-branch-rename.test.ts
  • src/main/agent-hooks/first-work-branch-rename.ts
  • src/main/agent-hooks/first-work-rename-runtime.ts
  • src/main/agent-hooks/launch-file-prompt-by-pane.ts
  • src/main/agent-hooks/server-launch-file-prompt.test.ts
  • src/main/agent-hooks/server/server-status-update.ts
  • src/main/agent-launch/__fixtures__/host-agent-startup-call-sites.txt
  • src/main/agent-launch/agent-launch-executor.test.ts
  • src/main/agent-launch/agent-launch-executor.ts
  • src/main/agent-launch/agent-launch-prompt-delivery.ts
  • src/main/agent-launch/agent-launch-surface-factories.ts
  • src/main/agent-launch/host-agent-startup-attribution.test.ts
  • src/main/ai-vault/session-title-launch-prompt.ts
  • src/main/daemon/daemon-create-or-attach-request.ts
  • src/main/daemon/daemon-foreground-confirmation-protocol.test.ts
  • src/main/daemon/daemon-launch-file-protocol.test.ts
  • src/main/daemon/daemon-protocol-version.test.ts
  • src/main/daemon/daemon-protocol-version.ts
  • src/main/daemon/daemon-pty-session-spawn.ts
  • src/main/daemon/daemon-pty-spawn-request.ts
  • src/main/daemon/daemon-pty-spawn-result.ts
  • src/main/daemon/daemon-pty-startup-delivery.test.ts
  • src/main/daemon/daemon-spawn-result-identity.ts
  • src/main/daemon/daemon-terminal-admission.ts
  • src/main/daemon/daemon-unstageable-line.test.ts
  • src/main/daemon/session.ts
  • src/main/daemon/terminal-host-create-contract.ts
  • src/main/daemon/terminal-host-session-create.ts
  • src/main/daemon/terminal-host-startup-staging.test.ts
  • src/main/daemon/terminal-host-wsl-launch-directory.test.ts
  • src/main/daemon/terminal-host.test.ts
  • src/main/daemon/types.ts
  • src/main/daemon/unstaged-agent-line-refusal.ts
  • src/main/ipc/ai-vault-session-title-routing.test.ts
  • src/main/ipc/ai-vault-session-title-routing.ts
  • src/main/ipc/pty/ipc/launched-agent-foreground.ts
  • src/main/ipc/pty/ipc/spawn-launch-refusal.test.ts
  • src/main/ipc/pty/ipc/spawn-options-launch-file.test.ts
  • src/main/ipc/pty/ipc/spawn-options.ts
  • src/main/ipc/pty/ipc/spawn-types.ts
  • src/main/ipc/pty/ipc/spawn.ts
  • src/main/ipc/pty/register-handlers.ts
  • src/main/ipc/pty/runtime/spawn-options.ts
  • src/main/ipc/pty/runtime/spawn-state.ts
  • src/main/ipc/qoder-independent-renderer-spawn-review.test.ts
  • src/main/ipc/worktree-remote.ts
  • src/main/opencode/opencode-model-startup-plan.test.ts
  • src/main/opencode/opencode-model-startup-plan.ts
  • src/main/providers/local-launch-artifact-directory.test.ts
  • src/main/providers/local-launch-artifact-directory.ts
  • src/main/providers/local-pty-launch-plan.ts
  • src/main/providers/local-pty-provider-shell-readiness.test.ts
  • src/main/providers/local-pty-session-activation.ts
  • src/main/providers/local-pty-spawn-wsl-shutdown-order.test.ts
  • src/main/providers/local-pty-spawn.ts
  • src/main/providers/pty-provider-contract.ts
  • src/main/providers/ssh-plain-shell-pty-provider.test.ts
  • src/main/providers/ssh-plain-shell-pty-provider.ts
  • src/main/providers/ssh-pty-spawn-request.ts
  • src/main/providers/startup-line-typed-length.live-shell.test.ts
  • src/main/providers/wsl-launch-directory-resolution.test.ts
  • src/main/providers/wsl-launch-directory-resolution.ts
  • src/main/pwsh.ts
  • src/main/runtime/agent-launch-typed-line-shell.test.ts
  • src/main/runtime/agent-launch-typed-line-shell.ts
  • src/main/runtime/agent-session-launch-startup.ts
  • src/main/runtime/agent-terminal-startup-prompt.test.ts
  • src/main/runtime/claude-agent-teams-shim-env.test.ts
  • src/main/runtime/claude-agent-teams-shim-env.ts
  • src/main/runtime/launched-agent-foreground.test.ts
  • src/main/runtime/launched-agent-foreground.ts
  • src/main/runtime/orca-runtime-activate-managed-worktree.ts
  • src/main/runtime/orca-runtime-agent-session-operation.test.ts
  • src/main/runtime/orca-runtime-agent-teams-launch-plan.ts
  • src/main/runtime/orca-runtime-create-agent-session.ts
  • src/main/runtime/orca-runtime-create-managed-worktree.ts
  • src/main/runtime/orca-runtime-create-runtime-owned-mobile-session-terminal.ts
  • src/main/runtime/orca-runtime-create-terminal-desktop.test.ts
  • src/main/runtime/orca-runtime-create-terminal-desktop.ts
  • src/main/runtime/orca-runtime-create-terminal.ts
  • src/main/runtime/orca-runtime-resolve-mobile-session-terminal-command.ts
  • src/main/runtime/orca-runtime-restore-live-paired-renderer-session-owned-mobile-terminals.ts
  • src/main/runtime/orca-runtime-run-create-mobile-session-terminal.ts
  • src/main/runtime/orca-runtime-terminal-create-deduplication.ts
  • src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-12.spec.ts
  • src/main/runtime/qoder-terminal-command-resolution.test.ts
  • src/main/runtime/rpc/methods/agent-launch-caller-selection.test.ts
  • src/main/runtime/rpc/methods/agent-launch-failure-code.test.ts
  • src/main/runtime/rpc/methods/agent-launch-failure-code.ts
  • src/main/runtime/rpc/methods/agent-launch-prompt-carry.test.ts
  • src/main/runtime/rpc/methods/agent-launch-surfaces.ts
  • src/main/runtime/rpc/methods/agent-launch-terminal-prompt.ts
  • src/main/runtime/rpc/methods/agent-launch-worktree-creation.ts
  • src/main/runtime/rpc/methods/agent-launch.test-fixture.ts
  • src/main/runtime/runtime-agent-launch-resolution.ts
  • src/main/runtime/runtime-agent-terminal-startup.ts
  • src/main/runtime/runtime-ai-vault-commands.test.ts
  • src/main/runtime/runtime-ai-vault-commands.ts
  • src/main/runtime/runtime-folder-worktree-create.ts
  • src/main/runtime/runtime-local-worktree-terminal-startup.ts
  • src/main/runtime/runtime-managed-worktree-create-types.ts
  • src/main/runtime/runtime-pty-controller-contract.ts
  • src/main/runtime/runtime-remote-managed-worktree-create.ts
  • src/main/runtime/runtime-store-contract.ts
  • src/main/runtime/runtime-terminal-contracts.ts
  • src/main/runtime/runtime-worktree-agent-startup.test.ts
  • src/main/runtime/runtime-worktree-agent-startup.ts
  • src/main/runtime/terminal-foreground-group.test.ts
  • src/main/runtime/terminal-foreground-group.ts
  • src/main/runtime/terminal-launch-refusals.test.ts
  • src/main/runtime/terminal-launch-refusals.ts
  • src/main/runtime/this-orca-launch-host.test.ts
  • src/main/runtime/this-orca-launch-host.ts
  • src/main/runtime/wsl-broken-launch-folder.test.ts
  • src/main/runtime/wsl-pane-launch-carry.test.ts
  • src/preload/api/pty-api.ts
  • src/preload/api/pty-bridge-session-control.ts
  • src/preload/api/pty-bridge-stream-and-serialization.ts
  • src/relay/pty-handler-startup-command-staging.test.ts
  • src/relay/pty-handler.ts
  • src/renderer/src/components/right-sidebar/buildSourceControlAgentDeliveryPlan.ts
  • src/renderer/src/components/right-sidebar/useSourceControlAgentActionStart.ts
  • src/renderer/src/components/sidebar/folder-workspace-agent-startup.ts
  • src/renderer/src/components/sidebar/folder-workspace-composer-submit.test.ts
  • src/renderer/src/components/sidebar/folder-workspace-composer-submit.ts
  • src/renderer/src/components/terminal-pane/ipc-pty-connect.ts
  • src/renderer/src/components/terminal-pane/ipc-pty-spawn-request.ts
  • src/renderer/src/components/terminal-pane/pty-connection-types.ts
  • src/renderer/src/components/terminal-pane/pty-connection/pane-startup-command-options.ts
  • src/renderer/src/components/terminal-pane/pty-connection/pty-input-recovery.ts
  • src/renderer/src/components/terminal-pane/pty-transport-launch-refusal.test.ts
  • src/renderer/src/components/terminal-pane/pty-transport-types.ts
  • src/renderer/src/components/terminal-pane/refused-agent-launch-tab.ts
  • src/renderer/src/hooks/composer-state/composer-submit-orchestration.ts
  • src/renderer/src/hooks/composer-state/full-creation-startup.test.ts
  • src/renderer/src/hooks/composer-state/full-creation-startup.ts
  • src/renderer/src/hooks/composer-state/full-submit-preparation.ts
  • src/renderer/src/hooks/composer-state/quick-creation-execution.ts
  • src/renderer/src/hooks/composer-state/quick-startup-plan.ts
  • src/renderer/src/hooks/ipc-events/terminal-request-ipc-bridge.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/lib/agent-background-draft-delivery.ts
  • src/renderer/src/lib/agent-draft-readiness.test.ts
  • src/renderer/src/lib/agent-draft-readiness.ts
  • src/renderer/src/lib/agent-launch-caller-permissions.test.ts
  • src/renderer/src/lib/agent-launch-caller-placement.test.ts
  • src/renderer/src/lib/agent-launch-caller-prompt-transport.test.ts
  • src/renderer/src/lib/agent-launch-platform.test.ts
  • src/renderer/src/lib/agent-launch-platform.ts
  • src/renderer/src/lib/agent-launch-prompt-delivery.ts
  • src/renderer/src/lib/agent-launch-prompt-not-delivered-notice.test.ts
  • src/renderer/src/lib/agent-launch-prompt-not-delivered-notice.ts
  • src/renderer/src/lib/agent-launch-prompt-receipt.test.ts
  • src/renderer/src/lib/agent-launch-prompt-receipt.ts
  • src/renderer/src/lib/agent-paste-draft.ts
  • src/renderer/src/lib/agent-startup-delayed-delivery.ts
  • src/renderer/src/lib/ai-vault-tab-title-requests.ts
  • src/renderer/src/lib/ai-vault-tab-title-sync.test.ts
  • src/renderer/src/lib/ai-vault-tab-title-sync.ts
  • src/renderer/src/lib/command-code-prompt-status-seed.ts
  • src/renderer/src/lib/composer-agent-startup-plan.ts
  • src/renderer/src/lib/launch-agent-background-session-remote.test.ts
  • src/renderer/src/lib/launch-agent-background-session.ts
  • src/renderer/src/lib/launch-agent-in-new-tab-windows-quoting.test.ts
  • src/renderer/src/lib/launch-agent-in-new-tab.test.ts
  • src/renderer/src/lib/launch-agent-in-new-tab.ts
  • src/renderer/src/lib/launch-agent-new-tab-prompt-delivery.test.ts
  • src/renderer/src/lib/launch-agent-new-tab-prompt-delivery.ts
  • src/renderer/src/lib/launch-agent-paste-timeout-notice.ts
  • src/renderer/src/lib/launch-agent-startup-prompt-plan.ts
  • src/renderer/src/lib/launch-file-host.test.ts
  • src/renderer/src/lib/launch-file-host.ts
  • src/renderer/src/lib/launch-work-item-direct-agent-routing.ts
  • src/renderer/src/lib/launch-work-item-direct-agent.test.ts
  • src/renderer/src/lib/launch-work-item-direct-agent.ts
  • src/renderer/src/lib/launch-work-item-direct-route-preparation.ts
  • src/renderer/src/lib/launch-work-item-direct.test.ts
  • src/renderer/src/lib/launch-work-item-direct.ts
  • src/renderer/src/lib/local-pwsh-availability.ts
  • src/renderer/src/lib/new-workspace.test.ts
  • src/renderer/src/lib/new-workspace.ts
  • src/renderer/src/lib/pending-worktree-creation.ts
  • src/renderer/src/lib/runtime-agent-background-create.ts
  • src/renderer/src/lib/source-control-agent-action-plan.test.ts
  • src/renderer/src/lib/source-control-agent-action-plan.ts
  • src/renderer/src/lib/ssh-background-startup-delivery.test.ts
  • src/renderer/src/lib/ssh-background-startup-delivery.ts
  • src/renderer/src/lib/tui-agent-startup.test.ts
  • src/renderer/src/lib/tui-agent-startup.ts
  • src/renderer/src/lib/worktree-creation-background-mount.test.ts
  • src/renderer/src/lib/worktree-creation-flow-ready-toast.test.ts
  • src/renderer/src/lib/worktree-creation-flow-startup.ts
  • src/renderer/src/lib/worktree-creation-flow-stranded-surface.test.ts
  • src/renderer/src/lib/worktree-creation-flow.test.ts
  • src/renderer/src/lib/worktree-startup-payload.ts
  • src/renderer/src/store/terminals/terminal-actions.ts
  • src/renderer/src/store/terminals/terminal-state.ts
  • src/renderer/src/web/preload-api/web-terminal-api.ts
  • src/shared/agent-launch-runtime-capability.ts
  • src/shared/ai-vault-session-title.ts
  • src/shared/claude-agent-teams-tmux-compat.ts
  • src/shared/cmd-startup-arg-quoting.win32.test.ts
  • src/shared/dsh-startup.test.ts
  • src/shared/flag-prompt-startup.ts
  • src/shared/freebuff-launch.test.ts
  • src/shared/freebuff-startup.test.ts
  • src/shared/launch-file-directory-grant.test.ts
  • src/shared/launch-file-writing.test.ts
  • src/shared/launch-file-writing.ts
  • src/shared/launch-host.test.ts
  • src/shared/launch-host.ts
  • src/shared/launch-prompt-carrier.test.ts
  • src/shared/launch-prompt-carry.test.ts
  • src/shared/launch-prompt-carry.ts
  • src/shared/launch-prompt-file.ts
  • src/shared/launch-prompt-plan.test-fixture.ts
  • src/shared/local-build-compatibility-contract.json
  • src/shared/local-build-compatibility-contract.ts
  • src/shared/omp-fresh-launch-shell.test.ts
  • src/shared/omp-fresh-launch.test.ts
  • src/shared/opencode-startup-prompt.test.ts
  • src/shared/qoder-agent.test.ts
  • src/shared/qoder-cn-launch-recipe.test.ts
  • src/shared/rpc-contract/ai-vault-params.ts
  • src/shared/runtime-terminal-contracts.ts
  • src/shared/runtime-terminal-create-request.ts
  • src/shared/runtime-types.ts
  • src/shared/startup-command-staging.node-pty.test.ts
  • src/shared/startup-command-staging.test.ts
  • src/shared/startup-command-staging.ts
  • src/shared/startup-line-prompt-carry.test.ts
  • src/shared/startup-line-prompt-carry.ts
  • src/shared/tui-agent-config-types.ts
  • src/shared/tui-agent-config.ts
  • src/shared/tui-agent-prompt-transport.test.ts
  • src/shared/tui-agent-resume-startup.ts
  • src/shared/tui-agent-startup-hermes.test.ts
  • src/shared/tui-agent-startup-shell.ts
  • src/shared/tui-agent-startup.test.ts
  • src/shared/tui-agent-startup.ts
  • src/shared/typed-startup-line.ts
  • src/shared/windows-launch-line.test.ts
  • src/shared/windows-launch-line.ts
  • src/shared/windows-shell-prompt-damage.test.ts
  • src/shared/worktree/launch-types.ts
  • src/shared/wsl-launch-directory.test.ts
  • src/shared/wsl-launch-directory.ts
  • src/shared/wsl-login-shell-command.ts
  • src/shared/zcode-orchestration-contract.test.ts
💤 Files with no reviewable changes (15)
  • src/renderer/src/lib/worktree-creation-flow-ready-toast.test.ts
  • src/shared/tui-agent-resume-startup.ts
  • src/renderer/src/lib/worktree-creation-background-mount.test.ts
  • src/main/runtime/agent-launch-typed-line-shell.test.ts
  • src/main/providers/startup-line-typed-length.live-shell.test.ts
  • src/renderer/src/lib/ssh-background-startup-delivery.test.ts
  • src/renderer/src/lib/worktree-creation-flow.test.ts
  • src/renderer/src/components/sidebar/folder-workspace-agent-startup.ts
  • src/shared/flag-prompt-startup.ts
  • src/renderer/src/lib/command-code-prompt-status-seed.ts
  • src/main/runtime/runtime-store-contract.ts
  • src/shared/startup-line-prompt-carry.test.ts
  • src/renderer/src/lib/ssh-background-startup-delivery.ts
  • src/main/runtime/agent-launch-typed-line-shell.ts
  • src/shared/startup-line-prompt-carry.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

Comment thread src/relay/pty-handler.ts
Comment on lines +2117 to +2121
const launchFile = writeRelayLaunchFile(params.launchFile, command, spawnEnv, shell)
if (launchFile) {
command = launchFile.command
Object.assign(spawnEnv, launchFile.env)
}

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
rg -nP -C4 'export (async )?function (applyRelayAgentWorkspaceTrust|getRelayShellLaunchConfig)' src/relay

Repository: stablyai/orca

Length of output: 1531


🏁 Script executed:

printf '%s\n' '--- pty-handler launch/spawn flow ---'
sed -n '2070,2290p' src/relay/pty-handler.ts
printf '%s\n' '--- workspace trust implementation ---'
sed -n '1,240p' src/relay/agent-workspace-trust-spawn.ts
printf '%s\n' '--- shell launch implementation ---'
sed -n '1,220p' src/relay/pty-shell-launch.ts
printf '%s\n' '--- launch-file writer/remover definitions and uses ---'
rg -n -C3 'writeRelayLaunchFile|removeLaunchFile|LaunchFile' src/relay/pty-handler.ts src/relay

Repository: stablyai/orca

Length of output: 29579


🏁 Script executed:

printf '%s\n' '--- workspace-trust operation ---'
rg -n 'applyWorkspaceTrustOnThisHost|function applyWorkspaceTrust' src/main/execution-host-workspace-trust.ts
nl -ba src/main/execution-host-workspace-trust.ts | sed -n '1,300p'
printf '%s\n' '--- launch-file helper ---'
nl -ba src/shared/launch-file-writing.ts | sed -n '1,220p'
printf '%s\n' '--- handler cleanup and ownership transfer ---'
nl -ba src/relay/pty-handler.ts | sed -n '2110,2325p'
printf '%s\n' '--- relevant base-to-head changes ---'
git diff --unified=8 761d63a4e52ff7915d4432c004df13182db63010 f5ed44e2220c4d8f27b236aac72e21ed6b9a7ca1 -- src/relay/pty-handler.ts | rg -n -C12 'writeRelayLaunchFile|applyRelayAgentWorkspaceTrust|removeLaunchFile|launchFile' || test "$?" -eq 1

Repository: stablyai/orca

Length of output: 36255


🏁 Script executed:

printf '%s\n' '--- history-injection bindings ---'
rg -n -C3 'injectRelayFishHistoryEnv|injectRelayHistoryEnv' src/relay
printf '%s\n' '--- relevant history helper declarations and bodies ---'
rg -l 'function injectRelay(Fish)?HistoryEnv|const injectRelay(Fish)?HistoryEnv' src/relay

Repository: stablyai/orca

Length of output: 13694


🏁 Script executed:

printf '%s\n' '--- history-injection bindings ---'
rg -n -C3 'injectRelayFishHistoryEnv|injectRelayHistoryEnv' src/relay
printf '%s\n' '--- history helper source files ---'
rg -l 'function injectRelay(Fish)?HistoryEnv|const injectRelay(Fish)?HistoryEnv' src/relay

Repository: stablyai/orca

Length of output: 13674


🏁 Script executed:

nl -ba src/relay/terminal-history.ts | sed -n '1,175p'

Repository: stablyai/orca

Length of output: 6586


🏁 Script executed:

nl -ba src/relay/terminal-history.ts | sed -n '1,175p']]

Repository: stablyai/orca

Length of output: 213


🏁 Script executed:

printf '%s\n' '--- trust-request parser ---'
rg -n -C35 'parseAgentWorkspaceTrustSpawnRequest' src/shared/agent-workspace-trust-spawn-request.ts
printf '%s\n' '--- shell startup feature implementation ---'
rg -n -C45 'export function (selectShellStartupFeatures|encodeShellStartupFeatures)' src/main/shell-startup-features.ts
printf '%s\n' '--- inherited ZDOTDIR helpers ---'
rg -n -C35 'export function (inheritedZdotdirEnv|resolveInheritedZdotdir)' src/main/zsh-wrapper-dir-ownership.ts

Repository: stablyai/orca

Length of output: 5959


🏁 Script executed:

printf '%s\n' '--- startup staging binding ---'
rg -n -C35 'function stageStartupCommand|export function stageStartupCommand' src/shared src/relay
printf '%s\n' '--- wireAndStore binding ---'
rg -n -C45 'wireAndStore\(' src/relay/pty-handler.ts

Repository: stablyai/orca

Length of output: 18723


🏁 Script executed:

nl -ba src/shared/startup-command-staging.ts | sed -n '1,185p']

Repository: stablyai/orca

Length of output: 213


🏁 Script executed:

nl -ba src/shared/startup-command-staging.ts | sed -n '1,185p'

Repository: stablyai/orca

Length of output: 9116


Clean up the launch file if startup staging throws.

After pty.spawn, provider-delivered commands may reach stageStartupCommand before wireAndStore takes ownership of the launch file. stageStartupCommand calls randomBytes(8) before its write-error try; if that synchronous call fails, the error escapes without removing the launch file.

🐛 Suggested fix
     if (managed.startupCommand?.providerDelivery && managed.startupCommand.command) {
-      managed.stagedStartupCommand = stageStartupCommand({
-        command: managed.startupCommand.command,
-        shellPath: shell,
-        orcaBuiltLine: launchAgent !== undefined
-      })
+      try {
+        managed.stagedStartupCommand = stageStartupCommand({
+          command: managed.startupCommand.command,
+          shellPath: shell,
+          orcaBuiltLine: launchAgent !== undefined
+        })
+      } catch (error) {
+        removeLaunchFile(launchFile)
+        throw error
+      }

Source: Learnings

…ch-prompt-argv-carry

Conflicts resolved against #23962's head this branch had merged (83de487), so each keeps
main's squashed #23962 plus this branch's change:
- daemon protocol v42 (launch files), v41 (#23962's staged startup lines) now in PREVIOUS;
  build compatibility contract and pinned version tests follow.
- agent-draft-readiness keeps main's OpenCode agent-row grace (#25101) and this branch's
  bracketed-paste-off revocation of the quiet window.
- startup-line-prompt-carry.test.ts stays deleted; its rule lives in launch-prompt-carry.

Semantic follow-ups from #23672 (CR submission, one-line PowerShell quoting):
- windowsLaunchLineVerdict reads line breaks from the prompt too: PowerShell now quotes a
  multi-line prompt onto one physical line, which hid the measured multi-line rows (a 9 KB
  multi-line prompt read as exact) and the shim's line-break cut.
- Typed launch lines end in CR; raw multi-line lines submit each break with CR; the PowerShell
  multi-line expectations use the one-line backtick form.
- A duplicated pair of #23962 staging tests left by the auto-merge is removed.
- agent-paste-draft: the two identical launch-paste tails share sendLaunchPaste; tui-agent-config
  writes devin on one line like jcode (main sat at the line cap).
- Takes main's deletion (#25791) of the host startup call-site inventory, its attribution test and
  daemon-protocol-version.test.ts; v42 stays pinned by daemon-foreground-confirmation-protocol and
  the build-contract test, and launch files by daemon-launch-file-protocol.
- #25720's custom-launch-command test plans its prompted launch with this branch's planner, which
  keeps the custom command on the line.
@brennanb2025
brennanb2025 marked this pull request as draft October 6, 2026 17:19
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Parked as a draft: launch-point unification (all agent launches through agent.launch: #24934, #25430, #25624) is being rebuilt directly on main so it doesn't wait on delivery changes. This PR changes how the first prompt reaches the agent, and it comes back after unification lands, as its own PR on top of it.

brennanb2025 added a commit that referenced this pull request Oct 6, 2026
… 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.
brennanb2025 added a commit that referenced this pull request Oct 6, 2026
… written when the surface exists (#24934)

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.
brennanb2025 added a commit that referenced this pull request Oct 7, 2026
…ed (#25430)

* feat(agent-launch): host-assigned caller identity and a launch record 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.

* feat(agent-launch): show an agent's tab at once, where the caller asked 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.

* style(runtime): one-line the tab-order map so the headless browser-tabs 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 added a commit that referenced this pull request Oct 7, 2026
…agent.launch (#25624)

* feat(agent-launch): host-assigned caller identity and a launch record 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.

* feat(agent-launch): show an agent's tab at once, where the caller asked 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.

* feat(agent-launch): the desktop AI buttons start their agent through 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.

* style(runtime): one-line the tab-order map so the headless browser-tabs 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.

* fix(agent-launch): watch an AI button's agent for readiness from its 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.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing this large older launch stack and rebuilding exact first-prompt delivery at the current way agents are started. Create an issue for long prompts and Windows quoting before closing.

The remaining problem is tracked in #27676.

brennanb2025 added a commit that referenced this pull request Oct 16, 2026
… message carries (#24488)

* fix(quick-commands): save agent prompts whole, bounded by what a chat message carries

Agent quick-command prompts were silently cut to 6,000 characters on save:
the editor accepted a long paste, settings normalization trimmed it, and
reopening showed the shortened copy. The 6,000 was a Windows launch speed
threshold, not a limit on prompts.

- Storage no longer trims. A save is refused, never trimmed, when a prompt's
  chat text block is over 256 KiB of JSON (measured on its real escaped size,
  the measure the chat send schema applies) or the list is too large for one
  paired reply. Host and desktop store both check before writing.
- The editor shows the size against the limit and blocks Save past it, and
  stays open with the text when the host refuses the save.
- terminal.quick-commands.long-prompts.v1: hosts withhold prompts over 6,000
  characters from clients that do not advertise it (older clients reject a
  whole list holding one); editors keep an older host's 6,000 cap.
- The phone editor no longer drops pasted text past 6,000 via maxLength.

Launch delivery of long prompts is left to the launch-prompt series
(#24257, #23962, #24080); this branch's earlier paste-after-ready delivery
duplicated it and is removed.

* fix(quick-commands): drop a stale duplicate doc comment

* fix(quick-commands): advertise long prompts to this build's own host too

* fix(quick-commands): keep submitted editor immutable until save settles

* test(quick-commands): keep storage and save lifecycle coverage focused

* fix(quick-commands): keep portalled choices within pending form ownership

* Preserve retained credentials when settings validation refuses a save

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
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