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

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 (148) hide show
  1. package/dist/config/index.js +7 -7
  2. package/dist/env/agent/agent-component.d.ts +1 -1
  3. package/dist/env/agent/agent-component.js +13 -12
  4. package/dist/env/agent/index.js +13 -12
  5. package/dist/env/agent/types.d.ts +14 -0
  6. package/dist/env/commands/index.js +7 -7
  7. package/dist/env/context/index.js +3 -3
  8. package/dist/env/debug/index.js +7 -7
  9. package/dist/env/error-reply.js +1 -1
  10. package/dist/env/event-source/event-source-handlers.d.ts +18 -2
  11. package/dist/env/event-source/event-source-handlers.js +26 -21
  12. package/dist/env/event-source/event-store.js +2 -2
  13. package/dist/env/event-source/index.js +25 -22
  14. package/dist/env/event-source/timer-handler.d.ts +98 -0
  15. package/dist/env/event-source/types.d.ts +53 -1
  16. package/dist/env/event-source/wecom-card-action-handler.d.ts +235 -3
  17. package/dist/env/event-source/wecom-card-builder.d.ts +207 -1
  18. package/dist/env/event-source/wecom-cli-handler.d.ts +25 -0
  19. package/dist/{shared/@ai-setting/roy-agent-core-vyr6svt6.js → env/event-source/wecom-respond-registry.js} +8 -6
  20. package/dist/env/event-source/wecom-session-card-sender.d.ts +135 -0
  21. package/dist/env/event-source/wecom-session-card-sender.js +153 -0
  22. package/dist/env/hook/index.js +5 -5
  23. package/dist/env/index.js +50 -45
  24. package/dist/env/llm/index.js +9 -8
  25. package/dist/env/log-trace/index.js +7 -7
  26. package/dist/env/log-trace/trace-filters.d.ts +6 -1
  27. package/dist/env/mcp/index.js +7 -7
  28. package/dist/env/memory/index.js +7 -7
  29. package/dist/env/plugin/index.js +7 -7
  30. package/dist/env/prompt/index.js +8 -8
  31. package/dist/env/prompt/prompts-index.d.ts +1 -1
  32. package/dist/env/session/group-session-link.d.ts +99 -0
  33. package/dist/env/session/index.js +11 -10
  34. package/dist/env/session/session-component.d.ts +89 -0
  35. package/dist/env/session/storage/index.js +5 -4
  36. package/dist/env/session/types.d.ts +10 -0
  37. package/dist/env/skill/index.js +7 -7
  38. package/dist/env/task/backend/index.d.ts +3 -0
  39. package/dist/env/task/backend/index.js +151 -3
  40. package/dist/env/task/backend/remote-task-port.d.ts +85 -0
  41. package/dist/env/task/backend/types.d.ts +5 -0
  42. package/dist/env/task/delegate/delegate-channel-hint.d.ts +39 -0
  43. package/dist/env/task/delegate/delegate-tool.d.ts +71 -6
  44. package/dist/env/task/delegate/delegate-tool.js +11 -9
  45. package/dist/env/task/delegate/index.js +8 -8
  46. package/dist/env/task/index.d.ts +1 -1
  47. package/dist/env/task/index.js +34 -18
  48. package/dist/env/task/mcp/index.js +4 -4
  49. package/dist/env/task/plugins/card-subject.d.ts +117 -0
  50. package/dist/env/task/plugins/index.js +19 -17
  51. package/dist/env/task/plugins/lark-card-action-handler.d.ts +261 -6
  52. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +501 -0
  53. package/dist/env/task/plugins/lark-card-patch-notifier.js +12 -2
  54. package/dist/env/task/plugins/lark-card-state-store.d.ts +60 -0
  55. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +45 -0
  56. package/dist/env/task/plugins/sub-session-activity.d.ts +415 -0
  57. package/dist/env/task/plugins/wecom-session-card-store.d.ts +134 -0
  58. package/dist/env/task/plugins/wecom-session-card-store.js +23 -0
  59. package/dist/env/task/plugins/wecom-task-notify-plugin.d.ts +40 -0
  60. package/dist/env/task/storage/index.d.ts +1 -0
  61. package/dist/env/task/storage/index.js +24 -4
  62. package/dist/env/task/storage/migrations/006_add_task_owner.d.ts +24 -0
  63. package/dist/env/task/storage/remote-task-store.d.ts +186 -0
  64. package/dist/env/task/task-component.d.ts +7 -0
  65. package/dist/env/task/tools/index.js +4 -4
  66. package/dist/env/task/tools/task-types.d.ts +7 -0
  67. package/dist/env/task/types.d.ts +17 -0
  68. package/dist/env/tool/built-in/index.js +6 -4
  69. package/dist/env/tool/index.js +14 -10
  70. package/dist/env/tool/types.d.ts +25 -0
  71. package/dist/env/tool/validator.d.ts +8 -0
  72. package/dist/env/workflow/engine/index.js +12 -12
  73. package/dist/env/workflow/hooks/index.js +7 -7
  74. package/dist/env/workflow/index.js +24 -23
  75. package/dist/env/workflow/nodes/index.js +2 -2
  76. package/dist/env/workflow/plugins/index.js +10 -8
  77. package/dist/env/workflow/service/index.js +2 -2
  78. package/dist/env/workflow/storage/index.js +3 -3
  79. package/dist/env/workflow/tools/index.js +16 -15
  80. package/dist/env/workflow/utils/index.js +4 -4
  81. package/dist/index.d.ts +1 -1
  82. package/dist/index.js +71 -60
  83. package/dist/shared/@ai-setting/{roy-agent-core-q813vafd.js → roy-agent-core-05z35shp.js} +1 -1
  84. package/dist/shared/@ai-setting/{roy-agent-core-dc9zq75z.js → roy-agent-core-0crmsc12.js} +3 -3
  85. package/dist/shared/@ai-setting/{roy-agent-core-7dd0s3xb.js → roy-agent-core-0n1wwndq.js} +1027 -14
  86. package/dist/shared/@ai-setting/{roy-agent-core-cha0jc7n.js → roy-agent-core-0sbmdz8d.js} +137 -16
  87. package/dist/shared/@ai-setting/{roy-agent-core-87942bw4.js → roy-agent-core-1mhyh4c5.js} +1 -1
  88. package/dist/shared/@ai-setting/{roy-agent-core-v6vr6za7.js → roy-agent-core-1vv4fx15.js} +4 -4
  89. package/dist/shared/@ai-setting/{roy-agent-core-nwbfepba.js → roy-agent-core-3e040tc5.js} +1 -1
  90. package/dist/shared/@ai-setting/roy-agent-core-418zc9ha.js +288 -0
  91. package/dist/shared/@ai-setting/{roy-agent-core-qk9hd89p.js → roy-agent-core-49qcpqwg.js} +54 -19
  92. package/dist/shared/@ai-setting/{roy-agent-core-xf6z96qy.js → roy-agent-core-4b38m749.js} +818 -346
  93. package/dist/shared/@ai-setting/{roy-agent-core-r5wt1h3c.js → roy-agent-core-4fkxy2gv.js} +1 -1
  94. package/dist/shared/@ai-setting/{roy-agent-core-na18zwj0.js → roy-agent-core-4t3yskp8.js} +1 -1
  95. package/dist/shared/@ai-setting/{roy-agent-core-d1n4jbza.js → roy-agent-core-5dhvfgaw.js} +5 -5
  96. package/dist/shared/@ai-setting/{roy-agent-core-5pr4zp88.js → roy-agent-core-5kzhaap1.js} +1 -1
  97. package/dist/shared/@ai-setting/{roy-agent-core-qq96p0tj.js → roy-agent-core-5s2t8z00.js} +22 -1
  98. package/dist/shared/@ai-setting/{roy-agent-core-6rh8k6ba.js → roy-agent-core-6gj1hc5z.js} +7 -7
  99. package/dist/shared/@ai-setting/{roy-agent-core-g8htfwxj.js → roy-agent-core-7f10p7m2.js} +1 -1
  100. package/dist/shared/@ai-setting/{roy-agent-core-h21gngxf.js → roy-agent-core-7yh5wps0.js} +1 -1
  101. package/dist/shared/@ai-setting/{roy-agent-core-efkjyh69.js → roy-agent-core-9710zwwz.js} +1 -1
  102. package/dist/shared/@ai-setting/{roy-agent-core-rykf7c04.js → roy-agent-core-9c565frx.js} +1 -1
  103. package/dist/shared/@ai-setting/{roy-agent-core-vffccgf6.js → roy-agent-core-9cen1jvg.js} +18 -7
  104. package/dist/shared/@ai-setting/{roy-agent-core-fghc2m8z.js → roy-agent-core-aa8kv18f.js} +34 -2
  105. package/dist/shared/@ai-setting/roy-agent-core-age3qseb.js +177 -0
  106. package/dist/shared/@ai-setting/{roy-agent-core-7bpj859q.js → roy-agent-core-azyz78ky.js} +7 -7
  107. package/dist/shared/@ai-setting/{roy-agent-core-264qexac.js → roy-agent-core-b7h39vrc.js} +4 -4
  108. package/dist/shared/@ai-setting/{roy-agent-core-dqxec05w.js → roy-agent-core-bjtc9d35.js} +2 -2
  109. package/dist/shared/@ai-setting/roy-agent-core-c4ec7k0p.js +426 -0
  110. package/dist/shared/@ai-setting/{roy-agent-core-ddyk1rc0.js → roy-agent-core-cre2y2ct.js} +160 -4
  111. package/dist/shared/@ai-setting/{roy-agent-core-tr3ep81v.js → roy-agent-core-d6zdbgtc.js} +1 -1
  112. package/dist/shared/@ai-setting/{roy-agent-core-0qyrk5k0.js → roy-agent-core-ds6qzmf8.js} +14 -5
  113. package/dist/shared/@ai-setting/{roy-agent-core-p6ks90tb.js → roy-agent-core-dvec4nn2.js} +6 -6
  114. package/dist/shared/@ai-setting/{roy-agent-core-cgtq6427.js → roy-agent-core-e08bvpaq.js} +1 -1
  115. package/dist/shared/@ai-setting/{roy-agent-core-12n08pg5.js → roy-agent-core-e69qxdyy.js} +1 -1
  116. package/dist/shared/@ai-setting/{roy-agent-core-xcp8vwax.js → roy-agent-core-fepm835e.js} +2 -2
  117. package/dist/shared/@ai-setting/{roy-agent-core-6j02sbee.js → roy-agent-core-fhg03v43.js} +1 -1
  118. package/dist/shared/@ai-setting/{roy-agent-core-dvtg4sn0.js → roy-agent-core-g8yr733d.js} +1 -1
  119. package/dist/shared/@ai-setting/{roy-agent-core-m3vjepzz.js → roy-agent-core-h1e1fm9b.js} +38 -202
  120. package/dist/shared/@ai-setting/{roy-agent-core-c2gbt68e.js → roy-agent-core-h83zdsrq.js} +2 -2
  121. package/dist/shared/@ai-setting/{roy-agent-core-tk98f92b.js → roy-agent-core-k3gcvybs.js} +2 -2
  122. package/dist/shared/@ai-setting/{roy-agent-core-b31v3qpv.js → roy-agent-core-k3h8t4q1.js} +992 -212
  123. package/dist/shared/@ai-setting/{roy-agent-core-pgvcf91v.js → roy-agent-core-m39v7qpx.js} +2 -2
  124. package/dist/shared/@ai-setting/{roy-agent-core-ezvpreqz.js → roy-agent-core-m66ysky0.js} +54 -6
  125. package/dist/shared/@ai-setting/{roy-agent-core-begm7ytz.js → roy-agent-core-m7d1t3db.js} +3 -3
  126. package/dist/shared/@ai-setting/{roy-agent-core-tm1mxttk.js → roy-agent-core-mf5p38yf.js} +2 -2
  127. package/dist/shared/@ai-setting/{roy-agent-core-83bewqx8.js → roy-agent-core-mvh0td7p.js} +2 -2
  128. package/dist/shared/@ai-setting/{roy-agent-core-ze5cqpf2.js → roy-agent-core-mx855td4.js} +17 -38
  129. package/dist/shared/@ai-setting/{roy-agent-core-0dh8npnz.js → roy-agent-core-n197medr.js} +13 -13
  130. package/dist/shared/@ai-setting/{roy-agent-core-cmprygrp.js → roy-agent-core-nqkfkpf4.js} +4 -4
  131. package/dist/shared/@ai-setting/{roy-agent-core-7cbpmk4q.js → roy-agent-core-p31ftgq9.js} +1 -1
  132. package/dist/shared/@ai-setting/{roy-agent-core-1v4g0d4x.js → roy-agent-core-rm7seqwb.js} +1 -1
  133. package/dist/shared/@ai-setting/{roy-agent-core-ax9e9szb.js → roy-agent-core-smf94h73.js} +1 -1
  134. package/dist/shared/@ai-setting/{roy-agent-core-acj8ag08.js → roy-agent-core-tjnabyvq.js} +1 -1
  135. package/dist/shared/@ai-setting/{roy-agent-core-qqaak24r.js → roy-agent-core-tkpp1ba0.js} +1 -1
  136. package/dist/shared/@ai-setting/{roy-agent-core-dkrvmjsh.js → roy-agent-core-tpm1dd1g.js} +23 -15
  137. package/dist/shared/@ai-setting/{roy-agent-core-ed233k5z.js → roy-agent-core-v1n0106v.js} +1 -1
  138. package/dist/shared/@ai-setting/{roy-agent-core-zxz5fkwj.js → roy-agent-core-v6yshv0z.js} +1 -1
  139. package/dist/shared/@ai-setting/{roy-agent-core-mcy5apay.js → roy-agent-core-vf9sp6s3.js} +1 -1
  140. package/dist/shared/@ai-setting/{roy-agent-core-xvt7kf7p.js → roy-agent-core-w9ryeg2p.js} +3 -3
  141. package/dist/shared/@ai-setting/{roy-agent-core-es203w7p.js → roy-agent-core-wqs6yypq.js} +1 -1
  142. package/dist/shared/@ai-setting/roy-agent-core-wxrkcb17.js +56 -0
  143. package/dist/shared/@ai-setting/{roy-agent-core-0snm5hc5.js → roy-agent-core-y6shd91r.js} +1 -1
  144. package/dist/shared/@ai-setting/{roy-agent-core-9vxh6f8h.js → roy-agent-core-y7fqtzk2.js} +7 -7
  145. package/dist/shared/@ai-setting/{roy-agent-core-cpcd24n2.js → roy-agent-core-yet36q5k.js} +2 -2
  146. package/dist/shared/@ai-setting/{roy-agent-core-3fh1f7s7.js → roy-agent-core-yfpnkc4z.js} +5 -5
  147. package/dist/shared/@ai-setting/roy-agent-core-zhwcje5a.js +352 -0
  148. package/package.json +9 -1
