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 MarkdownA 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
- Describe one concrete, independently finishable task.
- Use isolated
taskwhen the prompt can be self-contained; usefork_agentwhen earlier conversation materially changes the answer. - Let Kin choose the narrowest relevant profile and tools.
- Press ++ctrl+o++ to inspect foreground/background agents.
- Read the two-line status and latest activity; expand only when needed.
- Steer, pause, restart, or stop a child through the same panel.
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:
<workdir>/.kin/agents/<workdir>/.claude/agents/~/.kin/agents/~/.claude/agents/- 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
toolsallow-list is a real boundary: aresearcherprofile 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 panel —
Ctrl+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, orclosed; - outcome:
stop,error,turn_cap,loop_detected,truncated,crashed,interrupted, orkilled; - health:
healthyor advisorystale.
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’sxon a running row) cancels + closes the child’s session.- Dismissing a finished row (the panel’s
xon 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 lazyrun_codekernel / 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 onagent_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.