Oven is an agent-orchestration tool for Cursor. You give it a DAG of subagent
tasks; it runs each node in dependency order, picks a model from the task's
complexity (HIGH / MED / LOW), and streams live status into a
.canvas.tsx file so you can watch work move from PENDING to RUNNING to
FINISHED or ERROR.
The npm package is @flatbread/oven. It ships:
oven— run a DAG, or initialize its canvas without an API keyoven-supervisor— self-hosting wrapper that restarts when the runner's own sources change between ranks- Library exports for tooling that authors, validates, or inspects DAGs
pnpm install
pnpm buildReal DAG runs need a Cursor API key:
export CURSOR_API_KEY=crsr_...--init-only and --dry-check-cmds do not need the key.
If @cursor/sdk cannot find its bundled ripgrep, point it at a system binary:
export CURSOR_RIPGREP_PATH=/usr/bin/rgCreate a DAG JSON file:
{
"title": "Build a tiny CLI todo app",
"tasks": [
{
"id": "design",
"depends_on": [],
"complexity": "LOW",
"subtask_prompt": "Design the minimal CLI commands and file layout."
},
{
"id": "implement",
"depends_on": ["design"],
"complexity": "MED",
"subtask_prompt": "Implement the todo CLI based on the design."
}
]
}Write the initial canvas without CURSOR_API_KEY:
pnpm exec oven \
--init-only \
--dag /tmp/example-dag.json \
--canvas-path /tmp/example-dag.canvas.tsxRun the DAG:
export CURSOR_API_KEY=crsr_...
pnpm exec oven \
--dag /tmp/example-dag.json \
--canvas-path /tmp/example-dag.canvas.tsxValidate shell commands embedded in prompts (no API key, no canvas write):
pnpm exec oven --dry-check-cmds --dag .cursor/skills/oven/examples/example_dag.jsonEvery DAG has a title and a tasks array. Each task needs:
id: unique kebab-case task id.depends_on: ids of parent tasks that must finish first.complexity:HIGH,MED, orLOW; maps to a Cursor model.subtask_prompt: standalone instructions for the subagent.
Oven computes ranks with Kahn topological sort and runs sibling tasks in the same rank concurrently. Avoid placing two sibling tasks in the same rank if they write the same files.
Optional top-level models can override the default complexity map with plain
SDK model id strings or SDK model selections:
{
"models": {
"HIGH": {
"id": "gpt-5.4",
"params": [{ "id": "reasoning", "value": "high" }]
},
"MED": "composer-2",
"LOW": {
"id": "gpt-5.4-nano",
"params": [{ "id": "reasoning", "value": "low" }]
}
}
}Use the object shape when you need params; use a string when the model id is
enough. For example, use { "id": "gpt-5.4", "params": [{ "id": "reasoning", "value": "high" }] }, not a suffix-style id like gpt-5.4-high.
When a DAG runs, Oven calls Cursor.models.list(), validates model ids and
param values, and expands partial selections to the closest valid SDK preset
variant using that model's default variant for omitted params. --init-only
does not call the SDK, so it can still render a canvas without CURSOR_API_KEY.
Optional task kinds add control gates:
kind: "oracle"runs a shell command and records pass/fail evidence.kind: "pause"waits for a checkpoint sentinel so a human can inspect or approve before downstream work continues.
See .cursor/skills/oven/examples/example_gates_dag.json for a small DAG that combines mixed complexity, an oracle, a pause, and a DAG.loops entry.
Bounded convergence loops can live in the DAG itself instead of only on the CLI. This keeps the run reproducible: contributors do not need to remember a matching --converge-on ... --max-iterations ... flag pair.
{
"title": "implement then review until clean",
"loops": [
{
"convergeOn": "review",
"maxIterations": 3,
"reexecute": { "kind": "tasks", "tasks": ["implement"] }
}
],
"tasks": [
{
"id": "implement",
"depends_on": [],
"complexity": "MED",
"subtask_prompt": "Implement the feature."
},
{
"id": "review",
"depends_on": ["implement"],
"complexity": "HIGH",
"subtask_prompt": "Review the implementation. Use `## Blockers` and `## High-severity findings` when needed."
}
]
}Notes:
- Omit
idto get the defaultloop-<convergeOn>id. - Omit
reexecuteto re-run the full ancestor cone, which matches the legacy CLI behavior. reexecute: { "kind": "tasks", "tasks": [...] }must stay inside the convergence task's ancestor cone and be dependency-closed for every non-convergeOntask it names; invalid subsets fail fast during DAG parsing with the missing ancestor ids.- Parsed explicit rerun lists always include
convergeOnitself, even if the authored JSON omits it. DAG.loopsand--converge-onare mutually exclusive. If the DAG already declares loops, remove the CLI flag instead of relying on precedence.- Multiple loops are allowed only when their re-execution sets are disjoint, so one loop cannot invalidate another loop's converged result later in the run.
By default, every full DAG run writes per-task markdown transcripts to a timestamped directory (not --init-only, which exits before artifact setup, and not --dry-check-cmds, which never enters the runner):
<repo-root>/.oven/artifacts/dag-<title-slug>-<timestamp>/
_dag.json # The original DAG definition
_index.md # Run summary: outcome, timings, and links to all transcripts
<task-id>.md # Full agent output for each task (kind: task, oracle, or pause)
<task-id>.stream.txt # Append-only assistant transcript mirror (`kind: task` only)
Execution vs canvas:
- For
kind: "task"only, stitched prompts, in-process convergence parsing (--converge-on/DAG.loops),${task-id}.findings.jsonpayloads (--findings-dir), and<task-id>.mdderive from an execution-authoritative transcript. Resumed runs can reconstruct that transcript when the same--full-output-diris reused andtranscriptPathpoints at${task-id}.stream.txt; otherwise legacy boundedresultTextremains the fallback. - The inlined canvas payload snapshots only a 4000-character display tail (
CANVAS_DISPLAY_CAP) per task plus an optionaltranscriptPathwhen${task-id}.stream.txtis mirrored. - Author
DAG.outputPolicy.upstreamas"full"or"summarize"(default) to widen or keep the upstream excerpt policy; trims carry visible counted banners. - Downstream nodes are skipped with
ERRORwhen any upstream isERRORorBUDGET-EXCEEDED.
Paths resolve from --cwd (defaults to the process working directory). The live canvas still defaults under ~/.cursor/projects/<workspace-slug>/canvases/ when using --canvas without --canvas-path.
Previously, transcripts only appeared when you passed --full-output-dir; now they land under .oven/ by default. Use --no-artifacts for opt-out, or --full-output-dir to redirect elsewhere.
--no-artifacts suppresses transcripts, _index.md, and _dag.json only. --findings-dir JSON sidecars use a separate path — omit that flag (or point it elsewhere) if you need completely artifact-free output besides the canvas.
To suppress artifact writing:
pnpm exec oven --dag /tmp/my.json --canvas-path /tmp/my.canvas.tsx --no-artifactsTo write artifacts to a custom path:
pnpm exec oven --dag /tmp/my.json --canvas-path /tmp/my.canvas.tsx \
--full-output-dir /path/to/my-artifacts/The canonical Cursor skill entrypoint lives at:
.cursor/skills/oven/SKILL.md
Use that skill when a request asks to decompose work, run subagents in parallel, or execute a task as a dependency graph. The legacy .cursor/skills/dag-task-runner/SKILL.md entry remains as a compatibility handoff and points to Oven.
For read-only reviews of loop semantics, resume/restart boundaries, budget handling, and failure-mode ergonomics, use:
.cursor/agents/oven-runtime-skeptic.md
When the DAG may edit Oven itself, use the supervisor:
pnpm exec oven-supervisor \
--dag /tmp/example-dag.json \
--canvas-path /tmp/example-dag.canvas.tsx \
--state-path /tmp/example-dag-state.jsonThe supervisor adds --restart-on-runner-change. If runtime files change after a rank, Oven persists state, exits with code 75, and the supervisor resumes from the state file under the rebuilt runtime.
Each supervisor-spawned runner picks a new default .oven/artifacts/dag-<slug>-<timestamp>/ directory unless you pin --full-output-dir <path> on the supervisor command so every child inherits the same path.
After editing src/**, rebuild before resuming packaged CLI runs:
pnpm buildpnpm typecheck
pnpm build
pnpm test
pnpm lint
pnpm models:list
pnpm exec oven --dry-check-cmds --dag .cursor/skills/oven/examples/example_dag.jsonpnpm test runs the AVA suite (parser, bounded loops, output retention, and the cloud-agent fetch script smoke tests).
Oven also exposes helpers for tooling:
import {
computeRanks,
createModelSelectionResolver,
parseDAG,
resolveModelSelectionFromCatalog,
runDryCheck,
type DAG,
type TaskState,
} from '@flatbread/oven';The public API includes DAG parsing and rank computation, model resolution, canvas state types, convergence helpers, dry command checks, oracle and pause helpers, and self-hosting state utilities.