@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 +207 -536
- package/bin/relay.ts +88 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +111 -9
- package/dist/index.js.map +1 -1
- package/dist/relay-client.d.ts +51 -0
- package/dist/relay-client.d.ts.map +1 -0
- package/dist/relay-client.js +222 -0
- package/dist/relay-client.js.map +1 -0
- package/dist/relay.d.ts +49 -0
- package/dist/relay.d.ts.map +1 -0
- package/dist/relay.js +261 -0
- package/dist/relay.js.map +1 -0
- package/dist/server.d.ts +29 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +288 -65
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -1
- package/openclaw.plugin.json +66 -30
- package/package.json +9 -3
- package/skills/perkos-a2a/SKILL.md +68 -0
package/README.md
CHANGED
|
@@ -1,620 +1,291 @@
|
|
|
1
|
-
# @perkos/a2a
|
|
1
|
+
# @perkos/perkos-a2a
|
|
2
2
|
|
|
3
|
-
Agent-to-Agent (A2A) protocol
|
|
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
|
-
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
##
|
|
48
|
+
## Architecture
|
|
122
49
|
|
|
123
|
-
###
|
|
50
|
+
### Relay Hub (NAT Traversal)
|
|
124
51
|
|
|
125
|
-
|
|
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 "
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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 "
|
|
138
|
-
|
|
139
|
-
|
|
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 "
|
|
146
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
131
|
+
## Task Lifecycle
|
|
255
132
|
|
|
256
133
|
```mermaid
|
|
257
134
|
sequenceDiagram
|
|
258
|
-
participant
|
|
259
|
-
participant
|
|
260
|
-
participant
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
##
|
|
152
|
+
## Running the Relay Hub
|
|
293
153
|
|
|
294
|
-
|
|
154
|
+
The relay hub is a lightweight WebSocket broker. Deploy it on any VPS with a public IP.
|
|
295
155
|
|
|
296
|
-
|
|
156
|
+
```bash
|
|
157
|
+
# Via npx (development)
|
|
158
|
+
npx tsx bin/relay.ts --port 6060 --api-keys key1,key2
|
|
297
159
|
|
|
298
|
-
|
|
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
|
-
###
|
|
164
|
+
### Relay Hub Options
|
|
320
165
|
|
|
321
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
+
## Configuration
|
|
354
183
|
|
|
355
184
|
```json
|
|
356
185
|
{
|
|
357
|
-
"
|
|
358
|
-
"
|
|
359
|
-
"
|
|
360
|
-
"
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
|
372
|
-
|
|
373
|
-
| `
|
|
374
|
-
| `
|
|
375
|
-
| `
|
|
376
|
-
| `
|
|
377
|
-
| `
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
220
|
+
### Relay Auth
|
|
399
221
|
|
|
400
|
-
|
|
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
|
-
|
|
224
|
+
### HTTP Auth
|
|
416
225
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
##
|
|
231
|
+
## Session Injection
|
|
426
232
|
|
|
427
|
-
|
|
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
|
-
|
|
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
|
-
|
|
237
|
+
When the plugin is active, three tools are available to the agent:
|
|
436
238
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
243
|
+
## CLI Commands
|
|
444
244
|
|
|
445
245
|
```bash
|
|
446
|
-
openclaw
|
|
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
|
-
##
|
|
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
|
-
###
|
|
254
|
+
### Option 1: Relay Hub (Recommended for NAT)
|
|
456
255
|
|
|
457
|
-
|
|
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
|
-
|
|
258
|
+
### Option 2: Direct (Public IP / Same Network)
|
|
472
259
|
|
|
473
|
-
|
|
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
|
-
|
|
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
|
-
|
|
264
|
+
Both agents join the same tailnet. Use Tailscale IPs for peer URLs.
|
|
509
265
|
|
|
510
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
270
|
+
## Troubleshooting
|
|
592
271
|
|
|
593
|
-
|
|
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
|
-
|
|
596
|
-
|
|
277
|
+
**Port 5050 in use:**
|
|
278
|
+
Change the port in config, or run `lsof -i :5050` to find the conflicting process.
|
|
597
279
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
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)
|