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

# Agents

> What an agent is in auto: one YAML file that declares who it is, where it runs, what it knows, what it can touch, when it wakes, and how events route to it.

An agent is the unit of programming in auto. Each agent is a YAML document under `.auto/agents/` in your repository — merged to your production branch, applied by [GitHub Sync](/concepts/github-sync), woken by [events](/concepts/triggers-and-events), and run as [sessions](/concepts/sessions) in cloud [sandboxes](/runtime/sandbox). This page explains what an agent file declares and how the pieces fit together; the field-by-field contract lives in the [agent file reference](/reference/agent-file).

## The questions an agent file answers

Every agent file answers the same set of questions. Each maps to a group of fields, and each group has its own reference page.

**Who is it?** `name` is the agent's resource name — 1–128 characters of letters, digits, `_`, `.`, `-` — and the handle everything else uses to address it. `harness` selects the coding-agent runtime (`claude-code` or `codex`), `model` picks the model within that harness, and `reasoningEffort` tunes it. `identity` gives the agent a human-facing presence: display name, username, avatar, and bio. See [identity](/reference/identity).

**Where does it run?** `environment` declares the sandbox: an image preset, extra build `steps`, cached `setup` commands, CPU/memory `resources`. It can be written inline in the agent file or shared as a [fragment](/reference/imports-and-fragments) between agents. `workingDirectory` sets where the harness starts. See [environments](/reference/environments).

**What does it know at start?** `systemPrompt` is the agent's standing instructions (up to 100,000 characters). `initialPrompt` and per-trigger `message` templates are rendered against the triggering event's payload — `{{github.issue.title}}`, `{{message.text}}` — so the first thing the session reads is the event that woke it. `env` injects environment variables, including `$secret` references resolved from the encrypted secret store. See [variables and templating](/reference/variables-and-templating) and [secrets](/reference/secrets).

**What can it touch?** `mounts` clone git repositories into the sandbox, with per-capability GitHub App authorization (`contents`, `pullRequests`, `checks`, `merge`, …). `tools` wire MCP servers: the built-in `auto` platform tool, the unified `chat` tool, the brokered GitHub MCP proxy, and any remote MCP server. `spendCaps` bound what its sessions may spend. See [mounts](/reference/mounts), [tools](/reference/tools), and [runtime controls](/reference/runtime-controls).

**When does it run?** `triggers` subscribe the agent to events — GitHub webhooks, chat messages, Linear issues, cron heartbeats, custom webhooks, and auto's own lifecycle events — filtered by `where` clauses over the event payload. See [triggers](/reference/triggers) and the [event catalog](/reference/events/github).

**How do events route?** Each trigger declares `routing`: `spawn` a new session, `deliver` into existing sessions, or `bind` — resolve the event to the one session already bound to its subject (a PR, an issue, a chat thread) and deliver there. `bindings` set per-target policy, and `concurrency: 1` makes the agent a singleton whose live session absorbs all routed work. See [triggers and events](/concepts/triggers-and-events).

## Anatomy of an agent file

A complete, working agent: it wakes when someone labels a GitHub issue `auto-fix`, fixes the issue in a sandbox, opens a pull request, then keeps owning that PR — review comments and the eventual merge route back to the same session.

