@codemation/core 0.0.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +24 -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-BB4nqX3-.js → RunIntentService-BFA48UpH.js} +308 -71
  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-nRx-m0Xs.cjs → RunIntentService-DcxXf_AM.cjs} +318 -69
  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-B4_ZRTyI.d.ts → index-BHmrZIHp.d.ts} +32 -251
  20. package/dist/index.cjs +98 -223
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +196 -6
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +92 -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/browser.ts +11 -0
  41. package/src/contracts/executionPersistenceContracts.ts +186 -0
  42. package/src/contracts/index.ts +1 -0
  43. package/src/contracts/runFinishedAtFactory.ts +5 -2
  44. package/src/contracts/runTypes.ts +10 -0
  45. package/src/contracts/runtimeTypes.ts +6 -2
  46. package/src/contracts/workflowTypes.ts +3 -2
  47. package/src/events/EventPublishingWorkflowExecutionRepository.ts +5 -0
  48. package/src/execution/ActivationEnqueueService.ts +8 -8
  49. package/src/execution/PersistedRunStateTerminalBuilder.ts +3 -0
  50. package/src/index.ts +6 -0
  51. package/src/orchestration/NodeExecutionRequestHandlerService.ts +11 -6
  52. package/src/orchestration/RunContinuationService.ts +94 -24
  53. package/src/planning/CurrentStateFrontierPlanner.ts +24 -1
  54. package/src/runStorage/InMemoryWorkflowExecutionRepository.ts +14 -1
  55. package/src/runtime/RunIntentService.ts +68 -14
  56. package/src/scheduler/DefaultDrivingScheduler.ts +21 -11
  57. package/src/scheduler/InlineDrivingScheduler.ts +17 -21
  58. package/src/testing/CapturingScheduler.ts +15 -0
  59. package/src/testing/EngineTestKitRunIdFactory.ts +24 -0
  60. package/src/testing/InMemoryTriggerSetupStateRepository.ts +21 -0
  61. package/src/testing/PrefixedSequentialIdGenerator.ts +17 -0
  62. package/src/testing/RegistrarEngineTestKit.types.ts +76 -0
  63. package/src/testing/RegistrarEngineTestKitFactory.ts +154 -0
  64. package/src/testing/SubWorkflowRunnerTestNode.ts +83 -0
  65. package/src/testing/WorkflowTestHarnessManualTrigger.ts +39 -0
  66. package/src/testing/WorkflowTestKit.types.ts +9 -0
  67. package/src/testing/WorkflowTestKitBuilder.ts +77 -0
  68. package/src/testing/WorkflowTestKitNodeRegistrationContextFactory.ts +17 -0
  69. package/src/testing/WorkflowTestKitRunNodeWorkflowFactory.ts +26 -0
  70. package/src/testing.ts +19 -0
  71. package/src/types/index.ts +1 -0
  72. package/src/workflow/definition/ConnectionNodeIdFactory.ts +28 -0
  73. package/dist/InMemoryLiveWorkflowRepository-BTzHpQ6e.cjs +0 -151
  74. package/dist/InMemoryLiveWorkflowRepository-BTzHpQ6e.cjs.map +0 -1
  75. package/dist/InMemoryLiveWorkflowRepository-BoLNnVLg.js +0 -139
  76. package/dist/InMemoryLiveWorkflowRepository-BoLNnVLg.js.map +0 -1
  77. package/dist/RunIntentService-BB4nqX3-.js.map +0 -1
  78. package/dist/RunIntentService-ByuUYsAL.d.cts +0 -279
  79. package/dist/RunIntentService-nRx-m0Xs.cjs.map +0 -1
  80. package/dist/WorkflowSnapshotCodec-DSEzKyt3.d.cts +0 -22
  81. package/dist/bootstrap/index.cjs.map +0 -1
  82. package/dist/bootstrap/index.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import "reflect-metadata";
2
- import { container, delay, inject, injectAll, injectable, instanceCachingFactory, instancePerContainerCachingFactory, predicateAwareClassFactory, registry, singleton } from "tsyringe";
2
+ import { container as container$1, delay, inject, injectAll, injectable, instanceCachingFactory, instancePerContainerCachingFactory, predicateAwareClassFactory, registry, singleton } from "tsyringe";
3
3
  import { createHash } from "node:crypto";
4
4
  import { ReadableStream } from "node:stream/web";
5
5
 
