Complete Agent Mail documentation (bd-nl8z)

- Add AGENT_MAIL_QUICKSTART.md: 5-minute setup guide
- Add examples/python-agent/AGENT_MAIL_EXAMPLE.md: working code examples
- Add examples/python-agent/agent_with_mail.py: runnable multi-agent demo
- Update README.md: add Agent Mail to features and docs index
- Update AGENTS.md: enhance with quickstart/example references
- Update examples README: add Agent Mail example to index

Amp-Thread-ID: https://ampcode.com/threads/T-5d5e711d-7b5f-42ca-b75a-5b6cd843ad98
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Steve Yegge
2025-11-08 02:36:27 -08:00
parent 3b39672e76
commit 37f9d3610d
7 changed files with 1190 additions and 1 deletions

View File

@@ -262,7 +262,7 @@ bd close bd-42 "Done" # Updates via git sync
- ✅ Infrequent updates (low collision risk)
- ✅ Simplicity preferred over latency
See [docs/AGENT_MAIL.md](docs/AGENT_MAIL.md) for complete setup, troubleshooting, and architecture details.
See [docs/AGENT_MAIL_QUICKSTART.md](docs/AGENT_MAIL_QUICKSTART.md) for 5-minute setup, or [docs/AGENT_MAIL.md](docs/AGENT_MAIL.md) for complete documentation. Example code in [examples/python-agent/AGENT_MAIL_EXAMPLE.md](examples/python-agent/AGENT_MAIL_EXAMPLE.md).
### Issue Types

View File

@@ -58,6 +58,7 @@ Agents report that they enjoy working with Beads, and they will use it spontaneo
- 🤖 **Agent-friendly** - `--json` flags for programmatic integration
- 📦 **Git-versioned** - JSONL records stored in git, synced across machines
- 🌍 **Distributed by design** - Agents on multiple machines share one logical database via git
- 🚀 **Optional Agent Mail** - Real-time multi-agent coordination (<100ms vs 2-5s git sync)
- 🔐 **Protected branch support** - Works with GitHub/GitLab protected branches via separate sync branch
- 🏗️ **Extensible** - Add your own tables to the SQLite database
- 🔍 **Multi-project isolation** - Each project gets its own database, auto-discovered by directory
@@ -737,6 +738,8 @@ For advanced usage, see:
- **[README.md](README.md)** - You are here! Core features and quick start
- **[docs/INSTALLING.md](docs/INSTALLING.md)** - Complete installation guide for all platforms
- **[docs/QUICKSTART.md](docs/QUICKSTART.md)** - Interactive tutorial (`bd quickstart`)
- **[docs/AGENT_MAIL_QUICKSTART.md](docs/AGENT_MAIL_QUICKSTART.md)** - 5-minute Agent Mail setup guide
- **[docs/AGENT_MAIL.md](docs/AGENT_MAIL.md)** - Complete Agent Mail integration guide
- **[docs/MULTI_REPO_MIGRATION.md](docs/MULTI_REPO_MIGRATION.md)** - Multi-repo workflow guide (OSS, teams, multi-phase)
- **[docs/MULTI_REPO_AGENTS.md](docs/MULTI_REPO_AGENTS.md)** - Multi-repo patterns for AI agents
- **[docs/FAQ.md](docs/FAQ.md)** - Frequently asked questions

View File

