@tjamescouch/agentchat 0.22.1 → 0.23.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 (153) hide show
  1. package/Dockerfile +1 -1
  2. package/dist/bin/agentchat.d.ts +7 -0
  3. package/dist/bin/agentchat.d.ts.map +1 -0
  4. package/dist/bin/agentchat.js +1511 -0
  5. package/dist/bin/agentchat.js.map +1 -0
  6. package/dist/lib/allowlist.d.ts +77 -0
  7. package/dist/lib/allowlist.d.ts.map +1 -0
  8. package/dist/lib/allowlist.js +151 -0
  9. package/dist/lib/allowlist.js.map +1 -0
  10. package/dist/lib/client.d.ts +147 -0
  11. package/dist/lib/client.d.ts.map +1 -0
  12. package/dist/lib/client.js +704 -0
  13. package/dist/lib/client.js.map +1 -0
  14. package/dist/lib/daemon.d.ts +122 -0
  15. package/dist/lib/daemon.d.ts.map +1 -0
  16. package/dist/lib/daemon.js +523 -0
  17. package/dist/lib/daemon.js.map +1 -0
  18. package/dist/lib/deploy/akash.d.ts +271 -0
  19. package/dist/lib/deploy/akash.d.ts.map +1 -0
  20. package/dist/lib/deploy/akash.js +671 -0
  21. package/dist/lib/deploy/akash.js.map +1 -0
  22. package/dist/lib/deploy/config.d.ts +62 -0
  23. package/dist/lib/deploy/config.d.ts.map +1 -0
  24. package/dist/lib/deploy/config.js +116 -0
  25. package/dist/lib/deploy/config.js.map +1 -0
  26. package/dist/lib/deploy/docker.d.ts +37 -0
  27. package/dist/lib/deploy/docker.d.ts.map +1 -0
  28. package/dist/lib/deploy/docker.js +122 -0
  29. package/dist/lib/deploy/docker.js.map +1 -0
  30. package/dist/lib/deploy/index.d.ts +11 -0
  31. package/dist/lib/deploy/index.d.ts.map +1 -0
  32. package/dist/lib/deploy/index.js +11 -0
  33. package/dist/lib/deploy/index.js.map +1 -0
  34. package/dist/lib/escrow-hooks.d.ts +199 -0
  35. package/dist/lib/escrow-hooks.d.ts.map +1 -0
  36. package/dist/lib/escrow-hooks.js +221 -0
  37. package/dist/lib/escrow-hooks.js.map +1 -0
  38. package/dist/lib/identity.d.ts +134 -0
  39. package/dist/lib/identity.d.ts.map +1 -0
  40. package/dist/lib/identity.js +334 -0
  41. package/dist/lib/identity.js.map +1 -0
  42. package/dist/lib/jitter.d.ts +42 -0
  43. package/dist/lib/jitter.d.ts.map +1 -0
  44. package/{lib/jitter.ts → dist/lib/jitter.js} +10 -18
  45. package/dist/lib/jitter.js.map +1 -0
  46. package/dist/lib/proposals.d.ts +223 -0
  47. package/dist/lib/proposals.d.ts.map +1 -0
  48. package/dist/lib/proposals.js +379 -0
  49. package/dist/lib/proposals.js.map +1 -0
  50. package/dist/lib/protocol.d.ts +220 -0
  51. package/dist/lib/protocol.d.ts.map +1 -0
  52. package/dist/lib/protocol.js +507 -0
  53. package/dist/lib/protocol.js.map +1 -0
  54. package/dist/lib/receipts.d.ts +134 -0
  55. package/dist/lib/receipts.d.ts.map +1 -0
  56. package/dist/lib/receipts.js +270 -0
  57. package/dist/lib/receipts.js.map +1 -0
  58. package/dist/lib/reputation.d.ts +250 -0
  59. package/dist/lib/reputation.d.ts.map +1 -0
  60. package/dist/lib/reputation.js +586 -0
  61. package/dist/lib/reputation.js.map +1 -0
  62. package/dist/lib/security.d.ts +27 -0
  63. package/dist/lib/security.d.ts.map +1 -0
  64. package/dist/lib/security.js +150 -0
  65. package/dist/lib/security.js.map +1 -0
  66. package/dist/lib/server/handlers/admin.d.ts +26 -0
  67. package/dist/lib/server/handlers/admin.d.ts.map +1 -0
  68. package/dist/lib/server/handlers/admin.js +76 -0
  69. package/dist/lib/server/handlers/admin.js.map +1 -0
  70. package/dist/lib/server/handlers/identity.d.ts +36 -0
  71. package/dist/lib/server/handlers/identity.d.ts.map +1 -0
  72. package/dist/lib/server/handlers/identity.js +330 -0
  73. package/dist/lib/server/handlers/identity.js.map +1 -0
  74. package/dist/lib/server/handlers/index.d.ts +10 -0
  75. package/dist/lib/server/handlers/index.d.ts.map +1 -0
  76. package/dist/lib/server/handlers/index.js +15 -0
  77. package/dist/lib/server/handlers/index.js.map +1 -0
  78. package/dist/lib/server/handlers/message.d.ts +47 -0
  79. package/dist/lib/server/handlers/message.d.ts.map +1 -0
  80. package/dist/lib/server/handlers/message.js +265 -0
  81. package/dist/lib/server/handlers/message.js.map +1 -0
  82. package/dist/lib/server/handlers/presence.d.ts +18 -0
  83. package/dist/lib/server/handlers/presence.d.ts.map +1 -0
  84. package/dist/lib/server/handlers/presence.js +35 -0
  85. package/dist/lib/server/handlers/presence.js.map +1 -0
  86. package/dist/lib/server/handlers/proposal.d.ts +38 -0
  87. package/dist/lib/server/handlers/proposal.d.ts.map +1 -0
  88. package/dist/lib/server/handlers/proposal.js +273 -0
  89. package/dist/lib/server/handlers/proposal.js.map +1 -0
  90. package/dist/lib/server/handlers/skills.d.ts +22 -0
  91. package/dist/lib/server/handlers/skills.d.ts.map +1 -0
  92. package/dist/lib/server/handlers/skills.js +119 -0
  93. package/dist/lib/server/handlers/skills.js.map +1 -0
  94. package/dist/lib/server-directory.d.ts +85 -0
  95. package/dist/lib/server-directory.d.ts.map +1 -0
  96. package/dist/lib/server-directory.js +177 -0
  97. package/dist/lib/server-directory.js.map +1 -0
  98. package/dist/lib/server.d.ts +162 -0
  99. package/dist/lib/server.d.ts.map +1 -0
  100. package/dist/lib/server.js +602 -0
  101. package/dist/lib/server.js.map +1 -0
  102. package/dist/lib/types.d.ts +461 -0
  103. package/dist/lib/types.d.ts.map +1 -0
  104. package/dist/lib/types.js +98 -0
  105. package/dist/lib/types.js.map +1 -0
  106. package/package.json +22 -13
  107. package/bin/agentchat.js +0 -1617
  108. package/bin/agentchat.ts +0 -1812
  109. package/lib/allowlist.js +0 -162
  110. package/lib/chat.py +0 -241
  111. package/lib/client.js +0 -821
  112. package/lib/client.ts +0 -877
  113. package/lib/daemon.js +0 -562
  114. package/lib/daemon.ts +0 -662
  115. package/lib/deploy/akash.js +0 -811
  116. package/lib/deploy/config.js +0 -128
  117. package/lib/deploy/docker.js +0 -132
  118. package/lib/deploy/index.js +0 -24
  119. package/lib/elo_swarm.py +0 -569
  120. package/lib/escrow-hooks.js +0 -237
  121. package/lib/escrow-hooks.ts +0 -391
  122. package/lib/identity.js +0 -376
  123. package/lib/identity.ts +0 -412
  124. package/lib/jitter.js +0 -54
  125. package/lib/proposals.js +0 -426
  126. package/lib/proposals.ts +0 -612
  127. package/lib/protocol.js +0 -516
  128. package/lib/receipts.js +0 -294
  129. package/lib/receipts.ts +0 -359
  130. package/lib/reputation.js +0 -664
  131. package/lib/reputation.ts +0 -790
  132. package/lib/security.js +0 -183
  133. package/lib/server/handlers/admin.js +0 -94
  134. package/lib/server/handlers/identity.js +0 -258
  135. package/lib/server/handlers/index.js +0 -42
  136. package/lib/server/handlers/message.js +0 -319
  137. package/lib/server/handlers/presence.js +0 -45
  138. package/lib/server/handlers/proposal.js +0 -358
  139. package/lib/server/handlers/skills.js +0 -141
  140. package/lib/server-directory.js +0 -190
  141. package/lib/server-directory.ts +0 -232
  142. package/lib/server.js +0 -633
  143. package/lib/server.ts +0 -698
  144. package/lib/supervisor/USAGE.md +0 -110
  145. package/lib/supervisor/agent-health.sh +0 -107
  146. package/lib/supervisor/agent-monitor.sh +0 -123
  147. package/lib/supervisor/agent-supervisor.sh +0 -135
  148. package/lib/supervisor/agentctl.sh +0 -266
  149. package/lib/supervisor/god-backup.sh +0 -126
  150. package/lib/supervisor/god-watchdog.sh +0 -107
  151. package/lib/supervisor/killswitch.sh +0 -43
  152. package/lib/supervisor/notify.sh +0 -19
  153. package/lib/types.ts +0 -433
