@plurnk/plurnk-service 1.16.5 → 1.18.0

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 (661) hide show
  1. package/.env.defaults +140 -246
  2. package/INSTALL.md +84 -138
  3. package/README.md +10 -6
  4. package/SPEC.md +2468 -1694
  5. package/dist/Paths.d.ts +2 -0
  6. package/dist/Paths.d.ts.map +1 -1
  7. package/dist/Paths.js +2 -0
  8. package/dist/Paths.js.map +1 -1
  9. package/dist/build-info.d.ts.map +1 -1
  10. package/dist/build-info.js +7 -1
  11. package/dist/build-info.js.map +1 -1
  12. package/dist/build-info.json +1 -1
  13. package/dist/content/body-preview.d.ts +8 -0
  14. package/dist/content/body-preview.d.ts.map +1 -0
  15. package/dist/content/body-preview.js +39 -0
  16. package/dist/content/body-preview.js.map +1 -0
  17. package/dist/content/byte-view.d.ts +1 -4
  18. package/dist/content/byte-view.d.ts.map +1 -1
  19. package/dist/content/byte-view.js.map +1 -1
  20. package/dist/content/edit-collision.d.ts.map +1 -1
  21. package/dist/content/edit-collision.js +3 -4
  22. package/dist/content/edit-collision.js.map +1 -1
  23. package/dist/content/line-anchors.d.ts +2 -2
  24. package/dist/content/line-anchors.d.ts.map +1 -1
  25. package/dist/content/line-anchors.js +4 -3
  26. package/dist/content/line-anchors.js.map +1 -1
  27. package/dist/content/line-selection.d.ts +9 -0
  28. package/dist/content/line-selection.d.ts.map +1 -0
  29. package/dist/content/line-selection.js +29 -0
  30. package/dist/content/line-selection.js.map +1 -0
  31. package/dist/content/matcher.d.ts +0 -1
  32. package/dist/content/matcher.d.ts.map +1 -1
  33. package/dist/content/matcher.js +2 -3
  34. package/dist/content/matcher.js.map +1 -1
  35. package/dist/content/pattern-edits.d.ts +39 -0
  36. package/dist/content/pattern-edits.d.ts.map +1 -0
  37. package/dist/content/pattern-edits.js +121 -0
  38. package/dist/content/pattern-edits.js.map +1 -0
  39. package/dist/content/read-projector.d.ts +18 -9
  40. package/dist/content/read-projector.d.ts.map +1 -1
  41. package/dist/content/read-projector.js +132 -35
  42. package/dist/content/read-projector.js.map +1 -1
  43. package/dist/content/read-resolve.d.ts +4 -1
  44. package/dist/content/read-resolve.d.ts.map +1 -1
  45. package/dist/content/read-resolve.js +42 -7
  46. package/dist/content/read-resolve.js.map +1 -1
  47. package/dist/core/AdmittedTurnExecutor.d.ts +2 -5
  48. package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
  49. package/dist/core/AdmittedTurnExecutor.js +96 -88
  50. package/dist/core/AdmittedTurnExecutor.js.map +1 -1
  51. package/dist/core/BareBatchRunner.d.ts +6 -1
  52. package/dist/core/BareBatchRunner.d.ts.map +1 -1
  53. package/dist/core/BareBatchRunner.js +20 -5
  54. package/dist/core/BareBatchRunner.js.map +1 -1
  55. package/dist/core/BudgetReadout.d.ts +2 -3
  56. package/dist/core/BudgetReadout.d.ts.map +1 -1
  57. package/dist/core/BudgetReadout.js +23 -32
  58. package/dist/core/BudgetReadout.js.map +1 -1
  59. package/dist/core/CapabilityPolicies.d.ts +3 -8
  60. package/dist/core/CapabilityPolicies.d.ts.map +1 -1
  61. package/dist/core/CapabilityPolicies.js +2 -28
  62. package/dist/core/CapabilityPolicies.js.map +1 -1
  63. package/dist/core/CapabilityResolver.d.ts +10 -10
  64. package/dist/core/CapabilityResolver.d.ts.map +1 -1
  65. package/dist/core/CapabilityResolver.js +122 -93
  66. package/dist/core/CapabilityResolver.js.map +1 -1
  67. package/dist/core/ChannelWrite.d.ts +15 -11
  68. package/dist/core/ChannelWrite.d.ts.map +1 -1
  69. package/dist/core/ChannelWrite.js +12 -13
  70. package/dist/core/ChannelWrite.js.map +1 -1
  71. package/dist/core/ChannelWrite.sql +99 -9
  72. package/dist/core/ClientInteractions.d.ts.map +1 -1
  73. package/dist/core/ClientInteractions.js +7 -0
  74. package/dist/core/ClientInteractions.js.map +1 -1
  75. package/dist/core/CoreSchemeServices.d.ts +13 -10
  76. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  77. package/dist/core/CoreSchemeServices.js +13 -5
  78. package/dist/core/CoreSchemeServices.js.map +1 -1
  79. package/dist/core/DataStatementRunner.d.ts +2 -2
  80. package/dist/core/DataStatementRunner.d.ts.map +1 -1
  81. package/dist/core/DataStatementRunner.js +208 -161
  82. package/dist/core/DataStatementRunner.js.map +1 -1
  83. package/dist/core/Dispatcher.d.ts +24 -26
  84. package/dist/core/Dispatcher.d.ts.map +1 -1
  85. package/dist/core/Dispatcher.js +381 -232
  86. package/dist/core/Dispatcher.js.map +1 -1
  87. package/dist/core/Dispatcher.sql +66 -0
  88. package/dist/core/DurableStatement.d.ts.map +1 -1
  89. package/dist/core/DurableStatement.js +2 -1
  90. package/dist/core/DurableStatement.js.map +1 -1
  91. package/dist/core/EditMutations.d.ts +14 -18
  92. package/dist/core/EditMutations.d.ts.map +1 -1
  93. package/dist/core/EditMutations.js +211 -280
  94. package/dist/core/EditMutations.js.map +1 -1
  95. package/dist/core/EditMutations.sql +15 -0
  96. package/dist/core/EditSequence.d.ts +13 -0
  97. package/dist/core/EditSequence.d.ts.map +1 -0
  98. package/dist/core/EditSequence.js +73 -0
  99. package/dist/core/EditSequence.js.map +1 -0
  100. package/dist/core/Engine.d.ts +17 -23
  101. package/dist/core/Engine.d.ts.map +1 -1
  102. package/dist/core/Engine.js +74 -164
  103. package/dist/core/Engine.js.map +1 -1
  104. package/dist/core/Engine.sql +0 -724
  105. package/dist/core/EntryAddressBinding.d.ts +7 -17
  106. package/dist/core/EntryAddressBinding.d.ts.map +1 -1
  107. package/dist/core/EntryAddressBinding.js +12 -72
  108. package/dist/core/EntryAddressBinding.js.map +1 -1
  109. package/dist/core/ExecutionOutputs.d.ts +9 -0
  110. package/dist/core/ExecutionOutputs.d.ts.map +1 -0
  111. package/dist/core/ExecutionOutputs.js +36 -0
  112. package/dist/core/ExecutionOutputs.js.map +1 -0
  113. package/dist/core/ExecutionOutputs.sql +21 -0
  114. package/dist/core/ExecutorRegistry.d.ts +11 -5
  115. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  116. package/dist/core/ExecutorRegistry.js +37 -32
  117. package/dist/core/ExecutorRegistry.js.map +1 -1
  118. package/dist/core/HostPaths.d.ts +1 -0
  119. package/dist/core/HostPaths.d.ts.map +1 -1
  120. package/dist/core/HostPaths.js +4 -0
  121. package/dist/core/HostPaths.js.map +1 -1
  122. package/dist/core/InferenceCall.d.ts.map +1 -1
  123. package/dist/core/InferenceCall.js +1 -2
  124. package/dist/core/InferenceCall.js.map +1 -1
  125. package/dist/core/InferenceCall.sql +29 -0
  126. package/dist/core/KillHandler.d.ts +1 -0
  127. package/dist/core/KillHandler.d.ts.map +1 -1
  128. package/dist/core/KillHandler.js +24 -21
  129. package/dist/core/KillHandler.js.map +1 -1
  130. package/dist/core/LogBody.d.ts +8 -1
  131. package/dist/core/LogBody.d.ts.map +1 -1
  132. package/dist/core/LogBody.js +44 -12
  133. package/dist/core/LogBody.js.map +1 -1
  134. package/dist/core/LogEntryProjection.d.ts +1 -0
  135. package/dist/core/LogEntryProjection.d.ts.map +1 -1
  136. package/dist/core/LogEntryProjection.js +12 -10
  137. package/dist/core/LogEntryProjection.js.map +1 -1
  138. package/dist/core/LogVisibility.d.ts +1 -0
  139. package/dist/core/LogVisibility.d.ts.map +1 -1
  140. package/dist/core/LogVisibility.js +3 -0
  141. package/dist/core/LogVisibility.js.map +1 -1
  142. package/dist/core/LogWriter.d.ts +2 -3
  143. package/dist/core/LogWriter.d.ts.map +1 -1
  144. package/dist/core/LogWriter.js +31 -36
  145. package/dist/core/LogWriter.js.map +1 -1
  146. package/dist/core/LogWriter.sql +23 -0
  147. package/dist/core/LoopDriver.d.ts +11 -8
  148. package/dist/core/LoopDriver.d.ts.map +1 -1
  149. package/dist/core/LoopDriver.js +161 -189
  150. package/dist/core/LoopDriver.js.map +1 -1
  151. package/dist/core/LoopDriver.sql +11 -0
  152. package/dist/core/LoopLifecycle.d.ts +32 -2
  153. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  154. package/dist/core/LoopLifecycle.js +88 -4
  155. package/dist/core/LoopLifecycle.js.map +1 -1
  156. package/dist/core/LoopLifecycle.sql +145 -28
  157. package/dist/core/LoopPolicyReader.sql +6 -0
  158. package/dist/core/MembershipMaterialization.d.ts.map +1 -1
  159. package/dist/core/MembershipMaterialization.js +6 -28
  160. package/dist/core/MembershipMaterialization.js.map +1 -1
  161. package/dist/core/ModelCall.d.ts.map +1 -1
  162. package/dist/core/ModelCall.js +2 -6
  163. package/dist/core/ModelCall.js.map +1 -1
  164. package/dist/core/ModelCall.sql +41 -0
  165. package/dist/core/MutationEffects.d.ts +5 -3
  166. package/dist/core/MutationEffects.d.ts.map +1 -1
  167. package/dist/core/MutationEffects.js +10 -28
  168. package/dist/core/MutationEffects.js.map +1 -1
  169. package/dist/core/NativeContent.d.ts +6 -0
  170. package/dist/core/NativeContent.d.ts.map +1 -0
  171. package/dist/core/NativeContent.js +16 -0
  172. package/dist/core/NativeContent.js.map +1 -0
  173. package/dist/core/NativeContent.sql +6 -0
  174. package/dist/core/NoticeChannel.js +1 -1
  175. package/dist/core/NoticeChannel.js.map +1 -1
  176. package/dist/core/OperatorConfig.js +5 -5
  177. package/dist/core/OperatorConfig.js.map +1 -1
  178. package/dist/core/PacketBuilder.d.ts +8 -5
  179. package/dist/core/PacketBuilder.d.ts.map +1 -1
  180. package/dist/core/PacketBuilder.js +117 -81
  181. package/dist/core/PacketBuilder.js.map +1 -1
  182. package/dist/core/PacketBuilder.sql +146 -0
  183. package/dist/core/PatternSelection.d.ts +28 -0
  184. package/dist/core/PatternSelection.d.ts.map +1 -0
  185. package/dist/core/PatternSelection.js +42 -0
  186. package/dist/core/PatternSelection.js.map +1 -0
  187. package/dist/core/ProblemLog.d.ts.map +1 -1
  188. package/dist/core/ProblemLog.js +0 -1
  189. package/dist/core/ProblemLog.js.map +1 -1
  190. package/dist/core/PromptFrames.d.ts +12 -0
  191. package/dist/core/PromptFrames.d.ts.map +1 -0
  192. package/dist/core/PromptFrames.js +28 -0
  193. package/dist/core/PromptFrames.js.map +1 -0
  194. package/dist/core/ProposalLifecycle.d.ts +0 -1
  195. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  196. package/dist/core/ProposalLifecycle.js +26 -23
  197. package/dist/core/ProposalLifecycle.js.map +1 -1
  198. package/dist/core/ProposalLifecycle.sql +33 -0
  199. package/dist/core/ProviderInstantiate.d.ts +1 -1
  200. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  201. package/dist/core/ProviderInstantiate.js +7 -14
  202. package/dist/core/ProviderInstantiate.js.map +1 -1
  203. package/dist/core/ReasoningView.d.ts +9 -0
  204. package/dist/core/ReasoningView.d.ts.map +1 -0
  205. package/dist/core/ReasoningView.js +47 -0
  206. package/dist/core/ReasoningView.js.map +1 -0
  207. package/dist/core/ResourceBindings.d.ts +16 -0
  208. package/dist/core/ResourceBindings.d.ts.map +1 -0
  209. package/dist/core/ResourceBindings.js +45 -0
  210. package/dist/core/ResourceBindings.js.map +1 -0
  211. package/dist/core/ResourceMutations.d.ts +5 -8
  212. package/dist/core/ResourceMutations.d.ts.map +1 -1
  213. package/dist/core/ResourceMutations.js +6 -7
  214. package/dist/core/ResourceMutations.js.map +1 -1
  215. package/dist/core/ResourceSelector.d.ts +2 -2
  216. package/dist/core/ResourceSelector.d.ts.map +1 -1
  217. package/dist/core/ResourceSelector.js +122 -62
  218. package/dist/core/ResourceSelector.js.map +1 -1
  219. package/dist/core/ResourceTransfers.d.ts +4 -2
  220. package/dist/core/ResourceTransfers.d.ts.map +1 -1
  221. package/dist/core/ResourceTransfers.js +95 -26
  222. package/dist/core/ResourceTransfers.js.map +1 -1
  223. package/dist/core/RuntimeWorker.d.ts +5 -0
  224. package/dist/core/RuntimeWorker.d.ts.map +1 -0
  225. package/dist/core/RuntimeWorker.js +15 -0
  226. package/dist/core/RuntimeWorker.js.map +1 -0
  227. package/dist/core/RuntimeWorker.sql +11 -0
  228. package/dist/core/SchemeRegistry.d.ts +12 -16
  229. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  230. package/dist/core/SchemeRegistry.js +59 -85
  231. package/dist/core/SchemeRegistry.js.map +1 -1
  232. package/dist/core/StoredPacket.d.ts +6 -1
  233. package/dist/core/StoredPacket.d.ts.map +1 -1
  234. package/dist/core/StoredPacket.js +30 -10
  235. package/dist/core/StoredPacket.js.map +1 -1
  236. package/dist/core/StrikeRail.d.ts +11 -8
  237. package/dist/core/StrikeRail.d.ts.map +1 -1
  238. package/dist/core/StrikeRail.js +63 -114
  239. package/dist/core/StrikeRail.js.map +1 -1
  240. package/dist/core/StrikeRail.sql +12 -0
  241. package/dist/core/TerminalResult.d.ts +1 -1
  242. package/dist/core/TerminalResult.d.ts.map +1 -1
  243. package/dist/core/TerminalResult.js +2 -2
  244. package/dist/core/TerminalResult.js.map +1 -1
  245. package/dist/core/TokenCalibration.d.ts +1 -6
  246. package/dist/core/TokenCalibration.d.ts.map +1 -1
  247. package/dist/core/TokenCalibration.js +13 -9
  248. package/dist/core/TokenCalibration.js.map +1 -1
  249. package/dist/core/TokenCalibration.sql +15 -0
  250. package/dist/core/ToolInputSchema.d.ts +6 -0
  251. package/dist/core/ToolInputSchema.d.ts.map +1 -0
  252. package/dist/core/ToolInputSchema.js +43 -0
  253. package/dist/core/ToolInputSchema.js.map +1 -0
  254. package/dist/core/ToolResources.d.ts.map +1 -1
  255. package/dist/core/ToolResources.js +77 -51
  256. package/dist/core/ToolResources.js.map +1 -1
  257. package/dist/core/Turn.d.ts +5 -2
  258. package/dist/core/Turn.d.ts.map +1 -1
  259. package/dist/core/Turn.js +13 -11
  260. package/dist/core/Turn.js.map +1 -1
  261. package/dist/core/Turn.sql +25 -39
  262. package/dist/core/{SendBroadcastHandler.d.ts → TurnDispositionHandler.d.ts} +24 -18
  263. package/dist/core/TurnDispositionHandler.d.ts.map +1 -0
  264. package/dist/core/TurnDispositionHandler.js +206 -0
  265. package/dist/core/TurnDispositionHandler.js.map +1 -0
  266. package/dist/core/TurnMaterialization.d.ts +2 -3
  267. package/dist/core/TurnMaterialization.d.ts.map +1 -1
  268. package/dist/core/TurnMaterialization.js +47 -24
  269. package/dist/core/TurnMaterialization.js.map +1 -1
  270. package/dist/core/TurnMaterialization.sql +44 -0
  271. package/dist/core/TurnOps.d.ts.map +1 -1
  272. package/dist/core/TurnOps.js +8 -64
  273. package/dist/core/TurnOps.js.map +1 -1
  274. package/dist/core/TurnRunner.d.ts +8 -15
  275. package/dist/core/TurnRunner.d.ts.map +1 -1
  276. package/dist/core/TurnRunner.js +1175 -1317
  277. package/dist/core/TurnRunner.js.map +1 -1
  278. package/dist/core/TurnRunner.sql +74 -0
  279. package/dist/core/TurnSources.sql +39 -0
  280. package/dist/core/WorkerControlAddress.d.ts +1 -0
  281. package/dist/core/WorkerControlAddress.d.ts.map +1 -1
  282. package/dist/core/WorkerControlAddress.js +5 -0
  283. package/dist/core/WorkerControlAddress.js.map +1 -1
  284. package/dist/core/WorkerControlHandler.d.ts +1 -3
  285. package/dist/core/WorkerControlHandler.d.ts.map +1 -1
  286. package/dist/core/WorkerControlHandler.js +64 -48
  287. package/dist/core/WorkerControlHandler.js.map +1 -1
  288. package/dist/core/WorkerName.d.ts +11 -12
  289. package/dist/core/WorkerName.d.ts.map +1 -1
  290. package/dist/core/WorkerName.js +40 -47
  291. package/dist/core/WorkerName.js.map +1 -1
  292. package/dist/core/WorkerName.sql +7 -10
  293. package/dist/core/WorkspaceGate.d.ts +1 -1
  294. package/dist/core/WorkspaceGate.d.ts.map +1 -1
  295. package/dist/core/WorkspaceGate.js +33 -10
  296. package/dist/core/WorkspaceGate.js.map +1 -1
  297. package/dist/core/ambient.sql +236 -0
  298. package/dist/core/attachments.d.ts +2 -1
  299. package/dist/core/attachments.d.ts.map +1 -1
  300. package/dist/core/attachments.js +5 -4
  301. package/dist/core/attachments.js.map +1 -1
  302. package/dist/core/caps/CapsResolve.d.ts +2 -3
  303. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  304. package/dist/core/caps/CapsResolve.js +5 -5
  305. package/dist/core/caps/CapsResolve.js.map +1 -1
  306. package/dist/core/caps/DbChannelCaps.d.ts +1 -1
  307. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  308. package/dist/core/caps/DbChannelCaps.js +4 -6
  309. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  310. package/dist/core/caps/DbEntryCaps.d.ts +2 -1
  311. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  312. package/dist/core/caps/DbEntryCaps.js +11 -17
  313. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  314. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  315. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  316. package/dist/core/caps/DbNotifyCaps.js +3 -5
  317. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  318. package/dist/core/caps/DbProjectionCaps.js +1 -1
  319. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  320. package/dist/core/caps/DbSubscriptionCaps.d.ts +1 -1
  321. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  322. package/dist/core/caps/DbSubscriptionCaps.js +5 -7
  323. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  324. package/dist/core/caps/SchemeCtxImpl.d.ts +0 -2
  325. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  326. package/dist/core/caps/SchemeCtxImpl.js +4 -20
  327. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  328. package/dist/core/env-catalog.d.ts +28 -0
  329. package/dist/core/env-catalog.d.ts.map +1 -0
  330. package/dist/core/env-catalog.js +99 -0
  331. package/dist/core/env-catalog.js.map +1 -0
  332. package/dist/core/env-defaults.d.ts.map +1 -1
  333. package/dist/core/env-defaults.js +4 -2
  334. package/dist/core/env-defaults.js.map +1 -1
  335. package/dist/core/file-materialization.d.ts +4 -1
  336. package/dist/core/file-materialization.d.ts.map +1 -1
  337. package/dist/core/file-materialization.js +15 -1
  338. package/dist/core/file-materialization.js.map +1 -1
  339. package/dist/core/fork.d.ts +1 -6
  340. package/dist/core/fork.d.ts.map +1 -1
  341. package/dist/core/fork.js +9 -153
  342. package/dist/core/fork.js.map +1 -1
  343. package/dist/core/fork.sql +162 -185
  344. package/dist/core/git-env.d.ts +1 -0
  345. package/dist/core/git-env.d.ts.map +1 -1
  346. package/dist/core/git-env.js +39 -2
  347. package/dist/core/git-env.js.map +1 -1
  348. package/dist/core/git-membership.d.ts.map +1 -1
  349. package/dist/core/git-membership.js +64 -48
  350. package/dist/core/git-membership.js.map +1 -1
  351. package/dist/core/git-state.d.ts.map +1 -1
  352. package/dist/core/git-state.js +26 -20
  353. package/dist/core/git-state.js.map +1 -1
  354. package/dist/core/glob_match.d.ts +4 -0
  355. package/dist/core/glob_match.d.ts.map +1 -0
  356. package/dist/core/glob_match.js +7 -0
  357. package/dist/core/glob_match.js.map +1 -0
  358. package/dist/core/maintenance.sql +6 -0
  359. package/dist/core/mutation-types.d.ts +7 -28
  360. package/dist/core/mutation-types.d.ts.map +1 -1
  361. package/dist/core/operation-target-groups.d.ts.map +1 -1
  362. package/dist/core/operation-target-groups.js +2 -1
  363. package/dist/core/operation-target-groups.js.map +1 -1
  364. package/dist/core/packet-inject.d.ts.map +1 -1
  365. package/dist/core/packet-inject.js +2 -3
  366. package/dist/core/packet-inject.js.map +1 -1
  367. package/dist/core/packet-wire.d.ts +8 -6
  368. package/dist/core/packet-wire.d.ts.map +1 -1
  369. package/dist/core/packet-wire.js +381 -388
  370. package/dist/core/packet-wire.js.map +1 -1
  371. package/dist/core/plurnk-uri.d.ts +1 -1
  372. package/dist/core/plurnk-uri.d.ts.map +1 -1
  373. package/dist/core/plurnk-uri.js +5 -12
  374. package/dist/core/plurnk-uri.js.map +1 -1
  375. package/dist/core/scheme-types.d.ts +3 -3
  376. package/dist/core/scheme-types.d.ts.map +1 -1
  377. package/dist/core/scheme-types.js.map +1 -1
  378. package/dist/core/sha256.d.ts +4 -0
  379. package/dist/core/sha256.d.ts.map +1 -0
  380. package/dist/core/sha256.js +6 -0
  381. package/dist/core/sha256.js.map +1 -0
  382. package/dist/core/teaching.js +1 -1
  383. package/dist/core/teaching.js.map +1 -1
  384. package/dist/core/turn-scheduler.d.ts.map +1 -1
  385. package/dist/core/turn-scheduler.js +5 -18
  386. package/dist/core/turn-scheduler.js.map +1 -1
  387. package/dist/core/turn-signals.d.ts +0 -6
  388. package/dist/core/turn-signals.d.ts.map +1 -1
  389. package/dist/core/turn-signals.js +1 -7
  390. package/dist/core/turn-signals.js.map +1 -1
  391. package/dist/core/worker-ops.sql +15 -22
  392. package/dist/core/workspace-settings.d.ts.map +1 -1
  393. package/dist/core/workspace-settings.js +18 -2
  394. package/dist/core/workspace-settings.js.map +1 -1
  395. package/dist/core/workspace-settings.sql +6 -0
  396. package/dist/digest/Digest.d.ts.map +1 -1
  397. package/dist/digest/Digest.js +30 -91
  398. package/dist/digest/Digest.js.map +1 -1
  399. package/dist/digest/DigestRender.d.ts +1 -0
  400. package/dist/digest/DigestRender.d.ts.map +1 -1
  401. package/dist/digest/DigestRender.js +58 -62
  402. package/dist/digest/DigestRender.js.map +1 -1
  403. package/dist/digest/digest-rows.d.ts +16 -32
  404. package/dist/digest/digest-rows.d.ts.map +1 -1
  405. package/dist/digest/digest.sql +54 -20
  406. package/dist/schemes/Exec.d.ts +6 -12
  407. package/dist/schemes/Exec.d.ts.map +1 -1
  408. package/dist/schemes/Exec.js +318 -215
  409. package/dist/schemes/Exec.js.map +1 -1
  410. package/dist/schemes/ExecOutputScheme.d.ts +8 -6
  411. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  412. package/dist/schemes/ExecOutputScheme.js +33 -61
  413. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  414. package/dist/schemes/ExecScheduler.js +2 -2
  415. package/dist/schemes/ExecScheduler.js.map +1 -1
  416. package/dist/schemes/ExecutionInput.d.ts +12 -0
  417. package/dist/schemes/ExecutionInput.d.ts.map +1 -0
  418. package/dist/schemes/ExecutionInput.js +94 -0
  419. package/dist/schemes/ExecutionInput.js.map +1 -0
  420. package/dist/schemes/File.d.ts +5 -2
  421. package/dist/schemes/File.d.ts.map +1 -1
  422. package/dist/schemes/File.js +62 -91
  423. package/dist/schemes/File.js.map +1 -1
  424. package/dist/schemes/Log.d.ts.map +1 -1
  425. package/dist/schemes/Log.js +84 -79
  426. package/dist/schemes/Log.js.map +1 -1
  427. package/dist/schemes/Log.sql +183 -15
  428. package/dist/schemes/Prompt.d.ts.map +1 -1
  429. package/dist/schemes/Prompt.js +3 -12
  430. package/dist/schemes/Prompt.js.map +1 -1
  431. package/dist/schemes/QuestionTool.d.ts +5 -0
  432. package/dist/schemes/QuestionTool.d.ts.map +1 -1
  433. package/dist/schemes/QuestionTool.js +7 -15
  434. package/dist/schemes/QuestionTool.js.map +1 -1
  435. package/dist/schemes/Skill.d.ts +12 -7
  436. package/dist/schemes/Skill.d.ts.map +1 -1
  437. package/dist/schemes/Skill.js +149 -21
  438. package/dist/schemes/Skill.js.map +1 -1
  439. package/dist/schemes/TurnSource.d.ts +12 -0
  440. package/dist/schemes/TurnSource.d.ts.map +1 -0
  441. package/dist/schemes/TurnSource.js +117 -0
  442. package/dist/schemes/TurnSource.js.map +1 -0
  443. package/dist/schemes/Worker.d.ts +3 -3
  444. package/dist/schemes/Worker.d.ts.map +1 -1
  445. package/dist/schemes/Worker.js +61 -208
  446. package/dist/schemes/Worker.js.map +1 -1
  447. package/dist/schemes/_entry-crud.d.ts +12 -15
  448. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  449. package/dist/schemes/_entry-crud.js +63 -63
  450. package/dist/schemes/_entry-crud.js.map +1 -1
  451. package/dist/schemes/_entry-crud.sql +159 -45
  452. package/dist/schemes/_entry-find.d.ts +1 -3
  453. package/dist/schemes/_entry-find.d.ts.map +1 -1
  454. package/dist/schemes/_entry-find.js +111 -112
  455. package/dist/schemes/_entry-find.js.map +1 -1
  456. package/dist/schemes/_entry-find.sql +22 -14
  457. package/dist/schemes/_entry-fts.d.ts +14 -0
  458. package/dist/schemes/_entry-fts.d.ts.map +1 -0
  459. package/dist/schemes/_entry-fts.js +82 -0
  460. package/dist/schemes/_entry-fts.js.map +1 -0
  461. package/dist/schemes/_entry-fts.sql +31 -0
  462. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  463. package/dist/schemes/_entry-graph.js +6 -14
  464. package/dist/schemes/_entry-graph.js.map +1 -1
  465. package/dist/schemes/_entry-graph.sql +29 -44
  466. package/dist/schemes/_entry-manifest.d.ts +2 -1
  467. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  468. package/dist/schemes/_entry-manifest.js +20 -26
  469. package/dist/schemes/_entry-manifest.js.map +1 -1
  470. package/dist/schemes/_entry-manifest.sql +26 -0
  471. package/dist/schemes/_entry-ops.d.ts +3 -5
  472. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  473. package/dist/schemes/_entry-ops.js +30 -43
  474. package/dist/schemes/_entry-ops.js.map +1 -1
  475. package/dist/schemes/_entry-ops.sql +5 -6
  476. package/dist/schemes/_entry-readable.d.ts +8 -0
  477. package/dist/schemes/_entry-readable.d.ts.map +1 -0
  478. package/dist/schemes/_entry-readable.js +38 -0
  479. package/dist/schemes/_entry-readable.js.map +1 -0
  480. package/dist/schemes/_entry-send.d.ts +1 -1
  481. package/dist/schemes/_entry-send.d.ts.map +1 -1
  482. package/dist/schemes/_entry-send.js +3 -3
  483. package/dist/schemes/_entry-send.js.map +1 -1
  484. package/dist/schemes/_search-index.d.ts +5 -3
  485. package/dist/schemes/_search-index.d.ts.map +1 -1
  486. package/dist/schemes/_search-index.js +169 -160
  487. package/dist/schemes/_search-index.js.map +1 -1
  488. package/dist/schemes/_search-index.sql +47 -0
  489. package/dist/schemes/exec-abort.js +1 -1
  490. package/dist/schemes/exec-abort.js.map +1 -1
  491. package/dist/schemes/exec-env.d.ts +3 -0
  492. package/dist/schemes/exec-env.d.ts.map +1 -1
  493. package/dist/schemes/exec-env.js +66 -15
  494. package/dist/schemes/exec-env.js.map +1 -1
  495. package/dist/schemes/exec-runtime.d.ts +1 -1
  496. package/dist/schemes/exec-runtime.d.ts.map +1 -1
  497. package/dist/schemes/exec-runtime.js +1 -1
  498. package/dist/schemes/exec-runtime.js.map +1 -1
  499. package/dist/server/ClientReads.d.ts +2 -2
  500. package/dist/server/ClientReads.d.ts.map +1 -1
  501. package/dist/server/ClientReads.js +7 -9
  502. package/dist/server/ClientReads.js.map +1 -1
  503. package/dist/server/Daemon.d.ts +15 -21
  504. package/dist/server/Daemon.d.ts.map +1 -1
  505. package/dist/server/Daemon.js +165 -168
  506. package/dist/server/Daemon.js.map +1 -1
  507. package/dist/server/DaemonModule.d.ts +29 -22
  508. package/dist/server/DaemonModule.d.ts.map +1 -1
  509. package/dist/server/DrainSupervisor.d.ts +8 -21
  510. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  511. package/dist/server/DrainSupervisor.js +278 -306
  512. package/dist/server/DrainSupervisor.js.map +1 -1
  513. package/dist/server/EnvFunctionality.d.ts +43 -0
  514. package/dist/server/EnvFunctionality.d.ts.map +1 -0
  515. package/dist/server/EnvFunctionality.js +194 -0
  516. package/dist/server/EnvFunctionality.js.map +1 -0
  517. package/dist/server/Functionality.d.ts +12 -11
  518. package/dist/server/Functionality.d.ts.map +1 -1
  519. package/dist/server/Functionality.js +279 -142
  520. package/dist/server/Functionality.js.map +1 -1
  521. package/dist/server/FunctionalityManager.d.ts +5 -5
  522. package/dist/server/FunctionalityManager.d.ts.map +1 -1
  523. package/dist/server/FunctionalityManager.js +29 -57
  524. package/dist/server/FunctionalityManager.js.map +1 -1
  525. package/dist/server/MembersFunctionality.d.ts +5 -4
  526. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  527. package/dist/server/MembersFunctionality.js +24 -38
  528. package/dist/server/MembersFunctionality.js.map +1 -1
  529. package/dist/server/PlurnkSkill.d.ts +11 -0
  530. package/dist/server/PlurnkSkill.d.ts.map +1 -0
  531. package/dist/server/PlurnkSkill.js +41 -0
  532. package/dist/server/PlurnkSkill.js.map +1 -0
  533. package/dist/server/Retention.d.ts +25 -0
  534. package/dist/server/Retention.d.ts.map +1 -0
  535. package/dist/server/Retention.js +57 -0
  536. package/dist/server/Retention.js.map +1 -0
  537. package/dist/server/Retention.sql +59 -0
  538. package/dist/server/ServiceModules.d.ts +5 -0
  539. package/dist/server/ServiceModules.d.ts.map +1 -0
  540. package/dist/server/ServiceModules.js +10 -0
  541. package/dist/server/ServiceModules.js.map +1 -0
  542. package/dist/server/SkillsFunctionality.d.ts +14 -19
  543. package/dist/server/SkillsFunctionality.d.ts.map +1 -1
  544. package/dist/server/SkillsFunctionality.js +102 -106
  545. package/dist/server/SkillsFunctionality.js.map +1 -1
  546. package/dist/server/WorkerModelResolver.d.ts +4 -0
  547. package/dist/server/WorkerModelResolver.d.ts.map +1 -1
  548. package/dist/server/WorkerModelResolver.js +72 -39
  549. package/dist/server/WorkerModelResolver.js.map +1 -1
  550. package/dist/server/WorkspaceCapabilities.d.ts +21 -0
  551. package/dist/server/WorkspaceCapabilities.d.ts.map +1 -0
  552. package/dist/server/{WorkerCapabilities.js → WorkspaceCapabilities.js} +40 -40
  553. package/dist/server/WorkspaceCapabilities.js.map +1 -0
  554. package/dist/server/WorkspaceResidency.d.ts +38 -0
  555. package/dist/server/WorkspaceResidency.d.ts.map +1 -0
  556. package/dist/server/WorkspaceResidency.js +226 -0
  557. package/dist/server/WorkspaceResidency.js.map +1 -0
  558. package/dist/server/client-input.d.ts +1 -4
  559. package/dist/server/client-input.d.ts.map +1 -1
  560. package/dist/server/client-input.js +5 -46
  561. package/dist/server/client-input.js.map +1 -1
  562. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  563. package/dist/server/dispatch-as-plurnk.js +4 -22
  564. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  565. package/dist/server/drain.sql +66 -43
  566. package/dist/server/envelope.d.ts +8 -1
  567. package/dist/server/envelope.d.ts.map +1 -1
  568. package/dist/server/envelope.js +9 -19
  569. package/dist/server/envelope.js.map +1 -1
  570. package/dist/server/envelope.sql +49 -13
  571. package/dist/server/lifecycle-recovery.sql +18 -20
  572. package/dist/server/logEntry.d.ts +1 -1
  573. package/dist/server/logEntry.d.ts.map +1 -1
  574. package/dist/server/logEntry.js +4 -1
  575. package/dist/server/logEntry.js.map +1 -1
  576. package/dist/server/logEntry.sql +25 -1
  577. package/dist/server/loopDocs.d.ts +2 -2
  578. package/dist/server/loopDocs.d.ts.map +1 -1
  579. package/dist/server/loopDocs.js +38 -31
  580. package/dist/server/loopDocs.js.map +1 -1
  581. package/dist/server/loopDocs.sql +1 -3
  582. package/dist/server/model-catalog.d.ts.map +1 -1
  583. package/dist/server/model-catalog.js +2 -1
  584. package/dist/server/model-catalog.js.map +1 -1
  585. package/dist/server/model-route.d.ts +1 -1
  586. package/dist/server/model-route.d.ts.map +1 -1
  587. package/dist/server/model-route.js +4 -2
  588. package/dist/server/model-route.js.map +1 -1
  589. package/dist/server/seam-entry-read.sql +3 -4
  590. package/dist/server/seam-loop.sql +2 -9
  591. package/dist/server/workspace-capabilities.sql +64 -0
  592. package/dist/service.d.ts.map +1 -1
  593. package/dist/service.js +21 -18
  594. package/dist/service.js.map +1 -1
  595. package/docs/env.md +55 -0
  596. package/docs/members.md +83 -0
  597. package/docs/skills.md +50 -0
  598. package/migrations/001_workspaces.sql +53 -0
  599. package/migrations/002_workers.sql +198 -0
  600. package/migrations/003_loops.sql +326 -0
  601. package/migrations/004_inference.sql +401 -0
  602. package/migrations/005_entries.sql +215 -0
  603. package/migrations/006_log.sql +581 -0
  604. package/migrations/007_subscriptions.sql +175 -0
  605. package/migrations/008_interactions.sql +29 -0
  606. package/package.json +44 -41
  607. package/digest-sql/channel-dispositions/dispositions.sql +0 -15
  608. package/digest-sql/channel-state/state.sql +0 -6
  609. package/digest-sql/curation/curation.sql +0 -6
  610. package/digest-sql/derivations/derivations.sql +0 -4
  611. package/digest-sql/embeddings/embeddings.sql +0 -4
  612. package/digest-sql/entry-dispositions/dispositions.sql +0 -15
  613. package/digest-sql/entry-state/state.sql +0 -6
  614. package/digest-sql/token-counts/token-counts.sql +0 -2
  615. package/dist/core/EmbeddingCall.d.ts +0 -23
  616. package/dist/core/EmbeddingCall.d.ts.map +0 -1
  617. package/dist/core/EmbeddingCall.js +0 -149
  618. package/dist/core/EmbeddingCall.js.map +0 -1
  619. package/dist/core/OverflowTurn.d.ts +0 -11
  620. package/dist/core/OverflowTurn.d.ts.map +0 -1
  621. package/dist/core/OverflowTurn.js +0 -80
  622. package/dist/core/OverflowTurn.js.map +0 -1
  623. package/dist/core/Owner.d.ts +0 -8
  624. package/dist/core/Owner.d.ts.map +0 -1
  625. package/dist/core/Owner.js +0 -46
  626. package/dist/core/Owner.js.map +0 -1
  627. package/dist/core/SendBroadcastHandler.d.ts.map +0 -1
  628. package/dist/core/SendBroadcastHandler.js +0 -159
  629. package/dist/core/SendBroadcastHandler.js.map +0 -1
  630. package/dist/core/VectorPump.d.ts +0 -28
  631. package/dist/core/VectorPump.d.ts.map +0 -1
  632. package/dist/core/VectorPump.js +0 -130
  633. package/dist/core/VectorPump.js.map +0 -1
  634. package/dist/core/owner.sql +0 -11
  635. package/dist/core/worker-settings.d.ts +0 -10
  636. package/dist/core/worker-settings.d.ts.map +0 -1
  637. package/dist/core/worker-settings.js +0 -53
  638. package/dist/core/worker-settings.js.map +0 -1
  639. package/dist/schemes/_entry-chunk.d.ts +0 -14
  640. package/dist/schemes/_entry-chunk.d.ts.map +0 -1
  641. package/dist/schemes/_entry-chunk.js +0 -165
  642. package/dist/schemes/_entry-chunk.js.map +0 -1
  643. package/dist/schemes/_entry-semantic.d.ts +0 -63
  644. package/dist/schemes/_entry-semantic.d.ts.map +0 -1
  645. package/dist/schemes/_entry-semantic.js +0 -299
  646. package/dist/schemes/_entry-semantic.js.map +0 -1
  647. package/dist/schemes/_entry-semantic.sql +0 -99
  648. package/dist/schemes/cosine.d.ts +0 -4
  649. package/dist/schemes/cosine.d.ts.map +0 -1
  650. package/dist/schemes/cosine.js +0 -9
  651. package/dist/schemes/cosine.js.map +0 -1
  652. package/dist/server/WorkerCapabilities.d.ts +0 -21
  653. package/dist/server/WorkerCapabilities.d.ts.map +0 -1
  654. package/dist/server/WorkerCapabilities.js.map +0 -1
  655. package/dist/server/WorkerResidency.d.ts +0 -40
  656. package/dist/server/WorkerResidency.d.ts.map +0 -1
  657. package/dist/server/WorkerResidency.js +0 -283
  658. package/dist/server/WorkerResidency.js.map +0 -1
  659. package/dist/server/seam-log-read.sql +0 -21
  660. package/dist/server/worker-capabilities.sql +0 -28
  661. package/migrations/001_schema.sql +0 -2207
