Workflows
Cross-agent pipelines with DAG dependencies, reviewer loops, and verify/repair
@klein-kit/orchestrate chains agents across providers: Claude plans, Codex implements, tests gate the result, an independent reviewer can send it back.
pnpm add @klein-kit/orchestrateimport { workflow, runWorkflow } from "@klein-kit/orchestrate";
const def = workflow("dark-mode", "Add a dark-mode toggle to settings")
.plan("plan", { agent: { provider: "claude" } }) // Claude plans
.implement("impl", { agent: { provider: "codex" }, // Codex implements
dependsOn: ["plan"] })
.verify("tests", "pnpm test", { retries: 2 }) // failing tests → repair agent → re-run
.review("review", "impl", { agent: "auto", retries: 1 }) // independent reviewer; REVISE loops back
.build(); // plain JSON — serialize it, ship it
const result = await runWorkflow(def, { onEvent: (e) => render(e) });
// { status: "completed" | "failed", taskState, results }Builder
workflow(name, goal): WorkflowBuilder
.session(options) // Partial<SessionOptions> shared by every step
.plan(id, input?) // default planning prompt
.implement(id, input?) // default implementation prompt
.review(id, target, input?) // reviews step `target` (auto-added to dependsOn)
.verify(id, command, input?) // runs a shell command as a gate
.step(id, role, input) // fully custom
.build(): WorkflowDefinition // validated, JSON-safeStepInput:
interface StepInput {
agent?: AgentSpec; // "auto" | { provider, model?, reasoningEffort?, session? }
prompt?: PromptTemplate;
dependsOn?: string[];
retries?: number; // default 1
}Prompts are templates with {{goal}}, {{taskState}}, {{output:<stepId>}}, and {{feedback}} placeholders. .plan/.implement/.review ship sensible default prompts; supplying your own overrides them.
build() validates: unique ids, review targets exist, verify steps have a command, and no dependency cycles. Serialize with JSON.stringify, load back with loadWorkflow(json).
Running
runWorkflow(def, {
onEvent?: (event: WorkflowEvent) => void,
autoProviders?: ProviderId[], // restrict "auto" selection
signal?: AbortSignal, // aborts interrupt + close live sessions
});- Ready steps run concurrently (it's a DAG, not a list).
- Each attempt is a fresh session with one prompt, closed afterward.
- A step whose dependency failed is skipped with
reason: "dependency failed". - Verify loop: command fails → a repair agent runs (configurable via
repairAgent, default"auto") → command re-runs, up toretries. - Review loop: the reviewer answers
VERDICT: APPROVEorVERDICT: REVISE; on revise, the target step re-runs with the reviewer's notes injected as{{feedback}}, then gets re-reviewed. - Conflict reconciliation: if unrelated steps touched the same paths, a
conflict.detectedevent fires and a synthetic__reconcile__step merges them.
Workflow events
workflow.started · workflow.completed · workflow.failed · step.started · step.completed · step.failed · step.skipped · review.verdict · verify.result · handoff · conflict.detected · agent (wraps every underlying AgentEvent with its stepId — full streaming fidelity inside workflows).
Auto selection
agent: "auto" picks from installed, authenticated providers with role-based preferences (planning prefers Claude, implementation prefers Codex, …). Reviewers avoid the provider that produced the target. Restrict the pool with autoProviders.
Subagents
One-shot delegation without a full workflow:
import { runSubagent } from "@klein-kit/orchestrate";
const { result, taskState } = await runSubagent({
provider: "codex",
task: "Write unit tests for src/utils/dates.ts",
parentTaskState: session.taskState, // optional context injection
onEvent: (e) => render(e),
});