@kafca/agentdock 0.1.58 → 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.
- package/README.md +18 -0
- package/dist/renderer/assets/AutomationList-BgJgAdVO.js +16 -0
- package/dist/renderer/assets/{Badge-BfY1VaHS.js → Badge-Bq2hPVcR.js} +1 -1
- package/dist/renderer/assets/{Card-DWA_Tww_.js → Card-BPJNSZce.js} +1 -1
- package/dist/renderer/assets/{Config-nHj3g1U5.js → Config-aSFqlI88.js} +2 -2
- package/dist/renderer/assets/Dashboard-D3ezFmb7.js +36 -0
- package/dist/renderer/assets/{EmptyState-zatDjLj_.js → EmptyState-DK496gBq.js} +1 -1
- package/dist/renderer/assets/{HighlightedMarkdown-CsM5n70d.js → HighlightedMarkdown-Ckre5jNv.js} +1 -1
- package/dist/renderer/assets/{Input-R1XdFK1K.js → Input-C5C1d0ba.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-Co5sxeGd.js → KnowledgeDetail-N8Ey1ikc.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-DwziPw5m.js → KnowledgeHome-9aF8nJEf.js} +2 -2
- package/dist/renderer/assets/{Logs-NwZUtR_G.js → Logs-sgAdwEeS.js} +2 -2
- package/dist/renderer/assets/{Modal-CrcIwIFL.js → Modal-BB10hTRc.js} +3 -3
- package/dist/renderer/assets/{Page-Dp0obeSw.js → Page-DfC319Ck.js} +1 -1
- package/dist/renderer/assets/{Select-6mVQaf84.js → Select-RHJBmNER.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-BFzbn3L6.js → ThreadChat-BU1zCdr4.js} +3 -3
- package/dist/renderer/assets/{Workspace-DCmo8BMm.js → Workspace-BoZCz7S8.js} +1 -1
- package/dist/renderer/assets/{arrow-left-9rjg_Hp4.js → arrow-left-D2V1eZSD.js} +1 -1
- package/dist/renderer/assets/{book-open-Bu8GCHnG.js → book-open-DUcBn7Bb.js} +1 -1
- package/dist/renderer/assets/{channels-BxVbG-6j.js → channels-DMhDSlf2.js} +2 -2
- package/dist/renderer/assets/{chevron-down-BE8uQmWM.js → chevron-down-Ca2FQDB7.js} +1 -1
- package/dist/renderer/assets/en-Bnr4nMzg.js +1 -0
- package/dist/renderer/assets/es-B1GIFh3i.js +1 -0
- package/dist/renderer/assets/{index-BIKL3fTP.css → index-B6WcpkJF.css} +1 -1
- package/dist/renderer/assets/{index-B7TeF6Es.js → index-BlnvlekJ.js} +1 -1
- package/dist/renderer/assets/index-DRJ5wgw_.js +162 -0
- package/dist/renderer/assets/ja-CDWPWqEW.js +1 -0
- package/dist/renderer/assets/{knowledge-Kl19SqGS.js → knowledge-D1-k56eY.js} +1 -1
- package/dist/renderer/assets/{pencil-CPB7SK1m.js → pencil-BhWdv_86.js} +1 -1
- package/dist/renderer/assets/{plus-Bt26z-4q.js → plus-BacLMlK3.js} +1 -1
- package/dist/renderer/assets/{save-Une7GMxT.js → save-Bpoxbzau.js} +1 -1
- package/dist/renderer/assets/{search-DE0agJjJ.js → search-BVJcpdFX.js} +1 -1
- package/dist/renderer/assets/{shield-check-De7xaXZh.js → shield-check-C2cw_Ip6.js} +1 -1
- package/dist/renderer/assets/{threads-BnsNL5ei.js → threads-DOsBQBg_.js} +1 -1
- package/dist/renderer/assets/{trash-2-6iGF59Ac.js → trash-2-Bs3rEm3g.js} +1 -1
- package/dist/renderer/assets/zh-MSddxMDX.js +1 -0
- package/dist/renderer/assets/zh-TW-C_LNWX0o.js +1 -0
- package/dist/renderer/index.html +2 -2
- package/dist-electron/electron/managed-skills/agent-browser/SKILL.md +41 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/authentication.md +83 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/commands.md +50 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/profiling.md +20 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/proxy-support.md +18 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/session-management.md +17 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/snapshot-refs.md +17 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/video-recording.md +21 -0
- package/dist-electron/electron/managed-skills/agent-browser/templates/authenticated-session.sh +11 -0
- package/dist-electron/electron/managed-skills/agent-browser/templates/capture-workflow.sh +12 -0
- package/dist-electron/electron/managed-skills/agent-browser/templates/form-automation.sh +10 -0
- package/dist-electron/electron/managed-skills/condition-trigger/SKILL.md +21 -0
- package/dist-electron/electron/managed-skills/condition-trigger/scripts/register-condition-trigger.sh +106 -0
- package/dist-electron/electron/managed-skills/knowledge-base/SKILL.md +13 -0
- package/dist-electron/electron/managed-skills/knowledge-base/scripts/search-knowledge.sh +80 -0
- package/dist-electron/packages/contracts/src/automations.js +211 -0
- package/dist-electron/packages/contracts/src/index.js +1 -0
- package/dist-electron/packages/core-sdk/src/automations.js +87 -0
- package/dist-electron/packages/core-sdk/src/client.js +101 -0
- package/dist-electron/packages/core-sdk/src/index.js +2 -1
- package/dist-electron/services/local-ai-core/src/acp/store/automation-script-store.js +415 -0
- package/dist-electron/services/local-ai-core/src/acp/store/automation-store.js +829 -0
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +158 -0
- package/dist-electron/services/local-ai-core/src/acp/store/schema.js +81 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-action-executor.js +144 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-condition-engine.js +79 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +39 -73
- package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +695 -260
- package/dist-electron/services/local-ai-core/src/automation/automation-script-service.js +507 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-service.js +1019 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-trigger-engine.js +141 -0
- package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +60 -17
- package/dist-electron/services/local-ai-core/src/automation/legacy-automation-mappers.js +286 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/anthropic-sandbox-runner.js +792 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/sandbox-runner.js +2 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/script-package.js +475 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/script-protocol-runner.js +266 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/secret-resolver.js +37 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +1 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/transport.js +9 -8
- package/dist-electron/services/local-ai-core/src/cli/lac.js +229 -0
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +21 -0
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +76 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/automations-handler.js +322 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +20 -0
- package/dist-electron/services/local-ai-core/src/runtime/managed-skill-catalog.js +37 -0
- package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +14 -4
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +75 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +21 -0
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +1 -0
- package/dist-electron/services/local-ai-core/src/scheduler/cron.js +151 -39
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +2 -4
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-application-service.js +64 -12
- package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +71 -4
- package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +10 -0
- package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +16 -1
- package/dist-electron/src/pages/Automation/automation-page-model.js +76 -0
- package/dist-electron/tests/contracts/agent-message-policy.test.js +32 -0
- package/dist-electron/tests/contracts/architecture-docs.test.js +30 -0
- package/dist-electron/tests/contracts/automation-contracts.test.js +154 -0
- package/dist-electron/tests/contracts/automation-renderer-model.test.js +47 -0
- package/dist-electron/tests/electron/automation-action-executor.test.js +75 -0
- package/dist-electron/tests/electron/automation-compatibility.test.js +463 -0
- package/dist-electron/tests/electron/automation-deployment.test.js +66 -0
- package/dist-electron/tests/electron/automation-engine.test.js +264 -0
- package/dist-electron/tests/electron/automation-monitor.test.js +1333 -0
- package/dist-electron/tests/electron/automation-sandbox-runner.test.js +333 -0
- package/dist-electron/tests/electron/automation-script-approval.test.js +553 -0
- package/dist-electron/tests/electron/automation-script-package.test.js +499 -0
- package/dist-electron/tests/electron/automation-script-runner.test.js +120 -0
- package/dist-electron/tests/electron/automation-service.test.js +1078 -0
- package/dist-electron/tests/electron/automation-store.test.js +593 -0
- package/dist-electron/tests/electron/condition-trigger-skill.test.js +93 -0
- package/dist-electron/tests/electron/core-client.test.js +99 -0
- package/dist-electron/tests/electron/lac-cli.test.js +110 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +25 -0
- package/dist-electron/tests/integration/automation-routes.test.js +186 -0
- package/dist-electron/tests/integration/automation-sandbox-runtime.test.js +92 -0
- package/package.json +5 -6
- package/dist/renderer/assets/CronList-BwkA_I7v.js +0 -1
- package/dist/renderer/assets/Dashboard-D0_A7k02.js +0 -41
- package/dist/renderer/assets/MonitorList-CS1uLS5A.js +0 -1
- package/dist/renderer/assets/en-CmAr_0e6.js +0 -1
- package/dist/renderer/assets/es-Cs9x52ca.js +0 -1
- package/dist/renderer/assets/index-BkI9POxj.js +0 -167
- package/dist/renderer/assets/ja-DtvtBRTW.js +0 -1
- package/dist/renderer/assets/play-DGAO4OCu.js +0 -6
- package/dist/renderer/assets/zh-CcJ9vBZj.js +0 -1
- package/dist/renderer/assets/zh-TW-elI72AC1.js +0 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nextActivationAt = nextActivationAt;
|
|
4
|
+
exports.isActivationDue = isActivationDue;
|
|
5
|
+
exports.missedActivationAt = missedActivationAt;
|
|
6
|
+
const cron_js_1 = require("../scheduler/cron.js");
|
|
7
|
+
const MINUTE_MS = 60_000;
|
|
8
|
+
function nextActivationAt(activation, after) {
|
|
9
|
+
const afterMs = validDateMs(after, 'after');
|
|
10
|
+
const compiledCron = validateActivation(activation);
|
|
11
|
+
switch (activation.kind) {
|
|
12
|
+
case 'provider-event':
|
|
13
|
+
return null;
|
|
14
|
+
case 'once': {
|
|
15
|
+
const runAt = parseIsoTimestamp(activation.runAt, 'once runAt');
|
|
16
|
+
return runAt.getTime() > afterMs ? runAt : null;
|
|
17
|
+
}
|
|
18
|
+
case 'interval': {
|
|
19
|
+
const nextMs = (Math.floor(afterMs / activation.intervalMs) + 1) * activation.intervalMs;
|
|
20
|
+
return validResultDate(nextMs);
|
|
21
|
+
}
|
|
22
|
+
case 'cron': {
|
|
23
|
+
const result = (0, cron_js_1.findNextCronMatchUtc)(requireCompiledCron(compiledCron), after);
|
|
24
|
+
if (result.date)
|
|
25
|
+
return result.date;
|
|
26
|
+
throw new Error(`No UTC cron activation exists for expression: ${activation.expression}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function isActivationDue(activation, now, nextCheckAt) {
|
|
31
|
+
const nowMs = validDateMs(now, 'now');
|
|
32
|
+
const compiledCron = validateActivation(activation);
|
|
33
|
+
if (activation.kind === 'provider-event')
|
|
34
|
+
return false;
|
|
35
|
+
if (nextCheckAt !== undefined) {
|
|
36
|
+
return parseIsoTimestamp(nextCheckAt, 'nextCheckAt').getTime() <= nowMs;
|
|
37
|
+
}
|
|
38
|
+
switch (activation.kind) {
|
|
39
|
+
case 'once':
|
|
40
|
+
return parseIsoTimestamp(activation.runAt, 'once runAt').getTime() <= nowMs;
|
|
41
|
+
case 'interval':
|
|
42
|
+
return nowMs % activation.intervalMs === 0;
|
|
43
|
+
case 'cron':
|
|
44
|
+
return nowMs === floorUtcMinute(nowMs) && (0, cron_js_1.cronMatchesFields)(requireCompiledCron(compiledCron), {
|
|
45
|
+
minute: now.getUTCMinutes(),
|
|
46
|
+
hour: now.getUTCHours(),
|
|
47
|
+
dayOfMonth: now.getUTCDate(),
|
|
48
|
+
month: now.getUTCMonth() + 1,
|
|
49
|
+
dayOfWeek: now.getUTCDay(),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function missedActivationAt(activation, lastCheckedAt, now) {
|
|
54
|
+
const nowMs = validDateMs(now, 'now');
|
|
55
|
+
const compiledCron = validateActivation(activation);
|
|
56
|
+
if (activation.kind === 'provider-event' || lastCheckedAt === undefined)
|
|
57
|
+
return null;
|
|
58
|
+
const lastCheckedMs = parseIsoTimestamp(lastCheckedAt, 'lastCheckedAt').getTime();
|
|
59
|
+
if (lastCheckedMs >= nowMs)
|
|
60
|
+
return null;
|
|
61
|
+
switch (activation.kind) {
|
|
62
|
+
case 'once': {
|
|
63
|
+
const runAt = parseIsoTimestamp(activation.runAt, 'once runAt');
|
|
64
|
+
return runAt.getTime() > lastCheckedMs && runAt.getTime() <= nowMs ? runAt : null;
|
|
65
|
+
}
|
|
66
|
+
case 'interval': {
|
|
67
|
+
const latestMs = Math.floor(nowMs / activation.intervalMs) * activation.intervalMs;
|
|
68
|
+
return latestMs > lastCheckedMs ? validResultDate(latestMs) : null;
|
|
69
|
+
}
|
|
70
|
+
case 'cron': {
|
|
71
|
+
return (0, cron_js_1.findPreviousCronMatchUtc)(requireCompiledCron(compiledCron), now, new Date(lastCheckedMs)).date;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function validateActivation(activation) {
|
|
76
|
+
switch (activation.kind) {
|
|
77
|
+
case 'provider-event':
|
|
78
|
+
return undefined;
|
|
79
|
+
case 'once':
|
|
80
|
+
parseIsoTimestamp(activation.runAt, 'once runAt');
|
|
81
|
+
return undefined;
|
|
82
|
+
case 'interval':
|
|
83
|
+
assertInterval(activation.intervalMs);
|
|
84
|
+
return undefined;
|
|
85
|
+
case 'cron':
|
|
86
|
+
assertUtcTimezone(activation.timezone);
|
|
87
|
+
return (0, cron_js_1.compileCronExpression)(activation.expression);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function assertUtcTimezone(timezone) {
|
|
91
|
+
if (timezone !== 'UTC' && timezone !== 'Etc/UTC') {
|
|
92
|
+
throw new Error(`Automation scheduling currently supports only UTC cron timezones; received: ${timezone}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function assertInterval(intervalMs) {
|
|
96
|
+
if (!Number.isSafeInteger(intervalMs) || intervalMs <= 0) {
|
|
97
|
+
throw new Error('Automation intervalMs must be a positive safe integer.');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function parseIsoTimestamp(value, label) {
|
|
101
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/.exec(value);
|
|
102
|
+
if (!match)
|
|
103
|
+
throw new Error(`Automation ${label} must be a valid ISO timestamp.`);
|
|
104
|
+
const date = new Date(value);
|
|
105
|
+
validDateMs(date, label);
|
|
106
|
+
const [, yearText, monthText, dayText, hourText, minuteText, secondText] = match;
|
|
107
|
+
const year = Number(yearText);
|
|
108
|
+
const month = Number(monthText);
|
|
109
|
+
const day = Number(dayText);
|
|
110
|
+
const calendarDate = new Date(0);
|
|
111
|
+
calendarDate.setUTCFullYear(year, month - 1, day);
|
|
112
|
+
calendarDate.setUTCHours(0, 0, 0, 0);
|
|
113
|
+
if (calendarDate.getUTCFullYear() !== year
|
|
114
|
+
|| calendarDate.getUTCMonth() !== month - 1
|
|
115
|
+
|| calendarDate.getUTCDate() !== day
|
|
116
|
+
|| Number(hourText) > 23
|
|
117
|
+
|| Number(minuteText) > 59
|
|
118
|
+
|| Number(secondText) > 59) {
|
|
119
|
+
throw new Error(`Automation ${label} must be a valid ISO timestamp.`);
|
|
120
|
+
}
|
|
121
|
+
return date;
|
|
122
|
+
}
|
|
123
|
+
function requireCompiledCron(compiled) {
|
|
124
|
+
if (!compiled)
|
|
125
|
+
throw new Error('Automation cron expression was not compiled.');
|
|
126
|
+
return compiled;
|
|
127
|
+
}
|
|
128
|
+
function validDateMs(date, label) {
|
|
129
|
+
const value = date.getTime();
|
|
130
|
+
if (!Number.isFinite(value))
|
|
131
|
+
throw new Error(`Automation ${label} must be a valid date.`);
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
function validResultDate(value) {
|
|
135
|
+
const result = new Date(value);
|
|
136
|
+
validDateMs(result, 'activation result');
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
function floorUtcMinute(value) {
|
|
140
|
+
return Math.floor(value / MINUTE_MS) * MINUTE_MS;
|
|
141
|
+
}
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.evaluateMonitorCondition = evaluateMonitorCondition;
|
|
4
4
|
exports.evaluateExpression = evaluateExpression;
|
|
5
|
+
exports.evaluateRestrictedExpression = evaluateRestrictedExpression;
|
|
6
|
+
exports.validateRestrictedExpression = validateRestrictedExpression;
|
|
7
|
+
exports.compileRestrictedExpression = compileRestrictedExpression;
|
|
5
8
|
exports.readMetric = readMetric;
|
|
6
9
|
function evaluateMonitorCondition(condition, event) {
|
|
7
10
|
if (condition.expression) {
|
|
@@ -27,27 +30,52 @@ function evaluateMonitorCondition(condition, event) {
|
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
function evaluateExpression(expression, event) {
|
|
30
|
-
|
|
31
|
-
return orParts.some((orPart) => splitExpression(orPart, '&&').every((andPart) => evaluateComparison(andPart.trim(), event)));
|
|
33
|
+
return evaluateRestrictedExpression(expression, (metric) => readMetric(event, metric));
|
|
32
34
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
|
|
35
|
+
function evaluateRestrictedExpression(expression, contextOrResolver) {
|
|
36
|
+
const compiled = compileRestrictedExpression(expression);
|
|
37
|
+
const resolveMetric = typeof contextOrResolver === 'function'
|
|
38
|
+
? contextOrResolver
|
|
39
|
+
: (metric) => readContextMetric(contextOrResolver, metric);
|
|
40
|
+
return compiled.some((orPart) => orPart.every((comparison) => evaluateRestrictedComparison(comparison, resolveMetric)));
|
|
41
|
+
}
|
|
42
|
+
function validateRestrictedExpression(expression) {
|
|
43
|
+
compileRestrictedExpression(expression);
|
|
44
|
+
}
|
|
45
|
+
function compileRestrictedExpression(expression) {
|
|
46
|
+
const source = String(expression || '').trim();
|
|
47
|
+
if (!source)
|
|
36
48
|
throw new Error(`Unsupported monitor condition expression: ${expression}`);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
return source.split('||').map((orPart) => {
|
|
50
|
+
if (!orPart.trim())
|
|
51
|
+
throw new Error(`Unsupported monitor condition expression: ${expression}`);
|
|
52
|
+
return orPart.split('&&').map((andPart) => compileRestrictedComparison(andPart.trim(), expression));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function compileRestrictedComparison(expression, original) {
|
|
56
|
+
const match = expression.match(/^([a-zA-Z0-9_.-]+)\s*(>=|<=|==|!=|>|<)\s*("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s&|<>=!]+)$/);
|
|
57
|
+
if (!match)
|
|
58
|
+
throw new Error(`Unsupported monitor condition expression: ${original}`);
|
|
59
|
+
return {
|
|
60
|
+
metric: String(match[1]),
|
|
42
61
|
operator: match[2],
|
|
43
|
-
|
|
44
|
-
}
|
|
62
|
+
rawValue: String(match[3]),
|
|
63
|
+
};
|
|
45
64
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
function evaluateRestrictedComparison(comparison, resolveMetric) {
|
|
66
|
+
const rawValue = comparison.rawValue.replace(/^["']|["']$/g, '');
|
|
67
|
+
const numeric = Number(rawValue);
|
|
68
|
+
const actual = resolveMetric(comparison.metric);
|
|
69
|
+
const expected = Number.isFinite(numeric) && rawValue !== '' ? numeric : rawValue;
|
|
70
|
+
switch (comparison.operator) {
|
|
71
|
+
case '>': return Number(actual) > Number(expected);
|
|
72
|
+
case '>=': return Number(actual) >= Number(expected);
|
|
73
|
+
case '<': return Number(actual) < Number(expected);
|
|
74
|
+
case '<=': return Number(actual) <= Number(expected);
|
|
75
|
+
case '==': return String(actual) === String(expected);
|
|
76
|
+
case '!=': return String(actual) !== String(expected);
|
|
77
|
+
default: return false;
|
|
78
|
+
}
|
|
51
79
|
}
|
|
52
80
|
function readMetric(event, metric) {
|
|
53
81
|
const key = String(metric || '').trim();
|
|
@@ -75,3 +103,18 @@ function readMetric(event, metric) {
|
|
|
75
103
|
return current[part];
|
|
76
104
|
}, payload);
|
|
77
105
|
}
|
|
106
|
+
function readContextMetric(context, metric) {
|
|
107
|
+
if (metric === 'abs_change_percent') {
|
|
108
|
+
return Math.abs(Number(context.change_percent ?? context.changePercent ?? 0));
|
|
109
|
+
}
|
|
110
|
+
const normalizedKey = metric.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
|
|
111
|
+
if (Object.prototype.hasOwnProperty.call(context, metric))
|
|
112
|
+
return context[metric];
|
|
113
|
+
if (Object.prototype.hasOwnProperty.call(context, normalizedKey))
|
|
114
|
+
return context[normalizedKey];
|
|
115
|
+
return metric.split('.').reduce((current, part) => {
|
|
116
|
+
if (!current || typeof current !== 'object')
|
|
117
|
+
return undefined;
|
|
118
|
+
return current[part];
|
|
119
|
+
}, context);
|
|
120
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scheduledJobToAutomationInput = scheduledJobToAutomationInput;
|
|
4
|
+
exports.automationToScheduledJob = automationToScheduledJob;
|
|
5
|
+
exports.monitorToAutomationInput = monitorToAutomationInput;
|
|
6
|
+
exports.automationToMonitor = automationToMonitor;
|
|
7
|
+
exports.automationToScheduledJobRun = automationToScheduledJobRun;
|
|
8
|
+
exports.automationToMonitorRun = automationToMonitorRun;
|
|
9
|
+
exports.latestFinishedEvaluation = latestFinishedEvaluation;
|
|
10
|
+
exports.latestAutomationRun = latestAutomationRun;
|
|
11
|
+
const superai_contracts_1 = require("../../../../packages/contracts/src/index.js");
|
|
12
|
+
const condition_evaluator_js_1 = require("./condition-evaluator.js");
|
|
13
|
+
function scheduledJobToAutomationInput(input) {
|
|
14
|
+
const triggerType = (0, superai_contracts_1.normalizeScheduledJobTriggerType)(input.triggerType);
|
|
15
|
+
const activation = triggerType === 'once'
|
|
16
|
+
? { kind: 'once', runAt: requireText(input.runAt, 'Scheduled job runAt') }
|
|
17
|
+
: triggerType === 'cron'
|
|
18
|
+
? { kind: 'cron', expression: requireText(input.cronExpr, 'Scheduled job cronExpr'), timezone: 'UTC' }
|
|
19
|
+
: fail(`Unsupported scheduled job trigger type: ${triggerType}`);
|
|
20
|
+
return {
|
|
21
|
+
workspaceId: requireText(input.workspaceId, 'Scheduled job workspaceId'),
|
|
22
|
+
title: String(input.description || '').trim() || requireText(input.promptTemplate, 'Scheduled job promptTemplate'),
|
|
23
|
+
enabled: input.enabled !== false,
|
|
24
|
+
activation,
|
|
25
|
+
condition: { kind: 'always' },
|
|
26
|
+
action: {
|
|
27
|
+
kind: 'agent-prompt',
|
|
28
|
+
promptTemplate: requireText(input.promptTemplate, 'Scheduled job promptTemplate'),
|
|
29
|
+
executionMode: legacyExecutionMode(input.executionMode, 'same-thread'),
|
|
30
|
+
},
|
|
31
|
+
delivery: { platform: requireText(input.platform, 'Scheduled job platform'), route: input.route },
|
|
32
|
+
policies: { concurrency: 'skip-if-running', cooldownMs: 0 },
|
|
33
|
+
originKind: 'scheduled-job',
|
|
34
|
+
legacyMetadata: { scheduledDescription: input.description ?? '' },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function automationToScheduledJob(automation, latestRun) {
|
|
38
|
+
assertOrigin(automation, 'scheduled-job');
|
|
39
|
+
if (automation.condition.kind !== 'always')
|
|
40
|
+
throw new Error('Scheduled-job automation condition must be always.');
|
|
41
|
+
if (automation.activation.kind !== 'cron' && automation.activation.kind !== 'once') {
|
|
42
|
+
throw new Error('Scheduled-job automation activation must be cron or once.');
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
id: automation.id,
|
|
46
|
+
workspaceId: automation.workspaceId,
|
|
47
|
+
platform: automation.delivery.platform,
|
|
48
|
+
route: automation.delivery.route,
|
|
49
|
+
executionMode: automation.action.executionMode,
|
|
50
|
+
triggerType: automation.activation.kind,
|
|
51
|
+
...(automation.activation.kind === 'cron' ? { cronExpr: automation.activation.expression } : { runAt: automation.activation.runAt }),
|
|
52
|
+
promptTemplate: automation.action.promptTemplate,
|
|
53
|
+
description: automation.legacyMetadata?.scheduledDescription ?? automation.title,
|
|
54
|
+
enabled: automation.enabled,
|
|
55
|
+
concurrencyPolicy: 'skip_if_running',
|
|
56
|
+
createdAt: automation.createdAt,
|
|
57
|
+
updatedAt: automation.updatedAt,
|
|
58
|
+
...(latestRun?.finishedAt || latestRun?.startedAt || automation.lastEvaluationAt
|
|
59
|
+
? { lastRunAt: latestRun?.finishedAt || latestRun?.startedAt || automation.lastEvaluationAt }
|
|
60
|
+
: {}),
|
|
61
|
+
...(latestRun ? { lastStatus: latestRun.status } : {}),
|
|
62
|
+
...(latestRun?.error ? { lastError: latestRun.error } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function monitorToAutomationInput(input) {
|
|
66
|
+
return {
|
|
67
|
+
workspaceId: requireText(input.workspaceId, 'Automation monitor workspaceId'),
|
|
68
|
+
title: requireText(input.title, 'Automation monitor title'),
|
|
69
|
+
enabled: input.enabled !== false,
|
|
70
|
+
activation: {
|
|
71
|
+
kind: 'provider-event',
|
|
72
|
+
sourceType: requireText(input.sourceType, 'Automation monitor sourceType'),
|
|
73
|
+
sourceConfig: input.sourceConfig || {},
|
|
74
|
+
},
|
|
75
|
+
condition: { kind: 'expression', expression: monitorConditionExpression(input.condition) },
|
|
76
|
+
action: {
|
|
77
|
+
kind: 'agent-prompt',
|
|
78
|
+
promptTemplate: requireText(input.promptTemplate, 'Automation monitor promptTemplate'),
|
|
79
|
+
executionMode: legacyExecutionMode(input.executionMode, 'side-thread'),
|
|
80
|
+
},
|
|
81
|
+
delivery: { platform: requireText(input.platform, 'Automation monitor platform'), route: input.route },
|
|
82
|
+
policies: { concurrency: 'skip-if-running', cooldownMs: input.cooldownMs ?? 15 * 60 * 1_000 },
|
|
83
|
+
originKind: 'automation-monitor',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function automationToMonitor(automation, latestEvaluation, latestRun, latestStateEvaluation) {
|
|
87
|
+
assertOrigin(automation, 'automation-monitor');
|
|
88
|
+
if (automation.activation.kind !== 'provider-event') {
|
|
89
|
+
throw new Error('Automation-monitor automation activation must be provider-event.');
|
|
90
|
+
}
|
|
91
|
+
if (automation.condition.kind !== 'expression') {
|
|
92
|
+
throw new Error('Automation-monitor automation condition must be expression.');
|
|
93
|
+
}
|
|
94
|
+
const stateEvaluation = latestStateEvaluation || latestEvaluation;
|
|
95
|
+
const lastState = stateEvaluation?.status === 'finished' ? stateEvaluation.nextState : undefined;
|
|
96
|
+
const latestEvaluationStatus = latestEvaluation && latestRun?.evaluationId !== latestEvaluation.id
|
|
97
|
+
? evaluationStatus(latestEvaluation)
|
|
98
|
+
: undefined;
|
|
99
|
+
return {
|
|
100
|
+
id: automation.id,
|
|
101
|
+
workspaceId: automation.workspaceId,
|
|
102
|
+
title: automation.title,
|
|
103
|
+
sourceType: automation.activation.sourceType,
|
|
104
|
+
sourceConfig: automation.activation.sourceConfig,
|
|
105
|
+
condition: expressionToMonitorCondition(automation.condition.expression),
|
|
106
|
+
promptTemplate: automation.action.promptTemplate,
|
|
107
|
+
platform: automation.delivery.platform,
|
|
108
|
+
route: automation.delivery.route,
|
|
109
|
+
executionMode: automation.action.executionMode,
|
|
110
|
+
enabled: automation.enabled,
|
|
111
|
+
cooldownMs: automation.policies.cooldownMs,
|
|
112
|
+
concurrencyPolicy: 'skip_if_running',
|
|
113
|
+
...(lastState ? { lastState } : {}),
|
|
114
|
+
createdAt: automation.createdAt,
|
|
115
|
+
updatedAt: automation.updatedAt,
|
|
116
|
+
...(automation.lastTriggeredAt ? { lastTriggeredAt: automation.lastTriggeredAt } : {}),
|
|
117
|
+
...(latestEvaluationStatus ? { lastStatus: latestEvaluationStatus } : latestRun ? { lastStatus: latestRun.status } : {}),
|
|
118
|
+
...(latestEvaluation?.status === 'finished' && latestEvaluation.conditionOutcome === 'error' && latestEvaluation.resultSummary
|
|
119
|
+
? { lastError: latestEvaluation.resultSummary }
|
|
120
|
+
: latestRun?.error
|
|
121
|
+
? { lastError: latestRun.error }
|
|
122
|
+
: automation.consecutiveEvaluationFailures > 0 && latestEvaluation?.status === 'finished' && latestEvaluation.resultSummary
|
|
123
|
+
? { lastError: latestEvaluation.resultSummary }
|
|
124
|
+
: {}),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function automationToScheduledJobRun(evaluation, run) {
|
|
128
|
+
const status = run?.status || evaluationStatus(evaluation);
|
|
129
|
+
const evaluationError = !run && evaluation.status === 'finished'
|
|
130
|
+
&& (evaluation.conditionOutcome === 'skipped' || evaluation.conditionOutcome === 'error')
|
|
131
|
+
? evaluation.resultSummary
|
|
132
|
+
: undefined;
|
|
133
|
+
return {
|
|
134
|
+
id: run?.id || evaluation.id,
|
|
135
|
+
jobId: evaluation.automationId,
|
|
136
|
+
status,
|
|
137
|
+
triggeredAt: evaluation.status === 'finished' ? evaluation.triggeredAt || evaluation.startedAt : evaluation.startedAt,
|
|
138
|
+
...(run?.startedAt ? { startedAt: run.startedAt } : {}),
|
|
139
|
+
...(run?.finishedAt ? { finishedAt: run.finishedAt } : {}),
|
|
140
|
+
...(run?.error || evaluationError
|
|
141
|
+
? { error: run?.error || evaluationError, deliveryError: run?.error || evaluationError }
|
|
142
|
+
: {}),
|
|
143
|
+
...(run?.threadId ? { threadId: run.threadId } : {}),
|
|
144
|
+
...(run?.acpRunId ? { runId: run.acpRunId } : {}),
|
|
145
|
+
...(legacyDeliveryMode(run?.bridgeActivity?.deliveryMode) ? { deliveryMode: legacyDeliveryMode(run?.bridgeActivity?.deliveryMode) } : {}),
|
|
146
|
+
...(run?.deliveryStatus
|
|
147
|
+
? { deliveryStatus: legacyDeliveryStatus(run.deliveryStatus) }
|
|
148
|
+
: evaluationError ? { deliveryStatus: status === 'failed' ? 'failed' : 'skipped' } : {}),
|
|
149
|
+
...(typeof run?.bridgeActivity?.lastBridgeEventAt === 'string'
|
|
150
|
+
? { lastBridgeEventAt: run.bridgeActivity.lastBridgeEventAt }
|
|
151
|
+
: {}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function automationToMonitorRun(evaluation, run) {
|
|
155
|
+
const payload = evaluation.status === 'finished' ? evaluation.payload : undefined;
|
|
156
|
+
const eventSnapshot = payload?.eventSnapshot ? parseEventSnapshot(payload.eventSnapshot) : undefined;
|
|
157
|
+
const evaluationError = !run && evaluation.status === 'finished'
|
|
158
|
+
&& (evaluation.conditionOutcome === 'skipped' || evaluation.conditionOutcome === 'error')
|
|
159
|
+
? evaluation.resultSummary
|
|
160
|
+
: undefined;
|
|
161
|
+
return {
|
|
162
|
+
id: run?.id || evaluation.id,
|
|
163
|
+
monitorId: evaluation.automationId,
|
|
164
|
+
status: run?.status || evaluationStatus(evaluation),
|
|
165
|
+
triggeredAt: eventSnapshot?.occurredAt || (evaluation.status === 'finished' ? evaluation.triggeredAt : undefined) || evaluation.startedAt,
|
|
166
|
+
...(run?.startedAt ? { startedAt: run.startedAt } : {}),
|
|
167
|
+
...(run?.finishedAt ? { finishedAt: run.finishedAt } : {}),
|
|
168
|
+
...(run?.error || evaluationError
|
|
169
|
+
? { error: run?.error || evaluationError, deliveryError: run?.error || evaluationError }
|
|
170
|
+
: {}),
|
|
171
|
+
...(eventSnapshot ? { eventSnapshot } : {}),
|
|
172
|
+
...(run?.threadId ? { threadId: run.threadId } : {}),
|
|
173
|
+
...(run?.acpRunId ? { runId: run.acpRunId } : {}),
|
|
174
|
+
...(legacyDeliveryMode(run?.bridgeActivity?.deliveryMode) ? { deliveryMode: legacyDeliveryMode(run?.bridgeActivity?.deliveryMode) } : {}),
|
|
175
|
+
...(run?.deliveryStatus
|
|
176
|
+
? { deliveryStatus: legacyDeliveryStatus(run.deliveryStatus) }
|
|
177
|
+
: evaluationError ? { deliveryStatus: (run?.status || evaluationStatus(evaluation)) === 'failed' ? 'failed' : 'skipped' } : {}),
|
|
178
|
+
...(typeof run?.bridgeActivity?.lastBridgeEventAt === 'string'
|
|
179
|
+
? { lastBridgeEventAt: run.bridgeActivity.lastBridgeEventAt }
|
|
180
|
+
: {}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function latestFinishedEvaluation(evaluations) {
|
|
184
|
+
return evaluations.find((evaluation) => evaluation.status === 'finished');
|
|
185
|
+
}
|
|
186
|
+
function latestAutomationRun(runs) {
|
|
187
|
+
return runs[0];
|
|
188
|
+
}
|
|
189
|
+
function monitorConditionExpression(condition) {
|
|
190
|
+
const operator = (0, superai_contracts_1.normalizeAutomationMonitorConditionOperator)(condition.operator);
|
|
191
|
+
if (typeof condition.value !== 'number' && typeof condition.value !== 'string' && typeof condition.value !== 'boolean') {
|
|
192
|
+
throw new Error('Automation monitor condition value must be a number, string, or boolean.');
|
|
193
|
+
}
|
|
194
|
+
if (condition.expression?.trim()) {
|
|
195
|
+
const expression = condition.expression.trim();
|
|
196
|
+
(0, condition_evaluator_js_1.validateRestrictedExpression)(expression);
|
|
197
|
+
return expression;
|
|
198
|
+
}
|
|
199
|
+
const metric = requireText(condition.metric, 'Automation monitor condition metric');
|
|
200
|
+
const expression = `${metric} ${operator} ${JSON.stringify(condition.value)}`;
|
|
201
|
+
(0, condition_evaluator_js_1.validateRestrictedExpression)(expression);
|
|
202
|
+
return expression;
|
|
203
|
+
}
|
|
204
|
+
function expressionToMonitorCondition(expression) {
|
|
205
|
+
if (expression.includes('&&') || expression.includes('||')) {
|
|
206
|
+
return { metric: 'expression', operator: '==', value: true, expression };
|
|
207
|
+
}
|
|
208
|
+
const match = expression.trim().match(/^([a-zA-Z0-9_.-]+)\s*(>=|<=|==|!=|>|<)\s*(.+)$/);
|
|
209
|
+
if (!match)
|
|
210
|
+
return { metric: 'expression', operator: '==', value: true, expression };
|
|
211
|
+
const raw = match[3].trim();
|
|
212
|
+
let value = raw.replace(/^["']|["']$/g, '');
|
|
213
|
+
if (raw === 'true' || raw === 'false')
|
|
214
|
+
value = raw === 'true';
|
|
215
|
+
else if (raw !== '' && Number.isFinite(Number(raw)))
|
|
216
|
+
value = Number(raw);
|
|
217
|
+
return {
|
|
218
|
+
metric: match[1],
|
|
219
|
+
operator: parseMonitorOperator(match[2]),
|
|
220
|
+
value,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function evaluationStatus(evaluation) {
|
|
224
|
+
if (evaluation.status === 'running')
|
|
225
|
+
return 'running';
|
|
226
|
+
if (evaluation.conditionOutcome === 'error')
|
|
227
|
+
return 'failed';
|
|
228
|
+
if (evaluation.triggerDecision !== 'triggered')
|
|
229
|
+
return 'skipped';
|
|
230
|
+
return 'queued';
|
|
231
|
+
}
|
|
232
|
+
function legacyDeliveryStatus(status) {
|
|
233
|
+
if (status === 'delivered')
|
|
234
|
+
return 'succeeded';
|
|
235
|
+
if (status === 'delivering')
|
|
236
|
+
return 'streaming';
|
|
237
|
+
return status;
|
|
238
|
+
}
|
|
239
|
+
function legacyDeliveryMode(value) {
|
|
240
|
+
return value === 'thread-only' || value === 'bridge-stream' || value === 'final-message' ? value : undefined;
|
|
241
|
+
}
|
|
242
|
+
function parseEventSnapshot(value) {
|
|
243
|
+
if (!isRecord(value))
|
|
244
|
+
throw new Error('Automation eventSnapshot must be an object.');
|
|
245
|
+
const candidate = value;
|
|
246
|
+
if (!isRecord(candidate.payload)) {
|
|
247
|
+
throw new Error('Automation eventSnapshot payload must be an object.');
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
id: requireText(candidate.id, 'Automation eventSnapshot id'),
|
|
251
|
+
sourceType: requireText(candidate.sourceType, 'Automation eventSnapshot sourceType'),
|
|
252
|
+
occurredAt: requireText(candidate.occurredAt, 'Automation eventSnapshot occurredAt'),
|
|
253
|
+
subject: requireText(candidate.subject, 'Automation eventSnapshot subject'),
|
|
254
|
+
...(typeof candidate.summary === 'string' ? { summary: candidate.summary } : {}),
|
|
255
|
+
payload: candidate.payload,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function parseMonitorOperator(value) {
|
|
259
|
+
if (value === '>' || value === '>=' || value === '<' || value === '<=' || value === '==' || value === '!=')
|
|
260
|
+
return value;
|
|
261
|
+
throw new Error(`Automation monitor operator is invalid: ${String(value)}`);
|
|
262
|
+
}
|
|
263
|
+
function isRecord(value) {
|
|
264
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
265
|
+
}
|
|
266
|
+
function assertOrigin(automation, expected) {
|
|
267
|
+
if (automation.originKind !== expected)
|
|
268
|
+
throw new Error(`Automation must have ${expected} origin.`);
|
|
269
|
+
}
|
|
270
|
+
function requireText(value, label) {
|
|
271
|
+
const text = String(value || '').trim();
|
|
272
|
+
if (!text)
|
|
273
|
+
throw new Error(`${label} is required.`);
|
|
274
|
+
return text;
|
|
275
|
+
}
|
|
276
|
+
function legacyExecutionMode(value, fallback) {
|
|
277
|
+
const normalized = (0, superai_contracts_1.normalizeScheduledJobExecutionMode)(value, fallback);
|
|
278
|
+
if (normalized === 'same-thread')
|
|
279
|
+
return 'same-thread';
|
|
280
|
+
if (normalized === 'side-thread')
|
|
281
|
+
return 'side-thread';
|
|
282
|
+
throw new Error('Scheduled job execution mode must be same-thread or side-thread.');
|
|
283
|
+
}
|
|
284
|
+
function fail(message) {
|
|
285
|
+
throw new Error(message);
|
|
286
|
+
}
|