Skill: austindixson/agent-swarm (version 1.7.19)

--- 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": [[REDACTED_SECRET]] } } ``` **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), not token storage or collection.


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)