@kafca/agentdock 0.1.42 → 0.1.44

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 (106) hide show
  1. package/README.md +10 -0
  2. package/dist/renderer/assets/{Badge-D-WU4kUV.js → Badge-Qk9qHovH.js} +1 -1
  3. package/dist/renderer/assets/{Chat-BGkC5tAe.js → Chat-BK-fm0Xr.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-CfbC1jRd.js → ChatMarkdown-BKdW8OON.js} +1 -1
  5. package/dist/renderer/assets/{Config-qQb4bX5D.js → Config-B93CQGvY.js} +2 -2
  6. package/dist/renderer/assets/CronList-Z3TvSRKP.js +1 -0
  7. package/dist/renderer/assets/{Dashboard-xn6cUy_n.js → Dashboard-Ch3QkWwf.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-BdKBBUpr.js → EmptyState-kD9yAwtw.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-Duk8IQP3.js → KnowledgeDetail-BAMCLrjz.js} +2 -2
  10. package/dist/renderer/assets/{KnowledgeHome-BfjX_4Sv.js → KnowledgeHome-ChvjOfrc.js} +2 -2
  11. package/dist/renderer/assets/{Logs-Bfq61QMn.js → Logs-Cv0nth3j.js} +1 -1
  12. package/dist/renderer/assets/{Modal-B3vlBmBr.js → Modal-DwY4lecS.js} +1 -1
  13. package/dist/renderer/assets/MonitorList-JEvMoWUm.js +1 -0
  14. package/dist/renderer/assets/{Page-CPsLOP3z.js → Page-CnB9ePsb.js} +1 -1
  15. package/dist/renderer/assets/{ProjectDetail-D-g46wyF.js → ProjectDetail-DJa3sMmU.js} +2 -2
  16. package/dist/renderer/assets/{ProjectList-BrQEKDQ8.js → ProjectList-D6CXumR_.js} +1 -1
  17. package/dist/renderer/assets/{Select-C4CFMQmF.js → Select-zfoksLGB.js} +1 -1
  18. package/dist/renderer/assets/SessionChat-DUTh4WoM.js +1 -0
  19. package/dist/renderer/assets/{SessionList-DPWIUbOg.js → SessionList-D9o-lUst.js} +1 -1
  20. package/dist/renderer/assets/{ThreadChat-BjtU-j9q.js → ThreadChat-BYXlT-Kc.js} +7 -7
  21. package/dist/renderer/assets/{Workspace-BcNmHuJx.js → Workspace-BrhSwRQ-.js} +1 -1
  22. package/dist/renderer/assets/{arrow-left-DPzSmidS.js → arrow-left-CNEAuElg.js} +1 -1
  23. package/dist/renderer/assets/{book-open-B0xhoRqZ.js → book-open-BNJMhNI-.js} +1 -1
  24. package/dist/renderer/assets/{check-BTJTyd0C.js → check-BnclClRU.js} +1 -1
  25. package/dist/renderer/assets/{chevron-down-B6jJXlhi.js → chevron-down-Bh53e0O-.js} +1 -1
  26. package/dist/renderer/assets/{circle-qRhJR9qR.js → circle-ClZTWCWO.js} +1 -1
  27. package/dist/renderer/assets/desktop-C5vBT0yM.js +1 -0
  28. package/dist/renderer/assets/desktop-C6q6wBae.js +5 -0
  29. package/dist/renderer/assets/en-D2SkCrhG.js +1 -0
  30. package/dist/renderer/assets/{funnel-CbYKGTn8.js → funnel-_5hsz0xI.js} +1 -1
  31. package/dist/renderer/assets/{heart-DjwWJfkz.js → heart-u4Q1aOqj.js} +1 -1
  32. package/dist/renderer/assets/{index-BWmHJRhe.js → index-B36eowj-.js} +39 -34
  33. package/dist/renderer/assets/{index-CyZs_2NJ.js → index-BNFx4uYv.js} +3 -3
  34. package/dist/renderer/assets/{pencil-1dfAivmj.js → pencil-DLh9QNmT.js} +1 -1
  35. package/dist/renderer/assets/{play-CB3w1G6S.js → play-BDTk9Scm.js} +1 -1
  36. package/dist/renderer/assets/{plug-Bvep5o3r.js → plug-BG0JHW6B.js} +1 -1
  37. package/dist/renderer/assets/{plus-DJVg9m09.js → plus-BSc8POsz.js} +1 -1
  38. package/dist/renderer/assets/projects-DwfZwxDW.js +1 -0
  39. package/dist/renderer/assets/{save-xjJn3qf2.js → save-D5AO_G6a.js} +1 -1
  40. package/dist/renderer/assets/{search-BK3UrQxM.js → search-CniPk7Ey.js} +1 -1
  41. package/dist/renderer/assets/{server-Dyd2eXJu.js → server-GsXyQAZm.js} +1 -1
  42. package/dist/renderer/assets/{session-utils-eXC0020s.js → session-utils-BDxzgTBC.js} +1 -1
  43. package/dist/renderer/assets/{sessions-WLYpkyUt.js → sessions-CLcKA1Tt.js} +1 -1
  44. package/dist/renderer/assets/{shield-check-C0N6aWzF.js → shield-check-4i0E3hmf.js} +1 -1
  45. package/dist/renderer/assets/{trash-2-BGMTNJA6.js → trash-2-DhtOCsO5.js} +1 -1
  46. package/dist/renderer/assets/{user-BcGMLyhu.js → user-BtC_pVXV.js} +1 -1
  47. package/dist/renderer/assets/{wifi-off-FhcQx0HK.js → wifi-off-BHPzPY_B.js} +1 -1
  48. package/dist/renderer/assets/zh-DIcdHycM.js +1 -0
  49. package/dist/renderer/index.html +1 -1
  50. package/dist-electron/packages/contracts/src/local-core.js +28 -0
  51. package/dist-electron/packages/core-sdk/src/index.js +32 -0
  52. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +51 -0
  53. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +4 -1364
  54. package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +166 -0
  55. package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +231 -0
  56. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +263 -0
  57. package/dist-electron/services/local-ai-core/src/acp/store/platform-store.js +186 -0
  58. package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +201 -0
  59. package/dist-electron/services/local-ai-core/src/acp/store/schema.js +275 -0
  60. package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +266 -0
  61. package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +232 -0
  62. package/dist-electron/services/local-ai-core/src/acp/store/utils.js +47 -0
  63. package/dist-electron/services/local-ai-core/src/acp/store/workspace-registry-store.js +109 -0
  64. package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +136 -0
  65. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-repository.js +56 -0
  66. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +359 -0
  67. package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +77 -0
  68. package/dist-electron/services/local-ai-core/src/automation/mock-stock-provider.js +40 -0
  69. package/dist-electron/services/local-ai-core/src/automation/monitor-id.js +10 -0
  70. package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +55 -0
  71. package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -0
  72. package/dist-electron/services/local-ai-core/src/channel/lark/inbound.js +114 -0
  73. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +153 -94
  74. package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +53 -0
  75. package/dist-electron/services/local-ai-core/src/channel/shared/thread-routing.js +34 -0
  76. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +63 -51
  77. package/dist-electron/services/local-ai-core/src/cli/lac.js +196 -0
  78. package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +37 -0
  79. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +43 -0
  80. package/dist-electron/services/local-ai-core/src/kernel/capability-registry.js +12 -0
  81. package/dist-electron/services/local-ai-core/src/plugins/builtin/monitor-stock-plugin.js +30 -0
  82. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +41 -0
  83. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +31 -0
  84. package/dist-electron/services/local-ai-core/src/runtime/server.js +36 -0
  85. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-bridge-session.js +25 -11
  86. package/dist-electron/services/local-ai-core/src/thread/session-command-service.js +275 -0
  87. package/dist-electron/services/local-ai-core/src/thread/slash-command-registry.js +27 -0
  88. package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +15 -15
  89. package/dist-electron/shared/desktop.js +21 -1
  90. package/dist-electron/tests/contracts/local-core-contracts.test.js +9 -0
  91. package/dist-electron/tests/electron/automation-monitor.test.js +38 -0
  92. package/dist-electron/tests/electron/lac-cli.test.js +146 -0
  93. package/dist-electron/tests/integration/channel-gateways.test.js +32 -0
  94. package/dist-electron/tests/integration/lark-gateway.test.js +1918 -0
  95. package/dist-electron/tests/integration/local-core-acp-progress.test.js +1772 -0
  96. package/dist-electron/tests/integration/local-core-refactor.test.js +216 -4566
  97. package/dist-electron/tests/integration/local-core-routes.test.js +204 -0
  98. package/dist-electron/tests/integration/weixin-gateway.test.js +927 -0
  99. package/package.json +1 -1
  100. package/dist/renderer/assets/CronList-CKhggzYk.js +0 -1
  101. package/dist/renderer/assets/SessionChat-06Jny5_G.js +0 -1
  102. package/dist/renderer/assets/desktop-DXmXjjUR.js +0 -1
  103. package/dist/renderer/assets/desktop-O8_6ZCVZ.js +0 -4
  104. package/dist/renderer/assets/en-CKhCF6St.js +0 -1
  105. package/dist/renderer/assets/projects-xnW0fTtq.js +0 -1
  106. package/dist/renderer/assets/zh-Dz5FLTBV.js +0 -1
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildInteractiveCard = buildInteractiveCard;
4
4
  exports.renderPermissionCard = renderPermissionCard;
