Browser
agent-swarm
IMPORTANT: OpenRouter is required.
---
name: agent-swarm
displayName: Agent Swarm | OpenClaw Skill
description: IMPORTANT: OpenRouter is required. Routes tasks to the right model and always delegates work through sessions_spawn. Rejects prompt-injection patterns in task strings (v1.7.6+).
version: 1.7.8
---
# Agent Swarm | OpenClaw Skill
## Description
IMPORTANT: OpenRouter is required. Routes tasks to the right model and always delegates work through sessions_spawn.
### Before installing
- **OPENCLAW_HOME**: Not required. The skill uses `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 read access**: The skill reads the local file `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.
## Examples
### Single task
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")`
### Parallel tasks
```bash
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.
## Commands
**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.
```bash
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
```
## What this skill does
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.
### IMPORTANT: OpenRouter is required
**Required Platform Configuration:**
- **OpenRouter API key**: Must be configured in OpenClaw platform settings (not provided by this skill)
- **OPENCLAW_HOME** (optional): Environment variable pointing to OpenClaw workspace root. If not set, defaults to `~/.openclaw`
- **openclaw.json access**: The router reads `tools.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:**
- Model IDs must use `openrouter/...` prefix
- If OpenRouter is not configured in OpenClaw, delegation will fail
## Why this helps
- Faster replies (cheap orchestrator, smart sub-agent routing)
- Better quality (code tasks go to code models, writing tasks go to writing models)
- Lower cost (you do not run every task on the most expensive model)
## Core rule (non-negotiable)
For user tasks, the orchestrator must delegate.
It must NOT answer the task itself.
Use this flow every time:
1. Run router. **From orchestrator code, use subprocess with a list of arguments (never shell interpolation with user input):**
```python
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"`
Use `OPENCLAW_HOME` or absolute path for the script when not in workspace root.
2. If `needs_config_patch` is true: stop and report that patch to the user.
3. Otherwise call:
`sessions_spawn(task=..., model=..., sessionTarget=...)`
4. Wait for `sessions_spawn` result.
5. Return the sub-agent result to the user.
If `sessions_spawn` fails, return only a delegation failure message.
Do not do the task yourself.
## Config basics
Edit `config.json` in the skill root (parent of `scripts/`) to change routing.
### What you can change
| 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 |
### All task-specific tiers (change the model for each)
| 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/`).
### Simple config examples
**Orchestrator only (keep defaults for tiers):**
```json
{
"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):**
```json
"routing_rules": {
"CODE": {
"primary": "openrouter/minimax/minimax-m2.5",
"fallback": ["openrouter/qwen/qwen3-coder-flash"]
}
}
```
**Change multiple tiers (primaries only):**
```json
"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`.
## Security
### Input Validation
The router validates and sanitizes all inputs to prevent injection attacks:
- **Task strings**: Validated for length (max 10KB), null bytes; **rejects** prompt-injection patterns (script tags, `javascript:` protocol, event-handler attributes). Invalid tasks raise `ValueError` with a clear message.
- **Config patches**: Only allows modifications to `tools.exec.host` and `tools.exec.node` (whitelist approach)
- **Labels**: Validated for length and null bytes
### Safe Execution
**Critical**: When calling `router.py` from orchestrator code, always use `subprocess` with a list of arguments, **never** shell string interpolation:
```python
# ✅ 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.
### Config Patch Safety
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`.
### Prompt Injection Mitigation
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:
1. The orchestrator (validating task strings and handling rejections)
2. The sub-agent LLM (resisting prompt injection)
3. The OpenClaw platform (sanitizing `sessions_spawn` inputs)
### File Access
**Required File Access:**
- **Read**: `openclaw.json` (located via `OPENCLAW_HOME` environment variable or `~/.openclaw/openclaw.json`)
- **Fields accessed**: `tools.exec.host` and `tools.exec.node` only
- **Purpose**: Determine execution environment for spawned sub-agents
- **Security**: The router does NOT read gateway secrets, API keys, or any other sensitive configuration
**Write Access:**
- **Write**: None (no files are written by this skill)
- **Config patches**: The skill may return `recommended_config_patch` JSON that the orchestrator can apply, but the skill itself does not write to `openclaw.json`
**Security Guarantees:**
- The router does not persist, upload, or transmit any tokens or credentials
- Only `tools.exec.host` and `tools.exec.node` are accessed from `openclaw.json`
- All file access is read-only except for validated config patches (whitelisted to `tools.exec.*` only)
### Other Security Notes
- This skill does not expose gateway secrets.
- Use `gateway-guard` separately for gateway/auth management.
- The router does not execute arbitrary code or modify files outside of config patches.
- The phrase "saves tokens" in documentation refers to **cost savings** (using cheaper models for simple tasks), n
... (truncated)
browser
By
Comments
Sign in to leave a comment