Typed Workflow DSL — first-class authoring path
Plan reference: improvement plan #10 (handoff doc, May 2026 benchmark).
Goal: make the workflow surface a first-class authoring path alongside agents — a YAML author can validate, run, and observe a typed step-graph in under a minute, end-to-end.
Where we are
The engine already has every building block the handoff acceptance criteria asks for:
| Acceptance criterion | Existing surface |
|---|---|
| YAML authoring | src/workflows/yaml.ts — parseYamlWorkflow + flow: sugar for linear sequences |
| Conditional branching | branch and rule node types |
| Structured-output extraction | extract.structured built-in invoked via action.builtin node |
| Retries | per-node retry: { maxAttempts, backoffMs } on every node (Zod-validated) |
| Step-by-step traces | recordTraceStart / recordTraceEnd in dispatcher → GET /traces[/:taskId] |
The handoff still calls this "the biggest single item left." It is — but as ergonomics, not engine work. Today an operator has to:
- Hand-write
.agentx/workflows/<id>.jsonor learn the YAML schema by reading types.ts - Restart the daemon (or wait for
fs.watchto re-index) before they can validate - Trigger a run via
POST /workflows/:id/run(CLIrun <id>only works on stored ids — no ad-hoc file) - Pull traces via raw HTTP — no CLI helper
Every step is a small papercut. None of them block expert users; together they keep workflows from feeling first-class.
What "first-class" looks like
Concrete success: an operator types four commands end-to-end and the workflow runs with visible traces:
agentx workflow init lead-capture # scaffolds .agentx/workflows/lead-capture.yaml
$EDITOR .agentx/workflows/lead-capture.yaml # author
agentx workflow validate # already exists, unchanged
agentx workflow run lead-capture --watch # runs + tails tracesThat's the bar. Everything below either already exists, or fills a gap on the path from blank file → first run.
Plan
Stage 1 — Authoring scaffolds (CLI)
1a. agentx workflow init <id> [--template <name>] [--yaml]
Scaffolds .agentx/workflows/<id>.yaml (or .json) from a template, validates it, and adds it to the store.
Templates ship in the bundle (src/workflows/templates/<name>.yaml):
linear(default) — atrigger.manual→agent→endchain. Smallest workflow that actually runs.branching— adds abranchnode off the agent's structured output (e.g. classifier → routes).extract— usesaction.builtinwithextract.structuredto pull JSON fields from an upstream message, then routes on them.human-in-the-loop—userTaskform →signal.wait→agentfollow-up.retry— anagentnode with aretry: { maxAttempts: 3, backoffMs: 1000 }policy and a fallbackbranchon the failure path.
Each template is a working YAML file with comments explaining each section.
1b. agentx workflow add <file>
Copies a YAML/JSON file into .agentx/workflows/<id>.<ext>, runs the existing validate + lint pipe, and (if the daemon is up) fires POST /reload so the new workflow is live without a restart.
Why this and not just-copy-the-file: the reload trigger + the validate are the foot-guns operators hit today. Bundling them eliminates the silent "I edited the file but the daemon hasn't picked it up" failure mode.
1c. agentx workflow run <id-or-file> [--watch]
Today: run <id> only fires by stored id. Extension:
- If the argument resolves to a real file (
.yaml/.json), parse + validate + register-then-run as a one-shot. The synthesized id is_adhoc-<filename>-<timestamp>so it doesn't shadow stored workflows. --watchstreams the run's trace rows (the existingGET /traces?workflowRunId=<id>filter, polled every 500ms) and prints each step as it completes — node id, status, tokens, error if any.
1d. agentx workflow trace <runId-or-taskId>
Pretty-printer for GET /traces/:taskId — table per step with node | status | model | tokens (in/out/cache) | duration | error. Same data the dashboard's run viewer shows, but available in a terminal for CI / SSH sessions.
Stage 2 — Worked example
Ship examples/workflows/lead-capture.yaml in the repo, referenced from the docs:
trigger.channel(telegram source, project filter)agentnode (classifier — extractsintent)branchonintent === "newsletter"vs"sales"vsdefaultaction.builtinwithextract.structuredto pull{ name, email }from the agent replyaction.runwith a registeredhubspot-create-contactaction (per the actions reference page)- per-node
retry: { maxAttempts: 2 }on the HTTP-touching nodes endwithstatus: completed
This single file demonstrates branching, extraction, retries, action invocation, and trace-friendly node ids. It doubles as the docs' running example.
Stage 3 — Documentation refresh
Three doc edits:
- New page
docs/journey/13-typed-workflow.md— walkthrough that mirrors the four-command flow at the top of this plan, using the lead-capture example. - Update
docs/reference/workflows.md— add an "Authoring" section showing the new CLI surface and link to the journey. - Update
docs/index.md— promote workflows to a real feature pill ("Typed step-graphs in YAML — branching, extraction, retries, traces"), pointing at the journey.
Stage 4 — Tests
Unit + integration:
test/workflow-init.test.ts—agentx workflow initproduces files that round-trip validate clean for every shipped template.test/workflow-run-file.test.ts—runwith a YAML file path validates + dispatches;--watchpolls and prints; ad-hoc runs don't shadow stored workflows.test/workflow-trace-cli.test.ts—trace <id>formats the existing payload correctly and 404s gracefully on a missing id.- Extend
test/workflows.test.tswith a YAML round-trip case using all five templates.
Non-goals (deferred)
- Loop construct. Not in handoff acceptance. Workflows can already loop via
subProcess+ signals; a dedicatedloopnode is its own design pass. - Visual editor parity for the new templates. The editor (
src/web/workflow-editor/) renders any valid workflow already; templates appear as ordinary nodes once added. A "Pick a template" button in the editor's New menu is a separate small UI task — flag in roadmap. workflow lint --fix. Schema errors should still surface; auto-fix is too much rope without a careful design.- Sub-process composition tutorials. Already supported (
subProcessnode); deserves its own journey, not part of this scope. - Replay (handoff #11). Independent feature.
Effort & ordering
| Stage | Effort | Files |
|---|---|---|
1a init + templates | M | src/commands/workflow.ts, 5 template files under src/workflows/templates/ |
1b add | S | src/commands/workflow.ts |
1c run <file> + --watch | M | src/commands/workflow.ts, small daemon route for ad-hoc registration |
1d trace | S | src/commands/workflow.ts |
| 2 example | S | examples/workflows/lead-capture.yaml |
| 3 docs | M | journey 13, workflows.md, index.md |
| 4 tests | M | three new test files |
Total: ~1–1.5 days for an operator-ready first-class workflow surface, end-to-end. The handoff estimate of "≥2 weeks" assumed building the engine from scratch — the engine is already done.
Acceptance (matches handoff)
A YAML workflow with conditional branching, structured-output extraction, and retries can be authored, validated, and run via agentx workflow run. Each step's input/output flows through /traces.
Concrete repro:
agentx workflow init demo --template branching
# edit .agentx/workflows/demo.yaml
agentx workflow validate
agentx workflow run demo --watch
# prints each step as it completes; the final line is a /traces/:id link
agentx workflow trace <runId>
# table view of all steps, tokens, model, durationIf those four commands work end-to-end on a fresh install, #10 is done.
