@skaile/workspaces 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/README.md +32 -13
- package/dist/app-sdk/index.js +1 -1
- package/dist/{asset-feeds-DKNYRLED.js → asset-feeds-4AWAT5PW.js} +6 -6
- package/dist/{asset-feeds-DKNYRLED.js.map → asset-feeds-4AWAT5PW.js.map} +1 -1
- package/dist/asset-manager/catalog-deployer.js +1 -1
- package/dist/asset-manager/index.js +5 -5
- package/dist/asset-manager/installer.js +2 -2
- package/dist/bridge/drivers/claude-sdk.js +108 -5
- package/dist/bridge/drivers/claude-sdk.js.map +1 -1
- package/dist/bridge/drivers/codex.js +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +1 -1
- package/dist/bridge/drivers/gemini.js +3 -3
- 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 +3 -3
- package/dist/bridge/index.js +2 -2
- package/dist/bridge/src/drivers/claude-sdk.d.ts +33 -0
- package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
- package/dist/{chunk-CERGXDGG.js → chunk-2OCYS5EP.js} +3 -3
- package/dist/{chunk-CERGXDGG.js.map → chunk-2OCYS5EP.js.map} +1 -1
- package/dist/{chunk-W3Q37HEF.js → chunk-3HVASHBF.js} +3 -3
- package/dist/{chunk-W3Q37HEF.js.map → chunk-3HVASHBF.js.map} +1 -1
- package/dist/{chunk-UQ3RJKDD.js → chunk-4O2RAXKJ.js} +179 -52
- package/dist/chunk-4O2RAXKJ.js.map +1 -0
- package/dist/{chunk-DZM33LQO.js → chunk-4QROZZ5Z.js} +15 -3
- package/dist/chunk-4QROZZ5Z.js.map +1 -0
- package/dist/{chunk-G2ILW47R.js → chunk-57RFYEVM.js} +3 -3
- package/dist/{chunk-G2ILW47R.js.map → chunk-57RFYEVM.js.map} +1 -1
- package/dist/{chunk-BLWNI5KW.js → chunk-AKVAZ5CB.js} +2 -2
- package/dist/{chunk-BLWNI5KW.js.map → chunk-AKVAZ5CB.js.map} +1 -1
- package/dist/{chunk-BFIK6NPZ.js → chunk-AM5LFRXJ.js} +3 -3
- package/dist/{chunk-BFIK6NPZ.js.map → chunk-AM5LFRXJ.js.map} +1 -1
- package/dist/{chunk-RDE43ORT.js → chunk-D4VHBAJC.js} +4 -4
- package/dist/{chunk-RDE43ORT.js.map → chunk-D4VHBAJC.js.map} +1 -1
- package/dist/{chunk-PTS3QN5P.js → chunk-EEHOMCRU.js} +1021 -115
- package/dist/chunk-EEHOMCRU.js.map +1 -0
- package/dist/{chunk-CXX3UGJP.js → chunk-ENBTXMPL.js} +7 -7
- package/dist/{chunk-CXX3UGJP.js.map → chunk-ENBTXMPL.js.map} +1 -1
- package/dist/{chunk-LD6YGIKZ.js → chunk-EUWN4EK4.js} +60 -21
- package/dist/chunk-EUWN4EK4.js.map +1 -0
- package/dist/{chunk-FB4GPWYC.js → chunk-FKBQRV4J.js} +57 -16
- package/dist/chunk-FKBQRV4J.js.map +1 -0
- package/dist/{chunk-WG3L5VHF.js → chunk-HY3MBSNC.js} +2 -2
- package/dist/{chunk-WG3L5VHF.js.map → chunk-HY3MBSNC.js.map} +1 -1
- package/dist/{chunk-7IRH7MGV.js → chunk-IHX2SN35.js} +9 -9
- package/dist/{chunk-7IRH7MGV.js.map → chunk-IHX2SN35.js.map} +1 -1
- package/dist/{chunk-3F54MH2T.js → chunk-JAFEMG7W.js} +4 -4
- package/dist/{chunk-3F54MH2T.js.map → chunk-JAFEMG7W.js.map} +1 -1
- package/dist/{chunk-X6BPVUV2.js → chunk-L3TS7ZO4.js} +3 -3
- package/dist/{chunk-X6BPVUV2.js.map → chunk-L3TS7ZO4.js.map} +1 -1
- package/dist/{chunk-RIEXN7WA.js → chunk-MADO7PH5.js} +2 -2
- package/dist/{chunk-RIEXN7WA.js.map → chunk-MADO7PH5.js.map} +1 -1
- package/dist/{chunk-BCTU4TN7.js → chunk-MWFYXUFK.js} +3 -3
- package/dist/{chunk-BCTU4TN7.js.map → chunk-MWFYXUFK.js.map} +1 -1
- package/dist/{chunk-TK66CMXV.js → chunk-NU4E7C5I.js} +7 -3
- package/dist/chunk-NU4E7C5I.js.map +1 -0
- package/dist/{chunk-ZE2KEPE2.js → chunk-OEGHKO5N.js} +258 -24
- package/dist/chunk-OEGHKO5N.js.map +1 -0
- package/dist/{chunk-63ZPKREA.js → chunk-P43HBWKK.js} +11 -4
- package/dist/chunk-P43HBWKK.js.map +1 -0
- package/dist/{chunk-7UZGKVSK.js → chunk-PJ6INYDD.js} +3 -3
- package/dist/{chunk-7UZGKVSK.js.map → chunk-PJ6INYDD.js.map} +1 -1
- package/dist/{chunk-CA5CWLGN.js → chunk-PKEAYYEJ.js} +7 -4
- package/dist/chunk-PKEAYYEJ.js.map +1 -0
- package/dist/{chunk-ETFETLLB.js → chunk-UZYTSIX6.js} +6 -6
- package/dist/{chunk-ETFETLLB.js.map → chunk-UZYTSIX6.js.map} +1 -1
- package/dist/{chunk-SIF5F5SN.js → chunk-V4ZLZJFN.js} +3 -3
- package/dist/{chunk-SIF5F5SN.js.map → chunk-V4ZLZJFN.js.map} +1 -1
- package/dist/{chunk-ZR5Q4KZK.js → chunk-VNIKF66R.js} +3 -3
- package/dist/{chunk-ZR5Q4KZK.js.map → chunk-VNIKF66R.js.map} +1 -1
- package/dist/{chunk-CILACPUC.js → chunk-WSARNOPP.js} +5 -5
- package/dist/{chunk-CILACPUC.js.map → chunk-WSARNOPP.js.map} +1 -1
- package/dist/{chunk-TAJBFYCT.js → chunk-YWPEDILM.js} +83 -36
- package/dist/chunk-YWPEDILM.js.map +1 -0
- package/dist/{chunk-UNP4M5ZZ.js → chunk-ZR2X4SIG.js} +4 -4
- package/dist/{chunk-UNP4M5ZZ.js.map → chunk-ZR2X4SIG.js.map} +1 -1
- package/dist/cli/index.js +50 -50
- package/dist/connectors/config.js +1 -1
- package/dist/connectors/index.js +6 -6
- package/dist/core/index.js +1 -1
- package/dist/core/src/subprocess.d.ts +5 -1
- package/dist/core/src/subprocess.d.ts.map +1 -1
- package/dist/deploy/index.js +1 -1
- package/dist/discovery/index.js +1 -1
- package/dist/{ensure-sources-R6KW7K3M.js → ensure-sources-QF72HVUM.js} +8 -8
- package/dist/{ensure-sources-R6KW7K3M.js.map → ensure-sources-QF72HVUM.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +6 -6
- package/dist/factory-assets/connectors/deploy.js +6 -6
- package/dist/factory-assets/connectors/devserver.js +6 -6
- package/dist/factory-assets/connectors/flow/CONNECTOR.md +61 -14
- package/dist/factory-assets/connectors/flow/adapter.d.ts +78 -34
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +6 -6
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts +55 -0
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/flow.v2.schema.json +61 -134
- package/dist/factory-assets/connectors/flow/contract/index.d.ts +1 -1
- package/dist/factory-assets/connectors/flow/contract/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/normalize.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts +14 -2
- 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 +9 -0
- package/dist/factory-assets/connectors/flow/engine/bindings.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts +11 -6
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/flow-kind-provider.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/index.d.ts +2 -0
- package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts +22 -0
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts +18 -0
- package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/engine/types.d.ts +21 -15
- package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +4 -4
- package/dist/factory-assets/connectors/flow/index.d.ts +3 -0
- package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts +48 -0
- package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/node-executor.d.ts +45 -0
- package/dist/factory-assets/connectors/flow/node-executor.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts +3 -2
- 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 +10 -9
- package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/run-flow.js +7 -7
- package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts +73 -0
- package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts.map +1 -0
- package/dist/factory-assets/connectors/flow.js +6 -6
- package/dist/factory-assets/connectors/git.js +6 -6
- package/dist/factory-assets/connectors/gmail.js +6 -6
- package/dist/factory-assets/connectors/googledrive.js +6 -6
- package/dist/factory-assets/connectors/local.js +6 -6
- package/dist/factory-assets/connectors/mattermost.js +6 -6
- package/dist/factory-assets/connectors/memory.js +6 -6
- package/dist/factory-assets/connectors/minio.js +6 -6
- package/dist/factory-assets/connectors/postgres.js +6 -6
- package/dist/factory-assets/connectors/s3.js +6 -6
- package/dist/factory-assets/connectors/sharepoint.js +6 -6
- package/dist/factory-assets/connectors/sqlite.js +6 -6
- package/dist/factory-assets/connectors/static-server.js +6 -6
- package/dist/factory-assets/connectors/tunnel.js +6 -6
- package/dist/factory-assets/connectors/webdav.js +6 -6
- package/dist/factory-assets/connectors/xstate-store.js +6 -6
- package/dist/factory-assets/connectors/xstate.js +6 -6
- package/dist/{flows-F3CJNMK6.js → flows-YIW4HL3L.js} +4 -4
- package/dist/{flows-F3CJNMK6.js.map → flows-YIW4HL3L.js.map} +1 -1
- package/dist/{helpers-KPOLCGKX.js → helpers-LURCZHA4.js} +8 -8
- package/dist/{helpers-KPOLCGKX.js.map → helpers-LURCZHA4.js.map} +1 -1
- package/dist/library/index.js +3 -3
- package/dist/library/install.js +1 -1
- package/dist/{library-publish-ZAPZS64O.js → library-publish-2GMJM3NZ.js} +14 -14
- package/dist/{library-publish-ZAPZS64O.js.map → library-publish-2GMJM3NZ.js.map} +1 -1
- package/dist/{open-library-I4F2UNOY.js → open-library-POFOXZWH.js} +12 -12
- package/dist/{open-library-I4F2UNOY.js.map → open-library-POFOXZWH.js.map} +1 -1
- package/dist/{paths-KBACHM5B.js → paths-ZLX4ITAQ.js} +6 -6
- package/dist/{paths-KBACHM5B.js.map → paths-ZLX4ITAQ.js.map} +1 -1
- package/dist/{plugin-store-CYSNVZIX.js → plugin-store-YIMNH2FH.js} +3 -3
- package/dist/{plugin-store-CYSNVZIX.js.map → plugin-store-YIMNH2FH.js.map} +1 -1
- package/dist/{project-SIHZBTGB.js → project-GVOPZEJZ.js} +9 -9
- package/dist/{project-SIHZBTGB.js.map → project-GVOPZEJZ.js.map} +1 -1
- package/dist/resolver/index.js +1 -1
- package/dist/runner/index.js +15 -12
- package/dist/runner/src/serve.d.ts +3 -1
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +5 -5
- package/dist/sdk/bridge.js +2 -2
- package/dist/sdk/core.js +1 -1
- package/dist/sdk/flow.js +4 -4
- package/dist/sdk/index.js +15 -12
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/resolver.js +1 -1
- package/dist/sdk/runner.js +15 -12
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/session/src/dispatcher.d.ts.map +1 -1
- package/dist/{setup-KMBXMV25.js → setup-RHWARBBB.js} +5 -5
- package/dist/{setup-KMBXMV25.js.map → setup-RHWARBBB.js.map} +1 -1
- package/dist/{skills-LHS4TOKK.js → skills-HGVH6UTA.js} +5 -5
- package/dist/{skills-LHS4TOKK.js.map → skills-HGVH6UTA.js.map} +1 -1
- package/dist/{source-X226ZJPR.js → source-FB3T4IHP.js} +13 -13
- package/dist/{source-X226ZJPR.js.map → source-FB3T4IHP.js.map} +1 -1
- package/dist/{source-manifest-publish-N2QVUR5U.js → source-manifest-publish-XTRDVPSC.js} +15 -15
- package/dist/{source-manifest-publish-N2QVUR5U.js.map → source-manifest-publish-XTRDVPSC.js.map} +1 -1
- package/dist/tui/index.js +15 -12
- package/dist/tui/index.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/src/events.d.ts +20 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/flow.d.ts +26 -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/types/src/protocol.d.ts +1 -0
- package/dist/types/src/protocol.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +12 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +4 -4
- package/dist/workspace-plugin/adapters/omp.js +7 -7
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-63ZPKREA.js.map +0 -1
- package/dist/chunk-CA5CWLGN.js.map +0 -1
- package/dist/chunk-DZM33LQO.js.map +0 -1
- package/dist/chunk-FB4GPWYC.js.map +0 -1
- package/dist/chunk-LD6YGIKZ.js.map +0 -1
- package/dist/chunk-PTS3QN5P.js.map +0 -1
- package/dist/chunk-TAJBFYCT.js.map +0 -1
- package/dist/chunk-TK66CMXV.js.map +0 -1
- package/dist/chunk-UQ3RJKDD.js.map +0 -1
- package/dist/chunk-ZE2KEPE2.js.map +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
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 { computeFlowStateFromSnapshots } from './chunk-
|
|
5
|
-
import { parseFlowDefinition } from './chunk-
|
|
6
|
-
import { renderStimulusPrompt } from './chunk-
|
|
7
|
-
import { flowDefaults, nodeRequiresApproval,
|
|
8
|
-
import { portableSpawn } from './chunk-
|
|
4
|
+
import { getConsumedOutputSchema, validateConsumedNodeOutput, resolveNodeBindings, computeFlowStateFromSnapshots, interpolateNodeInstruction } from './chunk-4O2RAXKJ.js';
|
|
5
|
+
import { parseFlowDefinition } from './chunk-OEGHKO5N.js';
|
|
6
|
+
import { renderStimulusPrompt } from './chunk-FKBQRV4J.js';
|
|
7
|
+
import { flowDefaults, isAgentNode, isGateNode, nodeRequiresApproval, isSubFlowNode, isFunctionNode, nodeIsOptional, isActiveRuntimeNode, nodeLabel, nodeInstruction, nodeAssets } from './chunk-4QROZZ5Z.js';
|
|
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';
|
|
11
|
+
import { resolve, relative, sep, isAbsolute, join, dirname } from 'path';
|
|
11
12
|
import { mkdir, rename, writeFile, readFile, stat, cp, readdir, chmod, realpath } from 'fs/promises';
|
|
12
|
-
import
|
|
13
|
-
import * as zNS from 'zod';
|
|
13
|
+
import * as z4 from 'zod';
|
|
14
14
|
import { execFile } from 'child_process';
|
|
15
15
|
import { promisify } from 'util';
|
|
16
16
|
import { createReadStream, readFileSync, existsSync, mkdirSync, openSync, writeSync, closeSync, chmodSync, renameSync } from 'fs';
|
|
@@ -55,6 +55,326 @@ var AbstractConnector = class {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
+
|
|
59
|
+
// factory-assets/connectors/flow/inline-sub-flow-host.ts
|
|
60
|
+
var INLINE_SUB_FLOW_MAX_DEPTH = 16;
|
|
61
|
+
function isRecord(value) {
|
|
62
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63
|
+
}
|
|
64
|
+
function childDefinition(request, authored) {
|
|
65
|
+
const childRunInput = authored.defaults?.run_input;
|
|
66
|
+
const parentRunInput = request.parentFlow.defaults?.run_input;
|
|
67
|
+
const hasBoundInputs = Object.keys(request.inputs).length > 0;
|
|
68
|
+
const hasParentContext = request.passContext && parentRunInput !== void 0;
|
|
69
|
+
if (!hasBoundInputs && !hasParentContext) return authored;
|
|
70
|
+
let runInput;
|
|
71
|
+
if ((childRunInput === void 0 || isRecord(childRunInput)) && (!hasParentContext || isRecord(parentRunInput))) {
|
|
72
|
+
runInput = {
|
|
73
|
+
...isRecord(childRunInput) ? childRunInput : {},
|
|
74
|
+
...hasParentContext && isRecord(parentRunInput) ? parentRunInput : {},
|
|
75
|
+
...request.inputs
|
|
76
|
+
};
|
|
77
|
+
} else if (hasBoundInputs) {
|
|
78
|
+
runInput = request.inputs;
|
|
79
|
+
} else {
|
|
80
|
+
runInput = parentRunInput;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
...authored,
|
|
84
|
+
defaults: {
|
|
85
|
+
...authored.defaults,
|
|
86
|
+
run_input: runInput
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function terminalError(execution) {
|
|
91
|
+
const failed = Object.values(execution.nodes).find((node) => node.status === "failed");
|
|
92
|
+
if (failed?.error) return failed.error;
|
|
93
|
+
return {
|
|
94
|
+
message: `Inline child flow ${execution.flowId} ended with status ${execution.status}`,
|
|
95
|
+
recoverable: false,
|
|
96
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function projection(session) {
|
|
100
|
+
const execution = session.adapter.getExecution(session.handle);
|
|
101
|
+
for (const definition of session.flow.nodes) {
|
|
102
|
+
const node = execution.nodes[definition.id];
|
|
103
|
+
if (node?.status === "awaiting_approval" && node.approval?.decision == null) {
|
|
104
|
+
return { status: "awaiting_approval", approval: structuredClone(node.approval) };
|
|
105
|
+
}
|
|
106
|
+
if (node?.status === "awaiting_input" && node.input?.providedAt == null) {
|
|
107
|
+
return { status: "awaiting_input", input: structuredClone(node.input) };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const approvals = Object.values(execution.nodes).flatMap((node) => node.approvalHistory).sort((left, right) => left.requestedAt.localeCompare(right.requestedAt));
|
|
111
|
+
const inputs = Object.values(execution.nodes).flatMap((node) => node.inputHistory).sort((left, right) => left.requestedAt.localeCompare(right.requestedAt));
|
|
112
|
+
return {
|
|
113
|
+
status: "running",
|
|
114
|
+
approval: approvals.at(-1) ? structuredClone(approvals.at(-1)) : void 0,
|
|
115
|
+
input: inputs.at(-1) ? structuredClone(inputs.at(-1)) : void 0
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function currentInteractionNode(session, status) {
|
|
119
|
+
const execution = session.adapter.getExecution(session.handle);
|
|
120
|
+
const node = session.flow.nodes.find(
|
|
121
|
+
(definition) => execution.nodes[definition.id]?.status === status
|
|
122
|
+
);
|
|
123
|
+
if (!node) {
|
|
124
|
+
throw new Error(`Inline child ${session.flow.id} is not ${status.replaceAll("_", " ")}`);
|
|
125
|
+
}
|
|
126
|
+
return node.id;
|
|
127
|
+
}
|
|
128
|
+
var SessionInlineSubFlowRunner = class {
|
|
129
|
+
constructor(options) {
|
|
130
|
+
this.options = options;
|
|
131
|
+
}
|
|
132
|
+
options;
|
|
133
|
+
sessions = /* @__PURE__ */ new Map();
|
|
134
|
+
sessionKey(request) {
|
|
135
|
+
return `${request.parentExecution.runId}\0${request.node.id}`;
|
|
136
|
+
}
|
|
137
|
+
async dispose(session) {
|
|
138
|
+
if (session.closed) return;
|
|
139
|
+
session.closed = true;
|
|
140
|
+
this.sessions.delete(session.key);
|
|
141
|
+
session.request.routeOperationsTo(void 0);
|
|
142
|
+
await session.adapter.disconnect(session.handle);
|
|
143
|
+
}
|
|
144
|
+
async cancel(session) {
|
|
145
|
+
if (session.closed) return;
|
|
146
|
+
try {
|
|
147
|
+
await session.adapter.executeOp(session.handle, "cancel", {});
|
|
148
|
+
} finally {
|
|
149
|
+
await this.dispose(session);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async createSession(request) {
|
|
153
|
+
if (request.ancestry.includes(request.flowId)) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Inline sub-flow cycle detected: ${[...request.ancestry, request.flowId].join(" -> ")}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (request.ancestry.length >= INLINE_SUB_FLOW_MAX_DEPTH) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Inline sub-flow depth exceeds ${INLINE_SUB_FLOW_MAX_DEPTH}: ${[
|
|
161
|
+
...request.ancestry,
|
|
162
|
+
request.flowId
|
|
163
|
+
].join(" -> ")}`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const authored = await this.options.resolveFlow(request.flowId);
|
|
167
|
+
if (request.ancestry.includes(authored.id)) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`Inline sub-flow cycle detected after resolving ${request.flowId}: ${[
|
|
170
|
+
...request.ancestry,
|
|
171
|
+
authored.id
|
|
172
|
+
].join(" -> ")}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const flow = childDefinition(request, authored);
|
|
176
|
+
const adapter = new FlowAdapter();
|
|
177
|
+
const handle = await adapter.connect(
|
|
178
|
+
{
|
|
179
|
+
id: `flow:${request.parentExecution.runId}:${request.node.id}`,
|
|
180
|
+
driver: "flow",
|
|
181
|
+
access: "read-write",
|
|
182
|
+
options: {
|
|
183
|
+
flow,
|
|
184
|
+
flowInputOrigins: request.inputOrigins,
|
|
185
|
+
inlineSubFlowAncestry: [...request.ancestry, flow.id],
|
|
186
|
+
inlineSubFlowRunner: this,
|
|
187
|
+
nodeExecutor: this.options.nodeExecutor,
|
|
188
|
+
seed: {
|
|
189
|
+
runId: `${request.parentExecution.runId}:${request.node.id}`,
|
|
190
|
+
startedBy: request.parentExecution.startedBy,
|
|
191
|
+
autonomousMode: request.parentExecution.autonomousMode
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{}
|
|
196
|
+
);
|
|
197
|
+
const key = this.sessionKey(request);
|
|
198
|
+
let session;
|
|
199
|
+
const target = {
|
|
200
|
+
parentNodeId: request.node.id,
|
|
201
|
+
isAgentTurnActive: () => session.agentTurnActive || adapter.hasActiveInlineAgentTurn(handle),
|
|
202
|
+
executeAgentOp: (operation, args) => adapter.executeOp(handle, operation, args),
|
|
203
|
+
applyParentApproval: async (args) => {
|
|
204
|
+
const nodeId = currentInteractionNode(session, "awaiting_approval");
|
|
205
|
+
await adapter.executeOp(handle, "applyApproval", { ...args, nodeId });
|
|
206
|
+
session.stimulus = {
|
|
207
|
+
kind: "approval_received",
|
|
208
|
+
nodeId,
|
|
209
|
+
decision: args.decision === "rejected" ? "rejected" : "approved",
|
|
210
|
+
feedback: typeof args.feedback === "string" ? args.feedback : void 0,
|
|
211
|
+
decidedBy: String(args.decidedBy ?? "")
|
|
212
|
+
};
|
|
213
|
+
return projection(session);
|
|
214
|
+
},
|
|
215
|
+
applyParentInput: async (args) => {
|
|
216
|
+
const nodeId = currentInteractionNode(session, "awaiting_input");
|
|
217
|
+
await adapter.executeOp(handle, "applyInput", { ...args, nodeId });
|
|
218
|
+
session.stimulus = {
|
|
219
|
+
kind: "input_received",
|
|
220
|
+
nodeId,
|
|
221
|
+
response: args.response,
|
|
222
|
+
providedBy: String(args.providedBy ?? "")
|
|
223
|
+
};
|
|
224
|
+
return projection(session);
|
|
225
|
+
},
|
|
226
|
+
cancel: () => this.cancel(session),
|
|
227
|
+
getExecution: () => adapter.getAgentExecution(handle),
|
|
228
|
+
getMandatoryAgentApprovalNodeIds: () => adapter.getMandatoryAgentApprovalNodeIds(handle)
|
|
229
|
+
};
|
|
230
|
+
session = {
|
|
231
|
+
key,
|
|
232
|
+
request,
|
|
233
|
+
adapter,
|
|
234
|
+
handle,
|
|
235
|
+
flow,
|
|
236
|
+
target,
|
|
237
|
+
stimulus: { kind: "flow_started" },
|
|
238
|
+
changeVersion: 0,
|
|
239
|
+
agentTurnActive: false,
|
|
240
|
+
closed: false
|
|
241
|
+
};
|
|
242
|
+
adapter.onStateChange(handle, () => {
|
|
243
|
+
session.changeVersion += 1;
|
|
244
|
+
});
|
|
245
|
+
this.sessions.set(key, session);
|
|
246
|
+
request.routeOperationsTo(target);
|
|
247
|
+
return session;
|
|
248
|
+
}
|
|
249
|
+
async run(request) {
|
|
250
|
+
const key = this.sessionKey(request);
|
|
251
|
+
const session = this.sessions.get(key) ?? await this.createSession(request);
|
|
252
|
+
session.request = request;
|
|
253
|
+
request.routeOperationsTo(session.target);
|
|
254
|
+
try {
|
|
255
|
+
while (!session.closed) {
|
|
256
|
+
await session.adapter.drainRuntimeNodes(session.handle);
|
|
257
|
+
const execution = session.adapter.getExecution(session.handle);
|
|
258
|
+
if (execution.done || execution.status === "complete") {
|
|
259
|
+
await this.dispose(session);
|
|
260
|
+
return {
|
|
261
|
+
status: "complete",
|
|
262
|
+
output: {
|
|
263
|
+
summary: `Inline child flow ${session.flow.id} completed`,
|
|
264
|
+
artifacts: []
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
if (execution.status === "failed" || execution.status === "cancelled") {
|
|
269
|
+
const error = terminalError(execution);
|
|
270
|
+
await this.dispose(session);
|
|
271
|
+
return { status: "failed", error };
|
|
272
|
+
}
|
|
273
|
+
if (session.adapter.shouldDriveAgentTurn(session.handle, session.stimulus)) {
|
|
274
|
+
const versionBeforeTurn = session.changeVersion;
|
|
275
|
+
session.agentTurnActive = true;
|
|
276
|
+
try {
|
|
277
|
+
await this.options.driveAgentTurn({
|
|
278
|
+
adapter: session.adapter,
|
|
279
|
+
handle: session.handle,
|
|
280
|
+
flow: session.flow,
|
|
281
|
+
execution,
|
|
282
|
+
stimulus: session.stimulus
|
|
283
|
+
});
|
|
284
|
+
} finally {
|
|
285
|
+
session.agentTurnActive = false;
|
|
286
|
+
}
|
|
287
|
+
if (session.closed) {
|
|
288
|
+
return { status: "failed", error: terminalError(execution) };
|
|
289
|
+
}
|
|
290
|
+
session.stimulus = { kind: "state_changed" };
|
|
291
|
+
if (session.changeVersion === versionBeforeTurn) {
|
|
292
|
+
return { status: "paused", projection: projection(session) };
|
|
293
|
+
}
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
return { status: "paused", projection: projection(session) };
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
status: "failed",
|
|
300
|
+
error: {
|
|
301
|
+
message: `Inline child flow ${request.flowId} was cancelled`,
|
|
302
|
+
recoverable: false,
|
|
303
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
} catch (error) {
|
|
307
|
+
try {
|
|
308
|
+
await this.dispose(session);
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
function stableJsonStringify(value) {
|
|
316
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
317
|
+
if (nested === null || Array.isArray(nested) || typeof nested !== "object") return nested;
|
|
318
|
+
return Object.fromEntries(
|
|
319
|
+
Object.entries(nested).sort(([left], [right]) => {
|
|
320
|
+
if (left < right) return -1;
|
|
321
|
+
if (left > right) return 1;
|
|
322
|
+
return 0;
|
|
323
|
+
})
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function serializeEnvironmentInput(name, value) {
|
|
328
|
+
if (typeof value === "string") return value;
|
|
329
|
+
const serialized = stableJsonStringify(value);
|
|
330
|
+
if (serialized === void 0) {
|
|
331
|
+
throw new Error(`Function node input ${name} is not JSON-serializable`);
|
|
332
|
+
}
|
|
333
|
+
return serialized;
|
|
334
|
+
}
|
|
335
|
+
function commandFor(node) {
|
|
336
|
+
switch (node.run.runtime ?? "shell") {
|
|
337
|
+
case "shell":
|
|
338
|
+
return ["sh", "-c", node.run.command];
|
|
339
|
+
case "node":
|
|
340
|
+
return ["node", "-e", node.run.command];
|
|
341
|
+
case "python":
|
|
342
|
+
return ["python3", "-c", node.run.command];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
var SessionNodeExecutor = class {
|
|
346
|
+
workspaceDir;
|
|
347
|
+
/** Creates an executor scoped to one already-materialized session workspace. */
|
|
348
|
+
constructor(options) {
|
|
349
|
+
this.workspaceDir = resolve(options.workspaceDir);
|
|
350
|
+
}
|
|
351
|
+
/** Runs one function node process with session environment and binding overrides. */
|
|
352
|
+
async execute(request) {
|
|
353
|
+
const cwd = resolve(this.workspaceDir, request.node.run.cwd ?? ".");
|
|
354
|
+
const workspaceRelative = relative(this.workspaceDir, cwd);
|
|
355
|
+
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
|
+
);
|
|
359
|
+
}
|
|
360
|
+
const boundEnvironment = Object.fromEntries(
|
|
361
|
+
Object.entries(request.inputs).map(([name, value]) => [
|
|
362
|
+
name,
|
|
363
|
+
serializeEnvironmentInput(name, value)
|
|
364
|
+
])
|
|
365
|
+
);
|
|
366
|
+
const process2 = portableSpawn(commandFor(request.node), {
|
|
367
|
+
cwd,
|
|
368
|
+
env: { ...globalThis.process.env, ...boundEnvironment }
|
|
369
|
+
});
|
|
370
|
+
const [stdout, stderr, exitStatus] = await Promise.all([
|
|
371
|
+
new Response(process2.stdout).text(),
|
|
372
|
+
new Response(process2.stderr).text(),
|
|
373
|
+
process2.exited
|
|
374
|
+
]);
|
|
375
|
+
return { exitStatus, stdout, stderr };
|
|
376
|
+
}
|
|
377
|
+
};
|
|
58
378
|
var execFileAsync = promisify(execFile);
|
|
59
379
|
var GIT_REV_PARSE_TIMEOUT_MS = 15e3;
|
|
60
380
|
var DeployConnector = class extends AbstractConnector {
|
|
@@ -231,7 +551,7 @@ var DeployConnector = class extends AbstractConnector {
|
|
|
231
551
|
}
|
|
232
552
|
}
|
|
233
553
|
_mockPush(state, opts) {
|
|
234
|
-
const
|
|
554
|
+
const record2 = {
|
|
235
555
|
id: opts.deploymentId,
|
|
236
556
|
url: `https://mock.deploy.example.com/${opts.deploymentId}`,
|
|
237
557
|
ref: opts.ref,
|
|
@@ -240,8 +560,8 @@ var DeployConnector = class extends AbstractConnector {
|
|
|
240
560
|
deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
241
561
|
status: "live"
|
|
242
562
|
};
|
|
243
|
-
state.deployments.set(opts.deploymentId,
|
|
244
|
-
return JSON.stringify({ deployment_id:
|
|
563
|
+
state.deployments.set(opts.deploymentId, record2);
|
|
564
|
+
return JSON.stringify({ deployment_id: record2.id, url: record2.url, ref: record2.ref ?? null });
|
|
245
565
|
}
|
|
246
566
|
_opStatus(state, args) {
|
|
247
567
|
const id = args.deployment ? String(args.deployment) : void 0;
|
|
@@ -3126,7 +3446,7 @@ var MinIOConnector = class extends AbstractConnector {
|
|
|
3126
3446
|
const { client, bucket } = handle.state;
|
|
3127
3447
|
const entries = [];
|
|
3128
3448
|
const stream = client.listObjects(bucket, path ?? "", recursive);
|
|
3129
|
-
return new Promise((
|
|
3449
|
+
return new Promise((resolve6, reject) => {
|
|
3130
3450
|
stream.on("data", (obj) => {
|
|
3131
3451
|
entries.push({
|
|
3132
3452
|
name: obj.name,
|
|
@@ -3136,7 +3456,7 @@ var MinIOConnector = class extends AbstractConnector {
|
|
|
3136
3456
|
modifiedAt: obj.lastModified
|
|
3137
3457
|
});
|
|
3138
3458
|
});
|
|
3139
|
-
stream.on("end", () =>
|
|
3459
|
+
stream.on("end", () => resolve6(entries));
|
|
3140
3460
|
stream.on("error", reject);
|
|
3141
3461
|
});
|
|
3142
3462
|
}
|
|
@@ -3144,9 +3464,9 @@ var MinIOConnector = class extends AbstractConnector {
|
|
|
3144
3464
|
const { client, bucket } = handle.state;
|
|
3145
3465
|
const chunks = [];
|
|
3146
3466
|
const stream = await client.getObject(bucket, path);
|
|
3147
|
-
return new Promise((
|
|
3467
|
+
return new Promise((resolve6, reject) => {
|
|
3148
3468
|
stream.on("data", (chunk) => chunks.push(chunk));
|
|
3149
|
-
stream.on("end", () =>
|
|
3469
|
+
stream.on("end", () => resolve6({ data: Buffer.concat(chunks).toString() }));
|
|
3150
3470
|
stream.on("error", reject);
|
|
3151
3471
|
});
|
|
3152
3472
|
}
|
|
@@ -4414,7 +4734,7 @@ async function checkCliExists(cli) {
|
|
|
4414
4734
|
}
|
|
4415
4735
|
}
|
|
4416
4736
|
async function spawnCloudflaredTunnel(port) {
|
|
4417
|
-
return new Promise((
|
|
4737
|
+
return new Promise((resolve6, reject) => {
|
|
4418
4738
|
const proc = portableSpawn(["cloudflared", "tunnel", "--url", `http://localhost:${port}`]);
|
|
4419
4739
|
const decoder = new TextDecoder();
|
|
4420
4740
|
const reader = proc.stderr.getReader();
|
|
@@ -4425,7 +4745,7 @@ async function spawnCloudflaredTunnel(port) {
|
|
|
4425
4745
|
const chunk = decoder.decode(value);
|
|
4426
4746
|
const match = chunk.match(/https:\/\/[^\s"]+\.trycloudflare\.com/);
|
|
4427
4747
|
if (match) {
|
|
4428
|
-
|
|
4748
|
+
resolve6({ url: match[0], process: proc });
|
|
4429
4749
|
return;
|
|
4430
4750
|
}
|
|
4431
4751
|
}
|
|
@@ -5128,7 +5448,7 @@ function connectorTargetFromFactory(id, factory) {
|
|
|
5128
5448
|
id,
|
|
5129
5449
|
displayName: id,
|
|
5130
5450
|
apiVersion: 1,
|
|
5131
|
-
configSchema:
|
|
5451
|
+
configSchema: z4.unknown(),
|
|
5132
5452
|
create: (_config, _ctx) => factory()
|
|
5133
5453
|
};
|
|
5134
5454
|
}
|
|
@@ -5425,7 +5745,7 @@ var ConnectorManager = class {
|
|
|
5425
5745
|
id: driver,
|
|
5426
5746
|
displayName: driver,
|
|
5427
5747
|
apiVersion: 1,
|
|
5428
|
-
configSchema:
|
|
5748
|
+
configSchema: z4.unknown(),
|
|
5429
5749
|
create: () => connector
|
|
5430
5750
|
});
|
|
5431
5751
|
}
|
|
@@ -6083,7 +6403,7 @@ var ConnectorManager = class {
|
|
|
6083
6403
|
return buildSkillBody(descriptor, ops, shellAccess);
|
|
6084
6404
|
}
|
|
6085
6405
|
};
|
|
6086
|
-
var zStatic =
|
|
6406
|
+
var zStatic = z4.z ?? z4.default ?? z4;
|
|
6087
6407
|
async function buildSdkConnectorTools(manager, options) {
|
|
6088
6408
|
const shellAccess = options?.shellAccess ?? true;
|
|
6089
6409
|
let sdk = options?.sdk;
|
|
@@ -6094,7 +6414,7 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6094
6414
|
return null;
|
|
6095
6415
|
}
|
|
6096
6416
|
}
|
|
6097
|
-
const
|
|
6417
|
+
const z5 = options?.z ?? zStatic;
|
|
6098
6418
|
const tools = [];
|
|
6099
6419
|
tools.push({
|
|
6100
6420
|
name: "connector_list",
|
|
@@ -6116,7 +6436,7 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6116
6436
|
name: "connector_load_skill",
|
|
6117
6437
|
description: "Load full instructions for a specific connector into context. Returns a markdown document describing the connector and its available CLI commands.",
|
|
6118
6438
|
inputSchema: {
|
|
6119
|
-
connector_id:
|
|
6439
|
+
connector_id: z5.string().describe("Connector ID (use connector_list to enumerate)")
|
|
6120
6440
|
},
|
|
6121
6441
|
handler: async ({ connector_id }) => {
|
|
6122
6442
|
try {
|
|
@@ -6134,8 +6454,8 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6134
6454
|
name: "connector_read",
|
|
6135
6455
|
description: 'Read content from a connector by path. For KV stores, path is the key. For databases, path is "table/pk".',
|
|
6136
6456
|
inputSchema: {
|
|
6137
|
-
connector_id:
|
|
6138
|
-
path:
|
|
6457
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6458
|
+
path: z5.string().describe("Path within the connector")
|
|
6139
6459
|
},
|
|
6140
6460
|
handler: async ({ connector_id, path }) => {
|
|
6141
6461
|
try {
|
|
@@ -6155,9 +6475,9 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6155
6475
|
name: "connector_write",
|
|
6156
6476
|
description: "Write content to a connector at a path.",
|
|
6157
6477
|
inputSchema: {
|
|
6158
|
-
connector_id:
|
|
6159
|
-
path:
|
|
6160
|
-
content:
|
|
6478
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6479
|
+
path: z5.string().describe("Path within the connector"),
|
|
6480
|
+
content: z5.string().describe("Content to write")
|
|
6161
6481
|
},
|
|
6162
6482
|
handler: async ({
|
|
6163
6483
|
connector_id,
|
|
@@ -6179,9 +6499,9 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6179
6499
|
name: "connector_list_entries",
|
|
6180
6500
|
description: "List entries in a connector (files, keys, rows, etc).",
|
|
6181
6501
|
inputSchema: {
|
|
6182
|
-
connector_id:
|
|
6183
|
-
path:
|
|
6184
|
-
recursive:
|
|
6502
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6503
|
+
path: z5.string().optional().describe("Path prefix or directory"),
|
|
6504
|
+
recursive: z5.boolean().optional().describe("Include nested entries")
|
|
6185
6505
|
},
|
|
6186
6506
|
handler: async ({
|
|
6187
6507
|
connector_id,
|
|
@@ -6203,8 +6523,8 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6203
6523
|
name: "connector_search",
|
|
6204
6524
|
description: "Search within a connector for matching content.",
|
|
6205
6525
|
inputSchema: {
|
|
6206
|
-
connector_id:
|
|
6207
|
-
query:
|
|
6526
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6527
|
+
query: z5.string().describe("Search query")
|
|
6208
6528
|
},
|
|
6209
6529
|
handler: async ({ connector_id, query }) => {
|
|
6210
6530
|
try {
|
|
@@ -6222,8 +6542,8 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6222
6542
|
name: "connector_delete",
|
|
6223
6543
|
description: "Delete an entry from a connector.",
|
|
6224
6544
|
inputSchema: {
|
|
6225
|
-
connector_id:
|
|
6226
|
-
path:
|
|
6545
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6546
|
+
path: z5.string().describe("Path to delete")
|
|
6227
6547
|
},
|
|
6228
6548
|
handler: async ({ connector_id, path }) => {
|
|
6229
6549
|
try {
|
|
@@ -6242,9 +6562,9 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6242
6562
|
name: "connector_exec",
|
|
6243
6563
|
description: "Execute a custom connector operation by name. Use only when shell access is unavailable. Load the connector skill first to see available operations and their arguments.",
|
|
6244
6564
|
inputSchema: {
|
|
6245
|
-
connector_id:
|
|
6246
|
-
operation:
|
|
6247
|
-
args:
|
|
6565
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6566
|
+
operation: z5.string().describe("Operation name (e.g. 'send_message', 'query')"),
|
|
6567
|
+
args: z5.record(z5.unknown()).describe("Named arguments for the operation (without -- prefix)")
|
|
6248
6568
|
},
|
|
6249
6569
|
handler: async ({
|
|
6250
6570
|
connector_id,
|
|
@@ -6287,7 +6607,7 @@ async function buildSdkFlowTools(manager, connectorId, options) {
|
|
|
6287
6607
|
return null;
|
|
6288
6608
|
}
|
|
6289
6609
|
}
|
|
6290
|
-
const
|
|
6610
|
+
const z5 = options?.z ?? zStatic;
|
|
6291
6611
|
let ops;
|
|
6292
6612
|
try {
|
|
6293
6613
|
ops = manager.getOperations(connectorId);
|
|
@@ -6299,7 +6619,7 @@ async function buildSdkFlowTools(manager, connectorId, options) {
|
|
|
6299
6619
|
const tools = agentOps.map((op) => ({
|
|
6300
6620
|
name: op.name,
|
|
6301
6621
|
description: op.description,
|
|
6302
|
-
inputSchema: buildOperationInputSchema(op.args,
|
|
6622
|
+
inputSchema: buildOperationInputSchema(op.args, z5),
|
|
6303
6623
|
handler: async (input) => {
|
|
6304
6624
|
try {
|
|
6305
6625
|
const result = await manager.executeOp(connectorId, op.name, input ?? {});
|
|
@@ -6314,22 +6634,22 @@ async function buildSdkFlowTools(manager, connectorId, options) {
|
|
|
6314
6634
|
}));
|
|
6315
6635
|
return sdk.createSdkMcpServer({ name: "skaile-flow", tools });
|
|
6316
6636
|
}
|
|
6317
|
-
function buildOperationInputSchema(args,
|
|
6637
|
+
function buildOperationInputSchema(args, z5) {
|
|
6318
6638
|
const shape = {};
|
|
6319
6639
|
for (const arg of args) {
|
|
6320
6640
|
let schema;
|
|
6321
6641
|
switch (arg.type) {
|
|
6322
6642
|
case "boolean":
|
|
6323
|
-
schema =
|
|
6643
|
+
schema = z5.boolean();
|
|
6324
6644
|
break;
|
|
6325
6645
|
case "number":
|
|
6326
|
-
schema =
|
|
6646
|
+
schema = z5.number();
|
|
6327
6647
|
break;
|
|
6328
6648
|
case "json":
|
|
6329
|
-
schema =
|
|
6649
|
+
schema = z5.json();
|
|
6330
6650
|
break;
|
|
6331
6651
|
default:
|
|
6332
|
-
schema =
|
|
6652
|
+
schema = z5.string();
|
|
6333
6653
|
}
|
|
6334
6654
|
if (arg.description) schema = schema.describe(arg.description);
|
|
6335
6655
|
if (!arg.required) schema = schema.optional();
|
|
@@ -6625,7 +6945,7 @@ async function ensureFleetMounted(dir, log, options) {
|
|
|
6625
6945
|
return;
|
|
6626
6946
|
} catch (err) {
|
|
6627
6947
|
lastErr = err;
|
|
6628
|
-
await new Promise((
|
|
6948
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollIntervalMs));
|
|
6629
6949
|
}
|
|
6630
6950
|
}
|
|
6631
6951
|
throw new Error(
|
|
@@ -6922,19 +7242,19 @@ var PortPool = class _PortPool {
|
|
|
6922
7242
|
}
|
|
6923
7243
|
/** Quick TCP connect test — returns true if nothing is listening. */
|
|
6924
7244
|
static isPortFree(port, host = "127.0.0.1") {
|
|
6925
|
-
return new Promise((
|
|
7245
|
+
return new Promise((resolve6) => {
|
|
6926
7246
|
const socket = createConnection({ port, host });
|
|
6927
7247
|
socket.once("connect", () => {
|
|
6928
7248
|
socket.destroy();
|
|
6929
|
-
|
|
7249
|
+
resolve6(false);
|
|
6930
7250
|
});
|
|
6931
7251
|
socket.once("error", () => {
|
|
6932
7252
|
socket.destroy();
|
|
6933
|
-
|
|
7253
|
+
resolve6(true);
|
|
6934
7254
|
});
|
|
6935
7255
|
socket.setTimeout(500, () => {
|
|
6936
7256
|
socket.destroy();
|
|
6937
|
-
|
|
7257
|
+
resolve6(true);
|
|
6938
7258
|
});
|
|
6939
7259
|
});
|
|
6940
7260
|
}
|
|
@@ -7153,9 +7473,28 @@ async function listWorktrees(repoRoot) {
|
|
|
7153
7473
|
if (current.path) trees.push(current);
|
|
7154
7474
|
return trees;
|
|
7155
7475
|
}
|
|
7156
|
-
|
|
7157
|
-
// factory-assets/connectors/flow/adapter.ts
|
|
7158
7476
|
var now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
7477
|
+
var AGENT_INLINE_OPERATIONS = /* @__PURE__ */ new Set([
|
|
7478
|
+
"get_available",
|
|
7479
|
+
"get_state",
|
|
7480
|
+
"get_globals",
|
|
7481
|
+
"build_handoff",
|
|
7482
|
+
"validate_node",
|
|
7483
|
+
"start_node",
|
|
7484
|
+
"request_approval",
|
|
7485
|
+
"request_input",
|
|
7486
|
+
"complete_node",
|
|
7487
|
+
"skip_node",
|
|
7488
|
+
"fail_node"
|
|
7489
|
+
]);
|
|
7490
|
+
function mergeInteractionHistory(history, interaction) {
|
|
7491
|
+
if (!interaction) return history;
|
|
7492
|
+
const next = [...history];
|
|
7493
|
+
const index = next.findIndex((entry) => entry.requestedAt === interaction.requestedAt);
|
|
7494
|
+
if (index >= 0) next[index] = interaction;
|
|
7495
|
+
else next.push(interaction);
|
|
7496
|
+
return next;
|
|
7497
|
+
}
|
|
7159
7498
|
function normalizeRuntimeFlow(authored) {
|
|
7160
7499
|
const parsed = parseFlowDefinition(authored);
|
|
7161
7500
|
if (parsed.ok) return parsed.data;
|
|
@@ -7171,10 +7510,15 @@ function emptyNode(nodeId) {
|
|
|
7171
7510
|
errorHistory: []
|
|
7172
7511
|
};
|
|
7173
7512
|
}
|
|
7513
|
+
function nodeExecutionRecord(entries = []) {
|
|
7514
|
+
const record2 = /* @__PURE__ */ Object.create(null);
|
|
7515
|
+
for (const [nodeId, execution] of entries) record2[nodeId] = execution;
|
|
7516
|
+
return record2;
|
|
7517
|
+
}
|
|
7174
7518
|
function buildInitialExecution(flowDef, seed) {
|
|
7175
|
-
const nodes =
|
|
7519
|
+
const nodes = nodeExecutionRecord();
|
|
7176
7520
|
for (const n of flowDef.nodes) {
|
|
7177
|
-
if (
|
|
7521
|
+
if (isActiveRuntimeNode(n)) {
|
|
7178
7522
|
nodes[n.id] = emptyNode(n.id);
|
|
7179
7523
|
}
|
|
7180
7524
|
}
|
|
@@ -7197,7 +7541,8 @@ function toSnapshot(exec) {
|
|
|
7197
7541
|
approval: exec.approval,
|
|
7198
7542
|
input: exec.input,
|
|
7199
7543
|
output: exec.output,
|
|
7200
|
-
error: exec.error
|
|
7544
|
+
error: exec.error,
|
|
7545
|
+
failedPreconditions: exec.failedPreconditions
|
|
7201
7546
|
};
|
|
7202
7547
|
}
|
|
7203
7548
|
function snapshotMap(execution) {
|
|
@@ -7207,6 +7552,50 @@ function snapshotMap(execution) {
|
|
|
7207
7552
|
}
|
|
7208
7553
|
return map;
|
|
7209
7554
|
}
|
|
7555
|
+
function prepareExecutionSnapshot(flowDef, execution) {
|
|
7556
|
+
let json2;
|
|
7557
|
+
try {
|
|
7558
|
+
json2 = JSON.stringify(execution);
|
|
7559
|
+
} catch {
|
|
7560
|
+
throw new Error("FlowAdapter: execution snapshot must be a JSON object");
|
|
7561
|
+
}
|
|
7562
|
+
const cloned = z4.json().safeParse(JSON.parse(json2));
|
|
7563
|
+
if (!cloned.success || typeof cloned.data !== "object" || Array.isArray(cloned.data)) {
|
|
7564
|
+
throw new Error("FlowAdapter: execution snapshot must be a JSON object");
|
|
7565
|
+
}
|
|
7566
|
+
const snapshot = cloned.data;
|
|
7567
|
+
for (const [nodeId, node] of Object.entries(snapshot.nodes ?? {})) {
|
|
7568
|
+
if (node.status !== "complete") continue;
|
|
7569
|
+
const validation = validateConsumedNodeOutput(flowDef, nodeId, node.output?.fields);
|
|
7570
|
+
if (!validation.valid) {
|
|
7571
|
+
throw new Error(
|
|
7572
|
+
`FlowAdapter: invalid execution output for node ${nodeId}: ${validation.issues.join("; ")}`
|
|
7573
|
+
);
|
|
7574
|
+
}
|
|
7575
|
+
}
|
|
7576
|
+
const nodes = nodeExecutionRecord(Object.entries(snapshot.nodes ?? {}));
|
|
7577
|
+
for (const node of flowDef.nodes) {
|
|
7578
|
+
const existing = nodes[node.id];
|
|
7579
|
+
if (isSubFlowNode(node) && existing && ["running", "awaiting_approval", "awaiting_input"].includes(existing.status)) {
|
|
7580
|
+
nodes[node.id] = {
|
|
7581
|
+
...existing,
|
|
7582
|
+
status: "not_started",
|
|
7583
|
+
startedAt: void 0,
|
|
7584
|
+
completedAt: void 0,
|
|
7585
|
+
output: void 0,
|
|
7586
|
+
approval: void 0,
|
|
7587
|
+
input: void 0,
|
|
7588
|
+
error: void 0
|
|
7589
|
+
};
|
|
7590
|
+
continue;
|
|
7591
|
+
}
|
|
7592
|
+
if (isActiveRuntimeNode(node) && !Object.hasOwn(nodes, node.id)) {
|
|
7593
|
+
nodes[node.id] = emptyNode(node.id);
|
|
7594
|
+
}
|
|
7595
|
+
}
|
|
7596
|
+
snapshot.nodes = nodes;
|
|
7597
|
+
return snapshot;
|
|
7598
|
+
}
|
|
7210
7599
|
function assertLegal(cond, message) {
|
|
7211
7600
|
if (!cond) throw new Error(`FlowAdapter: ${message}`);
|
|
7212
7601
|
}
|
|
@@ -7218,7 +7607,7 @@ function findNode(flowDef, nodeId) {
|
|
|
7218
7607
|
function isOptional(flowDef, nodeId) {
|
|
7219
7608
|
return nodeIsOptional(findNode(flowDef, nodeId));
|
|
7220
7609
|
}
|
|
7221
|
-
function
|
|
7610
|
+
function requiresAgentApproval(flowDef, nodeId) {
|
|
7222
7611
|
return nodeRequiresApproval(findNode(flowDef, nodeId));
|
|
7223
7612
|
}
|
|
7224
7613
|
var FlowAdapter = class extends AbstractConnector {
|
|
@@ -7240,7 +7629,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7240
7629
|
const options = declaration.options ?? {};
|
|
7241
7630
|
if (!options.flow) throw new Error("FlowAdapter requires options.flow (FlowDefinition)");
|
|
7242
7631
|
const flowDef = normalizeRuntimeFlow(options.flow);
|
|
7243
|
-
const execution = options.execution
|
|
7632
|
+
const execution = options.execution ? prepareExecutionSnapshot(flowDef, options.execution) : buildInitialExecution(
|
|
7244
7633
|
flowDef,
|
|
7245
7634
|
options.seed ?? { runId: "local", startedBy: "system", autonomousMode: false }
|
|
7246
7635
|
);
|
|
@@ -7249,14 +7638,46 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7249
7638
|
execution,
|
|
7250
7639
|
stimulusBus: options.stimulusBus,
|
|
7251
7640
|
connectorId: declaration.id,
|
|
7252
|
-
useBusForStimulus: options.useBusForStimulus ?? false
|
|
7641
|
+
useBusForStimulus: options.useBusForStimulus ?? false,
|
|
7642
|
+
nodeExecutor: options.nodeExecutor,
|
|
7643
|
+
inlineSubFlowRunner: options.inlineSubFlowRunner,
|
|
7644
|
+
inlineOperationTargets: /* @__PURE__ */ new Map(),
|
|
7645
|
+
flowInputOrigins: options.flowInputOrigins,
|
|
7646
|
+
inlineSubFlowAncestry: options.inlineSubFlowAncestry ?? [flowDef.id],
|
|
7647
|
+
executionGeneration: 0
|
|
7253
7648
|
};
|
|
7254
7649
|
state.execution = this.recompute(state);
|
|
7255
7650
|
return this.makeHandle(declaration, state);
|
|
7256
7651
|
}
|
|
7257
7652
|
async disconnect(handle) {
|
|
7653
|
+
const state = this.s(handle);
|
|
7654
|
+
state.executionGeneration += 1;
|
|
7655
|
+
await this.cancelInlineTargets(state);
|
|
7258
7656
|
handle.status = "disconnected";
|
|
7259
7657
|
}
|
|
7658
|
+
async cancelInlineTargets(s) {
|
|
7659
|
+
const targets = [...s.inlineOperationTargets.values()];
|
|
7660
|
+
s.inlineOperationTargets.clear();
|
|
7661
|
+
for (const target of targets) await target.cancel();
|
|
7662
|
+
}
|
|
7663
|
+
projectInlineInteraction(handle, parentNodeId, projection2) {
|
|
7664
|
+
this.mutate(
|
|
7665
|
+
handle,
|
|
7666
|
+
(s) => {
|
|
7667
|
+
const node = s.execution.nodes[parentNodeId];
|
|
7668
|
+
assertLegal(node, `inline projection: unknown parent node ${parentNodeId}`);
|
|
7669
|
+
s.execution.nodes[parentNodeId] = {
|
|
7670
|
+
...node,
|
|
7671
|
+
status: projection2.status,
|
|
7672
|
+
approval: projection2.approval ?? node.approval,
|
|
7673
|
+
approvalHistory: mergeInteractionHistory(node.approvalHistory, projection2.approval),
|
|
7674
|
+
input: projection2.input ?? node.input,
|
|
7675
|
+
inputHistory: mergeInteractionHistory(node.inputHistory, projection2.input)
|
|
7676
|
+
};
|
|
7677
|
+
},
|
|
7678
|
+
false
|
|
7679
|
+
);
|
|
7680
|
+
}
|
|
7260
7681
|
// ── State change subscription ──
|
|
7261
7682
|
onStateChange(handle, cb) {
|
|
7262
7683
|
this.s(handle).onChange = cb;
|
|
@@ -7264,13 +7685,21 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7264
7685
|
/**
|
|
7265
7686
|
* Replace the current execution snapshot with a host-provided one.
|
|
7266
7687
|
* Used on container rehydration via the extended `configure` command.
|
|
7267
|
-
*
|
|
7268
|
-
*
|
|
7688
|
+
* Publishes only when compatibility preparation changes the supplied snapshot,
|
|
7689
|
+
* such as seeding a node kind activated after the snapshot was persisted.
|
|
7269
7690
|
*/
|
|
7270
7691
|
hydrate(handle, execution) {
|
|
7271
7692
|
const s = this.s(handle);
|
|
7272
|
-
|
|
7273
|
-
|
|
7693
|
+
assertLegal(
|
|
7694
|
+
s.inlineOperationTargets.size === 0,
|
|
7695
|
+
"hydrate requires live inline children to be cancelled first"
|
|
7696
|
+
);
|
|
7697
|
+
const candidate = prepareExecutionSnapshot(s.flowDef, execution);
|
|
7698
|
+
const prepared = this.recompute({ ...s, execution: candidate });
|
|
7699
|
+
const changed = JSON.stringify(prepared) !== JSON.stringify(execution);
|
|
7700
|
+
s.executionGeneration += 1;
|
|
7701
|
+
s.execution = prepared;
|
|
7702
|
+
if (changed && s.onChange) s.onChange(s.execution);
|
|
7274
7703
|
}
|
|
7275
7704
|
// ── Base operations (private — wired into tools face in constructor) ──
|
|
7276
7705
|
async read(handle, path) {
|
|
@@ -7360,7 +7789,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7360
7789
|
ops.push(
|
|
7361
7790
|
{
|
|
7362
7791
|
name: "start_node",
|
|
7363
|
-
description: "Mark an available node as running (records startedAt).",
|
|
7792
|
+
description: "Mark an available agent node as running (records startedAt).",
|
|
7364
7793
|
accessLevel: "write",
|
|
7365
7794
|
args: [
|
|
7366
7795
|
{ name: "nodeId", description: "Node ID to start", type: "string", required: true }
|
|
@@ -7368,7 +7797,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7368
7797
|
},
|
|
7369
7798
|
{
|
|
7370
7799
|
name: "request_approval",
|
|
7371
|
-
description: "Request human approval for a running node. Ends your turn \u2014 the runner yields until the user approves or rejects.",
|
|
7800
|
+
description: "Request human approval for a running agent node, or replace a rejected first-class gate decision with a fresh pending request. Ends your turn \u2014 the runner yields until the user approves or rejects.",
|
|
7372
7801
|
accessLevel: "write",
|
|
7373
7802
|
args: [
|
|
7374
7803
|
{ name: "nodeId", description: "Node ID", type: "string", required: true },
|
|
@@ -7388,7 +7817,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7388
7817
|
},
|
|
7389
7818
|
{
|
|
7390
7819
|
name: "request_input",
|
|
7391
|
-
description: "Request typed input
|
|
7820
|
+
description: "Request typed input for an agent node. Ends your turn. Legal from `available`, `running`, or `awaiting_approval`; never legal for a first-class gate.",
|
|
7392
7821
|
accessLevel: "write",
|
|
7393
7822
|
args: [
|
|
7394
7823
|
{ name: "nodeId", description: "Node ID", type: "string", required: true },
|
|
@@ -7408,7 +7837,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7408
7837
|
},
|
|
7409
7838
|
{
|
|
7410
7839
|
name: "complete_node",
|
|
7411
|
-
description: "Mark
|
|
7840
|
+
description: "Mark an agent node as complete with structured output. Consumed fields must match the declared output schema; an agent gets one corrective call before failure. Requires `awaiting_approval` with an `approved` decision, OR `running` when autonomous mode is on. Agent nodes with mandatory approval always require the approved decision. First-class gates complete directly through host approval and never use this operation.",
|
|
7412
7841
|
accessLevel: "write",
|
|
7413
7842
|
args: [
|
|
7414
7843
|
{ name: "nodeId", description: "Node ID to complete", type: "string", required: true },
|
|
@@ -7418,6 +7847,12 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7418
7847
|
type: "string",
|
|
7419
7848
|
required: true
|
|
7420
7849
|
},
|
|
7850
|
+
{
|
|
7851
|
+
name: "fields",
|
|
7852
|
+
description: "Named JSON object consumed by downstream nodes.",
|
|
7853
|
+
type: "json",
|
|
7854
|
+
required: false
|
|
7855
|
+
},
|
|
7421
7856
|
{
|
|
7422
7857
|
name: "artifacts",
|
|
7423
7858
|
description: "JSON array of ArtifactRef objects produced by this node.",
|
|
@@ -7581,6 +8016,29 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7581
8016
|
}
|
|
7582
8017
|
async executeOp(handle, operation, args) {
|
|
7583
8018
|
const s = this.s(handle);
|
|
8019
|
+
const agentTarget = [...s.inlineOperationTargets.values()].find(
|
|
8020
|
+
(target) => target.isAgentTurnActive()
|
|
8021
|
+
);
|
|
8022
|
+
if (agentTarget && AGENT_INLINE_OPERATIONS.has(operation)) {
|
|
8023
|
+
return agentTarget.executeAgentOp(operation, args);
|
|
8024
|
+
}
|
|
8025
|
+
if (operation === "applyApproval" || operation === "applyInput") {
|
|
8026
|
+
const parentNodeId = String(args.nodeId ?? "");
|
|
8027
|
+
const target = s.inlineOperationTargets.get(parentNodeId);
|
|
8028
|
+
if (target) {
|
|
8029
|
+
const projection2 = operation === "applyApproval" ? await target.applyParentApproval(args) : await target.applyParentInput(args);
|
|
8030
|
+
this.projectInlineInteraction(handle, parentNodeId, projection2);
|
|
8031
|
+
return JSON.stringify(s.execution);
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
if (operation === "cancel") {
|
|
8035
|
+
await this.cancelInlineTargets(s);
|
|
8036
|
+
this.cancel(handle);
|
|
8037
|
+
return JSON.stringify(s.execution);
|
|
8038
|
+
}
|
|
8039
|
+
if (operation === "hydrate" || operation === "start") {
|
|
8040
|
+
await this.cancelInlineTargets(s);
|
|
8041
|
+
}
|
|
7584
8042
|
switch (operation) {
|
|
7585
8043
|
case "get_available":
|
|
7586
8044
|
return this.opGetAvailable(s);
|
|
@@ -7613,8 +8071,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7613
8071
|
case "applyInput":
|
|
7614
8072
|
return this.opApplyInput(handle, s, args);
|
|
7615
8073
|
case "cancel":
|
|
7616
|
-
|
|
7617
|
-
return JSON.stringify(s.execution);
|
|
8074
|
+
throw new Error("FlowAdapter: cancel routing invariant violated");
|
|
7618
8075
|
case "setAutonomousMode":
|
|
7619
8076
|
return this.opSetAutonomousMode(handle, s, args);
|
|
7620
8077
|
case "retryNode":
|
|
@@ -7629,14 +8086,18 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7629
8086
|
}
|
|
7630
8087
|
// ── executeOp branch handlers (private dispatch helpers) ─────────────────
|
|
7631
8088
|
opGetAvailable(s) {
|
|
8089
|
+
const agentNodeIds = new Set(s.flowDef.nodes.filter(isAgentNode).map((node) => node.id));
|
|
7632
8090
|
const nodes = Object.values(s.execution.nodes);
|
|
7633
|
-
const available = nodes.filter((n) => n.status === "available").map((n) => n.id);
|
|
8091
|
+
const available = nodes.filter((n) => agentNodeIds.has(n.id) && n.status === "available").map((n) => n.id);
|
|
7634
8092
|
return JSON.stringify(available);
|
|
7635
8093
|
}
|
|
7636
8094
|
opValidateNode(s, args) {
|
|
7637
8095
|
const nodeId = String(args.nodeId ?? "");
|
|
7638
8096
|
const node = s.execution.nodes[nodeId];
|
|
7639
8097
|
if (!node) return JSON.stringify({ valid: false, reason: "Node not found" });
|
|
8098
|
+
if (!isAgentNode(findNode(s.flowDef, nodeId))) {
|
|
8099
|
+
return JSON.stringify({ valid: false, reason: "Node is not an active agent node" });
|
|
8100
|
+
}
|
|
7640
8101
|
const predsMet = flowPredecessors(s.flowDef, nodeId).every((pid) => {
|
|
7641
8102
|
const predStatus = s.execution.nodes[pid]?.status ?? "not_started";
|
|
7642
8103
|
return predStatus === "complete" || predStatus === "skipped";
|
|
@@ -7644,7 +8105,8 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7644
8105
|
return JSON.stringify({
|
|
7645
8106
|
valid: node.status === "available",
|
|
7646
8107
|
status: node.status,
|
|
7647
|
-
predecessorsReady: predsMet
|
|
8108
|
+
predecessorsReady: predsMet,
|
|
8109
|
+
failedPreconditions: node.failedPreconditions ?? []
|
|
7648
8110
|
});
|
|
7649
8111
|
}
|
|
7650
8112
|
opStartNode(handle, args) {
|
|
@@ -7665,12 +8127,14 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7665
8127
|
}
|
|
7666
8128
|
opCompleteNode(handle, args) {
|
|
7667
8129
|
const nodeId = String(args.nodeId ?? "");
|
|
8130
|
+
const parsedFields = args.fields === void 0 ? { issues: [] } : safeParseFields(args.fields);
|
|
7668
8131
|
const output = {
|
|
7669
8132
|
summary: String(args.summary ?? "Completed"),
|
|
7670
8133
|
artifacts: safeParseArtifacts(args.artifacts),
|
|
7671
|
-
concerns: safeParseStringArray(args.concerns)
|
|
8134
|
+
concerns: safeParseStringArray(args.concerns),
|
|
8135
|
+
...parsedFields.fields ? { fields: parsedFields.fields } : {}
|
|
7672
8136
|
};
|
|
7673
|
-
return this.doCompleteNode(handle, nodeId, output);
|
|
8137
|
+
return this.doCompleteNode(handle, nodeId, output, parsedFields.issues);
|
|
7674
8138
|
}
|
|
7675
8139
|
opSkipNode(handle, args) {
|
|
7676
8140
|
const nodeId = String(args.nodeId ?? "");
|
|
@@ -7727,6 +8191,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7727
8191
|
throw new Error("FlowAdapter: start requires args.seed.runId + args.seed.startedBy");
|
|
7728
8192
|
}
|
|
7729
8193
|
s.flowDef = flow;
|
|
8194
|
+
s.executionGeneration += 1;
|
|
7730
8195
|
s.execution = buildInitialExecution(flow, {
|
|
7731
8196
|
runId: seed.runId,
|
|
7732
8197
|
startedBy: seed.startedBy,
|
|
@@ -7739,6 +8204,10 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7739
8204
|
// ── Agent-side mutations (tool calls) ─────────────────────────────────────
|
|
7740
8205
|
doStartNode(handle, nodeId) {
|
|
7741
8206
|
return this.mutate(handle, (s) => {
|
|
8207
|
+
assertLegal(
|
|
8208
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8209
|
+
`node is not an active agent node: ${nodeId}`
|
|
8210
|
+
);
|
|
7742
8211
|
const node = s.execution.nodes[nodeId];
|
|
7743
8212
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7744
8213
|
assertLegal(node.status === "available", `cannot start ${nodeId}: status is ${node.status}`);
|
|
@@ -7752,11 +8221,17 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7752
8221
|
}
|
|
7753
8222
|
doRequestApproval(handle, nodeId, summary, artifacts) {
|
|
7754
8223
|
return this.mutate(handle, (s) => {
|
|
8224
|
+
const definition = findNode(s.flowDef, nodeId);
|
|
7755
8225
|
const node = s.execution.nodes[nodeId];
|
|
7756
8226
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
8227
|
+
const rejectedGateRerequest = isGateNode(definition) && node.status === "awaiting_approval" && node.approval?.decision === "rejected";
|
|
8228
|
+
assertLegal(
|
|
8229
|
+
isAgentNode(definition) || rejectedGateRerequest,
|
|
8230
|
+
`node is not an active agent node or rejected gate: ${nodeId}`
|
|
8231
|
+
);
|
|
7757
8232
|
assertLegal(
|
|
7758
|
-
node.status === "running",
|
|
7759
|
-
`request_approval requires running, got ${node.status}`
|
|
8233
|
+
isAgentNode(definition) && node.status === "running" || rejectedGateRerequest,
|
|
8234
|
+
`request_approval requires a running agent node or rejected gate, got ${node.status}`
|
|
7760
8235
|
);
|
|
7761
8236
|
const approval = { requestedAt: now(), summary };
|
|
7762
8237
|
const history = [...node.approvalHistory, approval];
|
|
@@ -7774,6 +8249,10 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7774
8249
|
}
|
|
7775
8250
|
doRequestInput(handle, nodeId, prompt, schema) {
|
|
7776
8251
|
return this.mutate(handle, (s) => {
|
|
8252
|
+
assertLegal(
|
|
8253
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8254
|
+
`node is not an active agent node: ${nodeId}`
|
|
8255
|
+
);
|
|
7777
8256
|
const node = s.execution.nodes[nodeId];
|
|
7778
8257
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7779
8258
|
assertLegal(
|
|
@@ -7796,20 +8275,64 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7796
8275
|
return JSON.stringify({ ok: true, status: "awaiting_input" });
|
|
7797
8276
|
});
|
|
7798
8277
|
}
|
|
7799
|
-
doCompleteNode(handle, nodeId, output) {
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
8278
|
+
doCompleteNode(handle, nodeId, output, fieldIssues = []) {
|
|
8279
|
+
const state = this.s(handle);
|
|
8280
|
+
assertLegal(
|
|
8281
|
+
isAgentNode(findNode(state.flowDef, nodeId)),
|
|
8282
|
+
`node is not an active agent node: ${nodeId}`
|
|
8283
|
+
);
|
|
8284
|
+
const node = state.execution.nodes[nodeId];
|
|
8285
|
+
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
8286
|
+
const autonomous = state.execution.autonomousMode;
|
|
8287
|
+
const mandatory = requiresAgentApproval(state.flowDef, nodeId);
|
|
8288
|
+
const approvedPrior = node.status === "awaiting_approval" && node.approval?.decision === "approved";
|
|
8289
|
+
const runningAutonomous = node.status === "running" && autonomous && !mandatory;
|
|
8290
|
+
assertLegal(
|
|
8291
|
+
approvedPrior || runningAutonomous,
|
|
8292
|
+
mandatory && node.status === "running" ? `complete_node blocked: node ${nodeId} is a mandatory approval gate \u2014 call request_approval and wait for an approved decision (autonomous mode cannot skip it)` : `complete_node requires awaiting_approval+approved OR running+autonomous; node is ${node.status}${node.approval?.decision ? ` with approval ${node.approval.decision}` : ""}, autonomous=${autonomous}`
|
|
8293
|
+
);
|
|
8294
|
+
const validation = fieldIssues.length > 0 && getConsumedOutputSchema(state.flowDef, nodeId) ? { valid: false, issues: fieldIssues } : validateConsumedNodeOutput(state.flowDef, nodeId, output.fields);
|
|
8295
|
+
if (!validation.valid) {
|
|
8296
|
+
const issueText = validation.issues.join("; ");
|
|
8297
|
+
if (!node.outputCorrection) {
|
|
8298
|
+
const correction = {
|
|
8299
|
+
requestedAt: now(),
|
|
8300
|
+
issues: validation.issues
|
|
8301
|
+
};
|
|
8302
|
+
this.mutate(handle, (current) => {
|
|
8303
|
+
const execution = current.execution.nodes[nodeId];
|
|
8304
|
+
assertLegal(execution, `node not in execution: ${nodeId}`);
|
|
8305
|
+
current.execution.nodes[nodeId] = { ...execution, outputCorrection: correction };
|
|
8306
|
+
});
|
|
8307
|
+
throw new Error(
|
|
8308
|
+
`FlowAdapter: output for node ${nodeId} does not match its consumed schema. Submit one corrected complete_node call. ${issueText}`
|
|
8309
|
+
);
|
|
8310
|
+
}
|
|
8311
|
+
this.mutate(handle, (current) => {
|
|
8312
|
+
const execution = current.execution.nodes[nodeId];
|
|
8313
|
+
assertLegal(execution, `node not in execution: ${nodeId}`);
|
|
8314
|
+
const error = {
|
|
8315
|
+
message: `Node ${nodeId} output failed schema validation after one correction: ${issueText}`,
|
|
8316
|
+
recoverable: false,
|
|
8317
|
+
at: now()
|
|
8318
|
+
};
|
|
8319
|
+
current.execution.nodes[nodeId] = {
|
|
8320
|
+
...execution,
|
|
8321
|
+
status: "failed",
|
|
8322
|
+
completedAt: now(),
|
|
8323
|
+
error,
|
|
8324
|
+
errorHistory: [...execution.errorHistory, error]
|
|
8325
|
+
};
|
|
8326
|
+
});
|
|
8327
|
+
throw new Error(
|
|
8328
|
+
`FlowAdapter: node ${nodeId} failed after one output correction. ${issueText}`
|
|
7810
8329
|
);
|
|
7811
|
-
|
|
7812
|
-
|
|
8330
|
+
}
|
|
8331
|
+
return this.mutate(handle, (current) => {
|
|
8332
|
+
const execution = current.execution.nodes[nodeId];
|
|
8333
|
+
assertLegal(execution, `node not in execution: ${nodeId}`);
|
|
8334
|
+
current.execution.nodes[nodeId] = {
|
|
8335
|
+
...execution,
|
|
7813
8336
|
status: "complete",
|
|
7814
8337
|
completedAt: now(),
|
|
7815
8338
|
output
|
|
@@ -7819,10 +8342,14 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7819
8342
|
}
|
|
7820
8343
|
doSkipNode(handle, nodeId) {
|
|
7821
8344
|
return this.mutate(handle, (s) => {
|
|
8345
|
+
assertLegal(
|
|
8346
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8347
|
+
`node is not an active agent node: ${nodeId}`
|
|
8348
|
+
);
|
|
7822
8349
|
const node = s.execution.nodes[nodeId];
|
|
7823
8350
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7824
8351
|
assertLegal(
|
|
7825
|
-
!
|
|
8352
|
+
!requiresAgentApproval(s.flowDef, nodeId),
|
|
7826
8353
|
`cannot skip node ${nodeId}: it is a mandatory approval gate \u2014 call request_approval and wait for an approved decision`
|
|
7827
8354
|
);
|
|
7828
8355
|
assertLegal(isOptional(s.flowDef, nodeId), `cannot skip required node ${nodeId}`);
|
|
@@ -7836,6 +8363,10 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7836
8363
|
}
|
|
7837
8364
|
doFailNode(handle, nodeId, message, recoverable) {
|
|
7838
8365
|
return this.mutate(handle, (s) => {
|
|
8366
|
+
assertLegal(
|
|
8367
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8368
|
+
`node is not an active agent node: ${nodeId}`
|
|
8369
|
+
);
|
|
7839
8370
|
const node = s.execution.nodes[nodeId];
|
|
7840
8371
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7841
8372
|
const error = { message, recoverable, at: now() };
|
|
@@ -7861,18 +8392,24 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7861
8392
|
// ── Host-side mutations (not exposed as agent tools) ─────────────────────
|
|
7862
8393
|
/**
|
|
7863
8394
|
* Record an approval decision on a node currently `awaiting_approval`.
|
|
7864
|
-
*
|
|
7865
|
-
*
|
|
7866
|
-
* node back to `available` so the agent can retry.
|
|
8395
|
+
* A first-class gate completes directly when approved and remains parked when rejected.
|
|
8396
|
+
* Agent-node approvals keep their existing resume behavior.
|
|
7867
8397
|
*/
|
|
7868
8398
|
applyApproval(handle, nodeId, decision, feedback, decidedBy) {
|
|
7869
8399
|
this.mutate(handle, (s) => {
|
|
8400
|
+
const definition = findNode(s.flowDef, nodeId);
|
|
7870
8401
|
const node = s.execution.nodes[nodeId];
|
|
7871
8402
|
assertLegal(node, `applyApproval: unknown node ${nodeId}`);
|
|
7872
8403
|
assertLegal(
|
|
7873
8404
|
node.status === "awaiting_approval",
|
|
7874
8405
|
`applyApproval: node ${nodeId} is ${node.status}, not awaiting_approval`
|
|
7875
8406
|
);
|
|
8407
|
+
if (isGateNode(definition)) {
|
|
8408
|
+
assertLegal(
|
|
8409
|
+
node.approval?.decidedAt == null && node.approval?.decision == null,
|
|
8410
|
+
`applyApproval: gate ${nodeId} approval attempt is already decided; request approval again before applying another decision`
|
|
8411
|
+
);
|
|
8412
|
+
}
|
|
7876
8413
|
const base = node.approval ?? { requestedAt: now(), summary: "" };
|
|
7877
8414
|
const approval = {
|
|
7878
8415
|
...base,
|
|
@@ -7884,7 +8421,22 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7884
8421
|
const history = [...node.approvalHistory];
|
|
7885
8422
|
if (history.length > 0) history[history.length - 1] = approval;
|
|
7886
8423
|
else history.push(approval);
|
|
7887
|
-
if (decision === "approved") {
|
|
8424
|
+
if (isGateNode(definition) && decision === "approved") {
|
|
8425
|
+
s.execution.nodes[nodeId] = {
|
|
8426
|
+
...node,
|
|
8427
|
+
status: "complete",
|
|
8428
|
+
completedAt: now(),
|
|
8429
|
+
approval,
|
|
8430
|
+
approvalHistory: history
|
|
8431
|
+
};
|
|
8432
|
+
} else if (isGateNode(definition)) {
|
|
8433
|
+
s.execution.nodes[nodeId] = {
|
|
8434
|
+
...node,
|
|
8435
|
+
status: "awaiting_approval",
|
|
8436
|
+
approval,
|
|
8437
|
+
approvalHistory: history
|
|
8438
|
+
};
|
|
8439
|
+
} else if (decision === "approved") {
|
|
7888
8440
|
s.execution.nodes[nodeId] = { ...node, approval, approvalHistory: history };
|
|
7889
8441
|
} else {
|
|
7890
8442
|
s.execution.nodes[nodeId] = {
|
|
@@ -7968,9 +8520,296 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7968
8520
|
getExecution(handle) {
|
|
7969
8521
|
return this.s(handle).execution;
|
|
7970
8522
|
}
|
|
7971
|
-
/**
|
|
8523
|
+
/** @internal Whether a nested inline descendant owns the current agent turn. */
|
|
8524
|
+
hasActiveInlineAgentTurn(handle) {
|
|
8525
|
+
return [...this.s(handle).inlineOperationTargets.values()].some(
|
|
8526
|
+
(target) => target.isAgentTurnActive()
|
|
8527
|
+
);
|
|
8528
|
+
}
|
|
8529
|
+
/** Snapshot currently visible through agent-facing tools, including a live inline child. */
|
|
8530
|
+
getAgentExecution(handle) {
|
|
8531
|
+
const state = this.s(handle);
|
|
8532
|
+
return [...state.inlineOperationTargets.values()].find((target) => target.isAgentTurnActive())?.getExecution() ?? state.execution;
|
|
8533
|
+
}
|
|
8534
|
+
/** Agent node IDs whose approval policy remains mandatory in autonomous mode. */
|
|
8535
|
+
getMandatoryAgentApprovalNodeIds(handle) {
|
|
8536
|
+
const state = this.s(handle);
|
|
8537
|
+
const target = [...state.inlineOperationTargets.values()].find(
|
|
8538
|
+
(candidate) => candidate.isAgentTurnActive()
|
|
8539
|
+
);
|
|
8540
|
+
if (target) return target.getMandatoryAgentApprovalNodeIds();
|
|
8541
|
+
return state.flowDef.nodes.filter(nodeRequiresApproval).map((n) => n.id);
|
|
8542
|
+
}
|
|
8543
|
+
/** @deprecated Use {@link getMandatoryAgentApprovalNodeIds}. */
|
|
7972
8544
|
getMandatoryGates(handle) {
|
|
7973
|
-
return this.
|
|
8545
|
+
return this.getMandatoryAgentApprovalNodeIds(handle);
|
|
8546
|
+
}
|
|
8547
|
+
/**
|
|
8548
|
+
* Decide whether the current state and stimulus require the session agent's conversation.
|
|
8549
|
+
* Runtime-owned gates stay dark while pending; a rejected gate is the exception because the
|
|
8550
|
+
* rejection starts the revision conversation promised by the gate lifecycle.
|
|
8551
|
+
*/
|
|
8552
|
+
shouldDriveAgentTurn(handle, stimulus) {
|
|
8553
|
+
const state = this.s(handle);
|
|
8554
|
+
const execution = state.execution;
|
|
8555
|
+
if (execution.done || execution.status === "failed" || execution.status === "cancelled") {
|
|
8556
|
+
return false;
|
|
8557
|
+
}
|
|
8558
|
+
if (stimulus.kind === "user_message") return true;
|
|
8559
|
+
for (const node of state.flowDef.nodes) {
|
|
8560
|
+
const current = execution.nodes[node.id];
|
|
8561
|
+
if (isGateNode(node) && current?.status === "awaiting_approval" && current.approval?.decision === "rejected") {
|
|
8562
|
+
return true;
|
|
8563
|
+
}
|
|
8564
|
+
}
|
|
8565
|
+
for (const node of state.flowDef.nodes) {
|
|
8566
|
+
if (!isAgentNode(node)) continue;
|
|
8567
|
+
const current = execution.nodes[node.id];
|
|
8568
|
+
if (!current) continue;
|
|
8569
|
+
if (current.status === "available" || current.status === "running") return true;
|
|
8570
|
+
if (current.status === "awaiting_approval" && current.approval?.decision === "approved") {
|
|
8571
|
+
return true;
|
|
8572
|
+
}
|
|
8573
|
+
}
|
|
8574
|
+
return false;
|
|
8575
|
+
}
|
|
8576
|
+
/**
|
|
8577
|
+
* Drain runtime-owned nodes in definition order until only agent work or a human decision
|
|
8578
|
+
* remains. Function nodes use NodeExecutor, gates park, and sub-flows delegate to the host.
|
|
8579
|
+
*/
|
|
8580
|
+
async drainRuntimeNodes(handle) {
|
|
8581
|
+
return this.drainAvailableRuntimeNodes(handle, true);
|
|
8582
|
+
}
|
|
8583
|
+
/**
|
|
8584
|
+
* Run every currently available function node through the configured
|
|
8585
|
+
* NodeExecutor. Newly-unblocked function nodes are drained in definition
|
|
8586
|
+
* order before returning control to the runner's agent-turn decision.
|
|
8587
|
+
*/
|
|
8588
|
+
async executeAvailableFunctionNodes(handle) {
|
|
8589
|
+
return this.drainAvailableRuntimeNodes(handle, false);
|
|
8590
|
+
}
|
|
8591
|
+
async drainAvailableRuntimeNodes(handle, includeGates) {
|
|
8592
|
+
const advancedSubFlows = /* @__PURE__ */ new Set();
|
|
8593
|
+
while (true) {
|
|
8594
|
+
const state = this.s(handle);
|
|
8595
|
+
if (state.execution.status === "cancelled" || state.execution.status === "failed") break;
|
|
8596
|
+
const node = state.flowDef.nodes.find((candidate) => {
|
|
8597
|
+
const status = state.execution.nodes[candidate.id]?.status;
|
|
8598
|
+
if (isSubFlowNode(candidate) && !advancedSubFlows.has(candidate.id) && (status === "available" || status === "running")) {
|
|
8599
|
+
return true;
|
|
8600
|
+
}
|
|
8601
|
+
return (isFunctionNode(candidate) || includeGates && isGateNode(candidate)) && status === "available";
|
|
8602
|
+
});
|
|
8603
|
+
if (!node) break;
|
|
8604
|
+
if (isGateNode(node)) {
|
|
8605
|
+
this.mutate(
|
|
8606
|
+
handle,
|
|
8607
|
+
(current) => {
|
|
8608
|
+
const execution = current.execution.nodes[node.id];
|
|
8609
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8610
|
+
assertLegal(
|
|
8611
|
+
execution.status === "available",
|
|
8612
|
+
`cannot park gate node ${node.id}: status is ${execution.status}`
|
|
8613
|
+
);
|
|
8614
|
+
const approval = { requestedAt: now(), summary: node.run.prompt };
|
|
8615
|
+
current.execution.nodes[node.id] = {
|
|
8616
|
+
...execution,
|
|
8617
|
+
status: "awaiting_approval",
|
|
8618
|
+
approval,
|
|
8619
|
+
approvalHistory: [...execution.approvalHistory, approval]
|
|
8620
|
+
};
|
|
8621
|
+
},
|
|
8622
|
+
false
|
|
8623
|
+
);
|
|
8624
|
+
continue;
|
|
8625
|
+
}
|
|
8626
|
+
if (isSubFlowNode(node)) {
|
|
8627
|
+
advancedSubFlows.add(node.id);
|
|
8628
|
+
const execution = state.execution.nodes[node.id];
|
|
8629
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8630
|
+
if (execution.status === "available") {
|
|
8631
|
+
this.mutate(
|
|
8632
|
+
handle,
|
|
8633
|
+
(current) => {
|
|
8634
|
+
const currentExecution = current.execution.nodes[node.id];
|
|
8635
|
+
assertLegal(currentExecution, `node not in execution: ${node.id}`);
|
|
8636
|
+
assertLegal(
|
|
8637
|
+
currentExecution.status === "available",
|
|
8638
|
+
`cannot execute sub-flow node ${node.id}: status is ${currentExecution.status}`
|
|
8639
|
+
);
|
|
8640
|
+
current.execution.nodes[node.id] = {
|
|
8641
|
+
...currentExecution,
|
|
8642
|
+
status: "running",
|
|
8643
|
+
startedAt: now()
|
|
8644
|
+
};
|
|
8645
|
+
},
|
|
8646
|
+
false
|
|
8647
|
+
);
|
|
8648
|
+
}
|
|
8649
|
+
const executionGeneration = state.executionGeneration;
|
|
8650
|
+
let result;
|
|
8651
|
+
try {
|
|
8652
|
+
const runner = state.inlineSubFlowRunner;
|
|
8653
|
+
if (!runner) {
|
|
8654
|
+
throw new Error(`no inline sub-flow runner configured for node ${node.id}`);
|
|
8655
|
+
}
|
|
8656
|
+
const resolved = resolveNodeBindings(
|
|
8657
|
+
state.flowDef,
|
|
8658
|
+
node,
|
|
8659
|
+
snapshotMap(state.execution),
|
|
8660
|
+
state.flowInputOrigins
|
|
8661
|
+
);
|
|
8662
|
+
result = await runner.run({
|
|
8663
|
+
node,
|
|
8664
|
+
flowId: node.run.flow,
|
|
8665
|
+
inputs: resolved.values,
|
|
8666
|
+
inputOrigins: {
|
|
8667
|
+
...node.run.passContext ? state.flowInputOrigins : {},
|
|
8668
|
+
...resolved.origins
|
|
8669
|
+
},
|
|
8670
|
+
passContext: node.run.passContext ?? false,
|
|
8671
|
+
parentFlow: state.flowDef,
|
|
8672
|
+
parentExecution: state.execution,
|
|
8673
|
+
ancestry: state.inlineSubFlowAncestry,
|
|
8674
|
+
routeOperationsTo: (target) => {
|
|
8675
|
+
if (target) state.inlineOperationTargets.set(node.id, target);
|
|
8676
|
+
else state.inlineOperationTargets.delete(node.id);
|
|
8677
|
+
}
|
|
8678
|
+
});
|
|
8679
|
+
} catch (error) {
|
|
8680
|
+
result = {
|
|
8681
|
+
status: "failed",
|
|
8682
|
+
error: {
|
|
8683
|
+
message: `Sub-flow node ${node.id} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
8684
|
+
recoverable: false,
|
|
8685
|
+
at: now()
|
|
8686
|
+
}
|
|
8687
|
+
};
|
|
8688
|
+
}
|
|
8689
|
+
if (this.s(handle).executionGeneration !== executionGeneration) break;
|
|
8690
|
+
if (result.status === "paused") {
|
|
8691
|
+
this.projectInlineInteraction(handle, node.id, result.projection);
|
|
8692
|
+
continue;
|
|
8693
|
+
}
|
|
8694
|
+
if (this.s(handle).execution.status === "cancelled") break;
|
|
8695
|
+
this.mutate(
|
|
8696
|
+
handle,
|
|
8697
|
+
(current) => {
|
|
8698
|
+
const execution2 = current.execution.nodes[node.id];
|
|
8699
|
+
assertLegal(execution2, `node not in execution: ${node.id}`);
|
|
8700
|
+
if (result.status === "complete") {
|
|
8701
|
+
current.execution.nodes[node.id] = {
|
|
8702
|
+
...execution2,
|
|
8703
|
+
status: "complete",
|
|
8704
|
+
completedAt: now(),
|
|
8705
|
+
output: result.output
|
|
8706
|
+
};
|
|
8707
|
+
return;
|
|
8708
|
+
}
|
|
8709
|
+
current.execution.nodes[node.id] = {
|
|
8710
|
+
...execution2,
|
|
8711
|
+
status: nodeIsOptional(node) ? "skipped" : "failed",
|
|
8712
|
+
completedAt: now(),
|
|
8713
|
+
output: result.output,
|
|
8714
|
+
error: result.error,
|
|
8715
|
+
errorHistory: [...execution2.errorHistory, result.error]
|
|
8716
|
+
};
|
|
8717
|
+
},
|
|
8718
|
+
false
|
|
8719
|
+
);
|
|
8720
|
+
continue;
|
|
8721
|
+
}
|
|
8722
|
+
if (!isFunctionNode(node)) continue;
|
|
8723
|
+
this.mutate(
|
|
8724
|
+
handle,
|
|
8725
|
+
(current) => {
|
|
8726
|
+
const execution = current.execution.nodes[node.id];
|
|
8727
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8728
|
+
assertLegal(
|
|
8729
|
+
execution.status === "available",
|
|
8730
|
+
`cannot execute function node ${node.id}: status is ${execution.status}`
|
|
8731
|
+
);
|
|
8732
|
+
current.execution.nodes[node.id] = {
|
|
8733
|
+
...execution,
|
|
8734
|
+
status: "running",
|
|
8735
|
+
startedAt: now()
|
|
8736
|
+
};
|
|
8737
|
+
},
|
|
8738
|
+
false
|
|
8739
|
+
);
|
|
8740
|
+
let capturedOutput;
|
|
8741
|
+
try {
|
|
8742
|
+
const executor = state.nodeExecutor;
|
|
8743
|
+
if (!executor) throw new Error(`no NodeExecutor configured for function node ${node.id}`);
|
|
8744
|
+
const resolved = resolveNodeBindings(
|
|
8745
|
+
state.flowDef,
|
|
8746
|
+
node,
|
|
8747
|
+
snapshotMap(state.execution),
|
|
8748
|
+
state.flowInputOrigins
|
|
8749
|
+
);
|
|
8750
|
+
const result = await executor.execute({
|
|
8751
|
+
node,
|
|
8752
|
+
inputs: resolved.values,
|
|
8753
|
+
inputOrigins: resolved.origins
|
|
8754
|
+
});
|
|
8755
|
+
capturedOutput = functionNodeOutput(node, result);
|
|
8756
|
+
const successfulStatuses = node.run.successExit ?? [0];
|
|
8757
|
+
if (!successfulStatuses.includes(result.exitStatus)) {
|
|
8758
|
+
const detail = result.stderr.trim() || result.stdout.trim();
|
|
8759
|
+
throw new Error(
|
|
8760
|
+
`Function node ${node.id} exited with unsuccessful exit status ${result.exitStatus}${detail ? `: ${detail}` : ""}`
|
|
8761
|
+
);
|
|
8762
|
+
}
|
|
8763
|
+
const validation = validateConsumedNodeOutput(
|
|
8764
|
+
state.flowDef,
|
|
8765
|
+
node.id,
|
|
8766
|
+
capturedOutput.fields
|
|
8767
|
+
);
|
|
8768
|
+
if (!validation.valid) {
|
|
8769
|
+
throw new Error(
|
|
8770
|
+
`output does not match its consumed schema: ${validation.issues.join("; ")}`
|
|
8771
|
+
);
|
|
8772
|
+
}
|
|
8773
|
+
this.mutate(
|
|
8774
|
+
handle,
|
|
8775
|
+
(current) => {
|
|
8776
|
+
const execution = current.execution.nodes[node.id];
|
|
8777
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8778
|
+
current.execution.nodes[node.id] = {
|
|
8779
|
+
...execution,
|
|
8780
|
+
status: "complete",
|
|
8781
|
+
completedAt: now(),
|
|
8782
|
+
output: capturedOutput
|
|
8783
|
+
};
|
|
8784
|
+
},
|
|
8785
|
+
false
|
|
8786
|
+
);
|
|
8787
|
+
} catch (error) {
|
|
8788
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8789
|
+
this.mutate(
|
|
8790
|
+
handle,
|
|
8791
|
+
(current) => {
|
|
8792
|
+
const execution = current.execution.nodes[node.id];
|
|
8793
|
+
assertLegal(execution, `node not in execution: ${node.id}`);
|
|
8794
|
+
const nodeError = {
|
|
8795
|
+
message: `Function node ${node.id} failed: ${message}`,
|
|
8796
|
+
recoverable: false,
|
|
8797
|
+
at: now()
|
|
8798
|
+
};
|
|
8799
|
+
current.execution.nodes[node.id] = {
|
|
8800
|
+
...execution,
|
|
8801
|
+
status: "failed",
|
|
8802
|
+
completedAt: now(),
|
|
8803
|
+
output: capturedOutput,
|
|
8804
|
+
error: nodeError,
|
|
8805
|
+
errorHistory: [...execution.errorHistory, nodeError]
|
|
8806
|
+
};
|
|
8807
|
+
},
|
|
8808
|
+
false
|
|
8809
|
+
);
|
|
8810
|
+
}
|
|
8811
|
+
}
|
|
8812
|
+
return this.getExecution(handle);
|
|
7974
8813
|
}
|
|
7975
8814
|
// ── Private helpers ──
|
|
7976
8815
|
s(handle) {
|
|
@@ -7979,23 +8818,21 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7979
8818
|
/**
|
|
7980
8819
|
* Central mutation pipeline: runs the mutator, recomputes derived
|
|
7981
8820
|
* fields, updates the flow-level `status` if the mutation completed
|
|
7982
|
-
* or failed the flow, fires `onChange` once, and
|
|
7983
|
-
*
|
|
7984
|
-
*
|
|
7985
|
-
*
|
|
7986
|
-
* The bus signal path is gated by the `useBusForStimulus` flag set at
|
|
7987
|
-
* connect time — Phase 3 builds the path; Phase 4 flips the default to
|
|
7988
|
-
* true globally and removes the orchestrator's onStateChange
|
|
7989
|
-
* subscription.
|
|
8821
|
+
* or failed the flow, fires `onChange` once, and optionally signals the
|
|
8822
|
+
* {@link FlowAdapterStimulusBus}. The signal path is gated by the
|
|
8823
|
+
* `useBusForStimulus` flag set at connect time.
|
|
7990
8824
|
*/
|
|
7991
|
-
mutate(handle, fn) {
|
|
8825
|
+
mutate(handle, fn, signalStimulus = true) {
|
|
7992
8826
|
const s = this.s(handle);
|
|
7993
8827
|
const previousExecution = s.lastSignaledExecution;
|
|
7994
|
-
s.execution = {
|
|
8828
|
+
s.execution = {
|
|
8829
|
+
...s.execution,
|
|
8830
|
+
nodes: nodeExecutionRecord(Object.entries(s.execution.nodes))
|
|
8831
|
+
};
|
|
7995
8832
|
const result = fn(s);
|
|
7996
8833
|
s.execution = this.recompute(s);
|
|
7997
8834
|
if (s.onChange) s.onChange(s.execution);
|
|
7998
|
-
if (s.useBusForStimulus && s.stimulusBus) {
|
|
8835
|
+
if (signalStimulus && s.useBusForStimulus && s.stimulusBus) {
|
|
7999
8836
|
const stim = computeStimulus(previousExecution, s.execution);
|
|
8000
8837
|
if (stim) {
|
|
8001
8838
|
const fragment = renderStimulusPrompt(stim);
|
|
@@ -8018,17 +8855,23 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8018
8855
|
recompute(s) {
|
|
8019
8856
|
const snapshots = snapshotMap(s.execution);
|
|
8020
8857
|
const computed = computeFlowStateFromSnapshots(s.flowDef, snapshots);
|
|
8021
|
-
const nodes =
|
|
8858
|
+
const nodes = nodeExecutionRecord(Object.entries(s.execution.nodes));
|
|
8022
8859
|
for (const ns of computed.nodes) {
|
|
8023
8860
|
const existing = nodes[ns.nodeId];
|
|
8024
8861
|
if (!existing) continue;
|
|
8025
|
-
if (existing.status === "not_started"
|
|
8026
|
-
|
|
8862
|
+
if (existing.status === "not_started" || existing.status === "available") {
|
|
8863
|
+
const hasFailedPreconditions = (ns.failedPreconditions?.length ?? 0) > 0;
|
|
8864
|
+
const status = existing.status === "available" && ns.status === "not_started" && !hasFailedPreconditions ? "available" : ns.status;
|
|
8865
|
+
nodes[ns.nodeId] = {
|
|
8866
|
+
...existing,
|
|
8867
|
+
status,
|
|
8868
|
+
failedPreconditions: hasFailedPreconditions ? ns.failedPreconditions : void 0
|
|
8869
|
+
};
|
|
8027
8870
|
}
|
|
8028
8871
|
}
|
|
8029
8872
|
let flowStatus = s.execution.status;
|
|
8030
8873
|
if (flowStatus !== "cancelled") {
|
|
8031
|
-
const anyRequiredFailed = s.flowDef.nodes.filter((n) =>
|
|
8874
|
+
const anyRequiredFailed = s.flowDef.nodes.filter((n) => isActiveRuntimeNode(n) && !nodeIsOptional(n)).some((n) => nodes[n.id]?.status === "failed");
|
|
8032
8875
|
if (anyRequiredFailed) {
|
|
8033
8876
|
flowStatus = "failed";
|
|
8034
8877
|
} else if (computed.done) {
|
|
@@ -8058,20 +8901,42 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8058
8901
|
`cannot build handoff for ${nodeId}: status is ${targetExecution.status}`
|
|
8059
8902
|
);
|
|
8060
8903
|
const sections = [`# Agent node: ${nodeLabel(target)}`, `Node ID: ${target.id}`];
|
|
8904
|
+
const resolved = resolveNodeBindings(
|
|
8905
|
+
s.flowDef,
|
|
8906
|
+
target,
|
|
8907
|
+
snapshotMap(s.execution),
|
|
8908
|
+
s.flowInputOrigins
|
|
8909
|
+
);
|
|
8061
8910
|
const instruction = nodeInstruction(target);
|
|
8062
|
-
if (instruction)
|
|
8911
|
+
if (instruction) {
|
|
8912
|
+
sections.push(
|
|
8913
|
+
`## Instruction
|
|
8063
8914
|
|
|
8064
|
-
${instruction}`
|
|
8065
|
-
|
|
8066
|
-
|
|
8915
|
+
${interpolateNodeInstruction(instruction, resolved.values)}`
|
|
8916
|
+
);
|
|
8917
|
+
}
|
|
8918
|
+
if (Object.keys(resolved.values).length > 0) {
|
|
8067
8919
|
sections.push(
|
|
8068
8920
|
`## Supplied bindings
|
|
8069
8921
|
|
|
8070
8922
|
\`\`\`json
|
|
8071
|
-
${JSON.stringify(
|
|
8923
|
+
${JSON.stringify(resolved.values, null, 2)}
|
|
8924
|
+
\`\`\``,
|
|
8925
|
+
`## Binding origins
|
|
8926
|
+
|
|
8927
|
+
\`\`\`json
|
|
8928
|
+
${JSON.stringify(resolved.origins, null, 2)}
|
|
8072
8929
|
\`\`\``
|
|
8073
8930
|
);
|
|
8074
8931
|
}
|
|
8932
|
+
if (targetExecution.outputCorrection) {
|
|
8933
|
+
sections.push(
|
|
8934
|
+
`## Output correction required
|
|
8935
|
+
|
|
8936
|
+
This node has one corrective output call available. Fix every issue before calling \`complete_node\` again:
|
|
8937
|
+
${targetExecution.outputCorrection.issues.map((issue) => `- ${issue}`).join("\n")}`
|
|
8938
|
+
);
|
|
8939
|
+
}
|
|
8075
8940
|
const assets = nodeAssets(target);
|
|
8076
8941
|
if (assets.length > 0) {
|
|
8077
8942
|
sections.push(`## Required assets
|
|
@@ -8104,6 +8969,26 @@ ${handoff.length > 0 ? handoff.join("\n\n") : "(no prior context)"}`
|
|
|
8104
8969
|
function flowPredecessors(flowDef, nodeId) {
|
|
8105
8970
|
return flowDef.edges.filter((e) => e.target === nodeId && e.type === "flow").map((e) => e.source);
|
|
8106
8971
|
}
|
|
8972
|
+
function functionNodeOutput(node, result) {
|
|
8973
|
+
const capturedFields = [];
|
|
8974
|
+
for (const channel of ["stdout", "stderr"]) {
|
|
8975
|
+
const destination = node.run.capture?.[channel];
|
|
8976
|
+
if (!destination) continue;
|
|
8977
|
+
const match = /^fields\.([^.]+)$/.exec(destination);
|
|
8978
|
+
if (!match?.[1]) {
|
|
8979
|
+
throw new Error(
|
|
8980
|
+
`Function node ${node.id} run.capture.${channel} destination "${destination}" is invalid; expected fields.<name>`
|
|
8981
|
+
);
|
|
8982
|
+
}
|
|
8983
|
+
capturedFields.push([match[1], result[channel]]);
|
|
8984
|
+
}
|
|
8985
|
+
const fields = Object.fromEntries(capturedFields);
|
|
8986
|
+
return {
|
|
8987
|
+
summary: `Function node ${node.id} exited with status ${result.exitStatus}.`,
|
|
8988
|
+
artifacts: [],
|
|
8989
|
+
...Object.keys(fields).length > 0 ? { fields } : {}
|
|
8990
|
+
};
|
|
8991
|
+
}
|
|
8107
8992
|
function safeParseStringArray(value) {
|
|
8108
8993
|
if (!value) return [];
|
|
8109
8994
|
if (Array.isArray(value)) return value.map(String);
|
|
@@ -8117,6 +9002,27 @@ function safeParseStringArray(value) {
|
|
|
8117
9002
|
}
|
|
8118
9003
|
return [];
|
|
8119
9004
|
}
|
|
9005
|
+
var JsonFieldsSchema = z4.record(z4.string(), z4.json());
|
|
9006
|
+
function safeParseFields(value) {
|
|
9007
|
+
let parsed = value;
|
|
9008
|
+
if (typeof value === "string") {
|
|
9009
|
+
try {
|
|
9010
|
+
parsed = JSON.parse(value);
|
|
9011
|
+
} catch {
|
|
9012
|
+
return { issues: ["fields: malformed JSON; expected an object"] };
|
|
9013
|
+
}
|
|
9014
|
+
}
|
|
9015
|
+
const result = JsonFieldsSchema.safeParse(parsed);
|
|
9016
|
+
if (!result.success) {
|
|
9017
|
+
return {
|
|
9018
|
+
issues: result.error.issues.map((issue) => {
|
|
9019
|
+
const suffix = issue.path.map(String).join(".");
|
|
9020
|
+
return `fields${suffix ? `.${suffix}` : ""}: ${issue.message}`;
|
|
9021
|
+
})
|
|
9022
|
+
};
|
|
9023
|
+
}
|
|
9024
|
+
return { fields: result.data, issues: [] };
|
|
9025
|
+
}
|
|
8120
9026
|
function safeParseArtifacts(value) {
|
|
8121
9027
|
if (!value) return [];
|
|
8122
9028
|
const raw = typeof value === "string" ? safeJsonArray(value) : Array.isArray(value) ? value : [];
|
|
@@ -8151,6 +9057,6 @@ function createConnector() {
|
|
|
8151
9057
|
return new FlowAdapter();
|
|
8152
9058
|
}
|
|
8153
9059
|
|
|
8154
|
-
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, 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 };
|
|
8155
|
-
//# sourceMappingURL=chunk-
|
|
8156
|
-
//# sourceMappingURL=chunk-
|
|
9060
|
+
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 };
|
|
9061
|
+
//# sourceMappingURL=chunk-EEHOMCRU.js.map
|
|
9062
|
+
//# sourceMappingURL=chunk-EEHOMCRU.js.map
|