@@ -157,6 +157,25 @@ var ConnectionNodeIdFactory = class {
157
157
  static isToolConnectionNodeId(nodeId) {
158
158
  return nodeId.includes(`${this.connectionSegment}tool${this.connectionSegment}`);
159
159
  }
160
+ static parseLanguageModelConnectionNodeId(nodeId) {
161
+ if (!this.isLanguageModelConnectionNodeId(nodeId)) return;
162
+ const suffix = `${this.connectionSegment}llm`;
163
+ const parentNodeId = nodeId.slice(0, -suffix.length);
164
+ return parentNodeId ? { parentNodeId } : void 0;
165
+ }
166
+ static parseToolConnectionNodeId(nodeId) {
167
+ if (!this.isToolConnectionNodeId(nodeId)) return;
168
+ const marker = `${this.connectionSegment}tool${this.connectionSegment}`;
169
+ const idx = nodeId.lastIndexOf(marker);
170
+ if (idx < 0) return;
171
+ const parentNodeId = nodeId.slice(0, idx);
172
+ const normalizedToolName = nodeId.slice(idx + marker.length);
173
+ if (!parentNodeId || !normalizedToolName) return;
174
+ return {
175
+ parentNodeId,
176
+ normalizedToolName
177
+ };
178
+ }
160
179
  /** True when `nodeId` is a connection-owned child of `parentNodeId` (LLM or tool slot). */
161
180
  static isConnectionOwnedDescendantOf(parentNodeId, nodeId) {
162
181
  return nodeId.startsWith(`${parentNodeId}${this.connectionSegment}`);
@@ -479,7 +498,7 @@ var ActivationEnqueueService = class {
479
498
  return result;
480
499
  }
481
500
  async enqueueActivationWithSnapshot(args) {
482
- const receipt = await this.activationScheduler.enqueue(args.request);
501
+ const preparedDispatch = await this.activationScheduler.prepareDispatch(args.request);
483
502
  const inputsByPort = NodeInputsByPortFactory.fromRequest(args.request);
484
503
  const itemsIn = args.request.kind === "multi" ? args.planner.sumItemsByPort(args.request.inputsByPort) : args.request.input.length;
485
504
  const enqueuedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -490,8 +509,8 @@ var ActivationEnqueueService = class {
490
509
  nodeId: args.request.nodeId,
491
510
  itemsIn,
492
511
  inputsByPort,
493
- receiptId: receipt.receiptId,
494
- queue: receipt.queue,
512
+ receiptId: preparedDispatch.receipt.receiptId,
513
+ queue: preparedDispatch.receipt.queue,
495
514
  batchId: args.request.batchId,
496
515
  enqueuedAt
497
516
  };
@@ -525,7 +544,7 @@ var ActivationEnqueueService = class {
525
544
  [args.request.nodeId]: queuedSnapshot
526
545
  }
527
546
  });
528
- this.notifyPendingStatePersisted(args.runId);
547
+ await this.dispatchPreparedActivation(preparedDispatch);
529
548
  return {
530
549
  result: {
531
550
  runId: args.runId,
@@ -537,8 +556,8 @@ var ActivationEnqueueService = class {
537
556
  queuedSnapshot
538
557
  };
539
558
  }
540
- notifyPendingStatePersisted(runId) {
541
- this.activationScheduler.notifyPendingStatePersisted?.(runId);
559
+ async dispatchPreparedActivation(preparedDispatch) {
560
+ await preparedDispatch.dispatch();
542
561
  }
543
562
  };
544
563
 
@@ -959,6 +978,127 @@ var PersistedWorkflowTokenRegistry = class {
959
978
  }
960
979
  };
961
980
 
981
+ //#endregion
982
+ //#region src/workflowSnapshots/WorkflowSnapshotCodec.ts
983
+ var WorkflowSnapshotCodec = class {
984
+ constructor(tokenRegistry) {
985
+ this.tokenRegistry = tokenRegistry;
986
+ }
987
+ create(workflow) {
988
+ return {
989
+ id: workflow.id,
990
+ name: workflow.name,
991
+ workflowErrorHandlerConfigured: workflow.workflowErrorHandler !== void 0,
992
+ ...workflow.connections !== void 0 && workflow.connections.length > 0 ? { connections: workflow.connections } : {},
993
+ nodes: workflow.nodes.map((node$1) => ({
994
+ id: node$1.id,
995
+ kind: node$1.kind,
996
+ name: node$1.name,
997
+ nodeTokenId: this.resolveTokenId(node$1.type),
998
+ configTokenId: this.resolveTokenId(node$1.config.type),
999
+ tokenName: this.resolveTokenName(node$1.type),
1000
+ configTokenName: this.resolveTokenName(node$1.config.type),
1001
+ config: this.serializeConfig(node$1.config)
1002
+ })),
1003
+ edges: workflow.edges.map((edge) => ({
1004
+ from: {
1005
+ nodeId: edge.from.nodeId,
1006
+ output: edge.from.output
1007
+ },
1008
+ to: {
1009
+ nodeId: edge.to.nodeId,
1010
+ input: edge.to.input
1011
+ }
1012
+ }))
1013
+ };
1014
+ }
1015
+ hydrate(snapshotNode, liveConfig) {
1016
+ const hydrated = this.mergeValue(liveConfig, snapshotNode.config);
1017
+ const configToken = this.tokenRegistry.resolve(snapshotNode.configTokenId);
1018
+ Object.assign(hydrated, {
1019
+ type: configToken ?? liveConfig.type,
1020
+ kind: snapshotNode.kind
1021
+ });
1022
+ if (snapshotNode.name && !("name" in hydrated && hydrated.name)) Object.assign(hydrated, { name: snapshotNode.name });
1023
+ return hydrated;
1024
+ }
1025
+ serializeConfig(config) {
1026
+ try {
1027
+ const cloned = JSON.parse(JSON.stringify(config));
1028
+ this.injectTokenIds(cloned, config);
1029
+ return cloned;
1030
+ } catch {
1031
+ const fallback = {
1032
+ kind: config.kind,
1033
+ name: config.name,
1034
+ id: config.id,
1035
+ icon: config.icon,
1036
+ execution: config.execution
1037
+ };
1038
+ this.injectTokenIds(fallback, config);
1039
+ return fallback;
1040
+ }
1041
+ }
1042
+ injectTokenIds(target, source) {
1043
+ const type = this.asTypeToken(source.type);
1044
+ if (type) target.tokenId = this.tokenRegistry.getTokenId(type) ?? this.resolveTokenName(type) ?? "unknown";
1045
+ for (const [key, value] of Object.entries(source)) {
1046
+ if (key === "type" || value == null) continue;
1047
+ if (Array.isArray(value)) {
1048
+ const targetArray = target[key];
1049
+ if (Array.isArray(targetArray)) value.forEach((item, index) => {
1050
+ if (item && typeof item === "object" && targetArray[index] && typeof targetArray[index] === "object") this.injectTokenIds(targetArray[index], item);
1051
+ });
1052
+ continue;
1053
+ }
1054
+ if (typeof value === "object") {
1055
+ const targetValue = target[key];
1056
+ if (targetValue && typeof targetValue === "object") this.injectTokenIds(targetValue, value);
1057
+ }
1058
+ }
1059
+ }
1060
+ mergeValue(liveValue, snapshotValue) {
1061
+ const liveRecord = this.asRecord(liveValue);
1062
+ const snapshotRecord = this.asRecord(snapshotValue);
1063
+ const hydrated = Object.create(liveValue && typeof liveValue === "object" ? Object.getPrototypeOf(liveValue) ?? Object.prototype : Object.prototype);
1064
+ for (const [key, value] of Object.entries(snapshotRecord)) hydrated[key] = this.mergeNestedValue(liveRecord[key], value);
1065
+ this.restoreNonSerializableProperties(liveRecord, hydrated);
1066
+ this.restoreTypeProperty(hydrated);
1067
+ return hydrated;
1068
+ }
1069
+ mergeNestedValue(liveValue, snapshotValue) {
1070
+ if (Array.isArray(snapshotValue)) {
1071
+ const liveArray = Array.isArray(liveValue) ? liveValue : [];
1072
+ return snapshotValue.map((entry, index) => this.mergeNestedValue(liveArray[index], entry));
1073
+ }
1074
+ if (snapshotValue && typeof snapshotValue === "object") return this.mergeValue(liveValue, snapshotValue);
1075
+ return snapshotValue;
1076
+ }
1077
+ restoreNonSerializableProperties(liveRecord, hydrated) {
1078
+ for (const [key, value] of Object.entries(liveRecord)) if (typeof value === "function" || typeof value === "symbol") hydrated[key] = value;
1079
+ }
1080
+ restoreTypeProperty(record) {
1081
+ const tokenId = typeof record.tokenId === "string" ? record.tokenId : void 0;
1082
+ if (!tokenId) return;
1083
+ const type = this.tokenRegistry.resolve(tokenId);
1084
+ if (type) record.type = type;
1085
+ }
1086
+ resolveTokenId(token) {
1087
+ return this.tokenRegistry.getTokenId(token) ?? this.resolveTokenName(token) ?? "unknown";
1088
+ }
1089
+ resolveTokenName(token) {
1090
+ if (typeof token === "function" && token.name) return token.name;
1091
+ if (typeof token === "string") return token;
1092
+ }
1093
+ asTypeToken(value) {
1094
+ if (typeof value === "function" || typeof value === "string" || typeof value === "symbol") return value;
1095
+ }
1096
+ asRecord(value) {
1097
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1098
+ return { ...value };
1099
+ }
1100
+ };
1101
+
962
1102
  //#endregion
963
1103
  //#region src/workflowSnapshots/WorkflowSnapshotResolver.ts
964
1104
  var WorkflowSnapshotResolver = class {
@@ -1208,7 +1348,8 @@ var PersistedRunStateTerminalBuilder = class {
1208
1348
  pending: void 0,
1209
1349
  queue: args.queue,
1210
1350
  outputsByNode: args.outputsByNode,
1211
- nodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId
1351
+ nodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId,
1352
+ finishedAt: args.finishedAtIso ?? args.state.finishedAt
1212
1353
  };
1213
1354
  }
1214
1355
  };
