23 KiB
Agent-to-Agent (A2A) Architecture
Version: 3.0.0
Last Updated: 2026-03-31
OpenClaw Version: v2026.3.28
This document describes the Agent-to-Agent (A2A) communication architecture in Heretek OpenClaw using OpenClaw Gateway.
Overview
Heretek OpenClaw uses OpenClaw Gateway WebSocket RPC as the primary communication mechanism between 12 specialized agents (main + 11 collective). This architecture enables real-time, decoupled message passing with automatic session persistence.
Current Architecture (Gateway-Based)
| Component | Technology | Status |
|---|---|---|
| A2A Protocol | Gateway WebSocket RPC | ✅ Current |
| Gateway Port | 18789 | ✅ Active |
| WebSocket Subprotocol | a2a-v1 |
✅ Active |
| Session Storage | JSONL files per workspace | ✅ Active |
| Agent Workspaces | ~/.openclaw/agents/ |
✅ Active |
Legacy Architecture (Deprecated)
| Component | Technology | Status |
|---|---|---|
| A2A Protocol | Redis Pub/Sub | ⛔ Deprecated |
| Agent Ports | 8001-8011 | ⛔ Stopped |
| Session Storage | Redis hashes | ⛔ Deprecated |
Architectural Decision
Gateway WebSocket RPC (Current)
OpenClaw Gateway WebSocket RPC was chosen for A2A communication because:
- Unified Runtime - All agents run within a single Gateway process
- Simplified Deployment - No need for 11 separate containers
- Native Protocol - Built-in WebSocket RPC for agent communication
- Session Isolation - Per-workspace JSONL session storage
- Plugin Integration - Native plugin system for extended functionality
- Reduced Overhead - Single Node.js runtime instead of 11
Redis Pub/Sub (Legacy - Deprecated)
Redis Pub/Sub was the original A2A mechanism but is now deprecated:
- Required 11 separate Docker containers (ports 8001-8011)
- Complex orchestration with docker-compose
- High resource overhead (11x Node.js runtimes)
- Redis used for both A2A and session storage
Migration Note: Redis is still used for caching only, not A2A communication.
Transport Layer
Connection Endpoint
The Gateway WebSocket endpoint:
ws://127.0.0.1:18789
Connection Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
protocol |
string | No | a2a-v1 |
Subprotocol identifier |
timeout |
number | No | 30000 |
Connection timeout (ms) |
heartbeat |
number | No | 30000 |
Heartbeat interval (ms) |
Connection States
| State | Code | Description |
|---|---|---|
CONNECTING |
0 | Connection initiated |
OPEN |
1 | Connection established |
CLOSING |
2 | Connection closing |
CLOSED |
3 | Connection closed |
WebSocket Subprotocol
Clients SHOULD specify the subprotocol during handshake:
const ws = new WebSocket('ws://127.0.0.1:18789', ['a2a-v1']);
Connection Example
const WebSocket = require('ws');
// Connect to Gateway
const ws = new WebSocket('ws://127.0.0.1:18789', ['a2a-v1']);
ws.on('open', () => {
console.log('Connected to Gateway');
// Send handshake
ws.send(JSON.stringify({
type: 'handshake',
content: {
action: 'advertise',
capabilities: {
supportedMessageTypes: ['message', 'status', 'error'],
version: '1.0.0'
}
}
}));
});
ws.on('message', (data) => {
const message = JSON.parse(data);
console.log('Received:', message);
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
Handshake Procedures
Connection Establishment
- Client initiates WebSocket connection to Gateway
- Gateway accepts connection and assigns client ID
- Client optionally sends capability advertisement
- Gateway acknowledges and connection is ready
Capability Advertisement (Optional)
After connection, clients MAY advertise capabilities:
{
"type": "handshake",
"content": {
"action": "advertise",
"capabilities": {
"supportedMessageTypes": ["message", "status", "error", "proposal", "vote", "decision"],
"supportedAgents": ["steward", "alpha", "beta"],
"version": "1.0.0"
}
}
}
Gateway Response
Gateway responds with acknowledgment:
{
"type": "handshake",
"content": {
"action": "acknowledge",
"clientId": "client-uuid",
"availableAgents": ["steward", "alpha", "beta", "charlie"],
"protocolVersion": "1.0.0"
}
}
Authentication (Optional)
For secured deployments, authentication MAY be required:
{
"type": "auth",
"content": {
"token": "<JWT-or-API-key>"
}
}
Connection Termination
Either party MAY terminate the connection:
{
"type": "disconnect",
"content": {
"reason": "shutdown" | "timeout" | "error" | "manual",
"message": "Optional explanation"
}
}
Message Envelope Format
Gateway Message Structure
interface A2AMessage {
// Required fields
type: MessageType; // Message type (see Message Types section)
content: MessageContent; // Message payload
// Optional fields
id?: string; // Unique message identifier (UUID)
agent?: string; // Target agent name
from?: string; // Source agent name
sessionId?: string; // Session identifier
timestamp?: number; // Unix timestamp (ms)
metadata?: MessageMetadata; // Additional metadata
}
interface MessageContent {
role: 'user' | 'assistant' | 'system' | 'agent';
content: string | object;
}
interface MessageMetadata {
priority?: 'low' | 'normal' | 'high' | 'critical';
requiresResponse?: boolean;
threadId?: string;
parentId?: string;
ttl?: number; // Time-to-live in seconds
correlationId?: string; // For request/response correlation
}
Example Message
{
"id": "msg-550e8400-e29b-41d4-a716-446655440000",
"type": "message",
"from": "steward",
"agent": "alpha",
"sessionId": "sess-12345",
"content": {
"role": "agent",
"content": "Proposal received: Implement feature X"
},
"timestamp": 1711843200000,
"metadata": {
"priority": "normal",
"requiresResponse": true,
"threadId": "thread-abc",
"correlationId": "corr-xyz"
}
}
Message Validation
Messages MUST be valid UTF-8 encoded JSON. Invalid messages SHOULD be rejected with an error response.
Message Types
Core Message Types (0x00-0x0F)
| Code | Type | Description | Required Fields |
|---|---|---|---|
| 0x01 | message |
Standard agent message | content, agent |
| 0x02 | status |
Agent status update | content |
| 0x03 | error |
Error notification | content, error |
| 0x04 | event |
Gateway event | content, event |
Control Message Types (0x10-0x1F)
| Code | Type | Description | Required Fields |
|---|---|---|---|
| 0x10 | handshake |
Connection handshake | content.action |
| 0x11 | discovery |
Agent/service discovery | content.action |
| 0x12 | subscribe |
Channel subscription | content.channel |
| 0x13 | unsubscribe |
Channel unsubscription | content.channel |
| 0x14 | ping |
Keep-alive ping | - |
| 0x15 | pong |
Keep-alive response | - |
Authentication Message Types (0x20-0x2F)
| Code | Type | Description | Required Fields |
|---|---|---|---|
| 0x20 | auth |
Authentication request | content.token |
| 0x21 | auth-response |
Authentication response | content.status |
| 0x22 | disconnect |
Connection termination | content.reason |
Application Message Types (0x30-0x4F)
| Code | Type | Description | Required Fields |
|---|---|---|---|
| 0x30 | proposal |
Triad proposal | content.proposal |
| 0x31 | decision |
Triad decision | content.result |
| 0x32 | vote |
Triad vote | content.vote |
| 0x33 | request |
Service request | content.service |
| 0x34 | response |
Service response | content.result |
| 0x35 | broadcast |
Multi-agent broadcast | content.message |
For complete message type registry, see ../standards/A2A_PROTOCOL.md.
Discovery Mechanisms
Agent Discovery
Agents are discovered through the Gateway registry:
Request:
{
"type": "discovery",
"content": {
"action": "list"
}
}
Response:
{
"type": "discovery",
"content": {
"agents": [
{
"name": "steward",
"role": "orchestrator",
"status": "online",
"workspace": "~/.openclaw/agents/steward"
},
{
"name": "alpha",
"role": "triad-member",
"status": "online",
"workspace": "~/.openclaw/agents/alpha"
}
]
}
}
Agent Status Subscription
Clients MAY subscribe to agent status updates:
{
"type": "subscribe",
"content": {
"channel": "agent:status",
"agents": ["steward", "alpha"]
}
}
Workspace Discovery
Workspaces can be discovered and queried:
{
"type": "workspace",
"content": {
"action": "list"
}
}
Triad Deliberation Protocol
The Alpha, Beta, and Charlie agents form a triad for consensus-based decision making:
Message Flow
1. Proposal received via Gateway WebSocket RPC
2. Gateway routes to triad members (alpha, beta, charlie)
3. Each triad member analyzes and votes
4. Votes sent back via Gateway WebSocket RPC
5. 2/3 consensus triggers decision
6. Decision sent to requesting agent
7. Other agents can be notified via Gateway broadcast
Triad Architecture Diagram
┌──────────────┐ ┌─────────────────────────────────────────┐ ┌──────────────┐
│ Client │────>│ OpenClaw Gateway │────>│ Steward │
│ │ │ (Port 18789) │ │ │
└──────────────┘ └─────────────────────────────────────────┘ └──────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Alpha │ │ Beta │ │ Charlie │
│ (Vote) │ │ (Vote) │ │ (Vote) │
└────────────┘ └────────────┘ └────────────┘
Proposal Message
{
"type": "proposal",
"from": "steward",
"content": {
"proposal": "Implement new feature X",
"reasoning": "User request analysis indicates need",
"deadline": 1711843200000
},
"metadata": {
"requiresResponse": true,
"correlationId": "prop-001"
}
}
Vote Message
{
"type": "vote",
"from": "alpha",
"content": {
"proposalId": "prop-001",
"vote": "approve" | "reject" | "abstain",
"reasoning": "Feature aligns with roadmap"
},
"metadata": {
"correlationId": "prop-001"
}
}
Decision Message
{
"type": "decision",
"from": "gateway",
"content": {
"proposalId": "prop-001",
"result": "approved" | "rejected",
"votes": {
"alpha": "approve",
"beta": "approve",
"charlie": "abstain"
},
"consensus": true
}
}
Consensus Rules
| Votes | Result |
|---|---|
| 3-0 | Approved |
| 2-1 | Approved |
| 2-0-1 | Approved |
| 1-2 | Rejected |
| 0-3 | Rejected |
| 1-1-1 | Rejected (no consensus) |
Session Management
JSONL Session Storage
Sessions are stored as JSONL files in each agent workspace:
~/.openclaw/agents/steward/session.jsonl
Session Entry Format
{"timestamp": 1711843200000, "role": "user", "content": "Hello!", "sessionId": "sess-123", "id": "msg-001"}
{"timestamp": 1711843201000, "role": "assistant", "content": "Hi there!", "sessionId": "sess-123", "id": "msg-002"}
Session Lifecycle
| Operation | Command | Description |
|---|---|---|
| Create | openclaw session create {agent} |
Initialize new session |
| List | openclaw session list |
List active sessions |
| Get | openclaw session get {agent} {sessionId} |
Retrieve session data |
| Commit | openclaw session commit {agent} {sessionId} |
Persist session |
| Archive | openclaw session archive {agent} --older-than 7d |
Archive old sessions |
| Delete | openclaw session delete {agent} {sessionId} |
Remove session |
Session Persistence
Sessions are automatically persisted on:
- Message receipt
- Session commit command
- Agent shutdown (graceful)
Session Isolation
Each workspace maintains isolated sessions. Cross-workspace session sharing requires explicit export/import.
Agent Registry
Available Agents
| Agent | Role | Workspace Path | Status |
|---|---|---|---|
main |
Default agent | ~/.openclaw/agents/main |
Active |
steward |
Orchestrator | ~/.openclaw/agents/steward |
Active |
alpha |
Triad member | ~/.openclaw/agents/alpha |
Active |
beta |
Triad member | ~/.openclaw/agents/beta |
Active |
charlie |
Triad member | ~/.openclaw/agents/charlie |
Active |
examiner |
Evaluator | ~/.openclaw/agents/examiner |
Active |
explorer |
Researcher | ~/.openclaw/agents/explorer |
Active |
sentinel |
Safety | ~/.openclaw/agents/sentinel |
Active |
coder |
Developer | ~/.openclaw/agents/coder |
Active |
dreamer |
Creative | ~/.openclaw/agents/dreamer |
Active |
empath |
Emotional | ~/.openclaw/agents/empath |
Active |
historian |
Historical | ~/.openclaw/agents/historian |
Active |
Agent Status States
| Status | Description |
|---|---|
online |
Agent is running and accepting messages |
offline |
Agent is not running |
busy |
Agent is processing a task |
idle |
Agent is available but not processing |
error |
Agent encountered an error |
Agent Commands
# List all agents
openclaw agent list
# Get agent status
openclaw agent status {agent-name}
# Get agent configuration
openclaw agent config {agent-name} get
# Restart agent
openclaw agent restart {agent-name}
Error Handling
Error Message Format
{
"type": "error",
"from": "gateway",
"content": {
"error": "AGENT_NOT_FOUND",
"message": "Agent not found: unknown-agent",
"code": 3001
},
"timestamp": 1711843200000
}
Error Code Registry
Connection Errors (1xxx)
| Code | Error | Description |
|---|---|---|
| 1001 | CONNECTION_REFUSED |
Gateway rejected connection |
| 1002 | CONNECTION_TIMEOUT |
Connection timed out |
| 1003 | CONNECTION_RESET |
Connection was reset |
| 1004 | PROTOCOL_ERROR |
Protocol violation detected |
Message Errors (2xxx)
| Code | Error | Description |
|---|---|---|
| 2001 | INVALID_JSON |
Message is not valid JSON |
| 2002 | MISSING_FIELD |
Required field is missing |
| 2003 | INVALID_TYPE |
Field has invalid type |
| 2004 | UNKNOWN_TYPE |
Unknown message type |
| 2005 | INVALID_CONTENT |
Message content is invalid |
Agent Errors (3xxx)
| Code | Error | Description |
|---|---|---|
| 3001 | AGENT_NOT_FOUND |
Target agent does not exist |
| 3002 | AGENT_OFFLINE |
Target agent is offline |
| 3003 | AGENT_BUSY |
Agent is busy processing |
| 3004 | AGENT_ERROR |
Agent encountered an error |
Session Errors (4xxx)
| Code | Error | Description |
|---|---|---|
| 4001 | SESSION_NOT_FOUND |
Session does not exist |
| 4002 | SESSION_EXPIRED |
Session has expired |
| 4003 | SESSION_LOCKED |
Session is locked by another process |
| 4004 | SESSION_CORRUPT |
Session data is corrupted |
Authentication Errors (5xxx)
| Code | Error | Description |
|---|---|---|
| 5001 | AUTH_REQUIRED |
Authentication required |
| 5002 | AUTH_FAILED |
Authentication failed |
| 5003 | TOKEN_EXPIRED |
Authentication token expired |
| 5004 | PERMISSION_DENIED |
Insufficient permissions |
For complete error code registry, see ../standards/A2A_PROTOCOL.md.
Model Routing
LiteLLM Integration
The Gateway integrates with LiteLLM Gateway (port 4000) for model routing:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ OpenClaw │ │ LiteLLM │ │ Model │
│ Gateway │─────>│ Gateway │─────>│ Providers │
│ (18789) │ │ (4000) │ │ (MiniMax, z.ai) │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Passthrough Endpoints
| Endpoint | Backend Model | Agent |
|---|---|---|
/agent/steward |
minimax/MiniMax-M2.7 |
steward |
/agent/alpha |
minimax/MiniMax-M2.7 |
alpha |
/agent/beta |
minimax/MiniMax-M2.7 |
beta |
/agent/charlie |
minimax/MiniMax-M2.7 |
charlie |
/agent/examiner |
minimax/MiniMax-M2.7 |
examiner |
/agent/explorer |
minimax/MiniMax-M2.7 |
explorer |
/agent/sentinel |
minimax/MiniMax-M2.7 |
sentinel |
/agent/coder |
minimax/MiniMax-M2.7 |
coder |
/agent/dreamer |
minimax/MiniMax-M2.7 |
dreamer |
/agent/empath |
minimax/MiniMax-M2.7 |
empath |
/agent/historian |
minimax/MiniMax-M2.7 |
historian |
Redis Pub/Sub (Legacy Reference)
This section is for historical reference only. Redis Pub/Sub is deprecated for A2A communication.
Legacy Channel Registry
Shared Channels (Deprecated)
| Channel | Purpose | Status |
|---|---|---|
agent:a2a |
General A2A communication | ⛔ Deprecated |
agent:status |
Status broadcasting | ⛔ Deprecated |
agent:message |
Chat messages | ⛔ Deprecated |
agent:activity |
Activity tracking | ⛔ Deprecated |
Per-Agent Inbox Channels (Deprecated)
| Channel | Agent | Status |
|---|---|---|
agent:steward:inbox |
steward | ⛔ Deprecated |
agent:alpha:inbox |
alpha | ⛔ Deprecated |
agent:beta:inbox |
beta | ⛔ Deprecated |
agent:charlie:inbox |
charlie | ⛔ Deprecated |
agent:examiner:inbox |
examiner | ⛔ Deprecated |
agent:explorer:inbox |
explorer | ⛔ Deprecated |
agent:sentinel:inbox |
sentinel | ⛔ Deprecated |
agent:coder:inbox |
coder | ⛔ Deprecated |
agent:dreamer:inbox |
dreamer | ⛔ Deprecated |
agent:empath:inbox |
empath | ⛔ Deprecated |
agent:historian:inbox |
historian | ⛔ Deprecated |
Legacy Message Format
// Legacy Redis Pub/Sub message format (deprecated)
interface LegacyA2AMessage {
id: string; // UUID
type: string; // proposal, decision, request, response, etc.
from: string; // Sending agent name
to?: string; // Target agent (optional for broadcast)
channel: string; // Publishing channel
content: any; // Message payload
timestamp: number; // Unix timestamp
threadId?: string; // Conversation thread ID
parentId?: string; // Parent message ID for threading
}
Legacy Implementation
// Legacy Redis Pub/Sub (deprecated)
const redis = require('redis');
const client = redis.createClient();
const message = {
id: 'msg-' + Date.now(),
type: 'proposal',
from: 'steward',
content: {
proposal: 'Implement new feature X',
reasoning: 'User request analysis indicates need'
},
timestamp: Date.now()
};
// Deprecated: Use Gateway WebSocket RPC instead
client.publish('agent:a2a', JSON.stringify(message));
Implementation Files
Current (Gateway-Based)
| File | Purpose |
|---|---|
modules/communication/channel-ws-adapter.js |
WebSocket adapter for Gateway |
web-interface/src/lib/server/agent-registry.ts |
Agent registry for UI |
web-interface/src/lib/server/litellm-client.ts |
LiteLLM client |
Legacy (Deprecated)
| File | Purpose | Status |
|---|---|---|
modules/communication/redis-websocket-bridge.js |
Redis-to-WebSocket bridge | ⛔ Deprecated |
modules/communication/redis-subscriber.js |
Redis subscriber | ⛔ Deprecated |
Health Monitoring
Gateway Health
# Check Gateway status
openclaw gateway status
# Deep health check
openclaw gateway status --deep
Agent Health
# List all agents
openclaw agent list
# Check specific agent
openclaw agent status steward
# Check all agents
for agent in main steward alpha beta charlie examiner explorer sentinel coder dreamer empath historian; do
openclaw agent status $agent
done
WebSocket Health
# Test WebSocket connection
wscat -c ws://127.0.0.1:18789
# Send test message
wscat -c ws://127.0.0.1:18789 -x '{"type":"status"}'
Migration from Redis Pub/Sub
Migration Steps
-
Install OpenClaw Gateway
curl -fsSL https://openclaw.ai/install.sh | bash -
Configure Gateway
cp openclaw.json ~/.openclaw/openclaw.json openclaw gateway validate -
Create Agent Workspaces
./agents/deploy-agent.sh steward orchestrator # ... repeat for all agents -
Stop Legacy Containers
docker stop heretek-steward heretek-alpha ... heretek-historian -
Update Client Code
// Before (Redis Pub/Sub) const redis = require('redis'); client.publish('agent:a2a', JSON.stringify(message)); // After (Gateway WebSocket RPC) const WebSocket = require('ws'); const ws = new WebSocket('ws://127.0.0.1:18789', ['a2a-v1']); ws.send(JSON.stringify({ type: 'message', agent: 'steward', content: content, metadata: { requiresResponse: true } }));
Migration Checklist
- OpenClaw Gateway installed and running
- All agent workspaces created
- Client code updated to use WebSocket RPC with subprotocol
- Legacy containers stopped
- Redis Pub/Sub subscriptions removed
- Session data migrated (if needed)
- Handshake procedures implemented
- Error handling updated for new error codes
References
../standards/A2A_PROTOCOL.md- Complete A2A Protocol specification./GATEWAY_ARCHITECTURE.md- OpenClaw Gateway details../api/WEBSOCKET_API.md- WebSocket RPC API reference../deployment/LOCAL_DEPLOYMENT.md- Deployment instructions../operations/LEGACY_CLEANUP.md- Container cleanup./REDIS_A2A_ARCHITECTURE.md- Legacy reference
🦞 The thought that never ends.