@perkos/perkos-a2a 0.3.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,620 +1,291 @@
1
- # @perkos/a2a
1
+ # @perkos/perkos-a2a
2
2
 
3
- Agent-to-Agent (A2A) protocol communication plugin for [OpenClaw](https://github.com/openclaw/openclaw).
3
+ Agent-to-Agent (A2A) protocol plugin for [OpenClaw](https://openclaw.ai). Enables multi-agent communication using Google's A2A protocol specification with enterprise-grade relay infrastructure for NAT traversal.
4
4
 
5
- Implements Google's [A2A Protocol](https://github.com/a2aproject/A2A) (v0.3.0) to enable OpenClaw agents to discover each other, send tasks, and collaborate autonomously.
5
+ ## Quick Start
6
6
 
7
- ## Features
8
-
9
- - **Agent Card Discovery** — Each agent publishes identity and skills at `/.well-known/agent-card.json`
10
- - **JSON-RPC 2.0** — Standard A2A protocol methods: `message/send`, `tasks/get`, `tasks/list`, `tasks/cancel`
11
- - **OpenClaw Tools** — `a2a_discover`, `a2a_send_task`, `a2a_task_status` available to the LLM
12
- - **CLI Commands** — `openclaw a2a status`, `openclaw a2a discover`, `openclaw a2a send`
13
- - **Peer Discovery** — Automatic discovery of online agents in the network
14
- - **Background Service** — A2A HTTP server runs alongside the OpenClaw gateway
15
-
16
- ## Architecture
17
-
18
- ### System Overview
19
-
20
- ```mermaid
21
- graph TB
22
- subgraph Internet
23
- TG[Telegram]
24
- end
7
+ ```bash
8
+ # Install the plugin
9
+ openclaw plugin install @perkos/perkos-a2a
25
10
 
26
- subgraph Docker Network - council
27
- subgraph Mimir Container
28
- M_OC[OpenClaw Gateway :3000]
29
- M_A2A["@perkos/a2a Plugin :5000"]
30
- M_OC --- M_A2A
31
- end
32
-
33
- subgraph Tyr Container
34
- T_OC[OpenClaw Gateway :3000]
35
- T_A2A["@perkos/a2a Plugin :5000"]
36
- T_OC --- T_A2A
37
- end
38
-
39
- subgraph Bragi Container
40
- B_OC[OpenClaw Gateway :3000]
41
- B_A2A["@perkos/a2a Plugin :5000"]
42
- B_OC --- B_A2A
43
- end
44
-
45
- subgraph Idunn Container
46
- I_OC[OpenClaw Gateway :3000]
47
- I_A2A["@perkos/a2a Plugin :5000"]
48
- I_OC --- I_A2A
49
- end
50
- end
11
+ # Run the setup wizard to detect your environment
12
+ openclaw perkos-a2a setup
51
13
 
52
- TG <--> M_OC
53
- TG <--> T_OC
54
- TG <--> B_OC
55
- TG <--> I_OC
56
-
57
- M_A2A <-->|A2A JSON-RPC| T_A2A
58
- M_A2A <-->|A2A JSON-RPC| B_A2A
59
- M_A2A <-->|A2A JSON-RPC| I_A2A
60
- T_A2A <-->|A2A JSON-RPC| B_A2A
61
- T_A2A <-->|A2A JSON-RPC| I_A2A
62
- B_A2A <-->|A2A JSON-RPC| I_A2A
63
-
64
- style M_OC fill:#eb1b69,color:#fff
65
- style T_OC fill:#eb1b69,color:#fff
66
- style B_OC fill:#eb1b69,color:#fff
67
- style I_OC fill:#eb1b69,color:#fff
68
- style M_A2A fill:#8e2051,color:#fff
69
- style T_A2A fill:#8e2051,color:#fff
70
- style B_A2A fill:#8e2051,color:#fff
71
- style I_A2A fill:#8e2051,color:#fff
14
+ # Check status
15
+ openclaw perkos-a2a status
72
16
  ```
73
17
 
74
- ### Plugin Architecture
75
-
76
- ```mermaid
77
- graph LR
78
- subgraph OpenClaw Gateway
79
- LLM[LLM Agent]
80
- Tools[Tool Registry]
81
- Session[Session Manager]
82
- end
83
-
84
- subgraph "@perkos/a2a Plugin"
85
- Service[Background Service]
86
- Express[Express HTTP Server]
87
- AgentCard[Agent Card Provider]
88
- TaskStore[In-Memory Task Store]
89
- Client[A2A Client]
90
- end
91
-
92
- subgraph Endpoints
93
- WK["/.well-known/agent-card.json"]
94
- RPC["/a2a/jsonrpc"]
95
- Health["/health"]
96
- Peers["/a2a/peers"]
97
- Send["/a2a/send"]
98
- end
18
+ Add to your `openclaw.json`:
99
19
 
100
- LLM --> Tools
101
- Tools -->|a2a_discover| Client
102
- Tools -->|a2a_send_task| Client
103
- Tools -->|a2a_task_status| Client
104
-
105
- Service --> Express
106
- Express --> WK
107
- Express --> RPC
108
- Express --> Health
109
- Express --> Peers
110
- Express --> Send
111
-
112
- RPC --> TaskStore
113
- WK --> AgentCard
114
- Client -->|HTTP POST| RPC
115
-
116
- style LLM fill:#eb1b69,color:#fff
117
- style Service fill:#8e2051,color:#fff
118
- style Express fill:#76437b,color:#fff
20
+ ```json
21
+ {
22
+ "plugins": {
23
+ "entries": {
24
+ "perkos-a2a": {
25
+ "config": {
26
+ "agentName": "my-agent",
27
+ "port": 5050,
28
+ "mode": "auto",
29
+ "peers": {
30
+ "other-agent": "http://10.0.0.2:5050"
31
+ },
32
+ "relay": {
33
+ "url": "wss://relay.perkos.xyz",
34
+ "apiKey": "your-agent-api-key",
35
+ "enabled": true
36
+ },
37
+ "auth": {
38
+ "requireApiKey": true,
39
+ "apiKeys": ["key1", "key2"]
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
119
46
  ```
120
47
 
121
- ## Protocol Specification
48
+ ## Architecture
122
49
 
123
- ### A2A Protocol Layers
50
+ ### Relay Hub (NAT Traversal)
124
51
 
125
- This plugin implements the three layers of the A2A Protocol Specification:
52
+ Agents behind NAT connect outbound to the relay hub via WebSocket. The relay routes messages between agents, queues messages for offline agents, and maintains a presence registry.
126
53
 
127
54
  ```mermaid
128
55
  graph TB
129
- subgraph "Layer 1: Data Model"
130
- Task[Task]
131
- Message[Message]
132
- AgentCard[AgentCard]
133
- Part[Part]
134
- Artifact[Artifact]
56
+ subgraph "A2A Relay Hub (Cloud VPS)"
57
+ RH[WebSocket Broker]
58
+ MQ[Message Queue]
59
+ REG[Agent Registry]
60
+ AUTH[API Key Auth]
61
+ RL[Rate Limiter]
135
62
  end
136
63
 
137
- subgraph "Layer 2: Operations"
138
- SendMsg["message/send"]
139
- GetTask["tasks/get"]
140
- ListTasks["tasks/list"]
141
- CancelTask["tasks/cancel"]
142
- GetCard["agent/card"]
64
+ subgraph "Agent A (Behind NAT)"
65
+ OC_A[OpenClaw Gateway] --> P_A[perkos-a2a plugin]
66
+ P_A --> RC_A[Relay Client]
143
67
  end
144
68
 
145
- subgraph "Layer 3: Protocol Binding"
146
- JSONRPC["JSON-RPC 2.0 over HTTP"]
69
+ subgraph "Agent B (VPS)"
70
+ OC_B[OpenClaw Gateway] --> P_B[perkos-a2a plugin]
71
+ P_B --> S_B[HTTP Server :5050]
72
+ P_B --> RC_B[Relay Client]
147
73
  end
148
74
 
149
- Task --> SendMsg
150
- Message --> SendMsg
151
- AgentCard --> GetCard
152
- Part --> Message
153
- Artifact --> Task
154
-
155
- SendMsg --> JSONRPC
156
- GetTask --> JSONRPC
157
- ListTasks --> JSONRPC
158
- CancelTask --> JSONRPC
159
- GetCard --> JSONRPC
160
-
161
- style Task fill:#e1f5fe,color:#000
162
- style Message fill:#e1f5fe,color:#000
163
- style AgentCard fill:#e1f5fe,color:#000
164
- style Part fill:#e1f5fe,color:#000
165
- style Artifact fill:#e1f5fe,color:#000
166
- style SendMsg fill:#f3e5f5,color:#000
167
- style GetTask fill:#f3e5f5,color:#000
168
- style ListTasks fill:#f3e5f5,color:#000
169
- style CancelTask fill:#f3e5f5,color:#000
170
- style GetCard fill:#f3e5f5,color:#000
171
- style JSONRPC fill:#e8f5e8,color:#000
172
- ```
173
-
174
- ### Task Lifecycle
75
+ subgraph "Agent C (Behind NAT)"
76
+ OC_C[OpenClaw Gateway] --> P_C[perkos-a2a plugin]
77
+ P_C --> RC_C[Relay Client]
78
+ end
175
79
 
176
- ```mermaid
177
- stateDiagram-v2
178
- [*] --> submitted: message/send received
179
- submitted --> working: Processing begins
180
- working --> completed: Task finished successfully
181
- working --> failed: Error during processing
182
- working --> canceled: tasks/cancel received
183
- completed --> [*]
184
- failed --> [*]
185
- canceled --> [*]
80
+ RC_A <-->|WSS| RH
81
+ RC_B <-->|WSS| RH
82
+ RC_C <-->|WSS| RH
83
+ RC_B <-->|Direct HTTP| S_B
84
+
85
+ style RH fill:#1d1029,stroke:#eb1b69,color:#fff
86
+ style MQ fill:#45193c,stroke:#eb1b69,color:#fff
87
+ style REG fill:#45193c,stroke:#eb1b69,color:#fff
88
+ style AUTH fill:#45193c,stroke:#eb1b69,color:#fff
89
+ style RL fill:#45193c,stroke:#eb1b69,color:#fff
90
+ style OC_A fill:#1d1029,stroke:#eb1b69,color:#fff
91
+ style OC_B fill:#1d1029,stroke:#eb1b69,color:#fff
92
+ style OC_C fill:#1d1029,stroke:#eb1b69,color:#fff
93
+ style P_A fill:#8e2051,stroke:#eb1b69,color:#fff
94
+ style P_B fill:#8e2051,stroke:#eb1b69,color:#fff
95
+ style P_C fill:#8e2051,stroke:#eb1b69,color:#fff
186
96
  ```
187
97
 
188
- ### Agent Discovery Sequence
98
+ ### Direct Peer-to-Peer
99
+
100
+ Agents on the same network or with public IPs can communicate directly via HTTP without a relay.
189
101
 
190
102
  ```mermaid
191
- sequenceDiagram
192
- participant User
193
- participant Mimir as Mimir (Orchestrator)
194
- participant TyrCard as Tyr /.well-known/agent-card.json
195
- participant BragiCard as Bragi /.well-known/agent-card.json
196
- participant IdunnCard as Idunn /.well-known/agent-card.json
197
-
198
- User->>Mimir: "Discover all agents"
199
- activate Mimir
200
- Mimir->>Mimir: a2a_discover tool invoked
201
-
202
- par Parallel Discovery
203
- Mimir->>TyrCard: GET /.well-known/agent-card.json
204
- TyrCard-->>Mimir: AgentCard {name, skills, url}
205
- and
206
- Mimir->>BragiCard: GET /.well-known/agent-card.json
207
- BragiCard-->>Mimir: AgentCard {name, skills, url}
208
- and
209
- Mimir->>IdunnCard: GET /.well-known/agent-card.json
210
- IdunnCard-->>Mimir: AgentCard {name, skills, url}
103
+ graph TB
104
+ subgraph "Agent A (VPS)"
105
+ OC_A[OpenClaw Gateway] --> P_A[perkos-a2a plugin]
106
+ P_A --> S_A[A2A Server :5050]
107
+ P_A --> C_A[A2A Client]
108
+ S_A --> AC_A["/.well-known/agent-card.json"]
109
+ S_A --> RPC_A["/a2a/jsonrpc"]
211
110
  end
212
111
 
213
- Mimir-->>User: "3 agents online: Tyr (engineering), Bragi (comms), Idunn (product)"
214
- deactivate Mimir
215
- ```
216
-
217
- ### Task Delegation Sequence
112
+ subgraph "Agent B (VPS)"
113
+ OC_B[OpenClaw Gateway] --> P_B[perkos-a2a plugin]
114
+ P_B --> S_B[A2A Server :5050]
115
+ P_B --> C_B[A2A Client]
116
+ end
218
117
 
219
- ```mermaid
220
- sequenceDiagram
221
- participant User
222
- participant Mimir as Mimir (Orchestrator)
223
- participant TyrA2A as Tyr A2A Server
224
- participant TyrOC as Tyr OpenClaw Agent
225
- participant TyrFS as Tyr Workspace
226
-
227
- User->>Mimir: "Ask Tyr to implement rate limiting"
228
- activate Mimir
229
- Mimir->>Mimir: a2a_send_task(target="tyr", message="...")
230
-
231
- Mimir->>TyrA2A: POST /a2a/jsonrpc {method: "message/send"}
232
- activate TyrA2A
233
-
234
- TyrA2A->>TyrA2A: Create Task (state: submitted)
235
- TyrA2A-->>Mimir: Task {id, status: "submitted"}
236
- Mimir-->>User: "Task sent to Tyr. ID: abc-123"
237
- deactivate Mimir
238
-
239
- TyrA2A->>TyrA2A: Process Task (state: working)
240
- TyrA2A->>TyrFS: Write task to memory/a2a-task-{id}.md
241
- TyrA2A->>TyrA2A: Complete Task (state: completed)
242
- deactivate TyrA2A
243
-
244
- Note over TyrOC: Agent picks up task<br/>from workspace on<br/>next heartbeat/session
245
-
246
- User->>Mimir: "Check status of task abc-123 on Tyr"
247
- activate Mimir
248
- Mimir->>TyrA2A: POST /a2a/jsonrpc {method: "tasks/get", id: "abc-123"}
249
- TyrA2A-->>Mimir: Task {status: "completed", artifacts: [...]}
250
- Mimir-->>User: "Task completed"
251
- deactivate Mimir
118
+ C_A <-->|JSON-RPC 2.0| S_B
119
+ C_B <-->|JSON-RPC 2.0| S_A
120
+
121
+ style OC_A fill:#1d1029,stroke:#eb1b69,color:#fff
122
+ style OC_B fill:#1d1029,stroke:#eb1b69,color:#fff
123
+ style P_A fill:#8e2051,stroke:#eb1b69,color:#fff
124
+ style P_B fill:#8e2051,stroke:#eb1b69,color:#fff
125
+ style S_A fill:#76437b,stroke:#eb1b69,color:#fff
126
+ style S_B fill:#76437b,stroke:#eb1b69,color:#fff
127
+ style C_A fill:#45193c,stroke:#eb1b69,color:#fff
128
+ style C_B fill:#45193c,stroke:#eb1b69,color:#fff
252
129
  ```
253
130
 
254
- ### Multi-Agent Collaboration Sequence
131
+ ## Task Lifecycle
255
132
 
256
133
  ```mermaid
257
134
  sequenceDiagram
258
- participant J as Julio (Founder)
259
- participant M as Mimir (Strategy)
260
- participant T as Tyr (Engineering)
261
- participant B as Bragi (Comms)
262
- participant I as Idunn (Product)
263
-
264
- J->>M: "Build a landing page for PerkOS"
265
- activate M
266
-
267
- M->>M: Strategic analysis
268
- Note over M: Break into subtasks:<br/>1. UX spec (Idunn)<br/>2. Implementation (Tyr)<br/>3. Copy (Bragi)
269
-
270
- M->>I: a2a_send_task("Design the landing page UX")
271
- activate I
272
- I-->>M: Task accepted (UX spec)
273
- I->>I: Creates wireframes + spec
274
- deactivate I
275
-
276
- M->>T: a2a_send_task("Implement landing page per Idunn's spec")
277
- activate T
278
- T-->>M: Task accepted (implementation)
279
- T->>T: Writes Next.js + Tailwind code
280
- deactivate T
281
-
282
- M->>B: a2a_send_task("Write landing page copy for PerkOS")
283
- activate B
284
- B-->>M: Task accepted (copywriting)
285
- B->>B: Writes headlines, CTAs, descriptions
286
- deactivate B
287
-
288
- M-->>J: "Landing page tasks delegated to Idunn (UX), Tyr (code), Bragi (copy)"
289
- deactivate M
135
+ participant Agent A
136
+ participant Relay Hub
137
+ participant Agent B
138
+
139
+ Note over Agent A,Agent B: Via Relay (NAT traversal)
140
+ Agent A->>Relay Hub: WSS: task message (to: Agent B)
141
+ Relay Hub->>Agent B: WSS: forward task
142
+ Agent B->>Agent B: Process task + inject into session
143
+ Agent B->>Relay Hub: WSS: task response
144
+ Relay Hub->>Agent A: WSS: forward response
145
+
146
+ Note over Agent A,Agent B: Via Direct HTTP
147
+ Agent A->>Agent B: POST /a2a/jsonrpc (message/send)
148
+ Agent B->>Agent B: Process task + inject into session
149
+ Agent B-->>Agent A: JSON-RPC response
290
150
  ```
291
151
 
292
- ## Data Model
152
+ ## Running the Relay Hub
293
153
 
294
- ### Agent Card
154
+ The relay hub is a lightweight WebSocket broker. Deploy it on any VPS with a public IP.
295
155
 
296
- Each agent publishes an Agent Card describing its identity and capabilities:
156
+ ```bash
157
+ # Via npx (development)
158
+ npx tsx bin/relay.ts --port 6060 --api-keys key1,key2
297
159
 
298
- ```json
299
- {
300
- "name": "Tyr",
301
- "description": "Engineering and execution agent",
302
- "protocolVersion": "0.3.0",
303
- "version": "1.0.0",
304
- "url": "http://perkos-tyr:5000/a2a/jsonrpc",
305
- "skills": [
306
- {
307
- "id": "code",
308
- "name": "Code Implementation",
309
- "description": "Write production-quality TypeScript, Solidity, Next.js code",
310
- "tags": ["coding", "typescript", "solidity"]
311
- }
312
- ],
313
- "capabilities": { "pushNotifications": false },
314
- "defaultInputModes": ["text"],
315
- "defaultOutputModes": ["text"]
316
- }
160
+ # Via environment variables
161
+ RELAY_PORT=6060 RELAY_API_KEYS=key1,key2 npx tsx bin/relay.ts
317
162
  ```
318
163
 
319
- ### Task
164
+ ### Relay Hub Options
320
165
 
321
- Tasks track the lifecycle of delegated work:
166
+ | Option | Env Var | Default | Description |
167
+ |---|---|---|---|
168
+ | `--port` | `RELAY_PORT` | 6060 | WebSocket listen port |
169
+ | `--api-keys` | `RELAY_API_KEYS` | (none) | Comma-separated accepted API keys |
170
+ | `--max-queue` | `RELAY_MAX_QUEUE` | 200 | Max queued messages per offline agent |
171
+ | `--rate-limit` | `RELAY_RATE_LIMIT` | 60 | Max messages per agent per minute |
322
172
 
323
- ```json
324
- {
325
- "kind": "task",
326
- "id": "f49f9755-86b9-4399-9463-fa6b9a11b51a",
327
- "contextId": "cc57cc50-762c-41d4-b9a1-59b1ee691a69",
328
- "status": {
329
- "state": "completed",
330
- "timestamp": "2026-03-12T16:24:45.709Z"
331
- },
332
- "messages": [
333
- {
334
- "kind": "message",
335
- "messageId": "a008eb3e-2fb0-4947-b1e4-15a6d3a8cba5",
336
- "role": "user",
337
- "parts": [{ "kind": "text", "text": "Implement rate limiting on the API" }],
338
- "metadata": { "fromAgent": "mimir" }
339
- }
340
- ],
341
- "artifacts": [
342
- {
343
- "kind": "artifact",
344
- "artifactId": "b2c3d4e5-...",
345
- "parts": [{ "kind": "text", "text": "Task queued for processing" }]
346
- }
347
- ]
348
- }
349
- ```
173
+ ## Modes
350
174
 
351
- ### Message
175
+ | Mode | HTTP Server | Relay Client | Use Case |
176
+ |---|---|---|---|
177
+ | `auto` | Conditional | If configured | Detects NAT, chooses best option |
178
+ | `full` | Yes | If configured | VPS with public IP |
179
+ | `client-only` | No | If configured | Behind NAT, no local server |
180
+ | `relay` | No | No | Run as relay hub only |
352
181
 
353
- Messages are communication turns between agents:
182
+ ## Configuration
354
183
 
355
184
  ```json
356
185
  {
357
- "kind": "message",
358
- "messageId": "unique-id",
359
- "role": "user",
360
- "parts": [
361
- { "kind": "text", "text": "Your task description here" }
362
- ],
363
- "metadata": {
364
- "fromAgent": "mimir"
186
+ "agentName": "my-agent",
187
+ "port": 5050,
188
+ "mode": "auto",
189
+ "skills": [],
190
+ "peers": {
191
+ "other-agent": "http://10.0.0.2:5050"
192
+ },
193
+ "relay": {
194
+ "url": "wss://relay.perkos.xyz",
195
+ "apiKey": "agent-specific-key",
196
+ "enabled": true
197
+ },
198
+ "auth": {
199
+ "requireApiKey": true,
200
+ "apiKeys": ["key1", "key2"]
365
201
  }
366
202
  }
367
203
  ```
368
204
 
369
- ## JSON-RPC 2.0 Methods
370
-
371
- | Method | Description | Request Params | Response |
372
- |--------|------------|----------------|----------|
373
- | `message/send` | Send a task to the agent | `{message: Message}` | `Task` |
374
- | `tasks/get` | Get task by ID | `{id: string}` | `Task` |
375
- | `tasks/list` | List all tasks | `{}` | `{tasks: Task[]}` |
376
- | `tasks/cancel` | Cancel a running task | `{id: string}` | `Task` |
377
- | `agent/card` | Get the agent's card | `{}` | `AgentCard` |
378
-
379
- ### Example JSON-RPC Request
205
+ | Option | Type | Default | Description |
206
+ |---|---|---|---|
207
+ | `agentName` | string | `"agent"` | This agent's name in the council |
208
+ | `port` | number | `5050` | HTTP server port |
209
+ | `mode` | string | `"auto"` | Operating mode (see table above) |
210
+ | `skills` | array | `[]` | Skills exposed via A2A |
211
+ | `peers` | object | `{}` | Direct peer URLs |
212
+ | `relay.url` | string | - | Relay hub WebSocket URL |
213
+ | `relay.apiKey` | string | - | API key for relay authentication |
214
+ | `relay.enabled` | boolean | `false` | Enable relay connectivity |
215
+ | `auth.requireApiKey` | boolean | `false` | Require API key for inbound HTTP |
216
+ | `auth.apiKeys` | string[] | `[]` | Accepted API keys for inbound HTTP |
380
217
 
381
- ```json
382
- {
383
- "jsonrpc": "2.0",
384
- "method": "message/send",
385
- "id": "req-1",
386
- "params": {
387
- "message": {
388
- "kind": "message",
389
- "messageId": "msg-1",
390
- "role": "user",
391
- "parts": [{"kind": "text", "text": "Review the API security"}],
392
- "metadata": {"fromAgent": "mimir"}
393
- }
394
- }
395
- }
396
- ```
218
+ ## Authentication
397
219
 
398
- ### Example JSON-RPC Response
220
+ ### Relay Auth
399
221
 
400
- ```json
401
- {
402
- "jsonrpc": "2.0",
403
- "id": "req-1",
404
- "result": {
405
- "kind": "task",
406
- "id": "task-uuid",
407
- "contextId": "ctx-uuid",
408
- "status": {"state": "submitted", "timestamp": "2026-03-12T16:00:00Z"},
409
- "messages": [...],
410
- "artifacts": []
411
- }
412
- }
413
- ```
222
+ Agents authenticate with the relay hub using an API key provided during WebSocket registration. The relay rejects connections with invalid keys.
414
223
 
415
- ## HTTP Endpoints
224
+ ### HTTP Auth
416
225
 
417
- | Endpoint | Method | Description |
418
- |----------|--------|-------------|
419
- | `/.well-known/agent-card.json` | GET | A2A Agent Card (discovery) |
420
- | `/a2a/jsonrpc` | POST | JSON-RPC 2.0 endpoint |
421
- | `/a2a/peers` | GET | Discover all peer agents |
422
- | `/a2a/send` | POST | REST convenience for sending tasks |
423
- | `/health` | GET | Health check with agent info |
226
+ When `auth.requireApiKey` is enabled, inbound HTTP requests must include an API key via:
227
+ - `X-API-Key` header
228
+ - `Authorization: Bearer <key>` header
229
+ - `?apiKey=<key>` query parameter
424
230
 
425
- ## OpenClaw Integration
231
+ ## Session Injection
426
232
 
427
- ### Registered Tools
233
+ When a task is received, the plugin attempts to inject it directly into the agent's OpenClaw session using the plugin API (`api.injectMessage`). If session injection is not available, tasks fall back to writing markdown files to the workspace.
428
234
 
429
- | Tool | Description | Parameters |
430
- |------|-------------|------------|
431
- | `a2a_discover` | Discover peer agents and capabilities | none |
432
- | `a2a_send_task` | Send a task to a peer agent | `target`, `message` |
433
- | `a2a_task_status` | Check status of a sent task | `target`, `taskId` |
235
+ ## Agent Tools
434
236
 
435
- ### CLI Commands
237
+ When the plugin is active, three tools are available to the agent:
436
238
 
437
- ```bash
438
- openclaw a2a status # Show A2A agent configuration
439
- openclaw a2a discover # Discover and list peer agents
440
- openclaw a2a send <agent> <message> # Send a task to a peer
441
- ```
239
+ - `a2a_discover` -- Discover all configured peer agents and their capabilities (direct + relay)
240
+ - `a2a_send_task` -- Send a task to a named peer agent (tries direct HTTP, falls back to relay)
241
+ - `a2a_task_status` -- Check the status of a previously sent task
442
242
 
443
- ### Gateway RPC
243
+ ## CLI Commands
444
244
 
445
245
  ```bash
446
- openclaw gateway call a2a.status # Get A2A status via RPC
246
+ openclaw perkos-a2a setup # Detect environment and show recommendations
247
+ openclaw perkos-a2a status # Show agent status, relay connection, and config
248
+ openclaw perkos-a2a discover # Discover peer agents (direct + relay)
249
+ openclaw perkos-a2a send <target> <message> # Send a task to a peer
447
250
  ```
448
251
 
449
- ## ⚠️ macOS Note
450
-
451
- On macOS, **port 5000 is used by AirPlay Receiver** (ControlCenter). The default port is `5050` to avoid conflicts. If the configured port is in use, the plugin automatically tries the next port (up to 3 attempts). Docker/Linux agents can still use port 5000 via explicit config.
452
-
453
- ## Installation
252
+ ## Networking Guide
454
253
 
455
- ### From npm
254
+ ### Option 1: Relay Hub (Recommended for NAT)
456
255
 
457
- ```bash
458
- openclaw plugins install @perkos/a2a
459
- ```
460
-
461
- ### Manual Installation
462
-
463
- ```bash
464
- git clone https://github.com/PerkOS-xyz/PerkOS-A2A.git
465
- cd PerkOS-A2A
466
- npm install
467
- npm run build
468
- openclaw plugins install -l .
469
- ```
256
+ Deploy the relay hub on a VPS, configure all agents to connect to it. No port forwarding or tunnels needed.
470
257
 
471
- ## Configuration
258
+ ### Option 2: Direct (Public IP / Same Network)
472
259
 
473
- Add to your `openclaw.json`:
260
+ Agents with public IPs or on the same network can communicate directly via HTTP. Configure peer URLs in the `peers` config.
474
261
 
475
- ```json5
476
- {
477
- plugins: {
478
- entries: {
479
- "perkos-a2a": {
480
- enabled: true,
481
- config: {
482
- // This agent's name in the network
483
- agentName: "mimir",
484
- // Port for the A2A HTTP server (default 5050; use 5000 on Linux/Docker)
485
- port: 5050,
486
- // Skills this agent exposes
487
- skills: [
488
- {
489
- id: "strategy",
490
- name: "Strategic Planning",
491
- description: "Break down goals into actionable tasks",
492
- tags: ["planning", "strategy"]
493
- }
494
- ],
495
- // Peer agents (name -> A2A URL)
496
- peers: {
497
- tyr: "http://perkos-tyr:5000",
498
- bragi: "http://perkos-bragi:5000",
499
- idunn: "http://perkos-idunn:5000"
500
- }
501
- }
502
- }
503
- }
504
- }
505
- }
506
- ```
262
+ ### Option 3: Tailscale
507
263
 
508
- ### Configuration Options
264
+ Both agents join the same tailnet. Use Tailscale IPs for peer URLs.
509
265
 
510
- | Option | Type | Default | Description |
511
- |--------|------|---------|-------------|
512
- | `agentName` | string | `"agent"` | This agent's name in the network |
513
- | `port` | number | `5050` | Port for the A2A HTTP server (macOS avoids 5000/AirPlay) |
514
- | `skills` | array | `[]` | Skills to advertise in the Agent Card |
515
- | `peers` | object | `{}` | Map of peer agent names to A2A URLs |
516
-
517
- ## Docker Deployment
518
-
519
- For multi-agent setups, use Docker Compose with a shared network:
520
-
521
- ```yaml
522
- services:
523
- mimir:
524
- build: .
525
- container_name: perkos-mimir
526
- ports:
527
- - "3001:3000" # OpenClaw
528
- - "5001:5000" # A2A
529
- environment:
530
- - AGENT_NAME=mimir
531
- networks:
532
- - council
533
-
534
- tyr:
535
- build: .
536
- container_name: perkos-tyr
537
- ports:
538
- - "3002:3000"
539
- - "5002:5000"
540
- environment:
541
- - AGENT_NAME=tyr
542
- networks:
543
- - council
544
-
545
- networks:
546
- council:
547
- driver: bridge
548
- ```
266
+ ### Option 4: Client-Only
549
267
 
550
- Agents can discover each other via Docker DNS: `http://perkos-tyr:5000`.
551
-
552
- ## A2A Protocol Compliance
553
-
554
- This plugin implements the [A2A Protocol Specification RC v1.0](https://a2a-protocol.org/latest/specification/):
555
-
556
- | Feature | Status |
557
- |---------|--------|
558
- | Agent Card at `/.well-known/agent-card.json` | ✅ |
559
- | JSON-RPC 2.0 binding | ✅ |
560
- | `message/send` | ✅ |
561
- | `tasks/get` | ✅ |
562
- | `tasks/list` | ✅ |
563
- | `tasks/cancel` | ✅ |
564
- | Task lifecycle states | ✅ |
565
- | Text parts | ✅ |
566
- | Artifact generation | ✅ |
567
- | Streaming (SSE) | 🔜 Planned |
568
- | Push notifications | 🔜 Planned |
569
- | File parts | 🔜 Planned |
570
- | gRPC binding | 🔜 Planned |
571
-
572
- ## TypeScript Types
573
-
574
- The plugin exports full TypeScript types for the A2A protocol:
575
-
576
- ```typescript
577
- import type {
578
- AgentCard,
579
- AgentSkill,
580
- Task,
581
- TaskStatus,
582
- Message,
583
- Part,
584
- Artifact,
585
- JsonRpcRequest,
586
- JsonRpcResponse,
587
- A2APluginConfig,
588
- } from "@perkos/a2a";
589
- ```
268
+ Set `"mode": "client-only"` to only send tasks (not receive). Combine with relay for full bidirectional support.
590
269
 
591
- ## Standalone Server
270
+ ## Troubleshooting
592
271
 
593
- You can also use the A2A server independently of OpenClaw:
272
+ **Relay connection failing:**
273
+ - Verify the relay URL is correct and reachable
274
+ - Check that your API key matches one configured on the relay hub
275
+ - Look for `[perkos-a2a]` log messages for connection errors
594
276
 
595
- ```typescript
596
- import { A2AServer } from "@perkos/a2a";
277
+ **Port 5050 in use:**
278
+ Change the port in config, or run `lsof -i :5050` to find the conflicting process.
597
279
 
598
- const server = new A2AServer({
599
- agentName: "my-agent",
600
- port: 5050,
601
- skills: [{ id: "chat", name: "Chat", description: "General chat", tags: [] }],
602
- peers: {
603
- other: "http://other-agent:5050",
604
- },
605
- });
280
+ **Peers show offline:**
281
+ - Verify the peer URL is correct and reachable
282
+ - Check firewalls/security groups
283
+ - If using relay, verify both agents are connected to the same relay hub
606
284
 
607
- server.start();
608
- ```
285
+ **Auth errors (401):**
286
+ - Ensure your API key is in the target agent's `auth.apiKeys` list
287
+ - Check `X-API-Key` header is being sent correctly
609
288
 
610
289
  ## License
611
290
 
612
291
  MIT
613
-
614
- ## Links
615
-
616
- - [A2A Protocol Specification](https://a2a-protocol.org/latest/specification/)
617
- - [A2A GitHub](https://github.com/a2aproject/A2A)
618
- - [A2A JS SDK](https://github.com/a2aproject/a2a-js)
619
- - [OpenClaw](https://github.com/openclaw/openclaw)
620
- - [PerkOS](https://perkos.xyz)