@plurnk/plurnk-service 1.7.0 → 1.9.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 (381) hide show
  1. package/.env.defaults +49 -42
  2. package/INSTALL.md +48 -11
  3. package/README.md +12 -2
  4. package/SPEC.md +1166 -475
  5. package/digest-sql/channel-dispositions/dispositions.sql +15 -0
  6. package/digest-sql/channel-state/state.sql +6 -0
  7. package/digest-sql/curation/curation.sql +5 -0
  8. package/digest-sql/derivations/derivations.sql +4 -0
  9. package/digest-sql/embeddings/embeddings.sql +4 -0
  10. package/digest-sql/entry-dispositions/dispositions.sql +15 -0
  11. package/digest-sql/entry-state/state.sql +6 -0
  12. package/digest-sql/token-counts/token-counts.sql +2 -0
  13. package/dist/Paths.d.ts +0 -4
  14. package/dist/Paths.d.ts.map +1 -1
  15. package/dist/Paths.js +1 -21
  16. package/dist/Paths.js.map +1 -1
  17. package/dist/build-info.json +1 -1
  18. package/dist/content/read-projector.d.ts.map +1 -1
  19. package/dist/content/read-projector.js +4 -3
  20. package/dist/content/read-projector.js.map +1 -1
  21. package/dist/core/BudgetReadout.d.ts.map +1 -1
  22. package/dist/core/BudgetReadout.js +12 -22
  23. package/dist/core/BudgetReadout.js.map +1 -1
  24. package/dist/core/ChannelWrite.d.ts +8 -3
  25. package/dist/core/ChannelWrite.d.ts.map +1 -1
  26. package/dist/core/ChannelWrite.js +40 -8
  27. package/dist/core/ChannelWrite.js.map +1 -1
  28. package/dist/core/ChannelWrite.sql +19 -5
  29. package/dist/core/CoreSchemeServices.d.ts +7 -3
  30. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  31. package/dist/core/CoreSchemeServices.js +8 -1
  32. package/dist/core/CoreSchemeServices.js.map +1 -1
  33. package/dist/core/Dispatcher.d.ts +15 -6
  34. package/dist/core/Dispatcher.d.ts.map +1 -1
  35. package/dist/core/Dispatcher.js +352 -306
  36. package/dist/core/Dispatcher.js.map +1 -1
  37. package/dist/core/Engine.d.ts +22 -10
  38. package/dist/core/Engine.d.ts.map +1 -1
  39. package/dist/core/Engine.js +86 -46
  40. package/dist/core/Engine.js.map +1 -1
  41. package/dist/core/Engine.sql +202 -177
  42. package/dist/core/EntryAddressBinding.d.ts +32 -0
  43. package/dist/core/EntryAddressBinding.d.ts.map +1 -0
  44. package/dist/core/EntryAddressBinding.js +93 -0
  45. package/dist/core/EntryAddressBinding.js.map +1 -0
  46. package/dist/core/ExecutorRegistry.d.ts +12 -6
  47. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  48. package/dist/core/ExecutorRegistry.js +34 -31
  49. package/dist/core/ExecutorRegistry.js.map +1 -1
  50. package/dist/core/HostPaths.d.ts +29 -0
  51. package/dist/core/HostPaths.d.ts.map +1 -0
  52. package/dist/core/HostPaths.js +74 -0
  53. package/dist/core/HostPaths.js.map +1 -0
  54. package/dist/core/LegacyHome.d.ts +10 -0
  55. package/dist/core/LegacyHome.d.ts.map +1 -0
  56. package/dist/core/LegacyHome.js +170 -0
  57. package/dist/core/LegacyHome.js.map +1 -0
  58. package/dist/core/LogBody.d.ts +1 -1
  59. package/dist/core/LogBody.d.ts.map +1 -1
  60. package/dist/core/LogBody.js +21 -3
  61. package/dist/core/LogBody.js.map +1 -1
  62. package/dist/core/LogEntryProjection.js +1 -1
  63. package/dist/core/LogEntryProjection.js.map +1 -1
  64. package/dist/core/LogVisibility.d.ts +28 -0
  65. package/dist/core/LogVisibility.d.ts.map +1 -0
  66. package/dist/core/LogVisibility.js +215 -0
  67. package/dist/core/LogVisibility.js.map +1 -0
  68. package/dist/core/LoopLifecycle.d.ts +1 -0
  69. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  70. package/dist/core/LoopLifecycle.js +8 -0
  71. package/dist/core/LoopLifecycle.js.map +1 -1
  72. package/dist/core/LoopLifecycle.sql +10 -0
  73. package/dist/core/OperatorConfig.d.ts +6 -0
  74. package/dist/core/OperatorConfig.d.ts.map +1 -0
  75. package/dist/core/OperatorConfig.js +95 -0
  76. package/dist/core/OperatorConfig.js.map +1 -0
  77. package/dist/core/OverflowTurn.d.ts +11 -0
  78. package/dist/core/OverflowTurn.d.ts.map +1 -0
  79. package/dist/core/OverflowTurn.js +81 -0
  80. package/dist/core/OverflowTurn.js.map +1 -0
  81. package/dist/core/Owner.js +3 -3
  82. package/dist/core/Owner.js.map +1 -1
  83. package/dist/core/PacketBuilder.d.ts +7 -15
  84. package/dist/core/PacketBuilder.d.ts.map +1 -1
  85. package/dist/core/PacketBuilder.js +64 -127
  86. package/dist/core/PacketBuilder.js.map +1 -1
  87. package/dist/core/ProblemLog.js +1 -1
  88. package/dist/core/ProblemLog.js.map +1 -1
  89. package/dist/core/ProposalLifecycle.d.ts +4 -1
  90. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  91. package/dist/core/ProposalLifecycle.js +86 -47
  92. package/dist/core/ProposalLifecycle.js.map +1 -1
  93. package/dist/core/ProviderInstantiate.d.ts +9 -5
  94. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  95. package/dist/core/ProviderInstantiate.js +99 -104
  96. package/dist/core/ProviderInstantiate.js.map +1 -1
  97. package/dist/core/ReasoningEvent.d.ts +13 -0
  98. package/dist/core/ReasoningEvent.d.ts.map +1 -0
  99. package/dist/core/ReasoningEvent.js +6 -0
  100. package/dist/core/ReasoningEvent.js.map +1 -0
  101. package/dist/core/ResourceMutations.d.ts +15 -19
  102. package/dist/core/ResourceMutations.d.ts.map +1 -1
  103. package/dist/core/ResourceMutations.js +118 -41
  104. package/dist/core/ResourceMutations.js.map +1 -1
  105. package/dist/core/SchemeRegistry.d.ts +12 -12
  106. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  107. package/dist/core/SchemeRegistry.js +58 -57
  108. package/dist/core/SchemeRegistry.js.map +1 -1
  109. package/dist/core/ToolResources.d.ts +19 -0
  110. package/dist/core/ToolResources.d.ts.map +1 -0
  111. package/dist/core/ToolResources.js +123 -0
  112. package/dist/core/ToolResources.js.map +1 -0
  113. package/dist/core/Turn.d.ts +27 -0
  114. package/dist/core/Turn.d.ts.map +1 -0
  115. package/dist/core/Turn.js +43 -0
  116. package/dist/core/Turn.js.map +1 -0
  117. package/dist/core/Turn.sql +72 -0
  118. package/dist/core/TurnOps.d.ts +7 -0
  119. package/dist/core/TurnOps.d.ts.map +1 -0
  120. package/dist/core/TurnOps.js +73 -0
  121. package/dist/core/TurnOps.js.map +1 -0
  122. package/dist/core/TurnRunner.d.ts +56 -5
  123. package/dist/core/TurnRunner.d.ts.map +1 -1
  124. package/dist/core/TurnRunner.js +1163 -929
  125. package/dist/core/TurnRunner.js.map +1 -1
  126. package/dist/core/WorkerName.d.ts +2 -1
  127. package/dist/core/WorkerName.d.ts.map +1 -1
  128. package/dist/core/WorkerName.js +2 -1
  129. package/dist/core/WorkerName.js.map +1 -1
  130. package/dist/core/WorkerName.sql +17 -2
  131. package/dist/core/WorkspaceGate.js +1 -1
  132. package/dist/core/WorkspaceGate.js.map +1 -1
  133. package/dist/core/caps/CapsResolve.d.ts +2 -2
  134. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  135. package/dist/core/caps/CapsResolve.js +5 -7
  136. package/dist/core/caps/CapsResolve.js.map +1 -1
  137. package/dist/core/caps/DbChannelCaps.d.ts +1 -1
  138. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  139. package/dist/core/caps/DbChannelCaps.js +13 -11
  140. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  141. package/dist/core/caps/DbEntryCaps.d.ts +5 -5
  142. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  143. package/dist/core/caps/DbEntryCaps.js +27 -30
  144. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  145. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  146. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  147. package/dist/core/caps/DbNotifyCaps.js +5 -3
  148. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  149. package/dist/core/caps/DbSubscriptionCaps.d.ts +3 -3
  150. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  151. package/dist/core/caps/DbSubscriptionCaps.js +16 -26
  152. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  153. package/dist/core/caps/SchemeCtxImpl.d.ts +6 -4
  154. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  155. package/dist/core/caps/SchemeCtxImpl.js +37 -5
  156. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  157. package/dist/core/env-defaults.d.ts.map +1 -1
  158. package/dist/core/env-defaults.js +7 -5
  159. package/dist/core/env-defaults.js.map +1 -1
  160. package/dist/core/file-creation-policy.d.ts +9 -0
  161. package/dist/core/file-creation-policy.d.ts.map +1 -0
  162. package/dist/core/file-creation-policy.js +25 -0
  163. package/dist/core/file-creation-policy.js.map +1 -0
  164. package/dist/core/fork.d.ts +4 -1
  165. package/dist/core/fork.d.ts.map +1 -1
  166. package/dist/core/fork.js +39 -16
  167. package/dist/core/fork.js.map +1 -1
  168. package/dist/core/fork.sql +83 -40
  169. package/dist/core/git-env.d.ts +0 -1
  170. package/dist/core/git-env.d.ts.map +1 -1
  171. package/dist/core/git-env.js +0 -3
  172. package/dist/core/git-env.js.map +1 -1
  173. package/dist/core/git-membership.d.ts +19 -1
  174. package/dist/core/git-membership.d.ts.map +1 -1
  175. package/dist/core/git-membership.js +233 -74
  176. package/dist/core/git-membership.js.map +1 -1
  177. package/dist/core/git-state.d.ts.map +1 -1
  178. package/dist/core/git-state.js +13 -14
  179. package/dist/core/git-state.js.map +1 -1
  180. package/dist/core/operation-target-groups.d.ts +3 -0
  181. package/dist/core/operation-target-groups.d.ts.map +1 -0
  182. package/dist/core/operation-target-groups.js +53 -0
  183. package/dist/core/operation-target-groups.js.map +1 -0
  184. package/dist/core/packet-inject.d.ts +0 -1
  185. package/dist/core/packet-inject.d.ts.map +1 -1
  186. package/dist/core/packet-inject.js +6 -13
  187. package/dist/core/packet-inject.js.map +1 -1
  188. package/dist/core/packet-wire.d.ts +4 -0
  189. package/dist/core/packet-wire.d.ts.map +1 -1
  190. package/dist/core/packet-wire.js +226 -53
  191. package/dist/core/packet-wire.js.map +1 -1
  192. package/dist/core/plurnk-uri.d.ts +14 -3
  193. package/dist/core/plurnk-uri.d.ts.map +1 -1
  194. package/dist/core/plurnk-uri.js +62 -24
  195. package/dist/core/plurnk-uri.js.map +1 -1
  196. package/dist/core/scheme-types.d.ts +1 -0
  197. package/dist/core/scheme-types.d.ts.map +1 -1
  198. package/dist/core/teaching.d.ts.map +1 -1
  199. package/dist/core/teaching.js +4 -5
  200. package/dist/core/teaching.js.map +1 -1
  201. package/dist/core/worker-ops.sql +10 -6
  202. package/dist/core/worker-settings.d.ts +10 -0
  203. package/dist/core/worker-settings.d.ts.map +1 -0
  204. package/dist/core/worker-settings.js +32 -0
  205. package/dist/core/worker-settings.js.map +1 -0
  206. package/dist/core/workspace-settings.d.ts +2 -11
  207. package/dist/core/workspace-settings.d.ts.map +1 -1
  208. package/dist/core/workspace-settings.js +7 -47
  209. package/dist/core/workspace-settings.js.map +1 -1
  210. package/dist/digest/Digest.d.ts.map +1 -1
  211. package/dist/digest/Digest.js +164 -92
  212. package/dist/digest/Digest.js.map +1 -1
  213. package/dist/digest/digest.sql +9 -3
  214. package/dist/observe/genai.d.ts +9 -0
  215. package/dist/observe/genai.d.ts.map +1 -0
  216. package/dist/observe/genai.js +31 -0
  217. package/dist/observe/genai.js.map +1 -0
  218. package/dist/observe/spans.d.ts +3 -3
  219. package/dist/observe/spans.d.ts.map +1 -1
  220. package/dist/observe/spans.js +20 -5
  221. package/dist/observe/spans.js.map +1 -1
  222. package/dist/schemes/EffectPolicy.d.ts +3 -0
  223. package/dist/schemes/EffectPolicy.d.ts.map +1 -1
  224. package/dist/schemes/EffectPolicy.js +38 -6
  225. package/dist/schemes/EffectPolicy.js.map +1 -1
  226. package/dist/schemes/Exec.d.ts +3 -2
  227. package/dist/schemes/Exec.d.ts.map +1 -1
  228. package/dist/schemes/Exec.js +109 -34
  229. package/dist/schemes/Exec.js.map +1 -1
  230. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  231. package/dist/schemes/ExecOutputScheme.js +5 -4
  232. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  233. package/dist/schemes/File.d.ts.map +1 -1
  234. package/dist/schemes/File.js +170 -80
  235. package/dist/schemes/File.js.map +1 -1
  236. package/dist/schemes/Log.d.ts +6 -2
  237. package/dist/schemes/Log.d.ts.map +1 -1
  238. package/dist/schemes/Log.js +73 -22
  239. package/dist/schemes/Log.js.map +1 -1
  240. package/dist/schemes/Log.sql +25 -7
  241. package/dist/schemes/Prompt.d.ts +2 -3
  242. package/dist/schemes/Prompt.d.ts.map +1 -1
  243. package/dist/schemes/Prompt.js +5 -9
  244. package/dist/schemes/Prompt.js.map +1 -1
  245. package/dist/schemes/QuestionTool.d.ts +10 -0
  246. package/dist/schemes/QuestionTool.d.ts.map +1 -0
  247. package/dist/schemes/QuestionTool.js +81 -0
  248. package/dist/schemes/QuestionTool.js.map +1 -0
  249. package/dist/schemes/Skill.d.ts.map +1 -1
  250. package/dist/schemes/Skill.js +6 -1
  251. package/dist/schemes/Skill.js.map +1 -1
  252. package/dist/schemes/Worker.d.ts.map +1 -1
  253. package/dist/schemes/Worker.js +97 -34
  254. package/dist/schemes/Worker.js.map +1 -1
  255. package/dist/schemes/_entry-crud.d.ts +5 -5
  256. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  257. package/dist/schemes/_entry-crud.js +22 -17
  258. package/dist/schemes/_entry-crud.js.map +1 -1
  259. package/dist/schemes/_entry-crud.sql +77 -24
  260. package/dist/schemes/_entry-find.d.ts +8 -5
  261. package/dist/schemes/_entry-find.d.ts.map +1 -1
  262. package/dist/schemes/_entry-find.js +121 -40
  263. package/dist/schemes/_entry-find.js.map +1 -1
  264. package/dist/schemes/_entry-find.sql +16 -11
  265. package/dist/schemes/_entry-graph.sql +2 -6
  266. package/dist/schemes/_entry-manifest.d.ts +2 -2
  267. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  268. package/dist/schemes/_entry-manifest.js +18 -6
  269. package/dist/schemes/_entry-manifest.js.map +1 -1
  270. package/dist/schemes/_entry-ops.d.ts +6 -5
  271. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  272. package/dist/schemes/_entry-ops.js +39 -46
  273. package/dist/schemes/_entry-ops.js.map +1 -1
  274. package/dist/schemes/_entry-ops.sql +9 -6
  275. package/dist/schemes/_entry-send.d.ts +2 -2
  276. package/dist/schemes/_entry-send.d.ts.map +1 -1
  277. package/dist/schemes/_entry-send.js +16 -14
  278. package/dist/schemes/_entry-send.js.map +1 -1
  279. package/dist/schemes/_search-index.d.ts.map +1 -1
  280. package/dist/schemes/_search-index.js +39 -25
  281. package/dist/schemes/_search-index.js.map +1 -1
  282. package/dist/server/Daemon.d.ts +98 -15
  283. package/dist/server/Daemon.d.ts.map +1 -1
  284. package/dist/server/Daemon.js +864 -348
  285. package/dist/server/Daemon.js.map +1 -1
  286. package/dist/server/DaemonModule.d.ts +84 -19
  287. package/dist/server/DaemonModule.d.ts.map +1 -1
  288. package/dist/server/DrainSupervisor.d.ts +15 -10
  289. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  290. package/dist/server/DrainSupervisor.js +70 -19
  291. package/dist/server/DrainSupervisor.js.map +1 -1
  292. package/dist/server/Functionality.d.ts +44 -0
  293. package/dist/server/Functionality.d.ts.map +1 -0
  294. package/dist/server/Functionality.js +422 -0
  295. package/dist/server/Functionality.js.map +1 -0
  296. package/dist/server/FunctionalityManager.d.ts +22 -0
  297. package/dist/server/FunctionalityManager.d.ts.map +1 -0
  298. package/dist/server/FunctionalityManager.js +121 -0
  299. package/dist/server/FunctionalityManager.js.map +1 -0
  300. package/dist/server/SkillsFunctionality.d.ts +61 -0
  301. package/dist/server/SkillsFunctionality.d.ts.map +1 -0
  302. package/dist/server/SkillsFunctionality.js +449 -0
  303. package/dist/server/SkillsFunctionality.js.map +1 -0
  304. package/dist/server/WorkerCapabilities.d.ts +21 -0
  305. package/dist/server/WorkerCapabilities.d.ts.map +1 -0
  306. package/dist/server/WorkerCapabilities.js +214 -0
  307. package/dist/server/WorkerCapabilities.js.map +1 -0
  308. package/dist/server/WorkerResidency.d.ts +40 -0
  309. package/dist/server/WorkerResidency.d.ts.map +1 -0
  310. package/dist/server/WorkerResidency.js +283 -0
  311. package/dist/server/WorkerResidency.js.map +1 -0
  312. package/dist/server/client-input.d.ts +6 -2
  313. package/dist/server/client-input.d.ts.map +1 -1
  314. package/dist/server/client-input.js +72 -32
  315. package/dist/server/client-input.js.map +1 -1
  316. package/dist/server/dispatch-as-plurnk.d.ts +2 -2
  317. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  318. package/dist/server/dispatch-as-plurnk.js +109 -23
  319. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  320. package/dist/server/drain.sql +33 -13
  321. package/dist/server/envelope.d.ts +7 -2
  322. package/dist/server/envelope.d.ts.map +1 -1
  323. package/dist/server/envelope.js +12 -6
  324. package/dist/server/envelope.js.map +1 -1
  325. package/dist/server/envelope.sql +36 -4
  326. package/dist/server/lifecycle-recovery.sql +24 -17
  327. package/dist/server/logEntry.d.ts +2 -1
  328. package/dist/server/logEntry.d.ts.map +1 -1
  329. package/dist/server/logEntry.js +3 -2
  330. package/dist/server/logEntry.js.map +1 -1
  331. package/dist/server/logEntry.sql +7 -0
  332. package/dist/server/loop-model.d.ts +2 -2
  333. package/dist/server/loop-model.d.ts.map +1 -1
  334. package/dist/server/loop-model.js +23 -33
  335. package/dist/server/loop-model.js.map +1 -1
  336. package/dist/server/loopDocs.d.ts +2 -1
  337. package/dist/server/loopDocs.d.ts.map +1 -1
  338. package/dist/server/loopDocs.js +88 -21
  339. package/dist/server/loopDocs.js.map +1 -1
  340. package/dist/server/loopDocs.sql +14 -8
  341. package/dist/server/model-catalog.d.ts +3 -0
  342. package/dist/server/model-catalog.d.ts.map +1 -0
  343. package/dist/server/model-catalog.js +83 -0
  344. package/dist/server/model-catalog.js.map +1 -0
  345. package/dist/server/model-route.d.ts +7 -0
  346. package/dist/server/model-route.d.ts.map +1 -0
  347. package/dist/server/model-route.js +47 -0
  348. package/dist/server/model-route.js.map +1 -0
  349. package/dist/server/module-discovery.d.ts +13 -0
  350. package/dist/server/module-discovery.d.ts.map +1 -0
  351. package/dist/server/module-discovery.js +103 -0
  352. package/dist/server/module-discovery.js.map +1 -0
  353. package/dist/server/seam-entry-read.sql +9 -7
  354. package/dist/server/seam-log-read.sql +1 -1
  355. package/dist/server/seam-loop.sql +10 -1
  356. package/dist/server/seam-proposal-list.sql +4 -2
  357. package/dist/server/worker-capabilities.sql +19 -0
  358. package/dist/service.d.ts.map +1 -1
  359. package/dist/service.js +193 -124
  360. package/dist/service.js.map +1 -1
  361. package/migrations/001_schema.sql +905 -196
  362. package/package.json +40 -38
  363. package/dist/core/ExecutableTools.d.ts +0 -11
  364. package/dist/core/ExecutableTools.d.ts.map +0 -1
  365. package/dist/core/ExecutableTools.js +0 -49
  366. package/dist/core/ExecutableTools.js.map +0 -1
  367. package/dist/core/JournalTurn.d.ts +0 -9
  368. package/dist/core/JournalTurn.d.ts.map +0 -1
  369. package/dist/core/JournalTurn.js +0 -14
  370. package/dist/core/JournalTurn.js.map +0 -1
  371. package/dist/core/JournalTurn.sql +0 -10
  372. package/dist/core/git-iso.d.ts +0 -13
  373. package/dist/core/git-iso.d.ts.map +0 -1
  374. package/dist/core/git-iso.js +0 -267
  375. package/dist/core/git-iso.js.map +0 -1
  376. package/dist/core/search-gate.d.ts +0 -17
  377. package/dist/core/search-gate.d.ts.map +0 -1
  378. package/dist/core/search-gate.js +0 -77
  379. package/dist/core/search-gate.js.map +0 -1
  380. package/dist/digest/Digest.sql +0 -1
  381. package/dist/server/workspace-capabilities.sql +0 -19
