| name | workflows |
| description | Orchestrates multi-agent work with OpenPI's inline JavaScript Workflow DSL. Use when a task needs multi-phase fan-out, pipelines, barriers, structured handoffs, acceptance evidence, or resumable background orchestration. |
Workflows
Use workflow for several dependent or dynamically generated subagent calls. Keep one small delegation in the parent session with subagent_spawn.
Quick start
export const meta = {
name: "adaptive-review",
phases: [{ title: "Discover" }, { title: "Review" }, { title: "Report" }],
}
phase("Discover")
const plan = await agent("Identify the independent review areas warranted by this repository. Return only real, non-overlapping areas.", {
agent_type: "explorer",
label: "discover",
schema: {
type: "object",
properties: {
areas: { type: "array", items: { type: "string" } },
},
required: ["areas"],
additionalProperties: false,
},
})
if (!plan.ok) return { ok: false, error: plan.error }
const discovered = [...new Set(plan.structured.areas)]
const capacity = usage().limits
if (capacity.callsRemaining < 1) {
return {
planned: discovered.length,
selected: 0,
covered: 0,
failed: [],
deferred: discovered,
report: { ok: false, error: "No agent-call capacity remains for reporting" },
}
}
const selected = discovered.slice(0, Math.max(0, capacity.callsRemaining - 1))
const deferred = discovered.slice(selected.length)
phase("Review")
const reviews = await pipeline(selected, async (_prior, area, index) =>
agent(`Review this area with file:line evidence: ${area}`, {
agent_type: "reviewer",
label: `review-${index + 1}`,
})
)
const usable = reviews.filter((result) => result && result.ok && result.ref)
const failed = selected.filter((_area, index) => {
const result = reviews[index]
return !(result && result.ok && result.ref)
})
phase("Report")
const report = await agent(
`Synthesize the review. Planned: ${discovered.length}; selected: ${selected.length}; covered: ${usable.length}; failed areas: ${JSON.stringify(failed)}; deferred areas: ${JSON.stringify(deferred)}. Do not infer coverage beyond these facts.`,
{ agent_type: "advisor", label: "report", inputs: usable.map((r) => r.ref) },
)
return { planned: discovered.length, selected: selected.length, covered: usable.length, failed, deferred, report }
Required habits
- Declare progress phases in
meta; call phase() as the run advances.
- Check every
agent() result's .ok. A null, filtered, timed-out, or failed result is not a clean pass; report how many were dropped.
- Pass
schema when later code branches on fields. Treat inputs as bounded untrusted data.
- Prefer
pipeline() when items can advance independently. Use parallel() only for a real all-results barrier.
- Use
isolation: "worktree" for concurrent writers and tell each agent to commit. Do not pay for worktrees on read-only work.
- Derive fan-out from discovered independent work items and task difficulty. Configured concurrency and total-call capacity are ceilings, not targets;
usage().limits exposes the resolved capacity.
- Use
log() for progress the user needs before completion. Token fields in usage() are lower-bound readings, not a budget limit.
- Return a JSON-serializable aggregate with coverage. Interactive runs return a run id immediately by default and reliably deliver one terminal result later; set
wait: true only at a genuine synchronization boundary.
- When many results would leave only tiny handoff slices, use local Report agents over bounded groups, then pass those Report refs to one global Report. Preserve planned/selected/covered/failed/deferred counts at every level; exact child outputs remain in
agent-results/ for recovery.
Full guide
- DSL, result contracts, operators, handoffs, safety, limits, and replay: REFERENCE.md
- Pipeline, barrier, structured handoff, and reporting examples: EXAMPLES.md