@ai-setting/roy-agent-core 1.7.77 → 1.7.85

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 (66) hide show
  1. package/dist/config/config-protection.d.ts +89 -0
  2. package/dist/config/index.d.ts +1 -0
  3. package/dist/config/index.js +16 -0
  4. package/dist/env/agent/agent-component.d.ts +42 -21
  5. package/dist/env/agent/agent-component.js +2 -2
  6. package/dist/env/agent/index.js +2 -2
  7. package/dist/env/agent/types.d.ts +32 -12
  8. package/dist/env/error-reply.d.ts +6 -0
  9. package/dist/env/error-reply.js +1 -1
  10. package/dist/env/event-source/bounty-im-handler.d.ts +8 -0
  11. package/dist/env/event-source/es-command.d.ts +39 -0
  12. package/dist/env/event-source/es-plugins.d.ts +68 -0
  13. package/dist/env/event-source/event-source-component.d.ts +15 -0
  14. package/dist/env/event-source/event-source-handlers.d.ts +68 -0
  15. package/dist/env/event-source/event-source-handlers.js +20 -8
  16. package/dist/env/event-source/index.d.ts +5 -1
  17. package/dist/env/event-source/index.js +57 -8
  18. package/dist/env/event-source/types.d.ts +101 -1
  19. package/dist/env/event-source/wecom-card-action-handler.d.ts +141 -0
  20. package/dist/env/event-source/wecom-card-builder.d.ts +220 -0
  21. package/dist/env/event-source/wecom-cli-handler.d.ts +266 -0
  22. package/dist/env/event-source/wecom-cli-sender.d.ts +88 -0
  23. package/dist/env/event-source/wecom-media.d.ts +130 -0
  24. package/dist/env/event-source/wecom-respond-registry.d.ts +151 -0
  25. package/dist/env/event-source/wecom-ws-bridge.d.ts +213 -0
  26. package/dist/env/index.js +16 -14
  27. package/dist/env/task/delegate/delegate-tool.d.ts +62 -2
  28. package/dist/env/task/delegate/delegate-tool.js +3 -1
  29. package/dist/env/task/delegate/index.js +1 -1
  30. package/dist/env/task/index.js +2 -2
  31. package/dist/env/task/plugins/index.d.ts +2 -0
  32. package/dist/env/task/plugins/index.js +23 -2
  33. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +41 -2
  34. package/dist/env/task/plugins/lark-card-patch-notifier.js +1 -1
  35. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +16 -0
  36. package/dist/env/task/plugins/wecom-card-state-store.d.ts +121 -0
  37. package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +224 -0
  38. package/dist/env/tool/built-in/index.d.ts +2 -0
  39. package/dist/env/tool/built-in/index.js +6 -1
  40. package/dist/env/tool/index.js +3 -2
  41. package/dist/env/workflow/index.js +3 -3
  42. package/dist/env/workflow/plugins/index.js +2 -2
  43. package/dist/env/workflow/tools/index.js +2 -2
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.js +68 -17
  46. package/dist/shared/@ai-setting/{roy-agent-core-v87njmq7.js → roy-agent-core-0dh8npnz.js} +1 -1
  47. package/dist/shared/@ai-setting/roy-agent-core-2stt4rp5.js +134 -0
  48. package/dist/shared/@ai-setting/{roy-agent-core-mww0mnd9.js → roy-agent-core-7bpj859q.js} +1 -1
  49. package/dist/shared/@ai-setting/{roy-agent-core-w0qjeq2v.js → roy-agent-core-7dd0s3xb.js} +14 -4
  50. package/dist/shared/@ai-setting/{roy-agent-core-rsn44n9y.js → roy-agent-core-b31v3qpv.js} +851 -46
  51. package/dist/shared/@ai-setting/{roy-agent-core-0v27y99n.js → roy-agent-core-begm7ytz.js} +63 -2
  52. package/dist/shared/@ai-setting/{roy-agent-core-aa2j7bk9.js → roy-agent-core-cha0jc7n.js} +40 -26
  53. package/dist/shared/@ai-setting/{roy-agent-core-hd3n57zs.js → roy-agent-core-d1n4jbza.js} +1 -1
  54. package/dist/shared/@ai-setting/{roy-agent-core-9vhq7fvv.js → roy-agent-core-dkrvmjsh.js} +12 -74
  55. package/dist/shared/@ai-setting/{roy-agent-core-99nwgj84.js → roy-agent-core-fzw9xzm1.js} +1 -1
  56. package/dist/shared/@ai-setting/roy-agent-core-m3vjepzz.js +544 -0
  57. package/dist/shared/@ai-setting/{roy-agent-core-ew6qb5yx.js → roy-agent-core-p6ks90tb.js} +1 -1
  58. package/dist/shared/@ai-setting/{roy-agent-core-rhtdspx6.js → roy-agent-core-qk9hd89p.js} +1 -1
  59. package/dist/shared/@ai-setting/{roy-agent-core-6cy43nss.js → roy-agent-core-rykf7c04.js} +49 -9
  60. package/dist/shared/@ai-setting/{roy-agent-core-8ez6mccb.js → roy-agent-core-tk98f92b.js} +4 -0
  61. package/dist/shared/@ai-setting/{roy-agent-core-bxkjk7zx.js → roy-agent-core-vx8zc847.js} +4 -1
  62. package/dist/shared/@ai-setting/roy-agent-core-vyr6svt6.js +31 -0
  63. package/dist/shared/@ai-setting/{roy-agent-core-3gpygqcx.js → roy-agent-core-xf6z96qy.js} +2086 -163
  64. package/package.json +10 -1
  65. /package/dist/shared/@ai-setting/{roy-agent-core-b4x6x4k2.js → roy-agent-core-m1fgtnrc.js} +0 -0
  66. /package/dist/shared/@ai-setting/{roy-agent-core-v85n42ve.js → roy-agent-core-ze5cqpf2.js} +0 -0