@@ -0,0 +1,478 @@
# Agent Mail Quick Start Guide
Get started with Agent Mail for multi-agent bd coordination in 5 minutes.
## What is Agent Mail?
Agent Mail is an **optional** coordination layer for bd that reduces latency from 2-5 seconds (git sync) to <100ms (HTTP API) and prevents work collisions through file reservations.
**When to use it:**
- ✅ Multiple AI agents working concurrently
- ✅ Frequent status updates (high collision risk)
- ✅ Real-time coordination needed
**When to skip it:**
- ❌ Single agent workflows
- ❌ Infrequent updates (low collision risk)
- ❌ Simplicity preferred over latency
## 5-Minute Setup
### Step 1: Install Agent Mail Server (30 seconds)
```bash
git clone https://github.com/Dicklesworthstone/mcp_agent_mail.git ~/mcp_agent_mail
cd ~/mcp_agent_mail
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
```
### Step 2: Start the Server (5 seconds)
```bash
python -m mcp_agent_mail.cli serve-http
# ✅ Server running on http://127.0.0.1:8765
```
Leave this terminal open. Open a new terminal for Step 3.
### Step 3: Configure Your Agent (10 seconds)
```bash
# Set environment variables
export BEADS_AGENT_MAIL_URL=http://127.0.0.1:8765
export BEADS_AGENT_NAME=my-agent
export BEADS_PROJECT_ID=my-project
```
### Step 4: Use bd Normally (30 seconds)
```bash
# Find ready work
bd ready
# Claim an issue
bd update bd-42 --status in_progress
# ✅ Reserved bd-42 for my-agent in <100ms
# Complete work
bd close bd-42 "Done"
# ✅ Reservation released automatically
```
**That's it!** bd now uses Agent Mail for coordination.
### Step 5: Test Multi-Agent (1 minute)
Open a second terminal:
```bash
# Terminal 2 - Different agent
export BEADS_AGENT_MAIL_URL=http://127.0.0.1:8765
export BEADS_AGENT_NAME=other-agent
export BEADS_PROJECT_ID=my-project
# Try claiming same issue
bd update bd-42 --status in_progress
# ❌ Error: bd-42 already reserved by my-agent
```
**Success!** Agent Mail prevented collision.
## Common Use Cases
### Use Case 1: Claude Desktop + Command Line Agent
**Terminal 1 - Agent Mail Server:**
```bash
cd ~/mcp_agent_mail
source .venv/bin/activate
python -m mcp_agent_mail.cli serve-http
```
**Terminal 2 - Command Line:**
```bash
export BEADS_AGENT_MAIL_URL=http://127.0.0.1:8765
export BEADS_AGENT_NAME=cli-user
export BEADS_PROJECT_ID=my-project
bd ready
bd update bd-100 --status in_progress
```
**Claude Desktop:**
```
# In Claude's MCP settings, add env vars:
{
"beads": {
"command": "beads-mcp",
"env": {
"BEADS_AGENT_MAIL_URL": "http://127.0.0.1:8765",
"BEADS_AGENT_NAME": "claude",
"BEADS_PROJECT_ID": "my-project"
}
}
}
```
Now Claude and your command line won't step on each other!
### Use Case 2: Multiple Python Agents
**Terminal 1 - Server:**
```bash
cd ~/mcp_agent_mail
source .venv/bin/activate
python -m mcp_agent_mail.cli serve-http
```
**Terminal 2 - Agent A:**
```bash
cd ~/myproject/examples/python-agent
./agent_with_mail.py \
--agent-name alice \
--project-id myproject \
--agent-mail-url http://127.0.0.1:8765
```
**Terminal 3 - Agent B:**
```bash
cd ~/myproject/examples/python-agent
./agent_with_mail.py \
--agent-name bob \
--project-id myproject \
--agent-mail-url http://127.0.0.1:8765
```
Watch them coordinate in real-time!
### Use Case 3: Team Workflow
**Shared Server (runs on dev machine):**
```bash
# Machine 192.168.1.100
python -m mcp_agent_mail.cli serve-http --host 0.0.0.0
```
**Team Member 1:**
```bash
export BEADS_AGENT_MAIL_URL=http://192.168.1.100:8765
export BEADS_AGENT_NAME=alice
export BEADS_PROJECT_ID=team-project
bd ready
```
**Team Member 2:**
```bash
export BEADS_AGENT_MAIL_URL=http://192.168.1.100:8765
export BEADS_AGENT_NAME=bob
export BEADS_PROJECT_ID=team-project
bd ready
```
Entire team shares one coordination server!
### Use Case 4: CI/CD Pipeline
**GitHub Actions Example:**
```yaml
name: AI Agent Workflow
on: [push]
jobs:
agent-work:
runs-on: ubuntu-latest
services:
agent-mail:
image: ghcr.io/dicklesworthstone/mcp_agent_mail:latest
ports:
- 8765:8765
strategy:
matrix:
agent: [agent-1, agent-2, agent-3]
steps:
- uses: actions/checkout@v4
- name: Run agent
env:
BEADS_AGENT_MAIL_URL: http://localhost:8765
BEADS_AGENT_NAME: ${{ matrix.agent }}
BEADS_PROJECT_ID: ${{ github.repository }}
run: |
bd ready | head -1 | xargs -I {} bd update {} --status in_progress
# ... do work ...
bd close {} "Completed by CI"
```
Three agents run in parallel without collisions!
## Verification Checklist
After setup, verify everything works:
**✅ Server is running:**
```bash
curl http://127.0.0.1:8765/health
# Expected: {"status": "healthy"}
```
**✅ Environment variables are set:**
```bash
echo $BEADS_AGENT_MAIL_URL
# Expected: http://127.0.0.1:8765
echo $BEADS_AGENT_NAME
# Expected: my-agent
echo $BEADS_PROJECT_ID
# Expected: my-project
```
**✅ bd sees Agent Mail:**
```bash
bd info --json | grep agent_mail
# Expected: JSON with agent_mail config
```
**✅ Reservations work:**
```bash
# Terminal 1
bd update bd-test --status in_progress
# Expected: Success
# Terminal 2 (different agent)
export BEADS_AGENT_NAME=other-agent
bd update bd-test --status in_progress
# Expected: Error - reservation conflict
```
**✅ Graceful degradation works:**
```bash
# Stop server (Ctrl+C in server terminal)
# Try bd command
bd ready
# Expected: Warning about Agent Mail unavailable, but command succeeds
```
## Troubleshooting
### Problem: "Agent Mail unavailable" warnings
**Symptoms:**
```
WARN Agent Mail unavailable, falling back to git-only mode
```
**Quick Fix:**
1. Check server is running: `curl http://127.0.0.1:8765/health`
2. Verify URL is correct: `echo $BEADS_AGENT_MAIL_URL`
3. Restart server if needed
### Problem: Agents don't see each other's reservations
**Cause:** Different `BEADS_PROJECT_ID` values
**Quick Fix:**
```bash
# All agents MUST use same project ID!
export BEADS_PROJECT_ID=same-project-name
```
### Problem: Reservation stuck after agent crashes
**Quick Fix:**
```bash
# Option 1: Release via API
curl -X DELETE http://127.0.0.1:8765/api/reservations/bd-stuck
# Option 2: Restart server (clears all reservations)
pkill -f mcp_agent_mail
python -m mcp_agent_mail.cli serve-http
```
### Problem: Port 8765 already in use
**Quick Fix:**
```bash
# Find what's using port
lsof -i :8765 # macOS/Linux
netstat -ano | findstr :8765 # Windows
# Kill old server
pkill -f mcp_agent_mail
# Or use different port
python -m mcp_agent_mail.cli serve-http --port 8766
export BEADS_AGENT_MAIL_URL=http://127.0.0.1:8766
```
## Monitoring
### Web UI
View all reservations in real-time:
```bash
open http://127.0.0.1:8765/mail
```
### API
Check reservations programmatically:
```bash
# List all reservations
curl http://127.0.0.1:8765/api/reservations | jq
# Check specific reservation
curl http://127.0.0.1:8765/api/reservations/bd-42 | jq
# Release reservation manually
curl -X DELETE http://127.0.0.1:8765/api/reservations/bd-42
```
### Logs
Agent Mail logs to stdout. Redirect to file if needed:
```bash
python -m mcp_agent_mail.cli serve-http > agent-mail.log 2>&1 &
tail -f agent-mail.log
```
## Best Practices
### 1. Use Descriptive Agent Names
**Bad:**
```bash
export BEADS_AGENT_NAME=agent1
export BEADS_AGENT_NAME=agent2
```
**Good:**
```bash
export BEADS_AGENT_NAME=claude-frontend
export BEADS_AGENT_NAME=gpt4-backend
export BEADS_AGENT_NAME=alice-laptop
```
Makes debugging much easier!
### 2. Set Environment Variables Globally
**Option 1: Shell Profile**
```bash
# Add to ~/.bashrc or ~/.zshrc
export BEADS_AGENT_MAIL_URL=http://127.0.0.1:8765
export BEADS_AGENT_NAME=$(whoami)-$(hostname)
export BEADS_PROJECT_ID=$(basename $(pwd))
```
**Option 2: Project Config**
```bash
# .env file in project root
BEADS_AGENT_MAIL_URL=http://127.0.0.1:8765
BEADS_AGENT_NAME=my-agent
BEADS_PROJECT_ID=my-project
# Load in scripts
source .env
```
### 3. Use Same Project ID Across Team
Create a shared config:
```bash
# team-config.sh
export BEADS_PROJECT_ID=our-team-project
export BEADS_AGENT_MAIL_URL=http://agent-mail.internal:8765
# Each team member sources it
source team-config.sh
export BEADS_AGENT_NAME=alice # Only this differs per person
```
### 4. Monitor Reservations in Long-Running Agents
```python
# Check reservation health periodically
import requests
def check_reservations():
resp = requests.get(f"{agent_mail_url}/api/reservations")
my_reservations = [r for r in resp.json() if r["agent_id"] == agent_name]
for res in my_reservations:
# Release if work completed
if is_done(res["resource_id"]):
requests.delete(f"{agent_mail_url}/api/reservations/{res['resource_id']}")
```
### 5. Handle Graceful Degradation
Always assume Agent Mail might be unavailable:
```python
try:
bd_update(issue_id, status="in_progress")
except ReservationConflict:
# Expected - try different issue
pass
except Exception:
# Agent Mail down - falls back to git
# Continue normally
pass
```
## Next Steps
1. **Read the full guide**: [AGENT_MAIL.md](AGENT_MAIL.md)
2. **Try the Python example**: [examples/python-agent/AGENT_MAIL_EXAMPLE.md](../examples/python-agent/AGENT_MAIL_EXAMPLE.md)
3. **Review the ADR**: [adr/002-agent-mail-integration.md](adr/002-agent-mail-integration.md)
4. **Check out benchmarks**: [../latency_results.md](../latency_results.md)
## Getting Help
**Documentation:**
- [AGENT_MAIL.md](AGENT_MAIL.md) - Complete integration guide
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - General bd troubleshooting
- [FAQ.md](FAQ.md) - Frequently asked questions
**Issues:**
- [bd issues](https://github.com/steveyegge/beads/issues) - Integration bugs
- [Agent Mail issues](https://github.com/Dicklesworthstone/mcp_agent_mail/issues) - Server bugs
**Community:**
- [Discussions](https://github.com/steveyegge/beads/discussions) - Ask questions
- [Examples](../examples/) - Learn from working code
## TL;DR - Copy-Paste Setup
```bash
# 1. Install Agent Mail
git clone https://github.com/Dicklesworthstone/mcp_agent_mail.git ~/mcp_agent_mail
cd ~/mcp_agent_mail
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
# 2. Start server (leave running)
python -m mcp_agent_mail.cli serve-http &
# 3. Configure agent (in new terminal)
export BEADS_AGENT_MAIL_URL=http://127.0.0.1:8765
export BEADS_AGENT_NAME=my-agent
export BEADS_PROJECT_ID=my-project
# 4. Use bd normally - coordination happens automatically!
bd ready
bd update bd-42 --status in_progress
bd close bd-42 "Done"
```
**Done!** You're now using Agent Mail for sub-100ms coordination.

View File

@@ -5,6 +5,7 @@ This directory contains examples of how to integrate bd with AI agents and workf
## Examples
- **[python-agent/](python-agent/)** - Simple Python agent that discovers ready work and completes tasks
- **[AGENT_MAIL_EXAMPLE.md](python-agent/AGENT_MAIL_EXAMPLE.md)** - Multi-agent coordination with Agent Mail
- **[bash-agent/](bash-agent/)** - Bash script showing the full agent workflow
- **[monitor-webui/](monitor-webui/)** - Standalone web interface for real-time issue monitoring and visualization
- **[markdown-to-jsonl/](markdown-to-jsonl/)** - Convert markdown planning docs to bd issues

View File

@@ -0,0 +1,418 @@
# Agent Mail Integration Example
This example demonstrates using bd with **Agent Mail** for multi-agent coordination. It shows how to handle reservation conflicts, graceful degradation, and best practices for real-time collaboration.
## Quick Start
### Prerequisites
1. **Install bd** (0.21.0+):
```bash
go install github.com/steveyegge/beads/cmd/bd@latest
```
2. **Install Agent Mail server**:
```bash
git clone https://github.com/Dicklesworthstone/mcp_agent_mail.git
cd mcp_agent_mail
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
```
3. **Initialize beads database**:
```bash
bd init --prefix bd
```
4. **Create some test issues**:
```bash
bd create "Implement login feature" -t feature -p 1
bd create "Add database migrations" -t task -p 1
bd create "Fix bug in auth flow" -t bug -p 0
bd create "Write integration tests" -t task -p 2
```
## Usage Scenarios
### Scenario 1: Single Agent (Git-Only Mode)
No Agent Mail server required. The agent works in traditional git-sync mode:
```bash
# Run agent without Agent Mail
./agent_with_mail.py --agent-name alice --project-id myproject
```
**What happens:**
- Agent finds ready work using `bd ready`
- Claims issues by updating status to `in_progress`
- Completes work and closes issues
- All coordination happens via git (2-5 second latency)
### Scenario 2: Multi-Agent with Agent Mail
Start the Agent Mail server and run multiple agents:
**Terminal 1 - Start Agent Mail server:**
```bash
cd ~/mcp_agent_mail
source .venv/bin/activate
python -m mcp_agent_mail.cli serve-http
# Server runs on http://127.0.0.1:8765
```
**Terminal 2 - First agent:**
```bash
./agent_with_mail.py \
--agent-name alice \
--project-id myproject \
--agent-mail-url http://127.0.0.1:8765 \
--max-iterations 5
```
**Terminal 3 - Second agent:**
```bash
./agent_with_mail.py \
--agent-name bob \
--project-id myproject \
--agent-mail-url http://127.0.0.1:8765 \
--max-iterations 5
```
**Terminal 4 - Monitor (optional):**
```bash
# Watch reservations in real-time
open http://127.0.0.1:8765/mail
```
**What happens:**
- Both agents query for ready work
- First agent to claim an issue gets exclusive reservation
- Second agent gets reservation conflict and tries different work
- Coordination happens in <100ms via Agent Mail
- No duplicate work, no git collisions
### Scenario 3: Environment Variables
Set Agent Mail configuration globally:
```bash
# In your shell profile (~/.bashrc, ~/.zshrc)
export BEADS_AGENT_MAIL_URL=http://127.0.0.1:8765
export BEADS_AGENT_NAME=my-agent
export BEADS_PROJECT_ID=my-project
# Now all bd commands use Agent Mail automatically
./agent_with_mail.py --max-iterations 3
```
### Scenario 4: Graceful Degradation
Start an agent with Agent Mail enabled, then stop the server mid-run:
**Terminal 1:**
```bash
# Start server
cd ~/mcp_agent_mail
source .venv/bin/activate
python -m mcp_agent_mail.cli serve-http
```
**Terminal 2:**
```bash
# Start agent
./agent_with_mail.py \
--agent-name charlie \
--agent-mail-url http://127.0.0.1:8765 \
--max-iterations 10
```
**Terminal 1 (after a few iterations):**
```bash
# Stop server (Ctrl+C)
^C
```
**What happens:**
- Agent starts in Agent Mail mode (<100ms latency)
- After server stops, agent automatically falls back to git-only mode
- No errors, no crashes - work continues normally
- Only difference is increased latency (2-5 seconds)
## Example Output
### With Agent Mail (Successful Reservation)
```
✨ Agent Mail enabled: alice @ http://127.0.0.1:8765
🚀 Agent 'alice' starting...
Project: myproject
Agent Mail: Enabled
============================================================
Iteration 1/5
============================================================
📋 Claiming issue: bd-42
✅ Successfully claimed bd-42
🤖 Working on: Implement login feature (bd-42)
Priority: 1, Type: feature
💡 Creating discovered issue: Follow-up work for Implement login feature
✅ Created bd-43
🔗 Linked bd-43 ← discovered-from ← bd-42
✅ Completing issue: bd-42
✅ Issue bd-42 completed
```
### With Agent Mail (Reservation Conflict)
```
✨ Agent Mail enabled: bob @ http://127.0.0.1:8765
🚀 Agent 'bob' starting...
Project: myproject
Agent Mail: Enabled
============================================================
Iteration 1/5
============================================================
📋 Claiming issue: bd-42
⚠️ Reservation conflict: Error: bd-42 already reserved by alice
⚠️ Issue bd-42 already claimed by another agent
📋 Claiming issue: bd-44
✅ Successfully claimed bd-44
🤖 Working on: Write integration tests (bd-44)
Priority: 2, Type: task
```
### Git-Only Mode (No Agent Mail)
```
📝 Git-only mode: charlie
🚀 Agent 'charlie' starting...
Project: myproject
Agent Mail: Disabled (git-only mode)
============================================================
Iteration 1/5
============================================================
📋 Claiming issue: bd-42
✅ Successfully claimed bd-42
🤖 Working on: Implement login feature (bd-42)
Priority: 1, Type: feature
```
## Code Walkthrough
### Key Methods
**`__init__`**: Configure Agent Mail environment variables
```python
if self.agent_mail_url:
os.environ["BEADS_AGENT_MAIL_URL"] = self.agent_mail_url
os.environ["BEADS_AGENT_NAME"] = self.agent_name
os.environ["BEADS_PROJECT_ID"] = self.project_id
```
**`run_bd`**: Execute bd commands with error handling
```python
result = subprocess.run(["bd"] + list(args) + ["--json"], ...)
if "already reserved" in result.stderr:
return {"error": "reservation_conflict"}
```
**`claim_issue`**: Try to claim an issue, handle conflicts
```python
result = self.run_bd("update", issue_id, "--status", "in_progress")
if result["error"] == "reservation_conflict":
return False # Try different issue
```
**`complete_issue`**: Close issue and release reservation
```python
self.run_bd("close", issue_id, "--reason", reason)
# Agent Mail automatically releases reservation
```
### Error Handling
The agent handles three types of failures:
1. **Reservation conflicts** - Expected in multi-agent workflows:
```python
if "reservation_conflict" in result:
print("⚠️ Issue already claimed by another agent")
return False # Try different work
```
2. **Agent Mail unavailable** - Graceful degradation:
```python
# bd automatically falls back to git-only mode
# No special handling needed!
```
3. **Command failures** - General errors:
```python
if returncode != 0:
print(f"❌ Command failed: {stderr}")
return {"error": "command_failed"}
```
## Integration Tips
### Real LLM Agents
To integrate with Claude, GPT-4, or other LLMs:
1. **Replace `simulate_work()` with LLM calls**:
```python
def simulate_work(self, issue: Dict[str, Any]) -> None:
# Call LLM with issue context
prompt = f"Implement: {issue['title']}\nDescription: {issue['description']}"
response = llm_client.generate(prompt)
# Parse response for new issues/bugs
if "TODO" in response or "BUG" in response:
self.create_discovered_issue(
"Found during work",
issue["id"]
)
```
2. **Use issue IDs for conversation context**:
```python
# Track conversation history per issue
conversation_history[issue["id"]].append({
"role": "user",
"content": issue["description"]
})
```
3. **Export state after each iteration**:
```python
# Ensure git state is synced
subprocess.run(["bd", "sync"])
```
### CI/CD Integration
Run agents in GitHub Actions with Agent Mail:
```yaml
jobs:
agent-workflow:
runs-on: ubuntu-latest
services:
agent-mail:
image: ghcr.io/dicklesworthstone/mcp_agent_mail:latest
ports:
- 8765:8765
strategy:
matrix:
agent: [alice, bob, charlie]
steps:
- uses: actions/checkout@v4
- name: Run agent
env:
BEADS_AGENT_MAIL_URL: http://localhost:8765
BEADS_AGENT_NAME: ${{ matrix.agent }}
BEADS_PROJECT_ID: ${{ github.repository }}
run: |
./examples/python-agent/agent_with_mail.py --max-iterations 3
```
### Monitoring & Debugging
**View reservations in real-time:**
```bash
# Web UI
open http://127.0.0.1:8765/mail
# API
curl http://127.0.0.1:8765/api/reservations | jq
```
**Check Agent Mail connectivity:**
```bash
# Health check
curl http://127.0.0.1:8765/health
# Test reservation
curl -X POST http://127.0.0.1:8765/api/reservations \
-H "Content-Type: application/json" \
-d '{"resource_id": "bd-test", "agent_id": "test-agent", "project_id": "test"}'
```
**Debug agent behavior:**
```bash
# Increase verbosity
./agent_with_mail.py --agent-name debug-agent --max-iterations 1
# Check bd Agent Mail status
bd info --json | grep -A5 agent_mail
```
## Common Issues
### "Agent Mail unavailable" warnings
**Cause:** Server not running or wrong URL
**Solution:**
```bash
# Verify server is running
curl http://127.0.0.1:8765/health
# Check environment variables
echo $BEADS_AGENT_MAIL_URL
echo $BEADS_AGENT_NAME
echo $BEADS_PROJECT_ID
```
### Reservations not released after crash
**Cause:** Agent crashed before calling `bd close`
**Solution:**
```bash
# Manual release via API
curl -X DELETE http://127.0.0.1:8765/api/reservations/bd-42
# Or restart server (clears all ephemeral state)
pkill -f mcp_agent_mail
python -m mcp_agent_mail.cli serve-http
```
### Agents don't see each other's reservations
**Cause:** Different `BEADS_PROJECT_ID` values
**Solution:**
```bash
# Ensure all agents use SAME project ID
export BEADS_PROJECT_ID=my-project # All agents must use this!
# Verify
./agent_with_mail.py --agent-name alice &
./agent_with_mail.py --agent-name bob &
# Both should coordinate on same namespace
```
## See Also
- [../../docs/AGENT_MAIL.md](../../docs/AGENT_MAIL.md) - Complete Agent Mail integration guide
- [../../docs/adr/002-agent-mail-integration.md](../../docs/adr/002-agent-mail-integration.md) - Architecture decision record
- [agent.py](agent.py) - Original agent example (git-only mode)
- [Agent Mail Repository](https://github.com/Dicklesworthstone/mcp_agent_mail)
## License
Apache 2.0 (same as beads)

View File

@@ -84,5 +84,6 @@ tree = agent.run_bd("dep", "tree", "bd-1")
## See Also
- [AGENT_MAIL_EXAMPLE.md](AGENT_MAIL_EXAMPLE.md) - Multi-agent coordination with Agent Mail
- [../bash-agent/](../bash-agent/) - Bash version of this example
- [../claude-desktop-mcp/](../claude-desktop-mcp/) - MCP server for Claude Desktop

View File

@@ -0,0 +1,288 @@
#!/usr/bin/env python3
"""
Beads Agent with Agent Mail Integration Example
Demonstrates how to use bd with optional Agent Mail coordination for multi-agent workflows.
Shows collision handling, graceful degradation, and best practices.
"""
import json
import os
import subprocess
import sys
import time
from typing import Optional, Dict, Any, List
class BeadsAgent:
"""A simple agent that uses bd with optional Agent Mail coordination."""
def __init__(self, agent_name: str, project_id: str, agent_mail_url: Optional[str] = None):
"""
Initialize the agent.
Args:
agent_name: Unique identifier for this agent (e.g., "assistant-alpha")
project_id: Project namespace for Agent Mail
agent_mail_url: Agent Mail server URL (optional, e.g., "http://127.0.0.1:8765")
"""
self.agent_name = agent_name
self.project_id = project_id
self.agent_mail_url = agent_mail_url
# Configure environment for Agent Mail if URL provided
if self.agent_mail_url:
os.environ["BEADS_AGENT_MAIL_URL"] = self.agent_mail_url
os.environ["BEADS_AGENT_NAME"] = self.agent_name
os.environ["BEADS_PROJECT_ID"] = self.project_id
print(f"✨ Agent Mail enabled: {agent_name} @ {agent_mail_url}")
else:
print(f"📝 Git-only mode: {agent_name}")
def run_bd(self, *args) -> Dict[str, Any]:
"""
Run a bd command and return parsed JSON output.
Args:
*args: Command arguments (e.g., "ready", "--json")
Returns:
Parsed JSON output from bd
"""
cmd = ["bd"] + list(args)
if "--json" not in args:
cmd.append("--json")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=False # Don't raise on non-zero exit
)
# Handle reservation conflicts gracefully
if result.returncode != 0:
# Check if it's a reservation conflict
if "already reserved" in result.stderr or "reservation conflict" in result.stderr:
print(f"⚠️ Reservation conflict: {result.stderr.strip()}")
return {"error": "reservation_conflict", "stderr": result.stderr}
else:
print(f"❌ Command failed: {' '.join(cmd)}")
print(f" Error: {result.stderr}")
return {"error": "command_failed", "stderr": result.stderr}
# Parse JSON output
if result.stdout.strip():
return json.loads(result.stdout)
else:
return {}
except json.JSONDecodeError as e:
print(f"❌ Failed to parse JSON from bd: {e}")
print(f" Output: {result.stdout}")
return {"error": "json_parse_failed"}
except Exception as e:
print(f"❌ Failed to run bd: {e}")
return {"error": str(e)}
def get_ready_work(self) -> List[Dict[str, Any]]:
"""Get list of unblocked issues ready to work on."""
result = self.run_bd("ready", "--json")
if "error" in result:
return []
# bd ready returns array of issues
if isinstance(result, list):
return result
else:
return []
def claim_issue(self, issue_id: str) -> bool:
"""
Claim an issue by setting status to in_progress.
Returns:
True if successful, False if reservation conflict or error
"""
print(f"📋 Claiming issue: {issue_id}")
result = self.run_bd("update", issue_id, "--status", "in_progress")
if "error" in result:
if result["error"] == "reservation_conflict":
print(f" ⚠️ Issue {issue_id} already claimed by another agent")
return False
else:
print(f" ❌ Failed to claim {issue_id}")
return False
print(f" ✅ Successfully claimed {issue_id}")
return True
def complete_issue(self, issue_id: str, reason: str = "Completed") -> bool:
"""
Complete an issue and release reservation.
Returns:
True if successful, False otherwise
"""
print(f"✅ Completing issue: {issue_id}")
result = self.run_bd("close", issue_id, "--reason", reason)
if "error" in result:
print(f" ❌ Failed to complete {issue_id}")
return False
print(f" ✅ Issue {issue_id} completed")
return True
def create_discovered_issue(
self,
title: str,
parent_id: str,
priority: int = 2,
issue_type: str = "task"
) -> Optional[str]:
"""
Create an issue discovered during work on another issue.
Args:
title: Issue title
parent_id: ID of the issue this was discovered from
priority: Priority level (0-4)
issue_type: Issue type (bug, feature, task, etc.)
Returns:
New issue ID if successful, None otherwise
"""
print(f"💡 Creating discovered issue: {title}")
result = self.run_bd(
"create",
title,
"-t", issue_type,
"-p", str(priority),
"--deps", f"discovered-from:{parent_id}"
)
if "error" in result or "id" not in result:
print(f" ❌ Failed to create issue")
return None
new_id = result["id"]
print(f" ✅ Created {new_id}")
return new_id
def simulate_work(self, issue: Dict[str, Any]) -> None:
"""Simulate working on an issue."""
print(f"🤖 Working on: {issue['title']} ({issue['id']})")
print(f" Priority: {issue['priority']}, Type: {issue['issue_type']}")
time.sleep(1) # Simulate work
def run(self, max_iterations: int = 10) -> None:
"""
Main agent loop: find work, claim it, complete it.
Args:
max_iterations: Maximum number of issues to process
"""
print(f"\n🚀 Agent '{self.agent_name}' starting...")
print(f" Project: {self.project_id}")
print(f" Agent Mail: {'Enabled' if self.agent_mail_url else 'Disabled (git-only mode)'}\n")
for iteration in range(1, max_iterations + 1):
print("=" * 60)
print(f"Iteration {iteration}/{max_iterations}")
print("=" * 60)
# Get ready work
ready_issues = self.get_ready_work()
if not ready_issues:
print("📭 No ready work available. Stopping.")
break
# Sort by priority (lower number = higher priority)
ready_issues.sort(key=lambda x: x.get("priority", 99))
# Try to claim the highest priority issue
claimed = False
for issue in ready_issues:
if self.claim_issue(issue["id"]):
claimed = True
# Simulate work
self.simulate_work(issue)
# Randomly discover new work (33% chance)
import random
if random.random() < 0.33:
discovered_title = f"Follow-up work for {issue['title']}"
new_id = self.create_discovered_issue(
discovered_title,
issue["id"],
priority=issue.get("priority", 2)
)
if new_id:
print(f"🔗 Linked {new_id} ← discovered-from ← {issue['id']}")
# Complete the issue
self.complete_issue(issue["id"], "Implemented successfully")
break
if not claimed:
print("⚠️ All ready issues are reserved by other agents. Waiting...")
time.sleep(2) # Wait before retrying
print()
print(f"🏁 Agent '{self.agent_name}' finished after {iteration} iterations.")
def main():
"""Main entry point."""
# Parse command line arguments
import argparse
parser = argparse.ArgumentParser(
description="Beads agent with optional Agent Mail coordination"
)
parser.add_argument(
"--agent-name",
default=os.getenv("BEADS_AGENT_NAME", f"agent-{os.getpid()}"),
help="Unique agent identifier (default: agent-<pid>)"
)
parser.add_argument(
"--project-id",
default=os.getenv("BEADS_PROJECT_ID", "default"),
help="Project namespace for Agent Mail"
)
parser.add_argument(
"--agent-mail-url",
default=os.getenv("BEADS_AGENT_MAIL_URL"),
help="Agent Mail server URL (optional, e.g., http://127.0.0.1:8765)"
)
parser.add_argument(
"--max-iterations",
type=int,
default=10,
help="Maximum number of issues to process (default: 10)"
)
args = parser.parse_args()
# Create and run agent
agent = BeadsAgent(
agent_name=args.agent_name,
project_id=args.project_id,
agent_mail_url=args.agent_mail_url
)
try:
agent.run(max_iterations=args.max_iterations)
except KeyboardInterrupt:
print("\n\n⚠️ Agent interrupted by user. Exiting...")
sys.exit(0)
if __name__ == "__main__":
main()