> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ziro-agent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agents

> An agent is a folder of policy files with a persona, a model, and its own memory. Ziro ships nine of them.

Ziro can run as one of several named agents, each with its own persona, tool
policy, MCP servers, and guardrails. Plug-and-play is **per agent**: every agent
carries its own on/off flag, so agents are enabled and disabled independently.
There is no global single-vs-multi toggle.

## An agent is a folder

Each agent lives in `packages/ziro-core/src/ziro/agents/<agent_id>/` and is self-contained:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
packages/ziro-core/src/ziro/agents/researcher/
├── meta.yaml               # name, description, enabled, model, provider
├── agent_config.yaml       # the persona / soul prompt
├── tool_policy.yaml        # core_tools, max_active
├── permissions.yaml        # F06 tool permission gate
├── guardrails_policy.yaml  # input/output guards
├── compaction_policy.yaml  # context compaction
├── mcp_servers.yaml        # external MCP servers
├── shell_policy.yaml       # run_shell (off by default)
├── fs_policy.yaml          # read/write file tools
├── webfetch_policy.yaml    # web_fetch
├── websearch_policy.yaml   # web_search
├── memory_policy.yaml      # fact extraction + project memory
├── subagent_policy.yaml    # which children may be spawned
├── hooks.yaml              # lifecycle hooks
└── plugin_config.yaml      # the plugin policy gate (opt-in)
```

There are **no agent config files in the project root**. If an agent omits a
file, the loader degrades to the built-in defaults for that feature.

<Note>
  For the field-by-field schema of each file, see
  [Agent files](/reference/agent-files).
</Note>

### meta.yaml

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
name: Researcher
description: A factual research aide with lighter guardrails
enabled: true
model: null        # null → the provider's default model
provider: null     # null → the global default provider (openrouter)
```

## The shipped agents

<Tabs>
  <Tab title="chat flavour">
    | Agent        | Role                                                                                                                   |
    | ------------ | ---------------------------------------------------------------------------------------------------------------------- |
    | `default`    | The autonomous **Generalist**. `web_search` and `fs_write` are core tools. The one shipped agent with plugins enabled. |
    | `life_coach` | The Four-Blocks coaching persona.                                                                                      |
  </Tab>

  <Tab title="engineering flavour">
    | Agent             | Role                                                                                                   |
    | ----------------- | ------------------------------------------------------------------------------------------------------ |
    | `product_manager` | Read-only role: `permissions.yaml` denies `fs_write:*` and `shell:*`. The flavour default.             |
    | `architect`       | Read-only role, same denials.                                                                          |
    | `coder`           | Full autonomous build surface: shell plus writes, no per-call approval. Web tools are disabled for it. |
  </Tab>

  <Tab title="research flavour">
    | Agent               | Role                                                                                                          |
    | ------------------- | ------------------------------------------------------------------------------------------------------------- |
    | `researcher`        | Factual research aide, lighter guardrails, shell and web on, host mode. The flavour default.                  |
    | `researcher_docker` | Same persona with `run_shell` in Docker-sandbox mode. A documentation and example agent, not for routine use. |
    | `analyst`           | Quantitative, autonomous host shell. Web tools disabled.                                                      |
    | `writer`            | Synthesis. Shell denied, writes allowed.                                                                      |
  </Tab>
</Tabs>

<Warning>
  `coder`, `analyst`, and `researcher` run shell and write tools with
  `default_action: allow` and no per-call approval, so they work in headless and
  subagent flows. Autonomy is bought back with deterministic containment rather
  than a human gate: they do not hold the web tools, `fs_write` refuses the
  control plane (`.env`, `*_policy.yaml`, `permissions.yaml`, `hooks.yaml`,
  `.git`, `.github`, `.ziro`), and their shell policies add a per-agent deny
  regex list. See [Permissions](/guides/permissions).
</Warning>

## Per-agent model and provider

