yaml-flow 3.0.0 → 3.1.1
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/examples/batch/batch-step-machine.ts +121 -0
- package/examples/browser/index.html +367 -0
- package/examples/continuous-event-graph/live-cards-board.ts +215 -0
- package/examples/continuous-event-graph/live-portfolio-dashboard.ts +555 -0
- package/examples/continuous-event-graph/portfolio-tracker.ts +287 -0
- package/examples/continuous-event-graph/reactive-monitoring.ts +265 -0
- package/examples/continuous-event-graph/reactive-pipeline.ts +168 -0
- package/examples/continuous-event-graph/soc-incident-board.ts +287 -0
- package/examples/continuous-event-graph/stock-dashboard.ts +229 -0
- package/examples/event-graph/ci-cd-pipeline.ts +243 -0
- package/examples/event-graph/executor-diamond.ts +165 -0
- package/examples/event-graph/executor-pipeline.ts +161 -0
- package/examples/event-graph/research-pipeline.ts +137 -0
- package/examples/flows/ai-conversation.yaml +116 -0
- package/examples/flows/order-processing.yaml +143 -0
- package/examples/flows/simple-greeting.yaml +54 -0
- package/examples/graph-of-graphs/multi-stage-etl.ts +307 -0
- package/examples/graph-of-graphs/url-processing-pipeline.ts +254 -0
- package/examples/inference/azure-deployment.ts +149 -0
- package/examples/inference/copilot-cli.ts +138 -0
- package/examples/inference/data-pipeline.ts +145 -0
- package/examples/inference/pluggable-adapters.ts +254 -0
- package/examples/ingest.js +733 -0
- package/examples/node/ai-conversation.ts +195 -0
- package/examples/node/simple-greeting.ts +101 -0
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var addFormats = require('ajv-formats');
|
|
4
4
|
var fs = require('fs');
|
|
5
|
+
var crypto$1 = require('crypto');
|
|
5
6
|
var child_process = require('child_process');
|
|
6
7
|
|
|
7
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -7231,7 +7232,7 @@ function addDynamicTask(graph, taskName, taskConfig) {
|
|
|
7231
7232
|
}
|
|
7232
7233
|
};
|
|
7233
7234
|
}
|
|
7234
|
-
function
|
|
7235
|
+
function createDefaultGraphEngineStore() {
|
|
7235
7236
|
return {
|
|
7236
7237
|
status: "not-started",
|
|
7237
7238
|
executionCount: 0,
|
|
@@ -7244,7 +7245,7 @@ function createDefaultTaskState() {
|
|
|
7244
7245
|
function createInitialExecutionState(graph, executionId) {
|
|
7245
7246
|
const tasks = {};
|
|
7246
7247
|
for (const taskName of Object.keys(graph.tasks)) {
|
|
7247
|
-
tasks[taskName] =
|
|
7248
|
+
tasks[taskName] = createDefaultGraphEngineStore();
|
|
7248
7249
|
}
|
|
7249
7250
|
return {
|
|
7250
7251
|
status: "running",
|
|
@@ -7765,7 +7766,7 @@ function selectOptimalTasks(candidates, graph, state, conflictStrategy) {
|
|
|
7765
7766
|
|
|
7766
7767
|
// src/event-graph/task-transitions.ts
|
|
7767
7768
|
function applyTaskStart(state, taskName) {
|
|
7768
|
-
const existingTask = state.tasks[taskName] ??
|
|
7769
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7769
7770
|
const updatedTask = {
|
|
7770
7771
|
...existingTask,
|
|
7771
7772
|
status: "running",
|
|
@@ -7780,8 +7781,8 @@ function applyTaskStart(state, taskName) {
|
|
|
7780
7781
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7781
7782
|
};
|
|
7782
7783
|
}
|
|
7783
|
-
function applyTaskCompletion(state, graph, taskName, result, dataHash) {
|
|
7784
|
-
const existingTask = state.tasks[taskName] ??
|
|
7784
|
+
function applyTaskCompletion(state, graph, taskName, result, dataHash, data) {
|
|
7785
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7785
7786
|
const taskConfig = graph.tasks[taskName];
|
|
7786
7787
|
if (!taskConfig) {
|
|
7787
7788
|
throw new Error(`Task "${taskName}" not found in graph`);
|
|
@@ -7813,6 +7814,7 @@ function applyTaskCompletion(state, graph, taskName, result, dataHash) {
|
|
|
7813
7814
|
executionCount: existingTask.executionCount + 1,
|
|
7814
7815
|
lastEpoch: existingTask.executionCount + 1,
|
|
7815
7816
|
lastDataHash: dataHash,
|
|
7817
|
+
data,
|
|
7816
7818
|
lastConsumedHashes,
|
|
7817
7819
|
error: void 0
|
|
7818
7820
|
};
|
|
@@ -7825,7 +7827,7 @@ function applyTaskCompletion(state, graph, taskName, result, dataHash) {
|
|
|
7825
7827
|
};
|
|
7826
7828
|
}
|
|
7827
7829
|
function applyTaskFailure(state, graph, taskName, error) {
|
|
7828
|
-
const existingTask = state.tasks[taskName] ??
|
|
7830
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7829
7831
|
const taskConfig = graph.tasks[taskName];
|
|
7830
7832
|
if (taskConfig?.retry) {
|
|
7831
7833
|
const retryCount = existingTask.retryCount + 1;
|
|
@@ -7868,7 +7870,7 @@ function applyTaskFailure(state, graph, taskName, error) {
|
|
|
7868
7870
|
};
|
|
7869
7871
|
}
|
|
7870
7872
|
function applyTaskProgress(state, taskName, message, progress) {
|
|
7871
|
-
const existingTask = state.tasks[taskName] ??
|
|
7873
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7872
7874
|
const updatedTask = {
|
|
7873
7875
|
...existingTask,
|
|
7874
7876
|
progress: typeof progress === "number" ? progress : existingTask.progress,
|
|
@@ -7884,7 +7886,27 @@ function applyTaskProgress(state, taskName, message, progress) {
|
|
|
7884
7886
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7885
7887
|
};
|
|
7886
7888
|
}
|
|
7887
|
-
function
|
|
7889
|
+
function applyTaskRestart(state, taskName) {
|
|
7890
|
+
const existingTask = state.tasks[taskName];
|
|
7891
|
+
if (!existingTask) return state;
|
|
7892
|
+
const updatedTask = {
|
|
7893
|
+
...existingTask,
|
|
7894
|
+
status: "not-started",
|
|
7895
|
+
startedAt: void 0,
|
|
7896
|
+
completedAt: void 0,
|
|
7897
|
+
failedAt: void 0,
|
|
7898
|
+
error: void 0,
|
|
7899
|
+
data: void 0,
|
|
7900
|
+
progress: null,
|
|
7901
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7902
|
+
};
|
|
7903
|
+
return {
|
|
7904
|
+
...state,
|
|
7905
|
+
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
7906
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7907
|
+
};
|
|
7908
|
+
}
|
|
7909
|
+
function createDefaultGraphEngineStore2() {
|
|
7888
7910
|
return {
|
|
7889
7911
|
status: "not-started",
|
|
7890
7912
|
executionCount: 0,
|
|
@@ -7904,11 +7926,13 @@ function apply(state, event, graph) {
|
|
|
7904
7926
|
case "task-started":
|
|
7905
7927
|
return applyTaskStart(state, event.taskName);
|
|
7906
7928
|
case "task-completed":
|
|
7907
|
-
return applyTaskCompletion(state, graph, event.taskName, event.result, event.dataHash);
|
|
7929
|
+
return applyTaskCompletion(state, graph, event.taskName, event.result, event.dataHash, event.data);
|
|
7908
7930
|
case "task-failed":
|
|
7909
7931
|
return applyTaskFailure(state, graph, event.taskName, event.error);
|
|
7910
7932
|
case "task-progress":
|
|
7911
7933
|
return applyTaskProgress(state, event.taskName, event.message, event.progress);
|
|
7934
|
+
case "task-restart":
|
|
7935
|
+
return applyTaskRestart(state, event.taskName);
|
|
7912
7936
|
case "inject-tokens":
|
|
7913
7937
|
return applyInjectTokens(state, event.tokens);
|
|
7914
7938
|
case "agent-action":
|
|
@@ -7979,7 +8003,7 @@ function applyTaskCreation(state, taskName, taskConfig) {
|
|
|
7979
8003
|
...state,
|
|
7980
8004
|
tasks: {
|
|
7981
8005
|
...state.tasks,
|
|
7982
|
-
[taskName]:
|
|
8006
|
+
[taskName]: createDefaultGraphEngineStore()
|
|
7983
8007
|
},
|
|
7984
8008
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7985
8009
|
};
|
|
@@ -9229,7 +9253,7 @@ function createLiveGraph(config, executionId) {
|
|
|
9229
9253
|
const id = executionId ?? `live-${Date.now()}`;
|
|
9230
9254
|
const tasks = {};
|
|
9231
9255
|
for (const taskName of Object.keys(config.tasks)) {
|
|
9232
|
-
tasks[taskName] =
|
|
9256
|
+
tasks[taskName] = createDefaultGraphEngineStore3();
|
|
9233
9257
|
}
|
|
9234
9258
|
const state = {
|
|
9235
9259
|
status: "running",
|
|
@@ -9257,7 +9281,7 @@ function applyEvent(live, event) {
|
|
|
9257
9281
|
newState = applyTaskStart(state, event.taskName);
|
|
9258
9282
|
break;
|
|
9259
9283
|
case "task-completed":
|
|
9260
|
-
newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash);
|
|
9284
|
+
newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data);
|
|
9261
9285
|
break;
|
|
9262
9286
|
case "task-failed":
|
|
9263
9287
|
newState = applyTaskFailure(state, config, event.taskName, event.error);
|
|
@@ -9265,6 +9289,9 @@ function applyEvent(live, event) {
|
|
|
9265
9289
|
case "task-progress":
|
|
9266
9290
|
newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
|
|
9267
9291
|
break;
|
|
9292
|
+
case "task-restart":
|
|
9293
|
+
newState = applyTaskRestart(state, event.taskName);
|
|
9294
|
+
break;
|
|
9268
9295
|
case "inject-tokens":
|
|
9269
9296
|
newState = {
|
|
9270
9297
|
...state,
|
|
@@ -9292,7 +9319,7 @@ function addNode(live, name, taskConfig) {
|
|
|
9292
9319
|
},
|
|
9293
9320
|
state: {
|
|
9294
9321
|
...live.state,
|
|
9295
|
-
tasks: { ...live.state.tasks, [name]:
|
|
9322
|
+
tasks: { ...live.state.tasks, [name]: createDefaultGraphEngineStore3() },
|
|
9296
9323
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
9297
9324
|
}
|
|
9298
9325
|
};
|
|
@@ -9409,7 +9436,7 @@ function resetNode(live, name) {
|
|
|
9409
9436
|
...live.state,
|
|
9410
9437
|
tasks: {
|
|
9411
9438
|
...live.state.tasks,
|
|
9412
|
-
[name]:
|
|
9439
|
+
[name]: createDefaultGraphEngineStore3()
|
|
9413
9440
|
},
|
|
9414
9441
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
9415
9442
|
}
|
|
@@ -9448,7 +9475,7 @@ function enableNode(live, name) {
|
|
|
9448
9475
|
function getNode(live, name) {
|
|
9449
9476
|
const config = live.config.tasks[name];
|
|
9450
9477
|
if (!config) return void 0;
|
|
9451
|
-
const state = live.state.tasks[name] ??
|
|
9478
|
+
const state = live.state.tasks[name] ?? createDefaultGraphEngineStore3();
|
|
9452
9479
|
return { name, config, state };
|
|
9453
9480
|
}
|
|
9454
9481
|
function snapshot(live) {
|
|
@@ -9486,7 +9513,7 @@ function restore(data) {
|
|
|
9486
9513
|
}
|
|
9487
9514
|
return { config, state };
|
|
9488
9515
|
}
|
|
9489
|
-
function
|
|
9516
|
+
function createDefaultGraphEngineStore3() {
|
|
9490
9517
|
return {
|
|
9491
9518
|
status: "not-started",
|
|
9492
9519
|
executionCount: 0,
|
|
@@ -10043,23 +10070,43 @@ var FileJournal = class {
|
|
|
10043
10070
|
}
|
|
10044
10071
|
}
|
|
10045
10072
|
};
|
|
10046
|
-
|
|
10047
|
-
|
|
10073
|
+
function computeDataHash(data) {
|
|
10074
|
+
const json = stableStringify(data);
|
|
10075
|
+
return crypto$1.createHash("sha256").update(json).digest("hex").slice(0, 16);
|
|
10076
|
+
}
|
|
10077
|
+
function stableStringify(value) {
|
|
10078
|
+
if (value === null || value === void 0 || typeof value !== "object") {
|
|
10079
|
+
return JSON.stringify(value);
|
|
10080
|
+
}
|
|
10081
|
+
if (Array.isArray(value)) {
|
|
10082
|
+
return "[" + value.map(stableStringify).join(",") + "]";
|
|
10083
|
+
}
|
|
10084
|
+
const obj = value;
|
|
10085
|
+
const keys = Object.keys(obj).sort();
|
|
10086
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
|
|
10087
|
+
}
|
|
10088
|
+
function encodeCallbackToken(taskName) {
|
|
10089
|
+
const payload = JSON.stringify({ t: taskName, n: Date.now().toString(36) + Math.random().toString(36).slice(2, 6) });
|
|
10090
|
+
return Buffer.from(payload).toString("base64url");
|
|
10091
|
+
}
|
|
10092
|
+
function decodeCallbackToken(token) {
|
|
10093
|
+
try {
|
|
10094
|
+
const payload = JSON.parse(Buffer.from(token, "base64url").toString());
|
|
10095
|
+
if (typeof payload?.t === "string") return { taskName: payload.t };
|
|
10096
|
+
return null;
|
|
10097
|
+
} catch {
|
|
10098
|
+
return null;
|
|
10099
|
+
}
|
|
10100
|
+
}
|
|
10048
10101
|
function createReactiveGraph(config, options, executionId) {
|
|
10049
10102
|
const {
|
|
10050
10103
|
handlers: initialHandlers,
|
|
10051
|
-
maxDispatchRetries = 3,
|
|
10052
|
-
defaultTimeoutMs = 3e4,
|
|
10053
10104
|
journal = new MemoryJournal(),
|
|
10054
|
-
onDispatchFailed,
|
|
10055
|
-
onAbandoned,
|
|
10056
10105
|
onDrain
|
|
10057
10106
|
} = options;
|
|
10058
10107
|
let live = createLiveGraph(config, executionId);
|
|
10059
10108
|
let disposed = false;
|
|
10060
10109
|
const handlers = new Map(Object.entries(initialHandlers));
|
|
10061
|
-
const dispatched = /* @__PURE__ */ new Map();
|
|
10062
|
-
const timeoutTimers = /* @__PURE__ */ new Map();
|
|
10063
10110
|
let draining = false;
|
|
10064
10111
|
let drainQueued = false;
|
|
10065
10112
|
function drain() {
|
|
@@ -10079,201 +10126,183 @@ function createReactiveGraph(config, options, executionId) {
|
|
|
10079
10126
|
}
|
|
10080
10127
|
}
|
|
10081
10128
|
function drainOnce() {
|
|
10082
|
-
sweepTimeouts();
|
|
10083
10129
|
const events = journal.drain();
|
|
10084
|
-
for (const event of events) {
|
|
10085
|
-
if (event.type === "task-completed" || event.type === "task-failed") {
|
|
10086
|
-
const taskName = event.taskName;
|
|
10087
|
-
dispatched.delete(taskName);
|
|
10088
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10089
|
-
timeoutTimers.delete(taskName);
|
|
10090
|
-
}
|
|
10091
|
-
}
|
|
10092
10130
|
if (events.length > 0) {
|
|
10093
10131
|
live = applyEvents(live, events);
|
|
10094
10132
|
}
|
|
10095
10133
|
const result = schedule(live);
|
|
10096
|
-
if (
|
|
10097
|
-
onDrain(events, live, result);
|
|
10134
|
+
if (events.length > 0) {
|
|
10135
|
+
onDrain?.(events, live, result);
|
|
10098
10136
|
}
|
|
10099
10137
|
for (const taskName of result.eligible) {
|
|
10100
|
-
if (dispatched.has(taskName)) continue;
|
|
10101
10138
|
dispatchTask(taskName);
|
|
10102
10139
|
}
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10140
|
+
}
|
|
10141
|
+
function resolveUpstreamState(taskName) {
|
|
10142
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10143
|
+
const requires = taskConfig.requires ?? [];
|
|
10144
|
+
const tokenToTask = /* @__PURE__ */ new Map();
|
|
10145
|
+
for (const [name, cfg] of Object.entries(live.config.tasks)) {
|
|
10146
|
+
for (const token of cfg.provides ?? []) {
|
|
10147
|
+
tokenToTask.set(token, name);
|
|
10148
|
+
}
|
|
10149
|
+
}
|
|
10150
|
+
const state = {};
|
|
10151
|
+
for (const token of requires) {
|
|
10152
|
+
const producerTask = tokenToTask.get(token);
|
|
10153
|
+
if (producerTask) {
|
|
10154
|
+
state[token] = live.state.tasks[producerTask]?.data;
|
|
10155
|
+
} else {
|
|
10156
|
+
state[token] = void 0;
|
|
10157
|
+
}
|
|
10158
|
+
}
|
|
10159
|
+
return state;
|
|
10160
|
+
}
|
|
10161
|
+
async function runPipeline(taskName, callbackToken) {
|
|
10162
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10163
|
+
const handlerNames = taskConfig.taskHandlers ?? [];
|
|
10164
|
+
const upstreamState = resolveUpstreamState(taskName);
|
|
10165
|
+
for (const handlerName of handlerNames) {
|
|
10166
|
+
const handler = handlers.get(handlerName);
|
|
10167
|
+
if (!handler) {
|
|
10168
|
+
throw new Error(`Handler '${handlerName}' not found in registry (task '${taskName}')`);
|
|
10169
|
+
}
|
|
10170
|
+
const input = {
|
|
10171
|
+
nodeId: taskName,
|
|
10172
|
+
state: upstreamState,
|
|
10173
|
+
taskState: live.state.tasks[taskName],
|
|
10174
|
+
config: taskConfig,
|
|
10175
|
+
callbackToken
|
|
10176
|
+
};
|
|
10177
|
+
const status = await handler(input);
|
|
10178
|
+
if (status === "task-initiate-failure") {
|
|
10179
|
+
throw new Error(`Handler '${handlerName}' returned task-initiate-failure (task '${taskName}')`);
|
|
10106
10180
|
}
|
|
10107
10181
|
}
|
|
10108
10182
|
}
|
|
10109
10183
|
function dispatchTask(taskName) {
|
|
10110
|
-
const
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
type: "task-failed",
|
|
10114
|
-
taskName,
|
|
10115
|
-
error: `No handler registered for task "${taskName}"`,
|
|
10116
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10117
|
-
});
|
|
10118
|
-
drainQueued = true;
|
|
10119
|
-
return;
|
|
10120
|
-
}
|
|
10121
|
-
const existing = dispatched.get(taskName);
|
|
10122
|
-
const attempt = existing ? existing.dispatchAttempts + 1 : 1;
|
|
10123
|
-
if (attempt > maxDispatchRetries) {
|
|
10124
|
-
dispatched.set(taskName, {
|
|
10125
|
-
status: "abandoned",
|
|
10126
|
-
dispatchedAt: existing?.dispatchedAt ?? Date.now(),
|
|
10127
|
-
dispatchAttempts: attempt - 1,
|
|
10128
|
-
lastError: existing?.lastError
|
|
10129
|
-
});
|
|
10130
|
-
onAbandoned?.(taskName);
|
|
10131
|
-
journal.append({
|
|
10132
|
-
type: "task-failed",
|
|
10133
|
-
taskName,
|
|
10134
|
-
error: `dispatch-abandoned: handler unreachable after ${attempt - 1} attempts${existing?.lastError ? ` (${existing.lastError})` : ""}`,
|
|
10135
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10136
|
-
});
|
|
10137
|
-
drainQueued = true;
|
|
10184
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10185
|
+
const handlerNames = taskConfig?.taskHandlers;
|
|
10186
|
+
if (!handlerNames || handlerNames.length === 0) {
|
|
10138
10187
|
return;
|
|
10139
10188
|
}
|
|
10140
|
-
dispatched.set(taskName, {
|
|
10141
|
-
status: "initiated",
|
|
10142
|
-
dispatchedAt: Date.now(),
|
|
10143
|
-
dispatchAttempts: attempt
|
|
10144
|
-
});
|
|
10145
10189
|
journal.append({
|
|
10146
10190
|
type: "task-started",
|
|
10147
10191
|
taskName,
|
|
10148
10192
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10149
10193
|
});
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
});
|
|
10159
|
-
dispatched.set(taskName, {
|
|
10160
|
-
...entry,
|
|
10161
|
-
status: entry.dispatchAttempts >= maxDispatchRetries ? "abandoned" : "retry-queued"
|
|
10162
|
-
});
|
|
10163
|
-
if (entry.dispatchAttempts >= maxDispatchRetries) {
|
|
10164
|
-
onAbandoned?.(taskName);
|
|
10165
|
-
journal.append({
|
|
10166
|
-
type: "task-failed",
|
|
10167
|
-
taskName,
|
|
10168
|
-
error: `dispatch-timeout: no callback after ${defaultTimeoutMs}ms (${entry.dispatchAttempts} attempts)`,
|
|
10169
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10170
|
-
});
|
|
10171
|
-
}
|
|
10172
|
-
drain();
|
|
10173
|
-
}
|
|
10174
|
-
}, defaultTimeoutMs);
|
|
10175
|
-
timeoutTimers.set(taskName, timer);
|
|
10176
|
-
}
|
|
10177
|
-
const ctx = {
|
|
10178
|
-
taskName,
|
|
10179
|
-
live,
|
|
10180
|
-
config: live.config.tasks[taskName]
|
|
10181
|
-
};
|
|
10182
|
-
try {
|
|
10183
|
-
const promise = handler(ctx);
|
|
10184
|
-
promise.then(
|
|
10185
|
-
(handlerResult) => {
|
|
10186
|
-
if (disposed) return;
|
|
10187
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10188
|
-
timeoutTimers.delete(taskName);
|
|
10189
|
-
journal.append({
|
|
10190
|
-
type: "task-completed",
|
|
10191
|
-
taskName,
|
|
10192
|
-
result: handlerResult.result,
|
|
10193
|
-
data: handlerResult.data,
|
|
10194
|
-
dataHash: handlerResult.dataHash,
|
|
10195
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10196
|
-
});
|
|
10197
|
-
drain();
|
|
10198
|
-
},
|
|
10199
|
-
(error) => {
|
|
10200
|
-
if (disposed) return;
|
|
10201
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10202
|
-
timeoutTimers.delete(taskName);
|
|
10203
|
-
journal.append({
|
|
10204
|
-
type: "task-failed",
|
|
10205
|
-
taskName,
|
|
10206
|
-
error: error.message ?? String(error),
|
|
10207
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10208
|
-
});
|
|
10209
|
-
drain();
|
|
10210
|
-
}
|
|
10211
|
-
);
|
|
10212
|
-
} catch (syncError) {
|
|
10213
|
-
const err = syncError instanceof Error ? syncError : new Error(String(syncError));
|
|
10214
|
-
dispatched.set(taskName, {
|
|
10215
|
-
status: "dispatch-failed",
|
|
10216
|
-
dispatchedAt: Date.now(),
|
|
10217
|
-
dispatchAttempts: attempt,
|
|
10218
|
-
lastError: err.message
|
|
10219
|
-
});
|
|
10220
|
-
onDispatchFailed?.(taskName, err, attempt);
|
|
10221
|
-
dispatched.set(taskName, {
|
|
10222
|
-
...dispatched.get(taskName),
|
|
10223
|
-
status: "retry-queued"
|
|
10194
|
+
const callbackToken = encodeCallbackToken(taskName);
|
|
10195
|
+
runPipeline(taskName, callbackToken).catch((error) => {
|
|
10196
|
+
if (disposed) return;
|
|
10197
|
+
journal.append({
|
|
10198
|
+
type: "task-failed",
|
|
10199
|
+
taskName,
|
|
10200
|
+
error: error.message ?? String(error),
|
|
10201
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10224
10202
|
});
|
|
10225
|
-
|
|
10226
|
-
}
|
|
10227
|
-
}
|
|
10228
|
-
function sweepTimeouts() {
|
|
10229
|
-
const now = Date.now();
|
|
10230
|
-
for (const [taskName, entry] of dispatched) {
|
|
10231
|
-
if (entry.status !== "initiated") continue;
|
|
10232
|
-
if (defaultTimeoutMs <= 0) continue;
|
|
10233
|
-
if (now - entry.dispatchedAt >= defaultTimeoutMs) {
|
|
10234
|
-
dispatched.set(taskName, {
|
|
10235
|
-
...entry,
|
|
10236
|
-
status: entry.dispatchAttempts >= maxDispatchRetries ? "abandoned" : "retry-queued"
|
|
10237
|
-
});
|
|
10238
|
-
if (entry.dispatchAttempts >= maxDispatchRetries) {
|
|
10239
|
-
onAbandoned?.(taskName);
|
|
10240
|
-
journal.append({
|
|
10241
|
-
type: "task-failed",
|
|
10242
|
-
taskName,
|
|
10243
|
-
error: `dispatch-timeout: no callback after ${defaultTimeoutMs}ms (${entry.dispatchAttempts} attempts)`,
|
|
10244
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10245
|
-
});
|
|
10246
|
-
}
|
|
10247
|
-
clearTimeout(timeoutTimers.get(taskName));
|
|
10248
|
-
timeoutTimers.delete(taskName);
|
|
10249
|
-
}
|
|
10250
|
-
}
|
|
10203
|
+
drain();
|
|
10204
|
+
});
|
|
10251
10205
|
}
|
|
10252
10206
|
return {
|
|
10253
10207
|
push(event) {
|
|
10254
10208
|
if (disposed) return;
|
|
10255
|
-
|
|
10209
|
+
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
10210
|
+
event = { ...event, dataHash: computeDataHash(event.data) };
|
|
10211
|
+
}
|
|
10212
|
+
journal.append(event);
|
|
10256
10213
|
drain();
|
|
10257
10214
|
},
|
|
10258
10215
|
pushAll(events) {
|
|
10259
10216
|
if (disposed) return;
|
|
10260
|
-
|
|
10261
|
-
|
|
10217
|
+
for (const event of events) {
|
|
10218
|
+
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
10219
|
+
journal.append({ ...event, dataHash: computeDataHash(event.data) });
|
|
10220
|
+
} else {
|
|
10221
|
+
journal.append(event);
|
|
10222
|
+
}
|
|
10223
|
+
}
|
|
10224
|
+
drain();
|
|
10225
|
+
},
|
|
10226
|
+
resolveCallback(callbackToken, data, errors) {
|
|
10227
|
+
if (disposed) return;
|
|
10228
|
+
const decoded = decodeCallbackToken(callbackToken);
|
|
10229
|
+
if (!decoded) return;
|
|
10230
|
+
const { taskName } = decoded;
|
|
10231
|
+
if (!live.config.tasks[taskName]) return;
|
|
10232
|
+
if (errors && errors.length > 0) {
|
|
10233
|
+
journal.append({
|
|
10234
|
+
type: "task-failed",
|
|
10235
|
+
taskName,
|
|
10236
|
+
error: errors.join("; "),
|
|
10237
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10238
|
+
});
|
|
10239
|
+
} else {
|
|
10240
|
+
const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
|
|
10241
|
+
journal.append({
|
|
10242
|
+
type: "task-completed",
|
|
10243
|
+
taskName,
|
|
10244
|
+
data,
|
|
10245
|
+
dataHash,
|
|
10246
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10247
|
+
});
|
|
10248
|
+
}
|
|
10262
10249
|
drain();
|
|
10263
10250
|
},
|
|
10264
|
-
addNode(name, taskConfig
|
|
10251
|
+
addNode(name, taskConfig) {
|
|
10265
10252
|
if (disposed) return;
|
|
10266
10253
|
live = addNode(live, name, taskConfig);
|
|
10267
|
-
handlers.set(name, handler);
|
|
10268
10254
|
drain();
|
|
10269
10255
|
},
|
|
10270
10256
|
removeNode(name) {
|
|
10271
10257
|
if (disposed) return;
|
|
10272
10258
|
live = removeNode(live, name);
|
|
10259
|
+
},
|
|
10260
|
+
addRequires(nodeName, tokens) {
|
|
10261
|
+
if (disposed) return;
|
|
10262
|
+
live = addRequires(live, nodeName, tokens);
|
|
10263
|
+
drain();
|
|
10264
|
+
},
|
|
10265
|
+
removeRequires(nodeName, tokens) {
|
|
10266
|
+
if (disposed) return;
|
|
10267
|
+
live = removeRequires(live, nodeName, tokens);
|
|
10268
|
+
drain();
|
|
10269
|
+
},
|
|
10270
|
+
addProvides(nodeName, tokens) {
|
|
10271
|
+
if (disposed) return;
|
|
10272
|
+
live = addProvides(live, nodeName, tokens);
|
|
10273
|
+
drain();
|
|
10274
|
+
},
|
|
10275
|
+
removeProvides(nodeName, tokens) {
|
|
10276
|
+
if (disposed) return;
|
|
10277
|
+
live = removeProvides(live, nodeName, tokens);
|
|
10278
|
+
},
|
|
10279
|
+
registerHandler(name, fn) {
|
|
10280
|
+
handlers.set(name, fn);
|
|
10281
|
+
},
|
|
10282
|
+
unregisterHandler(name) {
|
|
10273
10283
|
handlers.delete(name);
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10284
|
+
},
|
|
10285
|
+
retrigger(taskName) {
|
|
10286
|
+
if (disposed) return;
|
|
10287
|
+
if (!live.config.tasks[taskName]) return;
|
|
10288
|
+
journal.append({
|
|
10289
|
+
type: "task-restart",
|
|
10290
|
+
taskName,
|
|
10291
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10292
|
+
});
|
|
10293
|
+
drain();
|
|
10294
|
+
},
|
|
10295
|
+
retriggerAll(taskNames) {
|
|
10296
|
+
if (disposed) return;
|
|
10297
|
+
for (const name of taskNames) {
|
|
10298
|
+
if (!live.config.tasks[name]) continue;
|
|
10299
|
+
journal.append({
|
|
10300
|
+
type: "task-restart",
|
|
10301
|
+
taskName: name,
|
|
10302
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10303
|
+
});
|
|
10304
|
+
}
|
|
10305
|
+
drain();
|
|
10277
10306
|
},
|
|
10278
10307
|
getState() {
|
|
10279
10308
|
return live;
|
|
@@ -10281,267 +10310,360 @@ function createReactiveGraph(config, options, executionId) {
|
|
|
10281
10310
|
getSchedule() {
|
|
10282
10311
|
return schedule(live);
|
|
10283
10312
|
},
|
|
10284
|
-
getDispatchState() {
|
|
10285
|
-
return dispatched;
|
|
10286
|
-
},
|
|
10287
10313
|
dispose() {
|
|
10288
10314
|
disposed = true;
|
|
10289
|
-
for (const timer of timeoutTimers.values()) {
|
|
10290
|
-
clearTimeout(timer);
|
|
10291
|
-
}
|
|
10292
|
-
timeoutTimers.clear();
|
|
10293
10315
|
}
|
|
10294
10316
|
};
|
|
10295
10317
|
}
|
|
10296
10318
|
|
|
10297
|
-
// src/
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
const
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10319
|
+
// src/continuous-event-graph/validate.ts
|
|
10320
|
+
function validateLiveGraph(live) {
|
|
10321
|
+
const issues = [];
|
|
10322
|
+
const { config, state } = live;
|
|
10323
|
+
const tasks = getAllTasks(config);
|
|
10324
|
+
const taskNames = Object.keys(tasks);
|
|
10325
|
+
for (const name of taskNames) {
|
|
10326
|
+
if (!state.tasks[name]) {
|
|
10327
|
+
issues.push({
|
|
10328
|
+
severity: "error",
|
|
10329
|
+
code: "MISSING_STATE",
|
|
10330
|
+
message: `Task "${name}" exists in config but has no state entry`,
|
|
10331
|
+
tasks: [name]
|
|
10332
|
+
});
|
|
10333
|
+
}
|
|
10309
10334
|
}
|
|
10310
|
-
const
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
lines.push(`Completed tasks: ${completedTasks.join(", ")}`);
|
|
10320
|
-
lines.push("");
|
|
10335
|
+
for (const name of Object.keys(state.tasks)) {
|
|
10336
|
+
if (!tasks[name]) {
|
|
10337
|
+
issues.push({
|
|
10338
|
+
severity: "warning",
|
|
10339
|
+
code: "ORPHAN_STATE",
|
|
10340
|
+
message: `State entry "${name}" has no corresponding task config`,
|
|
10341
|
+
tasks: [name]
|
|
10342
|
+
});
|
|
10343
|
+
}
|
|
10321
10344
|
}
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10345
|
+
for (const name of taskNames) {
|
|
10346
|
+
const ts = state.tasks[name];
|
|
10347
|
+
if (!ts) continue;
|
|
10348
|
+
if (ts.status === TASK_STATUS.RUNNING && !ts.startedAt) {
|
|
10349
|
+
issues.push({
|
|
10350
|
+
severity: "warning",
|
|
10351
|
+
code: "RUNNING_WITHOUT_START",
|
|
10352
|
+
message: `Task "${name}" is running but has no startedAt timestamp`,
|
|
10353
|
+
tasks: [name]
|
|
10354
|
+
});
|
|
10330
10355
|
}
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
if (
|
|
10356
|
+
if (ts.status === TASK_STATUS.COMPLETED && !ts.completedAt) {
|
|
10357
|
+
issues.push({
|
|
10358
|
+
severity: "warning",
|
|
10359
|
+
code: "COMPLETED_WITHOUT_TIMESTAMP",
|
|
10360
|
+
message: `Task "${name}" is completed but has no completedAt timestamp`,
|
|
10361
|
+
tasks: [name]
|
|
10362
|
+
});
|
|
10363
|
+
}
|
|
10364
|
+
if (ts.status === TASK_STATUS.FAILED) {
|
|
10365
|
+
if (!ts.failedAt) {
|
|
10366
|
+
issues.push({
|
|
10367
|
+
severity: "warning",
|
|
10368
|
+
code: "FAILED_WITHOUT_INFO",
|
|
10369
|
+
message: `Task "${name}" is failed but has no failedAt timestamp`,
|
|
10370
|
+
tasks: [name]
|
|
10371
|
+
});
|
|
10372
|
+
}
|
|
10373
|
+
if (!ts.error) {
|
|
10374
|
+
issues.push({
|
|
10375
|
+
severity: "info",
|
|
10376
|
+
code: "FAILED_WITHOUT_INFO",
|
|
10377
|
+
message: `Task "${name}" is failed but has no error message`,
|
|
10378
|
+
tasks: [name]
|
|
10379
|
+
});
|
|
10380
|
+
}
|
|
10341
10381
|
}
|
|
10342
|
-
lines.push("");
|
|
10343
10382
|
}
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10383
|
+
const expectedOutputs = /* @__PURE__ */ new Set();
|
|
10384
|
+
for (const name of taskNames) {
|
|
10385
|
+
const ts = state.tasks[name];
|
|
10386
|
+
if (ts?.status === TASK_STATUS.COMPLETED) {
|
|
10387
|
+
for (const token of getProvides(tasks[name])) {
|
|
10388
|
+
expectedOutputs.add(token);
|
|
10389
|
+
}
|
|
10390
|
+
}
|
|
10349
10391
|
}
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
|
|
10362
|
-
lines.push("");
|
|
10363
|
-
lines.push("Rules:");
|
|
10364
|
-
lines.push('- Only include tasks from the "Tasks to Analyze" section');
|
|
10365
|
-
lines.push("- confidence 0.0 = no evidence of completion, 1.0 = certain it is complete");
|
|
10366
|
-
lines.push("- If you have no evidence for a task, omit it from the array");
|
|
10367
|
-
lines.push("- Be conservative \u2014 require clear evidence before high confidence");
|
|
10368
|
-
lines.push("- Respond ONLY with the JSON array, no additional text");
|
|
10369
|
-
return lines.join("\n");
|
|
10370
|
-
}
|
|
10371
|
-
async function inferCompletions(live, adapter, options = {}) {
|
|
10372
|
-
options.threshold ?? DEFAULT_THRESHOLD;
|
|
10373
|
-
const analyzedNodes = getAnalyzableCandidates(live, options.scope);
|
|
10374
|
-
if (analyzedNodes.length === 0) {
|
|
10375
|
-
return { suggestions: [], promptUsed: "", rawResponse: "", analyzedNodes: [] };
|
|
10392
|
+
const actualOutputs = new Set(state.availableOutputs);
|
|
10393
|
+
const allProducible = /* @__PURE__ */ new Set();
|
|
10394
|
+
for (const taskConfig of Object.values(tasks)) {
|
|
10395
|
+
for (const t of getProvides(taskConfig)) allProducible.add(t);
|
|
10396
|
+
if (taskConfig.on) {
|
|
10397
|
+
for (const tokens of Object.values(taskConfig.on)) {
|
|
10398
|
+
for (const t of tokens) allProducible.add(t);
|
|
10399
|
+
}
|
|
10400
|
+
}
|
|
10401
|
+
if (taskConfig.on_failure) {
|
|
10402
|
+
for (const t of taskConfig.on_failure) allProducible.add(t);
|
|
10403
|
+
}
|
|
10376
10404
|
}
|
|
10377
|
-
const
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
}
|
|
10387
|
-
function applyInferences(live, result, threshold = DEFAULT_THRESHOLD) {
|
|
10388
|
-
let current = live;
|
|
10389
|
-
for (const suggestion of result.suggestions) {
|
|
10390
|
-
if (suggestion.confidence < threshold) continue;
|
|
10391
|
-
const taskState = current.state.tasks[suggestion.taskName];
|
|
10392
|
-
if (!taskState) continue;
|
|
10393
|
-
if (taskState.status === "completed" || taskState.status === "running") continue;
|
|
10394
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10395
|
-
current = applyEvent(current, {
|
|
10396
|
-
type: "task-started",
|
|
10397
|
-
taskName: suggestion.taskName,
|
|
10398
|
-
timestamp: now
|
|
10399
|
-
});
|
|
10400
|
-
current = applyEvent(current, {
|
|
10401
|
-
type: "task-completed",
|
|
10402
|
-
taskName: suggestion.taskName,
|
|
10403
|
-
timestamp: now,
|
|
10404
|
-
result: "llm-inferred"
|
|
10405
|
-
});
|
|
10405
|
+
for (const token of actualOutputs) {
|
|
10406
|
+
if (!expectedOutputs.has(token) && !allProducible.has(token)) {
|
|
10407
|
+
issues.push({
|
|
10408
|
+
severity: "info",
|
|
10409
|
+
code: "INJECTED_TOKEN",
|
|
10410
|
+
message: `Token "${token}" is available but no task in the graph can produce it (likely injected)`,
|
|
10411
|
+
tokens: [token]
|
|
10412
|
+
});
|
|
10413
|
+
}
|
|
10406
10414
|
}
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
return {
|
|
10416
|
-
live: updated,
|
|
10417
|
-
inference,
|
|
10418
|
-
applied,
|
|
10419
|
-
skipped
|
|
10420
|
-
};
|
|
10421
|
-
}
|
|
10422
|
-
function getAnalyzableCandidates(live, scope) {
|
|
10423
|
-
const graphTasks = getAllTasks(live.config);
|
|
10424
|
-
const { state } = live;
|
|
10425
|
-
const candidates = [];
|
|
10426
|
-
for (const [name, config] of Object.entries(graphTasks)) {
|
|
10427
|
-
const taskState = state.tasks[name];
|
|
10428
|
-
if (taskState?.status === "completed" || taskState?.status === "running") continue;
|
|
10429
|
-
if (scope) {
|
|
10430
|
-
if (scope.includes(name)) candidates.push(name);
|
|
10431
|
-
} else {
|
|
10432
|
-
if (config.inference?.autoDetectable) candidates.push(name);
|
|
10415
|
+
for (const token of expectedOutputs) {
|
|
10416
|
+
if (!actualOutputs.has(token)) {
|
|
10417
|
+
issues.push({
|
|
10418
|
+
severity: "warning",
|
|
10419
|
+
code: "MISSING_OUTPUT",
|
|
10420
|
+
message: `Token "${token}" should be available (its producer completed) but is not in availableOutputs`,
|
|
10421
|
+
tokens: [token]
|
|
10422
|
+
});
|
|
10433
10423
|
}
|
|
10434
10424
|
}
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
confidence,
|
|
10454
|
-
reasoning: typeof item.reasoning === "string" ? item.reasoning : "",
|
|
10455
|
-
detectionMethod: "llm-inferred"
|
|
10425
|
+
for (const name of taskNames) {
|
|
10426
|
+
const ts = state.tasks[name];
|
|
10427
|
+
if (!ts) continue;
|
|
10428
|
+
if (ts.executionCount < 0) {
|
|
10429
|
+
issues.push({
|
|
10430
|
+
severity: "error",
|
|
10431
|
+
code: "INVALID_EXECUTION_COUNT",
|
|
10432
|
+
message: `Task "${name}" has negative execution count: ${ts.executionCount}`,
|
|
10433
|
+
tasks: [name]
|
|
10434
|
+
});
|
|
10435
|
+
}
|
|
10436
|
+
const maxExec = tasks[name].maxExecutions;
|
|
10437
|
+
if (maxExec !== void 0 && ts.executionCount > maxExec) {
|
|
10438
|
+
issues.push({
|
|
10439
|
+
severity: "error",
|
|
10440
|
+
code: "EXCEEDED_MAX_EXECUTIONS",
|
|
10441
|
+
message: `Task "${name}" executed ${ts.executionCount} times, exceeding maxExecutions of ${maxExec}`,
|
|
10442
|
+
tasks: [name]
|
|
10456
10443
|
});
|
|
10457
10444
|
}
|
|
10458
|
-
return suggestions;
|
|
10459
|
-
} catch {
|
|
10460
|
-
return [];
|
|
10461
10445
|
}
|
|
10446
|
+
return buildResult2(issues);
|
|
10462
10447
|
}
|
|
10463
|
-
function
|
|
10464
|
-
|
|
10465
|
-
const
|
|
10466
|
-
const
|
|
10467
|
-
|
|
10468
|
-
const
|
|
10469
|
-
const
|
|
10470
|
-
|
|
10471
|
-
|
|
10448
|
+
function validateReactiveGraph(input) {
|
|
10449
|
+
const { graph, handlers } = input;
|
|
10450
|
+
const live = graph.getState();
|
|
10451
|
+
const issues = [];
|
|
10452
|
+
const tasks = getAllTasks(live.config);
|
|
10453
|
+
const taskNames = Object.keys(tasks);
|
|
10454
|
+
const handlerNames = new Set(Object.keys(handlers));
|
|
10455
|
+
const referencedHandlers = /* @__PURE__ */ new Set();
|
|
10456
|
+
for (const name of taskNames) {
|
|
10457
|
+
const taskHandlers = tasks[name].taskHandlers;
|
|
10458
|
+
if (taskHandlers) {
|
|
10459
|
+
for (const h of taskHandlers) {
|
|
10460
|
+
referencedHandlers.add(h);
|
|
10461
|
+
}
|
|
10462
|
+
}
|
|
10472
10463
|
}
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
reject(new Error(
|
|
10495
|
-
`CLI adapter failed: ${opts.command} exited with ${error.code ?? "error"}` + (stderr ? `
|
|
10496
|
-
stderr: ${stderr.slice(0, 500)}` : "") + `
|
|
10497
|
-
${error.message}`
|
|
10498
|
-
));
|
|
10499
|
-
} else {
|
|
10500
|
-
resolve2(stdout);
|
|
10501
|
-
}
|
|
10502
|
-
}
|
|
10503
|
-
);
|
|
10504
|
-
if (opts.stdin && child.stdin) {
|
|
10505
|
-
child.stdin.write(prompt);
|
|
10506
|
-
child.stdin.end();
|
|
10507
|
-
}
|
|
10464
|
+
for (const name of taskNames) {
|
|
10465
|
+
const taskHandlers = tasks[name].taskHandlers;
|
|
10466
|
+
if (!taskHandlers) continue;
|
|
10467
|
+
for (const h of taskHandlers) {
|
|
10468
|
+
if (!handlers[h]) {
|
|
10469
|
+
issues.push({
|
|
10470
|
+
severity: "error",
|
|
10471
|
+
code: "MISSING_HANDLER",
|
|
10472
|
+
message: `Task "${name}" references handler "${h}" but it is not in the registry`,
|
|
10473
|
+
tasks: [name]
|
|
10474
|
+
});
|
|
10475
|
+
}
|
|
10476
|
+
}
|
|
10477
|
+
}
|
|
10478
|
+
for (const name of handlerNames) {
|
|
10479
|
+
if (!referencedHandlers.has(name)) {
|
|
10480
|
+
issues.push({
|
|
10481
|
+
severity: "warning",
|
|
10482
|
+
code: "ORPHAN_HANDLER",
|
|
10483
|
+
message: `Handler "${name}" is registered but not referenced by any task's taskHandlers`,
|
|
10484
|
+
tasks: [name]
|
|
10508
10485
|
});
|
|
10509
10486
|
}
|
|
10510
|
-
}
|
|
10487
|
+
}
|
|
10488
|
+
const liveResult = validateLiveGraph(live);
|
|
10489
|
+
issues.push(...liveResult.issues);
|
|
10490
|
+
return buildResult2(issues);
|
|
10511
10491
|
}
|
|
10512
|
-
function
|
|
10513
|
-
const
|
|
10492
|
+
function buildResult2(issues) {
|
|
10493
|
+
const errors = issues.filter((i) => i.severity === "error");
|
|
10494
|
+
const warnings = issues.filter((i) => i.severity === "warning");
|
|
10514
10495
|
return {
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10496
|
+
valid: errors.length === 0,
|
|
10497
|
+
issues,
|
|
10498
|
+
errors,
|
|
10499
|
+
warnings
|
|
10500
|
+
};
|
|
10501
|
+
}
|
|
10502
|
+
|
|
10503
|
+
// src/continuous-event-graph/mutate.ts
|
|
10504
|
+
function mutateGraph(live, mutations) {
|
|
10505
|
+
let current = live;
|
|
10506
|
+
for (const mutation of mutations) {
|
|
10507
|
+
current = applySingleMutation(current, mutation);
|
|
10508
|
+
}
|
|
10509
|
+
return current;
|
|
10510
|
+
}
|
|
10511
|
+
function applySingleMutation(live, mutation) {
|
|
10512
|
+
switch (mutation.type) {
|
|
10513
|
+
case "add-node":
|
|
10514
|
+
return addNode(live, mutation.name, mutation.config);
|
|
10515
|
+
case "remove-node":
|
|
10516
|
+
return removeNode(live, mutation.name);
|
|
10517
|
+
case "add-requires":
|
|
10518
|
+
return addRequires(live, mutation.taskName, mutation.tokens);
|
|
10519
|
+
case "remove-requires":
|
|
10520
|
+
return removeRequires(live, mutation.taskName, mutation.tokens);
|
|
10521
|
+
case "add-provides":
|
|
10522
|
+
return addProvides(live, mutation.taskName, mutation.tokens);
|
|
10523
|
+
case "remove-provides":
|
|
10524
|
+
return removeProvides(live, mutation.taskName, mutation.tokens);
|
|
10525
|
+
case "inject-tokens":
|
|
10526
|
+
return injectTokens(live, mutation.tokens);
|
|
10527
|
+
case "drain-tokens":
|
|
10528
|
+
return drainTokens(live, mutation.tokens);
|
|
10529
|
+
case "reset-node":
|
|
10530
|
+
return resetNode(live, mutation.name);
|
|
10531
|
+
case "disable-node":
|
|
10532
|
+
return disableNode(live, mutation.name);
|
|
10533
|
+
case "enable-node":
|
|
10534
|
+
return enableNode(live, mutation.name);
|
|
10535
|
+
case "apply-events":
|
|
10536
|
+
return applyEvents(live, mutation.events);
|
|
10537
|
+
default:
|
|
10538
|
+
throw new Error(`Unknown mutation type: ${mutation.type}`);
|
|
10539
|
+
}
|
|
10540
|
+
}
|
|
10541
|
+
function createCallbackHandler(fn, getResolve) {
|
|
10542
|
+
return async (input) => {
|
|
10543
|
+
const { callbackToken } = input;
|
|
10544
|
+
Promise.resolve(fn(input)).then((data) => getResolve()(callbackToken, data)).catch((err) => getResolve()(callbackToken, {}, [err instanceof Error ? err.message : String(err)]));
|
|
10545
|
+
return "task-initiated";
|
|
10546
|
+
};
|
|
10547
|
+
}
|
|
10548
|
+
function createFireAndForgetHandler(fn, getResolve) {
|
|
10549
|
+
return async (input) => {
|
|
10550
|
+
const { callbackToken } = input;
|
|
10551
|
+
Promise.resolve(fn(input)).then(() => getResolve()(callbackToken, {})).catch(() => getResolve()(callbackToken, {}));
|
|
10552
|
+
return "task-initiated";
|
|
10553
|
+
};
|
|
10554
|
+
}
|
|
10555
|
+
function createShellHandler(options) {
|
|
10556
|
+
const {
|
|
10557
|
+
command: commandTemplate,
|
|
10558
|
+
cwd,
|
|
10559
|
+
env,
|
|
10560
|
+
timeoutMs = 3e4,
|
|
10561
|
+
exitCodeMap,
|
|
10562
|
+
captureOutput = false,
|
|
10563
|
+
getResolve
|
|
10564
|
+
} = options;
|
|
10565
|
+
return async (input) => {
|
|
10566
|
+
const { callbackToken, nodeId } = input;
|
|
10567
|
+
const command = commandTemplate.replace(/\$\{taskName\}/g, nodeId);
|
|
10568
|
+
child_process.exec(
|
|
10569
|
+
command,
|
|
10570
|
+
{
|
|
10571
|
+
cwd,
|
|
10572
|
+
env: env ? { ...process.env, ...env } : void 0,
|
|
10573
|
+
timeout: timeoutMs,
|
|
10574
|
+
maxBuffer: 10 * 1024 * 1024
|
|
10575
|
+
// 10MB
|
|
10576
|
+
},
|
|
10577
|
+
(error, stdout, stderr) => {
|
|
10578
|
+
const exitCode = error?.code ?? (error ? 1 : 0);
|
|
10579
|
+
if (exitCode !== 0 && !exitCodeMap?.[exitCode]) {
|
|
10580
|
+
getResolve()(callbackToken, {}, [`Command exited with code ${exitCode}: ${stderr || error?.message}`]);
|
|
10581
|
+
return;
|
|
10532
10582
|
}
|
|
10533
|
-
const
|
|
10534
|
-
if (
|
|
10535
|
-
|
|
10583
|
+
const data = {};
|
|
10584
|
+
if (captureOutput) {
|
|
10585
|
+
data.stdout = stdout;
|
|
10586
|
+
data.stderr = stderr;
|
|
10587
|
+
data.exitCode = exitCode;
|
|
10536
10588
|
}
|
|
10537
|
-
|
|
10538
|
-
if (typeof json.text === "string") return json.text;
|
|
10539
|
-
if (typeof json.content === "string") return json.content;
|
|
10540
|
-
return JSON.stringify(json);
|
|
10541
|
-
} finally {
|
|
10542
|
-
clearTimeout(timer);
|
|
10589
|
+
getResolve()(callbackToken, data);
|
|
10543
10590
|
}
|
|
10544
|
-
|
|
10591
|
+
);
|
|
10592
|
+
return "task-initiated";
|
|
10593
|
+
};
|
|
10594
|
+
}
|
|
10595
|
+
function detectRuntime(scriptPath) {
|
|
10596
|
+
if (scriptPath.endsWith(".js") || scriptPath.endsWith(".mjs") || scriptPath.endsWith(".ts")) return "node";
|
|
10597
|
+
if (scriptPath.endsWith(".py")) return "python3";
|
|
10598
|
+
if (scriptPath.endsWith(".sh")) return "bash";
|
|
10599
|
+
return "bash";
|
|
10600
|
+
}
|
|
10601
|
+
function createScriptHandler(options) {
|
|
10602
|
+
const {
|
|
10603
|
+
scriptPath,
|
|
10604
|
+
runtime,
|
|
10605
|
+
args = [],
|
|
10606
|
+
cwd,
|
|
10607
|
+
timeoutMs = 6e4,
|
|
10608
|
+
captureOutput = false,
|
|
10609
|
+
getResolve
|
|
10610
|
+
} = options;
|
|
10611
|
+
const resolvedRuntime = runtime ?? detectRuntime(scriptPath);
|
|
10612
|
+
const shellArgs = [ctx_taskName_placeholder, ...args].join(" ");
|
|
10613
|
+
const command = `${resolvedRuntime} ${scriptPath} ${shellArgs}`;
|
|
10614
|
+
return createShellHandler({
|
|
10615
|
+
command: command.replace(ctx_taskName_placeholder, "${taskName}"),
|
|
10616
|
+
cwd,
|
|
10617
|
+
timeoutMs,
|
|
10618
|
+
captureOutput,
|
|
10619
|
+
getResolve
|
|
10620
|
+
});
|
|
10621
|
+
}
|
|
10622
|
+
var ctx_taskName_placeholder = "__TASK_NAME__";
|
|
10623
|
+
function createWebhookHandler(options) {
|
|
10624
|
+
const {
|
|
10625
|
+
url: urlTemplate,
|
|
10626
|
+
method = "POST",
|
|
10627
|
+
headers = {},
|
|
10628
|
+
timeoutMs = 3e4,
|
|
10629
|
+
failOnNon2xx = true,
|
|
10630
|
+
getResolve
|
|
10631
|
+
} = options;
|
|
10632
|
+
return async (input) => {
|
|
10633
|
+
const { callbackToken, nodeId, config } = input;
|
|
10634
|
+
const url = urlTemplate.replace(/\$\{taskName\}/g, nodeId);
|
|
10635
|
+
const body = JSON.stringify({
|
|
10636
|
+
taskName: nodeId,
|
|
10637
|
+
callbackToken,
|
|
10638
|
+
config
|
|
10639
|
+
});
|
|
10640
|
+
const controller = new AbortController();
|
|
10641
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
10642
|
+
fetch(url, {
|
|
10643
|
+
method,
|
|
10644
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
10645
|
+
body,
|
|
10646
|
+
signal: controller.signal
|
|
10647
|
+
}).then(async (response) => {
|
|
10648
|
+
clearTimeout(timer);
|
|
10649
|
+
if (failOnNon2xx && !response.ok) {
|
|
10650
|
+
const text = await response.text().catch(() => "");
|
|
10651
|
+
getResolve()(callbackToken, {}, [`HTTP ${response.status}: ${text}`]);
|
|
10652
|
+
return;
|
|
10653
|
+
}
|
|
10654
|
+
const data = await response.json().catch(() => ({}));
|
|
10655
|
+
getResolve()(callbackToken, data);
|
|
10656
|
+
}).catch((err) => {
|
|
10657
|
+
clearTimeout(timer);
|
|
10658
|
+
getResolve()(callbackToken, {}, [err instanceof Error ? err.message : String(err)]);
|
|
10659
|
+
});
|
|
10660
|
+
return "task-initiated";
|
|
10661
|
+
};
|
|
10662
|
+
}
|
|
10663
|
+
function createNoopHandler(getResolve, staticData) {
|
|
10664
|
+
return async (input) => {
|
|
10665
|
+
getResolve()(input.callbackToken, staticData ?? {});
|
|
10666
|
+
return "task-initiated";
|
|
10545
10667
|
};
|
|
10546
10668
|
}
|
|
10547
10669
|
|
|
@@ -11325,6 +11447,405 @@ var CardCompute = {
|
|
|
11325
11447
|
}
|
|
11326
11448
|
};
|
|
11327
11449
|
|
|
11450
|
+
// src/continuous-event-graph/live-cards-bridge.ts
|
|
11451
|
+
function liveCardsToReactiveGraph(input, options = {}) {
|
|
11452
|
+
let cards;
|
|
11453
|
+
let boardSettings = {};
|
|
11454
|
+
let boardId;
|
|
11455
|
+
if (!Array.isArray(input) && "nodes" in input) {
|
|
11456
|
+
const board = input;
|
|
11457
|
+
cards = board.nodes;
|
|
11458
|
+
boardId = board.id;
|
|
11459
|
+
boardSettings = board.settings ?? {};
|
|
11460
|
+
} else {
|
|
11461
|
+
cards = input;
|
|
11462
|
+
}
|
|
11463
|
+
const {
|
|
11464
|
+
sourceHandlers = {},
|
|
11465
|
+
defaultSourceHandler,
|
|
11466
|
+
cardHandlers = {},
|
|
11467
|
+
reactiveOptions = {},
|
|
11468
|
+
graphSettings = {},
|
|
11469
|
+
executionId
|
|
11470
|
+
} = options;
|
|
11471
|
+
const cardMap = /* @__PURE__ */ new Map();
|
|
11472
|
+
for (const card of cards) {
|
|
11473
|
+
if (cardMap.has(card.id)) {
|
|
11474
|
+
throw new Error(`Duplicate card ID: "${card.id}"`);
|
|
11475
|
+
}
|
|
11476
|
+
cardMap.set(card.id, card);
|
|
11477
|
+
}
|
|
11478
|
+
const sharedState = options.sharedState ?? /* @__PURE__ */ new Map();
|
|
11479
|
+
const tasks = {};
|
|
11480
|
+
for (const card of cards) {
|
|
11481
|
+
const requires = card.data?.requires ?? [];
|
|
11482
|
+
for (const req of requires) {
|
|
11483
|
+
if (!cardMap.has(req)) {
|
|
11484
|
+
throw new Error(`Card "${card.id}" requires "${req}" but no card with that ID exists`);
|
|
11485
|
+
}
|
|
11486
|
+
}
|
|
11487
|
+
tasks[card.id] = {
|
|
11488
|
+
requires: requires.length > 0 ? requires : void 0,
|
|
11489
|
+
provides: [card.id],
|
|
11490
|
+
taskHandlers: [card.id],
|
|
11491
|
+
// each card has a named handler matching its ID
|
|
11492
|
+
description: card.meta?.title ?? `${card.type}: ${card.id}`
|
|
11493
|
+
};
|
|
11494
|
+
}
|
|
11495
|
+
const config = {
|
|
11496
|
+
id: boardId ?? `live-cards-${Date.now()}`,
|
|
11497
|
+
settings: {
|
|
11498
|
+
completion: "manual",
|
|
11499
|
+
execution_mode: "eligibility-mode",
|
|
11500
|
+
...boardSettings,
|
|
11501
|
+
...graphSettings
|
|
11502
|
+
},
|
|
11503
|
+
tasks
|
|
11504
|
+
};
|
|
11505
|
+
const handlers = {};
|
|
11506
|
+
let graphRef = null;
|
|
11507
|
+
const getResolve = () => (token, data, errors) => {
|
|
11508
|
+
graphRef.resolveCallback(token, data, errors);
|
|
11509
|
+
};
|
|
11510
|
+
for (const card of cards) {
|
|
11511
|
+
if (card.type === "source") {
|
|
11512
|
+
handlers[card.id] = buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve);
|
|
11513
|
+
} else {
|
|
11514
|
+
handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve);
|
|
11515
|
+
}
|
|
11516
|
+
}
|
|
11517
|
+
const graph = createReactiveGraph(
|
|
11518
|
+
config,
|
|
11519
|
+
{
|
|
11520
|
+
...reactiveOptions,
|
|
11521
|
+
handlers
|
|
11522
|
+
},
|
|
11523
|
+
executionId
|
|
11524
|
+
);
|
|
11525
|
+
graphRef = graph;
|
|
11526
|
+
return { graph, config, handlers, cards: cardMap, sharedState };
|
|
11527
|
+
}
|
|
11528
|
+
function buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve) {
|
|
11529
|
+
if (sourceHandlers[card.id]) {
|
|
11530
|
+
const userHandler = sourceHandlers[card.id];
|
|
11531
|
+
return async (input) => {
|
|
11532
|
+
return userHandler(input);
|
|
11533
|
+
};
|
|
11534
|
+
}
|
|
11535
|
+
if (defaultSourceHandler) {
|
|
11536
|
+
const factoryHandler = defaultSourceHandler(card);
|
|
11537
|
+
return async (input) => {
|
|
11538
|
+
return factoryHandler(input);
|
|
11539
|
+
};
|
|
11540
|
+
}
|
|
11541
|
+
return async (input) => {
|
|
11542
|
+
const state = { ...card.state };
|
|
11543
|
+
sharedState.set(card.id, state);
|
|
11544
|
+
getResolve()(input.callbackToken, state);
|
|
11545
|
+
return "task-initiated";
|
|
11546
|
+
};
|
|
11547
|
+
}
|
|
11548
|
+
function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve) {
|
|
11549
|
+
if (cardHandlers[card.id]) {
|
|
11550
|
+
const userHandler = cardHandlers[card.id];
|
|
11551
|
+
return async (input) => {
|
|
11552
|
+
return userHandler(input);
|
|
11553
|
+
};
|
|
11554
|
+
}
|
|
11555
|
+
return async (input) => {
|
|
11556
|
+
const computeNode = {
|
|
11557
|
+
id: card.id,
|
|
11558
|
+
state: { ...card.state },
|
|
11559
|
+
compute: card.compute
|
|
11560
|
+
};
|
|
11561
|
+
const requires = card.data?.requires ?? [];
|
|
11562
|
+
for (const upstreamId of requires) {
|
|
11563
|
+
const upstreamState = sharedState.get(upstreamId);
|
|
11564
|
+
if (upstreamState) {
|
|
11565
|
+
computeNode.state[upstreamId] = upstreamState;
|
|
11566
|
+
}
|
|
11567
|
+
const upstreamCard = cardMap.get(upstreamId);
|
|
11568
|
+
if (upstreamCard?.data?.provides && upstreamState) {
|
|
11569
|
+
for (const [key, bindRef] of Object.entries(upstreamCard.data.provides)) {
|
|
11570
|
+
if (typeof bindRef === "string" && bindRef.startsWith("state.")) {
|
|
11571
|
+
const path = bindRef.slice(6);
|
|
11572
|
+
const value = deepGet2(upstreamState, path);
|
|
11573
|
+
if (value !== void 0) {
|
|
11574
|
+
computeNode.state[key] = value;
|
|
11575
|
+
}
|
|
11576
|
+
}
|
|
11577
|
+
}
|
|
11578
|
+
}
|
|
11579
|
+
}
|
|
11580
|
+
CardCompute.run(computeNode);
|
|
11581
|
+
const resultState = { ...computeNode.state };
|
|
11582
|
+
sharedState.set(card.id, resultState);
|
|
11583
|
+
getResolve()(input.callbackToken, resultState);
|
|
11584
|
+
return "task-initiated";
|
|
11585
|
+
};
|
|
11586
|
+
}
|
|
11587
|
+
function deepGet2(obj, path) {
|
|
11588
|
+
if (!path || !obj) return void 0;
|
|
11589
|
+
const parts = path.split(".");
|
|
11590
|
+
let cur = obj;
|
|
11591
|
+
for (const part of parts) {
|
|
11592
|
+
if (cur == null) return void 0;
|
|
11593
|
+
cur = cur[part];
|
|
11594
|
+
}
|
|
11595
|
+
return cur;
|
|
11596
|
+
}
|
|
11597
|
+
|
|
11598
|
+
// src/inference/core.ts
|
|
11599
|
+
var DEFAULT_THRESHOLD = 0.5;
|
|
11600
|
+
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.
|
|
11601
|
+
|
|
11602
|
+
For each task you analyze, provide a JSON response. Be conservative \u2014 only mark tasks as completed when the evidence strongly supports it.`;
|
|
11603
|
+
function buildInferencePrompt(live, options = {}) {
|
|
11604
|
+
const { scope, context, systemPrompt } = options;
|
|
11605
|
+
const graphTasks = getAllTasks(live.config);
|
|
11606
|
+
const { state } = live;
|
|
11607
|
+
const candidates = getAnalyzableCandidates(live, scope);
|
|
11608
|
+
if (candidates.length === 0) {
|
|
11609
|
+
return "";
|
|
11610
|
+
}
|
|
11611
|
+
const lines = [];
|
|
11612
|
+
lines.push(systemPrompt || DEFAULT_SYSTEM_PROMPT);
|
|
11613
|
+
lines.push("");
|
|
11614
|
+
lines.push("## Graph State");
|
|
11615
|
+
lines.push("");
|
|
11616
|
+
lines.push(`Available tokens: ${state.availableOutputs.length > 0 ? state.availableOutputs.join(", ") : "(none)"}`);
|
|
11617
|
+
lines.push("");
|
|
11618
|
+
const completedTasks = Object.entries(state.tasks).filter(([_, ts]) => ts.status === "completed").map(([name]) => name);
|
|
11619
|
+
if (completedTasks.length > 0) {
|
|
11620
|
+
lines.push(`Completed tasks: ${completedTasks.join(", ")}`);
|
|
11621
|
+
lines.push("");
|
|
11622
|
+
}
|
|
11623
|
+
lines.push("## Tasks to Analyze");
|
|
11624
|
+
lines.push("");
|
|
11625
|
+
for (const taskName of candidates) {
|
|
11626
|
+
const taskConfig = graphTasks[taskName];
|
|
11627
|
+
const taskState = state.tasks[taskName];
|
|
11628
|
+
lines.push(`### ${taskName}`);
|
|
11629
|
+
if (taskConfig.description) {
|
|
11630
|
+
lines.push(`Description: ${taskConfig.description}`);
|
|
11631
|
+
}
|
|
11632
|
+
const requires = getRequires(taskConfig);
|
|
11633
|
+
const provides = getProvides(taskConfig);
|
|
11634
|
+
if (requires.length > 0) lines.push(`Requires: ${requires.join(", ")}`);
|
|
11635
|
+
if (provides.length > 0) lines.push(`Provides: ${provides.join(", ")}`);
|
|
11636
|
+
lines.push(`Current status: ${taskState?.status || "not-started"}`);
|
|
11637
|
+
const hints = taskConfig.inference;
|
|
11638
|
+
if (hints) {
|
|
11639
|
+
if (hints.criteria) lines.push(`Completion criteria: ${hints.criteria}`);
|
|
11640
|
+
if (hints.keywords?.length) lines.push(`Keywords: ${hints.keywords.join(", ")}`);
|
|
11641
|
+
if (hints.suggestedChecks?.length) lines.push(`Suggested checks: ${hints.suggestedChecks.join("; ")}`);
|
|
11642
|
+
}
|
|
11643
|
+
lines.push("");
|
|
11644
|
+
}
|
|
11645
|
+
if (context) {
|
|
11646
|
+
lines.push("## Additional Context / Evidence");
|
|
11647
|
+
lines.push("");
|
|
11648
|
+
lines.push(context);
|
|
11649
|
+
lines.push("");
|
|
11650
|
+
}
|
|
11651
|
+
lines.push("## Response Format");
|
|
11652
|
+
lines.push("");
|
|
11653
|
+
lines.push("Respond with a JSON array of objects, one per task you have evidence for:");
|
|
11654
|
+
lines.push("```json");
|
|
11655
|
+
lines.push("[");
|
|
11656
|
+
lines.push(" {");
|
|
11657
|
+
lines.push(' "taskName": "task-name",');
|
|
11658
|
+
lines.push(' "confidence": 0.0 to 1.0,');
|
|
11659
|
+
lines.push(' "reasoning": "explanation of why you believe this task is complete or not"');
|
|
11660
|
+
lines.push(" }");
|
|
11661
|
+
lines.push("]");
|
|
11662
|
+
lines.push("```");
|
|
11663
|
+
lines.push("");
|
|
11664
|
+
lines.push("Rules:");
|
|
11665
|
+
lines.push('- Only include tasks from the "Tasks to Analyze" section');
|
|
11666
|
+
lines.push("- confidence 0.0 = no evidence of completion, 1.0 = certain it is complete");
|
|
11667
|
+
lines.push("- If you have no evidence for a task, omit it from the array");
|
|
11668
|
+
lines.push("- Be conservative \u2014 require clear evidence before high confidence");
|
|
11669
|
+
lines.push("- Respond ONLY with the JSON array, no additional text");
|
|
11670
|
+
return lines.join("\n");
|
|
11671
|
+
}
|
|
11672
|
+
async function inferCompletions(live, adapter, options = {}) {
|
|
11673
|
+
options.threshold ?? DEFAULT_THRESHOLD;
|
|
11674
|
+
const analyzedNodes = getAnalyzableCandidates(live, options.scope);
|
|
11675
|
+
if (analyzedNodes.length === 0) {
|
|
11676
|
+
return { suggestions: [], promptUsed: "", rawResponse: "", analyzedNodes: [] };
|
|
11677
|
+
}
|
|
11678
|
+
const prompt = buildInferencePrompt(live, options);
|
|
11679
|
+
const rawResponse = await adapter.analyze(prompt);
|
|
11680
|
+
const suggestions = parseInferenceResponse(rawResponse, analyzedNodes);
|
|
11681
|
+
return {
|
|
11682
|
+
suggestions,
|
|
11683
|
+
promptUsed: prompt,
|
|
11684
|
+
rawResponse,
|
|
11685
|
+
analyzedNodes
|
|
11686
|
+
};
|
|
11687
|
+
}
|
|
11688
|
+
function applyInferences(live, result, threshold = DEFAULT_THRESHOLD) {
|
|
11689
|
+
let current = live;
|
|
11690
|
+
for (const suggestion of result.suggestions) {
|
|
11691
|
+
if (suggestion.confidence < threshold) continue;
|
|
11692
|
+
const taskState = current.state.tasks[suggestion.taskName];
|
|
11693
|
+
if (!taskState) continue;
|
|
11694
|
+
if (taskState.status === "completed" || taskState.status === "running") continue;
|
|
11695
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11696
|
+
current = applyEvent(current, {
|
|
11697
|
+
type: "task-started",
|
|
11698
|
+
taskName: suggestion.taskName,
|
|
11699
|
+
timestamp: now
|
|
11700
|
+
});
|
|
11701
|
+
current = applyEvent(current, {
|
|
11702
|
+
type: "task-completed",
|
|
11703
|
+
taskName: suggestion.taskName,
|
|
11704
|
+
timestamp: now,
|
|
11705
|
+
result: "llm-inferred"
|
|
11706
|
+
});
|
|
11707
|
+
}
|
|
11708
|
+
return current;
|
|
11709
|
+
}
|
|
11710
|
+
async function inferAndApply(live, adapter, options = {}) {
|
|
11711
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
11712
|
+
const inference = await inferCompletions(live, adapter, options);
|
|
11713
|
+
const updated = applyInferences(live, inference, threshold);
|
|
11714
|
+
const applied = inference.suggestions.filter((s) => s.confidence >= threshold);
|
|
11715
|
+
const skipped = inference.suggestions.filter((s) => s.confidence < threshold);
|
|
11716
|
+
return {
|
|
11717
|
+
live: updated,
|
|
11718
|
+
inference,
|
|
11719
|
+
applied,
|
|
11720
|
+
skipped
|
|
11721
|
+
};
|
|
11722
|
+
}
|
|
11723
|
+
function getAnalyzableCandidates(live, scope) {
|
|
11724
|
+
const graphTasks = getAllTasks(live.config);
|
|
11725
|
+
const { state } = live;
|
|
11726
|
+
const candidates = [];
|
|
11727
|
+
for (const [name, config] of Object.entries(graphTasks)) {
|
|
11728
|
+
const taskState = state.tasks[name];
|
|
11729
|
+
if (taskState?.status === "completed" || taskState?.status === "running") continue;
|
|
11730
|
+
if (scope) {
|
|
11731
|
+
if (scope.includes(name)) candidates.push(name);
|
|
11732
|
+
} else {
|
|
11733
|
+
if (config.inference?.autoDetectable) candidates.push(name);
|
|
11734
|
+
}
|
|
11735
|
+
}
|
|
11736
|
+
return candidates;
|
|
11737
|
+
}
|
|
11738
|
+
function parseInferenceResponse(rawResponse, validNodes, _threshold) {
|
|
11739
|
+
const validSet = new Set(validNodes);
|
|
11740
|
+
try {
|
|
11741
|
+
const jsonStr = extractJson(rawResponse);
|
|
11742
|
+
if (!jsonStr) return [];
|
|
11743
|
+
const parsed = JSON.parse(jsonStr);
|
|
11744
|
+
if (!Array.isArray(parsed)) return [];
|
|
11745
|
+
const suggestions = [];
|
|
11746
|
+
for (const item of parsed) {
|
|
11747
|
+
if (!item || typeof item !== "object") continue;
|
|
11748
|
+
if (typeof item.taskName !== "string") continue;
|
|
11749
|
+
if (typeof item.confidence !== "number") continue;
|
|
11750
|
+
if (!validSet.has(item.taskName)) continue;
|
|
11751
|
+
const confidence = Math.max(0, Math.min(1, item.confidence));
|
|
11752
|
+
suggestions.push({
|
|
11753
|
+
taskName: item.taskName,
|
|
11754
|
+
confidence,
|
|
11755
|
+
reasoning: typeof item.reasoning === "string" ? item.reasoning : "",
|
|
11756
|
+
detectionMethod: "llm-inferred"
|
|
11757
|
+
});
|
|
11758
|
+
}
|
|
11759
|
+
return suggestions;
|
|
11760
|
+
} catch {
|
|
11761
|
+
return [];
|
|
11762
|
+
}
|
|
11763
|
+
}
|
|
11764
|
+
function extractJson(text) {
|
|
11765
|
+
if (!text || typeof text !== "string") return null;
|
|
11766
|
+
const trimmed = text.trim();
|
|
11767
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
11768
|
+
if (fenceMatch) return fenceMatch[1].trim();
|
|
11769
|
+
const firstBracket = trimmed.indexOf("[");
|
|
11770
|
+
const lastBracket = trimmed.lastIndexOf("]");
|
|
11771
|
+
if (firstBracket !== -1 && lastBracket > firstBracket) {
|
|
11772
|
+
return trimmed.slice(firstBracket, lastBracket + 1);
|
|
11773
|
+
}
|
|
11774
|
+
if (trimmed.startsWith("[")) return trimmed;
|
|
11775
|
+
return null;
|
|
11776
|
+
}
|
|
11777
|
+
function createCliAdapter(opts) {
|
|
11778
|
+
const timeout = opts.timeout ?? 6e4;
|
|
11779
|
+
return {
|
|
11780
|
+
analyze: (prompt) => {
|
|
11781
|
+
return new Promise((resolve2, reject) => {
|
|
11782
|
+
const args = opts.args(prompt);
|
|
11783
|
+
const child = child_process.execFile(
|
|
11784
|
+
opts.command,
|
|
11785
|
+
opts.stdin ? opts.args("") : args,
|
|
11786
|
+
{
|
|
11787
|
+
timeout,
|
|
11788
|
+
cwd: opts.cwd,
|
|
11789
|
+
env: opts.env ? { ...process.env, ...opts.env } : void 0,
|
|
11790
|
+
maxBuffer: 10 * 1024 * 1024
|
|
11791
|
+
// 10MB
|
|
11792
|
+
},
|
|
11793
|
+
(error, stdout, stderr) => {
|
|
11794
|
+
if (error) {
|
|
11795
|
+
reject(new Error(
|
|
11796
|
+
`CLI adapter failed: ${opts.command} exited with ${error.code ?? "error"}` + (stderr ? `
|
|
11797
|
+
stderr: ${stderr.slice(0, 500)}` : "") + `
|
|
11798
|
+
${error.message}`
|
|
11799
|
+
));
|
|
11800
|
+
} else {
|
|
11801
|
+
resolve2(stdout);
|
|
11802
|
+
}
|
|
11803
|
+
}
|
|
11804
|
+
);
|
|
11805
|
+
if (opts.stdin && child.stdin) {
|
|
11806
|
+
child.stdin.write(prompt);
|
|
11807
|
+
child.stdin.end();
|
|
11808
|
+
}
|
|
11809
|
+
});
|
|
11810
|
+
}
|
|
11811
|
+
};
|
|
11812
|
+
}
|
|
11813
|
+
function createHttpAdapter(opts) {
|
|
11814
|
+
const timeout = opts.timeout ?? 6e4;
|
|
11815
|
+
return {
|
|
11816
|
+
analyze: async (prompt) => {
|
|
11817
|
+
const body = opts.buildBody ? opts.buildBody(prompt) : { prompt };
|
|
11818
|
+
const controller = new AbortController();
|
|
11819
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
11820
|
+
try {
|
|
11821
|
+
const response = await fetch(opts.url, {
|
|
11822
|
+
method: "POST",
|
|
11823
|
+
headers: {
|
|
11824
|
+
"Content-Type": "application/json",
|
|
11825
|
+
...opts.headers ?? {}
|
|
11826
|
+
},
|
|
11827
|
+
body: JSON.stringify(body),
|
|
11828
|
+
signal: controller.signal
|
|
11829
|
+
});
|
|
11830
|
+
if (!response.ok) {
|
|
11831
|
+
const text = await response.text().catch(() => "");
|
|
11832
|
+
throw new Error(`HTTP ${response.status}: ${text.slice(0, 500)}`);
|
|
11833
|
+
}
|
|
11834
|
+
const json = await response.json();
|
|
11835
|
+
if (opts.extractResponse) {
|
|
11836
|
+
return opts.extractResponse(json);
|
|
11837
|
+
}
|
|
11838
|
+
if (typeof json.response === "string") return json.response;
|
|
11839
|
+
if (typeof json.text === "string") return json.text;
|
|
11840
|
+
if (typeof json.content === "string") return json.content;
|
|
11841
|
+
return JSON.stringify(json);
|
|
11842
|
+
} finally {
|
|
11843
|
+
clearTimeout(timer);
|
|
11844
|
+
}
|
|
11845
|
+
}
|
|
11846
|
+
};
|
|
11847
|
+
}
|
|
11848
|
+
|
|
11328
11849
|
exports.COMPLETION_STRATEGIES = COMPLETION_STRATEGIES;
|
|
11329
11850
|
exports.CONFLICT_STRATEGIES = CONFLICT_STRATEGIES;
|
|
11330
11851
|
exports.CardCompute = CardCompute;
|
|
@@ -11354,15 +11875,21 @@ exports.buildInferencePrompt = buildInferencePrompt;
|
|
|
11354
11875
|
exports.checkCircuitBreaker = checkCircuitBreaker;
|
|
11355
11876
|
exports.computeAvailableOutputs = computeAvailableOutputs;
|
|
11356
11877
|
exports.computeStepInput = computeStepInput;
|
|
11878
|
+
exports.createCallbackHandler = createCallbackHandler;
|
|
11357
11879
|
exports.createCliAdapter = createCliAdapter;
|
|
11358
|
-
exports.
|
|
11880
|
+
exports.createDefaultGraphEngineStore = createDefaultGraphEngineStore;
|
|
11359
11881
|
exports.createEngine = createStepMachine;
|
|
11882
|
+
exports.createFireAndForgetHandler = createFireAndForgetHandler;
|
|
11360
11883
|
exports.createHttpAdapter = createHttpAdapter;
|
|
11361
11884
|
exports.createInitialExecutionState = createInitialExecutionState;
|
|
11362
11885
|
exports.createInitialState = createInitialState;
|
|
11363
11886
|
exports.createLiveGraph = createLiveGraph;
|
|
11887
|
+
exports.createNoopHandler = createNoopHandler;
|
|
11364
11888
|
exports.createReactiveGraph = createReactiveGraph;
|
|
11889
|
+
exports.createScriptHandler = createScriptHandler;
|
|
11890
|
+
exports.createShellHandler = createShellHandler;
|
|
11365
11891
|
exports.createStepMachine = createStepMachine;
|
|
11892
|
+
exports.createWebhookHandler = createWebhookHandler;
|
|
11366
11893
|
exports.detectStuckState = detectStuckState;
|
|
11367
11894
|
exports.disableNode = disableNode;
|
|
11368
11895
|
exports.drainTokens = drainTokens;
|
|
@@ -11394,8 +11921,10 @@ exports.isNonActiveTask = isNonActiveTask;
|
|
|
11394
11921
|
exports.isRerunnable = isRerunnable;
|
|
11395
11922
|
exports.isTaskCompleted = isTaskCompleted;
|
|
11396
11923
|
exports.isTaskRunning = isTaskRunning;
|
|
11924
|
+
exports.liveCardsToReactiveGraph = liveCardsToReactiveGraph;
|
|
11397
11925
|
exports.loadGraphConfig = loadGraphConfig;
|
|
11398
11926
|
exports.loadStepFlow = loadStepFlow;
|
|
11927
|
+
exports.mutateGraph = mutateGraph;
|
|
11399
11928
|
exports.next = next;
|
|
11400
11929
|
exports.planExecution = planExecution;
|
|
11401
11930
|
exports.removeNode = removeNode;
|
|
@@ -11412,6 +11941,8 @@ exports.validateGraph = validateGraph;
|
|
|
11412
11941
|
exports.validateGraphConfig = validateGraphConfig;
|
|
11413
11942
|
exports.validateGraphSchema = validateGraphSchema;
|
|
11414
11943
|
exports.validateLiveCardSchema = validateLiveCardSchema;
|
|
11944
|
+
exports.validateLiveGraph = validateLiveGraph;
|
|
11945
|
+
exports.validateReactiveGraph = validateReactiveGraph;
|
|
11415
11946
|
exports.validateStepFlowConfig = validateStepFlowConfig;
|
|
11416
11947
|
//# sourceMappingURL=index.cjs.map
|
|
11417
11948
|
//# sourceMappingURL=index.cjs.map
|