```yaml .auto/agents/issue-fixer.yaml theme={null}
# Who it is
name: issue-fixer
harness: claude-code
model:
  provider: anthropic
  id: claude-sonnet-5
reasoningEffort: high
identity:
  displayName: Issue Fixer
  username: issue-fixer
  description: Fixes issues labeled auto-fix and opens pull requests.

# Where it runs: an inline environment compiles into its own resource
environment:
  name: node-runtime
  image:
    kind: preset
    name: node24
  resources:
    memoryMB: 8192
  steps:
    - RUN npm install -g tsx

# What it can touch
env:
  SENTRY_AUTH_TOKEN:
    $secret: sentry-auth-token
    optional: true
mounts:
  - kind: git
    repository: acme/widgets
    mountPath: /workspace/widgets
    ref: main
    auth:
      kind: githubApp
      capabilities:
        contents: write
        pullRequests: write
workingDirectory: /workspace/widgets
tools:
  auto:
    kind: local
    implementation: auto
  github:
    kind: github   # omitting `tools:` grants the curated default allowlist

# What it knows at start
systemPrompt: |
  You fix bugs in acme/widgets. Work from the mounted checkout on a fresh
  branch, keep changes minimal, and open a pull request that references the
  originating issue. Address review feedback on your own PR when it arrives.
displayTitle: "Fix #{{github.issue.number}}: {{github.issue.title}}"

# Session policy: archive after a day of inactivity
session:
  archiveAfterInactive:
    seconds: 86400

# PRs this agent opens auto-bind to this session; the platform holds the
# binding until the closed-PR trigger below releases it.
bindings:
  github.pull_request:
    lifecycle: held
    bind: onAttributedEvent

# When it runs, and how events route
triggers:
  - event: github.issue.labeled
    connection: github-acme
    where:
      $.github.label.name: auto-fix
    message: |
      Issue #{{github.issue.number}} was labeled auto-fix: {{github.issue.title}}
      {{github.issue.htmlUrl}}

      Reproduce it, fix it on a branch, and open a pull request.
    routing:
      kind: spawn
      bind:
        target: github.issue
  - events:
      - github.issue_comment.created
      - github.pull_request_review.submitted
    connection: github-acme
    where:
      $.github.auto.authored: false
    message: |
      New feedback on your PR #{{github.pullRequest.number}}. Read it and
      address anything actionable.
    routing:
      kind: bind
      target: github.pull_request
      onUnmatched: drop
  - event: github.pull_request.closed
    connection: github-acme
    message: |
      Your PR #{{github.pullRequest.number}} was merged or closed
      (merged={{github.pullRequest.merged}}). Wrap up and archive this session.
    routing:
      kind: bind
      target: github.pull_request
      onUnmatched: drop
      release: true
```

Walking through the load-bearing choices:

* **The inline `environment` becomes its own resource.** The compiler splits it out and leaves only the environment's name on the agent spec, so several agents can converge on identical inline environments — identical content under the same name deduplicates; conflicting content fails the apply.
* **`$secret` references never put values in YAML.** `SENTRY_AUTH_TOKEN` resolves from the project's envelope-encrypted secret store at session launch; `optional: true` means a missing secret omits the variable instead of failing the launch.
* **`connection: github-acme`** names the GitHub connection whose webhook events this trigger consumes. GitHub Sync injects the right connection name as a context variable when you use [managed templates](/reference/managed-templates), so most real files write `connection: "{{ $githubConnection }}"`.
* **`where` filters run before routing.** `$.github.label.name: auto-fix` means only that label wakes the agent; `$.github.auto.authored: false` keeps the agent from reacting to comments auto itself posted. All clauses in a `where` block AND together.
* **Templates render against the event payload.** `{{github.issue.number}}` in `message` and `displayTitle` resolves from the normalized event; a missing path renders as an empty string.
* **The spawn trigger binds at birth.** `bind: { target: github.issue }` records that this session owns that issue. The `bindings.github.pull_request` block then auto-binds any PR the session opens (`bind: onAttributedEvent`), and `lifecycle: held` prevents the session from unbinding or self-archiving while the PR is open — the closed-PR trigger's `release: true` is what frees it. Declaring `lifecycle: held` requires at least one bind-routed trigger on that target with `release: true`; the compiler enforces the pair.
* **`onUnmatched: drop`** silences bind-routed events whose subject has no bound session (for example, review comments on a PR this agent did not open).

## Identity and presence

The `identity` block is how an agent stops being an anonymous process and becomes a named coworker. All fields are optional individually, but an inline identity must set at least one:

| Field          | Constraint                                                                               |
| -------------- | ---------------------------------------------------------------------------------------- |
| `displayName`  | 1–80 characters                                                                          |
| `username`     | 1–80 characters                                                                          |
| `avatar.asset` | relative path under `.auto/assets/`, `.png`/`.jpg`/`.jpeg`, square, 512–2000 px, ≤ 2 MiB |
| `description`  | at most 140 characters as Slack counts them (non-ASCII and escaped characters cost more) |

An inline identity compiles into a separate identity resource named after the agent. The bound identity is what renders in the web app's session list and chat surfaces.

**Chat presence.** An identity can be *realized* as a real bot user in Slack or Telegram — a workspace member other people can @mention and DM. Realization is a separate, explicit per-workspace connect step for the agent's presence — it never happens as a side effect of an apply. Once realized, a mention of the bot ingests as a `chat.message.mentioned` event addressed to exactly that agent, a DM ingests as `chat.message.direct`, and replies in threads the agent participates in arrive as `chat.message.subscribed` — see the [Slack event catalog](/reference/events/slack).

**GitHub presence.** On GitHub there is no per-agent bot user; agents are addressed with slash commands at the start of a comment line: `/auto <agent-name> <message>` or the bare form `/<agent-name> <message>`. A bare token that does not resolve to an agent name (`/rebase`, a file path) triggers nothing, and `/auto rerun` is reserved as a platform command. An addressed comment produces a copy of the event targeted at that one agent, with `github.auto.mentioned` set in the payload.

## Composition: imports, fragments, templates

Agent files rarely stand alone. Three composition mechanisms, all covered in depth in [imports and fragments](/reference/imports-and-fragments) and [managed templates](/reference/managed-templates):

* `imports:` pulls in other documents — relative paths to fragments under `.auto/fragments/`, or managed-template specifiers like `@auto/agents@latest/pr-review.yaml`. Imports merge in listed order, `remove:` directives prune named `tools`/`triggers`/`env` entries from the merged result, and the document's own fields merge last and win.
* `variables:` declared on the entry document substitute `{{ $name }}` tokens inside imported content, so one fragment serves many repositories.
* Shared environments live in fragments: this repository's own agents import `.auto/fragments/environments/agent-runtime-base.yaml` rather than repeating the runtime definition.

The smallest possible agent file is real: after all imports merge, an agent needs only `name`, `harness`, and `environment` — and a file containing exactly `name: default` compiles, because Auto's built-in Default base supplies both.

## From file to running agent

Merging the file is the deployment. GitHub Sync compiles `.auto/` on every push to the bound branch, plans the diff against what was last applied, and applies it — the same compile-and-plan pipeline behind the `auto.resources.dry_run` MCP tool. A PR touching `.auto/` gets a plan comment before merge.

Two consequences worth internalizing:

* **The agent resource is versioned by apply, but sessions snapshot it at creation.** Every session copies the agent spec, its environment, and its tool wiring at spawn time; a later apply changes future sessions, never in-flight ones.
* **An agent is passive until an event or a person wakes it.** Applying an agent creates no session. Sessions come from triggers firing, from a person starting one in the web app, or from another agent calling `auto.sessions.spawn` — the subject of [sessions](/concepts/sessions).

## Where to go next

<CardGroup cols={2}>
  <Card title="Agent file reference" href="/reference/agent-file">
    Every field, type, default, and constraint in the agent facade.
  </Card>

  <Card title="Triggers and events" href="/concepts/triggers-and-events">
    Event keys, where-filters, and the spawn / deliver / bind routing model.
  </Card>

  <Card title="Sessions" href="/concepts/sessions">
    The durable execution of an agent: lifecycle, turns, commands, observation.
  </Card>

  <Card title="Environments" href="/concepts/environments">
    Sandbox images, build steps, cached setup, and resources.
  </Card>

  <Card title="Tools" href="/reference/tools">
    The auto platform tool, chat, GitHub MCP, and remote MCP servers.
  </Card>

  <Card title="Identity" href="/reference/identity">
    Display identity, avatars, and realized chat presence.
  </Card>
</CardGroup>