@@ -1,8 +1,10 @@
1
+ var _a;
1
2
  import { setTimeout as delay } from "node:timers/promises";
2
3
  import { aggregateProviderAccounting } from "@plurnk/plurnk-providers";
3
4
  import { routeForSpec } from "./model-route.js";
4
5
  import ChannelWrite from "../core/ChannelWrite.js";
5
6
  import ErrorDetail from "../core/ErrorDetail.js";
7
+ import { taskTiming } from "../core/LoopLifecycle.js";
6
8
  import { DEFAULT_LOOP_POLICY } from "../core/scheme-types.js";
7
9
  import Results, { OperationFailureError } from "../core/results.js";
8
10
  import { observed } from "../observe/spans.js";
@@ -13,7 +15,7 @@ import { execPollBackoffMs } from "./exec-poll-backoff.js";
13
15
  // Owns the worker-local queue consumer and every process-local edge that may
14
16
  // wake, cancel, or retire it. Daemon retains provider/process policy and the
15
17
  // external facade, supplying only the named capabilities below.
16
- export default class DrainSupervisor {
18
+ class DrainSupervisor {
17
19
  #db;
18
20
  #lifecycle;
19
21
  #injectPrompt;
@@ -22,30 +24,26 @@ export default class DrainSupervisor {
22
24
  #runLoop;
23
25
  #loopUsage;
24
26
  #loopAttributions;
25
- #takeParkDeadline;
26
27
  #cancelSubscription;
27
28
  #hasActiveStreams;
28
- #isDetachedSubscription;
29
29
  #readSystemPrompt;
30
30
  #emitLogEntry;
31
31
  #emit;
32
32
  // The handle is the drain identity. Start/exit compare by reference so an
33
33
  // exiting drain cannot clobber a successor that raced in.
34
34
  #activeDrains = new Map();
35
- #drainExitTasks = new Set();
36
- #wakeTasks = new Set();
37
- #wakeFailures = [];
35
+ #settlementTasks = new Set();
36
+ #settlementFailures = [];
38
37
  // One cancellation scope spans a worker's loops and streams. It outlives
39
38
  // any single drain and is replaced only after it has been aborted.
40
39
  #workerAborts = new Map();
41
- #parkTimers = new Map();
42
- #pollTimers = new Map();
40
+ #loopTimers = new Map();
43
41
  #pollBackoff = new Map();
44
42
  #drainLocks = new Map();
45
- #owedWakes = new Set();
43
+ #admissionLocks = new Map();
46
44
  #completionWakeGates = new Map();
47
45
  #acceptingWork = false;
48
- constructor({ db, lifecycle, injectPrompt, assertInjectionCompatibility, reconcilePrompts, runLoop, loopUsage, loopAttributions, takeParkDeadline, cancelSubscription, hasActiveStreams, isDetachedSubscription, readSystemPrompt, emitLogEntry, emit, }) {
46
+ constructor({ db, lifecycle, injectPrompt, assertInjectionCompatibility, reconcilePrompts, runLoop, loopUsage, loopAttributions, cancelSubscription, hasActiveStreams, readSystemPrompt, emitLogEntry, emit, }) {
49
47
  this.#db = db;
50
48
  this.#lifecycle = lifecycle;
51
49
  this.#injectPrompt = injectPrompt;
@@ -54,10 +52,8 @@ export default class DrainSupervisor {
54
52
  this.#runLoop = runLoop;
55
53
  this.#loopUsage = loopUsage;
56
54
  this.#loopAttributions = loopAttributions;
57
- this.#takeParkDeadline = takeParkDeadline;
58
55
  this.#cancelSubscription = cancelSubscription;
59
56
  this.#hasActiveStreams = hasActiveStreams;
60
- this.#isDetachedSubscription = isDetachedSubscription;
61
57
  this.#readSystemPrompt = readSystemPrompt;
62
58
  this.#emitLogEntry = emitLogEntry;
63
59
  this.#emit = emit;
@@ -71,28 +67,24 @@ export default class DrainSupervisor {
71
67
  if (!scope.signal.aborted)
72
68
  scope.abort(reason);
73
69
  }
74
- for (const timer of this.#pollTimers.values())
75
- clearTimeout(timer);
76
- for (const timer of this.#parkTimers.values())
70
+ for (const { timer } of this.#loopTimers.values())
77
71
  clearTimeout(timer);
78
72
  this.#pollBackoff.clear();
79
- this.#pollTimers.clear();
80
- this.#parkTimers.clear();
73
+ this.#loopTimers.clear();
81
74
  }
82
75
  async idle() {
83
76
  for (;;) {
84
77
  const pending = [
85
78
  ...[...this.#activeDrains.values()].map(({ promise }) => promise),
86
- ...this.#drainExitTasks,
87
- ...this.#wakeTasks,
79
+ ...this.#settlementTasks,
88
80
  ];
89
81
  if (pending.length === 0)
90
82
  break;
91
83
  await Promise.allSettled(pending);
92
84
  }
93
- const failures = this.#wakeFailures.splice(0);
85
+ const failures = this.#settlementFailures.splice(0);
94
86
  if (failures.length > 0) {
95
- throw new AggregateError(failures, "wake-on-completion settlement failed");
87
+ throw new AggregateError(failures, "worker lifecycle settlement failed");
96
88
  }
97
89
  }
98
90
  async inject(args) {
@@ -100,10 +92,23 @@ export default class DrainSupervisor {
100
92
  throw new Error("drain injection cannot combine an explicit policy with a fresh-loop policy");
101
93
  }
102
94
  const { workspaceId, workerId, prompt } = args;
103
- const activeInjection = await this.#withDrainLock(workerId, async () => {
104
- if (!this.#activeDrains.has(workerId))
105
- return null;
106
- const active = await this.#db.drain_current_loop_for_worker.get({ worker_id: workerId });
95
+ const delivery = await this.#withAdmissionLock(workspaceId, () => this.#withDrainLock(workerId, async () => {
96
+ if (!this.#acceptingWork)
97
+ throw new Error("The daemon is not accepting work.");
98
+ if (args.sourceLoopId !== undefined) {
99
+ const source = await this.#db.drain_message_source.get({
100
+ loop_id: args.sourceLoopId,
101
+ });
102
+ if (source === undefined || source.workspace_id !== workspaceId) {
103
+ throw new Error(`message source loop ${args.sourceLoopId} does not belong to workspace ${workspaceId}`);
104
+ }
105
+ if (source.status !== 102) {
106
+ throw new OperationFailureError(Results.failure("daemon:admission", "source-not-running", 409, `The originating task is not running (status ${source.status}); no message was admitted.`, {}, { loopId: args.sourceLoopId, sourceStatus: source.status, retryable: false }));
107
+ }
108
+ }
109
+ const active = args.schedule === undefined
110
+ ? await this.#db.drain_current_loop_for_worker.get({ worker_id: workerId })
111
+ : undefined;
107
112
  if (active !== undefined) {
108
113
  await this.#assertInjectionCompatibility({
109
114
  workerId,
@@ -116,93 +121,61 @@ export default class DrainSupervisor {
116
121
  ...(args.policy === undefined ? {} : { policy: args.policy }),
117
122
  });
118
123
  }
119
- const result = await this.#injectPrompt(workerId, prompt, args.openPaths ?? [], args.source);
124
+ const result = active === undefined ? null
125
+ : await this.#injectPrompt(active.id, prompt, args.openPaths ?? [], args.source);
120
126
  if (result !== null) {
121
127
  // runLoop may already have parked in the database while this drain
122
128
  // is still registered. Wake that state now; if it is still running,
123
129
  // the serialized park-boundary check below supplies the wake edge.
124
- await this.#lifecycle.wake(result.loopId);
130
+ await this.#wakeLoop(workerId, result.loopId);
125
131
  return { action: "injected_next_turn", loopId: result.loopId, turnSeq: result.turnSeq };
126
132
  }
127
- return null;
128
- });
129
- if (activeInjection !== null)
130
- return activeInjection;
131
- // A parked worker resumes the same durable loop; a wake is not a new
132
- // loop and cannot silently replace its policy/provider/turn ceiling.
133
- if (!this.#activeDrains.has(workerId)) {
134
- const slept = await this.#db.drain_find_slept_loop.get({ worker_id: workerId });
135
- if (slept !== undefined) {
136
- await this.#assertInjectionCompatibility({
137
- workerId,
138
- loopId: slept.id,
139
- providerSpec: args.providerSpec,
140
- providerSpecExplicit: args.providerSpecExplicit,
141
- reasoningPolicy: args.reasoningPolicy,
142
- ...(args.childProviderSpec === undefined ? {} : { childProviderSpec: args.childProviderSpec }),
143
- ...(args.turnCeiling === undefined ? {} : { turnCeiling: args.turnCeiling }),
144
- ...(args.policy === undefined ? {} : { policy: args.policy }),
145
- });
146
- const injected = await this.#injectPrompt(workerId, prompt, args.openPaths ?? [], args.source);
147
- await this.#lifecycle.wake(slept.id);
148
- const started = await this.ensureDrain({ workspaceId, workerId, systemPrompt: args.systemPrompt });
149
- return {
150
- action: "injected_next_turn",
151
- loopId: slept.id,
152
- ...(injected?.turnSeq === undefined ? {} : { turnSeq: injected.turnSeq }),
153
- ...(started ?? {}),
154
- };
155
- }
156
- }
157
- const loopId = await this.enqueueFreshLoop({
158
- workerId,
159
- prompt,
160
- ...(args.source === undefined ? {} : { source: args.source }),
161
- providerSpec: args.providerSpec,
162
- reasoningPolicy: args.reasoningPolicy,
163
- childProviderSpec: args.childProviderSpec ?? null,
164
- maxTurns: args.turnCeiling?.effective,
165
- policy: args.policy ?? args.freshLoopPolicy,
166
- openPaths: args.openPaths,
167
- });
168
- const started = await this.ensureDrain({ workspaceId, workerId, systemPrompt: args.systemPrompt });
169
- return { action: "enqueued_new_loop", loopId, ...(started ?? {}) };
170
- }
171
- enqueueFreshLoop(args) {
172
- return this.#withDrainLock(args.workerId, async () => {
173
- const seqRow = await this.#db.loop_run_next_sequence.get({ worker_id: args.workerId });
174
- if (seqRow === undefined)
175
- throw new Error("enqueueFreshLoop: next-sequence query returned no row");
176
- // {§worker-model-selection} — resolve the complete route before persistence;
177
- // the loop snapshot stores the immutable route ids, never re-serialized JSON.
178
- const modelRouteId = await routeForSpec(this.#db, args.providerSpec);
179
- const spawnRouteId = await routeForSpec(this.#db, args.childProviderSpec);
180
- const loopRow = await this.#db.drain_enqueue_loop.get({
181
- worker_id: args.workerId,
182
- sequence: seqRow.next,
183
- prompt: args.prompt,
184
- prompt_source: args.source ?? null,
185
- model_route_id: modelRouteId,
186
- spawn_model_route_id: spawnRouteId,
187
- reasoning_policy: args.reasoningPolicy,
188
- max_turns: args.maxTurns ?? Number(process.env.PLURNK_SERVICE_MAX_TURNS ?? "50"),
133
+ const accepted = await this.#enqueueFreshLoop({
134
+ workerId,
135
+ prompt,
136
+ ...(args.source === undefined ? {} : { source: args.source }),
137
+ providerSpec: args.providerSpec,
138
+ reasoningPolicy: args.reasoningPolicy,
139
+ childProviderSpec: args.childProviderSpec ?? null,
140
+ maxTurns: args.turnCeiling?.effective,
141
+ policy: args.policy ?? args.freshLoopPolicy,
142
+ openPaths: args.openPaths,
143
+ schedule: args.schedule,
189
144
  });
190
- if (loopRow === undefined)
191
- throw new Error("enqueueFreshLoop: loop enqueue returned no row");
192
- if (args.policy !== undefined) {
193
- await this.#db.engine_set_loop_policy.run({
194
- loop_id: loopRow.id,
195
- policy: JSON.stringify({ ...DEFAULT_LOOP_POLICY, ...args.policy }),
196
- });
197
- }
198
- if (args.openPaths !== undefined && args.openPaths.length > 0) {
199
- await this.#db.engine_set_loop_open_paths.run({
200
- loop_id: loopRow.id,
201
- open_paths: JSON.stringify(args.openPaths),
202
- });
145
+ return { action: "enqueued_new_loop", ...accepted };
146
+ }));
147
+ const started = await this.ensureDrain({ workspaceId, workerId, systemPrompt: args.systemPrompt });
148
+ return { ...delivery, ...(started ?? {}) };
149
+ }
150
+ async #enqueueFreshLoop(args) {
151
+ const now = Date.now();
152
+ if (args.schedule !== undefined) {
153
+ const { delayMs, intervalMs } = args.schedule;
154
+ if (!Number.isSafeInteger(delayMs) || delayMs < 0 || now + delayMs > 8.64e15
155
+ || (intervalMs !== undefined && (!Number.isSafeInteger(intervalMs) || intervalMs <= 0 || now + delayMs + intervalMs > 8.64e15))) {
156
+ throw new TypeError("task schedule requires nonnegative delay and positive interval in safe integer milliseconds within the supported date range");
203
157
  }
204
- return loopRow.id;
158
+ }
159
+ // {§worker-model-selection} — resolve the complete route before persistence;
160
+ // the loop snapshot stores the immutable route ids, never re-serialized JSON.
161
+ const modelRouteId = await routeForSpec(this.#db, args.providerSpec);
162
+ const spawnRouteId = await routeForSpec(this.#db, args.childProviderSpec);
163
+ const loopRow = await this.#db.drain_enqueue_loop.get({
164
+ worker_id: args.workerId,
165
+ prompt: args.prompt,
166
+ prompt_source: args.source ?? null,
167
+ model_route_id: modelRouteId,
168
+ spawn_model_route_id: spawnRouteId,
169
+ reasoning_policy: args.reasoningPolicy,
170
+ max_turns: args.maxTurns ?? Number(process.env.PLURNK_SERVICE_MAX_TURNS ?? "50"),
171
+ policy: JSON.stringify({ ...DEFAULT_LOOP_POLICY, ...args.policy }),
172
+ open_paths: JSON.stringify(args.openPaths ?? []),
173
+ scheduled_at: args.schedule === undefined ? null : now + args.schedule.delayMs,
174
+ repeat_interval_ms: args.schedule?.intervalMs ?? null,
205
175
  });
176
+ if (loopRow === undefined)
177
+ throw new Error("enqueueFreshLoop: loop enqueue returned no row");
178
+ return { loopId: loopRow.id, ...taskTiming(loopRow) };
206
179
  }
207
180
  // Consume one worker's durable queue until a lock-held empty re-claim
208
181
  // relinquishes this exact drain identity.
@@ -221,7 +194,7 @@ export default class DrainSupervisor {
221
194
  rejectFirst = rej;
222
195
  });
223
196
  let firstSettled = false;
224
- const claim = () => this.#db.drain_claim_next_loop.get({ worker_id: workerId });
197
+ const claim = () => this.#db.drain_claim_next_loop.get({ worker_id: workerId, now: Date.now() });
225
198
  const drainPromise = (async () => {
226
199
  let loopsDrained = 0;
227
200
  let lastResult = null;
@@ -248,6 +221,7 @@ export default class DrainSupervisor {
248
221
  break;
249
222
  }
250
223
  currentLoopId = loopRow.id;
224
+ this.#clearLoopTimer(loopRow.id);
251
225
  const onSettled = async (logEntryId) => {
252
226
  await this.#emitLogEntry(workspaceId, logEntryId).catch((error) => {
253
227
  console.error("log/entry broadcast failed:", error instanceof Error ? error.message : String(error));
@@ -269,32 +243,11 @@ export default class DrainSupervisor {
269
243
  return loopResult;
270
244
  });
271
245
  if (result.result.status === 202) {
272
- // The loop slept via SEND signal 202 — suspended, not terminated. Leave it at 202
246
+ // The loop parked — suspended, not terminated. Leave it at 202
273
247
  // (resumable); no loop/terminated, no orphan-reconcile. A stream conclusion
274
248
  // through handleWakeWorker re-queues it; if it holds a polled stream, a poll timer
275
249
  // wakes it every P to inspect ({§exec-poll}). {§worker-lifecycle-wake-liveness}.
276
- void this.schedulePollWake(workspaceId, workerId, systemPrompt).catch((err) => console.error("poll-wake scheduling failed:", err instanceof Error ? err.message : String(err)));
277
- // {§send-premature-terminate}/scoped SEND signal 202 — the park deadline:
278
- // dispatcher recorded the marker's seconds; a bounded park is woken at T
279
- // regardless of arrivals, so a park always has a next turn. -1 (indefinite:
280
- // the butler, a [300] ask) schedules nothing — irc/inject/conclusions wake it.
281
- // In-memory: a daemon restart drops pending deadlines.
282
- if (currentLoopId !== null) {
283
- const deadline = this.#takeParkDeadline(currentLoopId);
284
- const prior = this.#parkTimers.get(workerId);
285
- if (prior !== undefined) {
286
- clearTimeout(prior);
287
- this.#parkTimers.delete(workerId);
288
- }
289
- if (deadline !== undefined && deadline > 0) {
290
- const t = setTimeout(() => {
291
- this.#parkTimers.delete(workerId);
292
- void this.#wakeParkedWorker(workspaceId, workerId, systemPrompt).catch((err) => console.error("park-deadline wake failed:", err instanceof Error ? err.message : String(err)));
293
- }, deadline * 1000);
294
- t.unref();
295
- this.#parkTimers.set(workerId, t);
296
- }
297
- }
250
+ await this.scheduleWakes(workspaceId, workerId, systemPrompt);
298
251
  // Serialize the park boundary against active prompt injection.
299
252
  // Whichever side arrives first owns a wake edge: injection wakes
300
253
  // an already-parked loop, while this check wakes a prompt written
@@ -302,42 +255,37 @@ export default class DrainSupervisor {
302
255
  const promptWaiting = await this.#withDrainLock(workerId, async () => {
303
256
  const prefix = promptLoopPrefix(loopRow.sequence);
304
257
  const undelivered = await this.#db.drain_undelivered_prompts_for_loop.get({
305
- owner_id: workerId,
258
+ worker_id: workerId,
306
259
  pattern: `${prefix}%`,
307
260
  prefix_len: prefix.length,
308
261
  loop_id: loopRow.id,
309
262
  });
310
263
  if (undelivered === undefined)
311
264
  return false;
312
- await this.#lifecycle.wake(loopRow.id);
313
- return true;
265
+ return this.#wakeLoop(workerId, loopRow.id);
314
266
  });
315
267
  if (promptWaiting) {
316
268
  currentLoopId = null;
317
269
  continue;
318
270
  }
319
- // Honor an OWED wake ({§worker-lifecycle-child-wake}): a child/stream concluded while
320
- // this worker was mid-turn, before it slept — resume in place rather than park blind,
321
- // so a worker hibernation always returns. The loop is 202 here; reset to
322
- // claimable and the drain re-runs it on the next claim below.
323
- if (this.#owedWakes.delete(workerId)) {
324
- await this.#lifecycle.wake(loopRow.id);
271
+ // {§loop-wake-identity}: events observed by another loop
272
+ // cannot consume this loop's completion wake.
273
+ if (await this.#wakeLoop(workerId, loopRow.id, { eventOnly: true })) {
325
274
  currentLoopId = null;
326
275
  continue;
327
276
  }
328
- // The loop is blocked at 202 on a live obligation ({§wait-obligation-matrix});
329
- // that obligation's conclusion is its wake edge (the owed-wake above covers the
330
- // conclude-before-block race). An idle wait never reaches here — it concluded at dispatch.
277
+ // {§worker-wait-timing}: the wait now belongs to its
278
+ // durable clock or completion obligations, not this drain.
331
279
  currentLoopId = null;
332
280
  continue;
333
281
  }