@@ -1,8 +1,7 @@
1
1
  var _a;
2
- import Owner from "./Owner.js";
3
2
  import WorkerName, { WorkerNameError } from "./WorkerName.js";
4
3
  import WorkerControlAddress from "./WorkerControlAddress.js";
5
- import { entryPathnameOf, foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
4
+ import { entryCoordinateOf, foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
6
5
  import Fork from "./fork.js";
7
6
  import WorkerCap from "./worker-cap.js";
8
7
  import { PathSyntax, TagSignal } from "@plurnk/plurnk-contracts";
@@ -11,17 +10,16 @@ import LoopFlagsReader from "./LoopFlagsReader.js";
11
10
  import ChannelWrite from "./ChannelWrite.js";
12
11
  import { ReadProjector } from "../content/index.js";
13
12
  import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
14
- import EntryCrud from "../schemes/_entry-crud.js";
15
13
  import EntryOps from "../schemes/_entry-ops.js";
16
14
  import EntryFind from "../schemes/_entry-find.js";
17
- import WorkspaceSettings from "./workspace-settings.js";
18
15
  import LoopLifecycle from "./LoopLifecycle.js";
19
16
  import TerminalResult from "./TerminalResult.js";
20
17
  import Results from "./results.js";
21
18
  import { OperationFailureError } from "./results.js";
22
19
  import EffectPolicy from "../schemes/EffectPolicy.js";
23
- import { CoreSchemeAdapterBase, } from "./CoreSchemeServices.js";
24
- import { InvalidOperationResultError, NetworkAddress, } from "@plurnk/plurnk-schemes";
20
+ import WorkerSettingsReader from "./worker-settings.js";
21
+ import { CoreSchemeAdapterBase } from "./CoreSchemeServices.js";
22
+ import { InvalidOperationResultError, } from "@plurnk/plurnk-schemes";
25
23
  import DurableStatement from "./DurableStatement.js";
26
24
  import ResourceMutations from "./ResourceMutations.js";
27
25
  import LogBody from "./LogBody.js";
@@ -70,13 +68,13 @@ class Dispatcher {
70
68
  // {§send-premature-terminate}/SEND signal 202 with scope — the engine-owned park-deadline registry (loopId → seconds;
71
69
  // -1 = indefinite). The dispatcher WRITES at park; the daemon's drain park-exit consumes.
72
70
  #parkDeadlines;
73
- #searchGate;
74
71
  // Per-turn running-worker READ obligations. {§join-blocking-collect}
75
72
  #joinTargets;
76
73
  #liveSubscriptions;
77
74
  #lifecycle;
78
75
  #resourceMutations;
79
- constructor({ db, schemes, mimetypes, weigh, notices, proposals, interactions, executors, loopSignal, streamEventNotify, wakeWorkerNotify, injectWorker, branchWorker, branchCompletionGate, cancelWorker, cancelDescendants, searchGate, parkDeadlines, joinTargets, liveSubscriptions }) {
76
+ #entryAddresses;
77
+ constructor({ db, schemes, mimetypes, weigh, notices, proposals, interactions, executors, loopSignal, streamEventNotify, wakeWorkerNotify, injectWorker, branchWorker, branchCompletionGate, cancelWorker, cancelDescendants, parkDeadlines, joinTargets, liveSubscriptions, entryAddresses }) {
80
78
  this.#db = db;
81
79
  this.#schemes = schemes;
82
80
  this.#mimetypes = mimetypes;
@@ -94,48 +92,66 @@ class Dispatcher {
94
92
  this.#cancelWorker = cancelWorker;
95
93
  this.#cancelDescendants = cancelDescendants;
96
94
  this.#parkDeadlines = parkDeadlines ?? new Map();
97
- this.#searchGate = searchGate;
98
95
  this.#joinTargets = joinTargets ?? new Set();
99
96
  this.#liveSubscriptions = liveSubscriptions;
97
+ this.#entryAddresses = entryAddresses;
100
98
  this.#lifecycle = new LoopLifecycle(db);
101
99
  this.#resourceMutations = new ResourceMutations({
102
100
  schemes,
103
101
  liveSubscriptions,
104
102
  run: (schemeName, statement, ctx) => this.#run(schemeName, statement, ctx),
105
- checkWritable: (statement, origin, workspaceId) => this.#checkWritable(statement, origin, workspaceId),
106
- checkFlagsGate: (statement, loopId, workspaceId) => this.#checkFlagsGate(statement, loopId, workspaceId),
107
- editTargetIdentity: (statement, workspaceId) => this.#editTargetIdentity(statement, workspaceId),
103
+ checkWritable: (statement, origin, workerId) => this.#checkWritable(statement, origin, workerId),
104
+ checkFlagsGate: (statement, loopId, workerId) => this.#checkFlagsGate(statement, loopId, workerId),
105
+ editTargetIdentity: (statement, workspaceId, workerId) => this.#editTargetIdentity(statement, workspaceId, workerId),
108
106
  canonicalFilePath: (pathname, workspaceId) => this.#canonicalFilePath(pathname, workspaceId),
109
107
  prepareDataRepresentation: (args) => this.#prepareDataRepresentation({
110
108
  ...args,
111
109
  handler: args.handler,
112
110
  }),
113
- readEntry: (scheme, pathname, ctx) => this.#readEntry(scheme, pathname, ctx),
114
- writeEntry: (scheme, pathname, entry, ctx) => this.#writeEntry(scheme, pathname, entry, ctx),
115
- deleteChannel: (scheme, pathname, channel, ctx) => this.#deleteChannel(scheme, pathname, channel, ctx),
111
+ resolveDataEntryAddress: (args) => this.#entryAddresses.resolve(args),
112
+ readEntry: (scheme, address, ctx) => this.#readEntry(scheme, address, ctx),
113
+ writeEntry: (scheme, address, entry, ctx) => this.#writeEntry(scheme, address, entry, ctx),
114
+ deleteChannel: (scheme, address, channel, ctx) => this.#deleteChannel(scheme, address, channel, ctx),
116
115
  applyProposal: (statement, result, resolution, ids) => this.#proposals.workerApply(statement, result, resolution, ids),
117
116
  });
118
117
  }
119
118
  // workspace → project_root, memoized: {§fs-namespace} fixes the root immutably at
120
119
  // workspace creation, so a process-lifetime cache can never go stale.
121
120
  #rootCache = new Map();
122
- #handlerContext(scheme, ctx) {
123
- const manifest = this.#schemes.manifestFor(scheme, ctx.workspaceId);
124
- return manifest === undefined ? null : new SchemeCtxImpl(ctx, scheme, manifest, this.#liveSubscriptions);
121
+ evictWorkspaceCache(workspaceId) {
122
+ this.#rootCache.delete(workspaceId);
125
123
  }
126
- #entryContext(scheme, ctx) {
127
- const handlerCtx = this.#handlerContext(scheme, ctx);
128
- return this.#schemes.manifestFor(scheme, ctx.workspaceId)?.category === "data" ? handlerCtx : null;
124
+ async #fixedEntryOwnerId(manifest, ctx) {
125
+ return this.#entryAddresses.fixedOwnerId(manifest, ctx);
129
126
  }
130
- #coreCrud(scheme, workspaceId) {
131
- const handler = this.#schemes.get(scheme, workspaceId);
127
+ async #handlerContext(scheme, ctx, authority = "") {
128
+ const manifest = this.#schemes.manifestFor(scheme, ctx.functionalityWorkerId);
129
+ return manifest === undefined
130
+ ? null
131
+ : new SchemeCtxImpl(ctx, scheme, manifest, this.#liveSubscriptions, {
132
+ authority,
133
+ ownerId: await this.#fixedEntryOwnerId(manifest, ctx),
134
+ });
135
+ }
136
+ #boundEntryContext(routedScheme, address, ctx) {
137
+ const manifest = this.#schemes.manifestFor(routedScheme, ctx.functionalityWorkerId);
138
+ return manifest?.category === "data"
139
+ ? new SchemeCtxImpl(ctx, address.scheme, manifest, this.#liveSubscriptions, {
140
+ authority: address.authority,
141
+ ownerId: address.ownerId,
142
+ })
143
+ : null;
144
+ }
145
+ #coreCrud(scheme, workerId) {
146
+ const handler = this.#schemes.get(scheme, workerId);
132
147
  return handler instanceof CoreSchemeAdapterBase
