@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,32 +1,33 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import { container as container$1, delay, inject, injectAll, injectable, instanceCachingFactory, instancePerContainerCachingFactory, predicateAwareClassFactory, registry, singleton } from "tsyringe";
|
|
1
|
+
import { C as resolveItemExprsForExecution, T as ConnectionNodeIdFactory, _ as NodeIterationIdFactory, a as injectable, b as AgentConfigInspector, g as CredentialUnboundError, h as SuspensionRequest, v as AgentConnectionNodeCollector } from "./di-DdsgWfVy.js";
|
|
3
2
|
import { ZodError, z } from "zod";
|
|
4
3
|
import { ReadableStream } from "node:stream/web";
|
|
5
4
|
import { createHash } from "node:crypto";
|
|
6
5
|
|
|
7
|
-
//#region src/
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
6
|
+
//#region src/contracts/humanTaskStoreTypes.ts
|
|
7
|
+
const HumanTaskStoreToken = Symbol.for("codemation.core.HumanTaskStore");
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/contracts/hitlSeamTypes.ts
|
|
11
|
+
const HitlResumeTokenSignerToken = Symbol.for("codemation.core.HitlResumeTokenSigner");
|
|
12
|
+
const HitlTimeoutJobSchedulerToken = Symbol.for("codemation.core.HitlTimeoutJobScheduler");
|
|
13
|
+
/**
|
|
14
|
+
* Optional workspace ID injected into NodeSuspensionHandler in managed mode (T7 security fix).
|
|
15
|
+
* Allows the handler to stamp the workspaceId on each HumanTaskRecord so HitlCallbackHandler
|
|
16
|
+
* can assert workspace identity independently of the HMAC middleware.
|
|
17
|
+
* Not registered in non-managed mode; NodeSuspensionHandler defaults to null.
|
|
18
|
+
*/
|
|
19
|
+
const HitlWorkspaceIdToken = Symbol.for("codemation.core.HitlWorkspaceId");
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/authoring/DefinedNodeRegistry.ts
|
|
23
|
+
var DefinedNodeRegistry = class {
|
|
24
|
+
static definitions = /* @__PURE__ */ new Map();
|
|
25
|
+
static register(definition) {
|
|
26
|
+
this.definitions.set(definition.key, definition);
|
|
27
|
+
}
|
|
28
|
+
static resolve(key) {
|
|
29
|
+
return this.definitions.get(key);
|
|
30
|
+
}
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
//#endregion
|
|
@@ -139,287 +140,303 @@ function chatModel(options = {}) {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
//#endregion
|
|
142
|
-
//#region src/
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
toNodeItem(args) {
|
|
173
|
-
const mapped = this.mapInputValue?.(args) ?? args.input;
|
|
174
|
-
if (this.isItem(mapped)) return mapped;
|
|
175
|
-
return { json: mapped };
|
|
176
|
-
}
|
|
177
|
-
toToolOutput(args) {
|
|
178
|
-
const raw = this.mapOutputValue?.(args) ?? this.readDefaultToolOutput(args.outputs);
|
|
179
|
-
return this.outputSchemaValue.parse(raw);
|
|
180
|
-
}
|
|
181
|
-
readDefaultToolOutput(outputs) {
|
|
182
|
-
const firstMainItem = outputs.main?.[0];
|
|
183
|
-
if (!firstMainItem) throw new Error(`Node-backed tool "${this.name}" did not produce a main output item.`);
|
|
184
|
-
return firstMainItem.json;
|
|
185
|
-
}
|
|
186
|
-
isItem(value) {
|
|
187
|
-
return typeof value === "object" && value !== null && "json" in value;
|
|
143
|
+
//#region src/authoring/defineNode.types.ts
|
|
144
|
+
const definedNodeCredentialRequirementFactory = {
|
|
145
|
+
create(bindings) {
|
|
146
|
+
if (!bindings) return [];
|
|
147
|
+
return Object.entries(bindings).map(([slotKey, binding]) => {
|
|
148
|
+
if (typeof binding === "string" || this.isCredentialType(binding)) return {
|
|
149
|
+
slotKey,
|
|
150
|
+
label: this.humanize(slotKey),
|
|
151
|
+
acceptedTypes: [this.resolveTypeId(binding)]
|
|
152
|
+
};
|
|
153
|
+
const types = Array.isArray(binding.type) ? binding.type : [binding.type];
|
|
154
|
+
return {
|
|
155
|
+
slotKey,
|
|
156
|
+
label: binding.label ?? this.humanize(slotKey),
|
|
157
|
+
acceptedTypes: types.map((entry) => this.resolveTypeId(entry)),
|
|
158
|
+
optional: binding.optional,
|
|
159
|
+
helpText: binding.helpText,
|
|
160
|
+
helpUrl: binding.helpUrl
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
isCredentialType(value) {
|
|
165
|
+
return Boolean(value) && typeof value === "object" && "definition" in value && typeof value.definition?.typeId === "string";
|
|
166
|
+
},
|
|
167
|
+
resolveTypeId(type) {
|
|
168
|
+
return typeof type === "string" ? type : type.definition.typeId;
|
|
169
|
+
},
|
|
170
|
+
humanize(key) {
|
|
171
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_.]+/g, " ").replace(/\s+/g, " ").trim().replace(/^./, (character) => character.toUpperCase());
|
|
188
172
|
}
|
|
189
173
|
};
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
174
|
+
const definedNodeCredentialAccessorFactory = { create(bindings, ctx) {
|
|
175
|
+
if (!bindings) return {};
|
|
176
|
+
const entries = Object.keys(bindings).map((slotKey) => [slotKey, () => ctx.getCredential(slotKey)]);
|
|
177
|
+
return Object.fromEntries(entries);
|
|
178
|
+
} };
|
|
179
|
+
function defineNode(options) {
|
|
180
|
+
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
181
|
+
const DefinedNodeRuntime = class {
|
|
182
|
+
kind = "node";
|
|
183
|
+
outputPorts = ["main"];
|
|
184
|
+
inputSchema = options.inputSchema;
|
|
185
|
+
async execute(args) {
|
|
186
|
+
const ctx = args.ctx;
|
|
187
|
+
const context = {
|
|
188
|
+
config: ctx.config.config,
|
|
189
|
+
credentials: definedNodeCredentialAccessorFactory.create(options.credentials, ctx),
|
|
190
|
+
execution: ctx
|
|
191
|
+
};
|
|
192
|
+
const payload = {
|
|
193
|
+
input: args.input,
|
|
194
|
+
item: args.item,
|
|
195
|
+
itemIndex: args.itemIndex,
|
|
196
|
+
items: args.items,
|
|
197
|
+
ctx
|
|
198
|
+
};
|
|
199
|
+
return await options.execute(payload, context);
|
|
200
|
+
}
|
|
198
201
|
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Deep-resolves {@link itemExpr} leaves. Returns a new graph (does not mutate the original config object).
|
|
220
|
-
*/
|
|
221
|
-
async function resolveItemExprsInUnknown(value, args, seen = /* @__PURE__ */ new WeakSet()) {
|
|
222
|
-
if (isItemExpr(value)) return await Promise.resolve(value.fn(args));
|
|
223
|
-
if (value === null || typeof value !== "object") return value;
|
|
224
|
-
if (seen.has(value)) return value;
|
|
225
|
-
seen.add(value);
|
|
226
|
-
if (Array.isArray(value)) {
|
|
227
|
-
const out$1 = [];
|
|
228
|
-
for (let i = 0; i < value.length; i++) out$1.push(await resolveItemExprsInUnknown(value[i], args, seen));
|
|
229
|
-
return out$1;
|
|
230
|
-
}
|
|
231
|
-
const rec = value;
|
|
232
|
-
const entries = Object.entries(rec);
|
|
233
|
-
const proto = Object.getPrototypeOf(value);
|
|
234
|
-
if (proto !== Object.prototype && proto !== null && entries.length === 0) return value;
|
|
235
|
-
const out = Object.create(proto);
|
|
236
|
-
for (const [k, v] of entries) out[k] = await resolveItemExprsInUnknown(v, args, seen);
|
|
237
|
-
return out;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Clones runnable config (best-effort) so per-item {@link itemExpr} resolution never mutates shared instances.
|
|
241
|
-
*/
|
|
242
|
-
async function resolveItemExprsForExecution(config, nodeCtx, item, itemIndex, items) {
|
|
243
|
-
const exprArgs = {
|
|
244
|
-
item,
|
|
245
|
-
itemIndex,
|
|
246
|
-
items,
|
|
247
|
-
ctx: {
|
|
248
|
-
runId: nodeCtx.runId,
|
|
249
|
-
workflowId: nodeCtx.workflowId,
|
|
250
|
-
nodeId: nodeCtx.nodeId,
|
|
251
|
-
activationId: nodeCtx.activationId,
|
|
252
|
-
data: nodeCtx.data
|
|
202
|
+
node({ name: options.key })(DefinedNodeRuntime);
|
|
203
|
+
const DefinedRunnableNodeConfig = class {
|
|
204
|
+
kind = "node";
|
|
205
|
+
type = DefinedNodeRuntime;
|
|
206
|
+
icon = options.icon;
|
|
207
|
+
inputSchema = options.inputSchema;
|
|
208
|
+
keepBinaries = options.keepBinaries ?? false;
|
|
209
|
+
constructor(name, config, id) {
|
|
210
|
+
this.name = name;
|
|
211
|
+
this.id = id;
|
|
212
|
+
this.config = config;
|
|
213
|
+
}
|
|
214
|
+
config;
|
|
215
|
+
getCredentialRequirements() {
|
|
216
|
+
return credentialRequirements;
|
|
217
|
+
}
|
|
218
|
+
inspectorSummary() {
|
|
219
|
+
return options.inspectorSummary?.({ config: this.config });
|
|
253
220
|
}
|
|
254
221
|
};
|
|
255
|
-
|
|
256
|
-
|
|
222
|
+
const definition = {
|
|
223
|
+
kind: "defined-node",
|
|
224
|
+
key: options.key,
|
|
225
|
+
title: options.title,
|
|
226
|
+
description: options.description,
|
|
227
|
+
create(config, name = options.title, id) {
|
|
228
|
+
return new DefinedRunnableNodeConfig(name, config, id);
|
|
229
|
+
},
|
|
230
|
+
register(context) {
|
|
231
|
+
context.registerNode(DefinedNodeRuntime);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
DefinedNodeRegistry.register(definition);
|
|
235
|
+
return definition;
|
|
257
236
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (isItemExpr(messages)) return true;
|
|
273
|
-
const o = messages;
|
|
274
|
-
return Array.isArray(o.prompt) && o.prompt.length > 0 || typeof o.buildMessages === "function";
|
|
237
|
+
function defineBatchNode(options) {
|
|
238
|
+
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
239
|
+
const DefinedNodeRuntime = class {
|
|
240
|
+
kind = "node";
|
|
241
|
+
outputPorts = ["main"];
|
|
242
|
+
async execute(args) {
|
|
243
|
+
if (args.itemIndex !== args.items.length - 1) return [];
|
|
244
|
+
const ctx = args.ctx;
|
|
245
|
+
const context = {
|
|
246
|
+
config: ctx.config.config,
|
|
247
|
+
credentials: definedNodeCredentialAccessorFactory.create(options.credentials, ctx),
|
|
248
|
+
execution: ctx
|
|
249
|
+
};
|
|
250
|
+
return [...await options.run(args.items.map((item) => item.json), context)];
|
|
275
251
|
}
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
252
|
+
};
|
|
253
|
+
node({ name: options.key })(DefinedNodeRuntime);
|
|
254
|
+
const DefinedRunnableNodeConfig = class {
|
|
255
|
+
kind = "node";
|
|
256
|
+
type = DefinedNodeRuntime;
|
|
257
|
+
icon = options.icon;
|
|
258
|
+
constructor(name, config, id) {
|
|
259
|
+
this.name = name;
|
|
260
|
+
this.id = id;
|
|
261
|
+
this.config = config;
|
|
262
|
+
}
|
|
263
|
+
config;
|
|
264
|
+
getCredentialRequirements() {
|
|
265
|
+
return credentialRequirements;
|
|
266
|
+
}
|
|
267
|
+
inspectorSummary() {
|
|
268
|
+
return options.inspectorSummary?.({ config: this.config });
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const definition = {
|
|
272
|
+
kind: "defined-node",
|
|
273
|
+
key: options.key,
|
|
274
|
+
title: options.title,
|
|
275
|
+
description: options.description,
|
|
276
|
+
create(config, name = options.title, id) {
|
|
277
|
+
return new DefinedRunnableNodeConfig(name, config, id);
|
|
278
|
+
},
|
|
279
|
+
register(context) {
|
|
280
|
+
context.registerNode(DefinedNodeRuntime);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
DefinedNodeRegistry.register(definition);
|
|
284
|
+
return definition;
|
|
285
|
+
}
|
|
279
286
|
|
|
280
287
|
//#endregion
|
|
281
|
-
//#region src/
|
|
288
|
+
//#region src/authoring/defineHumanApprovalNode.types.ts
|
|
282
289
|
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
290
|
+
* Returns `true` when `node` was created by {@link defineHumanApprovalNode}.
|
|
291
|
+
* Uses the `humanApprovalToolBehavior` typed field as the discriminant.
|
|
285
292
|
*/
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
for (const tool$1 of agentConfig.tools ?? []) {
|
|
369
|
-
const toolNodeId = ConnectionNodeIdFactory.toolConnectionNodeId(parentNodeId, tool$1.name);
|
|
370
|
-
const isNestedAgent = this.isNodeBackedAgentTool(tool$1);
|
|
371
|
-
collected.push({
|
|
372
|
-
nodeId: toolNodeId,
|
|
373
|
-
parentNodeId,
|
|
374
|
-
connectionName: "tools",
|
|
375
|
-
role: isNestedAgent ? "nestedAgent" : "tool",
|
|
376
|
-
name: tool$1.presentation?.label ?? tool$1.name,
|
|
377
|
-
typeName: tool$1.name,
|
|
378
|
-
icon: tool$1.presentation?.icon,
|
|
379
|
-
credentialSource: tool$1
|
|
380
|
-
});
|
|
381
|
-
this.collectNestedAgentTools(toolNodeId, tool$1, collected, mcpServerResolver);
|
|
382
|
-
}
|
|
383
|
-
if (mcpServerResolver) {
|
|
384
|
-
const mcpServers = agentConfig.mcpServers;
|
|
385
|
-
for (const serverId of mcpServers ?? []) {
|
|
386
|
-
const decl = mcpServerResolver(serverId);
|
|
387
|
-
if (!decl) continue;
|
|
388
|
-
const acceptedTypes = decl.acceptedCredentialTypes ?? [];
|
|
389
|
-
collected.push({
|
|
390
|
-
nodeId: ConnectionNodeIdFactory.mcpConnectionNodeId(parentNodeId, serverId),
|
|
391
|
-
parentNodeId,
|
|
392
|
-
connectionName: "tools",
|
|
393
|
-
role: "tool",
|
|
394
|
-
name: decl.displayName,
|
|
395
|
-
typeName: serverId,
|
|
396
|
-
icon: "lucide:plug",
|
|
397
|
-
credentialSource: { getCredentialRequirements: () => [{
|
|
398
|
-
slotKey: "credential",
|
|
399
|
-
label: decl.displayName,
|
|
400
|
-
acceptedTypes
|
|
401
|
-
}] }
|
|
293
|
+
function isHumanApprovalNode(node$1) {
|
|
294
|
+
return typeof node$1 === "object" && node$1 !== null && "humanApprovalToolBehavior" in node$1 && typeof node$1.humanApprovalToolBehavior === "object";
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Authoring helper that compiles a HITL approval channel down to a regular
|
|
298
|
+
* {@link defineNode}-backed node with `SuspensionRequest` semantics.
|
|
299
|
+
*
|
|
300
|
+
* **Fast-forward decision semantics:**
|
|
301
|
+
* - On the first `execute` call (no `ctx.resumeContext`): throws a `SuspensionRequest`
|
|
302
|
+
* that calls the author's `deliver`. The engine persists the suspension and continues.
|
|
303
|
+
* - On resume (`ctx.resumeContext` set): calls `onDecision`/`onTimeout` as appropriate,
|
|
304
|
+
* merges a `decision` key into `item.json`, and returns an item with the original
|
|
305
|
+
* `binary` map passed by reference (no copy).
|
|
306
|
+
*
|
|
307
|
+
* **Output shape per item:**
|
|
308
|
+
* ```ts
|
|
309
|
+
* // Input: { json: { invoiceId: 42 }, binary?: {...} }
|
|
310
|
+
* // Output: { json: { invoiceId: 42, decision: { status: "approved", actor, decidedAt } }, binary: <unchanged> }
|
|
311
|
+
* ```
|
|
312
|
+
* If `item.json` already has a `decision` key it is **overwritten**. Namespace as
|
|
313
|
+
* needed if your schema reserves that key for another purpose.
|
|
314
|
+
*
|
|
315
|
+
* **Predicate persistence:**
|
|
316
|
+
* The `approvedPredicate` function is NOT serialized to the suspension record (except
|
|
317
|
+
* as an audit-only string via `toString()`). On resume, the workflow definition is
|
|
318
|
+
* reloaded from code at process start and the predicate closure is rebuilt naturally.
|
|
319
|
+
* If a deploy ships a changed predicate between suspend and resume, the *new* predicate
|
|
320
|
+
* runs — document this in your runbook when the predicate carries business logic that
|
|
321
|
+
* may change across deploys.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```ts
|
|
325
|
+
* export const slackApprovalNode = defineHumanApprovalNode({
|
|
326
|
+
* key: "my-plugin.slackApproval",
|
|
327
|
+
* title: "Slack Approval",
|
|
328
|
+
* channel: "slack",
|
|
329
|
+
* configSchema: z.object({ channel: z.string(), message: z.string() }),
|
|
330
|
+
* decisionSchema: z.object({ approved: z.boolean(), note: z.string().optional() }),
|
|
331
|
+
*
|
|
332
|
+
* async deliver({ task, config, item }, ctx) {
|
|
333
|
+
* const ts = await postSlackMessage(config.channel, `Approve? <${task.resumeUrl}>`);
|
|
334
|
+
* return { channel: config.channel, ts };
|
|
335
|
+
* },
|
|
336
|
+
*
|
|
337
|
+
* async onDecision({ decision, actor, delivery }, ctx) {
|
|
338
|
+
* await updateSlackMessage(delivery.channel, delivery.ts, decision.approved ? "✅" : "❌");
|
|
339
|
+
* },
|
|
340
|
+
* });
|
|
341
|
+
* ```
|
|
342
|
+
*/
|
|
343
|
+
function defineHumanApprovalNode(opts) {
|
|
344
|
+
const resolvedPredicate = resolveApprovedPredicate(opts.decisionSchema, opts.approvedPredicate);
|
|
345
|
+
const timeout = opts.defaultTimeout ?? "24h";
|
|
346
|
+
const onTimeout = opts.defaultOnTimeout ?? "halt";
|
|
347
|
+
const inner = defineNode({
|
|
348
|
+
key: opts.key,
|
|
349
|
+
title: opts.title,
|
|
350
|
+
description: opts.description,
|
|
351
|
+
icon: opts.icon,
|
|
352
|
+
configSchema: opts.configSchema,
|
|
353
|
+
inputSchema: opts.inputSchema,
|
|
354
|
+
credentials: opts.credentials,
|
|
355
|
+
inspectorSummary: opts.inspectorSummary ? ({ config }) => opts.inspectorSummary(config) : void 0,
|
|
356
|
+
async execute(args, { config, execution: ctx }) {
|
|
357
|
+
if (!ctx.resumeContext) {
|
|
358
|
+
const subject = buildSubject(opts.title, args.item, ctx);
|
|
359
|
+
throw new SuspensionRequest({
|
|
360
|
+
decisionSchema: opts.decisionSchema,
|
|
361
|
+
timeout,
|
|
362
|
+
onTimeout,
|
|
363
|
+
subject,
|
|
364
|
+
metadata: {
|
|
365
|
+
channel: opts.channel,
|
|
366
|
+
nodeKey: opts.key,
|
|
367
|
+
approvedPredicateSource: opts.approvedPredicate?.toString() ?? null
|
|
368
|
+
},
|
|
369
|
+
deliver: (handle) => opts.deliver({
|
|
370
|
+
task: handle,
|
|
371
|
+
config,
|
|
372
|
+
input: args.input,
|
|
373
|
+
item: args.item
|
|
374
|
+
}, ctx)
|
|
402
375
|
});
|
|
403
376
|
}
|
|
377
|
+
return await handleResume(args.item, ctx.resumeContext, opts.decisionSchema, resolvedPredicate, opts.onDecision, opts.onTimeout, ctx);
|
|
404
378
|
}
|
|
379
|
+
});
|
|
380
|
+
return Object.assign(inner, { humanApprovalToolBehavior: { onRejected: "return" } });
|
|
381
|
+
}
|
|
382
|
+
function resolveApprovedPredicate(schema, predicate) {
|
|
383
|
+
if (predicate) return predicate;
|
|
384
|
+
const shape = schema.shape;
|
|
385
|
+
if (shape && typeof shape === "object" && "approved" in shape) return (d) => d.approved === true;
|
|
386
|
+
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.");
|
|
387
|
+
}
|
|
388
|
+
function buildSubject(title, item, ctx) {
|
|
389
|
+
return {
|
|
390
|
+
title,
|
|
391
|
+
summary: "",
|
|
392
|
+
attributes: {
|
|
393
|
+
workflowId: ctx.workflowId,
|
|
394
|
+
nodeId: ctx.nodeId,
|
|
395
|
+
item: item.json
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function mergeDecision(item, decision) {
|
|
400
|
+
return {
|
|
401
|
+
json: {
|
|
402
|
+
...item.json,
|
|
403
|
+
decision
|
|
404
|
+
},
|
|
405
|
+
binary: item.binary,
|
|
406
|
+
meta: item.meta
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
async function handleResume(item, resumeContext, decisionSchema, resolvedPredicate, onDecision, onTimeoutCb, ctx) {
|
|
410
|
+
const { decision: dec, delivery, task } = resumeContext;
|
|
411
|
+
if (dec.kind === "timed_out" || dec.kind === "auto_accepted") {
|
|
412
|
+
const policy = dec.kind === "auto_accepted" ? "auto-accept" : "halt";
|
|
413
|
+
await onTimeoutCb?.({
|
|
414
|
+
task,
|
|
415
|
+
delivery,
|
|
416
|
+
item,
|
|
417
|
+
policy
|
|
418
|
+
}, ctx);
|
|
419
|
+
return mergeDecision(item, {
|
|
420
|
+
status: dec.kind === "auto_accepted" ? "auto-accepted" : "timed-out",
|
|
421
|
+
decidedAt: dec.at
|
|
422
|
+
});
|
|
405
423
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}();
|
|
424
|
+
const parsed = decisionSchema.parse(dec.value);
|
|
425
|
+
await onDecision?.({
|
|
426
|
+
decision: parsed,
|
|
427
|
+
actor: dec.actor,
|
|
428
|
+
task,
|
|
429
|
+
delivery,
|
|
430
|
+
item
|
|
431
|
+
}, ctx);
|
|
432
|
+
return mergeDecision(item, {
|
|
433
|
+
status: resolvedPredicate(parsed) ? "approved" : "rejected",
|
|
434
|
+
actor: dec.actor,
|
|
435
|
+
decidedAt: dec.decidedAt,
|
|
436
|
+
note: parsed.note,
|
|
437
|
+
payload: parsed
|
|
438
|
+
});
|
|
439
|
+
}
|
|
423
440
|
|
|
424
441
|
//#endregion
|
|
425
442
|
//#region src/workflow/dsl/WhenBuilder.ts
|
|
@@ -547,6 +564,25 @@ var ChainCursor = class ChainCursor {
|
|
|
547
564
|
}
|
|
548
565
|
return new ChainCursor(this.wf, nextEndpoints);
|
|
549
566
|
}
|
|
567
|
+
/**
|
|
568
|
+
* Chainable shorthand for `.then(node.create(config, metadata?.name, metadata?.nodeId))`.
|
|
569
|
+
*
|
|
570
|
+
* Signals to readers that this step suspends the run and waits for a human decision.
|
|
571
|
+
* Throws at workflow-build time if `node` was not created via `defineHumanApprovalNode`.
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* workflow
|
|
576
|
+
* .trigger(...)
|
|
577
|
+
* .humanApproval(inboxApproval, { title: "Approve?", body: "...", priority: "normal" })
|
|
578
|
+
* .then(nextStep.create(...))
|
|
579
|
+
* .build();
|
|
580
|
+
* ```
|
|
581
|
+
*/
|
|
582
|
+
humanApproval(node$1, config, metadata) {
|
|
583
|
+
if (!isHumanApprovalNode(node$1)) throw new Error(`.humanApproval() requires a node created via defineHumanApprovalNode (got '${node$1.key ?? String(node$1)}').`);
|
|
584
|
+
return this.then(node$1.create(config, metadata?.name, metadata?.nodeId));
|
|
585
|
+
}
|
|
550
586
|
build() {
|
|
551
587
|
return this.wf.build();
|
|
552
588
|
}
|
|
@@ -711,33 +747,6 @@ var ConnectionInvocationIdFactory = class {
|
|
|
711
747
|
}
|
|
712
748
|
};
|
|
713
749
|
|
|
714
|
-
//#endregion
|
|
715
|
-
//#region src/workflow/definition/NodeIterationIdFactory.ts
|
|
716
|
-
/**
|
|
717
|
-
* Unique ids for one per-item iteration of a runnable node's execute loop.
|
|
718
|
-
*
|
|
719
|
-
* Activations are per-batch (one scheduled execution of a node, possibly with N items).
|
|
720
|
-
* Iterations refine that to one identifier per item-index inside the batch loop, so per-item
|
|
721
|
-
* connection invocations and telemetry can be grouped without time-window heuristics.
|
|
722
|
-
*
|
|
723
|
-
* Uses Web Crypto's `randomUUID` (Node 19+ and all modern browsers) so this module is safe
|
|
724
|
-
* to include in the browser entry. Importing `node:crypto` here previously leaked into the
|
|
725
|
-
* canvas client bundle through `browser.ts` and OOM'd consumers' Turbopack builds.
|
|
726
|
-
*/
|
|
727
|
-
var NodeIterationIdFactory = class {
|
|
728
|
-
static create() {
|
|
729
|
-
return `iter_${globalThis.crypto.randomUUID()}`;
|
|
730
|
-
}
|
|
731
|
-
/** Deterministic id for tests when a stable sequence is needed. */
|
|
732
|
-
static createForTest(seed, sequence) {
|
|
733
|
-
return `iter_${seed}_${sequence}`;
|
|
734
|
-
}
|
|
735
|
-
/** Deterministic id derived from a connection node id (for sub-agent / tool-call scopes). */
|
|
736
|
-
static createForConnection(connectionNodeId, sequence) {
|
|
737
|
-
return `iter_${connectionNodeId}_${sequence}`;
|
|
738
|
-
}
|
|
739
|
-
};
|
|
740
|
-
|
|
741
750
|
//#endregion
|
|
742
751
|
//#region src/workflow/definition/WorkflowExecutableNodeClassifier.ts
|
|
743
752
|
/**
|
|
@@ -1040,7 +1049,7 @@ var NodeExecutionSnapshotFactory = class {
|
|
|
1040
1049
|
nodeId: args.nodeId,
|
|
1041
1050
|
activationId: args.activationId,
|
|
1042
1051
|
parent: args.parent,
|
|
1043
|
-
status: "completed",
|
|
1052
|
+
status: args.hitlStatus ?? "completed",
|
|
1044
1053
|
queuedAt: args.previous?.queuedAt,
|
|
1045
1054
|
startedAt,
|
|
1046
1055
|
finishedAt: args.finishedAt,
|
|
@@ -1167,7 +1176,9 @@ var ActivationEnqueueService = class {
|
|
|
1167
1176
|
nodeSnapshotsByNodeId: {
|
|
1168
1177
|
...args.previousNodeSnapshotsByNodeId,
|
|
1169
1178
|
[args.request.nodeId]: queuedSnapshot
|
|
1170
|
-
}
|
|
1179
|
+
},
|
|
1180
|
+
...args.suspension !== void 0 ? { suspension: args.suspension } : {},
|
|
1181
|
+
...args.pendingResume !== void 0 ? { pendingResume: args.pendingResume } : {}
|
|
1171
1182
|
});
|
|
1172
1183
|
await this.dispatchPreparedActivation(preparedDispatch);
|
|
1173
1184
|
return {
|
|
@@ -1419,20 +1430,6 @@ var DefaultAsyncSleeper = class {
|
|
|
1419
1430
|
}
|
|
1420
1431
|
};
|
|
1421
1432
|
|
|
1422
|
-
//#endregion
|
|
1423
|
-
//#region src/contracts/NoOpAgentMcpIntegration.ts
|
|
1424
|
-
/**
|
|
1425
|
-
* No-op implementation of AgentMcpIntegration.
|
|
1426
|
-
* Registered by the core engine runtime as a fallback when the host does not
|
|
1427
|
-
* supply a real implementation (e.g. in unit tests or headless engine setups).
|
|
1428
|
-
* Always returns an empty tool map so the agent runs with node-backed tools only.
|
|
1429
|
-
*/
|
|
1430
|
-
var NoOpAgentMcpIntegration = class {
|
|
1431
|
-
async prepareMcpTools() {
|
|
1432
|
-
return /* @__PURE__ */ new Map();
|
|
1433
|
-
}
|
|
1434
|
-
};
|
|
1435
|
-
|
|
1436
1433
|
//#endregion
|
|
1437
1434
|
//#region src/contracts/emitPorts.ts
|
|
1438
1435
|
const EMIT_PORTS_BRAND = Symbol.for("codemation.emitPorts");
|
|
@@ -1495,21 +1492,6 @@ var ExpRetryPolicy = class {
|
|
|
1495
1492
|
}
|
|
1496
1493
|
};
|
|
1497
1494
|
|
|
1498
|
-
//#endregion
|
|
1499
|
-
//#region src/contracts/credentialTypes.ts
|
|
1500
|
-
var CredentialUnboundError = class CredentialUnboundError extends Error {
|
|
1501
|
-
constructor(bindingKey, acceptedTypes = []) {
|
|
1502
|
-
super(CredentialUnboundError.createMessage(bindingKey, acceptedTypes));
|
|
1503
|
-
this.bindingKey = bindingKey;
|
|
1504
|
-
this.acceptedTypes = acceptedTypes;
|
|
1505
|
-
this.name = "CredentialUnboundError";
|
|
1506
|
-
}
|
|
1507
|
-
static createMessage(bindingKey, acceptedTypes) {
|
|
1508
|
-
const acceptedTypesSuffix = acceptedTypes.length > 0 ? ` Accepted credential types: ${acceptedTypes.join(", ")}.` : "";
|
|
1509
|
-
return `Credential slot "${bindingKey.slotKey}" is not bound for workflow ${bindingKey.workflowId} node ${bindingKey.nodeId}.${acceptedTypesSuffix}`;
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
|
|
1513
1495
|
//#endregion
|
|
1514
1496
|
//#region src/contracts/CostTrackingTelemetryContract.ts
|
|
1515
1497
|
const CostTrackingTelemetryMetricNames = {
|
|
@@ -1631,19 +1613,6 @@ var NoOpExecutionTelemetryFactory = class {
|
|
|
1631
1613
|
}
|
|
1632
1614
|
};
|
|
1633
1615
|
|
|
1634
|
-
//#endregion
|
|
1635
|
-
//#region src/contracts/runFinishedAtFactory.ts
|
|
1636
|
-
/** Derives workflow end time from persisted run root or node snapshots for run listings. */
|
|
1637
|
-
var RunFinishedAtFactory = class {
|
|
1638
|
-
static resolveIso(state) {
|
|
1639
|
-
if (state.finishedAt && state.status !== "running" && state.status !== "pending") return state.finishedAt;
|
|
1640
|
-
if (state.status === "running" || state.status === "pending") return;
|
|
1641
|
-
let max;
|
|
1642
|
-
for (const snap of Object.values(state.nodeSnapshotsByNodeId)) if (snap?.finishedAt && (!max || snap.finishedAt > max)) max = snap.finishedAt;
|
|
1643
|
-
return max;
|
|
1644
|
-
}
|
|
1645
|
-
};
|
|
1646
|
-
|
|
1647
1616
|
//#endregion
|
|
1648
1617
|
//#region src/contracts/workflowActivationPolicy.ts
|
|
1649
1618
|
/** Default for tests and harnesses: every workflow is treated as active (legacy behavior). */
|
|
@@ -1676,6 +1645,16 @@ var CodemationTelemetryAttributeNames = class {
|
|
|
1676
1645
|
static mcpServerId = "mcp.server_id";
|
|
1677
1646
|
/** MCP tool name on spans created for callTool invocations. */
|
|
1678
1647
|
static mcpToolName = "mcp.tool_name";
|
|
1648
|
+
/** Terminal node-execution status (e.g. `"hitl-approved"`, `"hitl-rejected"`) on HITL outcome spans. */
|
|
1649
|
+
static nodeExecutionStatus = "codemation.node.execution_status";
|
|
1650
|
+
/** Populated on run-halted spans; discriminates the halt reason (e.g. `"hitl-rejected"`). */
|
|
1651
|
+
static runHaltReason = "codemation.run.halt_reason";
|
|
1652
|
+
/** Human task ID on `hitl.task.*` span events. */
|
|
1653
|
+
static hitlTaskId = "codemation.hitl.task_id";
|
|
1654
|
+
/** HITL channel name (e.g. `"inbox"`, `"control-plane-inbox"`) on `hitl.task.*` span events. */
|
|
1655
|
+
static hitlChannel = "codemation.hitl.channel";
|
|
1656
|
+
/** Decision outcome (e.g. `"approved"`, `"rejected"`) on `hitl.task.decided` span events. */
|
|
1657
|
+
static hitlDecisionStatus = "codemation.hitl.decision_status";
|
|
1679
1658
|
};
|
|
1680
1659
|
|
|
1681
1660
|
//#endregion
|
|
@@ -1701,13 +1680,6 @@ var CodemationTelemetryMetricNames = class {
|
|
|
1701
1680
|
static gmailAttachmentBytes = "codemation.gmail.attachment_bytes";
|
|
1702
1681
|
};
|
|
1703
1682
|
|
|
1704
|
-
//#endregion
|
|
1705
|
-
//#region src/contracts/workflowTypes.ts
|
|
1706
|
-
function nodeRef(nodeId) {
|
|
1707
|
-
return nodeId;
|
|
1708
|
-
}
|
|
1709
|
-
const branchRef = (index) => `$${index}`;
|
|
1710
|
-
|
|
1711
1683
|
//#endregion
|
|
1712
1684
|
//#region src/execution/ExecutionTelemetryCostTrackingDecoratorFactory.ts
|
|
1713
1685
|
var ExecutionTelemetryCostTrackingDecoratorFactory = class {
|
|
@@ -1781,12 +1753,13 @@ var ExecutionTelemetryCostTrackingDecoratorFactory = class {
|
|
|
1781
1753
|
//#region src/execution/DefaultExecutionContextFactory.ts
|
|
1782
1754
|
var DefaultExecutionContextFactory = class {
|
|
1783
1755
|
telemetryDecoratorFactory = new ExecutionTelemetryCostTrackingDecoratorFactory();
|
|
1784
|
-
constructor(binaryStorage = new UnavailableBinaryStorage(), telemetryFactory = new NoOpExecutionTelemetryFactory(), costTrackingFactory = new NoOpCostTrackingTelemetryFactory(), currentDate = () => /* @__PURE__ */ new Date(), collections) {
|
|
1756
|
+
constructor(binaryStorage = new UnavailableBinaryStorage(), telemetryFactory = new NoOpExecutionTelemetryFactory(), costTrackingFactory = new NoOpCostTrackingTelemetryFactory(), currentDate = () => /* @__PURE__ */ new Date(), collections, nodeResolver) {
|
|
1785
1757
|
this.binaryStorage = binaryStorage;
|
|
1786
1758
|
this.telemetryFactory = telemetryFactory;
|
|
1787
1759
|
this.costTrackingFactory = costTrackingFactory;
|
|
1788
1760
|
this.currentDate = currentDate;
|
|
1789
1761
|
this.collections = collections;
|
|
1762
|
+
this.nodeResolver = nodeResolver;
|
|
1790
1763
|
}
|
|
1791
1764
|
create(args) {
|
|
1792
1765
|
const baseTelemetry = args.telemetry ?? this.telemetryFactory.create({
|
|
@@ -1813,7 +1786,11 @@ var DefaultExecutionContextFactory = class {
|
|
|
1813
1786
|
binary: new DefaultExecutionBinaryService(this.binaryStorage, args.workflowId, args.runId, this.currentDate),
|
|
1814
1787
|
getCredential: args.getCredential,
|
|
1815
1788
|
testContext: args.testContext,
|
|
1816
|
-
collections: this.collections
|
|
1789
|
+
collections: this.collections,
|
|
1790
|
+
resolve: (token) => {
|
|
1791
|
+
if (!this.nodeResolver) throw new Error("ExecutionContext.resolve() is not available: no NodeResolver was provided to DefaultExecutionContextFactory.");
|
|
1792
|
+
return this.nodeResolver.resolve(token);
|
|
1793
|
+
}
|
|
1817
1794
|
};
|
|
1818
1795
|
}
|
|
1819
1796
|
};
|
|
@@ -2155,6 +2132,27 @@ var NodeActivationRequestComposer = class {
|
|
|
2155
2132
|
}
|
|
2156
2133
|
};
|
|
2157
2134
|
|
|
2135
|
+
//#endregion
|
|
2136
|
+
//#region src/execution/RunSuspendedError.ts
|
|
2137
|
+
/**
|
|
2138
|
+
* Internal sentinel thrown by {@link NodeSuspensionHandler} after persisting a suspension
|
|
2139
|
+
* entry. `NodeExecutionRequestHandlerService` catches this specifically and returns cleanly —
|
|
2140
|
+
* no continuation call, preventing `resumeFromNodeResult` / `resumeFromNodeError` from
|
|
2141
|
+
* overwriting the `"suspended"` run status.
|
|
2142
|
+
*
|
|
2143
|
+
* The `Error` suffix satisfies the ESLint `no-manual-di-new` allowlist. This is NOT a
|
|
2144
|
+
* user-facing error — it is an engine-internal control-flow primitive and should NOT be
|
|
2145
|
+
* exported from the public barrel.
|
|
2146
|
+
*/
|
|
2147
|
+
var RunSuspendedError = class extends Error {
|
|
2148
|
+
constructor(runId, taskId) {
|
|
2149
|
+
super(`RunSuspendedError: run ${runId} suspended on task ${taskId}`);
|
|
2150
|
+
this.runId = runId;
|
|
2151
|
+
this.taskId = taskId;
|
|
2152
|
+
this.name = "RunSuspendedError";
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
|
|
2158
2156
|
//#endregion
|
|
2159
2157
|
//#region src/execution/NodeExecutor.ts
|
|
2160
2158
|
var NodeExecutor = class {
|
|
@@ -2162,9 +2160,11 @@ var NodeExecutor = class {
|
|
|
2162
2160
|
outputNormalizer = new NodeOutputNormalizer();
|
|
2163
2161
|
itemExprResolver;
|
|
2164
2162
|
outputBehaviorResolver;
|
|
2165
|
-
constructor(nodeInstanceFactory, retryRunner, itemExprResolver, outputBehaviorResolver) {
|
|
2163
|
+
constructor(nodeInstanceFactory, retryRunner, itemExprResolver, outputBehaviorResolver, suspensionHandler, loadRunState) {
|
|
2166
2164
|
this.nodeInstanceFactory = nodeInstanceFactory;
|
|
2167
2165
|
this.retryRunner = retryRunner;
|
|
2166
|
+
this.suspensionHandler = suspensionHandler;
|
|
2167
|
+
this.loadRunState = loadRunState;
|
|
2168
2168
|
this.itemExprResolver = itemExprResolver ?? new ItemExprResolver();
|
|
2169
2169
|
this.outputBehaviorResolver = outputBehaviorResolver ?? new RunnableOutputBehaviorResolver();
|
|
2170
2170
|
}
|
|
@@ -2264,6 +2264,7 @@ var NodeExecutor = class {
|
|
|
2264
2264
|
});
|
|
2265
2265
|
}
|
|
2266
2266
|
const byPort = {};
|
|
2267
|
+
let hasSuspension = false;
|
|
2267
2268
|
for (let i = 0; i < inputBatch.length; i++) {
|
|
2268
2269
|
const item = inputBatch[i];
|
|
2269
2270
|
this.assertItemJsonNotTopLevelArray(request.nodeId, item);
|
|
@@ -2282,7 +2283,28 @@ var NodeExecutor = class {
|
|
|
2282
2283
|
items: inputBatch,
|
|
2283
2284
|
ctx: iterationCtx
|
|
2284
2285
|
};
|
|
2285
|
-
|
|
2286
|
+
let raw;
|
|
2287
|
+
try {
|
|
2288
|
+
raw = await Promise.resolve(node$1.execute(args));
|
|
2289
|
+
} catch (e) {
|
|
2290
|
+
if (e instanceof SuspensionRequest || e instanceof Error && e.name === "SuspensionRequest" && typeof e.request === "object") {
|
|
2291
|
+
if (!this.suspensionHandler || !this.loadRunState) throw new Error(`Node ${request.nodeId} threw SuspensionRequest but this NodeExecutor has no suspensionHandler configured.`, { cause: e });
|
|
2292
|
+
const state = await this.loadRunState(request.runId);
|
|
2293
|
+
if (!state) throw new Error(`NodeExecutor: run state not found for runId ${request.runId} during suspension`, { cause: e });
|
|
2294
|
+
await this.suspensionHandler.handle({
|
|
2295
|
+
runId: request.runId,
|
|
2296
|
+
nodeId: request.nodeId,
|
|
2297
|
+
activationId: request.activationId,
|
|
2298
|
+
itemIndex: i,
|
|
2299
|
+
suspensionRequest: e,
|
|
2300
|
+
state,
|
|
2301
|
+
telemetry: iterationCtx.telemetry
|
|
2302
|
+
});
|
|
2303
|
+
hasSuspension = true;
|
|
2304
|
+
continue;
|
|
2305
|
+
}
|
|
2306
|
+
throw e;
|
|
2307
|
+
}
|
|
2286
2308
|
const normalized = this.outputNormalizer.normalizeExecuteResult({
|
|
2287
2309
|
baseItem: item,
|
|
2288
2310
|
raw,
|
|
@@ -2295,6 +2317,7 @@ var NodeExecutor = class {
|
|
|
2295
2317
|
byPort[port] = list;
|
|
2296
2318
|
}
|
|
2297
2319
|
}
|
|
2320
|
+
if (hasSuspension) throw new RunSuspendedError(request.runId, "unknown");
|
|
2298
2321
|
return byPort;
|
|
2299
2322
|
}
|
|
2300
2323
|
/** Use resolver ctx only when {@link NodeExecutionContext.config} is non-nullish. */
|
|
@@ -2321,8 +2344,8 @@ var NodeExecutor = class {
|
|
|
2321
2344
|
//#endregion
|
|
2322
2345
|
//#region src/execution/NodeExecutorFactory.ts
|
|
2323
2346
|
var NodeExecutorFactory = class {
|
|
2324
|
-
create(workflowNodeInstanceFactory, retryRunner, outputBehaviorResolver) {
|
|
2325
|
-
return new NodeExecutor(workflowNodeInstanceFactory, retryRunner, void 0, outputBehaviorResolver);
|
|
2347
|
+
create(workflowNodeInstanceFactory, retryRunner, outputBehaviorResolver, suspensionHandler, loadRunState) {
|
|
2348
|
+
return new NodeExecutor(workflowNodeInstanceFactory, retryRunner, void 0, outputBehaviorResolver, suspensionHandler, loadRunState);
|
|
2326
2349
|
}
|
|
2327
2350
|
};
|
|
2328
2351
|
|
|
@@ -2915,6 +2938,7 @@ var PersistedRunStateTerminalBuilder = class {
|
|
|
2915
2938
|
...args.state,
|
|
2916
2939
|
engineCounters: args.engineCounters,
|
|
2917
2940
|
status: args.status,
|
|
2941
|
+
reason: args.reason,
|
|
2918
2942
|
pending: void 0,
|
|
2919
2943
|
queue: args.queue,
|
|
2920
2944
|
outputsByNode: args.outputsByNode,
|
|
@@ -3197,6 +3221,7 @@ var RunContinuationService = class {
|
|
|
3197
3221
|
});
|
|
3198
3222
|
data.setOutputs(args.nodeId, args.outputs);
|
|
3199
3223
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3224
|
+
const hitlResolution = this.resolveHitlStatus(args.outputs);
|
|
3200
3225
|
const completedSnapshot = this.semantics.createFinishedSnapshot({
|
|
3201
3226
|
workflow: wf,
|
|
3202
3227
|
previous: state.nodeSnapshotsByNodeId?.[args.nodeId],
|
|
@@ -3207,8 +3232,41 @@ var RunContinuationService = class {
|
|
|
3207
3232
|
parent: state.parent,
|
|
3208
3233
|
finishedAt: completedAt,
|
|
3209
3234
|
inputsByPort: pendingExecution.inputsByPort,
|
|
3210
|
-
outputs: args.outputs
|
|
3235
|
+
outputs: args.outputs,
|
|
3236
|
+
hitlStatus: hitlResolution?.nodeStatus
|
|
3211
3237
|
});
|
|
3238
|
+
if (hitlResolution?.halt) {
|
|
3239
|
+
const haltedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
3240
|
+
state,
|
|
3241
|
+
engineCounters: state.engineCounters ?? { completedNodeActivations: 0 },
|
|
3242
|
+
status: "halted",
|
|
3243
|
+
reason: hitlResolution.reason,
|
|
3244
|
+
queue: [],
|
|
3245
|
+
outputsByNode: data.dump(),
|
|
3246
|
+
nodeSnapshotsByNodeId: {
|
|
3247
|
+
...state.nodeSnapshotsByNodeId ?? {},
|
|
3248
|
+
[args.nodeId]: completedSnapshot
|
|
3249
|
+
},
|
|
3250
|
+
finishedAtIso: completedAt
|
|
3251
|
+
});
|
|
3252
|
+
await this.workflowExecutionRepository.save(haltedState);
|
|
3253
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
3254
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
3255
|
+
workflow: wf,
|
|
3256
|
+
state: haltedState,
|
|
3257
|
+
finalStatus: "failed",
|
|
3258
|
+
finishedAt: completedAt
|
|
3259
|
+
});
|
|
3260
|
+
const result = {
|
|
3261
|
+
runId: state.runId,
|
|
3262
|
+
workflowId: state.workflowId,
|
|
3263
|
+
startedAt: state.startedAt,
|
|
3264
|
+
status: "halted",
|
|
3265
|
+
reason: hitlResolution.reason
|
|
3266
|
+
};
|
|
3267
|
+
this.waiters.resolveRunCompletion(result);
|
|
3268
|
+
return result;
|
|
3269
|
+
}
|
|
3212
3270
|
const completedActivations = (state.engineCounters?.completedNodeActivations ?? 0) + 1;
|
|
3213
3271
|
const engineCounters = { completedNodeActivations: completedActivations };
|
|
3214
3272
|
const maxNodeActivations = state.executionOptions?.maxNodeActivations ?? this.executionLimitsPolicy.createRootExecutionOptions().maxNodeActivations;
|
|
@@ -3509,13 +3567,114 @@ var RunContinuationService = class {
|
|
|
3509
3567
|
status: "failed",
|
|
3510
3568
|
error: { message: "Run failed" }
|
|
3511
3569
|
};
|
|
3570
|
+
if (existing?.status === "halted") return {
|
|
3571
|
+
runId: existing.runId,
|
|
3572
|
+
workflowId: existing.workflowId,
|
|
3573
|
+
startedAt: existing.startedAt,
|
|
3574
|
+
status: "halted",
|
|
3575
|
+
reason: existing.reason ?? "hitl-rejected"
|
|
3576
|
+
};
|
|
3512
3577
|
const result = await this.waiters.waitForCompletion(runId);
|
|
3513
|
-
if (result.status !== "completed" && result.status !== "failed") throw new Error(`Unexpected run completion status: ${result.status}`);
|
|
3578
|
+
if (result.status !== "completed" && result.status !== "failed" && result.status !== "halted") throw new Error(`Unexpected run completion status: ${result.status}`);
|
|
3514
3579
|
return result;
|
|
3515
3580
|
}
|
|
3516
3581
|
async waitForWebhookResponse(runId) {
|
|
3517
3582
|
return await this.waiters.waitForWebhookResponse(runId);
|
|
3518
3583
|
}
|
|
3584
|
+
/**
|
|
3585
|
+
* Re-activate a previously suspended run item with a human decision.
|
|
3586
|
+
*
|
|
3587
|
+
* Called by the HITL resume endpoint. This method:
|
|
3588
|
+
* 1. Loads `PersistedRunState` and locates the suspension entry by `taskId`.
|
|
3589
|
+
* 2. Removes the entry from the `suspension` array; if empty, run stays `"suspended"` until
|
|
3590
|
+
* enqueue flips it to `"pending"`.
|
|
3591
|
+
* 3. Writes `pendingResume` onto the state so `NodeExecutionRequestHandlerService` can
|
|
3592
|
+
* splice `resumeContext` into the node's execution context.
|
|
3593
|
+
* 4. Reconstructs the original input from `outputsByNode` of the upstream node and
|
|
3594
|
+
* enqueues a new activation via `activationEnqueueService`.
|
|
3595
|
+
*
|
|
3596
|
+
* @throws if the run is not found, not suspended, or the `taskId` is unknown.
|
|
3597
|
+
*/
|
|
3598
|
+
async resumeRun(args) {
|
|
3599
|
+
const state = await this.workflowExecutionRepository.load(args.runId);
|
|
3600
|
+
if (!state) throw new Error(`Unknown runId: ${args.runId}`);
|
|
3601
|
+
if (state.status !== "suspended") throw new Error(`Run ${args.runId} is not suspended (status: ${state.status})`);
|
|
3602
|
+
const suspensionEntry = (state.suspension ?? []).find((s) => s.taskId === args.taskId);
|
|
3603
|
+
if (!suspensionEntry) throw new Error(`No suspension entry with taskId "${args.taskId}" found on run ${args.runId}`);
|
|
3604
|
+
const wf = this.resolvePersistedWorkflow(state);
|
|
3605
|
+
if (!wf) throw new Error(`Unknown workflowId: ${state.workflowId}`);
|
|
3606
|
+
const { topology, planner } = this.planningFactory.create(wf);
|
|
3607
|
+
const def = topology.defsById.get(suspensionEntry.nodeId);
|
|
3608
|
+
if (!def || def.kind !== "node") throw new Error(`Node ${suspensionEntry.nodeId} is not a runnable node`);
|
|
3609
|
+
const data = this.runDataFactory.create(state.outputsByNode);
|
|
3610
|
+
const limits = this.resolveEngineLimitsFromState(state);
|
|
3611
|
+
const base = this.runExecutionContextFactory.create({
|
|
3612
|
+
runId: state.runId,
|
|
3613
|
+
workflowId: state.workflowId,
|
|
3614
|
+
nodeId: suspensionEntry.nodeId,
|
|
3615
|
+
parent: state.parent,
|
|
3616
|
+
policySnapshot: state.policySnapshot,
|
|
3617
|
+
subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
|
|
3618
|
+
engineMaxNodeActivations: limits.engineMaxNodeActivations,
|
|
3619
|
+
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
3620
|
+
data,
|
|
3621
|
+
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, state.parent),
|
|
3622
|
+
testContext: state.executionOptions?.testContext
|
|
3623
|
+
});
|
|
3624
|
+
const parentEdges = wf.edges.filter((e) => e.to.nodeId === suspensionEntry.nodeId);
|
|
3625
|
+
const parentNodeId = parentEdges[0]?.from.nodeId;
|
|
3626
|
+
const parentOutputPort = parentEdges[0]?.from.output ?? "main";
|
|
3627
|
+
const allParentItems = parentNodeId ? data.getOutputItems(parentNodeId, parentOutputPort) ?? [] : [];
|
|
3628
|
+
const resumeInput = allParentItems.length > suspensionEntry.itemIndex ? [allParentItems[suspensionEntry.itemIndex]] : allParentItems;
|
|
3629
|
+
const newActivationId = this.activationIdFactory.makeActivationId();
|
|
3630
|
+
const pendingResume = {
|
|
3631
|
+
activationId: newActivationId,
|
|
3632
|
+
nodeId: suspensionEntry.nodeId,
|
|
3633
|
+
resumeContext: args.resumeContext
|
|
3634
|
+
};
|
|
3635
|
+
const remainingSuspensions = (state.suspension ?? []).filter((s) => s.taskId !== args.taskId);
|
|
3636
|
+
const baseWithResume = {
|
|
3637
|
+
...base,
|
|
3638
|
+
resumeContext: args.resumeContext
|
|
3639
|
+
};
|
|
3640
|
+
const batchId = `resume_${newActivationId}`;
|
|
3641
|
+
const request = this.nodeActivationRequestComposer.createSingleFromDefinitionWithActivation({
|
|
3642
|
+
activationId: newActivationId,
|
|
3643
|
+
runId: state.runId,
|
|
3644
|
+
workflowId: state.workflowId,
|
|
3645
|
+
parent: state.parent,
|
|
3646
|
+
executionOptions: state.executionOptions,
|
|
3647
|
+
base: baseWithResume,
|
|
3648
|
+
data,
|
|
3649
|
+
definition: {
|
|
3650
|
+
id: suspensionEntry.nodeId,
|
|
3651
|
+
config: def.config
|
|
3652
|
+
},
|
|
3653
|
+
batchId,
|
|
3654
|
+
input: resumeInput
|
|
3655
|
+
});
|
|
3656
|
+
const { result, queuedSnapshot } = await this.activationEnqueueService.enqueueActivationWithSnapshot({
|
|
3657
|
+
runId: state.runId,
|
|
3658
|
+
workflowId: state.workflowId,
|
|
3659
|
+
startedAt: state.startedAt,
|
|
3660
|
+
parent: state.parent,
|
|
3661
|
+
executionOptions: state.executionOptions,
|
|
3662
|
+
control: state.control,
|
|
3663
|
+
workflowSnapshot: state.workflowSnapshot,
|
|
3664
|
+
mutableState: state.mutableState,
|
|
3665
|
+
policySnapshot: state.policySnapshot,
|
|
3666
|
+
pendingQueue: [],
|
|
3667
|
+
request,
|
|
3668
|
+
previousNodeSnapshotsByNodeId: state.nodeSnapshotsByNodeId ?? {},
|
|
3669
|
+
planner,
|
|
3670
|
+
engineCounters: state.engineCounters,
|
|
3671
|
+
connectionInvocations: state.connectionInvocations ?? [],
|
|
3672
|
+
suspension: remainingSuspensions.length > 0 ? remainingSuspensions : void 0,
|
|
3673
|
+
pendingResume
|
|
3674
|
+
});
|
|
3675
|
+
await this.nodeEventPublisher.publish("nodeQueued", queuedSnapshot);
|
|
3676
|
+
return result;
|
|
3677
|
+
}
|
|
3519
3678
|
async resumeFromWebhookControl(args) {
|
|
3520
3679
|
const data = this.runDataFactory.create(args.state.outputsByNode);
|
|
3521
3680
|
const { topology, planner } = this.planningFactory.create(args.workflow);
|
|
@@ -3902,6 +4061,34 @@ var RunContinuationService = class {
|
|
|
3902
4061
|
this.waiters.resolveRunCompletion(result);
|
|
3903
4062
|
return result;
|
|
3904
4063
|
}
|
|
4064
|
+
/**
|
|
4065
|
+
* Inspects node outputs for a `decision.status` written by `defineHumanApprovalNode`.
|
|
4066
|
+
* Returns the first-class HITL node status and halt classification, or `undefined`
|
|
4067
|
+
* when the node is not a HITL approval node.
|
|
4068
|
+
*/
|
|
4069
|
+
resolveHitlStatus(outputs) {
|
|
4070
|
+
const firstItem = outputs?.main?.[0];
|
|
4071
|
+
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;
|
|
4072
|
+
if (!decisionStatus) return void 0;
|
|
4073
|
+
if (decisionStatus === "approved") return {
|
|
4074
|
+
nodeStatus: "hitl-approved",
|
|
4075
|
+
halt: false
|
|
4076
|
+
};
|
|
4077
|
+
if (decisionStatus === "auto-accepted") return {
|
|
4078
|
+
nodeStatus: "hitl-auto-accepted",
|
|
4079
|
+
halt: false
|
|
4080
|
+
};
|
|
4081
|
+
if (decisionStatus === "rejected") return {
|
|
4082
|
+
nodeStatus: "hitl-rejected",
|
|
4083
|
+
halt: true,
|
|
4084
|
+
reason: "hitl-rejected"
|
|
4085
|
+
};
|
|
4086
|
+
if (decisionStatus === "timed-out") return {
|
|
4087
|
+
nodeStatus: "hitl-timeout",
|
|
4088
|
+
halt: true,
|
|
4089
|
+
reason: "hitl-timeout"
|
|
4090
|
+
};
|
|
4091
|
+
}
|
|
3905
4092
|
formatNodeLabel(args) {
|
|
3906
4093
|
const tokenName = typeof args.definition?.type === "function" ? args.definition.type.name : "Node";
|
|
3907
4094
|
return args.definition?.name ? `"${args.definition.name}" (${tokenName}:${args.nodeId})` : `${tokenName}:${args.nodeId}`;
|
|
@@ -5254,13 +5441,19 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5254
5441
|
const portKeys = Object.keys(inputsByPort);
|
|
5255
5442
|
const kind = portKeys.length === 1 && portKeys[0] === "in" ? "single" : "multi";
|
|
5256
5443
|
const batchId = pendingExecution.batchId ?? "batch_1";
|
|
5444
|
+
const pendingResume = state.pendingResume;
|
|
5445
|
+
const resumeContext = pendingResume?.activationId === request.activationId && pendingResume?.nodeId === request.nodeId ? pendingResume.resumeContext : void 0;
|
|
5446
|
+
const baseWithResume = resumeContext != null ? {
|
|
5447
|
+
...base,
|
|
5448
|
+
resumeContext
|
|
5449
|
+
} : base;
|
|
5257
5450
|
const activationRequest = kind === "multi" ? this.nodeActivationRequestComposer.createMultiFromDefinitionWithActivation({
|
|
5258
5451
|
activationId: request.activationId,
|
|
5259
5452
|
runId: request.runId,
|
|
5260
5453
|
workflowId: request.workflowId,
|
|
5261
5454
|
parent: resolvedParent,
|
|
5262
5455
|
executionOptions: request.executionOptions ?? state.executionOptions,
|
|
5263
|
-
base,
|
|
5456
|
+
base: baseWithResume,
|
|
5264
5457
|
data,
|
|
5265
5458
|
definition: {
|
|
5266
5459
|
id: definition.id,
|
|
@@ -5274,7 +5467,7 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5274
5467
|
workflowId: request.workflowId,
|
|
5275
5468
|
parent: resolvedParent,
|
|
5276
5469
|
executionOptions: request.executionOptions ?? state.executionOptions,
|
|
5277
|
-
base,
|
|
5470
|
+
base: baseWithResume,
|
|
5278
5471
|
data,
|
|
5279
5472
|
definition: {
|
|
5280
5473
|
id: definition.id,
|
|
@@ -5283,6 +5476,13 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5283
5476
|
batchId,
|
|
5284
5477
|
input: inputsByPort.in ?? request.input ?? []
|
|
5285
5478
|
});
|
|
5479
|
+
if (resumeContext != null) {
|
|
5480
|
+
const clearedState = await this.workflowExecutionRepository.load(request.runId);
|
|
5481
|
+
if (clearedState?.pendingResume?.activationId === request.activationId) await this.workflowExecutionRepository.save({
|
|
5482
|
+
...clearedState,
|
|
5483
|
+
pendingResume: void 0
|
|
5484
|
+
});
|
|
5485
|
+
}
|
|
5286
5486
|
await this.continuation.markNodeRunning({
|
|
5287
5487
|
runId: activationRequest.runId,
|
|
5288
5488
|
activationId: activationRequest.activationId,
|
|
@@ -5293,6 +5493,7 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
5293
5493
|
try {
|
|
5294
5494
|
outputs = await this.nodeExecutor.execute(activationRequest);
|
|
5295
5495
|
} catch (error) {
|
|
5496
|
+
if (error instanceof RunSuspendedError) return;
|
|
5296
5497
|
await this.resumeAfterExecutionError(activationRequest, this.asError(error));
|
|
5297
5498
|
return;
|
|
5298
5499
|
}
|
|
@@ -5853,7 +6054,7 @@ var EngineWaiters = class {
|
|
|
5853
6054
|
});
|
|
5854
6055
|
}
|
|
5855
6056
|
resolveRunCompletion(result) {
|
|
5856
|
-
if (result.status !== "completed" && result.status !== "failed") return;
|
|
6057
|
+
if (result.status !== "completed" && result.status !== "failed" && result.status !== "halted") return;
|
|
5857
6058
|
const list = this.completionWaiters.get(result.runId);
|
|
5858
6059
|
if (!list || list.length === 0) return;
|
|
5859
6060
|
this.completionWaiters.delete(result.runId);
|
|
@@ -5947,6 +6148,13 @@ var Engine = class {
|
|
|
5947
6148
|
async waitForWebhookResponse(runId) {
|
|
5948
6149
|
return await this.deps.runContinuationService.waitForWebhookResponse(runId);
|
|
5949
6150
|
}
|
|
6151
|
+
/**
|
|
6152
|
+
* Re-activate a suspended run item with a human decision (HITL).
|
|
6153
|
+
* The HTTP resume endpoint calls this; this method exposes the engine primitive.
|
|
6154
|
+
*/
|
|
6155
|
+
async resumeRun(args) {
|
|
6156
|
+
return await this.deps.runContinuationService.resumeRun(args);
|
|
6157
|
+
}
|
|
5950
6158
|
async handleNodeExecutionRequest(request) {
|
|
5951
6159
|
await this.deps.nodeExecutionRequestHandler.handleNodeExecutionRequest(request);
|
|
5952
6160
|
}
|
|
@@ -6154,6 +6362,7 @@ var RunIntentService = class {
|
|
|
6154
6362
|
};
|
|
6155
6363
|
return await Promise.race([this.engine.waitForWebhookResponse(scheduled.runId), this.engine.waitForCompletion(scheduled.runId).then((completed) => {
|
|
6156
6364
|
if (completed.status === "failed") throw new Error(completed.error.message);
|
|
6365
|
+
if (completed.status === "halted") throw new Error(`Run halted: ${completed.reason}`);
|
|
6157
6366
|
return {
|
|
6158
6367
|
runId: completed.runId,
|
|
6159
6368
|
workflowId: completed.workflowId,
|
|
@@ -6278,5 +6487,5 @@ var WorkflowRepositoryWebhookTriggerMatcherFactory = class {
|
|
|
6278
6487
|
};
|
|
6279
6488
|
|
|
6280
6489
|
//#endregion
|
|
6281
|
-
export {
|
|
6282
|
-
//# sourceMappingURL=runtime-
|
|
6490
|
+
export { CostTrackingTelemetryAttributeNames as $, PersistedWorkflowTokenRegistry as A, node as At, CatalogBackedCostTrackingTelemetryFactory as B, HumanTaskStoreToken as Bt, DefaultDrivingScheduler as C, WhenBuilder as Ct, NodeInstanceFactoryFactory as D, defineNode as Dt, StaticCostCatalog as E, defineBatchNode as Et, InProcessRetryRunnerFactory as F, PersistedRuntimeTypeNameResolver as Ft, AllWorkflowsActiveWorkflowActivationPolicy as G, CodemationTelemetryMetricNames as H, RunnableOutputBehaviorResolver as I, DefinedNodeRegistry as It, NoOpNodeExecutionTelemetry as J, NoOpExecutionTelemetryFactory as K, NodeOutputNormalizer as L, HitlResumeTokenSignerToken as Lt, NodeExecutorFactory as M, InjectableRuntimeDecoratorComposer as Mt, NodeExecutor as N, PersistedRuntimeTypeMetadataStore as Nt, NodeInstanceFactory as O, chatModel as Ot, RunSuspendedError as P, StackTraceCallSitePathResolver as Pt, NoOpCostTrackingTelemetry as Q, ItemExprResolver as R, HitlTimeoutJobSchedulerToken as Rt, HintOnlyOffloadPolicy as S, ChainCursor as St, RunPolicySnapshotFactory as T, isHumanApprovalNode as Tt, GenAiTelemetryAttributeNames as U, DefaultExecutionContextFactory as V, CodemationTelemetryAttributeNames as W, NoOpTelemetryArtifactReference as X, NoOpTelemetrySpanScope as Y, NoOpCostTrackingTelemetryFactory as Z, RunTerminalPersistenceCoordinator as _, WorkflowExecutableNodeClassifier as _t, InMemoryLiveWorkflowRepository as a, isPortsEmission as at, LocalOnlyScheduler as b, WorkflowDefinitionError as bt, EngineFactory as c, CredentialResolverFactory as ct, PollingTriggerRuntime as d, DefaultExecutionBinaryService as dt, CostTrackingTelemetryMetricNames as et, PollingTriggerDedupWindow as f, UnavailableBinaryStorage as ft, WorkflowPolicyErrorServices as g, WorkflowExecutableNodeClassifierFactory as gt, WorkflowStoragePolicyEvaluator as h, DefaultWorkflowGraphFactory as ht, RunIntentService as i, emitPorts as it, MissingRuntimeTriggerToken as j, tool as jt, WorkflowSnapshotCodec as k, getPersistedRuntimeTypeMetadata as kt, Engine as l, getOriginIndexFromItem as lt, InMemoryBinaryStorage as m, ConnectionInvocationEventPublisher as mt, WorkflowRepositoryWebhookTriggerMatcher as n, RetryPolicy as nt, EngineWorkflowRunnerServiceFactory as o, isUnbrandedPortsEmissionShape as ot, InMemoryRunDataFactory as p, NodeEventPublisher as pt, NoOpExecutionTelemetry as q, RunIntentServiceFactory as r, NoRetryPolicy as rt, EngineWorkflowRunnerService as s, DefaultAsyncSleeper as st, WorkflowRepositoryWebhookTriggerMatcherFactory as t, ExpRetryPolicy as tt, NoOpPollingTriggerLogger as u, ChildExecutionScopeFactory as ut, ENGINE_EXECUTION_LIMITS_DEFAULTS as v, ConnectionInvocationIdFactory as vt, ConfigDrivenOffloadPolicy as w, defineHumanApprovalNode as wt, InlineDrivingScheduler as x, NodeIdSlugifier as xt, EngineExecutionLimitsPolicy as y, WorkflowBuilder as yt, InProcessRetryRunner as z, HitlWorkspaceIdToken as zt };
|
|
6491
|
+
//# sourceMappingURL=runtime-BPZgnZ9G.js.map
|