effect-agent 0.1.0-beta.15 → 0.1.0-beta.151

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