133
148
  ? handler
134
149
  : undefined;
135
150
  }
136
- async #readEntry(scheme, pathname, ctx) {
137
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
138
- const handlerCtx = this.#entryContext(scheme, ctx);
151
+ async #readEntry(scheme, address, ctx) {
152
+ const { pathname } = address;
153
+ const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
154
+ const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
139
155
  if (typeof handler?.readEntry === "function" && handlerCtx !== null) {
140
156
  return Results.assert(await handler.readEntry(pathname, handlerCtx));
141
157
  }
@@ -144,11 +160,11 @@ class Dispatcher {
144
160
  return _a.#failure("entry-read-not-implemented", 501, `The '${scheme}' scheme does not provide entry reads.`, { entry: null }, {
145
161
  stage: "entry-read",
146
162
  scheme,
147
- target: renderAddress(scheme, pathname),
163
+ target: renderAddress(address),
148
164
  retryable: false,
149
165
  });
150
166
  }
151
- const result = Results.assert(await caps.read(pathname, scheme === "prompt" ? "worker" : "commons"));
167
+ const result = Results.assert(await caps.read(pathname));
152
168
  return Results.assert({
153
169
  ...result,
154
170
  status: result.status,
@@ -162,9 +178,10 @@ class Dispatcher {
162
178
  },
163
179
  });
164
180
  }
165
- async #writeEntry(scheme, pathname, entry, ctx) {
166
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
167
- const handlerCtx = this.#entryContext(scheme, ctx);
181
+ async #writeEntry(scheme, address, entry, ctx) {
182
+ const { pathname } = address;
183
+ const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
184
+ const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
168
185
  if (typeof handler?.writeEntry === "function" && handlerCtx !== null) {
169
186
  return Results.assert(await handler.writeEntry(pathname, entry, handlerCtx));
170
187
  }
@@ -173,15 +190,16 @@ class Dispatcher {
173
190
  return _a.#failure("entry-write-not-implemented", 501, `The '${scheme}' scheme does not provide entry writes.`, { created: false, entryId: null }, {
174
191
  stage: "entry-write",
175
192
  scheme,
176
- target: renderAddress(scheme, pathname),
193
+ target: renderAddress(address),
177
194
  retryable: false,
178
195
  });
179
196
  }
180
- return Results.assert(await caps.write(pathname, entry, scheme === "prompt" ? "worker" : "commons"));
197
+ return Results.assert(await caps.write(pathname, entry));
181
198
  }
182
- async #deleteEntry(scheme, pathname, ctx) {
183
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
184
- const handlerCtx = this.#entryContext(scheme, ctx);
199
+ async #deleteEntry(scheme, address, ctx) {
200
+ const { pathname } = address;
201
+ const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
202
+ const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
185
203
  if (typeof handler?.deleteEntry === "function" && handlerCtx !== null) {
186
204
  return Results.assert(await handler.deleteEntry(pathname, handlerCtx));
187
205
  }
@@ -190,15 +208,16 @@ class Dispatcher {
190
208
  return _a.#failure("entry-delete-not-implemented", 501, `The '${scheme}' scheme does not provide entry deletion.`, {}, {
191
209
  stage: "entry-delete",
192
210
  scheme,
193
- target: renderAddress(scheme, pathname),
211
+ target: renderAddress(address),
194
212
  retryable: false,
195
213
  });
196
214
  }
197
- return Results.assert(await caps.delete(pathname, scheme === "prompt" ? "worker" : "commons"));
215
+ return Results.assert(await caps.delete(pathname));
198
216
  }
199
- async #deleteChannel(scheme, pathname, channel, ctx) {
200
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
201
- const handlerCtx = this.#entryContext(scheme, ctx);
217
+ async #deleteChannel(scheme, address, channel, ctx) {
218
+ const { pathname } = address;
219
+ const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
220
+ const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
202
221
  if (typeof handler?.deleteChannel === "function" && handlerCtx !== null) {
203
222
  return Results.assert(await handler.deleteChannel(pathname, channel, handlerCtx));
204
223
  }
@@ -207,12 +226,12 @@ class Dispatcher {
207
226
  return _a.#failure("channel-delete-not-implemented", 501, `The '${scheme}' scheme does not provide channel deletion.`, {}, {
208
227
  stage: "channel-delete",
209
228
  scheme,
210
- target: renderAddress(scheme, pathname),
229
+ target: renderAddress(address),
211
230
  channel,
212
231
  retryable: false,
213
232
  });
214
233
  }
215
- return Results.assert(await caps.delete(pathname, scheme === "prompt" ? "worker" : "commons", channel));
234
+ return Results.assert(await caps.delete(pathname, channel));
216
235
  }
217
236
  async #workspaceRoot(workspaceId) {
218
237
  if (this.#rootCache.has(workspaceId))
@@ -235,8 +254,8 @@ class Dispatcher {
235
254
  async #canonicalFilePath(pathname, workspaceId) {
236
255
  return Namespace.canonicalizeSpelling(pathname, await this.#workspaceRoot(workspaceId));
237
256
  }
238
- async #editTargetIdentity(statement, workspaceId) {
239
- const target = this.#extractTarget(statement.target, workspaceId);
257
+ async #editTargetIdentity(statement, workspaceId, workerId) {
258
+ const target = this.#extractTarget(statement.target, workerId);
240
259
  await this.#canonColumns(target, workspaceId);
241
260
  return {
242
261
  key: JSON.stringify([
@@ -252,8 +271,8 @@ class Dispatcher {
252
271
  async prepareEditBatches(statements, context) {
253
272
  for (const statement of statements)
254
273
  assertClassifyingSignal(statement); // {§log-tag-signal}
255
- const { workspaceId, workerId, loopId, turnId, origin } = context;
256
- const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId, origin });
274
+ const { workspaceId, workerId, functionalityWorkerId, loopId, turnId, origin } = context;
275
+ const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId, loopId, turnId, origin });
257
276
  await this.#resourceMutations.prepareEditBatches(statements, context, schemeCtx);
258
277
  }
259
278
  async dispatch(context) {
@@ -266,12 +285,13 @@ class Dispatcher {
266
285
  }
267
286
  async #dispatchOne(context) {
268
287
  const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, } = context;
269
- const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId, origin });
288
+ const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId, origin });
289
+ const { functionalityWorkerId } = schemeCtx;
270
290
  let result;
271
291
  let curationPlan = null;
272
- let denial = this.#checkWritable(statement, origin, workspaceId);
292
+ let denial = this.#checkWritable(statement, origin, functionalityWorkerId);
273
293
  if (denial === null)
274
- denial = await this.#checkFlagsGate(statement, loopId, workspaceId);
294
+ denial = await this.#checkFlagsGate(statement, loopId, functionalityWorkerId);
275
295
  if (denial !== null) {
276
296
  result = denial;
277
297
  }
@@ -286,7 +306,14 @@ class Dispatcher {
286
306
  result = await this.#resourceMutations.preparedEditResult(statement);
287
307
  }
288
308
  else if (statement.op === "SEND" && statement.target === null) {
289
- result = await this.#handleSendBroadcast(statement, { workspaceId, workerId, loopId, turnId, sequence });
309
+ result = await this.#handleSendBroadcast(statement, {
310
+ workspaceId,
311
+ workerId,
312
+ loopId,
313
+ turnId,
314
+ sequence,
315
+ origin,
316
+ });
290
317
  }
291
318
  else if (statement.op === "OPEN" || statement.op === "FOLD") {
292
319
  const curation = await this.#runLogCuration(statement, schemeCtx);
@@ -309,9 +336,22 @@ class Dispatcher {
309
336
  result = this.#handlePlan(statement);
310
337
  }
311
338
  else if (statement.op === "EXEC") {
312
- // EXEC routes unconditionally to its operation owner. The
313
- // resolved runtime declaration owns body/target semantics.
314
- result = await this.#gatedExec(statement, schemeCtx, loopId, turnId);
339
+ // {§worker-tool-admission} — the question runtime is admitted
340
+ // per worker: a worker whose own rules don't request user input
341
+ // gets the explicit not-available outcome, never a parked loop.
342
+ if (("signal" in statement && typeof statement.signal === "string")
343
+ && !(await WorkerSettingsReader.toolAvailable(this.#db, schemeCtx.functionalityWorkerId, statement.signal))) {
344
+ result = _a.#failure("question-tool-unavailable", 404, "The question tool is not available to this worker.", {}, {
345
+ stage: "tool-admission",
346
+ recovery: "This worker does not request user input; continue from the available evidence.",
347
+ retryable: false,
348
+ });
349
+ }
350
+ else {
351
+ // EXEC routes unconditionally to its operation owner. The
352
+ // resolved runtime declaration owns body/target semantics.
353
+ result = await this.#run("exec", statement, schemeCtx);
354
+ }
315
355
  }
316
356
  else {
317
357
  result = await this.#run(schemeNameOf(statement.target), statement, schemeCtx); // {§op-methods-op-dispatch}
@@ -320,15 +360,18 @@ class Dispatcher {
320
360
  catch (err) { // a scheme exception becomes the op's 500 outcome — {§scheme-surface-exception-500}
321
361
  if (err instanceof InvalidOperationResultError)
322
362
  throw err;
323
- const scheme = schemeNameOf(statement.target);
324
- console.error(`Scheme '${scheme ?? "unknown"}' ${statement.op} threw outside its operation result contract:`, err);
325
- result = err instanceof OperationFailureError
326
- ? err.result
327
- : _a.#failure("scheme-handler-threw", 500, `The '${scheme ?? "unknown"}' scheme did not produce a ${statement.op} result.`, {}, {
363
+ if (err instanceof OperationFailureError) {
364
+ result = err.result;
365
+ }
366
+ else {
367
+ const scheme = schemeNameOf(statement.target);
368
+ console.error(`Scheme '${scheme ?? "unknown"}' ${statement.op} threw outside its operation result contract:`, err);
369
+ result = _a.#failure("scheme-handler-threw", 500, `The '${scheme ?? "unknown"}' scheme did not produce a ${statement.op} result.`, {}, {
328
370
  stage: "scheme-dispatch",
329
371
  scheme,
330
372
  operation: statement.op,
331
373
  });
374
+ }
332
375
  }
333
376
  }
334
377
  // {§fold-open-meta-operations} — persist OPEN/FOLD for forensics;
@@ -340,6 +383,7 @@ class Dispatcher {
340
383
  const logEntryId = await this.#writeLog({
341
384
  statement,
342
385
  result,
386
+ functionalityWorkerId,
343
387
  workspaceId,
344
388
  workerId,
345
389
  loopId,
@@ -349,15 +393,6 @@ class Dispatcher {
349
393
  curationPlan,
350
394
  modelCallId: null,
351
395
  });
352
- // {§search-gate} — register successful searches AFTER #writeLog stamps the runtime
353
- // entry's coordinate onto result.attrs.pathname (the gate's dedup serves from it).
354
- if (statement.op === "EXEC" && result.status < 400) {
355
- const rt = ("signal" in statement && typeof statement.signal === "string" && statement.signal.length > 0) ? statement.signal : "sh";
356
- const body = ("body" in statement && typeof statement.body === "string") ? statement.body : "";
357
- const attrPath = result.attrs?.pathname;
358
- if (typeof attrPath === "string")
359
- this.#searchGate?.registerPending(loopId, turnId, rt, body, attrPath);
360
- }
361
396
  onDispatch?.(logEntryId);
362
397
  // Proposal lifecycle (SPEC.md {§engine-rails} + {§methods-proposal-resolve}; {§proposal-202-pauses}). When a
363
398
  // side-effecting op returns status 202 (a broadcast SEND signal 202 park is model
@@ -379,13 +414,13 @@ class Dispatcher {
379
414
  autoAccept = EffectPolicy.decide(effect) === "auto";
380
415
  }
381
416
  if (autoAccept) {
382
- const initialSettlement = await this.#proposals.workerApply(statement, result, { decision: "accept" }, { workspaceId, workerId, loopId, turnId });
417
+ const initialSettlement = await this.#proposals.workerApply(statement, result, { decision: "accept" }, { workspaceId, workerId, functionalityWorkerId, loopId, turnId });
383
418
  const effective = await this.#resourceMutations.settleProposal({
384
419
  statement,
385
420
  result,
386
421
  settlement: initialSettlement,
387
422
  ctx: schemeCtx,
388
- ids: { workspaceId, workerId, loopId, turnId },
423
+ ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
389
424
  });
390
425
  return this.#proposals.applyResolution(logEntryId, effective);
391
426
  }
@@ -414,13 +449,13 @@ class Dispatcher {
414
449
  // file, spawns the process, etc.). Its operation result is
415
450
  // preserved: an apply failure keeps its original status and
416
451
  // Problem Details instead of masquerading as a client rejection.
417
- const initialSettlement = await this.#proposals.workerApply(statement, result, resolution, { workspaceId, workerId, loopId, turnId });
452
+ const initialSettlement = await this.#proposals.workerApply(statement, result, resolution, { workspaceId, workerId, functionalityWorkerId, loopId, turnId });
418
453
  const effective = await this.#resourceMutations.settleProposal({
419
454
  statement,
420
455
  result,
421
456
  settlement: initialSettlement,
422
457
  ctx: schemeCtx,
423
- ids: { workspaceId, workerId, loopId, turnId },
458
+ ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
424
459
  });
425
460
  const post = await this.#proposals.applyResolution(logEntryId, effective);
426
461
  return post;
@@ -437,8 +472,8 @@ class Dispatcher {
437
472
  if (statement.op !== "READ")
438
473
  throw new Error(`look resolves READ only; got ${statement.op}`);
439
474
  // turnId is a write-time FK only — a look writes no row, so 0 (no turn) is inert.
440
- const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId: 0, origin });
441
- const denial = await this.#checkFlagsGate(statement, loopId, workspaceId);
475
+ const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId: 0, origin });
476
+ const denial = await this.#checkFlagsGate(statement, loopId, schemeCtx.functionalityWorkerId);
442
477
  if (denial !== null)
443
478
  return denial;
444
479
  return this.#run(schemeNameOf(statement.target), statement, schemeCtx);
@@ -448,30 +483,17 @@ class Dispatcher {
448
483
  // schemes may return the already-authorized principal key.
449
484
  async resolveEntryAddress(context) {
450
485
  const { target, workspaceId, workerId } = context;
451
- const routedScheme = schemeNameOf(target);
452
- if (routedScheme === null)
453
- return null;
454
- const handler = this.#schemes.get(routedScheme, workspaceId);
455
- const manifest = this.#schemes.manifestFor(routedScheme, workspaceId);
456
- if (handler === undefined || manifest?.category !== "data")
457
- return null;
458
- const addressedScheme = target.kind === "url" ? target.scheme : routedScheme;
459
486
  const coreCtx = this.#buildSchemeCtx({
460
487
  workspaceId,
461
488
  workerId,
489
+ functionalityWorkerId: context.functionalityWorkerId,
462
490
  loopId: 0,
463
491
  turnId: 0,
464
492
  origin: "client",
465
493
  });
466
- const schemeCtx = new SchemeCtxImpl(coreCtx, addressedScheme, manifest, this.#liveSubscriptions);
467
- const resolved = await this.#resolveDataEntryAddress({
468
- target,
469
- routedScheme,
470
- handler,
471
- manifest,
472
- ctx: coreCtx,
473
- schemeCtx,
474
- });
494
+ const resolved = await this.bindEntryAddress(target, coreCtx);
495
+ if (resolved === null)
496
+ return null;
475
497
  if (resolved.address === null)
476
498
  return null;
477
499
  const rendered = target.kind === "url"
@@ -482,70 +504,30 @@ class Dispatcher {
482
504
  return {
483
505
  ownerId: resolved.address.ownerId,
484
506
  scheme: resolved.address.scheme,
507
+ authority: resolved.address.authority,
485
508
  pathname: resolved.address.pathname,
486
509
  target: rendered,
487
510
  };
488
511
  }
