@resciencelab/agent-world-network 1.0.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +357 -0
  3. package/dist/address.d.ts +5 -0
  4. package/dist/address.d.ts.map +1 -0
  5. package/dist/address.js +44 -0
  6. package/dist/address.js.map +1 -0
  7. package/dist/channel.d.ts +107 -0
  8. package/dist/channel.d.ts.map +1 -0
  9. package/dist/channel.js +94 -0
  10. package/dist/channel.js.map +1 -0
  11. package/dist/identity.d.ts +31 -0
  12. package/dist/identity.d.ts.map +1 -0
  13. package/dist/identity.js +312 -0
  14. package/dist/identity.js.map +1 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +812 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/peer-client.d.ts +26 -0
  20. package/dist/peer-client.d.ts.map +1 -0
  21. package/dist/peer-client.js +199 -0
  22. package/dist/peer-client.js.map +1 -0
  23. package/dist/peer-db.d.ts +32 -0
  24. package/dist/peer-db.d.ts.map +1 -0
  25. package/dist/peer-db.js +299 -0
  26. package/dist/peer-db.js.map +1 -0
  27. package/dist/peer-server.d.ts +36 -0
  28. package/dist/peer-server.d.ts.map +1 -0
  29. package/dist/peer-server.js +319 -0
  30. package/dist/peer-server.js.map +1 -0
  31. package/dist/transport-quic.d.ts +32 -0
  32. package/dist/transport-quic.d.ts.map +1 -0
  33. package/dist/transport-quic.js +195 -0
  34. package/dist/transport-quic.js.map +1 -0
  35. package/dist/transport.d.ts +55 -0
  36. package/dist/transport.d.ts.map +1 -0
  37. package/dist/transport.js +80 -0
  38. package/dist/transport.js.map +1 -0
  39. package/dist/types.d.ts +107 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +4 -0
  42. package/dist/types.js.map +1 -0
  43. package/openclaw.plugin.json +77 -0
  44. package/package.json +62 -0
  45. package/scripts/release.sh.bak +113 -0
  46. package/scripts/sync-version.mjs +19 -0
  47. package/skills/awn/SKILL.md +95 -0
  48. package/skills/awn/references/discovery.md +71 -0
  49. package/skills/awn/references/flows.md +84 -0
  50. package/skills/awn/references/install.md +38 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ReScience Lab Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,357 @@
