DromioWorkflow SDK
Workflow SDKAuthoringAPIWorkflow Apps & Harnesses

Design the graph

Decision treeStepsWorkflowsCompositionModels and judgmentHuman boundaries
Design the graph
Decision tree
Steps
Workflows
Composition
Models and judgment
Human boundaries

Workflows

A workflow combines a versioned document with executable catalog definitions. The document owns topology; TypeScript step definitions own runtime behavior.

Define a workflow​

import { workflow } from "@dromio/workflow";
import { z } from "zod";

// Excerpt: these step definitions are created with step() or step.model().
export const responseReview = workflow({
catalog: [generateResponse, saveReview],
document: new URL("./response-review.workflow.json", import.meta.url),
input: {prompt: z.string().min(1)},
output: {reviewPath: z.string().min(1)},
});

document accepts a WorkflowDocument object, a filesystem path, or a URL. The returned workflow exposes start(), hydrate(), graph(), configure(), its parsed document and catalog, contracts, child sources, and workspace.

workflow is callable and also owns bundled workflow builders:

  • workflow.judge() runs one assessor and one deterministic gate.
  • workflow.judgeUntil() adds bounded revision and repetition.
  • workflow.clarifyUntil() adds focused durable human questions and accepted-answer provenance.

Each builder returns an independently runnable workflow. Place it in a parent with step.workflow() only when the parent needs that child boundary.

Complete workflow document​

This is the minimum useful JSON shape:

{
"id": "response-review",
"label": "Review a response",
"description": "Generate and save one reviewed response.",
"version": 1,
"trigger": {
"id": "request",
"type": "manual",
"label": "Review request",
"input": {
"prompt": {"jsonSchema": {"type": "string", "minLength": 1}}
}
},
"nodes": [
{
"id": "generate",
"catalogItemId": "responses.generate",
"label": "Generate response"
},
{
"id": "save",
"catalogItemId": "responses.save-review",
"label": "Save review"
}
],
"edges": [
{"id": "request-to-generate", "source": "request", "target": "generate"},
{"id": "generate-to-save", "source": "generate", "target": "save"},
{"id": "save-to-result", "source": "save", "target": "result"}
],
"end": {
"id": "result",
"type": "result",
"label": "Review ready",
"output": {
"reviewPath": {"jsonSchema": {"type": "string", "minLength": 1}}
}
}
}

Every node catalogItemId must exist in the supplied catalog. Trigger and end contract keys must agree with the TypeScript input and output when both are declared. Every visible node must be reachable from the trigger and able to reach the end. Validation errors are reported during workflow() compilation.

end is the document field, type: "result" declares the boundary kind, and the boundary's id is user-defined ("result" here). They need not share a name.

Bind placement keys​

By default a step reads and writes its declared keys. Use node bindings when a placement needs different workflow-state names:

{
"id": "summarize-source",
"catalogItemId": "notes.summarize",
"bindings": {
"input": {"note": "sourceText"},
"output": {"summary": "sourceSummary"}
}
}

Input bindings map the step key to an existing state key. Output bindings map the step key to the new state key.

Loops and explicit control flow​

Documents may declare loop regions with id, start, end, and backTo. Steps return goto(stepId) for a visible non-linear transition. Keep ordinary dependencies as edges and use router, fork, and iteration primitives for typed child-workflow composition.

Child workflows​

Composite step definitions carry their child sources. You may also pass workflows explicitly when compilation needs nested documents:

// Excerpt: these workflows are compiled with workflow().
workflow({
catalog: [evaluateResponse, reviewResponse],
document,
workflows: [
evaluateResponse.workflow,
assessResponseWorkflow,
analyzeNoteWorkflow,
],
});

Register actual workflow objects rather than rebuilding factories or copying child documents into the parent.

Configuration​

Provide a typed config default when authoring, then compile another placement configuration with configure():

const configured = responseReview.configure({prefix: "Reviewed"});
const session = await configured.start({prompt: "Explain traces"});

configure() returns a compiled executable workflow but does not create a new authored definition, document, or catalog identity.

Run directly​

const session = await responseReview.start({prompt: "Explain traces"});

if (session.status === "completed") {
console.log(session.state.reviewPath);
}

See Execution and durability before deploying waits or external side effects.

On this page

  • Define a workflow
  • Workflow document
  • View document
  • Trigger and end contracts
  • Register child workflows
  • Configuration