@codemation/core 0.0.19 → 0.2.1

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.
Files changed (81) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/EngineRuntimeRegistration.types-0sgV2XL2.d.ts +42 -0
  3. package/dist/EngineWorkflowRunnerService-Dx7bJsJR.d.cts +73 -0
  4. package/dist/InMemoryRunDataFactory-qIYQEar7.d.cts +94 -0
  5. package/dist/{InMemoryLiveWorkflowRepository-DxoualoC.d.ts → RunIntentService-BCvGdOSY.d.ts} +438 -9
  6. package/dist/{RunIntentService-C1nu_YwM.js → RunIntentService-BFA48UpH.js} +252 -67
  7. package/dist/RunIntentService-BFA48UpH.js.map +1 -0
  8. package/dist/{InMemoryLiveWorkflowRepository-orY1VsWG.d.cts → RunIntentService-CV8izV8t.d.cts} +214 -7
  9. package/dist/{RunIntentService-ZkjpY7MS.cjs → RunIntentService-DcxXf_AM.cjs} +262 -65
  10. package/dist/RunIntentService-DcxXf_AM.cjs.map +1 -0
  11. package/dist/bootstrap/index.cjs +14 -1135
  12. package/dist/bootstrap/index.d.cts +7 -60
  13. package/dist/bootstrap/index.d.ts +4 -40
  14. package/dist/bootstrap/index.js +3 -1122
  15. package/dist/bootstrap-D67Sf2BF.js +1136 -0
  16. package/dist/bootstrap-D67Sf2BF.js.map +1 -0
  17. package/dist/bootstrap-DoQHAEQJ.cjs +1203 -0
  18. package/dist/bootstrap-DoQHAEQJ.cjs.map +1 -0
  19. package/dist/{index-BIewO9-9.d.ts → index-CueSzHsf.d.ts} +37 -260
  20. package/dist/index.cjs +99 -223
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +201 -6
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +93 -218
  25. package/dist/index.js.map +1 -1
  26. package/dist/testing.cjs +329 -3
  27. package/dist/testing.cjs.map +1 -1
  28. package/dist/testing.d.cts +181 -4
  29. package/dist/testing.d.ts +181 -3
  30. package/dist/testing.js +319 -2
  31. package/dist/testing.js.map +1 -1
  32. package/dist/workflowActivationPolicy-B8HzTk3o.js +201 -0
  33. package/dist/workflowActivationPolicy-B8HzTk3o.js.map +1 -0
  34. package/dist/workflowActivationPolicy-BzyzXLa_.cjs +231 -0
  35. package/dist/workflowActivationPolicy-BzyzXLa_.cjs.map +1 -0
  36. package/package.json +1 -1
  37. package/src/ai/AgentConnectionNodeCollector.ts +99 -0
  38. package/src/ai/AgentToolFactory.ts +38 -2
  39. package/src/ai/AiHost.ts +1 -1
  40. package/src/authoring/defineNode.types.ts +6 -0
  41. package/src/browser.ts +11 -0
  42. package/src/contracts/executionPersistenceContracts.ts +186 -0
  43. package/src/contracts/index.ts +1 -0
  44. package/src/contracts/runFinishedAtFactory.ts +5 -2
  45. package/src/contracts/runTypes.ts +10 -0
  46. package/src/contracts/runtimeTypes.ts +6 -2
  47. package/src/contracts/workflowTypes.ts +3 -2
  48. package/src/events/EventPublishingWorkflowExecutionRepository.ts +5 -0
  49. package/src/execution/ActivationEnqueueService.ts +8 -8
  50. package/src/execution/PersistedRunStateTerminalBuilder.ts +3 -0
  51. package/src/index.ts +6 -0
  52. package/src/orchestration/NodeExecutionRequestHandlerService.ts +11 -6
  53. package/src/orchestration/RunContinuationService.ts +94 -24
  54. package/src/runStorage/InMemoryWorkflowExecutionRepository.ts +14 -1
  55. package/src/scheduler/DefaultDrivingScheduler.ts +21 -11
  56. package/src/scheduler/InlineDrivingScheduler.ts +17 -21
  57. package/src/testing/CapturingScheduler.ts +15 -0
  58. package/src/testing/EngineTestKitRunIdFactory.ts +24 -0
  59. package/src/testing/InMemoryTriggerSetupStateRepository.ts +21 -0
  60. package/src/testing/PrefixedSequentialIdGenerator.ts +17 -0
  61. package/src/testing/RegistrarEngineTestKit.types.ts +76 -0
  62. package/src/testing/RegistrarEngineTestKitFactory.ts +154 -0
  63. package/src/testing/SubWorkflowRunnerTestNode.ts +83 -0
  64. package/src/testing/WorkflowTestHarnessManualTrigger.ts +39 -0
  65. package/src/testing/WorkflowTestKit.types.ts +9 -0
  66. package/src/testing/WorkflowTestKitBuilder.ts +77 -0
  67. package/src/testing/WorkflowTestKitNodeRegistrationContextFactory.ts +17 -0
  68. package/src/testing/WorkflowTestKitRunNodeWorkflowFactory.ts +26 -0
  69. package/src/testing.ts +19 -0
  70. package/src/types/index.ts +1 -0
  71. package/src/workflow/definition/ConnectionNodeIdFactory.ts +28 -0
  72. package/dist/InMemoryLiveWorkflowRepository-BTzHpQ6e.cjs +0 -151
  73. package/dist/InMemoryLiveWorkflowRepository-BTzHpQ6e.cjs.map +0 -1
  74. package/dist/InMemoryLiveWorkflowRepository-BoLNnVLg.js +0 -139
  75. package/dist/InMemoryLiveWorkflowRepository-BoLNnVLg.js.map +0 -1
  76. package/dist/RunIntentService-C1nu_YwM.js.map +0 -1
  77. package/dist/RunIntentService-DjbxzBBP.d.cts +0 -288
  78. package/dist/RunIntentService-ZkjpY7MS.cjs.map +0 -1
  79. package/dist/WorkflowSnapshotCodec-DSEzKyt3.d.cts +0 -22
  80. package/dist/bootstrap/index.cjs.map +0 -1
  81. package/dist/bootstrap/index.js.map +0 -1