@@ -4,57 +4,76 @@ import {
4
4
  import {
5
5
  cliCommandHandler
6
6
  } from "./roy-agent-core-v0hf14bj.js";
7
+ import {
8
+ getSharedWecomSessionCardStore
9
+ } from "./roy-agent-core-age3qseb.js";
7
10
  import {
8
11
  guardConfigWrite
9
12
  } from "./roy-agent-core-2stt4rp5.js";
10
13
  import {
11
14
  TaskTagPlugin,
15
+ createGroupChatViaLarkCli,
12
16
  createLarkCliTaskNotifyHook,
13
17
  createPerTaskGroupChatPlugin,
14
18
  createWecomCardStateStore,
15
19
  createWecomTaskNotifyHook,
16
- extractPerTaskChatId,
17
- extractPerTaskChatName
18
- } from "./roy-agent-core-b31v3qpv.js";
20
+ deleteGroupChatViaLarkCli,
21
+ handleCardAction
22
+ } from "./roy-agent-core-k3h8t4q1.js";
19
23
  import {
20
- WecomCardActionKey,
21
- buildWecomCardUpdateFrame,
22
24
  registerWecomRespondTarget,
25
+ sendWecomCliMarkdown,
23
26
  unregisterWecomRespondTarget
24
- } from "./roy-agent-core-m3vjepzz.js";
27
+ } from "./roy-agent-core-h1e1fm9b.js";
28
+ import {
29
+ ROY_SESSION_PREFIX,
30
+ WECOM_SESSION_TASK_SEP,
31
+ WecomCardActionKey,
32
+ buildWecomCardUpdateFrame,
33
+ buildWecomSessionUpdateFrame
34
+ } from "./roy-agent-core-c4ec7k0p.js";
25
35
  import {
26
36
  getSharedEventStore
27
- } from "./roy-agent-core-na18zwj0.js";
37
+ } from "./roy-agent-core-4t3yskp8.js";
28
38
  import {
29
39
  createLarkCliWorkflowNotifyHook
30
- } from "./roy-agent-core-d1n4jbza.js";
40
+ } from "./roy-agent-core-5dhvfgaw.js";
41
+ import {
42
+ DEFAULT_APP_LINK_BASE,
43
+ getSharedLarkCardNotifier,
44
+ getSharedLarkCardStateStore
45
+ } from "./roy-agent-core-0n1wwndq.js";
31
46
  import {
32
- LarkCardPatchNotifier,
33
- buildTaskCard
34
- } from "./roy-agent-core-7dd0s3xb.js";
47
+ RECENT_MESSAGE_LIMIT,
48
+ createActivityProbe,
49
+ createSessionActivityProbe,
50
+ extractToolCallsFromMessages,
51
+ mapBgTaskStatusToSessionLifecycle,
52
+ toActivityMessage
53
+ } from "./roy-agent-core-418zc9ha.js";
35
54
  import {
36
55
  notifyReactFailure
37
- } from "./roy-agent-core-rykf7c04.js";
56
+ } from "./roy-agent-core-9c565frx.js";
38
57
  import {
39
58
  normalizeLarkAtMentions
40
59
  } from "./roy-agent-core-m880qz3y.js";
