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.
Files changed (59) hide show
  1. package/README.md +44 -23
  2. package/dist/{constants-B_ftYTTE.d.ts → constants-B2zqu10b.d.ts} +7 -57
  3. package/dist/{constants-CiyHX8L-.d.cts → constants-DJZU1pwJ.d.cts} +7 -57
  4. package/dist/continuous-event-graph/index.cjs +1161 -182
  5. package/dist/continuous-event-graph/index.cjs.map +1 -1
  6. package/dist/continuous-event-graph/index.d.cts +567 -48
  7. package/dist/continuous-event-graph/index.d.ts +567 -48
  8. package/dist/continuous-event-graph/index.js +1151 -183
  9. package/dist/continuous-event-graph/index.js.map +1 -1
  10. package/dist/event-graph/index.cjs +35 -11
  11. package/dist/event-graph/index.cjs.map +1 -1
  12. package/dist/event-graph/index.d.cts +14 -5
  13. package/dist/event-graph/index.d.ts +14 -5
  14. package/dist/event-graph/index.js +34 -11
  15. package/dist/event-graph/index.js.map +1 -1
  16. package/dist/index.cjs +945 -414
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +5 -4
  19. package/dist/index.d.ts +5 -4
  20. package/dist/index.js +936 -415
  21. package/dist/index.js.map +1 -1
  22. package/dist/inference/index.cjs +31 -7
  23. package/dist/inference/index.cjs.map +1 -1
  24. package/dist/inference/index.d.cts +2 -2
  25. package/dist/inference/index.d.ts +2 -2
  26. package/dist/inference/index.js +31 -7
  27. package/dist/inference/index.js.map +1 -1
  28. package/dist/{types-CxJg9Jrt.d.cts → types-BwvgvlOO.d.cts} +2 -2
  29. package/dist/{types-BuEo3wVG.d.ts → types-ClRA8hzC.d.ts} +2 -2
  30. package/dist/{types-BpWrH1sf.d.cts → types-DEj7OakX.d.cts} +14 -4
  31. package/dist/{types-BpWrH1sf.d.ts → types-DEj7OakX.d.ts} +14 -4
  32. package/dist/validate-DEZ2Ymdb.d.ts +53 -0
  33. package/dist/validate-DqKTZg_o.d.cts +53 -0
  34. package/examples/batch/batch-step-machine.ts +121 -0
  35. package/examples/browser/index.html +367 -0
  36. package/examples/continuous-event-graph/live-cards-board.ts +215 -0
  37. package/examples/continuous-event-graph/live-portfolio-dashboard.ts +555 -0
  38. package/examples/continuous-event-graph/portfolio-tracker.ts +287 -0
  39. package/examples/continuous-event-graph/reactive-monitoring.ts +265 -0
  40. package/examples/continuous-event-graph/reactive-pipeline.ts +168 -0
  41. package/examples/continuous-event-graph/soc-incident-board.ts +287 -0
  42. package/examples/continuous-event-graph/stock-dashboard.ts +229 -0
  43. package/examples/event-graph/ci-cd-pipeline.ts +243 -0
  44. package/examples/event-graph/executor-diamond.ts +165 -0
  45. package/examples/event-graph/executor-pipeline.ts +161 -0
  46. package/examples/event-graph/research-pipeline.ts +137 -0
  47. package/examples/flows/ai-conversation.yaml +116 -0
  48. package/examples/flows/order-processing.yaml +143 -0
  49. package/examples/flows/simple-greeting.yaml +54 -0
  50. package/examples/graph-of-graphs/multi-stage-etl.ts +307 -0
  51. package/examples/graph-of-graphs/url-processing-pipeline.ts +254 -0
  52. package/examples/inference/azure-deployment.ts +149 -0
  53. package/examples/inference/copilot-cli.ts +138 -0
  54. package/examples/inference/data-pipeline.ts +145 -0
  55. package/examples/inference/pluggable-adapters.ts +254 -0
  56. package/examples/ingest.js +733 -0
  57. package/examples/node/ai-conversation.ts +195 -0
  58. package/examples/node/simple-greeting.ts +101 -0
  59. 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 createDefaultTaskState() {
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] = createDefaultTaskState();
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] ?? createDefaultTaskState2();
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] ?? createDefaultTaskState2();
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] ?? createDefaultTaskState2();
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] ?? createDefaultTaskState2();
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 createDefaultTaskState2() {
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]: createDefaultTaskState()
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] = createDefaultTaskState3();
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]: createDefaultTaskState3() },
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]: createDefaultTaskState3()
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] ?? createDefaultTaskState3();
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 createDefaultTaskState3() {
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
- // src/continuous-event-graph/reactive.ts
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 (onDrain && events.length > 0) {
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
- for (const [taskName, entry] of dispatched) {
10104
- if (entry.status === "retry-queued") {
10105
- dispatchTask(taskName);
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 handler = handlers.get(taskName);
10111
- if (!handler) {
10112
- journal.append({
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
- if (defaultTimeoutMs > 0) {
10151
- const timer = setTimeout(() => {
10152
- if (disposed) return;
10153
- const entry = dispatched.get(taskName);
10154
- if (entry?.status === "initiated") {
10155
- dispatched.set(taskName, {
10156
- ...entry,
10157
- status: "timed-out"
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
- drainQueued = true;
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
- live = applyEvent(live, event);
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
- if (events.length === 0) return;
10261
- live = applyEvents(live, events);
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, handler) {
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
- dispatched.delete(name);
10275
- clearTimeout(timeoutTimers.get(name));
10276
- timeoutTimers.delete(name);
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/inference/core.ts
10298
- var DEFAULT_THRESHOLD = 0.5;
10299
- 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.
10300
-
10301
- For each task you analyze, provide a JSON response. Be conservative \u2014 only mark tasks as completed when the evidence strongly supports it.`;
10302
- function buildInferencePrompt(live, options = {}) {
10303
- const { scope, context, systemPrompt } = options;
10304
- const graphTasks = getAllTasks(live.config);
10305
- const { state } = live;
10306
- const candidates = getAnalyzableCandidates(live, scope);
10307
- if (candidates.length === 0) {
10308
- return "";
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 lines = [];
10311
- lines.push(systemPrompt || DEFAULT_SYSTEM_PROMPT);
10312
- lines.push("");
10313
- lines.push("## Graph State");
10314
- lines.push("");
10315
- lines.push(`Available tokens: ${state.availableOutputs.length > 0 ? state.availableOutputs.join(", ") : "(none)"}`);
10316
- lines.push("");
10317
- const completedTasks = Object.entries(state.tasks).filter(([_, ts]) => ts.status === "completed").map(([name]) => name);
10318
- if (completedTasks.length > 0) {
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
- lines.push("## Tasks to Analyze");
10323
- lines.push("");
10324
- for (const taskName of candidates) {
10325
- const taskConfig = graphTasks[taskName];
10326
- const taskState = state.tasks[taskName];
10327
- lines.push(`### ${taskName}`);
10328
- if (taskConfig.description) {
10329
- lines.push(`Description: ${taskConfig.description}`);
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
- const requires = getRequires(taskConfig);
10332
- const provides = getProvides(taskConfig);
10333
- if (requires.length > 0) lines.push(`Requires: ${requires.join(", ")}`);
10334
- if (provides.length > 0) lines.push(`Provides: ${provides.join(", ")}`);
10335
- lines.push(`Current status: ${taskState?.status || "not-started"}`);
10336
- const hints = taskConfig.inference;
10337
- if (hints) {
10338
- if (hints.criteria) lines.push(`Completion criteria: ${hints.criteria}`);
10339
- if (hints.keywords?.length) lines.push(`Keywords: ${hints.keywords.join(", ")}`);
10340
- if (hints.suggestedChecks?.length) lines.push(`Suggested checks: ${hints.suggestedChecks.join("; ")}`);
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
- if (context) {
10345
- lines.push("## Additional Context / Evidence");
10346
- lines.push("");
10347
- lines.push(context);
10348
- lines.push("");
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
- lines.push("## Response Format");
10351
- lines.push("");
10352
- lines.push("Respond with a JSON array of objects, one per task you have evidence for:");
10353
- lines.push("```json");
10354
- lines.push("[");
10355
- lines.push(" {");
10356
- lines.push(' "taskName": "task-name",');
10357
- lines.push(' "confidence": 0.0 to 1.0,');
10358
- lines.push(' "reasoning": "explanation of why you believe this task is complete or not"');
10359
- lines.push(" }");
10360
- lines.push("]");
10361
- lines.push("```");
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 prompt = buildInferencePrompt(live, options);
10378
- const rawResponse = await adapter.analyze(prompt);
10379
- const suggestions = parseInferenceResponse(rawResponse, analyzedNodes);
10380
- return {
10381
- suggestions,
10382
- promptUsed: prompt,
10383
- rawResponse,
10384
- analyzedNodes
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
- return current;
10408
- }
10409
- async function inferAndApply(live, adapter, options = {}) {
10410
- const threshold = options.threshold ?? DEFAULT_THRESHOLD;
10411
- const inference = await inferCompletions(live, adapter, options);
10412
- const updated = applyInferences(live, inference, threshold);
10413
- const applied = inference.suggestions.filter((s) => s.confidence >= threshold);
10414
- const skipped = inference.suggestions.filter((s) => s.confidence < threshold);
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
- return candidates;
10436
- }
10437
- function parseInferenceResponse(rawResponse, validNodes, _threshold) {
10438
- const validSet = new Set(validNodes);
10439
- try {
10440
- const jsonStr = extractJson(rawResponse);
10441
- if (!jsonStr) return [];
10442
- const parsed = JSON.parse(jsonStr);
10443
- if (!Array.isArray(parsed)) return [];
10444
- const suggestions = [];
10445
- for (const item of parsed) {
10446
- if (!item || typeof item !== "object") continue;
10447
- if (typeof item.taskName !== "string") continue;
10448
- if (typeof item.confidence !== "number") continue;
10449
- if (!validSet.has(item.taskName)) continue;
10450
- const confidence = Math.max(0, Math.min(1, item.confidence));
10451
- suggestions.push({
10452
- taskName: item.taskName,
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 extractJson(text) {
10464
- if (!text || typeof text !== "string") return null;
10465
- const trimmed = text.trim();
10466
- const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
10467
- if (fenceMatch) return fenceMatch[1].trim();
10468
- const firstBracket = trimmed.indexOf("[");
10469
- const lastBracket = trimmed.lastIndexOf("]");
10470
- if (firstBracket !== -1 && lastBracket > firstBracket) {
10471
- return trimmed.slice(firstBracket, lastBracket + 1);
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
- if (trimmed.startsWith("[")) return trimmed;
10474
- return null;
10475
- }
10476
- function createCliAdapter(opts) {
10477
- const timeout = opts.timeout ?? 6e4;
10478
- return {
10479
- analyze: (prompt) => {
10480
- return new Promise((resolve2, reject) => {
10481
- const args = opts.args(prompt);
10482
- const child = child_process.execFile(
10483
- opts.command,
10484
- opts.stdin ? opts.args("") : args,
10485
- {
10486
- timeout,
10487
- cwd: opts.cwd,
10488
- env: opts.env ? { ...process.env, ...opts.env } : void 0,
10489
- maxBuffer: 10 * 1024 * 1024
10490
- // 10MB
10491
- },
10492
- (error, stdout, stderr) => {
10493
- if (error) {
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 createHttpAdapter(opts) {
10513
- const timeout = opts.timeout ?? 6e4;
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
- analyze: async (prompt) => {
10516
- const body = opts.buildBody ? opts.buildBody(prompt) : { prompt };
10517
- const controller = new AbortController();
10518
- const timer = setTimeout(() => controller.abort(), timeout);
10519
- try {
10520
- const response = await fetch(opts.url, {
10521
- method: "POST",
10522
- headers: {
10523
- "Content-Type": "application/json",
10524
- ...opts.headers ?? {}
10525
- },
10526
- body: JSON.stringify(body),
10527
- signal: controller.signal
10528
- });
10529
- if (!response.ok) {
10530
- const text = await response.text().catch(() => "");
10531
- throw new Error(`HTTP ${response.status}: ${text.slice(0, 500)}`);
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 json = await response.json();
10534
- if (opts.extractResponse) {
10535
- return opts.extractResponse(json);
10583
+ const data = {};
10584
+ if (captureOutput) {
10585
+ data.stdout = stdout;
10586
+ data.stderr = stderr;
10587
+ data.exitCode = exitCode;
10536
10588
  }
10537
- if (typeof json.response === "string") return json.response;
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.createDefaultTaskState = createDefaultTaskState;
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