@skaile/workspaces 2.8.0 → 2.9.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/CHANGELOG.md +8 -0
- package/dist/{asset-feeds-23CP6YUY.js → asset-feeds-EO2QDJ4C.js} +4 -4
- package/dist/{asset-feeds-23CP6YUY.js.map → asset-feeds-EO2QDJ4C.js.map} +1 -1
- package/dist/asset-manager/index.js +1 -1
- package/dist/{chunk-6AYGXPPF.js → chunk-4PSDPZUG.js} +2 -2
- package/dist/{chunk-6AYGXPPF.js.map → chunk-4PSDPZUG.js.map} +1 -1
- package/dist/{chunk-OAUKA7VT.js → chunk-5LA4XI4Y.js} +8 -8
- package/dist/{chunk-OAUKA7VT.js.map → chunk-5LA4XI4Y.js.map} +1 -1
- package/dist/{chunk-JIA2S3T2.js → chunk-AFUOORJE.js} +6 -6
- package/dist/{chunk-JIA2S3T2.js.map → chunk-AFUOORJE.js.map} +1 -1
- package/dist/{chunk-TUYN2HDT.js → chunk-AGFAOSCZ.js} +10 -4
- package/dist/chunk-AGFAOSCZ.js.map +1 -0
- package/dist/{chunk-AZ44F3AT.js → chunk-BIKTLLLD.js} +6 -6
- package/dist/{chunk-AZ44F3AT.js.map → chunk-BIKTLLLD.js.map} +1 -1
- package/dist/{chunk-HVQWLYZB.js → chunk-E5VGAW4E.js} +508 -83
- package/dist/chunk-E5VGAW4E.js.map +1 -0
- package/dist/{chunk-XJ74XFIZ.js → chunk-INHE5U74.js} +21 -8
- package/dist/chunk-INHE5U74.js.map +1 -0
- package/dist/{chunk-SCR3WGW7.js → chunk-OHLKWUO3.js} +3 -3
- package/dist/{chunk-SCR3WGW7.js.map → chunk-OHLKWUO3.js.map} +1 -1
- package/dist/{chunk-XTFZOYNY.js → chunk-QVFCDSRH.js} +3 -3
- package/dist/{chunk-XTFZOYNY.js.map → chunk-QVFCDSRH.js.map} +1 -1
- package/dist/{chunk-ONUDDQGS.js → chunk-TU7JDHKS.js} +2 -2
- package/dist/{chunk-ONUDDQGS.js.map → chunk-TU7JDHKS.js.map} +1 -1
- package/dist/{chunk-E4AVJMOJ.js → chunk-UAIR3K6X.js} +85 -5
- package/dist/chunk-UAIR3K6X.js.map +1 -0
- package/dist/{chunk-ZJOEU5GP.js → chunk-UTHMTZEH.js} +2 -2
- package/dist/{chunk-ZJOEU5GP.js.map → chunk-UTHMTZEH.js.map} +1 -1
- package/dist/{chunk-P3DUVOAW.js → chunk-XHSS3YHV.js} +6 -6
- package/dist/{chunk-P3DUVOAW.js.map → chunk-XHSS3YHV.js.map} +1 -1
- package/dist/{chunk-FW2C6JJP.js → chunk-XXDQHZJY.js} +3 -3
- package/dist/{chunk-FW2C6JJP.js.map → chunk-XXDQHZJY.js.map} +1 -1
- package/dist/{chunk-ZJ7MRR2R.js → chunk-YFAED6OB.js} +4 -4
- package/dist/{chunk-ZJ7MRR2R.js.map → chunk-YFAED6OB.js.map} +1 -1
- package/dist/{chunk-IWUIXNMF.js → chunk-ZXJKWA44.js} +3 -3
- package/dist/{chunk-IWUIXNMF.js.map → chunk-ZXJKWA44.js.map} +1 -1
- package/dist/cli/index.js +31 -31
- package/dist/connectors/index.js +5 -5
- package/dist/{ensure-sources-N7B7657V.js → ensure-sources-6ZOD45TO.js} +4 -4
- package/dist/{ensure-sources-N7B7657V.js.map → ensure-sources-6ZOD45TO.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +5 -5
- package/dist/factory-assets/connectors/deploy.js +5 -5
- package/dist/factory-assets/connectors/devserver.js +5 -5
- package/dist/factory-assets/connectors/flow/CONNECTOR.md +39 -11
- package/dist/factory-assets/connectors/flow/adapter.d.ts +92 -5
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +5 -5
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts +2 -24
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract.js +2 -2
- package/dist/factory-assets/connectors/flow/engine/check-provenance.d.ts +47 -0
- package/dist/factory-assets/connectors/flow/engine/check-provenance.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/engine/index.d.ts +3 -0
- package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/node-attempts.d.ts +68 -0
- package/dist/factory-assets/connectors/flow/engine/node-attempts.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/engine/node-deadline.d.ts +44 -0
- package/dist/factory-assets/connectors/flow/engine/node-deadline.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts +8 -0
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/types.d.ts +13 -12
- package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +3 -3
- package/dist/factory-assets/connectors/flow/index.d.ts +1 -1
- package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/node-executor.d.ts +10 -4
- package/dist/factory-assets/connectors/flow/node-executor.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/prompt-fragments.js +2 -2
- package/dist/factory-assets/connectors/flow/run-flow.js +6 -6
- package/dist/factory-assets/connectors/flow.js +5 -5
- package/dist/factory-assets/connectors/git.js +5 -5
- package/dist/factory-assets/connectors/gmail.js +5 -5
- package/dist/factory-assets/connectors/googledrive.js +5 -5
- package/dist/factory-assets/connectors/local.js +5 -5
- package/dist/factory-assets/connectors/mattermost.js +5 -5
- package/dist/factory-assets/connectors/memory.js +5 -5
- package/dist/factory-assets/connectors/minio.js +5 -5
- package/dist/factory-assets/connectors/postgres.js +5 -5
- package/dist/factory-assets/connectors/s3.js +5 -5
- package/dist/factory-assets/connectors/sharepoint.js +5 -5
- package/dist/factory-assets/connectors/sqlite.js +5 -5
- package/dist/factory-assets/connectors/static-server.js +5 -5
- package/dist/factory-assets/connectors/tunnel.js +5 -5
- package/dist/factory-assets/connectors/webdav.js +5 -5
- package/dist/factory-assets/connectors/xstate-store.js +5 -5
- package/dist/factory-assets/connectors/xstate.js +5 -5
- package/dist/{flows-K7B253U6.js → flows-7N5AUUIS.js} +3 -3
- package/dist/{flows-K7B253U6.js.map → flows-7N5AUUIS.js.map} +1 -1
- package/dist/{helpers-PXEHIKF7.js → helpers-HWLCANAK.js} +4 -4
- package/dist/{helpers-PXEHIKF7.js.map → helpers-HWLCANAK.js.map} +1 -1
- package/dist/{library-publish-3Z33J2ZY.js → library-publish-GSAIZCO3.js} +9 -9
- package/dist/{library-publish-3Z33J2ZY.js.map → library-publish-GSAIZCO3.js.map} +1 -1
- package/dist/{open-library-Y5SKFAMK.js → open-library-ZB2STYC6.js} +8 -8
- package/dist/{open-library-Y5SKFAMK.js.map → open-library-ZB2STYC6.js.map} +1 -1
- package/dist/{project-DQRC6J6M.js → project-EJFCLSHS.js} +5 -5
- package/dist/{project-DQRC6J6M.js.map → project-EJFCLSHS.js.map} +1 -1
- package/dist/runner/index.js +9 -9
- package/dist/sdk/asset-manager.js +1 -1
- package/dist/sdk/flow.js +3 -3
- package/dist/sdk/index.js +9 -9
- package/dist/sdk/runner.js +9 -9
- package/dist/{source-XBJ5KTAV.js → source-ILV6ZKWM.js} +9 -9
- package/dist/{source-XBJ5KTAV.js.map → source-ILV6ZKWM.js.map} +1 -1
- package/dist/{source-manifest-publish-Q7TXBMCW.js → source-manifest-publish-QE2IFDRW.js} +10 -10
- package/dist/{source-manifest-publish-Q7TXBMCW.js.map → source-manifest-publish-QE2IFDRW.js.map} +1 -1
- package/dist/tui/index.js +9 -9
- package/dist/types/src/flow.d.ts +128 -0
- package/dist/types/src/flow.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +1 -1
- package/dist/workspace-plugin/adapters/omp.js +3 -3
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-E4AVJMOJ.js.map +0 -1
- package/dist/chunk-HVQWLYZB.js.map +0 -1
- package/dist/chunk-TUYN2HDT.js.map +0 -1
- package/dist/chunk-XJ74XFIZ.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { resolveAuthRef } from './chunk-ZNIHTT22.js';
|
|
2
2
|
import { pluginRegistry } from './chunk-6E6PKKAD.js';
|
|
3
3
|
import { computeStimulus } from './chunk-IFGTRL2I.js';
|
|
4
|
-
import { getConsumedOutputSchema, validateConsumedNodeOutput, resolveNodeBindings, computeFlowStateFromSnapshots, availableRouterFrontier, evaluateRouterSelection, routerSelectionOutput, planRouterSkips, unsettledRouterBranchIds, interpolateNodeInstruction, persistedRouterSelection } from './chunk-
|
|
5
|
-
import { parseFlowDefinition } from './chunk-
|
|
6
|
-
import { renderStimulusPrompt } from './chunk-
|
|
7
|
-
import { flowDefaults, isAgentNode, isGateNode, nodeRequiresApproval, isSubFlowNode, isFunctionNode, nodeIsOptional, isRouterNode, isActiveRuntimeNode, nodeLabel, nodeInstruction, nodeAssets } from './chunk-
|
|
4
|
+
import { getConsumedOutputSchema, validateConsumedNodeOutput, appendAttempt, remainingActiveExecutionMs, resolveNodeBindings, deriveCheckEvidence, computeFlowStateFromSnapshots, availableRouterFrontier, evaluateRouterSelection, routerSelectionOutput, planRouterSkips, unsettledRouterBranchIds, countFailedAttempts, hasAutomaticRetryBudget, interpolateNodeInstruction, persistedRouterSelection } from './chunk-UAIR3K6X.js';
|
|
5
|
+
import { parseFlowDefinition } from './chunk-INHE5U74.js';
|
|
6
|
+
import { renderStimulusPrompt } from './chunk-OHLKWUO3.js';
|
|
7
|
+
import { deterministicNodeKindLabel, flowDefaults, isAgentNode, isGateNode, isCheckNode, nodeRequiresApproval, isSubFlowNode, isFunctionNode, nodeIsOptional, isRouterNode, isActiveRuntimeNode, nodeLabel, nodeInstruction, nodeAssets } from './chunk-AGFAOSCZ.js';
|
|
8
8
|
import { portableSpawn } from './chunk-P43HBWKK.js';
|
|
9
9
|
import { withFsDeadline, tryWithFsDeadline, FsDeadlineError, fsDeadlineMs } from './chunk-DYMWJAL4.js';
|
|
10
10
|
import { createLogger } from './chunk-7GOCNYUV.js';
|
|
@@ -324,11 +324,11 @@ function stableJsonStringify(value) {
|
|
|
324
324
|
);
|
|
325
325
|
});
|
|
326
326
|
}
|
|
327
|
-
function serializeEnvironmentInput(name, value) {
|
|
327
|
+
function serializeEnvironmentInput(name, value, nodeLabel2) {
|
|
328
328
|
if (typeof value === "string") return value;
|
|
329
329
|
const serialized = stableJsonStringify(value);
|
|
330
330
|
if (serialized === void 0) {
|
|
331
|
-
throw new Error(
|
|
331
|
+
throw new Error(`${nodeLabel2} input ${name} is not JSON-serializable`);
|
|
332
332
|
}
|
|
333
333
|
return serialized;
|
|
334
334
|
}
|
|
@@ -348,19 +348,18 @@ var SessionNodeExecutor = class {
|
|
|
348
348
|
constructor(options) {
|
|
349
349
|
this.workspaceDir = resolve(options.workspaceDir);
|
|
350
350
|
}
|
|
351
|
-
/** Runs one function node process with session environment and binding overrides. */
|
|
351
|
+
/** Runs one function or check node process with session environment and binding overrides. */
|
|
352
352
|
async execute(request) {
|
|
353
|
+
const nodeLabel2 = `${deterministicNodeKindLabel(request.node.run.kind)} node ${request.node.id}`;
|
|
353
354
|
const cwd = resolve(this.workspaceDir, request.node.run.cwd ?? ".");
|
|
354
355
|
const workspaceRelative = relative(this.workspaceDir, cwd);
|
|
355
356
|
if (workspaceRelative === ".." || workspaceRelative.startsWith(`..${sep}`) || isAbsolute(workspaceRelative)) {
|
|
356
|
-
throw new Error(
|
|
357
|
-
`Function node ${request.node.id} cwd escapes the session workspace: ${request.node.run.cwd}`
|
|
358
|
-
);
|
|
357
|
+
throw new Error(`${nodeLabel2} cwd escapes the session workspace: ${request.node.run.cwd}`);
|
|
359
358
|
}
|
|
360
359
|
const boundEnvironment = Object.fromEntries(
|
|
361
360
|
Object.entries(request.inputs).map(([name, value]) => [
|
|
362
361
|
name,
|
|
363
|
-
serializeEnvironmentInput(name, value)
|
|
362
|
+
serializeEnvironmentInput(name, value, nodeLabel2)
|
|
364
363
|
])
|
|
365
364
|
);
|
|
366
365
|
const process2 = portableSpawn(commandFor(request.node), {
|
|
@@ -7487,6 +7486,28 @@ var AGENT_INLINE_OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
7487
7486
|
"skip_node",
|
|
7488
7487
|
"fail_node"
|
|
7489
7488
|
]);
|
|
7489
|
+
var NodeTimeoutError = class extends Error {
|
|
7490
|
+
};
|
|
7491
|
+
function raceWithTimeout(promise, ms, timeoutMessage) {
|
|
7492
|
+
return new Promise((resolve6, reject) => {
|
|
7493
|
+
const timer = setTimeout(() => {
|
|
7494
|
+
reject(new NodeTimeoutError(timeoutMessage));
|
|
7495
|
+
promise.catch(() => {
|
|
7496
|
+
});
|
|
7497
|
+
}, ms);
|
|
7498
|
+
timer.unref?.();
|
|
7499
|
+
promise.then(
|
|
7500
|
+
(value) => {
|
|
7501
|
+
clearTimeout(timer);
|
|
7502
|
+
resolve6(value);
|
|
7503
|
+
},
|
|
7504
|
+
(err) => {
|
|
7505
|
+
clearTimeout(timer);
|
|
7506
|
+
reject(err);
|
|
7507
|
+
}
|
|
7508
|
+
);
|
|
7509
|
+
});
|
|
7510
|
+
}
|
|
7490
7511
|
function mergeInteractionHistory(history, interaction) {
|
|
7491
7512
|
if (!interaction) return history;
|
|
7492
7513
|
const next = [...history];
|
|
@@ -7507,7 +7528,8 @@ function emptyNode(nodeId) {
|
|
|
7507
7528
|
status: "not_started",
|
|
7508
7529
|
approvalHistory: [],
|
|
7509
7530
|
inputHistory: [],
|
|
7510
|
-
errorHistory: []
|
|
7531
|
+
errorHistory: [],
|
|
7532
|
+
outputHistory: []
|
|
7511
7533
|
};
|
|
7512
7534
|
}
|
|
7513
7535
|
function nodeExecutionRecord(entries = []) {
|
|
@@ -7648,14 +7670,18 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7648
7670
|
inlineOperationTargets: /* @__PURE__ */ new Map(),
|
|
7649
7671
|
flowInputOrigins: options.flowInputOrigins,
|
|
7650
7672
|
inlineSubFlowAncestry: options.inlineSubFlowAncestry ?? [flowDef.id],
|
|
7651
|
-
executionGeneration: 0
|
|
7673
|
+
executionGeneration: 0,
|
|
7674
|
+
wakeTimer: null
|
|
7652
7675
|
};
|
|
7653
7676
|
state.execution = this.recompute(state);
|
|
7654
|
-
|
|
7677
|
+
const handle = this.makeHandle(declaration, state);
|
|
7678
|
+
this.rearmWakeTimer(handle);
|
|
7679
|
+
return handle;
|
|
7655
7680
|
}
|
|
7656
7681
|
async disconnect(handle) {
|
|
7657
7682
|
const state = this.s(handle);
|
|
7658
7683
|
state.executionGeneration += 1;
|
|
7684
|
+
this.clearWakeTimer(state);
|
|
7659
7685
|
await this.cancelInlineTargets(state);
|
|
7660
7686
|
handle.status = "disconnected";
|
|
7661
7687
|
}
|
|
@@ -7703,6 +7729,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7703
7729
|
const changed = JSON.stringify(prepared) !== JSON.stringify(execution);
|
|
7704
7730
|
s.executionGeneration += 1;
|
|
7705
7731
|
s.execution = prepared;
|
|
7732
|
+
this.rearmWakeTimer(handle);
|
|
7706
7733
|
if (changed && s.onChange) s.onChange(s.execution);
|
|
7707
7734
|
}
|
|
7708
7735
|
// ── Base operations (private — wired into tools face in constructor) ──
|
|
@@ -8315,17 +8342,27 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8315
8342
|
this.mutate(handle, (current) => {
|
|
8316
8343
|
const execution = current.execution.nodes[nodeId];
|
|
8317
8344
|
assertLegal(execution, `node not in execution: ${nodeId}`);
|
|
8345
|
+
const completedAt = now();
|
|
8318
8346
|
const error = {
|
|
8319
8347
|
message: `Node ${nodeId} output failed schema validation after one correction: ${issueText}`,
|
|
8320
8348
|
recoverable: false,
|
|
8321
|
-
at:
|
|
8349
|
+
at: completedAt
|
|
8322
8350
|
};
|
|
8351
|
+
const outputHistory = execution.startedAt ? appendAttempt(
|
|
8352
|
+
execution.outputHistory,
|
|
8353
|
+
"failed",
|
|
8354
|
+
execution.startedAt,
|
|
8355
|
+
completedAt,
|
|
8356
|
+
execution.output,
|
|
8357
|
+
error
|
|
8358
|
+
) : execution.outputHistory;
|
|
8323
8359
|
current.execution.nodes[nodeId] = {
|
|
8324
8360
|
...execution,
|
|
8325
8361
|
status: "failed",
|
|
8326
|
-
completedAt
|
|
8362
|
+
completedAt,
|
|
8327
8363
|
error,
|
|
8328
|
-
errorHistory: [...execution.errorHistory, error]
|
|
8364
|
+
errorHistory: [...execution.errorHistory, error],
|
|
8365
|
+
outputHistory
|
|
8329
8366
|
};
|
|
8330
8367
|
});
|
|
8331
8368
|
throw new Error(
|
|
@@ -8335,11 +8372,21 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8335
8372
|
return this.mutate(handle, (current) => {
|
|
8336
8373
|
const execution = current.execution.nodes[nodeId];
|
|
8337
8374
|
assertLegal(execution, `node not in execution: ${nodeId}`);
|
|
8375
|
+
const completedAt = now();
|
|
8376
|
+
const outputHistory = execution.startedAt ? appendAttempt(
|
|
8377
|
+
execution.outputHistory,
|
|
8378
|
+
"complete",
|
|
8379
|
+
execution.startedAt,
|
|
8380
|
+
completedAt,
|
|
8381
|
+
output,
|
|
8382
|
+
void 0
|
|
8383
|
+
) : execution.outputHistory;
|
|
8338
8384
|
current.execution.nodes[nodeId] = {
|
|
8339
8385
|
...execution,
|
|
8340
8386
|
status: "complete",
|
|
8341
|
-
completedAt
|
|
8342
|
-
output
|
|
8387
|
+
completedAt,
|
|
8388
|
+
output,
|
|
8389
|
+
outputHistory
|
|
8343
8390
|
};
|
|
8344
8391
|
return JSON.stringify({ ok: true, status: "complete" });
|
|
8345
8392
|
});
|
|
@@ -8357,10 +8404,20 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8357
8404
|
`cannot skip node ${nodeId}: it is a mandatory approval gate \u2014 call request_approval and wait for an approved decision`
|
|
8358
8405
|
);
|
|
8359
8406
|
assertLegal(isOptional(s.flowDef, nodeId), `cannot skip required node ${nodeId}`);
|
|
8407
|
+
const completedAt = now();
|
|
8408
|
+
const outputHistory = node.startedAt ? appendAttempt(
|
|
8409
|
+
node.outputHistory,
|
|
8410
|
+
"skipped",
|
|
8411
|
+
node.startedAt,
|
|
8412
|
+
completedAt,
|
|
8413
|
+
node.output,
|
|
8414
|
+
void 0
|
|
8415
|
+
) : node.outputHistory;
|
|
8360
8416
|
s.execution.nodes[nodeId] = {
|
|
8361
8417
|
...node,
|
|
8362
8418
|
status: "skipped",
|
|
8363
|
-
completedAt
|
|
8419
|
+
completedAt,
|
|
8420
|
+
outputHistory
|
|
8364
8421
|
};
|
|
8365
8422
|
return JSON.stringify({ ok: true, status: "skipped" });
|
|
8366
8423
|
});
|
|
@@ -8375,19 +8432,40 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8375
8432
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
8376
8433
|
const error = { message, recoverable, at: now() };
|
|
8377
8434
|
if (recoverable) {
|
|
8435
|
+
const completedAt = error.at;
|
|
8436
|
+
const outputHistory = node.startedAt ? appendAttempt(
|
|
8437
|
+
node.outputHistory,
|
|
8438
|
+
"failed",
|
|
8439
|
+
node.startedAt,
|
|
8440
|
+
completedAt,
|
|
8441
|
+
node.output,
|
|
8442
|
+
error,
|
|
8443
|
+
{ selfRetry: true }
|
|
8444
|
+
) : node.outputHistory;
|
|
8378
8445
|
s.execution.nodes[nodeId] = {
|
|
8379
8446
|
...node,
|
|
8380
8447
|
status: "available",
|
|
8381
8448
|
error,
|
|
8382
|
-
errorHistory: [...node.errorHistory, error]
|
|
8449
|
+
errorHistory: [...node.errorHistory, error],
|
|
8450
|
+
outputHistory
|
|
8383
8451
|
};
|
|
8384
8452
|
} else {
|
|
8453
|
+
const completedAt = now();
|
|
8454
|
+
const outputHistory = node.startedAt ? appendAttempt(
|
|
8455
|
+
node.outputHistory,
|
|
8456
|
+
"failed",
|
|
8457
|
+
node.startedAt,
|
|
8458
|
+
completedAt,
|
|
8459
|
+
node.output,
|
|
8460
|
+
error
|
|
8461
|
+
) : node.outputHistory;
|
|
8385
8462
|
s.execution.nodes[nodeId] = {
|
|
8386
8463
|
...node,
|
|
8387
8464
|
status: "failed",
|
|
8388
|
-
completedAt
|
|
8465
|
+
completedAt,
|
|
8389
8466
|
error,
|
|
8390
|
-
errorHistory: [...node.errorHistory, error]
|
|
8467
|
+
errorHistory: [...node.errorHistory, error],
|
|
8468
|
+
outputHistory
|
|
8391
8469
|
};
|
|
8392
8470
|
}
|
|
8393
8471
|
return JSON.stringify({ ok: true, status: s.execution.nodes[nodeId].status });
|
|
@@ -8397,7 +8475,9 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8397
8475
|
/**
|
|
8398
8476
|
* Record an approval decision on a node currently `awaiting_approval`.
|
|
8399
8477
|
* A first-class gate completes directly when approved and remains parked when rejected.
|
|
8400
|
-
* Agent-node approvals keep their existing resume behavior.
|
|
8478
|
+
* Agent-node approvals keep their existing resume behavior. A check's escalation is a
|
|
8479
|
+
* one-way door: only `rejected` is legal, and it ends the node terminally `failed` —
|
|
8480
|
+
* there is no decision that resumes or passes a check.
|
|
8401
8481
|
*/
|
|
8402
8482
|
applyApproval(handle, nodeId, decision, feedback, decidedBy) {
|
|
8403
8483
|
this.mutate(handle, (s) => {
|
|
@@ -8414,6 +8494,12 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8414
8494
|
`applyApproval: gate ${nodeId} approval attempt is already decided; request approval again before applying another decision`
|
|
8415
8495
|
);
|
|
8416
8496
|
}
|
|
8497
|
+
if (isCheckNode(definition)) {
|
|
8498
|
+
assertLegal(
|
|
8499
|
+
decision === "rejected",
|
|
8500
|
+
`applyApproval: check ${nodeId} cannot be approved \u2014 a failed check has no passing state to approve into; only "rejected" is a legal decision`
|
|
8501
|
+
);
|
|
8502
|
+
}
|
|
8417
8503
|
const base = node.approval ?? { requestedAt: now(), summary: "" };
|
|
8418
8504
|
const approval = {
|
|
8419
8505
|
...base,
|
|
@@ -8440,6 +8526,14 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8440
8526
|
approval,
|
|
8441
8527
|
approvalHistory: history
|
|
8442
8528
|
};
|
|
8529
|
+
} else if (isCheckNode(definition)) {
|
|
8530
|
+
s.execution.nodes[nodeId] = {
|
|
8531
|
+
...node,
|
|
8532
|
+
status: "failed",
|
|
8533
|
+
completedAt: now(),
|
|
8534
|
+
approval,
|
|
8535
|
+
approvalHistory: history
|
|
8536
|
+
};
|
|
8443
8537
|
} else if (decision === "approved") {
|
|
8444
8538
|
s.execution.nodes[nodeId] = { ...node, approval, approvalHistory: history };
|
|
8445
8539
|
} else {
|
|
@@ -8513,10 +8607,17 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8513
8607
|
return void 0;
|
|
8514
8608
|
});
|
|
8515
8609
|
}
|
|
8516
|
-
/**
|
|
8610
|
+
/**
|
|
8611
|
+
* Cancel the flow run. Status transitions to `cancelled`; dependents stay
|
|
8612
|
+
* put. Bumps `executionGeneration` — the sole late-settlement rejection
|
|
8613
|
+
* mechanism every in-flight await (function executor, sub-flow child)
|
|
8614
|
+
* checks after resolving, so a result that arrives after cancellation can
|
|
8615
|
+
* never write state.
|
|
8616
|
+
*/
|
|
8517
8617
|
cancel(handle) {
|
|
8518
8618
|
this.mutate(handle, (s) => {
|
|
8519
8619
|
s.execution = { ...s.execution, status: "cancelled" };
|
|
8620
|
+
s.executionGeneration += 1;
|
|
8520
8621
|
return void 0;
|
|
8521
8622
|
});
|
|
8522
8623
|
}
|
|
@@ -8586,9 +8687,11 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8586
8687
|
return this.drainAvailableRuntimeNodes(handle, true);
|
|
8587
8688
|
}
|
|
8588
8689
|
/**
|
|
8589
|
-
* Run every currently available function node through the
|
|
8590
|
-
* NodeExecutor. Newly-unblocked function nodes are
|
|
8591
|
-
* order before returning control to the runner's
|
|
8690
|
+
* Run every currently available function and check node through the
|
|
8691
|
+
* configured NodeExecutor. Newly-unblocked function/check nodes are
|
|
8692
|
+
* drained in definition order before returning control to the runner's
|
|
8693
|
+
* agent-turn decision. Routers and gates are excluded — see
|
|
8694
|
+
* {@link drainRuntimeNodes} for the full drain.
|
|
8592
8695
|
*/
|
|
8593
8696
|
async executeAvailableFunctionNodes(handle) {
|
|
8594
8697
|
return this.drainAvailableRuntimeNodes(handle, false);
|
|
@@ -8604,7 +8707,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8604
8707
|
if (isSubFlowNode(candidate) && !advancedSubFlows.has(candidate.id) && (status === "available" || status === "running")) {
|
|
8605
8708
|
return true;
|
|
8606
8709
|
}
|
|
8607
|
-
return (isFunctionNode(candidate) || includeRoutersAndGates && isGateNode(candidate)) && status === "available";
|
|
8710
|
+
return (isFunctionNode(candidate) || isCheckNode(candidate) || includeRoutersAndGates && isGateNode(candidate)) && status === "available";
|
|
8608
8711
|
});
|
|
8609
8712
|
if (!node) break;
|
|
8610
8713
|
if (isGateNode(node)) {
|
|
@@ -8653,6 +8756,9 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8653
8756
|
);
|
|
8654
8757
|
}
|
|
8655
8758
|
const executionGeneration = state.executionGeneration;
|
|
8759
|
+
const timeoutSec = node.control?.timeoutSec;
|
|
8760
|
+
const runningExecution = state.execution.nodes[node.id];
|
|
8761
|
+
const remainingMs = runningExecution ? remainingActiveExecutionMs(runningExecution, timeoutSec, Date.now()) : void 0;
|
|
8656
8762
|
let result;
|
|
8657
8763
|
try {
|
|
8658
8764
|
const runner = state.inlineSubFlowRunner;
|
|
@@ -8665,7 +8771,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8665
8771
|
snapshotMap(state.execution),
|
|
8666
8772
|
state.flowInputOrigins
|
|
8667
8773
|
);
|
|
8668
|
-
|
|
8774
|
+
const runPromise = runner.run({
|
|
8669
8775
|
node,
|
|
8670
8776
|
flowId: node.run.flow,
|
|
8671
8777
|
inputs: resolved.values,
|
|
@@ -8682,50 +8788,86 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8682
8788
|
else state.inlineOperationTargets.delete(node.id);
|
|
8683
8789
|
}
|
|
8684
8790
|
});
|
|
8791
|
+
result = remainingMs !== void 0 ? await raceWithTimeout(
|
|
8792
|
+
runPromise,
|
|
8793
|
+
Math.max(0, remainingMs),
|
|
8794
|
+
`Sub-flow node ${node.id} timed out after ${timeoutSec}s`
|
|
8795
|
+
) : await runPromise;
|
|
8685
8796
|
} catch (error) {
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8797
|
+
if (error instanceof NodeTimeoutError) {
|
|
8798
|
+
const target = state.inlineOperationTargets.get(node.id);
|
|
8799
|
+
if (target) {
|
|
8800
|
+
state.inlineOperationTargets.delete(node.id);
|
|
8801
|
+
void target.cancel().catch(() => {
|
|
8802
|
+
});
|
|
8692
8803
|
}
|
|
8693
|
-
|
|
8804
|
+
result = {
|
|
8805
|
+
status: "failed",
|
|
8806
|
+
error: { message: error.message, recoverable: false, at: now() }
|
|
8807
|
+
};
|
|
8808
|
+
} else {
|
|
8809
|
+
result = {
|
|
8810
|
+
status: "failed",
|
|
8811
|
+
error: {
|
|
8812
|
+
message: `Sub-flow node ${node.id} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
8813
|
+
recoverable: false,
|
|
8814
|
+
at: now()
|
|
8815
|
+
}
|
|
8816
|
+
};
|
|
8817
|
+
}
|
|
8694
8818
|
}
|
|
8695
8819
|
if (this.s(handle).executionGeneration !== executionGeneration) break;
|
|
8696
8820
|
if (result.status === "paused") {
|
|
8697
8821
|
this.projectInlineInteraction(handle, node.id, result.projection);
|
|
8698
8822
|
continue;
|
|
8699
8823
|
}
|
|
8700
|
-
|
|
8824
|
+
let subFlowRetrying = false;
|
|
8701
8825
|
this.mutate(
|
|
8702
8826
|
handle,
|
|
8703
8827
|
(current) => {
|
|
8704
8828
|
const execution2 = current.execution.nodes[node.id];
|
|
8705
8829
|
assertLegal(execution2, `node not in execution: ${node.id}`);
|
|
8830
|
+
const completedAt = now();
|
|
8706
8831
|
if (result.status === "complete") {
|
|
8832
|
+
const outputHistory = appendAttempt(
|
|
8833
|
+
execution2.outputHistory,
|
|
8834
|
+
"complete",
|
|
8835
|
+
execution2.startedAt ?? completedAt,
|
|
8836
|
+
completedAt,
|
|
8837
|
+
result.output,
|
|
8838
|
+
void 0
|
|
8839
|
+
);
|
|
8707
8840
|
current.execution.nodes[node.id] = {
|
|
8708
8841
|
...execution2,
|
|
8709
8842
|
status: "complete",
|
|
8710
|
-
completedAt
|
|
8711
|
-
output: result.output
|
|
8843
|
+
completedAt,
|
|
8844
|
+
output: result.output,
|
|
8845
|
+
outputHistory
|
|
8712
8846
|
};
|
|
8713
8847
|
return;
|
|
8714
8848
|
}
|
|
8715
|
-
|
|
8849
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
8850
|
+
node,
|
|
8851
|
+
execution2,
|
|
8852
|
+
result.error.message,
|
|
8853
|
+
completedAt,
|
|
8854
|
+
result.output,
|
|
8855
|
+
result.error.at
|
|
8856
|
+
);
|
|
8857
|
+
subFlowRetrying = retry;
|
|
8858
|
+
current.execution.nodes[node.id] = retry ? { ...execution2, ...patch, status: "available", error: void 0 } : {
|
|
8716
8859
|
...execution2,
|
|
8860
|
+
...patch,
|
|
8717
8861
|
status: nodeIsOptional(node) ? "skipped" : "failed",
|
|
8718
|
-
completedAt
|
|
8719
|
-
output: result.output,
|
|
8720
|
-
error: result.error,
|
|
8721
|
-
errorHistory: [...execution2.errorHistory, result.error]
|
|
8862
|
+
completedAt
|
|
8722
8863
|
};
|
|
8723
8864
|
},
|
|
8724
8865
|
false
|
|
8725
8866
|
);
|
|
8867
|
+
if (subFlowRetrying) advancedSubFlows.delete(node.id);
|
|
8726
8868
|
continue;
|
|
8727
8869
|
}
|
|
8728
|
-
if (!isFunctionNode(node)) continue;
|
|
8870
|
+
if (!isFunctionNode(node) && !isCheckNode(node)) continue;
|
|
8729
8871
|
this.mutate(
|
|
8730
8872
|
handle,
|
|
8731
8873
|
(current) => {
|
|
@@ -8733,7 +8875,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8733
8875
|
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8734
8876
|
assertLegal(
|
|
8735
8877
|
execution.status === "available",
|
|
8736
|
-
`cannot execute
|
|
8878
|
+
`cannot execute ${node.run.kind} node ${node.id}: status is ${execution.status}`
|
|
8737
8879
|
);
|
|
8738
8880
|
current.execution.nodes[node.id] = {
|
|
8739
8881
|
...execution,
|
|
@@ -8743,73 +8885,178 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8743
8885
|
},
|
|
8744
8886
|
false
|
|
8745
8887
|
);
|
|
8888
|
+
const deterministicExecutionGeneration = state.executionGeneration;
|
|
8889
|
+
const deterministicTimeoutSec = node.control?.timeoutSec;
|
|
8890
|
+
const deterministicRunningExecution = state.execution.nodes[node.id];
|
|
8891
|
+
const deterministicRemainingMs = deterministicRunningExecution ? remainingActiveExecutionMs(
|
|
8892
|
+
deterministicRunningExecution,
|
|
8893
|
+
deterministicTimeoutSec,
|
|
8894
|
+
Date.now()
|
|
8895
|
+
) : void 0;
|
|
8896
|
+
const deterministicKindLabel = deterministicNodeKindLabel(node.run.kind);
|
|
8746
8897
|
let capturedOutput;
|
|
8898
|
+
let checkEvidence;
|
|
8899
|
+
let deterministicOutcome;
|
|
8747
8900
|
try {
|
|
8748
8901
|
const executor = state.nodeExecutor;
|
|
8749
|
-
if (!executor)
|
|
8902
|
+
if (!executor)
|
|
8903
|
+
throw new Error(`no NodeExecutor configured for ${node.run.kind} node ${node.id}`);
|
|
8750
8904
|
const resolved = resolveNodeBindings(
|
|
8751
8905
|
state.flowDef,
|
|
8752
8906
|
node,
|
|
8753
8907
|
snapshotMap(state.execution),
|
|
8754
8908
|
state.flowInputOrigins
|
|
8755
8909
|
);
|
|
8756
|
-
const
|
|
8910
|
+
const runPromise = executor.execute({
|
|
8757
8911
|
node,
|
|
8758
8912
|
inputs: resolved.values,
|
|
8759
8913
|
inputOrigins: resolved.origins
|
|
8760
8914
|
});
|
|
8761
|
-
|
|
8915
|
+
const result = deterministicRemainingMs !== void 0 ? await raceWithTimeout(
|
|
8916
|
+
runPromise,
|
|
8917
|
+
Math.max(0, deterministicRemainingMs),
|
|
8918
|
+
`${deterministicKindLabel} node ${node.id} timed out after ${deterministicTimeoutSec}s`
|
|
8919
|
+
) : await runPromise;
|
|
8762
8920
|
const successfulStatuses = node.run.successExit ?? [0];
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8921
|
+
const passed = successfulStatuses.includes(result.exitStatus);
|
|
8922
|
+
capturedOutput = deterministicNodeOutput(node, result, passed);
|
|
8923
|
+
const detail = passed ? "" : result.stderr.trim() || result.stdout.trim();
|
|
8924
|
+
const detailSuffix = detail ? `: ${detail}` : "";
|
|
8925
|
+
if (passed) {
|
|
8926
|
+
const validation = validateConsumedNodeOutput(
|
|
8927
|
+
state.flowDef,
|
|
8928
|
+
node.id,
|
|
8929
|
+
capturedOutput.fields
|
|
8767
8930
|
);
|
|
8931
|
+
if (!validation.valid) {
|
|
8932
|
+
throw new Error(
|
|
8933
|
+
`output does not match its consumed schema: ${validation.issues.join("; ")}`
|
|
8934
|
+
);
|
|
8935
|
+
}
|
|
8768
8936
|
}
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8937
|
+
if (isCheckNode(node)) {
|
|
8938
|
+
checkEvidence = deriveCheckEvidence(
|
|
8939
|
+
resolved,
|
|
8940
|
+
{
|
|
8941
|
+
runtime: node.run.runtime ?? "shell",
|
|
8942
|
+
command: node.run.command,
|
|
8943
|
+
cwd: node.run.cwd,
|
|
8944
|
+
successExit: successfulStatuses,
|
|
8945
|
+
exitStatus: result.exitStatus,
|
|
8946
|
+
passed
|
|
8947
|
+
},
|
|
8948
|
+
now()
|
|
8949
|
+
);
|
|
8950
|
+
deterministicOutcome = passed ? { kind: "success" } : {
|
|
8951
|
+
kind: "check-fail",
|
|
8952
|
+
message: `Check node ${node.id} failed with exit status ${result.exitStatus}${detailSuffix}`,
|
|
8953
|
+
onFail: node.run.onFail
|
|
8954
|
+
};
|
|
8955
|
+
} else if (!passed) {
|
|
8775
8956
|
throw new Error(
|
|
8776
|
-
`
|
|
8957
|
+
`Function node ${node.id} exited with unsuccessful exit status ${result.exitStatus}${detailSuffix}`
|
|
8777
8958
|
);
|
|
8959
|
+
} else {
|
|
8960
|
+
deterministicOutcome = { kind: "success" };
|
|
8778
8961
|
}
|
|
8962
|
+
} catch (error) {
|
|
8963
|
+
deterministicOutcome = error instanceof NodeTimeoutError ? { kind: "failure", message: error.message } : {
|
|
8964
|
+
kind: "failure",
|
|
8965
|
+
message: `${deterministicKindLabel} node ${node.id} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
8966
|
+
};
|
|
8967
|
+
}
|
|
8968
|
+
if (this.s(handle).executionGeneration !== deterministicExecutionGeneration) break;
|
|
8969
|
+
if (deterministicOutcome.kind === "success") {
|
|
8779
8970
|
this.mutate(
|
|
8780
8971
|
handle,
|
|
8781
8972
|
(current) => {
|
|
8782
8973
|
const execution = current.execution.nodes[node.id];
|
|
8783
8974
|
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8975
|
+
const completedAt = now();
|
|
8976
|
+
const outputHistory = appendAttempt(
|
|
8977
|
+
execution.outputHistory,
|
|
8978
|
+
"complete",
|
|
8979
|
+
execution.startedAt ?? completedAt,
|
|
8980
|
+
completedAt,
|
|
8981
|
+
capturedOutput,
|
|
8982
|
+
void 0,
|
|
8983
|
+
{ check: checkEvidence }
|
|
8984
|
+
);
|
|
8784
8985
|
current.execution.nodes[node.id] = {
|
|
8785
8986
|
...execution,
|
|
8786
8987
|
status: "complete",
|
|
8787
|
-
completedAt
|
|
8788
|
-
output: capturedOutput
|
|
8988
|
+
completedAt,
|
|
8989
|
+
output: capturedOutput,
|
|
8990
|
+
outputHistory
|
|
8789
8991
|
};
|
|
8790
8992
|
},
|
|
8791
8993
|
false
|
|
8792
8994
|
);
|
|
8793
|
-
}
|
|
8794
|
-
const message
|
|
8995
|
+
} else if (deterministicOutcome.kind === "check-fail") {
|
|
8996
|
+
const { message: failureMessage, onFail } = deterministicOutcome;
|
|
8795
8997
|
this.mutate(
|
|
8796
8998
|
handle,
|
|
8797
8999
|
(current) => {
|
|
8798
9000
|
const execution = current.execution.nodes[node.id];
|
|
8799
9001
|
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
9002
|
+
assertLegal(checkEvidence, `check node ${node.id} completed without evidence`);
|
|
9003
|
+
const completedAt = now();
|
|
9004
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
9005
|
+
node,
|
|
9006
|
+
execution,
|
|
9007
|
+
failureMessage,
|
|
9008
|
+
completedAt,
|
|
9009
|
+
capturedOutput,
|
|
9010
|
+
void 0,
|
|
9011
|
+
checkEvidence
|
|
9012
|
+
);
|
|
9013
|
+
if (retry) {
|
|
9014
|
+
current.execution.nodes[node.id] = {
|
|
9015
|
+
...execution,
|
|
9016
|
+
...patch,
|
|
9017
|
+
status: "available",
|
|
9018
|
+
error: void 0
|
|
9019
|
+
};
|
|
9020
|
+
} else if (onFail === "fail") {
|
|
9021
|
+
current.execution.nodes[node.id] = {
|
|
9022
|
+
...execution,
|
|
9023
|
+
...patch,
|
|
9024
|
+
status: "failed",
|
|
9025
|
+
completedAt
|
|
9026
|
+
};
|
|
9027
|
+
} else {
|
|
9028
|
+
const provenanceNote = checkEvidence.provenance ? ` (${checkEvidence.provenance})` : "";
|
|
9029
|
+
const approval = {
|
|
9030
|
+
requestedAt: now(),
|
|
9031
|
+
summary: `Check node ${node.id} failed with exit status ${checkEvidence.exitStatus}${provenanceNote}.`
|
|
9032
|
+
};
|
|
9033
|
+
current.execution.nodes[node.id] = {
|
|
9034
|
+
...execution,
|
|
9035
|
+
...patch,
|
|
9036
|
+
status: "awaiting_approval",
|
|
9037
|
+
approval,
|
|
9038
|
+
approvalHistory: [...execution.approvalHistory, approval]
|
|
9039
|
+
};
|
|
9040
|
+
}
|
|
9041
|
+
},
|
|
9042
|
+
false
|
|
9043
|
+
);
|
|
9044
|
+
} else {
|
|
9045
|
+
const failureMessage = deterministicOutcome.message;
|
|
9046
|
+
this.mutate(
|
|
9047
|
+
handle,
|
|
9048
|
+
(current) => {
|
|
9049
|
+
const execution = current.execution.nodes[node.id];
|
|
9050
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
9051
|
+
const completedAt = now();
|
|
9052
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
9053
|
+
node,
|
|
9054
|
+
execution,
|
|
9055
|
+
failureMessage,
|
|
9056
|
+
completedAt,
|
|
9057
|
+
capturedOutput
|
|
9058
|
+
);
|
|
9059
|
+
current.execution.nodes[node.id] = retry ? { ...execution, ...patch, status: "available", error: void 0 } : { ...execution, ...patch, status: "failed", completedAt };
|
|
8813
9060
|
},
|
|
8814
9061
|
false
|
|
8815
9062
|
);
|
|
@@ -8955,6 +9202,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8955
9202
|
s.lastSignaledExecution = s.execution;
|
|
8956
9203
|
}
|
|
8957
9204
|
}
|
|
9205
|
+
this.rearmWakeTimer(handle);
|
|
8958
9206
|
return typeof result === "string" ? result : JSON.stringify({ ok: true });
|
|
8959
9207
|
}
|
|
8960
9208
|
/**
|
|
@@ -9000,6 +9248,181 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
9000
9248
|
done: computed.done && flowStatus === "complete"
|
|
9001
9249
|
};
|
|
9002
9250
|
}
|
|
9251
|
+
// ── Agent-node deadline wake timer ────────────────────────────────────────
|
|
9252
|
+
//
|
|
9253
|
+
// Agent nodes have no `await` the adapter can wrap — it marks the node
|
|
9254
|
+
// started and waits for the model to call `complete_node`, which may never
|
|
9255
|
+
// come. The derived deadline (see `engine/node-deadline.ts`) is the source
|
|
9256
|
+
// of truth: it is a pure function of `startedAt` + `approvalHistory` +
|
|
9257
|
+
// `inputHistory`, so it is correct on its own after `hydrate()` or a
|
|
9258
|
+
// reconnect with no timer required. This wake timer exists only so a
|
|
9259
|
+
// genuinely hung run — by definition nothing else happening — still gets
|
|
9260
|
+
// *some* event that triggers a re-evaluation of that deadline.
|
|
9261
|
+
clearWakeTimer(s) {
|
|
9262
|
+
if (s.wakeTimer !== null) {
|
|
9263
|
+
clearTimeout(s.wakeTimer);
|
|
9264
|
+
s.wakeTimer = null;
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
/**
|
|
9268
|
+
* Whether `execution` counts as "actively running" for `control.timeoutSec`
|
|
9269
|
+
* evaluation. Ordinarily this is just `status === "running"`. The one
|
|
9270
|
+
* exception: a non-gate agent node whose approval was just decided
|
|
9271
|
+
* `approved` stays at `awaiting_approval` — `applyApproval` deliberately
|
|
9272
|
+
* does not restore `running` there, because `doCompleteNode`'s
|
|
9273
|
+
* `approvedPrior` check requires exactly that status (a mandatory-approval
|
|
9274
|
+
* node has no `runningAutonomous` fallback). Without this second clause the
|
|
9275
|
+
* timeout would pause for the approval wait (correctly, via
|
|
9276
|
+
* `remainingActiveExecutionMs` reading the now-resolved `approvalHistory`
|
|
9277
|
+
* entry) and then never resume, since nothing re-evaluates a node that
|
|
9278
|
+
* never looks "running" again.
|
|
9279
|
+
*/
|
|
9280
|
+
isDeadlineEligible(execution) {
|
|
9281
|
+
if (execution.status === "running") return true;
|
|
9282
|
+
return execution.status === "awaiting_approval" && execution.approval?.decision === "approved";
|
|
9283
|
+
}
|
|
9284
|
+
/**
|
|
9285
|
+
* Recomputes the pending wake timer against the current state: cancels any
|
|
9286
|
+
* previously-armed timer, then arms a fresh one for the earliest deadline
|
|
9287
|
+
* among currently `running` agent nodes with a `control.timeoutSec`. A
|
|
9288
|
+
* no-op when nothing is running against a timeout, or the flow is already
|
|
9289
|
+
* terminal. Called after every {@link mutate}, plus `connect()` and
|
|
9290
|
+
* `hydrate()` (which bypass `mutate()`).
|
|
9291
|
+
*
|
|
9292
|
+
* `disconnect()` clears the timer and bumps `executionGeneration`, but a
|
|
9293
|
+
* `mutate()` already in flight (or one that lands after teardown for any
|
|
9294
|
+
* other reason) still calls this on its way out — bail here on
|
|
9295
|
+
* `handle.status` so a post-disconnect mutation can never re-arm a timer
|
|
9296
|
+
* that would fire against a handle nothing is watching anymore.
|
|
9297
|
+
*/
|
|
9298
|
+
rearmWakeTimer(handle) {
|
|
9299
|
+
const s = this.s(handle);
|
|
9300
|
+
this.clearWakeTimer(s);
|
|
9301
|
+
if (handle.status === "disconnected") return;
|
|
9302
|
+
if (s.execution.done || s.execution.status === "failed" || s.execution.status === "cancelled") {
|
|
9303
|
+
return;
|
|
9304
|
+
}
|
|
9305
|
+
const nowMs = Date.now();
|
|
9306
|
+
let earliestRemainingMs;
|
|
9307
|
+
for (const node of s.flowDef.nodes) {
|
|
9308
|
+
if (!isAgentNode(node)) continue;
|
|
9309
|
+
const timeoutSec = node.control?.timeoutSec;
|
|
9310
|
+
if (timeoutSec === void 0) continue;
|
|
9311
|
+
const execution = s.execution.nodes[node.id];
|
|
9312
|
+
if (!execution || !this.isDeadlineEligible(execution)) continue;
|
|
9313
|
+
const remaining = remainingActiveExecutionMs(execution, timeoutSec, nowMs);
|
|
9314
|
+
if (remaining === void 0) continue;
|
|
9315
|
+
if (earliestRemainingMs === void 0 || remaining < earliestRemainingMs) {
|
|
9316
|
+
earliestRemainingMs = remaining;
|
|
9317
|
+
}
|
|
9318
|
+
}
|
|
9319
|
+
if (earliestRemainingMs === void 0) return;
|
|
9320
|
+
const delayMs = Math.max(0, earliestRemainingMs);
|
|
9321
|
+
const timer = setTimeout(() => {
|
|
9322
|
+
s.wakeTimer = null;
|
|
9323
|
+
void this.evaluateAgentNodeDeadlines(handle);
|
|
9324
|
+
}, delayMs);
|
|
9325
|
+
timer.unref?.();
|
|
9326
|
+
s.wakeTimer = timer;
|
|
9327
|
+
}
|
|
9328
|
+
/**
|
|
9329
|
+
* Re-checks every `running` agent node's derived deadline and, for any that
|
|
9330
|
+
* have expired, either auto-retries (bounded by `control.retries`, composing
|
|
9331
|
+
* with the existing `doFailNode(recoverable: true)` and `retryNode`
|
|
9332
|
+
* pathways) or terminally fails the node. Safe to call at any time — the
|
|
9333
|
+
* derived deadline means this is a no-op unless something has actually
|
|
9334
|
+
* expired. Goes through {@link mutate}, so a state change here republishes
|
|
9335
|
+
* `onChange`/stimulus exactly like any other mutation, waking a fresh turn
|
|
9336
|
+
* when the node retries or the flow rolls up to `failed`.
|
|
9337
|
+
*/
|
|
9338
|
+
async evaluateAgentNodeDeadlines(handle) {
|
|
9339
|
+
const s = this.s(handle);
|
|
9340
|
+
const nowMs = Date.now();
|
|
9341
|
+
const expired = s.flowDef.nodes.filter((node) => {
|
|
9342
|
+
if (!isAgentNode(node)) return false;
|
|
9343
|
+
const timeoutSec = node.control?.timeoutSec;
|
|
9344
|
+
if (timeoutSec === void 0) return false;
|
|
9345
|
+
const execution = s.execution.nodes[node.id];
|
|
9346
|
+
if (!execution || !this.isDeadlineEligible(execution)) return false;
|
|
9347
|
+
const remaining = remainingActiveExecutionMs(execution, timeoutSec, nowMs);
|
|
9348
|
+
return remaining !== void 0 && remaining <= 0;
|
|
9349
|
+
});
|
|
9350
|
+
for (const node of expired) {
|
|
9351
|
+
this.mutate(
|
|
9352
|
+
handle,
|
|
9353
|
+
(current) => {
|
|
9354
|
+
const execution = current.execution.nodes[node.id];
|
|
9355
|
+
if (!execution || !this.isDeadlineEligible(execution)) return;
|
|
9356
|
+
const completedAt = now();
|
|
9357
|
+
const message = `Agent node ${node.id} timed out after ${node.control?.timeoutSec}s`;
|
|
9358
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
9359
|
+
node,
|
|
9360
|
+
execution,
|
|
9361
|
+
message,
|
|
9362
|
+
completedAt,
|
|
9363
|
+
execution.output
|
|
9364
|
+
);
|
|
9365
|
+
current.execution.nodes[node.id] = retry ? { ...execution, ...patch, status: "available", error: void 0 } : { ...execution, ...patch, status: "failed", completedAt };
|
|
9366
|
+
},
|
|
9367
|
+
true
|
|
9368
|
+
);
|
|
9369
|
+
}
|
|
9370
|
+
}
|
|
9371
|
+
/**
|
|
9372
|
+
* Records one failed attempt on `nodeId`'s execution and decides whether
|
|
9373
|
+
* the automatic bounded retry (`control.retries`) still has budget. Shared
|
|
9374
|
+
* by every automatic-failure seam (agent-node timeout, function/check
|
|
9375
|
+
* execution/timeout failure, sub-flow execution/timeout failure, and a
|
|
9376
|
+
* check's fail verdict) so the budget — derived from prior `'failed'`
|
|
9377
|
+
* entries in `outputHistory`, not a separate durable counter — is computed
|
|
9378
|
+
* once, the same way, everywhere.
|
|
9379
|
+
*
|
|
9380
|
+
* Returns the `NodeExecution` patch (error/errorHistory/outputHistory/
|
|
9381
|
+
* output) plus whether budget remains; the caller decides the resulting
|
|
9382
|
+
* `status` because that differs by kind (e.g. sub-flow degrades an
|
|
9383
|
+
* optional node's exhausted failure to `skipped`; agent/function do not,
|
|
9384
|
+
* preserving their existing behavior). A terminal failure whose node has
|
|
9385
|
+
* any `control.retries` configured carries `recoverable: true` so the
|
|
9386
|
+
* manual `retryNode` operator override — which sits outside this budget
|
|
9387
|
+
* entirely — remains usable once the automatic budget runs out.
|
|
9388
|
+
*
|
|
9389
|
+
* @param sourceErrorAtIso - The failure's own timestamp, when the caller has
|
|
9390
|
+
* one more precise than `completedAtIso` (e.g. a sub-flow child reports
|
|
9391
|
+
* its own `error.at`, which can predate this node's parent-side
|
|
9392
|
+
* `completedAt` by however long the result took to propagate). Defaults
|
|
9393
|
+
* to `completedAtIso` for callers (agent-timeout, function-node) whose
|
|
9394
|
+
* failure message is synthesized at the moment of detection.
|
|
9395
|
+
* @param checkEvidence - Derived evidence for a check's fail verdict, so it
|
|
9396
|
+
* rides on the same appended attempt as the failure it explains.
|
|
9397
|
+
*/
|
|
9398
|
+
planAutomaticRetry(node, execution, message, completedAtIso, capturedOutput, sourceErrorAtIso, checkEvidence) {
|
|
9399
|
+
const retries = node.control?.retries;
|
|
9400
|
+
const priorFailures = countFailedAttempts(execution.outputHistory);
|
|
9401
|
+
const retry = hasAutomaticRetryBudget(priorFailures, retries);
|
|
9402
|
+
const error = {
|
|
9403
|
+
message,
|
|
9404
|
+
recoverable: !retry && !!retries,
|
|
9405
|
+
at: sourceErrorAtIso ?? completedAtIso
|
|
9406
|
+
};
|
|
9407
|
+
const outputHistory = appendAttempt(
|
|
9408
|
+
execution.outputHistory,
|
|
9409
|
+
"failed",
|
|
9410
|
+
execution.startedAt ?? completedAtIso,
|
|
9411
|
+
completedAtIso,
|
|
9412
|
+
capturedOutput,
|
|
9413
|
+
error,
|
|
9414
|
+
{ check: checkEvidence }
|
|
9415
|
+
);
|
|
9416
|
+
return {
|
|
9417
|
+
retry,
|
|
9418
|
+
patch: {
|
|
9419
|
+
error,
|
|
9420
|
+
errorHistory: [...execution.errorHistory, error],
|
|
9421
|
+
outputHistory,
|
|
9422
|
+
output: capturedOutput ?? execution.output
|
|
9423
|
+
}
|
|
9424
|
+
};
|
|
9425
|
+
}
|
|
9003
9426
|
/**
|
|
9004
9427
|
* Build the selected agent node's complete turn briefing plus upstream context.
|
|
9005
9428
|
*/
|
|
@@ -9087,7 +9510,8 @@ function routerSelectionHandoffSummary(router, execution) {
|
|
|
9087
9510
|
if (!selection) return void 0;
|
|
9088
9511
|
return selection.selectedTarget === null ? "Router completed without selecting a target." : `Router selected ${selection.selectedTarget}.`;
|
|
9089
9512
|
}
|
|
9090
|
-
function
|
|
9513
|
+
function deterministicNodeOutput(node, result, passed) {
|
|
9514
|
+
const kindLabel = deterministicNodeKindLabel(node.run.kind);
|
|
9091
9515
|
const capturedFields = [];
|
|
9092
9516
|
for (const channel of ["stdout", "stderr"]) {
|
|
9093
9517
|
const destination = node.run.capture?.[channel];
|
|
@@ -9095,14 +9519,15 @@ function functionNodeOutput(node, result) {
|
|
|
9095
9519
|
const match = /^fields\.([^.]+)$/.exec(destination);
|
|
9096
9520
|
if (!match?.[1]) {
|
|
9097
9521
|
throw new Error(
|
|
9098
|
-
|
|
9522
|
+
`${kindLabel} node ${node.id} run.capture.${channel} destination "${destination}" is invalid; expected fields.<name>`
|
|
9099
9523
|
);
|
|
9100
9524
|
}
|
|
9101
9525
|
capturedFields.push([match[1], result[channel]]);
|
|
9102
9526
|
}
|
|
9103
9527
|
const fields = Object.fromEntries(capturedFields);
|
|
9528
|
+
const summary = node.run.kind === "check" ? `Check node ${node.id} ${passed ? "passed" : "failed"} with exit status ${result.exitStatus}.` : `Function node ${node.id} exited with status ${result.exitStatus}.`;
|
|
9104
9529
|
return {
|
|
9105
|
-
summary
|
|
9530
|
+
summary,
|
|
9106
9531
|
artifacts: [],
|
|
9107
9532
|
...Object.keys(fields).length > 0 ? { fields } : {}
|
|
9108
9533
|
};
|
|
@@ -9176,5 +9601,5 @@ function createConnector() {
|
|
|
9176
9601
|
}
|
|
9177
9602
|
|
|
9178
9603
|
export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, BoxConnector, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, PortPool, PostgresConnector, S3Connector, SQLiteConnector, SessionInlineSubFlowRunner, SessionNodeExecutor, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildSdkConnectorTools, buildSdkFlowTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector20, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, ensureDirMode, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, readGitSyncStatus, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
|
|
9179
|
-
//# sourceMappingURL=chunk-
|
|
9180
|
-
//# sourceMappingURL=chunk-
|
|
9604
|
+
//# sourceMappingURL=chunk-E5VGAW4E.js.map
|
|
9605
|
+
//# sourceMappingURL=chunk-E5VGAW4E.js.map
|