@agent-core

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:

AgentThe question it answers
exploreWhere does something live, in how many places, or does it exist at all
researchWhat is actually true outside this workspace — an API, a version, a price, a convention
plannerWhat is the step sequence, and is it the right one
executeCarry out agreed work end to end
validateIs this plan, change or artifact sound
operateDoes the running system actually behave as claimed
generalAnything 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.

On this page