Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 63 additions & 62 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

175 changes: 175 additions & 0 deletions docs/ARCHITECTURE_DECISIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Multi-Agent Coordination: Architecture Decisions

This document compares approaches to multi-agent coordination and explains when to use each.

## Two Approaches

### 1. Subagents (Hierarchical)

Spawn child agents from a parent, collect results, continue.

```
┌─────────┐
│ Parent │
└────┬────┘
│ spawn
┌───────┼───────┐
▼ ▼ ▼
┌───────┐┌───────┐┌───────┐
│ Child ││ Child ││ Child │
└───────┘└───────┘└───────┘
│ │ │
└───────┼───────┘
▼ return
┌─────────┐
│ Parent │
└─────────┘
```

**Characteristics:**
- Tree topology (parent orchestrates)
- Synchronous - parent waits for children
- Ephemeral - children die after returning
- State held by parent
- No peer-to-peer communication

### 2. Agent-Relay (Mesh)

Persistent agents communicate via message passing.

```
┌───────┐ ┌───────┐
│ Agent │◄───►│ Agent │
└───┬───┘ └───┬───┘
│ │
└──────┬──────┘
┌─────────────┐
│ Daemon │ ← routes messages
│ (router) │ ← persists history
└─────────────┘
┌──────┴──────┐
▼ ▼
┌───────┐ ┌───────┐
│ Agent │◄───►│ Agent │
└───────┘ └───────┘
```

**Characteristics:**
- Mesh topology (any-to-any)
- Asynchronous - fire and respond
- Persistent - agents live across tasks
- Distributed state + message log
- Direct peer communication

## Decision Matrix

| Requirement | Subagents | Agent-Relay |
|-------------|:---------:|:-----------:|
| Simple one-shot tasks | ✅ | ⚠️ overkill |
| Clear parent/child hierarchy | ✅ | ✅ |
| No infrastructure to manage | ✅ | ❌ |
| Agents debate/negotiate | ❌ | ✅ |
| Long-running agents | ❌ | ✅ |
| Mix different AI providers | ⚠️ limited | ✅ |
| Audit trail / replay | ❌ | ✅ |
| External observability | ❌ | ✅ |
| Horizontal scaling | ❌ | ✅ |
| Resume after crash | ❌ | ✅ |

## When to Use Subagents

Choose subagents when:

1. **Tasks are independent** - Fan out, collect, aggregate
2. **Hierarchy is natural** - One coordinator, many workers
3. **No persistence needed** - Results matter, not the conversation
4. **Same provider** - All agents are Claude (or same SDK)
5. **Simplicity wins** - Fewer moving parts

**Example use cases:**
- Parallel code analysis (security, perf, style)
- Research tasks with multiple queries
- Map-reduce style workloads
- One-time batch processing

```typescript
// Subagent pattern
const [security, perf, style] = await Promise.all([
analyzeSecurityAgent(code),
analyzePerfAgent(code),
analyzeStyleAgent(code),
]);
return summarize(security, perf, style);
```

## When to Use Agent-Relay

Choose agent-relay when:

1. **Agents need to discuss** - Back-and-forth negotiation
2. **Persistence matters** - Audit, debug, replay conversations
3. **Mixed ecosystem** - Claude + Codex + Gemini + custom bots
4. **External integration** - Dashboard, Slack, webhooks
5. **Long-running sessions** - Agents stay alive, handle multiple tasks
6. **Decoupling** - Add/remove agents without code changes

**Example use cases:**
- Collaborative document editing
- Multi-agent game playing
- Continuous monitoring systems
- Team simulations with distinct personas
- Human-in-the-loop workflows

```typescript
// Agent-relay pattern
const daemon = new Daemon({ socketPath, storagePath });
await daemon.start();

// Agents live independently, communicate async
const architect = new RelayClient({ name: 'Architect', socketPath });
const developer = new RelayClient({ name: 'Developer', socketPath });
const reviewer = new RelayClient({ name: 'Reviewer', socketPath });

// They message each other directly
architect.send({ to: 'Developer', body: 'Implement auth module' });
// Developer works, then...
developer.send({ to: 'Reviewer', body: 'Ready for review' });
// Reviewer responds to Developer directly
```

## Hybrid Approach

You can combine both:

```
┌─────────────────────────────────────┐
│ Orchestrator │
│ (agent-relay) │
└──────┬──────────────┬───────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Team Lead │ │ Team Lead │
│ (relay) │ │ (relay) │
└──────┬──────┘ └──────┬──────┘
│ │
┌───┴───┐ ┌───┴───┐
▼ ▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│Sub 1│ │Sub 2│ │Sub 3│ │Sub 4│ ← subagents
└─────┘ └─────┘ └─────┘ └─────┘
```

- **Agent-relay** for cross-team coordination
- **Subagents** for parallelizable work within a team

## Summary

| Approach | Mental Model | Best For |
|----------|--------------|----------|
| Subagents | Function calls | Hierarchical, stateless, one-shot |
| Agent-Relay | Microservices | Mesh, persistent, observable |

**Rule of thumb:** Start with subagents. Move to agent-relay when you need persistence, observability, or peer-to-peer communication.
42 changes: 42 additions & 0 deletions examples/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Agent Relay Configuration
# Copy this file to .env in your project root

# ============================================
# Data Storage
# ============================================

# Base directory for all agent-relay data
# Default: ~/.agent-relay (or XDG_DATA_HOME/agent-relay)
# AGENT_RELAY_DATA_DIR=/path/to/data

# Storage backend type (sqlite, memory)
# Default: sqlite
# AGENT_RELAY_STORAGE_TYPE=sqlite

# Custom SQLite database path
# Default: <data_dir>/messages.sqlite
# AGENT_RELAY_STORAGE_PATH=/path/to/messages.sqlite

# SQLite driver preference (better-sqlite3, node)
# Default: better-sqlite3 (falls back to node:sqlite if unavailable)
# AGENT_RELAY_SQLITE_DRIVER=better-sqlite3

# ============================================
# Dashboard
# ============================================

# Web dashboard port
# Default: 3888
# AGENT_RELAY_DASHBOARD_PORT=3888

# ============================================
# Agent Identity
# ============================================

# Default agent name (used by hooks)
# If not set, a random name is generated
# AGENT_RELAY_NAME=MyAgent

# Custom inbox directory for file-based messaging
# Default: <data_dir>/inbox
# AGENT_RELAY_INBOX_DIR=/path/to/inbox
71 changes: 71 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Agent Relay Configuration Examples

This folder contains examples for configuring agent-relay in different environments.

## Configuration Files

| File | Description |
|------|-------------|
| `.env.example` | Environment variables for dotenv configuration |
| `cli-usage.sh` | CLI command examples and options |
| `programmatic-usage.ts` | Using agent-relay as a Node.js library |
| `docker-compose.yml` | Docker Compose setup for containerized deployment |
| `agent-relay.service` | Systemd service file for Linux servers |
| `team-config.json` | Team configuration with multiple agents |

## Usage Examples

| Directory | Description |
|-----------|-------------|
| `basic-chat/` | Simple two-agent chat example |
| `collaborative-task/` | Multi-agent collaboration workflow |

## Quick Start

### Environment Variables

Copy `.env.example` to your project root as `.env`:

```bash
cp examples/.env.example .env
```

Edit the values as needed. Agent-relay uses dotenv to load these automatically.

### CLI Configuration

All configuration can also be passed via CLI flags:

```bash
# Custom port and agent name
agent-relay up --port 4000
agent-relay -n MyAgent claude
```

### Programmatic Configuration

```typescript
import { Daemon, getProjectPaths } from 'agent-relay';

const paths = getProjectPaths();
const daemon = new Daemon({
socketPath: paths.socketPath,
storagePath: paths.dbPath,
});
```

## Configuration Priority

1. CLI flags (highest priority)
2. Environment variables
3. Default values (lowest priority)

## Multi-Project Setup

Agent-relay automatically isolates data per project based on the project root directory. Each project gets its own:

- SQLite database
- Unix socket
- Message history

Projects are identified by a hash of their root path (detected via `.git`, `package.json`, etc.).
43 changes: 43 additions & 0 deletions examples/agent-relay.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Systemd service file for agent-relay daemon
#
# Installation:
# 1. Copy this file to /etc/systemd/system/agent-relay.service
# 2. Edit the paths and user as needed
# 3. Run: sudo systemctl daemon-reload
# 4. Run: sudo systemctl enable agent-relay
# 5. Run: sudo systemctl start agent-relay

[Unit]
Description=Agent Relay Daemon
After=network.target

[Service]
Type=simple
User=your-username
Group=your-username

# Working directory (your project root)
WorkingDirectory=/path/to/your/project

# Environment configuration
Environment=NODE_ENV=production
Environment=AGENT_RELAY_DATA_DIR=/var/lib/agent-relay
Environment=AGENT_RELAY_DASHBOARD_PORT=3888

# Start the daemon
ExecStart=/usr/bin/npx agent-relay up --port 3888

# Graceful shutdown
ExecStop=/usr/bin/npx agent-relay down

# Restart on failure
Restart=on-failure
RestartSec=5

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=agent-relay

[Install]
WantedBy=multi-user.target
Loading