Repository navigation
fix(automations): a headless run is watched past tui-idle timeouts by the one run observer - #25733
Conversation
There was a problem hiding this comment.
Important
This branch ships a red test. src/main/orcad/orcad-automations.test.ts fails with TypeError: runtime.getTerminalHandleForPaneKey is not a function, because the headless dispatch path now always calls watchRun (the dispatcher no longer returns a completion) and its fake runtime was never updated. CI's unit job runs this file. Fix before merge.
Reviewed changes
- Headless watchdog unified into the run observer:
headless-run-completion.tsis deleted; its missing-command and idle-grace rules move to newautomation-run-agent-evidence.ts(judgeIdleRun,AGENT_START_GRACE_MS), and the existing observer now consumes them. - Observer judges satisfied
tui-idlewaits by agent evidence: when anAutomationRunand evidence are supplied,runtime-terminal-run-observer.tsskips the reused-pane busy edge and resolves the run viajudgeIdleRun, re-polling every 1s while the agent may still be starting. Wait timeouts keep re-arming under the existing 6h deadline. - Dispatcher stops owning a completion loop:
runtime-automation-service.tsreturns nocompletion, remembers each launch's handle by pane key (capped at 256) forresolveRunTerminal, andrunHeadlessAutomationDispatchnow always hands the run towatchRun; the watcher forwardsrunintoobserveCompletion. - Precheck survives the watched completion:
headless-dispatch-runner.tspersistsprecheckResulton thedispatchedwrite, since the watcher'smarkDispatchResultcarries no precheck.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
| terminalObserver: { | ||
| ...observer, | ||
| resolveRunTerminal: (run) => | ||
| observer.resolveRunTerminal(run) ?? |
There was a problem hiding this comment.
This wrapper now runs on the headless dispatch path, and a runtime stub that only faked the old loop breaks. The dispatcher no longer returns a completion, so runHeadlessAutomationDispatch always calls watchRun → watcher.watch → resolveRunTerminal, which calls the observer's default runtime.getTerminalHandleForPaneKey(...) and, when the watcher starts observing, the evidence's runtime.getAgentStatusRowsForPane(...) (automation-run-agent-evidence.ts:26). src/main/orcad/orcad-automations.test.ts's headlessRuntime() fakes waitForTerminal/readTerminal but neither of those, so the suite fails with TypeError: runtime.getTerminalHandleForPaneKey is not a function (and would next hit getAgentStatusRowsForPane).
Technical details
# Headless dispatch now needs the terminal host's pane methods on every runtime stub
## Affected sites
- `src/main/automations/runtime-automation-service.ts:44` — `resolveRunTerminal` wrapper calls `observer.resolveRunTerminal` unconditionally.
- `src/main/orcad/orcad-automations.test.ts:16` — `headlessRuntime()` stubs `waitForTerminal`/`readTerminal` but not `getTerminalHandleForPaneKey` or `getAgentStatusRowsForPane`.
- `src/main/automations/headless-dispatch-runner.ts:75` — `if (!launch.completion)` is now always true, so `watchRun` always fires.
## Required outcome
- `pnpm test src/main/orcad/orcad-automations.test.ts` passes; every runtime stub driving the headless dispatch path satisfies its terminal-host dependencies.
## Suggested approach
- Add `getTerminalHandleForPaneKey: vi.fn(() => 'term-1')` and `getAgentStatusRowsForPane: vi.fn(() => [])` to that test's fake runtime.|
This run was cancelled 🛑 The workflow was cancelled before completion. Please check the link below for details. |
… the one run observer The headless dispatcher awaited a single tui-idle wait, which rejects after its 5-minute default, so a healthy agent working longer was published as dispatch_failed and never observed again. The dispatcher now hands the run to its completion watcher, whose runtime observer already re-arms wait timeouts, honours cancellation and bounds total observation; the agent status and missing-command checks fold into that observer, and the separate completion loop is gone.
…w passes Real-host: a stub that exited before the window left an idle shell with no agent status, and the observer re-armed a tui-idle wait that never resolves for an already-idle shell, so it timed out instead of completing. The observer now keeps judging the pane while its output is unchanged, and only waits again once the pane changes.
52131e1 to
f6e0b67
Compare

ELI5
When an automation ran on a remote Orca server and its agent kept working for more than 5 minutes, Orca marked the run "failed" and stopped watching it, even though the agent was fine. Now the run is watched for as long as the agent works, and it ends as completed or failed based on what the agent actually did.
What Changed
Before: a server dispatch (orcad and Electron
orca serve) followed each run with its own short completion loop. That loop waited for the terminal to look idle, and that wait gives up with a timeout error after 5 minutes. The error escaped the loop, and the dispatch runner published the run asdispatch_failed. Nothing watched the run after that, so a long agent turn always read as failed. Two independent reviewers found this (Astra pass 5 §2 and round 8).After: the server dispatcher no longer runs its own loop. It hands the launched run to the run-completion watcher, which already exists. That watcher uses the runtime terminal observer, and the observer already:
The checks from #25700 move into that observer. They run only when the observer is given a run and agent evidence:
The separate completion loop is deleted. Two smaller changes support this:
dispatched, because a watched run's completion is written without it.Why
Linked Issue
Fixes # (Astra-5 §2 / round-8 finding)
Visual Proof
N/A. No UI change.
Testing
New
runtime-terminal-run-observer-evidence.test.ts:request_abortedand records nothing.runtime-automation-service.test.ts: the server dispatch returns no completion and never waits on the terminal itself. The watcher resolves the launch's own handle even when the pane key doesn't resolve yet. Run against the old dispatcher, this test fails.Locally the automations suites pass (33 files, 239 tests). Typecheck and the changed-code quality gate are clean.
I manually tested these changes locally
Automated tests added/updated, or explained why not below
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
This applies to server hosts on any platform (Linux, macOS, Windows, SSH). The desktop runner is unchanged. No wire changes.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)