Writing Agents
How to define, configure, and tune agents in swarm.
Overview
Agents are the core unit of work in swarm. Each agent runs in its own git worktree with its own backend session, system prompt, and permissions. You define agents in ~/.swarm/settings.json under the agents array of your project configuration.
Step 1: Create the Settings File
If you haven't already, initialize the configuration:
swarm init --path /path/to/your-project
This creates ~/.swarm/settings.json with a skeleton project config. Open it in your editor.
Step 2: Define a Provider
Providers specify how swarm connects to a model API. At minimum, you need one provider:
{
"version": 2,
"/home/user/my-project": {
"providers": {
"default": {
"type": "anthropic",
"api_key_env": "ANTHROPIC_API_KEY"
}
}
}
}
Provider fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type | String | Yes | — | Provider type ("anthropic") |
api_key_env | String | No | "ANTHROPIC_API_KEY" | Environment variable holding the API key |
base_url | String | No | null | Custom API base URL |
max_retries | u32 | No | null | Max retries for transient errors |
timeout | u64 | No | null | Request timeout in seconds |
You can define multiple providers and reference them by name in agent or defaults config.
Step 3: Set Project Defaults
The defaults section provides fallback values for all agents:
{
"defaults": {
"model": "sonnet",
"provider": "default",
"commit_interval": 300,
"max_consecutive_errors": 5,
"max_total_errors": 20,
"liveness": {
"enabled": true,
"idle_nudge_after_secs": 120,
"idle_nudge_interval_secs": 300,
"max_nudges": 3
}
}
}
Key defaults:
| Field | Default | Description |
|---|---|---|
model | "sonnet" | Default model identifier |
provider | "default" | Default provider name |
session_timeout | null | Per-session timeout (seconds) |
commit_interval | 300 | Auto-commit interval (seconds) |
max_consecutive_errors | 5 | Errors before an agent stops |
max_total_errors | 20 | Lifetime errors before an agent stops |
Step 4: Define Agents
Each agent needs a name and a prompt. All other fields are optional:
{
"agents": [
{
"name": "backend",
"prompt": "You are a senior backend engineer. Focus on API design, database schemas, and server-side logic. Write tests for all new endpoints.",
"model": "sonnet"
},
{
"name": "frontend",
"prompt": "@prompts/frontend.md",
"model": "sonnet"
},
{
"name": "reviewer",
"prompt": "You are a code reviewer. Read changes from other agents and provide feedback via messages.",
"model": "sonnet"
}
]
}
Agent fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | String | Yes | — | Unique identifier ([a-z][a-z0-9-]*) |
prompt | String | Yes | — | System prompt text or @path/to/file |
model | String | No | defaults.model | Model identifier |
provider | String | No | defaults.provider | Provider name reference |
permissions | PermissionsConfig | No | null | Agent-level permission overrides |
mode | String | No | See cascade | Agent execution mode |
Prompt Loading
Prompts can be specified in two ways:
- Inline: Write the prompt directly as a string value
- File reference: Use
@path/to/fileto load from a file relative to the project root
File references are useful for long prompts and allow version-controlling prompts alongside your code:
prompts/
backend.md
frontend.md
reviewer.md
Name Rules
Agent names must:
- Start with a lowercase letter
- Contain only lowercase letters, digits, and hyphens
- Be unique within the project
Invalid names cause a ValidationError at startup.
Step 5: Configure Permissions
Permissions control what tools each agent can use. They're defined at the project level and optionally overridden per agent.
Project-Level Permissions
{
"permissions": {
"allow": ["Read(*)", "Glob(*)", "Grep(*)"],
"deny": ["Bash(rm -rf *)"],
"default_mode": "default"
}
}
Agent-Level Overrides
{
"name": "frontend",
"prompt": "...",
"permissions": {
"allow": ["Bash(npm *)"],
"deny": ["Bash(rm *)"]
}
}
Agent permissions are evaluated after project permissions. See Permissions for the full evaluation order.
Rule Format
Rules follow the pattern Tool(specifier):
| Example | Meaning |
|---|---|
Read(*) | Allow reading any file |
Bash(npm *) | Allow any npm command |
Bash(rm *) | Match any rm command |
Edit(src/*.rs) | Match editing Rust files in src/ |
Step 6: Choose Agent Modes
The mode field controls how an agent interacts with tools and the operator. Available modes:
| Mode | Description |
|---|---|
default | Standard mode — tools require permission checks |
accept-edits | Auto-accept file edits without confirmation |
plan | Planning mode — agent proposes changes but doesn't execute |
dont-ask | Skip all permission prompts (auto-allow) |
bypass-permissions | Bypass the permission system entirely |
Set a default mode for all agents:
{
"defaults": {
"mode": "dont-ask"
}
}
Or override per agent:
{
"name": "reviewer",
"prompt": "...",
"mode": "plan"
}
Step 7: Add a Supervisor (Optional)
The supervisor generates the final merge commit message when stopping with --merge or --squash:
{
"supervisor": {
"prompt": "Summarize all agent changes into a concise merge commit message.",
"model": "sonnet"
}
}
If omitted, swarm uses a built-in merge prompt.
Complete Example
A full three-agent configuration:
{
"version": 2,
"/home/user/my-project": {
"providers": {
"default": {
"type": "anthropic",
"api_key_env": "ANTHROPIC_API_KEY"
}
},
"defaults": {
"model": "sonnet",
"commit_interval": 300,
"max_consecutive_errors": 5,
"max_total_errors": 20,
"mode": "dont-ask",
"liveness": {
"enabled": true,
"idle_nudge_after_secs": 120,
"idle_nudge_interval_secs": 300,
"max_nudges": 3,
"stall_timeout_secs": 900
}
},
"agents": [
{
"name": "backend",
"prompt": "@prompts/backend.md",
"model": "sonnet"
},
{
"name": "frontend",
"prompt": "@prompts/frontend.md",
"model": "sonnet",
"permissions": {
"allow": ["Bash(npm *)", "Bash(npx *)"],
"deny": ["Bash(rm -rf *)"]
}
},
{
"name": "reviewer",
"prompt": "@prompts/reviewer.md",
"model": "sonnet",
"mode": "plan"
}
],
"supervisor": {
"prompt": "@prompts/supervisor.md"
},
"permissions": {
"allow": ["Read(*)", "Glob(*)", "Grep(*)"],
"default_mode": "default"
}
}
}
Troubleshooting
"config validation failed: agent names must be unique"
Two agents have the same name. Each agent must have a distinct name.
"config validation failed: agents list cannot be empty"
You must define at least one agent in the agents array.
"config file not found"
Run swarm init to create the settings file, or verify the path in ~/.swarm/settings.json matches your project's canonicalized absolute path.
Agent keeps entering CoolingDown state
Check the agent's logs with swarm logs <name>. Common causes:
- Invalid API key (check the
api_key_envenvironment variable) - Model name not recognized by the provider
- Prompt too large for the model context window
The backoff formula is min(2000 * 2^(n-1), 60000) ms, where n is consecutive errors. After max_consecutive_errors (default 5), the agent stops.
Related
- Configuration — How config loading and resolution works
- Config Schema — Complete field reference
- Permissions — Permission system details
- Agent Lifecycle — Agent states and transitions