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