@codemation/core 0.11.0 → 0.12.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 +16 -0
- package/dist/CostCatalogContract-DD7fQ4FF.d.cts +19 -0
- package/dist/{EngineRuntimeRegistration.types-MPYWsEM0.d.cts → EngineRuntimeRegistration.types-DTV5_7Jw.d.cts} +3 -2
- package/dist/{EngineRuntimeRegistration.types-BZ_1XWAJ.d.ts → EngineRuntimeRegistration.types-Dl92Hdoi.d.ts} +2 -2
- package/dist/InMemoryRunDataFactory-qMiYjhCK.d.cts +202 -0
- package/dist/{InMemoryRunEventBusRegistry-sM4z4n_i.js → InMemoryRunEventBusRegistry-Bwunvt1T.js} +1 -1
- package/dist/{InMemoryRunEventBusRegistry-sM4z4n_i.js.map → InMemoryRunEventBusRegistry-Bwunvt1T.js.map} +1 -1
- package/dist/{InMemoryRunEventBusRegistry-VM3OWnHo.cjs → InMemoryRunEventBusRegistry-Sa86VxuV.cjs} +1 -1
- package/dist/{InMemoryRunEventBusRegistry-VM3OWnHo.cjs.map → InMemoryRunEventBusRegistry-Sa86VxuV.cjs.map} +1 -1
- package/dist/ItemsInputNormalizer-BhuxvZh5.js +36 -0
- package/dist/ItemsInputNormalizer-BhuxvZh5.js.map +1 -0
- package/dist/ItemsInputNormalizer-C09a7iFP.d.ts +321 -0
- package/dist/ItemsInputNormalizer-DLaD6rTl.d.cts +407 -0
- package/dist/ItemsInputNormalizer-Div-fb6a.cjs +43 -0
- package/dist/ItemsInputNormalizer-Div-fb6a.cjs.map +1 -0
- package/dist/RunIntentService-BOSGwmqn.d.ts +299 -0
- package/dist/RunIntentService-CWMMrAP4.d.cts +220 -0
- package/dist/{RunIntentService-MUHJ1bhO.d.cts → agentMcpTypes-DUmniLOY.d.cts} +183 -206
- package/dist/bootstrap/index.cjs +4 -2
- package/dist/bootstrap/index.d.cts +63 -5
- package/dist/bootstrap/index.d.ts +5 -4
- package/dist/bootstrap/index.js +4 -2
- package/dist/{bootstrap-Dgzsjoj7.js → bootstrap-CKTMMNmL.js} +174 -3
- package/dist/bootstrap-CKTMMNmL.js.map +1 -0
- package/dist/{bootstrap-dVmpU1ju.cjs → bootstrap-D460dCgS.cjs} +209 -36
- package/dist/bootstrap-D460dCgS.cjs.map +1 -0
- package/dist/browser.cjs +17 -0
- package/dist/browser.d.cts +4 -0
- package/dist/browser.d.ts +3 -0
- package/dist/browser.js +4 -0
- package/dist/contracts-CK0x6w_G.cjs +74 -0
- package/dist/contracts-CK0x6w_G.cjs.map +1 -0
- package/dist/contracts-DXdfTdpW.js +50 -0
- package/dist/contracts-DXdfTdpW.js.map +1 -0
- package/dist/contracts.cjs +6 -0
- package/dist/contracts.d.cts +5 -0
- package/dist/contracts.d.ts +2 -0
- package/dist/contracts.js +3 -0
- package/dist/di-DdsgWfVy.js +405 -0
- package/dist/di-DdsgWfVy.js.map +1 -0
- package/dist/di-tO6R7VJV.cjs +524 -0
- package/dist/di-tO6R7VJV.cjs.map +1 -0
- package/dist/executionPersistenceContracts-DenJJK2T.d.cts +275 -0
- package/dist/{index-Bes88mxT.d.ts → index-BZDhEQ6W.d.ts} +278 -415
- package/dist/{RunIntentService-BrEq6Jm6.d.ts → index-CSKKuK60.d.ts} +441 -286
- package/dist/index.cjs +97 -250
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +395 -803
- package/dist/index.d.ts +5 -3
- package/dist/index.js +58 -224
- package/dist/index.js.map +1 -1
- package/dist/params-DqRvku2h.d.cts +44 -0
- package/dist/{runtime-Duf3ClPw.js → runtime-BPZgnZ9G.js} +591 -382
- package/dist/runtime-BPZgnZ9G.js.map +1 -0
- package/dist/{runtime-vH0EeZzH.cjs → runtime-CyW9c9XM.cjs} +651 -500
- package/dist/runtime-CyW9c9XM.cjs.map +1 -0
- package/dist/testing.cjs +23 -21
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +3 -2
- package/dist/testing.d.ts +3 -2
- package/dist/testing.js +5 -3
- package/dist/testing.js.map +1 -1
- package/package.json +9 -5
- package/src/ai/AgentConnectionNodeCollector.ts +1 -1
- package/src/authoring/defineHumanApprovalNode.types.ts +379 -0
- package/src/authoring/index.ts +6 -0
- package/src/bootstrap/runtime/EngineRuntimeRegistrar.ts +29 -0
- package/src/contracts/CodemationTelemetryAttributeNames.ts +10 -0
- package/src/contracts/credentialTypes.ts +10 -0
- package/src/contracts/hitlSeamTypes.ts +34 -0
- package/src/contracts/humanTaskStoreTypes.ts +48 -0
- package/src/contracts/inboxChannelTypes.ts +58 -0
- package/src/contracts/index.ts +3 -0
- package/src/contracts/runTypes.ts +61 -3
- package/src/contracts/runtimeTypes.ts +112 -0
- package/src/credentials/CredentialMaterialProvider.types.ts +61 -0
- package/src/credentials/ManagedCredentialMaterialWriteError.ts +14 -0
- package/src/credentials/ManagedMaterialFetchError.ts +16 -0
- package/src/execution/ActivationEnqueueService.ts +16 -0
- package/src/execution/DefaultExecutionContextFactory.ts +11 -0
- package/src/execution/NodeExecutionSnapshotFactory.ts +7 -1
- package/src/execution/NodeExecutor.ts +60 -1
- package/src/execution/NodeExecutorFactory.ts +12 -2
- package/src/execution/NodeSuspensionHandler.ts +220 -0
- package/src/execution/PersistedRunStateTerminalBuilder.ts +5 -2
- package/src/execution/RunStateSemantics.ts +5 -0
- package/src/execution/RunSuspendedError.ts +21 -0
- package/src/index.ts +40 -0
- package/src/orchestration/Engine.ts +12 -2
- package/src/orchestration/EngineWaiters.ts +1 -1
- package/src/orchestration/NodeExecutionRequestHandlerService.ts +25 -2
- package/src/orchestration/RunContinuationService.ts +226 -2
- package/src/orchestration/TestSuiteOrchestrator.ts +5 -4
- package/src/runtime/RunIntentService.ts +3 -0
- package/src/workflow/dsl/ChainCursorResolver.ts +36 -0
- package/tsdown.config.ts +1 -1
- package/dist/InMemoryRunDataFactory-hmkh0lzR.d.cts +0 -138
- package/dist/bootstrap-Dgzsjoj7.js.map +0 -1
- package/dist/bootstrap-dVmpU1ju.cjs.map +0 -1
- package/dist/runtime-Duf3ClPw.js.map +0 -1
- package/dist/runtime-vH0EeZzH.cjs.map +0 -1
|
@@ -1,59 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
-
key = keys[i];
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
-
get: ((k) => from[k]).bind(null, key),
|
|
13
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
-
value: mod,
|
|
20
|
-
enumerable: true
|
|
21
|
-
}) : target, mod));
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
require("reflect-metadata");
|
|
25
|
-
let tsyringe = require("tsyringe");
|
|
26
|
-
tsyringe = __toESM(tsyringe);
|
|
1
|
+
const require_di = require('./di-tO6R7VJV.cjs');
|
|
27
2
|
let zod = require("zod");
|
|
28
|
-
zod = __toESM(zod);
|
|
3
|
+
zod = require_di.__toESM(zod);
|
|
29
4
|
let node_stream_web = require("node:stream/web");
|
|
30
|
-
node_stream_web = __toESM(node_stream_web);
|
|
5
|
+
node_stream_web = require_di.__toESM(node_stream_web);
|
|
31
6
|
let node_crypto = require("node:crypto");
|
|
32
|
-
node_crypto = __toESM(node_crypto);
|
|
7
|
+
node_crypto = require_di.__toESM(node_crypto);
|
|
8
|
+
let tsyringe = require("tsyringe");
|
|
9
|
+
tsyringe = require_di.__toESM(tsyringe);
|
|
10
|
+
|
|
11
|
+
//#region src/contracts/humanTaskStoreTypes.ts
|
|
12
|
+
const HumanTaskStoreToken = Symbol.for("codemation.core.HumanTaskStore");
|
|
33
13
|
|
|
34
|
-
//#
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
AgentMcpIntegration: Symbol.for("codemation.core.AgentMcpIntegration")
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/contracts/hitlSeamTypes.ts
|
|
16
|
+
const HitlResumeTokenSignerToken = Symbol.for("codemation.core.HitlResumeTokenSigner");
|
|
17
|
+
const HitlTimeoutJobSchedulerToken = Symbol.for("codemation.core.HitlTimeoutJobScheduler");
|
|
18
|
+
/**
|
|
19
|
+
* Optional workspace ID injected into NodeSuspensionHandler in managed mode (T7 security fix).
|
|
20
|
+
* Allows the handler to stamp the workspaceId on each HumanTaskRecord so HitlCallbackHandler
|
|
21
|
+
* can assert workspace identity independently of the HMAC middleware.
|
|
22
|
+
* Not registered in non-managed mode; NodeSuspensionHandler defaults to null.
|
|
23
|
+
*/
|
|
24
|
+
const HitlWorkspaceIdToken = Symbol.for("codemation.core.HitlWorkspaceId");
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/authoring/DefinedNodeRegistry.ts
|
|
28
|
+
var DefinedNodeRegistry = class {
|
|
29
|
+
static definitions = /* @__PURE__ */ new Map();
|
|
30
|
+
static register(definition) {
|
|
31
|
+
this.definitions.set(definition.key, definition);
|
|
32
|
+
}
|
|
33
|
+
static resolve(key) {
|
|
34
|
+
return this.definitions.get(key);
|
|
35
|
+
}
|
|
57
36
|
};
|
|
58
37
|
|
|
59
38
|
//#endregion
|
|
@@ -166,287 +145,303 @@ function chatModel(options = {}) {
|
|
|
166
145
|
}
|
|
167
146
|
|
|
168
147
|
//#endregion
|
|
169
|
-
//#region src/
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return
|
|
198
|
-
}
|
|
199
|
-
toNodeItem(args) {
|
|
200
|
-
const mapped = this.mapInputValue?.(args) ?? args.input;
|
|
201
|
-
if (this.isItem(mapped)) return mapped;
|
|
202
|
-
return { json: mapped };
|
|
203
|
-
}
|
|
204
|
-
toToolOutput(args) {
|
|
205
|
-
const raw = this.mapOutputValue?.(args) ?? this.readDefaultToolOutput(args.outputs);
|
|
206
|
-
return this.outputSchemaValue.parse(raw);
|
|
207
|
-
}
|
|
208
|
-
readDefaultToolOutput(outputs) {
|
|
209
|
-
const firstMainItem = outputs.main?.[0];
|
|
210
|
-
if (!firstMainItem) throw new Error(`Node-backed tool "${this.name}" did not produce a main output item.`);
|
|
211
|
-
return firstMainItem.json;
|
|
212
|
-
}
|
|
213
|
-
isItem(value) {
|
|
214
|
-
return typeof value === "object" && value !== null && "json" in value;
|
|
148
|
+
//#region src/authoring/defineNode.types.ts
|
|
149
|
+
const definedNodeCredentialRequirementFactory = {
|
|
150
|
+
create(bindings) {
|
|
151
|
+
if (!bindings) return [];
|
|
152
|
+
return Object.entries(bindings).map(([slotKey, binding]) => {
|
|
153
|
+
if (typeof binding === "string" || this.isCredentialType(binding)) return {
|
|
154
|
+
slotKey,
|
|
155
|
+
label: this.humanize(slotKey),
|
|
156
|
+
acceptedTypes: [this.resolveTypeId(binding)]
|
|
157
|
+
};
|
|
158
|
+
const types = Array.isArray(binding.type) ? binding.type : [binding.type];
|
|
159
|
+
return {
|
|
160
|
+
slotKey,
|
|
161
|
+
label: binding.label ?? this.humanize(slotKey),
|
|
162
|
+
acceptedTypes: types.map((entry) => this.resolveTypeId(entry)),
|
|
163
|
+
optional: binding.optional,
|
|
164
|
+
helpText: binding.helpText,
|
|
165
|
+
helpUrl: binding.helpUrl
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
isCredentialType(value) {
|
|
170
|
+
return Boolean(value) && typeof value === "object" && "definition" in value && typeof value.definition?.typeId === "string";
|
|
171
|
+
},
|
|
172
|
+
resolveTypeId(type) {
|
|
173
|
+
return typeof type === "string" ? type : type.definition.typeId;
|
|
174
|
+
},
|
|
175
|
+
humanize(key) {
|
|
176
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_.]+/g, " ").replace(/\s+/g, " ").trim().replace(/^./, (character) => character.toUpperCase());
|
|
215
177
|
}
|
|
216
178
|
};
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
179
|
+
const definedNodeCredentialAccessorFactory = { create(bindings, ctx) {
|
|
180
|
+
if (!bindings) return {};
|
|
181
|
+
const entries = Object.keys(bindings).map((slotKey) => [slotKey, () => ctx.getCredential(slotKey)]);
|
|
182
|
+
return Object.fromEntries(entries);
|
|
183
|
+
} };
|
|
184
|
+
function defineNode(options) {
|
|
185
|
+
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
186
|
+
const DefinedNodeRuntime = class {
|
|
187
|
+
kind = "node";
|
|
188
|
+
outputPorts = ["main"];
|
|
189
|
+
inputSchema = options.inputSchema;
|
|
190
|
+
async execute(args) {
|
|
191
|
+
const ctx = args.ctx;
|
|
192
|
+
const context = {
|
|
193
|
+
config: ctx.config.config,
|
|
194
|
+
credentials: definedNodeCredentialAccessorFactory.create(options.credentials, ctx),
|
|
195
|
+
execution: ctx
|
|
196
|
+
};
|
|
197
|
+
const payload = {
|
|
198
|
+
input: args.input,
|
|
199
|
+
item: args.item,
|
|
200
|
+
itemIndex: args.itemIndex,
|
|
201
|
+
items: args.items,
|
|
202
|
+
ctx
|
|
203
|
+
};
|
|
204
|
+
return await options.execute(payload, context);
|
|
205
|
+
}
|
|
225
206
|
};
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Deep-resolves {@link itemExpr} leaves. Returns a new graph (does not mutate the original config object).
|
|
247
|
-
*/
|
|
248
|
-
async function resolveItemExprsInUnknown(value, args, seen = /* @__PURE__ */ new WeakSet()) {
|
|
249
|
-
if (isItemExpr(value)) return await Promise.resolve(value.fn(args));
|
|
250
|
-
if (value === null || typeof value !== "object") return value;
|
|
251
|
-
if (seen.has(value)) return value;
|
|
252
|
-
seen.add(value);
|
|
253
|
-
if (Array.isArray(value)) {
|
|
254
|
-
const out$1 = [];
|
|
255
|
-
for (let i = 0; i < value.length; i++) out$1.push(await resolveItemExprsInUnknown(value[i], args, seen));
|
|
256
|
-
return out$1;
|
|
257
|
-
}
|
|
258
|
-
const rec = value;
|
|
259
|
-
const entries = Object.entries(rec);
|
|
260
|
-
const proto = Object.getPrototypeOf(value);
|
|
261
|
-
if (proto !== Object.prototype && proto !== null && entries.length === 0) return value;
|
|
262
|
-
const out = Object.create(proto);
|
|
263
|
-
for (const [k, v] of entries) out[k] = await resolveItemExprsInUnknown(v, args, seen);
|
|
264
|
-
return out;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Clones runnable config (best-effort) so per-item {@link itemExpr} resolution never mutates shared instances.
|
|
268
|
-
*/
|
|
269
|
-
async function resolveItemExprsForExecution(config, nodeCtx, item, itemIndex, items) {
|
|
270
|
-
const exprArgs = {
|
|
271
|
-
item,
|
|
272
|
-
itemIndex,
|
|
273
|
-
items,
|
|
274
|
-
ctx: {
|
|
275
|
-
runId: nodeCtx.runId,
|
|
276
|
-
workflowId: nodeCtx.workflowId,
|
|
277
|
-
nodeId: nodeCtx.nodeId,
|
|
278
|
-
activationId: nodeCtx.activationId,
|
|
279
|
-
data: nodeCtx.data
|
|
207
|
+
node({ name: options.key })(DefinedNodeRuntime);
|
|
208
|
+
const DefinedRunnableNodeConfig = class {
|
|
209
|
+
kind = "node";
|
|
210
|
+
type = DefinedNodeRuntime;
|
|
211
|
+
icon = options.icon;
|
|
212
|
+
inputSchema = options.inputSchema;
|
|
213
|
+
keepBinaries = options.keepBinaries ?? false;
|
|
214
|
+
constructor(name, config, id) {
|
|
215
|
+
this.name = name;
|
|
216
|
+
this.id = id;
|
|
217
|
+
this.config = config;
|
|
218
|
+
}
|
|
219
|
+
config;
|
|
220
|
+
getCredentialRequirements() {
|
|
221
|
+
return credentialRequirements;
|
|
222
|
+
}
|
|
223
|
+
inspectorSummary() {
|
|
224
|
+
return options.inspectorSummary?.({ config: this.config });
|
|
280
225
|
}
|
|
281
226
|
};
|
|
282
|
-
|
|
283
|
-
|
|
227
|
+
const definition = {
|
|
228
|
+
kind: "defined-node",
|
|
229
|
+
key: options.key,
|
|
230
|
+
title: options.title,
|
|
231
|
+
description: options.description,
|
|
232
|
+
create(config, name = options.title, id) {
|
|
233
|
+
return new DefinedRunnableNodeConfig(name, config, id);
|
|
234
|
+
},
|
|
235
|
+
register(context) {
|
|
236
|
+
context.registerNode(DefinedNodeRuntime);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
DefinedNodeRegistry.register(definition);
|
|
240
|
+
return definition;
|
|
284
241
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (isItemExpr(messages)) return true;
|
|
300
|
-
const o = messages;
|
|
301
|
-
return Array.isArray(o.prompt) && o.prompt.length > 0 || typeof o.buildMessages === "function";
|
|
242
|
+
function defineBatchNode(options) {
|
|
243
|
+
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
244
|
+
const DefinedNodeRuntime = class {
|
|
245
|
+
kind = "node";
|
|
246
|
+
outputPorts = ["main"];
|
|
247
|
+
async execute(args) {
|
|
248
|
+
if (args.itemIndex !== args.items.length - 1) return [];
|
|
249
|
+
const ctx = args.ctx;
|
|
250
|
+
const context = {
|
|
251
|
+
config: ctx.config.config,
|
|
252
|
+
credentials: definedNodeCredentialAccessorFactory.create(options.credentials, ctx),
|
|
253
|
+
execution: ctx
|
|
254
|
+
};
|
|
255
|
+
return [...await options.run(args.items.map((item) => item.json), context)];
|
|
302
256
|
}
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
257
|
+
};
|
|
258
|
+
node({ name: options.key })(DefinedNodeRuntime);
|
|
259
|
+
const DefinedRunnableNodeConfig = class {
|
|
260
|
+
kind = "node";
|
|
261
|
+
type = DefinedNodeRuntime;
|
|
262
|
+
icon = options.icon;
|
|
263
|
+
constructor(name, config, id) {
|
|
264
|
+
this.name = name;
|
|
265
|
+
this.id = id;
|
|
266
|
+
this.config = config;
|
|
267
|
+
}
|
|
268
|
+
config;
|
|
269
|
+
getCredentialRequirements() {
|
|
270
|
+
return credentialRequirements;
|
|
271
|
+
}
|
|
272
|
+
inspectorSummary() {
|
|
273
|
+
return options.inspectorSummary?.({ config: this.config });
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const definition = {
|
|
277
|
+
kind: "defined-node",
|
|
278
|
+
key: options.key,
|
|
279
|
+
title: options.title,
|
|
280
|
+
description: options.description,
|
|
281
|
+
create(config, name = options.title, id) {
|
|
282
|
+
return new DefinedRunnableNodeConfig(name, config, id);
|
|
283
|
+
},
|
|
284
|
+
register(context) {
|
|
285
|
+
context.registerNode(DefinedNodeRuntime);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
DefinedNodeRegistry.register(definition);
|
|
289
|
+
return definition;
|
|
290
|
+
}
|
|
306
291
|
|
|
307
292
|
//#endregion
|
|
308
|
-
//#region src/
|
|
293
|
+
//#region src/authoring/defineHumanApprovalNode.types.ts
|
|
309
294
|
/**
|
|
310
|
-
*
|
|
311
|
-
*
|
|
295
|
+
* Returns `true` when `node` was created by {@link defineHumanApprovalNode}.
|
|
296
|
+
* Uses the `humanApprovalToolBehavior` typed field as the discriminant.
|
|
312
297
|
*/
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
for (const tool$1 of agentConfig.tools ?? []) {
|
|
396
|
-
const toolNodeId = ConnectionNodeIdFactory.toolConnectionNodeId(parentNodeId, tool$1.name);
|
|
397
|
-
const isNestedAgent = this.isNodeBackedAgentTool(tool$1);
|
|
398
|
-
collected.push({
|
|
399
|
-
nodeId: toolNodeId,
|
|
400
|
-
parentNodeId,
|
|
401
|
-
connectionName: "tools",
|
|
402
|
-
role: isNestedAgent ? "nestedAgent" : "tool",
|
|
403
|
-
name: tool$1.presentation?.label ?? tool$1.name,
|
|
404
|
-
typeName: tool$1.name,
|
|
405
|
-
icon: tool$1.presentation?.icon,
|
|
406
|
-
credentialSource: tool$1
|
|
407
|
-
});
|
|
408
|
-
this.collectNestedAgentTools(toolNodeId, tool$1, collected, mcpServerResolver);
|
|
409
|
-
}
|
|
410
|
-
if (mcpServerResolver) {
|
|
411
|
-
const mcpServers = agentConfig.mcpServers;
|
|
412
|
-
for (const serverId of mcpServers ?? []) {
|
|
413
|
-
const decl = mcpServerResolver(serverId);
|
|
414
|
-
if (!decl) continue;
|
|
415
|
-
const acceptedTypes = decl.acceptedCredentialTypes ?? [];
|
|
416
|
-
collected.push({
|
|
417
|
-
nodeId: ConnectionNodeIdFactory.mcpConnectionNodeId(parentNodeId, serverId),
|
|
418
|
-
parentNodeId,
|
|
419
|
-
connectionName: "tools",
|
|
420
|
-
role: "tool",
|
|
421
|
-
name: decl.displayName,
|
|
422
|
-
typeName: serverId,
|
|
423
|
-
icon: "lucide:plug",
|
|
424
|
-
credentialSource: { getCredentialRequirements: () => [{
|
|
425
|
-
slotKey: "credential",
|
|
426
|
-
label: decl.displayName,
|
|
427
|
-
acceptedTypes
|
|
428
|
-
}] }
|
|
298
|
+
function isHumanApprovalNode(node$1) {
|
|
299
|
+
return typeof node$1 === "object" && node$1 !== null && "humanApprovalToolBehavior" in node$1 && typeof node$1.humanApprovalToolBehavior === "object";
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Authoring helper that compiles a HITL approval channel down to a regular
|
|
303
|
+
* {@link defineNode}-backed node with `SuspensionRequest` semantics.
|
|
304
|
+
*
|
|
305
|
+
* **Fast-forward decision semantics:**
|
|
306
|
+
* - On the first `execute` call (no `ctx.resumeContext`): throws a `SuspensionRequest`
|
|
307
|
+
* that calls the author's `deliver`. The engine persists the suspension and continues.
|
|
308
|
+
* - On resume (`ctx.resumeContext` set): calls `onDecision`/`onTimeout` as appropriate,
|
|
309
|
+
* merges a `decision` key into `item.json`, and returns an item with the original
|
|
310
|
+
* `binary` map passed by reference (no copy).
|
|
311
|
+
*
|
|
312
|
+
* **Output shape per item:**
|
|
313
|
+
* ```ts
|
|
314
|
+
* // Input: { json: { invoiceId: 42 }, binary?: {...} }
|
|
315
|
+
* // Output: { json: { invoiceId: 42, decision: { status: "approved", actor, decidedAt } }, binary: <unchanged> }
|
|
316
|
+
* ```
|
|
317
|
+
* If `item.json` already has a `decision` key it is **overwritten**. Namespace as
|
|
318
|
+
* needed if your schema reserves that key for another purpose.
|
|
319
|
+
*
|
|
320
|
+
* **Predicate persistence:**
|
|
321
|
+
* The `approvedPredicate` function is NOT serialized to the suspension record (except
|
|
322
|
+
* as an audit-only string via `toString()`). On resume, the workflow definition is
|
|
323
|
+
* reloaded from code at process start and the predicate closure is rebuilt naturally.
|
|
324
|
+
* If a deploy ships a changed predicate between suspend and resume, the *new* predicate
|
|
325
|
+
* runs — document this in your runbook when the predicate carries business logic that
|
|
326
|
+
* may change across deploys.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* export const slackApprovalNode = defineHumanApprovalNode({
|
|
331
|
+
* key: "my-plugin.slackApproval",
|
|
332
|
+
* title: "Slack Approval",
|
|
333
|
+
* channel: "slack",
|
|
334
|
+
* configSchema: z.object({ channel: z.string(), message: z.string() }),
|
|
335
|
+
* decisionSchema: z.object({ approved: z.boolean(), note: z.string().optional() }),
|
|
336
|
+
*
|
|
337
|
+
* async deliver({ task, config, item }, ctx) {
|
|
338
|
+
* const ts = await postSlackMessage(config.channel, `Approve? <${task.resumeUrl}>`);
|
|
339
|
+
* return { channel: config.channel, ts };
|
|
340
|
+
* },
|
|
341
|
+
*
|
|
342
|
+
* async onDecision({ decision, actor, delivery }, ctx) {
|
|
343
|
+
* await updateSlackMessage(delivery.channel, delivery.ts, decision.approved ? "✅" : "❌");
|
|
344
|
+
* },
|
|
345
|
+
* });
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
function defineHumanApprovalNode(opts) {
|
|
349
|
+
const resolvedPredicate = resolveApprovedPredicate(opts.decisionSchema, opts.approvedPredicate);
|
|
350
|
+
const timeout = opts.defaultTimeout ?? "24h";
|
|
351
|
+
const onTimeout = opts.defaultOnTimeout ?? "halt";
|
|
352
|
+
const inner = defineNode({
|
|
353
|
+
key: opts.key,
|
|
354
|
+
title: opts.title,
|
|
355
|
+
description: opts.description,
|
|
356
|
+
icon: opts.icon,
|
|
357
|
+
configSchema: opts.configSchema,
|
|
358
|
+
inputSchema: opts.inputSchema,
|
|
359
|
+
credentials: opts.credentials,
|
|
360
|
+
inspectorSummary: opts.inspectorSummary ? ({ config }) => opts.inspectorSummary(config) : void 0,
|
|
361
|
+
async execute(args, { config, execution: ctx }) {
|
|
362
|
+
if (!ctx.resumeContext) {
|
|
363
|
+
const subject = buildSubject(opts.title, args.item, ctx);
|
|
364
|
+
throw new require_di.SuspensionRequest({
|
|
365
|
+
decisionSchema: opts.decisionSchema,
|
|
366
|
+
timeout,
|
|
367
|
+
onTimeout,
|
|
368
|
+
subject,
|
|
369
|
+
metadata: {
|
|
370
|
+
channel: opts.channel,
|
|
371
|
+
nodeKey: opts.key,
|
|
372
|
+
approvedPredicateSource: opts.approvedPredicate?.toString() ?? null
|
|
373
|
+
},
|
|
374
|
+
deliver: (handle) => opts.deliver({
|
|
375
|
+
task: handle,
|
|
376
|
+
config,
|
|
377
|
+
input: args.input,
|
|
378
|
+
item: args.item
|
|
379
|
+
}, ctx)
|
|
429
380
|
});
|
|
430
381
|
}
|
|
382
|
+
return await handleResume(args.item, ctx.resumeContext, opts.decisionSchema, resolvedPredicate, opts.onDecision, opts.onTimeout, ctx);
|
|
431
383
|
}
|
|
384
|
+
});
|
|
385
|
+
return Object.assign(inner, { humanApprovalToolBehavior: { onRejected: "return" } });
|
|
386
|
+
}
|
|
387
|
+
function resolveApprovedPredicate(schema, predicate) {
|
|
388
|
+
if (predicate) return predicate;
|
|
389
|
+
const shape = schema.shape;
|
|
390
|
+
if (shape && typeof shape === "object" && "approved" in shape) return (d) => d.approved === true;
|
|
391
|
+
throw new Error("defineHumanApprovalNode: decisionSchema has no \"approved\" field and no approvedPredicate was provided. Either add { approved: z.boolean() } to the decision schema or supply approvedPredicate explicitly.");
|
|
392
|
+
}
|
|
393
|
+
function buildSubject(title, item, ctx) {
|
|
394
|
+
return {
|
|
395
|
+
title,
|
|
396
|
+
summary: "",
|
|
397
|
+
attributes: {
|
|
398
|
+
workflowId: ctx.workflowId,
|
|
399
|
+
nodeId: ctx.nodeId,
|
|
400
|
+
item: item.json
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function mergeDecision(item, decision) {
|
|
405
|
+
return {
|
|
406
|
+
json: {
|
|
407
|
+
...item.json,
|
|
408
|
+
decision
|
|
409
|
+
},
|
|
410
|
+
binary: item.binary,
|
|
411
|
+
meta: item.meta
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async function handleResume(item, resumeContext, decisionSchema, resolvedPredicate, onDecision, onTimeoutCb, ctx) {
|
|
415
|
+
const { decision: dec, delivery, task } = resumeContext;
|
|
416
|
+
if (dec.kind === "timed_out" || dec.kind === "auto_accepted") {
|
|
417
|
+
const policy = dec.kind === "auto_accepted" ? "auto-accept" : "halt";
|
|
418
|
+
await onTimeoutCb?.({
|
|
419
|
+
task,
|
|
420
|
+
delivery,
|
|
421
|
+
item,
|
|
422
|
+
policy
|
|
423
|
+
}, ctx);
|
|
424
|
+
return mergeDecision(item, {
|
|
425
|
+
status: dec.kind === "auto_accepted" ? "auto-accepted" : "timed-out",
|
|
426
|
+
decidedAt: dec.at
|
|
427
|
+
});
|
|
432
428
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}();
|
|
429
|
+
const parsed = decisionSchema.parse(dec.value);
|
|
430
|
+
await onDecision?.({
|
|
431
|
+
decision: parsed,
|
|
432
|
+
actor: dec.actor,
|
|
433
|
+
task,
|
|
434
|
+
delivery,
|
|
435
|
+
item
|
|
436
|
+
}, ctx);
|
|
437
|
+
return mergeDecision(item, {
|
|
438
|
+
status: resolvedPredicate(parsed) ? "approved" : "rejected",
|
|
439
|
+
actor: dec.actor,
|
|
440
|
+
decidedAt: dec.decidedAt,
|
|
441
|
+
note: parsed.note,
|
|
442
|
+
payload: parsed
|
|
443
|
+
});
|
|
444
|
+
}
|
|
450
445
|
|
|
451
446
|
//#endregion
|
|
452
447
|
//#region src/workflow/dsl/WhenBuilder.ts
|
|
@@ -574,6 +569,25 @@ var ChainCursor = class ChainCursor {
|
|
|
574
569
|
}
|
|
575
570
|
return new ChainCursor(this.wf, nextEndpoints);
|
|
576
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Chainable shorthand for `.then(node.create(config, metadata?.name, metadata?.nodeId))`.
|
|
574
|
+
*
|
|
575
|
+
* Signals to readers that this step suspends the run and waits for a human decision.
|
|
576
|
+
* Throws at workflow-build time if `node` was not created via `defineHumanApprovalNode`.
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```ts
|
|
580
|
+
* workflow
|
|
581
|
+
* .trigger(...)
|
|
582
|
+
* .humanApproval(inboxApproval, { title: "Approve?", body: "...", priority: "normal" })
|
|
583
|
+
* .then(nextStep.create(...))
|
|
584
|
+
* .build();
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
humanApproval(node$1, config, metadata) {
|
|
588
|
+
if (!isHumanApprovalNode(node$1)) throw new Error(`.humanApproval() requires a node created via defineHumanApprovalNode (got '${node$1.key ?? String(node$1)}').`);
|
|
589
|
+
return this.then(node$1.create(config, metadata?.name, metadata?.nodeId));
|
|
590
|
+
}
|
|
577
591
|
build() {
|
|
578
592
|
return this.wf.build();
|
|
579
593
|
}
|
|
@@ -685,7 +699,7 @@ var WorkflowBuilder = class {
|
|
|
685
699
|
tokenName,
|
|
686
700
|
label: node$1.name ?? ""
|
|
687
701
|
});
|
|
688
|
-
if (AgentConfigInspector.isAgentNodeConfig(node$1.config)) for (const child of AgentConnectionNodeCollector.collect(node$1.id, node$1.config)) entries.push({
|
|
702
|
+
if (require_di.AgentConfigInspector.isAgentNodeConfig(node$1.config)) for (const child of require_di.AgentConnectionNodeCollector.collect(node$1.id, node$1.config)) entries.push({
|
|
689
703
|
nodeId: child.nodeId,
|
|
690
704
|
tokenName: child.typeName,
|
|
691
705
|
label: child.name
|
|
@@ -738,33 +752,6 @@ var ConnectionInvocationIdFactory = class {
|
|
|
738
752
|
}
|
|
739
753
|
};
|
|
740
754
|
|
|
741
|
-
//#endregion
|
|
742
|
-
//#region src/workflow/definition/NodeIterationIdFactory.ts
|
|
743
|
-
/**
|
|
744
|
-
* Unique ids for one per-item iteration of a runnable node's execute loop.
|
|
745
|
-
*
|
|
746
|
-
* Activations are per-batch (one scheduled execution of a node, possibly with N items).
|
|
747
|
-
* Iterations refine that to one identifier per item-index inside the batch loop, so per-item
|
|
748
|
-
* connection invocations and telemetry can be grouped without time-window heuristics.
|
|
749
|
-
*
|
|
750
|
-
* Uses Web Crypto's `randomUUID` (Node 19+ and all modern browsers) so this module is safe
|
|
751
|
-
* to include in the browser entry. Importing `node:crypto` here previously leaked into the
|
|
752
|
-
* canvas client bundle through `browser.ts` and OOM'd consumers' Turbopack builds.
|
|
753
|
-
*/
|
|
754
|
-
var NodeIterationIdFactory = class {
|
|
755
|
-
static create() {
|
|
756
|
-
return `iter_${globalThis.crypto.randomUUID()}`;
|
|
757
|
-
}
|
|
758
|
-
/** Deterministic id for tests when a stable sequence is needed. */
|
|
759
|
-
static createForTest(seed, sequence) {
|
|
760
|
-
return `iter_${seed}_${sequence}`;
|
|
761
|
-
}
|
|
762
|
-
/** Deterministic id derived from a connection node id (for sub-agent / tool-call scopes). */
|
|
763
|
-
static createForConnection(connectionNodeId, sequence) {
|
|
764
|
-
return `iter_${connectionNodeId}_${sequence}`;
|
|
765
|
-
}
|
|
766
|
-
};
|
|
767
|
-
|
|
768
755
|
//#endregion
|
|
769
756
|
//#region src/workflow/definition/WorkflowExecutableNodeClassifier.ts
|
|
770
757
|
/**
|
|
@@ -1067,7 +1054,7 @@ var NodeExecutionSnapshotFactory = class {
|
|
|
1067
1054
|
nodeId: args.nodeId,
|
|
1068
1055
|
activationId: args.activationId,
|
|
1069
1056
|
parent: args.parent,
|
|
1070
|
-
status: "completed",
|
|
1057
|
+
status: args.hitlStatus ?? "completed",
|
|
1071
1058
|
queuedAt: args.previous?.queuedAt,
|
|
1072
1059
|
startedAt,
|
|
1073
1060
|
finishedAt: args.finishedAt,
|
|
@@ -1194,7 +1181,9 @@ var ActivationEnqueueService = class {
|
|
|
1194
1181
|
nodeSnapshotsByNodeId: {
|
|
1195
1182
|
...args.previousNodeSnapshotsByNodeId,
|
|
1196
1183
|
[args.request.nodeId]: queuedSnapshot
|
|
1197
|
-
}
|
|
1184
|
+
},
|
|
1185
|
+
...args.suspension !== void 0 ? { suspension: args.suspension } : {},
|
|
1186
|
+
...args.pendingResume !== void 0 ? { pendingResume: args.pendingResume } : {}
|
|
1198
1187
|
});
|
|
1199
1188
|
await this.dispatchPreparedActivation(preparedDispatch);
|
|
1200
1189
|
return {
|
|
@@ -1446,20 +1435,6 @@ var DefaultAsyncSleeper = class {
|
|
|
1446
1435
|
}
|
|
1447
1436
|
};
|
|
1448
1437
|
|
|
1449
|
-
//#endregion
|
|
1450
|
-
//#region src/contracts/NoOpAgentMcpIntegration.ts
|
|
1451
|
-
/**
|
|
1452
|
-
* No-op implementation of AgentMcpIntegration.
|
|
1453
|
-
* Registered by the core engine runtime as a fallback when the host does not
|
|
1454
|
-
* supply a real implementation (e.g. in unit tests or headless engine setups).
|
|
1455
|
-
* Always returns an empty tool map so the agent runs with node-backed tools only.
|
|
1456
|
-
*/
|
|
1457
|
-
var NoOpAgentMcpIntegration = class {
|
|
1458
|
-
async prepareMcpTools() {
|
|
1459
|
-
return /* @__PURE__ */ new Map();
|
|
1460
|
-
}
|
|
1461
|
-
};
|
|
1462
|
-
|
|
1463
1438
|
//#endregion
|
|
1464
1439
|
//#region src/contracts/emitPorts.ts
|
|
1465
1440
|
const EMIT_PORTS_BRAND = Symbol.for("codemation.emitPorts");
|
|
@@ -1522,21 +1497,6 @@ var ExpRetryPolicy = class {
|
|
|
1522
1497
|
}
|
|
1523
1498
|
};
|
|
1524
1499
|
|
|
1525
|
-
//#endregion
|
|
1526
|
-
//#region src/contracts/credentialTypes.ts
|
|
1527
|
-
var CredentialUnboundError = class CredentialUnboundError extends Error {
|
|
1528
|
-
constructor(bindingKey, acceptedTypes = []) {
|
|
1529
|
-
super(CredentialUnboundError.createMessage(bindingKey, acceptedTypes));
|
|
1530
|
-
this.bindingKey = bindingKey;
|
|
1531
|
-
this.acceptedTypes = acceptedTypes;
|
|
1532
|
-
this.name = "CredentialUnboundError";
|
|
1533
|
-
}
|
|
1534
|
-
static createMessage(bindingKey, acceptedTypes) {
|
|
1535
|
-
const acceptedTypesSuffix = acceptedTypes.length > 0 ? ` Accepted credential types: ${acceptedTypes.join(", ")}.` : "";
|
|
1536
|
-
return `Credential slot "${bindingKey.slotKey}" is not bound for workflow ${bindingKey.workflowId} node ${bindingKey.nodeId}.${acceptedTypesSuffix}`;
|
|
1537
|
-
}
|
|
1538
|
-
};
|
|
1539
|
-
|
|
1540
1500
|
//#endregion
|
|
1541
1501
|
//#region src/contracts/CostTrackingTelemetryContract.ts
|
|
1542
1502
|
const CostTrackingTelemetryMetricNames = {
|
|
@@ -1658,19 +1618,6 @@ var NoOpExecutionTelemetryFactory = class {
|
|
|
1658
1618
|
}
|
|
1659
1619
|
};
|
|
1660
1620
|
|
|
1661
|
-
//#endregion
|
|
1662
|
-
//#region src/contracts/runFinishedAtFactory.ts
|
|
1663
|
-
/** Derives workflow end time from persisted run root or node snapshots for run listings. */
|
|
1664
|
-
var RunFinishedAtFactory = class {
|
|
1665
|
-
static resolveIso(state) {
|
|
1666
|
-
if (state.finishedAt && state.status !== "running" && state.status !== "pending") return state.finishedAt;
|
|
1667
|
-
if (state.status === "running" || state.status === "pending") return;
|
|
1668
|
-
let max;
|
|
1669
|
-
for (const snap of Object.values(state.nodeSnapshotsByNodeId)) if (snap?.finishedAt && (!max || snap.finishedAt > max)) max = snap.finishedAt;
|
|
1670
|
-
return max;
|
|
1671
|
-
}
|
|
1672
|
-
};
|
|
1673
|
-
|
|
1674
1621
|
//#endregion
|
|
1675
1622
|
//#region src/contracts/workflowActivationPolicy.ts
|
|
1676
1623
|
/** Default for tests and harnesses: every workflow is treated as active (legacy behavior). */
|
|
@@ -1703,6 +1650,16 @@ var CodemationTelemetryAttributeNames = class {
|
|
|
1703
1650
|
static mcpServerId = "mcp.server_id";
|
|
1704
1651
|
/** MCP tool name on spans created for callTool invocations. */
|
|
1705
1652
|
static mcpToolName = "mcp.tool_name";
|
|
1653
|
+
/** Terminal node-execution status (e.g. `"hitl-approved"`, `"hitl-rejected"`) on HITL outcome spans. */
|
|
1654
|
+
static nodeExecutionStatus = "codemation.node.execution_status";
|
|
1655
|
+
/** Populated on run-halted spans; discriminates the halt reason (e.g. `"hitl-rejected"`). */
|
|
1656
|
+
static runHaltReason = "codemation.run.halt_reason";
|
|
1657
|
+
/** Human task ID on `hitl.task.*` span events. */
|
|
1658
|
+
static hitlTaskId = "codemation.hitl.task_id";
|
|
1659
|
+
/** HITL channel name (e.g. `"inbox"`, `"control-plane-inbox"`) on `hitl.task.*` span events. */
|
|
1660
|
+
static hitlChannel = "codemation.hitl.channel";
|
|
1661
|
+
/** Decision outcome (e.g. `"approved"`, `"rejected"`) on `hitl.task.decided` span events. */
|
|
1662
|
+
static hitlDecisionStatus = "codemation.hitl.decision_status";
|
|
1706
1663
|
};
|
|
1707
1664
|
|
|
1708
1665
|
//#endregion
|
|
@@ -1728,13 +1685,6 @@ var CodemationTelemetryMetricNames = class {
|
|
|
1728
1685
|
static gmailAttachmentBytes = "codemation.gmail.attachment_bytes";
|
|
1729
1686
|
};
|
|
1730
1687
|
|
|
1731
|
-
//#endregion
|
|
1732
|
-
//#region src/contracts/workflowTypes.ts
|
|
1733
|
-
function nodeRef(nodeId) {
|
|
1734
|
-
return nodeId;
|
|
1735
|
-
}
|
|
1736
|
-
const branchRef = (index) => `$${index}`;
|
|
1737
|
-
|
|
1738
1688
|
//#endregion
|
|
1739
1689
|
//#region src/execution/ExecutionTelemetryCostTrackingDecoratorFactory.ts
|
|
1740
1690
|
var ExecutionTelemetryCostTrackingDecoratorFactory = class {
|
|
@@ -1808,12 +1758,13 @@ var ExecutionTelemetryCostTrackingDecoratorFactory = class {
|
|
|
1808
1758
|
//#region src/execution/DefaultExecutionContextFactory.ts
|
|
1809
1759
|
var DefaultExecutionContextFactory = class {
|
|
1810
1760
|
telemetryDecoratorFactory = new ExecutionTelemetryCostTrackingDecoratorFactory();
|
|
1811
|
-
constructor(binaryStorage = new UnavailableBinaryStorage(), telemetryFactory = new NoOpExecutionTelemetryFactory(), costTrackingFactory = new NoOpCostTrackingTelemetryFactory(), currentDate = () => /* @__PURE__ */ new Date(), collections) {
|
|
1761
|
+
constructor(binaryStorage = new UnavailableBinaryStorage(), telemetryFactory = new NoOpExecutionTelemetryFactory(), costTrackingFactory = new NoOpCostTrackingTelemetryFactory(), currentDate = () => /* @__PURE__ */ new Date(), collections, nodeResolver) {
|
|
1812
1762
|
this.binaryStorage = binaryStorage;
|
|
1813
1763
|
this.telemetryFactory = telemetryFactory;
|
|
1814
1764
|
this.costTrackingFactory = costTrackingFactory;
|
|
1815
1765
|
this.currentDate = currentDate;
|
|
1816
1766
|
this.collections = collections;
|
|
1767
|
+
this.nodeResolver = nodeResolver;
|
|
1817
1768
|
}
|
|
1818
1769
|
create(args) {
|
|
1819
1770
|
const baseTelemetry = args.telemetry ?? this.telemetryFactory.create({
|
|
@@ -1840,7 +1791,11 @@ var DefaultExecutionContextFactory = class {
|
|
|
1840
1791
|
binary: new DefaultExecutionBinaryService(this.binaryStorage, args.workflowId, args.runId, this.currentDate),
|
|
1841
1792
|
getCredential: args.getCredential,
|
|
1842
1793
|
testContext: args.testContext,
|
|
1843
|
-
collections: this.collections
|
|
1794
|
+
collections: this.collections,
|
|
1795
|
+
resolve: (token) => {
|
|
1796
|
+
if (!this.nodeResolver) throw new Error("ExecutionContext.resolve() is not available: no NodeResolver was provided to DefaultExecutionContextFactory.");
|
|
1797
|
+
return this.nodeResolver.resolve(token);
|
|
1798
|
+
}
|
|
1844
1799
|
};
|
|
1845
1800
|
}
|
|
1846
1801
|
};
|
|
@@ -2016,7 +1971,7 @@ var InProcessRetryRunner = class InProcessRetryRunner {
|
|
|
2016
1971
|
var ItemExprResolver = class {
|
|
2017
1972
|
async resolveConfigForItem(ctx, item, itemIndex, items) {
|
|
2018
1973
|
if (!ctx) throw new Error("ItemExprResolver.resolveConfigForItem: ctx is required");
|
|
2019
|
-
const resolvedConfig = await resolveItemExprsForExecution(ctx.config, ctx, item, itemIndex, items);
|
|
1974
|
+
const resolvedConfig = await require_di.resolveItemExprsForExecution(ctx.config, ctx, item, itemIndex, items);
|
|
2020
1975
|
const merged = resolvedConfig !== void 0 && resolvedConfig !== null ? resolvedConfig : ctx.config;
|
|
2021
1976
|
if (merged === void 0 || merged === null) return ctx;
|
|
2022
1977
|
return {
|
|
@@ -2182,6 +2137,27 @@ var NodeActivationRequestComposer = class {
|
|
|
2182
2137
|
}
|
|
2183
2138
|
};
|
|
2184
2139
|
|
|
2140
|
+
//#endregion
|
|
2141
|
+
//#region src/execution/RunSuspendedError.ts
|
|
2142
|
+
/**
|
|
2143
|
+
* Internal sentinel thrown by {@link NodeSuspensionHandler} after persisting a suspension
|
|
2144
|
+
* entry. `NodeExecutionRequestHandlerService` catches this specifically and returns cleanly —
|
|
2145
|
+
* no continuation call, preventing `resumeFromNodeResult` / `resumeFromNodeError` from
|
|
2146
|
+
* overwriting the `"suspended"` run status.
|
|
2147
|
+
*
|
|
2148
|
+
* The `Error` suffix satisfies the ESLint `no-manual-di-new` allowlist. This is NOT a
|
|
2149
|
+
* user-facing error — it is an engine-internal control-flow primitive and should NOT be
|
|
2150
|
+
* exported from the public barrel.
|
|
2151
|
+
*/
|
|
2152
|
+
var RunSuspendedError = class extends Error {
|
|
2153
|
+
constructor(runId, taskId) {
|
|
2154
|
+
super(`RunSuspendedError: run ${runId} suspended on task ${taskId}`);
|
|
2155
|
+
this.runId = runId;
|
|
2156
|
+
this.taskId = taskId;
|
|
2157
|
+
this.name = "RunSuspendedError";
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
|
|
2185
2161
|
//#endregion
|
|
2186
2162
|
//#region src/execution/NodeExecutor.ts
|
|
2187
2163
|
var NodeExecutor = class {
|
|
@@ -2189,9 +2165,11 @@ var NodeExecutor = class {
|
|
|
2189
2165
|
outputNormalizer = new NodeOutputNormalizer();
|
|
2190
2166
|
itemExprResolver;
|
|
2191
2167
|
outputBehaviorResolver;
|
|
2192
|
-
constructor(nodeInstanceFactory, retryRunner, itemExprResolver, outputBehaviorResolver) {
|
|
2168
|
+
constructor(nodeInstanceFactory, retryRunner, itemExprResolver, outputBehaviorResolver, suspensionHandler, loadRunState) {
|
|
2193
2169
|
this.nodeInstanceFactory = nodeInstanceFactory;
|
|
2194
2170
|
this.retryRunner = retryRunner;
|
|
2171
|
+
this.suspensionHandler = suspensionHandler;
|
|
2172
|
+
this.loadRunState = loadRunState;
|
|
2195
2173
|
this.itemExprResolver = itemExprResolver ?? new ItemExprResolver();
|
|
2196
2174
|
this.outputBehaviorResolver = outputBehaviorResolver ?? new RunnableOutputBehaviorResolver();
|
|
2197
2175
|
}
|
|
@@ -2212,8 +2190,8 @@ var NodeExecutor = class {
|
|
|
2212
2190
|
}
|
|
2213
2191
|
}
|
|
2214
2192
|
isCredentialError(e) {
|
|
2215
|
-
if (e instanceof CredentialUnboundError) return true;
|
|
2216
|
-
return (e instanceof Error ? e.cause : void 0) instanceof CredentialUnboundError;
|
|
2193
|
+
if (e instanceof require_di.CredentialUnboundError) return true;
|
|
2194
|
+
return (e instanceof Error ? e.cause : void 0) instanceof require_di.CredentialUnboundError;
|
|
2217
2195
|
}
|
|
2218
2196
|
async executeMultiInputActivation(request, node$1) {
|
|
2219
2197
|
const multiInputNode = node$1;
|
|
@@ -2291,6 +2269,7 @@ var NodeExecutor = class {
|
|
|
2291
2269
|
});
|
|
2292
2270
|
}
|
|
2293
2271
|
const byPort = {};
|
|
2272
|
+
let hasSuspension = false;
|
|
2294
2273
|
for (let i = 0; i < inputBatch.length; i++) {
|
|
2295
2274
|
const item = inputBatch[i];
|
|
2296
2275
|
this.assertItemJsonNotTopLevelArray(request.nodeId, item);
|
|
@@ -2299,7 +2278,7 @@ var NodeExecutor = class {
|
|
|
2299
2278
|
const resolvedCtx = await this.itemExprResolver.resolveConfigForItem(runnableCtx, item, i, inputBatch);
|
|
2300
2279
|
const iterationCtx = {
|
|
2301
2280
|
...this.pickExecutionContext(runnableCtx, resolvedCtx),
|
|
2302
|
-
iterationId: NodeIterationIdFactory.create(),
|
|
2281
|
+
iterationId: require_di.NodeIterationIdFactory.create(),
|
|
2303
2282
|
itemIndex: i
|
|
2304
2283
|
};
|
|
2305
2284
|
const args = {
|
|
@@ -2309,7 +2288,28 @@ var NodeExecutor = class {
|
|
|
2309
2288
|
items: inputBatch,
|
|
2310
2289
|
ctx: iterationCtx
|
|
2311
2290
|
};
|
|
2312
|
-
|
|
2291
|
+
let raw;
|
|
2292
|
+
try {
|
|
2293
|
+
raw = await Promise.resolve(node$1.execute(args));
|
|
2294
|
+
} catch (e) {
|
|
2295
|
+
if (e instanceof require_di.SuspensionRequest || e instanceof Error && e.name === "SuspensionRequest" && typeof e.request === "object") {
|
|
2296
|
+
if (!this.suspensionHandler || !this.loadRunState) throw new Error(`Node ${request.nodeId} threw SuspensionRequest but this NodeExecutor has no suspensionHandler configured.`, { cause: e });
|
|
2297
|
+
const state = await this.loadRunState(request.runId);
|
|
2298
|
+
if (!state) throw new Error(`NodeExecutor: run state not found for runId ${request.runId} during suspension`, { cause: e });
|
|
2299
|
+
await this.suspensionHandler.handle({
|
|
2300
|
+
runId: request.runId,
|
|
2301
|
+
nodeId: request.nodeId,
|
|
2302
|
+
activationId: request.activationId,
|
|
2303
|
+
itemIndex: i,
|
|
2304
|
+
suspensionRequest: e,
|
|
2305
|
+
state,
|
|
2306
|
+
telemetry: iterationCtx.telemetry
|
|
2307
|
+
});
|
|
2308
|
+
hasSuspension = true;
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
throw e;
|
|
2312
|
+
}
|
|
2313
2313
|
const normalized = this.outputNormalizer.normalizeExecuteResult({
|
|
2314
2314
|
baseItem: item,
|
|
2315
2315
|
raw,
|
|
@@ -2322,6 +2322,7 @@ var NodeExecutor = class {
|
|
|
2322
2322
|
byPort[port] = list;
|
|
2323
2323
|
}
|
|
2324
2324
|
}
|
|
2325
|
+
if (hasSuspension) throw new RunSuspendedError(request.runId, "unknown");
|
|
2325
2326
|
return byPort;
|
|
2326
2327
|
}
|
|
2327
2328
|
/** Use resolver ctx only when {@link NodeExecutionContext.config} is non-nullish. */
|
|
@@ -2348,8 +2349,8 @@ var NodeExecutor = class {
|
|
|
2348
2349
|
//#endregion
|
|
2349
2350
|
//#region src/execution/NodeExecutorFactory.ts
|
|
2350
2351
|
var NodeExecutorFactory = class {
|
|
2351
|
-
create(workflowNodeInstanceFactory, retryRunner, outputBehaviorResolver) {
|
|
2352
|
-
return new NodeExecutor(workflowNodeInstanceFactory, retryRunner, void 0, outputBehaviorResolver);
|
|
2352
|
+
create(workflowNodeInstanceFactory, retryRunner, outputBehaviorResolver, suspensionHandler, loadRunState) {
|
|
2353
|
+
return new NodeExecutor(workflowNodeInstanceFactory, retryRunner, void 0, outputBehaviorResolver, suspensionHandler, loadRunState);
|
|
2353
2354
|
}
|
|
2354
2355
|
};
|
|
2355
2356
|
|
|
@@ -2942,6 +2943,7 @@ var PersistedRunStateTerminalBuilder = class {
|
|
|
2942
2943
|
...args.state,
|
|
2943
2944
|
engineCounters: args.engineCounters,
|
|
2944
2945
|
status: args.status,
|
|
2946
|
+
reason: args.reason,
|
|
2945
2947
|
pending: void 0,
|
|
2946
2948
|
queue: args.queue,
|
|
2947
2949
|
outputsByNode: args.outputsByNode,
|
|
@@ -3224,6 +3226,7 @@ var RunContinuationService = class {
|
|
|
3224
3226
|
});
|
|
3225
3227
|
data.setOutputs(args.nodeId, args.outputs);
|
|
3226
3228
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3229
|
+
const hitlResolution = this.resolveHitlStatus(args.outputs);
|
|
3227
3230
|
const completedSnapshot = this.semantics.createFinishedSnapshot({
|
|
3228
3231
|
workflow: wf,
|
|
3229
3232
|
previous: state.nodeSnapshotsByNodeId?.[args.nodeId],
|
|
@@ -3234,8 +3237,41 @@ var RunContinuationService = class {
|
|
|
3234
3237
|
parent: state.parent,
|
|
3235
3238
|
finishedAt: completedAt,
|
|
3236
3239
|
inputsByPort: pendingExecution.inputsByPort,
|
|
3237
|
-
outputs: args.outputs
|
|
3240
|
+
outputs: args.outputs,
|
|
3241
|
+
hitlStatus: hitlResolution?.nodeStatus
|
|
3238
3242
|
});
|
|
3243
|
+
if (hitlResolution?.halt) {
|
|
3244
|
+
const haltedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
3245
|
+
state,
|
|
3246
|
+
engineCounters: state.engineCounters ?? { completedNodeActivations: 0 },
|
|
3247
|
+
status: "halted",
|
|
3248
|
+
reason: hitlResolution.reason,
|
|
3249
|
+
queue: [],
|
|
3250
|
+
outputsByNode: data.dump(),
|
|
3251
|
+
nodeSnapshotsByNodeId: {
|
|
3252
|
+
...state.nodeSnapshotsByNodeId ?? {},
|
|
3253
|
+
[args.nodeId]: completedSnapshot
|
|
3254
|
+
},
|
|
3255
|
+
finishedAtIso: completedAt
|
|
3256
|
+
});
|
|
3257
|
+
await this.workflowExecutionRepository.save(haltedState);
|
|
3258
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
3259
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
3260
|
+
workflow: wf,
|
|
3261
|
+
state: haltedState,
|
|
3262
|
+
finalStatus: "failed",
|
|
3263
|
+
finishedAt: completedAt
|
|
3264
|
+
});
|
|
3265
|
+
const result = {
|
|
3266
|
+
runId: state.runId,
|
|
3267
|
+
workflowId: state.workflowId,
|
|
3268
|
+
startedAt: state.startedAt,
|
|
3269
|
+
status: "halted",
|
|
3270
|
+
reason: hitlResolution.reason
|
|
3271
|
+
};
|
|
3272
|
+
this.waiters.resolveRunCompletion(result);
|
|
3273
|
+
return result;
|
|
3274
|
+
}
|
|
3239
3275
|
const completedActivations = (state.engineCounters?.completedNodeActivations ?? 0) + 1;
|
|
3240
3276
|
const engineCounters = { completedNodeActivations: completedActivations };
|
|
3241
3277
|
const maxNodeActivations = state.executionOptions?.maxNodeActivations ?? this.executionLimitsPolicy.createRootExecutionOptions().maxNodeActivations;
|
|
@@ -3536,13 +3572,114 @@ var RunContinuationService = class {
|
|
|
3536
3572
|
status: "failed",
|
|
3537
3573
|
error: { message: "Run failed" }
|
|
3538
3574
|
};
|
|
3575
|
+
if (existing?.status === "halted") return {
|
|
3576
|
+
runId: existing.runId,
|
|
3577
|
+
workflowId: existing.workflowId,
|
|
3578
|
+
startedAt: existing.startedAt,
|
|
3579
|
+
status: "halted",
|
|
3580
|
+
reason: existing.reason ?? "hitl-rejected"
|
|
3581
|
+
};
|
|
3539
3582
|
const result = await this.waiters.waitForCompletion(runId);
|
|
3540
|
-
if (result.status !== "completed" && result.status !== "failed") throw new Error(`Unexpected run completion status: ${result.status}`);
|
|
3583
|
+
if (result.status !== "completed" && result.status !== "failed" && result.status !== "halted") throw new Error(`Unexpected run completion status: ${result.status}`);
|
|
3541
3584
|
return result;
|
|
3542
3585
|
}
|
|
3543
3586
|
async waitForWebhookResponse(runId) {
|
|
3544
3587
|
return await this.waiters.waitForWebhookResponse(runId);
|
|
3545
3588
|
}
|
|
3589
|
+
/**
|
|
3590
|
+
* Re-activate a previously suspended run item with a human decision.
|
|
3591
|
+
*
|
|
3592
|
+
* Called by the HITL resume endpoint. This method:
|
|
3593
|
+
* 1. Loads `PersistedRunState` and locates the suspension entry by `taskId`.
|
|
3594
|
+
* 2. Removes the entry from the `suspension` array; if empty, run stays `"suspended"` until
|
|
3595
|
+
* enqueue flips it to `"pending"`.
|
|
3596
|
+
* 3. Writes `pendingResume` onto the state so `NodeExecutionRequestHandlerService` can
|
|
3597
|
+
* splice `resumeContext` into the node's execution context.
|
|
3598
|
+
* 4. Reconstructs the original input from `outputsByNode` of the upstream node and
|
|
3599
|
+
* enqueues a new activation via `activationEnqueueService`.
|
|
3600
|
+
*
|
|
3601
|
+
* @throws if the run is not found, not suspended, or the `taskId` is unknown.
|
|
3602
|
+
*/
|
|
3603
|
+
async resumeRun(args) {
|
|
3604
|
+
const state = await this.workflowExecutionRepository.load(args.runId);
|
|
3605
|
+
if (!state) throw new Error(`Unknown runId: ${args.runId}`);
|
|
3606
|
+
if (state.status !== "suspended") throw new Error(`Run ${args.runId} is not suspended (status: ${state.status})`);
|
|
3607
|
+
const suspensionEntry = (state.suspension ?? []).find((s) => s.taskId === args.taskId);
|
|
3608
|
+
if (!suspensionEntry) throw new Error(`No suspension entry with taskId "${args.taskId}" found on run ${args.runId}`);
|
|
3609
|
+
const wf = this.resolvePersistedWorkflow(state);
|
|
3610
|
+
if (!wf) throw new Error(`Unknown workflowId: ${state.workflowId}`);
|
|
3611
|
+
const { topology, planner } = this.planningFactory.create(wf);
|
|
3612
|
+
const def = topology.defsById.get(suspensionEntry.nodeId);
|
|
3613
|
+
if (!def || def.kind !== "node") throw new Error(`Node ${suspensionEntry.nodeId} is not a runnable node`);
|
|
3614
|
+
const data = this.runDataFactory.create(state.outputsByNode);
|
|
3615
|
+
const limits = this.resolveEngineLimitsFromState(state);
|
|
3616
|
+
const base = this.runExecutionContextFactory.create({
|
|
3617
|
+
runId: state.runId,
|
|
3618
|
+
workflowId: state.workflowId,
|
|
3619
|
+
nodeId: suspensionEntry.nodeId,
|
|
3620
|
+
parent: state.parent,
|
|
3621
|
+
policySnapshot: state.policySnapshot,
|
|
3622
|
+
subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
|
|
3623
|
+
engineMaxNodeActivations: limits.engineMaxNodeActivations,
|
|
3624
|
+
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
3625
|
+
data,
|
|
3626
|
+
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, state.parent),
|
|
3627
|
+
testContext: state.executionOptions?.testContext
|
|
3628
|
+
});
|
|
3629
|
+
const parentEdges = wf.edges.filter((e) => e.to.nodeId === suspensionEntry.nodeId);
|
|
3630
|
+
const parentNodeId = parentEdges[0]?.from.nodeId;
|
|
3631
|
+
const parentOutputPort = parentEdges[0]?.from.output ?? "main";
|
|
3632
|
+
const allParentItems = parentNodeId ? data.getOutputItems(parentNodeId, parentOutputPort) ?? [] : [];
|
|
3633
|
+
const resumeInput = allParentItems.length > suspensionEntry.itemIndex ? [allParentItems[suspensionEntry.itemIndex]] : allParentItems;
|
|
3634
|
+
const newActivationId = this.activationIdFactory.makeActivationId();
|
|
3635
|
+
const pendingResume = {
|
|
3636
|
+
activationId: newActivationId,
|
|
3637
|
+
nodeId: suspensionEntry.nodeId,
|
|
3638
|
+
resumeContext: args.resumeContext
|
|
3639
|
+
};
|
|
3640
|
+
const remainingSuspensions = (state.suspension ?? []).filter((s) => s.taskId !== args.taskId);
|
|
3641
|
+
const baseWithResume = {
|
|
3642
|
+
...base,
|
|
3643
|
+
resumeContext: args.resumeContext
|
|
3644
|
+
};
|
|
3645
|
+
const batchId = `resume_${newActivationId}`;
|
|
3646
|
+
const request = this.nodeActivationRequestComposer.createSingleFromDefinitionWithActivation({
|
|
3647
|
+
activationId: newActivationId,
|
|
3648
|
+
runId: state.runId,
|
|
3649
|
+
workflowId: state.workflowId,
|
|
3650
|
+
parent: state.parent,
|
|
3651
|
+
executionOptions: state.executionOptions,
|
|
3652
|
+
base: baseWithResume,
|
|
3653
|
+
data,
|
|
3654
|
+
definition: {
|
|
3655
|
+
id: suspensionEntry.nodeId,
|
|
3656
|
+
config: def.config
|
|
3657
|
+
},
|
|
3658
|
+
batchId,
|
|
3659
|
+
input: resumeInput
|
|
3660
|
+
});
|
|
3661
|
+
const { result, queuedSnapshot } = await this.activationEnqueueService.enqueueActivationWithSnapshot({
|
|
3662
|
+
runId: state.runId,
|
|
3663
|
+
workflowId: state.workflowId,
|
|
3664
|
+
startedAt: state.startedAt,
|
|
3665
|
+
parent: state.parent,
|
|
3666
|
+
executionOptions: state.executionOptions,
|
|
3667
|
+
control: state.control,
|
|
3668
|
+
workflowSnapshot: state.workflowSnapshot,
|
|
3669
|
+
mutableState: state.mutableState,
|
|
3670
|
+
policySnapshot: state.policySnapshot,
|
|
3671
|
+
pendingQueue: [],
|
|
3672
|
+
request,
|
|
3673
|
+
previousNodeSnapshotsByNodeId: state.nodeSnapshotsByNodeId ?? {},
|
|
3674
|
+
planner,
|
|
3675
|
+
engineCounters: state.engineCounters,
|
|
3676
|
+
connectionInvocations: state.connectionInvocations ?? [],
|
|
3677
|
+
suspension: remainingSuspensions.length > 0 ? remainingSuspensions : void 0,
|
|
3678
|
+
pendingResume
|
|
3679
|
+
});
|
|
3680
|
+
await this.nodeEventPublisher.publish("nodeQueued", queuedSnapshot);
|
|
3681
|
+
return result;
|
|
3682
|
+
}
|
|
3546
3683
|
async resumeFromWebhookControl(args) {
|
|
3547
3684
|
const data = this.runDataFactory.create(args.state.outputsByNode);
|
|
3548
3685
|
const { topology, planner } = this.planningFactory.create(args.workflow);
|
|
@@ -3929,6 +4066,34 @@ var RunContinuationService = class {
|
|
|
3929
4066
|
this.waiters.resolveRunCompletion(result);
|
|
3930
4067
|
return result;
|
|
3931
4068
|
}
|
|
4069
|
+
/**
|
|
4070
|
+
* Inspects node outputs for a `decision.status` written by `defineHumanApprovalNode`.
|
|
4071
|
+
* Returns the first-class HITL node status and halt classification, or `undefined`
|
|
4072
|
+
* when the node is not a HITL approval node.
|
|
4073
|
+
*/
|
|
4074
|
+
resolveHitlStatus(outputs) {
|
|
4075
|
+
const firstItem = outputs?.main?.[0];
|
|
4076
|
+
const decisionStatus = firstItem && typeof firstItem === "object" && "json" in firstItem && firstItem.json && typeof firstItem.json === "object" && "decision" in firstItem.json && firstItem.json.decision && typeof firstItem.json.decision === "object" && "status" in firstItem.json.decision ? firstItem.json.decision.status : void 0;
|
|
4077
|
+
if (!decisionStatus) return void 0;
|
|
4078
|
+
if (decisionStatus === "approved") return {
|
|
4079
|
+
nodeStatus: "hitl-approved",
|
|
4080
|
+
halt: false
|
|
4081
|
+
};
|
|
4082
|
+
if (decisionStatus === "auto-accepted") return {
|
|
4083
|
+
nodeStatus: "hitl-auto-accepted",
|
|
4084
|
+
halt: false
|
|
4085
|
+
};
|
|
4086
|
+
if (decisionStatus === "rejected") return {
|
|
4087
|
+
nodeStatus: "hitl-rejected",
|
|
4088
|
+
halt: true,
|
|
4089
|
+
reason: "hitl-rejected"
|
|
4090
|
+
};
|
|
4091
|
+
if (decisionStatus === "timed-out") return {
|
|
4092
|
+
nodeStatus: "hitl-timeout",
|
|
4093
|
+
halt: true,
|
|
4094
|
+
reason: "hitl-timeout"
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
3932
4097
|
formatNodeLabel(args) {
|
|
3933
4098
|
const tokenName = typeof args.definition?.type === "function" ? args.definition.type.name : "Node";
|
|
3934
4099
|
return args.definition?.name ? `"${args.definition.name}" (${tokenName}:${args.nodeId})` : `${tokenName}:${args.nodeId}`;
|
|
@@ -4201,7 +4366,7 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
4201
4366
|
isRuntimeDescendant(nodeId, descendantNodeIds) {
|
|
4202
4367
|
for (const descendantNodeId of descendantNodeIds) {
|
|
4203
4368
|
if (nodeId === descendantNodeId) return false;
|
|
4204
|
-
if (ConnectionNodeIdFactory.isConnectionOwnedDescendantOf(descendantNodeId, nodeId)) return true;
|
|
4369
|
+
if (require_di.ConnectionNodeIdFactory.isConnectionOwnedDescendantOf(descendantNodeId, nodeId)) return true;
|
|
4205
4370
|
}
|
|
4206
4371
|
return false;
|
|
4207
4372
|
}
|
|
@@ -5281,13 +5446,19 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5281
5446
|
const portKeys = Object.keys(inputsByPort);
|
|
5282
5447
|
const kind = portKeys.length === 1 && portKeys[0] === "in" ? "single" : "multi";
|
|
5283
5448
|
const batchId = pendingExecution.batchId ?? "batch_1";
|
|
5449
|
+
const pendingResume = state.pendingResume;
|
|
5450
|
+
const resumeContext = pendingResume?.activationId === request.activationId && pendingResume?.nodeId === request.nodeId ? pendingResume.resumeContext : void 0;
|
|
5451
|
+
const baseWithResume = resumeContext != null ? {
|
|
5452
|
+
...base,
|
|
5453
|
+
resumeContext
|
|
5454
|
+
} : base;
|
|
5284
5455
|
const activationRequest = kind === "multi" ? this.nodeActivationRequestComposer.createMultiFromDefinitionWithActivation({
|
|
5285
5456
|
activationId: request.activationId,
|
|
5286
5457
|
runId: request.runId,
|
|
5287
5458
|
workflowId: request.workflowId,
|
|
5288
5459
|
parent: resolvedParent,
|
|
5289
5460
|
executionOptions: request.executionOptions ?? state.executionOptions,
|
|
5290
|
-
base,
|
|
5461
|
+
base: baseWithResume,
|
|
5291
5462
|
data,
|
|
5292
5463
|
definition: {
|
|
5293
5464
|
id: definition.id,
|
|
@@ -5301,7 +5472,7 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5301
5472
|
workflowId: request.workflowId,
|
|
5302
5473
|
parent: resolvedParent,
|
|
5303
5474
|
executionOptions: request.executionOptions ?? state.executionOptions,
|
|
5304
|
-
base,
|
|
5475
|
+
base: baseWithResume,
|
|
5305
5476
|
data,
|
|
5306
5477
|
definition: {
|
|
5307
5478
|
id: definition.id,
|
|
@@ -5310,6 +5481,13 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5310
5481
|
batchId,
|
|
5311
5482
|
input: inputsByPort.in ?? request.input ?? []
|
|
5312
5483
|
});
|
|
5484
|
+
if (resumeContext != null) {
|
|
5485
|
+
const clearedState = await this.workflowExecutionRepository.load(request.runId);
|
|
5486
|
+
if (clearedState?.pendingResume?.activationId === request.activationId) await this.workflowExecutionRepository.save({
|
|
5487
|
+
...clearedState,
|
|
5488
|
+
pendingResume: void 0
|
|
5489
|
+
});
|
|
5490
|
+
}
|
|
5313
5491
|
await this.continuation.markNodeRunning({
|
|
5314
5492
|
runId: activationRequest.runId,
|
|
5315
5493
|
activationId: activationRequest.activationId,
|
|
@@ -5320,6 +5498,7 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5320
5498
|
try {
|
|
5321
5499
|
outputs = await this.nodeExecutor.execute(activationRequest);
|
|
5322
5500
|
} catch (error) {
|
|
5501
|
+
if (error instanceof RunSuspendedError) return;
|
|
5323
5502
|
await this.resumeAfterExecutionError(activationRequest, this.asError(error));
|
|
5324
5503
|
return;
|
|
5325
5504
|
}
|
|
@@ -5880,7 +6059,7 @@ var EngineWaiters = class {
|
|
|
5880
6059
|
});
|
|
5881
6060
|
}
|
|
5882
6061
|
resolveRunCompletion(result) {
|
|
5883
|
-
if (result.status !== "completed" && result.status !== "failed") return;
|
|
6062
|
+
if (result.status !== "completed" && result.status !== "failed" && result.status !== "halted") return;
|
|
5884
6063
|
const list = this.completionWaiters.get(result.runId);
|
|
5885
6064
|
if (!list || list.length === 0) return;
|
|
5886
6065
|
this.completionWaiters.delete(result.runId);
|
|
@@ -5974,6 +6153,13 @@ var Engine = class {
|
|
|
5974
6153
|
async waitForWebhookResponse(runId) {
|
|
5975
6154
|
return await this.deps.runContinuationService.waitForWebhookResponse(runId);
|
|
5976
6155
|
}
|
|
6156
|
+
/**
|
|
6157
|
+
* Re-activate a suspended run item with a human decision (HITL).
|
|
6158
|
+
* The HTTP resume endpoint calls this; this method exposes the engine primitive.
|
|
6159
|
+
*/
|
|
6160
|
+
async resumeRun(args) {
|
|
6161
|
+
return await this.deps.runContinuationService.resumeRun(args);
|
|
6162
|
+
}
|
|
5977
6163
|
async handleNodeExecutionRequest(request) {
|
|
5978
6164
|
await this.deps.nodeExecutionRequestHandler.handleNodeExecutionRequest(request);
|
|
5979
6165
|
}
|
|
@@ -6181,6 +6367,7 @@ var RunIntentService = class {
|
|
|
6181
6367
|
};
|
|
6182
6368
|
return await Promise.race([this.engine.waitForWebhookResponse(scheduled.runId), this.engine.waitForCompletion(scheduled.runId).then((completed) => {
|
|
6183
6369
|
if (completed.status === "failed") throw new Error(completed.error.message);
|
|
6370
|
+
if (completed.status === "halted") throw new Error(`Run halted: ${completed.reason}`);
|
|
6184
6371
|
return {
|
|
6185
6372
|
runId: completed.runId,
|
|
6186
6373
|
workflowId: completed.workflowId,
|
|
@@ -6305,18 +6492,6 @@ var WorkflowRepositoryWebhookTriggerMatcherFactory = class {
|
|
|
6305
6492
|
};
|
|
6306
6493
|
|
|
6307
6494
|
//#endregion
|
|
6308
|
-
Object.defineProperty(exports, 'AgentConfigInspector', {
|
|
6309
|
-
enumerable: true,
|
|
6310
|
-
get: function () {
|
|
6311
|
-
return AgentConfigInspector;
|
|
6312
|
-
}
|
|
6313
|
-
});
|
|
6314
|
-
Object.defineProperty(exports, 'AgentConnectionNodeCollector', {
|
|
6315
|
-
enumerable: true,
|
|
6316
|
-
get: function () {
|
|
6317
|
-
return AgentConnectionNodeCollector;
|
|
6318
|
-
}
|
|
6319
|
-
});
|
|
6320
6495
|
Object.defineProperty(exports, 'AllWorkflowsActiveWorkflowActivationPolicy', {
|
|
6321
6496
|
enumerable: true,
|
|
6322
6497
|
get: function () {
|
|
@@ -6371,18 +6546,6 @@ Object.defineProperty(exports, 'ConnectionInvocationIdFactory', {
|
|
|
6371
6546
|
return ConnectionInvocationIdFactory;
|
|
6372
6547
|
}
|
|
6373
6548
|
});
|
|
6374
|
-
Object.defineProperty(exports, 'ConnectionNodeIdFactory', {
|
|
6375
|
-
enumerable: true,
|
|
6376
|
-
get: function () {
|
|
6377
|
-
return ConnectionNodeIdFactory;
|
|
6378
|
-
}
|
|
6379
|
-
});
|
|
6380
|
-
Object.defineProperty(exports, 'CoreTokens', {
|
|
6381
|
-
enumerable: true,
|
|
6382
|
-
get: function () {
|
|
6383
|
-
return CoreTokens;
|
|
6384
|
-
}
|
|
6385
|
-
});
|
|
6386
6549
|
Object.defineProperty(exports, 'CostTrackingTelemetryAttributeNames', {
|
|
6387
6550
|
enumerable: true,
|
|
6388
6551
|
get: function () {
|
|
@@ -6401,12 +6564,6 @@ Object.defineProperty(exports, 'CredentialResolverFactory', {
|
|
|
6401
6564
|
return CredentialResolverFactory;
|
|
6402
6565
|
}
|
|
6403
6566
|
});
|
|
6404
|
-
Object.defineProperty(exports, 'CredentialUnboundError', {
|
|
6405
|
-
enumerable: true,
|
|
6406
|
-
get: function () {
|
|
6407
|
-
return CredentialUnboundError;
|
|
6408
|
-
}
|
|
6409
|
-
});
|
|
6410
6567
|
Object.defineProperty(exports, 'DefaultAsyncSleeper', {
|
|
6411
6568
|
enumerable: true,
|
|
6412
6569
|
get: function () {
|
|
@@ -6437,6 +6594,12 @@ Object.defineProperty(exports, 'DefaultWorkflowGraphFactory', {
|
|
|
6437
6594
|
return DefaultWorkflowGraphFactory;
|
|
6438
6595
|
}
|
|
6439
6596
|
});
|
|
6597
|
+
Object.defineProperty(exports, 'DefinedNodeRegistry', {
|
|
6598
|
+
enumerable: true,
|
|
6599
|
+
get: function () {
|
|
6600
|
+
return DefinedNodeRegistry;
|
|
6601
|
+
}
|
|
6602
|
+
});
|
|
6440
6603
|
Object.defineProperty(exports, 'ENGINE_EXECUTION_LIMITS_DEFAULTS', {
|
|
6441
6604
|
enumerable: true,
|
|
6442
6605
|
get: function () {
|
|
@@ -6491,6 +6654,30 @@ Object.defineProperty(exports, 'HintOnlyOffloadPolicy', {
|
|
|
6491
6654
|
return HintOnlyOffloadPolicy;
|
|
6492
6655
|
}
|
|
6493
6656
|
});
|
|
6657
|
+
Object.defineProperty(exports, 'HitlResumeTokenSignerToken', {
|
|
6658
|
+
enumerable: true,
|
|
6659
|
+
get: function () {
|
|
6660
|
+
return HitlResumeTokenSignerToken;
|
|
6661
|
+
}
|
|
6662
|
+
});
|
|
6663
|
+
Object.defineProperty(exports, 'HitlTimeoutJobSchedulerToken', {
|
|
6664
|
+
enumerable: true,
|
|
6665
|
+
get: function () {
|
|
6666
|
+
return HitlTimeoutJobSchedulerToken;
|
|
6667
|
+
}
|
|
6668
|
+
});
|
|
6669
|
+
Object.defineProperty(exports, 'HitlWorkspaceIdToken', {
|
|
6670
|
+
enumerable: true,
|
|
6671
|
+
get: function () {
|
|
6672
|
+
return HitlWorkspaceIdToken;
|
|
6673
|
+
}
|
|
6674
|
+
});
|
|
6675
|
+
Object.defineProperty(exports, 'HumanTaskStoreToken', {
|
|
6676
|
+
enumerable: true,
|
|
6677
|
+
get: function () {
|
|
6678
|
+
return HumanTaskStoreToken;
|
|
6679
|
+
}
|
|
6680
|
+
});
|
|
6494
6681
|
Object.defineProperty(exports, 'InMemoryBinaryStorage', {
|
|
6495
6682
|
enumerable: true,
|
|
6496
6683
|
get: function () {
|
|
@@ -6551,12 +6738,6 @@ Object.defineProperty(exports, 'MissingRuntimeTriggerToken', {
|
|
|
6551
6738
|
return MissingRuntimeTriggerToken;
|
|
6552
6739
|
}
|
|
6553
6740
|
});
|
|
6554
|
-
Object.defineProperty(exports, 'NoOpAgentMcpIntegration', {
|
|
6555
|
-
enumerable: true,
|
|
6556
|
-
get: function () {
|
|
6557
|
-
return NoOpAgentMcpIntegration;
|
|
6558
|
-
}
|
|
6559
|
-
});
|
|
6560
6741
|
Object.defineProperty(exports, 'NoOpCostTrackingTelemetry', {
|
|
6561
6742
|
enumerable: true,
|
|
6562
6743
|
get: function () {
|
|
@@ -6611,12 +6792,6 @@ Object.defineProperty(exports, 'NoRetryPolicy', {
|
|
|
6611
6792
|
return NoRetryPolicy;
|
|
6612
6793
|
}
|
|
6613
6794
|
});
|
|
6614
|
-
Object.defineProperty(exports, 'NodeBackedToolConfig', {
|
|
6615
|
-
enumerable: true,
|
|
6616
|
-
get: function () {
|
|
6617
|
-
return NodeBackedToolConfig;
|
|
6618
|
-
}
|
|
6619
|
-
});
|
|
6620
6795
|
Object.defineProperty(exports, 'NodeEventPublisher', {
|
|
6621
6796
|
enumerable: true,
|
|
6622
6797
|
get: function () {
|
|
@@ -6653,12 +6828,6 @@ Object.defineProperty(exports, 'NodeInstanceFactoryFactory', {
|
|
|
6653
6828
|
return NodeInstanceFactoryFactory;
|
|
6654
6829
|
}
|
|
6655
6830
|
});
|
|
6656
|
-
Object.defineProperty(exports, 'NodeIterationIdFactory', {
|
|
6657
|
-
enumerable: true,
|
|
6658
|
-
get: function () {
|
|
6659
|
-
return NodeIterationIdFactory;
|
|
6660
|
-
}
|
|
6661
|
-
});
|
|
6662
6831
|
Object.defineProperty(exports, 'NodeOutputNormalizer', {
|
|
6663
6832
|
enumerable: true,
|
|
6664
6833
|
get: function () {
|
|
@@ -6701,12 +6870,6 @@ Object.defineProperty(exports, 'RetryPolicy', {
|
|
|
6701
6870
|
return RetryPolicy;
|
|
6702
6871
|
}
|
|
6703
6872
|
});
|
|
6704
|
-
Object.defineProperty(exports, 'RunFinishedAtFactory', {
|
|
6705
|
-
enumerable: true,
|
|
6706
|
-
get: function () {
|
|
6707
|
-
return RunFinishedAtFactory;
|
|
6708
|
-
}
|
|
6709
|
-
});
|
|
6710
6873
|
Object.defineProperty(exports, 'RunIntentService', {
|
|
6711
6874
|
enumerable: true,
|
|
6712
6875
|
get: function () {
|
|
@@ -6725,6 +6888,12 @@ Object.defineProperty(exports, 'RunPolicySnapshotFactory', {
|
|
|
6725
6888
|
return RunPolicySnapshotFactory;
|
|
6726
6889
|
}
|
|
6727
6890
|
});
|
|
6891
|
+
Object.defineProperty(exports, 'RunSuspendedError', {
|
|
6892
|
+
enumerable: true,
|
|
6893
|
+
get: function () {
|
|
6894
|
+
return RunSuspendedError;
|
|
6895
|
+
}
|
|
6896
|
+
});
|
|
6728
6897
|
Object.defineProperty(exports, 'RunTerminalPersistenceCoordinator', {
|
|
6729
6898
|
enumerable: true,
|
|
6730
6899
|
get: function () {
|
|
@@ -6815,22 +6984,28 @@ Object.defineProperty(exports, 'WorkflowStoragePolicyEvaluator', {
|
|
|
6815
6984
|
return WorkflowStoragePolicyEvaluator;
|
|
6816
6985
|
}
|
|
6817
6986
|
});
|
|
6818
|
-
Object.defineProperty(exports, '
|
|
6987
|
+
Object.defineProperty(exports, 'chatModel', {
|
|
6819
6988
|
enumerable: true,
|
|
6820
6989
|
get: function () {
|
|
6821
|
-
return
|
|
6990
|
+
return chatModel;
|
|
6822
6991
|
}
|
|
6823
6992
|
});
|
|
6824
|
-
Object.defineProperty(exports, '
|
|
6993
|
+
Object.defineProperty(exports, 'defineBatchNode', {
|
|
6825
6994
|
enumerable: true,
|
|
6826
6995
|
get: function () {
|
|
6827
|
-
return
|
|
6996
|
+
return defineBatchNode;
|
|
6828
6997
|
}
|
|
6829
6998
|
});
|
|
6830
|
-
Object.defineProperty(exports, '
|
|
6999
|
+
Object.defineProperty(exports, 'defineHumanApprovalNode', {
|
|
6831
7000
|
enumerable: true,
|
|
6832
7001
|
get: function () {
|
|
6833
|
-
return
|
|
7002
|
+
return defineHumanApprovalNode;
|
|
7003
|
+
}
|
|
7004
|
+
});
|
|
7005
|
+
Object.defineProperty(exports, 'defineNode', {
|
|
7006
|
+
enumerable: true,
|
|
7007
|
+
get: function () {
|
|
7008
|
+
return defineNode;
|
|
6834
7009
|
}
|
|
6835
7010
|
});
|
|
6836
7011
|
Object.defineProperty(exports, 'emitPorts', {
|
|
@@ -6851,10 +7026,10 @@ Object.defineProperty(exports, 'getPersistedRuntimeTypeMetadata', {
|
|
|
6851
7026
|
return getPersistedRuntimeTypeMetadata;
|
|
6852
7027
|
}
|
|
6853
7028
|
});
|
|
6854
|
-
Object.defineProperty(exports, '
|
|
7029
|
+
Object.defineProperty(exports, 'isHumanApprovalNode', {
|
|
6855
7030
|
enumerable: true,
|
|
6856
7031
|
get: function () {
|
|
6857
|
-
return
|
|
7032
|
+
return isHumanApprovalNode;
|
|
6858
7033
|
}
|
|
6859
7034
|
});
|
|
6860
7035
|
Object.defineProperty(exports, 'isPortsEmission', {
|
|
@@ -6869,40 +7044,16 @@ Object.defineProperty(exports, 'isUnbrandedPortsEmissionShape', {
|
|
|
6869
7044
|
return isUnbrandedPortsEmissionShape;
|
|
6870
7045
|
}
|
|
6871
7046
|
});
|
|
6872
|
-
Object.defineProperty(exports, 'itemExpr', {
|
|
6873
|
-
enumerable: true,
|
|
6874
|
-
get: function () {
|
|
6875
|
-
return itemExpr;
|
|
6876
|
-
}
|
|
6877
|
-
});
|
|
6878
7047
|
Object.defineProperty(exports, 'node', {
|
|
6879
7048
|
enumerable: true,
|
|
6880
7049
|
get: function () {
|
|
6881
7050
|
return node;
|
|
6882
7051
|
}
|
|
6883
7052
|
});
|
|
6884
|
-
Object.defineProperty(exports, 'nodeRef', {
|
|
6885
|
-
enumerable: true,
|
|
6886
|
-
get: function () {
|
|
6887
|
-
return nodeRef;
|
|
6888
|
-
}
|
|
6889
|
-
});
|
|
6890
|
-
Object.defineProperty(exports, 'resolveItemExprsForExecution', {
|
|
6891
|
-
enumerable: true,
|
|
6892
|
-
get: function () {
|
|
6893
|
-
return resolveItemExprsForExecution;
|
|
6894
|
-
}
|
|
6895
|
-
});
|
|
6896
|
-
Object.defineProperty(exports, 'resolveItemExprsInUnknown', {
|
|
6897
|
-
enumerable: true,
|
|
6898
|
-
get: function () {
|
|
6899
|
-
return resolveItemExprsInUnknown;
|
|
6900
|
-
}
|
|
6901
|
-
});
|
|
6902
7053
|
Object.defineProperty(exports, 'tool', {
|
|
6903
7054
|
enumerable: true,
|
|
6904
7055
|
get: function () {
|
|
6905
7056
|
return tool;
|
|
6906
7057
|
}
|
|
6907
7058
|
});
|
|
6908
|
-
//# sourceMappingURL=runtime-
|
|
7059
|
+
//# sourceMappingURL=runtime-CyW9c9XM.cjs.map
|