Skip to content

fix(agent-launch): settle launch readiness on the agent's own composer - #23452

Closed
brennanb2025 wants to merge 9 commits into
mainfrom
brennanb2025/agent-input-ready-wait
Closed

brennanb2025 wants to merge 9 commits into
mainfrom
brennanb2025/agent-input-ready-wait

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 26 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1318 $\color{#cf222e}{\Huge{\mathbf{−}}}$​20 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1298
Prod 30 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​460 $\color{#cf222e}{\Huge{\mathbf{−}}}$​35 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​425

ELI5

When Orca starts an agent by itself and has a task to hand it (an orchestration worker, orca worktree create --agent X --prompt "...", a launch from the phone, a startup draft), it waits until the agent's input box is up and then pastes the task in. goose never gave Orca a signal it recognised, so Orca waited out its whole budget and the task was never delivered. Now an agent can declare how its own input box shows that it is ready, backed by a recorded screen of that agent. Orca uses that declaration only when it can prove the agent (not the shell) is drawing the screen, which needs the shell integration Orca launched the pane with to mark when a command starts. Every agent without a declaration, and every pane whose shell integration gives no such mark, behaves exactly as before.

What Changed

Before:

  • An orchestration worker, an agent.launch prompt, or a startup draft for goose waited its full budget (60 s for a worker) and failed with Agent did not become ready (timeout), or the draft was dropped.
  • orca worktree create --agent goose --prompt "..." typed prompt\r into the pane as soon as the process name matched: no wait for the input box, no check for a dialog on screen, and each newline typed as a keystroke.

After:

  • Two agents declare their launch readiness in the agent config (launchReadiness), each backed by a captured screen that a test replays:
    • goose: composer-quiet, meaning it switched on its own paste mode (bracketed paste) and then went quiet for 3 s. Capture goose-composer-ready.
    • OpenCode: composer-marker, meaning its show-cursor after paste mode, which it sends only once its input row is mounted. Capture opencode-composer-ready.
  • Codex declares nothing. Its OpenAI Codex header, read from the output text or, since fix(runtime): read Codex readiness from the live screen #23475, from the live screen, is already strong evidence that settles every recorded Codex launch. Codex draws its › input glyph while that header still says loading, so a second Codex rule could only disagree with the header rule.
  • For a declared agent in a terminal this launch created, once the shell has marked the agent's command as started (OSC 133;C), the declared evidence stands in for the weak "the title names the agent" signal. goose workers, agent.launch prompts, startup drafts and worktree-create follow-ups now reach it. An OpenCode launch no longer settles on its OpenCode title, which it paints before its input box exists.
  • Every other agent keeps the base behaviour on every path (worker, federated worker, agent.launch, worktree follow-up, worktree draft). That includes Codex, Claude Code (claude, Agent Teams, OpenClaude), aider, and the "type after start" agents that have not been recorded. Those still report "not delivered" until each one is recorded and declared. aider is the motivating case: in manual permission mode it asks Add .aider* to .gitignore (recommended)? (Y)es/(N)o on a fresh worktree, switches paste mode on for that question too, and a paste would answer it.
  • A startup draft or worktree-create follow-up for a declared agent, in a pane whose Orca shell integration marks the startup command, always goes through the launch-readiness wait, whatever output has or has not reached Orca yet. Previously a follow-up could type a two-line prompt into goose while it was still loading, which submitted the first line alone and lost the second.
  • Every other pane keeps the base delivery for startup drafts and follow-ups, decided up front with no added wait: fish of any version (Orca's fish integration writes only its ready marker; fish 4's own marks are not Orca's), every SSH pane (Orca does not know which shell the remote host launched), Windows shells, and any shell Orca does not wrap, such as sh, ksh or dash. The draft pastes on the agent's own marker or 1.5 s after paste mode goes quiet, within the agent's draft budget, without Enter. The follow-up is typed as prompt\r once the process name matches.
  • A reused terminal (a worker, federated worker or agent.launch pointed at a running agent) is still asked "is the agent idle", never "is its input box up": its agent may be mid-turn.
  • A plain orca terminal wait --for tui-idle behaves exactly as before.

Mechanism: #22927 made tui-idle one ranked evaluator: blocked > strong ready > working > weak ready, where weak evidence settles only on the 2 s poll, after a read of the rendered screen finds no dialog.

  • The declaration. launchReadiness?: 'composer-marker' | 'composer-quiet' in tui-agent-config.ts. composer-marker reads the marker from the agent's existing draftPasteReadySignal. composer-quiet reads paste mode followed by quiet. A test pins the declared set to the two captured agents. A second test requires every composer-marker agent to have a marker and no quiet-window fallback, so paste mode alone never counts as its input box.
  • Ownership. The watch reads output only between the shell's OSC 133;C (command started) and its next 133;D / 133;A (command finished / prompt drawn). The shell turns paste mode on for its own prompt too, so output outside that window never counts. Outside it the watch reads unowned.
  • The evaluator rule. Consider a launch wait on a freshly created terminal, for a declared agent whose command owns the PTY. There the declared evidence replaces the weak name-only-title rank. Otherwise the wait is exactly the base tui-idle. Blocked, strong ready (including fix(runtime): read Codex readiness from the live screen #23475's Codex header on the live screen) and working (a working title or a first-party working status) still win. A title saying the agent waits on a permission answer also still wins. The rendered-screen check still gates every weak settle.
  • Opt-in per wait. waitForTerminal gets an internal acceptComposerReady option. Only launch waits set it, and only when that launch created the terminal. It is not part of the terminal.wait RPC, so there is no wire change. The idle poll entry owns the watch, so the watch dies with the waiter.
  • Whether a pane's startup command is marked, decided when Orca spawns it. The provider that picks the shell wrapper records it on the spawn result (startupCommandMarked), and the runtime keeps it on the terminal's record. shell-startup-command-marks.ts is the one rule: zsh is marked where Orca's wrapper for that host writes OSC 133 (local and daemon yes, the SSH relay's no), bash is marked when its startup command is typed at its prompt (not when the wrapper's prompt hook runs it, which bash before 5.1 leaves unmarked), everything else is not.
    • Daemon terminals (the default): main answers from the same shell and feature selection the daemon's launch plan makes, so nothing new crosses the daemon connection.
    • Local terminals without the daemon: the local provider answers from the launch config it actually used, including a wrapper it could not write.
    • SSH terminals: left unknown, which means base delivery. A pane whose own shell setup writes marks (for example a user's shell-integration script) is also base.
  • Startup draft and follow-up: one decision each, made before anything is written.
    • Declared agent in a marked pane: the launch-readiness wait delivers. It already waits for the command-start mark before the declared evidence counts.
    • Anything else: the base delivery, exactly as before.
    • Draft: the launch-readiness wait pastes within the draft's own budget plus the new check's extra confirmation time (its 3 s quiet and one 2 s poll, less the old 1.5 s).
    • Follow-up: the text goes through the runtime's agent-prompt writer (sendTerminalAgentPrompt: bracketed paste, per-PTY serialisation, per-agent submit timing), after the same launch-readiness wait agent.launch uses.
    • Exactly one branch runs, so the text can never be delivered twice.
  • Why not read the marks from the output. Local terminals run in Orca's terminal daemon by default. Its output travels on a separate stream connection, batched on a short timer, while the foreground-process check is a request answered at once on the control connection. The previous revision decided from "has this pane shown a mark yet" at the moment the check found goose, and in live QA on zsh that still fell to the base branch: the mark had not reached Orca. Whether the mark will come is known when the wrapper is chosen, so nothing has to be read or waited for.

Why

  • Declared per agent, not a generic rule. An earlier revision let any agent without a known screen settle on "paste mode on, then quiet". Paste mode also comes on for first-run questions, such as aider's .gitignore prompt and Claude Code's trust and bypass-permissions dialogs. So that rule could paste a task into a question while reporting it delivered. A declaration backed by a recorded screen makes each agent's evidence reviewable. It also removes the per-agent exceptions added during review, because an agent without a declaration simply keeps the base behaviour.
  • Codex left to its header rule. In all four Codex 0.157 recordings that fix(runtime): read Codex readiness from the live screen #23475 added, a launch wait gives Codex exactly the plain verdict, with or without a declaration. The one place a declaration changed anything was a screen size that garbles Codex's header, where fix(runtime): read Codex readiness from the live screen #23475 deliberately keeps the plain answer; the declaration settled there on a glyph drawn while the header still said loading.
  • One rank instead of a race. The declared evidence goes through the one ranking and the one rendered-screen check that every other weak signal uses. It is not a second readiness decision racing tui-idle.
  • Decided at spawn, not inferred from output. Output arrival order across the daemon's two connections is a race; which wrapper Orca launched is not. A grace period for the mark would still race under load and would delay shells that never mark. For the daemon, main derives the answer from what it asked the daemon to spawn, so no daemon or relay wire field is added; SSH panes stay on base delivery until the relay reports it.
  • Base delivery where ownership cannot be proven. Without a command-start mark, Orca cannot tell the agent's paste mode from the shell's. The new signal therefore has nothing to stand on there, and the base delivery (which those shells relied on) stays.
  • Not taken: reclassifying individual agents to pass their task on the command line. That is planned per agent, after the transport change that keeps the prompt out of the typed command line.

Linked Issue

No tracking issue. Part of the agent launch prompt delivery series; stacked on #22927, whose evaluator this plugs into. The base now includes #23475 (Codex readiness from the live screen).

Visual Proof

Live QA on a separate Mac: the Orca desktop app launched hidden from dev builds of this PR at f924105c31 and of its base, #22927 at 83aa3aa98f (which includes #23475). Each round used its own user-data folder, HOME and git repo, and each arm was checked against its compiled main bundle. Real agents: goose 1.52.0 (dummy key, unreachable endpoint), Claude Code in a folder it has never trusted, Codex 0.157.1 (unreachable provider) and OpenCode 1.18.32. Each agent was started as an orchestration worker, then goose was launched through orca worktree create --agent goose --prompt with a two-line prompt.

Launch base 83aa3aa98f (zsh) this PR f924105c31 (zsh, 3 rounds)
goose worker Agent did not become ready (timeout) after 60 s; goose's input box was up, brief never delivered ready in 4.7 s / 4.6 s / 4.7 s; brief delivered as one message
goose, worktree create --prompt (two lines) typed at the zsh prompt before goose's banner, then left unsent in goose's input both lines arrive together as one submitted message after goose is ready, nothing typed at the shell prompt (3/3)
Codex worker ready in 3.7 s (the base's live-screen check) ready in 3.7 s (unchanged; Codex is not declared)
OpenCode worker ready in 6.6 s, delivered ready in 6.7 s twice and 34.7 s once, delivered (the slow run did not repeat; cause unknown)
Claude worker, untrusted folder blocked on the trust dialog in 2.1 s same; dialog untouched at the end of the round
Claude first run with --dangerously-skip-permissions timeout, "Bypass Permissions" dialog untouched same; nothing pasted into it

Unmarked shell (SHELL=/bin/ksh, which Orca launches unwrapped): every worker matched the base (goose timeout, Codex and OpenCode ready, Claude blocked). The goose follow-up takes the base code path on both arms; the base's typed write lands before or after goose switches its terminal to raw input depending on timing, so one base round showed it typed at the prompt and unsent while both PR rounds showed it submitted as one message.

The earlier revision a385580027 decided the follow-up from the marks that had arrived by the time the agent's process appeared, and on zsh it failed live in 2/2 rounds (one typed early, one submitted only line one and lost line two); that is what the spawn-time decision replaces.

Screenshots

goose two-line follow-up on zsh, this PR (rounds 1 and 2):
Z-PR-3-S5.jpg
Z-PR-4-S5.jpg

goose two-line follow-up under /bin/ksh, this PR (base code path):
K-PR-2-S5.jpg

The earlier split at dd4775c75b (only line one submitted):
pr-6-S5.jpg

goose worker, base (60 s later, brief never arrived) / this PR at e6d33aca47:
base-3-S1.jpg
pr-3-S1.jpg

Claude "Bypass Permissions" dialog at the end of the round, base / this PR:
base-5-S6-late.jpg
pr-5-S6-late.jpg

Not run live: Windows, WSL, SSH, fish, the worktree-create startup draft (no CLI or local-app surface sends one; covered by runtime specs), and aider (undeclared; its captured transcript pins that a launch wait gives it exactly the plain verdict).

Testing

  • I manually tested these changes locally (live Electron QA on a separate Mac on earlier revisions; see Visual Proof)
  • Automated tests added/updated, or explained why not below

Tests at f924105c31:

  • shell-startup-command-marks.test.ts pairs each shell launch with the wrapper bytes Orca writes for it: local and daemon zsh are marked and their wrappers write 133;C; the SSH relay's zsh wrapper writes none and is unmarked; bash is marked when its startup command is typed and not when the wrapper's prompt hook runs it; fish's init writes no mark and is unmarked; unwrapped shells (sh, ksh, dash, or a local zsh/bash whose wrapper could not be written) and Windows shells are unmarked. A daemon pane whose wrapper write failed is still counted as marked, because main cannot see that result; there the base follow-up also fails, since the daemon types the agent command only after its 15 s shell-ready wait.
  • daemon-pty-adapter-startup-command-marks.test.ts (a real in-process daemon): a startup command under zsh or bash reports marked, under fish or ksh unmarked; a pane without a startup command reports nothing.
  • local-pty-provider-shell-readiness.test.ts (local provider without the daemon): zsh marked for goose and for Codex's wrapper-run startup; bash marked for goose, unmarked for Codex's prompt-hook startup; sh unmarked.
  • spawn-commit-startup-command-marks.test.ts: the provider's answer (true or false) reaches the runtime's spawn reply, and an absent answer stays absent.
  • agent-composer-ready-transcripts.test.ts replays real captured screens through the runtime, each preceded by the 133;C mark.
  • agent-composer-ready-watch.test.ts pins the declared set (goose, OpenCode). It requires every composer-marker agent to have a marker and no quiet window. It also covers: undeclared agents (aider, Claude, Agent Teams, Codex, Grok, opencode2) get no watch; ownership starts at 133;C and ends at 133;D / 133;A; a mark split across chunks; replay of output from before the wait subscribed.
  • tui-idle-evidence.test.ts: while the declared agent owns the PTY, its evidence replaces the name-only title (held while pending, settles on ready). unowned and plain waits keep the title. The evidence loses to working and permission titles, a blocking prompt and a first-party working status.
  • runtime-worktree-startup-readiness.test.ts, per path:
    • Draft: goose in an unmarked pane and aider in a marked one paste after exactly 1.5 s of quiet without asking the launch wait, even though their output carries 133;C. A declared goose or OpenCode in a marked pane hands over to the launch wait before any output arrives, with the draft budget plus confirmation time (11.5 s, 23.5 s), and pastes once. An unready wait drops the draft.
    • Follow-up: goose in an unmarked pane and aider in a marked one type fix it\r on the process match. A declared goose in a marked pane goes through the launch deliverer with both lines of a two-line prompt, with no raw write, even with no mark received 4.5 s in.
  • worktree-setup-and-startup-part-06.spec.ts (runtime, goose, spawn result carrying the provider's answer):
    • Draft, marked pane: not pasted when a 1.5 s quiet window alone would have fired (7.9 s), pasted once tui-idle confirms, inside the extended budget.
    • Draft, marked pane whose shell arms paste mode for its own prompt before goose's command-start mark arrives (after the base quiet window would have fired): nothing pasted into the shell; pasted once goose's composer is confirmed.
    • Draft, unmarked pane whose output nonetheless carries 133;C: pasted at exactly 1.5 s.
    • Follow-up, marked pane: one bracketed paste, never fix it\r.
    • Follow-up, marked pane where the OS reports goose in the foreground before its command-start mark, and the mark arrives only after the base's whole 4.5 s poll (the live zsh failure): nothing typed at the process match or at 4.5 s; the two-line prompt arrives as one bracketed paste after the mark and the input box.
    • Follow-up, unmarked pane whose output carries 133;C: the two-line prompt typed as prompt\r on the process match.
  • The worktree-create specs in parts 03-05 are the base branch's versions unchanged (Codex and OpenCode drafts and the aider follow-up, spawn results without the new field), and pass.
  • Reused versus launched terminals: terminal-agent-prompt-delivery.test.ts, agent-launch-executor.test.ts, worker-start-readiness-question.test.ts and federation-agent-launch.test.ts pin acceptComposerReady: false for a reused terminal and true for a launched one.

Ablations at f924105c31 (part removed, targeted suites run, part restored):

Removed Red
the pane's capability at the decision (declared agents always take the new path) 5: both unmarked unit tests, both unmarked runtime specs, and a base-branch OpenCode draft spec
the declaration at the decision (marked panes always take the new path) 2: aider draft and follow-up in a marked pane
recording the provider's answer on the terminal record 4: all four marked-pane runtime specs, including the late-mark follow-up and draft
zsh reading its host's wrapper spec (every zsh marked) 1: the SSH relay zsh row
bash's prompt-hook exception 2: the mapping row and the local provider's Codex-under-bash case
fish unmarked (fish marked) 2: the mapping row and the daemon's fish case
unwrapped shells unmarked 2: the mapping row and the local provider's sh case
the local provider passing its prompt-hook delivery 1: Codex under bash
the daemon adapter reporting at all 4: every daemon shell case
the daemon answer depending on the shell 2: fish and ksh
the spawn commit carrying the field 1
the runtime spawn reply carrying the field 1
the draft's added confirmation time 3: both unit budget tests and the marked-pane runtime draft

The runtime "wait for the mark" machinery from a385580027 (the watch's "any mark seen" record and its ownership waiters) is removed, so its ablation rows no longer apply.

Earlier ablations at dd4775c75b (still apply, except the two decision-point rows, which the rows above replace):

Removed Red
goose's declaration 16: goose transcripts, the pin, the watch tests, both goose paths
OpenCode's declaration 6: the pin, the OpenCode marker replay and title hold, the OpenCode draft handover
no declaration needed (the old generic rule) 7: the undeclared-agent watch tests, aider draft and follow-up stay base
the 133;C ownership gate 8: the no-mark watch, transcript, draft and follow-up tests, and a base Codex draft spec
replacing the title rank while owned 2: the evaluator test and the OpenCode transcript hold
the composer rank 9
the permission-title guard 1
the draft decision point 4
requiring the mark at the decision 6, including 2 base-branch specs (Codex drafts without marks)
the follow-up decision point 2
the draft's added confirmation time 3
subtracting the time the base already spent 1
a declared agent with a quiet-window fallback (Grok declared composer-marker) 3, including the no-quiet-window check

Gates at f924105c31: targeted vitest (245 files, 3696 tests, including orca-runtime.test.ts, the provider, daemon adapter and PTY IPC suites, #23475's Codex transcript suite and the serializer replay suite), pnpm tc, oxlint on changed files (including max-lines), check:code-quality:changed clean. The codex-composer-ready capture is removed with Codex's declaration; #23475's Codex captures replace it.

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • User-visible timing: a goose launch settles about 3-5 s after the agent's last paint (3 s quiet window plus up to one 2 s poll). An OpenCode startup draft in a marked shell pastes after one rendered-screen read once its marker appears, instead of on the marker byte itself. An OpenCode worker now becomes ready at its input box rather than at its title, which is later by the gap between the two.
  • Startup draft budget: when the launch-readiness wait decides, the draft's per-agent budget (8 s for goose, 20 s for OpenCode) grows by the new check's extra confirmation time (11.5 s, 23.5 s in total). So the latest a draft can land stays the same time after the agent settles, and unsent text still cannot arrive long after start, in the middle of what the user is typing. The follow-up keeps the 60 s launch budget.
  • Uncaptured agents stay "not delivered": goose was the motivating case for this PR; every other "type after start" agent (aider, amp, kilo, devin, muse, and the rest) keeps the base behaviour until its first run is recorded and a declaration is added with a replayed capture. aider's manual-mode .gitignore question is why this is not generic.
  • Which panes count as marked: local terminals run in Orca's terminal daemon by default, which types the startup command at the shell's first prompt once the shell reports ready; zsh and bash of any version, including macOS /bin/bash 3.2, mark that command's start there. Without the daemon, the local provider types it the same way, except a Codex startup command under bash, which the wrapper's prompt hook runs and bash before 5.1 leaves unmarked, so that pane counts as unmarked (Codex is undeclared anyway). A daemon that cannot write its wrapper files runs the shell unwrapped while main still counts it as marked; a declared agent there is reported "not delivered" rather than typed into.
  • Unmarked or unknown panes (fish, every SSH pane, Windows shells, and shells Orca does not wrap such as sh, ksh, dash) keep base delivery for startup drafts and worktree-create follow-ups, decided up front with no added wait. A pane whose own shell setup writes marks (for example a user's shell-integration script) is also base, because Orca did not launch those marks. For worker and agent.launch waits the evaluator still reads the marks in the output, so an unmarked shell gets exactly tui-idle, as on the base. Windows behaviour is unchanged by this PR.
  • Follow-up (not in this PR): make Orca's own shell integrations that lack it (the SSH zsh wrapper, fish, the prompt-hook bash startup path) emit the command-start mark, and have the SSH relay report its wrapper's answer as an optional spawn-reply field (older relays: unknown, base), so those panes get the declared-agent behaviour too.
  • Where failures land: a worktree-create follow-up or draft that is not delivered is still only logged in the main process, as before. A user-visible notice is a separate follow-up.
  • Not this PR: Codex's lost Enter on a first-ever launch is pre-existing and is handled in fix(agent-prompt): send Codex's retry Enter for a just-launched worker's brief #23044. aider under a shell that auto-titles panes (for example oh-my-zsh) settles on its title before its input box on the base too.
  • Mixed versions: no wire change. acceptComposerReady, launchReadiness and startupCommandMarked are in-process (the daemon's answer is derived in main from what main asked it to spawn), and terminal.wait params and replies are unchanged.
  • SSH / remote: SSH panes have no answer from main, which does not know the remote shell, so their startup drafts and follow-ups are exactly base, including relay bash, which does mark commands (see the follow-up above). Worker and agent.launch waits on SSH still read the marks in the output through the runtime's own PTY data stream, as before this revision.
  • Serializer suite: opencode-composer-ready is pinned there as a known divergence (24) from the earlier revision; one OpenCode checkpoint fails only with the add-on from fix(terminal): serialize only the visible width after a column shrink #22586, a serializer regression this capture exposes and not caused by this PR.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: needs product decisions before merge (head 594b84fa43)

Four review rounds, an architecture review, and start/end readiness checks ran on this PR. The goal of each: deliver an agent's launch text exactly once, only after its input box is up, and never into a dialog or the shell prompt. Plain terminal wait --for tui-idle must stay unchanged.

Fixed during review (pushed)

  • dac7143b2f: a worktree-create startup draft could paste as soon as OpenCode-style agents (Codex, OpenCode, opencode2, mimo-code, Grok, ZCode) set their name title, before their input box was drawn. For those agents a launch wait now holds until their own input-box marker renders.
  • dac7143b2f: launches into a reused terminal no longer treat "input box is up" as ready. There the question is whether the agent is idle, and a quiet but busy agent could otherwise get the brief mid-turn.
  • dac7143b2f, e6d33aca47: Claude Code agents (claude, claude-agent-teams, openclaude) no longer use the new signal. They switch on paste mode before their trust and "Bypass Permissions" dialogs, so a paste plus Enter could pick "No, exit". Their readiness still comes from their own title, and their launch text rides the command line.
  • dac7143b2f, 594b84fa43: the startup draft is back on its per-agent budget. It is extended by exactly the extra time the new ready check takes to confirm, so a draft the base delivered is not dropped on a slow start.

Live QA (separate Mac, dev builds of the base 92482496be and this PR at e6d33aca47, fresh profile and repo per round, two rounds per arm)

Launch base this PR
goose worker timeout after 60 s; input box up, brief never arrived ready in 4.7 s / 4.7 s, brief delivered as one message
Codex worker (no Orca hooks in the rig) timeout after 60 s ready in 3.7 s, brief landed after the input box was up
OpenCode worker ready 6.6-6.7 s, delivered ready 6.7 s, delivered (unchanged)
Claude worker, untrusted folder blocked on the trust dialog in 2.2 s same; dialog untouched at the end of the round
Claude first run with --dangerously-skip-permissions timeout, "Bypass Permissions" dialog untouched same; nothing pasted into it
goose, worktree create --prompt with two lines lines typed at the shell prompt before goose started one message, after goose's input box was up

The startup-draft path was not reachable live without a raw RPC to a remote runtime; the draft budget commit (594b84fa43) only touches that path and is covered by unit tests.

Screenshots

goose worker, base (60 s later, brief never arrived) / this PR:
base-3-S1.jpg
pr-3-S1.jpg

Codex worker, base / this PR:
base-3-S3.jpg
pr-3-S3.jpg

goose worktree create --prompt, base (typed at the shell first) / this PR:
base-3-S5.jpg
pr-3-S5.jpg

OpenCode worker, this PR:
pr-3-S4.jpg

Claude trust dialog at the end of the round, this PR:
pr-3-S2-late.jpg

Claude "Bypass Permissions" dialog at the end of the round, base / this PR:
base-5-S6-late.jpg
pr-5-S6-late.jpg

Open: needs a decision before merge

  1. Agents without a recorded input-box screen use a generic "paste mode on, then 3 s quiet" rule. Two reviewers found independently that aider in manual permission mode (without --yes-always) asks Add .aider* to .gitignore (recommended)? (Y)es/(N)o on a fresh worktree. Its input library switches paste mode on for that question too, and the dialog check has no yes/no pattern. So the brief would be pasted into the question and lost, while the launch reports it delivered. On the base these launches timed out and reported "not delivered". The same generic rule covers every agent whose screens have not been recorded.
    • Suggested direction (from the architecture review): declare one launch-readiness source per agent in the agent config (input-box marker, or paste-mode-then-quiet), backed by a recorded screen for each. Agents with no declaration keep exactly the base behaviour. This also replaces the per-agent exceptions added during review with one declared field.
    • The cost: undeclared "type after start" agents stay at "not delivered" until each one is recorded.
  2. Shells that emit no command marks now report the startup draft and the worktree-create follow-up as not delivered where the base delivered them. The base pasted on the agent's own marker, or typed on a process-name match, without proof that the agent owned the terminal. Affected shells are fish, zsh on SSH hosts (the relay's zsh wrapper emits no marks), and local bash older than 5.1. Example: a phone "create from task" draft to Codex on an SSH host running zsh. This is the same question as the pending Windows decision (paste without ownership proof, or report not delivered), so it should be decided with it.
  3. Windows behaviour without command marks: still pending, unchanged by this review.

Not caused by this PR (noted for follow-up)

  • aider under a shell that auto-titles panes (for example oh-my-zsh) settles on its title before its input box; the base does the same.
  • A captured untrusted-folder Codex screen reads as ready under plain tui-idle on the base too.

Checks

Targeted vitest suites (including the transcript replays and the serializer replay suite), pnpm tc, oxlint, and check:code-quality:changed are clean at 594b84fa43. Every new test was seen red with the code it protects removed.

@brennanb2025
brennanb2025 force-pushed the brennanb2025/blocked-prompt-rendered-screen branch from 9248249 to 83aa3aa Compare September 27, 2026 23:49
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-input-ready-wait branch from dd4775c to 679583b Compare September 28, 2026 00:35
@nwparker

Copy link
Copy Markdown
Contributor

@brennanb2025 heads-up on an overlap, and a suggestion for a second agent to prove the abstraction against.

#23374 (@Alex-wangyang) fixes the same class of bug for ZCode: a freshly launched worker shows its composer, but startLocalWorker waits on generic tui-idle, which never settles, so the first task is never delivered. You touch three of the same files — local-worker-start.ts, runtime-worktree-startup-readiness.ts, and its test.

That PR is non-draft, narrow (6 files), and fixes a bug in a released build, so I'd expect it to land first. Its gate is deliberately tiny — agent === 'zcode' && !params.terminal — which should be straightforward to lift into your declaration mechanism and then delete.

ZCode is probably a useful second case for your design, because it fails for a reason goose may not. From the harness work on #22464:

  • ZCode emits no OSC title in any state — the recorded transcript at src/main/runtime/__fixtures__/zcode-composer-ready.txt contains no OSC 0/1/2 at all.
  • It repaints its ASCII banner indefinitely, so no quiet-render window ever settles. The same fixture is still repainting ~175KB after the composer mounts.
  • It paints the composer once, so the evidence scrolls out of the line-folded idle tail — zcode-readiness-transcript.test.ts asserts that as a negative.
  • Its SessionStart hook fires on the first turn, not at launch, so hook-derived readiness cannot unblock a first dispatch.

So ZCode is an agent with no title, no quiescence, and no durable tail evidence. If your declaration mechanism handles that alongside goose, it's likely general enough for most of the roster. The recorded transcript is already committed, so the evidence your design wants exists.

One thing I'd check: your description says the declaration is used "only when it can prove the agent (not the shell) is drawing the screen," via shell-integration command-start marks. ZCode's composer signal is currently anchored on the alternate-screen switch (\x1b[?1049h) with revocation on \x1b[?1049l, for the same reason grok's is — a powerline shell prompt can draw the same glyph. Worth confirming the two gating approaches compose, or that yours supersedes it cleanly.

Happy to help wire ZCode into the declaration model once yours lands.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: decisions implemented, reviews clean, live QA passing (head f924105c31, rebased on #22927 at 83aa3aa98f)

Follow-up to the earlier status comment. The three decisions it asked for are now implemented.

What changed since the last status

  • Declared launch readiness. Each agent declares how Orca knows its input box is up (launchReadiness in the agent config), backed by a captured screen that a test replays. Declared: goose (paste mode on inside the launched command, then quiet) and OpenCode (its own input-box marker). Codex is no longer declared, because fix(runtime): read Codex readiness from the live screen #23475's live-screen check in the base now settles it, and on one grid size the declaration disagreed with it. Every undeclared agent keeps the base behaviour exactly, on every launch path. That includes aider, whose manual-mode .gitignore (Y)es/(N)o question was the motivating case, and Claude Code agents. Uncaptured "type after start" agents stay "not delivered" until each one is recorded.
  • No regression on shells without command marks. The worktree-create startup draft and follow-up decide from a fact Orca records when it spawns the pane: whether the shell integration Orca launched for it writes command marks. Local and daemon zsh/bash with the command typed at the prompt are marked. Orca's SSH zsh wrapper, fish, bash running the command from its prompt hook, unwrapped shells (sh, ksh) and Windows shells are unmarked. On unmarked shells, and for undeclared agents, the base code runs unchanged. On marked shells, declared agents always take the new path.
  • Why a spawn-time fact. An intermediate revision (a385580027) inferred this from the marks that had arrived when the agent's process appeared. On the terminal daemon, the foreground-process check answers before the batched output stream delivers the marks. Live on zsh it fell back to base typing in 2/2 rounds: once typed early, once submitting only the first line of a two-line prompt. The spawn-time fact removes that race.
  • Windows is unchanged from the base; it is still gated on the pending decision.

Review

Three fresh review rounds after the decisions (on dd4775c75b, a385580027 and f924105c31) all returned clean. The only follow-ups were wording fixes, which are applied: fish before 4.0 vs later, and daemon wrapper-write failures. Start and end readiness checks pass with no P0, P1 or P2 findings. CI is green on f924105c31 (34 passed, 12 skipped).

Live QA at f924105c31 (separate Mac, dev builds, fresh profile/HOME/repo per round)

Launch base 83aa3aa98f this PR
goose worker (zsh) timeout after 60 s, brief never delivered ready in 4.6-4.7 s, delivered as one message (3/3)
goose two-line worktree create --prompt (zsh) typed at the shell prompt before goose started, left unsent both lines as one submitted message after goose is ready, nothing at the shell prompt (3/3)
Codex worker ready 3.7 s ready 3.7 s (unchanged)
OpenCode worker ready 6.6 s ready 6.7 s twice, 34.7 s once (did not repeat; cause unknown), delivered each time
Claude, untrusted folder / first-run bypass dialog blocked / timeout, dialogs untouched same
/bin/ksh (unmarked) workers: goose timeout, Codex/OpenCode ready every worker matches the base; the follow-up takes the base code path

Screenshots are in the PR body under Visual Proof.

Remaining, not blocking

  • Not run live: Windows, WSL, SSH and fish. The worktree startup draft also wasn't run live, because no CLI or local-app surface sends one; runtime specs cover it.
  • Follow-up, not in this PR: have Orca's own shell integrations that lack the command-start mark (the SSH zsh wrapper, fish, the daemon-fallback bash path) emit it, so those shells get the declared-agent behaviour. Also report the remote shell over SSH so SSH bash panes can use it too.
  • Not caused by this PR:
    • aider under a shell that auto-titles panes settles on its title before its input box is up.
    • The base follow-up's own typed write is timing-dependent: it lands at the prompt or in the agent's input depending on timing, and that is what unmarked shells keep.

@brennanb2025
brennanb2025 marked this pull request as ready for review September 28, 2026 03:02

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes — initial review at f924105c, full 56-file diff across all 9 commits.

  • Per-agent launch readiness declaration. launchReadiness: 'composer-marker' | 'composer-quiet' in tui-agent-config.ts, declared by opencode and goose only and pinned by agent-composer-ready-watch.test.ts; undeclared agents keep plain tui-idle.
  • Composer watch. agent-composer-ready-watch.ts feeds the existing draft scanner only between the shell's OSC 133 C (command started) and the next D/A, so the shell's own bracketed paste can never count; chunk-split marks are carried, and the replay path is re-owned at the byte level.
  • Evaluator tier 3b. tui-idle-evidence.ts replaces the name-only title with composer evidence while the command owns the PTY; blocked, strong-ready, working and permission signals still outrank it, and the rendered-screen check still gates every weak settle.
  • Ownership decided at spawn, not inferred from output. shell-startup-command-marks.ts plus the daemon/local derivations carry startupCommandMarked through PtySpawnResult → spawn commit/reply → the PTY record; the draft/follow-up path branches once on it, so no delivery can run twice.
  • Reused-terminals guarded everywhere. acceptComposerReady is threaded as true only for a terminal the launch created (agent.launch, local/federated worker, draft, follow-up) and false for a reused terminal, verified per path.
  • Captured-screen tests. Real goose/opencode/aider/claude/Codex transcripts are replayed with fake timers, and the worktree specs pin the marked/unmarked draft and follow-up paths, including the late command-start mark.

ℹ️ Nitpicks

  • config/scripts/ci-shard-timings.json still keys the renamed test as src/main/runtime/rpc/methods/agent-launch-terminal-prompt.test.ts (93 ms); the new path src/main/runtime/terminal-agent-prompt-delivery.test.ts has no entry and is costed at the median fallback in balanceFiles. Harmless for correctness — regenerate the baseline when convenient so shard balance stays accurate.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

A stdin-after-start agent with no ready title and no known ready screen
(goose, aider and most others) never satisfied tui-idle, so a launch
prompt, a worker brief, a worktree.create follow-up or a startup draft
waited out its budget and was never delivered. The follow-up path instead
typed `prompt\r` into the pane as soon as the process name matched.

Launch-readiness waits now opt in to one more weak-ready rank in the
tui-idle evaluator: the launched agent's composer-ready signal (the
scanner the renderer's draft paste uses), read only between the shell's
OSC 133;C and its next 133;D/133;A, and only on a pane with no
title-derived status. Being weak, it settles only on the poll after the
rendered screen shows no blocking dialog. A plain tui-idle wait never
reads it: it says the composer mounted, not that a turn finished.

All four launch waiters (agent.launch, orchestration worker start and
federation, the worktree.create follow-up and draft) share the opt-in.
The follow-up becomes a bracketed paste plus submit through the
runtime's agent-prompt writer.
…s and Claude

- A launch wait holds name-only title idle while the launched agent owns the PTY and its
  composer marker has not rendered (OpenCode titles itself before mounting its composer).
- Claude is read by its own rest title only: it arms bracketed paste before its trust and
  bypass-permissions dialogs.
- Composer evidence applies only to a terminal the launch created, not a reused one.
- The worktree-create startup draft keeps its per-agent budget.
The exclusion was keyed on the `claude` id alone, so an Agent Teams launch
(`orca claude-teams`, which runs Claude Code and takes its launch text on the
PTY) and the OpenClaude fork got a composer watcher. Both arm bracketed paste
before Claude Code's bypass-permissions confirmation, which the rendered-screen
check does not recognise, so a launch wait could settle on that dialog and the
brief's Enter would answer it.
…irmation latency

The per-agent draft budgets were sized for a 1.5 s quiet timer. tui-idle now confirms
composer evidence after its 3 s quiescence window and on the next 2 s poll, so a draft
for an agent that settled just before the old deadline timed out. Grow the budget by
exactly that added latency, derived from the evaluator's own constants.
…tartup delivery elsewhere

Each agent now declares the composer evidence a launch wait may settle on
(`launchReadiness` in the agent config), backed by a captured screen:
goose (composer-quiet), Codex and OpenCode (composer-marker). Undeclared
agents, Claude Code included, get no composer watch and keep plain tui-idle
on every launch path. Once a declared agent's command owns the PTY
(OSC 133;C), its composer evidence replaces the name-only title rank.

The worktree startup draft and follow-up run their base mechanisms again
(draft scanner with the 1.5 s quiet window and per-agent budget; process
match then a typed prompt). When the base would fire, the launch-readiness
wait decides instead only for a declared agent whose command owns the PTY,
so shells without command marks keep base delivery exactly.
…ader

Codex's header, read from the tail or the live emulator screen, already
settles every captured Codex launch at tier 1. Its composer glyph paints
while the header still says loading, so a declared Codex composer rule
could only disagree with the header rule — it settled a launch on a
mismatched grid where the header rule deliberately keeps waiting. Codex
declares no launch readiness now; goose and OpenCode keep theirs.
…re choosing startup delivery

The worktree follow-up decided when the foreground probe first matched
the agent. Daemon output travels on its own batched stream while the
probe is answered at once on the control connection, so the probe can
overtake the shell's OSC 133;C. The follow-up then fell to the base
path and typed a multi-line prompt into an agent still starting, which
submitted the first line alone and lost the rest.

A shell that has written any OSC 133 mark marks the command start too,
so the decision now waits for that mark until the end of the base
budget (the follow-up's 30 x 150 ms poll, the draft's paste budget),
then falls back to the base delivery. A pane that has written no mark
at all still decides immediately, exactly as before.
… Orca launched

The worktree follow-up chose between the launch-readiness wait and the
base typed prompt from whether the pane had shown an OSC 133 mark when
the foreground probe matched the agent. On the terminal daemon that
output arrives on its own batched stream, so the probe could match
goose before any mark reached the runtime, and a two-line prompt was
typed into goose while it loaded: the first line submitted alone and
the second was lost.

Whether a pane's startup command is marked is fixed when Orca picks the
shell wrapper, so each provider now records it on the spawn result:
the local provider from the launch config it used, the daemon adapter
from the same shell and feature selection the daemon makes, and SSH
panes stay unknown. A declared agent in a marked pane always goes
through the launch-readiness wait; everything else keeps the base
delivery exactly. The runtime mark-wait is removed.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-input-ready-wait branch from f924105 to 0376254 Compare September 28, 2026 04:16
@brennanb2025
brennanb2025 changed the base branch from brennanb2025/blocked-prompt-rendered-screen to main September 28, 2026 04:16
@brennanb2025
brennanb2025 marked this pull request as draft September 28, 2026 04:21
@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

The changes add shell startup-command marking and carry that value from PTY spawn results into runtime terminal state. They add composer-readiness signals for OpenCode and Goose and allow opted-in terminal waits to use those signals as idle evidence. Worktree draft and follow-up delivery, agent launch prompts, and worker startup waits now distinguish eligible new terminals from reused terminals. Tests and captured terminal transcripts cover marking, readiness evaluation, and delivery behavior.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 03762

The Windows test workflow will fail on these new assertions. Restrict the Unix-wrapper cases or assert Windows behavior separately before merging.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 03762

Output in an affected terminal session may be able to imitate the new readiness signal, causing a task to be delivered before the intended recipient is ready. The demonstrated scope is the affected session; broader exposure has not been established.

Retained concerns

  • Medium · security · inferred: Composer readiness can be attributed to the launched assistant using ownership marks parsed from ordinary PTY output, without verifying the emitter. A same-session output writer could imitate the marks and readiness evidence; newly created worker terminals opt into this path without a startup-command-mark check.
Security review details

Security Blast Radius

  • inferred — A forged readiness signal would affect task delivery into the PTY being watched. The evidence does not establish cross-user or cross-tenant reachability; the relevant attacker prerequisite is influence over output in that session.

Security Findings and Attack Paths

  • inferred — An output writer in an opted-in PTY could emit command-start control bytes followed by matching composer evidence. If the idle evaluator accepts that signal, automated task text can be sent before the intended assistant is ready. This is an inferred attack path, not a verified exploitation result.

Trust Boundaries and Controls

  • observed — Agent-specific signal selection, blocked-screen detection, a permission-status veto, and wait-before-send ordering constrain delivery. They do not authenticate the producer of an ownership mark within raw PTY output.

Resilience and Maintainability Implications

  • observed — Readiness failure prevents the shared deliverer from sending, and its write path checks terminal identity and lifecycle generation. The caller supplies no cancellation signal, and a fresh receipt identifier on each call is not evidence of deduplication across retries.

Hardening Proposals

  • proposed — Bind composer readiness to verified startup-command provenance and a terminal incarnation across all callers, rather than treating ownership control bytes in ordinary output as proof of their emitter.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 42 functions across 48 files. (8 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 and concisely describes the primary change: launch readiness now settles on the agent's composer.
Description check ✅ Passed The description is complete and directly related to the changes. It includes ELI5 context, detailed changes and rationale, linked issue context, visual proof, extensive testing, platform limitations, …
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 30.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 42 functions across 48 files. (8 skipped: 8 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 10543a25-d546-44b6-a4fa-5fca92da9742

📥 Commits

Reviewing files that changed from the base of the PR and between 52dc32f and 0376254.

📒 Files selected for processing (56)
  • 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/daemon/daemon-pty-adapter-startup-command-marks.test.ts
  • src/main/daemon/daemon-pty-session-spawn.ts
  • src/main/daemon/daemon-startup-command-marks.ts
  • src/main/daemon/serialize-grid-transcript-replay.test.ts
  • src/main/ipc/pty/runtime/spawn-commit-startup-command-marks.test.ts
  • src/main/ipc/pty/runtime/spawn-commit.ts
  • src/main/ipc/pty/runtime/spawn.ts
  • src/main/providers/local-pty-provider-shell-readiness.test.ts
  • src/main/providers/local-pty-session-activation.ts
  • src/main/providers/pty-spawn-result.ts
  • src/main/runtime/__fixtures__/aider-composer-ready.meta.json
  • src/main/runtime/__fixtures__/aider-composer-ready.txt
  • src/main/runtime/__fixtures__/claude-composer-ready.meta.json
  • src/main/runtime/__fixtures__/claude-composer-ready.txt
  • src/main/runtime/__fixtures__/goose-composer-ready.meta.json
  • src/main/runtime/__fixtures__/goose-composer-ready.txt
  • src/main/runtime/__fixtures__/opencode-composer-ready.meta.json
  • src/main/runtime/__fixtures__/opencode-composer-ready.txt
  • src/main/runtime/agent-composer-ready-transcripts.test.ts
  • src/main/runtime/agent-composer-ready-watch.test.ts
  • src/main/runtime/agent-composer-ready-watch.ts
  • src/main/runtime/orca-runtime-create-agent-prompt-render-gate.ts
  • src/main/runtime/orca-runtime-create-terminal.ts
  • src/main/runtime/orca-runtime-get-worktree-terminal-provisioning-host.ts
  • src/main/runtime/orca-runtime-runtime-id.ts
  • src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-06.spec.ts
  • src/main/runtime/orca-runtime.test.ts
  • src/main/runtime/rpc/methods/agent-launch-surfaces.ts
  • src/main/runtime/rpc/methods/orchestration/federation/federation-agent-launch.test.ts
  • src/main/runtime/rpc/methods/orchestration/federation/federation.ts
  • src/main/runtime/rpc/methods/orchestration/worker/local-worker-start.ts
  • src/main/runtime/rpc/methods/orchestration/worker/worker-start-readiness-question.test.ts
  • src/main/runtime/rpc/methods/orchestration/worker/workers-new-worktree.test.ts
  • src/main/runtime/runtime-pty-controller-contract.ts
  • src/main/runtime/runtime-terminal-contracts.ts
  • src/main/runtime/runtime-terminal-idle-polls.test.ts
  • src/main/runtime/runtime-terminal-idle-polls.ts
  • src/main/runtime/runtime-terminal-state-records.ts
  • src/main/runtime/runtime-terminal-wait.ts
  • src/main/runtime/runtime-worktree-agent-startup.ts
  • src/main/runtime/runtime-worktree-startup-readiness.test.ts
  • src/main/runtime/runtime-worktree-startup-readiness.ts
  • src/main/runtime/terminal-agent-prompt-delivery.test.ts
  • src/main/runtime/terminal-agent-prompt-delivery.ts
  • src/main/runtime/terminal-wait-name-only-idle.test.ts
  • src/main/runtime/tui-idle-evidence.test.ts
  • src/main/runtime/tui-idle-evidence.ts
  • src/main/shell-startup-command-marks.test.ts
  • src/main/shell-startup-command-marks.ts
  • src/main/shell-startup-features.ts
  • src/shared/draft-paste-ready-scanner.ts
  • src/shared/tui-agent-config.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.

env: { SHELL: shell }
})

expect(result.startupCommandMarked).toBe(marked)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '1,100p' src/main/daemon/daemon-pty-adapter-startup-command-marks.test.ts
sed -n '220,280p' src/main/daemon/daemon-pty-session-spawn.ts
rg -n 'effectiveShellPath|process.platform|describe.skipIf|test.skipIf' src/main/daemon/daemon-pty-session-spawn.ts src/main/daemon/daemon-pty-adapter-startup-command-marks.test.ts

Repository: stablyai/orca

Length of output: 4752


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- spawn selection ---'
sed -n '200,230p' src/main/daemon/daemon-pty-session-spawn.ts
printf '%s\n' '--- harness spawn/default context ---'
rg -n -C 4 'wslContext|spawn\(|startDaemonAdapterHarness|createMockSubprocess' src/main/daemon/daemon-pty-adapter-test-harness.ts src/main/daemon/daemon-pty-adapter-startup-command-marks.test.ts
printf '%s\n' '--- local Vitest platform guards ---'
rg -n -C 2 'skipIf|process\.platform' src/main/daemon src/main | head -160

Repository: stablyai/orca

Length of output: 20475


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- finishSpawn and result shape ---'
rg -n -C 8 'finishSpawn|interface PtySpawnResult|type PtySpawnResult|startupCommandMarked' src/main/daemon/daemon-pty-session-spawn.ts src/main/daemon src/main | head -240

Repository: stablyai/orca

Length of output: 20326


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- finishSpawn implementation ---'
sed -n '1,130p' src/main/daemon/daemon-pty-spawn-result.ts
printf '%s\n' '--- PtySpawnResult declaration ---'
rg -n -C 12 'PtySpawnResult' src/main/providers/types.ts src/main/providers

Repository: stablyai/orca

Length of output: 41772


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- complete startup result type ---'
sed -n '8,34p' src/main/providers/pty-spawn-result.ts
printf '%s\n' '--- finishSpawn return paths ---'
rg -n -C 12 'return finalizeSpawnResult|return .*spawnResult|startupCommandMarked|finalizeSpawnResult' src/main/daemon/daemon-pty-spawn-result.ts
printf '%s\n' '--- daemon spawn result producers ---'
rg -n -C 8 'startupCommandMarked|createOrAttachSpawn|spawnResult' src/main/daemon/daemon-pty-adapter-test-harness.ts src/main/daemon/daemon-server.ts src/main/daemon

Repository: stablyai/orca

Length of output: 17865


🏁 Script executed:

set -o pipefail
printf '%s\n' '--- test harness setup ---'
sed -n '28,125p' src/main/daemon/daemon-pty-adapter-test-harness.ts
printf '%s\n' '--- create/attach result shape and normal path ---'
rg -n -C 10 'export type CreateOrAttachResult|type CreateOrAttachResult|isNew:|cwdReadableByDaemon' src/main/daemon src/main/daemon/daemon-server.ts | head -220

Repository: stablyai/orca

Length of output: 22378


Restrict the wrapper test to Unix hosts.

On Windows, DaemonPtySessionSpawn.doSpawn sets effectiveShellPath to an empty string. The normal fresh-spawn result omits startupCommandMarked, so the parameterized assertions fail for every shell row. Skip these Unix-wrapper cases on Windows, or assert the Windows result separately.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing: after review narrowed it, the new launch-readiness signal applied only to agents with a captured ready screen (goose and OpenCode). OpenCode was already delivered on main, and goose has very low usage (about 0.16% of launching installs over 90 days), so the added mechanism isn't worth carrying. The captures, ablations and QA stay on the branch in case a widely used paste-after-start agent needs it later.

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.

2 participants