@@ -1398,9 +1539,10 @@ var RunContinuationService = class {
1398
1539
  this.executionLimitsPolicy = executionLimitsPolicy;
1399
1540
  }
1400
1541
  async markNodeRunning(args) {
1401
- const state = await this.workflowExecutionRepository.load(args.runId);
1402
- if (!state?.pending) return;
1403
- if (state.pending.activationId !== args.activationId || state.pending.nodeId !== args.nodeId) return;
1542
+ const [state, schedulingState] = await Promise.all([this.workflowExecutionRepository.load(args.runId), this.workflowExecutionRepository.loadSchedulingState(args.runId)]);
1543
+ const pendingExecution = schedulingState?.pending;
1544
+ if (!state || !pendingExecution) return;
1545
+ if (pendingExecution.activationId !== args.activationId || pendingExecution.nodeId !== args.nodeId) return;
1404
1546
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1405
1547
  const previous = state.nodeSnapshotsByNodeId?.[args.nodeId];
1406
1548
  const snapshot = NodeExecutionSnapshotFactory.running({
@@ -1423,11 +1565,11 @@ var RunContinuationService = class {
1423
1565
  await this.nodeEventPublisher.publish("nodeStarted", snapshot);
1424
1566
  }
1425
1567
  async resumeFromNodeResult(args) {
1426
- const state = await this.workflowExecutionRepository.load(args.runId);
1568
+ const [state, schedulingState] = await Promise.all([this.workflowExecutionRepository.load(args.runId), this.workflowExecutionRepository.loadSchedulingState(args.runId)]);
1427
1569
  if (!state) throw new Error(`Unknown runId: ${args.runId}`);
1428
- if (state.status !== "pending" || !state.pending) throw new Error(`Run ${args.runId} is not pending`);
1429
- if (state.pending.activationId !== args.activationId) throw new Error(`activationId mismatch for run ${args.runId}`);
1430
- if (state.pending.nodeId !== args.nodeId) throw new Error(`nodeId mismatch for run ${args.runId}`);
1570
+ const pendingExecution = this.requirePendingExecution(args.runId, args.activationId, args.nodeId, state, schedulingState);
1571
+ if (pendingExecution.activationId !== args.activationId) throw new Error(`activationId mismatch for run ${args.runId}`);
1572
+ if (pendingExecution.nodeId !== args.nodeId) throw new Error(`nodeId mismatch for run ${args.runId}`);
1431
1573
  const wf = this.resolvePersistedWorkflow(state);
1432
1574
  if (!wf) throw new Error(`Unknown workflowId: ${state.workflowId}`);
1433
1575
  const { topology, planner } = this.planningFactory.create(wf);
@@ -1455,7 +1597,7 @@ var RunContinuationService = class {
1455
1597
  activationId: args.activationId,
1456
1598
  parent: state.parent,
1457
1599
  finishedAt: completedAt,
1458
- inputsByPort: state.pending.inputsByPort,
1600
+ inputsByPort: pendingExecution.inputsByPort,
1459
1601
  outputs: args.outputs
1460
1602
  });
1461
1603
  const completedActivations = (state.engineCounters?.completedNodeActivations ?? 0) + 1;
@@ -1471,7 +1613,8 @@ var RunContinuationService = class {
1471
1613
  nodeSnapshotsByNodeId: {
1472
1614
  ...state.nodeSnapshotsByNodeId ?? {},
1473
1615
  [args.nodeId]: completedSnapshot
1474
- }
1616
+ },
1617
+ finishedAtIso: completedAt
1475
1618
  });
1476
1619
  await this.workflowExecutionRepository.save(completedState);
1477
1620
  await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
@@ -1491,8 +1634,8 @@ var RunContinuationService = class {
1491
1634
  this.waiters.resolveRunCompletion(result$1);
1492
1635
  return result$1;
1493
1636
  }
1494
- const batchId = state.pending.batchId ?? "batch_1";
1495
- const queue = (state.queue ?? []).map((q) => ({
1637
+ const batchId = pendingExecution.batchId ?? "batch_1";
1638
+ const queue = (schedulingState?.queue ?? []).map((q) => ({
1496
1639
  ...q,
1497
1640
  batchId: q.batchId ?? batchId
1498
1641
  }));
@@ -1537,7 +1680,8 @@ var RunContinuationService = class {
1537
1680
  status: "completed",
1538
1681
  queue: [],
1539
1682
  outputsByNode: data.dump(),
1540
- nodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId
1683
+ nodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId,
1684
+ finishedAtIso: completedAt
1541
1685
  });
1542
1686
  await this.workflowExecutionRepository.save(completedState);
1543
1687
  await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
@@ -1565,7 +1709,8 @@ var RunContinuationService = class {
1565
1709
  status: "failed",
1566
1710
  queue: queue.map((q) => ({ ...q })),
1567
1711
  outputsByNode: data.dump(),
1568
- nodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId
1712
+ nodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId,
1713
+ finishedAtIso: completedAt
1569
1714
  });
1570
1715
  await this.workflowExecutionRepository.save(failedState);
1571
1716
  await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
@@ -1619,17 +1764,19 @@ var RunContinuationService = class {
1619
1764
  return result;
1620
1765
  }
1621
1766
  async resumeFromNodeError(args) {
1622
- const state = await this.workflowExecutionRepository.load(args.runId);
1767
+ const [state, schedulingState] = await Promise.all([this.workflowExecutionRepository.load(args.runId), this.workflowExecutionRepository.loadSchedulingState(args.runId)]);
1623
1768
  if (!state) throw new Error(`Unknown runId: ${args.runId}`);
1624
- if (state.status !== "pending" || !state.pending) throw new Error(`Run ${args.runId} is not pending`);
1625
- if (state.pending.activationId !== args.activationId) throw new Error(`activationId mismatch for run ${args.runId}`);
1626
- if (state.pending.nodeId !== args.nodeId) throw new Error(`nodeId mismatch for run ${args.runId}`);
1769
+ const pendingExecution = this.requirePendingExecution(args.runId, args.activationId, args.nodeId, state, schedulingState);
1770
+ if (pendingExecution.activationId !== args.activationId) throw new Error(`activationId mismatch for run ${args.runId}`);
1771
+ if (pendingExecution.nodeId !== args.nodeId) throw new Error(`nodeId mismatch for run ${args.runId}`);
1627
1772
  const wf = this.resolvePersistedWorkflow(state);
1628
1773
  if (!wf) throw new Error(`Unknown workflowId: ${state.workflowId}`);
1629
1774
  const failedDefinition = WorkflowTopology.fromWorkflow(wf).defsById.get(args.nodeId);
1630
1775
  const webhookControlSignal = state.executionOptions?.webhook && failedDefinition?.kind === "trigger" ? this.asWebhookControlSignal(args.error) : void 0;
1631
1776
  if (webhookControlSignal) return await this.resumeFromWebhookControl({
1632
1777
  state,
1778
+ schedulingState,
1779
+ pendingExecution,
1633
1780
  workflow: wf,
1634
1781
  args,
1635
1782
  signal: webhookControlSignal
@@ -1637,8 +1784,8 @@ var RunContinuationService = class {
1637
1784
  if (failedDefinition && failedDefinition.kind === "node") {
1638
1785
  const nodeHandler = this.policyErrorServices.resolveNodeErrorHandler(failedDefinition.config.nodeErrorHandler);
1639
1786
  if (nodeHandler) try {
1640
- const ctx = this.buildNodeExecutionContextForPending(state, wf, failedDefinition, args.nodeId);
1641
- const inputsByPort = state.pending.inputsByPort;
1787
+ const ctx = this.buildNodeExecutionContextForPending(state, pendingExecution, wf, failedDefinition, args.nodeId);
1788
+ const inputsByPort = pendingExecution.inputsByPort;
1642
1789
  const portKeys = Object.keys(inputsByPort);
1643
1790
  const kind = portKeys.length === 1 && portKeys[0] === "in" ? "single" : "multi";
1644
1791
  const items = inputsByPort.in ?? [];
@@ -1667,19 +1814,20 @@ var RunContinuationService = class {
1667
1814
  activationId: args.activationId,
1668
1815
  parent: state.parent,
1669
1816
  finishedAt,
1670
- inputsByPort: state.pending.inputsByPort,
1817
+ inputsByPort: pendingExecution.inputsByPort,
1671
1818
  error: args.error
1672
1819
  });
1673
1820
  const failedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
1674
1821
  state,
1675
1822
  engineCounters: state.engineCounters ?? { completedNodeActivations: 0 },
1676
1823
  status: "failed",
1677
- queue: (state.queue ?? []).map((q) => ({ ...q })),
1824
+ queue: (schedulingState?.queue ?? []).map((q) => ({ ...q })),
1678
1825
  outputsByNode: state.outputsByNode,
1679
1826
  nodeSnapshotsByNodeId: {
1680
1827
  ...state.nodeSnapshotsByNodeId ?? {},
1681
1828
  [args.nodeId]: failedSnapshot
1682
- }
1829
+ },
1830
+ finishedAtIso: finishedAt
1683
1831
  });
1684
1832
  await this.workflowExecutionRepository.save(failedState);
1685
1833
  await this.nodeEventPublisher.publish("nodeFailed", failedSnapshot);
@@ -1756,7 +1904,7 @@ var RunContinuationService = class {
1756
1904
  activationId: args.args.activationId,
1757
1905
  parent: args.state.parent,
1758
1906
  finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
1759
- inputsByPort: args.state.pending?.inputsByPort ?? NodeInputsByPortFactory.empty(),
1907
+ inputsByPort: args.pendingExecution.inputsByPort,
1760
1908
  outputs: triggerOutputs
1761
1909
  });
1762
1910
  const completedActivations = (args.state.engineCounters?.completedNodeActivations ?? 0) + 1;
@@ -1772,7 +1920,8 @@ var RunContinuationService = class {
1772
1920
  nodeSnapshotsByNodeId: {
1773
1921
  ...args.state.nodeSnapshotsByNodeId ?? {},
1774
1922
  [args.args.nodeId]: completedSnapshot
1775
- }
1923
+ },
1924
+ finishedAtIso: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
1776
1925
  });
1777
1926
  await this.workflowExecutionRepository.save(completedState);
1778
1927
  await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
@@ -1809,7 +1958,8 @@ var RunContinuationService = class {
1809
1958
  nodeSnapshotsByNodeId: {
1810
1959
  ...args.state.nodeSnapshotsByNodeId ?? {},
1811
1960
  [args.args.nodeId]: completedSnapshot
1812
- }
1961
+ },
1962
+ finishedAtIso: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
1813
1963
  });