@@ -0,0 +1,1136 @@
1
+ import { A as NodeExecutor, B as NodeEventPublisher, C as WorkflowSnapshotResolver, E as MissingRuntimeFallbacks, F as DefaultAsyncSleeper, I as CredentialResolverFactory, L as ActivationEnqueueService, M as InProcessRetryRunnerFactory, O as MissingRuntimeExecutionMarker, V as WorkflowExecutableNodeClassifierFactory, _ as WorkflowRunExecutionContextFactory, b as NodeRunStateWriterFactory, ct as CoreTokens, g as WorkflowTopology, h as RunContinuationService, j as NodeActivationRequestComposer, k as NodeExecutorFactory, l as InlineDrivingScheduler, o as ENGINE_EXECUTION_LIMITS_DEFAULTS, p as RunStartService, r as RunFinishedAtFactory, rt as instanceCachingFactory, s as EngineExecutionLimitsPolicy, t as RunIntentService, v as RunStateSemantics, w as WorkflowSnapshotCodec, x as NodeInstanceFactoryFactory, y as PersistedRunStateTerminalBuilder } from "./RunIntentService-BFA48UpH.js";
2
+
3
+ //#region src/policies/executionLimits/EngineExecutionLimitsPolicyFactory.ts
4
+ /**
5
+ * Builds {@link EngineExecutionLimitsPolicy} by merging {@link ENGINE_EXECUTION_LIMITS_DEFAULTS} with optional `overrides` (e.g. host `runtime.engineExecutionLimits`).
6
+ */
7
+ var EngineExecutionLimitsPolicyFactory = class {
8
+ create(overrides) {
9
+ return new EngineExecutionLimitsPolicy({
10
+ ...ENGINE_EXECUTION_LIMITS_DEFAULTS,
11
+ ...overrides
12
+ });
13
+ }
14
+ };
15
+
16
+ //#endregion
17
+ //#region src/policies/storage/RunTerminalPersistenceCoordinator.ts
18
+ var RunTerminalPersistenceCoordinator = class {
19
+ constructor(runRepository, storageEvaluator) {
20
+ this.runRepository = runRepository;
21
+ this.storageEvaluator = storageEvaluator;
22
+ }
23
+ async maybeDeleteAfterTerminalState(args) {
24
+ if (await this.storageEvaluator.shouldPersist(args.workflow, args.state.policySnapshot, {
25
+ runId: args.state.runId,
26
+ workflowId: args.state.workflowId,
27
+ workflow: args.workflow,
28
+ finalStatus: args.finalStatus,
29
+ startedAt: args.state.startedAt,
30
+ finishedAt: args.finishedAt
31
+ })) return;
32
+ if (!this.runRepository.deleteRun) return;
33
+ await this.runRepository.deleteRun(args.state.runId);
34
+ }
35
+ };
36
+
37
+ //#endregion
38
+ //#region src/policies/WorkflowPolicyErrorServices.ts
39
+ var WorkflowPolicyErrorServices = class {
40
+ constructor(nodeResolver) {
41
+ this.nodeResolver = nodeResolver;
42
+ }
43
+ resolveNodeErrorHandler(spec) {
44
+ if (!spec) return void 0;
45
+ if (typeof spec === "object" && spec !== null && "handle" in spec && typeof spec.handle === "function") return spec;
46
+ return this.nodeResolver.resolve(spec);
47
+ }
48
+ resolveWorkflowErrorHandler(spec) {
49
+ if (!spec) return void 0;
50
+ if (typeof spec === "object" && spec !== null && "onError" in spec && typeof spec.onError === "function") return spec;
51
+ return this.nodeResolver.resolve(spec);
52
+ }
53
+ };
54
+
55
+ //#endregion
56
+ //#region src/policies/storage/WorkflowStoragePolicyEvaluator.ts
57
+ var WorkflowStoragePolicyEvaluator = class {
58
+ constructor(nodeResolver) {
59
+ this.nodeResolver = nodeResolver;
60
+ }
61
+ async shouldPersist(workflow, snapshot, args) {
62
+ const spec = workflow.storagePolicy;
63
+ if (spec === void 0) return this.modeMatches(snapshot?.storagePolicy ?? "ALL", args);
64
+ if (typeof spec === "string") return this.modeMatches(spec, args);
65
+ const resolver = this.nodeResolver.resolve(spec);
66
+ return Boolean(await resolver.shouldPersist(args));
67
+ }
68
+ modeMatches(mode, args) {
69
+ if (mode === "ALL") return true;
70
+ if (mode === "NEVER") return false;
71
+ if (mode === "SUCCESS") return args.finalStatus === "completed";
72
+ if (mode === "ERROR") return args.finalStatus === "failed";
73
+ return true;
74
+ }
75
+ };
76
+
77
+ //#endregion
78
+ //#region src/runStorage/RunSummaryMapper.ts
79
+ /** Maps persisted run state to API run summaries for listings. */
80
+ var RunSummaryMapper = class {
81
+ static fromPersistedState(state) {
82
+ return {
83
+ runId: state.runId,
84
+ workflowId: state.workflowId,
85
+ startedAt: state.startedAt,
86
+ status: state.status,
87
+ finishedAt: RunFinishedAtFactory.resolveIso(state),
88
+ parent: state.parent,
89
+ executionOptions: state.executionOptions
90
+ };
91
+ }
92
+ };
93
+
94
+ //#endregion
95
+ //#region src/runStorage/InMemoryWorkflowExecutionRepository.ts
96
+ var InMemoryWorkflowExecutionRepository = class {
97
+ runs = /* @__PURE__ */ new Map();
98
+ async createRun(args) {
99
+ this.runs.set(args.runId, {
100
+ runId: args.runId,
101
+ workflowId: args.workflowId,
102
+ startedAt: args.startedAt,
103
+ revision: 0,
104
+ parent: args.parent,
105
+ executionOptions: args.executionOptions,
106
+ control: args.control,
107
+ workflowSnapshot: args.workflowSnapshot,
108
+ mutableState: args.mutableState,
109
+ policySnapshot: args.policySnapshot,
110
+ engineCounters: args.engineCounters,
111
+ status: "running",
112
+ queue: [],
113
+ outputsByNode: {},
114
+ nodeSnapshotsByNodeId: {},
115
+ connectionInvocations: []
116
+ });
117
+ }
118
+ async load(runId) {
119
+ return this.runs.get(runId);
120
+ }
121
+ async loadSchedulingState(runId) {
122
+ const state = this.runs.get(runId);
123
+ if (!state) return;
124
+ return {
125
+ pending: state.pending ? { ...state.pending } : void 0,
126
+ queue: state.queue.map((entry) => ({ ...entry }))
127
+ };
128
+ }
129
+ async save(state) {
130
+ this.runs.set(state.runId, {
131
+ ...state,
132
+ revision: (state.revision ?? 0) + 1
133
+ });
134
+ }
135
+ async deleteRun(runId) {
136
+ this.runs.delete(runId);
137
+ }
138
+ async listRuns(args) {
139
+ const limit = args?.limit ?? 50;
140
+ return [...this.runs.values()].filter((s) => args?.workflowId ? s.workflowId === args.workflowId : true).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).slice(0, limit).map((s) => RunSummaryMapper.fromPersistedState(s));
141
+ }
142
+ async listRunsOlderThan(args) {
143
+ const limit = args.limit ?? 100;
144
+ const out = [];
145
+ for (const s of this.runs.values()) {
146
+ if (s.status !== "completed" && s.status !== "failed") continue;
147
+ const finishedAt = RunFinishedAtFactory.resolveIso(s);
148
+ if (!finishedAt || finishedAt >= args.beforeIso) continue;
149
+ out.push({
150
+ runId: s.runId,
151
+ workflowId: s.workflowId,
152
+ startedAt: s.startedAt,
153
+ finishedAt
154
+ });
155
+ }
156
+ out.sort((a, b) => a.finishedAt.localeCompare(b.finishedAt));
157
+ return out.slice(0, limit);
158
+ }
159
+ };
160
+
161
+ //#endregion
162
+ //#region src/orchestration/NodeExecutionRequestHandlerService.ts
163
+ var NodeExecutionRequestHandlerService = class {
164
+ constructor(workflowExecutionRepository, workflowSnapshotResolver, runDataFactory, runExecutionContextFactory, nodeStatePublisherFactory, nodeActivationRequestComposer, nodeExecutor, continuation, executionLimitsPolicy) {
165
+ this.workflowExecutionRepository = workflowExecutionRepository;
166
+ this.workflowSnapshotResolver = workflowSnapshotResolver;
167
+ this.runDataFactory = runDataFactory;
168
+ this.runExecutionContextFactory = runExecutionContextFactory;
169
+ this.nodeStatePublisherFactory = nodeStatePublisherFactory;
170
+ this.nodeActivationRequestComposer = nodeActivationRequestComposer;
171
+ this.nodeExecutor = nodeExecutor;
172
+ this.continuation = continuation;
173
+ this.executionLimitsPolicy = executionLimitsPolicy;
174
+ }
175
+ async handleNodeExecutionRequest(request) {
176
+ const [state, schedulingState] = await Promise.all([this.workflowExecutionRepository.load(request.runId), this.workflowExecutionRepository.loadSchedulingState(request.runId)]);
177
+ if (!state) throw new Error(`Unknown runId: ${request.runId}`);
178
+ if (state.workflowId !== request.workflowId) throw new Error(`workflowId mismatch for run ${request.runId}: ${state.workflowId} vs ${request.workflowId}`);
179
+ const pendingExecution = schedulingState?.pending;
180
+ if (state.status !== "pending" || !pendingExecution) return;
181
+ if (pendingExecution.activationId !== request.activationId || pendingExecution.nodeId !== request.nodeId) return;
182
+ const workflow = this.resolvePersistedWorkflow(state);
183
+ if (!workflow) throw new Error(`Unknown workflowId: ${state.workflowId}`);
184
+ const definition = workflow.nodes.find((node) => node.id === request.nodeId);
185
+ if (!definition) throw new Error(`Unknown nodeId: ${request.nodeId}`);
186
+ if (definition.kind !== "node") throw new Error(`Node ${request.nodeId} is not runnable`);
187
+ const resolvedParent = request.parent ?? state.parent;
188
+ const data = this.runDataFactory.create(state.outputsByNode);
189
+ const limits = this.resolveEngineLimitsFromState(state);
190
+ const persistedInput = pendingExecution.inputsByPort.in ?? request.input;
191
+ const base = this.runExecutionContextFactory.create({
192
+ runId: state.runId,
193
+ workflowId: state.workflowId,
194
+ nodeId: request.nodeId,
195
+ parent: resolvedParent,
196
+ subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
197
+ engineMaxNodeActivations: limits.engineMaxNodeActivations,
198
+ engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
199
+ data,
200
+ nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, resolvedParent)
201
+ });
202
+ const activationRequest = this.nodeActivationRequestComposer.createSingleFromDefinitionWithActivation({
203
+ activationId: request.activationId,
204
+ runId: request.runId,
205
+ workflowId: request.workflowId,
206
+ parent: resolvedParent,
207
+ executionOptions: request.executionOptions ?? state.executionOptions,
208
+ base,
209
+ data,
210
+ definition: {
211
+ id: definition.id,
212
+ config: definition.config
213
+ },
214
+ batchId: pendingExecution.batchId ?? "batch_1",
215
+ input: persistedInput
216
+ });
217
+ await this.continuation.markNodeRunning({
218
+ runId: activationRequest.runId,
219
+ activationId: activationRequest.activationId,
220
+ nodeId: activationRequest.nodeId,
221
+ inputsByPort: pendingExecution.inputsByPort
222
+ });
223
+ let outputs;
224
+ try {
225
+ outputs = await this.nodeExecutor.execute(activationRequest);
226
+ } catch (error) {
227
+ await this.resumeAfterExecutionError(activationRequest, this.asError(error));
228
+ return;
229
+ }
230
+ await this.resumeAfterExecutionResult(activationRequest, outputs ?? {});
231
+ }
232
+ resolvePersistedWorkflow(state) {
233
+ return this.workflowSnapshotResolver.resolve({
234
+ workflowId: state.workflowId,
235
+ workflowSnapshot: state.workflowSnapshot
236
+ });
237
+ }
238
+ resolveEngineLimitsFromState(state) {
239
+ const fallback = this.executionLimitsPolicy.createRootExecutionOptions();
240
+ return {
241
+ engineMaxNodeActivations: state.executionOptions?.maxNodeActivations ?? fallback.maxNodeActivations,
242
+ engineMaxSubworkflowDepth: state.executionOptions?.maxSubworkflowDepth ?? fallback.maxSubworkflowDepth
243
+ };
244
+ }
245
+ async resumeAfterExecutionResult(request, outputs) {
246
+ try {
247
+ await this.continuation.resumeFromNodeResult({
248
+ runId: request.runId,
249
+ activationId: request.activationId,
250
+ nodeId: request.nodeId,
251
+ outputs
252
+ });
253
+ } catch (error) {
254
+ this.rethrowUnlessIgnorableContinuationError(error);
255
+ }
256
+ }
257
+ async resumeAfterExecutionError(request, error) {
258
+ try {
259
+ await this.continuation.resumeFromNodeError({
260
+ runId: request.runId,
261
+ activationId: request.activationId,
262
+ nodeId: request.nodeId,
263
+ error
264
+ });
265
+ } catch (continuationError) {
266
+ this.rethrowUnlessIgnorableContinuationError(continuationError);
267
+ }
268
+ }
269
+ asError(error) {
270
+ return error instanceof Error ? error : new Error(String(error));
271
+ }
272
+ rethrowUnlessIgnorableContinuationError(error) {
273
+ if (this.isIgnorableContinuationError(error)) return;
274
+ throw this.asError(error);
275
+ }
276
+ isIgnorableContinuationError(error) {
277
+ const message = this.asError(error).message;
278
+ return message.includes(" is not pending") || message.includes("activationId mismatch") || message.includes("nodeId mismatch");
279
+ }
280
+ };
281
+
282
+ //#endregion
283
+ //#region src/planning/RunQueuePlanner.ts
284
+ var RunQueuePlanner = class {
285
+ constructor(topology, nodeInstances) {
286
+ this.topology = topology;
287
+ this.nodeInstances = nodeInstances;
288
+ }
289
+ validateNodeKinds() {
290
+ for (const [toNodeId, inputs] of this.topology.expectedInputsByNode.entries()) {
291
+ if (inputs.length <= 1) {
292
+ const only = inputs[0];
293
+ if (only && only !== "in") {
294
+ const inst$1 = this.nodeInstances.get(toNodeId);
295
+ if (!this.isMultiInputNode(inst$1)) throw new Error(`Node ${toNodeId} only supports input 'in' (got '${only}').`);
296
+ }
297
+ continue;
298
+ }
299
+ const inst = this.nodeInstances.get(toNodeId);
300
+ if (!this.isMultiInputNode(inst)) throw new Error(`Node ${toNodeId} has ${inputs.length} inbound edges. Insert a Merge node to combine branches.`);
301
+ }
302
+ }
303
+ seedFromTrigger(args) {
304
+ const queue = [];
305
+ for (const e of this.topology.outgoingByNode.get(args.startNodeId) ?? []) {
306
+ if (e.output !== "main") continue;
307
+ this.enqueueEdge(queue, {
308
+ batchId: args.batchId,
309
+ to: e.to,
310
+ from: {
311
+ nodeId: args.startNodeId,
312
+ output: "main"
313
+ },
314
+ items: args.items
315
+ });
316
+ }
317
+ return queue;
318
+ }
319
+ applyOutputs(queue, args) {
320
+ for (const e of this.topology.outgoingByNode.get(args.fromNodeId) ?? []) {
321
+ const outItems = args.outputs[e.output] ?? [];
322
+ this.enqueueEdge(queue, {
323
+ batchId: args.batchId,
324
+ to: e.to,
325
+ from: {
326
+ nodeId: args.fromNodeId,
327
+ output: e.output
328
+ },
329
+ items: outItems
330
+ });
331
+ }
332
+ }
333
+ nextActivation(queue) {
334
+ const readyCollect = this.resolveReadyCollect(queue);
335
+ if (readyCollect) return readyCollect;
336
+ const jobIdx = queue.findIndex((q) => !q.collect);
337
+ if (jobIdx === -1) {
338
+ if (queue.length === 0) return null;
339
+ const sealedCollect = this.resolveSealedCollect(queue);
340
+ if (sealedCollect) return sealedCollect;
341
+ const stuck = queue[0];
342
+ throw new Error(this.describeUnsatisfiedCollect(stuck));
343
+ }
344
+ const job = queue.splice(jobIdx, 1)[0];
345
+ const def = this.topology.defsById.get(job.nodeId);
346
+ if (!def || def.kind !== "node") return this.nextActivation(queue);
347
+ return {
348
+ kind: "single",
349
+ nodeId: job.nodeId,
350
+ input: job.input,
351
+ batchId: job.batchId ?? "batch_1"
352
+ };
353
+ }
354
+ sumItemsByPort(inputsByPort) {
355
+ let n = 0;
356
+ for (const v of Object.values(inputsByPort)) n += v?.length ?? 0;
357
+ return n;
358
+ }
359
+ resolveReadyCollect(queue) {
360
+ for (let i = 0; i < queue.length; i++) {
361
+ const ready = this.tryDequeueCollect(queue, i);
362
+ if (ready) return ready;
363
+ }
364
+ return null;
365
+ }
366
+ resolveSealedCollect(queue) {
367
+ for (let i = 0; i < queue.length; i++) {
368
+ const queueEntry = queue[i];
369
+ if (!queueEntry.collect) continue;
370
+ const received = queueEntry.collect.received;
371
+ if (Object.keys(received).length === 0) continue;
372
+ this.fillMissingCollectInputs(queueEntry);
373
+ const ready = this.tryDequeueCollect(queue, i);
374
+ if (ready) return ready;
375
+ }
376
+ return null;
377
+ }
378
+ tryDequeueCollect(queue, index) {
379
+ const queueEntry = queue[index];
380
+ if (!queueEntry.collect) return null;
381
+ const batchId = queueEntry.batchId ?? "batch_1";
382
+ const expected = queueEntry.collect.expectedInputs ?? [];
383
+ const received = queueEntry.collect.received;
384
+ for (const input of expected) if (!(input in received)) return null;
385
+ queue.splice(index, 1);
386
+ return {
387
+ kind: "multi",
388
+ nodeId: queueEntry.nodeId,
389
+ inputsByPort: received,
390
+ batchId
391
+ };
392
+ }
393
+ fillMissingCollectInputs(queueEntry) {
394
+ if (!queueEntry.collect) return;
395
+ const received = queueEntry.collect.received;
396
+ for (const input of queueEntry.collect.expectedInputs ?? []) if (!(input in received)) received[input] = [];
397
+ }
398
+ enqueueEdge(queue, args) {
399
+ const target = this.nodeInstances.get(args.to.nodeId);
400
+ if (!this.isMultiInputNode(target)) {
401
+ if (args.items.length === 0) {
402
+ if (this.shouldContinueAfterEmptyOutputFromSource(args.from.nodeId)) {
403
+ queue.push({
404
+ nodeId: args.to.nodeId,
405
+ input: args.items,
406
+ toInput: args.to.input,
407
+ batchId: args.batchId,
408
+ from: args.from
409
+ });
410
+ return;
411
+ }
412
+ this.propagateEmptyPath(queue, args.to.nodeId, args.batchId);
413
+ return;
414
+ }
415
+ queue.push({
416
+ nodeId: args.to.nodeId,
417
+ input: args.items,
418
+ toInput: args.to.input,
419
+ batchId: args.batchId,
420
+ from: args.from
421
+ });
422
+ return;
423
+ }
424
+ const expected = this.topology.expectedInputsByNode.get(args.to.nodeId) ?? [];
425
+ let collect = queue.find((q) => q.nodeId === args.to.nodeId && (q.batchId ?? "batch_1") === args.batchId && !!q.collect);
426
+ if (!collect) {
427
+ collect = {
428
+ nodeId: args.to.nodeId,
429
+ input: [],
430
+ batchId: args.batchId,
431
+ collect: {
432
+ expectedInputs: expected,
433
+ received: {}
434
+ }
435
+ };
436
+ queue.push(collect);
437
+ }
438
+ const received = collect.collect.received;
439
+ received[args.to.input] = args.items;
440
+ }
441
+ shouldContinueAfterEmptyOutputFromSource(fromNodeId) {
442
+ const def = this.topology.defsById.get(fromNodeId);
443
+ if (!def) return false;
444
+ return def.config.continueWhenEmptyOutput === true;
445
+ }
446
+ propagateEmptyPath(queue, nodeId, batchId) {
447
+ for (const edge of this.topology.outgoingByNode.get(nodeId) ?? []) this.enqueueEdge(queue, {
448
+ batchId,
449
+ to: edge.to,
450
+ from: {
451
+ nodeId,
452
+ output: edge.output
453
+ },
454
+ items: []
455
+ });
456
+ }
457
+ isMultiInputNode(n) {
458
+ return typeof n?.executeMulti === "function";
459
+ }
460
+ describeUnsatisfiedCollect(queueEntry) {
461
+ const batchId = queueEntry.batchId ?? "batch_1";
462
+ const expectedInputs = queueEntry.collect?.expectedInputs ?? [];
463
+ const receivedInputs = Object.keys(queueEntry.collect?.received ?? {});
464
+ const missingInputs = expectedInputs.filter((input) => !receivedInputs.includes(input));
465
+ const mergeNodeLabel = this.formatNodeLabel(queueEntry.nodeId);
466
+ const receivedSummary = this.describeReceivedInputs(queueEntry);
467
+ const missingSummary = this.describeMissingInputs(queueEntry.nodeId, missingInputs);
468
+ return [
469
+ `Multi-input collect is stuck at ${mergeNodeLabel} (batchId=${batchId}).`,
470
+ `Expected inputs: ${this.formatInputList(expectedInputs)}.`,
471
+ `Received inputs: ${receivedSummary}.`,
472
+ `Missing inputs: ${missingSummary}.`
473
+ ].join(" ");
474
+ }
475
+ describeReceivedInputs(queueEntry) {
476
+ const received = queueEntry.collect?.received ?? {};
477
+ const receivedEntries = Object.entries(received);
478
+ if (receivedEntries.length === 0) return "none";
479
+ return receivedEntries.map(([input, items]) => `${input} (${items.length} item${items.length === 1 ? "" : "s"})`).join(", ");
480
+ }
481
+ describeMissingInputs(nodeId, missingInputs) {
482
+ if (missingInputs.length === 0) return "none";
483
+ return missingInputs.map((input) => {
484
+ const sources = this.findSources(nodeId, input);
485
+ if (sources.length === 0) return input;
486
+ return `${input} from ${sources.join(" or ")}`;
487
+ }).join(", ");
488
+ }
489
+ findSources(nodeId, input) {
490
+ const matches = [];
491
+ for (const [sourceNodeId, edges] of this.topology.outgoingByNode.entries()) for (const edge of edges) if (edge.to.nodeId === nodeId && edge.to.input === input) matches.push(this.formatNodeLabel(sourceNodeId));
492
+ return matches;
493
+ }
494
+ formatInputList(inputs) {
495
+ return inputs.length > 0 ? `[${inputs.join(", ")}]` : "[]";
496
+ }
497
+ formatNodeLabel(nodeId) {
498
+ const definition = this.topology.defsById.get(nodeId);
499
+ const instance = this.nodeInstances.get(nodeId);
500
+ const typeName = definition?.type && typeof definition.type === "function" ? definition.type.name : instance && typeof instance === "object" && "constructor" in instance ? instance.constructor.name ?? "Node" : "Node";
501
+ return definition?.name ? `"${definition.name}" (${typeName}:${nodeId})` : `${typeName}:${nodeId}`;
502
+ }
503
+ };
504
+
505
+ //#endregion
506
+ //#region src/planning/EngineWorkflowPlanningFactory.ts
507
+ var EngineWorkflowPlanningFactory = class {
508
+ constructor(workflowNodeInstanceFactory) {
509
+ this.workflowNodeInstanceFactory = workflowNodeInstanceFactory;
510
+ }
511
+ create(workflow) {
512
+ this.validateAcyclic(workflow);
513
+ const topology = WorkflowTopology.fromWorkflow(workflow);
514
+ const planner = new RunQueuePlanner(topology, this.workflowNodeInstanceFactory.createNodes(workflow));
515
+ planner.validateNodeKinds();
516
+ return {
517
+ topology,
518
+ planner
519
+ };
520
+ }
521
+ validateAcyclic(workflow) {
522
+ const classifier = WorkflowExecutableNodeClassifierFactory.create(workflow);
523
+ const outgoing = /* @__PURE__ */ new Map();
524
+ const visitState = /* @__PURE__ */ new Map();
525
+ for (const node of workflow.nodes) if (classifier.isExecutableNodeId(node.id)) visitState.set(node.id, "unvisited");
526
+ for (const edge of workflow.edges) {
527
+ if (!classifier.isExecutableNodeId(edge.from.nodeId) || !classifier.isExecutableNodeId(edge.to.nodeId)) continue;
528
+ const destinations = outgoing.get(edge.from.nodeId) ?? [];
529
+ destinations.push(edge.to.nodeId);
530
+ outgoing.set(edge.from.nodeId, destinations);
531
+ }
532
+ for (const node of workflow.nodes) if (classifier.isExecutableNodeId(node.id) && visitState.get(node.id) === "unvisited") this.depthFirstSearch(node.id, outgoing, visitState);
533
+ }
534
+ depthFirstSearch(nodeId, outgoing, visitState) {
535
+ visitState.set(nodeId, "visiting");
536
+ for (const toNodeId of outgoing.get(nodeId) ?? []) {
537
+ const state = visitState.get(toNodeId);
538
+ if (state === "visiting") throw new Error(`Workflow graph contains a directed cycle (edge ${nodeId} -> ${toNodeId}).`);
539
+ if (state === "unvisited") this.depthFirstSearch(toNodeId, outgoing, visitState);
540
+ }
541
+ visitState.set(nodeId, "done");
542
+ }
543
+ };
544
+
545
+ //#endregion
546
+ //#region src/orchestration/TriggerRuntimeService.ts
547
+ var TriggerRuntimeService = class {
548
+ credentialResolverFactory;
549
+ triggerCleanupHandlesByKey = /* @__PURE__ */ new Map();
550
+ constructor(workflowRepository, workflowActivationPolicy, runIdFactory, runDataFactory, executionContextFactory, credentialResolverFactory, nodeExecutionStatePublisherFactory, nodeResolver, triggerSetupStateRepository, emitHandler, executionLimitsPolicy, diagnostics) {
551
+ this.workflowRepository = workflowRepository;
552
+ this.workflowActivationPolicy = workflowActivationPolicy;
553
+ this.runIdFactory = runIdFactory;
554
+ this.runDataFactory = runDataFactory;
555
+ this.executionContextFactory = executionContextFactory;
556
+ this.nodeExecutionStatePublisherFactory = nodeExecutionStatePublisherFactory;
557
+ this.nodeResolver = nodeResolver;
558
+ this.triggerSetupStateRepository = triggerSetupStateRepository;
559
+ this.emitHandler = emitHandler;
560
+ this.executionLimitsPolicy = executionLimitsPolicy;
561
+ this.diagnostics = diagnostics;
562
+ this.credentialResolverFactory = credentialResolverFactory;
563
+ }
564
+ async startTriggers() {
565
+ for (const wf of this.workflowRepository.list()) {
566
+ if (!this.workflowActivationPolicy.isActive(wf.id)) {
567
+ const summaries = this.formatTriggerSummaries(wf);
568
+ if (summaries.length > 0) this.logInfo(`Workflow "${wf.name}" (${wf.id}) is inactive; skipping trigger setup — ${summaries.join("; ")}.`);
569
+ continue;
570
+ }
571
+ await this.startTriggersForWorkflow(wf);
572
+ }
573
+ }
574
+ async syncWorkflowTriggersForActivation(workflowId) {
575
+ const wf = this.workflowRepository.get(workflowId);
576
+ if (!wf) return;
577
+ const summaries = this.formatTriggerSummaries(wf);
578
+ if (summaries.length > 0) this.logInfo(`Workflow "${wf.name}" (${wf.id}): stopping triggers — ${summaries.join("; ")}.`);
579
+ await this.stopTriggersForWorkflow(wf);
580
+ if (this.workflowActivationPolicy.isActive(workflowId)) {
581
+ if (summaries.length > 0) this.logInfo(`Workflow "${wf.name}" (${wf.id}): activation on; starting triggers — ${summaries.join("; ")}.`);
582
+ await this.startTriggersForWorkflow(wf);
583
+ } else this.logInfo(`Workflow "${wf.name}" (${wf.id}): activation off; triggers not started.`);
584
+ }
585
+ async stop() {
586
+ for (const workflow of this.workflowRepository.list()) await this.stopTriggersForWorkflow(workflow);
587
+ }
588
+ async createTriggerTestItems(args) {
589
+ const definition = args.workflow.nodes.find((node$1) => node$1.id === args.nodeId);
590
+ if (!definition) throw new Error(`Unknown trigger nodeId: ${args.nodeId}`);
591
+ if (definition.kind !== "trigger") throw new Error(`Node ${args.nodeId} is not a trigger`);
592
+ const node = this.nodeResolver.resolve(definition.type);
593
+ if (!this.isTestableTriggerNode(node)) return;
594
+ const data = this.runDataFactory.create();
595
+ const runId = this.runIdFactory.makeRunId();
596
+ const trigger = {
597
+ workflowId: args.workflow.id,
598
+ nodeId: definition.id
599
+ };
600
+ const previousState = await this.triggerSetupStateRepository.load(trigger);
601
+ return await node.getTestItems({
602
+ ...this.createExecutionContext({
603
+ runId,
604
+ workflowId: args.workflow.id,
605
+ nodeId: definition.id,
606
+ data
607
+ }),
608
+ trigger,
609
+ nodeId: definition.id,
610
+ config: definition.config,
611
+ previousState: previousState?.state
612
+ });
613
+ }
614
+ async startTriggersForWorkflow(wf) {
615
+ for (const def of wf.nodes) {
616
+ if (def.kind !== "trigger") continue;
617
+ const node = this.nodeResolver.resolve(def.type);
618
+ const data = this.runDataFactory.create();
619
+ const triggerRunId = this.runIdFactory.makeRunId();
620
+ const trigger = {
621
+ workflowId: wf.id,
622
+ nodeId: def.id
623
+ };
624
+ await this.stopTrigger(trigger);
625
+ const previousState = await this.triggerSetupStateRepository.load(trigger);
626
+ let nextState;
627
+ try {
628
+ nextState = await node.setup({
629
+ ...this.createExecutionContext({
630
+ runId: triggerRunId,
631
+ workflowId: wf.id,
632
+ nodeId: def.id,
633
+ data
634
+ }),
635
+ trigger,
636
+ config: def.config,
637
+ previousState: previousState?.state,
638
+ registerCleanup: (cleanup) => {
639
+ this.registerTriggerCleanupHandle(trigger, cleanup);
640
+ },
641
+ emit: async (items) => {
642
+ await this.emitHandler.emit(wf, def.id, items);
643
+ }
644
+ });
645
+ } catch (triggerError) {
646
+ await this.stopTrigger(trigger);
647
+ const message = triggerError instanceof Error ? triggerError.message : String(triggerError);
648
+ this.logWarn(`Skipping trigger setup for workflow ${wf.id} node ${def.id}: ${message}`);
649
+ continue;
650
+ }
651
+ if (nextState === void 0) await this.triggerSetupStateRepository.delete(trigger);
652
+ else await this.triggerSetupStateRepository.save({
653
+ trigger,
654
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
655
+ state: nextState
656
+ });
657
+ }
658
+ }
659
+ async stopTriggersForWorkflow(workflow) {
660
+ for (const node of workflow.nodes) {
661
+ if (node.kind !== "trigger") continue;
662
+ await this.stopTrigger({
663
+ workflowId: workflow.id,
664
+ nodeId: node.id
665
+ });
666
+ }
667
+ }
668
+ createExecutionContext(args) {
669
+ const nodeState = this.nodeExecutionStatePublisherFactory.create(args.runId, args.workflowId, void 0);
670
+ const rootLimits = this.executionLimitsPolicy.createRootExecutionOptions();
671
+ return this.executionContextFactory.create({
672
+ runId: args.runId,
673
+ workflowId: args.workflowId,
674
+ parent: void 0,
675
+ subworkflowDepth: rootLimits.subworkflowDepth ?? 0,
676
+ engineMaxNodeActivations: rootLimits.maxNodeActivations,
677
+ engineMaxSubworkflowDepth: rootLimits.maxSubworkflowDepth,
678
+ data: args.data,
679
+ nodeState,
680
+ getCredential: this.credentialResolverFactory.create(args.workflowId, args.nodeId)
681
+ });
682
+ }
683
+ registerTriggerCleanupHandle(trigger, cleanup) {
684
+ const key = this.toTriggerKey(trigger);
685
+ const cleanups = this.triggerCleanupHandlesByKey.get(key) ?? [];
686
+ cleanups.push(cleanup);
687
+ this.triggerCleanupHandlesByKey.set(key, cleanups);
688
+ }
689
+ async stopTrigger(trigger) {
690
+ const key = this.toTriggerKey(trigger);
691
+ const cleanups = this.triggerCleanupHandlesByKey.get(key) ?? [];
692
+ this.triggerCleanupHandlesByKey.delete(key);
693
+ for (const cleanup of [...cleanups].reverse()) await cleanup.stop();
694
+ }
695
+ toTriggerKey(trigger) {
696
+ return `${trigger.workflowId}:${trigger.nodeId}`;
697
+ }
698
+ formatTriggerSummaries(wf) {
699
+ const out = [];
700
+ for (const def of wf.nodes) {
701
+ if (def.kind !== "trigger") continue;
702
+ out.push(this.describeTriggerNode(def));
703
+ }
704
+ return out;
705
+ }
706
+ describeTriggerNode(def) {
707
+ const label = def.name !== void 0 && def.name.trim().length > 0 ? def.name.trim() : String(def.id);
708
+ const cfg = def.config;
709
+ if (typeof cfg.endpointKey === "string" && cfg.endpointKey.trim().length > 0) return `${label} (webhook "${cfg.endpointKey.trim()}")`;
710
+ return label;
711
+ }
712
+ logInfo(message) {
713
+ if (this.diagnostics) this.diagnostics.info(message);
714
+ }
715
+ logWarn(message) {
716
+ if (this.diagnostics) this.diagnostics.warn(message);
717
+ else console.warn(`[engine] ${message}`);
718
+ }
719
+ isTestableTriggerNode(node) {
720
+ return typeof node.getTestItems === "function";
721
+ }
722
+ };
723
+
724
+ //#endregion
725
+ //#region src/orchestration/EngineWaiters.ts
726
+ var EngineWaiters = class {
727
+ completionWaiters = /* @__PURE__ */ new Map();
728
+ webhookResponseWaiters = /* @__PURE__ */ new Map();
729
+ waitForCompletion(runId) {
730
+ return new Promise((resolve) => {
731
+ const list = this.completionWaiters.get(runId) ?? [];
732
+ list.push(resolve);
733
+ this.completionWaiters.set(runId, list);
734
+ });
735
+ }
736
+ waitForWebhookResponse(runId) {
737
+ return new Promise((resolve) => {
738
+ const list = this.webhookResponseWaiters.get(runId) ?? [];
739
+ list.push(resolve);
740
+ this.webhookResponseWaiters.set(runId, list);
741
+ });
742
+ }
743
+ resolveRunCompletion(result) {
744
+ if (result.status !== "completed" && result.status !== "failed") return;
745
+ const list = this.completionWaiters.get(result.runId);
746
+ if (!list || list.length === 0) return;
747
+ this.completionWaiters.delete(result.runId);
748
+ for (const r of list) r(result);
749
+ }
750
+ resolveWebhookResponse(result) {
751
+ const list = this.webhookResponseWaiters.get(result.runId);
752
+ if (!list || list.length === 0) return;
753
+ this.webhookResponseWaiters.delete(result.runId);
754
+ for (const resolve of list) resolve(result);
755
+ }
756
+ };
757
+
758
+ //#endregion
759
+ //#region src/orchestration/Engine.ts
760
+ /**
761
+ * Runtime facade for orchestration, continuation, triggers, and webhook routing.
762
+ * Prefer {@link import("../intents/RunIntentService").RunIntentService} for host/HTTP invocation boundaries.
763
+ * The class token is exported from `@codemation/core/bootstrap` (not the main `@codemation/core` barrel).
764
+ */
765
+ var Engine = class {
766
+ constructor(deps) {
767
+ this.deps = deps;
768
+ }
769
+ loadWorkflows(workflows) {
770
+ this.deps.tokenRegistry.registerFromWorkflows?.(workflows);
771
+ this.deps.liveWorkflowRepository.setWorkflows(workflows);
772
+ this.deps.webhookTriggerMatcher.onEngineWorkflowsLoaded?.();
773
+ }
774
+ getTokenRegistry() {
775
+ return this.deps.tokenRegistry;
776
+ }
777
+ resolveWorkflowSnapshot(args) {
778
+ return this.deps.workflowSnapshotResolver.resolve(args);
779
+ }
780
+ async startTriggers() {
781
+ return await this.deps.triggerRuntime.startTriggers();
782
+ }
783
+ async syncWorkflowTriggersForActivation(workflowId) {
784
+ await this.deps.triggerRuntime.syncWorkflowTriggersForActivation(workflowId);
785
+ this.deps.webhookTriggerMatcher.reloadWebhookRoutes?.();
786
+ }
787
+ async start(workflows) {
788
+ await this.stop();
789
+ this.loadWorkflows(workflows);
790
+ await this.startTriggers();
791
+ }
792
+ async stop() {
793
+ await this.deps.triggerRuntime.stop();
794
+ this.deps.webhookTriggerMatcher.onEngineStopped?.();
795
+ }
796
+ resolveWebhookTrigger(args) {
797
+ const entry = this.deps.webhookTriggerMatcher.lookup(args.endpointPath);
798
+ if (!entry) return { status: "notFound" };
799
+ if (!entry.methods.includes(args.method)) return {
800
+ status: "methodNotAllowed",
801
+ match: entry
802
+ };
803
+ return {
804
+ status: "ok",
805
+ match: entry
806
+ };
807
+ }
808
+ async createTriggerTestItems(args) {
809
+ return await this.deps.triggerRuntime.createTriggerTestItems(args);
810
+ }
811
+ async runWorkflow(wf, startAt, items, parent, executionOptions, persistedStateOverrides) {
812
+ return await this.deps.runStartService.runWorkflow(wf, startAt, items, parent, executionOptions, persistedStateOverrides);
813
+ }
814
+ async runWorkflowFromState(request) {
815
+ return await this.deps.runStartService.runWorkflowFromState(request);
816
+ }
817
+ async markNodeRunning(args) {
818
+ return await this.deps.runContinuationService.markNodeRunning(args);
819
+ }
820
+ async resumeFromNodeResult(args) {
821
+ return await this.deps.runContinuationService.resumeFromNodeResult(args);
822
+ }
823
+ async resumeFromNodeError(args) {
824
+ return await this.deps.runContinuationService.resumeFromNodeError(args);
825
+ }
826
+ async resumeFromStepResult(args) {
827
+ return await this.deps.runContinuationService.resumeFromStepResult(args);
828
+ }
829
+ async resumeFromStepError(args) {
830
+ return await this.deps.runContinuationService.resumeFromStepError(args);
831
+ }
832
+ async waitForCompletion(runId) {
833
+ return await this.deps.runContinuationService.waitForCompletion(runId);
834
+ }
835
+ async waitForWebhookResponse(runId) {
836
+ return await this.deps.runContinuationService.waitForWebhookResponse(runId);
837
+ }
838
+ async handleNodeExecutionRequest(request) {
839
+ await this.deps.nodeExecutionRequestHandler.handleNodeExecutionRequest(request);
840
+ }
841
+ };
842
+
843
+ //#endregion
844
+ //#region src/runtime/EngineFactory.ts
845
+ /**
846
+ * Composes the {@link Engine} graph from {@link EngineCompositionDeps}. Production wiring usually goes through
847
+ * {@link import("../bootstrap/runtime/EngineRuntimeRegistrar").EngineRuntimeRegistrar}; this factory remains for tests and custom composition.
848
+ * Exported from `@codemation/core/bootstrap` (not the main `@codemation/core` barrel).
849
+ */
850
+ var EngineFactory = class {
851
+ create(deps) {
852
+ const waiters = new EngineWaiters();
853
+ const credentialResolverFactory = new CredentialResolverFactory(deps.credentialSessions);
854
+ const nodeEventPublisher = new NodeEventPublisher(deps.eventBus);
855
+ const nodeStatePublisherFactory = new NodeRunStateWriterFactory(deps.workflowExecutionRepository, nodeEventPublisher);
856
+ const planningFactory = new EngineWorkflowPlanningFactory(deps.workflowNodeInstanceFactory);
857
+ const executionLimitsPolicy = deps.executionLimitsPolicy ?? new EngineExecutionLimitsPolicy();
858
+ const workflowSnapshotCodec = deps.workflowSnapshotCodec ?? new WorkflowSnapshotCodec(deps.tokenRegistry);
859
+ const missingRuntimeFallbacks = deps.missingRuntimeFallbacks ?? new MissingRuntimeFallbacks();
860
+ const workflowSnapshotResolver = new WorkflowSnapshotResolver(deps.workflowRepository, deps.tokenRegistry, workflowSnapshotCodec, missingRuntimeFallbacks);
861
+ const semantics = new RunStateSemantics(new MissingRuntimeExecutionMarker());
862
+ const activationEnqueueService = new ActivationEnqueueService(deps.activationScheduler, deps.workflowExecutionRepository, nodeEventPublisher);
863
+ const runExecutionContextFactory = new WorkflowRunExecutionContextFactory(deps.executionContextFactory, credentialResolverFactory);
864
+ const nodeActivationRequestComposer = new NodeActivationRequestComposer(deps.activationIdFactory, credentialResolverFactory);
865
+ const persistedRunStateTerminalBuilder = new PersistedRunStateTerminalBuilder();
866
+ const storagePolicyEvaluator = new WorkflowStoragePolicyEvaluator(deps.nodeResolver);
867
+ const terminalPersistence = new RunTerminalPersistenceCoordinator(deps.workflowExecutionRepository, storagePolicyEvaluator);
868
+ const policyErrorServices = new WorkflowPolicyErrorServices(deps.nodeResolver);
869
+ const runStartService = new RunStartService(deps.runIdFactory, deps.workflowExecutionRepository, deps.runDataFactory, workflowSnapshotCodec, planningFactory, nodeStatePublisherFactory, runExecutionContextFactory, nodeActivationRequestComposer, activationEnqueueService, semantics, waiters, deps.workflowPolicyRuntimeDefaults, executionLimitsPolicy);
870
+ const runContinuationService = new RunContinuationService(deps.activationIdFactory, deps.workflowExecutionRepository, deps.runDataFactory, runExecutionContextFactory, workflowSnapshotResolver, planningFactory, nodeStatePublisherFactory, credentialResolverFactory, nodeActivationRequestComposer, persistedRunStateTerminalBuilder, activationEnqueueService, nodeEventPublisher, semantics, waiters, policyErrorServices, terminalPersistence, executionLimitsPolicy);
871
+ const nodeExecutionRequestHandler = new NodeExecutionRequestHandlerService(deps.workflowExecutionRepository, workflowSnapshotResolver, deps.runDataFactory, runExecutionContextFactory, nodeStatePublisherFactory, nodeActivationRequestComposer, deps.nodeExecutor, runContinuationService, executionLimitsPolicy);
872
+ const triggerRuntime = new TriggerRuntimeService(deps.workflowRepository, deps.workflowActivationPolicy, deps.runIdFactory, deps.runDataFactory, deps.executionContextFactory, credentialResolverFactory, nodeStatePublisherFactory, deps.nodeResolver, deps.triggerSetupStateRepository, { emit: async (workflow, triggerNodeId, items) => {
873
+ await runStartService.runWorkflow(workflow, triggerNodeId, items, void 0);
874
+ } }, executionLimitsPolicy, deps.triggerRuntimeDiagnostics);
875
+ const engine = new Engine({
876
+ liveWorkflowRepository: deps.liveWorkflowRepository,
877
+ tokenRegistry: deps.tokenRegistry,
878
+ webhookTriggerMatcher: deps.webhookTriggerMatcher,
879
+ workflowSnapshotResolver,
880
+ triggerRuntime,
881
+ runStartService,
882
+ runContinuationService,
883
+ nodeExecutionRequestHandler
884
+ });
885
+ deps.activationScheduler.setContinuation?.(engine);
886
+ return engine;
887
+ }
888
+ };
889
+
890
+ //#endregion
891
+ //#region src/runtime/EngineWorkflowRunnerService.ts
892
+ var EngineWorkflowRunnerService = class {
893
+ constructor(engine, workflowRepository) {
894
+ this.engine = engine;
895
+ this.workflowRepository = workflowRepository;
896
+ }
897
+ async runById(args) {
898
+ const { workflowId, startAt, items, parent } = args;
899
+ const wf = this.workflowRepository.get(workflowId);
900
+ if (!wf) throw new Error(`Unknown workflowId: ${workflowId}`);
901
+ const startNodeId = startAt ?? this.findDefaultStartNodeId(wf);
902
+ const scheduled = await this.engine.runWorkflow(wf, startNodeId, items, parent);
903
+ if (scheduled.status !== "pending") return scheduled;
904
+ return await this.engine.waitForCompletion(scheduled.runId);
905
+ }
906
+ findDefaultStartNodeId(wf) {
907
+ return WorkflowExecutableNodeClassifierFactory.create(wf).findDefaultExecutableStartNodeId(wf);
908
+ }
909
+ };
910
+
911
+ //#endregion
912
+ //#region src/runtime/EngineWorkflowRunnerServiceFactory.ts
913
+ var EngineWorkflowRunnerServiceFactory = class {
914
+ create(engine, workflowRepository) {
915
+ return new EngineWorkflowRunnerService(engine, workflowRepository);
916
+ }
917
+ };
918
+
919
+ //#endregion
920
+ //#region src/runtime/RunIntentServiceFactory.ts
921
+ var RunIntentServiceFactory = class {
922
+ create(engine, workflowRepository) {
923
+ return new RunIntentService(engine, workflowRepository);
924
+ }
925
+ };
926
+
927
+ //#endregion
928
+ //#region src/runtime/WorkflowRepositoryWebhookTriggerMatcher.ts
929
+ /**
930
+ * Resolves webhook HTTP routes from the live workflow repository (no trigger setup / registration).
931
+ * Maintains an in-memory index keyed by user-defined endpoint path for O(1) lookups after reload.
932
+ */
933
+ var WorkflowRepositoryWebhookTriggerMatcher = class {
934
+ routeByPath = /* @__PURE__ */ new Map();
935
+ engineRoutesActive = false;
936
+ constructor(workflowRepository, workflowActivationPolicy, diagnostics) {
937
+ this.workflowRepository = workflowRepository;
938
+ this.workflowActivationPolicy = workflowActivationPolicy;
939
+ this.diagnostics = diagnostics;
940
+ }
941
+ onEngineWorkflowsLoaded() {
942
+ this.engineRoutesActive = true;
943
+ this.rebuildRouteIndex();
944
+ }
945
+ onEngineStopped() {
946
+ this.engineRoutesActive = false;
947
+ this.routeByPath.clear();
948
+ }
949
+ reloadWebhookRoutes() {
950
+ if (!this.engineRoutesActive) return;
951
+ this.rebuildRouteIndex();
952
+ }
953
+ lookup(endpointPath) {
954
+ if (!this.engineRoutesActive) return;
955
+ const normalized = this.normalizeEndpointPath(endpointPath);
956
+ return this.routeByPath.get(normalized);
957
+ }
958
+ match(args) {
959
+ const entry = this.lookup(args.endpointPath);
960
+ if (!entry) return;
961
+ return entry.methods.includes(args.method) ? entry : void 0;
962
+ }
963
+ rebuildRouteIndex() {
964
+ this.routeByPath.clear();
965
+ for (const workflow of this.workflowRepository.list()) {
966
+ if (!this.workflowActivationPolicy.isActive(workflow.id)) {
967
+ if (workflow.nodes.filter((n) => n.kind === "trigger").length > 0) {
968
+ const paths = this.collectWebhookEndpointPaths(workflow);
969
+ if (paths.length > 0) this.diagnostics?.info?.(`Workflow "${workflow.name}" (${workflow.id}) is inactive; webhook routes not registered: ${paths.map((p) => `"${p}"`).join(", ")}`);
970
+ else this.diagnostics?.info?.(`Workflow "${workflow.name}" (${workflow.id}) is inactive; no repository webhook routes for its triggers (other trigger kinds are unchanged).`);
971
+ }
972
+ continue;
973
+ }
974
+ for (const def of workflow.nodes) {
975
+ const match = this.tryMatchFromTriggerNode(workflow, def);
976
+ if (!match) continue;
977
+ const key = this.normalizeEndpointPath(match.endpointPath);
978
+ const existing = this.routeByPath.get(key);
979
+ if (existing) this.diagnostics?.warn(`Duplicate webhook endpoint path "${key}" (workflows "${existing.workflowId}" and "${match.workflowId}"); using "${match.workflowId}".`);
980
+ this.routeByPath.set(key, match);
981
+ }
982
+ }
983
+ }
984
+ collectWebhookEndpointPaths(workflow) {
985
+ const paths = [];
986
+ for (const def of workflow.nodes) {
987
+ if (def.kind !== "trigger") continue;
988
+ const match = this.tryMatchFromTriggerNode(workflow, def);
989
+ if (match) paths.push(match.endpointPath);
990
+ }
991
+ return paths;
992
+ }
993
+ tryMatchFromTriggerNode(workflow, def) {
994
+ if (def.kind !== "trigger") return;
995
+ const config = def.config;
996
+ if (typeof config.endpointKey !== "string" || config.endpointKey.length === 0) return;
997
+ if (!Array.isArray(config.methods) || config.methods.length === 0) return;
998
+ const methods = config.methods;
999
+ const parseJsonBody = typeof config.parseJsonBody === "function" ? config.parseJsonBody.bind(config) : void 0;
1000
+ return {
1001
+ endpointPath: config.endpointKey,
1002
+ workflowId: workflow.id,
1003
+ nodeId: def.id,
1004
+ methods: [...methods],
1005
+ parseJsonBody
1006
+ };
1007
+ }
1008
+ normalizeEndpointPath(endpointPath) {
1009
+ return endpointPath.trim();
1010
+ }
1011
+ };
1012
+
1013
+ //#endregion
1014
+ //#region src/runtime/WorkflowRepositoryWebhookTriggerMatcherFactory.ts
1015
+ var WorkflowRepositoryWebhookTriggerMatcherFactory = class {
1016
+ create(workflowRepository, workflowActivationPolicy, diagnostics) {
1017
+ return new WorkflowRepositoryWebhookTriggerMatcher(workflowRepository, workflowActivationPolicy, diagnostics);
1018
+ }
1019
+ };
1020
+
1021
+ //#endregion
1022
+ //#region src/scheduler/InlineDrivingSchedulerFactory.ts
1023
+ var InlineDrivingSchedulerFactory = class {
1024
+ create(nodeExecutor) {
1025
+ return new InlineDrivingScheduler(nodeExecutor);
1026
+ }
1027
+ };
1028
+
1029
+ //#endregion
1030
+ //#region src/bootstrap/runtime/EngineRuntimeRegistrar.ts
1031
+ /**
1032
+ * Container-first entry: call on a host/test container **after** workflow, run, node, and credential
1033
+ * ports are registered. The registrar owns the default inline scheduler, engine binding,
1034
+ * and intent-surface wiring so hosts only override the seams they actually replace.
1035
+ */
1036
+ var EngineRuntimeRegistrar = class {
1037
+ register(container, options) {
1038
+ this.registerSupportFactories(container);
1039
+ this.registerExecutionLimitsPolicy(container, options);
1040
+ this.ensureWorkflowNodeInstanceFactory(container);
1041
+ this.ensureNodeExecutor(container);
1042
+ this.registerDefaultActivationScheduler(container);
1043
+ this.registerEngine(container, options);
1044
+ this.registerIntentServices(container);
1045
+ }
1046
+ registerSupportFactories(container) {
1047
+ container.register(EngineExecutionLimitsPolicyFactory, { useClass: EngineExecutionLimitsPolicyFactory });
1048
+ container.register(NodeInstanceFactoryFactory, { useClass: NodeInstanceFactoryFactory });
1049
+ container.register(DefaultAsyncSleeper, { useClass: DefaultAsyncSleeper });
1050
+ container.register(InProcessRetryRunnerFactory, { useClass: InProcessRetryRunnerFactory });
1051
+ container.register(NodeExecutorFactory, { useClass: NodeExecutorFactory });
1052
+ container.register(InlineDrivingSchedulerFactory, { useClass: InlineDrivingSchedulerFactory });
1053
+ container.register(RunIntentServiceFactory, { useClass: RunIntentServiceFactory });
1054
+ container.register(EngineWorkflowRunnerServiceFactory, { useClass: EngineWorkflowRunnerServiceFactory });
1055
+ container.register(WorkflowRepositoryWebhookTriggerMatcherFactory, { useClass: WorkflowRepositoryWebhookTriggerMatcherFactory });
1056
+ }
1057
+ registerExecutionLimitsPolicy(container, options) {
1058
+ if (container.isRegistered(CoreTokens.EngineExecutionLimitsPolicy, true)) return;
1059
+ container.register(CoreTokens.EngineExecutionLimitsPolicy, { useFactory: instanceCachingFactory((dependencyContainer) => {
1060
+ const merged = options?.resolveEngineExecutionLimits?.() ?? options?.engineExecutionLimits;
1061
+ return dependencyContainer.resolve(EngineExecutionLimitsPolicyFactory).create(merged);
1062
+ }) });
1063
+ }
1064
+ ensureWorkflowNodeInstanceFactory(container) {
1065
+ if (container.isRegistered(CoreTokens.WorkflowNodeInstanceFactory, true)) return;
1066
+ container.register(CoreTokens.WorkflowNodeInstanceFactory, { useFactory: instanceCachingFactory((dependencyContainer) => {
1067
+ return dependencyContainer.resolve(NodeInstanceFactoryFactory).create(dependencyContainer.resolve(CoreTokens.NodeResolver));
1068
+ }) });
1069
+ }
1070
+ ensureNodeExecutor(container) {
1071
+ if (container.isRegistered(NodeExecutor, true)) return;
1072
+ container.register(NodeExecutor, { useFactory: instanceCachingFactory((dependencyContainer) => {
1073
+ const retryRunner = dependencyContainer.resolve(InProcessRetryRunnerFactory).create(dependencyContainer.resolve(DefaultAsyncSleeper));
1074
+ return dependencyContainer.resolve(NodeExecutorFactory).create(dependencyContainer.resolve(CoreTokens.WorkflowNodeInstanceFactory), retryRunner);
1075
+ }) });
1076
+ }
1077
+ registerDefaultActivationScheduler(container) {
1078
+ if (container.isRegistered(CoreTokens.NodeActivationScheduler, true)) return;
1079
+ container.register(InlineDrivingScheduler, { useFactory: instanceCachingFactory((dependencyContainer) => {
1080
+ return dependencyContainer.resolve(InlineDrivingSchedulerFactory).create(dependencyContainer.resolve(NodeExecutor));
1081
+ }) });
1082
+ container.register(CoreTokens.NodeActivationScheduler, { useFactory: instanceCachingFactory((dependencyContainer) => {
1083
+ return dependencyContainer.resolve(InlineDrivingScheduler);
1084
+ }) });
1085
+ }
1086
+ registerEngine(container, options) {
1087
+ container.register(EngineFactory, { useClass: EngineFactory });
1088
+ const matcherProvider = this.resolveMatcherProvider(options);
1089
+ container.register(Engine, { useFactory: instanceCachingFactory((dependencyContainer) => {
1090
+ const liveWorkflowRepository = dependencyContainer.resolve(CoreTokens.LiveWorkflowRepository);
1091
+ const nodeResolver = dependencyContainer.resolve(CoreTokens.NodeResolver);
1092
+ const tokenRegistryLike = dependencyContainer.resolve(CoreTokens.PersistedWorkflowTokenRegistry);
1093
+ const workflowActivationPolicy = dependencyContainer.resolve(CoreTokens.WorkflowActivationPolicy);
1094
+ const webhookTriggerMatcher = matcherProvider.createMatcher(dependencyContainer);
1095
+ const workflowNodeInstanceFactory = dependencyContainer.resolve(CoreTokens.WorkflowNodeInstanceFactory);
1096
+ const triggerRuntimeDiagnostics = options?.triggerRuntimeDiagnosticsProvider?.create(dependencyContainer);
1097
+ return dependencyContainer.resolve(EngineFactory).create({
1098
+ credentialSessions: dependencyContainer.resolve(CoreTokens.CredentialSessionService),
1099
+ liveWorkflowRepository,
1100
+ workflowRepository: dependencyContainer.resolve(CoreTokens.WorkflowRepository),
1101
+ workflowActivationPolicy,
1102
+ nodeResolver,
1103
+ triggerSetupStateRepository: dependencyContainer.resolve(CoreTokens.TriggerSetupStateRepository),
1104
+ webhookTriggerMatcher,
1105
+ runIdFactory: dependencyContainer.resolve(CoreTokens.RunIdFactory),
1106
+ activationIdFactory: dependencyContainer.resolve(CoreTokens.ActivationIdFactory),
1107
+ workflowExecutionRepository: dependencyContainer.resolve(CoreTokens.WorkflowExecutionRepository),
1108
+ activationScheduler: dependencyContainer.resolve(CoreTokens.NodeActivationScheduler),
1109
+ runDataFactory: dependencyContainer.resolve(CoreTokens.RunDataFactory),
1110
+ executionContextFactory: dependencyContainer.resolve(CoreTokens.ExecutionContextFactory),
1111
+ nodeExecutor: dependencyContainer.resolve(NodeExecutor),
1112
+ eventBus: dependencyContainer.resolve(CoreTokens.RunEventBus),
1113
+ tokenRegistry: tokenRegistryLike,
1114
+ workflowNodeInstanceFactory,
1115
+ executionLimitsPolicy: dependencyContainer.resolve(CoreTokens.EngineExecutionLimitsPolicy),
1116
+ triggerRuntimeDiagnostics
1117
+ });
1118
+ }) });
1119
+ }
1120
+ registerIntentServices(container) {
1121
+ container.register(RunIntentService, { useFactory: instanceCachingFactory((dependencyContainer) => {
1122
+ return dependencyContainer.resolve(RunIntentServiceFactory).create(dependencyContainer.resolve(Engine), dependencyContainer.resolve(CoreTokens.WorkflowRepository));
1123
+ }) });
1124
+ container.register(CoreTokens.WorkflowRunnerService, { useFactory: instanceCachingFactory((dependencyContainer) => {
1125
+ return dependencyContainer.resolve(EngineWorkflowRunnerServiceFactory).create(dependencyContainer.resolve(Engine), dependencyContainer.resolve(CoreTokens.WorkflowRepository));
1126
+ }) });
1127
+ }
1128
+ resolveMatcherProvider(options) {
1129
+ if (options?.webhookTriggerMatcherProvider) return options.webhookTriggerMatcherProvider;
1130
+ return { createMatcher: (container) => container.resolve(WorkflowRepositoryWebhookTriggerMatcherFactory).create(container.resolve(CoreTokens.WorkflowRepository), container.resolve(CoreTokens.WorkflowActivationPolicy), options?.webhookTriggerRoutingDiagnostics) };
1131
+ }
1132
+ };
1133
+
1134
+ //#endregion
1135
+ export { Engine as a, WorkflowStoragePolicyEvaluator as c, EngineExecutionLimitsPolicyFactory as d, EngineFactory as i, WorkflowPolicyErrorServices as l, WorkflowRepositoryWebhookTriggerMatcher as n, InMemoryWorkflowExecutionRepository as o, EngineWorkflowRunnerService as r, RunSummaryMapper as s, EngineRuntimeRegistrar as t, RunTerminalPersistenceCoordinator as u };
1136
+ //# sourceMappingURL=bootstrap-D67Sf2BF.js.map