Run bunx smithers-orchestrator init first to scaffold a .smithers/ folder with ready-to-use workflows. This guide builds a custom workflow from scratch.
Step 1: Create the Workflow
workflow.tsx:
/** @jsxImportSource smithers-orchestrator */
import { createSmithers, Sequence, Task } from "smithers-orchestrator";
import { ToolLoopAgent as Agent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import ResearchPrompt from "./prompts/research.mdx";
import ReportPrompt from "./prompts/report.mdx";
const { Workflow, smithers, outputs } = createSmithers({
research: z.object({
summary: z.string(),
keyPoints: z.array(z.string()),
}),
report: z.object({
title: z.string(),
body: z.string(),
}),
});
const researcher = new Agent({
model: anthropic("claude-sonnet-4-20250514"),
instructions: "You are an expert research assistant.",
});
const writer = new Agent({
model: anthropic("claude-sonnet-4-20250514"),
instructions: "You are a concise technical writer.",
});
export default smithers((ctx) => (
<Workflow name="research-report">
<Sequence>
<Task id="research" output={outputs.research} agent={researcher}>
<ResearchPrompt topic={ctx.input.topic} />
</Task>
<Task id="report" output={outputs.report} agent={writer} deps={{ research: outputs.research }}>
{(deps) => (
<ReportPrompt
summary={deps.research.summary}
keyPoints={deps.research.keyPoints}
/>
)}
</Task>
</Sequence>
</Workflow>
));
deps={{ research: outputs.research }} makes report wait for research. The child render function receives deps.research with the inferred schema type.
Step 2: Run It
main.ts:
import { runWorkflow } from "smithers-orchestrator";
import workflow from "./workflow";
const result = await runWorkflow(workflow, {
input: { topic: "The history of the Zig programming language" },
});
console.log(result.status);
console.log(JSON.stringify(result.output, null, 2));
What Happened
- Smithers rendered the JSX tree. Only
research was mounted.
research ran, validated output against Zod, and persisted it.
- Re-render made the output available to
report via deps.
report mounted on the second render with typed deps.research.
Next Steps