Kin / Agents & automation

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…

Read as Markdown

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.

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.

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.

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. 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. 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 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’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 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
---
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 KB The returned summary string is truncated past this
Mailbox 16 messages / 64 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 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.

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 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 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 (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?) 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 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.