5
5
  exports.renderPendingPairingCard = renderPendingPairingCard;
6
+ exports.buildSessionCommandCard = buildSessionCommandCard;
6
7
  exports.extractCardActionMessageId = extractCardActionMessageId;
7
8
  exports.extractCardActionValue = extractCardActionValue;
9
+ exports.extractSessionCommandActionValue = extractSessionCommandActionValue;
8
10
  exports.formatPermissionButtonLabel = formatPermissionButtonLabel;
9
11
  exports.formatPermissionResponseLabel = formatPermissionResponseLabel;
10
12
  const desktop_js_1 = require("../../../../../shared/desktop.js");
@@ -80,6 +82,37 @@ function renderPendingPairingCard(code) {
80
82
  }
81
83
  return lines.join('\n\n');
82
84
  }
85
+ function buildSessionCommandCard(text, actionRows = [], sessionKey, threadId) {
86
+ const elements = [];
87
+ if (text) {
88
+ elements.push({ tag: 'markdown', content: text });
89
+ }
90
+ for (const row of actionRows) {
91
+ const actions = row
92
+ .filter((button) => button.label && button.command)
93
+ .map((button) => ({
94
+ tag: 'button',
95
+ text: {
96
+ tag: 'plain_text',
97
+ content: button.label,
98
+ },
99
+ type: button.type || 'default',
100
+ value: {
101
+ action: 'session_command',
102
+ command: button.command,
103
+ session_key: sessionKey || '',
104
+ thread_id: threadId || '',
105
+ },
106
+ }));
107
+ if (actions.length) {
108
+ elements.push({ tag: 'action', actions });
109
+ }
110
+ }
111
+ return {
112
+ config: { wide_screen_mode: true },
113
+ elements,
114
+ };
115
+ }
83
116
  function extractCardActionMessageId(...payloads) {
84
117
  for (const payload of payloads) {
85
118
  const messageId = extractKnownCardActionMessageId(payload);
@@ -117,6 +150,28 @@ function extractCardActionValue(payload) {
117
150
  sessionKey,
118
151
  };
119
152
  }
153
+ function extractSessionCommandActionValue(payload) {
154
+ const event = (payload?.event && typeof payload.event === 'object')
155
+ ? payload.event
156
+ : payload;
157
+ const value = event?.action?.value;
158
+ if (!value || value.action !== 'session_command') {
159
+ return null;
160
+ }
161
+ const command = String(value.command || '').trim();
162
+ const threadId = String(value.thread_id || '').trim();
163
+ const sessionKey = String(value.session_key || '').trim();
164
+ if (!command || !threadId) {
165
+ return null;
166
+ }
167
+ return {
168
+ event,
169
+ value: value,
170
+ command,
171
+ threadId,
172
+ sessionKey,
173
+ };
174
+ }
120
175
  function formatPermissionButtonLabel(button) {
121
176
  return formatPermissionResponseLabel(button.data || button.text);
122
177
  }
@@ -45,6 +45,8 @@ function collectLarkWorkspaceBindings(config, options) {
45
45
  || platform.options.card_actions === true
46
46
  || platform.options.enable_card_actions === true
47
47
  || options.defaultCardActionsEnabled,
48
+ groupReplyAll: String(platform.options.group_reply_all || '').trim().toLowerCase() === 'true'
49
+ || platform.options.group_reply_all === true,
48
50
  brand: String(platform.options.brand || platform.options.lark_brand || '').trim().toLowerCase() === 'lark' ? 'lark' : 'feishu',
49
51
  enabled: Boolean(String(platform.options.app_id || '').trim() && String(platform.options.app_secret || '').trim()),
50
52
  project,
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeLarkInboundMessageEvent = normalizeLarkInboundMessageEvent;
4
+ exports.isLarkBotMentioned = isLarkBotMentioned;
5
+ exports.stripLarkMentions = stripLarkMentions;
6
+ exports.summarizeLarkInboundPayload = summarizeLarkInboundPayload;
7
+ const node_crypto_1 = require("node:crypto");
8
+ function normalizeLarkInboundMessageEvent(data, options = {}) {
9
+ const payload = (data?.event && typeof data.event === 'object')
10
+ ? data.event
11
+ : data;
12
+ const message = payload?.message;
13
+ const sender = payload?.sender;
14
+ if (!message || !sender) {
15
+ return { ok: false, reason: 'missing-message-or-sender', detail: JSON.stringify(Object.keys(data || {})) };
16
+ }
17
+ let parsedContent = {};
18
+ try {
19
+ parsedContent = JSON.parse(String(message.content || '{}'));
20
+ }
21
+ catch {
22
+ parsedContent = {};
23
+ }
24
+ const messageType = String(message.message_type || '').trim().toLowerCase();
25
+ const chatType = String(message.chat_type || '').trim().toLowerCase();
26
+ const mentions = Array.isArray(message.mentions) ? message.mentions : [];
27
+ const botOpenId = String(options.botOpenId || '').trim();
28
+ if (chatType === 'group' && !options.groupReplyAll && botOpenId && !isLarkBotMentioned(mentions, botOpenId)) {
29
+ return {
30
+ ok: false,
31
+ reason: 'group-message-without-bot-mention',
32
+ detail: `chat=${String(message.chat_id || '')} message=${String(message.message_id || '')}`,
33
+ };
34
+ }
35
+ const text = stripLarkMentions(String(parsedContent.text || '').trim(), mentions, botOpenId);
36
+ const platformUserId = String(sender.sender_id?.user_id || sender.sender_id?.open_id || '').trim();
37
+ const chatId = String(message.chat_id || platformUserId).trim();
38
+ if (!platformUserId || !chatId) {
39
+ return {
40
+ ok: false,
41
+ reason: 'missing-sender-or-chat',
42
+ detail: `senderKeys=${JSON.stringify(Object.keys(sender?.sender_id || {}))} chat=${String(message.chat_id || '')}`,
43
+ };
44
+ }
45
+ return {
46
+ ok: true,
47
+ message: {
48
+ payload,
49
+ message,
50
+ sender,
51
+ parsedContent,
52
+ messageType,
53
+ chatType,
54
+ mentions,
55
+ text,
56
+ platformUserId,
57
+ chatId,
58
+ displayName: String(sender.sender_id?.user_id ||
59
+ sender.sender_id?.open_id ||
60
+ `Lark ${platformUserId.slice(-6)}`),
61
+ messageId: String(message.message_id || (0, node_crypto_1.randomUUID)()),
62
+ },
63
+ };
64
+ }
65
+ function isLarkBotMentioned(mentions, botOpenId) {
66
+ if (!botOpenId) {
67
+ return false;
68
+ }
69
+ return mentions.some((mention) => {
70
+ const item = mention && typeof mention === 'object' ? mention : {};
71
+ return getMentionOpenId(item) === botOpenId;
72
+ });
73
+ }
74
+ function stripLarkMentions(text, mentions, botOpenId) {
75
+ let next = String(text || '');
76
+ for (const mention of mentions) {
77
+ const item = mention && typeof mention === 'object' ? mention : {};
78
+ const key = String(item.key || '').trim();
79
+ if (!key) {
80
+ continue;
81
+ }
82
+ const openId = getMentionOpenId(item);
83
+ const name = String(item.name || item.display_name || item.displayName || '').trim();
84
+ if (botOpenId && openId === botOpenId) {
85
+ next = next.split(key).join('');
86
+ continue;
87
+ }
88
+ next = next.split(key).join(name ? `@${name}` : '');
89
+ }
90
+ return next.trim();
91
+ }
92
+ function summarizeLarkInboundPayload(payload) {
93
+ const eventPayload = (payload?.event && typeof payload.event === 'object')
94
+ ? payload.event
95
+ : payload;
96
+ const message = eventPayload?.message || {};
97
+ const sender = eventPayload?.sender || {};
98
+ const senderId = sender?.sender_id || {};
99
+ const content = typeof message.content === 'string' ? message.content : '';
100
+ return [
101
+ `message=${String(message.message_id || '') || 'missing'}`,
102
+ `type=${String(message.message_type || '') || 'missing'}`,
103
+ `chat=${String(message.chat_id || '') || 'missing'}`,
104
+ `chatType=${String(message.chat_type || '') || 'missing'}`,
105
+ `sender=${String(senderId.user_id || senderId.open_id || '') || 'missing'}`,
106
+ `mentions=${Array.isArray(message.mentions) ? message.mentions.length : 0}`,
107
+ `contentBytes=${Buffer.byteLength(content, 'utf8')}`,
108
+ `keys=${JSON.stringify(Object.keys(eventPayload || {}))}`,
109
+ ].join(' ');
110
+ }
111
+ function getMentionOpenId(item) {
112
+ const id = item.id || item.user_id || item.userId || {};
113
+ return String(id.open_id || id.openId || item.open_id || item.openId || '').trim();
114
+ }
@@ -41,11 +41,15 @@ const node_path_1 = require("node:path");
41
41
  const desktop_js_1 = require("../../../../../shared/desktop.js");
42
42
  const content_js_1 = require("../shared/content.js");
43
43
  const file_utils_js_1 = require("../shared/file-utils.js");
44
+ const session_command_runtime_js_1 = require("../shared/session-command-runtime.js");
45
+ const thread_routing_js_1 = require("../shared/thread-routing.js");
44
46
  const cards_js_1 = require("./cards.js");
45
47
  const config_js_1 = require("./config.js");
46
48
  const registration_js_1 = require("./registration.js");
47
49
  const messages_js_1 = require("./rendering/messages.js");
50
+ const inbound_js_1 = require("./inbound.js");
48
51
  const runtime_state_js_1 = require("./runtime-state.js");
52
+ const session_command_service_js_1 = require("../../thread/session-command-service.js");
49
53
  const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
50
54
  const LARK_MAX_UPLOAD_FILE_SIZE = 30 * 1024 * 1024;
51
55
  const LARK_FINAL_PATCH_INTERVAL_MS = 900;
@@ -62,12 +66,37 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
62
66
  outboundEventChains = new Map();
63
67
  outboundTurns = new Map();
64
68
  mutedThreadBridgeCounts = new Map();
69
+ sessionCommandRuntime;
65
70
  larkModulePromise = null;
66
71
  platform = 'lark';
67
72
  routeType = 'channel.chat';
68
73
  constructor(options) {
69
74
  super();
70
75
  this.options = options;
76
+ const sessionCommandService = new session_command_service_js_1.SessionCommandService({
77
+ listThreads: (workspaceId) => this.options.getWorkspaceRouter().listThreads(workspaceId),
78
+ getThread: (threadId) => this.options.getWorkspaceRouter().getThread(threadId),
79
+ createThread: (workspaceId, title) => this.options.getWorkspaceRouter().createThread(workspaceId, title),
80
+ renameThread: (threadId, title) => this.options.getWorkspaceRouter().renameThread(threadId, title),
81
+ deleteThread: (threadId) => this.options.getWorkspaceRouter().deleteThread(threadId),
82
+ });
83
+ this.sessionCommandRuntime = new session_command_runtime_js_1.ChannelSessionCommandRuntime({
84
+ service: sessionCommandService,
85
+ store: this.options.store,
86
+ getThreadSessionKey: (threadId) => this.options.getWorkspaceRouter().getThreadSessionKey(threadId),
87
+ setThreadRoute: (sessionKey, route) => {
88
+ this.threadRouting.set(sessionKey, route);
89
+ },
90
+ createRoute: (input, threadId) => ({
91
+ workspaceId: input.workspaceId,
92
+ instanceId: input.instanceId,
93
+ platformKey: input.platformKey,
94
+ platformUserId: input.platformUserId,
95
+ chatId: input.chatId,
96
+ threadId,
97
+ }),
98
+ sendResult: (input, result) => this.sendSessionCommandResult(input, result),
99
+ });
71
100
  }
72
101
  async refreshBindings() {
73
102
  const config = await this.options.readConfig();
@@ -97,6 +126,8 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
97
126
  appId: binding.appId,
98
127
  autoApprove: binding.autoApprove,
99
128
  cardActionsEnabled: binding.cardActionsEnabled,
129
+ groupReplyAll: binding.groupReplyAll,
130
+ botOpenId: binding.botOpenId,
100
131
  });
101
132
  }
102
133
  continue;
@@ -204,6 +235,17 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
204
235
  }
205
236
  };
206
237
  }
238
+ async executeSessionCommand(input) {
239
+ return this.sessionCommandRuntime.execute(input);
240
+ }
241
+ async sendSessionCommandResult(input, result) {
242
+ const state = this.resolveRuntimeState(input.workspaceId, input.instanceId).state;
243
+ if (state?.client && state.connected && result.card?.actions?.length && state.cardActionsEnabled) {
244
+ await this.sendSessionCommandCard(state, input.chatId, result.displayText, result.card.actions, this.options.getWorkspaceRouter().getThreadSessionKey(input.currentThreadId), input.currentThreadId);
245
+ return;
246
+ }
247
+ await this.sendImmediateCard(input.workspaceId, input.chatId, result.displayText, input.instanceId);
248
+ }
207
249
  async sendOutboundMessage(workspaceId, input) {
208
250
  const state = this.resolveRuntimeState(workspaceId, input.route?.instanceId).state;
209
251
  if (!state?.client || !state.connected) {
@@ -621,25 +663,17 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
621
663
  return { paired: false };
622
664
  }
623
665
  const router = this.options.getWorkspaceRouter();
624
- const threadBinding = this.options.store.getPlatformThreadBinding(input.workspaceId, input.chatId, input.platformUserId, platformKey);
625
- let threadId = threadBinding?.thread_id || authorized.thread_id || '';
626
- if (!threadId) {
627
- const thread = await router.createThread(input.workspaceId, input.displayName || `Lark ${input.chatId}`);
628
- threadId = thread.id;
629
- this.options.store.updateAuthorizedUserThread(input.workspaceId, input.platformUserId, threadId, platformKey);
630
- const now = new Date().toISOString();
631
- this.options.store.upsertPlatformThreadBinding({
632
- workspace_id: input.workspaceId,
633
- platform: platformKey,
634
- chat_id: input.chatId,
635
- platform_user_id: input.platformUserId,
636
- thread_id: threadId,
637
- last_platform_message_id: null,
638
- created_at: now,
639
- updated_at: now,
640
- });
641
- }
642
- const sessionKey = this.options.getWorkspaceRouter().getThreadSessionKey(threadId);
666
+ let { threadId } = await (0, thread_routing_js_1.resolveChannelThreadRoute)({
667
+ store: this.options.store,
668
+ router,
669
+ workspaceId: input.workspaceId,
670
+ platformKey,
671
+ chatId: input.chatId,
672
+ platformUserId: input.platformUserId,
673
+ displayName: input.displayName,
674
+ fallbackTitlePrefix: 'Lark',
675
+ authorized,
676
+ });
643
677
  const normalizedText = String(input.text || '').trim().toLowerCase();
644
678
  const permissionThreadId = (normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny')
645
679
  ? this.findAwaitingPermissionThreadId(input.workspaceId, input.chatId, input.platformUserId)
@@ -659,35 +693,18 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
659
693
  const acknowledgement = this.createTurnState(effectiveSessionKey, input.messageId);
660
694
  await this.addAcknowledgementReaction(input.workspaceId, input.messageId, acknowledgement, instanceId);
661
695
  const slashCommand = this.parseSlashCommand(input.text);
662
- if (slashCommand?.name === 'new') {
663
- const title = slashCommand.args.join(' ').trim() || `${input.displayName || 'Lark'} ${new Date().toLocaleTimeString()}`;
664
- const nextThread = await router.createThread(input.workspaceId, title);
665
- const inheritedMode = this.options.store.getThreadRow?.(threadId)?.agent_mode || '';
666
- if (inheritedMode && inheritedMode !== 'default') {
667
- this.options.store.updateThreadAgentMode?.(nextThread.id, inheritedMode);
668
- }
669
- const now = new Date().toISOString();
670
- this.options.store.updateAuthorizedUserThread(input.workspaceId, input.platformUserId, nextThread.id, platformKey);
671
- this.options.store.upsertPlatformThreadBinding({
672
- workspace_id: input.workspaceId,
673
- platform: platformKey,
674
- chat_id: input.chatId,
675
- platform_user_id: input.platformUserId,
676
- thread_id: nextThread.id,
677
- last_platform_message_id: null,
678
- created_at: now,
679
- updated_at: now,
680
- });
681
- this.threadRouting.set(this.options.getWorkspaceRouter().getThreadSessionKey(nextThread.id), {
682
- workspaceId: input.workspaceId,
683
- instanceId,
684
- platformKey,
685
- platformUserId: input.platformUserId,
686
- chatId: input.chatId,
687
- threadId: nextThread.id,
688
- });
689
- await this.sendImmediateCard(input.workspaceId, input.chatId, '**已开始新会话**', instanceId);
690
- return { paired: true, threadId: nextThread.id };
696
+ const sessionCommand = await this.executeSessionCommand({
697
+ workspaceId: input.workspaceId,
698
+ currentThreadId: threadId,
699
+ text: input.text,
700
+ defaultTitle: `${input.displayName || 'Lark'} ${new Date().toLocaleTimeString()}`,
701
+ chatId: input.chatId,
702
+ platformUserId: input.platformUserId,
703
+ platformKey,
704
+ instanceId,
705
+ });
706
+ if (sessionCommand.handled) {
707
+ return { paired: true, threadId: sessionCommand.threadId || threadId };
691
708
  }
692
709
  const latestRun = this.options.store.getLatestRunForThread(threadId);
693
710
  if ((normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny')
@@ -768,6 +785,8 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
768
785
  appId: binding.appId,
769
786
  autoApprove: binding.autoApprove,
770
787
  cardActionsEnabled: binding.cardActionsEnabled,
788
+ groupReplyAll: binding.groupReplyAll,
789
+ botOpenId: binding.botOpenId,
771
790
  };
772
791
  this.runtime.set(key, status);
773
792
  this.notifyRuntimeStateChanged();
@@ -780,6 +799,14 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
780
799
  appType: mod.AppType.SelfBuild,
781
800
  domain: mod.Domain.Feishu,
782
801
  });
802
+ try {
803
+ status.botOpenId = await this.fetchBotOpenId(status);
804
+ binding.botOpenId = status.botOpenId;
805
+ this.options.log?.(`localcore-lark bot identified for ${binding.workspaceId}/${binding.instanceId}: ${status.botOpenId}`);
806
+ }
807
+ catch (error) {
808
+ this.options.log?.(`localcore-lark bot identity lookup failed for ${binding.workspaceId}/${binding.instanceId}; group mention filtering may be limited: ${error instanceof Error ? error.message : String(error)}`);
809
+ }
783
810
  status.eventDispatcher = new mod.EventDispatcher({
784
811
  encryptKey: binding.encryptKey || '',
785
812
  verificationToken: binding.verificationToken || '',
@@ -854,6 +881,8 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
854
881
  appId: state.appId,
855
882
  autoApprove: state.autoApprove,
856
883
  cardActionsEnabled: state.cardActionsEnabled,
884
+ groupReplyAll: state.groupReplyAll,
885
+ botOpenId: state.botOpenId,
857
886
  });
858
887
  this.notifyRuntimeStateChanged();
859
888
  }
@@ -870,25 +899,17 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
870
899
  const instanceId = legacyCall ? 'default' : instanceIdOrData;
871
900
  const platformKey = legacyCall ? 'lark' : String(platformKeyOrData || (0, config_js_1.channelPlatformKey)('lark', instanceId));
872
901
  const data = (legacyCall ? instanceIdOrData : maybeData) || {};
873
- const payload = (data?.event && typeof data.event === 'object')
874
- ? data.event
875
- : data;
876
- const message = payload?.message;
877
- const sender = payload?.sender;
878
- this.options.log?.(`localcore-lark handling message event for ${workspaceId}: ${this.summarizeLarkPayload(payload)}`);
879
- if (!message || !sender) {
880
- this.options.log?.(`localcore-lark ignored event without message/sender for ${workspaceId}: ${JSON.stringify(Object.keys(data || {}))}`);
902
+ const runtimeState = this.resolveRuntimeState(workspaceId, instanceId).state;
903
+ const normalized = (0, inbound_js_1.normalizeLarkInboundMessageEvent)(data, {
904
+ botOpenId: runtimeState?.botOpenId,
905
+ groupReplyAll: runtimeState?.groupReplyAll,
906
+ });
907
+ this.options.log?.(`localcore-lark handling message event for ${workspaceId}: ${(0, inbound_js_1.summarizeLarkInboundPayload)(data)}`);
908
+ if (!normalized.ok) {
909
+ this.options.log?.(`localcore-lark ignored message event for ${workspaceId}: reason=${normalized.reason}${normalized.detail ? ` ${normalized.detail}` : ''}`);
881
910
  return;
882
911
  }
883
- let parsedContent = {};
884
- try {
885
- parsedContent = JSON.parse(String(message.content || '{}'));
886
- }
887
- catch {
888
- parsedContent = {};
889
- }
890
- const messageType = String(message.message_type || '').trim().toLowerCase();
891
- const text = String(parsedContent.text || '').trim();
912
+ const { message, parsedContent, messageType, chatType, mentions, text, platformUserId, chatId, displayName, messageId, } = normalized.message;
892
913
  const contentParts = [];
893
914
  if (text) {
894
915
  contentParts.push({ type: 'text', text });
@@ -924,24 +945,16 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
924
945
  return;
925
946
  }
926
947
  const displayText = text || this.summarizeInboundContentParts(contentParts);
927
- const platformUserId = String(sender.sender_id?.user_id || sender.sender_id?.open_id || '').trim();
928
- const chatId = String(message.chat_id || platformUserId).trim();
929
- if (!platformUserId || !chatId) {
930
- this.options.log?.(`localcore-lark ignored message without sender/chat for ${workspaceId}: senderKeys=${JSON.stringify(Object.keys(sender?.sender_id || {}))} chat=${String(message.chat_id || '')}`);
931
- return;
932
- }
933
- this.options.log?.(`localcore-lark inbound message for ${workspaceId}: chat=${chatId} user=${platformUserId} type=${messageType || 'unknown'} text=${JSON.stringify(displayText.slice(0, 120))}`);
948
+ this.options.log?.(`localcore-lark inbound message for ${workspaceId}: chat=${chatId} user=${platformUserId} chatType=${chatType || 'unknown'} mentions=${mentions.length} type=${messageType || 'unknown'} text=${JSON.stringify(displayText.slice(0, 120))}`);
934
949
  await this.handleInboundMessage({
935
950
  workspaceId,
936
951
  instanceId,
937
952
  platformKey,
938
953
  platformUserId,
939
954
  chatId,
940
- displayName: String(payload?.sender?.sender_id?.user_id ||
941
- payload?.sender?.sender_id?.open_id ||
942
- `Lark ${platformUserId.slice(-6)}`),
955
+ displayName,
943
956
  text: displayText,
944
- messageId: String(message.message_id || (0, node_crypto_1.randomUUID)()),
957
+ messageId,
945
958
  contentParts,
946
959
  });
947
960
  }
@@ -1088,24 +1101,19 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
1088
1101
  }
1089
1102
  }
1090
1103
  summarizeLarkEvent(data) {
1091
- const payload = (data?.event && typeof data.event === 'object')
1092
- ? data.event
1093
- : data;
1094
- return this.summarizeLarkPayload(payload);
1095
- }
1096
- summarizeLarkPayload(payload) {
1097
- const message = payload?.message || {};
1098
- const sender = payload?.sender || {};
1099
- const senderId = sender?.sender_id || {};
1100
- const content = typeof message.content === 'string' ? message.content : '';
1101
- return [
1102
- `message=${String(message.message_id || '') || 'missing'}`,
1103
- `type=${String(message.message_type || '') || 'missing'}`,
1104
- `chat=${String(message.chat_id || '') || 'missing'}`,
1105
- `sender=${String(senderId.user_id || senderId.open_id || '') || 'missing'}`,
1106
- `contentBytes=${Buffer.byteLength(content, 'utf8')}`,
1107
- `keys=${JSON.stringify(Object.keys(payload || {}))}`,
1108
- ].join(' ');
1104
+ return (0, inbound_js_1.summarizeLarkInboundPayload)(data);
1105
+ }
1106
+ async fetchBotOpenId(state) {
1107
+ const response = await state.client.request({
1108
+ url: '/open-apis/bot/v3/info',
1109
+ method: 'GET',
1110
+ });
1111
+ const bot = response?.bot || response?.data?.bot;
1112
+ const openId = String(bot?.open_id || bot?.openId || '').trim();
1113
+ if (!openId) {
1114
+ throw new Error('Lark bot info response did not include bot.open_id');
1115
+ }
1116
+ return openId;
1109
1117
  }
1110
1118
  summarizeWsArgs(args) {
1111
1119
  if (args.length === 0) {
@@ -1157,6 +1165,21 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
1157
1165
  this.options.log?.(`localcore-lark card create took ${Date.now() - startedAt}ms textBytes=${Buffer.byteLength(text || '', 'utf8')}`);
1158
1166
  return String(response?.data?.message_id || '').trim();
1159
1167
  }
1168
+ async sendSessionCommandCard(state, chatId, text, actionRows, sessionKey, threadId) {
1169
+ const startedAt = Date.now();
1170
+ const response = await state.client.im.message.create({
1171
+ params: {
1172
+ receive_id_type: this.resolveReceiveIdType(chatId),
1173
+ },
1174
+ data: {
1175
+ receive_id: chatId,
1176
+ msg_type: 'interactive',
1177
+ content: JSON.stringify((0, cards_js_1.buildSessionCommandCard)(text, actionRows, sessionKey, threadId)),
1178
+ },
1179
+ });
1180
+ this.options.log?.(`localcore-lark session card create took ${Date.now() - startedAt}ms textBytes=${Buffer.byteLength(text || '', 'utf8')}`);
1181
+ return String(response?.data?.message_id || '').trim();
1182
+ }
1160
1183
  async sendTextAsMessage(state, chatId, text) {
1161
1184
  const startedAt = Date.now();
1162
1185
  const rendered = (0, messages_js_1.renderLarkTextMessage)(text);
@@ -1272,6 +1295,27 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
1272
1295
  const instanceId = legacyCall ? 'default' : instanceIdOrData;
1273
1296
  const data = (legacyCall ? instanceIdOrData : maybeData) || {};
1274
1297
  try {
1298
+ const sessionAction = (0, cards_js_1.extractSessionCommandActionValue)(data);
1299
+ if (sessionAction) {
1300
+ const route = this.threadRouting.get(sessionAction.sessionKey)
1301
+ || this.routeFromThreadBinding(sessionAction.threadId, instanceId);
1302
+ if (!route) {
1303
+ this.options.log?.(`localcore-lark session card action ignored without route for thread=${sessionAction.threadId}`);
1304
+ return;
1305
+ }
1306
+ await this.executeSessionCommand({
1307
+ workspaceId,
1308
+ currentThreadId: sessionAction.threadId,
1309
+ text: sessionAction.command,
1310
+ defaultTitle: `Lark ${new Date().toLocaleTimeString()}`,
1311
+ chatId: route.chatId,
1312
+ platformUserId: route.platformUserId,
1313
+ platformKey: route.platformKey,
1314
+ instanceId: route.instanceId,
1315
+ });
1316
+ this.options.log?.(`localcore-lark processed session card action for ${workspaceId}/${instanceId}: ${sessionAction.command}`);
1317
+ return;
1318
+ }
1275
1319
  const action = (0, cards_js_1.extractCardActionValue)(data);
1276
1320
  if (!action) {
1277
1321
  return;
@@ -1285,6 +1329,21 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
1285
1329
  this.options.log?.(`localcore-lark card action failed for ${workspaceId}/${instanceId}: ${error instanceof Error ? error.message : String(error)}`);
1286
1330
  }
1287
1331
  }
1332
+ routeFromThreadBinding(threadId, instanceId) {
1333
+ const binding = this.options.store.getPlatformThreadBindingByThreadId(threadId);
1334
+ if (!binding || (binding.platform !== 'lark' && !binding.platform.startsWith('lark:'))) {
1335
+ return undefined;
1336
+ }
1337
+ const bindingInstanceId = getLarkInstanceId(binding.platform) || instanceId || 'default';
1338
+ return {
1339
+ workspaceId: binding.workspace_id,
1340
+ instanceId: bindingInstanceId,
1341
+ platformKey: binding.platform,
1342
+ platformUserId: binding.platform_user_id,
1343
+ chatId: binding.chat_id,
1344
+ threadId: binding.thread_id,
1345
+ };
1346
+ }
1288
1347
  async markPermissionCardActionHandled(workspaceId, instanceId, rootPayload, payload, value, response, threadId) {
1289
1348
  const state = this.resolveRuntimeState(workspaceId, instanceId).state;
1290
1349
  if (!state?.client) {
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChannelSessionCommandRuntime = void 0;
4
+ class ChannelSessionCommandRuntime {
5
+ options;
6
+ constructor(options) {
7
+ this.options = options;
8
+ }
9
+ async execute(input) {
10
+ const result = await this.options.service.execute(input.text, {
11
+ workspaceId: input.workspaceId,
12
+ currentThreadId: input.currentThreadId,
13
+ defaultTitle: input.defaultTitle,
14
+ });
15
+ if (!result.handled) {
16
+ return { handled: false, threadId: input.currentThreadId };
17
+ }
18
+ let activeThreadId = input.currentThreadId;
19
+ for (const effect of result.effects || []) {
20
+ if (effect.type === 'created_thread') {
21
+ this.inheritAgentMode(input.currentThreadId, effect.threadId);
22
+ }
23
+ if (effect.type === 'activate_thread') {
24
+ activeThreadId = effect.threadId;
25
+ this.activateThread(input, effect.threadId);
26
+ }
27
+ }
28
+ await this.options.sendResult(input, result);
29
+ return { handled: true, threadId: activeThreadId, result };
30
+ }
31
+ inheritAgentMode(sourceThreadId, targetThreadId) {
32
+ const inheritedMode = this.options.store.getThreadRow?.(sourceThreadId)?.agent_mode || '';
33
+ if (inheritedMode && inheritedMode !== 'default') {
34
+ this.options.store.updateThreadAgentMode?.(targetThreadId, inheritedMode);
35
+ }
36
+ }
37
+ activateThread(input, threadId) {
38
+ const now = new Date().toISOString();
39
+ this.options.store.updateAuthorizedUserThread(input.workspaceId, input.platformUserId, threadId, input.platformKey);
40
+ this.options.store.upsertPlatformThreadBinding({
41
+ workspace_id: input.workspaceId,
42
+ platform: input.platformKey,
43
+ chat_id: input.chatId,
44
+ platform_user_id: input.platformUserId,
45
+ thread_id: threadId,
46
+ last_platform_message_id: null,
47
+ created_at: now,
48
+ updated_at: now,
49
+ });
50
+ this.options.setThreadRoute(this.options.getThreadSessionKey(threadId), this.options.createRoute(input, threadId));
51
+ }
52
+ }
53
+ exports.ChannelSessionCommandRuntime = ChannelSessionCommandRuntime;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveChannelThreadRoute = resolveChannelThreadRoute;
4
+ async function resolveChannelThreadRoute(input) {
5
+ const threadBinding = input.store.getPlatformThreadBinding(input.workspaceId, input.chatId, input.platformUserId, input.platformKey);
6
+ let threadId = threadBinding?.thread_id || (input.authorized.chat_id === input.chatId ? input.authorized.thread_id : '') || '';
7
+ let createdThread = false;
8
+ if (!threadId) {
9
+ const thread = await input.router.createThread(input.workspaceId, input.displayName || `${input.fallbackTitlePrefix} ${input.chatId}`);
10
+ threadId = thread.id;
11
+ createdThread = true;
12
+ input.store.updateAuthorizedUserThread(input.workspaceId, input.platformUserId, threadId, input.platformKey);
13
+ }
14
+ let createdBinding = false;
15
+ if (!threadBinding) {
16
+ const now = new Date().toISOString();
17
+ input.store.upsertPlatformThreadBinding({
18
+ workspace_id: input.workspaceId,
19
+ platform: input.platformKey,
20
+ chat_id: input.chatId,
21
+ platform_user_id: input.platformUserId,
22
+ thread_id: threadId,
23
+ last_platform_message_id: null,
24
+ created_at: now,
25
+ updated_at: now,
26
+ });
27
+ createdBinding = true;
28
+ }
29
+ return {
30
+ threadId,
31
+ createdThread,
32
+ createdBinding,
33
+ };
34
+ }