yaml-flow 2.8.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -3
- package/dist/{constants-BEbO2_OK.d.ts → constants-B2zqu10b.d.ts} +32 -52
- package/dist/{constants-BNjeIlZ8.d.cts → constants-DJZU1pwJ.d.cts} +32 -52
- package/dist/continuous-event-graph/index.cjs +1388 -52
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +643 -5
- package/dist/continuous-event-graph/index.d.ts +643 -5
- package/dist/continuous-event-graph/index.js +1374 -53
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/event-graph/index.cjs +6817 -53
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.d.cts +15 -6
- package/dist/event-graph/index.d.ts +15 -6
- package/dist/event-graph/index.js +6808 -51
- package/dist/event-graph/index.js.map +1 -1
- package/dist/index.cjs +1827 -441
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +1808 -440
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +46 -13
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.d.cts +2 -2
- package/dist/inference/index.d.ts +2 -2
- package/dist/inference/index.js +46 -13
- package/dist/inference/index.js.map +1 -1
- package/dist/step-machine/index.cjs +6600 -0
- package/dist/step-machine/index.cjs.map +1 -1
- package/dist/step-machine/index.d.cts +26 -1
- package/dist/step-machine/index.d.ts +26 -1
- package/dist/step-machine/index.js +6596 -1
- package/dist/step-machine/index.js.map +1 -1
- package/dist/{types-C2lOwquM.d.cts → types-BwvgvlOO.d.cts} +2 -2
- package/dist/{types-mS_pPftm.d.ts → types-ClRA8hzC.d.ts} +2 -2
- package/dist/{types-DAI_a2as.d.ts → types-DEj7OakX.d.cts} +29 -10
- package/dist/{types-DAI_a2as.d.cts → types-DEj7OakX.d.ts} +29 -10
- package/dist/validate-DEZ2Ymdb.d.ts +53 -0
- package/dist/validate-DqKTZg_o.d.cts +53 -0
- package/package.json +1 -1
- package/schema/event-graph.schema.json +254 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { execFile } from 'child_process';
|
|
2
1
|
import addFormats from 'ajv-formats';
|
|
2
|
+
import { existsSync, writeFileSync, appendFileSync, readFileSync } from 'fs';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { exec, execFile } from 'child_process';
|
|
3
5
|
|
|
4
6
|
var __create = Object.create;
|
|
5
7
|
var __defProp = Object.defineProperty;
|
|
@@ -3911,7 +3913,7 @@ var require_core = __commonJS({
|
|
|
3911
3913
|
uriResolver
|
|
3912
3914
|
};
|
|
3913
3915
|
}
|
|
3914
|
-
var
|
|
3916
|
+
var Ajv4 = class {
|
|
3915
3917
|
constructor(opts = {}) {
|
|
3916
3918
|
this.schemas = {};
|
|
3917
3919
|
this.refs = {};
|
|
@@ -4281,9 +4283,9 @@ var require_core = __commonJS({
|
|
|
4281
4283
|
}
|
|
4282
4284
|
}
|
|
4283
4285
|
};
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
exports$1.default =
|
|
4286
|
+
Ajv4.ValidationError = validation_error_1.default;
|
|
4287
|
+
Ajv4.MissingRefError = ref_error_1.default;
|
|
4288
|
+
exports$1.default = Ajv4;
|
|
4287
4289
|
function checkOptions(checkOpts, options, msg, log = "error") {
|
|
4288
4290
|
for (const key in checkOpts) {
|
|
4289
4291
|
const opt = key;
|
|
@@ -6354,7 +6356,7 @@ var require_ajv = __commonJS({
|
|
|
6354
6356
|
var draft7MetaSchema = require_json_schema_draft_07();
|
|
6355
6357
|
var META_SUPPORT_DATA = ["/properties"];
|
|
6356
6358
|
var META_SCHEMA_ID = "http://json-schema.org/draft-07/schema";
|
|
6357
|
-
var
|
|
6359
|
+
var Ajv4 = class extends core_1.default {
|
|
6358
6360
|
_addVocabularies() {
|
|
6359
6361
|
super._addVocabularies();
|
|
6360
6362
|
draft7_1.default.forEach((v) => this.addVocabulary(v));
|
|
@@ -6373,11 +6375,11 @@ var require_ajv = __commonJS({
|
|
|
6373
6375
|
return this.opts.defaultMeta = super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : void 0);
|
|
6374
6376
|
}
|
|
6375
6377
|
};
|
|
6376
|
-
exports$1.Ajv =
|
|
6377
|
-
module.exports = exports$1 =
|
|
6378
|
-
module.exports.Ajv =
|
|
6378
|
+
exports$1.Ajv = Ajv4;
|
|
6379
|
+
module.exports = exports$1 = Ajv4;
|
|
6380
|
+
module.exports.Ajv = Ajv4;
|
|
6379
6381
|
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
6380
|
-
exports$1.default =
|
|
6382
|
+
exports$1.default = Ajv4;
|
|
6381
6383
|
var validate_1 = require_validate();
|
|
6382
6384
|
Object.defineProperty(exports$1, "KeywordCxt", { enumerable: true, get: function() {
|
|
6383
6385
|
return validate_1.KeywordCxt;
|
|
@@ -6911,6 +6913,188 @@ async function loadStepFlow(source) {
|
|
|
6911
6913
|
return flow;
|
|
6912
6914
|
}
|
|
6913
6915
|
|
|
6916
|
+
// schema/flow.schema.json
|
|
6917
|
+
var flow_schema_default = {
|
|
6918
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
6919
|
+
$id: "https://github.com/yaml-flow/schema/flow.json",
|
|
6920
|
+
title: "YamlFlow Configuration",
|
|
6921
|
+
description: "Schema for yaml-flow workflow definitions",
|
|
6922
|
+
type: "object",
|
|
6923
|
+
required: ["settings", "steps", "terminal_states"],
|
|
6924
|
+
properties: {
|
|
6925
|
+
id: {
|
|
6926
|
+
type: "string",
|
|
6927
|
+
description: "Optional flow identifier"
|
|
6928
|
+
},
|
|
6929
|
+
settings: {
|
|
6930
|
+
type: "object",
|
|
6931
|
+
description: "Flow-level settings",
|
|
6932
|
+
required: ["start_step"],
|
|
6933
|
+
properties: {
|
|
6934
|
+
start_step: {
|
|
6935
|
+
type: "string",
|
|
6936
|
+
description: "Step to start execution from"
|
|
6937
|
+
},
|
|
6938
|
+
max_total_steps: {
|
|
6939
|
+
type: "integer",
|
|
6940
|
+
minimum: 1,
|
|
6941
|
+
default: 100,
|
|
6942
|
+
description: "Maximum steps before forced termination"
|
|
6943
|
+
},
|
|
6944
|
+
timeout_ms: {
|
|
6945
|
+
type: "integer",
|
|
6946
|
+
minimum: 0,
|
|
6947
|
+
description: "Flow timeout in milliseconds"
|
|
6948
|
+
}
|
|
6949
|
+
},
|
|
6950
|
+
additionalProperties: false
|
|
6951
|
+
},
|
|
6952
|
+
steps: {
|
|
6953
|
+
type: "object",
|
|
6954
|
+
description: "Step definitions",
|
|
6955
|
+
minProperties: 1,
|
|
6956
|
+
additionalProperties: {
|
|
6957
|
+
$ref: "#/definitions/step"
|
|
6958
|
+
}
|
|
6959
|
+
},
|
|
6960
|
+
terminal_states: {
|
|
6961
|
+
type: "object",
|
|
6962
|
+
description: "Terminal state definitions",
|
|
6963
|
+
minProperties: 1,
|
|
6964
|
+
additionalProperties: {
|
|
6965
|
+
$ref: "#/definitions/terminal_state"
|
|
6966
|
+
}
|
|
6967
|
+
}
|
|
6968
|
+
},
|
|
6969
|
+
additionalProperties: false,
|
|
6970
|
+
definitions: {
|
|
6971
|
+
step: {
|
|
6972
|
+
type: "object",
|
|
6973
|
+
description: "Individual step configuration",
|
|
6974
|
+
required: ["transitions"],
|
|
6975
|
+
properties: {
|
|
6976
|
+
description: {
|
|
6977
|
+
type: "string",
|
|
6978
|
+
description: "Human-readable description"
|
|
6979
|
+
},
|
|
6980
|
+
expects_data: {
|
|
6981
|
+
type: "array",
|
|
6982
|
+
items: { type: "string" },
|
|
6983
|
+
description: "Data keys this step expects as input"
|
|
6984
|
+
},
|
|
6985
|
+
produces_data: {
|
|
6986
|
+
type: "array",
|
|
6987
|
+
items: { type: "string" },
|
|
6988
|
+
description: "Data keys this step produces as output"
|
|
6989
|
+
},
|
|
6990
|
+
transitions: {
|
|
6991
|
+
type: "object",
|
|
6992
|
+
description: "Mapping of result -> next step name",
|
|
6993
|
+
additionalProperties: { type: "string" },
|
|
6994
|
+
minProperties: 1
|
|
6995
|
+
},
|
|
6996
|
+
retry: {
|
|
6997
|
+
$ref: "#/definitions/retry_config"
|
|
6998
|
+
},
|
|
6999
|
+
circuit_breaker: {
|
|
7000
|
+
$ref: "#/definitions/circuit_breaker_config"
|
|
7001
|
+
}
|
|
7002
|
+
},
|
|
7003
|
+
additionalProperties: false
|
|
7004
|
+
},
|
|
7005
|
+
terminal_state: {
|
|
7006
|
+
type: "object",
|
|
7007
|
+
description: "Terminal state configuration",
|
|
7008
|
+
required: ["return_intent"],
|
|
7009
|
+
properties: {
|
|
7010
|
+
description: {
|
|
7011
|
+
type: "string",
|
|
7012
|
+
description: "Human-readable description"
|
|
7013
|
+
},
|
|
7014
|
+
return_intent: {
|
|
7015
|
+
type: "string",
|
|
7016
|
+
description: "Intent/status to return (e.g., 'success', 'error')"
|
|
7017
|
+
},
|
|
7018
|
+
return_artifacts: {
|
|
7019
|
+
oneOf: [
|
|
7020
|
+
{ type: "string" },
|
|
7021
|
+
{ type: "array", items: { type: "string" } },
|
|
7022
|
+
{ type: "boolean", const: false }
|
|
7023
|
+
],
|
|
7024
|
+
description: "Data key(s) to include in result, or false to exclude"
|
|
7025
|
+
},
|
|
7026
|
+
expects_data: {
|
|
7027
|
+
type: "array",
|
|
7028
|
+
items: { type: "string" },
|
|
7029
|
+
description: "Data keys this terminal state expects"
|
|
7030
|
+
}
|
|
7031
|
+
},
|
|
7032
|
+
additionalProperties: false
|
|
7033
|
+
},
|
|
7034
|
+
retry_config: {
|
|
7035
|
+
type: "object",
|
|
7036
|
+
description: "Retry configuration for step failures",
|
|
7037
|
+
required: ["max_attempts"],
|
|
7038
|
+
properties: {
|
|
7039
|
+
max_attempts: {
|
|
7040
|
+
type: "integer",
|
|
7041
|
+
minimum: 1,
|
|
7042
|
+
description: "Maximum retry attempts"
|
|
7043
|
+
},
|
|
7044
|
+
delay_ms: {
|
|
7045
|
+
type: "integer",
|
|
7046
|
+
minimum: 0,
|
|
7047
|
+
description: "Delay between retries in ms"
|
|
7048
|
+
},
|
|
7049
|
+
backoff_multiplier: {
|
|
7050
|
+
type: "number",
|
|
7051
|
+
minimum: 1,
|
|
7052
|
+
description: "Backoff multiplier (e.g., 2 for exponential)"
|
|
7053
|
+
}
|
|
7054
|
+
},
|
|
7055
|
+
additionalProperties: false
|
|
7056
|
+
},
|
|
7057
|
+
circuit_breaker_config: {
|
|
7058
|
+
type: "object",
|
|
7059
|
+
description: "Circuit breaker configuration for loops",
|
|
7060
|
+
required: ["max_iterations", "on_open"],
|
|
7061
|
+
properties: {
|
|
7062
|
+
max_iterations: {
|
|
7063
|
+
type: "integer",
|
|
7064
|
+
minimum: 1,
|
|
7065
|
+
description: "Maximum iterations before circuit opens"
|
|
7066
|
+
},
|
|
7067
|
+
on_open: {
|
|
7068
|
+
type: "string",
|
|
7069
|
+
description: "Step to transition to when circuit opens"
|
|
7070
|
+
}
|
|
7071
|
+
},
|
|
7072
|
+
additionalProperties: false
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
};
|
|
7076
|
+
|
|
7077
|
+
// src/step-machine/schema-validator.ts
|
|
7078
|
+
var import_ajv = __toESM(require_ajv());
|
|
7079
|
+
var _compiled = null;
|
|
7080
|
+
function getValidator() {
|
|
7081
|
+
if (_compiled) return _compiled;
|
|
7082
|
+
const ajv = new import_ajv.default({ allErrors: true });
|
|
7083
|
+
addFormats(ajv);
|
|
7084
|
+
_compiled = ajv.compile(flow_schema_default);
|
|
7085
|
+
return _compiled;
|
|
7086
|
+
}
|
|
7087
|
+
function validateFlowSchema(config) {
|
|
7088
|
+
const validate = getValidator();
|
|
7089
|
+
const valid = validate(config);
|
|
7090
|
+
if (valid) return { ok: true, errors: [] };
|
|
7091
|
+
const errors = (validate.errors ?? []).map((e) => {
|
|
7092
|
+
const path = e.instancePath || "/";
|
|
7093
|
+
return `${path}: ${e.message ?? "unknown error"}`;
|
|
7094
|
+
});
|
|
7095
|
+
return { ok: false, errors };
|
|
7096
|
+
}
|
|
7097
|
+
|
|
6914
7098
|
// src/event-graph/constants.ts
|
|
6915
7099
|
var TASK_STATUS = {
|
|
6916
7100
|
NOT_STARTED: "not-started",
|
|
@@ -6987,15 +7171,14 @@ function isTaskCompleted(taskState) {
|
|
|
6987
7171
|
function isTaskRunning(taskState) {
|
|
6988
7172
|
return taskState?.status === TASK_STATUS.RUNNING;
|
|
6989
7173
|
}
|
|
6990
|
-
function
|
|
6991
|
-
return taskConfig.
|
|
7174
|
+
function getRefreshStrategy(taskConfig, graphSettings) {
|
|
7175
|
+
return taskConfig.refreshStrategy ?? graphSettings?.refreshStrategy ?? "data-changed";
|
|
6992
7176
|
}
|
|
6993
|
-
function
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
return void 0;
|
|
7177
|
+
function isRerunnable(taskConfig, graphSettings) {
|
|
7178
|
+
return getRefreshStrategy(taskConfig, graphSettings) !== "once";
|
|
7179
|
+
}
|
|
7180
|
+
function getMaxExecutions(taskConfig) {
|
|
7181
|
+
return taskConfig.maxExecutions;
|
|
6999
7182
|
}
|
|
7000
7183
|
function computeAvailableOutputs(graph, taskStates) {
|
|
7001
7184
|
const outputs = /* @__PURE__ */ new Set();
|
|
@@ -7043,7 +7226,7 @@ function addDynamicTask(graph, taskName, taskConfig) {
|
|
|
7043
7226
|
}
|
|
7044
7227
|
};
|
|
7045
7228
|
}
|
|
7046
|
-
function
|
|
7229
|
+
function createDefaultGraphEngineStore() {
|
|
7047
7230
|
return {
|
|
7048
7231
|
status: "not-started",
|
|
7049
7232
|
executionCount: 0,
|
|
@@ -7056,7 +7239,7 @@ function createDefaultTaskState() {
|
|
|
7056
7239
|
function createInitialExecutionState(graph, executionId) {
|
|
7057
7240
|
const tasks = {};
|
|
7058
7241
|
for (const taskName of Object.keys(graph.tasks)) {
|
|
7059
|
-
tasks[taskName] =
|
|
7242
|
+
tasks[taskName] = createDefaultGraphEngineStore();
|
|
7060
7243
|
}
|
|
7061
7244
|
return {
|
|
7062
7245
|
status: "running",
|
|
@@ -7427,48 +7610,88 @@ function getCandidateTasks(graph, state) {
|
|
|
7427
7610
|
const candidates = [];
|
|
7428
7611
|
for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
|
|
7429
7612
|
const taskState = state.tasks[taskName];
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
}
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7613
|
+
const strategy = getRefreshStrategy(taskConfig, graph.settings);
|
|
7614
|
+
const rerunnable = strategy !== "once";
|
|
7615
|
+
if (taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
|
|
7616
|
+
continue;
|
|
7617
|
+
}
|
|
7618
|
+
const maxExec = getMaxExecutions(taskConfig);
|
|
7619
|
+
if (maxExec !== void 0 && taskState && taskState.executionCount >= maxExec) {
|
|
7620
|
+
continue;
|
|
7621
|
+
}
|
|
7622
|
+
if (taskConfig.circuit_breaker && taskState && taskState.executionCount >= taskConfig.circuit_breaker.max_executions) {
|
|
7623
|
+
continue;
|
|
7624
|
+
}
|
|
7625
|
+
if (!rerunnable) {
|
|
7626
|
+
if (taskState?.status === TASK_STATUS.COMPLETED) {
|
|
7440
7627
|
continue;
|
|
7441
7628
|
}
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7629
|
+
}
|
|
7630
|
+
if (rerunnable && taskState?.status === TASK_STATUS.COMPLETED) {
|
|
7631
|
+
const requires2 = getRequires(taskConfig);
|
|
7632
|
+
switch (strategy) {
|
|
7633
|
+
case "data-changed": {
|
|
7634
|
+
if (requires2.length > 0) {
|
|
7635
|
+
const hasChangedData = requires2.some((req) => {
|
|
7636
|
+
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
7637
|
+
if (getProvides(otherConfig).includes(req)) {
|
|
7638
|
+
const otherState = state.tasks[otherName];
|
|
7639
|
+
if (!otherState) continue;
|
|
7640
|
+
const consumed = taskState.lastConsumedHashes?.[req];
|
|
7641
|
+
if (otherState.lastDataHash == null) {
|
|
7642
|
+
return otherState.executionCount > taskState.lastEpoch;
|
|
7643
|
+
}
|
|
7644
|
+
return otherState.lastDataHash !== consumed;
|
|
7645
|
+
}
|
|
7646
|
+
}
|
|
7647
|
+
return false;
|
|
7648
|
+
});
|
|
7649
|
+
if (!hasChangedData) continue;
|
|
7650
|
+
} else {
|
|
7651
|
+
continue;
|
|
7652
|
+
}
|
|
7653
|
+
break;
|
|
7445
7654
|
}
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
return true;
|
|
7655
|
+
case "epoch-changed": {
|
|
7656
|
+
if (requires2.length > 0) {
|
|
7657
|
+
const hasRefreshedInputs = requires2.some((req) => {
|
|
7658
|
+
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
7659
|
+
if (getProvides(otherConfig).includes(req)) {
|
|
7660
|
+
const otherState = state.tasks[otherName];
|
|
7661
|
+
if (otherState && otherState.executionCount > taskState.lastEpoch) {
|
|
7662
|
+
return true;
|
|
7663
|
+
}
|
|
7456
7664
|
}
|
|
7457
7665
|
}
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7666
|
+
return false;
|
|
7667
|
+
});
|
|
7668
|
+
if (!hasRefreshedInputs) continue;
|
|
7669
|
+
} else {
|
|
7670
|
+
continue;
|
|
7671
|
+
}
|
|
7672
|
+
break;
|
|
7673
|
+
}
|
|
7674
|
+
case "time-based": {
|
|
7675
|
+
const interval = taskConfig.refreshInterval ?? 0;
|
|
7676
|
+
if (interval <= 0) continue;
|
|
7677
|
+
const completedAt = taskState.completedAt;
|
|
7678
|
+
if (!completedAt) continue;
|
|
7679
|
+
const elapsedSec = (Date.now() - Date.parse(completedAt)) / 1e3;
|
|
7680
|
+
if (elapsedSec < interval) continue;
|
|
7681
|
+
break;
|
|
7682
|
+
}
|
|
7683
|
+
case "manual": {
|
|
7463
7684
|
continue;
|
|
7464
7685
|
}
|
|
7686
|
+
default:
|
|
7687
|
+
continue;
|
|
7465
7688
|
}
|
|
7466
7689
|
}
|
|
7467
7690
|
const requires = getRequires(taskConfig);
|
|
7468
7691
|
if (!requires.every((req) => availableOutputs.includes(req))) {
|
|
7469
7692
|
continue;
|
|
7470
7693
|
}
|
|
7471
|
-
if (!
|
|
7694
|
+
if (!rerunnable) {
|
|
7472
7695
|
const provides = getProvides(taskConfig);
|
|
7473
7696
|
const allAlreadyAvailable = provides.length > 0 && provides.every((output) => availableOutputs.includes(output));
|
|
7474
7697
|
if (allAlreadyAvailable) continue;
|
|
@@ -7537,7 +7760,7 @@ function selectOptimalTasks(candidates, graph, state, conflictStrategy) {
|
|
|
7537
7760
|
|
|
7538
7761
|
// src/event-graph/task-transitions.ts
|
|
7539
7762
|
function applyTaskStart(state, taskName) {
|
|
7540
|
-
const existingTask = state.tasks[taskName] ??
|
|
7763
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7541
7764
|
const updatedTask = {
|
|
7542
7765
|
...existingTask,
|
|
7543
7766
|
status: "running",
|
|
@@ -7552,8 +7775,8 @@ function applyTaskStart(state, taskName) {
|
|
|
7552
7775
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7553
7776
|
};
|
|
7554
7777
|
}
|
|
7555
|
-
function applyTaskCompletion(state, graph, taskName, result) {
|
|
7556
|
-
const existingTask = state.tasks[taskName] ??
|
|
7778
|
+
function applyTaskCompletion(state, graph, taskName, result, dataHash, data) {
|
|
7779
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7557
7780
|
const taskConfig = graph.tasks[taskName];
|
|
7558
7781
|
if (!taskConfig) {
|
|
7559
7782
|
throw new Error(`Task "${taskName}" not found in graph`);
|
|
@@ -7564,6 +7787,19 @@ function applyTaskCompletion(state, graph, taskName, result) {
|
|
|
7564
7787
|
} else {
|
|
7565
7788
|
outputTokens = getProvides(taskConfig);
|
|
7566
7789
|
}
|
|
7790
|
+
const lastConsumedHashes = { ...existingTask.lastConsumedHashes };
|
|
7791
|
+
const requires = taskConfig.requires ?? [];
|
|
7792
|
+
for (const token of requires) {
|
|
7793
|
+
for (const [otherName, otherConfig] of Object.entries(graph.tasks)) {
|
|
7794
|
+
if (getProvides(otherConfig).includes(token)) {
|
|
7795
|
+
const otherState = state.tasks[otherName];
|
|
7796
|
+
if (otherState?.lastDataHash) {
|
|
7797
|
+
lastConsumedHashes[token] = otherState.lastDataHash;
|
|
7798
|
+
}
|
|
7799
|
+
break;
|
|
7800
|
+
}
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
7567
7803
|
const updatedTask = {
|
|
7568
7804
|
...existingTask,
|
|
7569
7805
|
status: "completed",
|
|
@@ -7571,11 +7807,11 @@ function applyTaskCompletion(state, graph, taskName, result) {
|
|
|
7571
7807
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7572
7808
|
executionCount: existingTask.executionCount + 1,
|
|
7573
7809
|
lastEpoch: existingTask.executionCount + 1,
|
|
7810
|
+
lastDataHash: dataHash,
|
|
7811
|
+
data,
|
|
7812
|
+
lastConsumedHashes,
|
|
7574
7813
|
error: void 0
|
|
7575
7814
|
};
|
|
7576
|
-
if (isRepeatableTask(taskConfig)) {
|
|
7577
|
-
updatedTask.status = "not-started";
|
|
7578
|
-
}
|
|
7579
7815
|
const newOutputs = [.../* @__PURE__ */ new Set([...state.availableOutputs, ...outputTokens])];
|
|
7580
7816
|
return {
|
|
7581
7817
|
...state,
|
|
@@ -7585,7 +7821,7 @@ function applyTaskCompletion(state, graph, taskName, result) {
|
|
|
7585
7821
|
};
|
|
7586
7822
|
}
|
|
7587
7823
|
function applyTaskFailure(state, graph, taskName, error) {
|
|
7588
|
-
const existingTask = state.tasks[taskName] ??
|
|
7824
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7589
7825
|
const taskConfig = graph.tasks[taskName];
|
|
7590
7826
|
if (taskConfig?.retry) {
|
|
7591
7827
|
const retryCount = existingTask.retryCount + 1;
|
|
@@ -7628,7 +7864,7 @@ function applyTaskFailure(state, graph, taskName, error) {
|
|
|
7628
7864
|
};
|
|
7629
7865
|
}
|
|
7630
7866
|
function applyTaskProgress(state, taskName, message, progress) {
|
|
7631
|
-
const existingTask = state.tasks[taskName] ??
|
|
7867
|
+
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore2();
|
|
7632
7868
|
const updatedTask = {
|
|
7633
7869
|
...existingTask,
|
|
7634
7870
|
progress: typeof progress === "number" ? progress : existingTask.progress,
|
|
@@ -7644,7 +7880,27 @@ function applyTaskProgress(state, taskName, message, progress) {
|
|
|
7644
7880
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7645
7881
|
};
|
|
7646
7882
|
}
|
|
7647
|
-
function
|
|
7883
|
+
function applyTaskRestart(state, taskName) {
|
|
7884
|
+
const existingTask = state.tasks[taskName];
|
|
7885
|
+
if (!existingTask) return state;
|
|
7886
|
+
const updatedTask = {
|
|
7887
|
+
...existingTask,
|
|
7888
|
+
status: "not-started",
|
|
7889
|
+
startedAt: void 0,
|
|
7890
|
+
completedAt: void 0,
|
|
7891
|
+
failedAt: void 0,
|
|
7892
|
+
error: void 0,
|
|
7893
|
+
data: void 0,
|
|
7894
|
+
progress: null,
|
|
7895
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7896
|
+
};
|
|
7897
|
+
return {
|
|
7898
|
+
...state,
|
|
7899
|
+
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
7900
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7901
|
+
};
|
|
7902
|
+
}
|
|
7903
|
+
function createDefaultGraphEngineStore2() {
|
|
7648
7904
|
return {
|
|
7649
7905
|
status: "not-started",
|
|
7650
7906
|
executionCount: 0,
|
|
@@ -7664,11 +7920,13 @@ function apply(state, event, graph) {
|
|
|
7664
7920
|
case "task-started":
|
|
7665
7921
|
return applyTaskStart(state, event.taskName);
|
|
7666
7922
|
case "task-completed":
|
|
7667
|
-
return applyTaskCompletion(state, graph, event.taskName, event.result);
|
|
7923
|
+
return applyTaskCompletion(state, graph, event.taskName, event.result, event.dataHash, event.data);
|
|
7668
7924
|
case "task-failed":
|
|
7669
7925
|
return applyTaskFailure(state, graph, event.taskName, event.error);
|
|
7670
7926
|
case "task-progress":
|
|
7671
7927
|
return applyTaskProgress(state, event.taskName, event.message, event.progress);
|
|
7928
|
+
case "task-restart":
|
|
7929
|
+
return applyTaskRestart(state, event.taskName);
|
|
7672
7930
|
case "inject-tokens":
|
|
7673
7931
|
return applyInjectTokens(state, event.tokens);
|
|
7674
7932
|
case "agent-action":
|
|
@@ -7739,7 +7997,7 @@ function applyTaskCreation(state, taskName, taskConfig) {
|
|
|
7739
7997
|
...state,
|
|
7740
7998
|
tasks: {
|
|
7741
7999
|
...state.tasks,
|
|
7742
|
-
[taskName]:
|
|
8000
|
+
[taskName]: createDefaultGraphEngineStore()
|
|
7743
8001
|
},
|
|
7744
8002
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
7745
8003
|
};
|
|
@@ -8354,107 +8612,377 @@ function buildResult(issues) {
|
|
|
8354
8612
|
};
|
|
8355
8613
|
}
|
|
8356
8614
|
|
|
8357
|
-
//
|
|
8358
|
-
var
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
localStorage.setItem(this.indexKey(), JSON.stringify(runs));
|
|
8381
|
-
}
|
|
8382
|
-
}
|
|
8383
|
-
async loadRunState(runId) {
|
|
8384
|
-
const raw = localStorage.getItem(this.runKey(runId));
|
|
8385
|
-
return raw ? JSON.parse(raw) : null;
|
|
8386
|
-
}
|
|
8387
|
-
async deleteRunState(runId) {
|
|
8388
|
-
localStorage.removeItem(this.runKey(runId));
|
|
8389
|
-
localStorage.removeItem(this.dataKey(runId));
|
|
8390
|
-
const runs = await this.listRuns();
|
|
8391
|
-
const filtered = runs.filter((id) => id !== runId);
|
|
8392
|
-
localStorage.setItem(this.indexKey(), JSON.stringify(filtered));
|
|
8393
|
-
}
|
|
8394
|
-
async setData(runId, key, value) {
|
|
8395
|
-
const allData = await this.getAllData(runId);
|
|
8396
|
-
allData[key] = value;
|
|
8397
|
-
localStorage.setItem(this.dataKey(runId), JSON.stringify(allData));
|
|
8398
|
-
}
|
|
8399
|
-
async getData(runId, key) {
|
|
8400
|
-
const allData = await this.getAllData(runId);
|
|
8401
|
-
return allData[key];
|
|
8402
|
-
}
|
|
8403
|
-
async getAllData(runId) {
|
|
8404
|
-
const raw = localStorage.getItem(this.dataKey(runId));
|
|
8405
|
-
return raw ? JSON.parse(raw) : {};
|
|
8406
|
-
}
|
|
8407
|
-
async clearData(runId) {
|
|
8408
|
-
localStorage.removeItem(this.dataKey(runId));
|
|
8409
|
-
}
|
|
8410
|
-
async listRuns() {
|
|
8411
|
-
const raw = localStorage.getItem(this.indexKey());
|
|
8412
|
-
return raw ? JSON.parse(raw) : [];
|
|
8413
|
-
}
|
|
8414
|
-
/**
|
|
8415
|
-
* Clear all flow data from localStorage
|
|
8416
|
-
*/
|
|
8417
|
-
clearAll() {
|
|
8418
|
-
const keysToRemove = [];
|
|
8419
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
8420
|
-
const key = localStorage.key(i);
|
|
8421
|
-
if (key?.startsWith(this.prefix + ":")) {
|
|
8422
|
-
keysToRemove.push(key);
|
|
8615
|
+
// schema/event-graph.schema.json
|
|
8616
|
+
var event_graph_schema_default = {
|
|
8617
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
8618
|
+
$id: "https://github.com/yaml-flow/schema/event-graph.json",
|
|
8619
|
+
title: "Event Graph Configuration",
|
|
8620
|
+
description: "Schema for stateless event-graph (DAG) workflow definitions in yaml-flow",
|
|
8621
|
+
type: "object",
|
|
8622
|
+
required: ["settings", "tasks"],
|
|
8623
|
+
additionalProperties: false,
|
|
8624
|
+
properties: {
|
|
8625
|
+
id: {
|
|
8626
|
+
type: "string",
|
|
8627
|
+
description: "Optional graph identifier"
|
|
8628
|
+
},
|
|
8629
|
+
settings: {
|
|
8630
|
+
$ref: "#/definitions/settings"
|
|
8631
|
+
},
|
|
8632
|
+
tasks: {
|
|
8633
|
+
type: "object",
|
|
8634
|
+
description: "Task definitions keyed by name",
|
|
8635
|
+
minProperties: 1,
|
|
8636
|
+
additionalProperties: {
|
|
8637
|
+
$ref: "#/definitions/task"
|
|
8423
8638
|
}
|
|
8424
8639
|
}
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8640
|
+
},
|
|
8641
|
+
definitions: {
|
|
8642
|
+
settings: {
|
|
8643
|
+
type: "object",
|
|
8644
|
+
required: ["completion"],
|
|
8645
|
+
properties: {
|
|
8646
|
+
completion: {
|
|
8647
|
+
type: "string",
|
|
8648
|
+
enum: [
|
|
8649
|
+
"all-tasks-done",
|
|
8650
|
+
"all-outputs-done",
|
|
8651
|
+
"only-resolved",
|
|
8652
|
+
"goal-reached",
|
|
8653
|
+
"manual"
|
|
8654
|
+
],
|
|
8655
|
+
description: "Completion strategy"
|
|
8656
|
+
},
|
|
8657
|
+
conflict_strategy: {
|
|
8658
|
+
type: "string",
|
|
8659
|
+
enum: [
|
|
8660
|
+
"alphabetical",
|
|
8661
|
+
"priority-first",
|
|
8662
|
+
"duration-first",
|
|
8663
|
+
"cost-optimized",
|
|
8664
|
+
"resource-aware",
|
|
8665
|
+
"random-select",
|
|
8666
|
+
"user-choice",
|
|
8667
|
+
"parallel-all",
|
|
8668
|
+
"skip-conflicts",
|
|
8669
|
+
"round-robin"
|
|
8670
|
+
],
|
|
8671
|
+
description: "Conflict resolution strategy"
|
|
8672
|
+
},
|
|
8673
|
+
execution_mode: {
|
|
8674
|
+
type: "string",
|
|
8675
|
+
enum: ["dependency-mode", "eligibility-mode"],
|
|
8676
|
+
description: "Execution mode"
|
|
8677
|
+
},
|
|
8678
|
+
goal: {
|
|
8679
|
+
type: "array",
|
|
8680
|
+
items: { type: "string" },
|
|
8681
|
+
minItems: 1,
|
|
8682
|
+
description: "Goal outputs \u2014 required when completion is 'goal-reached'"
|
|
8683
|
+
},
|
|
8684
|
+
max_iterations: {
|
|
8685
|
+
type: "integer",
|
|
8686
|
+
minimum: 1,
|
|
8687
|
+
description: "Max scheduler iterations (safety limit, default: 1000)"
|
|
8688
|
+
},
|
|
8689
|
+
timeout_ms: {
|
|
8690
|
+
type: "integer",
|
|
8691
|
+
minimum: 0,
|
|
8692
|
+
description: "Timeout in ms (declared for drivers, not enforced by pure engine)"
|
|
8693
|
+
},
|
|
8694
|
+
refreshStrategy: {
|
|
8695
|
+
$ref: "#/definitions/refresh_strategy",
|
|
8696
|
+
description: "Default refresh strategy for all tasks (default: 'data-changed')"
|
|
8697
|
+
}
|
|
8698
|
+
},
|
|
8699
|
+
additionalProperties: false,
|
|
8700
|
+
if: {
|
|
8701
|
+
properties: { completion: { const: "goal-reached" } }
|
|
8702
|
+
},
|
|
8703
|
+
then: {
|
|
8704
|
+
required: ["completion", "goal"]
|
|
8705
|
+
}
|
|
8706
|
+
},
|
|
8707
|
+
task: {
|
|
8708
|
+
type: "object",
|
|
8709
|
+
required: ["provides"],
|
|
8710
|
+
properties: {
|
|
8711
|
+
requires: {
|
|
8712
|
+
type: "array",
|
|
8713
|
+
items: { type: "string" },
|
|
8714
|
+
description: "Tokens this task needs to become eligible"
|
|
8715
|
+
},
|
|
8716
|
+
provides: {
|
|
8717
|
+
type: "array",
|
|
8718
|
+
items: { type: "string" },
|
|
8719
|
+
description: "Tokens this task produces on successful completion"
|
|
8720
|
+
},
|
|
8721
|
+
on: {
|
|
8722
|
+
type: "object",
|
|
8723
|
+
description: "Conditional provides based on handler result key",
|
|
8724
|
+
additionalProperties: {
|
|
8725
|
+
type: "array",
|
|
8726
|
+
items: { type: "string" }
|
|
8727
|
+
}
|
|
8728
|
+
},
|
|
8729
|
+
on_failure: {
|
|
8730
|
+
type: "array",
|
|
8731
|
+
items: { type: "string" },
|
|
8732
|
+
description: "Tokens to inject when this task fails"
|
|
8733
|
+
},
|
|
8734
|
+
method: {
|
|
8735
|
+
type: "string",
|
|
8736
|
+
description: "Task execution method (informational \u2014 driver concern)"
|
|
8737
|
+
},
|
|
8738
|
+
config: {
|
|
8739
|
+
type: "object",
|
|
8740
|
+
description: "Arbitrary task configuration (driver concern)"
|
|
8741
|
+
},
|
|
8742
|
+
priority: {
|
|
8743
|
+
type: "number",
|
|
8744
|
+
description: "Higher = preferred in conflict resolution"
|
|
8745
|
+
},
|
|
8746
|
+
estimatedDuration: {
|
|
8747
|
+
type: "number",
|
|
8748
|
+
minimum: 0,
|
|
8749
|
+
description: "Estimated duration in ms (used by duration-first strategy)"
|
|
8750
|
+
},
|
|
8751
|
+
estimatedCost: {
|
|
8752
|
+
type: "number",
|
|
8753
|
+
minimum: 0,
|
|
8754
|
+
description: "Estimated cost (used by cost-optimized strategy)"
|
|
8755
|
+
},
|
|
8756
|
+
estimatedResources: {
|
|
8757
|
+
type: "object",
|
|
8758
|
+
additionalProperties: { type: "number" },
|
|
8759
|
+
description: "Resource requirements (used by resource-aware strategy)"
|
|
8760
|
+
},
|
|
8761
|
+
retry: {
|
|
8762
|
+
$ref: "#/definitions/task_retry"
|
|
8763
|
+
},
|
|
8764
|
+
refreshStrategy: {
|
|
8765
|
+
$ref: "#/definitions/refresh_strategy",
|
|
8766
|
+
description: "Task-level refresh strategy (overrides settings default)"
|
|
8767
|
+
},
|
|
8768
|
+
refreshInterval: {
|
|
8769
|
+
type: "number",
|
|
8770
|
+
minimum: 0,
|
|
8771
|
+
description: "Interval in seconds for time-based refresh strategy"
|
|
8772
|
+
},
|
|
8773
|
+
maxExecutions: {
|
|
8774
|
+
type: "integer",
|
|
8775
|
+
minimum: 1,
|
|
8776
|
+
description: "Maximum number of times this task can execute"
|
|
8777
|
+
},
|
|
8778
|
+
circuit_breaker: {
|
|
8779
|
+
$ref: "#/definitions/task_circuit_breaker"
|
|
8780
|
+
},
|
|
8781
|
+
description: {
|
|
8782
|
+
type: "string",
|
|
8783
|
+
description: "Human-readable description"
|
|
8784
|
+
},
|
|
8785
|
+
inference: {
|
|
8786
|
+
$ref: "#/definitions/inference_hints"
|
|
8787
|
+
}
|
|
8788
|
+
},
|
|
8789
|
+
additionalProperties: false
|
|
8790
|
+
},
|
|
8791
|
+
task_retry: {
|
|
8792
|
+
type: "object",
|
|
8793
|
+
required: ["max_attempts"],
|
|
8794
|
+
properties: {
|
|
8795
|
+
max_attempts: {
|
|
8796
|
+
type: "integer",
|
|
8797
|
+
minimum: 1,
|
|
8798
|
+
description: "Maximum retry attempts"
|
|
8799
|
+
},
|
|
8800
|
+
delay_ms: {
|
|
8801
|
+
type: "integer",
|
|
8802
|
+
minimum: 0,
|
|
8803
|
+
description: "Delay between retries in ms"
|
|
8804
|
+
},
|
|
8805
|
+
backoff_multiplier: {
|
|
8806
|
+
type: "number",
|
|
8807
|
+
minimum: 1,
|
|
8808
|
+
description: "Backoff multiplier (e.g., 2 for exponential)"
|
|
8809
|
+
}
|
|
8810
|
+
},
|
|
8811
|
+
additionalProperties: false
|
|
8812
|
+
},
|
|
8813
|
+
refresh_strategy: {
|
|
8814
|
+
type: "string",
|
|
8815
|
+
enum: ["data-changed", "epoch-changed", "time-based", "manual", "once"],
|
|
8816
|
+
description: "Strategy for determining when a completed task should re-run"
|
|
8817
|
+
},
|
|
8818
|
+
task_circuit_breaker: {
|
|
8819
|
+
type: "object",
|
|
8820
|
+
required: ["max_executions", "on_break"],
|
|
8821
|
+
properties: {
|
|
8822
|
+
max_executions: {
|
|
8823
|
+
type: "integer",
|
|
8824
|
+
minimum: 1,
|
|
8825
|
+
description: "Max executions before breaker trips"
|
|
8826
|
+
},
|
|
8827
|
+
on_break: {
|
|
8828
|
+
type: "array",
|
|
8829
|
+
items: { type: "string" },
|
|
8830
|
+
minItems: 1,
|
|
8831
|
+
description: "Tokens to inject when breaker trips"
|
|
8832
|
+
}
|
|
8833
|
+
},
|
|
8834
|
+
additionalProperties: false
|
|
8835
|
+
},
|
|
8836
|
+
inference_hints: {
|
|
8837
|
+
type: "object",
|
|
8838
|
+
description: "LLM inference hints \u2014 opt-in metadata for AI-assisted completion detection",
|
|
8839
|
+
properties: {
|
|
8840
|
+
criteria: {
|
|
8841
|
+
type: "string",
|
|
8842
|
+
description: "Human-readable completion criteria"
|
|
8843
|
+
},
|
|
8844
|
+
keywords: {
|
|
8845
|
+
type: "array",
|
|
8846
|
+
items: { type: "string" },
|
|
8847
|
+
description: "Keywords to help the LLM understand the domain"
|
|
8848
|
+
},
|
|
8849
|
+
suggestedChecks: {
|
|
8850
|
+
type: "array",
|
|
8851
|
+
items: { type: "string" },
|
|
8852
|
+
description: "Suggested checks for verification"
|
|
8853
|
+
},
|
|
8854
|
+
autoDetectable: {
|
|
8855
|
+
type: "boolean",
|
|
8856
|
+
description: "Whether the LLM should attempt to auto-detect completion (default: false)"
|
|
8857
|
+
}
|
|
8858
|
+
},
|
|
8859
|
+
additionalProperties: false
|
|
8860
|
+
}
|
|
8861
|
+
}
|
|
8862
|
+
};
|
|
8863
|
+
|
|
8864
|
+
// src/event-graph/schema-validator.ts
|
|
8865
|
+
var import_ajv2 = __toESM(require_ajv());
|
|
8866
|
+
var _compiled2 = null;
|
|
8867
|
+
function getValidator2() {
|
|
8868
|
+
if (_compiled2) return _compiled2;
|
|
8869
|
+
const ajv = new import_ajv2.default({ allErrors: true });
|
|
8870
|
+
addFormats(ajv);
|
|
8871
|
+
_compiled2 = ajv.compile(event_graph_schema_default);
|
|
8872
|
+
return _compiled2;
|
|
8873
|
+
}
|
|
8874
|
+
function validateGraphSchema(config) {
|
|
8875
|
+
const validate = getValidator2();
|
|
8876
|
+
const valid = validate(config);
|
|
8877
|
+
if (valid) return { ok: true, errors: [] };
|
|
8878
|
+
const errors = (validate.errors ?? []).map((e) => {
|
|
8879
|
+
const path = e.instancePath || "/";
|
|
8880
|
+
return `${path}: ${e.message ?? "unknown error"}`;
|
|
8881
|
+
});
|
|
8882
|
+
return { ok: false, errors };
|
|
8883
|
+
}
|
|
8884
|
+
|
|
8885
|
+
// src/stores/localStorage.ts
|
|
8886
|
+
var LocalStorageStore = class {
|
|
8887
|
+
prefix;
|
|
8888
|
+
constructor(options = {}) {
|
|
8889
|
+
this.prefix = options.prefix ?? "yamlflow";
|
|
8890
|
+
if (typeof localStorage === "undefined") {
|
|
8891
|
+
throw new Error("LocalStorageStore requires localStorage (browser environment)");
|
|
8892
|
+
}
|
|
8893
|
+
}
|
|
8894
|
+
runKey(runId) {
|
|
8895
|
+
return `${this.prefix}:run:${runId}`;
|
|
8896
|
+
}
|
|
8897
|
+
dataKey(runId) {
|
|
8898
|
+
return `${this.prefix}:data:${runId}`;
|
|
8899
|
+
}
|
|
8900
|
+
indexKey() {
|
|
8901
|
+
return `${this.prefix}:runs`;
|
|
8902
|
+
}
|
|
8903
|
+
async saveRunState(runId, state) {
|
|
8904
|
+
localStorage.setItem(this.runKey(runId), JSON.stringify(state));
|
|
8905
|
+
const runs = await this.listRuns();
|
|
8906
|
+
if (!runs.includes(runId)) {
|
|
8907
|
+
runs.push(runId);
|
|
8908
|
+
localStorage.setItem(this.indexKey(), JSON.stringify(runs));
|
|
8909
|
+
}
|
|
8910
|
+
}
|
|
8911
|
+
async loadRunState(runId) {
|
|
8912
|
+
const raw = localStorage.getItem(this.runKey(runId));
|
|
8913
|
+
return raw ? JSON.parse(raw) : null;
|
|
8914
|
+
}
|
|
8915
|
+
async deleteRunState(runId) {
|
|
8916
|
+
localStorage.removeItem(this.runKey(runId));
|
|
8917
|
+
localStorage.removeItem(this.dataKey(runId));
|
|
8918
|
+
const runs = await this.listRuns();
|
|
8919
|
+
const filtered = runs.filter((id) => id !== runId);
|
|
8920
|
+
localStorage.setItem(this.indexKey(), JSON.stringify(filtered));
|
|
8921
|
+
}
|
|
8922
|
+
async setData(runId, key, value) {
|
|
8923
|
+
const allData = await this.getAllData(runId);
|
|
8924
|
+
allData[key] = value;
|
|
8925
|
+
localStorage.setItem(this.dataKey(runId), JSON.stringify(allData));
|
|
8926
|
+
}
|
|
8927
|
+
async getData(runId, key) {
|
|
8928
|
+
const allData = await this.getAllData(runId);
|
|
8929
|
+
return allData[key];
|
|
8930
|
+
}
|
|
8931
|
+
async getAllData(runId) {
|
|
8932
|
+
const raw = localStorage.getItem(this.dataKey(runId));
|
|
8933
|
+
return raw ? JSON.parse(raw) : {};
|
|
8934
|
+
}
|
|
8935
|
+
async clearData(runId) {
|
|
8936
|
+
localStorage.removeItem(this.dataKey(runId));
|
|
8937
|
+
}
|
|
8938
|
+
async listRuns() {
|
|
8939
|
+
const raw = localStorage.getItem(this.indexKey());
|
|
8940
|
+
return raw ? JSON.parse(raw) : [];
|
|
8941
|
+
}
|
|
8942
|
+
/**
|
|
8943
|
+
* Clear all flow data from localStorage
|
|
8944
|
+
*/
|
|
8945
|
+
clearAll() {
|
|
8946
|
+
const keysToRemove = [];
|
|
8947
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
8948
|
+
const key = localStorage.key(i);
|
|
8949
|
+
if (key?.startsWith(this.prefix + ":")) {
|
|
8950
|
+
keysToRemove.push(key);
|
|
8951
|
+
}
|
|
8952
|
+
}
|
|
8953
|
+
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
8954
|
+
}
|
|
8955
|
+
};
|
|
8956
|
+
|
|
8957
|
+
// src/stores/file.ts
|
|
8958
|
+
var FileStore = class {
|
|
8959
|
+
directory;
|
|
8960
|
+
fs = null;
|
|
8961
|
+
path = null;
|
|
8962
|
+
constructor(options) {
|
|
8963
|
+
this.directory = options.directory;
|
|
8964
|
+
}
|
|
8965
|
+
async ensureModules() {
|
|
8966
|
+
if (!this.fs || !this.path) {
|
|
8967
|
+
this.fs = await import('fs/promises');
|
|
8968
|
+
this.path = await import('path');
|
|
8969
|
+
await this.fs.mkdir(this.directory, { recursive: true });
|
|
8970
|
+
}
|
|
8971
|
+
}
|
|
8972
|
+
runPath(runId) {
|
|
8973
|
+
return this.path.join(this.directory, `${runId}.run.json`);
|
|
8974
|
+
}
|
|
8975
|
+
dataPath(runId) {
|
|
8976
|
+
return this.path.join(this.directory, `${runId}.data.json`);
|
|
8977
|
+
}
|
|
8978
|
+
async saveRunState(runId, state) {
|
|
8979
|
+
await this.ensureModules();
|
|
8980
|
+
await this.fs.writeFile(
|
|
8981
|
+
this.runPath(runId),
|
|
8982
|
+
JSON.stringify(state, null, 2),
|
|
8983
|
+
"utf-8"
|
|
8984
|
+
);
|
|
8985
|
+
}
|
|
8458
8986
|
async loadRunState(runId) {
|
|
8459
8987
|
await this.ensureModules();
|
|
8460
8988
|
try {
|
|
@@ -8719,7 +9247,7 @@ function createLiveGraph(config, executionId) {
|
|
|
8719
9247
|
const id = executionId ?? `live-${Date.now()}`;
|
|
8720
9248
|
const tasks = {};
|
|
8721
9249
|
for (const taskName of Object.keys(config.tasks)) {
|
|
8722
|
-
tasks[taskName] =
|
|
9250
|
+
tasks[taskName] = createDefaultGraphEngineStore3();
|
|
8723
9251
|
}
|
|
8724
9252
|
const state = {
|
|
8725
9253
|
status: "running",
|
|
@@ -8747,7 +9275,7 @@ function applyEvent(live, event) {
|
|
|
8747
9275
|
newState = applyTaskStart(state, event.taskName);
|
|
8748
9276
|
break;
|
|
8749
9277
|
case "task-completed":
|
|
8750
|
-
newState = applyTaskCompletion(state, config, event.taskName, event.result);
|
|
9278
|
+
newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data);
|
|
8751
9279
|
break;
|
|
8752
9280
|
case "task-failed":
|
|
8753
9281
|
newState = applyTaskFailure(state, config, event.taskName, event.error);
|
|
@@ -8755,6 +9283,9 @@ function applyEvent(live, event) {
|
|
|
8755
9283
|
case "task-progress":
|
|
8756
9284
|
newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
|
|
8757
9285
|
break;
|
|
9286
|
+
case "task-restart":
|
|
9287
|
+
newState = applyTaskRestart(state, event.taskName);
|
|
9288
|
+
break;
|
|
8758
9289
|
case "inject-tokens":
|
|
8759
9290
|
newState = {
|
|
8760
9291
|
...state,
|
|
@@ -8770,6 +9301,9 @@ function applyEvent(live, event) {
|
|
|
8770
9301
|
}
|
|
8771
9302
|
return { config, state: newState };
|
|
8772
9303
|
}
|
|
9304
|
+
function applyEvents(live, events) {
|
|
9305
|
+
return events.reduce((current, event) => applyEvent(current, event), live);
|
|
9306
|
+
}
|
|
8773
9307
|
function addNode(live, name, taskConfig) {
|
|
8774
9308
|
if (live.config.tasks[name]) return live;
|
|
8775
9309
|
return {
|
|
@@ -8779,7 +9313,7 @@ function addNode(live, name, taskConfig) {
|
|
|
8779
9313
|
},
|
|
8780
9314
|
state: {
|
|
8781
9315
|
...live.state,
|
|
8782
|
-
tasks: { ...live.state.tasks, [name]:
|
|
9316
|
+
tasks: { ...live.state.tasks, [name]: createDefaultGraphEngineStore3() },
|
|
8783
9317
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
8784
9318
|
}
|
|
8785
9319
|
};
|
|
@@ -8896,7 +9430,7 @@ function resetNode(live, name) {
|
|
|
8896
9430
|
...live.state,
|
|
8897
9431
|
tasks: {
|
|
8898
9432
|
...live.state.tasks,
|
|
8899
|
-
[name]:
|
|
9433
|
+
[name]: createDefaultGraphEngineStore3()
|
|
8900
9434
|
},
|
|
8901
9435
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
8902
9436
|
}
|
|
@@ -8935,7 +9469,7 @@ function enableNode(live, name) {
|
|
|
8935
9469
|
function getNode(live, name) {
|
|
8936
9470
|
const config = live.config.tasks[name];
|
|
8937
9471
|
if (!config) return void 0;
|
|
8938
|
-
const state = live.state.tasks[name] ??
|
|
9472
|
+
const state = live.state.tasks[name] ?? createDefaultGraphEngineStore3();
|
|
8939
9473
|
return { name, config, state };
|
|
8940
9474
|
}
|
|
8941
9475
|
function snapshot(live) {
|
|
@@ -8973,7 +9507,7 @@ function restore(data) {
|
|
|
8973
9507
|
}
|
|
8974
9508
|
return { config, state };
|
|
8975
9509
|
}
|
|
8976
|
-
function
|
|
9510
|
+
function createDefaultGraphEngineStore3() {
|
|
8977
9511
|
return {
|
|
8978
9512
|
status: "not-started",
|
|
8979
9513
|
executionCount: 0,
|
|
@@ -9014,55 +9548,101 @@ function schedule(live) {
|
|
|
9014
9548
|
const blocked = [];
|
|
9015
9549
|
for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
|
|
9016
9550
|
const taskState = state.tasks[taskName];
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
} else {
|
|
9022
|
-
if (taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
|
|
9023
|
-
continue;
|
|
9024
|
-
}
|
|
9025
|
-
const maxExec = getRepeatableMax(taskConfig);
|
|
9026
|
-
if (maxExec !== void 0 && taskState && taskState.executionCount >= maxExec) {
|
|
9027
|
-
continue;
|
|
9028
|
-
}
|
|
9029
|
-
if (taskConfig.circuit_breaker && taskState && taskState.executionCount >= taskConfig.circuit_breaker.max_executions) {
|
|
9030
|
-
continue;
|
|
9031
|
-
}
|
|
9032
|
-
if (taskState?.status === TASK_STATUS.COMPLETED) {
|
|
9033
|
-
const requires2 = getRequires(taskConfig);
|
|
9034
|
-
if (requires2.length > 0) {
|
|
9035
|
-
const hasRefreshed = requires2.some((req) => {
|
|
9036
|
-
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
9037
|
-
if (getProvides(otherConfig).includes(req)) {
|
|
9038
|
-
const otherState = state.tasks[otherName];
|
|
9039
|
-
if (otherState && otherState.executionCount > taskState.lastEpoch) return true;
|
|
9040
|
-
}
|
|
9041
|
-
}
|
|
9042
|
-
return false;
|
|
9043
|
-
});
|
|
9044
|
-
if (!hasRefreshed) continue;
|
|
9045
|
-
} else {
|
|
9046
|
-
continue;
|
|
9047
|
-
}
|
|
9048
|
-
}
|
|
9551
|
+
const strategy = getRefreshStrategy(taskConfig, config.settings);
|
|
9552
|
+
const rerunnable = strategy !== "once";
|
|
9553
|
+
if (taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
|
|
9554
|
+
continue;
|
|
9049
9555
|
}
|
|
9050
|
-
const
|
|
9051
|
-
if (
|
|
9052
|
-
eligible.push(taskName);
|
|
9556
|
+
const maxExec = getMaxExecutions(taskConfig);
|
|
9557
|
+
if (maxExec !== void 0 && taskState && taskState.executionCount >= maxExec) {
|
|
9053
9558
|
continue;
|
|
9054
9559
|
}
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
9560
|
+
if (taskConfig.circuit_breaker && taskState && taskState.executionCount >= taskConfig.circuit_breaker.max_executions) {
|
|
9561
|
+
continue;
|
|
9562
|
+
}
|
|
9563
|
+
if (!rerunnable && taskState?.status === TASK_STATUS.COMPLETED) {
|
|
9564
|
+
continue;
|
|
9565
|
+
}
|
|
9566
|
+
if (rerunnable && taskState?.status === TASK_STATUS.COMPLETED) {
|
|
9567
|
+
const requires2 = getRequires(taskConfig);
|
|
9568
|
+
let shouldSkip = false;
|
|
9569
|
+
switch (strategy) {
|
|
9570
|
+
case "data-changed": {
|
|
9571
|
+
if (requires2.length > 0) {
|
|
9572
|
+
const hasChangedData = requires2.some((req) => {
|
|
9573
|
+
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
9574
|
+
if (getProvides(otherConfig).includes(req)) {
|
|
9575
|
+
const otherState = state.tasks[otherName];
|
|
9576
|
+
if (!otherState) continue;
|
|
9577
|
+
const consumed = taskState.lastConsumedHashes?.[req];
|
|
9578
|
+
if (otherState.lastDataHash == null) {
|
|
9579
|
+
return otherState.executionCount > taskState.lastEpoch;
|
|
9580
|
+
}
|
|
9581
|
+
return otherState.lastDataHash !== consumed;
|
|
9582
|
+
}
|
|
9583
|
+
}
|
|
9584
|
+
return false;
|
|
9585
|
+
});
|
|
9586
|
+
if (!hasChangedData) shouldSkip = true;
|
|
9587
|
+
} else {
|
|
9588
|
+
shouldSkip = true;
|
|
9589
|
+
}
|
|
9590
|
+
break;
|
|
9591
|
+
}
|
|
9592
|
+
case "epoch-changed": {
|
|
9593
|
+
if (requires2.length > 0) {
|
|
9594
|
+
const hasRefreshed = requires2.some((req) => {
|
|
9595
|
+
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
9596
|
+
if (getProvides(otherConfig).includes(req)) {
|
|
9597
|
+
const otherState = state.tasks[otherName];
|
|
9598
|
+
if (otherState && otherState.executionCount > taskState.lastEpoch) return true;
|
|
9599
|
+
}
|
|
9600
|
+
}
|
|
9601
|
+
return false;
|
|
9602
|
+
});
|
|
9603
|
+
if (!hasRefreshed) shouldSkip = true;
|
|
9604
|
+
} else {
|
|
9605
|
+
shouldSkip = true;
|
|
9606
|
+
}
|
|
9607
|
+
break;
|
|
9608
|
+
}
|
|
9609
|
+
case "time-based": {
|
|
9610
|
+
const interval = taskConfig.refreshInterval ?? 0;
|
|
9611
|
+
if (interval <= 0) {
|
|
9612
|
+
shouldSkip = true;
|
|
9613
|
+
break;
|
|
9614
|
+
}
|
|
9615
|
+
const completedAt = taskState.completedAt;
|
|
9616
|
+
if (!completedAt) {
|
|
9617
|
+
shouldSkip = true;
|
|
9618
|
+
break;
|
|
9619
|
+
}
|
|
9620
|
+
const elapsedSec = (Date.now() - Date.parse(completedAt)) / 1e3;
|
|
9621
|
+
if (elapsedSec < interval) shouldSkip = true;
|
|
9622
|
+
break;
|
|
9623
|
+
}
|
|
9624
|
+
case "manual":
|
|
9625
|
+
shouldSkip = true;
|
|
9626
|
+
break;
|
|
9627
|
+
}
|
|
9628
|
+
if (shouldSkip) continue;
|
|
9629
|
+
}
|
|
9630
|
+
const requires = getRequires(taskConfig);
|
|
9631
|
+
if (requires.length === 0) {
|
|
9632
|
+
eligible.push(taskName);
|
|
9633
|
+
continue;
|
|
9634
|
+
}
|
|
9635
|
+
const missingTokens = [];
|
|
9636
|
+
const pendingTokens = [];
|
|
9637
|
+
const failedTokenInfo = [];
|
|
9638
|
+
for (const token of requires) {
|
|
9639
|
+
if (availableOutputs.has(token)) continue;
|
|
9640
|
+
const producers = producerMap[token] || [];
|
|
9641
|
+
if (producers.length === 0) {
|
|
9642
|
+
missingTokens.push(token);
|
|
9643
|
+
} else {
|
|
9644
|
+
const allFailed = producers.every((p) => isNonActiveTask(state.tasks[p]));
|
|
9645
|
+
if (allFailed) {
|
|
9066
9646
|
failedTokenInfo.push({ token, failedProducer: producers[0] });
|
|
9067
9647
|
} else {
|
|
9068
9648
|
pendingTokens.push(token);
|
|
@@ -9440,255 +10020,644 @@ function getDownstream(live, nodeName) {
|
|
|
9440
10020
|
}));
|
|
9441
10021
|
return { nodeName, nodes, tokens: [...tokenSet] };
|
|
9442
10022
|
}
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
For each task you analyze, provide a JSON response. Be conservative \u2014 only mark tasks as completed when the evidence strongly supports it.`;
|
|
9449
|
-
function buildInferencePrompt(live, options = {}) {
|
|
9450
|
-
const { scope, context, systemPrompt } = options;
|
|
9451
|
-
const graphTasks = getAllTasks(live.config);
|
|
9452
|
-
const { state } = live;
|
|
9453
|
-
const candidates = getAnalyzableCandidates(live, scope);
|
|
9454
|
-
if (candidates.length === 0) {
|
|
9455
|
-
return "";
|
|
10023
|
+
var MemoryJournal = class {
|
|
10024
|
+
buffer = [];
|
|
10025
|
+
append(event) {
|
|
10026
|
+
this.buffer.push(event);
|
|
9456
10027
|
}
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
lines.push("");
|
|
9462
|
-
lines.push(`Available tokens: ${state.availableOutputs.length > 0 ? state.availableOutputs.join(", ") : "(none)"}`);
|
|
9463
|
-
lines.push("");
|
|
9464
|
-
const completedTasks = Object.entries(state.tasks).filter(([_, ts]) => ts.status === "completed").map(([name]) => name);
|
|
9465
|
-
if (completedTasks.length > 0) {
|
|
9466
|
-
lines.push(`Completed tasks: ${completedTasks.join(", ")}`);
|
|
9467
|
-
lines.push("");
|
|
10028
|
+
drain() {
|
|
10029
|
+
const events = this.buffer;
|
|
10030
|
+
this.buffer = [];
|
|
10031
|
+
return events;
|
|
9468
10032
|
}
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
10033
|
+
get size() {
|
|
10034
|
+
return this.buffer.length;
|
|
10035
|
+
}
|
|
10036
|
+
};
|
|
10037
|
+
var FileJournal = class {
|
|
10038
|
+
constructor(path) {
|
|
10039
|
+
this.path = path;
|
|
10040
|
+
if (!existsSync(path)) {
|
|
10041
|
+
writeFileSync(path, "", "utf-8");
|
|
10042
|
+
}
|
|
10043
|
+
}
|
|
10044
|
+
path;
|
|
10045
|
+
pending = 0;
|
|
10046
|
+
append(event) {
|
|
10047
|
+
appendFileSync(this.path, JSON.stringify(event) + "\n", "utf-8");
|
|
10048
|
+
this.pending++;
|
|
10049
|
+
}
|
|
10050
|
+
drain() {
|
|
10051
|
+
const content = readFileSync(this.path, "utf-8").trim();
|
|
10052
|
+
writeFileSync(this.path, "", "utf-8");
|
|
10053
|
+
this.pending = 0;
|
|
10054
|
+
if (!content) return [];
|
|
10055
|
+
return content.split("\n").map((line) => JSON.parse(line));
|
|
10056
|
+
}
|
|
10057
|
+
get size() {
|
|
10058
|
+
try {
|
|
10059
|
+
const content = readFileSync(this.path, "utf-8").trim();
|
|
10060
|
+
if (!content) return 0;
|
|
10061
|
+
return content.split("\n").length;
|
|
10062
|
+
} catch {
|
|
10063
|
+
return this.pending;
|
|
9488
10064
|
}
|
|
9489
|
-
lines.push("");
|
|
9490
10065
|
}
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
10066
|
+
};
|
|
10067
|
+
function computeDataHash(data) {
|
|
10068
|
+
const json = stableStringify(data);
|
|
10069
|
+
return createHash("sha256").update(json).digest("hex").slice(0, 16);
|
|
10070
|
+
}
|
|
10071
|
+
function stableStringify(value) {
|
|
10072
|
+
if (value === null || value === void 0 || typeof value !== "object") {
|
|
10073
|
+
return JSON.stringify(value);
|
|
9496
10074
|
}
|
|
9497
|
-
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
lines.push(' "taskName": "task-name",');
|
|
9504
|
-
lines.push(' "confidence": 0.0 to 1.0,');
|
|
9505
|
-
lines.push(' "reasoning": "explanation of why you believe this task is complete or not"');
|
|
9506
|
-
lines.push(" }");
|
|
9507
|
-
lines.push("]");
|
|
9508
|
-
lines.push("```");
|
|
9509
|
-
lines.push("");
|
|
9510
|
-
lines.push("Rules:");
|
|
9511
|
-
lines.push('- Only include tasks from the "Tasks to Analyze" section');
|
|
9512
|
-
lines.push("- confidence 0.0 = no evidence of completion, 1.0 = certain it is complete");
|
|
9513
|
-
lines.push("- If you have no evidence for a task, omit it from the array");
|
|
9514
|
-
lines.push("- Be conservative \u2014 require clear evidence before high confidence");
|
|
9515
|
-
lines.push("- Respond ONLY with the JSON array, no additional text");
|
|
9516
|
-
return lines.join("\n");
|
|
10075
|
+
if (Array.isArray(value)) {
|
|
10076
|
+
return "[" + value.map(stableStringify).join(",") + "]";
|
|
10077
|
+
}
|
|
10078
|
+
const obj = value;
|
|
10079
|
+
const keys = Object.keys(obj).sort();
|
|
10080
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
|
|
9517
10081
|
}
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
10082
|
+
function encodeCallbackToken(taskName) {
|
|
10083
|
+
const payload = JSON.stringify({ t: taskName, n: Date.now().toString(36) + Math.random().toString(36).slice(2, 6) });
|
|
10084
|
+
return Buffer.from(payload).toString("base64url");
|
|
10085
|
+
}
|
|
10086
|
+
function decodeCallbackToken(token) {
|
|
10087
|
+
try {
|
|
10088
|
+
const payload = JSON.parse(Buffer.from(token, "base64url").toString());
|
|
10089
|
+
if (typeof payload?.t === "string") return { taskName: payload.t };
|
|
10090
|
+
return null;
|
|
10091
|
+
} catch {
|
|
10092
|
+
return null;
|
|
9523
10093
|
}
|
|
9524
|
-
const prompt = buildInferencePrompt(live, options);
|
|
9525
|
-
const rawResponse = await adapter.analyze(prompt);
|
|
9526
|
-
const suggestions = parseInferenceResponse(rawResponse, analyzedNodes);
|
|
9527
|
-
return {
|
|
9528
|
-
suggestions,
|
|
9529
|
-
promptUsed: prompt,
|
|
9530
|
-
rawResponse,
|
|
9531
|
-
analyzedNodes
|
|
9532
|
-
};
|
|
9533
10094
|
}
|
|
9534
|
-
function
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
10095
|
+
function createReactiveGraph(config, options, executionId) {
|
|
10096
|
+
const {
|
|
10097
|
+
handlers: initialHandlers,
|
|
10098
|
+
journal = new MemoryJournal(),
|
|
10099
|
+
onDrain
|
|
10100
|
+
} = options;
|
|
10101
|
+
let live = createLiveGraph(config, executionId);
|
|
10102
|
+
let disposed = false;
|
|
10103
|
+
const handlers = new Map(Object.entries(initialHandlers));
|
|
10104
|
+
let draining = false;
|
|
10105
|
+
let drainQueued = false;
|
|
10106
|
+
function drain() {
|
|
10107
|
+
if (disposed) return;
|
|
10108
|
+
if (draining) {
|
|
10109
|
+
drainQueued = true;
|
|
10110
|
+
return;
|
|
10111
|
+
}
|
|
10112
|
+
draining = true;
|
|
10113
|
+
try {
|
|
10114
|
+
do {
|
|
10115
|
+
drainQueued = false;
|
|
10116
|
+
drainOnce();
|
|
10117
|
+
} while (drainQueued);
|
|
10118
|
+
} finally {
|
|
10119
|
+
draining = false;
|
|
10120
|
+
}
|
|
10121
|
+
}
|
|
10122
|
+
function drainOnce() {
|
|
10123
|
+
const events = journal.drain();
|
|
10124
|
+
if (events.length > 0) {
|
|
10125
|
+
live = applyEvents(live, events);
|
|
10126
|
+
}
|
|
10127
|
+
const result = schedule(live);
|
|
10128
|
+
if (events.length > 0) {
|
|
10129
|
+
onDrain?.(events, live, result);
|
|
10130
|
+
}
|
|
10131
|
+
for (const taskName of result.eligible) {
|
|
10132
|
+
dispatchTask(taskName);
|
|
10133
|
+
}
|
|
10134
|
+
}
|
|
10135
|
+
function resolveUpstreamState(taskName) {
|
|
10136
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10137
|
+
const requires = taskConfig.requires ?? [];
|
|
10138
|
+
const tokenToTask = /* @__PURE__ */ new Map();
|
|
10139
|
+
for (const [name, cfg] of Object.entries(live.config.tasks)) {
|
|
10140
|
+
for (const token of cfg.provides ?? []) {
|
|
10141
|
+
tokenToTask.set(token, name);
|
|
10142
|
+
}
|
|
10143
|
+
}
|
|
10144
|
+
const state = {};
|
|
10145
|
+
for (const token of requires) {
|
|
10146
|
+
const producerTask = tokenToTask.get(token);
|
|
10147
|
+
if (producerTask) {
|
|
10148
|
+
state[token] = live.state.tasks[producerTask]?.data;
|
|
10149
|
+
} else {
|
|
10150
|
+
state[token] = void 0;
|
|
10151
|
+
}
|
|
10152
|
+
}
|
|
10153
|
+
return state;
|
|
10154
|
+
}
|
|
10155
|
+
async function runPipeline(taskName, callbackToken) {
|
|
10156
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10157
|
+
const handlerNames = taskConfig.taskHandlers ?? [];
|
|
10158
|
+
const upstreamState = resolveUpstreamState(taskName);
|
|
10159
|
+
for (const handlerName of handlerNames) {
|
|
10160
|
+
const handler = handlers.get(handlerName);
|
|
10161
|
+
if (!handler) {
|
|
10162
|
+
throw new Error(`Handler '${handlerName}' not found in registry (task '${taskName}')`);
|
|
10163
|
+
}
|
|
10164
|
+
const input = {
|
|
10165
|
+
nodeId: taskName,
|
|
10166
|
+
state: upstreamState,
|
|
10167
|
+
taskState: live.state.tasks[taskName],
|
|
10168
|
+
config: taskConfig,
|
|
10169
|
+
callbackToken
|
|
10170
|
+
};
|
|
10171
|
+
const status = await handler(input);
|
|
10172
|
+
if (status === "task-initiate-failure") {
|
|
10173
|
+
throw new Error(`Handler '${handlerName}' returned task-initiate-failure (task '${taskName}')`);
|
|
10174
|
+
}
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
function dispatchTask(taskName) {
|
|
10178
|
+
const taskConfig = live.config.tasks[taskName];
|
|
10179
|
+
const handlerNames = taskConfig?.taskHandlers;
|
|
10180
|
+
if (!handlerNames || handlerNames.length === 0) {
|
|
10181
|
+
return;
|
|
10182
|
+
}
|
|
10183
|
+
journal.append({
|
|
9543
10184
|
type: "task-started",
|
|
9544
|
-
taskName
|
|
9545
|
-
timestamp:
|
|
10185
|
+
taskName,
|
|
10186
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9546
10187
|
});
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
10188
|
+
const callbackToken = encodeCallbackToken(taskName);
|
|
10189
|
+
runPipeline(taskName, callbackToken).catch((error) => {
|
|
10190
|
+
if (disposed) return;
|
|
10191
|
+
journal.append({
|
|
10192
|
+
type: "task-failed",
|
|
10193
|
+
taskName,
|
|
10194
|
+
error: error.message ?? String(error),
|
|
10195
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10196
|
+
});
|
|
10197
|
+
drain();
|
|
9552
10198
|
});
|
|
9553
10199
|
}
|
|
9554
|
-
return current;
|
|
9555
|
-
}
|
|
9556
|
-
async function inferAndApply(live, adapter, options = {}) {
|
|
9557
|
-
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
9558
|
-
const inference = await inferCompletions(live, adapter, options);
|
|
9559
|
-
const updated = applyInferences(live, inference, threshold);
|
|
9560
|
-
const applied = inference.suggestions.filter((s) => s.confidence >= threshold);
|
|
9561
|
-
const skipped = inference.suggestions.filter((s) => s.confidence < threshold);
|
|
9562
10200
|
return {
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
10201
|
+
push(event) {
|
|
10202
|
+
if (disposed) return;
|
|
10203
|
+
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
10204
|
+
event = { ...event, dataHash: computeDataHash(event.data) };
|
|
10205
|
+
}
|
|
10206
|
+
journal.append(event);
|
|
10207
|
+
drain();
|
|
10208
|
+
},
|
|
10209
|
+
pushAll(events) {
|
|
10210
|
+
if (disposed) return;
|
|
10211
|
+
for (const event of events) {
|
|
10212
|
+
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
10213
|
+
journal.append({ ...event, dataHash: computeDataHash(event.data) });
|
|
10214
|
+
} else {
|
|
10215
|
+
journal.append(event);
|
|
10216
|
+
}
|
|
10217
|
+
}
|
|
10218
|
+
drain();
|
|
10219
|
+
},
|
|
10220
|
+
resolveCallback(callbackToken, data, errors) {
|
|
10221
|
+
if (disposed) return;
|
|
10222
|
+
const decoded = decodeCallbackToken(callbackToken);
|
|
10223
|
+
if (!decoded) return;
|
|
10224
|
+
const { taskName } = decoded;
|
|
10225
|
+
if (!live.config.tasks[taskName]) return;
|
|
10226
|
+
if (errors && errors.length > 0) {
|
|
10227
|
+
journal.append({
|
|
10228
|
+
type: "task-failed",
|
|
10229
|
+
taskName,
|
|
10230
|
+
error: errors.join("; "),
|
|
10231
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10232
|
+
});
|
|
10233
|
+
} else {
|
|
10234
|
+
const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
|
|
10235
|
+
journal.append({
|
|
10236
|
+
type: "task-completed",
|
|
10237
|
+
taskName,
|
|
10238
|
+
data,
|
|
10239
|
+
dataHash,
|
|
10240
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10241
|
+
});
|
|
10242
|
+
}
|
|
10243
|
+
drain();
|
|
10244
|
+
},
|
|
10245
|
+
addNode(name, taskConfig) {
|
|
10246
|
+
if (disposed) return;
|
|
10247
|
+
live = addNode(live, name, taskConfig);
|
|
10248
|
+
drain();
|
|
10249
|
+
},
|
|
10250
|
+
removeNode(name) {
|
|
10251
|
+
if (disposed) return;
|
|
10252
|
+
live = removeNode(live, name);
|
|
10253
|
+
},
|
|
10254
|
+
addRequires(nodeName, tokens) {
|
|
10255
|
+
if (disposed) return;
|
|
10256
|
+
live = addRequires(live, nodeName, tokens);
|
|
10257
|
+
drain();
|
|
10258
|
+
},
|
|
10259
|
+
removeRequires(nodeName, tokens) {
|
|
10260
|
+
if (disposed) return;
|
|
10261
|
+
live = removeRequires(live, nodeName, tokens);
|
|
10262
|
+
drain();
|
|
10263
|
+
},
|
|
10264
|
+
addProvides(nodeName, tokens) {
|
|
10265
|
+
if (disposed) return;
|
|
10266
|
+
live = addProvides(live, nodeName, tokens);
|
|
10267
|
+
drain();
|
|
10268
|
+
},
|
|
10269
|
+
removeProvides(nodeName, tokens) {
|
|
10270
|
+
if (disposed) return;
|
|
10271
|
+
live = removeProvides(live, nodeName, tokens);
|
|
10272
|
+
},
|
|
10273
|
+
registerHandler(name, fn) {
|
|
10274
|
+
handlers.set(name, fn);
|
|
10275
|
+
},
|
|
10276
|
+
unregisterHandler(name) {
|
|
10277
|
+
handlers.delete(name);
|
|
10278
|
+
},
|
|
10279
|
+
retrigger(taskName) {
|
|
10280
|
+
if (disposed) return;
|
|
10281
|
+
if (!live.config.tasks[taskName]) return;
|
|
10282
|
+
journal.append({
|
|
10283
|
+
type: "task-restart",
|
|
10284
|
+
taskName,
|
|
10285
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10286
|
+
});
|
|
10287
|
+
drain();
|
|
10288
|
+
},
|
|
10289
|
+
retriggerAll(taskNames) {
|
|
10290
|
+
if (disposed) return;
|
|
10291
|
+
for (const name of taskNames) {
|
|
10292
|
+
if (!live.config.tasks[name]) continue;
|
|
10293
|
+
journal.append({
|
|
10294
|
+
type: "task-restart",
|
|
10295
|
+
taskName: name,
|
|
10296
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10297
|
+
});
|
|
10298
|
+
}
|
|
10299
|
+
drain();
|
|
10300
|
+
},
|
|
10301
|
+
getState() {
|
|
10302
|
+
return live;
|
|
10303
|
+
},
|
|
10304
|
+
getSchedule() {
|
|
10305
|
+
return schedule(live);
|
|
10306
|
+
},
|
|
10307
|
+
dispose() {
|
|
10308
|
+
disposed = true;
|
|
10309
|
+
}
|
|
9567
10310
|
};
|
|
9568
10311
|
}
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
const
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
10312
|
+
|
|
10313
|
+
// src/continuous-event-graph/validate.ts
|
|
10314
|
+
function validateLiveGraph(live) {
|
|
10315
|
+
const issues = [];
|
|
10316
|
+
const { config, state } = live;
|
|
10317
|
+
const tasks = getAllTasks(config);
|
|
10318
|
+
const taskNames = Object.keys(tasks);
|
|
10319
|
+
for (const name of taskNames) {
|
|
10320
|
+
if (!state.tasks[name]) {
|
|
10321
|
+
issues.push({
|
|
10322
|
+
severity: "error",
|
|
10323
|
+
code: "MISSING_STATE",
|
|
10324
|
+
message: `Task "${name}" exists in config but has no state entry`,
|
|
10325
|
+
tasks: [name]
|
|
10326
|
+
});
|
|
9580
10327
|
}
|
|
9581
10328
|
}
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
const parsed = JSON.parse(jsonStr);
|
|
9590
|
-
if (!Array.isArray(parsed)) return [];
|
|
9591
|
-
const suggestions = [];
|
|
9592
|
-
for (const item of parsed) {
|
|
9593
|
-
if (!item || typeof item !== "object") continue;
|
|
9594
|
-
if (typeof item.taskName !== "string") continue;
|
|
9595
|
-
if (typeof item.confidence !== "number") continue;
|
|
9596
|
-
if (!validSet.has(item.taskName)) continue;
|
|
9597
|
-
const confidence = Math.max(0, Math.min(1, item.confidence));
|
|
9598
|
-
suggestions.push({
|
|
9599
|
-
taskName: item.taskName,
|
|
9600
|
-
confidence,
|
|
9601
|
-
reasoning: typeof item.reasoning === "string" ? item.reasoning : "",
|
|
9602
|
-
detectionMethod: "llm-inferred"
|
|
10329
|
+
for (const name of Object.keys(state.tasks)) {
|
|
10330
|
+
if (!tasks[name]) {
|
|
10331
|
+
issues.push({
|
|
10332
|
+
severity: "warning",
|
|
10333
|
+
code: "ORPHAN_STATE",
|
|
10334
|
+
message: `State entry "${name}" has no corresponding task config`,
|
|
10335
|
+
tasks: [name]
|
|
9603
10336
|
});
|
|
9604
10337
|
}
|
|
9605
|
-
return suggestions;
|
|
9606
|
-
} catch {
|
|
9607
|
-
return [];
|
|
9608
10338
|
}
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
10339
|
+
for (const name of taskNames) {
|
|
10340
|
+
const ts = state.tasks[name];
|
|
10341
|
+
if (!ts) continue;
|
|
10342
|
+
if (ts.status === TASK_STATUS.RUNNING && !ts.startedAt) {
|
|
10343
|
+
issues.push({
|
|
10344
|
+
severity: "warning",
|
|
10345
|
+
code: "RUNNING_WITHOUT_START",
|
|
10346
|
+
message: `Task "${name}" is running but has no startedAt timestamp`,
|
|
10347
|
+
tasks: [name]
|
|
10348
|
+
});
|
|
10349
|
+
}
|
|
10350
|
+
if (ts.status === TASK_STATUS.COMPLETED && !ts.completedAt) {
|
|
10351
|
+
issues.push({
|
|
10352
|
+
severity: "warning",
|
|
10353
|
+
code: "COMPLETED_WITHOUT_TIMESTAMP",
|
|
10354
|
+
message: `Task "${name}" is completed but has no completedAt timestamp`,
|
|
10355
|
+
tasks: [name]
|
|
10356
|
+
});
|
|
10357
|
+
}
|
|
10358
|
+
if (ts.status === TASK_STATUS.FAILED) {
|
|
10359
|
+
if (!ts.failedAt) {
|
|
10360
|
+
issues.push({
|
|
10361
|
+
severity: "warning",
|
|
10362
|
+
code: "FAILED_WITHOUT_INFO",
|
|
10363
|
+
message: `Task "${name}" is failed but has no failedAt timestamp`,
|
|
10364
|
+
tasks: [name]
|
|
10365
|
+
});
|
|
10366
|
+
}
|
|
10367
|
+
if (!ts.error) {
|
|
10368
|
+
issues.push({
|
|
10369
|
+
severity: "info",
|
|
10370
|
+
code: "FAILED_WITHOUT_INFO",
|
|
10371
|
+
message: `Task "${name}" is failed but has no error message`,
|
|
10372
|
+
tasks: [name]
|
|
10373
|
+
});
|
|
10374
|
+
}
|
|
10375
|
+
}
|
|
9619
10376
|
}
|
|
9620
|
-
|
|
9621
|
-
|
|
10377
|
+
const expectedOutputs = /* @__PURE__ */ new Set();
|
|
10378
|
+
for (const name of taskNames) {
|
|
10379
|
+
const ts = state.tasks[name];
|
|
10380
|
+
if (ts?.status === TASK_STATUS.COMPLETED) {
|
|
10381
|
+
for (const token of getProvides(tasks[name])) {
|
|
10382
|
+
expectedOutputs.add(token);
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
}
|
|
10386
|
+
const actualOutputs = new Set(state.availableOutputs);
|
|
10387
|
+
const allProducible = /* @__PURE__ */ new Set();
|
|
10388
|
+
for (const taskConfig of Object.values(tasks)) {
|
|
10389
|
+
for (const t of getProvides(taskConfig)) allProducible.add(t);
|
|
10390
|
+
if (taskConfig.on) {
|
|
10391
|
+
for (const tokens of Object.values(taskConfig.on)) {
|
|
10392
|
+
for (const t of tokens) allProducible.add(t);
|
|
10393
|
+
}
|
|
10394
|
+
}
|
|
10395
|
+
if (taskConfig.on_failure) {
|
|
10396
|
+
for (const t of taskConfig.on_failure) allProducible.add(t);
|
|
10397
|
+
}
|
|
10398
|
+
}
|
|
10399
|
+
for (const token of actualOutputs) {
|
|
10400
|
+
if (!expectedOutputs.has(token) && !allProducible.has(token)) {
|
|
10401
|
+
issues.push({
|
|
10402
|
+
severity: "info",
|
|
10403
|
+
code: "INJECTED_TOKEN",
|
|
10404
|
+
message: `Token "${token}" is available but no task in the graph can produce it (likely injected)`,
|
|
10405
|
+
tokens: [token]
|
|
10406
|
+
});
|
|
10407
|
+
}
|
|
10408
|
+
}
|
|
10409
|
+
for (const token of expectedOutputs) {
|
|
10410
|
+
if (!actualOutputs.has(token)) {
|
|
10411
|
+
issues.push({
|
|
10412
|
+
severity: "warning",
|
|
10413
|
+
code: "MISSING_OUTPUT",
|
|
10414
|
+
message: `Token "${token}" should be available (its producer completed) but is not in availableOutputs`,
|
|
10415
|
+
tokens: [token]
|
|
10416
|
+
});
|
|
10417
|
+
}
|
|
10418
|
+
}
|
|
10419
|
+
for (const name of taskNames) {
|
|
10420
|
+
const ts = state.tasks[name];
|
|
10421
|
+
if (!ts) continue;
|
|
10422
|
+
if (ts.executionCount < 0) {
|
|
10423
|
+
issues.push({
|
|
10424
|
+
severity: "error",
|
|
10425
|
+
code: "INVALID_EXECUTION_COUNT",
|
|
10426
|
+
message: `Task "${name}" has negative execution count: ${ts.executionCount}`,
|
|
10427
|
+
tasks: [name]
|
|
10428
|
+
});
|
|
10429
|
+
}
|
|
10430
|
+
const maxExec = tasks[name].maxExecutions;
|
|
10431
|
+
if (maxExec !== void 0 && ts.executionCount > maxExec) {
|
|
10432
|
+
issues.push({
|
|
10433
|
+
severity: "error",
|
|
10434
|
+
code: "EXCEEDED_MAX_EXECUTIONS",
|
|
10435
|
+
message: `Task "${name}" executed ${ts.executionCount} times, exceeding maxExecutions of ${maxExec}`,
|
|
10436
|
+
tasks: [name]
|
|
10437
|
+
});
|
|
10438
|
+
}
|
|
10439
|
+
}
|
|
10440
|
+
return buildResult2(issues);
|
|
9622
10441
|
}
|
|
9623
|
-
function
|
|
9624
|
-
const
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
10442
|
+
function validateReactiveGraph(input) {
|
|
10443
|
+
const { graph, handlers } = input;
|
|
10444
|
+
const live = graph.getState();
|
|
10445
|
+
const issues = [];
|
|
10446
|
+
const tasks = getAllTasks(live.config);
|
|
10447
|
+
const taskNames = Object.keys(tasks);
|
|
10448
|
+
const handlerNames = new Set(Object.keys(handlers));
|
|
10449
|
+
const referencedHandlers = /* @__PURE__ */ new Set();
|
|
10450
|
+
for (const name of taskNames) {
|
|
10451
|
+
const taskHandlers = tasks[name].taskHandlers;
|
|
10452
|
+
if (taskHandlers) {
|
|
10453
|
+
for (const h of taskHandlers) {
|
|
10454
|
+
referencedHandlers.add(h);
|
|
10455
|
+
}
|
|
10456
|
+
}
|
|
10457
|
+
}
|
|
10458
|
+
for (const name of taskNames) {
|
|
10459
|
+
const taskHandlers = tasks[name].taskHandlers;
|
|
10460
|
+
if (!taskHandlers) continue;
|
|
10461
|
+
for (const h of taskHandlers) {
|
|
10462
|
+
if (!handlers[h]) {
|
|
10463
|
+
issues.push({
|
|
10464
|
+
severity: "error",
|
|
10465
|
+
code: "MISSING_HANDLER",
|
|
10466
|
+
message: `Task "${name}" references handler "${h}" but it is not in the registry`,
|
|
10467
|
+
tasks: [name]
|
|
10468
|
+
});
|
|
10469
|
+
}
|
|
10470
|
+
}
|
|
10471
|
+
}
|
|
10472
|
+
for (const name of handlerNames) {
|
|
10473
|
+
if (!referencedHandlers.has(name)) {
|
|
10474
|
+
issues.push({
|
|
10475
|
+
severity: "warning",
|
|
10476
|
+
code: "ORPHAN_HANDLER",
|
|
10477
|
+
message: `Handler "${name}" is registered but not referenced by any task's taskHandlers`,
|
|
10478
|
+
tasks: [name]
|
|
9655
10479
|
});
|
|
9656
10480
|
}
|
|
9657
|
-
}
|
|
10481
|
+
}
|
|
10482
|
+
const liveResult = validateLiveGraph(live);
|
|
10483
|
+
issues.push(...liveResult.issues);
|
|
10484
|
+
return buildResult2(issues);
|
|
9658
10485
|
}
|
|
9659
|
-
function
|
|
9660
|
-
const
|
|
10486
|
+
function buildResult2(issues) {
|
|
10487
|
+
const errors = issues.filter((i) => i.severity === "error");
|
|
10488
|
+
const warnings = issues.filter((i) => i.severity === "warning");
|
|
9661
10489
|
return {
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
10490
|
+
valid: errors.length === 0,
|
|
10491
|
+
issues,
|
|
10492
|
+
errors,
|
|
10493
|
+
warnings
|
|
10494
|
+
};
|
|
10495
|
+
}
|
|
10496
|
+
|
|
10497
|
+
// src/continuous-event-graph/mutate.ts
|
|
10498
|
+
function mutateGraph(live, mutations) {
|
|
10499
|
+
let current = live;
|
|
10500
|
+
for (const mutation of mutations) {
|
|
10501
|
+
current = applySingleMutation(current, mutation);
|
|
10502
|
+
}
|
|
10503
|
+
return current;
|
|
10504
|
+
}
|
|
10505
|
+
function applySingleMutation(live, mutation) {
|
|
10506
|
+
switch (mutation.type) {
|
|
10507
|
+
case "add-node":
|
|
10508
|
+
return addNode(live, mutation.name, mutation.config);
|
|
10509
|
+
case "remove-node":
|
|
10510
|
+
return removeNode(live, mutation.name);
|
|
10511
|
+
case "add-requires":
|
|
10512
|
+
return addRequires(live, mutation.taskName, mutation.tokens);
|
|
10513
|
+
case "remove-requires":
|
|
10514
|
+
return removeRequires(live, mutation.taskName, mutation.tokens);
|
|
10515
|
+
case "add-provides":
|
|
10516
|
+
return addProvides(live, mutation.taskName, mutation.tokens);
|
|
10517
|
+
case "remove-provides":
|
|
10518
|
+
return removeProvides(live, mutation.taskName, mutation.tokens);
|
|
10519
|
+
case "inject-tokens":
|
|
10520
|
+
return injectTokens(live, mutation.tokens);
|
|
10521
|
+
case "drain-tokens":
|
|
10522
|
+
return drainTokens(live, mutation.tokens);
|
|
10523
|
+
case "reset-node":
|
|
10524
|
+
return resetNode(live, mutation.name);
|
|
10525
|
+
case "disable-node":
|
|
10526
|
+
return disableNode(live, mutation.name);
|
|
10527
|
+
case "enable-node":
|
|
10528
|
+
return enableNode(live, mutation.name);
|
|
10529
|
+
case "apply-events":
|
|
10530
|
+
return applyEvents(live, mutation.events);
|
|
10531
|
+
default:
|
|
10532
|
+
throw new Error(`Unknown mutation type: ${mutation.type}`);
|
|
10533
|
+
}
|
|
10534
|
+
}
|
|
10535
|
+
function createCallbackHandler(fn, getResolve) {
|
|
10536
|
+
return async (input) => {
|
|
10537
|
+
const { callbackToken } = input;
|
|
10538
|
+
Promise.resolve(fn(input)).then((data) => getResolve()(callbackToken, data)).catch((err) => getResolve()(callbackToken, {}, [err instanceof Error ? err.message : String(err)]));
|
|
10539
|
+
return "task-initiated";
|
|
10540
|
+
};
|
|
10541
|
+
}
|
|
10542
|
+
function createFireAndForgetHandler(fn, getResolve) {
|
|
10543
|
+
return async (input) => {
|
|
10544
|
+
const { callbackToken } = input;
|
|
10545
|
+
Promise.resolve(fn(input)).then(() => getResolve()(callbackToken, {})).catch(() => getResolve()(callbackToken, {}));
|
|
10546
|
+
return "task-initiated";
|
|
10547
|
+
};
|
|
10548
|
+
}
|
|
10549
|
+
function createShellHandler(options) {
|
|
10550
|
+
const {
|
|
10551
|
+
command: commandTemplate,
|
|
10552
|
+
cwd,
|
|
10553
|
+
env,
|
|
10554
|
+
timeoutMs = 3e4,
|
|
10555
|
+
exitCodeMap,
|
|
10556
|
+
captureOutput = false,
|
|
10557
|
+
getResolve
|
|
10558
|
+
} = options;
|
|
10559
|
+
return async (input) => {
|
|
10560
|
+
const { callbackToken, nodeId } = input;
|
|
10561
|
+
const command = commandTemplate.replace(/\$\{taskName\}/g, nodeId);
|
|
10562
|
+
exec(
|
|
10563
|
+
command,
|
|
10564
|
+
{
|
|
10565
|
+
cwd,
|
|
10566
|
+
env: env ? { ...process.env, ...env } : void 0,
|
|
10567
|
+
timeout: timeoutMs,
|
|
10568
|
+
maxBuffer: 10 * 1024 * 1024
|
|
10569
|
+
// 10MB
|
|
10570
|
+
},
|
|
10571
|
+
(error, stdout, stderr) => {
|
|
10572
|
+
const exitCode = error?.code ?? (error ? 1 : 0);
|
|
10573
|
+
if (exitCode !== 0 && !exitCodeMap?.[exitCode]) {
|
|
10574
|
+
getResolve()(callbackToken, {}, [`Command exited with code ${exitCode}: ${stderr || error?.message}`]);
|
|
10575
|
+
return;
|
|
9679
10576
|
}
|
|
9680
|
-
const
|
|
9681
|
-
if (
|
|
9682
|
-
|
|
10577
|
+
const data = {};
|
|
10578
|
+
if (captureOutput) {
|
|
10579
|
+
data.stdout = stdout;
|
|
10580
|
+
data.stderr = stderr;
|
|
10581
|
+
data.exitCode = exitCode;
|
|
9683
10582
|
}
|
|
9684
|
-
|
|
9685
|
-
if (typeof json.text === "string") return json.text;
|
|
9686
|
-
if (typeof json.content === "string") return json.content;
|
|
9687
|
-
return JSON.stringify(json);
|
|
9688
|
-
} finally {
|
|
9689
|
-
clearTimeout(timer);
|
|
10583
|
+
getResolve()(callbackToken, data);
|
|
9690
10584
|
}
|
|
9691
|
-
|
|
10585
|
+
);
|
|
10586
|
+
return "task-initiated";
|
|
10587
|
+
};
|
|
10588
|
+
}
|
|
10589
|
+
function detectRuntime(scriptPath) {
|
|
10590
|
+
if (scriptPath.endsWith(".js") || scriptPath.endsWith(".mjs") || scriptPath.endsWith(".ts")) return "node";
|
|
10591
|
+
if (scriptPath.endsWith(".py")) return "python3";
|
|
10592
|
+
if (scriptPath.endsWith(".sh")) return "bash";
|
|
10593
|
+
return "bash";
|
|
10594
|
+
}
|
|
10595
|
+
function createScriptHandler(options) {
|
|
10596
|
+
const {
|
|
10597
|
+
scriptPath,
|
|
10598
|
+
runtime,
|
|
10599
|
+
args = [],
|
|
10600
|
+
cwd,
|
|
10601
|
+
timeoutMs = 6e4,
|
|
10602
|
+
captureOutput = false,
|
|
10603
|
+
getResolve
|
|
10604
|
+
} = options;
|
|
10605
|
+
const resolvedRuntime = runtime ?? detectRuntime(scriptPath);
|
|
10606
|
+
const shellArgs = [ctx_taskName_placeholder, ...args].join(" ");
|
|
10607
|
+
const command = `${resolvedRuntime} ${scriptPath} ${shellArgs}`;
|
|
10608
|
+
return createShellHandler({
|
|
10609
|
+
command: command.replace(ctx_taskName_placeholder, "${taskName}"),
|
|
10610
|
+
cwd,
|
|
10611
|
+
timeoutMs,
|
|
10612
|
+
captureOutput,
|
|
10613
|
+
getResolve
|
|
10614
|
+
});
|
|
10615
|
+
}
|
|
10616
|
+
var ctx_taskName_placeholder = "__TASK_NAME__";
|
|
10617
|
+
function createWebhookHandler(options) {
|
|
10618
|
+
const {
|
|
10619
|
+
url: urlTemplate,
|
|
10620
|
+
method = "POST",
|
|
10621
|
+
headers = {},
|
|
10622
|
+
timeoutMs = 3e4,
|
|
10623
|
+
failOnNon2xx = true,
|
|
10624
|
+
getResolve
|
|
10625
|
+
} = options;
|
|
10626
|
+
return async (input) => {
|
|
10627
|
+
const { callbackToken, nodeId, config } = input;
|
|
10628
|
+
const url = urlTemplate.replace(/\$\{taskName\}/g, nodeId);
|
|
10629
|
+
const body = JSON.stringify({
|
|
10630
|
+
taskName: nodeId,
|
|
10631
|
+
callbackToken,
|
|
10632
|
+
config
|
|
10633
|
+
});
|
|
10634
|
+
const controller = new AbortController();
|
|
10635
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
10636
|
+
fetch(url, {
|
|
10637
|
+
method,
|
|
10638
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
10639
|
+
body,
|
|
10640
|
+
signal: controller.signal
|
|
10641
|
+
}).then(async (response) => {
|
|
10642
|
+
clearTimeout(timer);
|
|
10643
|
+
if (failOnNon2xx && !response.ok) {
|
|
10644
|
+
const text = await response.text().catch(() => "");
|
|
10645
|
+
getResolve()(callbackToken, {}, [`HTTP ${response.status}: ${text}`]);
|
|
10646
|
+
return;
|
|
10647
|
+
}
|
|
10648
|
+
const data = await response.json().catch(() => ({}));
|
|
10649
|
+
getResolve()(callbackToken, data);
|
|
10650
|
+
}).catch((err) => {
|
|
10651
|
+
clearTimeout(timer);
|
|
10652
|
+
getResolve()(callbackToken, {}, [err instanceof Error ? err.message : String(err)]);
|
|
10653
|
+
});
|
|
10654
|
+
return "task-initiated";
|
|
10655
|
+
};
|
|
10656
|
+
}
|
|
10657
|
+
function createNoopHandler(getResolve, staticData) {
|
|
10658
|
+
return async (input) => {
|
|
10659
|
+
getResolve()(input.callbackToken, staticData ?? {});
|
|
10660
|
+
return "task-initiated";
|
|
9692
10661
|
};
|
|
9693
10662
|
}
|
|
9694
10663
|
|
|
@@ -10005,17 +10974,17 @@ var live_cards_schema_default = {
|
|
|
10005
10974
|
};
|
|
10006
10975
|
|
|
10007
10976
|
// src/card-compute/schema-validator.ts
|
|
10008
|
-
var
|
|
10009
|
-
var
|
|
10010
|
-
function
|
|
10011
|
-
if (
|
|
10012
|
-
const ajv = new
|
|
10977
|
+
var import_ajv3 = __toESM(require_ajv());
|
|
10978
|
+
var _compiled3 = null;
|
|
10979
|
+
function getValidator3() {
|
|
10980
|
+
if (_compiled3) return _compiled3;
|
|
10981
|
+
const ajv = new import_ajv3.default({ allErrors: true });
|
|
10013
10982
|
addFormats(ajv);
|
|
10014
|
-
|
|
10015
|
-
return
|
|
10983
|
+
_compiled3 = ajv.compile(live_cards_schema_default);
|
|
10984
|
+
return _compiled3;
|
|
10016
10985
|
}
|
|
10017
10986
|
function validateLiveCardSchema(node) {
|
|
10018
|
-
const validate =
|
|
10987
|
+
const validate = getValidator3();
|
|
10019
10988
|
const valid = validate(node);
|
|
10020
10989
|
if (valid) return { ok: true, errors: [] };
|
|
10021
10990
|
const errors = (validate.errors ?? []).map((e) => {
|
|
@@ -10472,6 +11441,405 @@ var CardCompute = {
|
|
|
10472
11441
|
}
|
|
10473
11442
|
};
|
|
10474
11443
|
|
|
10475
|
-
|
|
11444
|
+
// src/continuous-event-graph/live-cards-bridge.ts
|
|
11445
|
+
function liveCardsToReactiveGraph(input, options = {}) {
|
|
11446
|
+
let cards;
|
|
11447
|
+
let boardSettings = {};
|
|
11448
|
+
let boardId;
|
|
11449
|
+
if (!Array.isArray(input) && "nodes" in input) {
|
|
11450
|
+
const board = input;
|
|
11451
|
+
cards = board.nodes;
|
|
11452
|
+
boardId = board.id;
|
|
11453
|
+
boardSettings = board.settings ?? {};
|
|
11454
|
+
} else {
|
|
11455
|
+
cards = input;
|
|
11456
|
+
}
|
|
11457
|
+
const {
|
|
11458
|
+
sourceHandlers = {},
|
|
11459
|
+
defaultSourceHandler,
|
|
11460
|
+
cardHandlers = {},
|
|
11461
|
+
reactiveOptions = {},
|
|
11462
|
+
graphSettings = {},
|
|
11463
|
+
executionId
|
|
11464
|
+
} = options;
|
|
11465
|
+
const cardMap = /* @__PURE__ */ new Map();
|
|
11466
|
+
for (const card of cards) {
|
|
11467
|
+
if (cardMap.has(card.id)) {
|
|
11468
|
+
throw new Error(`Duplicate card ID: "${card.id}"`);
|
|
11469
|
+
}
|
|
11470
|
+
cardMap.set(card.id, card);
|
|
11471
|
+
}
|
|
11472
|
+
const sharedState = options.sharedState ?? /* @__PURE__ */ new Map();
|
|
11473
|
+
const tasks = {};
|
|
11474
|
+
for (const card of cards) {
|
|
11475
|
+
const requires = card.data?.requires ?? [];
|
|
11476
|
+
for (const req of requires) {
|
|
11477
|
+
if (!cardMap.has(req)) {
|
|
11478
|
+
throw new Error(`Card "${card.id}" requires "${req}" but no card with that ID exists`);
|
|
11479
|
+
}
|
|
11480
|
+
}
|
|
11481
|
+
tasks[card.id] = {
|
|
11482
|
+
requires: requires.length > 0 ? requires : void 0,
|
|
11483
|
+
provides: [card.id],
|
|
11484
|
+
taskHandlers: [card.id],
|
|
11485
|
+
// each card has a named handler matching its ID
|
|
11486
|
+
description: card.meta?.title ?? `${card.type}: ${card.id}`
|
|
11487
|
+
};
|
|
11488
|
+
}
|
|
11489
|
+
const config = {
|
|
11490
|
+
id: boardId ?? `live-cards-${Date.now()}`,
|
|
11491
|
+
settings: {
|
|
11492
|
+
completion: "manual",
|
|
11493
|
+
execution_mode: "eligibility-mode",
|
|
11494
|
+
...boardSettings,
|
|
11495
|
+
...graphSettings
|
|
11496
|
+
},
|
|
11497
|
+
tasks
|
|
11498
|
+
};
|
|
11499
|
+
const handlers = {};
|
|
11500
|
+
let graphRef = null;
|
|
11501
|
+
const getResolve = () => (token, data, errors) => {
|
|
11502
|
+
graphRef.resolveCallback(token, data, errors);
|
|
11503
|
+
};
|
|
11504
|
+
for (const card of cards) {
|
|
11505
|
+
if (card.type === "source") {
|
|
11506
|
+
handlers[card.id] = buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve);
|
|
11507
|
+
} else {
|
|
11508
|
+
handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve);
|
|
11509
|
+
}
|
|
11510
|
+
}
|
|
11511
|
+
const graph = createReactiveGraph(
|
|
11512
|
+
config,
|
|
11513
|
+
{
|
|
11514
|
+
...reactiveOptions,
|
|
11515
|
+
handlers
|
|
11516
|
+
},
|
|
11517
|
+
executionId
|
|
11518
|
+
);
|
|
11519
|
+
graphRef = graph;
|
|
11520
|
+
return { graph, config, handlers, cards: cardMap, sharedState };
|
|
11521
|
+
}
|
|
11522
|
+
function buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve) {
|
|
11523
|
+
if (sourceHandlers[card.id]) {
|
|
11524
|
+
const userHandler = sourceHandlers[card.id];
|
|
11525
|
+
return async (input) => {
|
|
11526
|
+
return userHandler(input);
|
|
11527
|
+
};
|
|
11528
|
+
}
|
|
11529
|
+
if (defaultSourceHandler) {
|
|
11530
|
+
const factoryHandler = defaultSourceHandler(card);
|
|
11531
|
+
return async (input) => {
|
|
11532
|
+
return factoryHandler(input);
|
|
11533
|
+
};
|
|
11534
|
+
}
|
|
11535
|
+
return async (input) => {
|
|
11536
|
+
const state = { ...card.state };
|
|
11537
|
+
sharedState.set(card.id, state);
|
|
11538
|
+
getResolve()(input.callbackToken, state);
|
|
11539
|
+
return "task-initiated";
|
|
11540
|
+
};
|
|
11541
|
+
}
|
|
11542
|
+
function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve) {
|
|
11543
|
+
if (cardHandlers[card.id]) {
|
|
11544
|
+
const userHandler = cardHandlers[card.id];
|
|
11545
|
+
return async (input) => {
|
|
11546
|
+
return userHandler(input);
|
|
11547
|
+
};
|
|
11548
|
+
}
|
|
11549
|
+
return async (input) => {
|
|
11550
|
+
const computeNode = {
|
|
11551
|
+
id: card.id,
|
|
11552
|
+
state: { ...card.state },
|
|
11553
|
+
compute: card.compute
|
|
11554
|
+
};
|
|
11555
|
+
const requires = card.data?.requires ?? [];
|
|
11556
|
+
for (const upstreamId of requires) {
|
|
11557
|
+
const upstreamState = sharedState.get(upstreamId);
|
|
11558
|
+
if (upstreamState) {
|
|
11559
|
+
computeNode.state[upstreamId] = upstreamState;
|
|
11560
|
+
}
|
|
11561
|
+
const upstreamCard = cardMap.get(upstreamId);
|
|
11562
|
+
if (upstreamCard?.data?.provides && upstreamState) {
|
|
11563
|
+
for (const [key, bindRef] of Object.entries(upstreamCard.data.provides)) {
|
|
11564
|
+
if (typeof bindRef === "string" && bindRef.startsWith("state.")) {
|
|
11565
|
+
const path = bindRef.slice(6);
|
|
11566
|
+
const value = deepGet2(upstreamState, path);
|
|
11567
|
+
if (value !== void 0) {
|
|
11568
|
+
computeNode.state[key] = value;
|
|
11569
|
+
}
|
|
11570
|
+
}
|
|
11571
|
+
}
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
CardCompute.run(computeNode);
|
|
11575
|
+
const resultState = { ...computeNode.state };
|
|
11576
|
+
sharedState.set(card.id, resultState);
|
|
11577
|
+
getResolve()(input.callbackToken, resultState);
|
|
11578
|
+
return "task-initiated";
|
|
11579
|
+
};
|
|
11580
|
+
}
|
|
11581
|
+
function deepGet2(obj, path) {
|
|
11582
|
+
if (!path || !obj) return void 0;
|
|
11583
|
+
const parts = path.split(".");
|
|
11584
|
+
let cur = obj;
|
|
11585
|
+
for (const part of parts) {
|
|
11586
|
+
if (cur == null) return void 0;
|
|
11587
|
+
cur = cur[part];
|
|
11588
|
+
}
|
|
11589
|
+
return cur;
|
|
11590
|
+
}
|
|
11591
|
+
|
|
11592
|
+
// src/inference/core.ts
|
|
11593
|
+
var DEFAULT_THRESHOLD = 0.5;
|
|
11594
|
+
var DEFAULT_SYSTEM_PROMPT = `You are a workflow completion analyzer. Given a graph of tasks with their current states, evidence, and inference hints, determine which tasks appear to be completed based on the available evidence.
|
|
11595
|
+
|
|
11596
|
+
For each task you analyze, provide a JSON response. Be conservative \u2014 only mark tasks as completed when the evidence strongly supports it.`;
|
|
11597
|
+
function buildInferencePrompt(live, options = {}) {
|
|
11598
|
+
const { scope, context, systemPrompt } = options;
|
|
11599
|
+
const graphTasks = getAllTasks(live.config);
|
|
11600
|
+
const { state } = live;
|
|
11601
|
+
const candidates = getAnalyzableCandidates(live, scope);
|
|
11602
|
+
if (candidates.length === 0) {
|
|
11603
|
+
return "";
|
|
11604
|
+
}
|
|
11605
|
+
const lines = [];
|
|
11606
|
+
lines.push(systemPrompt || DEFAULT_SYSTEM_PROMPT);
|
|
11607
|
+
lines.push("");
|
|
11608
|
+
lines.push("## Graph State");
|
|
11609
|
+
lines.push("");
|
|
11610
|
+
lines.push(`Available tokens: ${state.availableOutputs.length > 0 ? state.availableOutputs.join(", ") : "(none)"}`);
|
|
11611
|
+
lines.push("");
|
|
11612
|
+
const completedTasks = Object.entries(state.tasks).filter(([_, ts]) => ts.status === "completed").map(([name]) => name);
|
|
11613
|
+
if (completedTasks.length > 0) {
|
|
11614
|
+
lines.push(`Completed tasks: ${completedTasks.join(", ")}`);
|
|
11615
|
+
lines.push("");
|
|
11616
|
+
}
|
|
11617
|
+
lines.push("## Tasks to Analyze");
|
|
11618
|
+
lines.push("");
|
|
11619
|
+
for (const taskName of candidates) {
|
|
11620
|
+
const taskConfig = graphTasks[taskName];
|
|
11621
|
+
const taskState = state.tasks[taskName];
|
|
11622
|
+
lines.push(`### ${taskName}`);
|
|
11623
|
+
if (taskConfig.description) {
|
|
11624
|
+
lines.push(`Description: ${taskConfig.description}`);
|
|
11625
|
+
}
|
|
11626
|
+
const requires = getRequires(taskConfig);
|
|
11627
|
+
const provides = getProvides(taskConfig);
|
|
11628
|
+
if (requires.length > 0) lines.push(`Requires: ${requires.join(", ")}`);
|
|
11629
|
+
if (provides.length > 0) lines.push(`Provides: ${provides.join(", ")}`);
|
|
11630
|
+
lines.push(`Current status: ${taskState?.status || "not-started"}`);
|
|
11631
|
+
const hints = taskConfig.inference;
|
|
11632
|
+
if (hints) {
|
|
11633
|
+
if (hints.criteria) lines.push(`Completion criteria: ${hints.criteria}`);
|
|
11634
|
+
if (hints.keywords?.length) lines.push(`Keywords: ${hints.keywords.join(", ")}`);
|
|
11635
|
+
if (hints.suggestedChecks?.length) lines.push(`Suggested checks: ${hints.suggestedChecks.join("; ")}`);
|
|
11636
|
+
}
|
|
11637
|
+
lines.push("");
|
|
11638
|
+
}
|
|
11639
|
+
if (context) {
|
|
11640
|
+
lines.push("## Additional Context / Evidence");
|
|
11641
|
+
lines.push("");
|
|
11642
|
+
lines.push(context);
|
|
11643
|
+
lines.push("");
|
|
11644
|
+
}
|
|
11645
|
+
lines.push("## Response Format");
|
|
11646
|
+
lines.push("");
|
|
11647
|
+
lines.push("Respond with a JSON array of objects, one per task you have evidence for:");
|
|
11648
|
+
lines.push("```json");
|
|
11649
|
+
lines.push("[");
|
|
11650
|
+
lines.push(" {");
|
|
11651
|
+
lines.push(' "taskName": "task-name",');
|
|
11652
|
+
lines.push(' "confidence": 0.0 to 1.0,');
|
|
11653
|
+
lines.push(' "reasoning": "explanation of why you believe this task is complete or not"');
|
|
11654
|
+
lines.push(" }");
|
|
11655
|
+
lines.push("]");
|
|
11656
|
+
lines.push("```");
|
|
11657
|
+
lines.push("");
|
|
11658
|
+
lines.push("Rules:");
|
|
11659
|
+
lines.push('- Only include tasks from the "Tasks to Analyze" section');
|
|
11660
|
+
lines.push("- confidence 0.0 = no evidence of completion, 1.0 = certain it is complete");
|
|
11661
|
+
lines.push("- If you have no evidence for a task, omit it from the array");
|
|
11662
|
+
lines.push("- Be conservative \u2014 require clear evidence before high confidence");
|
|
11663
|
+
lines.push("- Respond ONLY with the JSON array, no additional text");
|
|
11664
|
+
return lines.join("\n");
|
|
11665
|
+
}
|
|
11666
|
+
async function inferCompletions(live, adapter, options = {}) {
|
|
11667
|
+
options.threshold ?? DEFAULT_THRESHOLD;
|
|
11668
|
+
const analyzedNodes = getAnalyzableCandidates(live, options.scope);
|
|
11669
|
+
if (analyzedNodes.length === 0) {
|
|
11670
|
+
return { suggestions: [], promptUsed: "", rawResponse: "", analyzedNodes: [] };
|
|
11671
|
+
}
|
|
11672
|
+
const prompt = buildInferencePrompt(live, options);
|
|
11673
|
+
const rawResponse = await adapter.analyze(prompt);
|
|
11674
|
+
const suggestions = parseInferenceResponse(rawResponse, analyzedNodes);
|
|
11675
|
+
return {
|
|
11676
|
+
suggestions,
|
|
11677
|
+
promptUsed: prompt,
|
|
11678
|
+
rawResponse,
|
|
11679
|
+
analyzedNodes
|
|
11680
|
+
};
|
|
11681
|
+
}
|
|
11682
|
+
function applyInferences(live, result, threshold = DEFAULT_THRESHOLD) {
|
|
11683
|
+
let current = live;
|
|
11684
|
+
for (const suggestion of result.suggestions) {
|
|
11685
|
+
if (suggestion.confidence < threshold) continue;
|
|
11686
|
+
const taskState = current.state.tasks[suggestion.taskName];
|
|
11687
|
+
if (!taskState) continue;
|
|
11688
|
+
if (taskState.status === "completed" || taskState.status === "running") continue;
|
|
11689
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11690
|
+
current = applyEvent(current, {
|
|
11691
|
+
type: "task-started",
|
|
11692
|
+
taskName: suggestion.taskName,
|
|
11693
|
+
timestamp: now
|
|
11694
|
+
});
|
|
11695
|
+
current = applyEvent(current, {
|
|
11696
|
+
type: "task-completed",
|
|
11697
|
+
taskName: suggestion.taskName,
|
|
11698
|
+
timestamp: now,
|
|
11699
|
+
result: "llm-inferred"
|
|
11700
|
+
});
|
|
11701
|
+
}
|
|
11702
|
+
return current;
|
|
11703
|
+
}
|
|
11704
|
+
async function inferAndApply(live, adapter, options = {}) {
|
|
11705
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
11706
|
+
const inference = await inferCompletions(live, adapter, options);
|
|
11707
|
+
const updated = applyInferences(live, inference, threshold);
|
|
11708
|
+
const applied = inference.suggestions.filter((s) => s.confidence >= threshold);
|
|
11709
|
+
const skipped = inference.suggestions.filter((s) => s.confidence < threshold);
|
|
11710
|
+
return {
|
|
11711
|
+
live: updated,
|
|
11712
|
+
inference,
|
|
11713
|
+
applied,
|
|
11714
|
+
skipped
|
|
11715
|
+
};
|
|
11716
|
+
}
|
|
11717
|
+
function getAnalyzableCandidates(live, scope) {
|
|
11718
|
+
const graphTasks = getAllTasks(live.config);
|
|
11719
|
+
const { state } = live;
|
|
11720
|
+
const candidates = [];
|
|
11721
|
+
for (const [name, config] of Object.entries(graphTasks)) {
|
|
11722
|
+
const taskState = state.tasks[name];
|
|
11723
|
+
if (taskState?.status === "completed" || taskState?.status === "running") continue;
|
|
11724
|
+
if (scope) {
|
|
11725
|
+
if (scope.includes(name)) candidates.push(name);
|
|
11726
|
+
} else {
|
|
11727
|
+
if (config.inference?.autoDetectable) candidates.push(name);
|
|
11728
|
+
}
|
|
11729
|
+
}
|
|
11730
|
+
return candidates;
|
|
11731
|
+
}
|
|
11732
|
+
function parseInferenceResponse(rawResponse, validNodes, _threshold) {
|
|
11733
|
+
const validSet = new Set(validNodes);
|
|
11734
|
+
try {
|
|
11735
|
+
const jsonStr = extractJson(rawResponse);
|
|
11736
|
+
if (!jsonStr) return [];
|
|
11737
|
+
const parsed = JSON.parse(jsonStr);
|
|
11738
|
+
if (!Array.isArray(parsed)) return [];
|
|
11739
|
+
const suggestions = [];
|
|
11740
|
+
for (const item of parsed) {
|
|
11741
|
+
if (!item || typeof item !== "object") continue;
|
|
11742
|
+
if (typeof item.taskName !== "string") continue;
|
|
11743
|
+
if (typeof item.confidence !== "number") continue;
|
|
11744
|
+
if (!validSet.has(item.taskName)) continue;
|
|
11745
|
+
const confidence = Math.max(0, Math.min(1, item.confidence));
|
|
11746
|
+
suggestions.push({
|
|
11747
|
+
taskName: item.taskName,
|
|
11748
|
+
confidence,
|
|
11749
|
+
reasoning: typeof item.reasoning === "string" ? item.reasoning : "",
|
|
11750
|
+
detectionMethod: "llm-inferred"
|
|
11751
|
+
});
|
|
11752
|
+
}
|
|
11753
|
+
return suggestions;
|
|
11754
|
+
} catch {
|
|
11755
|
+
return [];
|
|
11756
|
+
}
|
|
11757
|
+
}
|
|
11758
|
+
function extractJson(text) {
|
|
11759
|
+
if (!text || typeof text !== "string") return null;
|
|
11760
|
+
const trimmed = text.trim();
|
|
11761
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
11762
|
+
if (fenceMatch) return fenceMatch[1].trim();
|
|
11763
|
+
const firstBracket = trimmed.indexOf("[");
|
|
11764
|
+
const lastBracket = trimmed.lastIndexOf("]");
|
|
11765
|
+
if (firstBracket !== -1 && lastBracket > firstBracket) {
|
|
11766
|
+
return trimmed.slice(firstBracket, lastBracket + 1);
|
|
11767
|
+
}
|
|
11768
|
+
if (trimmed.startsWith("[")) return trimmed;
|
|
11769
|
+
return null;
|
|
11770
|
+
}
|
|
11771
|
+
function createCliAdapter(opts) {
|
|
11772
|
+
const timeout = opts.timeout ?? 6e4;
|
|
11773
|
+
return {
|
|
11774
|
+
analyze: (prompt) => {
|
|
11775
|
+
return new Promise((resolve2, reject) => {
|
|
11776
|
+
const args = opts.args(prompt);
|
|
11777
|
+
const child = execFile(
|
|
11778
|
+
opts.command,
|
|
11779
|
+
opts.stdin ? opts.args("") : args,
|
|
11780
|
+
{
|
|
11781
|
+
timeout,
|
|
11782
|
+
cwd: opts.cwd,
|
|
11783
|
+
env: opts.env ? { ...process.env, ...opts.env } : void 0,
|
|
11784
|
+
maxBuffer: 10 * 1024 * 1024
|
|
11785
|
+
// 10MB
|
|
11786
|
+
},
|
|
11787
|
+
(error, stdout, stderr) => {
|
|
11788
|
+
if (error) {
|
|
11789
|
+
reject(new Error(
|
|
11790
|
+
`CLI adapter failed: ${opts.command} exited with ${error.code ?? "error"}` + (stderr ? `
|
|
11791
|
+
stderr: ${stderr.slice(0, 500)}` : "") + `
|
|
11792
|
+
${error.message}`
|
|
11793
|
+
));
|
|
11794
|
+
} else {
|
|
11795
|
+
resolve2(stdout);
|
|
11796
|
+
}
|
|
11797
|
+
}
|
|
11798
|
+
);
|
|
11799
|
+
if (opts.stdin && child.stdin) {
|
|
11800
|
+
child.stdin.write(prompt);
|
|
11801
|
+
child.stdin.end();
|
|
11802
|
+
}
|
|
11803
|
+
});
|
|
11804
|
+
}
|
|
11805
|
+
};
|
|
11806
|
+
}
|
|
11807
|
+
function createHttpAdapter(opts) {
|
|
11808
|
+
const timeout = opts.timeout ?? 6e4;
|
|
11809
|
+
return {
|
|
11810
|
+
analyze: async (prompt) => {
|
|
11811
|
+
const body = opts.buildBody ? opts.buildBody(prompt) : { prompt };
|
|
11812
|
+
const controller = new AbortController();
|
|
11813
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
11814
|
+
try {
|
|
11815
|
+
const response = await fetch(opts.url, {
|
|
11816
|
+
method: "POST",
|
|
11817
|
+
headers: {
|
|
11818
|
+
"Content-Type": "application/json",
|
|
11819
|
+
...opts.headers ?? {}
|
|
11820
|
+
},
|
|
11821
|
+
body: JSON.stringify(body),
|
|
11822
|
+
signal: controller.signal
|
|
11823
|
+
});
|
|
11824
|
+
if (!response.ok) {
|
|
11825
|
+
const text = await response.text().catch(() => "");
|
|
11826
|
+
throw new Error(`HTTP ${response.status}: ${text.slice(0, 500)}`);
|
|
11827
|
+
}
|
|
11828
|
+
const json = await response.json();
|
|
11829
|
+
if (opts.extractResponse) {
|
|
11830
|
+
return opts.extractResponse(json);
|
|
11831
|
+
}
|
|
11832
|
+
if (typeof json.response === "string") return json.response;
|
|
11833
|
+
if (typeof json.text === "string") return json.text;
|
|
11834
|
+
if (typeof json.content === "string") return json.content;
|
|
11835
|
+
return JSON.stringify(json);
|
|
11836
|
+
} finally {
|
|
11837
|
+
clearTimeout(timer);
|
|
11838
|
+
}
|
|
11839
|
+
}
|
|
11840
|
+
};
|
|
11841
|
+
}
|
|
11842
|
+
|
|
11843
|
+
export { COMPLETION_STRATEGIES, CONFLICT_STRATEGIES, CardCompute, DEFAULTS, EXECUTION_MODES, EXECUTION_STATUS, FileJournal, FileStore, StepMachine as FlowEngine, LocalStorageStore, MemoryJournal, MemoryStore, StepMachine, TASK_STATUS, addDynamicTask, addNode, addProvides, addRequires, apply, applyAll, applyEvent, applyEvents, applyInferences, applyStepResult, batch, buildInferencePrompt, checkCircuitBreaker, computeAvailableOutputs, computeStepInput, createCallbackHandler, createCliAdapter, createDefaultGraphEngineStore, createStepMachine as createEngine, createFireAndForgetHandler, createHttpAdapter, createInitialExecutionState, createInitialState, createLiveGraph, createNoopHandler, createReactiveGraph, createScriptHandler, createShellHandler, createStepMachine, createWebhookHandler, detectStuckState, disableNode, drainTokens, enableNode, exportGraphConfig, exportGraphConfigToFile, extractReturnData, flowToMermaid, getAllTasks, getCandidateTasks, getDownstream, getMaxExecutions, getNode, getProvides, getRefreshStrategy, getRequires, getTask, getUnreachableNodes, getUnreachableTokens, getUpstream, graphToMermaid, hasTask, inferAndApply, inferCompletions, injectTokens, inspect, isExecutionComplete, isNonActiveTask, isRerunnable, isTaskCompleted, isTaskRunning, liveCardsToReactiveGraph, loadGraphConfig, loadStepFlow, mutateGraph, next, planExecution, removeNode, removeProvides, removeRequires, resetNode, resolveConfigTemplates, resolveVariables, restore, schedule, snapshot, validateFlowSchema, validateGraph, validateGraphConfig, validateGraphSchema, validateLiveCardSchema, validateLiveGraph, validateReactiveGraph, validateStepFlowConfig };
|
|
10476
11844
|
//# sourceMappingURL=index.js.map
|
|
10477
11845
|
//# sourceMappingURL=index.js.map
|