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