Skip to content

Start server-run automations' agents through the launch executor - #26820

Merged
brennanb2025 merged 3 commits into
mainfrom
brennanb2025/p1b-headless-automations
Oct 9, 2026
Merged

brennanb2025 merged 3 commits into
mainfrom
brennanb2025/p1b-headless-automations

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

When Orca runs as a server, it runs your scheduled automations itself. It starts the agent you picked, either in a fresh workspace made for that run or in an existing workspace, and gives it the automation's prompt. Orca has one shared host operation that starts agents (the launch executor), but server-run automations still started their agent their own way, beside it. This PR sends them through the executor too. Nothing you see changes.

What Changed

The problem. A server-run automation started its agent through two separate code paths. A new-workspace run created the workspace with the agent already in it. An existing-workspace run called a dedicated terminal launcher. Neither went through the launch executor that the other fresh launches (agent.launch, worktree.create, orchestration workers) now use, so the order of steps in a launch had to be kept in step by hand in one more place.

What is different for the user: nothing, by design.

  • Agents that take the prompt on their command line (Claude, Codex and the rest): same command, same prompt, same tab.
  • Agents that read the prompt after they start (Aider, Goose, Amp and about twenty others): the prompt is typed in once, with the same writer and the same timing as before.
  • Also unchanged: the tab title (the run's title in an existing workspace), the run starting in the background with no workspace switch, when the run is recorded as dispatched, how its completion is detected from the terminal, the automation run history, and SSH and folder workspaces.

The mechanism.

  1. A new launchHeadlessAutomationAgent runs executeAgentLaunch for both kinds of run as a terminal-only launch (the input Start worktree.create's agent through the launch executor #26709 added). Automations detect completion from the terminal, so the chat default never applies.
  2. The automation keeps its own workspace and terminal factories, so nothing it creates changes. A new-workspace run creates the workspace with the same arguments as before. An existing-workspace run uses the same terminal launcher, with the run's title. Both still fold an argv agent's prompt into its command at any length.
  3. For an agent that reads its prompt after start, the executor asks the automation's factory to deliver it. The factory uses the same follow-up writer the workspace create has always used. That writer now has an awaitable form (deliverWorktreeStartupFollowup), and the existing fire-and-forget call wraps it, so there is one writer, not two.
  4. The run is recorded as soon as the agent's terminal exists, as before. Prompt delivery finishes in the background, as before.
  5. A failure after the terminal exists is now logged instead of being dropped silently.

Why

This is phase 1b, step 2 of moving every fresh agent start onto one executor. After it, only launches from the desktop window (later steps) start fresh agents outside it.

Why main's prompt writer and not the executor's own: the first version of this PR used the executor's writer for agents that read the prompt after start. That writer waits for the agent's input box, sends a multi-line prompt as one paste, and keeps waiting past the old writer's roughly 4.5 s limit. Testing showed it gives up on an agent whose input box appears more than about 8 seconds after start. Main delivers in that case, because it types early and the terminal holds the text until the agent reads it. So this PR keeps main's writer exactly. Making the executor's writer handle slow starts is a separate follow-up, because it also changes phone and CLI launches.

Labeled differences:

  • Intended: automations stay terminal-only whatever the chat default is, because their completion is detected from the terminal.
  • Intended: the tab title and background behaviour stay in the automation's own factories rather than becoming new launch inputs. Server-run automations add no new executor input.
  • Temporary: prompts for agents that read them after start use the create's older writer instead of the executor's. Its known limits are unchanged from main: it types as soon as the agent process appears, gives up after about 4.5 s, and sends a multi-line prompt as separate lines. The follow-up fixes the executor writer's slow-start gap and then moves automations onto it.

Linked Issue

Maintainer change, part of the launch-unification work (no separate issue). Follows #26703, #26709 and #26724.

Visual Proof

No visual change by design. Server-run automations have no window of their own. This is a hidden desktop app paired to a hidden test server with stand-in agents, before (main) and after (this PR), around a "new workspace per run" automation run. In both, the run's workspace appears in the sidebar with background activity, and the desktop stays on its start screen without switching workspace. These shots were taken on the PR's earlier head, 74bb7a7. Its later change touched only how a post-start prompt is typed, which these screens don't show.

Before (main) After (this PR)
Before the run h10-before-pre.png h10-after-pre.png
After a new-workspace run h10-before-newws.png h10-after-newws.png

The prompt evidence is text from the stand-in agents' logs (see the review comment). For each row it shows the bytes received and the number of submits, which were identical between main and this PR.

Testing

  • New: headless-automation-agent-launch.test.ts. For each kind of run it checks:
    • An argv agent gets the prompt on its command, and nothing is typed in.
    • A post-start agent starts bare, and its prompt is delivered exactly once, after the run is recorded.
    • The create arguments are exact.
    • A create that returns no agent terminal fails with the create's own warning and starts no second terminal.
    • A blank prompt sends nothing.
    • A missing workspace is refused.
    • With the chat default on, it is still a terminal, and the host is never asked about chat.
    • A failure after the terminal exists is logged.
  • New: deliverWorktreeStartupFollowup writes once when the agent is in front, and writes nothing when it never appears. launchAgentTerminal with an empty prompt sends no follow-up.
  • Existing: runtime-automation-service.test.ts, runtime-automation-service-retention.test.ts, headless-workspace-create.test.ts, launch-agent-terminal-followup.test.ts, orcad-automations.test.ts.
  • tc:node typecheck and CI static checks pass.
  • Manual: a hidden server on macOS with stand-in agents, comparing main with this PR. It covered "Run now" and scheduled runs, existing and new-per-run workspaces, argv and post-start agents, a folder workspace, a multi-line prompt, a slow-starting agent, a clean agent exit, and a paired desktop view. Details are in the review comment. Not run: SSH and Windows hosts.

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Server-run automations, both the new-workspace and existing-workspace runs, now start their agent
through executeAgentLaunch as a terminal-only launch with automation-owned factories. argv agents
keep the prompt folded into their command; agents that read the prompt after start get it from the
executor's own terminal writer once their composer is ready. The run is recorded as soon as the
terminal exists, as before.
… starts out of run completion

- Headless automations pass the write guard's write-unless-shell policy, as worker briefs do,
  so a host that cannot find the agent in front (Windows) still gets the prompt; only a proven
  shell refuses. agent.launch keeps refusing by default.
- judgeIdleRun ignores session-boundary status rows: a SessionStart row lands before the
  prompt and must not settle the run completed.
- A launch failure after the terminal was published is logged instead of dropped.
…iter

QA found the executor's composer-readiness writer drops the prompt of a slow-starting post-start
agent (aider): it watches the input-box signal only ~8 s, then tui-idle, which those agents never
satisfy. Main's follow-up writer (wait for the agent process, type the prompt) delivered it.

- Extract the follow-up writer into an awaitable deliverWorktreeStartupFollowup (same waits,
  write and log lines); sendWorktreeStartupFollowupWhenReady wraps it. The runtime exposes it as
  deliverStartupFollowup, and the headless launch's deliverTerminalPrompt uses it, so the executor
  still sequences the launch with main's delivery.
- Revert the round-1 write-guard option and the judgeIdleRun session-boundary rule, which existed
  only because of the executor's writer.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review and QA summary (head 9c7be59)

The problem. When Orca runs as a server, scheduled automations started their agent through their own two code paths, a new-workspace path and an existing-workspace path, instead of through the shared launch executor that every other fresh launch uses. That meant one more place where the order of launch steps had to be kept in sync by hand.

What changes for the user: nothing. Automations now start their agent through the executor. The tab title, background behaviour, prompt bytes, when the run is recorded, completion detection and run records are the same as on main.

Review

Three review rounds ran, plus an independent architecture check and a pre-release checklist.

  • Architecture check: it sketched a solution from the issue alone and arrived at the same structure: a terminal-only launch with automation-owned factories, with the run recorded as soon as the terminal exists. It recommended keeping the PR whole, because either half alone would leave the two run types on different paths.
  • Fixed during the loop:
    • The first design was rejected after QA. It used the executor's own prompt writer for agents that read their prompt after they start (Aider, Goose, Amp and about 20 others). That writer gives up on an agent whose input box appears more than about 8 seconds after start, and testing reproduced the lost prompt. The PR now uses main's writer exactly, still sequenced by the executor.
    • Two round-1 fixes were reverted with it. One let the writer type on Windows hosts; the other stopped session-start status rows from counting as the run finishing. Both were only needed for the executor's writer, and both are written into the follow-up below.
    • Failures after the run is recorded are logged. A launch failure after the terminal exists is now logged instead of being dropped silently.
  • Deferred, unchanged from main:
    • Writer limits. The writer types as soon as the agent process appears, gives up after about 4.5 s, and sends a multi-line prompt as one submit per line.
    • Planned fix: follow-up B. The executor's writer will keep watching for the input box for its whole 60 s budget, as worker start already does. Then automations move onto it, and the Windows and session-start changes come back with it. That change also affects phone and CLI launches, so it gets its own PR.
  • Other notes (P2/P3, not regressions):
    • Silent prompt loss. A prompt that never arrives still leaves a run that later reads as completed, as on main. The only trace is a server log line. Candidate follow-up: fail or annotate the run.
    • Lost prompt on server stop. If the server stops between "dispatched" and the prompt being typed, the prompt is lost, as on main.
    • Dead follow-up path. The terminal launcher's own follow-up path, from Send scheduled automation prompts to agents that read them after they start #26703, is now unreachable from automations. It is kept so a future caller can't silently drop a post-start prompt.
    • Asserted, not measured. "Prompt rode the launch command" is asserted by the automation's factories, not reported by the command builder. This is true today because both always fold the prompt in at any length.

QA

Run on hidden laptop test servers (serve mode) with stand-in agents, main vs this PR. The driver was opencode.

  • Parity at 9c7be59: these rows were identical to main in the bytes received, the number of submits, run records, titles and terminal counts:
    • Claude, existing workspace;
    • Aider, existing workspace;
    • Aider, new workspace per run;
    • a 3-line prompt to Aider;
    • an Aider that draws its input box at 10 s, which now gets its prompt, as on main.
  • Rows that also matched main, run on the earlier head:
    • "Run now" and scheduled runs;
    • Claude, new workspace per run;
    • a folder (non-git) workspace;
    • a clean agent exit;
    • a paired desktop: background activity, no workspace switch (screenshots in the PR body).
  • Already broken on main, still the same: a new-per-run workspace whose setup script runs about 8 s before the agent starts loses the prompt on both builds.
  • Not verified: SSH hosts, Windows hosts, and real agent CLIs (stand-ins only).

Checks

tc:node typecheck, the named unit tests and the CI static checks pass. CI is green on 9c7be59. One unit shard first failed on an unhandled window is not defined from an unrelated renderer test; all 2,384 test files passed in that run, and the rerun was green. The PR touches no renderer code.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 9, 2026 10:39
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

Walkthrough

The runtime automation service now delegates headless launches to a new launcher that handles existing and new-per-run workspaces. The launcher trims prompts, starts agent terminals, and delivers post-start prompts through an awaited startup follow-up. The readiness helper reports whether delivery succeeded. Tests cover launch modes, prompt handling, missing workspaces, and startup failures.


Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 9c7be

Server-run automations now launch through the shared executor with their prompt delivery unchanged. No concrete defect was found in this change. SSH and Windows hosts were not verified by the author.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 11 files. 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 identifies the main change: routing server-run automation agents through the launch executor.
Description check Passed The description is detailed and covers the user impact, implementation, rationale, linked work, visual proof, testing, limitations, and agent-skill boundary. The Review, Notes, and Checklist sections …
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.


  • 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


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/main/automations/headless-automation-agent-launch.ts (1)

148-163: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Settle the promise when the executor resolves without publishing a surface.

launchUntilPublished resolves only from onSurfacePublished. It rejects only from .catch. Suppose executeAgentLaunch resolves normally but never calls onSurfacePublished. The returned promise then stays pending. The headless dispatcher then waits indefinitely, and the run never reaches the "did not report its terminal" error on Line 128. The visible executor paths currently publish before they return, so this case cannot happen today. However, nothing in the type contract enforces that ordering. To guard against a future executor branch, add a .then that resolves or rejects when the launch did not publish.

Proposed fix
-    }).catch((error: unknown) => {
+    }).then(
+      (result) => {
+        if (!published) {
+          reject(new Error('Automation agent launch did not report its terminal.'))
+        }
+        return result
+      }
+    ).catch((error: unknown) => {

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f238d6f8-1575-4e16-91be-0c41d4a1fa1c
📥 Commits

Reviewing files that changed from the base of the PR and between 39a01f9 and 9c7be59.

📒 Files selected for processing (11)
  • src/main/agent-launch/agent-launch-executor.ts
  • src/main/agent-launch/agent-launch-legacy-host.ts
  • src/main/agent-launch/startup-agent-worktree-create.ts
  • src/main/automations/headless-automation-agent-launch.test.ts
  • src/main/automations/headless-automation-agent-launch.ts
  • src/main/automations/runtime-automation-service.test.ts
  • src/main/automations/runtime-automation-service.ts
  • src/main/runtime/launch-agent-terminal-followup.test.ts
  • src/main/runtime/orca-runtime-activate-managed-worktree.ts
  • src/main/runtime/runtime-worktree-startup-readiness.test.ts
  • src/main/runtime/runtime-worktree-startup-readiness.ts

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

@brennanb2025
brennanb2025 merged commit 1a2ccb8 into main Oct 9, 2026
77 of 79 checks passed
@brennanb2025
brennanb2025 deleted the brennanb2025/p1b-headless-automations branch October 9, 2026 22:32
ggbdpq added a commit to ggbdpq/orca that referenced this pull request Oct 25, 2026
Rebase adaptation for upstream stablyai#26820: WorktreeStartupFollowup now carries
the TUI agent (used to pick the composer-ready signal and the submit settle
delay), so the headless automation's deliverTerminalPrompt passes it and the
launch tests assert the full call shape.

Generated-by: GLM-5.3-Flash (ZCode)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant