effect-agent 0.1.0-beta.11 → 0.1.0-beta.111

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