Files
the-nexus/docs/A2A_PROTOCOL.md
Alexander Whitestone bb9758c4d2
Some checks failed
CI / test (pull_request) Failing after 31s
Review Approval Gate / verify-review (pull_request) Failing after 4s
CI / validate (pull_request) Failing after 30s
feat: implement A2A protocol for fleet-wizard delegation (#1122)
Implements Google Agent2Agent Protocol v1.0 with full fleet integration:

## Phase 1 - Agent Card & Discovery
- Agent Card types with JSON serialization (camelCase, Part discrimination by key)
- Card generation from YAML config (~/.hermes/agent_card.yaml)
- Fleet registry with LocalFileRegistry + GiteaRegistry backends
- Discovery by skill ID or tag

## Phase 2 - Task Delegation
- Async A2A client with JSON-RPC SendMessage/GetTask/ListTasks/CancelTask
- FastAPI server with pluggable task handlers (skill-routed)
- CLI tool (bin/a2a_delegate.py) for fleet delegation
- Broadcast to multiple agents in parallel

## Phase 3 - Security & Reliability
- Bearer token + API key auth (configurable per agent)
- Retry logic (max 3 retries, 30s timeout)
- Audit logging for all inter-agent requests
- Error handling per A2A spec (-32001 to -32009 codes)

## Test Coverage
- 37 tests covering types, card building, registry, server integration
- Auth (required + success), handler routing, error handling

Files:
- nexus/a2a/ (types.py, card.py, client.py, server.py, registry.py)
- bin/a2a_delegate.py (CLI)
- config/ (agent_card.example.yaml, fleet_agents.json)
- docs/A2A_PROTOCOL.md
- tests/test_a2a.py (37 tests, all passing)
2026-04-13 18:31:05 -04:00

242 lines
7.1 KiB
Markdown

# A2A Protocol for Fleet-Wizard Delegation
Implements Google's [Agent2Agent (A2A) Protocol v1.0](https://github.com/google/A2A) for the Timmy Foundation fleet.
## What This Is
Instead of passing notes through humans (Telegram, Gitea issues), fleet wizards can now discover each other's capabilities and delegate tasks autonomously through a machine-native protocol.
```
┌─────────┐ A2A Protocol ┌─────────┐
│ Timmy │ ◄────────────────► │ Ezra │
│ (You) │ JSON-RPC / HTTP │ (CI/CD) │
└────┬────┘ └─────────┘
│ ╲ ╲
│ ╲ Agent Card Discovery ╲ Task Delegation
│ ╲ GET /agent.json ╲ POST /a2a/v1
▼ ▼ ▼
┌──────────────────────────────────────────┐
│ Fleet Registry │
│ config/fleet_agents.json │
└──────────────────────────────────────────┘
```
## Components
| File | Purpose |
|------|---------|
| `nexus/a2a/types.py` | A2A data types — Agent Card, Task, Message, Part, JSON-RPC |
| `nexus/a2a/card.py` | Agent Card generation from `~/.hermes/agent_card.yaml` |
| `nexus/a2a/client.py` | Async client for sending tasks to other agents |
| `nexus/a2a/server.py` | FastAPI server for receiving A2A tasks |
| `nexus/a2a/registry.py` | Fleet agent discovery (local file + Gitea backends) |
| `bin/a2a_delegate.py` | CLI tool for fleet delegation |
| `config/agent_card.example.yaml` | Example agent card config |
| `config/fleet_agents.json` | Fleet registry with all wizards |
## Quick Start
### 1. Configure Your Agent Card
```bash
cp config/agent_card.example.yaml ~/.hermes/agent_card.yaml
# Edit with your agent name, URL, skills, and auth
```
### 2. List Fleet Agents
```bash
python bin/a2a_delegate.py list
```
### 3. Discover Agents by Skill
```bash
python bin/a2a_delegate.py discover --skill ci-health
python bin/a2a_delegate.py discover --tag devops
```
### 4. Send a Task
```bash
python bin/a2a_delegate.py send --to ezra --task "Check CI pipeline health"
python bin/a2a_delegate.py send --to allegro --task "Analyze the codebase" --wait
```
### 5. Fetch an Agent Card
```bash
python bin/a2a_delegate.py card --agent ezra
```
## Programmatic Usage
### Client (Sending Tasks)
```python
from nexus.a2a.client import A2AClient, A2AClientConfig
from nexus.a2a.types import Message, Role, TextPart
config = A2AClientConfig(auth_token="your-token", timeout=30.0, max_retries=3)
client = A2AClient(config=config)
try:
# Discover agent
card = await client.get_agent_card("https://ezra.example.com")
print(f"Found: {card.name} with {len(card.skills)} skills")
# Delegate task
task = await client.delegate(
"https://ezra.example.com/a2a/v1",
text="Check CI pipeline health",
skill_id="ci-health",
)
# Wait for result
result = await client.wait_for_completion(
"https://ezra.example.com/a2a/v1",
task.id,
)
print(f"Result: {result.artifacts[0].parts[0].text}")
# Audit log
for entry in client.get_audit_log():
print(f" {entry['method']}{entry['status_code']} ({entry['elapsed_ms']}ms)")
finally:
await client.close()
```
### Server (Receiving Tasks)
```python
from nexus.a2a.server import A2AServer
from nexus.a2a.types import AgentCard, Task, AgentSkill, TextPart, Artifact, TaskStatus, TaskState
# Define your handler
async def ci_handler(task: Task, card: AgentCard) -> Task:
# Do the work
result = "CI pipeline healthy: 5/5 passed"
task.artifacts.append(
Artifact(parts=[TextPart(text=result)], name="ci_report")
)
task.status = TaskStatus(state=TaskState.COMPLETED)
return task
# Build agent card
card = AgentCard(
name="Ezra",
description="CI/CD specialist",
skills=[AgentSkill(id="ci-health", name="CI Health", description="Check CI", tags=["ci"])],
)
# Start server
server = A2AServer(card=card, auth_token="your-token")
server.register_handler("ci-health", ci_handler)
await server.start(host="0.0.0.0", port=8080)
```
### Registry (Agent Discovery)
```python
from nexus.a2a.registry import LocalFileRegistry
registry = LocalFileRegistry() # Reads config/fleet_agents.json
# List all agents
for agent in registry.list_agents():
print(f"{agent.name}: {agent.description}")
# Find agents by capability
ci_agents = registry.list_agents(skill="ci-health")
devops_agents = registry.list_agents(tag="devops")
# Get endpoint
url = registry.get_endpoint("ezra")
```
## A2A Protocol Reference
### Endpoints
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/.well-known/agent-card.json` | GET | Agent Card discovery |
| `/agent.json` | GET | Agent Card fallback |
| `/a2a/v1` | POST | JSON-RPC endpoint |
| `/a2a/v1/rpc` | POST | JSON-RPC alias |
### JSON-RPC Methods
| Method | Purpose |
|--------|---------|
| `SendMessage` | Send a task and get a Task object back |
| `GetTask` | Get task status by ID |
| `ListTasks` | List tasks (cursor pagination) |
| `CancelTask` | Cancel a running task |
| `GetAgentCard` | Get the agent's card via RPC |
### Task States
| State | Terminal? | Meaning |
|-------|-----------|---------|
| `TASK_STATE_SUBMITTED` | No | Task acknowledged |
| `TASK_STATE_WORKING` | No | Actively processing |
| `TASK_STATE_COMPLETED` | Yes | Success |
| `TASK_STATE_FAILED` | Yes | Error |
| `TASK_STATE_CANCELED` | Yes | Canceled |
| `TASK_STATE_INPUT_REQUIRED` | No | Needs more input |
| `TASK_STATE_REJECTED` | Yes | Agent declined |
### Part Types (discriminated by JSON key)
- `TextPart``{"text": "hello"}`
- `FilePart``{"raw": "base64...", "mediaType": "image/png"}` or `{"url": "https://..."}`
- `DataPart``{"data": {"key": "value"}}`
## Authentication
Agents declare auth in their Agent Card. Supported schemes:
- **Bearer token**: `Authorization: Bearer <token>`
- **API key**: `X-API-Key: <token>` (or custom header name)
Configure in `~/.hermes/agent_card.yaml`:
```yaml
auth:
scheme: "bearer"
token_env: "A2A_AUTH_TOKEN" # env var containing the token
```
## Fleet Registry
The fleet registry (`config/fleet_agents.json`) lists all wizards and their capabilities. Agents can be registered via:
1. **Local file**`LocalFileRegistry` reads/writes JSON directly
2. **Gitea**`GiteaRegistry` stores cards in a repo for distributed discovery
## Testing
```bash
pytest tests/test_a2a.py -v
```
Covers:
- Type serialization roundtrips
- Agent Card building from YAML
- Registry operations (register, list, filter)
- Server integration (SendMessage, GetTask, ListTasks, CancelTask)
- Authentication (required, success)
- Custom handler routing
- Error handling
## Phase Status
- [x] Phase 1 — Agent Card & Discovery
- [x] Phase 2 — Task Delegation
- [x] Phase 3 — Security & Reliability
## Linked Issue
[#1122](https://forge.alexanderwhitestone.com/Timmy_Foundation/the-nexus/issues/1122)