effect-agent 0.1.0-beta.14 → 0.1.0-beta.142

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 (532) hide show
  1. package/dist/Admin-DGZXE5BU.d.mts +3237 -0
  2. package/dist/Agent-1DMSO_Fl.mjs +86 -0
  3. package/dist/Agent-1DMSO_Fl.mjs.map +1 -0
  4. package/dist/Agent-BWhD4Dvu.d.mts +255 -0
  5. package/dist/AgentError-Bn1ytXRD.d.mts +117 -0
  6. package/dist/AgentPolicy-BqDoSXO6.d.mts +96 -0
  7. package/dist/AgentRuntime-B4kIzmSF.d.mts +346 -0
  8. package/dist/AgentRuntime-xeCUXutN.mjs +4740 -0
  9. package/dist/AgentRuntime-xeCUXutN.mjs.map +1 -0
  10. package/dist/AgentUpdates-DMJ-_xow.d.mts +53 -0
  11. package/dist/ContextCompactor-WwSIG_Z2.d.mts +161 -0
  12. package/dist/ContextWindow-DRQ5Mbm2.d.mts +58 -0
  13. package/dist/Digest-U69rDHuO.d.mts +22 -0
  14. package/dist/DurableAgentRuntime-DQXDcPKQ.mjs +7165 -0
  15. package/dist/DurableAgentRuntime-DQXDcPKQ.mjs.map +1 -0
  16. package/dist/DurableFailpoint-Cnr3wx1L.d.mts +41 -0
  17. package/dist/DurableStep-Cry_iol4.d.mts +103 -0
  18. package/dist/EventSource-Cg3IeJa6.d.mts +51 -0
  19. package/dist/FailureDiagnostic-BElLOoED.mjs +274 -0
  20. package/dist/FailureDiagnostic-BElLOoED.mjs.map +1 -0
  21. package/dist/FailureDiagnostic-CaAkzGHL.d.mts +223 -0
  22. package/dist/Identifiers-CN2s30fF.d.mts +40 -0
  23. package/dist/InMemory.d.mts +24 -0
  24. package/dist/InMemory.mjs +24 -0
  25. package/dist/InMemory.mjs.map +1 -0
  26. package/dist/MemoryNamespace-BbRW5p71.d.mts +36 -0
  27. package/dist/MemoryReference-B8Q3UJoJ.d.mts +79 -0
  28. package/dist/Messaging-Dkg87ebg.d.mts +221 -0
  29. package/dist/Messaging-Dnmk31yq.d.mts +1067 -0
  30. package/dist/MessagingHost-CajpPDPG.d.mts +74 -0
  31. package/dist/MessagingHost-DfsbMPvq.mjs +110 -0
  32. package/dist/MessagingHost-DfsbMPvq.mjs.map +1 -0
  33. package/dist/OperationAuthorizer-TzcQtpX5.d.mts +61 -0
  34. package/dist/Output-DhtWbBcw.d.mts +25 -0
  35. package/dist/PageCapture-Df8Xwskw.d.mts +583 -0
  36. package/dist/PreparedInputAdmission-CLvaruRe.d.mts +42 -0
  37. package/dist/Receipt-BAHyigAF.d.mts +33 -0
  38. package/dist/Records-B5GFWCpn.d.mts +1104 -0
  39. package/dist/RunEvent-DIrzkjLr.d.mts +460 -0
  40. package/dist/RunEventSink-D5aVhISG.d.mts +108 -0
  41. package/dist/RunJournal-Gd83HYqG.mjs +954 -0
  42. package/dist/RunJournal-Gd83HYqG.mjs.map +1 -0
  43. package/dist/RunOptions-IlEXLG5F.d.mts +935 -0
  44. package/dist/RunPolicyUsage-ClZxyR46.d.mts +21 -0
  45. package/dist/Sandbox-CEsDWBLq.d.mts +173 -0
  46. package/dist/Schedule-CtYREFnz.d.mts +847 -0
  47. package/dist/Scheduling-DPb2QyAK.mjs +691 -0
  48. package/dist/Scheduling-DPb2QyAK.mjs.map +1 -0
  49. package/dist/Subagent-8RzJkqJ9.mjs +1502 -0
  50. package/dist/Subagent-8RzJkqJ9.mjs.map +1 -0
  51. package/dist/Subagent-Cl8D1mFM.d.mts +1652 -0
  52. package/dist/SubagentContract-DdT7Cqe6.d.mts +116 -0
  53. package/dist/SubagentHost-B6f4PBC2.d.mts +172 -0
  54. package/dist/Subscription-_VTIVN8o.d.mts +979 -0
  55. package/dist/SubscriptionInput-BIH1f3Th.d.mts +41 -0
  56. package/dist/Thread-DOBQ0QUY.d.mts +96 -0
  57. package/dist/ThreadHistory-tS1KwxHR.d.mts +60 -0
  58. package/dist/ToolBroker-DWs4tZra.d.mts +133 -0
  59. package/dist/ToolExposure-9uF_Yo0T.mjs +10 -0
  60. package/dist/ToolExposure-9uF_Yo0T.mjs.map +1 -0
  61. package/dist/ToolExposure-C93U1sPu.mjs +30 -0
  62. package/dist/ToolExposure-C93U1sPu.mjs.map +1 -0
  63. package/dist/ToolExposure-UCN12eCd.d.mts +39 -0
  64. package/dist/ToolExposure-r2eOWk4F.d.mts +46 -0
  65. package/dist/ToolExposure.d.mts +8 -0
  66. package/dist/ToolExposure.mjs +21 -0
  67. package/dist/ToolExposure.mjs.map +1 -0
  68. package/dist/ToolReconciler-BmVRAHKs.d.mts +80 -0
  69. package/dist/ToolResult-BKdqWRff.d.mts +78 -0
  70. package/dist/Usage-Dx0aknfk.d.mts +121 -0
  71. package/dist/WakeScheduler-EjUF-GhC.d.mts +56 -0
  72. package/dist/Worker-CfwzMZEK.mjs +271 -0
  73. package/dist/Worker-CfwzMZEK.mjs.map +1 -0
  74. package/dist/Worker-fwWkBg12.d.mts +472 -0
  75. package/dist/agent-registration-CKNsrpwo.mjs +201 -0
  76. package/dist/agent-registration-CKNsrpwo.mjs.map +1 -0
  77. package/dist/capabilities/Approval.d.mts +114 -0
  78. package/dist/capabilities/Approval.mjs +226 -0
  79. package/dist/capabilities/Approval.mjs.map +1 -0
  80. package/dist/capabilities/Budget.d.mts +112 -0
  81. package/dist/capabilities/Budget.mjs +346 -0
  82. package/dist/capabilities/Budget.mjs.map +1 -0
  83. package/dist/capabilities/CodeMode.d.mts +157 -0
  84. package/dist/capabilities/CodeMode.mjs +523 -0
  85. package/dist/capabilities/CodeMode.mjs.map +1 -0
  86. package/dist/capabilities/Commands.d.mts +55 -0
  87. package/dist/capabilities/Commands.mjs +57 -0
  88. package/dist/capabilities/Commands.mjs.map +1 -0
  89. package/dist/capabilities/ContextTools.d.mts +321 -0
  90. package/dist/capabilities/ContextTools.mjs +126 -0
  91. package/dist/capabilities/ContextTools.mjs.map +1 -0
  92. package/dist/capabilities/Mcp.d.mts +108 -0
  93. package/dist/capabilities/Mcp.mjs +259 -0
  94. package/dist/capabilities/Mcp.mjs.map +1 -0
  95. package/dist/capabilities/McpClient.d.mts +115 -0
  96. package/dist/capabilities/McpClient.mjs +519 -0
  97. package/dist/capabilities/McpClient.mjs.map +1 -0
  98. package/dist/capabilities/MemoryNotes.d.mts +424 -0
  99. package/dist/capabilities/MemoryNotes.mjs +128 -0
  100. package/dist/capabilities/MemoryNotes.mjs.map +1 -0
  101. package/dist/capabilities/Messaging.d.mts +3 -0
  102. package/dist/capabilities/Messaging.mjs +152 -0
  103. package/dist/capabilities/Messaging.mjs.map +1 -0
  104. package/dist/capabilities/ModelContext.d.mts +89 -0
  105. package/dist/capabilities/ModelContext.mjs +168 -0
  106. package/dist/capabilities/ModelContext.mjs.map +1 -0
  107. package/dist/capabilities/Redaction.d.mts +37 -0
  108. package/dist/capabilities/Redaction.mjs +104 -0
  109. package/dist/capabilities/Redaction.mjs.map +1 -0
  110. package/dist/capabilities/Remembering.d.mts +434 -0
  111. package/dist/capabilities/Remembering.mjs +290 -0
  112. package/dist/capabilities/Remembering.mjs.map +1 -0
  113. package/dist/capabilities/RunHooks.d.mts +82 -0
  114. package/dist/capabilities/RunHooks.mjs +144 -0
  115. package/dist/capabilities/RunHooks.mjs.map +1 -0
  116. package/dist/capabilities/SemanticMemory.d.mts +431 -0
  117. package/dist/capabilities/SemanticMemory.mjs +315 -0
  118. package/dist/capabilities/SemanticMemory.mjs.map +1 -0
  119. package/dist/capabilities/Subagent.d.mts +4 -0
  120. package/dist/capabilities/Subagent.mjs +5 -0
  121. package/dist/capabilities/SubagentReservations.d.mts +186 -0
  122. package/dist/capabilities/SubagentReservations.mjs +479 -0
  123. package/dist/capabilities/SubagentReservations.mjs.map +1 -0
  124. package/dist/capabilities/ToolDiscovery.d.mts +124 -0
  125. package/dist/capabilities/ToolDiscovery.mjs +230 -0
  126. package/dist/capabilities/ToolDiscovery.mjs.map +1 -0
  127. package/dist/capabilities/WebCapture.d.mts +163 -0
  128. package/dist/capabilities/WebCapture.mjs +335 -0
  129. package/dist/capabilities/WebCapture.mjs.map +1 -0
  130. package/dist/capabilities/WebSearch.d.mts +71 -0
  131. package/dist/capabilities/WebSearch.mjs +115 -0
  132. package/dist/capabilities/WebSearch.mjs.map +1 -0
  133. package/dist/compaction-C2OTceKw.mjs +505 -0
  134. package/dist/compaction-C2OTceKw.mjs.map +1 -0
  135. package/dist/core/Agent.d.mts +2 -0
  136. package/dist/core/Agent.mjs +2 -0
  137. package/dist/core/AgentError.d.mts +2 -0
  138. package/dist/core/AgentError.mjs +134 -0
  139. package/dist/core/AgentError.mjs.map +1 -0
  140. package/dist/core/AgentPolicy.d.mts +2 -0
  141. package/dist/core/AgentPolicy.mjs +94 -0
  142. package/dist/core/AgentPolicy.mjs.map +1 -0
  143. package/dist/core/AgentUpdates.d.mts +2 -0
  144. package/dist/core/AgentUpdates.mjs +63 -0
  145. package/dist/core/AgentUpdates.mjs.map +1 -0
  146. package/dist/core/FailureDiagnostic.d.mts +2 -0
  147. package/dist/core/FailureDiagnostic.mjs +2 -0
  148. package/dist/core/IdGenerator.d.mts +26 -0
  149. package/dist/core/IdGenerator.mjs +27 -0
  150. package/dist/core/IdGenerator.mjs.map +1 -0
  151. package/dist/core/Identifiers.d.mts +2 -0
  152. package/dist/core/Identifiers.mjs +43 -0
  153. package/dist/core/Identifiers.mjs.map +1 -0
  154. package/dist/core/Memory.d.mts +57 -0
  155. package/dist/core/Memory.mjs +203 -0
  156. package/dist/core/Memory.mjs.map +1 -0
  157. package/dist/core/MemoryNamespace.d.mts +2 -0
  158. package/dist/core/MemoryNamespace.mjs +116 -0
  159. package/dist/core/MemoryNamespace.mjs.map +1 -0
  160. package/dist/core/MemoryReference.d.mts +2 -0
  161. package/dist/core/MemoryReference.mjs +109 -0
  162. package/dist/core/MemoryReference.mjs.map +1 -0
  163. package/dist/core/MemoryRevalidation.d.mts +65 -0
  164. package/dist/core/MemoryRevalidation.mjs +126 -0
  165. package/dist/core/MemoryRevalidation.mjs.map +1 -0
  166. package/dist/core/MemoryStore.d.mts +186 -0
  167. package/dist/core/MemoryStore.mjs +243 -0
  168. package/dist/core/MemoryStore.mjs.map +1 -0
  169. package/dist/core/Receipt.d.mts +2 -0
  170. package/dist/core/Receipt.mjs +36 -0
  171. package/dist/core/Receipt.mjs.map +1 -0
  172. package/dist/core/RememberingStore.d.mts +1562 -0
  173. package/dist/core/RememberingStore.mjs +190 -0
  174. package/dist/core/RememberingStore.mjs.map +1 -0
  175. package/dist/core/RunEvent.d.mts +2 -0
  176. package/dist/core/RunEvent.mjs +337 -0
  177. package/dist/core/RunEvent.mjs.map +1 -0
  178. package/dist/core/RunPolicyUsage.d.mts +2 -0
  179. package/dist/core/RunPolicyUsage.mjs +21 -0
  180. package/dist/core/RunPolicyUsage.mjs.map +1 -0
  181. package/dist/core/SemanticMemoryIndex.d.mts +826 -0
  182. package/dist/core/SemanticMemoryIndex.mjs +185 -0
  183. package/dist/core/SemanticMemoryIndex.mjs.map +1 -0
  184. package/dist/core/SemanticMemoryRevalidation.d.mts +45 -0
  185. package/dist/core/SemanticMemoryRevalidation.mjs +191 -0
  186. package/dist/core/SemanticMemoryRevalidation.mjs.map +1 -0
  187. package/dist/core/SubagentContract.d.mts +2 -0
  188. package/dist/core/SubagentContract.mjs +148 -0
  189. package/dist/core/SubagentContract.mjs.map +1 -0
  190. package/dist/core/Thread.d.mts +2 -0
  191. package/dist/core/Thread.mjs +250 -0
  192. package/dist/core/Thread.mjs.map +1 -0
  193. package/dist/core/ToolResult.d.mts +2 -0
  194. package/dist/core/ToolResult.mjs +156 -0
  195. package/dist/core/ToolResult.mjs.map +1 -0
  196. package/dist/core/Usage.d.mts +2 -0
  197. package/dist/core/Usage.mjs +319 -0
  198. package/dist/core/Usage.mjs.map +1 -0
  199. package/dist/core/Worker.d.mts +3 -0
  200. package/dist/core/Worker.mjs +3 -0
  201. package/dist/durable/ActivityStore.d.mts +116 -0
  202. package/dist/durable/ActivityStore.mjs +131 -0
  203. package/dist/durable/ActivityStore.mjs.map +1 -0
  204. package/dist/durable/Admin.d.mts +2 -0
  205. package/dist/durable/Admin.mjs +330 -0
  206. package/dist/durable/Admin.mjs.map +1 -0
  207. package/dist/durable/AgentRegistration.d.mts +2 -0
  208. package/dist/durable/AgentRegistration.mjs +14 -0
  209. package/dist/durable/AgentRegistration.mjs.map +1 -0
  210. package/dist/durable/Certification.d.mts +110 -0
  211. package/dist/durable/Certification.mjs +159 -0
  212. package/dist/durable/Certification.mjs.map +1 -0
  213. package/dist/durable/CommittedActivity.d.mts +64 -0
  214. package/dist/durable/CommittedActivity.mjs +159 -0
  215. package/dist/durable/CommittedActivity.mjs.map +1 -0
  216. package/dist/durable/Digest.d.mts +2 -0
  217. package/dist/durable/Digest.mjs +50 -0
  218. package/dist/durable/Digest.mjs.map +1 -0
  219. package/dist/durable/DurableAgentRuntime.d.mts +3 -0
  220. package/dist/durable/DurableAgentRuntime.mjs +3 -0
  221. package/dist/durable/DurableFailpoint.d.mts +2 -0
  222. package/dist/durable/DurableFailpoint.mjs +102 -0
  223. package/dist/durable/DurableFailpoint.mjs.map +1 -0
  224. package/dist/durable/DurableFailpointTestControl.d.mts +14 -0
  225. package/dist/durable/DurableFailpointTestControl.mjs +19 -0
  226. package/dist/durable/DurableFailpointTestControl.mjs.map +1 -0
  227. package/dist/durable/EventSource.d.mts +2 -0
  228. package/dist/durable/EventSource.mjs +65 -0
  229. package/dist/durable/EventSource.mjs.map +1 -0
  230. package/dist/durable/GitHubWorkflowSource.d.mts +185 -0
  231. package/dist/durable/GitHubWorkflowSource.mjs +209 -0
  232. package/dist/durable/GitHubWorkflowSource.mjs.map +1 -0
  233. package/dist/durable/MessageDelivery.d.mts +934 -0
  234. package/dist/durable/MessageDelivery.mjs +557 -0
  235. package/dist/durable/MessageDelivery.mjs.map +1 -0
  236. package/dist/durable/MessageDeliveryStoreConformance.d.mts +400 -0
  237. package/dist/durable/MessageDeliveryStoreConformance.mjs +270 -0
  238. package/dist/durable/MessageDeliveryStoreConformance.mjs.map +1 -0
  239. package/dist/durable/OperationAuthorizer.d.mts +2 -0
  240. package/dist/durable/OperationAuthorizer.mjs +70 -0
  241. package/dist/durable/OperationAuthorizer.mjs.map +1 -0
  242. package/dist/durable/PersistentHistory.d.mts +16 -0
  243. package/dist/durable/PersistentHistory.mjs +125 -0
  244. package/dist/durable/PersistentHistory.mjs.map +1 -0
  245. package/dist/durable/PreparedInputAdmission.d.mts +2 -0
  246. package/dist/durable/PreparedInputAdmission.mjs +38 -0
  247. package/dist/durable/PreparedInputAdmission.mjs.map +1 -0
  248. package/dist/durable/Records.d.mts +2 -0
  249. package/dist/durable/Records.mjs +858 -0
  250. package/dist/durable/Records.mjs.map +1 -0
  251. package/dist/durable/Recovery.d.mts +2 -0
  252. package/dist/durable/Recovery.mjs +739 -0
  253. package/dist/durable/Recovery.mjs.map +1 -0
  254. package/dist/durable/RunJournal.d.mts +2 -0
  255. package/dist/durable/RunJournal.mjs +2 -0
  256. package/dist/durable/Schedule.d.mts +2 -0
  257. package/dist/durable/Schedule.mjs +391 -0
  258. package/dist/durable/Schedule.mjs.map +1 -0
  259. package/dist/durable/ScheduleStoreConformance.d.mts +17 -0
  260. package/dist/durable/ScheduleStoreConformance.mjs +420 -0
  261. package/dist/durable/ScheduleStoreConformance.mjs.map +1 -0
  262. package/dist/durable/ScheduleTransition.d.mts +24 -0
  263. package/dist/durable/ScheduleTransition.mjs +180 -0
  264. package/dist/durable/ScheduleTransition.mjs.map +1 -0
  265. package/dist/durable/Scheduling.d.mts +55 -0
  266. package/dist/durable/Scheduling.mjs +2 -0
  267. package/dist/durable/SqlMemoryStore.d.mts +29 -0
  268. package/dist/durable/SqlMemoryStore.mjs +436 -0
  269. package/dist/durable/SqlMemoryStore.mjs.map +1 -0
  270. package/dist/durable/SubmissionLedger.d.mts +3 -0
  271. package/dist/durable/SubmissionLedger.mjs +986 -0
  272. package/dist/durable/SubmissionLedger.mjs.map +1 -0
  273. package/dist/durable/SubmissionLedgerConformance.d.mts +30 -0
  274. package/dist/durable/SubmissionLedgerConformance.mjs +2254 -0
  275. package/dist/durable/SubmissionLedgerConformance.mjs.map +1 -0
  276. package/dist/durable/SubmissionStatus.d.mts +2 -0
  277. package/dist/durable/SubmissionStatus.mjs +18 -0
  278. package/dist/durable/SubmissionStatus.mjs.map +1 -0
  279. package/dist/durable/Subscription.d.mts +2 -0
  280. package/dist/durable/Subscription.mjs +360 -0
  281. package/dist/durable/Subscription.mjs.map +1 -0
  282. package/dist/durable/SubscriptionInput.d.mts +2 -0
  283. package/dist/durable/SubscriptionInput.mjs +52 -0
  284. package/dist/durable/SubscriptionInput.mjs.map +1 -0
  285. package/dist/durable/SubscriptionStoreConformance.d.mts +16 -0
  286. package/dist/durable/SubscriptionStoreConformance.mjs +625 -0
  287. package/dist/durable/SubscriptionStoreConformance.mjs.map +1 -0
  288. package/dist/durable/SubscriptionTools.d.mts +274 -0
  289. package/dist/durable/SubscriptionTools.mjs +190 -0
  290. package/dist/durable/SubscriptionTools.mjs.map +1 -0
  291. package/dist/durable/SubscriptionTransition.d.mts +21 -0
  292. package/dist/durable/SubscriptionTransition.mjs +148 -0
  293. package/dist/durable/SubscriptionTransition.mjs.map +1 -0
  294. package/dist/durable/Subscriptions.d.mts +74 -0
  295. package/dist/durable/Subscriptions.mjs +627 -0
  296. package/dist/durable/Subscriptions.mjs.map +1 -0
  297. package/dist/durable/ThreadContextHistory.d.mts +32 -0
  298. package/dist/durable/ThreadContextHistory.mjs +152 -0
  299. package/dist/durable/ThreadContextHistory.mjs.map +1 -0
  300. package/dist/durable/ThreadContextHistoryProjection.d.mts +60 -0
  301. package/dist/durable/ThreadContextHistoryProjection.mjs +149 -0
  302. package/dist/durable/ThreadContextHistoryProjection.mjs.map +1 -0
  303. package/dist/durable/ThreadInvariants.d.mts +77 -0
  304. package/dist/durable/ThreadInvariants.mjs +246 -0
  305. package/dist/durable/ThreadInvariants.mjs.map +1 -0
  306. package/dist/durable/ThreadProjection.d.mts +78 -0
  307. package/dist/durable/ThreadProjection.mjs +158 -0
  308. package/dist/durable/ThreadProjection.mjs.map +1 -0
  309. package/dist/durable/ThreadProjectionMaintenance.d.mts +49 -0
  310. package/dist/durable/ThreadProjectionMaintenance.mjs +33 -0
  311. package/dist/durable/ThreadProjectionMaintenance.mjs.map +1 -0
  312. package/dist/durable/ThreadStore.d.mts +2 -0
  313. package/dist/durable/ThreadStore.mjs +383 -0
  314. package/dist/durable/ThreadStore.mjs.map +1 -0
  315. package/dist/durable/ThreadStoreConformance.d.mts +28 -0
  316. package/dist/durable/ThreadStoreConformance.mjs +543 -0
  317. package/dist/durable/ThreadStoreConformance.mjs.map +1 -0
  318. package/dist/durable/ToolReconciler.d.mts +2 -0
  319. package/dist/durable/ToolReconciler.mjs +85 -0
  320. package/dist/durable/ToolReconciler.mjs.map +1 -0
  321. package/dist/durable/WakeScheduler.d.mts +2 -0
  322. package/dist/durable/WakeScheduler.mjs +85 -0
  323. package/dist/durable/WakeScheduler.mjs.map +1 -0
  324. package/dist/durable/WorkerHost.d.mts +136 -0
  325. package/dist/durable/WorkerHost.mjs +77 -0
  326. package/dist/durable/WorkerHost.mjs.map +1 -0
  327. package/dist/engine/AgentRuntime.d.mts +2 -0
  328. package/dist/engine/AgentRuntime.mjs +2 -0
  329. package/dist/engine/Compaction.d.mts +7 -0
  330. package/dist/engine/Compaction.mjs +16 -0
  331. package/dist/engine/Compaction.mjs.map +1 -0
  332. package/dist/engine/ContextCompactor.d.mts +2 -0
  333. package/dist/engine/ContextCompactor.mjs +119 -0
  334. package/dist/engine/ContextCompactor.mjs.map +1 -0
  335. package/dist/engine/ContextHistory.d.mts +65 -0
  336. package/dist/engine/ContextHistory.mjs +76 -0
  337. package/dist/engine/ContextHistory.mjs.map +1 -0
  338. package/dist/engine/ContextWindow.d.mts +2 -0
  339. package/dist/engine/ContextWindow.mjs +59 -0
  340. package/dist/engine/ContextWindow.mjs.map +1 -0
  341. package/dist/engine/DurableStep.d.mts +2 -0
  342. package/dist/engine/DurableStep.mjs +62 -0
  343. package/dist/engine/DurableStep.mjs.map +1 -0
  344. package/dist/engine/MessagingHost.d.mts +2 -0
  345. package/dist/engine/MessagingHost.mjs +2 -0
  346. package/dist/engine/Output.d.mts +2 -0
  347. package/dist/engine/Output.mjs +8 -0
  348. package/dist/engine/Output.mjs.map +1 -0
  349. package/dist/engine/RunEventSink.d.mts +2 -0
  350. package/dist/engine/RunEventSink.mjs +22 -0
  351. package/dist/engine/RunEventSink.mjs.map +1 -0
  352. package/dist/engine/RunOptions.d.mts +2 -0
  353. package/dist/engine/RunOptions.mjs +124 -0
  354. package/dist/engine/RunOptions.mjs.map +1 -0
  355. package/dist/engine/SubagentHost.d.mts +2 -0
  356. package/dist/engine/SubagentHost.mjs +76 -0
  357. package/dist/engine/SubagentHost.mjs.map +1 -0
  358. package/dist/engine/ThreadHistory.d.mts +2 -0
  359. package/dist/engine/ThreadHistory.mjs +67 -0
  360. package/dist/engine/ThreadHistory.mjs.map +1 -0
  361. package/dist/engine/ToolBroker.d.mts +2 -0
  362. package/dist/engine/ToolBroker.mjs +42 -0
  363. package/dist/engine/ToolBroker.mjs.map +1 -0
  364. package/dist/foreign-diagnostic-DBQ1XHKh.mjs +37 -0
  365. package/dist/foreign-diagnostic-DBQ1XHKh.mjs.map +1 -0
  366. package/dist/index.d.mts +91 -6
  367. package/dist/index.mjs +98 -6
  368. package/dist/output-contract-CANJJGA5.mjs +99 -0
  369. package/dist/output-contract-CANJJGA5.mjs.map +1 -0
  370. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  371. package/dist/sandbox/CodeExecutor.d.mts +194 -0
  372. package/dist/sandbox/CodeExecutor.mjs +278 -0
  373. package/dist/sandbox/CodeExecutor.mjs.map +1 -0
  374. package/dist/sandbox/InteractiveBrowser.d.mts +257 -0
  375. package/dist/sandbox/InteractiveBrowser.mjs +236 -0
  376. package/dist/sandbox/InteractiveBrowser.mjs.map +1 -0
  377. package/dist/sandbox/PageCapture.d.mts +2 -0
  378. package/dist/sandbox/PageCapture.mjs +450 -0
  379. package/dist/sandbox/PageCapture.mjs.map +1 -0
  380. package/dist/sandbox/PageCrawl.d.mts +88 -0
  381. package/dist/sandbox/PageCrawl.mjs +124 -0
  382. package/dist/sandbox/PageCrawl.mjs.map +1 -0
  383. package/dist/sandbox/PageScreenshot.d.mts +48 -0
  384. package/dist/sandbox/PageScreenshot.mjs +53 -0
  385. package/dist/sandbox/PageScreenshot.mjs.map +1 -0
  386. package/dist/sandbox/Sandbox.d.mts +2 -0
  387. package/dist/sandbox/Sandbox.mjs +208 -0
  388. package/dist/sandbox/Sandbox.mjs.map +1 -0
  389. package/dist/utf8-BYfhEkcj.mjs +11 -0
  390. package/dist/utf8-BYfhEkcj.mjs.map +1 -0
  391. package/dist/utf8-DchGZRBd.mjs +23 -0
  392. package/dist/utf8-DchGZRBd.mjs.map +1 -0
  393. package/package.json +1 -40
  394. package/src/InMemory.ts +19 -0
  395. package/src/ToolExposure.ts +2 -0
  396. package/src/capabilities/Approval.ts +352 -0
  397. package/src/capabilities/Budget.ts +536 -0
  398. package/src/capabilities/CodeMode.ts +1099 -0
  399. package/src/capabilities/Commands.ts +86 -0
  400. package/src/capabilities/ContextTools.ts +184 -0
  401. package/src/capabilities/Mcp.ts +426 -0
  402. package/src/capabilities/McpClient.ts +983 -0
  403. package/src/capabilities/MemoryNotes.ts +174 -0
  404. package/src/capabilities/Messaging.ts +261 -0
  405. package/src/capabilities/ModelContext.ts +354 -0
  406. package/src/capabilities/Redaction.ts +157 -0
  407. package/src/capabilities/Remembering.ts +550 -0
  408. package/src/capabilities/RunHooks.ts +280 -0
  409. package/src/capabilities/SemanticMemory.ts +447 -0
  410. package/src/capabilities/Subagent.ts +2489 -0
  411. package/src/capabilities/SubagentReservations.ts +850 -0
  412. package/src/capabilities/ToolDiscovery.ts +369 -0
  413. package/src/capabilities/WebCapture.ts +722 -0
  414. package/src/capabilities/WebSearch.ts +190 -0
  415. package/src/capabilities/internal/subagent-background.ts +1307 -0
  416. package/src/capabilities/internal/subagent-contract.ts +146 -0
  417. package/src/capabilities/internal/subagent-policy.ts +235 -0
  418. package/src/capabilities/internal/subagent-reporting.ts +136 -0
  419. package/src/capabilities/internal/utf8.ts +10 -0
  420. package/src/core/Agent.ts +605 -0
  421. package/src/core/AgentError.ts +172 -0
  422. package/src/core/AgentPolicy.ts +153 -0
  423. package/src/core/AgentUpdates.ts +100 -0
  424. package/src/core/FailureDiagnostic.ts +343 -0
  425. package/src/core/IdGenerator.ts +39 -0
  426. package/src/core/Identifiers.ts +48 -0
  427. package/src/core/Memory.ts +290 -0
  428. package/src/core/MemoryNamespace.ts +196 -0
  429. package/src/core/MemoryReference.ts +121 -0
  430. package/src/core/MemoryRevalidation.ts +185 -0
  431. package/src/core/MemoryStore.ts +368 -0
  432. package/src/core/Messaging.ts +72 -0
  433. package/src/core/Receipt.ts +42 -0
  434. package/src/core/RememberingStore.ts +300 -0
  435. package/src/core/RunEvent.ts +366 -0
  436. package/src/core/RunPolicyUsage.ts +17 -0
  437. package/src/core/SemanticMemoryIndex.ts +250 -0
  438. package/src/core/SemanticMemoryRevalidation.ts +254 -0
  439. package/src/core/SubagentContract.ts +196 -0
  440. package/src/core/Thread.ts +371 -0
  441. package/src/core/ToolExposure.ts +74 -0
  442. package/src/core/ToolResult.ts +190 -0
  443. package/src/core/Usage.ts +529 -0
  444. package/src/core/Worker.ts +240 -0
  445. package/src/core/internal/agent-updates.ts +34 -0
  446. package/src/core/internal/message-status.ts +60 -0
  447. package/src/core/internal/strict-schema.ts +14 -0
  448. package/src/core/internal/utf8.ts +23 -0
  449. package/src/durable/ActivityStore.ts +160 -0
  450. package/src/durable/Admin.ts +505 -0
  451. package/src/durable/AgentRegistration.ts +14 -0
  452. package/src/durable/Certification.ts +210 -0
  453. package/src/durable/CommittedActivity.ts +296 -0
  454. package/src/durable/Digest.ts +87 -0
  455. package/src/durable/DurableAgentRuntime.ts +11359 -0
  456. package/src/durable/DurableFailpoint.ts +111 -0
  457. package/src/durable/DurableFailpointTestControl.ts +39 -0
  458. package/src/durable/EventSource.ts +172 -0
  459. package/src/durable/GitHubWorkflowSource.ts +447 -0
  460. package/src/durable/MessageDelivery.ts +832 -0
  461. package/src/durable/MessageDeliveryStoreConformance.ts +410 -0
  462. package/src/durable/OperationAuthorizer.ts +85 -0
  463. package/src/durable/PersistentHistory.ts +260 -0
  464. package/src/durable/PreparedInputAdmission.ts +57 -0
  465. package/src/durable/Records.ts +1087 -0
  466. package/src/durable/Recovery.ts +1018 -0
  467. package/src/durable/RunJournal.ts +1695 -0
  468. package/src/durable/Schedule.ts +513 -0
  469. package/src/durable/ScheduleStoreConformance.ts +638 -0
  470. package/src/durable/ScheduleTransition.ts +265 -0
  471. package/src/durable/Scheduling.ts +1077 -0
  472. package/src/durable/SqlMemoryStore.ts +778 -0
  473. package/src/durable/SubmissionLedger.ts +1297 -0
  474. package/src/durable/SubmissionLedgerConformance.ts +4940 -0
  475. package/src/durable/SubmissionStatus.ts +14 -0
  476. package/src/durable/Subscription.ts +544 -0
  477. package/src/durable/SubscriptionInput.ts +144 -0
  478. package/src/durable/SubscriptionStoreConformance.ts +945 -0
  479. package/src/durable/SubscriptionTools.ts +327 -0
  480. package/src/durable/SubscriptionTransition.ts +302 -0
  481. package/src/durable/Subscriptions.ts +1210 -0
  482. package/src/durable/ThreadContextHistory.ts +227 -0
  483. package/src/durable/ThreadContextHistoryProjection.ts +216 -0
  484. package/src/durable/ThreadInvariants.ts +507 -0
  485. package/src/durable/ThreadProjection.ts +269 -0
  486. package/src/durable/ThreadProjectionMaintenance.ts +67 -0
  487. package/src/durable/ThreadStore.ts +635 -0
  488. package/src/durable/ThreadStoreConformance.ts +1296 -0
  489. package/src/durable/ToolReconciler.ts +109 -0
  490. package/src/durable/WakeScheduler.ts +134 -0
  491. package/src/durable/WorkerHost.ts +183 -0
  492. package/src/durable/internal/agent-registration.ts +587 -0
  493. package/src/durable/internal/agent-updates.ts +306 -0
  494. package/src/durable/internal/foreign-diagnostic.ts +47 -0
  495. package/src/durable/internal/journal-checkpoint.ts +104 -0
  496. package/src/durable/internal/journal-metadata.ts +124 -0
  497. package/src/durable/internal/message-status.ts +20 -0
  498. package/src/durable/internal/messaging-host.ts +637 -0
  499. package/src/durable/internal/schedule-time.ts +303 -0
  500. package/src/durable/internal/worker-host.ts +3159 -0
  501. package/src/durable/internal/worker-runtime.ts +9 -0
  502. package/src/engine/AgentRuntime.ts +37 -0
  503. package/src/engine/Compaction.ts +11 -0
  504. package/src/engine/ContextCompactor.ts +277 -0
  505. package/src/engine/ContextHistory.ts +73 -0
  506. package/src/engine/ContextWindow.ts +72 -0
  507. package/src/engine/DurableStep.ts +132 -0
  508. package/src/engine/MessagingHost.ts +110 -0
  509. package/src/engine/Output.ts +2 -0
  510. package/src/engine/RunEventSink.ts +132 -0
  511. package/src/engine/RunOptions.ts +1101 -0
  512. package/src/engine/SubagentHost.ts +239 -0
  513. package/src/engine/ThreadHistory.ts +120 -0
  514. package/src/engine/ToolBroker.ts +157 -0
  515. package/src/engine/ToolExposure.ts +48 -0
  516. package/src/engine/internal/agent-runtime.ts +10534 -0
  517. package/src/engine/internal/bounded-value.ts +315 -0
  518. package/src/engine/internal/compaction.ts +678 -0
  519. package/src/engine/internal/error-diagnostic.ts +39 -0
  520. package/src/engine/internal/output-contract.ts +191 -0
  521. package/src/engine/internal/primitive-delta.ts +80 -0
  522. package/src/engine/internal/provider-result-staging.ts +211 -0
  523. package/src/engine/internal/tool-derivative.ts +106 -0
  524. package/src/engine/internal/tool-exposure.ts +224 -0
  525. package/src/engine/internal/tool-telemetry.ts +484 -0
  526. package/src/index.ts +97 -22
  527. package/src/sandbox/CodeExecutor.ts +352 -0
  528. package/src/sandbox/InteractiveBrowser.ts +339 -0
  529. package/src/sandbox/PageCapture.ts +691 -0
  530. package/src/sandbox/PageCrawl.ts +148 -0
  531. package/src/sandbox/PageScreenshot.ts +81 -0
  532. package/src/sandbox/Sandbox.ts +241 -0
