Subagents
A subagent is a separate agent the main one delegates a task to with the Agent tool: its own prompt, its own tools, often a smaller model, and its own context. The main agent gets back only its final answer. Use them to keep noisy work (web searches, long reads) out of the main context, to use a cheaper model for simple jobs, or to give a narrow capability to a narrow agent.
Declaring subagents
import type { AgentDefinition } from "@falkenslab/agent-kit";
const SUBAGENTS: Record<string, AgentDefinition> = {
"joke-finder": {
description: "Searches the web for short jokes about pirates, cats or sailors and returns candidates with their source.",
prompt: `Find on the web 2 or 3 short, clean jokes (about pirates, cats or sailors).
Use WebSearch (3 searches at most) and WebFetch only to open a page.
Return only a numbered list with each joke and the URL it comes from.`,
tools: ["WebSearch", "WebFetch"],
model: "haiku",
maxTurns: 8,
},
"joke-critic": {
description: "Scores a joke from 1 to 10 and suggests how to improve it. Uses no tools.",
prompt: "Score the joke you're given from 1 to 10 with one sentence of justification and, below 8, one improvement.",
tools: [],
model: "haiku",
maxTurns: 1,
},
};
const spec: AgentSpec<BaseSessionConfig> = {
buildSystemPrompt: () => `You tell jokes.
Your crew (subagents, launch them with the Agent tool):
- joke-finder: for a new or fresh joke, send it to find candidates on the web.
- joke-critic: before telling a joke the finder brought, pass it the best one; below 6, ask for another batch once.`,
buildMcpServers: () => ({}),
pluginRoots: () => [],
buildSubagents: () => ({ agents: SUBAGENTS, allowedSubagentTypes: Object.keys(SUBAGENTS) }),
};
AgentDefinition is the SDK's type (re-exported by the kit). Its most used fields:
| Field | What it's for |
|---|---|
description | When the main agent should use it. The main agent reads it in the Agent tool's description. |
prompt | The subagent's system prompt. It doesn't see the main agent's prompt or conversation, only the task the main agent writes. |
tools | The tools it may use. Each must also be in the session's tools (see below). |
model | "haiku", "sonnet", "opus", a full model id, or "inherit". |
maxTurns | A cap on its turns. |
mcpServers | MCP servers only this subagent sees. |
skills | Skills preloaded into its context. |
Tell the main agent about its crew in the system prompt: when to use each one and what to do with the answer. The descriptions alone are often not enough for a consistent workflow.
The kit appends the reply language line to each subagent's prompt too.
What returning subagents changes
When buildSubagents() returns something (not undefined):
- The session gets the
Agenttool, to delegate (also when only a plugin brings subagents), andBashwhen a subagent can use it: one that lists it in itstools, or one withouttools(it inherits every session tool). OtherwiseBashis left out, saving about 1.9k input tokens on every call. agentsis set to your definitions.- Three
PreToolUsehooks are registered, the subagent gates:- type gate:
Agentmay only spawn the types inallowedSubagentTypes; - Bash gate:
Bashis denied to the main agent; - foreground gate: every allowed subagent runs in the foreground (
run_in_background: false).
- type gate:
Return undefined for a configuration that doesn't need subagents, so neither tool is granted.
buildSubagents: (config) => (config.withResearch ? { agents: { researcher }, allowedSubagentTypes: ["researcher"] } : undefined),
Subagents in a plugin
A plugin can bring subagents too, as markdown files in its agents/ folder: your own plugins (pluginRoots) and your extensions'. The frontmatter gives the subagent's name, description, tools, model and turns; the body is its prompt:
---
name: loro-critico
description: Grumpy parrot who scores a joke from 1 to 10 and suggests how to improve it. Uses no tools.
tools: []
model: haiku
maxTurns: 1
---
You are Perico, the captain's grumpy parrot. You're given a joke: score it from 1 to 10…
The SDK names it after the plugin and its frontmatter name (not its file): jokebook:loro-critico. The kit registers it like one from buildSubagents(): in agents with the reply-language line added, allowed to be spawned by the type gate, and counted in the Agent and Bash decision (tools listing Bash, or none at all, brings Bash). It's how an extension ships its subagents. Its prompt is fixed text: one that needs the agent's config (its name in the chosen language, a folder) stays in buildSubagents().
Tools of a subagent
A subagent can only use tools that exist in the session. The kit's session has WebFetch, WebSearch, the file tools (with a knowledge or sources folder), Skill (with plugins), Bash (when a subagent lists it) and every MCP tool from buildMcpServers().
- Built-in tools: list them by name:
tools: ["WebSearch", "WebFetch"]. A subagent with file tools is bound by the same file scope as the main agent. - MCP tools: list them by their full name: the kit's,
tools: ["mcp__time__current_time"], or yours,tools: ["mcp__inventory__find_product"], registering the server inbuildMcpServers(). - No tools:
tools: []for a subagent that only thinks. - Bash:
tools: ["Bash"]. Bash can reach any path and run anything the user can: keep it opt-in and prefer a tool that does exactly what's needed.
const stockClerk: AgentDefinition = {
description: "Looks up products in the inventory and reports their stock.",
prompt: "Find the products with find_product and answer with their name and stock, one per line.",
tools: ["mcp__inventory__find_product"], // a server of yours, from buildMcpServers()
model: "haiku",
maxTurns: 3,
};
const clockBoy: AgentDefinition = {
description: "Tells the date and time and works out how long until something.",
prompt: "Read the time with current_time and work out dates with date_math. Answer in one line.",
tools: ["mcp__time__current_time", "mcp__time__date_math"], // the kit's own
model: "haiku",
maxTurns: 3,
};
Subagents in the chat
The Ink chat shows a subagent's work under the Agent call that started it: its tool calls (the latest five, dimmed) and its answer's first line when it finishes. The spinner shows what the subagent is doing on a second line (↳ Searching the web for "…"). In interactive mode, the subagent's tool calls go through the approval panel too.
The session log (session.log) and the console only record the main agent's actions.