# Subagents

> A subagent is a supervised child agent that handles one focused task. The model chooses either an isolated task child or a full-context forkagent child, gets one final answer back, and keeps the noisy detail out of th…

A subagent is a supervised child agent that handles one focused task. The model
chooses either an isolated `task` child or a full-context `fork_agent` child,
gets one final answer back, and keeps the noisy detail out of the main
conversation.

<!-- SOURCE: src/kin/harness/model_routes.py, src/kin/harness/tools/task.py, src/kin/harness/tools/fork_agent.py, src/kin/harness/subagents.py, src/kin/harness/agents.py, src/kin/harness/session/, src/kin/harness/defaults/agents/ -->

## Outcome

Delegate one bounded piece of work, understand whether it needs isolated or
full conversation context, and supervise it without reading a raw child log.

## Fast path

1. Describe one concrete, independently finishable task.
2. Use isolated `task` when the prompt can be self-contained; use
   `fork_agent` when earlier conversation materially changes the answer.
3. Let Kin choose the narrowest relevant profile and tools.
4. Press ++ctrl+o++ to inspect foreground/background agents.
5. Read the two-line status and latest activity; expand only when needed.
6. Steer, pause, restart, or stop a child through the same panel.

[![Kin agent panel showing readable foreground and background agent states, current activity, and supervision controls.](/docs-assets/kin/assets/screenshots/agent_panel.svg)](/docs-assets/kin/assets/screenshots/agent_panel.svg)

*The agent panel is organized for supervision: identity, state, activity, and
the next available control are visible without exposing child prose by default.*

## Choosing the context

| Tool | Context | Best for |
|---|---|---|
| `task(subagent_type=…, prompt=…)` | Isolated. It receives the prompt plus Kin/project system context, not the parent conversation. | Independent research or review, specialist profiles, model overrides, and lower context cost. |
| `fork_agent(prompt=…)` | Full represented conversation through the current user request, followed by the focused fork instruction. | Work where earlier decisions, corrections, or discussion materially affect the answer. |

A compacted conversation forks its current summary-plus-tail representation.
Kin does not silently compact or truncate the inherited prefix for a fork.
The unresolved assistant tool batch—including the `fork_agent` call and any
sibling calls—is excluded.

The sections below are the detailed tool/profile and lifecycle reference. For
day-to-day use, the context choice above and the agent panel are sufficient.

## 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](/docs/kin/guide/workflows/).

