Agent Swarm
> Autonomous multi-agent task orchestration daemon — ingest work from GitHub, Linear, or internal queues and dispatch to Claude, Codex, or Gemini agents with workspace isolation, state machine tracking, and full audit trails.
Full Public Reader
Agent Swarm
> Autonomous multi-agent task orchestration daemon — ingest work from GitHub, Linear, or internal queues and dispatch to Claude, Codex, or Gemini agents with workspace isolation, state machine tracking, and full audit trails.
---
Table of Contents
- [Overview](#overview)
- [Architecture](#architecture)
- [Packages](#packages)
- [Data Flow](#data-flow)
- [Setup](#setup)
- [Configuration](#configuration)
- [Usage](#usage)
- [API Reference](#api-reference)
- [Workflows](#workflows)
- [Database Schema](#database-schema)
- [Development](#development)
---
Overview
Agent Swarm sits between your work item sources (GitHub Issues, Linear tickets, internal queues) and AI agents (Claude Code, Codex, Gemini CLI). It fully automates the loop:
1. Ingest — Poll or receive webhooks from GitHub / Linear / machine queue
2. Dispatch — Select the best available agent (by label hints, load, or metadata)
3. Execute — Isolate each task in its own workspace (git clone), render a Liquid prompt, run the agent subprocess
4. Track — Persist every state transition to Supabase for full audit trails
5. Notify — Post Discord messages + update source status (GitHub labels, Linear status) on completion
Tech stack: Bun + TypeScript monorepo (10 workspace packages), Supabase for state, Prometheus for metrics, NUMU Bus for cross-system events.
---
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ Source Providers │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ GitHub Issues│ │Linear Tickets│ │ Machine Queue (Spore) │ │
│ │ (webhook + │ │ (GraphQL │ │ (Supabase polling) │ │
│ │ polling) │ │ polling) │ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬─────────────┘ │
└─────────┼─────────────────┼────────────────────── ┼────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ swarm-daemon (services/) │
│ │
│ ┌─────────────────┐ ┌──────────────────────────────────────┐ │
│ │ webhook-server │ │ orchestrator.ts │ │
│ │ :9300 │────▶│ 1. List queued work items │ │
│ └─────────────────┘ │ 2. selectAgent() (label/load) │ │
│ │ 3. WorkspaceManager.create() │ │
│ ┌─────────────────┐ │ 4. OrbitBridge.enrich() │ │
│ │ api-server │ │ 5. PromptRenderer.render() │ │
│ │ :9301 │ │ 6. AgentAdapter.startSession() │ │
│ └─────────────────┘ │ 7. Stream AgentEvents │ │
│ │ 8. Transition state + notify │ │
│ ┌─────────────────┐ └──────────────────────────────────────┘ │
│ │ metrics │ │ │
│ │ :9302 │ │ │
│ └─────────────────┘ ▼ │
└────────────────────────────────────────────┼────────────────────────┘
│
┌────────────────┬─────────────────┼─────────────────┐
▼ ▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Supabase │ │ Agent CLI │ │ Discord │ │ NUMU Bus │
│ State + FSM │ │ Claude/Codex │ │ Notifications│ │ :7890 Events │
│ Audit trail │ │ /Gemini/SSH │ │ │ │ │
└─────────────┘ └──────────────┘ └──────────────┘ └──────────────┘State Machine
Work items flow through these states:
queued ──▶ preparing ──▶ running ──┬──▶ succeeded ✓
└──▶ cancelled ✗ ├──▶ failed ✗
├──▶ retrying ──▶ running
└──▶ waiting_approval ──▶ runningEvery transition is written to `swarm_transitions` for a full audit trail.
---
Packages
The monorepo contains 10 packages under `packages/` plus the main daemon in `services/`:
| Package | Description |
|---|---|
| `@swarm/agent-adapters` | Unified CLI wrappers for Claude Code, Codex, Gemini, SSH-Claude |
| `@swarm/state-machine` | Pure FSM transition logic + Supabase store |
| `@swarm/source-providers` | GitHub, Linear, and Machine Queue ingest adapters |
| `@swarm/prompt-renderer` | Liquid template engine for workflow prompts |
| `@swarm/workspace-manager` | Per-task filesystem isolation + git cloning |
| `@swarm/orbit-bridge` | Orbit project context enrichment |
| `@swarm/numu-bridge` | WebSocket client for NUMU Bus event publishing |
| `@swarm/discord` | Discord message formatting and channel posting |
| `@swarm/metrics` | Prometheus Counter, Gauge, Histogram exposition |
| `@swarm/logger` | Structured JSON logger with sensitive-data redaction |
| `swarm-daemon` _(service)_ | Main daemon — bootstraps all servers and loops |
---
Data Flow
GitHub Issue opened
│
▼
webhook-server (:9300)
HMAC verify → SourceProvider.handleWebhook()
│
▼
WorkItem normalized (externalId, title, description, labels, git info)
│
▼
SupabaseStore.create() → status: "queued"
│
▼
Dispatch loop (every 30s)
listByStatus("queued")
selectAgent() ──► label hint? → metadata pref? → load balance
│
▼
WorkspaceManager.createForIssue()
mkdir [home-path]
git clone --depth=1 <repo>
│
▼
OrbitBridge.enrich() → inject project context
│
▼
PromptRenderer.render(workflow, issue)
pick template by labels (feature-impl.md, bug-fix.md, ios-build.md…)
Liquid render with issue variables
│
▼
AgentAdapter.startSession({ cwd, model, approvalMode, prompt })
spawn: claude --dangerously-skip-permissions --output-format stream-json
│
▼
Stream AgentEvents:
text_delta → accumulate
tool_start / tool_result → log
approval_request → auto-approve or pause
session_end
│
▼
SupabaseStore.transition("running" → "succeeded" | "failed")
SourceProvider.updateStatus() → GitHub label, Linear status
Discord.postCompletion()
NUMUBridge.publish("swarm.task.complete", { ... })
WorkspaceManager.scheduleCleanup(hours)---
Setup
Prerequisites
- [Bun](https://bun.sh) v1.0+
- A [Supabase](https://supabase.com) project
- GitHub personal access token (or App installation token)
- At least one agent CLI installed: `claude`, `codex`, or `gemini`
Install
git clone https://github.com/Diomandeee/agent-swarm
cd agent-swarm
bun installDatabase Migration
Apply the Supabase migration to create the required tables:
# Via Supabase CLI
supabase db push --db-url "postgresql://..."
# Or manually in the Supabase SQL editor
cat migrations/001_swarm_work_items.sql | supabase sqlThis creates:
- `swarm_work_items` — work item records with full metadata
- `swarm_transitions` — FSM audit log (every state change)
Environment
Copy and fill in the environment file:
cp .env.example .envSee [Configuration](#configuration) for all available options.
---
Configuration
All configuration is via environment variables (or a `.env` file):
Required
| Variable | Description |
|---|---|
| `SUPABASE_URL` | Your Supabase project URL |
| `SUPABASE_SERVICE_KEY` | Supabase service role key (bypasses RLS) |
GitHub (required for GitHub source)
| Variable | Description |
|---|---|
| `GITHUB_TOKEN` | Personal access token or App token |
| `GITHUB_WEBHOOK_SECRET` | Secret for HMAC webhook verification |
| `GITHUB_ORG` | Organization name (e.g. `Diomandeee`) |
Linear (optional)
| Variable | Default | Description |
|---|---|---|
| `LINEAR_API_KEY` | — | Linear API key |
| `LINEAR_TEAM_KEYS` | — | Comma-separated team keys to watch |
| `LINEAR_TRIGGER_LABELS` | `swarm,auto` | Labels that trigger dispatch |
| `LINEAR_POLL_INTERVAL_MS` | `60000` | How often to poll Linear (ms) |
Ports
| Variable | Default | Description |
|---|---|---|
| `SWARM_WEBHOOK_PORT` | `9300` | GitHub/Linear webhook receiver |
| `SWARM_API_PORT` | `9301` | REST API + metrics endpoint |
| `SWARM_METRICS_PORT` | `9302` | Dedicated Prometheus metrics port |
Daemon Behavior
| Variable | Default | Description |
|---|---|---|
| `SWARM_POLL_INTERVAL_MS` | `30000` | Dispatch loop poll interval |
| `SWARM_MAX_SESSIONS` | `3` | Max concurrent agent sessions |
| `SWARM_APPROVAL_MODE` | `auto-all` | `auto-all`, `auto-edit`, `manual` |
| `SWARM_WORKSPACE_ROOT` | `[home-path]` | Root dir for task workspaces |
| `SWARM_WORKSPACE_CLEANUP_HOURS` | `24` | Hours to keep workspace after completion |
| `SWARM_GIT_DEPTH` | `1` | Git clone depth |
Notifications
| Variable | Default | Description |
|---|---|---|
| `DISCORD_ENABLED` | `false` | Enable Discord notifications |
| `DISCORD_WEBHOOK_URL` | — | Discord webhook URL |
| `DISCORD_OPS_CHANNEL` | — | Channel ID for ops alerts |
NUMU Bus
| Variable | Default | Description |
|---|---|---|
| `NUMU_HOST` | `localhost` | NUMU Bus WebSocket host |
| `NUMU_PORT` | `7890` | NUMU Bus WebSocket port |
Logging
| Variable | Default | Description |
|---|---|---|
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
---
Usage
Start the Daemon
# Development (with hot reload)
bun run dev
# Production
bun build services/swarm-daemon/src/index.ts --outdir dist --target bun
bun dist/index.jsRegister GitHub Webhook
In your GitHub repo/org settings, add a webhook pointing to:
https://your-host:9300/webhooks/github- Content type: `application/json`
- [sensitive field redacted]`GITHUB_WEBHOOK_SECRET`
- Events: `Issues`
Trigger a Task via Label
Add the `swarm` label to any GitHub issue. The daemon will:
1. Ingest the issue on next poll (or immediately via webhook)
2. Create a workspace, clone the repo
3. Render the appropriate workflow template
4. Dispatch to an available agent
5. Update the issue with a completion comment + label change
Manual Task via API
curl -X POST http://localhost:9301/work-items \
-H "Content-Type: application/json" \
-d '{
"title": "Refactor auth module",
"description": "Clean up the JWT validation logic in src/auth/",
"source": "manual",
"labels": ["refactor"],
"gitRepoUrl": "https://github.com/Diomandeee/myapp",
"gitBranch": "main"
}'Force a Specific Agent
Add labels to route to a specific agent:
- `agent:claude` — use Claude Code
- `agent:codex` — use Codex CLI
- `agent:gemini` — use Gemini CLI
Or set `agentKind` in the API payload.
Check Status
# List all running tasks
curl http://localhost:9301/work-items?status=running
# Get Prometheus metrics
curl http://localhost:9301/metrics
# Check a specific item
curl http://localhost:9301/work-items/<uuid>---
API Reference
`GET /work-items`
List work items, optionally filtered by status.
Query params:
- `status` — filter by state (`queued`, `running`, `succeeded`, `failed`, etc.)
- `limit` — max results (default: 50)
- `source` — filter by source kind (`github`, `linear`, `manual`, `machine_queue`)
Response:
[
{
"id": "uuid",
"title": "Fix login bug",
"status": "running",
"agentKind": "claude",
"source": "github",
"externalId": "123",
"url": "https://github.com/...",
"createdAt": "2026-03-17T10:00:00Z",
"updatedAt": "2026-03-17T10:05:00Z"
}
]`POST /work-items`
Manually enqueue a work item.
Body:
{
"title": "string (required)",
"description": "string",
"source": "manual",
"labels": ["string"],
"agentKind": "claude | codex | gemini",
"gitRepoUrl": "string",
"gitBranch": "string",
"priority": 0,
"metadata": {}
}`PATCH /work-items/:id/status`
Manually override work item status (admin use).
Body:
{ "status": "cancelled" }`GET /metrics`
Prometheus exposition format. Key metrics:
| Metric | Type | Description |
|---|---|---|
| `swarm_tasks_total` | Counter | Total tasks by status/agent/source |
| `swarm_active_sessions` | Gauge | Currently running agent sessions |
| `swarm_task_duration_seconds` | Histogram | Task execution time |
| `swarm_queue_depth` | Gauge | Items in queued state |
| `swarm_retries_total` | Counter | Retry events by reason |
`POST /webhooks/github`
GitHub webhook receiver (HMAC-verified). Handles `issues.opened`, `issues.labeled`, `issues.edited`.
`POST /webhooks/linear`
Linear webhook receiver. Handles issue create/update events for watched teams.
---
Workflows
Liquid Markdown templates in `workflows/` define the instructions rendered for each task type. The daemon selects a template based on issue labels (first match wins):
| Template | Trigger Labels | Agent | Approval |
|---|---|---|---|
| `feature-impl.md` | `feature`, `enhancement` | claude | auto-all |
| `bug-fix.md` | `bug`, `fix` | claude | auto-all |
| `refactor.md` | `refactor`, `cleanup` | claude | auto-edit |
| `test-coverage.md` | `test`, `coverage` | claude | auto-all |
| `security-audit.md` | `security`, `audit` | claude | manual |
| `ios-build.md` | `ios`, `xcode`, `build` | claude | auto-all |
| `app-evolution.md` | `evolution`, `evo` | claude | auto-all |
| `default.md` | _(fallback)_ | claude | auto-all |
Writing a Custom Workflow
Create `workflows/my-task.md`:
---
name: My Custom Task
agent: claude
approval_mode: auto-all
max_retries: 2
timeout_seconds: 900
labels: [my-label]
---
# Task: {{ issue.title }}
## Context
Repository: {{ issue.gitRepoUrl }}
Branch: {{ issue.gitBranch | default: "main" }}
Workspace: {{ workspace.path }}
## Issue Description
{{ issue.description }}
{% if issue.labels %}
## Labels
{{ issue.labels | join: ", " }}
{% endif %}
## Instructions
1. Read the codebase in {{ workspace.path }}
2. Implement the requested changes
3. Ensure all tests pass
4. Create a summary of changes made---
Database Schema
`swarm_work_items`
CREATE TABLE swarm_work_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
external_id TEXT, -- GitHub issue number, Linear ID
identifier TEXT UNIQUE, -- e.g. "github/Diomandeee/myapp/123"
title TEXT NOT NULL,
description TEXT,
source_kind TEXT NOT NULL, -- "github" | "linear" | "manual" | "machine_queue"
project TEXT,
priority INTEGER DEFAULT 0,
labels TEXT[] DEFAULT '{}',
status TEXT NOT NULL DEFAULT 'queued',
agent_kind TEXT, -- "claude" | "codex" | "gemini"
session_id TEXT,
workspace_path TEXT,
retry_count INTEGER DEFAULT 0,
max_retries INTEGER DEFAULT 3,
git_repo_url TEXT,
git_branch TEXT DEFAULT 'main',
url TEXT,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);`swarm_transitions`
CREATE TABLE swarm_transitions (
id BIGSERIAL PRIMARY KEY,
work_item_id UUID NOT NULL REFERENCES swarm_work_items(id),
from_status TEXT NOT NULL,
to_status TEXT NOT NULL,
event_type TEXT NOT NULL,
event_data JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT now()
);---
Development
Project Structure
agent-swarm/
├── migrations/ # Supabase DDL (apply once)
├── packages/
│ ├── agent-adapters/ # Claude/Codex/Gemini/SSH adapter interfaces
│ ├── discord/ # Notification formatting
│ ├── logger/ # Structured logger
│ ├── metrics/ # Prometheus client
│ ├── numu-bridge/ # NUMU Bus WebSocket client
│ ├── orbit-bridge/ # Orbit context enrichment
│ ├── prompt-renderer/ # Liquid template engine
│ ├── source-providers/ # GitHub + Linear + Machine Queue
│ ├── state-machine/ # FSM + Supabase store
│ └── workspace-manager/ # Git workspace isolation
├── services/
│ └── swarm-daemon/
│ └── src/
│ ├── index.ts # Bootstrap (servers + loops)
│ ├── orchestrator.ts # Core dispatch loop
│ ├── dispatch.ts # Agent selection logic
│ ├── api-server.ts # REST API + metrics
│ ├── webhook-server.ts# GitHub/Linear receiver
│ └── config.ts # Zod-validated config
├── workflows/ # 13 Liquid task templates
├── .env.example
└── package.json # Bun workspaces manifestCommon Commands
# Install all workspace dependencies
bun install
# Run daemon with hot reload
bun run dev
# Type check across all packages
bunx tsc --noEmit
# Run tests
bun test
# Build production binary
bun build services/swarm-daemon/src/index.ts --outdir dist --target bun
# Lint
bunx eslint packages/ services/Adding a New Source Provider
1. Create `packages/source-providers/src/my-source.ts`
2. Implement the `SourceProvider` interface:
export interface SourceProvider {
initialize(): Promise<void>
poll?(): Promise<SourceEvent[]>
handleWebhook?(payload: unknown, headers: Headers): Promise<SourceEvent[]>
updateStatus(item: StoredWorkItem, status: WorkItemStatus): Promise<void>
postComment(item: StoredWorkItem, body: string): Promise<void>
getWorkItem(externalId: string): Promise<WorkItem | null>
}3. Register in `services/swarm-daemon/src/index.ts`
Adding a New Agent Adapter
1. Create `packages/agent-adapters/src/my-agent.ts`
2. Implement the `AgentAdapter` interface:
export interface AgentAdapter {
startSession(config: SessionConfig): Promise<string>
sendPrompt(sessionId: string, prompt: string): AsyncIterableIterator<AgentEvent>
respondToApproval(sessionId: string, approved: boolean): Promise<void>
interrupt(sessionId: string): Promise<void>
endSession(sessionId: string): Promise<void>
isAlive(sessionId: string): boolean
shutdown(): Promise<void>
}3. Add to the factory in `packages/agent-adapters/src/factory.ts`
---
Integration with the Mesh
Agent Swarm integrates with the broader mesh infrastructure:
- NUMU Bus (`:7890`) — Publishes `swarm.task.*` events for cross-system visibility
- Supabase (`aaqbofotpchgpyuohmmz`) — Shares the same project with other mesh services
- Orbit — Enriches task prompts with project context from the Orbit graph
- Discord — Posts to ops channel on task completion / failure
- Prometheus — Metrics scraped by Grafana dashboard (`:3000`)
- Machine Queue — Accepts Spore dispatch events from `swarm_machine_queue` table
LaunchAgent (macOS)
The daemon runs as a macOS LaunchAgent (`com.swarm.daemon`) on Mac1:
# Load
launchctl load [home-path]
# Status
launchctl list | grep swarm
# Logs
tail -f [home-path]---
License
Private — internal tooling for the OpenClaw / Diomandeee portfolio.
Promotion Decision
Attach run IDs, datasets, metrics, and reproduction commands.
Source Anchor
projects/agent-swarm/README.md
Detected Structure
Method · Evaluation · References · Code Anchors · Architecture