@skaile/workspaces 2.8.0 → 3.0.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 +22 -0
- package/README.md +17 -6
- package/dist/{asset-feeds-23CP6YUY.js → asset-feeds-XKPJZ6QT.js} +4 -4
- package/dist/{asset-feeds-23CP6YUY.js.map → asset-feeds-XKPJZ6QT.js.map} +1 -1
- package/dist/asset-manager/index.js +1 -1
- package/dist/bridge/drivers/claude-sdk.js +2 -2
- package/dist/bridge/drivers/codex.js +2 -2
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +1 -1
- package/dist/bridge/drivers/gemini.js +4 -4
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +4 -4
- package/dist/bridge/index.js +3 -3
- package/dist/bridge/src/models.d.ts +59 -0
- package/dist/bridge/src/models.d.ts.map +1 -1
- package/dist/{chunk-XJ74XFIZ.js → chunk-5QGMEPUX.js} +27 -18
- package/dist/chunk-5QGMEPUX.js.map +1 -0
- package/dist/{chunk-ONUDDQGS.js → chunk-7HXS7WZF.js} +2 -2
- package/dist/{chunk-ONUDDQGS.js.map → chunk-7HXS7WZF.js.map} +1 -1
- package/dist/{chunk-SCR3WGW7.js → chunk-7KFN2EPR.js} +12 -4
- package/dist/chunk-7KFN2EPR.js.map +1 -0
- package/dist/{chunk-L3TS7ZO4.js → chunk-BJDACPWZ.js} +3 -3
- package/dist/{chunk-L3TS7ZO4.js.map → chunk-BJDACPWZ.js.map} +1 -1
- package/dist/{chunk-JIA2S3T2.js → chunk-EH7BCJF4.js} +6 -6
- package/dist/{chunk-JIA2S3T2.js.map → chunk-EH7BCJF4.js.map} +1 -1
- package/dist/{chunk-TUYN2HDT.js → chunk-F5VREBV2.js} +14 -5
- package/dist/chunk-F5VREBV2.js.map +1 -0
- package/dist/{chunk-OAUKA7VT.js → chunk-JQMULHO6.js} +24 -12
- package/dist/chunk-JQMULHO6.js.map +1 -0
- package/dist/{chunk-ZR2X4SIG.js → chunk-KXWUCKP5.js} +5 -5
- package/dist/{chunk-ZR2X4SIG.js.map → chunk-KXWUCKP5.js.map} +1 -1
- package/dist/{chunk-IWUIXNMF.js → chunk-N4GFUWWC.js} +3 -3
- package/dist/{chunk-IWUIXNMF.js.map → chunk-N4GFUWWC.js.map} +1 -1
- package/dist/{chunk-ZJOEU5GP.js → chunk-NMKTM55X.js} +2 -2
- package/dist/{chunk-ZJOEU5GP.js.map → chunk-NMKTM55X.js.map} +1 -1
- package/dist/{chunk-XTFZOYNY.js → chunk-P2J3MLTW.js} +3 -3
- package/dist/{chunk-XTFZOYNY.js.map → chunk-P2J3MLTW.js.map} +1 -1
- package/dist/{chunk-VNIKF66R.js → chunk-P5W6JVD4.js} +4 -4
- package/dist/{chunk-VNIKF66R.js.map → chunk-P5W6JVD4.js.map} +1 -1
- package/dist/{chunk-6AYGXPPF.js → chunk-Q6O53D5Q.js} +2 -2
- package/dist/{chunk-6AYGXPPF.js.map → chunk-Q6O53D5Q.js.map} +1 -1
- package/dist/{chunk-ZJ7MRR2R.js → chunk-QGDQHNYC.js} +4 -4
- package/dist/{chunk-ZJ7MRR2R.js.map → chunk-QGDQHNYC.js.map} +1 -1
- package/dist/{chunk-E4AVJMOJ.js → chunk-S5FV6ISY.js} +89 -6
- package/dist/chunk-S5FV6ISY.js.map +1 -0
- package/dist/{chunk-HY3MBSNC.js → chunk-SR6NBBWT.js} +2 -2
- package/dist/{chunk-HY3MBSNC.js.map → chunk-SR6NBBWT.js.map} +1 -1
- package/dist/{chunk-AZ44F3AT.js → chunk-TXTCW7V6.js} +6 -6
- package/dist/{chunk-AZ44F3AT.js.map → chunk-TXTCW7V6.js.map} +1 -1
- package/dist/{chunk-BMQKMOQY.js → chunk-V34ZEXSE.js} +61 -3
- package/dist/chunk-V34ZEXSE.js.map +1 -0
- package/dist/{chunk-FW2C6JJP.js → chunk-WECBTWWZ.js} +3 -3
- package/dist/{chunk-FW2C6JJP.js.map → chunk-WECBTWWZ.js.map} +1 -1
- package/dist/{chunk-P3DUVOAW.js → chunk-XJECOBCO.js} +16 -7
- package/dist/chunk-XJECOBCO.js.map +1 -0
- package/dist/{chunk-HVQWLYZB.js → chunk-ZO7KL7GN.js} +731 -85
- package/dist/chunk-ZO7KL7GN.js.map +1 -0
- package/dist/cli/index.js +38 -38
- package/dist/connectors/index.js +11 -5
- package/dist/{ensure-sources-N7B7657V.js → ensure-sources-D6R57GAP.js} +4 -4
- package/dist/{ensure-sources-N7B7657V.js.map → ensure-sources-D6R57GAP.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +11 -5
- package/dist/factory-assets/connectors/deploy.js +11 -5
- package/dist/factory-assets/connectors/devserver.js +11 -5
- package/dist/factory-assets/connectors/flow/CONNECTOR.md +80 -11
- package/dist/factory-assets/connectors/flow/adapter.d.ts +97 -5
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +11 -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/flow.v2.schema.json +0 -6
- package/dist/factory-assets/connectors/flow/contract/normalize.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts +0 -4
- 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/bindings.d.ts.map +1 -1
- 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 +15 -1
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/types.d.ts +16 -15
- 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 +2 -1
- package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts +3 -0
- package/dist/factory-assets/connectors/flow/inline-sub-flow-host.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.d.ts +14 -0
- package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/prompt-fragments.js +2 -2
- package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/run-flow.js +12 -6
- package/dist/factory-assets/connectors/flow/subprompt-runner.d.ts +99 -0
- package/dist/factory-assets/connectors/flow/subprompt-runner.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow.js +11 -5
- package/dist/factory-assets/connectors/git.js +11 -5
- package/dist/factory-assets/connectors/gmail.js +11 -5
- package/dist/factory-assets/connectors/googledrive.js +11 -5
- package/dist/factory-assets/connectors/local.js +11 -5
- package/dist/factory-assets/connectors/mattermost.js +11 -5
- package/dist/factory-assets/connectors/memory.js +11 -5
- package/dist/factory-assets/connectors/minio.js +11 -5
- package/dist/factory-assets/connectors/postgres.js +11 -5
- package/dist/factory-assets/connectors/s3.js +11 -5
- package/dist/factory-assets/connectors/sharepoint.js +11 -5
- package/dist/factory-assets/connectors/sqlite.js +11 -5
- package/dist/factory-assets/connectors/static-server.js +11 -5
- package/dist/factory-assets/connectors/tunnel.js +11 -5
- package/dist/factory-assets/connectors/webdav.js +11 -5
- package/dist/factory-assets/connectors/xstate-store.js +11 -5
- package/dist/factory-assets/connectors/xstate.js +11 -5
- package/dist/{flows-K7B253U6.js → flows-LQSVM45J.js} +3 -3
- package/dist/{flows-K7B253U6.js.map → flows-LQSVM45J.js.map} +1 -1
- package/dist/{helpers-PXEHIKF7.js → helpers-IS4MSEOQ.js} +4 -4
- package/dist/{helpers-PXEHIKF7.js.map → helpers-IS4MSEOQ.js.map} +1 -1
- package/dist/{library-publish-3Z33J2ZY.js → library-publish-QJACP6FW.js} +9 -9
- package/dist/{library-publish-3Z33J2ZY.js.map → library-publish-QJACP6FW.js.map} +1 -1
- package/dist/{open-library-Y5SKFAMK.js → open-library-3FMLAU3F.js} +8 -8
- package/dist/{open-library-Y5SKFAMK.js.map → open-library-3FMLAU3F.js.map} +1 -1
- package/dist/{project-DQRC6J6M.js → project-KEAAGHSX.js} +5 -5
- package/dist/{project-DQRC6J6M.js.map → project-KEAAGHSX.js.map} +1 -1
- package/dist/runner/index.js +15 -15
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +1 -1
- package/dist/sdk/bridge.js +3 -3
- package/dist/sdk/flow.js +3 -3
- package/dist/sdk/index.js +15 -15
- package/dist/sdk/runner.js +15 -15
- package/dist/{setup-RHWARBBB.js → setup-VX6EPF5P.js} +5 -5
- package/dist/{setup-RHWARBBB.js.map → setup-VX6EPF5P.js.map} +1 -1
- package/dist/{source-XBJ5KTAV.js → source-45XHQSBF.js} +9 -9
- package/dist/{source-XBJ5KTAV.js.map → source-45XHQSBF.js.map} +1 -1
- package/dist/{source-manifest-publish-Q7TXBMCW.js → source-manifest-publish-M34SKD46.js} +10 -10
- package/dist/{source-manifest-publish-Q7TXBMCW.js.map → source-manifest-publish-M34SKD46.js.map} +1 -1
- package/dist/tui/index.js +15 -15
- 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-BMQKMOQY.js.map +0 -1
- package/dist/chunk-E4AVJMOJ.js.map +0 -1
- package/dist/chunk-HVQWLYZB.js.map +0 -1
- package/dist/chunk-OAUKA7VT.js.map +0 -1
- package/dist/chunk-P3DUVOAW.js.map +0 -1
- package/dist/chunk-SCR3WGW7.js.map +0 -1
- package/dist/chunk-TUYN2HDT.js.map +0 -1
- package/dist/chunk-XJ74XFIZ.js.map +0 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { resolveAuthRef } from './chunk-ZNIHTT22.js';
|
|
2
|
+
import { EventNormalizer } from './chunk-P5W6JVD4.js';
|
|
3
|
+
import { resolveModelTier } from './chunk-V34ZEXSE.js';
|
|
4
|
+
import { createDriver } from './chunk-SR6NBBWT.js';
|
|
2
5
|
import { pluginRegistry } from './chunk-6E6PKKAD.js';
|
|
3
6
|
import { computeStimulus } from './chunk-IFGTRL2I.js';
|
|
4
|
-
import { getConsumedOutputSchema, validateConsumedNodeOutput, resolveNodeBindings, computeFlowStateFromSnapshots, availableRouterFrontier, evaluateRouterSelection, routerSelectionOutput, planRouterSkips, unsettledRouterBranchIds,
|
|
5
|
-
import { parseFlowDefinition } from './chunk-
|
|
6
|
-
import { renderStimulusPrompt } from './chunk-
|
|
7
|
-
import { flowDefaults, isAgentNode, isGateNode, nodeRequiresApproval, isSubFlowNode, isFunctionNode, nodeIsOptional, isRouterNode, isActiveRuntimeNode, nodeLabel,
|
|
7
|
+
import { getConsumedOutputSchema, validateConsumedNodeOutput, appendAttempt, remainingActiveExecutionMs, resolveNodeBindings, interpolateNodeInstruction, deriveCheckEvidence, computeFlowStateFromSnapshots, availableRouterFrontier, evaluateRouterSelection, routerSelectionOutput, planRouterSkips, unsettledRouterBranchIds, countFailedAttempts, hasAutomaticRetryBudget, persistedRouterSelection } from './chunk-S5FV6ISY.js';
|
|
8
|
+
import { parseFlowDefinition } from './chunk-5QGMEPUX.js';
|
|
9
|
+
import { renderSubpromptPrompt, renderStimulusPrompt } from './chunk-7KFN2EPR.js';
|
|
10
|
+
import { deterministicNodeKindLabel, flowDefaults, isAgentNode, isGateNode, isCheckNode, nodeRequiresApproval, isSubFlowNode, isFunctionNode, isSubpromptNode, nodeIsOptional, nodeInstruction, isRouterNode, isActiveRuntimeNode, nodeLabel, nodeAssets } from './chunk-F5VREBV2.js';
|
|
8
11
|
import { portableSpawn } from './chunk-P43HBWKK.js';
|
|
9
12
|
import { withFsDeadline, tryWithFsDeadline, FsDeadlineError, fsDeadlineMs } from './chunk-DYMWJAL4.js';
|
|
10
13
|
import { createLogger } from './chunk-7GOCNYUV.js';
|
|
@@ -185,6 +188,7 @@ var SessionInlineSubFlowRunner = class {
|
|
|
185
188
|
inlineSubFlowAncestry: [...request.ancestry, flow.id],
|
|
186
189
|
inlineSubFlowRunner: this,
|
|
187
190
|
nodeExecutor: this.options.nodeExecutor,
|
|
191
|
+
subpromptRunner: this.options.subpromptRunner,
|
|
188
192
|
seed: {
|
|
189
193
|
runId: `${request.parentExecution.runId}:${request.node.id}`,
|
|
190
194
|
startedBy: request.parentExecution.startedBy,
|
|
@@ -324,11 +328,11 @@ function stableJsonStringify(value) {
|
|
|
324
328
|
);
|
|
325
329
|
});
|
|
326
330
|
}
|
|
327
|
-
function serializeEnvironmentInput(name, value) {
|
|
331
|
+
function serializeEnvironmentInput(name, value, nodeLabel2) {
|
|
328
332
|
if (typeof value === "string") return value;
|
|
329
333
|
const serialized = stableJsonStringify(value);
|
|
330
334
|
if (serialized === void 0) {
|
|
331
|
-
throw new Error(
|
|
335
|
+
throw new Error(`${nodeLabel2} input ${name} is not JSON-serializable`);
|
|
332
336
|
}
|
|
333
337
|
return serialized;
|
|
334
338
|
}
|
|
@@ -348,19 +352,18 @@ var SessionNodeExecutor = class {
|
|
|
348
352
|
constructor(options) {
|
|
349
353
|
this.workspaceDir = resolve(options.workspaceDir);
|
|
350
354
|
}
|
|
351
|
-
/** Runs one function node process with session environment and binding overrides. */
|
|
355
|
+
/** Runs one function or check node process with session environment and binding overrides. */
|
|
352
356
|
async execute(request) {
|
|
357
|
+
const nodeLabel2 = `${deterministicNodeKindLabel(request.node.run.kind)} node ${request.node.id}`;
|
|
353
358
|
const cwd = resolve(this.workspaceDir, request.node.run.cwd ?? ".");
|
|
354
359
|
const workspaceRelative = relative(this.workspaceDir, cwd);
|
|
355
360
|
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
|
-
);
|
|
361
|
+
throw new Error(`${nodeLabel2} cwd escapes the session workspace: ${request.node.run.cwd}`);
|
|
359
362
|
}
|
|
360
363
|
const boundEnvironment = Object.fromEntries(
|
|
361
364
|
Object.entries(request.inputs).map(([name, value]) => [
|
|
362
365
|
name,
|
|
363
|
-
serializeEnvironmentInput(name, value)
|
|
366
|
+
serializeEnvironmentInput(name, value, nodeLabel2)
|
|
364
367
|
])
|
|
365
368
|
);
|
|
366
369
|
const process2 = portableSpawn(commandFor(request.node), {
|
|
@@ -375,6 +378,78 @@ var SessionNodeExecutor = class {
|
|
|
375
378
|
return { exitStatus, stdout, stderr };
|
|
376
379
|
}
|
|
377
380
|
};
|
|
381
|
+
|
|
382
|
+
// factory-assets/connectors/flow/subprompt-runner.ts
|
|
383
|
+
var SessionSubpromptRunner = class {
|
|
384
|
+
constructor(options) {
|
|
385
|
+
this.options = options;
|
|
386
|
+
}
|
|
387
|
+
options;
|
|
388
|
+
async execute(request) {
|
|
389
|
+
const model = resolveModelTier(request.node.run.model, this.options.sessionModel, {
|
|
390
|
+
provider: this.options.provider,
|
|
391
|
+
cloud: this.options.cloud,
|
|
392
|
+
overrides: this.options.modelTierOverrides
|
|
393
|
+
});
|
|
394
|
+
const config = {
|
|
395
|
+
cwd: this.options.workspaceDir,
|
|
396
|
+
provider: this.options.provider,
|
|
397
|
+
model,
|
|
398
|
+
// A subprompt is one model call by definition. Backends default to a
|
|
399
|
+
// multi-turn agentic loop (claude-sdk: 15), which would let a node that
|
|
400
|
+
// was asked to classify a string go off and work instead.
|
|
401
|
+
maxTurns: 1,
|
|
402
|
+
...this.options.apiKey ? { apiKeys: { [this.options.provider ?? "anthropic"]: this.options.apiKey } } : {},
|
|
403
|
+
...this.options.cloud && this.options.cloud !== "default" ? { cloud: this.options.cloud } : {},
|
|
404
|
+
...this.options.cloudConfig ? { cloudConfig: this.options.cloudConfig } : {},
|
|
405
|
+
...this.options.cloudSecrets ? { cloudSecrets: this.options.cloudSecrets } : {}
|
|
406
|
+
};
|
|
407
|
+
const driver = await createDriver(this.options.driverType, config);
|
|
408
|
+
try {
|
|
409
|
+
await driver.start();
|
|
410
|
+
const text = await runIsolatedPrompt(
|
|
411
|
+
driver,
|
|
412
|
+
request.prompt,
|
|
413
|
+
this.options.timeoutMs ?? DEFAULT_SUBPROMPT_TIMEOUT_MS
|
|
414
|
+
);
|
|
415
|
+
return { text };
|
|
416
|
+
} finally {
|
|
417
|
+
driver.kill();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
var DEFAULT_SUBPROMPT_TIMEOUT_MS = 5 * 6e4;
|
|
422
|
+
function runIsolatedPrompt(driver, prompt, timeoutMs) {
|
|
423
|
+
return new Promise((resolve6, reject) => {
|
|
424
|
+
const normalizer = new EventNormalizer();
|
|
425
|
+
let text = "";
|
|
426
|
+
let settled = false;
|
|
427
|
+
const done = (err) => {
|
|
428
|
+
if (settled) return;
|
|
429
|
+
settled = true;
|
|
430
|
+
clearTimeout(timer);
|
|
431
|
+
driver.off("agent-event", onEvent);
|
|
432
|
+
if (err) reject(err);
|
|
433
|
+
else resolve6(text);
|
|
434
|
+
};
|
|
435
|
+
const timer = setTimeout(
|
|
436
|
+
() => done(new Error(`isolated subprompt call exceeded ${timeoutMs}ms`)),
|
|
437
|
+
timeoutMs
|
|
438
|
+
);
|
|
439
|
+
const onEvent = (event) => {
|
|
440
|
+
for (const normalized of normalizer.normalize(event)) {
|
|
441
|
+
if (normalized.type === "text") text += normalized.content;
|
|
442
|
+
}
|
|
443
|
+
if (event.type === "agent_end") {
|
|
444
|
+
done();
|
|
445
|
+
} else if (event.type === "error") {
|
|
446
|
+
done(new Error(event.error));
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
driver.on("agent-event", onEvent);
|
|
450
|
+
driver.prompt(prompt).catch(done);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
378
453
|
var execFileAsync = promisify(execFile);
|
|
379
454
|
var GIT_REV_PARSE_TIMEOUT_MS = 15e3;
|
|
380
455
|
var DeployConnector = class extends AbstractConnector {
|
|
@@ -7487,6 +7562,28 @@ var AGENT_INLINE_OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
7487
7562
|
"skip_node",
|
|
7488
7563
|
"fail_node"
|
|
7489
7564
|
]);
|
|
7565
|
+
var NodeTimeoutError = class extends Error {
|
|
7566
|
+
};
|
|
7567
|
+
function raceWithTimeout(promise, ms, timeoutMessage) {
|
|
7568
|
+
return new Promise((resolve6, reject) => {
|
|
7569
|
+
const timer = setTimeout(() => {
|
|
7570
|
+
reject(new NodeTimeoutError(timeoutMessage));
|
|
7571
|
+
promise.catch(() => {
|
|
7572
|
+
});
|
|
7573
|
+
}, ms);
|
|
7574
|
+
timer.unref?.();
|
|
7575
|
+
promise.then(
|
|
7576
|
+
(value) => {
|
|
7577
|
+
clearTimeout(timer);
|
|
7578
|
+
resolve6(value);
|
|
7579
|
+
},
|
|
7580
|
+
(err) => {
|
|
7581
|
+
clearTimeout(timer);
|
|
7582
|
+
reject(err);
|
|
7583
|
+
}
|
|
7584
|
+
);
|
|
7585
|
+
});
|
|
7586
|
+
}
|
|
7490
7587
|
function mergeInteractionHistory(history, interaction) {
|
|
7491
7588
|
if (!interaction) return history;
|
|
7492
7589
|
const next = [...history];
|
|
@@ -7507,7 +7604,8 @@ function emptyNode(nodeId) {
|
|
|
7507
7604
|
status: "not_started",
|
|
7508
7605
|
approvalHistory: [],
|
|
7509
7606
|
inputHistory: [],
|
|
7510
|
-
errorHistory: []
|
|
7607
|
+
errorHistory: [],
|
|
7608
|
+
outputHistory: []
|
|
7511
7609
|
};
|
|
7512
7610
|
}
|
|
7513
7611
|
function nodeExecutionRecord(entries = []) {
|
|
@@ -7580,7 +7678,7 @@ function prepareExecutionSnapshot(flowDef, execution) {
|
|
|
7580
7678
|
const nodes = nodeExecutionRecord(Object.entries(snapshot.nodes ?? {}));
|
|
7581
7679
|
for (const node of flowDef.nodes) {
|
|
7582
7680
|
const existing = nodes[node.id];
|
|
7583
|
-
if (isSubFlowNode(node) && existing && ["running", "awaiting_approval", "awaiting_input"].includes(existing.status)) {
|
|
7681
|
+
if ((isSubFlowNode(node) || isSubpromptNode(node)) && existing && ["running", "awaiting_approval", "awaiting_input"].includes(existing.status)) {
|
|
7584
7682
|
nodes[node.id] = {
|
|
7585
7683
|
...existing,
|
|
7586
7684
|
status: "not_started",
|
|
@@ -7644,18 +7742,23 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7644
7742
|
connectorId: declaration.id,
|
|
7645
7743
|
useBusForStimulus: options.useBusForStimulus ?? false,
|
|
7646
7744
|
nodeExecutor: options.nodeExecutor,
|
|
7745
|
+
subpromptRunner: options.subpromptRunner,
|
|
7647
7746
|
inlineSubFlowRunner: options.inlineSubFlowRunner,
|
|
7648
7747
|
inlineOperationTargets: /* @__PURE__ */ new Map(),
|
|
7649
7748
|
flowInputOrigins: options.flowInputOrigins,
|
|
7650
7749
|
inlineSubFlowAncestry: options.inlineSubFlowAncestry ?? [flowDef.id],
|
|
7651
|
-
executionGeneration: 0
|
|
7750
|
+
executionGeneration: 0,
|
|
7751
|
+
wakeTimer: null
|
|
7652
7752
|
};
|
|
7653
7753
|
state.execution = this.recompute(state);
|
|
7654
|
-
|
|
7754
|
+
const handle = this.makeHandle(declaration, state);
|
|
7755
|
+
this.rearmWakeTimer(handle);
|
|
7756
|
+
return handle;
|
|
7655
7757
|
}
|
|
7656
7758
|
async disconnect(handle) {
|
|
7657
7759
|
const state = this.s(handle);
|
|
7658
7760
|
state.executionGeneration += 1;
|
|
7761
|
+
this.clearWakeTimer(state);
|
|
7659
7762
|
await this.cancelInlineTargets(state);
|
|
7660
7763
|
handle.status = "disconnected";
|
|
7661
7764
|
}
|
|
@@ -7703,6 +7806,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7703
7806
|
const changed = JSON.stringify(prepared) !== JSON.stringify(execution);
|
|
7704
7807
|
s.executionGeneration += 1;
|
|
7705
7808
|
s.execution = prepared;
|
|
7809
|
+
this.rearmWakeTimer(handle);
|
|
7706
7810
|
if (changed && s.onChange) s.onChange(s.execution);
|
|
7707
7811
|
}
|
|
7708
7812
|
// ── Base operations (private — wired into tools face in constructor) ──
|
|
@@ -8315,17 +8419,27 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8315
8419
|
this.mutate(handle, (current) => {
|
|
8316
8420
|
const execution = current.execution.nodes[nodeId];
|
|
8317
8421
|
assertLegal(execution, `node not in execution: ${nodeId}`);
|
|
8422
|
+
const completedAt = now();
|
|
8318
8423
|
const error = {
|
|
8319
8424
|
message: `Node ${nodeId} output failed schema validation after one correction: ${issueText}`,
|
|
8320
8425
|
recoverable: false,
|
|
8321
|
-
at:
|
|
8426
|
+
at: completedAt
|
|
8322
8427
|
};
|
|
8428
|
+
const outputHistory = execution.startedAt ? appendAttempt(
|
|
8429
|
+
execution.outputHistory,
|
|
8430
|
+
"failed",
|
|
8431
|
+
execution.startedAt,
|
|
8432
|
+
completedAt,
|
|
8433
|
+
execution.output,
|
|
8434
|
+
error
|
|
8435
|
+
) : execution.outputHistory;
|
|
8323
8436
|
current.execution.nodes[nodeId] = {
|
|
8324
8437
|
...execution,
|
|
8325
8438
|
status: "failed",
|
|
8326
|
-
completedAt
|
|
8439
|
+
completedAt,
|
|
8327
8440
|
error,
|
|
8328
|
-
errorHistory: [...execution.errorHistory, error]
|
|
8441
|
+
errorHistory: [...execution.errorHistory, error],
|
|
8442
|
+
outputHistory
|
|
8329
8443
|
};
|
|
8330
8444
|
});
|
|
8331
8445
|
throw new Error(
|
|
@@ -8335,11 +8449,21 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8335
8449
|
return this.mutate(handle, (current) => {
|
|
8336
8450
|
const execution = current.execution.nodes[nodeId];
|
|
8337
8451
|
assertLegal(execution, `node not in execution: ${nodeId}`);
|
|
8452
|
+
const completedAt = now();
|
|
8453
|
+
const outputHistory = execution.startedAt ? appendAttempt(
|
|
8454
|
+
execution.outputHistory,
|
|
8455
|
+
"complete",
|
|
8456
|
+
execution.startedAt,
|
|
8457
|
+
completedAt,
|
|
8458
|
+
output,
|
|
8459
|
+
void 0
|
|
8460
|
+
) : execution.outputHistory;
|
|
8338
8461
|
current.execution.nodes[nodeId] = {
|
|
8339
8462
|
...execution,
|
|
8340
8463
|
status: "complete",
|
|
8341
|
-
completedAt
|
|
8342
|
-
output
|
|
8464
|
+
completedAt,
|
|
8465
|
+
output,
|
|
8466
|
+
outputHistory
|
|
8343
8467
|
};
|
|
8344
8468
|
return JSON.stringify({ ok: true, status: "complete" });
|
|
8345
8469
|
});
|
|
@@ -8357,10 +8481,20 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8357
8481
|
`cannot skip node ${nodeId}: it is a mandatory approval gate \u2014 call request_approval and wait for an approved decision`
|
|
8358
8482
|
);
|
|
8359
8483
|
assertLegal(isOptional(s.flowDef, nodeId), `cannot skip required node ${nodeId}`);
|
|
8484
|
+
const completedAt = now();
|
|
8485
|
+
const outputHistory = node.startedAt ? appendAttempt(
|
|
8486
|
+
node.outputHistory,
|
|
8487
|
+
"skipped",
|
|
8488
|
+
node.startedAt,
|
|
8489
|
+
completedAt,
|
|
8490
|
+
node.output,
|
|
8491
|
+
void 0
|
|
8492
|
+
) : node.outputHistory;
|
|
8360
8493
|
s.execution.nodes[nodeId] = {
|
|
8361
8494
|
...node,
|
|
8362
8495
|
status: "skipped",
|
|
8363
|
-
completedAt
|
|
8496
|
+
completedAt,
|
|
8497
|
+
outputHistory
|
|
8364
8498
|
};
|
|
8365
8499
|
return JSON.stringify({ ok: true, status: "skipped" });
|
|
8366
8500
|
});
|
|
@@ -8375,19 +8509,40 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8375
8509
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
8376
8510
|
const error = { message, recoverable, at: now() };
|
|
8377
8511
|
if (recoverable) {
|
|
8512
|
+
const completedAt = error.at;
|
|
8513
|
+
const outputHistory = node.startedAt ? appendAttempt(
|
|
8514
|
+
node.outputHistory,
|
|
8515
|
+
"failed",
|
|
8516
|
+
node.startedAt,
|
|
8517
|
+
completedAt,
|
|
8518
|
+
node.output,
|
|
8519
|
+
error,
|
|
8520
|
+
{ selfRetry: true }
|
|
8521
|
+
) : node.outputHistory;
|
|
8378
8522
|
s.execution.nodes[nodeId] = {
|
|
8379
8523
|
...node,
|
|
8380
8524
|
status: "available",
|
|
8381
8525
|
error,
|
|
8382
|
-
errorHistory: [...node.errorHistory, error]
|
|
8526
|
+
errorHistory: [...node.errorHistory, error],
|
|
8527
|
+
outputHistory
|
|
8383
8528
|
};
|
|
8384
8529
|
} else {
|
|
8530
|
+
const completedAt = now();
|
|
8531
|
+
const outputHistory = node.startedAt ? appendAttempt(
|
|
8532
|
+
node.outputHistory,
|
|
8533
|
+
"failed",
|
|
8534
|
+
node.startedAt,
|
|
8535
|
+
completedAt,
|
|
8536
|
+
node.output,
|
|
8537
|
+
error
|
|
8538
|
+
) : node.outputHistory;
|
|
8385
8539
|
s.execution.nodes[nodeId] = {
|
|
8386
8540
|
...node,
|
|
8387
8541
|
status: "failed",
|
|
8388
|
-
completedAt
|
|
8542
|
+
completedAt,
|
|
8389
8543
|
error,
|
|
8390
|
-
errorHistory: [...node.errorHistory, error]
|
|
8544
|
+
errorHistory: [...node.errorHistory, error],
|
|
8545
|
+
outputHistory
|
|
8391
8546
|
};
|
|
8392
8547
|
}
|
|
8393
8548
|
return JSON.stringify({ ok: true, status: s.execution.nodes[nodeId].status });
|
|
@@ -8397,7 +8552,9 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8397
8552
|
/**
|
|
8398
8553
|
* Record an approval decision on a node currently `awaiting_approval`.
|
|
8399
8554
|
* A first-class gate completes directly when approved and remains parked when rejected.
|
|
8400
|
-
* Agent-node approvals keep their existing resume behavior.
|
|
8555
|
+
* Agent-node approvals keep their existing resume behavior. A check's escalation is a
|
|
8556
|
+
* one-way door: only `rejected` is legal, and it ends the node terminally `failed` —
|
|
8557
|
+
* there is no decision that resumes or passes a check.
|
|
8401
8558
|
*/
|
|
8402
8559
|
applyApproval(handle, nodeId, decision, feedback, decidedBy) {
|
|
8403
8560
|
this.mutate(handle, (s) => {
|
|
@@ -8414,6 +8571,12 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8414
8571
|
`applyApproval: gate ${nodeId} approval attempt is already decided; request approval again before applying another decision`
|
|
8415
8572
|
);
|
|
8416
8573
|
}
|
|
8574
|
+
if (isCheckNode(definition)) {
|
|
8575
|
+
assertLegal(
|
|
8576
|
+
decision === "rejected",
|
|
8577
|
+
`applyApproval: check ${nodeId} cannot be approved \u2014 a failed check has no passing state to approve into; only "rejected" is a legal decision`
|
|
8578
|
+
);
|
|
8579
|
+
}
|
|
8417
8580
|
const base = node.approval ?? { requestedAt: now(), summary: "" };
|
|
8418
8581
|
const approval = {
|
|
8419
8582
|
...base,
|
|
@@ -8440,6 +8603,14 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8440
8603
|
approval,
|
|
8441
8604
|
approvalHistory: history
|
|
8442
8605
|
};
|
|
8606
|
+
} else if (isCheckNode(definition)) {
|
|
8607
|
+
s.execution.nodes[nodeId] = {
|
|
8608
|
+
...node,
|
|
8609
|
+
status: "failed",
|
|
8610
|
+
completedAt: now(),
|
|
8611
|
+
approval,
|
|
8612
|
+
approvalHistory: history
|
|
8613
|
+
};
|
|
8443
8614
|
} else if (decision === "approved") {
|
|
8444
8615
|
s.execution.nodes[nodeId] = { ...node, approval, approvalHistory: history };
|
|
8445
8616
|
} else {
|
|
@@ -8513,10 +8684,17 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8513
8684
|
return void 0;
|
|
8514
8685
|
});
|
|
8515
8686
|
}
|
|
8516
|
-
/**
|
|
8687
|
+
/**
|
|
8688
|
+
* Cancel the flow run. Status transitions to `cancelled`; dependents stay
|
|
8689
|
+
* put. Bumps `executionGeneration` — the sole late-settlement rejection
|
|
8690
|
+
* mechanism every in-flight await (function executor, sub-flow child)
|
|
8691
|
+
* checks after resolving, so a result that arrives after cancellation can
|
|
8692
|
+
* never write state.
|
|
8693
|
+
*/
|
|
8517
8694
|
cancel(handle) {
|
|
8518
8695
|
this.mutate(handle, (s) => {
|
|
8519
8696
|
s.execution = { ...s.execution, status: "cancelled" };
|
|
8697
|
+
s.executionGeneration += 1;
|
|
8520
8698
|
return void 0;
|
|
8521
8699
|
});
|
|
8522
8700
|
}
|
|
@@ -8586,9 +8764,11 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8586
8764
|
return this.drainAvailableRuntimeNodes(handle, true);
|
|
8587
8765
|
}
|
|
8588
8766
|
/**
|
|
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
|
|
8767
|
+
* Run every currently available function and check node through the
|
|
8768
|
+
* configured NodeExecutor. Newly-unblocked function/check nodes are
|
|
8769
|
+
* drained in definition order before returning control to the runner's
|
|
8770
|
+
* agent-turn decision. Routers and gates are excluded — see
|
|
8771
|
+
* {@link drainRuntimeNodes} for the full drain.
|
|
8592
8772
|
*/
|
|
8593
8773
|
async executeAvailableFunctionNodes(handle) {
|
|
8594
8774
|
return this.drainAvailableRuntimeNodes(handle, false);
|
|
@@ -8604,7 +8784,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8604
8784
|
if (isSubFlowNode(candidate) && !advancedSubFlows.has(candidate.id) && (status === "available" || status === "running")) {
|
|
8605
8785
|
return true;
|
|
8606
8786
|
}
|
|
8607
|
-
return (isFunctionNode(candidate) || includeRoutersAndGates && isGateNode(candidate)) && status === "available";
|
|
8787
|
+
return (isFunctionNode(candidate) || isCheckNode(candidate) || isSubpromptNode(candidate) || includeRoutersAndGates && isGateNode(candidate)) && status === "available";
|
|
8608
8788
|
});
|
|
8609
8789
|
if (!node) break;
|
|
8610
8790
|
if (isGateNode(node)) {
|
|
@@ -8653,6 +8833,9 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8653
8833
|
);
|
|
8654
8834
|
}
|
|
8655
8835
|
const executionGeneration = state.executionGeneration;
|
|
8836
|
+
const timeoutSec = node.control?.timeoutSec;
|
|
8837
|
+
const runningExecution = state.execution.nodes[node.id];
|
|
8838
|
+
const remainingMs = runningExecution ? remainingActiveExecutionMs(runningExecution, timeoutSec, Date.now()) : void 0;
|
|
8656
8839
|
let result;
|
|
8657
8840
|
try {
|
|
8658
8841
|
const runner = state.inlineSubFlowRunner;
|
|
@@ -8665,7 +8848,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8665
8848
|
snapshotMap(state.execution),
|
|
8666
8849
|
state.flowInputOrigins
|
|
8667
8850
|
);
|
|
8668
|
-
|
|
8851
|
+
const runPromise = runner.run({
|
|
8669
8852
|
node,
|
|
8670
8853
|
flowId: node.run.flow,
|
|
8671
8854
|
inputs: resolved.values,
|
|
@@ -8682,50 +8865,210 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8682
8865
|
else state.inlineOperationTargets.delete(node.id);
|
|
8683
8866
|
}
|
|
8684
8867
|
});
|
|
8868
|
+
result = remainingMs !== void 0 ? await raceWithTimeout(
|
|
8869
|
+
runPromise,
|
|
8870
|
+
Math.max(0, remainingMs),
|
|
8871
|
+
`Sub-flow node ${node.id} timed out after ${timeoutSec}s`
|
|
8872
|
+
) : await runPromise;
|
|
8685
8873
|
} catch (error) {
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8874
|
+
if (error instanceof NodeTimeoutError) {
|
|
8875
|
+
const target = state.inlineOperationTargets.get(node.id);
|
|
8876
|
+
if (target) {
|
|
8877
|
+
state.inlineOperationTargets.delete(node.id);
|
|
8878
|
+
void target.cancel().catch(() => {
|
|
8879
|
+
});
|
|
8692
8880
|
}
|
|
8693
|
-
|
|
8881
|
+
result = {
|
|
8882
|
+
status: "failed",
|
|
8883
|
+
error: { message: error.message, recoverable: false, at: now() }
|
|
8884
|
+
};
|
|
8885
|
+
} else {
|
|
8886
|
+
result = {
|
|
8887
|
+
status: "failed",
|
|
8888
|
+
error: {
|
|
8889
|
+
message: `Sub-flow node ${node.id} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
8890
|
+
recoverable: false,
|
|
8891
|
+
at: now()
|
|
8892
|
+
}
|
|
8893
|
+
};
|
|
8894
|
+
}
|
|
8694
8895
|
}
|
|
8695
8896
|
if (this.s(handle).executionGeneration !== executionGeneration) break;
|
|
8696
8897
|
if (result.status === "paused") {
|
|
8697
8898
|
this.projectInlineInteraction(handle, node.id, result.projection);
|
|
8698
8899
|
continue;
|
|
8699
8900
|
}
|
|
8700
|
-
|
|
8901
|
+
let subFlowRetrying = false;
|
|
8701
8902
|
this.mutate(
|
|
8702
8903
|
handle,
|
|
8703
8904
|
(current) => {
|
|
8704
8905
|
const execution2 = current.execution.nodes[node.id];
|
|
8705
8906
|
assertLegal(execution2, `node not in execution: ${node.id}`);
|
|
8907
|
+
const completedAt = now();
|
|
8706
8908
|
if (result.status === "complete") {
|
|
8909
|
+
const outputHistory = appendAttempt(
|
|
8910
|
+
execution2.outputHistory,
|
|
8911
|
+
"complete",
|
|
8912
|
+
execution2.startedAt ?? completedAt,
|
|
8913
|
+
completedAt,
|
|
8914
|
+
result.output,
|
|
8915
|
+
void 0
|
|
8916
|
+
);
|
|
8707
8917
|
current.execution.nodes[node.id] = {
|
|
8708
8918
|
...execution2,
|
|
8709
8919
|
status: "complete",
|
|
8710
|
-
completedAt
|
|
8711
|
-
output: result.output
|
|
8920
|
+
completedAt,
|
|
8921
|
+
output: result.output,
|
|
8922
|
+
outputHistory
|
|
8712
8923
|
};
|
|
8713
8924
|
return;
|
|
8714
8925
|
}
|
|
8715
|
-
|
|
8926
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
8927
|
+
node,
|
|
8928
|
+
execution2,
|
|
8929
|
+
result.error.message,
|
|
8930
|
+
completedAt,
|
|
8931
|
+
result.output,
|
|
8932
|
+
result.error.at
|
|
8933
|
+
);
|
|
8934
|
+
subFlowRetrying = retry;
|
|
8935
|
+
current.execution.nodes[node.id] = retry ? { ...execution2, ...patch, status: "available", error: void 0 } : {
|
|
8716
8936
|
...execution2,
|
|
8937
|
+
...patch,
|
|
8717
8938
|
status: nodeIsOptional(node) ? "skipped" : "failed",
|
|
8718
|
-
completedAt
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8939
|
+
completedAt
|
|
8940
|
+
};
|
|
8941
|
+
},
|
|
8942
|
+
false
|
|
8943
|
+
);
|
|
8944
|
+
if (subFlowRetrying) advancedSubFlows.delete(node.id);
|
|
8945
|
+
continue;
|
|
8946
|
+
}
|
|
8947
|
+
if (isSubpromptNode(node)) {
|
|
8948
|
+
this.mutate(
|
|
8949
|
+
handle,
|
|
8950
|
+
(current) => {
|
|
8951
|
+
const execution = current.execution.nodes[node.id];
|
|
8952
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8953
|
+
assertLegal(
|
|
8954
|
+
execution.status === "available",
|
|
8955
|
+
`cannot execute subprompt node ${node.id}: status is ${execution.status}`
|
|
8956
|
+
);
|
|
8957
|
+
current.execution.nodes[node.id] = {
|
|
8958
|
+
...execution,
|
|
8959
|
+
status: "running",
|
|
8960
|
+
startedAt: now()
|
|
8722
8961
|
};
|
|
8723
8962
|
},
|
|
8724
8963
|
false
|
|
8725
8964
|
);
|
|
8965
|
+
const subpromptExecutionGeneration = state.executionGeneration;
|
|
8966
|
+
const subpromptTimeoutSec = node.control?.timeoutSec;
|
|
8967
|
+
const subpromptRunningExecution = state.execution.nodes[node.id];
|
|
8968
|
+
const subpromptRemainingMs = subpromptRunningExecution ? remainingActiveExecutionMs(subpromptRunningExecution, subpromptTimeoutSec, Date.now()) : void 0;
|
|
8969
|
+
let subpromptOutput;
|
|
8970
|
+
let subpromptOutcome;
|
|
8971
|
+
try {
|
|
8972
|
+
const runner = state.subpromptRunner;
|
|
8973
|
+
if (!runner) {
|
|
8974
|
+
throw new Error(`no SubpromptRunner configured for subprompt node ${node.id}`);
|
|
8975
|
+
}
|
|
8976
|
+
const resolved = resolveNodeBindings(
|
|
8977
|
+
state.flowDef,
|
|
8978
|
+
node,
|
|
8979
|
+
snapshotMap(state.execution),
|
|
8980
|
+
state.flowInputOrigins
|
|
8981
|
+
);
|
|
8982
|
+
const instruction = nodeInstruction(node);
|
|
8983
|
+
assertLegal(instruction, `subprompt node ${node.id} has no instruction`);
|
|
8984
|
+
const outputSchema = node.contract?.output?.fields;
|
|
8985
|
+
const prompt = renderSubpromptPrompt(
|
|
8986
|
+
interpolateNodeInstruction(instruction, resolved.values),
|
|
8987
|
+
outputSchema
|
|
8988
|
+
);
|
|
8989
|
+
const runPromise = runner.execute({
|
|
8990
|
+
node,
|
|
8991
|
+
prompt,
|
|
8992
|
+
inputs: resolved.values,
|
|
8993
|
+
inputOrigins: resolved.origins
|
|
8994
|
+
});
|
|
8995
|
+
const result = subpromptRemainingMs !== void 0 ? await raceWithTimeout(
|
|
8996
|
+
runPromise,
|
|
8997
|
+
Math.max(0, subpromptRemainingMs),
|
|
8998
|
+
`Subprompt node ${node.id} timed out after ${subpromptTimeoutSec}s`
|
|
8999
|
+
) : await runPromise;
|
|
9000
|
+
subpromptOutput = subpromptNodeOutput(node, result.text);
|
|
9001
|
+
const validation = validateConsumedNodeOutput(
|
|
9002
|
+
state.flowDef,
|
|
9003
|
+
node.id,
|
|
9004
|
+
subpromptOutput.fields
|
|
9005
|
+
);
|
|
9006
|
+
if (!validation.valid) {
|
|
9007
|
+
throw new Error(
|
|
9008
|
+
`output does not match its consumed schema: ${validation.issues.join("; ")}`
|
|
9009
|
+
);
|
|
9010
|
+
}
|
|
9011
|
+
subpromptOutcome = { kind: "success" };
|
|
9012
|
+
} catch (error) {
|
|
9013
|
+
subpromptOutcome = error instanceof NodeTimeoutError ? { kind: "failure", message: error.message } : {
|
|
9014
|
+
kind: "failure",
|
|
9015
|
+
message: `Subprompt node ${node.id} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
9016
|
+
};
|
|
9017
|
+
}
|
|
9018
|
+
if (this.s(handle).executionGeneration !== subpromptExecutionGeneration) break;
|
|
9019
|
+
if (subpromptOutcome.kind === "success") {
|
|
9020
|
+
this.mutate(
|
|
9021
|
+
handle,
|
|
9022
|
+
(current) => {
|
|
9023
|
+
const execution = current.execution.nodes[node.id];
|
|
9024
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
9025
|
+
const completedAt = now();
|
|
9026
|
+
const outputHistory = appendAttempt(
|
|
9027
|
+
execution.outputHistory,
|
|
9028
|
+
"complete",
|
|
9029
|
+
execution.startedAt ?? completedAt,
|
|
9030
|
+
completedAt,
|
|
9031
|
+
subpromptOutput,
|
|
9032
|
+
void 0
|
|
9033
|
+
);
|
|
9034
|
+
current.execution.nodes[node.id] = {
|
|
9035
|
+
...execution,
|
|
9036
|
+
status: "complete",
|
|
9037
|
+
completedAt,
|
|
9038
|
+
output: subpromptOutput,
|
|
9039
|
+
outputHistory
|
|
9040
|
+
};
|
|
9041
|
+
},
|
|
9042
|
+
false
|
|
9043
|
+
);
|
|
9044
|
+
} else {
|
|
9045
|
+
const failureMessage = subpromptOutcome.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
|
+
subpromptOutput
|
|
9058
|
+
);
|
|
9059
|
+
current.execution.nodes[node.id] = retry ? { ...execution, ...patch, status: "available", error: void 0 } : {
|
|
9060
|
+
...execution,
|
|
9061
|
+
...patch,
|
|
9062
|
+
status: nodeIsOptional(node) ? "skipped" : "failed",
|
|
9063
|
+
completedAt
|
|
9064
|
+
};
|
|
9065
|
+
},
|
|
9066
|
+
false
|
|
9067
|
+
);
|
|
9068
|
+
}
|
|
8726
9069
|
continue;
|
|
8727
9070
|
}
|
|
8728
|
-
if (!isFunctionNode(node)) continue;
|
|
9071
|
+
if (!isFunctionNode(node) && !isCheckNode(node)) continue;
|
|
8729
9072
|
this.mutate(
|
|
8730
9073
|
handle,
|
|
8731
9074
|
(current) => {
|
|
@@ -8733,7 +9076,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8733
9076
|
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8734
9077
|
assertLegal(
|
|
8735
9078
|
execution.status === "available",
|
|
8736
|
-
`cannot execute
|
|
9079
|
+
`cannot execute ${node.run.kind} node ${node.id}: status is ${execution.status}`
|
|
8737
9080
|
);
|
|
8738
9081
|
current.execution.nodes[node.id] = {
|
|
8739
9082
|
...execution,
|
|
@@ -8743,73 +9086,178 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8743
9086
|
},
|
|
8744
9087
|
false
|
|
8745
9088
|
);
|
|
9089
|
+
const deterministicExecutionGeneration = state.executionGeneration;
|
|
9090
|
+
const deterministicTimeoutSec = node.control?.timeoutSec;
|
|
9091
|
+
const deterministicRunningExecution = state.execution.nodes[node.id];
|
|
9092
|
+
const deterministicRemainingMs = deterministicRunningExecution ? remainingActiveExecutionMs(
|
|
9093
|
+
deterministicRunningExecution,
|
|
9094
|
+
deterministicTimeoutSec,
|
|
9095
|
+
Date.now()
|
|
9096
|
+
) : void 0;
|
|
9097
|
+
const deterministicKindLabel = deterministicNodeKindLabel(node.run.kind);
|
|
8746
9098
|
let capturedOutput;
|
|
9099
|
+
let checkEvidence;
|
|
9100
|
+
let deterministicOutcome;
|
|
8747
9101
|
try {
|
|
8748
9102
|
const executor = state.nodeExecutor;
|
|
8749
|
-
if (!executor)
|
|
9103
|
+
if (!executor)
|
|
9104
|
+
throw new Error(`no NodeExecutor configured for ${node.run.kind} node ${node.id}`);
|
|
8750
9105
|
const resolved = resolveNodeBindings(
|
|
8751
9106
|
state.flowDef,
|
|
8752
9107
|
node,
|
|
8753
9108
|
snapshotMap(state.execution),
|
|
8754
9109
|
state.flowInputOrigins
|
|
8755
9110
|
);
|
|
8756
|
-
const
|
|
9111
|
+
const runPromise = executor.execute({
|
|
8757
9112
|
node,
|
|
8758
9113
|
inputs: resolved.values,
|
|
8759
9114
|
inputOrigins: resolved.origins
|
|
8760
9115
|
});
|
|
8761
|
-
|
|
9116
|
+
const result = deterministicRemainingMs !== void 0 ? await raceWithTimeout(
|
|
9117
|
+
runPromise,
|
|
9118
|
+
Math.max(0, deterministicRemainingMs),
|
|
9119
|
+
`${deterministicKindLabel} node ${node.id} timed out after ${deterministicTimeoutSec}s`
|
|
9120
|
+
) : await runPromise;
|
|
8762
9121
|
const successfulStatuses = node.run.successExit ?? [0];
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
9122
|
+
const passed = successfulStatuses.includes(result.exitStatus);
|
|
9123
|
+
capturedOutput = deterministicNodeOutput(node, result, passed);
|
|
9124
|
+
const detail = passed ? "" : result.stderr.trim() || result.stdout.trim();
|
|
9125
|
+
const detailSuffix = detail ? `: ${detail}` : "";
|
|
9126
|
+
if (passed) {
|
|
9127
|
+
const validation = validateConsumedNodeOutput(
|
|
9128
|
+
state.flowDef,
|
|
9129
|
+
node.id,
|
|
9130
|
+
capturedOutput.fields
|
|
8767
9131
|
);
|
|
9132
|
+
if (!validation.valid) {
|
|
9133
|
+
throw new Error(
|
|
9134
|
+
`output does not match its consumed schema: ${validation.issues.join("; ")}`
|
|
9135
|
+
);
|
|
9136
|
+
}
|
|
8768
9137
|
}
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
9138
|
+
if (isCheckNode(node)) {
|
|
9139
|
+
checkEvidence = deriveCheckEvidence(
|
|
9140
|
+
resolved,
|
|
9141
|
+
{
|
|
9142
|
+
runtime: node.run.runtime ?? "shell",
|
|
9143
|
+
command: node.run.command,
|
|
9144
|
+
cwd: node.run.cwd,
|
|
9145
|
+
successExit: successfulStatuses,
|
|
9146
|
+
exitStatus: result.exitStatus,
|
|
9147
|
+
passed
|
|
9148
|
+
},
|
|
9149
|
+
now()
|
|
9150
|
+
);
|
|
9151
|
+
deterministicOutcome = passed ? { kind: "success" } : {
|
|
9152
|
+
kind: "check-fail",
|
|
9153
|
+
message: `Check node ${node.id} failed with exit status ${result.exitStatus}${detailSuffix}`,
|
|
9154
|
+
onFail: node.run.onFail
|
|
9155
|
+
};
|
|
9156
|
+
} else if (!passed) {
|
|
8775
9157
|
throw new Error(
|
|
8776
|
-
`
|
|
9158
|
+
`Function node ${node.id} exited with unsuccessful exit status ${result.exitStatus}${detailSuffix}`
|
|
8777
9159
|
);
|
|
9160
|
+
} else {
|
|
9161
|
+
deterministicOutcome = { kind: "success" };
|
|
8778
9162
|
}
|
|
9163
|
+
} catch (error) {
|
|
9164
|
+
deterministicOutcome = error instanceof NodeTimeoutError ? { kind: "failure", message: error.message } : {
|
|
9165
|
+
kind: "failure",
|
|
9166
|
+
message: `${deterministicKindLabel} node ${node.id} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
9167
|
+
};
|
|
9168
|
+
}
|
|
9169
|
+
if (this.s(handle).executionGeneration !== deterministicExecutionGeneration) break;
|
|
9170
|
+
if (deterministicOutcome.kind === "success") {
|
|
8779
9171
|
this.mutate(
|
|
8780
9172
|
handle,
|
|
8781
9173
|
(current) => {
|
|
8782
9174
|
const execution = current.execution.nodes[node.id];
|
|
8783
9175
|
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
9176
|
+
const completedAt = now();
|
|
9177
|
+
const outputHistory = appendAttempt(
|
|
9178
|
+
execution.outputHistory,
|
|
9179
|
+
"complete",
|
|
9180
|
+
execution.startedAt ?? completedAt,
|
|
9181
|
+
completedAt,
|
|
9182
|
+
capturedOutput,
|
|
9183
|
+
void 0,
|
|
9184
|
+
{ check: checkEvidence }
|
|
9185
|
+
);
|
|
8784
9186
|
current.execution.nodes[node.id] = {
|
|
8785
9187
|
...execution,
|
|
8786
9188
|
status: "complete",
|
|
8787
|
-
completedAt
|
|
8788
|
-
output: capturedOutput
|
|
9189
|
+
completedAt,
|
|
9190
|
+
output: capturedOutput,
|
|
9191
|
+
outputHistory
|
|
8789
9192
|
};
|
|
8790
9193
|
},
|
|
8791
9194
|
false
|
|
8792
9195
|
);
|
|
8793
|
-
}
|
|
8794
|
-
const message
|
|
9196
|
+
} else if (deterministicOutcome.kind === "check-fail") {
|
|
9197
|
+
const { message: failureMessage, onFail } = deterministicOutcome;
|
|
8795
9198
|
this.mutate(
|
|
8796
9199
|
handle,
|
|
8797
9200
|
(current) => {
|
|
8798
9201
|
const execution = current.execution.nodes[node.id];
|
|
8799
9202
|
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
9203
|
+
assertLegal(checkEvidence, `check node ${node.id} completed without evidence`);
|
|
9204
|
+
const completedAt = now();
|
|
9205
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
9206
|
+
node,
|
|
9207
|
+
execution,
|
|
9208
|
+
failureMessage,
|
|
9209
|
+
completedAt,
|
|
9210
|
+
capturedOutput,
|
|
9211
|
+
void 0,
|
|
9212
|
+
checkEvidence
|
|
9213
|
+
);
|
|
9214
|
+
if (retry) {
|
|
9215
|
+
current.execution.nodes[node.id] = {
|
|
9216
|
+
...execution,
|
|
9217
|
+
...patch,
|
|
9218
|
+
status: "available",
|
|
9219
|
+
error: void 0
|
|
9220
|
+
};
|
|
9221
|
+
} else if (onFail === "fail") {
|
|
9222
|
+
current.execution.nodes[node.id] = {
|
|
9223
|
+
...execution,
|
|
9224
|
+
...patch,
|
|
9225
|
+
status: "failed",
|
|
9226
|
+
completedAt
|
|
9227
|
+
};
|
|
9228
|
+
} else {
|
|
9229
|
+
const provenanceNote = checkEvidence.provenance ? ` (${checkEvidence.provenance})` : "";
|
|
9230
|
+
const approval = {
|
|
9231
|
+
requestedAt: now(),
|
|
9232
|
+
summary: `Check node ${node.id} failed with exit status ${checkEvidence.exitStatus}${provenanceNote}.`
|
|
9233
|
+
};
|
|
9234
|
+
current.execution.nodes[node.id] = {
|
|
9235
|
+
...execution,
|
|
9236
|
+
...patch,
|
|
9237
|
+
status: "awaiting_approval",
|
|
9238
|
+
approval,
|
|
9239
|
+
approvalHistory: [...execution.approvalHistory, approval]
|
|
9240
|
+
};
|
|
9241
|
+
}
|
|
9242
|
+
},
|
|
9243
|
+
false
|
|
9244
|
+
);
|
|
9245
|
+
} else {
|
|
9246
|
+
const failureMessage = deterministicOutcome.message;
|
|
9247
|
+
this.mutate(
|
|
9248
|
+
handle,
|
|
9249
|
+
(current) => {
|
|
9250
|
+
const execution = current.execution.nodes[node.id];
|
|
9251
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
9252
|
+
const completedAt = now();
|
|
9253
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
9254
|
+
node,
|
|
9255
|
+
execution,
|
|
9256
|
+
failureMessage,
|
|
9257
|
+
completedAt,
|
|
9258
|
+
capturedOutput
|
|
9259
|
+
);
|
|
9260
|
+
current.execution.nodes[node.id] = retry ? { ...execution, ...patch, status: "available", error: void 0 } : { ...execution, ...patch, status: "failed", completedAt };
|
|
8813
9261
|
},
|
|
8814
9262
|
false
|
|
8815
9263
|
);
|
|
@@ -8955,6 +9403,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8955
9403
|
s.lastSignaledExecution = s.execution;
|
|
8956
9404
|
}
|
|
8957
9405
|
}
|
|
9406
|
+
this.rearmWakeTimer(handle);
|
|
8958
9407
|
return typeof result === "string" ? result : JSON.stringify({ ok: true });
|
|
8959
9408
|
}
|
|
8960
9409
|
/**
|
|
@@ -9000,6 +9449,181 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
9000
9449
|
done: computed.done && flowStatus === "complete"
|
|
9001
9450
|
};
|
|
9002
9451
|
}
|
|
9452
|
+
// ── Agent-node deadline wake timer ────────────────────────────────────────
|
|
9453
|
+
//
|
|
9454
|
+
// Agent nodes have no `await` the adapter can wrap — it marks the node
|
|
9455
|
+
// started and waits for the model to call `complete_node`, which may never
|
|
9456
|
+
// come. The derived deadline (see `engine/node-deadline.ts`) is the source
|
|
9457
|
+
// of truth: it is a pure function of `startedAt` + `approvalHistory` +
|
|
9458
|
+
// `inputHistory`, so it is correct on its own after `hydrate()` or a
|
|
9459
|
+
// reconnect with no timer required. This wake timer exists only so a
|
|
9460
|
+
// genuinely hung run — by definition nothing else happening — still gets
|
|
9461
|
+
// *some* event that triggers a re-evaluation of that deadline.
|
|
9462
|
+
clearWakeTimer(s) {
|
|
9463
|
+
if (s.wakeTimer !== null) {
|
|
9464
|
+
clearTimeout(s.wakeTimer);
|
|
9465
|
+
s.wakeTimer = null;
|
|
9466
|
+
}
|
|
9467
|
+
}
|
|
9468
|
+
/**
|
|
9469
|
+
* Whether `execution` counts as "actively running" for `control.timeoutSec`
|
|
9470
|
+
* evaluation. Ordinarily this is just `status === "running"`. The one
|
|
9471
|
+
* exception: a non-gate agent node whose approval was just decided
|
|
9472
|
+
* `approved` stays at `awaiting_approval` — `applyApproval` deliberately
|
|
9473
|
+
* does not restore `running` there, because `doCompleteNode`'s
|
|
9474
|
+
* `approvedPrior` check requires exactly that status (a mandatory-approval
|
|
9475
|
+
* node has no `runningAutonomous` fallback). Without this second clause the
|
|
9476
|
+
* timeout would pause for the approval wait (correctly, via
|
|
9477
|
+
* `remainingActiveExecutionMs` reading the now-resolved `approvalHistory`
|
|
9478
|
+
* entry) and then never resume, since nothing re-evaluates a node that
|
|
9479
|
+
* never looks "running" again.
|
|
9480
|
+
*/
|
|
9481
|
+
isDeadlineEligible(execution) {
|
|
9482
|
+
if (execution.status === "running") return true;
|
|
9483
|
+
return execution.status === "awaiting_approval" && execution.approval?.decision === "approved";
|
|
9484
|
+
}
|
|
9485
|
+
/**
|
|
9486
|
+
* Recomputes the pending wake timer against the current state: cancels any
|
|
9487
|
+
* previously-armed timer, then arms a fresh one for the earliest deadline
|
|
9488
|
+
* among currently `running` agent nodes with a `control.timeoutSec`. A
|
|
9489
|
+
* no-op when nothing is running against a timeout, or the flow is already
|
|
9490
|
+
* terminal. Called after every {@link mutate}, plus `connect()` and
|
|
9491
|
+
* `hydrate()` (which bypass `mutate()`).
|
|
9492
|
+
*
|
|
9493
|
+
* `disconnect()` clears the timer and bumps `executionGeneration`, but a
|
|
9494
|
+
* `mutate()` already in flight (or one that lands after teardown for any
|
|
9495
|
+
* other reason) still calls this on its way out — bail here on
|
|
9496
|
+
* `handle.status` so a post-disconnect mutation can never re-arm a timer
|
|
9497
|
+
* that would fire against a handle nothing is watching anymore.
|
|
9498
|
+
*/
|
|
9499
|
+
rearmWakeTimer(handle) {
|
|
9500
|
+
const s = this.s(handle);
|
|
9501
|
+
this.clearWakeTimer(s);
|
|
9502
|
+
if (handle.status === "disconnected") return;
|
|
9503
|
+
if (s.execution.done || s.execution.status === "failed" || s.execution.status === "cancelled") {
|
|
9504
|
+
return;
|
|
9505
|
+
}
|
|
9506
|
+
const nowMs = Date.now();
|
|
9507
|
+
let earliestRemainingMs;
|
|
9508
|
+
for (const node of s.flowDef.nodes) {
|
|
9509
|
+
if (!isAgentNode(node)) continue;
|
|
9510
|
+
const timeoutSec = node.control?.timeoutSec;
|
|
9511
|
+
if (timeoutSec === void 0) continue;
|
|
9512
|
+
const execution = s.execution.nodes[node.id];
|
|
9513
|
+
if (!execution || !this.isDeadlineEligible(execution)) continue;
|
|
9514
|
+
const remaining = remainingActiveExecutionMs(execution, timeoutSec, nowMs);
|
|
9515
|
+
if (remaining === void 0) continue;
|
|
9516
|
+
if (earliestRemainingMs === void 0 || remaining < earliestRemainingMs) {
|
|
9517
|
+
earliestRemainingMs = remaining;
|
|
9518
|
+
}
|
|
9519
|
+
}
|
|
9520
|
+
if (earliestRemainingMs === void 0) return;
|
|
9521
|
+
const delayMs = Math.max(0, earliestRemainingMs);
|
|
9522
|
+
const timer = setTimeout(() => {
|
|
9523
|
+
s.wakeTimer = null;
|
|
9524
|
+
void this.evaluateAgentNodeDeadlines(handle);
|
|
9525
|
+
}, delayMs);
|
|
9526
|
+
timer.unref?.();
|
|
9527
|
+
s.wakeTimer = timer;
|
|
9528
|
+
}
|
|
9529
|
+
/**
|
|
9530
|
+
* Re-checks every `running` agent node's derived deadline and, for any that
|
|
9531
|
+
* have expired, either auto-retries (bounded by `control.retries`, composing
|
|
9532
|
+
* with the existing `doFailNode(recoverable: true)` and `retryNode`
|
|
9533
|
+
* pathways) or terminally fails the node. Safe to call at any time — the
|
|
9534
|
+
* derived deadline means this is a no-op unless something has actually
|
|
9535
|
+
* expired. Goes through {@link mutate}, so a state change here republishes
|
|
9536
|
+
* `onChange`/stimulus exactly like any other mutation, waking a fresh turn
|
|
9537
|
+
* when the node retries or the flow rolls up to `failed`.
|
|
9538
|
+
*/
|
|
9539
|
+
async evaluateAgentNodeDeadlines(handle) {
|
|
9540
|
+
const s = this.s(handle);
|
|
9541
|
+
const nowMs = Date.now();
|
|
9542
|
+
const expired = s.flowDef.nodes.filter((node) => {
|
|
9543
|
+
if (!isAgentNode(node)) return false;
|
|
9544
|
+
const timeoutSec = node.control?.timeoutSec;
|
|
9545
|
+
if (timeoutSec === void 0) return false;
|
|
9546
|
+
const execution = s.execution.nodes[node.id];
|
|
9547
|
+
if (!execution || !this.isDeadlineEligible(execution)) return false;
|
|
9548
|
+
const remaining = remainingActiveExecutionMs(execution, timeoutSec, nowMs);
|
|
9549
|
+
return remaining !== void 0 && remaining <= 0;
|
|
9550
|
+
});
|
|
9551
|
+
for (const node of expired) {
|
|
9552
|
+
this.mutate(
|
|
9553
|
+
handle,
|
|
9554
|
+
(current) => {
|
|
9555
|
+
const execution = current.execution.nodes[node.id];
|
|
9556
|
+
if (!execution || !this.isDeadlineEligible(execution)) return;
|
|
9557
|
+
const completedAt = now();
|
|
9558
|
+
const message = `Agent node ${node.id} timed out after ${node.control?.timeoutSec}s`;
|
|
9559
|
+
const { retry, patch } = this.planAutomaticRetry(
|
|
9560
|
+
node,
|
|
9561
|
+
execution,
|
|
9562
|
+
message,
|
|
9563
|
+
completedAt,
|
|
9564
|
+
execution.output
|
|
9565
|
+
);
|
|
9566
|
+
current.execution.nodes[node.id] = retry ? { ...execution, ...patch, status: "available", error: void 0 } : { ...execution, ...patch, status: "failed", completedAt };
|
|
9567
|
+
},
|
|
9568
|
+
true
|
|
9569
|
+
);
|
|
9570
|
+
}
|
|
9571
|
+
}
|
|
9572
|
+
/**
|
|
9573
|
+
* Records one failed attempt on `nodeId`'s execution and decides whether
|
|
9574
|
+
* the automatic bounded retry (`control.retries`) still has budget. Shared
|
|
9575
|
+
* by every automatic-failure seam (agent-node timeout, function/check
|
|
9576
|
+
* execution/timeout failure, sub-flow execution/timeout failure, and a
|
|
9577
|
+
* check's fail verdict) so the budget — derived from prior `'failed'`
|
|
9578
|
+
* entries in `outputHistory`, not a separate durable counter — is computed
|
|
9579
|
+
* once, the same way, everywhere.
|
|
9580
|
+
*
|
|
9581
|
+
* Returns the `NodeExecution` patch (error/errorHistory/outputHistory/
|
|
9582
|
+
* output) plus whether budget remains; the caller decides the resulting
|
|
9583
|
+
* `status` because that differs by kind (e.g. sub-flow degrades an
|
|
9584
|
+
* optional node's exhausted failure to `skipped`; agent/function do not,
|
|
9585
|
+
* preserving their existing behavior). A terminal failure whose node has
|
|
9586
|
+
* any `control.retries` configured carries `recoverable: true` so the
|
|
9587
|
+
* manual `retryNode` operator override — which sits outside this budget
|
|
9588
|
+
* entirely — remains usable once the automatic budget runs out.
|
|
9589
|
+
*
|
|
9590
|
+
* @param sourceErrorAtIso - The failure's own timestamp, when the caller has
|
|
9591
|
+
* one more precise than `completedAtIso` (e.g. a sub-flow child reports
|
|
9592
|
+
* its own `error.at`, which can predate this node's parent-side
|
|
9593
|
+
* `completedAt` by however long the result took to propagate). Defaults
|
|
9594
|
+
* to `completedAtIso` for callers (agent-timeout, function-node) whose
|
|
9595
|
+
* failure message is synthesized at the moment of detection.
|
|
9596
|
+
* @param checkEvidence - Derived evidence for a check's fail verdict, so it
|
|
9597
|
+
* rides on the same appended attempt as the failure it explains.
|
|
9598
|
+
*/
|
|
9599
|
+
planAutomaticRetry(node, execution, message, completedAtIso, capturedOutput, sourceErrorAtIso, checkEvidence) {
|
|
9600
|
+
const retries = node.control?.retries;
|
|
9601
|
+
const priorFailures = countFailedAttempts(execution.outputHistory);
|
|
9602
|
+
const retry = hasAutomaticRetryBudget(priorFailures, retries);
|
|
9603
|
+
const error = {
|
|
9604
|
+
message,
|
|
9605
|
+
recoverable: !retry && !!retries,
|
|
9606
|
+
at: sourceErrorAtIso ?? completedAtIso
|
|
9607
|
+
};
|
|
9608
|
+
const outputHistory = appendAttempt(
|
|
9609
|
+
execution.outputHistory,
|
|
9610
|
+
"failed",
|
|
9611
|
+
execution.startedAt ?? completedAtIso,
|
|
9612
|
+
completedAtIso,
|
|
9613
|
+
capturedOutput,
|
|
9614
|
+
error,
|
|
9615
|
+
{ check: checkEvidence }
|
|
9616
|
+
);
|
|
9617
|
+
return {
|
|
9618
|
+
retry,
|
|
9619
|
+
patch: {
|
|
9620
|
+
error,
|
|
9621
|
+
errorHistory: [...execution.errorHistory, error],
|
|
9622
|
+
outputHistory,
|
|
9623
|
+
output: capturedOutput ?? execution.output
|
|
9624
|
+
}
|
|
9625
|
+
};
|
|
9626
|
+
}
|
|
9003
9627
|
/**
|
|
9004
9628
|
* Build the selected agent node's complete turn briefing plus upstream context.
|
|
9005
9629
|
*/
|
|
@@ -9087,7 +9711,8 @@ function routerSelectionHandoffSummary(router, execution) {
|
|
|
9087
9711
|
if (!selection) return void 0;
|
|
9088
9712
|
return selection.selectedTarget === null ? "Router completed without selecting a target." : `Router selected ${selection.selectedTarget}.`;
|
|
9089
9713
|
}
|
|
9090
|
-
function
|
|
9714
|
+
function deterministicNodeOutput(node, result, passed) {
|
|
9715
|
+
const kindLabel = deterministicNodeKindLabel(node.run.kind);
|
|
9091
9716
|
const capturedFields = [];
|
|
9092
9717
|
for (const channel of ["stdout", "stderr"]) {
|
|
9093
9718
|
const destination = node.run.capture?.[channel];
|
|
@@ -9095,18 +9720,39 @@ function functionNodeOutput(node, result) {
|
|
|
9095
9720
|
const match = /^fields\.([^.]+)$/.exec(destination);
|
|
9096
9721
|
if (!match?.[1]) {
|
|
9097
9722
|
throw new Error(
|
|
9098
|
-
|
|
9723
|
+
`${kindLabel} node ${node.id} run.capture.${channel} destination "${destination}" is invalid; expected fields.<name>`
|
|
9099
9724
|
);
|
|
9100
9725
|
}
|
|
9101
9726
|
capturedFields.push([match[1], result[channel]]);
|
|
9102
9727
|
}
|
|
9103
9728
|
const fields = Object.fromEntries(capturedFields);
|
|
9729
|
+
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
9730
|
return {
|
|
9105
|
-
summary
|
|
9731
|
+
summary,
|
|
9106
9732
|
artifacts: [],
|
|
9107
9733
|
...Object.keys(fields).length > 0 ? { fields } : {}
|
|
9108
9734
|
};
|
|
9109
9735
|
}
|
|
9736
|
+
function extractSubpromptJsonPayload(text) {
|
|
9737
|
+
const trimmed = text.trim();
|
|
9738
|
+
const fenceMatch = /^```(?:json)?\s*([\s\S]*?)\s*```$/.exec(trimmed);
|
|
9739
|
+
return fenceMatch?.[1] ?? trimmed;
|
|
9740
|
+
}
|
|
9741
|
+
function subpromptNodeOutput(node, text) {
|
|
9742
|
+
const outputSchema = node.contract?.output?.fields;
|
|
9743
|
+
if (!outputSchema) return { summary: text.trim(), artifacts: [] };
|
|
9744
|
+
const parsed = safeParseFields(extractSubpromptJsonPayload(text));
|
|
9745
|
+
if (!parsed.fields) {
|
|
9746
|
+
throw new Error(
|
|
9747
|
+
`Subprompt node ${node.id} response is not valid JSON: ${parsed.issues.join("; ")}`
|
|
9748
|
+
);
|
|
9749
|
+
}
|
|
9750
|
+
return {
|
|
9751
|
+
summary: text.trim(),
|
|
9752
|
+
artifacts: [],
|
|
9753
|
+
fields: parsed.fields
|
|
9754
|
+
};
|
|
9755
|
+
}
|
|
9110
9756
|
function safeParseStringArray(value) {
|
|
9111
9757
|
if (!value) return [];
|
|
9112
9758
|
if (Array.isArray(value)) return value.map(String);
|
|
@@ -9175,6 +9821,6 @@ function createConnector() {
|
|
|
9175
9821
|
return new FlowAdapter();
|
|
9176
9822
|
}
|
|
9177
9823
|
|
|
9178
|
-
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-
|
|
9824
|
+
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, SessionSubpromptRunner, 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 };
|
|
9825
|
+
//# sourceMappingURL=chunk-ZO7KL7GN.js.map
|
|
9826
|
+
//# sourceMappingURL=chunk-ZO7KL7GN.js.map
|