Skip to content

[FEATURE]: Preemption primitives for sub-agent orchestration — max-turns-per-dispatch, sub-agent cancellation, per-tool-call streaming #44748

Open

Description

@stephenstubbs

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

We run opencode as the agent harness for a production multi-phase batch pipeline (openroadmap — a repo-wide march: many phases, each a separate opencode session, orchestrated by a parent). A recent no-op-loop post-mortem across 604 sessions found 208 (≈34%) were class-b no-op loops — a sub-agent repeating the same tool call (a re-read, a re-grep, a re-run) with no forward progress until a gate or manual intervention broke the cycle. The loops are cross-persona (qa 102, integration 9, archive 8, implementation 5–6).

The harness-side guardrails exist; the platform-side primitives do not. We request three platform capabilities so a harness can bound, cancel, and observe its sub-agents.

1. max-turns-per-dispatch — a hard per-dispatch turn budget

A per-subagent-Task-call maximum number of LLM/tool turns. When reached, the dispatch terminates with a distinct reason (max-turns-reached) surfaced to the parent (SDK event), partial transcript retained. The ceiling is settable by the orchestrator per dispatch, with a provider/operator default guard. AC intent: maxTurns: <N> on a dispatch; stops a loop at the bound without manual intervention or process kill.

2. Sub-agent cancellation — an operator/orchestrator-invoked cancel signal

A first-class cancel API on a specific dispatch that marks it cancelled at runtime level, terminates the child's LLM loop + pending tool call, propagates a terminal cancelled reason to the parent, and is idempotent for an already-parked/blocked dispatch. Distinct from a single tool-call interruption. AC intent: orchestrator can cancel a specific running subagent; parent reliably observes cancelled; works even when the child is parked on a permission ask / dropped stream / dead retry — no zombie sessions that block a parent turn forever.

3. Per-tool-call streaming — the tool-call output visible in-flight

Tool/output events stream to the session's event stream as the tool executes (partial output, progress, per-line for shell/exec), not only a single completion event, so a stalled or long-running call is visible and interruptible in-flight rather than surfaced only at completion. AC intent: a harness starts a long call and sees intermediate output before it completes; can interrupt it mid-flight; a stalled call is observable within a bounded window.

The three are independent and each independently addressable. Related open items: #17471 (output-token auto-continue — adjacent), #40175 (unbounded research fan-out — the unbounded-dispatch class P1 bounds), #27110 (max parallel sub-agents — a complementary concurrency ceiling). No client-side workaround is proposed — these require runtime-level guarantees.

Activity

  1. chenhz01 commented on Sep 17, 2026

    @chenhz01

    +1 on all three, from running a parent-orchestrated multi-agent setup with hundreds of dispatches. What we made mandatory after too many silent-runaway sessions: every subagent dispatch carries a max-turns budget, and every dispatch must be cancellable — no exceptions. Your no-op-loop numbers (208/604, ~34%) match our experience that the loop class is the norm, not the edge case.

    Two design notes from operating this:

    1. Surface max-turns-reached with the partial transcript in the parent's context, not just as an SDK event. The parent needs enough state to re-dispatch differently — otherwise it just re-runs the same prompt into the same loop and the ceiling becomes a paid subscription to failure.
    2. Cancellation must cover pending tool calls, not just the LLM loop. We've hit the zombie-parked-on-a-permission-ask state exactly as you describe, and an idempotent cancel that also aborts the in-flight tool call is the only thing that reliably unblocks the parent turn.

    The three primitives being independent is also the right call — max-turns alone eliminated most of our incidents, so shipping them incrementally has real value.

  2. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed write-up and the loop statistics. You're asking for three separate runtime capabilities for orchestrating subagents: a hard turn budget per dispatch that ends with a distinct max-turns-reached reason, a reliable cancel signal for a specific running subagent that the parent sees as cancelled, and tool output that streams while the tool is still running. Maintainers will consider it.

    These existing issues are related, and some cover parts of this:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions