Skill: nissan/agent-hive (version 1.0.2)

--- name: agent-hive version: 1.0.2 description: "Create and manage multi-agent teams in OpenClaw with shared workspace, budget governance, and mesh networking. Use when: (1) adding a new agent to an existing team, (2) setting up a multi-agent workspace from scratch, (3) configuring agent-to-agent spawn permissions (mesh/hub-spoke), (4) implementing budget governance for agent teams, (5) auditing agent spend and enforcing demotions. NOT for: single-agent setups or modifying agent personalities (edit SOUL.md directly)." metadata: version: 1.0.0 openclaw: requires: bins: [] env: [] network: outbound: [] primaryEnv: "" --- **Last used:** 2026-03-24 **Memory references:** 2 **Status:** Active # Agent Hive Create and manage multi-agent teams with shared memory, budget governance, and configurable spawn permissions. ## Architecture ``` Main Workspace (~/.openclaw/workspace/) ├── MEMORY.md, TOOLS.md, USER.md, IDENTITY.md ← shared brain (real files) ├── agents/<name>/ ← per-agent outbox, inbox, budget ├── agents/governance/ ← governance rules + audit log ├── memory/, projects/, scripts/, skills/ ← shared resources └── content/ ← shared content Agent Workspace (~/.openclaw/workspace-<id>/) ├── SOUL.md ← unique personality (local file) ├── AGENTS.md ← unique instructions (local file) ├── HEARTBEAT.md ← agent-specific heartbeat (local file) ├── .openclaw/ ← agent-specific config dir └── everything else ← SYMLINKS to main workspace ``` ## Adding a New Agent ### Step 1: Create workspace with symlinks ```bash AGENT_ID="<id>" MAIN="$HOME/.openclaw/workspace" WS="$HOME/.openclaw/workspace-$AGENT_ID" mkdir -p "$WS/.openclaw" # Symlink shared brain for f in .learnings IDENTITY.md MEMORY.md ROADMAP.md TOOLS.md USER.md; do ln -sf "../workspace/$f" "$WS/$f" done for d in agents content memory projects scripts skills; do ln -sf "../workspace/$d" "$WS/$d" done ``` ### Step 2: Create unique files Create `$WS/SOUL.md` — the agent's personality. This is theirs alone. Create `$WS/AGENTS.md` — agent-specific instructions. Must include: - Session startup checklist (read SOUL.md, check BUDGET.json, check INBOX.md) - Communication rules (INBOX/OUTBOX pattern) - What the agent does and doesn't do - Budget rules section (see references/budget-rules.md) Create `$WS/HEARTBEAT.md` — minimal heartbeat config. ### Step 3: Create agent directory in shared workspace ```bash mkdir -p "$MAIN/agents/$AGENT_ID" touch "$MAIN/agents/$AGENT_ID/INBOX.md" touch "$MAIN/agents/$AGENT_ID/OUTBOX.md" ``` ### Step 4: Create budget file Copy `scripts/create_budget.sh` output or create manually: ```json { "daily_limit_output_tokens": 50000, "today": "YYYY-MM-DD", "used_output_tokens": 0, "spawns": [], "status": "active", "warnings": [], "consecutive_overbudget_days": 0 } ``` Save to `$MAIN/agents/$AGENT_ID/BUDGET.json`. ### Step 5: Register in openclaw.json Add to `agents.list[]`: ```json { "id": "<id>", "name": "<Name>", "workspace": "/absolute/path/to/workspace-<id>", "identity": { "name": "<Name>", "emoji": "<emoji>" }, "model": { "primary": "anthropic/claude-sonnet-4-6", "fallbacks": ["<fallback-model-1>", "<fallback-model-2>"] }, "subagents": { "allowAgents": ["<peer1>", "<peer2>"] } } ``` Update existing agents' `allowAgents` to include the new agent. ### Step 6: Validate and restart ```bash python3 -c "import json; json.load(open('$HOME/.openclaw/openclaw.json')); print('JSON valid')" openclaw doctor # check for schema errors launchctl stop ai.openclaw.gateway && sleep 2 && launchctl start ai.openclaw.gateway ``` Wait 15 seconds before testing. ## Spawn Permission Models ### Hub-and-spoke (conservative) Only the orchestrator (main) can spawn agents. Agents route requests through OUTBOX. ```json "subagents": { "allowAgents": [] } // for all non-main agents ``` ### Full mesh with budget (recommended) All agents can spawn peers. Budget governance prevents abuse. ```json // Each agent can spawn all peers (not themselves) "subagents": { "allowAgents": ["<all other agent ids>"] } ``` ### Earned mesh (progressive trust) Start hub-and-spoke. Promote to mesh after demonstrating budget responsibility. Demote back to hub-and-spoke after repeated overspend. ## Budget Governance See `references/governance.md` for the full framework. Run the audit script on heartbeat: ```bash python3 scripts/budget_audit.py ``` Thresholds: Green (<80%), Yellow (80-100%), Red (>100%), Emergency demotion (>200%). 3 consecutive overbudget days → automatic demotion (mesh privileges revoked). Add step 4.5 to your HEARTBEAT.md — see `references/heartbeat-snippet.md`. ## Committee Pattern For project work, compose agent committees: 1. Identify which agents are needed (e.g., content + marketing + design review) 2. Spawn them in parallel with clear, scoped tasks 3. Each writes to their OUTBOX.md 4. Orchestrator collects results and synthesizes 5. Budget tracked per-agent across the project ## Verification Checklist After adding an agent, verify: - [ ] `workspace-<id>/` has SOUL.md + AGENTS.md as local files - [ ] All other files are symlinks to main workspace - [ ] `agents/<id>/BUDGET.json` exists with correct schema - [ ] Agent appears in `openclaw doctor` output - [ ] Gateway restarts without errors - [ ] Agent can be spawned: `sessions_spawn(agentId="<id>", task="Say hello")` - [ ] Budget audit includes the new agent: `python3 scripts/budget_audit.py`

path: references/governance.md
content: # Agent Budget Governance Framework

## Philosophy

Trust-based autonomy with accountability. Agents start with mesh privileges
(can spawn each other directly), keep them by being responsible with budget,
and lose them through waste.

## Daily Token Budget

Each agent has a daily output token budget. Resets at midnight in the configured timezone.

Default: 50,000 output tokens/day per agent. Orchestrator (main) is unlimited.

## Budget File Schema

Location: `agents/<agent_dir>/BUDGET.json`

```json
{
  "daily_limit_output_tokens": 50000,
  "today": "YYYY-MM-DD",
  "used_output_tokens": 0,
  "spawns": [
    {"target": "agent_id", "task": "brief description", "tokens": 5000, "ts": "ISO8601"}
  ],
  "status": "active|red|demoted",
  "warnings": [],
  "consecutive_overbudget_days": 0
}
```

## Enforcement Levels

| Level | Threshold | Effect |
|-------|-----------|--------|
| Green | < 80% | Normal operation |
| Yellow | 80-100% | Warning logged. Finish current work, avoid new spawns |
| Red | > 100% | Over budget. MUST NOT spawn. Route through orchestrator |
| Emergency | > 200% | Immediate demotion |
| Demoted | 3 consecutive days over | Mesh privileges revoked |

## Demotion Process

1. Set `allowAgents: []` in openclaw.json for the agent
2. Set `status: "demoted"` in BUDGET.json
3. Log event to `agents/governance/log.jsonl`
4. Restart gateway
5. Alert the human operator

## Rehabilitation

1. Agent demonstrates responsible behaviour for 5 days via orchestrator-mediated calls
2. Orchestrator recommends reinstatement
3. Human approves
4. Restore `allowAgents` and set `status: "active"`

## Governance Log

Append-only JSONL at `agents/governance/log.jsonl`:

```json
{"ts": "ISO8601", "agent": "name", "event": "daily_reset|warning|red|demotion|reinstatement", "details": {}}
```

## Agent AGENTS.md Budget Rules Section

Every agent's AGENTS.md must include:

```markdown
## Budget Rules
- Read `agents/<your_dir>/BUDGET.json` at session start to check your status
- If status is "red" or "demoted": do NOT spawn other agents. Write to OUTBOX.md instead.
- After completing spawned tasks, update BUDGET.json: increment `used_output_tokens` and add to `spawns[]`
- If approaching 80% of daily limit, finish current work and avoid new spawns
- Budget resets daily at midnight. Orchestrator audits on heartbeat.
- See `agents/governance/GOVERNANCE.md` for full rules
```


path: references/heartbeat-snippet.md
content: # Heartbeat Budget Audit Snippet

Add this as a step in your HEARTBEAT.md:

```markdown
### Agent Budget Audit

Run the budget audit to check all agent spending and enforce governance rules:

\```bash
python3 /path/to/workspace/scripts/budget_audit.py
\```

- Automatically resets daily counters at midnight
- Yellow warning at 80%, Red at 100%, Emergency demotion at 200%
- 3 consecutive overbudget days → automatic demotion (loses mesh spawn privileges)
- If any agent is demoted → alert human: "⛔ Agent {name} demoted: {reason}"
- If any agent is in Red → note in daily log
- If all green → silent
```


path: scripts/budget_audit.py
content: #!/usr/bin/env python3
"""Budget audit — run from Loki's heartbeat to check agent budgets.

Resets daily counters at midnight AEST. Flags warnings and demotions.

Usage:
  python3 scripts/budget_audit.py [--reset-only] [--json]
"""

import sys, os, json, glob
from datetime import datetime
from zoneinfo import ZoneInfo

AEST = ZoneInfo("Australia/Sydney")
AGENTS_DIR = os.path.expanduser("~/.openclaw/workspace/agents")
GOV_LOG = os.path.join(AGENTS_DIR, "governance", "log.jsonl")

# Agent directory name → agent id mapping
AGENT_MAP = {
    "analyst": "archie",
    "sara": "sara",
    "kit": "kit",
    "liv": "liv",
    "belle": "belle",
}

WARN_THRESHOLD = 0.80  # 80%
RED_THRESHOLD = 1.00   # 100%
DEMOTION_CONSECUTIVE_DAYS = 3
EMERGENCY_MULTIPLIER = 2.0  # 200% in one day = immediate demotion


def log_event(agent: str, event: str, details: dict):
    """Append to governance log."""
    os.makedirs(os.path.dirname(GOV_LOG), exist_ok=True)
    entry = {
        "ts": datetime.now(AEST).isoformat(),
        "agent": agent,
        "event": event,
        "details": details,
    }
    with open(GOV_LOG, "a") as f:
        f.write(json.dumps(entry) + "\n")


def audit_agent(agent_dir: str, agent_name: str, today: str) -> dict:
    """Audit one agent's budget. Returns report dict."""
    budget_path = os.path.join(agent_dir, "BUDGET.json")
    if not os.path.exists(budget_path):
        return {"agent": agent_name, "status": "no_budget_file"}

    with open(budget_path) as f:
        budget = json.load(f)

    report = {
        "agent": agent_name,
        "dir": os.path.basename(agent_dir),
        "limit": budget.get("daily_limit_output_tokens", 50000),
        "used": budget.get("used_output_tokens", 0),
        "status": budget.get("status", "active"),
        "consecutive_overbudget": budget.get("consecutive_overbudget_days", 0),
        "spawns_today": len(budget.get("spawns", [])),
        "actions": [],
    }

    # Check if we need to reset (new day)
    budget_day = budget.get("today", "")
    if budget_day != today:
        previous_used = budget.get("used_output_tokens", 0)
        previous_limit = budget.get("daily_limit_output_tokens", 50000)
        was_over = previous_used > previous_limit

        # Update consecutive overbudget counter
        consecutive = budget.get("consecutive_overbudget_days", 0)
        if was_over:
            consecutive += 1
            log_event(agent_name, "overbudget_day", {
                "day": budget_day,
                "used": previous_used,
                "limit": previous_limit,
                "pct": round(previous_used / previous_limit * 100, 1),
                "consecutive": consecutive,
            })
        else:
            consecutive = 0

        # Reset for new day
        budget["today"] = today
        budget["used_output_tokens"] = 0
        budget["spawns"] = []
        budget["warnings"] = []
        budget["consecutive_overbudget_days"] = consecutive

        # Check for demotion
        if consecutive >= DEMOTION_CONSECUTIVE_DAYS and budget["status"] != "demoted":
            budget["status"] = "demoted"
            report["actions"].append(f"DEMOTED: {consecutive} consecutive days over budget")
            log_event(agent_name, "demotion", {
                "reason": f"{consecutive} consecutive days over budget",
            })
        elif budget["status"] == "demoted":
            pass  # Stay demoted until manually reinstated
        else:
            budget["status"] = "active"

        log_event(agent_name, "daily_reset", {
            "previous_day": budget_day,
            "previous_used": previous_used,
            "was_over": was_over,
            "consecutive": consecutive,
        })

        report["actions"].append(f"Daily reset (prev: {previous_used} tokens)")

        with open(budget_path, "w") as f:
            json.dump(budget, f, indent=2)

        report["used"] = 0
        report["status"] = budget["status"]
        report["consecutive_overbudget"] = consecutive
        return report

    # Same day — check thresholds
    limit = budget.get("daily_limit_output_tokens", 50000)
    used = budget.get("used_output_tokens", 0)
    pct = used / limit if limit > 0 else 0

    report["pct"] = round(pct * 100, 1)

    if pct >= EMERGENCY_MULTIPLIER and budget["status"] != "demoted":
        budget["status"] = "demoted"
        report["actions"].append(f"EMERGENCY DEMOTION: {report['pct']}% of daily budget")
        log_event(agent_name, "demotion", {
            "reason": f"Emergency: {report['pct']}% in single day",
            "used": used,
            "limit": limit,
        })
    elif pct >= RED_THRESHOLD and budget["status"] == "active":
        budget["status"] = "red"
        report["actions"].append(f"RED: Over budget at {report['pct']}%")
        log_event(agent_name, "red", {"used": used, "limit": limit, "pct": report["pct"]})
    elif pct >= WARN_THRESHOLD and "yellow_warned" not in budget.get("warnings", []):
        budget.setdefault("warnings", []).append("yellow_warned")
        report["actions"].append(f"YELLOW: Approaching budget at {report['pct']}%")
        log_event(agent_name, "warning", {"used": used, "limit": limit, "pct": report["pct"]})

    report["status"] = budget["status"]

    with open(budget_path, "w") as f:
        json.dump(budget, f, indent=2)

    return report


