Skip to the content.

Agent definition

An agent is one YAML file, agents/<id>.agent.yaml, in the project. This page is the field reference. For what the settings do while an agent runs, see Agent runtime; for the overview, see Agents.

Unknown keys are rejected (agent.unknown_field), so a typo is an error rather than a setting that silently does nothing.

Minimal file

id: support
name: Support
instructions: |
  Answer questions about our product. Say so when you do not know.

Everything else has a default. Saving through the console, API, CLI or MCP writes the canonical form: fixed key order, defaults left out, so a diff between two revisions shows only what someone set.

Fields

Identity

Field Type Default Rules
id string made from name ^[a-z][a-z0-9_]{0,63}$. Must match the file name. Fixed after creation (agent.id_immutable). sessions, approvals and new are reserved because they are part of the /api/agents/ URLs.
name string required 1–120 characters.
description string none Up to 500 characters.
greeting string none Up to 500 characters. Shown on the public web chat page.
enabled boolean false A new agent is always created disabled; enabling is its own revision.
instructions string required The system prompt.
project_context boolean false Adds the project's AGENTS.md (or, if absent, CLAUDE.md) to the system prompt, first 20,000 characters.

If you create an agent without an id, one is made from the name (lowercase, other characters become _, at most 56 characters, a number added if taken; agent when the name has no usable letters).

model

Field Type Default Rules
model.backend pi | claude pi Only pi runs. claude saves, warns (agent.backend_unavailable) and fails when a session starts.
model.id string project default A model id from a connected provider. An id no provider lists only warns (agent.model_unknown).
model.thinking off | low | medium | high medium How hard the model thinks, for models that support it.

execution is trusted-local (default) or sandbox. sandbox needs a sandbox runner that is not installed yet: it warns (agent.sandbox_unavailable) and sessions fail.

tools

Field Type Default Notes
tools.builtin list of read, write, shell [] Whole groups: read = read, grep, find, ls; write = write, edit; shell = bash.
tools.builtin_tools list of read, grep, find, ls, write, edit, bash [] Single tools on top of the groups.
tools.mcp map: server name → list of tool names {} Names match ^[A-Za-z0-9_.-]{1,64}$. Each list needs at least one tool; "*" allows every tool of the server. stageflow is reserved.
tools.skills list of skill names [] Skills must be installed, or a session fails to start.
tools.browser boolean false Stored but not wired to anything yet.
tools.stageflow.groups list of group ids [] See Stageflow tools.
tools.stageflow.tools list of tool names [] Single Stageflow tools on top of the groups.

The console shows one checkbox per tool and stores the compact form: a full group as the group, a partial one as single tools.

Operator-only Stageflow tools (gates.decide and get_started) cannot be granted: agent.stageflow_operator_only.

approvals

Field Type Default Rules
approvals.ask_first list of tool ids [] Each must be a tool the agent is granted (agent.ask_first_not_granted). bash is always ask-first when granted, listed or not.
approvals.approvers.operators boolean true People deciding in the console, CLI or MCP.
approvals.approvers.channel_ids list of strings [] Reserved. Nothing decides from a chat channel yet; ids listed here are ignored and save with the warning agent.channel_approvers_unused.
approvals.timeout_minutes integer 30 1–10,080.
approvals.on_timeout skip | deny skip skip lets the agent continue without the action; deny ends the turn.

At least one approver must be allowed, so operators: false with no channel_ids is an error (approvals.approvers). Because channel_ids is reserved, operators: false leaves nobody able to decide: do not use it.

Tool ids in ask_first: a builtin name (edit, bash), stageflow.<tool>, <server>.<tool> for an MCP tool, or agent:<id> for a sub-agent. How asking works is in Agent runtime.

sub_agents

Field Type Default Rules
sub_agents list of agent ids [] No duplicates; an agent cannot list itself (agent.sub_agent_self). An id with no agent warns (agent.sub_agent_unknown).
sub_agent_depth integer 1 1–5. How deep nested calls may go.

guardrails, evals, observability

Field Type Default Rules
guardrails.block_patterns list of regular expressions [] Each must compile. Matched against the finished reply (JavaScript RegExp, no flags).
evals.groundedness boolean false Scores replies that used tools. See Observability.
observability.capture off | metadata | full metadata See Observability.
observability.otlp boolean false Push each finished turn to an OTLP collector.

channel

channel:
  connection_id: support-inbox
  allow_from: [jane@example.com, "@acme.com", "+14155550123"]
Field Type Default Rules
channel.connection_id string none The connection this agent answers on. One agent per connection; two connections that reach the same inbox or line also count as one (agent.channel_in_use). An unknown id warns (agent.connection_unknown).
channel.allow_from list of strings [] Who may message the agent. No duplicates. Empty lets nobody in and warns (agent.channel_nobody_allowed); * lets anyone in and warns (agent.channel_open_to_anyone).

An allow_from entry is one of:

Entry Matches Used by
jane@example.com That address (case-insensitive; Jane <Jane@X.com> is read as jane@x.com) AgentMail
@example.com Everyone at that domain AgentMail
+14155550123 That phone number (E.164; spaces, dashes and brackets in the sender are ignored) iMessage
U02ABC1234 That Slack user id (starts with U or W; 8 or more capitals and digits in all) Slack
@bob_smith That Telegram username (5–32 letters, digits, _; written with the @, case-insensitive) Telegram
* Anyone any

