Subagent Delegation — AI's Clones
Subagent Delegation — AI's Clones — easy-to-understand guide based on official docs
Imagine you’re a project manager juggling three deadlines. You wouldn’t do all the work yourself—you’d brief three assistants, send them off, and collect their reports. That’s exactly what delegate_task does for your AI.
What Is Subagent Delegation?
In plain terms, it lets your main AI spin up several “mini AIs” (subagents), each working in its own isolated sandbox. They don’t share notes, they don’t interrupt each other, and they only hand back a single summary when done. Think of it as giving your AI the power to clone itself.
The Catch: Subagents Have Amnesia
Here’s the most important thing to remember: your subagent knows nothing about your conversation history. It’s a fresh hire on day one. Everything you’ve discussed, every command you’ve run—gone. Its only “onboarding” comes from two fields you provide:
goal: What you want it to accomplishcontext: The background info it needs to get the job done
So you must stuff all necessary details into that call. Otherwise, your subagent will wander around like a headless chicken.
One exception: when the parent has a resolved workspace directory, every subagent’s system prompt embeds that workspace’s project context files (.hermes.md > AGENTS.md chain > CLAUDE.md > .cursorrules — same discovery, priority, and size caps as the main agent; SOUL.md is excluded). So a subagent working in a repo picks up the repo’s own conventions without rediscovering them.
What Subagents Can and Can’t Do
Subagents inherit the parent’s enabled toolsets, but a few tools are blocked even when the parent has them:
delegate_task— blocked for leaf subagents (the default). Retained forrole="orchestrator"children, bounded bymax_spawn_depth.clarify— subagents cannot interact with the usermemory— no writes to shared persistent memorysend_message— no cross-platform side effectscronjob— no scheduling more work in the parent’s namestart_chat— no opening chats in the user’s name
Both roles retain execute_code (programmatic tool calling) so children can batch mechanical work instead of burning reasoning iterations.
One Task? Send One Assistant
For a single job, the syntax is straightforward:
delegate_task(
goal="Debug why tests fail",
context="Error: assertion in test_foo.py line 42"
)
Here, you’re telling the subagent: “Investigate the test failure—the error is at line 42.”
Multiple Tasks? Send a Team
Need several things done at once? Fire off multiple subagents in parallel. By default, up to 10 can work simultaneously (you can adjust this—there’s no hard ceiling):
delegate_task(tasks=[
{"goal": "Research topic A", "context": "Focus on recent primary sources"},
{"goal": "Research topic B", "context": "Compare the leading explanations"},
{"goal": "Fix the build", "context": "Project root: /home/user/project"}
])
Each task becomes its own independent subagent. They work in parallel, never waiting on each other.
Structured Output with output_schema
Each task can carry an optional output_schema, a JSON Schema object the child’s final answer must validate against. The child sees the schema up front as an output contract (“return ONLY the JSON value — no prose, no code fence”); when the answer comes back the parent validates it, and on failure sends the child exactly one bounded correction turn carrying the validation errors verbatim (the schema is not re-pasted). The task’s result then gains schema_valid (true/false) and, on failure, schema_errors.
A contract miss after the retry does not discard the child’s work: the result keeps status: completed with the child’s raw final text in summary, schema_valid: false, the schema_errors, and a schema_note saying the text is unvalidated. The parent extracts what it needs from the raw text instead of re-running a task that may have taken an hour. Prose or a code fence around otherwise-valid JSON (object or array) is tolerated by the validator. Keep schemas forgiving: require only the fields you will actually read. Tasks without an output_schema are unaffected.
Sending Images to a Subagent
Text context isn’t enough when the task is inherently visual—a screenshot the user sent, a design mock, a rendered chart. Each task accepts an optional images list (up to 8 entries; local file paths, http(s) URLs or data:image/... URLs):
delegate_task(tasks=[{
"goal": "Compare the rendered dashboard against the design mock and list layout deviations",
"context": "The app runs at http://localhost:3000; the repo is at /home/user/dash.",
"images": ["/home/user/mocks/dashboard-v2.png",
"https://cdn.example.com/current-render.png"],
}])
If the child model can see images, they arrive as native multimodal content on its first turn—it sees the actual pixels. If not, the goal gains [Image attached at: <path>] hint lines and the child is told to inspect them with vision_analyze. Forwarding is best-effort: unreadable paths are skipped with a log line, and any failure in the image plumbing falls back to the plain text goal—it can never break a spawn. Images are for things the child must see; put text file paths in context as usual.
Delegation Strategies (delegation.backend)
delegation.backend selects how each task becomes a subagent. default (the default) is everything above. Strategy plugins can let the model choose per task, from options you define:
- a profile per subagent (
profile_routes): each child runs as one of your Hermes profiles, with that profile’s model, credentials, skills and persona; - a named model map (
model_map): you list titled options (fast,deep, …) with a description and a provider/model each, and the model picks one per task.
Both come from the official advanced-delegation plugin, which also adds hermes delegation config (create, edit and delete model routes, pick profiles) and the /delegation slash command. While model_map or profile_routes is active, delegation.model / delegation.provider are inactive: the routes decide.
Each strategy’s settings live under delegation.backends.<backend name>:
delegation:
backend: model_map
backends:
model_map:
options:
- name: fast
description: Cheap and quick; lookups, summaries, simple edits.
provider: openrouter
model: google/gemini-3-flash-preview
- name: deep
description: Slow and careful; hard debugging and design work.
provider: anthropic
model: claude-opus-5
reasoning_effort: high
service_tier: fast # optional; refused at spawn if this route has no fast tier
default: fast # optional: option for tasks that set none
profile_routes: # read when backend: profile_routes
profiles:
- name: coder
description: Writes and refactors code; runs tests.
The model only ever sees the names and descriptions you configured, never raw provider, model or key fields. Each result of a backend-routed task carries route: the backend, the option or profile name, and the provider, model, reasoning effort and speed tier the child actually ran on (fallback_activated: true when a fallback provider took over). Writing your own: Delegation Provider Plugins.
Failures are explicit, never a silent switch. If the selected backend is missing (plugin not installed or disabled) or reports it cannot run, delegate_task returns a tool error naming the backend and the fix, and no subagent starts; it never quietly runs the children on the default route. Within a batch, a task its backend cannot route (an unknown option, a parameter the selected backend does not take, a profile that does not exist, a service_tier the route does not offer, an unknown reasoning_effort, missing credentials) is refused alone: its result entry has status: "error", the reason, and route.refused: true, while the other tasks run. A model route without its own fallback_providers uses delegation.fallback_providers; fallback_providers: [] on the route disables fallback for it. A profile route uses that profile’s own fallback chain.
Switching back. /delegation default (from the advanced-delegation plugin) saves delegation.backend to config.yaml and applies from the next session (/new): a running chat keeps routing on the backend it started with, because the per-task choices the model sees (option or profile names) are part of the cached tool list and the two switch together. A hand edit of delegation.backend behaves the same. /delegation <backend> --now also rebuilds the current chat’s tools at its next turn, so it routes on and offers the new backend from there, at the cost of one prompt-cache miss (context compression, which rebuilds the tools anyway, picks up the saved backend too). A task that sends a parameter its chat’s backend does not take is refused with a message naming the backend, the parameter and the fix (/delegation <backend> --now, or /new); it never runs on the backend’s default route. Without the plugin, hermes config set delegation.backend default restores the default.
:::note Profile routes share credentials
Listing a profile in delegation.backends.profile_routes lets this profile’s subagents use that profile’s credentials, credential pool, SOUL.md and terminal policy. Only list profiles whose keys and terminal access you are happy for this profile’s agent to use.
:::
Background Mode: The Main AI Stays Busy
Here’s a cool trick: top-level calls run automatically in the background. The main AI fires off the instructions and keeps working on other things. When a subagent finishes, its results come back as a new message. If your main AI is playing “coordinator,” it waits for all subagents to finish, then synthesizes everything into one final report.
Watching the Dock
Background processes share the dock: anything started with terminal(background=true) appears in a Processes block under the subagent rows the moment it spawns (⚙ <command> · 42s · last: <latest output line>), flips in place to ✔ exit 0 / ✘ exit 1 / ✘ killed when it ends, and leaves about 60 seconds later (the completion notification in the conversation is the durable record). In the Classic CLI monitor, process rows sit under the agents: Enter shows the process log tail and x stops that one process (processes can’t be steered); the TUI /agents overlay lists the same block under the spawn tree, and /stop ends every background process. Desktop shows the same processes as tiles above the composer. Press Ctrl+R in the Classic CLI or TUI composer to toggle the dock between its multi-row preview and a single shaded summary line (F7 remains an optional alias when the terminal forwards function keys). The summary retains the live count and expand/restore hints, adding activity when space permits. Typing and sending remain available; opening and closing the monitor preserves your draft and insertion point. This is a local presentation choice, not a saved config change.
When Subagents Compact Their Memory
Subagents don’t get unlimited room to think. They compact (summarize their own history) exactly where their parent does: at compression.threshold × the model’s window—0.50 by default—or at the global compression.threshold_tokens cap when one is set and lower than that.
There’s also an optional per-child cap: delegation.compression_threshold_tokens (default 0, meaning off). When you set it, the child compacts at whichever comes first—this cap or the ratio threshold above. It only moves the child’s trigger; it never touches the request payload or the parent’s own compaction. A token count of at least 16000 enables it; true or "200k" are config errors that get warned about and ignored.
It stays off by default for a good reason: a replay of a 1,393-agent run showed that 200K–400K caps landed within 5% of each other in cost once cache prefixes were intact—and every compaction is a chance to lose detail. So unless you have a specific reason, leave it alone.
On Messaging Platforms: One Message or Two?
By default, a background delegation on a chat platform has the parent send a one-line status and end its turn; the real answer arrives when the children finish. But if that platform has interim messages off (display.interim_assistant_messages: false), that status becomes the turn’s final answer — so you get a “working on it” message and then the real answer as a second message.
delegation.gateway_mode controls this:
delegation:
gateway_mode: auto # auto (default) | inline | background
auto— a messaging chat whose resolveddisplay.interim_assistant_messagesis off (global value,display.platforms.<platform>override, or the platform’s default) joins its children inside the turn: the parent waits in thedelegate_taskcall and sends one completed answer. Chats with interim messages on, the API server, local sessions and webhooks keep background delegation.inline— every gateway session joins its children inside the turn.background(aliasasync) — every gateway session keeps background delegation.
An inline session’s delegate_task returns results in the call and leaves out the live-control parameters (action list/steer/stop, subagent_id, message), just like a one-shot run. The typing indicator stays on while it waits, and /stop (or a follow-up message under the default busy_input_mode: interrupt) stops the children — each returns an interrupted result with its partial output.
Inline turns that run past agent.gateway_notify_interval (180 seconds by default) still get the periodic “still working” notice, which display.long_running_notifications controls separately (on by default for Telegram, Slack, Discord and WhatsApp). For exactly one message per turn, turn that off for the platform too:
display:
interim_assistant_messages: false
platforms:
telegram:
long_running_notifications: false
gateway_mode and interim_assistant_messages are part of a session’s tool schema, which is pinned with the conversation — changes apply from the next session (/new). A running chat keeps the mode it started with, across gateway restarts, context compaction and /reload-mcp.
Summary & Practical Advice
delegate_task is like giving your AI a cloning machine. It handles multiple independent tasks simultaneously, boosting efficiency dramatically.
Pro tip: Because subagents have amnesia, write your context like you’re emailing a brand-new colleague who knows nothing about your project. Include background details, file paths, error messages, reference links—everything. When in doubt, over-share. That’s the secret to making this tool sing.
Keep reading: git worktrees — Two agents sharing one checkout fight over the same files — git worktrees give each its own.
📖 Official Docs
This article is based on the official Hermes Agent documentation:Official docs › user-guide/features/delegation