@caupulican/pi-adaptative 0.81.41 → 0.81.42

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 (237) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +5 -1
  3. package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +3 -3
  4. package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +8 -0
  5. package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +26 -3
  6. package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +3 -0
  7. package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +4 -1
  8. package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +2 -1
  9. package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +2 -0
  10. package/dist/core/agent-paths.d.ts +32 -0
  11. package/dist/core/agent-paths.d.ts.map +1 -1
  12. package/dist/core/agent-paths.js +65 -0
  13. package/dist/core/agent-paths.js.map +1 -1
  14. package/dist/core/agent-session-contracts.d.ts +19 -1
  15. package/dist/core/agent-session-contracts.d.ts.map +1 -1
  16. package/dist/core/agent-session-contracts.js.map +1 -1
  17. package/dist/core/agent-session.d.ts.map +1 -1
  18. package/dist/core/agent-session.js +4 -9
  19. package/dist/core/agent-session.js.map +1 -1
  20. package/dist/core/autonomy/contracts.d.ts +2 -0
  21. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  22. package/dist/core/autonomy/contracts.js.map +1 -1
  23. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  24. package/dist/core/autonomy/envelope-enforcement.js +4 -9
  25. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  26. package/dist/core/autonomy/lane-tool-surface.d.ts +3 -1
  27. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  28. package/dist/core/autonomy/lane-tool-surface.js +17 -4
  29. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  30. package/dist/core/autonomy/path-scope.d.ts +14 -0
  31. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  32. package/dist/core/autonomy/path-scope.js +37 -6
  33. package/dist/core/autonomy/path-scope.js.map +1 -1
  34. package/dist/core/background-lane-controller.d.ts +27 -1
  35. package/dist/core/background-lane-controller.d.ts.map +1 -1
  36. package/dist/core/background-lane-controller.js +32 -1
  37. package/dist/core/background-lane-controller.js.map +1 -1
  38. package/dist/core/delegation/managed-lane-controller.d.ts +11 -1
  39. package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -1
  40. package/dist/core/delegation/managed-lane-controller.js +84 -41
  41. package/dist/core/delegation/managed-lane-controller.js.map +1 -1
  42. package/dist/core/delegation/session-worker-claim.d.ts.map +1 -1
  43. package/dist/core/delegation/session-worker-claim.js +4 -3
  44. package/dist/core/delegation/session-worker-claim.js.map +1 -1
  45. package/dist/core/delegation/worker-action-journal.d.ts +73 -0
  46. package/dist/core/delegation/worker-action-journal.d.ts.map +1 -0
  47. package/dist/core/delegation/worker-action-journal.js +297 -0
  48. package/dist/core/delegation/worker-action-journal.js.map +1 -0
  49. package/dist/core/delegation/worker-actions.d.ts +36 -1
  50. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  51. package/dist/core/delegation/worker-actions.js +171 -46
  52. package/dist/core/delegation/worker-actions.js.map +1 -1
  53. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +75 -0
  54. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -0
  55. package/dist/core/delegation/worker-agent-control-coordinator.js +218 -0
  56. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -0
  57. package/dist/core/delegation/worker-agent-control.d.ts +66 -0
  58. package/dist/core/delegation/worker-agent-control.d.ts.map +1 -0
  59. package/dist/core/delegation/worker-agent-control.js +163 -0
  60. package/dist/core/delegation/worker-agent-control.js.map +1 -0
  61. package/dist/core/delegation/worker-attempt-executor.d.ts +80 -0
  62. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -0
  63. package/dist/core/delegation/worker-attempt-executor.js +343 -0
  64. package/dist/core/delegation/worker-attempt-executor.js.map +1 -0
  65. package/dist/core/delegation/worker-claim.d.ts +30 -0
  66. package/dist/core/delegation/worker-claim.d.ts.map +1 -1
  67. package/dist/core/delegation/worker-claim.js +205 -53
  68. package/dist/core/delegation/worker-claim.js.map +1 -1
  69. package/dist/core/delegation/worker-conversation-store.d.ts +116 -0
  70. package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -0
  71. package/dist/core/delegation/worker-conversation-store.js +442 -0
  72. package/dist/core/delegation/worker-conversation-store.js.map +1 -0
  73. package/dist/core/delegation/worker-delegation-controller.d.ts +21 -5
  74. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  75. package/dist/core/delegation/worker-delegation-controller.js +386 -251
  76. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  77. package/dist/core/delegation/worker-delegation-request.d.ts +14 -0
  78. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  79. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  80. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +4 -2
  81. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
  82. package/dist/core/delegation/worker-dispatch-scheduler.js +16 -6
  83. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
  84. package/dist/core/delegation/worker-execution-policy.d.ts +2 -1
  85. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  86. package/dist/core/delegation/worker-execution-policy.js +5 -7
  87. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  88. package/dist/core/delegation/worker-lifecycle.d.ts +44 -1
  89. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  90. package/dist/core/delegation/worker-lifecycle.js +120 -0
  91. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  92. package/dist/core/delegation/worker-process-owner.d.ts +18 -0
  93. package/dist/core/delegation/worker-process-owner.d.ts.map +1 -0
  94. package/dist/core/delegation/worker-process-owner.js +54 -0
  95. package/dist/core/delegation/worker-process-owner.js.map +1 -0
  96. package/dist/core/delegation/worker-profile-resolver.d.ts +5 -1
  97. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  98. package/dist/core/delegation/worker-profile-resolver.js +8 -0
  99. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  100. package/dist/core/delegation/worker-recovery-coordinator.d.ts +54 -0
  101. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -0
  102. package/dist/core/delegation/worker-recovery-coordinator.js +181 -0
  103. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -0
  104. package/dist/core/delegation/worker-resource-catalog.d.ts +32 -0
  105. package/dist/core/delegation/worker-resource-catalog.d.ts.map +1 -0
  106. package/dist/core/delegation/worker-resource-catalog.js +116 -0
  107. package/dist/core/delegation/worker-resource-catalog.js.map +1 -0
  108. package/dist/core/delegation/worker-resource-materializer.d.ts +47 -0
  109. package/dist/core/delegation/worker-resource-materializer.d.ts.map +1 -0
  110. package/dist/core/delegation/worker-resource-materializer.js +137 -0
  111. package/dist/core/delegation/worker-resource-materializer.js.map +1 -0
  112. package/dist/core/delegation/worker-runner.d.ts +3 -1
  113. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  114. package/dist/core/delegation/worker-runner.js +189 -54
  115. package/dist/core/delegation/worker-runner.js.map +1 -1
  116. package/dist/core/delegation/worker-terminal-finalizer.d.ts +23 -0
  117. package/dist/core/delegation/worker-terminal-finalizer.d.ts.map +1 -0
  118. package/dist/core/delegation/worker-terminal-finalizer.js +12 -0
  119. package/dist/core/delegation/worker-terminal-finalizer.js.map +1 -0
  120. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +38 -0
  121. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -0
  122. package/dist/core/delegation/worker-write-reservation-coordinator.js +107 -0
  123. package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -0
  124. package/dist/core/delegation/worker-write-reservation.d.ts +90 -0
  125. package/dist/core/delegation/worker-write-reservation.d.ts.map +1 -0
  126. package/dist/core/delegation/worker-write-reservation.js +348 -0
  127. package/dist/core/delegation/worker-write-reservation.js.map +1 -0
  128. package/dist/core/models/model-endpoint.d.ts +3 -0
  129. package/dist/core/models/model-endpoint.d.ts.map +1 -0
  130. package/dist/core/models/model-endpoint.js +11 -0
  131. package/dist/core/models/model-endpoint.js.map +1 -0
  132. package/dist/core/orchestration/agent-resume.d.ts +2 -0
  133. package/dist/core/orchestration/agent-resume.d.ts.map +1 -1
  134. package/dist/core/orchestration/agent-resume.js +7 -0
  135. package/dist/core/orchestration/agent-resume.js.map +1 -1
  136. package/dist/core/orchestration/attempt-usage.d.ts +28 -0
  137. package/dist/core/orchestration/attempt-usage.d.ts.map +1 -0
  138. package/dist/core/orchestration/attempt-usage.js +168 -0
  139. package/dist/core/orchestration/attempt-usage.js.map +1 -0
  140. package/dist/core/orchestration/capability-gateway.d.ts +19 -1
  141. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  142. package/dist/core/orchestration/capability-gateway.js +128 -28
  143. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  144. package/dist/core/orchestration/contracts.d.ts +35 -0
  145. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  146. package/dist/core/orchestration/contracts.js +17 -0
  147. package/dist/core/orchestration/contracts.js.map +1 -1
  148. package/dist/core/orchestration/delegation-ledger.d.ts +17 -2
  149. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  150. package/dist/core/orchestration/delegation-ledger.js +78 -3
  151. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  152. package/dist/core/orchestration/event-store.d.ts +1 -0
  153. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  154. package/dist/core/orchestration/event-store.js +78 -38
  155. package/dist/core/orchestration/event-store.js.map +1 -1
  156. package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
  157. package/dist/core/orchestration/profile-registry.js +81 -41
  158. package/dist/core/orchestration/profile-registry.js.map +1 -1
  159. package/dist/core/orchestration/profile-store.d.ts.map +1 -1
  160. package/dist/core/orchestration/profile-store.js +18 -4
  161. package/dist/core/orchestration/profile-store.js.map +1 -1
  162. package/dist/core/orchestration/risk-budget.d.ts +2 -0
  163. package/dist/core/orchestration/risk-budget.d.ts.map +1 -1
  164. package/dist/core/orchestration/risk-budget.js +26 -0
  165. package/dist/core/orchestration/risk-budget.js.map +1 -1
  166. package/dist/core/orchestration/task-runtime.d.ts +13 -1
  167. package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
  168. package/dist/core/orchestration/task-runtime.js +127 -16
  169. package/dist/core/orchestration/task-runtime.js.map +1 -1
  170. package/dist/core/orchestration/worker-execution-contract.d.ts +3 -1
  171. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
  172. package/dist/core/orchestration/worker-execution-contract.js +75 -23
  173. package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
  174. package/dist/core/orchestration/worker-result-adapter.d.ts +9 -3
  175. package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -1
  176. package/dist/core/orchestration/worker-result-adapter.js +167 -14
  177. package/dist/core/orchestration/worker-result-adapter.js.map +1 -1
  178. package/dist/core/reflection-controller.d.ts +1 -1
  179. package/dist/core/reflection-controller.d.ts.map +1 -1
  180. package/dist/core/reflection-controller.js +90 -54
  181. package/dist/core/reflection-controller.js.map +1 -1
  182. package/dist/core/research/evidence-bundle.d.ts +20 -0
  183. package/dist/core/research/evidence-bundle.d.ts.map +1 -1
  184. package/dist/core/research/evidence-bundle.js +173 -69
  185. package/dist/core/research/evidence-bundle.js.map +1 -1
  186. package/dist/core/runtime-builder.d.ts +7 -0
  187. package/dist/core/runtime-builder.d.ts.map +1 -1
  188. package/dist/core/runtime-builder.js +15 -1
  189. package/dist/core/runtime-builder.js.map +1 -1
  190. package/dist/core/session-artifact-bundle.d.ts +35 -0
  191. package/dist/core/session-artifact-bundle.d.ts.map +1 -0
  192. package/dist/core/session-artifact-bundle.js +61 -0
  193. package/dist/core/session-artifact-bundle.js.map +1 -0
  194. package/dist/core/tools/delegate-status.d.ts +6 -1
  195. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  196. package/dist/core/tools/delegate-status.js +100 -18
  197. package/dist/core/tools/delegate-status.js.map +1 -1
  198. package/dist/core/tools/delegate.d.ts +9 -1
  199. package/dist/core/tools/delegate.d.ts.map +1 -1
  200. package/dist/core/tools/delegate.js +323 -61
  201. package/dist/core/tools/delegate.js.map +1 -1
  202. package/dist/core/tools/run-process.d.ts +1 -1
  203. package/dist/core/tools/run-process.d.ts.map +1 -1
  204. package/dist/core/tools/run-process.js +69 -9
  205. package/dist/core/tools/run-process.js.map +1 -1
  206. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  207. package/dist/core/tools/shell-contract-router.js +12 -5
  208. package/dist/core/tools/shell-contract-router.js.map +1 -1
  209. package/dist/core/util/bounded-file.d.ts +15 -0
  210. package/dist/core/util/bounded-file.d.ts.map +1 -0
  211. package/dist/core/util/bounded-file.js +120 -0
  212. package/dist/core/util/bounded-file.js.map +1 -0
  213. package/dist/core/util/bounded-jsonl.d.ts.map +1 -1
  214. package/dist/core/util/bounded-jsonl.js +119 -9
  215. package/dist/core/util/bounded-jsonl.js.map +1 -1
  216. package/dist/core/util/bounded-value.d.ts +3 -0
  217. package/dist/core/util/bounded-value.d.ts.map +1 -0
  218. package/dist/core/util/bounded-value.js +12 -0
  219. package/dist/core/util/bounded-value.js.map +1 -0
  220. package/dist/core/util/value-guards.d.ts +2 -0
  221. package/dist/core/util/value-guards.d.ts.map +1 -1
  222. package/dist/core/util/value-guards.js +5 -0
  223. package/dist/core/util/value-guards.js.map +1 -1
  224. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  225. package/dist/modes/interactive/components/session-selector.js +27 -44
  226. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  227. package/docs/tool-repair.md +3 -0
  228. package/docs/windows.md +4 -2
  229. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  230. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  231. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  232. package/examples/extensions/sandbox/package-lock.json +2 -2
  233. package/examples/extensions/sandbox/package.json +1 -1
  234. package/examples/extensions/with-deps/package-lock.json +2 -2
  235. package/examples/extensions/with-deps/package.json +1 -1
  236. package/npm-shrinkwrap.json +12 -12
  237. package/package.json +4 -4
@@ -33,6 +33,7 @@ export class DelegationOrchestrationLedger {
33
33
  executionContract: input.executionContract,
34
34
  ...(input.goal ? { goal: input.goal } : {}),
35
35
  ...(input.verificationOfTaskId ? { verificationOfTaskId: input.verificationOfTaskId } : {}),
36
+ ...(input.taskContext ? { taskContext: input.taskContext } : {}),
36
37
  });
37
38
  }
38
39
  prepareManaged(input) {
@@ -59,6 +60,64 @@ export class DelegationOrchestrationLedger {
59
60
  },
60
61
  });
61
62
  }
