File Structure
A SirenSpec workflow is a YAML file with a top-level mapping. Three fields are required; the rest are optional.version
Required. The schema version string. Currently only "0.1" is supported.
agents
Required. A mapping of agent IDs to agent definitions. Each agent wraps an LLM with a system prompt.
See Agents for full agent documentation and Providers for valid model URIs.
nodes
Required. A mapping of node IDs to node definitions. Six node types are supported:
Agent node (default) — binds an LLM agent to an output path:
Agent node fields
Human node fields
Ahuman node pauses execution to collect a response from a human operator. The node consumes no LLM tokens. The rendered prompt is shown via the configured input source (stdin by default), and the response is written to the workflow context just like an agent’s output. Downstream when: conditions can gate on the response.
Factory node fields
A factory node supports three mutually exclusive execution modes: Mode 1: Agent + for_each — one agent call per item in a runtime list (native list, JSON array, or fenced```json).
```json).
Loop Variables
Insideinputs: templates, agent prompts, and swrm agent/synthesis prompts (when using swrm mode), three special variables are available:
Each key under
inputs: is additionally available in the spawned agent’s system prompt as {{ inputs.<key> }} once resolved. For example, inputs: { task: "{{ item }}" } makes {{ inputs.task }} reference the current item inside the agent’s prompt.
Swrm Within Factory
Theswrm field in factory nodes accepts:
Agent prompts and synthesis prompts support all standard interpolation namespaces plus the three loop variables (
{{ item }}, {{ index }}, {{ total }}).
See the following pages for detailed field reference and examples:
- Tool Nodes — HTTP and Python adapters
- Swrm — Parallel agents and synthesis
- Workflow Nodes — Sub-workflow composition
Context paths
Thewrites field uses dot-notation to specify where output is written in the workflow context:
Examples:
output.reply— final response available in the JSON trace.working.intent— intermediate classification readable by the next node.working.triage.intent— nested intermediate state.
edges
Optional. A list of directed edges connecting nodes. If omitted, all nodes are treated as roots and execute in definition order.
when expressions
The when field enables conditional branching. The expression is evaluated after the source node writes its output to the context.
Available names in when expressions:
No imports are available and
__builtins__ is otherwise cleared, so arbitrary code execution is not possible — only the names above are in scope.
when expression raises an error (missing key, syntax error, type mismatch), it is treated as false and the edge is not traversed. Edges without when are always traversed.
input
Optional. A static input message for the first node. Can be overridden at runtime with the --input CLI flag.
If neither
input.message nor --input is provided, the CLI exits with an error.
state
Optional. Initial state to seed the workflow context before execution begins.
working and output context buckets at startup. Nodes can read and overwrite these values during execution.
env_file
Optional. Path to a .env file (relative to the workflow file) loaded into os.environ before execution. This lets provider clients and {{ env.* }} templates pick up credentials without exporting them manually.
load_workflow() — before the Workflow object is returned — so provider clients that read os.environ at construction time see the values regardless of call order. Existing environment variables are never overwritten: if a key in the .env file is already present in os.environ as an empty string, an EnvFileShadowWarning is emitted (the existing empty value wins, which is usually a misconfiguration). A missing env_file raises FileNotFoundError.
defaults
Optional. Workflow-wide defaults for retry and failure handling, inherited by all nodes that do not specify their own.
guardrails
Optional. A list of guardrail names (or guardrail specs with configuration) applied globally to all agents. Defaults to ["injection"] if omitted.
Simple guardrails (no configuration)
Guardrails with configuration
Some guardrails require configuration. Use thename and config fields:
An empty list (
[]) disables all guardrails for the entire workflow. Individual agents can override this with their own guardrails field.
See Guardrails for full details.
budget
Optional. Workflow-level cumulative budget for token, USD, and wall-clock spend. The executor checks the running totals after every node and enforces the ceilings declared here. At least one of max_tokens, max_cost_usd, or max_duration_s must be set — an empty budget: block is rejected at validation time.
Budget status in the trace
When abudget: block is configured, the trace summary includes a budget block:
Per-node max_tokens_per_call
Agent nodes also support a per-call ceiling:
max_tokens_per_call is forwarded to the provider as the max_tokens API parameter so the LLM truncates its own response. Combined with the workflow budget: block, this gives you two layers of protection: each individual call is bounded and the cumulative spend is bounded.