Subagents
A subagent definition is a Markdown file that configures a bounded delegate. For runtime behavior, see Subagents.
A subagent definition lives in a Markdown file, much like a facet: the frontmatter declares what the subagent does, and the body is the system-prompt framing. The shared keys work as they do for a facet, so the sections below cover what is different.
Where definitions live
Section titled “Where definitions live”A subagent definition goes in .polytoken/subagents/ in your project, or in
$XDG_CONFIG_HOME/polytoken/subagents/ (default:
~/.config/polytoken/subagents/) for ones you want everywhere. The filename is
the subagent’s name: triage.md defines a subagent named triage. The name
key in the frontmatter must match the filename; Polytoken rejects the definition
at load time when they differ.
Polytoken searches shipped and global definitions first, then each
daemon.discovery.extra_subagents_dirs entry in config order, then each
--subagents-dir directory supplied when you create a session, and finally
project .polytoken/subagents/. A later directory replaces a definition with
the same name. Project definitions take precedence over session extras, session
extras over config extras, and config extras over global and shipped definitions.
Polytoken skips missing directories. The daemon.follow_symlinks_for_configs
setting controls whether Polytoken follows symlinks while loading definitions.
exec --subagents-dir adds definitions for that invocation only. The
daemon.discovery.extra_subagents_dirs setting is available in configuration
files, not in the interactive configurator. A subagent defined under the config
subagents key takes precedence over every filesystem definition with the same
name.
What carries over from facets
Section titled “What carries over from facets”The shared frontmatter keys mean what they do for a facet. polytoken.model and
polytoken.fallback_models pin the subagent to a model. polytoken.tools grants
the subagent’s tools, with the same mcp__<server>, tag!ALL, and tag!ALL_MCP
shorthands; polytoken.tools_deny removes from the granted set.
polytoken.skills_allow and polytoken.skills_deny control skill access.
polytoken.undeferred_tools lists the tools whose full definitions appear up
front. Polytoken passes polytoken.autonomous_hint to the autonomous permissions
classifier. See what the classifier sees for the complete input context.
The body is your framing for the subagent, and Polytoken renders it as a template.
You do not need to put tool-use guidance or base framing in it: Polytoken wraps
every subagent in its shipped base prompt, which carries that guidance, then
appends your body as a suffix. You can use transclude to include shipped prompt
fragments in the body when a subagent needs shared reference text. Write the
instructions specific to this subagent’s job.
Launch metadata
Section titled “Launch metadata”The model’s subagent call supplies the task prompt. Polytoken renders that
task as a template first, then renders the definition body with the rendered task
available as prompt. Both sources render once for each clone in a counted
batch (several copies requested with the count input to the subagent
tool). Use a raw block when the task must contain literal {{ ... }} or {% ... %} text; see Templating.
The caller’s current working directory supplies the launch directory unless the
call supplies cwd. A supplied directory must exist, and an external directory
requires chdir approval. The subagent cannot move above its launch directory.
If a definition uses polytoken.model with a model group (an ordered failover
list of models; see Model groups and
failover), Polytoken
starts each clone at a different group candidate. In a counted batch, Polytoken
launches all copies together, but every clone gets its own job handle and
lifecycle. Polytoken keeps the rendered launch metadata fixed for each clone and
retries the current provider before moving to the next group candidate.
The route remains on a successful candidate until you or the model explicitly
selects a group again or the provider fails and the route
advances.
What is specific to subagents
Section titled “What is specific to subagents”A few keys exist only on subagents.
When polytoken.inherit_tools is true, the subagent receives the calling facet’s
tools alongside its own declared polytoken.tools at spawn time. One tool never
carries over: switch_facet. A subagent’s facet is fixed when Polytoken starts
it, and a switch_facet call from inside a subagent returns an error saying the
facet is fixed for that run. Facet switching stays in the main session.
polytoken.allow_subagent_spawn, when true, lets this subagent start subagents
of its own.
Polytoken registers an exit_tool for every subagent and rejects any definition
that lists exit_tool in polytoken.tools. The model calls exit_tool to finish,
and the payload becomes the result Polytoken returns to the parent conversation. The
default payload is a success flag and a summary. To change its shape, declare a
polytoken.exit_tool_schema: a JSON Schema describing the payload you want the
subagent to return. The Template Reference gives
the default. When you declare polytoken.exit_tool_schema explicitly, Polytoken
also logs a warning if the subagent finishes without calling exit_tool — the
declared structured result was never produced. The warning is informational: the
run’s result, classification, and finalization are unchanged.
Polytoken normalizes the exit status before classifying the run. A success
boolean wins when the payload carries one. Otherwise Polytoken reads the first
string among the kind, outcome, and status fields, lowercases it, and
matches it against the recognized families: success, ok, done, and
completed mean success; failure, failed, and error mean failure; and
cancelled, canceled, and cancel mean cancelled. A payload that offers no
status field at all counts as a successful structured return whose meaning
belongs to the parent.
Custom schemas interact with that normalization. When a
polytoken.exit_tool_schema declares a status, kind, or outcome string
enum with values outside those families, the definition still loads, but
Polytoken logs a warning at load, at reload, and under
polytoken validate subagent: the model will follow your enum, and the exit
check will not recognize the values. Align the enum with the recognized
families, or add a success boolean, to keep every exit call first-class.
When the model exits with a status outside the recognized families anyway, Polytoken rejects the call and forces a corrective retry. After the retry budget runs out, Polytoken salvages the payload: the parent receives the full exit payload marked salvaged and nonconforming, the run counts as completed with concerns rather than failed, and the job stays in the completed status. The no-progress breaker still terminates a run that repeats failing exit calls without making progress, and a breaker termination never produces a salvaged result.
progress_update records a brief status snapshot. In a subagent, the snapshot
signals progress to the parent, and when Polytoken compacts the subagent’s
context it carries the latest snapshot through as a reminder, so the subagent
resumes from recorded progress. Subagent findings and conclusions go through
exit_tool.
During an exec run, Polytoken echoes each snapshot to the terminal’s stderr as
exec: progress {…}. Exec results go through the structured-output completion
tool. progress_update is not a results channel in either context.
@skill: references in the spawn prompt
Section titled “@skill: references in the spawn prompt”A subagent spawn prompt can include @skill:<name>. Polytoken resolves each reference against the subagent definition’s polytoken.skills_allow and polytoken.skills_deny, not the parent facet’s lists. A denied, polytoken.disable_model_invocation: true, or unresolvable reference returns an error before the subagent starts. Forked subagents keep the skills referenced by the fork prompt.
message_subagent validates @skill: references against the same subagent policy before queueing a delivery. A failed reference prevents the message from entering the inbox. Polytoken caches referenced skills in the subagent context, so the skills survive the subagent’s own compaction.
A referenced skill reaches the subagent, not the parent conversation: Polytoken injects the skill body into the subagent’s context ahead of its first instruction. The reference shapes what the subagent reads; it does not choose the model the subagent runs on. Pin the model in the definition with polytoken.model and polytoken.fallback_models.
A small subagent
Section titled “A small subagent”A subagent named triage, in .polytoken/subagents/triage.md, that reads and
classifies without the tools to change anything:
---name: triagedescription: Reads an issue and proposes a category and severity.polytoken: model: your-smaller-model tools: [file_read, glob, grep] skills_allow: []---You triage incoming issues. Read what the caller points you at, then propose acategory and a severity with one sentence of reasoning each. Your tools areread-only.The name matches the filename. The polytoken.tools list grants reading and
searching but no edit or shell tool, so the subagent cannot modify the project. The
empty polytoken.skills_allow states that this subagent’s skill access is its own,
not inherited.
Frontmatter keys
Section titled “Frontmatter keys”The shared keys carry the meanings in the Facets key table. The keys below are the ones specific to subagents. The machine-readable schema is in the Template Reference.
| Key | Type | Default | Meaning |
|---|---|---|---|
polytoken.inherit_tools | boolean | false | Unions the calling facet’s tools into this subagent’s at spawn time. Does not inherit skill access, and never includes switch_facet: a subagent’s facet is fixed at launch. |
polytoken.allow_subagent_spawn | boolean | false | Lets this subagent start subagents of its own. |
polytoken.exit_tool_schema | JSON Schema | {success, summary} | The schema for the exit_tool payload the subagent returns to its caller. Declaring it explicitly also makes Polytoken log a warning when the subagent finishes without calling exit_tool. |
Loading changes
Section titled “Loading changes”Polytoken loads subagent definitions at startup and when you reload its configuration; an edit to a definition takes effect only on the next reload.