yaml-flow 3.0.0 → 3.1.0
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 +44 -23
- package/dist/{constants-B_ftYTTE.d.ts → constants-B2zqu10b.d.ts} +7 -57
- package/dist/{constants-CiyHX8L-.d.cts → constants-DJZU1pwJ.d.cts} +7 -57
- package/dist/continuous-event-graph/index.cjs +1161 -182
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +567 -48
- package/dist/continuous-event-graph/index.d.ts +567 -48
- package/dist/continuous-event-graph/index.js +1151 -183
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/event-graph/index.cjs +35 -11
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.d.cts +14 -5
- package/dist/event-graph/index.d.ts +14 -5
- package/dist/event-graph/index.js +34 -11
- package/dist/event-graph/index.js.map +1 -1
- package/dist/index.cjs +945 -414
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +936 -415
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +31 -7
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.d.cts +2 -2
- package/dist/inference/index.d.ts +2 -2
- package/dist/inference/index.js +31 -7
- package/dist/inference/index.js.map +1 -1
- package/dist/{types-CxJg9Jrt.d.cts → types-BwvgvlOO.d.cts} +2 -2
- package/dist/{types-BuEo3wVG.d.ts → types-ClRA8hzC.d.ts} +2 -2
- package/dist/{types-BpWrH1sf.d.cts → types-DEj7OakX.d.cts} +14 -4
- package/dist/{types-BpWrH1sf.d.ts → types-DEj7OakX.d.ts} +14 -4
- package/dist/validate-DEZ2Ymdb.d.ts +53 -0
- package/dist/validate-DqKTZg_o.d.cts +53 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import addFormats from 'ajv-formats';
|
|
2
2
|
import { existsSync, writeFileSync, appendFileSync, readFileSync } from 'fs';
|
|
3
|
-
import {
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { exec, execFile } from 'child_process';
|
|
4
5
|
|
|
5
6
|
var __create = Object.create;
|
|
6
7
|
var __defProp = Object.defineProperty;
|
|
@@ -7225,7 +7226,7 @@ function addDynamicTask(graph, taskName, taskConfig) {
|
|
|
7225
7226
|
}
|
|
7226
7227
|
};
|
|
7227
7228
|
}
|
|
7228
|
-
function
|
|
7229
|
+
function createDefaultGraphEngineStore() {
|
|
7229
7230
|
return {
|
|
7230
7231
|
status: "not-started",
|
|
7231
7232
|
executionCount: 0,
|
|
@@ -7238,7 +7239,7 @@ function createDefaultTaskState() {
|
|
|
7238
7239
|
function createInitialExecutionState(graph, executionId) {
|
|
7239
7240
|
const tasks = {};
|
|
7240
7241
|
for (const taskName of Object.keys(graph.tasks)) {
|
|
7241
|
-
tasks[taskName] =
|
|
7242
|
+
tasks[taskName] = createDefaultGraphEngineStore();
|
|
7242
7243
|
}
|
|
7243
7244
|
return {
|
|
7244
7245
|
status: "running",
|
|
@@ -7759,7 +7760,7 @@ function selectOptimalTasks(candidates, graph, state, conflictStrategy) {
|
|
|
7759
7760
|
|
|
7760
7761
|
// src/event-graph/task-transitions.ts
|
|
7761
7762
|
function applyTaskStart(state, taskName) {
|
|
7762
|
-
const existingTask = state.tasks[taskName] ??
|
|
7763
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7763
7764
|
const updatedTask = {
|
|
7764
7765
|
...existingTask,
|
|
7765
7766
|
status: "running",
|
|
@@ -7774,8 +7775,8 @@ function applyTaskStart(state, taskName) {
|
|
|
7774
7775
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7775
7776
|
};
|
|
7776
7777
|
}
|
|
7777
|
-
function applyTaskCompletion(state, graph, taskName, result, dataHash) {
|
|
7778
|
-
const existingTask = state.tasks[taskName] ??
|
|
7778
|
+
function applyTaskCompletion(state, graph, taskName, result, dataHash, data) {
|
|
7779
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7779
7780
|
const taskConfig = graph.tasks[taskName];
|
|
7780
7781
|
if (!taskConfig) {
|
|
7781
7782
|
throw new Error(`Task "${taskName}" not found in graph`);
|
|
@@ -7807,6 +7808,7 @@ function applyTaskCompletion(state, graph, taskName, result, dataHash) {
|
|
|
7807
7808
|
executionCount: existingTask.executionCount + 1,
|
|
7808
7809
|
lastEpoch: existingTask.executionCount + 1,
|
|
7809
7810
|
lastDataHash: dataHash,
|
|
7811
|
+
data,
|
|
7810
7812
|
lastConsumedHashes,
|
|
7811
7813
|
error: void 0
|
|
7812
7814
|
};
|
|
@@ -7819,7 +7821,7 @@ function applyTaskCompletion(state, graph, taskName, result, dataHash) {
|
|
|
7819
7821
|
};
|
|
7820
7822
|
}
|
|
7821
7823
|
function applyTaskFailure(state, graph, taskName, error) {
|
|
7822
|
-
const existingTask = state.tasks[taskName] ??
|
|
7824
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7823
7825
|
const taskConfig = graph.tasks[taskName];
|
|
7824
7826
|
if (taskConfig?.retry) {
|
|
7825
7827
|
const retryCount = existingTask.retryCount + 1;
|
|
@@ -7862,7 +7864,7 @@ function applyTaskFailure(state, graph, taskName, error) {
|
|
|
7862
7864
|
};
|
|
7863
7865
|
}
|
|
7864
7866
|
function applyTaskProgress(state, taskName, message, progress) {
|
|
7865
|
-
const existingTask = state.tasks[taskName] ??
|
|
7867
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7866
7868
|
const updatedTask = {
|
|
7867
7869
|
...existingTask,
|
|
7868
7870
|
progress: typeof progress === "number" ? progress : existingTask.progress,
|
|
@@ -7878,7 +7880,27 @@ function applyTaskProgress(state, taskName, message, progress) {
|
|
|
7878
7880
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7879
7881
|
};
|
|
7880
7882
|
}
|
|
7881
|
-
function
|
|
7883
|
+
function applyTaskRestart(state, taskName) {
|
|
7884
|
+
const existingTask = state.tasks[taskName];
|
|
7885
|
+
if (!existingTask) return state;
|
|
7886
|
+
const updatedTask = {
|
|
7887
|
+
...existingTask,
|
|
7888
|
+
status: "not-started",
|
|
7889
|
+
startedAt: void 0,
|
|
7890
|
+
completedAt: void 0,
|
|
7891
|
+
failedAt: void 0,
|
|
7892
|
+
error: void 0,
|
|
7893
|
+
data: void 0,
|
|
7894
|
+
progress: null,
|
|
7895
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7896
|
+
};
|
|
7897
|
+
return {
|
|
7898
|
+
...state,
|
|
7899
|
+
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
7900
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7901
|
+
};
|
|
7902
|
+
}
|
|
7903
|
+
function createDefaultGraphEngineStore2() {
|
|
7882
7904
|
return {
|
|
7883
7905
|
status: "not-started",
|
|
7884
7906
|
executionCount: 0,
|
|
@@ -7898,11 +7920,13 @@ function apply(state, event, graph) {
|
|
|
7898
7920
|
case "task-started":
|
|
7899
7921
|
return applyTaskStart(state, event.taskName);
|
|
7900
7922
|
case "task-completed":
|
|
7901
|
-
return applyTaskCompletion(state, graph, event.taskName, event.result, event.dataHash);
|
|
7923
|
+
return applyTaskCompletion(state, graph, event.taskName, event.result, event.dataHash, event.data);
|
|
7902
7924
|
case "task-failed":
|
|
7903
7925
|
return applyTaskFailure(state, graph, event.taskName, event.error);
|
|
7904
7926
|
case "task-progress":
|
|
7905
7927
|
return applyTaskProgress(state, event.taskName, event.message, event.progress);
|
|
7928
|
+
case "task-restart":
|
|
7929
|
+
return applyTaskRestart(state, event.taskName);
|
|
7906
7930
|
case "inject-tokens":
|
|
7907
7931
|
return applyInjectTokens(state, event.tokens);
|
|
7908
7932
|
case "agent-action":
|
|
@@ -7973,7 +7997,7 @@ function applyTaskCreation(state, taskName, taskConfig) {
|
|
|
7973
7997
|
...state,
|
|
7974
7998
|
tasks: {
|
|
7975
7999
|
...state.tasks,
|
|
7976
|
-
[taskName]:
|
|
8000
|
+
[taskName]: createDefaultGraphEngineStore()
|
|
7977
8001
|
},
|
|
7978
8002
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7979
8003
|
};
|
|
@@ -9223,7 +9247,7 @@ function createLiveGraph(config, executionId) {
|
|
|
9223
9247
|
const id = executionId ?? `live-${Date.now()}`;
|
|
9224
9248
|
const tasks = {};
|
|
9225
9249
|
for (const taskName of Object.keys(config.tasks)) {
|
|
9226
|
-
tasks[taskName] =
|
|
9250
|
+
tasks[taskName] = createDefaultGraphEngineStore3();
|
|
9227
9251
|
}
|
|
9228
9252
|
const state = {
|
|
9229
9253
|
status: "running",
|
|
@@ -9251,7 +9275,7 @@ function applyEvent(live, event) {
|
|
|
9251
9275
|
newState = applyTaskStart(state, event.taskName);
|
|
9252
9276
|
break;
|
|
9253
9277
|
case "task-completed":
|
|
9254
|
-
newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash);
|
|
9278
|
+
newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data);
|
|
9255
9279
|
break;
|
|
9256
9280
|
case "task-failed":
|
|
9257
9281
|
newState = applyTaskFailure(state, config, event.taskName, event.error);
|
|
@@ -9259,6 +9283,9 @@ function applyEvent(live, event) {
|
|
|
9259
9283
|
case "task-progress":
|
|
9260
9284
|
newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
|
|
9261
9285
|
break;
|
|
9286
|
+
case "task-restart":
|
|
9287
|
+
newState = applyTaskRestart(state, event.taskName);
|
|
9288
|
+
break;
|
|
9262
9289
|
case "inject-tokens":
|
|
9263
9290
|
newState = {
|
|
9264
9291
|
...state,
|
|
@@ -9286,7 +9313,7 @@ function addNode(live, name, taskConfig) {
|
|
|
9286
9313
|
},
|
|
9287
9314
|
state: {
|
|
9288
9315
|
...live.state,
|
|
9289
|
-
tasks: { ...live.state.tasks, [name]:
|
|
9316
|
+
tasks: { ...live.state.tasks, [name]: createDefaultGraphEngineStore3() },
|
|
9290
9317
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
9291
9318
|
}
|
|
9292
9319
|
};
|
|
@@ -9403,7 +9430,7 @@ function resetNode(live, name) {
|
|
|
9403
9430
|
...live.state,
|
|
9404
9431
|
tasks: {
|
|
9405
9432
|
...live.state.tasks,
|
|
9406
|
-
[name]:
|
|
9433
|
+
[name]: createDefaultGraphEngineStore3()
|
|
9407
9434
|
},
|
|
9408
9435
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
9409
9436
|
}
|
|
@@ -9442,7 +9469,7 @@ function enableNode(live, name) {
|
|
|
9442
9469
|
function getNode(live, name) {
|
|
9443
9470
|
const config = live.config.tasks[name];
|
|
9444
9471
|
if (!config) return void 0;
|
|
9445
|
-
const state = live.state.tasks[name] ??
|
|
9472
|
+
const state = live.state.tasks[name] ?? createDefaultGraphEngineStore3();
|
|
9446
9473
|
return { name, config, state };
|
|
9447
9474
|
}
|
|
9448
9475
|
function snapshot(live) {
|
|
@@ -9480,7 +9507,7 @@ function restore(data) {
|
|
|
9480
9507
|
}
|
|
9481
9508
|
return { config, state };
|
|
9482
9509
|
}
|
|
9483
|
-
function
|
|
9510
|
+
function createDefaultGraphEngineStore3() {
|
|
9484
9511
|
return {
|
|
9485
9512
|
status: "not-started",
|
|
9486
9513
|
executionCount: 0,
|
|
@@ -10037,23 +10064,43 @@ var FileJournal = class {
|
|
|
10037
10064
|
}
|
|
10038
10065
|
}
|
|
10039
10066
|
};
|
|
10040
|
-
|
|
10041
|
-
|
|
10067
|
+
function computeDataHash(data) {
|
|
10068
|
+
const json = stableStringify(data);
|
|
10069
|
+
return createHash("sha256").update(json).digest("hex").slice(0, 16);
|
|
10070
|
+
}
|
|
10071
|
+
function stableStringify(value) {
|
|
10072
|
+
if (value === null || value === void 0 || typeof value !== "object") {
|
|
10073
|
+
return JSON.stringify(value);
|
|
10074
|
+
}
|
|
10075
|
+
if (Array.isArray(value)) {
|
|
10076
|
+
return "[" + value.map(stableStringify).join(",") + "]";
|
|
10077
|
+
}
|
|
10078
|
+
const obj = value;
|
|
10079
|
+
const keys = Object.keys(obj).sort();
|
|
10080
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
|
|
10081
|
+
}
|
|
10082
|
+
function encodeCallbackToken(taskName) {
|
|
10083
|
+
const payload = JSON.stringify({ t: taskName, n: Date.now().toString(36) + Math.random().toString(36).slice(2, 6) });
|
|
10084
|
+
return Buffer.from(payload).toString("base64url");
|
|
10085
|
+
}
|
|
10086
|
+
function decodeCallbackToken(token) {
|
|
10087
|
+
try {
|
|
10088
|
+
const payload = JSON.parse(Buffer.from(token, "base64url").toString());
|
|
10089
|
+
if (typeof payload?.t === "string") return { taskName: payload.t };
|
|
10090
|
+
return null;
|
|
10091
|
+
} catch {
|
|
10092
|
+
return null;
|
|
10093
|
+
}
|
|
10094
|
+
}
|
|
10042
10095
|
function createReactiveGraph(config, options, executionId) {
|
|
10043
10096
|
const {
|
|
10044
10097
|
handlers: initialHandlers,
|
|
10045
|
-
maxDispatchRetries = 3,
|
|
10046
|
-
defaultTimeoutMs = 3e4,
|
|
10047
10098
|
journal = new MemoryJournal(),
|
|
10048
|
-
onDispatchFailed,
|
|
10049
|
-
onAbandoned,
|
|
10050
10099
|
onDrain
|
|
10051
10100
|
} = options;
|
|
10052
10101
|
let live = createLiveGraph(config, executionId);
|
|
10053
10102
|
let disposed = false;
|
|
10054
10103
|
const handlers = new Map(Object.entries(initialHandlers));
|
|
10055
|
-
const dispatched = /* @__PURE__ */ new Map();
|
|
10056
|
-
const timeoutTimers = /* @__PURE__ */ new Map();
|
|
10057
10104
|
let draining = false;
|
|
10058
10105
|
let drainQueued = false;
|
|
10059
10106
|
function drain() {
|
|
@@ -10073,201 +10120,183 @@ function createReactiveGraph(config, options, executionId) {
|
|
|
10073
10120
|
}
|
|
10074
10121
|
}
|
|
10075
10122
|
function drainOnce() {
|
|
10076
|
-
sweepTimeouts();
|
|
10077
10123
|
const events = journal.drain();
|
|
10078
|
-
for (const event of events) {
|
|
10079
|
-
if (event.type === "task-completed" || event.type === "task-failed") {
|
|
10080
|
-
const taskName = event.taskName;
|
|
10081
|
-
dispatched.delete(taskName);
|
|
10082
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10083
|
-
timeoutTimers.delete(taskName);
|
|
10084
|
-
}
|
|
10085
|
-
}
|
|
10086
10124
|
if (events.length > 0) {
|
|
10087
10125
|
live = applyEvents(live, events);
|
|
10088
10126
|
}
|
|
10089
10127
|
const result = schedule(live);
|
|
10090
|
-
if (
|
|
10091
|
-
onDrain(events, live, result);
|
|
10128
|
+
if (events.length > 0) {
|
|
10129
|
+
onDrain?.(events, live, result);
|
|
10092
10130
|
}
|
|
10093
10131
|
for (const taskName of result.eligible) {
|
|
10094
|
-
if (dispatched.has(taskName)) continue;
|
|
10095
10132
|
dispatchTask(taskName);
|
|
10096
10133
|
}
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10134
|
+
}
|
|
10135
|
+
function resolveUpstreamState(taskName) {
|
|
10136
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10137
|
+
const requires = taskConfig.requires ?? [];
|
|
10138
|
+
const tokenToTask = /* @__PURE__ */ new Map();
|
|
10139
|
+
for (const [name, cfg] of Object.entries(live.config.tasks)) {
|
|
10140
|
+
for (const token of cfg.provides ?? []) {
|
|
10141
|
+
tokenToTask.set(token, name);
|
|
10142
|
+
}
|
|
10143
|
+
}
|
|
10144
|
+
const state = {};
|
|
10145
|
+
for (const token of requires) {
|
|
10146
|
+
const producerTask = tokenToTask.get(token);
|
|
10147
|
+
if (producerTask) {
|
|
10148
|
+
state[token] = live.state.tasks[producerTask]?.data;
|
|
10149
|
+
} else {
|
|
10150
|
+
state[token] = void 0;
|
|
10151
|
+
}
|
|
10152
|
+
}
|
|
10153
|
+
return state;
|
|
10154
|
+
}
|
|
10155
|
+
async function runPipeline(taskName, callbackToken) {
|
|
10156
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10157
|
+
const handlerNames = taskConfig.taskHandlers ?? [];
|
|
10158
|
+
const upstreamState = resolveUpstreamState(taskName);
|
|
10159
|
+
for (const handlerName of handlerNames) {
|
|
10160
|
+
const handler = handlers.get(handlerName);
|
|
10161
|
+
if (!handler) {
|
|
10162
|
+
throw new Error(`Handler '${handlerName}' not found in registry (task '${taskName}')`);
|
|
10163
|
+
}
|
|
10164
|
+
const input = {
|
|
10165
|
+
nodeId: taskName,
|
|
10166
|
+
state: upstreamState,
|
|
10167
|
+
taskState: live.state.tasks[taskName],
|
|
10168
|
+
config: taskConfig,
|
|
10169
|
+
callbackToken
|
|
10170
|
+
};
|
|
10171
|
+
const status = await handler(input);
|
|
10172
|
+
if (status === "task-initiate-failure") {
|
|
10173
|
+
throw new Error(`Handler '${handlerName}' returned task-initiate-failure (task '${taskName}')`);
|
|
10100
10174
|
}
|
|
10101
10175
|
}
|
|
10102
10176
|
}
|
|
10103
10177
|
function dispatchTask(taskName) {
|
|
10104
|
-
const
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
type: "task-failed",
|
|
10108
|
-
taskName,
|
|
10109
|
-
error: `No handler registered for task "${taskName}"`,
|
|
10110
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10111
|
-
});
|
|
10112
|
-
drainQueued = true;
|
|
10113
|
-
return;
|
|
10114
|
-
}
|
|
10115
|
-
const existing = dispatched.get(taskName);
|
|
10116
|
-
const attempt = existing ? existing.dispatchAttempts + 1 : 1;
|
|
10117
|
-
if (attempt > maxDispatchRetries) {
|
|
10118
|
-
dispatched.set(taskName, {
|
|
10119
|
-
status: "abandoned",
|
|
10120
|
-
dispatchedAt: existing?.dispatchedAt ?? Date.now(),
|
|
10121
|
-
dispatchAttempts: attempt - 1,
|
|
10122
|
-
lastError: existing?.lastError
|
|
10123
|
-
});
|
|
10124
|
-
onAbandoned?.(taskName);
|
|
10125
|
-
journal.append({
|
|
10126
|
-
type: "task-failed",
|
|
10127
|
-
taskName,
|
|
10128
|
-
error: `dispatch-abandoned: handler unreachable after ${attempt - 1} attempts${existing?.lastError ? ` (${existing.lastError})` : ""}`,
|
|
10129
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10130
|
-
});
|
|
10131
|
-
drainQueued = true;
|
|
10178
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10179
|
+
const handlerNames = taskConfig?.taskHandlers;
|
|
10180
|
+
if (!handlerNames || handlerNames.length === 0) {
|
|
10132
10181
|
return;
|
|
10133
10182
|
}
|
|
10134
|
-
dispatched.set(taskName, {
|
|
10135
|
-
status: "initiated",
|
|
10136
|
-
dispatchedAt: Date.now(),
|
|
10137
|
-
dispatchAttempts: attempt
|
|
10138
|
-
});
|
|
10139
10183
|
journal.append({
|
|
10140
10184
|
type: "task-started",
|
|
10141
10185
|
taskName,
|
|
10142
10186
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10143
10187
|
});
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
});
|
|
10153
|
-
dispatched.set(taskName, {
|
|
10154
|
-
...entry,
|
|
10155
|
-
status: entry.dispatchAttempts >= maxDispatchRetries ? "abandoned" : "retry-queued"
|
|
10156
|
-
});
|
|
10157
|
-
if (entry.dispatchAttempts >= maxDispatchRetries) {
|
|
10158
|
-
onAbandoned?.(taskName);
|
|
10159
|
-
journal.append({
|
|
10160
|
-
type: "task-failed",
|
|
10161
|
-
taskName,
|
|
10162
|
-
error: `dispatch-timeout: no callback after ${defaultTimeoutMs}ms (${entry.dispatchAttempts} attempts)`,
|
|
10163
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10164
|
-
});
|
|
10165
|
-
}
|
|
10166
|
-
drain();
|
|
10167
|
-
}
|
|
10168
|
-
}, defaultTimeoutMs);
|
|
10169
|
-
timeoutTimers.set(taskName, timer);
|
|
10170
|
-
}
|
|
10171
|
-
const ctx = {
|
|
10172
|
-
taskName,
|
|
10173
|
-
live,
|
|
10174
|
-
config: live.config.tasks[taskName]
|
|
10175
|
-
};
|
|
10176
|
-
try {
|
|
10177
|
-
const promise = handler(ctx);
|
|
10178
|
-
promise.then(
|
|
10179
|
-
(handlerResult) => {
|
|
10180
|
-
if (disposed) return;
|
|
10181
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10182
|
-
timeoutTimers.delete(taskName);
|
|
10183
|
-
journal.append({
|
|
10184
|
-
type: "task-completed",
|
|
10185
|
-
taskName,
|
|
10186
|
-
result: handlerResult.result,
|
|
10187
|
-
data: handlerResult.data,
|
|
10188
|
-
dataHash: handlerResult.dataHash,
|
|
10189
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10190
|
-
});
|
|
10191
|
-
drain();
|
|
10192
|
-
},
|
|
10193
|
-
(error) => {
|
|
10194
|
-
if (disposed) return;
|
|
10195
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10196
|
-
timeoutTimers.delete(taskName);
|
|
10197
|
-
journal.append({
|
|
10198
|
-
type: "task-failed",
|
|
10199
|
-
taskName,
|
|
10200
|
-
error: error.message ?? String(error),
|
|
10201
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10202
|
-
});
|
|
10203
|
-
drain();
|
|
10204
|
-
}
|
|
10205
|
-
);
|
|
10206
|
-
} catch (syncError) {
|
|
10207
|
-
const err = syncError instanceof Error ? syncError : new Error(String(syncError));
|
|
10208
|
-
dispatched.set(taskName, {
|
|
10209
|
-
status: "dispatch-failed",
|
|
10210
|
-
dispatchedAt: Date.now(),
|
|
10211
|
-
dispatchAttempts: attempt,
|
|
10212
|
-
lastError: err.message
|
|
10213
|
-
});
|
|
10214
|
-
onDispatchFailed?.(taskName, err, attempt);
|
|
10215
|
-
dispatched.set(taskName, {
|
|
10216
|
-
...dispatched.get(taskName),
|
|
10217
|
-
status: "retry-queued"
|
|
10188
|
+
const callbackToken = encodeCallbackToken(taskName);
|
|
10189
|
+
runPipeline(taskName, callbackToken).catch((error) => {
|
|
10190
|
+
if (disposed) return;
|
|
10191
|
+
journal.append({
|
|
10192
|
+
type: "task-failed",
|
|
10193
|
+
taskName,
|
|
10194
|
+
error: error.message ?? String(error),
|
|
10195
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10218
10196
|
});
|
|
10219
|
-
|
|
10220
|
-
}
|
|
10221
|
-
}
|
|
10222
|
-
function sweepTimeouts() {
|
|
10223
|
-
const now = Date.now();
|
|
10224
|
-
for (const [taskName, entry] of dispatched) {
|
|
10225
|
-
if (entry.status !== "initiated") continue;
|
|
10226
|
-
if (defaultTimeoutMs <= 0) continue;
|
|
10227
|
-
if (now - entry.dispatchedAt >= defaultTimeoutMs) {
|
|
10228
|
-
dispatched.set(taskName, {
|
|
10229
|
-
...entry,
|
|
10230
|
-
status: entry.dispatchAttempts >= maxDispatchRetries ? "abandoned" : "retry-queued"
|
|
10231
|
-
});
|
|
10232
|
-
if (entry.dispatchAttempts >= maxDispatchRetries) {
|
|
10233
|
-
onAbandoned?.(taskName);
|
|
10234
|
-
journal.append({
|
|
10235
|
-
type: "task-failed",
|
|
10236
|
-
taskName,
|
|
10237
|
-
error: `dispatch-timeout: no callback after ${defaultTimeoutMs}ms (${entry.dispatchAttempts} attempts)`,
|
|
10238
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10239
|
-
});
|
|
10240
|
-
}
|
|
10241
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10242
|
-
timeoutTimers.delete(taskName);
|
|
10243
|
-
}
|
|
10244
|
-
}
|
|
10197
|
+
drain();
|
|
10198
|
+
});
|
|
10245
10199
|
}
|
|
10246
10200
|
return {
|
|
10247
10201
|
push(event) {
|
|
10248
10202
|
if (disposed) return;
|
|
10249
|
-
|
|
10203
|
+
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
10204
|
+
event = { ...event, dataHash: computeDataHash(event.data) };
|
|
10205
|
+
}
|
|
10206
|
+
journal.append(event);
|
|
10250
10207
|
drain();
|
|
10251
10208
|
},
|
|
10252
10209
|
pushAll(events) {
|
|
10253
10210
|
if (disposed) return;
|
|
10254
|
-
|
|
10255
|
-
|
|
10211
|
+
for (const event of events) {
|
|
10212
|
+
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
10213
|
+
journal.append({ ...event, dataHash: computeDataHash(event.data) });
|
|
10214
|
+
} else {
|
|
10215
|
+
journal.append(event);
|
|
10216
|
+
}
|
|
10217
|
+
}
|
|
10218
|
+
drain();
|
|
10219
|
+
},
|
|
10220
|
+
resolveCallback(callbackToken, data, errors) {
|
|
10221
|
+
if (disposed) return;
|
|
10222
|
+
const decoded = decodeCallbackToken(callbackToken);
|
|
10223
|
+
if (!decoded) return;
|
|
10224
|
+
const { taskName } = decoded;
|
|
10225
|
+
if (!live.config.tasks[taskName]) return;
|
|
10226
|
+
if (errors && errors.length > 0) {
|
|
10227
|
+
journal.append({
|
|
10228
|
+
type: "task-failed",
|
|
10229
|
+
taskName,
|
|
10230
|
+
error: errors.join("; "),
|
|
10231
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10232
|
+
});
|
|
10233
|
+
} else {
|
|
10234
|
+
const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
|
|
10235
|
+
journal.append({
|
|
10236
|
+
type: "task-completed",
|
|
10237
|
+
taskName,
|
|
10238
|
+
data,
|
|
10239
|
+
dataHash,
|
|
10240
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10241
|
+
});
|
|
10242
|
+
}
|
|
10256
10243
|
drain();
|
|
10257
10244
|
},
|
|
10258
|
-
addNode(name, taskConfig
|
|
10245
|
+
addNode(name, taskConfig) {
|
|
10259
10246
|
if (disposed) return;
|
|
10260
10247
|
live = addNode(live, name, taskConfig);
|
|
10261
|
-
handlers.set(name, handler);
|
|
10262
10248
|
drain();
|
|
10263
10249
|
},
|
|
10264
10250
|
removeNode(name) {
|
|
10265
10251
|
if (disposed) return;
|
|
10266
10252
|
live = removeNode(live, name);
|
|
10253
|
+
},
|
|
10254
|
+
addRequires(nodeName, tokens) {
|
|
10255
|
+
if (disposed) return;
|
|
10256
|
+
live = addRequires(live, nodeName, tokens);
|
|
10257
|
+
drain();
|
|
10258
|
+
},
|
|
10259
|
+
removeRequires(nodeName, tokens) {
|
|
10260
|
+
if (disposed) return;
|
|
10261
|
+
live = removeRequires(live, nodeName, tokens);
|
|
10262
|
+
drain();
|
|
10263
|
+
},
|
|
10264
|
+
addProvides(nodeName, tokens) {
|
|
10265
|
+
if (disposed) return;
|
|
10266
|
+
live = addProvides(live, nodeName, tokens);
|
|
10267
|
+
drain();
|
|
10268
|
+
},
|
|
10269
|
+
removeProvides(nodeName, tokens) {
|
|
10270
|
+
if (disposed) return;
|
|
10271
|
+
live = removeProvides(live, nodeName, tokens);
|
|
10272
|
+
},
|
|
10273
|
+
registerHandler(name, fn) {
|
|
10274
|
+
handlers.set(name, fn);
|
|
10275
|
+
},
|
|
10276
|
+
unregisterHandler(name) {
|
|
10267
10277
|
handlers.delete(name);
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10278
|
+
},
|
|
10279
|
+
retrigger(taskName) {
|
|
10280
|
+
if (disposed) return;
|
|
10281
|
+
if (!live.config.tasks[taskName]) return;
|
|
10282
|
+
journal.append({
|
|
10283
|
+
type: "task-restart",
|
|
10284
|
+
taskName,
|
|
10285
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10286
|
+
});
|
|
10287
|
+
drain();
|
|
10288
|
+
},
|
|
10289
|
+
retriggerAll(taskNames) {
|
|
10290
|
+
if (disposed) return;
|
|
10291
|
+
for (const name of taskNames) {
|
|
10292
|
+
if (!live.config.tasks[name]) continue;
|
|
10293
|
+
journal.append({
|
|
10294
|
+
type: "task-restart",
|
|
10295
|
+
taskName: name,
|
|
10296
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10297
|
+
});
|
|
10298
|
+
}
|
|
10299
|
+
drain();
|
|
10271
10300
|
},
|
|
10272
10301
|
getState() {
|
|
10273
10302
|
return live;
|
|
@@ -10275,267 +10304,360 @@ function createReactiveGraph(config, options, executionId) {
|
|
|
10275
10304
|
getSchedule() {
|
|
10276
10305
|
return schedule(live);
|
|
10277
10306
|
},
|
|
10278
|
-
getDispatchState() {
|
|
10279
|
-
return dispatched;
|
|
10280
|
-
},
|
|
10281
10307
|
dispose() {
|
|
10282
10308
|
disposed = true;
|
|
10283
|
-
for (const timer of timeoutTimers.values()) {
|
|
10284
|
-
clearTimeout(timer);
|
|
10285
|
-
}
|
|
10286
|
-
timeoutTimers.clear();
|
|
10287
10309
|
}
|
|
10288
10310
|
};
|
|
10289
10311
|
}
|
|
10290
10312
|
|
|
10291
|
-
// src/
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
const
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10313
|
+
// src/continuous-event-graph/validate.ts
|
|
10314
|
+
function validateLiveGraph(live) {
|
|
10315
|
+
const issues = [];
|
|
10316
|
+
const { config, state } = live;
|
|
10317
|
+
const tasks = getAllTasks(config);
|
|
10318
|
+
const taskNames = Object.keys(tasks);
|
|
10319
|
+
for (const name of taskNames) {
|
|
10320
|
+
if (!state.tasks[name]) {
|
|
10321
|
+
issues.push({
|
|
10322
|
+
severity: "error",
|
|
10323
|
+
code: "MISSING_STATE",
|
|
10324
|
+
message: `Task "${name}" exists in config but has no state entry`,
|
|
10325
|
+
tasks: [name]
|
|
10326
|
+
});
|
|
10327
|
+
}
|
|
10303
10328
|
}
|
|
10304
|
-
const
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
lines.push(`Completed tasks: ${completedTasks.join(", ")}`);
|
|
10314
|
-
lines.push("");
|
|
10329
|
+
for (const name of Object.keys(state.tasks)) {
|
|
10330
|
+
if (!tasks[name]) {
|
|
10331
|
+
issues.push({
|
|
10332
|
+
severity: "warning",
|
|
10333
|
+
code: "ORPHAN_STATE",
|
|
10334
|
+
message: `State entry "${name}" has no corresponding task config`,
|
|
10335
|
+
tasks: [name]
|
|
10336
|
+
});
|
|
10337
|
+
}
|
|
10315
10338
|
}
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10339
|
+
for (const name of taskNames) {
|
|
10340
|
+
const ts = state.tasks[name];
|
|
10341
|
+
if (!ts) continue;
|
|
10342
|
+
if (ts.status === TASK_STATUS.RUNNING && !ts.startedAt) {
|
|
10343
|
+
issues.push({
|
|
10344
|
+
severity: "warning",
|
|
10345
|
+
code: "RUNNING_WITHOUT_START",
|
|
10346
|
+
message: `Task "${name}" is running but has no startedAt timestamp`,
|
|
10347
|
+
tasks: [name]
|
|
10348
|
+
});
|
|
10324
10349
|
}
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
if (
|
|
10350
|
+
if (ts.status === TASK_STATUS.COMPLETED && !ts.completedAt) {
|
|
10351
|
+
issues.push({
|
|
10352
|
+
severity: "warning",
|
|
10353
|
+
code: "COMPLETED_WITHOUT_TIMESTAMP",
|
|
10354
|
+
message: `Task "${name}" is completed but has no completedAt timestamp`,
|
|
10355
|
+
tasks: [name]
|
|
10356
|
+
});
|
|
10357
|
+
}
|
|
10358
|
+
if (ts.status === TASK_STATUS.FAILED) {
|
|
10359
|
+
if (!ts.failedAt) {
|
|
10360
|
+
issues.push({
|
|
10361
|
+
severity: "warning",
|
|
10362
|
+
code: "FAILED_WITHOUT_INFO",
|
|
10363
|
+
message: `Task "${name}" is failed but has no failedAt timestamp`,
|
|
10364
|
+
tasks: [name]
|
|
10365
|
+
});
|
|
10366
|
+
}
|
|
10367
|
+
if (!ts.error) {
|
|
10368
|
+
issues.push({
|
|
10369
|
+
severity: "info",
|
|
10370
|
+
code: "FAILED_WITHOUT_INFO",
|
|
10371
|
+
message: `Task "${name}" is failed but has no error message`,
|
|
10372
|
+
tasks: [name]
|
|
10373
|
+
});
|
|
10374
|
+
}
|
|
10335
10375
|
}
|
|
10336
|
-
lines.push("");
|
|
10337
10376
|
}
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10377
|
+
const expectedOutputs = /* @__PURE__ */ new Set();
|
|
10378
|
+
for (const name of taskNames) {
|
|
10379
|
+
const ts = state.tasks[name];
|
|
10380
|
+
if (ts?.status === TASK_STATUS.COMPLETED) {
|
|
10381
|
+
for (const token of getProvides(tasks[name])) {
|
|
10382
|
+
expectedOutputs.add(token);
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10343
10385
|
}
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
lines.push("");
|
|
10357
|
-
lines.push("Rules:");
|
|
10358
|
-
lines.push('- Only include tasks from the "Tasks to Analyze" section');
|
|
10359
|
-
lines.push("- confidence 0.0 = no evidence of completion, 1.0 = certain it is complete");
|
|
10360
|
-
lines.push("- If you have no evidence for a task, omit it from the array");
|
|
10361
|
-
lines.push("- Be conservative \u2014 require clear evidence before high confidence");
|
|
10362
|
-
lines.push("- Respond ONLY with the JSON array, no additional text");
|
|
10363
|
-
return lines.join("\n");
|
|
10364
|
-
}
|
|
10365
|
-
async function inferCompletions(live, adapter, options = {}) {
|
|
10366
|
-
options.threshold ?? DEFAULT_THRESHOLD;
|
|
10367
|
-
const analyzedNodes = getAnalyzableCandidates(live, options.scope);
|
|
10368
|
-
if (analyzedNodes.length === 0) {
|
|
10369
|
-
return { suggestions: [], promptUsed: "", rawResponse: "", analyzedNodes: [] };
|
|
10386
|
+
const actualOutputs = new Set(state.availableOutputs);
|
|
10387
|
+
const allProducible = /* @__PURE__ */ new Set();
|
|
10388
|
+
for (const taskConfig of Object.values(tasks)) {
|
|
10389
|
+
for (const t of getProvides(taskConfig)) allProducible.add(t);
|
|
10390
|
+
if (taskConfig.on) {
|
|
10391
|
+
for (const tokens of Object.values(taskConfig.on)) {
|
|
10392
|
+
for (const t of tokens) allProducible.add(t);
|
|
10393
|
+
}
|
|
10394
|
+
}
|
|
10395
|
+
if (taskConfig.on_failure) {
|
|
10396
|
+
for (const t of taskConfig.on_failure) allProducible.add(t);
|
|
10397
|
+
}
|
|
10370
10398
|
}
|
|
10371
|
-
const
|
|
10372
|
-
|
|
10373
|
-
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
}
|
|
10381
|
-
function applyInferences(live, result, threshold = DEFAULT_THRESHOLD) {
|
|
10382
|
-
let current = live;
|
|
10383
|
-
for (const suggestion of result.suggestions) {
|
|
10384
|
-
if (suggestion.confidence < threshold) continue;
|
|
10385
|
-
const taskState = current.state.tasks[suggestion.taskName];
|
|
10386
|
-
if (!taskState) continue;
|
|
10387
|
-
if (taskState.status === "completed" || taskState.status === "running") continue;
|
|
10388
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10389
|
-
current = applyEvent(current, {
|
|
10390
|
-
type: "task-started",
|
|
10391
|
-
taskName: suggestion.taskName,
|
|
10392
|
-
timestamp: now
|
|
10393
|
-
});
|
|
10394
|
-
current = applyEvent(current, {
|
|
10395
|
-
type: "task-completed",
|
|
10396
|
-
taskName: suggestion.taskName,
|
|
10397
|
-
timestamp: now,
|
|
10398
|
-
result: "llm-inferred"
|
|
10399
|
-
});
|
|
10399
|
+
for (const token of actualOutputs) {
|
|
10400
|
+
if (!expectedOutputs.has(token) && !allProducible.has(token)) {
|
|
10401
|
+
issues.push({
|
|
10402
|
+
severity: "info",
|
|
10403
|
+
code: "INJECTED_TOKEN",
|
|
10404
|
+
message: `Token "${token}" is available but no task in the graph can produce it (likely injected)`,
|
|
10405
|
+
tokens: [token]
|
|
10406
|
+
});
|
|
10407
|
+
}
|
|
10400
10408
|
}
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
return {
|
|
10410
|
-
live: updated,
|
|
10411
|
-
inference,
|
|
10412
|
-
applied,
|
|
10413
|
-
skipped
|
|
10414
|
-
};
|
|
10415
|
-
}
|
|
10416
|
-
function getAnalyzableCandidates(live, scope) {
|
|
10417
|
-
const graphTasks = getAllTasks(live.config);
|
|
10418
|
-
const { state } = live;
|
|
10419
|
-
const candidates = [];
|
|
10420
|
-
for (const [name, config] of Object.entries(graphTasks)) {
|
|
10421
|
-
const taskState = state.tasks[name];
|
|
10422
|
-
if (taskState?.status === "completed" || taskState?.status === "running") continue;
|
|
10423
|
-
if (scope) {
|
|
10424
|
-
if (scope.includes(name)) candidates.push(name);
|
|
10425
|
-
} else {
|
|
10426
|
-
if (config.inference?.autoDetectable) candidates.push(name);
|
|
10409
|
+
for (const token of expectedOutputs) {
|
|
10410
|
+
if (!actualOutputs.has(token)) {
|
|
10411
|
+
issues.push({
|
|
10412
|
+
severity: "warning",
|
|
10413
|
+
code: "MISSING_OUTPUT",
|
|
10414
|
+
message: `Token "${token}" should be available (its producer completed) but is not in availableOutputs`,
|
|
10415
|
+
tokens: [token]
|
|
10416
|
+
});
|
|
10427
10417
|
}
|
|
10428
10418
|
}
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
confidence,
|
|
10448
|
-
reasoning: typeof item.reasoning === "string" ? item.reasoning : "",
|
|
10449
|
-
detectionMethod: "llm-inferred"
|
|
10419
|
+
for (const name of taskNames) {
|
|
10420
|
+
const ts = state.tasks[name];
|
|
10421
|
+
if (!ts) continue;
|
|
10422
|
+
if (ts.executionCount < 0) {
|
|
10423
|
+
issues.push({
|
|
10424
|
+
severity: "error",
|
|
10425
|
+
code: "INVALID_EXECUTION_COUNT",
|
|
10426
|
+
message: `Task "${name}" has negative execution count: ${ts.executionCount}`,
|
|
10427
|
+
tasks: [name]
|
|
10428
|
+
});
|
|
10429
|
+
}
|
|
10430
|
+
const maxExec = tasks[name].maxExecutions;
|
|
10431
|
+
if (maxExec !== void 0 && ts.executionCount > maxExec) {
|
|
10432
|
+
issues.push({
|
|
10433
|
+
severity: "error",
|
|
10434
|
+
code: "EXCEEDED_MAX_EXECUTIONS",
|
|
10435
|
+
message: `Task "${name}" executed ${ts.executionCount} times, exceeding maxExecutions of ${maxExec}`,
|
|
10436
|
+
tasks: [name]
|
|
10450
10437
|
});
|
|
10451
10438
|
}
|
|
10452
|
-
return suggestions;
|
|
10453
|
-
} catch {
|
|
10454
|
-
return [];
|
|
10455
10439
|
}
|
|
10440
|
+
return buildResult2(issues);
|
|
10456
10441
|
}
|
|
10457
|
-
function
|
|
10458
|
-
|
|
10459
|
-
const
|
|
10460
|
-
const
|
|
10461
|
-
|
|
10462
|
-
const
|
|
10463
|
-
const
|
|
10464
|
-
|
|
10465
|
-
|
|
10442
|
+
function validateReactiveGraph(input) {
|
|
10443
|
+
const { graph, handlers } = input;
|
|
10444
|
+
const live = graph.getState();
|
|
10445
|
+
const issues = [];
|
|
10446
|
+
const tasks = getAllTasks(live.config);
|
|
10447
|
+
const taskNames = Object.keys(tasks);
|
|
10448
|
+
const handlerNames = new Set(Object.keys(handlers));
|
|
10449
|
+
const referencedHandlers = /* @__PURE__ */ new Set();
|
|
10450
|
+
for (const name of taskNames) {
|
|
10451
|
+
const taskHandlers = tasks[name].taskHandlers;
|
|
10452
|
+
if (taskHandlers) {
|
|
10453
|
+
for (const h of taskHandlers) {
|
|
10454
|
+
referencedHandlers.add(h);
|
|
10455
|
+
}
|
|
10456
|
+
}
|
|
10466
10457
|
}
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
reject(new Error(
|
|
10489
|
-
`CLI adapter failed: ${opts.command} exited with ${error.code ?? "error"}` + (stderr ? `
|
|
10490
|
-
stderr: ${stderr.slice(0, 500)}` : "") + `
|
|
10491
|
-
${error.message}`
|
|
10492
|
-
));
|
|
10493
|
-
} else {
|
|
10494
|
-
resolve2(stdout);
|
|
10495
|
-
}
|
|
10496
|
-
}
|
|
10497
|
-
);
|
|
10498
|
-
if (opts.stdin && child.stdin) {
|
|
10499
|
-
child.stdin.write(prompt);
|
|
10500
|
-
child.stdin.end();
|
|
10501
|
-
}
|
|
10458
|
+
for (const name of taskNames) {
|
|
10459
|
+
const taskHandlers = tasks[name].taskHandlers;
|
|
10460
|
+
if (!taskHandlers) continue;
|
|
10461
|
+
for (const h of taskHandlers) {
|
|
10462
|
+
if (!handlers[h]) {
|
|
10463
|
+
issues.push({
|
|
10464
|
+
severity: "error",
|
|
10465
|
+
code: "MISSING_HANDLER",
|
|
10466
|
+
message: `Task "${name}" references handler "${h}" but it is not in the registry`,
|
|
10467
|
+
tasks: [name]
|
|
10468
|
+
});
|
|
10469
|
+
}
|
|
10470
|
+
}
|
|
10471
|
+
}
|
|
10472
|
+
for (const name of handlerNames) {
|
|
10473
|
+
if (!referencedHandlers.has(name)) {
|
|
10474
|
+
issues.push({
|
|
10475
|
+
severity: "warning",
|
|
10476
|
+
code: "ORPHAN_HANDLER",
|
|
10477
|
+
message: `Handler "${name}" is registered but not referenced by any task's taskHandlers`,
|
|
10478
|
+
tasks: [name]
|
|
10502
10479
|
});
|
|
10503
10480
|
}
|
|
10504
|
-
}
|
|
10481
|
+
}
|
|
10482
|
+
const liveResult = validateLiveGraph(live);
|
|
10483
|
+
issues.push(...liveResult.issues);
|
|
10484
|
+
return buildResult2(issues);
|
|
10505
10485
|
}
|
|
10506
|
-
function
|
|
10507
|
-
const
|
|
10486
|
+
function buildResult2(issues) {
|
|
10487
|
+
const errors = issues.filter((i) => i.severity === "error");
|
|
10488
|
+
const warnings = issues.filter((i) => i.severity === "warning");
|
|
10508
10489
|
return {
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10490
|
+
valid: errors.length === 0,
|
|
10491
|
+
issues,
|
|
10492
|
+
errors,
|
|
10493
|
+
warnings
|
|
10494
|
+
};
|
|
10495
|
+
}
|
|
10496
|
+
|
|
10497
|
+
// src/continuous-event-graph/mutate.ts
|
|
10498
|
+
function mutateGraph(live, mutations) {
|
|
10499
|
+
let current = live;
|
|
10500
|
+
for (const mutation of mutations) {
|
|
10501
|
+
current = applySingleMutation(current, mutation);
|
|
10502
|
+
}
|
|
10503
|
+
return current;
|
|
10504
|
+
}
|
|
10505
|
+
function applySingleMutation(live, mutation) {
|
|
10506
|
+
switch (mutation.type) {
|
|
10507
|
+
case "add-node":
|
|
10508
|
+
return addNode(live, mutation.name, mutation.config);
|
|
10509
|
+
case "remove-node":
|
|
10510
|
+
return removeNode(live, mutation.name);
|
|
10511
|
+
case "add-requires":
|
|
10512
|
+
return addRequires(live, mutation.taskName, mutation.tokens);
|
|
10513
|
+
case "remove-requires":
|
|
10514
|
+
return removeRequires(live, mutation.taskName, mutation.tokens);
|
|
10515
|
+
case "add-provides":
|
|
10516
|
+
return addProvides(live, mutation.taskName, mutation.tokens);
|
|
10517
|
+
case "remove-provides":
|
|
10518
|
+
return removeProvides(live, mutation.taskName, mutation.tokens);
|
|
10519
|
+
case "inject-tokens":
|
|
10520
|
+
return injectTokens(live, mutation.tokens);
|
|
10521
|
+
case "drain-tokens":
|
|
10522
|
+
return drainTokens(live, mutation.tokens);
|
|
10523
|
+
case "reset-node":
|
|
10524
|
+
return resetNode(live, mutation.name);
|
|
10525
|
+
case "disable-node":
|
|
10526
|
+
return disableNode(live, mutation.name);
|
|
10527
|
+
case "enable-node":
|
|
10528
|
+
return enableNode(live, mutation.name);
|
|
10529
|
+
case "apply-events":
|
|
10530
|
+
return applyEvents(live, mutation.events);
|
|
10531
|
+
default:
|
|
10532
|
+
throw new Error(`Unknown mutation type: ${mutation.type}`);
|
|
10533
|
+
}
|
|
10534
|
+
}
|
|
10535
|
+
function createCallbackHandler(fn, getResolve) {
|
|
10536
|
+
return async (input) => {
|
|
10537
|
+
const { callbackToken } = input;
|
|
10538
|
+
Promise.resolve(fn(input)).then((data) => getResolve()(callbackToken, data)).catch((err) => getResolve()(callbackToken, {}, [err instanceof Error ? err.message : String(err)]));
|
|
10539
|
+
return "task-initiated";
|
|
10540
|
+
};
|
|
10541
|
+
}
|
|
10542
|
+
function createFireAndForgetHandler(fn, getResolve) {
|
|
10543
|
+
return async (input) => {
|
|
10544
|
+
const { callbackToken } = input;
|
|
10545
|
+
Promise.resolve(fn(input)).then(() => getResolve()(callbackToken, {})).catch(() => getResolve()(callbackToken, {}));
|
|
10546
|
+
return "task-initiated";
|
|
10547
|
+
};
|
|
10548
|
+
}
|
|
10549
|
+
function createShellHandler(options) {
|
|
10550
|
+
const {
|
|
10551
|
+
command: commandTemplate,
|
|
10552
|
+
cwd,
|
|
10553
|
+
env,
|
|
10554
|
+
timeoutMs = 3e4,
|
|
10555
|
+
exitCodeMap,
|
|
10556
|
+
captureOutput = false,
|
|
10557
|
+
getResolve
|
|
10558
|
+
} = options;
|
|
10559
|
+
return async (input) => {
|
|
10560
|
+
const { callbackToken, nodeId } = input;
|
|
10561
|
+
const command = commandTemplate.replace(/\$\{taskName\}/g, nodeId);
|
|
10562
|
+
exec(
|
|
10563
|
+
command,
|
|
10564
|
+
{
|
|
10565
|
+
cwd,
|
|
10566
|
+
env: env ? { ...process.env, ...env } : void 0,
|
|
10567
|
+
timeout: timeoutMs,
|
|
10568
|
+
maxBuffer: 10 * 1024 * 1024
|
|
10569
|
+
// 10MB
|
|
10570
|
+
},
|
|
10571
|
+
(error, stdout, stderr) => {
|
|
10572
|
+
const exitCode = error?.code ?? (error ? 1 : 0);
|
|
10573
|
+
if (exitCode !== 0 && !exitCodeMap?.[exitCode]) {
|
|
10574
|
+
getResolve()(callbackToken, {}, [`Command exited with code ${exitCode}: ${stderr || error?.message}`]);
|
|
10575
|
+
return;
|
|
10526
10576
|
}
|
|
10527
|
-
const
|
|
10528
|
-
if (
|
|
10529
|
-
|
|
10577
|
+
const data = {};
|
|
10578
|
+
if (captureOutput) {
|
|
10579
|
+
data.stdout = stdout;
|
|
10580
|
+
data.stderr = stderr;
|
|
10581
|
+
data.exitCode = exitCode;
|
|
10530
10582
|
}
|
|
10531
|
-
|
|
10532
|
-
if (typeof json.text === "string") return json.text;
|
|
10533
|
-
if (typeof json.content === "string") return json.content;
|
|
10534
|
-
return JSON.stringify(json);
|
|
10535
|
-
} finally {
|
|
10536
|
-
clearTimeout(timer);
|
|
10583
|
+
getResolve()(callbackToken, data);
|
|
10537
10584
|
}
|
|
10538
|
-
|
|
10585
|
+
);
|
|
10586
|
+
return "task-initiated";
|
|
10587
|
+
};
|
|
10588
|
+
}
|
|
10589
|
+
function detectRuntime(scriptPath) {
|
|
10590
|
+
if (scriptPath.endsWith(".js") || scriptPath.endsWith(".mjs") || scriptPath.endsWith(".ts")) return "node";
|
|
10591
|
+
if (scriptPath.endsWith(".py")) return "python3";
|
|
10592
|
+
if (scriptPath.endsWith(".sh")) return "bash";
|
|
10593
|
+
return "bash";
|
|
10594
|
+
}
|
|
10595
|
+
function createScriptHandler(options) {
|
|
10596
|
+
const {
|
|
10597
|
+
scriptPath,
|
|
10598
|
+
runtime,
|
|
10599
|
+
args = [],
|
|
10600
|
+
cwd,
|
|
10601
|
+
timeoutMs = 6e4,
|
|
10602
|
+
captureOutput = false,
|
|
10603
|
+
getResolve
|
|
10604
|
+
} = options;
|
|
10605
|
+
const resolvedRuntime = runtime ?? detectRuntime(scriptPath);
|
|
10606
|
+
const shellArgs = [ctx_taskName_placeholder, ...args].join(" ");
|
|
10607
|
+
const command = `${resolvedRuntime} ${scriptPath} ${shellArgs}`;
|
|
10608
|
+
return createShellHandler({
|
|
10609
|
+
command: command.replace(ctx_taskName_placeholder, "${taskName}"),
|
|
10610
|
+
cwd,
|
|
10611
|
+
timeoutMs,
|
|
10612
|
+
captureOutput,
|
|
10613
|
+
getResolve
|
|
10614
|
+
});
|
|
10615
|
+
}
|
|
10616
|
+
var ctx_taskName_placeholder = "__TASK_NAME__";
|
|
10617
|
+
function createWebhookHandler(options) {
|
|
10618
|
+
const {
|
|
10619
|
+
url: urlTemplate,
|
|
10620
|
+
method = "POST",
|
|
10621
|
+
headers = {},
|
|
10622
|
+
timeoutMs = 3e4,
|
|
10623
|
+
failOnNon2xx = true,
|
|
10624
|
+
getResolve
|
|
10625
|
+
} = options;
|
|
10626
|
+
return async (input) => {
|
|
10627
|
+
const { callbackToken, nodeId, config } = input;
|
|
10628
|
+
const url = urlTemplate.replace(/\$\{taskName\}/g, nodeId);
|
|
10629
|
+
const body = JSON.stringify({
|
|
10630
|
+
taskName: nodeId,
|
|
10631
|
+
callbackToken,
|
|
10632
|
+
config
|
|
10633
|
+
});
|
|
10634
|
+
const controller = new AbortController();
|
|
10635
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
10636
|
+
fetch(url, {
|
|
10637
|
+
method,
|
|
10638
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
10639
|
+
body,
|
|
10640
|
+
signal: controller.signal
|
|
10641
|
+
}).then(async (response) => {
|
|
10642
|
+
clearTimeout(timer);
|
|
10643
|
+
if (failOnNon2xx && !response.ok) {
|
|
10644
|
+
const text = await response.text().catch(() => "");
|
|
10645
|
+
getResolve()(callbackToken, {}, [`HTTP ${response.status}: ${text}`]);
|
|
10646
|
+
return;
|
|
10647
|
+
}
|
|
10648
|
+
const data = await response.json().catch(() => ({}));
|
|
10649
|
+
getResolve()(callbackToken, data);
|
|
10650
|
+
}).catch((err) => {
|
|
10651
|
+
clearTimeout(timer);
|
|
10652
|
+
getResolve()(callbackToken, {}, [err instanceof Error ? err.message : String(err)]);
|
|
10653
|
+
});
|
|
10654
|
+
return "task-initiated";
|
|
10655
|
+
};
|
|
10656
|
+
}
|
|
10657
|
+
function createNoopHandler(getResolve, staticData) {
|
|
10658
|
+
return async (input) => {
|
|
10659
|
+
getResolve()(input.callbackToken, staticData ?? {});
|
|
10660
|
+
return "task-initiated";
|
|
10539
10661
|
};
|
|
10540
10662
|
}
|
|
10541
10663
|
|
|
@@ -11319,6 +11441,405 @@ var CardCompute = {
|
|
|
11319
11441
|
}
|
|
11320
11442
|
};
|
|
11321
11443
|
|
|
11322
|
-
|
|
11444
|
+
// src/continuous-event-graph/live-cards-bridge.ts
|
|
11445
|
+
function liveCardsToReactiveGraph(input, options = {}) {
|
|
11446
|
+
let cards;
|
|
11447
|
+
let boardSettings = {};
|
|
11448
|
+
let boardId;
|
|
11449
|
+
if (!Array.isArray(input) && "nodes" in input) {
|
|
11450
|
+
const board = input;
|
|
11451
|
+
cards = board.nodes;
|
|
11452
|
+
boardId = board.id;
|
|
11453
|
+
boardSettings = board.settings ?? {};
|
|
11454
|
+
} else {
|
|
11455
|
+
cards = input;
|
|
11456
|
+
}
|
|
11457
|
+
const {
|
|
11458
|
+
sourceHandlers = {},
|
|
11459
|
+
defaultSourceHandler,
|
|
11460
|
+
cardHandlers = {},
|
|
11461
|
+
reactiveOptions = {},
|
|
11462
|
+
graphSettings = {},
|
|
11463
|
+
executionId
|
|
11464
|
+
} = options;
|
|
11465
|
+
const cardMap = /* @__PURE__ */ new Map();
|
|
11466
|
+
for (const card of cards) {
|
|
11467
|
+
if (cardMap.has(card.id)) {
|
|
11468
|
+
throw new Error(`Duplicate card ID: "${card.id}"`);
|
|
11469
|
+
}
|
|
11470
|
+
cardMap.set(card.id, card);
|
|
11471
|
+
}
|
|
11472
|
+
const sharedState = options.sharedState ?? /* @__PURE__ */ new Map();
|
|
11473
|
+
const tasks = {};
|
|
11474
|
+
for (const card of cards) {
|
|
11475
|
+
const requires = card.data?.requires ?? [];
|
|
11476
|
+
for (const req of requires) {
|
|
11477
|
+
if (!cardMap.has(req)) {
|
|
11478
|
+
throw new Error(`Card "${card.id}" requires "${req}" but no card with that ID exists`);
|
|
11479
|
+
}
|
|
11480
|
+
}
|
|
11481
|
+
tasks[card.id] = {
|
|
11482
|
+
requires: requires.length > 0 ? requires : void 0,
|
|
11483
|
+
provides: [card.id],
|
|
11484
|
+
taskHandlers: [card.id],
|
|
11485
|
+
// each card has a named handler matching its ID
|
|
11486
|
+
description: card.meta?.title ?? `${card.type}: ${card.id}`
|
|
11487
|
+
};
|
|
11488
|
+
}
|
|
11489
|
+
const config = {
|
|
11490
|
+
id: boardId ?? `live-cards-${Date.now()}`,
|
|
11491
|
+
settings: {
|
|
11492
|
+
completion: "manual",
|
|
11493
|
+
execution_mode: "eligibility-mode",
|
|
11494
|
+
...boardSettings,
|
|
11495
|
+
...graphSettings
|
|
11496
|
+
},
|
|
11497
|
+
tasks
|
|
11498
|
+
};
|
|
11499
|
+
const handlers = {};
|
|
11500
|
+
let graphRef = null;
|
|
11501
|
+
const getResolve = () => (token, data, errors) => {
|
|
11502
|
+
graphRef.resolveCallback(token, data, errors);
|
|
11503
|
+
};
|
|
11504
|
+
for (const card of cards) {
|
|
11505
|
+
if (card.type === "source") {
|
|
11506
|
+
handlers[card.id] = buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve);
|
|
11507
|
+
} else {
|
|
11508
|
+
handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve);
|
|
11509
|
+
}
|
|
11510
|
+
}
|
|
11511
|
+
const graph = createReactiveGraph(
|
|
11512
|
+
config,
|
|
11513
|
+
{
|
|
11514
|
+
...reactiveOptions,
|
|
11515
|
+
handlers
|
|
11516
|
+
},
|
|
11517
|
+
executionId
|
|
11518
|
+
);
|
|
11519
|
+
graphRef = graph;
|
|
11520
|
+
return { graph, config, handlers, cards: cardMap, sharedState };
|
|
11521
|
+
}
|
|
11522
|
+
function buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve) {
|
|
11523
|
+
if (sourceHandlers[card.id]) {
|
|
11524
|
+
const userHandler = sourceHandlers[card.id];
|
|
11525
|
+
return async (input) => {
|
|
11526
|
+
return userHandler(input);
|
|
11527
|
+
};
|
|
11528
|
+
}
|
|
11529
|
+
if (defaultSourceHandler) {
|
|
11530
|
+
const factoryHandler = defaultSourceHandler(card);
|
|
11531
|
+
return async (input) => {
|
|
11532
|
+
return factoryHandler(input);
|
|
11533
|
+
};
|
|
11534
|
+
}
|
|
11535
|
+
return async (input) => {
|
|
11536
|
+
const state = { ...card.state };
|
|
11537
|
+
sharedState.set(card.id, state);
|
|
11538
|
+
getResolve()(input.callbackToken, state);
|
|
11539
|
+
return "task-initiated";
|
|
11540
|
+
};
|
|
11541
|
+
}
|
|
11542
|
+
function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve) {
|
|
11543
|
+
if (cardHandlers[card.id]) {
|
|
11544
|
+
const userHandler = cardHandlers[card.id];
|
|
11545
|
+
return async (input) => {
|
|
11546
|
+
return userHandler(input);
|
|
11547
|
+
};
|
|
11548
|
+
}
|
|
11549
|
+
return async (input) => {
|
|
11550
|
+
const computeNode = {
|
|
11551
|
+
id: card.id,
|
|
11552
|
+
state: { ...card.state },
|
|
11553
|
+
compute: card.compute
|
|
11554
|
+
};
|
|
11555
|
+
const requires = card.data?.requires ?? [];
|
|
11556
|
+
for (const upstreamId of requires) {
|
|
11557
|
+
const upstreamState = sharedState.get(upstreamId);
|
|
11558
|
+
if (upstreamState) {
|
|
11559
|
+
computeNode.state[upstreamId] = upstreamState;
|
|
11560
|
+
}
|
|
11561
|
+
const upstreamCard = cardMap.get(upstreamId);
|
|
11562
|
+
if (upstreamCard?.data?.provides && upstreamState) {
|
|
11563
|
+
for (const [key, bindRef] of Object.entries(upstreamCard.data.provides)) {
|
|
11564
|
+
if (typeof bindRef === "string" && bindRef.startsWith("state.")) {
|
|
11565
|
+
const path = bindRef.slice(6);
|
|
11566
|
+
const value = deepGet2(upstreamState, path);
|
|
11567
|
+
if (value !== void 0) {
|
|
11568
|
+
computeNode.state[key] = value;
|
|
11569
|
+
}
|
|
11570
|
+
}
|
|
11571
|
+
}
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
CardCompute.run(computeNode);
|
|
11575
|
+
const resultState = { ...computeNode.state };
|
|
11576
|
+
sharedState.set(card.id, resultState);
|
|
11577
|
+
getResolve()(input.callbackToken, resultState);
|
|
11578
|
+
return "task-initiated";
|
|
11579
|
+
};
|
|
11580
|
+
}
|
|
11581
|
+
function deepGet2(obj, path) {
|
|
11582
|
+
if (!path || !obj) return void 0;
|
|
11583
|
+
const parts = path.split(".");
|
|
11584
|
+
let cur = obj;
|
|
11585
|
+
for (const part of parts) {
|
|
11586
|
+
if (cur == null) return void 0;
|
|
11587
|
+
cur = cur[part];
|
|
11588
|
+
}
|
|
11589
|
+
return cur;
|
|
11590
|
+
}
|
|
11591
|
+
|
|
11592
|
+
// src/inference/core.ts
|
|
11593
|
+
var DEFAULT_THRESHOLD = 0.5;
|
|
11594
|
+
var DEFAULT_SYSTEM_PROMPT = `You are a workflow completion analyzer. Given a graph of tasks with their current states, evidence, and inference hints, determine which tasks appear to be completed based on the available evidence.
|
|
11595
|
+
|
|
11596
|
+
For each task you analyze, provide a JSON response. Be conservative \u2014 only mark tasks as completed when the evidence strongly supports it.`;
|
|
11597
|
+
function buildInferencePrompt(live, options = {}) {
|
|
11598
|
+
const { scope, context, systemPrompt } = options;
|
|
11599
|
+
const graphTasks = getAllTasks(live.config);
|
|
11600
|
+
const { state } = live;
|
|
11601
|
+
const candidates = getAnalyzableCandidates(live, scope);
|
|
11602
|
+
if (candidates.length === 0) {
|
|
11603
|
+
return "";
|
|
11604
|
+
}
|
|
11605
|
+
const lines = [];
|
|
11606
|
+
lines.push(systemPrompt || DEFAULT_SYSTEM_PROMPT);
|
|
11607
|
+
lines.push("");
|
|
11608
|
+
lines.push("## Graph State");
|
|
11609
|
+
lines.push("");
|
|
11610
|
+
lines.push(`Available tokens: ${state.availableOutputs.length > 0 ? state.availableOutputs.join(", ") : "(none)"}`);
|
|
11611
|
+
lines.push("");
|
|
11612
|
+
const completedTasks = Object.entries(state.tasks).filter(([_, ts]) => ts.status === "completed").map(([name]) => name);
|
|
11613
|
+
if (completedTasks.length > 0) {
|
|
11614
|
+
lines.push(`Completed tasks: ${completedTasks.join(", ")}`);
|
|
11615
|
+
lines.push("");
|
|
11616
|
+
}
|
|
11617
|
+
lines.push("## Tasks to Analyze");
|
|
11618
|
+
lines.push("");
|
|
11619
|
+
for (const taskName of candidates) {
|
|
11620
|
+
const taskConfig = graphTasks[taskName];
|
|
11621
|
+
const taskState = state.tasks[taskName];
|
|
11622
|
+
lines.push(`### ${taskName}`);
|
|
11623
|
+
if (taskConfig.description) {
|
|
11624
|
+
lines.push(`Description: ${taskConfig.description}`);
|
|
11625
|
+
}
|
|
11626
|
+
const requires = getRequires(taskConfig);
|
|
11627
|
+
const provides = getProvides(taskConfig);
|
|
11628
|
+
if (requires.length > 0) lines.push(`Requires: ${requires.join(", ")}`);
|
|
11629
|
+
if (provides.length > 0) lines.push(`Provides: ${provides.join(", ")}`);
|
|
11630
|
+
lines.push(`Current status: ${taskState?.status || "not-started"}`);
|
|
11631
|
+
const hints = taskConfig.inference;
|
|
11632
|
+
if (hints) {
|
|
11633
|
+
if (hints.criteria) lines.push(`Completion criteria: ${hints.criteria}`);
|
|
11634
|
+
if (hints.keywords?.length) lines.push(`Keywords: ${hints.keywords.join(", ")}`);
|
|
11635
|
+
if (hints.suggestedChecks?.length) lines.push(`Suggested checks: ${hints.suggestedChecks.join("; ")}`);
|
|
11636
|
+
}
|
|
11637
|
+
lines.push("");
|
|
11638
|
+
}
|
|
11639
|
+
if (context) {
|
|
11640
|
+
lines.push("## Additional Context / Evidence");
|
|
11641
|
+
lines.push("");
|
|
11642
|
+
lines.push(context);
|
|
11643
|
+
lines.push("");
|
|
11644
|
+
}
|
|
11645
|
+
lines.push("## Response Format");
|
|
11646
|
+
lines.push("");
|
|
11647
|
+
lines.push("Respond with a JSON array of objects, one per task you have evidence for:");
|
|
11648
|
+
lines.push("```json");
|
|
11649
|
+
lines.push("[");
|
|
11650
|
+
lines.push(" {");
|
|
11651
|
+
lines.push(' "taskName": "task-name",');
|
|
11652
|
+
lines.push(' "confidence": 0.0 to 1.0,');
|
|
11653
|
+
lines.push(' "reasoning": "explanation of why you believe this task is complete or not"');
|
|
11654
|
+
lines.push(" }");
|
|
11655
|
+
lines.push("]");
|
|
11656
|
+
lines.push("```");
|
|
11657
|
+
lines.push("");
|
|
11658
|
+
lines.push("Rules:");
|
|
11659
|
+
lines.push('- Only include tasks from the "Tasks to Analyze" section');
|
|
11660
|
+
lines.push("- confidence 0.0 = no evidence of completion, 1.0 = certain it is complete");
|
|
11661
|
+
lines.push("- If you have no evidence for a task, omit it from the array");
|
|
11662
|
+
lines.push("- Be conservative \u2014 require clear evidence before high confidence");
|
|
11663
|
+
lines.push("- Respond ONLY with the JSON array, no additional text");
|
|
11664
|
+
return lines.join("\n");
|
|
11665
|
+
}
|
|
11666
|
+
async function inferCompletions(live, adapter, options = {}) {
|
|
11667
|
+
options.threshold ?? DEFAULT_THRESHOLD;
|
|
11668
|
+
const analyzedNodes = getAnalyzableCandidates(live, options.scope);
|
|
11669
|
+
if (analyzedNodes.length === 0) {
|
|
11670
|
+
return { suggestions: [], promptUsed: "", rawResponse: "", analyzedNodes: [] };
|
|
11671
|
+
}
|
|
11672
|
+
const prompt = buildInferencePrompt(live, options);
|
|
11673
|
+
const rawResponse = await adapter.analyze(prompt);
|
|
11674
|
+
const suggestions = parseInferenceResponse(rawResponse, analyzedNodes);
|
|
11675
|
+
return {
|
|
11676
|
+
suggestions,
|
|
11677
|
+
promptUsed: prompt,
|
|
11678
|
+
rawResponse,
|
|
11679
|
+
analyzedNodes
|
|
11680
|
+
};
|
|
11681
|
+
}
|
|
11682
|
+
function applyInferences(live, result, threshold = DEFAULT_THRESHOLD) {
|
|
11683
|
+
let current = live;
|
|
11684
|
+
for (const suggestion of result.suggestions) {
|
|
11685
|
+
if (suggestion.confidence < threshold) continue;
|
|
11686
|
+
const taskState = current.state.tasks[suggestion.taskName];
|
|
11687
|
+
if (!taskState) continue;
|
|
11688
|
+
if (taskState.status === "completed" || taskState.status === "running") continue;
|
|
11689
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11690
|
+
current = applyEvent(current, {
|
|
11691
|
+
type: "task-started",
|
|
11692
|
+
taskName: suggestion.taskName,
|
|
11693
|
+
timestamp: now
|
|
11694
|
+
});
|
|
11695
|
+
current = applyEvent(current, {
|
|
11696
|
+
type: "task-completed",
|
|
11697
|
+
taskName: suggestion.taskName,
|
|
11698
|
+
timestamp: now,
|
|
11699
|
+
result: "llm-inferred"
|
|
11700
|
+
});
|
|
11701
|
+
}
|
|
11702
|
+
return current;
|
|
11703
|
+
}
|
|
11704
|
+
async function inferAndApply(live, adapter, options = {}) {
|
|
11705
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
11706
|
+
const inference = await inferCompletions(live, adapter, options);
|
|
11707
|
+
const updated = applyInferences(live, inference, threshold);
|
|
11708
|
+
const applied = inference.suggestions.filter((s) => s.confidence >= threshold);
|
|
11709
|
+
const skipped = inference.suggestions.filter((s) => s.confidence < threshold);
|
|
11710
|
+
return {
|
|
11711
|
+
live: updated,
|
|
11712
|
+
inference,
|
|
11713
|
+
applied,
|
|
11714
|
+
skipped
|
|
11715
|
+
};
|
|
11716
|
+
}
|
|
11717
|
+
function getAnalyzableCandidates(live, scope) {
|
|
11718
|
+
const graphTasks = getAllTasks(live.config);
|
|
11719
|
+
const { state } = live;
|
|
11720
|
+
const candidates = [];
|
|
11721
|
+
for (const [name, config] of Object.entries(graphTasks)) {
|
|
11722
|
+
const taskState = state.tasks[name];
|
|
11723
|
+
if (taskState?.status === "completed" || taskState?.status === "running") continue;
|
|
11724
|
+
if (scope) {
|
|
11725
|
+
if (scope.includes(name)) candidates.push(name);
|
|
11726
|
+
} else {
|
|
11727
|
+
if (config.inference?.autoDetectable) candidates.push(name);
|
|
11728
|
+
}
|
|
11729
|
+
}
|
|
11730
|
+
return candidates;
|
|
11731
|
+
}
|
|
11732
|
+
function parseInferenceResponse(rawResponse, validNodes, _threshold) {
|
|
11733
|
+
const validSet = new Set(validNodes);
|
|
11734
|
+
try {
|
|
11735
|
+
const jsonStr = extractJson(rawResponse);
|
|
11736
|
+
if (!jsonStr) return [];
|
|
11737
|
+
const parsed = JSON.parse(jsonStr);
|
|
11738
|
+
if (!Array.isArray(parsed)) return [];
|
|
11739
|
+
const suggestions = [];
|
|
11740
|
+
for (const item of parsed) {
|
|
11741
|
+
if (!item || typeof item !== "object") continue;
|
|
11742
|
+
if (typeof item.taskName !== "string") continue;
|
|
11743
|
+
if (typeof item.confidence !== "number") continue;
|
|
11744
|
+
if (!validSet.has(item.taskName)) continue;
|
|
11745
|
+
const confidence = Math.max(0, Math.min(1, item.confidence));
|
|
11746
|
+
suggestions.push({
|
|
11747
|
+
taskName: item.taskName,
|
|
11748
|
+
confidence,
|
|
11749
|
+
reasoning: typeof item.reasoning === "string" ? item.reasoning : "",
|
|
11750
|
+
detectionMethod: "llm-inferred"
|
|
11751
|
+
});
|
|
11752
|
+
}
|
|
11753
|
+
return suggestions;
|
|
11754
|
+
} catch {
|
|
11755
|
+
return [];
|
|
11756
|
+
}
|
|
11757
|
+
}
|
|
11758
|
+
function extractJson(text) {
|
|
11759
|
+
if (!text || typeof text !== "string") return null;
|
|
11760
|
+
const trimmed = text.trim();
|
|
11761
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
11762
|
+
if (fenceMatch) return fenceMatch[1].trim();
|
|
11763
|
+
const firstBracket = trimmed.indexOf("[");
|
|
11764
|
+
const lastBracket = trimmed.lastIndexOf("]");
|
|
11765
|
+
if (firstBracket !== -1 && lastBracket > firstBracket) {
|
|
11766
|
+
return trimmed.slice(firstBracket, lastBracket + 1);
|
|
11767
|
+
}
|
|
11768
|
+
if (trimmed.startsWith("[")) return trimmed;
|
|
11769
|
+
return null;
|
|
11770
|
+
}
|
|
11771
|
+
function createCliAdapter(opts) {
|
|
11772
|
+
const timeout = opts.timeout ?? 6e4;
|
|
11773
|
+
return {
|
|
11774
|
+
analyze: (prompt) => {
|
|
11775
|
+
return new Promise((resolve2, reject) => {
|
|
11776
|
+
const args = opts.args(prompt);
|
|
11777
|
+
const child = execFile(
|
|
11778
|
+
opts.command,
|
|
11779
|
+
opts.stdin ? opts.args("") : args,
|
|
11780
|
+
{
|
|
11781
|
+
timeout,
|
|
11782
|
+
cwd: opts.cwd,
|
|
11783
|
+
env: opts.env ? { ...process.env, ...opts.env } : void 0,
|
|
11784
|
+
maxBuffer: 10 * 1024 * 1024
|
|
11785
|
+
// 10MB
|
|
11786
|
+
},
|
|
11787
|
+
(error, stdout, stderr) => {
|
|
11788
|
+
if (error) {
|
|
11789
|
+
reject(new Error(
|
|
11790
|
+
`CLI adapter failed: ${opts.command} exited with ${error.code ?? "error"}` + (stderr ? `
|
|
11791
|
+
stderr: ${stderr.slice(0, 500)}` : "") + `
|
|
11792
|
+
${error.message}`
|
|
11793
|
+
));
|
|
11794
|
+
} else {
|
|
11795
|
+
resolve2(stdout);
|
|
11796
|
+
}
|
|
11797
|
+
}
|
|
11798
|
+
);
|
|
11799
|
+
if (opts.stdin && child.stdin) {
|
|
11800
|
+
child.stdin.write(prompt);
|
|
11801
|
+
child.stdin.end();
|
|
11802
|
+
}
|
|
11803
|
+
});
|
|
11804
|
+
}
|
|
11805
|
+
};
|
|
11806
|
+
}
|
|
11807
|
+
function createHttpAdapter(opts) {
|
|
11808
|
+
const timeout = opts.timeout ?? 6e4;
|
|
11809
|
+
return {
|
|
11810
|
+
analyze: async (prompt) => {
|
|
11811
|
+
const body = opts.buildBody ? opts.buildBody(prompt) : { prompt };
|
|
11812
|
+
const controller = new AbortController();
|
|
11813
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
11814
|
+
try {
|
|
11815
|
+
const response = await fetch(opts.url, {
|
|
11816
|
+
method: "POST",
|
|
11817
|
+
headers: {
|
|
11818
|
+
"Content-Type": "application/json",
|
|
11819
|
+
...opts.headers ?? {}
|
|
11820
|
+
},
|
|
11821
|
+
body: JSON.stringify(body),
|
|
11822
|
+
signal: controller.signal
|
|
11823
|
+
});
|
|
11824
|
+
if (!response.ok) {
|
|
11825
|
+
const text = await response.text().catch(() => "");
|
|
11826
|
+
throw new Error(`HTTP ${response.status}: ${text.slice(0, 500)}`);
|
|
11827
|
+
}
|
|
11828
|
+
const json = await response.json();
|
|
11829
|
+
if (opts.extractResponse) {
|
|
11830
|
+
return opts.extractResponse(json);
|
|
11831
|
+
}
|
|
11832
|
+
if (typeof json.response === "string") return json.response;
|
|
11833
|
+
if (typeof json.text === "string") return json.text;
|
|
11834
|
+
if (typeof json.content === "string") return json.content;
|
|
11835
|
+
return JSON.stringify(json);
|
|
11836
|
+
} finally {
|
|
11837
|
+
clearTimeout(timer);
|
|
11838
|
+
}
|
|
11839
|
+
}
|
|
11840
|
+
};
|
|
11841
|
+
}
|
|
11842
|
+
|
|
11843
|
+
export { COMPLETION_STRATEGIES, CONFLICT_STRATEGIES, CardCompute, DEFAULTS, EXECUTION_MODES, EXECUTION_STATUS, FileJournal, FileStore, StepMachine as FlowEngine, LocalStorageStore, MemoryJournal, MemoryStore, StepMachine, TASK_STATUS, addDynamicTask, addNode, addProvides, addRequires, apply, applyAll, applyEvent, applyEvents, applyInferences, applyStepResult, batch, buildInferencePrompt, checkCircuitBreaker, computeAvailableOutputs, computeStepInput, createCallbackHandler, createCliAdapter, createDefaultGraphEngineStore, createStepMachine as createEngine, createFireAndForgetHandler, createHttpAdapter, createInitialExecutionState, createInitialState, createLiveGraph, createNoopHandler, createReactiveGraph, createScriptHandler, createShellHandler, createStepMachine, createWebhookHandler, detectStuckState, disableNode, drainTokens, enableNode, exportGraphConfig, exportGraphConfigToFile, extractReturnData, flowToMermaid, getAllTasks, getCandidateTasks, getDownstream, getMaxExecutions, getNode, getProvides, getRefreshStrategy, getRequires, getTask, getUnreachableNodes, getUnreachableTokens, getUpstream, graphToMermaid, hasTask, inferAndApply, inferCompletions, injectTokens, inspect, isExecutionComplete, isNonActiveTask, isRerunnable, isTaskCompleted, isTaskRunning, liveCardsToReactiveGraph, loadGraphConfig, loadStepFlow, mutateGraph, next, planExecution, removeNode, removeProvides, removeRequires, resetNode, resolveConfigTemplates, resolveVariables, restore, schedule, snapshot, validateFlowSchema, validateGraph, validateGraphConfig, validateGraphSchema, validateLiveCardSchema, validateLiveGraph, validateReactiveGraph, validateStepFlowConfig };
|
|
11323
11844
|
//# sourceMappingURL=index.js.map
|
|
11324
11845
|
//# sourceMappingURL=index.js.map
|