package/lib/client.js DELETED
@@ -1,821 +0,0 @@
1
- /**
2
- * AgentChat Client
3
- * Connect to agentchat servers from Node.js or CLI
4
- */
5
-
6
- import WebSocket from 'ws';
7
- import { EventEmitter } from 'events';
8
- import {
9
- ClientMessageType,
10
- ServerMessageType,
11
- createMessage,
12
- serialize,
13
- parse,
14
- generateNonce
15
- } from './protocol.js';
16
- import { Identity } from './identity.js';
17
- import {
18
- getProposalSigningContent,
19
- getAcceptSigningContent,
20
- getRejectSigningContent,
21
- getCompleteSigningContent,
22
- getDisputeSigningContent
23
- } from './proposals.js';
24
-
25
- export class AgentChatClient extends EventEmitter {
26
- constructor(options = {}) {
27
- super();
28
- this.server = options.server;
29
- this.name = options.name || `agent-${Date.now()}`;
30
- this.pubkey = options.pubkey || null;
31
-
32
- // Identity support
33
- this.identityPath = options.identity || null;
34
- this._identity = null;
35
-
36
- this.ws = null;
37
- this.agentId = null;
38
- this.connected = false;
39
- this.channels = new Set();
40
-
41
- this._pendingRequests = new Map();
42
- this._requestId = 0;
43
- }
44
-
45
- /**
46
- * Load identity from file, or create new one if it doesn't exist
47
- */
48
- async _loadIdentity() {
49
- if (this.identityPath) {
50
- try {
51
- // Check if identity file exists
52
- const exists = await Identity.exists(this.identityPath);
53
- if (exists) {
54
- this._identity = await Identity.load(this.identityPath);
55
- } else {
56
- // Generate new identity and save it
57
- this._identity = Identity.generate(this.name);
58
- await this._identity.save(this.identityPath);
59
- }
60
- this.name = this._identity.name;
61
- this.pubkey = this._identity.pubkey;
62
- } catch (err) {
63
- throw new Error(`Failed to load/create identity at ${this.identityPath}: ${err.message}`);
64
- }
65
- }
66
- }
67
-
68
- /**
69
- * Connect to the server and identify
70
- */
71
- async connect() {
72
- // Load identity if path provided
73
- await this._loadIdentity();
74
-
75
- return new Promise((resolve, reject) => {
76
- this.ws = new WebSocket(this.server);
77
-
78
- this.ws.on('open', () => {
79
- // Send identify
80
- this._send({
81
- type: ClientMessageType.IDENTIFY,
82
- name: this.name,
83
- pubkey: this.pubkey
84
- });
85
- });
86
-
87
- this.ws.on('message', (data) => {
88
- this._handleMessage(data.toString());
89
- });
90
-
91
- this.ws.on('close', () => {
92
- this.connected = false;
93
- this.emit('disconnect');
94
- });
95
-
96
- this.ws.on('error', (err) => {
97
- this.emit('error', err);
98
- if (!this.connected) {
99
- reject(err);
100
- }
101
- });
102
-
103
- // Wait for WELCOME
104
- this.once('welcome', (info) => {
105
- this.connected = true;
106
- this.agentId = info.agent_id;
107
- resolve(info);
108
- });
109
-
110
- // Handle connection error
111
- this.once('error', (err) => {
112
- if (!this.connected) {
113
- reject(err);
114
- }
115
- });
116
- });
117
- }
118
-
119
- /**
120
- * Disconnect from server
121
- */
122
- disconnect() {
123
- if (this.ws) {
124
- this.ws.close();
125
- this.ws = null;
126
- }
127
- }
128
-
129
- /**
130
- * Join a channel
131
- */
132
- async join(channel) {
133
- this._send({
134
- type: ClientMessageType.JOIN,
135
- channel
136
- });
137
-
138
- return new Promise((resolve, reject) => {
139
- const onJoined = (msg) => {
140
- if (msg.channel === channel) {
141
- this.removeListener('error', onError);
142
- this.channels.add(channel);
143
- resolve(msg);
144
- }
145
- };
146
-
147
- const onError = (msg) => {
148
- this.removeListener('joined', onJoined);
149
- reject(new Error(msg.message));
150
- };
151
-
152
- this.once('joined', onJoined);
153
- this.once('error', onError);
154
- });
155
- }
156
-
157
- /**
158
- * Leave a channel
159
- */
160
- async leave(channel) {
161
- this._send({
162
- type: ClientMessageType.LEAVE,
163
- channel
164
- });
165
- this.channels.delete(channel);
166
- }
167
-
168
- /**
169
- * Send a message to a channel or agent
170
- */
171
- async send(to, content) {
172
- const msg = {
173
- type: ClientMessageType.MSG,
174
- to,
175
- content
176
- };
177
-
178
- // Sign message if identity available
179
- if (this._identity && this._identity.privkey) {
180
- msg.ts = Date.now();
181
- const dataToSign = JSON.stringify({
182
- to: msg.to,
183
- content: msg.content,
184
- ts: msg.ts
185
- });
186
- msg.sig = this._identity.sign(dataToSign);
187
- }
188
-
189
- this._send(msg);
190
- }
191
-
192
- /**
193
- * Send a direct message (alias for send with @target)
194
- */
195
- async dm(agent, content) {
196
- const target = agent.startsWith('@') ? agent : `@${agent}`;
197
- return this.send(target, content);
198
- }
199
-
200
- /**
201
- * List available channels
202
- */
203
- async listChannels() {
204
- this._send({
205
- type: ClientMessageType.LIST_CHANNELS
206
- });
207
-
208
- return new Promise((resolve) => {
209
- this.once('channels', (msg) => {
210
- resolve(msg.list);
211
- });
212
- });
213
- }
214
-
215
- /**
216
- * List agents in a channel
217
- */
218
- async listAgents(channel) {
219
- this._send({
220
- type: ClientMessageType.LIST_AGENTS,
221
- channel
222
- });
223
-
224
- return new Promise((resolve) => {
225
- this.once('agents', (msg) => {
226
- resolve(msg.list);
227
- });
228
- });
229
- }
230
-
231
- /**
232
- * Create a new channel
233
- */
234
- async createChannel(channel, inviteOnly = false) {
235
- this._send({
236
- type: ClientMessageType.CREATE_CHANNEL,
237
- channel,
238
- invite_only: inviteOnly
239
- });
240
-
241
- return new Promise((resolve, reject) => {
242
- const onJoined = (msg) => {
243
- if (msg.channel === channel) {
244
- this.removeListener('error', onError);
245
- this.channels.add(channel);
246
- resolve(msg);
247
- }
248
- };
249
-
250
- const onError = (msg) => {
251
- this.removeListener('joined', onJoined);
252
- reject(new Error(msg.message));
253
- };
254
-
255
- this.once('joined', onJoined);
256
- this.once('error', onError);
257
- });
258
- }
259
-
260
- /**
261
- * Invite an agent to a channel
262
- */
263
- async invite(channel, agent) {
264
- const target = agent.startsWith('@') ? agent : `@${agent}`;
265
- this._send({
266
- type: ClientMessageType.INVITE,
267
- channel,
268
- agent: target
269
- });
270
- }
271
-
272
- /**
273
- * Send ping to server
274
- */
275
- ping() {
276
- this._send({ type: ClientMessageType.PING });
277
- }
278
-
279
- // ===== PROPOSAL/NEGOTIATION METHODS =====
280
-
281
- /**
282
- * Send a proposal to another agent
283
- * Requires persistent identity for signing
284
- *
285
- * @param {string} to - Target agent (@id)
286
- * @param {object} proposal - Proposal details
287
- * @param {string} proposal.task - Description of the task/work
288
- * @param {number} [proposal.amount] - Payment amount
289
- * @param {string} [proposal.currency] - Currency (SOL, USDC, AKT, etc)
290
- * @param {string} [proposal.payment_code] - BIP47 payment code or address
291
- * @param {string} [proposal.terms] - Additional terms
292
- * @param {number} [proposal.expires] - Seconds until expiration
293
- */
294
- async propose(to, proposal) {
295
- if (!this._identity || !this._identity.privkey) {
296
- throw new Error('Proposals require persistent identity. Use --identity flag.');
297
- }
298
-
299
- const target = to.startsWith('@') ? to : `@${to}`;
300
-
301
- const msg = {
302
- type: ClientMessageType.PROPOSAL,
303
- to: target,
304
- task: proposal.task,
305
- amount: proposal.amount,
306
- currency: proposal.currency,
307
- payment_code: proposal.payment_code,
308
- terms: proposal.terms,
309
- expires: proposal.expires,
310
- elo_stake: proposal.elo_stake
311
- };
312
-
313
- // Sign the proposal
314
- const sigContent = getProposalSigningContent(msg);
315
- msg.sig = this._identity.sign(sigContent);
316
-
317
- this._send(msg);
318
-
319
- // Wait for the proposal response with ID
320
- return new Promise((resolve, reject) => {
321
- const timeout = setTimeout(() => {
322
- this.removeListener('proposal', onProposal);
323
- this.removeListener('error', onError);
324
- reject(new Error('Proposal timeout'));
325
- }, 10000);
326
-
327
- const onProposal = (p) => {
328
- if (p.to === target && p.from === this.agentId) {
329
- clearTimeout(timeout);
330
- this.removeListener('error', onError);
331
- resolve(p);
332
- }
333
- };
334
-
335
- const onError = (err) => {
336
- clearTimeout(timeout);
337
- this.removeListener('proposal', onProposal);
338
- reject(new Error(err.message));
339
- };
340
-
341
- this.once('proposal', onProposal);
342
- this.once('error', onError);
343
- });
344
- }
345
-
346
- /**
347
- * Accept a proposal
348
- * @param {string} proposalId - The proposal ID to accept
349
- * @param {string} [payment_code] - Your payment code for receiving payment
350
- * @param {number} [elo_stake] - ELO points to stake as acceptor
351
- */
352
- async accept(proposalId, payment_code = null, elo_stake = null) {
353
- if (!this._identity || !this._identity.privkey) {
354
- throw new Error('Accepting proposals requires persistent identity.');
355
- }
356
-
357
- const sigContent = getAcceptSigningContent(proposalId, payment_code || '', elo_stake || '');
358
- const sig = this._identity.sign(sigContent);
359
-
360
- const msg = {
361
- type: ClientMessageType.ACCEPT,
362
- proposal_id: proposalId,
363
- ...(payment_code != null && { payment_code }),
364
- ...(elo_stake != null && { elo_stake }),
365
- sig
366
- };
367
-
368
- this._send(msg);
369
-
370
- return new Promise((resolve, reject) => {
371
- const timeout = setTimeout(() => {
372
- this.removeListener('accept', onAccept);
373
- this.removeListener('error', onError);
374
- reject(new Error('Accept timeout'));
375
- }, 10000);
376
-
377
- const onAccept = (response) => {
378
- if (response.proposal_id === proposalId) {
379
- clearTimeout(timeout);
380
- this.removeListener('error', onError);
381
- resolve(response);
382
- }
383
- };
384
-
385
- const onError = (err) => {
386
- clearTimeout(timeout);
387
- this.removeListener('accept', onAccept);
388
- reject(new Error(err.message));
389
- };
390
-
391
- this.once('accept', onAccept);
392
- this.once('error', onError);
393
- });
394
- }
395
-
396
- /**
397
- * Reject a proposal
398
- * @param {string} proposalId - The proposal ID to reject
399
- * @param {string} [reason] - Reason for rejection
400
- */
401
- async reject(proposalId, reason = null) {
402
- if (!this._identity || !this._identity.privkey) {
403
- throw new Error('Rejecting proposals requires persistent identity.');
404
- }
405
-
406
- const sigContent = getRejectSigningContent(proposalId, reason || '');
407
- const sig = this._identity.sign(sigContent);
408
-
409
- const msg = {
410
- type: ClientMessageType.REJECT,
411
- proposal_id: proposalId,
412
- reason,
413
- sig
414
- };
415
-
416
- this._send(msg);
417
-
418
- return new Promise((resolve, reject) => {
419
- const timeout = setTimeout(() => {
420
- this.removeListener('reject', onReject);
421
- this.removeListener('error', onError);
422
- reject(new Error('Reject timeout'));
423
- }, 10000);
424
-
425
- const onReject = (response) => {
426
- if (response.proposal_id === proposalId) {
427
- clearTimeout(timeout);
428
- this.removeListener('error', onError);
429
- resolve(response);
430
- }
431
- };
432
-
433
- const onError = (err) => {
434
- clearTimeout(timeout);
435
- this.removeListener('reject', onReject);
436
- reject(new Error(err.message));
437
- };
438
-
439
- this.once('reject', onReject);
440
- this.once('error', onError);
441
- });
442
- }
443
-
444
- /**
445
- * Mark a proposal as complete
446
- * @param {string} proposalId - The proposal ID to complete
447
- * @param {string} [proof] - Proof of completion (tx hash, URL, etc)
448
- */
449
- async complete(proposalId, proof = null) {
450
- if (!this._identity || !this._identity.privkey) {
451
- throw new Error('Completing proposals requires persistent identity.');
452
- }
453
-
454
- const sigContent = getCompleteSigningContent(proposalId, proof || '');
455
- const sig = this._identity.sign(sigContent);
456
-
457
- const msg = {
458
- type: ClientMessageType.COMPLETE,
459
- proposal_id: proposalId,
460
- proof,
461
- sig
462
- };
463
-
464
- this._send(msg);
465
-
466
- return new Promise((resolve, reject) => {
467
- const timeout = setTimeout(() => {
468
- this.removeListener('complete', onComplete);
469
- this.removeListener('error', onError);
470
- reject(new Error('Complete timeout'));
471
- }, 10000);
472
-
473
- const onComplete = (response) => {
474
- if (response.proposal_id === proposalId) {
475
- clearTimeout(timeout);
476
- this.removeListener('error', onError);
477
- resolve(response);
478
- }
479
- };
480
-
481
- const onError = (err) => {
482
- clearTimeout(timeout);
483
- this.removeListener('complete', onComplete);
484
- reject(new Error(err.message));
485
- };
486
-
487
- this.once('complete', onComplete);
488
- this.once('error', onError);
489
- });
490
- }
491
-
492
- /**
493
- * Dispute a proposal
494
- * @param {string} proposalId - The proposal ID to dispute
495
- * @param {string} reason - Reason for the dispute
496
- */
497
- async dispute(proposalId, reason) {
498
- if (!this._identity || !this._identity.privkey) {
499
- throw new Error('Disputing proposals requires persistent identity.');
500
- }
501
-
502
- if (!reason) {
503
- throw new Error('Dispute reason is required');
504
- }
505
-
506
- const sigContent = getDisputeSigningContent(proposalId, reason);
507
- const sig = this._identity.sign(sigContent);
508
-
509
- const msg = {
510
- type: ClientMessageType.DISPUTE,
511
- proposal_id: proposalId,
512
- reason,
513
- sig
514
- };
515
-
516
- this._send(msg);
517
-
518
- return new Promise((resolve, reject) => {
519
- const timeout = setTimeout(() => {
520
- this.removeListener('dispute', onDispute);
521
- this.removeListener('error', onError);
522
- reject(new Error('Dispute timeout'));
523
- }, 10000);
524
-
525
- const onDispute = (response) => {
526
- if (response.proposal_id === proposalId) {
527
- clearTimeout(timeout);
528
- this.removeListener('error', onError);
529
- resolve(response);
530
- }
531
- };
532
-
533
- const onError = (err) => {
534
- clearTimeout(timeout);
535
- this.removeListener('dispute', onDispute);
536
- reject(new Error(err.message));
537
- };
538
-
539
- this.once('dispute', onDispute);
540
- this.once('error', onError);
541
- });
542
- }
543
-
544
- // ===== IDENTITY VERIFICATION METHODS =====
545
-
546
- /**
547
- * Request identity verification from another agent
548
- * Sends a challenge nonce that the target must sign to prove they control their identity
549
- * @param {string} target - Target agent to verify (@id)
550
- * @returns {Promise<object>} Verification result with pubkey if successful
551
- */
552
- async verify(target) {
553
- const targetAgent = target.startsWith('@') ? target : `@${target}`;
554
- const nonce = generateNonce();
555
-
556
- const msg = {
557
- type: ClientMessageType.VERIFY_REQUEST,
558
- target: targetAgent,
559
- nonce
560
- };
561
-
562
- this._send(msg);
563
-
564
- return new Promise((resolve, reject) => {
565
- const timeout = setTimeout(() => {
566
- this.removeListener('verify_success', onSuccess);
567
- this.removeListener('verify_failed', onFailed);
568
- this.removeListener('error', onError);
569
- reject(new Error('Verification timeout'));
570
- }, 35000); // Slightly longer than server timeout
571
-
572
- const onSuccess = (response) => {
573
- if (response.agent === targetAgent || response.target === targetAgent) {
574
- clearTimeout(timeout);
575
- this.removeListener('verify_failed', onFailed);
576
- this.removeListener('error', onError);
577
- resolve({
578
- verified: true,
579
- agent: response.agent,
580
- pubkey: response.pubkey,
581
- request_id: response.request_id
582
- });
583
- }
584
- };
585
-
586
- const onFailed = (response) => {
587
- if (response.target === targetAgent) {
588
- clearTimeout(timeout);
589
- this.removeListener('verify_success', onSuccess);
590
- this.removeListener('error', onError);
591
- resolve({
592
- verified: false,
593
- target: response.target,
594
- reason: response.reason,
595
- request_id: response.request_id
596
- });
597
- }
598
- };
599
-
600
- const onError = (err) => {
601
- clearTimeout(timeout);
602
- this.removeListener('verify_success', onSuccess);
603
- this.removeListener('verify_failed', onFailed);
604
- reject(new Error(err.message));
605
- };
606
-
607
- this.on('verify_success', onSuccess);
608
- this.on('verify_failed', onFailed);
609
- this.once('error', onError);
610
- });
611
- }
612
-
613
- /**
614
- * Respond to a verification request by signing the nonce
615
- * This is typically called automatically when a VERIFY_REQUEST is received
616
- * @param {string} requestId - The verification request ID
617
- * @param {string} nonce - The nonce to sign
618
- */
619
- async respondToVerification(requestId, nonce) {
620
- if (!this._identity || !this._identity.privkey) {
621
- throw new Error('Responding to verification requires persistent identity.');
622
- }
623
-
624
- const sig = this._identity.sign(nonce);
625
-
626
- const msg = {
627
- type: ClientMessageType.VERIFY_RESPONSE,
628
- request_id: requestId,
629
- nonce,
630
- sig
631
- };
632
-
633
- this._send(msg);
634
- }
635
-
636
- /**
637
- * Enable automatic verification response
638
- * When enabled, the client will automatically respond to VERIFY_REQUEST messages
639
- * @param {boolean} enabled - Whether to enable auto-response
640
- */
641
- enableAutoVerification(enabled = true) {
642
- if (enabled) {
643
- this._autoVerifyHandler = (msg) => {
644
- if (msg.request_id && msg.nonce && msg.from) {
645
- this.respondToVerification(msg.request_id, msg.nonce)
646
- .catch(err => this.emit('error', { message: `Auto-verification failed: ${err.message}` }));
647
- }
648
- };
649
- this.on('verify_request', this._autoVerifyHandler);
650
- } else if (this._autoVerifyHandler) {
651
- this.removeListener('verify_request', this._autoVerifyHandler);
652
- this._autoVerifyHandler = null;
653
- }
654
- }
655
-
656
- _send(msg) {
657
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
658
- this.ws.send(serialize(msg));
659
- }
660
- }
661
-
662
- /**
663
- * Send a raw message (for protocol extensions)
664
- */
665
- sendRaw(msg) {
666
- this._send(msg);
667
- }
668
-
669
- _handleMessage(data) {
670
- let msg;
671
- try {
672
- msg = parse(data);
673
- } catch (e) {
674
- this.emit('error', { message: 'Invalid JSON from server' });
675
- return;
676
- }
677
-
678
- // Emit raw message
679
- this.emit('raw', msg);
680
-
681
- // Handle by type
682
- switch (msg.type) {
683
- case ServerMessageType.WELCOME:
684
- this.emit('welcome', msg);
685
- break;
686
-
687
- case ServerMessageType.MSG:
688
- this.emit('message', msg);
689
- break;
690
-
691
- case ServerMessageType.JOINED:
692
- this.emit('joined', msg);
693
- break;
694
-
695
- case ServerMessageType.LEFT:
696
- this.emit('left', msg);
697
- break;
698
-
699
- case ServerMessageType.AGENT_JOINED:
700
- this.emit('agent_joined', msg);
701
- break;
702
-
703
- case ServerMessageType.AGENT_LEFT:
704
- this.emit('agent_left', msg);
705
- break;
706
-
707
- case ServerMessageType.CHANNELS:
708
- this.emit('channels', msg);
709
- break;
710
-
711
- case ServerMessageType.AGENTS:
712
- this.emit('agents', msg);
713
- break;
714
-
715
- case ServerMessageType.ERROR:
716
- this.emit('error', msg);
717
- break;
718
-
719
- case ServerMessageType.PONG:
720
- this.emit('pong', msg);
721
- break;
722
-
723
- // Proposal/negotiation messages
724
- case ServerMessageType.PROPOSAL:
725
- this.emit('proposal', msg);
726
- break;
727
-
728
- case ServerMessageType.ACCEPT:
729
- this.emit('accept', msg);
730
- break;
731
-
732
- case ServerMessageType.REJECT:
733
- this.emit('reject', msg);
734
- break;
735
-
736
- case ServerMessageType.COMPLETE:
737
- this.emit('complete', msg);
738
- break;
739
-
740
- case ServerMessageType.DISPUTE:
741
- this.emit('dispute', msg);
742
- break;
743
-
744
- // Skills discovery messages
745
- case ServerMessageType.SKILLS_REGISTERED:
746
- this.emit('skills_registered', msg);
747
- this.emit('message', msg);
748
- break;
749
-
750
- case ServerMessageType.SEARCH_RESULTS:
751
- this.emit('search_results', msg);
752
- this.emit('message', msg);
753
- break;
754
-
755
- // Identity verification messages
756
- case ServerMessageType.VERIFY_REQUEST:
757
- this.emit('verify_request', msg);
758
- break;
759
-
760
- case ServerMessageType.VERIFY_RESPONSE:
761
- this.emit('verify_response', msg);
762
- break;
763
-
764
- case ServerMessageType.VERIFY_SUCCESS:
765
- this.emit('verify_success', msg);
766
- break;
767
-
768
- case ServerMessageType.VERIFY_FAILED:
769
- this.emit('verify_failed', msg);
770
- break;
771
- }
772
- }
773
- }
774
-
775
- /**
776
- * Quick send - connect, send message, disconnect
777
- */
778
- export async function quickSend(server, name, to, content, identityPath = null) {
779
- const client = new AgentChatClient({ server, name, identity: identityPath });
780
- await client.connect();
781
-
782
- // Join channel if needed
783
- if (to.startsWith('#')) {
784
- await client.join(to);
785
- }
786
-
787
- await client.send(to, content);
788
-
789
- // Small delay to ensure message is sent
790
- await new Promise(r => setTimeout(r, 100));
791
-
792
- client.disconnect();
793
- }
794
-
795
- /**
796
- * Listen mode - connect, join channels, stream messages
797
- */
798
- export async function listen(server, name, channels, callback, identityPath = null) {
799
- const client = new AgentChatClient({ server, name, identity: identityPath });
800
- await client.connect();
801
-
802
- for (const channel of channels) {
803
- await client.join(channel);
804
- }
805
-
806
- client.on('message', callback);
807
- client.on('agent_joined', callback);
808
- client.on('agent_left', callback);
809
-
810
- // Also stream proposal events
811
- client.on('proposal', callback);
812
- client.on('accept', callback);
813
- client.on('reject', callback);
814
- client.on('complete', callback);
815
- client.on('dispute', callback);
816
-
817
- return client;
818
- }
819
-
820
- // Re-export security utilities
821
- export { checkDirectorySafety, enforceDirectorySafety } from './security.js';