1814
1964
  await this.workflowExecutionRepository.save(completedState);
1815
1965
  await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
@@ -1836,8 +1986,8 @@ var RunContinuationService = class {
1836
1986
  this.waiters.resolveRunCompletion(result$1);
1837
1987
  return result$1;
1838
1988
  }
1839
- const batchId = args.state.pending?.batchId ?? "batch_1";
1840
- const queue = (args.state.queue ?? []).map((entry) => ({
1989
+ const batchId = args.pendingExecution.batchId ?? "batch_1";
1990
+ const queue = (args.schedulingState?.queue ?? []).map((entry) => ({
1841
1991
  ...entry,
1842
1992
  batchId: entry.batchId ?? batchId
1843
1993
  }));
@@ -1859,7 +2009,8 @@ var RunContinuationService = class {
1859
2009
  nodeSnapshotsByNodeId: {
1860
2010
  ...args.state.nodeSnapshotsByNodeId ?? {},
1861
2011
  [args.args.nodeId]: completedSnapshot
1862
- }
2012
+ },
2013
+ finishedAtIso: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
1863
2014
  });
1864
2015
  await this.workflowExecutionRepository.save(completedState);
1865
2016
  await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
@@ -1897,7 +2048,8 @@ var RunContinuationService = class {
1897
2048
  nodeSnapshotsByNodeId: {
1898
2049
  ...args.state.nodeSnapshotsByNodeId ?? {},
1899
2050
  [args.args.nodeId]: completedSnapshot
1900
- }
2051
+ },
2052
+ finishedAtIso: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
1901
2053
  });
