Skip to main content

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 placeholder gotcha

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:

  1. The require sits outside the agent — deterministic judgments are expressed as language structure
  2. expect Answer makes result.reply a compile-time-checked field access
  3. tools none requests an empty tool set — a capability declaration, not decoration

Command cheatsheet

CommandPurpose
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