Repository navigation
fix(agent-launch): settle launch readiness on the agent's own composer - #23452
brennanb2025 wants to merge 9 commits into
Conversation
9248249 to
83aa3aa
Compare
dd4775c to
679583b
Compare
|
@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 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 — 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:
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 ( Happy to help wire ZCode into the declaration model once yours lands. |
Review status: decisions implemented, reviews clean, live QA passing (head
|
| 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.
There was a problem hiding this comment.
✅ 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'intui-agent-config.ts, declared byopencodeandgooseonly and pinned byagent-composer-ready-watch.test.ts; undeclared agents keep plaintui-idle. - Composer watch.
agent-composer-ready-watch.tsfeeds the existing draft scanner only between the shell's OSC 133C(command started) and the nextD/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.tsreplaces 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.tsplus the daemon/local derivations carrystartupCommandMarkedthroughPtySpawnResult→ 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.
acceptComposerReadyis threaded astrueonly for a terminal the launch created (agent.launch, local/federated worker, draft, follow-up) andfalsefor 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.jsonstill keys the renamed test assrc/main/runtime/rpc/methods/agent-launch-terminal-prompt.test.ts(93 ms); the new pathsrc/main/runtime/terminal-agent-prompt-delivery.test.tshas no entry and is costed at the median fallback inbalanceFiles. Harmless for correctness — regenerate the baseline when convenient so shard balance stays accurate.
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.
f924105 to
0376254
Compare
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 📝 WalkthroughWalkthroughThe 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 The Windows test workflow will fail on these new assertions. Restrict the Unix-wrapper cases or assert Windows behavior separately before merging. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to 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
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation 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.)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 10543a25-d546-44b6-a4fa-5fca92da9742
📒 Files selected for processing (56)
src/main/agent-launch/agent-launch-executor.test.tssrc/main/agent-launch/agent-launch-executor.tssrc/main/agent-launch/agent-launch-prompt-delivery.tssrc/main/agent-launch/agent-launch-surface-factories.tssrc/main/daemon/daemon-pty-adapter-startup-command-marks.test.tssrc/main/daemon/daemon-pty-session-spawn.tssrc/main/daemon/daemon-startup-command-marks.tssrc/main/daemon/serialize-grid-transcript-replay.test.tssrc/main/ipc/pty/runtime/spawn-commit-startup-command-marks.test.tssrc/main/ipc/pty/runtime/spawn-commit.tssrc/main/ipc/pty/runtime/spawn.tssrc/main/providers/local-pty-provider-shell-readiness.test.tssrc/main/providers/local-pty-session-activation.tssrc/main/providers/pty-spawn-result.tssrc/main/runtime/__fixtures__/aider-composer-ready.meta.jsonsrc/main/runtime/__fixtures__/aider-composer-ready.txtsrc/main/runtime/__fixtures__/claude-composer-ready.meta.jsonsrc/main/runtime/__fixtures__/claude-composer-ready.txtsrc/main/runtime/__fixtures__/goose-composer-ready.meta.jsonsrc/main/runtime/__fixtures__/goose-composer-ready.txtsrc/main/runtime/__fixtures__/opencode-composer-ready.meta.jsonsrc/main/runtime/__fixtures__/opencode-composer-ready.txtsrc/main/runtime/agent-composer-ready-transcripts.test.tssrc/main/runtime/agent-composer-ready-watch.test.tssrc/main/runtime/agent-composer-ready-watch.tssrc/main/runtime/orca-runtime-create-agent-prompt-render-gate.tssrc/main/runtime/orca-runtime-create-terminal.tssrc/main/runtime/orca-runtime-get-worktree-terminal-provisioning-host.tssrc/main/runtime/orca-runtime-runtime-id.tssrc/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-06.spec.tssrc/main/runtime/orca-runtime.test.tssrc/main/runtime/rpc/methods/agent-launch-surfaces.tssrc/main/runtime/rpc/methods/orchestration/federation/federation-agent-launch.test.tssrc/main/runtime/rpc/methods/orchestration/federation/federation.tssrc/main/runtime/rpc/methods/orchestration/worker/local-worker-start.tssrc/main/runtime/rpc/methods/orchestration/worker/worker-start-readiness-question.test.tssrc/main/runtime/rpc/methods/orchestration/worker/workers-new-worktree.test.tssrc/main/runtime/runtime-pty-controller-contract.tssrc/main/runtime/runtime-terminal-contracts.tssrc/main/runtime/runtime-terminal-idle-polls.test.tssrc/main/runtime/runtime-terminal-idle-polls.tssrc/main/runtime/runtime-terminal-state-records.tssrc/main/runtime/runtime-terminal-wait.tssrc/main/runtime/runtime-worktree-agent-startup.tssrc/main/runtime/runtime-worktree-startup-readiness.test.tssrc/main/runtime/runtime-worktree-startup-readiness.tssrc/main/runtime/terminal-agent-prompt-delivery.test.tssrc/main/runtime/terminal-agent-prompt-delivery.tssrc/main/runtime/terminal-wait-name-only-idle.test.tssrc/main/runtime/tui-idle-evidence.test.tssrc/main/runtime/tui-idle-evidence.tssrc/main/shell-startup-command-marks.test.tssrc/main/shell-startup-command-marks.tssrc/main/shell-startup-features.tssrc/shared/draft-paste-ready-scanner.tssrc/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) |
There was a problem hiding this comment.
🎯 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.tsRepository: 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 -160Repository: 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 -240Repository: 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/providersRepository: 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/daemonRepository: 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 -220Repository: 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.
|
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. |











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:
agent.launchprompt, or a startup draft for goose waited its full budget (60 s for a worker) and failed withAgent did not become ready (timeout), or the draft was dropped.orca worktree create --agent goose --prompt "..."typedprompt\rinto 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:
launchReadiness), each backed by a captured screen that a test replays:composer-quiet, meaning it switched on its own paste mode (bracketed paste) and then went quiet for 3 s. Capturegoose-composer-ready.composer-marker, meaning its show-cursor after paste mode, which it sends only once its input row is mounted. Captureopencode-composer-ready.OpenAI Codexheader, 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 saysloading, so a second Codex rule could only disagree with the header rule.agent.launchprompts, startup drafts and worktree-create follow-ups now reach it. An OpenCode launch no longer settles on itsOpenCodetitle, which it paints before its input box exists.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 asksAdd .aider* to .gitignore (recommended)? (Y)es/(N)oon a fresh worktree, switches paste mode on for that question too, and a paste would answer it.sh,kshordash. 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 asprompt\ronce the process name matches.agent.launchpointed at a running agent) is still asked "is the agent idle", never "is its input box up": its agent may be mid-turn.orca terminal wait --for tui-idlebehaves exactly as before.Mechanism: #22927 made
tui-idleone 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.launchReadiness?: 'composer-marker' | 'composer-quiet'intui-agent-config.ts.composer-markerreads the marker from the agent's existingdraftPasteReadySignal.composer-quietreads paste mode followed by quiet. A test pins the declared set to the two captured agents. A second test requires everycomposer-markeragent to have a marker and no quiet-window fallback, so paste mode alone never counts as its input box.unowned.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.waitForTerminalgets an internalacceptComposerReadyoption. Only launch waits set it, and only when that launch created the terminal. It is not part of theterminal.waitRPC, so there is no wire change. The idle poll entry owns the watch, so the watch dies with the waiter.startupCommandMarked), and the runtime keeps it on the terminal's record.shell-startup-command-marks.tsis 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.sendTerminalAgentPrompt: bracketed paste, per-PTY serialisation, per-agent submit timing), after the same launch-readiness waitagent.launchuses.Why
.gitignoreprompt 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.loading.tui-idle.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
f924105c31and of its base, #22927 at83aa3aa98f(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 throughorca worktree create --agent goose --promptwith a two-line prompt.83aa3aa98f(zsh)f924105c31(zsh, 3 rounds)Agent did not become ready (timeout)after 60 s; goose's input box was up, brief never deliveredworktree create --prompt(two lines)goose is ready, nothing typed at the shell prompt (3/3)--dangerously-skip-permissionsUnmarked 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
a385580027decided 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 onlyline oneand lostline two); that is what the spawn-time decision replaces.Screenshots
goose two-line follow-up on zsh, this PR (rounds 1 and 2):


