Repository navigation
Description
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
Multi-phase tasks are slow in wall-clock time because independent subtasks run sequentially. The task tool already supports background=true and the BackgroundJob registry forks each job into its own scope, so parallel execution is possible — but the model has to orchestrate it manually by issuing multiple task calls in one message and then collecting each background notification. That's error-prone: the model often forgets to wait, polls, or duplicates work.
A focused orchestrator agent that does fan-out / fan-in with discipline would close the gap without a new scheduler:
- decomposes a complex objective into 2–7 independent subtasks,
- dispatches each via
taskwithbackground: truein parallel, - respects
concurrency: 10(Effect.all({ concurrency: 10 })) anddepth: 1(subagents cannot spawn further subagents), - continues on partial failure and reports it,
- caps cost per dispatch.
This is the upstream ask in #47105 (task-parallel fan-out) and the open PR #47107 (task-parallel tool, everest-an, non-draft, 4 files). That PR is a solid invoker: pass tasks[], run Effect.forEach, return a <parallel-tasks> summary. This request is the harness around the same idea — scoping it as an agent, not a tool, so the discipline lives in the system prompt rather than in ad-hoc tool arguments.
The two are complementary, not competing: #47107 can land as the builtin tool for explicit calls; this agent is the opt-in harness for when the model should decide to fan out on its own (keyword trigger, no behavior change when the flag is off). If maintainers prefer one path, happy to fold into #47107 as review suggestions (e.g. concurrency: 10 instead of unbounded, opt-in flag) rather than a separate effort.
Why this complements existing issues (honest scope — alivia vs resolve, verificado contra os bodies reais):
opencode run(headless) exits 0 mid-pipeline after a parallel subagent fan-out, before the model's next turn #37548 (headless exit mid-fan-out) — alivia, não resolve. Fan-in disciplinado viaBackgroundJob.waitfecha o race que causa o silent success na maioria dos runs; mas o root cause está noopencode runcommand (exit 0 antes do next turn) — precisa fix próprio.- V2 beta: Transport error / hang when spawning multiple custom-agent subagents with large inline prompts #43594 (V2 transport hang, prompts grandes) — alivia, não resolve. Cap
concurrency: 10+ prompt curto por worker + limitemaxPromptBytesPerWorker: 8000(proposto) reduzem pressão no transport; mas é regression de transport da V2 (funciona em 1.18.15) — precisa fix no transport layer. - Session cost excludes subagent cost (TUI sidebar, opencode stats, /export) #45417 (cost exclui subagents no display) — atribui, não agrega. Harness checa
totalSessionCost()antes de cada dispatch (cap observável); mas TUI/stats/export precisam somarparent_idrecursivo — fix separado no display layer. - [FEATURE]: Preemption primitives for sub-agent orchestration — max-turns-per-dispatch, sub-agent cancellation, per-tool-call streaming #44748 (preemption primitives) — referência, não resolve. Harness seria o primeiro consumer de
max-turns-per-dispatch/cancel/streaming, mas funciona sem eles. - [FEATURE]: Anything similar to Claude code dynamic workflows? #30308 (dynamic workflows) — referência, não resolve. Fan-out-and-synthesize é o pattern mínimo; dynamic harness generation é state-of-the-art separado (Anthropic 06/2026).
Expected gain (DEFERRED until H1-H5 measured — honest, per quality-gate):
- Wall-clock ~2.5× (3×20s) to ~4× (5×30s): projection on recall lab corpus (
docs/POC-orchestrator.md3 scenarios), not measurement. Without an orchestrator-specific dataset, no numeric gain is claimed as verified; H1 will be measured in Sprint 2 withtime opencode runbefore/after. - Discipline ~30% error-prone → 0% by construction: [FEATURE]: add task-parallel tool to fan out independent subtasks concurrently #47105 reports the model "often forgets to wait, or polls, or duplicates work"; [FEATURE]: Preemption primitives for sub-agent orchestration — max-turns-per-dispatch, sub-agent cancellation, per-tool-call streaming #44748 reports ~34% no-op loops from missing bounds. Claim: harness enforces
background: truealways,concurrency: 10always,task: denyon workers always — so the counted failure class should drop to 0%. Verification: H5 in lab (flag OFF 0% spurious, flag ON 100% intended). - Cost capped: $0.50 per dispatch via
totalSessionCost()(0-safe when the provider returns cost=0, D5+D6 as backstop) vs linear unbounded billing (Session cost excludes subagent cost (TUI sidebar, opencode stats, /export) #45417 as baseline). H4 deferred — provider must return cost; local models cost=0.
Proposed shape (behind an opt-in experimental flag, no default behavior change — same posture as OPENCODE_EXPERIMENTAL_TRANSCRIPT_RECALL in the recall work):
- Agent
orchestrator(mode: all,steps: 7, read-only by default — noedit/write/patch/bash, allowread/grep/glob/webfetch/websearch) with a prompt that encodes the discipline above (9 bullets + 5-step protocol + cost & output format).allis needed so the harness itself is not blocked by thesubagent_depthguard when delegating (see feat(opencode): add task-parallel tool to fan out independent subtasks concurrently #47107's opposite choice:concurrency: "unbounded"without a harness discipline — this agent is the disciplined alternative). - Agent
worker(read-only,task: deny,todowrite: deny) — workers that cannot spawn further subagents (enforced byderiveSubagentSessionPermissionatdepth: 1;Truncate.GLOBallow preserved). - An opt-in flag (same
enabledByExperimentalpattern asOPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS, e.g.OPENCODE_EXPERIMENTAL_ORCHESTRATOR,experimental || enabledsemantics) + a keyword trigger (orchestrate|orchestrator|parallel|fans?out|delegateatstep===1aftertitleand after the overflow guard, immediately beforeagents.get(lastUser.agent),\bboundaries, contíguofans?out— notfan outwith space norfan-outwith hyphen) — no behavior change when the flag is off.
Why a harness matters vs a tool: a tool is an invoker (the model decides when/how to call it); the harness is where discipline is enforced — background: true always, concurrency: 10 always, task: deny on workers always, steps: 7 (2–7 workers + synthesize), cost cap observability via totalSessionCost(). The recall landed the same way: substrate first, gated, lab-verified, no breaking change.
Out of scope: dynamic workflow generation (Claude writes its own harness), LangGraph-style state machines, Temporal-style multi-cluster durability, and TUI cards — all already exist or are separately tracked. The open task-parallel tool PR #47107 is complementary (not a substitute): this harness delegates to it internally if/when both land, rather than re-implementing the dispatch logic.
Related prior art: #37421 (MCP hot reload), #34492 (unified watch service), #8751 (agent/skill hot reload) — different domain, same pattern of unifying scattered issues under one design discussion.
Happy to work on this behind a design review if maintainers agree with the scope; also glad to fold it into #47105 / #47107 as review suggestions if you prefer one umbrella effort (e.g. add a cap on concurrency for the tool, an opt-in flag for the harness, and a dedicated read-only worker) rather than a separate agent.
Refs: #47105 (primary ask), #47107 (complementary tool PR, non-draft), #37548, #43594, #45417, #44748, #30308.
Activity
github-actions commentedon Sep 6, 2026
This issue might be a duplicate of existing issues. Please check:
The feature described here is closely related to (and partially superseded by) #47105 and #47107. Please consider whether this can be folded into those existing efforts rather than tracked separately, as the issue itself suggests.
AllanSantos-DV commentedon Sep 26, 2026
Answering the duplicate-check question, then reporting where this landed.
On folding into #47105 / #47107 — I'd keep them separate, for a concrete reason: #47107 (
task-parallel) is an invoker. The model decides when to call it. This is the harness that decides whether to fan out at all, plus the resiency needed for the fan-in to actually complete. In testing, the invoker alone doesn't fix the failure #47105 describes — the model still skips the fan-out, and when a subagent dies mid-flight the parent waits on a job that never reports. So the two pieces are: opt-in harness (when/whether to fan out) + dependable completion (what happens when a worker dies). Happy to be told otherwise; the harness part is a prompt and two agent definitions, so it folds cheaply if maintainers prefer one umbrella.Scope — two complementary capabilities, one feature:
step === 1;orchestratorand read-onlyworkeragents; workers cannot spawn subagents (task: deny); per-dispatch cost cap viatotalSessionCost(). No behavior change when the flag is off.wakeop to avoidrunLoopdeadlock, non-blocking notify so async subtasks don't block the parent, and sanitizing every orphanedrunningtool part on resume (previous code took only the first via.find()).Splitting these would ship the harness without the case where it matters most: a worker that dies mid-fan-out.
State of the branch (
feat/async-subagents-orchestrator, on currentupstream/dev):bun turbo typecheck30/30 ·tsc --noEmit0 errors · 28/28 intest/tool/task.test.tstask_id, four invariants asserted (state=error,metadata.interrupted=true,time.end >= time.start, zero orphanrunningparts)No speedup numbers claimed. The issue defers H1–H5 and I'm keeping that. What I validated is crash-resumption, not wall-clock. The 2.5–4× figure stays a projection until there's a fan-out-specific dataset.
Would welcome a design review on the harness shape before I open the PR — particularly whether the trigger and the cost cap belong in the agent definition or in the tool layer.
neriousy commentedon Oct 21, 2026
Thanks for the thorough write-up and testing. Your branch is built on V1 internals (
BackgroundJob,runLoop) that don't exist in V2, and V2 already covers most of the runtime side: thesubagenttool can run subagents in the background and notifies the parent when they finish, with no polling, and asubagentpermission rule can stop workers from starting their own subagents (https://opencode.ai/v2/docs/agents). The orchestrator part is, as you say, a prompt plus two agent definitions, so it works best as custom agents you share with others (https://opencode.ai/v2/docs/agents) rather than something built into opencode. A native parallel fan-out tool is still tracked in #47105. If you hit a gap in V2's subagent completion or recovery, please open a bug with steps to reproduce on V2. Closing as not planned.