Skip to content

Subagents

A subagent is a child agent that handles one focused task in its own isolated context. The model delegates to it through the task tool, gets a single summary back, and keeps the noisy detail out of the main conversation.

The task tool

The model calls task to spawn a subagent. The subagent sees only the prompt it's given — not the parent conversation — runs its tools to completion, and returns a single string. Use it to parallelize independent work or to keep a self-contained subtask from cluttering the main context. For a loop or a wide fan-out driven by real Python control flow instead of one task per call, see Workflows.

The task tool takes four arguments:

Argument Required Notes
prompt yes The complete, self-contained task — the subagent sees nothing else
subagent_type yes Which profile to run (see below)
model no A per-call model override (e.g. a cheaper or faster model)
run_in_background no Detach the subagent — the tool returns immediately with an agent id instead of the subagent's result; the model gets a one-line completion reminder on the next user turn and inspects the child with agent_list / agent_output / agent_kill

Spawning is non-destructive and auto-allowed, so the spawn itself never prompts. The subagent inherits the parent's permission mode, so any destructive tool call it makes still goes through the normal gate — see Modes & permissions. Every subagent is dispatched non-interactively: a shared harness preamble tells it there is no user to ask, and the ask tool is scoped out of every spawned child (even general's all-tools set) — the parent asks on the child's behalf after it returns. Its tool activity folds into the task row itself — the row's title tracks the latest child call live (⬡ task ▸ grep … · 4 calls · 12s) and expanding the row shows the full activity log, one line per call with its verdict glyph — instead of each child call printing its own transcript line. The returned summary is capped at 32 KB; anything longer is truncated with a note.

A background subagent is inspected with companion tools: agent_list (one row per agent — fg/bg marker, status, profile, last output line, tool count) and agent_output(agent_id, wait?, max_wait_ms?) (the buffered prose; wait=true blocks up to max_wait_ms, default 10s), alongside agent_kill (below) and agent_message (see Continuing a parked subagent).

Agent profiles

A profile defines a subagent's persona and its toolset. Eight are bundled:

Profile Tools For
general all available tools A general focused task in isolation (the default)
researcher read + web (read_file, web_fetch, web_search, web_context, cite_check, todos, skill) Read-only research; no edits, no shell
coder read + edit + shell (read_file, write_file, edit_file, shell family, todos, skill) Code changes verified with the shell; no web
explorer read-only + search_workspace (read_file, glob, grep, ls, search_workspace, todos) Read-only codebase orientation; no web (disjoint from researcher)
planner read-only + write_plan (read_file, glob, grep, ls, web_fetch, web_search, search_workspace, todos, write_plan) Researches and drafts a plan file; the plan lifecycle's entry point
critic read-only (read_file, glob, grep, ls, web_fetch, web_search, todos) Adversarial plan review; the plan lifecycle's "Review plan first" path
critic-code read-only (read_file, glob, grep, ls, web_fetch, web_search, todos) Adversarial code/output review; what the bundled /critique command dispatches
security-auditor read-only (read_file, glob, grep, ls, web_fetch, web_search, todos) Adversarial security review (six-dimension rubric: INJECTION / INPUT_HANDLING / AUTH / SECRETS / SUPPLY_CHAIN / CONFIG); dispatched independently via task

You can add your own, or override a bundled one, with a markdown-plus-frontmatter file. Discovery walks five roots, highest priority first; a project profile always wins, and the bundled defaults are the last-resort fallback:

  1. <workdir>/.kin/agents/
  2. <workdir>/.claude/agents/
  3. ~/.kin/agents/
  4. ~/.claude/agents/
  5. the bundled defaults shipped with kin
---
name: reviewer
description: Reviews a diff for correctness and risk.
tools:
  - read_file
  - shell
max-turns: 15
---
You are a code reviewer. Read the diff, then report bugs and risks.
Field Notes
name Required; must match ^[a-z][a-z0-9-]*$. The filename stem is canonical
description Shown to the model when it picks a subagent_type
tools The allow-list. Omit it (or leave it empty) to grant all available tools
max-turns The subagent's turn budget; defaults to 100, clamped to 1–1000

The body becomes the subagent's system prompt. The subagent_type enum the model sees is rebuilt each turn against your project's workspace, so a profile you add shows up without a restart. A profile using TOML-style +++ frontmatter is skipped with a warning — kin reads only --- frontmatter.

Note

The tools allow-list is a real boundary: a researcher profile can't write files or run a shell. Scope a profile to the least it needs.

Limits

The harness caps recursion and fan-out so a runaway delegation can't spiral:

Limit Value Meaning
Max depth 4 How deeply subagents may nest (0 is the top-level agent). A task call that would exceed it is refused, and the model is told to do the work directly
Max parallel 3 Concurrent in-flight subagents per session, bounded by a semaphore
Max background agents 3 Concurrent background subagents (run_in_background=true) per root session. Foreground children don't count against it — they're bounded by max-parallel + depth
Max result size 32 KB The returned summary string is truncated past this