@@ -0,0 +1,1695 @@
1
+ import { type Crypto, Effect, Schema, Stream, type DateTime } from "effect";
2
+ import { Prompt } from "effect/unstable/ai";
3
+
4
+ import { ThreadId, RunId, ToolCallId, TurnId, type SubmissionId } from "../core/Identifiers.ts";
5
+ import { type ExhaustedLimit } from "../core/RunEvent.ts";
6
+ import { RunPolicyUsage } from "../core/RunPolicyUsage.ts";
7
+ import { type Selection, type Snapshot } from "../core/ToolExposure.ts";
8
+ import type { ToolParameterRejection } from "../core/ToolResult.ts";
9
+ import { ModelCallUsage, summarizeModelUsage, type RunUsageSummary } from "../core/Usage.ts";
10
+ import type { WorkerRef } from "../core/Worker.ts";
11
+ import {
12
+ CLEARED_TOOL_RESULT,
13
+ COMPACTION_SUMMARY_PREFIX,
14
+ contextWindowId,
15
+ contextWindowMessage,
16
+ } from "../engine/Compaction.ts";
17
+ import { digestJson, type DigestError } from "./Digest.ts";
18
+ import { type JournalCheckpointSeed } from "./internal/journal-checkpoint.ts";
19
+ import {
20
+ makeJournalMetadata,
21
+ toolExecutionKey,
22
+ type JournalMetadata,
23
+ } from "./internal/journal-metadata.ts";
24
+ import {
25
+ BatchId,
26
+ CanonicalBatch,
27
+ CompactionCreated,
28
+ ModelResponseRecorded,
29
+ PersistedJson,
30
+ RecordEnvelope,
31
+ RecordId,
32
+ RunCompleted,
33
+ ToolCallSettled,
34
+ type CanonicalRecordEnvelope,
35
+ type CanonicalSequence,
36
+ type DeploymentId,
37
+ type ProducerId,
38
+ } from "./Records.ts";
39
+ import { IdempotencyKey } from "./SubmissionLedger.ts";
40
+
41
+ /** A canonical record could not be projected into Run/Prompt state. */
42
+ export class RunJournalError extends Schema.TaggedError<RunJournalError>()("RunJournalError", {
43
+ message: Schema.String,
44
+ cause: Schema.optionalKey(Schema.Defect()),
45
+ }) {}
46
+
47
+ const journalError = (message: string, cause?: unknown): RunJournalError =>
48
+ cause === undefined
49
+ ? RunJournalError.make({ message })
50
+ : RunJournalError.make({ message, cause });
51
+
52
+ const decodeRunId = Schema.decodeSync(RunId);
53
+ const decodeTurnId = Schema.decodeSync(TurnId);
54
+ const decodeBatchId = Schema.decodeSync(BatchId);
55
+ const decodeRecordId = Schema.decodeSync(RecordId);
56
+
57
+ /**
58
+ * Deterministic Run identity pinned per Submission (plan §Coordinator flow). Every Attempt of one
59
+ * Submission shares this Run identity, so canonical per-Turn records from different Attempts
60
+ * belong to one logical Run.
61
+ */
62
+ export const runIdForSubmission = (submissionId: SubmissionId): RunId =>
63
+ decodeRunId(`run:${submissionId}`);
64
+
65
+ /** Stable canonical clock identity shared by every Attempt of one Run. */
66
+ export const runStartedRecordId = (runId: RunId): RecordId => decodeRecordId(`run-start:${runId}`);
67
+
68
+ export const runStartedBatchId = (runId: RunId): BatchId => decodeBatchId(`run-start:${runId}`);
69
+
70
+ export const runDurationRecordId = (runId: RunId): RecordId =>
71
+ decodeRecordId(`run-duration:${runId}`);
72
+
73
+ export const runDurationBatchId = (runId: RunId): BatchId => decodeBatchId(`run-duration:${runId}`);
74
+
75
+ /** Deterministic Turn identity: Attempt-independent for one (Run, canonical turn) pair. */
76
+ export const turnIdForRun = (runId: RunId, turn: number): TurnId =>
77
+ decodeTurnId(`turn:${runId}:${turn}`);
78
+
79
+ /** Deterministic batch identity of one committed canonical no-tool Turn (P4 single-batch shape). */
80
+ export const turnBatchId = (runId: RunId, turn: number): BatchId =>
81
+ decodeBatchId(`turn:${runId}:${turn}`);
82
+
83
+ /**
84
+ * Deterministic batch identity of a tool-declaring Turn's RESPONSE commit (plan §2.1 commit 1):
85
+ * the assistant response plus pending steering becomes canonical BEFORE approval preflight and
86
+ * tool preparation, creating the durability §15 "resume tool scheduling" window.
87
+ */
88
+ export const turnResponseBatchId = (runId: RunId, turn: number): BatchId =>
89
+ decodeBatchId(`turn-response:${runId}:${turn}`);
90
+
91
+ /**
92
+ * Deterministic batch identity of a tool-declaring Turn's RESULTS commit (plan §2.1 commit 5):
93
+ * every `ToolCallSettled` record of the Turn, in declaration order, model-visible atomically.
94
+ */
95
+ export const turnResultsBatchId = (runId: RunId, turn: number): BatchId =>
96
+ decodeBatchId(`turn-results:${runId}:${turn}`);
97
+
98
+ /**
99
+ * Deterministic per-call late-settle batch identity used by the resolution path when one
100
+ * recovered/resolved call settles outside its Turn's results batch. Record identity
101
+ * (`tool-settled:{runId}:{turn}:{toolCallId}`) dedupes double-settles across both paths.
102
+ */
103
+ export const toolCallResultBatchId = (
104
+ runId: RunId,
105
+ turn: number,
106
+ toolCallId: ToolCallId,
107
+ ): BatchId => decodeBatchId(`turn-results:${runId}:${turn}:${toolCallId}`);
108
+
109
+ /** Deterministic batch identity of one Turn's `ToolCallPrepared` commit (plan §2.1 commit 3). */
110
+ export const turnPreparedBatchId = (runId: RunId, turn: number): BatchId =>
111
+ decodeBatchId(`turn-prepared:${runId}:${turn}`);
112
+
113
+ /**
114
+ * Deterministic identity of one pre-Turn compaction record (RUN-026,
115
+ * RUN-026). Keyed by Run, Turn, and kind — the engine performs at most one
116
+ * threshold compaction per Turn plus at most one overflow-forced summarize,
117
+ * so a superseding Attempt that re-decides the same compaction replays the
118
+ * batch identity instead of duplicating the record.
119
+ */
120
+ export const compactionRecordId = (
121
+ runId: RunId,
122
+ turn: number,
123
+ kind: "clear-tool-results" | "summarize" | "rollover",
124
+ ): RecordId => decodeRecordId(`compaction:${runId}:${turn}:${kind}`);
125
+
126
+ /** Deterministic batch identity of one compaction append (same string as its record id). */
127
+ export const compactionBatchId = (
128
+ runId: RunId,
129
+ turn: number,
130
+ kind: "clear-tool-results" | "summarize" | "rollover",
131
+ ): BatchId => decodeBatchId(`compaction:${runId}:${turn}:${kind}`);
132
+
133
+ /** Deterministic canonical record identity of one Turn's `ModelResponseRecorded` record. */
134
+ export const modelResponseRecordId = (runId: RunId, turn: number): RecordId =>
135
+ decodeRecordId(`model-response:${runId}:${turn}`);
136
+
137
+ /** Terminal Tool completion marker committed atomically with its settled Tool result. */
138
+ export const runCompletedRecordId = (runId: RunId): RecordId =>
139
+ decodeRecordId(`run-completed:${runId}`);
140
+
141
+ /** Native source reservation locator, including the first admission of a continuing worker. */
142
+ export const workerInputRecordId = (messageId: IdempotencyKey): RecordId =>
143
+ decodeRecordId(`worker-input:${messageId}`);
144
+
145
+ /** Source-owned first reservation; native worker identity is minted from its first message. */
146
+ export const firstWorkerInputRecordId = (worker: WorkerRef): RecordId =>
147
+ decodeRecordId(`worker-input:${worker.threadId}`);
148
+
149
+ export const workerOriginRecordId = (threadId: ThreadId): RecordId =>
150
+ decodeRecordId(`worker-origin:${threadId}`);
151
+
152
+ export const workerReportRecordId = (messageId: IdempotencyKey): RecordId =>
153
+ decodeRecordId(`worker-report:${messageId}`);
154
+
155
+ export const peerMessageRecordId = (messageId: IdempotencyKey): RecordId =>
156
+ decodeRecordId(messageId);
157
+
158
+ /** Same immutable tuple used by native update admission; callers never parse its hash. */
159
+ export const agentUpdateRecordId = Effect.fn("RunJournal.agentUpdateRecordId")(function* (
160
+ threadId: ThreadId,
161
+ runId: RunId,
162
+ updateId: IdempotencyKey,
163
+ ) {
164
+ return decodeRecordId(`agent-update:${yield* digestJson([threadId, runId, updateId])}`);
165
+ });
166
+
167
+ /** Deterministic canonical record identity of one Turn's `ToolCallSettled` record. */
168
+ export const toolCallSettledRecordId = (
169
+ runId: RunId,
170
+ turn: number,
171
+ toolCallId: ToolCallId,
172
+ ): RecordId => decodeRecordId(`tool-settled:${runId}:${turn}:${toolCallId}`);
173
+
174
+ /** Deterministic canonical record identity of one Tool Call's `ToolCallPrepared` record. */
175
+ export const toolCallPreparedRecordId = (
176
+ runId: RunId,
177
+ turn: number,
178
+ toolCallId: ToolCallId,
179
+ ): RecordId => decodeRecordId(`tool-prepared:${runId}:${turn}:${toolCallId}`);
180
+
181
+ /** Deterministic batch identity of one Turn's `ToolCallUnknown` marking append. */
182
+ export const markUnknownBatchId = (submissionId: SubmissionId, turn: number): BatchId =>
183
+ decodeBatchId(`mark-unknown:${submissionId}:${turn}`);
184
+
185
+ /** Deterministic canonical record identity of one Tool Call's `ToolCallUnknown` record. */
186
+ export const toolCallUnknownRecordId = (
187
+ runId: RunId,
188
+ turn: number,
189
+ toolCallId: ToolCallId,
190
+ ): RecordId => decodeRecordId(`tool-unknown:${runId}:${turn}:${toolCallId}`);
191
+
192
+ /** Deterministic batch identity of one Tool Call's resolution append (DUR-017). */
193
+ export const toolCallResolutionBatchId = (
194
+ submissionId: SubmissionId,
195
+ toolCallId: ToolCallId,
196
+ ): BatchId => decodeBatchId(`resolve:${submissionId}:${toolCallId}`);
197
+
198
+ /** Deterministic canonical record identity of one Tool Call's `ToolCallResolved` record. */
199
+ export const toolCallResolvedRecordId = (
200
+ runId: RunId,
201
+ turn: number,
202
+ toolCallId: ToolCallId,
203
+ ): RecordId => decodeRecordId(`tool-resolved:${runId}:${turn}:${toolCallId}`);
204
+
205
+ /**
206
+ * Deterministic identity of one accepted Durable Step result. The one-record batch reuses the
207
+ * SAME string, so batch idempotency plus the epoch fence realize the durability §11
208
+ * racing-writers rule: only the fenced winner's record commits; the loser replays it.
209
+ * A versioned JSON tuple keeps separator characters in each identity component distinct.
210
+ * Replay derives this key from the structured payload, including records with legacy IDs.
211
+ */
212
+ export const toolStepSettledRecordId = (
213
+ runId: RunId,
214
+ toolCallId: ToolCallId,
215
+ stepName: string,
216
+ ): RecordId => decodeRecordId(JSON.stringify(["step@2", runId, toolCallId, stepName]));
217
+
218
+ /** Deterministic batch identity of one Durable Step commit (same string as its record id). */
219
+ export const toolStepSettledBatchId = (
220
+ runId: RunId,
221
+ toolCallId: ToolCallId,
222
+ stepName: string,
223
+ ): BatchId => decodeBatchId(toolStepSettledRecordId(runId, toolCallId, stepName));
224
+
225
+ /** Deterministic batch identity of one Turn's canonical approval-request append (plan §2.6). */
226
+ export const turnApprovalsBatchId = (runId: RunId, turn: number): BatchId =>
227
+ decodeBatchId(`turn-approvals:${runId}:${turn}`);
228
+
229
+ /** Deterministic canonical record identity of one Tool Call's `ToolApprovalRequested` record. */
230
+ export const toolApprovalRequestRecordId = (
231
+ runId: RunId,
232
+ turn: number,
233
+ toolCallId: ToolCallId,
234
+ ): RecordId => decodeRecordId(`approval-request:${runId}:${turn}:${toolCallId}`);
235
+
236
+ /** Deterministic batch identity of one Tool Call's canonical approval-decision append. */
237
+ export const approvalDecisionBatchId = (
238
+ submissionId: SubmissionId,
239
+ toolCallId: ToolCallId,
240
+ ): BatchId => decodeBatchId(`approval-decision:${submissionId}:${toolCallId}`);
241
+
242
+ /** Deterministic canonical record identity of one Tool Call's `ToolApprovalDecided` record. */
243
+ export const toolApprovalDecisionRecordId = (
244
+ runId: RunId,
245
+ turn: number,
246
+ toolCallId: ToolCallId,
247
+ ): RecordId => decodeRecordId(`approval-decision:${runId}:${turn}:${toolCallId}`);
248
+
249
+ /**
250
+ * Deterministic identity of one superseded Attempt's `ModelResponseInterrupted` audit record
251
+ * (durability §9). Keyed by the superseded epoch, so each interrupted ownership period is
252
+ * recorded at most once; the one-record batch reuses the same string.
253
+ */
254
+ export const modelResponseInterruptedRecordId = (runId: RunId, supersededEpoch: number): RecordId =>
255
+ decodeRecordId(`interrupted:${runId}:${supersededEpoch}`);
256
+
257
+ /** Deterministic batch identity of one `ModelResponseInterrupted` append (same string). */
258
+ export const modelResponseInterruptedBatchId = (runId: RunId, supersededEpoch: number): BatchId =>
259
+ decodeBatchId(`interrupted:${runId}:${supersededEpoch}`);
260
+
261
+ const decodeThreadId = Schema.decodeSync(ThreadId);
262
+ const decodeIdempotencyKey = Schema.decodeSync(IdempotencyKey);
263
+
264
+ /**
265
+ * Deterministic canonical record identity of one parent Tool Call's `SubagentRequested` record.
266
+ * The one-record batch reuses the same string, so batch
267
+ * idempotency plus the parent epoch fence make the request append exactly-once-canonical.
268
+ */
269
+ export const subagentRequestedRecordId = (runId: RunId, toolCallId: ToolCallId): RecordId =>
270
+ decodeRecordId(`subagent-requested:${runId}:${toolCallId}`);
271
+
272
+ /** Deterministic batch identity of one `SubagentRequested` append (same string). */
273
+ export const subagentRequestedBatchId = (runId: RunId, toolCallId: ToolCallId): BatchId =>
274
+ decodeBatchId(`subagent-requested:${runId}:${toolCallId}`);
275
+
276
+ /**
277
+ * Deterministic canonical record identity of one parent Tool Call's `SubagentStarted` record.
278
+ * Recovery's start-link repair appends the EXACT same record
279
+ * under this identity, so a raced repair replays instead of duplicating (SUB-016/SUB-017).
280
+ */
281
+ export const subagentStartedRecordId = (runId: RunId, toolCallId: ToolCallId): RecordId =>
282
+ decodeRecordId(`subagent-started:${runId}:${toolCallId}`);
283
+
284
+ /** Deterministic batch identity of one `SubagentStarted` append (same string). */
285
+ export const subagentStartedBatchId = (runId: RunId, toolCallId: ToolCallId): BatchId =>
286
+ decodeBatchId(`subagent-started:${runId}:${toolCallId}`);
287
+
288
+ /**
289
+ * Deterministic batch identity of one parent Tool Call's atomic settlement join: the
290
+ * `SubagentJoined` record plus the parent's `ToolCallSettled` record (its existing
291
+ * `tool-settled:{runId}:{turn}:{toolCallId}` identity) commit as ONE canonical batch (SUB-019).
292
+ */
293
+ export const subagentJoinBatchId = (runId: RunId, toolCallId: ToolCallId): BatchId =>
294
+ decodeBatchId(`subagent-join:${runId}:${toolCallId}`);
295
+
296
+ /** Deterministic canonical record identity of one parent Tool Call's `SubagentJoined` record. */
297
+ export const subagentJoinedRecordId = (runId: RunId, toolCallId: ToolCallId): RecordId =>
298
+ decodeRecordId(`subagent-joined:${runId}:${toolCallId}`);
299
+
300
+ /**
301
+ * Deterministic identity of one child Thread's `SubagentLineageRecorded` record.
302
+ * Its own single-record batch reuses the same string so the generic
303
+ * `thread-created:{cid}` batch identity is never contradicted.
304
+ */
305
+ export const subagentLineageRecordId = (threadId: ThreadId): RecordId =>
306
+ decodeRecordId(`subagent-lineage:${threadId}`);
307
+
308
+ /** Deterministic batch identity of one `SubagentLineageRecorded` append (same string). */
309
+ export const subagentLineageBatchId = (threadId: ThreadId): BatchId =>
310
+ decodeBatchId(`subagent-lineage:${threadId}`);
311
+
312
+ /**
313
+ * Deterministic intended child Thread identity: the
314
+ * parent Submission and Tool Call pair addresses exactly one child Thread, so a replayed
315
+ * establishment converges on the one existing child (SUB-016).
316
+ */
317
+ export const childThreadIdFor = (
318
+ parentSubmissionId: SubmissionId,
319
+ toolCallId: ToolCallId,
320
+ ): ThreadId => decodeThreadId(`subagent:${parentSubmissionId}:${toolCallId}`);
321
+
322
+ /**
323
+ * Deterministic child admission idempotency key: scoped to
324
+ * the parent Run and Tool Call identity, so duplicate admission attempts resolve through the
325
+ * ledger's idempotency contract to one child Receipt (SUB-016, SUB-031). The ledger key is
326
+ * bounded (256); coordinator-minted Run and Tool Call identities stay far below that bound.
327
+ */
328
+ export const childIdempotencyKeyFor = (
329
+ parentRunId: RunId,
330
+ toolCallId: ToolCallId,
331
+ ): IdempotencyKey => decodeIdempotencyKey(`subagent:${parentRunId}:${toolCallId}`);
332
+
333
+ const decodePromptMessages = Effect.fn("RunJournal.decodePromptMessages")(
334
+ (messages: PersistedJson): Effect.Effect<Prompt.Prompt, RunJournalError> =>
335
+ Schema.decodeUnknownEffect(Prompt.Prompt)(messages).pipe(
336
+ Effect.mapError((cause) =>
337
+ journalError("Canonical messages are not Schema-encoded Prompt messages", cause),
338
+ ),
339
+ ),
340
+ );
341
+
342
+ interface PendingSettledTool {
343
+ readonly record: ToolCallSettled;
344
+ /** RUN-026: a `clear-tool-results` compaction covers this record's sequence. */
345
+ readonly cleared: boolean;
346
+ }
347
+
348
+ const toolMessageFromSettled = Effect.fn("RunJournal.toolMessageFromSettled")(
349
+ (settled: ReadonlyArray<PendingSettledTool>): Effect.Effect<Prompt.Message, RunJournalError> =>
350
+ Effect.try({
351
+ try: () =>
352
+ Prompt.makeMessage("tool", {
353
+ content: settled.map(({ record, cleared }) =>
354
+ Prompt.makePart("tool-result", {
355
+ id: record.toolCallId,
356
+ name: record.toolName,
357
+ result: cleared ? CLEARED_TOOL_RESULT : record.result,
358
+ isFailure: record.isFailure,
359
+ providerExecuted: false,
360
+ }),
361
+ ),
362
+ }),
363
+ catch: (cause) => journalError("Unable to rebuild Tool message from ToolCallSettled", cause),
364
+ }),
365
+ );
366
+
367
+ /**
368
+ * Pure canonical projection of one Run's durable execution state.
369
+ *
370
+ * Prompt reconstruction contract (D8):
371
+ *
372
+ * - `ModelResponseRecorded.messages` carries the Schema-encoded Prompt messages the Turn appended
373
+ * to the model-visible prompt — for the Run's FIRST committed Turn that includes the evaluated
374
+ * instruction and user-input messages, for later Turns only the assistant response messages.
375
+ * Tool messages are excluded from `messages`.
376
+ * - `ToolCallSettled` records (committed in the same per-Turn batch, in declaration order)
377
+ * deterministically rebuild the Turn's single Tool message.
378
+ * - `UserInputRecorded` records input, with a Submission identity only for durable admission. Its
379
+ * Prompt-visible form (instructions + user message) becomes canonical inside the owning Run's
380
+ * first `ModelResponseRecorded`. The projection consumes it only for Run correlation.
381
+ * - Immediate retained history uses `ModelCompleted.messages` for the successful Run's exact
382
+ * native Prompt suffix, including any input examples. It has no resumable Turn state.
383
+ */
384
+ /** Cumulative committed usage of the projected Run (RUN-023 resume re-seed). */
385
+ export interface RunJournalUsage {
386
+ readonly modelCalls: number;
387
+ /** Known accounting gaps retained across Attempts without changing numeric usage totals. */
388
+ readonly unobservedModelCalls?: number | undefined;
389
+ readonly inputTokens: number;
390
+ readonly outputTokens: number;
391
+ readonly lastInputTokens: number;
392
+ readonly lastOutputTokens: number;
393
+ /** Cumulative persisted spend of the projected Run's committed calls (RUN-023). */
394
+ readonly costMicrousd: number;
395
+ /** Canonical per-call detail used for settlement aggregation and recovery. */
396
+ readonly modelUsage: ReadonlyArray<ModelCallUsage>;
397
+ /** Detailed usage of retired records, grouped without retaining per-call payloads. */
398
+ readonly summarizedModelUsage?: RunUsageSummary | undefined;
399
+ }
400
+
401
+ export interface RunJournalProjection {
402
+ readonly toolSelection?: Selection | undefined;
403
+ readonly policyUsage: RunPolicyUsage;
404
+ /** Canonical projection for the requested Run; may end at its resumable Tool declaration. */
405
+ readonly prompt: Prompt.Prompt;
406
+ /** Prior-Run history with model-only unknown results closing incomplete application calls. */
407
+ readonly historyBefore: Prompt.Prompt;
408
+ /** Number of canonical Turns already committed for the projected Run. */
409
+ readonly committedTurns: number;
410
+ /** Summed per-call usage of the projected Run's committed responses; zeros for records predating usage capture. */
411
+ readonly usage: RunJournalUsage;
412
+ /** Latest committed context window identity, retained across ownership changes. */
413
+ readonly contextWindowId?: string | undefined;
414
+ /** Canonical evaluated instructions and initial input for this Run, independent of the view. */
415
+ readonly protectedContext?: Prompt.Prompt | undefined;
416
+ /** Last successful singleton Tool awaiting possible context-control interpretation by the engine. */
417
+ readonly pendingContextToolCallId?: string | undefined;
418
+ }
419
+
420
+ interface ProjectedResponseUsage {
421
+ readonly modelCalls: number;
422
+ readonly inputTokens: number;
423
+ readonly outputTokens: number;
424
+ readonly costMicrousd: number;
425
+ readonly modelUsage: ReadonlyArray<ModelCallUsage>;
426
+ }
427
+
428
+ const projectedResponseUsage = (
429
+ response: ModelResponseRecorded,
430
+ ): Effect.Effect<ProjectedResponseUsage, RunJournalError> =>
431
+ Effect.gen(function* () {
432
+ const calls = response.modelUsage;
433
+
434
+ if (calls === undefined) {
435
+ return {
436
+ modelCalls: 1,
437
+ inputTokens: response.inputTokens ?? 0,
438
+ outputTokens: response.outputTokens ?? 0,
439
+ costMicrousd: response.costMicrousd ?? 0,
440
+ modelUsage: [],
441
+ };
442
+ }
443
+ if (calls.length === 0) {
444
+ return yield* journalError("A canonical modelUsage list must not be empty");
445
+ }
446
+
447
+ const summary = yield* summarizeModelUsage(calls).pipe(
448
+ Effect.mapError((cause) =>
449
+ journalError("Canonical model usage exceeds accounting bounds", cause),
450
+ ),
451
+ );
452
+
453
+ if (
454
+ (response.inputTokens !== undefined && response.inputTokens !== summary.inputTokens.total) ||
455
+ (response.outputTokens !== undefined &&
456
+ response.outputTokens !== summary.outputTokens.total) ||
457
+ (response.costMicrousd !== undefined && response.costMicrousd !== summary.costMicrousd)
458
+ ) {
459
+ return yield* journalError("Canonical detailed and aggregate model usage disagree");
460
+ }
461
+
462
+ return {
463
+ modelCalls: summary.modelCalls,
464
+ inputTokens: summary.inputTokens.total,
465
+ outputTokens: summary.outputTokens.total,
466
+ costMicrousd: summary.costMicrousd,
467
+ modelUsage: calls,
468
+ };
469
+ });
470
+
471
+ const addProjectedUsage = (
472
+ field: string,
473
+ left: number,
474
+ right: number,
475
+ ): Effect.Effect<number, RunJournalError> =>
476
+ Schema.decodeEffect(Schema.Natural)(left + right).pipe(
477
+ Effect.mapError((cause) =>
478
+ journalError(`Canonical projected usage exceeds safe-integer bounds at ${field}`, cause),
479
+ ),
480
+ );
481
+
482
+ interface FoldState {
483
+ readonly all: Array<Prompt.Message>;
484
+ readonly before: Array<Prompt.Message>;
485
+ readonly pendingTools: Array<PendingSettledTool>;
486
+ readonly pendingToolsForRun: boolean;
487
+ readonly committedTurns: number;
488
+ }
489
+
490
+ const decodeToolCallId = Schema.decodeSync(ToolCallId);
491
+
492
+ const declaredApplicationToolCallIds = (prompt: Prompt.Prompt): ReadonlyArray<string> => {
493
+ const ids: Array<string> = [];
494
+
495
+ for (const message of prompt.content) {
496
+ if (message.role !== "assistant") continue;
497
+ for (const part of message.content) {
498
+ if (part.type === "tool-call" && !part.providerExecuted) ids.push(part.id);
499
+ }
500
+ }
501
+
502
+ return ids;
503
+ };
504
+
505
+ /**
506
+ * Phase 5 audit tags that are prompt-transparent: they carry durability evidence (preparation,
507
+ * unknown marking, resolution, Step results, approvals, interruption) but contribute nothing to
508
+ * the model-visible Prompt, and — unlike the P4 tags — they do NOT split a contiguous
509
+ * `ToolCallSettled` group into separate Tool messages, so a late-settled call's audit records
510
+ * cannot change the replayed prompt shape.
511
+ *
512
+ * The four S2 Subagent lifecycle tags are prompt-transparent for the same reason (spec §5/§11):
513
+ * the parent prompt never carries child transcript or establishment evidence — the joined child
514
+ * result reaches the model exclusively through the paired `ToolCallSettled` record, and the
515
+ * child-log lineage record is not model input.
516
+ */
517
+ const PROMPT_TRANSPARENT_TAGS: ReadonlySet<string> = new Set([
518
+ "RunPolicyUsageReserved",
519
+ "RunStarted",
520
+ "RunDurationExhausted",
521
+ "ToolCallPrepared",
522
+ "ToolCallUnknown",
523
+ "ToolCallResolved",
524
+ "ToolStepSettled",
525
+ "ToolApprovalRequested",
526
+ "ToolApprovalDecided",
527
+ "ModelResponseInterrupted",
528
+ "SubagentRequested",
529
+ "SubagentStarted",
530
+ "SubagentJoined",
531
+ "SubagentLineageRecorded",
532
+ "WorkerInputRequested",
533
+ "WorkerOriginRecorded",
534
+ "WorkerInputCompleted",
535
+ "WorkerReportPrepared",
536
+ "AgentUpdateEmitted",
537
+ "WorkerReportRefused",
538
+ "PeerMessagePrepared",
539
+ "SubtreeBudgetReserved",
540
+ ]);
541
+
542
+ /**
543
+ * Pure projection: rebuild one Run's resume state from canonical records (DUR-015). Canonical
544
+ * order is authoritative; the fold projects each `ModelResponseRecorded` Turn and the
545
+ * owning Run's resumable incomplete Tool Turn. It flushes each contiguous group of valid `ToolCallSettled` records into one Tool message,
546
+ * exactly mirroring the per-Turn commit shape produced by `turnCanonicalBatch` (no-tool Turns) and
547
+ * by the
548
+ * `turnResponseBatch`/`turnResultsBatch` split (tool-declaring Turns). The Phase 5 audit tags
549
+ * are skipped transparently, so split-batch commits replay to the same prompt as P4 single-batch
550
+ * commits.
551
+ *
552
+ * An incomplete application Tool turn remains visible while projecting its owning Run so active
553
+ * recovery can resume the declared batch. It is not a valid model-visible Turn boundary for a
554
+ * later Run until model-only unknown results close its missing calls. Those explanatory results
555
+ * are never canonical settlements or evidence for compaction, recovery or accounting. Real
556
+ * results replace them at the original declaration, including results appended after another Run.
557
+ * Prior user intent and assistant text remain visible; prior system instructions do not.
558
+ * Each Run retains the history preceding its start and its own continuation. Other Runs'
559
+ * subsequent Turns cannot replace its input. Compaction covers only its creator's Run view;
560
+ * a projection without an owner retains interleaved exchanges from the complete Thread history.
561
+ */
562
+ /** @internal Lightweight canonical boundaries collected without retaining record payloads. */
563
+ export interface JournalBoundary {
564
+ readonly sequence: CanonicalSequence;
565
+ readonly tag: "ModelResponseRecorded" | "ToolCallSettled";
566
+ readonly promptLength: number;
567
+ /** A declaration without all settled results requires terminal-prior-Run proof for coverage. */
568
+ readonly incomplete?: true | undefined;
569
+ /** The Run terminated after its last response and differs from the projection's owner. */
570
+ readonly terminalPriorRun?: true | undefined;
571
+ }
572
+
573
+ /**
574
+ * Reconstruct a fixed canonical prefix from a re-readable stream. The caller must keep the same
575
+ * records visible on every traversal. The first collects compaction and settlement metadata;
576
+ * rollovers additionally validate covered Tool batches before rebuilding Prompt and usage. Metadata and the
577
+ * live Prompt remain resident; covered historical message/tool payloads do not. An uncompacted Prompt still grows
578
+ * with its conversation, so hosts must configure an appropriate context compaction policy.
579
+ * Prepared metadata must describe exactly this stream and seed through the same fixed tail;
580
+ * it skips only the metadata scan, never covered-Tool validation or the canonical fold.
581
+ * @internal
582
+ */
583
+ export const projectRunJournalStream = Effect.fn("RunJournal.projectRunJournalStream")(function* <
584
+ E,
585
+ R,
586
+ >(
587
+ records: Stream.Stream<CanonicalRecordEnvelope, E, R>,
588
+ ownerRunId: RunId | undefined,
589
+ onBoundary?: (boundary: JournalBoundary) => void,
590
+ seed?: JournalCheckpointSeed,
591
+ preparedMetadata?: JournalMetadata,
592
+ ): Effect.fn.Return<RunJournalProjection, RunJournalError | E, R> {
593
+ if (seed !== undefined && seed.runId !== ownerRunId)
594
+ return yield* journalError("Recovery checkpoint belongs to another Run");
595
+
596
+ let state: FoldState = {
597
+ all: [],
598
+ before: [],
599
+ pendingTools: [],
600
+ pendingToolsForRun: false,
601
+ committedTurns: seed?.committedTurns ?? 0,
602
+ };
603
+
604
+ // RUN-026 pre-scan: valid compactions retire only their creator's Run view. A bound
605
+ // stays below its own sequence and cannot split a visible response from its settled results.
606
+ // Pruning and rollovers may cover complete owner-Run batches; a summarize record carries
607
+ // its summary. Invalid records leave full history authoritative. A wider containing view
608
+ // replaces earlier coverage, with equal bounds preferring the later record.
609
+ // One span per settled record, paired with its declaring response the same
610
+ // way the fold pairs them: a settled belongs to the most recent
611
+ // ModelResponseRecorded of its Run. A bound inside (response, settled)
612
+ // would orphan the tool message from its declaring response. Orphaned
613
+ // settleds (filtered later by the fold) still contribute spans —
614
+ // over-invalidating is the fail-safe direction.
615
+ if (
616
+ preparedMetadata !== undefined &&
617
+ (preparedMetadata.ownerRunId !== ownerRunId || preparedMetadata.seed !== seed)
618
+ )
619
+ return yield* journalError("Prepared journal metadata belongs to another replay");
620
+
621
+ let metadata = preparedMetadata;
622
+
623
+ if (metadata === undefined) {
624
+ const collected = makeJournalMetadata(ownerRunId, seed);
625
+
626
+ yield* Stream.runForEach(records, (envelope) => Effect.sync(() => collected.add(envelope)));
627
+ metadata = collected.snapshot();
628
+ }
629
+
630
+ const {
631
+ firstSequenceByRun,
632
+ lastResponseSequenceByRun,
633
+ terminalSequenceByRun,
634
+ settledSpans,
635
+ toolExecutionEvidence,
636
+ settledToolCallRecordIds,
637
+ settledById,
638
+ } = metadata;
639
+
640
+ const declarationByResultSequence = new Map(settledSpans.map(({ from, to }) => [to, from]));
641
+
642
+ const isInRunView = (
643
+ sequence: number,
644
+ payload: { readonly _tag: string; readonly runId?: RunId | undefined },
645
+ runId: RunId | undefined,
646
+ ): boolean => {
647
+ if (runId === undefined || payload.runId === undefined || payload.runId === runId) return true;
648
+ const first = firstSequenceByRun.get(runId) ?? Number.POSITIVE_INFINITY;
649
+
650
+ return (
651
+ sequence < first ||
652
+ (payload._tag === "ToolCallSettled" &&
653
+ (declarationByResultSequence.get(sequence) ?? Number.POSITIVE_INFINITY) < first)
654
+ );
655
+ };
656
+
657
+ const compactions = metadata.compactions.filter(({ sequence, payload }) =>
658
+ isInRunView(sequence, payload, ownerRunId),
659
+ );
660
+
661
+ const recordsForRun = records.pipe(
662
+ Stream.filter(({ sequence, record: { payload } }) =>
663
+ isInRunView(sequence, payload, ownerRunId),
664
+ ),
665
+ );
666
+
667
+ const isCovered = (envelope: CanonicalRecordEnvelope, compaction: CompactionCreated): boolean =>
668
+ envelope.sequence <= compaction.coversThrough &&
669
+ isInRunView(envelope.sequence, envelope.record.payload, compaction.runId);
670
+
671
+ const settledCoverage = compactions.reduce(
672
+ (through, { payload }) =>
673
+ payload.kind !== "summarize" ? Math.max(through, payload.coversThrough) : through,
674
+ 0,
675
+ );
676
+
677
+ // Terminality changes only prompt coverage; it never settles or resolves the Tool Call.
678
+ // Compare against the compaction's Run and sequence, independent of the replay's owner.
679
+ const isTerminalPriorRun = (
680
+ candidateRunId: RunId,
681
+ compactionRunId: RunId | undefined,
682
+ beforeSequence: number,
683
+ ): boolean => {
684
+ const terminal = terminalSequenceByRun.get(candidateRunId);
685
+
686
+ return (
687
+ candidateRunId !== compactionRunId &&
688
+ terminal !== undefined &&
689
+ terminal < beforeSequence &&
690
+ terminal > (lastResponseSequenceByRun.get(candidateRunId) ?? 0)
691
+ );
692
+ };
693
+
694
+ const incompleteResponseSequences: Array<{ readonly sequence: number; readonly runId: RunId }> =
695
+ [];
696
+
697
+ let ownerPrefixSequence = seed?.firstSequence ?? Number.POSITIVE_INFINITY;
698
+
699
+ let protectedContext: Prompt.Prompt | undefined =
700
+ seed?.protectedContext === undefined
701
+ ? undefined
702
+ : yield* decodePromptMessages(seed.protectedContext);
703
+
704
+ if (settledCoverage > 0) {
705
+ yield* Stream.runForEach(recordsForRun, (envelope) =>
706
+ Effect.gen(function* () {
707
+ const payload = envelope.record.payload;
708
+
709
+ if (payload._tag !== "ModelResponseRecorded" || envelope.sequence > settledCoverage) return;
710
+ const messages = yield* decodePromptMessages(payload.messages);
711
+ const declared = declaredApplicationToolCallIds(messages);
712
+
713
+ for (const id of declared) {
714
+ const callId = yield* Schema.decodeEffect(ToolCallId)(id).pipe(
715
+ Effect.mapError((cause) =>
716
+ journalError("Failed to decode a declared Tool Call ID", cause),
717
+ ),
718
+ );
719
+
720
+ if (
721
+ !settledToolCallRecordIds.has(
722
+ toolCallSettledRecordId(payload.runId, payload.turn, callId),
723
+ )
724
+ ) {
725
+ incompleteResponseSequences.push({ sequence: envelope.sequence, runId: payload.runId });
726
+ break;
727
+ }
728
+ }
729
+ if (
730
+ payload.runId === ownerRunId &&
731
+ payload.turn === 1 &&
732
+ payload.runScopedPrefixLength !== undefined
733
+ ) {
734
+ ownerPrefixSequence = envelope.sequence;
735
+ protectedContext = Prompt.fromMessages(
736
+ messages.content.slice(0, payload.runScopedPrefixLength),
737
+ );
738
+ }
739
+ }),
740
+ );
741
+ }
742
+
743
+ const boundIsValid = (payload: CompactionCreated, ownSequence: number): boolean => {
744
+ const { runId, coversThrough } = payload;
745
+
746
+ if (coversThrough <= 0 || coversThrough >= ownSequence) return false;
747
+ if (seed !== undefined && ownSequence === seed.compaction.sequence)
748
+ return (
749
+ seed.compaction.record.payload._tag === "CompactionCreated" &&
750
+ Schema.toEquivalence(CompactionCreated)(payload, seed.compaction.record.payload)
751
+ );
752
+ const ownerFirst = firstSequenceByRun.get(runId);
753
+
754
+ if (payload.kind === "summarize" && ownerFirst !== undefined && coversThrough >= ownerFirst)
755
+ return false;
756
+ if (
757
+ payload.kind !== "summarize" &&
758
+ incompleteResponseSequences.some(
759
+ (response) =>
760
+ response.sequence <= coversThrough &&
761
+ isInRunView(
762
+ response.sequence,
763
+ { _tag: "ModelResponseRecorded", runId: response.runId },
764
+ runId,
765
+ ) &&
766
+ !isTerminalPriorRun(response.runId, runId, ownSequence),
767
+ )
768
+ )
769
+ return false;
770
+ for (const span of settledSpans) {
771
+ if (
772
+ span.from <= coversThrough &&
773
+ coversThrough < span.to &&
774
+ isInRunView(span.from, { _tag: "ModelResponseRecorded", runId: span.runId }, runId)
775
+ )
776
+ return false;
777
+ }
778
+
779
+ return true;
780
+ };
781
+
782
+ type CompactionView = (typeof compactions)[number];
783
+
784
+ let replacements: ReadonlyArray<CompactionView> = [];
785
+ let clearings: ReadonlyArray<CompactionView> = [];
786
+
787
+ const retainCompaction = (views: ReadonlyArray<CompactionView>, next: CompactionView) => {
788
+ // Independent Run views overlap without containing one another. Retire an overlay only
789
+ // when the replacement actually contains its view, not merely a larger sequence number.
790
+ if (
791
+ views.some(
792
+ (prior) =>
793
+ prior.payload.coversThrough > next.payload.coversThrough &&
794
+ isInRunView(next.sequence, next.payload, prior.payload.runId),
795
+ )
796
+ )
797
+ return views;
798
+
799
+ return [
800
+ ...views.filter(
801
+ (prior) =>
802
+ next.payload.coversThrough < prior.payload.coversThrough ||
803
+ !isInRunView(prior.sequence, prior.payload, next.payload.runId),
804
+ ),
805
+ next,
806
+ ];
807
+ };
808
+
809
+ let latestWindowId: string | undefined = seed?.contextWindowId;
810
+ let latestWindowSequence = seed?.throughSequence ?? -1;
811
+ let rolloverCoveredThrough = 0;
812
+
813
+ for (const { payload, sequence } of compactions) {
814
+ if (!boundIsValid(payload, sequence)) continue;
815
+ if (payload.kind === "rollover" && sequence > latestWindowSequence) {
816
+ latestWindowId = contextWindowId(payload.runId, payload.turn);
817
+ latestWindowSequence = sequence;
818
+ }
819
+ if (payload.kind === "rollover")
820
+ rolloverCoveredThrough = Math.max(rolloverCoveredThrough, payload.coversThrough);
821
+ if (payload.kind === "summarize" || payload.kind === "rollover") {
822
+ if (payload.kind === "summarize" && payload.summary === undefined) continue;
823
+ replacements = retainCompaction(replacements, { payload, sequence });
824
+ } else clearings = retainCompaction(clearings, { payload, sequence });
825
+ }
826
+ let summaryEmitted = false;
827
+
828
+ const retainedPrefix = replacements.some(
829
+ ({ payload }) =>
830
+ payload.kind === "rollover" &&
831
+ payload.runId === ownerRunId &&
832
+ ownerPrefixSequence <= payload.coversThrough,
833
+ )
834
+ ? (protectedContext?.content ?? [])
835
+ : [];
836
+
837
+ const replacementLength = retainedPrefix.length + replacements.length;
838
+
839
+ if (seed?.frontier !== undefined)
840
+ onBoundary?.({ ...seed.frontier, promptLength: replacementLength });
841
+
842
+ const emitSummary = () => {
843
+ if (summaryEmitted || replacements.length === 0) return;
844
+ summaryEmitted = true;
845
+ state.all.push(...retainedPrefix);
846
+ for (const { payload: replacement } of replacements) {
847
+ const message =
848
+ replacement.kind === "rollover"
849
+ ? contextWindowMessage(
850
+ contextWindowId(replacement.runId, replacement.turn),
851
+ replacement.handoff,
852
+ )
853
+ : Prompt.makeMessage("user", {
854
+ content: [
855
+ Prompt.makePart("text", {
856
+ text: `${COMPACTION_SUMMARY_PREFIX}${replacement.summary}`,
857
+ }),
858
+ ],
859
+ });
860
+
861
+ state.all.push(message);
862
+ if (replacement.kind !== "rollover" || replacement.runId !== ownerRunId)
863
+ state.before.push(message);
864
+ }
865
+ };
866
+
867
+ const modelUsage: Array<ModelCallUsage> = [];
868
+ let unobservedModelCalls = seed?.unobservedModelCalls ?? 0;
869
+
870
+ const usage = {
871
+ modelCalls: seed?.modelCalls ?? 0,
872
+ inputTokens: seed?.inputTokens ?? 0,
873
+ outputTokens: seed?.outputTokens ?? 0,
874
+ lastInputTokens: seed?.lastInputTokens ?? 0,
875
+ lastOutputTokens: seed?.lastOutputTokens ?? 0,
876
+ costMicrousd: seed?.costMicrousd ?? 0,
877
+ modelUsage,
878
+ ...(seed === undefined ? {} : { summarizedModelUsage: seed.summarizedModelUsage }),
879
+ };
880
+
881
+ let toolSelection = seed?.toolSelection;
882
+ let usageTurn = seed?.committedTurns ?? 0;
883
+
884
+ const incompleteToolTurns = new Set<string>();
885
+ const incompleteToolCalls = new Set<string>();
886
+ let pendingContextToolCallId: string | undefined;
887
+ let ownerTerminated = false;
888
+
889
+ const policyUsage = {
890
+ committedTurns: seed?.policyUsage.committedTurns ?? 0,
891
+ toolCalls: seed?.policyUsage.toolCalls ?? 0,
892
+ programmaticToolCalls: seed?.policyUsage.programmaticToolCalls ?? 0,
893
+ consecutiveToolFailures: seed?.policyUsage.consecutiveToolFailures ?? 0,
894
+ finalizationUsed: seed?.policyUsage.finalizationUsed ?? false,
895
+ };
896
+
897
+ const accountResponse = Effect.fn("RunJournal.accountResponse")(function* (
898
+ envelope: CanonicalRecordEnvelope,
899
+ payload: ModelResponseRecorded,
900
+ messages: Prompt.Prompt,
901
+ ) {
902
+ const record = envelope.record;
903
+
904
+ const declared = declaredApplicationToolCallIds(messages);
905
+ const declaredRecordIds: Array<RecordId> = [];
906
+
907
+ for (const id of declared) {
908
+ const toolCallId = yield* Effect.try({
909
+ try: () => decodeToolCallId(id),
910
+ catch: (cause) => journalError("Failed to decode a declared Tool Call ID", cause),
911
+ });
912
+
913
+ declaredRecordIds.push(toolCallSettledRecordId(payload.runId, payload.turn, toolCallId));
914
+ }
915
+ if (declaredRecordIds.some((recordId) => !settledToolCallRecordIds.has(recordId))) {
916
+ incompleteToolTurns.add(envelope.record.recordId);
917
+ for (const recordId of declaredRecordIds) incompleteToolCalls.add(recordId);
918
+ }
919
+ if (payload.runId !== ownerRunId) return;
920
+ if (seed !== undefined && envelope.sequence <= seed.throughSequence) return;
921
+ if (payload.turn === 1 && payload.runScopedPrefixLength !== undefined) {
922
+ protectedContext = Prompt.fromMessages(
923
+ messages.content.slice(0, payload.runScopedPrefixLength),
924
+ );
925
+ }
926
+
927
+ const responseUsage = yield* projectedResponseUsage(payload);
928
+
929
+ unobservedModelCalls = yield* addProjectedUsage(
930
+ "unobservedModelCalls",
931
+ unobservedModelCalls,
932
+ payload.unobservedModelCalls ?? 0,
933
+ );
934
+
935
+ usage.modelCalls = yield* addProjectedUsage(
936
+ "modelCalls",
937
+ usage.modelCalls,
938
+ responseUsage.modelCalls,
939
+ );
940
+ usage.inputTokens = yield* addProjectedUsage(
941
+ "inputTokens",
942
+ usage.inputTokens,
943
+ responseUsage.inputTokens,
944
+ );
945
+ usage.outputTokens = yield* addProjectedUsage(
946
+ "outputTokens",
947
+ usage.outputTokens,
948
+ responseUsage.outputTokens,
949
+ );
950
+ usage.costMicrousd = yield* addProjectedUsage(
951
+ "costMicrousd",
952
+ usage.costMicrousd,
953
+ responseUsage.costMicrousd,
954
+ );
955
+ usage.modelUsage.push(...responseUsage.modelUsage);
956
+ if (payload.turn > usageTurn) {
957
+ usageTurn = payload.turn;
958
+ usage.lastInputTokens = responseUsage.inputTokens;
959
+ usage.lastOutputTokens = responseUsage.outputTokens;
960
+ }
961
+
962
+ policyUsage.committedTurns = Math.max(policyUsage.committedTurns, payload.turn);
963
+
964
+ const calls = messages.content.flatMap((message) =>
965
+ message.role === "assistant"
966
+ ? message.content.filter((part) => part.type === "tool-call")
967
+ : [],
968
+ );
969
+
970
+ const candidate = calls.length === 1 ? calls[0] : undefined;
971
+
972
+ const candidateResult =
973
+ candidate === undefined
974
+ ? undefined
975
+ : settledById.get(`tool-settled:${ownerRunId}:${payload.turn}:${candidate.id}`);
976
+
977
+ pendingContextToolCallId =
978
+ candidate !== undefined &&
979
+ !candidate.providerExecuted &&
980
+ candidateResult?.isFailure === false &&
981
+ candidateResult.budgetRejected !== true &&
982
+ envelope.sequence > rolloverCoveredThrough
983
+ ? candidate.id
984
+ : undefined;
985
+
986
+ policyUsage.toolCalls += calls.length;
987
+ if (payload.toolExposure !== undefined) toolSelection = payload.toolExposure.selection;
988
+ if (incompleteToolTurns.has(record.recordId)) return;
989
+ for (const call of calls) {
990
+ const result = call.providerExecuted
991
+ ? messages.content
992
+ .flatMap((message) => (message.role === "assistant" ? message.content : []))
993
+ .find(
994
+ (part) => part.type === "tool-result" && part.providerExecuted && part.id === call.id,
995
+ )
996
+ : settledById.get(`tool-settled:${ownerRunId}:${payload.turn}:${call.id}`);
997
+
998
+ if (result === undefined || ("budgetRejected" in result && result.budgetRejected === true))
999
+ continue;
1000
+ if (!("isFailure" in result)) continue;
1001
+ if (!result.isFailure && "toolSelection" in result && result.toolSelection !== undefined)
1002
+ toolSelection = result.toolSelection;
1003
+ policyUsage.consecutiveToolFailures = result.isFailure
1004
+ ? policyUsage.consecutiveToolFailures + 1
1005
+ : 0;
1006
+ }
1007
+ });
1008
+
1009
+ // Slots refer to the retained Prompt itself, not an additional history payload index.
1010
+ // Only still-unseen results retain a slot; late results fill the original declaration in place.
1011
+ const historicalResults = new Map<
1012
+ string,
1013
+ {
1014
+ readonly allIndex: number;
1015
+ readonly beforeIndex: number;
1016
+ readonly parts: Array<Prompt.ToolResultPart>;
1017
+ readonly partIndex: number;
1018
+ }
1019
+ >();
1020
+
1021
+ let pendingToolOrder = new Map<string, number>();
1022
+
1023
+ const flushTools = Effect.fn("RunJournal.flushTools")(function* (
1024
+ current: FoldState,
1025
+ ): Effect.fn.Return<FoldState, RunJournalError> {
1026
+ if (current.pendingTools.length === 0) return current;
1027
+
1028
+ // Suspension can persist an ordinary sibling before a delegated call joins. Model context
1029
+ // still uses declaration order, matching the live interpreter's completed results batch.
1030
+ const ordered = current.pendingTools.toSorted(
1031
+ (left, right) =>
1032
+ (pendingToolOrder.get(left.record.toolCallId) ?? Number.MAX_SAFE_INTEGER) -
1033
+ (pendingToolOrder.get(right.record.toolCallId) ?? Number.MAX_SAFE_INTEGER),
1034
+ );
1035
+
1036
+ const toolMessage = yield* toolMessageFromSettled(ordered);
1037
+
1038
+ current.all.push(toolMessage);
1039
+ if (!current.pendingToolsForRun) current.before.push(toolMessage);
1040
+
1041
+ return {
1042
+ ...current,
1043
+ pendingTools: [],
1044
+ pendingToolsForRun: false,
1045
+ };
1046
+ });
1047
+
1048
+ yield* Stream.runForEach(recordsForRun, (envelope) =>
1049
+ Effect.gen(function* () {
1050
+ const payload = envelope.record.payload;
1051
+
1052
+ if (
1053
+ ((payload._tag === "RunCompleted" || payload._tag === "RunFailed") &&
1054
+ payload.runId === ownerRunId) ||
1055
+ (payload._tag === "SubmissionSettled" &&
1056
+ runIdForSubmission(payload.submissionId) === ownerRunId)
1057
+ )
1058
+ ownerTerminated = true;
1059
+
1060
+ if (payload._tag === "RunPolicyUsageReserved" && payload.runId === ownerRunId) {
1061
+ if (seed !== undefined && envelope.sequence <= seed.throughSequence) return;
1062
+ if (
1063
+ payload.programmaticToolCalls < policyUsage.programmaticToolCalls ||
1064
+ (policyUsage.finalizationUsed && !payload.finalizationUsed)
1065
+ ) {
1066
+ return yield* journalError("Run policy reservations must be monotonic");
1067
+ }
1068
+ policyUsage.programmaticToolCalls = payload.programmaticToolCalls;
1069
+ policyUsage.finalizationUsed = payload.finalizationUsed;
1070
+ }
1071
+ if (PROMPT_TRANSPARENT_TAGS.has(payload._tag)) return;
1072
+ // The compaction record governs the fold (pre-scan) and contributes no
1073
+ // message of its own; records at or below the summarize bound render as
1074
+ // the one summary message emitted at the covered/kept transition.
1075
+ if (payload._tag === "CompactionCreated") return;
1076
+ if (
1077
+ seed !== undefined &&
1078
+ envelope.sequence <= seed.throughSequence &&
1079
+ (payload._tag === "ModelResponseRecorded" || payload._tag === "ToolCallSettled")
1080
+ )
1081
+ return;
1082
+ if (replacements.some(({ payload }) => isCovered(envelope, payload))) {
1083
+ // Retiring Prompt payloads does not retire the owning Run's policy or usage accounting.
1084
+ if (payload._tag === "ModelResponseRecorded" && payload.runId === ownerRunId) {
1085
+ const messages = yield* decodePromptMessages(payload.messages);
1086
+
1087
+ yield* accountResponse(envelope, payload, messages);
1088
+ state = { ...state, committedTurns: Math.max(state.committedTurns, payload.turn) };
1089
+ }
1090
+ if (payload._tag === "ModelResponseRecorded" || payload._tag === "ToolCallSettled") {
1091
+ onBoundary?.({
1092
+ sequence: envelope.sequence,
1093
+ tag: payload._tag,
1094
+ promptLength: Math.max(replacementLength, state.all.length),
1095
+ ...(isTerminalPriorRun(payload.runId, ownerRunId, Number.POSITIVE_INFINITY)
1096
+ ? { terminalPriorRun: true }
1097
+ : {}),
1098
+ ...(incompleteToolTurns.has(envelope.record.recordId) ||
1099
+ incompleteToolCalls.has(envelope.record.recordId)
1100
+ ? { incomplete: true }
1101
+ : {}),
1102
+ });
1103
+ }
1104
+
1105
+ return;
1106
+ }
1107
+ emitSummary();
1108
+ if (payload._tag === "ToolCallSettled") {
1109
+ if (payload.runId !== ownerRunId) {
1110
+ const slot = historicalResults.get(envelope.record.recordId);
1111
+
1112
+ if (slot === undefined)
1113
+ return yield* journalError("Historical Tool result has no matching declaration");
1114
+ const declared = slot.parts[slot.partIndex];
1115
+
1116
+ if (declared?.id !== payload.toolCallId || declared.name !== payload.toolName)
1117
+ return yield* journalError("Historical Tool result differs from its declaration");
1118
+ slot.parts[slot.partIndex] = Prompt.makePart("tool-result", {
1119
+ id: payload.toolCallId,
1120
+ name: payload.toolName,
1121
+ result: clearings.some(({ payload }) => isCovered(envelope, payload))
1122
+ ? CLEARED_TOOL_RESULT
1123
+ : payload.result,
1124
+ isFailure: payload.isFailure,
1125
+ providerExecuted: false,
1126
+ });
1127
+ const message = Prompt.makeMessage("tool", { content: [...slot.parts] });
1128
+
1129
+ state.all[slot.allIndex] = message;
1130
+ state.before[slot.beforeIndex] = message;
1131
+ historicalResults.delete(envelope.record.recordId);
1132
+ onBoundary?.({
1133
+ sequence: envelope.sequence,
1134
+ tag: payload._tag,
1135
+ promptLength: state.all.length,
1136
+ ...(incompleteToolCalls.has(envelope.record.recordId) ? { incomplete: true } : {}),
1137
+ ...(isTerminalPriorRun(payload.runId, ownerRunId, Number.POSITIVE_INFINITY)
1138
+ ? { terminalPriorRun: true }
1139
+ : {}),
1140
+ });
1141
+
1142
+ return;
1143
+ }
1144
+ if (
1145
+ state.pendingTools.length > 0 &&
1146
+ state.pendingToolsForRun !== (payload.runId === ownerRunId)
1147
+ ) {
1148
+ state = yield* flushTools(state);
1149
+ }
1150
+ state.pendingTools.push({
1151
+ record: payload,
1152
+ cleared: clearings.some(({ payload }) => isCovered(envelope, payload)),
1153
+ });
1154
+ state = {
1155
+ ...state,
1156
+ pendingToolsForRun: payload.runId === ownerRunId,
1157
+ };
1158
+ onBoundary?.({
1159
+ sequence: envelope.sequence,
1160
+ tag: payload._tag,
1161
+ promptLength: state.all.length + 1,
1162
+ ...(isTerminalPriorRun(payload.runId, ownerRunId, Number.POSITIVE_INFINITY)
1163
+ ? { terminalPriorRun: true }
1164
+ : {}),
1165
+ ...(incompleteToolCalls.has(envelope.record.recordId) ? { incomplete: true } : {}),
1166
+ });
1167
+
1168
+ return;
1169
+ }
1170
+ state = yield* flushTools(state);
1171
+ if (payload._tag === "ModelCompleted" && payload.messages !== undefined) {
1172
+ const messages = yield* decodePromptMessages(payload.messages);
1173
+
1174
+ for (const message of messages.content) {
1175
+ state.all.push(message);
1176
+ if (payload.runId !== ownerRunId) state.before.push(message);
1177
+ }
1178
+
1179
+ return;
1180
+ }
1181
+ if (payload._tag !== "ModelResponseRecorded") return;
1182
+ const messages = yield* decodePromptMessages(payload.messages);
1183
+ const forRun = payload.runId === ownerRunId;
1184
+
1185
+ pendingToolOrder = new Map(
1186
+ declaredApplicationToolCallIds(messages).map((id, index) => [id, index]),
1187
+ );
1188
+
1189
+ yield* accountResponse(envelope, payload, messages);
1190
+
1191
+ const visibleMessages = forRun
1192
+ ? messages.content
1193
+ : messages.content.filter((message) => message.role !== "system");
1194
+
1195
+ for (const message of visibleMessages) {
1196
+ state.all.push(message);
1197
+ if (!forRun) state.before.push(message);
1198
+ }
1199
+ if (!forRun) {
1200
+ const calls = messages.content.flatMap((message) =>
1201
+ message.role === "assistant"
1202
+ ? message.content.filter(
1203
+ (part): part is Prompt.ToolCallPart =>
1204
+ part.type === "tool-call" && !part.providerExecuted,
1205
+ )
1206
+ : [],
1207
+ );
1208
+
1209
+ if (calls.length > 0) {
1210
+ const parts = yield* Effect.forEach(calls, (call) =>
1211
+ Effect.gen(function* () {
1212
+ const callId = yield* Schema.decodeEffect(ToolCallId)(call.id).pipe(
1213
+ Effect.mapError((cause) => journalError("Invalid historical Tool Call ID", cause)),
1214
+ );
1215
+
1216
+ const operation = payload.toolOperations?.find(
1217
+ (operation) => operation.toolCallId === callId && operation.toolName === call.name,
1218
+ );
1219
+
1220
+ // Only the recorded execution contract can prove that dispatch required a
1221
+ // preparation. Ordinary readonly calls may execute without one.
1222
+ const notExecuted =
1223
+ operation !== undefined &&
1224
+ (operation.executionClass !== "readonly" ||
1225
+ operation.executionKind !== "ordinary") &&
1226
+ !toolExecutionEvidence.has(
1227
+ toolExecutionKey({
1228
+ runId: payload.runId,
1229
+ turn: payload.turn,
1230
+ toolCallId: callId,
1231
+ }),
1232
+ );
1233
+
1234
+ return Prompt.makePart("tool-result", {
1235
+ id: call.id,
1236
+ name: call.name,
1237
+ result: notExecuted
1238
+ ? {
1239
+ _tag: "ToolUnavailable",
1240
+ toolName: call.name,
1241
+ execution: "not-executed",
1242
+ message:
1243
+ "This earlier call never reached durable preparation and was not executed.",
1244
+ }
1245
+ : {
1246
+ _tag: "ToolOutcomeUnknown",
1247
+ message:
1248
+ "This earlier operation has no recorded outcome. It may have executed. Do not assume success or retry it; its original operation remains unresolved.",
1249
+ },
1250
+ isFailure: true,
1251
+ providerExecuted: false,
1252
+ });
1253
+ }),
1254
+ );
1255
+
1256
+ const allIndex = state.all.length;
1257
+ const beforeIndex = state.before.length;
1258
+ const message = Prompt.makeMessage("tool", { content: parts });
1259
+
1260
+ state.all.push(message);
1261
+ state.before.push(message);
1262
+ for (const [partIndex, call] of calls.entries()) {
1263
+ const callId = yield* Schema.decodeEffect(ToolCallId)(call.id).pipe(
1264
+ Effect.mapError((cause) => journalError("Invalid historical Tool Call ID", cause)),
1265
+ );
1266
+
1267
+ historicalResults.set(toolCallSettledRecordId(payload.runId, payload.turn, callId), {
1268
+ allIndex,
1269
+ beforeIndex,
1270
+ parts,
1271
+ partIndex,
1272
+ });
1273
+ }
1274
+ }
1275
+ }
1276
+ state = {
1277
+ ...state,
1278
+ committedTurns: forRun
1279
+ ? Math.max(state.committedTurns, payload.turn)
1280
+ : state.committedTurns,
1281
+ };
1282
+ onBoundary?.({
1283
+ sequence: envelope.sequence,
1284
+ tag: payload._tag,
1285
+ promptLength: state.all.length,
1286
+ ...(isTerminalPriorRun(payload.runId, ownerRunId, Number.POSITIVE_INFINITY)
1287
+ ? { terminalPriorRun: true }
1288
+ : {}),
1289
+ ...(incompleteToolTurns.has(envelope.record.recordId) ? { incomplete: true } : {}),
1290
+ });
1291
+ }),
1292
+ );
1293
+ emitSummary();
1294
+ state = yield* flushTools(state);
1295
+
1296
+ const validatedPolicyUsage = yield* Schema.decodeEffect(RunPolicyUsage)(policyUsage).pipe(
1297
+ Effect.mapError((cause) =>
1298
+ journalError("Run policy accounting exceeds its Schema bounds", cause),
1299
+ ),
1300
+ );
1301
+
1302
+ return {
1303
+ ...(toolSelection === undefined ? {} : { toolSelection }),
1304
+ policyUsage: validatedPolicyUsage,
1305
+ prompt: Prompt.fromMessages(state.all),
1306
+ historyBefore: Prompt.fromMessages(state.before),
1307
+ committedTurns: state.committedTurns,
1308
+ usage: unobservedModelCalls === 0 ? usage : { ...usage, unobservedModelCalls },
1309
+ ...(latestWindowId === undefined ? {} : { contextWindowId: latestWindowId }),
1310
+ ...(protectedContext === undefined ? {} : { protectedContext }),
1311
+ ...(ownerTerminated || pendingContextToolCallId === undefined
1312
+ ? {}
1313
+ : { pendingContextToolCallId }),
1314
+ };
1315
+ });
1316
+
1317
+ /** Pure projection of one Run's durable recovery state from canonical records. */
1318
+ export const projectRunJournal = Effect.fn("RunJournal.projectRunJournal")(
1319
+ (
1320
+ records: ReadonlyArray<CanonicalRecordEnvelope>,
1321
+ runId: RunId,
1322
+ ): Effect.Effect<RunJournalProjection, RunJournalError> =>
1323
+ projectRunJournalStream(Stream.fromIterable(records), runId),
1324
+ );
1325
+
1326
+ /**
1327
+ * Pure valid-prompt projection from canonical records: `UserInputRecorded` +
1328
+ * `ModelResponseRecorded` + complete `ToolCallSettled` batches → the deterministic model-visible
1329
+ * Prompt (plan §Coordinator flow step 3).
1330
+ */
1331
+ export const promptFromCanonicalRecords = Effect.fn("RunJournal.promptFromCanonicalRecords")(
1332
+ (
1333
+ records: ReadonlyArray<CanonicalRecordEnvelope>,
1334
+ ): Effect.Effect<Prompt.Prompt, RunJournalError> =>
1335
+ projectRunJournalStream(Stream.fromIterable(records), undefined).pipe(
1336
+ Effect.map((projection) => projection.prompt),
1337
+ ),
1338
+ );
1339
+
1340
+ /** Everything one committed Turn contributes to its canonical batch. */
1341
+ /**
1342
+ * Staged usage is validated, never repaired: clamping negatives or truncating
1343
+ * fractions would under-record canonical usage, and NaN/Infinity must fail
1344
+ * typed instead of escaping as a record-construction defect (RUN-023).
1345
+ */
1346
+ const validStagedUsage = (label: string, value: number): Effect.Effect<number, RunJournalError> =>
1347
+ Number.isSafeInteger(value) && value >= 0
1348
+ ? Effect.succeed(value)
1349
+ : Effect.fail(
1350
+ journalError(`Staged ${label} must be a non-negative safe integer, got ${String(value)}`),
1351
+ );
1352
+
1353
+ export interface TurnCommitInput {
1354
+ readonly toolOperations?: ModelResponseRecorded["toolOperations"] | undefined;
1355
+ readonly toolParameterRejections?: ReadonlyArray<ToolParameterRejection> | undefined;
1356
+ readonly toolExposure?: Snapshot | undefined;
1357
+ readonly toolSelections?: ReadonlyMap<string, Selection> | undefined;
1358
+ readonly budgetRejectedCalls?: ReadonlySet<string>;
1359
+ readonly runId: RunId;
1360
+ /** Canonical (Run-relative, Attempt-independent) Turn number; must be positive. */
1361
+ readonly turn: number;
1362
+ readonly turnId: TurnId;
1363
+ /**
1364
+ * The Prompt messages this Turn appended to official history, in order, including the Turn's
1365
+ * Tool message when application Tools ran. Tool messages become `ToolCallSettled` records; the
1366
+ * remaining messages become the Turn's `ModelResponseRecorded.messages`.
1367
+ */
1368
+ readonly appended: ReadonlyArray<Prompt.Message>;
1369
+ readonly producerId: ProducerId;
1370
+ readonly deploymentId: DeploymentId;
1371
+ readonly createdAt: DateTime.Utc;
1372
+ /** Leading instruction/wake messages that stay canonical but are hidden from later Runs. */
1373
+ readonly runScopedPrefixLength?: number | undefined;
1374
+ /** Terminal output committed atomically with this Turn's final canonical batch. */
1375
+ readonly runCompletion?:
1376
+ | {
1377
+ readonly output: PersistedJson;
1378
+ readonly runDisposition?: PersistedJson | undefined;
1379
+ readonly finishReason?: "budget-exhausted" | undefined;
1380
+ readonly exhausted?: ExhaustedLimit | undefined;
1381
+ }
1382
+ | undefined;
1383
+ /** Known missing-accounting invocations staged before this canonical response. */
1384
+ readonly unobservedModelCalls?: number | undefined;
1385
+ /** Per-call provider usage staged by the engine's `noteTurnUsage` (RUN-023). */
1386
+ readonly usage?:
1387
+ | {
1388
+ readonly inputTokens: number;
1389
+ readonly outputTokens: number;
1390
+ readonly costMicrousd?: number | undefined;
1391
+ readonly modelUsage?: ReadonlyArray<ModelCallUsage> | undefined;
1392
+ }
1393
+ | undefined;
1394
+ }
1395
+
1396
+ const decodePersistedJson = Schema.decodeUnknownEffect(PersistedJson);
1397
+ const encodePrompt = Schema.encodeEffect(Prompt.Prompt);
1398
+ const decodeModelUsage = Schema.decodeUnknownEffect(Schema.Array(ModelCallUsage));
1399
+
1400
+ const requireCanonicalTurn = (turn: number): Effect.Effect<void, RunJournalError> =>
1401
+ !Number.isInteger(turn) || turn <= 0
1402
+ ? Effect.fail(journalError(`Canonical turn number must be a positive integer: ${turn}`))
1403
+ : Effect.void;
1404
+
1405
+ interface SplitTurnMessages {
1406
+ readonly promptMessages: ReadonlyArray<Prompt.Message>;
1407
+ readonly toolParts: ReadonlyArray<Prompt.ToolResultPart>;
1408
+ }
1409
+
1410
+ const splitTurnMessages = (appended: ReadonlyArray<Prompt.Message>): SplitTurnMessages => {
1411
+ const promptMessages: Array<Prompt.Message> = [];
1412
+ const toolParts: Array<Prompt.ToolResultPart> = [];
1413
+
1414
+ for (const message of appended) {
1415
+ if (message.role !== "tool") {
1416
+ promptMessages.push(message);
1417
+ continue;
1418
+ }
1419
+ for (const part of message.content) {
1420
+ if (part.type !== "tool-result") continue;
1421
+ toolParts.push(part);
1422
+ }
1423
+ }
1424
+
1425
+ return { promptMessages, toolParts };
1426
+ };
1427
+
1428
+ const modelResponseRecord = Effect.fn("RunJournal.modelResponseRecord")(function* (
1429
+ input: TurnCommitInput,
1430
+ promptMessages: ReadonlyArray<Prompt.Message>,
1431
+ ): Effect.fn.Return<RecordEnvelope, RunJournalError | DigestError, Crypto.Crypto> {
1432
+ if (promptMessages.length === 0) {
1433
+ return yield* journalError(`Turn ${input.turn} appended no model-visible Prompt messages`);
1434
+ }
1435
+ const runScopedPrefixLength = input.runScopedPrefixLength;
1436
+
1437
+ if (
1438
+ runScopedPrefixLength !== undefined &&
1439
+ (input.turn !== 1 ||
1440
+ !Number.isSafeInteger(runScopedPrefixLength) ||
1441
+ runScopedPrefixLength <= 0 ||
1442
+ runScopedPrefixLength >= promptMessages.length ||
1443
+ promptMessages
1444
+ .slice(0, runScopedPrefixLength)
1445
+ .some((message) => message.role !== "system" && message.role !== "user"))
1446
+ ) {
1447
+ return yield* journalError(
1448
+ "Run-scoped Prompt provenance must identify a non-empty system/user prefix of Turn 1 before its assistant response",
1449
+ );
1450
+ }
1451
+
1452
+ const encodedMessages = yield* encodePrompt(Prompt.fromMessages([...promptMessages])).pipe(
1453
+ Effect.mapError((cause) => journalError("Turn Prompt messages failed to encode", cause)),
1454
+ );
1455
+
1456
+ const messages = yield* decodePersistedJson(encodedMessages).pipe(
1457
+ Effect.mapError((cause) =>
1458
+ journalError("Turn Prompt messages exceed canonical persistence bounds", cause),
1459
+ ),
1460
+ );
1461
+
1462
+ const messagesDigest = yield* digestJson(messages);
1463
+
1464
+ const modelUsage =
1465
+ input.usage?.modelUsage === undefined
1466
+ ? undefined
1467
+ : yield* decodeModelUsage(input.usage.modelUsage).pipe(
1468
+ Effect.mapError((cause) => journalError("Turn model usage failed to decode", cause)),
1469
+ );
1470
+
1471
+ if (modelUsage !== undefined) {
1472
+ if (modelUsage.length === 0) {
1473
+ return yield* journalError("Turn model usage must contain at least one completed call");
1474
+ }
1475
+
1476
+ const summary = yield* summarizeModelUsage(modelUsage).pipe(
1477
+ Effect.mapError((cause) => journalError("Turn model usage exceeds accounting bounds", cause)),
1478
+ );
1479
+
1480
+ if (
1481
+ input.usage === undefined ||
1482
+ input.usage.inputTokens !== summary.inputTokens.total ||
1483
+ input.usage.outputTokens !== summary.outputTokens.total ||
1484
+ (input.usage.costMicrousd ?? 0) !== summary.costMicrousd
1485
+ ) {
1486
+ return yield* journalError("Turn detailed and aggregate model usage disagree");
1487
+ }
1488
+ }
1489
+
1490
+ return RecordEnvelope.make({
1491
+ recordId: modelResponseRecordId(input.runId, input.turn),
1492
+ family: "thread",
1493
+ schemaVersion: 1,
1494
+ createdAt: input.createdAt,
1495
+ deploymentId: input.deploymentId,
1496
+ payload: ModelResponseRecorded.make({
1497
+ ...(input.toolOperations === undefined ? {} : { toolOperations: input.toolOperations }),
1498
+ ...(input.toolParameterRejections === undefined || input.toolParameterRejections.length === 0
1499
+ ? {}
1500
+ : { toolParameterRejections: input.toolParameterRejections }),
1501
+ ...(input.toolExposure === undefined ? {} : { toolExposure: input.toolExposure }),
1502
+ runId: input.runId,
1503
+ turnId: input.turnId,
1504
+ turn: input.turn,
1505
+ messages,
1506
+ messagesDigest,
1507
+ ...(runScopedPrefixLength === undefined ? {} : { runScopedPrefixLength }),
1508
+ ...(modelUsage === undefined ? {} : { modelUsage }),
1509
+ ...(input.unobservedModelCalls === undefined || input.unobservedModelCalls === 0
1510
+ ? {}
1511
+ : {
1512
+ unobservedModelCalls: yield* validStagedUsage(
1513
+ "unobservedModelCalls",
1514
+ input.unobservedModelCalls,
1515
+ ),
1516
+ }),
1517
+ ...(input.usage === undefined
1518
+ ? {}
1519
+ : {
1520
+ inputTokens: yield* validStagedUsage("inputTokens", input.usage.inputTokens),
1521
+ outputTokens: yield* validStagedUsage("outputTokens", input.usage.outputTokens),
1522
+ // Written only when non-zero: absent re-seeds as zero, so the
1523
+ // no-estimator case stays byte-identical to pre-cost histories.
1524
+ ...(input.usage.costMicrousd === undefined || input.usage.costMicrousd === 0
1525
+ ? {}
1526
+ : {
1527
+ costMicrousd: yield* validStagedUsage("costMicrousd", input.usage.costMicrousd),
1528
+ }),
1529
+ }),
1530
+ }),
1531
+ });
1532
+ });
1533
+
1534
+ const toolSettledRecords = Effect.fn("RunJournal.toolSettledRecords")(function* (
1535
+ input: TurnCommitInput,
1536
+ toolParts: ReadonlyArray<Prompt.ToolResultPart>,
1537
+ ): Effect.fn.Return<Array<RecordEnvelope>, RunJournalError> {
1538
+ const toolRecords: Array<RecordEnvelope> = [];
1539
+
1540
+ for (const part of toolParts) {
1541
+ const result = yield* decodePersistedJson(part.result).pipe(
1542
+ Effect.mapError((cause) =>
1543
+ journalError(`Tool result ${part.id} exceeds canonical persistence bounds`, cause),
1544
+ ),
1545
+ );
1546
+
1547
+ const toolCallId = yield* Effect.try({
1548
+ try: () => decodeToolCallId(part.id),
1549
+ catch: (cause) => journalError(`Invalid Tool Call ID ${part.id}`, cause),
1550
+ });
1551
+
1552
+ toolRecords.push(
1553
+ RecordEnvelope.make({
1554
+ recordId: toolCallSettledRecordId(input.runId, input.turn, toolCallId),
1555
+ family: "thread",
1556
+ schemaVersion: 1,
1557
+ createdAt: input.createdAt,
1558
+ deploymentId: input.deploymentId,
1559
+ payload: ToolCallSettled.make({
1560
+ ...(input.toolSelections?.get(part.id) === undefined
1561
+ ? {}
1562
+ : { toolSelection: input.toolSelections.get(part.id) }),
1563
+ runId: input.runId,
1564
+ toolCallId,
1565
+ toolName: part.name,
1566
+ result,
1567
+ isFailure: part.isFailure,
1568
+ ...(input.budgetRejectedCalls?.has(part.id) === true ? { budgetRejected: true } : {}),
1569
+ }),
1570
+ }),
1571
+ );
1572
+ }
1573
+
1574
+ return toolRecords;
1575
+ });
1576
+
1577
+ /** Integrity of the original validated terminal values; never a current-code projection. */
1578
+ export const runCompletionDigest = (
1579
+ completion: Pick<
1580
+ RunCompleted,
1581
+ "runId" | "output" | "runDisposition" | "finishReason" | "exhausted"
1582
+ >,
1583
+ ) =>
1584
+ digestJson({
1585
+ runId: completion.runId,
1586
+ output: completion.output,
1587
+ runDisposition: completion.runDisposition ?? null,
1588
+ finishReason: completion.finishReason ?? null,
1589
+ exhausted: completion.exhausted ?? null,
1590
+ });
1591
+
1592
+ const runCompletionRecord = Effect.fn("RunJournal.runCompletionRecord")(function* (
1593
+ input: TurnCommitInput,
1594
+ ) {
1595
+ if (input.runCompletion === undefined) return undefined;
1596
+ const completion = { runId: input.runId, ...input.runCompletion };
1597
+
1598
+ return RecordEnvelope.make({
1599
+ recordId: runCompletedRecordId(input.runId),
1600
+ family: "thread",
1601
+ schemaVersion: 1,
1602
+ createdAt: input.createdAt,
1603
+ deploymentId: input.deploymentId,
1604
+ payload: RunCompleted.make({
1605
+ ...completion,
1606
+ resultDigest: yield* runCompletionDigest(completion),
1607
+ }),
1608
+ });
1609
+ });
1610
+
1611
+ /**
1612
+ * Pure per-Turn canonical batch builder (TurnCompleted seam fold, D6/D8): one
1613
+ * `ModelResponseRecorded` record plus one `ToolCallSettled` record per terminal Tool result, all
1614
+ * under the WP0-style deterministic identities, committed as ONE atomic batch. The same input
1615
+ * always yields byte-identical content, so an in-Attempt append retry is an honest batch replay.
1616
+ *
1617
+ * Phase 5 keeps this shape for Turns that declare no application Tool calls; their terminal
1618
+ * `RunCompleted` marker joins the response in this same atomic batch. Tool-declaring Turns split
1619
+ * into `turnResponseBatch` + `turnResultsBatch`.
1620
+ */
1621
+ export const turnCanonicalBatch = Effect.fn("RunJournal.turnCanonicalBatch")(function* (
1622
+ input: TurnCommitInput,
1623
+ ): Effect.fn.Return<CanonicalBatch, RunJournalError | DigestError, Crypto.Crypto> {
1624
+ yield* requireCanonicalTurn(input.turn);
1625
+ const { promptMessages, toolParts } = splitTurnMessages(input.appended);
1626
+ const modelResponse = yield* modelResponseRecord(input, promptMessages);
1627
+ const toolRecords = yield* toolSettledRecords(input, toolParts);
1628
+ const completionRecord = yield* runCompletionRecord(input);
1629
+
1630
+ return CanonicalBatch.make({
1631
+ batchId: turnBatchId(input.runId, input.turn),
1632
+ producerId: input.producerId,
1633
+ records:
1634
+ completionRecord === undefined
1635
+ ? [modelResponse, ...toolRecords]
1636
+ : [modelResponse, ...toolRecords, completionRecord],
1637
+ });
1638
+ });
1639
+
1640
+ /**
1641
+ * Commit 1 of a tool-declaring Turn: the Turn's `ModelResponseRecorded` record, including
1642
+ * provider results retained in assistant content before application Tools execute.
1643
+ * — pending steering plus the assistant response with its declared tool calls — under batch
1644
+ * identity `turn-response:{runId}:{turn}`. Committing the response before preparation creates
1645
+ * the provably-safe durability §15 window ("after model item commit, before tool preparation →
1646
+ * resume tool scheduling"): a crash there resumes the declared batch with no model re-invocation
1647
+ * and no Unknown. Tool messages in `appended` are ignored; the record identity is the same
1648
+ * `model-response:{runId}:{turn}` as the single-batch shape, so record-id assertions and the
1649
+ * prompt projection are unchanged.
1650
+ */
1651
+ export const turnResponseBatch = Effect.fn("RunJournal.turnResponseBatch")(function* (
1652
+ input: TurnCommitInput,
1653
+ ): Effect.fn.Return<CanonicalBatch, RunJournalError | DigestError, Crypto.Crypto> {
1654
+ yield* requireCanonicalTurn(input.turn);
1655
+ const { promptMessages } = splitTurnMessages(input.appended);
1656
+ const modelResponse = yield* modelResponseRecord(input, promptMessages);
1657
+
1658
+ return CanonicalBatch.make({
1659
+ batchId: turnResponseBatchId(input.runId, input.turn),
1660
+ producerId: input.producerId,
1661
+ records: [modelResponse],
1662
+ });
1663
+ });
1664
+
1665
+ /**
1666
+ * Commit 5 of a tool-declaring Turn (plan §2.1): the Turn's `ToolCallSettled` records in
1667
+ * declaration order under batch identity `turn-results:{runId}:{turn}` — the batch becomes
1668
+ * model-visible atomically. Non-tool messages in `appended` are ignored; a Turn without any
1669
+ * terminal Tool result has no results batch and fails typed.
1670
+ */
1671
+ export const turnResultsBatch = Effect.fn("RunJournal.turnResultsBatch")(function* (
1672
+ input: TurnCommitInput,
1673
+ ): Effect.fn.Return<CanonicalBatch, RunJournalError | DigestError, Crypto.Crypto> {
1674
+ yield* requireCanonicalTurn(input.turn);
1675
+ const { toolParts } = splitTurnMessages(input.appended);
1676
+ const toolRecords = yield* toolSettledRecords(input, toolParts);
1677
+ const first = toolRecords[0];
1678
+
1679
+ if (first === undefined) {
1680
+ return yield* journalError(`Turn ${input.turn} has no terminal Tool results to commit`);
1681
+ }
1682
+ if (input.runCompletion !== undefined && toolRecords.length !== 1) {
1683
+ return yield* journalError("A terminal Tool completion requires exactly one settled result");
1684
+ }
1685
+ const completionRecord = yield* runCompletionRecord(input);
1686
+
1687
+ return CanonicalBatch.make({
1688
+ batchId: turnResultsBatchId(input.runId, input.turn),
1689
+ producerId: input.producerId,
1690
+ records:
1691
+ completionRecord === undefined
1692
+ ? [first, ...toolRecords.slice(1)]
1693
+ : [first, ...toolRecords.slice(1), completionRecord],
1694
+ });
1695
+ });