Overview
This page covers the control-flow and reliability features of theagent-workflow/v1 DSL, plus several complete, runnable example workflows. If
you’re new to the DSL, start with Creating Workflows.
Branching
if — conditional tasks
Attach if: to run a task only when a condition is true. When the condition is
false, the task and everything downstream of it cascade-skip:
inputs.score >= 60, tasks.check.output.exitCode == 0, or a plain boolean
input.
else_of — the else branch
else_of: runs a task only when the referenced task was condition-skipped. It’s
the “else” side of an if:
run_when: always — join and cleanup
A task with needs normally skips if any dependency skipped, and the run stops
if a required dependency fails. run_when: always overrides both — the task runs
as long as its dependencies have finished, whether they succeeded, skipped, or
failed. Use it for joins and cleanup:
Loops
foreach — fan out over a list
foreach: runs the task once per item. Inside, {{ item }} is the current
element and {{ index }} is its position. Downstream tasks receive the collected
array of outputs:
foreach also accepts an inline array, and it composes with the other executors
(a foreach of tool: calls, of subworkflows, and so on). An empty list
produces an empty result and downstream tasks still run.
loop — repeat until a condition holds
loop: repeats a task. until: is checked after each round against output
(that round’s result); {{ round }} is the round number and {{ last }} is the
previous round’s output. Exhausting maxRounds without satisfying until fails
the task.
until to loop a fixed number of times:
Reliability
retries— retry a failed task. Either a count (retries: 2) or an object (retries: { maxAttempts: 3, delayMs: 500 }).required: false— a failure of this task does not abort the run; downstream tasks continue.timeoutMs— kill a shell task that runs too long.
Output contracts — output_schema
Attach an output_schema (a JSON Schema object) to a prompt or agent task to
enforce the shape of the reply. You do not need to describe the JSON format in
the prompt text — the schema is enforced by the model provider and injected as an
instruction, and the reply is parsed (with repair) into an object you can index
into downstream. If the output doesn’t conform, the task retries; if it never
conforms, the task fails.
Per-task model overrides
prompt and agent tasks accept optional provider: and model: to switch the
LLM for that task only. This is a run-scoped override — your saved model
configuration is untouched. Use a cheap model for bulk steps and a stronger one
where it matters:
provider is a provider command (e.g. enconvo_ai, open_ai, ollama) and
model is a model id for that provider. Both are only valid on prompt/agent
tasks.Subworkflows
Compose workflows withuses: workflow, either by workflowId: (a saved
workflow) or an inline workflow: block. The task’s output is the child run
result — reference the child’s task outputs via output.outputs.<taskId>:
Templates reference
Templates support Nunjucks filters (
| trim, | int, | dump, | default(...),
{% for %}, etc.), which is how you reshape data between tasks.
Complete examples
Research → summarize → report
Search the web, summarize the findings, and assemble a Markdown report. Note the sequential-by-default chaining — noneeds wiring required:
Conditional routing
Approve or reject based on a score, then always write a final report:Loop until converged
Refine a draft repeatedly until a self-check approves it, capping the rounds:Related
Creating workflows
The editor, the editing assistant, and each executor.
Introduction
The task model and how workflows are run.