Swarm Mail
Event-sourced messaging system for multi-agent coordination
_.------._
.' .--. '. 🐝
/ .' '. \ 🐝
| / __ \ | 🐝
🐝 | | ( ) | | 🐝
🐝 _ _ | | |__| | |
( \/ ) \ '. .' / 🐝
🐝 ____/ \____ '. '----' .'
/ \ / \ '-._____.-' 🐝
/ () \ / () \
| /\ || /\ | ███████╗██╗ ██╗ █████╗ ██████╗ ███╗ ███╗
| /__\ || /__\ | ██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ████║
\ / \ / ███████╗██║ █╗ ██║███████║██████╔╝██╔████╔██║
🐝 '----' '----' ╚════██║██║███╗██║██╔══██║██╔══██╗██║╚██╔╝██║
███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║
🐝 ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
🐝
🐝 ███╗ ███╗ █████╗ ██╗██╗
████╗ ████║██╔══██╗██║██║ 🐝
🐝 ██╔████╔██║███████║██║██║
██║╚██╔╝██║██╔══██║██║██║ 🐝
🐝 🐝 ██║ ╚═╝ ██║██║ ██║██║███████╗
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝ 🐝
🐝
⚡ Actor-Model Primitives for Agent Coordination ⚡Overview
Swarm Mail is an embedded, event-sourced messaging system for multi-agent coordination. Built on Durable Streams primitives with Effect-TS, it provides actor-model communication without external server dependencies.
What Problem Does It Solve?
When multiple AI agents work on the same codebase in parallel, they need to:
- Coordinate file access - prevent edit conflicts via reservations
- Exchange messages - async communication for status, blockers, handoffs
- Request/response - synchronous-style RPC for data queries
- Resume after crashes - positioned consumption with checkpointing
- Audit all actions - full event history for debugging and learning
Traditional solutions require external servers (Redis, Kafka, NATS). Swarm Mail is embedded - just libSQL (embedded SQLite) + Effect-TS.
Key Features
- ✅ Local-first - No external servers, no network dependencies
- ✅ Event-sourced - Full audit trail of all agent actions
- ✅ Resumable - Checkpointed cursors for exactly-once processing
- ✅ Type-safe - Effect-TS with full type inference
- ✅ Actor-model - Mailboxes, envelopes, distributed promises
- ✅ File safety - CAS-based locks for mutual exclusion
- ✅ Semantic memory - Vector embeddings for persistent agent learning
Architecture Stack
Swarm Mail is built in 3 tiers - primitives, patterns, and coordination layers.
┌─────────────────────────────────────────────────────────────────────────────┐
│ SWARM MAIL STACK │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ TIER 3: COORDINATION │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ask<Req, Res>() - Request/Response over Streams (RPC-style) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ TIER 2: PATTERNS ▼ │
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ DurableMailbox │ │ DurableLock │ │
│ │ Actor Inbox + Reply │ │ CAS Mutual Exclusion │ │
│ └───────────────────────┘ └───────────────────────┘ │
│ │ │ │
│ TIER 1: PRIMITIVES ▼ │
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ DurableCursor │ │ DurableDeferred │ │
│ │ Checkpointed Reader │ │ Distributed Promise │ │
│ └───────────────────────┘ └───────────────────────┘ │
│ │ │ │
│ MEMORY ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Semantic Memory - Vector embeddings (pgvector) + Ollama │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ STORAGE ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ libSQL (Embedded SQLite) + Migrations │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘Quick Start
Installation
npm install swarm-mailInitialize Agent
import { Effect } from "effect";
import { DurableAskLive } from "swarm-mail/streams/effect/layers";
import {
initSwarmAgent,
sendSwarmMessage,
reserveSwarmFiles,
} from "swarm-mail";
const program = Effect.gen(function* () {
// 1. Initialize agent
const { agentName, projectKey } = yield* Effect.promise(() =>
initSwarmAgent({
projectPath: "/abs/path",
taskDescription: "bd-123.2: Auth service",
}),
);
console.log(`Agent: ${agentName}`); // e.g., "DarkStone"
// 2. Reserve files
const { granted, conflicts } = yield* Effect.promise(() =>
reserveSwarmFiles({
projectPath: projectKey,
agentName,
paths: ["src/auth/**"],
reason: "bd-123.2: Auth service",
exclusive: true,
}),
);
if (conflicts.length > 0) {
console.warn("File conflicts detected:", conflicts);
}
// 3. Report progress
yield* Effect.promise(() =>
sendSwarmMessage({
projectPath: projectKey,
fromAgent: agentName,
toAgents: ["coordinator"],
subject: "Progress: bd-123.2",
body: "Reserved files, starting work",
threadId: "bd-123",
importance: "normal",
}),
);
// 4. Do work...
yield* Effect.sleep("5 seconds");
// 5. Report completion
yield* Effect.promise(() =>
sendSwarmMessage({
projectPath: projectKey,
fromAgent: agentName,
toAgents: ["coordinator"],
subject: "Complete: bd-123.2",
body: "Auth service implemented",
threadId: "bd-123",
importance: "high",
}),
);
});
Effect.runPromise(program.pipe(Effect.provide(DurableAskLive)));Event Sourcing Architecture
Swarm Mail is fully event-sourced. All state changes are events first, materialized views second.
Event Flow
┌─────────────┐
│ Action │ Agent calls API (e.g., sendMessage)
└──────┬──────┘
│
▼
┌─────────────┐
│ Create │ createEvent("message_sent", {...})
│ Event │ Returns: { type, timestamp, ...payload }
└──────┬──────┘
│
▼
┌─────────────┐
│ Append │ appendEvent() → INSERT INTO events
│ to Log │ Gets auto-increment id + sequence
└──────┬──────┘
│
▼
┌─────────────┐
│ Update │ updateProjections() triggers based on event type
│ Views │ - message_sent → INSERT messages, UPDATE agent read status
└──────┬──────┘ - file_reserved → INSERT reservations
│ - message_read → UPDATE message read flags
▼
┌─────────────┐
│ Query │ getInbox(), getMessage(), getReservations()
│ Views │ Fast queries on materialized tables
└─────────────┘Event Types
| Event Type | Trigger | Projections Updated |
|---|---|---|
agent_registered | Agent init | agents table |
message_sent | sendMessage() | messages, message_recipients |
message_read | readMessage(markAsRead=true) | messages.read_by JSONB |
message_acked | acknowledgeMessage() | messages.acked_by JSONB |
file_reserved | reserveFiles() | reservations |
file_released | releaseFiles() | reservations (DELETE expired) |
Why Event Sourcing?
- ✅ Full audit trail - every agent action is logged forever
- ✅ Time travel - replay events to reconstruct past state
- ✅ Debugging - when agents conflict, trace exact sequence of events
- ✅ Learning - analyze event patterns to improve swarm strategies
- ✅ Resumability - cursors checkpoint position, replay from there on crash
Comparison to Agent Mail
Swarm Mail is inspired by Agent Mail (SST's multi-agent coordination layer) but built from scratch with different trade-offs.
| Aspect | Agent Mail (SST) | Swarm Mail (This Plugin) |
|---|---|---|
| Architecture | MCP server (external process) | Embedded (libSQL in-process) |
| Storage | SQLite file | libSQL (SQLite-compatible) |
| Dependencies | Requires MCP server running | Zero external deps, just npm install |
| Effect-TS | No | Yes (full Effect integration) |
| Event Sourcing | No (CRUD operations) | Yes (append-only event log) |
| Cursors | No (queries are one-shot) | Yes (resumable positioned consumption) |
| Distributed Promises | No | Yes (DurableDeferred) |
| Type Safety | MCP JSON-RPC (strings) | Full TypeScript + Zod validation |
| Local-First | Requires server | True local-first (no network) |
| Learning System | No | Yes (eval records, pattern maturity) |
When to Use Agent Mail:
- You're already using OpenCode with MCP infrastructure
- You need cross-project coordination (multiple repos)
- You want battle-tested SST ecosystem integration
When to Use Swarm Mail:
- You want embedded, zero-config coordination
- You're building Effect-TS applications
- You need event sourcing and audit trails
- You want resumable cursors for exactly-once semantics
- You're using this plugin's learning/swarm features
Database Schema
Swarm Mail uses libSQL (SQLite-compatible embedded database). Schema is managed via migrations.
Core Tables
events - Append-Only Event Log
CREATE TABLE events (
id SERIAL PRIMARY KEY,
sequence SERIAL, -- Auto-increment, never gaps
type TEXT NOT NULL, -- Event type (message_sent, file_reserved, etc.)
timestamp BIGINT NOT NULL, -- Unix timestamp in ms
payload JSONB NOT NULL, -- Event-specific data
project_key TEXT NOT NULL,
agent_name TEXT
);
CREATE INDEX idx_events_sequence ON events(sequence);
CREATE INDEX idx_events_type ON events(type);
CREATE INDEX idx_events_project ON events(project_key);All state changes flow through this table. Other tables are materialized views.
See the Primitives documentation for detailed schema of each primitive's tables.
Effect-TS Integration
Swarm Mail is built with Effect - a functional effect system for TypeScript.
Why Effect?
- ✅ Composability - combine primitives into patterns without callback hell
- ✅ Type inference - full TypeScript inference for errors and dependencies
- ✅ Retries - built-in retry schedules with exponential backoff
- ✅ Resource safety -
Effect.ensuringguarantees cleanup (like try/finally) - ✅ Dependency injection - Layers for services, no globals
Services and Layers
Each primitive is an Effect Service (via Context.Tag):
// Define service interface
export class DurableCursor extends Context.Tag("DurableCursor")<
DurableCursor,
DurableCursorService
>() {}
// Implement service
export const DurableCursorLive = DurableCursor.of({
create: createCursorImpl,
});
// Use service in Effect
const program = Effect.gen(function* () {
const cursor = yield* DurableCursor; // Service dependency
const consumer = yield* cursor.create({ stream: "..." });
// ...
});
// Provide service implementation
Effect.runPromise(program.pipe(Effect.provide(DurableCursorLive)));Layers compose - higher-level services depend on lower-level ones:
// DurableMailbox depends on DurableCursor
const MailboxLayer = Layer.mergeAll(CursorLayer, DurableMailboxLive);
// Ask pattern depends on Mailbox + Deferred
export const DurableAskLive = Layer.mergeAll(DurableDeferredLive, MailboxLayer);
// Use in program
const program = Effect.gen(function* () {
const mailbox = yield* DurableMailbox;
const response = yield* ask({ mailbox, to: "worker-2", payload: {...} });
});
Effect.runPromise(program.pipe(Effect.provide(DurableAskLive)));Error Handling
Effect has typed errors - errors are part of the Effect signature:
// Effect<Success, Error, Requirements>
type MyEffect = Effect.Effect<number, LockError | TimeoutError, DurableLock>;
// Handle specific error types
yield *
lockService.acquire("resource").pipe(
Effect.catchTag("LockTimeout", () => Effect.succeed(null)),
Effect.catchTag("LockContention", () => Effect.fail(new MyError())),
);Credits and Inspiration
- Kyle Matthews (Founder/CPO @ Electric SQL) - The Durable Streams protocol and the insight that composable primitives can build powerful actor systems. Original tweet
- Agent Mail - Multi-agent coordination layer for OpenCode (SST). Swarm Mail's API surface is heavily inspired by Agent Mail's design.
- Electric SQL - Real-time sync engine for Postgres. The cursor pattern and positioned consumption ideas come from Electric's sync protocol.
- Effect-TS - Functional effect system powering the implementation. Effect's composability and type safety make the primitives ergonomic.
What's Next?
Swarm Mail is production-ready but has room for optimization:
Performance Improvements
- Connection pooling - reuse libSQL connections instead of opening new ones
- Batch inserts - batch multiple events into one transaction
- Index tuning - add covering indexes for hot queries
Features
- Message priorities - priority queue for urgent messages
- Dead letter queue - failed messages go to DLQ for retry
- Message TTL - auto-expire old messages
- Broadcast channels - pub/sub for topic-based routing
- Saga pattern - distributed transactions with compensations
Developer Experience
- DevTools UI - web UI for inspecting events, cursors, locks
- CLI tools -
swarm-mail inspect,swarm-mail replay - Metrics - Prometheus metrics for message latency, lock contention
- Tracing - OpenTelemetry spans for distributed tracing
* * 🐝 * *
* * * * *
🐝 SHIP IT 🐝
* * * * *
* * 🐝 * *