@kafca/agentdock 0.1.42 → 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 (98) hide show
  1. package/README.md +4 -0
  2. package/dist/renderer/assets/{Badge-D-WU4kUV.js → Badge-CbG6yRSm.js} +1 -1
  3. package/dist/renderer/assets/{Chat-BGkC5tAe.js → Chat-BvNNWGGX.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-CfbC1jRd.js → ChatMarkdown-Ct-PvJB8.js} +1 -1
  5. package/dist/renderer/assets/{Config-qQb4bX5D.js → Config-lQX2qVVV.js} +2 -2
  6. package/dist/renderer/assets/CronList-Dj_3muVG.js +1 -0
  7. package/dist/renderer/assets/{Dashboard-xn6cUy_n.js → Dashboard-eh_RhGYb.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-BdKBBUpr.js → EmptyState-Bn2Qh14U.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-Duk8IQP3.js → KnowledgeDetail-Bsz6Ou41.js} +2 -2
  10. package/dist/renderer/assets/{KnowledgeHome-BfjX_4Sv.js → KnowledgeHome-Cgj4XFXn.js} +2 -2
  11. package/dist/renderer/assets/{Logs-Bfq61QMn.js → Logs-Dp-NF6GN.js} +1 -1
  12. package/dist/renderer/assets/{Modal-B3vlBmBr.js → Modal-B6YPPBN6.js} +1 -1
  13. package/dist/renderer/assets/MonitorList-xEZWg460.js +1 -0
  14. package/dist/renderer/assets/{Page-CPsLOP3z.js → Page-Bn7RjGOS.js} +1 -1
  15. package/dist/renderer/assets/{ProjectDetail-D-g46wyF.js → ProjectDetail-Cv_jIE61.js} +2 -2
  16. package/dist/renderer/assets/{ProjectList-BrQEKDQ8.js → ProjectList-BDk5_gqY.js} +1 -1
  17. package/dist/renderer/assets/{Select-C4CFMQmF.js → Select-xshfMIEQ.js} +1 -1
  18. package/dist/renderer/assets/SessionChat-BkHy90z3.js +1 -0
  19. package/dist/renderer/assets/{SessionList-DPWIUbOg.js → SessionList-BZyXT92L.js} +1 -1
  20. package/dist/renderer/assets/{ThreadChat-BjtU-j9q.js → ThreadChat-DUOIyXrS.js} +7 -7
  21. package/dist/renderer/assets/{Workspace-BcNmHuJx.js → Workspace-Dl80fNEC.js} +1 -1
  22. package/dist/renderer/assets/{arrow-left-DPzSmidS.js → arrow-left-C54ZI-AS.js} +1 -1
  23. package/dist/renderer/assets/{book-open-B0xhoRqZ.js → book-open-Dl7GO0zn.js} +1 -1
  24. package/dist/renderer/assets/{check-BTJTyd0C.js → check-CiqgtrOL.js} +1 -1
  25. package/dist/renderer/assets/{chevron-down-B6jJXlhi.js → chevron-down-DTL2OuVh.js} +1 -1
  26. package/dist/renderer/assets/{circle-qRhJR9qR.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-CbYKGTn8.js → funnel-CrmnLMJI.js} +1 -1
  31. package/dist/renderer/assets/{heart-DjwWJfkz.js → heart-BMfQI7y_.js} +1 -1
  32. package/dist/renderer/assets/{index-BWmHJRhe.js → index-HPNb1zfx.js} +39 -34
  33. package/dist/renderer/assets/{index-CyZs_2NJ.js → index-nreiKY1f.js} +3 -3
  34. package/dist/renderer/assets/{pencil-1dfAivmj.js → pencil-wTQ4l465.js} +1 -1
  35. package/dist/renderer/assets/{play-CB3w1G6S.js → play-BK--MXmw.js} +1 -1
  36. package/dist/renderer/assets/{plug-Bvep5o3r.js → plug-BIx9fjip.js} +1 -1
  37. package/dist/renderer/assets/{plus-DJVg9m09.js → plus-Dn7JFWQ8.js} +1 -1
  38. package/dist/renderer/assets/projects-brJZgwEJ.js +1 -0
  39. package/dist/renderer/assets/{save-xjJn3qf2.js → save-CC3zOSWb.js} +1 -1
  40. package/dist/renderer/assets/{search-BK3UrQxM.js → search-CDTy9Czu.js} +1 -1
  41. package/dist/renderer/assets/{server-Dyd2eXJu.js → server-DtQk3t2a.js} +1 -1
  42. package/dist/renderer/assets/{session-utils-eXC0020s.js → session-utils-4ep5D66T.js} +1 -1
  43. package/dist/renderer/assets/{sessions-WLYpkyUt.js → sessions-Dor1LteH.js} +1 -1
  44. package/dist/renderer/assets/{shield-check-C0N6aWzF.js → shield-check-Cz2LpU6r.js} +1 -1
  45. package/dist/renderer/assets/{trash-2-BGMTNJA6.js → trash-2-C2EmWFZV.js} +1 -1
  46. package/dist/renderer/assets/{user-BcGMLyhu.js → user-I8B-cs5A.js} +1 -1
  47. package/dist/renderer/assets/{wifi-off-FhcQx0HK.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 +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/local-core-lark-gateway.js +101 -29
  72. package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +53 -0
  73. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +51 -33
  74. package/dist-electron/services/local-ai-core/src/cli/lac.js +196 -0
  75. package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +37 -0
  76. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +43 -0
  77. package/dist-electron/services/local-ai-core/src/kernel/capability-registry.js +12 -0
  78. package/dist-electron/services/local-ai-core/src/plugins/builtin/monitor-stock-plugin.js +30 -0
  79. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +41 -0
  80. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +31 -0
  81. package/dist-electron/services/local-ai-core/src/runtime/server.js +36 -0
  82. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-bridge-session.js +25 -11
  83. package/dist-electron/services/local-ai-core/src/thread/session-command-service.js +275 -0
  84. package/dist-electron/services/local-ai-core/src/thread/slash-command-registry.js +27 -0
  85. package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +15 -15
  86. package/dist-electron/shared/desktop.js +21 -1
  87. package/dist-electron/tests/contracts/local-core-contracts.test.js +9 -0
  88. package/dist-electron/tests/electron/automation-monitor.test.js +38 -0
  89. package/dist-electron/tests/electron/lac-cli.test.js +146 -0
  90. package/dist-electron/tests/integration/local-core-refactor.test.js +194 -0
  91. package/package.json +1 -1
  92. package/dist/renderer/assets/CronList-CKhggzYk.js +0 -1
  93. package/dist/renderer/assets/SessionChat-06Jny5_G.js +0 -1
  94. package/dist/renderer/assets/desktop-DXmXjjUR.js +0 -1
  95. package/dist/renderer/assets/desktop-O8_6ZCVZ.js +0 -4
  96. package/dist/renderer/assets/en-CKhCF6St.js +0 -1
  97. package/dist/renderer/assets/projects-xnW0fTtq.js +0 -1
  98. package/dist/renderer/assets/zh-Dz5FLTBV.js +0 -1
@@ -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
+ }
@@ -17,10 +17,12 @@ const scheduler_cron_plugin_js_1 = require("../plugins/builtin/scheduler-cron-pl
17
17
  const scheduler_lark_plugin_js_1 = require("../plugins/builtin/scheduler-lark-plugin.js");
18
18
  const scheduler_local_plugin_js_1 = require("../plugins/builtin/scheduler-local-plugin.js");
19
19
  const scheduler_weixin_plugin_js_1 = require("../plugins/builtin/scheduler-weixin-plugin.js");
20
+ const monitor_stock_plugin_js_1 = require("../plugins/builtin/monitor-stock-plugin.js");
20
21
  const workspace_router_js_1 = require("../router/workspace-router.js");
21
22
  const local_core_runtime_state_js_1 = require("../runtime/local-core-runtime-state.js");
22
23
  const scheduled_job_application_service_js_1 = require("../scheduler/scheduled-job-application-service.js");
23
24
  const scheduler_service_js_1 = require("../scheduler/scheduler-service.js");
25
+ const automation_monitor_service_js_1 = require("../automation/automation-monitor-service.js");
24
26
  function bootstrapLocalCoreKernel(options) {
25
27
  const capabilities = new capability_registry_js_1.LocalCoreCapabilityRegistry();
26
28
  const plugins = new plugin_registry_js_1.LocalCorePluginRegistry(options?.disabledPluginIds);
@@ -59,6 +61,7 @@ function bootstrapLocalCoreKernel(options) {
59
61
  diagnostics,
60
62
  getCapabilitySnapshot() {
61
63
  const snapshot = capabilities.snapshot();
64
+ const monitorCapabilities = snapshot.monitors || [];
62
65
  return {
63
66
  adapters: {
64
67
  channels: snapshot.channels.map((capability) => capability.platform),
@@ -74,6 +77,14 @@ function bootstrapLocalCoreKernel(options) {
74
77
  deliveryTargets: [...new Set(snapshot.schedulers.flatMap((capability) => capability.deliveryTargets || capability.deliveryPlatforms || []))],
75
78
  platforms: [...new Set(snapshot.schedulers.flatMap((capability) => capability.deliveryTargets || capability.deliveryPlatforms || []))],
76
79
  },
80
+ ...(monitorCapabilities.length > 0
81
+ ? {
82
+ monitors: {
83
+ enabled: monitorCapabilities.some((capability) => capability.enabled !== false),
84
+ sourceTypes: [...new Set(monitorCapabilities.flatMap((capability) => capability.sourceTypes))],
85
+ },
86
+ }
87
+ : {}),
77
88
  snapshot,
78
89
  };
79
90
  },
@@ -99,6 +110,7 @@ function logCapabilityContributions(plugin, log) {
99
110
  ...(plugin.capabilities.channels || []).map((capability) => capability.id),
100
111
  ...(plugin.capabilities.knowledge || []).map((capability) => capability.id),
101
112
  ...(plugin.capabilities.schedulers || []).map((capability) => capability.id),
113
+ ...(plugin.capabilities.monitors || []).map((capability) => capability.id),
102
114
  ...(plugin.capabilities.ui || []).map((capability) => capability.id),
103
115
  ];
104
116
  for (const capabilityId of capabilityIds) {
@@ -145,6 +157,16 @@ function resolveSchedulerRuntime(plugin, context) {
145
157
  }
146
158
  return runtime;
147
159
  }
160
+ function resolveMonitorRuntime(plugin, context) {
161
+ if (!plugin.createRuntime) {
162
+ throw new Error(`Monitor plugin ${plugin.manifest.id} does not provide a runtime factory.`);
163
+ }
164
+ const runtime = plugin.createRuntime(context);
165
+ if (runtime instanceof Promise) {
166
+ throw new Error(`Monitor plugin ${plugin.manifest.id} returned an async runtime factory during synchronous bootstrap.`);
167
+ }
168
+ return runtime;
169
+ }
148
170
  function bootstrapLocalCoreRuntime(options) {
149
171
  const state = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({
150
172
  userDataPath: options.userDataPath,
@@ -209,6 +231,9 @@ function bootstrapLocalCoreRuntime(options) {
209
231
  log: options.log,
210
232
  }),
211
233
  ];
234
+ const monitorPlugins = [
235
+ (0, monitor_stock_plugin_js_1.createBuiltinStockMonitorPlugin)(),
236
+ ];
212
237
  for (const plugin of agentPlugins.filter((plugin) => plugin !== localCoreAgentPlugin)) {
213
238
  registerPlugin(kernel, plugin);
214
239
  }
@@ -218,6 +243,9 @@ function bootstrapLocalCoreRuntime(options) {
218
243
  for (const plugin of schedulerPlugins) {
219
244
  registerPlugin(kernel, plugin);
220
245
  }
246
+ for (const plugin of monitorPlugins) {
247
+ registerPlugin(kernel, plugin);
248
+ }
221
249
  const agentRuntimes = agentPlugins
222
250
  .filter((plugin) => kernel.plugins.isEnabled(plugin.manifest.id))
223
251
  .map((plugin) => resolveAgentRuntime(plugin, kernel.context).runtime);
@@ -241,6 +269,10 @@ function bootstrapLocalCoreRuntime(options) {
241
269
  ];
242
270
  const schedulerTriggers = schedulerRuntimes.flatMap((runtime) => runtime.triggers || []);
243
271
  const schedulerExecutors = schedulerRuntimes.flatMap((runtime) => runtime.executors || []);
272
+ const monitorRuntimes = monitorPlugins
273
+ .filter((plugin) => kernel.plugins.isEnabled(plugin.manifest.id))
274
+ .map((plugin) => resolveMonitorRuntime(plugin, kernel.context));
275
+ const monitorProviders = monitorRuntimes.flatMap((runtime) => runtime.providers || []);
244
276
  const knowledgeProvider = knowledgeRuntime.provider;
245
277
  const knowledgeAttachments = knowledgeRuntime.attachments;
246
278
  workspaceRouter = (0, workspace_router_js_1.createWorkspaceRouter)({
@@ -266,6 +298,14 @@ function bootstrapLocalCoreRuntime(options) {
266
298
  store,
267
299
  scheduler,
268
300
  });
301
+ const automationMonitors = new automation_monitor_service_js_1.AutomationMonitorService({
302
+ store,
303
+ providers: monitorProviders,
304
+ getWorkspaceRouter: () => workspaceRouter,
305
+ getChannelRuntime: (platform) => channelRuntimes.find((runtime) => runtime.platform === platform || platform.startsWith(`${runtime.platform}:`)),
306
+ eventBus: kernel.context.bus,
307
+ log: options.log,
308
+ });
269
309
  workspaceRouter.setSchedulerBridge({
270
310
  createJob: async ({ workspaceId, platform, route, name, schedule, scheduleDescription, message }) => scheduledJobs.createCronJob({ workspaceId, platform, route, name, schedule, scheduleDescription, message }),
271
311
  listJobsForThread: async (threadId) => scheduledJobs.listJobsForThread(threadId),
@@ -286,11 +326,14 @@ function bootstrapLocalCoreRuntime(options) {
286
326
  workspaceRouter,
287
327
  scheduler,
288
328
  scheduledJobs,
329
+ automationMonitors,
289
330
  async start() {
290
331
  await kernel.lifecycle.startAll();
291
332
  await scheduler.start();
333
+ await automationMonitors.start();
292
334
  },
293
335
  async stop() {
336
+ await automationMonitors.stop();
294
337
  await scheduler.stop();
295
338
  await kernel.lifecycle.stopAll();
296
339
  workspaceRouter.close();
@@ -6,6 +6,7 @@ class LocalCoreCapabilityRegistry {
6
6
  channels = new Map();
7
7
  knowledge = new Map();
8
8
  schedulers = new Map();
9
+ monitors = new Map();
9
10
  ui = new Map();
10
11
  registerAgent(capability) {
11
12
  this.agents.set(capability.id, capability);
@@ -19,6 +20,9 @@ class LocalCoreCapabilityRegistry {
19
20
  registerScheduler(capability) {
20
21
  this.schedulers.set(capability.id, capability);
21
22
  }
23
+ registerMonitor(capability) {
24
+ this.monitors.set(capability.id, capability);
25
+ }
22
26
  registerUi(capability) {
23
27
  this.ui.set(capability.id, capability);
24
28
  }
@@ -35,6 +39,9 @@ class LocalCoreCapabilityRegistry {
35
39
  for (const capability of contributions.schedulers || []) {
36
40
  this.registerScheduler(capability);
37
41
  }
42
+ for (const capability of contributions.monitors || []) {
43
+ this.registerMonitor(capability);
44
+ }
38
45
  for (const capability of contributions.ui || []) {
39
46
  this.registerUi(capability);
40
47
  }
@@ -51,15 +58,20 @@ class LocalCoreCapabilityRegistry {
51
58
  listSchedulers() {
52
59
  return [...this.schedulers.values()];
53
60
  }
61
+ listMonitors() {
62
+ return [...this.monitors.values()];
63
+ }
54
64
  listUi() {
55
65
  return [...this.ui.values()];
56
66
  }
57
67
  snapshot() {
68
+ const monitors = this.listMonitors();
58
69
  return {
59
70
  agents: this.listAgents(),
60
71
  channels: this.listChannels(),
61
72
  knowledge: this.listKnowledge(),
62
73
  schedulers: this.listSchedulers(),
74
+ ...(monitors.length > 0 ? { monitors } : {}),
63
75
  ui: this.listUi(),
64
76
  };
65
77
  }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBuiltinStockMonitorPlugin = createBuiltinStockMonitorPlugin;
4
+ const mock_stock_provider_js_1 = require("../../automation/mock-stock-provider.js");
5
+ function createBuiltinStockMonitorPlugin() {
6
+ return {
7
+ manifest: {
8
+ id: 'builtin.monitor-stock',
9
+ kind: 'monitor',
10
+ version: '0.1.0',
11
+ provides: ['monitor.source.stock.quote'],
12
+ },
13
+ capabilities: {
14
+ monitors: [
15
+ {
16
+ id: 'monitor.source.stock.quote',
17
+ sourceTypes: ['stock.quote'],
18
+ modes: ['poll'],
19
+ enabled: true,
20
+ displayName: 'Stock Quote Monitor',
21
+ },
22
+ ],
23
+ },
24
+ createRuntime() {
25
+ return {
26
+ providers: [new mock_stock_provider_js_1.MockStockQuoteProvider()],
27
+ };
28
+ },
29
+ };
30
+ }
@@ -12,6 +12,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
12
12
  knowledgeProvider;
13
13
  channelRuntimes;
14
14
  scheduledJobs;
15
+ automationMonitors;
15
16
  kernel;
16
17
  runtime;
17
18
  runtimeDetection;
@@ -33,6 +34,15 @@ class LocalCoreController extends node_events_1.EventEmitter {
33
34
  const runtimeChannels = this.runtime.channelRuntimes || [this.runtime.channelRuntime, this.runtime.weixinChannelRuntime];
34
35
  this.channelRuntimes = new Map(runtimeChannels.filter(Boolean).map((runtime) => [runtime.platform, runtime]));
35
36
  this.scheduledJobs = this.runtime.scheduledJobs;
37
+ this.automationMonitors = this.runtime.automationMonitors || {
38
+ listMonitors: () => [],
39
+ getMonitor: () => undefined,
40
+ createMonitor: () => { throw new Error('Automation monitor service is not available.'); },
41
+ updateMonitor: () => { throw new Error('Automation monitor service is not available.'); },
42
+ deleteMonitor: () => ({ deleted: false }),
43
+ runMonitorNow: async () => { throw new Error('Automation monitor service is not available.'); },
44
+ listRuns: () => [],
45
+ };
36
46
  this.runtimeDetection = new runtime_detection_service_js_1.RuntimeDetectionService({
37
47
  userDataPath,
38
48
  readConfig: async () => (await this.readConfigFile()).parsed,
@@ -42,10 +52,16 @@ class LocalCoreController extends node_events_1.EventEmitter {
42
52
  this.flushPendingLogs();
43
53
  this.busUnsubscribers.push(this.kernel.context.bus.on('platform.bridge.updated', (event) => {
44
54
  this.emit('bridge', event);
55
+ }), this.kernel.context.bus.on('thread.session.activated', (event) => {
56
+ this.emit('thread-session-activated', event);
45
57
  }), this.kernel.context.bus.on('scheduler.job.updated', (job) => {
46
58
  this.emit('scheduler-job', job);
47
59
  }), this.kernel.context.bus.on('scheduler.run.updated', (run) => {
48
60
  this.emit('scheduler-run', run);
61
+ }), this.kernel.context.bus.on('automation.monitor.updated', (monitor) => {
62
+ this.emit('automation-monitor', monitor);
63
+ }), this.kernel.context.bus.on('automation.monitor.run.updated', (run) => {
64
+ this.emit('automation-monitor-run', run);
49
65
  }), this.kernel.context.bus.on('runtime.state.changed', () => {
50
66
  void this.emitRuntime();
51
67
  }));
@@ -208,6 +224,31 @@ class LocalCoreController extends node_events_1.EventEmitter {
208
224
  async listScheduledJobRuns(jobId) {
209
225
  return this.scheduledJobs.listJobRuns(jobId);
210
226
  }
227
+ async listAutomationMonitors(workspaceId) {
228
+ return this.automationMonitors.listMonitors(workspaceId);
229
+ }
230
+ async getAutomationMonitor(monitorId) {
231
+ const monitor = this.automationMonitors.getMonitor(monitorId);
232
+ if (!monitor) {
233
+ throw new Error(`Automation monitor not found: ${monitorId}`);
234
+ }
235
+ return monitor;
236
+ }
237
+ async createAutomationMonitor(input) {
238
+ return this.automationMonitors.createMonitor(input);
239
+ }
240
+ async updateAutomationMonitor(monitorId, input) {
241
+ return this.automationMonitors.updateMonitor(monitorId, input);
242
+ }
243
+ async deleteAutomationMonitor(monitorId) {
244
+ return this.automationMonitors.deleteMonitor(monitorId);
245
+ }
246
+ async runAutomationMonitor(monitorId) {
247
+ return this.automationMonitors.runMonitorNow(monitorId);
248
+ }
249
+ async listAutomationMonitorRuns(monitorId) {
250
+ return this.automationMonitors.listRuns(monitorId);
251
+ }
211
252
  async listThreads(workspaceId) {
212
253
  return this.workspaceRouter.listThreads(workspaceId);
213
254
  }
@@ -48,6 +48,9 @@ function parseLocalAiCoreRoute(method, path) {
48
48
  if (segments[0] === 'scheduler' && segments[1] === 'jobs') {
49
49
  return parseSchedulerJobsRoute(normalizedMethod, segments);
50
50
  }
51
+ if (segments[0] === 'automation' && segments[1] === 'monitors') {
52
+ return parseAutomationMonitorsRoute(normalizedMethod, segments);
53
+ }
51
54
  if (segments[0] === 'threads') {
52
55
  return parseThreadsRoute(normalizedMethod, segments);
53
56
  }
@@ -92,6 +95,34 @@ function parseLocalAiCoreRoute(method, path) {
92
95
  }
93
96
  return null;
94
97
  }
98
+ function parseAutomationMonitorsRoute(method, segments) {
99
+ if (method === 'GET' && segments.length === 2) {
100
+ return { name: 'automation.monitors.list' };
101
+ }
102
+ if (method === 'POST' && segments.length === 2) {
103
+ return { name: 'automation.monitors.create' };
104
+ }
105
+ const monitorId = segments.length >= 3 ? decodeURIComponent(segments[2] || '') : '';
106
+ if (!monitorId) {
107
+ return null;
108
+ }
109
+ if (method === 'GET' && segments.length === 3) {
110
+ return { name: 'automation.monitor.get', monitorId };
111
+ }
112
+ if (method === 'GET' && segments.length === 4 && segments[3] === 'runs') {
113
+ return { name: 'automation.monitor.runs', monitorId };
114
+ }
115
+ if (method === 'POST' && segments.length === 4 && segments[3] === 'run') {
116
+ return { name: 'automation.monitor.run', monitorId };
117
+ }
118
+ if (method === 'PATCH' && segments.length === 3) {
119
+ return { name: 'automation.monitor.update', monitorId };
120
+ }
121
+ if (method === 'DELETE' && segments.length === 3) {
122
+ return { name: 'automation.monitor.delete', monitorId };
123
+ }
124
+ return null;
125
+ }
95
126
  function parseRuntimesRoute(method, segments) {
96
127
  if (method === 'GET' && segments.length === 1) {
97
128
  return { name: 'runtimes.list' };
@@ -62,12 +62,21 @@ class LocalAiCoreServer {
62
62
  });
63
63
  }
64
64
  });
65
+ this.bindings.on('thread-session-activated', (event) => {
66
+ this.broadcast({ type: 'thread.session.activated', ...event });
67
+ });
65
68
  this.bindings.on('scheduler-job', (job) => {
66
69
  this.broadcast({ type: 'scheduler.job.updated', job });
67
70
  });
68
71
  this.bindings.on('scheduler-run', (run) => {
69
72
  this.broadcast({ type: 'scheduler.run.updated', run });
70
73
  });
74
+ this.bindings.on('automation-monitor', (monitor) => {
75
+ this.broadcast({ type: 'automation.monitor.updated', monitor });
76
+ });
77
+ this.bindings.on('automation-monitor-run', (run) => {
78
+ this.broadcast({ type: 'automation.monitor.run.updated', run });
79
+ });
71
80
  this.bindings.on('runtime-detection', (event) => {
72
81
  this.broadcast(event);
73
82
  });
@@ -226,6 +235,33 @@ class LocalAiCoreServer {
226
235
  case 'scheduler.job.delete':
227
236
  json(res, 200, await this.bindings.deleteScheduledJob(route.jobId));
228
237
  return;
238
+ case 'automation.monitors.list': {
239
+ const workspaceId = String(url.searchParams.get('workspace_id') || '');
240
+ json(res, 200, { monitors: await this.bindings.listAutomationMonitors(workspaceId || undefined) });
241
+ return;
242
+ }
243
+ case 'automation.monitors.create': {
244
+ const body = await readJsonBody(req);
245
+ json(res, 200, await this.bindings.createAutomationMonitor(body));
246
+ return;
247
+ }
248
+ case 'automation.monitor.get':
249
+ json(res, 200, await this.bindings.getAutomationMonitor(route.monitorId));
250
+ return;
251
+ case 'automation.monitor.runs':
252
+ json(res, 200, { runs: await this.bindings.listAutomationMonitorRuns(route.monitorId) });
253
+ return;
254
+ case 'automation.monitor.run':
255
+ json(res, 200, await this.bindings.runAutomationMonitor(route.monitorId));
256
+ return;
257
+ case 'automation.monitor.update': {
258
+ const body = await readJsonBody(req);
259
+ json(res, 200, await this.bindings.updateAutomationMonitor(route.monitorId, body));
260
+ return;
261
+ }
262
+ case 'automation.monitor.delete':
263
+ json(res, 200, await this.bindings.deleteAutomationMonitor(route.monitorId));
264
+ return;
229
265
  case 'threads.list': {
230
266
  const workspaceId = String(url.searchParams.get('workspace_id') || '');
231
267
  json(res, 200, { threads: workspaceId ? await this.bindings.listThreads(workspaceId) : [] });