goose two-line follow-up under

/bin/ksh, this PR (base code path):The earlier split at

dd4775c75b(onlyline onesubmitted):goose worker, base (60 s later, brief never arrived) / this PR at


e6d33aca47:Claude "Bypass Permissions" dialog at the end of the round, base / this PR:


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
Tests at
f924105c31:shell-startup-command-marks.test.tspairs 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;shunmarked.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.tsreplays real captured screens through the runtime, each preceded by the 133;C mark.tui-idlewait on the same pane times out. It settles after the quiet window, on a poll.readyexactly at its input-box marker and not before. Cut after itsOpenCodetitle but before its marker, it does not settle a launch wait; the rest of the capture then settles it.agent-composer-ready-watch.test.tspins the declared set (goose, OpenCode). It requires everycomposer-markeragent 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 whilepending, settles onready).unownedand 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:fix it\ron 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):tui-idleconfirms, inside the extended budget.fix it\r.prompt\ron the process match.terminal-agent-prompt-delivery.test.ts,agent-launch-executor.test.ts,worker-start-readiness-question.test.tsandfederation-agent-launch.test.tspinacceptComposerReady: falsefor a reused terminal andtruefor a launched one.Ablations at
f924105c31(part removed, targeted suites run, part restored):shcaseThe 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):composer-marker)Gates at
f924105c31: targeted vitest (245 files, 3696 tests, includingorca-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:changedclean. Thecodex-composer-readycapture is removed with Codex's declaration; #23475's Codex captures replace it.AI Disclosure
Review
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
.gitignorequestion is why this is not generic./bin/bash3.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.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 andagent.launchwaits the evaluator still reads the marks in the output, so an unmarked shell gets exactlytui-idle, as on the base. Windows behaviour is unchanged by this PR.acceptComposerReady,launchReadinessandstartupCommandMarkedare in-process (the daemon's answer is derived in main from what main asked it to spawn), andterminal.waitparams and replies are unchanged.agent.launchwaits on SSH still read the marks in the output through the runtime's own PTY data stream, as before this revision.opencode-composer-readyis 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.