334
- this.#owedWakes.delete(workerId); // the loop concluded (non-202) — no park to honor a held wake at
282
+ this.#pollBackoff.delete(loopRow.id);
335
283
  const [usage, attributions, turnIds] = await Promise.all([
336
284
  this.#loopUsage(loopRow.id),
337
285
  this.#loopAttributions(loopRow.id),
338
286
  this.#lifecycle.turnIds(loopRow.id),
339
287
  ]);
340
- this.#emit(workspaceId, "loop/terminated", {
288
+ this.#publishTermination(workspaceId, {
341
289
  workerId,
342
290
  loopId: loopRow.id,
343
291
  result: result.result,
@@ -396,7 +344,7 @@ export default class DrainSupervisor {
396
344
  this.#loopAttributions(currentLoopId),
397
345
  ]);
398
346
  if (cancelled !== null) {
399
- this.#emit(workspaceId, "loop/terminated", {
347
+ this.#publishTermination(workspaceId, {
400
348
  workerId,
401
349
  loopId: currentLoopId,
402
350
  result: cancelled,
@@ -450,7 +398,7 @@ export default class DrainSupervisor {
450
398
  this.#loopUsage(currentLoopId),
451
399
  this.#loopAttributions(currentLoopId),
452
400
  ]);
453
- this.#emit(workspaceId, "loop/terminated", {
401
+ this.#publishTermination(workspaceId, {
454
402
  workerId,
