@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
@@ -79,6 +79,11 @@ export interface DefineNodeOptions<
79
79
  readonly key: TKey;
80
80
  readonly title: string;
81
81
  readonly description?: string;
82
+ /**
83
+ * Canvas icon for this node (same contract as `NodeConfigBase.icon` on runnable configs).
84
+ * The Next host resolves Lucide (`lucide:…`), built-in SVGs (`builtin:…`), Simple Icons (`si:…`), and image URLs (`https:`, `data:`, `/…`).
85
+ */
86
+ readonly icon?: string;
82
87
  readonly input?: Readonly<Record<keyof TConfig & string, unknown>>;
83
88
  readonly configSchema?: z.ZodType<TConfig>;
84
89
  readonly credentials?: TBindings;
@@ -196,6 +201,7 @@ export function defineNode<
196
201
  const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson> {
197
202
  readonly kind = "node" as const;
198
203
  readonly type: TypeToken<unknown> = DefinedNodeRuntime;
204
+ readonly icon = options.icon;
199
205
 
200
206
  constructor(
201
207
  public readonly name: string,
package/src/browser.ts CHANGED
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Browser / Next client–safe surface: no Node builtins or modules that pull `runStorage` (e.g. `node:stream/web`).
3
+ */
4
+ export { AgentConnectionNodeCollector } from "./ai/AgentConnectionNodeCollector";
5
+ export type {
6
+ AgentConnectionCredentialSource,
7
+ AgentConnectionNodeDescriptor,
8
+ AgentConnectionNodeRole,
9
+ } from "./ai/AgentConnectionNodeCollector";
10
+ export type { AgentNodeConfig } from "./ai/AiHost";
1
11
  export { ConnectionNodeIdFactory } from "./workflow/definition/ConnectionNodeIdFactory";
2
12
  export * from "./contracts/credentialTypes";
3
13
  export * from "./contracts/runtimeTypes";
@@ -5,4 +15,5 @@ export * from "./contracts/runFinishedAtFactory";
5
15
  export * from "./contracts/runTypes";
6
16
  export * from "./contracts/webhookTypes";
7
17
  export * from "./contracts/workflowTypes";
18
+ export type { RetryPolicySpec } from "./contracts/retryPolicySpec.types";
8
19
  export { ItemsInputNormalizer } from "./serialization/ItemsInputNormalizer";
@@ -0,0 +1,186 @@
1
+ import type { JsonValue, NodeActivationId, NodeId, RunId, WorkflowId } from "./workflowTypes";
2
+ import type {
3
+ NodeExecutionError,
4
+ NodeExecutionStatus,
5
+ PersistedMutableRunState,
6
+ PersistedWorkflowSnapshot,
7
+ RunStatus,
8
+ } from "./runTypes";
9
+
10
+ /** Canonical id for persisted execution rows (activation or connection invocation). */
11
+ export type ExecutionInstanceId = string;
12
+
13
+ /** Stable id for persisted work-queue rows. */
14
+ export type WorkItemId = string;
15
+
16
+ /** Batch grouping for planner activations. */
17
+ export type BatchId = string;
18
+
19
+ /** Optimistic concurrency on the run root. */
20
+ export type RunRevision = number;
21
+
22
+ export type PersistedRunWorkItemKind = "queue" | "pending";
23
+
24
+ export type WorkItemStatus = "queued" | "claimed" | "completed" | "failed" | "cancelled";
25
+
26
+ export type PersistedExecutionInstanceKind = "workflowNodeActivation" | "connectionInvocation";
27
+
28
+ export type ConnectionInvocationKind = "languageModel" | "tool" | "nestedAgent";
29
+
30
+ export type PayloadStorageKind = "inline" | "external" | "omitted";
31
+
32
+ /**
33
+ * Persisted run-work-queue row (queue entry or pending activation).
34
+ * Serialized to {@link RunWorkItem} in Prisma; engine still uses {@link PersistedRunState} queue + pending.
35
+ */
36
+ export interface PersistedRunWorkItemRecord {
37
+ readonly workItemId: WorkItemId;
38
+ readonly runId: RunId;
39
+ readonly workflowId: WorkflowId;
40
+ readonly kind: PersistedRunWorkItemKind;
41
+ readonly orderIndex: number;
42
+ readonly status: WorkItemStatus;
43
+ readonly queueName?: string;
44
+ readonly claimToken?: string;
45
+ readonly claimedBy?: string;
46
+ readonly claimedAt?: string;
47
+ readonly availableAt: string;
48
+ readonly enqueuedAt: string;
49
+ readonly completedAt?: string;
50
+ readonly failedAt?: string;
51
+ readonly sourceInstanceId?: ExecutionInstanceId;
52
+ readonly parentInstanceId?: ExecutionInstanceId;
53
+ readonly itemsIn: number;
54
+ /** Queue entry when kind is queue; pending activation when kind is pending. */
55
+ readonly payloadJson: string;
56
+ readonly error?: Readonly<NodeExecutionError>;
57
+ }
58
+
59
+ /**
60
+ * Payload policy fields for large-batch externalization (optional on first rollout).
61
+ */
62
+ export interface ExecutionPayloadPolicyFields {
63
+ readonly inputStorageKind: PayloadStorageKind;
64
+ readonly outputStorageKind: PayloadStorageKind;
65
+ readonly inputBytes?: number;
66
+ readonly outputBytes?: number;
67
+ readonly inputPreviewJson?: unknown;
68
+ readonly outputPreviewJson?: unknown;
69
+ readonly inputPayloadRef?: string;
70
+ readonly outputPayloadRef?: string;
71
+ readonly inputTruncated?: boolean;
72
+ readonly outputTruncated?: boolean;
73
+ }
74
+
75
+ /**
76
+ * One persisted execution row (workflow node activation or connection invocation).
77
+ */
78
+ export interface PersistedExecutionInstanceRecord {
79
+ readonly instanceId: ExecutionInstanceId;
80
+ readonly runId: RunId;
81
+ readonly workflowId: WorkflowId;
82
+ readonly slotNodeId: NodeId;
83
+ readonly workflowNodeId: NodeId;
84
+ readonly kind: PersistedExecutionInstanceKind;
85
+ readonly connectionKind?: ConnectionInvocationKind;
86
+ readonly activationId?: NodeActivationId;
87
+ readonly batchId: BatchId;
88
+ readonly runIndex: number;
89
+ readonly parentInstanceId?: ExecutionInstanceId;
90
+ readonly parentRunId?: RunId;
91
+ readonly workerClaimToken?: string;
92
+ readonly status: NodeExecutionStatus;
93
+ readonly queuedAt?: string;
94
+ readonly startedAt?: string;
95
+ readonly finishedAt?: string;
96
+ readonly updatedAt: string;
97
+ readonly itemCount: number;
98
+ readonly inputJson?: string;
99
+ readonly outputJson?: string;
100
+ readonly errorJson?: string;
101
+ readonly inputItemIndicesJson?: string;
102
+ readonly outputItemCount?: number;
103
+ readonly successfulItemCount?: number;
104
+ readonly failedItemCount?: number;
105
+ readonly truncatedInputPreviewJson?: string;
106
+ readonly truncatedOutputPreviewJson?: string;
107
+ readonly inputTruncated?: boolean;
108
+ readonly outputTruncated?: boolean;
109
+ readonly usedPinnedOutput?: boolean;
110
+ readonly payloadPolicy?: ExecutionPayloadPolicyFields;
111
+ }
112
+
113
+ /**
114
+ * Cached slot projection for planner/debugger/UI (not canonical history).
115
+ */
116
+ export interface RunSlotProjectionState {
117
+ readonly runId: RunId;
118
+ readonly workflowId: WorkflowId;
119
+ readonly revision: RunRevision;
120
+ readonly slotStatesByNodeId: Record<
121
+ NodeId,
122
+ Readonly<{
123
+ latestInstanceId?: ExecutionInstanceId;
124
+ latestTerminalInstanceId?: ExecutionInstanceId;
125
+ latestRunningInstanceId?: ExecutionInstanceId;
126
+ latestStatus?: NodeExecutionStatus;
127
+ invocationCount: number;
128
+ runCount: number;
129
+ }>
130
+ >;
131
+ }
132
+
133
+ export interface PersistedRunSlotProjectionRecord {
134
+ readonly runId: RunId;
135
+ readonly workflowId: WorkflowId;
136
+ readonly revision: RunRevision;
137
+ readonly updatedAt: string;
138
+ readonly slotStatesJson: string;
139
+ }
140
+
141
+ export interface WorkflowRunDetailDto {
142
+ readonly runId: RunId;
143
+ readonly workflowId: WorkflowId;
144
+ readonly startedAt: string;
145
+ readonly finishedAt?: string;
146
+ readonly status: RunStatus;
147
+ readonly workflowSnapshot?: PersistedWorkflowSnapshot;
148
+ readonly mutableState?: PersistedMutableRunState;
149
+ readonly slotStates: ReadonlyArray<SlotExecutionStateDto>;
150
+ readonly executionInstances: ReadonlyArray<ExecutionInstanceDto>;
151
+ }
152
+
153
+ export interface SlotExecutionStateDto {
154
+ readonly slotNodeId: NodeId;
155
+ readonly latestInstanceId?: ExecutionInstanceId;
156
+ readonly latestTerminalInstanceId?: ExecutionInstanceId;
157
+ readonly latestRunningInstanceId?: ExecutionInstanceId;
158
+ readonly status?: NodeExecutionStatus;
159
+ readonly invocationCount: number;
160
+ readonly runCount: number;
161
+ }
162
+
163
+ export interface ExecutionInstanceDto {
164
+ readonly instanceId: ExecutionInstanceId;
165
+ readonly slotNodeId: NodeId;
166
+ readonly workflowNodeId: NodeId;
167
+ readonly parentInstanceId?: ExecutionInstanceId;
168
+ readonly kind: PersistedExecutionInstanceKind;
169
+ readonly connectionKind?: ConnectionInvocationKind;
170
+ readonly runIndex: number;
171
+ readonly batchId: BatchId;
172
+ readonly activationId?: NodeActivationId;
173
+ readonly status: NodeExecutionStatus;
174
+ readonly queuedAt?: string;
175
+ readonly startedAt?: string;
176
+ readonly finishedAt?: string;
177
+ readonly itemCount: number;
178
+ readonly inputJson?: JsonValue;
179
+ readonly outputJson?: JsonValue;
180
+ readonly error?: Readonly<NodeExecutionError>;
181
+ }
182
+
183
+ export interface WorkflowDetailSelectionState {
184
+ readonly selectedSlotNodeId: NodeId | null;
185
+ readonly selectedInstanceId: ExecutionInstanceId | null;
186
+ }
@@ -1,4 +1,5 @@
1
1
  export * from "./credentialTypes";
2
+ export * from "./executionPersistenceContracts";
2
3
  export * from "./runtimeTypes";
3
4
  export * from "./runFinishedAtFactory";
4
5
  export * from "./runTypes";
@@ -1,10 +1,13 @@
1
1
  import type { PersistedRunState } from "./runTypes";
2
2
 
3
- type RunFinishedAtSource = Pick<PersistedRunState, "status" | "nodeSnapshotsByNodeId">;
3
+ type RunFinishedAtSource = Pick<PersistedRunState, "status" | "nodeSnapshotsByNodeId" | "finishedAt">;
4
4
 
5
- /** Derives workflow end time from node snapshots for run listings. */
5
+ /** Derives workflow end time from persisted run root or node snapshots for run listings. */
6
6
  export class RunFinishedAtFactory {
7
7
  static resolveIso(state: RunFinishedAtSource): string | undefined {
8
+ if (state.finishedAt && state.status !== "running" && state.status !== "pending") {
9
+ return state.finishedAt;
10
+ }
8
11
  if (state.status === "running" || state.status === "pending") {
9
12
  return undefined;
10
13
  }
@@ -228,10 +228,19 @@ export interface PendingNodeExecution {
228
228
  enqueuedAt: string;
229
229
  }
230
230
 
231
+ export interface PersistedRunSchedulingState {
232
+ pending?: PendingNodeExecution;
233
+ queue: RunQueueEntry[];
234
+ }
235
+
231
236
  export interface PersistedRunState {
232
237
  runId: RunId;
233
238
  workflowId: WorkflowId;
234
239
  startedAt: string;
240
+ /** Canonical terminal time for listings and retention when persisted on the run root. */
241
+ finishedAt?: string;
242
+ /** Optimistic concurrency / CAS on the run aggregate (repository may increment on save). */
243
+ revision?: number;
235
244
  parent?: ParentExecutionRef;
236
245
  executionOptions?: RunExecutionOptions;
237
246
  control?: PersistedRunControlState;
@@ -264,6 +273,7 @@ export interface WorkflowExecutionRepository {
264
273
  engineCounters?: EngineRunCounters;
265
274
  }): Promise<void>;
266
275
  load(runId: RunId): Promise<PersistedRunState | undefined>;
276
+ loadSchedulingState(runId: RunId): Promise<PersistedRunSchedulingState | undefined>;
267
277
  save(state: PersistedRunState): Promise<void>;
268
278
  deleteRun?(runId: RunId): Promise<void>;
269
279
  }
@@ -298,6 +298,11 @@ export interface NodeActivationReceipt {
298
298
  queue?: string;
299
299
  }
300
300
 
301
+ export interface PreparedNodeActivationDispatch {
302
+ readonly receipt: NodeActivationReceipt;
303
+ dispatch(): Promise<void>;
304
+ }
305
+
301
306
  export interface NodeActivationContinuation {
302
307
  markNodeRunning(args: {
303
308
  runId: RunId;
@@ -321,8 +326,7 @@ export interface NodeActivationContinuation {
321
326
 
322
327
  export interface NodeActivationScheduler {
323
328
  setContinuation?(continuation: NodeActivationContinuation): void;
324
- enqueue(request: NodeActivationRequest): Promise<NodeActivationReceipt>;
325
- notifyPendingStatePersisted?(runId: RunId): void;
329
+ prepareDispatch(request: NodeActivationRequest): Promise<PreparedNodeActivationDispatch>;
326
330
  cancel?(receiptId: string): Promise<void>;
327
331
  }
328
332
 
@@ -24,8 +24,9 @@ export interface Edge {
24
24
  export type NodeConnectionName = string;
25
25
 
26
26
  /**
27
- * Named connection from an executable parent node to child nodes that exist in {@link WorkflowDefinition.nodes}
28
- * but are not traversed by the main execution graph.
27
+ * Named connection from a parent node to child nodes that exist in {@link WorkflowDefinition.nodes}
28
+ * but are not traversed by the main execution graph. Parents are commonly executable nodes, but may
29
+ * also be connection-owned nodes for recursive agent attachments.
29
30
  */
30
31
  export interface WorkflowNodeConnection {
31
32
  readonly parentNodeId: NodeId;
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ PersistedRunSchedulingState,
2
3
  PersistedRunState,
3
4
  RunId,
4
5
  RunPruneCandidate,
@@ -34,6 +35,10 @@ export class EventPublishingWorkflowExecutionRepository
34
35
  return await this.inner.load(runId);
35
36
  }
36
37
 
38
+ async loadSchedulingState(runId: RunId): Promise<PersistedRunSchedulingState | undefined> {
39
+ return await this.inner.loadSchedulingState(runId);
40
+ }
41
+
37
42
  async save(state: PersistedRunState): Promise<void> {
38
43
  await this.inner.save(state);
39
44
  await this.eventBus.publish({
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  ConnectionInvocationRecord,
3
3
  EngineRunCounters,
4
+ PreparedNodeActivationDispatch,
4
5
  NodeActivationRequest,
5
6
  NodeActivationScheduler,
6
7
  NodeExecutionSnapshot,
@@ -25,8 +26,7 @@ import { NodeInputsByPortFactory } from "./NodeInputsByPortFactory";
25
26
 
26
27
  type PersistedRunStateRecord = NonNullable<Awaited<ReturnType<WorkflowExecutionRepository["load"]>>>;
27
28
 
28
- type ActivationSchedulerPort = Pick<NodeActivationScheduler, "enqueue"> &
29
- Pick<Partial<NodeActivationScheduler>, "notifyPendingStatePersisted">;
29
+ type ActivationSchedulerPort = Pick<NodeActivationScheduler, "prepareDispatch">;
30
30
 
31
31
  export type ActivationEnqueueRequest = {
32
32
  runId: RunId;
@@ -62,7 +62,7 @@ export class ActivationEnqueueService {
62
62
  async enqueueActivationWithSnapshot(
63
63
  args: ActivationEnqueueRequest,
64
64
  ): Promise<{ result: RunResult; queuedSnapshot: NodeExecutionSnapshot }> {
65
- const receipt = await this.activationScheduler.enqueue(args.request);
65
+ const preparedDispatch = await this.activationScheduler.prepareDispatch(args.request);
66
66
  const inputsByPort = NodeInputsByPortFactory.fromRequest(args.request);
67
67
  const itemsIn =
68
68
  args.request.kind === "multi"
@@ -76,8 +76,8 @@ export class ActivationEnqueueService {
76
76
  nodeId: args.request.nodeId,
77
77
  itemsIn,
78
78
  inputsByPort,
79
- receiptId: receipt.receiptId,
80
- queue: receipt.queue,
79
+ receiptId: preparedDispatch.receipt.receiptId,
80
+ queue: preparedDispatch.receipt.queue,
81
81
  batchId: args.request.batchId,
82
82
  enqueuedAt,
83
83
  };
@@ -112,14 +112,14 @@ export class ActivationEnqueueService {
112
112
  [args.request.nodeId]: queuedSnapshot,
113
113
  },
114
114
  });
115
- this.notifyPendingStatePersisted(args.runId);
115
+ await this.dispatchPreparedActivation(preparedDispatch);
116
116
  return {
117
117
  result: { runId: args.runId, workflowId: args.workflowId, startedAt: args.startedAt, status: "pending", pending },
118
118
  queuedSnapshot,
119
119
  };
120
120
  }
121
121
 
122
- private notifyPendingStatePersisted(runId: RunId): void {
123
- this.activationScheduler.notifyPendingStatePersisted?.(runId);
122
+ private async dispatchPreparedActivation(preparedDispatch: PreparedNodeActivationDispatch): Promise<void> {
123
+ await preparedDispatch.dispatch();
124
124
  }
125
125
  }
@@ -11,6 +11,8 @@ export class PersistedRunStateTerminalBuilder {
11
11
  queue: RunQueueEntry[];
12
12
  outputsByNode: PersistedRunState["outputsByNode"];
13
13
  nodeSnapshotsByNodeId: NonNullable<PersistedRunState["nodeSnapshotsByNodeId"]>;
14
+ /** When set, persisted on the run root for listings and retention pruning. */
15
+ finishedAtIso?: string;
14
16
  }): PersistedRunState {
15
17
  return {
16
18
  ...args.state,
@@ -20,6 +22,7 @@ export class PersistedRunStateTerminalBuilder {
20
22
  queue: args.queue,
21
23
  outputsByNode: args.outputsByNode,
22
24
  nodeSnapshotsByNodeId: args.nodeSnapshotsByNodeId,
25
+ finishedAt: args.finishedAtIso ?? args.state.finishedAt,
23
26
  };
24
27
  }
25
28
  }
package/src/index.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  export { SystemClock, type Clock } from "./contracts/Clock";
2
2
  export * from "./authoring";
3
3
  export * from "./ai/AiHost";
4
+ export { AgentConnectionNodeCollector } from "./ai/AgentConnectionNodeCollector";
5
+ export type {
6
+ AgentConnectionCredentialSource,
7
+ AgentConnectionNodeDescriptor,
8
+ AgentConnectionNodeRole,
9
+ } from "./ai/AgentConnectionNodeCollector";
4
10
  export * from "./workflow";
5
11
  export * from "./di";
6
12
  export * from "./events";
@@ -33,17 +33,21 @@ export class NodeExecutionRequestHandlerService implements NodeExecutionRequestH
33
33
  ) {}
34
34
 
35
35
  async handleNodeExecutionRequest(request: NodeExecutionRequest): Promise<void> {
36
- const state = await this.workflowExecutionRepository.load(request.runId);
36
+ const [state, schedulingState] = await Promise.all([
37
+ this.workflowExecutionRepository.load(request.runId),
38
+ this.workflowExecutionRepository.loadSchedulingState(request.runId),
39
+ ]);
37
40
  if (!state) {
38
41
  throw new Error(`Unknown runId: ${request.runId}`);
39
42
  }
40
43
  if (state.workflowId !== request.workflowId) {
41
44
  throw new Error(`workflowId mismatch for run ${request.runId}: ${state.workflowId} vs ${request.workflowId}`);
42
45
  }
43
- if (state.status !== "pending" || !state.pending) {
46
+ const pendingExecution = schedulingState?.pending;
47
+ if (state.status !== "pending" || !pendingExecution) {
44
48
  return;
45
49
  }
46
- if (state.pending.activationId !== request.activationId || state.pending.nodeId !== request.nodeId) {
50
+ if (pendingExecution.activationId !== request.activationId || pendingExecution.nodeId !== request.nodeId) {
47
51
  return;
48
52
  }
49
53
 
@@ -62,6 +66,7 @@ export class NodeExecutionRequestHandlerService implements NodeExecutionRequestH
62
66
  const resolvedParent = request.parent ?? state.parent;
63
67
  const data = this.runDataFactory.create(state.outputsByNode);
64
68
  const limits = this.resolveEngineLimitsFromState(state);
69
+ const persistedInput = pendingExecution.inputsByPort.in ?? request.input;
65
70
  const base = this.runExecutionContextFactory.create({
66
71
  runId: state.runId,
67
72
  workflowId: state.workflowId,
@@ -85,15 +90,15 @@ export class NodeExecutionRequestHandlerService implements NodeExecutionRequestH
85
90
  id: definition.id,
86
91
  config: definition.config,
87
92
  },
88
- batchId: state.pending.batchId ?? "batch_1",
89
- input: request.input,
93
+ batchId: pendingExecution.batchId ?? "batch_1",
94
+ input: persistedInput,
90
95
  });
91
96
 
92
97
  await this.continuation.markNodeRunning({
93
98
  runId: activationRequest.runId,
94
99
  activationId: activationRequest.activationId,
95
100
  nodeId: activationRequest.nodeId,
96
- inputsByPort: { in: activationRequest.input },
101
+ inputsByPort: pendingExecution.inputsByPort,
97
102
  });
98
103
 
99
104
  let outputs;