imports: — from local fragments under .auto/fragments/ and from managed templates. This page is the reference for that composition: how import paths resolve, the exact order and per-field semantics of the merge, the remove: and append: directives, and the pitfalls that follow from “later imports win”.
Import syntax
imports: (or the singular import:; imports wins when both are present) accepts a string or an array of non-empty strings:
.auto/agents/pr-review.yaml
- A relative path, resolved against the importing file’s directory. From
.auto/agents/pr-review.yaml, the path../fragments/environments/agent-runtime-base.yamlresolves to.auto/fragments/environments/agent-runtime-base.yaml. Absolute paths and URLs are rejected:Agent import must be a relative path. - A managed-template specifier — anything starting with
@, in the form@scope/name[@version|@latest]/subpath. A file subpath is required (Managed template import must include a file subpath); omitting the version means@latest. See Managed templates for the full grammar and versioning behavior.
- An imported file must contain exactly one YAML document. (Files under
.auto/agents/may hold several----separated documents; imported fragments may not.) - Import cycles fail with the full chain:
Agent import cycle detected: a -> b -> a.
Agent import not found.
An agent named
default receives Auto’s built-in Default base before its declared imports, which is why name: default alone is a complete, runnable agent. This built-in base is not an active managed template.Merge order
Compilation of one agent document follows a fixed order:1
Merge imports, in listed order
Starting from an empty draft, each import is compiled recursively (its own imports first, depth-first), then merged over the accumulated result. Later imports win over earlier ones, per the per-field semantics below.
2
Apply remove directives
The document’s
remove: directives run against the merged import result — so removals strip inherited items, and the document’s own body can re-add.3
Merge the document's own body last
Local fields always win over anything imported.
name, harness, and environment — imports typically supply the last two.
Per-field merge semantics
“Later wins” means different things per field. The default rule — used by every field without special semantics — is: records deep-merge key-wise; scalars and plain arrays are replaced wholesale. On top of that default, four field families have their own behavior:
Consequences worth internalizing:
- Deep-merging object fields compose across imports. An imported fragment’s
session: { archiveAfterInactive: … }and your ownsession: { observeSpawnedSessions: false }merge into one policy; you do not lose the imported key by setting your own. - Named collections never lose items on override. Overriding a trigger with the same key deep-merges your fields into the imported trigger; new keys append. To drop an inherited item, you need
remove:— replacement of a whole collection never happens. - Trigger merge identity is fragile without names. A trigger’s merge key falls back to its event shape, so changing
events:in an overlay creates a second trigger instead of overriding the imported one. Give triggers an explicitname:when a fragment is meant to be overridden — the name is authoring-only and is stripped before validation.
remove: directives
remove: deletes named items inherited from imports. Exactly three targets are supported — tools, triggers, and env — anything else fails with Unsupported agent remove target "<t>"; supported targets are tools, triggers, env.
- Each target takes a name or an array of names. Names key into: the tool alias for
tools, the env var name forenv, and the trigger merge key (name → event → joined events →cron:<expr>:<tz>) fortriggers. - Removals run after imports merge and before the document’s own body merges, so a document can remove an inherited item and declare its own replacement in the same file.
mountsare deliberately not removable — a fragment’s mount grant is part of its contract.
The append directive
On the three file-backed string fields —systemPrompt, initialPrompt, onReplace — a plain string replaces the imported value entirely. To extend the imported value instead, use a directive object with an append: key:
.auto/agents/pr-review.yaml (excerpt)
- Exact whitespace, no hidden separator. The resolved value is the imported string immediately followed by the appended text. Authors own paragraph breaks — start the appended block with a blank line when you want separation from the base. (The final schema still trims outer whitespace on prompt fields.)
appendis the only operation. The directive object must carry exactly one key with a string value; unknown operations and multiple keys fail at apply and dry-run, naming the field and file. A directive object on a spec scalar field (model,displayTitle, …) is rejected outright:<field> does not support directive objects; append is supported on systemPrompt, initialPrompt, onReplace.- Directives compose down the import chain. Each document’s append resolves against the value its imports produced, so a chain of fragments can each add a section. Apply-time
{{ $name }}variables substitute inside appended text like any other imported string leaf. - Sibling composition works. An overlay fragment that appends may be imported as a sibling of the fragment supplying the base — the pending append resolves when the parent merges the two, and chained pending appends fold in order. The one ordering mistake this allows is importing the overlay before its base; that fails explicitly:
append directive was merged before any base value; import the base document before its append overlay. - An append with no base ever is an error. If the whole import chain never produces a value for the field, the compile fails:
append directive has no imported systemPrompt to append to.
@auto/agents entrypoint imports the shared runtime and operator baseline, then appends the role-specific review contract:
@auto/agents@latest/pr-review.yaml (excerpt)
Authoring fragments
Fragments are reusable partial agent documents under.auto/fragments/. They are validated at apply time — single document, valid facade fields, no import cycles, supported removal targets — but produce no resources themselves; they only exist to be imported.
Conventions from production .auto/ directories:
- Keep shared runtimes under
.auto/fragments/environments/. A runtime fragment declaresharnessplus an inlineenvironment:
.auto/fragments/environments/agent-runtime-base.yaml
- Fragments may declare any facade field, and may have their own
imports:— composition nests arbitrarily. - A fragment’s
variables:map is ignored when the fragment is compiled as an import; the entry agent document owns the variable scope (see Variables and templating).
Inline resources deduplicate by content
An inlineenvironment: (or identity:) compiles into a standalone generated resource named by its name. When several agent files generate a resource with the same name — the normal outcome of several agents importing the same runtime fragment — the definitions must be byte-identical, or apply fails:
Pitfalls
Import order decides who wins. Because later imports win, a local fragment that should override a managed template’s field must come after the managed import. This repo’s ownpr-review agent documents the rule inline:
.auto/agents/pr-review.yaml (excerpt)
model, session, triggers, …) at final validation. See Agent file for the full key list.
Legacy layouts are rejected, not ignored. kind:/metadata:/spec: envelopes inside agent files, and standalone .auto/sessions/, .auto/environments/, .auto/identities/ directories, all fail apply with migration guidance — environments and identities are inline-only.
remove: cannot touch mounts. Use an overlay that overrides the mount’s capabilities instead, or author a variant fragment without the mount.