455
403
  loopId: currentLoopId,
456
404
  result: settled,
@@ -474,21 +422,12 @@ export default class DrainSupervisor {
474
422
  }
475
423
  if (this.#activeDrains.get(workerId) === handle)
476
424
  this.#activeDrains.delete(workerId);
425
+ await this.scheduleWakes(workspaceId, workerId, systemPrompt);
477
426
  }
478
427
  return { loopsDrained, lastResult };
479
428
  })();
480
429
  handle.promise = drainPromise;
481
430
  this.#activeDrains.set(workerId, handle);
482
- // Topology join ({§worker-loop-lifecycle}): when this drain exits having CONCLUDED the worker, wake its parent
483
- // if parked. Runs after the drain fully tears down (settled promise) so the quiescence check sees
484
- // final state; speculative (#onDrainExit no-ops unless the worker concluded AND the parent is parked).
485
- const drainExitTask = drainPromise.then(() => this.#onDrainExit(workspaceId, workerId, systemPrompt), () => this.#onDrainExit(workspaceId, workerId, systemPrompt));
486
- this.#drainExitTasks.add(drainExitTask);
487
- void drainExitTask.catch((err) => {
488
- console.error(`parent wake after worker ${workerId} settlement failed:`, err);
489
- }).finally(() => {
490
- this.#drainExitTasks.delete(drainExitTask);
491
- });
492
431
  // Swallow unhandled rejections (drain aborts with no awaiter); the
493
432
  // error already surfaced via firstLoopPromise or was logged inside.
494
433
  drainPromise.catch(() => { });
@@ -502,12 +441,20 @@ export default class DrainSupervisor {
502
441
  // A promise-chain mutex: each caller awaits the prior holder; the tail self-prunes
503
442
  // when idle so the Map stays bounded to workers mid-transition.
504
443
  #withDrainLock(workerId, fn) {
505
- const prev = this.#drainLocks.get(workerId) ?? Promise.resolve();
444
+ return _a.#withLock(this.#drainLocks, workerId, fn);
445
+ }
446
+ // {§worker-causal-admission}: workspace control precedes worker queue control;
447
+ // neither encloses provider/tool execution or waits for a drain to finish.
448
+ #withAdmissionLock(workspaceId, fn) {
449
+ return _a.#withLock(this.#admissionLocks, workspaceId, fn);
450
+ }
451
+ static #withLock(locks, id, fn) {
452
+ const prev = locks.get(id) ?? Promise.resolve();
506
453
  const run = prev.then(fn, fn);
507
454
  const tail = run.catch(() => { });
