Install
openclaw skills install agent-swarmIMPORTANT: OpenRouter is required. Routes tasks to the right model and always delegates work through sessions_spawn.
openclaw skills install agent-swarmIMPORTANT: OpenRouter is required. Routes tasks to the right model and always delegates work through sessions_spawn.
OPENCLAW_HOME only if set; otherwise it defaults to ~/.openclaw. This is consistent in both metadata (_meta.json: listed in optionalEnv, not in env) and behavior.openclaw.json (at $OPENCLAW_HOME/openclaw.json or ~/.openclaw/openclaw.json). Only the fields tools.exec.host and tools.exec.node are used; no gateway secrets or API keys are read. Verify you are comfortable granting read access to that file before installing.Router output:
{"task":"write a poem","model":"openrouter/moonshotai/kimi-k2.5","sessionTarget":"isolated"}
Then call:
sessions_spawn(task="write a poem", model="openrouter/moonshotai/kimi-k2.5", sessionTarget="isolated")
python3 workspace/skills/agent-swarm/scripts/router.py spawn --json --multi "fix bug and write poem"
This returns multiple spawn configs. Start one sub-agent per config.
Manual/CLI use only. The examples below pass the task as a single argument; for programmatic use with untrusted user input, always invoke the router via subprocess.run(..., [..., user_message], ...) with a list of arguments (see Security). Do not build a shell command string from user input.
python scripts/router.py default
python scripts/router.py classify "fix lint errors"
python scripts/router.py spawn --json "write a poem"
python scripts/router.py spawn --json --multi "fix bug and write poem"
python scripts/router.py models
Agent Swarm is a traffic cop for AI models. It picks the best model for each task, then starts a sub-agent to do the work.
Required Platform Configuration:
~/.openclawtools.exec.host and tools.exec.node from openclaw.json (located at $OPENCLAW_HOME/openclaw.json or ~/.openclaw/openclaw.json). Only these two fields are accessed; no gateway secrets or API keys are read.Model Requirements:
openrouter/... prefixFor user tasks, the orchestrator must delegate. It must NOT answer the task itself.
Use this flow every time:
import subprocess
result = subprocess.run(
["python3", "/path/to/workspace/skills/agent-swarm/scripts/router.py", "spawn", "--json", user_message],
capture_output=True,
text=True
)
data = json.loads(result.stdout) if result.returncode == 0 else {}
CLI only (manual testing; do not use from code with untrusted user input):python3 workspace/skills/agent-swarm/scripts/router.py spawn --json "your task here"OPENCLAW_HOME or absolute path for the script when not in workspace root.needs_config_patch is true: stop and report that patch to the user.sessions_spawn(task=..., model=..., sessionTarget=...)sessions_spawn result.If sessions_spawn fails, return only a delegation failure message.
Do not do the task yourself.
Edit config.json in the skill root (parent of scripts/) to change routing.
| What | Key | Purpose |
|---|---|---|
| Orchestrator / session default | default_model | Main agent and new sessions (e.g. Gemini 2.5 Flash) |
| Task-specific model per tier | routing_rules.<TIER>.primary | Model used when a task matches that tier |
| Backup models if primary fails | routing_rules.<TIER>.fallback | Array of model IDs to try next |
| Tier | Key to change primary | Typical use |
|---|---|---|
| FAST | routing_rules.FAST.primary | Simple tasks: check, list, status, fetch |
| REASONING | routing_rules.REASONING.primary | Logic, math, step-by-step analysis |
| CREATIVE | routing_rules.CREATIVE.primary | Writing, stories, UI/UX, design |
| RESEARCH | routing_rules.RESEARCH.primary | Research, search, fact-finding |
| CODE | routing_rules.CODE.primary | Code, debug, refactor, implement |
| QUALITY | routing_rules.QUALITY.primary | Complex/architecture tasks |
| COMPLEX | routing_rules.COMPLEX.primary | Multi-step / complex system tasks |
| VISION | routing_rules.VISION.primary | Image analysis, screenshots, visual |
To change all task-specific models: edit each routing_rules.<TIER>.primary above. Use model IDs from the models array in config.json (must start with openrouter/).
Orchestrator only (keep defaults for tiers):
{
"default_model": "openrouter/google/gemini-2.5-flash"
}
(Other keys like routing_rules and models can stay as in the shipped config.json.)
Change one tier (e.g. CODE to MiniMax):
"routing_rules": {
"CODE": {
"primary": "openrouter/minimax/minimax-m2.5",
"fallback": ["openrouter/qwen/qwen3-coder-flash"]
}
}
Change multiple tiers (primaries only):
"routing_rules": {
"CREATIVE": { "primary": "openrouter/moonshotai/kimi-k2.5", "fallback": [] },
"CODE": { "primary": "openrouter/z-ai/glm-4.7-flash", "fallback": ["openrouter/minimax/minimax-m2.5"] },
"RESEARCH": { "primary": "openrouter/x-ai/grok-4.1-fast", "fallback": [] }
}
Only include tiers you want to override; the rest are read from the full config.json.
The router validates and sanitizes all inputs to prevent injection attacks:
javascript: protocol, event-handler attributes). Invalid tasks raise ValueError with a clear message.tools.exec.host and tools.exec.node (whitelist approach)Critical: When calling router.py from orchestrator code, always use subprocess with a list of arguments, never shell string interpolation:
# ✅ SAFE: Use subprocess with list arguments
import subprocess
result = subprocess.run(
["python3", "/path/to/router.py", "spawn", "--json", user_message],
capture_output=True,
text=True
)
# ❌ UNSAFE: Shell string interpolation (vulnerable to injection)
import os
os.system(f'python3 router.py spawn --json "{user_message}"') # DON'T DO THIS
The router uses Python's argparse, which safely handles arguments when passed as a list. Shell string interpolation is vulnerable to command injection if the user message contains shell metacharacters.
The recommended_config_patch only modifies safe fields:
tools.exec.host (must be 'sandbox' or 'node')tools.exec.node (only when host is 'node')All config patches are validated before being returned. The orchestrator should validate patches again before applying them to openclaw.json.
The router rejects task strings that contain prompt-injection patterns (e.g. <script>, javascript:, onclick=). Rejected tasks raise ValueError; the orchestrator should surface a clear message and not pass the task to sub-agents. Additional layers:
sessions_spawn inputs)Required File Access:
openclaw.json (located via OPENCLAW_HOME environment variable or ~/.openclaw/openclaw.json)
tools.exec.host and tools.exec.node onlyWrite Access:
recommended_config_patch JSON that the orchestrator can apply, but the skill itself does not write to openclaw.jsonSecurity Guarantees:
tools.exec.host and tools.exec.node are accessed from openclaw.jsontools.exec.* only)gateway-guard separately for gateway/auth management.