effect-agent 0.1.0-beta.13 → 0.1.0-beta.130

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