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)
99 lines
2.2 KiB
Python
99 lines
2.2 KiB
Python
"""
|
|
A2A Protocol for Fleet-Wizard Delegation
|
|
|
|
Implements Google's Agent2Agent (A2A) protocol v1.0 for the Timmy
|
|
Foundation fleet. Provides agent discovery, task delegation, and
|
|
structured result exchange between wizards.
|
|
|
|
Components:
|
|
types.py — A2A data types (Agent Card, Task, Message, Part)
|
|
card.py — Agent Card generation from YAML config
|
|
client.py — Async client for sending tasks to remote agents
|
|
server.py — FastAPI server for receiving A2A tasks
|
|
registry.py — Fleet agent discovery (local file + Gitea backends)
|
|
"""
|
|
|
|
from nexus.a2a.types import (
|
|
AgentCard,
|
|
AgentCapabilities,
|
|
AgentInterface,
|
|
AgentSkill,
|
|
Artifact,
|
|
DataPart,
|
|
FilePart,
|
|
JSONRPCError,
|
|
JSONRPCRequest,
|
|
JSONRPCResponse,
|
|
Message,
|
|
Part,
|
|
Role,
|
|
Task,
|
|
TaskState,
|
|
TaskStatus,
|
|
TextPart,
|
|
part_from_dict,
|
|
part_to_dict,
|
|
)
|
|
|
|
from nexus.a2a.card import (
|
|
AgentCard,
|
|
build_card,
|
|
get_auth_headers,
|
|
load_agent_card,
|
|
load_card_config,
|
|
)
|
|
|
|
from nexus.a2a.registry import (
|
|
GiteaRegistry,
|
|
LocalFileRegistry,
|
|
discover_agents,
|
|
)
|
|
|
|
__all__ = [
|
|
"A2AClient",
|
|
"A2AClientConfig",
|
|
"A2AServer",
|
|
"AgentCard",
|
|
"AgentCapabilities",
|
|
"AgentInterface",
|
|
"AgentSkill",
|
|
"Artifact",
|
|
"DataPart",
|
|
"FilePart",
|
|
"GiteaRegistry",
|
|
"JSONRPCError",
|
|
"JSONRPCRequest",
|
|
"JSONRPCResponse",
|
|
"LocalFileRegistry",
|
|
"Message",
|
|
"Part",
|
|
"Role",
|
|
"Task",
|
|
"TaskState",
|
|
"TaskStatus",
|
|
"TextPart",
|
|
"build_card",
|
|
"discover_agents",
|
|
"echo_handler",
|
|
"get_auth_headers",
|
|
"load_agent_card",
|
|
"load_card_config",
|
|
"part_from_dict",
|
|
"part_to_dict",
|
|
]
|
|
|
|
# Lazy imports for optional deps
|
|
def get_client(**kwargs):
|
|
"""Get A2AClient (avoids aiohttp import at module level)."""
|
|
from nexus.a2a.client import A2AClient, A2AClientConfig
|
|
config = kwargs.pop("config", None)
|
|
if config is None:
|
|
config = A2AClientConfig(**kwargs)
|
|
return A2AClient(config=config)
|
|
|
|
|
|
def get_server(card: AgentCard, **kwargs):
|
|
"""Get A2AServer (avoids fastapi import at module level)."""
|
|
from nexus.a2a.server import A2AServer, echo_handler
|
|
return A2AServer(card=card, **kwargs)
|