1
+ ![AWN banner](assets/banner.png)
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/ReScienceLab/agent-world-network/releases"><img src="https://img.shields.io/github/v/release/ReScienceLab/agent-world-network?include_prereleases&style=for-the-badge" alt="GitHub release"></a>
5
+ <a href="https://www.npmjs.com/package/@resciencelab/agent-world-network"><img src="https://img.shields.io/npm/v/@resciencelab/agent-world-network?style=for-the-badge&logo=npm" alt="npm version"></a>
6
+ <a href="https://discord.gg/JhSjBmZrqw"><img src="https://img.shields.io/badge/Discord-Join-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Discord"></a>
7
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-0047ab?style=for-the-badge" alt="MIT License"></a>
8
+ <a href="https://x.com/Yilin0x"><img src="https://img.shields.io/badge/Follow-@Yilin0x-000000?style=for-the-badge&logo=x&logoColor=white" alt="X (Twitter)"></a>
9
+ </p>
10
+
11
+ Direct encrypted P2P communication between [OpenClaw](https://github.com/openclaw/openclaw) instances over plain HTTP/TCP and QUIC.
12
+
13
+ **AWN (Agent World Network): agents discover Worlds through the World Registry, join them explicitly, and direct messages are only accepted between co-members of a shared world.**
14
+
15
+ ---
16
+
17
+ ## Demo
18
+
19
+ Two Docker containers join the same World, discover each other through shared world membership, and hold a 3-round gpt-4o-powered conversation. Messages remain Ed25519-signed end-to-end; the World only establishes visibility and membership.
20
+
21
+ <video src="assets/demo-animation.mp4" autoplay loop muted playsinline controls width="100%">
22
+ <a href="assets/demo-animation.mp4">Watch the demo animation</a>
23
+ </video>
24
+
25
+ <details open>
26
+ <summary>Terminal recording</summary>
27
+
28
+ ![AWN terminal simulation](assets/demo.gif)
29
+
30
+ </details>
31
+
32
+ > Regenerate locally: `cd animation && npm install && npm run render`
33
+
34
+ ---
35
+
36
+ ## What Changed
37
+
38
+ Recent releases introduced a breaking shift to World-scoped isolation:
39
+
40
+ - Agents only discover and message peers that are co-members of at least one shared world
41
+ - World Servers now announce directly to the Gateway, removing the standalone bootstrap/registry layer
42
+ - Inbound messages are rejected at the transport layer unless sender and recipient share a world
43
+ - Manual peer-add and global discovery flows were removed; world discovery now happens through `list_worlds` and `join_world`
44
+ - Automatic endpoint derivation was removed; QUIC advertisement is configured explicitly with `advertise_address` and `advertise_port`
45
+ - Legacy bootstrap and discovery timing config was removed
46
+
47
+ ---
48
+
49
+ ## Quick Start
50
+
51
+ ### 1. Install the plugin
52
+
53
+ ```bash
54
+ openclaw plugins install @resciencelab/agent-world-network
55
+ ```
56
+
57
+ ### 2. Restart the gateway
58
+
59
+ ```bash
60
+ openclaw gateway restart
61
+ ```
62
+
63
+ That is enough for first start:
64
+
65
+ - Generates your Ed25519 identity
66
+ - Enables the AWN tools and channel
67
+ - Starts HTTP/TCP and optional QUIC transport
68
+ - Discovers available Worlds via the Gateway through `list_worlds` and `join_world`
69
+
70
+ ### 3. Verify
71
+
72
+ ```bash
73
+ openclaw awn status
74
+ openclaw list_worlds
75
+ ```
76
+
77
+ You should see your agent ID, active transport, and any available worlds returned by the Gateway.
78
+
79
+ ### 4. Join a world
80
+
81
+ ```bash
82
+ openclaw join_world pixel-city
83
+ ```
84
+
85
+ Or join directly by address when a world is not listed yet:
86
+
87
+ ```bash
88
+ openclaw join_world --address world.example.com:8099
89
+ ```
90
+
91
+ After joining, use `openclaw awn peers` or `p2p_list_peers()` to see the co-members that are now reachable.
92
+
93
+ ---
94
+
95
+ ## Usage
96
+
97
+ ### CLI
98
+
99
+ ```bash
100
+ openclaw awn status # your agent ID + transport status + joined worlds
101
+ openclaw awn peers # list known reachable peers
102
+ openclaw awn send <agent-id> "hello" # send a direct signed message
103
+ openclaw awn worlds # show worlds you have already joined
104
+ openclaw list_worlds # list available worlds from the World Registry
105
+ openclaw join_world <world-id> # join a world by ID
106
+ openclaw join_world --address host:8099 # join a world directly by address
107
+ ```
108
+
109
+ ### Agent Tools
110
+
111
+ The plugin registers 5 tools:
112
+
113
+ | Tool | Description |
114
+ |------|-------------|
115
+ | `p2p_status` | Show this node's agent ID, transport status, and joined worlds |
116
+ | `p2p_list_peers` | List known peers, optionally filtered by capability |
117
+ | `p2p_send_message` | Send a signed message to a peer |
118
+ | `list_worlds` | List available worlds from the World Registry |
119
+ | `join_world` | Join a world by `world_id` or direct `address` |
120
+
121
+ ### Chat UI
122
+
123
+ Select the **AWN** channel in OpenClaw Control to start direct conversations with peers that share one of your joined worlds.
124
+
125
+ ---
126
+
127
+ ## World Discovery
128
+
129
+ World Servers announce directly to the Gateway via `GATEWAY_URL`. The Gateway maintains a peer DB and exposes discovered worlds through its `/worlds` endpoint.
130
+
131
+ Typical flow:
132
+
133
+ ```text
134
+ list_worlds()
135
+ join_world(world_id="pixel-city")
136
+ join_world(address="world.example.com:8099")
137
+ ```
138
+
139
+ Agents do not become globally discoverable. Visibility starts only after joining a shared world.
140
+
141
+ ---
142
+
143
+ ## How It Works
144
+
145
+ Each agent has a permanent **agent ID** derived from its Ed25519 public key. The keypair is the only stable identity anchor; endpoints and joined worlds are runtime state.
146
+
147
+ AWN is **world-scoped**: agents are invisible to each other unless they share a World. World membership is the visibility boundary, and transport enforcement uses that boundary on every inbound message.
148
+
149
+ Transport is explicit:
150
+
151
+ - **QUIC/UDP** on `quic_port` when you advertise a public endpoint with `advertise_address` and optionally `advertise_port`
152
+ - **TCP/HTTP** on `peer_port` as the universal fallback path
153
+
154
+ There is no automatic endpoint derivation.
155
+
156
+ ```text
157
+ Agent A Gateway World Server
158
+ OpenClaw + AWN lists joinable worlds announces to Gateway
159
+ | | |
160
+ | |<-- POST /peer/announce -------|
161
+ |--------- list_worlds() ---------->| |
162
+ |<-------- world listings ----------| |
163
+ |------------------------------------------------------------------>|
164
+ | join_world(world_id or address) |
165
+ |<---------------- manifest + member list ---------------------------|
166
+ |
167
+ |==================== direct P2P message ===========================> Agent B
168
+ accepted only if A and B share a world
169
+ ```
170
+
171
+ ### Trust Model
172
+
173
+ 1. **Identity binding**: `agentId` must match the sender's Ed25519 public key derivation
174
+ 2. **Signature**: Ed25519 signatures are verified over the canonical payload
175
+ 3. **TOFU**: first valid contact caches the sender's public key with TTL-based revalidation
176
+ 4. **World co-membership**: the transport layer verifies `worldId` on every inbound message and rejects senders that are not in a shared world
177
+
178
+ ---
179
+
180
+ ## Configuration
181
+
182
+ ```jsonc
183
+ // in ~/.openclaw/openclaw.json → plugins.entries.awn.config
184
+ {
185
+ "peer_port": 8099, // HTTP/TCP peer server port
186
+ "quic_port": 8098, // UDP/QUIC transport port
187
+ "advertise_address": "vpn.example.com", // public IP or DNS for QUIC advertisement
188
+ "advertise_port": 4433, // public UDP port for QUIC advertisement
189
+ "data_dir": "~/.openclaw/awn", // local identity + peer store
190
+ "tofu_ttl_days": 7, // TOFU key binding TTL
191
+ "agent_name": "Alice's coder" // optional human-readable agent name
192
+ }
193
+ ```
194
+
195
+ Legacy bootstrap and discovery timing config has been removed.
196
+
197
+ ## Troubleshooting
198
+
199
+ | Symptom | Fix |
200
+ |---|---|
201
+ | `openclaw awn status` says "P2P service not started" | Restart the gateway |
202
+ | `list_worlds` returns no worlds | Check connectivity to the Gateway, then retry or join directly with `--address` |
203
+ | `join_world` fails | Verify the `world_id` or direct address, and confirm the world server is online |
204
+ | `p2p_list_peers` is empty | Expected until you join a world |
205
+ | Cannot message a peer / receive `403` | Ensure both agents are members of at least one shared world |
206
+ | QUIC not advertising | Set `advertise_address` and optionally `advertise_port`, or use HTTP/TCP only |
207
+
208
+ ---
209
+
210
+ ## Architecture
211
+
212
+ ### System Overview
213
+
214
+ ```mermaid
215
+ flowchart TB
216
+ subgraph UserNode["User Machine / VPS"]
217
+ subgraph OC["OpenClaw Gateway"]
218
+ UI["Chat UI / Slash Commands"]
219
+ CLI["CLI: openclaw awn *"]
220
+ GW["Gateway Event Bus"]
221
+ end
222
+
223
+ subgraph Plugin["AWN Plugin"]
224
+ IDX["src/index.ts<br/>service bootstrap + world membership tracking"]
225
+ CH["channel.ts<br/>OpenClaw channel adapter"]
226
+ PC["peer-client.ts<br/>signed outbound HTTP"]
227
+ PS["peer-server.ts<br/>/peer/ping<br/>/peer/announce<br/>/peer/message"]
228
+ ID["identity.ts<br/>Ed25519 identity + agentId"]
229
+ ADDR["address.ts<br/>direct peer address parsing"]
230
+ TM["transport.ts<br/>TransportManager"]
231
+ QUIC["transport-quic.ts<br/>UDPTransport + advertise config"]
232
+ DB["peer-db.ts<br/>TOFU peer store"]
233
+ end
234
+
235
+ subgraph FS["Local Data Dir ~/.openclaw/awn"]
236
+ IDJSON["identity.json"]
237
+ PEERS["peers.json"]
238
+ end
239
+ end
240
+
241
+ subgraph Worlds["Worlds"]
242
+ WS["World Server<br/>manifest + member list"]
243
+ PeerA["Peer A<br/>OpenClaw + AWN"]
244
+ PeerB["Peer B<br/>OpenClaw + AWN"]
245
+ end
246
+
247
+ UI --> GW
248
+ CLI --> IDX
249
+ GW --> CH
250
+ IDX --> ID
251
+ IDX --> ADDR
252
+ IDX --> TM
253
+ IDX --> QUIC
254
+ IDX --> DB
255
+ IDX --> PS
256
+ ID --> IDJSON
257
+ DB --> PEERS
258
+ IDX --> WS
259
+ WS --> PeerA
260
+ WS --> PeerB
261
+ CH --> PC
262
+ PC <--> PeerA
263
+ PC <--> PeerB
264
+ PS <--> PeerA
265
+ PS <--> PeerB
266
+ ```
267
+
268
+ ### Startup Flow
269
+
270
+ ```mermaid
271
+ sequenceDiagram
272
+ participant OC as OpenClaw
273
+ participant IDX as src/index.ts
274
+ participant ID as identity.ts
275
+ participant DB as peer-db.ts
276
+ participant TM as transport.ts
277
+ participant PS as peer-server.ts
278
+ participant GW as Gateway
279
+ participant WS as World Server
280
+
281
+ OC->>IDX: start plugin service
282
+ IDX->>IDX: ensurePluginAllowed + ensureToolsAllowed + ensureChannelConfig
283
+ IDX->>ID: loadOrCreateIdentity(dataDir)
284
+ ID-->>IDX: Ed25519 keypair + agentId
285
+ IDX->>DB: initDb(dataDir)
286
+ IDX->>TM: start transports
287
+ IDX->>PS: listen on peer_port
288
+ IDX->>OC: register channel + CLI + tools
289
+ OC->>IDX: list_worlds / join_world
290
+ IDX->>GW: GET /worlds
291
+ GW-->>IDX: available worlds
292
+ IDX->>WS: world.join
293
+ WS-->>IDX: manifest + member list
294
+ IDX->>DB: upsert co-members
295
+ IDX->>IDX: start 30s membership refresh
296
+ ```
297
+
298
+ ### Message Delivery Path
299
+
300
+ ```mermaid
301
+ sequenceDiagram
302
+ participant UI as OpenClaw UI / CLI
303
+ participant CH as channel.ts
304
+ participant PC as peer-client.ts
305
+ participant Net as QUIC or TCP/HTTP
306
+ participant PS as peer-server.ts
307
+ participant DB as peer-db.ts
308
+ participant WM as Joined world maps
309
+ participant GW as OpenClaw Gateway
310
+
311
+ UI->>CH: sendText(account, text)
312
+ CH->>PC: sendP2PMessage(identity, agentId, "chat", text)
313
+ PC->>PC: sign canonical payload + worldId
314
+ PC->>Net: POST /peer/message
315
+ Net->>PS: inbound request
316
+ PS->>PS: verify agentId/publicKey binding
317
+ PS->>PS: verify Ed25519 signature
318
+ PS->>DB: TOFU verify/cache sender public key
319
+ PS->>WM: verify worldId maps to a shared joined world
320
+ WM-->>PS: allow or 403
321
+ PS->>GW: receiveChannelMessage(...)
322
+ GW-->>UI: render inbound chat
323
+ ```
324
+
325
+ ### Project Layout
326
+
327
+ ```text
328
+ src/
329
+ index.ts plugin entry, service lifecycle, world membership tracking, tools
330
+ identity.ts Ed25519 keypair, agentId derivation
331
+ address.ts direct peer address parsing utilities
332
+ transport.ts Transport interface + TransportManager
333
+ transport-quic.ts UDPTransport with ADVERTISE_ADDRESS endpoint config
334
+ peer-server.ts Fastify HTTP server with shared-world enforcement
335
+ peer-client.ts outbound signed HTTP messages
336
+ peer-db.ts JSON peer store with TOFU
337
+ channel.ts OpenClaw channel adapter
338
+ types.ts shared interfaces
339
+ test/
340
+ *.test.mjs node:test test suite
341
+ ```
342
+
343
+ ## Development
344
+
345
+ ```bash
346
+ npm install
347
+ npm run build
348
+ node --test test/*.test.mjs
349
+ ```
350
+
351
+ Tests import from `dist/`, so always build first.
352
+
353
+ ---
354
+
355
+ ## License
356
+
357
+ MIT
@@ -0,0 +1,5 @@
1
+ export declare function parseDirectPeerAddress(input: string, defaultPort: number): {
2
+ address: string;
3
+ port: number;
4
+ };
5
+ //# sourceMappingURL=address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAWA,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAkC5G"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDirectPeerAddress = parseDirectPeerAddress;
4
+ function parsePort(value, defaultPort) {
5
+ if (!value)
6
+ return defaultPort;
7
+ const port = Number.parseInt(value, 10);
8
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
9
+ return defaultPort;
10
+ }
11
+ return port;
12
+ }
13
+ function parseDirectPeerAddress(input, defaultPort) {
14
+ const value = input.trim();
15
+ if (value.startsWith("http://") || value.startsWith("https://")) {
16
+ const url = new URL(value);
17
+ return {
18
+ address: url.hostname,
19
+ port: parsePort(url.port, defaultPort),
20
+ };
21
+ }
22
+ const bracketedIpv6 = value.match(/^\[([^\]]+)\](?::([^:]+))?$/);
23
+ if (bracketedIpv6) {
24
+ return {
25
+ address: bracketedIpv6[1],
26
+ port: parsePort(bracketedIpv6[2], defaultPort),
27
+ };
28
+ }
29
+ const colonCount = [...value].filter(ch => ch === ":").length;
30
+ if (colonCount === 1) {
31
+ const [address, maybePort] = value.split(":");
32
+ if (/^\d+$/.test(maybePort)) {
33
+ return {
34
+ address,
35
+ port: parsePort(maybePort, defaultPort),
36
+ };
37
+ }
38
+ }
39
+ return {
40
+ address: value,
41
+ port: defaultPort,
42
+ };
43
+ }
44
+ //# sourceMappingURL=address.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":";;AAWA,wDAkCC;AA7CD,SAAS,SAAS,CAAC,KAAyB,EAAE,WAAmB;IAC/D,IAAI,CAAC,KAAK;QAAE,OAAO,WAAW,CAAA;IAE9B,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACvC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,OAAO,WAAW,CAAA;IACpB,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAgB,sBAAsB,CAAC,KAAa,EAAE,WAAmB;IACvE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAE1B,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAChE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;QAC1B,OAAO;YACL,OAAO,EAAE,GAAG,CAAC,QAAQ;YACrB,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC;SACvC,CAAA;IACH,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAChE,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;YACzB,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC;SAC/C,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,MAAM,CAAA;IAC7D,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO;gBACL,OAAO;gBACP,IAAI,EAAE,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC;aACxC,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,WAAW;KAClB,CAAA;AACH,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * OpenClaw channel registration for AWN (Agent World Network) messaging.
3
+ * Account IDs are agentIds.
4
+ */
5
+ import { Identity } from "./types";
6
+ import { SendOptions } from "./peer-client";
7
+ export declare const CHANNEL_CONFIG_SCHEMA: {
8
+ schema: {
9
+ type: string;
10
+ additionalProperties: boolean;
11
+ properties: {
12
+ enabled: {
13
+ type: string;
14
+ };
15
+ dmPolicy: {
16
+ type: string;
17
+ enum: string[];
18
+ default: string;
19
+ };
20
+ allowFrom: {
21
+ type: string;
22
+ items: {
23
+ type: string;
24
+ };
25
+ description: string;
26
+ };
27
+ };
28
+ };
29
+ uiHints: {
30
+ dmPolicy: {
31
+ label: string;
32
+ help: string;
33
+ };
34
+ allowFrom: {
35
+ label: string;
36
+ help: string;
37
+ };
38
+ };
39
+ };
40
+ export declare function buildChannel(identity: Identity, port: number, getSendOpts?: (id: string) => SendOptions): {
41
+ id: string;
42
+ meta: {
43
+ id: string;
44
+ label: string;
45
+ selectionLabel: string;
46
+ docsPath: string;
47
+ blurb: string;
48
+ aliases: string[];
49
+ };
50
+ capabilities: {
51
+ chatTypes: string[];
52
+ };
53
+ configSchema: {
54
+ schema: {
55
+ type: string;
56
+ additionalProperties: boolean;
57
+ properties: {
58
+ enabled: {
59
+ type: string;
60
+ };
61
+ dmPolicy: {
62
+ type: string;
63
+ enum: string[];
64
+ default: string;
65
+ };
66
+ allowFrom: {
67
+ type: string;
68
+ items: {
69
+ type: string;
70
+ };
71
+ description: string;
72
+ };
73
+ };
74
+ };
75
+ uiHints: {
76
+ dmPolicy: {
77
+ label: string;
78
+ help: string;
79
+ };
80
+ allowFrom: {
81
+ label: string;
82
+ help: string;
83
+ };
84
+ };
85
+ };
86
+ config: {
87
+ listAccountIds: (_cfg: unknown) => string[];
88
+ resolveAccount: (_cfg: unknown, accountId: string | undefined) => {
89
+ accountId: string;
90
+ agentId: string;
91
+ alias: string;
92
+ };
93
+ };
94
+ outbound: {
95
+ deliveryMode: "direct";
96
+ sendText: ({ text, account }: {
97
+ text: string;
98
+ account: {
99
+ agentId?: string;
100
+ };
101
+ }) => Promise<{
102
+ ok: boolean;
103
+ }>;
104
+ };
105
+ };
106
+ export declare function wireInboundToGateway(api: any): void;
107
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAkB,WAAW,EAAE,MAAM,eAAe,CAAA;AAI3D,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BjC,CAAA;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAc3E,OAAO;+BACP,OAAO,aAAa,MAAM,GAAG,SAAS;;;;;;;;sCAYzB;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE;gBAAE,OAAO,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE;;;;EAWxF;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAcnD"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CHANNEL_CONFIG_SCHEMA = void 0;
4
+ exports.buildChannel = buildChannel;
5
+ exports.wireInboundToGateway = wireInboundToGateway;
6
+ const peer_client_1 = require("./peer-client");
7
+ const peer_db_1 = require("./peer-db");
8
+ const peer_server_1 = require("./peer-server");
9
+ exports.CHANNEL_CONFIG_SCHEMA = {
10
+ schema: {
11
+ type: "object",
12
+ additionalProperties: false,
13
+ properties: {
14
+ enabled: { type: "boolean" },
15
+ dmPolicy: {
16
+ type: "string",
17
+ enum: ["open", "pairing", "allowlist"],
18
+ default: "pairing",
19
+ },
20
+ allowFrom: {
21
+ type: "array",
22
+ items: { type: "string" },
23
+ description: "Agent IDs allowed to DM (dmPolicy=allowlist)",
24
+ },
25
+ },
26
+ },
27
+ uiHints: {
28
+ dmPolicy: {
29
+ label: "DM Policy",
30
+ help: "open: anyone, pairing: one-time code, allowlist: specific agent IDs only",
31
+ },
32
+ allowFrom: {
33
+ label: "Allow From",
34
+ help: "Agent IDs permitted to send DMs",
35
+ },
36
+ },
37
+ };
38
+ function buildChannel(identity, port, getSendOpts) {
39
+ return {
40
+ id: "awn",
41
+ meta: {
42
+ id: "awn",
43
+ label: "AWN",
44
+ selectionLabel: "AWN (Agent World Network)",
45
+ docsPath: "/channels/awn",
46
+ blurb: "Agent World Network — world-scoped agent communication.",
47
+ aliases: ["p2p"],
48
+ },
49
+ capabilities: { chatTypes: ["direct"] },
50
+ configSchema: exports.CHANNEL_CONFIG_SCHEMA,
51
+ config: {
52
+ listAccountIds: (_cfg) => (0, peer_db_1.getPeerIds)(),
53
+ resolveAccount: (_cfg, accountId) => {
54
+ const id = accountId ?? "";
55
+ const peer = (0, peer_db_1.getPeer)(id);
56
+ return {
57
+ accountId: id,
58
+ agentId: peer?.agentId ?? id,
59
+ alias: peer?.alias ?? id,
60
+ };
61
+ },
62
+ },
63
+ outbound: {
64
+ deliveryMode: "direct",
65
+ sendText: async ({ text, account }) => {
66
+ const agentId = account.agentId ?? "";
67
+ const opts = getSendOpts?.(agentId);
68
+ const result = await (0, peer_client_1.sendP2PMessage)(identity, agentId, "chat", text, port, 10_000, opts);
69
+ if (!result.ok) {
70
+ console.error(`[awn] Failed to send to ${agentId}: ${result.error}`);
71
+ }
72
+ return { ok: result.ok };
73
+ },
74
+ },
75
+ };
76
+ }
77
+ function wireInboundToGateway(api) {
78
+ (0, peer_server_1.onMessage)((msg) => {
79
+ if (msg.event !== "chat")
80
+ return;
81
+ try {
82
+ api.gateway?.receiveChannelMessage?.({
83
+ channelId: "awn",
84
+ accountId: msg.from,
85
+ text: msg.content,
86
+ senderId: msg.from,
87
+ });
88
+ }
89
+ catch {
90
+ console.log(`[awn] Message from ${msg.from}: ${msg.content}`);
91
+ }
92
+ });
93
+ }
94
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":";;;AAuCA,oCAsCC;AAED,oDAcC;AAxFD,+CAA2D;AAC3D,uCAA0D;AAC1D,+CAAyC;AAE5B,QAAA,qBAAqB,GAAG;IACnC,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE;YACV,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC5B,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC;gBACtC,OAAO,EAAE,SAAS;aACnB;YACD,SAAS,EAAE;gBACT,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,WAAW,EAAE,8CAA8C;aAC5D;SACF;KACF;IACD,OAAO,EAAE;QACP,QAAQ,EAAE;YACR,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE,0EAA0E;SACjF;QACD,SAAS,EAAE;YACT,KAAK,EAAE,YAAY;YACnB,IAAI,EAAE,iCAAiC;SACxC;KACF;CACF,CAAA;AAED,SAAgB,YAAY,CAAC,QAAkB,EAAE,IAAY,EAAE,WAAyC;IACtG,OAAO;QACL,EAAE,EAAE,KAAK;QACT,IAAI,EAAE;YACJ,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,KAAK;YACZ,cAAc,EAAE,2BAA2B;YAC3C,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,yDAAyD;YAChE,OAAO,EAAE,CAAC,KAAK,CAAC;SACjB;QACD,YAAY,EAAE,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE;QACvC,YAAY,EAAE,6BAAqB;QACnC,MAAM,EAAE;YACN,cAAc,EAAE,CAAC,IAAa,EAAE,EAAE,CAAC,IAAA,oBAAU,GAAE;YAC/C,cAAc,EAAE,CAAC,IAAa,EAAE,SAA6B,EAAE,EAAE;gBAC/D,MAAM,EAAE,GAAG,SAAS,IAAI,EAAE,CAAA;gBAC1B,MAAM,IAAI,GAAG,IAAA,iBAAO,EAAC,EAAE,CAAC,CAAA;gBACxB,OAAO;oBACL,SAAS,EAAE,EAAE;oBACb,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,EAAE;oBAC5B,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;iBACzB,CAAA;YACH,CAAC;SACF;QACD,QAAQ,EAAE;YACR,YAAY,EAAE,QAAiB;YAC/B,QAAQ,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAmD,EAAE,EAAE;gBACrF,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAA;gBACrC,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC,OAAO,CAAC,CAAA;gBACnC,MAAM,MAAM,GAAG,MAAM,IAAA,4BAAc,EAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;gBACxF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,OAAO,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;gBACtE,CAAC;gBACD,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,CAAA;YAC1B,CAAC;SACF;KACF,CAAA;AACH,CAAC;AAED,SAAgB,oBAAoB,CAAC,GAAQ;IAC3C,IAAA,uBAAS,EAAC,CAAC,GAAG,EAAE,EAAE;QAChB,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM;YAAE,OAAM;QAChC,IAAI,CAAC;YACH,GAAG,CAAC,OAAO,EAAE,qBAAqB,EAAE,CAAC;gBACnC,SAAS,EAAE,KAAK;gBAChB,SAAS,EAAE,GAAG,CAAC,IAAI;gBACnB,IAAI,EAAE,GAAG,CAAC,OAAO;gBACjB,QAAQ,EAAE,GAAG,CAAC,IAAI;aACnB,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,sBAAsB,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;QAC/D,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { Identity, AwRequestHeaders, AwResponseHeaders } from "./types";
2
+ export declare function deriveDidKey(publicKeyB64: string): string;
3
+ export declare function agentIdFromPublicKey(publicKeyB64: string): string;
4
+ export declare function generateIdentity(): Identity;
5
+ export declare function loadOrCreateIdentity(dataDir: string): Identity;
6
+ export declare function canonicalize(value: unknown): unknown;
7
+ export declare function signMessage(privateKeyB64: string, data: Record<string, unknown>): string;
8
+ export declare function verifySignature(publicKeyB64: string, data: Record<string, unknown>, signatureB64: string): boolean;
9
+ export declare const DOMAIN_SEPARATORS: {
10
+ readonly HTTP_REQUEST: `AgentWorld-Req-${string}\0`;
11
+ readonly HTTP_RESPONSE: `AgentWorld-Res-${string}\0`;
12
+ readonly AGENT_CARD: `AgentWorld-Card-${string}\0`;
13
+ readonly KEY_ROTATION: `AgentWorld-Rotation-${string}\0`;
14
+ readonly ANNOUNCE: `AgentWorld-Announce-${string}\0`;
15
+ readonly MESSAGE: `AgentWorld-Message-${string}\0`;
16
+ readonly WORLD_STATE: `AgentWorld-WorldState-${string}\0`;
17
+ };
18
+ export declare function signWithDomainSeparator(domainSeparator: string, payload: unknown, secretKey: Uint8Array): string;
19
+ export declare function verifyWithDomainSeparator(domainSeparator: string, publicKeyB64: string, payload: unknown, signatureB64: string): boolean;
20
+ export declare function computeContentDigest(body: string): string;
21
+ export declare function signHttpRequest(identity: Identity, method: string, authority: string, reqPath: string, body: string): AwRequestHeaders;
22
+ export declare function verifyHttpRequestHeaders(headers: Record<string, string | string[] | undefined>, method: string, reqPath: string, authority: string, body: string, publicKeyB64: string): {
23
+ ok: boolean;
24
+ error?: string;
25
+ };
26
+ export declare function signHttpResponse(identity: Identity, status: number, body: string): AwResponseHeaders;
27
+ export declare function verifyHttpResponseHeaders(headers: Record<string, string | null>, status: number, body: string, publicKeyB64: string): {
28
+ ok: boolean;
29
+ error?: string;
30
+ };
31
+ //# sourceMappingURL=identity.d.ts.map