agent(prompt, *, agent_type="task", model=None, context=None, label=None, schema=None)— run ONE subagent; returns its final text, or the validated object whenschema(a JSON Schema dict) is given. Withschemathe subagent is forced to emit structured output that is validated for you — branch on the object, not on parsed prose.agent_typepicks a discovered agent ("explore", "reviewer", "oracle", …);contextis shared background;labelnames the artifact. Subagents are told their final text IS the return value, so they hand back raw data.agent()blocks until the subagent finishes; eval-spawned agents nest at most 3 deep.parallel(thunks, *, concurrency=4)— run zero-arg callables concurrently through a bounded pool (default 4, max 16), preserving input order; returns once all finish. A thunk that raises propagates — wrap risky work intry/exceptinside the thunk to keep partial results. In a loop, bind each closure's value with a default arg (lambda d=d: …) or every thunk captures the last one.pipeline(items, *stages, concurrency=4)— map items throughstagesleft-to-right. There is a BARRIER between stages: ALL items clear stage N before stage N+1 begins. Each stage is a one-arg callable; stage 1 gets the original item, later stages get the previous result.llm(prompt, *, model="default", system=None, schema=None)— oneshot, stateless model call (no tools, no history). Tiers: "smol", "default", "slow". Cheap classification/scoring inside a fan-out.log(message)— emit a progress line above the status tree.phase(title)— start a phase; the status lines that follow group under it.budget—budget.total(output-token ceiling, orNonewhen none is set),budget.spent()(tokens spent this turn — main loop + eval subagents),budget.remaining()(math.infwhen total isNone),budget.hard(whether it's enforced). A ceiling is set by the user:+Nkin their message is advisory (you self-limit viabudget.remaining()),+Nk!(or Goal Mode) is hard —agent()refuses to spawn once spent reaches it. Gate loops onbudget.totalfirst, since it'sNonewhen the user set no budget.
Everything runs INLINE and synchronously inside the eval call — no background mode, no resume, no separate progress app. Each eval call is one well-scoped fan-out; chain several across cells and turns for multi-phase work, reading each result before you decide the next phase.
For independent per-item chains (review → verify, fetch → extract → score), wrap the WHOLE chain in one function and run it with `parallel()` — then each item flows through its own steps without waiting on the others:DIMENSIONS = [{"key": "bugs", "prompt": "…"}, {"key": "perf", "prompt": "…"}]
def review_and_verify(d):
found = agent(d["prompt"], label=f"review:{d['key']}", schema=FINDINGS_SCHEMA)
return parallel([lambda f=f: {**f, "verdict": agent(
f"Refute if you can (default refuted when unsure): {f['title']}",
label=f"verify:{f['file']}", schema=VERDICT_SCHEMA)} for f in found["findings"]])
phase("Review")
results = parallel([lambda d=d: review_and_verify(d) for d in DIMENSIONS])
confirmed = [f for group in results for f in group if f["verdict"]["is_real"]]
Reach for pipeline() only when a stage genuinely needs ALL of the previous stage first — dedup/merge across the whole set, early-exit on zero, or "compare against the other findings" — because its inter-stage barrier makes every item wait for the slowest peer:
phase("Find")
found = parallel([lambda d=d: agent(d["prompt"], schema=FINDINGS_SCHEMA) for d in DIMENSIONS])
findings = dedupe([f for r in found for f in r["findings"]]) # needs everything at once
phase("Verify")
verdicts = parallel([lambda f=f: agent(verify_prompt(f), schema=VERDICT_SCHEMA) for f in findings])
Don't add a barrier just to flatten/map/filter — do that with plain Python between calls. Nested parallel() pools each cap independently, so keep total fan-out sane.
Scale to the ask: "find any bugs" → a few finders, single-vote verify. "thoroughly audit / be comprehensive" → larger finder pool, 3–5-vote adversarial pass, a synthesis stage.
- Decompose the surface first; capture it in `todo_write` when it spans phases. - Prefer `schema=` for any agent whose output you branch on. - After a fan-out returns, YOU own correctness: read the artifacts, run the gate, verify before acting. Subagents do the legwork; they don't get the last word. - Keep going until the task is closed — a returned fan-out is a step, not a stopping point.