A message from a sender who matches nothing is ignored. The earlier channels: [{ connection_id }] list still loads as the one channel; more than one is an error.

memories

Field Type Default Rules
memories list of ids none Each id is [a-z][a-z0-9_-]{0,63} and must already exist under $STAGEFLOW_HOME/memories/. Duplicates are rejected. An empty list is omitted when the file is written.

Every session of this agent can call read_memory and replace_memory for the cover, and add_memory_card, update_memory_card, read_memory_card, and search_memory_cards for cards on those ids. The prompt lists the memory ids and descriptions. It does not include the cover or any card body, and the session never receives a file path. add_memory_card refuses an existing key. update_memory_card refuses a missing key. read_memory_card returns the current body. search_memory_cards is a case-insensitive word scan of the current keys and bodies, at most 20 hits, snippet only. A session cannot delete a card and cannot read the previous copy. A missing, unreadable, or mismatched id is agent.memory_unavailable: create, save, and enable all fail. Enable checks the data directory again even when the agent file has not changed. Sub-agents do not inherit the list.

Create the memory in the console (Memories) before attaching it. The operator can edit the cover there, and can delete a card (the current card and the previous copy; that cannot be undone). A session adds cards. See YAML catalog — Memories.

web_chat

web_chat:
  enabled: true
  slug: k7x9m2qp4h8d3w6n
  rate_limit_per_minute: 10
  max_message_chars: 4000
Field Type Default Rules
web_chat.enabled boolean false in a hand-written file A new agent created through the console, API, CLI or MCP starts with it on.
web_chat.slug string made on save 16–32 lowercase letters or digits. The link is the credential. An enabled block with no slug gets a random one when saved through the console, API, CLI or MCP; a hand-written file shows the warning agent.web_chat_slug_missing until then. Saving with slug: "" rotates the link.
web_chat.rate_limit_per_minute integer 10 1–120 messages per visitor per minute.
web_chat.max_message_chars integer 4000 1–20,000.

A client that leaves web_chat out of a save (CLI, MCP, scripts) keeps the agent's current link and limits. The public page and its API are in Agents.

schedule

schedule:
  cron: "0 9 * * 1-5"
  timezone: Europe/London
  message: Post the morning summary.
Field Type Default Rules
schedule.cron string required A valid cron expression.
schedule.timezone string Host's local zone An IANA zone name.
schedule.message string Scheduled check-in. What the scheduled turn says.

See Schedules for when it fires.

limits

Field Type Default Rules
limits.session_minutes integer 10 1–1,440. A session older than this refuses a new turn.
limits.max_turns integer 40 1–1,000.
limits.cost_usd number or null null Above 0. A session that has spent this much refuses a new turn.
limits.long_context summarize | truncate summarize How a replayed transcript is cut down.
limits.always_on boolean false Skips session_minutes and max_turns; cost_usd then counts per day.

Limits are checked when a turn starts, not during it. See Agent runtime.

Validation: issues and warnings

Validating, creating or saving returns two lists.

Issue codes:

Code Meaning
agent.required A required field is missing.
agent.unknown_field A key the schema does not have.
agent.invalid_value, agent.invalid_type, agent.too_small, agent.too_big, agent.invalid_format, … A value of the wrong type, range or shape. The message says what is expected.
agent.yaml_syntax, agent.invalid_shape The file is not valid YAML, or is not a mapping.
agent.id_mismatch The id differs from the file name.
agent.id_immutable A save tried to change the id.
agent.ask_first_not_granted An ask_first tool the agent does not hold.
agent.stageflow_group_unknown, agent.stageflow_tool_unknown Not a Stageflow group or tool.
agent.stageflow_operator_only A group or tool only a person may use.
agent.mcp_name_reserved tools.mcp.stageflow.
agent.sub_agent_self The agent lists itself.
agent.channel_in_use The connection (or the inbox behind it) already answers for another agent.
agent.memory_unavailable A memories id is missing, unreadable, or does not match its file.

Warning codes:

Code Meaning
agent.shell_not_path_confined bash is granted; see Agent runtime.
agent.backend_unavailable, agent.sandbox_unavailable claude backend or sandbox execution: sessions fail.
agent.model_unknown No connected provider lists the model.
agent.model_native_tools The provider runs its own file and shell tools beside the agent's, so grants and approvals cannot hold; sessions fail unless the Host opts in.
agent.mcp_server_unknown The server is in neither the project .mcp.json nor the servers saved from the console.
agent.skill_unknown The skill is not installed.
agent.sub_agent_unknown No agent has that id.
agent.connection_unknown No connection has that id.
agent.channel_nobody_allowed, agent.channel_open_to_anyone An empty or * allow_from.
agent.channel_approvers_unused approvals.approvers.channel_ids is reserved and ignored.
agent.web_chat_slug_missing Web chat is on with no link yet.
agent.web_chat_reads_conversations Web chat is on and the agent can read every agent's sessions, including visitors' messages.

Files, revisions and sync

Definitions stay in git-tracked YAML. The Host keeps a copy of every version in the run store database under $STAGEFLOW_HOME.