Skip to content

Agent swarms

A swarm is the layer of agent cooperation. A fleet is many scoped processes running side by side, and a swarm is those processes or in-process engines working together on a task. A lead agent delegates subtasks to specialized sub-agents, auditors review the output, and the group iterates until the goal is met. Fleet and swarm orchestration are two layers, not two names for one thing.

Fleet orchestration Swarm orchestration
Concern Running agents Making agents cooperate
Unit polyglav serve process scoped to a folder Agent with a role, either a fleet process or an in-process engine
Primitives Supervisor with port allocation, health checks, restart policy, config generation /agent roles, delegate tool, sub-agent loops, auditor agents
Channel None POST /chat (cross-process) or an in-process sub-engine
Question How do I keep the agents alive? How do the agents get the job done?

The two compose. A swarm can run on top of a fleet, each swarm agent a fleet process and delegate routing over the same POST /chat API. Delegation also works in-process as a sub-Engine with no fleet at all. The two tasks are therefore separable, and neither is subordinate to the other. See fleet.md for the fleet side.

Building blocks

  • Roles (/agent): per-agent system prompt, session namespace, and optional model override. A role makes a sub-agent specialized instead of a copy of the caller. Per-agent permission profiles build on this.
  • delegate(role, task) tool: spawns a sub-agent loop that runs a task with its own role, session, and model, and returns the result. The core swarm primitive.
  • Auditor agents: sub-agents that review a produced output (tests, code review, fact-checking) instead of producing content themselves.
  • Generate to check to correct: run a main agent, an auditor, and a fix pass in a loop until the auditor passes. A team expresses this as a loop over a producer/reviewer stage block (see teams.md).
  • PM/dev/tester orchestration: multiple specialized agents cooperating on one outcome as a user-facing pattern.

Roles and delegation

Roles are named agent definitions (a system prompt, optional model override, and optional per-agent tool permissions) stored in a single JSON catalog with bundled, global, and local layers (schema, permission rule, and bundled default roster in roles.md). delegate(role, task) spawns a sub-agent that runs the task with the role's prompt, session, and model, and returns the result. Delegation resolves its permission from the selected type: a configured role uses its own tool_permission overrides (category delegate defaults to allow, so delegation runs without a prompt unless a role sets ask), while a temporary role created only for parallel work defaults to deny until opted in. The bundled catalog ships two pre-carved teams ready to delegate to out of the box: researcher/writer/referencer/editor for documents and planner/programmer/tester/code-reviewer for programming.

In-process sub-engines run with a quiet NullUI, so they never interleave with the caller's REPL, execute synchronously (one sub-agent at a time), and keep their own session (sub_<ts>_<id> in the shared sessions/ dir, the calling session recorded as parent_id, saved as a complete log). A caller may resume an existing run or session with delegate(role, task, resume=..., context=...), so the same agent keeps its prior context while per-run skills extend it (see teams.md). There is no standing warm-session name, because a session belongs to the run that started it. A sub-engine inherits the caller's provider, plugin manager, and worktree. The role's system_prompt becomes the sub-agent's system prompt, its model overrides the caller's when set, and its tool_permission carve narrows the caller's categories. The role's standing skills are injected, and a caller may layer per-run skills through delegate/team or a team stage, so a reusable agent keeps its identity while each task adds its own instructions. The carve is capped by the caller's grant_permission ceiling (default: the caller's own carve), so a sub-agent can never gain a permission the caller was not authorized to delegate. A role that sets grant_permission may hand down categories it does not use itself (a supervisor that denies edit/bash can still delegate them). Sub-agents run in build mode regardless of the caller's mode, so the role carve alone decides edit/bash. Ask-gated tools are auto-denied (no interactive confirm), so a sub-agent's effective permissions are exactly the categories its carve allows. A denial returns an explicit permission denied result rather than a user-decline marker, the sub-agent prompt names the auto-denied categories and forbids retrying, and the loop stops after a few consecutive denials so the agent escalates through ask or returns a final answer instead of looping. The delegate tool surfaces which role and task are running, returns the sub-agent's final answer as its result, and, with delegate_echo on (default), renders the answer plus a sub footer (duration + completion tokens) in the REPL. A sub-agent or team stage that needs a decision or permission mid-run calls the core ask tool: target='human' routes to the operator at the terminal (prefixed with the asking session), target='lead' routes to the calling engine's model via a bounded consultation, and the answer feeds back into the asking agent so it can continue instead of returning an open question. A kind='permission' ask is routed by ask_policy.permission and, when approved within the ceiling, adds a one-shot grant on the asking sub-agent (the operator may grant always for the rest of that run). Live in-REPL progress of a running sub-agent is a deferred enhancement, because Engine.chat() is a single blocking call with no progress channel yet. An interactive "jump into the active sub-agent" view is also future work. The focus_on_delegate config (off/ask/on, default off) is the first step toward that view: after a synchronous delegate completes, the REPL can move its focus to the child run (asking first with ask) while the caller's turn still finishes.

A document pipeline is a typical use: a lead agent asks a researcher for findings, hands them to a writer, has a referencer collect citations into a .bib file, and runs an editor to check the result against the original prompt. This is team orchestration built on roles and delegation. Named pipelines are stored as teams: ordered role chains with per-stage task hints and handoff notes, in a four-layer registry (bundled writing/programming rosters, plugin contributions, global/local teams.json, see teams.md). Engine.run_team executes a team sequentially, with per-run briefs built from the task, prior stage results, the shared team memory (.polyglav/memory/teams/<name>.md), and stage handoffs, and a rolling memory write after the run. A lead agent runs one with the core team(name, task) tool, and the operator with /teams run <name> <task>.

In-process vs. cross-process

In-process Cross-process
Unit Sub-Engine in the same process Another polyglav serve agent
Setup Spawn a child engine with a role Point delegate at a sibling agent's POST /chat
Isolation Shares the caller's process Process, worktree, and tool-policy boundaries (see fleet.md)
Uses Auditors, quick subtasks, no fleet required Team orchestration across scoped folders

Security

Sub-agents follow the same tool policy as any agent. In-process sub-engines share the caller's privileges. Cross-process delegation is confined by the target agent's own worktree and tool_permission config.