1902
2054
  await this.workflowExecutionRepository.save(failedState);
1903
2055
  await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
@@ -1992,7 +2144,7 @@ var RunContinuationService = class {
1992
2144
  workflowSnapshot: state.workflowSnapshot
1993
2145
  });
1994
2146
  }
1995
- buildNodeExecutionContextForPending(state, wf, def, nodeId) {
2147
+ buildNodeExecutionContextForPending(state, pendingExecution, wf, def, nodeId) {
1996
2148
  const data = this.runDataFactory.create(state.outputsByNode);
1997
2149
  const limits = this.resolveEngineLimitsFromState(state);
1998
2150
  const base = this.runExecutionContextFactory.create({
@@ -2006,7 +2158,7 @@ var RunContinuationService = class {
2006
2158
  data,
2007
2159
  nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, state.parent)
2008
2160
  });
2009
- const activationId = state.pending.activationId;
2161
+ const activationId = pendingExecution.activationId;
2010
2162
  return {
2011
2163
  ...base,
2012
2164
  data,
@@ -2020,6 +2172,14 @@ var RunContinuationService = class {
2020
2172
  getCredential: this.credentialResolverFactory.create(wf.id, nodeId, def.config)
2021
2173
  };
2022
2174
  }
2175
+ requirePendingExecution(runId, activationId, nodeId, state, schedulingState) {
2176
+ if (state.status !== "pending") throw new Error(`Run ${runId} is not pending`);
2177
+ const pendingExecution = schedulingState?.pending;
2178
+ if (!pendingExecution) throw new Error(`Run ${runId} is not pending`);
2179
+ if (pendingExecution.activationId !== activationId) throw new Error(`activationId mismatch for run ${runId}`);
2180
+ if (pendingExecution.nodeId !== nodeId) throw new Error(`nodeId mismatch for run ${runId}`);
2181
+ return pendingExecution;
2182
+ }
2023
2183
  resolveEngineLimitsFromState(state) {
2024
2184
  const fb = this.executionLimitsPolicy.createRootExecutionOptions();
2025
2185
  return {
@@ -2236,7 +2396,10 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
2236
2396
  isEdgeSatisfied(currentState, nodeId, input) {
2237
2397
  const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input);
2238
2398
  if (!incomingEdge) return false;
2239
- return this.hasOutputPort(currentState, incomingEdge.from.nodeId, incomingEdge.from.output);
2399
+ if (!this.hasOutputPort(currentState, incomingEdge.from.nodeId, incomingEdge.from.output)) return false;
2400
+ if (this.usesCollect(nodeId)) return true;
2401
+ if (this.resolveOutputItems(currentState, incomingEdge.from.nodeId, incomingEdge.from.output).length > 0) return true;
2402
+ return this.shouldContinueAfterEmptyOutputFromSource(incomingEdge.from.nodeId);
2240
2403
  }
2241
2404
  resolveInput(currentState, nodeId, input) {
2242
2405
  const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input);
@@ -2258,6 +2421,15 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
2258
2421
  resolveOutputItems(currentState, nodeId, output) {
2259
2422
  return currentState.outputsByNode[nodeId]?.[output] ?? [];
2260
2423
  }
2424
+ usesCollect(nodeId) {
2425
+ const expectedInputs = this.topology.expectedInputsByNode.get(nodeId) ?? [];
2426
+ return expectedInputs.length !== 1 || expectedInputs[0] !== "in";
2427
+ }
2428
+ shouldContinueAfterEmptyOutputFromSource(nodeId) {
2429
+ const definition = this.topology.defsById.get(nodeId);
2430
+ if (!definition) return false;
2431
+ return definition.config.continueWhenEmptyOutput === true;
2432
+ }
2261
2433
  getPinnedOutputs(currentState, nodeId) {
2262
2434
  return currentState.mutableState?.nodesById?.[nodeId]?.pinnedOutputsByPort;
2263
2435
  }
@@ -2664,7 +2836,7 @@ var DefaultDrivingScheduler = class {
2664
2836
  setContinuation(continuation) {
2665
2837
  this.inline.setContinuation(continuation);
2666
2838
  }
2667
- async enqueue(request) {
2839
+ async prepareDispatch(request) {
2668
2840
  const selection = await this.selectScheduler(request);
2669
2841
  if (selection.mode === "worker") {
2670
2842
  if (request.kind === "multi") throw new Error(`Multi-input node ${request.nodeId} cannot be scheduled to worker (insert local placement)`);
@@ -2679,15 +2851,17 @@ var DefaultDrivingScheduler = class {
2679
2851
  executionOptions: request.executionOptions
2680
2852
  };
2681
2853
  return {
2682
- receiptId: (await this.workerScheduler.enqueue(workerRequest)).receiptId,
2683
- mode: "worker",
2684
- queue: selection.queue
2854
+ receipt: {
2855
+ receiptId: request.activationId,
2856
+ mode: "worker",
2857
+ queue: selection.queue
2858
+ },
2859
+ dispatch: async () => {
2860
+ await this.workerScheduler.enqueue(workerRequest);
2861
+ }
2685
2862
  };
2686
2863
  }
2687
- return await this.enqueueInline(request);
2688
- }
2689
- notifyPendingStatePersisted(runId) {
2690
- this.inline.notifyPendingStatePersisted(runId);
2864
+ return await this.prepareInlineDispatch(request);
2691
2865
  }
2692
2866
  /**
2693
2867
  * Scheduler precedence is explicit:
@@ -2719,10 +2893,16 @@ var DefaultDrivingScheduler = class {
2719
2893
  hasNodeSchedulingPreference(request) {
2720
2894
  return request.ctx.config.execution?.hint !== void 0 || request.ctx.config.execution?.queue !== void 0;
2721
2895
  }
2722
- async enqueueInline(request) {
2896
+ async prepareInlineDispatch(request) {
2897
+ const prepared = await this.inline.prepareDispatch(request);
2723
2898
  return {
2724
- ...await this.inline.enqueue(request),
2725
- mode: "local"
2899
+ receipt: {
2900
+ ...prepared.receipt,
2901
+ mode: "local"
2902
+ },
2903
+ dispatch: async () => {
2904
+ await prepared.dispatch();
2905
+ }
2726
2906
  };
2727
2907
  }
2728
2908
  };
@@ -2746,29 +2926,29 @@ var InlineDrivingScheduler = class {
2746
2926
  drainingRuns = /* @__PURE__ */ new Set();
2747
2927
  queuesByRunId = /* @__PURE__ */ new Map();
2748
2928
  scheduledRuns = /* @__PURE__ */ new Set();
2749
- seq = 0;
2750
2929
  constructor(nodeExecutor) {
2751
2930
  this.nodeExecutor = nodeExecutor;
2752
2931
  }
2753
2932
  setContinuation(continuation) {
2754
2933
  this.continuation = continuation;
2755
2934
  }
2756
- async enqueue(request) {
2935
+ async prepareDispatch(request) {
2757
2936
  const receipt = {
2758
- receiptId: `inline_${++this.seq}`,
2937
+ receiptId: request.activationId,
2759
2938
  mode: "local"
2760
2939
  };
2761
- const q = this.queuesByRunId.get(request.runId) ?? [];
2762
- q.push({
2763
- request,
2764
- receipt
2765
- });
2766
- this.queuesByRunId.set(request.runId, q);
2767
- return receipt;
2768
- }
2769
- notifyPendingStatePersisted(runId) {
2770
- if ((this.queuesByRunId.get(runId)?.length ?? 0) === 0) return;
2771
- this.scheduleDrain(runId);
2940
+ return {
2941
+ receipt,
2942
+ dispatch: async () => {
2943
+ const queue = this.queuesByRunId.get(request.runId) ?? [];
2944
+ queue.push({
2945
+ request,
2946
+ receipt
2947
+ });
2948
+ this.queuesByRunId.set(request.runId, queue);
2949
+ this.scheduleDrain(request.runId);
2950
+ }
2951
+ };
2772
2952
  }
2773
2953
  async drainRun(runId) {
2774
2954
  if (this.drainingRuns.has(runId)) return;
@@ -3034,9 +3214,10 @@ var InMemoryRunDataFactory = class {
3034
3214
 
3035
3215
  //#endregion
3036
3216
  //#region src/contracts/runFinishedAtFactory.ts
3037
- /** Derives workflow end time from node snapshots for run listings. */
3217
+ /** Derives workflow end time from persisted run root or node snapshots for run listings. */
3038
3218
  var RunFinishedAtFactory = class {
3039
3219
  static resolveIso(state) {
3220
+ if (state.finishedAt && state.status !== "running" && state.status !== "pending") return state.finishedAt;
3040
3221
  if (state.status === "running" || state.status === "pending") return;
3041
3222
  let max;
3042
3223
  for (const snap of Object.values(state.nodeSnapshotsByNodeId)) if (snap?.finishedAt && (!max || snap.finishedAt > max)) max = snap.finishedAt;
@@ -3044,6 +3225,22 @@ var RunFinishedAtFactory = class {
3044
3225
  }
3045
3226
  };
3046
3227
 
3228
+ //#endregion
3229
+ //#region src/runtime/InMemoryLiveWorkflowRepository.ts
3230
+ var InMemoryLiveWorkflowRepository = class {
3231
+ workflowsById = /* @__PURE__ */ new Map();
3232
+ setWorkflows(workflows) {
3233
+ this.workflowsById.clear();
3234
+ for (const workflow of workflows) this.workflowsById.set(workflow.id, workflow);
3235
+ }
3236
+ list() {
3237
+ return [...this.workflowsById.values()];
3238
+ }
3239
+ get(workflowId) {
3240
+ return this.workflowsById.get(workflowId);
3241
+ }
3242
+ };
3243
+
3047
3244
  //#endregion
3048
3245
  //#region src/runtime/RunIntentService.ts
3049
3246
  var RunIntentService = class {
@@ -3052,13 +3249,14 @@ var RunIntentService = class {
3052
3249
  this.workflowRepository = workflowRepository;
3053
3250
  }
3054
3251
  async startWorkflow(args) {
3055
- if (args.startAt && !args.currentState && !args.stopCondition && !args.reset) return await this.engine.runWorkflow(args.workflow, args.startAt, args.items, args.parent, args.executionOptions, {
3252
+ const items = await this.resolveStartWorkflowItems(args);
3253
+ if (args.startAt && !args.currentState && !args.stopCondition && !args.reset) return await this.engine.runWorkflow(args.workflow, args.startAt, items, args.parent, args.executionOptions, {
3056
3254
  workflowSnapshot: args.workflowSnapshot,
3057
3255
  mutableState: args.mutableState
3058
3256
  });
3059
3257
  return await this.engine.runWorkflowFromState({
3060
3258
  workflow: args.workflow,
3061
- items: args.items,
3259
+ items,
3062
3260
  parent: args.parent,
3063
3261
  executionOptions: args.executionOptions,
3064
3262
  workflowSnapshot: args.workflowSnapshot,
@@ -3069,7 +3267,8 @@ var RunIntentService = class {
3069
3267
  });
3070
3268
  }
3071
3269
  async rerunFromNode(args) {
3072
- if (args.items) return await this.engine.runWorkflow(args.workflow, args.nodeId, args.items, args.parent, args.executionOptions, {
3270
+ const items = await this.resolveRerunItems(args);
3271
+ if (items) return await this.engine.runWorkflow(args.workflow, args.nodeId, items, args.parent, args.executionOptions, {
3073
3272
  workflowSnapshot: args.workflowSnapshot,
3074
3273
  mutableState: args.mutableState
3075
3274
  });
@@ -3084,6 +3283,44 @@ var RunIntentService = class {
3084
3283
  reset: { clearFromNodeId: args.nodeId }
3085
3284
  });
3086
3285
  }
3286
+ async resolveStartWorkflowItems(args) {
3287
+ if (this.hasNonEmptyItems(args.items)) return args.items;
3288
+ const triggerNodeId = this.resolveStartWorkflowTriggerNodeId(args);
3289
+ if (!triggerNodeId) return args.items;
3290
+ return await this.engine.createTriggerTestItems({
3291
+ workflow: args.workflow,
3292
+ nodeId: triggerNodeId
3293
+ }) ?? args.items;
3294
+ }
3295
+ async resolveRerunItems(args) {
3296
+ if (this.hasNonEmptyItems(args.items)) return args.items;
3297
+ const triggerNodeId = this.resolveRerunTriggerNodeId(args);
3298
+ if (!triggerNodeId) return args.items;
3299
+ return await this.engine.createTriggerTestItems({
3300
+ workflow: args.workflow,
3301
+ nodeId: triggerNodeId
3302
+ }) ?? args.items;
3303
+ }
3304
+ resolveStartWorkflowTriggerNodeId(args) {
3305
+ if (args.stopCondition?.kind === "nodeCompleted" && this.isTriggerNode(args.workflow, args.stopCondition.nodeId)) return args.stopCondition.nodeId;
3306
+ if (!args.synthesizeTriggerItems) return;
3307
+ if (args.startAt && this.isTriggerNode(args.workflow, args.startAt)) return args.startAt;
3308
+ return this.firstTriggerNodeId(args.workflow);
3309
+ }
3310
+ resolveRerunTriggerNodeId(args) {
3311
+ if (this.isTriggerNode(args.workflow, args.nodeId)) return args.nodeId;
3312
+ if (!args.synthesizeTriggerItems) return;
3313
+ return this.firstTriggerNodeId(args.workflow);
3314
+ }
3315
+ firstTriggerNodeId(workflow) {
3316
+ return workflow.nodes.find((node$1) => node$1.kind === "trigger")?.id;
3317
+ }
3318
+ isTriggerNode(workflow, nodeId) {
3319
+ return workflow.nodes.find((node$1) => node$1.id === nodeId)?.kind === "trigger";
3320
+ }
3321
+ hasNonEmptyItems(items) {
3322
+ return (items?.length ?? 0) > 0;
3323
+ }
3087
3324
  resolveWebhookTrigger(args) {
3088
3325
  return this.engine.resolveWebhookTrigger(args);
3089
3326
  }
@@ -3132,5 +3369,5 @@ var RunIntentService = class {
3132
3369
  };
3133
3370
 
3134
3371
  //#endregion
3135
- export { injectAll as $, InProcessRetryRunnerFactory as A, WorkflowExecutableNodeClassifier as B, PersistedWorkflowTokenRegistry as C, NodeExecutorFactory as D, MissingRuntimeExecutionMarker as E, ActivationEnqueueService as F, tool as G, chatModel as H, DefaultExecutionBinaryService as I, StackTraceCallSitePathResolver as J, InjectableRuntimeDecoratorComposer as K, UnavailableBinaryStorage as L, DefaultExecutionContextFactory as M, DefaultAsyncSleeper as N, NodeExecutor as O, CredentialResolverFactory as P, inject as Q, NodeEventPublisher as R, WorkflowSnapshotResolver as S, MissingRuntimeTriggerToken as T, getPersistedRuntimeTypeMetadata as U, ConnectionNodeIdFactory as V, node as W, container as X, PersistedRuntimeTypeNameResolver as Y, delay as Z, RunStateSemantics as _, ENGINE_EXECUTION_LIMITS_DEFAULTS as a, singleton as at, NodeInstanceFactoryFactory as b, InlineDrivingScheduler as c, ConfigDrivenOffloadPolicy as d, injectable as et, RunStartService as f, WorkflowRunExecutionContextFactory as g, WorkflowTopology as h, InMemoryBinaryStorage as i, registry as it, InProcessRetryRunner as j, NodeActivationRequestComposer as k, HintOnlyOffloadPolicy as l, RunContinuationService as m, RunFinishedAtFactory as n, instancePerContainerCachingFactory as nt, EngineExecutionLimitsPolicy as o, CoreTokens as ot, RunPolicySnapshotFactory as p, PersistedRuntimeTypeMetadataStore as q, InMemoryRunDataFactory as r, predicateAwareClassFactory as rt, LocalOnlyScheduler as s, RunIntentService as t, instanceCachingFactory as tt, DefaultDrivingScheduler as u, PersistedRunStateTerminalBuilder as v, MissingRuntimeFallbacks as w, NodeInstanceFactory as x, NodeRunStateWriterFactory as y, WorkflowExecutableNodeClassifierFactory as z };
3136
- //# sourceMappingURL=RunIntentService-BB4nqX3-.js.map
3372
+ export { delay as $, NodeExecutor as A, NodeEventPublisher as B, WorkflowSnapshotResolver as C, MissingRuntimeTriggerToken as D, MissingRuntimeFallbacks as E, DefaultAsyncSleeper as F, getPersistedRuntimeTypeMetadata as G, WorkflowExecutableNodeClassifier as H, CredentialResolverFactory as I, InjectableRuntimeDecoratorComposer as J, node as K, ActivationEnqueueService as L, InProcessRetryRunnerFactory as M, InProcessRetryRunner as N, MissingRuntimeExecutionMarker as O, DefaultExecutionContextFactory as P, container$1 as Q, DefaultExecutionBinaryService as R, NodeInstanceFactory as S, PersistedWorkflowTokenRegistry as T, ConnectionNodeIdFactory as U, WorkflowExecutableNodeClassifierFactory as V, chatModel as W, StackTraceCallSitePathResolver as X, PersistedRuntimeTypeMetadataStore as Y, PersistedRuntimeTypeNameResolver as Z, WorkflowRunExecutionContextFactory as _, InMemoryBinaryStorage as a, predicateAwareClassFactory as at, NodeRunStateWriterFactory as b, LocalOnlyScheduler as c, CoreTokens as ct, DefaultDrivingScheduler as d, inject as et, ConfigDrivenOffloadPolicy as f, WorkflowTopology as g, RunContinuationService as h, InMemoryRunDataFactory as i, instancePerContainerCachingFactory as it, NodeActivationRequestComposer as j, NodeExecutorFactory as k, InlineDrivingScheduler as l, RunPolicySnapshotFactory as m, InMemoryLiveWorkflowRepository as n, injectable as nt, ENGINE_EXECUTION_LIMITS_DEFAULTS as o, registry as ot, RunStartService as p, tool as q, RunFinishedAtFactory as r, instanceCachingFactory as rt, EngineExecutionLimitsPolicy as s, singleton as st, RunIntentService as t, injectAll as tt, HintOnlyOffloadPolicy as u, RunStateSemantics as v, WorkflowSnapshotCodec as w, NodeInstanceFactoryFactory as x, PersistedRunStateTerminalBuilder as y, UnavailableBinaryStorage as z };
3373
+ //# sourceMappingURL=RunIntentService-BFA48UpH.js.map