effect-agent 0.1.0-beta.12 → 0.1.0-beta.121

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