@skaile/workspaces 2.2.0 → 2.4.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 +24 -0
- package/README.md +35 -12
- package/dist/{asset-feeds-D2LOXTOS.js → asset-feeds-4AWAT5PW.js} +6 -6
- package/dist/{asset-feeds-D2LOXTOS.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 +5 -2
- 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.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-TQIU4HQG.js → chunk-2QVPCZTL.js} +101 -40
- package/dist/chunk-2QVPCZTL.js.map +1 -0
- package/dist/{chunk-A67FJ4YT.js → chunk-3HVASHBF.js} +3 -3
- package/dist/{chunk-A67FJ4YT.js.map → chunk-3HVASHBF.js.map} +1 -1
- package/dist/chunk-4O2RAXKJ.js +446 -0
- package/dist/chunk-4O2RAXKJ.js.map +1 -0
- package/dist/chunk-4QROZZ5Z.js +50 -0
- package/dist/chunk-4QROZZ5Z.js.map +1 -0
- package/dist/{chunk-VSSAJYFK.js → chunk-57RFYEVM.js} +3 -3
- package/dist/{chunk-VSSAJYFK.js.map → chunk-57RFYEVM.js.map} +1 -1
- package/dist/{chunk-SZJCUC52.js → chunk-6BV5NAPD.js} +156 -28
- package/dist/chunk-6BV5NAPD.js.map +1 -0
- package/dist/{chunk-MAIKGYFR.js → chunk-AKVAZ5CB.js} +2 -2
- package/dist/{chunk-MAIKGYFR.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-6QSGCHE5.js → chunk-EEHOMCRU.js} +1071 -123
- package/dist/chunk-EEHOMCRU.js.map +1 -0
- package/dist/{chunk-6I5VRIX3.js → chunk-ENBTXMPL.js} +7 -7
- package/dist/{chunk-6I5VRIX3.js.map → chunk-ENBTXMPL.js.map} +1 -1
- package/dist/{chunk-Y662DNQQ.js → chunk-FKBQRV4J.js} +68 -24
- package/dist/chunk-FKBQRV4J.js.map +1 -0
- package/dist/{chunk-BUPFJFGP.js → chunk-G5V6YCHF.js} +3 -3
- package/dist/{chunk-BUPFJFGP.js.map → chunk-G5V6YCHF.js.map} +1 -1
- package/dist/{chunk-KGIVZOLU.js → chunk-GM2X24Q6.js} +2 -2
- package/dist/{chunk-KGIVZOLU.js.map → chunk-GM2X24Q6.js.map} +1 -1
- package/dist/{chunk-5BH6ZEGC.js → chunk-IHX2SN35.js} +9 -9
- package/dist/{chunk-5BH6ZEGC.js.map → chunk-IHX2SN35.js.map} +1 -1
- package/dist/{chunk-6HPTB6KT.js → chunk-JAFEMG7W.js} +4 -4
- package/dist/{chunk-6HPTB6KT.js.map → chunk-JAFEMG7W.js.map} +1 -1
- package/dist/{chunk-I5ESMJMW.js → chunk-MADO7PH5.js} +2 -2
- package/dist/{chunk-I5ESMJMW.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-MAN7AGQJ.js → chunk-NU4E7C5I.js} +25 -6
- package/dist/chunk-NU4E7C5I.js.map +1 -0
- package/dist/{chunk-RKMXBOIO.js → chunk-O7ROIKTF.js} +3 -3
- package/dist/{chunk-RKMXBOIO.js.map → chunk-O7ROIKTF.js.map} +1 -1
- package/dist/chunk-OEGHKO5N.js +1236 -0
- 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-IR6VPAVH.js → chunk-PJ6INYDD.js} +3 -3
- package/dist/{chunk-IR6VPAVH.js.map → chunk-PJ6INYDD.js.map} +1 -1
- package/dist/{chunk-DHGMYXOY.js → chunk-UGKOWIRP.js} +4 -4
- package/dist/{chunk-DHGMYXOY.js.map → chunk-UGKOWIRP.js.map} +1 -1
- package/dist/{chunk-WA7ADYSA.js → chunk-UZYTSIX6.js} +6 -6
- package/dist/{chunk-WA7ADYSA.js.map → chunk-UZYTSIX6.js.map} +1 -1
- package/dist/chunk-V4ZLZJFN.js +86 -0
- package/dist/chunk-V4ZLZJFN.js.map +1 -0
- package/dist/{chunk-GPMIXOJJ.js → chunk-WSARNOPP.js} +5 -5
- package/dist/{chunk-GPMIXOJJ.js.map → chunk-WSARNOPP.js.map} +1 -1
- package/dist/cli/index.js +49 -46
- package/dist/cli/index.js.map +1 -1
- package/dist/connectors/config.js +1 -1
- package/dist/connectors/index.js +6 -4
- 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/discovery/src/requires-graph.d.ts +1 -1
- package/dist/discovery/src/requires-graph.d.ts.map +1 -1
- package/dist/{ensure-sources-6QMXRI6D.js → ensure-sources-QF72HVUM.js} +8 -8
- package/dist/{ensure-sources-6QMXRI6D.js.map → ensure-sources-QF72HVUM.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +6 -4
- package/dist/factory-assets/connectors/deploy.js +6 -4
- package/dist/factory-assets/connectors/devserver.js +6 -4
- package/dist/factory-assets/connectors/flow/CONNECTOR.md +63 -10
- package/dist/factory-assets/connectors/flow/adapter.d.ts +79 -36
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +6 -4
- 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 +4 -1
- package/dist/factory-assets/connectors/flow/contract/normalize.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts +20 -8
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract.js +2 -1024
- package/dist/factory-assets/connectors/flow/contract.js.map +1 -1
- 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 +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/loader.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts +42 -0
- package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts.map +1 -0
- 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 +67 -33
- package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +4 -2
- 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 +5 -6
- package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/prompt-fragments.js +2 -1
- 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 -5
- 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 -4
- package/dist/factory-assets/connectors/git.js +6 -4
- package/dist/factory-assets/connectors/gmail.js +6 -4
- package/dist/factory-assets/connectors/googledrive.js +6 -4
- package/dist/factory-assets/connectors/local.js +6 -4
- package/dist/factory-assets/connectors/mattermost.js +6 -4
- package/dist/factory-assets/connectors/memory.js +6 -4
- package/dist/factory-assets/connectors/minio.js +6 -4
- package/dist/factory-assets/connectors/postgres.js +6 -4
- package/dist/factory-assets/connectors/s3.js +6 -4
- package/dist/factory-assets/connectors/sharepoint.js +6 -4
- package/dist/factory-assets/connectors/sqlite.js +6 -4
- package/dist/factory-assets/connectors/static-server.js +6 -4
- package/dist/factory-assets/connectors/tunnel.js +6 -4
- package/dist/factory-assets/connectors/webdav.js +6 -4
- package/dist/factory-assets/connectors/xstate-store.js +6 -4
- package/dist/factory-assets/connectors/xstate.js +6 -4
- package/dist/{flows-TDDBCLNF.js → flows-YIW4HL3L.js} +4 -4
- package/dist/{flows-TDDBCLNF.js.map → flows-YIW4HL3L.js.map} +1 -1
- package/dist/{helpers-COQ7TDD4.js → helpers-LURCZHA4.js} +8 -8
- package/dist/{helpers-COQ7TDD4.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-SNPER6PO.js → library-publish-2GMJM3NZ.js} +14 -12
- package/dist/library-publish-2GMJM3NZ.js.map +1 -0
- package/dist/{open-library-VH2VEYQ7.js → open-library-POFOXZWH.js} +12 -10
- package/dist/{open-library-VH2VEYQ7.js.map → open-library-POFOXZWH.js.map} +1 -1
- package/dist/{paths-XZA22IB4.js → paths-ZLX4ITAQ.js} +6 -6
- package/dist/{paths-XZA22IB4.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-FKL7PMJV.js → project-GVOPZEJZ.js} +9 -9
- package/dist/{project-FKL7PMJV.js.map → project-GVOPZEJZ.js.map} +1 -1
- package/dist/resolver/index.js +1 -1
- package/dist/runner/index.js +14 -8
- 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 -2
- package/dist/sdk/index.js +14 -8
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/resolver.js +1 -1
- package/dist/sdk/runner.js +14 -8
- package/dist/{setup-BM6HMYJJ.js → setup-TJYLYT2A.js} +5 -5
- package/dist/{setup-BM6HMYJJ.js.map → setup-TJYLYT2A.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-JIUBJC4F.js → source-FB3T4IHP.js} +13 -11
- package/dist/{source-JIUBJC4F.js.map → source-FB3T4IHP.js.map} +1 -1
- package/dist/{source-manifest-publish-TLH6UWWU.js → source-manifest-publish-XTRDVPSC.js} +15 -13
- package/dist/source-manifest-publish-XTRDVPSC.js.map +1 -0
- package/dist/tui/index.js +14 -8
- package/dist/tui/index.js.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/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-6QSGCHE5.js.map +0 -1
- package/dist/chunk-HPGV54O4.js +0 -301
- package/dist/chunk-HPGV54O4.js.map +0 -1
- package/dist/chunk-MAN7AGQJ.js.map +0 -1
- package/dist/chunk-SZJCUC52.js.map +0 -1
- package/dist/chunk-TQIU4HQG.js.map +0 -1
- package/dist/chunk-Y662DNQQ.js.map +0 -1
- package/dist/library-publish-SNPER6PO.js.map +0 -1
- package/dist/source-manifest-publish-TLH6UWWU.js.map +0 -1
|
@@ -1,14 +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 {
|
|
6
|
-
import {
|
|
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';
|
|
7
9
|
import { withFsDeadline, tryWithFsDeadline, FsDeadlineError, fsDeadlineMs } from './chunk-DYMWJAL4.js';
|
|
8
10
|
import { createLogger } from './chunk-7GOCNYUV.js';
|
|
11
|
+
import { resolve, relative, sep, isAbsolute, join, dirname } from 'path';
|
|
9
12
|
import { mkdir, rename, writeFile, readFile, stat, cp, readdir, chmod, realpath } from 'fs/promises';
|
|
10
|
-
import
|
|
11
|
-
import * as zNS from 'zod';
|
|
13
|
+
import * as z4 from 'zod';
|
|
12
14
|
import { execFile } from 'child_process';
|
|
13
15
|
import { promisify } from 'util';
|
|
14
16
|
import { createReadStream, readFileSync, existsSync, mkdirSync, openSync, writeSync, closeSync, chmodSync, renameSync } from 'fs';
|
|
@@ -53,6 +55,326 @@ var AbstractConnector = class {
|
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
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
|
+
};
|
|
56
378
|
var execFileAsync = promisify(execFile);
|
|
57
379
|
var GIT_REV_PARSE_TIMEOUT_MS = 15e3;
|
|
58
380
|
var DeployConnector = class extends AbstractConnector {
|
|
@@ -229,7 +551,7 @@ var DeployConnector = class extends AbstractConnector {
|
|
|
229
551
|
}
|
|
230
552
|
}
|
|
231
553
|
_mockPush(state, opts) {
|
|
232
|
-
const
|
|
554
|
+
const record2 = {
|
|
233
555
|
id: opts.deploymentId,
|
|
234
556
|
url: `https://mock.deploy.example.com/${opts.deploymentId}`,
|
|
235
557
|
ref: opts.ref,
|
|
@@ -238,8 +560,8 @@ var DeployConnector = class extends AbstractConnector {
|
|
|
238
560
|
deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
239
561
|
status: "live"
|
|
240
562
|
};
|
|
241
|
-
state.deployments.set(opts.deploymentId,
|
|
242
|
-
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 });
|
|
243
565
|
}
|
|
244
566
|
_opStatus(state, args) {
|
|
245
567
|
const id = args.deployment ? String(args.deployment) : void 0;
|
|
@@ -3124,7 +3446,7 @@ var MinIOConnector = class extends AbstractConnector {
|
|
|
3124
3446
|
const { client, bucket } = handle.state;
|
|
3125
3447
|
const entries = [];
|
|
3126
3448
|
const stream = client.listObjects(bucket, path ?? "", recursive);
|
|
3127
|
-
return new Promise((
|
|
3449
|
+
return new Promise((resolve6, reject) => {
|
|
3128
3450
|
stream.on("data", (obj) => {
|
|
3129
3451
|
entries.push({
|
|
3130
3452
|
name: obj.name,
|
|
@@ -3134,7 +3456,7 @@ var MinIOConnector = class extends AbstractConnector {
|
|
|
3134
3456
|
modifiedAt: obj.lastModified
|
|
3135
3457
|
});
|
|
3136
3458
|
});
|
|
3137
|
-
stream.on("end", () =>
|
|
3459
|
+
stream.on("end", () => resolve6(entries));
|
|
3138
3460
|
stream.on("error", reject);
|
|
3139
3461
|
});
|
|
3140
3462
|
}
|
|
@@ -3142,9 +3464,9 @@ var MinIOConnector = class extends AbstractConnector {
|
|
|
3142
3464
|
const { client, bucket } = handle.state;
|
|
3143
3465
|
const chunks = [];
|
|
3144
3466
|
const stream = await client.getObject(bucket, path);
|
|
3145
|
-
return new Promise((
|
|
3467
|
+
return new Promise((resolve6, reject) => {
|
|
3146
3468
|
stream.on("data", (chunk) => chunks.push(chunk));
|
|
3147
|
-
stream.on("end", () =>
|
|
3469
|
+
stream.on("end", () => resolve6({ data: Buffer.concat(chunks).toString() }));
|
|
3148
3470
|
stream.on("error", reject);
|
|
3149
3471
|
});
|
|
3150
3472
|
}
|
|
@@ -4412,7 +4734,7 @@ async function checkCliExists(cli) {
|
|
|
4412
4734
|
}
|
|
4413
4735
|
}
|
|
4414
4736
|
async function spawnCloudflaredTunnel(port) {
|
|
4415
|
-
return new Promise((
|
|
4737
|
+
return new Promise((resolve6, reject) => {
|
|
4416
4738
|
const proc = portableSpawn(["cloudflared", "tunnel", "--url", `http://localhost:${port}`]);
|
|
4417
4739
|
const decoder = new TextDecoder();
|
|
4418
4740
|
const reader = proc.stderr.getReader();
|
|
@@ -4423,7 +4745,7 @@ async function spawnCloudflaredTunnel(port) {
|
|
|
4423
4745
|
const chunk = decoder.decode(value);
|
|
4424
4746
|
const match = chunk.match(/https:\/\/[^\s"]+\.trycloudflare\.com/);
|
|
4425
4747
|
if (match) {
|
|
4426
|
-
|
|
4748
|
+
resolve6({ url: match[0], process: proc });
|
|
4427
4749
|
return;
|
|
4428
4750
|
}
|
|
4429
4751
|
}
|
|
@@ -5126,7 +5448,7 @@ function connectorTargetFromFactory(id, factory) {
|
|
|
5126
5448
|
id,
|
|
5127
5449
|
displayName: id,
|
|
5128
5450
|
apiVersion: 1,
|
|
5129
|
-
configSchema:
|
|
5451
|
+
configSchema: z4.unknown(),
|
|
5130
5452
|
create: (_config, _ctx) => factory()
|
|
5131
5453
|
};
|
|
5132
5454
|
}
|
|
@@ -5423,7 +5745,7 @@ var ConnectorManager = class {
|
|
|
5423
5745
|
id: driver,
|
|
5424
5746
|
displayName: driver,
|
|
5425
5747
|
apiVersion: 1,
|
|
5426
|
-
configSchema:
|
|
5748
|
+
configSchema: z4.unknown(),
|
|
5427
5749
|
create: () => connector
|
|
5428
5750
|
});
|
|
5429
5751
|
}
|
|
@@ -6081,7 +6403,7 @@ var ConnectorManager = class {
|
|
|
6081
6403
|
return buildSkillBody(descriptor, ops, shellAccess);
|
|
6082
6404
|
}
|
|
6083
6405
|
};
|
|
6084
|
-
var zStatic =
|
|
6406
|
+
var zStatic = z4.z ?? z4.default ?? z4;
|
|
6085
6407
|
async function buildSdkConnectorTools(manager, options) {
|
|
6086
6408
|
const shellAccess = options?.shellAccess ?? true;
|
|
6087
6409
|
let sdk = options?.sdk;
|
|
@@ -6092,7 +6414,7 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6092
6414
|
return null;
|
|
6093
6415
|
}
|
|
6094
6416
|
}
|
|
6095
|
-
const
|
|
6417
|
+
const z5 = options?.z ?? zStatic;
|
|
6096
6418
|
const tools = [];
|
|
6097
6419
|
tools.push({
|
|
6098
6420
|
name: "connector_list",
|
|
@@ -6114,7 +6436,7 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6114
6436
|
name: "connector_load_skill",
|
|
6115
6437
|
description: "Load full instructions for a specific connector into context. Returns a markdown document describing the connector and its available CLI commands.",
|
|
6116
6438
|
inputSchema: {
|
|
6117
|
-
connector_id:
|
|
6439
|
+
connector_id: z5.string().describe("Connector ID (use connector_list to enumerate)")
|
|
6118
6440
|
},
|
|
6119
6441
|
handler: async ({ connector_id }) => {
|
|
6120
6442
|
try {
|
|
@@ -6132,8 +6454,8 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6132
6454
|
name: "connector_read",
|
|
6133
6455
|
description: 'Read content from a connector by path. For KV stores, path is the key. For databases, path is "table/pk".',
|
|
6134
6456
|
inputSchema: {
|
|
6135
|
-
connector_id:
|
|
6136
|
-
path:
|
|
6457
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6458
|
+
path: z5.string().describe("Path within the connector")
|
|
6137
6459
|
},
|
|
6138
6460
|
handler: async ({ connector_id, path }) => {
|
|
6139
6461
|
try {
|
|
@@ -6153,9 +6475,9 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6153
6475
|
name: "connector_write",
|
|
6154
6476
|
description: "Write content to a connector at a path.",
|
|
6155
6477
|
inputSchema: {
|
|
6156
|
-
connector_id:
|
|
6157
|
-
path:
|
|
6158
|
-
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")
|
|
6159
6481
|
},
|
|
6160
6482
|
handler: async ({
|
|
6161
6483
|
connector_id,
|
|
@@ -6177,9 +6499,9 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6177
6499
|
name: "connector_list_entries",
|
|
6178
6500
|
description: "List entries in a connector (files, keys, rows, etc).",
|
|
6179
6501
|
inputSchema: {
|
|
6180
|
-
connector_id:
|
|
6181
|
-
path:
|
|
6182
|
-
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")
|
|
6183
6505
|
},
|
|
6184
6506
|
handler: async ({
|
|
6185
6507
|
connector_id,
|
|
@@ -6201,8 +6523,8 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6201
6523
|
name: "connector_search",
|
|
6202
6524
|
description: "Search within a connector for matching content.",
|
|
6203
6525
|
inputSchema: {
|
|
6204
|
-
connector_id:
|
|
6205
|
-
query:
|
|
6526
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6527
|
+
query: z5.string().describe("Search query")
|
|
6206
6528
|
},
|
|
6207
6529
|
handler: async ({ connector_id, query }) => {
|
|
6208
6530
|
try {
|
|
@@ -6220,8 +6542,8 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6220
6542
|
name: "connector_delete",
|
|
6221
6543
|
description: "Delete an entry from a connector.",
|
|
6222
6544
|
inputSchema: {
|
|
6223
|
-
connector_id:
|
|
6224
|
-
path:
|
|
6545
|
+
connector_id: z5.string().describe("Connector ID"),
|
|
6546
|
+
path: z5.string().describe("Path to delete")
|
|
6225
6547
|
},
|
|
6226
6548
|
handler: async ({ connector_id, path }) => {
|
|
6227
6549
|
try {
|
|
@@ -6240,9 +6562,9 @@ async function buildSdkConnectorTools(manager, options) {
|
|
|
6240
6562
|
name: "connector_exec",
|
|
6241
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.",
|
|
6242
6564
|
inputSchema: {
|
|
6243
|
-
connector_id:
|
|
6244
|
-
operation:
|
|
6245
|
-
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)")
|
|
6246
6568
|
},
|
|
6247
6569
|
handler: async ({
|
|
6248
6570
|
connector_id,
|
|
@@ -6285,7 +6607,7 @@ async function buildSdkFlowTools(manager, connectorId, options) {
|
|
|
6285
6607
|
return null;
|
|
6286
6608
|
}
|
|
6287
6609
|
}
|
|
6288
|
-
const
|
|
6610
|
+
const z5 = options?.z ?? zStatic;
|
|
6289
6611
|
let ops;
|
|
6290
6612
|
try {
|
|
6291
6613
|
ops = manager.getOperations(connectorId);
|
|
@@ -6297,7 +6619,7 @@ async function buildSdkFlowTools(manager, connectorId, options) {
|
|
|
6297
6619
|
const tools = agentOps.map((op) => ({
|
|
6298
6620
|
name: op.name,
|
|
6299
6621
|
description: op.description,
|
|
6300
|
-
inputSchema: buildOperationInputSchema(op.args,
|
|
6622
|
+
inputSchema: buildOperationInputSchema(op.args, z5),
|
|
6301
6623
|
handler: async (input) => {
|
|
6302
6624
|
try {
|
|
6303
6625
|
const result = await manager.executeOp(connectorId, op.name, input ?? {});
|
|
@@ -6312,22 +6634,22 @@ async function buildSdkFlowTools(manager, connectorId, options) {
|
|
|
6312
6634
|
}));
|
|
6313
6635
|
return sdk.createSdkMcpServer({ name: "skaile-flow", tools });
|
|
6314
6636
|
}
|
|
6315
|
-
function buildOperationInputSchema(args,
|
|
6637
|
+
function buildOperationInputSchema(args, z5) {
|
|
6316
6638
|
const shape = {};
|
|
6317
6639
|
for (const arg of args) {
|
|
6318
6640
|
let schema;
|
|
6319
6641
|
switch (arg.type) {
|
|
6320
6642
|
case "boolean":
|
|
6321
|
-
schema =
|
|
6643
|
+
schema = z5.boolean();
|
|
6322
6644
|
break;
|
|
6323
6645
|
case "number":
|
|
6324
|
-
schema =
|
|
6646
|
+
schema = z5.number();
|
|
6325
6647
|
break;
|
|
6326
6648
|
case "json":
|
|
6327
|
-
schema =
|
|
6649
|
+
schema = z5.json();
|
|
6328
6650
|
break;
|
|
6329
6651
|
default:
|
|
6330
|
-
schema =
|
|
6652
|
+
schema = z5.string();
|
|
6331
6653
|
}
|
|
6332
6654
|
if (arg.description) schema = schema.describe(arg.description);
|
|
6333
6655
|
if (!arg.required) schema = schema.optional();
|
|
@@ -6623,7 +6945,7 @@ async function ensureFleetMounted(dir, log, options) {
|
|
|
6623
6945
|
return;
|
|
6624
6946
|
} catch (err) {
|
|
6625
6947
|
lastErr = err;
|
|
6626
|
-
await new Promise((
|
|
6948
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollIntervalMs));
|
|
6627
6949
|
}
|
|
6628
6950
|
}
|
|
6629
6951
|
throw new Error(
|
|
@@ -6920,19 +7242,19 @@ var PortPool = class _PortPool {
|
|
|
6920
7242
|
}
|
|
6921
7243
|
/** Quick TCP connect test — returns true if nothing is listening. */
|
|
6922
7244
|
static isPortFree(port, host = "127.0.0.1") {
|
|
6923
|
-
return new Promise((
|
|
7245
|
+
return new Promise((resolve6) => {
|
|
6924
7246
|
const socket = createConnection({ port, host });
|
|
6925
7247
|
socket.once("connect", () => {
|
|
6926
7248
|
socket.destroy();
|
|
6927
|
-
|
|
7249
|
+
resolve6(false);
|
|
6928
7250
|
});
|
|
6929
7251
|
socket.once("error", () => {
|
|
6930
7252
|
socket.destroy();
|
|
6931
|
-
|
|
7253
|
+
resolve6(true);
|
|
6932
7254
|
});
|
|
6933
7255
|
socket.setTimeout(500, () => {
|
|
6934
7256
|
socket.destroy();
|
|
6935
|
-
|
|
7257
|
+
resolve6(true);
|
|
6936
7258
|
});
|
|
6937
7259
|
});
|
|
6938
7260
|
}
|
|
@@ -7151,9 +7473,34 @@ async function listWorktrees(repoRoot) {
|
|
|
7151
7473
|
if (current.path) trees.push(current);
|
|
7152
7474
|
return trees;
|
|
7153
7475
|
}
|
|
7154
|
-
|
|
7155
|
-
// factory-assets/connectors/flow/adapter.ts
|
|
7156
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
|
+
}
|
|
7498
|
+
function normalizeRuntimeFlow(authored) {
|
|
7499
|
+
const parsed = parseFlowDefinition(authored);
|
|
7500
|
+
if (parsed.ok) return parsed.data;
|
|
7501
|
+
const details = parsed.errors.map((issue) => `${issue.path || "<root>"}: ${issue.message}`).join("; ");
|
|
7502
|
+
throw new Error(`FlowAdapter: invalid flow definition: ${details}`);
|
|
7503
|
+
}
|
|
7157
7504
|
function emptyNode(nodeId) {
|
|
7158
7505
|
return {
|
|
7159
7506
|
id: nodeId,
|
|
@@ -7163,10 +7510,15 @@ function emptyNode(nodeId) {
|
|
|
7163
7510
|
errorHistory: []
|
|
7164
7511
|
};
|
|
7165
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
|
+
}
|
|
7166
7518
|
function buildInitialExecution(flowDef, seed) {
|
|
7167
|
-
const nodes =
|
|
7519
|
+
const nodes = nodeExecutionRecord();
|
|
7168
7520
|
for (const n of flowDef.nodes) {
|
|
7169
|
-
if (n
|
|
7521
|
+
if (isActiveRuntimeNode(n)) {
|
|
7170
7522
|
nodes[n.id] = emptyNode(n.id);
|
|
7171
7523
|
}
|
|
7172
7524
|
}
|
|
@@ -7189,7 +7541,8 @@ function toSnapshot(exec) {
|
|
|
7189
7541
|
approval: exec.approval,
|
|
7190
7542
|
input: exec.input,
|
|
7191
7543
|
output: exec.output,
|
|
7192
|
-
error: exec.error
|
|
7544
|
+
error: exec.error,
|
|
7545
|
+
failedPreconditions: exec.failedPreconditions
|
|
7193
7546
|
};
|
|
7194
7547
|
}
|
|
7195
7548
|
function snapshotMap(execution) {
|
|
@@ -7199,6 +7552,50 @@ function snapshotMap(execution) {
|
|
|
7199
7552
|
}
|
|
7200
7553
|
return map;
|
|
7201
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
|
+
}
|
|
7202
7599
|
function assertLegal(cond, message) {
|
|
7203
7600
|
if (!cond) throw new Error(`FlowAdapter: ${message}`);
|
|
7204
7601
|
}
|
|
@@ -7208,10 +7605,10 @@ function findNode(flowDef, nodeId) {
|
|
|
7208
7605
|
return node;
|
|
7209
7606
|
}
|
|
7210
7607
|
function isOptional(flowDef, nodeId) {
|
|
7211
|
-
return findNode(flowDef, nodeId)
|
|
7608
|
+
return nodeIsOptional(findNode(flowDef, nodeId));
|
|
7212
7609
|
}
|
|
7213
|
-
function
|
|
7214
|
-
return findNode(flowDef, nodeId)
|
|
7610
|
+
function requiresAgentApproval(flowDef, nodeId) {
|
|
7611
|
+
return nodeRequiresApproval(findNode(flowDef, nodeId));
|
|
7215
7612
|
}
|
|
7216
7613
|
var FlowAdapter = class extends AbstractConnector {
|
|
7217
7614
|
name = "flow";
|
|
@@ -7230,9 +7627,9 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7230
7627
|
// ── Lifecycle ──
|
|
7231
7628
|
async connect(declaration, _ctx) {
|
|
7232
7629
|
const options = declaration.options ?? {};
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
const execution = options.execution
|
|
7630
|
+
if (!options.flow) throw new Error("FlowAdapter requires options.flow (FlowDefinition)");
|
|
7631
|
+
const flowDef = normalizeRuntimeFlow(options.flow);
|
|
7632
|
+
const execution = options.execution ? prepareExecutionSnapshot(flowDef, options.execution) : buildInitialExecution(
|
|
7236
7633
|
flowDef,
|
|
7237
7634
|
options.seed ?? { runId: "local", startedBy: "system", autonomousMode: false }
|
|
7238
7635
|
);
|
|
@@ -7241,14 +7638,46 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7241
7638
|
execution,
|
|
7242
7639
|
stimulusBus: options.stimulusBus,
|
|
7243
7640
|
connectorId: declaration.id,
|
|
7244
|
-
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
|
|
7245
7648
|
};
|
|
7246
7649
|
state.execution = this.recompute(state);
|
|
7247
7650
|
return this.makeHandle(declaration, state);
|
|
7248
7651
|
}
|
|
7249
7652
|
async disconnect(handle) {
|
|
7653
|
+
const state = this.s(handle);
|
|
7654
|
+
state.executionGeneration += 1;
|
|
7655
|
+
await this.cancelInlineTargets(state);
|
|
7250
7656
|
handle.status = "disconnected";
|
|
7251
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
|
+
}
|
|
7252
7681
|
// ── State change subscription ──
|
|
7253
7682
|
onStateChange(handle, cb) {
|
|
7254
7683
|
this.s(handle).onChange = cb;
|
|
@@ -7256,13 +7685,21 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7256
7685
|
/**
|
|
7257
7686
|
* Replace the current execution snapshot with a host-provided one.
|
|
7258
7687
|
* Used on container rehydration via the extended `configure` command.
|
|
7259
|
-
*
|
|
7260
|
-
*
|
|
7688
|
+
* Publishes only when compatibility preparation changes the supplied snapshot,
|
|
7689
|
+
* such as seeding a node kind activated after the snapshot was persisted.
|
|
7261
7690
|
*/
|
|
7262
7691
|
hydrate(handle, execution) {
|
|
7263
7692
|
const s = this.s(handle);
|
|
7264
|
-
|
|
7265
|
-
|
|
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);
|
|
7266
7703
|
}
|
|
7267
7704
|
// ── Base operations (private — wired into tools face in constructor) ──
|
|
7268
7705
|
async read(handle, path) {
|
|
@@ -7323,7 +7760,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7323
7760
|
},
|
|
7324
7761
|
{
|
|
7325
7762
|
name: "build_handoff",
|
|
7326
|
-
description: "Build
|
|
7763
|
+
description: "Build the selected agent node briefing: instruction, supplied bindings, required assets, and completed predecessor context.",
|
|
7327
7764
|
accessLevel: "read",
|
|
7328
7765
|
args: [
|
|
7329
7766
|
{
|
|
@@ -7352,7 +7789,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7352
7789
|
ops.push(
|
|
7353
7790
|
{
|
|
7354
7791
|
name: "start_node",
|
|
7355
|
-
description: "Mark an available node as running (records startedAt).",
|
|
7792
|
+
description: "Mark an available agent node as running (records startedAt).",
|
|
7356
7793
|
accessLevel: "write",
|
|
7357
7794
|
args: [
|
|
7358
7795
|
{ name: "nodeId", description: "Node ID to start", type: "string", required: true }
|
|
@@ -7360,7 +7797,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7360
7797
|
},
|
|
7361
7798
|
{
|
|
7362
7799
|
name: "request_approval",
|
|
7363
|
-
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.",
|
|
7364
7801
|
accessLevel: "write",
|
|
7365
7802
|
args: [
|
|
7366
7803
|
{ name: "nodeId", description: "Node ID", type: "string", required: true },
|
|
@@ -7380,7 +7817,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7380
7817
|
},
|
|
7381
7818
|
{
|
|
7382
7819
|
name: "request_input",
|
|
7383
|
-
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.",
|
|
7384
7821
|
accessLevel: "write",
|
|
7385
7822
|
args: [
|
|
7386
7823
|
{ name: "nodeId", description: "Node ID", type: "string", required: true },
|
|
@@ -7400,7 +7837,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7400
7837
|
},
|
|
7401
7838
|
{
|
|
7402
7839
|
name: "complete_node",
|
|
7403
|
-
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.",
|
|
7404
7841
|
accessLevel: "write",
|
|
7405
7842
|
args: [
|
|
7406
7843
|
{ name: "nodeId", description: "Node ID to complete", type: "string", required: true },
|
|
@@ -7410,6 +7847,12 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7410
7847
|
type: "string",
|
|
7411
7848
|
required: true
|
|
7412
7849
|
},
|
|
7850
|
+
{
|
|
7851
|
+
name: "fields",
|
|
7852
|
+
description: "Named JSON object consumed by downstream nodes.",
|
|
7853
|
+
type: "json",
|
|
7854
|
+
required: false
|
|
7855
|
+
},
|
|
7413
7856
|
{
|
|
7414
7857
|
name: "artifacts",
|
|
7415
7858
|
description: "JSON array of ArtifactRef objects produced by this node.",
|
|
@@ -7573,13 +8016,36 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7573
8016
|
}
|
|
7574
8017
|
async executeOp(handle, operation, args) {
|
|
7575
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
|
+
}
|
|
7576
8042
|
switch (operation) {
|
|
7577
8043
|
case "get_available":
|
|
7578
8044
|
return this.opGetAvailable(s);
|
|
7579
8045
|
case "get_state":
|
|
7580
8046
|
return JSON.stringify(s.execution);
|
|
7581
8047
|
case "get_globals":
|
|
7582
|
-
return JSON.stringify(s.flowDef
|
|
8048
|
+
return JSON.stringify(flowDefaults(s.flowDef));
|
|
7583
8049
|
case "build_handoff":
|
|
7584
8050
|
return this.buildHandoff(s, String(args.nodeId ?? ""));
|
|
7585
8051
|
case "validate_node":
|
|
@@ -7605,8 +8071,7 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7605
8071
|
case "applyInput":
|
|
7606
8072
|
return this.opApplyInput(handle, s, args);
|
|
7607
8073
|
case "cancel":
|
|
7608
|
-
|
|
7609
|
-
return JSON.stringify(s.execution);
|
|
8074
|
+
throw new Error("FlowAdapter: cancel routing invariant violated");
|
|
7610
8075
|
case "setAutonomousMode":
|
|
7611
8076
|
return this.opSetAutonomousMode(handle, s, args);
|
|
7612
8077
|
case "retryNode":
|
|
@@ -7621,14 +8086,18 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7621
8086
|
}
|
|
7622
8087
|
// ── executeOp branch handlers (private dispatch helpers) ─────────────────
|
|
7623
8088
|
opGetAvailable(s) {
|
|
8089
|
+
const agentNodeIds = new Set(s.flowDef.nodes.filter(isAgentNode).map((node) => node.id));
|
|
7624
8090
|
const nodes = Object.values(s.execution.nodes);
|
|
7625
|
-
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);
|
|
7626
8092
|
return JSON.stringify(available);
|
|
7627
8093
|
}
|
|
7628
8094
|
opValidateNode(s, args) {
|
|
7629
8095
|
const nodeId = String(args.nodeId ?? "");
|
|
7630
8096
|
const node = s.execution.nodes[nodeId];
|
|
7631
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
|
+
}
|
|
7632
8101
|
const predsMet = flowPredecessors(s.flowDef, nodeId).every((pid) => {
|
|
7633
8102
|
const predStatus = s.execution.nodes[pid]?.status ?? "not_started";
|
|
7634
8103
|
return predStatus === "complete" || predStatus === "skipped";
|
|
@@ -7636,7 +8105,8 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7636
8105
|
return JSON.stringify({
|
|
7637
8106
|
valid: node.status === "available",
|
|
7638
8107
|
status: node.status,
|
|
7639
|
-
predecessorsReady: predsMet
|
|
8108
|
+
predecessorsReady: predsMet,
|
|
8109
|
+
failedPreconditions: node.failedPreconditions ?? []
|
|
7640
8110
|
});
|
|
7641
8111
|
}
|
|
7642
8112
|
opStartNode(handle, args) {
|
|
@@ -7657,12 +8127,14 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7657
8127
|
}
|
|
7658
8128
|
opCompleteNode(handle, args) {
|
|
7659
8129
|
const nodeId = String(args.nodeId ?? "");
|
|
8130
|
+
const parsedFields = args.fields === void 0 ? { issues: [] } : safeParseFields(args.fields);
|
|
7660
8131
|
const output = {
|
|
7661
8132
|
summary: String(args.summary ?? "Completed"),
|
|
7662
8133
|
artifacts: safeParseArtifacts(args.artifacts),
|
|
7663
|
-
concerns: safeParseStringArray(args.concerns)
|
|
8134
|
+
concerns: safeParseStringArray(args.concerns),
|
|
8135
|
+
...parsedFields.fields ? { fields: parsedFields.fields } : {}
|
|
7664
8136
|
};
|
|
7665
|
-
return this.doCompleteNode(handle, nodeId, output);
|
|
8137
|
+
return this.doCompleteNode(handle, nodeId, output, parsedFields.issues);
|
|
7666
8138
|
}
|
|
7667
8139
|
opSkipNode(handle, args) {
|
|
7668
8140
|
const nodeId = String(args.nodeId ?? "");
|
|
@@ -7709,15 +8181,17 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7709
8181
|
return JSON.stringify(s.execution);
|
|
7710
8182
|
}
|
|
7711
8183
|
opStart(handle, s, args) {
|
|
7712
|
-
const
|
|
7713
|
-
if (!
|
|
8184
|
+
const authoredFlow = args.flow;
|
|
8185
|
+
if (!authoredFlow || typeof authoredFlow !== "object") {
|
|
7714
8186
|
throw new Error("FlowAdapter: start requires args.flow (FlowDefinition)");
|
|
7715
8187
|
}
|
|
8188
|
+
const flow = normalizeRuntimeFlow(authoredFlow);
|
|
7716
8189
|
const seed = args.seed ?? {};
|
|
7717
8190
|
if (!seed.runId || !seed.startedBy) {
|
|
7718
8191
|
throw new Error("FlowAdapter: start requires args.seed.runId + args.seed.startedBy");
|
|
7719
8192
|
}
|
|
7720
8193
|
s.flowDef = flow;
|
|
8194
|
+
s.executionGeneration += 1;
|
|
7721
8195
|
s.execution = buildInitialExecution(flow, {
|
|
7722
8196
|
runId: seed.runId,
|
|
7723
8197
|
startedBy: seed.startedBy,
|
|
@@ -7730,6 +8204,10 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7730
8204
|
// ── Agent-side mutations (tool calls) ─────────────────────────────────────
|
|
7731
8205
|
doStartNode(handle, nodeId) {
|
|
7732
8206
|
return this.mutate(handle, (s) => {
|
|
8207
|
+
assertLegal(
|
|
8208
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8209
|
+
`node is not an active agent node: ${nodeId}`
|
|
8210
|
+
);
|
|
7733
8211
|
const node = s.execution.nodes[nodeId];
|
|
7734
8212
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7735
8213
|
assertLegal(node.status === "available", `cannot start ${nodeId}: status is ${node.status}`);
|
|
@@ -7743,11 +8221,17 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7743
8221
|
}
|
|
7744
8222
|
doRequestApproval(handle, nodeId, summary, artifacts) {
|
|
7745
8223
|
return this.mutate(handle, (s) => {
|
|
8224
|
+
const definition = findNode(s.flowDef, nodeId);
|
|
7746
8225
|
const node = s.execution.nodes[nodeId];
|
|
7747
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
|
+
);
|
|
7748
8232
|
assertLegal(
|
|
7749
|
-
node.status === "running",
|
|
7750
|
-
`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}`
|
|
7751
8235
|
);
|
|
7752
8236
|
const approval = { requestedAt: now(), summary };
|
|
7753
8237
|
const history = [...node.approvalHistory, approval];
|
|
@@ -7765,6 +8249,10 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7765
8249
|
}
|
|
7766
8250
|
doRequestInput(handle, nodeId, prompt, schema) {
|
|
7767
8251
|
return this.mutate(handle, (s) => {
|
|
8252
|
+
assertLegal(
|
|
8253
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8254
|
+
`node is not an active agent node: ${nodeId}`
|
|
8255
|
+
);
|
|
7768
8256
|
const node = s.execution.nodes[nodeId];
|
|
7769
8257
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7770
8258
|
assertLegal(
|
|
@@ -7787,20 +8275,64 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7787
8275
|
return JSON.stringify({ ok: true, status: "awaiting_input" });
|
|
7788
8276
|
});
|
|
7789
8277
|
}
|
|
7790
|
-
doCompleteNode(handle, nodeId, output) {
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
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}`
|
|
7801
8329
|
);
|
|
7802
|
-
|
|
7803
|
-
|
|
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,
|
|
7804
8336
|
status: "complete",
|
|
7805
8337
|
completedAt: now(),
|
|
7806
8338
|
output
|
|
@@ -7810,10 +8342,14 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7810
8342
|
}
|
|
7811
8343
|
doSkipNode(handle, nodeId) {
|
|
7812
8344
|
return this.mutate(handle, (s) => {
|
|
8345
|
+
assertLegal(
|
|
8346
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8347
|
+
`node is not an active agent node: ${nodeId}`
|
|
8348
|
+
);
|
|
7813
8349
|
const node = s.execution.nodes[nodeId];
|
|
7814
8350
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7815
8351
|
assertLegal(
|
|
7816
|
-
!
|
|
8352
|
+
!requiresAgentApproval(s.flowDef, nodeId),
|
|
7817
8353
|
`cannot skip node ${nodeId}: it is a mandatory approval gate \u2014 call request_approval and wait for an approved decision`
|
|
7818
8354
|
);
|
|
7819
8355
|
assertLegal(isOptional(s.flowDef, nodeId), `cannot skip required node ${nodeId}`);
|
|
@@ -7827,6 +8363,10 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7827
8363
|
}
|
|
7828
8364
|
doFailNode(handle, nodeId, message, recoverable) {
|
|
7829
8365
|
return this.mutate(handle, (s) => {
|
|
8366
|
+
assertLegal(
|
|
8367
|
+
isAgentNode(findNode(s.flowDef, nodeId)),
|
|
8368
|
+
`node is not an active agent node: ${nodeId}`
|
|
8369
|
+
);
|
|
7830
8370
|
const node = s.execution.nodes[nodeId];
|
|
7831
8371
|
assertLegal(node, `node not in execution: ${nodeId}`);
|
|
7832
8372
|
const error = { message, recoverable, at: now() };
|
|
@@ -7852,18 +8392,24 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7852
8392
|
// ── Host-side mutations (not exposed as agent tools) ─────────────────────
|
|
7853
8393
|
/**
|
|
7854
8394
|
* Record an approval decision on a node currently `awaiting_approval`.
|
|
7855
|
-
*
|
|
7856
|
-
*
|
|
7857
|
-
* 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.
|
|
7858
8397
|
*/
|
|
7859
8398
|
applyApproval(handle, nodeId, decision, feedback, decidedBy) {
|
|
7860
8399
|
this.mutate(handle, (s) => {
|
|
8400
|
+
const definition = findNode(s.flowDef, nodeId);
|
|
7861
8401
|
const node = s.execution.nodes[nodeId];
|
|
7862
8402
|
assertLegal(node, `applyApproval: unknown node ${nodeId}`);
|
|
7863
8403
|
assertLegal(
|
|
7864
8404
|
node.status === "awaiting_approval",
|
|
7865
8405
|
`applyApproval: node ${nodeId} is ${node.status}, not awaiting_approval`
|
|
7866
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
|
+
}
|
|
7867
8413
|
const base = node.approval ?? { requestedAt: now(), summary: "" };
|
|
7868
8414
|
const approval = {
|
|
7869
8415
|
...base,
|
|
@@ -7875,7 +8421,22 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7875
8421
|
const history = [...node.approvalHistory];
|
|
7876
8422
|
if (history.length > 0) history[history.length - 1] = approval;
|
|
7877
8423
|
else history.push(approval);
|
|
7878
|
-
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") {
|
|
7879
8440
|
s.execution.nodes[nodeId] = { ...node, approval, approvalHistory: history };
|
|
7880
8441
|
} else {
|
|
7881
8442
|
s.execution.nodes[nodeId] = {
|
|
@@ -7959,9 +8520,296 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7959
8520
|
getExecution(handle) {
|
|
7960
8521
|
return this.s(handle).execution;
|
|
7961
8522
|
}
|
|
7962
|
-
/**
|
|
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}. */
|
|
7963
8544
|
getMandatoryGates(handle) {
|
|
7964
|
-
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);
|
|
7965
8813
|
}
|
|
7966
8814
|
// ── Private helpers ──
|
|
7967
8815
|
s(handle) {
|
|
@@ -7970,23 +8818,21 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
7970
8818
|
/**
|
|
7971
8819
|
* Central mutation pipeline: runs the mutator, recomputes derived
|
|
7972
8820
|
* fields, updates the flow-level `status` if the mutation completed
|
|
7973
|
-
* or failed the flow, fires `onChange` once, and
|
|
7974
|
-
*
|
|
7975
|
-
*
|
|
7976
|
-
*
|
|
7977
|
-
* The bus signal path is gated by the `useBusForStimulus` flag set at
|
|
7978
|
-
* connect time — Phase 3 builds the path; Phase 4 flips the default to
|
|
7979
|
-
* true globally and removes the orchestrator's onStateChange
|
|
7980
|
-
* 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.
|
|
7981
8824
|
*/
|
|
7982
|
-
mutate(handle, fn) {
|
|
8825
|
+
mutate(handle, fn, signalStimulus = true) {
|
|
7983
8826
|
const s = this.s(handle);
|
|
7984
8827
|
const previousExecution = s.lastSignaledExecution;
|
|
7985
|
-
s.execution = {
|
|
8828
|
+
s.execution = {
|
|
8829
|
+
...s.execution,
|
|
8830
|
+
nodes: nodeExecutionRecord(Object.entries(s.execution.nodes))
|
|
8831
|
+
};
|
|
7986
8832
|
const result = fn(s);
|
|
7987
8833
|
s.execution = this.recompute(s);
|
|
7988
8834
|
if (s.onChange) s.onChange(s.execution);
|
|
7989
|
-
if (s.useBusForStimulus && s.stimulusBus) {
|
|
8835
|
+
if (signalStimulus && s.useBusForStimulus && s.stimulusBus) {
|
|
7990
8836
|
const stim = computeStimulus(previousExecution, s.execution);
|
|
7991
8837
|
if (stim) {
|
|
7992
8838
|
const fragment = renderStimulusPrompt(stim);
|
|
@@ -8009,17 +8855,23 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8009
8855
|
recompute(s) {
|
|
8010
8856
|
const snapshots = snapshotMap(s.execution);
|
|
8011
8857
|
const computed = computeFlowStateFromSnapshots(s.flowDef, snapshots);
|
|
8012
|
-
const nodes =
|
|
8858
|
+
const nodes = nodeExecutionRecord(Object.entries(s.execution.nodes));
|
|
8013
8859
|
for (const ns of computed.nodes) {
|
|
8014
8860
|
const existing = nodes[ns.nodeId];
|
|
8015
8861
|
if (!existing) continue;
|
|
8016
|
-
if (existing.status === "not_started"
|
|
8017
|
-
|
|
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
|
+
};
|
|
8018
8870
|
}
|
|
8019
8871
|
}
|
|
8020
8872
|
let flowStatus = s.execution.status;
|
|
8021
8873
|
if (flowStatus !== "cancelled") {
|
|
8022
|
-
const anyRequiredFailed = s.flowDef.nodes.filter((n) => n
|
|
8874
|
+
const anyRequiredFailed = s.flowDef.nodes.filter((n) => isActiveRuntimeNode(n) && !nodeIsOptional(n)).some((n) => nodes[n.id]?.status === "failed");
|
|
8023
8875
|
if (anyRequiredFailed) {
|
|
8024
8876
|
flowStatus = "failed";
|
|
8025
8877
|
} else if (computed.done) {
|
|
@@ -8037,13 +8889,62 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8037
8889
|
};
|
|
8038
8890
|
}
|
|
8039
8891
|
/**
|
|
8040
|
-
* Build
|
|
8041
|
-
* artifact refs produced by its upstream flow-type predecessors.
|
|
8892
|
+
* Build the selected agent node's complete turn briefing plus upstream context.
|
|
8042
8893
|
*/
|
|
8043
8894
|
buildHandoff(s, nodeId) {
|
|
8895
|
+
const target = findNode(s.flowDef, nodeId);
|
|
8896
|
+
assertLegal(isAgentNode(target), `node is not an active agent node: ${nodeId}`);
|
|
8897
|
+
const targetExecution = s.execution.nodes[nodeId];
|
|
8898
|
+
assertLegal(targetExecution, `node not in execution: ${nodeId}`);
|
|
8899
|
+
assertLegal(
|
|
8900
|
+
targetExecution.status === "available" || targetExecution.status === "running",
|
|
8901
|
+
`cannot build handoff for ${nodeId}: status is ${targetExecution.status}`
|
|
8902
|
+
);
|
|
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
|
+
);
|
|
8910
|
+
const instruction = nodeInstruction(target);
|
|
8911
|
+
if (instruction) {
|
|
8912
|
+
sections.push(
|
|
8913
|
+
`## Instruction
|
|
8914
|
+
|
|
8915
|
+
${interpolateNodeInstruction(instruction, resolved.values)}`
|
|
8916
|
+
);
|
|
8917
|
+
}
|
|
8918
|
+
if (Object.keys(resolved.values).length > 0) {
|
|
8919
|
+
sections.push(
|
|
8920
|
+
`## Supplied bindings
|
|
8921
|
+
|
|
8922
|
+
\`\`\`json
|
|
8923
|
+
${JSON.stringify(resolved.values, null, 2)}
|
|
8924
|
+
\`\`\``,
|
|
8925
|
+
`## Binding origins
|
|
8926
|
+
|
|
8927
|
+
\`\`\`json
|
|
8928
|
+
${JSON.stringify(resolved.origins, null, 2)}
|
|
8929
|
+
\`\`\``
|
|
8930
|
+
);
|
|
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
|
+
}
|
|
8940
|
+
const assets = nodeAssets(target);
|
|
8941
|
+
if (assets.length > 0) {
|
|
8942
|
+
sections.push(`## Required assets
|
|
8943
|
+
|
|
8944
|
+
${assets.map((asset) => `- ${asset}`).join("\n")}`);
|
|
8945
|
+
}
|
|
8044
8946
|
const deps = flowPredecessors(s.flowDef, nodeId);
|
|
8045
|
-
|
|
8046
|
-
const sections = [];
|
|
8947
|
+
const handoff = [];
|
|
8047
8948
|
for (const depId of deps) {
|
|
8048
8949
|
const exec = s.execution.nodes[depId];
|
|
8049
8950
|
const output = exec?.output;
|
|
@@ -8052,16 +8953,42 @@ var FlowAdapter = class extends AbstractConnector {
|
|
|
8052
8953
|
Artifacts: ${output.artifacts.map((a) => a.uri).join(", ")}` : "";
|
|
8053
8954
|
const concerns = output.concerns && output.concerns.length > 0 ? `
|
|
8054
8955
|
Concerns: ${output.concerns.join("; ")}` : "";
|
|
8055
|
-
const
|
|
8056
|
-
|
|
8956
|
+
const predecessor = s.flowDef.nodes.find((n) => n.id === depId);
|
|
8957
|
+
const label = predecessor ? nodeLabel(predecessor) : depId;
|
|
8958
|
+
handoff.push(`### ${label} (${exec?.status})
|
|
8057
8959
|
${output.summary}${artifacts}${concerns}`);
|
|
8058
8960
|
}
|
|
8059
|
-
|
|
8961
|
+
sections.push(
|
|
8962
|
+
`## Upstream handoff
|
|
8963
|
+
|
|
8964
|
+
${handoff.length > 0 ? handoff.join("\n\n") : "(no prior context)"}`
|
|
8965
|
+
);
|
|
8966
|
+
return sections.join("\n\n");
|
|
8060
8967
|
}
|
|
8061
8968
|
};
|
|
8062
8969
|
function flowPredecessors(flowDef, nodeId) {
|
|
8063
8970
|
return flowDef.edges.filter((e) => e.target === nodeId && e.type === "flow").map((e) => e.source);
|
|
8064
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
|
+
}
|
|
8065
8992
|
function safeParseStringArray(value) {
|
|
8066
8993
|
if (!value) return [];
|
|
8067
8994
|
if (Array.isArray(value)) return value.map(String);
|
|
@@ -8075,6 +9002,27 @@ function safeParseStringArray(value) {
|
|
|
8075
9002
|
}
|
|
8076
9003
|
return [];
|
|
8077
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
|
+
}
|
|
8078
9026
|
function safeParseArtifacts(value) {
|
|
8079
9027
|
if (!value) return [];
|
|
8080
9028
|
const raw = typeof value === "string" ? safeJsonArray(value) : Array.isArray(value) ? value : [];
|
|
@@ -8109,6 +9057,6 @@ function createConnector() {
|
|
|
8109
9057
|
return new FlowAdapter();
|
|
8110
9058
|
}
|
|
8111
9059
|
|
|
8112
|
-
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 };
|
|
8113
|
-
//# sourceMappingURL=chunk-
|
|
8114
|
-
//# 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
|