41
60
  import {
42
61
  globalHookManager,
43
62
  init_global_hook_manager
44
- } from "./roy-agent-core-cpcd24n2.js";
63
+ } from "./roy-agent-core-yet36q5k.js";
45
64
  import {
46
65
  init_context
47
- } from "./roy-agent-core-qqaak24r.js";
66
+ } from "./roy-agent-core-tkpp1ba0.js";
48
67
  import {
49
68
  createEnvContext,
50
69
  normalizeParentTaskId,
51
70
  runWithEnvContext
52
- } from "./roy-agent-core-7cbpmk4q.js";
71
+ } from "./roy-agent-core-p31ftgq9.js";
53
72
  import {
54
73
  TracedAs,
55
74
  init_decorator,
56
75
  wrapFunction
57
- } from "./roy-agent-core-qq96p0tj.js";
76
+ } from "./roy-agent-core-5s2t8z00.js";
58
77
  import {
59
78
  createLogger,
60
79
  getTracerProvider,
@@ -67,7 +86,7 @@ import {
67
86
 
68
87
  // src/env/event-source/event-source-handlers.ts
69
88
  import { spawn as spawn3, exec as execSync } from "child_process";
70
- import { promisify as promisify2 } from "node:util";
89
+ import { promisify } from "node:util";
71
90
  init_global_hook_manager();
72
91
  init_context();
73
92
 
@@ -193,245 +212,12 @@ function unregisterPluginHooksShared(pluginName, hookPoints) {
193
212
  }
194
213
  }
195
214
 
196
- // src/env/task/plugins/lark-card-action-handler.ts
197
- init_logger();
198
- import { exec } from "node:child_process";
199
- import { promisify } from "node:util";
200
- var logger = createLogger("lark-card-action-handler");
201
- var execAsync = promisify(exec);
202
- var CTX_FIELD_CHAT_ID = "larkPerTaskChatId";
203
- var CTX_FIELD_CHAT_NAME = "larkPerTaskChatName";
204
- var CTX_FIELD_CHAT_CREATED_AT = "larkPerTaskChatCreatedAt";
205
- var CTX_FIELD_CHAT_TASK_ID = "larkPerTaskChatTaskId";
206
- var CTX_FIELD_CHAT_INVITEES = "larkPerTaskChatInvitees";
207
- var cardActionInflight = new Map;
208
- async function handleCardAction(event, ports) {
209
- const { action, taskId: taskIdRaw } = event.actionValue;
210
- const taskId = typeof taskIdRaw === "string" ? parseInt(taskIdRaw, 10) : taskIdRaw;
211
- if (!taskId || Number.isNaN(taskId)) {
212
- return { ok: false, action, error: `Invalid taskId: ${taskIdRaw}` };
213
- }
214
- if (action !== "create_group" && action !== "disband_group") {
215
- return { ok: false, action, error: `Unknown action: ${action}` };
216
- }
217
- const inflightKey = `${event.profile ?? "-"}:${event.messageId || String(taskId)}:${action}`;
218
- const inflight = cardActionInflight.get(inflightKey);
219
- if (inflight) {
220
- logger.warn(`handleCardAction: ${inflightKey} 已有同操作在处理中(疑似用户重复点击),本次忽略。`);
221
- return { ok: false, action, error: `重复点击:${inflightKey} 正在处理中,已忽略` };
222
- }
223
- const pending = handleCardActionInner(event, ports, taskId);
224
- cardActionInflight.set(inflightKey, pending);
225
- try {
226
- return await pending;
227
- } finally {
228
- cardActionInflight.delete(inflightKey);
229
- }
230
- }
231
- async function handleCardActionInner(event, ports, taskId) {
232
- const { action } = event.actionValue;
233
- if (!ports.getTask) {
234
- return { ok: false, action, error: "ports.getTask not injected" };
235
- }
236
- const task = await ports.getTask(taskId);
237
- if (!task) {
238
- return { ok: false, action, error: `Task #${taskId} not found` };
239
- }
240
- const existingChatId = extractPerTaskChatId(task.context);
241
- if (action === "create_group") {
242
- return await handleCreateGroup(event, ports, task, existingChatId);
243
- } else {
244
- return await handleDisbandGroup(event, ports, task, existingChatId);
245
- }
246
- }
247
- function buildCardAfterGroupChange(event, ports, task, perTaskChatId, perTaskChatName) {
248
- const meta = buildTaskMetaFromTask(task, perTaskChatId, perTaskChatName);
249
- const preserved = ports.notifier?.updateMetaAndBuildCard?.(event.messageId, meta, { clearPerTaskChat: !perTaskChatId });
250
- if (preserved) {
251
- logger.debug(`buildCardAfterGroupChange: 复用 notifier 累积状态(里程碑历史保留)messageId=${event.messageId}`);
252
- return preserved;
253
- }
254
- logger.warn(`buildCardAfterGroupChange: notifier 无 messageId=${event.messageId} 的状态,` + `fallback 重建卡片(该卡片的历史里程碑可能丢失)`);
255
- return buildTaskCard({
256
- status: "running",
257
- meta,
258
- milestones: [
259
- { title: "Task 创建", timestamp: new Date().toISOString(), description: task.current_status }
260
- ]
261
- });
262
- }
263
- async function handleCreateGroup(event, ports, task, existingChatId) {
264
- if (existingChatId) {
265
- const existingName = extractPerTaskChatName(task.context);
266
- logger.info(`handleCreateGroup: Task #${task.id} 已有专属群 ${existingChatId}(幂等命中,不重复建群)`);
267
- return {
268
- ok: true,
269
- action: "create_group",
270
- newChatId: existingChatId,
271
- cardUpdate: buildCardAfterGroupChange(event, ports, task, existingChatId, existingName)
272
- };
273
- }
274
- if (!ports.createGroupChat) {
275
- return { ok: false, action: "create_group", error: "ports.createGroupChat not injected" };
276
- }
277
- try {
278
- const renderedName = truncateText(`Task #${task.id} - ${task.title ?? "(无标题)"}`, 60);
279
- const description = truncateText(`roy-agent task #${task.id} - ${task.project_path ?? ""}`, 100);
280
- const operatorOpenId = extractOperatorOpenId(event.operatorId);
281
- const initialUserIds = operatorOpenId ? [operatorOpenId] : undefined;
282
- const chatId = await ports.createGroupChat({
283
- name: renderedName,
284
- description,
285
- profile: event.profile,
286
- initialUserIds
287
- });
288
- let invitedOpenIds = [];
289
- if (operatorOpenId) {
290
- if (initialUserIds) {
291
- invitedOpenIds = [operatorOpenId];
292
- logger.debug(`handleCreateGroup: ${operatorOpenId} 已在建群时邀请(initialUserIds 路径)`);
293
- } else if (ports.inviteUser) {
294
- try {
295
- await ports.inviteUser(chatId, operatorOpenId, event.profile);
296
- invitedOpenIds = [operatorOpenId];
297
- logger.debug(`handleCreateGroup: 走 inviteUser 端口邀请 ${operatorOpenId} 进 ${chatId}`);
298
- } catch (err) {
299
- logger.warn(`handleCreateGroup: inviteUser(${chatId}, ${operatorOpenId}) 失败(不阻断主流程): ${err.message}`);
300
- }
301
- } else {
302
- logger.warn(`handleCreateGroup: createGroupChat 不支持 initialUserIds 且 inviteUser 未注入,` + `${operatorOpenId} 未被拉进群 ${chatId}。` + `需升级 caller(bridge/real impl)使用 ports.inviteUser 或实现 createGroupChat.initialUserIds 参数。`);
303
- }
304
- }
305
- const newContext = patchContext(task.context ?? "{}", {
306
- [CTX_FIELD_CHAT_ID]: chatId,
307
- [CTX_FIELD_CHAT_NAME]: renderedName,
308
- [CTX_FIELD_CHAT_CREATED_AT]: new Date().toISOString(),
309
- [CTX_FIELD_CHAT_TASK_ID]: task.id,
310
- [CTX_FIELD_CHAT_INVITEES]: invitedOpenIds.length > 0 ? invitedOpenIds : undefined
311
- });
312
- if (ports.updateTaskContext) {
313
- await ports.updateTaskContext(task.id, newContext);
314
- }
315
- const cardUpdate = buildCardAfterGroupChange(event, ports, task, chatId, renderedName);
316
- return { ok: true, action: "create_group", newChatId: chatId, cardUpdate };
317
- } catch (err) {
318
- logger.error(`handleCardAction.create_group failed: ${err.message}`);
319
- return { ok: false, action: "create_group", error: err.message };
320
- }
321
- }
322
- function extractOperatorOpenId(raw) {
323
- if (!raw)
324
- return;
325
- if (typeof raw === "string")
326
- return raw;
327
- if (typeof raw === "object" && typeof raw.open_id === "string")
328
- return raw.open_id;
329
- return;
330
- }
331
- async function handleDisbandGroup(event, ports, task, existingChatId) {
332
- if (!existingChatId) {
333
- return {
334
- ok: false,
335
- action: "disband_group",
336
- error: `Task #${task.id} 无专属群(context.larkPerTaskChatId 空),无需解散`
337
- };
338
- }
339
- if (!ports.deleteGroupChat) {
340
- return { ok: false, action: "disband_group", error: "ports.deleteGroupChat not injected" };
341
- }
342
- try {
343
- await ports.deleteGroupChat(existingChatId, event.profile);
344
- const newContext = patchContext(task.context ?? "{}", {
345
- [CTX_FIELD_CHAT_ID]: undefined,
346
- [CTX_FIELD_CHAT_NAME]: undefined,
347
- [CTX_FIELD_CHAT_CREATED_AT]: undefined,
348
- [CTX_FIELD_CHAT_TASK_ID]: undefined
349
- });
350
- if (ports.updateTaskContext) {
351
- await ports.updateTaskContext(task.id, newContext);
352
- }
353
- const cardUpdate = buildCardAfterGroupChange(event, ports, task, undefined, undefined);
354
- return { ok: true, action: "disband_group", cardUpdate };
355
- } catch (err) {
356
- logger.error(`handleCardAction.disband_group failed: ${err.message}`);
357
- return { ok: false, action: "disband_group", error: err.message };
358
- }
359
- }
360
- function patchContext(contextStr, changes) {
361
- let obj = {};
362
- try {
363
- const parsed = JSON.parse(contextStr);
364
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
365
- obj = parsed;
366
- }
367
- } catch {
368
- obj = {};
369
- }
370
- for (const [k, v] of Object.entries(changes)) {
371
- if (v === undefined) {
372
- delete obj[k];
373
- } else {
374
- obj[k] = v;
375
- }
376
- }
377
- return JSON.stringify(obj);
378
- }
379
- function buildTaskMetaFromTask(task, perTaskChatId, perTaskChatName) {
380
- return {
381
- id: task.id,
382
- title: task.title,
383
- description: task.description,
384
- goals: task.goals,
385
- parentTaskId: typeof task.parent_task_id === "number" && task.parent_task_id > 0 ? task.parent_task_id : undefined,
386
- priority: task.priority,
387
- type: task.type,
388
- projectPath: task.project_path,
389
- context: task.context,
390
- currentStatus: task.current_status,
391
- perTaskChatId,
392
- perTaskChatName
393
- };
394
- }
395
- async function deleteGroupChatViaLarkCli(chatId, profile) {
396
- const profileFlag = profile ? `--profile ${profile} ` : "";
397
- try {
398
- const { stdout: authStdout } = await execAsync(`lark-cli ${profileFlag}auth status --json`, { timeout: 15000 });
399
- const appId = JSON.parse(authStdout).appId;
400
- if (!appId) {
401
- throw new Error(`lark-cli auth status 未返回 appId: ${authStdout.slice(0, 200)}`);
402
- }
403
- const cmd = `lark-cli ${profileFlag}api DELETE /open-apis/im/v1/chats/${chatId}/members ` + `--params '{"member_id_type":"app_id"}' ` + `--data ${shellQuote(JSON.stringify({ id_list: [appId] }))} ` + `--as bot`;
404
- const { stdout, stderr } = await execAsync(cmd, { timeout: 30000 });
405
- logger.debug(`deleteGroupChatViaLarkCli(${chatId}) bot 退群 ok: ` + `stdout=${stdout?.slice(0, 300)} stderr=${stderr?.slice(0, 300)}`);
406
- } catch (err) {
407
- logger.warn(`deleteGroupChatViaLarkCli(${chatId}) bot 退群失败(task.context 仍会清掉,` + `飞书侧群会保留):${err.message}`);
408
- }
409
- }
410
- function shellQuote(s) {
411
- return `'${s.replace(/'/g, `'\\''`)}'`;
412
- }
413
- function truncateText(text, maxLen) {
414
- if (!text)
415
- return "";
416
- if (text.length <= maxLen)
417
- return text;
418
- return text.substring(0, Math.max(0, maxLen - 3)) + "...";
419
- }
420
- async function createGroupChatViaLarkCli(opts) {
421
- const profileFlag = opts.profile ? `--profile ${opts.profile} ` : "";
422
- const usersFlag = opts.initialUserIds && opts.initialUserIds.length > 0 ? `--users ${shellQuote(opts.initialUserIds.join(","))} ` : "";
423
- const cmd = `lark-cli ${profileFlag}im +chat-create --name ${shellQuote(truncateText(opts.name, 60))} ` + usersFlag + `--description ${shellQuote(truncateText(opts.description, 100))} --as bot`;
424
- const { stdout } = await execAsync(cmd, { timeout: 60000 });
425
- const m = stdout.match(/"chat_id"\s*:\s*"(oc_[a-f0-9]+)"/);
426
- if (!m) {
427
- throw new Error(`lark-cli +chat-create 返回无法解析: ${stdout.slice(0, 300)}`);
428
- }
429
- return m[1];
430
- }
431
-
432
215
  // src/env/event-source/timer-handler.ts
433
216
  init_decorator();
217
+ init_logger();
434
218
  var DEFAULT_TIMER_INTERVAL_MS = 10 * 60 * 1000;
219
+ var SKIPPED_TICK_LOG_EVERY = 20;
220
+ var timerLogger = createLogger("timer:event-source");
435
221
  var SELF_REMINDER_MESSAGE = `[Timer Self-Reminder]
436
222
  请读取最近 1 周处理的 task 信息,分析用户当前的工作目标和重点任务。
437
223
  基于分析结果,自主构建有价值的任务来推进用户目标。
@@ -449,6 +235,19 @@ var TIMER_OPTIONS_KEY_SCHEDULE = "schedule";
449
235
  var TIMER_OPTIONS_KEY_SESSION_ID = "sessionId";
450
236
  var TIMER_OPTIONS_KEY_SKIP_WHEN_RUNNING = "skipWhenRunning";
451
237
  var TIME_PATTERN = /^([01]\d|2[0-3]):([0-5]\d)$/;
238
+ function buildFireSpanAttributes(ctx) {
239
+ const attrs = {
240
+ mode: ctx.mode,
241
+ sourceId: ctx.sourceId,
242
+ skippedSinceLastFire: ctx.skippedSinceLastFire
243
+ };
244
+ if (ctx.entry !== undefined) {
245
+ attrs.scheduleTime = ctx.entry.time;
246
+ if (ctx.entry.label !== undefined)
247
+ attrs.scheduleLabel = ctx.entry.label;
248
+ }
249
+ return attrs;
250
+ }
452
251
  function isValidScheduleTime(time) {
453
252
  return typeof time === "string" && TIME_PATTERN.test(time);
454
253
  }
@@ -539,6 +338,9 @@ class TimerInstance {
539
338
  scheduleSessionId;
540
339
  skipWhenRunning;
541
340
  firedKeys = new Set;
341
+ firedTicks = 0;
342
+ skippedTicks = 0;
343
+ skippedSinceLastFire = 0;
542
344
  now;
543
345
  status = "created";
544
346
  timerId;
@@ -594,6 +396,13 @@ class TimerInstance {
594
396
  getStatus() {
595
397
  return this.status;
596
398
  }
399
+ getTickStats() {
400
+ return {
401
+ firedTicks: this.firedTicks,
402
+ skippedTicks: this.skippedTicks,
403
+ skippedSinceLastFire: this.skippedSinceLastFire
404
+ };
405
+ }
597
406
  onEvent(handler) {
598
407
  this.eventHandler = handler;
599
408
  }
@@ -610,13 +419,31 @@ class TimerInstance {
610
419
  allTasks = this.getRunningTasks();
611
420
  } catch (error) {
612
421
  console.warn(`[TimerInstance] Failed to get running tasks, skipping tick: ${error}`);
422
+ this.recordSkip();
613
423
  return;
614
424
  }
615
425
  const hasRunning = Array.isArray(allTasks) && allTasks.some((t) => t.status === "running");
616
426
  if (hasRunning) {
427
+ this.recordSkip();
617
428
  return;
618
429
  }
619
- const event = this.buildSelfReminderEvent();
430
+ this.fireTick({
431
+ mode: "interval",
432
+ sourceId: this.config.id,
433
+ skippedSinceLastFire: this.skippedSinceLastFire
434
+ });
435
+ }
436
+ recordSkip() {
437
+ this.skippedTicks++;
438
+ this.skippedSinceLastFire++;
439
+ if (this.skippedTicks % SKIPPED_TICK_LOG_EVERY === 0) {
440
+ timerLogger.debug(`[TimerInstance] ${this.config.id}: ${this.skippedTicks} ticks skipped ` + `(${this.skippedSinceLastFire} since last fire, mode=${this.isScheduleMode ? "schedule" : "interval"})`);
441
+ }
442
+ }
443
+ fireTick(ctx) {
444
+ this.firedTicks++;
445
+ this.skippedSinceLastFire = 0;
446
+ const event = this.buildSelfReminderEvent(ctx.entry);
620
447
  this.eventHandler?.(event);
621
448
  }
622
449
  tickSchedule() {
@@ -625,6 +452,7 @@ class TimerInstance {
625
452
  this.invalidScheduleWarned = true;
626
453
  console.warn(`[TimerInstance] Timer ${this.config.id} has an invalid or empty schedule configuration; ` + `no events will be fired. Check options.schedule (expected [{ time: "HH:MM", prompt: "..." }]).`);
627
454
  }
455
+ this.recordSkip();
628
456
  return;
629
457
  }
630
458
  const now = new Date(this.now());
@@ -646,6 +474,7 @@ class TimerInstance {
646
474
  hasRunning = Array.isArray(allTasks) && allTasks.some((t) => t.status === "running");
647
475
  } catch (error) {
648
476
  console.warn(`[TimerInstance] Failed to get running tasks, skipping tick: ${error}`);
477
+ this.recordSkip();
649
478
  return;
650
479
  }
651
480
  if (hasRunning) {
@@ -654,10 +483,15 @@ class TimerInstance {
654
483
  }
655
484
  this.firedKeys.add(firedKey);
656
485
  this.pruneFiredKeys(dateKey);
657
- const event = this.buildSelfReminderEvent(entry);
658
- this.eventHandler?.(event);
486
+ this.fireTick({
487
+ mode: "schedule",
488
+ sourceId: this.config.id,
489
+ skippedSinceLastFire: this.skippedSinceLastFire,
490
+ entry
491
+ });
659
492
  return;
660
493
  }
494
+ this.recordSkip();
661
495
  }
662
496
  pruneFiredKeys(currentDateKey) {
663
497
  if (this.firedKeys.size <= 32)
@@ -714,14 +548,14 @@ class TimerInstance {
714
548
  }
715
549
  }
716
550
  __legacyDecorateClassTS([
717
- TracedAs("timer.tick", { recordParams: false, log: false })
718
- ], TimerInstance.prototype, "tick", null);
719
- __legacyDecorateClassTS([
720
- TracedAs("timer.tickSchedule", { recordParams: false, log: false })
721
- ], TimerInstance.prototype, "tickSchedule", null);
722
- __legacyDecorateClassTS([
723
- TracedAs("timer.buildSelfReminderEvent", { recordParams: false, log: false })
724
- ], TimerInstance.prototype, "buildSelfReminderEvent", null);
551
+ TracedAs("timer.tick.fire", {
552
+ recordParams: true,
553
+ recordResult: false,
554
+ log: false,
555
+ paramFilter: (args) => buildFireSpanAttributes(args[0]),
556
+ flattenParamFilter: true
557
+ })
558
+ ], TimerInstance.prototype, "fireTick", null);
725
559
  var timerHandler = {
726
560
  type: "timer",
727
561
  validateConfig(config) {
@@ -1711,9 +1545,17 @@ class WecomWsBridge {
1711
1545
 
1712
1546
  // src/env/event-source/wecom-card-action-handler.ts
1713
1547
  init_logger();
1714
- var logger2 = createLogger("wecom:card-action");
1715
- var cardActionInflight2 = new Map;
1548
+ var logger = createLogger("wecom:card-action");
1549
+ var cardActionInflight = new Map;
1716
1550
  var ROY_TASK_PREFIX = "roy-task-";
1551
+ function parseRoySessionId(cardTaskId) {
1552
+ if (!cardTaskId.startsWith(ROY_SESSION_PREFIX))
1553
+ return null;
1554
+ const rest = cardTaskId.slice(ROY_SESSION_PREFIX.length);
1555
+ const sepIdx = rest.indexOf(WECOM_SESSION_TASK_SEP);
1556
+ const id = (sepIdx >= 0 ? rest.slice(0, sepIdx) : rest).trim();
1557
+ return id || null;
1558
+ }
1717
1559
  function parseRoyTaskId(cardTaskId) {
1718
1560
  if (!cardTaskId.startsWith(ROY_TASK_PREFIX))
1719
1561
  return null;
@@ -1745,6 +1587,7 @@ function extractWecomCardAction(frame) {
1745
1587
  eventKey: String(tce.event_key ?? ""),
1746
1588
  cardTaskId,
1747
1589
  taskId: parseRoyTaskId(cardTaskId),
1590
+ sessionId: parseRoySessionId(cardTaskId),
1748
1591
  userId: from.userid ?? "",
1749
1592
  reqId: f.headers?.req_id ?? ""
1750
1593
  };
@@ -1761,6 +1604,10 @@ var ACTION_CARD_LABEL = {
1761
1604
  reopen: {
1762
1605
  statusLabel: "\uD83D\uDD04 进行中",
1763
1606
  note: "用户已重新打开任务"
1607
+ },
1608
+ refresh_activity: {
1609
+ statusLabel: "⚡ 活性已刷新",
1610
+ note: "已读取子会话最新活性"
1764
1611
  }
1765
1612
  };
1766
1613
  async function handleWecomCardAction(frame, ports) {
@@ -1769,17 +1616,26 @@ async function handleWecomCardAction(frame, ports) {
1769
1616
  return { ok: false, error: "not a template_card_event frame" };
1770
1617
  }
1771
1618
  const inflightKey = `${action.cardTaskId}:${action.eventKey}`;
1772
- const inflight = cardActionInflight2.get(inflightKey);
1619
+ const inflight = cardActionInflight.get(inflightKey);
1773
1620
  if (inflight) {
1774
- logger2.warn(`handleWecomCardAction: ${inflightKey} 已有同操作在处理中` + `(疑似用户重复点击),本次忽略。`);
1621
+ logger.warn(`handleWecomCardAction: ${inflightKey} 已有同操作在处理中` + `(疑似用户重复点击),本次忽略。`);
1775
1622
  return { ok: false, error: "同操作处理中,已忽略重复点击" };
1776
1623
  }
1777
1624
  const job = doHandle(action, ports).finally(() => {
1778
- cardActionInflight2.delete(inflightKey);
1625
+ cardActionInflight.delete(inflightKey);
1779
1626
  });
1780
- cardActionInflight2.set(inflightKey, job);
1627
+ cardActionInflight.set(inflightKey, job);
1781
1628
  return job;
1782
1629
  }
1630
+ async function probeSessionActivityByAssociatedTask(taskId, ports) {
1631
+ if (!ports.findSessionCardByTaskId || !ports.loadSessionActivity) {
1632
+ return;
1633
+ }
1634
+ const card = await ports.findSessionCardByTaskId(taskId);
1635
+ if (!card?.sessionId)
1636
+ return;
1637
+ return await ports.loadSessionActivity(card.sessionId);
1638
+ }
1783
1639
  async function doHandle(action, ports) {
1784
1640
  let actionType;
1785
1641
  if (action.eventKey === WecomCardActionKey.MARK_DONE) {
@@ -1788,9 +1644,25 @@ async function doHandle(action, ports) {
1788
1644
  actionType = "reopen";
1789
1645
  } else if (action.eventKey === WecomCardActionKey.VIEW) {
1790
1646
  actionType = "view";
1647
+ } else if (action.eventKey === WecomCardActionKey.REFRESH_ACTIVITY) {
1648
+ actionType = "refresh_activity";
1649
+ } else if (action.eventKey === WecomCardActionKey.CREATE_LARK_GROUP) {
1650
+ actionType = "create_lark_group";
1791
1651
  } else {
1792
1652
  return { ok: false, error: `unknown event_key: ${action.eventKey}` };
1793
1653
  }
1654
+ if (action.sessionId !== null) {
1655
+ if (actionType === "refresh_activity") {
1656
+ return await doHandleSessionActivity(action, ports);
1657
+ }
1658
+ if (actionType === "create_lark_group") {
1659
+ return await doHandleSessionCreateLarkGroup(action, ports);
1660
+ }
1661
+ return {
1662
+ ok: false,
1663
+ error: `action ${actionType} requires a task card (got session ${action.sessionId})`
1664
+ };
1665
+ }
1794
1666
  if (action.taskId === null && actionType !== "view") {
1795
1667
  return {
1796
1668
  ok: false,
@@ -1806,20 +1678,20 @@ async function doHandle(action, ports) {
1806
1678
  }
1807
1679
  } catch (err) {
1808
1680
  opError = err instanceof Error ? err.message : String(err);
1809
- logger2.error(`handleWecomCardAction: task op failed for #${action.taskId}: ${opError}`);
1681
+ logger.error(`handleWecomCardAction: task op failed for #${action.taskId}: ${opError}`);
1810
1682
  }
1811
1683
  const label = ACTION_CARD_LABEL[actionType];
1812
- const note = actionType === "view" ? label.note : opError ? `${label.note}(操作失败:${opError})` : label.note;
1684
+ const note = actionType === "view" || actionType === "refresh_activity" ? label.note : opError ? `${label.note}(操作失败:${opError})` : label.note;
1813
1685
  let timeline;
1814
1686
  if ((actionType === "view" || actionType === "reopen") && action.taskId !== null) {
1815
1687
  try {
1816
1688
  timeline = ports.loadTimeline?.(action.taskId) ?? [];
1817
1689
  if (timeline.length === 0) {
1818
- logger2.warn(`handleWecomCardAction: task #${action.taskId} 无时间线记录,` + `降级为简单按钮态更新`);
1690
+ logger.warn(`handleWecomCardAction: task #${action.taskId} 无时间线记录,` + `降级为简单按钮态更新`);
1819
1691
  }
1820
1692
  } catch (err) {
1821
1693
  const msg = err instanceof Error ? err.message : String(err);
1822
- logger2.warn(`handleWecomCardAction: loadTimeline failed for ` + `task #${action.taskId}: ${msg}(降级为简单按钮态更新)`);
1694
+ logger.warn(`handleWecomCardAction: loadTimeline failed for ` + `task #${action.taskId}: ${msg}(降级为简单按钮态更新)`);
1823
1695
  timeline = [];
1824
1696
  }
1825
1697
  }
@@ -1831,13 +1703,43 @@ async function doHandle(action, ports) {
1831
1703
  }
1832
1704
  } catch (err) {
1833
1705
  const msg = err instanceof Error ? err.message : String(err);
1834
- logger2.warn(`handleWecomCardAction: loadTaskTitle failed for ` + `task #${action.taskId}: ${msg}(降级为无标题更新)`);
1706
+ logger.warn(`handleWecomCardAction: loadTaskTitle failed for ` + `task #${action.taskId}: ${msg}(降级为无标题更新)`);
1707
+ }
1708
+ let activity;
1709
+ let activityTerminal;
1710
+ if (actionType === "refresh_activity" && action.taskId !== null) {
1711
+ try {
1712
+ const r = await ports.loadSubSessionActivity?.(action.taskId);
1713
+ activity = r?.activity ?? null;
1714
+ activityTerminal = r?.terminal === true;
1715
+ } catch (err) {
1716
+ const msg = err instanceof Error ? err.message : String(err);
1717
+ logger.warn(`handleWecomCardAction: loadSubSessionActivity failed for ` + `task #${action.taskId}: ${msg}(降级为「暂无数据」)`);
1718
+ activity = null;
1719
+ }
1720
+ if (activity === null) {
1721
+ try {
1722
+ const fallback = await probeSessionActivityByAssociatedTask(action.taskId, ports);
1723
+ if (fallback?.activity) {
1724
+ activity = fallback.activity;
1725
+ activityTerminal = fallback.terminal === true;
1726
+ logger.info(`handleWecomCardAction: task #${action.taskId} bgTask 反查失败,` + `已回退 session store probe(messageCount=${fallback.activity.messageCount})`);
1727
+ }
1728
+ } catch (err) {
1729
+ const msg = err instanceof Error ? err.message : String(err);
1730
+ logger.warn(`handleWecomCardAction: session probe fallback failed for ` + `task #${action.taskId}: ${msg}(保持「暂无数据」)`);
1731
+ }
1732
+ }
1835
1733
  }
1836
1734
  try {
1837
- await ports.updateCard(action.reqId, action.cardTaskId, label.statusLabel, note, timeline, actionType, taskExtra);
1735
+ await ports.updateCard(action.reqId, action.cardTaskId, label.statusLabel, note, timeline, actionType, activity === undefined ? taskExtra : {
1736
+ ...taskExtra ?? {},
1737
+ activity,
1738
+ ...activityTerminal !== undefined ? { activityTerminal } : {}
1739
+ });
1838
1740
  } catch (err) {
1839
1741
  const msg = err instanceof Error ? err.message : String(err);
1840
- logger2.error(`handleWecomCardAction: updateCard failed: ${msg}`);
1742
+ logger.error(`handleWecomCardAction: updateCard failed: ${msg}`);
1841
1743
  return {
1842
1744
  ok: false,
1843
1745
  action: actionType,
@@ -1847,11 +1749,164 @@ async function doHandle(action, ports) {
1847
1749
  if (opError) {
1848
1750
  return { ok: false, action: actionType, error: opError };
1849
1751
  }
1850
- logger2.info(`handleWecomCardAction: task #${action.taskId} → ${actionType} ` + `(by ${action.userId})`);
1752
+ logger.info(`handleWecomCardAction: task #${action.taskId} → ${actionType} ` + `(by ${action.userId})`);
1851
1753
  return { ok: true, action: actionType };
1852
1754
  }
1755
+ async function doHandleSessionActivity(action, ports) {
1756
+ const sessionId = action.sessionId;
1757
+ let state;
1758
+ try {
1759
+ state = await ports.loadSessionCardState?.(sessionId);
1760
+ } catch (err) {
1761
+ logger.warn(`handleWecomCardAction: loadSessionCardState failed for ${sessionId}: ` + `${err instanceof Error ? err.message : String(err)}(降级为仅活性)`);
1762
+ state = undefined;
1763
+ }
1764
+ if (!state) {
1765
+ logger.warn(`handleWecomCardAction: 无 session 卡持久层记录(${sessionId})` + ` ⇒ 刷新卡降级为仅活性(生命周期/操作记录不可用)`);
1766
+ }
1767
+ let activity = null;
1768
+ let probeTerminal;
1769
+ try {
1770
+ const r = await ports.loadSessionActivity?.(sessionId);
1771
+ activity = r?.activity ?? null;
1772
+ probeTerminal = r?.terminal;
1773
+ } catch (err) {
1774
+ logger.warn(`handleWecomCardAction: loadSessionActivity failed for ${sessionId}: ` + `${err instanceof Error ? err.message : String(err)}(降级为「暂无数据」)`);
1775
+ activity = null;
1776
+ }
1777
+ let sessionTask;
1778
+ try {
1779
+ const t = await ports.loadSessionTaskInfo?.(sessionId);
1780
+ if (t && typeof t.id === "number") {
1781
+ sessionTask = t;
1782
+ }
1783
+ } catch (err) {
1784
+ logger.warn(`handleWecomCardAction: loadSessionTaskInfo failed for ${sessionId}: ` + `${err instanceof Error ? err.message : String(err)}(降级为无任务行)`);
1785
+ }
1786
+ let linkedChatId;
1787
+ try {
1788
+ const g = await ports.findGroupChatForSession?.(sessionId);
1789
+ if (g && typeof g.chatId === "string" && g.chatId.trim()) {
1790
+ linkedChatId = g.chatId.trim();
1791
+ }
1792
+ } catch (err) {
1793
+ logger.warn(`handleWecomCardAction: findGroupChatForSession failed for ${sessionId}: ` + `${err instanceof Error ? err.message : String(err)}(当作没有群)`);
1794
+ }
1795
+ if (ports.updateSessionCard) {
1796
+ try {
1797
+ await ports.updateSessionCard(action.reqId, {
1798
+ sessionId,
1799
+ cardTaskId: action.cardTaskId,
1800
+ state,
1801
+ activity,
1802
+ ...sessionTask ? { task: sessionTask } : {},
1803
+ ...linkedChatId ? { perTaskChatId: linkedChatId } : {}
1804
+ });
1805
+ logger.info(`handleWecomCardAction: session ${sessionId} → refresh ` + `(lifecycle=${state?.lifecycle ?? "unknown"}, by ${action.userId})`);
1806
+ return { ok: true, action: "refresh_activity" };
1807
+ } catch (err) {
1808
+ const msg = err instanceof Error ? err.message : String(err);
1809
+ logger.error(`handleWecomCardAction: session card update failed for ${sessionId}: ${msg}`);
1810
+ return {
1811
+ ok: false,
1812
+ action: "refresh_activity",
1813
+ error: `card update failed: ${msg}`
1814
+ };
1815
+ }
1816
+ }
1817
+ try {
1818
+ await ports.updateCard(action.reqId, action.cardTaskId, ACTION_CARD_LABEL.refresh_activity.statusLabel, ACTION_CARD_LABEL.refresh_activity.note, undefined, "refresh_activity", {
1819
+ activity,
1820
+ activityTerminal: state?.lifecycle !== undefined ? state.lifecycle !== "running" : probeTerminal ?? false
1821
+ });
1822
+ } catch (err) {
1823
+ const msg = err instanceof Error ? err.message : String(err);
1824
+ logger.error(`handleWecomCardAction: session card update failed for ${sessionId}: ${msg}`);
1825
+ return { ok: false, action: "refresh_activity", error: `card update failed: ${msg}` };
1826
+ }
1827
+ logger.info(`handleWecomCardAction: session ${sessionId} → refresh ` + `(lifecycle=${state?.lifecycle ?? "unknown"}, by ${action.userId})`);
1828
+ return { ok: true, action: "refresh_activity" };
1829
+ }
1830
+ async function doHandleSessionCreateLarkGroup(action, ports) {
1831
+ const sessionId = action.sessionId;
1832
+ let existingChatId;
1833
+ try {
1834
+ const g = await ports.findGroupChatForSession?.(sessionId);
1835
+ if (g && typeof g.chatId === "string" && g.chatId.trim()) {
1836
+ existingChatId = g.chatId.trim();
1837
+ }
1838
+ } catch (err) {
1839
+ logger.warn(`handleWecomCardAction: findGroupChatForSession(${sessionId}) 失败` + `(当作没有群): ${err instanceof Error ? err.message : String(err)}`);
1840
+ }
1841
+ if (existingChatId) {
1842
+ logger.info(`handleWecomCardAction: session ${sessionId} 已有飞书群 ` + `${existingChatId}(幂等命中,不重复建群)`);
1843
+ await updateSessionCardWithGroup(ports, action, sessionId, existingChatId);
1844
+ return { ok: true, action: "create_lark_group", newChatId: existingChatId };
1845
+ }
1846
+ if (!ports.createLarkGroupForSession) {
1847
+ return {
1848
+ ok: false,
1849
+ action: "create_lark_group",
1850
+ error: "ports.createLarkGroupForSession not injected " + "(wecom-cli-handler wiring missing)"
1851
+ };
1852
+ }
1853
+ let chatId;
1854
+ try {
1855
+ chatId = await ports.createLarkGroupForSession(sessionId);
1856
+ } catch (err) {
1857
+ const msg = err instanceof Error ? err.message : String(err);
1858
+ logger.error(`handleWecomCardAction: createLarkGroupForSession(${sessionId}) ` + `failed: ${msg}`);
1859
+ await updateSessionCardWithGroup(ports, action, sessionId, undefined, `⚠️ 建群失败:${msg.slice(0, 80)}`);
1860
+ return { ok: false, action: "create_lark_group", error: msg };
1861
+ }
1862
+ logger.info(`handleWecomCardAction: session ${sessionId} → 飞书群 ${chatId} ` + `创建成功(by ${action.userId})`);
1863
+ await updateSessionCardWithGroup(ports, action, sessionId, chatId);
1864
+ if (ports.notifyGroupLink) {
1865
+ try {
1866
+ await ports.notifyGroupLink({
1867
+ sessionId,
1868
+ chatId,
1869
+ ...action.userId ? { userId: action.userId } : {}
1870
+ });
1871
+ } catch (err) {
1872
+ logger.warn(`handleWecomCardAction: notifyGroupLink(${sessionId} → ${chatId}) ` + `失败(不阻断建群结果): ` + `${err instanceof Error ? err.message : String(err)}`);
1873
+ }
1874
+ }
1875
+ return { ok: true, action: "create_lark_group", newChatId: chatId };
1876
+ }
1877
+ async function updateSessionCardWithGroup(ports, action, sessionId, chatId, errorHint) {
1878
+ if (!ports.updateSessionCard)
1879
+ return;
1880
+ try {
1881
+ let state;
1882
+ try {
1883
+ state = await ports.loadSessionCardState?.(sessionId);
1884
+ } catch {
1885
+ state = undefined;
1886
+ }
1887
+ let activity = null;
1888
+ try {
1889
+ const r = await ports.loadSessionActivity?.(sessionId);
1890
+ activity = r?.activity ?? null;
1891
+ } catch {
1892
+ activity = null;
1893
+ }
1894
+ await ports.updateSessionCard(action.reqId, {
1895
+ sessionId,
1896
+ cardTaskId: action.cardTaskId,
1897
+ ...state ? { state } : {},
1898
+ activity,
1899
+ ...chatId ? { perTaskChatId: chatId } : {},
1900
+ ...errorHint ? { errorHint } : {}
1901
+ });
1902
+ logger.info(`handleWecomCardAction: session card updated after group change ` + `(session=${sessionId}, chatId=${chatId ?? "none"})`);
1903
+ } catch (err) {
1904
+ logger.warn(`handleWecomCardAction: session card update after group change ` + `failed for ${sessionId}(不阻断建群结果): ` + `${err instanceof Error ? err.message : String(err)}`);
1905
+ }
1906
+ }
1853
1907
  function buildWecomCardUpdateBody(input) {
1854
1908
  const refreshable = input.action === "view" || input.action === "reopen";
1909
+ const activityRefreshable = input.action === "refresh_activity";
1855
1910
  const frame = buildWecomCardUpdateFrame({
1856
1911
  reqId: "placeholder",
1857
1912
  taskId: input.cardTaskId,
@@ -1860,7 +1915,44 @@ function buildWecomCardUpdateBody(input) {
1860
1915
  timeline: input.timeline,
1861
1916
  taskTitle: input.taskTitle,
1862
1917
  taskNumber: input.taskNumber,
1863
- buttonOverrides: refreshable ? [{ text: "刷新详情", key: WecomCardActionKey.VIEW, style: 1 }] : [{ text: "已处理", key: "task_done_ack", style: 4 }]
1918
+ buttonOverrides: refreshable ? [{ text: "刷新详情", key: WecomCardActionKey.VIEW, style: 1 }] : activityRefreshable ? [
1919
+ { text: "刷新详情", key: WecomCardActionKey.VIEW, style: 2 },
1920
+ { text: "刷新活性", key: WecomCardActionKey.REFRESH_ACTIVITY, style: 1 }
1921
+ ] : [{ text: "已处理", key: "task_done_ack", style: 4 }],
1922
+ ...input.activity !== undefined ? { activity: input.activity } : {},
1923
+ ...input.activityTerminal !== undefined ? { activityTerminal: input.activityTerminal } : {}
1924
+ });
1925
+ return { cmd: frame.cmd, body: frame.body };
1926
+ }
1927
+ function buildWecomSessionCardUpdateBody(input) {
1928
+ const state = input.state;
1929
+ const lifecycle = state?.lifecycle ?? "unknown";
1930
+ let milestones;
1931
+ if (state?.milestones) {
1932
+ try {
1933
+ const arr = JSON.parse(state.milestones);
1934
+ if (Array.isArray(arr) && arr.length > 0) {
1935
+ milestones = arr;
1936
+ }
1937
+ } catch {
1938
+ milestones = undefined;
1939
+ }
1940
+ }
1941
+ const task = input.task ?? (state?.associatedTaskId !== undefined ? { id: state.associatedTaskId } : undefined);
1942
+ const frame = buildWecomSessionUpdateFrame({
1943
+ reqId: input.reqIdForTest ?? "placeholder",
1944
+ cardTaskId: input.cardTaskId,
1945
+ sessionId: input.sessionId,
1946
+ lifecycle,
1947
+ ...state?.title ? { title: state.title } : {},
1948
+ ...state?.agentType ? { agentType: state.agentType } : {},
1949
+ ...state?.startedAt ? { startedAt: state.startedAt } : {},
1950
+ ...task ? { task } : {},
1951
+ ...milestones ? { milestones } : {},
1952
+ ...input.perTaskChatId ? { perTaskChatId: input.perTaskChatId } : {},
1953
+ ...input.errorHint ? { errorHint: input.errorHint } : {},
1954
+ ...input.activity !== undefined ? { activity: input.activity } : {},
1955
+ ...input.now !== undefined ? { now: input.now } : {}
1864
1956
  });
1865
1957
  return { cmd: frame.cmd, body: frame.body };
1866
1958
  }
@@ -2046,7 +2138,8 @@ function buildWecomReplyChannel(input) {
2046
2138
  wecomChatId: rawChatId,
2047
2139
  wecomUserId: userId,
2048
2140
  ...input.sourceId ? { sourceId: input.sourceId } : {},
2049
- ...input.reqId ? { reqId: input.reqId } : {}
2141
+ ...input.reqId ? { reqId: input.reqId } : {},
2142
+ statusNotifications: input.statusNotifications === true
2050
2143
  }
2051
2144
  };
2052
2145
  }
@@ -2161,6 +2254,22 @@ function parseWecomFrame(raw) {
2161
2254
  reqId: frame.headers?.req_id
2162
2255
  };
2163
2256
  }
2257
+ var WECOM_DEFAULT_EXTEND_TOOLS = ["wecom_respond"];
2258
+ function resolveWecomExtendTools(config) {
2259
+ const out = [];
2260
+ const push = (name) => {
2261
+ if (typeof name !== "string")
2262
+ return;
2263
+ const trimmed = name.trim();
2264
+ if (trimmed.length === 0)
2265
+ return;
2266
+ if (!out.includes(trimmed))
2267
+ out.push(trimmed);
2268
+ };
2269
+ WECOM_DEFAULT_EXTEND_TOOLS.forEach(push);
2270
+ (config?.extendTools ?? []).forEach(push);
2271
+ return out;
2272
+ }
2164
2273
  var WECOM_CHANNEL_PROMPT_SECTION_TITLE = "⭐ 企业微信回复规范(system prompt 注入版)";
2165
2274
  function buildWecomChannelSystemPrompt(opts) {
2166
2275
  const botLine = opts.botId ? `本渠道智能机器人 BotID:${opts.botId}。` : `本渠道未配置 BotID(使用事件回调里的 aibotid)。`;
@@ -2292,15 +2401,82 @@ class WecomCliInstance {
2292
2401
  pluginInstances = new Map;
2293
2402
  loadedPlugins = [];
2294
2403
  cardActionPorts;
2404
+ sessionCardStore;
2295
2405
  constructor(config) {
2296
2406
  this.config = config;
2297
2407
  }
2298
2408
  setCardActionPorts(ports) {
2299
2409
  this.cardActionPorts = ports;
2410
+ this.applySessionCardPorts();
2300
2411
  }
2301
2412
  hasCardActionPorts() {
2302
2413
  return this.cardActionPorts !== undefined;
2303
2414
  }
2415
+ setSessionCardStore(store) {
2416
+ this.sessionCardStore = store;
2417
+ this.applySessionCardPorts();
2418
+ }
2419
+ hasSessionCardStore() {
2420
+ return this.sessionCardStore !== undefined;
2421
+ }
2422
+ applySessionCardPorts() {
2423
+ if (!this.cardActionPorts || !this.sessionCardStore)
2424
+ return;
2425
+ const store = this.sessionCardStore;
2426
+ this.cardActionPorts.loadSessionCardState ??= (sessionId) => store.get(sessionId);
2427
+ this.cardActionPorts.findSessionCardByTaskId ??= (taskId) => {
2428
+ const hits = store.list().filter((s) => s.associatedTaskId === taskId);
2429
+ if (hits.length === 0)
2430
+ return;
2431
+ const open = hits.filter((s) => s.closedAt === undefined);
2432
+ const pool = open.length > 0 ? open : hits;
2433
+ return pool.reduce((a, b) => b.updatedAt > a.updatedAt ? b : a);
2434
+ };
2435
+ this.cardActionPorts.updateSessionCard ??= async (reqId, input) => {
2436
+ const { cmd, body } = buildWecomSessionCardUpdateBody({
2437
+ cardTaskId: input.cardTaskId,
2438
+ sessionId: input.sessionId,
2439
+ ...input.state ? { state: input.state } : {},
2440
+ activity: input.activity,
2441
+ ...input.task ? { task: input.task } : {},
2442
+ ...input.perTaskChatId ? { perTaskChatId: input.perTaskChatId } : {},
2443
+ ...input.errorHint ? { errorHint: input.errorHint } : {},
2444
+ ...input.now !== undefined ? { now: input.now } : {}
2445
+ });
2446
+ const err = this.wsBridge?.sendFrameWithReqId({
2447
+ cmd,
2448
+ headers: { req_id: reqId },
2449
+ body
2450
+ });
2451
+ if (err)
2452
+ throw err;
2453
+ };
2454
+ this.cardActionPorts.loadSessionTaskInfo ??= async (sessionId) => {
2455
+ const taskComponent = this.environment?.getComponent?.("task");
2456
+ const getter = taskComponent?.getTaskIdBySessionId;
2457
+ if (typeof getter !== "function")
2458
+ return;
2459
+ const taskId = await getter.call(taskComponent, sessionId);
2460
+ if (typeof taskId !== "number")
2461
+ return;
2462
+ const withOps = await taskComponent?.getTaskWithOperations?.(taskId);
2463
+ if (!withOps)
2464
+ return { id: taskId };
2465
+ const ops = Array.isArray(withOps.operations) ? withOps.operations : [];
2466
+ return {
2467
+ id: taskId,
2468
+ ...typeof withOps.title === "string" ? { title: withOps.title } : {},
2469
+ ...typeof withOps.status === "string" ? { status: withOps.status } : {},
2470
+ ...typeof withOps.progress === "number" ? { progress: withOps.progress } : {},
2471
+ ...ops.length > 0 ? {
2472
+ milestones: ops.slice(-10).map((o) => ({
2473
+ title: String(o.milestoneTitle ?? o.milestoneType ?? ""),
2474
+ timestamp: String(o.timestamp ?? "")
2475
+ }))
2476
+ } : {}
2477
+ };
2478
+ };
2479
+ }
2304
2480
  environment;
2305
2481
  setEnvironment(env) {
2306
2482
  this.environment = env;
@@ -2322,6 +2498,7 @@ class WecomCliInstance {
2322
2498
  const metadata = {
2323
2499
  ...parsed.metadata,
2324
2500
  systemPrompt: channelPrompt,
2501
+ extendTools: resolveWecomExtendTools(this.config),
2325
2502
  reqId: parsed.reqId,
2326
2503
  plugins: this.config.handleRule?.plugins || [],
2327
2504
  pluginEnabled: Object.fromEntries((this.config.handleRule?.plugins || []).map((p) => [
@@ -2335,7 +2512,8 @@ class WecomCliInstance {
2335
2512
  chatType: metadata.chatType,
2336
2513
  userId: metadata.senderId,
2337
2514
  sourceId: this.config.id,
2338
- reqId: parsed.reqId
2515
+ reqId: parsed.reqId,
2516
+ statusNotifications: this.config.statusNotifications === true
2339
2517
  });
2340
2518
  const event = {
2341
2519
  id: `es-${this.config.id}-${Date.now()}`,
@@ -2408,18 +2586,29 @@ class WecomCliInstance {
2408
2586
  if (this.cardActionPorts)
2409
2587
  return;
2410
2588
  const taskComponent = this.environment?.getComponent?.("task");
2411
- if (!taskComponent?.updateTask || !this.wsBridge) {
2412
- console.warn(`[wecom-cli] cardActionPorts 未注入:taskComponent.updateTask=` + `${typeof taskComponent?.updateTask}, wsBridge=${!!this.wsBridge}。` + `卡片按钮点击会被跳过。`);
2589
+ if (!this.wsBridge) {
2590
+ console.warn(`[wecom-cli] cardActionPorts 未注入:wsBridge 缺失(没有通道发更新帧)。` + `卡片按钮点击会被跳过。`);
2413
2591
  return;
2414
2592
  }
2593
+ const taskOpsAvailable = typeof taskComponent?.updateTask === "function";
2594
+ if (!taskOpsAvailable) {
2595
+ console.warn(`[wecom-cli] taskComponent.updateTask 不可用 ⇒ 「标记完成 / 重新打开」` + `降级为报错;session 卡「刷新」不受影响(Task #5364)。`);
2596
+ }
2597
+ const requireTaskOps = () => {
2598
+ if (!taskOpsAvailable) {
2599
+ throw new Error("task ops unavailable (taskComponent.updateTask missing)");
2600
+ }
2601
+ };
2415
2602
  this.cardActionPorts = {
2416
2603
  markTaskDone: async (taskId) => {
2604
+ requireTaskOps();
2417
2605
  await taskComponent.updateTask(taskId, {
2418
2606
  status: "completed",
2419
2607
  current_status: "用户在企微卡片点「标记完成」确认完成"
2420
2608
  });
2421
2609
  },
2422
2610
  reopenTask: async (taskId) => {
2611
+ requireTaskOps();
2423
2612
  await taskComponent.updateTask(taskId, {
2424
2613
  status: "active",
2425
2614
  current_status: "用户在企微卡片点「重新打开」"
@@ -2433,7 +2622,9 @@ class WecomCliInstance {
2433
2622
  timeline,
2434
2623
  action,
2435
2624
  taskTitle: extra?.taskTitle,
2436
- taskNumber: extra?.taskNumber
2625
+ taskNumber: extra?.taskNumber,
2626
+ ...extra?.activity !== undefined ? { activity: extra.activity } : {},
2627
+ ...extra?.activityTerminal !== undefined ? { activityTerminal: extra.activityTerminal } : {}
2437
2628
  });
2438
2629
  const frame = {
2439
2630
  cmd,
@@ -2489,7 +2680,114 @@ class WecomCliInstance {
2489
2680
  }
2490
2681
  }
2491
2682
  };
2492
- console.info(`[wecom-cli] cardActionPorts 已注入(task ops + bridge.respond 更新 + timeline + taskTitle)`);
2683
+ this.cardActionPorts.loadSubSessionActivity = createActivityProbe({
2684
+ listBackgroundTasks: () => {
2685
+ const tc = this.environment?.getComponent?.("task");
2686
+ return tc?.getBackgroundTaskManager?.()?.listTasks?.();
2687
+ },
2688
+ getSubSessionActivity: (bgId) => {
2689
+ const tc = this.environment?.getComponent?.("task");
2690
+ return tc?.getBackgroundTaskManager?.()?.getSubSessionActivity?.(bgId);
2691
+ },
2692
+ getLastMessage: async (subSessionId) => {
2693
+ const sc = this.environment?.getComponent?.("session");
2694
+ if (!sc?.getMessages)
2695
+ return;
2696
+ const tail = await sc.getMessages(subSessionId, { offset: -1, limit: 1 });
2697
+ return toActivityMessage(tail?.[tail.length - 1]);
2698
+ },
2699
+ isTerminalTask: async (taskId) => {
2700
+ const tc = this.environment?.getComponent?.("task");
2701
+ const raw = tc?.getTask?.call(tc, taskId);
2702
+ const t = raw && typeof raw.then === "function" ? await raw : raw;
2703
+ return t?.status === "completed" || t?.status === "cancelled" || t?.status === "failed" || t?.status === "archived";
2704
+ }
2705
+ });
2706
+ this.cardActionPorts.loadSessionActivity = createSessionActivityProbe({
2707
+ getSession: async (sessionId) => {
2708
+ const sc = this.environment?.getComponent?.("session");
2709
+ if (!sc?.get)
2710
+ return;
2711
+ const s = await sc.get(sessionId);
2712
+ if (!s)
2713
+ return;
2714
+ return {
2715
+ ...typeof s.messageCount === "number" ? { messageCount: s.messageCount } : {},
2716
+ ...typeof s.updatedAt === "number" ? { updatedAt: s.updatedAt } : {}
2717
+ };
2718
+ },
2719
+ getLastMessage: async (sessionId) => {
2720
+ const sc = this.environment?.getComponent?.("session");
2721
+ if (!sc?.getMessages)
2722
+ return;
2723
+ const tail = await sc.getMessages(sessionId, { offset: -1, limit: 1 });
2724
+ return toActivityMessage(tail?.[tail.length - 1]);
2725
+ },
2726
+ getLifecycle: (sessionId) => {
2727
+ const tc = this.environment?.getComponent?.("task");
2728
+ const tasks = tc?.getBackgroundTaskManager?.()?.listTasks?.() ?? [];
2729
+ const hit = tasks.find((t) => t?.subSessionId === sessionId);
2730
+ return mapBgTaskStatusToSessionLifecycle(hit?.status);
2731
+ }
2732
+ });
2733
+ this.sessionCardStore ??= getSharedWecomSessionCardStore();
2734
+ this.applySessionCardPorts();
2735
+ if (this.cardActionPorts) {
2736
+ const sessionComponent = () => this.environment?.getComponent?.("session");
2737
+ this.cardActionPorts.findGroupChatForSession ??= async (sessionId) => {
2738
+ const sc = sessionComponent();
2739
+ if (typeof sc?.findGroupChatForSession !== "function") {
2740
+ return;
2741
+ }
2742
+ return sc.findGroupChatForSession(sessionId);
2743
+ };
2744
+ this.cardActionPorts.createLarkGroupForSession ??= async (sessionId) => {
2745
+ const sc = sessionComponent();
2746
+ let title;
2747
+ try {
2748
+ const s = await sc?.get?.(sessionId);
2749
+ if (typeof s?.title === "string" && s.title.trim()) {
2750
+ title = s.title.trim();
2751
+ }
2752
+ } catch {}
2753
+ const name = title ?? sessionId.slice(-20);
2754
+ const chatId = await createGroupChatViaLarkCli({
2755
+ name: `会话 ${name}`.slice(0, 60),
2756
+ description: `roy-agent session ${sessionId}`.slice(0, 100)
2757
+ });
2758
+ try {
2759
+ await sc?.attachGroupSession?.({
2760
+ chatId,
2761
+ originSessionId: sessionId,
2762
+ title: name
2763
+ });
2764
+ } catch (err) {
2765
+ console.warn(`[wecom-cli] attachGroupSession(${chatId} ← ${sessionId}) ` + `失败(不阻断建群): ${err}`);
2766
+ }
2767
+ return chatId;
2768
+ };
2769
+ this.cardActionPorts.notifyGroupLink ??= async (input) => {
2770
+ const link = `${DEFAULT_APP_LINK_BASE}/client/chat/open?openChatId=${input.chatId}`;
2771
+ const text = `\uD83D\uDCE8 会话 ${input.sessionId} 的关联飞书群已建立:` + `[点击进入群](${link})`;
2772
+ let target = input.userId || "";
2773
+ if (!target) {
2774
+ try {
2775
+ target = this.sessionCardStore?.get(input.sessionId)?.chatId ?? "";
2776
+ } catch {
2777
+ target = "";
2778
+ }
2779
+ }
2780
+ if (!target) {
2781
+ console.warn(`[wecom-cli] notifyGroupLink: 无法定位企微会话 ` + `(userId 缺失且 session store 无 chatId),跳过通知`);
2782
+ return;
2783
+ }
2784
+ const r = await sendWecomCliMarkdown({ chatId: target, content: text });
2785
+ if (!r.ok) {
2786
+ console.warn(`[wecom-cli] notifyGroupLink cli failed: ${r.ok ? "unknown" : r.error}`);
2787
+ }
2788
+ };
2789
+ }
2790
+ console.info(`[wecom-cli] cardActionPorts 已注入(task ops + bridge.respond 更新 + timeline + taskTitle + activity + session task info + lark group)`);
2493
2791
  }
2494
2792
  getWecomCardStateStore() {
2495
2793
  const plugin = this.pluginInstances.get("WecomTaskNotifyPlugin");
@@ -2567,6 +2865,7 @@ class WecomCliInstance {
2567
2865
  const plugin = importPluginShared(pluginName, getWecomPluginFactories(), {
2568
2866
  sourceId: this.config.id,
2569
2867
  pluginName,
2868
+ statusNotifications: this.config.statusNotifications === true,
2570
2869
  ...pluginConfig
2571
2870
  });
2572
2871
  if (!plugin)
@@ -2625,7 +2924,8 @@ class WecomCliInstance {
2625
2924
  chatType: parsed.metadata.chatType,
2626
2925
  userId: parsed.metadata.senderId,
2627
2926
  sourceId: this.config.id,
2628
- reqId: parsed.reqId
2927
+ reqId: parsed.reqId,
2928
+ statusNotifications: this.config.statusNotifications === true
2629
2929
  });
2630
2930
  notifyReactFailure({
2631
2931
  error: err,
@@ -2745,7 +3045,8 @@ class WecomCliInstance {
2745
3045
  chatType: parsed.metadata.chatType,
2746
3046
  userId: parsed.metadata.senderId,
2747
3047
  sourceId: this.config.id,
2748
- reqId: parsed.reqId
3048
+ reqId: parsed.reqId,
3049
+ statusNotifications: this.config.statusNotifications === true
2749
3050
  });
2750
3051
  const event = {
2751
3052
  metadata: parsed.metadata,
@@ -3606,7 +3907,7 @@ ${USAGE2}`
3606
3907
  }
3607
3908
 
3608
3909
  // src/env/event-source/command-registry.ts
3609
- var logger3 = createLogger("command-registry");
3910
+ var logger2 = createLogger("command-registry");
3610
3911
  var COMMAND_PREFIX = "##command##:";
3611
3912
  var COMMAND_PREFIX_LOWER = COMMAND_PREFIX.toLowerCase();
3612
3913
  function stripLeadingMentions(text) {
@@ -3679,7 +3980,7 @@ ${this.renderHelp()}`
3679
3980
  });
3680
3981
  } catch (err) {
3681
3982
  const msg = err instanceof Error ? err.message : String(err);
3682
- logger3.error(`[CommandRegistry] command "${subcommand}" failed: ${msg}`);
3983
+ logger2.error(`[CommandRegistry] command "${subcommand}" failed: ${msg}`);
3683
3984
  return { ok: false, error: `命令执行失败:${msg}` };
3684
3985
  }
3685
3986
  }
@@ -3710,7 +4011,7 @@ async function handleCommandMessage(text, opts = {}) {
3710
4011
  if (!invocation)
3711
4012
  return null;
3712
4013
  const registry = opts.registry ?? getGlobalCommandRegistry();
3713
- logger3.info(`[handleCommandMessage] dispatching "${invocation.subcommand}" args=${JSON.stringify(invocation.args)}`);
4014
+ logger2.info(`[handleCommandMessage] dispatching "${invocation.subcommand}" args=${JSON.stringify(invocation.args)}`);
3714
4015
  return registry.dispatch(invocation.subcommand, invocation.args, {
3715
4016
  rawText: text,
3716
4017
  configPath: opts.configPath
@@ -3719,7 +4020,7 @@ async function handleCommandMessage(text, opts = {}) {
3719
4020
 
3720
4021
  // src/env/event-source/command-card-action.ts
3721
4022
  init_logger();
3722
- var logger4 = createLogger("command-card-action");
4023
+ var logger3 = createLogger("command-card-action");
3723
4024
  function isCommandCardAction(event) {
3724
4025
  const av = event.actionValue;
3725
4026
  if (av && typeof av === "object") {
@@ -3776,7 +4077,7 @@ async function handleCommandCardAction(event, ports) {
3776
4077
  return { ok: false, error: "无法识别的 command 卡片动作" };
3777
4078
  }
3778
4079
  const executed = `${call.subcommand} ${call.args.join(" ")}`;
3779
- logger4.info(`[handleCommandCardAction] 执行 ${executed}`);
4080
+ logger3.info(`[handleCommandCardAction] 执行 ${executed}`);
3780
4081
  let result;
3781
4082
  try {
3782
4083
  result = await ports.executeCommand(call.subcommand, call.args, {
@@ -3784,7 +4085,7 @@ async function handleCommandCardAction(event, ports) {
3784
4085
  });
3785
4086
  } catch (err) {
3786
4087
  const msg = err instanceof Error ? err.message : String(err);
3787
- logger4.error(`[handleCommandCardAction] ${executed} 执行异常:${msg}`);
4088
+ logger3.error(`[handleCommandCardAction] ${executed} 执行异常:${msg}`);
3788
4089
  return { ok: false, executed, error: `命令执行异常:${msg}` };
3789
4090
  }
3790
4091
  if (!result.ok) {
@@ -3797,7 +4098,7 @@ async function handleCommandCardAction(event, ports) {
3797
4098
  await ports.patchCard(event.token, card, event.profile);
3798
4099
  } catch (err) {
3799
4100
  patchError = err.message;
3800
- logger4.error(`[handleCommandCardAction] patch 卡片失败:${patchError}`);
4101
+ logger3.error(`[handleCommandCardAction] patch 卡片失败:${patchError}`);
3801
4102
  }
3802
4103
  }
3803
4104
  return { ok: true, executed, card, patchError };
@@ -3805,8 +4106,8 @@ async function handleCommandCardAction(event, ports) {
3805
4106
 
3806
4107
  // src/env/event-source/event-source-handlers.ts
3807
4108
  init_logger();
3808
- var execAsync2 = promisify2(execSync);
3809
- var logger5 = createLogger("lark-cli:event-source");
4109
+ var execAsync = promisify(execSync);
4110
+ var logger4 = createLogger("lark-cli:event-source");
3810
4111
  var CHILD_READY_TIMEOUT_MS = 5000;
3811
4112
  var GRACEFUL_STOP_TIMEOUT_MS = 5000;
3812
4113
  var CARD_ACTION_EVENT_TYPE = "card.action.trigger";
@@ -3830,18 +4131,52 @@ function matchEventType(eventType, patterns) {
3830
4131
  }
3831
4132
  return false;
3832
4133
  }
4134
+ function buildSlimRawData(rawEvent) {
4135
+ const event = rawEvent ?? {};
4136
+ const innerEvent = event.event;
4137
+ const message = innerEvent?.message ?? event.message;
4138
+ const sender = innerEvent?.sender ?? event.sender;
4139
+ const slim = {};
4140
+ const chatId = message?.chat_id ?? event.chat_id;
4141
+ if (chatId !== undefined)
4142
+ slim.chat_id = chatId;
4143
+ const messageId = message?.message_id ?? event.message_id ?? event.id;
4144
+ if (messageId !== undefined)
4145
+ slim.message_id = messageId;
4146
+ const rawSenderId = sender?.sender_id ?? event.sender_id;
4147
+ let senderId;
4148
+ if (typeof rawSenderId === "string") {
4149
+ senderId = rawSenderId;
4150
+ } else if (rawSenderId && typeof rawSenderId === "object") {
4151
+ const obj = rawSenderId;
4152
+ senderId = obj.open_id ?? obj.user_id ?? obj.union_id;
4153
+ }
4154
+ if (senderId !== undefined)
4155
+ slim.sender_id = senderId;
4156
+ const chatType = message?.chat_type ?? event.chat_type;
4157
+ if (chatType !== undefined)
4158
+ slim.chat_type = chatType;
4159
+ const content = message?.content ?? event.content;
4160
+ if (content !== undefined)
4161
+ slim.content = content;
4162
+ const mentions = message?.mentions ?? event.mentions;
4163
+ if (mentions !== undefined)
4164
+ slim.mentions = mentions;
4165
+ return slim;
4166
+ }
3833
4167
  function buildLarkRecommendedAction(opts) {
3834
- const { profile, chatType, appId, chatId, messageId } = opts;
4168
+ const { profile, chatType, appId, chatId, messageId, senderId } = opts;
3835
4169
  const profileFlag = profile ? `--profile ${profile} ` : "";
3836
4170
  const profileWarn = profile ? `(⚠️ 必须用 profile="${profile}" 调 lark-cli;openId per-app 隔离,换 profile 会失败)` : "";
3837
4171
  const isGroup = chatType === "group";
3838
- const scopeHint = isGroup ? `群聊消息;回复到 replyTo.chatId。如需 @发件人,用结构化 at 节点 {"tag":"at","user_id":"<senderId>"}(不要写 <at> 文本标签)` : "p2p 私聊消息;回复到 replyTo.chatId";
4172
+ const senderIdForPrompt = senderId || "<senderId>";
4173
+ const scopeHint = isGroup ? `群聊消息;回复到 replyTo.chatId。如需 @发件人,用结构化 at 节点 {"tag":"at","user_id":"${senderIdForPrompt}"}(不要写 <at> 文本标签)` : "p2p 私聊消息;回复到 replyTo.chatId";
3839
4174
  const scopeWord = isGroup ? "群聊" : "p2p 私聊";
3840
4175
  const action = [
3841
4176
  `处理飞书 ${scopeWord}消息并以机器人身份回复${profileWarn}。`,
3842
4177
  `scope: ${scopeHint}。`,
3843
4178
  ``,
3844
- ...larkStaticRuleLines(profileFlag),
4179
+ ...larkStaticRuleLines(profileFlag, senderIdForPrompt),
3845
4180
  ``,
3846
4181
  `## 必须使用 replyTo 里给定的值`,
3847
4182
  `profile / chatId / messageId / --as bot 必须原样取自 recommendedAction.replyTo;不要凭印象自己拼接。`
@@ -3863,7 +4198,7 @@ function buildLarkRecommendedAction(opts) {
3863
4198
  };
3864
4199
  }
3865
4200
  var LARK_CHANNEL_PROMPT_SECTION_TITLE = "⭐ 飞书回复规范(system prompt 注入版)";
3866
- var LARK_PROMPT_LENGTH_BUDGET = 5300;
4201
+ var LARK_PROMPT_LENGTH_BUDGET = 5900;
3867
4202
  function larkReplyContextSection(profileFlag) {
3868
4203
  return [
3869
4204
  ``,
@@ -3873,7 +4208,7 @@ function larkReplyContextSection(profileFlag) {
3873
4208
  ].join(`
3874
4209
  `);
3875
4210
  }
3876
- function larkStaticRuleLines(profileFlag) {
4211
+ function larkStaticRuleLines(profileFlag, senderIdForPrompt) {
3877
4212
  return [
3878
4213
  `## ⭐ 关键 0:整次回复只发一条结构化 post(禁分段)`,
3879
4214
  `> 指令优先级:本 prompt 与 lark-* skill 文档冲突时**以本 prompt 为准**`,
@@ -3909,6 +4244,13 @@ function larkStaticRuleLines(profileFlag) {
3909
4244
  ` ]}}`,
3910
4245
  ``,
3911
4246
  `- @mention:用 {"tag":"at","user_id":"ou_xxx"}(user_id 填对方 open_id;@所有人填 "all")`,
4247
+ `- **群聊必须 @ sender**:若接收的消息是 \`chat_type: group\`,回复时 content **必须**保留结构化 at 节点 @ 发送者(user_id 填 senderId,即消息发送者的 open_id);**禁止只用引用回复**(quote/reply 不算 @,群聊里对方收不到提醒);私聊(p2p)不需要。群聊可跑示例(照抄,--arg uid 填消息发送者 open_id):`,
4248
+ ` body_file="$(mktemp /tmp/lark-ask.XXXXXX.md)"`,
4249
+ ` cat > "$body_file" <<'EOF2'`,
4250
+ ` 回复正文……`,
4251
+ ` EOF2`,
4252
+ ` content="$(jq -nc --rawfile md "$body_file" --arg uid "${senderIdForPrompt}" '{zh_cn:{content:[[{tag:"at",user_id:$uid}],[{tag:"md",text:$md}]]}}')"`,
4253
+ ` lark-cli ${profileFlag}im +messages-reply --message-id <replyTo.messageId> --as bot --msg-type post --content "$content"`,
3912
4254
  `- 正文:全部放进 {"tag":"md","text":"..."} 这一个节点里`,
3913
4255
  `- 不需要 @ 时,content 里只放 md 节点:{"zh_cn":{"content":[[{"tag":"md","text":"..."}]]}}`,
3914
4256
  `- md 节点支持 CommonMark 0.31 + GFM:ATX 标题(#~######)/ 表格 / 任务列表 / 加粗 / 围栏代码块 / 超链接`,
@@ -3926,7 +4268,7 @@ function larkStaticRuleLines(profileFlag) {
3926
4268
  ` cat > "$body_file" <<'LARK_REPLY_EOF'`,
3927
4269
  ` ...(回复的 Markdown 正文,可多行 / 含表格 / 含特殊字符;不要写 <at> 标签)`,
3928
4270
  ` LARK_REPLY_EOF`,
3929
- ` content="$(jq -nc --rawfile md "$body_file" --arg uid "<senderId>" '{zh_cn:{content:[[{tag:"at",user_id:$uid}],[{tag:"md",text:$md}]]}}')"`,
4271
+ ` content="$(jq -nc --rawfile md "$body_file" --arg uid "${senderIdForPrompt}" '{zh_cn:{content:[[{tag:"at",user_id:$uid}],[{tag:"md",text:$md}]]}}')"`,
3930
4272
  ` lark-cli ${profileFlag}im +messages-reply --message-id <取自 replyTo.messageId> --as bot --msg-type post --content "$content"`,
3931
4273
  ``,
3932
4274
  `不需要 @ 某人时,把 jq 表达式换成 '{zh_cn:{content:[[{tag:"md",text:$md}]]}}'。`,
@@ -3999,12 +4341,13 @@ function buildLarkChannelSystemPrompt(opts) {
3999
4341
  larkReplyContextSection(profileFlag)
4000
4342
  ].join(`
4001
4343
  `);
4002
- return header + larkStaticRuleLines(profileFlag).join(`
4344
+ return header + larkStaticRuleLines(profileFlag, "<senderId>").join(`
4003
4345
  `) + footer;
4004
4346
  }
4005
- function buildLarkLeanAction(chatType) {
4347
+ function buildLarkLeanAction(chatType, senderId) {
4006
4348
  const isGroup = chatType === "group";
4007
- const scopeHint = isGroup ? `群聊消息;回复到 replyTo.chatId。如需 @发件人,用结构化 at 节点 {"tag":"at","user_id":"<senderId>"}(不要写 <at> 文本标签)` : "p2p 私聊消息;回复到 replyTo.chatId";
4349
+ const senderIdForPrompt = senderId || "<senderId>";
4350
+ const scopeHint = isGroup ? `群聊消息;回复到 replyTo.chatId。如需 @发件人,用结构化 at 节点 {"tag":"at","user_id":"${senderIdForPrompt}"}(不要写 <at> 文本标签)` : "p2p 私聊消息;回复到 replyTo.chatId";
4008
4351
  return [
4009
4352
  `处理飞书${isGroup ? "群聊" : "p2p 私聊"}消息并回复。回复规范见 system prompt「${LARK_CHANNEL_PROMPT_SECTION_TITLE}」章节。`,
4010
4353
  `scope: ${scopeHint}。`,
@@ -4087,11 +4430,12 @@ function extractMetadata(rawEvent, eventType) {
4087
4430
  chatType: metadata.chatType,
4088
4431
  appId: metadata.appId,
4089
4432
  chatId: metadata.chatId,
4090
- messageId: metadata.messageId
4433
+ messageId: metadata.messageId,
4434
+ senderId: metadata.senderId
4091
4435
  });
4092
4436
  } else {
4093
4437
  recommendedAction = {
4094
- action: buildLarkLeanAction(metadata.chatType),
4438
+ action: buildLarkLeanAction(metadata.chatType, metadata.senderId),
4095
4439
  replyTo: {
4096
4440
  appId: metadata.appId,
4097
4441
  profile: profile2,
@@ -4117,7 +4461,8 @@ function extractMetadata(rawEvent, eventType) {
4117
4461
  chatId: messageData?.chat_id,
4118
4462
  messageId: messageData?.message_id,
4119
4463
  params: {
4120
- userId: senderOpenId
4464
+ userId: senderOpenId,
4465
+ statusNotifications: event.statusNotifications === true
4121
4466
  }
4122
4467
  };
4123
4468
  return { metadata, replyChannel, recommendedAction };
@@ -4135,8 +4480,8 @@ class LarkCliInstance {
4135
4480
  cardActionPorts;
4136
4481
  setCardActionPorts(ports) {
4137
4482
  this.cardActionPorts = ports;
4138
- if (typeof logger5 !== "undefined" && logger5?.debug) {
4139
- logger5.debug(`[LarkCliInstance] CardActionPorts injected: ` + `getTask=${typeof ports.getTask}, updateTaskContext=${typeof ports.updateTaskContext}, ` + `createGroupChat=${typeof ports.createGroupChat}, deleteGroupChat=${typeof ports.deleteGroupChat}, ` + `notifier=${typeof ports.notifier}`);
4483
+ if (typeof logger4 !== "undefined" && logger4?.debug) {
4484
+ logger4.debug(`[LarkCliInstance] CardActionPorts injected: ` + `getTask=${typeof ports.getTask}, updateTaskContext=${typeof ports.updateTaskContext}, ` + `createGroupChat=${typeof ports.createGroupChat}, deleteGroupChat=${typeof ports.deleteGroupChat}, ` + `notifier=${typeof ports.notifier}`);
4140
4485
  }
4141
4486
  }
4142
4487
  hasCardActionPorts() {
@@ -4172,7 +4517,7 @@ class LarkCliInstance {
4172
4517
  if (this.status === "running")
4173
4518
  return;
4174
4519
  if (this.starting) {
4175
- logger5.warn(`[LarkCliInstance] start() 已在执行中,忽略重复调用`);
4520
+ logger4.warn(`[LarkCliInstance] start() 已在执行中,忽略重复调用`);
4176
4521
  return;
4177
4522
  }
4178
4523
  this.starting = true;
@@ -4202,9 +4547,9 @@ class LarkCliInstance {
4202
4547
  if (!consumesCardAction)
4203
4548
  return;
4204
4549
  if (this.config.eventTypes?.length && !matchEventType(CARD_ACTION_EVENT_TYPE, this.config.eventTypes)) {
4205
- logger5.warn(`[LarkCliInstance] eventKeys 含 ${CARD_ACTION_EVENT_TYPE},但 eventTypes=` + `${JSON.stringify(this.config.eventTypes)} 不含它 → 卡片点击会被**静默丢弃**。` + `请把 ${CARD_ACTION_EVENT_TYPE} 加进 eventTypes,或清空 eventTypes。`);
4550
+ logger4.warn(`[LarkCliInstance] eventKeys 含 ${CARD_ACTION_EVENT_TYPE},但 eventTypes=` + `${JSON.stringify(this.config.eventTypes)} 不含它 → 卡片点击会被**静默丢弃**。` + `请把 ${CARD_ACTION_EVENT_TYPE} 加进 eventTypes,或清空 eventTypes。`);
4206
4551
  }
4207
- logger5.info(`[LarkCliInstance] 已监听 ${CARD_ACTION_EVENT_TYPE}。` + `⚠️ 若点击卡片无反应,先确认飞书开发者后台「事件与回调 → 回调配置」已开启该事件。`);
4552
+ logger4.info(`[LarkCliInstance] 已监听 ${CARD_ACTION_EVENT_TYPE}。` + `⚠️ 若点击卡片无反应,先确认飞书开发者后台「事件与回调 → 回调配置」已开启该事件。`);
4208
4553
  }
4209
4554
  spawnConsumeChild(command) {
4210
4555
  return spawn3("exec " + command, [], {
@@ -4244,14 +4589,14 @@ class LarkCliInstance {
4244
4589
  const conflicts = keys.filter((k) => busy.has(k));
4245
4590
  if (conflicts.length > 0) {
4246
4591
  const profile = this.config.profile;
4247
- logger5.warn(`[LarkCliInstance] eventKey 已被其它 consumer 占用(lark-cli 一个 key 只允许一个 consumer):` + `${conflicts.join(", ")}。本 event-source 启动后可能 exit 2。` + `用 \`lark-cli${profile ? ` --profile ${profile}` : ""} event status\` 查看,必要时 \`event stop\`。`);
4592
+ logger4.warn(`[LarkCliInstance] eventKey 已被其它 consumer 占用(lark-cli 一个 key 只允许一个 consumer):` + `${conflicts.join(", ")}。本 event-source 启动后可能 exit 2。` + `用 \`lark-cli${profile ? ` --profile ${profile}` : ""} event status\` 查看,必要时 \`event stop\`。`);
4248
4593
  }
4249
4594
  }
4250
4595
  async probeBusyEventKeys() {
4251
4596
  const profile = this.config.profile;
4252
4597
  const profileFlag = profile ? `--profile ${profile} ` : "";
4253
4598
  try {
4254
- const { stdout } = await execAsync2(`lark-cli ${profileFlag}event status --json`, {
4599
+ const { stdout } = await execAsync(`lark-cli ${profileFlag}event status --json`, {
4255
4600
  timeout: 5000
4256
4601
  });
4257
4602
  const parsed = JSON.parse(stdout);
@@ -4295,16 +4640,16 @@ class LarkCliInstance {
4295
4640
  });
4296
4641
  const handleExit = (code, source) => {
4297
4642
  if (code !== 0 && code !== null) {
4298
- logger5.warn(`[LarkCliInstance] consume 子进程异常退出 code=${code} (${source}) cmd="${command}"`);
4643
+ logger4.warn(`[LarkCliInstance] consume 子进程异常退出 code=${code} (${source}) cmd="${command}"`);
4299
4644
  this.status = "error";
4300
4645
  } else {
4301
- logger5.info(`[LarkCliInstance] consume 子进程已退出 code=${code} (${source}) cmd="${command}"`);
4646
+ logger4.info(`[LarkCliInstance] consume 子进程已退出 code=${code} (${source}) cmd="${command}"`);
4302
4647
  }
4303
4648
  settle();
4304
4649
  queueMicrotask(() => {
4305
4650
  if (this.children.length > 0 && this.children.every((c) => !isChildAlive(c))) {
4306
4651
  if (this.status !== "error") {
4307
- logger5.warn(`[LarkCliInstance] 所有 consume 子进程都已退出,状态从 running 改为 stopped`);
4652
+ logger4.warn(`[LarkCliInstance] 所有 consume 子进程都已退出,状态从 running 改为 stopped`);
4308
4653
  this.status = "stopped";
4309
4654
  }
4310
4655
  }
@@ -4313,12 +4658,12 @@ class LarkCliInstance {
4313
4658
  child.on("exit", (code) => handleExit(code, "exit"));
4314
4659
  child.on("close", (code) => handleExit(code, "close"));
4315
4660
  child.on("error", (error) => {
4316
- logger5.error(`[LarkCliInstance] consume 子进程 error: ${error.message}`);
4661
+ logger4.error(`[LarkCliInstance] consume 子进程 error: ${error.message}`);
4317
4662
  this.status = "error";
4318
4663
  settle();
4319
4664
  });
4320
4665
  const timer = setTimeout(() => {
4321
- logger5.warn(`[LarkCliInstance] consume 子进程 ${CHILD_READY_TIMEOUT_MS}ms 内未见 ready marker: cmd="${command}"`);
4666
+ logger4.warn(`[LarkCliInstance] consume 子进程 ${CHILD_READY_TIMEOUT_MS}ms 内未见 ready marker: cmd="${command}"`);
4322
4667
  settle();
4323
4668
  }, CHILD_READY_TIMEOUT_MS);
4324
4669
  ready.then(() => clearTimeout(timer));
@@ -4341,7 +4686,7 @@ class LarkCliInstance {
4341
4686
  try {
4342
4687
  child.kill("SIGTERM");
4343
4688
  } catch (error) {
4344
- logger5.warn(`[LarkCliInstance] Error sending SIGTERM to ${child.pid}: ${error}`);
4689
+ logger4.warn(`[LarkCliInstance] Error sending SIGTERM to ${child.pid}: ${error}`);
4345
4690
  }
4346
4691
  }
4347
4692
  const allExited = Promise.all(children.map((child) => new Promise((resolve2) => {
@@ -4396,15 +4741,15 @@ class LarkCliInstance {
4396
4741
  }
4397
4742
  wireCardActionPorts() {
4398
4743
  if (this.cardActionPorts) {
4399
- logger5.debug("[LarkCliInstance] cardActionPorts 已由外部注入,跳过默认 cross-wire");
4744
+ logger4.debug("[LarkCliInstance] cardActionPorts 已由外部注入,跳过默认 cross-wire");
4400
4745
  return;
4401
4746
  }
4402
4747
  const taskComponent = this.environment?.getComponent?.("task");
4403
4748
  if (!taskComponent?.getTask || !taskComponent?.updateTask) {
4404
- logger5.debug(`[LarkCliInstance] card.action.trigger 端口未注入:taskComponent 不可用` + `(getTask=${typeof taskComponent?.getTask}, updateTask=${typeof taskComponent?.updateTask})。` + `卡片按钮点击会被跳过。`);
4749
+ logger4.debug(`[LarkCliInstance] card.action.trigger 端口未注入:taskComponent 不可用` + `(getTask=${typeof taskComponent?.getTask}, updateTask=${typeof taskComponent?.updateTask})。` + `卡片按钮点击会被跳过。`);
4405
4750
  return;
4406
4751
  }
4407
- const sharedNotifier = this.pluginInstances.get("LarkCliTaskNotifyPlugin")?.cardNotifier ?? this.pluginInstances.get("PerTaskGroupChatPlugin")?.cardNotifier;
4752
+ const sharedNotifier = this.pluginInstances.get("LarkCliTaskNotifyPlugin")?.cardNotifier ?? this.pluginInstances.get("PerTaskGroupChatPlugin")?.cardNotifier ?? getSharedLarkCardNotifier();
4408
4753
  this.setCardActionPorts({
4409
4754
  getTask: async (id) => {
4410
4755
  const t = await taskComponent.getTask(id);
@@ -4428,9 +4773,119 @@ class LarkCliInstance {
4428
4773
  },
4429
4774
  createGroupChat: (opts) => createGroupChatViaLarkCli(opts),
4430
4775
  deleteGroupChat: (chatId, profile) => deleteGroupChatViaLarkCli(chatId, profile),
4431
- notifier: sharedNotifier ?? new LarkCardPatchNotifier
4776
+ notifier: sharedNotifier ?? getSharedLarkCardNotifier(),
4777
+ resolveSessionIdByTask: async (taskId) => {
4778
+ const fromNotifier = sharedNotifier?.getSessionIdByTask?.(taskId);
4779
+ if (fromNotifier)
4780
+ return fromNotifier;
4781
+ const tc = taskComponent;
4782
+ try {
4783
+ const rec = getSharedLarkCardStateStore().get(taskId);
4784
+ if (rec?.sessionId)
4785
+ return rec.sessionId;
4786
+ } catch {}
4787
+ return;
4788
+ },
4789
+ loadSessionActivity: createSessionActivityProbe({
4790
+ getSession: async (sessionId) => {
4791
+ const sc = this.environment?.getComponent?.("session");
4792
+ if (!sc?.get)
4793
+ return;
4794
+ const s = await sc.get(sessionId);
4795
+ if (!s)
4796
+ return;
4797
+ return {
4798
+ ...typeof s.messageCount === "number" ? { messageCount: s.messageCount } : {},
4799
+ ...typeof s.updatedAt === "number" ? { updatedAt: s.updatedAt } : {}
4800
+ };
4801
+ },
4802
+ getLastMessage: async (sessionId) => {
4803
+ const sc = this.environment?.getComponent?.("session");
4804
+ if (!sc?.getMessages)
4805
+ return;
4806
+ const tail = await sc.getMessages(sessionId, { offset: -1, limit: 1 });
4807
+ return toActivityMessage(tail?.[tail.length - 1]);
4808
+ },
4809
+ getLifecycle: (sessionId) => {
4810
+ const bgTasks = taskComponent.getBackgroundTaskManager?.()?.listTasks?.() ?? [];
4811
+ const hit = bgTasks.find((t) => t?.subSessionId === sessionId);
4812
+ return mapBgTaskStatusToSessionLifecycle(hit?.status);
4813
+ }
4814
+ }),
4815
+ loadSubSessionActivity: createActivityProbe({
4816
+ listBackgroundTasks: () => taskComponent.getBackgroundTaskManager?.()?.listTasks?.(),
4817
+ getSubSessionActivity: (bgId) => taskComponent.getBackgroundTaskManager?.()?.getSubSessionActivity?.(bgId),
4818
+ getLastMessage: async (subSessionId) => {
4819
+ const sessionComponent = this.environment?.getComponent?.("session");
4820
+ if (!sessionComponent?.getMessages)
4821
+ return;
4822
+ const tail = await sessionComponent.getMessages(subSessionId, {
4823
+ offset: -1,
4824
+ limit: 1
4825
+ });
4826
+ return toActivityMessage(tail?.[tail.length - 1]);
4827
+ },
4828
+ isTerminalTask: async (taskId) => {
4829
+ const t = await taskComponent.getTask?.(taskId);
4830
+ const status = t?.status;
4831
+ return status === "completed" || status === "cancelled" || status === "failed" || status === "archived";
4832
+ }
4833
+ }),
4834
+ loadSessionMessages: async (sessionId, limit) => {
4835
+ const sc = this.environment?.getComponent?.("session");
4836
+ if (!sc?.getMessages)
4837
+ return;
4838
+ const n = typeof limit === "number" && limit > 0 ? limit : RECENT_MESSAGE_LIMIT;
4839
+ const tail = await sc.getMessages(sessionId, {
4840
+ offset: -1,
4841
+ limit: n * 3
4842
+ });
4843
+ const calls = extractToolCallsFromMessages(tail);
4844
+ return calls.slice(-n);
4845
+ },
4846
+ attachGroupSession: async (args) => {
4847
+ const sessionComponent = this.environment?.getComponent?.("session");
4848
+ if (typeof sessionComponent?.attachGroupSession !== "function") {
4849
+ logger4.warn(`[LarkCliInstance] attachGroupSession 跳过:sessionComponent.attachGroupSession 不可用`);
4850
+ return;
4851
+ }
4852
+ await sessionComponent.attachGroupSession(args);
4853
+ logger4.info(`[LarkCliInstance] 群 session 已初始化:${args.chatId} ← ${args.originSessionId}` + `(独立会话 + 1 条 user 事件背景提示)`);
4854
+ },
4855
+ findGroupChatForSession: async (sessionId) => {
4856
+ const sessionComponent = this.environment?.getComponent?.("session");
4857
+ if (typeof sessionComponent?.findGroupChatForSession !== "function") {
4858
+ logger4.warn(`[LarkCliInstance] findGroupChatForSession 跳过:sessionComponent 不支持`);
4859
+ return;
4860
+ }
4861
+ return await sessionComponent.findGroupChatForSession(sessionId);
4862
+ },
4863
+ detachGroupSession: async (chatId) => {
4864
+ const sessionComponent = this.environment?.getComponent?.("session");
4865
+ if (typeof sessionComponent?.detachGroupSession !== "function") {
4866
+ logger4.warn(`[LarkCliInstance] detachGroupSession 跳过:sessionComponent.detachGroupSession 不可用`);
4867
+ return;
4868
+ }
4869
+ const r = await sessionComponent.detachGroupSession(chatId);
4870
+ if (!r.ok) {
4871
+ logger4.warn(`[LarkCliInstance] detachGroupSession 失败: ${chatId}`);
4872
+ return;
4873
+ }
4874
+ logger4.info(`[LarkCliInstance] 群 session 已标记解散:${chatId}`);
4875
+ },
4876
+ getSessionTitle: async (sessionId) => {
4877
+ const sessionComponent = this.environment?.getComponent?.("session");
4878
+ if (typeof sessionComponent?.get !== "function")
4879
+ return;
4880
+ try {
4881
+ const s = await sessionComponent.get(sessionId);
4882
+ return typeof s?.title === "string" && s.title.trim() ? s.title.trim() : undefined;
4883
+ } catch {
4884
+ return;
4885
+ }
4886
+ }
4432
4887
  });
4433
- logger5.info(`[LarkCliInstance] card.action.trigger 端口已注入` + `(notifier ${sharedNotifier ? "复用插件共享实例" : "新建实例"})`);
4888
+ logger4.info(`[LarkCliInstance] card.action.trigger 端口已注入` + `(notifier ${sharedNotifier ? "复用插件共享实例" : "新建实例"})`);
4434
4889
  }
4435
4890
  getPluginHookPoints(plugin) {
4436
4891
  if (Array.isArray(plugin.hooks)) {
@@ -4589,6 +5044,12 @@ class LarkCliInstance {
4589
5044
  getTask: taskComponent?.getTask ? (id) => taskComponent.getTask(id) : undefined
4590
5045
  };
4591
5046
  plugin.setCardRoutingPorts({ ...this.cardRoutingPorts });
5047
+ if (typeof plugin === "object" && "syncGroupCardFromOriginFn" in plugin) {
5048
+ const sessionComponent = this.environment?.getComponent?.("session");
5049
+ if (typeof sessionComponent?.findGroupChatForSession === "function") {
5050
+ plugin.syncGroupCardFromOriginFn = (sid) => sessionComponent.findGroupChatForSession(sid);
5051
+ }
5052
+ }
4592
5053
  }
4593
5054
  injectSessionComponent(plugin) {
4594
5055
  if (typeof plugin.setSessionComponent !== "function") {
@@ -4628,7 +5089,7 @@ class LarkCliInstance {
4628
5089
  }
4629
5090
  return null;
4630
5091
  } catch (err) {
4631
- logger5.warn(`[LarkCliInstance] resolveChatTaskBinding failed for chatId=${chatId}: ${err}. ` + `群消息将不会注入 parentTaskId。`);
5092
+ logger4.warn(`[LarkCliInstance] resolveChatTaskBinding failed for chatId=${chatId}: ${err}. ` + `群消息将不会注入 parentTaskId。`);
4632
5093
  return null;
4633
5094
  }
4634
5095
  }
@@ -4650,7 +5111,7 @@ class LarkCliInstance {
4650
5111
  processStreamWith(data, buffer, opts = {}) {
4651
5112
  buffer.value += data;
4652
5113
  if (buffer.value.length > 2 * 1024 * 1024) {
4653
- logger5.warn(`[LarkCliInstance] stream 行缓冲超过 2MB 仍无换行,丢弃(疑似非 NDJSON 输出)`);
5114
+ logger4.warn(`[LarkCliInstance] stream 行缓冲超过 2MB 仍无换行,丢弃(疑似非 NDJSON 输出)`);
4654
5115
  buffer.value = "";
4655
5116
  return;
4656
5117
  }
@@ -4686,12 +5147,12 @@ class LarkCliInstance {
4686
5147
  const now = Date.now();
4687
5148
  const lastShared = LarkCliInstance.SHARED_SEEN.get(dedupKey);
4688
5149
  if (lastShared !== undefined && now - lastShared < LarkCliInstance.SHARED_MSG_TTL_MS) {
4689
- logger5.warn(`[LarkCliInstance] duplicate lark-cli event suppressed across instances (event_id=${dedupKey}, ` + `lastSeen=${now - lastShared}ms ago). ` + `Task #4137: SHARED_SEEN 跨实例去重。`);
5150
+ logger4.warn(`[LarkCliInstance] duplicate lark-cli event suppressed across instances (event_id=${dedupKey}, ` + `lastSeen=${now - lastShared}ms ago). ` + `Task #4137: SHARED_SEEN 跨实例去重。`);
4690
5151
  return;
4691
5152
  }
4692
5153
  const last = this.recentEventIds.get(dedupKey);
4693
5154
  if (last !== undefined && now - last < this.DEDUP_WINDOW_MS) {
4694
- logger5.warn(`[LarkCliInstance] duplicate lark-cli event suppressed (event_id=${dedupKey}, ` + `lastSeen=${now - last}ms ago). ` + `Task #3977/3978: 防止同一飞书事件触发两次任务创建。`);
5155
+ logger4.warn(`[LarkCliInstance] duplicate lark-cli event suppressed (event_id=${dedupKey}, ` + `lastSeen=${now - last}ms ago). ` + `Task #3977/3978: 防止同一飞书事件触发两次任务创建。`);
4695
5156
  return;
4696
5157
  }
4697
5158
  LarkCliInstance.SHARED_SEEN.set(dedupKey, now);
@@ -4750,13 +5211,17 @@ class LarkCliInstance {
4750
5211
  ...rawEvent,
4751
5212
  profile: this.config.profile,
4752
5213
  eventSourceId: this.config.id,
4753
- channelPromptMode: this.config.channelPromptMode ?? "lean"
5214
+ channelPromptMode: this.config.channelPromptMode ?? "lean",
5215
+ statusNotifications: this.config.statusNotifications === true
4754
5216
  };
4755
5217
  const { metadata, replyChannel, recommendedAction } = extractMetadata(enrichedEvent, eventType);
4756
5218
  const channelSystemPrompt = this.getChannelSystemPrompt();
4757
5219
  if (channelSystemPrompt !== undefined) {
4758
5220
  metadata.systemPrompt = channelSystemPrompt;
4759
5221
  }
5222
+ if (this.config.extendTools?.length) {
5223
+ metadata.extendTools = [...this.config.extendTools];
5224
+ }
4760
5225
  failureReplyChannel = replyChannel;
4761
5226
  const commandText = isCardAction ? undefined : extractCommandText(enrichedEvent);
4762
5227
  const parentTaskId = isCardAction ? null : await this.resolveChatTaskBinding(metadata?.chatId);
@@ -4785,8 +5250,9 @@ class LarkCliInstance {
4785
5250
  console.error(`[LarkCliInstance] emitTriggerEvent failed: ${err instanceof Error ? err.message : String(err)}`);
4786
5251
  }
4787
5252
  }
5253
+ const slimRawData = buildSlimRawData(rawEvent);
4788
5254
  const message = JSON.stringify({
4789
- rawData,
5255
+ rawData: slimRawData,
4790
5256
  recommendedAction,
4791
5257
  ...triggerEventId ? { triggerEventId } : {}
4792
5258
  });
@@ -4810,6 +5276,7 @@ class LarkCliInstance {
4810
5276
  replyChannel,
4811
5277
  agent,
4812
5278
  ...channelSystemPrompt !== undefined ? { systemPrompt: channelSystemPrompt } : {},
5279
+ ...this.config.extendTools?.length ? { extendTools: [...this.config.extendTools] } : {},
4813
5280
  parentTaskId: parentTaskId ?? undefined,
4814
5281
  ...triggerEventId ? { triggerEventId } : {},
4815
5282
  traceContext: eventTraceContext
@@ -4886,11 +5353,11 @@ class LarkCliInstance {
4886
5353
  const chatId = event.chat_id || "";
4887
5354
  const operatorId = event.operator_id?.open_id ?? event.operator_id ?? undefined;
4888
5355
  if (!token || !messageId || !chatId) {
4889
- logger5.warn(`[LarkCliInstance] handleCardActionEvent: missing token/messageId/chatId, skip`);
5356
+ logger4.warn(`[LarkCliInstance] handleCardActionEvent: missing token/messageId/chatId, skip`);
4890
5357
  return;
4891
5358
  }
4892
5359
  if (!this.cardActionPorts) {
4893
- logger5.warn(`[LarkCliInstance] handleCardActionEvent: cardActionPorts 未注入,` + `card.action.trigger 事件被跳过。` + `调 instance.setCardActionPorts({getTask, updateTaskContext, createGroupChat, deleteGroupChat, notifier}) 注入。`);
5360
+ logger4.warn(`[LarkCliInstance] handleCardActionEvent: cardActionPorts 未注入,` + `card.action.trigger 事件被跳过。` + `调 instance.setCardActionPorts({getTask, updateTaskContext, createGroupChat, deleteGroupChat, notifier}) 注入。`);
4894
5361
  return;
4895
5362
  }
4896
5363
  const result = await handleCardAction({
@@ -4903,12 +5370,17 @@ class LarkCliInstance {
4903
5370
  }, this.cardActionPorts);
4904
5371
  if (result.ok && result.cardUpdate) {
4905
5372
  const patch = this.cardActionPorts.patchByToken ?? patchCardByLarkCli;
4906
- await patch(token, result.cardUpdate, profile);
5373
+ try {
5374
+ await patch(token, result.cardUpdate, profile);
5375
+ logger4.info(`[LarkCliInstance] 卡片 patch 成功: messageId=${messageId} action=${String(result.action)} chatId=${chatId}`);
5376
+ } catch (patchErr) {
5377
+ logger4.warn(`[LarkCliInstance] 卡片 patch 失败: messageId=${messageId} ` + `action=${String(result.action)} error=${patchErr.message}` + `(token 30min 有效,过期即失败——需用新卡重试)`);
5378
+ }
4907
5379
  } else {
4908
- logger5.warn(`[LarkCliInstance] handleCardActionEvent failed: ${result.error ?? "unknown"}`);
5380
+ logger4.warn(`[LarkCliInstance] handleCardActionEvent failed: ${result.error ?? "unknown"}`);
4909
5381
  }
4910
5382
  } catch (err) {
4911
- logger5.error(`[LarkCliInstance] handleCardActionEvent error: ${err.message}`);
5383
+ logger4.error(`[LarkCliInstance] handleCardActionEvent error: ${err.message}`);
4912
5384
  }
4913
5385
  }
4914
5386
  isCommandCardActionEvent(event) {
@@ -5024,7 +5496,7 @@ class LarkCliInstance {
5024
5496
  }
5025
5497
  await this.sendLarkText(output.text, profile, messageId, chatId);
5026
5498
  } catch (err) {
5027
- logger5.error(`[LarkCliInstance] handleCommandMessage error: ${err.message}`);
5499
+ logger4.error(`[LarkCliInstance] handleCommandMessage error: ${err.message}`);
5028
5500
  try {
5029
5501
  const card = renderTextCard("⚠️ 命令执行异常", `命令处理时发生异常:${err.message}`);
5030
5502
  await this.sendLarkCard(card, profile, messageId, chatId);
@@ -5314,7 +5786,7 @@ var larkCliHandler = {
5314
5786
  },
5315
5787
  async emitTriggerEvent(payload, store) {
5316
5788
  const eventId = await store.insertTriggerEvent(payload, "lark-cli");
5317
- logger5.info(`[larkCliHandler] emitTriggerEvent: id=${eventId}`, {
5789
+ logger4.info(`[larkCliHandler] emitTriggerEvent: id=${eventId}`, {
5318
5790
  chatId: payload.metadata?.chatId,
5319
5791
  senderId: payload.senderId
5320
5792
  });
@@ -5329,7 +5801,7 @@ var larkCliHandler = {
5329
5801
  try {
5330
5802
  const result = await runner.run(command);
5331
5803
  if (result.code !== 0) {
5332
- logger5.error(`[larkCliHandler] replyMessage non-zero exit`, {
5804
+ logger4.error(`[larkCliHandler] replyMessage non-zero exit`, {
5333
5805
  code: result.code,
5334
5806
  stderr: result.stderr
5335
5807
  });
@@ -5343,7 +5815,7 @@ var larkCliHandler = {
5343
5815
  return { success: true, messageId: replyMessageId };
5344
5816
  } catch (err) {
5345
5817
  const errMsg = err instanceof Error ? err.message : String(err);
5346
- logger5.error(`[larkCliHandler] replyMessage spawn error`, { error: errMsg });
5818
+ logger4.error(`[larkCliHandler] replyMessage spawn error`, { error: errMsg });
5347
5819
  return { success: false, error: errMsg };
5348
5820
  }
5349
5821
  },
@@ -5392,7 +5864,7 @@ ${headline}
5392
5864
  - **chatId: ${chatId || "(缺失)"}** ← 关键!必须发到这里
5393
5865
  - **messageId: ${messageId || "(缺失)"}** ← ${messageId ? "reply 这条,保持话题线索" : "无触发消息,用 +messages-send"}
5394
5866
  - sender: ${senderId || "(未知)"}
5395
- ${input.notifyEvent.subSessionId ? `- 子 session: ${input.notifyEvent.subSessionId} ← 如需续跑:delegate_task(existingSubSessionId=<此值>, prompt=<新要求>)
5867
+ ${input.notifyEvent.subSessionId ? `- 子 session: ${input.notifyEvent.subSessionId} ← 如需续跑:delegate_task(sub_session_id="${input.notifyEvent.subSessionId}", prompt=<新要求>)
5396
5868
  ` : ""}${profile ? `- profile: ${profile} ← 调用 lark-cli 必须带 --profile
5397
5869
  ` : ""}
5398
5870
  ### 系统回执(rule-based 直发)
@@ -5447,11 +5919,11 @@ async function patchCardByLarkCli(token, card, profile) {
5447
5919
  const profileFlag = profile ? `--profile ${profile} ` : "";
5448
5920
  const cmd = `lark-cli ${profileFlag}api POST /open-apis/interactive/v1/card/update ` + `--data '${JSON.stringify({ token, card }).replace(/'/g, "'\\''")}' --as bot`;
5449
5921
  try {
5450
- const { stdout } = await execAsync2(cmd, { timeout: 30000 });
5451
- logger5.debug(`patchCardByLarkCli ok: ${stdout?.slice(0, 200)}`);
5922
+ const { stdout } = await execAsync(cmd, { timeout: 30000 });
5923
+ logger4.debug(`patchCardByLarkCli ok: ${stdout?.slice(0, 200)}`);
5452
5924
  } catch (err) {
5453
5925
  const msg = err.message;
5454
- logger5.error(`patchCardByLarkCli failed: ${msg}`);
5926
+ logger4.error(`patchCardByLarkCli failed: ${msg}`);
5455
5927
  throw new Error(`卡片刷新失败:${msg}`);
5456
5928
  }
5457
5929
  }
@@ -5467,4 +5939,4 @@ function getBuiltInHandler(type) {
5467
5939
  return builtInHandlers.find((h) => h.type === type);
5468
5940
  }
5469
5941
 
5470
- export { TimerInstance, timerHandler, buildDefaultBountyIMSystemPrompt, resolveBountyIMSystemPrompt, BountyIMInstance, bountyIMHandler, bountyCliHandler, sanitizeWecomFrame, WECOM_CLI_ES_TYPE, WECOM_CHAT_ID_PREFIX, WECOM_WS_DEFAULT_URL, readWecomCliConfig, WECOM_CREDENTIALS_DEFAULT_PATH, readWecomCredentialsFile, resolveWecomCredentials, buildWecomReplyChannel, parseWecomFrame, WECOM_CHANNEL_PROMPT_SECTION_TITLE, buildWecomChannelSystemPrompt, buildWecomNotifyPrompt, resolveWecomBinary, probeCommandBinary, wecomCliHandler, FALLBACK_MODEL_ACTION_NS, FALLBACK_MODEL_ACTIONS, isFallbackModelCard, renderTextCard, renderFallbackModelCard, DEFAULT_CONFIG_PATH, readFallbackModelList, readRawFallbackModelArray, writeFallbackModelList, fallbackModelCommand, listCommand, resolveEsFilePath, esCommand, COMMAND_PREFIX, isCommandMessage, extractCommandInvocation, CommandRegistry, getGlobalCommandRegistry, handleCommandMessage, isCommandCardAction, translateCardActionToCommand, handleCommandCardAction, buildLarkRecommendedAction, LARK_CHANNEL_PROMPT_SECTION_TITLE, LARK_PROMPT_LENGTH_BUDGET, buildLarkChannelSystemPrompt, resolveLarkChannelSystemPrompt, extractMetadata, LarkCliInstance, renderRuleNoticeSection, extractCommandText, larkCliHandler, patchCardByLarkCli, builtInHandlers, getBuiltInHandler };
5942
+ export { TimerInstance, timerHandler, buildDefaultBountyIMSystemPrompt, resolveBountyIMSystemPrompt, BountyIMInstance, bountyIMHandler, bountyCliHandler, sanitizeWecomFrame, WECOM_CLI_ES_TYPE, WECOM_CHAT_ID_PREFIX, WECOM_WS_DEFAULT_URL, readWecomCliConfig, WECOM_CREDENTIALS_DEFAULT_PATH, readWecomCredentialsFile, resolveWecomCredentials, buildWecomReplyChannel, parseWecomFrame, WECOM_CHANNEL_PROMPT_SECTION_TITLE, buildWecomChannelSystemPrompt, buildWecomNotifyPrompt, resolveWecomBinary, probeCommandBinary, wecomCliHandler, FALLBACK_MODEL_ACTION_NS, FALLBACK_MODEL_ACTIONS, isFallbackModelCard, renderTextCard, renderFallbackModelCard, DEFAULT_CONFIG_PATH, readFallbackModelList, readRawFallbackModelArray, writeFallbackModelList, fallbackModelCommand, listCommand, resolveEsFilePath, esCommand, COMMAND_PREFIX, isCommandMessage, extractCommandInvocation, CommandRegistry, getGlobalCommandRegistry, handleCommandMessage, isCommandCardAction, translateCardActionToCommand, handleCommandCardAction, buildSlimRawData, buildLarkRecommendedAction, LARK_CHANNEL_PROMPT_SECTION_TITLE, LARK_PROMPT_LENGTH_BUDGET, buildLarkChannelSystemPrompt, resolveLarkChannelSystemPrompt, extractMetadata, LarkCliInstance, renderRuleNoticeSection, extractCommandText, larkCliHandler, patchCardByLarkCli, builtInHandlers, getBuiltInHandler };