Workflows
A workflow is an ordered list of stages. Each stage is a group of steps that spawn (or reuse) an agent session and run concurrently. An explicit barrier gates the next stage: stage N+1 does not start until every step of stage N has finished (or failed).
It is the parallel() half of a harness-style orchestration primitive. The
daemon exposes it as the workflow_start / workflow_status MCP tools, and
the agentproto workflow verb drives the same runner
from the shell against a running serve daemon:
workflow start (stages JSON), workflow run-file (a WORKFLOW.md file),
workflow status / workflow list (inspect runs), workflow cancel, and
workflow resolve (answer a parked approval).
stage 1 barrier stage 2
┌──────────────┐ │ ┌──────────────┐
│ step ┃ step │ all done ───┼─── start ──▶│ step ┃ step │
│ (run parallel) │ │ may reuse a stage-1
└──────────────┘ │ │ session via sessionRef
│ └──────────────┘Starting a workflow
workflow_start returns a runId immediately and runs in the background; poll
with workflow_status.
// workflow_start
{
"workflowId": "review-then-fix",
"stages": [
{
"label": "review",
"steps": [
{ "label": "review-auth", "adapter": "claude-code", "prompt": "Review auth.ts for bugs." },
{ "label": "review-db", "adapter": "claude-code", "prompt": "Review db.ts for bugs." }
]
},
{
"label": "fix",
"steps": [
// reuse the reviewer's session so the fixer sees its findings:
{ "label": "fix-auth", "sessionRef": "review-auth", "prompt": "Now fix what you found." }
]
}
]
}adapterspawns a new session for the step;sessionRefreuses the session a prior step (any earlier stage) spawned, by that step'slabel— how a later stage acts on an earlier stage's output.policydecides what happens if a step's session asks for input mid-stage:auto-allow(send a canned prompt),escalate(webhook + timeout), orfail.notifyUrlfires a webhook on completion or escalation.
workflow_status reports each stage's steps with their status and sessionId,
so later work can inspect what a stage produced (e.g. agent_output on that
sessionId).
Run a WORKFLOW.md file
The daemon also exposes workflow_run_file, which loads an AIP-15
WORKFLOW.md (plus optional entry.mjs) via the workflow loader and
runs it through the same engine as workflow_start. Poll the returned
runId with workflow_status.
| Parameter | Meaning |
|---|---|
path | Absolute or workspace-relative path to the WORKFLOW.md file. |
input | Optional invocation input, bound to $input in the compiled workflow. |
cwd / workspaceSlug | Passed to spawned sessions. |
cacheKey | Enables journal caching for the run. |
Which primitive?
| Use | Shape |
|---|---|
workflow_start | Stages of parallel steps with a barrier between stages. A flat sequential list is just single-step stages. |
run-swarm | A kernel-driven loop — a dispatcher picks who speaks each turn over a shared conversation substrate. |
Reach for a workflow when the work is a fixed DAG of stages (a straight sequence with joins is single-step stages); a swarm when turn-taking is dynamic and conversation-driven.
Resume cache
A workflow run can journal its steps so a re-run replays unchanged work
instead of re-spawning sessions. Pass a cacheKey on the run and mark the
idempotent steps cacheable: true:
{
"workflowId": "nightly-audit",
"cacheKey": "nightly-audit", // enables the journal for this run
"stages": [
{ "steps": [
{ "label": "scan", "adapter": "claude-code",
"prompt": "Summarize the changelog since the last tag.",
"cacheable": true } // idempotent → safe to replay
] }
]
}On the next workflow_start with the same cacheKey, any cacheable step
whose resolved inputs (prompt + adapter + sessionRef) are unchanged replays its
journaled output — no session spawn, no cost. The journal is file-backed under
~/.agentproto/workflow-cache/. Only mark steps cacheable when re-running them
would be wasteful rather than wrong; most agent steps have side effects and
should stay uncached.
Retry a failed run (AIP-58 §6)
workflow_retry({ runId }) starts a NEW run (its own runId + AIP-58 §4
workspace, retryOf pointing at the original) that replays every step the
original already completed — no re-spawn — and re-executes only from the
first step that never succeeded. Unlike the cacheKey resume cache above,
this needs no setup: every run journals its own steps internally, so
workflow_retry works even when the original never passed a cacheKey or
marked any step cacheable. Refused on a still-running or already-succeeded
run — only a failed/cancelled run is retryable. An optional input
override re-resolves every step against the new value; a step whose resolved
input changes as a result (directly, or transitively via an upstream step it
depends on) re-executes instead of replaying — the same resolved-input-hash
comparison the resume cache above uses, not a separate mechanism.
{ "runId": "wfrun_..." }
// → { "runId": "wfrun_...", "status": "running", "retryOf": "wfrun_..." }The engine underneath
workflow_start translates onto
@agentproto/workflow-runtime
— a typed step algebra (tool, agent, pipeline, map, branch, loop,
parallel, approval, suspend, subworkflow, …) walked by a single
interpreter. The MCP surface exposes the agent-session stage/barrier subset
plus resume-cache. When you embed the engine directly you also get:
pipeline— N items through K stages with no cross-item barrier (each item flows independently; wall-clock = slowest single chain).- structured output — validate an agent step's final message against a schema and re-prompt on mismatch.
- aggregate budget —
maxTotalCostUsdcaps summed session cost across the run; the next spawn past the cap failsbudget_exceeded.
See the package README for the embedding API and examples.