@tjamescouch/agentchat 0.22.0 → 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 → dist/lib}/jitter.js +16 -19
- 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/lib/chat.py +0 -241
- package/lib/client.js +0 -821
- package/lib/daemon.js +0 -562
- 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/identity.js +0 -376
- package/lib/proposals.js +0 -426
- package/lib/protocol.js +0 -484
- package/lib/receipts.js +0 -294
- package/lib/reputation.js +0 -664
- package/lib/security.js +0 -183
- package/lib/server/handlers/identity.js +0 -242
- package/lib/server/handlers/index.js +0 -42
- package/lib/server/handlers/message.js +0 -306
- package/lib/server/handlers/presence.js +0 -44
- package/lib/server/handlers/proposal.js +0 -358
- package/lib/server/handlers/skills.js +0 -141
- package/lib/server-directory.js +0 -181
- package/lib/server.js +0 -528
- package/lib/supervisor/USAGE.md +0 -110
- package/lib/supervisor/agent-supervisor.sh +0 -135
- package/lib/supervisor/agentctl.sh +0 -250
- package/lib/supervisor/killswitch.sh +0 -36
- package/lib/supervisor/notify.sh +0 -19
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Message Handlers
|
|
3
|
-
* Handles message routing, join, leave, and channel operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
ServerMessageType,
|
|
8
|
-
ErrorCode,
|
|
9
|
-
createMessage,
|
|
10
|
-
createError,
|
|
11
|
-
isChannel,
|
|
12
|
-
isAgent,
|
|
13
|
-
} from '../../protocol.js';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Handle MSG command - route messages to channels or agents
|
|
17
|
-
*/
|
|
18
|
-
export function handleMsg(server, ws, msg) {
|
|
19
|
-
const agent = server.agents.get(ws);
|
|
20
|
-
if (!agent) {
|
|
21
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Rate limiting: 1 message per second per agent
|
|
26
|
-
const now = Date.now();
|
|
27
|
-
const lastTime = server.lastMessageTime.get(ws) || 0;
|
|
28
|
-
if (now - lastTime < server.rateLimitMs) {
|
|
29
|
-
server._send(ws, createError(ErrorCode.RATE_LIMITED, 'Rate limit exceeded (max 1 message per second)'));
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
server.lastMessageTime.set(ws, now);
|
|
33
|
-
|
|
34
|
-
const outMsg = createMessage(ServerMessageType.MSG, {
|
|
35
|
-
from: `@${agent.id}`,
|
|
36
|
-
to: msg.to,
|
|
37
|
-
content: msg.content,
|
|
38
|
-
...(msg.sig && { sig: msg.sig })
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
if (isChannel(msg.to)) {
|
|
42
|
-
// Channel message
|
|
43
|
-
const channel = server.channels.get(msg.to);
|
|
44
|
-
if (!channel) {
|
|
45
|
-
server._send(ws, createError(ErrorCode.CHANNEL_NOT_FOUND, `Channel ${msg.to} not found`));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!agent.channels.has(msg.to)) {
|
|
50
|
-
server._send(ws, createError(ErrorCode.NOT_INVITED, `Not a member of ${msg.to}`));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Broadcast to channel including sender
|
|
55
|
-
server._broadcast(msg.to, outMsg);
|
|
56
|
-
|
|
57
|
-
// Buffer the message for replay to future joiners
|
|
58
|
-
server._bufferMessage(msg.to, outMsg);
|
|
59
|
-
|
|
60
|
-
// Update channel activity timestamp (for idle detection)
|
|
61
|
-
server.channelLastActivity.set(msg.to, Date.now());
|
|
62
|
-
|
|
63
|
-
} else if (isAgent(msg.to)) {
|
|
64
|
-
// Direct message
|
|
65
|
-
const targetId = msg.to.slice(1);
|
|
66
|
-
const targetWs = server.agentById.get(targetId);
|
|
67
|
-
|
|
68
|
-
if (!targetWs) {
|
|
69
|
-
server._send(ws, createError(ErrorCode.AGENT_NOT_FOUND, `Agent ${msg.to} not found`));
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Send to target
|
|
74
|
-
server._send(targetWs, outMsg);
|
|
75
|
-
// Echo back to sender
|
|
76
|
-
server._send(ws, outMsg);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Handle JOIN command - add agent to channel
|
|
82
|
-
*/
|
|
83
|
-
export function handleJoin(server, ws, msg) {
|
|
84
|
-
const agent = server.agents.get(ws);
|
|
85
|
-
if (!agent) {
|
|
86
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const channel = server.channels.get(msg.channel);
|
|
91
|
-
if (!channel) {
|
|
92
|
-
server._send(ws, createError(ErrorCode.CHANNEL_NOT_FOUND, `Channel ${msg.channel} not found`));
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Check invite-only
|
|
97
|
-
if (channel.inviteOnly && !channel.invited.has(agent.id)) {
|
|
98
|
-
server._send(ws, createError(ErrorCode.NOT_INVITED, `Channel ${msg.channel} is invite-only`));
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Add to channel
|
|
103
|
-
channel.agents.add(ws);
|
|
104
|
-
agent.channels.add(msg.channel);
|
|
105
|
-
|
|
106
|
-
server._log('join', { agent: agent.id, channel: msg.channel });
|
|
107
|
-
|
|
108
|
-
// Notify others
|
|
109
|
-
server._broadcast(msg.channel, createMessage(ServerMessageType.AGENT_JOINED, {
|
|
110
|
-
channel: msg.channel,
|
|
111
|
-
agent: `@${agent.id}`
|
|
112
|
-
}), ws);
|
|
113
|
-
|
|
114
|
-
// Send confirmation with agent list
|
|
115
|
-
const agentList = [];
|
|
116
|
-
for (const memberWs of channel.agents) {
|
|
117
|
-
const member = server.agents.get(memberWs);
|
|
118
|
-
if (member) agentList.push(`@${member.id}`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
server._send(ws, createMessage(ServerMessageType.JOINED, {
|
|
122
|
-
channel: msg.channel,
|
|
123
|
-
agents: agentList
|
|
124
|
-
}));
|
|
125
|
-
|
|
126
|
-
// Replay recent messages to the joining agent
|
|
127
|
-
server._replayMessages(ws, msg.channel);
|
|
128
|
-
|
|
129
|
-
// Send welcome prompt to the new joiner
|
|
130
|
-
server._send(ws, createMessage(ServerMessageType.MSG, {
|
|
131
|
-
from: '@server',
|
|
132
|
-
to: msg.channel,
|
|
133
|
-
content: `Welcome to ${msg.channel}, @${agent.id}! Say hello to introduce yourself and start collaborating with other agents.`
|
|
134
|
-
}));
|
|
135
|
-
|
|
136
|
-
// Prompt existing agents to engage with the new joiner (if there are others)
|
|
137
|
-
const otherAgents = [];
|
|
138
|
-
for (const memberWs of channel.agents) {
|
|
139
|
-
if (memberWs !== ws) {
|
|
140
|
-
const member = server.agents.get(memberWs);
|
|
141
|
-
if (member) otherAgents.push({ ws: memberWs, id: member.id });
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (otherAgents.length > 0) {
|
|
146
|
-
const welcomePrompt = createMessage(ServerMessageType.MSG, {
|
|
147
|
-
from: '@server',
|
|
148
|
-
to: msg.channel,
|
|
149
|
-
content: `Hey ${otherAgents.map(a => `@${a.id}`).join(', ')} - new agent @${agent.id} just joined! Say hi and share what you're working on.`
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
for (const other of otherAgents) {
|
|
153
|
-
server._send(other.ws, welcomePrompt);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Update channel activity
|
|
158
|
-
server.channelLastActivity.set(msg.channel, Date.now());
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Handle LEAVE command - remove agent from channel
|
|
163
|
-
*/
|
|
164
|
-
export function handleLeave(server, ws, msg) {
|
|
165
|
-
const agent = server.agents.get(ws);
|
|
166
|
-
if (!agent) {
|
|
167
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const channel = server.channels.get(msg.channel);
|
|
172
|
-
if (!channel) return;
|
|
173
|
-
|
|
174
|
-
channel.agents.delete(ws);
|
|
175
|
-
agent.channels.delete(msg.channel);
|
|
176
|
-
|
|
177
|
-
server._log('leave', { agent: agent.id, channel: msg.channel });
|
|
178
|
-
|
|
179
|
-
// Notify others
|
|
180
|
-
server._broadcast(msg.channel, createMessage(ServerMessageType.AGENT_LEFT, {
|
|
181
|
-
channel: msg.channel,
|
|
182
|
-
agent: `@${agent.id}`
|
|
183
|
-
}));
|
|
184
|
-
|
|
185
|
-
server._send(ws, createMessage(ServerMessageType.LEFT, {
|
|
186
|
-
channel: msg.channel
|
|
187
|
-
}));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Handle LIST_CHANNELS command
|
|
192
|
-
*/
|
|
193
|
-
export function handleListChannels(server, ws) {
|
|
194
|
-
const list = [];
|
|
195
|
-
for (const [name, channel] of server.channels) {
|
|
196
|
-
if (!channel.inviteOnly) {
|
|
197
|
-
list.push({
|
|
198
|
-
name,
|
|
199
|
-
agents: channel.agents.size
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
server._send(ws, createMessage(ServerMessageType.CHANNELS, { list }));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Handle LIST_AGENTS command
|
|
209
|
-
*/
|
|
210
|
-
export function handleListAgents(server, ws, msg) {
|
|
211
|
-
const channel = server.channels.get(msg.channel);
|
|
212
|
-
if (!channel) {
|
|
213
|
-
server._send(ws, createError(ErrorCode.CHANNEL_NOT_FOUND, `Channel ${msg.channel} not found`));
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const list = [];
|
|
218
|
-
for (const memberWs of channel.agents) {
|
|
219
|
-
const member = server.agents.get(memberWs);
|
|
220
|
-
if (member) {
|
|
221
|
-
list.push({
|
|
222
|
-
id: `@${member.id}`,
|
|
223
|
-
name: member.name,
|
|
224
|
-
presence: member.presence || 'online',
|
|
225
|
-
status_text: member.statusText || null
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
server._send(ws, createMessage(ServerMessageType.AGENTS, {
|
|
231
|
-
channel: msg.channel,
|
|
232
|
-
list
|
|
233
|
-
}));
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Handle CREATE_CHANNEL command
|
|
238
|
-
*/
|
|
239
|
-
export function handleCreateChannel(server, ws, msg) {
|
|
240
|
-
const agent = server.agents.get(ws);
|
|
241
|
-
if (!agent) {
|
|
242
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (server.channels.has(msg.channel)) {
|
|
247
|
-
server._send(ws, createError(ErrorCode.CHANNEL_EXISTS, `Channel ${msg.channel} already exists`));
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const channel = server._createChannel(msg.channel, msg.invite_only || false);
|
|
252
|
-
|
|
253
|
-
// Creator is automatically invited and joined
|
|
254
|
-
if (channel.inviteOnly) {
|
|
255
|
-
channel.invited.add(agent.id);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
server._log('create_channel', { agent: agent.id, channel: msg.channel, inviteOnly: channel.inviteOnly });
|
|
259
|
-
|
|
260
|
-
// Auto-join creator
|
|
261
|
-
channel.agents.add(ws);
|
|
262
|
-
agent.channels.add(msg.channel);
|
|
263
|
-
|
|
264
|
-
server._send(ws, createMessage(ServerMessageType.JOINED, {
|
|
265
|
-
channel: msg.channel,
|
|
266
|
-
agents: [`@${agent.id}`]
|
|
267
|
-
}));
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Handle INVITE command
|
|
272
|
-
*/
|
|
273
|
-
export function handleInvite(server, ws, msg) {
|
|
274
|
-
const agent = server.agents.get(ws);
|
|
275
|
-
if (!agent) {
|
|
276
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const channel = server.channels.get(msg.channel);
|
|
281
|
-
if (!channel) {
|
|
282
|
-
server._send(ws, createError(ErrorCode.CHANNEL_NOT_FOUND, `Channel ${msg.channel} not found`));
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Must be a member to invite
|
|
287
|
-
if (!agent.channels.has(msg.channel)) {
|
|
288
|
-
server._send(ws, createError(ErrorCode.NOT_INVITED, `Not a member of ${msg.channel}`));
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const targetId = msg.agent.slice(1);
|
|
293
|
-
channel.invited.add(targetId);
|
|
294
|
-
|
|
295
|
-
server._log('invite', { agent: agent.id, target: targetId, channel: msg.channel });
|
|
296
|
-
|
|
297
|
-
// Notify target if connected
|
|
298
|
-
const targetWs = server.agentById.get(targetId);
|
|
299
|
-
if (targetWs) {
|
|
300
|
-
server._send(targetWs, createMessage(ServerMessageType.MSG, {
|
|
301
|
-
from: `@${agent.id}`,
|
|
302
|
-
to: msg.agent,
|
|
303
|
-
content: `You have been invited to ${msg.channel}`
|
|
304
|
-
}));
|
|
305
|
-
}
|
|
306
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Presence Handlers
|
|
3
|
-
* Handles presence status updates
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
ServerMessageType,
|
|
8
|
-
ErrorCode,
|
|
9
|
-
createMessage,
|
|
10
|
-
createError,
|
|
11
|
-
} from '../../protocol.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Handle SET_PRESENCE command
|
|
15
|
-
*/
|
|
16
|
-
export function handleSetPresence(server, ws, msg) {
|
|
17
|
-
const agent = server.agents.get(ws);
|
|
18
|
-
if (!agent) {
|
|
19
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const oldPresence = agent.presence;
|
|
24
|
-
agent.presence = msg.status;
|
|
25
|
-
agent.statusText = msg.status_text || null;
|
|
26
|
-
|
|
27
|
-
server._log('presence_changed', {
|
|
28
|
-
agent: agent.id,
|
|
29
|
-
from: oldPresence,
|
|
30
|
-
to: msg.status,
|
|
31
|
-
statusText: agent.statusText
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// Broadcast presence change to all channels the agent is in
|
|
35
|
-
const presenceMsg = createMessage(ServerMessageType.PRESENCE_CHANGED, {
|
|
36
|
-
agent_id: `@${agent.id}`,
|
|
37
|
-
presence: agent.presence,
|
|
38
|
-
status_text: agent.statusText
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
for (const channelName of agent.channels) {
|
|
42
|
-
server._broadcast(channelName, presenceMsg);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Proposal Handlers
|
|
3
|
-
* Handles proposal, accept, reject, complete, dispute operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
ServerMessageType,
|
|
8
|
-
ErrorCode,
|
|
9
|
-
createMessage,
|
|
10
|
-
createError,
|
|
11
|
-
} from '../../protocol.js';
|
|
12
|
-
import { formatProposal, formatProposalResponse } from '../../proposals.js';
|
|
13
|
-
import {
|
|
14
|
-
EscrowEvent,
|
|
15
|
-
createEscrowCreatedPayload,
|
|
16
|
-
createCompletionPayload,
|
|
17
|
-
createDisputePayload,
|
|
18
|
-
} from '../../escrow-hooks.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Handle PROPOSAL command
|
|
22
|
-
*/
|
|
23
|
-
export function handleProposal(server, ws, msg) {
|
|
24
|
-
const agent = server.agents.get(ws);
|
|
25
|
-
if (!agent) {
|
|
26
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Proposals require a persistent identity (signature verification)
|
|
31
|
-
if (!agent.pubkey) {
|
|
32
|
-
server._send(ws, createError(ErrorCode.SIGNATURE_REQUIRED, 'Proposals require persistent identity'));
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const targetId = msg.to.slice(1);
|
|
37
|
-
const targetWs = server.agentById.get(targetId);
|
|
38
|
-
|
|
39
|
-
if (!targetWs) {
|
|
40
|
-
server._send(ws, createError(ErrorCode.AGENT_NOT_FOUND, `Agent ${msg.to} not found`));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Create proposal in store
|
|
45
|
-
const proposal = server.proposals.create({
|
|
46
|
-
from: `@${agent.id}`,
|
|
47
|
-
to: msg.to,
|
|
48
|
-
task: msg.task,
|
|
49
|
-
amount: msg.amount,
|
|
50
|
-
currency: msg.currency,
|
|
51
|
-
payment_code: msg.payment_code,
|
|
52
|
-
terms: msg.terms,
|
|
53
|
-
expires: msg.expires,
|
|
54
|
-
sig: msg.sig,
|
|
55
|
-
elo_stake: msg.elo_stake || null
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
server._log('proposal', { id: proposal.id, from: agent.id, to: targetId });
|
|
59
|
-
|
|
60
|
-
// Send to target
|
|
61
|
-
const outMsg = createMessage(ServerMessageType.PROPOSAL, {
|
|
62
|
-
...formatProposal(proposal)
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
server._send(targetWs, outMsg);
|
|
66
|
-
// Echo back to sender with the assigned ID
|
|
67
|
-
server._send(ws, outMsg);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Handle ACCEPT command
|
|
72
|
-
*/
|
|
73
|
-
export async function handleAccept(server, ws, msg) {
|
|
74
|
-
const agent = server.agents.get(ws);
|
|
75
|
-
if (!agent) {
|
|
76
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!agent.pubkey) {
|
|
81
|
-
server._send(ws, createError(ErrorCode.SIGNATURE_REQUIRED, 'Accepting proposals requires persistent identity'));
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Get proposal first to check stakes
|
|
86
|
-
const existingProposal = server.proposals.get(msg.proposal_id);
|
|
87
|
-
if (!existingProposal) {
|
|
88
|
-
server._send(ws, createError(ErrorCode.PROPOSAL_NOT_FOUND, 'Proposal not found'));
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const proposerStake = existingProposal.proposer_stake || 0;
|
|
93
|
-
const acceptorStake = msg.elo_stake || 0;
|
|
94
|
-
|
|
95
|
-
// Validate proposer can stake (if they declared a stake)
|
|
96
|
-
if (proposerStake > 0) {
|
|
97
|
-
const canProposerStake = await server.reputationStore.canStake(existingProposal.from, proposerStake);
|
|
98
|
-
if (!canProposerStake.canStake) {
|
|
99
|
-
server._send(ws, createError(ErrorCode.INSUFFICIENT_REPUTATION, `Proposer: ${canProposerStake.reason}`));
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Validate acceptor can stake (if they declared a stake)
|
|
105
|
-
if (acceptorStake > 0) {
|
|
106
|
-
const canAcceptorStake = await server.reputationStore.canStake(`@${agent.id}`, acceptorStake);
|
|
107
|
-
if (!canAcceptorStake.canStake) {
|
|
108
|
-
server._send(ws, createError(ErrorCode.INSUFFICIENT_REPUTATION, canAcceptorStake.reason));
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const result = server.proposals.accept(
|
|
114
|
-
msg.proposal_id,
|
|
115
|
-
`@${agent.id}`,
|
|
116
|
-
msg.sig,
|
|
117
|
-
msg.payment_code,
|
|
118
|
-
acceptorStake
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
if (result.error) {
|
|
122
|
-
server._send(ws, createError(ErrorCode.INVALID_PROPOSAL, result.error));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const proposal = result.proposal;
|
|
127
|
-
|
|
128
|
-
// Create escrow if either party has a stake
|
|
129
|
-
if (proposerStake > 0 || acceptorStake > 0) {
|
|
130
|
-
const escrowResult = await server.reputationStore.createEscrow(
|
|
131
|
-
proposal.id,
|
|
132
|
-
{ agent_id: proposal.from, stake: proposerStake },
|
|
133
|
-
{ agent_id: proposal.to, stake: acceptorStake },
|
|
134
|
-
proposal.expires
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
if (escrowResult.success) {
|
|
138
|
-
proposal.stakes_escrowed = true;
|
|
139
|
-
server._log('escrow_created', {
|
|
140
|
-
proposal_id: proposal.id,
|
|
141
|
-
proposer_stake: proposerStake,
|
|
142
|
-
acceptor_stake: acceptorStake
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Emit escrow:created hook for external integrations
|
|
146
|
-
server.escrowHooks.emit(EscrowEvent.CREATED, createEscrowCreatedPayload(proposal, escrowResult))
|
|
147
|
-
.catch(err => server._log('escrow_hook_error', { event: 'created', error: err.message }));
|
|
148
|
-
} else {
|
|
149
|
-
server._log('escrow_error', { proposal_id: proposal.id, error: escrowResult.error });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
server._log('accept', { id: proposal.id, by: agent.id, proposer_stake: proposerStake, acceptor_stake: acceptorStake });
|
|
154
|
-
|
|
155
|
-
// Notify the proposal creator
|
|
156
|
-
const creatorId = proposal.from.slice(1);
|
|
157
|
-
const creatorWs = server.agentById.get(creatorId);
|
|
158
|
-
|
|
159
|
-
const outMsg = createMessage(ServerMessageType.ACCEPT, {
|
|
160
|
-
...formatProposalResponse(proposal, 'accept')
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
if (creatorWs) {
|
|
164
|
-
server._send(creatorWs, outMsg);
|
|
165
|
-
}
|
|
166
|
-
// Echo to acceptor
|
|
167
|
-
server._send(ws, outMsg);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Handle REJECT command
|
|
172
|
-
*/
|
|
173
|
-
export function handleReject(server, ws, msg) {
|
|
174
|
-
const agent = server.agents.get(ws);
|
|
175
|
-
if (!agent) {
|
|
176
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (!agent.pubkey) {
|
|
181
|
-
server._send(ws, createError(ErrorCode.SIGNATURE_REQUIRED, 'Rejecting proposals requires persistent identity'));
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const result = server.proposals.reject(
|
|
186
|
-
msg.proposal_id,
|
|
187
|
-
`@${agent.id}`,
|
|
188
|
-
msg.sig,
|
|
189
|
-
msg.reason
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
if (result.error) {
|
|
193
|
-
server._send(ws, createError(ErrorCode.INVALID_PROPOSAL, result.error));
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const proposal = result.proposal;
|
|
198
|
-
server._log('reject', { id: proposal.id, by: agent.id });
|
|
199
|
-
|
|
200
|
-
// Notify the proposal creator
|
|
201
|
-
const creatorId = proposal.from.slice(1);
|
|
202
|
-
const creatorWs = server.agentById.get(creatorId);
|
|
203
|
-
|
|
204
|
-
const outMsg = createMessage(ServerMessageType.REJECT, {
|
|
205
|
-
...formatProposalResponse(proposal, 'reject')
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
if (creatorWs) {
|
|
209
|
-
server._send(creatorWs, outMsg);
|
|
210
|
-
}
|
|
211
|
-
// Echo to rejector
|
|
212
|
-
server._send(ws, outMsg);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Handle COMPLETE command
|
|
217
|
-
*/
|
|
218
|
-
export async function handleComplete(server, ws, msg) {
|
|
219
|
-
const agent = server.agents.get(ws);
|
|
220
|
-
if (!agent) {
|
|
221
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (!agent.pubkey) {
|
|
226
|
-
server._send(ws, createError(ErrorCode.SIGNATURE_REQUIRED, 'Completing proposals requires persistent identity'));
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const result = server.proposals.complete(
|
|
231
|
-
msg.proposal_id,
|
|
232
|
-
`@${agent.id}`,
|
|
233
|
-
msg.sig,
|
|
234
|
-
msg.proof
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
if (result.error) {
|
|
238
|
-
server._send(ws, createError(ErrorCode.INVALID_PROPOSAL, result.error));
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const proposal = result.proposal;
|
|
243
|
-
server._log('complete', { id: proposal.id, by: agent.id });
|
|
244
|
-
|
|
245
|
-
// Update reputation ratings (includes escrow settlement)
|
|
246
|
-
let ratingChanges = null;
|
|
247
|
-
try {
|
|
248
|
-
ratingChanges = await server.reputationStore.processCompletion({
|
|
249
|
-
type: 'COMPLETE',
|
|
250
|
-
proposal_id: proposal.id,
|
|
251
|
-
from: proposal.from,
|
|
252
|
-
to: proposal.to,
|
|
253
|
-
amount: proposal.amount
|
|
254
|
-
});
|
|
255
|
-
server._log('reputation_updated', {
|
|
256
|
-
proposal_id: proposal.id,
|
|
257
|
-
changes: ratingChanges,
|
|
258
|
-
escrow: ratingChanges?._escrow
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// Emit settlement:completion hook for external integrations
|
|
262
|
-
if (ratingChanges?._escrow) {
|
|
263
|
-
server.escrowHooks.emit(EscrowEvent.COMPLETION_SETTLED, createCompletionPayload(proposal, ratingChanges))
|
|
264
|
-
.catch(err => server._log('escrow_hook_error', { event: 'completion', error: err.message }));
|
|
265
|
-
}
|
|
266
|
-
} catch (err) {
|
|
267
|
-
server._log('reputation_error', { error: err.message });
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Notify both parties
|
|
271
|
-
const outMsg = createMessage(ServerMessageType.COMPLETE, {
|
|
272
|
-
...formatProposalResponse(proposal, 'complete'),
|
|
273
|
-
rating_changes: ratingChanges
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Notify the other party
|
|
277
|
-
const otherId = proposal.from === `@${agent.id}` ? proposal.to.slice(1) : proposal.from.slice(1);
|
|
278
|
-
const otherWs = server.agentById.get(otherId);
|
|
279
|
-
|
|
280
|
-
if (otherWs) {
|
|
281
|
-
server._send(otherWs, outMsg);
|
|
282
|
-
}
|
|
283
|
-
// Echo to completer
|
|
284
|
-
server._send(ws, outMsg);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Handle DISPUTE command
|
|
289
|
-
*/
|
|
290
|
-
export async function handleDispute(server, ws, msg) {
|
|
291
|
-
const agent = server.agents.get(ws);
|
|
292
|
-
if (!agent) {
|
|
293
|
-
server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (!agent.pubkey) {
|
|
298
|
-
server._send(ws, createError(ErrorCode.SIGNATURE_REQUIRED, 'Disputing proposals requires persistent identity'));
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const result = server.proposals.dispute(
|
|
303
|
-
msg.proposal_id,
|
|
304
|
-
`@${agent.id}`,
|
|
305
|
-
msg.sig,
|
|
306
|
-
msg.reason
|
|
307
|
-
);
|
|
308
|
-
|
|
309
|
-
if (result.error) {
|
|
310
|
-
server._send(ws, createError(ErrorCode.INVALID_PROPOSAL, result.error));
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const proposal = result.proposal;
|
|
315
|
-
server._log('dispute', { id: proposal.id, by: agent.id, reason: msg.reason });
|
|
316
|
-
|
|
317
|
-
// Update reputation ratings (includes escrow settlement)
|
|
318
|
-
let ratingChanges = null;
|
|
319
|
-
try {
|
|
320
|
-
ratingChanges = await server.reputationStore.processDispute({
|
|
321
|
-
type: 'DISPUTE',
|
|
322
|
-
proposal_id: proposal.id,
|
|
323
|
-
from: proposal.from,
|
|
324
|
-
to: proposal.to,
|
|
325
|
-
amount: proposal.amount,
|
|
326
|
-
disputed_by: `@${agent.id}`
|
|
327
|
-
});
|
|
328
|
-
server._log('reputation_updated', {
|
|
329
|
-
proposal_id: proposal.id,
|
|
330
|
-
changes: ratingChanges,
|
|
331
|
-
escrow: ratingChanges?._escrow
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// Emit settlement:dispute hook for external integrations
|
|
335
|
-
if (ratingChanges?._escrow) {
|
|
336
|
-
server.escrowHooks.emit(EscrowEvent.DISPUTE_SETTLED, createDisputePayload(proposal, ratingChanges))
|
|
337
|
-
.catch(err => server._log('escrow_hook_error', { event: 'dispute', error: err.message }));
|
|
338
|
-
}
|
|
339
|
-
} catch (err) {
|
|
340
|
-
server._log('reputation_error', { error: err.message });
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Notify both parties
|
|
344
|
-
const outMsg = createMessage(ServerMessageType.DISPUTE, {
|
|
345
|
-
...formatProposalResponse(proposal, 'dispute'),
|
|
346
|
-
rating_changes: ratingChanges
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// Notify the other party
|
|
350
|
-
const otherId = proposal.from === `@${agent.id}` ? proposal.to.slice(1) : proposal.from.slice(1);
|
|
351
|
-
const otherWs = server.agentById.get(otherId);
|
|
352
|
-
|
|
353
|
-
if (otherWs) {
|
|
354
|
-
server._send(otherWs, outMsg);
|
|
355
|
-
}
|
|
356
|
-
// Echo to disputer
|
|
357
|
-
server._send(ws, outMsg);
|
|
358
|
-
}
|