@kafca/agentdock 0.1.41 → 0.1.43

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 (102) hide show
  1. package/README.md +10 -0
  2. package/dist/renderer/assets/{Badge-BX3RC8l6.js → Badge-CbG6yRSm.js} +1 -1
  3. package/dist/renderer/assets/{Chat-DJwIciel.js → Chat-BvNNWGGX.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-DfGbRR0S.js → ChatMarkdown-Ct-PvJB8.js} +1 -1
  5. package/dist/renderer/assets/{Config-2x2RDL3j.js → Config-lQX2qVVV.js} +2 -2
  6. package/dist/renderer/assets/CronList-Dj_3muVG.js +1 -0
  7. package/dist/renderer/assets/{Dashboard-2Wcx_bvJ.js → Dashboard-eh_RhGYb.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-DoCpF2tn.js → EmptyState-Bn2Qh14U.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-CBRu2bXX.js → KnowledgeDetail-Bsz6Ou41.js} +2 -2
  10. package/dist/renderer/assets/{KnowledgeHome-CItdlDVR.js → KnowledgeHome-Cgj4XFXn.js} +2 -2
  11. package/dist/renderer/assets/{Logs-Cd_7ZOXc.js → Logs-Dp-NF6GN.js} +1 -1
  12. package/dist/renderer/assets/{Modal-B4eOiBsB.js → Modal-B6YPPBN6.js} +1 -1
  13. package/dist/renderer/assets/MonitorList-xEZWg460.js +1 -0
  14. package/dist/renderer/assets/{Page-C5VH4D9U.js → Page-Bn7RjGOS.js} +1 -1
  15. package/dist/renderer/assets/{ProjectDetail-D_DA--IP.js → ProjectDetail-Cv_jIE61.js} +2 -2
  16. package/dist/renderer/assets/{ProjectList-CtCXJ50u.js → ProjectList-BDk5_gqY.js} +1 -1
  17. package/dist/renderer/assets/{Select-B6E68LcN.js → Select-xshfMIEQ.js} +1 -1
  18. package/dist/renderer/assets/SessionChat-BkHy90z3.js +1 -0
  19. package/dist/renderer/assets/{SessionList-Do-5-Xx6.js → SessionList-BZyXT92L.js} +1 -1
  20. package/dist/renderer/assets/{ThreadChat-B1tEtE3V.js → ThreadChat-DUOIyXrS.js} +7 -7
  21. package/dist/renderer/assets/{Workspace-BlhS2RFw.js → Workspace-Dl80fNEC.js} +1 -1
  22. package/dist/renderer/assets/{arrow-left-DF1_F9Pk.js → arrow-left-C54ZI-AS.js} +1 -1
  23. package/dist/renderer/assets/{book-open-DxRTGpWO.js → book-open-Dl7GO0zn.js} +1 -1
  24. package/dist/renderer/assets/{check-BAqPPYkf.js → check-CiqgtrOL.js} +1 -1
  25. package/dist/renderer/assets/{chevron-down-DIZZgSb3.js → chevron-down-DTL2OuVh.js} +1 -1
  26. package/dist/renderer/assets/{circle-B5raL_XK.js → circle-DaVKfa3P.js} +1 -1
  27. package/dist/renderer/assets/desktop-C6q6wBae.js +5 -0
  28. package/dist/renderer/assets/desktop-jaAiayNB.js +1 -0
  29. package/dist/renderer/assets/en-D2SkCrhG.js +1 -0
  30. package/dist/renderer/assets/{funnel-BPjLpVSB.js → funnel-CrmnLMJI.js} +1 -1
  31. package/dist/renderer/assets/{heart-D2M1xN1N.js → heart-BMfQI7y_.js} +1 -1
  32. package/dist/renderer/assets/{index-Cx71F_Lz.js → index-HPNb1zfx.js} +39 -34
  33. package/dist/renderer/assets/{index-BijJAYmo.js → index-nreiKY1f.js} +3 -3
  34. package/dist/renderer/assets/{pencil-DlDr8Mmp.js → pencil-wTQ4l465.js} +1 -1
  35. package/dist/renderer/assets/{play-BOEG_mY8.js → play-BK--MXmw.js} +1 -1
  36. package/dist/renderer/assets/{plug-BI27elC-.js → plug-BIx9fjip.js} +1 -1
  37. package/dist/renderer/assets/{plus-BhURuFIQ.js → plus-Dn7JFWQ8.js} +1 -1
  38. package/dist/renderer/assets/projects-brJZgwEJ.js +1 -0
  39. package/dist/renderer/assets/{save-RwkHNm9e.js → save-CC3zOSWb.js} +1 -1
  40. package/dist/renderer/assets/{search-DMNtm2WP.js → search-CDTy9Czu.js} +1 -1
  41. package/dist/renderer/assets/{server-CNaxKyAq.js → server-DtQk3t2a.js} +1 -1
  42. package/dist/renderer/assets/{session-utils-vsALZkZN.js → session-utils-4ep5D66T.js} +1 -1
  43. package/dist/renderer/assets/{sessions-BY-R1IL_.js → sessions-Dor1LteH.js} +1 -1
  44. package/dist/renderer/assets/{shield-check-BF-4oJbj.js → shield-check-Cz2LpU6r.js} +1 -1
  45. package/dist/renderer/assets/{trash-2-DduflM4q.js → trash-2-C2EmWFZV.js} +1 -1
  46. package/dist/renderer/assets/{user-DBcvnuW4.js → user-I8B-cs5A.js} +1 -1
  47. package/dist/renderer/assets/{wifi-off-X1nXESYu.js → wifi-off-CPevTQJn.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 +76 -39
  53. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +13 -0
  54. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +4 -1357
  55. package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +28 -0
  56. package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +166 -0
  57. package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +231 -0
  58. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +263 -0
  59. package/dist-electron/services/local-ai-core/src/acp/store/platform-store.js +186 -0
  60. package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +201 -0
  61. package/dist-electron/services/local-ai-core/src/acp/store/schema.js +275 -0
  62. package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +266 -0
  63. package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +232 -0
  64. package/dist-electron/services/local-ai-core/src/acp/store/utils.js +47 -0
  65. package/dist-electron/services/local-ai-core/src/acp/store/workspace-registry-store.js +109 -0
  66. package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +136 -0
  67. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-repository.js +56 -0
  68. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +359 -0
  69. package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +77 -0
  70. package/dist-electron/services/local-ai-core/src/automation/mock-stock-provider.js +40 -0
  71. package/dist-electron/services/local-ai-core/src/automation/monitor-id.js +10 -0
  72. package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +55 -0
  73. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +101 -29
  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/weixin/local-core-weixin-gateway.js +51 -33
  76. package/dist-electron/services/local-ai-core/src/cli/lac.js +196 -0
  77. package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +37 -0
  78. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +43 -0
  79. package/dist-electron/services/local-ai-core/src/kernel/capability-registry.js +12 -0
  80. package/dist-electron/services/local-ai-core/src/plugins/builtin/monitor-stock-plugin.js +30 -0
  81. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +40 -4
  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 +134 -0
  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/electron/plugin-kernel.test.js +26 -0
  94. package/dist-electron/tests/integration/local-core-refactor.test.js +359 -0
  95. package/package.json +1 -1
  96. package/dist/renderer/assets/CronList-C6nPAmEo.js +0 -1
  97. package/dist/renderer/assets/SessionChat-D1HtMdQS.js +0 -1
  98. package/dist/renderer/assets/desktop-CFcnzwcY.js +0 -1
  99. package/dist/renderer/assets/desktop-O8_6ZCVZ.js +0 -4
  100. package/dist/renderer/assets/en-CKhCF6St.js +0 -1
  101. package/dist/renderer/assets/projects-DpR972HM.js +0 -1
  102. package/dist/renderer/assets/zh-Dz5FLTBV.js +0 -1
@@ -41,11 +41,13 @@ 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");
44
45
  const cards_js_1 = require("./cards.js");
45
46
  const config_js_1 = require("./config.js");
46
47
  const registration_js_1 = require("./registration.js");
47
48
  const messages_js_1 = require("./rendering/messages.js");
48
49
  const runtime_state_js_1 = require("./runtime-state.js");
50
+ const session_command_service_js_1 = require("../../thread/session-command-service.js");
49
51
  const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
50
52
  const LARK_MAX_UPLOAD_FILE_SIZE = 30 * 1024 * 1024;
51
53
  const LARK_FINAL_PATCH_INTERVAL_MS = 900;
@@ -62,12 +64,37 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
62
64
  outboundEventChains = new Map();
63
65
  outboundTurns = new Map();
64
66
  mutedThreadBridgeCounts = new Map();
67
+ sessionCommandRuntime;
65
68
  larkModulePromise = null;
66
69
  platform = 'lark';
67
70
  routeType = 'channel.chat';
68
71
  constructor(options) {
69
72
  super();
70
73
  this.options = options;
74
+ const sessionCommandService = new session_command_service_js_1.SessionCommandService({
75
+ listThreads: (workspaceId) => this.options.getWorkspaceRouter().listThreads(workspaceId),
76
+ getThread: (threadId) => this.options.getWorkspaceRouter().getThread(threadId),
77
+ createThread: (workspaceId, title) => this.options.getWorkspaceRouter().createThread(workspaceId, title),
78
+ renameThread: (threadId, title) => this.options.getWorkspaceRouter().renameThread(threadId, title),
79
+ deleteThread: (threadId) => this.options.getWorkspaceRouter().deleteThread(threadId),
80
+ });
81
+ this.sessionCommandRuntime = new session_command_runtime_js_1.ChannelSessionCommandRuntime({
82
+ service: sessionCommandService,
83
+ store: this.options.store,
84
+ getThreadSessionKey: (threadId) => this.options.getWorkspaceRouter().getThreadSessionKey(threadId),
85
+ setThreadRoute: (sessionKey, route) => {
86
+ this.threadRouting.set(sessionKey, route);
87
+ },
88
+ createRoute: (input, threadId) => ({
89
+ workspaceId: input.workspaceId,
90
+ instanceId: input.instanceId,
91
+ platformKey: input.platformKey,
92
+ platformUserId: input.platformUserId,
93
+ chatId: input.chatId,
94
+ threadId,
95
+ }),
96
+ sendResult: (input, result) => this.sendSessionCommandResult(input, result),
97
+ });
71
98
  }
72
99
  async refreshBindings() {
73
100
  const config = await this.options.readConfig();
@@ -204,6 +231,17 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
204
231
  }
205
232
  };
206
233
  }
234
+ async executeSessionCommand(input) {
235
+ return this.sessionCommandRuntime.execute(input);
236
+ }
237
+ async sendSessionCommandResult(input, result) {
238
+ const state = this.resolveRuntimeState(input.workspaceId, input.instanceId).state;
239
+ if (state?.client && state.connected && result.card?.actions?.length && state.cardActionsEnabled) {
240
+ await this.sendSessionCommandCard(state, input.chatId, result.displayText, result.card.actions, this.options.getWorkspaceRouter().getThreadSessionKey(input.currentThreadId), input.currentThreadId);
241
+ return;
242
+ }
243
+ await this.sendImmediateCard(input.workspaceId, input.chatId, result.displayText, input.instanceId);
244
+ }
207
245
  async sendOutboundMessage(workspaceId, input) {
208
246
  const state = this.resolveRuntimeState(workspaceId, input.route?.instanceId).state;
209
247
  if (!state?.client || !state.connected) {
@@ -659,35 +697,18 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
659
697
  const acknowledgement = this.createTurnState(effectiveSessionKey, input.messageId);
660
698
  await this.addAcknowledgementReaction(input.workspaceId, input.messageId, acknowledgement, instanceId);
661
699
  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 };
700
+ const sessionCommand = await this.executeSessionCommand({
701
+ workspaceId: input.workspaceId,
702
+ currentThreadId: threadId,
703
+ text: input.text,
704
+ defaultTitle: `${input.displayName || 'Lark'} ${new Date().toLocaleTimeString()}`,
705
+ chatId: input.chatId,
706
+ platformUserId: input.platformUserId,
707
+ platformKey,
708
+ instanceId,
709
+ });
710
+ if (sessionCommand.handled) {
711
+ return { paired: true, threadId: sessionCommand.threadId || threadId };
691
712
  }
692
713
  const latestRun = this.options.store.getLatestRunForThread(threadId);
693
714
  if ((normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny')
@@ -1157,6 +1178,21 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
1157
1178
  this.options.log?.(`localcore-lark card create took ${Date.now() - startedAt}ms textBytes=${Buffer.byteLength(text || '', 'utf8')}`);
1158
1179
  return String(response?.data?.message_id || '').trim();
1159
1180
  }
1181
+ async sendSessionCommandCard(state, chatId, text, actionRows, sessionKey, threadId) {
1182
+ const startedAt = Date.now();
1183
+ const response = await state.client.im.message.create({
1184
+ params: {
1185
+ receive_id_type: this.resolveReceiveIdType(chatId),
1186
+ },
1187
+ data: {
1188
+ receive_id: chatId,
1189
+ msg_type: 'interactive',
1190
+ content: JSON.stringify((0, cards_js_1.buildSessionCommandCard)(text, actionRows, sessionKey, threadId)),
1191
+ },
1192
+ });
1193
+ this.options.log?.(`localcore-lark session card create took ${Date.now() - startedAt}ms textBytes=${Buffer.byteLength(text || '', 'utf8')}`);
1194
+ return String(response?.data?.message_id || '').trim();
1195
+ }
1160
1196
  async sendTextAsMessage(state, chatId, text) {
1161
1197
  const startedAt = Date.now();
1162
1198
  const rendered = (0, messages_js_1.renderLarkTextMessage)(text);
@@ -1272,6 +1308,27 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
1272
1308
  const instanceId = legacyCall ? 'default' : instanceIdOrData;
1273
1309
  const data = (legacyCall ? instanceIdOrData : maybeData) || {};
1274
1310
  try {
1311
+ const sessionAction = (0, cards_js_1.extractSessionCommandActionValue)(data);
1312
+ if (sessionAction) {
1313
+ const route = this.threadRouting.get(sessionAction.sessionKey)
1314
+ || this.routeFromThreadBinding(sessionAction.threadId, instanceId);
1315
+ if (!route) {
1316
+ this.options.log?.(`localcore-lark session card action ignored without route for thread=${sessionAction.threadId}`);
1317
+ return;
1318
+ }
1319
+ await this.executeSessionCommand({
1320
+ workspaceId,
1321
+ currentThreadId: sessionAction.threadId,
1322
+ text: sessionAction.command,
1323
+ defaultTitle: `Lark ${new Date().toLocaleTimeString()}`,
1324
+ chatId: route.chatId,
1325
+ platformUserId: route.platformUserId,
1326
+ platformKey: route.platformKey,
1327
+ instanceId: route.instanceId,
1328
+ });
1329
+ this.options.log?.(`localcore-lark processed session card action for ${workspaceId}/${instanceId}: ${sessionAction.command}`);
1330
+ return;
1331
+ }
1275
1332
  const action = (0, cards_js_1.extractCardActionValue)(data);
1276
1333
  if (!action) {
1277
1334
  return;
@@ -1285,6 +1342,21 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
1285
1342
  this.options.log?.(`localcore-lark card action failed for ${workspaceId}/${instanceId}: ${error instanceof Error ? error.message : String(error)}`);
1286
1343
  }
1287
1344
  }
1345
+ routeFromThreadBinding(threadId, instanceId) {
1346
+ const binding = this.options.store.getPlatformThreadBindingByThreadId(threadId);
1347
+ if (!binding || (binding.platform !== 'lark' && !binding.platform.startsWith('lark:'))) {
1348
+ return undefined;
1349
+ }
1350
+ const bindingInstanceId = getLarkInstanceId(binding.platform) || instanceId || 'default';
1351
+ return {
1352
+ workspaceId: binding.workspace_id,
1353
+ instanceId: bindingInstanceId,
1354
+ platformKey: binding.platform,
1355
+ platformUserId: binding.platform_user_id,
1356
+ chatId: binding.chat_id,
1357
+ threadId: binding.thread_id,
1358
+ };
1359
+ }
1288
1360
  async markPermissionCardActionHandled(workspaceId, instanceId, rootPayload, payload, value, response, threadId) {
1289
1361
  const state = this.resolveRuntimeState(workspaceId, instanceId).state;
1290
1362
  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;
@@ -13,6 +13,8 @@ const node_crypto_2 = require("node:crypto");
13
13
  const desktop_js_1 = require("../../../../../shared/desktop.js");
14
14
  const content_js_1 = require("../shared/content.js");
15
15
  const file_utils_js_1 = require("../shared/file-utils.js");
16
+ const session_command_runtime_js_1 = require("../shared/session-command-runtime.js");
17
+ const session_command_service_js_1 = require("../../thread/session-command-service.js");
16
18
  const config_js_1 = require("./config.js");
17
19
  const transport_js_1 = require("./transport.js");
18
20
  // ==================== Constants ====================
@@ -150,11 +152,36 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
150
152
  outboundTurns = new Map();
151
153
  processedInboundMessages = new Map();
152
154
  mutedThreadBridgeCounts = new Map();
155
+ sessionCommandRuntime;
153
156
  platform = 'weixin';
154
157
  routeType = 'channel.chat';
155
158
  constructor(options) {
156
159
  super();
157
160
  this.options = options;
161
+ const sessionCommandService = new session_command_service_js_1.SessionCommandService({
162
+ listThreads: (workspaceId) => this.options.getWorkspaceRouter().listThreads(workspaceId),
163
+ getThread: (threadId) => this.options.getWorkspaceRouter().getThread(threadId),
164
+ createThread: (workspaceId, title) => this.options.getWorkspaceRouter().createThread(workspaceId, title),
165
+ renameThread: (threadId, title) => this.options.getWorkspaceRouter().renameThread(threadId, title),
166
+ deleteThread: (threadId) => this.options.getWorkspaceRouter().deleteThread(threadId),
167
+ });
168
+ this.sessionCommandRuntime = new session_command_runtime_js_1.ChannelSessionCommandRuntime({
169
+ service: sessionCommandService,
170
+ store: this.options.store,
171
+ getThreadSessionKey: (threadId) => this.options.getWorkspaceRouter().getThreadSessionKey(threadId),
172
+ setThreadRoute: (sessionKey, route) => {
173
+ this.threadRouting.set(sessionKey, route);
174
+ },
175
+ createRoute: (input, threadId) => ({
176
+ workspaceId: input.workspaceId,
177
+ instanceId: input.instanceId,
178
+ platformKey: input.platformKey,
179
+ platformUserId: input.platformUserId,
180
+ chatId: input.chatId,
181
+ threadId,
182
+ }),
183
+ sendResult: (input, result) => this.sendSessionCommandResult(input, result),
184
+ });
158
185
  }
159
186
  // ==================== Lifecycle ====================
160
187
  async refreshBindings() {
@@ -648,7 +675,6 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
648
675
  updated_at: now,
649
676
  });
650
677
  }
651
- const sessionKey = router.getThreadSessionKey(threadId);
652
678
  const normalizedText = String(input.text || '').trim().toLowerCase();
653
679
  const permissionThreadId = (normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny') ? this.findAwaitingPermissionThreadId(input.workspaceId, input.chatId, input.platformUserId, input.platformKey) : '';
654
680
  if (permissionThreadId && permissionThreadId !== threadId) {
@@ -665,38 +691,19 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
665
691
  });
666
692
  // Handle slash commands
667
693
  const slashCommand = this.parseSlashCommand(input.text);
668
- if (slashCommand?.name === 'new') {
669
- const title = slashCommand.args.join(' ').trim() || `${input.displayName || 'WeChat'} ${new Date().toLocaleTimeString()}`;
670
- const nextThread = await router.createThread(input.workspaceId, title);
671
- const inheritedMode = this.options.store.getThreadRow?.(threadId)?.agent_mode || '';
672
- if (inheritedMode && inheritedMode !== 'default') {
673
- this.options.store.updateThreadAgentMode?.(nextThread.id, inheritedMode);
674
- }
675
- const now = new Date().toISOString();
676
- this.options.store.updateAuthorizedUserThread(input.workspaceId, input.platformUserId, nextThread.id, platformKey);
677
- this.options.store.upsertPlatformThreadBinding({
678
- workspace_id: input.workspaceId,
679
- platform: platformKey,
680
- chat_id: input.chatId,
681
- platform_user_id: input.platformUserId,
682
- thread_id: nextThread.id,
683
- last_platform_message_id: null,
684
- created_at: now,
685
- updated_at: now,
686
- });
687
- this.threadRouting.set(router.getThreadSessionKey(nextThread.id), {
688
- workspaceId: input.workspaceId,
689
- instanceId,
690
- platformKey,
691
- platformUserId: input.platformUserId,
692
- chatId: input.chatId,
693
- threadId: nextThread.id,
694
- });
695
- const st = this.runtime.get((0, config_js_1.runtimeKey)(input.workspaceId, instanceId));
696
- if (st?.connected) {
697
- await this.sendTextMessage(st, input.chatId, '**已开始新会话**', input.contextToken);
698
- }
699
- return { paired: true, threadId: nextThread.id };
694
+ const sessionCommand = await this.executeSessionCommand({
695
+ workspaceId: input.workspaceId,
696
+ currentThreadId: threadId,
697
+ text: input.text,
698
+ defaultTitle: `${input.displayName || 'WeChat'} ${new Date().toLocaleTimeString()}`,
699
+ chatId: input.chatId,
700
+ platformUserId: input.platformUserId,
701
+ platformKey,
702
+ instanceId,
703
+ contextToken: input.contextToken,
704
+ });
705
+ if (sessionCommand.handled) {
706
+ return { paired: true, threadId: sessionCommand.threadId || threadId };
700
707
  }
701
708
  const latestRun = this.options.store.getLatestRunForThread(threadId);
702
709
  if ((normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny')
@@ -711,6 +718,17 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
711
718
  await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(wrappedText, input.contentParts));
712
719
  return { paired: true, threadId };
713
720
  }
721
+ async executeSessionCommand(input) {
722
+ return this.sessionCommandRuntime.execute(input);
723
+ }
724
+ async sendSessionCommandResult(input, result) {
725
+ const state = this.resolveRuntimeState(input.workspaceId, input.instanceId).state;
726
+ if (!state?.connected) {
727
+ this.options.log?.(`localcore-weixin session command response skipped: workspace not connected: ${input.workspaceId}`);
728
+ return;
729
+ }
730
+ await this.sendTextMessage(state, input.chatId, result.displayText, input.contextToken);
731
+ }
714
732
  // ==================== Private: Bindings ====================
715
733
  resolveRuntimeState(workspaceId, instanceId) {
716
734
  if (instanceId) {
@@ -8,6 +8,8 @@ const node_process_1 = __importDefault(require("node:process"));
8
8
  const index_js_1 = require("../../../../packages/contracts/src/index.js");
9
9
  const job_id_js_1 = require("../scheduler/job-id.js");
10
10
  const scheduled_job_route_js_1 = require("../scheduler/scheduled-job-route.js");
11
+ const monitor_id_js_1 = require("../automation/monitor-id.js");
12
+ const monitor_cli_parsers_js_1 = require("./monitor-cli-parsers.js");
11
13
  const DEFAULT_BASE_URL = 'http://127.0.0.1:9831/api/local/v1';
12
14
  async function runCli(argv, env = node_process_1.default.env, io = node_process_1.default) {
13
15
  try {
@@ -23,6 +25,26 @@ async function runCli(argv, env = node_process_1.default.env, io = node_process_
23
25
  return 2;
24
26
  }
25
27
  }
28
+ if (domain === 'monitor') {
29
+ switch (action) {
30
+ case 'add':
31
+ return await handleMonitorAdd(flags, env, io, json);
32
+ case 'list':
33
+ return await handleMonitorList(flags, env, io, json);
34
+ case 'info':
35
+ return await handleMonitorInfo(maybeId, flags, env, io, json);
36
+ case 'edit':
37
+ return await handleMonitorEdit(maybeId, flags, env, io, json);
38
+ case 'del':
39
+ case 'delete':
40
+ return await handleMonitorDelete(maybeId, flags, env, io, json);
41
+ case 'run':
42
+ return await handleMonitorRun(maybeId, flags, env, io, json);
43
+ default:
44
+ printUsage(io.stderr);
45
+ return 2;
46
+ }
47
+ }
26
48
  if (domain !== 'scheduler') {
27
49
  printUsage(io.stderr);
28
50
  return 2;
@@ -51,6 +73,110 @@ async function runCli(argv, env = node_process_1.default.env, io = node_process_
51
73
  return 1;
52
74
  }
53
75
  }
76
+ async function handleMonitorAdd(flags, env, io, json) {
77
+ const context = resolveContext(flags, env);
78
+ if (!context.workspaceId) {
79
+ throw new Error('monitor add requires a workspace context. Set LOCAL_AI_WORKSPACE_ID or pass --workspace.');
80
+ }
81
+ const title = getRequiredFlag(flags, 'title');
82
+ const sourceType = getRequiredFlag(flags, 'source');
83
+ const promptTemplate = getRequiredFlag(flags, 'message');
84
+ const condition = (0, monitor_cli_parsers_js_1.parseMonitorCondition)(getRequiredFlag(flags, 'condition'));
85
+ const sourceConfig = buildSourceConfig(sourceType, flags);
86
+ const monitor = await request(context.baseUrl, 'POST', '/automation/monitors', {
87
+ workspaceId: context.workspaceId,
88
+ ...(context.threadId ? { threadId: context.threadId } : {}),
89
+ title,
90
+ sourceType,
91
+ sourceConfig,
92
+ condition,
93
+ promptTemplate,
94
+ executionMode: getMonitorExecutionMode(flags),
95
+ cooldownMs: (0, monitor_cli_parsers_js_1.parseDurationMs)(getFlag(flags, 'cooldown') || '15m'),
96
+ enabled: true,
97
+ });
98
+ print(json, io.stdout, presentMonitor(monitor), [
99
+ `Created monitor ${(0, monitor_id_js_1.toPublicAutomationMonitorId)(monitor.id)}`,
100
+ `Title: ${monitor.title}`,
101
+ `Source: ${monitor.sourceType}`,
102
+ `Condition: ${formatCondition(monitor.condition)}`,
103
+ `Execution mode: ${monitor.executionMode}`,
104
+ ].join('\n'));
105
+ return 0;
106
+ }
107
+ async function handleMonitorList(flags, env, io, json) {
108
+ const context = resolveContext(flags, env);
109
+ const workspaceId = getFlag(flags, 'workspace') || context.workspaceId;
110
+ const threadId = flags.has('thread')
111
+ ? normalizeMaybeBooleanFlag(getFlag(flags, 'thread')) || context.threadId
112
+ : '';
113
+ const suffix = workspaceId ? `?workspace_id=${encodeURIComponent(workspaceId)}` : '';
114
+ const response = await request(context.baseUrl, 'GET', `/automation/monitors${suffix}`);
115
+ const monitors = threadId
116
+ ? response.monitors.filter((monitor) => monitor.route.threadId === threadId || monitorMatchesCliContext(monitor, context))
117
+ : response.monitors;
118
+ print(json, io.stdout, { monitors: monitors.map(presentMonitor) }, monitors.length === 0 ? 'No monitors.' : monitors.map(formatMonitorLine).join('\n'));
119
+ return 0;
120
+ }
121
+ async function handleMonitorInfo(monitorId, flags, env, io, json) {
122
+ if (!monitorId) {
123
+ throw new Error('monitor info requires a monitor id.');
124
+ }
125
+ const context = resolveContext(flags, env);
126
+ const monitor = await request(context.baseUrl, 'GET', `/automation/monitors/${encodeURIComponent(monitorId)}`);
127
+ const runs = await request(context.baseUrl, 'GET', `/automation/monitors/${encodeURIComponent(monitorId)}/runs`);
128
+ print(json, io.stdout, { monitor: presentMonitor(monitor), runs: runs.runs }, formatMonitorDetails(monitor, runs.runs[0]));
129
+ return 0;
130
+ }
131
+ async function handleMonitorEdit(monitorId, flags, env, io, json) {
132
+ if (!monitorId) {
133
+ throw new Error('monitor edit requires a monitor id.');
134
+ }
135
+ const input = {};
136
+ const title = getFlag(flags, 'title');
137
+ const promptTemplate = getFlag(flags, 'message');
138
+ const condition = getFlag(flags, 'condition');
139
+ const enabled = getOptionalBooleanFlag(flags, 'enabled');
140
+ const executionMode = getFlag(flags, 'execution-mode');
141
+ const cooldown = getFlag(flags, 'cooldown');
142
+ if (title)
143
+ input.title = title;
144
+ if (typeof promptTemplate === 'string' && promptTemplate)
145
+ input.promptTemplate = promptTemplate;
146
+ if (condition)
147
+ input.condition = (0, monitor_cli_parsers_js_1.parseMonitorCondition)(condition);
148
+ if (typeof enabled === 'boolean')
149
+ input.enabled = enabled;
150
+ if (executionMode)
151
+ input.executionMode = (0, index_js_1.normalizeScheduledJobExecutionMode)(executionMode);
152
+ if (cooldown)
153
+ input.cooldownMs = (0, monitor_cli_parsers_js_1.parseDurationMs)(cooldown);
154
+ if (Object.keys(input).length === 0) {
155
+ throw new Error('monitor edit requires at least one editable field.');
156
+ }
157
+ const context = resolveContext(flags, env);
158
+ const monitor = await request(context.baseUrl, 'PATCH', `/automation/monitors/${encodeURIComponent(monitorId)}`, input);
159
+ print(json, io.stdout, presentMonitor(monitor), `Updated monitor ${(0, monitor_id_js_1.toPublicAutomationMonitorId)(monitor.id)}`);
160
+ return 0;
161
+ }
162
+ async function handleMonitorDelete(monitorId, flags, env, io, json) {
163
+ if (!monitorId) {
164
+ throw new Error('monitor del requires a monitor id.');
165
+ }
166
+ const context = resolveContext(flags, env);
167
+ const result = await request(context.baseUrl, 'DELETE', `/automation/monitors/${encodeURIComponent(monitorId)}`);
168
+ print(json, io.stdout, result, `Deleted monitor ${monitorId}`);
169
+ return 0;
170
+ }
171
+ async function handleMonitorRun(monitorId, flags, env, io, json) {
172
+ if (!monitorId) {
173
+ throw new Error('monitor run requires a monitor id.');
174
+ }
175
+ const context = resolveContext(flags, env);
176
+ const run = await request(context.baseUrl, 'POST', `/automation/monitors/${encodeURIComponent(monitorId)}/run`);
177
+ print(json, io.stdout, run, `Triggered monitor ${monitorId}: ${run.status}`);
178
+ return 0;
179
+ }
54
180
  async function handleChannelSendFile(flags, env, io, json) {
55
181
  const context = resolveContext(flags, env);
56
182
  const filePath = getRequiredFlag(flags, 'path');
@@ -282,6 +408,23 @@ function normalizeMaybeBooleanFlag(value) {
282
408
  function getExecutionMode(flags) {
283
409
  return (0, index_js_1.normalizeScheduledJobExecutionMode)(getFlag(flags, 'execution-mode'));
284
410
  }
411
+ function getMonitorExecutionMode(flags) {
412
+ return (0, index_js_1.normalizeScheduledJobExecutionMode)(getFlag(flags, 'execution-mode') || 'side-thread');
413
+ }
414
+ function buildSourceConfig(sourceType, flags) {
415
+ const config = {};
416
+ if (sourceType === 'stock.quote') {
417
+ config.symbol = getRequiredFlag(flags, 'symbol').toUpperCase();
418
+ const price = getFlag(flags, 'price');
419
+ if (price)
420
+ config.price = Number(price);
421
+ }
422
+ const rawConfig = getFlag(flags, 'source-config');
423
+ if (rawConfig) {
424
+ return { ...config, ...JSON.parse(rawConfig) };
425
+ }
426
+ return config;
427
+ }
285
428
  function print(asJson, output, payload, text) {
286
429
  output.write(asJson ? `${JSON.stringify(payload, null, 2)}\n` : `${text}\n`);
287
430
  }
@@ -294,6 +437,12 @@ function printUsage(output) {
294
437
  ' lac scheduler edit <job-id> [--cron "<expr>"] [--message "<text>"] [--desc "<label>"] [--enabled true|false] [--execution-mode same-thread|side-thread] [--json]',
295
438
  ' lac scheduler del <job-id> [--json]',
296
439
  ' lac scheduler run <job-id> [--json]',
440
+ ' lac monitor add --title "<title>" --source stock.quote --symbol <symbol> --condition "change_percent >= 3" --message "<text>" [--cooldown 15m] [--execution-mode same-thread|side-thread] [--json]',
441
+ ' lac monitor list [--workspace <id>] [--thread [<id>]] [--json]',
442
+ ' lac monitor info <monitor-id> [--json]',
443
+ ' lac monitor edit <monitor-id> [--title "<title>"] [--condition "<expr>"] [--message "<text>"] [--enabled true|false] [--cooldown 15m] [--execution-mode same-thread|side-thread] [--json]',
444
+ ' lac monitor del <monitor-id> [--json]',
445
+ ' lac monitor run <monitor-id> [--json]',
297
446
  ' lac channel send-file --path "<file>" [--target <chat-or-user-id>] [--workspace <id>] [--workspace-path <path>] [--platform lark] [--name <filename>] [--json]',
298
447
  ].join('\n') + '\n');
299
448
  }
@@ -321,6 +470,53 @@ function presentJob(job) {
321
470
  id: (0, job_id_js_1.toPublicScheduledJobId)(job.id),
322
471
  };
323
472
  }
473
+ function formatMonitorLine(monitor) {
474
+ return `${(0, monitor_id_js_1.toPublicAutomationMonitorId)(monitor.id)} | ${monitor.enabled ? 'enabled' : 'disabled'} | ${monitor.executionMode} | ${monitor.sourceType} | ${formatCondition(monitor.condition)} | ${monitor.title}`;
475
+ }
476
+ function formatMonitorDetails(monitor, latestRun) {
477
+ return [
478
+ `Monitor: ${(0, monitor_id_js_1.toPublicAutomationMonitorId)(monitor.id)}`,
479
+ `Title: ${monitor.title}`,
480
+ `Workspace: ${monitor.workspaceId}`,
481
+ `Platform: ${monitor.platform}`,
482
+ `Thread: ${monitor.route.threadId || ''}`,
483
+ `Execution mode: ${monitor.executionMode}`,
484
+ `Source: ${monitor.sourceType}`,
485
+ `Condition: ${formatCondition(monitor.condition)}`,
486
+ `Cooldown: ${monitor.cooldownMs}ms`,
487
+ `Enabled: ${monitor.enabled ? 'true' : 'false'}`,
488
+ `Message: ${monitor.promptTemplate}`,
489
+ latestRun ? `Latest run: ${latestRun.status} @ ${latestRun.triggeredAt}` : 'Latest run: none',
490
+ ].join('\n');
491
+ }
492
+ function formatCondition(condition) {
493
+ if (condition.expression) {
494
+ return condition.expression;
495
+ }
496
+ return `${condition.metric} ${condition.operator} ${condition.value}`;
497
+ }
498
+ function presentMonitor(monitor) {
499
+ return {
500
+ ...monitor,
501
+ id: (0, monitor_id_js_1.toPublicAutomationMonitorId)(monitor.id),
502
+ };
503
+ }
504
+ function monitorMatchesCliContext(monitor, context) {
505
+ return (0, scheduled_job_route_js_1.scheduledJobMatchesCliContext)({
506
+ id: monitor.id,
507
+ workspaceId: monitor.workspaceId,
508
+ platform: monitor.platform,
509
+ route: monitor.route,
510
+ executionMode: monitor.executionMode,
511
+ triggerType: 'once',
512
+ promptTemplate: monitor.promptTemplate,
513
+ description: monitor.title,
514
+ enabled: monitor.enabled,
515
+ concurrencyPolicy: monitor.concurrencyPolicy,
516
+ createdAt: monitor.createdAt,
517
+ updatedAt: monitor.updatedAt,
518
+ }, context);
519
+ }
324
520
  function formatError(error) {
325
521
  return error instanceof Error ? error.message : String(error);
326
522
  }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseMonitorCondition = parseMonitorCondition;
4
+ exports.parseDurationMs = parseDurationMs;
5
+ const index_js_1 = require("../../../../packages/contracts/src/index.js");
6
+ function parseMonitorCondition(value) {
7
+ const expression = String(value || '').trim();
8
+ if (expression.includes('&&') || expression.includes('||')) {
9
+ return {
10
+ metric: 'expression',
11
+ operator: '==',
12
+ value: true,
13
+ expression,
14
+ };
15
+ }
16
+ const match = expression.match(/^([a-zA-Z0-9_.-]+)\s*(>=|<=|==|!=|>|<)\s*(.+)$/);
17
+ if (!match) {
18
+ throw new Error('Monitor condition must look like "change_percent >= 3".');
19
+ }
20
+ const rawValue = String(match[3] || '').trim();
21
+ const numeric = Number(rawValue);
22
+ return {
23
+ metric: String(match[1] || '').trim(),
24
+ operator: (0, index_js_1.normalizeAutomationMonitorConditionOperator)(match[2]),
25
+ value: Number.isFinite(numeric) && rawValue !== '' ? numeric : rawValue,
26
+ };
27
+ }
28
+ function parseDurationMs(value) {
29
+ const match = String(value || '').trim().match(/^(\d+(?:\.\d+)?)(ms|s|m|h)?$/i);
30
+ if (!match) {
31
+ throw new Error('Duration must be a number with optional ms, s, m, or h suffix.');
32
+ }
33
+ const amount = Number(match[1]);
34
+ const unit = String(match[2] || 'ms').toLowerCase();
35
+ const multiplier = unit === 'h' ? 60 * 60 * 1000 : unit === 'm' ? 60 * 1000 : unit === 's' ? 1000 : 1;
36
+ return Math.round(amount * multiplier);
37
+ }