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

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