Skip to content

Subagents

A subagent definition configures a bounded delegate in a Markdown file. Its frontmatter declares the subagent’s capabilities, and its body supplies system-prompt framing. Subagents share frontmatter keys with facets. For runtime behavior, see Subagents.

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. 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 definition in subagents.<name> takes precedence over every filesystem definition with the same name. The definition’s frontmatter name must match <name>; Polytoken rejects a mismatch at load time.

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. The older polytoken.auto_facet_hint spelling also works; polytoken.autonomous_hint takes precedence when you supply both keys. 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.

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. After a successful request, each clone keeps using its current candidate. Selecting a model group again resets that choice; a provider failure triggers failover to the next candidate.

When polytoken.inherit_tools is true, the subagent receives the calling facet’s tools alongside its own declared polytoken.tools at spawn time. Polytoken never inherits switch_facet and rejects definitions that list switch_facet in polytoken.tools or polytoken.undeferred_tools. A subagent’s facet stays fixed for the run; a switch_facet call from a subagent returns an error. Facet switching stays in the main session.

Use literal tool names in polytoken.tools_deny and polytoken.undeferred_tools, not tag! references. Polytoken also rejects mcp__<server> shorthands in polytoken.tools_deny; use full mcp__<server>__<tool> names. The deny list cannot contain exit_tool, progress_update, job-control tools (job_status, job_result, job_cancel, job_block, and list_jobs), tool_search, subagent, or skill. A tool cannot appear in both polytoken.tools_deny and polytoken.undeferred_tools; Polytoken rejects the definition at load time. When you neither inherit tools nor use shorthands in polytoken.tools, every polytoken.undeferred_tools entry must also appear in polytoken.tools.

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 explicitly declare polytoken.exit_tool_schema, Polytoken logs an informational warning if the subagent finishes without calling exit_tool. The warning reports that the subagent did not produce the declared structured result. Polytoken does not change the run’s result, classification, or finalization because of the warning.

Polytoken normalizes the exit status before classifying the run. A success boolean wins when the payload carries one. Otherwise Polytoken selects the first present field in this order: kind, outcome, status. If the selected field contains a string, Polytoken lowercases the value and matches the recognized families: success, ok, done, and completed mean success; failure, failed, and error mean failure; and cancelled, canceled, and cancel mean cancelled. If the selected field contains a non-string value, Polytoken does not check later fields. A payload with no selected string status 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. Use recognized enum values or include a success boolean so Polytoken can classify the exit payload without rejecting the call.

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.

In a subagent, progress_update records a brief status snapshot for the parent. Polytoken preserves the latest snapshot as a reminder during compaction so the subagent can resume from recorded progress. Return findings and conclusions through exit_tool.

During an exec run, Polytoken echoes each progress_update 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.

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. Before starting the subagent, Polytoken returns an error if a reference names a denied skill, a skill with polytoken.disable_model_invocation: true, or a skill that does not resolve. 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 subagent named triage, in .polytoken/subagents/triage.md, that reads and classifies without the tools to change anything:

---
name: triage
description: 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 a
category and a severity with one sentence of reasoning each. Your tools are
read-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 list restricts nothing: the subagent can access every skill unless polytoken.skills_deny excludes it. Subagents use their own skill policy regardless of whether you include the allow list.

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.

KeyTypeDefaultMeaning
polytoken.inherit_toolsbooleanfalseUnions 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_spawnbooleanfalseLets this subagent start subagents of its own.
polytoken.exit_tool_schemaJSON 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.

Polytoken loads subagent definitions at startup and when you reload its configuration; an edit to a definition takes effect only on the next reload.