@codemation/core 0.0.5 → 0.0.7
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/dist/InMemoryLiveWorkflowRepository-DxoualoC.d.ts +1344 -0
- package/dist/InMemoryLiveWorkflowRepository-orY1VsWG.d.cts +1172 -0
- package/dist/RunIntentService-ByuUYsAL.d.cts +279 -0
- package/dist/RunIntentService-CYnn140t.js +3136 -0
- package/dist/RunIntentService-CYnn140t.js.map +1 -0
- package/dist/RunIntentService-DlQH5eZ2.cjs +3467 -0
- package/dist/RunIntentService-DlQH5eZ2.cjs.map +1 -0
- package/dist/WorkflowSnapshotCodec-DSEzKyt3.d.cts +22 -0
- package/dist/bootstrap/index.cjs +133 -4
- package/dist/bootstrap/index.cjs.map +1 -1
- package/dist/bootstrap/index.d.cts +4 -3
- package/dist/bootstrap/index.d.ts +3 -2
- package/dist/bootstrap/index.js +133 -4
- package/dist/bootstrap/index.js.map +1 -1
- package/dist/index-CTjfVHJh.d.ts +694 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
- package/src/bootstrap/runtime/EngineRuntimeRegistrar.ts +10 -2
- package/src/contracts/runtimeTypes.ts +5 -0
- package/src/execution/NodeActivationRequestComposer.ts +15 -4
- package/src/orchestration/Engine.ts +12 -1
- package/src/orchestration/NodeExecutionRequestHandlerService.ts +179 -0
- package/src/runtime/EngineFactory.ts +13 -0
|
@@ -0,0 +1,3467 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
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
|
+
let node_crypto = require("node:crypto");
|
|
25
|
+
node_crypto = __toESM(node_crypto);
|
|
26
|
+
require("reflect-metadata");
|
|
27
|
+
let tsyringe = require("tsyringe");
|
|
28
|
+
tsyringe = __toESM(tsyringe);
|
|
29
|
+
let node_stream_web = require("node:stream/web");
|
|
30
|
+
node_stream_web = __toESM(node_stream_web);
|
|
31
|
+
|
|
32
|
+
//#region src/workflow/definition/ConnectionNodeIdFactory.ts
|
|
33
|
+
/**
|
|
34
|
+
* Deterministic ids for workflow connection-owned child nodes (LLM slot, tools, etc.).
|
|
35
|
+
* These are stable across loads.
|
|
36
|
+
*/
|
|
37
|
+
var ConnectionNodeIdFactory = class {
|
|
38
|
+
static connectionSegment = "__conn__";
|
|
39
|
+
static languageModelConnectionNodeId(parentNodeId) {
|
|
40
|
+
return `${parentNodeId}${this.connectionSegment}llm`;
|
|
41
|
+
}
|
|
42
|
+
static toolConnectionNodeId(parentNodeId, toolName) {
|
|
43
|
+
const normalized = this.normalizeToolName(toolName);
|
|
44
|
+
return `${parentNodeId}${this.connectionSegment}tool${this.connectionSegment}${normalized}`;
|
|
45
|
+
}
|
|
46
|
+
static isLanguageModelConnectionNodeId(nodeId) {
|
|
47
|
+
return nodeId.endsWith(`${this.connectionSegment}llm`);
|
|
48
|
+
}
|
|
49
|
+
static isToolConnectionNodeId(nodeId) {
|
|
50
|
+
return nodeId.includes(`${this.connectionSegment}tool${this.connectionSegment}`);
|
|
51
|
+
}
|
|
52
|
+
/** True when `nodeId` is a connection-owned child of `parentNodeId` (LLM or tool slot). */
|
|
53
|
+
static isConnectionOwnedDescendantOf(parentNodeId, nodeId) {
|
|
54
|
+
return nodeId.startsWith(`${parentNodeId}${this.connectionSegment}`);
|
|
55
|
+
}
|
|
56
|
+
/** Normalizes a tool display name to a stable id segment. */
|
|
57
|
+
static normalizeToolName(toolName) {
|
|
58
|
+
return toolName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "tool";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/workflow/definition/WorkflowExecutableNodeClassifier.ts
|
|
64
|
+
/**
|
|
65
|
+
* Derives which workflow nodes participate in the main execution graph vs connection-only children.
|
|
66
|
+
*/
|
|
67
|
+
var WorkflowExecutableNodeClassifier = class {
|
|
68
|
+
connectionOwnedIds;
|
|
69
|
+
constructor(workflow) {
|
|
70
|
+
this.connectionOwnedIds = this.collectConnectionOwnedIds(workflow);
|
|
71
|
+
}
|
|
72
|
+
isConnectionOwnedNodeId(nodeId) {
|
|
73
|
+
return this.connectionOwnedIds.has(nodeId);
|
|
74
|
+
}
|
|
75
|
+
isExecutableNodeId(nodeId) {
|
|
76
|
+
return !this.connectionOwnedIds.has(nodeId);
|
|
77
|
+
}
|
|
78
|
+
filterExecutableNodeDefinitions(nodes) {
|
|
79
|
+
return nodes.filter((n) => this.isExecutableNodeId(n.id));
|
|
80
|
+
}
|
|
81
|
+
collectConnectionOwnedIds(workflow) {
|
|
82
|
+
const ids = /* @__PURE__ */ new Set();
|
|
83
|
+
for (const connection of workflow.connections ?? []) for (const childId of connection.childNodeIds) ids.add(childId);
|
|
84
|
+
return ids;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Resolves the default start node: first trigger, else first executable node with no incoming edges from executable nodes.
|
|
88
|
+
*/
|
|
89
|
+
findDefaultExecutableStartNodeId(workflow) {
|
|
90
|
+
const firstTrigger = workflow.nodes.find((n) => n.kind === "trigger" && this.isExecutableNodeId(n.id))?.id;
|
|
91
|
+
if (firstTrigger) return firstTrigger;
|
|
92
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
93
|
+
for (const n of workflow.nodes) if (this.isExecutableNodeId(n.id)) incoming.set(n.id, 0);
|
|
94
|
+
for (const e of workflow.edges) {
|
|
95
|
+
if (!this.isExecutableNodeId(e.from.nodeId) || !this.isExecutableNodeId(e.to.nodeId)) continue;
|
|
96
|
+
incoming.set(e.to.nodeId, (incoming.get(e.to.nodeId) ?? 0) + 1);
|
|
97
|
+
}
|
|
98
|
+
return workflow.nodes.find((n) => this.isExecutableNodeId(n.id) && (incoming.get(n.id) ?? 0) === 0)?.id ?? workflow.nodes.find((n) => this.isExecutableNodeId(n.id))?.id ?? (() => {
|
|
99
|
+
throw new Error(`Workflow ${workflow.id} has no executable nodes`);
|
|
100
|
+
})();
|
|
101
|
+
}
|
|
102
|
+
firstExecutableNodeIdInDefinitionOrder(workflow) {
|
|
103
|
+
return workflow.nodes.find((n) => this.isExecutableNodeId(n.id))?.id;
|
|
104
|
+
}
|
|
105
|
+
lastExecutableNodeIdInDefinitionOrder(workflow) {
|
|
106
|
+
for (let i = workflow.nodes.length - 1; i >= 0; i--) {
|
|
107
|
+
const n = workflow.nodes[i];
|
|
108
|
+
if (this.isExecutableNodeId(n.id)) return n.id;
|
|
109
|
+
}
|
|
110
|
+
throw new Error(`Workflow ${workflow.id} has no executable nodes`);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/workflow/definition/WorkflowExecutableNodeClassifierFactory.ts
|
|
116
|
+
var WorkflowExecutableNodeClassifierFactory = class {
|
|
117
|
+
static create(workflow) {
|
|
118
|
+
return new WorkflowExecutableNodeClassifier(workflow);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/di/CoreTokens.ts
|
|
124
|
+
const CoreTokens = {
|
|
125
|
+
PersistedWorkflowTokenRegistry: Symbol.for("codemation.core.PersistedWorkflowTokenRegistry"),
|
|
126
|
+
CredentialSessionService: Symbol.for("codemation.core.CredentialSessionService"),
|
|
127
|
+
CredentialTypeRegistry: Symbol.for("codemation.core.CredentialTypeRegistry"),
|
|
128
|
+
WorkflowRunnerService: Symbol.for("codemation.core.WorkflowRunnerService"),
|
|
129
|
+
LiveWorkflowRepository: Symbol.for("codemation.core.LiveWorkflowRepository"),
|
|
130
|
+
WorkflowRepository: Symbol.for("codemation.core.WorkflowRepository"),
|
|
131
|
+
NodeResolver: Symbol.for("codemation.core.NodeResolver"),
|
|
132
|
+
WorkflowNodeInstanceFactory: Symbol.for("codemation.core.WorkflowNodeInstanceFactory"),
|
|
133
|
+
RunIdFactory: Symbol.for("codemation.core.RunIdFactory"),
|
|
134
|
+
ActivationIdFactory: Symbol.for("codemation.core.ActivationIdFactory"),
|
|
135
|
+
WorkflowExecutionRepository: Symbol.for("codemation.core.WorkflowExecutionRepository"),
|
|
136
|
+
TriggerSetupStateRepository: Symbol.for("codemation.core.TriggerSetupStateRepository"),
|
|
137
|
+
NodeActivationScheduler: Symbol.for("codemation.core.NodeActivationScheduler"),
|
|
138
|
+
RunDataFactory: Symbol.for("codemation.core.RunDataFactory"),
|
|
139
|
+
ExecutionContextFactory: Symbol.for("codemation.core.ExecutionContextFactory"),
|
|
140
|
+
RunEventBus: Symbol.for("codemation.core.RunEventBus"),
|
|
141
|
+
BinaryStorage: Symbol.for("codemation.core.BinaryStorage"),
|
|
142
|
+
WebhookBasePath: Symbol.for("codemation.core.WebhookBasePath"),
|
|
143
|
+
EngineExecutionLimitsPolicy: Symbol.for("codemation.core.EngineExecutionLimitsPolicy"),
|
|
144
|
+
WorkflowActivationPolicy: Symbol.for("codemation.core.WorkflowActivationPolicy")
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/events/NodeEventPublisher.ts
|
|
149
|
+
/** Publishes node lifecycle snapshots onto the run {@link RunEventBus}. */
|
|
150
|
+
var NodeEventPublisher = class {
|
|
151
|
+
constructor(eventBus) {
|
|
152
|
+
this.eventBus = eventBus;
|
|
153
|
+
}
|
|
154
|
+
async publish(kind, snapshot) {
|
|
155
|
+
if (!this.eventBus) return;
|
|
156
|
+
await this.eventBus.publish({
|
|
157
|
+
kind,
|
|
158
|
+
runId: snapshot.runId,
|
|
159
|
+
workflowId: snapshot.workflowId,
|
|
160
|
+
parent: snapshot.parent,
|
|
161
|
+
at: snapshot.updatedAt,
|
|
162
|
+
snapshot
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/runtime-types/persistedRuntimeTypeModelRegistry.ts
|
|
169
|
+
/** Shared metadata key used to attach persisted runtime-type information to decorated classes. */
|
|
170
|
+
const persistedRuntimeTypeMetadataKey = Symbol.for("codemation.core.persistedRuntimeTypeMetadata");
|
|
171
|
+
/** Normalizes decorator options so persistence metadata has stable defaults. */
|
|
172
|
+
var PersistedRuntimeTypeDecoratorDefaults = class {
|
|
173
|
+
static appPackageName = "app";
|
|
174
|
+
static apply(options) {
|
|
175
|
+
return {
|
|
176
|
+
...options,
|
|
177
|
+
packageName: options.packageName ?? this.appPackageName
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/runtime-types/PersistedRuntimeTypeNameResolver.ts
|
|
184
|
+
/** Resolves the persisted type name from either an explicit override or the class name itself. */
|
|
185
|
+
var PersistedRuntimeTypeNameResolver = class {
|
|
186
|
+
static resolve(target, override) {
|
|
187
|
+
const resolved = override ?? target.name;
|
|
188
|
+
if (!resolved) throw new Error("Persisted runtime token metadata requires a named class or an explicit decorator name override.");
|
|
189
|
+
return resolved;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/runtime-types/StackTraceCallSitePathResolver.ts
|
|
195
|
+
var StackTraceCallSitePathResolver = class {
|
|
196
|
+
static resolve(decoratorFileUrl) {
|
|
197
|
+
const stack = (/* @__PURE__ */ new Error()).stack ?? "";
|
|
198
|
+
for (const line of stack.split("\n")) {
|
|
199
|
+
const candidate = this.extractPath(line.trim());
|
|
200
|
+
if (!candidate) continue;
|
|
201
|
+
if (candidate === decoratorFileUrl || candidate.includes("runtimeTypeDecorators")) continue;
|
|
202
|
+
return candidate;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
static extractPath(line) {
|
|
206
|
+
const fileUrlMatch = line.match(/file:\/\/[^\s)]+/);
|
|
207
|
+
if (fileUrlMatch) return fileUrlMatch[0];
|
|
208
|
+
const parenMatch = line.match(/\((\/[^)]+)\)/);
|
|
209
|
+
if (parenMatch) return parenMatch[1];
|
|
210
|
+
return line.match(/at (\/[^\s]+)/)?.[1];
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/runtime-types/PersistedRuntimeTypeMetadataStoreRegistry.ts
|
|
216
|
+
/**
|
|
217
|
+
* Defines and retrieves persisted runtime metadata on decorated classes.
|
|
218
|
+
* The metadata is attached as a non-enumerable property so runtime objects stay serializable.
|
|
219
|
+
*/
|
|
220
|
+
var PersistedRuntimeTypeMetadataStore = class {
|
|
221
|
+
static define(target, kind, options, decoratorFileUrl) {
|
|
222
|
+
const normalizedOptions = PersistedRuntimeTypeDecoratorDefaults.apply(options);
|
|
223
|
+
const metadata = {
|
|
224
|
+
persistedName: PersistedRuntimeTypeNameResolver.resolve(target, normalizedOptions.name),
|
|
225
|
+
kind,
|
|
226
|
+
packageName: normalizedOptions.packageName ?? PersistedRuntimeTypeDecoratorDefaults.appPackageName,
|
|
227
|
+
sourceHint: normalizedOptions.moduleUrl ?? StackTraceCallSitePathResolver.resolve(decoratorFileUrl)
|
|
228
|
+
};
|
|
229
|
+
Object.defineProperty(target, persistedRuntimeTypeMetadataKey, {
|
|
230
|
+
configurable: false,
|
|
231
|
+
enumerable: false,
|
|
232
|
+
writable: false,
|
|
233
|
+
value: metadata
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
static get(target) {
|
|
237
|
+
if (!target || typeof target !== "function" && typeof target !== "object") return;
|
|
238
|
+
return target[persistedRuntimeTypeMetadataKey];
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/runtime-types/InjectableRuntimeDecoratorComposerRegistry.ts
|
|
244
|
+
/**
|
|
245
|
+
* Applies both tsyringe injectability and persisted runtime metadata in one decorator.
|
|
246
|
+
* This keeps runtime-type decorators thin while still recording enough data for snapshot hydration.
|
|
247
|
+
*/
|
|
248
|
+
var InjectableRuntimeDecoratorComposer = class {
|
|
249
|
+
static compose(kind, options, decoratorFileUrl) {
|
|
250
|
+
return (target) => {
|
|
251
|
+
(0, tsyringe.injectable)()(target);
|
|
252
|
+
PersistedRuntimeTypeMetadataStore.define(target, kind, options, decoratorFileUrl);
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/runtime-types/runtimeTypeDecorators.types.ts
|
|
259
|
+
/** Reads persisted runtime metadata from a decorated class or object. */
|
|
260
|
+
function getPersistedRuntimeTypeMetadata(target) {
|
|
261
|
+
return PersistedRuntimeTypeMetadataStore.get(target);
|
|
262
|
+
}
|
|
263
|
+
/** Marks a class as a persisted node runtime type and an injectable tsyringe service. */
|
|
264
|
+
function node(options = {}) {
|
|
265
|
+
return InjectableRuntimeDecoratorComposer.compose("node", options, require("url").pathToFileURL(__filename).href);
|
|
266
|
+
}
|
|
267
|
+
/** Marks a class as a persisted tool runtime type and an injectable tsyringe service. */
|
|
268
|
+
function tool(options = {}) {
|
|
269
|
+
return InjectableRuntimeDecoratorComposer.compose("tool", options, require("url").pathToFileURL(__filename).href);
|
|
270
|
+
}
|
|
271
|
+
/** Marks a class as a persisted chat-model runtime type and an injectable tsyringe service. */
|
|
272
|
+
function chatModel(options = {}) {
|
|
273
|
+
return InjectableRuntimeDecoratorComposer.compose("chatModel", options, require("url").pathToFileURL(__filename).href);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/binaries/DefaultNodeBinaryAttachmentServiceFactory.ts
|
|
278
|
+
var DefaultNodeBinaryAttachmentService = class DefaultNodeBinaryAttachmentService {
|
|
279
|
+
constructor(storage, workflowId, runId, nodeId, activationId, now) {
|
|
280
|
+
this.storage = storage;
|
|
281
|
+
this.workflowId = workflowId;
|
|
282
|
+
this.runId = runId;
|
|
283
|
+
this.nodeId = nodeId;
|
|
284
|
+
this.activationId = activationId;
|
|
285
|
+
this.now = now;
|
|
286
|
+
}
|
|
287
|
+
async attach(args) {
|
|
288
|
+
const attachmentId = this.createAttachmentId();
|
|
289
|
+
const createdAt = this.now().toISOString();
|
|
290
|
+
const storageKey = this.createStorageKey(args, attachmentId);
|
|
291
|
+
const stored = await this.storage.write({
|
|
292
|
+
storageKey,
|
|
293
|
+
body: args.body
|
|
294
|
+
});
|
|
295
|
+
return {
|
|
296
|
+
id: attachmentId,
|
|
297
|
+
storageKey: stored.storageKey,
|
|
298
|
+
mimeType: args.mimeType,
|
|
299
|
+
size: stored.size,
|
|
300
|
+
storageDriver: this.storage.driverName,
|
|
301
|
+
previewKind: args.previewKind ?? this.resolvePreviewKind(args.mimeType),
|
|
302
|
+
createdAt,
|
|
303
|
+
runId: this.runId,
|
|
304
|
+
workflowId: this.workflowId,
|
|
305
|
+
nodeId: this.nodeId,
|
|
306
|
+
activationId: this.activationId,
|
|
307
|
+
filename: args.filename,
|
|
308
|
+
sha256: stored.sha256
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
withAttachment(item, name, attachment) {
|
|
312
|
+
return {
|
|
313
|
+
...item,
|
|
314
|
+
binary: {
|
|
315
|
+
...item.binary ?? {},
|
|
316
|
+
[name]: attachment
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
forNode(args) {
|
|
321
|
+
return new DefaultNodeBinaryAttachmentService(this.storage, this.workflowId, this.runId, args.nodeId, args.activationId, this.now);
|
|
322
|
+
}
|
|
323
|
+
async openReadStream(attachment) {
|
|
324
|
+
return await this.storage.openReadStream(attachment.storageKey);
|
|
325
|
+
}
|
|
326
|
+
createAttachmentId() {
|
|
327
|
+
return DefaultNodeBinaryAttachmentService.createAttachmentIdValue(`${this.activationId}-${this.now().getTime()}`);
|
|
328
|
+
}
|
|
329
|
+
static createAttachmentIdValue(fallbackValue) {
|
|
330
|
+
const cryptoObject = globalThis.crypto;
|
|
331
|
+
if (cryptoObject && typeof cryptoObject.randomUUID === "function") return cryptoObject.randomUUID();
|
|
332
|
+
return fallbackValue;
|
|
333
|
+
}
|
|
334
|
+
createStorageKey(args, attachmentId) {
|
|
335
|
+
const safeName = this.sanitizeSegment(args.name);
|
|
336
|
+
const safeFilename = this.sanitizeFilename(args.filename);
|
|
337
|
+
const filenameSuffix = safeFilename ? `-${safeFilename}` : "";
|
|
338
|
+
return `${this.sanitizeSegment(this.workflowId)}/${this.sanitizeSegment(this.runId)}/${this.sanitizeSegment(this.nodeId)}/${this.sanitizeSegment(this.activationId)}/${attachmentId}-${safeName}${filenameSuffix}`;
|
|
339
|
+
}
|
|
340
|
+
sanitizeSegment(value) {
|
|
341
|
+
const normalized = value.trim();
|
|
342
|
+
if (!normalized) return "item";
|
|
343
|
+
return normalized.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
344
|
+
}
|
|
345
|
+
sanitizeFilename(value) {
|
|
346
|
+
if (!value) return;
|
|
347
|
+
const normalized = value.trim().split("/").at(-1)?.split("\\").at(-1) ?? value.trim();
|
|
348
|
+
if (!normalized) return;
|
|
349
|
+
return normalized.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
350
|
+
}
|
|
351
|
+
resolvePreviewKind(mimeType) {
|
|
352
|
+
if (mimeType.startsWith("image/")) return "image";
|
|
353
|
+
if (mimeType.startsWith("audio/")) return "audio";
|
|
354
|
+
if (mimeType.startsWith("video/")) return "video";
|
|
355
|
+
return "download";
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/binaries/UnavailableBinaryStorage.ts
|
|
361
|
+
var UnavailableBinaryStorage = class {
|
|
362
|
+
driverName = "unavailable";
|
|
363
|
+
async write() {
|
|
364
|
+
throw new Error("Binary storage is not configured for this runtime.");
|
|
365
|
+
}
|
|
366
|
+
async openReadStream() {}
|
|
367
|
+
async stat() {
|
|
368
|
+
return { exists: false };
|
|
369
|
+
}
|
|
370
|
+
async delete() {}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region src/binaries/DefaultExecutionBinaryServiceFactory.ts
|
|
375
|
+
var DefaultExecutionBinaryService = class {
|
|
376
|
+
constructor(storage, workflowId, runId, now) {
|
|
377
|
+
this.storage = storage;
|
|
378
|
+
this.workflowId = workflowId;
|
|
379
|
+
this.runId = runId;
|
|
380
|
+
this.now = now;
|
|
381
|
+
}
|
|
382
|
+
forNode(args) {
|
|
383
|
+
return new DefaultNodeBinaryAttachmentService(this.storage, this.workflowId, this.runId, args.nodeId, args.activationId, this.now);
|
|
384
|
+
}
|
|
385
|
+
async openReadStream(attachment) {
|
|
386
|
+
return await this.storage.openReadStream(attachment.storageKey);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/execution/NodeExecutionSnapshotFactory.ts
|
|
392
|
+
var NodeExecutionSnapshotFactory = class {
|
|
393
|
+
static queued(args) {
|
|
394
|
+
return {
|
|
395
|
+
runId: args.runId,
|
|
396
|
+
workflowId: args.workflowId,
|
|
397
|
+
nodeId: args.nodeId,
|
|
398
|
+
activationId: args.activationId,
|
|
399
|
+
parent: args.parent,
|
|
400
|
+
status: "queued",
|
|
401
|
+
queuedAt: args.queuedAt,
|
|
402
|
+
updatedAt: args.queuedAt,
|
|
403
|
+
inputsByPort: args.inputsByPort
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
static running(args) {
|
|
407
|
+
return {
|
|
408
|
+
runId: args.runId,
|
|
409
|
+
workflowId: args.workflowId,
|
|
410
|
+
nodeId: args.nodeId,
|
|
411
|
+
activationId: args.activationId,
|
|
412
|
+
parent: args.parent,
|
|
413
|
+
status: "running",
|
|
414
|
+
queuedAt: args.previous?.queuedAt,
|
|
415
|
+
startedAt: args.startedAt,
|
|
416
|
+
updatedAt: args.startedAt,
|
|
417
|
+
inputsByPort: args.inputsByPort,
|
|
418
|
+
outputs: args.previous?.outputs,
|
|
419
|
+
error: void 0
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
static completed(args) {
|
|
423
|
+
const fromPinnedOutput = args.fromPinnedOutput ?? false;
|
|
424
|
+
const startedAt = fromPinnedOutput ? args.previous?.startedAt ?? args.finishedAt : args.previous?.startedAt;
|
|
425
|
+
return {
|
|
426
|
+
runId: args.runId,
|
|
427
|
+
workflowId: args.workflowId,
|
|
428
|
+
nodeId: args.nodeId,
|
|
429
|
+
activationId: args.activationId,
|
|
430
|
+
parent: args.parent,
|
|
431
|
+
status: "completed",
|
|
432
|
+
queuedAt: args.previous?.queuedAt,
|
|
433
|
+
startedAt,
|
|
434
|
+
finishedAt: args.finishedAt,
|
|
435
|
+
updatedAt: args.finishedAt,
|
|
436
|
+
inputsByPort: args.inputsByPort,
|
|
437
|
+
outputs: args.outputs,
|
|
438
|
+
usedPinnedOutput: fromPinnedOutput,
|
|
439
|
+
error: void 0
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
static skipped(args) {
|
|
443
|
+
return {
|
|
444
|
+
runId: args.runId,
|
|
445
|
+
workflowId: args.workflowId,
|
|
446
|
+
nodeId: args.nodeId,
|
|
447
|
+
activationId: args.activationId,
|
|
448
|
+
parent: args.parent,
|
|
449
|
+
status: "skipped",
|
|
450
|
+
queuedAt: args.previous?.queuedAt,
|
|
451
|
+
startedAt: args.previous?.startedAt,
|
|
452
|
+
finishedAt: args.finishedAt,
|
|
453
|
+
updatedAt: args.finishedAt,
|
|
454
|
+
inputsByPort: args.inputsByPort,
|
|
455
|
+
outputs: args.outputs,
|
|
456
|
+
error: void 0
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
static failed(args) {
|
|
460
|
+
return {
|
|
461
|
+
runId: args.runId,
|
|
462
|
+
workflowId: args.workflowId,
|
|
463
|
+
nodeId: args.nodeId,
|
|
464
|
+
activationId: args.activationId,
|
|
465
|
+
parent: args.parent,
|
|
466
|
+
status: "failed",
|
|
467
|
+
queuedAt: args.previous?.queuedAt,
|
|
468
|
+
startedAt: args.previous?.startedAt,
|
|
469
|
+
finishedAt: args.finishedAt,
|
|
470
|
+
updatedAt: args.finishedAt,
|
|
471
|
+
inputsByPort: args.inputsByPort,
|
|
472
|
+
outputs: void 0,
|
|
473
|
+
error: {
|
|
474
|
+
message: args.error.message,
|
|
475
|
+
name: args.error.name,
|
|
476
|
+
stack: args.error.stack
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
//#endregion
|
|
483
|
+
//#region src/execution/NodeInputsByPortFactory.ts
|
|
484
|
+
var NodeInputsByPortFactory = class {
|
|
485
|
+
static empty() {
|
|
486
|
+
return {};
|
|
487
|
+
}
|
|
488
|
+
static fromRequest(request) {
|
|
489
|
+
if (request.kind === "multi") return request.inputsByPort;
|
|
490
|
+
return { in: request.input };
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/execution/ActivationEnqueueService.ts
|
|
496
|
+
var ActivationEnqueueService = class {
|
|
497
|
+
constructor(activationScheduler, workflowExecutionRepository, nodeEventPublisher) {
|
|
498
|
+
this.activationScheduler = activationScheduler;
|
|
499
|
+
this.workflowExecutionRepository = workflowExecutionRepository;
|
|
500
|
+
this.nodeEventPublisher = nodeEventPublisher;
|
|
501
|
+
}
|
|
502
|
+
async enqueueActivation(args) {
|
|
503
|
+
const { result, queuedSnapshot } = await this.enqueueActivationWithSnapshot(args);
|
|
504
|
+
await this.nodeEventPublisher.publish("nodeQueued", queuedSnapshot);
|
|
505
|
+
return result;
|
|
506
|
+
}
|
|
507
|
+
async enqueueActivationWithSnapshot(args) {
|
|
508
|
+
const receipt = await this.activationScheduler.enqueue(args.request);
|
|
509
|
+
const inputsByPort = NodeInputsByPortFactory.fromRequest(args.request);
|
|
510
|
+
const itemsIn = args.request.kind === "multi" ? args.planner.sumItemsByPort(args.request.inputsByPort) : args.request.input.length;
|
|
511
|
+
const enqueuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
512
|
+
const pending = {
|
|
513
|
+
runId: args.runId,
|
|
514
|
+
activationId: args.request.activationId,
|
|
515
|
+
workflowId: args.workflowId,
|
|
516
|
+
nodeId: args.request.nodeId,
|
|
517
|
+
itemsIn,
|
|
518
|
+
inputsByPort,
|
|
519
|
+
receiptId: receipt.receiptId,
|
|
520
|
+
queue: receipt.queue,
|
|
521
|
+
batchId: args.request.batchId,
|
|
522
|
+
enqueuedAt
|
|
523
|
+
};
|
|
524
|
+
const queuedSnapshot = NodeExecutionSnapshotFactory.queued({
|
|
525
|
+
runId: args.runId,
|
|
526
|
+
workflowId: args.workflowId,
|
|
527
|
+
nodeId: args.request.nodeId,
|
|
528
|
+
activationId: args.request.activationId,
|
|
529
|
+
parent: args.parent,
|
|
530
|
+
queuedAt: enqueuedAt,
|
|
531
|
+
inputsByPort
|
|
532
|
+
});
|
|
533
|
+
await this.workflowExecutionRepository.save({
|
|
534
|
+
runId: args.runId,
|
|
535
|
+
workflowId: args.workflowId,
|
|
536
|
+
startedAt: args.startedAt,
|
|
537
|
+
parent: args.parent,
|
|
538
|
+
executionOptions: args.executionOptions,
|
|
539
|
+
control: args.control,
|
|
540
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
541
|
+
mutableState: args.mutableState,
|
|
542
|
+
policySnapshot: args.policySnapshot,
|
|
543
|
+
engineCounters: args.engineCounters,
|
|
544
|
+
connectionInvocations: args.connectionInvocations ? [...args.connectionInvocations] : [],
|
|
545
|
+
status: "pending",
|
|
546
|
+
pending,
|
|
547
|
+
queue: args.pendingQueue.map((entry) => ({ ...entry })),
|
|
548
|
+
outputsByNode: args.request.ctx.data.dump(),
|
|
549
|
+
nodeSnapshotsByNodeId: {
|
|
550
|
+
...args.previousNodeSnapshotsByNodeId,
|
|
551
|
+
[args.request.nodeId]: queuedSnapshot
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
this.notifyPendingStatePersisted(args.runId);
|
|
555
|
+
return {
|
|
556
|
+
result: {
|
|
557
|
+
runId: args.runId,
|
|
558
|
+
workflowId: args.workflowId,
|
|
559
|
+
startedAt: args.startedAt,
|
|
560
|
+
status: "pending",
|
|
561
|
+
pending
|
|
562
|
+
},
|
|
563
|
+
queuedSnapshot
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
notifyPendingStatePersisted(runId) {
|
|
567
|
+
this.activationScheduler.notifyPendingStatePersisted?.(runId);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
//#endregion
|
|
572
|
+
//#region src/execution/CredentialResolverFactory.ts
|
|
573
|
+
var CredentialResolverFactory = class {
|
|
574
|
+
constructor(credentialSessions) {
|
|
575
|
+
this.credentialSessions = credentialSessions;
|
|
576
|
+
}
|
|
577
|
+
create(workflowId, nodeId, config) {
|
|
578
|
+
const acceptedTypesBySlot = /* @__PURE__ */ new Map();
|
|
579
|
+
for (const requirement of config?.getCredentialRequirements?.() ?? []) acceptedTypesBySlot.set(requirement.slotKey, requirement.acceptedTypes);
|
|
580
|
+
return async (slotKey) => {
|
|
581
|
+
try {
|
|
582
|
+
return await this.credentialSessions.getSession({
|
|
583
|
+
workflowId,
|
|
584
|
+
nodeId,
|
|
585
|
+
slotKey
|
|
586
|
+
});
|
|
587
|
+
} catch (error) {
|
|
588
|
+
const acceptedTypes = acceptedTypesBySlot.get(slotKey) ?? [];
|
|
589
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
590
|
+
const alreadyListsAcceptedTypes = message.includes("Accepted types:") || message.includes("Accepted credential types:") || message.includes("binding points at an unknown type");
|
|
591
|
+
const acceptedTypesSuffix = acceptedTypes.length > 0 && !alreadyListsAcceptedTypes ? ` Accepted types: ${acceptedTypes.join(", ")}.` : "";
|
|
592
|
+
throw new Error(`Failed to resolve credential for workflow ${workflowId} node ${nodeId} slot "${slotKey}". ${message}${acceptedTypesSuffix}`, { cause: error });
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
//#endregion
|
|
599
|
+
//#region src/execution/DefaultAsyncSleeper.ts
|
|
600
|
+
var DefaultAsyncSleeper = class {
|
|
601
|
+
sleep(ms) {
|
|
602
|
+
return new Promise((resolve) => {
|
|
603
|
+
setTimeout(resolve, ms);
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
//#endregion
|
|
609
|
+
//#region src/execution/DefaultExecutionContextFactory.ts
|
|
610
|
+
var DefaultExecutionContextFactory = class {
|
|
611
|
+
constructor(binaryStorage = new UnavailableBinaryStorage(), currentDate = () => /* @__PURE__ */ new Date()) {
|
|
612
|
+
this.binaryStorage = binaryStorage;
|
|
613
|
+
this.currentDate = currentDate;
|
|
614
|
+
}
|
|
615
|
+
create(args) {
|
|
616
|
+
return {
|
|
617
|
+
runId: args.runId,
|
|
618
|
+
workflowId: args.workflowId,
|
|
619
|
+
parent: args.parent,
|
|
620
|
+
subworkflowDepth: args.subworkflowDepth,
|
|
621
|
+
engineMaxNodeActivations: args.engineMaxNodeActivations,
|
|
622
|
+
engineMaxSubworkflowDepth: args.engineMaxSubworkflowDepth,
|
|
623
|
+
now: this.currentDate,
|
|
624
|
+
data: args.data,
|
|
625
|
+
nodeState: args.nodeState,
|
|
626
|
+
binary: new DefaultExecutionBinaryService(this.binaryStorage, args.workflowId, args.runId, this.currentDate),
|
|
627
|
+
getCredential: args.getCredential
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
//#endregion
|
|
633
|
+
//#region src/execution/InProcessRetryRunner.ts
|
|
634
|
+
var InProcessRetryRunner = class InProcessRetryRunner {
|
|
635
|
+
constructor(sleeper) {
|
|
636
|
+
this.sleeper = sleeper;
|
|
637
|
+
}
|
|
638
|
+
async run(policy, work) {
|
|
639
|
+
const spec = InProcessRetryRunner.normalizePolicy(policy);
|
|
640
|
+
let lastError;
|
|
641
|
+
for (let attempt = 1; attempt <= spec.maxAttempts; attempt++) try {
|
|
642
|
+
return await work();
|
|
643
|
+
} catch (error) {
|
|
644
|
+
lastError = error;
|
|
645
|
+
if (attempt >= spec.maxAttempts) break;
|
|
646
|
+
const delayMs = InProcessRetryRunner.delayAfterFailureMs(spec, attempt);
|
|
647
|
+
await this.sleeper.sleep(delayMs);
|
|
648
|
+
}
|
|
649
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
650
|
+
}
|
|
651
|
+
static delayAfterFailureMs(spec, failedAttempt) {
|
|
652
|
+
if (spec.kind === "none") return 0;
|
|
653
|
+
if (spec.kind === "fixed") return spec.delayMs;
|
|
654
|
+
const exponent = failedAttempt - 1;
|
|
655
|
+
let ms = spec.initialDelayMs * Math.pow(spec.multiplier, exponent);
|
|
656
|
+
if (spec.jitter === true) ms *= 1 + Math.random() * .2;
|
|
657
|
+
if (spec.maxDelayMs !== void 0 && ms > spec.maxDelayMs) ms = spec.maxDelayMs;
|
|
658
|
+
return Math.max(0, Math.floor(ms));
|
|
659
|
+
}
|
|
660
|
+
static normalizePolicy(policy) {
|
|
661
|
+
if (policy === void 0) return {
|
|
662
|
+
kind: "none",
|
|
663
|
+
maxAttempts: 1
|
|
664
|
+
};
|
|
665
|
+
if (typeof policy !== "object" || policy === null) return {
|
|
666
|
+
kind: "none",
|
|
667
|
+
maxAttempts: 1
|
|
668
|
+
};
|
|
669
|
+
const kind = policy.kind;
|
|
670
|
+
if (kind === "none") return {
|
|
671
|
+
kind: "none",
|
|
672
|
+
maxAttempts: 1
|
|
673
|
+
};
|
|
674
|
+
if (kind === "fixed") {
|
|
675
|
+
const p = policy;
|
|
676
|
+
return {
|
|
677
|
+
kind: "fixed",
|
|
678
|
+
maxAttempts: InProcessRetryRunner.assertPositiveInt(p.maxAttempts, "fixed.maxAttempts"),
|
|
679
|
+
delayMs: InProcessRetryRunner.assertNonNegativeFinite(p.delayMs, "fixed.delayMs")
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
if (kind === "exponential") {
|
|
683
|
+
const p = policy;
|
|
684
|
+
return {
|
|
685
|
+
kind: "exponential",
|
|
686
|
+
maxAttempts: InProcessRetryRunner.assertPositiveInt(p.maxAttempts, "exponential.maxAttempts"),
|
|
687
|
+
initialDelayMs: InProcessRetryRunner.assertNonNegativeFinite(p.initialDelayMs, "exponential.initialDelayMs"),
|
|
688
|
+
multiplier: InProcessRetryRunner.assertMultiplier(p.multiplier),
|
|
689
|
+
maxDelayMs: p.maxDelayMs === void 0 ? void 0 : InProcessRetryRunner.assertNonNegativeFinite(p.maxDelayMs, "exponential.maxDelayMs"),
|
|
690
|
+
jitter: p.jitter === true
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
return {
|
|
694
|
+
kind: "none",
|
|
695
|
+
maxAttempts: 1
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
static assertPositiveInt(value, label) {
|
|
699
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1 || !Number.isInteger(value)) throw new Error(`Retry policy ${label} must be a positive integer`);
|
|
700
|
+
return value;
|
|
701
|
+
}
|
|
702
|
+
static assertNonNegativeFinite(value, label) {
|
|
703
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new Error(`Retry policy ${label} must be a non-negative finite number`);
|
|
704
|
+
return value;
|
|
705
|
+
}
|
|
706
|
+
static assertMultiplier(value) {
|
|
707
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) throw new Error(`Retry policy exponential.multiplier must be >= 1`);
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region src/execution/InProcessRetryRunnerFactory.ts
|
|
714
|
+
var InProcessRetryRunnerFactory = class {
|
|
715
|
+
create(defaultAsyncSleeper) {
|
|
716
|
+
return new InProcessRetryRunner(defaultAsyncSleeper);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
//#endregion
|
|
721
|
+
//#region src/execution/NodeActivationRequestComposer.ts
|
|
722
|
+
/**
|
|
723
|
+
* Builds {@link NodeActivationRequest} values shared by workflow starters and continuation.
|
|
724
|
+
*/
|
|
725
|
+
var NodeActivationRequestComposer = class {
|
|
726
|
+
constructor(activationIdFactory, credentialResolverFactory) {
|
|
727
|
+
this.activationIdFactory = activationIdFactory;
|
|
728
|
+
this.credentialResolverFactory = credentialResolverFactory;
|
|
729
|
+
}
|
|
730
|
+
createSingleFromDefinition(args) {
|
|
731
|
+
return this.createSingleFromDefinitionWithActivation({
|
|
732
|
+
...args,
|
|
733
|
+
activationId: this.activationIdFactory.makeActivationId()
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
createSingleFromDefinitionWithActivation(args) {
|
|
737
|
+
const ctx = this.createNodeExecutionContext(args, args.definition, args.activationId);
|
|
738
|
+
return {
|
|
739
|
+
kind: "single",
|
|
740
|
+
runId: args.runId,
|
|
741
|
+
activationId: args.activationId,
|
|
742
|
+
workflowId: args.workflowId,
|
|
743
|
+
nodeId: args.definition.id,
|
|
744
|
+
parent: args.parent,
|
|
745
|
+
executionOptions: args.executionOptions,
|
|
746
|
+
batchId: args.batchId,
|
|
747
|
+
input: args.input,
|
|
748
|
+
ctx
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
createFromPlannedActivation(args) {
|
|
752
|
+
const activationId = this.activationIdFactory.makeActivationId();
|
|
753
|
+
const ctx = this.createNodeExecutionContext(args, args.nodeDefinition, activationId);
|
|
754
|
+
if (args.next.kind === "multi") return {
|
|
755
|
+
kind: "multi",
|
|
756
|
+
runId: args.runId,
|
|
757
|
+
activationId,
|
|
758
|
+
workflowId: args.workflowId,
|
|
759
|
+
nodeId: args.nodeDefinition.id,
|
|
760
|
+
parent: args.parent,
|
|
761
|
+
executionOptions: args.executionOptions,
|
|
762
|
+
batchId: args.next.batchId,
|
|
763
|
+
inputsByPort: args.next.inputsByPort,
|
|
764
|
+
ctx
|
|
765
|
+
};
|
|
766
|
+
return {
|
|
767
|
+
kind: "single",
|
|
768
|
+
runId: args.runId,
|
|
769
|
+
activationId,
|
|
770
|
+
workflowId: args.workflowId,
|
|
771
|
+
nodeId: args.nodeDefinition.id,
|
|
772
|
+
parent: args.parent,
|
|
773
|
+
executionOptions: args.executionOptions,
|
|
774
|
+
batchId: args.next.batchId,
|
|
775
|
+
input: args.next.input,
|
|
776
|
+
ctx
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
createNodeExecutionContext(args, definition, activationId) {
|
|
780
|
+
return {
|
|
781
|
+
...args.base,
|
|
782
|
+
data: args.data,
|
|
783
|
+
nodeId: definition.id,
|
|
784
|
+
activationId,
|
|
785
|
+
config: definition.config,
|
|
786
|
+
binary: args.base.binary.forNode({
|
|
787
|
+
nodeId: definition.id,
|
|
788
|
+
activationId
|
|
789
|
+
}),
|
|
790
|
+
getCredential: this.credentialResolverFactory.create(args.workflowId, definition.id, definition.config)
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
//#endregion
|
|
796
|
+
//#region src/execution/NodeExecutor.ts
|
|
797
|
+
var NodeExecutor = class {
|
|
798
|
+
constructor(nodeInstanceFactory, retryRunner) {
|
|
799
|
+
this.nodeInstanceFactory = nodeInstanceFactory;
|
|
800
|
+
this.retryRunner = retryRunner;
|
|
801
|
+
}
|
|
802
|
+
async execute(request) {
|
|
803
|
+
const policy = request.ctx.config.retryPolicy;
|
|
804
|
+
return await this.retryRunner.run(policy, async () => {
|
|
805
|
+
const nodeInstance = this.nodeInstanceFactory.createByType(request.ctx.config.type);
|
|
806
|
+
if (request.kind === "multi") return await this.executeMultiInputNode(request, nodeInstance);
|
|
807
|
+
return await this.executeSingleInputNode(request, nodeInstance);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
async executeMultiInputNode(request, node$1) {
|
|
811
|
+
const multiInputNode = node$1;
|
|
812
|
+
if (typeof multiInputNode.executeMulti !== "function") throw new Error(`Node ${request.nodeId} does not support executeMulti but received multi-input activation`);
|
|
813
|
+
return await multiInputNode.executeMulti(request.inputsByPort, request.ctx);
|
|
814
|
+
}
|
|
815
|
+
async executeSingleInputNode(request, node$1) {
|
|
816
|
+
const singleInputNode = node$1;
|
|
817
|
+
if (typeof singleInputNode.execute !== "function") throw new Error(`Node ${request.nodeId} does not support execute but received single-input activation`);
|
|
818
|
+
return await singleInputNode.execute(request.input, request.ctx);
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
//#endregion
|
|
823
|
+
//#region src/execution/NodeExecutorFactory.ts
|
|
824
|
+
var NodeExecutorFactory = class {
|
|
825
|
+
create(workflowNodeInstanceFactory, retryRunner) {
|
|
826
|
+
return new NodeExecutor(workflowNodeInstanceFactory, retryRunner);
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region src/workflowSnapshots/MissingRuntimeExecutionMarker.ts
|
|
832
|
+
var MissingRuntimeExecutionMarker = class {
|
|
833
|
+
isMarked(config) {
|
|
834
|
+
return Boolean(config?.missingRuntime);
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/workflowSnapshots/MissingRuntimeNodeToken.ts
|
|
840
|
+
var MissingRuntimeNodeToken = class {};
|
|
841
|
+
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region src/workflowSnapshots/MissingRuntimeNodeConfig.ts
|
|
844
|
+
var MissingRuntimeNodeConfig = class {
|
|
845
|
+
kind = "node";
|
|
846
|
+
type = MissingRuntimeNodeToken;
|
|
847
|
+
constructor(name, missingTokenId, missingRuntime = true) {
|
|
848
|
+
this.name = name;
|
|
849
|
+
this.missingTokenId = missingTokenId;
|
|
850
|
+
this.missingRuntime = missingRuntime;
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
//#endregion
|
|
855
|
+
//#region src/workflowSnapshots/MissingRuntimeTriggerToken.ts
|
|
856
|
+
var MissingRuntimeTriggerToken = class {};
|
|
857
|
+
|
|
858
|
+
//#endregion
|
|
859
|
+
//#region src/workflowSnapshots/MissingRuntimeTriggerConfig.ts
|
|
860
|
+
var MissingRuntimeTriggerConfig = class {
|
|
861
|
+
kind = "trigger";
|
|
862
|
+
type = MissingRuntimeTriggerToken;
|
|
863
|
+
constructor(name, missingTokenId, missingRuntime = true) {
|
|
864
|
+
this.name = name;
|
|
865
|
+
this.missingTokenId = missingTokenId;
|
|
866
|
+
this.missingRuntime = missingRuntime;
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
//#endregion
|
|
871
|
+
//#region src/workflowSnapshots/MissingRuntimeFallbacksFactory.ts
|
|
872
|
+
var MissingRuntimeFallbacks = class {
|
|
873
|
+
createDefinition(snapshotNode) {
|
|
874
|
+
if (snapshotNode.kind === "trigger") return {
|
|
875
|
+
id: snapshotNode.id,
|
|
876
|
+
kind: "trigger",
|
|
877
|
+
name: snapshotNode.name,
|
|
878
|
+
type: MissingRuntimeTriggerToken,
|
|
879
|
+
config: new MissingRuntimeTriggerConfig(snapshotNode.name ?? snapshotNode.id, snapshotNode.nodeTokenId)
|
|
880
|
+
};
|
|
881
|
+
return {
|
|
882
|
+
id: snapshotNode.id,
|
|
883
|
+
kind: "node",
|
|
884
|
+
name: snapshotNode.name,
|
|
885
|
+
type: MissingRuntimeNodeToken,
|
|
886
|
+
config: new MissingRuntimeNodeConfig(snapshotNode.name ?? snapshotNode.id, snapshotNode.nodeTokenId)
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/workflowSnapshots/MissingRuntimeNode.ts
|
|
893
|
+
var MissingRuntimeNode = class {
|
|
894
|
+
kind = "node";
|
|
895
|
+
outputPorts = ["main"];
|
|
896
|
+
async execute(items) {
|
|
897
|
+
return { main: items };
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
//#endregion
|
|
902
|
+
//#region src/workflowSnapshots/MissingRuntimeTrigger.ts
|
|
903
|
+
var MissingRuntimeTrigger = class {
|
|
904
|
+
kind = "trigger";
|
|
905
|
+
outputPorts = ["main"];
|
|
906
|
+
async setup(_ctx) {}
|
|
907
|
+
async execute(items) {
|
|
908
|
+
return { main: items };
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
//#endregion
|
|
913
|
+
//#region src/workflowSnapshots/PersistedRuntimeTypeIdFactory.ts
|
|
914
|
+
var PersistedRuntimeTypeIdFactory = class {
|
|
915
|
+
static fromMetadata(args) {
|
|
916
|
+
const metadata = getPersistedRuntimeTypeMetadata(args.type);
|
|
917
|
+
if (!metadata) return;
|
|
918
|
+
const packageName = metadata.packageName;
|
|
919
|
+
if (!packageName) return;
|
|
920
|
+
return `${packageName}::${metadata.persistedName}`;
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
//#endregion
|
|
925
|
+
//#region src/workflowSnapshots/PersistedWorkflowTokenRegistry.ts
|
|
926
|
+
var PersistedWorkflowTokenRegistry = class {
|
|
927
|
+
tokensById = /* @__PURE__ */ new Map();
|
|
928
|
+
tokenIdsByToken = /* @__PURE__ */ new Map();
|
|
929
|
+
/**
|
|
930
|
+
* Register a token with its package ID. Token ID is inferred as `packageId::tokenName`.
|
|
931
|
+
*/
|
|
932
|
+
register(type, packageId, persistedNameOverride) {
|
|
933
|
+
const tokenId = `${packageId}::${persistedNameOverride ?? this.displayNameForTypeToken(type)}`;
|
|
934
|
+
this.tokensById.set(tokenId, type);
|
|
935
|
+
this.tokenIdsByToken.set(type, tokenId);
|
|
936
|
+
return tokenId;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Register all decorated runtime types discovered in workflows.
|
|
940
|
+
*/
|
|
941
|
+
registerFromWorkflows(workflows) {
|
|
942
|
+
for (const workflow of workflows) for (const node$1 of workflow.nodes) {
|
|
943
|
+
this.registerDecoratedType(node$1.type);
|
|
944
|
+
this.registerDecoratedType(node$1.config.type);
|
|
945
|
+
this.registerNestedTypes(node$1.config);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
registerDecoratedType(type) {
|
|
949
|
+
if (this.tokenIdsByToken.has(type)) return;
|
|
950
|
+
const tokenId = PersistedRuntimeTypeIdFactory.fromMetadata({ type });
|
|
951
|
+
if (!tokenId) return;
|
|
952
|
+
this.tokensById.set(tokenId, type);
|
|
953
|
+
this.tokenIdsByToken.set(type, tokenId);
|
|
954
|
+
}
|
|
955
|
+
registerNestedTypes(value) {
|
|
956
|
+
if (Array.isArray(value)) {
|
|
957
|
+
for (const entry of value) this.registerNestedTypes(entry);
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
if (!value || typeof value !== "object") return;
|
|
961
|
+
const record = value;
|
|
962
|
+
const type = this.asTypeToken(record.type);
|
|
963
|
+
if (type) this.registerDecoratedType(type);
|
|
964
|
+
for (const v of Object.values(record)) this.registerNestedTypes(v);
|
|
965
|
+
}
|
|
966
|
+
displayNameForTypeToken(token) {
|
|
967
|
+
if (typeof token === "function" && token.name) return token.name;
|
|
968
|
+
if (typeof token === "string") return token;
|
|
969
|
+
return "";
|
|
970
|
+
}
|
|
971
|
+
asTypeToken(value) {
|
|
972
|
+
if (typeof value === "function" || typeof value === "string" || typeof value === "symbol") return value;
|
|
973
|
+
}
|
|
974
|
+
getTokenId(token) {
|
|
975
|
+
const existing = this.tokenIdsByToken.get(token);
|
|
976
|
+
if (existing) return existing;
|
|
977
|
+
const tokenId = PersistedRuntimeTypeIdFactory.fromMetadata({ type: token });
|
|
978
|
+
if (!tokenId) return;
|
|
979
|
+
this.tokensById.set(tokenId, token);
|
|
980
|
+
this.tokenIdsByToken.set(token, tokenId);
|
|
981
|
+
return tokenId;
|
|
982
|
+
}
|
|
983
|
+
resolve(tokenId) {
|
|
984
|
+
return this.tokensById.get(tokenId);
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
//#endregion
|
|
989
|
+
//#region src/workflowSnapshots/WorkflowSnapshotResolver.ts
|
|
990
|
+
var WorkflowSnapshotResolver = class {
|
|
991
|
+
constructor(workflowRepository, tokenRegistry, codec, missingRuntimeFallbacks) {
|
|
992
|
+
this.workflowRepository = workflowRepository;
|
|
993
|
+
this.tokenRegistry = tokenRegistry;
|
|
994
|
+
this.codec = codec;
|
|
995
|
+
this.missingRuntimeFallbacks = missingRuntimeFallbacks;
|
|
996
|
+
}
|
|
997
|
+
resolve(args) {
|
|
998
|
+
const liveWorkflow = this.workflowRepository.get(args.workflowId);
|
|
999
|
+
if (!args.workflowSnapshot) return liveWorkflow;
|
|
1000
|
+
if (!liveWorkflow) return this.rebuildWorkflow(args.workflowSnapshot, void 0);
|
|
1001
|
+
return this.rebuildWorkflow(args.workflowSnapshot, liveWorkflow);
|
|
1002
|
+
}
|
|
1003
|
+
rebuildWorkflow(snapshot, liveWorkflow) {
|
|
1004
|
+
const liveNodesById = new Map((liveWorkflow?.nodes ?? []).map((node$1) => [node$1.id, node$1]));
|
|
1005
|
+
const nodes = snapshot.nodes.map((snapshotNode) => {
|
|
1006
|
+
const liveNode = liveNodesById.get(snapshotNode.id);
|
|
1007
|
+
if (!this.isCompatibleLiveNode(liveNode, snapshotNode)) return this.missingRuntimeFallbacks.createDefinition(snapshotNode);
|
|
1008
|
+
return {
|
|
1009
|
+
id: snapshotNode.id,
|
|
1010
|
+
kind: snapshotNode.kind,
|
|
1011
|
+
name: snapshotNode.name ?? liveNode.name,
|
|
1012
|
+
type: liveNode.type,
|
|
1013
|
+
config: this.codec.hydrate(snapshotNode, liveNode.config)
|
|
1014
|
+
};
|
|
1015
|
+
});
|
|
1016
|
+
const nodeIds = new Set(nodes.map((node$1) => node$1.id));
|
|
1017
|
+
const connectionsFromSnapshot = snapshot.connections?.map((connection) => ({
|
|
1018
|
+
...connection,
|
|
1019
|
+
childNodeIds: connection.childNodeIds.filter((childId) => nodeIds.has(childId))
|
|
1020
|
+
})).filter((connection) => connection.childNodeIds.length > 0) ?? [];
|
|
1021
|
+
return {
|
|
1022
|
+
id: snapshot.id,
|
|
1023
|
+
name: snapshot.name,
|
|
1024
|
+
nodes,
|
|
1025
|
+
edges: snapshot.edges.filter((edge) => nodeIds.has(edge.from.nodeId) && nodeIds.has(edge.to.nodeId)),
|
|
1026
|
+
...connectionsFromSnapshot.length > 0 ? { connections: connectionsFromSnapshot } : {},
|
|
1027
|
+
...liveWorkflow?.discoveryPathSegments !== void 0 ? { discoveryPathSegments: liveWorkflow.discoveryPathSegments } : {}
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
isCompatibleLiveNode(liveNode, snapshotNode) {
|
|
1031
|
+
if (!liveNode || liveNode.kind !== snapshotNode.kind) return false;
|
|
1032
|
+
if (!snapshotNode.nodeTokenId || !snapshotNode.configTokenId) throw new Error(`Persisted workflow snapshot node "${snapshotNode.id}" is missing stable token ids.`);
|
|
1033
|
+
const liveNodeTokenId = this.resolveLiveTokenId(liveNode.type);
|
|
1034
|
+
const liveConfigTokenId = this.resolveLiveTokenId(liveNode.config.type);
|
|
1035
|
+
return liveNodeTokenId === snapshotNode.nodeTokenId && liveConfigTokenId === snapshotNode.configTokenId;
|
|
1036
|
+
}
|
|
1037
|
+
resolveLiveTokenId(type) {
|
|
1038
|
+
const registeredTokenId = this.tokenRegistry.getTokenId(type);
|
|
1039
|
+
if (registeredTokenId) return registeredTokenId;
|
|
1040
|
+
if (typeof type === "function" && type.name) return type.name;
|
|
1041
|
+
if (typeof type === "string") return type;
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
//#endregion
|
|
1046
|
+
//#region src/execution/NodeInstanceFactory.ts
|
|
1047
|
+
var NodeInstanceFactory = class {
|
|
1048
|
+
constructor(nodeResolver) {
|
|
1049
|
+
this.nodeResolver = nodeResolver;
|
|
1050
|
+
}
|
|
1051
|
+
createNodes(workflow) {
|
|
1052
|
+
const nodeInstances = /* @__PURE__ */ new Map();
|
|
1053
|
+
for (const definition of workflow.nodes) nodeInstances.set(definition.id, this.createNode(definition));
|
|
1054
|
+
return nodeInstances;
|
|
1055
|
+
}
|
|
1056
|
+
createNode(definition) {
|
|
1057
|
+
return this.createByType(definition.type);
|
|
1058
|
+
}
|
|
1059
|
+
createByType(type) {
|
|
1060
|
+
if (type === MissingRuntimeNodeToken) return new MissingRuntimeNode();
|
|
1061
|
+
if (type === MissingRuntimeTriggerToken) return new MissingRuntimeTrigger();
|
|
1062
|
+
return this.nodeResolver.resolve(type);
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
//#endregion
|
|
1067
|
+
//#region src/execution/NodeInstanceFactoryFactory.ts
|
|
1068
|
+
var NodeInstanceFactoryFactory = class {
|
|
1069
|
+
create(nodeResolver) {
|
|
1070
|
+
return new NodeInstanceFactory(nodeResolver);
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
//#endregion
|
|
1075
|
+
//#region src/execution/NodeRunStateWriter.ts
|
|
1076
|
+
var NodeRunStateWriter = class {
|
|
1077
|
+
chain = Promise.resolve();
|
|
1078
|
+
constructor(workflowExecutionRepository, runId, workflowId, parent, publishNodeEvent) {
|
|
1079
|
+
this.workflowExecutionRepository = workflowExecutionRepository;
|
|
1080
|
+
this.runId = runId;
|
|
1081
|
+
this.workflowId = workflowId;
|
|
1082
|
+
this.parent = parent;
|
|
1083
|
+
this.publishNodeEvent = publishNodeEvent;
|
|
1084
|
+
}
|
|
1085
|
+
markQueued(args) {
|
|
1086
|
+
return this.enqueue(async () => {
|
|
1087
|
+
const state = await this.loadState();
|
|
1088
|
+
const previous = state.nodeSnapshotsByNodeId?.[args.nodeId];
|
|
1089
|
+
const queuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1090
|
+
const snapshot = NodeExecutionSnapshotFactory.queued({
|
|
1091
|
+
runId: this.runId,
|
|
1092
|
+
workflowId: this.workflowId,
|
|
1093
|
+
nodeId: args.nodeId,
|
|
1094
|
+
activationId: args.activationId ?? previous?.activationId ?? `synthetic_${args.nodeId}`,
|
|
1095
|
+
parent: this.parent,
|
|
1096
|
+
queuedAt,
|
|
1097
|
+
inputsByPort: args.inputsByPort ?? previous?.inputsByPort ?? NodeInputsByPortFactory.empty()
|
|
1098
|
+
});
|
|
1099
|
+
await this.saveSnapshot(state, snapshot);
|
|
1100
|
+
await this.publishNodeEvent("nodeQueued", snapshot);
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
markRunning(args) {
|
|
1104
|
+
return this.enqueue(async () => {
|
|
1105
|
+
const state = await this.loadState();
|
|
1106
|
+
const previous = state.nodeSnapshotsByNodeId?.[args.nodeId];
|
|
1107
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1108
|
+
const snapshot = NodeExecutionSnapshotFactory.running({
|
|
1109
|
+
previous,
|
|
1110
|
+
runId: this.runId,
|
|
1111
|
+
workflowId: this.workflowId,
|
|
1112
|
+
nodeId: args.nodeId,
|
|
1113
|
+
activationId: args.activationId ?? previous?.activationId ?? `synthetic_${args.nodeId}`,
|
|
1114
|
+
parent: this.parent,
|
|
1115
|
+
startedAt,
|
|
1116
|
+
inputsByPort: args.inputsByPort ?? previous?.inputsByPort ?? NodeInputsByPortFactory.empty()
|
|
1117
|
+
});
|
|
1118
|
+
await this.saveSnapshot(state, snapshot);
|
|
1119
|
+
await this.publishNodeEvent("nodeStarted", snapshot);
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
markCompleted(args) {
|
|
1123
|
+
return this.enqueue(async () => {
|
|
1124
|
+
const state = await this.loadState();
|
|
1125
|
+
const previous = state.nodeSnapshotsByNodeId?.[args.nodeId];
|
|
1126
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1127
|
+
const snapshot = NodeExecutionSnapshotFactory.completed({
|
|
1128
|
+
previous,
|
|
1129
|
+
runId: this.runId,
|
|
1130
|
+
workflowId: this.workflowId,
|
|
1131
|
+
nodeId: args.nodeId,
|
|
1132
|
+
activationId: args.activationId ?? previous?.activationId ?? `synthetic_${args.nodeId}`,
|
|
1133
|
+
parent: this.parent,
|
|
1134
|
+
finishedAt,
|
|
1135
|
+
inputsByPort: args.inputsByPort ?? previous?.inputsByPort ?? NodeInputsByPortFactory.empty(),
|
|
1136
|
+
outputs: args.outputs ?? previous?.outputs ?? {}
|
|
1137
|
+
});
|
|
1138
|
+
await this.saveSnapshot(state, snapshot);
|
|
1139
|
+
await this.publishNodeEvent("nodeCompleted", snapshot);
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
markFailed(args) {
|
|
1143
|
+
return this.enqueue(async () => {
|
|
1144
|
+
const state = await this.loadState();
|
|
1145
|
+
const previous = state.nodeSnapshotsByNodeId?.[args.nodeId];
|
|
1146
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1147
|
+
const snapshot = NodeExecutionSnapshotFactory.failed({
|
|
1148
|
+
previous,
|
|
1149
|
+
runId: this.runId,
|
|
1150
|
+
workflowId: this.workflowId,
|
|
1151
|
+
nodeId: args.nodeId,
|
|
1152
|
+
activationId: args.activationId ?? previous?.activationId ?? `synthetic_${args.nodeId}`,
|
|
1153
|
+
parent: this.parent,
|
|
1154
|
+
finishedAt,
|
|
1155
|
+
inputsByPort: args.inputsByPort ?? previous?.inputsByPort ?? NodeInputsByPortFactory.empty(),
|
|
1156
|
+
error: args.error
|
|
1157
|
+
});
|
|
1158
|
+
await this.saveSnapshot(state, snapshot);
|
|
1159
|
+
await this.publishNodeEvent("nodeFailed", snapshot);
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
appendConnectionInvocation(args) {
|
|
1163
|
+
return this.enqueue(async () => {
|
|
1164
|
+
const state = await this.loadState();
|
|
1165
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1166
|
+
const record = {
|
|
1167
|
+
invocationId: args.invocationId,
|
|
1168
|
+
runId: this.runId,
|
|
1169
|
+
workflowId: this.workflowId,
|
|
1170
|
+
connectionNodeId: args.connectionNodeId,
|
|
1171
|
+
parentAgentNodeId: args.parentAgentNodeId,
|
|
1172
|
+
parentAgentActivationId: args.parentAgentActivationId,
|
|
1173
|
+
status: args.status,
|
|
1174
|
+
managedInput: args.managedInput,
|
|
1175
|
+
managedOutput: args.managedOutput,
|
|
1176
|
+
error: args.error,
|
|
1177
|
+
queuedAt: args.queuedAt,
|
|
1178
|
+
startedAt: args.startedAt,
|
|
1179
|
+
finishedAt: args.finishedAt,
|
|
1180
|
+
updatedAt
|
|
1181
|
+
};
|
|
1182
|
+
await this.workflowExecutionRepository.save({
|
|
1183
|
+
...state,
|
|
1184
|
+
connectionInvocations: [...state.connectionInvocations ?? [], record]
|
|
1185
|
+
});
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
enqueue(work) {
|
|
1189
|
+
const next = this.chain.then(work);
|
|
1190
|
+
this.chain = next.catch(() => void 0);
|
|
1191
|
+
return next;
|
|
1192
|
+
}
|
|
1193
|
+
async loadState() {
|
|
1194
|
+
const state = await this.workflowExecutionRepository.load(this.runId);
|
|
1195
|
+
if (!state) throw new Error(`Unknown runId: ${this.runId}`);
|
|
1196
|
+
return state;
|
|
1197
|
+
}
|
|
1198
|
+
async saveSnapshot(state, snapshot) {
|
|
1199
|
+
await this.workflowExecutionRepository.save({
|
|
1200
|
+
...state,
|
|
1201
|
+
nodeSnapshotsByNodeId: {
|
|
1202
|
+
...state.nodeSnapshotsByNodeId ?? {},
|
|
1203
|
+
[snapshot.nodeId]: snapshot
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
//#endregion
|
|
1210
|
+
//#region src/execution/NodeRunStateWriterFactory.ts
|
|
1211
|
+
var NodeRunStateWriterFactory = class {
|
|
1212
|
+
constructor(workflowExecutionRepository, nodeEventPublisher) {
|
|
1213
|
+
this.workflowExecutionRepository = workflowExecutionRepository;
|
|
1214
|
+
this.nodeEventPublisher = nodeEventPublisher;
|
|
1215
|
+
}
|
|
1216
|
+
create(runId, workflowId, parent) {
|
|
1217
|
+
return new NodeRunStateWriter(this.workflowExecutionRepository, runId, workflowId, parent, async (kind, snapshot) => {
|
|
1218
|
+
await this.nodeEventPublisher.publish(kind, snapshot);
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region src/execution/PersistedRunStateTerminalBuilder.ts
|
|
1225
|
+
/**
|
|
1226
|
+
* Merges common terminal-run fields onto a loaded {@link PersistedRunState} without repeating object literals.
|
|
1227
|
+
*/
|
|
1228
|
+
var PersistedRunStateTerminalBuilder = class {
|
|
1229
|
+
mergeTerminal(args) {
|
|
1230
|
+
return {
|
|
1231
|
+
...args.state,
|
|
1232
|
+
engineCounters: args.engineCounters,
|
|
1233
|
+
status: args.status,
|
|
1234
|
+
pending: void 0,
|
|
1235
|
+
queue: args.queue,
|
|
1236
|
+
outputsByNode: args.outputsByNode,
|
|
1237
|
+
nodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
//#endregion
|
|
1243
|
+
//#region src/execution/RunStateSemantics.ts
|
|
1244
|
+
var RunStateSemantics = class {
|
|
1245
|
+
constructor(missingRuntimeExecutionMarker) {
|
|
1246
|
+
this.missingRuntimeExecutionMarker = missingRuntimeExecutionMarker;
|
|
1247
|
+
}
|
|
1248
|
+
isStopConditionSatisfied(stopCondition, nodeId) {
|
|
1249
|
+
if (!stopCondition || stopCondition.kind === "workflowCompleted") return false;
|
|
1250
|
+
return stopCondition.nodeId === nodeId;
|
|
1251
|
+
}
|
|
1252
|
+
resolveResultOutputs(workflow, stopCondition, outputsByNode) {
|
|
1253
|
+
if (stopCondition?.kind === "nodeCompleted") return outputsByNode[stopCondition.nodeId]?.main ?? [];
|
|
1254
|
+
return outputsByNode[WorkflowExecutableNodeClassifierFactory.create(workflow).lastExecutableNodeIdInDefinitionOrder(workflow)]?.main ?? [];
|
|
1255
|
+
}
|
|
1256
|
+
applySkippedSnapshots(args) {
|
|
1257
|
+
const snapshots = { ...args.currentState.nodeSnapshotsByNodeId };
|
|
1258
|
+
const skippedPinnedNodeIds = new Set(args.preservedPinnedNodeIds.filter((nodeId) => args.skippedNodeIds.includes(nodeId)));
|
|
1259
|
+
for (const nodeId of args.skippedNodeIds) if (args.currentState.mutableState?.nodesById?.[nodeId]?.pinnedOutputsByPort) skippedPinnedNodeIds.add(nodeId);
|
|
1260
|
+
for (const nodeId of skippedPinnedNodeIds) {
|
|
1261
|
+
const previous = snapshots[nodeId];
|
|
1262
|
+
snapshots[nodeId] = NodeExecutionSnapshotFactory.completed({
|
|
1263
|
+
previous,
|
|
1264
|
+
runId: args.runId,
|
|
1265
|
+
workflowId: args.workflowId,
|
|
1266
|
+
nodeId,
|
|
1267
|
+
activationId: previous?.activationId ?? `synthetic_${nodeId}`,
|
|
1268
|
+
parent: args.parent,
|
|
1269
|
+
finishedAt: args.finishedAt,
|
|
1270
|
+
inputsByPort: previous?.inputsByPort ?? NodeInputsByPortFactory.empty(),
|
|
1271
|
+
outputs: args.currentState.outputsByNode[nodeId] ?? {},
|
|
1272
|
+
fromPinnedOutput: true
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
return snapshots;
|
|
1276
|
+
}
|
|
1277
|
+
applyPinnedQueueSkips(args) {
|
|
1278
|
+
let changed = true;
|
|
1279
|
+
while (changed) {
|
|
1280
|
+
changed = false;
|
|
1281
|
+
for (let index = 0; index < args.queue.length; index += 1) {
|
|
1282
|
+
const queueEntry = args.queue[index];
|
|
1283
|
+
const pinnedOutputs = args.mutableState?.nodesById?.[queueEntry.nodeId]?.pinnedOutputsByPort;
|
|
1284
|
+
if (!pinnedOutputs) continue;
|
|
1285
|
+
args.queue.splice(index, 1);
|
|
1286
|
+
const previous = args.nodeSnapshotsByNodeId[queueEntry.nodeId];
|
|
1287
|
+
args.nodeSnapshotsByNodeId[queueEntry.nodeId] = NodeExecutionSnapshotFactory.completed({
|
|
1288
|
+
previous,
|
|
1289
|
+
runId: args.runId,
|
|
1290
|
+
workflowId: args.workflowId,
|
|
1291
|
+
nodeId: queueEntry.nodeId,
|
|
1292
|
+
activationId: previous?.activationId ?? `synthetic_${queueEntry.nodeId}`,
|
|
1293
|
+
parent: args.parent,
|
|
1294
|
+
finishedAt: args.finishedAt,
|
|
1295
|
+
inputsByPort: this.resolveQueueEntryInputsByPort(queueEntry),
|
|
1296
|
+
outputs: pinnedOutputs,
|
|
1297
|
+
fromPinnedOutput: true
|
|
1298
|
+
});
|
|
1299
|
+
args.data.setOutputs(queueEntry.nodeId, pinnedOutputs);
|
|
1300
|
+
args.planner.applyOutputs(args.queue, {
|
|
1301
|
+
fromNodeId: queueEntry.nodeId,
|
|
1302
|
+
outputs: pinnedOutputs,
|
|
1303
|
+
batchId: queueEntry.batchId ?? "batch_1"
|
|
1304
|
+
});
|
|
1305
|
+
changed = true;
|
|
1306
|
+
break;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
createFinishedSnapshot(args) {
|
|
1311
|
+
const definition = args.workflow.nodes.find((node$1) => node$1.id === args.nodeId);
|
|
1312
|
+
if (this.missingRuntimeExecutionMarker.isMarked(definition?.config)) return NodeExecutionSnapshotFactory.skipped(args);
|
|
1313
|
+
return NodeExecutionSnapshotFactory.completed(args);
|
|
1314
|
+
}
|
|
1315
|
+
resolveQueueEntryInputsByPort(queueEntry) {
|
|
1316
|
+
if (queueEntry.collect) return queueEntry.collect.received;
|
|
1317
|
+
return { [queueEntry.toInput ?? "in"]: queueEntry.input };
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
|
|
1321
|
+
//#endregion
|
|
1322
|
+
//#region src/execution/WorkflowRunExecutionContextFactory.ts
|
|
1323
|
+
/**
|
|
1324
|
+
* Shared {@link ExecutionContextFactory#create} wiring for workflow runners (base context before node-specific fields).
|
|
1325
|
+
*/
|
|
1326
|
+
var WorkflowRunExecutionContextFactory = class {
|
|
1327
|
+
constructor(executionContextFactory, credentialResolverFactory) {
|
|
1328
|
+
this.executionContextFactory = executionContextFactory;
|
|
1329
|
+
this.credentialResolverFactory = credentialResolverFactory;
|
|
1330
|
+
}
|
|
1331
|
+
create(args) {
|
|
1332
|
+
return this.executionContextFactory.create({
|
|
1333
|
+
runId: args.runId,
|
|
1334
|
+
workflowId: args.workflowId,
|
|
1335
|
+
parent: args.parent,
|
|
1336
|
+
subworkflowDepth: args.subworkflowDepth,
|
|
1337
|
+
engineMaxNodeActivations: args.engineMaxNodeActivations,
|
|
1338
|
+
engineMaxSubworkflowDepth: args.engineMaxSubworkflowDepth,
|
|
1339
|
+
data: args.data,
|
|
1340
|
+
nodeState: args.nodeState,
|
|
1341
|
+
getCredential: this.credentialResolverFactory.create(args.workflowId, args.nodeId)
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1346
|
+
//#endregion
|
|
1347
|
+
//#region src/planning/WorkflowTopologyPlanner.ts
|
|
1348
|
+
var WorkflowTopology = class WorkflowTopology {
|
|
1349
|
+
constructor(defsById, outgoingByNode, incomingByNode, expectedInputsByNode, rootNodeIds) {
|
|
1350
|
+
this.defsById = defsById;
|
|
1351
|
+
this.outgoingByNode = outgoingByNode;
|
|
1352
|
+
this.incomingByNode = incomingByNode;
|
|
1353
|
+
this.expectedInputsByNode = expectedInputsByNode;
|
|
1354
|
+
this.rootNodeIds = rootNodeIds;
|
|
1355
|
+
}
|
|
1356
|
+
static fromWorkflow(wf) {
|
|
1357
|
+
const classifier = WorkflowExecutableNodeClassifierFactory.create(wf);
|
|
1358
|
+
const defs = /* @__PURE__ */ new Map();
|
|
1359
|
+
for (const n of wf.nodes) if (classifier.isExecutableNodeId(n.id)) defs.set(n.id, n);
|
|
1360
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
1361
|
+
for (const e of wf.edges) {
|
|
1362
|
+
if (!classifier.isExecutableNodeId(e.from.nodeId) || !classifier.isExecutableNodeId(e.to.nodeId)) continue;
|
|
1363
|
+
const list = outgoing.get(e.from.nodeId) ?? [];
|
|
1364
|
+
list.push({
|
|
1365
|
+
output: e.from.output,
|
|
1366
|
+
to: {
|
|
1367
|
+
nodeId: e.to.nodeId,
|
|
1368
|
+
input: e.to.input
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
outgoing.set(e.from.nodeId, list);
|
|
1372
|
+
}
|
|
1373
|
+
const incomingByNode = /* @__PURE__ */ new Map();
|
|
1374
|
+
for (const e of wf.edges) {
|
|
1375
|
+
if (!classifier.isExecutableNodeId(e.from.nodeId) || !classifier.isExecutableNodeId(e.to.nodeId)) continue;
|
|
1376
|
+
const list = incomingByNode.get(e.to.nodeId) ?? [];
|
|
1377
|
+
list.push({
|
|
1378
|
+
from: {
|
|
1379
|
+
nodeId: e.from.nodeId,
|
|
1380
|
+
output: e.from.output
|
|
1381
|
+
},
|
|
1382
|
+
input: e.to.input
|
|
1383
|
+
});
|
|
1384
|
+
incomingByNode.set(e.to.nodeId, list);
|
|
1385
|
+
}
|
|
1386
|
+
const expected = /* @__PURE__ */ new Map();
|
|
1387
|
+
for (const [toNodeId, inputs] of incomingByNode.entries()) {
|
|
1388
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1389
|
+
for (const edge of inputs) counts.set(edge.input, (counts.get(edge.input) ?? 0) + 1);
|
|
1390
|
+
for (const [k, n] of counts.entries()) if (n > 1) throw new Error(`Node ${toNodeId} has multiple edges into input '${k}'. Use a Merge node upstream.`);
|
|
1391
|
+
const order = [];
|
|
1392
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1393
|
+
for (const edge of inputs) {
|
|
1394
|
+
if (seen.has(edge.input)) continue;
|
|
1395
|
+
seen.add(edge.input);
|
|
1396
|
+
order.push(edge.input);
|
|
1397
|
+
}
|
|
1398
|
+
expected.set(toNodeId, order);
|
|
1399
|
+
}
|
|
1400
|
+
return new WorkflowTopology(defs, outgoing, incomingByNode, expected, wf.nodes.filter((node$1) => classifier.isExecutableNodeId(node$1.id) && !incomingByNode.has(node$1.id)).map((node$1) => node$1.id));
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
|
|
1404
|
+
//#endregion
|
|
1405
|
+
//#region src/orchestration/RunContinuationService.ts
|
|
1406
|
+
var RunContinuationService = class {
|
|
1407
|
+
constructor(activationIdFactory, workflowExecutionRepository, runDataFactory, runExecutionContextFactory, workflowSnapshotResolver, planningFactory, nodeStatePublisherFactory, credentialResolverFactory, nodeActivationRequestComposer, persistedRunStateTerminalBuilder, activationEnqueueService, nodeEventPublisher, semantics, waiters, policyErrorServices, terminalPersistence, executionLimitsPolicy) {
|
|
1408
|
+
this.activationIdFactory = activationIdFactory;
|
|
1409
|
+
this.workflowExecutionRepository = workflowExecutionRepository;
|
|
1410
|
+
this.runDataFactory = runDataFactory;
|
|
1411
|
+
this.runExecutionContextFactory = runExecutionContextFactory;
|
|
1412
|
+
this.workflowSnapshotResolver = workflowSnapshotResolver;
|
|
1413
|
+
this.planningFactory = planningFactory;
|
|
1414
|
+
this.nodeStatePublisherFactory = nodeStatePublisherFactory;
|
|
1415
|
+
this.credentialResolverFactory = credentialResolverFactory;
|
|
1416
|
+
this.nodeActivationRequestComposer = nodeActivationRequestComposer;
|
|
1417
|
+
this.persistedRunStateTerminalBuilder = persistedRunStateTerminalBuilder;
|
|
1418
|
+
this.activationEnqueueService = activationEnqueueService;
|
|
1419
|
+
this.nodeEventPublisher = nodeEventPublisher;
|
|
1420
|
+
this.semantics = semantics;
|
|
1421
|
+
this.waiters = waiters;
|
|
1422
|
+
this.policyErrorServices = policyErrorServices;
|
|
1423
|
+
this.terminalPersistence = terminalPersistence;
|
|
1424
|
+
this.executionLimitsPolicy = executionLimitsPolicy;
|
|
1425
|
+
}
|
|
1426
|
+
async markNodeRunning(args) {
|
|
1427
|
+
const state = await this.workflowExecutionRepository.load(args.runId);
|
|
1428
|
+
if (!state?.pending) return;
|
|
1429
|
+
if (state.pending.activationId !== args.activationId || state.pending.nodeId !== args.nodeId) return;
|
|
1430
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1431
|
+
const previous = state.nodeSnapshotsByNodeId?.[args.nodeId];
|
|
1432
|
+
const snapshot = NodeExecutionSnapshotFactory.running({
|
|
1433
|
+
previous,
|
|
1434
|
+
runId: state.runId,
|
|
1435
|
+
workflowId: state.workflowId,
|
|
1436
|
+
nodeId: args.nodeId,
|
|
1437
|
+
activationId: args.activationId,
|
|
1438
|
+
parent: state.parent,
|
|
1439
|
+
startedAt,
|
|
1440
|
+
inputsByPort: args.inputsByPort
|
|
1441
|
+
});
|
|
1442
|
+
await this.workflowExecutionRepository.save({
|
|
1443
|
+
...state,
|
|
1444
|
+
nodeSnapshotsByNodeId: {
|
|
1445
|
+
...state.nodeSnapshotsByNodeId ?? {},
|
|
1446
|
+
[args.nodeId]: snapshot
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
await this.nodeEventPublisher.publish("nodeStarted", snapshot);
|
|
1450
|
+
}
|
|
1451
|
+
async resumeFromNodeResult(args) {
|
|
1452
|
+
const state = await this.workflowExecutionRepository.load(args.runId);
|
|
1453
|
+
if (!state) throw new Error(`Unknown runId: ${args.runId}`);
|
|
1454
|
+
if (state.status !== "pending" || !state.pending) throw new Error(`Run ${args.runId} is not pending`);
|
|
1455
|
+
if (state.pending.activationId !== args.activationId) throw new Error(`activationId mismatch for run ${args.runId}`);
|
|
1456
|
+
if (state.pending.nodeId !== args.nodeId) throw new Error(`nodeId mismatch for run ${args.runId}`);
|
|
1457
|
+
const wf = this.resolvePersistedWorkflow(state);
|
|
1458
|
+
if (!wf) throw new Error(`Unknown workflowId: ${state.workflowId}`);
|
|
1459
|
+
const { topology, planner } = this.planningFactory.create(wf);
|
|
1460
|
+
const data = this.runDataFactory.create(state.outputsByNode);
|
|
1461
|
+
const limits = this.resolveEngineLimitsFromState(state);
|
|
1462
|
+
const base = this.runExecutionContextFactory.create({
|
|
1463
|
+
runId: state.runId,
|
|
1464
|
+
workflowId: state.workflowId,
|
|
1465
|
+
nodeId: args.nodeId,
|
|
1466
|
+
parent: state.parent,
|
|
1467
|
+
subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
|
|
1468
|
+
engineMaxNodeActivations: limits.engineMaxNodeActivations,
|
|
1469
|
+
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
1470
|
+
data,
|
|
1471
|
+
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, state.parent)
|
|
1472
|
+
});
|
|
1473
|
+
data.setOutputs(args.nodeId, args.outputs);
|
|
1474
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1475
|
+
const completedSnapshot = this.semantics.createFinishedSnapshot({
|
|
1476
|
+
workflow: wf,
|
|
1477
|
+
previous: state.nodeSnapshotsByNodeId?.[args.nodeId],
|
|
1478
|
+
runId: state.runId,
|
|
1479
|
+
workflowId: state.workflowId,
|
|
1480
|
+
nodeId: args.nodeId,
|
|
1481
|
+
activationId: args.activationId,
|
|
1482
|
+
parent: state.parent,
|
|
1483
|
+
finishedAt: completedAt,
|
|
1484
|
+
inputsByPort: state.pending.inputsByPort,
|
|
1485
|
+
outputs: args.outputs
|
|
1486
|
+
});
|
|
1487
|
+
const completedActivations = (state.engineCounters?.completedNodeActivations ?? 0) + 1;
|
|
1488
|
+
const engineCounters = { completedNodeActivations: completedActivations };
|
|
1489
|
+
const maxNodeActivations = state.executionOptions?.maxNodeActivations ?? Number.MAX_SAFE_INTEGER;
|
|
1490
|
+
if (this.semantics.isStopConditionSatisfied(state.control?.stopCondition, args.nodeId)) {
|
|
1491
|
+
const completedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1492
|
+
state,
|
|
1493
|
+
engineCounters,
|
|
1494
|
+
status: "completed",
|
|
1495
|
+
queue: [],
|
|
1496
|
+
outputsByNode: data.dump(),
|
|
1497
|
+
nodeSnapshotsByNodeId: {
|
|
1498
|
+
...state.nodeSnapshotsByNodeId ?? {},
|
|
1499
|
+
[args.nodeId]: completedSnapshot
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
1502
|
+
await this.workflowExecutionRepository.save(completedState);
|
|
1503
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1504
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1505
|
+
workflow: wf,
|
|
1506
|
+
state: completedState,
|
|
1507
|
+
finalStatus: "completed",
|
|
1508
|
+
finishedAt: completedAt
|
|
1509
|
+
});
|
|
1510
|
+
const result$1 = {
|
|
1511
|
+
runId: state.runId,
|
|
1512
|
+
workflowId: state.workflowId,
|
|
1513
|
+
startedAt: state.startedAt,
|
|
1514
|
+
status: "completed",
|
|
1515
|
+
outputs: this.semantics.resolveResultOutputs(wf, state.control?.stopCondition, data.dump())
|
|
1516
|
+
};
|
|
1517
|
+
this.waiters.resolveRunCompletion(result$1);
|
|
1518
|
+
return result$1;
|
|
1519
|
+
}
|
|
1520
|
+
const batchId = state.pending.batchId ?? "batch_1";
|
|
1521
|
+
const queue = (state.queue ?? []).map((q) => ({
|
|
1522
|
+
...q,
|
|
1523
|
+
batchId: q.batchId ?? batchId
|
|
1524
|
+
}));
|
|
1525
|
+
const nextNodeSnapshotsByNodeId = {
|
|
1526
|
+
...state.nodeSnapshotsByNodeId ?? {},
|
|
1527
|
+
[args.nodeId]: completedSnapshot
|
|
1528
|
+
};
|
|
1529
|
+
planner.applyOutputs(queue, {
|
|
1530
|
+
fromNodeId: args.nodeId,
|
|
1531
|
+
outputs: args.outputs,
|
|
1532
|
+
batchId
|
|
1533
|
+
});
|
|
1534
|
+
this.semantics.applyPinnedQueueSkips({
|
|
1535
|
+
runId: state.runId,
|
|
1536
|
+
workflowId: state.workflowId,
|
|
1537
|
+
parent: state.parent,
|
|
1538
|
+
mutableState: state.mutableState,
|
|
1539
|
+
planner,
|
|
1540
|
+
queue,
|
|
1541
|
+
data,
|
|
1542
|
+
nodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId,
|
|
1543
|
+
finishedAt: completedAt
|
|
1544
|
+
});
|
|
1545
|
+
let next;
|
|
1546
|
+
try {
|
|
1547
|
+
next = planner.nextActivation(queue);
|
|
1548
|
+
} catch (cause) {
|
|
1549
|
+
const completedDefinition = topology.defsById.get(args.nodeId);
|
|
1550
|
+
const completedNodeLabel = this.formatNodeLabel({
|
|
1551
|
+
definition: completedDefinition,
|
|
1552
|
+
nodeId: args.nodeId
|
|
1553
|
+
});
|
|
1554
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
1555
|
+
throw new Error(`After completing ${completedNodeLabel}, the engine could not plan the next activation. ${reason} Outputs: ${this.formatOutputCounts(args.outputs)}.`, { cause });
|
|
1556
|
+
}
|
|
1557
|
+
if (!next) {
|
|
1558
|
+
const lastNodeId = WorkflowExecutableNodeClassifierFactory.create(wf).lastExecutableNodeIdInDefinitionOrder(wf);
|
|
1559
|
+
const outputs = data.getOutputItems(lastNodeId, "main");
|
|
1560
|
+
const completedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1561
|
+
state,
|
|
1562
|
+
engineCounters,
|
|
1563
|
+
status: "completed",
|
|
1564
|
+
queue: [],
|
|
1565
|
+
outputsByNode: data.dump(),
|
|
1566
|
+
nodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId
|
|
1567
|
+
});
|
|
1568
|
+
await this.workflowExecutionRepository.save(completedState);
|
|
1569
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1570
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1571
|
+
workflow: wf,
|
|
1572
|
+
state: completedState,
|
|
1573
|
+
finalStatus: "completed",
|
|
1574
|
+
finishedAt: completedAt
|
|
1575
|
+
});
|
|
1576
|
+
const result$1 = {
|
|
1577
|
+
runId: state.runId,
|
|
1578
|
+
workflowId: state.workflowId,
|
|
1579
|
+
startedAt: state.startedAt,
|
|
1580
|
+
status: "completed",
|
|
1581
|
+
outputs
|
|
1582
|
+
};
|
|
1583
|
+
this.waiters.resolveRunCompletion(result$1);
|
|
1584
|
+
return result$1;
|
|
1585
|
+
}
|
|
1586
|
+
if (completedActivations >= maxNodeActivations) {
|
|
1587
|
+
const message = `Run exceeded maxNodeActivations (${maxNodeActivations}) after ${completedActivations} completed node activations (next would be ${next.nodeId}).`;
|
|
1588
|
+
const failedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1589
|
+
state,
|
|
1590
|
+
engineCounters,
|
|
1591
|
+
status: "failed",
|
|
1592
|
+
queue: queue.map((q) => ({ ...q })),
|
|
1593
|
+
outputsByNode: data.dump(),
|
|
1594
|
+
nodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId
|
|
1595
|
+
});
|
|
1596
|
+
await this.workflowExecutionRepository.save(failedState);
|
|
1597
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1598
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1599
|
+
workflow: wf,
|
|
1600
|
+
state: failedState,
|
|
1601
|
+
finalStatus: "failed",
|
|
1602
|
+
finishedAt: completedAt
|
|
1603
|
+
});
|
|
1604
|
+
const result$1 = {
|
|
1605
|
+
runId: state.runId,
|
|
1606
|
+
workflowId: state.workflowId,
|
|
1607
|
+
startedAt: state.startedAt,
|
|
1608
|
+
status: "failed",
|
|
1609
|
+
error: { message }
|
|
1610
|
+
};
|
|
1611
|
+
this.waiters.resolveRunCompletion(result$1);
|
|
1612
|
+
return result$1;
|
|
1613
|
+
}
|
|
1614
|
+
const def = topology.defsById.get(next.nodeId);
|
|
1615
|
+
if (!def || def.kind !== "node") throw new Error(`Node ${next.nodeId} is not a runnable node`);
|
|
1616
|
+
const request = this.nodeActivationRequestComposer.createFromPlannedActivation({
|
|
1617
|
+
next,
|
|
1618
|
+
base,
|
|
1619
|
+
data,
|
|
1620
|
+
runId: state.runId,
|
|
1621
|
+
workflowId: state.workflowId,
|
|
1622
|
+
parent: state.parent,
|
|
1623
|
+
executionOptions: state.executionOptions,
|
|
1624
|
+
nodeDefinition: def
|
|
1625
|
+
});
|
|
1626
|
+
const { queuedSnapshot, result } = await this.activationEnqueueService.enqueueActivationWithSnapshot({
|
|
1627
|
+
runId: state.runId,
|
|
1628
|
+
workflowId: state.workflowId,
|
|
1629
|
+
startedAt: state.startedAt,
|
|
1630
|
+
parent: state.parent,
|
|
1631
|
+
executionOptions: state.executionOptions,
|
|
1632
|
+
control: state.control,
|
|
1633
|
+
workflowSnapshot: state.workflowSnapshot,
|
|
1634
|
+
mutableState: state.mutableState,
|
|
1635
|
+
policySnapshot: state.policySnapshot,
|
|
1636
|
+
pendingQueue: queue,
|
|
1637
|
+
request,
|
|
1638
|
+
previousNodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId,
|
|
1639
|
+
planner,
|
|
1640
|
+
engineCounters,
|
|
1641
|
+
connectionInvocations: state.connectionInvocations ?? []
|
|
1642
|
+
});
|
|
1643
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1644
|
+
await this.nodeEventPublisher.publish("nodeQueued", queuedSnapshot);
|
|
1645
|
+
return result;
|
|
1646
|
+
}
|
|
1647
|
+
async resumeFromNodeError(args) {
|
|
1648
|
+
const state = await this.workflowExecutionRepository.load(args.runId);
|
|
1649
|
+
if (!state) throw new Error(`Unknown runId: ${args.runId}`);
|
|
1650
|
+
if (state.status !== "pending" || !state.pending) throw new Error(`Run ${args.runId} is not pending`);
|
|
1651
|
+
if (state.pending.activationId !== args.activationId) throw new Error(`activationId mismatch for run ${args.runId}`);
|
|
1652
|
+
if (state.pending.nodeId !== args.nodeId) throw new Error(`nodeId mismatch for run ${args.runId}`);
|
|
1653
|
+
const wf = this.resolvePersistedWorkflow(state);
|
|
1654
|
+
if (!wf) throw new Error(`Unknown workflowId: ${state.workflowId}`);
|
|
1655
|
+
const failedDefinition = WorkflowTopology.fromWorkflow(wf).defsById.get(args.nodeId);
|
|
1656
|
+
const webhookControlSignal = state.executionOptions?.webhook && failedDefinition?.kind === "trigger" ? this.asWebhookControlSignal(args.error) : void 0;
|
|
1657
|
+
if (webhookControlSignal) return await this.resumeFromWebhookControl({
|
|
1658
|
+
state,
|
|
1659
|
+
workflow: wf,
|
|
1660
|
+
args,
|
|
1661
|
+
signal: webhookControlSignal
|
|
1662
|
+
});
|
|
1663
|
+
if (failedDefinition && failedDefinition.kind === "node") {
|
|
1664
|
+
const nodeHandler = this.policyErrorServices.resolveNodeErrorHandler(failedDefinition.config.nodeErrorHandler);
|
|
1665
|
+
if (nodeHandler) try {
|
|
1666
|
+
const ctx = this.buildNodeExecutionContextForPending(state, wf, failedDefinition, args.nodeId);
|
|
1667
|
+
const inputsByPort = state.pending.inputsByPort;
|
|
1668
|
+
const portKeys = Object.keys(inputsByPort);
|
|
1669
|
+
const kind = portKeys.length === 1 && portKeys[0] === "in" ? "single" : "multi";
|
|
1670
|
+
const items = inputsByPort.in ?? [];
|
|
1671
|
+
const recovered = await nodeHandler.handle({
|
|
1672
|
+
kind,
|
|
1673
|
+
items,
|
|
1674
|
+
inputsByPort: kind === "multi" ? inputsByPort : void 0,
|
|
1675
|
+
ctx,
|
|
1676
|
+
error: args.error
|
|
1677
|
+
});
|
|
1678
|
+
return await this.resumeFromNodeResult({
|
|
1679
|
+
runId: args.runId,
|
|
1680
|
+
activationId: args.activationId,
|
|
1681
|
+
nodeId: args.nodeId,
|
|
1682
|
+
outputs: recovered
|
|
1683
|
+
});
|
|
1684
|
+
} catch {}
|
|
1685
|
+
}
|
|
1686
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1687
|
+
const message = args.error?.message ?? String(args.error);
|
|
1688
|
+
const failedSnapshot = NodeExecutionSnapshotFactory.failed({
|
|
1689
|
+
previous: state.nodeSnapshotsByNodeId?.[args.nodeId],
|
|
1690
|
+
runId: state.runId,
|
|
1691
|
+
workflowId: state.workflowId,
|
|
1692
|
+
nodeId: args.nodeId,
|
|
1693
|
+
activationId: args.activationId,
|
|
1694
|
+
parent: state.parent,
|
|
1695
|
+
finishedAt,
|
|
1696
|
+
inputsByPort: state.pending.inputsByPort,
|
|
1697
|
+
error: args.error
|
|
1698
|
+
});
|
|
1699
|
+
const failedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1700
|
+
state,
|
|
1701
|
+
engineCounters: state.engineCounters ?? { completedNodeActivations: 0 },
|
|
1702
|
+
status: "failed",
|
|
1703
|
+
queue: (state.queue ?? []).map((q) => ({ ...q })),
|
|
1704
|
+
outputsByNode: state.outputsByNode,
|
|
1705
|
+
nodeSnapshotsByNodeId: {
|
|
1706
|
+
...state.nodeSnapshotsByNodeId ?? {},
|
|
1707
|
+
[args.nodeId]: failedSnapshot
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
await this.workflowExecutionRepository.save(failedState);
|
|
1711
|
+
await this.nodeEventPublisher.publish("nodeFailed", failedSnapshot);
|
|
1712
|
+
const wfErr = this.policyErrorServices.resolveWorkflowErrorHandler(wf.workflowErrorHandler);
|
|
1713
|
+
if (wfErr) await Promise.resolve(wfErr.onError({
|
|
1714
|
+
runId: state.runId,
|
|
1715
|
+
workflowId: state.workflowId,
|
|
1716
|
+
workflow: wf,
|
|
1717
|
+
failedNodeId: args.nodeId,
|
|
1718
|
+
error: args.error,
|
|
1719
|
+
startedAt: state.startedAt,
|
|
1720
|
+
finishedAt
|
|
1721
|
+
}));
|
|
1722
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1723
|
+
workflow: wf,
|
|
1724
|
+
state: failedState,
|
|
1725
|
+
finalStatus: "failed",
|
|
1726
|
+
finishedAt
|
|
1727
|
+
});
|
|
1728
|
+
const result = {
|
|
1729
|
+
runId: state.runId,
|
|
1730
|
+
workflowId: state.workflowId,
|
|
1731
|
+
startedAt: state.startedAt,
|
|
1732
|
+
status: "failed",
|
|
1733
|
+
error: { message }
|
|
1734
|
+
};
|
|
1735
|
+
this.waiters.resolveRunCompletion(result);
|
|
1736
|
+
return result;
|
|
1737
|
+
}
|
|
1738
|
+
async resumeFromStepResult(args) {
|
|
1739
|
+
return await this.resumeFromNodeResult(args);
|
|
1740
|
+
}
|
|
1741
|
+
async resumeFromStepError(args) {
|
|
1742
|
+
return await this.resumeFromNodeError(args);
|
|
1743
|
+
}
|
|
1744
|
+
async waitForCompletion(runId) {
|
|
1745
|
+
const existing = await this.workflowExecutionRepository.load(runId);
|
|
1746
|
+
if (existing?.status === "completed") {
|
|
1747
|
+
const wf = this.resolvePersistedWorkflow(existing);
|
|
1748
|
+
const outputs = wf ? this.semantics.resolveResultOutputs(wf, existing.control?.stopCondition, existing.outputsByNode) : [];
|
|
1749
|
+
return {
|
|
1750
|
+
runId: existing.runId,
|
|
1751
|
+
workflowId: existing.workflowId,
|
|
1752
|
+
startedAt: existing.startedAt,
|
|
1753
|
+
status: "completed",
|
|
1754
|
+
outputs
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
if (existing?.status === "failed") return {
|
|
1758
|
+
runId: existing.runId,
|
|
1759
|
+
workflowId: existing.workflowId,
|
|
1760
|
+
startedAt: existing.startedAt,
|
|
1761
|
+
status: "failed",
|
|
1762
|
+
error: { message: "Run failed" }
|
|
1763
|
+
};
|
|
1764
|
+
const result = await this.waiters.waitForCompletion(runId);
|
|
1765
|
+
if (result.status !== "completed" && result.status !== "failed") throw new Error(`Unexpected run completion status: ${result.status}`);
|
|
1766
|
+
return result;
|
|
1767
|
+
}
|
|
1768
|
+
async waitForWebhookResponse(runId) {
|
|
1769
|
+
return await this.waiters.waitForWebhookResponse(runId);
|
|
1770
|
+
}
|
|
1771
|
+
async resumeFromWebhookControl(args) {
|
|
1772
|
+
const data = this.runDataFactory.create(args.state.outputsByNode);
|
|
1773
|
+
const { topology, planner } = this.planningFactory.create(args.workflow);
|
|
1774
|
+
const triggerOutputs = { main: args.signal.kind === "respondNowAndContinue" ? args.signal.continueItems ?? [] : args.signal.responseItems };
|
|
1775
|
+
data.setOutputs(args.args.nodeId, triggerOutputs);
|
|
1776
|
+
const completedSnapshot = this.semantics.createFinishedSnapshot({
|
|
1777
|
+
workflow: args.workflow,
|
|
1778
|
+
previous: args.state.nodeSnapshotsByNodeId?.[args.args.nodeId],
|
|
1779
|
+
runId: args.state.runId,
|
|
1780
|
+
workflowId: args.state.workflowId,
|
|
1781
|
+
nodeId: args.args.nodeId,
|
|
1782
|
+
activationId: args.args.activationId,
|
|
1783
|
+
parent: args.state.parent,
|
|
1784
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1785
|
+
inputsByPort: args.state.pending?.inputsByPort ?? NodeInputsByPortFactory.empty(),
|
|
1786
|
+
outputs: triggerOutputs
|
|
1787
|
+
});
|
|
1788
|
+
const completedActivations = (args.state.engineCounters?.completedNodeActivations ?? 0) + 1;
|
|
1789
|
+
const engineCounters = { completedNodeActivations: completedActivations };
|
|
1790
|
+
const maxNodeActivations = args.state.executionOptions?.maxNodeActivations ?? Number.MAX_SAFE_INTEGER;
|
|
1791
|
+
if (this.semantics.isStopConditionSatisfied(args.state.control?.stopCondition, args.args.nodeId)) {
|
|
1792
|
+
const completedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1793
|
+
state: args.state,
|
|
1794
|
+
engineCounters,
|
|
1795
|
+
status: "completed",
|
|
1796
|
+
queue: [],
|
|
1797
|
+
outputsByNode: data.dump(),
|
|
1798
|
+
nodeSnapshotsByNodeId: {
|
|
1799
|
+
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
1800
|
+
[args.args.nodeId]: completedSnapshot
|
|
1801
|
+
}
|
|
1802
|
+
});
|
|
1803
|
+
await this.workflowExecutionRepository.save(completedState);
|
|
1804
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1805
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1806
|
+
workflow: args.workflow,
|
|
1807
|
+
state: completedState,
|
|
1808
|
+
finalStatus: "completed",
|
|
1809
|
+
finishedAt: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
1810
|
+
});
|
|
1811
|
+
this.waiters.resolveWebhookResponse({
|
|
1812
|
+
runId: args.state.runId,
|
|
1813
|
+
workflowId: args.state.workflowId,
|
|
1814
|
+
startedAt: args.state.startedAt,
|
|
1815
|
+
runStatus: "completed",
|
|
1816
|
+
response: args.signal.responseItems
|
|
1817
|
+
});
|
|
1818
|
+
const result$1 = {
|
|
1819
|
+
runId: args.state.runId,
|
|
1820
|
+
workflowId: args.state.workflowId,
|
|
1821
|
+
startedAt: args.state.startedAt,
|
|
1822
|
+
status: "completed",
|
|
1823
|
+
outputs: this.semantics.resolveResultOutputs(args.workflow, args.state.control?.stopCondition, data.dump())
|
|
1824
|
+
};
|
|
1825
|
+
this.waiters.resolveRunCompletion(result$1);
|
|
1826
|
+
return result$1;
|
|
1827
|
+
}
|
|
1828
|
+
if (args.signal.kind === "respondNow") {
|
|
1829
|
+
const completedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1830
|
+
state: args.state,
|
|
1831
|
+
engineCounters,
|
|
1832
|
+
status: "completed",
|
|
1833
|
+
queue: [],
|
|
1834
|
+
outputsByNode: data.dump(),
|
|
1835
|
+
nodeSnapshotsByNodeId: {
|
|
1836
|
+
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
1837
|
+
[args.args.nodeId]: completedSnapshot
|
|
1838
|
+
}
|
|
1839
|
+
});
|
|
1840
|
+
await this.workflowExecutionRepository.save(completedState);
|
|
1841
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1842
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1843
|
+
workflow: args.workflow,
|
|
1844
|
+
state: completedState,
|
|
1845
|
+
finalStatus: "completed",
|
|
1846
|
+
finishedAt: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
1847
|
+
});
|
|
1848
|
+
const result$1 = {
|
|
1849
|
+
runId: args.state.runId,
|
|
1850
|
+
workflowId: args.state.workflowId,
|
|
1851
|
+
startedAt: args.state.startedAt,
|
|
1852
|
+
status: "completed",
|
|
1853
|
+
outputs: args.signal.responseItems
|
|
1854
|
+
};
|
|
1855
|
+
this.waiters.resolveWebhookResponse({
|
|
1856
|
+
runId: args.state.runId,
|
|
1857
|
+
workflowId: args.state.workflowId,
|
|
1858
|
+
startedAt: args.state.startedAt,
|
|
1859
|
+
runStatus: "completed",
|
|
1860
|
+
response: args.signal.responseItems
|
|
1861
|
+
});
|
|
1862
|
+
this.waiters.resolveRunCompletion(result$1);
|
|
1863
|
+
return result$1;
|
|
1864
|
+
}
|
|
1865
|
+
const batchId = args.state.pending?.batchId ?? "batch_1";
|
|
1866
|
+
const queue = (args.state.queue ?? []).map((entry) => ({
|
|
1867
|
+
...entry,
|
|
1868
|
+
batchId: entry.batchId ?? batchId
|
|
1869
|
+
}));
|
|
1870
|
+
planner.applyOutputs(queue, {
|
|
1871
|
+
fromNodeId: args.args.nodeId,
|
|
1872
|
+
outputs: triggerOutputs,
|
|
1873
|
+
batchId
|
|
1874
|
+
});
|
|
1875
|
+
const next = planner.nextActivation(queue);
|
|
1876
|
+
if (!next) {
|
|
1877
|
+
const lastNodeId = WorkflowExecutableNodeClassifierFactory.create(args.workflow).lastExecutableNodeIdInDefinitionOrder(args.workflow);
|
|
1878
|
+
const outputs = data.getOutputItems(lastNodeId, "main");
|
|
1879
|
+
const completedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1880
|
+
state: args.state,
|
|
1881
|
+
engineCounters,
|
|
1882
|
+
status: "completed",
|
|
1883
|
+
queue: [],
|
|
1884
|
+
outputsByNode: data.dump(),
|
|
1885
|
+
nodeSnapshotsByNodeId: {
|
|
1886
|
+
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
1887
|
+
[args.args.nodeId]: completedSnapshot
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
await this.workflowExecutionRepository.save(completedState);
|
|
1891
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1892
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1893
|
+
workflow: args.workflow,
|
|
1894
|
+
state: completedState,
|
|
1895
|
+
finalStatus: "completed",
|
|
1896
|
+
finishedAt: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
1897
|
+
});
|
|
1898
|
+
const result$1 = {
|
|
1899
|
+
runId: args.state.runId,
|
|
1900
|
+
workflowId: args.state.workflowId,
|
|
1901
|
+
startedAt: args.state.startedAt,
|
|
1902
|
+
status: "completed",
|
|
1903
|
+
outputs
|
|
1904
|
+
};
|
|
1905
|
+
this.waiters.resolveWebhookResponse({
|
|
1906
|
+
runId: args.state.runId,
|
|
1907
|
+
workflowId: args.state.workflowId,
|
|
1908
|
+
startedAt: args.state.startedAt,
|
|
1909
|
+
runStatus: "completed",
|
|
1910
|
+
response: args.signal.responseItems
|
|
1911
|
+
});
|
|
1912
|
+
this.waiters.resolveRunCompletion(result$1);
|
|
1913
|
+
return result$1;
|
|
1914
|
+
}
|
|
1915
|
+
if (completedActivations >= maxNodeActivations) {
|
|
1916
|
+
const message = `Run exceeded maxNodeActivations (${maxNodeActivations}) after ${completedActivations} completed node activations (next would be ${next.nodeId}).`;
|
|
1917
|
+
const failedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
1918
|
+
state: args.state,
|
|
1919
|
+
engineCounters,
|
|
1920
|
+
status: "failed",
|
|
1921
|
+
queue: queue.map((q) => ({ ...q })),
|
|
1922
|
+
outputsByNode: data.dump(),
|
|
1923
|
+
nodeSnapshotsByNodeId: {
|
|
1924
|
+
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
1925
|
+
[args.args.nodeId]: completedSnapshot
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
await this.workflowExecutionRepository.save(failedState);
|
|
1929
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1930
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
1931
|
+
workflow: args.workflow,
|
|
1932
|
+
state: failedState,
|
|
1933
|
+
finalStatus: "failed",
|
|
1934
|
+
finishedAt: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
1935
|
+
});
|
|
1936
|
+
const result$1 = {
|
|
1937
|
+
runId: args.state.runId,
|
|
1938
|
+
workflowId: args.state.workflowId,
|
|
1939
|
+
startedAt: args.state.startedAt,
|
|
1940
|
+
status: "failed",
|
|
1941
|
+
error: { message }
|
|
1942
|
+
};
|
|
1943
|
+
this.waiters.resolveWebhookResponse({
|
|
1944
|
+
runId: args.state.runId,
|
|
1945
|
+
workflowId: args.state.workflowId,
|
|
1946
|
+
startedAt: args.state.startedAt,
|
|
1947
|
+
runStatus: "pending",
|
|
1948
|
+
response: args.signal.responseItems
|
|
1949
|
+
});
|
|
1950
|
+
this.waiters.resolveRunCompletion(result$1);
|
|
1951
|
+
return result$1;
|
|
1952
|
+
}
|
|
1953
|
+
const nextDefinition = topology.defsById.get(next.nodeId);
|
|
1954
|
+
if (!nextDefinition || nextDefinition.kind !== "node") throw new Error(`Node ${next.nodeId} is not a runnable node`);
|
|
1955
|
+
const webhookLimits = this.resolveEngineLimitsFromState(args.state);
|
|
1956
|
+
const base = this.runExecutionContextFactory.create({
|
|
1957
|
+
runId: args.state.runId,
|
|
1958
|
+
workflowId: args.state.workflowId,
|
|
1959
|
+
nodeId: nextDefinition.id,
|
|
1960
|
+
parent: args.state.parent,
|
|
1961
|
+
subworkflowDepth: args.state.executionOptions?.subworkflowDepth ?? 0,
|
|
1962
|
+
engineMaxNodeActivations: webhookLimits.engineMaxNodeActivations,
|
|
1963
|
+
engineMaxSubworkflowDepth: webhookLimits.engineMaxSubworkflowDepth,
|
|
1964
|
+
data,
|
|
1965
|
+
nodeState: this.nodeStatePublisherFactory.create(args.state.runId, args.state.workflowId, args.state.parent)
|
|
1966
|
+
});
|
|
1967
|
+
const request = this.nodeActivationRequestComposer.createFromPlannedActivation({
|
|
1968
|
+
next,
|
|
1969
|
+
base,
|
|
1970
|
+
data,
|
|
1971
|
+
runId: args.state.runId,
|
|
1972
|
+
workflowId: args.state.workflowId,
|
|
1973
|
+
parent: args.state.parent,
|
|
1974
|
+
executionOptions: args.state.executionOptions,
|
|
1975
|
+
nodeDefinition: nextDefinition
|
|
1976
|
+
});
|
|
1977
|
+
const { queuedSnapshot, result } = await this.activationEnqueueService.enqueueActivationWithSnapshot({
|
|
1978
|
+
runId: args.state.runId,
|
|
1979
|
+
workflowId: args.state.workflowId,
|
|
1980
|
+
startedAt: args.state.startedAt,
|
|
1981
|
+
parent: args.state.parent,
|
|
1982
|
+
executionOptions: args.state.executionOptions,
|
|
1983
|
+
control: args.state.control,
|
|
1984
|
+
workflowSnapshot: args.state.workflowSnapshot,
|
|
1985
|
+
mutableState: args.state.mutableState,
|
|
1986
|
+
policySnapshot: args.state.policySnapshot,
|
|
1987
|
+
pendingQueue: queue,
|
|
1988
|
+
request,
|
|
1989
|
+
previousNodeSnapshotsByNodeId: {
|
|
1990
|
+
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
1991
|
+
[args.args.nodeId]: completedSnapshot
|
|
1992
|
+
},
|
|
1993
|
+
planner,
|
|
1994
|
+
engineCounters,
|
|
1995
|
+
connectionInvocations: args.state.connectionInvocations ?? []
|
|
1996
|
+
});
|
|
1997
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1998
|
+
await this.nodeEventPublisher.publish("nodeQueued", queuedSnapshot);
|
|
1999
|
+
this.waiters.resolveWebhookResponse({
|
|
2000
|
+
runId: args.state.runId,
|
|
2001
|
+
workflowId: args.state.workflowId,
|
|
2002
|
+
startedAt: args.state.startedAt,
|
|
2003
|
+
runStatus: "pending",
|
|
2004
|
+
response: args.signal.responseItems
|
|
2005
|
+
});
|
|
2006
|
+
return result;
|
|
2007
|
+
}
|
|
2008
|
+
asWebhookControlSignal(error) {
|
|
2009
|
+
const candidate = error;
|
|
2010
|
+
if (!candidate || candidate.__webhookControl !== true) return void 0;
|
|
2011
|
+
if (candidate.kind !== "respondNow" && candidate.kind !== "respondNowAndContinue") return void 0;
|
|
2012
|
+
if (!Array.isArray(candidate.responseItems)) return void 0;
|
|
2013
|
+
return candidate;
|
|
2014
|
+
}
|
|
2015
|
+
resolvePersistedWorkflow(state) {
|
|
2016
|
+
return this.workflowSnapshotResolver.resolve({
|
|
2017
|
+
workflowId: state.workflowId,
|
|
2018
|
+
workflowSnapshot: state.workflowSnapshot
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
buildNodeExecutionContextForPending(state, wf, def, nodeId) {
|
|
2022
|
+
const data = this.runDataFactory.create(state.outputsByNode);
|
|
2023
|
+
const limits = this.resolveEngineLimitsFromState(state);
|
|
2024
|
+
const base = this.runExecutionContextFactory.create({
|
|
2025
|
+
runId: state.runId,
|
|
2026
|
+
workflowId: state.workflowId,
|
|
2027
|
+
nodeId,
|
|
2028
|
+
parent: state.parent,
|
|
2029
|
+
subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
|
|
2030
|
+
engineMaxNodeActivations: limits.engineMaxNodeActivations,
|
|
2031
|
+
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
2032
|
+
data,
|
|
2033
|
+
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, state.parent)
|
|
2034
|
+
});
|
|
2035
|
+
const activationId = state.pending.activationId;
|
|
2036
|
+
return {
|
|
2037
|
+
...base,
|
|
2038
|
+
data,
|
|
2039
|
+
nodeId,
|
|
2040
|
+
activationId,
|
|
2041
|
+
config: def.config,
|
|
2042
|
+
binary: base.binary.forNode({
|
|
2043
|
+
nodeId,
|
|
2044
|
+
activationId
|
|
2045
|
+
}),
|
|
2046
|
+
getCredential: this.credentialResolverFactory.create(wf.id, nodeId, def.config)
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
resolveEngineLimitsFromState(state) {
|
|
2050
|
+
const fb = this.executionLimitsPolicy.createRootExecutionOptions();
|
|
2051
|
+
return {
|
|
2052
|
+
engineMaxNodeActivations: state.executionOptions?.maxNodeActivations ?? fb.maxNodeActivations,
|
|
2053
|
+
engineMaxSubworkflowDepth: state.executionOptions?.maxSubworkflowDepth ?? fb.maxSubworkflowDepth
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
formatNodeLabel(args) {
|
|
2057
|
+
const tokenName = typeof args.definition?.type === "function" ? args.definition.type.name : "Node";
|
|
2058
|
+
return args.definition?.name ? `"${args.definition.name}" (${tokenName}:${args.nodeId})` : `${tokenName}:${args.nodeId}`;
|
|
2059
|
+
}
|
|
2060
|
+
formatOutputCounts(outputs) {
|
|
2061
|
+
const entries = Object.entries(outputs ?? {});
|
|
2062
|
+
if (entries.length === 0) return "no outputs";
|
|
2063
|
+
return entries.map(([port, items]) => `${port}=${items?.length ?? 0}`).join(", ");
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
|
|
2067
|
+
//#endregion
|
|
2068
|
+
//#region src/planning/CurrentStateFrontierPlanner.ts
|
|
2069
|
+
var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
2070
|
+
constructor(topology) {
|
|
2071
|
+
this.topology = topology;
|
|
2072
|
+
}
|
|
2073
|
+
/** Composition-root-friendly factory (avoids `new` at orchestration call sites under ESLint manual-DI rules). */
|
|
2074
|
+
static createFromTopology(topology) {
|
|
2075
|
+
return new CurrentStateFrontierPlanner(topology);
|
|
2076
|
+
}
|
|
2077
|
+
plan(args) {
|
|
2078
|
+
const stopCondition = args.stopCondition ?? { kind: "workflowCompleted" };
|
|
2079
|
+
const baseState = this.cloneCurrentState(args.currentState);
|
|
2080
|
+
const normalizedState = this.overlayPinnedOutputs(baseState);
|
|
2081
|
+
const resetResult = this.applyReset({
|
|
2082
|
+
currentState: normalizedState,
|
|
2083
|
+
reset: args.reset
|
|
2084
|
+
});
|
|
2085
|
+
const requiredNodeIds = this.collectRequiredNodeIds(stopCondition, resetResult.currentState);
|
|
2086
|
+
const satisfiedNodeIds = this.collectSatisfiedNodeIds(resetResult.currentState);
|
|
2087
|
+
const skippedNodeIds = [...new Set([...[...requiredNodeIds].filter((nodeId) => this.isNodeSatisfied(resetResult.currentState, nodeId)), ...resetResult.preservedPinnedNodeIds.filter((nodeId) => requiredNodeIds.has(nodeId))])];
|
|
2088
|
+
const frontierNodeIds = this.collectFrontierNodeIds(requiredNodeIds, resetResult.currentState);
|
|
2089
|
+
const rootNodeIds = frontierNodeIds.filter((nodeId) => (this.topology.incomingByNode.get(nodeId) ?? []).length === 0);
|
|
2090
|
+
if (rootNodeIds.length > 1) throw new Error(`Ambiguous execution frontier. Multiple root nodes require input: ${rootNodeIds.join(", ")}`);
|
|
2091
|
+
if (frontierNodeIds.length === 0) return {
|
|
2092
|
+
queue: [],
|
|
2093
|
+
currentState: resetResult.currentState,
|
|
2094
|
+
stopCondition,
|
|
2095
|
+
satisfiedNodeIds,
|
|
2096
|
+
skippedNodeIds,
|
|
2097
|
+
clearedNodeIds: resetResult.clearedNodeIds,
|
|
2098
|
+
preservedPinnedNodeIds: resetResult.preservedPinnedNodeIds
|
|
2099
|
+
};
|
|
2100
|
+
if (rootNodeIds.length === 1) {
|
|
2101
|
+
const rootNodeId = rootNodeIds[0];
|
|
2102
|
+
const definition = this.topology.defsById.get(rootNodeId);
|
|
2103
|
+
if (!definition) throw new Error(`Unknown frontier nodeId: ${rootNodeId}`);
|
|
2104
|
+
return {
|
|
2105
|
+
rootNodeId,
|
|
2106
|
+
rootNodeInput: this.resolveRootNodeInput({
|
|
2107
|
+
nodeKind: definition.kind,
|
|
2108
|
+
items: args.items
|
|
2109
|
+
}),
|
|
2110
|
+
queue: [],
|
|
2111
|
+
currentState: resetResult.currentState,
|
|
2112
|
+
stopCondition,
|
|
2113
|
+
satisfiedNodeIds,
|
|
2114
|
+
skippedNodeIds,
|
|
2115
|
+
clearedNodeIds: resetResult.clearedNodeIds,
|
|
2116
|
+
preservedPinnedNodeIds: resetResult.preservedPinnedNodeIds
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
const queue = [];
|
|
2120
|
+
for (const nodeId of frontierNodeIds) queue.push(...this.buildFrontierQueue(nodeId, resetResult.currentState));
|
|
2121
|
+
return {
|
|
2122
|
+
queue,
|
|
2123
|
+
currentState: resetResult.currentState,
|
|
2124
|
+
stopCondition,
|
|
2125
|
+
satisfiedNodeIds,
|
|
2126
|
+
skippedNodeIds,
|
|
2127
|
+
clearedNodeIds: resetResult.clearedNodeIds,
|
|
2128
|
+
preservedPinnedNodeIds: resetResult.preservedPinnedNodeIds
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
cloneCurrentState(currentState) {
|
|
2132
|
+
if (!currentState) return {
|
|
2133
|
+
outputsByNode: {},
|
|
2134
|
+
nodeSnapshotsByNodeId: {},
|
|
2135
|
+
connectionInvocations: [],
|
|
2136
|
+
mutableState: void 0
|
|
2137
|
+
};
|
|
2138
|
+
return {
|
|
2139
|
+
outputsByNode: { ...currentState.outputsByNode },
|
|
2140
|
+
nodeSnapshotsByNodeId: { ...currentState.nodeSnapshotsByNodeId },
|
|
2141
|
+
connectionInvocations: currentState.connectionInvocations ? [...currentState.connectionInvocations] : void 0,
|
|
2142
|
+
mutableState: currentState.mutableState
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
overlayPinnedOutputs(currentState) {
|
|
2146
|
+
const outputsByNode = { ...currentState.outputsByNode };
|
|
2147
|
+
for (const [nodeId, nodeState] of Object.entries(currentState.mutableState?.nodesById ?? {})) {
|
|
2148
|
+
const pinnedOutputs = nodeState.pinnedOutputsByPort;
|
|
2149
|
+
if (!pinnedOutputs) continue;
|
|
2150
|
+
outputsByNode[nodeId] = pinnedOutputs;
|
|
2151
|
+
}
|
|
2152
|
+
return {
|
|
2153
|
+
outputsByNode,
|
|
2154
|
+
nodeSnapshotsByNodeId: { ...currentState.nodeSnapshotsByNodeId },
|
|
2155
|
+
connectionInvocations: currentState.connectionInvocations,
|
|
2156
|
+
mutableState: currentState.mutableState
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
applyReset(args) {
|
|
2160
|
+
if (!args.reset) return {
|
|
2161
|
+
currentState: args.currentState,
|
|
2162
|
+
clearedNodeIds: [],
|
|
2163
|
+
preservedPinnedNodeIds: []
|
|
2164
|
+
};
|
|
2165
|
+
const outputsByNode = { ...args.currentState.outputsByNode };
|
|
2166
|
+
const nodeSnapshotsByNodeId = { ...args.currentState.nodeSnapshotsByNodeId };
|
|
2167
|
+
const clearedNodeIds = [];
|
|
2168
|
+
const preservedPinnedNodeIds = [];
|
|
2169
|
+
const descendants = this.collectDescendants(args.reset.clearFromNodeId);
|
|
2170
|
+
const runtimeDescendants = this.collectRuntimeDescendants(args.currentState, descendants);
|
|
2171
|
+
const clearedIdSet = new Set([...descendants, ...runtimeDescendants]);
|
|
2172
|
+
for (const nodeId of [...descendants, ...runtimeDescendants]) {
|
|
2173
|
+
const pinnedOutputs = this.getPinnedOutputs(args.currentState, nodeId);
|
|
2174
|
+
if (pinnedOutputs) {
|
|
2175
|
+
outputsByNode[nodeId] = pinnedOutputs;
|
|
2176
|
+
delete nodeSnapshotsByNodeId[nodeId];
|
|
2177
|
+
preservedPinnedNodeIds.push(nodeId);
|
|
2178
|
+
continue;
|
|
2179
|
+
}
|
|
2180
|
+
delete outputsByNode[nodeId];
|
|
2181
|
+
delete nodeSnapshotsByNodeId[nodeId];
|
|
2182
|
+
clearedNodeIds.push(nodeId);
|
|
2183
|
+
}
|
|
2184
|
+
return {
|
|
2185
|
+
currentState: {
|
|
2186
|
+
outputsByNode,
|
|
2187
|
+
nodeSnapshotsByNodeId,
|
|
2188
|
+
connectionInvocations: this.filterConnectionInvocations(args.currentState.connectionInvocations, clearedIdSet),
|
|
2189
|
+
mutableState: args.currentState.mutableState
|
|
2190
|
+
},
|
|
2191
|
+
clearedNodeIds,
|
|
2192
|
+
preservedPinnedNodeIds
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
collectSatisfiedNodeIds(currentState) {
|
|
2196
|
+
const satisfiedNodeIds = [];
|
|
2197
|
+
for (const nodeId of this.topology.defsById.keys()) if (this.isNodeSatisfied(currentState, nodeId)) satisfiedNodeIds.push(nodeId);
|
|
2198
|
+
return satisfiedNodeIds;
|
|
2199
|
+
}
|
|
2200
|
+
collectFrontierNodeIds(requiredNodeIds, currentState) {
|
|
2201
|
+
const frontierNodeIds = [];
|
|
2202
|
+
for (const nodeId of this.topology.defsById.keys()) {
|
|
2203
|
+
if (!requiredNodeIds.has(nodeId) || this.isNodeSatisfied(currentState, nodeId)) continue;
|
|
2204
|
+
if ((this.topology.incomingByNode.get(nodeId) ?? []).every((edge) => this.isEdgeSatisfied(currentState, nodeId, edge.input))) frontierNodeIds.push(nodeId);
|
|
2205
|
+
}
|
|
2206
|
+
return frontierNodeIds;
|
|
2207
|
+
}
|
|
2208
|
+
collectRequiredNodeIds(stopCondition, currentState) {
|
|
2209
|
+
const requiredNodeIds = /* @__PURE__ */ new Set();
|
|
2210
|
+
if (stopCondition.kind === "workflowCompleted") {
|
|
2211
|
+
for (const nodeId of this.topology.defsById.keys()) if (!this.isNodeSatisfied(currentState, nodeId)) this.collectRequiredNode(requiredNodeIds, currentState, nodeId);
|
|
2212
|
+
return requiredNodeIds;
|
|
2213
|
+
}
|
|
2214
|
+
if (!this.topology.defsById.has(stopCondition.nodeId)) throw new Error(`Unknown stop nodeId: ${stopCondition.nodeId}`);
|
|
2215
|
+
this.collectRequiredNode(requiredNodeIds, currentState, stopCondition.nodeId);
|
|
2216
|
+
return requiredNodeIds;
|
|
2217
|
+
}
|
|
2218
|
+
collectRequiredNode(requiredNodeIds, currentState, nodeId) {
|
|
2219
|
+
if (requiredNodeIds.has(nodeId)) return;
|
|
2220
|
+
if (this.isNodeSatisfied(currentState, nodeId) && !this.isNodeSatisfiedByOutputsOnly(currentState, nodeId)) return;
|
|
2221
|
+
requiredNodeIds.add(nodeId);
|
|
2222
|
+
for (const edge of this.topology.incomingByNode.get(nodeId) ?? []) if (!this.isEdgeSatisfied(currentState, nodeId, edge.input) || this.isNodeSatisfiedByOutputsOnly(currentState, edge.from.nodeId)) this.collectRequiredNode(requiredNodeIds, currentState, edge.from.nodeId);
|
|
2223
|
+
}
|
|
2224
|
+
buildFrontierQueue(nodeId, currentState) {
|
|
2225
|
+
const incomingEdges = this.topology.incomingByNode.get(nodeId) ?? [];
|
|
2226
|
+
if (incomingEdges.length === 0) return [];
|
|
2227
|
+
const expectedInputs = this.topology.expectedInputsByNode.get(nodeId) ?? [];
|
|
2228
|
+
if (expectedInputs.length !== 1 || expectedInputs[0] !== "in") {
|
|
2229
|
+
const received = {};
|
|
2230
|
+
for (const input$1 of expectedInputs) received[input$1] = this.resolveInput(currentState, nodeId, input$1);
|
|
2231
|
+
return [{
|
|
2232
|
+
nodeId,
|
|
2233
|
+
input: [],
|
|
2234
|
+
batchId: "batch_1",
|
|
2235
|
+
collect: {
|
|
2236
|
+
expectedInputs,
|
|
2237
|
+
received
|
|
2238
|
+
}
|
|
2239
|
+
}];
|
|
2240
|
+
}
|
|
2241
|
+
const input = expectedInputs[0] ?? "in";
|
|
2242
|
+
const incomingEdge = incomingEdges.find((edge) => edge.input === input);
|
|
2243
|
+
return [{
|
|
2244
|
+
nodeId,
|
|
2245
|
+
input: this.resolveInput(currentState, nodeId, input),
|
|
2246
|
+
toInput: input,
|
|
2247
|
+
batchId: "batch_1",
|
|
2248
|
+
from: incomingEdge?.from
|
|
2249
|
+
}];
|
|
2250
|
+
}
|
|
2251
|
+
resolveRootNodeInput(args) {
|
|
2252
|
+
if (args.items) return args.items;
|
|
2253
|
+
if (args.nodeKind === "trigger") return [];
|
|
2254
|
+
return [{ json: {} }];
|
|
2255
|
+
}
|
|
2256
|
+
isNodeSatisfied(currentState, nodeId) {
|
|
2257
|
+
return this.hasOutputs(currentState, nodeId) || this.hasCompletedSnapshot(currentState, nodeId);
|
|
2258
|
+
}
|
|
2259
|
+
isNodeSatisfiedByOutputsOnly(currentState, nodeId) {
|
|
2260
|
+
return this.hasOutputs(currentState, nodeId) && !this.hasCompletedSnapshot(currentState, nodeId);
|
|
2261
|
+
}
|
|
2262
|
+
isEdgeSatisfied(currentState, nodeId, input) {
|
|
2263
|
+
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input);
|
|
2264
|
+
if (!incomingEdge) return false;
|
|
2265
|
+
return this.hasOutputPort(currentState, incomingEdge.from.nodeId, incomingEdge.from.output);
|
|
2266
|
+
}
|
|
2267
|
+
resolveInput(currentState, nodeId, input) {
|
|
2268
|
+
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input);
|
|
2269
|
+
if (!incomingEdge) return [];
|
|
2270
|
+
return this.resolveOutputItems(currentState, incomingEdge.from.nodeId, incomingEdge.from.output);
|
|
2271
|
+
}
|
|
2272
|
+
hasOutputs(currentState, nodeId) {
|
|
2273
|
+
return Object.prototype.hasOwnProperty.call(currentState.outputsByNode, nodeId);
|
|
2274
|
+
}
|
|
2275
|
+
hasCompletedSnapshot(currentState, nodeId) {
|
|
2276
|
+
const snapshot = currentState.nodeSnapshotsByNodeId[nodeId];
|
|
2277
|
+
return snapshot?.status === "completed" || snapshot?.status === "skipped";
|
|
2278
|
+
}
|
|
2279
|
+
hasOutputPort(currentState, nodeId, output) {
|
|
2280
|
+
const outputs = currentState.outputsByNode[nodeId];
|
|
2281
|
+
if (!outputs) return false;
|
|
2282
|
+
return Object.prototype.hasOwnProperty.call(outputs, output);
|
|
2283
|
+
}
|
|
2284
|
+
resolveOutputItems(currentState, nodeId, output) {
|
|
2285
|
+
return currentState.outputsByNode[nodeId]?.[output] ?? [];
|
|
2286
|
+
}
|
|
2287
|
+
getPinnedOutputs(currentState, nodeId) {
|
|
2288
|
+
return currentState.mutableState?.nodesById?.[nodeId]?.pinnedOutputsByPort;
|
|
2289
|
+
}
|
|
2290
|
+
filterConnectionInvocations(invocations, clearedIdSet) {
|
|
2291
|
+
if (!invocations || invocations.length === 0) return invocations;
|
|
2292
|
+
const kept = invocations.filter((invocation) => !clearedIdSet.has(invocation.parentAgentNodeId) && !clearedIdSet.has(invocation.connectionNodeId));
|
|
2293
|
+
return kept.length === invocations.length ? invocations : kept;
|
|
2294
|
+
}
|
|
2295
|
+
collectDescendants(startNodeId) {
|
|
2296
|
+
const pendingNodeIds = [startNodeId];
|
|
2297
|
+
const descendants = /* @__PURE__ */ new Set();
|
|
2298
|
+
while (pendingNodeIds.length > 0) {
|
|
2299
|
+
const nodeId = pendingNodeIds.pop();
|
|
2300
|
+
if (!nodeId || descendants.has(nodeId)) continue;
|
|
2301
|
+
descendants.add(nodeId);
|
|
2302
|
+
for (const edge of this.topology.outgoingByNode.get(nodeId) ?? []) pendingNodeIds.push(edge.to.nodeId);
|
|
2303
|
+
}
|
|
2304
|
+
return [...descendants];
|
|
2305
|
+
}
|
|
2306
|
+
collectRuntimeDescendants(currentState, descendantNodeIds) {
|
|
2307
|
+
const descendantSet = new Set(descendantNodeIds);
|
|
2308
|
+
const runtimeNodeIds = /* @__PURE__ */ new Set();
|
|
2309
|
+
for (const nodeId of [
|
|
2310
|
+
...Object.keys(currentState.outputsByNode),
|
|
2311
|
+
...Object.keys(currentState.nodeSnapshotsByNodeId),
|
|
2312
|
+
...Object.keys(currentState.mutableState?.nodesById ?? {})
|
|
2313
|
+
]) if (this.isRuntimeDescendant(nodeId, descendantSet)) runtimeNodeIds.add(nodeId);
|
|
2314
|
+
return [...runtimeNodeIds];
|
|
2315
|
+
}
|
|
2316
|
+
isRuntimeDescendant(nodeId, descendantNodeIds) {
|
|
2317
|
+
for (const descendantNodeId of descendantNodeIds) {
|
|
2318
|
+
if (nodeId === descendantNodeId) return false;
|
|
2319
|
+
if (ConnectionNodeIdFactory.isConnectionOwnedDescendantOf(descendantNodeId, nodeId)) return true;
|
|
2320
|
+
}
|
|
2321
|
+
return false;
|
|
2322
|
+
}
|
|
2323
|
+
};
|
|
2324
|
+
|
|
2325
|
+
//#endregion
|
|
2326
|
+
//#region src/policies/storage/RunPolicySnapshotFactory.ts
|
|
2327
|
+
var RunPolicySnapshotFactory = class {
|
|
2328
|
+
static create(workflow, defaults) {
|
|
2329
|
+
const prune = workflow.prunePolicy;
|
|
2330
|
+
return {
|
|
2331
|
+
retentionSeconds: prune?.runDataRetentionSeconds ?? defaults?.retentionSeconds,
|
|
2332
|
+
binaryRetentionSeconds: prune?.binaryRetentionSeconds ?? defaults?.binaryRetentionSeconds,
|
|
2333
|
+
storagePolicy: typeof workflow.storagePolicy === "string" ? workflow.storagePolicy : defaults?.storagePolicy ?? "ALL"
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
|
|
2338
|
+
//#endregion
|
|
2339
|
+
//#region src/orchestration/RunStartService.ts
|
|
2340
|
+
var RunStartService = class {
|
|
2341
|
+
constructor(runIdFactory, workflowExecutionRepository, runDataFactory, workflowSnapshotFactory, planningFactory, nodeStatePublisherFactory, runExecutionContextFactory, nodeActivationRequestComposer, activationEnqueueService, semantics, waiters, workflowPolicyRuntimeDefaults, executionLimitsPolicy) {
|
|
2342
|
+
this.runIdFactory = runIdFactory;
|
|
2343
|
+
this.workflowExecutionRepository = workflowExecutionRepository;
|
|
2344
|
+
this.runDataFactory = runDataFactory;
|
|
2345
|
+
this.workflowSnapshotFactory = workflowSnapshotFactory;
|
|
2346
|
+
this.planningFactory = planningFactory;
|
|
2347
|
+
this.nodeStatePublisherFactory = nodeStatePublisherFactory;
|
|
2348
|
+
this.runExecutionContextFactory = runExecutionContextFactory;
|
|
2349
|
+
this.nodeActivationRequestComposer = nodeActivationRequestComposer;
|
|
2350
|
+
this.activationEnqueueService = activationEnqueueService;
|
|
2351
|
+
this.semantics = semantics;
|
|
2352
|
+
this.waiters = waiters;
|
|
2353
|
+
this.workflowPolicyRuntimeDefaults = workflowPolicyRuntimeDefaults;
|
|
2354
|
+
this.executionLimitsPolicy = executionLimitsPolicy;
|
|
2355
|
+
}
|
|
2356
|
+
async runWorkflow(workflow, startAt, items, parent, executionOptions, persistedStateOverrides) {
|
|
2357
|
+
const runId = this.runIdFactory.makeRunId();
|
|
2358
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2359
|
+
const workflowSnapshot = persistedStateOverrides?.workflowSnapshot ?? this.workflowSnapshotFactory.create(workflow);
|
|
2360
|
+
const mutableState = persistedStateOverrides?.mutableState;
|
|
2361
|
+
const policySnapshot = RunPolicySnapshotFactory.create(workflow, this.workflowPolicyRuntimeDefaults);
|
|
2362
|
+
const mergedExecutionOptions = this.executionLimitsPolicy.mergeExecutionOptionsForNewRun(parent, executionOptions);
|
|
2363
|
+
await this.workflowExecutionRepository.createRun({
|
|
2364
|
+
runId,
|
|
2365
|
+
workflowId: workflow.id,
|
|
2366
|
+
startedAt,
|
|
2367
|
+
parent,
|
|
2368
|
+
executionOptions: mergedExecutionOptions,
|
|
2369
|
+
workflowSnapshot,
|
|
2370
|
+
mutableState,
|
|
2371
|
+
policySnapshot,
|
|
2372
|
+
engineCounters: { completedNodeActivations: 0 }
|
|
2373
|
+
});
|
|
2374
|
+
const data = this.runDataFactory.create();
|
|
2375
|
+
const base = this.runExecutionContextFactory.create({
|
|
2376
|
+
runId,
|
|
2377
|
+
workflowId: workflow.id,
|
|
2378
|
+
nodeId: startAt,
|
|
2379
|
+
parent,
|
|
2380
|
+
subworkflowDepth: mergedExecutionOptions.subworkflowDepth ?? 0,
|
|
2381
|
+
engineMaxNodeActivations: mergedExecutionOptions.maxNodeActivations,
|
|
2382
|
+
engineMaxSubworkflowDepth: mergedExecutionOptions.maxSubworkflowDepth,
|
|
2383
|
+
data,
|
|
2384
|
+
nodeState: this.nodeStatePublisherFactory.create(runId, workflow.id, parent)
|
|
2385
|
+
});
|
|
2386
|
+
const { topology, planner } = this.planningFactory.create(workflow);
|
|
2387
|
+
const startDefinition = topology.defsById.get(startAt);
|
|
2388
|
+
if (!startDefinition) throw new Error(`Unknown start nodeId: ${startAt}`);
|
|
2389
|
+
const initialNodeSnapshotsByNodeId = {};
|
|
2390
|
+
if (startDefinition.kind === "trigger") {
|
|
2391
|
+
const request = this.nodeActivationRequestComposer.createSingleFromDefinition({
|
|
2392
|
+
runId,
|
|
2393
|
+
workflowId: workflow.id,
|
|
2394
|
+
definition: startDefinition,
|
|
2395
|
+
parent,
|
|
2396
|
+
executionOptions: mergedExecutionOptions,
|
|
2397
|
+
batchId: "batch_1",
|
|
2398
|
+
input: items,
|
|
2399
|
+
base,
|
|
2400
|
+
data
|
|
2401
|
+
});
|
|
2402
|
+
return await this.activationEnqueueService.enqueueActivation({
|
|
2403
|
+
runId,
|
|
2404
|
+
workflowId: workflow.id,
|
|
2405
|
+
startedAt,
|
|
2406
|
+
parent,
|
|
2407
|
+
executionOptions: mergedExecutionOptions,
|
|
2408
|
+
workflowSnapshot,
|
|
2409
|
+
mutableState,
|
|
2410
|
+
policySnapshot,
|
|
2411
|
+
control: void 0,
|
|
2412
|
+
pendingQueue: [],
|
|
2413
|
+
request,
|
|
2414
|
+
previousNodeSnapshotsByNodeId: initialNodeSnapshotsByNodeId,
|
|
2415
|
+
planner,
|
|
2416
|
+
engineCounters: { completedNodeActivations: 0 },
|
|
2417
|
+
connectionInvocations: []
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
const queue = [{
|
|
2421
|
+
nodeId: startAt,
|
|
2422
|
+
input: items,
|
|
2423
|
+
toInput: "in",
|
|
2424
|
+
batchId: "batch_1"
|
|
2425
|
+
}];
|
|
2426
|
+
return await this.scheduleQueuedPlan({
|
|
2427
|
+
runId,
|
|
2428
|
+
workflowId: workflow.id,
|
|
2429
|
+
startedAt,
|
|
2430
|
+
parent,
|
|
2431
|
+
executionOptions: mergedExecutionOptions,
|
|
2432
|
+
control: void 0,
|
|
2433
|
+
workflowSnapshot,
|
|
2434
|
+
mutableState,
|
|
2435
|
+
policySnapshot,
|
|
2436
|
+
workflow,
|
|
2437
|
+
planner,
|
|
2438
|
+
queue,
|
|
2439
|
+
base,
|
|
2440
|
+
data,
|
|
2441
|
+
nodeSnapshotsByNodeId: initialNodeSnapshotsByNodeId,
|
|
2442
|
+
connectionInvocations: []
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
async runWorkflowFromState(request) {
|
|
2446
|
+
const runId = this.runIdFactory.makeRunId();
|
|
2447
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2448
|
+
const workflowSnapshot = request.workflowSnapshot ?? this.workflowSnapshotFactory.create(request.workflow);
|
|
2449
|
+
const mutableState = request.mutableState ?? request.currentState?.mutableState;
|
|
2450
|
+
const policySnapshot = RunPolicySnapshotFactory.create(request.workflow, this.workflowPolicyRuntimeDefaults);
|
|
2451
|
+
const control = { stopCondition: request.stopCondition ?? { kind: "workflowCompleted" } };
|
|
2452
|
+
const mergedExecutionOptions = this.executionLimitsPolicy.mergeExecutionOptionsForNewRun(request.parent, request.executionOptions);
|
|
2453
|
+
await this.workflowExecutionRepository.createRun({
|
|
2454
|
+
runId,
|
|
2455
|
+
workflowId: request.workflow.id,
|
|
2456
|
+
startedAt,
|
|
2457
|
+
parent: request.parent,
|
|
2458
|
+
executionOptions: mergedExecutionOptions,
|
|
2459
|
+
control,
|
|
2460
|
+
workflowSnapshot,
|
|
2461
|
+
mutableState,
|
|
2462
|
+
policySnapshot,
|
|
2463
|
+
engineCounters: { completedNodeActivations: 0 }
|
|
2464
|
+
});
|
|
2465
|
+
const { topology, planner } = this.planningFactory.create(request.workflow);
|
|
2466
|
+
const plan = CurrentStateFrontierPlanner.createFromTopology(topology).plan({
|
|
2467
|
+
currentState: this.createRunCurrentState(request.currentState, mutableState),
|
|
2468
|
+
stopCondition: control.stopCondition,
|
|
2469
|
+
reset: request.reset,
|
|
2470
|
+
items: request.items
|
|
2471
|
+
});
|
|
2472
|
+
const data = this.runDataFactory.create(plan.currentState.outputsByNode);
|
|
2473
|
+
const base = this.runExecutionContextFactory.create({
|
|
2474
|
+
runId,
|
|
2475
|
+
workflowId: request.workflow.id,
|
|
2476
|
+
nodeId: WorkflowExecutableNodeClassifierFactory.create(request.workflow).firstExecutableNodeIdInDefinitionOrder(request.workflow) ?? "unknown_node",
|
|
2477
|
+
parent: request.parent,
|
|
2478
|
+
subworkflowDepth: mergedExecutionOptions.subworkflowDepth ?? 0,
|
|
2479
|
+
engineMaxNodeActivations: mergedExecutionOptions.maxNodeActivations,
|
|
2480
|
+
engineMaxSubworkflowDepth: mergedExecutionOptions.maxSubworkflowDepth,
|
|
2481
|
+
data,
|
|
2482
|
+
nodeState: this.nodeStatePublisherFactory.create(runId, request.workflow.id, request.parent)
|
|
2483
|
+
});
|
|
2484
|
+
return await this.scheduleInitialPlan({
|
|
2485
|
+
runId,
|
|
2486
|
+
startedAt,
|
|
2487
|
+
workflow: request.workflow,
|
|
2488
|
+
workflowSnapshot,
|
|
2489
|
+
mutableState,
|
|
2490
|
+
policySnapshot,
|
|
2491
|
+
executionOptions: mergedExecutionOptions,
|
|
2492
|
+
control,
|
|
2493
|
+
parent: request.parent,
|
|
2494
|
+
planner,
|
|
2495
|
+
plan,
|
|
2496
|
+
base,
|
|
2497
|
+
data
|
|
2498
|
+
});
|
|
2499
|
+
}
|
|
2500
|
+
createRunCurrentState(currentState, mutableState) {
|
|
2501
|
+
return {
|
|
2502
|
+
outputsByNode: { ...currentState?.outputsByNode ?? {} },
|
|
2503
|
+
nodeSnapshotsByNodeId: { ...currentState?.nodeSnapshotsByNodeId ?? {} },
|
|
2504
|
+
connectionInvocations: currentState?.connectionInvocations ? [...currentState.connectionInvocations] : void 0,
|
|
2505
|
+
mutableState: mutableState ?? currentState?.mutableState
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
async scheduleInitialPlan(args) {
|
|
2509
|
+
const initialNodeSnapshotsByNodeId = this.semantics.applySkippedSnapshots({
|
|
2510
|
+
runId: args.runId,
|
|
2511
|
+
workflowId: args.workflow.id,
|
|
2512
|
+
parent: args.parent,
|
|
2513
|
+
currentState: args.plan.currentState,
|
|
2514
|
+
skippedNodeIds: args.plan.skippedNodeIds,
|
|
2515
|
+
preservedPinnedNodeIds: args.plan.preservedPinnedNodeIds,
|
|
2516
|
+
finishedAt: args.startedAt
|
|
2517
|
+
});
|
|
2518
|
+
if (args.plan.rootNodeId) {
|
|
2519
|
+
const startDef = WorkflowTopology.fromWorkflow(args.workflow).defsById.get(args.plan.rootNodeId);
|
|
2520
|
+
if (!startDef) throw new Error(`Unknown frontier nodeId: ${args.plan.rootNodeId}`);
|
|
2521
|
+
const startItems = args.plan.rootNodeInput ?? [];
|
|
2522
|
+
const request = this.nodeActivationRequestComposer.createSingleFromDefinition({
|
|
2523
|
+
runId: args.runId,
|
|
2524
|
+
workflowId: args.workflow.id,
|
|
2525
|
+
definition: startDef,
|
|
2526
|
+
parent: args.parent,
|
|
2527
|
+
executionOptions: args.executionOptions,
|
|
2528
|
+
batchId: "batch_1",
|
|
2529
|
+
input: startItems,
|
|
2530
|
+
base: args.base,
|
|
2531
|
+
data: args.data
|
|
2532
|
+
});
|
|
2533
|
+
return await this.activationEnqueueService.enqueueActivation({
|
|
2534
|
+
runId: args.runId,
|
|
2535
|
+
workflowId: args.workflow.id,
|
|
2536
|
+
startedAt: args.startedAt,
|
|
2537
|
+
parent: args.parent,
|
|
2538
|
+
executionOptions: args.executionOptions,
|
|
2539
|
+
control: args.control,
|
|
2540
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
2541
|
+
mutableState: args.mutableState,
|
|
2542
|
+
policySnapshot: args.policySnapshot,
|
|
2543
|
+
pendingQueue: [],
|
|
2544
|
+
request,
|
|
2545
|
+
previousNodeSnapshotsByNodeId: initialNodeSnapshotsByNodeId,
|
|
2546
|
+
planner: args.planner,
|
|
2547
|
+
engineCounters: { completedNodeActivations: 0 },
|
|
2548
|
+
connectionInvocations: args.plan.currentState.connectionInvocations ?? []
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
return await this.scheduleQueuedPlan({
|
|
2552
|
+
runId: args.runId,
|
|
2553
|
+
workflowId: args.workflow.id,
|
|
2554
|
+
startedAt: args.startedAt,
|
|
2555
|
+
parent: args.parent,
|
|
2556
|
+
executionOptions: args.executionOptions,
|
|
2557
|
+
control: args.control,
|
|
2558
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
2559
|
+
mutableState: args.mutableState,
|
|
2560
|
+
policySnapshot: args.policySnapshot,
|
|
2561
|
+
workflow: args.workflow,
|
|
2562
|
+
planner: args.planner,
|
|
2563
|
+
queue: [...args.plan.queue],
|
|
2564
|
+
base: args.base,
|
|
2565
|
+
data: args.data,
|
|
2566
|
+
nodeSnapshotsByNodeId: initialNodeSnapshotsByNodeId,
|
|
2567
|
+
connectionInvocations: args.plan.currentState.connectionInvocations ?? []
|
|
2568
|
+
});
|
|
2569
|
+
}
|
|
2570
|
+
async scheduleQueuedPlan(args) {
|
|
2571
|
+
this.semantics.applyPinnedQueueSkips({
|
|
2572
|
+
runId: args.runId,
|
|
2573
|
+
workflowId: args.workflowId,
|
|
2574
|
+
parent: args.parent,
|
|
2575
|
+
mutableState: args.mutableState,
|
|
2576
|
+
planner: args.planner,
|
|
2577
|
+
queue: args.queue,
|
|
2578
|
+
data: args.data,
|
|
2579
|
+
nodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId,
|
|
2580
|
+
finishedAt: args.startedAt
|
|
2581
|
+
});
|
|
2582
|
+
const next = args.planner.nextActivation(args.queue);
|
|
2583
|
+
if (!next) return await this.completeRun({
|
|
2584
|
+
runId: args.runId,
|
|
2585
|
+
workflowId: args.workflowId,
|
|
2586
|
+
startedAt: args.startedAt,
|
|
2587
|
+
parent: args.parent,
|
|
2588
|
+
executionOptions: args.executionOptions,
|
|
2589
|
+
control: args.control,
|
|
2590
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
2591
|
+
mutableState: args.mutableState,
|
|
2592
|
+
policySnapshot: args.policySnapshot,
|
|
2593
|
+
workflow: args.workflow,
|
|
2594
|
+
data: args.data,
|
|
2595
|
+
nodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId,
|
|
2596
|
+
connectionInvocations: args.connectionInvocations
|
|
2597
|
+
});
|
|
2598
|
+
const definition = WorkflowTopology.fromWorkflow(args.workflow).defsById.get(next.nodeId);
|
|
2599
|
+
if (!definition || definition.kind !== "node") throw new Error(`Node ${next.nodeId} is not a runnable node`);
|
|
2600
|
+
const request = this.nodeActivationRequestComposer.createFromPlannedActivation({
|
|
2601
|
+
next,
|
|
2602
|
+
base: args.base,
|
|
2603
|
+
data: args.data,
|
|
2604
|
+
runId: args.runId,
|
|
2605
|
+
workflowId: args.workflowId,
|
|
2606
|
+
parent: args.parent,
|
|
2607
|
+
executionOptions: args.executionOptions,
|
|
2608
|
+
nodeDefinition: definition
|
|
2609
|
+
});
|
|
2610
|
+
return await this.activationEnqueueService.enqueueActivation({
|
|
2611
|
+
runId: args.runId,
|
|
2612
|
+
workflowId: args.workflowId,
|
|
2613
|
+
startedAt: args.startedAt,
|
|
2614
|
+
parent: args.parent,
|
|
2615
|
+
executionOptions: args.executionOptions,
|
|
2616
|
+
control: args.control,
|
|
2617
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
2618
|
+
mutableState: args.mutableState,
|
|
2619
|
+
policySnapshot: args.policySnapshot,
|
|
2620
|
+
pendingQueue: args.queue,
|
|
2621
|
+
request,
|
|
2622
|
+
previousNodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId,
|
|
2623
|
+
planner: args.planner,
|
|
2624
|
+
engineCounters: { completedNodeActivations: 0 },
|
|
2625
|
+
connectionInvocations: args.connectionInvocations ?? []
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
async completeRun(args) {
|
|
2629
|
+
await this.workflowExecutionRepository.save({
|
|
2630
|
+
runId: args.runId,
|
|
2631
|
+
workflowId: args.workflowId,
|
|
2632
|
+
startedAt: args.startedAt,
|
|
2633
|
+
parent: args.parent,
|
|
2634
|
+
executionOptions: args.executionOptions,
|
|
2635
|
+
control: args.control,
|
|
2636
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
2637
|
+
mutableState: args.mutableState,
|
|
2638
|
+
policySnapshot: args.policySnapshot,
|
|
2639
|
+
engineCounters: { completedNodeActivations: 0 },
|
|
2640
|
+
connectionInvocations: args.connectionInvocations ? [...args.connectionInvocations] : [],
|
|
2641
|
+
status: "completed",
|
|
2642
|
+
pending: void 0,
|
|
2643
|
+
queue: [],
|
|
2644
|
+
outputsByNode: args.data.dump(),
|
|
2645
|
+
nodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId
|
|
2646
|
+
});
|
|
2647
|
+
const result = {
|
|
2648
|
+
runId: args.runId,
|
|
2649
|
+
workflowId: args.workflowId,
|
|
2650
|
+
startedAt: args.startedAt,
|
|
2651
|
+
status: "completed",
|
|
2652
|
+
outputs: this.semantics.resolveResultOutputs(args.workflow, args.control?.stopCondition, args.data.dump())
|
|
2653
|
+
};
|
|
2654
|
+
this.waiters.resolveRunCompletion(result);
|
|
2655
|
+
return result;
|
|
2656
|
+
}
|
|
2657
|
+
};
|
|
2658
|
+
|
|
2659
|
+
//#endregion
|
|
2660
|
+
//#region src/scheduler/ConfigDrivenOffloadPolicy.ts
|
|
2661
|
+
var ConfigDrivenOffloadPolicy = class {
|
|
2662
|
+
defaultMode;
|
|
2663
|
+
constructor(defaultMode = "worker") {
|
|
2664
|
+
this.defaultMode = defaultMode;
|
|
2665
|
+
}
|
|
2666
|
+
decide(args) {
|
|
2667
|
+
const hint = args.config.execution?.hint;
|
|
2668
|
+
const queue = args.config.execution?.queue;
|
|
2669
|
+
if (hint === "local") return { mode: "local" };
|
|
2670
|
+
if (hint === "worker") return {
|
|
2671
|
+
mode: "worker",
|
|
2672
|
+
queue
|
|
2673
|
+
};
|
|
2674
|
+
if (queue) return {
|
|
2675
|
+
mode: "worker",
|
|
2676
|
+
queue
|
|
2677
|
+
};
|
|
2678
|
+
return { mode: this.defaultMode };
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
|
|
2682
|
+
//#endregion
|
|
2683
|
+
//#region src/scheduler/DefaultDrivingScheduler.ts
|
|
2684
|
+
var DefaultDrivingScheduler = class {
|
|
2685
|
+
constructor(offloadPolicy, workerScheduler, inline) {
|
|
2686
|
+
this.offloadPolicy = offloadPolicy;
|
|
2687
|
+
this.workerScheduler = workerScheduler;
|
|
2688
|
+
this.inline = inline;
|
|
2689
|
+
}
|
|
2690
|
+
setContinuation(continuation) {
|
|
2691
|
+
this.inline.setContinuation(continuation);
|
|
2692
|
+
}
|
|
2693
|
+
async enqueue(request) {
|
|
2694
|
+
const selection = await this.selectScheduler(request);
|
|
2695
|
+
if (selection.mode === "worker") {
|
|
2696
|
+
if (request.kind === "multi") throw new Error(`Multi-input node ${request.nodeId} cannot be scheduled to worker (insert local placement)`);
|
|
2697
|
+
const workerRequest = {
|
|
2698
|
+
runId: request.runId,
|
|
2699
|
+
activationId: request.activationId,
|
|
2700
|
+
workflowId: request.workflowId,
|
|
2701
|
+
nodeId: request.nodeId,
|
|
2702
|
+
input: request.input,
|
|
2703
|
+
parent: request.parent,
|
|
2704
|
+
queue: selection.queue,
|
|
2705
|
+
executionOptions: request.executionOptions
|
|
2706
|
+
};
|
|
2707
|
+
return {
|
|
2708
|
+
receiptId: (await this.workerScheduler.enqueue(workerRequest)).receiptId,
|
|
2709
|
+
mode: "worker",
|
|
2710
|
+
queue: selection.queue
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
return await this.enqueueInline(request);
|
|
2714
|
+
}
|
|
2715
|
+
notifyPendingStatePersisted(runId) {
|
|
2716
|
+
this.inline.notifyPendingStatePersisted(runId);
|
|
2717
|
+
}
|
|
2718
|
+
/**
|
|
2719
|
+
* Scheduler precedence is explicit:
|
|
2720
|
+
* 1. run-intent override (`executionOptions.localOnly`)
|
|
2721
|
+
* 2. node-level execution hint / queue policy
|
|
2722
|
+
* 3. container-default scheduler policy fallback
|
|
2723
|
+
*/
|
|
2724
|
+
async selectScheduler(request) {
|
|
2725
|
+
if (request.executionOptions?.localOnly) return {
|
|
2726
|
+
mode: "local",
|
|
2727
|
+
decision: "runIntentOverride"
|
|
2728
|
+
};
|
|
2729
|
+
const decision = this.offloadPolicy.decide({
|
|
2730
|
+
workflowId: request.workflowId,
|
|
2731
|
+
nodeId: request.nodeId,
|
|
2732
|
+
config: request.ctx.config
|
|
2733
|
+
});
|
|
2734
|
+
if (this.hasNodeSchedulingPreference(request)) return {
|
|
2735
|
+
mode: decision.mode,
|
|
2736
|
+
queue: decision.queue,
|
|
2737
|
+
decision: "nodePolicy"
|
|
2738
|
+
};
|
|
2739
|
+
return {
|
|
2740
|
+
mode: decision.mode,
|
|
2741
|
+
queue: decision.queue,
|
|
2742
|
+
decision: "containerDefault"
|
|
2743
|
+
};
|
|
2744
|
+
}
|
|
2745
|
+
hasNodeSchedulingPreference(request) {
|
|
2746
|
+
return request.ctx.config.execution?.hint !== void 0 || request.ctx.config.execution?.queue !== void 0;
|
|
2747
|
+
}
|
|
2748
|
+
async enqueueInline(request) {
|
|
2749
|
+
return {
|
|
2750
|
+
...await this.inline.enqueue(request),
|
|
2751
|
+
mode: "local"
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
|
|
2756
|
+
//#endregion
|
|
2757
|
+
//#region src/scheduler/HintOnlyOffloadPolicy.ts
|
|
2758
|
+
var HintOnlyOffloadPolicy = class {
|
|
2759
|
+
decide(args) {
|
|
2760
|
+
if (args.config.execution?.hint === "worker") return {
|
|
2761
|
+
mode: "worker",
|
|
2762
|
+
queue: args.config.execution?.queue
|
|
2763
|
+
};
|
|
2764
|
+
return { mode: "local" };
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
|
|
2768
|
+
//#endregion
|
|
2769
|
+
//#region src/scheduler/InlineDrivingScheduler.ts
|
|
2770
|
+
var InlineDrivingScheduler = class {
|
|
2771
|
+
continuation;
|
|
2772
|
+
drainingRuns = /* @__PURE__ */ new Set();
|
|
2773
|
+
queuesByRunId = /* @__PURE__ */ new Map();
|
|
2774
|
+
scheduledRuns = /* @__PURE__ */ new Set();
|
|
2775
|
+
seq = 0;
|
|
2776
|
+
constructor(nodeExecutor) {
|
|
2777
|
+
this.nodeExecutor = nodeExecutor;
|
|
2778
|
+
}
|
|
2779
|
+
setContinuation(continuation) {
|
|
2780
|
+
this.continuation = continuation;
|
|
2781
|
+
}
|
|
2782
|
+
async enqueue(request) {
|
|
2783
|
+
const receipt = {
|
|
2784
|
+
receiptId: `inline_${++this.seq}`,
|
|
2785
|
+
mode: "local"
|
|
2786
|
+
};
|
|
2787
|
+
const q = this.queuesByRunId.get(request.runId) ?? [];
|
|
2788
|
+
q.push({
|
|
2789
|
+
request,
|
|
2790
|
+
receipt
|
|
2791
|
+
});
|
|
2792
|
+
this.queuesByRunId.set(request.runId, q);
|
|
2793
|
+
return receipt;
|
|
2794
|
+
}
|
|
2795
|
+
notifyPendingStatePersisted(runId) {
|
|
2796
|
+
if ((this.queuesByRunId.get(runId)?.length ?? 0) === 0) return;
|
|
2797
|
+
this.scheduleDrain(runId);
|
|
2798
|
+
}
|
|
2799
|
+
async drainRun(runId) {
|
|
2800
|
+
if (this.drainingRuns.has(runId)) return;
|
|
2801
|
+
this.drainingRuns.add(runId);
|
|
2802
|
+
this.scheduledRuns.delete(runId);
|
|
2803
|
+
try {
|
|
2804
|
+
const q = this.queuesByRunId.get(runId) ?? [];
|
|
2805
|
+
while (q.length > 0) {
|
|
2806
|
+
const { request } = q.shift();
|
|
2807
|
+
const cont = this.continuation;
|
|
2808
|
+
if (!cont) throw new Error("InlineDrivingScheduler is missing a continuation (setContinuation was not called)");
|
|
2809
|
+
await cont.markNodeRunning({
|
|
2810
|
+
runId: request.runId,
|
|
2811
|
+
activationId: request.activationId,
|
|
2812
|
+
nodeId: request.nodeId,
|
|
2813
|
+
inputsByPort: request.kind === "multi" ? request.inputsByPort : { in: request.input }
|
|
2814
|
+
});
|
|
2815
|
+
let outputs;
|
|
2816
|
+
try {
|
|
2817
|
+
outputs = await this.nodeExecutor.execute(request);
|
|
2818
|
+
} catch (e) {
|
|
2819
|
+
await this.resumeAfterExecutionError(cont, request, this.asError(e));
|
|
2820
|
+
continue;
|
|
2821
|
+
}
|
|
2822
|
+
await this.resumeAfterExecutionResult(cont, request, outputs ?? {});
|
|
2823
|
+
}
|
|
2824
|
+
} finally {
|
|
2825
|
+
if ((this.queuesByRunId.get(runId)?.length ?? 0) === 0) this.queuesByRunId.delete(runId);
|
|
2826
|
+
this.drainingRuns.delete(runId);
|
|
2827
|
+
if ((this.queuesByRunId.get(runId)?.length ?? 0) > 0) this.scheduleDrain(runId);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
scheduleDrain(runId) {
|
|
2831
|
+
if (this.drainingRuns.has(runId) || this.scheduledRuns.has(runId)) return;
|
|
2832
|
+
this.scheduledRuns.add(runId);
|
|
2833
|
+
setTimeout(() => {
|
|
2834
|
+
this.scheduledRuns.delete(runId);
|
|
2835
|
+
this.drainRun(runId);
|
|
2836
|
+
}, 0);
|
|
2837
|
+
}
|
|
2838
|
+
async resumeAfterExecutionResult(continuation, request, outputs) {
|
|
2839
|
+
try {
|
|
2840
|
+
await continuation.resumeFromNodeResult({
|
|
2841
|
+
runId: request.runId,
|
|
2842
|
+
activationId: request.activationId,
|
|
2843
|
+
nodeId: request.nodeId,
|
|
2844
|
+
outputs
|
|
2845
|
+
});
|
|
2846
|
+
} catch (e) {
|
|
2847
|
+
this.rethrowUnlessIgnorableContinuationError(e);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
async resumeAfterExecutionError(continuation, request, error) {
|
|
2851
|
+
try {
|
|
2852
|
+
await continuation.resumeFromNodeError({
|
|
2853
|
+
runId: request.runId,
|
|
2854
|
+
activationId: request.activationId,
|
|
2855
|
+
nodeId: request.nodeId,
|
|
2856
|
+
error
|
|
2857
|
+
});
|
|
2858
|
+
} catch (e) {
|
|
2859
|
+
this.rethrowUnlessIgnorableContinuationError(e);
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
asError(e) {
|
|
2863
|
+
return e instanceof Error ? e : new Error(String(e));
|
|
2864
|
+
}
|
|
2865
|
+
rethrowUnlessIgnorableContinuationError(e) {
|
|
2866
|
+
if (this.isIgnorableContinuationError(e)) return;
|
|
2867
|
+
throw this.asError(e);
|
|
2868
|
+
}
|
|
2869
|
+
isIgnorableContinuationError(e) {
|
|
2870
|
+
const message = this.asError(e).message;
|
|
2871
|
+
return message.includes(" is not pending") || message.includes("activationId mismatch") || message.includes("nodeId mismatch");
|
|
2872
|
+
}
|
|
2873
|
+
};
|
|
2874
|
+
|
|
2875
|
+
//#endregion
|
|
2876
|
+
//#region src/scheduler/LocalOnlyScheduler.ts
|
|
2877
|
+
var LocalOnlyScheduler = class {
|
|
2878
|
+
async enqueue(_request) {
|
|
2879
|
+
throw new Error("No worker scheduler configured");
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
|
|
2883
|
+
//#endregion
|
|
2884
|
+
//#region src/policies/executionLimits/EngineExecutionLimitsPolicy.ts
|
|
2885
|
+
/** Framework defaults for {@link EngineExecutionLimitsPolicy} (merged with host `runtime.engineExecutionLimits`). */
|
|
2886
|
+
const ENGINE_EXECUTION_LIMITS_DEFAULTS = {
|
|
2887
|
+
defaultMaxNodeActivations: 1e5,
|
|
2888
|
+
hardMaxNodeActivations: 1e5,
|
|
2889
|
+
defaultMaxSubworkflowDepth: 32,
|
|
2890
|
+
hardMaxSubworkflowDepth: 32
|
|
2891
|
+
};
|
|
2892
|
+
/**
|
|
2893
|
+
* Resolves per-run execution limits: defaults, hard ceilings, and subworkflow depth for new runs.
|
|
2894
|
+
*/
|
|
2895
|
+
var EngineExecutionLimitsPolicy = class {
|
|
2896
|
+
constructor(config = ENGINE_EXECUTION_LIMITS_DEFAULTS) {
|
|
2897
|
+
this.config = config;
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Effective options for a new root run (depth 0): defaults merged with engine ceilings.
|
|
2901
|
+
* Replaces a separate one-method factory for root-run bootstrap.
|
|
2902
|
+
*/
|
|
2903
|
+
createRootExecutionOptions() {
|
|
2904
|
+
return this.mergeExecutionOptionsForNewRun(void 0, void 0);
|
|
2905
|
+
}
|
|
2906
|
+
mergeExecutionOptionsForNewRun(parent, user) {
|
|
2907
|
+
const subworkflowDepth = parent === void 0 ? 0 : (parent.subworkflowDepth ?? 0) + 1;
|
|
2908
|
+
const inheritedMaxNode = parent?.engineMaxNodeActivations;
|
|
2909
|
+
const inheritedMaxSub = parent?.engineMaxSubworkflowDepth;
|
|
2910
|
+
const maxNodeActivations = this.capNumber(user?.maxNodeActivations ?? inheritedMaxNode, this.config.defaultMaxNodeActivations, this.config.hardMaxNodeActivations);
|
|
2911
|
+
const maxSubworkflowDepth = this.capNumber(user?.maxSubworkflowDepth ?? inheritedMaxSub, this.config.defaultMaxSubworkflowDepth, this.config.hardMaxSubworkflowDepth);
|
|
2912
|
+
if (subworkflowDepth > maxSubworkflowDepth) throw new Error(`Subworkflow nesting depth ${subworkflowDepth} exceeds maxSubworkflowDepth ${maxSubworkflowDepth} (run would be a child of parent run).`);
|
|
2913
|
+
return {
|
|
2914
|
+
...user,
|
|
2915
|
+
subworkflowDepth,
|
|
2916
|
+
maxNodeActivations,
|
|
2917
|
+
maxSubworkflowDepth
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2920
|
+
capNumber(requested, defaultValue, hardCeiling) {
|
|
2921
|
+
const base = requested === void 0 ? defaultValue : requested;
|
|
2922
|
+
return Math.min(base, hardCeiling);
|
|
2923
|
+
}
|
|
2924
|
+
};
|
|
2925
|
+
|
|
2926
|
+
//#endregion
|
|
2927
|
+
//#region src/runStorage/BinaryBodyBufferReader.ts
|
|
2928
|
+
var BinaryBodyBufferReader = class {
|
|
2929
|
+
async read(body) {
|
|
2930
|
+
if (body instanceof Uint8Array) return body;
|
|
2931
|
+
if (body instanceof ArrayBuffer) return new Uint8Array(body);
|
|
2932
|
+
if (body instanceof node_stream_web.ReadableStream) return await this.readReadableStream(body);
|
|
2933
|
+
return await this.readAsyncIterable(body);
|
|
2934
|
+
}
|
|
2935
|
+
async readReadableStream(body) {
|
|
2936
|
+
const reader = body.getReader();
|
|
2937
|
+
const chunks = [];
|
|
2938
|
+
let totalSize = 0;
|
|
2939
|
+
try {
|
|
2940
|
+
while (true) {
|
|
2941
|
+
const result = await reader.read();
|
|
2942
|
+
if (result.done) break;
|
|
2943
|
+
chunks.push(result.value);
|
|
2944
|
+
totalSize += result.value.byteLength;
|
|
2945
|
+
}
|
|
2946
|
+
} finally {
|
|
2947
|
+
reader.releaseLock();
|
|
2948
|
+
}
|
|
2949
|
+
return this.joinChunks(chunks, totalSize);
|
|
2950
|
+
}
|
|
2951
|
+
async readAsyncIterable(body) {
|
|
2952
|
+
const chunks = [];
|
|
2953
|
+
let totalSize = 0;
|
|
2954
|
+
for await (const chunk of body) {
|
|
2955
|
+
chunks.push(chunk);
|
|
2956
|
+
totalSize += chunk.byteLength;
|
|
2957
|
+
}
|
|
2958
|
+
return this.joinChunks(chunks, totalSize);
|
|
2959
|
+
}
|
|
2960
|
+
joinChunks(chunks, totalSize) {
|
|
2961
|
+
const bytes = new Uint8Array(totalSize);
|
|
2962
|
+
let offset = 0;
|
|
2963
|
+
for (const chunk of chunks) {
|
|
2964
|
+
bytes.set(chunk, offset);
|
|
2965
|
+
offset += chunk.byteLength;
|
|
2966
|
+
}
|
|
2967
|
+
return bytes;
|
|
2968
|
+
}
|
|
2969
|
+
};
|
|
2970
|
+
|
|
2971
|
+
//#endregion
|
|
2972
|
+
//#region src/runStorage/BinaryBodyReadableStreamFactory.ts
|
|
2973
|
+
var BinaryBodyReadableStreamFactory = class {
|
|
2974
|
+
constructor(bytes) {
|
|
2975
|
+
this.bytes = bytes;
|
|
2976
|
+
}
|
|
2977
|
+
create() {
|
|
2978
|
+
const value = this.bytes;
|
|
2979
|
+
let consumed = false;
|
|
2980
|
+
return new node_stream_web.ReadableStream({ pull(controller) {
|
|
2981
|
+
if (consumed) {
|
|
2982
|
+
controller.close();
|
|
2983
|
+
return;
|
|
2984
|
+
}
|
|
2985
|
+
consumed = true;
|
|
2986
|
+
controller.enqueue(value);
|
|
2987
|
+
controller.close();
|
|
2988
|
+
} });
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
|
|
2992
|
+
//#endregion
|
|
2993
|
+
//#region src/runStorage/InMemoryBinaryStorageRegistry.ts
|
|
2994
|
+
var InMemoryBinaryStorage = class {
|
|
2995
|
+
driverName = "memory";
|
|
2996
|
+
values = /* @__PURE__ */ new Map();
|
|
2997
|
+
async write(args) {
|
|
2998
|
+
const bytes = await new BinaryBodyBufferReader().read(args.body);
|
|
2999
|
+
this.values.set(args.storageKey, bytes);
|
|
3000
|
+
return {
|
|
3001
|
+
storageKey: args.storageKey,
|
|
3002
|
+
size: bytes.byteLength,
|
|
3003
|
+
sha256: (0, node_crypto.createHash)("sha256").update(bytes).digest("hex")
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
async openReadStream(storageKey) {
|
|
3007
|
+
const bytes = this.values.get(storageKey);
|
|
3008
|
+
if (!bytes) return;
|
|
3009
|
+
return {
|
|
3010
|
+
body: new BinaryBodyReadableStreamFactory(bytes).create(),
|
|
3011
|
+
size: bytes.byteLength
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
async stat(storageKey) {
|
|
3015
|
+
const bytes = this.values.get(storageKey);
|
|
3016
|
+
if (!bytes) return { exists: false };
|
|
3017
|
+
return {
|
|
3018
|
+
exists: true,
|
|
3019
|
+
size: bytes.byteLength
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
async delete(storageKey) {
|
|
3023
|
+
this.values.delete(storageKey);
|
|
3024
|
+
}
|
|
3025
|
+
};
|
|
3026
|
+
|
|
3027
|
+
//#endregion
|
|
3028
|
+
//#region src/runStorage/InMemoryRunData.ts
|
|
3029
|
+
var InMemoryRunData = class {
|
|
3030
|
+
byNode = /* @__PURE__ */ new Map();
|
|
3031
|
+
constructor(initial) {
|
|
3032
|
+
if (initial) for (const [nodeId, outputs] of Object.entries(initial)) this.byNode.set(nodeId, outputs);
|
|
3033
|
+
}
|
|
3034
|
+
setOutputs(nodeId, outputs) {
|
|
3035
|
+
this.byNode.set(nodeId, outputs);
|
|
3036
|
+
}
|
|
3037
|
+
getOutputs(nodeId) {
|
|
3038
|
+
return this.byNode.get(nodeId);
|
|
3039
|
+
}
|
|
3040
|
+
getOutputItems(nodeId, output = "main") {
|
|
3041
|
+
return this.byNode.get(nodeId)?.[output] ?? [];
|
|
3042
|
+
}
|
|
3043
|
+
getOutputItem(nodeId, itemIndex, output = "main") {
|
|
3044
|
+
return this.getOutputItems(nodeId, output)[itemIndex];
|
|
3045
|
+
}
|
|
3046
|
+
dump() {
|
|
3047
|
+
const out = {};
|
|
3048
|
+
for (const [nodeId, outputs] of this.byNode.entries()) out[nodeId] = outputs;
|
|
3049
|
+
return out;
|
|
3050
|
+
}
|
|
3051
|
+
};
|
|
3052
|
+
|
|
3053
|
+
//#endregion
|
|
3054
|
+
//#region src/runStorage/InMemoryRunDataFactory.ts
|
|
3055
|
+
var InMemoryRunDataFactory = class {
|
|
3056
|
+
create(initial) {
|
|
3057
|
+
return new InMemoryRunData(initial);
|
|
3058
|
+
}
|
|
3059
|
+
};
|
|
3060
|
+
|
|
3061
|
+
//#endregion
|
|
3062
|
+
//#region src/contracts/runFinishedAtFactory.ts
|
|
3063
|
+
/** Derives workflow end time from node snapshots for run listings. */
|
|
3064
|
+
var RunFinishedAtFactory = class {
|
|
3065
|
+
static resolveIso(state) {
|
|
3066
|
+
if (state.status === "running" || state.status === "pending") return;
|
|
3067
|
+
let max;
|
|
3068
|
+
for (const snap of Object.values(state.nodeSnapshotsByNodeId)) if (snap?.finishedAt && (!max || snap.finishedAt > max)) max = snap.finishedAt;
|
|
3069
|
+
return max;
|
|
3070
|
+
}
|
|
3071
|
+
};
|
|
3072
|
+
|
|
3073
|
+
//#endregion
|
|
3074
|
+
//#region src/runtime/RunIntentService.ts
|
|
3075
|
+
var RunIntentService = class {
|
|
3076
|
+
constructor(engine, workflowRepository) {
|
|
3077
|
+
this.engine = engine;
|
|
3078
|
+
this.workflowRepository = workflowRepository;
|
|
3079
|
+
}
|
|
3080
|
+
async startWorkflow(args) {
|
|
3081
|
+
if (args.startAt && !args.currentState && !args.stopCondition && !args.reset) return await this.engine.runWorkflow(args.workflow, args.startAt, args.items, args.parent, args.executionOptions, {
|
|
3082
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
3083
|
+
mutableState: args.mutableState
|
|
3084
|
+
});
|
|
3085
|
+
return await this.engine.runWorkflowFromState({
|
|
3086
|
+
workflow: args.workflow,
|
|
3087
|
+
items: args.items,
|
|
3088
|
+
parent: args.parent,
|
|
3089
|
+
executionOptions: args.executionOptions,
|
|
3090
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
3091
|
+
mutableState: args.mutableState,
|
|
3092
|
+
currentState: args.currentState,
|
|
3093
|
+
stopCondition: args.stopCondition ?? { kind: "workflowCompleted" },
|
|
3094
|
+
reset: args.reset
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
async rerunFromNode(args) {
|
|
3098
|
+
if (args.items) return await this.engine.runWorkflow(args.workflow, args.nodeId, args.items, args.parent, args.executionOptions, {
|
|
3099
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
3100
|
+
mutableState: args.mutableState
|
|
3101
|
+
});
|
|
3102
|
+
return await this.engine.runWorkflowFromState({
|
|
3103
|
+
workflow: args.workflow,
|
|
3104
|
+
parent: args.parent,
|
|
3105
|
+
executionOptions: args.executionOptions,
|
|
3106
|
+
workflowSnapshot: args.workflowSnapshot,
|
|
3107
|
+
mutableState: args.mutableState,
|
|
3108
|
+
currentState: args.currentState,
|
|
3109
|
+
stopCondition: { kind: "workflowCompleted" },
|
|
3110
|
+
reset: { clearFromNodeId: args.nodeId }
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
resolveWebhookTrigger(args) {
|
|
3114
|
+
return this.engine.resolveWebhookTrigger(args);
|
|
3115
|
+
}
|
|
3116
|
+
async runMatchedWebhook(args) {
|
|
3117
|
+
const resolution = this.resolveWebhookTrigger(args);
|
|
3118
|
+
if (resolution.status === "notFound") throw new Error("Unknown webhook endpoint");
|
|
3119
|
+
if (resolution.status === "methodNotAllowed") throw new Error("Method not allowed");
|
|
3120
|
+
return await this.runWebhookMatch({
|
|
3121
|
+
match: resolution.match,
|
|
3122
|
+
requestItem: args.requestItem
|
|
3123
|
+
});
|
|
3124
|
+
}
|
|
3125
|
+
async runWebhookMatch(args) {
|
|
3126
|
+
const workflow = this.workflowRepository.get(args.match.workflowId);
|
|
3127
|
+
if (!workflow) throw new Error(`Unknown workflowId: ${args.match.workflowId}`);
|
|
3128
|
+
const scheduled = await this.engine.runWorkflow(workflow, args.match.nodeId, [args.requestItem], void 0, this.createWebhookExecutionOptions());
|
|
3129
|
+
if (scheduled.status === "failed") throw new Error(scheduled.error.message);
|
|
3130
|
+
if (scheduled.status === "completed") return {
|
|
3131
|
+
runId: scheduled.runId,
|
|
3132
|
+
workflowId: scheduled.workflowId,
|
|
3133
|
+
startedAt: scheduled.startedAt,
|
|
3134
|
+
runStatus: "completed",
|
|
3135
|
+
response: scheduled.outputs
|
|
3136
|
+
};
|
|
3137
|
+
return await Promise.race([this.engine.waitForWebhookResponse(scheduled.runId), this.engine.waitForCompletion(scheduled.runId).then((completed) => {
|
|
3138
|
+
if (completed.status === "failed") throw new Error(completed.error.message);
|
|
3139
|
+
return {
|
|
3140
|
+
runId: completed.runId,
|
|
3141
|
+
workflowId: completed.workflowId,
|
|
3142
|
+
startedAt: completed.startedAt,
|
|
3143
|
+
runStatus: "completed",
|
|
3144
|
+
response: completed.outputs
|
|
3145
|
+
};
|
|
3146
|
+
})]);
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* Webhook-triggered runs always force inline execution first.
|
|
3150
|
+
* This is the highest-precedence scheduler override: it wins over node hints and container defaults.
|
|
3151
|
+
*/
|
|
3152
|
+
createWebhookExecutionOptions() {
|
|
3153
|
+
return {
|
|
3154
|
+
localOnly: true,
|
|
3155
|
+
webhook: true
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
3158
|
+
};
|
|
3159
|
+
|
|
3160
|
+
//#endregion
|
|
3161
|
+
Object.defineProperty(exports, 'ActivationEnqueueService', {
|
|
3162
|
+
enumerable: true,
|
|
3163
|
+
get: function () {
|
|
3164
|
+
return ActivationEnqueueService;
|
|
3165
|
+
}
|
|
3166
|
+
});
|
|
3167
|
+
Object.defineProperty(exports, 'ConfigDrivenOffloadPolicy', {
|
|
3168
|
+
enumerable: true,
|
|
3169
|
+
get: function () {
|
|
3170
|
+
return ConfigDrivenOffloadPolicy;
|
|
3171
|
+
}
|
|
3172
|
+
});
|
|
3173
|
+
Object.defineProperty(exports, 'ConnectionNodeIdFactory', {
|
|
3174
|
+
enumerable: true,
|
|
3175
|
+
get: function () {
|
|
3176
|
+
return ConnectionNodeIdFactory;
|
|
3177
|
+
}
|
|
3178
|
+
});
|
|
3179
|
+
Object.defineProperty(exports, 'CoreTokens', {
|
|
3180
|
+
enumerable: true,
|
|
3181
|
+
get: function () {
|
|
3182
|
+
return CoreTokens;
|
|
3183
|
+
}
|
|
3184
|
+
});
|
|
3185
|
+
Object.defineProperty(exports, 'CredentialResolverFactory', {
|
|
3186
|
+
enumerable: true,
|
|
3187
|
+
get: function () {
|
|
3188
|
+
return CredentialResolverFactory;
|
|
3189
|
+
}
|
|
3190
|
+
});
|
|
3191
|
+
Object.defineProperty(exports, 'DefaultAsyncSleeper', {
|
|
3192
|
+
enumerable: true,
|
|
3193
|
+
get: function () {
|
|
3194
|
+
return DefaultAsyncSleeper;
|
|
3195
|
+
}
|
|
3196
|
+
});
|
|
3197
|
+
Object.defineProperty(exports, 'DefaultDrivingScheduler', {
|
|
3198
|
+
enumerable: true,
|
|
3199
|
+
get: function () {
|
|
3200
|
+
return DefaultDrivingScheduler;
|
|
3201
|
+
}
|
|
3202
|
+
});
|
|
3203
|
+
Object.defineProperty(exports, 'DefaultExecutionBinaryService', {
|
|
3204
|
+
enumerable: true,
|
|
3205
|
+
get: function () {
|
|
3206
|
+
return DefaultExecutionBinaryService;
|
|
3207
|
+
}
|
|
3208
|
+
});
|
|
3209
|
+
Object.defineProperty(exports, 'DefaultExecutionContextFactory', {
|
|
3210
|
+
enumerable: true,
|
|
3211
|
+
get: function () {
|
|
3212
|
+
return DefaultExecutionContextFactory;
|
|
3213
|
+
}
|
|
3214
|
+
});
|
|
3215
|
+
Object.defineProperty(exports, 'ENGINE_EXECUTION_LIMITS_DEFAULTS', {
|
|
3216
|
+
enumerable: true,
|
|
3217
|
+
get: function () {
|
|
3218
|
+
return ENGINE_EXECUTION_LIMITS_DEFAULTS;
|
|
3219
|
+
}
|
|
3220
|
+
});
|
|
3221
|
+
Object.defineProperty(exports, 'EngineExecutionLimitsPolicy', {
|
|
3222
|
+
enumerable: true,
|
|
3223
|
+
get: function () {
|
|
3224
|
+
return EngineExecutionLimitsPolicy;
|
|
3225
|
+
}
|
|
3226
|
+
});
|
|
3227
|
+
Object.defineProperty(exports, 'HintOnlyOffloadPolicy', {
|
|
3228
|
+
enumerable: true,
|
|
3229
|
+
get: function () {
|
|
3230
|
+
return HintOnlyOffloadPolicy;
|
|
3231
|
+
}
|
|
3232
|
+
});
|
|
3233
|
+
Object.defineProperty(exports, 'InMemoryBinaryStorage', {
|
|
3234
|
+
enumerable: true,
|
|
3235
|
+
get: function () {
|
|
3236
|
+
return InMemoryBinaryStorage;
|
|
3237
|
+
}
|
|
3238
|
+
});
|
|
3239
|
+
Object.defineProperty(exports, 'InMemoryRunDataFactory', {
|
|
3240
|
+
enumerable: true,
|
|
3241
|
+
get: function () {
|
|
3242
|
+
return InMemoryRunDataFactory;
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
Object.defineProperty(exports, 'InProcessRetryRunner', {
|
|
3246
|
+
enumerable: true,
|
|
3247
|
+
get: function () {
|
|
3248
|
+
return InProcessRetryRunner;
|
|
3249
|
+
}
|
|
3250
|
+
});
|
|
3251
|
+
Object.defineProperty(exports, 'InProcessRetryRunnerFactory', {
|
|
3252
|
+
enumerable: true,
|
|
3253
|
+
get: function () {
|
|
3254
|
+
return InProcessRetryRunnerFactory;
|
|
3255
|
+
}
|
|
3256
|
+
});
|
|
3257
|
+
Object.defineProperty(exports, 'InjectableRuntimeDecoratorComposer', {
|
|
3258
|
+
enumerable: true,
|
|
3259
|
+
get: function () {
|
|
3260
|
+
return InjectableRuntimeDecoratorComposer;
|
|
3261
|
+
}
|
|
3262
|
+
});
|
|
3263
|
+
Object.defineProperty(exports, 'InlineDrivingScheduler', {
|
|
3264
|
+
enumerable: true,
|
|
3265
|
+
get: function () {
|
|
3266
|
+
return InlineDrivingScheduler;
|
|
3267
|
+
}
|
|
3268
|
+
});
|
|
3269
|
+
Object.defineProperty(exports, 'LocalOnlyScheduler', {
|
|
3270
|
+
enumerable: true,
|
|
3271
|
+
get: function () {
|
|
3272
|
+
return LocalOnlyScheduler;
|
|
3273
|
+
}
|
|
3274
|
+
});
|
|
3275
|
+
Object.defineProperty(exports, 'MissingRuntimeExecutionMarker', {
|
|
3276
|
+
enumerable: true,
|
|
3277
|
+
get: function () {
|
|
3278
|
+
return MissingRuntimeExecutionMarker;
|
|
3279
|
+
}
|
|
3280
|
+
});
|
|
3281
|
+
Object.defineProperty(exports, 'MissingRuntimeFallbacks', {
|
|
3282
|
+
enumerable: true,
|
|
3283
|
+
get: function () {
|
|
3284
|
+
return MissingRuntimeFallbacks;
|
|
3285
|
+
}
|
|
3286
|
+
});
|
|
3287
|
+
Object.defineProperty(exports, 'MissingRuntimeTriggerToken', {
|
|
3288
|
+
enumerable: true,
|
|
3289
|
+
get: function () {
|
|
3290
|
+
return MissingRuntimeTriggerToken;
|
|
3291
|
+
}
|
|
3292
|
+
});
|
|
3293
|
+
Object.defineProperty(exports, 'NodeActivationRequestComposer', {
|
|
3294
|
+
enumerable: true,
|
|
3295
|
+
get: function () {
|
|
3296
|
+
return NodeActivationRequestComposer;
|
|
3297
|
+
}
|
|
3298
|
+
});
|
|
3299
|
+
Object.defineProperty(exports, 'NodeEventPublisher', {
|
|
3300
|
+
enumerable: true,
|
|
3301
|
+
get: function () {
|
|
3302
|
+
return NodeEventPublisher;
|
|
3303
|
+
}
|
|
3304
|
+
});
|
|
3305
|
+
Object.defineProperty(exports, 'NodeExecutor', {
|
|
3306
|
+
enumerable: true,
|
|
3307
|
+
get: function () {
|
|
3308
|
+
return NodeExecutor;
|
|
3309
|
+
}
|
|
3310
|
+
});
|
|
3311
|
+
Object.defineProperty(exports, 'NodeExecutorFactory', {
|
|
3312
|
+
enumerable: true,
|
|
3313
|
+
get: function () {
|
|
3314
|
+
return NodeExecutorFactory;
|
|
3315
|
+
}
|
|
3316
|
+
});
|
|
3317
|
+
Object.defineProperty(exports, 'NodeInstanceFactory', {
|
|
3318
|
+
enumerable: true,
|
|
3319
|
+
get: function () {
|
|
3320
|
+
return NodeInstanceFactory;
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3323
|
+
Object.defineProperty(exports, 'NodeInstanceFactoryFactory', {
|
|
3324
|
+
enumerable: true,
|
|
3325
|
+
get: function () {
|
|
3326
|
+
return NodeInstanceFactoryFactory;
|
|
3327
|
+
}
|
|
3328
|
+
});
|
|
3329
|
+
Object.defineProperty(exports, 'NodeRunStateWriterFactory', {
|
|
3330
|
+
enumerable: true,
|
|
3331
|
+
get: function () {
|
|
3332
|
+
return NodeRunStateWriterFactory;
|
|
3333
|
+
}
|
|
3334
|
+
});
|
|
3335
|
+
Object.defineProperty(exports, 'PersistedRunStateTerminalBuilder', {
|
|
3336
|
+
enumerable: true,
|
|
3337
|
+
get: function () {
|
|
3338
|
+
return PersistedRunStateTerminalBuilder;
|
|
3339
|
+
}
|
|
3340
|
+
});
|
|
3341
|
+
Object.defineProperty(exports, 'PersistedRuntimeTypeMetadataStore', {
|
|
3342
|
+
enumerable: true,
|
|
3343
|
+
get: function () {
|
|
3344
|
+
return PersistedRuntimeTypeMetadataStore;
|
|
3345
|
+
}
|
|
3346
|
+
});
|
|
3347
|
+
Object.defineProperty(exports, 'PersistedRuntimeTypeNameResolver', {
|
|
3348
|
+
enumerable: true,
|
|
3349
|
+
get: function () {
|
|
3350
|
+
return PersistedRuntimeTypeNameResolver;
|
|
3351
|
+
}
|
|
3352
|
+
});
|
|
3353
|
+
Object.defineProperty(exports, 'PersistedWorkflowTokenRegistry', {
|
|
3354
|
+
enumerable: true,
|
|
3355
|
+
get: function () {
|
|
3356
|
+
return PersistedWorkflowTokenRegistry;
|
|
3357
|
+
}
|
|
3358
|
+
});
|
|
3359
|
+
Object.defineProperty(exports, 'RunContinuationService', {
|
|
3360
|
+
enumerable: true,
|
|
3361
|
+
get: function () {
|
|
3362
|
+
return RunContinuationService;
|
|
3363
|
+
}
|
|
3364
|
+
});
|
|
3365
|
+
Object.defineProperty(exports, 'RunFinishedAtFactory', {
|
|
3366
|
+
enumerable: true,
|
|
3367
|
+
get: function () {
|
|
3368
|
+
return RunFinishedAtFactory;
|
|
3369
|
+
}
|
|
3370
|
+
});
|
|
3371
|
+
Object.defineProperty(exports, 'RunIntentService', {
|
|
3372
|
+
enumerable: true,
|
|
3373
|
+
get: function () {
|
|
3374
|
+
return RunIntentService;
|
|
3375
|
+
}
|
|
3376
|
+
});
|
|
3377
|
+
Object.defineProperty(exports, 'RunPolicySnapshotFactory', {
|
|
3378
|
+
enumerable: true,
|
|
3379
|
+
get: function () {
|
|
3380
|
+
return RunPolicySnapshotFactory;
|
|
3381
|
+
}
|
|
3382
|
+
});
|
|
3383
|
+
Object.defineProperty(exports, 'RunStartService', {
|
|
3384
|
+
enumerable: true,
|
|
3385
|
+
get: function () {
|
|
3386
|
+
return RunStartService;
|
|
3387
|
+
}
|
|
3388
|
+
});
|
|
3389
|
+
Object.defineProperty(exports, 'RunStateSemantics', {
|
|
3390
|
+
enumerable: true,
|
|
3391
|
+
get: function () {
|
|
3392
|
+
return RunStateSemantics;
|
|
3393
|
+
}
|
|
3394
|
+
});
|
|
3395
|
+
Object.defineProperty(exports, 'StackTraceCallSitePathResolver', {
|
|
3396
|
+
enumerable: true,
|
|
3397
|
+
get: function () {
|
|
3398
|
+
return StackTraceCallSitePathResolver;
|
|
3399
|
+
}
|
|
3400
|
+
});
|
|
3401
|
+
Object.defineProperty(exports, 'UnavailableBinaryStorage', {
|
|
3402
|
+
enumerable: true,
|
|
3403
|
+
get: function () {
|
|
3404
|
+
return UnavailableBinaryStorage;
|
|
3405
|
+
}
|
|
3406
|
+
});
|
|
3407
|
+
Object.defineProperty(exports, 'WorkflowExecutableNodeClassifier', {
|
|
3408
|
+
enumerable: true,
|
|
3409
|
+
get: function () {
|
|
3410
|
+
return WorkflowExecutableNodeClassifier;
|
|
3411
|
+
}
|
|
3412
|
+
});
|
|
3413
|
+
Object.defineProperty(exports, 'WorkflowExecutableNodeClassifierFactory', {
|
|
3414
|
+
enumerable: true,
|
|
3415
|
+
get: function () {
|
|
3416
|
+
return WorkflowExecutableNodeClassifierFactory;
|
|
3417
|
+
}
|
|
3418
|
+
});
|
|
3419
|
+
Object.defineProperty(exports, 'WorkflowRunExecutionContextFactory', {
|
|
3420
|
+
enumerable: true,
|
|
3421
|
+
get: function () {
|
|
3422
|
+
return WorkflowRunExecutionContextFactory;
|
|
3423
|
+
}
|
|
3424
|
+
});
|
|
3425
|
+
Object.defineProperty(exports, 'WorkflowSnapshotResolver', {
|
|
3426
|
+
enumerable: true,
|
|
3427
|
+
get: function () {
|
|
3428
|
+
return WorkflowSnapshotResolver;
|
|
3429
|
+
}
|
|
3430
|
+
});
|
|
3431
|
+
Object.defineProperty(exports, 'WorkflowTopology', {
|
|
3432
|
+
enumerable: true,
|
|
3433
|
+
get: function () {
|
|
3434
|
+
return WorkflowTopology;
|
|
3435
|
+
}
|
|
3436
|
+
});
|
|
3437
|
+
Object.defineProperty(exports, '__toESM', {
|
|
3438
|
+
enumerable: true,
|
|
3439
|
+
get: function () {
|
|
3440
|
+
return __toESM;
|
|
3441
|
+
}
|
|
3442
|
+
});
|
|
3443
|
+
Object.defineProperty(exports, 'chatModel', {
|
|
3444
|
+
enumerable: true,
|
|
3445
|
+
get: function () {
|
|
3446
|
+
return chatModel;
|
|
3447
|
+
}
|
|
3448
|
+
});
|
|
3449
|
+
Object.defineProperty(exports, 'getPersistedRuntimeTypeMetadata', {
|
|
3450
|
+
enumerable: true,
|
|
3451
|
+
get: function () {
|
|
3452
|
+
return getPersistedRuntimeTypeMetadata;
|
|
3453
|
+
}
|
|
3454
|
+
});
|
|
3455
|
+
Object.defineProperty(exports, 'node', {
|
|
3456
|
+
enumerable: true,
|
|
3457
|
+
get: function () {
|
|
3458
|
+
return node;
|
|
3459
|
+
}
|
|
3460
|
+
});
|
|
3461
|
+
Object.defineProperty(exports, 'tool', {
|
|
3462
|
+
enumerable: true,
|
|
3463
|
+
get: function () {
|
|
3464
|
+
return tool;
|
|
3465
|
+
}
|
|
3466
|
+
});
|
|
3467
|
+
//# sourceMappingURL=RunIntentService-DlQH5eZ2.cjs.map
|