489
- async #resolveDataEntryAddress({ target, routedScheme, handler, manifest, ctx, schemeCtx, }) {
490
- const addressedScheme = target.kind === "url" ? target.scheme : routedScheme;
491
- const identityTarget = target.kind === "url"
492
- ? {
493
- ...target,
494
- pathname: PathSyntax.decodeParens(target.pathname),
495
- fragment: null,
496
- }
497
- : { ...target, raw: PathSyntax.decodeParens(target.raw) };
498
- const resolved = handler.resolveEntryAddress === undefined
499
- ? { pathname: entryPathnameOf(identityTarget), owner: "commons" }
500
- : await handler.resolveEntryAddress(identityTarget, schemeCtx);
501
- if (resolved === null)
502
- return { address: null, result: null };
503
- if ("status" in resolved) {
504
- const result = Results.assert(resolved);
505
- if (result.status < 300) {
506
- throw new InvalidOperationResultError(`Scheme '${routedScheme}' returned a successful operation result instead of an entry address.`);
507
- }
508
- return { address: null, result };
509
- }
510
- if (typeof resolved.pathname !== "string") {
511
- throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry pathname.`);
512
- }
513
- let ownerId;
514
- if ("ownerId" in resolved) {
515
- if (!(handler instanceof CoreSchemeAdapterBase)) {
516
- throw new TypeError(`Scheme '${routedScheme}' returned a core-only entry owner id.`);
517
- }
518
- ownerId = resolved.ownerId;
519
- }
520
- else if (resolved.owner === "worker") {
521
- ownerId = ctx.workerId;
522
- }
523
- else if (resolved.owner === "commons") {
524
- ownerId = await Owner.commonsId(this.#db, ctx.workspaceId);
525
- }
526
- else {
527
- throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry owner.`);
528
- }
529
- if (!Number.isSafeInteger(ownerId) || ownerId < 1) {
530
- throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry owner id.`);
512
+ async bindEntryAddress(target, ctx) {
513
+ const routedScheme = schemeNameOf(target);
514
+ if (routedScheme === null)
515
+ return null;
516
+ const handler = this.#schemes.get(routedScheme, ctx.functionalityWorkerId);
517
+ const manifest = this.#schemes.manifestFor(routedScheme, ctx.functionalityWorkerId);
518
+ if (handler === undefined || manifest?.category !== "data")
519
+ return null;
520
+ return this.#resolveDataEntryAddress({ target, routedScheme, handler, manifest, ctx });
521
+ }
522
+ async #resolveDataEntryAddress({ target, routedScheme, handler, manifest, ctx, }) {
523
+ if (manifest.category !== "data") {
524
+ throw new TypeError(`Scheme '${routedScheme}' is not entry-bearing.`);
531
525
  }
532
- return {
533
- address: {
534
- ownerId,
535
- scheme: manifest.storedScheme ?? addressedScheme,
536
- pathname: resolved.pathname,
537
- },
538
- result: null,
539
- };
526
+ return this.#entryAddresses.resolve({ target, routedScheme, handler, manifest, ctx });
540
527
  }
541
- async #prepareDataRepresentation({ target, routedScheme, handler, manifest, ctx, schemeCtx, publishedChannel, }) {
542
- const resolved = await this.#resolveDataEntryAddress({
543
- target,
544
- routedScheme,
545
- handler,
546
- manifest,
547
- ctx,
548
- schemeCtx,
528
+ async #prepareDataRepresentation({ target, routedScheme, handler, manifest, ctx, publishedChannel, resolved: priorResolution, }) {
529
+ const resolved = priorResolution ?? await this.#resolveDataEntryAddress({
530
+ target, routedScheme, handler, manifest, ctx,
549
531
  });
550
532
  if (resolved.address === null
551
533
  || resolved.result !== null
@@ -561,11 +543,13 @@ class Dispatcher {
561
543
  }
562
544
  : target;
563
545
  const preparationCtx = new SchemeCtxImpl(ctx, target.kind === "url" ? target.scheme : routedScheme, manifest, this.#liveSubscriptions, {
546
+ authority: address.authority,
564
547
  ownerId: address.ownerId,
565
548
  publishedChannel,
566
549
  });
567
550
  const prepared = Results.assertRepresentationPreparation(await handler.prepareRepresentation({
568
551
  target: selectionNeutralTarget,
552
+ authority: address.authority,
569
553
  pathname: address.pathname,
570
554
  }, preparationCtx));
571
555
  return {
@@ -577,7 +561,7 @@ class Dispatcher {
577
561
  // handler and addressed context as an authored READ. {§exec-target-routing}
578
562
  async readExecSource(statement, ctx) {
579
563
  const schemeName = schemeNameOf(statement.target);
580
- const manifest = schemeName === null ? undefined : this.#schemes.manifestFor(schemeName, ctx.workspaceId);
564
+ const manifest = schemeName === null ? undefined : this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
581
565
  if (manifest !== undefined && manifest.category !== "data") {
582
566
  return _a.#failure("exec-source-not-data", 501, `Scheme '${schemeName}' is not a data source for EXEC.`, {}, {
583
567
  scheme: schemeName,
@@ -587,11 +571,16 @@ class Dispatcher {
587
571
  }
588
572
  return Results.assertReadResult(await this.#run(schemeName, statement, ctx));
589
573
  }
574
+ // The one place per-dispatch coordinates are built; a caller that carries no
575
+ // explicit Functionality coordinate acts in its own Worker's
576
+ // ({§actor-boundary-attached-functionality}). Consumers read the built
577
+ // PlurnkSchemeContext and never re-derive.
590
578
  #buildSchemeCtx(ids) {
591
579
  const { workspaceId, workerId, loopId, turnId, origin } = ids;
580
+ const functionalityWorkerId = ids.functionalityWorkerId ?? workerId;
592
581
  return {
593
582
  db: this.#db,
594
- workspaceId, workerId, loopId, turnId,
583
+ workspaceId, workerId, functionalityWorkerId, loopId, turnId,
595
584
  writer: origin,
596
585
  signal: this.#loopSignal(loopId),
597
586
  streamEventNotify: this.#streamEventNotify,
@@ -610,7 +599,8 @@ class Dispatcher {
610
599
  // - SEND broadcast (path=null) has no target scheme; not gated.
611
600
  // - COPY: dst scheme writableBy applies.
612
601
  // - MOVE: both src (delete) and dst (write) schemes' writableBy apply.
613
- #checkWritable(statement, origin, workspaceId) {
602
+ #checkWritable(statement, origin, functionalityWorkerId) {
603
+ const workerId = functionalityWorkerId;
614
604
  if (!MUTATING_OPS.has(statement.op))
615
605
  return null;
616
606
  if (statement.op === "SEND" && statement.target === null)
@@ -618,23 +608,23 @@ class Dispatcher {
618
608
  // EXEC's operation authority always belongs to the exec scheme;
619
609
  // runtime-specific resource authority is gated separately below.
620
610
  if (statement.op === "EXEC") {
621
- return this.#denyIfDisallowed("exec", origin, workspaceId);
611
+ return this.#denyIfDisallowed("exec", origin, workerId);
622
612
  }
623
613
  // Worker control (FORK/WORK → worker://<name>, spawn or fork) is gated by worker://'s writableBy — its
624
614
  // body is a seed prompt, not a dst path, so the entry-COPY dst-parse below doesn't apply.
625
615
  // {§machine-processes}
626
616
  if (this.#isWorkerControl(statement))
627
- return this.#denyIfDisallowed("worker", origin, workspaceId);
617
+ return this.#denyIfDisallowed("worker", origin, workerId);
628
618
  if (statement.op === "COPY" || statement.op === "MOVE") {
629
619
  const dst = statement.body?.target ?? null;
630
620
  const dstScheme = schemeNameOf(dst);
631
- const dstDenial = this.#denyIfDisallowed(dstScheme, origin, workspaceId);
621
+ const dstDenial = this.#denyIfDisallowed(dstScheme, origin, workerId);
632
622
  if (dstDenial !== null)
633
623
  return dstDenial;
634
624
  if (statement.op === "MOVE") {
635
625
  const srcScheme = schemeNameOf(statement.target);
636
626
  if (srcScheme !== dstScheme) {
637
- const srcDenial = this.#denyIfDisallowed(srcScheme, origin, workspaceId);
627
+ const srcDenial = this.#denyIfDisallowed(srcScheme, origin, workerId);
638
628
  if (srcDenial !== null)
639
629
  return srcDenial;
640
630
  }
@@ -642,45 +632,15 @@ class Dispatcher {
642
632
  return null;
643
633
  }
644
634
  const target = schemeNameOf(statement.target);
645
- return this.#denyIfDisallowed(target, origin, workspaceId);
646
- }
647
- // {§search-gate} — gate only configured search runtimes; duplicates serve
648
- // the prior durable digest and the per-turn cap refuses without execution.
649
- async #gatedExec(statement, ctx, loopId, turnId) {
650
- const runtime = ("signal" in statement && typeof statement.signal === "string" && statement.signal.length > 0) ? statement.signal : "sh";
651
- const body = ("body" in statement && typeof statement.body === "string") ? statement.body : "";
652
- const verdict = this.#searchGate?.check(loopId, turnId, runtime, body) ?? { verdict: "pass" };
653
- if (verdict.verdict === "capped") {
654
- return _a.#failure("search-limit-reached", 429, `This turn already used its ${verdict.cap} permitted searches.`, {}, {
655
- searchLimit: verdict.cap,
656
- stage: "search-admission",
657
- recovery: "Continue without another search in this turn.",
658
- retryable: false,
659
- });
660
- }
661
- if (verdict.verdict === "duplicate") {
662
- // {§stream-owner-scoped} — the prior ranked digest is the CALLER's own stream entry.
663
- const prior = await EntryCrud.readEntry(verdict.priorPathname, ctx, runtime, ctx.workerId);
664
- const raw = prior.entry?.channels["#results"]?.content ?? "";
665
- let results = raw;
666
- try {
667
- results = JSON.parse(raw);
668
- }
669
- catch { /* non-JSON results serve verbatim */ }
670
- return _a.#failure("duplicate-search", 409, "This search already ran in the current loop; the prior results are attached.", { results }, {
671
- recovery: "Use the attached prior results.",
672
- retryable: false,
673
- });
674
- }
675
- return this.#run("exec", statement, ctx);
635
+ return this.#denyIfDisallowed(target, origin, workerId);
676
636
  }
677
- #denyIfDisallowed(schemeName, origin, workspaceId) {
637
+ #denyIfDisallowed(schemeName, origin, workerId) {
678
638
  if (schemeName === null)
679
639
  return null;
680
- const handler = this.#schemes.get(schemeName, workspaceId);
640
+ const handler = this.#schemes.get(schemeName, workerId);
681
641
  if (handler === undefined)
682
642
  return null;
683
- const manifest = this.#schemes.manifestFor(schemeName, workspaceId);
643
+ const manifest = this.#schemes.manifestFor(schemeName, workerId);
684
644
  if (manifest === undefined)
685
645
  throw new Error(`registered scheme '${schemeName}' has no manifest`);
686
646
  if (manifest.writableBy.includes(origin))
@@ -698,7 +658,8 @@ class Dispatcher {
698
658
  // active set under the loop's persisted flags. A registered scheme outside
699
659
  // that set returns 403; unknown names continue to their operation owner for
700
660
  // the ordinary registration failure. Action-entry-as-outcome carries either.
701
- async #checkFlagsGate(statement, loopId, workspaceId) {
661
+ async #checkFlagsGate(statement, loopId, functionalityWorkerId) {
662
+ const workerId = functionalityWorkerId;
702
663
  // Broadcast SEND has no scheme to gate.
703
664
  if (statement.op === "SEND" && statement.target === null)
704
665
  return null;
@@ -732,15 +693,15 @@ class Dispatcher {
732
693
  });
733
694
  }
734
695
  }
735
- const active = this.#schemes.resolveForLoop(flags, workspaceId);
736
- // {§tools-loop-affinity}: name the non-retryable restriction so the model changes course.
696
+ const active = this.#schemes.resolveForLoop(flags, workerId);
697
+ // {§mode-ask-read-only}: name the non-retryable restriction so the model changes course.
737
698
  const restriction = flags.mode === "ask"
738
699
  ? "this is an ask-mode (read-only) loop — you cannot run commands or take host actions here"
739
700
  : flags.noWeb && flags.noInteraction ? "web and interaction are disabled for this loop"
740
701
  : flags.noWeb ? "web access is disabled for this loop"
741
702
  : "interaction is disabled for this loop";
742
703
  const checkScheme = (scheme) => {
743
- if (scheme === null || !this.#schemes.has(scheme, workspaceId))
704
+ if (scheme === null || !this.#schemes.has(scheme, workerId))
744
705
  return null;
745
706
  if (active.has(scheme))
746
707
  return null;
@@ -764,7 +725,7 @@ class Dispatcher {
764
725
  return operationDenial;
765
726
  const requested = typeof statement.signal === "string" ? statement.signal : "";
766
727
  const runtime = requested === "" ? "sh" : requested;
767
- const targetKind = this.#executors()?.entry(runtime, workspaceId)?.invocation.target?.kind;
728
+ const targetKind = this.#executors()?.entry(runtime, workerId)?.invocation.target?.kind;
768
729
  if (targetKind !== "resource")
769
730
  return null;
770
731
  const sourceScheme = schemeNameOf(statement.target);
@@ -837,17 +798,32 @@ class Dispatcher {
837
798
  }
838
799
  if (statement.op === "FORK") {
839
800
  // Branch the current worker's log into a named sister.
840
- const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name);
841
- await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: branchWorkerId, prompt, flags, parentLoopId: ctx.loopId });
801
+ const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
802
+ await ctx.injectWorker({
803
+ workspaceId: ctx.workspaceId,
804
+ workerId: branchWorkerId,
805
+ sourceWorkerId: ctx.workerId,
806
+ prompt,
807
+ flags,
808
+ parentLoopId: ctx.loopId,
809
+ });
842
810
  return { status: 200, body: name };
843
811
  }
844
812
  // WORK — a fresh worker sister named <name>.
845
813
  const row = await this.#db.fork_insert_worker.get({
846
814
  workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
815
+ fork_snapshot: 0,
847
816
  });
848
817
  if (row === undefined)
849
818
  throw new Error("worker spawn: worker insert returned no row");
850
- await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: row.id, prompt, flags, parentLoopId: ctx.loopId });
819
+ await ctx.injectWorker({
820
+ workspaceId: ctx.workspaceId,
821
+ workerId: row.id,
822
+ sourceWorkerId: ctx.workerId,
823
+ prompt,
824
+ flags,
825
+ parentLoopId: ctx.loopId,
826
+ });
851
827
  return { status: 200, body: name };
852
828
  }
853
829
  // KILL is target-polymorphic. Scheme handlers own the optional numeric code's
@@ -863,20 +839,40 @@ class Dispatcher {
863
839
  if (schemeName === null) {
864
840
  return _a.#failure("kill-target-scheme-required", 400, "KILL target requires a scheme.", {}, { retryable: false });
865
841
  }
842
+ const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
843
+ const coordinate = entryCoordinateOf(path, manifest?.authority ?? "namespace");
866
844
  // Log targets use the same killable dispatch as streams; erasure is their
867
- // permanent curation operation. {§model-entry-log-curation}
845
+ // permanent curation operation. {§turn-ops-log-curation}
868
846
  // Process-KILL: any scheme whose handler exposes kill() aborts a live stream — the
869
847
  // exec handler, registered as "exec" + under every runtime tag (sh/node), so a tag-
870
848
  // addressed stream (sh:///l/t/s) routes here, not to deleteEntry. {§exec}
871
- const killable = this.#schemes.get(schemeName, ctx.workspaceId);
849
+ const killable = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
872
850
  if (killable !== undefined && typeof killable.kill === "function") {
873
851
  // Pass the model's OWN scheme so a stream-KILL error answers in the runtime tag the
874
852
  // model addressed (sh:///…), not the internal `exec` ({§fs-answer-in-canon}).
875
- const handlerCtx = this.#handlerContext(schemeName, ctx);
853
+ let handlerCtx;
854
+ if (manifest?.category === "data") {
855
+ const resolved = await this.#resolveDataEntryAddress({
856
+ target: path,
857
+ routedScheme: schemeName,
858
+ handler: killable,
859
+ manifest,
860
+ ctx,
861
+ });
862
+ if (resolved.result !== null)
863
+ return resolved.result;
864
+ if (resolved.address === null) {
865
+ return _a.#failure("entry-not-found", 404, `No entry exists at ${renderAddress({ scheme: schemeName, ...coordinate })}.`);
866
+ }
867
+ handlerCtx = this.#boundEntryContext(schemeName, resolved.address, ctx);
868
+ }
869
+ else {
870
+ handlerCtx = await this.#handlerContext(schemeName, ctx, coordinate.authority);
871
+ }
876
872
  if (handlerCtx === null) {
877
873
  throw new InvalidOperationResultError(`Registered scheme '${schemeName}' has no dispatch context.`);
878
874
  }
879
- return await killable.kill(entryPathnameOf(path), statement.signal, handlerCtx, schemeName);
875
+ return await killable.kill(coordinate.pathname, statement.signal, handlerCtx, schemeName);
880
876
  }
881
877
  if (schemeName === "worker") {
882
878
  // Entry-path present → KILL a private owner-held entry (delete it), self-only —
@@ -885,7 +881,22 @@ class Dispatcher {
885
881
  const entryPath = path.kind === "url" ? (path.pathname ?? "") : "";
886
882
  if (entryPath !== "" && entryPath !== "/") {
887
883
  const workerHandler = this.#schemes.get("worker");
888
- const handlerCtx = this.#handlerContext("worker", ctx);
884
+ if (manifest?.category !== "data") {
885
+ throw new InvalidOperationResultError("Registered scheme 'worker' is not entry-bearing.");
886
+ }
887
+ const resolved = await this.#resolveDataEntryAddress({
888
+ target: path,
889
+ routedScheme: "worker",
890
+ handler: workerHandler,
891
+ manifest,
892
+ ctx,
893
+ });
894
+ if (resolved.result !== null)
895
+ return resolved.result;
896
+ if (resolved.address === null) {
897
+ return _a.#failure("entry-not-found", 404, "The worker entry does not exist.");
898
+ }
899
+ const handlerCtx = this.#boundEntryContext("worker", resolved.address, ctx);
889
900
  if (handlerCtx === null) {
890
901
  throw new InvalidOperationResultError("Registered scheme 'worker' has no dispatch context.");
891
902
  }
@@ -912,12 +923,28 @@ class Dispatcher {
912
923
  await this.#cancelWorker(workerId, "killed via worker:// KILL");
913
924
  return { status: 200 };
914
925
  }
915
- if (!this.#schemes.has(schemeName, ctx.workspaceId)) {
926
+ if (!this.#schemes.has(schemeName, ctx.functionalityWorkerId)) {
916
927
  return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
917
928
  }
929
+ const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
930
+ if (handler === undefined || manifest?.category !== "data") {
931
+ return _a.#failure("entry-operation-unsupported", 400, `KILL requires an entry-bearing target; '${schemeName}' does not provide one.`, {}, { scheme: schemeName, retryable: false });
932
+ }
933
+ const resolved = await this.#resolveDataEntryAddress({
934
+ target: path,
935
+ routedScheme: schemeName,
936
+ handler,
937
+ manifest,
938
+ ctx,
939
+ });
940
+ if (resolved.result !== null)
941
+ return resolved.result;
942
+ if (resolved.address === null) {
943
+ return _a.#failure("entry-not-found", 404, "The KILL target does not exist.");
944
+ }
918
945
  // A host-effecting delete (file) returns 202 to PROPOSE — pass its attrs through so the proposal
919
946
  // carries the delete target to review (#isProposal fires on 202). Plurnk-internal deletes execute inline.
920
- return this.#deleteEntry(schemeName, entryPathnameOf(path), ctx);
947
+ return this.#deleteEntry(schemeName, resolved.address, ctx);
921
948
  }
922
949
  async #writeActionlessEntry({ verbatim, workerId, loopId, turnId, sequence, origin, kind, folded, modelCallId = null, attrs = {} }) {
923
950
  const durableAttrs = { ...attrs, kind };
@@ -925,7 +952,7 @@ class Dispatcher {
925
952
  const row = await this.#db.engine_insert_log_entry.get({
926
953
  worker_id: workerId, loop_id: loopId, turn_id: turnId, sequence,
927
954
  origin, source: null, model_call_id: modelCallId,
928
- op: null, suffix: "", signal: null,
955
+ op: null, delimiter: "", signal: null,
929
956
  scheme: null, username: null, password: null, hostname: null, port: null,
930
957
  pathname: null, query: null, fragment: null, lineMarker: null,
931
958
  tx: "", mimetype_tx: "text/vnd.plurnk",
@@ -949,30 +976,29 @@ class Dispatcher {
949
976
  await this.#db.engine_fold_log_entry.run({ id: row.id });
950
977
  return row.id;
951
978
  }
952
- // {§worker-initialization-entry} — the kernel-authored, born-open worked
953
- // turn that initializes a worker's log without impersonating model output.
954
- async writeInitializationEntry({ verbatim, workerId, loopId, turnId, sequence }) {
979
+ // {§turn-ops-entry} — preserve exact admitted source beside, never instead
980
+ // of, the ordinary operation-result rows produced by dispatch.
981
+ async writeTurnOps({ verbatim, workerId, loopId, turnId, sequence, origin, folded, modelCallId = null, reasoningItems }) {
955
982
  return this.#writeActionlessEntry({
956
983
  verbatim, workerId, loopId, turnId, sequence,
957
- origin: "plurnk", kind: "initialization", folded: false,
984
+ origin, kind: "turnOps", folded, modelCallId,
985
+ attrs: {
986
+ ...(reasoningItems !== undefined && reasoningItems.length > 0 ? { reasoning: reasoningItems } : {}),
987
+ },
958
988
  });
959
989
  }
960
- // {§model-entry} — mirror a model emission back as an actionless log row, so
961
- // the model can inspect and curate its own prior behavior. Ordinary admitted
962
- // emissions are folded; the bounded invalid-emission lifeline is the one
963
- // born-open rejected item under {§invalid-emission-attempts} and labels
964
- // that admission fact structurally.
965
- async writeModelEntry({ verbatim, workerId, loopId, turnId, sequence, folded, modelCallId, admission = "accepted", reasoningItems }) {
990
+ // {§rejected-emission-entry} — provider bytes that fail admission are an
991
+ // attempt artifact, never a turn program.
992
+ async writeEmissionAttempt({ verbatim, workerId, loopId, turnId, sequence, modelCallId, reasoningItems }) {
966
993
  return this.#writeActionlessEntry({
967
994
  verbatim, workerId, loopId, turnId, sequence,
968
- origin: "model", kind: "model_emission", folded, modelCallId,
995
+ origin: "model", kind: "emissionAttempt", folded: true, modelCallId,
969
996
  attrs: {
970
- ...(admission === "rejected" ? { admission } : {}),
971
997
  ...(reasoningItems !== undefined && reasoningItems.length > 0 ? { reasoning: reasoningItems } : {}),
972
998
  },
973
999
  });
974
1000
  }
975
- // PLAN — the model's intended-goals op. An ordinary op: dispatched like any
1001
+ // PLAN — the model's complete current Plan. An ordinary op: dispatched like any
976
1002
  // other, logged, and broadcast to the client as a log entry — but a pure no-op for
977
1003
  // state (PLAN ∉ MUTATING_OPS); its body serializes into the log row's tx, no effect.
978
1004
  #handlePlan(statement) {
@@ -987,6 +1013,7 @@ class Dispatcher {
987
1013
  Results.assert(result);
988
1014
  const logEntryId = await this.#writeLog({
989
1015
  ...context,
1016
+ functionalityWorkerId: context.functionalityWorkerId ?? context.workerId,
990
1017
  result,
991
1018
  curationPlan: null,
992
1019
  modelCallId,
@@ -1013,8 +1040,10 @@ class Dispatcher {
1013
1040
  const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
1014
1041
  if (boundaries.retrievals)
1015
1042
  pending.push("this turn's retrieval results (they land in the NEXT packet's Log)");
1016
- if (boundaries.streamTerminations) {
1017
- pending.push("completed stream results that land in the NEXT packet's Log");
1043
+ if (boundaries.streamTerminations.length > 0) {
1044
+ // Name the exact streams (bench#5 requiem #9): the model can wait on
1045
+ // or KILL them specifically instead of guessing what remains.
1046
+ pending.push(`completed stream results that land in the NEXT packet's Log: ${boundaries.streamTerminations.join("; ")}`);
1018
1047
  }
