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.
- package/package.json +1 -1
- package/packages/api/src/do/connection-do.ts +186 -382
- package/packages/api/src/index.ts +50 -67
- package/packages/api/src/routes/agents.ts +3 -3
- package/packages/api/src/routes/auth.ts +1 -0
- package/packages/api/src/routes/channels.ts +11 -11
- package/packages/api/src/routes/demo.ts +156 -0
- package/packages/api/src/routes/sessions.ts +5 -5
- package/packages/api/src/routes/tasks.ts +33 -33
- package/packages/plugin/dist/src/channel.js +50 -0
- package/packages/plugin/dist/src/channel.js.map +1 -1
- package/packages/plugin/package.json +18 -2
- package/packages/web/dist/assets/index-BtPyCBCl.css +1 -0
- package/packages/web/dist/assets/index-BtpsFe4Z.js +2 -0
- package/packages/web/dist/assets/index-CQbIYr6_.js +2 -0
- package/packages/web/dist/assets/{index-CYQMu_-c.js → index-C_GamcQc.js} +1 -1
- package/packages/web/dist/assets/index-LiBjPMg2.js +1 -0
- package/packages/web/dist/assets/{index-DYCO-ry1.js → index-MyoWvQAH.js} +1 -1
- package/packages/web/dist/assets/index-STIPTMK8.js +1516 -0
- package/packages/web/dist/assets/{index.esm-CvOpngZM.js → index.esm-BpQAwtdR.js} +1 -1
- package/packages/web/dist/assets/{web-D3LMODYp.js → web-BbTzVNLt.js} +1 -1
- package/packages/web/dist/assets/{web-1cdhq2RW.js → web-cnzjgNfD.js} +1 -1
- package/packages/web/dist/index.html +2 -2
- package/packages/web/src/App.tsx +9 -56
- package/packages/web/src/api.ts +5 -61
- package/packages/web/src/components/ChatWindow.tsx +9 -9
- package/packages/web/src/components/CronDetail.tsx +1 -1
- package/packages/web/src/components/ImageLightbox.tsx +96 -0
- package/packages/web/src/components/LoginPage.tsx +59 -1
- package/packages/web/src/components/MessageContent.tsx +17 -2
- package/packages/web/src/components/SessionTabs.tsx +1 -1
- package/packages/web/src/components/Sidebar.tsx +1 -3
- package/packages/web/src/hooks/useIMEComposition.ts +14 -9
- package/packages/web/src/store.ts +7 -39
- package/packages/web/src/ws.ts +0 -1
- package/scripts/dev.sh +0 -53
- package/migrations/0013_agents_table.sql +0 -29
- package/migrations/0014_agent_sessions.sql +0 -19
- package/migrations/0015_message_traces.sql +0 -27
- package/migrations/0016_multi_agent_channels_messages.sql +0 -9
- package/migrations/0017_rename_cron_job_id.sql +0 -2
- package/packages/api/src/protocol-v2.ts +0 -154
- package/packages/api/src/routes/agents-v2.ts +0 -192
- package/packages/api/src/routes/history-v2.ts +0 -221
- package/packages/api/src/routes/migrate-v2.ts +0 -110
- package/packages/web/dist/assets/index-BARPtt0v.css +0 -1
- package/packages/web/dist/assets/index-Bf-XL3te.js +0 -2
- package/packages/web/dist/assets/index-CYlvfpX9.js +0 -1519
- package/packages/web/dist/assets/index-CxcpA4Qo.js +0 -1
- package/packages/web/dist/assets/index-QebPVqwj.js +0 -2
- package/packages/web/src/components/AgentSettings.tsx +0 -328
- 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();
|