botschat 0.1.20 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/package.json +1 -1
  2. package/packages/api/src/do/connection-do.ts +186 -382
  3. package/packages/api/src/index.ts +50 -67
  4. package/packages/api/src/routes/agents.ts +3 -3
  5. package/packages/api/src/routes/auth.ts +1 -0
  6. package/packages/api/src/routes/channels.ts +11 -11
  7. package/packages/api/src/routes/demo.ts +156 -0
  8. package/packages/api/src/routes/sessions.ts +5 -5
  9. package/packages/api/src/routes/tasks.ts +33 -33
  10. package/packages/plugin/dist/src/channel.js +50 -0
  11. package/packages/plugin/dist/src/channel.js.map +1 -1
  12. package/packages/plugin/package.json +18 -2
  13. package/packages/web/dist/assets/index-BtPyCBCl.css +1 -0
  14. package/packages/web/dist/assets/index-BtpsFe4Z.js +2 -0
  15. package/packages/web/dist/assets/index-CQbIYr6_.js +2 -0
  16. package/packages/web/dist/assets/{index-CYQMu_-c.js → index-C_GamcQc.js} +1 -1
  17. package/packages/web/dist/assets/index-LiBjPMg2.js +1 -0
  18. package/packages/web/dist/assets/{index-DYCO-ry1.js → index-MyoWvQAH.js} +1 -1
  19. package/packages/web/dist/assets/index-STIPTMK8.js +1516 -0
  20. package/packages/web/dist/assets/{index.esm-CvOpngZM.js → index.esm-BpQAwtdR.js} +1 -1
  21. package/packages/web/dist/assets/{web-D3LMODYp.js → web-BbTzVNLt.js} +1 -1
  22. package/packages/web/dist/assets/{web-1cdhq2RW.js → web-cnzjgNfD.js} +1 -1
  23. package/packages/web/dist/index.html +2 -2
  24. package/packages/web/src/App.tsx +9 -56
  25. package/packages/web/src/api.ts +5 -61
  26. package/packages/web/src/components/ChatWindow.tsx +9 -9
  27. package/packages/web/src/components/CronDetail.tsx +1 -1
  28. package/packages/web/src/components/ImageLightbox.tsx +96 -0
  29. package/packages/web/src/components/LoginPage.tsx +59 -1
  30. package/packages/web/src/components/MessageContent.tsx +17 -2
  31. package/packages/web/src/components/SessionTabs.tsx +1 -1
  32. package/packages/web/src/components/Sidebar.tsx +1 -3
  33. package/packages/web/src/hooks/useIMEComposition.ts +14 -9
  34. package/packages/web/src/store.ts +7 -39
  35. package/packages/web/src/ws.ts +0 -1
  36. package/scripts/dev.sh +0 -53
  37. package/migrations/0013_agents_table.sql +0 -29
  38. package/migrations/0014_agent_sessions.sql +0 -19
  39. package/migrations/0015_message_traces.sql +0 -27
  40. package/migrations/0016_multi_agent_channels_messages.sql +0 -9
  41. package/migrations/0017_rename_cron_job_id.sql +0 -2
  42. package/packages/api/src/protocol-v2.ts +0 -154
  43. package/packages/api/src/routes/agents-v2.ts +0 -192
  44. package/packages/api/src/routes/history-v2.ts +0 -221
  45. package/packages/api/src/routes/migrate-v2.ts +0 -110
  46. package/packages/web/dist/assets/index-BARPtt0v.css +0 -1
  47. package/packages/web/dist/assets/index-Bf-XL3te.js +0 -2
  48. package/packages/web/dist/assets/index-CYlvfpX9.js +0 -1519
  49. package/packages/web/dist/assets/index-CxcpA4Qo.js +0 -1
  50. package/packages/web/dist/assets/index-QebPVqwj.js +0 -2
  51. package/packages/web/src/components/AgentSettings.tsx +0 -328
  52. package/scripts/mock-openclaw-v2.mjs +0 -486
