Skip to content

feat(dev): supervise every runtime in headless mode - #2138

Merged
tejaskash merged 3 commits into
refactorfrom
feat/headless-multi-agent
Sep 2, 2026
Merged

feat(dev): supervise every runtime in headless mode#2138
tejaskash merged 3 commits into
refactorfrom
feat/headless-multi-agent

Conversation

@tejaskash

Copy link
Copy Markdown
Contributor

What

--mode headless without --agent now supervises every runtime in the project, streaming attributed [name] output to the terminal, the same behavior browser mode gets from the Inspector. Follows the discussion on #2086: multi-agent terminal supervision (useful for A2A testing) was the interim behavior on refactor between #2041 and #2086, and this restores it as a first-class mode instead of requiring --agent.

  • --mode headless (no --agent): all runtimes start eagerly, output is [name]-attributed, one agent crashing leaves the others running, and the command exits non-zero only when nothing is left running and something failed.
  • --mode headless --agent <name>: unchanged, the direct single-runtime path where a crash fails the command with a real exit code (scripts and CI rely on this).
  • Browser mode: unchanged.

Verification

  • Handler suite covers the new path: attributed output and per-runtime OTEL env for two supervised runtimes, crash isolation, SIGINT exits 130 and closes the collector, all-agents-failed exits non-zero.
  • End to end against a real 2-runtime project: project dev --mode headless started both agents eagerly on distinct ports with [bob]/[alice] attributed output, and SIGTERM tore both down.
  • Full suite (2163 pass), typecheck, format clean. The two oxlint errors are pre-existing on refactor (DataTable.tsx, usePagedList.tsx).

@github-actions github-actions Bot added the size/m PR size: M label Aug 28, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Aug 28, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
@codecov-commenter

codecov-commenter commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.16%. Comparing base (6357f2e) to head (2089652).

Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2138      +/-   ##
============================================
+ Coverage     97.14%   97.16%   +0.02%     
============================================
  Files           519      519              
  Lines         35492    35506      +14     
============================================
+ Hits          34478    34499      +21     
+ Misses         1014     1007       -7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@agentcore-devx-automation agentcore-devx-automation 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.

AgentCore Harness Review

Verdict: Looks good

The change cleanly extends headless mode to supervise every runtime by reusing DevSupervisor instead of restricting to a single agent. I traced the new loop in src/handlers/project/dev/index.ts (lines 225–240) against DevSupervisor.launch/pump/events, and the phase/event ordering holds up:

  • entry.phase = "starting" is set synchronously in launch() before any await, so the initial Promise.allSettled(runtimes.map(start)) guarantees no agent looks "idle" when the loop first iterates.
  • Failure events are pushed after entry.phase = "failed", so snapshot() on each iteration is consistent with what the user just saw rendered.
  • The every(!starting && !running) + some(failed) predicate correctly avoids tearing down peers when one agent dies but others are still running, and only escalates to SilentCLIError when the whole cohort is down and at least one failed.
  • Single-runtime --mode headless without --agent now falls into this path and exits gracefully when the lone runtime stops — a strict improvement over the previous hard-error.
  • Cleanup: since the throw only fires once no agent is still starting/running, there are no orphan children when SilentCLIError propagates and the finally block closes the collector (verified by state.closed === 1 in the new test).

Test coverage looks appropriate: stayingRunner is a good addition and models a real dev server better than captureRunner for these cases, the removed --mode headless requires --agent and --port with several runtimes is rejected assertions are correctly folded into the parameterized cases / new tests, and the new tests exercise attributed output, per-runtime OTEL env rewrite, partial-failure survival, and total-failure exit.

One minor UX nit worth being aware of (not a blocker): when both agents fail nearly simultaneously and both failure events land in the supervisor queue before the outer loop drains it, the break can fire after the first yield and the second agent's failure line is never rendered. Users still get non-zero exit and the first failure message, but if you wanted symmetry you could drain remaining failed-phase events from supervisor.snapshot() before throwing. Fine to defer.

Nothing here that needs to change before merging.

@agentcore-devx-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 28, 2026
const starts = Promise.allSettled(
runtimes.map((runtime) => supervisor.start(runtime.name)),
);
for await (const { agentName, event } of supervisor.events()) {

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.

I think normal Ctrl-C gets reported as a crash in this supervised headless path. The production process runner rejects with the child signal's UserCancellationError, so the supervisor emits Agent 'orders' crashed: Operation cancelled by user before the command exits 130. Could the supervisor treat an error from an already-aborted child signal as a normal stop? A test where the runner throws input.signal.reason on abort would cover the production behavior better than stayingRunner, which returns cleanly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, fixed. The pump now treats an error from an already-aborted child signal as a stop, and the runner fakes throw the abort reason like the real runner so the SIGINT tests cover the production path. Verified end to end: Ctrl-C exits 130 with no crash line.

@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Aug 28, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026

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

lgtm, agree with Aidan's comment that we don't want to show user cancellations as errors

@AlexanderRichey

Copy link
Copy Markdown
Contributor

Stale

@tejaskash tejaskash reopened this Sep 2, 2026
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Sep 2, 2026
--mode headless without --agent now runs all of the project's runtimes at
once with attributed output, matching the supervised behavior browser mode
gets from the Inspector. One agent crashing leaves the others running, and
the command exits non-zero only when nothing is left running and something
failed. --agent keeps the direct single-runtime path where a crash fails
the command.
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Sep 2, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 2, 2026

@agentcore-devx-automation agentcore-devx-automation 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.

AgentCore Harness Review

Verdict: Looks good

I traced the new headless multi‑agent loop in src/handlers/project/dev/index.ts (lines 205–237) against DevSupervisor.launch/pump/events and it holds together: entry.phase is set to "starting" synchronously in launch before any await, so the first pass of the for await never sees all-idle; failure status events are pushed after the phase flip, so snapshot() after each yielded event is consistent with what was just rendered; and the every(!starting && !running) + some(failed) predicate correctly avoids tearing peers down when only some agents die. The test additions (stayingRunner, per-runtime OTEL env assertions, partial-failure survival, total-failure exit, collector-close verification) are appropriate and exercise the interesting states without over-mocking.

The one real correctness issue — that a normal Ctrl‑C on a running cohort will land in pump's catch as a UserCancellationError, flip every phase to failed, render "Agent 'X' crashed: Operation cancelled by user", and throw SilentCLIError (exit 1) instead of falling through to controller.signal.throwIfAborted() (exit 130) — has already been flagged by @aidandaly24 and seconded by @Hweinstock, and hasn't been addressed on the current head (ba8cddf). Nothing new to add from me; that's the thing to fix before merging.

@agentcore-devx-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Sep 2, 2026
The process runner rejects with the abort reason on teardown, so Ctrl-C in
supervised headless mode printed 'Agent crashed: Operation cancelled by
user'. The pump now treats an error from an already-aborted child signal as
a normal stop. The runner fakes throw the abort reason like the real runner,
so the SIGINT tests exercise the production path.
@tejaskash
tejaskash force-pushed the feat/headless-multi-agent branch from ba8cddf to 2089652 Compare September 2, 2026 17:20
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Sep 2, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 2, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 2, 2026
@tejaskash
tejaskash merged commit 89d302b into refactor Sep 2, 2026
22 checks passed
@tejaskash
tejaskash deleted the feat/headless-multi-agent branch September 2, 2026 17:47
This was referenced Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants