@kafca/agentdock 0.1.61 → 0.1.62

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 (127) hide show
  1. package/README.md +18 -0
  2. package/dist/renderer/assets/AutomationList-BgJgAdVO.js +16 -0
  3. package/dist/renderer/assets/{Badge-BLI4LHse.js → Badge-Bq2hPVcR.js} +1 -1
  4. package/dist/renderer/assets/{Card-DJeLDdL3.js → Card-BPJNSZce.js} +1 -1
  5. package/dist/renderer/assets/{Config-D1awc81A.js → Config-aSFqlI88.js} +2 -2
  6. package/dist/renderer/assets/Dashboard-D3ezFmb7.js +36 -0
  7. package/dist/renderer/assets/{EmptyState-CkI1RwPX.js → EmptyState-DK496gBq.js} +1 -1
  8. package/dist/renderer/assets/{HighlightedMarkdown-BUhDlhgW.js → HighlightedMarkdown-Ckre5jNv.js} +1 -1
  9. package/dist/renderer/assets/{Input-DHYrn-Ne.js → Input-C5C1d0ba.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeDetail-Bij-Xo90.js → KnowledgeDetail-N8Ey1ikc.js} +2 -2
  11. package/dist/renderer/assets/{KnowledgeHome-DmPZ2IlI.js → KnowledgeHome-9aF8nJEf.js} +2 -2
  12. package/dist/renderer/assets/{Logs-ISY99heA.js → Logs-sgAdwEeS.js} +2 -2
  13. package/dist/renderer/assets/{Modal-DAQfz0ot.js → Modal-BB10hTRc.js} +3 -3
  14. package/dist/renderer/assets/{Page-Dw43UYDT.js → Page-DfC319Ck.js} +1 -1
  15. package/dist/renderer/assets/{Select-DzmN5OGQ.js → Select-RHJBmNER.js} +1 -1
  16. package/dist/renderer/assets/{ThreadChat-B5CWqqMK.js → ThreadChat-BU1zCdr4.js} +3 -3
  17. package/dist/renderer/assets/{Workspace-C5g9LBj9.js → Workspace-BoZCz7S8.js} +1 -1
  18. package/dist/renderer/assets/{arrow-left-BLXh56Ip.js → arrow-left-D2V1eZSD.js} +1 -1
  19. package/dist/renderer/assets/{book-open-DhPGbc-7.js → book-open-DUcBn7Bb.js} +1 -1
  20. package/dist/renderer/assets/{channels-DfQrmuYO.js → channels-DMhDSlf2.js} +2 -2
  21. package/dist/renderer/assets/{chevron-down-CEFtpPDc.js → chevron-down-Ca2FQDB7.js} +1 -1
  22. package/dist/renderer/assets/en-Bnr4nMzg.js +1 -0
  23. package/dist/renderer/assets/es-B1GIFh3i.js +1 -0
  24. package/dist/renderer/assets/{index-BIKL3fTP.css → index-B6WcpkJF.css} +1 -1
  25. package/dist/renderer/assets/{index-CYhDUAR_.js → index-BlnvlekJ.js} +1 -1
  26. package/dist/renderer/assets/index-DRJ5wgw_.js +162 -0
  27. package/dist/renderer/assets/ja-CDWPWqEW.js +1 -0
  28. package/dist/renderer/assets/{knowledge-uHRQ80sR.js → knowledge-D1-k56eY.js} +1 -1
  29. package/dist/renderer/assets/{pencil-BD2Y31YC.js → pencil-BhWdv_86.js} +1 -1
  30. package/dist/renderer/assets/{plus-DDcldp_c.js → plus-BacLMlK3.js} +1 -1
  31. package/dist/renderer/assets/{save-CMW_cZI9.js → save-Bpoxbzau.js} +1 -1
  32. package/dist/renderer/assets/{search-BM-0CvJr.js → search-BVJcpdFX.js} +1 -1
  33. package/dist/renderer/assets/{shield-check-DaMB0FKG.js → shield-check-C2cw_Ip6.js} +1 -1
  34. package/dist/renderer/assets/{threads-DZgt9tWj.js → threads-DOsBQBg_.js} +1 -1
  35. package/dist/renderer/assets/{trash-2-DTXVbC5y.js → trash-2-Bs3rEm3g.js} +1 -1
  36. package/dist/renderer/assets/zh-MSddxMDX.js +1 -0
  37. package/dist/renderer/assets/zh-TW-C_LNWX0o.js +1 -0
  38. package/dist/renderer/index.html +2 -2
  39. package/dist-electron/electron/managed-skills/agent-browser/SKILL.md +41 -0
  40. package/dist-electron/electron/managed-skills/agent-browser/references/authentication.md +83 -0
  41. package/dist-electron/electron/managed-skills/agent-browser/references/commands.md +50 -0
  42. package/dist-electron/electron/managed-skills/agent-browser/references/profiling.md +20 -0
  43. package/dist-electron/electron/managed-skills/agent-browser/references/proxy-support.md +18 -0
  44. package/dist-electron/electron/managed-skills/agent-browser/references/session-management.md +17 -0
  45. package/dist-electron/electron/managed-skills/agent-browser/references/snapshot-refs.md +17 -0
  46. package/dist-electron/electron/managed-skills/agent-browser/references/video-recording.md +21 -0
  47. package/dist-electron/electron/managed-skills/agent-browser/templates/authenticated-session.sh +11 -0
  48. package/dist-electron/electron/managed-skills/agent-browser/templates/capture-workflow.sh +12 -0
  49. package/dist-electron/electron/managed-skills/agent-browser/templates/form-automation.sh +10 -0
  50. package/dist-electron/electron/managed-skills/condition-trigger/SKILL.md +21 -0
  51. package/dist-electron/electron/managed-skills/condition-trigger/scripts/register-condition-trigger.sh +106 -0
  52. package/dist-electron/electron/managed-skills/knowledge-base/SKILL.md +13 -0
  53. package/dist-electron/electron/managed-skills/knowledge-base/scripts/search-knowledge.sh +80 -0
  54. package/dist-electron/packages/contracts/src/automations.js +211 -0
  55. package/dist-electron/packages/contracts/src/index.js +1 -0
  56. package/dist-electron/packages/core-sdk/src/automations.js +87 -0
  57. package/dist-electron/packages/core-sdk/src/client.js +101 -0
  58. package/dist-electron/packages/core-sdk/src/index.js +2 -1
  59. package/dist-electron/services/local-ai-core/src/acp/store/automation-script-store.js +415 -0
  60. package/dist-electron/services/local-ai-core/src/acp/store/automation-store.js +829 -0
  61. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +158 -0
  62. package/dist-electron/services/local-ai-core/src/acp/store/schema.js +81 -0
  63. package/dist-electron/services/local-ai-core/src/automation/automation-action-executor.js +144 -0
  64. package/dist-electron/services/local-ai-core/src/automation/automation-condition-engine.js +79 -0
  65. package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +39 -73
  66. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +695 -260
  67. package/dist-electron/services/local-ai-core/src/automation/automation-script-service.js +507 -0
  68. package/dist-electron/services/local-ai-core/src/automation/automation-service.js +1019 -0
  69. package/dist-electron/services/local-ai-core/src/automation/automation-trigger-engine.js +141 -0
  70. package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +60 -17
  71. package/dist-electron/services/local-ai-core/src/automation/legacy-automation-mappers.js +286 -0
  72. package/dist-electron/services/local-ai-core/src/automation/scripts/anthropic-sandbox-runner.js +792 -0
  73. package/dist-electron/services/local-ai-core/src/automation/scripts/sandbox-runner.js +2 -0
  74. package/dist-electron/services/local-ai-core/src/automation/scripts/script-package.js +475 -0
  75. package/dist-electron/services/local-ai-core/src/automation/scripts/script-protocol-runner.js +266 -0
  76. package/dist-electron/services/local-ai-core/src/automation/scripts/secret-resolver.js +37 -0
  77. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +1 -2
  78. package/dist-electron/services/local-ai-core/src/channel/weixin/transport.js +9 -8
  79. package/dist-electron/services/local-ai-core/src/cli/lac.js +229 -0
  80. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +21 -0
  81. package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +76 -0
  82. package/dist-electron/services/local-ai-core/src/runtime/handlers/automations-handler.js +322 -0
  83. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +20 -0
  84. package/dist-electron/services/local-ai-core/src/runtime/managed-skill-catalog.js +37 -0
  85. package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +14 -4
  86. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +75 -0
  87. package/dist-electron/services/local-ai-core/src/runtime/server.js +21 -0
  88. package/dist-electron/services/local-ai-core/src/runtime/standalone.js +1 -0
  89. package/dist-electron/services/local-ai-core/src/scheduler/cron.js +151 -39
  90. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +2 -4
  91. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-application-service.js +64 -12
  92. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +71 -4
  93. package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +10 -0
  94. package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +16 -1
  95. package/dist-electron/src/pages/Automation/automation-page-model.js +76 -0
  96. package/dist-electron/tests/contracts/agent-message-policy.test.js +32 -0
  97. package/dist-electron/tests/contracts/architecture-docs.test.js +30 -0
  98. package/dist-electron/tests/contracts/automation-contracts.test.js +154 -0
  99. package/dist-electron/tests/contracts/automation-renderer-model.test.js +47 -0
  100. package/dist-electron/tests/electron/automation-action-executor.test.js +75 -0
  101. package/dist-electron/tests/electron/automation-compatibility.test.js +463 -0
  102. package/dist-electron/tests/electron/automation-deployment.test.js +66 -0
  103. package/dist-electron/tests/electron/automation-engine.test.js +264 -0
  104. package/dist-electron/tests/electron/automation-monitor.test.js +1333 -0
  105. package/dist-electron/tests/electron/automation-sandbox-runner.test.js +333 -0
  106. package/dist-electron/tests/electron/automation-script-approval.test.js +553 -0
  107. package/dist-electron/tests/electron/automation-script-package.test.js +499 -0
  108. package/dist-electron/tests/electron/automation-script-runner.test.js +120 -0
  109. package/dist-electron/tests/electron/automation-service.test.js +1078 -0
  110. package/dist-electron/tests/electron/automation-store.test.js +593 -0
  111. package/dist-electron/tests/electron/condition-trigger-skill.test.js +93 -0
  112. package/dist-electron/tests/electron/core-client.test.js +99 -0
  113. package/dist-electron/tests/electron/lac-cli.test.js +110 -0
  114. package/dist-electron/tests/electron/workspace-task-store.test.js +25 -0
  115. package/dist-electron/tests/integration/automation-routes.test.js +186 -0
  116. package/dist-electron/tests/integration/automation-sandbox-runtime.test.js +92 -0
  117. package/package.json +4 -3
  118. package/dist/renderer/assets/CronList-CupZoNwW.js +0 -1
  119. package/dist/renderer/assets/Dashboard-nLZnOtcs.js +0 -41
  120. package/dist/renderer/assets/MonitorList-BlX82rXF.js +0 -1
  121. package/dist/renderer/assets/en-CmAr_0e6.js +0 -1
  122. package/dist/renderer/assets/es-Cs9x52ca.js +0 -1
  123. package/dist/renderer/assets/index-Dd00oqgY.js +0 -167
  124. package/dist/renderer/assets/ja-DtvtBRTW.js +0 -1
  125. package/dist/renderer/assets/play-DBJ8wsSy.js +0 -6
  126. package/dist/renderer/assets/zh-CcJ9vBZj.js +0 -1
  127. package/dist/renderer/assets/zh-TW-elI72AC1.js +0 -1
