yaml-flow 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +368 -0
- package/dist/{constants-DMbnp--H.d.ts → constants-BEbO2_OK.d.ts} +2 -187
- package/dist/{constants-BftTHiuV.d.cts → constants-BNjeIlZ8.d.cts} +2 -187
- package/dist/continuous-event-graph/index.cjs +939 -0
- package/dist/continuous-event-graph/index.cjs.map +1 -0
- package/dist/continuous-event-graph/index.d.cts +179 -0
- package/dist/continuous-event-graph/index.d.ts +179 -0
- package/dist/continuous-event-graph/index.js +916 -0
- package/dist/continuous-event-graph/index.js.map +1 -0
- package/dist/event-graph/index.d.cts +3 -2
- package/dist/event-graph/index.d.ts +3 -2
- package/dist/index.cjs +1008 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +981 -1
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +450 -0
- package/dist/inference/index.cjs.map +1 -0
- package/dist/inference/index.d.cts +229 -0
- package/dist/inference/index.d.ts +229 -0
- package/dist/inference/index.js +443 -0
- package/dist/inference/index.js.map +1 -0
- package/dist/types-C2lOwquM.d.cts +135 -0
- package/dist/types-DAI_a2as.d.cts +198 -0
- package/dist/types-DAI_a2as.d.ts +198 -0
- package/dist/types-mS_pPftm.d.ts +135 -0
- package/package.json +11 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var child_process = require('child_process');
|
|
4
|
+
|
|
3
5
|
// src/step-machine/reducer.ts
|
|
4
6
|
function applyStepResult(flow, state, stepName, stepResult) {
|
|
5
7
|
const stepConfig = flow.steps[stepName];
|
|
@@ -2302,6 +2304,984 @@ function resolveConfigTemplates(config) {
|
|
|
2302
2304
|
return result;
|
|
2303
2305
|
}
|
|
2304
2306
|
|
|
2307
|
+
// src/continuous-event-graph/core.ts
|
|
2308
|
+
function createLiveGraph(config, executionId) {
|
|
2309
|
+
const id = executionId ?? `live-${Date.now()}`;
|
|
2310
|
+
const tasks = {};
|
|
2311
|
+
for (const taskName of Object.keys(config.tasks)) {
|
|
2312
|
+
tasks[taskName] = createDefaultTaskState3();
|
|
2313
|
+
}
|
|
2314
|
+
const state = {
|
|
2315
|
+
status: "running",
|
|
2316
|
+
tasks,
|
|
2317
|
+
availableOutputs: [],
|
|
2318
|
+
stuckDetection: { is_stuck: false, stuck_description: null, outputs_unresolvable: [], tasks_blocked: [] },
|
|
2319
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2320
|
+
executionId: id,
|
|
2321
|
+
executionConfig: {
|
|
2322
|
+
executionMode: config.settings.execution_mode ?? "eligibility-mode",
|
|
2323
|
+
conflictStrategy: config.settings.conflict_strategy ?? "alphabetical",
|
|
2324
|
+
completionStrategy: config.settings.completion
|
|
2325
|
+
}
|
|
2326
|
+
};
|
|
2327
|
+
return { config, state };
|
|
2328
|
+
}
|
|
2329
|
+
function applyEvent(live, event) {
|
|
2330
|
+
const { config, state } = live;
|
|
2331
|
+
if ("executionId" in event && event.executionId && event.executionId !== state.executionId) {
|
|
2332
|
+
return live;
|
|
2333
|
+
}
|
|
2334
|
+
let newState;
|
|
2335
|
+
switch (event.type) {
|
|
2336
|
+
case "task-started":
|
|
2337
|
+
newState = applyTaskStart(state, event.taskName);
|
|
2338
|
+
break;
|
|
2339
|
+
case "task-completed":
|
|
2340
|
+
newState = applyTaskCompletion(state, config, event.taskName, event.result);
|
|
2341
|
+
break;
|
|
2342
|
+
case "task-failed":
|
|
2343
|
+
newState = applyTaskFailure(state, config, event.taskName, event.error);
|
|
2344
|
+
break;
|
|
2345
|
+
case "task-progress":
|
|
2346
|
+
newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
|
|
2347
|
+
break;
|
|
2348
|
+
case "inject-tokens":
|
|
2349
|
+
newState = {
|
|
2350
|
+
...state,
|
|
2351
|
+
availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
|
|
2352
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2353
|
+
};
|
|
2354
|
+
break;
|
|
2355
|
+
case "agent-action":
|
|
2356
|
+
newState = applyAgentAction2(state, event.action);
|
|
2357
|
+
break;
|
|
2358
|
+
default:
|
|
2359
|
+
return live;
|
|
2360
|
+
}
|
|
2361
|
+
return { config, state: newState };
|
|
2362
|
+
}
|
|
2363
|
+
function addNode(live, name, taskConfig) {
|
|
2364
|
+
if (live.config.tasks[name]) return live;
|
|
2365
|
+
return {
|
|
2366
|
+
config: {
|
|
2367
|
+
...live.config,
|
|
2368
|
+
tasks: { ...live.config.tasks, [name]: taskConfig }
|
|
2369
|
+
},
|
|
2370
|
+
state: {
|
|
2371
|
+
...live.state,
|
|
2372
|
+
tasks: { ...live.state.tasks, [name]: createDefaultTaskState3() },
|
|
2373
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
function removeNode(live, name) {
|
|
2378
|
+
if (!live.config.tasks[name]) return live;
|
|
2379
|
+
const { [name]: _removedConfig, ...remainingTasks } = live.config.tasks;
|
|
2380
|
+
const { [name]: _removedState, ...remainingStates } = live.state.tasks;
|
|
2381
|
+
return {
|
|
2382
|
+
config: {
|
|
2383
|
+
...live.config,
|
|
2384
|
+
tasks: remainingTasks
|
|
2385
|
+
},
|
|
2386
|
+
state: {
|
|
2387
|
+
...live.state,
|
|
2388
|
+
tasks: remainingStates,
|
|
2389
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2390
|
+
}
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
function addRequires(live, nodeName, tokens) {
|
|
2394
|
+
const task = live.config.tasks[nodeName];
|
|
2395
|
+
if (!task) return live;
|
|
2396
|
+
const current = getRequires(task);
|
|
2397
|
+
const toAdd = tokens.filter((t) => !current.includes(t));
|
|
2398
|
+
if (toAdd.length === 0) return live;
|
|
2399
|
+
return {
|
|
2400
|
+
config: {
|
|
2401
|
+
...live.config,
|
|
2402
|
+
tasks: {
|
|
2403
|
+
...live.config.tasks,
|
|
2404
|
+
[nodeName]: { ...task, requires: [...current, ...toAdd] }
|
|
2405
|
+
}
|
|
2406
|
+
},
|
|
2407
|
+
state: live.state
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
function removeRequires(live, nodeName, tokens) {
|
|
2411
|
+
const task = live.config.tasks[nodeName];
|
|
2412
|
+
if (!task) return live;
|
|
2413
|
+
const current = getRequires(task);
|
|
2414
|
+
const remaining = current.filter((t) => !tokens.includes(t));
|
|
2415
|
+
if (remaining.length === current.length) return live;
|
|
2416
|
+
return {
|
|
2417
|
+
config: {
|
|
2418
|
+
...live.config,
|
|
2419
|
+
tasks: {
|
|
2420
|
+
...live.config.tasks,
|
|
2421
|
+
[nodeName]: { ...task, requires: remaining }
|
|
2422
|
+
}
|
|
2423
|
+
},
|
|
2424
|
+
state: live.state
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
function addProvides(live, nodeName, tokens) {
|
|
2428
|
+
const task = live.config.tasks[nodeName];
|
|
2429
|
+
if (!task) return live;
|
|
2430
|
+
const current = getProvides(task);
|
|
2431
|
+
const toAdd = tokens.filter((t) => !current.includes(t));
|
|
2432
|
+
if (toAdd.length === 0) return live;
|
|
2433
|
+
return {
|
|
2434
|
+
config: {
|
|
2435
|
+
...live.config,
|
|
2436
|
+
tasks: {
|
|
2437
|
+
...live.config.tasks,
|
|
2438
|
+
[nodeName]: { ...task, provides: [...current, ...toAdd] }
|
|
2439
|
+
}
|
|
2440
|
+
},
|
|
2441
|
+
state: live.state
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
function removeProvides(live, nodeName, tokens) {
|
|
2445
|
+
const task = live.config.tasks[nodeName];
|
|
2446
|
+
if (!task) return live;
|
|
2447
|
+
const current = getProvides(task);
|
|
2448
|
+
const remaining = current.filter((t) => !tokens.includes(t));
|
|
2449
|
+
if (remaining.length === current.length) return live;
|
|
2450
|
+
return {
|
|
2451
|
+
config: {
|
|
2452
|
+
...live.config,
|
|
2453
|
+
tasks: {
|
|
2454
|
+
...live.config.tasks,
|
|
2455
|
+
[nodeName]: { ...task, provides: remaining }
|
|
2456
|
+
}
|
|
2457
|
+
},
|
|
2458
|
+
state: live.state
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
function injectTokens(live, tokens) {
|
|
2462
|
+
return applyEvent(live, {
|
|
2463
|
+
type: "inject-tokens",
|
|
2464
|
+
tokens,
|
|
2465
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2466
|
+
});
|
|
2467
|
+
}
|
|
2468
|
+
function drainTokens(live, tokens) {
|
|
2469
|
+
const toRemove = new Set(tokens);
|
|
2470
|
+
const remaining = live.state.availableOutputs.filter((t) => !toRemove.has(t));
|
|
2471
|
+
if (remaining.length === live.state.availableOutputs.length) return live;
|
|
2472
|
+
return {
|
|
2473
|
+
config: live.config,
|
|
2474
|
+
state: {
|
|
2475
|
+
...live.state,
|
|
2476
|
+
availableOutputs: remaining,
|
|
2477
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2478
|
+
}
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
function resetNode(live, name) {
|
|
2482
|
+
if (!live.config.tasks[name] || !live.state.tasks[name]) return live;
|
|
2483
|
+
return {
|
|
2484
|
+
config: live.config,
|
|
2485
|
+
state: {
|
|
2486
|
+
...live.state,
|
|
2487
|
+
tasks: {
|
|
2488
|
+
...live.state.tasks,
|
|
2489
|
+
[name]: createDefaultTaskState3()
|
|
2490
|
+
},
|
|
2491
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2492
|
+
}
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
function disableNode(live, name) {
|
|
2496
|
+
const taskState = live.state.tasks[name];
|
|
2497
|
+
if (!taskState || taskState.status === "inactivated") return live;
|
|
2498
|
+
return {
|
|
2499
|
+
config: live.config,
|
|
2500
|
+
state: {
|
|
2501
|
+
...live.state,
|
|
2502
|
+
tasks: {
|
|
2503
|
+
...live.state.tasks,
|
|
2504
|
+
[name]: { ...taskState, status: "inactivated", lastUpdated: (/* @__PURE__ */ new Date()).toISOString() }
|
|
2505
|
+
},
|
|
2506
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2510
|
+
function enableNode(live, name) {
|
|
2511
|
+
const taskState = live.state.tasks[name];
|
|
2512
|
+
if (!taskState || taskState.status !== "inactivated") return live;
|
|
2513
|
+
return {
|
|
2514
|
+
config: live.config,
|
|
2515
|
+
state: {
|
|
2516
|
+
...live.state,
|
|
2517
|
+
tasks: {
|
|
2518
|
+
...live.state.tasks,
|
|
2519
|
+
[name]: { ...taskState, status: "not-started", lastUpdated: (/* @__PURE__ */ new Date()).toISOString() }
|
|
2520
|
+
},
|
|
2521
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
function getNode(live, name) {
|
|
2526
|
+
const config = live.config.tasks[name];
|
|
2527
|
+
if (!config) return void 0;
|
|
2528
|
+
const state = live.state.tasks[name] ?? createDefaultTaskState3();
|
|
2529
|
+
return { name, config, state };
|
|
2530
|
+
}
|
|
2531
|
+
function snapshot(live) {
|
|
2532
|
+
return {
|
|
2533
|
+
version: 1,
|
|
2534
|
+
config: live.config,
|
|
2535
|
+
state: live.state,
|
|
2536
|
+
snapshotAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
function restore(data) {
|
|
2540
|
+
if (!data || typeof data !== "object") {
|
|
2541
|
+
throw new Error("Invalid snapshot: expected an object");
|
|
2542
|
+
}
|
|
2543
|
+
const snap = data;
|
|
2544
|
+
if (!snap.config || typeof snap.config !== "object") {
|
|
2545
|
+
throw new Error('Invalid snapshot: missing or invalid "config"');
|
|
2546
|
+
}
|
|
2547
|
+
if (!snap.state || typeof snap.state !== "object") {
|
|
2548
|
+
throw new Error('Invalid snapshot: missing or invalid "state"');
|
|
2549
|
+
}
|
|
2550
|
+
const config = snap.config;
|
|
2551
|
+
const state = snap.state;
|
|
2552
|
+
if (!config.settings || typeof config.settings !== "object") {
|
|
2553
|
+
throw new Error("Invalid snapshot: config.settings missing");
|
|
2554
|
+
}
|
|
2555
|
+
if (!config.tasks || typeof config.tasks !== "object") {
|
|
2556
|
+
throw new Error("Invalid snapshot: config.tasks missing");
|
|
2557
|
+
}
|
|
2558
|
+
if (!state.tasks || typeof state.tasks !== "object") {
|
|
2559
|
+
throw new Error("Invalid snapshot: state.tasks missing");
|
|
2560
|
+
}
|
|
2561
|
+
if (!Array.isArray(state.availableOutputs)) {
|
|
2562
|
+
throw new Error("Invalid snapshot: state.availableOutputs must be an array");
|
|
2563
|
+
}
|
|
2564
|
+
return { config, state };
|
|
2565
|
+
}
|
|
2566
|
+
function createDefaultTaskState3() {
|
|
2567
|
+
return {
|
|
2568
|
+
status: "not-started",
|
|
2569
|
+
executionCount: 0,
|
|
2570
|
+
retryCount: 0,
|
|
2571
|
+
lastEpoch: 0,
|
|
2572
|
+
messages: [],
|
|
2573
|
+
progress: null
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
function applyAgentAction2(state, action) {
|
|
2577
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2578
|
+
switch (action) {
|
|
2579
|
+
case "stop":
|
|
2580
|
+
return { ...state, status: "stopped", lastUpdated: now };
|
|
2581
|
+
case "pause":
|
|
2582
|
+
return { ...state, status: "paused", lastUpdated: now };
|
|
2583
|
+
case "resume":
|
|
2584
|
+
return { ...state, status: "running", lastUpdated: now };
|
|
2585
|
+
default:
|
|
2586
|
+
return state;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
// src/continuous-event-graph/schedule.ts
|
|
2591
|
+
function schedule(live) {
|
|
2592
|
+
const { config, state } = live;
|
|
2593
|
+
const graphTasks = getAllTasks(config);
|
|
2594
|
+
const taskNames = Object.keys(graphTasks);
|
|
2595
|
+
if (taskNames.length === 0) {
|
|
2596
|
+
return { eligible: [], pending: [], unresolved: [], blocked: [], conflicts: {} };
|
|
2597
|
+
}
|
|
2598
|
+
const producerMap = buildProducerMap3(graphTasks);
|
|
2599
|
+
const computedOutputs = computeAvailableOutputs(config, state.tasks);
|
|
2600
|
+
const availableOutputs = /* @__PURE__ */ new Set([...computedOutputs, ...state.availableOutputs]);
|
|
2601
|
+
const eligible = [];
|
|
2602
|
+
const pending = [];
|
|
2603
|
+
const unresolved = [];
|
|
2604
|
+
const blocked = [];
|
|
2605
|
+
for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
|
|
2606
|
+
const taskState = state.tasks[taskName];
|
|
2607
|
+
if (!isRepeatableTask(taskConfig)) {
|
|
2608
|
+
if (taskState?.status === TASK_STATUS.COMPLETED || taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
|
|
2609
|
+
continue;
|
|
2610
|
+
}
|
|
2611
|
+
} else {
|
|
2612
|
+
if (taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
|
|
2613
|
+
continue;
|
|
2614
|
+
}
|
|
2615
|
+
const maxExec = getRepeatableMax(taskConfig);
|
|
2616
|
+
if (maxExec !== void 0 && taskState && taskState.executionCount >= maxExec) {
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
if (taskConfig.circuit_breaker && taskState && taskState.executionCount >= taskConfig.circuit_breaker.max_executions) {
|
|
2620
|
+
continue;
|
|
2621
|
+
}
|
|
2622
|
+
if (taskState?.status === TASK_STATUS.COMPLETED) {
|
|
2623
|
+
const requires2 = getRequires(taskConfig);
|
|
2624
|
+
if (requires2.length > 0) {
|
|
2625
|
+
const hasRefreshed = requires2.some((req) => {
|
|
2626
|
+
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
2627
|
+
if (getProvides(otherConfig).includes(req)) {
|
|
2628
|
+
const otherState = state.tasks[otherName];
|
|
2629
|
+
if (otherState && otherState.executionCount > taskState.lastEpoch) return true;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
return false;
|
|
2633
|
+
});
|
|
2634
|
+
if (!hasRefreshed) continue;
|
|
2635
|
+
} else {
|
|
2636
|
+
continue;
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
const requires = getRequires(taskConfig);
|
|
2641
|
+
if (requires.length === 0) {
|
|
2642
|
+
eligible.push(taskName);
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2645
|
+
const missingTokens = [];
|
|
2646
|
+
const pendingTokens = [];
|
|
2647
|
+
const failedTokenInfo = [];
|
|
2648
|
+
for (const token of requires) {
|
|
2649
|
+
if (availableOutputs.has(token)) continue;
|
|
2650
|
+
const producers = producerMap[token] || [];
|
|
2651
|
+
if (producers.length === 0) {
|
|
2652
|
+
missingTokens.push(token);
|
|
2653
|
+
} else {
|
|
2654
|
+
const allFailed = producers.every((p) => isNonActiveTask(state.tasks[p]));
|
|
2655
|
+
if (allFailed) {
|
|
2656
|
+
failedTokenInfo.push({ token, failedProducer: producers[0] });
|
|
2657
|
+
} else {
|
|
2658
|
+
pendingTokens.push(token);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
if (missingTokens.length > 0) {
|
|
2663
|
+
unresolved.push({ taskName, missingTokens });
|
|
2664
|
+
} else if (failedTokenInfo.length > 0) {
|
|
2665
|
+
blocked.push({
|
|
2666
|
+
taskName,
|
|
2667
|
+
failedTokens: failedTokenInfo.map((f) => f.token),
|
|
2668
|
+
failedProducers: [...new Set(failedTokenInfo.map((f) => f.failedProducer))]
|
|
2669
|
+
});
|
|
2670
|
+
} else if (pendingTokens.length > 0) {
|
|
2671
|
+
pending.push({ taskName, waitingOn: pendingTokens });
|
|
2672
|
+
} else {
|
|
2673
|
+
eligible.push(taskName);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
const conflicts = {};
|
|
2677
|
+
if (eligible.length > 1) {
|
|
2678
|
+
const outputGroups = groupTasksByProvides(eligible, graphTasks);
|
|
2679
|
+
for (const [outputKey, groupTasks] of Object.entries(outputGroups)) {
|
|
2680
|
+
if (groupTasks.length > 1) {
|
|
2681
|
+
conflicts[outputKey] = groupTasks;
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
return { eligible, pending, unresolved, blocked, conflicts };
|
|
2686
|
+
}
|
|
2687
|
+
function buildProducerMap3(tasks) {
|
|
2688
|
+
const map = {};
|
|
2689
|
+
for (const [name, config] of Object.entries(tasks)) {
|
|
2690
|
+
for (const token of getProvides(config)) {
|
|
2691
|
+
if (!map[token]) map[token] = [];
|
|
2692
|
+
map[token].push(name);
|
|
2693
|
+
}
|
|
2694
|
+
if (config.on) {
|
|
2695
|
+
for (const tokens of Object.values(config.on)) {
|
|
2696
|
+
for (const token of tokens) {
|
|
2697
|
+
if (!map[token]) map[token] = [];
|
|
2698
|
+
if (!map[token].includes(name)) map[token].push(name);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
if (config.on_failure) {
|
|
2703
|
+
for (const token of config.on_failure) {
|
|
2704
|
+
if (!map[token]) map[token] = [];
|
|
2705
|
+
if (!map[token].includes(name)) map[token].push(name);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
return map;
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
// src/continuous-event-graph/inspect.ts
|
|
2713
|
+
function inspect(live) {
|
|
2714
|
+
const { config, state } = live;
|
|
2715
|
+
const graphTasks = getAllTasks(config);
|
|
2716
|
+
const taskNames = Object.keys(graphTasks);
|
|
2717
|
+
let running = 0, completed = 0, failed = 0, waiting = 0, notStarted = 0, disabled = 0;
|
|
2718
|
+
for (const taskName of taskNames) {
|
|
2719
|
+
const ts = state.tasks[taskName];
|
|
2720
|
+
if (!ts || ts.status === TASK_STATUS.NOT_STARTED) {
|
|
2721
|
+
notStarted++;
|
|
2722
|
+
} else {
|
|
2723
|
+
switch (ts.status) {
|
|
2724
|
+
case TASK_STATUS.RUNNING:
|
|
2725
|
+
running++;
|
|
2726
|
+
break;
|
|
2727
|
+
case TASK_STATUS.COMPLETED:
|
|
2728
|
+
completed++;
|
|
2729
|
+
break;
|
|
2730
|
+
case TASK_STATUS.FAILED:
|
|
2731
|
+
failed++;
|
|
2732
|
+
break;
|
|
2733
|
+
case "inactivated":
|
|
2734
|
+
disabled++;
|
|
2735
|
+
break;
|
|
2736
|
+
default:
|
|
2737
|
+
waiting++;
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
const producerMap = {};
|
|
2742
|
+
for (const [name, taskConfig] of Object.entries(graphTasks)) {
|
|
2743
|
+
for (const token of getProvides(taskConfig)) {
|
|
2744
|
+
if (!producerMap[token]) producerMap[token] = [];
|
|
2745
|
+
producerMap[token].push(name);
|
|
2746
|
+
}
|
|
2747
|
+
if (taskConfig.on) {
|
|
2748
|
+
for (const tokens of Object.values(taskConfig.on)) {
|
|
2749
|
+
for (const token of tokens) {
|
|
2750
|
+
if (!producerMap[token]) producerMap[token] = [];
|
|
2751
|
+
if (!producerMap[token].includes(name)) producerMap[token].push(name);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
if (taskConfig.on_failure) {
|
|
2756
|
+
for (const token of taskConfig.on_failure) {
|
|
2757
|
+
if (!producerMap[token]) producerMap[token] = [];
|
|
2758
|
+
if (!producerMap[token].includes(name)) producerMap[token].push(name);
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
const openDeps = /* @__PURE__ */ new Set();
|
|
2763
|
+
let unresolvedCount = 0;
|
|
2764
|
+
let blockedCount = 0;
|
|
2765
|
+
for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
|
|
2766
|
+
const ts = state.tasks[taskName];
|
|
2767
|
+
if (ts?.status === TASK_STATUS.COMPLETED || ts?.status === TASK_STATUS.RUNNING) continue;
|
|
2768
|
+
let hasOpen = false;
|
|
2769
|
+
let hasBlocked = false;
|
|
2770
|
+
for (const token of getRequires(taskConfig)) {
|
|
2771
|
+
const producers = producerMap[token] || [];
|
|
2772
|
+
if (producers.length === 0) {
|
|
2773
|
+
openDeps.add(token);
|
|
2774
|
+
hasOpen = true;
|
|
2775
|
+
} else {
|
|
2776
|
+
const allFailed = producers.every((p) => {
|
|
2777
|
+
const ps = state.tasks[p];
|
|
2778
|
+
return ps?.status === TASK_STATUS.FAILED || ps?.status === "inactivated";
|
|
2779
|
+
});
|
|
2780
|
+
if (allFailed) hasBlocked = true;
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
if (hasOpen) unresolvedCount++;
|
|
2784
|
+
if (hasBlocked && !hasOpen) blockedCount++;
|
|
2785
|
+
}
|
|
2786
|
+
const conflictTokens = [];
|
|
2787
|
+
for (const [token, producers] of Object.entries(producerMap)) {
|
|
2788
|
+
if (producers.length > 1) conflictTokens.push(token);
|
|
2789
|
+
}
|
|
2790
|
+
const deps = buildTaskDeps2(graphTasks, producerMap);
|
|
2791
|
+
const cycles = detectCycles2(taskNames, deps);
|
|
2792
|
+
return {
|
|
2793
|
+
totalNodes: taskNames.length,
|
|
2794
|
+
running,
|
|
2795
|
+
completed,
|
|
2796
|
+
failed,
|
|
2797
|
+
waiting,
|
|
2798
|
+
notStarted,
|
|
2799
|
+
disabled,
|
|
2800
|
+
unresolvedCount,
|
|
2801
|
+
blockedCount,
|
|
2802
|
+
openDependencies: [...openDeps],
|
|
2803
|
+
cycles,
|
|
2804
|
+
conflictTokens
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
function buildTaskDeps2(tasks, producerMap) {
|
|
2808
|
+
const deps = {};
|
|
2809
|
+
for (const [name, config] of Object.entries(tasks)) {
|
|
2810
|
+
deps[name] = /* @__PURE__ */ new Set();
|
|
2811
|
+
for (const token of getRequires(config)) {
|
|
2812
|
+
for (const producer of producerMap[token] || []) {
|
|
2813
|
+
if (producer !== name) deps[name].add(producer);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
return deps;
|
|
2818
|
+
}
|
|
2819
|
+
function detectCycles2(taskNames, deps) {
|
|
2820
|
+
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
2821
|
+
const color = {};
|
|
2822
|
+
const parent = {};
|
|
2823
|
+
const cycles = [];
|
|
2824
|
+
for (const name of taskNames) {
|
|
2825
|
+
color[name] = WHITE;
|
|
2826
|
+
parent[name] = null;
|
|
2827
|
+
}
|
|
2828
|
+
function dfs(node) {
|
|
2829
|
+
color[node] = GRAY;
|
|
2830
|
+
for (const dep of deps[node] || []) {
|
|
2831
|
+
if (color[dep] === GRAY) {
|
|
2832
|
+
const cycle = [dep];
|
|
2833
|
+
let cur = node;
|
|
2834
|
+
while (cur !== dep) {
|
|
2835
|
+
cycle.push(cur);
|
|
2836
|
+
cur = parent[cur];
|
|
2837
|
+
}
|
|
2838
|
+
cycle.push(dep);
|
|
2839
|
+
cycle.reverse();
|
|
2840
|
+
cycles.push(cycle);
|
|
2841
|
+
} else if (color[dep] === WHITE) {
|
|
2842
|
+
parent[dep] = node;
|
|
2843
|
+
dfs(dep);
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
color[node] = BLACK;
|
|
2847
|
+
}
|
|
2848
|
+
for (const name of taskNames) {
|
|
2849
|
+
if (color[name] === WHITE) dfs(name);
|
|
2850
|
+
}
|
|
2851
|
+
return cycles;
|
|
2852
|
+
}
|
|
2853
|
+
function buildProducerMap4(tasks) {
|
|
2854
|
+
const map = {};
|
|
2855
|
+
for (const [name, config] of Object.entries(tasks)) {
|
|
2856
|
+
for (const token of getProvides(config)) {
|
|
2857
|
+
if (!map[token]) map[token] = [];
|
|
2858
|
+
map[token].push(name);
|
|
2859
|
+
}
|
|
2860
|
+
if (config.on) {
|
|
2861
|
+
for (const tokens of Object.values(config.on)) {
|
|
2862
|
+
for (const token of tokens) {
|
|
2863
|
+
if (!map[token]) map[token] = [];
|
|
2864
|
+
if (!map[token].includes(name)) map[token].push(name);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
if (config.on_failure) {
|
|
2869
|
+
for (const token of config.on_failure) {
|
|
2870
|
+
if (!map[token]) map[token] = [];
|
|
2871
|
+
if (!map[token].includes(name)) map[token].push(name);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
return map;
|
|
2876
|
+
}
|
|
2877
|
+
function getUnreachableTokens(live) {
|
|
2878
|
+
const { config, state } = live;
|
|
2879
|
+
const graphTasks = getAllTasks(config);
|
|
2880
|
+
const producerMap = buildProducerMap4(graphTasks);
|
|
2881
|
+
const available = /* @__PURE__ */ new Set([...state.availableOutputs]);
|
|
2882
|
+
for (const [taskName, taskState] of Object.entries(state.tasks)) {
|
|
2883
|
+
if (taskState.status === "completed") {
|
|
2884
|
+
const tc = graphTasks[taskName];
|
|
2885
|
+
if (tc) getProvides(tc).forEach((t) => available.add(t));
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
const allRequired = /* @__PURE__ */ new Set();
|
|
2889
|
+
for (const taskConfig of Object.values(graphTasks)) {
|
|
2890
|
+
for (const token of getRequires(taskConfig)) {
|
|
2891
|
+
allRequired.add(token);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
const unreachable = /* @__PURE__ */ new Set();
|
|
2895
|
+
const unreachableNodes = /* @__PURE__ */ new Set();
|
|
2896
|
+
for (const token of allRequired) {
|
|
2897
|
+
if (available.has(token)) continue;
|
|
2898
|
+
const producers = producerMap[token] || [];
|
|
2899
|
+
if (producers.length === 0) {
|
|
2900
|
+
unreachable.add(token);
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
let changed = true;
|
|
2904
|
+
while (changed) {
|
|
2905
|
+
changed = false;
|
|
2906
|
+
for (const [name, taskConfig] of Object.entries(graphTasks)) {
|
|
2907
|
+
if (unreachableNodes.has(name)) continue;
|
|
2908
|
+
const ts = state.tasks[name];
|
|
2909
|
+
if (ts?.status === "completed") continue;
|
|
2910
|
+
const isNonActive = isNonActiveTask(ts);
|
|
2911
|
+
const requires = getRequires(taskConfig);
|
|
2912
|
+
const hasUnreachableDep = requires.some((t) => unreachable.has(t));
|
|
2913
|
+
if (isNonActive || hasUnreachableDep) {
|
|
2914
|
+
if (!unreachableNodes.has(name)) {
|
|
2915
|
+
unreachableNodes.add(name);
|
|
2916
|
+
changed = true;
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
for (const token of allRequired) {
|
|
2921
|
+
if (unreachable.has(token) || available.has(token)) continue;
|
|
2922
|
+
const producers = producerMap[token] || [];
|
|
2923
|
+
const allProducersUnreachable = producers.length > 0 && producers.every((p) => unreachableNodes.has(p) || isNonActiveTask(state.tasks[p]));
|
|
2924
|
+
if (producers.length === 0 || allProducersUnreachable) {
|
|
2925
|
+
if (!unreachable.has(token)) {
|
|
2926
|
+
unreachable.add(token);
|
|
2927
|
+
changed = true;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
const tokens = [];
|
|
2933
|
+
for (const token of unreachable) {
|
|
2934
|
+
const producers = producerMap[token] || [];
|
|
2935
|
+
let reason;
|
|
2936
|
+
if (producers.length === 0) {
|
|
2937
|
+
reason = "no-producer";
|
|
2938
|
+
} else {
|
|
2939
|
+
const allFailed = producers.every((p) => isNonActiveTask(state.tasks[p]));
|
|
2940
|
+
reason = allFailed ? "all-producers-failed" : "transitive";
|
|
2941
|
+
}
|
|
2942
|
+
tokens.push({ token, reason, producers });
|
|
2943
|
+
}
|
|
2944
|
+
return { tokens };
|
|
2945
|
+
}
|
|
2946
|
+
function getUnreachableNodes(live) {
|
|
2947
|
+
const { config, state } = live;
|
|
2948
|
+
const graphTasks = getAllTasks(config);
|
|
2949
|
+
const { tokens: unreachableTokens } = getUnreachableTokens(live);
|
|
2950
|
+
const unreachableTokenSet = new Set(unreachableTokens.map((t) => t.token));
|
|
2951
|
+
const nodes = [];
|
|
2952
|
+
for (const [name, taskConfig] of Object.entries(graphTasks)) {
|
|
2953
|
+
const ts = state.tasks[name];
|
|
2954
|
+
if (ts?.status === "completed") continue;
|
|
2955
|
+
const requires = getRequires(taskConfig);
|
|
2956
|
+
const missingTokens = requires.filter((t) => unreachableTokenSet.has(t));
|
|
2957
|
+
if (missingTokens.length > 0) {
|
|
2958
|
+
nodes.push({ nodeName: name, missingTokens });
|
|
2959
|
+
} else if (isNonActiveTask(ts)) {
|
|
2960
|
+
nodes.push({ nodeName: name, missingTokens: [] });
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
return { nodes };
|
|
2964
|
+
}
|
|
2965
|
+
function getUpstream(live, nodeName) {
|
|
2966
|
+
const graphTasks = getAllTasks(live.config);
|
|
2967
|
+
if (!graphTasks[nodeName]) return { nodeName, nodes: [], tokens: [] };
|
|
2968
|
+
const producerMap = buildProducerMap4(graphTasks);
|
|
2969
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2970
|
+
const tokenSet = /* @__PURE__ */ new Set();
|
|
2971
|
+
const nodeEntries = /* @__PURE__ */ new Map();
|
|
2972
|
+
function walk(current) {
|
|
2973
|
+
const taskConfig = graphTasks[current];
|
|
2974
|
+
if (!taskConfig) return;
|
|
2975
|
+
for (const token of getRequires(taskConfig)) {
|
|
2976
|
+
const producers = producerMap[token] || [];
|
|
2977
|
+
for (const producer of producers) {
|
|
2978
|
+
if (producer === nodeName) continue;
|
|
2979
|
+
tokenSet.add(token);
|
|
2980
|
+
if (!nodeEntries.has(producer)) nodeEntries.set(producer, /* @__PURE__ */ new Set());
|
|
2981
|
+
nodeEntries.get(producer).add(token);
|
|
2982
|
+
if (!visited.has(producer)) {
|
|
2983
|
+
visited.add(producer);
|
|
2984
|
+
walk(producer);
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
walk(nodeName);
|
|
2990
|
+
const nodes = [...nodeEntries.entries()].map(([name, tokens]) => ({
|
|
2991
|
+
nodeName: name,
|
|
2992
|
+
providesTokens: [...tokens]
|
|
2993
|
+
}));
|
|
2994
|
+
return { nodeName, nodes, tokens: [...tokenSet] };
|
|
2995
|
+
}
|
|
2996
|
+
function getDownstream(live, nodeName) {
|
|
2997
|
+
const graphTasks = getAllTasks(live.config);
|
|
2998
|
+
if (!graphTasks[nodeName]) return { nodeName, nodes: [], tokens: [] };
|
|
2999
|
+
const consumerMap = {};
|
|
3000
|
+
for (const [name, config] of Object.entries(graphTasks)) {
|
|
3001
|
+
for (const token of getRequires(config)) {
|
|
3002
|
+
if (!consumerMap[token]) consumerMap[token] = [];
|
|
3003
|
+
consumerMap[token].push(name);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3007
|
+
const tokenSet = /* @__PURE__ */ new Set();
|
|
3008
|
+
const nodeEntries = /* @__PURE__ */ new Map();
|
|
3009
|
+
function walk(current) {
|
|
3010
|
+
const taskConfig = graphTasks[current];
|
|
3011
|
+
if (!taskConfig) return;
|
|
3012
|
+
for (const token of getProvides(taskConfig)) {
|
|
3013
|
+
const consumers = consumerMap[token] || [];
|
|
3014
|
+
for (const consumer of consumers) {
|
|
3015
|
+
if (consumer === nodeName) continue;
|
|
3016
|
+
tokenSet.add(token);
|
|
3017
|
+
if (!nodeEntries.has(consumer)) nodeEntries.set(consumer, /* @__PURE__ */ new Set());
|
|
3018
|
+
nodeEntries.get(consumer).add(token);
|
|
3019
|
+
if (!visited.has(consumer)) {
|
|
3020
|
+
visited.add(consumer);
|
|
3021
|
+
walk(consumer);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
walk(nodeName);
|
|
3027
|
+
const nodes = [...nodeEntries.entries()].map(([name, tokens]) => ({
|
|
3028
|
+
nodeName: name,
|
|
3029
|
+
requiresTokens: [...tokens]
|
|
3030
|
+
}));
|
|
3031
|
+
return { nodeName, nodes, tokens: [...tokenSet] };
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
// src/inference/core.ts
|
|
3035
|
+
var DEFAULT_THRESHOLD = 0.5;
|
|
3036
|
+
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.
|
|
3037
|
+
|
|
3038
|
+
For each task you analyze, provide a JSON response. Be conservative \u2014 only mark tasks as completed when the evidence strongly supports it.`;
|
|
3039
|
+
function buildInferencePrompt(live, options = {}) {
|
|
3040
|
+
const { scope, context, systemPrompt } = options;
|
|
3041
|
+
const graphTasks = getAllTasks(live.config);
|
|
3042
|
+
const { state } = live;
|
|
3043
|
+
const candidates = getAnalyzableCandidates(live, scope);
|
|
3044
|
+
if (candidates.length === 0) {
|
|
3045
|
+
return "";
|
|
3046
|
+
}
|
|
3047
|
+
const lines = [];
|
|
3048
|
+
lines.push(systemPrompt || DEFAULT_SYSTEM_PROMPT);
|
|
3049
|
+
lines.push("");
|
|
3050
|
+
lines.push("## Graph State");
|
|
3051
|
+
lines.push("");
|
|
3052
|
+
lines.push(`Available tokens: ${state.availableOutputs.length > 0 ? state.availableOutputs.join(", ") : "(none)"}`);
|
|
3053
|
+
lines.push("");
|
|
3054
|
+
const completedTasks = Object.entries(state.tasks).filter(([_, ts]) => ts.status === "completed").map(([name]) => name);
|
|
3055
|
+
if (completedTasks.length > 0) {
|
|
3056
|
+
lines.push(`Completed tasks: ${completedTasks.join(", ")}`);
|
|
3057
|
+
lines.push("");
|
|
3058
|
+
}
|
|
3059
|
+
lines.push("## Tasks to Analyze");
|
|
3060
|
+
lines.push("");
|
|
3061
|
+
for (const taskName of candidates) {
|
|
3062
|
+
const taskConfig = graphTasks[taskName];
|
|
3063
|
+
const taskState = state.tasks[taskName];
|
|
3064
|
+
lines.push(`### ${taskName}`);
|
|
3065
|
+
if (taskConfig.description) {
|
|
3066
|
+
lines.push(`Description: ${taskConfig.description}`);
|
|
3067
|
+
}
|
|
3068
|
+
const requires = getRequires(taskConfig);
|
|
3069
|
+
const provides = getProvides(taskConfig);
|
|
3070
|
+
if (requires.length > 0) lines.push(`Requires: ${requires.join(", ")}`);
|
|
3071
|
+
if (provides.length > 0) lines.push(`Provides: ${provides.join(", ")}`);
|
|
3072
|
+
lines.push(`Current status: ${taskState?.status || "not-started"}`);
|
|
3073
|
+
const hints = taskConfig.inference;
|
|
3074
|
+
if (hints) {
|
|
3075
|
+
if (hints.criteria) lines.push(`Completion criteria: ${hints.criteria}`);
|
|
3076
|
+
if (hints.keywords?.length) lines.push(`Keywords: ${hints.keywords.join(", ")}`);
|
|
3077
|
+
if (hints.suggestedChecks?.length) lines.push(`Suggested checks: ${hints.suggestedChecks.join("; ")}`);
|
|
3078
|
+
}
|
|
3079
|
+
lines.push("");
|
|
3080
|
+
}
|
|
3081
|
+
if (context) {
|
|
3082
|
+
lines.push("## Additional Context / Evidence");
|
|
3083
|
+
lines.push("");
|
|
3084
|
+
lines.push(context);
|
|
3085
|
+
lines.push("");
|
|
3086
|
+
}
|
|
3087
|
+
lines.push("## Response Format");
|
|
3088
|
+
lines.push("");
|
|
3089
|
+
lines.push("Respond with a JSON array of objects, one per task you have evidence for:");
|
|
3090
|
+
lines.push("```json");
|
|
3091
|
+
lines.push("[");
|
|
3092
|
+
lines.push(" {");
|
|
3093
|
+
lines.push(' "taskName": "task-name",');
|
|
3094
|
+
lines.push(' "confidence": 0.0 to 1.0,');
|
|
3095
|
+
lines.push(' "reasoning": "explanation of why you believe this task is complete or not"');
|
|
3096
|
+
lines.push(" }");
|
|
3097
|
+
lines.push("]");
|
|
3098
|
+
lines.push("```");
|
|
3099
|
+
lines.push("");
|
|
3100
|
+
lines.push("Rules:");
|
|
3101
|
+
lines.push('- Only include tasks from the "Tasks to Analyze" section');
|
|
3102
|
+
lines.push("- confidence 0.0 = no evidence of completion, 1.0 = certain it is complete");
|
|
3103
|
+
lines.push("- If you have no evidence for a task, omit it from the array");
|
|
3104
|
+
lines.push("- Be conservative \u2014 require clear evidence before high confidence");
|
|
3105
|
+
lines.push("- Respond ONLY with the JSON array, no additional text");
|
|
3106
|
+
return lines.join("\n");
|
|
3107
|
+
}
|
|
3108
|
+
async function inferCompletions(live, adapter, options = {}) {
|
|
3109
|
+
options.threshold ?? DEFAULT_THRESHOLD;
|
|
3110
|
+
const analyzedNodes = getAnalyzableCandidates(live, options.scope);
|
|
3111
|
+
if (analyzedNodes.length === 0) {
|
|
3112
|
+
return { suggestions: [], promptUsed: "", rawResponse: "", analyzedNodes: [] };
|
|
3113
|
+
}
|
|
3114
|
+
const prompt = buildInferencePrompt(live, options);
|
|
3115
|
+
const rawResponse = await adapter.analyze(prompt);
|
|
3116
|
+
const suggestions = parseInferenceResponse(rawResponse, analyzedNodes);
|
|
3117
|
+
return {
|
|
3118
|
+
suggestions,
|
|
3119
|
+
promptUsed: prompt,
|
|
3120
|
+
rawResponse,
|
|
3121
|
+
analyzedNodes
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
function applyInferences(live, result, threshold = DEFAULT_THRESHOLD) {
|
|
3125
|
+
let current = live;
|
|
3126
|
+
for (const suggestion of result.suggestions) {
|
|
3127
|
+
if (suggestion.confidence < threshold) continue;
|
|
3128
|
+
const taskState = current.state.tasks[suggestion.taskName];
|
|
3129
|
+
if (!taskState) continue;
|
|
3130
|
+
if (taskState.status === "completed" || taskState.status === "running") continue;
|
|
3131
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3132
|
+
current = applyEvent(current, {
|
|
3133
|
+
type: "task-started",
|
|
3134
|
+
taskName: suggestion.taskName,
|
|
3135
|
+
timestamp: now
|
|
3136
|
+
});
|
|
3137
|
+
current = applyEvent(current, {
|
|
3138
|
+
type: "task-completed",
|
|
3139
|
+
taskName: suggestion.taskName,
|
|
3140
|
+
timestamp: now,
|
|
3141
|
+
result: "llm-inferred"
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
3144
|
+
return current;
|
|
3145
|
+
}
|
|
3146
|
+
async function inferAndApply(live, adapter, options = {}) {
|
|
3147
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
3148
|
+
const inference = await inferCompletions(live, adapter, options);
|
|
3149
|
+
const updated = applyInferences(live, inference, threshold);
|
|
3150
|
+
const applied = inference.suggestions.filter((s) => s.confidence >= threshold);
|
|
3151
|
+
const skipped = inference.suggestions.filter((s) => s.confidence < threshold);
|
|
3152
|
+
return {
|
|
3153
|
+
live: updated,
|
|
3154
|
+
inference,
|
|
3155
|
+
applied,
|
|
3156
|
+
skipped
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
function getAnalyzableCandidates(live, scope) {
|
|
3160
|
+
const graphTasks = getAllTasks(live.config);
|
|
3161
|
+
const { state } = live;
|
|
3162
|
+
const candidates = [];
|
|
3163
|
+
for (const [name, config] of Object.entries(graphTasks)) {
|
|
3164
|
+
const taskState = state.tasks[name];
|
|
3165
|
+
if (taskState?.status === "completed" || taskState?.status === "running") continue;
|
|
3166
|
+
if (scope) {
|
|
3167
|
+
if (scope.includes(name)) candidates.push(name);
|
|
3168
|
+
} else {
|
|
3169
|
+
if (config.inference?.autoDetectable) candidates.push(name);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
return candidates;
|
|
3173
|
+
}
|
|
3174
|
+
function parseInferenceResponse(rawResponse, validNodes, _threshold) {
|
|
3175
|
+
const validSet = new Set(validNodes);
|
|
3176
|
+
try {
|
|
3177
|
+
const jsonStr = extractJson(rawResponse);
|
|
3178
|
+
if (!jsonStr) return [];
|
|
3179
|
+
const parsed = JSON.parse(jsonStr);
|
|
3180
|
+
if (!Array.isArray(parsed)) return [];
|
|
3181
|
+
const suggestions = [];
|
|
3182
|
+
for (const item of parsed) {
|
|
3183
|
+
if (!item || typeof item !== "object") continue;
|
|
3184
|
+
if (typeof item.taskName !== "string") continue;
|
|
3185
|
+
if (typeof item.confidence !== "number") continue;
|
|
3186
|
+
if (!validSet.has(item.taskName)) continue;
|
|
3187
|
+
const confidence = Math.max(0, Math.min(1, item.confidence));
|
|
3188
|
+
suggestions.push({
|
|
3189
|
+
taskName: item.taskName,
|
|
3190
|
+
confidence,
|
|
3191
|
+
reasoning: typeof item.reasoning === "string" ? item.reasoning : "",
|
|
3192
|
+
detectionMethod: "llm-inferred"
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
return suggestions;
|
|
3196
|
+
} catch {
|
|
3197
|
+
return [];
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
function extractJson(text) {
|
|
3201
|
+
if (!text || typeof text !== "string") return null;
|
|
3202
|
+
const trimmed = text.trim();
|
|
3203
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
3204
|
+
if (fenceMatch) return fenceMatch[1].trim();
|
|
3205
|
+
const firstBracket = trimmed.indexOf("[");
|
|
3206
|
+
const lastBracket = trimmed.lastIndexOf("]");
|
|
3207
|
+
if (firstBracket !== -1 && lastBracket > firstBracket) {
|
|
3208
|
+
return trimmed.slice(firstBracket, lastBracket + 1);
|
|
3209
|
+
}
|
|
3210
|
+
if (trimmed.startsWith("[")) return trimmed;
|
|
3211
|
+
return null;
|
|
3212
|
+
}
|
|
3213
|
+
function createCliAdapter(opts) {
|
|
3214
|
+
const timeout = opts.timeout ?? 6e4;
|
|
3215
|
+
return {
|
|
3216
|
+
analyze: (prompt) => {
|
|
3217
|
+
return new Promise((resolve, reject) => {
|
|
3218
|
+
const args = opts.args(prompt);
|
|
3219
|
+
const child = child_process.execFile(
|
|
3220
|
+
opts.command,
|
|
3221
|
+
opts.stdin ? opts.args("") : args,
|
|
3222
|
+
{
|
|
3223
|
+
timeout,
|
|
3224
|
+
cwd: opts.cwd,
|
|
3225
|
+
env: opts.env ? { ...process.env, ...opts.env } : void 0,
|
|
3226
|
+
maxBuffer: 10 * 1024 * 1024
|
|
3227
|
+
// 10MB
|
|
3228
|
+
},
|
|
3229
|
+
(error, stdout, stderr) => {
|
|
3230
|
+
if (error) {
|
|
3231
|
+
reject(new Error(
|
|
3232
|
+
`CLI adapter failed: ${opts.command} exited with ${error.code ?? "error"}` + (stderr ? `
|
|
3233
|
+
stderr: ${stderr.slice(0, 500)}` : "") + `
|
|
3234
|
+
${error.message}`
|
|
3235
|
+
));
|
|
3236
|
+
} else {
|
|
3237
|
+
resolve(stdout);
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
);
|
|
3241
|
+
if (opts.stdin && child.stdin) {
|
|
3242
|
+
child.stdin.write(prompt);
|
|
3243
|
+
child.stdin.end();
|
|
3244
|
+
}
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
};
|
|
3248
|
+
}
|
|
3249
|
+
function createHttpAdapter(opts) {
|
|
3250
|
+
const timeout = opts.timeout ?? 6e4;
|
|
3251
|
+
return {
|
|
3252
|
+
analyze: async (prompt) => {
|
|
3253
|
+
const body = opts.buildBody ? opts.buildBody(prompt) : { prompt };
|
|
3254
|
+
const controller = new AbortController();
|
|
3255
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
3256
|
+
try {
|
|
3257
|
+
const response = await fetch(opts.url, {
|
|
3258
|
+
method: "POST",
|
|
3259
|
+
headers: {
|
|
3260
|
+
"Content-Type": "application/json",
|
|
3261
|
+
...opts.headers ?? {}
|
|
3262
|
+
},
|
|
3263
|
+
body: JSON.stringify(body),
|
|
3264
|
+
signal: controller.signal
|
|
3265
|
+
});
|
|
3266
|
+
if (!response.ok) {
|
|
3267
|
+
const text = await response.text().catch(() => "");
|
|
3268
|
+
throw new Error(`HTTP ${response.status}: ${text.slice(0, 500)}`);
|
|
3269
|
+
}
|
|
3270
|
+
const json = await response.json();
|
|
3271
|
+
if (opts.extractResponse) {
|
|
3272
|
+
return opts.extractResponse(json);
|
|
3273
|
+
}
|
|
3274
|
+
if (typeof json.response === "string") return json.response;
|
|
3275
|
+
if (typeof json.text === "string") return json.text;
|
|
3276
|
+
if (typeof json.content === "string") return json.content;
|
|
3277
|
+
return JSON.stringify(json);
|
|
3278
|
+
} finally {
|
|
3279
|
+
clearTimeout(timer);
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
|
|
2305
3285
|
exports.COMPLETION_STRATEGIES = COMPLETION_STRATEGIES;
|
|
2306
3286
|
exports.CONFLICT_STRATEGIES = CONFLICT_STRATEGIES;
|
|
2307
3287
|
exports.DEFAULTS = DEFAULTS;
|
|
@@ -2314,30 +3294,51 @@ exports.MemoryStore = MemoryStore;
|
|
|
2314
3294
|
exports.StepMachine = StepMachine;
|
|
2315
3295
|
exports.TASK_STATUS = TASK_STATUS;
|
|
2316
3296
|
exports.addDynamicTask = addDynamicTask;
|
|
3297
|
+
exports.addNode = addNode;
|
|
3298
|
+
exports.addProvides = addProvides;
|
|
3299
|
+
exports.addRequires = addRequires;
|
|
2317
3300
|
exports.apply = apply;
|
|
2318
3301
|
exports.applyAll = applyAll;
|
|
3302
|
+
exports.applyEvent = applyEvent;
|
|
3303
|
+
exports.applyInferences = applyInferences;
|
|
2319
3304
|
exports.applyStepResult = applyStepResult;
|
|
2320
3305
|
exports.batch = batch;
|
|
3306
|
+
exports.buildInferencePrompt = buildInferencePrompt;
|
|
2321
3307
|
exports.checkCircuitBreaker = checkCircuitBreaker;
|
|
2322
3308
|
exports.computeAvailableOutputs = computeAvailableOutputs;
|
|
2323
3309
|
exports.computeStepInput = computeStepInput;
|
|
3310
|
+
exports.createCliAdapter = createCliAdapter;
|
|
2324
3311
|
exports.createDefaultTaskState = createDefaultTaskState;
|
|
2325
3312
|
exports.createEngine = createStepMachine;
|
|
3313
|
+
exports.createHttpAdapter = createHttpAdapter;
|
|
2326
3314
|
exports.createInitialExecutionState = createInitialExecutionState;
|
|
2327
3315
|
exports.createInitialState = createInitialState;
|
|
3316
|
+
exports.createLiveGraph = createLiveGraph;
|
|
2328
3317
|
exports.createStepMachine = createStepMachine;
|
|
2329
3318
|
exports.detectStuckState = detectStuckState;
|
|
3319
|
+
exports.disableNode = disableNode;
|
|
3320
|
+
exports.drainTokens = drainTokens;
|
|
3321
|
+
exports.enableNode = enableNode;
|
|
2330
3322
|
exports.exportGraphConfig = exportGraphConfig;
|
|
2331
3323
|
exports.exportGraphConfigToFile = exportGraphConfigToFile;
|
|
2332
3324
|
exports.extractReturnData = extractReturnData;
|
|
2333
3325
|
exports.flowToMermaid = flowToMermaid;
|
|
2334
3326
|
exports.getAllTasks = getAllTasks;
|
|
2335
3327
|
exports.getCandidateTasks = getCandidateTasks;
|
|
3328
|
+
exports.getDownstream = getDownstream;
|
|
3329
|
+
exports.getNode = getNode;
|
|
2336
3330
|
exports.getProvides = getProvides;
|
|
2337
3331
|
exports.getRequires = getRequires;
|
|
2338
3332
|
exports.getTask = getTask;
|
|
3333
|
+
exports.getUnreachableNodes = getUnreachableNodes;
|
|
3334
|
+
exports.getUnreachableTokens = getUnreachableTokens;
|
|
3335
|
+
exports.getUpstream = getUpstream;
|
|
2339
3336
|
exports.graphToMermaid = graphToMermaid;
|
|
2340
3337
|
exports.hasTask = hasTask;
|
|
3338
|
+
exports.inferAndApply = inferAndApply;
|
|
3339
|
+
exports.inferCompletions = inferCompletions;
|
|
3340
|
+
exports.injectTokens = injectTokens;
|
|
3341
|
+
exports.inspect = inspect;
|
|
2341
3342
|
exports.isExecutionComplete = isExecutionComplete;
|
|
2342
3343
|
exports.isNonActiveTask = isNonActiveTask;
|
|
2343
3344
|
exports.isRepeatableTask = isRepeatableTask;
|
|
@@ -2347,8 +3348,15 @@ exports.loadGraphConfig = loadGraphConfig;
|
|
|
2347
3348
|
exports.loadStepFlow = loadStepFlow;
|
|
2348
3349
|
exports.next = next;
|
|
2349
3350
|
exports.planExecution = planExecution;
|
|
3351
|
+
exports.removeNode = removeNode;
|
|
3352
|
+
exports.removeProvides = removeProvides;
|
|
3353
|
+
exports.removeRequires = removeRequires;
|
|
3354
|
+
exports.resetNode = resetNode;
|
|
2350
3355
|
exports.resolveConfigTemplates = resolveConfigTemplates;
|
|
2351
3356
|
exports.resolveVariables = resolveVariables;
|
|
3357
|
+
exports.restore = restore;
|
|
3358
|
+
exports.schedule = schedule;
|
|
3359
|
+
exports.snapshot = snapshot;
|
|
2352
3360
|
exports.validateGraph = validateGraph;
|
|
2353
3361
|
exports.validateGraphConfig = validateGraphConfig;
|
|
2354
3362
|
exports.validateStepFlowConfig = validateStepFlowConfig;
|