@@ -0,0 +1,544 @@
1
+ import {
2
+ getEnvContextOrEmpty,
3
+ init_env_context
4
+ } from "./roy-agent-core-7cbpmk4q.js";
5
+
6
+ // src/env/event-source/wecom-respond-registry.ts
7
+ init_env_context();
8
+ import { z } from "zod";
9
+
10
+ // src/env/event-source/wecom-cli-sender.ts
11
+ import { spawn } from "child_process";
12
+ var WECOM_CLI_SEND_TIMEOUT_MS = 15000;
13
+ var WECOM_CLI_RESOLVE_TIMEOUT_MS = 15000;
14
+ function resolveWecomCliBinary() {
15
+ const fromEnv = process.env.WECOM_CLI_BIN;
16
+ if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
17
+ return fromEnv.trim();
18
+ }
19
+ return "wecom-cli";
20
+ }
21
+ function buildWecomCliSendArgs(input) {
22
+ return [
23
+ "message",
24
+ "aibot",
25
+ "send",
26
+ "--chat-id",
27
+ input.chatId,
28
+ "--msg-type",
29
+ "markdown",
30
+ "--markdown",
31
+ JSON.stringify({ content: input.content })
32
+ ];
33
+ }
34
+ function buildWecomCliResolveArgs(keywords) {
35
+ return ["contact", "users", "search", "--set", `keywords=${keywords}`];
36
+ }
37
+ function parseWecomCliResult(stdout) {
38
+ const text = (stdout ?? "").trim();
39
+ if (!text)
40
+ return { ok: false, error: "empty stdout" };
41
+ const start = text.indexOf("{");
42
+ const jsonText = start >= 0 ? text.slice(start) : text;
43
+ let parsed;
44
+ try {
45
+ parsed = JSON.parse(jsonText);
46
+ } catch {
47
+ return { ok: false, error: `non-JSON stdout: ${text.slice(0, 200)}` };
48
+ }
49
+ const obj = parsed;
50
+ if (obj?.error) {
51
+ const code = obj.error.code ?? "?";
52
+ const message = obj.error.message ?? obj.error.type ?? "unknown error";
53
+ return { ok: false, error: `[code=${code}] ${message}` };
54
+ }
55
+ if (obj?.success === true)
56
+ return { ok: true };
57
+ return { ok: true };
58
+ }
59
+ function parseWecomCliUserSearch(stdout, fallback) {
60
+ const text = (stdout ?? "").trim();
61
+ const start = text.indexOf("{");
62
+ if (start < 0)
63
+ return fallback;
64
+ try {
65
+ const parsed = JSON.parse(text.slice(start));
66
+ const userid = parsed?.users?.[0]?.userid;
67
+ return typeof userid === "string" && userid.length > 0 ? userid : fallback;
68
+ } catch {
69
+ return fallback;
70
+ }
71
+ }
72
+ function runCli(args, timeoutMs) {
73
+ return new Promise((resolve) => {
74
+ let settled = false;
75
+ let stdout = "";
76
+ let stderr = "";
77
+ const finish = (code) => {
78
+ if (settled)
79
+ return;
80
+ settled = true;
81
+ clearTimeout(timer);
82
+ resolve({ stdout, stderr, code });
83
+ };
84
+ let proc;
85
+ try {
86
+ proc = spawn(resolveWecomCliBinary(), args, {
87
+ shell: false,
88
+ stdio: ["ignore", "pipe", "pipe"]
89
+ });
90
+ } catch (err) {
91
+ resolve({
92
+ stdout: "",
93
+ stderr: err instanceof Error ? err.message : String(err),
94
+ code: null
95
+ });
96
+ return;
97
+ }
98
+ const timer = setTimeout(() => {
99
+ try {
100
+ proc.kill("SIGKILL");
101
+ } catch {}
102
+ finish(null);
103
+ }, timeoutMs);
104
+ timer.unref?.();
105
+ proc.stdout?.on("data", (d) => {
106
+ stdout += d.toString();
107
+ });
108
+ proc.stderr?.on("data", (d) => {
109
+ stderr += d.toString();
110
+ });
111
+ proc.on("error", (err) => {
112
+ stderr += err.message;
113
+ finish(null);
114
+ });
115
+ proc.on("close", (code) => finish(code));
116
+ });
117
+ }
118
+ var cliChatIdCache = new Map;
119
+ async function resolveWecomCliChatId(userId) {
120
+ const key = userId.trim();
121
+ if (!key)
122
+ return key;
123
+ const cached = cliChatIdCache.get(key);
124
+ if (cached)
125
+ return cached;
126
+ const { stdout } = await runCli(buildWecomCliResolveArgs(key), WECOM_CLI_RESOLVE_TIMEOUT_MS);
127
+ const resolved = parseWecomCliUserSearch(stdout, key);
128
+ if (resolved !== key)
129
+ cliChatIdCache.set(key, resolved);
130
+ return resolved;
131
+ }
132
+ async function sendWecomCliMarkdown(input) {
133
+ const content = input.content ?? "";
134
+ if (!input.chatId || content.trim().length === 0) {
135
+ return { ok: false, error: "empty chatId or content" };
136
+ }
137
+ const { stdout, stderr, code } = await runCli(buildWecomCliSendArgs({ chatId: input.chatId, content }), input.timeoutMs ?? WECOM_CLI_SEND_TIMEOUT_MS);
138
+ const parsed = parseWecomCliResult(stdout);
139
+ if (parsed.ok)
140
+ return parsed;
141
+ const detail = stderr.trim() ? `${parsed.error} | stderr: ${stderr.trim().slice(0, 200)}` : parsed.error;
142
+ return { ok: false, error: `${detail} (exit=${code ?? "null"})` };
143
+ }
144
+
145
+ // src/env/event-source/wecom-card-builder.ts
146
+ var WecomCardActionKey = {
147
+ VIEW: "task_view",
148
+ MARK_DONE: "task_mark_done",
149
+ REOPEN: "task_reopen"
150
+ };
151
+ function wecomCardTaskIdForTask(taskId) {
152
+ return `roy-task-${taskId}`;
153
+ }
154
+ var cardSeq = 0;
155
+ function newWecomCardTaskId(taskId) {
156
+ cardSeq = (cardSeq + 1) % 1e6;
157
+ const rand = Math.random().toString(36).slice(2, 8);
158
+ return `${wecomCardTaskIdForTask(taskId)}-${cardSeq}-${rand}`;
159
+ }
160
+ function formatTs(iso) {
161
+ try {
162
+ const d = new Date(iso);
163
+ if (isNaN(d.getTime()))
164
+ return iso;
165
+ const pad = (n) => String(n).padStart(2, "0");
166
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` + `${pad(d.getHours())}:${pad(d.getMinutes())}`;
167
+ } catch {
168
+ return iso;
169
+ }
170
+ }
171
+ function clip(text, max) {
172
+ if (!text)
173
+ return "";
174
+ const t = text.trim();
175
+ return t.length > max ? `${t.slice(0, max)}…` : t;
176
+ }
177
+ function shortMilestoneValue(text) {
178
+ if (!text)
179
+ return "";
180
+ const t = text.trim();
181
+ const colonIdx = t.indexOf(":");
182
+ const head = colonIdx > 0 && colonIdx <= 20 ? t.slice(0, colonIdx).trim() : t;
183
+ if (head.length <= MAX_HORIZONTAL_VALUE)
184
+ return head;
185
+ return clipWord(head, MAX_HORIZONTAL_VALUE);
186
+ }
187
+ function clipWord(text, max) {
188
+ if (!text)
189
+ return "";
190
+ const t = text.trim();
191
+ if (t.length <= max)
192
+ return t;
193
+ let cut = t.slice(0, max - 1);
194
+ const lastSpace = cut.lastIndexOf(" ");
195
+ if (lastSpace > max * 0.4)
196
+ return `${cut.slice(0, lastSpace).trimEnd()}…`;
197
+ const cjkBoundary = findCjkAsciiBoundary(cut);
198
+ if (cjkBoundary > max * 0.4)
199
+ return `${cut.slice(0, cjkBoundary).trimEnd()}…`;
200
+ return cut.slice(0, max - 1);
201
+ }
202
+ function findCjkAsciiBoundary(text) {
203
+ const isCjk = (ch) => /[一-鿿぀-ヿ가-힯]/.test(ch);
204
+ const isAsciiAlnum = (ch) => /[A-Za-z0-9]/.test(ch);
205
+ for (let i = text.length - 1;i > 0; i--) {
206
+ const prev = text[i - 1];
207
+ const cur = text[i];
208
+ if (isCjk(prev) !== isAsciiAlnum(cur) && isCjk(cur) !== isAsciiAlnum(prev)) {
209
+ if (isCjk(prev) && isAsciiAlnum(cur) || isAsciiAlnum(prev) && isCjk(cur)) {
210
+ return i;
211
+ }
212
+ }
213
+ }
214
+ return -1;
215
+ }
216
+ var MAX_TIMELINE_LINES = 8;
217
+ var MAX_HORIZONTAL_ROWS = 6;
218
+ var MAX_HORIZONTAL_VALUE = 26;
219
+ function buildWecomTextNoticeCard(input) {
220
+ const progressPct = `${Math.max(0, Math.min(100, Math.round(input.progress ?? 0)))}%`;
221
+ const sorted = [...input.milestones].slice(-MAX_TIMELINE_LINES).reverse();
222
+ const timelineRows = sorted.slice(0, MAX_HORIZONTAL_ROWS - 1).map((m) => ({
223
+ keyname: formatTs(m.timestamp).slice(11),
224
+ value: shortMilestoneValue(m.title)
225
+ }));
226
+ const horizontal = [
227
+ { keyname: "状态", value: clip(input.statusLabel, MAX_HORIZONTAL_VALUE) },
228
+ ...timelineRows
229
+ ];
230
+ const latest = sorted[0];
231
+ const quoteLine = latest ? `${formatTs(latest.timestamp)} — ${clipWord(latest.title, 80)}${latest.description ? `:${clipWord(latest.description, 80)}` : ""}` : "";
232
+ const latestSummary = latest && latest.title ? `${formatTs(latest.timestamp)} ${clip(latest.title, 140)}` : undefined;
233
+ const subTitle = input.terminal ? `任务${input.statusLabel.replace(/^[^\u4e00-\u9fa5A-Za-z]*/, "")} · 详见会话` : latestSummary;
234
+ return {
235
+ card_type: "text_notice",
236
+ main_title: {
237
+ title: `\uD83D\uDCCB Task #${input.taskId} · ${clip(input.title, 60)}`,
238
+ desc: clip(input.description, 80) || undefined
239
+ },
240
+ emphasis_content: {
241
+ title: progressPct,
242
+ desc: "进度"
243
+ },
244
+ sub_title_text: subTitle,
245
+ horizontal_content_list: horizontal,
246
+ quote_area: {
247
+ type: 0,
248
+ title: "最新进展",
249
+ quote_text: quoteLine || "(暂无操作记录)"
250
+ },
251
+ card_action: { type: 0 }
252
+ };
253
+ }
254
+ var MAX_BUTTONS = 6;
255
+ var MAX_BUTTON_TEXT = 10;
256
+ function buildWecomButtonInteractionCard(input) {
257
+ const notice = buildWecomTextNoticeCard({
258
+ taskId: input.taskId,
259
+ title: input.title,
260
+ statusLabel: input.statusLabel,
261
+ progress: input.progress,
262
+ description: input.description,
263
+ changeSummary: input.changeSummary,
264
+ milestones: input.milestones ?? []
265
+ });
266
+ const buttons = (input.buttons ?? []).slice(0, MAX_BUTTONS).map((b) => ({
267
+ text: clip(b.text, MAX_BUTTON_TEXT),
268
+ style: b.style ?? 1,
269
+ key: b.key
270
+ }));
271
+ return {
272
+ card_type: "button_interaction",
273
+ main_title: notice.main_title,
274
+ sub_title_text: notice.sub_title_text,
275
+ horizontal_content_list: notice.horizontal_content_list,
276
+ quote_area: notice.quote_area,
277
+ button_list: buttons,
278
+ card_action: { type: 0 },
279
+ task_id: input.cardTaskId ?? newWecomCardTaskId(input.taskId)
280
+ };
281
+ }
282
+ function buildWecomSendCardFrame(input) {
283
+ const reqId = input.reqId && input.reqId.trim() ? input.reqId.trim() : `aibot_send_msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
284
+ return {
285
+ cmd: "aibot_send_msg",
286
+ headers: { req_id: reqId },
287
+ body: {
288
+ chatid: input.chatId,
289
+ chat_type: input.chatType === "group" ? 2 : 1,
290
+ msgtype: "template_card",
291
+ template_card: input.card
292
+ }
293
+ };
294
+ }
295
+ var MAX_UPDATE_TITLE = 60;
296
+ function buildUpdateMainTitle(statusLabel, taskNumber, taskTitle) {
297
+ const segments = [];
298
+ if (taskNumber !== undefined && Number.isFinite(taskNumber)) {
299
+ segments.push(`Task #${taskNumber}`);
300
+ }
301
+ if (taskTitle) {
302
+ segments.push(clip(taskTitle, MAX_UPDATE_TITLE));
303
+ }
304
+ segments.push(statusLabel);
305
+ return segments.length > 1 ? `\uD83D\uDCCB ${segments.join(" · ")}` : `\uD83D\uDCCB ${statusLabel}`;
306
+ }
307
+ function buildWecomCardUpdateFrame(input) {
308
+ const reqId = (input.reqId ?? "").trim();
309
+ if (!reqId) {
310
+ throw new Error("buildWecomCardUpdateFrame: req_id is required " + "(update must reference the template_card_event frame)");
311
+ }
312
+ const buttons = (input.buttonOverrides ?? []).slice(0, MAX_BUTTONS).map((b) => ({
313
+ text: clip(b.text, MAX_BUTTON_TEXT),
314
+ style: b.style ?? 1,
315
+ key: b.key
316
+ }));
317
+ const sorted = [...input.timeline ?? []].reverse();
318
+ const timelineRows = sorted.slice(0, MAX_HORIZONTAL_ROWS).map((m) => ({
319
+ keyname: formatTs(m.timestamp).slice(11),
320
+ value: shortMilestoneValue(m.title)
321
+ }));
322
+ const total = sorted.length;
323
+ const subTitle = total > 0 ? total > MAX_HORIZONTAL_ROWS ? `共 ${total} 条操作记录,显示最近 ${MAX_HORIZONTAL_ROWS} 条(最新在顶部)` : `共 ${total} 条操作记录(最新在顶部)` : undefined;
324
+ return {
325
+ cmd: "aibot_respond_update_msg",
326
+ headers: { req_id: reqId },
327
+ body: {
328
+ response_type: "update_template_card",
329
+ template_card: {
330
+ card_type: "button_interaction",
331
+ main_title: {
332
+ title: buildUpdateMainTitle(input.statusLabel, input.taskNumber, input.taskTitle),
333
+ desc: input.note ? clip(input.note, 80) : undefined
334
+ },
335
+ sub_title_text: subTitle,
336
+ horizontal_content_list: timelineRows,
337
+ button_list: buttons,
338
+ task_id: String(input.taskId)
339
+ }
340
+ }
341
+ };
342
+ }
343
+
344
+ // src/env/event-source/wecom-respond-registry.ts
345
+ var registry = new Map;
346
+ function registerWecomRespondTarget(sourceId, target) {
347
+ registry.set(sourceId, target);
348
+ }
349
+ function unregisterWecomRespondTarget(sourceId) {
350
+ registry.delete(sourceId);
351
+ }
352
+ function getWecomRespondTarget(sourceId) {
353
+ return registry.get(sourceId);
354
+ }
355
+ function __resetWecomRespondRegistryForTest() {
356
+ registry.clear();
357
+ }
358
+ async function sendWecomReply(input) {
359
+ const content = input.content ?? "";
360
+ if (content.trim().length === 0) {
361
+ return { ok: false, error: "empty content" };
362
+ }
363
+ const errors = [];
364
+ if (input.reqId) {
365
+ const target = getWecomRespondTarget(input.sourceId);
366
+ if (target) {
367
+ const wsErr = target.respond(input.reqId, {
368
+ msgtype: "markdown",
369
+ markdown: { content }
370
+ });
371
+ if (!wsErr)
372
+ return { ok: true, channel: "ws" };
373
+ errors.push(`ws: ${wsErr.message}`);
374
+ } else {
375
+ errors.push(`ws: no instance registered for sourceId=${input.sourceId}`);
376
+ }
377
+ } else {
378
+ errors.push("ws: no req_id");
379
+ }
380
+ if (input.rawChatId) {
381
+ try {
382
+ const chatId = await resolveWecomCliChatId(input.rawChatId);
383
+ const cliResult = await sendWecomCliMarkdown({ chatId, content });
384
+ if (cliResult.ok)
385
+ return { ok: true, channel: "cli" };
386
+ errors.push(`cli: ${cliResult.error}`);
387
+ } catch (err) {
388
+ errors.push(`cli: ${err instanceof Error ? err.message : String(err)}`);
389
+ }
390
+ } else {
391
+ errors.push("cli: no chatId");
392
+ }
393
+ return { ok: false, error: errors.join(" | ") };
394
+ }
395
+ async function sendWecomTemplateCard(input) {
396
+ if (!input.chatId) {
397
+ return { ok: false, error: "no chatId" };
398
+ }
399
+ const target = getWecomRespondTarget(input.sourceId);
400
+ if (!target) {
401
+ return {
402
+ ok: false,
403
+ error: `no instance registered for sourceId=${input.sourceId}`
404
+ };
405
+ }
406
+ if (typeof target.sendCmd !== "function") {
407
+ return {
408
+ ok: false,
409
+ error: `registered instance has no sendCmd (old bridge?)`
410
+ };
411
+ }
412
+ const frame = buildWecomSendCardFrame({
413
+ chatId: input.chatId,
414
+ chatType: input.chatType,
415
+ card: input.card
416
+ });
417
+ try {
418
+ const err = target.sendCmd(frame.cmd, frame.body);
419
+ if (err)
420
+ return { ok: false, error: err.message };
421
+ return { ok: true, channel: "ws" };
422
+ } catch (err) {
423
+ return {
424
+ ok: false,
425
+ error: err instanceof Error ? err.message : String(err)
426
+ };
427
+ }
428
+ }
429
+ var wecomRespondTool = {
430
+ name: "wecom_respond",
431
+ description: "Reply to the WeCom (企业微信) user. Primary path is the official wecom-cli " + "(message aibot send, markdown); WS long-connection frame is used as fallback. " + "This is the ONLY outbound channel for WeCom messages.",
432
+ parameters: z.object({
433
+ content: z.string().describe("Reply content (markdown supported)"),
434
+ chat_id: z.string().optional().describe("Target chat id (single chat = peer userid, group = chatid). " + "Defaults to the current event's chatId/senderId."),
435
+ req_id: z.string().optional().describe("Original message frame req_id (optional; used for WS fallback only). " + "Defaults to the current event metadata.reqId.")
436
+ }),
437
+ metadata: {
438
+ category: "messaging",
439
+ tags: ["wecom", "reply", "im"],
440
+ version: "2.0.0"
441
+ },
442
+ execute: async (args, _ctx) => {
443
+ const { content } = args;
444
+ let sourceId;
445
+ let eventChatId;
446
+ let eventReqId;
447
+ try {
448
+ const ctx = getEnvContextOrEmpty();
449
+ sourceId = typeof ctx.sourceId === "string" ? ctx.sourceId : undefined;
450
+ const meta = ctx.envEvent?.metadata;
451
+ const cid = meta?.chatId;
452
+ if (typeof cid === "string" && cid.length > 0)
453
+ eventChatId = cid;
454
+ const rid = meta?.reqId;
455
+ if (typeof rid === "string" && rid.length > 0)
456
+ eventReqId = rid;
457
+ } catch {}
458
+ const chatId = args.chat_id?.trim() || eventChatId;
459
+ const reqId = args.req_id?.trim() || eventReqId;
460
+ if (!chatId) {
461
+ return {
462
+ success: false,
463
+ output: "",
464
+ error: "wecom_respond: cannot resolve chat_id (no explicit chat_id and no EnvContext event chatId)."
465
+ };
466
+ }
467
+ if (!sourceId) {
468
+ sourceId = "unknown";
469
+ }
470
+ const startedAt = Date.now();
471
+ const result = await sendWecomReply({
472
+ sourceId,
473
+ rawChatId: chatId,
474
+ reqId,
475
+ content
476
+ });
477
+ if (!result.ok) {
478
+ return {
479
+ success: false,
480
+ output: "",
481
+ error: `wecom_respond: send failed: ${result.error ?? "unknown"}`
482
+ };
483
+ }
484
+ markWecomReplySent();
485
+ return {
486
+ success: true,
487
+ output: `Reply sent to WeCom via ${result.channel} ` + `(chat_id=${chatId}, ${content.length} chars)`,
488
+ metadata: {
489
+ execution_time_ms: Date.now() - startedAt,
490
+ channel: result.channel,
491
+ chat_id: chatId
492
+ }
493
+ };
494
+ }
495
+ };
496
+ var REPLY_SENT_KEY = "__wecomReplySent";
497
+ function replySentBag(explicit) {
498
+ if (explicit)
499
+ return explicit;
500
+ try {
501
+ const ctx = getEnvContextOrEmpty();
502
+ return ctx.agentContext ??= {};
503
+ } catch {
504
+ return {};
505
+ }
506
+ }
507
+ function markWecomReplySent(explicitBag) {
508
+ replySentBag(explicitBag)[REPLY_SENT_KEY] = true;
509
+ }
510
+ function wasWecomReplySent(explicitBag) {
511
+ return replySentBag(explicitBag)[REPLY_SENT_KEY] === true;
512
+ }
513
+ function __resetWecomReplySentForTest(explicitBag) {
514
+ delete replySentBag(explicitBag)[REPLY_SENT_KEY];
515
+ }
516
+ var WECOM_EVENT_TYPE = "event-source.event.wecom-cli";
517
+ async function autoReplyWecomTurn(envEvent, finalText) {
518
+ const evt = envEvent;
519
+ if (evt?.type !== WECOM_EVENT_TYPE) {
520
+ return { skipped: true, reason: "not a wecom event" };
521
+ }
522
+ const text = (finalText ?? "").trim();
523
+ if (!text)
524
+ return { skipped: true, reason: "empty final text" };
525
+ if (wasWecomReplySent()) {
526
+ return { skipped: true, reason: "already replied via tool" };
527
+ }
528
+ const meta = evt.metadata ?? {};
529
+ const chatId = typeof meta.chatId === "string" && meta.chatId || typeof meta.senderId === "string" && meta.senderId || "";
530
+ const sourceId = typeof meta.sourceId === "string" && meta.sourceId || "unknown";
531
+ const reqId = typeof meta.reqId === "string" ? meta.reqId : undefined;
532
+ if (!chatId) {
533
+ return { skipped: true, reason: "no chatId in event metadata" };
534
+ }
535
+ const r = await sendWecomReply({
536
+ sourceId,
537
+ rawChatId: chatId,
538
+ reqId,
539
+ content: text
540
+ });
541
+ return { skipped: false, ok: r.ok, channel: r.channel, error: r.error };
542
+ }
543
+
544
+ export { WecomCardActionKey, buildWecomTextNoticeCard, buildWecomButtonInteractionCard, buildWecomCardUpdateFrame, registerWecomRespondTarget, unregisterWecomRespondTarget, getWecomRespondTarget, __resetWecomRespondRegistryForTest, sendWecomReply, sendWecomTemplateCard, wecomRespondTool, markWecomReplySent, wasWecomReplySent, __resetWecomReplySentForTest, autoReplyWecomTurn };
@@ -2,7 +2,7 @@ import {
2
2
  createAutoTaskPlugin,
3
3
  createLayaDecisionPlugin,
4
4
  registerLayaCompletionTaskHook
5
- } from "./roy-agent-core-rsn44n9y.js";
5
+ } from "./roy-agent-core-b31v3qpv.js";
6
6
  import {
7
7
  TaskHookPoints
8
8
  } from "./roy-agent-core-1pg0fepg.js";
