Skip to main content
A package is one agent.yaml plus the prompts, tool files and knowledge folders it points at. It answers three questions: who answers the call, what each agent may do, and what each of those things actually is. One rule explains the whole file:
Every list on an agent has a matching top-level block with the same name. The list holds names. The block holds the definitions.
On this page:

Quickstart

Two agents. front_desk answers, and can hand the caller to complaint_specialist:
agent.yaml
front_desk lists the name to_complaints. The top-level handoffs: block holds the definition. That is the rule, and every other list works the same way. Tasks are the one exception: a task is defined right inside the agent that uses it first, under that agent’s own tasks: list. A second agent that runs the same task just names it.

The five things an agent can do

An agent is a person on the phone. It can do five kinds of things, and each kind has its own list: If you remember one thing, remember the third column. Tools, tasks and task groups come back. Handoffs and escalations do not.

Every key an agent takes

An agent has three required keys and five optional lists. There are no others.
file path
required
The Markdown file holding this agent’s prompt, relative to the package root.
model name
required
Which reasoning model this agent uses. Names one entry under models.think.
model name
required
Which voice this agent speaks in. Names one entry under models.speak.
list of names
Tools this agent may call. Each name is a file under tools/<name>.yaml.
list of tasks or names
Tasks this agent may run. Write the task in full to define it here, or write a bare name to run a task another agent already defined.
list of names
Task groups this agent may run. Each name is an entry under the top-level task_groups:.
list of names
Agents this one can hand the caller to. Each name is an entry under the top-level handoffs:.
list of names
People this agent can put the caller through to. Each name is an entry under the top-level escalations:.

Build one in four steps

Say you want two agents, and each one can run a task. Step 1. Say who answers.
agent.yaml
Step 2. Write the agents, with their tasks nested right inside. A task carries both what it is and when to run it, so there is one name and one place to read it. You have not defined the handoffs and escalations yet. That is fine, you will in the next step.
agent.yaml
Read front_desk top to bottom. You already know everything it can do, and which kind of thing each one is. That glance is the point of the five lists. Step 3. Define the handoffs and escalations. Each handoff may choose how much conversation travels with the caller. Omitted history means messages. Saved values reach the receiving model only through placeholders in its prompt.
agent.yaml
Step 4. Define the tools. Tools are the one place the rule bends: a tool is its own file under tools/<name>.yaml, so the top-level tools: is a plain list of names rather than a block of definitions:
agent.yaml
Done. Notice you never wrote verify_customer twice. It is defined once, inside front_desk, and complaint_specialist just names it. Sharing is naming the same thing.

How a value gets from one step to the next

Three keys do this, and they each do one thing. Each has its own page.
top-level block
Declares a value the call can hold, and what type it is. A variable is the box. Nothing fills it by itself.Variables
on a task
Fills a variable when the task finishes. - customer_phone: result.phone means “put the task’s phone answer into customer_phone”.Tasks
on a task or a handoff
Chooses how much of the conversation so far the next step can read. It moves the transcript, not your saved values.Context scope
Saved values are never added to a prompt on their own. A prompt reads a value only when it names it, as {{customer_phone}}.

Ordering work without a gate

In the example above, verify_customer fills customer_phone with assign:, and manage_booking’s own when: says it runs only once the caller is identified. That is how “verify before booking” works: the order lives in the prompt, not in a gate, so the caller hears nothing about it. Put that clause on the work that needs the value, not on the route to it. And never hold up the way to a person: somebody asking for a manager should not be interviewed first.

Tasks attach handoffs, and nothing else

A task has tools: and handoffs: and no other list. There is no tasks:, no task_groups: and no escalations: key on a task, so a task cannot run another task or task group, or reach a person directly. That is not a rule you have to remember. The key does not exist, so the file cannot be written.

What the compiler holds for you

  • Every name in an agent’s list must exist in the matching top-level block, or, for a task, be defined inline by some agent. A typo is a build error, not a silent gap.
  • A name listed under the wrong kind is refused, and the message names the list it belongs on.
  • All five kinds share one namespace, because every name becomes a callable function at runtime. Two things cannot share a name, and two agents defining a task under the same name is refused the same way.
  • A handoff, escalation, task group or tool that no agent reaches is also a build error. There is no dead config to forget about.
  • A task can list tools: and handoffs: only, enforced by the shape of the file.
Two of those errors, as you will actually see them:

Order, and what it does not mean

Nothing an agent lists runs in list order. The model picks what to use from the when: text. Two things in a package do run in a fixed order: the steps: of a task group, and the entries of prefetch:. Pre-fetch entries resolve top to bottom, and an entry that reads a value only a later entry assigns stops the build. Order is not free everywhere, though: the order of names in a list is the order the tools are declared to the model in the generated code, so reordering a list produces a different file. Treat that as presentation, not as control flow.

The order to write the file in

That order is a convention, not a schema rule, and it exists so a reader meets the agents first and the plumbing last. unmute init writes the shape without prefetch:, since the scaffold declares none. examples/salon-concierge shows where it lands once a package uses it: right after variables:. Every one of those keys, with its type, whether it is required, and what values it accepts, is in the agent.yaml key table.

Where to go next

Your first task

Add one task to the agent you just made, one key at a time.

Tasks

Nested tasks and typed results.

Handoffs

When to split into two agents.

Task groups

Tasks in a fixed order, sharing what they learn.

Human transfers

Cold and warm escalation to a person.