Pipelines
Pipelines wire multiple agents together for complex workflows. Define them
as YAML files in your local workspace (~/.muaz/plugins/local/pipelines/).
From the REPL:
/run <name> [input] run a pipeline/pipelines list available pipelinesFrom the shell (scriptable, no REPL):
muaz pipeline run code-review "the staged changes"echo "the last 3 commits" | muaz pipeline run code-reviewmuaz pipeline listA pipeline is a single ordered list of steps. Each step runs an agent
(or a parallel: group), builds its input from a template over shared variables,
and can gate itself with when:, retry on failure, cap its budget, and expose
structured output as variables for later steps. There is no pattern field —
sequential, parallel, and router flows are all expressed with steps.
The examples below name agents like researcher and writer to show the shape;
they are agents you would author, not ones muaz ships. The one shipped pipeline
is code-review.
Variables and input templates
Section titled “Variables and input templates”A step’s input: is a template. These placeholders are substituted (an unknown
placeholder is a hard error, so typos surface immediately):
| Placeholder | Value |
|---|---|
${input} | the pipeline’s initial input |
${vars.<name>} | a top-level output field promoted from a step’s output_schema |
${steps.<name>.output} | a named step’s full text output |
${steps.<name>.<field>} | a structured field from that step’s output_schema |
Sequential
Section titled “Sequential”Chain steps by feeding one step’s output into the next. Reference steps by their
name:
name: research-writedescription: "Research a topic, then write from the sourced notes"steps: - name: research agent: researcher # resolved across namespaces; use @plugin/name to disambiguate input: "Research this topic and produce structured notes:\n\n${input}" retry: { max: 1, backoff_secs: 2 } - name: write agent: writer input: | Write the piece requested below, working only from the research notes.
Request: ${input}
Research notes: ${steps.research.output}Parallel
Section titled “Parallel”A step may be a parallel: group whose members run concurrently. Later steps
read each member’s output by name.
steps: - name: reviews parallel: - name: security agent: security-reviewer input: "${input}" - name: style agent: style-reviewer input: "${input}" - name: summary agent: editor input: | Security review: ${steps.security.output}
Style review: ${steps.style.output}Router (division of labour)
Section titled “Router (division of labour)”A router is just a step with an output_schema containing a route field; the
downstream specialists gate on it with when:. No free-text parsing — the route
is a typed, structured-output field. Bind the router to a cheap model and the
specialists to a strong one.
name: triagesteps: - name: route agent: triage-router output_schema: type: object properties: route: { type: string, enum: [write, analyze, general] } message: { type: string, description: The request, forwarded to the specialist. } required: [route, message] additionalProperties: false budget: { max_tokens: 6000 }
- name: write agent: writer when: { value: "${steps.route.route}", equals: write } input: "${steps.route.message}"
- name: analyze agent: analyst when: { value: "${steps.route.route}", equals: analyze } input: "${steps.route.message}"Step controls
Section titled “Step controls”when:— run the step only if a condition holds. The form is{ value, equals | not_equals | contains }(no loops, no free-form expressions):when: { value: "${steps.route.route}", equals: write }.output_schema:— a JSON schema; the step returns structured output, and top-level fields become${steps.<name>.<field>}(and, at the pipeline’s top level,${vars.<field>}).retry:—{ max, backoff_secs }retries a failing step.on_error:—fail(default, abort the pipeline),continue(skip and move on), orfallback(use the step’s fallback path).budget:—{ max_cost_usd, max_tokens, max_seconds }, allowed per step and at the pipeline top level. Token/cost limits are checked at step boundaries; amax_secondslimit hard-times-out an in-flight step.
Step agents
Section titled “Step agents”Every step names an agent, and that agent’s identity is what its model binding
in models.yaml is keyed on. This is the reason a step cannot carry an inline
prompt instead: an inline prompt has no name, so there would be nothing to bind,
and running two steps on deliberately different models is most of what pipelines
are for.
That leaves agents built purely to be pipeline steps. Mark them
entrypoint: false and they stay out of the pickers that start a new chat:
name: review-disputerdescription: "Code review step: attacks the proposed findings"entrypoint: falsesystem_prompt: "./review-disputer.md"A step agent is not a restricted agent. It resolves by name, appears in
muaz agents list, on the UI Agents page (tagged step), and in the pipeline
editor’s picker; it takes its own binding; and you can open a chat with it
directly when you want to iterate on its prompt. The flag governs exactly one
surface — the new-chat picker.
Namespaces and the shipped pipeline
Section titled “Namespaces and the shipped pipeline”Pipelines resolve across namespaces (local → plugins → built-ins) just like
agents, so a plugin’s pipeline is referenced by its qualified @plugin/name.
The one shipped built-in is code-review: an adversarial review where
review-proposer finds defects, review-disputer attacks them, and
review-arbiter decides which survive. Bind the proposer and disputer to
different models — the dispute step is there to catch findings that are merely
plausible, and two runs of the same model share the blind spot that produced
them. See the pipelines guide.