Skip to main content
Workflows coordinate multiple agents into a sequential pipeline. Each step runs an agent, validates the output, and passes it to the next step via ctx.prev. Approval gates suspend the workflow until a human approves.

Defining workflows

Use the builder pattern: workflow() returns a builder, chain .step() calls, then .build() to finalize.
Each step’s ctx.prev is strongly typed based on preceding steps’ output schemas. No casts needed.

Step options

Input callbacks

The input callback receives a StepContext with access to the workflow input and all previous step outputs:
The callback can return a plain string or { message: string }:
If no input callback is provided, the agent receives a default message: "Execute step \"step-name\"".

Workflow options

Validation rules

  • Workflow names must match /^[a-z][a-z0-9-]*$/
  • Step names must match the same pattern
  • At least one step is required
  • Duplicate step names within a workflow throw an error
  • Both workflow and step definitions are deeply frozen after creation

Running workflows

Use runWorkflow() to execute a workflow. It runs each step sequentially, passing outputs forward via ctx.prev.

Result shape

How output accumulation works

Each step’s output is stored in ctx.prev keyed by step name. If a step has an output schema, the agent’s response is parsed as JSON and validated against it. Validated data is stored in prev. Steps without an output schema store { response: string }. If output validation fails (invalid JSON or schema mismatch), the workflow returns an error — it does not silently fall back.
ctx.prev is strongly typed — each step sees the output types of all preceding steps based on their output schemas. No casts needed.

Approval gates

An approval gate suspends the workflow until a human approves. When runWorkflow() hits an approval step, it returns immediately with status: 'pending'.

Approval config

Resuming after approval

When the workflow returns pending, store the step results. After the human approves, call runWorkflow() again with resumeAfter pointing to the approval step:
The resumeAfter step name must match an existing step in the workflow. An invalid name throws an error.

Building the approval UX

The approval primitive is transport-agnostic — runWorkflow() doesn’t dictate how approvals are delivered or collected. Common patterns:
  • HTTP endpoint — Store pending state in a database, expose POST /workflows/:id/approve, render an approval button in a dashboard
  • Webhook — Send the approval message to Slack/Discord, listen for a reaction or command
  • Durable Object — On Cloudflare, hold workflow state in a Durable Object that wakes when an approval event arrives
  • CLI prompt — For dev tooling, prompt in the terminal and resume immediately

Agent-to-agent invocation

Tools can invoke other agents using ctx.agents.invoke(). This enables delegation patterns where a coordinator agent dispatches work to specialized agents.

Invoke options

The invoked agent runs a full ReAct loop with the same LLM adapter as the calling agent. It returns { response: string }.

Session persistence

Agents support persistent sessions via an AgentStore. Pass a store to run() to enable conversation history across multiple calls.

Available stores

Session options

LLM adapters

Agents communicate with LLMs through adapters. Use createAdapter() to create one:

Available providers

Custom adapters

You can provide a custom LLMAdapter directly — any object with a chat() method:

Agent lifecycle

Agents have three lifecycle hooks:

Loop configuration

Control the ReAct loop behavior: