@plurnk/plurnk-service 1.3.12 → 1.4.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 (469) hide show
  1. package/.env.defaults +84 -77
  2. package/INSTALL.md +50 -25
  3. package/README.md +24 -10
  4. package/SPEC.md +2311 -928
  5. package/dist/Paths.d.ts +4 -1
  6. package/dist/Paths.d.ts.map +1 -1
  7. package/dist/Paths.js +23 -21
  8. package/dist/Paths.js.map +1 -1
  9. package/dist/build-info.json +1 -1
  10. package/dist/content/edit-receipt.d.ts +13 -28
  11. package/dist/content/edit-receipt.d.ts.map +1 -1
  12. package/dist/content/edit-receipt.js +285 -104
  13. package/dist/content/edit-receipt.js.map +1 -1
  14. package/dist/content/edited-span.d.ts.map +1 -1
  15. package/dist/content/edited-span.js +2 -5
  16. package/dist/content/edited-span.js.map +1 -1
  17. package/dist/content/index.d.ts +3 -3
  18. package/dist/content/index.d.ts.map +1 -1
  19. package/dist/content/index.js +1 -1
  20. package/dist/content/index.js.map +1 -1
  21. package/dist/content/line-marker.d.ts +6 -9
  22. package/dist/content/line-marker.d.ts.map +1 -1
  23. package/dist/content/line-marker.js +7 -11
  24. package/dist/content/line-marker.js.map +1 -1
  25. package/dist/content/matcher.d.ts +14 -25
  26. package/dist/content/matcher.d.ts.map +1 -1
  27. package/dist/content/matcher.js +77 -107
  28. package/dist/content/matcher.js.map +1 -1
  29. package/dist/content/mimetype-binary.d.ts +2 -2
  30. package/dist/content/mimetype-binary.d.ts.map +1 -1
  31. package/dist/content/mimetype-binary.js +10 -10
  32. package/dist/content/mimetype-binary.js.map +1 -1
  33. package/dist/content/path-mimetype.js +3 -3
  34. package/dist/content/path-mimetype.js.map +1 -1
  35. package/dist/content/read-resolve.d.ts +8 -4
  36. package/dist/content/read-resolve.d.ts.map +1 -1
  37. package/dist/content/read-resolve.js +101 -49
  38. package/dist/content/read-resolve.js.map +1 -1
  39. package/dist/core/BranchReceipt.d.ts +7 -0
  40. package/dist/core/BranchReceipt.d.ts.map +1 -0
  41. package/dist/core/BranchReceipt.js +27 -0
  42. package/dist/core/BranchReceipt.js.map +1 -0
  43. package/dist/core/BudgetOverflow.d.ts +23 -0
  44. package/dist/core/BudgetOverflow.d.ts.map +1 -0
  45. package/dist/core/BudgetOverflow.js +61 -0
  46. package/dist/core/BudgetOverflow.js.map +1 -0
  47. package/dist/core/BudgetReadout.d.ts +8 -0
  48. package/dist/core/BudgetReadout.d.ts.map +1 -0
  49. package/dist/core/BudgetReadout.js +79 -0
  50. package/dist/core/BudgetReadout.js.map +1 -0
  51. package/dist/core/ChannelWrite.d.ts +28 -9
  52. package/dist/core/ChannelWrite.d.ts.map +1 -1
  53. package/dist/core/ChannelWrite.js +28 -21
  54. package/dist/core/ChannelWrite.js.map +1 -1
  55. package/dist/core/ChannelWrite.sql +11 -10
  56. package/dist/core/CoreSchemeServices.d.ts +10 -4
  57. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  58. package/dist/core/CoreSchemeServices.js +7 -1
  59. package/dist/core/CoreSchemeServices.js.map +1 -1
  60. package/dist/core/Dispatcher.d.ts +29 -20
  61. package/dist/core/Dispatcher.d.ts.map +1 -1
  62. package/dist/core/Dispatcher.js +1488 -599
  63. package/dist/core/Dispatcher.js.map +1 -1
  64. package/dist/core/DurableStatement.d.ts +6 -0
  65. package/dist/core/DurableStatement.d.ts.map +1 -0
  66. package/dist/core/DurableStatement.js +51 -0
  67. package/dist/core/DurableStatement.js.map +1 -0
  68. package/dist/core/Engine.d.ts +48 -20
  69. package/dist/core/Engine.d.ts.map +1 -1
  70. package/dist/core/Engine.js +1208 -789
  71. package/dist/core/Engine.js.map +1 -1
  72. package/dist/core/Engine.sql +309 -217
  73. package/dist/core/EnvFlags.js +1 -1
  74. package/dist/core/EnvFlags.js.map +1 -1
  75. package/dist/core/ErrorDetail.d.ts +6 -0
  76. package/dist/core/ErrorDetail.d.ts.map +1 -0
  77. package/dist/core/ErrorDetail.js +20 -0
  78. package/dist/core/ErrorDetail.js.map +1 -0
  79. package/dist/core/ExecutorRegistry.d.ts +14 -3
  80. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  81. package/dist/core/ExecutorRegistry.js +56 -30
  82. package/dist/core/ExecutorRegistry.js.map +1 -1
  83. package/dist/core/GitBranch.d.ts +20 -0
  84. package/dist/core/GitBranch.d.ts.map +1 -0
  85. package/dist/core/GitBranch.js +110 -0
  86. package/dist/core/GitBranch.js.map +1 -0
  87. package/dist/core/JournalTurn.d.ts +9 -0
  88. package/dist/core/JournalTurn.d.ts.map +1 -0
  89. package/dist/core/JournalTurn.js +14 -0
  90. package/dist/core/JournalTurn.js.map +1 -0
  91. package/dist/core/JournalTurn.sql +10 -0
  92. package/dist/core/LogBody.d.ts +17 -0
  93. package/dist/core/LogBody.d.ts.map +1 -0
  94. package/dist/core/LogBody.js +148 -0
  95. package/dist/core/LogBody.js.map +1 -0
  96. package/dist/core/LoopFlagsReader.d.ts +7 -0
  97. package/dist/core/LoopFlagsReader.d.ts.map +1 -0
  98. package/dist/core/LoopFlagsReader.js +33 -0
  99. package/dist/core/LoopFlagsReader.js.map +1 -0
  100. package/dist/core/LoopLifecycle.d.ts +8 -1
  101. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  102. package/dist/core/LoopLifecycle.js +55 -9
  103. package/dist/core/LoopLifecycle.js.map +1 -1
  104. package/dist/core/LoopLifecycle.sql +9 -3
  105. package/dist/core/NoticeChannel.d.ts +13 -0
  106. package/dist/core/NoticeChannel.d.ts.map +1 -0
  107. package/dist/core/NoticeChannel.js +40 -0
  108. package/dist/core/NoticeChannel.js.map +1 -0
  109. package/dist/core/Owner.d.ts +0 -1
  110. package/dist/core/Owner.d.ts.map +1 -1
  111. package/dist/core/Owner.js +2 -5
  112. package/dist/core/Owner.js.map +1 -1
  113. package/dist/core/PacketBuilder.d.ts +25 -24
  114. package/dist/core/PacketBuilder.d.ts.map +1 -1
  115. package/dist/core/PacketBuilder.js +313 -375
  116. package/dist/core/PacketBuilder.js.map +1 -1
  117. package/dist/core/ProblemLog.d.ts +26 -0
  118. package/dist/core/ProblemLog.d.ts.map +1 -0
  119. package/dist/core/ProblemLog.js +58 -0
  120. package/dist/core/ProblemLog.js.map +1 -0
  121. package/dist/core/ProposalLifecycle.d.ts +15 -21
  122. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  123. package/dist/core/ProposalLifecycle.js +291 -78
  124. package/dist/core/ProposalLifecycle.js.map +1 -1
  125. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  126. package/dist/core/ProviderInstantiate.js +51 -48
  127. package/dist/core/ProviderInstantiate.js.map +1 -1
  128. package/dist/core/SchemeRegistry.d.ts +14 -8
  129. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  130. package/dist/core/SchemeRegistry.js +189 -114
  131. package/dist/core/SchemeRegistry.js.map +1 -1
  132. package/dist/core/ServiceTeardown.d.ts +11 -0
  133. package/dist/core/ServiceTeardown.d.ts.map +1 -0
  134. package/dist/core/ServiceTeardown.js +70 -0
  135. package/dist/core/ServiceTeardown.js.map +1 -0
  136. package/dist/core/StoredPacket.d.ts +29 -0
  137. package/dist/core/StoredPacket.d.ts.map +1 -0
  138. package/dist/core/StoredPacket.js +147 -0
  139. package/dist/core/StoredPacket.js.map +1 -0
  140. package/dist/core/StrikeRail.d.ts +1 -2
  141. package/dist/core/StrikeRail.d.ts.map +1 -1
  142. package/dist/core/StrikeRail.js +25 -55
  143. package/dist/core/StrikeRail.js.map +1 -1
  144. package/dist/core/WorkerControlAddress.d.ts +17 -0
  145. package/dist/core/WorkerControlAddress.d.ts.map +1 -0
  146. package/dist/core/WorkerControlAddress.js +44 -0
  147. package/dist/core/WorkerControlAddress.js.map +1 -0
  148. package/dist/core/WorkerName.d.ts +31 -0
  149. package/dist/core/WorkerName.d.ts.map +1 -0
  150. package/dist/core/WorkerName.js +95 -0
  151. package/dist/core/WorkerName.js.map +1 -0
  152. package/dist/core/WorkerName.sql +36 -0
  153. package/dist/core/WorkspaceGate.d.ts +14 -0
  154. package/dist/core/WorkspaceGate.d.ts.map +1 -0
  155. package/dist/core/WorkspaceGate.js +155 -0
  156. package/dist/core/WorkspaceGate.js.map +1 -0
  157. package/dist/core/caps/CapsResolve.d.ts +1 -1
  158. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  159. package/dist/core/caps/CapsResolve.js.map +1 -1
  160. package/dist/core/caps/DbChannelCaps.d.ts +5 -11
  161. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  162. package/dist/core/caps/DbChannelCaps.js +24 -8
  163. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  164. package/dist/core/caps/DbEntryCaps.d.ts +4 -13
  165. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  166. package/dist/core/caps/DbEntryCaps.js +15 -6
  167. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  168. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  169. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  170. package/dist/core/caps/DbNotifyCaps.js +12 -11
  171. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  172. package/dist/core/caps/DbProjectionCaps.d.ts +5 -5
  173. package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
  174. package/dist/core/caps/DbProjectionCaps.js +21 -4
  175. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  176. package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -4
  177. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  178. package/dist/core/caps/DbSubscriptionCaps.js +57 -62
  179. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  180. package/dist/core/caps/DbTagCaps.d.ts +5 -12
  181. package/dist/core/caps/DbTagCaps.d.ts.map +1 -1
  182. package/dist/core/caps/DbTagCaps.js +14 -7
  183. package/dist/core/caps/DbTagCaps.js.map +1 -1
  184. package/dist/core/content-hash.js +1 -1
  185. package/dist/core/content-hash.js.map +1 -1
  186. package/dist/core/env-defaults.d.ts.map +1 -1
  187. package/dist/core/env-defaults.js.map +1 -1
  188. package/dist/core/fork.d.ts.map +1 -1
  189. package/dist/core/fork.js +55 -30
  190. package/dist/core/fork.js.map +1 -1
  191. package/dist/core/fork.sql +41 -27
  192. package/dist/core/git-env.d.ts +2 -0
  193. package/dist/core/git-env.d.ts.map +1 -1
  194. package/dist/core/git-env.js +13 -18
  195. package/dist/core/git-env.js.map +1 -1
  196. package/dist/core/git-iso.d.ts.map +1 -1
  197. package/dist/core/git-iso.js +9 -15
  198. package/dist/core/git-iso.js.map +1 -1
  199. package/dist/core/git-membership.d.ts +1 -1
  200. package/dist/core/git-membership.d.ts.map +1 -1
  201. package/dist/core/git-membership.js +198 -146
  202. package/dist/core/git-membership.js.map +1 -1
  203. package/dist/core/git-state.d.ts.map +1 -1
  204. package/dist/core/git-state.js +10 -11
  205. package/dist/core/git-state.js.map +1 -1
  206. package/dist/core/owner.sql +1 -1
  207. package/dist/core/packet-inject.d.ts.map +1 -1
  208. package/dist/core/packet-inject.js +7 -11
  209. package/dist/core/packet-inject.js.map +1 -1
  210. package/dist/core/packet-wire.d.ts +2 -20
  211. package/dist/core/packet-wire.d.ts.map +1 -1
  212. package/dist/core/packet-wire.js +242 -368
  213. package/dist/core/packet-wire.js.map +1 -1
  214. package/dist/core/plurnk-uri.d.ts +12 -1
  215. package/dist/core/plurnk-uri.d.ts.map +1 -1
  216. package/dist/core/plurnk-uri.js +49 -29
  217. package/dist/core/plurnk-uri.js.map +1 -1
  218. package/dist/core/results.d.ts +16 -5
  219. package/dist/core/results.d.ts.map +1 -1
  220. package/dist/core/results.js +27 -18
  221. package/dist/core/results.js.map +1 -1
  222. package/dist/core/ruler_count.d.ts +4 -0
  223. package/dist/core/ruler_count.d.ts.map +1 -0
  224. package/dist/core/ruler_count.js +7 -0
  225. package/dist/core/ruler_count.js.map +1 -0
  226. package/dist/core/scheme-types.d.ts +9 -11
  227. package/dist/core/scheme-types.d.ts.map +1 -1
  228. package/dist/core/scheme-types.js.map +1 -1
  229. package/dist/core/search-gate.d.ts.map +1 -1
  230. package/dist/core/search-gate.js +3 -11
  231. package/dist/core/search-gate.js.map +1 -1
  232. package/dist/core/teaching-corpus.d.ts +6 -0
  233. package/dist/core/teaching-corpus.d.ts.map +1 -0
  234. package/dist/core/teaching-corpus.js +23 -0
  235. package/dist/core/teaching-corpus.js.map +1 -0
  236. package/dist/core/teaching.d.ts +0 -1
  237. package/dist/core/teaching.d.ts.map +1 -1
  238. package/dist/core/teaching.js +2 -8
  239. package/dist/core/teaching.js.map +1 -1
  240. package/dist/core/token-ruler.d.ts.map +1 -1
  241. package/dist/core/token-ruler.js +3 -16
  242. package/dist/core/token-ruler.js.map +1 -1
  243. package/dist/core/turn-scheduler.d.ts +1 -1
  244. package/dist/core/turn-scheduler.d.ts.map +1 -1
  245. package/dist/core/turn-scheduler.js +1 -1
  246. package/dist/core/turn-scheduler.js.map +1 -1
  247. package/dist/core/worker-cap.d.ts +2 -4
  248. package/dist/core/worker-cap.d.ts.map +1 -1
  249. package/dist/core/worker-cap.js +13 -4
  250. package/dist/core/worker-cap.js.map +1 -1
  251. package/dist/core/{run-ops.sql → worker-ops.sql} +11 -11
  252. package/dist/core/workspace-settings.d.ts.map +1 -1
  253. package/dist/core/workspace-settings.js +17 -9
  254. package/dist/core/workspace-settings.js.map +1 -1
  255. package/dist/digest/Digest.d.ts +2 -1
  256. package/dist/digest/Digest.d.ts.map +1 -1
  257. package/dist/digest/Digest.js +337 -138
  258. package/dist/digest/Digest.js.map +1 -1
  259. package/dist/digest/digest.sql +19 -10
  260. package/dist/index.d.ts.map +1 -1
  261. package/dist/index.js +3 -5
  262. package/dist/index.js.map +1 -1
  263. package/dist/matrix/live-specimen.d.ts +9 -0
  264. package/dist/matrix/live-specimen.d.ts.map +1 -0
  265. package/dist/matrix/live-specimen.js +37 -0
  266. package/dist/matrix/live-specimen.js.map +1 -0
  267. package/dist/observe/api.d.ts +6 -0
  268. package/dist/observe/api.d.ts.map +1 -0
  269. package/dist/observe/api.js +11 -0
  270. package/dist/observe/api.js.map +1 -0
  271. package/dist/observe/init.d.ts +7 -0
  272. package/dist/observe/init.d.ts.map +1 -0
  273. package/dist/observe/init.js +159 -0
  274. package/dist/observe/init.js.map +1 -0
  275. package/dist/observe/metrics.d.ts +6 -0
  276. package/dist/observe/metrics.d.ts.map +1 -0
  277. package/dist/observe/metrics.js +15 -0
  278. package/dist/observe/metrics.js.map +1 -0
  279. package/dist/observe/spans.d.ts +4 -0
  280. package/dist/observe/spans.d.ts.map +1 -0
  281. package/dist/observe/spans.js +51 -0
  282. package/dist/observe/spans.js.map +1 -0
  283. package/dist/schemes/EffectPolicy.d.ts +1 -0
  284. package/dist/schemes/EffectPolicy.d.ts.map +1 -1
  285. package/dist/schemes/EffectPolicy.js +5 -2
  286. package/dist/schemes/EffectPolicy.js.map +1 -1
  287. package/dist/schemes/Exec.d.ts +6 -11
  288. package/dist/schemes/Exec.d.ts.map +1 -1
  289. package/dist/schemes/Exec.js +364 -247
  290. package/dist/schemes/Exec.js.map +1 -1
  291. package/dist/schemes/ExecOutputScheme.d.ts +8 -7
  292. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  293. package/dist/schemes/ExecOutputScheme.js +48 -9
  294. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  295. package/dist/schemes/File.d.ts +7 -11
  296. package/dist/schemes/File.d.ts.map +1 -1
  297. package/dist/schemes/File.js +279 -142
  298. package/dist/schemes/File.js.map +1 -1
  299. package/dist/schemes/Log.d.ts +4 -8
  300. package/dist/schemes/Log.d.ts.map +1 -1
  301. package/dist/schemes/Log.js +475 -208
  302. package/dist/schemes/Log.js.map +1 -1
  303. package/dist/schemes/Log.sql +62 -30
  304. package/dist/schemes/Prompt.d.ts +3 -2
  305. package/dist/schemes/Prompt.d.ts.map +1 -1
  306. package/dist/schemes/Prompt.js +9 -6
  307. package/dist/schemes/Prompt.js.map +1 -1
  308. package/dist/schemes/Skill.d.ts +1 -1
  309. package/dist/schemes/Skill.d.ts.map +1 -1
  310. package/dist/schemes/Skill.js +2 -3
  311. package/dist/schemes/Skill.js.map +1 -1
  312. package/dist/schemes/Worker.d.ts +8 -16
  313. package/dist/schemes/Worker.d.ts.map +1 -1
  314. package/dist/schemes/Worker.js +179 -69
  315. package/dist/schemes/Worker.js.map +1 -1
  316. package/dist/schemes/_entry-chunk.d.ts.map +1 -1
  317. package/dist/schemes/_entry-chunk.js +12 -5
  318. package/dist/schemes/_entry-chunk.js.map +1 -1
  319. package/dist/schemes/_entry-crud.d.ts +10 -9
  320. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  321. package/dist/schemes/_entry-crud.js +59 -13
  322. package/dist/schemes/_entry-crud.js.map +1 -1
  323. package/dist/schemes/_entry-crud.sql +20 -8
  324. package/dist/schemes/_entry-find.d.ts +20 -15
  325. package/dist/schemes/_entry-find.d.ts.map +1 -1
  326. package/dist/schemes/_entry-find.js +339 -174
  327. package/dist/schemes/_entry-find.js.map +1 -1
  328. package/dist/schemes/_entry-find.sql +20 -8
  329. package/dist/schemes/_entry-graph.d.ts +4 -2
  330. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  331. package/dist/schemes/_entry-graph.js +55 -31
  332. package/dist/schemes/_entry-graph.js.map +1 -1
  333. package/dist/schemes/_entry-graph.sql +37 -22
  334. package/dist/schemes/_entry-manifest.d.ts +10 -15
  335. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  336. package/dist/schemes/_entry-manifest.js +50 -268
  337. package/dist/schemes/_entry-manifest.js.map +1 -1
  338. package/dist/schemes/_entry-ops.d.ts +9 -14
  339. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  340. package/dist/schemes/_entry-ops.js +149 -77
  341. package/dist/schemes/_entry-ops.js.map +1 -1
  342. package/dist/schemes/_entry-ops.sql +1 -1
  343. package/dist/schemes/_entry-semantic.d.ts +23 -8
  344. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  345. package/dist/schemes/_entry-semantic.js +141 -60
  346. package/dist/schemes/_entry-semantic.js.map +1 -1
  347. package/dist/schemes/_entry-semantic.sql +63 -45
  348. package/dist/schemes/_entry-send.d.ts +3 -4
  349. package/dist/schemes/_entry-send.d.ts.map +1 -1
  350. package/dist/schemes/_entry-send.js +48 -23
  351. package/dist/schemes/_entry-send.js.map +1 -1
  352. package/dist/schemes/_path-scope.d.ts +23 -0
  353. package/dist/schemes/_path-scope.d.ts.map +1 -0
  354. package/dist/schemes/_path-scope.js +61 -0
  355. package/dist/schemes/_path-scope.js.map +1 -0
  356. package/dist/schemes/_search-candidate.d.ts +20 -0
  357. package/dist/schemes/_search-candidate.d.ts.map +1 -0
  358. package/dist/schemes/_search-candidate.js +19 -0
  359. package/dist/schemes/_search-candidate.js.map +1 -0
  360. package/dist/schemes/_search-exclusion.d.ts +7 -0
  361. package/dist/schemes/_search-exclusion.d.ts.map +1 -0
  362. package/dist/schemes/_search-exclusion.js +23 -0
  363. package/dist/schemes/_search-exclusion.js.map +1 -0
  364. package/dist/schemes/_search-index.d.ts +6 -0
  365. package/dist/schemes/_search-index.d.ts.map +1 -0
  366. package/dist/schemes/_search-index.js +326 -0
  367. package/dist/schemes/_search-index.js.map +1 -0
  368. package/dist/schemes/cosine.js +1 -1
  369. package/dist/schemes/cosine.js.map +1 -1
  370. package/dist/schemes/exec-abort.js +3 -3
  371. package/dist/schemes/exec-abort.js.map +1 -1
  372. package/dist/schemes/exec-env.js +3 -3
  373. package/dist/schemes/exec-env.js.map +1 -1
  374. package/dist/server/BranchBatches.d.ts +48 -0
  375. package/dist/server/BranchBatches.d.ts.map +1 -0
  376. package/dist/server/BranchBatches.js +591 -0
  377. package/dist/server/BranchBatches.js.map +1 -0
  378. package/dist/server/Daemon.d.ts +36 -66
  379. package/dist/server/Daemon.d.ts.map +1 -1
  380. package/dist/server/Daemon.js +920 -446
  381. package/dist/server/Daemon.js.map +1 -1
  382. package/dist/server/DaemonModule.d.ts +31 -0
  383. package/dist/server/DaemonModule.d.ts.map +1 -0
  384. package/dist/server/DaemonModule.js +2 -0
  385. package/dist/server/DaemonModule.js.map +1 -0
  386. package/dist/server/branch-batch.sql +152 -0
  387. package/dist/server/client-input.d.ts +12 -0
  388. package/dist/server/client-input.d.ts.map +1 -1
  389. package/dist/server/client-input.js +262 -57
  390. package/dist/server/client-input.js.map +1 -1
  391. package/dist/server/dispatch-as-plurnk.d.ts +1 -1
  392. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  393. package/dist/server/dispatch-as-plurnk.js +15 -7
  394. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  395. package/dist/server/drain.sql +92 -62
  396. package/dist/server/envelope.d.ts +2 -5
  397. package/dist/server/envelope.d.ts.map +1 -1
  398. package/dist/server/envelope.js +119 -95
  399. package/dist/server/envelope.js.map +1 -1
  400. package/dist/server/envelope.sql +19 -23
  401. package/dist/server/exec-poll-backoff.js +1 -1
  402. package/dist/server/exec-poll-backoff.js.map +1 -1
  403. package/dist/server/lifecycle-recovery.sql +79 -2
  404. package/dist/server/logEntry.d.ts +3 -1
  405. package/dist/server/logEntry.d.ts.map +1 -1
  406. package/dist/server/logEntry.js +4 -2
  407. package/dist/server/logEntry.js.map +1 -1
  408. package/dist/server/logEntry.sql +1 -1
  409. package/dist/server/loop-model.d.ts.map +1 -1
  410. package/dist/server/loop-model.js +22 -9
  411. package/dist/server/loop-model.js.map +1 -1
  412. package/dist/server/loopDocs.d.ts.map +1 -1
  413. package/dist/server/loopDocs.js +5 -4
  414. package/dist/server/loopDocs.js.map +1 -1
  415. package/dist/server/seam-entry-read.sql +15 -10
  416. package/dist/server/seam-log-read.sql +2 -2
  417. package/dist/server/seam-loop.sql +1 -1
  418. package/dist/server/seam-proposal-list.sql +17 -7
  419. package/dist/service.d.ts.map +1 -1
  420. package/dist/service.js +66 -60
  421. package/dist/service.js.map +1 -1
  422. package/migrations/001_schema.sql +897 -0
  423. package/package.json +57 -26
  424. package/dist/core/PluginLoader.d.ts +0 -20
  425. package/dist/core/PluginLoader.d.ts.map +0 -1
  426. package/dist/core/PluginLoader.js +0 -142
  427. package/dist/core/PluginLoader.js.map +0 -1
  428. package/dist/core/TelemetryChannel.d.ts +0 -37
  429. package/dist/core/TelemetryChannel.d.ts.map +0 -1
  430. package/dist/core/TelemetryChannel.js +0 -80
  431. package/dist/core/TelemetryChannel.js.map +0 -1
  432. package/dist/core/path-decode.d.ts +0 -3
  433. package/dist/core/path-decode.d.ts.map +0 -1
  434. package/dist/core/path-decode.js +0 -9
  435. package/dist/core/path-decode.js.map +0 -1
  436. package/dist/core/plugin-attribution.d.ts +0 -5
  437. package/dist/core/plugin-attribution.d.ts.map +0 -1
  438. package/dist/core/plugin-attribution.js +0 -39
  439. package/dist/core/plugin-attribution.js.map +0 -1
  440. package/dist/core/world-state.d.ts +0 -10
  441. package/dist/core/world-state.d.ts.map +0 -1
  442. package/dist/core/world-state.js +0 -35
  443. package/dist/core/world-state.js.map +0 -1
  444. package/dist/core/world-state.sql +0 -35
  445. package/dist/core/zero-pin.d.ts +0 -3
  446. package/dist/core/zero-pin.d.ts.map +0 -1
  447. package/dist/core/zero-pin.js +0 -16
  448. package/dist/core/zero-pin.js.map +0 -1
  449. package/dist/server/auto.d.ts +0 -6
  450. package/dist/server/auto.d.ts.map +0 -1
  451. package/dist/server/auto.js +0 -63
  452. package/dist/server/auto.js.map +0 -1
  453. package/dist/server/clientTurn.d.ts +0 -6
  454. package/dist/server/clientTurn.d.ts.map +0 -1
  455. package/dist/server/clientTurn.js +0 -22
  456. package/dist/server/clientTurn.js.map +0 -1
  457. package/dist/server/clientTurn.sql +0 -10
  458. package/dist/server/noProposals.d.ts +0 -6
  459. package/dist/server/noProposals.d.ts.map +0 -1
  460. package/dist/server/noProposals.js +0 -37
  461. package/dist/server/noProposals.js.map +0 -1
  462. package/dist/server/version-info.d.ts +0 -14
  463. package/dist/server/version-info.d.ts.map +0 -1
  464. package/dist/server/version-info.js +0 -69
  465. package/dist/server/version-info.js.map +0 -1
  466. package/migrations/0000-00-00.01_schema.sql +0 -536
  467. package/migrations/0004_loop-provider.sql +0 -3
  468. package/migrations/0005_subscription-published-channel.sql +0 -3
  469. package/migrations/0006_loop-max-turns.sql +0 -3
@@ -4,24 +4,89 @@ import { rulerCount } from "./token-ruler.js";
4
4
  import { docsExcludeSet } from "./teaching.js";
5
5
  import { Policy } from "@plurnk/plurnk-execs";
6
6
  import WorkspaceSettings from "./workspace-settings.js";
7
- import { DEFAULT_LOOP_FLAGS } from "./scheme-types.js";
7
+ import LoopFlagsReader from "./LoopFlagsReader.js";
8
8
  import { readPacketInject, readSystemPolicy, readProjectPolicy } from "./packet-inject.js";
9
9
  import { readFile } from "node:fs/promises";
10
- import { resolve as resolvePath } from "node:path";
11
10
  import Paths from "../Paths.js";
11
+ import { readTeachingSource } from "./teaching-corpus.js";
12
12
  // Shared module imported by both Engine and bin/digest.ts, so wire
13
13
  // projection and digest projection are structurally one function — no
14
14
  // drift between wire and digest possible.
15
15
  import PacketWire from "./packet-wire.js";
16
- import { scopeEnvToAlias, resolveActiveAlias } from "@plurnk/plurnk-providers";
16
+ import { assertPromptTokenMeasurement, scopeEnvToAlias, resolveActiveAlias } from "@plurnk/plurnk-providers";
17
17
  import ProviderInstantiate from "./ProviderInstantiate.js";
18
- // Substituted into the budget readout after the assembled packet is measured
19
- // (the figure depends on the packet's own rendered size — chicken/egg).
20
- const TOKENS_FREE_PLACEHOLDER = "{{tokensFree}}";
21
- const TOKEN_USAGE_PLACEHOLDER = "{{tokenUsage}}";
22
- const TOKEN_PERCENT_PLACEHOLDER = "{{tokenPercent}}";
23
- const SYSTEM_CTX_PLACEHOLDER = "{{systemCtx}}"; // #440 — treemap's non-turn overhead = total − Σturns, known only post-assembly
24
- // §tokenomics-window-partition — the four partition numbers. REQUIRED (fail-hard, the
18
+ import BudgetOverflow from "./BudgetOverflow.js";
19
+ import BudgetReadout from "./BudgetReadout.js";
20
+ const trimHorizontal = (value) => value.replace(/^[\t ]+|[\t ]+$/gu, "");
21
+ const tableCells = (line) => {
22
+ if (!line.startsWith("|") || !line.endsWith("|"))
23
+ return null;
24
+ const cells = [];
25
+ let start = 1;
26
+ for (let index = 1; index < line.length - 1; index += 1) {
27
+ if (line[index] !== "|")
28
+ continue;
29
+ let escapes = 0;
30
+ for (let previous = index - 1; previous >= start && line[previous] === "\\"; previous -= 1)
31
+ escapes += 1;
32
+ if (escapes % 2 === 1)
33
+ continue;
34
+ cells.push(line.slice(start, index));
35
+ start = index + 1;
36
+ }
37
+ cells.push(line.slice(start, -1));
38
+ return cells;
39
+ };
40
+ const isTableDivider = (cells) => cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/u.test(trimHorizontal(cell)));
41
+ // {§definition-table-projection} — plurnk.md remains spacious for human editing;
42
+ // only its well-formed Markdown tables lose authoring alignment on the packet wire.
43
+ const compactDefinitionTables = (markdown) => {
44
+ const lines = markdown.split("\n");
45
+ let fence = null;
46
+ let inTable = false;
47
+ let dividerIndex = -1;
48
+ return lines.map((rawLine, index) => {
49
+ const carriageReturn = rawLine.endsWith("\r") ? "\r" : "";
50
+ const line = carriageReturn.length > 0 ? rawLine.slice(0, -1) : rawLine;
51
+ const fenceRun = line.match(/^ {0,3}(`{3,}|~{3,})/u)?.[1];
52
+ if (fence !== null) {
53
+ const closingRun = line.match(/^ {0,3}(`+|~+)[\t ]*$/u)?.[1];
54
+ if (closingRun?.[0] === fence.marker && closingRun.length >= fence.length)
55
+ fence = null;
56
+ return rawLine;
57
+ }
58
+ if (fenceRun !== undefined) {
59
+ fence = { marker: fenceRun[0], length: fenceRun.length };
60
+ inTable = false;
61
+ dividerIndex = -1;
62
+ return rawLine;
63
+ }
64
+ const cells = tableCells(line);
65
+ if (!inTable) {
66
+ const nextRawLine = lines[index + 1] ?? "";
67
+ const nextLine = nextRawLine.endsWith("\r") ? nextRawLine.slice(0, -1) : nextRawLine;
68
+ const dividerCells = tableCells(nextLine);
69
+ if (cells === null || dividerCells === null || cells.length !== dividerCells.length || !isTableDivider(dividerCells))
70
+ return rawLine;
71
+ inTable = true;
72
+ dividerIndex = index + 1;
73
+ }
74
+ else if (cells === null) {
75
+ inTable = false;
76
+ dividerIndex = -1;
77
+ return rawLine;
78
+ }
79
+ if (index === dividerIndex) {
80
+ const divider = cells.map((cell) => {
81
+ const value = trimHorizontal(cell);
82
+ return `${value.startsWith(":") ? ":" : ""}---${value.endsWith(":") ? ":" : ""}`;
83
+ });
84
+ return `|${divider.join("|")}|${carriageReturn}`;
85
+ }
86
+ return `| ${cells.map(trimHorizontal).join(" | ")} |${carriageReturn}`;
87
+ }).join("\n");
88
+ };
89
+ // {§tokenomics-window-partition} — the four partition numbers. REQUIRED (fail-hard, the
25
90
  // providers-env convention): the ceiling is DERIVED from these, never set directly