63
+ /**
64
+ * Queue one new turn for an existing logical agent. The agent identity and its immutable
65
+ * execution contract are inherited from the last bound turn; the durable task and attempt ids
66
+ * are intentionally new so retries, leases, and terminal evidence never collide across turns.
67
+ */
68
+ prepareAgentTurn(input) {
69
+ const agentId = input.agentId.trim();
70
+ const instructions = input.instructions.trim();
71
+ if (!agentId)
72
+ throw new DurableTaskRuntimeError("Logical worker agent id is required.");
73
+ if (!instructions)
74
+ throw new DurableTaskRuntimeError("Worker follow-up instructions are required.");
75
+ const snapshot = this.runtime.getSnapshot();
76
+ const agent = snapshot.agents[agentId];
77
+ if (!agent)
78
+ throw new DurableTaskRuntimeError(`Unknown logical worker agent '${agentId}'.`);
79
+ if (agent.status !== "registered") {
80
+ throw new DurableTaskRuntimeError(`Logical worker agent '${agentId}' is not idle.`);
81
+ }
82
+ const prior = this.latestAgentAttempt(snapshot, agent);
83
+ const contract = prior?.dispatch.executionContract;
84
+ if (!prior || !contract) {
85
+ throw new DurableTaskRuntimeError(`Logical worker agent '${agentId}' has no immutable execution contract.`);
86
+ }
87
+ const priorTask = snapshot.tasks[prior.taskId]?.task;
88
+ if (!priorTask)
89
+ throw new DurableTaskRuntimeError(`Logical worker agent '${agentId}' has no prior durable task.`);
90
+ const sequence = Object.values(snapshot.attempts).filter((attempt) => attempt.agentId === agentId).length + 1;
91
+ const taskId = `${agentId}:turn:${sequence}`;
92
+ if (snapshot.tasks[taskId]) {
93
+ throw new DurableTaskRuntimeError(`Logical worker agent '${agentId}' already has turn ${sequence}.`);
94
+ }
95
+ return this.prepareNormalized({
96
+ laneId: taskId,
97
+ instructions,
98
+ profileId: contract.worker.profile.profileId,
99
+ role: agent.role,
100
+ requiredCapabilities: contract.worker.authority.capabilities,
101
+ riskBudget: contract.worker.profile.budget,
102
+ taskContext: {
103
+ requirementIds: prior.dispatch.requirementIds ?? [],
104
+ dependsOnTaskIds: priorTask.dependsOn,
105
+ acceptanceCriterionIds: priorTask.acceptanceCriterionIds,
106
+ resourcePointerIds: prior.dispatch.resourcePointerIds,
107
+ },
108
+ executionContract: contract,
109
+ dispatchMetadata: {
110
+ logicalLaneId: agentId,
111
+ dispatchSequence: sequence,
112
+ },
113
+ });
114
+ }
115
+ latestAgentAttempt(snapshot, agent) {
116
+ return Object.values(snapshot.attempts)
117
+ .filter((attempt) => attempt.agentId === agent.agentId)
118
+ .sort((left, right) => left.createdAt.localeCompare(right.createdAt))
119
+ .at(-1);
120
+ }
62
121
  prepareNormalized(input) {
63
122
  let snapshot = this.runtime.getSnapshot();
64
123
  const existingVerificationSubject = input.verificationOfTaskId
@@ -93,7 +152,9 @@ export class DelegationOrchestrationLedger {
93
152
  title: deriveWorkerTaskLabel(input.instructions, `Delegated ${input.role} work`),
94
153
  description: input.instructions,
95
154
  role: input.role,
155
+ dependsOn: input.taskContext?.dependsOnTaskIds ?? [],
96
156
  requiredCapabilities: input.requiredCapabilities,
157
+ acceptanceCriterionIds: input.taskContext?.acceptanceCriterionIds ?? [],
97
158
  riskBudget: input.riskBudget,
98
159
  ...(input.verificationOfTaskId
99
160
  ? {
@@ -117,7 +178,8 @@ export class DelegationOrchestrationLedger {
117
178
  taskId: input.laneId,
118
179
  profileId: input.profileId,
119
180
  instructions: input.instructions,
120
- resourcePointerIds: [],
181
+ resourcePointerIds: input.taskContext?.resourcePointerIds ?? [],
182
+ requirementIds: input.taskContext?.requirementIds ?? [],
121
183
  ...(input.executionContract ? { executionContract: input.executionContract } : {}),
122
184
  ...input.dispatchMetadata,
123
185
  });
@@ -152,7 +214,7 @@ export class DelegationOrchestrationLedger {
152
214
  break;
153
215
  }
154
216
  }
155
- start(attemptId, leaseTtlMs, ownerId = `in-process:${this.sessionId}`) {
217
+ start(attemptId, leaseTtlMs, ownerId = `in-process:${this.sessionId}`, agentId) {
156
218
  const snapshot = this.runtime.getSnapshot();
157
219
  const attempt = snapshot.attempts[attemptId];
158
220
  if (!attempt)
@@ -160,7 +222,7 @@ export class DelegationOrchestrationLedger {
160
222
  const task = snapshot.tasks[attempt.taskId];
161
223
  if (!task)
162
224
  throw new DurableTaskRuntimeError(`Unknown task '${attempt.taskId}'.`);
163
- const lease = this.runtime.leaseAttempt(attemptId, ownerId, leaseTtlMs);
225
+ const lease = this.runtime.leaseAttempt(attemptId, ownerId, leaseTtlMs, agentId);
164
226
  this.runtime.startAttempt(attemptId, lease.leaseId, lease.fencingToken);
165
227
  return {
166
228
  objectiveId: task.task.objectiveId,
@@ -174,6 +236,19 @@ export class DelegationOrchestrationLedger {
174
236
  cancel(attemptId, reasonCode) {
175
237
  this.runtime.cancelAttempt(attemptId, reasonCode);
176
238
  }
239
+ /** Latest fenced cumulative usage, if this attempt has crossed a durable checkpoint boundary. */
240
+ getAttemptUsage(attemptId) {
241
+ const snapshot = this.runtime.getSnapshot();
242
+ const attempt = snapshot.attempts[attemptId];
243
+ if (!attempt)
244
+ throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);
245
+ for (const checkpointId of [...attempt.checkpointIds].reverse()) {
246
+ const usage = snapshot.checkpoints[checkpointId]?.usage;
247
+ if (usage)
248
+ return structuredClone(usage);
249
+ }
250
+ return undefined;
251
+ }
177
252
  /** Fence interrupted isolated completions and queue one replacement attempt per task. */
178
253
  recoverQueuedDispatches() {
179
254
  this.runtime.recoverInterruptedUnboundAttempts((attempt) => attempt.dispatch.executionKind !== "managed-process");
@@ -1 +1 @@
1
- {"version":3,"file":"delegation-ledger.js","sourceRoot":"","sources":["../../../src/core/orchestration/delegation-ledger.ts"],"names":[],"mappings":"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","sourcesContent":["import { deriveWorkerTaskLabel } from \"../delegation/worker-task-label.ts\";\nimport { hasGoalAcceptanceOverride } from \"../goals/goal-acceptance.ts\";\nimport type { GoalState } from \"../goals/goal-state.ts\";\nimport type {\n\tHarnessCapability,\n\tOrchestrationDispatchRequest,\n\tRiskBudget,\n\tWorkerExecutionContract,\n\tWorkerRole,\n} from \"./contracts.ts\";\nimport { OrchestrationEventStore } from \"./event-store.ts\";\nimport { type AttemptRuntimeState, DurableTaskRuntime, DurableTaskRuntimeError } from \"./task-runtime.ts\";\nimport { goalObjectiveId, projectGoalAcceptanceEvidence, projectGoalObjective } from \"./work-state-projection.ts\";\n\nexport interface DelegationLedgerOptions {\n\tagentDir: string;\n\tsessionId: string;\n\tnow?: () => number;\n}\n\nexport interface PrepareDelegationInput {\n\tlaneId: string;\n\tinstructions: string;\n\texecutionContract: WorkerExecutionContract;\n\trequiredCapabilities: readonly HarnessCapability[];\n\tgoal?: GoalState;\n\tverificationOfTaskId?: string;\n}\n\nexport interface PrepareManagedDelegationInput {\n\tlaneId: string;\n\tdispatchSequence: number;\n\tinstructions: string;\n\tprofileId: string;\n\tprovider: string;\n\tauthorizationId: string;\n\trole: WorkerRole;\n\triskBudget: RiskBudget;\n\tgoal?: GoalState;\n\tgoalId?: string;\n\tworktreeLaneKey?: string;\n}\n\nexport interface StartedDelegationAttempt {\n\tobjectiveId: string;\n\ttaskId: string;\n\tattemptId: string;\n\tleaseId: string;\n\tfencingToken: number;\n\texpiresAt: string;\n}\n\nfunction activeAttempt(attempt: AttemptRuntimeState): boolean {\n\treturn attempt.status === \"queued\" || attempt.status === \"leased\" || attempt.status === \"running\";\n}\n\n/**\n * Durable adapter for in-process delegated completions. It records the dispatch before execution,\n * fences interrupted completions on restart, and returns queued work for event-driven re-dispatch.\n * It deliberately does not pretend an isolated completion has a resumable model transcript.\n */\nexport class DelegationOrchestrationLedger {\n\treadonly runtime: DurableTaskRuntime;\n\tprivate readonly sessionId: string;\n\n\tconstructor(options: DelegationLedgerOptions) {\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.runtime = new DurableTaskRuntime({\n\t\t\tstore: new OrchestrationEventStore({ agentDir: options.agentDir, sessionId: options.sessionId }),\n\t\t\tnow: options.now,\n\t\t});\n\t}\n\n\tprepare(input: PrepareDelegationInput): AttemptRuntimeState {\n\t\tconst profile = input.executionContract.worker.profile;\n\t\treturn this.prepareNormalized({\n\t\t\tlaneId: input.laneId,\n\t\t\tinstructions: input.instructions,\n\t\t\tprofileId: profile.profileId,\n\t\t\trole: profile.role,\n\t\t\trequiredCapabilities: input.requiredCapabilities,\n\t\t\triskBudget: profile.budget,\n\t\t\texecutionContract: input.executionContract,\n\t\t\t...(input.goal ? { goal: input.goal } : {}),\n\t\t\t...(input.verificationOfTaskId ? { verificationOfTaskId: input.verificationOfTaskId } : {}),\n\t\t});\n\t}\n\n\tprepareManaged(input: PrepareManagedDelegationInput): AttemptRuntimeState {\n\t\tif (!Number.isSafeInteger(input.dispatchSequence) || input.dispatchSequence < 1) {\n\t\t\tthrow new DurableTaskRuntimeError(\"Managed dispatch sequence must be a positive integer.\");\n\t\t}\n\t\tconst taskId = `${input.laneId}:turn:${input.dispatchSequence}`;\n\t\treturn this.prepareNormalized({\n\t\t\tlaneId: taskId,\n\t\t\tinstructions: input.instructions,\n\t\t\tprofileId: input.profileId,\n\t\t\trole: input.role,\n\t\t\trequiredCapabilities: [],\n\t\t\triskBudget: input.riskBudget,\n\t\t\t...(input.goal ? { goal: input.goal } : {}),\n\t\t\t...(input.goalId ? { goalId: input.goalId } : {}),\n\t\t\tdispatchMetadata: {\n\t\t\t\texecutionKind: \"managed-process\",\n\t\t\t\tlogicalLaneId: input.laneId,\n\t\t\t\tdispatchSequence: input.dispatchSequence,\n\t\t\t\tprovider: input.provider,\n\t\t\t\tauthorizationId: input.authorizationId,\n\t\t\t\t...(input.worktreeLaneKey ? { worktreeLaneKey: input.worktreeLaneKey } : {}),\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate prepareNormalized(input: {\n\t\tlaneId: string;\n\t\tinstructions: string;\n\t\tprofileId: string;\n\t\trole: WorkerRole;\n\t\trequiredCapabilities: readonly HarnessCapability[];\n\t\triskBudget: RiskBudget;\n\t\tgoal?: GoalState;\n\t\tgoalId?: string;\n\t\tverificationOfTaskId?: string;\n\t\texecutionContract?: WorkerExecutionContract;\n\t\tdispatchMetadata?: Pick<\n\t\t\tOrchestrationDispatchRequest,\n\t\t\t\"executionKind\" | \"logicalLaneId\" | \"dispatchSequence\" | \"provider\" | \"authorizationId\" | \"worktreeLaneKey\"\n\t\t>;\n\t}): AttemptRuntimeState {\n\t\tlet snapshot = this.runtime.getSnapshot();\n\t\tconst existingVerificationSubject = input.verificationOfTaskId\n\t\t\t? snapshot.tasks[input.verificationOfTaskId]\n\t\t\t: undefined;\n\t\tif (input.verificationOfTaskId && !existingVerificationSubject) {\n\t\t\tthrow new DurableTaskRuntimeError(`Unknown verification subject '${input.verificationOfTaskId}'.`);\n\t\t}\n\t\tconst projectedGoal = input.goal ? projectGoalObjective(input.goal) : undefined;\n\t\tconst objectiveId =\n\t\t\texistingVerificationSubject?.task.objectiveId ??\n\t\t\tprojectedGoal?.objectiveId ??\n\t\t\t(input.goalId ? goalObjectiveId(input.goalId) : `session:${this.sessionId}`);\n\t\tif (input.goal && objectiveId === projectedGoal?.objectiveId) {\n\t\t\tthis.synchronizeGoalState(input.goal);\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t} else if (!snapshot.objectives[objectiveId]) {\n\t\t\tthis.runtime.createObjective({\n\t\t\t\tobjectiveId,\n\t\t\t\ttitle: input.goalId ? `Goal ${input.goalId}` : `Session ${this.sessionId}`,\n\t\t\t\tdescription: input.goalId ? \"Externally managed goal work\" : \"Session-scoped delegated work\",\n\t\t\t});\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t}\n\n\t\tif (!snapshot.tasks[input.laneId]) {\n\t\t\tconst verificationSubject = input.verificationOfTaskId\n\t\t\t\t? snapshot.tasks[input.verificationOfTaskId]\n\t\t\t\t: undefined;\n\t\t\tthis.runtime.createTask({\n\t\t\t\ttaskId: input.laneId,\n\t\t\t\tobjectiveId,\n\t\t\t\ttitle: deriveWorkerTaskLabel(input.instructions, `Delegated ${input.role} work`),\n\t\t\t\tdescription: input.instructions,\n\t\t\t\trole: input.role,\n\t\t\t\trequiredCapabilities: input.requiredCapabilities,\n\t\t\t\triskBudget: input.riskBudget,\n\t\t\t\t...(input.verificationOfTaskId\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tverificationOfTaskId: input.verificationOfTaskId,\n\t\t\t\t\t\t\tacceptanceCriterionIds: verificationSubject?.task.acceptanceCriterionIds ?? [],\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t}\n\n\t\tconst task = snapshot.tasks[input.laneId];\n\t\tif (!task) throw new DurableTaskRuntimeError(`Failed to create task '${input.laneId}'.`);\n\t\tconst existing = [...task.attemptIds]\n\t\t\t.reverse()\n\t\t\t.map((attemptId) => snapshot.attempts[attemptId])\n\t\t\t.find((attempt): attempt is AttemptRuntimeState => attempt !== undefined && activeAttempt(attempt));\n\t\tif (existing) return existing;\n\n\t\treturn this.runtime.queueAttempt(input.laneId, {\n\t\t\ttaskId: input.laneId,\n\t\t\tprofileId: input.profileId,\n\t\t\tinstructions: input.instructions,\n\t\t\tresourcePointerIds: [],\n\t\t\t...(input.executionContract ? { executionContract: input.executionContract } : {}),\n\t\t\t...input.dispatchMetadata,\n\t\t});\n\t}\n\n\tsynchronizeGoalState(goal: GoalState): void {\n\t\tconst objective = projectGoalObjective(goal);\n\t\tthis.runtime.ensureObjective(objective);\n\t\tfor (const evidence of projectGoalAcceptanceEvidence(goal)) {\n\t\t\tthis.runtime.recordObjectiveEvidence(objective.objectiveId, evidence);\n\t\t}\n\t\tconst status = this.runtime.getSnapshot().objectives[goalObjectiveId(goal.goalId)]?.objective.status;\n\t\tswitch (goal.status) {\n\t\t\tcase \"active\":\n\t\t\t\tif (status === \"paused\") this.runtime.resumeObjective(objective.objectiveId);\n\t\t\t\tbreak;\n\t\t\tcase \"paused\":\n\t\t\tcase \"blocked\":\n\t\t\tcase \"usage_limited\":\n\t\t\tcase \"budget_limited\":\n\t\t\t\tif (status === \"active\") this.runtime.pauseObjective(objective.objectiveId);\n\t\t\t\tbreak;\n\t\t\tcase \"cancelled\":\n\t\t\t\tif (status !== \"cancelled\") this.runtime.cancelObjective(objective.objectiveId);\n\t\t\t\tbreak;\n\t\t\tcase \"completed\":\n\t\t\t\tif (status !== \"completed\") {\n\t\t\t\t\tthis.runtime.completeObjectiveFromOwner(objective.objectiveId, hasGoalAcceptanceOverride(goal));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tstart(attemptId: string, leaseTtlMs: number, ownerId = `in-process:${this.sessionId}`): StartedDelegationAttempt {\n\t\tconst snapshot = this.runtime.getSnapshot();\n\t\tconst attempt = snapshot.attempts[attemptId];\n\t\tif (!attempt) throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);\n\t\tconst task = snapshot.tasks[attempt.taskId];\n\t\tif (!task) throw new DurableTaskRuntimeError(`Unknown task '${attempt.taskId}'.`);\n\t\tconst lease = this.runtime.leaseAttempt(attemptId, ownerId, leaseTtlMs);\n\t\tthis.runtime.startAttempt(attemptId, lease.leaseId, lease.fencingToken);\n\t\treturn {\n\t\t\tobjectiveId: task.task.objectiveId,\n\t\t\ttaskId: task.task.taskId,\n\t\t\tattemptId,\n\t\t\tleaseId: lease.leaseId,\n\t\t\tfencingToken: lease.fencingToken,\n\t\t\texpiresAt: lease.expiresAt,\n\t\t};\n\t}\n\n\tcancel(attemptId: string, reasonCode: string): void {\n\t\tthis.runtime.cancelAttempt(attemptId, reasonCode);\n\t}\n\n\t/** Fence interrupted isolated completions and queue one replacement attempt per task. */\n\trecoverQueuedDispatches(): AttemptRuntimeState[] {\n\t\tthis.runtime.recoverInterruptedUnboundAttempts((attempt) => attempt.dispatch.executionKind !== \"managed-process\");\n\t\tlet snapshot = this.runtime.getSnapshot();\n\t\tfor (const task of Object.values(snapshot.tasks)) {\n\t\t\tconst attempts = task.attemptIds.map((attemptId) => snapshot.attempts[attemptId]).filter(Boolean);\n\t\t\tif (attempts.some((attempt) => attempt && activeAttempt(attempt))) continue;\n\t\t\tconst interrupted = [...attempts].reverse().find((attempt) => attempt?.status === \"expired\");\n\t\t\tif (!interrupted || task.task.status !== \"ready\") continue;\n\t\t\tconst maxAttempts = task.task.riskBudget.maxAttempts;\n\t\t\tif (maxAttempts !== undefined && task.attemptIds.length >= maxAttempts) {\n\t\t\t\tthis.runtime.failTask(task.task.taskId, \"attempt_budget_exhausted\");\n\t\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.runtime.queueAttempt(task.task.taskId, interrupted.dispatch);\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t}\n\t\treturn Object.values(snapshot.attempts).filter(\n\t\t\t(attempt) => attempt.status === \"queued\" && attempt.dispatch.executionKind !== \"managed-process\",\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"delegation-ledger.js","sourceRoot":"","sources":["../../../src/core/orchestration/delegation-ledger.ts"],"names":[],"mappings":"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","sourcesContent":["import type { WorkerDelegationTaskContext } from \"../delegation/worker-delegation-request.ts\";\nimport { deriveWorkerTaskLabel } from \"../delegation/worker-task-label.ts\";\nimport { hasGoalAcceptanceOverride } from \"../goals/goal-acceptance.ts\";\nimport type { GoalState } from \"../goals/goal-state.ts\";\nimport type {\n\tAgentBindingContract,\n\tAttemptUsageSnapshot,\n\tHarnessCapability,\n\tOrchestrationDispatchRequest,\n\tRiskBudget,\n\tWorkerExecutionContract,\n\tWorkerRole,\n} from \"./contracts.ts\";\nimport { OrchestrationEventStore } from \"./event-store.ts\";\nimport { type AttemptRuntimeState, DurableTaskRuntime, DurableTaskRuntimeError } from \"./task-runtime.ts\";\nimport { goalObjectiveId, projectGoalAcceptanceEvidence, projectGoalObjective } from \"./work-state-projection.ts\";\n\nexport interface DelegationLedgerOptions {\n\tagentDir: string;\n\tsessionId: string;\n\tnow?: () => number;\n}\n\nexport interface PrepareDelegationInput {\n\tlaneId: string;\n\tinstructions: string;\n\texecutionContract: WorkerExecutionContract;\n\trequiredCapabilities: readonly HarnessCapability[];\n\tgoal?: GoalState;\n\tverificationOfTaskId?: string;\n\ttaskContext?: WorkerDelegationTaskContext;\n}\n\nexport interface PrepareManagedDelegationInput {\n\tlaneId: string;\n\tdispatchSequence: number;\n\tinstructions: string;\n\tprofileId: string;\n\tprovider: string;\n\tauthorizationId: string;\n\trole: WorkerRole;\n\triskBudget: RiskBudget;\n\tgoal?: GoalState;\n\tgoalId?: string;\n\tworktreeLaneKey?: string;\n}\n\nexport interface StartedDelegationAttempt {\n\tobjectiveId: string;\n\ttaskId: string;\n\tattemptId: string;\n\tleaseId: string;\n\tfencingToken: number;\n\texpiresAt: string;\n}\n\nexport interface PrepareAgentTurnInput {\n\tagentId: string;\n\tinstructions: string;\n}\n\nfunction activeAttempt(attempt: AttemptRuntimeState): boolean {\n\treturn attempt.status === \"queued\" || attempt.status === \"leased\" || attempt.status === \"running\";\n}\n\n/**\n * Durable adapter for in-process delegated completions. It records the dispatch before execution,\n * fences interrupted completions on restart, and returns queued work for event-driven re-dispatch.\n * It deliberately does not pretend an isolated completion has a resumable model transcript.\n */\nexport class DelegationOrchestrationLedger {\n\treadonly runtime: DurableTaskRuntime;\n\tprivate readonly sessionId: string;\n\n\tconstructor(options: DelegationLedgerOptions) {\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.runtime = new DurableTaskRuntime({\n\t\t\tstore: new OrchestrationEventStore({ agentDir: options.agentDir, sessionId: options.sessionId }),\n\t\t\tnow: options.now,\n\t\t});\n\t}\n\n\tprepare(input: PrepareDelegationInput): AttemptRuntimeState {\n\t\tconst profile = input.executionContract.worker.profile;\n\t\treturn this.prepareNormalized({\n\t\t\tlaneId: input.laneId,\n\t\t\tinstructions: input.instructions,\n\t\t\tprofileId: profile.profileId,\n\t\t\trole: profile.role,\n\t\t\trequiredCapabilities: input.requiredCapabilities,\n\t\t\triskBudget: profile.budget,\n\t\t\texecutionContract: input.executionContract,\n\t\t\t...(input.goal ? { goal: input.goal } : {}),\n\t\t\t...(input.verificationOfTaskId ? { verificationOfTaskId: input.verificationOfTaskId } : {}),\n\t\t\t...(input.taskContext ? { taskContext: input.taskContext } : {}),\n\t\t});\n\t}\n\n\tprepareManaged(input: PrepareManagedDelegationInput): AttemptRuntimeState {\n\t\tif (!Number.isSafeInteger(input.dispatchSequence) || input.dispatchSequence < 1) {\n\t\t\tthrow new DurableTaskRuntimeError(\"Managed dispatch sequence must be a positive integer.\");\n\t\t}\n\t\tconst taskId = `${input.laneId}:turn:${input.dispatchSequence}`;\n\t\treturn this.prepareNormalized({\n\t\t\tlaneId: taskId,\n\t\t\tinstructions: input.instructions,\n\t\t\tprofileId: input.profileId,\n\t\t\trole: input.role,\n\t\t\trequiredCapabilities: [],\n\t\t\triskBudget: input.riskBudget,\n\t\t\t...(input.goal ? { goal: input.goal } : {}),\n\t\t\t...(input.goalId ? { goalId: input.goalId } : {}),\n\t\t\tdispatchMetadata: {\n\t\t\t\texecutionKind: \"managed-process\",\n\t\t\t\tlogicalLaneId: input.laneId,\n\t\t\t\tdispatchSequence: input.dispatchSequence,\n\t\t\t\tprovider: input.provider,\n\t\t\t\tauthorizationId: input.authorizationId,\n\t\t\t\t...(input.worktreeLaneKey ? { worktreeLaneKey: input.worktreeLaneKey } : {}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Queue one new turn for an existing logical agent. The agent identity and its immutable\n\t * execution contract are inherited from the last bound turn; the durable task and attempt ids\n\t * are intentionally new so retries, leases, and terminal evidence never collide across turns.\n\t */\n\tprepareAgentTurn(input: PrepareAgentTurnInput): AttemptRuntimeState {\n\t\tconst agentId = input.agentId.trim();\n\t\tconst instructions = input.instructions.trim();\n\t\tif (!agentId) throw new DurableTaskRuntimeError(\"Logical worker agent id is required.\");\n\t\tif (!instructions) throw new DurableTaskRuntimeError(\"Worker follow-up instructions are required.\");\n\t\tconst snapshot = this.runtime.getSnapshot();\n\t\tconst agent = snapshot.agents[agentId];\n\t\tif (!agent) throw new DurableTaskRuntimeError(`Unknown logical worker agent '${agentId}'.`);\n\t\tif (agent.status !== \"registered\") {\n\t\t\tthrow new DurableTaskRuntimeError(`Logical worker agent '${agentId}' is not idle.`);\n\t\t}\n\t\tconst prior = this.latestAgentAttempt(snapshot, agent);\n\t\tconst contract = prior?.dispatch.executionContract;\n\t\tif (!prior || !contract) {\n\t\t\tthrow new DurableTaskRuntimeError(`Logical worker agent '${agentId}' has no immutable execution contract.`);\n\t\t}\n\t\tconst priorTask = snapshot.tasks[prior.taskId]?.task;\n\t\tif (!priorTask) throw new DurableTaskRuntimeError(`Logical worker agent '${agentId}' has no prior durable task.`);\n\t\tconst sequence = Object.values(snapshot.attempts).filter((attempt) => attempt.agentId === agentId).length + 1;\n\t\tconst taskId = `${agentId}:turn:${sequence}`;\n\t\tif (snapshot.tasks[taskId]) {\n\t\t\tthrow new DurableTaskRuntimeError(`Logical worker agent '${agentId}' already has turn ${sequence}.`);\n\t\t}\n\t\treturn this.prepareNormalized({\n\t\t\tlaneId: taskId,\n\t\t\tinstructions,\n\t\t\tprofileId: contract.worker.profile.profileId,\n\t\t\trole: agent.role,\n\t\t\trequiredCapabilities: contract.worker.authority.capabilities,\n\t\t\triskBudget: contract.worker.profile.budget,\n\t\t\ttaskContext: {\n\t\t\t\trequirementIds: prior.dispatch.requirementIds ?? [],\n\t\t\t\tdependsOnTaskIds: priorTask.dependsOn,\n\t\t\t\tacceptanceCriterionIds: priorTask.acceptanceCriterionIds,\n\t\t\t\tresourcePointerIds: prior.dispatch.resourcePointerIds,\n\t\t\t},\n\t\t\texecutionContract: contract,\n\t\t\tdispatchMetadata: {\n\t\t\t\tlogicalLaneId: agentId,\n\t\t\t\tdispatchSequence: sequence,\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate latestAgentAttempt(\n\t\tsnapshot: ReturnType<DurableTaskRuntime[\"getSnapshot\"]>,\n\t\tagent: AgentBindingContract,\n\t): AttemptRuntimeState | undefined {\n\t\treturn Object.values(snapshot.attempts)\n\t\t\t.filter((attempt) => attempt.agentId === agent.agentId)\n\t\t\t.sort((left, right) => left.createdAt.localeCompare(right.createdAt))\n\t\t\t.at(-1);\n\t}\n\n\tprivate prepareNormalized(input: {\n\t\tlaneId: string;\n\t\tinstructions: string;\n\t\tprofileId: string;\n\t\trole: WorkerRole;\n\t\trequiredCapabilities: readonly HarnessCapability[];\n\t\triskBudget: RiskBudget;\n\t\tgoal?: GoalState;\n\t\tgoalId?: string;\n\t\tverificationOfTaskId?: string;\n\t\ttaskContext?: WorkerDelegationTaskContext;\n\t\texecutionContract?: WorkerExecutionContract;\n\t\tdispatchMetadata?: Pick<\n\t\t\tOrchestrationDispatchRequest,\n\t\t\t\"executionKind\" | \"logicalLaneId\" | \"dispatchSequence\" | \"provider\" | \"authorizationId\" | \"worktreeLaneKey\"\n\t\t>;\n\t}): AttemptRuntimeState {\n\t\tlet snapshot = this.runtime.getSnapshot();\n\t\tconst existingVerificationSubject = input.verificationOfTaskId\n\t\t\t? snapshot.tasks[input.verificationOfTaskId]\n\t\t\t: undefined;\n\t\tif (input.verificationOfTaskId && !existingVerificationSubject) {\n\t\t\tthrow new DurableTaskRuntimeError(`Unknown verification subject '${input.verificationOfTaskId}'.`);\n\t\t}\n\t\tconst projectedGoal = input.goal ? projectGoalObjective(input.goal) : undefined;\n\t\tconst objectiveId =\n\t\t\texistingVerificationSubject?.task.objectiveId ??\n\t\t\tprojectedGoal?.objectiveId ??\n\t\t\t(input.goalId ? goalObjectiveId(input.goalId) : `session:${this.sessionId}`);\n\t\tif (input.goal && objectiveId === projectedGoal?.objectiveId) {\n\t\t\tthis.synchronizeGoalState(input.goal);\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t} else if (!snapshot.objectives[objectiveId]) {\n\t\t\tthis.runtime.createObjective({\n\t\t\t\tobjectiveId,\n\t\t\t\ttitle: input.goalId ? `Goal ${input.goalId}` : `Session ${this.sessionId}`,\n\t\t\t\tdescription: input.goalId ? \"Externally managed goal work\" : \"Session-scoped delegated work\",\n\t\t\t});\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t}\n\n\t\tif (!snapshot.tasks[input.laneId]) {\n\t\t\tconst verificationSubject = input.verificationOfTaskId\n\t\t\t\t? snapshot.tasks[input.verificationOfTaskId]\n\t\t\t\t: undefined;\n\t\t\tthis.runtime.createTask({\n\t\t\t\ttaskId: input.laneId,\n\t\t\t\tobjectiveId,\n\t\t\t\ttitle: deriveWorkerTaskLabel(input.instructions, `Delegated ${input.role} work`),\n\t\t\t\tdescription: input.instructions,\n\t\t\t\trole: input.role,\n\t\t\t\tdependsOn: input.taskContext?.dependsOnTaskIds ?? [],\n\t\t\t\trequiredCapabilities: input.requiredCapabilities,\n\t\t\t\tacceptanceCriterionIds: input.taskContext?.acceptanceCriterionIds ?? [],\n\t\t\t\triskBudget: input.riskBudget,\n\t\t\t\t...(input.verificationOfTaskId\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tverificationOfTaskId: input.verificationOfTaskId,\n\t\t\t\t\t\t\tacceptanceCriterionIds: verificationSubject?.task.acceptanceCriterionIds ?? [],\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t}\n\n\t\tconst task = snapshot.tasks[input.laneId];\n\t\tif (!task) throw new DurableTaskRuntimeError(`Failed to create task '${input.laneId}'.`);\n\t\tconst existing = [...task.attemptIds]\n\t\t\t.reverse()\n\t\t\t.map((attemptId) => snapshot.attempts[attemptId])\n\t\t\t.find((attempt): attempt is AttemptRuntimeState => attempt !== undefined && activeAttempt(attempt));\n\t\tif (existing) return existing;\n\n\t\treturn this.runtime.queueAttempt(input.laneId, {\n\t\t\ttaskId: input.laneId,\n\t\t\tprofileId: input.profileId,\n\t\t\tinstructions: input.instructions,\n\t\t\tresourcePointerIds: input.taskContext?.resourcePointerIds ?? [],\n\t\t\trequirementIds: input.taskContext?.requirementIds ?? [],\n\t\t\t...(input.executionContract ? { executionContract: input.executionContract } : {}),\n\t\t\t...input.dispatchMetadata,\n\t\t});\n\t}\n\n\tsynchronizeGoalState(goal: GoalState): void {\n\t\tconst objective = projectGoalObjective(goal);\n\t\tthis.runtime.ensureObjective(objective);\n\t\tfor (const evidence of projectGoalAcceptanceEvidence(goal)) {\n\t\t\tthis.runtime.recordObjectiveEvidence(objective.objectiveId, evidence);\n\t\t}\n\t\tconst status = this.runtime.getSnapshot().objectives[goalObjectiveId(goal.goalId)]?.objective.status;\n\t\tswitch (goal.status) {\n\t\t\tcase \"active\":\n\t\t\t\tif (status === \"paused\") this.runtime.resumeObjective(objective.objectiveId);\n\t\t\t\tbreak;\n\t\t\tcase \"paused\":\n\t\t\tcase \"blocked\":\n\t\t\tcase \"usage_limited\":\n\t\t\tcase \"budget_limited\":\n\t\t\t\tif (status === \"active\") this.runtime.pauseObjective(objective.objectiveId);\n\t\t\t\tbreak;\n\t\t\tcase \"cancelled\":\n\t\t\t\tif (status !== \"cancelled\") this.runtime.cancelObjective(objective.objectiveId);\n\t\t\t\tbreak;\n\t\t\tcase \"completed\":\n\t\t\t\tif (status !== \"completed\") {\n\t\t\t\t\tthis.runtime.completeObjectiveFromOwner(objective.objectiveId, hasGoalAcceptanceOverride(goal));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tstart(\n\t\tattemptId: string,\n\t\tleaseTtlMs: number,\n\t\townerId = `in-process:${this.sessionId}`,\n\t\tagentId?: string,\n\t): StartedDelegationAttempt {\n\t\tconst snapshot = this.runtime.getSnapshot();\n\t\tconst attempt = snapshot.attempts[attemptId];\n\t\tif (!attempt) throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);\n\t\tconst task = snapshot.tasks[attempt.taskId];\n\t\tif (!task) throw new DurableTaskRuntimeError(`Unknown task '${attempt.taskId}'.`);\n\t\tconst lease = this.runtime.leaseAttempt(attemptId, ownerId, leaseTtlMs, agentId);\n\t\tthis.runtime.startAttempt(attemptId, lease.leaseId, lease.fencingToken);\n\t\treturn {\n\t\t\tobjectiveId: task.task.objectiveId,\n\t\t\ttaskId: task.task.taskId,\n\t\t\tattemptId,\n\t\t\tleaseId: lease.leaseId,\n\t\t\tfencingToken: lease.fencingToken,\n\t\t\texpiresAt: lease.expiresAt,\n\t\t};\n\t}\n\n\tcancel(attemptId: string, reasonCode: string): void {\n\t\tthis.runtime.cancelAttempt(attemptId, reasonCode);\n\t}\n\n\t/** Latest fenced cumulative usage, if this attempt has crossed a durable checkpoint boundary. */\n\tgetAttemptUsage(attemptId: string): AttemptUsageSnapshot | undefined {\n\t\tconst snapshot = this.runtime.getSnapshot();\n\t\tconst attempt = snapshot.attempts[attemptId];\n\t\tif (!attempt) throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);\n\t\tfor (const checkpointId of [...attempt.checkpointIds].reverse()) {\n\t\t\tconst usage = snapshot.checkpoints[checkpointId]?.usage;\n\t\t\tif (usage) return structuredClone(usage);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Fence interrupted isolated completions and queue one replacement attempt per task. */\n\trecoverQueuedDispatches(): AttemptRuntimeState[] {\n\t\tthis.runtime.recoverInterruptedUnboundAttempts((attempt) => attempt.dispatch.executionKind !== \"managed-process\");\n\t\tlet snapshot = this.runtime.getSnapshot();\n\t\tfor (const task of Object.values(snapshot.tasks)) {\n\t\t\tconst attempts = task.attemptIds.map((attemptId) => snapshot.attempts[attemptId]).filter(Boolean);\n\t\t\tif (attempts.some((attempt) => attempt && activeAttempt(attempt))) continue;\n\t\t\tconst interrupted = [...attempts].reverse().find((attempt) => attempt?.status === \"expired\");\n\t\t\tif (!interrupted || task.task.status !== \"ready\") continue;\n\t\t\tconst maxAttempts = task.task.riskBudget.maxAttempts;\n\t\t\tif (maxAttempts !== undefined && task.attemptIds.length >= maxAttempts) {\n\t\t\t\tthis.runtime.failTask(task.task.taskId, \"attempt_budget_exhausted\");\n\t\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.runtime.queueAttempt(task.task.taskId, interrupted.dispatch);\n\t\t\tsnapshot = this.runtime.getSnapshot();\n\t\t}\n\t\treturn Object.values(snapshot.attempts).filter(\n\t\t\t(attempt) => attempt.status === \"queued\" && attempt.dispatch.executionKind !== \"managed-process\",\n\t\t);\n\t}\n}\n"]}
@@ -72,6 +72,7 @@ export declare class OrchestrationEventStore {
72
72
  private unlinkManagedFile;
73
73
  private readEventFileUnlocked;
74
74
  private idempotencyPath;
75
+ private serializeIdempotencyMarker;
75
76
  private writeIdempotencyMarkerUnlocked;
76
77
  private readIdempotentEventUnlocked;
77
78
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"event-store.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/event-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { stateFile } from \"../agent-paths.ts\";\nimport type { JsonObject } from \"../autonomy/contracts.ts\";\nimport { withFileLockSync, writeFileAtomicSync } from \"../util/atomic-file.ts\";\nimport {\n\ttype AppendOrchestrationEventInput,\n\tisOrchestrationEvent,\n\tORCHESTRATION_SCHEMA_VERSION,\n\ttype OrchestrationEvent,\n} from \"./contracts.ts\";\n\nconst EVENT_FILE_PATTERN = /^(\\d{16})\\.json$/;\nconst SNAPSHOT_FILE_PATTERN = /^(\\d{16})\\.json$/;\nconst DEFAULT_MAX_TAIL_EVENTS = 256;\nconst DEFAULT_MAX_TAIL_BYTES = 4 * 1024 * 1024;\nconst DEFAULT_MAX_IDEMPOTENCY_EVENTS = 512;\n\ninterface EventCursor {\n\tversion: 1;\n\tlastOrdinal: number;\n\ttailBytes?: number;\n}\n\ninterface SynchronizedIndexes {\n\tlastOrdinal: number;\n\ttailBytes: number;\n}\n\ninterface IdempotencyMarker {\n\tversion: 1;\n\tkey: string;\n\tordinal: number;\n}\n\ninterface ProjectionSnapshotContent {\n\tversion: 1;\n\tschemaVersion: typeof ORCHESTRATION_SCHEMA_VERSION;\n\tthroughOrdinal: number;\n\tcreatedAt: string;\n\tprojection: JsonObject;\n\tidempotencyEvents: OrchestrationEvent[];\n}\n\ninterface ProjectionSnapshot extends ProjectionSnapshotContent {\n\tdigest: string;\n}\n\ninterface SnapshotBaseline {\n\tversion: 1;\n\tthroughOrdinal: number;\n\tdigest: string;\n\tsnapshotFile: string;\n}\n\ninterface SnapshotFileSignature {\n\tsize: number;\n\tmtimeMs: number;\n\tctimeMs: number;\n}\n\ninterface VerifiedSnapshotBaseline {\n\tbaseline: SnapshotBaseline;\n\tsignature: SnapshotFileSignature;\n}\n\nexport interface OrchestrationEventStoreOptions {\n\tagentDir: string;\n\tsessionId: string;\n\tnow?: () => string;\n\tcreateEventId?: () => string;\n\tmaxTailEvents?: number;\n\tmaxTailBytes?: number;\n\tmaxIdempotencyEvents?: number;\n}\n\nexport class OrchestrationEventStoreError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"OrchestrationEventStoreError\";\n\t}\n}\n\nexport class OrchestrationConcurrencyError extends OrchestrationEventStoreError {\n\tconstructor(expected: number, actual: number) {\n\t\tsuper(`Orchestration event cursor changed: expected ${expected}, actual ${actual}.`);\n\t\tthis.name = \"OrchestrationConcurrencyError\";\n\t}\n}\n\nexport class OrchestrationSnapshotRequiredError extends OrchestrationEventStoreError {\n\treadonly throughOrdinal: number;\n\n\tconstructor(throughOrdinal: number) {\n\t\tsuper(`Orchestration projection snapshot through ordinal ${throughOrdinal} is required.`);\n\t\tthis.name = \"OrchestrationSnapshotRequiredError\";\n\t\tthis.throughOrdinal = throughOrdinal;\n\t}\n}\n\nfunction safeSessionDirectoryName(sessionId: string): string {\n\tconst readable = encodeURIComponent(sessionId).replaceAll(\"%\", \"_\").slice(0, 80) || \"session\";\n\tconst digest = createHash(\"sha256\").update(sessionId).digest(\"hex\").slice(0, 16);\n\treturn `${readable}-${digest}`;\n}\n\nfunction parseCursor(filePath: string): EventCursor | undefined {\n\tif (!existsSync(filePath)) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(readFileSync(filePath, \"utf-8\"));\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\tconst record = parsed as Record<string, unknown>;\n\t\tif (record.version !== 1 || !Number.isSafeInteger(record.lastOrdinal) || Number(record.lastOrdinal) < 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (record.tailBytes !== undefined && (!Number.isSafeInteger(record.tailBytes) || Number(record.tailBytes) < 0)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tlastOrdinal: Number(record.lastOrdinal),\n\t\t\t...(record.tailBytes !== undefined ? { tailBytes: Number(record.tailBytes) } : {}),\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction serialize(value: unknown): string {\n\treturn `${JSON.stringify(value, null, \"\\t\")}\\n`;\n}\n\nfunction eventFileName(ordinal: number): string {\n\treturn `${String(ordinal).padStart(16, \"0\")}.json`;\n}\n\nfunction snapshotDigest(content: ProjectionSnapshotContent): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(content)).digest(\"hex\");\n}\n\nfunction positiveSafeInteger(value: number | undefined, fallback: number, label: string): number {\n\tconst resolved = value ?? fallback;\n\tif (!Number.isSafeInteger(resolved) || resolved < 1) {\n\t\tthrow new OrchestrationEventStoreError(`${label} must be a positive safe integer.`);\n\t}\n\treturn resolved;\n}\n\n/**\n * Append-only event tail with a replaceable full-state projection snapshot. Atomic rename prevents\n * torn records. Snapshot publication is two-phase (payload, then small baseline pointer), after which\n * covered event/idempotency files are pruned. A crash at any point leaves either the old replay prefix\n * or the new verified snapshot authoritative; ordinals never reset.\n */\nexport class OrchestrationEventStore {\n\treadonly rootDir: string;\n\treadonly eventsDir: string;\n\treadonly idempotencyDir: string;\n\treadonly cursorPath: string;\n\treadonly snapshotsDir: string;\n\treadonly baselinePath: string;\n\tprivate readonly now: () => string;\n\tprivate readonly createEventId: () => string;\n\tprivate readonly maxTailEvents: number;\n\tprivate readonly maxTailBytes: number;\n\tprivate readonly maxIdempotencyEvents: number;\n\tprivate readonly listeners = new Set<(event: OrchestrationEvent) => void>();\n\tprivate verifiedSnapshotBaseline: VerifiedSnapshotBaseline | undefined;\n\n\tconstructor(options: OrchestrationEventStoreOptions) {\n\t\tthis.rootDir = stateFile(options.agentDir, \"orchestration\", safeSessionDirectoryName(options.sessionId));\n\t\tthis.eventsDir = join(this.rootDir, \"events\");\n\t\tthis.idempotencyDir = join(this.rootDir, \"idempotency\");\n\t\tthis.cursorPath = join(this.rootDir, \"cursor.json\");\n\t\tthis.snapshotsDir = join(this.rootDir, \"snapshots\");\n\t\tthis.baselinePath = join(this.rootDir, \"projection-baseline.json\");\n\t\tthis.now = options.now ?? (() => new Date().toISOString());\n\t\tthis.createEventId = options.createEventId ?? randomUUID;\n\t\tthis.maxTailEvents = positiveSafeInteger(options.maxTailEvents, DEFAULT_MAX_TAIL_EVENTS, \"maxTailEvents\");\n\t\tthis.maxTailBytes = positiveSafeInteger(options.maxTailBytes, DEFAULT_MAX_TAIL_BYTES, \"maxTailBytes\");\n\t\tthis.maxIdempotencyEvents = positiveSafeInteger(\n\t\t\toptions.maxIdempotencyEvents,\n\t\t\tDEFAULT_MAX_IDEMPOTENCY_EVENTS,\n\t\t\t\"maxIdempotencyEvents\",\n\t\t);\n\t}\n\n\tappend(input: AppendOrchestrationEventInput, options: { expectedLastOrdinal?: number } = {}): OrchestrationEvent {\n\t\tconst committed = withFileLockSync(this.cursorPath, (): { event: OrchestrationEvent; appended: boolean } => {\n\t\t\tconst cursor = this.synchronizeIndexesUnlocked();\n\t\t\tconst actual = cursor.lastOrdinal;\n\t\t\tif (input.idempotencyKey) {\n\t\t\t\tconst existing = this.readIdempotentEventUnlocked(input.idempotencyKey);\n\t\t\t\tif (existing) return { event: existing, appended: false };\n\t\t\t}\n\t\t\tif (options.expectedLastOrdinal !== undefined) {\n\t\t\t\tif (actual !== options.expectedLastOrdinal) {\n\t\t\t\t\tthrow new OrchestrationConcurrencyError(options.expectedLastOrdinal, actual);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ordinal = actual + 1;\n\t\t\tconst next: OrchestrationEvent = {\n\t\t\t\tschemaVersion: ORCHESTRATION_SCHEMA_VERSION,\n\t\t\t\tordinal,\n\t\t\t\teventId: this.createEventId(),\n\t\t\t\ttype: input.type,\n\t\t\t\taggregateId: input.aggregateId,\n\t\t\t\tactor: input.actor,\n\t\t\t\toccurredAt: this.now(),\n\t\t\t\t...(input.correlationId ? { correlationId: input.correlationId } : {}),\n\t\t\t\t...(input.causationId ? { causationId: input.causationId } : {}),\n\t\t\t\t...(input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}),\n\t\t\t\tpayload: structuredClone(input.payload),\n\t\t\t};\n\t\t\tconst serializedEvent = serialize(next);\n\t\t\twriteFileAtomicSync(join(this.eventsDir, eventFileName(ordinal)), serializedEvent);\n\t\t\tif (input.idempotencyKey) this.writeIdempotencyMarkerUnlocked(input.idempotencyKey, ordinal);\n\t\t\twriteFileAtomicSync(\n\t\t\t\tthis.cursorPath,\n\t\t\t\tserialize({\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tlastOrdinal: ordinal,\n\t\t\t\t\ttailBytes: cursor.tailBytes + Buffer.byteLength(serializedEvent),\n\t\t\t\t} satisfies EventCursor),\n\t\t\t);\n\t\t\treturn { event: next, appended: true };\n\t\t});\n\n\t\tif (committed.appended) {\n\t\t\tfor (const listener of this.listeners) listener(structuredClone(committed.event));\n\t\t}\n\t\treturn structuredClone(committed.event);\n\t}\n\n\treadAll(): OrchestrationEvent[] {\n\t\treturn this.readAllUnlocked();\n\t}\n\n\treadAfter(ordinal: number): OrchestrationEvent[] {\n\t\tconst baseline = this.readBaselineUnlocked();\n\t\tif (baseline && ordinal < baseline.throughOrdinal) {\n\t\t\tthrow new OrchestrationSnapshotRequiredError(baseline.throughOrdinal);\n\t\t}\n\t\tconst events: OrchestrationEvent[] = [];\n\t\tconst names = this.eventFileNamesUnlocked();\n\t\tthis.assertReadableTailUnlocked(names, baseline?.throughOrdinal ?? 0);\n\t\tlet previousOrdinal = ordinal;\n\t\tfor (const name of names) {\n\t\t\tconst match = EVENT_FILE_PATTERN.exec(name);\n\t\t\tif (!match) continue;\n\t\t\tconst fileOrdinal = Number(match[1]);\n\t\t\tif (fileOrdinal <= ordinal) continue;\n\t\t\tconst event = this.readEventFileUnlocked(name);\n\t\t\tif (event.ordinal !== fileOrdinal || event.ordinal <= previousOrdinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(`Non-monotonic orchestration event ordinal in ${name}`);\n\t\t\t}\n\t\t\tpreviousOrdinal = event.ordinal;\n\t\t\tevents.push(structuredClone(event));\n\t\t}\n\t\treturn events;\n\t}\n\n\treadProjectionSnapshot(): { throughOrdinal: number; projection: JsonObject } | undefined {\n\t\tconst snapshot = this.readProjectionSnapshotUnlocked();\n\t\treturn snapshot\n\t\t\t? { throughOrdinal: snapshot.throughOrdinal, projection: structuredClone(snapshot.projection) }\n\t\t\t: undefined;\n\t}\n\n\t/** Replace a large replay prefix with one verified current-state snapshot and a bounded tail. */\n\tcompactIfNeeded(throughOrdinal: number, projection: () => JsonObject): boolean {\n\t\tconst baselineBeforeLock = this.readBaselineUnlocked()?.throughOrdinal ?? 0;\n\t\tconst cursorBeforeLock = parseCursor(this.cursorPath);\n\t\tif (\n\t\t\tcursorBeforeLock?.lastOrdinal === throughOrdinal &&\n\t\t\tcursorBeforeLock.tailBytes !== undefined &&\n\t\t\tthroughOrdinal - baselineBeforeLock < this.maxTailEvents &&\n\t\t\tcursorBeforeLock.tailBytes < this.maxTailBytes\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\treturn withFileLockSync(this.cursorPath, () => {\n\t\t\tconst cursor = this.synchronizeIndexesUnlocked();\n\t\t\tif (cursor.lastOrdinal !== throughOrdinal) return false;\n\t\t\tconst baselineOrdinal = this.readBaselineUnlocked()?.throughOrdinal ?? 0;\n\t\t\tconst tailNames = this.eventFileNamesUnlocked().filter(\n\t\t\t\t(name) => Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0) > baselineOrdinal,\n\t\t\t);\n\t\t\tif (tailNames.length < this.maxTailEvents && cursor.tailBytes < this.maxTailBytes) return false;\n\n\t\t\tconst retainedByKey = new Map<string, OrchestrationEvent>();\n\t\t\tfor (const event of this.readProjectionSnapshotUnlocked()?.idempotencyEvents ?? []) {\n\t\t\t\tif (event.idempotencyKey) retainedByKey.set(event.idempotencyKey, event);\n\t\t\t}\n\t\t\tfor (const name of tailNames) {\n\t\t\t\tconst event = this.readEventFileUnlocked(name);\n\t\t\t\tif (event.ordinal <= throughOrdinal && event.idempotencyKey) {\n\t\t\t\t\tretainedByKey.set(event.idempotencyKey, event);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst idempotencyEvents = [...retainedByKey.values()]\n\t\t\t\t.sort((left, right) => left.ordinal - right.ordinal)\n\t\t\t\t.slice(-this.maxIdempotencyEvents)\n\t\t\t\t.map((event) => structuredClone(event));\n\t\t\tconst content: ProjectionSnapshotContent = {\n\t\t\t\tversion: 1,\n\t\t\t\tschemaVersion: ORCHESTRATION_SCHEMA_VERSION,\n\t\t\t\tthroughOrdinal,\n\t\t\t\tcreatedAt: this.now(),\n\t\t\t\tprojection: structuredClone(projection()),\n\t\t\t\tidempotencyEvents,\n\t\t\t};\n\t\t\tconst digest = snapshotDigest(content);\n\t\t\tconst snapshotFile = eventFileName(throughOrdinal);\n\t\t\twriteFileAtomicSync(\n\t\t\t\tjoin(this.snapshotsDir, snapshotFile),\n\t\t\t\tserialize({ ...content, digest } satisfies ProjectionSnapshot),\n\t\t\t);\n\t\t\twriteFileAtomicSync(\n\t\t\t\tthis.baselinePath,\n\t\t\t\tserialize({ version: 1, throughOrdinal, digest, snapshotFile } satisfies SnapshotBaseline),\n\t\t\t);\n\n\t\t\tfor (const name of this.idempotencyFileNamesUnlocked()) {\n\t\t\t\tthis.unlinkManagedFile(join(this.idempotencyDir, name));\n\t\t\t}\n\t\t\tfor (const name of this.eventFileNamesUnlocked()) {\n\t\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\t\tif (ordinal <= throughOrdinal) this.unlinkManagedFile(join(this.eventsDir, name));\n\t\t\t}\n\t\t\tfor (const name of this.snapshotFileNamesUnlocked()) {\n\t\t\t\tif (name !== snapshotFile) this.unlinkManagedFile(join(this.snapshotsDir, name));\n\t\t\t}\n\t\t\twriteFileAtomicSync(\n\t\t\t\tthis.cursorPath,\n\t\t\t\tserialize({ version: 1, lastOrdinal: throughOrdinal, tailBytes: 0 } satisfies EventCursor),\n\t\t\t);\n\t\t\treturn true;\n\t\t});\n\t}\n\n\tsubscribe(listener: (event: OrchestrationEvent) => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\tprivate readAllUnlocked(): OrchestrationEvent[] {\n\t\tconst names = this.eventFileNamesUnlocked();\n\t\tconst events: OrchestrationEvent[] = [];\n\t\tconst baselineOrdinal = this.readProjectionSnapshotUnlocked()?.throughOrdinal ?? 0;\n\t\tthis.assertReadableTailUnlocked(names, baselineOrdinal);\n\t\tlet previousOrdinal = baselineOrdinal;\n\t\tfor (const name of names) {\n\t\t\tconst match = EVENT_FILE_PATTERN.exec(name);\n\t\t\tif (!match) continue;\n\t\t\tconst fileOrdinal = Number(match[1]);\n\t\t\tif (fileOrdinal <= baselineOrdinal) continue;\n\t\t\tconst parsed = this.readEventFileUnlocked(name);\n\t\t\tif (parsed.ordinal !== fileOrdinal || parsed.ordinal <= previousOrdinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(`Non-monotonic orchestration event ordinal in ${name}`);\n\t\t\t}\n\t\t\tpreviousOrdinal = parsed.ordinal;\n\t\t\tevents.push(structuredClone(parsed));\n\t\t}\n\t\treturn events;\n\t}\n\n\tprivate eventFileNamesUnlocked(): string[] {\n\t\ttry {\n\t\t\treturn readdirSync(this.eventsDir)\n\t\t\t\t.filter((candidate) => EVENT_FILE_PATTERN.test(candidate))\n\t\t\t\t.sort();\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate idempotencyFileNamesUnlocked(): string[] {\n\t\ttry {\n\t\t\treturn readdirSync(this.idempotencyDir).filter((candidate) => candidate.endsWith(\".json\"));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tprivate snapshotFileNamesUnlocked(): string[] {\n\t\ttry {\n\t\t\treturn readdirSync(this.snapshotsDir)\n\t\t\t\t.filter((candidate) => SNAPSHOT_FILE_PATTERN.test(candidate))\n\t\t\t\t.sort();\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/** Event files are a contiguous append-only tail after the published snapshot. */\n\tprivate assertContiguousTailOrdinalsUnlocked(names: readonly string[], baselineOrdinal: number): void {\n\t\tlet expectedOrdinal = baselineOrdinal + 1;\n\t\tfor (const name of names) {\n\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\t// A crash after publishing the baseline but before pruning is safe: the snapshot covers these files.\n\t\t\tif (ordinal <= baselineOrdinal) continue;\n\t\t\tif (ordinal !== expectedOrdinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t\t`Missing orchestration event ordinal ${expectedOrdinal} before ${name}.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\texpectedOrdinal++;\n\t\t}\n\t}\n\n\t/** A valid cursor may lag a newly published snapshot, but it must never lead its committed tail. */\n\tprivate assertReadableTailUnlocked(names: readonly string[], baselineOrdinal: number): void {\n\t\tthis.assertContiguousTailOrdinalsUnlocked(names, baselineOrdinal);\n\t\tconst highestEventOrdinal = names.length > 0 ? Number(EVENT_FILE_PATTERN.exec(names.at(-1)!)?.[1] ?? 0) : 0;\n\t\tconst highestCommittedOrdinal = Math.max(baselineOrdinal, highestEventOrdinal);\n\t\tconst cursor = parseCursor(this.cursorPath);\n\t\tif (cursor && cursor.lastOrdinal > highestCommittedOrdinal) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Orchestration cursor ${cursor.lastOrdinal} is ahead of the last committed event ${highestCommittedOrdinal}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate readBaselineUnlocked(): SnapshotBaseline | undefined {\n\t\tif (!existsSync(this.baselinePath)) return undefined;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(this.baselinePath, \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration snapshot baseline: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration snapshot baseline.\");\n\t\t}\n\t\tconst baseline = parsed as Record<string, unknown>;\n\t\tif (\n\t\t\tbaseline.version !== 1 ||\n\t\t\t!Number.isSafeInteger(baseline.throughOrdinal) ||\n\t\t\tNumber(baseline.throughOrdinal) < 1 ||\n\t\t\ttypeof baseline.digest !== \"string\" ||\n\t\t\tbaseline.digest.length === 0 ||\n\t\t\ttypeof baseline.snapshotFile !== \"string\" ||\n\t\t\t!SNAPSHOT_FILE_PATTERN.test(baseline.snapshotFile) ||\n\t\t\tNumber(SNAPSHOT_FILE_PATTERN.exec(baseline.snapshotFile)?.[1] ?? 0) !== Number(baseline.throughOrdinal)\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration snapshot baseline.\");\n\t\t}\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tthroughOrdinal: Number(baseline.throughOrdinal),\n\t\t\tdigest: baseline.digest,\n\t\t\tsnapshotFile: baseline.snapshotFile,\n\t\t};\n\t}\n\n\tprivate readProjectionSnapshotUnlocked(): ProjectionSnapshot | undefined {\n\t\tconst baseline = this.readBaselineUnlocked();\n\t\tif (!baseline) return undefined;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(join(this.snapshotsDir, baseline.snapshotFile), \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration projection snapshot: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration projection snapshot.\");\n\t\t}\n\t\tconst snapshot = parsed as Record<string, unknown>;\n\t\tif (\n\t\t\tsnapshot.version !== 1 ||\n\t\t\tsnapshot.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION ||\n\t\t\tsnapshot.throughOrdinal !== baseline.throughOrdinal ||\n\t\t\ttypeof snapshot.createdAt !== \"string\" ||\n\t\t\t!snapshot.projection ||\n\t\t\ttypeof snapshot.projection !== \"object\" ||\n\t\t\tArray.isArray(snapshot.projection) ||\n\t\t\t!Array.isArray(snapshot.idempotencyEvents) ||\n\t\t\t!snapshot.idempotencyEvents.every(isOrchestrationEvent) ||\n\t\t\t!snapshot.idempotencyEvents.every(\n\t\t\t\t(event) => event.ordinal <= baseline.throughOrdinal && event.idempotencyKey !== undefined,\n\t\t\t) ||\n\t\t\tnew Set(snapshot.idempotencyEvents.map((event) => event.idempotencyKey)).size !==\n\t\t\t\tsnapshot.idempotencyEvents.length ||\n\t\t\ttypeof snapshot.digest !== \"string\" ||\n\t\t\tsnapshot.digest !== baseline.digest\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration projection snapshot.\");\n\t\t}\n\t\tconst content: ProjectionSnapshotContent = {\n\t\t\tversion: 1,\n\t\t\tschemaVersion: ORCHESTRATION_SCHEMA_VERSION,\n\t\t\tthroughOrdinal: baseline.throughOrdinal,\n\t\t\tcreatedAt: snapshot.createdAt,\n\t\t\tprojection: structuredClone(snapshot.projection) as JsonObject,\n\t\t\tidempotencyEvents: snapshot.idempotencyEvents.map((event) => structuredClone(event)),\n\t\t};\n\t\tif (snapshotDigest(content) !== baseline.digest) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration projection snapshot digest mismatch.\");\n\t\t}\n\t\treturn { ...content, digest: baseline.digest };\n\t}\n\n\t/**\n\t * Appends need a verified snapshot baseline but must not deserialize its projection on every event.\n\t * The baseline pointer remains small and is reread each time; an immutable snapshot is reparsed only\n\t * when its pointer or filesystem identity changes.\n\t */\n\tprivate readVerifiedSnapshotBaselineUnlocked(): SnapshotBaseline | undefined {\n\t\tconst baseline = this.readBaselineUnlocked();\n\t\tif (!baseline) {\n\t\t\tthis.verifiedSnapshotBaseline = undefined;\n\t\t\treturn undefined;\n\t\t}\n\t\tconst signature = this.snapshotFileSignatureUnlocked(baseline);\n\t\tconst cached = this.verifiedSnapshotBaseline;\n\t\tif (\n\t\t\tcached &&\n\t\t\tcached.baseline.throughOrdinal === baseline.throughOrdinal &&\n\t\t\tcached.baseline.digest === baseline.digest &&\n\t\t\tcached.baseline.snapshotFile === baseline.snapshotFile &&\n\t\t\tcached.signature.size === signature.size &&\n\t\t\tcached.signature.mtimeMs === signature.mtimeMs &&\n\t\t\tcached.signature.ctimeMs === signature.ctimeMs\n\t\t) {\n\t\t\treturn baseline;\n\t\t}\n\t\tthis.readProjectionSnapshotUnlocked();\n\t\tconst verifiedSignature = this.snapshotFileSignatureUnlocked(baseline);\n\t\tif (\n\t\t\tverifiedSignature.size !== signature.size ||\n\t\t\tverifiedSignature.mtimeMs !== signature.mtimeMs ||\n\t\t\tverifiedSignature.ctimeMs !== signature.ctimeMs\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration projection snapshot changed while being verified.\");\n\t\t}\n\t\tthis.verifiedSnapshotBaseline = { baseline, signature };\n\t\treturn baseline;\n\t}\n\n\tprivate snapshotFileSignatureUnlocked(baseline: SnapshotBaseline): SnapshotFileSignature {\n\t\ttry {\n\t\t\tconst stats = statSync(join(this.snapshotsDir, baseline.snapshotFile));\n\t\t\tif (!stats.isFile())\n\t\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration projection snapshot is not a file.\");\n\t\t\treturn { size: stats.size, mtimeMs: stats.mtimeMs, ctimeMs: stats.ctimeMs };\n\t\t} catch (error) {\n\t\t\tif (error instanceof OrchestrationEventStoreError) throw error;\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration projection snapshot: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate unlinkManagedFile(filePath: string): void {\n\t\ttry {\n\t\t\tunlinkSync(filePath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t}\n\t}\n\n\tprivate readEventFileUnlocked(name: string): OrchestrationEvent {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(join(this.eventsDir, name), \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration event ${name}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOrchestrationEvent(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(`Invalid orchestration event record: ${name}`);\n\t\t}\n\t\treturn parsed;\n\t}\n\n\tprivate idempotencyPath(key: string): string {\n\t\treturn join(this.idempotencyDir, `${createHash(\"sha256\").update(key).digest(\"hex\")}.json`);\n\t}\n\n\tprivate writeIdempotencyMarkerUnlocked(key: string, ordinal: number): void {\n\t\twriteFileAtomicSync(\n\t\t\tthis.idempotencyPath(key),\n\t\t\tserialize({ version: 1, key, ordinal } satisfies IdempotencyMarker),\n\t\t);\n\t}\n\n\tprivate readIdempotentEventUnlocked(key: string): OrchestrationEvent | undefined {\n\t\tconst markerPath = this.idempotencyPath(key);\n\t\tif (!existsSync(markerPath)) {\n\t\t\treturn this.readProjectionSnapshotUnlocked()?.idempotencyEvents.find((event) => event.idempotencyKey === key);\n\t\t}\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(markerPath, \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration idempotency marker: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration idempotency marker.\");\n\t\t}\n\t\tconst marker = parsed as Record<string, unknown>;\n\t\tif (\n\t\t\tmarker.version !== 1 ||\n\t\t\tmarker.key !== key ||\n\t\t\t!Number.isSafeInteger(marker.ordinal) ||\n\t\t\tNumber(marker.ordinal) < 1\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration idempotency marker.\");\n\t\t}\n\t\tconst ordinal = Number(marker.ordinal);\n\t\tif (!existsSync(join(this.eventsDir, eventFileName(ordinal)))) {\n\t\t\tconst retained = this.readProjectionSnapshotUnlocked()?.idempotencyEvents.find(\n\t\t\t\t(event) => event.idempotencyKey === key && event.ordinal === ordinal,\n\t\t\t);\n\t\t\tif (retained) return retained;\n\t\t}\n\t\tconst event = this.readEventFileUnlocked(eventFileName(ordinal));\n\t\tif (event.ordinal !== ordinal || event.idempotencyKey !== key) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration idempotency marker does not match its event.\");\n\t\t}\n\t\treturn event;\n\t}\n\n\t/**\n\t * Reconcile only the crash tail when an event rename committed before its marker/cursor. Normal\n\t * appends read directory metadata plus one marker, never the accumulated event payload prefix.\n\t */\n\tprivate synchronizeIndexesUnlocked(): SynchronizedIndexes {\n\t\tconst names = this.eventFileNamesUnlocked();\n\t\tconst baselineOrdinal = this.readVerifiedSnapshotBaselineUnlocked()?.throughOrdinal ?? 0;\n\t\tthis.assertReadableTailUnlocked(names, baselineOrdinal);\n\t\tconst highestEventOrdinal = names.length > 0 ? Number(EVENT_FILE_PATTERN.exec(names.at(-1)!)?.[1] ?? 0) : 0;\n\t\tconst highest = Math.max(baselineOrdinal, highestEventOrdinal);\n\t\tconst cursor = parseCursor(this.cursorPath);\n\t\tif (\n\t\t\tcursor?.lastOrdinal === highest &&\n\t\t\tcursor.tailBytes !== undefined &&\n\t\t\t(highest > baselineOrdinal || cursor.tailBytes === 0)\n\t\t) {\n\t\t\treturn { lastOrdinal: highest, tailBytes: cursor.tailBytes };\n\t\t}\n\t\tif (cursor && cursor.lastOrdinal > highest) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Orchestration cursor ${cursor.lastOrdinal} is ahead of the last committed event ${highest}.`,\n\t\t\t);\n\t\t}\n\t\tconst rebuildFrom = Math.max(\n\t\t\tbaselineOrdinal + 1,\n\t\t\tcursor && cursor.lastOrdinal >= baselineOrdinal && cursor.lastOrdinal <= highest\n\t\t\t\t? cursor.lastOrdinal + 1\n\t\t\t\t: baselineOrdinal + 1,\n\t\t);\n\t\tfor (const name of names) {\n\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\tif (ordinal < rebuildFrom || ordinal <= baselineOrdinal) continue;\n\t\t\tconst event = this.readEventFileUnlocked(name);\n\t\t\tif (event.ordinal !== ordinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(`Event ordinal does not match file name: ${name}`);\n\t\t\t}\n\t\t\tif (event.idempotencyKey) this.writeIdempotencyMarkerUnlocked(event.idempotencyKey, ordinal);\n\t\t}\n\t\tconst tailBytes = names.reduce((total, name) => {\n\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\tif (ordinal <= baselineOrdinal) return total;\n\t\t\ttry {\n\t\t\t\treturn total + statSync(join(this.eventsDir, name)).size;\n\t\t\t} catch {\n\t\t\t\treturn total;\n\t\t\t}\n\t\t}, 0);\n\t\twriteFileAtomicSync(\n\t\t\tthis.cursorPath,\n\t\t\tserialize({ version: 1, lastOrdinal: highest, tailBytes } satisfies EventCursor),\n\t\t);\n\t\treturn { lastOrdinal: highest, tailBytes };\n\t}\n}\n"]}
1
+ {"version":3,"file":"event-store.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/event-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { existsSync, statSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { orchestrationEventStoreDir } from \"../agent-paths.ts\";\nimport type { JsonObject } from \"../autonomy/contracts.ts\";\nimport { withFileLockSync, writeFileAtomicSync } from \"../util/atomic-file.ts\";\nimport { readBoundedDirectoryNamesSync, readBoundedTextFileSync } from \"../util/bounded-file.ts\";\nimport {\n\ttype AppendOrchestrationEventInput,\n\tisOrchestrationEvent,\n\tORCHESTRATION_SCHEMA_VERSION,\n\ttype OrchestrationEvent,\n} from \"./contracts.ts\";\n\nconst EVENT_FILE_PATTERN = /^(\\d{16})\\.json$/;\nconst SNAPSHOT_FILE_PATTERN = /^(\\d{16})\\.json$/;\nconst DEFAULT_MAX_TAIL_EVENTS = 256;\nconst DEFAULT_MAX_TAIL_BYTES = 4 * 1024 * 1024;\nconst DEFAULT_MAX_IDEMPOTENCY_EVENTS = 512;\nconst MAX_CONFIGURED_TAIL_EVENTS = 1_024;\nconst MAX_CONFIGURED_TAIL_BYTES = 16 * 1024 * 1024;\nconst MAX_CONFIGURED_IDEMPOTENCY_EVENTS = 1_024;\nconst MAX_EVENT_DIRECTORY_ENTRIES = 2_048;\nconst MAX_IDEMPOTENCY_DIRECTORY_ENTRIES = 2_048;\nconst MAX_SNAPSHOT_DIRECTORY_ENTRIES = 8;\nconst MAX_CURSOR_BYTES = 64 * 1024;\nconst MAX_BASELINE_BYTES = 64 * 1024;\nconst MAX_IDEMPOTENCY_MARKER_BYTES = 64 * 1024;\n/** Full current task state plus retained idempotency evidence; intentionally above the 16MiB tail cap. */\nconst MAX_PROJECTION_SNAPSHOT_BYTES = 32 * 1024 * 1024;\n\ninterface EventCursor {\n\tversion: 1;\n\tlastOrdinal: number;\n\ttailBytes?: number;\n}\n\ninterface SynchronizedIndexes {\n\tlastOrdinal: number;\n\ttailBytes: number;\n}\n\ninterface IdempotencyMarker {\n\tversion: 1;\n\tkey: string;\n\tordinal: number;\n}\n\ninterface ProjectionSnapshotContent {\n\tversion: 1;\n\tschemaVersion: typeof ORCHESTRATION_SCHEMA_VERSION;\n\tthroughOrdinal: number;\n\tcreatedAt: string;\n\tprojection: JsonObject;\n\tidempotencyEvents: OrchestrationEvent[];\n}\n\ninterface ProjectionSnapshot extends ProjectionSnapshotContent {\n\tdigest: string;\n}\n\ninterface SnapshotBaseline {\n\tversion: 1;\n\tthroughOrdinal: number;\n\tdigest: string;\n\tsnapshotFile: string;\n}\n\ninterface SnapshotFileSignature {\n\tsize: number;\n\tmtimeMs: number;\n\tctimeMs: number;\n}\n\ninterface VerifiedSnapshotBaseline {\n\tbaseline: SnapshotBaseline;\n\tsignature: SnapshotFileSignature;\n}\n\nexport interface OrchestrationEventStoreOptions {\n\tagentDir: string;\n\tsessionId: string;\n\tnow?: () => string;\n\tcreateEventId?: () => string;\n\tmaxTailEvents?: number;\n\tmaxTailBytes?: number;\n\tmaxIdempotencyEvents?: number;\n}\n\nexport class OrchestrationEventStoreError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"OrchestrationEventStoreError\";\n\t}\n}\n\nexport class OrchestrationConcurrencyError extends OrchestrationEventStoreError {\n\tconstructor(expected: number, actual: number) {\n\t\tsuper(`Orchestration event cursor changed: expected ${expected}, actual ${actual}.`);\n\t\tthis.name = \"OrchestrationConcurrencyError\";\n\t}\n}\n\nexport class OrchestrationSnapshotRequiredError extends OrchestrationEventStoreError {\n\treadonly throughOrdinal: number;\n\n\tconstructor(throughOrdinal: number) {\n\t\tsuper(`Orchestration projection snapshot through ordinal ${throughOrdinal} is required.`);\n\t\tthis.name = \"OrchestrationSnapshotRequiredError\";\n\t\tthis.throughOrdinal = throughOrdinal;\n\t}\n}\n\nfunction parseCursor(filePath: string): EventCursor | undefined {\n\tif (!existsSync(filePath)) return undefined;\n\tlet content: string;\n\ttry {\n\t\tcontent = readBoundedTextFileSync(filePath, MAX_CURSOR_BYTES, \"Orchestration cursor\");\n\t} catch (error) {\n\t\tthrow new OrchestrationEventStoreError(\n\t\t\t`Failed to read orchestration cursor: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(content);\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\tconst record = parsed as Record<string, unknown>;\n\t\tif (record.version !== 1 || !Number.isSafeInteger(record.lastOrdinal) || Number(record.lastOrdinal) < 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (record.tailBytes !== undefined && (!Number.isSafeInteger(record.tailBytes) || Number(record.tailBytes) < 0)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tlastOrdinal: Number(record.lastOrdinal),\n\t\t\t...(record.tailBytes !== undefined ? { tailBytes: Number(record.tailBytes) } : {}),\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction serialize(value: unknown): string {\n\treturn `${JSON.stringify(value, null, \"\\t\")}\\n`;\n}\n\nfunction serializeBounded(\n\tvalue: unknown,\n\tmaxBytes: number,\n\tlabel: string,\n\toversizedMessage = `${label} exceeds its byte limit.`,\n): string {\n\tconst serialized = serialize(value);\n\tif (Buffer.byteLength(serialized, \"utf-8\") > maxBytes) {\n\t\tthrow new OrchestrationEventStoreError(oversizedMessage);\n\t}\n\treturn serialized;\n}\n\nfunction eventFileName(ordinal: number): string {\n\treturn `${String(ordinal).padStart(16, \"0\")}.json`;\n}\n\nfunction snapshotDigest(content: ProjectionSnapshotContent): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(content)).digest(\"hex\");\n}\n\nfunction positiveSafeInteger(value: number | undefined, fallback: number, maximum: number, label: string): number {\n\tconst resolved = value ?? fallback;\n\tif (!Number.isSafeInteger(resolved) || resolved < 1) {\n\t\tthrow new OrchestrationEventStoreError(`${label} must be a positive safe integer.`);\n\t}\n\tif (resolved > maximum) throw new OrchestrationEventStoreError(`${label} must not exceed ${maximum}.`);\n\treturn resolved;\n}\n\n/**\n * Append-only event tail with a replaceable full-state projection snapshot. Atomic rename prevents\n * torn records. Snapshot publication is two-phase (payload, then small baseline pointer), after which\n * covered event/idempotency files are pruned. A crash at any point leaves either the old replay prefix\n * or the new verified snapshot authoritative; ordinals never reset.\n */\nexport class OrchestrationEventStore {\n\treadonly rootDir: string;\n\treadonly eventsDir: string;\n\treadonly idempotencyDir: string;\n\treadonly cursorPath: string;\n\treadonly snapshotsDir: string;\n\treadonly baselinePath: string;\n\tprivate readonly now: () => string;\n\tprivate readonly createEventId: () => string;\n\tprivate readonly maxTailEvents: number;\n\tprivate readonly maxTailBytes: number;\n\tprivate readonly maxIdempotencyEvents: number;\n\tprivate readonly listeners = new Set<(event: OrchestrationEvent) => void>();\n\tprivate verifiedSnapshotBaseline: VerifiedSnapshotBaseline | undefined;\n\n\tconstructor(options: OrchestrationEventStoreOptions) {\n\t\tthis.rootDir = orchestrationEventStoreDir(options.agentDir, options.sessionId);\n\t\tthis.eventsDir = join(this.rootDir, \"events\");\n\t\tthis.idempotencyDir = join(this.rootDir, \"idempotency\");\n\t\tthis.cursorPath = join(this.rootDir, \"cursor.json\");\n\t\tthis.snapshotsDir = join(this.rootDir, \"snapshots\");\n\t\tthis.baselinePath = join(this.rootDir, \"projection-baseline.json\");\n\t\tthis.now = options.now ?? (() => new Date().toISOString());\n\t\tthis.createEventId = options.createEventId ?? randomUUID;\n\t\tthis.maxTailEvents = positiveSafeInteger(\n\t\t\toptions.maxTailEvents,\n\t\t\tDEFAULT_MAX_TAIL_EVENTS,\n\t\t\tMAX_CONFIGURED_TAIL_EVENTS,\n\t\t\t\"maxTailEvents\",\n\t\t);\n\t\tthis.maxTailBytes = positiveSafeInteger(\n\t\t\toptions.maxTailBytes,\n\t\t\tDEFAULT_MAX_TAIL_BYTES,\n\t\t\tMAX_CONFIGURED_TAIL_BYTES,\n\t\t\t\"maxTailBytes\",\n\t\t);\n\t\tthis.maxIdempotencyEvents = positiveSafeInteger(\n\t\t\toptions.maxIdempotencyEvents,\n\t\t\tDEFAULT_MAX_IDEMPOTENCY_EVENTS,\n\t\t\tMAX_CONFIGURED_IDEMPOTENCY_EVENTS,\n\t\t\t\"maxIdempotencyEvents\",\n\t\t);\n\t}\n\n\tappend(input: AppendOrchestrationEventInput, options: { expectedLastOrdinal?: number } = {}): OrchestrationEvent {\n\t\tconst committed = withFileLockSync(this.cursorPath, (): { event: OrchestrationEvent; appended: boolean } => {\n\t\t\tconst cursor = this.synchronizeIndexesUnlocked();\n\t\t\tconst actual = cursor.lastOrdinal;\n\t\t\tif (input.idempotencyKey) {\n\t\t\t\tconst existing = this.readIdempotentEventUnlocked(input.idempotencyKey);\n\t\t\t\tif (existing) return { event: existing, appended: false };\n\t\t\t}\n\t\t\tif (options.expectedLastOrdinal !== undefined) {\n\t\t\t\tif (actual !== options.expectedLastOrdinal) {\n\t\t\t\t\tthrow new OrchestrationConcurrencyError(options.expectedLastOrdinal, actual);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ordinal = actual + 1;\n\t\t\tconst next: OrchestrationEvent = {\n\t\t\t\tschemaVersion: ORCHESTRATION_SCHEMA_VERSION,\n\t\t\t\tordinal,\n\t\t\t\teventId: this.createEventId(),\n\t\t\t\ttype: input.type,\n\t\t\t\taggregateId: input.aggregateId,\n\t\t\t\tactor: input.actor,\n\t\t\t\toccurredAt: this.now(),\n\t\t\t\t...(input.correlationId ? { correlationId: input.correlationId } : {}),\n\t\t\t\t...(input.causationId ? { causationId: input.causationId } : {}),\n\t\t\t\t...(input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}),\n\t\t\t\tpayload: structuredClone(input.payload),\n\t\t\t};\n\t\t\tconst serializedEvent = serializeBounded(\n\t\t\t\tnext,\n\t\t\t\tthis.maxTailBytes,\n\t\t\t\t\"Orchestration event\",\n\t\t\t\t\"Orchestration event exceeds its configured tail byte limit.\",\n\t\t\t);\n\t\t\tconst serializedMarker = input.idempotencyKey\n\t\t\t\t? this.serializeIdempotencyMarker(input.idempotencyKey, ordinal)\n\t\t\t\t: undefined;\n\t\t\tconst serializedCursor = serializeBounded(\n\t\t\t\t{\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tlastOrdinal: ordinal,\n\t\t\t\t\ttailBytes: cursor.tailBytes + Buffer.byteLength(serializedEvent),\n\t\t\t\t} satisfies EventCursor,\n\t\t\t\tMAX_CURSOR_BYTES,\n\t\t\t\t\"Orchestration cursor\",\n\t\t\t);\n\t\t\twriteFileAtomicSync(join(this.eventsDir, eventFileName(ordinal)), serializedEvent);\n\t\t\tif (input.idempotencyKey && serializedMarker) {\n\t\t\t\tthis.writeIdempotencyMarkerUnlocked(input.idempotencyKey, serializedMarker);\n\t\t\t}\n\t\t\twriteFileAtomicSync(this.cursorPath, serializedCursor);\n\t\t\treturn { event: next, appended: true };\n\t\t});\n\n\t\tif (committed.appended) {\n\t\t\tfor (const listener of this.listeners) listener(structuredClone(committed.event));\n\t\t}\n\t\treturn structuredClone(committed.event);\n\t}\n\n\treadAll(): OrchestrationEvent[] {\n\t\treturn this.readAllUnlocked();\n\t}\n\n\treadAfter(ordinal: number): OrchestrationEvent[] {\n\t\tconst baseline = this.readBaselineUnlocked();\n\t\tif (baseline && ordinal < baseline.throughOrdinal) {\n\t\t\tthrow new OrchestrationSnapshotRequiredError(baseline.throughOrdinal);\n\t\t}\n\t\tconst events: OrchestrationEvent[] = [];\n\t\tconst names = this.eventFileNamesUnlocked();\n\t\tthis.assertReadableTailUnlocked(names, baseline?.throughOrdinal ?? 0);\n\t\tlet previousOrdinal = ordinal;\n\t\tfor (const name of names) {\n\t\t\tconst match = EVENT_FILE_PATTERN.exec(name);\n\t\t\tif (!match) continue;\n\t\t\tconst fileOrdinal = Number(match[1]);\n\t\t\tif (fileOrdinal <= ordinal) continue;\n\t\t\tconst event = this.readEventFileUnlocked(name);\n\t\t\tif (event.ordinal !== fileOrdinal || event.ordinal <= previousOrdinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(`Non-monotonic orchestration event ordinal in ${name}`);\n\t\t\t}\n\t\t\tpreviousOrdinal = event.ordinal;\n\t\t\tevents.push(structuredClone(event));\n\t\t}\n\t\treturn events;\n\t}\n\n\treadProjectionSnapshot(): { throughOrdinal: number; projection: JsonObject } | undefined {\n\t\tconst snapshot = this.readProjectionSnapshotUnlocked();\n\t\treturn snapshot\n\t\t\t? { throughOrdinal: snapshot.throughOrdinal, projection: structuredClone(snapshot.projection) }\n\t\t\t: undefined;\n\t}\n\n\t/** Replace a large replay prefix with one verified current-state snapshot and a bounded tail. */\n\tcompactIfNeeded(throughOrdinal: number, projection: () => JsonObject): boolean {\n\t\tconst baselineBeforeLock = this.readBaselineUnlocked()?.throughOrdinal ?? 0;\n\t\tconst cursorBeforeLock = parseCursor(this.cursorPath);\n\t\tif (\n\t\t\tcursorBeforeLock?.lastOrdinal === throughOrdinal &&\n\t\t\tcursorBeforeLock.tailBytes !== undefined &&\n\t\t\tthroughOrdinal - baselineBeforeLock < this.maxTailEvents &&\n\t\t\tcursorBeforeLock.tailBytes < this.maxTailBytes\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\treturn withFileLockSync(this.cursorPath, () => {\n\t\t\tconst cursor = this.synchronizeIndexesUnlocked();\n\t\t\tif (cursor.lastOrdinal !== throughOrdinal) return false;\n\t\t\tconst baselineOrdinal = this.readBaselineUnlocked()?.throughOrdinal ?? 0;\n\t\t\tconst tailNames = this.eventFileNamesUnlocked().filter(\n\t\t\t\t(name) => Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0) > baselineOrdinal,\n\t\t\t);\n\t\t\tif (tailNames.length < this.maxTailEvents && cursor.tailBytes < this.maxTailBytes) return false;\n\n\t\t\tconst retainedByKey = new Map<string, OrchestrationEvent>();\n\t\t\tfor (const event of this.readProjectionSnapshotUnlocked()?.idempotencyEvents ?? []) {\n\t\t\t\tif (event.idempotencyKey) retainedByKey.set(event.idempotencyKey, event);\n\t\t\t}\n\t\t\tfor (const name of tailNames) {\n\t\t\t\tconst event = this.readEventFileUnlocked(name);\n\t\t\t\tif (event.ordinal <= throughOrdinal && event.idempotencyKey) {\n\t\t\t\t\tretainedByKey.set(event.idempotencyKey, event);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst idempotencyEvents = [...retainedByKey.values()]\n\t\t\t\t.sort((left, right) => left.ordinal - right.ordinal)\n\t\t\t\t.slice(-this.maxIdempotencyEvents)\n\t\t\t\t.map((event) => structuredClone(event));\n\t\t\tconst content: ProjectionSnapshotContent = {\n\t\t\t\tversion: 1,\n\t\t\t\tschemaVersion: ORCHESTRATION_SCHEMA_VERSION,\n\t\t\t\tthroughOrdinal,\n\t\t\t\tcreatedAt: this.now(),\n\t\t\t\tprojection: structuredClone(projection()),\n\t\t\t\tidempotencyEvents,\n\t\t\t};\n\t\t\tconst digest = snapshotDigest(content);\n\t\t\tconst snapshotFile = eventFileName(throughOrdinal);\n\t\t\tconst serializedSnapshot = serializeBounded(\n\t\t\t\t{ ...content, digest } satisfies ProjectionSnapshot,\n\t\t\t\tMAX_PROJECTION_SNAPSHOT_BYTES,\n\t\t\t\t\"Orchestration projection snapshot\",\n\t\t\t);\n\t\t\tconst serializedBaseline = serializeBounded(\n\t\t\t\t{ version: 1, throughOrdinal, digest, snapshotFile } satisfies SnapshotBaseline,\n\t\t\t\tMAX_BASELINE_BYTES,\n\t\t\t\t\"Orchestration snapshot baseline\",\n\t\t\t);\n\t\t\tconst serializedCursor = serializeBounded(\n\t\t\t\t{ version: 1, lastOrdinal: throughOrdinal, tailBytes: 0 } satisfies EventCursor,\n\t\t\t\tMAX_CURSOR_BYTES,\n\t\t\t\t\"Orchestration cursor\",\n\t\t\t);\n\t\t\twriteFileAtomicSync(join(this.snapshotsDir, snapshotFile), serializedSnapshot);\n\t\t\twriteFileAtomicSync(this.baselinePath, serializedBaseline);\n\n\t\t\tfor (const name of this.idempotencyFileNamesUnlocked()) {\n\t\t\t\tthis.unlinkManagedFile(join(this.idempotencyDir, name));\n\t\t\t}\n\t\t\tfor (const name of this.eventFileNamesUnlocked()) {\n\t\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\t\tif (ordinal <= throughOrdinal) this.unlinkManagedFile(join(this.eventsDir, name));\n\t\t\t}\n\t\t\tfor (const name of this.snapshotFileNamesUnlocked()) {\n\t\t\t\tif (name !== snapshotFile) this.unlinkManagedFile(join(this.snapshotsDir, name));\n\t\t\t}\n\t\t\twriteFileAtomicSync(this.cursorPath, serializedCursor);\n\t\t\treturn true;\n\t\t});\n\t}\n\n\tsubscribe(listener: (event: OrchestrationEvent) => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\tprivate readAllUnlocked(): OrchestrationEvent[] {\n\t\tconst names = this.eventFileNamesUnlocked();\n\t\tconst events: OrchestrationEvent[] = [];\n\t\tconst baselineOrdinal = this.readProjectionSnapshotUnlocked()?.throughOrdinal ?? 0;\n\t\tthis.assertReadableTailUnlocked(names, baselineOrdinal);\n\t\tlet previousOrdinal = baselineOrdinal;\n\t\tfor (const name of names) {\n\t\t\tconst match = EVENT_FILE_PATTERN.exec(name);\n\t\t\tif (!match) continue;\n\t\t\tconst fileOrdinal = Number(match[1]);\n\t\t\tif (fileOrdinal <= baselineOrdinal) continue;\n\t\t\tconst parsed = this.readEventFileUnlocked(name);\n\t\t\tif (parsed.ordinal !== fileOrdinal || parsed.ordinal <= previousOrdinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(`Non-monotonic orchestration event ordinal in ${name}`);\n\t\t\t}\n\t\t\tpreviousOrdinal = parsed.ordinal;\n\t\t\tevents.push(structuredClone(parsed));\n\t\t}\n\t\treturn events;\n\t}\n\n\tprivate eventFileNamesUnlocked(): string[] {\n\t\ttry {\n\t\t\treturn readBoundedDirectoryNamesSync(\n\t\t\t\tthis.eventsDir,\n\t\t\t\tMAX_EVENT_DIRECTORY_ENTRIES,\n\t\t\t\t\"Orchestration events directory\",\n\t\t\t)\n\t\t\t\t.filter((candidate) => EVENT_FILE_PATTERN.test(candidate))\n\t\t\t\t.sort();\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate idempotencyFileNamesUnlocked(): string[] {\n\t\ttry {\n\t\t\treturn readBoundedDirectoryNamesSync(\n\t\t\t\tthis.idempotencyDir,\n\t\t\t\tMAX_IDEMPOTENCY_DIRECTORY_ENTRIES,\n\t\t\t\t\"Orchestration idempotency directory\",\n\t\t\t).filter((candidate) => candidate.endsWith(\".json\"));\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate snapshotFileNamesUnlocked(): string[] {\n\t\ttry {\n\t\t\treturn readBoundedDirectoryNamesSync(\n\t\t\t\tthis.snapshotsDir,\n\t\t\t\tMAX_SNAPSHOT_DIRECTORY_ENTRIES,\n\t\t\t\t\"Orchestration snapshots directory\",\n\t\t\t)\n\t\t\t\t.filter((candidate) => SNAPSHOT_FILE_PATTERN.test(candidate))\n\t\t\t\t.sort();\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/** Event files are a contiguous append-only tail after the published snapshot. */\n\tprivate assertContiguousTailOrdinalsUnlocked(names: readonly string[], baselineOrdinal: number): void {\n\t\tlet expectedOrdinal = baselineOrdinal + 1;\n\t\tfor (const name of names) {\n\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\t// A crash after publishing the baseline but before pruning is safe: the snapshot covers these files.\n\t\t\tif (ordinal <= baselineOrdinal) continue;\n\t\t\tif (ordinal !== expectedOrdinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t\t`Missing orchestration event ordinal ${expectedOrdinal} before ${name}.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\texpectedOrdinal++;\n\t\t}\n\t}\n\n\t/** A valid cursor may lag a newly published snapshot, but it must never lead its committed tail. */\n\tprivate assertReadableTailUnlocked(names: readonly string[], baselineOrdinal: number): void {\n\t\tthis.assertContiguousTailOrdinalsUnlocked(names, baselineOrdinal);\n\t\tconst highestEventOrdinal = names.length > 0 ? Number(EVENT_FILE_PATTERN.exec(names.at(-1)!)?.[1] ?? 0) : 0;\n\t\tconst highestCommittedOrdinal = Math.max(baselineOrdinal, highestEventOrdinal);\n\t\tconst cursor = parseCursor(this.cursorPath);\n\t\tif (cursor && cursor.lastOrdinal > highestCommittedOrdinal) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Orchestration cursor ${cursor.lastOrdinal} is ahead of the last committed event ${highestCommittedOrdinal}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate readBaselineUnlocked(): SnapshotBaseline | undefined {\n\t\tif (!existsSync(this.baselinePath)) return undefined;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(\n\t\t\t\treadBoundedTextFileSync(this.baselinePath, MAX_BASELINE_BYTES, \"Orchestration snapshot baseline\"),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration snapshot baseline: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration snapshot baseline.\");\n\t\t}\n\t\tconst baseline = parsed as Record<string, unknown>;\n\t\tif (\n\t\t\tbaseline.version !== 1 ||\n\t\t\t!Number.isSafeInteger(baseline.throughOrdinal) ||\n\t\t\tNumber(baseline.throughOrdinal) < 1 ||\n\t\t\ttypeof baseline.digest !== \"string\" ||\n\t\t\tbaseline.digest.length === 0 ||\n\t\t\ttypeof baseline.snapshotFile !== \"string\" ||\n\t\t\t!SNAPSHOT_FILE_PATTERN.test(baseline.snapshotFile) ||\n\t\t\tNumber(SNAPSHOT_FILE_PATTERN.exec(baseline.snapshotFile)?.[1] ?? 0) !== Number(baseline.throughOrdinal)\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration snapshot baseline.\");\n\t\t}\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tthroughOrdinal: Number(baseline.throughOrdinal),\n\t\t\tdigest: baseline.digest,\n\t\t\tsnapshotFile: baseline.snapshotFile,\n\t\t};\n\t}\n\n\tprivate readProjectionSnapshotUnlocked(): ProjectionSnapshot | undefined {\n\t\tconst baseline = this.readBaselineUnlocked();\n\t\tif (!baseline) return undefined;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(\n\t\t\t\treadBoundedTextFileSync(\n\t\t\t\t\tjoin(this.snapshotsDir, baseline.snapshotFile),\n\t\t\t\t\tMAX_PROJECTION_SNAPSHOT_BYTES,\n\t\t\t\t\t\"Orchestration projection snapshot\",\n\t\t\t\t),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration projection snapshot: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration projection snapshot.\");\n\t\t}\n\t\tconst snapshot = parsed as Record<string, unknown>;\n\t\tif (\n\t\t\tsnapshot.version !== 1 ||\n\t\t\tsnapshot.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION ||\n\t\t\tsnapshot.throughOrdinal !== baseline.throughOrdinal ||\n\t\t\ttypeof snapshot.createdAt !== \"string\" ||\n\t\t\t!snapshot.projection ||\n\t\t\ttypeof snapshot.projection !== \"object\" ||\n\t\t\tArray.isArray(snapshot.projection) ||\n\t\t\t!Array.isArray(snapshot.idempotencyEvents) ||\n\t\t\tsnapshot.idempotencyEvents.length > this.maxIdempotencyEvents ||\n\t\t\t!snapshot.idempotencyEvents.every(isOrchestrationEvent) ||\n\t\t\t!snapshot.idempotencyEvents.every(\n\t\t\t\t(event) => event.ordinal <= baseline.throughOrdinal && event.idempotencyKey !== undefined,\n\t\t\t) ||\n\t\t\tnew Set(snapshot.idempotencyEvents.map((event) => event.idempotencyKey)).size !==\n\t\t\t\tsnapshot.idempotencyEvents.length ||\n\t\t\ttypeof snapshot.digest !== \"string\" ||\n\t\t\tsnapshot.digest !== baseline.digest\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration projection snapshot.\");\n\t\t}\n\t\tconst content: ProjectionSnapshotContent = {\n\t\t\tversion: 1,\n\t\t\tschemaVersion: ORCHESTRATION_SCHEMA_VERSION,\n\t\t\tthroughOrdinal: baseline.throughOrdinal,\n\t\t\tcreatedAt: snapshot.createdAt,\n\t\t\tprojection: structuredClone(snapshot.projection) as JsonObject,\n\t\t\tidempotencyEvents: snapshot.idempotencyEvents.map((event) => structuredClone(event)),\n\t\t};\n\t\tif (snapshotDigest(content) !== baseline.digest) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration projection snapshot digest mismatch.\");\n\t\t}\n\t\treturn { ...content, digest: baseline.digest };\n\t}\n\n\t/**\n\t * Appends need a verified snapshot baseline but must not deserialize its projection on every event.\n\t * The baseline pointer remains small and is reread each time; an immutable snapshot is reparsed only\n\t * when its pointer or filesystem identity changes.\n\t */\n\tprivate readVerifiedSnapshotBaselineUnlocked(): SnapshotBaseline | undefined {\n\t\tconst baseline = this.readBaselineUnlocked();\n\t\tif (!baseline) {\n\t\t\tthis.verifiedSnapshotBaseline = undefined;\n\t\t\treturn undefined;\n\t\t}\n\t\tconst signature = this.snapshotFileSignatureUnlocked(baseline);\n\t\tconst cached = this.verifiedSnapshotBaseline;\n\t\tif (\n\t\t\tcached &&\n\t\t\tcached.baseline.throughOrdinal === baseline.throughOrdinal &&\n\t\t\tcached.baseline.digest === baseline.digest &&\n\t\t\tcached.baseline.snapshotFile === baseline.snapshotFile &&\n\t\t\tcached.signature.size === signature.size &&\n\t\t\tcached.signature.mtimeMs === signature.mtimeMs &&\n\t\t\tcached.signature.ctimeMs === signature.ctimeMs\n\t\t) {\n\t\t\treturn baseline;\n\t\t}\n\t\tthis.readProjectionSnapshotUnlocked();\n\t\tconst verifiedSignature = this.snapshotFileSignatureUnlocked(baseline);\n\t\tif (\n\t\t\tverifiedSignature.size !== signature.size ||\n\t\t\tverifiedSignature.mtimeMs !== signature.mtimeMs ||\n\t\t\tverifiedSignature.ctimeMs !== signature.ctimeMs\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration projection snapshot changed while being verified.\");\n\t\t}\n\t\tthis.verifiedSnapshotBaseline = { baseline, signature };\n\t\treturn baseline;\n\t}\n\n\tprivate snapshotFileSignatureUnlocked(baseline: SnapshotBaseline): SnapshotFileSignature {\n\t\ttry {\n\t\t\tconst stats = statSync(join(this.snapshotsDir, baseline.snapshotFile));\n\t\t\tif (!stats.isFile())\n\t\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration projection snapshot is not a file.\");\n\t\t\treturn { size: stats.size, mtimeMs: stats.mtimeMs, ctimeMs: stats.ctimeMs };\n\t\t} catch (error) {\n\t\t\tif (error instanceof OrchestrationEventStoreError) throw error;\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration projection snapshot: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate unlinkManagedFile(filePath: string): void {\n\t\ttry {\n\t\t\tunlinkSync(filePath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t}\n\t}\n\n\tprivate readEventFileUnlocked(name: string): OrchestrationEvent {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(\n\t\t\t\treadBoundedTextFileSync(join(this.eventsDir, name), this.maxTailBytes, `Orchestration event ${name}`),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration event ${name}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOrchestrationEvent(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(`Invalid orchestration event record: ${name}`);\n\t\t}\n\t\treturn parsed;\n\t}\n\n\tprivate idempotencyPath(key: string): string {\n\t\treturn join(this.idempotencyDir, `${createHash(\"sha256\").update(key).digest(\"hex\")}.json`);\n\t}\n\n\tprivate serializeIdempotencyMarker(key: string, ordinal: number): string {\n\t\treturn serializeBounded(\n\t\t\t{ version: 1, key, ordinal } satisfies IdempotencyMarker,\n\t\t\tMAX_IDEMPOTENCY_MARKER_BYTES,\n\t\t\t\"Orchestration idempotency marker\",\n\t\t);\n\t}\n\n\tprivate writeIdempotencyMarkerUnlocked(key: string, serializedMarker: string): void {\n\t\twriteFileAtomicSync(this.idempotencyPath(key), serializedMarker);\n\t}\n\n\tprivate readIdempotentEventUnlocked(key: string): OrchestrationEvent | undefined {\n\t\tconst markerPath = this.idempotencyPath(key);\n\t\tif (!existsSync(markerPath)) {\n\t\t\treturn this.readProjectionSnapshotUnlocked()?.idempotencyEvents.find((event) => event.idempotencyKey === key);\n\t\t}\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(\n\t\t\t\treadBoundedTextFileSync(markerPath, MAX_IDEMPOTENCY_MARKER_BYTES, \"Orchestration idempotency marker\"),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Failed to parse orchestration idempotency marker: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration idempotency marker.\");\n\t\t}\n\t\tconst marker = parsed as Record<string, unknown>;\n\t\tif (\n\t\t\tmarker.version !== 1 ||\n\t\t\tmarker.key !== key ||\n\t\t\t!Number.isSafeInteger(marker.ordinal) ||\n\t\t\tNumber(marker.ordinal) < 1\n\t\t) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Invalid orchestration idempotency marker.\");\n\t\t}\n\t\tconst ordinal = Number(marker.ordinal);\n\t\tif (!existsSync(join(this.eventsDir, eventFileName(ordinal)))) {\n\t\t\tconst retained = this.readProjectionSnapshotUnlocked()?.idempotencyEvents.find(\n\t\t\t\t(event) => event.idempotencyKey === key && event.ordinal === ordinal,\n\t\t\t);\n\t\t\tif (retained) return retained;\n\t\t}\n\t\tconst event = this.readEventFileUnlocked(eventFileName(ordinal));\n\t\tif (event.ordinal !== ordinal || event.idempotencyKey !== key) {\n\t\t\tthrow new OrchestrationEventStoreError(\"Orchestration idempotency marker does not match its event.\");\n\t\t}\n\t\treturn event;\n\t}\n\n\t/**\n\t * Reconcile only the crash tail when an event rename committed before its marker/cursor. Normal\n\t * appends read directory metadata plus one marker, never the accumulated event payload prefix.\n\t */\n\tprivate synchronizeIndexesUnlocked(): SynchronizedIndexes {\n\t\tconst names = this.eventFileNamesUnlocked();\n\t\tconst baselineOrdinal = this.readVerifiedSnapshotBaselineUnlocked()?.throughOrdinal ?? 0;\n\t\tthis.assertReadableTailUnlocked(names, baselineOrdinal);\n\t\tconst highestEventOrdinal = names.length > 0 ? Number(EVENT_FILE_PATTERN.exec(names.at(-1)!)?.[1] ?? 0) : 0;\n\t\tconst highest = Math.max(baselineOrdinal, highestEventOrdinal);\n\t\tconst cursor = parseCursor(this.cursorPath);\n\t\tif (\n\t\t\tcursor?.lastOrdinal === highest &&\n\t\t\tcursor.tailBytes !== undefined &&\n\t\t\t(highest > baselineOrdinal || cursor.tailBytes === 0)\n\t\t) {\n\t\t\treturn { lastOrdinal: highest, tailBytes: cursor.tailBytes };\n\t\t}\n\t\tif (cursor && cursor.lastOrdinal > highest) {\n\t\t\tthrow new OrchestrationEventStoreError(\n\t\t\t\t`Orchestration cursor ${cursor.lastOrdinal} is ahead of the last committed event ${highest}.`,\n\t\t\t);\n\t\t}\n\t\tconst rebuildFrom = Math.max(\n\t\t\tbaselineOrdinal + 1,\n\t\t\tcursor && cursor.lastOrdinal >= baselineOrdinal && cursor.lastOrdinal <= highest\n\t\t\t\t? cursor.lastOrdinal + 1\n\t\t\t\t: baselineOrdinal + 1,\n\t\t);\n\t\tfor (const name of names) {\n\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\tif (ordinal < rebuildFrom || ordinal <= baselineOrdinal) continue;\n\t\t\tconst event = this.readEventFileUnlocked(name);\n\t\t\tif (event.ordinal !== ordinal) {\n\t\t\t\tthrow new OrchestrationEventStoreError(`Event ordinal does not match file name: ${name}`);\n\t\t\t}\n\t\t\tif (event.idempotencyKey) {\n\t\t\t\tthis.writeIdempotencyMarkerUnlocked(\n\t\t\t\t\tevent.idempotencyKey,\n\t\t\t\t\tthis.serializeIdempotencyMarker(event.idempotencyKey, ordinal),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tconst tailBytes = names.reduce((total, name) => {\n\t\t\tconst ordinal = Number(EVENT_FILE_PATTERN.exec(name)?.[1] ?? 0);\n\t\t\tif (ordinal <= baselineOrdinal) return total;\n\t\t\ttry {\n\t\t\t\treturn total + statSync(join(this.eventsDir, name)).size;\n\t\t\t} catch {\n\t\t\t\treturn total;\n\t\t\t}\n\t\t}, 0);\n\t\twriteFileAtomicSync(\n\t\t\tthis.cursorPath,\n\t\t\tserializeBounded(\n\t\t\t\t{ version: 1, lastOrdinal: highest, tailBytes } satisfies EventCursor,\n\t\t\t\tMAX_CURSOR_BYTES,\n\t\t\t\t\"Orchestration cursor\",\n\t\t\t),\n\t\t);\n\t\treturn { lastOrdinal: highest, tailBytes };\n\t}\n}\n"]}