@botcord/daemon 0.2.60 → 0.2.61
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/dist/gateway/channels/botcord.d.ts +7 -0
- package/dist/gateway/channels/botcord.js +3 -1
- package/dist/gateway/dispatcher.js +3 -0
- package/dist/gateway/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/gateway/__tests__/botcord-channel.test.ts +77 -0
- package/src/gateway/__tests__/dispatcher.test.ts +4 -0
- package/src/gateway/channels/botcord.ts +10 -1
- package/src/gateway/dispatcher.ts +3 -0
- package/src/gateway/types.ts +1 -0
|
@@ -23,6 +23,13 @@ export interface BotCordChannelClient {
|
|
|
23
23
|
hub_msg_id?: string;
|
|
24
24
|
message_id?: string;
|
|
25
25
|
} & Record<string, unknown>>;
|
|
26
|
+
sendTypedMessage?(to: string, type: "result" | "error", text: string, options?: {
|
|
27
|
+
replyTo?: string;
|
|
28
|
+
topic?: string;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
hub_msg_id?: string;
|
|
31
|
+
message_id?: string;
|
|
32
|
+
} & Record<string, unknown>>;
|
|
26
33
|
getHubUrl(): string;
|
|
27
34
|
onTokenRefresh?: (token: string, expiresAt: number) => void;
|
|
28
35
|
}
|
|
@@ -674,7 +674,9 @@ export function createBotCordChannel(options) {
|
|
|
674
674
|
options.replyTo = message.replyTo;
|
|
675
675
|
if (message.threadId)
|
|
676
676
|
options.topic = message.threadId;
|
|
677
|
-
const resp =
|
|
677
|
+
const resp = message.type === "error" && client.sendTypedMessage
|
|
678
|
+
? await client.sendTypedMessage(message.conversationId, "error", message.text, options)
|
|
679
|
+
: await client.sendMessage(message.conversationId, message.text, options);
|
|
678
680
|
const providerMessageId = (resp && typeof resp.hub_msg_id === "string" && resp.hub_msg_id) ||
|
|
679
681
|
(resp && typeof resp.message_id === "string"
|
|
680
682
|
? resp.message_id
|
|
@@ -1022,6 +1022,7 @@ export class Dispatcher {
|
|
|
1022
1022
|
accountId: msg.accountId,
|
|
1023
1023
|
conversationId: msg.conversation.id,
|
|
1024
1024
|
threadId: msg.conversation.threadId ?? null,
|
|
1025
|
+
type: "error",
|
|
1025
1026
|
text: `⚠️ Runtime timeout after ${Math.round(this.turnTimeoutMs / 60000)} minute(s); aborted`,
|
|
1026
1027
|
replyTo: msg.id,
|
|
1027
1028
|
traceId: msg.trace?.id ?? null,
|
|
@@ -1067,6 +1068,7 @@ export class Dispatcher {
|
|
|
1067
1068
|
accountId: msg.accountId,
|
|
1068
1069
|
conversationId: msg.conversation.id,
|
|
1069
1070
|
threadId: msg.conversation.threadId ?? null,
|
|
1071
|
+
type: "error",
|
|
1070
1072
|
text: `⚠️ Runtime error: ${truncate(errMsg, 500)}`,
|
|
1071
1073
|
replyTo: msg.id,
|
|
1072
1074
|
traceId: msg.trace?.id ?? null,
|
|
@@ -1157,6 +1159,7 @@ export class Dispatcher {
|
|
|
1157
1159
|
accountId: msg.accountId,
|
|
1158
1160
|
conversationId: msg.conversation.id,
|
|
1159
1161
|
threadId: msg.conversation.threadId ?? null,
|
|
1162
|
+
type: "error",
|
|
1160
1163
|
text: `⚠️ Runtime error: ${truncate(result.error, 500)}`,
|
|
1161
1164
|
replyTo: msg.id,
|
|
1162
1165
|
traceId: msg.trace?.id ?? null,
|
package/dist/gateway/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -27,6 +27,9 @@ function makeClient(overrides: Partial<BotCordChannelClient> = {}): BotCordChann
|
|
|
27
27
|
sendMessage: vi
|
|
28
28
|
.fn()
|
|
29
29
|
.mockResolvedValue({ hub_msg_id: "m_provider", queued: true, status: "queued" }),
|
|
30
|
+
sendTypedMessage: vi
|
|
31
|
+
.fn()
|
|
32
|
+
.mockResolvedValue({ hub_msg_id: "m_provider_typed", queued: true, status: "queued" }),
|
|
30
33
|
getHubUrl: vi.fn().mockReturnValue("http://127.0.0.1:1"),
|
|
31
34
|
...overrides,
|
|
32
35
|
};
|
|
@@ -138,6 +141,34 @@ describe("createBotCordChannel — send()", () => {
|
|
|
138
141
|
expect(client.sendMessage).toHaveBeenCalledWith("rm_dm_1", "hey", {});
|
|
139
142
|
expect(result.providerMessageId).toBeNull();
|
|
140
143
|
});
|
|
144
|
+
|
|
145
|
+
it("sends runtime diagnostics as BotCord error envelopes", async () => {
|
|
146
|
+
const client = makeClient();
|
|
147
|
+
const channel = createBotCordChannel({
|
|
148
|
+
id: "botcord-main",
|
|
149
|
+
accountId: "ag_self",
|
|
150
|
+
agentId: "ag_self",
|
|
151
|
+
client,
|
|
152
|
+
});
|
|
153
|
+
const result = await channel.send({
|
|
154
|
+
message: {
|
|
155
|
+
channel: "botcord",
|
|
156
|
+
accountId: "ag_self",
|
|
157
|
+
conversationId: "rm_group_a",
|
|
158
|
+
threadId: "tp_42",
|
|
159
|
+
replyTo: "env_source",
|
|
160
|
+
type: "error",
|
|
161
|
+
text: "Runtime error: boom",
|
|
162
|
+
},
|
|
163
|
+
log: silentLog,
|
|
164
|
+
});
|
|
165
|
+
expect(client.sendTypedMessage).toHaveBeenCalledWith("rm_group_a", "error", "Runtime error: boom", {
|
|
166
|
+
topic: "tp_42",
|
|
167
|
+
replyTo: "env_source",
|
|
168
|
+
});
|
|
169
|
+
expect(client.sendMessage).not.toHaveBeenCalled();
|
|
170
|
+
expect(result.providerMessageId).toBe("m_provider_typed");
|
|
171
|
+
});
|
|
141
172
|
});
|
|
142
173
|
|
|
143
174
|
// ---------------------------------------------------------------------------
|
|
@@ -263,6 +294,52 @@ describe("createBotCordChannel — inbox normalization", () => {
|
|
|
263
294
|
}
|
|
264
295
|
});
|
|
265
296
|
|
|
297
|
+
it("acks error InboxMessages without dispatching them", async () => {
|
|
298
|
+
const server = await startAuthOkServer();
|
|
299
|
+
const errorMessage = makeInbox({
|
|
300
|
+
hub_msg_id: "m_error_1",
|
|
301
|
+
text: undefined,
|
|
302
|
+
envelope: {
|
|
303
|
+
type: "error",
|
|
304
|
+
from: "ag_peer",
|
|
305
|
+
payload: { error: { code: "agent_error", message: "Runtime error: boom" } },
|
|
306
|
+
} as InboxMessage["envelope"],
|
|
307
|
+
});
|
|
308
|
+
const client = makeClient({
|
|
309
|
+
pollInbox: vi.fn().mockResolvedValue({ messages: [errorMessage], count: 1, has_more: false }),
|
|
310
|
+
getHubUrl: vi.fn().mockReturnValue(server.url),
|
|
311
|
+
});
|
|
312
|
+
const channel = createBotCordChannel({
|
|
313
|
+
id: "botcord-main",
|
|
314
|
+
accountId: "ag_self",
|
|
315
|
+
agentId: "ag_self",
|
|
316
|
+
client,
|
|
317
|
+
hubBaseUrl: server.url,
|
|
318
|
+
});
|
|
319
|
+
const abort = new AbortController();
|
|
320
|
+
const emits: GatewayInboundEnvelope[] = [];
|
|
321
|
+
const startPromise = channel.start({
|
|
322
|
+
config: stubConfig,
|
|
323
|
+
accountId: "ag_self",
|
|
324
|
+
abortSignal: abort.signal,
|
|
325
|
+
log: silentLog,
|
|
326
|
+
emit: async (env) => {
|
|
327
|
+
emits.push(env);
|
|
328
|
+
},
|
|
329
|
+
setStatus: () => {},
|
|
330
|
+
});
|
|
331
|
+
try {
|
|
332
|
+
await vi.waitFor(() => {
|
|
333
|
+
expect(client.ackMessages).toHaveBeenCalledWith(["m_error_1"]);
|
|
334
|
+
});
|
|
335
|
+
expect(emits).toHaveLength(0);
|
|
336
|
+
} finally {
|
|
337
|
+
abort.abort();
|
|
338
|
+
await startPromise;
|
|
339
|
+
await server.close();
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
266
343
|
it("marks rm_dm_ and rm_oc_ rooms as direct; rm_oc_ also sets streamable + user-kind", async () => {
|
|
267
344
|
const { emits, server } = await startWithInbox([
|
|
268
345
|
makeInbox({
|
|
@@ -492,6 +492,7 @@ describe("Dispatcher", () => {
|
|
|
492
492
|
await dispatcher.handle(makeEnvelope({ id: "msg_error" }));
|
|
493
493
|
|
|
494
494
|
expect(channel.sends.length).toBe(1);
|
|
495
|
+
expect(channel.sends[0].message.type).toBe("error");
|
|
495
496
|
expect(channel.sends[0].message.text).toContain("Runtime error");
|
|
496
497
|
expect(channel.sends[0].message.text).toContain("missing openclawAgent");
|
|
497
498
|
});
|
|
@@ -1256,6 +1257,7 @@ describe("Dispatcher", () => {
|
|
|
1256
1257
|
|
|
1257
1258
|
await dispatcher.handle(makeEnvelope({ id: "m1" }));
|
|
1258
1259
|
expect(channel.sends.length).toBe(1);
|
|
1260
|
+
expect(channel.sends[0].message.type).toBe("error");
|
|
1259
1261
|
expect(channel.sends[0].message.text).toContain("Runtime error");
|
|
1260
1262
|
expect(channel.sends[0].message.text).toContain("boom");
|
|
1261
1263
|
expect(store.all().length).toBe(0);
|
|
@@ -1921,6 +1923,7 @@ describe("Dispatcher", () => {
|
|
|
1921
1923
|
await p;
|
|
1922
1924
|
expect(runtime.calls[0].signal.aborted).toBe(true);
|
|
1923
1925
|
expect(channel.sends.length).toBe(1);
|
|
1926
|
+
expect(channel.sends[0].message.type).toBe("error");
|
|
1924
1927
|
expect(channel.sends[0].message.text).toMatch(/Runtime timeout/);
|
|
1925
1928
|
expect(channel.sends[0].message.conversationId).toBe("rm_g_other");
|
|
1926
1929
|
expect(channel.sends[0].message.replyTo).toBe("m_to");
|
|
@@ -1941,6 +1944,7 @@ describe("Dispatcher", () => {
|
|
|
1941
1944
|
}),
|
|
1942
1945
|
);
|
|
1943
1946
|
expect(channel.sends.length).toBe(1);
|
|
1947
|
+
expect(channel.sends[0].message.type).toBe("error");
|
|
1944
1948
|
expect(channel.sends[0].message.text).toContain("Runtime error: boom");
|
|
1945
1949
|
expect(channel.sends[0].message.conversationId).toBe("rm_g_other");
|
|
1946
1950
|
expect(channel.sends[0].message.replyTo).toBe("m_err");
|
|
@@ -55,6 +55,12 @@ export interface BotCordChannelClient {
|
|
|
55
55
|
text: string,
|
|
56
56
|
options?: { replyTo?: string; topic?: string },
|
|
57
57
|
): Promise<{ hub_msg_id?: string; message_id?: string } & Record<string, unknown>>;
|
|
58
|
+
sendTypedMessage?(
|
|
59
|
+
to: string,
|
|
60
|
+
type: "result" | "error",
|
|
61
|
+
text: string,
|
|
62
|
+
options?: { replyTo?: string; topic?: string },
|
|
63
|
+
): Promise<{ hub_msg_id?: string; message_id?: string } & Record<string, unknown>>;
|
|
58
64
|
getHubUrl(): string;
|
|
59
65
|
onTokenRefresh?: (token: string, expiresAt: number) => void;
|
|
60
66
|
}
|
|
@@ -804,7 +810,10 @@ export function createBotCordChannel(options: BotCordChannelOptions): ChannelAda
|
|
|
804
810
|
const options: { replyTo?: string; topic?: string } = {};
|
|
805
811
|
if (message.replyTo) options.replyTo = message.replyTo;
|
|
806
812
|
if (message.threadId) options.topic = message.threadId;
|
|
807
|
-
const resp =
|
|
813
|
+
const resp =
|
|
814
|
+
message.type === "error" && client.sendTypedMessage
|
|
815
|
+
? await client.sendTypedMessage(message.conversationId, "error", message.text, options)
|
|
816
|
+
: await client.sendMessage(message.conversationId, message.text, options);
|
|
808
817
|
const providerMessageId =
|
|
809
818
|
(resp && typeof resp.hub_msg_id === "string" && resp.hub_msg_id) ||
|
|
810
819
|
(resp && typeof (resp as { message_id?: unknown }).message_id === "string"
|
|
@@ -1264,6 +1264,7 @@ export class Dispatcher {
|
|
|
1264
1264
|
accountId: msg.accountId,
|
|
1265
1265
|
conversationId: msg.conversation.id,
|
|
1266
1266
|
threadId: msg.conversation.threadId ?? null,
|
|
1267
|
+
type: "error",
|
|
1267
1268
|
text: `⚠️ Runtime timeout after ${Math.round(this.turnTimeoutMs / 60000)} minute(s); aborted`,
|
|
1268
1269
|
replyTo: msg.id,
|
|
1269
1270
|
traceId: msg.trace?.id ?? null,
|
|
@@ -1309,6 +1310,7 @@ export class Dispatcher {
|
|
|
1309
1310
|
accountId: msg.accountId,
|
|
1310
1311
|
conversationId: msg.conversation.id,
|
|
1311
1312
|
threadId: msg.conversation.threadId ?? null,
|
|
1313
|
+
type: "error",
|
|
1312
1314
|
text: `⚠️ Runtime error: ${truncate(errMsg, 500)}`,
|
|
1313
1315
|
replyTo: msg.id,
|
|
1314
1316
|
traceId: msg.trace?.id ?? null,
|
|
@@ -1398,6 +1400,7 @@ export class Dispatcher {
|
|
|
1398
1400
|
accountId: msg.accountId,
|
|
1399
1401
|
conversationId: msg.conversation.id,
|
|
1400
1402
|
threadId: msg.conversation.threadId ?? null,
|
|
1403
|
+
type: "error",
|
|
1401
1404
|
text: `⚠️ Runtime error: ${truncate(result.error, 500)}`,
|
|
1402
1405
|
replyTo: msg.id,
|
|
1403
1406
|
traceId: msg.trace?.id ?? null,
|
package/src/gateway/types.ts
CHANGED