Skip to main content

Overview

This page covers the control-flow and reliability features of the agent-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:
Conditions are template expressions evaluated for truthiness — for example 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.
Omit 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 with uses: 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 — no needs 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:

Creating workflows

The editor, the editing assistant, and each executor.

Introduction

The task model and how workflows are run.