Quickstart
Ten minutes: install the toolchain, validate and run a first workflow, and read your first compiled artifact.
Prerequisites
- Node.js 20 or 22 (the native sandbox dependency does not yet support newer majors)
- pnpm ≥ 9 (the repo uses a pnpm workspace)
1. Build the toolchain
git clone <repo> dynamic-workflow && cd dynamic-workflow
pnpm install
pnpm build # tsc -b: builds all five packages/*
After the build, the CLI entry point is packages/cli/dist/main.js.
2. Validate a workflow
node packages/cli/dist/main.js check examples/simple.flow
# ✓ examples/delivery.flow checks clean
check runs three layers: lexing/parsing (Langium) → semantics
(scopes, resources, stage dependencies) → types (expression inference and
assignability). Any error-level diagnostic exits non-zero with line/column
positions.
Try a failing one: rename the parameter of workflow hello(input: Request)
to req, then check again:
error workflow-param-name 1:15 workflow parameter must be named 'input'
3. Run in the sandbox
node packages/cli/dist/main.js run examples/simple.flow \
--input '{"text": "hello agent flow"}'
run executes the compiled artifact in a sandbox (subprocess + isolate).
The CLI ships a schema-driven FakeAgentRuntime: instead of calling a real
model it fabricates output matching the expect JSON Schema — so the answer
you see is a placeholder like "generated". Which proves the point:
even with a fake model, contract validation, flow progression and event
streaming are all real.
The FakeAgentRuntime emits the constant "generated" for every string field.
For examples whose require asserts cross-item uniqueness (e.g.
unique(items[*].id)), run with a single-item input — otherwise the
assertion trips over duplicated placeholders.
4. Inspect the compiled output
node packages/cli/dist/main.js compile examples/simple.flow --js
node packages/cli/dist/main.js compile examples/simple.flow --ir
--js prints the restricted JavaScript glue; --ir prints the Flow IR
(JSON). Add --out <path> to write to a file. The generated JS performs no
direct I/O — every runtime capability flows through the $runtime ABI.
5. Write your own first workflow
Create mine.flow:
workflow first(input: Ask) -> Answer {
use agent "my-assistant" as helper
type Ask { question: text }
type Answer { reply: text }
pipeline {
stage reply -> Answer {
let result = agent(helper) {
task """
Answer the question in one short paragraph.
"""
input { question: input.question }
tools none
expect Answer
timeout 2m
}
require result.reply != ""
else fail "reply must not be empty"
return result
}
return reply
}
}
node packages/cli/dist/main.js check mine.flow
node packages/cli/dist/main.js run mine.flow --input '{"question": "why dsl"}'
Three things to notice:
- The
requiresits outside the agent — deterministic judgments are expressed as language structure expect Answermakesresult.replya compile-time-checked field accesstools nonerequests an empty tool set — a capability declaration, not decoration
Command cheatsheet
| Command | Purpose |
|---|---|
flow check <file> | syntax + semantics + type diagnostics |
flow compile <file> [--js|--ir] [--out p] | compile to restricted JS or Flow IR |
flow run <file> [--input '<json>'|--input-file p] | sandbox execution, FakeAgentRuntime-driven |
Full details in the CLI reference.
Hooking up a real model
The CLI's FakeAgentRuntime is for demos. A real runtime implements the
AgentRuntime interface (resolveAgent / resolveTeam / invocation) and is
injected by the host. That single interface is the entire coupling between
language and model — see Packages & Architecture.
Next steps
- Top-level structure & pipelines
- Examples: 24 ready-to-check/run flows