1019
1048
  if (boundaries.childTerminations)
1020
1049
  pending.push("worker results that arrived during this turn (they land NEXT turn)");
@@ -1026,17 +1055,17 @@ class Dispatcher {
1026
1055
  // their distinct contracts: retrievals block explicit completion, whereas FOLD only
1027
1056
  // prevents an empty wait from being inferred as completion.
1028
1057
  async #nextPacketBoundaries(workerId, turnId) {
1029
- const [turnBoundaries, streamTermination, childTermination] = await Promise.all([
1058
+ const [turnBoundaries, streamTerminations, childTermination] = await Promise.all([
1030
1059
  this.#db.engine_turn_packet_boundaries.all({ turn_id: turnId }),
1031
1060
  this.#db.engine_worker_has_undelivered_stream_term
1032
- .get({ worker_id: workerId }),
1061
+ .all({ worker_id: workerId }),
1033
1062
  this.#db.engine_worker_has_undelivered_child_term
1034
1063
  .get({ worker_id: workerId }),
1035
1064
  ]);
1036
1065
  return {
1037
1066
  retrievals: turnBoundaries.some(({ op }) => op !== "FOLD"),
1038
1067
  folds: turnBoundaries.some(({ op }) => op === "FOLD"),
1039
- streamTerminations: streamTermination !== undefined,
1068
+ streamTerminations: streamTerminations.map(({ handle }) => handle),
1040
1069
  childTerminations: childTermination !== undefined,
1041
1070
  };
1042
1071
  }
@@ -1118,7 +1147,7 @@ class Dispatcher {
1118
1147
  // fired, so do not park; continue directly to the packet that
1119
1148
  // materializes them.
1120
1149
  const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
1121
- if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations || boundaries.childTerminations) {
1150
+ if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations.length > 0 || boundaries.childTerminations) {
1122
1151
  return { status: 102 };
1123
1152
  }
1124
1153
  const failCount = await this.#unobservedFailureCount(turnId);
@@ -1135,58 +1164,45 @@ class Dispatcher {
1135
1164
  attrs: { joined: true, pending: 0 },
1136
1165
  };
1137
1166
  }
1138
- // {§send-300-choices} — ask the operator and park (the answer returns via loop.inject).
1139
- // The three-state cascade (owner design): PLURNK_QUESTIONS unset = ALLOWED (not enabled);
1140
- // =0 = DENIED servicewide (a ceiling the client cannot override); ENABLED requires the
1141
- // client to affirmatively pass it per workspace (settings.questions — the interactive client
1142
- // with a human enables its own workspaces; headless/bench never asks). Enabled workspaces ALSO
1143
- // get the questions.md teaching injected (docEntries) — capability and teaching gate as one.
1144
- // Disabled → refused with a self-decide steer, never a park into the void.
1145
- if (status === 300) {
1146
- if (!(await WorkspaceSettings.questionsEnabled(this.#db, ctx.workspaceId))) {
1147
- return _a.#failure("operator-questions-disabled", 409, "Operator questions are disabled in this workspace.", {}, {
1148
- recovery: "Decide from the available evidence and continue or conclude.",
1149
- retryable: false,
1150
- });
1151
- }
1152
- // {§send-300-choices} — return a proposal whose accepted body becomes
1153
- // the answer; zero choices denotes an open question.
1154
- const parts = raw.split(";").map((x) => x.trim()).filter((x) => x.length > 0);
1155
- const [question = "", ...choices] = parts;
1156
- return { status: 202, attrs: choices.length > 0 ? { question, choices } : { question } };
1157
- }
1158
1167
  // [200] — terminate, gated by the pending set (post-batch). The row records the refused
1159
1168
  // attempt faithfully (status_rx=409, never erased); the loop stays a continue; the strike
1160
1169
  // couples in runTurn. [499] abandons and cancels the descendant scope.
1161
1170
  if (status === 200) {
1162
- // {§send-premature-terminate} — same-turn failures are unobserved
1163
- // pending results and therefore refuse completion.
1164
- const failCount = await this.#unobservedFailureCount(turnId);
1165
- if (failCount > 0)
1166
- return _a.#unobservedFailures(failCount);
1167
- const pending = await this.#pendingSet(workerId, turnId);
1168
- if (pending.length > 0) {
1169
- // A retrieval-only refusal needs no KILL/park remedy menu: the results simply
1170
- // arrive in the next packet. Streams and children retain their remedy steer.
1171
- const retrievalsOnly = pending.every((k) => k.startsWith("this turn's retrieval results"));
1172
- if (retrievalsOnly) {
1173
- return _a.#failure("retrieval-results-unobserved", 409, "Last turn both performed retrieval operations and attempted to terminate. Retrieval operations force an additional turn so their results can be reviewed.", {}, {
1171
+ // Model completion is a claim about the Worker's observed work and
1172
+ // therefore crosses the pending-result rails. A `_plurnk`
1173
+ // maintenance program closes only its own administrative loop; it
1174
+ // must not claim, consume, or be blocked by model work elsewhere in
1175
+ // the same Worker.
1176
+ if (ctx.origin === "model") {
1177
+ // {§send-premature-terminate} — same-turn failures are unobserved
1178
+ // pending results and therefore refuse completion.
1179
+ const failCount = await this.#unobservedFailureCount(turnId);
1180
+ if (failCount > 0)
1181
+ return _a.#unobservedFailures(failCount);
1182
+ const pending = await this.#pendingSet(workerId, turnId);
1183
+ if (pending.length > 0) {
1184
+ // A retrieval-only refusal needs no KILL/park remedy menu: the results simply
1185
+ // arrive in the next packet. Streams and children retain their remedy steer.
1186
+ const retrievalsOnly = pending.every((k) => k.startsWith("this turn's retrieval results"));
1187
+ if (retrievalsOnly) {
1188
+ return _a.#failure("retrieval-results-unobserved", 409, "Last turn both performed retrieval operations and attempted to terminate. Retrieval operations force an additional turn so their results can be reviewed.", {}, {
1189
+ pending: [...pending],
1190
+ stage: "completion",
1191
+ recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
1192
+ retryable: false,
1193
+ });
1194
+ }
1195
+ return _a.#failure("work-remains", 409, `Completion was refused while work remains: ${pending.join("; ")}.`, {}, {
1174
1196
  pending: [...pending],
1175
1197
  stage: "completion",
1176
- recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
1198
+ recovery: "Resolve the listed pending work before concluding.",
1177
1199
  retryable: false,
1178
1200
  });
1179
1201
  }
1180
- return _a.#failure("work-remains", 409, `Completion was refused while work remains: ${pending.join("; ")}.`, {}, {
1181
- pending: [...pending],
1182
- stage: "completion",
1183
- recovery: "Resolve the listed pending work before concluding.",
1184
- retryable: false,
1185
- });
1202
+ const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
1203
+ if (branchDenial !== null)
1204
+ return branchDenial;
1186
1205
  }
1187
- const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
1188
- if (branchDenial !== null)
1189
- return branchDenial;
1190
1206
  const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
1191
1207
  return _a.#statusResult(finished !== null ? 200 : await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to conclude it.");
1192
1208
  }
