Repository navigation
feat(agent-launch): the host delivers AI buttons' first prompt across reload - #26168
brennanb2025 wants to merge 24 commits into
Conversation
…nd finishes it after a restart The desktop's AI buttons started their agent through agent.launch (#25624), but the window still pasted the prompt. A window reload or an app restart during the launch lost the prompt with nobody to finish it. The window now hands the prompt to agent.launch (temporary `transport: 'paste'`, kept off the launch line as on main), and the host pastes it with the window's own readiness rule and bytes. The launch record owes the prompt until a durable mark written just before the first byte; a host that restarts pastes a still-owed prompt once into the surviving agent, and never re-pastes one whose write may have begun. Follow-ups run on the host's answer.
…start may paste Round-1 review of the host-owned first prompt: - One signal, the host-assigned desktop caller, for every desktop-only behaviour: the budget-spent write, owing the prompt, and resuming it. The phone and the CLI keep main's. - A resume writes only on a recorded W2, and refuses on a host that cannot find the agent. - An owed prompt has its own 5-minute deadline; a terminal not found yet stays owed and is looked for again until then, instead of settling not-delivered. - The budget-spent write uses main's Enter timing; marker agents get main's budget and blind write on the desktop; Codex gives up at its budget, as main did. - The chat copy is seeded once the agent started; "wasn't sent" only for a definite not-delivered; the receipt reads composerUnobserved: false.
…llers quiet on a maybe-landed prompt Round-2 review of the host-owned first prompt: - The owed prompt records the PTY the launch started its agent in; a resume pastes into that PTY only, and another process in the pane reads as gone (not delivered). - A failed resume looks again only while the prompt is owed and inside its deadline; a prompt whose write may have begun never retries. - The chat copy is seeded on start but stamped at the click, so the chat view matches it to the agent's turn. - An unconfirmed or missing receipt reports itself as already said, so no caller offers to send the prompt again.
…n a respawn changes A cold restore respawns the pane's shell under the same PTY id with a new incarnation; the resume tests now cover that case, and a runtime-level test reads the identity an owed prompt records from a terminal the runtime really created.
…, not its store reads
…te nothing past the deadline The restart sweep opened the record store only when the profile held a chat, so a profile that only ever launched terminal agents never resumed its owed prompt. It now asks, read-only and without creating the database, whether any launch record still owes one. The deadline only stopped a resume from starting; one that waited for its agent past it could still write after the window stopped waiting. The write's compare-and-set now refuses past the deadline, and the prompt settles as not delivered.
…e launch-prompt boundary
…which nothing else reads
…aste a write this process made unrecorded - A plain-SSH terminal reports no foreground process, so its host is treated as one that cannot find the agent in front, the Windows rule: the desktop's paste writes unless a shell is proven there, as main's window did; a resume still refuses. - When a live launch's write mark fails and it writes anyway, this process remembers it until the deadline, so a later resume in the same process settles it unconfirmed instead of pasting again.
…ch prompt; worker briefs keep main's
|
Desktop AI buttons that open a terminal started the agent through Orca's background service while the window sent its first prompt. Reloading the window or restarting Orca during launch could lose the prompt. Notes New agent and Checks Resolve comments also depended on that window to clear notes or resolve threads, so a reload could leave them looking unsent after the prompt arrived. PR #26168 makes the background service deliver the prompt. PR #26173 records the selected notes or review-thread follow-up with that launch and runs it once after a reload. Pending notes and threads keep their existing sending state. On macOS and Linux, restarting Orca can finish an unsent prompt within five minutes, into the same surviving terminal only. The pair must land together. Review fixed caller isolation, bounded restart recovery, original terminal identity checks, duplicate chat copies, misleading failure notices, one-time follow-up takes, settlement notifications and pending thread holds. Live Windows QA exposed another failure: a crashed agent could leave the host typing instructions into cmd or PowerShell. The fix shares main's positive readiness decision with the host and checks the original terminal before waiting and before each write. Codex never uses the timeout fallback. Verified on the reviewed stacked Windows build a42e12a, containing lower f175b66: the crash stand-in received zero bytes; Codex without a composer was refused and received zero bytes throughout its 15-minute lifetime; the positive idle-title case received exactly one bracketed prompt and one Enter, with no further input throughout its 15-minute lifetime. Complete input files and lifetime logs were inspected. The host follow-up check returned the matching follow-up once, then nothing on a second take. That check proves the one-time take, not the Notes or Checks reload interface. Original full screenshots were recovered and inspected; the crash case's initial terminal-stream capture gap remains disclosed. On hidden laptop builds of the same reviewed stack and the parent-selected main comparator 7f43d9b, both actual AI Fix and Resolve with AI button launches delivered identical complete prompt bytes: one paste and one Enter, with no later input through the stand-ins' configured 15-minute lifetimes. The upper host recorded delivery and removed its owed prompt fields. Each arm's original RuntimeClient, bundled with its own esbuild, returned matching CLI launch receipts and supplied one prompt argument with zero stdin throughout that lifetime. Full original screenshots were inspected. These were isolated stand-ins, not signed-in agent sessions; the setup timings do not establish performance. The same isolated desktop passed mid-launch reload and restart into the original terminal, an empty-history profile, and a real restart4182ms after the original five-minute deadline. Successful recovery delivered one prompt; expired recovery delivered none and cleared the owed prompt. Complete original15-minute input files and final host records were checked independently. Notes New agent remained in flight after reload, then removed only the unchanged sent note; a note edited after sending and a newly added note survived. Checks Resolve comments remained in flight, then resolved one selected thread and posted one conversation reply through an owned closed provider, leaving the unrelated thread unchanged. Both original launches delivered one prompt frame throughout their complete48-second lifetimes; a second reload caused no duplicate prompt, note removal or review mutation. Full original interface screenshots and action-specific protected-config metadata were inspected. This does not claim signed-in agent or real provider-write verification. The timing comparison used reviewed combined build a42e12a and main baseline 7f43d9b, before the final main integration. It used three interleaved pairs for each actual AI Fix and Resolve conflicts action. Median paired time-to-prompt changes were −70 ms and +4 ms respectively; individual pairs had mixed signs and unequal observed shared-host load. All prompt-frame bytes matched, normal submit delays stayed 50–54 ms, and all 24 shared queue observations showed no active typecheck or test. This small isolated-agent sample shows no consistent slowdown and does not establish real-agent performance. On final main-integrated lower 8caa73a and upper 0bb108d, fresh scoped node/web typechecks pass on both trees; named tests pass 203 lower and 109 upper cases. Both trees also passed all 22 required repository checks. Fresh combined source readiness review without counsel found no new proven issues; CI on both exact final heads is green: all ten Node 24 test groups per PR, static analysis, packaging, relay integration, wire compatibility and final verification checks passed; optional platform jobs were skipped. Additional checks triggered when both PRs were marked ready passed on the same source heads: lower ready checks, upper ready checks, and the packaged Windows serve check. The first lower Windows serve-mode attempt completed every runtime assertion, then failed in an unchanged main cleanup helper reading a disappearing process-ID file. One retry of that job, including its dependencies, passed without a source change. The final main check found seventeen newer commits with no overlap with this pair’s changed files; adjacent terminal and launch contracts were checked, and both branches remain mergeable. Accepted boundaries and deferred work:
Final evidence: lower CI and upper CI passed on the exact final heads above. Eight full original screenshots and the precise live-tested build are in the two PR bodies. Windows PowerShell crash was not verified: driver usage quota, an unavailable fallback route and the final contact-window deadline prevented that row; this gap was accepted. The verified crash case used cmd.exe; PowerShell startup and profile behavior were not exercised. Actual SSH restart recovery, GitLab live writes, the inline-reply fallback and signed-in agent sessions have not been verified. Both PRs have been marked ready together; neither is merged or set to auto-merge. |
📝 WalkthroughWalkthroughLaunch requests can mark prompt text for paste transport. The host now evaluates readiness and terminal identity before guarded delivery, then reports whether delivery reached the terminal and whether the composer was observed. The renderer uses that receipt to update chat state and delivery callbacks. Shared readiness polling, fallback checks, process-inspection limits, and launch replay coverage also changed. Priority: ➖ Normal 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
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:
a12ed33d-ff02-4ca8-85c3-1bbb470352ae
📒 Files selected for processing (62)
config/scripts/vitest-sqlite-database-runtime-files.mjssrc/main/agent-launch/agent-launch-executor.test.tssrc/main/agent-launch/agent-launch-executor.tssrc/main/agent-launch/agent-launch-owed-prompt-resume.test.tssrc/main/agent-launch/agent-launch-owed-prompt-resume.tssrc/main/agent-launch/agent-launch-prompt-delivery.tssrc/main/agent-launch/agent-launch-surface-factories.tssrc/main/native-chat/agent-session-journal/journal-database.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-mutations.tssrc/main/orcad/orcad-entry.tssrc/main/runtime/agent-launch-fallback-lifecycle.test.tssrc/main/runtime/agent-launch-fallback-readiness.test.tssrc/main/runtime/agent-launch-fallback.test-fixture.tssrc/main/runtime/agent-launch-owed-prompt-record.test.tssrc/main/runtime/agent-launch-owed-prompt-record.tssrc/main/runtime/agent-launch-persisted-obligations.test.tssrc/main/runtime/agent-launch-persisted-obligations.tssrc/main/runtime/agent-launch-pty-identity.test.tssrc/main/runtime/agent-session-operation-admission.tssrc/main/runtime/agent-session-record-store.tssrc/main/runtime/launched-agent-composer-readiness.test.tssrc/main/runtime/launched-agent-composer-readiness.tssrc/main/runtime/launched-agent-exit-delivery.test.tssrc/main/runtime/launched-agent-host-proves-agent.test.tssrc/main/runtime/launched-agent-write-guard.test.tssrc/main/runtime/launched-agent-write-guard.tssrc/main/runtime/orca-runtime-activate-managed-worktree.tssrc/main/runtime/orca-runtime.tssrc/main/runtime/orchestration/send-agent-turn-boundary.test.tssrc/main/runtime/rpc/methods/agent-launch-active-operations.tssrc/main/runtime/rpc/methods/agent-launch-desktop-caller.tssrc/main/runtime/rpc/methods/agent-launch-owed-prompt-host.test.tssrc/main/runtime/rpc/methods/agent-launch-owed-prompt-host.tssrc/main/runtime/rpc/methods/agent-launch-prompt-carry.test.tssrc/main/runtime/rpc/methods/agent-launch-replay.tssrc/main/runtime/rpc/methods/agent-launch-restart-replay.test.tssrc/main/runtime/rpc/methods/agent-launch-surfaces.tssrc/main/runtime/rpc/methods/agent-launch-terminal-prompt.test.tssrc/main/runtime/rpc/methods/agent-launch-terminal-prompt.tssrc/main/runtime/rpc/methods/agent-launch.test-fixture.tssrc/main/runtime/rpc/methods/agent-launch.tssrc/main/startup/main-process-ipc-bootstrap.tssrc/main/startup/main-process-runtime-launch.tssrc/relay/agent-exec-disposal.test.tssrc/renderer/src/components/native-chat/NativeChatComposer.tsxsrc/renderer/src/components/native-chat/NativeChatInterruptedContinue.test.tsxsrc/renderer/src/lib/agent-launch-caller-prompt-transport.test.tssrc/renderer/src/lib/agent-launch-paste-byte-parity.test.tssrc/renderer/src/lib/agent-launch-prompt-delivery.tssrc/renderer/src/lib/agent-launch-through-host.tssrc/renderer/src/lib/agent-paste-draft.tssrc/renderer/src/lib/agent-ready-wait.tssrc/renderer/src/lib/launch-agent-in-new-tab.tssrc/renderer/src/lib/launch-agent-new-tab-host-route-readiness.test.tssrc/renderer/src/lib/launch-agent-new-tab-host-route.test.tssrc/renderer/src/lib/launch-agent-new-tab-host-route.tssrc/renderer/src/lib/launch-agent-tab-prompt-paste.tssrc/shared/agent-launch-intent.tssrc/shared/agent-launch-prompt-receipt.test.tssrc/shared/agent-ready-evidence.tssrc/shared/agent-session-operation-ledger.tssrc/shared/rpc-contract/agent-launch-params.ts
💤 Files with no reviewable changes (3)
- src/renderer/src/lib/launch-agent-in-new-tab.ts
- src/renderer/src/lib/launch-agent-new-tab-host-route-readiness.test.ts
- src/renderer/src/lib/launch-agent-tab-prompt-paste.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.
| const foreground = process?.foregroundProcess?.toLowerCase() ?? '' | ||
| if (isExpectedAgentProcess(foreground, expectedProcess)) { | ||
| return 'foreground-match' | ||
| } | ||
| // Give shell startup children three probes to settle, and never accept a foreground shell. | ||
| if (attempt >= 4 && process?.hasChildProcesses && !isShellProcess(foreground)) { | ||
| return 'child-process' | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
# How does the runtime's inspectTerminalProcess shape a null foreground with children?
rg -nP -C8 '\binspectTerminalProcess\s*\(' src/main/runtime --type=ts -g '!**/*.test.ts'Repository: stablyai/orca
Length of output: 9198
🏁 Script executed:
printf '%s\n' '--- inspectTerminalProcess implementation ---'
sed -n '220,285p' src/main/runtime/orca-runtime-restore-structured-agent-session-tabs-once.ts
printf '%s\n' '--- relevant types and providers ---'
rg -n -F --glob '*.ts' 'interface PtyProcessInspection|type PtyProcessInspection|PtyProcessInspection|foregroundProcess|hasChildProcesses' src/main src/shared | head -120
printf '%s\n' '--- readiness source ---'
sed -n '1,120p' src/shared/agent-ready-evidence.ts
sed -n '50,100p' src/main/runtime/orca-runtime.tsRepository: stablyai/orca
Length of output: 7463
Require a known non-shell foreground for the child-process rule.
inspectTerminalProcess preserves a null foregroundProcess. The readiness code converts it to '', and attempt 4 can then return 'child-process' when child processes exist. This can authorize the desktop fallback without a known foreground process.
🛡️ Proposed guard
- if (attempt >= 4 && process?.hasChildProcesses && !isShellProcess(foreground)) {
+ if (attempt >= 4 && process?.hasChildProcesses && foreground !== '' && !isShellProcess(foreground)) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const foreground = process?.foregroundProcess?.toLowerCase() ?? '' | |
| if (isExpectedAgentProcess(foreground, expectedProcess)) { | |
| return 'foreground-match' | |
| } | |
| // Give shell startup children three probes to settle, and never accept a foreground shell. | |
| if (attempt >= 4 && process?.hasChildProcesses && !isShellProcess(foreground)) { | |
| return 'child-process' | |
| } | |
| const foreground = process?.foregroundProcess?.toLowerCase() ?? '' | |
| if (isExpectedAgentProcess(foreground, expectedProcess)) { | |
| return 'foreground-match' | |
| } | |
| // Give shell startup children three probes to settle, and never accept a foreground shell. | |
| if (attempt >= 4 && process?.hasChildProcesses && foreground !== '' && !isShellProcess(foreground)) { | |
| return 'child-process' | |
| } |
…s-first-prompt # Conflicts: # src/main/runtime/rpc/methods/agent-launch.ts
…s-first-prompt # Conflicts: # src/main/runtime/orchestration/send-agent-turn-boundary.test.ts
Ready for review: #26168 + #26173 (land together)The problem. Clicking an AI button (Resolve with AI, Fix with AI, Notes "New agent", Checks "Resolve comments") starts an agent and then types the first prompt once the agent is ready. If the window reloaded during that wait, the prompt could be lost; for Notes and Checks, the "clear sent notes" / "resolve the selected threads" step that should follow could also be lost or run twice, leaving notes looking unsent and threads open after the agent had actually received them. What changes for you.
How. The app's host process (not the window) now sends the first prompt and records the result, so a reloaded window joins the launch already in progress instead of starting over. #26173 stores the Notes/Checks follow-up with that launch and takes it in one step after the prompt is delivered, so no second window or reload can repeat it. Fixed in this final round.
Deferred (P3, not fixed here). If a launch somehow stays pending past the 24-hour record retention and finishes while the window is mid-navigation, Notes/Checks can stay "sending…" until the next reload. No follow-up runs from an expired record. Seen, but already on main (not changed here). After the "prompt wasn't sent" timeout, the dialog stays open with Start agent enabled, so a second click would start a second agent. Same code path on main. Verified.
Not verified.
Author: @BrennanKB5 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/main/runtime/rpc/methods/agent-launch-terminal-prompt.test.ts (1)
553-581: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRestore the console spies in a
finallyblock.
spies.forEach((spy) => spy.mockRestore())runs only after the assertions pass. If an assertion fails, the console stays mocked. Later tests in this file then lose their log output. Usetry/finally, or useafterEach(() => vi.restoreAllMocks()).
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
cd90c2d5-800f-424b-b111-f19058964bfe
📒 Files selected for processing (21)
src/main/agent-launch/agent-launch-executor.test.tssrc/main/agent-launch/agent-launch-prompt-delivery.tssrc/main/agent-launch/agent-launch-surface-factories.tssrc/main/providers/local-pty-foreground-inspection-cheap-tier.test.tssrc/main/providers/local-pty-provider.tssrc/main/runtime/agent-launch-fallback-lifecycle.test.tssrc/main/runtime/agent-session-operation-admission.tssrc/main/runtime/orca-runtime-activate-managed-worktree.tssrc/main/runtime/rpc/methods/agent-launch-active-operations.tssrc/main/runtime/rpc/methods/agent-launch-desktop-caller.tssrc/main/runtime/rpc/methods/agent-launch-replay.tssrc/main/runtime/rpc/methods/agent-launch-restart-replay.test.tssrc/main/runtime/rpc/methods/agent-launch-surfaces.tssrc/main/runtime/rpc/methods/agent-launch-terminal-prompt.test.tssrc/main/runtime/rpc/methods/agent-launch-terminal-prompt.tssrc/main/runtime/rpc/methods/agent-launch.test-fixture.tssrc/main/runtime/rpc/methods/agent-launch.tssrc/renderer/src/lib/agent-launch-through-host.tssrc/renderer/src/lib/launch-agent-in-new-tab.tssrc/shared/agent-launch-intent.tssrc/shared/rpc-contract/agent-launch-params.ts
💤 Files with no reviewable changes (1)
- src/renderer/src/lib/launch-agent-in-new-tab.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/runtime/rpc/methods/agent-launch-replay.ts
- src/main/runtime/agent-session-operation-admission.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.
| value.outcome === 'not-delivered' || | ||
| value.outcome === 'unconfirmed' | ||
| : value.outcome === 'handed-to-terminal' | ||
| ? !('composerUnobserved' in value) || typeof value.composerUnobserved === 'boolean' |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Reject composerUnobserved: false.
The receipt type at Line 179 allows this field only as true or absent. This guard accepts false, so an invalid RPC receipt passes validation and is narrowed to a type that excludes that value. Require a present field to equal true.
|
Closed: the agent.launch migration is parity-only. This fixes main's reload-loses-the-prompt bug, which moves to the post-migration bug-fix work, done there by handing the prompt over at agent start instead of pasting. |
ELI5
Reloading the window mid-launch could lose the agent’s first prompt. The host now sends it once while the window may reload.
The earlier version also saved the first-prompt text and recovered it after an app restart. That restart guarantee is withdrawn: an interrupted paste-only launch after an app restart remains unconfirmed, with no automatic late paste, matching main. The existing quiet uncertainty surfaces remain; no new notice is added.
What Changed
The host remains the single live sender. Same-process retries join the running launch, and later replay reads the recorded result. Original terminal identity, program ownership and original pseudo-terminal guards still apply before every write. The host still checks who may launch and durably records the completed result.
The direct native foreground inspection uses the existing evidence budget. If that inspection exceeds the budget, program ownership remains unproven and the timeout authorizes no paste.
The saved first-prompt text, first-byte claims, five-minute recovery deadline, startup scans, ten-second rescans and resume-before-replay are removed. No replacement delivery or recovery mechanism is added. #26173 keeps durable Notes and Checks follow-ups, with an atomic take only after the prompt is handed over.
Why
Main does not automatically paste an interrupted first prompt after restart. The extra recovery promise added saved state and another later writer that could meet newer user activity. Removing it gives launch unification parity first and reduces that failure class. This is the step that reaches parity with main; a later simplification phase will change how prompts are delivered.
Linked Issue
Step 2 of agent-launch unification after #24934, #25430 and #25624. Stacked follow-ups: #26173; the pair must land together.
Visual Proof
Fresh live QA ran on hidden desktop windows with an isolated profile, a stand-in Codex agent that records every byte it receives, and a closed fake GitHub provider (no real agent, account or GitHub writes). The reload rows and the slow-agent check ran on #26173 at c746688, which contains this branch; restart and Checks rows ran on c6dbbe4 before the final sync with main. Between those builds and the final heads only main's own commits differ (none touch agent launch).
Window reload while the agent is still starting: the same terminal is still waiting (the agent is set to become ready after 12 seconds), nothing has been typed yet.
After the agent became ready: the terminal received exactly one copy of the prompt followed by the normal submit keystrokes (from the raw input record; the stand-in does not echo the prompt). A second reload added nothing.
App restart while the agent is still starting: the launch stays "unconfirmed". For four minutes after the agent became ready it received no prompt bytes and no submit, and no new notice appeared.
A slow agent (ready after 35 seconds): the dialog gives up after 20 seconds, the same as main for Codex, with main's existing "Your prompt wasn't sent — paste it once the agent is ready." notice, and nothing is pasted later.
Testing
QA ran on this laptop with hidden windows because the remote Mac test machine was unreachable that evening. Stand-in agent and fake provider only: no real Codex or GitHub. Windows, WSL and SSH were not exercised.
Review
Fresh adversarial reviews and an independent architecture sketch checked reload ownership, admission/replay, settled receipts, original-terminal write guards and atomic follow-ups. Final readiness review and a fresh narrow review of the last settlement change are complete; no open P0 or P1 was established. Live QA and CI remain separate gates.
Agent skill upstream boundary
Notes
No new UI copy or status surface is added. No merge or auto-merge is enabled. Review, CI and live QA are green.
Author: @BrennanKB5
Checklist