`meta.yaml`'s `model` field sets the model id for that agent. It flows through
`AgentProfile.model` into the LLM factory at startup. Omit it, or set
`model: null`, to use the provider's env default.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
python -m app.cli.manage_agents set-model researcher anthropic/claude-sonnet-4
python -m app.cli.manage_agents set-model researcher none   # clear the pin
```

`meta.yaml`'s `provider` field selects the LLM provider for that agent:
`openrouter`, `anthropic`, `openai`, or any custom label you have registered.
Default is `None`, which falls back to the global default provider
(`ZIRO_LLM_PROVIDER`, itself defaulting to `openrouter`).

<Info>
  Provider and model resolve as **one coupled pair**, never as independent axes.
  An explicit model choice wins and carries its provider; otherwise the provider
  decides and the model follows the agent's pin only when compatible, else the
  provider default. See [Providers](/guides/providers).
</Info>

You can also pin a **model tier** token instead of a concrete id:

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
model: ${EXECUTION_MODEL}
```

`planning`, `execution`, and `fast_execution` each resolve from
`<PROVIDER>_<TIER>_MODEL` (for example `OPENROUTER_EXECUTION_MODEL`), collapsing
to `<PROVIDER>_MODEL` when unset. The token resolves against whichever provider
is active at build time.

## The enabled flag

`meta.yaml`'s `enabled` flag controls whether an agent is selectable. A disabled
agent is hidden from the picker and rejected if requested with `--agent`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
python -m app.cli.manage_agents list
python -m app.cli.manage_agents enable life_coach
python -m app.cli.manage_agents disable researcher_docker
```

`packages/ziro-core/src/ziro/agents/registry.yaml` holds only `default`, the agent used when none is
named. If the default agent is disabled, the first enabled agent is used instead.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
python -m app.cli.manage_agents set-default researcher
```

## Config resolution order

Every policy file resolves **most-specific-first** across three layers:

<Steps>
  <Step title="Project layer">
    `<project>/.ziro/agents/<id>/<file>`
  </Step>

  <Step title="Home layer">
    `~/.ziro/agents/<id>/<file>`
  </Step>

  <Step title="Bundled package">
    The file shipped inside Ziro itself.
  </Step>
</Steps>

Resolution is per file, not per folder: an agent can override just
`permissions.yaml` in the project layer and inherit everything else from the
bundle. This is how an installed Ziro is configured under `~/.ziro/agents/<id>/`
without editing the package.

<Warning>
  An untrusted project layer is skipped wholesale for files in
  `DANGEROUS_PROJECT_FILES` (which includes `plugin_config.yaml`). Folder trust is
  content-hashed, so editing such a file revokes trust until you re-confirm it.
</Warning>

## Memory isolation

Long-term memory is keyed `("memory", user_id, agent_id)`, so each agent keeps
its own facts per user. The `agent_id` flows through the runnable config to both
the memory loader and the `save_memory` tool.

When a flavour is active, the key becomes `("memory", user_id, flavour)` instead.
Every agent in a flavour shares one memory store, so switching agents inside a
flavour never loses or cross-contaminates memory. A config with no flavour
(subagents, legacy callers) falls back to `agent_id`.

## Selecting an agent

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
ziro                                              # interactive picker
python -m app.main --user alice --agent coder     # explicit
python -m app.cli.chat_once --user alice --agent researcher --message "..."
```

In-chat, `/agent` lists the agents available in the active flavour and switches
in place, keeping the same thread and history.

## Managing agents

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
python -m app.cli.manage_agents add my_agent --name "My Agent"
python -m app.cli.manage_agents add my_agent --name "My Agent" --template researcher
python -m app.cli.manage_agents remove my_agent
```

`add` seeds the config files from the default agent, or from `--template <id>`.
Pass `--no-copy` for a meta-only scaffold.

<Note>
  A newly scaffolded agent ships **no** `plugin_config.yaml`, and an agent with no
  `plugin_config.yaml` in any config layer surfaces **zero** plugins. Plugins are
  per-agent opt-in: a new agent never silently inherits whatever the machine has
  installed.
</Note>

## Next steps

<Columns cols={3}>
  <Card title="Manage agents CLI" icon="terminal" href="/reference/manage-agents">
    Every subcommand, with flags.
  </Card>

  <Card title="Agent files" icon="folder" href="/reference/agent-files">
    The schema of each policy file.
  </Card>

  <Card title="Flavours" icon="git-branch" href="/concepts/flavours">
    Grouping agents into a shared workspace.
  </Card>
</Columns>
