yaml-flow 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1735,6 +1735,213 @@ ${serialized}`;
1735
1735
  return String(obj);
1736
1736
  }
1737
1737
 
1738
+ // src/event-graph/validate.ts
1739
+ function buildProducerMap2(tasks) {
1740
+ const map = {};
1741
+ for (const [name, config] of Object.entries(tasks)) {
1742
+ for (const token of getProvides(config)) {
1743
+ if (!map[token]) map[token] = [];
1744
+ map[token].push(name);
1745
+ }
1746
+ if (config.on) {
1747
+ for (const tokens of Object.values(config.on)) {
1748
+ for (const token of tokens) {
1749
+ if (!map[token]) map[token] = [];
1750
+ if (!map[token].includes(name)) map[token].push(name);
1751
+ }
1752
+ }
1753
+ }
1754
+ if (config.on_failure) {
1755
+ for (const token of config.on_failure) {
1756
+ if (!map[token]) map[token] = [];
1757
+ if (!map[token].includes(name)) map[token].push(name);
1758
+ }
1759
+ }
1760
+ }
1761
+ return map;
1762
+ }
1763
+ function buildTaskDeps(tasks, producerMap) {
1764
+ const deps = {};
1765
+ for (const [name, config] of Object.entries(tasks)) {
1766
+ deps[name] = /* @__PURE__ */ new Set();
1767
+ for (const token of getRequires(config)) {
1768
+ for (const producer of producerMap[token] || []) {
1769
+ if (producer !== name) deps[name].add(producer);
1770
+ }
1771
+ }
1772
+ }
1773
+ return deps;
1774
+ }
1775
+ function detectCycles(taskNames, deps) {
1776
+ const WHITE = 0, GRAY = 1, BLACK = 2;
1777
+ const color = {};
1778
+ const parent = {};
1779
+ const cycles = [];
1780
+ for (const name of taskNames) {
1781
+ color[name] = WHITE;
1782
+ parent[name] = null;
1783
+ }
1784
+ function dfs(node) {
1785
+ color[node] = GRAY;
1786
+ for (const dep of deps[node] || []) {
1787
+ if (color[dep] === GRAY) {
1788
+ const cycle = [dep];
1789
+ let cur = node;
1790
+ while (cur !== dep) {
1791
+ cycle.push(cur);
1792
+ cur = parent[cur];
1793
+ }
1794
+ cycle.push(dep);
1795
+ cycle.reverse();
1796
+ cycles.push(cycle);
1797
+ } else if (color[dep] === WHITE) {
1798
+ parent[dep] = node;
1799
+ dfs(dep);
1800
+ }
1801
+ }
1802
+ color[node] = BLACK;
1803
+ }
1804
+ for (const name of taskNames) {
1805
+ if (color[name] === WHITE) {
1806
+ dfs(name);
1807
+ }
1808
+ }
1809
+ return cycles;
1810
+ }
1811
+ function validateGraph(graph) {
1812
+ const issues = [];
1813
+ const tasks = getAllTasks(graph);
1814
+ const taskNames = Object.keys(tasks);
1815
+ if (taskNames.length === 0) {
1816
+ issues.push({
1817
+ severity: "error",
1818
+ code: "EMPTY_GRAPH",
1819
+ message: "Graph has no tasks"
1820
+ });
1821
+ return buildResult(issues);
1822
+ }
1823
+ const producerMap = buildProducerMap2(tasks);
1824
+ const deps = buildTaskDeps(tasks, producerMap);
1825
+ for (const [name, config] of Object.entries(tasks)) {
1826
+ for (const token of getRequires(config)) {
1827
+ if (!producerMap[token]) {
1828
+ issues.push({
1829
+ severity: "error",
1830
+ code: "DANGLING_REQUIRES",
1831
+ message: `Task "${name}" requires token "${token}" but no task produces it`,
1832
+ tasks: [name],
1833
+ tokens: [token]
1834
+ });
1835
+ }
1836
+ }
1837
+ }
1838
+ const cycles = detectCycles(taskNames, deps);
1839
+ for (const cycle of cycles) {
1840
+ issues.push({
1841
+ severity: "error",
1842
+ code: "CIRCULAR_DEPENDENCY",
1843
+ message: `Circular dependency: ${cycle.join(" \u2192 ")}`,
1844
+ tasks: cycle.filter((_t, i) => i < cycle.length - 1)
1845
+ // dedupe last = first
1846
+ });
1847
+ }
1848
+ for (const [name, config] of Object.entries(tasks)) {
1849
+ const req = getRequires(config);
1850
+ const prov = getProvides(config);
1851
+ const self = req.filter((token) => prov.includes(token));
1852
+ if (self.length > 0) {
1853
+ issues.push({
1854
+ severity: "error",
1855
+ code: "SELF_DEPENDENCY",
1856
+ message: `Task "${name}" requires tokens it provides itself: [${self.join(", ")}]`,
1857
+ tasks: [name],
1858
+ tokens: self
1859
+ });
1860
+ }
1861
+ }
1862
+ for (const [token, producers] of Object.entries(producerMap)) {
1863
+ if (producers.length > 1) {
1864
+ issues.push({
1865
+ severity: "warning",
1866
+ code: "PROVIDE_CONFLICT",
1867
+ message: `Token "${token}" is produced by multiple tasks: [${producers.join(", ")}]. This requires a conflict strategy.`,
1868
+ tasks: producers,
1869
+ tokens: [token]
1870
+ });
1871
+ }
1872
+ }
1873
+ if (graph.settings.completion === "goal-reached" && graph.settings.goal) {
1874
+ for (const goalToken of graph.settings.goal) {
1875
+ if (!producerMap[goalToken]) {
1876
+ issues.push({
1877
+ severity: "error",
1878
+ code: "UNREACHABLE_GOAL",
1879
+ message: `Goal token "${goalToken}" cannot be produced by any task`,
1880
+ tokens: [goalToken]
1881
+ });
1882
+ }
1883
+ }
1884
+ }
1885
+ if (taskNames.length > 1) {
1886
+ for (const [name, config] of Object.entries(tasks)) {
1887
+ const prov = getProvides(config);
1888
+ const onProv = config.on ? Object.values(config.on).flat() : [];
1889
+ const failProv = config.on_failure || [];
1890
+ if (prov.length === 0 && onProv.length === 0 && failProv.length === 0) {
1891
+ issues.push({
1892
+ severity: "warning",
1893
+ code: "DEAD_END_TASK",
1894
+ message: `Task "${name}" has no provides \u2014 it cannot unblock any downstream task`,
1895
+ tasks: [name]
1896
+ });
1897
+ }
1898
+ }
1899
+ }
1900
+ const allRequired = /* @__PURE__ */ new Set();
1901
+ for (const config of Object.values(tasks)) {
1902
+ for (const token of getRequires(config)) {
1903
+ allRequired.add(token);
1904
+ }
1905
+ }
1906
+ for (const [name, config] of Object.entries(tasks)) {
1907
+ const req = getRequires(config);
1908
+ const prov = getProvides(config);
1909
+ const onProv = config.on ? Object.values(config.on).flat() : [];
1910
+ const allProv = [...prov, ...onProv];
1911
+ const isEntryPoint = req.length === 0;
1912
+ const hasDownstream = allProv.some((token) => allRequired.has(token));
1913
+ if (isEntryPoint && !hasDownstream && taskNames.length > 1) {
1914
+ const isGoalRelevant = graph.settings.completion === "goal-reached" && graph.settings.goal?.some((g) => allProv.includes(g));
1915
+ if (!isGoalRelevant) {
1916
+ issues.push({
1917
+ severity: "info",
1918
+ code: "ISOLATED_TASK",
1919
+ message: `Task "${name}" is disconnected \u2014 it has no requires and nothing depends on its provides`,
1920
+ tasks: [name]
1921
+ });
1922
+ }
1923
+ }
1924
+ }
1925
+ if (graph.settings.completion === "goal-reached" && !graph.settings.goal) {
1926
+ issues.push({
1927
+ severity: "error",
1928
+ code: "MISSING_GOAL",
1929
+ message: 'Completion strategy is "goal-reached" but no goal tokens are defined'
1930
+ });
1931
+ }
1932
+ return buildResult(issues);
1933
+ }
1934
+ function buildResult(issues) {
1935
+ const errors = issues.filter((i) => i.severity === "error");
1936
+ const warnings = issues.filter((i) => i.severity === "warning");
1937
+ return {
1938
+ valid: errors.length === 0,
1939
+ issues,
1940
+ errors,
1941
+ warnings
1942
+ };
1943
+ }
1944
+
1738
1945
  // src/stores/localStorage.ts
1739
1946
  var LocalStorageStore = class {
1740
1947
  prefix;
@@ -2095,6 +2302,733 @@ function resolveConfigTemplates(config) {
2095
2302
  return result;
2096
2303
  }
2097
2304
 
2305
+ // src/continuous-event-graph/core.ts
2306
+ function createLiveGraph(config, executionId) {
2307
+ const id = executionId ?? `live-${Date.now()}`;
2308
+ const tasks = {};
2309
+ for (const taskName of Object.keys(config.tasks)) {
2310
+ tasks[taskName] = createDefaultTaskState3();
2311
+ }
2312
+ const state = {
2313
+ status: "running",
2314
+ tasks,
2315
+ availableOutputs: [],
2316
+ stuckDetection: { is_stuck: false, stuck_description: null, outputs_unresolvable: [], tasks_blocked: [] },
2317
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2318
+ executionId: id,
2319
+ executionConfig: {
2320
+ executionMode: config.settings.execution_mode ?? "eligibility-mode",
2321
+ conflictStrategy: config.settings.conflict_strategy ?? "alphabetical",
2322
+ completionStrategy: config.settings.completion
2323
+ }
2324
+ };
2325
+ return { config, state };
2326
+ }
2327
+ function applyEvent(live, event) {
2328
+ const { config, state } = live;
2329
+ if ("executionId" in event && event.executionId && event.executionId !== state.executionId) {
2330
+ return live;
2331
+ }
2332
+ let newState;
2333
+ switch (event.type) {
2334
+ case "task-started":
2335
+ newState = applyTaskStart(state, event.taskName);
2336
+ break;
2337
+ case "task-completed":
2338
+ newState = applyTaskCompletion(state, config, event.taskName, event.result);
2339
+ break;
2340
+ case "task-failed":
2341
+ newState = applyTaskFailure(state, config, event.taskName, event.error);
2342
+ break;
2343
+ case "task-progress":
2344
+ newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
2345
+ break;
2346
+ case "inject-tokens":
2347
+ newState = {
2348
+ ...state,
2349
+ availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
2350
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2351
+ };
2352
+ break;
2353
+ case "agent-action":
2354
+ newState = applyAgentAction2(state, event.action);
2355
+ break;
2356
+ default:
2357
+ return live;
2358
+ }
2359
+ return { config, state: newState };
2360
+ }
2361
+ function addNode(live, name, taskConfig) {
2362
+ if (live.config.tasks[name]) return live;
2363
+ return {
2364
+ config: {
2365
+ ...live.config,
2366
+ tasks: { ...live.config.tasks, [name]: taskConfig }
2367
+ },
2368
+ state: {
2369
+ ...live.state,
2370
+ tasks: { ...live.state.tasks, [name]: createDefaultTaskState3() },
2371
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2372
+ }
2373
+ };
2374
+ }
2375
+ function removeNode(live, name) {
2376
+ if (!live.config.tasks[name]) return live;
2377
+ const { [name]: _removedConfig, ...remainingTasks } = live.config.tasks;
2378
+ const { [name]: _removedState, ...remainingStates } = live.state.tasks;
2379
+ return {
2380
+ config: {
2381
+ ...live.config,
2382
+ tasks: remainingTasks
2383
+ },
2384
+ state: {
2385
+ ...live.state,
2386
+ tasks: remainingStates,
2387
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2388
+ }
2389
+ };
2390
+ }
2391
+ function addRequires(live, nodeName, tokens) {
2392
+ const task = live.config.tasks[nodeName];
2393
+ if (!task) return live;
2394
+ const current = getRequires(task);
2395
+ const toAdd = tokens.filter((t) => !current.includes(t));
2396
+ if (toAdd.length === 0) return live;
2397
+ return {
2398
+ config: {
2399
+ ...live.config,
2400
+ tasks: {
2401
+ ...live.config.tasks,
2402
+ [nodeName]: { ...task, requires: [...current, ...toAdd] }
2403
+ }
2404
+ },
2405
+ state: live.state
2406
+ };
2407
+ }
2408
+ function removeRequires(live, nodeName, tokens) {
2409
+ const task = live.config.tasks[nodeName];
2410
+ if (!task) return live;
2411
+ const current = getRequires(task);
2412
+ const remaining = current.filter((t) => !tokens.includes(t));
2413
+ if (remaining.length === current.length) return live;
2414
+ return {
2415
+ config: {
2416
+ ...live.config,
2417
+ tasks: {
2418
+ ...live.config.tasks,
2419
+ [nodeName]: { ...task, requires: remaining }
2420
+ }
2421
+ },
2422
+ state: live.state
2423
+ };
2424
+ }
2425
+ function addProvides(live, nodeName, tokens) {
2426
+ const task = live.config.tasks[nodeName];
2427
+ if (!task) return live;
2428
+ const current = getProvides(task);
2429
+ const toAdd = tokens.filter((t) => !current.includes(t));
2430
+ if (toAdd.length === 0) return live;
2431
+ return {
2432
+ config: {
2433
+ ...live.config,
2434
+ tasks: {
2435
+ ...live.config.tasks,
2436
+ [nodeName]: { ...task, provides: [...current, ...toAdd] }
2437
+ }
2438
+ },
2439
+ state: live.state
2440
+ };
2441
+ }
2442
+ function removeProvides(live, nodeName, tokens) {
2443
+ const task = live.config.tasks[nodeName];
2444
+ if (!task) return live;
2445
+ const current = getProvides(task);
2446
+ const remaining = current.filter((t) => !tokens.includes(t));
2447
+ if (remaining.length === current.length) return live;
2448
+ return {
2449
+ config: {
2450
+ ...live.config,
2451
+ tasks: {
2452
+ ...live.config.tasks,
2453
+ [nodeName]: { ...task, provides: remaining }
2454
+ }
2455
+ },
2456
+ state: live.state
2457
+ };
2458
+ }
2459
+ function injectTokens(live, tokens) {
2460
+ return applyEvent(live, {
2461
+ type: "inject-tokens",
2462
+ tokens,
2463
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2464
+ });
2465
+ }
2466
+ function drainTokens(live, tokens) {
2467
+ const toRemove = new Set(tokens);
2468
+ const remaining = live.state.availableOutputs.filter((t) => !toRemove.has(t));
2469
+ if (remaining.length === live.state.availableOutputs.length) return live;
2470
+ return {
2471
+ config: live.config,
2472
+ state: {
2473
+ ...live.state,
2474
+ availableOutputs: remaining,
2475
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2476
+ }
2477
+ };
2478
+ }
2479
+ function resetNode(live, name) {
2480
+ if (!live.config.tasks[name] || !live.state.tasks[name]) return live;
2481
+ return {
2482
+ config: live.config,
2483
+ state: {
2484
+ ...live.state,
2485
+ tasks: {
2486
+ ...live.state.tasks,
2487
+ [name]: createDefaultTaskState3()
2488
+ },
2489
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2490
+ }
2491
+ };
2492
+ }
2493
+ function disableNode(live, name) {
2494
+ const taskState = live.state.tasks[name];
2495
+ if (!taskState || taskState.status === "inactivated") return live;
2496
+ return {
2497
+ config: live.config,
2498
+ state: {
2499
+ ...live.state,
2500
+ tasks: {
2501
+ ...live.state.tasks,
2502
+ [name]: { ...taskState, status: "inactivated", lastUpdated: (/* @__PURE__ */ new Date()).toISOString() }
2503
+ },
2504
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2505
+ }
2506
+ };
2507
+ }
2508
+ function enableNode(live, name) {
2509
+ const taskState = live.state.tasks[name];
2510
+ if (!taskState || taskState.status !== "inactivated") return live;
2511
+ return {
2512
+ config: live.config,
2513
+ state: {
2514
+ ...live.state,
2515
+ tasks: {
2516
+ ...live.state.tasks,
2517
+ [name]: { ...taskState, status: "not-started", lastUpdated: (/* @__PURE__ */ new Date()).toISOString() }
2518
+ },
2519
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2520
+ }
2521
+ };
2522
+ }
2523
+ function getNode(live, name) {
2524
+ const config = live.config.tasks[name];
2525
+ if (!config) return void 0;
2526
+ const state = live.state.tasks[name] ?? createDefaultTaskState3();
2527
+ return { name, config, state };
2528
+ }
2529
+ function snapshot(live) {
2530
+ return {
2531
+ version: 1,
2532
+ config: live.config,
2533
+ state: live.state,
2534
+ snapshotAt: (/* @__PURE__ */ new Date()).toISOString()
2535
+ };
2536
+ }
2537
+ function restore(data) {
2538
+ if (!data || typeof data !== "object") {
2539
+ throw new Error("Invalid snapshot: expected an object");
2540
+ }
2541
+ const snap = data;
2542
+ if (!snap.config || typeof snap.config !== "object") {
2543
+ throw new Error('Invalid snapshot: missing or invalid "config"');
2544
+ }
2545
+ if (!snap.state || typeof snap.state !== "object") {
2546
+ throw new Error('Invalid snapshot: missing or invalid "state"');
2547
+ }
2548
+ const config = snap.config;
2549
+ const state = snap.state;
2550
+ if (!config.settings || typeof config.settings !== "object") {
2551
+ throw new Error("Invalid snapshot: config.settings missing");
2552
+ }
2553
+ if (!config.tasks || typeof config.tasks !== "object") {
2554
+ throw new Error("Invalid snapshot: config.tasks missing");
2555
+ }
2556
+ if (!state.tasks || typeof state.tasks !== "object") {
2557
+ throw new Error("Invalid snapshot: state.tasks missing");
2558
+ }
2559
+ if (!Array.isArray(state.availableOutputs)) {
2560
+ throw new Error("Invalid snapshot: state.availableOutputs must be an array");
2561
+ }
2562
+ return { config, state };
2563
+ }
2564
+ function createDefaultTaskState3() {
2565
+ return {
2566
+ status: "not-started",
2567
+ executionCount: 0,
2568
+ retryCount: 0,
2569
+ lastEpoch: 0,
2570
+ messages: [],
2571
+ progress: null
2572
+ };
2573
+ }
2574
+ function applyAgentAction2(state, action) {
2575
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2576
+ switch (action) {
2577
+ case "stop":
2578
+ return { ...state, status: "stopped", lastUpdated: now };
2579
+ case "pause":
2580
+ return { ...state, status: "paused", lastUpdated: now };
2581
+ case "resume":
2582
+ return { ...state, status: "running", lastUpdated: now };
2583
+ default:
2584
+ return state;
2585
+ }
2586
+ }
2587
+
2588
+ // src/continuous-event-graph/schedule.ts
2589
+ function schedule(live) {
2590
+ const { config, state } = live;
2591
+ const graphTasks = getAllTasks(config);
2592
+ const taskNames = Object.keys(graphTasks);
2593
+ if (taskNames.length === 0) {
2594
+ return { eligible: [], pending: [], unresolved: [], blocked: [], conflicts: {} };
2595
+ }
2596
+ const producerMap = buildProducerMap3(graphTasks);
2597
+ const computedOutputs = computeAvailableOutputs(config, state.tasks);
2598
+ const availableOutputs = /* @__PURE__ */ new Set([...computedOutputs, ...state.availableOutputs]);
2599
+ const eligible = [];
2600
+ const pending = [];
2601
+ const unresolved = [];
2602
+ const blocked = [];
2603
+ for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
2604
+ const taskState = state.tasks[taskName];
2605
+ if (!isRepeatableTask(taskConfig)) {
2606
+ if (taskState?.status === TASK_STATUS.COMPLETED || taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
2607
+ continue;
2608
+ }
2609
+ } else {
2610
+ if (taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
2611
+ continue;
2612
+ }
2613
+ const maxExec = getRepeatableMax(taskConfig);
2614
+ if (maxExec !== void 0 && taskState && taskState.executionCount >= maxExec) {
2615
+ continue;
2616
+ }
2617
+ if (taskConfig.circuit_breaker && taskState && taskState.executionCount >= taskConfig.circuit_breaker.max_executions) {
2618
+ continue;
2619
+ }
2620
+ if (taskState?.status === TASK_STATUS.COMPLETED) {
2621
+ const requires2 = getRequires(taskConfig);
2622
+ if (requires2.length > 0) {
2623
+ const hasRefreshed = requires2.some((req) => {
2624
+ for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
2625
+ if (getProvides(otherConfig).includes(req)) {
2626
+ const otherState = state.tasks[otherName];
2627
+ if (otherState && otherState.executionCount > taskState.lastEpoch) return true;
2628
+ }
2629
+ }
2630
+ return false;
2631
+ });
2632
+ if (!hasRefreshed) continue;
2633
+ } else {
2634
+ continue;
2635
+ }
2636
+ }
2637
+ }
2638
+ const requires = getRequires(taskConfig);
2639
+ if (requires.length === 0) {
2640
+ eligible.push(taskName);
2641
+ continue;
2642
+ }
2643
+ const missingTokens = [];
2644
+ const pendingTokens = [];
2645
+ const failedTokenInfo = [];
2646
+ for (const token of requires) {
2647
+ if (availableOutputs.has(token)) continue;
2648
+ const producers = producerMap[token] || [];
2649
+ if (producers.length === 0) {
2650
+ missingTokens.push(token);
2651
+ } else {
2652
+ const allFailed = producers.every((p) => isNonActiveTask(state.tasks[p]));
2653
+ if (allFailed) {
2654
+ failedTokenInfo.push({ token, failedProducer: producers[0] });
2655
+ } else {
2656
+ pendingTokens.push(token);
2657
+ }
2658
+ }
2659
+ }
2660
+ if (missingTokens.length > 0) {
2661
+ unresolved.push({ taskName, missingTokens });
2662
+ } else if (failedTokenInfo.length > 0) {
2663
+ blocked.push({
2664
+ taskName,
2665
+ failedTokens: failedTokenInfo.map((f) => f.token),
2666
+ failedProducers: [...new Set(failedTokenInfo.map((f) => f.failedProducer))]
2667
+ });
2668
+ } else if (pendingTokens.length > 0) {
2669
+ pending.push({ taskName, waitingOn: pendingTokens });
2670
+ } else {
2671
+ eligible.push(taskName);
2672
+ }
2673
+ }
2674
+ const conflicts = {};
2675
+ if (eligible.length > 1) {
2676
+ const outputGroups = groupTasksByProvides(eligible, graphTasks);
2677
+ for (const [outputKey, groupTasks] of Object.entries(outputGroups)) {
2678
+ if (groupTasks.length > 1) {
2679
+ conflicts[outputKey] = groupTasks;
2680
+ }
2681
+ }
2682
+ }
2683
+ return { eligible, pending, unresolved, blocked, conflicts };
2684
+ }
2685
+ function buildProducerMap3(tasks) {
2686
+ const map = {};
2687
+ for (const [name, config] of Object.entries(tasks)) {
2688
+ for (const token of getProvides(config)) {
2689
+ if (!map[token]) map[token] = [];
2690
+ map[token].push(name);
2691
+ }
2692
+ if (config.on) {
2693
+ for (const tokens of Object.values(config.on)) {
2694
+ for (const token of tokens) {
2695
+ if (!map[token]) map[token] = [];
2696
+ if (!map[token].includes(name)) map[token].push(name);
2697
+ }
2698
+ }
2699
+ }
2700
+ if (config.on_failure) {
2701
+ for (const token of config.on_failure) {
2702
+ if (!map[token]) map[token] = [];
2703
+ if (!map[token].includes(name)) map[token].push(name);
2704
+ }
2705
+ }
2706
+ }
2707
+ return map;
2708
+ }
2709
+
2710
+ // src/continuous-event-graph/inspect.ts
2711
+ function inspect(live) {
2712
+ const { config, state } = live;
2713
+ const graphTasks = getAllTasks(config);
2714
+ const taskNames = Object.keys(graphTasks);
2715
+ let running = 0, completed = 0, failed = 0, waiting = 0, notStarted = 0, disabled = 0;
2716
+ for (const taskName of taskNames) {
2717
+ const ts = state.tasks[taskName];
2718
+ if (!ts || ts.status === TASK_STATUS.NOT_STARTED) {
2719
+ notStarted++;
2720
+ } else {
2721
+ switch (ts.status) {
2722
+ case TASK_STATUS.RUNNING:
2723
+ running++;
2724
+ break;
2725
+ case TASK_STATUS.COMPLETED:
2726
+ completed++;
2727
+ break;
2728
+ case TASK_STATUS.FAILED:
2729
+ failed++;
2730
+ break;
2731
+ case "inactivated":
2732
+ disabled++;
2733
+ break;
2734
+ default:
2735
+ waiting++;
2736
+ }
2737
+ }
2738
+ }
2739
+ const producerMap = {};
2740
+ for (const [name, taskConfig] of Object.entries(graphTasks)) {
2741
+ for (const token of getProvides(taskConfig)) {
2742
+ if (!producerMap[token]) producerMap[token] = [];
2743
+ producerMap[token].push(name);
2744
+ }
2745
+ if (taskConfig.on) {
2746
+ for (const tokens of Object.values(taskConfig.on)) {
2747
+ for (const token of tokens) {
2748
+ if (!producerMap[token]) producerMap[token] = [];
2749
+ if (!producerMap[token].includes(name)) producerMap[token].push(name);
2750
+ }
2751
+ }
2752
+ }
2753
+ if (taskConfig.on_failure) {
2754
+ for (const token of taskConfig.on_failure) {
2755
+ if (!producerMap[token]) producerMap[token] = [];
2756
+ if (!producerMap[token].includes(name)) producerMap[token].push(name);
2757
+ }
2758
+ }
2759
+ }
2760
+ const openDeps = /* @__PURE__ */ new Set();
2761
+ let unresolvedCount = 0;
2762
+ let blockedCount = 0;
2763
+ for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
2764
+ const ts = state.tasks[taskName];
2765
+ if (ts?.status === TASK_STATUS.COMPLETED || ts?.status === TASK_STATUS.RUNNING) continue;
2766
+ let hasOpen = false;
2767
+ let hasBlocked = false;
2768
+ for (const token of getRequires(taskConfig)) {
2769
+ const producers = producerMap[token] || [];
2770
+ if (producers.length === 0) {
2771
+ openDeps.add(token);
2772
+ hasOpen = true;
2773
+ } else {
2774
+ const allFailed = producers.every((p) => {
2775
+ const ps = state.tasks[p];
2776
+ return ps?.status === TASK_STATUS.FAILED || ps?.status === "inactivated";
2777
+ });
2778
+ if (allFailed) hasBlocked = true;
2779
+ }
2780
+ }
2781
+ if (hasOpen) unresolvedCount++;
2782
+ if (hasBlocked && !hasOpen) blockedCount++;
2783
+ }
2784
+ const conflictTokens = [];
2785
+ for (const [token, producers] of Object.entries(producerMap)) {
2786
+ if (producers.length > 1) conflictTokens.push(token);
2787
+ }
2788
+ const deps = buildTaskDeps2(graphTasks, producerMap);
2789
+ const cycles = detectCycles2(taskNames, deps);
2790
+ return {
2791
+ totalNodes: taskNames.length,
2792
+ running,
2793
+ completed,
2794
+ failed,
2795
+ waiting,
2796
+ notStarted,
2797
+ disabled,
2798
+ unresolvedCount,
2799
+ blockedCount,
2800
+ openDependencies: [...openDeps],
2801
+ cycles,
2802
+ conflictTokens
2803
+ };
2804
+ }
2805
+ function buildTaskDeps2(tasks, producerMap) {
2806
+ const deps = {};
2807
+ for (const [name, config] of Object.entries(tasks)) {
2808
+ deps[name] = /* @__PURE__ */ new Set();
2809
+ for (const token of getRequires(config)) {
2810
+ for (const producer of producerMap[token] || []) {
2811
+ if (producer !== name) deps[name].add(producer);
2812
+ }
2813
+ }
2814
+ }
2815
+ return deps;
2816
+ }
2817
+ function detectCycles2(taskNames, deps) {
2818
+ const WHITE = 0, GRAY = 1, BLACK = 2;
2819
+ const color = {};
2820
+ const parent = {};
2821
+ const cycles = [];
2822
+ for (const name of taskNames) {
2823
+ color[name] = WHITE;
2824
+ parent[name] = null;
2825
+ }
2826
+ function dfs(node) {
2827
+ color[node] = GRAY;
2828
+ for (const dep of deps[node] || []) {
2829
+ if (color[dep] === GRAY) {
2830
+ const cycle = [dep];
2831
+ let cur = node;
2832
+ while (cur !== dep) {
2833
+ cycle.push(cur);
2834
+ cur = parent[cur];
2835
+ }
2836
+ cycle.push(dep);
2837
+ cycle.reverse();
2838
+ cycles.push(cycle);
2839
+ } else if (color[dep] === WHITE) {
2840
+ parent[dep] = node;
2841
+ dfs(dep);
2842
+ }
2843
+ }
2844
+ color[node] = BLACK;
2845
+ }
2846
+ for (const name of taskNames) {
2847
+ if (color[name] === WHITE) dfs(name);
2848
+ }
2849
+ return cycles;
2850
+ }
2851
+ function buildProducerMap4(tasks) {
2852
+ const map = {};
2853
+ for (const [name, config] of Object.entries(tasks)) {
2854
+ for (const token of getProvides(config)) {
2855
+ if (!map[token]) map[token] = [];
2856
+ map[token].push(name);
2857
+ }
2858
+ if (config.on) {
2859
+ for (const tokens of Object.values(config.on)) {
2860
+ for (const token of tokens) {
2861
+ if (!map[token]) map[token] = [];
2862
+ if (!map[token].includes(name)) map[token].push(name);
2863
+ }
2864
+ }
2865
+ }
2866
+ if (config.on_failure) {
2867
+ for (const token of config.on_failure) {
2868
+ if (!map[token]) map[token] = [];
2869
+ if (!map[token].includes(name)) map[token].push(name);
2870
+ }
2871
+ }
2872
+ }
2873
+ return map;
2874
+ }
2875
+ function getUnreachableTokens(live) {
2876
+ const { config, state } = live;
2877
+ const graphTasks = getAllTasks(config);
2878
+ const producerMap = buildProducerMap4(graphTasks);
2879
+ const available = /* @__PURE__ */ new Set([...state.availableOutputs]);
2880
+ for (const [taskName, taskState] of Object.entries(state.tasks)) {
2881
+ if (taskState.status === "completed") {
2882
+ const tc = graphTasks[taskName];
2883
+ if (tc) getProvides(tc).forEach((t) => available.add(t));
2884
+ }
2885
+ }
2886
+ const allRequired = /* @__PURE__ */ new Set();
2887
+ for (const taskConfig of Object.values(graphTasks)) {
2888
+ for (const token of getRequires(taskConfig)) {
2889
+ allRequired.add(token);
2890
+ }
2891
+ }
2892
+ const unreachable = /* @__PURE__ */ new Set();
2893
+ const unreachableNodes = /* @__PURE__ */ new Set();
2894
+ for (const token of allRequired) {
2895
+ if (available.has(token)) continue;
2896
+ const producers = producerMap[token] || [];
2897
+ if (producers.length === 0) {
2898
+ unreachable.add(token);
2899
+ }
2900
+ }
2901
+ let changed = true;
2902
+ while (changed) {
2903
+ changed = false;
2904
+ for (const [name, taskConfig] of Object.entries(graphTasks)) {
2905
+ if (unreachableNodes.has(name)) continue;
2906
+ const ts = state.tasks[name];
2907
+ if (ts?.status === "completed") continue;
2908
+ const isNonActive = isNonActiveTask(ts);
2909
+ const requires = getRequires(taskConfig);
2910
+ const hasUnreachableDep = requires.some((t) => unreachable.has(t));
2911
+ if (isNonActive || hasUnreachableDep) {
2912
+ if (!unreachableNodes.has(name)) {
2913
+ unreachableNodes.add(name);
2914
+ changed = true;
2915
+ }
2916
+ }
2917
+ }
2918
+ for (const token of allRequired) {
2919
+ if (unreachable.has(token) || available.has(token)) continue;
2920
+ const producers = producerMap[token] || [];
2921
+ const allProducersUnreachable = producers.length > 0 && producers.every((p) => unreachableNodes.has(p) || isNonActiveTask(state.tasks[p]));
2922
+ if (producers.length === 0 || allProducersUnreachable) {
2923
+ if (!unreachable.has(token)) {
2924
+ unreachable.add(token);
2925
+ changed = true;
2926
+ }
2927
+ }
2928
+ }
2929
+ }
2930
+ const tokens = [];
2931
+ for (const token of unreachable) {
2932
+ const producers = producerMap[token] || [];
2933
+ let reason;
2934
+ if (producers.length === 0) {
2935
+ reason = "no-producer";
2936
+ } else {
2937
+ const allFailed = producers.every((p) => isNonActiveTask(state.tasks[p]));
2938
+ reason = allFailed ? "all-producers-failed" : "transitive";
2939
+ }
2940
+ tokens.push({ token, reason, producers });
2941
+ }
2942
+ return { tokens };
2943
+ }
2944
+ function getUnreachableNodes(live) {
2945
+ const { config, state } = live;
2946
+ const graphTasks = getAllTasks(config);
2947
+ const { tokens: unreachableTokens } = getUnreachableTokens(live);
2948
+ const unreachableTokenSet = new Set(unreachableTokens.map((t) => t.token));
2949
+ const nodes = [];
2950
+ for (const [name, taskConfig] of Object.entries(graphTasks)) {
2951
+ const ts = state.tasks[name];
2952
+ if (ts?.status === "completed") continue;
2953
+ const requires = getRequires(taskConfig);
2954
+ const missingTokens = requires.filter((t) => unreachableTokenSet.has(t));
2955
+ if (missingTokens.length > 0) {
2956
+ nodes.push({ nodeName: name, missingTokens });
2957
+ } else if (isNonActiveTask(ts)) {
2958
+ nodes.push({ nodeName: name, missingTokens: [] });
2959
+ }
2960
+ }
2961
+ return { nodes };
2962
+ }
2963
+ function getUpstream(live, nodeName) {
2964
+ const graphTasks = getAllTasks(live.config);
2965
+ if (!graphTasks[nodeName]) return { nodeName, nodes: [], tokens: [] };
2966
+ const producerMap = buildProducerMap4(graphTasks);
2967
+ const visited = /* @__PURE__ */ new Set();
2968
+ const tokenSet = /* @__PURE__ */ new Set();
2969
+ const nodeEntries = /* @__PURE__ */ new Map();
2970
+ function walk(current) {
2971
+ const taskConfig = graphTasks[current];
2972
+ if (!taskConfig) return;
2973
+ for (const token of getRequires(taskConfig)) {
2974
+ const producers = producerMap[token] || [];
2975
+ for (const producer of producers) {
2976
+ if (producer === nodeName) continue;
2977
+ tokenSet.add(token);
2978
+ if (!nodeEntries.has(producer)) nodeEntries.set(producer, /* @__PURE__ */ new Set());
2979
+ nodeEntries.get(producer).add(token);
2980
+ if (!visited.has(producer)) {
2981
+ visited.add(producer);
2982
+ walk(producer);
2983
+ }
2984
+ }
2985
+ }
2986
+ }
2987
+ walk(nodeName);
2988
+ const nodes = [...nodeEntries.entries()].map(([name, tokens]) => ({
2989
+ nodeName: name,
2990
+ providesTokens: [...tokens]
2991
+ }));
2992
+ return { nodeName, nodes, tokens: [...tokenSet] };
2993
+ }
2994
+ function getDownstream(live, nodeName) {
2995
+ const graphTasks = getAllTasks(live.config);
2996
+ if (!graphTasks[nodeName]) return { nodeName, nodes: [], tokens: [] };
2997
+ const consumerMap = {};
2998
+ for (const [name, config] of Object.entries(graphTasks)) {
2999
+ for (const token of getRequires(config)) {
3000
+ if (!consumerMap[token]) consumerMap[token] = [];
3001
+ consumerMap[token].push(name);
3002
+ }
3003
+ }
3004
+ const visited = /* @__PURE__ */ new Set();
3005
+ const tokenSet = /* @__PURE__ */ new Set();
3006
+ const nodeEntries = /* @__PURE__ */ new Map();
3007
+ function walk(current) {
3008
+ const taskConfig = graphTasks[current];
3009
+ if (!taskConfig) return;
3010
+ for (const token of getProvides(taskConfig)) {
3011
+ const consumers = consumerMap[token] || [];
3012
+ for (const consumer of consumers) {
3013
+ if (consumer === nodeName) continue;
3014
+ tokenSet.add(token);
3015
+ if (!nodeEntries.has(consumer)) nodeEntries.set(consumer, /* @__PURE__ */ new Set());
3016
+ nodeEntries.get(consumer).add(token);
3017
+ if (!visited.has(consumer)) {
3018
+ visited.add(consumer);
3019
+ walk(consumer);
3020
+ }
3021
+ }
3022
+ }
3023
+ }
3024
+ walk(nodeName);
3025
+ const nodes = [...nodeEntries.entries()].map(([name, tokens]) => ({
3026
+ nodeName: name,
3027
+ requiresTokens: [...tokens]
3028
+ }));
3029
+ return { nodeName, nodes, tokens: [...tokenSet] };
3030
+ }
3031
+
2098
3032
  exports.COMPLETION_STRATEGIES = COMPLETION_STRATEGIES;
2099
3033
  exports.CONFLICT_STRATEGIES = CONFLICT_STRATEGIES;
2100
3034
  exports.DEFAULTS = DEFAULTS;
@@ -2107,8 +3041,12 @@ exports.MemoryStore = MemoryStore;
2107
3041
  exports.StepMachine = StepMachine;
2108
3042
  exports.TASK_STATUS = TASK_STATUS;
2109
3043
  exports.addDynamicTask = addDynamicTask;
3044
+ exports.addNode = addNode;
3045
+ exports.addProvides = addProvides;
3046
+ exports.addRequires = addRequires;
2110
3047
  exports.apply = apply;
2111
3048
  exports.applyAll = applyAll;
3049
+ exports.applyEvent = applyEvent;
2112
3050
  exports.applyStepResult = applyStepResult;
2113
3051
  exports.batch = batch;
2114
3052
  exports.checkCircuitBreaker = checkCircuitBreaker;
@@ -2118,19 +3056,30 @@ exports.createDefaultTaskState = createDefaultTaskState;
2118
3056
  exports.createEngine = createStepMachine;
2119
3057
  exports.createInitialExecutionState = createInitialExecutionState;
2120
3058
  exports.createInitialState = createInitialState;
3059
+ exports.createLiveGraph = createLiveGraph;
2121
3060
  exports.createStepMachine = createStepMachine;
2122
3061
  exports.detectStuckState = detectStuckState;
3062
+ exports.disableNode = disableNode;
3063
+ exports.drainTokens = drainTokens;
3064
+ exports.enableNode = enableNode;
2123
3065
  exports.exportGraphConfig = exportGraphConfig;
2124
3066
  exports.exportGraphConfigToFile = exportGraphConfigToFile;
2125
3067
  exports.extractReturnData = extractReturnData;
2126
3068
  exports.flowToMermaid = flowToMermaid;
2127
3069
  exports.getAllTasks = getAllTasks;
2128
3070
  exports.getCandidateTasks = getCandidateTasks;
3071
+ exports.getDownstream = getDownstream;
3072
+ exports.getNode = getNode;
2129
3073
  exports.getProvides = getProvides;
2130
3074
  exports.getRequires = getRequires;
2131
3075
  exports.getTask = getTask;
3076
+ exports.getUnreachableNodes = getUnreachableNodes;
3077
+ exports.getUnreachableTokens = getUnreachableTokens;
3078
+ exports.getUpstream = getUpstream;
2132
3079
  exports.graphToMermaid = graphToMermaid;
2133
3080
  exports.hasTask = hasTask;
3081
+ exports.injectTokens = injectTokens;
3082
+ exports.inspect = inspect;
2134
3083
  exports.isExecutionComplete = isExecutionComplete;
2135
3084
  exports.isNonActiveTask = isNonActiveTask;
2136
3085
  exports.isRepeatableTask = isRepeatableTask;
@@ -2140,8 +3089,16 @@ exports.loadGraphConfig = loadGraphConfig;
2140
3089
  exports.loadStepFlow = loadStepFlow;
2141
3090
  exports.next = next;
2142
3091
  exports.planExecution = planExecution;
3092
+ exports.removeNode = removeNode;
3093
+ exports.removeProvides = removeProvides;
3094
+ exports.removeRequires = removeRequires;
3095
+ exports.resetNode = resetNode;
2143
3096
  exports.resolveConfigTemplates = resolveConfigTemplates;
2144
3097
  exports.resolveVariables = resolveVariables;
3098
+ exports.restore = restore;
3099
+ exports.schedule = schedule;
3100
+ exports.snapshot = snapshot;
3101
+ exports.validateGraph = validateGraph;
2145
3102
  exports.validateGraphConfig = validateGraphConfig;
2146
3103
  exports.validateStepFlowConfig = validateStepFlowConfig;
2147
3104
  //# sourceMappingURL=index.cjs.map