Subagents
Delegate work to root-agent copies or declared specialists with their own tools and sandbox.
eve supports two ways to delegate work: the root-only built-in agent tool, which starts or continues a copy of the root agent, and declared subagents, which are specialists with their own directories. Use a subagent to run independent work in parallel, narrow the available tools, or give a task to a specialist.
Completion batching
Overlapping background work owned by a session forms a cohort. Tasks launched in later user turns join the open cohort while earlier work is still pending or its successful results await delivery. eve holds successful completion notifications until every task in that cohort has completed, failed, or been cancelled, then delivers the successful results together in one parent turn. Partial completions do not invoke the parent model. Work started after the cohort settles forms a new cohort. No configuration or debounce timer is required.
User messages, input requests, authorization events, failures, and cancellation are handled without waiting for the cohort. Child lifecycle events are processed before completion delivery so usage accounting and handle cleanup stay ordered. To follow work in progress, subscribe to the child session streams.
The built-in agent tool
The root session receives agent by default. The model calls it to delegate a task to a new copy of the root agent or continue an existing copy:
{
message: string; // everything the child needs; it does not see the parent's history
agentId?: string; // continue or steer an existing child
outputSchema?: object; // require structured output for this turn
}The copy uses the root's instructions, connections, auth, and sandbox. It receives the same tools except for the root-only agent, and starts with fresh conversation history and fresh state. Its file writes are immediately visible to the root. The built-in agent always runs in the background and needs no configuration: each call returns { status: "working", taskId, agentId }, then task notifications wake the parent with completion, failure, or cancellation. Give parallel children non-overlapping write scopes.
agent is intentionally root-only. Copies created by it cannot call agent, and declared subagents never receive the built-in tool. If a stale or forced recursive call reaches execution, eve rejects it instead of starting another child session.
The parent transfers data to the child through the message input it gives the subagent. Do not include sensitive data in a subagent request unless that child and its inherited tools, connections, sandbox, and telemetry path are appropriate for that data.
To prevent the root session from delegating to a fresh copy of itself, disable agent the same way as any other built-in tool:
import { disableTool } from "eve/tools";
export default disableTool();An authored root tool at agent/tools/agent.ts takes priority over the built-in.
Declared subagents
A declared subagent lives under agent/subagents/<id>/ and uses the same defineAgent helper as the root. Its location under subagents/ is the only thing that marks it as a subagent. Declare one when the child needs a clearly different prompt, role, or tool surface.
import { defineAgent } from "eve";
export default defineAgent({
description: "Investigate ambiguous questions before the parent agent responds.",
model: "anthropic/claude-opus-4.8",
});description is required. The parent reads it to decide whether to delegate, so the compiler rejects any subagent whose agent.ts leaves it out. Every declared local or remote subagent runs as a durable background task: the call returns { status: "working", taskId, agentId } immediately, then task notifications wake the parent with completion, failure, or cancellation. Human input requests surface separately on the parent session.
task_cancel is available to sessions that own background tasks, including a child that starts nested background work.
A mounted extension can also contribute declared subagents from extension/subagents/. The mount namespace prefixes the subagent visible to the consuming agent node: mounting an extension as crm exposes its reviewer subagent as crm__reviewer. The contributed subagent keeps its own isolated tools, connections, skills, hooks, instructions, sandbox, and nested subagents, and its modules can read configuration from the extension handle. See Extensions for the authoring and override behavior.
Vercel workspace peers
In a Vercel agent workspace, expose one other workspace member as a remote subagent with defineWorkspaceAgent. Create an ordinary file under the caller's agent/subagents/ directory. Its filename gives the model-visible tool name; name identifies the peer under agents/.
import { defineWorkspaceAgent } from "eve";
export default defineWorkspaceAgent({
name: "research",
});The helper uses the peer's root defineAgent({ description }) value as the model-visible tool description. Pass description to override it for this caller. When transport is omitted, eve selects the built-in transport for the runtime environment. Vercel deployments route through the current deployment and authenticate with the caller's Vercel OIDC token. A workspace hosted through withEve() in a Next.js app uses its /eve/agents/<name> mount; a hostless workspace uses /<name>. Outside Vercel, provide an explicit transport with url and optional auth or headers:
import { defineWorkspaceAgent } from "eve";
import { bearer } from "eve/agents/auth";
export default defineWorkspaceAgent({
name: "research",
transport: {
url: () => process.env.RESEARCH_AGENT_URL!,
auth: bearer(() => process.env.RESEARCH_AGENT_TOKEN!),
},
});An explicit transport replaces the environment default completely. The receiving peer must still accept the transport's credentials through its channel authentication policy. Set forwardPrincipal: true only when the peer trusts the caller to forward user identity, as described in Remote agents.
Use the peer's directory name under agents/. eve requires an exact workspace-member match, so add another declared subagent file for each additional peer. eve dev does not provide local workspace routing; configure an explicit transport to target a separately running peer.
Conditional availability
To expose a declared subagent only for certain sessions or turns, export
defineDynamic from that subagent's agent.ts. Return a defineAgent
configuration to expose the subagent, or null to omit it from the parent's tools.
import { defineAgent, defineDynamic } from "eve";
export default defineDynamic({
events: {
"session.started": (_event, ctx) =>
ctx.session.auth.current?.attributes.research === true
? defineAgent({
description: "Investigate ambiguous questions before the parent responds.",
model: "anthropic/claude-opus-4.8",
})
: null,
},
});Resolvers run at session.started or turn.started; step.started is not
supported for subagents. A nullish result (null or undefined) removes the
subagent's description and tool definition from the model-visible surface.
The subagent's filesystem manifest is always compiled; a non-nullish result injects
the returned agent configuration when the child runs.
Packaging controls must be available before the resolver runs. Put them on
defineDynamic, rather than the defineAgent returned by an event handler:
export default defineDynamic({
build: { externalDependencies: ["native-package"] },
events: {
"session.started": () =>
defineAgent({
description: "Use a native package when handling delegated work.",
model: "anthropic/claude-opus-4.8",
}),
},
});The compiler applies build.externalDependencies while bundling every authored
module in that dynamic subagent.
See Dynamic capabilities for
scope precedence, failure behavior, and the dispatch-time guard.
Minimum files:
agent/subagents/researcher/
├── agent.ts # required
├── instructions.md # or instructions.ts, optional
├── tools/ # optional, its own tools
├── extensions/ # optional, mounted only into this subagent
├── skills/ # optional, its own skills
├── sandbox/ # optional, its own sandbox + workspace seed
└── subagents/ # optional, nested subagentsExtensions mounted under subagents/<id>/extensions/ contribute only to that subagent. They use the same file and directory mount forms, namespacing, configuration, and overrides as root-agent extensions:
export { default } from "@acme/research-search";The root agent does not receive the extension's tools, skills, instructions, connections, or hooks.
schedules/ is not supported inside a declared subagent. Schedules are root-only.
The isolation boundary
A declared subagent inherits nothing from the root's authored slots. Discovery treats its directory as its own agent root, so it has only the instructions, tools, connections, skills, sandbox, hooks, and nested subagents authored under agent/subagents/<id>/. For a slot with a framework default, eve selects that source when the subagent does not author a replacement; it never inherits the root's authored version.
| Slot | Root built-in agent tool | Declared subagent |
|---|---|---|
| Instructions | Inherited (copy of the agent) | Own instructions.{md,ts}, optional |
| Tools | Inherited except root-only | Own tools/ |
| Connections | Inherited | Own connections/ |
| Skills | Inherited | Own skills/ |
| Sandbox | Shared with parent | Own sandbox/, else framework default |
| Hooks | Inherited | Own hooks/ |
| Extensions | Inherited contributions | Own extensions/ |
| State | Fresh | Fresh |
| Channels | Root-only | Root-only |
| Schedules | Root-only | Root-only |
For a declared subagent this means authoring or mounting anything the child needs. When two subagents need the same procedure, package the skill in a workspace extension and mount that extension in each subagent. Share typed helpers through lib/. The sandbox does not inherit from the parent; eve selects the default sandbox source unless the subagent authors subagents/<id>/sandbox.ts or seeds files via subagents/<id>/sandbox/workspace/.
The root built-in agent tool is the exception. Its children share the root's sandbox and tools because they are copies of the same agent working on the same files.
defineState is never shared, for either kind. Each child starts with fresh durable state.
What the parent sees
eve lowers every subagent visible to the current agent (the root built-in copy, declared, or remote) into a model-visible tool with the same { message, agentId?, outputSchema? } shape. The parent packs message with everything the child needs, since the child never sees the parent's history. Set outputSchema to require structured output for that turn; the child remains available for follow-up messages afterward.
Declared subagents can call nested subagents defined under their own directories. eve does not apply a separate depth limit; nesting ends where the authored directory tree ends. The built-in agent follows the stricter root-only rule above, so limits.maxSubagentDepth no longer exists.
Child sessions can still call their own declared or remote subagents, but they do not receive the built-in agent. Authored workflow tools use the same subagent availability and authorization checks as direct delegation.
A directly declared subagent's tool name is the bare path-derived name, with no prefix. agent/subagents/researcher/ registers as the tool researcher. A subagent supplied by a mounted extension includes the mount namespace, such as crm__reviewer. The model, approvals, logs, and evals reference the resulting name. Its input schema is:
{
message: string; // all context the child needs; it never sees the parent's history
agentId?: string; // continue or steer an existing child
outputSchema?: object; // require structured output for this turn
}Because the name lives in the same runtime tool namespace as authored tools, a subagent named researcher collides with a tool named researcher. eve rejects static collisions at build time and active dynamic collisions at runtime rather than picking a winner, so keep subagent directory names distinct from tool names.
Do not rely on subagent delegation by itself as an approval boundary. Put sensitive tools behind approval, connection approval, route/session authorization, or other controls wherever those tools can be called.
Each delegated subagent spins up its own child session and stream. The parent stream carries the control-plane events subagent.called and subagent.completed, plus interactive input.requested, authorization.required, and authorization.completed events proxied from descendants so the root channel can prompt the user. To follow the child's other progress, read subagent.called.data.childSessionId and subscribe at GET /eve/v1/session/:childSessionId/stream.
Channels with activity reporting attribute the backing agent's tool activity to its background task. Local and remote subagents share the task's progress item rather than adding a separate agent item. Continuing or steering a child with agentId attaches its new activity to the new task; the previous task keeps its completed or cancelled status. This applies to the built-in agent and declared subagent tools, not to child invocations inside custom workflow tools. Activity reporting is best-effort and does not change task execution or result delivery.
A background task that was already admitted survives cancellation of the turn that started it; background work that has not yet been admitted is rejected with the cancelled step. Use task_cancel to stop an admitted task. Cancellation delivers the task's final notification to an active parent even if the task must be stopped forcibly. Parent-session finalization cancels remaining live tasks.
Subagent model calls automatically retry classified transient provider failures, including overload errors delivered after a stream starts. eve makes at most three fresh model-call attempts, repeating only the current uncommitted call by default so completed earlier Workflow steps, tool results, and sandbox work remain available to the child. A declared subagent that opts into Workflow checkpoint batching can instead repeat the uncommitted calls and inline tool executions in its current batch. Other recoverable task errors fall back to Workflow's durable step retry from the last committed session snapshot. Exhausting the transient model-call attempts or the dedicated empty-response reissue returns one failed task result instead of stacking both retry budgets; terminal errors fail immediately.
Agent messaging
A child parks after answering instead of terminating, keeping its session and conversation history alive. A failed child turn can also leave the child parked — its latest status shows the error and the parent may message it again. Pass a parked child's agentId to the same subagent tool with a new message to continue that session. Omitting agentId (or passing an empty string or null) always starts a new child, and an agentId that matches no known agent falls back to starting a new child rather than failing. Passing a known agentId through a different subagent tool fails with AGENT_MISMATCH.
To steer a running background child, call the same subagent tool with its agentId and the updated message. eve cancels the previous task before starting a new task in the same child session. The child retains its conversation history, and the receipt contains the same agentId with a new taskId. The cancelled task cannot publish a later successful result. Steering does not undo tool side effects that have already occurred.
A child that is still starting, or is owned by a blocking workflow invocation rather than an admitted background task, continues to return AGENT_BUSY. Cancellation or delivery failures are reported to the caller; eve does not start a replacement child session to hide them.
Whenever the set of parked (resumable) children changes, eve appends a framework-injected note to the conversation — labeled [Agents] and carrying an <agents> block — listing each child's agentId, name, and latest status. The static system prompt tells the model the note is injected by eve, not written by the user. The note is appended only when the listing changes (an append-only design that preserves the provider prompt cache), the most recent note is authoritative, and children that are starting or running do not appear until they park again.
The parent holds agent handles only for its session lifetime. When the parent session ends, eve terminates local children and sends authenticated reset requests for remote children. Remote reset is best-effort: an unreachable deployment may retain the parked child until its own session deadline. For remote agents, upgrade both deployments before relying on continuation or reset behavior introduced by a newer eve version.
When to split
Split out a subagent when the task needs a different prompt or specialist role, a narrower tool surface, or its own runtime context. Don't reach for one when a skill would do. If the agent can keep its identity and needs only an optional procedure, a skill is the lighter choice.
What to read next
- Remote agents: call another eve deployment as a subagent.