@@ -9,7 +9,7 @@ import {
9
9
  import {
10
10
  BackgroundTaskManager,
11
11
  createDelegateTool
12
- } from "./roy-agent-core-aa2j7bk9.js";
12
+ } from "./roy-agent-core-cha0jc7n.js";
13
13
  import {
14
14
  TaskEntityEventTypes
15
15
  } from "./roy-agent-core-8gxth0eh.js";
@@ -8,6 +8,9 @@ import {
8
8
  createLogger,
9
9
  init_logger
10
10
  } from "./roy-agent-core-7f7t9wwp.js";
11
+ import {
12
+ __require
13
+ } from "./roy-agent-core-fs0mn2jk.js";
11
14
 
12
15
  // src/env/error-reply.ts
13
16
  init_logger();
@@ -361,9 +364,10 @@ function isUsableChannel(ch) {
361
364
  return false;
362
365
  if (!ch.chatId && !ch.messageId)
363
366
  return false;
364
- if (ch.type && !ch.type.toLowerCase().includes("lark"))
365
- return false;
366
- return true;
367
+ if (!ch.type)
368
+ return true;
369
+ const t = ch.type.toLowerCase();
370
+ return t.includes("lark") || t.startsWith("wecom");
367
371
  }
368
372
  var repliedTriggers = new Map;
