Core concepts

Workflows & topologies

Wire agents, tools, and policies together with a pipeline, supervisor, or parallel topology.

A workflow wires agents, tools, and policies together behind one typed run method. You declare the pieces and a topology — pipeline, supervisor, or parallel — and the runtime handles execution order, Zod validation at both boundaries, tracing, approvals, and run-state persistence.

workflow({...}) returns a frozen definition. Input is validated against inputSchema before anything runs (a mismatch throws an AppError with status 422), and the final output is validated against outputSchema if you set one. Everything in between is recorded, so every run can be traced, replayed, and evaluated.

Defining a workflow#

src/workflow.ts
import { workflow } from "@assemble-dev/sdk";
import { z } from "zod";

export const pipeline = workflow({
  name: "draft-then-edit",
  inputSchema: z.object({ brief: z.string().min(1) }),
  outputSchema: z.string(),
  agents: [drafter, editor],
  topology: { kind: "pipeline" },
});

const result = await pipeline.run({ brief: "Launch post for the new API" });

if (result.status === "completed") {
  console.log(result.output);
}

run returns a discriminated result: status: "completed" with typed output, or status: "awaiting_approval" with an approvalId when a tool or policy pauses the run. Both carry the full RunState and ordered events — see runtime for run options like sinks, runId, and persistRunState, and approvals for resuming paused runs.

Options#

OptionTypeDescription
namestringWorkflow identifier, used in traces, run state, and the dashboard. Required.
inputSchemaz.ZodType<TInput>Zod schema run input is validated against. Required.
outputSchemaz.ZodType<TOutput>Optional schema for the final output. A mismatch fails the run with a 422.
agentsWorkflowAgentDefinition[]The agents available to the topology. Spread a team with [...team.agents]. Required.
toolsWorkflowToolDefinition[]Tools registered for this workflow. Calls to unregistered tools fail with a 404.
agentToolBindingsWorkflowAgentToolBinding[]{ agentName, toolName, buildToolInput } entries that run a tool after the named agent's step.
topologyWorkflowTopology{ kind: "pipeline" }, { kind: "supervisor", ... }, or { kind: "parallel", ... }. Defaults to pipeline.
policiesToolPolicy[]Policies evaluated on every tool call. See policies.
memory{ provider: MemoryProvider }Memory provider for thread memory and artifacts. Defaults to in-memory. See memory.
stopCondition({ state, stepIndex }) => booleanChecked after each step in pipeline and supervisor runs; returning true ends the run early with the current output.
continueOnAgentFailurebooleanWhen true, a failed agent step passes the previous output through instead of failing the run. Defaults to false.

Each agent's output is saved as a run artifact keyed step-<index>:<agentName>, so later steps, route functions, and evals can inspect intermediate results.

Pipeline#

The default topology. Agents run sequentially in array order; each agent receives the previous agent's output, and the last output becomes the workflow output. Use it whenever the order is known ahead of time — draft then edit, extract then summarize.

ts
export const pipeline = workflow({
  name: "draft-then-edit",
  inputSchema: z.object({ brief: z.string().min(1) }),
  outputSchema: z.string(),
  agents: [drafter, editor],
  topology: { kind: "pipeline" },
});

After each step, tool bindings for that agent run (and may pause the run for approval), then stopCondition is checked. Because topology defaults to { kind: "pipeline" }, you can omit it entirely.

Supervisor#

A route function picks the next agent at each step, so the path through the agents is decided at runtime. It receives { state, stepIndex, lastAgentName, lastOutput } and returns one of:

  • an agent name as a string,
  • { agentName, reason } — the reason lands in the trace as a routing decision,
  • finishWorkflow to stop and return the current output.
ts
import { finishWorkflow, workflow } from "@assemble-dev/sdk";
import { z } from "zod";

export const supervised = workflow({
  name: "supervised-support",
  inputSchema: z.object({ ticket: z.string().min(1) }),
  outputSchema: z.object({ reply: z.string() }),
  agents: [classifier, refundSpecialist],
  topology: {
    kind: "supervisor",
    supervisorName: "support-router",
    route: ({ stepIndex }) => {
      if (stepIndex === 0) {
        return "classifier";
      }

      if (stepIndex === 1) {
        return { agentName: "refund-specialist", reason: "refund category" };
      }

      return finishWorkflow;
    },
  },
});

The topology accepts two guard rails beyond supervisorName and route: maxSteps (defaults to 10) fails the run with a 409 if the loop exceeds it, and an optional stopWhen predicate over the same route context ends the run before the next route call. Routing to a name that is not in agents fails the run with a 400 that lists the valid names, and a route function that throws fails the run with a 500. Every decision is recorded in state.routingDecisions.

Parallel#

Fans agents out concurrently: every agent receives the workflow input, and their outputs are merged into one object keyed by agent name, which becomes the workflow output and is saved as the parallel:merged artifact. This is the shape behind a review panel.

ts
export const panel = workflow({
  name: "review-panel",
  inputSchema: z.object({ content: z.string().min(1) }),
  agents: [...reviewPanel.agents],
  topology: { kind: "parallel" },
});

The topology accepts agentNames to fan out a subset of the registered agents, and mergedArtifactKey to override the artifact key. Each branch gets its own memory thread (<runId>:<agentName>), so concurrent agents never interleave conversation state. If any branch fails, the run fails with an error naming every failed agent.

Warning: Tool bindings that require approval are not supported inside parallel fan-out — the run fails with a 409. Run approval-gated agents in a pipeline or supervisor topology instead.

Choosing a topology#

PipelineSupervisorParallel
ExecutionSequential, fixed orderSequential, chosen per step by routeConcurrent fan-out
Each agent seesPrevious agent's outputPrevious step's outputThe workflow input
OutputLast agent's outputOutput at finishWorkflowObject keyed by agent name
Ends whenAgent list is exhaustedfinishWorkflow, stopWhen, or maxStepsAll branches settle
Best forKnown linear flowsDynamic routing, triage, loopsIndependent perspectives on one input

Start with a pipeline — it is the simplest to reason about, trace, and eval. Reach for a supervisor when the next step genuinely depends on a previous output, and for parallel when agents are independent and you want all of their answers. Team presets scaffold each shape from a team in one call, and examples contains a complete supervisor workflow and a parallel review panel.