508
- this.#drainLocks.set(workerId, tail);
509
- void tail.then(() => { if (this.#drainLocks.get(workerId) === tail)
510
- this.#drainLocks.delete(workerId); });
455
+ locks.set(id, tail);
456
+ void tail.then(() => { if (locks.get(id) === tail)
457
+ locks.delete(id); });
511
458
  return run;
512
459
  }
513
460
  // The drain guarantee, serialized per worker via #withDrainLock so it can't race a
@@ -516,18 +463,33 @@ export default class DrainSupervisor {
516
463
  // or its lock-held exit re-claim → return null. A registered-but-ABORTING drain is
517
464
  // in teardown and won't claim, so we don't defer to it — start fresh, or the loop
518
465
  // strands on a cancel/resume race (I6 no-lost-loop). Otherwise start one.
519
- ensureDrain(opts) {
520
- return this.#withDrainLock(opts.workerId, async () => {
466
+ async ensureDrain(opts) {
467
+ let deferred = false;
468
+ const started = await this.#withDrainLock(opts.workerId, async () => {
469
+ if (!this.#acceptingWork)
470
+ return null;
521
471
  const existing = this.#activeDrains.get(opts.workerId);
522
472
  if (existing !== undefined && !existing.controller.signal.aborted)
523
473
  return null;
474
+ if (await this.#db.drain_ready_loop.get({ worker_id: opts.workerId, now: Date.now() }) === undefined) {
475
+ deferred = true;
476
+ return null;
477
+ }
478
+ if (!this.#acceptingWork)
479
+ return null;
524
480
  return this.#startDrain(opts);
525
481
  });
482
+ if (deferred)
483
+ await this.scheduleWakes(opts.workspaceId, opts.workerId, opts.systemPrompt);
484
+ return started;
526
485
  }
527
486
  // Prompt promotion shares the worker lock with enqueue and drain teardown,
528
487
  // while Daemon retains the durable prompt-policy implementation.
529
- reconcileOrphanedPrompts(workerId, endedLoopId) {
530
- return this.#withDrainLock(workerId, () => this.#reconcilePrompts(workerId, endedLoopId));
488
+ async reconcileOrphanedPrompts(workerId, endedLoopId) {
489
+ const row = await this.#db.drain_get_worker_workspace.get({ worker_id: workerId });
490
+ if (row === undefined)
491
+ throw new Error(`prompt promotion worker ${workerId} does not exist`);
492
+ return this.#withAdmissionLock(row.workspace_id, () => this.#withDrainLock(workerId, () => this.#reconcilePrompts(workerId, endedLoopId)));
531
493
  }
532
494
  #workerSignal(workerId) {
533
495
  const existing = this.#workerAborts.get(workerId);
@@ -538,25 +500,26 @@ export default class DrainSupervisor {
538
500
  return fresh;
539
501
  }
540
502
  async #cancelTree(workerId, reason, includeRoot) {
541
- const cancelled = await this.#lifecycle.cancelTree(workerId, reason, includeRoot);
542
- for (const targetWorkerId of cancelled.workerIds) {
543
- const pollTimer = this.#pollTimers.get(targetWorkerId);
544
- if (pollTimer !== undefined) {
545
- clearTimeout(pollTimer);
546
- this.#pollTimers.delete(targetWorkerId);
547
- }
548
- const parkTimer = this.#parkTimers.get(targetWorkerId);
549
- if (parkTimer !== undefined) {
550
- clearTimeout(parkTimer);
551
- this.#parkTimers.delete(targetWorkerId);
503
+ const owner = await this.#db.drain_get_worker_workspace.get({ worker_id: workerId });
504
+ if (owner === undefined)
505
+ throw new Error(`cancellation worker ${workerId} does not exist`);
506
+ const { cancelled, subscriptions } = await this.#withAdmissionLock(owner.workspace_id, async () => {
507
+ const cancelled = await this.#lifecycle.cancelTree(workerId, reason, includeRoot);
508
+ const subscriptions = (await Promise.all(cancelled.workerIds.map((id) => ChannelWrite.findOpenSubscriptionsForWorker(this.#db, id)))).flat();
509
+ for (const targetWorkerId of cancelled.workerIds) {
510
+ for (const [loopId, timer] of this.#loopTimers) {
511
+ if (timer.workerId === targetWorkerId)
512
+ this.#clearLoopTimer(loopId);
513
+ }
514
+ const scope = this.#workerAborts.get(targetWorkerId);
515
+ if (scope !== undefined && !scope.signal.aborted)
516
+ scope.abort(reason);
552
517
  }
553
- this.#pollBackoff.delete(targetWorkerId);
554
- this.#owedWakes.delete(targetWorkerId);
555
- const scope = this.#workerAborts.get(targetWorkerId);
556
- if (scope !== undefined && !scope.signal.aborted)
557
- scope.abort(reason);
558
- }
559
- await Promise.all(cancelled.workerIds.map(async (targetWorkerId) => this.#reapWorkerStreams(targetWorkerId)));
518
+ return { cancelled, subscriptions };
519
+ });
520
+ for (const { loopId } of cancelled.loops)
521
+ this.#pollBackoff.delete(loopId);
522
+ await Promise.all(subscriptions.map(({ id }) => this.#cancelSubscription(id)));
560
523
  for (const { loopId, workerId: targetWorkerId, result } of cancelled.loops) {
561
524
  const row = await this.#db.drain_get_worker_workspace.get({
562
525
  worker_id: targetWorkerId,
@@ -567,7 +530,7 @@ export default class DrainSupervisor {
567
530
  this.#loopUsage(loopId),
568
531
  this.#loopAttributions(loopId),
569
532
  ]);
570
- this.#emit(row.workspace_id, "loop/terminated", {
533
+ this.#publishTermination(row.workspace_id, {
571
534
  workerId: targetWorkerId,
572
535
  loopId,
573
536
  result,
@@ -579,90 +542,55 @@ export default class DrainSupervisor {
579
542
  }
580
543
  }
581
544
  cancelWorkerTree(workerId, reason) {
582
- return this.#cancelTree(workerId, reason, true);
545
+ return this.#trackSettlement(this.#cancelTree(workerId, reason, true), `cancelTree(${workerId})`);
583
546
  }
584
547
  cancelDescendants(workerId, reason) {
585
- return this.#cancelTree(workerId, reason, false);
586
- }
587
- // {§worker-model-selection} — the 409 boundary: a worker with a live drain
588
- // holding a loop, or a parked (slept) loop, owns active work; a model
589
- // selection must not mutate underneath it.
590
- async hasLiveWork(workerId) {
591
- if (this.#activeDrains.has(workerId)) {
592
- const active = await this.#db.drain_current_loop_for_worker.get({ worker_id: workerId });
593
- if (active !== undefined)
594
- return true;
595
- }
596
- const slept = await this.#db.drain_find_slept_loop.get({ worker_id: workerId });
597
- return slept !== undefined;
548
+ return this.#trackSettlement(this.#cancelTree(workerId, reason, false), `cancelDescendants(${workerId})`);
598
549
  }
599
550
  cancel(workerId, reason = "user_cancelled") {
600
551
  const hadDrain = this.#activeDrains.has(workerId);
601
552
  const hadWork = hadDrain || this.#hasActiveStreams(workerId);
602
- const pollTimer = this.#pollTimers.get(workerId);
603
- if (pollTimer !== undefined) {
604
- clearTimeout(pollTimer);
605
- this.#pollTimers.delete(workerId);
606
- }
607
- void this.cancelWorkerTree(workerId, reason).catch((error) => {
608
- console.error(`cancelTree(${workerId}) failed:`, error);
609
- });
553
+ void this.cancelWorkerTree(workerId, reason);
610
554
  return hadWork;
611
555
  }
612
- async #reapWorkerStreams(workerId) {
613
- const open = await ChannelWrite.findOpenSubscriptionsForWorker(this.#db, workerId);
614
- await Promise.all(open.map(({ id }) => this.#cancelSubscription(id)));
615
- }
616
556
  // {§module-shutdown-order}: the producer emits synchronously, while the
617
557
  // supervisor owns the asynchronous scheduler work and its shutdown truth.
618
558
  notifyWakeWorker(payload) {
619
- const task = this.#handleWakeWorker(payload);
620
- this.#wakeTasks.add(task);
621
- void task.then(() => { this.#wakeTasks.delete(task); }, (error) => {
622
- this.#wakeTasks.delete(task);
623
- this.#wakeFailures.push(error);
624
- console.error("wake-on-completion failed:", error);
559
+ this.#trackSettlement(this.#handleWakeWorker(payload), "wake-on-completion");
560
+ }
561
+ #trackSettlement(task, label) {
562
+ this.#settlementTasks.add(task);
563
+ void task.then(() => { this.#settlementTasks.delete(task); }, (error) => {
564
+ this.#settlementTasks.delete(task);
565
+ this.#settlementFailures.push(error);
566
+ console.error(`${label} failed:`, error);
625
567
  });
568
+ return task;
626
569
  }
627
570
  async #handleWakeWorker(payload) {
628
- const { entryOwnerId, workspaceId, ...wake } = payload;
629
- const conclusion = { ...wake, workerId: entryOwnerId };
630
- // Aborted streams don't wake — the abort was deliberate.
631
- if (payload.result.status === 499) {
632
- this.#emit(workspaceId, "stream/concluded", {
633
- ...conclusion, wakeAction: "skipped-aborted",
634
- });
635
- return;
636
- }
637
- // No resurrection ({§worker-lifecycle-no-resurrection}): a non-499 completion whose
638
- // worker was cancelled (idle + its scope aborted) must not start a fresh drain —
639
- // the cancel was deliberate. The deliverable is already in the channel/log and
640
- // surfaces as a `collect` environment delta ({§env-delta}) if the worker is read or
641
- // resumed; we just don't inject a turn. (An active worker folds the wake into its
642
- // next turn via inject below; a resumed worker is active, never aborted, so it is
643
- // unaffected.)
571
+ const { workspaceId, ...conclusion } = payload;
572
+ // {§worker-lifecycle-no-resurrection}: scope cancellation, not the
573
+ // command's result code, prevents a wake. Cancelling one command still
574
+ // produces an observation owed to its live worker.
644
575
  const scope = this.#workerAborts.get(payload.workerId);
645
- if (scope?.signal.aborted === true && !this.#activeDrains.has(payload.workerId)) {
576
+ if (scope?.signal.aborted === true) {
646
577
  this.#emit(workspaceId, "stream/concluded", {
647
- ...conclusion, wakeAction: "skipped-cancelled",
578
+ ...conclusion, wakeAction: payload.result.status === 499 ? "skipped-aborted" : "skipped-cancelled",
648
579
  });
649
580
  return;
650
581
  }
651
582
  const systemPrompt = await this.#readSystemPrompt();
652
- // A slept (202) loop means the worker parked via SEND signal 202 → resume it in place: re-queue
653
- // it (202→100) so the drain re-claims and CONTINUES it (seq>1 → no re-foist). Checked
654
- // FIRST: the slept status is the worker's true disposition regardless of a draining
655
- // sibling mid-teardown (the ensureDrain lock serializes the re-claim). No fresh loop,
656
- // no summary-as-prompt — the resumed loop reads the concluded stream's own state from
657
- // the manifest. {§worker-lifecycle-wake-liveness}.
583
+ // {§notifications-stream-concluded}: publish the terminal stream fact
584
+ // before settlement. Rechecked wait identities, not this event, decide
585
+ // whether any loop actually resumes.
658
586
  const slept = await this.#db.drain_find_slept_loop.get({ worker_id: payload.workerId });
659
587
  if (slept !== undefined) {
660
588
  // {§worker-optimistic-settlement} — publish this conclusion now,
661
589
  // then let the worker-local gate coalesce only the provider
662
590
  // dispatch. Concurrent stream/child callbacks join that one gate.
663
- const settlement = this.settleCompletionWake(workspaceId, payload.workerId, systemPrompt, false);
591
+ const settlement = this.settleCompletionWake(workspaceId, payload.workerId, systemPrompt);
664
592
  this.#emit(workspaceId, "stream/concluded", {
665
- ...conclusion, wakeAction: "resumed-loop", wakeLoopId: slept.id,
593
+ ...conclusion, wakeAction: "wake-pending",
666
594
  });
667
595
  await settlement;
668
596
  return;
@@ -677,77 +605,130 @@ export default class DrainSupervisor {
677
605
  });
678
606
  return;
679
607
  }
680
- // No slept loop, no active drain — nothing to resume (e.g. a SEND-200-done worker whose
608
+ // No slept loop, no active drain — nothing to resume (e.g. a completed worker whose
681
609
  // streams were swept). Surface the conclusion without opening a loop.
682
610
  this.#emit(workspaceId, "stream/concluded", {
683
611
  ...conclusion, wakeAction: "no-loop",
684
612
  });
685
613
  }
686
- async schedulePollWake(workspaceId, workerId, systemPrompt) {
687
- const existing = this.#pollTimers.get(workerId);
688
- if (existing !== undefined) {
689
- clearTimeout(existing);
690
- this.#pollTimers.delete(workerId);
614
+ #clearLoopTimer(loopId) {
615
+ const current = this.#loopTimers.get(loopId);
616
+ if (current === undefined)
617
+ return;
618
+ clearTimeout(current.timer);
619
+ this.#loopTimers.delete(loopId);
620
+ }
621
+ async scheduleWakes(workspaceId, workerId, systemPrompt) {
622
+ await this.#withDrainLock(workerId, async () => {
623
+ if (!this.#acceptingWork)
624
+ return;
625
+ const waits = await this.#lifecycle.parked(workerId);
626
+ const queued = await this.#db.drain_scheduled_loops.all({ worker_id: workerId });
627
+ if (!this.#acceptingWork)
628
+ return;
629
+ for (const [loopId, timer] of this.#loopTimers) {
630
+ if (timer.workerId === workerId && !waits.some(({ id }) => id === loopId)
631
+ && !queued.some(({ id }) => id === loopId))
632
+ this.#clearLoopTimer(loopId);
633
+ }
634
+ for (const wait of waits) {
635
+ if (wait.wait_poll_interval === null && wait.wait_poll_at === null) {
636
+ const interval = await this.#inheritedPollMs(workerId, wait.id);
637
+ if (!this.#acceptingWork)
638
+ return;
639
+ if (interval !== null) {
640
+ wait.wait_poll_at = Date.now() + interval;
641
+ await this.#lifecycle.inheritPoll(wait.id, wait.wait_revision, wait.wait_poll_at);
642
+ }
643
+ }
644
+ const dueAt = Math.min(wait.wait_deadline_at ?? Infinity, wait.wait_poll_at ?? Infinity);
645
+ if (!Number.isFinite(dueAt))
646
+ continue;
647
+ this.#armTimer(workspaceId, workerId, systemPrompt, wait.id, 202, wait.wait_revision, dueAt);
648
+ }
649
+ for (const task of queued) {
650
+ this.#armTimer(workspaceId, workerId, systemPrompt, task.id, 100, task.wait_revision, task.scheduled_at);
651
+ }
652
+ });
653
+ }
654
+ #armTimer(workspaceId, workerId, systemPrompt, loopId, status, revision, dueAt) {
655
+ if (!this.#acceptingWork)
656
+ return;
657
+ const prior = this.#loopTimers.get(loopId);
658
+ if (prior?.revision === revision && prior.status === status && prior.dueAt === dueAt)
659
+ return;
660
+ this.#clearLoopTimer(loopId);
661
+ const timer = setTimeout(() => {
662
+ if (this.#loopTimers.get(loopId)?.timer !== timer)
663
+ return;
664
+ this.#loopTimers.delete(loopId);
665
+ this.#trackSettlement(this.#wakeTimedLoop(workspaceId, workerId, systemPrompt, loopId, status, revision), "wake-on-completion");
666
+ }, Math.max(1, Math.min(dueAt - Date.now(), 2_147_483_647)));
667
+ timer.unref();
668
+ this.#loopTimers.set(loopId, { workerId, status, revision, dueAt, timer });
669
+ }
670
+ async #wakeTimedLoop(workspaceId, workerId, systemPrompt, loopId, status, revision) {
671
+ if (!this.#acceptingWork)
672
+ return;
673
+ if (status === 100 || await this.#wakeLoop(workerId, loopId, { revision, dueAt: Date.now() })) {
674
+ await this.ensureDrain({ workspaceId, workerId, systemPrompt });
691
675
  }
676
+ else {
677
+ // A capped timer or backwards clock may fire before the durable due time.
678
+ await this.scheduleWakes(workspaceId, workerId, systemPrompt);
679
+ }
680
+ }
681
+ async #inheritedPollMs(workerId, loopId) {
692
682
  const row = await this.#db.drain_worker_min_poll.get({ worker_id: workerId });
683
+ if (!this.#acceptingWork)
684
+ return null;
693
685
  if ((row?.open_count ?? 0) === 0) {
694
- this.#pollBackoff.delete(workerId);
695
- return;
686
+ this.#pollBackoff.delete(loopId);
687
+ return null;
696
688
  }
697
689
  const pollSec = row?.poll_seconds ?? null;
698
690
  // {§exec-poll} — a positive explicit cadence wins, zero opts out,
699
691
  // and an absent cadence uses the worker's exponential-backoff step.
700
692
  let delayMs;
701
693
  if (pollSec !== null && pollSec > 0) {
702
- this.#pollBackoff.delete(workerId);
694
+ this.#pollBackoff.delete(loopId);
703
695
  delayMs = pollSec * 1000;
704
696
  }
705
697
  else if (pollSec === 0) {
706
- this.#pollBackoff.delete(workerId);
707
- return; // explicit opt-out
698
+ this.#pollBackoff.delete(loopId);
699
+ return null; // explicit opt-out
708
700
  }
709
701
  else {
710
702
  // An open stream without an explicit cadence uses the stream polling floor.
711
703
  // Child joins never enter this branch: durable child settlement is their only wake edge.
712
704
  const base = Number(process.env.PLURNK_SERVICE_EXEC_POLL_SEC ?? "60");
713
705
  const turns = Number(process.env.PLURNK_SERVICE_EXEC_POLL_TURNS ?? "8");
714
- const step = this.#pollBackoff.get(workerId) ?? 0;
706
+ const step = this.#pollBackoff.get(loopId) ?? 0;
715
707
  delayMs = execPollBackoffMs(step, base, turns);
716
- this.#pollBackoff.set(workerId, step + 1);
708
+ this.#pollBackoff.set(loopId, step + 1);
717
709
  }
718
710
  // Floored by the optimistic settlement cap so a `<…,1>` cannot wake a
719
711
  // parked loop faster than the preceding turn's settlement scale.
720
- const optimisticWaitMs = readOptimisticSettlementMs();
721
- const timer = setTimeout(() => {
722
- this.#pollTimers.delete(workerId);
723
- void this.#wakeParkedWorker(workspaceId, workerId, systemPrompt);
724
- }, Math.max(delayMs, optimisticWaitMs));
725
- timer.unref();
726
- this.#pollTimers.set(workerId, timer);
712
+ return Math.max(delayMs, readOptimisticSettlementMs());
727
713
  }
728
- /** Resume `workerId`'s slept (202) loop in place — the same 202→100 resume handleWakeWorker uses, minus a
729
- * wake payload. The shared wake primitive: a poll cadence ({§exec-poll}), a watched stream concluding,
730
- * or a child worker finishing ({§worker-loop-lifecycle} topology join) all call this. A no-op if the worker was
731
- * cancelled or isn't actually parked (no slept loop) — so calling it speculatively is safe. */
732
- async #wakeParkedWorker(workspaceId, workerId, systemPrompt, oweIfActive = true) {
714
+ async #wakeLoop(workerId, loopId, condition = {}) {
715
+ // {§worker-lifecycle-durable-disposition}: test admission at the mutation,
716
+ // after asynchronous prompt, completion, or deadline selection.
717
+ if (!this.#acceptingWork || this.#workerAborts.get(workerId)?.signal.aborted)
718
+ return false;
719
+ return this.#lifecycle.wake(loopId, condition);
720
+ }
721
+ async #wakeParkedWorker(workspaceId, workerId, systemPrompt) {
733
722
  if (!this.#acceptingWork)
734
723
  return;
735
- const scope = this.#workerAborts.get(workerId);
736
- if (scope?.signal.aborted === true && !this.#activeDrains.has(workerId))
737
- return; // cancelled — no resurrection
738
- const slept = await this.#db.drain_find_slept_loop.get({ worker_id: workerId });
739
- if (slept === undefined) {
740
- // Not parked. If a drain is still ACTIVE, the worker is mid-turn and about to park — the
741
- // conclusion that fired this wake arrived before the 202 committed (the conclude-before-park
742
- // race). Owe the wake: the drain honors it at park so a worker hibernation never deadlocks.
743
- // (No active drain → already concluded/running; nothing to wake.)
744
- if (this.#activeDrains.has(workerId)) {
745
- if (oweIfActive)
746
- this.#owedWakes.add(workerId);
724
+ const waits = await this.#lifecycle.parked(workerId);
725
+ let woke = false;
726
+ for (const wait of waits) {
727
+ if (await this.#wakeLoop(workerId, wait.id, { revision: wait.wait_revision, eventOnly: true })) {
728
+ this.#clearLoopTimer(wait.id);
729
+ woke = true;
747
730
  }
748
- return;
749
731
  }
750
- const woke = await this.#lifecycle.wake(slept.id);
751
732
  if (!woke)
752
733
  return;
753
734
  const started = await this.ensureDrain({
@@ -760,14 +741,11 @@ export default class DrainSupervisor {
760
741
  });
761
742
  }
762
743
  async #workerHasLiveObligation(workerId) {
763
- const [openSubscriptions, liveChild] = await Promise.all([
764
- this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId }),
765
- this.#db.engine_worker_has_live_child.get({ worker_id: workerId }),
766
- ]);
767
- // {§exec-timeout} — a `<-1>` spawn outlives the loop and is nobody's obligation.
768
- return openSubscriptions.some(({ id }) => !this.#isDetachedSubscription(id)) || liveChild !== undefined;
769
- }
770
- settleCompletionWake(workspaceId, workerId, systemPrompt, oweIfActive = true) {
744
+ // {§worker-obligations} — the same row the completion gate reads.
745
+ const held = await this.#db.worker_live_obligations.get({ worker_id: workerId });
746
+ return held !== undefined && (held.streams === 1 || held.workers === 1);
747
+ }
748
+ settleCompletionWake(workspaceId, workerId, systemPrompt) {
771
749
  const existing = this.#completionWakeGates.get(workerId);
772
750
  if (existing !== undefined) {
773
751
  existing.conclusions++;
@@ -781,23 +759,23 @@ export default class DrainSupervisor {
781
759
  promise: completed.promise,
782
760
  };
783
761
  this.#completionWakeGates.set(workerId, gate);
784
- void this.#runCompletionWake(workspaceId, workerId, systemPrompt, oweIfActive, gate).then(completed.resolve, completed.reject).finally(() => {
762
+ void this.#runCompletionWake(workspaceId, workerId, systemPrompt, gate).then(completed.resolve, completed.reject).finally(() => {
785
763
  if (this.#completionWakeGates.get(workerId) === gate) {
786
764
  this.#completionWakeGates.delete(workerId);
787
765
  }
788
766
  });
789
767
  return gate.promise;
790
768
  }
791
- async #runCompletionWake(workspaceId, workerId, systemPrompt, oweIfActive, gate) {
769
+ async #runCompletionWake(workspaceId, workerId, systemPrompt, gate) {
792
770
  const slept = await this.#db.drain_find_slept_loop.get({ worker_id: workerId });
793
771
  if (slept === undefined) {
794
772
  this.#releaseCompletionWake(workerId, gate);
795
- return this.#wakeParkedWorker(workspaceId, workerId, systemPrompt, oweIfActive);
773
+ return this.#wakeParkedWorker(workspaceId, workerId, systemPrompt);
796
774
  }
797
775
  const timeoutMs = readOptimisticSettlementMs();
798
776
  if (timeoutMs === 0 || !(await this.#workerHasLiveObligation(workerId))) {
799
777
  this.#releaseCompletionWake(workerId, gate);
800
- return this.#wakeParkedWorker(workspaceId, workerId, systemPrompt, false);
778
+ return this.#wakeParkedWorker(workspaceId, workerId, systemPrompt);
801
779
  }
802
780
  return observed("worker.wake.settlement", { "window.ms": timeoutMs }, async (span) => {
803
781
  const startedAt = performance.now();
@@ -829,7 +807,7 @@ export default class DrainSupervisor {
829
807
  this.#releaseCompletionWake(workerId, gate);
830
808
  if (release === "cancelled")
831
809
  return;
832
- return this.#wakeParkedWorker(workspaceId, workerId, systemPrompt, false);
810
+ return this.#wakeParkedWorker(workspaceId, workerId, systemPrompt);
833
811
  });
834
812
  }
835
813
  #releaseCompletionWake(workerId, gate) {
@@ -837,24 +815,18 @@ export default class DrainSupervisor {
837
815
  this.#completionWakeGates.delete(workerId);
838
816
  }
839
817
  }
840
- /** A worker's drain exited. If the worker truly CONCLUDED — no 202-blocked loop, no open stream — then
841
- * wake its PARENT in place if the parent is blocked on the join (the structured-concurrency join — a
842
- * child finishing is the wake edge for a parent that waited on it, {§worker-lifecycle-child-wake}). A worker
843
- * blocked at 202, or still holding a stream, is NOT concluded — its own wake edges drive it, not this.
844
- * The parent reads the child's deliverable from its own log (the {§worker-scheme-collect} delta) on
845
- * resume — control edge here, never an injected prompt. Recurses up via the parent's own drain-exit. */
846
- async #onDrainExit(workspaceId, workerId, systemPrompt) {
847
- const slept = await this.#db.drain_find_slept_loop.get({ worker_id: workerId });
848
- if (slept !== undefined)
849
- return; // parked at 202 — not concluded, the worker is still alive
850
- const openSubs = (await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId }))
851
- .filter(({ id }) => !this.#isDetachedSubscription(id)); // {§exec-timeout} — `<-1>` is nobody's obligation
852
- if (openSubs.length > 0)
853
- return; // a stream still runs — its conclusion re-evaluates, not this exit
818
+ #publishTermination(workspaceId, event) {
819
+ this.#emit(workspaceId, "loop/terminated", event);
820
+ this.#trackSettlement(this.#notifyParentCompletion(workspaceId, event.workerId), "wake-on-completion");
821
+ }
822
+ // {§worker-lifecycle-child-wake}: completion belongs to a task, not drain teardown.
823
+ async #notifyParentCompletion(workspaceId, workerId) {
854
824
  const parent = await this.#db.worker_parent_id.get({ worker_id: workerId });
855
825
  if (parent?.parent_worker_id == null)
856
- return; // a root worker — nobody to wake
857
- await this.settleCompletionWake(workspaceId, parent.parent_worker_id, systemPrompt);
826
+ return;
827
+ await this.settleCompletionWake(workspaceId, parent.parent_worker_id, await this.#readSystemPrompt());
858
828
  }
859
829
  }
830
+ _a = DrainSupervisor;
831
+ export default DrainSupervisor;
860
832
  //# sourceMappingURL=DrainSupervisor.js.map