Each subagent gets its own parallelism budget, so a parent waiting on a child never contends with that child's own children. Blocking prompts (approvals, questions) raised inside a subagent are always resolved at the root session, so they reach you in the normal modal.

When a subagent fails

A foreground task call can fail in a few ways, and each one produces a structured, id-bearing result so the parent can tell what happened and where the full transcript lives. The result is ALWAYS prefixed with error: — that prefix is the cross-backend is_error mechanism (loop._finalize auto-detects it; the Anthropic wire carries is_error: true, the OpenAI wire has no error field at all, so the text prefix is the only signal that reaches the model on both wires):

done_reason Result shape (registered task child)
error (wire death after retries) error: subagent <id> failed mid-run — partial output below; transcript retained (agent_output("<id>")) + the partial prose. A mid-run wire death surfaces the partial prose with a marker — you can no longer mistake a half-answer for a whole.
turn_cap (hit the profile's max-turns bound) error: subagent <id> hit its turn cap (N turns) — partial output below; transcript retained (agent_output("<id>")) + partial prose (or the head alone when no prose was produced).
loop_detected (doom-loop guard tripped) error: subagent <id> stopped — doom-loop guard tripped (partial output below; transcript retained (agent_output("<id>"))) + partial prose.
crashed (a raw harness exception in run_subagent) error: subagent <id> crashed: {exc} — transcript retained (agent_output("<id>")). The child parks (not closes — see the Lifecycle section below), so the transcript hint is true.
killed (DR 0057 — agent_kill / panel x) error: subagent <id> was killed before finishing. The parent turn continues.

The full transcript is always reachable via agent_output("<id>") — or the agent panelCtrl+O → select the row → o or Enter — for every failed row. The returned prose is capped at 32 KB; if a child produced more, the result gets a truncated at 32KB note pointing at agent_output("<id>") for the uncapped text.

The 32 KB cap is signalled by a truncated flag on the child's emit, not by string-sniffing — the shaper reads the flag and appends the id-bearing hint.

Lifecycle: spawn → run → park → closed

A subagent isn't destroyed when its run finishes — it parks. After a foreground task call returns (or a background agent completes), the child's session stays live on its registry row with the full transcript retained in messages. You can re-read it through the agent panel (Ctrl+O → select the row → o or Enter) or the agent_output tool without re-running anything.

Closing is deliberate, and happens in three places:

  • agent_kill (or the panel's x on a running row) cancels + closes the child's session.
  • Dismissing a finished row (the panel's x on a done agent) closes the child before dropping the row.
  • App exit (Session.aclose — quitting kin) closes every retained-idle child, so a parked subagent's lazy run_code kernel / headless browser / bg shells don't orphan.

Closing tears down processes, not data — a closed row's transcript stays readable. Each subagent also writes its own journal next to the root session's; those child journals are hidden from /resume and --continue (see Sessions).

Continuing a parked subagent: agent_message

A parked child isn't a snapshot — it's a live Session between turns. The agent_message(agent_id, message, run_in_background?) tool sends the next instruction to an idle child: the message is appended to its transcript and runs as a new turn, with the full prior history retained. This is the turn-completion resume model — what to call when you want to continue a conversation rather than spawn a fresh task.

Arg Required What it does
agent_id yes The parked child's id (from task or agent_list)
message yes The next instruction (appended to the child's transcript, runs as a new turn)
run_in_background no Detach the resume — returns immediately, fresh completion reminder fires on the next user turn

Refusals (all error:-prefixed, one line):

Target state Tool result
Unknown id error: unknown agent_id: <aid> (call agent_list to see live agents)
Still running error: <aid> is still running — it can only be messaged between turns
Closed (or killed) error: <aid> is closed — its transcript is still readable via agent_output

Two rules govern the model:

  • A running agent cannot be messaged — wait with agent_output(wait=true) for it to finish, then agent_message. A running resumed child refuses further messages too — the depth-invariant: any await-chain target must be IDLE, so two children can never await each other.
  • Resume does not change the child's depth — the child already exists (no new spawn), so the depth cap is the original spawn's depth. There is no spawn-rate gate on agent_message.

A fg agent_message returns ONLY the new turn's prose (the parent's messages already carries the full transcript; you re-read via agent_output / the panel). A bg agent_message returns resumed agent<N> (<profile>) in background, fires a fresh subagent_completed reminder on the next depth-0 turn, and behaves like an initial task(run_in_background=true) from the parent's perspective (counts against MAX_BG_SUBAGENTS = 3).

Step 4's failure strings now carry the resume hint alongside the transcript hint — a failed child hands the model both: transcript retained (agent_output("<id>")) — resume with agent_message("<id>", "<next instruction>"). The stop branch deliberately omits the resume hint — a clean stop is a completed turn, not a failure to recover from.