@kafca/agentdock 0.1.57 → 0.1.58

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 (61) hide show
  1. package/dist/renderer/assets/{Badge-CDTbmw1h.js → Badge-BfY1VaHS.js} +1 -1
  2. package/dist/renderer/assets/{Card-DayUo35w.js → Card-DWA_Tww_.js} +1 -1
  3. package/dist/renderer/assets/{Config-ByGMQuzR.js → Config-nHj3g1U5.js} +1 -1
  4. package/dist/renderer/assets/{CronList-XqHdVlgu.js → CronList-BwkA_I7v.js} +1 -1
  5. package/dist/renderer/assets/{Dashboard-CZj-oANw.js → Dashboard-D0_A7k02.js} +2 -2
  6. package/dist/renderer/assets/{EmptyState-EFMkOjZO.js → EmptyState-zatDjLj_.js} +1 -1
  7. package/dist/renderer/assets/{HighlightedMarkdown-DyvF3DHK.js → HighlightedMarkdown-CsM5n70d.js} +1 -1
  8. package/dist/renderer/assets/{Input-DxoWVc9d.js → Input-R1XdFK1K.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-CbUb2tG7.js → KnowledgeDetail-Co5sxeGd.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-BWV1gz4f.js → KnowledgeHome-DwziPw5m.js} +1 -1
  11. package/dist/renderer/assets/{Logs-DqnanQTc.js → Logs-NwZUtR_G.js} +1 -1
  12. package/dist/renderer/assets/{Modal-yY7TzR0x.js → Modal-CrcIwIFL.js} +1 -1
  13. package/dist/renderer/assets/{MonitorList-CIjGQkFs.js → MonitorList-CS1uLS5A.js} +1 -1
  14. package/dist/renderer/assets/{Page-vwHNUdoj.js → Page-Dp0obeSw.js} +1 -1
  15. package/dist/renderer/assets/{Select-CmbK36pN.js → Select-6mVQaf84.js} +1 -1
  16. package/dist/renderer/assets/{ThreadChat-4OceMgM4.js → ThreadChat-BFzbn3L6.js} +3 -3
  17. package/dist/renderer/assets/{Workspace-DplPJOjJ.js → Workspace-DCmo8BMm.js} +1 -1
  18. package/dist/renderer/assets/{arrow-left-C8HfBt2K.js → arrow-left-9rjg_Hp4.js} +1 -1
  19. package/dist/renderer/assets/{book-open-BCdZWncQ.js → book-open-Bu8GCHnG.js} +1 -1
  20. package/dist/renderer/assets/{channels-hgO3cAIx.js → channels-BxVbG-6j.js} +1 -1
  21. package/dist/renderer/assets/{chevron-down-BPk77tqY.js → chevron-down-BE8uQmWM.js} +1 -1
  22. package/dist/renderer/assets/{index-BlA4xPdd.js → index-B7TeF6Es.js} +1 -1
  23. package/dist/renderer/assets/{index-wYpjqVqC.js → index-BkI9POxj.js} +2 -2
  24. package/dist/renderer/assets/{knowledge-7pl06KuH.js → knowledge-Kl19SqGS.js} +1 -1
  25. package/dist/renderer/assets/{pencil-CB1SWZ9A.js → pencil-CPB7SK1m.js} +1 -1
  26. package/dist/renderer/assets/{play-BvsoXUrM.js → play-DGAO4OCu.js} +1 -1
  27. package/dist/renderer/assets/{plus-CokBDnaL.js → plus-Bt26z-4q.js} +1 -1
  28. package/dist/renderer/assets/{save-D-w4kATO.js → save-Une7GMxT.js} +1 -1
  29. package/dist/renderer/assets/{search-Cnsn6i4Q.js → search-DE0agJjJ.js} +1 -1
  30. package/dist/renderer/assets/{shield-check-Cjja2Gh6.js → shield-check-De7xaXZh.js} +1 -1
  31. package/dist/renderer/assets/{threads-BCm6j4eI.js → threads-BnsNL5ei.js} +1 -1
  32. package/dist/renderer/assets/{trash-2-CIxz9qYY.js → trash-2-6iGF59Ac.js} +1 -1
  33. package/dist/renderer/index.html +1 -1
  34. package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +6 -47
  35. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +2 -5
  36. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +6 -53
  37. package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +25 -37
  38. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +5 -20
  39. package/dist-electron/services/local-ai-core/src/channel/shared/base-channel-gateway.js +37 -0
  40. package/dist-electron/services/local-ai-core/src/channel/shared/bridge-event-helpers.js +23 -0
  41. package/dist-electron/services/local-ai-core/src/channel/shared/channel-keys.js +6 -0
  42. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +2 -2
  43. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +6 -44
  44. package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +4 -11
  45. package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +0 -8
  46. package/dist-electron/services/local-ai-core/src/cli/lac.js +3 -5
  47. package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +8 -0
  48. package/dist-electron/services/local-ai-core/src/sandbox/opensandbox-client.js +3 -2
  49. package/dist-electron/services/local-ai-core/src/scheduler/base-channel-schedule-adapter.js +77 -0
  50. package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +13 -79
  51. package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +2 -10
  52. package/dist-electron/services/local-ai-core/src/scheduler/run-polling.js +19 -0
  53. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +3 -37
  54. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-route.js +20 -0
  55. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +15 -28
  56. package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +12 -0
  57. package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +13 -79
  58. package/dist-electron/tests/integration/local-core-refactor.test.js +15 -8
  59. package/package.json +1 -1
  60. package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +0 -13
  61. package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +0 -13
@@ -16,6 +16,7 @@ const thread_routing_js_1 = require("../shared/thread-routing.js");
16
16
  const base_channel_gateway_js_1 = require("../shared/base-channel-gateway.js");
17
17
  const inbound_authorization_js_1 = require("../shared/inbound-authorization.js");
18
18
  const channel_keys_js_1 = require("../shared/channel-keys.js");
19
+ const local_core_slash_commands_js_1 = require("../../acp/local-core-slash-commands.js");
19
20
  const config_js_1 = require("./config.js");
20
21
  const transport_js_1 = require("./transport.js");
21
22
  const runtime_state_js_1 = require("./runtime-state.js");
@@ -201,10 +202,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
201
202
  && event.type !== 'status') {
202
203
  return;
203
204
  }
204
- const previous = this.outboundEventChains.get(sessionKey) || Promise.resolve();
205
- const current = previous
206
- .catch(() => undefined)
207
- .then(async () => {
205
+ const current = this.scheduleOutboundChain(sessionKey, async () => {
208
206
  const binding = this.options.store.getPlatformThreadBinding(route.workspaceId, route.chatId, route.platformUserId, routePlatformKey);
209
207
  if (!binding)
210
208
  return;
@@ -246,19 +244,13 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
246
244
  this.options.log?.(`localcore-weixin sent message for sessionKey=${sessionKey} type=${event.type} sent=${turn.sentCount}/${binding.last_platform_message_id ? WEIXIN_CONTEXT_SEND_LIMIT : 'unlimited'}`);
247
245
  }
248
246
  catch (error) {
249
- this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${(0, text_utils_js_1.formatWeixinError)(error)}`);
250
- }
251
- })
252
- .finally(() => {
253
- if (this.outboundEventChains.get(sessionKey) === current) {
254
- this.outboundEventChains.delete(sessionKey);
247
+ this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${(0, local_core_errors_js_1.formatSafeError)(error)}`);
255
248
  }
256
249
  });
257
- this.outboundEventChains.set(sessionKey, current);
258
250
  await current;
259
251
  }
260
252
  registerScheduledThreadBridge(input) {
261
- const instanceId = input.route.instanceId || getWeixinInstanceId(input.platform) || 'default';
253
+ const instanceId = input.route.instanceId || (0, channel_keys_js_1.extractChannelInstanceId)(input.platform, 'weixin') || 'default';
262
254
  const platformKey = (0, channel_keys_js_1.channelPlatformKey)('weixin', instanceId);
263
255
  const route = {
264
256
  workspaceId: input.workspaceId,
@@ -413,7 +405,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
413
405
  authorized,
414
406
  });
415
407
  const normalizedText = String(msg.text || '').trim().toLowerCase();
416
- const permissionThreadId = (normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny') ? this.findAwaitingPermissionThreadId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.platformKey) : '';
408
+ const permissionThreadId = (normalizedText === 'allow' || normalizedText === 'allow all' || normalizedText === 'deny') ? this.findAwaitingPermissionThreadId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.platformKey || 'weixin') : '';
417
409
  if (permissionThreadId && permissionThreadId !== threadId) {
418
410
  threadId = permissionThreadId;
419
411
  }
@@ -427,7 +419,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
427
419
  threadId,
428
420
  });
429
421
  // Handle slash commands
430
- const slashCommand = this.parseSlashCommand(msg.text);
422
+ const slashCommand = (0, local_core_slash_commands_js_1.parseSlashCommand)(msg.text);
431
423
  const sessionCommand = await this.executeSessionCommand({
432
424
  workspaceId: msg.workspaceId,
433
425
  currentThreadId: threadId,
@@ -453,13 +445,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
453
445
  await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(msg.text, msg.contentParts));
454
446
  return;
455
447
  }
456
- async resolveDefaultAgentType(workspaceId, threadId) {
457
- const router = this.options.getWorkspaceRouter();
458
- if (typeof router.getWorkspaceDefaultAgentType === 'function') {
459
- return router.getWorkspaceDefaultAgentType(workspaceId);
460
- }
461
- return this.options.store.getThreadRow(threadId)?.agent_type || 'codex';
462
- }
463
448
  // ==================== Private: Bindings ====================
464
449
  // ==================== Private: Workspace Lifecycle ====================
465
450
  async startWorkspace(binding) {
@@ -723,28 +708,5 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
723
708
  this.processedInboundMessages.set(key, now + PROCESSED_MESSAGE_TTL_MS);
724
709
  return false;
725
710
  }
726
- findAwaitingPermissionThreadId(workspaceId, chatId, platformUserId, platformKey = 'weixin') {
727
- for (const [sessionKey, route] of this.threadRouting.entries()) {
728
- if (route.workspaceId !== workspaceId || route.platformKey !== platformKey || route.chatId !== chatId || route.platformUserId !== platformUserId)
729
- continue;
730
- const turn = this.outboundTurns.get(sessionKey);
731
- if (turn?.awaitingPermission && route.threadId)
732
- return route.threadId;
733
- }
734
- return '';
735
- }
736
- parseSlashCommand(text) {
737
- const normalized = String(text || '').trim();
738
- if (!normalized.startsWith('/'))
739
- return null;
740
- const [name = '', ...args] = normalized.slice(1).split(/\s+/);
741
- if (!name)
742
- return null;
743
- return { name: name.trim().toLowerCase(), args };
744
- }
745
711
  }
746
712
  exports.LocalCoreWeixinGateway = LocalCoreWeixinGateway;
747
- function getWeixinInstanceId(platform) {
748
- const normalized = String(platform || '').trim();
749
- return normalized.startsWith('weixin:') ? normalized.slice('weixin:'.length).trim() : '';
750
- }
@@ -5,6 +5,7 @@ exports.getOrCreateWeixinTurnState = getOrCreateWeixinTurnState;
5
5
  exports.consumeWeixinBridgeEvent = consumeWeixinBridgeEvent;
6
6
  exports.renderWeixinTurnText = renderWeixinTurnText;
7
7
  exports.isTerminalWeixinBridgeMessage = isTerminalWeixinBridgeMessage;
8
+ const bridge_event_helpers_js_1 = require("../shared/bridge-event-helpers.js");
8
9
  function renderBridgeContent(event) {
9
10
  const toolCall = event.toolCall;
10
11
  if (!toolCall) {
@@ -16,16 +17,8 @@ function renderBridgeContent(event) {
16
17
  return '';
17
18
  return [name, status].filter(Boolean).join(' - ');
18
19
  }
19
- function bridgeEventKind(event) {
20
- return event.bridgeKind || (event.type === 'status' ? 'status' : 'assistant');
21
- }
22
20
  function pushUnique(target, value) {
23
- const normalized = value.trim();
24
- if (!normalized || target[target.length - 1] === normalized)
25
- return;
26
- target.push(normalized);
27
- if (target.length > 8)
28
- target.splice(0, target.length - 8);
21
+ (0, bridge_event_helpers_js_1.pushUniqueLine)(target, value, 8);
29
22
  }
30
23
  function flushPendingThought(turn) {
31
24
  const text = String(turn.pendingThoughtText || '').trim();
@@ -61,7 +54,7 @@ function getOrCreateWeixinTurnState(turns, sessionKey) {
61
54
  }
62
55
  function consumeWeixinBridgeEvent(turn, event) {
63
56
  const content = renderBridgeContent(event);
64
- const bridgeKind = bridgeEventKind(event);
57
+ const bridgeKind = (0, bridge_event_helpers_js_1.resolveBridgeEventKind)(event);
65
58
  if (event.type === 'typing_start') {
66
59
  Object.assign(turn, {
67
60
  processing: true,
@@ -149,7 +142,7 @@ function isTerminalWeixinBridgeMessage(event, rendered) {
149
142
  return true;
150
143
  if (event.type !== 'reply')
151
144
  return false;
152
- const kind = bridgeEventKind(event);
145
+ const kind = (0, bridge_event_helpers_js_1.resolveBridgeEventKind)(event);
153
146
  if (kind === 'tool' || kind === 'thought' || kind === 'plan' || kind === 'status')
154
147
  return false;
155
148
  return Boolean(rendered.trim());
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.stripWeixinHtml = stripWeixinHtml;
4
- exports.formatWeixinError = formatWeixinError;
5
4
  exports.waitForWeixinRetry = waitForWeixinRetry;
6
5
  exports.utf8ByteLength = utf8ByteLength;
7
6
  exports.splitTextByUtf8Bytes = splitTextByUtf8Bytes;
@@ -21,13 +20,6 @@ function stripWeixinHtml(html) {
21
20
  .replace(/'/g, "'")
22
21
  .replace(/ /g, ' ');
23
22
  }
24
- function formatWeixinError(error) {
25
- if (error instanceof Error) {
26
- const cause = error.cause;
27
- return cause !== undefined ? `${error.message}: ${String(cause)}` : error.message;
28
- }
29
- return String(error);
30
- }
31
23
  function waitForWeixinRetry(ms, signal) {
32
24
  return new Promise((resolve, reject) => {
33
25
  const timer = setTimeout(resolve, ms);
@@ -10,6 +10,7 @@ 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
11
  const monitor_id_js_1 = require("../automation/monitor-id.js");
12
12
  const monitor_cli_parsers_js_1 = require("./monitor-cli-parsers.js");
13
+ const local_core_errors_js_1 = require("../kernel/local-core-errors.js");
13
14
  const DEFAULT_BASE_URL = 'http://127.0.0.1:9831/api/local/v1';
14
15
  async function runCli(argv, env = node_process_1.default.env, io = node_process_1.default) {
15
16
  try {
@@ -69,7 +70,7 @@ async function runCli(argv, env = node_process_1.default.env, io = node_process_
69
70
  }
70
71
  }
71
72
  catch (error) {
72
- io.stderr.write(`${formatError(error)}\n`);
73
+ io.stderr.write(`${(0, local_core_errors_js_1.formatSafeError)(error)}\n`);
73
74
  return 1;
74
75
  }
75
76
  }
@@ -324,7 +325,7 @@ async function request(baseUrl, method, path, body) {
324
325
  });
325
326
  }
326
327
  catch (error) {
327
- throw new Error(`Local AI Core is unavailable at ${baseUrl}: ${formatError(error)}`);
328
+ throw new Error(`Local AI Core is unavailable at ${baseUrl}: ${(0, local_core_errors_js_1.formatSafeError)(error)}`);
328
329
  }
329
330
  const payload = await response.json();
330
331
  if (!response.ok || !payload.ok) {
@@ -517,9 +518,6 @@ function monitorMatchesCliContext(monitor, context) {
517
518
  updatedAt: monitor.updatedAt,
518
519
  }, context);
519
520
  }
520
- function formatError(error) {
521
- return error instanceof Error ? error.message : String(error);
522
- }
523
521
  void (async () => {
524
522
  if (require.main !== module) {
525
523
  return;
@@ -4,6 +4,7 @@ exports.LocalCoreErrorReporter = exports.LocalCoreError = void 0;
4
4
  exports.toLocalCoreErrorInfo = toLocalCoreErrorInfo;
5
5
  exports.formatUserError = formatUserError;
6
6
  exports.formatLogError = formatLogError;
7
+ exports.formatSafeError = formatSafeError;
7
8
  exports.errorInfoToHttpBody = errorInfoToHttpBody;
8
9
  const DEFAULTS = {
9
10
  runtime_not_found: {
@@ -175,6 +176,13 @@ function formatLogError(info) {
175
176
  const message = typeof info.message === 'string' ? info.message : safeStringify(info.message);
176
177
  return `${info.code}: ${message}${suffix}`;
177
178
  }
179
+ function formatSafeError(error) {
180
+ if (error instanceof Error) {
181
+ const cause = error.cause;
182
+ return cause !== undefined ? `${error.message}: ${cause instanceof Error ? cause.message : coerceErrorMessage(cause)}` : error.message;
183
+ }
184
+ return coerceErrorMessage(error);
185
+ }
178
186
  function errorInfoToHttpBody(info) {
179
187
  return {
180
188
  ok: false,
@@ -82,8 +82,9 @@ class OpenSandboxClient {
82
82
  });
83
83
  }
84
84
  catch (error) {
85
- throw new local_core_errors_js_1.LocalCoreError('sandbox_unavailable', `OpenSandbox request failed: ${error instanceof Error ? error.message : String(error)}`, {
86
- cause: error instanceof Error ? error.message : String(error),
85
+ const message = (0, local_core_errors_js_1.formatSafeError)(error);
86
+ throw new local_core_errors_js_1.LocalCoreError('sandbox_unavailable', `OpenSandbox request failed: ${message}`, {
87
+ cause: message,
87
88
  details: { method, path, serverUrl: this.options.serverUrl },
88
89
  });
89
90
  }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseChannelScheduleAdapter = void 0;
4
+ const scheduled_conversation_executor_js_1 = require("./scheduled-conversation-executor.js");
5
+ const scheduled_job_route_js_1 = require("./scheduled-job-route.js");
6
+ const thread_resolution_js_1 = require("./thread-resolution.js");
7
+ class BaseChannelScheduleAdapter {
8
+ options;
9
+ executor;
10
+ constructor(options) {
11
+ this.options = options;
12
+ this.executor = new scheduled_conversation_executor_js_1.ScheduledConversationExecutor({
13
+ store: options.store,
14
+ getWorkspaceRouter: options.getWorkspaceRouter,
15
+ });
16
+ }
17
+ supports(job) {
18
+ return (0, scheduled_job_route_js_1.platformMatches)(job.platform, this.platformBase) && this.supportedRouteTypes.includes(job.route.type);
19
+ }
20
+ async execute(context) {
21
+ const { job } = context;
22
+ const executionPolicy = this.createPolicy(job, {
23
+ store: this.options.store,
24
+ workspaceRouter: this.options.getWorkspaceRouter(),
25
+ getChannelRuntime: this.options.getChannelRuntime,
26
+ }, (nextJob) => this.resolveThread(nextJob), (nextJob) => this.preferredAgentFor(nextJob));
27
+ const execution = await this.executor.execute(job, job.promptTemplate, executionPolicy);
28
+ return {
29
+ threadId: execution.threadId,
30
+ runId: execution.runId,
31
+ replyText: execution.replyText,
32
+ deliveryMode: 'bridge-stream',
33
+ deliveryStatus: 'succeeded',
34
+ lastBridgeEventAt: new Date().toISOString(),
35
+ };
36
+ }
37
+ async resolveThread(job) {
38
+ const workspaceRouter = this.options.getWorkspaceRouter();
39
+ const route = job.route;
40
+ const channelId = route.channelId;
41
+ const participantId = route.participantId || '';
42
+ const binding = this.options.store.getPlatformThreadBinding(job.workspaceId, channelId, participantId, job.platform);
43
+ if (binding?.thread_id && await (0, thread_resolution_js_1.threadExists)(workspaceRouter, binding.thread_id)) {
44
+ return binding.thread_id;
45
+ }
46
+ if (route.threadId && await (0, thread_resolution_js_1.threadExists)(workspaceRouter, route.threadId)) {
47
+ return route.threadId;
48
+ }
49
+ const thread = await workspaceRouter.createThread(job.workspaceId, job.description || `Scheduled ${job.platform} task`);
50
+ const now = new Date().toISOString();
51
+ this.options.store.upsertPlatformThreadBinding({
52
+ workspace_id: job.workspaceId,
53
+ platform: job.platform,
54
+ chat_id: channelId,
55
+ platform_user_id: participantId,
56
+ thread_id: thread.id,
57
+ last_platform_message_id: null,
58
+ created_at: now,
59
+ updated_at: now,
60
+ });
61
+ const authorized = this.options.store.getAuthorizedUser(job.workspaceId, participantId, job.platform);
62
+ if (authorized) {
63
+ this.options.store.updateAuthorizedUserThread(job.workspaceId, participantId, thread.id, job.platform);
64
+ }
65
+ return thread.id;
66
+ }
67
+ preferredAgentFor(job) {
68
+ const route = job.route;
69
+ const channelId = route.channelId;
70
+ if (!channelId)
71
+ return '';
72
+ const participantId = route.participantId || '';
73
+ const binding = this.options.store.getPlatformThreadBinding(job.workspaceId, channelId, participantId, job.platform);
74
+ return binding?.preferred_agent_type || '';
75
+ }
76
+ }
77
+ exports.BaseChannelScheduleAdapter = BaseChannelScheduleAdapter;
@@ -1,87 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LarkScheduleAdapter = void 0;
4
- const scheduled_conversation_executor_js_1 = require("./scheduled-conversation-executor.js");
5
- const lark_execution_policies_js_1 = require("./lark-execution-policies.js");
6
- const scheduled_job_route_js_1 = require("./scheduled-job-route.js");
7
- class LarkScheduleAdapter {
8
- options;
9
- executor;
4
+ const channel_execution_policy_js_1 = require("./channel-execution-policy.js");
5
+ const base_channel_schedule_adapter_js_1 = require("./base-channel-schedule-adapter.js");
6
+ class LarkScheduleAdapter extends base_channel_schedule_adapter_js_1.BaseChannelScheduleAdapter {
7
+ platformBase = 'lark';
8
+ supportedRouteTypes = ['channel.chat', 'lark_chat'];
10
9
  deliveryTargets = ['lark'];
11
- constructor(options) {
12
- this.options = options;
13
- this.executor = new scheduled_conversation_executor_js_1.ScheduledConversationExecutor({
14
- store: options.store,
15
- getWorkspaceRouter: options.getWorkspaceRouter,
10
+ createPolicy(job, options, resolveSameThread, preferredAgentType) {
11
+ const label = this.platformBase.charAt(0).toUpperCase() + this.platformBase.slice(1);
12
+ return (0, channel_execution_policy_js_1.createChannelExecutionPolicy)(job, options, {
13
+ platformBase: this.platformBase,
14
+ resolveSameThread,
15
+ sideThreadTitle: (nextJob) => `[Scheduled:${label}] ${nextJob.description || nextJob.id}`,
16
+ legacySideThreadTitles: (nextJob) => [`[Scheduled] ${nextJob.description || nextJob.id}`],
17
+ preferredAgentType,
16
18
  });
17
19
  }
18
- supports(job) {
19
- return (0, scheduled_job_route_js_1.platformMatches)(job.platform, 'lark') && (job.route.type === 'channel.chat' || job.route.type === 'lark_chat');
20
- }
21
- async execute(context) {
22
- const { job } = context;
23
- const executionPolicy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
24
- store: this.options.store,
25
- workspaceRouter: this.options.getWorkspaceRouter(),
26
- getChannelRuntime: this.options.getChannelRuntime,
27
- }, (nextJob) => this.resolveThread(nextJob), (nextJob) => this.preferredAgentFor(nextJob));
28
- const execution = await this.executor.execute(job, job.promptTemplate, executionPolicy);
29
- return {
30
- threadId: execution.threadId,
31
- runId: execution.runId,
32
- replyText: execution.replyText,
33
- deliveryMode: 'bridge-stream',
34
- deliveryStatus: 'succeeded',
35
- lastBridgeEventAt: new Date().toISOString(),
36
- };
37
- }
38
- async resolveThread(job) {
39
- const workspaceRouter = this.options.getWorkspaceRouter();
40
- const route = job.route;
41
- const channelId = route.channelId;
42
- const participantId = route.participantId || '';
43
- const binding = this.options.store.getPlatformThreadBinding(job.workspaceId, channelId, participantId, job.platform);
44
- if (binding?.thread_id && await this.threadExists(binding.thread_id)) {
45
- return binding.thread_id;
46
- }
47
- if (route.threadId && await this.threadExists(route.threadId)) {
48
- return route.threadId;
49
- }
50
- const thread = await workspaceRouter.createThread(job.workspaceId, job.description || `Scheduled ${job.platform} task`);
51
- const now = new Date().toISOString();
52
- this.options.store.upsertPlatformThreadBinding({
53
- workspace_id: job.workspaceId,
54
- platform: job.platform,
55
- chat_id: channelId,
56
- platform_user_id: participantId,
57
- thread_id: thread.id,
58
- last_platform_message_id: null,
59
- created_at: now,
60
- updated_at: now,
61
- });
62
- const authorized = this.options.store.getAuthorizedUser(job.workspaceId, participantId, job.platform);
63
- if (authorized) {
64
- this.options.store.updateAuthorizedUserThread(job.workspaceId, participantId, thread.id, job.platform);
65
- }
66
- return thread.id;
67
- }
68
- async threadExists(threadId) {
69
- try {
70
- await this.options.getWorkspaceRouter().getThread(threadId);
71
- return true;
72
- }
73
- catch {
74
- return false;
75
- }
76
- }
77
- preferredAgentFor(job) {
78
- const route = job.route;
79
- const channelId = route.channelId;
80
- if (!channelId)
81
- return '';
82
- const participantId = route.participantId || '';
83
- const binding = this.options.store.getPlatformThreadBinding(job.workspaceId, channelId, participantId, job.platform);
84
- return binding?.preferred_agent_type || '';
85
- }
86
20
  }
87
21
  exports.LarkScheduleAdapter = LarkScheduleAdapter;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalScheduleAdapter = void 0;
4
4
  const scheduled_conversation_executor_js_1 = require("./scheduled-conversation-executor.js");
5
+ const thread_resolution_js_1 = require("./thread-resolution.js");
5
6
  class LocalScheduleAdapter {
6
7
  options;
7
8
  executor;
@@ -40,7 +41,7 @@ class LocalScheduleAdapter {
40
41
  }
41
42
  async resolveThread(job) {
42
43
  const workspaceRouter = this.options.getWorkspaceRouter();
43
- if (job.route.threadId && await this.threadExists(job.route.threadId)) {
44
+ if (job.route.threadId && await (0, thread_resolution_js_1.threadExists)(workspaceRouter, job.route.threadId)) {
44
45
  return job.route.threadId;
45
46
  }
46
47
  const title = `[Scheduled] ${job.description || job.id}`;
@@ -52,14 +53,5 @@ class LocalScheduleAdapter {
52
53
  const created = await workspaceRouter.createThread(job.workspaceId, title);
53
54
  return created.id;
54
55
  }
55
- async threadExists(threadId) {
56
- try {
57
- await this.options.getWorkspaceRouter().getThread(threadId);
58
- return true;
59
- }
60
- catch {
61
- return false;
62
- }
63
- }
64
56
  }
65
57
  exports.LocalScheduleAdapter = LocalScheduleAdapter;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.waitForRunCompletion = waitForRunCompletion;
4
+ const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'interrupted']);
5
+ const RUN_POLL_INTERVAL_MS = 300;
6
+ async function waitForRunCompletion(store, runId, timeoutMs, label) {
7
+ const startedAt = Date.now();
8
+ while (Date.now() - startedAt < timeoutMs) {
9
+ const run = store.getRun(runId);
10
+ if (run && TERMINAL_RUN_STATES.has(run.status)) {
11
+ if (run.status !== 'completed') {
12
+ throw new Error(`${label} run finished with status ${run.status}`);
13
+ }
14
+ return run;
15
+ }
16
+ await new Promise((resolve) => setTimeout(resolve, RUN_POLL_INTERVAL_MS));
17
+ }
18
+ throw new Error(`Timed out waiting for ${label.toLowerCase()} run ${runId}`);
19
+ }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ScheduledConversationExecutor = void 0;
4
4
  const scheduled_job_route_js_1 = require("./scheduled-job-route.js");
5
- const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'interrupted']);
5
+ const run_polling_js_1 = require("./run-polling.js");
6
6
  const SCHEDULED_RUN_PERMISSION_MODE = 'bypassPermissions';
7
7
  class ScheduledConversationExecutor {
8
8
  options;
@@ -16,9 +16,9 @@ class ScheduledConversationExecutor {
16
16
  const workspaceRouter = this.options.getWorkspaceRouter();
17
17
  const sendResult = await workspaceRouter.sendThreadMessage(target.threadId, prompt, {
18
18
  permissionMode: SCHEDULED_RUN_PERMISSION_MODE,
19
- runtimeEnv: this.buildScheduledRuntimeEnv(target),
19
+ runtimeEnv: (0, scheduled_job_route_js_1.buildPlatformRuntimeEnv)(target.platform, target.route),
20
20
  });
21
- await this.waitForRun(sendResult.runId, timeoutMs);
21
+ await (0, run_polling_js_1.waitForRunCompletion)(this.options.store, sendResult.runId, timeoutMs, 'Scheduled');
22
22
  const thread = await workspaceRouter.getThread(target.threadId);
23
23
  const replyText = [...thread.messages]
24
24
  .reverse()
@@ -34,39 +34,5 @@ class ScheduledConversationExecutor {
34
34
  await policy.afterExecute?.(target, job);
35
35
  }
36
36
  }
37
- buildScheduledRuntimeEnv(target) {
38
- const route = target.route;
39
- const basePlatform = (0, scheduled_job_route_js_1.getChannelPlatformBase)(target.platform);
40
- const env = {};
41
- if (basePlatform && basePlatform !== 'local') {
42
- env.LOCAL_AI_PLATFORM = basePlatform;
43
- env.LOCAL_AI_ROUTE_TYPE = (0, scheduled_job_route_js_1.routeTypeForPlatform)(target.platform);
44
- }
45
- const instanceId = route.instanceId || (0, scheduled_job_route_js_1.getChannelPlatformInstanceId)(target.platform);
46
- if (instanceId) {
47
- env.LOCAL_AI_PLATFORM_INSTANCE_ID = instanceId;
48
- }
49
- if (route.channelId) {
50
- env.LOCAL_AI_CHAT_ID = route.channelId;
51
- }
52
- if (route.participantId) {
53
- env.LOCAL_AI_PLATFORM_USER_ID = route.participantId;
54
- }
55
- return env;
56
- }
57
- async waitForRun(runId, timeoutMs) {
58
- const startedAt = Date.now();
59
- while (Date.now() - startedAt < timeoutMs) {
60
- const run = this.options.store.getRun(runId);
61
- if (run && TERMINAL_RUN_STATES.has(run.status)) {
62
- if (run.status !== 'completed') {
63
- throw new Error(`Scheduled run finished with status ${run.status}`);
64
- }
65
- return run;
66
- }
67
- await new Promise((resolve) => setTimeout(resolve, 300));
68
- }
69
- throw new Error(`Timed out waiting for scheduled run ${runId}`);
70
- }
71
37
  }
72
38
  exports.ScheduledConversationExecutor = ScheduledConversationExecutor;
@@ -6,6 +6,7 @@ exports.getChannelPlatformInstanceId = getChannelPlatformInstanceId;
6
6
  exports.platformMatches = platformMatches;
7
7
  exports.routeWithPlatformInstance = routeWithPlatformInstance;
8
8
  exports.routeTypeForPlatform = routeTypeForPlatform;
9
+ exports.buildPlatformRuntimeEnv = buildPlatformRuntimeEnv;
9
10
  exports.routeFromPlatformThreadBinding = routeFromPlatformThreadBinding;
10
11
  exports.scheduledJobMatchesPlatformBinding = scheduledJobMatchesPlatformBinding;
11
12
  exports.scheduledJobMatchesCliContext = scheduledJobMatchesCliContext;
@@ -32,6 +33,25 @@ function routeTypeForPlatform(platform) {
32
33
  const base = getChannelPlatformBase(platform);
33
34
  return base === 'lark' || base === 'weixin' ? 'channel.chat' : base;
34
35
  }
36
+ function buildPlatformRuntimeEnv(platform, route) {
37
+ const basePlatform = getChannelPlatformBase(platform);
38
+ const env = {};
39
+ if (basePlatform && basePlatform !== 'local') {
40
+ env.LOCAL_AI_PLATFORM = basePlatform;
41
+ env.LOCAL_AI_ROUTE_TYPE = routeTypeForPlatform(platform);
42
+ }
43
+ const instanceId = route.instanceId || getChannelPlatformInstanceId(platform);
44
+ if (instanceId) {
45
+ env.LOCAL_AI_PLATFORM_INSTANCE_ID = instanceId;
46
+ }
47
+ if (route.channelId) {
48
+ env.LOCAL_AI_CHAT_ID = route.channelId;
49
+ }
50
+ if (route.participantId) {
51
+ env.LOCAL_AI_PLATFORM_USER_ID = route.participantId;
52
+ }
53
+ return env;
54
+ }
35
55
  function routeFromPlatformThreadBinding(binding) {
36
56
  return {
37
57
  type: routeTypeForPlatform(binding.platform),
@@ -16,10 +16,18 @@ class SchedulerService extends node_events_1.EventEmitter {
16
16
  this.options = options;
17
17
  this.runLifecycle = new scheduler_run_lifecycle_js_1.SchedulerRunLifecycle({
18
18
  store: options.store,
19
- emitRun: (run) => this.emit('run', run),
20
- emitJob: (job) => this.emit('job', job),
19
+ emitRun: (run) => this.emitRun(run),
20
+ emitJob: (job) => this.emitJob(job),
21
21
  });
22
22
  }
23
+ emitJob(job) {
24
+ this.emit('job', job);
25
+ this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: job });
26
+ }
27
+ emitRun(run) {
28
+ this.emit('run', run);
29
+ this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: run });
30
+ }
23
31
  async start() {
24
32
  await this.tick();
25
33
  this.timer = setInterval(() => {
@@ -41,14 +49,12 @@ class SchedulerService extends node_events_1.EventEmitter {
41
49
  }
42
50
  createJob(input) {
43
51
  const job = this.options.store.createScheduledJob(input);
44
- this.emit('job', job);
45
- this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: job });
52
+ this.emitJob(job);
46
53
  return job;
47
54
  }
48
55
  updateJob(jobId, input) {
49
56
  const job = this.options.store.updateScheduledJob(this.resolveRequiredJobId(jobId), input);
50
- this.emit('job', job);
51
- this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: job });
57
+ this.emitJob(job);
52
58
  return job;
53
59
  }
54
60
  deleteJob(jobId) {
@@ -115,39 +121,21 @@ class SchedulerService extends node_events_1.EventEmitter {
115
121
  }
116
122
  async executeJob(job, triggeredAt, manual) {
117
123
  if (this.runningJobs.has(job.id)) {
118
- const skipped = this.runLifecycle.markSkipped(job, triggeredAt, 'Skipped because the previous run is still active.');
119
- this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: skipped });
120
- return skipped;
124
+ return this.runLifecycle.markSkipped(job, triggeredAt, 'Skipped because the previous run is still active.');
121
125
  }
122
126
  this.runningJobs.add(job.id);
123
127
  const run = this.runLifecycle.markQueued(job, triggeredAt);
124
- this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: run });
125
128
  try {
126
129
  const executor = this.options.executors.find((candidate) => candidate.supports(job));
127
130
  if (!executor) {
128
131
  throw new Error(`No scheduler executor is available for delivery target "${job.platform}"`);
129
132
  }
130
133
  this.runLifecycle.markRunning(run.id);
131
- const running = this.options.store.getScheduledJobRun(run.id);
132
- if (running) {
133
- this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: running });
134
- }
135
134
  const result = await executor.execute({ job, triggeredAt });
136
- const succeeded = this.runLifecycle.markSucceeded(job, run.id, result, !manual && job.triggerType === 'once');
137
- this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: succeeded });
138
- const nextJob = this.options.store.getScheduledJob(job.id);
139
- if (nextJob) {
140
- this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: nextJob });
141
- }
142
- return succeeded;
135
+ return this.runLifecycle.markSucceeded(job, run.id, result, !manual && job.triggerType === 'once');
143
136
  }
144
137
  catch (error) {
145
138
  const failed = this.runLifecycle.markFailed(job.id, run.id, error instanceof Error ? error.message : String(error));
146
- this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: failed });
147
- const nextJob = this.options.store.getScheduledJob(job.id);
148
- if (nextJob) {
149
- this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: nextJob });
150
- }
151
139
  this.options.log?.(`scheduler job failed ${job.id}: ${failed.error || 'unknown error'}`);
152
140
  this.maybeAutoDisableAfterFailure(job.id);
153
141
  return failed;
@@ -173,8 +161,7 @@ class SchedulerService extends node_events_1.EventEmitter {
173
161
  this.options.store.updateScheduledJobStatus(jobId, { enabled: false });
174
162
  const disabled = this.options.store.getScheduledJob(jobId);
175
163
  if (disabled) {
176
- this.emit('job', disabled);
177
- this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: disabled });
164
+ this.emitJob(disabled);
178
165
  }
179
166
  this.options.log?.(`scheduler auto-disabled job ${jobId} after ${consecutiveFailures} consecutive failures`);
180
167
  }