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)
242 lines
7.1 KiB
Markdown
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)
|