Agents
The seven base subagents under agents/, and how delegate spawns agent runtimes with their own loop.
agent-core's agents/ folder holds agent definitions — markdown
contributions (agents/*.md) discovered through the same
directory contract as skills,
instructions, and rules. An agent definition is a role: a frontmatter header
(id, title, description, context: fork) plus a body that becomes the
subagent's system prompt when it is spawned.
The seven base subagents
agent-core ships seven, and they are deliberately universal — none of them is about a particular subsystem, and none declares a tool allowlist:
| Agent | The question it answers |
|---|---|
explore | Where does something live, in how many places, or does it exist at all |
research | What is actually true outside this workspace — an API, a version, a price, a convention |
planner | What is the step sequence, and is it the right one |
execute | Carry out agreed work end to end |
validate | Is this plan, change or artifact sound |
operate | Does the running system actually behave as claimed |
general | Anything that fits no sharper role — the default when agent is omitted |
They form a chain in which roles do not overlap: whoever plans does not execute, whoever executes does not certify their own work, and whoever validates does not fix. That separation is the point — a second opinion from the author is not a second opinion.
Why no allowed-tools
A base agent declares no tool allowlist, so it inherits the caller's full tool set — which already had the caller's role, features and scope applied. That is what makes them universal: a fixed allowlist would blind them to every package tool, every MCP tool and everything the user later adds.
For a base agent, narrowing belongs to the call, not the file: delegate's
tools, blocked_tools and disabled_packages bound a specific run (an empty
tools: [] is rejected — omitting the field is what means "inherit"). A
persona file is the exception, and it works the other way round: its declared
allowed-tools is a CEILING the call can only narrow (see below). This also means
"read-only" agents are read-only by instruction, not by contract — and blocking
fs_write alone does not close it, because execute runs a shell
that can write anywhere the session's URI policies
allow, without pausing under the auto profile. Block execute too where the
restriction must actually hold. Custom agents that exist to be restricted may of
course declare allowed-tools themselves.
A persona's allowed-tools is a ceiling, and the call can only narrow it.
A delegate call naming its own tools is intersected with the agent's list:
names outside it are dropped, the drop is reported back in the result rather than
applied in silence, and a call that asks for none of the agent's tools is refused
instead of starting a child with nothing. The ceiling is one of several reasons a
requested name can fall out, and every one of them is named back the same way — see
delegate. So the file sets the widest a run may be, and
the call chooses how much of that to use.
On a persona file the two empty cases are deliberately different statements:
omitting allowed-tools inherits, while writing allowed-tools: [] hands the
child no tools at all — the shape for a persona whose job is to think rather
than act. That is the opposite of the call axis above, where tools: [] is
rejected, and the asymmetry is the point: the file declares a standing role,
the call narrows one run.
A subagent's limits hold when it CALLS, not only in what it is shown
Whatever bounds a child run — the agent's own list, the call's tools /
blocked_tools, a disabled package — bounds the tools it may actually invoke,
not merely the ones it is offered. A model does not have to call only what it was
shown: a name it remembers, guesses or reads in a file used to resolve anyway.
Each run now carries its own tool set, so a call outside it is refused with the
same neutral Tool not available every other unavailable name gets.
The same holds for a run that is paused for an approval and later continued: if the original call cannot be read back, the continuation is refused rather than resumed with wider access.
How delegation uses agent definitions
The delegate tool spawns a child
agent runtime with its own complete agentic loop. The optional agent
parameter selects a definition; omitting it resolves to general. A subagent can only
narrow the parent's package and tool access, never re-enable something the
parent disabled — the disabled-package set unions down the delegation tree,
and the parent agent's own Tool Access selection
caps the child's catalog the same way.
Agent resolution itself is gated, not only the child's tools. A definition whose feature requirements the caller does not hold, or whose package the caller has disabled, cannot be reached — by name or by path. An agent body becomes the child's system prompt, so knowing its id is not a grant.
An unknown or hidden name returns an error listing only the agents that caller can actually reach. The same gate runs when a paused child is resumed after a tool approval, and it runs before the approval is recorded: if the caller's access changed while the approval was waiting, the run fails with a clear error rather than quietly continuing on a generic prompt under the specialist's name.
Per-file switch-offs (for this agent or this conversation) currently reach only agents contributed by a synced source; for a built-in agent that switch removes it from the catalog and from injection, but does not block delegation.
Agents contributed by a synced source — markdown
found in a folder like .claude/agents/, with no install step — resolve the
same way. A file is an agent only when its frontmatter declares an id or
name; it is then reachable by that name (suffixed with its source when it
would collide with a built-in). Reference prose sitting beside a real
definition, with no such declaration, is not an agent at all — it is indexed
as a plain file, never listed and never summonable, in a synced source
exactly as in an installed package. Any address the model has already been
shown also works: the uri, the host path, or the container path. Addresses
are only built for agents that passed the gates, so an unreachable one is
indistinguishable from one that does not exist, and an address that two
sources both claim resolves to neither.
A contribution is addressed by its package, its category and its id, so a
skill and an agent inside one package may share a name — both load, and each
keeps its own enable/disable switch. Two files of the SAME category sharing an
id are genuinely ambiguous: the later one discovery walks replaces the earlier,
and it warns naming both paths. Where a name is shared across categories,
delegate answers with the AGENT; the skill of that name stays reachable
through the skill action of execute.
Every delegate run is persisted under the parent conversation (meta.json +
a full messages.jsonl child transcript), so finished and failed runs alike
can be opened read-only from the chat UI's Delegates tab.
A subagent receives a real working context, not just its own definition file:
the <packages> overview (with its own narrowed package set applied), the
rules and instructions those packages inject, the <runtime_stack> map of
where things live, and the current time and per-source state. Its definition
file contributes its BODY — the frontmatter is configuration, parsed and
applied rather than pasted into the prompt.
The <workspace> block is the one context the parent has and the child does
not: it is built by the workspace port, which a child runtime has none of, so
a subagent does not see the speaker's name or the project's agent roster.
Answer in the language of the brief you were given.
Two things it deliberately does not receive. The parent agent's identity
files do not travel down: a subagent is a role, not a person, and its own
definition body is its identity — it replaces the base system prompt
outright. Nor does it get the parent's <session> block or conversation
history; it works from the brief alone. Because a base agent already receives
the platform's rules and instructions, a well-written agent body says only
what is specific to the role — its boundary, its stopping condition, its
output shape — and never restates the general operating floor.
For the contribution mechanics (how agents/*.md is discovered, the
frontmatter vocabulary, and the agentskills.io alignment) see
package-system contributions.
Living identity files
Provisioned team agents own SOUL.md, USER.md, and HEARTBEAT.md under their private data subtree. Package team/ files are seeds, not updateable mirrors: ordinary file/config resync preserves evolved identity, while an explicit identity reset requires preview/backup/apply. The two are separately authorized — ordinary resync needs agent-update capability, and an identity reset needs more than that. Because it overwrites the files that open the target agent's every prompt, a reset requires full agent-management capability and either ownership of that agent or authority at least as strong as both an administrator and the agent's own role. A manager may reset the agents it owns and no others. The strength comparison is a number, never a role name, so a custom role behaves exactly as its configured strength says. An identity reset runs as a two-step preview → apply: the preview records a backup so a mid-apply failure rolls the whole identity group back, and a stale preview at apply is rejected. The files remain small but living—after meaningful work they may add, replace, or prune direct links to focused memories, plans, commits, workflow reports, sources, or READMEs. They do not copy those artifacts or force every link through one master memory index. SOUL changes rarely with durable craft, USER tracks current person-scoped collaboration links, and HEARTBEAT changes most often as a resume index.
Each file is injected as its own labelled block at the head of the prompt, and the platform guarantees the shape of that block: a file's contents cannot close its own block or open another one naming a different agent, its name cannot inject an attribute, and a symbolic link dropped into the directory is skipped rather than followed. The number of files injected is bounded by a platform setting, and when the bound is reached the prompt says so rather than dropping files silently.
What the platform does not claim is that the block cannot be influenced: the identity directory is writable by the agent and by members who hold write access to it, which is what makes a living identity possible in the first place. The guarantee is that whatever is written stays inside its own block, attributed to the agent that owns it.