The `task` tool takes five 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 legacy per-call model override on the current endpoint. Mutually exclusive with `route`. |
| `route` | no | An operator-defined [model route](/docs/kin/guide/models-and-providers/#model-routes). The model can request only routes whose `selectable_for` includes `task`; mutually exclusive with `model`. |
| `run_in_background` | no | Detach the subagent. It returns an id immediately; lifecycle events automatically regain the main agent's attention so it can supervise the child without asking you to monitor it. |

Operator bindings outrank per-call choices: a profile assignment wins first,
then the default `task` assignment, then an allowed requested route, then the
legacy model override. Route/preset/model identity is shown on task progress,
agent rows, and inspection output; endpoint URLs and keys are never exposed to
the task model. A paused or restarted child keeps the exact backend snapshot it
started with, even after the root runs `/reload`.

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](/docs/kin/guide/modes-and-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&nbsp;KB; anything longer is truncated with a note.

A registered `task` agent is owned by the main conversational agent (MCA),
which uses `agent_list`, `agent_inspect`, `agent_wait`, `agent_message`,
`agent_control`, `agent_output`, and `agent_kill` to observe and settle it.
Supervision is event-driven: a child that settles, fails, or goes stale
regains the MCA's attention automatically, so the MCA is prompted **not** to
poll a healthy running child — it does other work or ends its turn and acts
when the event arrives (a planner or researcher legitimately runs for
minutes). The occasional check-in is fine; the harness discourages the loop:
`agent_wait` clamps its timeout to 10–60 seconds, and a timed-out wait
returns compact liveness rows plus a running count of consecutive misses
instead of a full snapshot. You never need to watch or relay for a subagent
yourself.
Automatic supervisor reminders contain structural metadata only; child prose
and raw diagnostics are available only through the explicitly framed output
and inspection tools.

## The fork_agent tool

`fork_agent` takes `prompt` and optional `run_in_background`. It deliberately
has no profile or model override: the child keeps the parent's exact backend,
model/route, composed system/project context, workdir, permission mode, and a
child-safe copy of the parent's live tool registry. Dynamic and MCP tools are
therefore available when the parent has them. `ask`, `workflow`, `write_plan`,
`present_plan`, and `fork_agent` itself are removed; `task` remains available
for isolated nested delegation.

Foreground execution returns the fork's final answer synchronously. Background
execution returns an agent id immediately and uses the same registry, events,
panel, ambient strip, steering, pause, restart, output, and kill lifecycle as a
background `task`. Inspection surfaces label the context truthfully as
`full conversation · N turns` or `isolated prompt`. `N` counts represented
human turns; canonical internal memory-reflection traffic remains in the
inherited snapshot but does not inflate that label.

Fork history is a frozen launch snapshot. `agent_message` continues the fork's
retained conversation normally. `agent_control(action="restart")` creates a
fresh background attempt from the original snapshot even if the root has moved
on; an optional replacement `prompt` changes only the focused fork instruction.

## Agent profiles

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

| Profile | Tools | For |
|---------|-------|-----|
| `general` | all available tools | A general focused task in isolation (the default) |
| `researcher` | read + web (`read_file`, `inspect_media`, `web_fetch`, `web_search`, `web_context`, `cite_check`, `todos`, `skill`) | Read-only research; no edits, no shell |
| `coder` | read + edit + shell (`read_file`, `inspect_media`, `write_file`, `edit_file`, `shell` family, `todos`, `skill`) | Code changes verified with the shell; no web |
| `explorer` | read-only + `search_workspace` (`read_file`, `inspect_media`, `glob`, `grep`, `ls`, `search_workspace`, `todos`) | Read-only codebase orientation; no web (disjoint from `researcher`) |
| `planner` | read-only + `write_plan` (`read_file`, `inspect_media`, `glob`, `grep`, `ls`, `web_fetch`, `web_search`, `search_workspace`, `todos`, `write_plan`) | Researches and drafts a plan file; the [plan lifecycle](/docs/kin/guide/modes-and-permissions/#planning)'s entry point |
| `critic` | read-only (`read_file`, `inspect_media`, `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`, `inspect_media`, `glob`, `grep`, `ls`, `web_fetch`, `web_search`, `todos`) | Adversarial code/output review; what the bundled [`/critique`](/docs/kin/guide/slash-commands/#kind-workflow-saved-workflow-scripts) command dispatches |
| `security-auditor` | read-only (`read_file`, `inspect_media`, `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` |
| `rig` | `rig`, `rig_shell`, `rig_memory`, `todos` | A non-interactive excursion on the persistent computer; distills its answer and records durable machine knowledge in Rig memory before returning. Credentials always require the operator's peek/control handoff. |

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

```markdown
---
name: reviewer
description: Reviews a diff for correctness and risk.
tools:
  - read_file
  - shell
---
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` | Optional operator leash; `0` or omitted = unlimited (the default), positive values are 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` or `fork_agent` call that would exceed it is refused, and the model is told to do the work directly |
| Max parallel | 3 by default; 4–7 on selected MiniMax/Z.ai plans | Concurrent in-flight subagents per session, bounded by a semaphore |
| Max background agents | Same provider-aware limit | 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&nbsp;KB | The returned summary string is truncated past this |
| Mailbox | 16 messages / 64&nbsp;KiB | Parent steering queued for a running or paused child is rejected on overflow, never truncated |
| Stall timeout | 120 seconds | An active non-paused child with no model/tool activity becomes `stale`; this is advisory and never cancels or restarts it |

Bundled profiles have no round cap. A registered child runs until it finishes
or the MCA pauses, interrupts, restarts, or kills it. This is deliberate:
resuming a capped child grants a fresh window, so a default profile cap would
only split healthy work into arbitrary chunks and force another MCA
intervention. The doom-loop guard catches repeated no-progress calls, and the
root's optional [`token_budget`](/docs/kin/reference/settings-toml/) accounts the MCA
and every child against one cumulative resource leash. Set a positive
`max-turns` only when a custom profile genuinely needs a round-count boundary.

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.

The higher cloud limits are selected with `/providers` and stored in
`provider_tiers`; they do not change the recursion-depth or workflow total-agent
guards. See [Cloud-plan subagent capacity](/docs/kin/guide/providers/#cloud-plan-subagent-capacity).

## 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` (a custom profile hit its positive `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 — exact diagnostics available via agent_inspect("<id>"); transcript retained via agent_output("<id>")`. Raw exceptions are not inserted as trusted tool prose. The child **parks**. |
| `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 panel](/docs/kin/concepts/agents/) —
`Ctrl+O` → select the row → `o` or Enter — for **every** failed row. The
returned prose is capped at 32&nbsp;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&nbsp;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 and control

Lifecycle, outcome, and health are separate:

- lifecycle: `running`, `pause_requested`, `paused`, `interrupt_requested`,
  `idle`, or `closed`;
- outcome: `stop`, `error`, `turn_cap`, `loop_detected`, `truncated`,
  `crashed`, `interrupted`, or `killed`;
- health: `healthy` or advisory `stale`.

`agent_control` requests a cooperative pause at the next model boundary,
resumes with an optional atomically queued message, interrupts the current run
while preserving its session, restarts from the frozen launch specification
under a new linked id, or closes an idle row. `agent_kill` remains the
destructive emergency path. Paused agents still count against capacity.

Pause is **background-only**: a foreground run blocks its parent turn, so the
resume that would release a paused foreground child could never be issued —
the request is refused (interrupt it, or queue steering with `agent_message`).
An interrupted row settles idle only once its cancelled run has fully
unwound, so a follow-up `agent_message` can never start a second concurrent
run on the same child session; a child interrupted while still queued for a
free slot is settled before its turn ever starts.

At each child model-round boundary, Kin acknowledges a pending pause, waits
for resume, then drains queued parent messages FIFO into one defanged steering
reminder. It never suspends arbitrary Python or an in-flight tool.

## Retention: spawn → run → idle → 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](/docs/kin/concepts/agents/)
(`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](/docs/kin/guide/sessions/#subagent-journals-are-hidden)).

## 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?)` queues steering for a
running, pause-requested, or paused child. The queue drains FIFO at the next
model boundary; messaging a paused child does not resume it. For an idle child,
the message is appended to its transcript and runs as a new turn.

| 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 | For an idle continuation, detach the new turn and return immediately |

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

| Target state | Tool result |
|---|---|
| Unknown id | `error: unknown agent_id: <aid> (call agent_list to see live agents)` |
| Interrupt requested | Refused because cancellation is already settling |
| Closed | Refused; output remains readable through `agent_output` |

Two rules govern the model:

- **Running steering is bounded** — at most 16 messages and 64&nbsp;KiB are
  accepted. Overflow is refused rather than truncating instructions.
- **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 background idle continuation returns
`resumed agent<N> (<profile>) in background`, publishes a fresh supervisor
event, and behaves like an
initial `task(run_in_background=true)` from the parent's perspective (counts
against the active provider tier's background-agent capacity).

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.

When a custom profile sets a positive turn cap, resuming a child that hit it
grants a fresh round window (the counter is otherwise cumulative across
steering within a run), so the advertised recovery path actually makes progress
instead of re-capping instantly. A resume also survives its profile being
deleted or renamed — everything a continuation needs is frozen on the row.
