waxell.yaml Reference
The waxell.yaml file is the declarative specification for a Waxell agent. It defines what an agent is, what it can do, and what governance constraints it operates under. The runtime reads this file at deploy time (wax push) and uses it to instantiate the agent, register tools, wire workflows, and apply governance.
If you don't already have the wax CLI: pip install waxell (or pipx install waxell for an isolated install). See CLI reference for Windows PATH issues and troubleshooting.
Minimal example
version: 1
agents:
- name: my_agent
version: 1.0.0
description: A simple agent.
system_prompt: |
You are a helpful assistant.
tools: [my_tool]
workflows: [my_workflow]
tools:
- name: my_tool
description: Does something useful.
inputs:
query:
type: string
outputs:
result:
type: string
workflows:
- name: my_workflow
type: graph
nodes: [step_one]
edges: []
Full example
version: 1
defaults:
framework: autogen
owner: team@example.com
tags: [production]
agents:
- name: doc_intake
version: 1.0.3
description: Classifies an inbound document and files it against a deal.
tags: [intake, documents]
model: anthropic:claude-sonnet-4-6
timeout_seconds: 120
system_prompt: |
You classify inbound documents. Read the document, decide which type it
is, extract the key fields, and file it.
# or reference the prompt registry instead of inlining:
# system_prompt_ref:
# name: doc_intake_system
# label: production
# What the agent can reach.
domains: [document, deal]
tools: [calculator]
workflows: [escalation]
capabilities: [waxell.hooks]
# Governance. Bare names, or objects with conditions.
policies:
- block_pii_leakage
- name: require_human_review
conditions:
- output_contains_dollar_amount_above(50000)
# Inbound events that start a run.
signals:
- name: document_received
source_type: api
description: A document landed and needs classifying.
schema:
document_id: string
filename: string
idempotency_key: $.document_id
memory:
scratchpad:
tier: working
scope: [run_id]
ttl: 1h
# Libraries this agent needs that the platform image doesn't carry.
# Declaring these builds it a dedicated container. See Execution Tiers.
dependencies:
apt: [antiword]
pip: [pypdf>=4.0, python-docx>=1.1]
python: "3.11"
execution:
mode: isolated # shared (default) | isolated
tier: ephemeral # ephemeral (default) | warm
telemetry:
alerts:
- p95_latency_ms > 5000
- error_rate > 0.05
sample_rate: 1.0
tools:
- name: calculator
version: 1.0.1
description: Basic arithmetic.
tags: [math, utility]
inputs:
a:
type: number
b:
type: number
op:
type: string
enum: [add, sub, mul, div]
outputs:
result:
type: number
workflows:
- name: escalation
version: 1.0.1
type: graph
description: Human-in-the-loop escalation.
nodes: [triage, draft_response, human_review, send]
edges:
- {from: triage, to: draft_response}
- {from: draft_response, to: human_review}
- {from: human_review, to: send}
Top-level fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
version | integer | Yes | - | Spec format version. Currently 1. |
defaults | object | No | {} | Project-wide defaults inherited by all agents/tools in this file. |
agents | list[Agent] | Yes | - | List of agent definitions. |
tools | list[Tool] | No | [] | Tool definitions referenced by agents. |
workflows | list[Workflow] | No | [] | Workflow definitions referenced by agents. |
defaults
| Field | Type | Default | Description |
|---|---|---|---|
framework | string | - | Default agent framework (e.g., autogen). |
owner | string | - | Default owner email. |
tags | list[string] | [] | Default tags applied to all agents. |
agents
Each entry in the agents list defines one agent. An agent is the top-level unit of deployment.
Core fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Unique agent identifier. Used in URLs, CLI, and traces. Must be a valid Python identifier (letters, digits, underscores). |
version | string | No | - | Semantic version string (e.g., 1.0.3). |
description | string | No | "" | Human-readable description. Shown in the controlplane UI. |
tags | list[string] | No | [] | Classification tags. Merged with defaults.tags. |
model | string | No | - | Default LLM model in provider:model format (e.g., openai:gpt-4o-mini). Overridden by llm_config task routes. |
timeout_seconds | integer | No | - | Maximum wall-time for a single agent run. |
Prompt configuration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
system_prompt | string | No | - | Inline system prompt. Mutually exclusive with system_prompt_ref in practice; if both are set, system_prompt_ref takes precedence at runtime. |
system_prompt_ref | object | No | - | Reference to a prompt in the prompt registry. |
system_prompt_ref.name | string | Yes | - | Prompt name in the registry. |
system_prompt_ref.label | string | No | "production" | Version label. Allows promoting prompts without redeploying. |
Bindings
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
tools | list[string] | No | [] | Tool names this agent can use. Must match entries in the top-level tools section or be registered via wax push-tool. |
workflows | list[string] | No | [] | Workflow names this agent can execute. Must match entries in the top-level workflows section or be registered via wax push-workflow. |
domains | list[string] | No | [] | External domain names (e.g., crm, email). Resolved against the tenant's registered domains at runtime. |
capabilities | list[string] | No | [] | Named capabilities this agent exposes. Each maps to a tool, workflow, or domain action. |
Relationships
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
signals | list[Signal] | No | [] | Inbound event definitions. See signals section. |
schedule | string or list | No | - | Cron schedule(s) that fire this agent, e.g. "0 9 * * *" (five-field, UTC). Expanded form takes cron, optional signal, payload, and slot. See Operating Agents. |
memory | map[string, Memory] | No | {} | Named memory slots. See memory section. |
policies | list | No | [] | Governance policy references. Can be bare strings (policy names) or objects with name and conditions. |
telemetry | object | No | - | Telemetry and alerting configuration. See telemetry section. |
Execution
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
dependencies | object | No | - | Libraries and system packages this agent needs. Declaring any triggers a per-agent container build. See dependencies section. |
execution | object | No | - | Where this agent runs. See execution section and Execution Tiers. |
tools
Each entry in the top-level tools list defines a tool the agent can call.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Unique tool identifier. Referenced by agents in their tools list. |
version | string | No | - | Semantic version string. |
description | string | No | "" | What the tool does. Shown to the LLM as tool documentation. |
tags | list[string] | No | [] | Classification tags. |
async_only | boolean | No | false | If true, tool must be called asynchronously. |
inputs | map | No | {} | Input parameter definitions. See tool inputs. |
outputs | map | No | {} | Output field definitions. |
schema | object | No | - | Unified JSON Schema for inputs/outputs. Alternative to separate inputs/outputs. |
Tool inputs
Each key in inputs is a parameter name. The value is an object:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type | string | No | "string" | JSON Schema type: string, number, integer, boolean, array, object. |
description | string | No | - | Parameter description shown to the LLM. |
enum | list | No | - | Allowed values. |
required | boolean | No | false | Whether the parameter is required. |
default | any | No | - | Default value if not provided. |
items | object | No | - | For array type: schema of array elements. |
format | string | No | - | JSON Schema format hint (e.g., email, uri). |
pattern | string | No | - | Regex pattern for validation. |
minLength | integer | No | - | Minimum string length. |
maxLength | integer | No | - | Maximum string length. |
minimum | number | No | - | Minimum numeric value. |
maximum | number | No | - | Maximum numeric value. |
Example:
tools:
- name: search
description: Search a knowledge base.
inputs:
query:
type: string
description: Search query
required: true
limit:
type: integer
description: Max results
default: 10
minimum: 1
maximum: 100
outputs:
results:
type: array
total_count:
type: integer
workflows
Each entry defines a workflow the agent can execute.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Unique workflow identifier. |
version | string | No | - | Semantic version string. |
type | string | No | - | Workflow type. Common values: graph, linear, router. |
description | string | No | "" | What the workflow does. |
nodes | list[string] | No | [] | Node names in the workflow graph. |
edges | list[Edge] | No | [] | Edges connecting nodes. |
steps | list[Step] | No | [] | Alternative to nodes/edges for linear workflows. |
routes | list[Route] | No | [] | Routing rules for decision-based workflows. |
metadata | object | No | {} | Arbitrary key-value metadata. |
inputs_schema | object | No | {} | JSON Schema for workflow inputs. |
Edge format
edges:
- {from: node_a, to: node_b}
- {from: node_b, to: node_c, condition: "result.status == 'ok'"}
Step format (linear workflows)
steps:
- id: step_0
type: tool # tool | workflow | decision
name: my_tool
inputs:
query: "$.signal.query"
produces: sym_0
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique step identifier. |
type | string | Yes | Step type: tool, workflow, or decision. |
name | string | Yes | Name of the tool/workflow/decision to invoke. |
inputs | object | No | Input mappings. Can reference prior step outputs via $. syntax. |
produces | string | No | Symbol name for this step's output. Referenced by later steps. |
signals
Signals define inbound events that trigger agent execution.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Signal identifier. Used in webhook URLs: /api/v1/signals/<name>/. |
source_type | string | No | "webhook" | Source type: webhook, queue, schedule, api, internal. |
description | string | No | - | Human-readable description. |
schema | object | No | {} | Expected payload shape. Keys are field names, values are types. |
triggers_workflow | string | No | - | Workflow to run when this signal fires. |
idempotency_key | string | No | - | JSONPath expression for deduplication (e.g., $.proposal_id). |
ttl_seconds | integer | No | - | Time-to-live for the signal in the queue. |
priority | integer | No | 0 | Processing priority (higher = sooner). |
tags | list[string] | No | [] | Classification tags. |
Example:
signals:
- name: document_uploaded
source_type: webhook
description: Triggered when a user uploads a document
schema:
document_id: string
filename: string
size_bytes: number
triggers_workflow: process_document
idempotency_key: $.document_id
ttl_seconds: 3600
Limits
Every agent runs under caps. Set them explicitly — the defaults are generous, and an agent that loops will spend to them.
| Field | Type | Default | Effect |
|---|---|---|---|
max_budget_usd | number | 10.0 | Hard USD ceiling; the run stops when it's reached |
max_turns | integer | 25 | Maximum agent turns before the loop is cut |
timeout_seconds | integer | — | Wall-time cap applied by the dispatcher |
max_duration_s | integer | container timeout | Hard wall-time for the run |
agents:
- name: doc_intake
max_budget_usd: 2.00
max_turns: 15
timeout_seconds: 120
Per-child caps are set at the call site — ctx.spawn(..., budget_usd=2.00, timeout_seconds=300) — and debit from the parent's budget, so a runaway child
can't outspend its parent.
Tenant-wide ceilings (concurrent spawns, rate limits, kill switch) are configured in the Governance panel and apply across every agent.
memory
Named memory slots scoped to different dimensions and persistence tiers.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
tier | string | No | "episodic" | Memory tier: working, session, episodic, semantic. |
scope | string or list[string] | No | "tenant_id" | Isolation boundary. Single dimension or list of dimensions. tenant_id is always enforced automatically. |
type | string | No | "dict" | Data structure: dict, list, value. |
description | string | No | "" | Human-readable description. |
ttl | string | No | - | Time-to-live: "1h", "30d", "7d", etc. |
max_items | integer | No | - | For list type: max items before oldest are evicted. |
custom_scope_key | string | No | - | When scope is "custom", the input field to use as the key. |
searchable | boolean | No | true (semantic) | Semantic tier: enable vector embedding and retrieval. |
embedding_model | string | No | - | Semantic tier: override default embedding model. |
schema | string | No | - | Fully-qualified Python class for typed validation (e.g., myapp.schemas.NoteSchema). |
schema_version | integer | No | 1 | Schema version for migration support. |
Memory tiers
| Tier | Backend | Lifecycle | Use case |
|---|---|---|---|
working | Redis | Per-run, cleared on completion | Scratchpad for in-flight tool results. |
session | Postgres | 24h default TTL | Conversation/plan state within a user session. |
episodic | Postgres | TTL-based | Cross-run state: conversation history, cached results. |
semantic | pgvector | Indefinite | Long-term facts with semantic search. |
Scope dimensions
| Dimension | Source | Description |
|---|---|---|
tenant_id | Always | Outermost boundary (automatic). |
agent | Agent name | Per-agent within tenant. |
agent_version | Agent version | Per-agent version. |
user_id | Sub-user identity | Per end-user. |
user_group | Sub-user identity | Per user group. |
user_email | Sub-user identity | Per user email. |
session_id | Conversation session | Per conversation session. |
workflow | Workflow name | Per workflow. |
run_id | Execution run | Per workflow execution (ephemeral). |
channel_id | Slack/chat | Per chat channel. |
thread_ts | Slack | Per thread. |
Example:
memory:
# Working memory — auto-captures tool results
scratchpad:
tier: working
scope: [run_id]
ttl: 1h
# Episodic — per-user conversation history
conversation:
tier: episodic
scope: user_id
type: list
ttl: 30d
max_items: 100
# Semantic — long-term facts with vector search
client_knowledge:
tier: semantic
scope: [user_id, portfolio_id]
searchable: true
description: Facts about this client relationship
llm_config
Per-agent LLM routing configuration. Controls which models handle which task types.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
default_model | string | No | - | Default model when no task-specific route matches. Format: provider:model. |
task_routes | map[string, TaskRoute] | No | {} | Per-task routing rules. |
inherit_defaults | boolean | No | true | Whether unmatched tasks fall through to tenant/global defaults. |
custom_task_types | list[string] | No | [] | Additional task type names beyond the built-in set. |
TaskRoute
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
models | list[string] | Yes | - | Ordered model list: primary + fallbacks. |
temperature | number | No | - | Temperature override for this task. |
max_tokens | integer | No | - | Token limit override for this task. |
Example:
llm_config:
default_model: openai:gpt-4o-mini
task_routes:
email:
models: [openai:gpt-4o-mini, openai:gpt-4o]
temperature: 0.7
max_tokens: 2000
classify:
models: [openai:gpt-4o-mini]
temperature: 0.1
research:
models: [anthropic:claude-sonnet-4-5-20250929]
temperature: 0.3
max_tokens: 4000
inherit_defaults: true
mcps
MCP (Model Context Protocol) server bindings. Each binding declares that the agent uses a registered MCP server.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
server | string | Yes | - | MCP server slug. Must be registered in the tenant's MCPServer registry. |
tool_allowlist | list[string] | No | [] | Only expose these tools from the server. Empty = all tools. |
tool_blocklist | list[string] | No | [] | Hide these tools from the agent. |
tool_descriptions | map[string, string] | No | {} | Agent-specific tool descriptions (override the server's defaults). |
enabled | boolean | No | true | Whether this binding is active. |
transport | string | No | "http" | Transport type: http or stdio. |
command | string | Conditional | - | Required for stdio transport. Command to launch the MCP server process. |
args | list[string] | No | [] | Arguments for the stdio command. |
env | map[string, string] | No | {} | Environment variables for the stdio process. |
sandbox | object | No | defaults | Sandbox configuration for stdio processes. |
Sandbox (stdio only)
| Field | Type | Default | Description |
|---|---|---|---|
fs_allow | list[string] | [] | Read-only filesystem paths the process can access. |
net | boolean | false | Allow network egress. |
cpu_limit | number | 0.5 | CPU-seconds/sec soft cap. |
mem_limit_mb | integer | 256 | RSS memory cap in MB. |
max_duration_seconds | integer | 300 | Hard wall-time for the session. |
Example:
mcps:
# HTTP transport — server registered in tenant MCPServer registry
- server: github
tool_allowlist: [search_repos, get_file, list_issues]
# stdio transport — launches a local process
- server: filesystem
transport: stdio
command: npx
args: [-y, "@anthropic/mcp-filesystem"]
env:
HOME: /tmp/sandbox
sandbox:
fs_allow: [/data/documents]
net: false
mem_limit_mb: 128
max_duration_seconds: 60
prompts
Prompt registry bindings. Each binding links the agent to a versioned prompt.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Prompt name in the registry. |
version_label | string | No | "production" | Version label pin. Allows promoting prompts without redeploying. |
order | integer | No | 0 | Injection order when multiple prompts are bound. |
enabled | boolean | No | true | Whether this binding is active. |
Example:
prompts:
- name: sales_playbook
version_label: production
- name: compliance_rules
version_label: production
order: 1
metacog
Per-agent metacognition configuration. Enables conditional planning (triage), multi-step planning, and reflexion (learning from outcomes).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
triage | boolean | No | true | Run a triage step before each dispatch to classify complexity. |
plan | boolean | No | true | Generate a multi-step plan on hard turns. |
reflect | boolean | No | true | Run reflexion at workflow exit to generate learnings. |
inject_learnings | integer | No | 5 | Top-N learnings from prior runs injected into prompts. Set to 0 to disable. |
reflect_on_trivial | boolean | No | false | Reflect on trivial turns too (not just hard turns or failures). |
triage_prompt | string | No | built-in | Override the triage prompt template. |
plan_prompt | string | No | built-in | Override the plan prompt template. |
reflect_prompt | string | No | built-in | Override the reflect prompt template. |
triage_max_tokens | integer | No | 200 | Token cap for triage calls. |
plan_max_tokens | integer | No | 800 | Token cap for plan calls. |
reflect_max_tokens | integer | No | 300 | Token cap for reflect calls. |
Example:
metacog:
triage: true
plan: true
reflect: true
inject_learnings: 5
reflect_on_trivial: false
triage_max_tokens: 200
telemetry
Agent-level telemetry and alerting configuration.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
alerts | list[string] | No | [] | Alert rules as condition expressions. |
sample_rate | number | No | 1.0 | Trace sampling rate (0.0 to 1.0). |
Example:
telemetry:
alerts:
- p95_latency_ms > 5000
- error_rate > 0.05
- cost_per_run > 0.50
sample_rate: 1.0
policies
Names of policies from your tenant's policy registry that should apply to this agent:
policies:
- block_pii_leakage
- require_human_review
This is a reference, not a definition — the policy itself lives in the policy engine, where it carries its category, rules, scope, and action. Declaring the name here associates the agent with it and surfaces it on the agent's Governance panel.
To create the policies themselves, see
Adding Governance for the code-first path (policy() +
wax policies push) and
Policy Categories for all 49 categories.
Policies scoped to an agent in the engine apply whether or not the agent names them here — scope is set on the policy. That's deliberate: an operator's rail shouldn't be something an agent author can drop by editing their manifest.
dependencies
Libraries and system packages your agent needs that the platform image doesn't
carry. Declaring any of these makes the agent self-contained: on wax push
Waxell builds it a container layered on the governed runtime base, and routes
its runs there.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
pip | list[string] | No | [] | Python packages, pip syntax. Version specifiers supported (pypdf>=4.0). |
apt | list[string] | No | [] | Debian system packages (e.g. antiword, poppler-utils). |
python | string | No | "3.11" | Python version for the built image. |
bundle | string | No | - | Name of a pre-approved dependency bundle. Uses a pre-built image instead of building — faster, no build step. |
agents:
- name: doc_intake
dependencies:
apt: [antiword] # legacy .doc extraction
pip: [pypdf>=4.0, python-docx>=1.1, olefile]
python: "3.11"
Notes:
- Rebuilds happen automatically when your declared dependencies change. Unchanged dependencies reuse the existing image, so a re-push is cheap.
- A failed build doesn't break the agent. It continues on the shared tier until a build succeeds; the error is on the agent's Build panel.
- Declaring
dependencieswithoutexecution.modestill builds the image — setmode: isolatedto route runs to it.
execution
Where this agent runs. See Execution Tiers for how to choose, cold-start behaviour, and billing multipliers.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string | No | shared | shared — pooled workers. isolated — this agent's own container (requires dependencies). |
tier | string | No | ephemeral | ephemeral — a fresh task per run, no idle cost, ~30–60s cold start. warm — a standing slot, no cold start. |
agents:
- name: doc_intake
execution:
mode: isolated # shared (default) | isolated
tier: ephemeral # ephemeral (default) | warm
Billing follows the tier: shared 1.0×, ephemeral 1.5× (covers cold start), reserved capacity 0.7× plus a monthly fee. The multiplier applied to each run is shown on your usage lines.
See also
- Runtime Overview -- how the runtime executes agents
- Execution Context -- the
ctxobject available in tools and workflows - Workflow Envelope -- checkpoint/resume behavior
- Backends -- pluggable runtime backends