def main():
    json_output = "--json" in sys.argv
    today = datetime.now(AEST).strftime("%Y-%m-%d")

    reports = []
    agent_dirs = sorted(glob.glob(os.path.join(AGENTS_DIR, "*")))

    for agent_dir in agent_dirs:
        if not os.path.isdir(agent_dir):
            continue
        dirname = os.path.basename(agent_dir)
        if dirname == "governance":
            continue
        agent_name = AGENT_MAP.get(dirname, dirname)
        budget_path = os.path.join(agent_dir, "BUDGET.json")
        if not os.path.exists(budget_path):
            continue

        report = audit_agent(agent_dir, agent_name, today)
        reports.append(report)

    if json_output:
        print(json.dumps(reports, indent=2))
        return

    # Human-readable output
    alerts = []
    print(f"📊 Budget Audit — {today}")
    print(f"{'Agent':<10} {'Status':<10} {'Used':>8} {'Limit':>8} {'%':>6} {'Spawns':>6}  Actions")
    print("-" * 75)
    for r in reports:
        pct_str = f"{r.get('pct', 0):.0f}%" if "pct" in r else "—"
        status_emoji = {
            "active": "🟢",
            "red": "🔴",
            "demoted": "⛔",
            "no_budget_file": "❓",
        }.get(r["status"], "⚪")

        print(f"{r['agent']:<10} {status_emoji} {r['status']:<8} "
              f"{r.get('used', 0):>8,} {r.get('limit', 0):>8,} {pct_str:>6} "
              f"{r.get('spawns_today', 0):>6}  {'; '.join(r.get('actions', []))}")

        if r.get("actions"):
            alerts.extend(r["actions"])

    if alerts:
        print(f"\n⚠️ {len(alerts)} action(s) taken")
    else:
        print("\n✅ All agents within budget")


if __name__ == "__main__":
    main()


path: scripts/create_agent.sh
content: #!/bin/bash
# Create a new agent workspace with shared brain symlinks
# Usage: create_agent.sh <agent_id>

set -euo pipefail

AGENT_ID="${1:?Usage: create_agent.sh <agent_id>}"
MAIN="$HOME/.openclaw/workspace"
WS="$HOME/.openclaw/workspace-$AGENT_ID"

if [ -d "$WS" ]; then
  echo "ERROR: Workspace already exists at $WS"
  exit 1
fi

echo "Creating agent workspace: $AGENT_ID"

# Create workspace and .openclaw dir
mkdir -p "$WS/.openclaw"

# Symlink shared brain files
for f in .learnings IDENTITY.md MEMORY.md ROADMAP.md TOOLS.md USER.md; do
  ln -sf "../workspace/$f" "$WS/$f"
done

# Symlink shared resource directories
for d in agents content memory projects scripts skills; do
  ln -sf "../workspace/$d" "$WS/$d"
done

# Create placeholder unique files
cat > "$WS/SOUL.md" << 'EOF'
# SOUL.md — TODO

_Replace this with the agent's personality and identity._
EOF

cat > "$WS/AGENTS.md" << 'EOF'
# AGENTS.md — TODO

_Replace this with the agent's instructions, responsibilities, and budget rules._
EOF

cat > "$WS/HEARTBEAT.md" << 'EOF'
# HEARTBEAT.md
If nothing needs attention, reply HEARTBEAT_OK.
EOF

# Create agent directory in shared workspace
mkdir -p "$MAIN/agents/$AGENT_ID"
touch "$MAIN/agents/$AGENT_ID/INBOX.md"
touch "$MAIN/agents/$AGENT_ID/OUTBOX.md"

# Create budget file
TODAY=$(date +%Y-%m-%d)
cat > "$MAIN/agents/$AGENT_ID/BUDGET.json" << EOF
{
  "daily_limit_output_tokens": 50000,
  "today": "$TODAY",
  "used_output_tokens": 0,
  "spawns": [],
  "status": "active",
  "warnings": [],
  "consecutive_overbudget_days": 0
}
EOF

echo ""
echo "✅ Agent workspace created at: $WS"
echo ""
echo "Next steps:"
echo "  1. Edit $WS/SOUL.md — give the agent a personality"
echo "  2. Edit $WS/AGENTS.md — define responsibilities + budget rules"
echo "  3. Register in openclaw.json under agents.list[]"
echo "  4. Update other agents' allowAgents to include '$AGENT_ID'"
echo "  5. Validate: python3 -c \"import json; json.load(open('openclaw.json'))\""
echo "  6. Run: openclaw doctor"
echo "  7. Restart gateway: launchctl stop ai.openclaw.gateway && sleep 2 && launchctl start ai.openclaw.gateway"


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