← Back to Skills
Browser

agent-swarm

runeweaverstudios By runeweaverstudios 👁 65 views ▲ 0 votes

IMPORTANT: OpenRouter is required.

GitHub
---
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

Comments

Sign in to leave a comment

Loading comments...