@@ -48,6 +48,12 @@ function parseLocalAiCoreRoute(method, path) {
48
48
  if (segments[0] === 'automation' && segments[1] === 'monitors') {
49
49
  return parseAutomationMonitorsRoute(normalizedMethod, segments);
50
50
  }
51
+ if (segments[0] === 'automations') {
52
+ return parseAutomationsRoute(normalizedMethod, segments);
53
+ }
54
+ if (segments[0] === 'automation-scripts') {
55
+ return parseAutomationScriptsRoute(normalizedMethod, segments);
56
+ }
51
57
  if (segments[0] === 'threads') {
52
58
  return parseThreadsRoute(normalizedMethod, segments);
53
59
  }
@@ -104,6 +110,75 @@ function parseLocalAiCoreRoute(method, path) {
104
110
  }
105
111
  return null;
106
112
  }
113
+ function parseAutomationsRoute(method, segments) {
114
+ if (segments.length === 1) {
115
+ if (method === 'GET')
116
+ return { name: 'automations.list' };
117
+ if (method === 'POST')
118
+ return { name: 'automations.create' };
119
+ return null;
120
+ }
121
+ const automationId = decodeURIComponent(segments[1] || '').trim();
122
+ if (!automationId)
123
+ return null;
124
+ if (segments.length === 2) {
125
+ if (method === 'GET')
126
+ return { name: 'automation.get', automationId };
127
+ if (method === 'PATCH')
128
+ return { name: 'automation.update', automationId };
129
+ if (method === 'DELETE')
130
+ return { name: 'automation.delete', automationId };
131
+ return null;
132
+ }
133
+ if (segments.length !== 3)
134
+ return null;
135
+ if (method === 'POST' && segments[2] === 'check')
136
+ return { name: 'automation.check', automationId };
137
+ if (method === 'GET' && segments[2] === 'evaluations')
138
+ return { name: 'automation.evaluations', automationId };
139
+ if (method === 'GET' && segments[2] === 'runs')
140
+ return { name: 'automation.runs', automationId };
141
+ return null;
142
+ }
143
+ function parseAutomationScriptsRoute(method, segments) {
144
+ if (segments.length === 1) {
145
+ if (method === 'GET')
146
+ return { name: 'automation-scripts.list' };
147
+ if (method === 'POST')
148
+ return { name: 'automation-scripts.create' };
149
+ return null;
150
+ }
151
+ if (segments[1] === 'versions') {
152
+ const versionId = decodeURIComponent(segments[2] || '').trim();
153
+ if (!versionId || segments.length !== 4 || method !== 'POST')
154
+ return null;
155
+ switch (segments[3]) {
156
+ case 'test-approval': return { name: 'automation-script-version.test-approval', versionId };
157
+ case 'test': return { name: 'automation-script-version.test', versionId };
158
+ case 'enable-approval': return { name: 'automation-script-version.enable-approval', versionId };
159
+ case 'approve': return { name: 'automation-script-version.approve', versionId };
160
+ case 'reject': return { name: 'automation-script-version.reject', versionId };
161
+ case 'revoke': return { name: 'automation-script-version.revoke', versionId };
162
+ default: return null;
163
+ }
164
+ }
165
+ const scriptId = decodeURIComponent(segments[1] || '').trim();
166
+ if (!scriptId)
167
+ return null;
168
+ if (segments.length === 2) {
169
+ if (method === 'GET')
170
+ return { name: 'automation-script.get', scriptId };
171
+ if (method === 'PATCH')
172
+ return { name: 'automation-script.update', scriptId };
173
+ }
174
+ if (segments.length === 3 && segments[2] === 'versions') {
175
+ if (method === 'GET')
176
+ return { name: 'automation-script.versions', scriptId };
177
+ if (method === 'POST')
178
+ return { name: 'automation-script.version.submit', scriptId };
179
+ }
180
+ return null;
181
+ }
107
182
  function parseOpenAiRoute(method, segments) {
108
183
  if (method === 'POST' && segments.length === 3 && segments[1] === 'chat' && segments[2] === 'completions') {
109
184
  return { name: 'openai.chat.completions' };
@@ -12,6 +12,7 @@ const security_handler_js_1 = require("./handlers/security-handler.js");
12
12
  const task_handler_js_1 = require("./handlers/task-handler.js");
13
13
  const scheduler_handler_js_1 = require("./handlers/scheduler-handler.js");
14
14
  const automation_handler_js_1 = require("./handlers/automation-handler.js");
15
+ const automations_handler_js_1 = require("./handlers/automations-handler.js");
15
16
  const knowledge_handler_js_1 = require("./handlers/knowledge-handler.js");
16
17
  const capabilities_handler_js_1 = require("./handlers/capabilities-handler.js");
17
18
  const provider_handler_js_1 = require("./handlers/provider-handler.js");
@@ -93,6 +94,14 @@ class LocalAiCoreServer {
93
94
  (0, task_handler_js_1.registerTaskHandlers)(this.handlers, b.workspaceRouter);
94
95
  (0, scheduler_handler_js_1.registerSchedulerHandlers)(this.handlers, b.scheduledJobs);
95
96
  (0, automation_handler_js_1.registerAutomationHandlers)(this.handlers, b.automationMonitors);
97
+ if (b.automations) {
98
+ (0, automations_handler_js_1.registerUnifiedAutomationHandlers)(this.handlers, {
99
+ automations: b.automations,
100
+ store: b.store,
101
+ executeScriptTest: (versionId) => b.automations.executeAuthorizedScriptTest(versionId),
102
+ emitScriptVersion: (version) => b.controller.emit('automation-script-version', version),
103
+ });
104
+ }
96
105
  (0, knowledge_handler_js_1.registerKnowledgeHandlers)(this.handlers, b.knowledgeProvider);
97
106
  (0, capabilities_handler_js_1.registerCapabilitiesHandlers)(this.handlers, b.kernel);
98
107
  (0, provider_handler_js_1.registerProviderHandlers)(this.handlers, b.store);
@@ -153,6 +162,18 @@ class LocalAiCoreServer {
153
162
  b.controller.on('automation-monitor-run', (run) => {
154
163
  this.broadcast({ type: 'automation.monitor.run.updated', run });
155
164
  });
165
+ b.controller.on('automation-definition', (automation) => {
166
+ this.broadcast({ type: 'automation.definition.updated', automation });
167
+ });
168
+ b.controller.on('automation-evaluation', (evaluation) => {
169
+ this.broadcast({ type: 'automation.evaluation.updated', evaluation });
170
+ });
171
+ b.controller.on('automation-run', (run) => {
172
+ this.broadcast({ type: 'automation.run.updated', run });
173
+ });
174
+ b.controller.on('automation-script-version', (version) => {
175
+ this.broadcast({ type: 'automation.script-version.updated', version });
176
+ });
156
177
  b.controller.on('runtime-detection', (event) => {
157
178
  this.broadcast(event);
158
179
  });
@@ -21,6 +21,7 @@ async function main() {
21
21
  knowledgeProvider: controller.knowledgeProvider,
22
22
  scheduledJobs: controller.scheduledJobs,
23
23
  automationMonitors: controller.automationMonitors,
24
+ automations: controller.automations,
24
25
  store: controller.store,
25
26
  runtimeDetection: controller.runtimeDetection,
26
27
  kernel: controller.kernel,
@@ -1,53 +1,165 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileCronExpression = compileCronExpression;
4
+ exports.cronMatchesFields = cronMatchesFields;
3
5
  exports.cronMatchesDate = cronMatchesDate;
6
+ exports.findNextCronMatchUtc = findNextCronMatchUtc;
7
+ exports.findPreviousCronMatchUtc = findPreviousCronMatchUtc;
4
8
  exports.floorToMinute = floorToMinute;
5
- function expandSegment(segment, min, max) {
6
- const values = new Set();
7
- const normalized = segment.trim();
8
- if (!normalized || normalized === '*') {
9
- for (let value = min; value <= max; value += 1) {
10
- values.add(value);
11
- }
12
- return values;
9
+ const DAY_MS = 24 * 60 * 60 * 1000;
10
+ const GREGORIAN_CYCLE_DAYS = 146_097;
11
+ const MIN_DATE_MS = -8_640_000_000_000_000;
12
+ const MAX_DATE_MS = 8_640_000_000_000_000;
13
+ function compileCronExpression(expression) {
14
+ const normalized = String(expression ?? '').trim();
15
+ const fields = normalized ? normalized.split(/\s+/) : [];
16
+ if (fields.length !== 5) {
17
+ throw invalidCron(normalized, 'expected exactly 5 fields');
13
18
  }
14
- for (const part of normalized.split(',')) {
15
- const token = part.trim();
16
- if (!token) {
17
- continue;
18
- }
19
- const [rangePart, stepPart] = token.split('/');
20
- const step = Math.max(1, Number(stepPart || '1'));
21
- let rangeStart = min;
22
- let rangeEnd = max;
23
- if (rangePart && rangePart !== '*') {
24
- const [startPart, endPart] = rangePart.split('-');
25
- rangeStart = Math.max(min, Number(startPart));
26
- rangeEnd = Math.min(max, Number(endPart || startPart));
27
- }
28
- for (let value = rangeStart; value <= rangeEnd; value += step) {
29
- if (value >= min && value <= max) {
30
- values.add(value);
19
+ return Object.freeze({
20
+ expression: normalized,
21
+ minutes: compileField(normalized, 'minute', fields[0], 0, 59),
22
+ hours: compileField(normalized, 'hour', fields[1], 0, 23),
23
+ daysOfMonth: compileField(normalized, 'day-of-month', fields[2], 1, 31),
24
+ months: compileField(normalized, 'month', fields[3], 1, 12),
25
+ daysOfWeek: compileField(normalized, 'day-of-week', fields[4], 0, 6),
26
+ });
27
+ }
28
+ function cronMatchesFields(compiled, fields) {
29
+ // Preserve the legacy scheduler's DOM+DOW AND behavior.
30
+ return compiled.minutes.includes(fields.minute)
31
+ && compiled.hours.includes(fields.hour)
32
+ && compiled.daysOfMonth.includes(fields.dayOfMonth)
33
+ && compiled.months.includes(fields.month)
34
+ && compiled.daysOfWeek.includes(fields.dayOfWeek);
35
+ }
36
+ function cronMatchesDate(cronExpr, date) {
37
+ let compiled;
38
+ try {
39
+ compiled = compileCronExpression(cronExpr);
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ return cronMatchesFields(compiled, {
45
+ minute: date.getMinutes(),
46
+ hour: date.getHours(),
47
+ dayOfMonth: date.getDate(),
48
+ month: date.getMonth() + 1,
49
+ dayOfWeek: date.getDay(),
50
+ });
51
+ }
52
+ function findNextCronMatchUtc(compiled, after) {
53
+ const afterMs = validDateMs(after, 'cron search date');
54
+ let dayMs = startOfUtcDay(afterMs);
55
+ for (let inspectedDays = 1; inspectedDays <= GREGORIAN_CYCLE_DAYS + 1; inspectedDays += 1) {
56
+ if (dayMs > MAX_DATE_MS)
57
+ return { date: null, inspectedDays };
58
+ const day = new Date(dayMs);
59
+ if (cronMatchesUtcDay(compiled, day)) {
60
+ for (const hour of compiled.hours) {
61
+ for (const minute of compiled.minutes) {
62
+ const candidateMs = dayMs + hour * 60 * 60 * 1000 + minute * 60 * 1000;
63
+ if (candidateMs > afterMs && candidateMs <= MAX_DATE_MS) {
64
+ return { date: new Date(candidateMs), inspectedDays };
65
+ }
66
+ }
31
67
  }
32
68
  }
69
+ dayMs += DAY_MS;
33
70
  }
34
- return values;
71
+ return { date: null, inspectedDays: GREGORIAN_CYCLE_DAYS + 1 };
35
72
  }
36
- function cronMatchesDate(cronExpr, date) {
37
- const fields = String(cronExpr || '').trim().split(/\s+/);
38
- if (fields.length !== 5) {
39
- return false;
73
+ function findPreviousCronMatchUtc(compiled, atOrBefore, afterExclusive) {
74
+ const atOrBeforeMs = validDateMs(atOrBefore, 'cron search date');
75
+ const afterExclusiveMs = afterExclusive
76
+ ? validDateMs(afterExclusive, 'cron search lower bound')
77
+ : undefined;
78
+ if (afterExclusiveMs !== undefined && afterExclusiveMs >= atOrBeforeMs) {
79
+ return { date: null, inspectedDays: 0 };
40
80
  }
41
- const [minute, hour, dayOfMonth, month, dayOfWeek] = fields;
42
- const checks = [
43
- [minute, 0, 59, date.getMinutes()],
44
- [hour, 0, 23, date.getHours()],
45
- [dayOfMonth, 1, 31, date.getDate()],
46
- [month, 1, 12, date.getMonth() + 1],
47
- [dayOfWeek, 0, 6, date.getDay()],
48
- ];
49
- return checks.every(([segment, min, max, current]) => expandSegment(segment, min, max).has(current));
81
+ const lowerBoundDayMs = afterExclusiveMs === undefined ? undefined : startOfUtcDay(afterExclusiveMs);
82
+ let dayMs = startOfUtcDay(atOrBeforeMs);
83
+ for (let inspectedDays = 1; inspectedDays <= GREGORIAN_CYCLE_DAYS + 1; inspectedDays += 1) {
84
+ if (dayMs < MIN_DATE_MS)
85
+ return { date: null, inspectedDays };
86
+ if (lowerBoundDayMs !== undefined && dayMs < lowerBoundDayMs) {
87
+ return { date: null, inspectedDays: inspectedDays - 1 };
88
+ }
89
+ const day = new Date(dayMs);
90
+ if (cronMatchesUtcDay(compiled, day)) {
91
+ for (let hourIndex = compiled.hours.length - 1; hourIndex >= 0; hourIndex -= 1) {
92
+ for (let minuteIndex = compiled.minutes.length - 1; minuteIndex >= 0; minuteIndex -= 1) {
93
+ const candidateMs = dayMs
94
+ + compiled.hours[hourIndex] * 60 * 60 * 1000
95
+ + compiled.minutes[minuteIndex] * 60 * 1000;
96
+ if (candidateMs <= atOrBeforeMs
97
+ && candidateMs >= MIN_DATE_MS
98
+ && (afterExclusiveMs === undefined || candidateMs > afterExclusiveMs)) {
99
+ return { date: new Date(candidateMs), inspectedDays };
100
+ }
101
+ }
102
+ }
103
+ }
104
+ if (dayMs === lowerBoundDayMs)
105
+ return { date: null, inspectedDays };
106
+ dayMs -= DAY_MS;
107
+ }
108
+ return { date: null, inspectedDays: GREGORIAN_CYCLE_DAYS + 1 };
50
109
  }
51
110
  function floorToMinute(date) {
52
111
  return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), 0, 0);
53
112
  }
113
+ function compileField(expression, fieldName, field, min, max) {
114
+ if (!field)
115
+ throw invalidCron(expression, `${fieldName} field is empty`);
116
+ const values = new Set();
117
+ const tokens = field.split(',');
118
+ if (tokens.some((token) => token === '')) {
119
+ throw invalidCron(expression, `${fieldName} contains an empty list item`);
120
+ }
121
+ for (const token of tokens) {
122
+ const match = /^(\*|\d+|\d+-\d+)(?:\/(\d+))?$/.exec(token);
123
+ if (!match)
124
+ throw invalidCron(expression, `${fieldName} contains invalid token "${token}"`);
125
+ const step = match[2] === undefined ? 1 : Number(match[2]);
126
+ if (!Number.isSafeInteger(step) || step <= 0) {
127
+ throw invalidCron(expression, `${fieldName} step must be a positive integer`);
128
+ }
129
+ const range = match[1];
130
+ let start = min;
131
+ let end = max;
132
+ if (range !== '*') {
133
+ const rangeParts = range.split('-');
134
+ start = Number(rangeParts[0]);
135
+ end = Number(rangeParts[1] ?? rangeParts[0]);
136
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < min || end > max) {
137
+ throw invalidCron(expression, `${fieldName} values must be between ${min} and ${max}`);
138
+ }
139
+ if (start > end)
140
+ throw invalidCron(expression, `${fieldName} range start must not exceed its end`);
141
+ }
142
+ for (let value = start; value <= end; value += step)
143
+ values.add(value);
144
+ }
145
+ return Object.freeze([...values].sort((left, right) => left - right));
146
+ }
147
+ function cronMatchesUtcDay(compiled, day) {
148
+ return compiled.daysOfMonth.includes(day.getUTCDate())
149
+ && compiled.months.includes(day.getUTCMonth() + 1)
150
+ && compiled.daysOfWeek.includes(day.getUTCDay());
151
+ }
152
+ function startOfUtcDay(value) {
153
+ const date = new Date(value);
154
+ date.setUTCHours(0, 0, 0, 0);
155
+ return date.getTime();
156
+ }
157
+ function validDateMs(date, label) {
158
+ const value = date.getTime();
159
+ if (!Number.isFinite(value))
160
+ throw new Error(`Invalid ${label}.`);
161
+ return value;
162
+ }
163
+ function invalidCron(expression, reason) {
164
+ return new Error(`Invalid cron expression "${expression}": ${reason}.`);
165
+ }
@@ -3,6 +3,7 @@ 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
5
  const run_polling_js_1 = require("./run-polling.js");
6
+ const thread_resolution_js_1 = require("./thread-resolution.js");
6
7
  const SCHEDULED_RUN_PERMISSION_MODE = 'bypassPermissions';
7
8
  class ScheduledConversationExecutor {
8
9
  options;
@@ -20,10 +21,7 @@ class ScheduledConversationExecutor {
20
21
  });
21
22
  await (0, run_polling_js_1.waitForRunCompletion)(this.options.store, sendResult.runId, timeoutMs, 'Scheduled');
22
23
  const thread = await workspaceRouter.getThread(target.threadId);
23
- const replyText = [...thread.messages]
24
- .reverse()
25
- .find((message) => message.role === 'assistant' && message.kind === 'final')
26
- ?.content;
24
+ const replyText = (0, thread_resolution_js_1.getLatestAssistantFinalContent)(thread);
27
25
  return {
28
26
  threadId: target.threadId,
29
27
  runId: sendResult.runId,
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ScheduledJobApplicationService = void 0;
4
+ const legacy_automation_mappers_js_1 = require("../automation/legacy-automation-mappers.js");
5
+ const job_id_js_1 = require("./job-id.js");
4
6
  const scheduled_job_route_js_1 = require("./scheduled-job-route.js");
5
7
  class ScheduledJobApplicationService {
6
8
  options;
@@ -8,16 +10,22 @@ class ScheduledJobApplicationService {
8
10
  this.options = options;
9
11
  }
10
12
  listJobs(workspaceId) {
11
- return this.options.scheduler.listJobs(workspaceId);
13
+ return this.options.automations.list(workspaceId)
14
+ .filter((automation) => automation.originKind === 'scheduled-job')
15
+ .map((automation) => (0, legacy_automation_mappers_js_1.automationToScheduledJob)(automation, (0, legacy_automation_mappers_js_1.latestAutomationRun)(this.options.automations.listRuns(automation.id))));
12
16
  }
13
17
  getJob(jobId) {
14
- return this.options.scheduler.getJob(jobId);
18
+ const resolved = this.resolveJobId(jobId);
19
+ const automation = resolved ? this.options.automations.get(resolved) : undefined;
20
+ return automation?.originKind === 'scheduled-job'
21
+ ? (0, legacy_automation_mappers_js_1.automationToScheduledJob)(automation, (0, legacy_automation_mappers_js_1.latestAutomationRun)(this.options.automations.listRuns(automation.id)))
22
+ : undefined;
15
23
  }
16
24
  createJob(input) {
17
- return this.options.scheduler.createJob(this.resolveCreateInput(input));
25
+ return (0, legacy_automation_mappers_js_1.automationToScheduledJob)(this.options.automations.createFromLegacy((0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)(this.resolveCreateInput(input))));
18
26
  }
19
27
  createCronJob(input) {
20
- return this.options.scheduler.createJob({
28
+ return this.createJob({
21
29
  workspaceId: input.workspaceId,
22
30
  platform: input.platform,
23
31
  route: this.resolveExplicitRoute(input.platform, input.route),
@@ -29,23 +37,45 @@ class ScheduledJobApplicationService {
29
37
  });
30
38
  }
31
39
  updateJob(jobId, input) {
32
- return this.options.scheduler.updateJob(jobId, {
33
- ...input,
34
- ...(input.route ? { route: (0, scheduled_job_route_js_1.withoutThreadRoute)(input.route) } : {}),
40
+ this.options.automations.assertLegacyFacadesAvailable();
41
+ const existing = this.getRequiredJob(jobId);
42
+ const resolved = this.resolveRequiredJobId(jobId);
43
+ const merged = this.resolveCreateInput({
44
+ workspaceId: existing.workspaceId,
45
+ platform: existing.platform,
46
+ route: input.route ? (0, scheduled_job_route_js_1.withoutThreadRoute)(input.route) : existing.route,
47
+ executionMode: input.executionMode ?? existing.executionMode,
48
+ triggerType: input.triggerType ?? existing.triggerType,
49
+ cronExpr: input.cronExpr ?? existing.cronExpr,
50
+ runAt: input.runAt ?? existing.runAt,
51
+ promptTemplate: input.promptTemplate ?? existing.promptTemplate,
52
+ description: input.description ?? existing.description,
53
+ enabled: input.enabled ?? existing.enabled,
35
54
  });
55
+ const mapped = (0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)(merged);
56
+ const { workspaceId: _workspaceId, originKind: _originKind, ...update } = mapped;
57
+ return (0, legacy_automation_mappers_js_1.automationToScheduledJob)(this.options.automations.updateFromLegacy(resolved, update));
36
58
  }
37
59
  deleteJob(jobId) {
38
- return this.options.scheduler.deleteJob(jobId);
60
+ this.options.automations.assertLegacyFacadesAvailable();
61
+ return this.options.automations.delete(this.resolveRequiredJobId(jobId));
39
62
  }
40
- runJobNow(jobId) {
41
- return this.options.scheduler.runJobNow(jobId);
63
+ async runJobNow(jobId) {
64
+ this.options.automations.assertLegacyFacadesAvailable();
65
+ const resolved = this.resolveRequiredJobId(jobId);
66
+ const evaluation = await this.options.automations.checkNow(resolved);
67
+ const run = this.options.automations.listRuns(resolved).find((candidate) => candidate.evaluationId === evaluation.id);
68
+ return (0, legacy_automation_mappers_js_1.automationToScheduledJobRun)(evaluation, run);
42
69
  }
43
70
  listJobRuns(jobId) {
44
- return this.options.scheduler.listJobRuns(jobId);
71
+ const resolved = this.resolveRequiredJobId(jobId);
72
+ const runs = this.options.automations.listRuns(resolved);
73
+ const runsByEvaluationId = new Map(runs.map((run) => [run.evaluationId, run]));
74
+ return this.options.automations.listEvaluations(resolved).map((evaluation) => (0, legacy_automation_mappers_js_1.automationToScheduledJobRun)(evaluation, runsByEvaluationId.get(evaluation.id)));
45
75
  }
46
76
  listJobsForThread(threadId) {
47
77
  const binding = this.options.store.getPlatformThreadBindingByThreadId(threadId);
48
- return this.options.scheduler
78
+ return this
49
79
  .listJobs()
50
80
  .filter((job) => job.route.threadId === threadId ||
51
81
  (binding ? (0, scheduled_job_route_js_1.scheduledJobMatchesPlatformBinding)(job, binding) : false));
@@ -80,5 +110,27 @@ class ScheduledJobApplicationService {
80
110
  resolveExplicitRoute(platform, route) {
81
111
  return (0, scheduled_job_route_js_1.routeWithPlatformInstance)((0, scheduled_job_route_js_1.withoutThreadRoute)(route), platform);
82
112
  }
113
+ resolveJobId(jobId) {
114
+ const direct = this.options.automations.get(jobId);
115
+ if (direct?.originKind === 'scheduled-job')
116
+ return direct.id;
117
+ const matches = this.options.automations.list()
118
+ .filter((automation) => automation.originKind === 'scheduled-job' && (0, job_id_js_1.toPublicScheduledJobId)(automation.id) === jobId);
119
+ if (matches.length > 1)
120
+ throw new Error(`Scheduled job id is ambiguous: ${jobId}`);
121
+ return matches[0]?.id || '';
122
+ }
123
+ resolveRequiredJobId(jobId) {
124
+ const resolved = this.resolveJobId(jobId);
125
+ if (!resolved)
126
+ throw new Error(`Scheduled job not found: ${jobId}`);
127
+ return resolved;
128
+ }
129
+ getRequiredJob(jobId) {
130
+ const job = this.getJob(jobId);
131
+ if (!job)
132
+ throw new Error(`Scheduled job not found: ${jobId}`);
133
+ return job;
134
+ }
83
135
  }
84
136
  exports.ScheduledJobApplicationService = ScheduledJobApplicationService;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SchedulerService = void 0;
4
4
  const node_events_1 = require("node:events");
5
+ const legacy_automation_mappers_js_1 = require("../automation/legacy-automation-mappers.js");
5
6
  const job_id_js_1 = require("./job-id.js");
6
7
  const scheduler_run_lifecycle_js_1 = require("./scheduler-run-lifecycle.js");
7
8
  const SCHEDULER_AUTO_DISABLE_THRESHOLD = 5;
@@ -29,10 +30,7 @@ class SchedulerService extends node_events_1.EventEmitter {
29
30
  this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: run });
30
31
  }
31
32
  async start() {
32
- await this.tick();
33
- this.timer = setInterval(() => {
34
- void this.tick();
35
- }, 1000);
33
+ // Unified AutomationService owns scheduled-job execution after migration.
36
34
  }
37
35
  async stop() {
38
36
  if (this.timer) {
@@ -41,29 +39,88 @@ class SchedulerService extends node_events_1.EventEmitter {
41
39
  }
42
40
  }
43
41
  listJobs(workspaceId) {
42
+ const automations = this.options.automations;
43
+ if (automations) {
44
+ return automations.list(workspaceId)
45
+ .filter((automation) => automation.originKind === 'scheduled-job')
46
+ .map((automation) => (0, legacy_automation_mappers_js_1.automationToScheduledJob)(automation, (0, legacy_automation_mappers_js_1.latestAutomationRun)(automations.listRuns(automation.id))));
47
+ }
44
48
  return this.options.store.listScheduledJobs(workspaceId);
45
49
  }
46
50
  getJob(jobId) {
51
+ if (this.options.automations) {
52
+ const resolved = this.resolveJobId(jobId);
53
+ const automation = resolved ? this.options.automations.get(resolved) : undefined;
54
+ return automation?.originKind === 'scheduled-job'
55
+ ? (0, legacy_automation_mappers_js_1.automationToScheduledJob)(automation, (0, legacy_automation_mappers_js_1.latestAutomationRun)(this.options.automations.listRuns(automation.id)))
56
+ : undefined;
57
+ }
47
58
  const resolvedJobId = this.resolveJobId(jobId);
48
59
  return resolvedJobId ? this.options.store.getScheduledJob(resolvedJobId) : undefined;
49
60
  }
50
61
  createJob(input) {
62
+ if (this.options.automations) {
63
+ if (!input.platform || !input.route)
64
+ throw new Error('Scheduled job creation requires a resolved platform and route.');
65
+ return (0, legacy_automation_mappers_js_1.automationToScheduledJob)(this.options.automations.createFromLegacy((0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)({
66
+ ...input,
67
+ platform: input.platform,
68
+ route: input.route,
69
+ })));
70
+ }
51
71
  const job = this.options.store.createScheduledJob(input);
52
72
  this.emitJob(job);
53
73
  return job;
54
74
  }
55
75
  updateJob(jobId, input) {
76
+ if (this.options.automations) {
77
+ this.options.automations.assertLegacyFacadesAvailable();
78
+ const existing = this.getJob(jobId);
79
+ if (!existing)
80
+ throw new Error(`Scheduled job not found: ${jobId}`);
81
+ const mapped = (0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)({
82
+ workspaceId: existing.workspaceId,
83
+ platform: existing.platform,
84
+ route: input.route ?? existing.route,
85
+ executionMode: input.executionMode ?? existing.executionMode,
86
+ triggerType: input.triggerType ?? existing.triggerType,
87
+ cronExpr: input.cronExpr ?? existing.cronExpr,
88
+ runAt: input.runAt ?? existing.runAt,
89
+ promptTemplate: input.promptTemplate ?? existing.promptTemplate,
90
+ description: input.description ?? existing.description,
91
+ enabled: input.enabled ?? existing.enabled,
92
+ });
93
+ const { workspaceId: _workspaceId, originKind: _originKind, ...update } = mapped;
94
+ return (0, legacy_automation_mappers_js_1.automationToScheduledJob)(this.options.automations.updateFromLegacy(this.resolveRequiredJobId(jobId), update));
95
+ }
56
96
  const job = this.options.store.updateScheduledJob(this.resolveRequiredJobId(jobId), input);
57
97
  this.emitJob(job);
58
98
  return job;
59
99
  }
60
100
  deleteJob(jobId) {
101
+ if (this.options.automations) {
102
+ this.options.automations.assertLegacyFacadesAvailable();
103
+ return this.options.automations.delete(this.resolveRequiredJobId(jobId));
104
+ }
61
105
  return this.options.store.deleteScheduledJob(this.resolveRequiredJobId(jobId));
62
106
  }
63
107
  listJobRuns(jobId) {
108
+ if (this.options.automations) {
109
+ const resolved = this.resolveRequiredJobId(jobId);
110
+ const runs = this.options.automations.listRuns(resolved);
111
+ const runsByEvaluationId = new Map(runs.map((run) => [run.evaluationId, run]));
112
+ return this.options.automations.listEvaluations(resolved).map((evaluation) => (0, legacy_automation_mappers_js_1.automationToScheduledJobRun)(evaluation, runsByEvaluationId.get(evaluation.id)));
113
+ }
64
114
  return this.options.store.listScheduledJobRuns(this.resolveRequiredJobId(jobId));
65
115
  }
66
116
  async runJobNow(jobId) {
117
+ if (this.options.automations) {
118
+ this.options.automations.assertLegacyFacadesAvailable();
119
+ const resolved = this.resolveRequiredJobId(jobId);
120
+ const evaluation = await this.options.automations.checkNow(resolved);
121
+ const run = this.options.automations.listRuns(resolved).find((candidate) => candidate.evaluationId === evaluation.id);
122
+ return (0, legacy_automation_mappers_js_1.automationToScheduledJobRun)(evaluation, run);
123
+ }
67
124
  const resolvedJobId = this.resolveRequiredJobId(jobId);
68
125
  const job = this.options.store.getScheduledJob(resolvedJobId);
69
126
  if (!job) {
@@ -72,6 +129,16 @@ class SchedulerService extends node_events_1.EventEmitter {
72
129
  return this.executeJob(job, new Date().toISOString(), true);
73
130
  }
74
131
  resolveJobId(jobId) {
132
+ if (this.options.automations) {
133
+ const direct = this.options.automations.get(jobId);
134
+ if (direct?.originKind === 'scheduled-job')
135
+ return direct.id;
136
+ const matches = this.options.automations.list()
137
+ .filter((automation) => automation.originKind === 'scheduled-job' && (0, job_id_js_1.toPublicScheduledJobId)(automation.id) === jobId);
138
+ if (matches.length > 1)
139
+ throw new Error(`Scheduled job id is ambiguous: ${jobId}`);
140
+ return matches[0]?.id || '';
141
+ }
75
142
  if (this.options.store.getScheduledJob(jobId)) {
76
143
  return jobId;
77
144
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.threadExists = threadExists;
4
+ exports.getLatestAssistantFinalContent = getLatestAssistantFinalContent;
4
5
  async function threadExists(router, threadId) {
5
6
  try {
6
7
  await router.getThread(threadId);
@@ -10,3 +11,12 @@ async function threadExists(router, threadId) {
10
11
  return false;
11
12
  }
12
13
  }
14
+ function getLatestAssistantFinalContent(thread) {
15
+ for (let i = thread.messages.length - 1; i >= 0; i -= 1) {
16
+ const message = thread.messages[i];
17
+ if (message && message.role === 'assistant' && message.kind === 'final') {
18
+ return message.content;
19
+ }
20
+ }
21
+ return undefined;
22
+ }