@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.
- package/Dockerfile +1 -1
- package/dist/bin/agentchat.d.ts +7 -0
- package/dist/bin/agentchat.d.ts.map +1 -0
- package/dist/bin/agentchat.js +1511 -0
- package/dist/bin/agentchat.js.map +1 -0
- package/dist/lib/allowlist.d.ts +77 -0
- package/dist/lib/allowlist.d.ts.map +1 -0
- package/dist/lib/allowlist.js +151 -0
- package/dist/lib/allowlist.js.map +1 -0
- package/dist/lib/client.d.ts +147 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/client.js +704 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/daemon.d.ts +122 -0
- package/dist/lib/daemon.d.ts.map +1 -0
- package/dist/lib/daemon.js +523 -0
- package/dist/lib/daemon.js.map +1 -0
- package/dist/lib/deploy/akash.d.ts +271 -0
- package/dist/lib/deploy/akash.d.ts.map +1 -0
- package/dist/lib/deploy/akash.js +671 -0
- package/dist/lib/deploy/akash.js.map +1 -0
- package/dist/lib/deploy/config.d.ts +62 -0
- package/dist/lib/deploy/config.d.ts.map +1 -0
- package/dist/lib/deploy/config.js +116 -0
- package/dist/lib/deploy/config.js.map +1 -0
- package/dist/lib/deploy/docker.d.ts +37 -0
- package/dist/lib/deploy/docker.d.ts.map +1 -0
- package/dist/lib/deploy/docker.js +122 -0
- package/dist/lib/deploy/docker.js.map +1 -0
- package/dist/lib/deploy/index.d.ts +11 -0
- package/dist/lib/deploy/index.d.ts.map +1 -0
- package/dist/lib/deploy/index.js +11 -0
- package/dist/lib/deploy/index.js.map +1 -0
- package/dist/lib/escrow-hooks.d.ts +199 -0
- package/dist/lib/escrow-hooks.d.ts.map +1 -0
- package/dist/lib/escrow-hooks.js +221 -0
- package/dist/lib/escrow-hooks.js.map +1 -0
- package/dist/lib/identity.d.ts +134 -0
- package/dist/lib/identity.d.ts.map +1 -0
- package/dist/lib/identity.js +334 -0
- package/dist/lib/identity.js.map +1 -0
- package/dist/lib/jitter.d.ts +42 -0
- package/dist/lib/jitter.d.ts.map +1 -0
- package/{lib/jitter.ts → dist/lib/jitter.js} +10 -18
- package/dist/lib/jitter.js.map +1 -0
- package/dist/lib/proposals.d.ts +223 -0
- package/dist/lib/proposals.d.ts.map +1 -0
- package/dist/lib/proposals.js +379 -0
- package/dist/lib/proposals.js.map +1 -0
- package/dist/lib/protocol.d.ts +220 -0
- package/dist/lib/protocol.d.ts.map +1 -0
- package/dist/lib/protocol.js +507 -0
- package/dist/lib/protocol.js.map +1 -0
- package/dist/lib/receipts.d.ts +134 -0
- package/dist/lib/receipts.d.ts.map +1 -0
- package/dist/lib/receipts.js +270 -0
- package/dist/lib/receipts.js.map +1 -0
- package/dist/lib/reputation.d.ts +250 -0
- package/dist/lib/reputation.d.ts.map +1 -0
- package/dist/lib/reputation.js +586 -0
- package/dist/lib/reputation.js.map +1 -0
- package/dist/lib/security.d.ts +27 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +150 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/server/handlers/admin.d.ts +26 -0
- package/dist/lib/server/handlers/admin.d.ts.map +1 -0
- package/dist/lib/server/handlers/admin.js +76 -0
- package/dist/lib/server/handlers/admin.js.map +1 -0
- package/dist/lib/server/handlers/identity.d.ts +36 -0
- package/dist/lib/server/handlers/identity.d.ts.map +1 -0
- package/dist/lib/server/handlers/identity.js +330 -0
- package/dist/lib/server/handlers/identity.js.map +1 -0
- package/dist/lib/server/handlers/index.d.ts +10 -0
- package/dist/lib/server/handlers/index.d.ts.map +1 -0
- package/dist/lib/server/handlers/index.js +15 -0
- package/dist/lib/server/handlers/index.js.map +1 -0
- package/dist/lib/server/handlers/message.d.ts +47 -0
- package/dist/lib/server/handlers/message.d.ts.map +1 -0
- package/dist/lib/server/handlers/message.js +265 -0
- package/dist/lib/server/handlers/message.js.map +1 -0
- package/dist/lib/server/handlers/presence.d.ts +18 -0
- package/dist/lib/server/handlers/presence.d.ts.map +1 -0
- package/dist/lib/server/handlers/presence.js +35 -0
- package/dist/lib/server/handlers/presence.js.map +1 -0
- package/dist/lib/server/handlers/proposal.d.ts +38 -0
- package/dist/lib/server/handlers/proposal.d.ts.map +1 -0
- package/dist/lib/server/handlers/proposal.js +273 -0
- package/dist/lib/server/handlers/proposal.js.map +1 -0
- package/dist/lib/server/handlers/skills.d.ts +22 -0
- package/dist/lib/server/handlers/skills.d.ts.map +1 -0
- package/dist/lib/server/handlers/skills.js +119 -0
- package/dist/lib/server/handlers/skills.js.map +1 -0
- package/dist/lib/server-directory.d.ts +85 -0
- package/dist/lib/server-directory.d.ts.map +1 -0
- package/dist/lib/server-directory.js +177 -0
- package/dist/lib/server-directory.js.map +1 -0
- package/dist/lib/server.d.ts +162 -0
- package/dist/lib/server.d.ts.map +1 -0
- package/dist/lib/server.js +602 -0
- package/dist/lib/server.js.map +1 -0
- package/dist/lib/types.d.ts +461 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +98 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +22 -13
- package/bin/agentchat.js +0 -1617
- package/bin/agentchat.ts +0 -1812
- package/lib/allowlist.js +0 -162
- package/lib/chat.py +0 -241
- package/lib/client.js +0 -821
- package/lib/client.ts +0 -877
- package/lib/daemon.js +0 -562
- package/lib/daemon.ts +0 -662
- package/lib/deploy/akash.js +0 -811
- package/lib/deploy/config.js +0 -128
- package/lib/deploy/docker.js +0 -132
- package/lib/deploy/index.js +0 -24
- package/lib/elo_swarm.py +0 -569
- package/lib/escrow-hooks.js +0 -237
- package/lib/escrow-hooks.ts +0 -391
- package/lib/identity.js +0 -376
- package/lib/identity.ts +0 -412
- package/lib/jitter.js +0 -54
- package/lib/proposals.js +0 -426
- package/lib/proposals.ts +0 -612
- package/lib/protocol.js +0 -516
- package/lib/receipts.js +0 -294
- package/lib/receipts.ts +0 -359
- package/lib/reputation.js +0 -664
- package/lib/reputation.ts +0 -790
- package/lib/security.js +0 -183
- package/lib/server/handlers/admin.js +0 -94
- package/lib/server/handlers/identity.js +0 -258
- package/lib/server/handlers/index.js +0 -42
- package/lib/server/handlers/message.js +0 -319
- package/lib/server/handlers/presence.js +0 -45
- package/lib/server/handlers/proposal.js +0 -358
- package/lib/server/handlers/skills.js +0 -141
- package/lib/server-directory.js +0 -190
- package/lib/server-directory.ts +0 -232
- package/lib/server.js +0 -633
- package/lib/server.ts +0 -698
- package/lib/supervisor/USAGE.md +0 -110
- package/lib/supervisor/agent-health.sh +0 -107
- package/lib/supervisor/agent-monitor.sh +0 -123
- package/lib/supervisor/agent-supervisor.sh +0 -135
- package/lib/supervisor/agentctl.sh +0 -266
- package/lib/supervisor/god-backup.sh +0 -126
- package/lib/supervisor/god-watchdog.sh +0 -107
- package/lib/supervisor/killswitch.sh +0 -43
- package/lib/supervisor/notify.sh +0 -19
- 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';
|