369
373
  var CHAT_DEDUP_WINDOW_MS = 5 * 60000;
@@ -501,6 +505,32 @@ function spawnLarkReply(options) {
501
505
  proc.on("error", (err) => finish(err));
502
506
  });
503
507
  }
508
+ async function spawnWecomReply(options) {
509
+ const { chatId, content, params } = options;
510
+ if (!chatId) {
511
+ throw new Error("No chatId specified for wecom failure reply");
512
+ }
513
+ try {
514
+ const { sendWecomReply } = await import("./roy-agent-core-vyr6svt6.js");
515
+ const result = await sendWecomReply({
516
+ sourceId: typeof params?.sourceId === "string" && params.sourceId || "unknown",
517
+ rawChatId: chatId,
518
+ reqId: typeof params?.reqId === "string" ? params.reqId : undefined,
519
+ content
520
+ });
521
+ if (!result.ok) {
522
+ throw new Error(result.error ?? "sendWecomReply failed");
523
+ }
524
+ } catch (err) {
525
+ throw err instanceof Error ? err : new Error(String(err));
526
+ }
527
+ }
528
+ function pickSender(channel) {
529
+ const t = (channel.type ?? "").toLowerCase();
530
+ if (t.startsWith("wecom"))
531
+ return spawnWecomReply;
532
+ return spawnLarkReply;
533
+ }
504
534
  async function notifyReactFailure(options) {
505
535
  const info = classifyReactFailure(options.error);
506
536
  if (!info.notify) {
@@ -510,7 +540,12 @@ async function notifyReactFailure(options) {
510
540
  const channel = (isUsableChannel(options.replyChannel) ? options.replyChannel : undefined) ?? extractReplyChannelFromEvent(options.envEvent);
511
541
  if (!isUsableChannel(channel)) {
512
542
  const hasTrigger = Boolean(options.envEvent);
513
- const noChannelMsg = `[#4418] React 中断(${info.kind}/${info.ruleId})无法定位 replyChannel,` + `飞书端收不到失败说明` + (hasTrigger ? " —— triggerEvent 存在但缺少 metadata.replyChannel" : "(本地执行,无 triggerEvent)") + (options.sessionId ? ` (session=${options.sessionId})` : "");
543
+ const hasChannelObj = !hasTrigger || (() => {
544
+ const e = options.envEvent;
545
+ const rc = e?.metadata?.replyChannel ?? e?.payload?.replyChannel ?? options.replyChannel;
546
+ return Boolean(rc && typeof rc === "object");
547
+ })();
548
+ const noChannelMsg = hasChannelObj ? `[#4418] React 中断(${info.kind}/${info.ruleId})replyChannel.type 不在支持列表(lark*/wecom*),` + `触发方收不到失败说明` + (hasTrigger ? ` —— channel 存在但 type=${String(options.envEvent?.metadata?.replyChannel?.type ?? options.envEvent?.payload?.replyChannel?.type ?? options.replyChannel?.type ?? "unknown")} 未被放行` : "") + (options.sessionId ? ` (session=${options.sessionId})` : "") : `[#4418] React 中断(${info.kind}/${info.ruleId})无法定位 replyChannel,` + `触发方收不到失败说明` + (hasTrigger ? " —— triggerEvent 存在但缺少 metadata.replyChannel" : "(本地执行,无 triggerEvent)") + (options.sessionId ? ` (session=${options.sessionId})` : "");
514
549
  if (hasTrigger)
515
550
  logger.warn(noChannelMsg);
516
551
  else
@@ -523,21 +558,26 @@ async function notifyReactFailure(options) {
523
558
  return { sent: false, kind: info.kind, info, skipped: "duplicate" };
524
559
  }
525
560
  rememberTrigger(dedupKey);
526
- const content = buildFailureReplyMarkdown(info, { query: options.query });
561
+ const isWecom = (channel.type ?? "").toLowerCase().startsWith("wecom");
562
+ const baseContent = buildFailureReplyMarkdown(info, { query: options.query });
563
+ const content = isWecom ? `⚠️ 系统失败通知
564
+
565
+ ${baseContent}` : baseContent;
527
566
  try {
528
- await spawnLarkReply({
567
+ await pickSender(channel)({
529
568
  chatId: channel.chatId,
530
569
  messageId: channel.messageId,
531
570
  content,
532
571
  profile: channel.profile,
533
- timeoutMs: options.replyTimeoutMs
572
+ timeoutMs: options.replyTimeoutMs,
573
+ params: channel.params
534
574
  });
535
- logger.info(`[#4418] React 中断(${info.kind}/${info.ruleId})已回飞书 ` + `(${channel.messageId ?? channel.chatId})`);
575
+ logger.info(`[#4418] React 中断(${info.kind}/${info.ruleId})已回${isWecom ? "企微" : "飞书"} (${channel.messageId ?? channel.chatId})`);
536
576
  return { sent: true, kind: info.kind, info };
537
577
  } catch (err) {
538
578
  const errorMsg = err instanceof Error ? err.message : String(err);
539
579
  repliedTriggers.delete(dedupKey);
540
- logger.warn(`[#4418] React 中断(${info.kind}/${info.ruleId})回飞书失败(已忽略): ${errorMsg}`);
580
+ logger.warn(`[#4418] React 中断(${info.kind}/${info.ruleId})回${isWecom ? "企微" : "飞书"}失败(已忽略): ${errorMsg}`);
541
581
  return { sent: false, kind: info.kind, info, error: errorMsg };
542
582
  }
543
583
  }
@@ -9,6 +9,9 @@ import {
9
9
  getCurrentProcessRegistry,
10
10
  killProcessTree
11
11
  } from "./roy-agent-core-fttgm52a.js";
12
+ import {
13
+ wecomRespondTool
14
+ } from "./roy-agent-core-m3vjepzz.js";
12
15
  import {
13
16
  init_decorator,
14
17
  init_trace_filters,
@@ -940,6 +943,7 @@ function getAllBuiltInTools() {
940
943
  editFileTool,
941
944
  grepTool,
942
945
  understandImageTool,
946
+ wecomRespondTool,
943
947
  queryBackgroundProcessTool,
944
948
  compactSessionTool
945
949
  ];
@@ -7,7 +7,8 @@ var BUILT_IN_EVENT_SOURCE_TYPES = {
7
7
  LARK_CLI: "lark-cli",
8
8
  TIMER: "timer",
9
9
  BOUNTY_IM: "bounty-im",
10
- CLI_COMMAND: "cli-command"
10
+ CLI_COMMAND: "cli-command",
11
+ WECOM_CLI: "wecom-cli"
11
12
  };
12
13
  var BUILT_IN_EVENT_SOURCE_TYPE_LIST = Object.values(BUILT_IN_EVENT_SOURCE_TYPES);
13
14
  var defaultLarkCliRunner = {
@@ -60,6 +61,8 @@ function getDefaultConfigForType(type) {
60
61
  switch (type) {
61
62
  case BUILT_IN_EVENT_SOURCE_TYPES.LARK_CLI:
62
63
  return { command: "lark-cli event consume im.message.receive_v1 --as bot" };
64
+ case BUILT_IN_EVENT_SOURCE_TYPES.WECOM_CLI:
65
+ return { command: "wecom-cli event consume" };
63
66
  default:
64
67
  return {};
65
68
  }