A TypeScript SDK for coding agent workflows

Atomic’s workflow SDK turns an engineering process into a versioned TypeScript module. Define typed inputs and outputs, run agent tasks with explicit context, preserve large handoffs as files, branch or fan out with ordinary code, compose child workflows, and request human input at the point of decision.

For the complete technical reference, read the Atomic workflow documentation. It covers the full API, execution model, composition contracts, structured outputs, run controls, and advanced implementation details.

What does the Atomic workflow SDK provide?

The SDK exports workflow() from @bastani/workflows. A definition declares its contract and implements an async run(ctx) function. The context exposes tracked primitives: ctx.task, ctx.stage, ctx.chain, ctx.parallel, ctx.workflow, and ctx.ui. Atomic executes and checkpoints those supported operations. Ordinary TypeScript controls branches, loops, and reduction logic.

Write a typed workflow

Project workflows live in .atomic/workflows/*.{ts,js,mjs,cjs}. The current authoring API uses workflow({...}). Older defineWorkflow() builder examples are not the current API. Declare and return every output that a caller may consume. The name result has no automatic behavior.

import { workflow } from "@bastani/workflows";
import { Type } from "typebox";

export default workflow({
  name: "summarize-pr",
  description: "Summarize a pull request in one task.",
  inputs: {
    pr_url: Type.String({ description: "Pull request URL." }),
  },
  outputs: {
    summary: Type.String({ description: "Pull request summary." }),
  },
  run: async (ctx) => {
    const summary = await ctx.task("summarize", {
      prompt: `Summarize ${String(ctx.inputs.pr_url)} clearly.`,
      context: "fresh",
    });
    return { summary: summary.text };
  },
});

Run /workflow reload after adding the file, inspect its contract with /workflow inputs summarize-pr, then launch it with /workflow summarize-pr pr_url="https://github.com/org/repo/pull/42". Named runs are background-oriented and return a run ID for status, connection, and resume controls.

Inputs, outputs, and handoffs

Inputs and outputs are TypeBox schema maps. Atomic validates supplied inputs before running the workflow body and validates returned outputs against the declared contract. Unknown, missing, mismatched, or undeclared values fail instead of silently changing the interface.

Small handoffs can use a task’s typed result. For larger research, logs, and review reports, write an artifact with output and pass its path through reads. File handoffs focus the next model context and preserve evidence for inspection after the session ends.

Fan out independent work, then synthesize

Use ctx.parallel for independent branches. Concurrency is explicit, and failFast: false lets remaining branches finish when one fails. The synthesis task runs after the fan-out barrier and reads the branch artifacts.

const reports = {
  api: ".atomic/workflows/runs/review/api.md",
  tests: ".atomic/workflows/runs/review/tests.md",
} as const;

await ctx.parallel([
  {
    name: "api-review",
    task: "Review the public API for compatibility risks.",
    output: reports.api,
    outputMode: "file-only",
  },
  {
    name: "test-review",
    task: "Review changed tests and missing behavior coverage.",
    output: reports.tests,
    outputMode: "file-only",
  },
], { concurrency: 2, failFast: false });

const synthesis = await ctx.task("synthesize", {
  prompt: "Synthesize blockers and cite the supplied reports.",
  reads: Object.values(reports),
  context: "fresh",
});

Parallelism is not automatically better. Use it when branches do not depend on each other and do not contend for the same edit surface. Encode dependencies serially or in a composed graph when tasks share files, migrations, or acceptance criteria.

Put human input in the runtime path

Human input is requested where it is needed, not declared as a static workflow flag. Use ctx.ui.input, confirm, select, editor, or custom<T>. A workflow can produce a plan artifact, pause for approval, and return a cancelled status without starting implementation.

const plan = await ctx.task("planner", {
  prompt: `Plan: ${String(ctx.inputs.task)}`,
  output: ".atomic/workflows/runs/review-and-merge/plan.md",
});

const approved = await ctx.ui.confirm(
  `Proceed with this plan?\n\n${plan.text}`,
);
if (!approved) return { status: "cancelled" };

const result = await ctx.task("implementer", {
  prompt: "Read the approved plan, implement it, and run its checks.",
  reads: [".atomic/workflows/runs/review-and-merge/plan.md"],
});

Compose proven workflows instead of copying them

Import a workflow definition and pass it to ctx.workflow(child, { inputs }). The parent receives only the child’s declared outputs. Import Atomic’s built-ins from @bastani/workflows/builtin. A custom parent can then reuse a research or reviewer-gated loop without duplicating its prompts and controls.

Composition is bounded by the configured maximum depth. Map parent inputs to child inputs deliberately, and treat the child output contract as the stable integration surface.

SDK boundaries

Continue into the technical documentation

This page provides an overview for evaluating the SDK, not the full reference. The complete Atomic workflow documentation is the current source of truth for implementation details. Use the built-in workflow catalog to compare shipped control and review shapes, the workflow design explainer to define stages and handoffs, and the verification guide to design checks, evidence, and gates.

Read the complete technical documentation Inspect the SDK source Install Atomic