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. Policy names from your tenant's policy registry.
policies:
- block_pii_leakage
- require_human_review
# 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
# 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"
# Your own code. `source` is copied into the container; `entrypoint`
# names what runs there (a LangGraph graph, a pydantic_ai Agent, a
# CrewAI Crew, or a callable). See the source section.
source: ./agents/doc_intake
entrypoint: graph:app
execution:
mode: isolated # shared | isolated
tier: ephemeral
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. Use letters, digits and underscores — the same name appears in URLs and signal names. |
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 | 300 | Maximum wall-time for a single agent run, in seconds. Capped at 3600. |
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 for native @agent Python agents. See memory section. |
policies | list[string] | No | [] | Names of policies from your tenant's policy registry. See policies section. |
Execution
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
source | string | No | - | File or directory of your agent's code, relative to waxell.yaml. Shown on the Source tab, scanned on push, and copied into the agent's container. See source section. |
entrypoint | string | No | - | module:attr inside source that the container executes — a compiled LangGraph graph, a pydantic_ai Agent, a CrewAI Crew, or a callable taking the signal payload. Setting it runs the agent in its own container. For framework: langgraph manifests without code, a bare name is the entry node instead. See source section. |
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. |
identity | object | No | - | Agent Execution Identity for this agent's runs. See identity section. |
Identity
agents:
- name: legacy_sync
identity:
axid: false # this agent's runs mint no AXID; its domain calls carry no X-Waxell-AXID
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
axid | bool | No | workspace default (on) | false opts this agent out of Agent Execution Identity: no identity is minted for its runs and its domain calls carry no X-Waxell-AXID header. Use it for agents whose downstream systems never verify the header. Unset follows the workspace setting. The sub-user identity headers (X-Waxell-Sub-User-*) are governed by the domain's per_user_auth, not by this field. |
Precedence: the agent's identity.axid → the workspace setting → the platform default (on).
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: graph, team, or linear. |
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. |
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
Limits
Every agent runs under caps. Set them explicitly — an agent that loops will spend to them.
| Field | Type | Default | Applies to | Effect |
|---|---|---|---|---|
timeout_seconds | integer | 300 | All agents | Wall-time cap per run, applied by the dispatcher. Capped at 3600. |
max_duration_s | integer | — | All agents | Alternate spelling of the wall-time cap, read when timeout_seconds is not set. |
max_budget_usd | number | 10.0 | claude_agent_sdk | Hard USD ceiling; the run stops when it's reached. |
max_turns | integer | 25 | claude_agent_sdk | Maximum agent turns before the loop is cut. |
Agents on other frameworks run a tool-calling loop of at most 8 iterations per run. To cap their spend in dollars, use a budget policy.
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. The
same block works for every framework: the runtime turns each slot into tools
the agent can call (memory__recall__<slot>, memory__remember__<slot>,
memory__search__<slot> on semantic slots; graph__recall__<slot>,
graph__remember_entity__<slot>, graph__remember_batch__<slot>, … on graph
slots) and the agent's prompt decides when to use them. Native @agent Python
code gets the same slots on ctx.memory. wax memory show <agent> prints what
the agent would recall on its next run; wax graph show <name> prints a graph.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
tier | string | No | "episodic" | Memory tier: working, session, episodic, semantic, graph. |
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_provider | string | No | workspace default, else local | Semantic tier: which provider embeds this slot — the instance_id of one of the workspace's LLM provider instances (any OpenAI-compatible kind), or local for the bundled open-source model. See Semantic memory embeddings. |
embedding_model | string | No | the instance's default | Semantic tier: the embedding model on that provider (text-embedding-3-large, nomic-embed-text, …). |
embedding_dimensions | integer | No | the model's native width | Semantic tier: output width, at most 1536. Honoured by OpenAI's text-embedding-3 family. |
graph_name | string | No | the slot name | Graph tier: which graph. A graph is (workspace, name); declare as many as you need, and several agents may name the same one. |
graph_access | string | No | "read_write" | Graph tier: read, write or read_write. Enforced when the tools are built (a read slot gets no remember_* tools) and again at the backend. |
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. |
graph | graph store | Indefinite, per node TTL optional | Entities and relationships shared across agents, with provenance (which agent, which run, acting for whom), visibility groups and an optional ontology. See Graph Memory. |
Semantic memory embeddings
A semantic slot turns each fact into a vector and finds it later by meaning. Which model produces the vector is a workspace decision, not a platform one:
| Setting | What embeds the slot |
|---|---|
| nothing | The workspace's provider instance that advertises embeddings (one is marked when you add OpenAI, Azure OpenAI, Ollama, vLLM, TEI, Together, Fireworks, Mistral … from the catalog). If none does, the bundled local model — BAAI/bge-small-en-v1.5, MIT licence, 384 dimensions, runs on the worker's CPU, nothing leaves Waxell. A new workspace has working semantic memory with no key anywhere. |
embedding_provider: <instance_id> | That instance, with its secret_ref for the key and its catalog default model. |
embedding_provider: local | The bundled model, even when a hosted instance exists. |
embedding_model: … | A different model on the chosen instance. |
memory:
style_rules:
tier: semantic
scope: tenant_id
embedding_provider: ollama_local # a provider instance you added
embedding_model: nomic-embed-text
Every fact records the provider and model that embedded it, and search only
ranks facts from the slot's current space, so changing the provider never
mixes vectors from two models. Facts written under the previous model stay
listed by memory__recall__<slot> and wax memory show, and are found by
search again once re-remembered. wax memory show <agent> prints which
embedder a semantic slot resolves to.
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 — per-run scratchpad
scratchpad:
tier: working
scope: [run_id]
# 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
Alerts
Alert rules (latency, error rate, cost per run) are configured in Settings → Alerts, not in the manifest. Every run is traced.
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.
source
source points at your agent's code — a file or a directory, relative to
waxell.yaml. On wax push every UTF-8 text file under it travels with the
agent (Python, Markdown prompts, YAML, JSON, …), so what the runtime builds
and shows is exactly what you have on disk.
agents:
- name: reviewer
framework: langgraph
source: ./agents/reviewer # directory or single file
entrypoint: graph:app # module:attr inside source
dependencies:
pip: [langgraph>=0.2, langchain-anthropic>=0.3]
What happens to the files:
- Source tab. The agent's page shows them as pushed.
- Security scan. The artifact scanner reads them on every push (hardcoded credentials, risky calls, dependency manifests). Findings are on the agent's Risk panel.
- Container. With
dependenciesorentrypoint, the files are copied to/app/agent/srcin the agent's image and byte-compiled during the build, so a syntax error fails the build with the file and line rather than the first run.
Rules:
- Everything text travels; binaries are skipped. Files that are not UTF-8 text are left out and listed in the push output.
- Ignored by default:
__pycache__,.git,.venv/venv,node_modules,.pytest_cache,*.pyc,.env*,.DS_Store,*.ipynb,*.log,*.db. .waxellignoreat thesourceroot adds your own rules, one glob per line (fixtures/,*.csv,notebooks/). Lines starting with#are comments.- Limits: 500 files, 1 MiB per file, 4 MiB in total. Over a limit the push fails and names the file; nothing is truncated.
- Credentials fail the push. A line that looks like a live key (Anthropic,
OpenAI, AWS, GitHub, Slack, Stripe, Google, a private key block, a Waxell
token) stops the push with the file and line, and the value is never shown.
Put secrets in
wax secrets setand read them from the environment. A test fixture can carry# waxell: allow-secreton the line.
entrypoint
entrypoint: module:attr names what the container runs, imported from
source. The runtime resolves the attribute and dispatches on what it is:
| It resolves to | The runtime calls | The run's result |
|---|---|---|
A compiled LangGraph graph (has invoke) | graph.invoke(payload) | the final state; the last message's content is the run's text |
A pydantic_ai Agent | agent.run_sync(prompt) with prompt from the payload's prompt/input/message key, else the payload as JSON | .output |
A CrewAI Crew | crew.kickoff(inputs=payload) | .raw |
| Any callable | fn(payload); a coroutine is awaited | the return value, JSON-projected |
payload is the signal body with the _-prefixed runtime keys removed.
How the code runs:
- In its own container, as a child process of the governed run with a
minimal environment: your source on
PYTHONPATH, the secrets your code reads viaos.environ/os.getenv(fromwax secrets set, matched by name), and the model endpoint below. Platform configuration is not visible to it. - Model calls go through Waxell.
ANTHROPIC_BASE_URLandANTHROPIC_API_KEYare set so theanthropicSDK — andlangchain-anthropicandpydantic-aithrough it — send requests to the run's governed proxy. Your code never holds the real key; every call is policy-checked and appears on the run page with tokens and cost. Use Anthropic models in code agents. - Governance: the run's
before_workflowpolicies evaluate before the process starts andafter_workflowpolicies evaluate on what it returned; a blocked result is withheld from the run.timeout_secondskills the process at the deadline and the run is markedtimeout. - While the image builds, a fired run waits (its event stream says so) and starts automatically when the build completes. If the build fails, the run fails with the build error.
- Your
print()output goes to the run's diagnostic log, not the result.
Without entrypoint, source is captured for the Source tab and the security
scan and the agent runs declaratively from this manifest (see
Framework Support).
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. 3.11 is the only published base; any other value is rejected at push. |
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 a declarative agent. It continues on the
shared tier until a build succeeds, and each such run carries an event saying
it ran there without the declared dependencies; the error is on the agent's
Build panel. An agent with an
entrypointhas no shared fallback: its runs wait for the build, and fail with the build error if the build fails. - Package names are checked at push.
aptentries must be Debian package names;pipentries must be single requirement lines (no-r, no--index-url). - Routing rule.
dependencieswith noexecution:block routes the agent's runs to its own container. If you write anexecution:block, it takesmode: sharedunless you setmode— so anexecution:block with onlytierkeeps the agent on shared workers. Setmode: isolatedexplicitly whenever you include anexecution:block.
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 or entrypoint). With dependencies or an entrypoint and no execution: block, the agent routes isolated. |
tier | string | No | ephemeral | ephemeral — a fresh task per run, no idle cost, ~30–60s cold start. |
agents:
- name: doc_intake
execution:
mode: isolated # shared | isolated
tier: ephemeral
Warm slots are selected by your tenant's sensitivity tier or reserved capacity, not per agent — see Execution Tiers.
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