@@ -1230,13 +1246,56 @@ class Dispatcher {
1230
1246
  : {};
1231
1247
  return _a.#failure("target-scheme-required", 400, `${statement.op} requires a target scheme.`, fields, { operation: statement.op, retryable: false });
1232
1248
  }
1233
- if (statement.op === "SEND" && statement.signal === 499 && statement.target?.kind === "url") {
1249
+ const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
1250
+ const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
1251
+ if (handler === undefined) {
1252
+ return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
1253
+ }
1254
+ const methodName = statement.op.toLowerCase();
1255
+ const method = handler[methodName];
1256
+ const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
1257
+ if (manifest === undefined)
1258
+ throw new Error(`scheme '${schemeName}' has no manifest`);
1259
+ const publishedChannel = statement.target?.kind === "url"
1260
+ ? statement.target.fragment ?? manifest.defaultChannel
1261
+ : manifest.defaultChannel;
1262
+ const authoredCoordinate = statement.target === null
1263
+ ? { authority: "", pathname: "" }
1264
+ : entryCoordinateOf(statement.target, manifest.authority ?? "namespace");
1265
+ // EXEC's authored target belongs to its declared invocation contract;
1266
+ // a resource target is input to the executor, never the output-stream
1267
+ // address owned by the internal exec scheme. {§exec-target-routing}
1268
+ const addressResolution = manifest.category === "data"
1269
+ && statement.target !== null
1270
+ && statement.op !== "EXEC"
1271
+ ? await this.#resolveDataEntryAddress({
1272
+ target: statement.target,
1273
+ routedScheme: schemeName,
1274
+ handler: handler,
1275
+ manifest,
1276
+ ctx,
1277
+ })
1278
+ : null;
1279
+ if (addressResolution?.result !== null && addressResolution?.result !== undefined) {
1280
+ return addressResolution.result;
1281
+ }
1282
+ const operationAddress = addressResolution?.address ?? null;
1283
+ const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, {
1284
+ authority: operationAddress?.authority ?? authoredCoordinate.authority,
1285
+ ownerId: operationAddress?.ownerId ?? await this.#fixedEntryOwnerId(manifest, ctx),
1286
+ publishedChannel,
1287
+ });
1288
+ if (statement.op === "SEND"
1289
+ && statement.signal === 499
1290
+ && statement.target?.kind === "url"
1291
+ && operationAddress !== null) {
1234
1292
  const addressedScheme = statement.target.scheme;
1235
1293
  const entry = await this.#db.crud_find_workspace_entry.get({
1236
1294
  workspace_id: ctx.workspaceId,
1237
- owner_id: await Owner.commonsId(this.#db, ctx.workspaceId),
1238
- scheme: addressedScheme,
1239
- pathname: entryPathnameOf(statement.target),
1295
+ owner_id: operationAddress.ownerId,
1296
+ scheme: operationAddress.scheme,
1297
+ authority: operationAddress.authority,
1298
+ pathname: operationAddress.pathname,
1240
1299
  });
1241
1300
  if (entry !== undefined) {
1242
1301
  const subscription = await ChannelWrite.findActiveSubscription(this.#db, {
@@ -1252,20 +1311,6 @@ class Dispatcher {
1252
1311
  }
1253
1312
  }
1254
1313
  }
1255
- const handler = this.#schemes.get(schemeName, ctx.workspaceId);
1256
- if (handler === undefined) {
1257
- return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
1258
- }
1259
- const methodName = statement.op.toLowerCase();
1260
- const method = handler[methodName];
1261
- const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
1262
- const manifest = this.#schemes.manifestFor(schemeName, ctx.workspaceId);
1263
- if (manifest === undefined)
1264
- throw new Error(`scheme '${schemeName}' has no manifest`);
1265
- const publishedChannel = statement.target?.kind === "url"
1266
- ? statement.target.fragment ?? manifest.defaultChannel
1267
- : manifest.defaultChannel;
1268
- const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, { publishedChannel });
1269
1314
  if (statement.op === "READ"
1270
1315
  && handler instanceof CoreSchemeAdapterBase
1271
1316
  && typeof handler.resolveCoreRepresentation === "function") {
@@ -1307,8 +1352,8 @@ class Dispatcher {
1307
1352
  handler: handler,
1308
1353
  manifest,
1309
1354
  ctx,
1310
- schemeCtx,
1311
1355
  publishedChannel,
1356
+ ...(addressResolution === null ? {} : { resolved: addressResolution }),
1312
1357
  });
1313
1358
  if (prepared.result !== null)
1314
1359
  return prepared.result;
@@ -1321,9 +1366,10 @@ class Dispatcher {
1321
1366
  }
1322
1367
  const storageScheme = resolved?.scheme ?? manifest.storedScheme ?? addressedScheme ?? schemeName;
1323
1368
  const projected = Results.assertReadResult(await EntryOps.readWorkspaceEntry(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, resolved === null
1324
- ? {}
1369
+ ? null
1325
1370
  : {
1326
1371
  ownerId: resolved.ownerId,
1372
+ authority: resolved.authority,
1327
1373
  pathname: resolved.pathname,
1328
1374
  }));
1329
1375
  return projected;
@@ -1345,7 +1391,7 @@ class Dispatcher {
1345
1391
  && (targetPathname === "" || targetPathname.endsWith("/"));
1346
1392
  const exactTarget = statement.target !== null
1347
1393
  && !collectionTarget
1348
- && !PathSyntax.hasGlob(entryPathnameOf(statement.target));
1394
+ && !PathSyntax.hasGlob(authoredCoordinate.pathname);
1349
1395
  if (exactTarget && statement.target !== null) {
1350
1396
  const prepared = await this.#prepareDataRepresentation({
1351
1397
  target: statement.target,
@@ -1353,8 +1399,8 @@ class Dispatcher {
1353
1399
  handler: handler,
1354
1400
  manifest,
1355
1401
  ctx,
1356
- schemeCtx,
1357
1402
  publishedChannel,
1403
+ ...(addressResolution === null ? {} : { resolved: addressResolution }),
1358
1404
  });
1359
1405
  if (prepared.result !== null)
1360
1406
  return prepared.result;
@@ -1378,6 +1424,7 @@ class Dispatcher {
1378
1424
  ?? schemeName;
1379
1425
  return Results.assert(await EntryFind.findWorkspaceEntries(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, {
1380
1426
  ownerId: prepared.address.ownerId,
1427
+ authority: prepared.address.authority,
1381
1428
  pathname: prepared.address.pathname,
1382
1429
  }));
1383
1430
  }
@@ -1409,23 +1456,20 @@ class Dispatcher {
1409
1456
  if (handler === undefined || manifest === undefined) {
1410
1457
  throw new Error("the core log curation owner is not registered");
1411
1458
  }
1412
- const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions);
1459
+ const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions, { ownerId: null });
1413
1460
  const outcome = await handler.curate(statement, schemeCtx);
1414
1461
  return { result: Results.assert(outcome.result), plan: outcome.plan };
1415
1462
  }
1416
1463
  // {§proposal}/{§send} — status 202 is a proposal except for broadcast
1417
1464
  // SEND signal 202, which parks the loop. The operation disambiguates the status.
1418
1465
  static #isProposal(statement, result) {
1419
- // {§send-300-choices} — SEND signal 300 is the proposal exception.
1420
1466
  if (result.status !== 202)
1421
1467
  return false;
1422
- if (statement.op === "SEND" && statement.signal === 300)
1423
- return true;
1424
1468
  return !(statement.op === "SEND" && statement.target === null);
1425
1469
  }
1426
- async #writeLog({ statement, result, workspaceId, workerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
1470
+ async #writeLog({ statement, result, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
1427
1471
  const durableStatement = DurableStatement.project(statement);
1428
- const target = this.#extractTarget(durableStatement.target, workspaceId);
1472
+ const target = this.#extractTarget(durableStatement.target, functionalityWorkerId);
1429
1473
  await this.#canonColumns(target, workspaceId); // {§fs-answer-in-canon}
1430
1474
  const lineMarkerJson = "lineMarker" in durableStatement && durableStatement.lineMarker !== null
1431
1475
  ? JSON.stringify(durableStatement.lineMarker)
@@ -1444,8 +1488,7 @@ class Dispatcher {
1444
1488
  throw new Error("Dispatcher.#writeLog: result attrs collide with private log curation state");
1445
1489
  }
1446
1490
  attrsObj.__plurnk_curation = {
1447
- ids: curationPlan.targetLogEntryIds,
1448
- expanded: curationPlan.expanded,
1491
+ targets: curationPlan.targets,
1449
1492
  add: curationPlan.add,
1450
1493
  remove: curationPlan.remove,
1451
1494
  };
@@ -1502,7 +1545,7 @@ class Dispatcher {
1502
1545
  source: null, // dispatch entries are self-authored; {§env-delta} deltas set this
1503
1546
  model_call_id: modelCallId,
1504
1547
  op: durableStatement.op,
1505
- suffix: durableStatement.suffix,
1548
+ delimiter: durableStatement.delimiter,
1506
1549
  signal: this.#signalToJson(durableStatement.signal),
1507
1550
  scheme: target.scheme,
1508
1551
  username: target.username,
@@ -1538,23 +1581,26 @@ class Dispatcher {
1538
1581
  // inputs collapse to scheme=null in log target metadata because both render
1539
1582
  // as bare paths. Addressable file entries separately persist under the
1540
1583
  // reserved `file` identity scheme ({§entry-identity-no-null}).
1541
- #extractTarget(path, workspaceId) {
1584
+ #extractTarget(path, workerId) {
1542
1585
  if (path === null)
1543
1586
  return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: null, query: null, fragment: null };
1544
1587
  // `local` (bare path) carries no URL parts — store the raw text as the pathname for the log record, scheme=null.
1545
1588
  if (path.kind === "local")
1546
1589
  return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: PathSyntax.decodeParens(path.raw), query: null, fragment: null }; // {§path-parentheses}
1547
1590
  const scheme = path.scheme === "file" ? null : path.scheme;
1548
- // {§scheme-address-namespace-fold} — a registered non-network scheme folds its
1549
- // namespace authority into the canonical pathname. Network authorities remain hosts;
1550
- // worker:// is the registered exception because its authority selects the owner.
1591
+ // The registered scheme owns authority disposition. Namespace authority
1592
+ // is path syntax; resource and owner authorities remain explicit in the
1593
+ // durable operation evidence.
1594
+ const routedScheme = schemeNameOf(path);
1595
+ const manifest = routedScheme === null
1596
+ ? undefined
1597
+ : this.#schemes.manifestFor(routedScheme, workerId);
1551
1598
  const foldNs = scheme !== null
1552
- && scheme !== "worker"
1553
- && !NetworkAddress.supports(scheme)
1554
- && this.#schemes.has(scheme, workspaceId);
1599
+ && manifest !== undefined
1600
+ && (manifest.authority ?? "namespace") === "namespace";
1555
1601
  return {
1556
1602
  scheme, username: path.username, password: path.password,
1557
- hostname: foldNs ? null : path.hostname, port: path.port,
1603
+ hostname: foldNs ? null : path.hostname, port: foldNs ? null : path.port,
1558
1604
  pathname: PathSyntax.decodeParens(foldNs ? foldAuthorityIntoPath(path.hostname, path.pathname) : path.pathname), // {§path-parentheses}
1559
1605
  query: path.query, fragment: path.fragment,
1560
1606
  };