effect-agent 0.0.1-beta.6 → 0.1.0-beta.100

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