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,3159 @@
1
+ import {
2
+ Cause,
3
+ Clock,
4
+ Context,
5
+ Crypto,
6
+ DateTime,
7
+ Duration,
8
+ Effect,
9
+ Option,
10
+ Schema,
11
+ Stream,
12
+ } from "effect";
13
+
14
+ import type * as Agent from "../../core/Agent.ts";
15
+ import { AgentPolicy } from "../../core/AgentPolicy.ts";
16
+ import { Update, UpdateError } from "../../core/AgentUpdates.ts";
17
+ import { ThreadId, type AgentId, type SubmissionId } from "../../core/Identifiers.ts";
18
+ import { MessageRef, type MessageStatus } from "../../core/Messaging.ts";
19
+ import { IdempotencyKey, Receipt } from "../../core/Receipt.ts";
20
+ import { SubagentDelegationCaps, SubagentGrant } from "../../core/SubagentContract.ts";
21
+ import {
22
+ type WorkerSummary,
23
+ type WorkerStarted,
24
+ WorkerCompletion,
25
+ FrameworkMessage,
26
+ WorkerUpdate,
27
+ WorkerError,
28
+ WorkerHistoryEntry,
29
+ type WorkerContext,
30
+ WorkerRef,
31
+ WorkerStop,
32
+ } from "../../core/Worker.ts";
33
+ import {
34
+ type SubagentHost,
35
+ type DeferredStartWorkerRequest,
36
+ type DeferredFollowUpWorkerRequest,
37
+ type FollowUpWorkerRequest,
38
+ type StartWorkerRequest,
39
+ type WorkerObservation,
40
+ type WorkerReceiptRequest,
41
+ type WorkerRunReport,
42
+ } from "../../engine/SubagentHost.ts";
43
+ import { digestJson } from "../Digest.ts";
44
+ import type {
45
+ DurableAbortFailure,
46
+ DurableAwaitFailure,
47
+ DurableSubmitFailure,
48
+ DurableSubmitOptions,
49
+ } from "../DurableAgentRuntime.ts";
50
+ import {
51
+ DurableRuntimeFailpoint,
52
+ type DurableRuntimeFailpointLocation,
53
+ } from "../DurableFailpoint.ts";
54
+ import {
55
+ MessageDeliveryDriver,
56
+ MessageDeliveryStore,
57
+ prepareMessageDelivery,
58
+ type MessageDeliveryRecord,
59
+ } from "../MessageDelivery.ts";
60
+ import { PreparedInputAdmission } from "../PreparedInputAdmission.ts";
61
+ import {
62
+ BatchId,
63
+ CanonicalBatch,
64
+ CanonicalSequence,
65
+ type Digest,
66
+ type DeploymentId,
67
+ type ProducerId,
68
+ RecordEnvelope,
69
+ RecordId,
70
+ WorkerAdmission,
71
+ WorkerInputRequested,
72
+ WorkerStopRequested,
73
+ WorkerOrigin,
74
+ WorkerContinuation,
75
+ WorkerOriginRecorded,
76
+ WorkerInputCompleted,
77
+ WorkerReportPrepared,
78
+ WorkerReportRefused,
79
+ WorkerReportingIntent,
80
+ SubtreeBudgetReserved,
81
+ PersistedJson,
82
+ } from "../Records.ts";
83
+ import {
84
+ workerInputRecordId,
85
+ agentUpdateRecordId,
86
+ firstWorkerInputRecordId,
87
+ workerOriginRecordId,
88
+ workerReportRecordId,
89
+ } from "../RunJournal.ts";
90
+ import {
91
+ type ScheduledInputFailure,
92
+ ScheduledInputRefused,
93
+ ScheduledInputRetryable,
94
+ } from "../Schedule.ts";
95
+ import {
96
+ SubmissionLedger,
97
+ WorkerLedgerState,
98
+ LedgerError,
99
+ AbortCommand,
100
+ type AbortIntent,
101
+ type Principal,
102
+ type Settlement,
103
+ SubmissionLookupById,
104
+ submissionSettlementRecordId,
105
+ type SubmissionSnapshot,
106
+ } from "../SubmissionLedger.ts";
107
+ import type { SubmissionStatus } from "../SubmissionStatus.ts";
108
+ import { PreparedInput } from "../Subscription.ts";
109
+ import {
110
+ ThreadIdentityRequest,
111
+ ThreadStore,
112
+ getRecord,
113
+ getRunInput,
114
+ readWorkerState as readNativeWorkerState,
115
+ FencedAppendRequest,
116
+ ThreadExportRequest,
117
+ type ThreadExport,
118
+ MAX_THREAD_EXPORT_RECORDS,
119
+ ThreadRead,
120
+ type ThreadTail,
121
+ ThreadTailRequest,
122
+ } from "../ThreadStore.ts";
123
+ import {
124
+ WorkerBudgetAuthorizer,
125
+ WorkerConcurrencyLimit,
126
+ WorkerConcurrencyResolver,
127
+ WorkerHostAuthorizer,
128
+ WorkerHostConfig,
129
+ WorkerPolicyResolver,
130
+ type WorkerPolicyTarget,
131
+ } from "../WorkerHost.ts";
132
+ import {
133
+ definitionDigestsEqual,
134
+ resolveDefinitionBinding,
135
+ type ResolvedBinding,
136
+ } from "./agent-registration.ts";
137
+ import { lastWorkerReportMessageId } from "./agent-updates.ts";
138
+ import { messageStatus } from "./message-status.ts";
139
+
140
+ const failure = (
141
+ operation: WorkerError["operation"],
142
+ reason: WorkerError["reason"],
143
+ cause?: unknown,
144
+ ) => WorkerError.make({ operation, reason, ...(cause === undefined ? {} : { cause }) });
145
+
146
+ const storageFailure = (operation: WorkerError["operation"]) => (cause: unknown) =>
147
+ failure(operation, "storage", cause);
148
+
149
+ const decode = <S extends Schema.Top>(
150
+ schema: S,
151
+ value: unknown,
152
+ operation: WorkerError["operation"],
153
+ ) =>
154
+ Schema.decodeUnknownEffect(Schema.toType(schema))(value).pipe(
155
+ Effect.mapError((cause) => failure(operation, "corrupt", cause)),
156
+ );
157
+
158
+ const sameOrigin = Schema.toEquivalence(WorkerOrigin);
159
+ const sameAdmission = Schema.toEquivalence(WorkerAdmission);
160
+ const sameReporting = Schema.toEquivalence(Schema.UndefinedOr(WorkerReportingIntent));
161
+ const sameJson = Schema.toEquivalence(PersistedJson);
162
+ const sameCaps = Schema.toEquivalence(SubagentDelegationCaps);
163
+ const samePolicy = Schema.toEquivalence(AgentPolicy);
164
+
165
+ const amountCaps = [
166
+ ["turns", "maxTurns"],
167
+ ["toolCalls", "maxToolCalls"],
168
+ ["durationMillis", "maxDurationMillis"],
169
+ ["inputTokens", "maxInputTokens"],
170
+ ["outputTokens", "maxOutputTokens"],
171
+ ["costMicrousd", "maxCostMicrousd"],
172
+ ["resultBytes", "maxResultBytes"],
173
+ ] as const;
174
+
175
+ const sourceCaps = (policy: AgentPolicy): SubagentDelegationCaps =>
176
+ SubagentDelegationCaps.make({
177
+ maxTotalChildInvocations: policy.maxToolCalls,
178
+ maxConcurrentChildren: policy.toolConcurrency,
179
+ maxTurns: policy.maxTurns,
180
+ maxToolCalls: policy.maxToolCalls,
181
+ maxDurationMillis: Math.ceil(Duration.toMillis(policy.maxDuration)),
182
+ maxResultBytes: Math.min(
183
+ Number.MAX_SAFE_INTEGER,
184
+ policy.toolResultBounds.maxBytes * policy.maxToolCalls,
185
+ ),
186
+ ...(policy.tokenBudget === undefined
187
+ ? {}
188
+ : { maxInputTokens: policy.tokenBudget, maxOutputTokens: policy.tokenBudget }),
189
+ ...(policy.costBudgetMicrousd === undefined
190
+ ? {}
191
+ : { maxCostMicrousd: policy.costBudgetMicrousd }),
192
+ });
193
+
194
+ const withinPolicy = (origin: WorkerOrigin, source: AgentPolicy, target: AgentPolicy): boolean => {
195
+ const policy = origin.policy;
196
+ const allocation = origin.budget.allocation;
197
+ const caps = origin.budget.caps;
198
+ const ceilings = sourceCaps(source);
199
+
200
+ for (const [, name] of origin.budgetScope === "worker-run" ? [] : amountCaps) {
201
+ if (caps[name] !== undefined && ceilings[name] !== undefined && caps[name] > ceilings[name])
202
+ return false;
203
+ }
204
+ if (
205
+ origin.budgetScope !== "worker-run" &&
206
+ ((caps.maxTotalChildInvocations !== undefined &&
207
+ caps.maxTotalChildInvocations > source.maxToolCalls) ||
208
+ (caps.maxConcurrentChildren !== undefined &&
209
+ caps.maxConcurrentChildren > source.toolConcurrency))
210
+ )
211
+ return false;
212
+ const tokenCeiling = Math.min(source.tokenBudget ?? Infinity, target.tokenBudget ?? Infinity);
213
+
214
+ const costCeiling = Math.min(
215
+ source.costBudgetMicrousd ?? Infinity,
216
+ target.costBudgetMicrousd ?? Infinity,
217
+ );
218
+
219
+ return (
220
+ policy.maxTurns <= Math.min(allocation.turns, source.maxTurns, target.maxTurns) &&
221
+ policy.maxToolCalls <=
222
+ Math.min(allocation.toolCalls, source.maxToolCalls, target.maxToolCalls) &&
223
+ Duration.toMillis(policy.maxDuration) <=
224
+ Math.min(
225
+ allocation.durationMillis,
226
+ Duration.toMillis(source.maxDuration),
227
+ Duration.toMillis(target.maxDuration),
228
+ ) &&
229
+ policy.toolConcurrency <= Math.min(source.toolConcurrency, target.toolConcurrency) &&
230
+ policy.toolResultBounds.maxBytes <=
231
+ Math.min(source.toolResultBounds.maxBytes, target.toolResultBounds.maxBytes) &&
232
+ (tokenCeiling === Infinity ||
233
+ (policy.tokenBudget !== undefined && policy.tokenBudget <= tokenCeiling)) &&
234
+ (costCeiling === Infinity ||
235
+ (policy.costBudgetMicrousd !== undefined && policy.costBudgetMicrousd <= costCeiling)) &&
236
+ (source.tokenBudget === undefined ||
237
+ (allocation.inputTokens > 0 &&
238
+ allocation.outputTokens > 0 &&
239
+ policy.tokenBudget !== undefined &&
240
+ policy.tokenBudget <= allocation.inputTokens + allocation.outputTokens)) &&
241
+ (source.costBudgetMicrousd === undefined ||
242
+ (policy.costBudgetMicrousd !== undefined &&
243
+ policy.costBudgetMicrousd <= allocation.costMicrousd))
244
+ );
245
+ };
246
+
247
+ export interface WorkerRuntimeOptions {
248
+ readonly bindings: ReadonlyArray<ResolvedBinding>;
249
+ readonly deploymentId: DeploymentId;
250
+ readonly producerId: ProducerId;
251
+ readonly settlementPollInterval: Duration.Duration;
252
+ }
253
+
254
+ /** Admission and cancellation enter the owning runtime, never the raw ledger. */
255
+ export class WorkerInputControl extends Context.Service<
256
+ WorkerInputControl,
257
+ {
258
+ readonly submit: (envelope: PreparedInput) => Effect.Effect<Receipt, DurableSubmitFailure>;
259
+ readonly status: (
260
+ receipt: Receipt,
261
+ ) => Effect.Effect<SubmissionStatus, DurableAwaitFailure | ScheduledInputFailure>;
262
+ readonly abort: (command: AbortCommand) => Effect.Effect<AbortIntent, DurableAbortFailure>;
263
+ }
264
+ >()("@effect-agent/thread/internal/WorkerInputControl") {}
265
+
266
+ /** No in-memory ownership: every mutation is reserved in source canonical history with CAS. */
267
+ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
268
+ options: WorkerRuntimeOptions,
269
+ ) {
270
+ const control = yield* WorkerInputControl;
271
+ // A routed read may belong to another owner; capture it before exposing worker operations.
272
+ const admission = yield* Effect.serviceOption(PreparedInputAdmission);
273
+
274
+ const deps = {
275
+ ...options,
276
+ ...control,
277
+ store: yield* ThreadStore,
278
+ ledger: yield* SubmissionLedger,
279
+ deliveries: yield* Effect.serviceOption(MessageDeliveryStore),
280
+ crypto: yield* Crypto.Crypto,
281
+ authorizer: yield* WorkerHostAuthorizer,
282
+ budgetAuthorizer: yield* WorkerBudgetAuthorizer,
283
+ policyResolver: yield* WorkerPolicyResolver,
284
+ concurrencyResolver: yield* WorkerConcurrencyResolver,
285
+ limits: yield* WorkerHostConfig,
286
+ failpoint: yield* DurableRuntimeFailpoint,
287
+ status: Option.getOrUndefined(admission)?.submissionStatus ?? control.status,
288
+ };
289
+
290
+ const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>) =>
291
+ Effect.provideService(effect, Crypto.Crypto, deps.crypto);
292
+
293
+ const authorizeBudget = Effect.fn("WorkerHost.authorizeBudget")(function* (
294
+ origin: WorkerOrigin,
295
+ principal: Principal,
296
+ ) {
297
+ yield* verifyContinuation(origin);
298
+ if (origin.budgetScope !== "worker-run") return;
299
+ // Independence changes accounting ownership, never delegation generations.
300
+ if (origin.depth !== 1) return yield* failure("start", "denied");
301
+
302
+ yield* deps.budgetAuthorizer.authorize({
303
+ source: origin.source,
304
+ principal,
305
+ worker: origin.worker,
306
+ ...(origin.continuationOf === undefined ? {} : { continuationOf: origin.continuationOf }),
307
+ policy: origin.policy,
308
+ budget: origin.budget,
309
+ });
310
+ });
311
+
312
+ const read = (threadId: ThreadId, operation: WorkerError["operation"]) =>
313
+ deps.store
314
+ .export(ThreadExportRequest.make({ threadId }))
315
+ .pipe(Effect.mapError(storageFailure(operation)));
316
+
317
+ const exactRecord = Effect.fn("WorkerHost.exactRecord")(function* (
318
+ threadId: ThreadId,
319
+ recordId: RecordId,
320
+ operation: WorkerError["operation"],
321
+ ) {
322
+ return yield* getRecord({ threadId, recordId }).pipe(
323
+ Effect.provideService(ThreadStore, deps.store),
324
+ Effect.mapError(storageFailure(operation)),
325
+ );
326
+ });
327
+
328
+ const completedContinuation = Effect.fn("WorkerHost.completedContinuation")(function* (
329
+ source: WorkerContext["source"],
330
+ worker: WorkerRef,
331
+ targetAgentId: AgentId,
332
+ delegationId: WorkerRef["delegationId"],
333
+ expected?: WorkerContinuation,
334
+ ) {
335
+ const selected = yield* decode(WorkerRef, worker, "start");
336
+
337
+ if (
338
+ selected.targetAgentId !== targetAgentId ||
339
+ selected.delegationId !== delegationId ||
340
+ selected.threadId === source.threadId ||
341
+ deps.ledger.inspectWorker === undefined
342
+ )
343
+ return yield* failure("start", "worker-mismatch");
344
+ const sourceState = yield* readWorkerState(source.threadId, "start");
345
+
346
+ if (
347
+ sourceState.records.some(
348
+ ({ record }) =>
349
+ record.payload._tag === "WorkerStopRequested" &&
350
+ record.payload.command.worker.threadId === selected.threadId,
351
+ )
352
+ )
353
+ return yield* failure("start", "denied");
354
+
355
+ const first = sourceState.records.find(
356
+ ({ record }) => record.recordId === firstWorkerInputRecordId(selected),
357
+ )?.record.payload;
358
+
359
+ const origin = first?._tag === "WorkerInputRequested" ? first.admission.origin : undefined;
360
+
361
+ if (
362
+ origin === undefined ||
363
+ origin.lifecycle !== "assignment" ||
364
+ !Schema.toEquivalence(WorkerRef)(origin.worker, selected) ||
365
+ origin.source.threadId !== source.threadId ||
366
+ origin.source.agentId !== source.agentId ||
367
+ first?._tag !== "WorkerInputRequested" ||
368
+ first.admission.messageId !== origin.firstMessageId
369
+ )
370
+ return yield* failure("start", "worker-mismatch");
371
+
372
+ const recorded = Option.getOrUndefined(
373
+ yield* exactRecord(selected.threadId, workerOriginRecordId(selected.threadId), "start"),
374
+ )?.record.payload;
375
+
376
+ if (recorded?._tag !== "WorkerOriginRecorded" || !sameOrigin(recorded.origin, origin))
377
+ return yield* failure("start", "worker-mismatch");
378
+
379
+ const control = yield* deps.ledger
380
+ .inspectWorker(selected.threadId)
381
+ .pipe(Effect.mapError(storageFailure("start")));
382
+
383
+ if (control.terminal !== "completed" || !control.stopped || control.active !== null)
384
+ return yield* failure("start", "denied");
385
+
386
+ const tail = yield* deps.store
387
+ .inspectTail(ThreadTailRequest.make({ threadId: selected.threadId }))
388
+ .pipe(Effect.mapError(storageFailure("start")));
389
+
390
+ const execution = yield* deps.store
391
+ .read({
392
+ threadId: selected.threadId,
393
+ selection: {
394
+ _tag: "WorkerExecution",
395
+ expectedTailSequence: tail.tailSequence,
396
+ expectedTailDigest: tail.tailDigest,
397
+ },
398
+ page: { limit: 2 },
399
+ })
400
+ .pipe(Stream.runCollect, Effect.mapError(storageFailure("start")));
401
+
402
+ const run = execution.find(({ record }) => record.payload._tag === "RunStarted")?.record
403
+ .payload;
404
+
405
+ if (run?._tag !== "RunStarted") return yield* failure("start", "corrupt");
406
+
407
+ const input = Option.getOrUndefined(
408
+ yield* getRunInput({ threadId: selected.threadId, runId: run.runId }).pipe(
409
+ Effect.provideService(ThreadStore, deps.store),
410
+ Effect.mapError(storageFailure("start")),
411
+ ),
412
+ )?.record.payload;
413
+
414
+ if (input?._tag !== "UserInputRecorded" || input.submissionId === undefined)
415
+ return yield* failure("start", "corrupt");
416
+
417
+ const found = yield* deps.ledger
418
+ .lookup(SubmissionLookupById.make({ submissionId: input.submissionId }))
419
+ .pipe(Effect.mapError(storageFailure("start")));
420
+
421
+ const row = Option.getOrUndefined(found);
422
+
423
+ const settlement = Option.getOrUndefined(
424
+ yield* exactRecord(
425
+ selected.threadId,
426
+ submissionSettlementRecordId(input.submissionId),
427
+ "start",
428
+ ),
429
+ )?.record.payload;
430
+
431
+ if (
432
+ row === undefined ||
433
+ row.threadId !== selected.threadId ||
434
+ row.state !== "settled" ||
435
+ row.settledOutcome !== "completed" ||
436
+ row.workerAdmission === undefined ||
437
+ !sameOrigin(row.workerAdmission.origin, origin) ||
438
+ settlement?._tag !== "SubmissionSettled" ||
439
+ settlement.outcome !== "completed" ||
440
+ settlement.runDisposition !== "completed" ||
441
+ settlement.finishReason !== undefined ||
442
+ settlement.runId !== run.runId ||
443
+ settlement.submissionId !== row.submissionId ||
444
+ settlement.receiptId !== row.receiptId
445
+ )
446
+ return yield* failure("start", "denied");
447
+
448
+ const evidence = WorkerContinuation.make({
449
+ worker: selected,
450
+ receipt: Receipt.make({
451
+ threadId: row.threadId,
452
+ submissionId: row.submissionId,
453
+ receiptId: row.receiptId,
454
+ queueSequence: row.queueSequence,
455
+ }),
456
+ settlementId: settlement.settlementId,
457
+ });
458
+
459
+ if (expected !== undefined && !Schema.toEquivalence(WorkerContinuation)(expected, evidence))
460
+ return yield* failure("start", "worker-mismatch");
461
+
462
+ return { evidence, origin };
463
+ });
464
+
465
+ const verifyContinuation = Effect.fn("WorkerHost.verifyContinuation")(function* (
466
+ origin: WorkerOrigin,
467
+ ) {
468
+ if (origin.continuationOf === undefined) return;
469
+
470
+ const previous = yield* completedContinuation(
471
+ origin.source,
472
+ origin.continuationOf.worker,
473
+ origin.worker.targetAgentId,
474
+ origin.worker.delegationId,
475
+ origin.continuationOf,
476
+ );
477
+
478
+ if (
479
+ origin.worker.threadId === previous.origin.worker.threadId ||
480
+ origin.lifecycle !== "assignment" ||
481
+ !samePolicy(origin.policy, previous.origin.policy) ||
482
+ !Schema.toEquivalence(WorkerOrigin.fields.budget)(origin.budget, previous.origin.budget) ||
483
+ !Schema.toEquivalence(SubagentGrant)(origin.grant, previous.origin.grant) ||
484
+ origin.budgetScope !== previous.origin.budgetScope ||
485
+ origin.depth !== previous.origin.depth ||
486
+ origin.toolCallAllowance !== previous.origin.toolCallAllowance ||
487
+ origin.expiresAtMillis > previous.origin.expiresAtMillis
488
+ )
489
+ return yield* failure("start", "denied");
490
+ });
491
+
492
+ const readIdentity = Effect.fn("WorkerHost.readIdentity")(function* (
493
+ threadId: ThreadId,
494
+ operation: WorkerError["operation"],
495
+ ) {
496
+ return yield* deps.store
497
+ .readIdentity(ThreadIdentityRequest.make({ threadId }))
498
+ .pipe(Effect.mapError(storageFailure(operation)));
499
+ });
500
+
501
+ const readWorkerState = Effect.fn("WorkerHost.readWorkerState")(function* (
502
+ threadId: ThreadId,
503
+ operation: WorkerError["operation"],
504
+ sourceSubmissionId?: SubmissionId,
505
+ ) {
506
+ return yield* readNativeWorkerState({
507
+ threadId,
508
+ limit: MAX_THREAD_EXPORT_RECORDS,
509
+ ...(sourceSubmissionId === undefined ? {} : { sourceSubmissionId }),
510
+ }).pipe(
511
+ Effect.provideService(ThreadStore, deps.store),
512
+ Effect.mapError(storageFailure(operation)),
513
+ );
514
+ });
515
+
516
+ const hit = (location: DurableRuntimeFailpointLocation, operation: WorkerError["operation"]) =>
517
+ deps.failpoint.hit(location).pipe(Effect.mapError(storageFailure(operation)));
518
+
519
+ const append = Effect.fn("WorkerHost.append")(
520
+ function* (
521
+ threadId: ThreadId,
522
+ id: string,
523
+ payload:
524
+ | WorkerStopRequested
525
+ | WorkerInputRequested
526
+ | WorkerOriginRecorded
527
+ | WorkerInputCompleted
528
+ | WorkerReportPrepared
529
+ | WorkerReportRefused
530
+ | SubtreeBudgetReserved,
531
+ current: Pick<ThreadExport, "tailSequence" | "tailDigest" | "records"> &
532
+ Partial<Pick<ThreadTail, "producerEpoch">>,
533
+ phase: "source" | "origin" | "completion" | "subtree" | "report" | "stop",
534
+ acknowledgements: ReadonlyArray<WorkerInputCompleted> = [],
535
+ ) {
536
+ const operation = "start";
537
+ let producerEpoch = current.producerEpoch;
538
+
539
+ if (producerEpoch === undefined) {
540
+ const tail = yield* deps.store.inspectTail(ThreadTailRequest.make({ threadId }));
541
+
542
+ if (tail.tailSequence !== current.tailSequence || tail.tailDigest !== current.tailDigest)
543
+ return false;
544
+ producerEpoch = tail.producerEpoch;
545
+ }
546
+
547
+ // The store atomically fences the validated prefix and its producer epoch.
548
+ yield* hit(`worker:before-${phase}-append`, operation);
549
+
550
+ const result = yield* deps.store
551
+ .append(
552
+ FencedAppendRequest.make({
553
+ threadId,
554
+ producerEpoch,
555
+ expectedTailSequence: current.tailSequence,
556
+ expectedTailDigest: current.tailDigest,
557
+ batch: CanonicalBatch.make({
558
+ batchId: Schema.decodeSync(BatchId)(id),
559
+ producerId: deps.producerId,
560
+ records: [
561
+ RecordEnvelope.make({
562
+ recordId: Schema.decodeSync(RecordId)(id),
563
+ family: "thread",
564
+ schemaVersion: 1,
565
+ createdAt: DateTime.makeUnsafe(
566
+ payload._tag === "WorkerInputRequested"
567
+ ? payload.admission.createdAtMillis
568
+ : payload._tag === "WorkerOriginRecorded"
569
+ ? payload.origin.createdAtMillis
570
+ : payload._tag === "WorkerInputCompleted"
571
+ ? payload.completedAtMillis
572
+ : yield* Clock.currentTimeMillis,
573
+ ),
574
+ deploymentId: deps.deploymentId,
575
+ payload,
576
+ }),
577
+ ...acknowledgements.map((completion) =>
578
+ RecordEnvelope.make({
579
+ recordId: Schema.decodeSync(RecordId)(
580
+ `worker-effects-resolved:${completion.messageId}`,
581
+ ),
582
+ family: "thread",
583
+ schemaVersion: 1,
584
+ createdAt: DateTime.makeUnsafe(completion.completedAtMillis),
585
+ deploymentId: deps.deploymentId,
586
+ payload: completion,
587
+ }),
588
+ ),
589
+ ],
590
+ }),
591
+ }),
592
+ )
593
+ .pipe(
594
+ Effect.as(true),
595
+ Effect.catchTag(["AppendConflict", "FenceRejected"], () => Effect.succeed(false)),
596
+ );
597
+
598
+ if (result) yield* hit(`worker:after-${phase}-append`, operation);
599
+
600
+ return result;
601
+ },
602
+ Effect.mapError(storageFailure("start")),
603
+ );
604
+
605
+ const requests = (records: Effect.Success<ReturnType<typeof read>>["records"]) =>
606
+ records.flatMap(({ record }) =>
607
+ record.payload._tag === "WorkerInputRequested" ? [record.payload] : [],
608
+ );
609
+
610
+ // Completion is published in the child's own journal. Only the source, while
611
+ // admitting work, reads those exact receipts to release its capacity reservations.
612
+ const completedInputs = Effect.fn("WorkerHost.completedInputs")(function* (
613
+ rows: ReadonlyArray<WorkerInputRequested>,
614
+ sourceRecords: ThreadExport["records"],
615
+ ) {
616
+ const completed = new Set<string>(
617
+ sourceRecords.flatMap(({ record }) =>
618
+ record.payload._tag === "WorkerInputCompleted" && record.payload.effectsResolved
619
+ ? [record.payload.messageId]
620
+ : [],
621
+ ),
622
+ );
623
+
624
+ const acknowledgements: Array<WorkerInputCompleted> = [];
625
+
626
+ for (const { admission } of rows) {
627
+ if (completed.has(admission.messageId)) continue;
628
+
629
+ const receipt = Option.getOrUndefined(
630
+ yield* getRecord({
631
+ threadId: admission.origin.worker.threadId,
632
+ recordId: Schema.decodeSync(RecordId)(`worker-effects-resolved:${admission.messageId}`),
633
+ }).pipe(
634
+ Effect.provideService(ThreadStore, deps.store),
635
+ // A source reservation can precede child materialization. It still consumes
636
+ // capacity; absence of storage is never a completion acknowledgement.
637
+ Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed(Option.none())),
638
+ Effect.mapError(storageFailure("start")),
639
+ ),
640
+ )?.record.payload;
641
+
642
+ if (receipt === undefined) continue;
643
+ if (
644
+ receipt._tag !== "WorkerInputCompleted" ||
645
+ !receipt.effectsResolved ||
646
+ receipt.messageId !== admission.messageId ||
647
+ receipt.workerThreadId !== admission.origin.worker.threadId
648
+ )
649
+ return yield* failure("start", "corrupt");
650
+ completed.add(admission.messageId);
651
+ acknowledgements.push(receipt);
652
+ }
653
+
654
+ return { completed, acknowledgements };
655
+ });
656
+
657
+ const reportIntent = Effect.fn("WorkerHost.reportIntent")(function* (
658
+ source: ResolvedBinding,
659
+ target: ResolvedBinding,
660
+ delegationId: WorkerRef["delegationId"],
661
+ authority: Effect.Success<ReturnType<typeof sourceAuthority>>,
662
+ ): Effect.fn.Return<WorkerOrigin["reporting"], WorkerError> {
663
+ const reports = source.reporting?.filter((entry) => entry.delegationId === delegationId) ?? [];
664
+ const report = reports[0];
665
+
666
+ if (report === undefined) return undefined;
667
+ if (reports.length !== 1 || !Object.is(report.target, target.definition))
668
+ return yield* failure("start", "declaration-unavailable");
669
+
670
+ const submission = authority.submission;
671
+
672
+ if (
673
+ submission === undefined ||
674
+ (authority.depth !== 0 && submission.workerAdmission === undefined)
675
+ )
676
+ return yield* failure("start", "denied");
677
+
678
+ const retained =
679
+ submission.workerAdmission === undefined
680
+ ? undefined
681
+ : yield* Schema.encodeEffect(WorkerAdmission)(submission.workerAdmission).pipe(
682
+ Effect.flatMap(Schema.decodeEffect(PersistedJson)),
683
+ Effect.mapError(storageFailure("start")),
684
+ );
685
+
686
+ return {
687
+ sourceDigests: source.digests,
688
+ mode: "standard",
689
+ returnAddress: {
690
+ input: submission.inputPayload,
691
+ principal: submission.principal,
692
+ policy: authority.policy,
693
+ depth: authority.depth,
694
+ ...(authority.grant === undefined ? {} : { grant: authority.grant }),
695
+ ...(retained === undefined ? {} : { workerAdmission: retained }),
696
+ },
697
+ };
698
+ });
699
+
700
+ const currentBinding = (agentId: AgentId): ResolvedBinding | undefined => {
701
+ const matches = deps.bindings.filter((entry) => entry.agentId === agentId);
702
+
703
+ return matches.length === 1 ? matches[0] : undefined;
704
+ };
705
+
706
+ const sourceAuthority = Effect.fn("WorkerHost.sourceAuthority")(function* (
707
+ threadId: ThreadId,
708
+ submissionId?: SubmissionId,
709
+ reservations = false,
710
+ ) {
711
+ const current = yield* reservations
712
+ ? readWorkerState(threadId, "start", submissionId)
713
+ : readIdentity(threadId, "start");
714
+
715
+ const created = current.records[0]?.record.payload;
716
+
717
+ if (created?._tag !== "ThreadCreated" || current.records[0]?.sequence !== 1)
718
+ return yield* failure("start", "not-found");
719
+
720
+ const binding = currentBinding(created.agentId);
721
+
722
+ const worker = current.records.find(
723
+ ({ record }) => record.payload._tag === "WorkerOriginRecorded",
724
+ )?.record.payload;
725
+
726
+ const attached = current.records.find(
727
+ ({ record }) => record.payload._tag === "SubagentLineageRecorded",
728
+ )?.record.payload;
729
+
730
+ const nested =
731
+ worker?._tag === "WorkerOriginRecorded" || attached?._tag === "SubagentLineageRecorded";
732
+
733
+ let ownerSubmission: SubmissionSnapshot | undefined;
734
+
735
+ if (nested && submissionId === undefined) return yield* failure("start", "denied");
736
+ if (submissionId !== undefined) {
737
+ const submission = yield* deps.ledger
738
+ .lookup(SubmissionLookupById.make({ submissionId }))
739
+ .pipe(Effect.mapError(storageFailure("start")));
740
+
741
+ if (
742
+ Option.isNone(submission) ||
743
+ submission.value.threadId !== threadId ||
744
+ (nested && submission.value.agentId !== created.agentId)
745
+ )
746
+ return yield* failure("start", "denied");
747
+ ownerSubmission = submission.value;
748
+ if (
749
+ worker?._tag === "WorkerOriginRecorded" &&
750
+ (submission.value.workerAdmission === undefined ||
751
+ !sameOrigin(submission.value.workerAdmission.origin, worker.origin))
752
+ )
753
+ return yield* failure("start", "denied");
754
+ if (
755
+ attached?._tag === "SubagentLineageRecorded" &&
756
+ (submission.value.parentLinkage?.parentSubmissionId !== attached.parentSubmissionId ||
757
+ submission.value.parentLinkage.parentToolCallId !== attached.parentLink.parentToolCallId)
758
+ )
759
+ return yield* failure("start", "denied");
760
+ }
761
+
762
+ const ownerBinding =
763
+ ownerSubmission === undefined ? undefined : currentBinding(ownerSubmission.agentId);
764
+
765
+ const selectedBinding = ownerBinding ?? binding;
766
+
767
+ if (worker?._tag === "WorkerOriginRecorded")
768
+ return {
769
+ current,
770
+ binding: selectedBinding,
771
+ submission: ownerSubmission,
772
+ policyOverride: Option.none<AgentPolicy>(),
773
+ policy: worker.origin.policy,
774
+ budget: worker.origin.budget,
775
+ budgetScope: worker.origin.budgetScope,
776
+ grant: worker.origin.grant,
777
+ depth: worker.origin.depth,
778
+ };
779
+ if (attached?._tag === "SubagentLineageRecorded") {
780
+ if (
781
+ attached.policy === undefined ||
782
+ attached.budget === undefined ||
783
+ attached.grant === undefined
784
+ )
785
+ return yield* failure("start", "denied");
786
+
787
+ return {
788
+ current,
789
+ binding: selectedBinding,
790
+ submission: ownerSubmission,
791
+ policyOverride: Option.none<AgentPolicy>(),
792
+ policy: attached.policy,
793
+ budget: attached.budget,
794
+ budgetScope: undefined,
795
+ grant: attached.grant,
796
+ depth: attached.parentLink.depth,
797
+ };
798
+ }
799
+
800
+ const changedRootAgent =
801
+ ownerSubmission !== undefined && ownerSubmission.agentId !== created.agentId;
802
+
803
+ // A root conversation may admit a new registered Agent after deployment.
804
+ // Its immutable owner input selects authority; child lineage never changes.
805
+ if (selectedBinding === undefined || (changedRootAgent && ownerBinding === undefined))
806
+ return yield* failure("start", "declaration-unavailable");
807
+
808
+ const selected = yield* deps.policyResolver.resolveSource({
809
+ threadId,
810
+ definition: selectedBinding.definition,
811
+ definitions: ownerSubmission?.agentDigests ?? selectedBinding.digests,
812
+ ...(ownerSubmission === undefined ? {} : { submission: ownerSubmission }),
813
+ });
814
+
815
+ if (Option.isSome(selected) && ownerSubmission !== undefined && ownerBinding === undefined)
816
+ return yield* failure("start", "declaration-unavailable");
817
+
818
+ const effectiveBinding =
819
+ Option.isSome(selected) || changedRootAgent || binding === undefined
820
+ ? selectedBinding
821
+ : binding;
822
+
823
+ if (effectiveBinding === undefined) return yield* failure("start", "declaration-unavailable");
824
+
825
+ const policy = Option.isNone(selected)
826
+ ? effectiveBinding.definition.policy
827
+ : yield* decode(AgentPolicy, selected.value, "start");
828
+
829
+ return {
830
+ current,
831
+ binding: effectiveBinding,
832
+ submission: ownerSubmission,
833
+ policyOverride: Option.map(selected, () => policy),
834
+ policy,
835
+ budget: undefined,
836
+ budgetScope: undefined,
837
+ grant: undefined,
838
+ depth: 0,
839
+ };
840
+ });
841
+
842
+ const resolveTargetPolicy = Effect.fn("WorkerHost.resolveTargetPolicy")(function* (
843
+ request: WorkerPolicyTarget,
844
+ ) {
845
+ const selected = yield* deps.policyResolver.resolveTarget(request);
846
+
847
+ if (Option.isNone(selected)) return selected;
848
+ const policy = yield* decode(AgentPolicy, selected.value, "start");
849
+
850
+ if (request._tag === "RetainedWorker" && !samePolicy(policy, request.origin.policy))
851
+ return yield* failure("start", "worker-mismatch");
852
+
853
+ return Option.some(policy);
854
+ });
855
+
856
+ const reserveSubtree = Effect.fn("WorkerHost.reserveSubtree")(function* (
857
+ sourceThreadId: ThreadId,
858
+ requested: SubtreeBudgetReserved,
859
+ ): Effect.fn.Return<void, WorkerError> {
860
+ const payload = yield* decode(SubtreeBudgetReserved, requested, "start");
861
+ const recordId = `subtree:${payload.reservationId}`;
862
+
863
+ for (let attempt = 0; attempt < 16; attempt++) {
864
+ const source = yield* sourceAuthority(sourceThreadId, payload.sourceSubmissionId, true);
865
+
866
+ const previous = source.current.records.find(({ record }) => record.recordId === recordId)
867
+ ?.record.payload;
868
+
869
+ if (previous !== undefined) {
870
+ if (
871
+ previous._tag !== "SubtreeBudgetReserved" ||
872
+ !Schema.toEquivalence(SubtreeBudgetReserved)(previous, payload)
873
+ )
874
+ return yield* failure("start", "idempotency-conflict");
875
+
876
+ return;
877
+ }
878
+ if (payload.depth !== source.depth + 1 || payload.depth > payload.grant.maxDepth)
879
+ return yield* failure("start", "denied");
880
+ const inherited = source.grant;
881
+ const lifetimes = inherited?.childLifetimes;
882
+
883
+ if (
884
+ inherited !== undefined &&
885
+ (payload.depth > inherited.maxDepth ||
886
+ payload.grant.maxDepth > inherited.maxDepth ||
887
+ (lifetimes !== undefined && !lifetimes.includes(payload.lifetime)) ||
888
+ payload.grant.allowedToolNames.some(
889
+ (name) => !inherited.allowedToolNames.includes(name),
890
+ ) ||
891
+ (lifetimes !== undefined &&
892
+ (payload.grant.childLifetimes ?? ["attached", "background"]).some(
893
+ (lifetime) => !lifetimes.includes(lifetime),
894
+ )))
895
+ )
896
+ return yield* failure("start", "denied");
897
+
898
+ const rows = source.current.records.flatMap(({ record }) =>
899
+ record.payload._tag === "SubtreeBudgetReserved" &&
900
+ record.payload.sourceSubmissionId === payload.sourceSubmissionId
901
+ ? [record.payload]
902
+ : [],
903
+ );
904
+
905
+ const caps = payload.budget.caps;
906
+
907
+ if (rows.some((row) => !sameCaps(row.budget.caps, caps)))
908
+ return yield* failure("start", "capacity");
909
+
910
+ const own = {
911
+ turns: source.policy.maxTurns,
912
+ toolCalls: source.policy.maxToolCalls,
913
+ durationMillis: Math.ceil(Duration.toMillis(source.policy.maxDuration)),
914
+ inputTokens: source.policy.tokenBudget ?? source.budget?.allocation.inputTokens ?? 0,
915
+ outputTokens: source.policy.tokenBudget ?? source.budget?.allocation.outputTokens ?? 0,
916
+ costMicrousd:
917
+ source.policy.costBudgetMicrousd ?? source.budget?.allocation.costMicrousd ?? 0,
918
+ resultBytes: source.policy.toolResultBounds.maxBytes,
919
+ };
920
+
921
+ const ceilings = sourceCaps(source.policy);
922
+ const child = payload.policy;
923
+ const allocation = payload.budget.allocation;
924
+
925
+ if (
926
+ child.maxTurns > Math.min(source.policy.maxTurns, allocation.turns) ||
927
+ child.maxToolCalls > Math.min(source.policy.maxToolCalls, allocation.toolCalls) ||
928
+ Duration.toMillis(child.maxDuration) >
929
+ Math.min(Duration.toMillis(source.policy.maxDuration), allocation.durationMillis) ||
930
+ child.toolConcurrency > source.policy.toolConcurrency ||
931
+ child.toolResultBounds.maxBytes > source.policy.toolResultBounds.maxBytes ||
932
+ (source.policy.tokenBudget !== undefined &&
933
+ (child.tokenBudget === undefined ||
934
+ child.tokenBudget > source.policy.tokenBudget ||
935
+ child.tokenBudget > allocation.inputTokens + allocation.outputTokens)) ||
936
+ (source.policy.costBudgetMicrousd !== undefined &&
937
+ (child.costBudgetMicrousd === undefined ||
938
+ child.costBudgetMicrousd >
939
+ Math.min(source.policy.costBudgetMicrousd, allocation.costMicrousd)))
940
+ )
941
+ return yield* failure("start", "capacity");
942
+ for (const [, cap] of amountCaps) {
943
+ if (caps[cap] !== undefined && ceilings[cap] !== undefined && caps[cap] > ceilings[cap])
944
+ return yield* failure("start", "capacity");
945
+ }
946
+ if (
947
+ (caps.maxTotalChildInvocations ?? 0) > source.policy.maxToolCalls ||
948
+ (caps.maxConcurrentChildren ?? 0) > source.policy.toolConcurrency
949
+ )
950
+ return yield* failure("start", "capacity");
951
+ const slots = 1 + (payload.budget.descendantInvocations ?? 0);
952
+
953
+ const chargedSlots = rows.reduce(
954
+ (sum, row) => sum + 1 + (row.budget.descendantInvocations ?? 0),
955
+ slots,
956
+ );
957
+
958
+ const slotLimit = Math.min(
959
+ caps.maxTotalChildInvocations ?? Infinity,
960
+ source.budget === undefined
961
+ ? source.policy.maxToolCalls
962
+ : (source.budget.descendantInvocations ?? 0),
963
+ );
964
+
965
+ if (chargedSlots > slotLimit) return yield* failure("start", "capacity");
966
+ for (const [amount, cap] of amountCaps) {
967
+ // Zero is the encoded absence of an allocation when this dimension is unconfigured.
968
+ // It must not become a new cumulative token or dollar ceiling in a descendant.
969
+ if (
970
+ source.budgetScope === "worker-run" &&
971
+ (amount === "inputTokens" || amount === "outputTokens" || amount === "costMicrousd") &&
972
+ source.budget !== undefined &&
973
+ source.budget.caps[cap] === undefined &&
974
+ source.budget.allocation[amount] === 0 &&
975
+ ceilings[cap] === undefined
976
+ ) {
977
+ const charged = rows.reduce(
978
+ (sum, row) => sum + row.budget.allocation[amount],
979
+ allocation[amount],
980
+ );
981
+
982
+ if (charged > (caps[cap] ?? Infinity)) return yield* failure("start", "capacity");
983
+ continue;
984
+ }
985
+
986
+ const residual =
987
+ source.budget === undefined
988
+ ? (ceilings[cap] ?? Infinity)
989
+ : Math.max(0, source.budget.allocation[amount] - own[amount]);
990
+
991
+ const limit = Math.min(caps[cap] ?? Infinity, residual);
992
+
993
+ const charged = rows.reduce(
994
+ (sum, row) => sum + row.budget.allocation[amount],
995
+ payload.budget.allocation[amount],
996
+ );
997
+
998
+ if (charged > limit) return yield* failure("start", "capacity");
999
+ }
1000
+
1001
+ // Include unreconciled reservations conservatively; only canonical completion frees an
1002
+ // active slot. A worker Thread consumes one slot even when multiple inputs safely join it.
1003
+ const completedWorkers = yield* completedInputs(
1004
+ requests(source.current.records),
1005
+ source.current.records,
1006
+ );
1007
+
1008
+ const completedAttached = new Set<string>(
1009
+ source.current.records.flatMap(({ record }) =>
1010
+ record.payload._tag === "SubagentJoined" ? [record.payload.reservationId] : [],
1011
+ ),
1012
+ );
1013
+
1014
+ const active = new Set(
1015
+ rows
1016
+ .filter((row) =>
1017
+ row.lifetime === "background"
1018
+ ? !completedWorkers.completed.has(row.reservationId)
1019
+ : !completedAttached.has(row.reservationId),
1020
+ )
1021
+ .map((row) => row.childThreadId),
1022
+ );
1023
+
1024
+ if (
1025
+ !active.has(payload.childThreadId) &&
1026
+ active.size >=
1027
+ Math.min(caps.maxConcurrentChildren ?? Infinity, source.policy.toolConcurrency)
1028
+ )
1029
+ return yield* failure("start", "capacity");
1030
+ if (
1031
+ yield* append(
1032
+ sourceThreadId,
1033
+ recordId,
1034
+ payload,
1035
+ source.current,
1036
+ "subtree",
1037
+ completedWorkers.acknowledgements,
1038
+ )
1039
+ )
1040
+ return;
1041
+ }
1042
+
1043
+ return yield* failure("start", "storage");
1044
+ });
1045
+
1046
+ const reservation = Effect.fn("WorkerHost.reserveInput")(function* (
1047
+ admission: WorkerAdmission,
1048
+ inputDigest: Digest,
1049
+ input: PersistedJson,
1050
+ principal: Principal,
1051
+ retainedFrameworkInput: boolean,
1052
+ ): Effect.fn.Return<void, WorkerError> {
1053
+ const origin = admission.origin;
1054
+
1055
+ yield* verifyContinuation(origin);
1056
+ const id = workerInputRecordId(admission.messageId);
1057
+
1058
+ for (let attempt = 0; attempt < 16; attempt++) {
1059
+ const source = yield* sourceAuthority(
1060
+ origin.source.threadId,
1061
+ admission.sourceSubmissionId,
1062
+ true,
1063
+ );
1064
+
1065
+ const current = source.current;
1066
+
1067
+ if (
1068
+ origin.continuationOf !== undefined &&
1069
+ current.records.some(
1070
+ ({ record }) =>
1071
+ record.payload._tag === "WorkerStopRequested" &&
1072
+ record.payload.command.worker.threadId === origin.continuationOf?.worker.threadId,
1073
+ )
1074
+ )
1075
+ return yield* failure("start", "denied");
1076
+ const rows = requests(current.records);
1077
+ const existing = current.records.find(({ record }) => record.recordId === id)?.record.payload;
1078
+
1079
+ if (existing !== undefined) {
1080
+ if (
1081
+ existing._tag !== "WorkerInputRequested" ||
1082
+ existing.inputDigest !== inputDigest ||
1083
+ !sameAdmission(existing.admission, admission)
1084
+ )
1085
+ return yield* failure("start", "idempotency-conflict");
1086
+ }
1087
+ const first = current.records[0]?.record.payload;
1088
+
1089
+ if (first?._tag !== "ThreadCreated") return yield* failure("start", "denied");
1090
+
1091
+ const own = rows.filter(
1092
+ (row) => row.admission.origin.worker.threadId === origin.worker.threadId,
1093
+ );
1094
+
1095
+ const origins = new Map(
1096
+ rows.map((row) => [row.admission.origin.worker.threadId, row.admission.origin]),
1097
+ );
1098
+
1099
+ const prior = origins.get(origin.worker.threadId);
1100
+
1101
+ if (
1102
+ prior === undefined &&
1103
+ (source.submission?.agentId ?? first.agentId) !== origin.source.agentId
1104
+ )
1105
+ return yield* failure("start", "denied");
1106
+ if (prior !== undefined && !sameOrigin(prior, origin))
1107
+ return yield* failure("start", "worker-mismatch");
1108
+ if (prior === undefined && admission.messageId !== origin.firstMessageId)
1109
+ return yield* failure("start", "not-found");
1110
+ const caps = origin.budget.caps;
1111
+
1112
+ const sourceBinding = source.binding;
1113
+
1114
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
1115
+
1116
+ if (sourceBinding === undefined || targetBinding === undefined)
1117
+ return yield* failure("start", "declaration-unavailable");
1118
+ // Standard reports and updates retain the owner's original input bytes. Their
1119
+ // framework message carries the new information independently of the current input codec.
1120
+ if (!retainedFrameworkInput)
1121
+ yield* Schema.decodeEffect(Schema.toEncoded(targetBinding.definition.input))(input).pipe(
1122
+ Effect.mapError((cause) => failure("start", "corrupt", cause)),
1123
+ );
1124
+
1125
+ const targetRequest = {
1126
+ definition: targetBinding.definition,
1127
+ definitions: targetBinding.digests,
1128
+ source: origin.source,
1129
+ };
1130
+
1131
+ const selected = yield* resolveTargetPolicy(
1132
+ admission.messageId === origin.firstMessageId
1133
+ ? {
1134
+ ...targetRequest,
1135
+ _tag: "InitialInput",
1136
+ ...(origin.continuationOf === undefined
1137
+ ? {}
1138
+ : { continuationOf: origin.continuationOf }),
1139
+ input,
1140
+ inputDigest,
1141
+ ...(source.submission === undefined ? {} : { sourceSubmission: source.submission }),
1142
+ }
1143
+ : { ...targetRequest, _tag: "RetainedWorker", origin: prior ?? origin },
1144
+ );
1145
+
1146
+ const independent = origin.budgetScope === "worker-run";
1147
+
1148
+ const targetPolicy = Option.isSome(selected)
1149
+ ? selected.value
1150
+ : independent
1151
+ ? targetBinding.definition.policy
1152
+ : AgentPolicy.resolve(
1153
+ targetBinding.definition.policyOverrides ?? targetBinding.definition.policy,
1154
+ source.policy,
1155
+ );
1156
+
1157
+ // Captured authority is checked on replay too, before the retained reservation returns.
1158
+ // Static callers retain the old idempotent-return behavior.
1159
+ if (
1160
+ (existing === undefined || Option.isSome(selected)) &&
1161
+ !withinPolicy(origin, independent ? targetPolicy : source.policy, targetPolicy)
1162
+ )
1163
+ return yield* failure("start", "capacity");
1164
+ if (existing !== undefined) return;
1165
+ const now = yield* Clock.currentTimeMillis;
1166
+
1167
+ if (now >= origin.expiresAtMillis) return yield* failure("start", "capacity");
1168
+ if (
1169
+ (prior === undefined && origins.size >= deps.limits.maxWorkersPerSource) ||
1170
+ own.filter((row) => row.admission.reportKind === admission.reportKind).length >=
1171
+ (admission.reportKind === "update"
1172
+ ? (deps.limits.maxUpdateInputsPerWorker ?? 256)
1173
+ : deps.limits.maxInputsPerWorker)
1174
+ )
1175
+ return yield* failure("start", "capacity");
1176
+ if (
1177
+ prior === undefined &&
1178
+ !sameReporting(
1179
+ yield* reportIntent(sourceBinding, targetBinding, origin.worker.delegationId, source),
1180
+ origin.reporting,
1181
+ )
1182
+ )
1183
+ return yield* failure("start", "denied");
1184
+ if (independent && source.depth !== 0) return yield* failure("start", "denied");
1185
+ const ceilings = sourceCaps(source.policy);
1186
+
1187
+ const conservedRows = rows.filter((row) => row.admission.origin.budgetScope !== "worker-run");
1188
+
1189
+ const chargedRows =
1190
+ source.depth === 0
1191
+ ? conservedRows
1192
+ : conservedRows.filter(
1193
+ (row) => row.admission.sourceSubmissionId === admission.sourceSubmissionId,
1194
+ );
1195
+
1196
+ // Independent origins do not belong to this pool. Preserve the retained conserved
1197
+ // origins' cap identity check separately from the current input's charge selection.
1198
+ if (
1199
+ !independent &&
1200
+ conservedRows.some((row) => !sameCaps(row.admission.origin.budget.caps, caps))
1201
+ )
1202
+ return yield* failure("start", "capacity");
1203
+ if (
1204
+ !independent &&
1205
+ chargedRows.reduce(
1206
+ (sum, row) => sum + 1 + (row.admission.origin.budget.descendantInvocations ?? 0),
1207
+ 1 + (origin.budget.descendantInvocations ?? 0),
1208
+ ) > Math.min(caps.maxTotalChildInvocations ?? Infinity, source.policy.maxToolCalls)
1209
+ )
1210
+ return yield* failure("start", "capacity");
1211
+ // Worker-owned Run usage is already durable in the destination's Run journal. Each
1212
+ // Receipt may join an existing Run, so admitting input must never mint an allowance.
1213
+ for (const [amount, cap] of independent ? [] : amountCaps) {
1214
+ const limit = Math.min(caps[cap] ?? Infinity, ceilings[cap] ?? Infinity);
1215
+
1216
+ if (
1217
+ chargedRows.reduce(
1218
+ (sum, row) => sum + row.admission.origin.budget.allocation[amount],
1219
+ origin.budget.allocation[amount],
1220
+ ) > limit
1221
+ )
1222
+ return yield* failure("start", "capacity");
1223
+ }
1224
+
1225
+ const completed = yield* completedInputs(rows, current.records);
1226
+
1227
+ const pendingRows = rows.filter((row) => !completed.completed.has(row.admission.messageId));
1228
+ const activeWorkers = new Set(pendingRows.map((row) => row.admission.origin.worker.threadId));
1229
+
1230
+ const pendingOwn = pendingRows.filter(
1231
+ (row) =>
1232
+ row.admission.origin.worker.threadId === origin.worker.threadId &&
1233
+ row.admission.reportKind === admission.reportKind,
1234
+ ).length;
1235
+
1236
+ // Resolve inside the source CAS loop: independently delivered starts must compete
1237
+ // against one canonical prefix. A conflict repeats both authority and capacity reads.
1238
+ const selectedConcurrency = yield* deps.concurrencyResolver.resolve({
1239
+ source: origin.source,
1240
+ worker: origin.worker,
1241
+ ...(origin.continuationOf === undefined ? {} : { continuationOf: origin.continuationOf }),
1242
+ principal,
1243
+ ...(source.submission === undefined ? {} : { sourceSubmission: source.submission }),
1244
+ });
1245
+
1246
+ const sourceConcurrency = Option.isSome(selectedConcurrency)
1247
+ ? (yield* decode(WorkerConcurrencyLimit, selectedConcurrency.value, "start"))
1248
+ .maxActiveWorkersPerSource
1249
+ : Infinity;
1250
+
1251
+ if (
1252
+ pendingOwn >=
1253
+ (admission.reportKind === "update"
1254
+ ? (deps.limits.maxPendingUpdateInputsPerWorker ?? 32)
1255
+ : deps.limits.maxPendingInputsPerWorker) ||
1256
+ (!activeWorkers.has(origin.worker.threadId) &&
1257
+ activeWorkers.size >=
1258
+ Math.min(
1259
+ sourceConcurrency,
1260
+ deps.limits.maxActiveWorkersPerSource ?? source.policy.toolConcurrency,
1261
+ independent
1262
+ ? Infinity
1263
+ : Math.min(caps.maxConcurrentChildren ?? Infinity, source.policy.toolConcurrency),
1264
+ ))
1265
+ )
1266
+ return yield* WorkerError.make({ operation: "start", reason: "capacity", retryable: true });
1267
+ if (!independent)
1268
+ yield* reserveSubtree(
1269
+ origin.source.threadId,
1270
+ SubtreeBudgetReserved.make({
1271
+ reservationId: Schema.decodeSync(SubtreeBudgetReserved.fields.reservationId)(
1272
+ admission.messageId,
1273
+ ),
1274
+ ...(admission.sourceSubmissionId === undefined
1275
+ ? {}
1276
+ : { sourceSubmissionId: admission.sourceSubmissionId }),
1277
+ childThreadId: origin.worker.threadId,
1278
+ lifetime: "background",
1279
+ depth: origin.depth,
1280
+ policy: origin.policy,
1281
+ grant: origin.grant,
1282
+ budget: origin.budget,
1283
+ }),
1284
+ );
1285
+ if (
1286
+ yield* append(
1287
+ origin.source.threadId,
1288
+ id,
1289
+ WorkerInputRequested.make({ admission, inputDigest }),
1290
+ current,
1291
+ "source",
1292
+ completed.acknowledgements,
1293
+ )
1294
+ )
1295
+ return;
1296
+ }
1297
+
1298
+ return yield* failure("start", "storage");
1299
+ });
1300
+
1301
+ const ensureOrigin = Effect.fn("WorkerHost.ensureOrigin")(function* (origin: WorkerOrigin) {
1302
+ for (let attempt = 0; attempt < 16; attempt++) {
1303
+ const current = yield* readIdentity(origin.worker.threadId, "start");
1304
+
1305
+ const existing = current.records.find(
1306
+ ({ record }) => record.payload._tag === "WorkerOriginRecorded",
1307
+ )?.record.payload;
1308
+
1309
+ if (existing?._tag === "WorkerOriginRecorded") {
1310
+ if (!sameOrigin(existing.origin, origin)) return yield* failure("start", "worker-mismatch");
1311
+
1312
+ return;
1313
+ }
1314
+ const first = current.records[0]?.record.payload;
1315
+
1316
+ if (
1317
+ first?._tag !== "ThreadCreated" ||
1318
+ first.agentId !== origin.worker.targetAgentId ||
1319
+ !definitionDigestsEqual(first.definitions, origin.targetDigests) ||
1320
+ current.records.some(({ record }) => record.payload._tag === "SubagentLineageRecorded")
1321
+ )
1322
+ return yield* failure("start", "worker-mismatch");
1323
+ if (
1324
+ yield* append(
1325
+ origin.worker.threadId,
1326
+ workerOriginRecordId(origin.worker.threadId),
1327
+ WorkerOriginRecorded.make({ origin }),
1328
+ current,
1329
+ "origin",
1330
+ )
1331
+ )
1332
+ return;
1333
+ }
1334
+
1335
+ return yield* failure("start", "storage");
1336
+ });
1337
+
1338
+ const validateAdmission = Effect.fn("WorkerHost.validateAdmission")(function* (
1339
+ unvalidated: WorkerAdmission,
1340
+ options: DurableSubmitOptions,
1341
+ agentId: Agent.AnyDefinition["id"],
1342
+ inputDigest: Digest,
1343
+ input: PersistedJson,
1344
+ ) {
1345
+ const admission = yield* decode(WorkerAdmission, unvalidated, "start");
1346
+
1347
+ if (admission.reportKind === "update") {
1348
+ if (!Schema.is(WorkerUpdate)(options.messageAdmission))
1349
+ return yield* failure("start", "denied");
1350
+ yield* validateCompletion(options.messageAdmission, options, agentId, inputDigest);
1351
+ }
1352
+
1353
+ yield* verifyContinuation(admission.origin);
1354
+ yield* deps.authorizer.authorize({
1355
+ sourceThreadId: admission.origin.source.threadId,
1356
+ ...(admission.sourceSubmissionId === undefined
1357
+ ? {}
1358
+ : { sourceSubmissionId: admission.sourceSubmissionId }),
1359
+ principal: options.principal,
1360
+ operation: "start",
1361
+ access: "send",
1362
+ worker: admission.origin.worker,
1363
+ ...(admission.origin.continuationOf === undefined
1364
+ ? {}
1365
+ : { continuationOf: admission.origin.continuationOf }),
1366
+ });
1367
+ yield* authorizeBudget(admission.origin, options.principal);
1368
+ if (
1369
+ admission.origin.worker.threadId !== options.threadId ||
1370
+ admission.origin.worker.targetAgentId !== agentId ||
1371
+ !definitionDigestsEqual(admission.origin.targetDigests, options.definitions) ||
1372
+ options.idempotencyKey !== admission.messageId ||
1373
+ (admission.deliveryPrincipal !== undefined &&
1374
+ admission.deliveryPrincipal !== options.principal)
1375
+ )
1376
+ return yield* failure("start", "worker-mismatch");
1377
+ const retainedFrameworkInput = Schema.is(FrameworkMessage)(options.messageAdmission);
1378
+
1379
+ if (retainedFrameworkInput && admission.reportKind !== "update")
1380
+ yield* validateCompletion(options.messageAdmission, options, agentId, inputDigest);
1381
+ yield* reservation(admission, inputDigest, input, options.principal, retainedFrameworkInput);
1382
+
1383
+ return admission;
1384
+ });
1385
+
1386
+ const prepareUpdate = Effect.fn("WorkerHost.prepareUpdate")(
1387
+ function* (update: Update, submission: SubmissionSnapshot, messageId: IdempotencyKey) {
1388
+ const origin = submission.workerAdmission?.origin;
1389
+ const intent = origin?.reporting;
1390
+
1391
+ if (origin === undefined || intent?.mode !== "standard") return undefined;
1392
+ if (update.threadId !== submission.threadId || update.agentId !== origin.worker.targetAgentId)
1393
+ return yield* failure("followUp", "worker-mismatch");
1394
+
1395
+ const destination = intent.returnAddress;
1396
+
1397
+ if (destination === undefined) return yield* failure("followUp", "corrupt");
1398
+
1399
+ const retained =
1400
+ destination.workerAdmission === undefined
1401
+ ? undefined
1402
+ : yield* Schema.decodeUnknownEffect(WorkerAdmission)(destination.workerAdmission).pipe(
1403
+ Effect.mapError((cause) => failure("followUp", "corrupt", cause)),
1404
+ );
1405
+
1406
+ const sourceBinding = currentBinding(origin.source.agentId);
1407
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
1408
+
1409
+ const reports =
1410
+ sourceBinding?.reporting?.filter(
1411
+ (entry) => entry.delegationId === origin.worker.delegationId,
1412
+ ) ?? [];
1413
+
1414
+ if (
1415
+ sourceBinding === undefined ||
1416
+ targetBinding === undefined ||
1417
+ reports.length !== 1 ||
1418
+ reports[0] === undefined ||
1419
+ reports[0].target !== targetBinding.definition ||
1420
+ !definitionDigestsEqual(submission.agentDigests, origin.targetDigests)
1421
+ )
1422
+ return yield* failure("followUp", "declaration-unavailable");
1423
+
1424
+ const receivingOrigin = retained?.origin;
1425
+
1426
+ if (destination.depth !== 0 && receivingOrigin === undefined)
1427
+ return yield* failure("followUp", "denied");
1428
+ const now = yield* Clock.currentTimeMillis;
1429
+
1430
+ const deadlineAtMillis = Math.min(
1431
+ origin.expiresAtMillis,
1432
+ receivingOrigin?.expiresAtMillis ?? Infinity,
1433
+ );
1434
+
1435
+ if (now >= deadlineAtMillis) return yield* failure("followUp", "denied");
1436
+ if (reports[0].reportUpdate !== undefined && !reports[0].reportUpdate(update))
1437
+ return undefined;
1438
+ const input = destination.input;
1439
+
1440
+ let workerAdmission: WorkerAdmission | undefined;
1441
+
1442
+ if (retained !== undefined) {
1443
+ workerAdmission = {
1444
+ origin: retained.origin,
1445
+ reportKind: "update",
1446
+ messageId,
1447
+ parameters: retained.parameters,
1448
+ createdAtMillis: now,
1449
+ ...(retained.sourceSubmissionId === undefined
1450
+ ? {}
1451
+ : { sourceSubmissionId: retained.sourceSubmissionId }),
1452
+ };
1453
+ }
1454
+
1455
+ const message = WorkerUpdate.make({
1456
+ _tag: "WorkerUpdate",
1457
+ schemaVersion: 1,
1458
+ worker: origin.worker,
1459
+ update,
1460
+ });
1461
+
1462
+ const envelope: PreparedInput = {
1463
+ schemaVersion: 1,
1464
+ threadId: origin.source.threadId,
1465
+ deliveryPrincipal: destination.principal,
1466
+ agentId: origin.source.agentId,
1467
+ definitions: intent.sourceDigests,
1468
+ input,
1469
+ inputDigest: yield* withCrypto(digestJson(input)).pipe(
1470
+ Effect.mapError(storageFailure("followUp")),
1471
+ ),
1472
+ admissionKey: messageId,
1473
+ authorization: { policyId: "worker-report", decisionId: messageId },
1474
+ messageAdmission: message,
1475
+ ...(workerAdmission === undefined
1476
+ ? {}
1477
+ : { workerAdmission: { ...workerAdmission, deliveryPrincipal: destination.principal } }),
1478
+ };
1479
+
1480
+ return {
1481
+ messageId,
1482
+ envelope: yield* Schema.encodeEffect(PreparedInput)(envelope).pipe(
1483
+ Effect.flatMap(Schema.decodeEffect(PersistedJson)),
1484
+ Effect.mapError(storageFailure("followUp")),
1485
+ ),
1486
+ createdAtMillis: now,
1487
+ deadlineAtMillis,
1488
+ };
1489
+ },
1490
+ Effect.mapError((error) =>
1491
+ error.reason === "storage" || error.reason === "corrupt"
1492
+ ? LedgerError.make({ operation: "worker-update", message: error.reason, cause: error })
1493
+ : UpdateError.make({ reason: error.reason === "denied" ? "denied" : "unavailable" }),
1494
+ ),
1495
+ );
1496
+
1497
+ const reportRun = Effect.fn("WorkerHost.reportRun")(function* (
1498
+ submission: SubmissionSnapshot,
1499
+ history: Effect.Success<ReturnType<typeof read>>,
1500
+ ): Effect.fn.Return<void, WorkerError> {
1501
+ const admission = submission.workerAdmission;
1502
+ const intent = admission?.origin.reporting;
1503
+
1504
+ if (admission === undefined || intent === undefined) return;
1505
+ const origin = admission.origin;
1506
+
1507
+ const member = history.records.find(
1508
+ ({ record }) =>
1509
+ record.payload._tag === "SubmissionSettled" &&
1510
+ record.payload.submissionId === submission.submissionId,
1511
+ )?.record.payload;
1512
+
1513
+ if (member?._tag !== "SubmissionSettled") return yield* failure("inspect", "corrupt");
1514
+ if (member.runId === undefined) return; // No actual Run means no fabricated report identity.
1515
+ const runId = member.runId;
1516
+
1517
+ const host = history.records.find(
1518
+ ({ record }) => record.payload._tag === "SubmissionSettled" && record.payload.runId === runId,
1519
+ )?.record.payload;
1520
+
1521
+ if (host?._tag !== "SubmissionSettled") return yield* failure("inspect", "corrupt");
1522
+
1523
+ const messageId = yield* withCrypto(
1524
+ digestJson(["worker-report", submission.threadId, runId]),
1525
+ ).pipe(
1526
+ Effect.map((value) => Schema.decodeSync(IdempotencyKey)(`report:${value}`)),
1527
+ Effect.mapError(storageFailure("inspect")),
1528
+ );
1529
+
1530
+ const recordId = workerReportRecordId(messageId);
1531
+
1532
+ const prepare = Effect.fn("WorkerHost.prepareReport")(function* (): Effect.fn.Return<
1533
+ WorkerReportPrepared | WorkerReportRefused,
1534
+ WorkerError
1535
+ > {
1536
+ const refused = (reason: WorkerReportRefused["reason"]) =>
1537
+ WorkerReportRefused.make({ runId, messageId, reason });
1538
+
1539
+ // Retired custom origins remain readable; already prepared envelopes bypass preparation.
1540
+ if (intent.mode !== "standard") return refused("declaration-unavailable");
1541
+
1542
+ const selected = yield* deps.ledger
1543
+ .lookup(SubmissionLookupById.make({ submissionId: host.submissionId }))
1544
+ .pipe(Effect.mapError(storageFailure("inspect")));
1545
+
1546
+ if (
1547
+ Option.isNone(selected) ||
1548
+ selected.value.threadId !== submission.threadId ||
1549
+ selected.value.receiptId !== host.receiptId ||
1550
+ selected.value.workerAdmission === undefined ||
1551
+ !sameOrigin(selected.value.workerAdmission.origin, origin)
1552
+ )
1553
+ return yield* failure("inspect", "corrupt");
1554
+ const hostSubmission = selected.value;
1555
+ const hostAdmission = selected.value.workerAdmission;
1556
+
1557
+ const address = intent.returnAddress;
1558
+
1559
+ if (address === undefined) return yield* failure("inspect", "corrupt");
1560
+
1561
+ const retainedAdmission =
1562
+ address.workerAdmission === undefined
1563
+ ? undefined
1564
+ : yield* Schema.decodeUnknownEffect(WorkerAdmission)(address.workerAdmission).pipe(
1565
+ Effect.mapError((cause) => failure("inspect", "corrupt", cause)),
1566
+ );
1567
+
1568
+ const source = { ...address, admission: retainedAdmission };
1569
+
1570
+ const sourceBinding = currentBinding(origin.source.agentId);
1571
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
1572
+
1573
+ const reports =
1574
+ sourceBinding?.reporting?.filter(
1575
+ (entry) => entry.delegationId === origin.worker.delegationId,
1576
+ ) ?? [];
1577
+
1578
+ const descriptor = reports[0];
1579
+
1580
+ if (
1581
+ sourceBinding === undefined ||
1582
+ targetBinding === undefined ||
1583
+ descriptor === undefined ||
1584
+ reports.length !== 1 ||
1585
+ !Object.is(descriptor.target, targetBinding.definition)
1586
+ )
1587
+ return refused("declaration-unavailable");
1588
+
1589
+ const sourceOrigin = source.admission?.origin;
1590
+
1591
+ if (source.depth !== 0 && sourceOrigin === undefined) return refused("destination");
1592
+ const now = yield* Clock.currentTimeMillis;
1593
+
1594
+ const deadlineAtMillis = Math.min(
1595
+ origin.expiresAtMillis,
1596
+ sourceOrigin?.expiresAtMillis ?? Infinity,
1597
+ );
1598
+
1599
+ if (now >= deadlineAtMillis) return refused("expired");
1600
+
1601
+ const context: WorkerContext = {
1602
+ source: origin.source,
1603
+ policy: source.policy,
1604
+ depth: source.depth,
1605
+ ...(source.grant === undefined ? {} : { grant: source.grant }),
1606
+ };
1607
+
1608
+ const report: WorkerRunReport = {
1609
+ worker: origin.worker,
1610
+ context,
1611
+ observation: {
1612
+ _tag: "Settled",
1613
+ receipt: Receipt.make({
1614
+ threadId: hostSubmission.threadId,
1615
+ submissionId: hostSubmission.submissionId,
1616
+ receiptId: hostSubmission.receiptId,
1617
+ queueSequence: hostSubmission.queueSequence,
1618
+ }),
1619
+ runId,
1620
+ settlementId: host.settlementId,
1621
+ outcome: host.outcome,
1622
+ encodedParameters: hostAdmission.parameters,
1623
+ encodedResult: host.result ?? null,
1624
+ ...(host.outcome === "failed" ? { diagnostic: host.result } : {}),
1625
+ budgetExhausted: host.finishReason === "budget-exhausted",
1626
+ },
1627
+ };
1628
+
1629
+ const projection = yield* Effect.suspend(() =>
1630
+ descriptor.reportCompletion?.(report) === false
1631
+ ? Effect.succeed(undefined)
1632
+ : descriptor.prepare(report),
1633
+ ).pipe(
1634
+ Effect.timeout(
1635
+ Math.min(deps.limits.reportPreparationTimeoutMillis ?? 5_000, deadlineAtMillis - now),
1636
+ ),
1637
+ Effect.map((value) => ({ _tag: "Prepared" as const, value })),
1638
+ Effect.catchTag("WorkerReportPreparationFailure", (error) =>
1639
+ Effect.succeed(refused(error.stage)),
1640
+ ),
1641
+ Effect.catchTag("TimeoutError", () => Effect.succeed(refused("timeout"))),
1642
+ Effect.catchCauseIf(
1643
+ (cause) => !Cause.hasInterrupts(cause),
1644
+ () => Effect.succeed(refused("defect")),
1645
+ ),
1646
+ );
1647
+
1648
+ if (projection._tag === "WorkerReportRefused") return projection;
1649
+
1650
+ if (projection.value === undefined) return refused("filtered");
1651
+
1652
+ const message = projection.value.message;
1653
+
1654
+ if (!Schema.is(WorkerCompletion)(message)) return refused("input");
1655
+ if (
1656
+ !Schema.toEquivalence(WorkerRef)(message.report.worker, origin.worker) ||
1657
+ !Schema.toEquivalence(Receipt)(message.report.receipt, report.observation.receipt) ||
1658
+ message.report.runId !== runId ||
1659
+ message.report.settlementId !== host.settlementId ||
1660
+ message.report.outcome !== host.outcome ||
1661
+ message.budgetExhausted !== report.observation.budgetExhausted
1662
+ )
1663
+ return refused("input");
1664
+
1665
+ const validated = yield* Schema.decodeEffect(PersistedJson)(source.input).pipe(Effect.option);
1666
+
1667
+ if (Option.isNone(validated)) return refused("input");
1668
+ const input = validated.value;
1669
+
1670
+ const inputDigest = yield* withCrypto(digestJson(input)).pipe(
1671
+ Effect.mapError(storageFailure("inspect")),
1672
+ );
1673
+
1674
+ let workerAdmission: WorkerAdmission | undefined;
1675
+ const principal = source.principal;
1676
+
1677
+ if (sourceOrigin !== undefined) {
1678
+ const retained = source.admission;
1679
+
1680
+ if (retained === undefined) return refused("destination");
1681
+
1682
+ const parameters = yield* Schema.decodeEffect(PersistedJson)(retained.parameters).pipe(
1683
+ Effect.option,
1684
+ );
1685
+
1686
+ if (Option.isNone(parameters)) return refused("destination");
1687
+ workerAdmission = {
1688
+ origin: sourceOrigin,
1689
+ messageId,
1690
+ parameters: parameters.value,
1691
+ createdAtMillis: now,
1692
+ ...(retained.sourceSubmissionId === undefined
1693
+ ? {}
1694
+ : { sourceSubmissionId: retained.sourceSubmissionId }),
1695
+ };
1696
+ }
1697
+
1698
+ // The destination reauthorizes standard messages through validateCompletion.
1699
+ const envelope: PreparedInput = {
1700
+ schemaVersion: 1,
1701
+ threadId: origin.source.threadId,
1702
+ deliveryPrincipal: principal,
1703
+ agentId: origin.source.agentId,
1704
+ definitions: intent.sourceDigests,
1705
+ input,
1706
+ inputDigest,
1707
+ admissionKey: messageId,
1708
+ authorization: { policyId: "worker-report", decisionId: messageId },
1709
+ messageAdmission: message,
1710
+ ...(workerAdmission === undefined
1711
+ ? {}
1712
+ : { workerAdmission: { ...workerAdmission, deliveryPrincipal: principal } }),
1713
+ };
1714
+
1715
+ const encoded = yield* Schema.encodeEffect(PreparedInput)(envelope).pipe(
1716
+ Effect.flatMap(Schema.decodeUnknownEffect(PersistedJson)),
1717
+ Effect.mapError(storageFailure("inspect")),
1718
+ );
1719
+
1720
+ return WorkerReportPrepared.make({
1721
+ runId,
1722
+ messageId,
1723
+ envelope: encoded,
1724
+ createdAtMillis: now,
1725
+ deadlineAtMillis,
1726
+ ...(lastWorkerReportMessageId(history.records) === undefined
1727
+ ? {}
1728
+ : { predecessor: lastWorkerReportMessageId(history.records) }),
1729
+ });
1730
+ });
1731
+
1732
+ let decision: WorkerReportPrepared | WorkerReportRefused | undefined;
1733
+
1734
+ for (let attempt = 0; attempt < 16; attempt++) {
1735
+ const current = yield* read(submission.threadId, "inspect");
1736
+
1737
+ const existing = current.records.find(({ record }) => record.recordId === recordId)?.record
1738
+ .payload;
1739
+
1740
+ if (existing !== undefined) {
1741
+ if (
1742
+ (existing._tag !== "WorkerReportPrepared" && existing._tag !== "WorkerReportRefused") ||
1743
+ existing.runId !== runId ||
1744
+ existing.messageId !== messageId
1745
+ )
1746
+ return yield* failure("inspect", "corrupt");
1747
+ decision = existing;
1748
+ break;
1749
+ }
1750
+ decision ??= yield* prepare();
1751
+ if (yield* append(submission.threadId, recordId, decision, current, "report")) break;
1752
+ if (attempt === 15) return yield* failure("inspect", "storage");
1753
+ }
1754
+ if (decision?._tag !== "WorkerReportPrepared") return;
1755
+ if (Option.isNone(deps.deliveries)) return yield* failure("inspect", "unavailable");
1756
+
1757
+ const envelope = yield* Schema.decodeUnknownEffect(PreparedInput)(decision.envelope).pipe(
1758
+ Effect.mapError((cause) => failure("inspect", "corrupt", cause)),
1759
+ );
1760
+
1761
+ const prepared = yield* withCrypto(
1762
+ prepareMessageDelivery({
1763
+ key: { ownerThreadId: submission.threadId, messageId },
1764
+ envelope,
1765
+ createdAtMillis: decision.createdAtMillis,
1766
+ deadlineAtMillis: decision.deadlineAtMillis,
1767
+ ...(decision.predecessor === undefined ? {} : { predecessor: decision.predecessor }),
1768
+ }),
1769
+ ).pipe(Effect.mapError(storageFailure("inspect")));
1770
+
1771
+ // Until this insertion commits the child ledger stays nonterminal. Afterwards its independent
1772
+ // due index survives both child and source settlement, including Object eviction.
1773
+ yield* hit("worker:before-report-delivery", "inspect");
1774
+ yield* deps.deliveries.value.insert(prepared).pipe(Effect.mapError(storageFailure("inspect")));
1775
+ yield* hit("worker:after-report-delivery", "inspect");
1776
+ });
1777
+
1778
+ const completeInput = Effect.fn("WorkerHost.completeInput")(function* (
1779
+ submission: SubmissionSnapshot,
1780
+ ) {
1781
+ const admission = submission.workerAdmission;
1782
+
1783
+ if (admission === undefined) return;
1784
+ const child = yield* read(submission.threadId, "inspect");
1785
+
1786
+ yield* reportRun(submission, child);
1787
+
1788
+ const settledEnvelope = child.records.find(
1789
+ ({ record }) =>
1790
+ record.payload._tag === "SubmissionSettled" &&
1791
+ record.payload.submissionId === submission.submissionId,
1792
+ )?.record;
1793
+
1794
+ const settled = settledEnvelope?.payload;
1795
+
1796
+ if (
1797
+ settled?._tag !== "SubmissionSettled" ||
1798
+ settled.receiptId !== submission.receiptId ||
1799
+ settledEnvelope === undefined
1800
+ )
1801
+ return yield* failure("inspect", "corrupt");
1802
+
1803
+ const unresolved = new Set<string>();
1804
+
1805
+ for (const { record } of child.records) {
1806
+ const value = record.payload;
1807
+
1808
+ if (
1809
+ (value._tag === "ToolCallPrepared" || value._tag === "ToolCallUnknown") &&
1810
+ value.runId === settled.runId
1811
+ )
1812
+ unresolved.add(value.toolCallId);
1813
+ if (value._tag === "ToolCallSettled" && value.runId === settled.runId)
1814
+ unresolved.delete(value.toolCallId);
1815
+ }
1816
+ if (unresolved.size > 0) return;
1817
+
1818
+ const payload = WorkerInputCompleted.make({
1819
+ effectsResolved: true,
1820
+ messageId: admission.messageId,
1821
+ workerThreadId: submission.threadId,
1822
+ submissionId: submission.submissionId,
1823
+ receiptId: submission.receiptId,
1824
+ settlementId: settled.settlementId,
1825
+ completedAtMillis: DateTime.toEpochMillis(settledEnvelope.createdAt),
1826
+ });
1827
+
1828
+ for (let attempt = 0; attempt < 16; attempt++) {
1829
+ const current = yield* read(submission.threadId, "inspect");
1830
+
1831
+ const existing = current.records.findLast(
1832
+ ({ record }) =>
1833
+ record.payload._tag === "WorkerInputCompleted" &&
1834
+ record.payload.messageId === admission.messageId,
1835
+ )?.record.payload;
1836
+
1837
+ if (existing !== undefined) {
1838
+ if (
1839
+ existing._tag !== "WorkerInputCompleted" ||
1840
+ !Schema.toEquivalence(WorkerInputCompleted)(
1841
+ { ...existing, effectsResolved: true },
1842
+ payload,
1843
+ )
1844
+ )
1845
+ return yield* failure("inspect", "corrupt");
1846
+
1847
+ if (existing.effectsResolved) return;
1848
+ }
1849
+ if (
1850
+ yield* append(
1851
+ submission.threadId,
1852
+ `worker-effects-resolved:${admission.messageId}`,
1853
+ payload,
1854
+ current,
1855
+ "completion",
1856
+ )
1857
+ )
1858
+ return;
1859
+ }
1860
+
1861
+ return yield* failure("inspect", "storage");
1862
+ });
1863
+
1864
+ const facet = (
1865
+ context: WorkerContext,
1866
+ principal: Principal,
1867
+ sourceSubmissionId?: SubmissionId,
1868
+ ): SubagentHost["Service"] => {
1869
+ const authorize = (
1870
+ operation: WorkerError["operation"],
1871
+ access: "context" | "read" | "send" | "control",
1872
+ worker?: WorkerRef,
1873
+ continuationOf?: WorkerContinuation,
1874
+ ) =>
1875
+ deps.authorizer.authorize({
1876
+ sourceThreadId: context.source.threadId,
1877
+ ...(sourceSubmissionId === undefined ? {} : { sourceSubmissionId }),
1878
+ principal,
1879
+ operation,
1880
+ access,
1881
+ ...(worker === undefined ? {} : { worker }),
1882
+ ...(continuationOf === undefined ? {} : { continuationOf }),
1883
+ });
1884
+
1885
+ const binding = (target: Agent.AnyDefinition, operation: WorkerError["operation"]) =>
1886
+ resolveDefinitionBinding(deps.bindings, target).pipe(
1887
+ Effect.mapError((cause) => failure(operation, "declaration-unavailable", cause)),
1888
+ );
1889
+
1890
+ const deliveryStatus = (row: MessageDeliveryRecord, operation: WorkerError["operation"]) =>
1891
+ messageStatus(row).pipe(Effect.mapError((cause) => failure(operation, "corrupt", cause)));
1892
+
1893
+ const preparedTarget = Effect.fn("WorkerHost.preparedTarget")(function* (
1894
+ target: Agent.AnyDefinition,
1895
+ encodedInput: unknown,
1896
+ continuationOf?: WorkerContinuation,
1897
+ ) {
1898
+ const resolved = yield* binding(target, "start");
1899
+
1900
+ yield* Schema.decodeUnknownEffect(Schema.toEncoded(target.input))(encodedInput).pipe(
1901
+ Effect.mapError((cause) => failure("start", "corrupt", cause)),
1902
+ );
1903
+ const input = yield* decode(PersistedJson, encodedInput, "start");
1904
+
1905
+ const inputDigest = yield* withCrypto(digestJson(input)).pipe(
1906
+ Effect.mapError(storageFailure("start")),
1907
+ );
1908
+
1909
+ const source = yield* sourceAuthority(context.source.threadId, sourceSubmissionId);
1910
+
1911
+ const policy = yield* resolveTargetPolicy({
1912
+ _tag: "InitialInput",
1913
+ ...(continuationOf === undefined ? {} : { continuationOf }),
1914
+ definition: resolved.definition,
1915
+ definitions: resolved.digests,
1916
+ source: context.source,
1917
+ ...(source.submission === undefined ? {} : { sourceSubmission: source.submission }),
1918
+ input,
1919
+ inputDigest,
1920
+ });
1921
+
1922
+ return { resolved, source, policy };
1923
+ });
1924
+
1925
+ const findOrigin = Effect.fn("WorkerHost.findOrigin")(function* (
1926
+ worker: WorkerRef,
1927
+ target: Agent.AnyDefinition,
1928
+ operation: WorkerError["operation"],
1929
+ ) {
1930
+ yield* binding(target, operation);
1931
+
1932
+ if (worker.targetAgentId !== target.id) return yield* failure(operation, "worker-mismatch");
1933
+
1934
+ const first = Option.getOrUndefined(
1935
+ yield* exactRecord(context.source.threadId, firstWorkerInputRecordId(worker), operation),
1936
+ )?.record.payload;
1937
+
1938
+ const origin = first?._tag === "WorkerInputRequested" ? first.admission.origin : undefined;
1939
+
1940
+ if (origin === undefined) {
1941
+ const retained = Option.isNone(deps.deliveries)
1942
+ ? null
1943
+ : yield* deps.deliveries.value
1944
+ .get({
1945
+ ownerThreadId: context.source.threadId,
1946
+ messageId: Schema.decodeSync(IdempotencyKey)(worker.threadId),
1947
+ })
1948
+ .pipe(Effect.mapError(storageFailure(operation)));
1949
+
1950
+ const pendingOrigin = retained?.envelope.workerAdmission?.origin;
1951
+
1952
+ if (pendingOrigin === undefined) return yield* failure(operation, "not-found");
1953
+ if (
1954
+ !Schema.toEquivalence(WorkerRef)(pendingOrigin.worker, worker) ||
1955
+ pendingOrigin.firstMessageId !== retained?.key.messageId ||
1956
+ pendingOrigin.source.threadId !== context.source.threadId
1957
+ )
1958
+ return yield* failure(operation, "worker-mismatch");
1959
+
1960
+ return pendingOrigin;
1961
+ }
1962
+ if (
1963
+ !Schema.toEquivalence(WorkerRef)(origin.worker, worker) ||
1964
+ first?._tag !== "WorkerInputRequested" ||
1965
+ first.admission.messageId !== origin.firstMessageId ||
1966
+ workerInputRecordId(origin.firstMessageId) !== firstWorkerInputRecordId(worker)
1967
+ )
1968
+ return yield* failure(operation, "worker-mismatch");
1969
+
1970
+ return origin;
1971
+ });
1972
+
1973
+ const receiptInput = Effect.fn("WorkerHost.receiptInput")(function* (
1974
+ request: WorkerReceiptRequest,
1975
+ operation: WorkerError["operation"],
1976
+ ) {
1977
+ const origin = yield* findOrigin(request.worker, request.target, operation);
1978
+ const receipt = request.receipt;
1979
+
1980
+ const snapshot = yield* deps.ledger
1981
+ .lookup(SubmissionLookupById.make({ submissionId: receipt.submissionId }))
1982
+ .pipe(Effect.mapError(storageFailure(operation)));
1983
+
1984
+ if (Option.isNone(snapshot)) return yield* failure(operation, "not-found");
1985
+ const row = snapshot.value;
1986
+
1987
+ if (
1988
+ row.threadId !== request.worker.threadId ||
1989
+ row.threadId !== receipt.threadId ||
1990
+ row.receiptId !== receipt.receiptId ||
1991
+ row.queueSequence !== receipt.queueSequence ||
1992
+ row.workerAdmission === undefined ||
1993
+ !sameOrigin(row.workerAdmission.origin, origin)
1994
+ )
1995
+ return yield* failure(operation, "receipt-mismatch");
1996
+
1997
+ return row.workerAdmission;
1998
+ });
1999
+
2000
+ const observation = Effect.fn("WorkerHost.observation")(function* (
2001
+ request: WorkerReceiptRequest,
2002
+ admission: WorkerAdmission,
2003
+ settlement: Settlement,
2004
+ ): Effect.fn.Return<WorkerObservation, WorkerError> {
2005
+ const history = yield* read(request.worker.threadId, "inspect");
2006
+
2007
+ const record = history.records.find(
2008
+ ({ record }) =>
2009
+ record.payload._tag === "SubmissionSettled" &&
2010
+ record.payload.submissionId === settlement.submissionId,
2011
+ )?.record.payload;
2012
+
2013
+ if (
2014
+ record?._tag !== "SubmissionSettled" ||
2015
+ record.settlementId !== settlement.settlementId ||
2016
+ record.receiptId !== request.receipt.receiptId ||
2017
+ record.outcome !== settlement.outcome
2018
+ )
2019
+ return yield* failure("inspect", "corrupt");
2020
+
2021
+ const input = history.records.find(
2022
+ ({ record }) =>
2023
+ record.payload._tag === "UserInputRecorded" &&
2024
+ record.payload.submissionId === settlement.submissionId,
2025
+ )?.record.payload;
2026
+
2027
+ const inputRunId = input?._tag === "UserInputRecorded" ? input.runId : undefined;
2028
+
2029
+ if (inputRunId !== undefined && record.runId !== undefined && inputRunId !== record.runId)
2030
+ return yield* failure("inspect", "corrupt");
2031
+ const runId = record.runId ?? inputRunId;
2032
+
2033
+ // The host settlement precedes the joined members and carries the Run's actual output.
2034
+ // Both identities come from canonical history; no foreign recovery or finalization is needed.
2035
+ const host =
2036
+ runId === undefined
2037
+ ? undefined
2038
+ : history.records.find(
2039
+ ({ record }) =>
2040
+ record.payload._tag === "SubmissionSettled" && record.payload.runId === runId,
2041
+ )?.record.payload;
2042
+
2043
+ const result = host?._tag === "SubmissionSettled" ? host : record;
2044
+
2045
+ if (result.outcome !== record.outcome) return yield* failure("inspect", "corrupt");
2046
+
2047
+ return {
2048
+ _tag: "Settled",
2049
+ receipt: request.receipt,
2050
+ settlementId: settlement.settlementId,
2051
+ ...(result.runId === undefined ? {} : { runId: result.runId }),
2052
+ outcome: settlement.outcome,
2053
+ encodedParameters: admission.parameters,
2054
+ encodedResult: result.result ?? null,
2055
+ ...(result.outcome === "failed" ? { diagnostic: result.result } : {}),
2056
+ budgetExhausted: result.finishReason === "budget-exhausted",
2057
+ };
2058
+ });
2059
+
2060
+ const send = Effect.fn("WorkerHost.send")(function* (
2061
+ origin: WorkerOrigin,
2062
+ messageId: IdempotencyKey,
2063
+ encodedInput: unknown,
2064
+ encodedParameters: unknown,
2065
+ principal: Principal,
2066
+ operation: "start" | "followUp",
2067
+ ) {
2068
+ if (Option.isNone(deps.deliveries)) return yield* failure(operation, "unavailable");
2069
+ const deliveries = deps.deliveries.value;
2070
+ const input = yield* decode(PersistedJson, encodedInput, operation);
2071
+ const parameters = yield* decode(PersistedJson, encodedParameters, operation);
2072
+
2073
+ const inputDigest = yield* withCrypto(digestJson(input)).pipe(
2074
+ Effect.mapError(storageFailure(operation)),
2075
+ );
2076
+
2077
+ const key = { ownerThreadId: context.source.threadId, messageId };
2078
+ const saved = yield* deliveries.get(key).pipe(Effect.mapError(storageFailure(operation)));
2079
+
2080
+ if (saved !== null) {
2081
+ const metadata = saved.envelope.workerAdmission;
2082
+
2083
+ if (
2084
+ metadata === undefined ||
2085
+ !sameOrigin(metadata.origin, origin) ||
2086
+ !sameJson(metadata.parameters, parameters) ||
2087
+ saved.envelope.inputDigest !== inputDigest ||
2088
+ !sameJson(saved.envelope.input, input)
2089
+ )
2090
+ return yield* failure(operation, "idempotency-conflict");
2091
+ if (saved.receipt !== null) return yield* deliveryStatus(saved, operation);
2092
+ } else {
2093
+ const now = yield* Clock.currentTimeMillis;
2094
+
2095
+ if (now >= origin.expiresAtMillis) return yield* failure(operation, "capacity");
2096
+
2097
+ const envelope: PreparedInput = {
2098
+ schemaVersion: 1,
2099
+ threadId: origin.worker.threadId,
2100
+ deliveryPrincipal: principal,
2101
+ agentId: origin.worker.targetAgentId,
2102
+ definitions: origin.targetDigests,
2103
+ input,
2104
+ inputDigest,
2105
+ admissionKey: messageId,
2106
+ authorization: { policyId: "worker-host", decisionId: messageId },
2107
+ workerAdmission: {
2108
+ origin,
2109
+ messageId,
2110
+ parameters,
2111
+ createdAtMillis: now,
2112
+ deliveryPrincipal: principal,
2113
+ ...(sourceSubmissionId === undefined ? {} : { sourceSubmissionId }),
2114
+ },
2115
+ };
2116
+
2117
+ const prepared = yield* withCrypto(
2118
+ prepareMessageDelivery({
2119
+ key,
2120
+ envelope,
2121
+ createdAtMillis: now,
2122
+ deadlineAtMillis: origin.expiresAtMillis,
2123
+ }),
2124
+ ).pipe(Effect.mapError(storageFailure(operation)));
2125
+
2126
+ yield* deliveries
2127
+ .insert(prepared)
2128
+ .pipe(
2129
+ Effect.mapError((error) =>
2130
+ failure(
2131
+ operation,
2132
+ error._tag === "MessageDeliveryError" && error.reason === "capacity"
2133
+ ? "capacity"
2134
+ : "storage",
2135
+ error,
2136
+ ),
2137
+ ),
2138
+ );
2139
+ }
2140
+
2141
+ // Direct callers and automatic pumps run the same claim/accept/refuse protocol. A
2142
+ // conclusive refusal is retained before it becomes visible to this caller.
2143
+ const processed = yield* MessageDeliveryDriver.pipe(
2144
+ Effect.flatMap((driver) => driver.process(key)),
2145
+ Effect.provide(MessageDeliveryDriver.layer({ batchSize: 1, concurrency: 1 })),
2146
+ Effect.provideService(MessageDeliveryStore, deliveries),
2147
+ Effect.provideService(PreparedInputAdmission, {
2148
+ submit: (prepared) =>
2149
+ deps
2150
+ .submit(prepared)
2151
+ .pipe(
2152
+ Effect.mapError((error) =>
2153
+ error._tag === "AdmissionConflict" || error._tag === "AgentInputError"
2154
+ ? ScheduledInputRefused.make({ code: error._tag, cause: error })
2155
+ : error._tag === "AdmissionPolicyError" && error.reason === "refused"
2156
+ ? ScheduledInputRefused.make({ code: error.code, cause: error })
2157
+ : ScheduledInputRetryable.make({ reason: "storage", cause: error }),
2158
+ ),
2159
+ ),
2160
+ submissionStatus: (receipt) =>
2161
+ deps
2162
+ .status(receipt)
2163
+ .pipe(
2164
+ Effect.mapError((error) =>
2165
+ ScheduledInputRetryable.make({ reason: "storage", cause: error }),
2166
+ ),
2167
+ ),
2168
+ }),
2169
+ Effect.provideService(Crypto.Crypto, deps.crypto),
2170
+ Effect.mapError(storageFailure(operation)),
2171
+ );
2172
+
2173
+ return yield* deliveryStatus(processed, operation);
2174
+ });
2175
+
2176
+ const messageIdFor = (parts: ReadonlyArray<string>) =>
2177
+ withCrypto(digestJson(parts)).pipe(
2178
+ Effect.map((digest) => Schema.decodeSync(IdempotencyKey)(`worker:${digest}`)),
2179
+ Effect.mapError(storageFailure("start")),
2180
+ );
2181
+
2182
+ const summarize = Effect.fn("WorkerHost.summarize")(function* (
2183
+ origin: WorkerOrigin,
2184
+ operation: "list" | "inspect",
2185
+ ): Effect.fn.Return<WorkerSummary, WorkerError> {
2186
+ yield* authorize(operation, "read", origin.worker);
2187
+ if (deps.ledger.inspectWorker === undefined || Option.isNone(deps.deliveries))
2188
+ return yield* failure(operation, "unavailable");
2189
+ const threadId = origin.worker.threadId;
2190
+ const deliveries = deps.deliveries.value;
2191
+
2192
+ const pending = () =>
2193
+ deliveries
2194
+ .list({ ownerThreadId: context.source.threadId, pendingWorker: threadId, limit: 1 })
2195
+ .pipe(
2196
+ Effect.mapError(storageFailure(operation)),
2197
+ Effect.map((page) => page.items[0]),
2198
+ );
2199
+
2200
+ const tail = () =>
2201
+ deps.store.inspectTail(ThreadTailRequest.make({ threadId })).pipe(
2202
+ Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed(undefined)),
2203
+ Effect.mapError(storageFailure(operation)),
2204
+ );
2205
+
2206
+ const receipt = (row: SubmissionSnapshot): Receipt => ({
2207
+ threadId: row.threadId,
2208
+ submissionId: row.submissionId,
2209
+ receiptId: row.receiptId,
2210
+ queueSequence: row.queueSequence,
2211
+ });
2212
+
2213
+ const input = (row: SubmissionSnapshot) => {
2214
+ if (row.workerAdmission === undefined || !sameOrigin(row.workerAdmission.origin, origin))
2215
+ return failure(operation, "corrupt");
2216
+
2217
+ return Effect.succeed({ receipt: receipt(row), messageId: row.workerAdmission.messageId });
2218
+ };
2219
+
2220
+ const submission = (submissionId: SubmissionId) =>
2221
+ deps.ledger.lookup(SubmissionLookupById.make({ submissionId })).pipe(
2222
+ Effect.mapError(storageFailure(operation)),
2223
+ Effect.flatMap((row) =>
2224
+ Option.isNone(row) ? failure(operation, "corrupt") : Effect.succeed(row.value),
2225
+ ),
2226
+ );
2227
+
2228
+ // Native pages and exact lookups only. Retry changing watermarks instead of combining
2229
+ // acceptance, application and completion facts that never coexisted.
2230
+ for (let attempt = 0; attempt < 16; attempt++) {
2231
+ const delivery = yield* pending();
2232
+
2233
+ const control = yield* deps.ledger
2234
+ .inspectWorker(threadId)
2235
+ .pipe(Effect.mapError(storageFailure(operation)));
2236
+
2237
+ const current = yield* tail();
2238
+
2239
+ const records =
2240
+ current === undefined
2241
+ ? []
2242
+ : yield* deps.store
2243
+ .read({
2244
+ threadId,
2245
+ selection: {
2246
+ _tag: "WorkerExecution",
2247
+ expectedTailSequence: current.tailSequence,
2248
+ expectedTailDigest: current.tailDigest,
2249
+ },
2250
+ page: { limit: 2 },
2251
+ })
2252
+ .pipe(
2253
+ Stream.runCollect,
2254
+ Effect.mapError(storageFailure(operation)),
2255
+ Effect.catch((error) =>
2256
+ Effect.gen(function* () {
2257
+ // An unavailable recheck cannot justify discarding the original error.
2258
+ const after = yield* tail().pipe(Effect.orElseSucceed(() => current));
2259
+
2260
+ if (
2261
+ after !== undefined &&
2262
+ (current.tailSequence !== after.tailSequence ||
2263
+ current.tailDigest !== after.tailDigest)
2264
+ )
2265
+ return undefined;
2266
+
2267
+ return yield* error;
2268
+ }),
2269
+ ),
2270
+ );
2271
+
2272
+ if (records === undefined) continue;
2273
+
2274
+ const applied = records.find((entry) => entry.record.payload._tag === "UserInputRecorded");
2275
+
2276
+ const started = records.find((entry) => entry.record.payload._tag === "RunStarted")?.record
2277
+ .payload;
2278
+
2279
+ const acceptedInput = control.latest === null ? null : yield* input(control.latest);
2280
+ let appliedInput: WorkerSummary["appliedInput"] = null;
2281
+ let run: WorkerSummary["run"] = null;
2282
+
2283
+ if (applied?.record.payload._tag === "UserInputRecorded") {
2284
+ const value = applied.record.payload;
2285
+
2286
+ if (value.submissionId === undefined || value.runId === undefined)
2287
+ return yield* failure(operation, "corrupt");
2288
+ appliedInput = {
2289
+ ...(yield* input(yield* submission(value.submissionId))),
2290
+ runId: value.runId,
2291
+ sequence: applied.sequence,
2292
+ };
2293
+ }
2294
+ if (started?._tag === "RunStarted") {
2295
+ const initial = Option.getOrUndefined(
2296
+ yield* getRunInput({ threadId, runId: started.runId }).pipe(
2297
+ Effect.provideService(ThreadStore, deps.store),
2298
+ Effect.mapError(storageFailure(operation)),
2299
+ ),
2300
+ )?.record.payload;
2301
+
2302
+ if (initial?._tag !== "UserInputRecorded" || initial.submissionId === undefined)
2303
+ return yield* failure(operation, "corrupt");
2304
+ const host = yield* input(yield* submission(initial.submissionId));
2305
+
2306
+ const terminal = Option.getOrUndefined(
2307
+ yield* exactRecord(
2308
+ threadId,
2309
+ submissionSettlementRecordId(initial.submissionId),
2310
+ operation,
2311
+ ),
2312
+ )?.record.payload;
2313
+
2314
+ if (
2315
+ terminal !== undefined &&
2316
+ (terminal._tag !== "SubmissionSettled" || terminal.runId !== started.runId)
2317
+ )
2318
+ return yield* failure(operation, "corrupt");
2319
+
2320
+ run = {
2321
+ runId: started.runId,
2322
+ hostReceipt: host.receipt,
2323
+ outcome: terminal?.outcome ?? null,
2324
+ disposition: terminal?.runDisposition ?? null,
2325
+ };
2326
+ }
2327
+
2328
+ const after = yield* deps.ledger
2329
+ .inspectWorker(threadId)
2330
+ .pipe(Effect.mapError(storageFailure(operation)));
2331
+
2332
+ const afterTail = yield* tail();
2333
+ const afterDelivery = yield* pending();
2334
+
2335
+ if (
2336
+ !Schema.toEquivalence(WorkerLedgerState)(control, after) ||
2337
+ current?.tailSequence !== afterTail?.tailSequence ||
2338
+ current?.tailDigest !== afterTail?.tailDigest ||
2339
+ delivery?.key.messageId !== afterDelivery?.key.messageId ||
2340
+ delivery?.version !== afterDelivery?.version
2341
+ )
2342
+ continue;
2343
+
2344
+ return {
2345
+ worker: origin.worker,
2346
+ latestReceipt: acceptedInput?.receipt ?? null,
2347
+ state:
2348
+ control.terminal ??
2349
+ (control.stopped
2350
+ ? control.active === null
2351
+ ? "stopped"
2352
+ : "stopping"
2353
+ : control.active !== null
2354
+ ? "active"
2355
+ : delivery !== undefined
2356
+ ? "starting"
2357
+ : "idle"),
2358
+ acceptedInput,
2359
+ appliedInput,
2360
+ run,
2361
+ pendingDelivery:
2362
+ delivery === undefined ? null : yield* deliveryStatus(delivery, operation),
2363
+ watermark: {
2364
+ canonicalSequence: current?.tailSequence ?? 0,
2365
+ acceptedQueueSequence: control.latest?.queueSequence ?? 0,
2366
+ pendingDeliveryVersion: delivery?.version ?? null,
2367
+ },
2368
+ };
2369
+ }
2370
+
2371
+ return yield* failure(operation, "storage");
2372
+ });
2373
+
2374
+ return {
2375
+ context: Effect.gen(function* () {
2376
+ yield* authorize("context", "context");
2377
+ if (context.depth !== 0) return context;
2378
+ const source = yield* sourceAuthority(context.source.threadId, sourceSubmissionId);
2379
+
2380
+ return {
2381
+ ...context,
2382
+ policy: Option.getOrElse(source.policyOverride, () => context.policy),
2383
+ };
2384
+ }),
2385
+ resolveTargetPolicy: Effect.fn("WorkerHost.resolvePreparedTargetPolicy")(function* (request) {
2386
+ const previous =
2387
+ request.continuationOf === undefined
2388
+ ? undefined
2389
+ : yield* completedContinuation(
2390
+ context.source,
2391
+ request.continuationOf,
2392
+ request.target.id,
2393
+ request.continuationOf.delegationId,
2394
+ );
2395
+
2396
+ yield* authorize("start", "send", undefined, previous?.evidence);
2397
+
2398
+ return (yield* preparedTarget(request.target, request.encodedInput, previous?.evidence))
2399
+ .policy;
2400
+ }),
2401
+ start: Effect.fn("WorkerHost.start")(function* <E = never, R = never>(
2402
+ command: StartWorkerRequest | DeferredStartWorkerRequest<E, R>,
2403
+ ): Effect.fn.Return<WorkerStarted, WorkerError | E, R> {
2404
+ const previous =
2405
+ command.continuationOf === undefined
2406
+ ? undefined
2407
+ : yield* completedContinuation(
2408
+ context.source,
2409
+ command.continuationOf,
2410
+ command.target.id,
2411
+ command.delegationId,
2412
+ );
2413
+
2414
+ const principal = yield* authorize("start", "send", undefined, previous?.evidence);
2415
+
2416
+ if (context.depth !== 0 && sourceSubmissionId === undefined)
2417
+ return yield* failure("start", "denied");
2418
+
2419
+ const grant = yield* Schema.decodeUnknownEffect(SubagentGrant)(command.encodedGrant).pipe(
2420
+ Effect.mapError((cause) => failure("start", "corrupt", cause)),
2421
+ );
2422
+
2423
+ const parameters = yield* decode(PersistedJson, command.encodedParameters, "start");
2424
+
2425
+ const messageId = yield* messageIdFor([
2426
+ context.source.threadId,
2427
+ command.delegationId,
2428
+ command.idempotencyKey,
2429
+ ]);
2430
+
2431
+ if (Option.isNone(deps.deliveries)) return yield* failure("start", "unavailable");
2432
+ const deliveries = deps.deliveries.value;
2433
+
2434
+ const retained = () =>
2435
+ deliveries
2436
+ .get({ ownerThreadId: context.source.threadId, messageId })
2437
+ .pipe(Effect.mapError(storageFailure("start")));
2438
+
2439
+ const replay = (saved: MessageDeliveryRecord) =>
2440
+ Effect.gen(function* () {
2441
+ const currentPrevious =
2442
+ command.continuationOf === undefined
2443
+ ? undefined
2444
+ : yield* completedContinuation(
2445
+ context.source,
2446
+ command.continuationOf,
2447
+ command.target.id,
2448
+ command.delegationId,
2449
+ previous?.evidence,
2450
+ );
2451
+
2452
+ const replayPrincipal = yield* authorize(
2453
+ "start",
2454
+ "send",
2455
+ undefined,
2456
+ currentPrevious?.evidence,
2457
+ );
2458
+
2459
+ yield* binding(command.target, "start");
2460
+ const metadata = saved.envelope.workerAdmission;
2461
+
2462
+ if (metadata === undefined) return yield* failure("start", "idempotency-conflict");
2463
+ const origin = metadata.origin;
2464
+
2465
+ if (
2466
+ !Schema.toEquivalence(Schema.UndefinedOr(WorkerContinuation))(
2467
+ origin.continuationOf,
2468
+ currentPrevious?.evidence,
2469
+ ) ||
2470
+ origin.firstMessageId !== messageId ||
2471
+ metadata.messageId !== messageId ||
2472
+ origin.source.threadId !== context.source.threadId ||
2473
+ origin.worker.delegationId !== command.delegationId ||
2474
+ origin.worker.targetAgentId !== command.target.id ||
2475
+ !sameJson(metadata.parameters, parameters) ||
2476
+ !Schema.toEquivalence(SubagentGrant)(origin.grant, grant) ||
2477
+ origin.budgetScope !== command.budgetScope ||
2478
+ origin.depth !== context.depth + 1 ||
2479
+ (!("prepare" in command) &&
2480
+ (!samePolicy(origin.policy, command.policy) ||
2481
+ !Schema.toEquivalence(WorkerOrigin.fields.budget)(
2482
+ origin.budget,
2483
+ command.budget,
2484
+ ) ||
2485
+ origin.toolCallAllowance !== command.toolCallAllowance))
2486
+ )
2487
+ return yield* failure("start", "idempotency-conflict");
2488
+
2489
+ yield* verifyContinuation(origin);
2490
+
2491
+ return {
2492
+ worker: origin.worker,
2493
+ delivery: yield* send(
2494
+ origin,
2495
+ messageId,
2496
+ "prepare" in command ? saved.envelope.input : command.encodedInput,
2497
+ parameters,
2498
+ replayPrincipal,
2499
+ "start",
2500
+ ),
2501
+ };
2502
+ });
2503
+
2504
+ const existing = yield* retained();
2505
+
2506
+ if (existing !== null) return yield* replay(existing);
2507
+
2508
+ return yield* Effect.gen(function* () {
2509
+ const request =
2510
+ "prepare" in command ? { ...(yield* command.prepare), ...command } : command;
2511
+
2512
+ const currentPrevious =
2513
+ command.continuationOf === undefined
2514
+ ? undefined
2515
+ : yield* completedContinuation(
2516
+ context.source,
2517
+ command.continuationOf,
2518
+ command.target.id,
2519
+ command.delegationId,
2520
+ previous?.evidence,
2521
+ );
2522
+
2523
+ if (currentPrevious !== undefined) {
2524
+ const preparedPrincipal = yield* authorize(
2525
+ "start",
2526
+ "send",
2527
+ undefined,
2528
+ currentPrevious.evidence,
2529
+ );
2530
+
2531
+ if (preparedPrincipal !== principal) return yield* failure("start", "denied");
2532
+ }
2533
+
2534
+ const prepared = yield* preparedTarget(
2535
+ request.target,
2536
+ request.encodedInput,
2537
+ currentPrevious?.evidence,
2538
+ );
2539
+
2540
+ const resolved = prepared.resolved;
2541
+
2542
+ const sourcePolicy = Option.getOrElse(
2543
+ prepared.source.policyOverride,
2544
+ () => context.policy,
2545
+ );
2546
+
2547
+ const now = yield* Clock.currentTimeMillis;
2548
+ const sourceBinding = prepared.source.binding;
2549
+
2550
+ if (sourceBinding === undefined)
2551
+ return yield* failure("start", "declaration-unavailable");
2552
+
2553
+ const reporting = yield* reportIntent(
2554
+ sourceBinding,
2555
+ resolved,
2556
+ request.delegationId,
2557
+ prepared.source,
2558
+ );
2559
+
2560
+ if (reporting?.mode === "standard" && sourceSubmissionId === undefined)
2561
+ return yield* failure("start", "denied");
2562
+
2563
+ const origin = yield* decode(
2564
+ WorkerOrigin,
2565
+ {
2566
+ worker: {
2567
+ schemaVersion: 1,
2568
+ delegationId: request.delegationId,
2569
+ targetAgentId: request.target.id,
2570
+ threadId: Schema.decodeSync(ThreadId)(messageId),
2571
+ },
2572
+ source: context.source,
2573
+ ...(currentPrevious === undefined
2574
+ ? {}
2575
+ : { continuationOf: currentPrevious.evidence }),
2576
+ ...(resolved.definition.runDisposition?.workerLifecycle === "assignment"
2577
+ ? { lifecycle: "assignment" }
2578
+ : {}),
2579
+ targetDigests: resolved.digests,
2580
+ policy: request.policy,
2581
+ budget: request.budget,
2582
+ ...(request.budgetScope === undefined ? {} : { budgetScope: request.budgetScope }),
2583
+ grant,
2584
+ depth: context.depth + 1,
2585
+ firstMessageId: messageId,
2586
+ createdAtMillis: now,
2587
+ expiresAtMillis: Math.min(
2588
+ now + deps.limits.lifetimeMillis,
2589
+ currentPrevious?.origin.expiresAtMillis ?? Infinity,
2590
+ ),
2591
+ ...(reporting === undefined ? {} : { reporting }),
2592
+ ...(request.toolCallAllowance === undefined
2593
+ ? {}
2594
+ : { toolCallAllowance: request.toolCallAllowance }),
2595
+ },
2596
+ "start",
2597
+ );
2598
+
2599
+ yield* authorizeBudget(origin, principal);
2600
+
2601
+ const targetPolicy = Option.isSome(prepared.policy)
2602
+ ? prepared.policy.value
2603
+ : origin.budgetScope === "worker-run"
2604
+ ? resolved.definition.policy
2605
+ : AgentPolicy.resolve(
2606
+ resolved.definition.policyOverrides ?? resolved.definition.policy,
2607
+ sourcePolicy,
2608
+ );
2609
+
2610
+ if (
2611
+ !withinPolicy(
2612
+ origin,
2613
+ origin.budgetScope === "worker-run" ? targetPolicy : sourcePolicy,
2614
+ targetPolicy,
2615
+ )
2616
+ )
2617
+ return yield* failure("start", "capacity");
2618
+
2619
+ const delivery = yield* send(
2620
+ origin,
2621
+ messageId,
2622
+ request.encodedInput,
2623
+ request.encodedParameters,
2624
+ principal,
2625
+ "start",
2626
+ );
2627
+
2628
+ return { worker: origin.worker, delivery };
2629
+ }).pipe(
2630
+ Effect.catch((error) =>
2631
+ Effect.gen(function* () {
2632
+ if (!("prepare" in command)) return yield* Effect.fail(error);
2633
+ // A concurrent first writer may have retained the command during preparation or insert.
2634
+ // Only its exact declared arguments permit replay; absent proof preserves the failure.
2635
+ const saved = yield* retained();
2636
+
2637
+ return saved === null ? yield* Effect.fail(error) : yield* replay(saved);
2638
+ }),
2639
+ ),
2640
+ );
2641
+ }),
2642
+ followUp: Effect.fn("WorkerHost.followUp")(function* <E = never, R = never>(
2643
+ command: FollowUpWorkerRequest | DeferredFollowUpWorkerRequest<E, R>,
2644
+ ): Effect.fn.Return<MessageStatus, WorkerError | E, R> {
2645
+ const principal = yield* authorize("followUp", "send", command.worker);
2646
+ const origin = yield* findOrigin(command.worker, command.target, "followUp");
2647
+
2648
+ const currentPolicy = resolveTargetPolicy({
2649
+ _tag: "RetainedWorker",
2650
+ definition: command.target,
2651
+ definitions: origin.targetDigests,
2652
+ source: origin.source,
2653
+ origin,
2654
+ });
2655
+
2656
+ yield* currentPolicy;
2657
+
2658
+ const messageId = yield* messageIdFor([
2659
+ context.source.threadId,
2660
+ command.worker.threadId,
2661
+ "followUp",
2662
+ command.idempotencyKey,
2663
+ ]);
2664
+
2665
+ if (Option.isNone(deps.deliveries)) return yield* failure("followUp", "unavailable");
2666
+ const deliveries = deps.deliveries.value;
2667
+
2668
+ const retained = () =>
2669
+ deliveries
2670
+ .get({ ownerThreadId: context.source.threadId, messageId })
2671
+ .pipe(Effect.mapError(storageFailure("followUp")));
2672
+
2673
+ const replay = (saved: MessageDeliveryRecord) =>
2674
+ Effect.gen(function* () {
2675
+ const replayPrincipal = yield* authorize("followUp", "send", command.worker);
2676
+
2677
+ yield* currentPolicy;
2678
+
2679
+ return yield* send(
2680
+ origin,
2681
+ messageId,
2682
+ saved.envelope.input,
2683
+ command.encodedParameters,
2684
+ replayPrincipal,
2685
+ "followUp",
2686
+ );
2687
+ });
2688
+
2689
+ const existing = "prepare" in command ? yield* retained() : null;
2690
+
2691
+ if (existing !== null) return yield* replay(existing);
2692
+
2693
+ return yield* Effect.gen(function* () {
2694
+ const request =
2695
+ "prepare" in command ? { ...(yield* command.prepare), ...command } : command;
2696
+
2697
+ yield* Schema.decodeUnknownEffect(Schema.toEncoded(request.target.input))(
2698
+ request.encodedInput,
2699
+ ).pipe(Effect.mapError((cause) => failure("followUp", "corrupt", cause)));
2700
+
2701
+ let sendPrincipal = principal;
2702
+
2703
+ if ("prepare" in command) {
2704
+ // Preparation can yield while authority changes, even when another writer saves equal input.
2705
+ sendPrincipal = yield* authorize("followUp", "send", command.worker);
2706
+ yield* currentPolicy;
2707
+ }
2708
+
2709
+ return yield* send(
2710
+ origin,
2711
+ messageId,
2712
+ request.encodedInput,
2713
+ request.encodedParameters,
2714
+ sendPrincipal,
2715
+ "followUp",
2716
+ );
2717
+ }).pipe(
2718
+ Effect.catch((error) =>
2719
+ Effect.gen(function* () {
2720
+ if (!("prepare" in command)) return yield* Effect.fail(error);
2721
+ // A concurrent first writer can retain the original correction during preparation.
2722
+ // Replay rechecks current authority before reusing its immutable envelope.
2723
+ const saved = yield* retained();
2724
+
2725
+ return saved === null ? yield* Effect.fail(error) : yield* replay(saved);
2726
+ }),
2727
+ ),
2728
+ );
2729
+ }),
2730
+ inspect: Effect.fn("WorkerHost.inspect")(function* (request) {
2731
+ yield* authorize("inspect", "read", request.worker);
2732
+
2733
+ if ("message" in request) {
2734
+ const message = yield* Schema.decodeEffect(MessageRef)(request.message).pipe(
2735
+ Effect.mapError((cause) => failure("inspect", "message-mismatch", cause)),
2736
+ );
2737
+
2738
+ if (message.ownerThreadId !== context.source.threadId)
2739
+ return yield* failure("inspect", "denied");
2740
+ yield* binding(request.target, "inspect");
2741
+ if (request.worker.targetAgentId !== request.target.id)
2742
+ return yield* failure("inspect", "worker-mismatch");
2743
+ if (Option.isNone(deps.deliveries)) return yield* failure("inspect", "unavailable");
2744
+
2745
+ const row = yield* deps.deliveries.value
2746
+ .get(message)
2747
+ .pipe(Effect.mapError(storageFailure("inspect")));
2748
+
2749
+ if (row === null) return yield* failure("inspect", "not-found");
2750
+ const admission = row.envelope.workerAdmission;
2751
+
2752
+ if (
2753
+ admission === undefined ||
2754
+ !Schema.toEquivalence(MessageRef)(row.key, message) ||
2755
+ admission.messageId !== message.messageId ||
2756
+ admission.origin.source.threadId !== context.source.threadId ||
2757
+ !Schema.toEquivalence(WorkerRef)(admission.origin.worker, request.worker) ||
2758
+ row.envelope.threadId !== request.worker.threadId ||
2759
+ row.envelope.agentId !== request.target.id ||
2760
+ row.envelope.admissionKey !== message.messageId
2761
+ )
2762
+ return yield* failure("inspect", "message-mismatch");
2763
+
2764
+ // Retention precedes source reservation and child materialization. Read its own
2765
+ // evidence without requiring either execution journal or attempting delivery again.
2766
+ return yield* deliveryStatus(row, "inspect");
2767
+ }
2768
+
2769
+ const admission = yield* receiptInput(request, "inspect");
2770
+
2771
+ const status = yield* deps
2772
+ .status(request.receipt)
2773
+ .pipe(Effect.mapError(storageFailure("inspect")));
2774
+
2775
+ return status._tag === "pending"
2776
+ ? { _tag: "Pending" as const, receipt: request.receipt }
2777
+ : yield* observation(request, admission, status.settlement);
2778
+ }),
2779
+ summary: Effect.fn("WorkerHost.inspectWorker")(function* (request) {
2780
+ yield* authorize("inspect", "read", request.worker);
2781
+ const origin = yield* findOrigin(request.worker, request.target, "inspect");
2782
+
2783
+ return yield* summarize(origin, "inspect");
2784
+ }),
2785
+ observe: (request) =>
2786
+ Stream.unwrap(
2787
+ Effect.gen(function* () {
2788
+ yield* authorize("observe", "read", request.worker);
2789
+ yield* findOrigin(request.worker, request.target, "observe");
2790
+ const after = request.after ?? 0;
2791
+
2792
+ if (!Number.isSafeInteger(after) || after < 0)
2793
+ return yield* failure("observe", "corrupt");
2794
+
2795
+ const tail = yield* deps.store
2796
+ .inspectTail(ThreadTailRequest.make({ threadId: request.worker.threadId }))
2797
+ .pipe(Effect.mapError(storageFailure("observe")));
2798
+
2799
+ return Stream.paginate(after, (cursor) =>
2800
+ Effect.gen(function* () {
2801
+ if (cursor >= tail.tailSequence) return [[], Option.none<number>()] as const;
2802
+ const limit = Math.min(256, tail.tailSequence - cursor);
2803
+
2804
+ const page = yield* deps.store
2805
+ .read(
2806
+ ThreadRead.make({
2807
+ threadId: request.worker.threadId,
2808
+ afterSequence: Schema.decodeSync(CanonicalSequence)(cursor),
2809
+ limit,
2810
+ }),
2811
+ )
2812
+ .pipe(Stream.runCollect, Effect.mapError(storageFailure("observe")));
2813
+
2814
+ if (
2815
+ page.length === 0 ||
2816
+ page.length > limit ||
2817
+ page.some(
2818
+ (entry, index) =>
2819
+ entry.threadId !== request.worker.threadId ||
2820
+ entry.sequence !== cursor + index + 1,
2821
+ )
2822
+ )
2823
+ return yield* failure("observe", "corrupt");
2824
+
2825
+ const entries = yield* Effect.forEach(page, (entry) =>
2826
+ Schema.encodeEffect(RecordEnvelope)(entry.record).pipe(
2827
+ Effect.flatMap((record) =>
2828
+ Schema.decodeEffect(WorkerHistoryEntry)({
2829
+ sequence: entry.sequence,
2830
+ recordId: entry.record.recordId,
2831
+ record,
2832
+ }),
2833
+ ),
2834
+ Effect.mapError((cause) => failure("observe", "corrupt", cause)),
2835
+ ),
2836
+ );
2837
+
2838
+ const next = cursor + page.length;
2839
+
2840
+ return [
2841
+ entries,
2842
+ next >= tail.tailSequence ? Option.none<number>() : Option.some(next),
2843
+ ] as const;
2844
+ }),
2845
+ );
2846
+ }),
2847
+ ),
2848
+ await: Effect.fn("WorkerHost.await")(function* (request) {
2849
+ yield* authorize("await", "read", request.worker);
2850
+ const admission = yield* receiptInput(request, "await");
2851
+
2852
+ while (true) {
2853
+ const status = yield* deps
2854
+ .status(request.receipt)
2855
+ .pipe(Effect.mapError(storageFailure("await")));
2856
+
2857
+ if (status._tag === "settled")
2858
+ return yield* observation(request, admission, status.settlement);
2859
+ // Poll the same host-routed read used by inspect. Interruption stops only this wait.
2860
+ yield* Effect.sleep(deps.settlementPollInterval);
2861
+ }
2862
+ }),
2863
+ list: Effect.fn("WorkerHost.list")(function* (request) {
2864
+ yield* authorize("list", "read");
2865
+ yield* binding(request.target, "list");
2866
+ if (!Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > 100)
2867
+ return yield* failure("list", "capacity");
2868
+ if (Option.isNone(deps.deliveries)) return yield* failure("list", "unavailable");
2869
+
2870
+ const page = yield* deps.deliveries.value
2871
+ .list({
2872
+ ownerThreadId: context.source.threadId,
2873
+ limit: request.limit,
2874
+ workerStarts: { delegationId: request.delegationId, targetAgentId: request.target.id },
2875
+ ...(request.after === undefined
2876
+ ? {}
2877
+ : { after: Schema.decodeSync(IdempotencyKey)(request.after) }),
2878
+ })
2879
+ .pipe(Effect.mapError(storageFailure("list")));
2880
+
2881
+ const items = yield* Effect.forEach(page.items, (row) => {
2882
+ const origin = row.envelope.workerAdmission?.origin;
2883
+
2884
+ return origin === undefined || origin.firstMessageId !== row.key.messageId
2885
+ ? failure("list", "corrupt")
2886
+ : summarize(origin, "list");
2887
+ });
2888
+
2889
+ return { items, next: page.next === null ? null : Schema.decodeSync(ThreadId)(page.next) };
2890
+ }),
2891
+ stop: Effect.fn("WorkerHost.stop")(function* (request) {
2892
+ const principal = yield* authorize("stop", "control", request.worker);
2893
+
2894
+ yield* findOrigin(request.worker, request.target, "stop");
2895
+ if (deps.ledger.stopWorker === undefined) return yield* failure("stop", "unavailable");
2896
+ const command = yield* decode(WorkerStop, request, "stop");
2897
+
2898
+ const id = Schema.decodeSync(RecordId)(
2899
+ `worker-stop:${yield* messageIdFor([context.source.threadId, request.idempotencyKey])}`,
2900
+ );
2901
+
2902
+ let retained = false;
2903
+
2904
+ for (let attempt = 0; attempt < 16; attempt++) {
2905
+ const current = yield* deps.store
2906
+ .inspectTail(ThreadTailRequest.make({ threadId: context.source.threadId }))
2907
+ .pipe(Effect.mapError(storageFailure("stop")));
2908
+
2909
+ const prior = Option.getOrUndefined(
2910
+ yield* exactRecord(context.source.threadId, id, "stop"),
2911
+ )?.record.payload;
2912
+
2913
+ if (prior !== undefined) {
2914
+ if (
2915
+ prior._tag !== "WorkerStopRequested" ||
2916
+ !Schema.toEquivalence(WorkerStop)(prior.command, command)
2917
+ )
2918
+ return yield* failure("stop", "idempotency-conflict");
2919
+ retained = true;
2920
+ break;
2921
+ }
2922
+ if (
2923
+ yield* append(
2924
+ context.source.threadId,
2925
+ id,
2926
+ WorkerStopRequested.make({ command }),
2927
+ { ...current, records: [] },
2928
+ "stop",
2929
+ )
2930
+ ) {
2931
+ retained = true;
2932
+ break;
2933
+ }
2934
+ }
2935
+ if (!retained) return yield* failure("stop", "storage");
2936
+ // The destination serializes this seal with admission. Replays retain every earlier
2937
+ // abort intent and wait for active handlers/finalizers, rather than acknowledging a hint.
2938
+ while (true) {
2939
+ yield* hit("worker:before-stop-seal", "stop");
2940
+
2941
+ const owned = yield* deps.ledger
2942
+ .stopWorker({ threadId: request.worker.threadId, author: principal })
2943
+ .pipe(Effect.mapError(storageFailure("stop")));
2944
+
2945
+ yield* hit("worker:after-stop-seal", "stop");
2946
+ if (owned === 0) return command;
2947
+ yield* Effect.sleep(deps.settlementPollInterval);
2948
+ }
2949
+ }),
2950
+ cancel: Effect.fn("WorkerHost.cancel")(function* (request) {
2951
+ const principal = yield* authorize("cancel", "control", request.worker);
2952
+
2953
+ yield* receiptInput(request, "cancel");
2954
+ yield* deps
2955
+ .abort(
2956
+ AbortCommand.make({
2957
+ submissionId: request.receipt.submissionId,
2958
+ author: principal,
2959
+ reason: "Worker owner requested cancellation of this Receipt",
2960
+ }),
2961
+ )
2962
+ .pipe(
2963
+ Effect.mapError((error) =>
2964
+ error._tag === "JoinedToHost" ? error : failure("cancel", "storage", error),
2965
+ ),
2966
+ );
2967
+ }),
2968
+ };
2969
+ };
2970
+
2971
+ const validateCompletion = Effect.fn("WorkerHost.validateCompletion")(function* (
2972
+ unvalidated: FrameworkMessage,
2973
+ options: DurableSubmitOptions,
2974
+ agentId: AgentId,
2975
+ inputDigest: Digest,
2976
+ ) {
2977
+ const message = yield* Schema.decodeEffect(FrameworkMessage)(unvalidated).pipe(
2978
+ Effect.mapError((cause) => failure("followUp", "corrupt", cause)),
2979
+ );
2980
+
2981
+ const childThreadId =
2982
+ message._tag === "WorkerCompletion"
2983
+ ? message.report.worker.threadId
2984
+ : message.worker.threadId;
2985
+
2986
+ const proofId =
2987
+ message._tag === "WorkerCompletion"
2988
+ ? workerReportRecordId(options.idempotencyKey)
2989
+ : yield* withCrypto(
2990
+ agentUpdateRecordId(
2991
+ message.update.threadId,
2992
+ message.update.runId,
2993
+ message.update.updateId,
2994
+ ),
2995
+ ).pipe(Effect.mapError(storageFailure("followUp")));
2996
+
2997
+ const proof = Option.getOrUndefined(yield* exactRecord(childThreadId, proofId, "followUp"))
2998
+ ?.record.payload;
2999
+
3000
+ const frozen =
3001
+ message._tag === "WorkerCompletion"
3002
+ ? proof?._tag === "WorkerReportPrepared" &&
3003
+ proof.runId === message.report.runId &&
3004
+ proof.messageId === options.idempotencyKey
3005
+ ? proof.envelope
3006
+ : undefined
3007
+ : proof?._tag === "AgentUpdateEmitted" &&
3008
+ Schema.toEquivalence(Update)(proof.update, message.update)
3009
+ ? proof.delivery?.envelope
3010
+ : undefined;
3011
+
3012
+ if (frozen === undefined) return yield* failure("followUp", "denied");
3013
+
3014
+ const envelope = yield* Schema.decodeUnknownEffect(PreparedInput)(frozen).pipe(
3015
+ Effect.mapError((cause) => failure("followUp", "corrupt", cause)),
3016
+ );
3017
+
3018
+ if (
3019
+ !Schema.is(FrameworkMessage)(envelope.messageAdmission) ||
3020
+ !Schema.toEquivalence(FrameworkMessage)(envelope.messageAdmission, message) ||
3021
+ envelope.threadId !== options.threadId ||
3022
+ envelope.agentId !== agentId ||
3023
+ envelope.inputDigest !== inputDigest ||
3024
+ envelope.deliveryPrincipal !== options.principal ||
3025
+ envelope.admissionKey !== options.idempotencyKey ||
3026
+ !definitionDigestsEqual(envelope.definitions, options.definitions) ||
3027
+ !Schema.toEquivalence(Schema.UndefinedOr(WorkerAdmission))(
3028
+ envelope.workerAdmission,
3029
+ options.workerAdmission,
3030
+ )
3031
+ )
3032
+ return yield* failure("followUp", "denied");
3033
+
3034
+ const recorded = Option.getOrUndefined(
3035
+ yield* exactRecord(childThreadId, workerOriginRecordId(childThreadId), "followUp"),
3036
+ )?.record.payload;
3037
+
3038
+ if (
3039
+ recorded?._tag !== "WorkerOriginRecorded" ||
3040
+ recorded.origin.worker.threadId !== childThreadId ||
3041
+ recorded.origin.reporting?.mode !== "standard"
3042
+ )
3043
+ return yield* failure("followUp", "denied");
3044
+ const origin = recorded.origin;
3045
+
3046
+ const first = Option.getOrUndefined(
3047
+ yield* exactRecord(
3048
+ origin.source.threadId,
3049
+ workerInputRecordId(origin.firstMessageId),
3050
+ "followUp",
3051
+ ),
3052
+ )?.record.payload;
3053
+
3054
+ if (
3055
+ first?._tag !== "WorkerInputRequested" ||
3056
+ first.admission.messageId !== origin.firstMessageId ||
3057
+ !sameOrigin(first.admission.origin, origin)
3058
+ )
3059
+ return yield* failure("followUp", "denied");
3060
+ const receiving = envelope.workerAdmission;
3061
+
3062
+ const sourceSubmissionId =
3063
+ receiving === undefined ? first.admission.sourceSubmissionId : receiving.sourceSubmissionId;
3064
+
3065
+ // Emission only persists the child's outbound message. The receiving runtime
3066
+ // calls this before acceptance and checks live permission, including revocation.
3067
+ // A frozen return address never substitutes for destination authorization.
3068
+ const principal = yield* deps.authorizer.authorize({
3069
+ sourceThreadId: receiving?.origin.source.threadId ?? origin.source.threadId,
3070
+ ...(sourceSubmissionId === undefined ? {} : { sourceSubmissionId }),
3071
+ principal: options.principal,
3072
+ operation: "followUp",
3073
+ access: "report",
3074
+ worker: receiving?.origin.worker ?? origin.worker,
3075
+ });
3076
+
3077
+ if (principal !== options.principal) return yield* failure("followUp", "denied");
3078
+
3079
+ return message;
3080
+ });
3081
+
3082
+ const acquire = Effect.fn("WorkerHost.acquire")(function* (request: {
3083
+ readonly sourceThreadId: ThreadId;
3084
+ readonly principal: Principal;
3085
+ readonly sourceSubmissionId?: SubmissionId;
3086
+ }) {
3087
+ const { sourceThreadId, principal } = request;
3088
+
3089
+ yield* deps.authorizer.authorize({
3090
+ sourceThreadId,
3091
+ ...(request.sourceSubmissionId === undefined
3092
+ ? {}
3093
+ : { sourceSubmissionId: request.sourceSubmissionId }),
3094
+ principal,
3095
+ operation: "context",
3096
+ access: "context",
3097
+ });
3098
+ const current = yield* readIdentity(sourceThreadId, "context");
3099
+ const created = current.records[0]?.record.payload;
3100
+
3101
+ if (created?._tag !== "ThreadCreated") return yield* failure("context", "not-found");
3102
+
3103
+ const selected =
3104
+ request.sourceSubmissionId === undefined
3105
+ ? undefined
3106
+ : yield* sourceAuthority(sourceThreadId, request.sourceSubmissionId);
3107
+
3108
+ const resolved = selected === undefined ? currentBinding(created.agentId) : selected.binding;
3109
+
3110
+ if (resolved === undefined) return yield* failure("context", "declaration-unavailable");
3111
+
3112
+ const origin = current.records.find(
3113
+ ({ record }) => record.payload._tag === "WorkerOriginRecorded",
3114
+ )?.record.payload;
3115
+
3116
+ const attached = current.records.find(
3117
+ ({ record }) => record.payload._tag === "SubagentLineageRecorded",
3118
+ )?.record.payload;
3119
+
3120
+ const retained =
3121
+ origin?._tag === "WorkerOriginRecorded"
3122
+ ? origin.origin
3123
+ : attached?._tag === "SubagentLineageRecorded"
3124
+ ? attached
3125
+ : undefined;
3126
+
3127
+ return facet(
3128
+ {
3129
+ source: {
3130
+ _tag: "programmatic",
3131
+ threadId: sourceThreadId,
3132
+ agentId: selected?.submission?.agentId ?? created.agentId,
3133
+ },
3134
+ policy:
3135
+ retained?.policy ?? selected?.binding?.definition.policy ?? resolved.definition.policy,
3136
+ depth:
3137
+ origin?._tag === "WorkerOriginRecorded"
3138
+ ? origin.origin.depth
3139
+ : attached?._tag === "SubagentLineageRecorded"
3140
+ ? attached.parentLink.depth
3141
+ : 0,
3142
+ ...(retained?.grant === undefined ? {} : { grant: retained.grant }),
3143
+ },
3144
+ principal,
3145
+ request.sourceSubmissionId,
3146
+ );
3147
+ });
3148
+
3149
+ return {
3150
+ facet,
3151
+ prepareUpdate,
3152
+ acquire,
3153
+ validateCompletion,
3154
+ validateAdmission,
3155
+ ensureOrigin,
3156
+ completeInput,
3157
+ reserveSubtree,
3158
+ };
3159
+ });