26
91
  // (PLURNK_BUDGET_CEILING is retired — a settable ceiling let policy contradict physics).
27
92
  const readPartitionIntFrom = (env, name, min) => {
@@ -31,40 +96,47 @@ const readPartitionIntFrom = (env, name, min) => {
31
96
  throw new Error(`${name} must be an integer >= ${min}; got ${raw}`);
32
97
  return n;
33
98
  };
34
- // Packet assembly (SPEC §packet-assembly) + the budget grinder (§grinder):
99
+ const readOptionalPositiveIntFrom = (env, name) => {
100
+ const raw = env[name];
101
+ if (raw === undefined || raw.length === 0)
102
+ return null;
103
+ const n = Number(raw);
104
+ if (!Number.isInteger(n) || n <= 0)
105
+ throw new Error(`${name} must be a positive integer; got ${raw}`);
106
+ return n;
107
+ };
108
+ // Packet assembly (SPEC {§packet-assembly}) + the budget grinder ({§grinder}):
35
109
  // builds the spec'd request packet, measures it, and reclaims window on overflow.
36
110
  class PacketBuilder {
37
111
  #db;
38
112
  #schemes;
39
- #telemetry;
113
+ #problems;
40
114
  // Boot-discovered runtime executors, late-injected on Engine after daemon
41
115
  // start() — read through a thunk so the post-construction set is visible.
42
116
  #executors;
43
- // §tokenomics-window-partition — the partition is PER-ALIAS (#352), resolved per provider in
117
+ // {§tokenomics-window-partition} — the partition is PER-ALIAS, resolved per provider in
44
118
  // #partitionFor and cached by alias; no boot-time global read.
45
- constructor({ db, schemes, telemetry, executors }) {
119
+ constructor({ db, schemes, problems, executors }) {
46
120
  this.#db = db;
47
121
  this.#schemes = schemes;
48
- this.#telemetry = telemetry;
122
+ this.#problems = problems;
49
123
  this.#executors = executors;
50
- // #507 — the envelope rides the provider; construction only runs the retired-knob shed
124
+ // {§tokenomics-window-partition} — the envelope rides the provider; construction only runs the retired-knob shed
51
125
  // so a stale operator .env fails at BOOT, not first use.
52
- this.#safetyFor(resolveActiveAlias(process.env)?.alias ?? "");
126
+ const bootAlias = resolveActiveAlias(process.env)?.alias ?? "";
127
+ this.#safetyFor(bootAlias);
128
+ this.#promptBudgetCapFor(bootAlias);
53
129
  }
54
- // #352 — the generation envelope is PER-ALIAS: gemma keeps its measured llama-server policy
55
- // envelope (n_predict honored to the context wall — the cap MUST bound it, providers#10);
56
- // cloud aliases get a generous default and the backend self-clamps to its true output limit.
130
+ // {§tokenomics-window-partition} — provider generation settings and core packet policy both resolve per alias.
57
131
  // scopeEnvToAlias resolves PLURNK_SERVICE_*_<alias> over the bare fallback with providers' own
58
132
  // battle-tested suffix parser. Cached per alias; the boot-global case falls back to the active
59
133
  // alias when a provider carries no side-table entry (a test Mock).
60
- // #507 (owner-ruled full migration): the generation envelope is PROVIDER-owned — the window
61
- // and both reserves ride the Provider surface (contextWindow/reasoningReserve/completionReserve,
62
- // ingested or PLURNK_PROVIDERS_*-pinned in the provider tier). Core keeps ONE knob: SAFETY,
63
- // the ruler's own packing margin — a service fact, not a model fact.
64
- static #KNOBS = ["PLURNK_SERVICE_SAFETY"];
134
+ // Provider context and response reserves remain provider-owned. Core owns only its virtual
135
+ // prompt budget and the ruler's packing-safety margin.
136
+ static #KNOBS = ["PLURNK_SERVICE_PROMPT_BUDGET", "PLURNK_SERVICE_SAFETY"];
65
137
  #shedChecked = false;
66
138
  #safetyFor(alias) {
67
- // #507 hard shed (the #472 pattern): the three misprefixed partition knobs moved to the
139
+ // {§tokenomics-window-partition} hard shed: the three misprefixed partition knobs moved to the
68
140
  // provider tier; a stale operator .env must never silently lose its envelope to the move.
69
141
  if (!this.#shedChecked) {
70
142
  this.#shedChecked = true;
@@ -76,108 +148,84 @@ class PacketBuilder {
76
148
  for (const k of Object.keys(process.env)) {
77
149
  const m = /^PLURNK_SERVICE_(CTX|CONTEXT_WINDOW|REASONING|ASSISTANT|COMPLETION)(_.*)?$/.exec(k);
78
150
  if (m !== null)
79
- throw new Error(`${k} is retired (#507): the envelope is provider-owned — the knob is ${MOVED[m[1]]}${m[2] ?? ""}.`);
151
+ throw new Error(`${k} is retired: the envelope is provider-owned — the knob is ${MOVED[m[1]]}${m[2] ?? ""}.`);
80
152
  }
81
153
  }
82
154
  const view = scopeEnvToAlias(process.env, alias, _a.#KNOBS);
83
155
  return readPartitionIntFrom(view, "PLURNK_SERVICE_SAFETY", 0);
84
156
  }
85
- // #421 — §tokenomics-window-unpollable-deliberate: provider.contextWindow null (env/probe/catalog
86
- // all missed, provider-tier pins included) is genuinely-unknown — nobody chose an envelope. The
87
- // budget/ceiling short-circuit to NO-CAP rather than substitute a stand-in the operator never chose.
88
- #isUnboundedWindow(provider) {
89
- return provider.contextWindow === null;
157
+ #promptBudgetCapFor(alias) {
158
+ const view = scopeEnvToAlias(process.env, alias, _a.#KNOBS);
159
+ return readOptionalPositiveIntFrom(view, "PLURNK_SERVICE_PROMPT_BUDGET");
90
160
  }
91
161
  #partitionFor(provider) {
92
162
  const alias = ProviderInstantiate.aliasOf(provider) ?? resolveActiveAlias(process.env)?.alias ?? "";
93
163
  return { reasoning: provider.reasoningReserve ?? null, completion: provider.completionReserve ?? null, safety: this.#safetyFor(alias) };
94
164
  }
95
- // #528 — the operator's CONTEXT_WINDOW pin is the LOG-budget cap, core-composed: the provider
96
- // reports its natural window (construction strips the pin), and the cap tightens only the
97
- // prompt — never the reserves, which stay task-natural. Alias-scoped else bare; garbage fails hard.
98
- #capFor(provider) {
99
- const alias = ProviderInstantiate.aliasOf(provider) ?? resolveActiveAlias(process.env)?.alias ?? "";
100
- const raw = scopeEnvToAlias(process.env, alias).PLURNK_PROVIDERS_CONTEXT_WINDOW;
101
- if (raw === undefined || raw.length === 0)
102
- return null;
103
- const cap = Number.parseInt(raw, 10);
104
- if (!Number.isInteger(cap) || cap <= 0)
105
- throw new Error(`PLURNK_PROVIDERS_CONTEXT_WINDOW must be a positive integer, got '${raw}'`);
106
- return cap;
107
- }
108
165
  // The generation envelope — REASONING + COMPLETION, one undifferentiated pool, passed on
109
- // every generate({maxTokens}): no decode is unbounded (§tokenomics-window-partition). Per
110
- // alias (#352): gemma's measured envelope; a cloud alias's generous default the backend clamps.
166
+ // every generate({maxTokens}): no decode is unbounded ({§tokenomics-window-partition}). Per
167
+ // alias: gemma's measured envelope; a cloud alias's generous default the backend clamps.
111
168
  maxTokensFor(provider) {
112
169
  const { reasoning, completion } = this.#partitionFor(provider);
113
170
  if (reasoning === null || completion === null)
114
- return null; // #421 — unknown envelope, no cap; the backend clamps
171
+ return null; // {§tokenomics-window-unpollable-deliberate}: unknown envelope, no cap; the backend clamps
115
172
  return reasoning + completion;
116
173
  }
117
- // §tokenomics-window-partition — the prompt ceiling
118
- // is DERIVED, never set: effectiveWindow = min(CONTEXT_WINDOW, provider window; CONTEXT_WINDOW alone when the
119
- // provider reports none) minus the reserves, divided by the loop's observed real/measured
120
- // token ratio (usage.prompt is ground truth; a heuristic ruler shipped a 65k-real packet into
121
- // a 49k window, #311). A fractional ceiling also budgeted the prompt against the window and
122
- // FORGOT the response lives there too. Reserves exceeding the window is a configuration
123
- // contradiction — fail hard. ratio floors at 1: an overcounting ruler never expands the budget.
124
- // #274/#312 follow-up (owner): the CLIENT-facing gauge denominator — the prompt budget the
125
- // packet actually lives under (effective window minus the partition reserves), in REAL token
126
- // space (usage.prompt, the numerator, is real; the calibration ratio maps measured→real and
127
- // has no business here). The raw n_ctx overstates usable room by the reserve total.
174
+ // {§tokenomics-window-partition} — the natural prompt ceiling is the provider's
175
+ // effective context window minus its response reserves and the service's ruler
176
+ // safety. An optional service prompt budget may only tighten that result. It is
177
+ // model-facing grinder policy, never provider physics or a generation setting.
178
+ // The client-facing gauge, grinder ceiling, and persisted promptBudget use this one value.
128
179
  promptBudgetFor(provider) {
129
- return this.ceilingFor(provider); // ONE derivation (#528) — the gauge denominator IS the grinder ceiling
180
+ return this.ceilingFor(provider); // ONE derivation — the gauge denominator IS the grinder ceiling
130
181
  }
131
- // §tokenomics-agnostic-ruler — the ceiling is the real window partition (window − reserves),
182
+ // {§tokenomics-agnostic-ruler} — the ceiling is the real window partition (window − reserves),
132
183
  // NO calibration ratio: the model-facing measure is the chars/2 ruler (an over-count for
133
184
  // typical text), so comparing ruler-weight to the real-token ceiling is itself the conservative
134
- // bias — the model curates against less room than it has and never overflows for typical
135
- // content; the exact provider count guards the pathological tail at the materialization gate.
185
+ // bias - the packet reports less room than the provider usually has; authoritative
186
+ // provider request evidence guards the pathological tail at recovery admission.
136
187
  ceilingFor(provider) {
137
- if (this.#isUnboundedWindow(provider) || provider.contextWindow === null)
138
- return null; // #421 — no cap: the gauge headline is omitted
188
+ const alias = ProviderInstantiate.aliasOf(provider) ?? resolveActiveAlias(process.env)?.alias ?? "";
189
+ const operatorCap = this.#promptBudgetCapFor(alias);
190
+ // Unknown provider physics stays unknown; an explicit virtual ceiling still bounds
191
+ // PLURNK's packet without pretending to describe the backend.
192
+ if (provider.contextWindow === null)
193
+ return operatorCap;
139
194
  const { reasoning, completion, safety } = this.#partitionFor(provider);
140
195
  if (reasoning === null || completion === null)
141
- return null; // #421 — unknown reserves, no ceiling
196
+ return operatorCap;
142
197
  const naturalBudget = provider.contextWindow - reasoning - completion - safety;
143
198
  if (naturalBudget <= 0) {
144
- const alias = ProviderInstantiate.aliasOf(provider) ?? resolveActiveAlias(process.env)?.alias ?? "";
145
- // #507 — post-migration this contradiction has ONE cause: pinned absolute reserves
199
+ // {§tokenomics-window-partition} — this contradiction has one cause: pinned absolute reserves
146
200
  // exceeding the window the provider detected (percent reserves derive and cannot contradict).
147
201
  throw new Error(`window partition contradiction for alias '${alias}': window ${provider.contextWindow} <= reserves ${reasoning}+${completion}+${safety}. Pinned PLURNK_PROVIDERS_{REASONING,COMPLETION}_RESERVE absolutes exceed the detected window — repin them under it, or use percent reserves, which derive from the window.`);
148
202
  }
149
- // #528 — the operator's cap tightens the PROMPT alone (reserves above are off the natural
150
- // window). A cap tighter than the reserves is deliberate tightening, not a contradiction —
151
- // the budget floors at 1 (0 and 1 force the same every-packet-413; the usage record needs
152
- // a positive denominator). ONE derivation: the grinder ceiling IS the gauge denominator.
153
- const cap = this.#capFor(provider);
154
- if (cap === null)
155
- return naturalBudget;
156
- return Math.max(1, Math.min(cap, provider.contextWindow) - reasoning - completion - safety);
203
+ return operatorCap === null ? naturalBudget : Math.min(operatorCap, naturalBudget);
157
204
  }
158
- // Assemble the request half of the spec'd packet (Packet.json system
159
- // and §user) BEFORE the provider call. The same packet object is then
160
- // completed with assistant + assistantRaw after the model responds, so
161
- // the stored packet and the wire payload share one source of truth.
162
- async buildRequestPacket({ initialMessages, requirements, workspaceId, workerId, loopId, currentTurnSeq, provider, gitStatus, telemetryErrors: presetTelemetry, }) {
205
+ // {§packet-stored-shape} — assemble the system/user request before the
206
+ // provider call; complete the same record with the provider response.
207
+ async buildRequestPacket({ initialMessages, requirements, workspaceId, workerId, loopId, currentTurnSeq, provider, gitStatus, notices = [], }) {
163
208
  const byRole = (role) => initialMessages.filter((m) => m.role === role).map((m) => m.content).join("\n\n");
164
209
  // plurnk.md (grammar/dialects) ONLY — the definition is the hot-path grammar.
165
- // The scheme catalogue is its own `schemes` section below tools (§schemes-directory),
166
- // NOT appended here: grammar 0.49+ is scheme-agnostic, so the service advertises
167
- // the scheme set at packet-time (grammar#239 item 7) via SchemeRegistry.teach().
168
- const system_definition = byRole("system");
169
- // the prompt section sources from the loop's most recent prompt entry first
170
- // (plurnk://prompt/<run>/<loop>/<N> for the highest N written to date).
171
- // This is what inject + the turn-1 foist write into. Falls back to
172
- // the runLoop caller's messages.user for tests that bypass the
173
- // foist mechanism entirely.
210
+ // The scheme catalogue is its own `schemes` section below tools ({§schemes-directory}),
211
+ // NOT appended here: the language teaching is scheme-agnostic, so the service advertises
212
+ // the installed scheme set at packet-time via SchemeRegistry.teach().
213
+ const system_definition = compactDefinitionTables(byRole("system"));
214
+ // The prompt section sources the loop's prompt:///<loop>/<N> entries.
215
+ // Inject and turn-1 initialization write them. Bare callers that
216
+ // bypass prompt persistence fall back to messages.user.
174
217
  const loopSeqRow = await this.#db.engine_loop_sequence.get({ loop_id: loopId });
175
- const promptRows = (await this.#db.drain_get_all_prompt_bodies_for_loop.all({ owner_id: workerId, pattern: `${promptLoopPrefix(loopSeqRow?.sequence ?? loopId)}%` }))
218
+ const promptPrefix = promptLoopPrefix(loopSeqRow?.sequence ?? loopId);
219
+ const promptRows = (await this.#db.drain_get_all_prompt_bodies_for_loop.all({
220
+ owner_id: workerId,
221
+ pattern: `${promptPrefix}%`,
222
+ prefix_len: promptPrefix.length,
223
+ }))
176
224
  .filter((r) => typeof r.content === "string" && r.content.length > 0);
177
- // §prompt-auto-read (owner): the section is a PATHS list (the errors shape — no bodies);
178
- // each prompt's content reaches the model through its foisted auto-READ in the log, and
179
- // prior prompts stay READable by the listed address — never silently lost, never an
180
- // unfair curation imposition. Fallback: callers that bypass the foist (bare messages)
225
+ // The section is a paths list (the errors shape - no bodies);
226
+ // each prompt's content reaches the model through its actionless prompt row, and
227
+ // prior prompts stay READable by the listed address - never silently lost, never an
228
+ // unfair curation imposition. Fallback: callers that bypass persistence (bare messages)
181
229
  // still get their user text rendered directly.
182
230
  const prompt = promptRows.length > 0
183
231
  ? promptRows.map((r) => `* prompt://${r.pathname}`).join("\n")
@@ -186,44 +234,29 @@ class PacketBuilder {
186
234
  // exactly how it went missing (callers read the sysprompt but never the
187
235
  // requirements). Read Paths.defaultRequirements (PLURNK_SERVICE_REQUIREMENTS env →
188
236
  // requirements.md) fresh each build so edits take effect; a non-empty param wins.
189
- const baseRequirements = requirements.length > 0 ? requirements : await readFile(Paths.defaultRequirements, "utf8");
190
- // No injected syntax line: the grammar already headlines the system definition (§Syntax) and
191
- // leads requirements.md, so a third copy here was pure duplication in the model's packet. PLAN
192
- // is mandated unconditionally by plurnk.md §Imperatives (grammar 0.70 requires every turn to
193
- // lead with PLAN), so the service injects no separate plan directive either.
237
+ const baseRequirements = requirements.length > 0
238
+ ? requirements
239
+ : Paths.defaultRequirementsTeachingSource === null
240
+ ? await readFile(Paths.defaultRequirements, "utf8")
241
+ : await readTeachingSource(Paths.defaultRequirementsTeachingSource);
242
+ // {§emission-admission}: the definition owns PLAN framing, so the packet
243
+ // injects no duplicate syntax or plan directive.
194
244
  const log = await this.#buildLog(workerId);
195
- const telemetryErrors = presetTelemetry ?? await this.buildTelemetryErrors(loopId, currentTurnSeq);
196
- const countTokens = rulerCount; // §tokenomics-agnostic-ruler — the ONE model-facing ruler (chars/2), not the provider
197
- // #367 — the capability sheet must reflect the LOOP MODE, not just workspace-enablement: an
198
- // ask-mode loop advertises only what its dispatch gate (resolveForLoop) will accept, so the
199
- // model is never taught a tag it'll then be 403'd on (the taught→emitted→rejected→508 spiral).
200
- const activeSchemes = this.#schemes.resolveForLoop(await this.#loadLoopFlags(loopId));
245
+ const failures = await this.buildFailurePointers(loopId, currentTurnSeq);
246
+ const countTokens = rulerCount; // {§tokenomics-agnostic-ruler} — the ONE model-facing ruler (chars/2), not the provider
247
+ // {§tools-loop-affinity}: teaching and dispatch resolve the same loop flags.
248
+ const activeSchemes = this.#schemes.resolveForLoop(await LoopFlagsReader.read(this.#db, loopId));
201
249
  const tools = this.#collectTools(await this.#workspaceEnabled(workspaceId), await WorkspaceSettings.questionsEnabled(this.#db, workspaceId), activeSchemes);
202
- // Budget readout (SPEC.md §tokenomics). Two-pass: render the budget from
203
- // the structured log's subtotals with a {{tokensFree}} placeholder, build
204
- // the section list, measure the assembled total, resolve free, substitute.
205
- // Subtotals come from the real log render — meta and fences included — not
206
- // a serialized approximation. ceiling is the provider's window ×
207
- // PLURNK_BUDGET_CEILING (null when no window is reported → headline
208
- // omitted, section lines still shown). §tokenomics-render-weight-budget
209
250
  const ceiling = this.ceilingFor(provider);
210
- const logBudget = PacketWire.measureLogBudget(log, countTokens);
211
- const budgetReadout = this.#renderBudget(logBudget, ceiling);
212
- // The default packet: an ordered list of addressable sections (§packet-assembly).
213
- // `slot` is the MESSAGE boundary — and therefore the cache breakpoint every serving
214
- // stack keys on. The wire is monotone in volatility ({§packet-cache-monotone}, #531):
215
- // system = the timeless (definition/tools/schemes/policy — byte-stable doctrine), user
216
- // = the situated (append-mostly log → per-turn status clump → recap), so the prefix
217
- // cache survives through the log's frozen head instead of dying at the first gauge
218
- // digit. Trust is the one-way ADMISSION rule, not the slot: system admits only
219
- // framework-authored, non-injectable content; engine-authored status may ride user;
220
- // attacker-reachable text may never ride system. The budget section carries its
221
- // {{tokensFree}} placeholders here; they resolve below once the assembled total is known.
222
- const inject = await readPacketInject(); // #240 — operator section, per-turn, fail-hard on a broken path
251
+ const budgetReadout = BudgetReadout.draft(ceiling);
252
+ // The canonical default order, trust boundary, and cache-locality bias are
253
+ // specified at {§packet-cache-monotone}. Budget placeholders resolve only
254
+ // after trusted whole-list transforms establish the packet being measured.
255
+ const inject = await readPacketInject(); // {§packet-inject} — per-turn; a broken configured path fails hard
223
256
  const workspaceRoot = (await this.#db.envelope_get_workspace.get({ id: workspaceId }))?.project_root ?? null;
224
257
  const systemPolicy = await readSystemPolicy(); // ~/.plurnk/AGENTS.md (or PLURNK_SERVICE_POLICY)
225
258
  const projectPolicy = await readProjectPolicy(workspaceRoot); // <projectRoot>/AGENTS.md (or PLURNK_SERVICE_PROJECT)
226
- // Child-orientation (§child-orientation): the live things THIS run holds — open streams +
259
+ // Child-orientation ({§child-orientation}): the live things this worker holds — open streams +
227
260
  // unconcluded child workers — surfaced every turn as terse `* <status> <path>` pointers (same shape
228
261
  // as errors) just above the errors section. Orienting STATE so the model never loses track of
229
262
  // what it's holding (the premature-terminate trap), never advice on what to do. Empty → omitted.
@@ -232,193 +265,71 @@ class PacketBuilder {
232
265
  const childWorkers = (await this.#db.engine_child_workers_live.all({ worker_id: workerId }))
233
266
  .map((r) => ({ status: r.status, path: `worker://${r.name}` }));
234
267
  const defaults = [
235
- { name: "definition", slot: "system", header: null, content: system_definition, tokens: 0 },
236
- { name: "tools", slot: "system", header: "Registered Executable Tools", content: tools.executors, tokens: 0 },
268
+ { name: "definition", slot: "system", header: null, content: system_definition },
269
+ { name: "tools", slot: "system", header: "Registered Executable Tools", content: tools.executors },
237
270
  ...(tools.optionalOperations.length > 0
238
- ? [{ name: "optional-operations", slot: "system", header: "Enabled Optional Operations", content: tools.optionalOperations, tokens: 0 }]
271
+ ? [{ name: "optional-operations", slot: "system", header: "Enabled Optional Operations", content: tools.optionalOperations }]
239
272
  : []),
240
- { name: "schemes", slot: "system", header: "Schemes", content: this.#schemes.teach(), tokens: 0 },
241
- ...(inject !== null ? [{ name: "inject", slot: "system", header: "Operator Notes", content: inject, tokens: 0 }] : []),
273
+ { name: "schemes", slot: "system", header: "Schemes", content: this.#schemes.teach() },
274
+ ...(inject !== null ? [{ name: "inject", slot: "system", header: "Operator Notes", content: inject }] : []),
242
275
  // policy: the client's privileged rules — ~/.plurnk/AGENTS.md (system) then <root>/AGENTS.md (project) — below grammar/tools/schemes, above budget-the-law. AGENTS is POLICY here, never a curatable READable entry. Empty content ⇒ section omitted.
243
- { name: "system-policy", slot: "system", header: "Policy", content: systemPolicy ?? "", tokens: 0 },
244
- { name: "project-policy", slot: "system", header: "Project Policy", content: projectPolicy ?? "", tokens: 0 },
245
- // log leads the user slot: injectable content (READ results, exec output, the model's own mirror) — data, never rules — its frozen head extends the cache prefix ({§packet-cache-monotone}).
246
- { name: "log", slot: "user", header: "Log", content: PacketWire.renderLog(log, countTokens), tokens: 0 },
247
- // The status clump — every per-turn-volatile section, quarantined between the log and
248
- // the recap ({§packet-cache-monotone}, #531): after the append-only log so its churn
249
- // never invalidates the log's cache prefix, and nearest the generation point (the
250
- // dashboard reads freshest exactly where the model acts). Engine-authored riding the
251
- // user slot is the legal trust direction; errors are uri+status POINTERS (the item +
252
- // body live in the log), git is counts — no injection surface either way.
253
- // child-orientation: what THIS run holds live — streams then runs — just above errors. Terse
254
- // pointers (the path is the actionable address the model READs/OPENs/KILLs), never advice. §child-orientation
255
- { name: "child-streams", slot: "user", header: "Child Streams", content: PacketWire.renderChildPointers(childStreams), tokens: 0 },
256
- { name: "child-workers", slot: "user", header: "Active Child Workers", content: PacketWire.renderChildPointers(childWorkers), tokens: 0 },
257
- { name: "errors", slot: "user", header: "Errors", content: PacketWire.renderErrors(telemetryErrors), tokens: 0 },
258
- { name: "git", slot: "user", header: "Git Status", content: PacketWire.renderGit(gitStatus), tokens: 0 },
276
+ { name: "system-policy", slot: "system", header: "Policy", content: systemPolicy ?? "" },
277
+ { name: "project-policy", slot: "system", header: "Project Policy", content: projectPolicy ?? "" },
278
+ // The append-mostly log leads volatile user status ({§packet-cache-monotone}).
279
+ { name: "log", slot: "user", header: "Log", content: PacketWire.renderLog(log, countTokens) },
280
+ // The per-turn status clump sits between log and recap ({§packet-cache-monotone}).
281
+ // child-orientation: what this worker holds live — streams then child workers — just above errors. Terse
282
+ // pointers (the path is the actionable address the model READs/OPENs/KILLs), never advice. {§child-orientation}
283
+ { name: "child-streams", slot: "user", header: "Child Streams", content: PacketWire.renderChildPointers(childStreams) },
284
+ { name: "child-workers", slot: "user", header: "Active Child Workers", content: PacketWire.renderChildPointers(childWorkers) },
285
+ { name: "errors", slot: "user", header: "Errors", content: PacketWire.renderFailurePointers(failures) },
286
+ { name: "notices", slot: "user", header: "Notices", content: PacketWire.renderNotices(notices) },
287
+ { name: "git", slot: "user", header: "Git Status", content: PacketWire.renderGit(gitStatus) },
259
288
  // budget — LAW (a hard ceiling the model must obey).
260
- { name: "budget", slot: "user", header: "Budget", content: budgetReadout, tokens: 0 },
261
- // §prompt-auto-read (owner): the prompts section closes the status clump —
262
- // a paths-only list (the errors shape); bodies arrive via the foisted auto-READ.
263
- { name: "prompt", slot: "user", header: "User Prompts", content: prompt, tokens: 0 },
289
+ { name: "budget", slot: "user", header: "Budget", content: budgetReadout },
290
+ // The prompts section closes the status clump as a paths-only list;
291
+ // bodies arrive through first-class prompt rows.
292
+ { name: "prompt", slot: "user", header: "User Prompts", content: prompt },
264
293
  // requirements renders LAST — the user-slot footer, the syntax contract closest to the model's turn (a recency carve-out for weak models).
265
- { name: "requirements", slot: "user", header: "Recap", content: baseRequirements, tokens: 0 },
294
+ { name: "requirements", slot: "user", header: "Recap", content: baseRequirements },
266
295
  ];
267
- // Plugin packet control (§packet-assembly): trusted schemes rewrite the
296
+ // Plugin packet control ({§packet-assembly}): trusted schemes rewrite the
268
297
  // default list — add, remove, reorder — in-process, before measurement.
269
- const sections = await this.#schemes.transformSections(defaults);
270
- // Pass 1: measure the assembled total with the placeholder budget in
271
- // place, resolve free/percent, substitute into the budget section.
272
- let total = countTokens(PacketWire.renderSlot(sections, "system")) + countTokens(PacketWire.renderSlot(sections, "user"));
273
- {
274
- const budgetSec = sections.find((s) => s.name === "budget"); // a plugin may have removed it
275
- // A null ceiling (#421 — unbounded window) has no headline to calibrate: no truncation, no
276
- // percent/free substitution. #renderBudget already omitted the headline, so nothing to do.
277
- if (budgetSec && ceiling !== null) {
278
- // Curation pressure gates on OCCUPANCY (§tokenomics-pressure-gates-on-occupancy, #308):
279
- // the Turns/Heaviest tables are a standing FOLD-target list, and a high-headroom model
280
- // reads them as a todo — burning turns on token hygiene at 3% of a 64k window. Under
281
- // half-full, the headline's numbers stand alone (truncate at the first blank line) and
282
- // the total RE-measures — the substituted figures must reconcile with what ships.
283
- // A null ceiling can't calibrate, so the full readout stays.
284
- // A mermaid budget (#440) self-scales to pressure, so it is never truncated — the calm
285
- // low-usage view IS the point; only the tabular readout collapses under half-full.
286
- if (!budgetSec.content.includes("```mermaid") && (total / ceiling) * 100 < 50) {
287
- const cut = budgetSec.content.indexOf("\n\n");
288
- if (cut !== -1) {
289
- budgetSec.content = budgetSec.content.slice(0, cut);
290
- total = countTokens(PacketWire.renderSlot(sections, "system")) + countTokens(PacketWire.renderSlot(sections, "user"));
291
- }
292
- }
293
- const tokensFree = Math.max(0, ceiling - total); // free floors at 0 on overshoot — §tokenomics-over-budget-floor
294
- const percent = (total / ceiling) * 100; // usage as % of the ceiling — §tokenomics-context-percent
295
- const sumTurns = logBudget.byTurn.reduce((s, t) => s + t.tokens, 0); // #440 — treemap non-turn box = total − Σturns
296
- const systemCtx = Math.max(0, total - sumTurns);
297
- // replaceAll: a mermaid budget recurs free/used across the headline + treemap + pie, so a
298
- // single .replace would leave the diagrams carrying literal {{…}} placeholders.
299
- budgetSec.content = budgetSec.content
300
- .replaceAll(TOKEN_USAGE_PLACEHOLDER, String(total))
301
- // Any nonzero usage under 1% is "<1" — Math.round alone claimed "1%" from 0.51%,
302
- // overstating a near-empty window.
303
- .replaceAll(TOKEN_PERCENT_PLACEHOLDER, total > 0 && percent < 1 ? "<1" : String(Math.round(percent)))
304
- .replaceAll(TOKENS_FREE_PLACEHOLDER, String(tokensFree))
305
- .replaceAll(SYSTEM_CTX_PLACEHOLDER, String(systemCtx));
306
- }
298
+ let drafts = await this.#schemes.transformSections(defaults);
299
+ const budgetSection = drafts.find((section) => section.name === "budget");
300
+ if (budgetSection !== undefined && ceiling !== null) {
301
+ const content = BudgetReadout.resolve(budgetSection.content, ceiling, (candidate) => {
302
+ const candidateDrafts = drafts.map((section) => section === budgetSection ? { ...section, content: candidate } : section);
303
+ return countTokens(PacketWire.renderSlot(candidateDrafts, "system"))
304
+ + countTokens(PacketWire.renderSlot(candidateDrafts, "user"));
305
+ });
306
+ drafts = drafts.map((section) => section === budgetSection ? { ...section, content } : section);
307
307
  }
308
- // Pass 2: per-section render-weight + the assembled packet total (post
309
- // substitution — the placeholder/number length delta is negligible).
310
- for (const s of sections)
311
- s.tokens = countTokens(PacketWire.renderSection(s));
308
+ // Core alone turns validated drafts into measured durable sections.
309
+ const sections = drafts.map((section) => ({
310
+ ...section,
311
+ tokens: countTokens(PacketWire.renderSection(section)),
312
+ }));
312
313
  const packetTokens = countTokens(PacketWire.renderSlot(sections, "system")) + countTokens(PacketWire.renderSlot(sections, "user"));
313
- return { tokens: packetTokens, sections, telemetryErrors };
314
- }
315
- // Budget readout body, rendered into the `## Plurnk Service Budget` section.
316
- // Headline `ceiling/free` only when a ceiling exists; section lines for the
317
- // curatable index/log weight the model can FOLD back. tokensFree is a
318
- // placeholder here — buildRequestPacket substitutes it after measuring the packet.
319
- #renderBudget(log, ceiling) {
320
- const lines = [];
321
- // #421 — no ceiling (unbounded window): omit the headline entirely; the section lines below
322
- // stay so the model keeps its FOLD-target surface, just with no percent it can't compute.
323
- if (ceiling !== null)
324
- lines.push(`Token Ceiling ${ceiling} · Token Usage ${TOKEN_USAGE_PLACEHOLDER} (${TOKEN_PERCENT_PLACEHOLDER}%) · Tokens Free ${TOKENS_FREE_PLACEHOLDER}`);
325
- // #440 {§budget-mermaid} — the enriched visual Budget (default on). With a ceiling to scale
326
- // against, the treemap REPLACES the Turns table (per-turn composition) + a pie gauge; the
327
- // heaviest-items list stays a table (#450). Self-scaled to pressure (calm→urgent), never <50%-truncated.
328
- // Set PLURNK_SERVICE_BUDGET_MERMAID=off to A/B against the tabular baseline (#440's before/after).
329
- if (process.env.PLURNK_SERVICE_BUDGET_MERMAID !== "off" && ceiling !== null && log.entries > 0) {
330
- if (lines.length > 0)
331
- lines.push("");
332
- lines.push(_a.#renderBudgetMermaid(log, ceiling));
333
- // #450 — the heaviest items stay a plain ranked list (a ranking isn't a composition, no
334
- // treemap; two mermaid diagrams are enough visual examples) — the same table as the tabular budget.
335
- lines.push(..._a.#heaviestItemsLines(log.largest));
336
- return lines.join("\n");
337
- }
338
- if (log.entries > 0) {
339
- if (lines.length > 0)
340
- lines.push("");
341
- lines.push(`Log entries: ${log.entries} entries, ${log.tokens} tokens`);
342
- // Per-turn weight — chronological (oldest first); the turn is the grinder's
343
- // rollback unit and the rail folds the newest first (§tokenomics {§tokenomics-turn-totals}).
344
- if (log.byTurn.length > 0) {
345
- lines.push("", "Turns:", "| turn | tokens |", "|---|--:|");
346
- for (const t of log.byTurn)
347
- lines.push(`| ${t.turn} | ${t.tokens} |`);
348
- }
349
- lines.push(..._a.#heaviestItemsLines(log.largest));
350
- }
351
- return lines.join("\n");
352
- }
353
- // The heaviest individual log items — the FOLD targets behind the weight, a ranked LIST in both the
354
- // mermaid and tabular budgets (#450: a ranking isn't a composition, so it's never a chart). "items",
355
- // not "entries": log:/// rows, distinct from catalog entries (plurnk.md: "EDIT is only for entries").
356
- // {§tokenomics-largest-entries}
357
- static #heaviestItemsLines(largest) {
358
- if (largest.length === 0)
359
- return [];
360
- return ["", "Heaviest items (FOLD targets — folding reclaims their tokens):", "| item | tokens |", "|---|--:|",
361
- ...largest.map((e) => `| ${e.path} | ${e.tokens} |`)];
362
- }
363
- // #440 {§budget-mermaid} — the Budget as two budget-scaled mermaid diagrams (validated to render on
364
- // GitHub; syntax: plurnk-plurnkdown/demo/budget-mermaid.md). Both scaled to the CEILING, so salience
365
- // tracks pressure: `free` dominates at low usage (calm), turn boxes fill as it climbs (urgent).
366
- // free/used/system+context are placeholders — the post-assembly total resolves them. (#450 cut the xychart.)
367
- static #renderBudgetMermaid(log, ceiling) {
368
- // Turn composition → treemap: turn boxes + system+context + free compose the whole ceiling —
369
- // the per-turn FOLD surface (which turns are heavy, labeled `turn L/T`) the headline can't give.
370
- const treemap = [
371
- "```mermaid",
372
- "treemap-beta",
373
- `"Budget — ceiling ${ceiling}"`,
374
- ` "free": ${TOKENS_FREE_PLACEHOLDER}`,
375
- ` "system + context": ${SYSTEM_CTX_PLACEHOLDER}`,
376
- ...log.byTurn.map((t) => ` "turn ${t.turn}": ${t.tokens}`),
377
- "```",
378
- ].join("\n");
379
- // Gauge → pie: used vs free, budget-scaled (used + free = ceiling); also a visual exemplar for
380
- // the model's own user-facing SENDs. (#450 cut the heaviest-items xychart — its bare-coordinate
381
- // labels a floor model can't decode, and the treemap already surfaces per-turn heaviness.)
382
- const pie = [
383
- "```mermaid",
384
- "pie showData",
385
- ` title Budget — used vs free (ceiling ${ceiling})`,
386
- ` "used" : ${TOKEN_USAGE_PLACEHOLDER}`,
387
- ` "free" : ${TOKENS_FREE_PLACEHOLDER}`,
388
- "```",
389
- ].join("\n");
390
- return [treemap, pie].join("\n\n");
314
+ return { tokens: packetTokens, sections, attributions: [] };
391
315
  }
392
- // #328 — the per-workspace client execs policy narrows what the packet ADVERTISES, matching what
393
- // dispatch refuses: a workspace-disabled tag is absent from the capability sheet and the doc set,
394
- // never taught-then-refused. No policy (execs unset) → everything boot-registered shows.
316
+ // {§operator-config-workspace-execs} — the capability sheet and executor
317
+ // documents share one workspace predicate with dispatch.
395
318
  async #workspaceEnabled(workspaceId) {
396
319
  const { execs } = await WorkspaceSettings.read(this.#db, workspaceId);
397
320
  if (execs === null)
398
321
  return () => true;
399
322
  return (tag) => Policy.isEnabled(tag, execs);
400
323
  }
401
- // The ## Registered Executable Tools capability sheet (SPEC §tools). Each available executor
402
- // tag contributes its self-documenting example (plurnk-execs#7); the closed heading distinguishes
403
- // registered selectors from the open-ended general examples above it. §tools-capability-sheet
404
- // Mirror of Dispatcher.#loadLoopFlags — the packet reads the SAME persisted flags the gate does.
405
- async #loadLoopFlags(loopId) {
406
- const row = await this.#db.engine_get_loop_flags.get({ loop_id: loopId });
407
- if (row === undefined)
408
- return DEFAULT_LOOP_FLAGS;
409
- try {
410
- return { ...DEFAULT_LOOP_FLAGS, ...JSON.parse(row.flags) };
411
- }
412
- catch {
413
- return DEFAULT_LOOP_FLAGS;
414
- }
415
- }
324
+ // The ## Registered Executable Tools capability sheet. Each available executor
325
+ // tag contributes its self-documenting example; the closed heading distinguishes
326
+ // registered selectors from the open-ended general examples above it. {§tools-capability-sheet}
416
327
  #collectTools(workspaceEnabled, questionsOn = false, activeSchemes) {
417
- // §PACKET Tools (#441) — registered executor examples form a closed, explicitly titled
418
- // catalogue. Optional non-EXEC operations are separate so the heading remains truthful.
328
+ // Registered executors and optional operations remain distinct fenced sheets.
329
+ // {§tools-capability-sheet} {§packet-operation-fences}
419
330
  const executorOps = [];
420
331
  const notices = [];
421
- // §send-300-choices — the one-liner rides ONLY where questions are enabled (allowed +
332
+ // {§send-300-choices} — the one-liner rides ONLY where questions are enabled (allowed +
422
333
  // client-requested); the fuller questions.md doc injects through docEntries the same way.
423
334
  const optionalOperations = questionsOn
424
335
  ? "```plurnk\n<<SEND[300]:Deploy where?;staging;production:SEND\n```"
@@ -427,7 +338,7 @@ class PacketBuilder {
427
338
  if (executors !== undefined) {
428
339
  const excluded = docsExcludeSet();
429
340
  const runtimes = executors.availableRuntimes();
430
- // execs#24 / #367: the sheet's lines are EXEC-usage examples, keyed on the 'exec' scheme
341
+ // {§tools-capability-sheet} The sheet's lines are EXEC-usage examples, keyed on the 'exec' scheme
431
342
  // (the op face, excludedInAsk). When inactive, say so POSITIVELY (a prose notice): plurnk.md
432
343
  // still teaches EXEC as language, and silent absence measurably invites confabulated runtimes.
433
344
  const execActive = activeSchemes === undefined || activeSchemes.has("exec");
@@ -437,12 +348,12 @@ class PacketBuilder {
437
348
  else {
438
349
  for (const tag of runtimes) {
439
350
  if (excluded.has(tag))
440
- continue; // #240 — PLURNK_SERVICE_DOCS_EXCLUDE drops the oneliner + the doc
351
+ continue; // {§tools-capability-sheet} — exclude drops the example and doc
441
352
  if (!workspaceEnabled(tag))
442
- continue; // #328 — workspace-disabled tags aren't advertised
353
+ continue; // {§operator-config-workspace-execs}
443
354
  const entry = executors.entry(tag);
444
- // #240 — the example IS the oneliner (a bare op, fenced below); the fuller doc
445
- // materializes at plurnk://docs/<tag>.md. No example → no line.
355
+ // {§tools-capability-sheet} — the example is the bare op fenced below; the fuller doc
356
+ // materializes at worker://plurnk/docs/<tag>.md. No example → no line.
446
357
  if (entry?.example)
447
358
  executorOps.push(entry.example);
448
359
  }
@@ -454,28 +365,25 @@ class PacketBuilder {
454
365
  return { executors: parts.join("\n\n"), optionalOperations };
455
366
  }
456
367
  // #note12 — the plugin-provided reference docs (schemes' + execs' `documentation`),
457
- // materialized at plurnk:///docs/<name>.md by loop_run (like operator docs) so the
458
- // catalogue's doc-links READ and the manifest carries each doc's token weight.
368
+ // materialized at worker://plurnk/docs/<name>.md by LoopDocs (like operator
369
+ // docs) so the catalog can expose each doc's token weight.
459
370
  async docEntries(workspaceId) {
460
- const out = this.#schemes.docs(); // scheme docs already drop PLURNK_SERVICE_DOCS_EXCLUDE names
461
- // §send-300-choices — the conditional teaching: questions.md (from the docs corpus)
371
+ const out = await this.#schemes.docs(); // scheme docs already drop PLURNK_SERVICE_DOCS_EXCLUDE names
372
+ // {§send-300-choices} {§teaching-corpus} — the conditional teaching: questions.md
462
373
  // materializes ONLY for enabled workspaces — the same conditional-doc mechanism as the EXEC
463
374
  // plugin docs below. An un-enabled workspace is never taught the op it can't use.
464
375
  if (await WorkspaceSettings.questionsEnabled(this.#db, workspaceId)) {
465
- try {
466
- const q = await readFile(resolvePath(Paths.schemeDocs, "questions.md"), "utf8");
467
- if (q.length > 0)
468
- out.push({ name: "questions", content: q });
469
- }
470
- catch { /* docs package without questions.md — nothing to inject */ }
376
+ const q = await this.#schemes.questionsDoc();
377
+ if (q.length > 0)
378
+ out.push({ name: "questions", content: q });
471
379
  }
472
380
  const executors = this.#executors();
473
381
  if (executors !== undefined) {
474
382
  const excluded = docsExcludeSet();
475
- const workspaceEnabled = await this.#workspaceEnabled(workspaceId); // #328 — no doc for a disabled tag
383
+ const workspaceEnabled = await this.#workspaceEnabled(workspaceId); // {§operator-config-workspace-execs}
476
384
  for (const tag of executors.availableRuntimes()) {
477
385
  if (excluded.has(tag))
478
- continue; // #240 — exec docs honor the same exclude
386
+ continue; // {§tools-capability-sheet} — exec docs honor the same exclude
479
387
  if (!workspaceEnabled(tag))
480
388
  continue;
481
389
  const doc = executors.entry(tag)?.documentation;
@@ -485,90 +393,120 @@ class PacketBuilder {
485
393
  }
486
394
  return out;
487
395
  }
488
- // SPEC §grinder — the budget grinder. Runs pre-LLM (in runTurn, after the packet
396
+ // SPEC {§grinder} — the budget grinder. Runs pre-LLM (in runTurn, after the packet
489
397
  // is built, before provider.generate); fires only on actual overflow. One rule:
490
398
  // fold the NEWEST turn boundary's open rows (errors exempt) — never history —
491
399
  // strike, rebuild, re-measure. Folds (never deletes). The strike it raises and
492
400
  // the hard-stop it can signal are returned to runLoop, which owns abandonment.
493
- // §grinder-overflow-only — fires only on actual overflow, never speculatively
401
+ // {§grinder-overflow-only} — fires only on actual overflow, never speculatively
494
402
  async enforceBudget({ packet, provider, workerId, loopId, turnId, mintSequence, rebuild }) {
495
403
  const ceiling = this.ceilingFor(provider);
496
404
  const measure = (p) => p.tokens;
497
- // #421 — a null ceiling is an unbounded window: always fit, never fold or strike (the backend
498
- // clamps; this mirrors Engine's physicallySendable, which treats a null contextWindow as sendable).
499
- if (ceiling === null || measure(packet) <= ceiling)
500
- return { packet, fit: true, struck: false };
501
- // ONE rule, every turn — turn 1 and turn 101 alike (§grinder-layer1-rollback): fold the
405
+ // {§tokenomics-window-unpollable-deliberate} — a null policy ceiling never triggers grinding. If a virtual ceiling
406
+ // does trigger recovery while provider physics is unknown, physical admission
407
+ // separately fails closed under {§tokenomics-physical-admission}.
408
+ if (ceiling === null || measure(packet) <= ceiling) {
409
+ return { packet, fit: true, struck: false, recorded: false };
410
+ }
411
+ // ONE rule, every turn — turn 1 and turn 101 alike ({§grinder-layer1-rollback}): fold the
502
412
  // NEWEST turn boundary's still-open rows (the prior turn's emissions + this turn's
503
413
  // pre-model rows; errors exempt) in one set-op, strike once, rebuild, re-measure.
504
- // THE DOCTRINE: the log is the model's memory and the model ALONE curates history — the
505
- // grinder never reaches back; it only blocks NEW memories from landing when there is no
506
- // room, forcing the model to do its own housekeeping (the strike is the escalation,
507
- // §grinder-compaction-strikes; a model that won't curate hard-413s/strikes out).
508
- // Mint the overflow as a terse op='error' log row BEFORE the rebuild, so the rebuild's
509
- // re-derived errors section surfaces it THIS turn — the warning lands at strike 1, not a
510
- // turn late. The row is grinder-exempt, so it stacks into a visible recurrence trail. It
511
- // sits at the turn's reserved running sequence (mintSequence) so it never collides with the
512
- // post-generate dispatch rows. §telemetry-uniform-error-channel, §grinder-overflow-error-row
513
- await this.#telemetry.mintEngineError("budget_overflow", { workerId, loopId, turnId, sequence: mintSequence });
514
- await this.#db.engine_grinder_fold_newest_turn.run({ loop_id: loopId, turn_id: turnId });
515
- const current = await rebuild();
516
- return { packet: current, fit: measure(current) <= ceiling, struck: true };
517
- }
518
- // §tokenomics-agnostic-ruler — the EXACT materialization measure: the provider's own token
519
- // count of the assembled packet, the ONE place per-model exactness is used (once per turn, at
520
- // the fit-gate). The model-facing render-weight (packet.tokens) stays the ruler; this is the
521
- // physics check that the real bytes fit the real window.
522
- exactPacketTokens(packet, provider) {
523
- return provider.countTokens(PacketWire.renderSlot(packet.sections, "system"))
524
- + provider.countTokens(PacketWire.renderSlot(packet.sections, "user"));
525
- }
526
- // Complete the packet by adding the model's response. After this the
527
- // packet matches Packet.json fully and is ready for storage.
528
- completePacket(requestPacket, assistant, assistantRaw, provider) {
529
- const assistantTokens = rulerCount(assistant.content); // §tokenomics-agnostic-ruler — render-weight in ruler units (usage_* keep the provider's real count)
414
+ // The model owns history visibility. The grinder never reaches back; it folds only the
415
+ // newest boundary required to produce a physically valid packet. Continued overflow
416
+ // follows the explicit recovery/hard-stop contract ({§grinder-fold-strikes}).
417
+ const usage = measure(packet);
418
+ await this.#db.engine_grinder_fold_newest_turn({ loop_id: loopId, turn_id: turnId });
419
+ let current = await rebuild();
420
+ if (measure(current) > ceiling) {
421
+ // Engine owns the hard-overflow branch and records its one occurrence with
422
+ // the enforced recovery constraint (or terminal physics), so this layer does
423
+ // not mint a second, weaker account of the same failure.
424
+ return { packet: current, fit: false, struck: true, recorded: false };
425
+ }
426
+ // The fold recovered the request. Preserve the triggering measurements on one
427
+ // actionless row and rebuild once so its Problem surfaces in this same packet.
428
+ await this.#problems.record({
429
+ workerId,
430
+ loopId,
431
+ turnId,
432
+ sequence: mintSequence,
433
+ origin: "plurnk",
434
+ source: "rail",
435
+ result: BudgetOverflow.foldedResult(usage, ceiling),
436
+ });
437
+ current = await rebuild();
530
438
  return {
531
- tokens: requestPacket.tokens + assistantTokens,
532
- sections: requestPacket.sections,
533
- telemetryErrors: requestPacket.telemetryErrors,
534
- assistant,
535
- assistantRaw,
439
+ packet: current,
440
+ fit: measure(current) <= ceiling,
441
+ struck: true,
442
+ recorded: true,
536
443
  };
537
444
  }
538
- // SPEC §telemetry: model-facing alert surface.
539
- // Two sources, merged on each packet build:
540
- // 1. Previous-turn action-bound failures (status_rx >= 400 on log_entries).
541
- // 2. Engine-buffered actionless failures (no_send, parse, watchdog, rails).
542
- // Buffer drains on read — each error appears in exactly one packet.
543
- async buildTelemetryErrors(loopId, currentTurnSeq) {
544
- // The uniform error channel (§telemetry-uniform-error-channel): every 4xx/5xx log row
545
- // becomes a LogCoordinate-positioned TelemetryEvent — a terse pointer; the model READs the
546
- // row for its term + detail. Buffer events that point at the model's own emission keep their
547
- // ContentOffset position. info-level notices (progress) are not errors and never surface here.
548
- const rows = await this.#db.engine_render_telemetry_errors.all({ loop_id: loopId, current_turn_seq: currentTurnSeq });
549
- const logErrors = rows.map((r) => ({
550
- source: "engine:rail",
551
- kind: "log_error",
552
- level: "error",
445
+ // {§tokenomics-physical-admission}: one request-shaped physical predicate.
446
+ // The exact PacketWire messages used for dispatch are the measurement input;
447
+ // empirical estimates cannot authorize recovery against a hard window.
448
+ async physicalAdmission(packet, provider, signal) {
449
+ if (provider.contextWindow === null) {
450
+ return {
451
+ admitted: false,
452
+ reason: "unknown_context_window",
453
+ detail: `provider ${JSON.stringify(provider.model)} reports no physical context window`,
454
+ capacity: null,
455
+ };
456
+ }
457
+ const maxTokens = this.maxTokensFor(provider);
458
+ if (maxTokens === null) {
459
+ return {
460
+ admitted: false,
461
+ reason: "unknown_output_envelope",
462
+ detail: `provider ${JSON.stringify(provider.model)} reports no resolved output envelope`,
463
+ capacity: null,
464
+ };
465
+ }
466
+ const capacity = provider.contextWindow - maxTokens;
467
+ const measurement = assertPromptTokenMeasurement(await provider.countPromptTokens(PacketWire.packetToWireMessages(packet), signal), `provider ${JSON.stringify(provider.model)}`);
468
+ if (measurement.kind === "estimate") {
469
+ return {
470
+ admitted: false,
471
+ reason: "estimate",
472
+ detail: `${measurement.source} is an empirical estimate and cannot authorize physical recovery: ${measurement.detail}`,
473
+ capacity,
474
+ measurement,
475
+ };
476
+ }
477
+ if (measurement.tokens > capacity) {
478
+ return {
479
+ admitted: false,
480
+ reason: "over_capacity",
481
+ detail: `${measurement.kind} prompt measurement ${measurement.tokens} exceeds physical prompt capacity ${capacity}`,
482
+ capacity,
483
+ measurement,
484
+ };
485
+ }
486
+ return { admitted: true, capacity, measurement };
487
+ }
488
+ // Every prior-turn operation failure is durable before packet assembly.
489
+ // The model-facing Errors section is only a terse projection of those rows;
490
+ // it never reconstructs failure truth from an in-memory event.
491
+ async buildFailurePointers(loopId, currentTurnSeq) {
492
+ const rows = await this.#db.engine_render_errors.all({ loop_id: loopId, current_turn_seq: currentTurnSeq });
493
+ return rows.map((r) => ({
553
494
  status: r.status_rx,
554
- position: { type: "log-coordinate", coordinate: `${r.loop_seq}/${r.turn_seq}/${r.sequence}/${r.op}` },
495
+ coordinate: `${r.loop_seq}/${r.turn_seq}/${r.sequence}/${r.op}`,
555
496
  }));
556
- const bufferEvents = this.#telemetry.drain(loopId).filter((e) => e.level !== "info");
557
- return [...bufferEvents, ...logErrors];
558
497
  }
559
- // SPEC §packet the log section — chronological action-entries for the loop.
498
+ // SPEC {§packet} the log section — chronological action-entries for the loop.
560
499
  // Snapshot is taken at packet build (pre-dispatch this turn), so it
561
500
  // reflects "what has happened before this turn." Each row carries a
562
501
  // log:///<loop_seq>/<turn_seq>/<sequence> coordinate the model can READ.
563
502
  async #buildLog(workerId) {
564
- // SPEC §packet-terms: runs own log entries — log is the worker's history,
503
+ // SPEC {§packet-terms}: workers own log entries — log is the worker's history,
565
504
  // not the loop's. Span all loops in the worker so the model sees
566
505
  // earlier loops' work as conversational memory.
567
506
  //
568
- // User prompts are first-class log entries: runTurn writes a
569
- // client-origin SEND[200] row at sequence=0 of each new
570
- // turn-1. Prompts thus surface naturally in this query — no
571
- // synthetic / shim layer.
507
+ // User prompts are first-class actionless log entries written by
508
+ // runTurn. They surface naturally in this query without synthetic
509
+ // EDIT/READ delivery rows.
572
510
  const rows = await this.#db.engine_render_log.all({ worker_id: workerId });
573
511
  return rows.map((r) => ({
574
512
  coordinate: `${r.loop_seq}/${r.turn_seq}/${r.sequence}`,
@@ -581,7 +519,7 @@ class PacketBuilder {
581
519
  username: r.username, password: r.password,
582
520
  hostname: r.hostname, port: r.port,
583
521
  pathname: r.pathname,
584
- params: r.params === null ? null : JSON.parse(r.params),
522
+ query: r.query,
585
523
  fragment: r.fragment,
586
524
  },
587
525
  status: r.status_rx,