@@ -1,486 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Mock OpenClaw v2 — multi-agent aware mock for testing the full v2 architecture.
4
- *
5
- * Features:
6
- * - Echoes back all received content + context (raw JSON dump)
7
- * - Fetches L1/L2/L3 history via the v2 History API and returns it
8
- * - Special trigger words for testing different capabilities
9
- * - Sends agent.trace (lv2/lv3) to test verbose storage
10
- * - Supports agentId in auth and all outbound messages
11
- *
12
- * Trigger words (in message text):
13
- * /echo — Echo back the raw message JSON (default behavior)
14
- * /context — Fetch and return channel context via History API
15
- * /history — Fetch L1 history (conclusions only) and return
16
- * /history2 — Fetch L2 history (+ thinking process)
17
- * /history3 — Fetch L3 history (+ tool calls / references)
18
- * /agents — List all available agents and their skills
19
- * /delegate — Simulate agent.request delegation to another agent
20
- * /trace — Send sample lv2 + lv3 traces
21
- *
22
- * Usage:
23
- * node scripts/mock-openclaw-v2.mjs --token bc_pat_xxx --agent-id agt_xxx
24
- * node scripts/mock-openclaw-v2.mjs --token bc_pat_xxx --agent-id agt_xxx --url http://localhost:8788
25
- */
26
-
27
- import { randomUUID } from "node:crypto";
28
- import { parseArgs } from "node:util";
29
-
30
- const { values: args } = parseArgs({
31
- options: {
32
- token: { type: "string" },
33
- "agent-id": { type: "string" },
34
- url: { type: "string", default: "http://localhost:8788" },
35
- "api-token": { type: "string" },
36
- agents: { type: "string", default: "main" },
37
- delay: { type: "string", default: "200" },
38
- model: { type: "string", default: "mock/openclaw-v2" },
39
- help: { type: "boolean", short: "h", default: false },
40
- },
41
- strict: true,
42
- });
43
-
44
- if (args.help || !args.token || !args["agent-id"]) {
45
- console.log(`Mock OpenClaw v2 — multi-agent testing mock
46
-
47
- Usage:
48
- node scripts/mock-openclaw-v2.mjs --token <pat> --agent-id <agt_xxx> [options]
49
-
50
- Required:
51
- --token <pat> Agent pairing token
52
- --agent-id <agt_xxx> Agent ID from agents table
53
-
54
- Options:
55
- --url <url> Server URL (default: http://localhost:8788)
56
- --api-token <jwt> JWT token for History API (auto-acquired if not provided)
57
- --agents <list> Comma-separated OpenClaw agent IDs (default: main)
58
- --delay <ms> Reply delay (default: 200)
59
- --model <name> Model name (default: mock/openclaw-v2)
60
-
61
- Trigger words:
62
- /echo Echo raw message JSON
63
- /context Fetch channel context
64
- /history Fetch L1 history
65
- /history2 Fetch L2 history (+ thinking)
66
- /history3 Fetch L3 history (+ tool calls)
67
- /agents List available agents + skills
68
- /delegate Simulate delegation to another agent
69
- /trace Send sample traces (lv2 + lv3)`);
70
- process.exit(args.help ? 0 : 1);
71
- }
72
-
73
- const TOKEN = args.token;
74
- const AGENT_ID = args["agent-id"];
75
- const SERVER_URL = args.url;
76
- const OC_AGENTS = args.agents.split(",").map(s => s.trim());
77
- const DELAY_MS = parseInt(args.delay, 10);
78
- const MODEL = args.model;
79
- let API_TOKEN = args["api-token"] || null;
80
- let userId = null;
81
-
82
- // ── Colours ──
83
- const c = {
84
- reset:"\x1b[0m", dim:"\x1b[2m", cyan:"\x1b[36m", green:"\x1b[32m",
85
- yellow:"\x1b[33m", red:"\x1b[31m", magenta:"\x1b[35m", blue:"\x1b[34m",
86
- };
87
- const ts = () => new Date().toISOString().slice(11, 23);
88
- const logInfo = (m) => console.log(`${c.dim}${ts()}${c.reset} ${c.cyan}▸${c.reset} ${m}`);
89
- const logOk = (m) => console.log(`${c.dim}${ts()}${c.reset} ${c.green}✔${c.reset} ${m}`);
90
- const logWarn = (m) => console.log(`${c.dim}${ts()}${c.reset} ${c.yellow}▲${c.reset} ${m}`);
91
- const logErr = (m) => console.log(`${c.dim}${ts()}${c.reset} ${c.red}✖${c.reset} ${m}`);
92
- const logRecv = (m) => console.log(`${c.dim}${ts()}${c.reset} ${c.magenta}◂${c.reset} ${m}`);
93
- const logSend = (m) => console.log(`${c.dim}${ts()}${c.reset} ${c.blue}▸${c.reset} ${m}`);
94
-
95
- // ── HTTP helpers (for History API) ──
96
-
97
- async function apiGet(path) {
98
- if (!API_TOKEN) {
99
- logWarn("No API token, trying dev-auth...");
100
- await acquireApiToken();
101
- }
102
- const res = await fetch(`${SERVER_URL}/api${path}`, {
103
- headers: { Authorization: `Bearer ${API_TOKEN}` },
104
- });
105
- if (!res.ok) throw new Error(`API ${res.status}: ${await res.text()}`);
106
- return res.json();
107
- }
108
-
109
- async function acquireApiToken() {
110
- const DEV_SECRET = process.env.DEV_AUTH_SECRET || "botschat-local-dev-secret";
111
- const res = await fetch(`${SERVER_URL}/api/dev-auth/login`, {
112
- method: "POST",
113
- headers: { "Content-Type": "application/json" },
114
- body: JSON.stringify({ secret: DEV_SECRET, userId: userId || "u_v2test" }),
115
- });
116
- if (!res.ok) throw new Error(`dev-auth failed: ${res.status}`);
117
- const data = await res.json();
118
- API_TOKEN = data.token;
119
- logOk(`Acquired API token for ${data.userId}`);
120
- }
121
-
122
- // ── WebSocket ──
123
-
124
- let ws = null;
125
- let backoff = 1000;
126
- let pingTimer = null;
127
- let intentionalClose = false;
128
-
129
- function buildWsUrl() {
130
- const host = SERVER_URL.replace(/^https?:\/\//, "");
131
- const scheme = SERVER_URL.startsWith("http://") ? "ws" : "wss";
132
- return `${scheme}://${host}/api/gateway/mock?token=${encodeURIComponent(TOKEN)}`;
133
- }
134
-
135
- function connect() {
136
- const url = buildWsUrl();
137
- logInfo(`Connecting to ${url.replace(/token=.*/, "token=***")}`);
138
- ws = new WebSocket(url);
139
-
140
- ws.addEventListener("open", () => {
141
- logInfo("Connected, sending auth with agentId...");
142
- send({ type: "auth", token: TOKEN, agentId: AGENT_ID, agentType: "openclaw", agents: OC_AGENTS, model: MODEL });
143
- });
144
-
145
- ws.addEventListener("message", (event) => {
146
- try {
147
- const msg = JSON.parse(typeof event.data === "string" ? event.data : event.data.toString());
148
- handleMessage(msg);
149
- } catch (e) {
150
- logErr(`Bad JSON: ${e.message}`);
151
- }
152
- });
153
-
154
- ws.addEventListener("close", (event) => {
155
- logWarn(`Disconnected: code=${event.code}`);
156
- if (pingTimer) { clearInterval(pingTimer); pingTimer = null; }
157
- if (!intentionalClose) {
158
- logInfo(`Reconnecting in ${backoff}ms...`);
159
- setTimeout(() => { backoff = Math.min(backoff * 2, 30000); connect(); }, backoff);
160
- }
161
- });
162
-
163
- ws.addEventListener("error", (event) => logErr(`WS error: ${event.message || "unknown"}`));
164
- }
165
-
166
- function send(msg) {
167
- if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(msg));
168
- }
169
-
170
- // ── Message handlers ──
171
-
172
- function handleMessage(msg) {
173
- switch (msg.type) {
174
- case "auth.ok":
175
- userId = msg.userId;
176
- backoff = 1000;
177
- logOk(`Authenticated: userId=${userId}, agentId=${AGENT_ID}`);
178
- if (msg.availableAgents) {
179
- logInfo(`Available agents: ${msg.availableAgents.map(a => `${a.name}(${a.status})`).join(", ")}`);
180
- }
181
- pingTimer = setInterval(() => send({ type: "status", connected: true, agents: OC_AGENTS, model: MODEL }), 25000);
182
- break;
183
- case "auth.fail":
184
- logErr(`Auth failed: ${msg.reason}`);
185
- intentionalClose = true;
186
- ws?.close(4001);
187
- break;
188
- case "ping":
189
- send({ type: "pong" });
190
- break;
191
- case "user.message":
192
- logRecv(`[user.message] session=${msg.sessionKey} target=${msg.targetAgentId || "default"} text="${trunc(msg.text, 60)}"`);
193
- handleUserMessage(msg);
194
- break;
195
- case "user.action":
196
- logRecv(`[user.action] action=${msg.action}`);
197
- send({ type: "agent.text", agentId: AGENT_ID, sessionKey: msg.sessionKey, text: `Action: ${msg.action}`, messageId: randomUUID() });
198
- break;
199
- case "task.scan.request":
200
- send({ type: "task.scan.result", tasks: [] });
201
- break;
202
- case "models.request":
203
- send({ type: "models.list", models: [{ id: MODEL, name: "Mock OpenClaw v2", provider: "mock" }] });
204
- break;
205
- case "settings.defaultModel":
206
- send({ type: "defaultModel.updated", model: msg.defaultModel });
207
- break;
208
- case "settings.notifyPreview":
209
- break;
210
- case "agent.response":
211
- logRecv(`[agent.response] requestId=${msg.requestId} from=${msg.fromAgentId} text="${trunc(msg.text, 60)}"`);
212
- break;
213
- default:
214
- logWarn(`Unhandled: ${msg.type}`);
215
- }
216
- }
217
-
218
- // ── User message handler with trigger words ──
219
-
220
- async function handleUserMessage(msg) {
221
- const text = (msg.text || "").trim();
222
- const sessionKey = msg.sessionKey;
223
- const messageId = msg.messageId || randomUUID();
224
-
225
- await sleep(DELAY_MS);
226
-
227
- // Parse trigger word
228
- const trigger = text.startsWith("/") ? text.split(/\s/)[0].toLowerCase() : null;
229
-
230
- try {
231
- switch (trigger) {
232
- case "/context":
233
- await handleContextQuery(sessionKey, messageId);
234
- break;
235
- case "/history":
236
- await handleHistoryQuery(sessionKey, messageId, 1);
237
- break;
238
- case "/history2":
239
- await handleHistoryQuery(sessionKey, messageId, 2);
240
- break;
241
- case "/history3":
242
- await handleHistoryQuery(sessionKey, messageId, 3);
243
- break;
244
- case "/agents":
245
- await handleAgentsQuery(sessionKey, messageId);
246
- break;
247
- case "/delegate":
248
- await handleDelegate(sessionKey, messageId, text);
249
- break;
250
- case "/trace":
251
- await handleTraceSample(sessionKey, messageId);
252
- break;
253
- default:
254
- // Default: echo back everything we received
255
- await handleEcho(msg, sessionKey, messageId);
256
- }
257
- } catch (err) {
258
- sendReply(sessionKey, `Error: ${err.message}`, messageId);
259
- logErr(`Handler error: ${err.message}`);
260
- }
261
- }
262
-
263
- // ── /echo — dump raw message ──
264
-
265
- async function handleEcho(msg, sessionKey, messageId) {
266
- const dump = {
267
- received: {
268
- type: msg.type,
269
- sessionKey: msg.sessionKey,
270
- text: msg.text,
271
- userId: msg.userId,
272
- messageId: msg.messageId,
273
- targetAgentId: msg.targetAgentId,
274
- parentMessageId: msg.parentMessageId,
275
- parentText: msg.parentText,
276
- parentSender: msg.parentSender,
277
- context: msg.context,
278
- depth: msg.depth,
279
- },
280
- agent: { id: AGENT_ID, model: MODEL },
281
- };
282
- // Remove undefined keys
283
- for (const [k, v] of Object.entries(dump.received)) {
284
- if (v === undefined) delete dump.received[k];
285
- }
286
-
287
- const reply = `**Echo from ${AGENT_ID}**\n\nI received:\n\`\`\`json\n${JSON.stringify(dump, null, 2)}\n\`\`\`\n\nUse trigger words: \`/context\` \`/history\` \`/history2\` \`/history3\` \`/agents\` \`/delegate\` \`/trace\``;
288
- sendStreamingReply(sessionKey, reply, messageId);
289
- }
290
-
291
- // ── /context — fetch channel context ──
292
-
293
- async function handleContextQuery(sessionKey, messageId) {
294
- logInfo("[/context] Fetching channel context...");
295
- const channels = await apiGet("/channels");
296
- const agents = await apiGet("/v2/agents");
297
-
298
- const reply = `**Channel Context**\n\n**Channels:**\n\`\`\`json\n${JSON.stringify(channels.channels, null, 2)}\n\`\`\`\n\n**Agents:**\n\`\`\`json\n${JSON.stringify(agents.agents.map(a => ({ id: a.id, name: a.name, type: a.type, role: a.role, status: a.status, skills: a.skills })), null, 2)}\n\`\`\``;
299
- sendStreamingReply(sessionKey, reply, messageId);
300
- }
301
-
302
- // ── /history, /history2, /history3 — fetch history at different verbose levels ──
303
-
304
- async function handleHistoryQuery(sessionKey, messageId, level) {
305
- logInfo(`[/history${level > 1 ? level : ""}] Fetching L${level} history...`);
306
- const qs = `sessionKey=${encodeURIComponent(sessionKey)}&verboseLevel=${level}&limit=10`;
307
- const data = await apiGet(`/v2/messages/query?${qs}`);
308
-
309
- const levelLabel = { 1: "L1 (conclusions)", 2: "L2 (+ thinking)", 3: "L3 (+ references)" }[level];
310
- let reply = `**History — ${levelLabel}** (${data.messages.length} messages)\n\n`;
311
-
312
- for (const m of data.messages) {
313
- const sender = m.sender === "user" ? "You" : (m.senderAgentName || m.senderAgentId || "Agent");
314
- const time = new Date(m.timestamp * 1000).toLocaleTimeString();
315
- reply += `**[${time}] ${sender}:** ${trunc(m.text, 200)}\n`;
316
-
317
- if (m.traces && m.traces.length > 0) {
318
- for (const t of m.traces) {
319
- reply += ` _lv${t.verboseLevel} ${t.traceType}:_ ${trunc(t.content, 150)}\n`;
320
- }
321
- }
322
- reply += "\n";
323
- }
324
-
325
- reply += `_hasMore: ${data.hasMore}_`;
326
- sendStreamingReply(sessionKey, reply, messageId);
327
- }
328
-
329
- // ── /agents — list all agents with skills ──
330
-
331
- async function handleAgentsQuery(sessionKey, messageId) {
332
- logInfo("[/agents] Fetching agent list...");
333
- const data = await apiGet("/v2/agents");
334
-
335
- let reply = `**Available Agents** (${data.agents.length})\n\n`;
336
- for (const a of data.agents) {
337
- const status = a.status === "connected" ? "🟢" : "⚫";
338
- reply += `${status} **${a.name}** (${a.type}, role: ${a.role})\n`;
339
- reply += ` ID: \`${a.id}\`\n`;
340
- if (a.skills.length > 0) {
341
- reply += ` Skills: ${a.skills.map(s => s.name).join(", ")}\n`;
342
- }
343
- reply += ` Capabilities: ${a.capabilities.join(", ")}\n\n`;
344
- }
345
- sendStreamingReply(sessionKey, reply, messageId);
346
- }
347
-
348
- // ── /delegate — simulate agent.request to another agent ──
349
-
350
- async function handleDelegate(sessionKey, messageId, text) {
351
- // Parse: /delegate <agentId> <message>
352
- const parts = text.replace("/delegate", "").trim().split(/\s+/);
353
- if (parts.length < 2) {
354
- sendReply(sessionKey, "Usage: `/delegate <agentId> <message>`\n\nExample: `/delegate agt_xxx Please review this code`", messageId);
355
- return;
356
- }
357
-
358
- const targetId = parts[0];
359
- const delegateText = parts.slice(1).join(" ");
360
- const requestId = randomUUID();
361
-
362
- logInfo(`[/delegate] Sending agent.request to ${targetId}: "${trunc(delegateText, 40)}"`);
363
-
364
- // First, tell the user what we're doing
365
- sendReply(sessionKey, `Delegating to agent \`${targetId}\`:\n> ${delegateText}\n\n_Waiting for response..._`, randomUUID());
366
-
367
- // Send agent.request
368
- send({
369
- type: "agent.request",
370
- agentId: AGENT_ID,
371
- targetAgentId: targetId,
372
- sessionKey,
373
- text: delegateText,
374
- requestId,
375
- depth: 0,
376
- context: {
377
- summary: `User asked me to delegate this task. Original message: "${trunc(text, 200)}"`,
378
- constraints: ["Reply concisely"],
379
- expectedOutput: "Task result",
380
- },
381
- });
382
- logSend(`[agent.request] requestId=${requestId} target=${targetId}`);
383
- }
384
-
385
- // ── /trace — send sample lv2 + lv3 traces ──
386
-
387
- async function handleTraceSample(sessionKey, messageId) {
388
- logInfo("[/trace] Sending sample traces...");
389
-
390
- // lv2: thinking
391
- send({
392
- type: "agent.trace", agentId: AGENT_ID, sessionKey, messageId,
393
- verboseLevel: 2, traceType: "thinking",
394
- content: "Analyzing user request... The user wants to test trace functionality. I should demonstrate lv2 (thinking) and lv3 (tool calls) traces.",
395
- });
396
-
397
- // lv2: planning
398
- send({
399
- type: "agent.trace", agentId: AGENT_ID, sessionKey, messageId,
400
- verboseLevel: 2, traceType: "planning",
401
- content: "Plan: 1) Send thinking trace 2) Send file_read trace 3) Send command_exec trace 4) Return summary",
402
- });
403
-
404
- // lv3: file_read
405
- send({
406
- type: "agent.trace", agentId: AGENT_ID, sessionKey, messageId,
407
- verboseLevel: 3, traceType: "file_read",
408
- content: "// packages/api/src/index.ts\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\n// ... (42 lines)",
409
- metadata: { path: "packages/api/src/index.ts", lines: 42 },
410
- });
411
-
412
- // lv3: command_exec
413
- send({
414
- type: "agent.trace", agentId: AGENT_ID, sessionKey, messageId,
415
- verboseLevel: 3, traceType: "command_exec",
416
- content: "$ npm test\n\n> botschat@0.1.0 test\n> vitest run\n\n✓ 12 tests passed\n✓ 0 tests failed",
417
- metadata: { command: "npm test", exitCode: 0 },
418
- });
419
-
420
- await sleep(100);
421
-
422
- const reply = `**Trace Sample Sent**\n\nI just sent 4 traces attached to this message:\n- lv2 **thinking**: Analysis of your request\n- lv2 **planning**: Execution plan\n- lv3 **file_read**: Read \`packages/api/src/index.ts\` (42 lines)\n- lv3 **command_exec**: Ran \`npm test\` (12 tests passed)\n\nUse \`/history2\` or \`/history3\` to see them in the history query.`;
423
- sendStreamingReply(sessionKey, reply, messageId);
424
- logSend("[agent.trace] 4 traces + reply sent");
425
- }
426
-
427
- // ── Reply helpers ──
428
-
429
- function sendReply(sessionKey, text, messageId) {
430
- send({ type: "agent.text", agentId: AGENT_ID, sessionKey, text, messageId: messageId || randomUUID() });
431
- logSend(`[agent.text] "${trunc(text, 60)}"`);
432
- }
433
-
434
- async function sendStreamingReply(sessionKey, text, messageId) {
435
- const runId = randomUUID().slice(0, 8);
436
- send({ type: "agent.stream.start", agentId: AGENT_ID, sessionKey, runId });
437
-
438
- const words = text.split(" ");
439
- let accumulated = "";
440
- for (let i = 0; i < words.length; i++) {
441
- accumulated += (i > 0 ? " " : "") + words[i];
442
- send({ type: "agent.stream.chunk", agentId: AGENT_ID, sessionKey, runId, text: accumulated });
443
- if (i % 5 === 0) await sleep(20);
444
- }
445
-
446
- send({ type: "agent.text", agentId: AGENT_ID, sessionKey, text, messageId: messageId || randomUUID() });
447
- send({ type: "agent.stream.end", agentId: AGENT_ID, sessionKey, runId });
448
- logSend(`[stream] ${words.length} words`);
449
- }
450
-
451
- // ── Utils ──
452
-
453
- function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
454
- function trunc(s, n) { return s && s.length > n ? s.slice(0, n) + "…" : (s || ""); }
455
-
456
- // ── Shutdown ──
457
-
458
- process.on("SIGINT", () => { intentionalClose = true; ws?.close(1000); process.exit(0); });
459
- process.on("SIGTERM", () => { intentionalClose = true; ws?.close(1000); process.exit(0); });
460
-
461
- // ── Start ──
462
-
463
- console.log(`
464
- ${c.cyan}╭──────────────────────────────────────────╮
465
- │ Mock OpenClaw v2 (Multi-Agent) │
466
- │ Testing with context & history │
467
- ╰──────────────────────────────────────────╯${c.reset}
468
- Server: ${SERVER_URL}
469
- Agent ID: ${AGENT_ID}
470
- Token: ${TOKEN.slice(0, 12)}***
471
- OC Agents: ${OC_AGENTS.join(", ")}
472
- Model: ${MODEL}
473
- Delay: ${DELAY_MS}ms
474
-
475
- ${c.green}Trigger words:${c.reset}
476
- /echo Echo raw message JSON
477
- /context Fetch channel context
478
- /history L1 history (conclusions)
479
- /history2 L2 history (+ thinking)
480
- /history3 L3 history (+ references)
481
- /agents List agents + skills
482
- /delegate Agent-to-Agent delegation
483
- /trace Send sample lv2/lv3 traces
484
- `);
485
-
486
- connect();