@plurnk/plurnk-service 1.3.12 → 1.5.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 (488) hide show
  1. package/.env.defaults +95 -91
  2. package/INSTALL.md +54 -28
  3. package/README.md +25 -10
  4. package/SPEC.md +2426 -942
  5. package/dist/Paths.d.ts +4 -1
  6. package/dist/Paths.d.ts.map +1 -1
  7. package/dist/Paths.js +24 -23
  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 +4 -3
  18. package/dist/content/index.d.ts.map +1 -1
  19. package/dist/content/index.js +2 -1
  20. package/dist/content/index.js.map +1 -1
  21. package/dist/content/line-marker.d.ts +10 -9
  22. package/dist/content/line-marker.d.ts.map +1 -1
  23. package/dist/content/line-marker.js +8 -11
  24. package/dist/content/line-marker.js.map +1 -1
  25. package/dist/content/matcher.d.ts +15 -26
  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-projector.d.ts +14 -0
  36. package/dist/content/read-projector.d.ts.map +1 -0
  37. package/dist/content/read-projector.js +86 -0
  38. package/dist/content/read-projector.js.map +1 -0
  39. package/dist/content/read-resolve.d.ts +6 -7
  40. package/dist/content/read-resolve.d.ts.map +1 -1
  41. package/dist/content/read-resolve.js +36 -59
  42. package/dist/content/read-resolve.js.map +1 -1
  43. package/dist/core/BranchReceipt.d.ts +6 -0
  44. package/dist/core/BranchReceipt.d.ts.map +1 -0
  45. package/dist/core/BranchReceipt.js +24 -0
  46. package/dist/core/BranchReceipt.js.map +1 -0
  47. package/dist/core/BudgetOverflow.d.ts +20 -0
  48. package/dist/core/BudgetOverflow.d.ts.map +1 -0
  49. package/dist/core/BudgetOverflow.js +49 -0
  50. package/dist/core/BudgetOverflow.js.map +1 -0
  51. package/dist/core/BudgetReadout.d.ts +8 -0
  52. package/dist/core/BudgetReadout.d.ts.map +1 -0
  53. package/dist/core/BudgetReadout.js +79 -0
  54. package/dist/core/BudgetReadout.js.map +1 -0
  55. package/dist/core/ChannelWrite.d.ts +31 -9
  56. package/dist/core/ChannelWrite.d.ts.map +1 -1
  57. package/dist/core/ChannelWrite.js +30 -23
  58. package/dist/core/ChannelWrite.js.map +1 -1
  59. package/dist/core/ChannelWrite.sql +14 -12
  60. package/dist/core/CoreSchemeServices.d.ts +18 -4
  61. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  62. package/dist/core/CoreSchemeServices.js +7 -1
  63. package/dist/core/CoreSchemeServices.js.map +1 -1
  64. package/dist/core/Dispatcher.d.ts +26 -21
  65. package/dist/core/Dispatcher.d.ts.map +1 -1
  66. package/dist/core/Dispatcher.js +1626 -671
  67. package/dist/core/Dispatcher.js.map +1 -1
  68. package/dist/core/DurableStatement.d.ts +6 -0
  69. package/dist/core/DurableStatement.d.ts.map +1 -0
  70. package/dist/core/DurableStatement.js +51 -0
  71. package/dist/core/DurableStatement.js.map +1 -0
  72. package/dist/core/Engine.d.ts +64 -29
  73. package/dist/core/Engine.d.ts.map +1 -1
  74. package/dist/core/Engine.js +1348 -835
  75. package/dist/core/Engine.js.map +1 -1
  76. package/dist/core/Engine.sql +329 -234
  77. package/dist/core/EnvFlags.js +1 -1
  78. package/dist/core/EnvFlags.js.map +1 -1
  79. package/dist/core/ErrorDetail.d.ts +6 -0
  80. package/dist/core/ErrorDetail.d.ts.map +1 -0
  81. package/dist/core/ErrorDetail.js +20 -0
  82. package/dist/core/ErrorDetail.js.map +1 -0
  83. package/dist/core/ExecutorRegistry.d.ts +14 -3
  84. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  85. package/dist/core/ExecutorRegistry.js +56 -30
  86. package/dist/core/ExecutorRegistry.js.map +1 -1
  87. package/dist/core/GitBranch.d.ts +20 -0
  88. package/dist/core/GitBranch.d.ts.map +1 -0
  89. package/dist/core/GitBranch.js +110 -0
  90. package/dist/core/GitBranch.js.map +1 -0
  91. package/dist/core/JournalTurn.d.ts +9 -0
  92. package/dist/core/JournalTurn.d.ts.map +1 -0
  93. package/dist/core/JournalTurn.js +14 -0
  94. package/dist/core/JournalTurn.js.map +1 -0
  95. package/dist/core/JournalTurn.sql +10 -0
  96. package/dist/core/LogBody.d.ts +18 -0
  97. package/dist/core/LogBody.d.ts.map +1 -0
  98. package/dist/core/LogBody.js +180 -0
  99. package/dist/core/LogBody.js.map +1 -0
  100. package/dist/core/LoopFlagsReader.d.ts +7 -0
  101. package/dist/core/LoopFlagsReader.d.ts.map +1 -0
  102. package/dist/core/LoopFlagsReader.js +33 -0
  103. package/dist/core/LoopFlagsReader.js.map +1 -0
  104. package/dist/core/LoopLifecycle.d.ts +8 -2
  105. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  106. package/dist/core/LoopLifecycle.js +54 -11
  107. package/dist/core/LoopLifecycle.js.map +1 -1
  108. package/dist/core/LoopLifecycle.sql +10 -7
  109. package/dist/core/NoticeChannel.d.ts +13 -0
  110. package/dist/core/NoticeChannel.d.ts.map +1 -0
  111. package/dist/core/NoticeChannel.js +40 -0
  112. package/dist/core/NoticeChannel.js.map +1 -0
  113. package/dist/core/Owner.d.ts +0 -1
  114. package/dist/core/Owner.d.ts.map +1 -1
  115. package/dist/core/Owner.js +2 -5
  116. package/dist/core/Owner.js.map +1 -1
  117. package/dist/core/PacketBuilder.d.ts +27 -25
  118. package/dist/core/PacketBuilder.d.ts.map +1 -1
  119. package/dist/core/PacketBuilder.js +321 -383
  120. package/dist/core/PacketBuilder.js.map +1 -1
  121. package/dist/core/ProblemLog.d.ts +26 -0
  122. package/dist/core/ProblemLog.d.ts.map +1 -0
  123. package/dist/core/ProblemLog.js +58 -0
  124. package/dist/core/ProblemLog.js.map +1 -0
  125. package/dist/core/ProposalLifecycle.d.ts +15 -21
  126. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  127. package/dist/core/ProposalLifecycle.js +291 -78
  128. package/dist/core/ProposalLifecycle.js.map +1 -1
  129. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  130. package/dist/core/ProviderInstantiate.js +51 -48
  131. package/dist/core/ProviderInstantiate.js.map +1 -1
  132. package/dist/core/SchemeRegistry.d.ts +14 -8
  133. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  134. package/dist/core/SchemeRegistry.js +189 -114
  135. package/dist/core/SchemeRegistry.js.map +1 -1
  136. package/dist/core/ServiceTeardown.d.ts +11 -0
  137. package/dist/core/ServiceTeardown.d.ts.map +1 -0
  138. package/dist/core/ServiceTeardown.js +70 -0
  139. package/dist/core/ServiceTeardown.js.map +1 -0
  140. package/dist/core/StoredPacket.d.ts +29 -0
  141. package/dist/core/StoredPacket.d.ts.map +1 -0
  142. package/dist/core/StoredPacket.js +147 -0
  143. package/dist/core/StoredPacket.js.map +1 -0
  144. package/dist/core/StrikeRail.d.ts +6 -3
  145. package/dist/core/StrikeRail.d.ts.map +1 -1
  146. package/dist/core/StrikeRail.js +27 -56
  147. package/dist/core/StrikeRail.js.map +1 -1
  148. package/dist/core/TerminalResult.d.ts +16 -0
  149. package/dist/core/TerminalResult.d.ts.map +1 -0
  150. package/dist/core/TerminalResult.js +49 -0
  151. package/dist/core/TerminalResult.js.map +1 -0
  152. package/dist/core/WorkerControlAddress.d.ts +17 -0
  153. package/dist/core/WorkerControlAddress.d.ts.map +1 -0
  154. package/dist/core/WorkerControlAddress.js +44 -0
  155. package/dist/core/WorkerControlAddress.js.map +1 -0
  156. package/dist/core/WorkerName.d.ts +31 -0
  157. package/dist/core/WorkerName.d.ts.map +1 -0
  158. package/dist/core/WorkerName.js +95 -0
  159. package/dist/core/WorkerName.js.map +1 -0
  160. package/dist/core/WorkerName.sql +36 -0
  161. package/dist/core/WorkspaceGate.d.ts +14 -0
  162. package/dist/core/WorkspaceGate.d.ts.map +1 -0
  163. package/dist/core/WorkspaceGate.js +155 -0
  164. package/dist/core/WorkspaceGate.js.map +1 -0
  165. package/dist/core/caps/CapsResolve.d.ts +5 -1
  166. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  167. package/dist/core/caps/CapsResolve.js +8 -4
  168. package/dist/core/caps/CapsResolve.js.map +1 -1
  169. package/dist/core/caps/DbChannelCaps.d.ts +5 -11
  170. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  171. package/dist/core/caps/DbChannelCaps.js +30 -12
  172. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  173. package/dist/core/caps/DbEntryCaps.d.ts +5 -14
  174. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  175. package/dist/core/caps/DbEntryCaps.js +27 -8
  176. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  177. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  178. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  179. package/dist/core/caps/DbNotifyCaps.js +18 -15
  180. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  181. package/dist/core/caps/DbProjectionCaps.d.ts +5 -5
  182. package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
  183. package/dist/core/caps/DbProjectionCaps.js +21 -4
  184. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  185. package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -6
  186. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  187. package/dist/core/caps/DbSubscriptionCaps.js +75 -66
  188. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  189. package/dist/core/caps/DbTagCaps.d.ts +5 -12
  190. package/dist/core/caps/DbTagCaps.d.ts.map +1 -1
  191. package/dist/core/caps/DbTagCaps.js +20 -11
  192. package/dist/core/caps/DbTagCaps.js.map +1 -1
  193. package/dist/core/caps/SchemeCtxImpl.d.ts +6 -1
  194. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  195. package/dist/core/caps/SchemeCtxImpl.js +6 -6
  196. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  197. package/dist/core/content-hash.js +1 -1
  198. package/dist/core/content-hash.js.map +1 -1
  199. package/dist/core/env-defaults.d.ts.map +1 -1
  200. package/dist/core/env-defaults.js.map +1 -1
  201. package/dist/core/fork.d.ts.map +1 -1
  202. package/dist/core/fork.js +55 -30
  203. package/dist/core/fork.js.map +1 -1
  204. package/dist/core/fork.sql +43 -29
  205. package/dist/core/git-env.d.ts +2 -0
  206. package/dist/core/git-env.d.ts.map +1 -1
  207. package/dist/core/git-env.js +13 -18
  208. package/dist/core/git-env.js.map +1 -1
  209. package/dist/core/git-iso.d.ts +3 -2
  210. package/dist/core/git-iso.d.ts.map +1 -1
  211. package/dist/core/git-iso.js +36 -27
  212. package/dist/core/git-iso.js.map +1 -1
  213. package/dist/core/git-membership.d.ts +2 -1
  214. package/dist/core/git-membership.d.ts.map +1 -1
  215. package/dist/core/git-membership.js +276 -162
  216. package/dist/core/git-membership.js.map +1 -1
  217. package/dist/core/git-state.d.ts +8 -1
  218. package/dist/core/git-state.d.ts.map +1 -1
  219. package/dist/core/git-state.js +44 -26
  220. package/dist/core/git-state.js.map +1 -1
  221. package/dist/core/namespace.d.ts +1 -0
  222. package/dist/core/namespace.d.ts.map +1 -1
  223. package/dist/core/namespace.js +20 -0
  224. package/dist/core/namespace.js.map +1 -1
  225. package/dist/core/optimistic-settlement.d.ts +2 -0
  226. package/dist/core/optimistic-settlement.d.ts.map +1 -0
  227. package/dist/core/optimistic-settlement.js +14 -0
  228. package/dist/core/optimistic-settlement.js.map +1 -0
  229. package/dist/core/owner.sql +1 -1
  230. package/dist/core/packet-inject.d.ts.map +1 -1
  231. package/dist/core/packet-inject.js +7 -11
  232. package/dist/core/packet-inject.js.map +1 -1
  233. package/dist/core/packet-wire.d.ts +3 -21
  234. package/dist/core/packet-wire.d.ts.map +1 -1
  235. package/dist/core/packet-wire.js +305 -385
  236. package/dist/core/packet-wire.js.map +1 -1
  237. package/dist/core/plurnk-uri.d.ts +12 -1
  238. package/dist/core/plurnk-uri.d.ts.map +1 -1
  239. package/dist/core/plurnk-uri.js +49 -29
  240. package/dist/core/plurnk-uri.js.map +1 -1
  241. package/dist/core/results.d.ts +18 -5
  242. package/dist/core/results.d.ts.map +1 -1
  243. package/dist/core/results.js +33 -18
  244. package/dist/core/results.js.map +1 -1
  245. package/dist/core/ruler_count.d.ts +4 -0
  246. package/dist/core/ruler_count.d.ts.map +1 -0
  247. package/dist/core/ruler_count.js +7 -0
  248. package/dist/core/ruler_count.js.map +1 -0
  249. package/dist/core/scheme-types.d.ts +3 -17
  250. package/dist/core/scheme-types.d.ts.map +1 -1
  251. package/dist/core/scheme-types.js.map +1 -1
  252. package/dist/core/search-gate.d.ts.map +1 -1
  253. package/dist/core/search-gate.js +3 -11
  254. package/dist/core/search-gate.js.map +1 -1
  255. package/dist/core/teaching-corpus.d.ts +6 -0
  256. package/dist/core/teaching-corpus.d.ts.map +1 -0
  257. package/dist/core/teaching-corpus.js +23 -0
  258. package/dist/core/teaching-corpus.js.map +1 -0
  259. package/dist/core/teaching.d.ts +0 -1
  260. package/dist/core/teaching.d.ts.map +1 -1
  261. package/dist/core/teaching.js +2 -8
  262. package/dist/core/teaching.js.map +1 -1
  263. package/dist/core/token-ruler.d.ts.map +1 -1
  264. package/dist/core/token-ruler.js +3 -16
  265. package/dist/core/token-ruler.js.map +1 -1
  266. package/dist/core/turn-scheduler.d.ts +1 -1
  267. package/dist/core/turn-scheduler.d.ts.map +1 -1
  268. package/dist/core/turn-scheduler.js +1 -1
  269. package/dist/core/turn-scheduler.js.map +1 -1
  270. package/dist/core/worker-cap.d.ts +2 -4
  271. package/dist/core/worker-cap.d.ts.map +1 -1
  272. package/dist/core/worker-cap.js +13 -4
  273. package/dist/core/worker-cap.js.map +1 -1
  274. package/dist/core/{run-ops.sql → worker-ops.sql} +14 -14
  275. package/dist/core/workspace-settings.d.ts.map +1 -1
  276. package/dist/core/workspace-settings.js +17 -9
  277. package/dist/core/workspace-settings.js.map +1 -1
  278. package/dist/digest/Digest.d.ts +2 -1
  279. package/dist/digest/Digest.d.ts.map +1 -1
  280. package/dist/digest/Digest.js +510 -149
  281. package/dist/digest/Digest.js.map +1 -1
  282. package/dist/digest/digest.sql +23 -10
  283. package/dist/index.d.ts.map +1 -1
  284. package/dist/index.js +3 -5
  285. package/dist/index.js.map +1 -1
  286. package/dist/observe/api.d.ts +6 -0
  287. package/dist/observe/api.d.ts.map +1 -0
  288. package/dist/observe/api.js +11 -0
  289. package/dist/observe/api.js.map +1 -0
  290. package/dist/observe/init.d.ts +7 -0
  291. package/dist/observe/init.d.ts.map +1 -0
  292. package/dist/observe/init.js +159 -0
  293. package/dist/observe/init.js.map +1 -0
  294. package/dist/observe/metrics.d.ts +6 -0
  295. package/dist/observe/metrics.d.ts.map +1 -0
  296. package/dist/observe/metrics.js +15 -0
  297. package/dist/observe/metrics.js.map +1 -0
  298. package/dist/observe/spans.d.ts +4 -0
  299. package/dist/observe/spans.d.ts.map +1 -0
  300. package/dist/observe/spans.js +51 -0
  301. package/dist/observe/spans.js.map +1 -0
  302. package/dist/schemes/EffectPolicy.d.ts +1 -0
  303. package/dist/schemes/EffectPolicy.d.ts.map +1 -1
  304. package/dist/schemes/EffectPolicy.js +5 -2
  305. package/dist/schemes/EffectPolicy.js.map +1 -1
  306. package/dist/schemes/Exec.d.ts +9 -14
  307. package/dist/schemes/Exec.d.ts.map +1 -1
  308. package/dist/schemes/Exec.js +402 -262
  309. package/dist/schemes/Exec.js.map +1 -1
  310. package/dist/schemes/ExecOutputScheme.d.ts +9 -9
  311. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  312. package/dist/schemes/ExecOutputScheme.js +49 -18
  313. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  314. package/dist/schemes/File.d.ts +7 -13
  315. package/dist/schemes/File.d.ts.map +1 -1
  316. package/dist/schemes/File.js +280 -151
  317. package/dist/schemes/File.js.map +1 -1
  318. package/dist/schemes/Log.d.ts +8 -12
  319. package/dist/schemes/Log.d.ts.map +1 -1
  320. package/dist/schemes/Log.js +402 -233
  321. package/dist/schemes/Log.js.map +1 -1
  322. package/dist/schemes/Log.sql +71 -32
  323. package/dist/schemes/Prompt.d.ts +3 -3
  324. package/dist/schemes/Prompt.d.ts.map +1 -1
  325. package/dist/schemes/Prompt.js +10 -10
  326. package/dist/schemes/Prompt.js.map +1 -1
  327. package/dist/schemes/Skill.d.ts +2 -3
  328. package/dist/schemes/Skill.d.ts.map +1 -1
  329. package/dist/schemes/Skill.js +2 -6
  330. package/dist/schemes/Skill.js.map +1 -1
  331. package/dist/schemes/Worker.d.ts +11 -19
  332. package/dist/schemes/Worker.d.ts.map +1 -1
  333. package/dist/schemes/Worker.js +210 -89
  334. package/dist/schemes/Worker.js.map +1 -1
  335. package/dist/schemes/_entry-chunk.d.ts.map +1 -1
  336. package/dist/schemes/_entry-chunk.js +12 -5
  337. package/dist/schemes/_entry-chunk.js.map +1 -1
  338. package/dist/schemes/_entry-crud.d.ts +11 -9
  339. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  340. package/dist/schemes/_entry-crud.js +72 -13
  341. package/dist/schemes/_entry-crud.js.map +1 -1
  342. package/dist/schemes/_entry-crud.sql +28 -10
  343. package/dist/schemes/_entry-find.d.ts +49 -18
  344. package/dist/schemes/_entry-find.d.ts.map +1 -1
  345. package/dist/schemes/_entry-find.js +348 -195
  346. package/dist/schemes/_entry-find.js.map +1 -1
  347. package/dist/schemes/_entry-find.sql +20 -8
  348. package/dist/schemes/_entry-graph.d.ts +4 -2
  349. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  350. package/dist/schemes/_entry-graph.js +55 -31
  351. package/dist/schemes/_entry-graph.js.map +1 -1
  352. package/dist/schemes/_entry-graph.sql +37 -22
  353. package/dist/schemes/_entry-manifest.d.ts +10 -15
  354. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  355. package/dist/schemes/_entry-manifest.js +50 -268
  356. package/dist/schemes/_entry-manifest.js.map +1 -1
  357. package/dist/schemes/_entry-ops.d.ts +14 -15
  358. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  359. package/dist/schemes/_entry-ops.js +121 -110
  360. package/dist/schemes/_entry-ops.js.map +1 -1
  361. package/dist/schemes/_entry-ops.sql +2 -3
  362. package/dist/schemes/_entry-semantic.d.ts +22 -9
  363. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  364. package/dist/schemes/_entry-semantic.js +127 -67
  365. package/dist/schemes/_entry-semantic.js.map +1 -1
  366. package/dist/schemes/_entry-semantic.sql +63 -45
  367. package/dist/schemes/_entry-send.d.ts +3 -4
  368. package/dist/schemes/_entry-send.d.ts.map +1 -1
  369. package/dist/schemes/_entry-send.js +48 -23
  370. package/dist/schemes/_entry-send.js.map +1 -1
  371. package/dist/schemes/_path-scope.d.ts +23 -0
  372. package/dist/schemes/_path-scope.d.ts.map +1 -0
  373. package/dist/schemes/_path-scope.js +61 -0
  374. package/dist/schemes/_path-scope.js.map +1 -0
  375. package/dist/schemes/_search-candidate.d.ts +20 -0
  376. package/dist/schemes/_search-candidate.d.ts.map +1 -0
  377. package/dist/schemes/_search-candidate.js +19 -0
  378. package/dist/schemes/_search-candidate.js.map +1 -0
  379. package/dist/schemes/_search-exclusion.d.ts +7 -0
  380. package/dist/schemes/_search-exclusion.d.ts.map +1 -0
  381. package/dist/schemes/_search-exclusion.js +23 -0
  382. package/dist/schemes/_search-exclusion.js.map +1 -0
  383. package/dist/schemes/_search-index.d.ts +6 -0
  384. package/dist/schemes/_search-index.d.ts.map +1 -0
  385. package/dist/schemes/_search-index.js +328 -0
  386. package/dist/schemes/_search-index.js.map +1 -0
  387. package/dist/schemes/cosine.js +1 -1
  388. package/dist/schemes/cosine.js.map +1 -1
  389. package/dist/schemes/exec-abort.js +3 -3
  390. package/dist/schemes/exec-abort.js.map +1 -1
  391. package/dist/schemes/exec-env.js +3 -3
  392. package/dist/schemes/exec-env.js.map +1 -1
  393. package/dist/server/BranchBatches.d.ts +49 -0
  394. package/dist/server/BranchBatches.d.ts.map +1 -0
  395. package/dist/server/BranchBatches.js +592 -0
  396. package/dist/server/BranchBatches.js.map +1 -0
  397. package/dist/server/Daemon.d.ts +39 -71
  398. package/dist/server/Daemon.d.ts.map +1 -1
  399. package/dist/server/Daemon.js +1133 -491
  400. package/dist/server/Daemon.js.map +1 -1
  401. package/dist/server/DaemonModule.d.ts +31 -0
  402. package/dist/server/DaemonModule.d.ts.map +1 -0
  403. package/dist/server/DaemonModule.js +2 -0
  404. package/dist/server/DaemonModule.js.map +1 -0
  405. package/dist/server/branch-batch.sql +154 -0
  406. package/dist/server/client-input.d.ts +13 -0
  407. package/dist/server/client-input.d.ts.map +1 -1
  408. package/dist/server/client-input.js +268 -57
  409. package/dist/server/client-input.js.map +1 -1
  410. package/dist/server/dispatch-as-plurnk.d.ts +1 -1
  411. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  412. package/dist/server/dispatch-as-plurnk.js +15 -7
  413. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  414. package/dist/server/drain.sql +96 -65
  415. package/dist/server/envelope.d.ts +4 -7
  416. package/dist/server/envelope.d.ts.map +1 -1
  417. package/dist/server/envelope.js +118 -95
  418. package/dist/server/envelope.js.map +1 -1
  419. package/dist/server/envelope.sql +18 -23
  420. package/dist/server/exec-poll-backoff.js +1 -1
  421. package/dist/server/exec-poll-backoff.js.map +1 -1
  422. package/dist/server/lifecycle-recovery.sql +112 -2
  423. package/dist/server/logEntry.d.ts +4 -2
  424. package/dist/server/logEntry.d.ts.map +1 -1
  425. package/dist/server/logEntry.js +6 -3
  426. package/dist/server/logEntry.js.map +1 -1
  427. package/dist/server/logEntry.sql +1 -1
  428. package/dist/server/loop-model.d.ts.map +1 -1
  429. package/dist/server/loop-model.js +22 -9
  430. package/dist/server/loop-model.js.map +1 -1
  431. package/dist/server/loopDocs.d.ts.map +1 -1
  432. package/dist/server/loopDocs.js +5 -4
  433. package/dist/server/loopDocs.js.map +1 -1
  434. package/dist/server/seam-entry-read.sql +15 -10
  435. package/dist/server/seam-log-read.sql +2 -2
  436. package/dist/server/seam-loop.sql +1 -1
  437. package/dist/server/seam-proposal-list.sql +17 -7
  438. package/dist/service.d.ts.map +1 -1
  439. package/dist/service.js +79 -61
  440. package/dist/service.js.map +1 -1
  441. package/migrations/001_schema.sql +1010 -0
  442. package/package.json +65 -32
  443. package/dist/core/PluginLoader.d.ts +0 -20
  444. package/dist/core/PluginLoader.d.ts.map +0 -1
  445. package/dist/core/PluginLoader.js +0 -142
  446. package/dist/core/PluginLoader.js.map +0 -1
  447. package/dist/core/TelemetryChannel.d.ts +0 -37
  448. package/dist/core/TelemetryChannel.d.ts.map +0 -1
  449. package/dist/core/TelemetryChannel.js +0 -80
  450. package/dist/core/TelemetryChannel.js.map +0 -1
  451. package/dist/core/path-decode.d.ts +0 -3
  452. package/dist/core/path-decode.d.ts.map +0 -1
  453. package/dist/core/path-decode.js +0 -9
  454. package/dist/core/path-decode.js.map +0 -1
  455. package/dist/core/plugin-attribution.d.ts +0 -5
  456. package/dist/core/plugin-attribution.d.ts.map +0 -1
  457. package/dist/core/plugin-attribution.js +0 -39
  458. package/dist/core/plugin-attribution.js.map +0 -1
  459. package/dist/core/world-state.d.ts +0 -10
  460. package/dist/core/world-state.d.ts.map +0 -1
  461. package/dist/core/world-state.js +0 -35
  462. package/dist/core/world-state.js.map +0 -1
  463. package/dist/core/world-state.sql +0 -35
  464. package/dist/core/zero-pin.d.ts +0 -3
  465. package/dist/core/zero-pin.d.ts.map +0 -1
  466. package/dist/core/zero-pin.js +0 -16
  467. package/dist/core/zero-pin.js.map +0 -1
  468. package/dist/server/auto.d.ts +0 -6
  469. package/dist/server/auto.d.ts.map +0 -1
  470. package/dist/server/auto.js +0 -63
  471. package/dist/server/auto.js.map +0 -1
  472. package/dist/server/clientTurn.d.ts +0 -6
  473. package/dist/server/clientTurn.d.ts.map +0 -1
  474. package/dist/server/clientTurn.js +0 -22
  475. package/dist/server/clientTurn.js.map +0 -1
  476. package/dist/server/clientTurn.sql +0 -10
  477. package/dist/server/noProposals.d.ts +0 -6
  478. package/dist/server/noProposals.d.ts.map +0 -1
  479. package/dist/server/noProposals.js +0 -37
  480. package/dist/server/noProposals.js.map +0 -1
  481. package/dist/server/version-info.d.ts +0 -14
  482. package/dist/server/version-info.d.ts.map +0 -1
  483. package/dist/server/version-info.js +0 -69
  484. package/dist/server/version-info.js.map +0 -1
  485. package/migrations/0000-00-00.01_schema.sql +0 -536
  486. package/migrations/0004_loop-provider.sql +0 -3
  487. package/migrations/0005_subscription-published-channel.sql +0 -3
  488. package/migrations/0006_loop-max-turns.sql +0 -3
@@ -4,17 +4,12 @@
4
4
  // digest tool imports it to write byte-identical packetNNN.{system,user}.md
5
5
  // files. No second implementation, no drift.
6
6
  //
7
- // Format: markdown. user picked it over rummy's XML and JSON alternatives
8
- // 2026-05-22. Standard markdown idioms only — headers as section delimiters,
9
- // fenced code blocks for entry bodies, lists for arrays. No invented
10
- // separators. Models parse markdown natively.
11
- //
12
- // Section headers follow the `## Plurnk Service X` convention so the model
13
- // sees consistent framing across every section it might receive. Sections
14
- // with no content are omitted entirely (no empty headers in the wire).
15
- import { MimetypeBinary } from "../content/index.js";
16
- import { renderAddress } from "./plurnk-uri.js";
17
- import { encodePathParens } from "./path-decode.js";
7
+ // Format and omission rules are owned by {§packet-markdown}. Section producers
8
+ // supply names and typed content; this projection preserves their ordered evidence.
9
+ import { Validator } from "@plurnk/plurnk-contracts";
10
+ import { renderTarget } from "./plurnk-uri.js";
11
+ import LogBody from "./LogBody.js";
12
+ import { assertEditReceipt, assertResourceEffects, } from "../content/index.js";
18
13
  const editReceiptRevisionChars = () => {
19
14
  const raw = process.env.PLURNK_SERVICE_EDIT_RECEIPT_REVISION_CHARS;
20
15
  const value = Number(raw);
@@ -23,9 +18,22 @@ const editReceiptRevisionChars = () => {
23
18
  }
24
19
  return value;
25
20
  };
21
+ const previewBounds = () => {
22
+ const rawLines = process.env.PLURNK_SERVICE_PREVIEW_LINES;
23
+ const rawChars = process.env.PLURNK_SERVICE_PREVIEW_CHARS;
24
+ const lines = Number(rawLines);
25
+ const chars = Number(rawChars);
26
+ if (!Number.isSafeInteger(lines) || lines < 1) {
27
+ throw new Error(`PLURNK_SERVICE_PREVIEW_LINES must be a positive safe integer, got ${JSON.stringify(rawLines)}`);
28
+ }
29
+ if (!Number.isSafeInteger(chars) || chars < 1) {
30
+ throw new Error(`PLURNK_SERVICE_PREVIEW_CHARS must be a positive safe integer, got ${JSON.stringify(rawChars)}`);
31
+ }
32
+ return { lines, chars };
33
+ };
26
34
  export default class PacketWire {
27
- // Render the sections in `slot` to one ChatMessage.content string. Sections
28
- // render in list order; empties are omitted (no empty headers on the wire);
35
+ // {§packet-markdown} Render the sections in `slot` to one ChatMessage.content
36
+ // string. Sections render in list order; empties are omitted (no empty headers on the wire);
29
37
  // each is `## {header}\n\n{content}` (or bare content when header is null),
30
38
  // trailing newlines stripped, joined with a blank line.
31
39
  static renderSlot(sections, slot) {
@@ -45,15 +53,35 @@ export default class PacketWire {
45
53
  const header = typeof s.header === "string" && s.header.length > 0 ? s.header : null;
46
54
  return (header ? `## ${header}\n\n${s.content}` : s.content).replace(/\n+$/, "");
47
55
  }
48
- // The errors section content: the structured telemetry events rendered to
49
- // meta lines (+ snippet blocks). "" when empty (the section is omitted). The
50
- // events are ALSO kept structured on the packet (packet.telemetryErrors) —
51
- // ephemeral (the buffer drains on read), so the packet is their only home.
52
- static renderErrors(errors) {
53
- const events = Array.isArray(errors) ? errors : [];
54
- return events.length > 0 ? PacketWire.#renderTelemetryErrors(events) : "";
56
+ // Durable operation failures render as terse pointers to the log rows that
57
+ // own their exact RFC 9457 results.
58
+ static renderFailurePointers(failures) {
59
+ const rows = Array.isArray(failures) ? failures : [];
60
+ return rows
61
+ .filter((row) => typeof row.status === "number" && typeof row.coordinate === "string")
62
+ .map((row) => `* ${row.status} log:///${row.coordinate}`)
63
+ .join("\n");
55
64
  }
56
- // The Child Streams / Child Runs sections (§child-orientation) — the OPPOSITE of advice: terse
65
+ // Non-terminal model-facing observations are deliberately separate from
66
+ // operation failures. Producer messages are normalized and bounded by the
67
+ // shared preview limit; typed positions remain legible.
68
+ static renderNotices(notices) {
69
+ const observations = Array.isArray(notices) ? notices : [];
70
+ return observations.map((notice) => {
71
+ const kind = typeof notice.kind === "string" ? notice.kind : "notice";
72
+ const rawMessage = typeof notice.message === "string"
73
+ ? notice.message.replace(/\s+/g, " ").trim()
74
+ : "";
75
+ const message = rawMessage.length > 0
76
+ ? PacketWire.#preview(rawMessage).text
77
+ : "";
78
+ const position = notice.position?.type === "content-offset"
79
+ ? ` @ ${String(notice.position.line)}:${String(notice.position.column)}`
80
+ : "";
81
+ return `* ${kind}${message.length > 0 ? `: ${message}` : ""}${position}`;
82
+ }).join("\n");
83
+ }
84
+ // The Child Streams / Active Child Workers sections ({§child-orientation}) — the OPPOSITE of advice: terse
57
85
  // `* <status> <path>` pointers (same shape as the errors section) to the live things the worker holds,
58
86
  // so the model SEES its open streams + unconcluded workers each turn and reasons for itself (READ /
59
87
  // OPEN / KILL via the path). Orienting state, never an instruction. "" when none → section omitted.
@@ -62,10 +90,14 @@ export default class PacketWire {
62
90
  return items.map((r) => `* ${String(r.status)} ${String(r.path)}`).join("\n");
63
91
  }
64
92
  // The git section content: the working-tree summary. "" when absent.
65
- static renderGit(git) {
66
- return git === null || git === undefined ? "" : PacketWire.#renderGitState(git);
93
+ static renderGit(git, assignedBranch = null) {
94
+ const status = git === null || git === undefined ? "" : PacketWire.#renderGitState(git);
95
+ const assignment = assignedBranch === null
96
+ ? ""
97
+ : `assigned branch \`${assignedBranch}\` — commit any project changes and leave the checkout clean before concluding`;
98
+ return [status, assignment].filter((line) => line.length > 0).join("\n");
67
99
  }
68
- // The log section's content: the model's curated rows as a fenced `jsonplurnk` array (§jsonplurnk).
100
+ // The log section's content: the model's curated rows as a fenced `jsonplurnk` array ({§jsonplurnk}).
69
101
  // Data only — no prose leads the fence (the log carries rules for no one). Empty log → ""
70
102
  // (the section is omitted).
71
103
  static renderLog(entries, countTokens) {
@@ -73,9 +105,9 @@ export default class PacketWire {
73
105
  if (log.length === 0)
74
106
  return "";
75
107
  const items = PacketWire.#renderLogEntries(log, countTokens);
76
- // The opening fence is DYNAMIC — one backtick longer than the longest run in any body — so a
77
- // code sample inside a body can never close the block early (CommonMark closes a fence only on
78
- // a line of ≥ its own length). §jsonplurnk-dynamic-fence
108
+ // The opening fence is DYNAMIC — one backtick longer than the longest run in the rendered
109
+ // entries — so a code sample inside a body can never close the block early (CommonMark closes
110
+ // a fence only on a line of ≥ its own length). {§jsonplurnk-dynamic-fence}
79
111
  const longestTicks = Math.max(0, ...[...items.matchAll(/`+/g)].map((m) => m[0].length));
80
112
  const fence = "`".repeat(Math.max(3, longestTicks + 1));
81
113
  return `${fence}jsonplurnk\n[\n${items}\n]\n${fence}`;
@@ -97,64 +129,8 @@ export default class PacketWire {
97
129
  { role: "user", content: PacketWire.renderSlot(sections, "user") },
98
130
  ];
99
131
  }
100
- // Measure the curatable log section's budget subtotals from the STRUCTURED
101
- // log (the foldable unit), using the provider's tokenizer — meta lines and
102
- // fences included, matching what ships. Feeds the per-turn rollup (chronological;
103
- // the grinder folds the newest turn) and the FOLD unit (heaviest entries) — the
104
- // two budget levers the model can pull (§tokenomics {§tokenomics-turn-totals},
105
- // {§tokenomics-largest-entries}). Build-time only; the stored log section is
106
- // the rendered result. §tokenomics-render-weight-budget
107
- static measureLogBudget(entries, countTokens) {
108
- const logEntries = Array.isArray(entries) ? entries : [];
109
- const logBody = logEntries.length > 0 ? PacketWire.renderLog(logEntries, countTokens) : "";
110
- const HEAVIEST_COUNT = 5;
111
- const currentLoop = logEntries.reduce((max, e) => {
112
- const l = typeof e.coordinate === "string" ? Number(e.coordinate.split("/")[0]) : NaN;
113
- return Number.isFinite(l) && l > max ? l : max;
114
- }, 0);
115
- const byTurn = new Map();
116
- const perEntry = [];
117
- for (const e of logEntries) {
118
- // Two weights, two purposes (#466 — one labeled semantic per number): the row's FULL
119
- // render (meta line + fences + body) composes the turn rollup — the room the turn takes in
120
- // the packet; the heaviest list carries the row's BODY weight — the FOLD unit, the SAME
121
- // number the row's own `tokens` shows, so the budget and the log can never disagree
122
- // about one row. A row ranks ONLY when a FOLD would actually reclaim it: a bodyless
123
- // row has nothing to fold, an already-folded row is already reclaimed (its price is
124
- // an OPEN's cost), and the current loop's foisted preview is refused (§prompt-fold-
125
- // illegal) — listing any of these sells the model a lever that 403s or no-ops.
126
- const bodyPrice = [];
127
- const weight = countTokens(PacketWire.#renderLogEntries([e], countTokens, bodyPrice));
128
- const coordinate = typeof e.coordinate === "string" ? e.coordinate : null;
129
- const op = typeof e.op === "string" && e.op.length > 0 ? e.op : null;
130
- // Turn = the loop_seq/turn_seq prefix of the coordinate
131
- // (log:///<loop_seq>/<turn_seq>/<sequence>); the sequence drops off.
132
- if (coordinate !== null) {
133
- const turn = coordinate.split("/").slice(0, 2).join("/");
134
- byTurn.set(turn, (byTurn.get(turn) ?? 0) + weight);
135
- }
136
- const path = PacketWire.#entryPath(coordinate, op);
137
- const isPreview = e.target?.scheme === "prompt" && e.origin === "plurnk" && op === "READ"
138
- && coordinate !== null && Number(coordinate.split("/")[0]) === currentLoop;
139
- const foldable = e.folded !== true && !isPreview;
140
- if (path !== null && foldable && (bodyPrice[0] ?? 0) > 0)
141
- perEntry.push({ path, tokens: bodyPrice[0] });
142
- }
143
- return {
144
- entries: logEntries.length,
145
- tokens: logBody ? countTokens(`## Plurnk Service Log\n\n${logBody}`) : 0,
146
- byTurn: [...byTurn.entries()]
147
- .map(([turn, tokens]) => ({ turn, tokens }))
148
- .toSorted((a, b) => {
149
- const [al, at] = a.turn.split("/").map(Number);
150
- const [bl, bt] = b.turn.split("/").map(Number);
151
- return al - bl || at - bt;
152
- }),
153
- largest: perEntry.toSorted((a, b) => b.tokens - a.tokens).slice(0, HEAVIEST_COUNT),
154
- };
155
- }
156
132
  // Number each line of body as `<N>:<line>` — a bare `N:` prefix, NO separator whitespace
157
- // (#564, owner policy pivot): the leading digit prevents column-zero fence collisions and gives
133
+ // ({§render-rule-line-navigable-prefix}): the leading digit prevents column-zero fence collisions and gives
158
134
  // the model line refs for free (`READ<42-46>`), while the absence of any separator means a
159
135
  // reproduced line has nothing between `N:` and the content to copy — the hard-tab separator used
160
136
  // to leak into edit bodies and corrupt indentation. The content's OWN leading whitespace is
@@ -162,32 +138,25 @@ export default class PacketWire {
162
138
  // Used for READ@200 content; index-preview numbering is the framework's
163
139
  // job now (baked into the preview string — see renderHeredoc).
164
140
  static #numberLines(body, start = 1) {
165
- if (!body)
166
- return "";
167
- const trailingNewline = body.endsWith("\n");
168
- const source = trailingNewline ? body.slice(0, -1) : body;
169
- const numbered = source.split("\n").map((line, i) => `${start + i}:${line}`).join("\n");
170
- return trailingNewline ? `${numbered}\n` : numbered;
141
+ let line = start;
142
+ return body.replace(/(^|\r\n|\r|\n)(?=[\s\S])/g, (separator) => `${separator}${line++}:`);
171
143
  }
172
144
  // The single content-body renderer EVERY output-emitting op routes through, so the line-number
173
- // convention the model orients on can't drift: line-navigable mimetypes (text/*) get the `N:`
174
- // prefix from `startLine`; tree-navigable (JSON/XML/HTML) render verbatim so jsonpath/xpath
175
- // isn't shifted. Empty content ⇒ "" (the meta line stands alone). §render-rule-line-navigable-prefix
176
- static #renderContentBody(fence, content, mimetype, startLine = 1) {
145
+ // convention the model orients on can't drift. Every textual body receives
146
+ // the `N:` prefix from `startLine`; matchers consume canonical content before
147
+ // this presentation projection. Empty content produces no body.
148
+ static #renderContentBody(fence, content, startLine = 1) {
177
149
  if (content.length === 0)
178
150
  return "";
179
- // Line-navigable text gets the `N:` source-line prefix from startLine. `startLine === null`
180
- // means the content is ALREADY source-numbered — a matcher result whose lines carry their own
181
- // (non-contiguous) source numbers like `143:…`; re-numbering would double it to `1:143:…`
182
- // (one source-line prefix). Render verbatim. §render-rule-line-navigable-prefix
183
- const rendered = MimetypeBinary.isLineNavigableMimetype(mimetype) && startLine !== null
151
+ // `startLine === null` means the producer already supplied numbered
152
+ // content; re-numbering would duplicate its coordinates.
153
+ const rendered = startLine !== null
184
154
  ? PacketWire.#numberLines(content, startLine)
185
155
  : content;
186
156
  return PacketWire.#wrapHeredocBody(fence, rendered);
187
157
  }
188
- // Tolerant JSON parser for log entries' rx/tx fields. The engine
189
- // pre-parses application/json mimetypes, but render may also receive
190
- // strings (legacy paths, manual tests). Returns null on parse failure.
158
+ // Tolerant JSON parser for log entries' persisted rx/tx strings. The engine
159
+ // pre-parses application/json mimetypes; malformed stored text is not JSON.
191
160
  static #safeParse(s) {
192
161
  try {
193
162
  return JSON.parse(s);
@@ -197,8 +166,7 @@ export default class PacketWire {
197
166
  }
198
167
  }
199
168
  // Stable JSON: keys sorted alphabetically so the same meta produces the
200
- // same string across turns — prefix-cache friendly. Mirrors rummy
201
- // plugins/helpers.js canonicalJson.
169
+ // same string across turns — prefix-cache friendly.
202
170
  static #canonicalJson(obj) {
203
171
  const keys = Object.keys(obj).sort();
204
172
  const sorted = {};
@@ -206,107 +174,81 @@ export default class PacketWire {
206
174
  sorted[k] = obj[k];
207
175
  return JSON.stringify(sorted);
208
176
  }
209
- // Wrap a body in heredoc fences. Leading `\n` always (separates the
210
- // opening fence from the first body character — necessary because
211
- // numbered bodies start with `1:…` which would otherwise collide
212
- // visually with the `:::FENCE` markers). Trailing `\n` only when the
213
- // body doesn't already end with one — otherwise you get a doubled
214
- // newline that renders as a blank line before the closing fence, which
215
- // reads as "the content has a trailing blank line" when actually it
177
+ static #receiptMeta(value) {
178
+ const receipt = assertEditReceipt(value);
179
+ const head = {
180
+ rev: receipt.revision.slice(0, editReceiptRevisionChars()),
181
+ extent: `${receipt.unit} ${receipt.before}->${receipt.after}`,
182
+ };
183
+ if ("effect" in receipt) {
184
+ return {
185
+ ...head,
186
+ change: `-${receipt.effect.removed} +${receipt.effect.inserted}`,
187
+ range: `${receipt.effect.requested} ${receipt.effect.source}->${receipt.effect.result}`,
188
+ };
189
+ }
190
+ return {
191
+ ...head,
192
+ disposition: receipt.disposition,
193
+ requested: receipt.requested,
194
+ ...(receipt.replacement === undefined
195
+ ? {}
196
+ : {
197
+ change: `-${receipt.replacement.removed} +${receipt.replacement.inserted}`,
198
+ replacement: `${receipt.replacement.requested} ${receipt.replacement.source}->${receipt.replacement.result}`,
199
+ }),
200
+ };
201
+ }
202
+ // Wrap a body in heredoc fences. The fence is FIXED (`<<BODY`/`BODY`), never the
203
+ // target: every body line is `N:`-prefixed, so a bare fence line can never occur
204
+ // inside a body, and a fixed fence gives the model nothing address-shaped to
205
+ // mimic back as op syntax — identity lives in the adjacent meta JSON. Leading
206
+ // `\n` always (separates the opening fence from the first body character).
207
+ // Trailing `\n` only when the body doesn't already end with one — otherwise you
208
+ // get a doubled newline that renders as a blank line before the closing fence,
209
+ // which reads as "the content has a trailing blank line" when actually it
216
210
  // doesn't. The body's own whitespace decides the shape.
217
211
  static #wrapHeredocBody(fence, body) {
218
212
  const sep = body.endsWith("\n") ? "" : "\n";
219
- return `<<:::${fence}\n${body}${sep}:::${fence}`;
220
- }
221
- // Heredoc block for one channel of one entry. Fence is `URI#channel`
222
- // (the `<<:::FENCE` packet-rendering marker per wrapHeredocBody — a
223
- // projection is read-only context, NOT an emittable op, so it must not
224
- // wear the DSL op-fence; that conflation was the demo.sh corruption bug).
225
- // When `channel` is null/empty the fence is path-only —
226
- // this is the default-channel convention: the absence of `#channel` is
227
- // the addressing of the scheme's default channel, not a missing field.
228
- // Body is a mimetypes preview, rendered VERBATIM — the framework owns its
229
- // formatting (N: line numbers for text, source-annotated outline for
230
- // symbols, correct start-line for tail slices) and bakes it into the
231
- // preview string as of mimetypes 0.7.3, so the service must not re-number
232
- // it (re-numbering would double-prefix text and mis-number symbol
233
- // outlines — plurnk-mimetypes#8).
234
- static #renderHeredoc(uri, channel, body) {
235
- const fence = channel ? `${uri}#${channel}` : uri;
236
- return PacketWire.#wrapHeredocBody(fence, body);
237
- }
238
- // Render a (scheme, pathname) tuple as the URI the model should SEE.
239
- // Null scheme → bare pathname. The `file` scheme never reaches this
240
- // function because Dispatcher.#extractTarget normalizes it to null at the
241
- // storage boundary; storage and wire output are uniform on this.
242
- static #renderModelUri(scheme, pathname) {
243
- const path = pathname ?? "";
244
- // #370 — a null scheme is a workspace file, whose member key is /rel but whose MODEL-FACING
245
- // form is the bare relative path (the catalog lists data/users.json; FIND returns it; the
246
- // model types it). Rendering /data/users.json minted a second spelling of the same file.
247
- if (scheme === null || scheme === undefined)
248
- return encodePathParens(path.replace(/^\//, ""));
249
- return renderAddress(scheme, path);
213
+ return `<<BODY\n${body}${sep}BODY`;
250
214
  }
251
215
  // Render one Log entry → a single bullet line carrying the meta JSON.
252
216
  // No body, no fence — every meaningful field is in the JSON. Naming
253
217
  // follows the uniform principle: `path` is identity (this log row's
254
- // own URI), `target` is the URI the action acted on. COPY/MOVE add
255
- // `source`; currently the engine emits target only (source plumbing
256
- // pending the COPY/MOVE-specific log shape pass).
218
+ // own URI), `target` is the URI in the statement's target slot. COPY/MOVE
219
+ // retain their source and destination selections separately from ordered
220
+ // applied `effects`.
257
221
  //
258
- // On error, status >= 400 signals the failure; the message lives in
259
- // the next packet's user.telemetry.errors[] per SPEC §telemetry. (Forward:
260
- // meta will gain tokensBefore/After + linesBefore/After to convey
261
- // change scope without carrying the body content.)
222
+ // On error, status >= 400 signals the failure; Problem Details live on
223
+ // this durable row and the next packet's Errors section points here.
262
224
  //
263
- // Per-entry render: one meta JSON line plus a body block that the model
264
- // can read to know what it did. Two body cases:
265
- // 1. READ@200 with content → render rx.content under the target fence.
266
- // The model asked for content; show it the content. (Matcher is in
267
- // meta.matcher, count is in meta.matches.)
268
- // 2. Every other op → re-emit tx as a heredoc in the model's native
269
- // syntax. The model wrote this; mirror it back so the log is a true
270
- // record of its actions instead of a row of opaque status codes.
271
- // The log:/// handle the model sees for an entry — its FOLD target
272
- // (§open-fold) and the label the budget's heaviest-entries readout reuses,
273
- // so the readout names an entry exactly as the log does.
225
+ // Per-entry render: one meta JSON line plus the row's canonical body.
226
+ // LogBody owns tx/rx storage interpretation; packet projection owns only
227
+ // visibility, previewing, mimetype rendering, and metadata.
228
+ // The log:/// handle the model sees for an entry.
229
+ // ({§open-fold}).
274
230
  static #entryPath(coordinate, op) {
275
231
  if (coordinate === null)
276
232
  return null;
277
233
  return op !== null ? `log:///${coordinate}/${op}` : `log:///${coordinate}`;
278
234
  }
279
- // `collectBodyTokens`, when supplied, receives each row's body weight (the rendered meta.tokens)
280
- // in order — the FOLD unit measureLogBudget ranks, guaranteed identical to what the row shows.
281
- // §arrival-law (#499) — the pushed-lane bound: another actor's text rides OPEN only up to
282
- // the preview (N lines AND 80×N chars — the char cap guards single-line bombs); over, the
283
- // head rides with the cut stated and the address for a deliberate pull. run111 entry 56:
284
- // a child's ratified 19,363-token deliverable landed whole in its parent and cascaded.
285
- static #arrivalPreview(text) {
286
- const maxLines = Number(process.env.PLURNK_SERVICE_ARRIVAL_PREVIEW_LINES ?? "16");
287
- const maxChars = 80 * maxLines;
288
- const lines = text.split("\n");
289
- if (lines.length <= maxLines && text.length <= maxChars)
290
- return { text, cut: false };
291
- const head = lines.slice(0, maxLines).join("\n").slice(0, maxChars);
292
- return { text: head, cut: true };
293
- }
294
- // §arrival-law (#566): a model-COMPOSED op body — a PLAN/SEND/WORK/FORK's text or an EXEC
295
- // command — renders PREVIEW-bounded, so the model can never compose an unbounded OPEN log row.
296
- // run42: an unclosed `<<PLAN` swallowed a 57k-char runaway into ONE 29k open row the grinder is
297
- // forbidden to fold (op NOT IN … 'PLAN') — a permanent budget bomb that pushed the packet past
298
- // the physical window and gated out the recovery. CONTENT ops are exempt and render FULL —
299
- // READ/FIND (retrieved bytes; capping a READ would break its `<start,end>` slice contract) and
300
- // EDIT/COPY/MOVE spans (the resulting file content the model inspects). The verbatim emission
301
- // always survives in the folded `model` mirror, so nothing is lost; a cut states the true extent.
302
- static #renderAuthoredBody(logAddr, body, preNumbered) {
303
- const arrival = PacketWire.#arrivalPreview(body);
304
- const rendered = preNumbered
305
- ? PacketWire.#wrapHeredocBody(logAddr, arrival.text)
306
- : PacketWire.#renderContentBody(logAddr, arrival.text, "text/plain");
307
- return arrival.cut ? `${rendered}\n… preview — the full body is ${body.split("\n").length} line(s)` : rendered;
235
+ // One preview function for every bounded model-facing projection. Lines
236
+ // protect ordinary documents and chars protect a single-line bomb.
237
+ static #preview(text) {
238
+ const { lines: maxLines, chars: maxChars } = previewBounds();
239
+ let lineEnd = text.length;
240
+ let newline = -1;
241
+ for (let line = 0; line < maxLines; line++) {
242
+ newline = text.indexOf("\n", newline + 1);
243
+ if (newline === -1)
244
+ break;
245
+ if (line === maxLines - 1)
246
+ lineEnd = newline + 1;
247
+ }
248
+ const end = Math.min(text.length, lineEnd, maxChars);
249
+ return { text: text.slice(0, end), cut: end < text.length };
308
250
  }
309
- static #renderLogEntries(entries, countTokens, collectBodyTokens) {
251
+ static #renderLogEntries(entries, countTokens) {
310
252
  return entries.map((e) => {
311
253
  const meta = {};
312
254
  const coordinate = typeof e.coordinate === "string" ? e.coordinate : null;
@@ -322,214 +264,197 @@ export default class PacketWire {
322
264
  && e.attrs !== null && typeof e.attrs === "object"
323
265
  && e.attrs.kind === "entry_materialized";
324
266
  const renderedOp = materializedEntry ? "READ" : op;
267
+ const modelEmission = op === null
268
+ && e.attrs !== null && typeof e.attrs === "object"
269
+ && e.attrs.kind === "model_emission";
325
270
  const path = PacketWire.#entryPath(coordinate, renderedOp);
326
271
  if (path !== null)
327
272
  meta.path = path;
328
273
  if (typeof e.origin === "string")
329
274
  meta.origin = e.origin;
330
- // §env-delta: the environment-delta cause (a sibling run or a scheme),
331
- // rendered when present; absent ⇒ the owning run itself (self).
275
+ // {§env-delta-attribution}: render the causal worker address or
276
+ // subsystem token when present; absence means the owning worker.
332
277
  if (typeof e.source === "string" && e.source.length > 0)
333
- meta.run = e.source;
278
+ meta.source = e.source;
334
279
  if (renderedOp !== null)
335
280
  meta.op = renderedOp;
281
+ if (modelEmission)
282
+ meta.kind = "model_emission";
283
+ if (e.source === "file" && e.attrs !== null && typeof e.attrs === "object" && "git" in e.attrs) {
284
+ const git = e.attrs.git;
285
+ if (typeof git !== "string" || git.length !== 2) {
286
+ throw new TypeError("A source=file log row carries malformed Git XY metadata.");
287
+ }
288
+ meta.git = git;
289
+ }
336
290
  if (typeof e.status === "number")
337
291
  meta.status = e.status;
292
+ const tx = (typeof e.tx === "string" ? PacketWire.#safeParse(e.tx) : e.tx);
338
293
  const target = PacketWire.#renderActionTarget(e.target);
339
- if (target !== null)
340
- meta.target = target;
341
- // EXEC's output is a separate stream entry (§exec-stream); its address rides in a
294
+ if (op === "COPY" || op === "MOVE") {
295
+ const source = PacketWire.#renderSelection(e.target ?? tx?.target, tx?.lineMarker);
296
+ const destinationBody = tx?.body !== null && typeof tx?.body === "object"
297
+ ? tx.body
298
+ : null;
299
+ const destination = PacketWire.#renderSelection(destinationBody?.target, destinationBody?.lineMarker);
300
+ if (source !== null)
301
+ meta.source = source;
302
+ if (destination !== null)
303
+ meta.destination = destination;
304
+ if (typeof e.status === "number"
305
+ && e.status < 400
306
+ && (source === null || destination === null)) {
307
+ throw new Error(`A successful ${op} log row must retain both operand selections.`);
308
+ }
309
+ }
310
+ else {
311
+ if (target !== null)
312
+ meta.target = target;
313
+ }
314
+ // EXEC's output is a separate stream entry ({§exec-stream}); its address rides in a
342
315
  // `stream` link, distinct from `target` (the cwd / executable path it ran in).
343
316
  if (op === "EXEC" && e.attrs !== null && typeof e.attrs === "object" && typeof e.attrs.stream === "string") {
344
317
  meta.stream = e.attrs.stream;
345
318
  }
346
319
  // Parse rx once — reused for the matcher/items enrichment and the body.
347
320
  const rx = (typeof e.rx === "string" ? PacketWire.#safeParse(e.rx) : e.rx);
348
- // §log-row-self-explains — a FAILED op row carries its failure message ON THE META LINE,
349
- // so the record explains itself in every packet, folded or open. The old shape (a bare
350
- // status; the message buried in an rx no render shows) is what sent the wildcard model
351
- // theorizing "SEND[409] probably means bad request?" for 201s, and the jumbo model
352
- // chasing a phantom "engine error" off a message-less item. The row IS the model's op
353
- // result — it states its own why; the errors section stays a terse pointer at it.
321
+ // {§log-row-self-explains} — a failed operation projects its exact
322
+ // Problem on the row meta line; Errors remains only an index.
354
323
  if (typeof e.status === "number" && e.status >= 400 && rx !== null && typeof rx === "object") {
355
- const why = rx;
356
- const text = typeof why.error === "string" ? why.error : typeof why.reason === "string" ? why.reason : typeof why.message === "string" ? why.message : null;
357
- if (text !== null && text.length > 0)
358
- meta.error = text;
324
+ const problem = rx.problem;
325
+ Validator.assertProblemDetails(problem);
326
+ meta.problem = problem;
359
327
  }
360
- // READ + FIND enrichment: the matcher body (from tx) + `items`, the count
361
- // of rows the op returned — a matcher's hits, or a bare catalog FIND's entry
362
- // count. Without it the model can't tell "0 results" from "empty content",
363
- // nor weigh a re-FIND. `matches` when the dispatch tallied it; else a FIND
364
- // body is a JSON array whose length IS the count.
365
- let items = null;
328
+ // {§retrieval-packet-metadata}: one extent/coordinate owner plus
329
+ // only FIND aggregates that add information beyond that extent.
330
+ let findItems = null;
366
331
  if (op === "READ" || op === "FIND") {
367
- const tx = e.tx;
368
- if (tx !== null && tx !== undefined && typeof tx === "object" && tx.body !== null && typeof tx.body === "object") {
369
- if (typeof tx.body.raw === "string")
370
- meta.matcher = tx.body.raw;
371
- }
372
- if (op === "FIND" && rx !== null && typeof rx === "object" && typeof rx.overflow === "number") {
373
- items = rx.overflow; // §find-count-not-contents — the full match count, though the rows weren't enumerated
374
- }
375
- else if (rx !== null && typeof rx === "object" && typeof rx.matches === "number") {
376
- items = rx.matches;
332
+ const findMatcher = op === "FIND"
333
+ && tx !== null
334
+ && tx !== undefined
335
+ && typeof tx === "object"
336
+ && tx.body !== null
337
+ && typeof tx.body === "object";
338
+ if (findMatcher) {
339
+ const body = tx?.body;
340
+ if (body !== null && typeof body === "object" && typeof body.raw === "string") {
341
+ meta.matcher = body.raw;
342
+ }
377
343
  }
378
- else if (op === "FIND" && rx !== null && typeof rx === "object" && typeof rx.content === "string") {
344
+ if (op === "FIND" && rx !== null && typeof rx === "object" && typeof rx.content === "string") {
379
345
  const parsed = PacketWire.#safeParse(rx.content);
380
346
  if (Array.isArray(parsed))
381
- items = parsed.length;
347
+ findItems = parsed.length;
382
348
  }
383
- // The matched set's content weight (sum of the entries' live channel tokens) — the
384
- // FIND self-describes its hits' READ-weight; carries the per-scheme roll-up in the foist.
385
- if (op === "FIND" && rx !== null && typeof rx === "object" && typeof rx.itemsTokenTotal === "number") {
386
- meta.itemsTokenTotal = rx.itemsTokenTotal;
349
+ const range = rx !== null && typeof rx === "object" && rx.range !== undefined
350
+ ? rx.range
351
+ : undefined;
352
+ const problemOwnsRange = typeof e.status === "number"
353
+ && e.status >= 400
354
+ && meta.problem !== null
355
+ && typeof meta.problem === "object"
356
+ && Object.hasOwn(meta.problem, "range");
357
+ if (problemOwnsRange) {
358
+ Validator.assertRangeExtent(meta.problem.range);
387
359
  }
388
- }
389
- // The foldable body — computed ALWAYS (folded too), because `tokens` measures it: for
390
- // an open row that's what a FOLD would save, for a folded row what an OPEN would cost.
391
- // "" ⇒ genuinely no body (an empty FIND, an empty EDIT span, a @204).
392
- let body = "";
393
- if (op === "FIND" && e.status === 200 && items === 0) {
394
- // Empty FIND result → no body. items:0 already says "empty"; rendering [] under a
395
- // fence is redundant noise. (The always-foisted empty known:///** / unknown:///**
396
- // workspace rows are the common case.)
397
- body = "";
398
- }
399
- else if ((op === "READ" || op === "FIND") && e.status === 200 &&
400
- rx !== null && typeof rx === "object" && typeof rx.content === "string" && rx.content.length > 0) {
401
- // READ@200 / FIND@200: the content READ pulled, or the catalog rows / matched
402
- // entries FIND returned (§render-rule-find-renders-result) — the turn-0 foisted
403
- // FIND(scheme:///**) reaches the packet here, as does the exec-stream injector's
404
- // foisted READ of the channel. #renderContentBody applies the line-number convention
405
- // (§render-rule-line-navigable-prefix / §render-rule-tree-navigable-verbatim).
406
- const mimetype = typeof rx.mimetype === "string" ? rx.mimetype : "text/plain";
407
- // startLine === null is the matcher-result signal (already source-numbered → verbatim);
408
- // a number numbers from it; absent → 1 (whole-content default).
409
- const start = rx.startLine === null ? null : (typeof rx.startLine === "number" ? rx.startLine : 1);
410
- // §arrival-law — a foisted READ of a prompt path is PUSHED content (user- or
411
- // sibling-authored); the render-side preview bounds it like any arrival, closing
412
- // the single-line char-bomb the line-slice alone cannot cut (line markers cannot
413
- // cut mid-line; the render can). Model-authored READs stay self-invited — untouched.
414
- const promptPush = e.origin === "plurnk" && op === "READ"
415
- && e.target !== null && typeof e.target === "object" && e.target.scheme === "prompt";
416
- const streamPush = e.origin === "plurnk" && op === "READ" && mimetype === "text/stream";
417
- if (promptPush || streamPush) {
418
- const arrival = PacketWire.#arrivalPreview(rx.content);
419
- body = PacketWire.#renderContentBody(target ?? `log:///${coordinate}`, arrival.text, mimetype, start);
420
- if (arrival.cut) {
421
- const noun = streamPush ? "stream output" : "prompt";
422
- body += `\n… arrival preview — the full ${noun} is ${rx.content.split("\n").length} line(s), ${rx.content.length} chars: READ ${target}`;
423
- }
360
+ if (range !== undefined && !problemOwnsRange) {
361
+ meta.range = Validator.assertRangeExtent(range);
424
362
  }
425
- else {
426
- body = PacketWire.#renderContentBody(target ?? `log:///${coordinate}`, rx.content, mimetype, start);
363
+ else if (op === "READ" && rx !== null && typeof rx === "object" && rx.region !== undefined) {
364
+ meta.region = Validator.assertTextRegion(rx.region);
427
365
  }
428
- }
429
- else if (op === "EDIT" && rx !== null && typeof rx === "object" && rx.receipt !== null && typeof rx.receipt === "object") {
430
- const receipt = rx.receipt;
431
- const effect = receipt.effect;
432
- if (typeof receipt.revision !== "string" || !/^[a-f0-9]{64}$/.test(receipt.revision)
433
- || (receipt.unit !== "lines" && receipt.unit !== "items")
434
- || typeof receipt.before !== "number" || typeof receipt.after !== "number"
435
- || effect === undefined
436
- || typeof effect.requested !== "string" || typeof effect.source !== "string" || typeof effect.result !== "string"
437
- || typeof effect.removed !== "number" || typeof effect.inserted !== "number" || typeof effect.context !== "string") {
438
- throw new Error("invalid structured EDIT receipt");
366
+ // These are underlying selected-content weights, distinct from
367
+ // the emitted body's generic `tokens` measurement.
368
+ if (op === "FIND" && rx !== null && typeof rx === "object" && typeof rx.itemsTokenTotal === "number" && rx.itemsTokenTotal > 0) {
369
+ meta.itemsTokenTotal = rx.itemsTokenTotal;
370
+ }
371
+ if (op === "FIND"
372
+ && rx !== null
373
+ && typeof rx === "object"
374
+ && typeof rx.returnedItemsTokenTotal === "number"
375
+ && rx.returnedItemsTokenTotal > 0
376
+ && rx.returnedItemsTokenTotal !== rx.itemsTokenTotal) {
377
+ meta.returnedItemsTokenTotal = rx.returnedItemsTokenTotal;
378
+ }
379
+ if (findMatcher
380
+ && range !== null
381
+ && typeof range === "object"
382
+ && range.unit === "resource"
383
+ && rx !== null
384
+ && typeof rx === "object"
385
+ && typeof rx.matchLocationCount === "number"
386
+ && rx.matchLocationCount > 0) {
387
+ meta.matchLocationCount = rx.matchLocationCount;
439
388
  }
440
- meta.rev = receipt.revision.slice(0, editReceiptRevisionChars());
441
- meta.extent = `${receipt.unit} ${receipt.before}→${receipt.after}`;
442
- meta.change = `-${effect.removed} +${effect.inserted}`;
443
- meta.range = `${effect.requested} ${effect.source}→${effect.result}`;
444
- if (effect.context.length > 0)
445
- body = PacketWire.#wrapHeredocBody(target ?? `log:///${coordinate}`, effect.context);
446
- }
447
- else if ((op === "EDIT" || op === "COPY" || op === "MOVE") && rx !== null && typeof rx === "object" && (typeof rx.receipt === "string" || typeof rx.span === "string" || typeof rx.body === "string")) {
448
- // EDIT renders its bounded effect receipt; COPY/MOVE and environment-delta
449
- // EDITs retain their resulting span. Each is already line-addressed where
450
- // appropriate, so wrapping is verbatim.
451
- const r = rx;
452
- const span = typeof r.receipt === "string" ? r.receipt : typeof r.span === "string" ? r.span : (r.body ?? "");
453
- // An EDIT/COPY/MOVE span is the RESULTING file content — bytes the model inspects to
454
- // confirm its edit landed, CONTENT not composed directive text — so it renders FULL
455
- // like READ/FIND (#566). Grinder-foldable (not PLAN-exempt), so a large span reclaims.
456
- if (span.length > 0)
457
- body = PacketWire.#wrapHeredocBody(target ?? `log:///${coordinate}`, span);
458
389
  }
459
- else if (op === "EXEC" && e.tx !== null && e.tx !== undefined && typeof e.tx.body === "string") {
460
- // EXEC: the literal command body, :::-fenced + line-numbered at the op's log address —
461
- // the model sees what it ran and can reference lines of its own code. The OUTPUT is a
462
- // SEPARATE stream (meta.stream), surfaced by the injector, never re-emitted here. §exec-stream
463
- body = PacketWire.#renderAuthoredBody(path ?? `log:///${coordinate}`, e.tx.body, false);
390
+ // {§edit-result-receipt-projection} {§edit-result-copy-move-effects}
391
+ // Mutations expose compact, validated outcome metadata. EDIT owns
392
+ // one receipt; COPY/MOVE own ordered resource effects whose
393
+ // optional receipts describe scoped textual materializations.
394
+ if (op === "EDIT" && rx !== null && typeof rx === "object" && Object.hasOwn(rx, "receipt")) {
395
+ Object.assign(meta, PacketWire.#receiptMeta(rx.receipt));
464
396
  }
465
- else if ((op === "PLAN" || op === "SEND" || op === "WORK" || op === "FORK") && e.tx !== null && e.tx !== undefined) {
466
- // PLAN's plan / SEND's message / WORK's & FORK's seed task ride into the log as N:
467
- // content at the op's log address — a dispatch's record IS the task it dispatched, so
468
- // the next turn reads what each worker is doing (the spawn-then-retask confusion was
469
- // this body missing: the log showed "spawned worker-db" with no task). The log
470
- // mirrors the model's WORK, NEVER a repeated <<OP:…:OP tag (tags are emission
471
- // syntax, not the log paradigm). Bodyless ops (COPY/MOVE/OPEN/FOLD, a non-200 READ/FIND
472
- // whose matcher already rides in meta, a span-less EDIT) fall through to their meta line.
473
- const b = e.tx.body;
474
- const opBody = typeof b === "string" ? b
475
- : b !== null && typeof b === "object" && typeof b.raw === "string" ? b.raw : "";
476
- if (opBody.length > 0)
477
- body = PacketWire.#renderAuthoredBody(path ?? `log:///${coordinate}`, opBody, false);
478
- // §worker-scheme-collect: an injected SEND (a child worker's concluded deliverable surfaced in
479
- // the parent) has no authored tx body — its payload is the deliverable in rx (a raw string,
480
- // not the {status} envelope a model SEND gets). Surface it so a child's 2xx reaches the
481
- // parent OPEN (born-open at the insert), never a bodyless `*` row.
482
- else if (op === "SEND" && opBody.length === 0 && typeof e.rx === "string" && e.rx.length > 0) {
483
- // §arrival-law — the deliverable is PUSHED content: preview-bounded, never whole-by-default.
484
- const arrival = PacketWire.#arrivalPreview(e.rx);
485
- body = PacketWire.#renderContentBody(path ?? `log:///${coordinate}`, arrival.text, "text/plain");
486
- if (arrival.cut)
487
- body += `\n… arrival preview — the full deliverable is ${e.rx.split("\n").length} lines: READ worker://${(e.target && typeof e.target === "object" && "pathname" in e.target ? String(e.target.pathname ?? "") : "").replace(/^\//, "")}`;
488
- }
397
+ if ((op === "COPY" || op === "MOVE")
398
+ && rx !== null
399
+ && typeof rx === "object"
400
+ && Object.hasOwn(rx, "effects")) {
401
+ meta.effects = assertResourceEffects(rx.effects).map((effect) => ({
402
+ target: effect.target,
403
+ action: effect.action,
404
+ ...(effect.receipt === undefined
405
+ ? {}
406
+ : PacketWire.#receiptMeta(effect.receipt)),
407
+ }));
489
408
  }
490
- else if (op === "error") {
491
- // §telemetry — a parse-error row is a LOG ITEM; its body is the parser message, which
492
- // carries the content-offset `line:col`. The model resolves that line against its own
493
- // emission — READ the folded `model` mirror row (§model-entry) — so no snippet is embedded.
494
- // Foldable like any body; the errors section keeps only a pointer (status + coordinate).
495
- const detail = (rx !== null && typeof rx === "object" ? rx : {});
496
- if (typeof detail.message === "string" && detail.message.length > 0) {
497
- body = PacketWire.#wrapHeredocBody(path ?? `log:///${coordinate}`, detail.message);
498
- }
409
+ // The canonical full body is shared with READ(log://), FIND(log://),
410
+ // and search derivation. READ/FIND already own selection bounds and
411
+ // render complete; every other body uses this one preview projection.
412
+ const fullBody = LogBody.resolve({
413
+ op,
414
+ attrs: e.attrs,
415
+ tx: e.tx,
416
+ rx: e.rx,
417
+ mimetypeTx: typeof e.mimetype_tx === "string" ? e.mimetype_tx : undefined,
418
+ mimetypeRx: typeof e.mimetype_rx === "string" ? e.mimetype_rx : undefined,
419
+ });
420
+ const emptyFind = op === "FIND" && e.status === 200 && findItems === 0;
421
+ const previewExempt = op === "READ" || op === "FIND";
422
+ const projection = previewExempt
423
+ ? { text: fullBody.content, cut: false }
424
+ : PacketWire.#preview(fullBody.content);
425
+ const resourceBody = op === "READ" || op === "FIND" || op === "EDIT"
426
+ || op === "prompt";
427
+ const bodyFence = resourceBody ? (target ?? path) : path;
428
+ if (projection.cut && path === null) {
429
+ throw new Error("a previewed log body requires an addressable log path");
499
430
  }
500
- else if (op === "model") {
501
- // §model-entry — the model's own verbatim emission, mirrored back so it sees exactly
502
- // what it produced. Line-numbered like all content (the parser reports errors by line,
503
- // so the model can reason about its own syntax error). Folded by default → just the
504
- // meta line until OPENed; the turn-0 exemplar is born open (the worked example).
505
- if (rx !== null && typeof rx === "object" && typeof rx.content === "string" && rx.content.length > 0) {
506
- const mimetype = typeof rx.mimetype === "string" ? rx.mimetype : "text/vnd.plurnk";
507
- body = PacketWire.#renderContentBody(path ?? `log:///${coordinate}`, rx.content, mimetype);
508
- }
431
+ if (projection.cut) {
432
+ meta.overflow = `Body content truncated. Full body: ${path}`;
509
433
  }
434
+ const body = emptyFind || projection.text.length === 0
435
+ ? ""
436
+ : PacketWire.#renderContentBody(bodyFence ?? `log:///${coordinate}`, projection.text, fullBody.startLine);
510
437
  // tokens on EVERY row (0 when there's genuinely no body) so the model can always weigh
511
- // it; for a folded row this is the room an OPEN would add. items present even at 0.
512
- if (items !== null)
513
- meta.items = items;
438
+ // it; for a folded row this is the room an OPEN would add.
514
439
  // 0 for a genuinely empty body — never call countTokens("") (some providers return
515
440
  // undefined for it, which JSON.stringify would drop, leaving the row with no tokens).
516
441
  meta.tokens = body.length > 0 ? countTokens(body) : 0;
517
- collectBodyTokens?.push(meta.tokens);
518
- // lines beside tokens on any row with a navigable body — the count of `N:`-numbered
519
- // lines (fences and unnumbered prose don't count), so the model can plan a <start,end>
520
- // slice before paying for an OPEN. Omitted when the body isn't line-addressable.
521
- if (body.length > 0) {
442
+ // lines beside tokens on a non-retrieval row with a navigable body — the count of
443
+ // `N:`-numbered lines (fences and unnumbered prose don't count), so the model can plan
444
+ // a <start,end> slice before paying for an OPEN. READ/FIND own typed extents instead.
445
+ if (body.length > 0 && op !== "READ" && op !== "FIND") {
522
446
  const navigable = body.split("\n").filter((l) => /^\d+:/.test(l)).length;
523
447
  if (navigable > 0)
524
448
  meta.lines = navigable;
525
449
  }
526
- // §jsonplurnk — the `display` field (grammar-ratified name #437) replaces the old -/+/*
527
- // glyph: `none` (no body, nothing to OPEN), `folded` (body exists, hidden — OPEN to expand),
528
- // `open` (body shown). `display:none` mirrors CSS the model knows; `folded`/`open` echo the
529
- // FOLD/OPEN ops. The one deviation from valid JSON: an OPEN row appends its heredoc body as a
530
- // raw `body` value (unescaped, delimited by its <<:::tag … :::tag fence). §jsonplurnk
450
+ // {§jsonplurnk} — `display` describes the three body states: `none` carries an explicit
451
+ // empty JSON string, `folded` withholds an existing body, and `open` appends that body as
452
+ // the format's one non-JSON value (a raw, tagged heredoc). The explicit empty body keeps
453
+ // every state self-describing; OPEN/FOLD remain friendly no-ops on `none`.
531
454
  const display = body.length === 0 ? "none" : e.folded === true ? "folded" : "open";
532
455
  meta.display = display;
456
+ if (display === "none")
457
+ meta.body = "";
533
458
  const obj = PacketWire.#canonicalJson(meta);
534
459
  return display === "open" ? obj.replace(/\}$/, `,"body":\n${body}\n}`) : obj;
535
460
  }).join(",\n");
@@ -537,41 +462,36 @@ export default class PacketWire {
537
462
  static #renderActionTarget(target) {
538
463
  if (target === null || target === undefined)
539
464
  return null;
540
- // An authority-bearing target (worker://<name> — the worker IS the authority, §worker-scheme;
541
- // a web host http://host/path) keeps its name in `hostname`. Without it a spawn renders
542
- // as a bare `worker://`, indistinguishable across workers — the model goes blind to what it
543
- // spawned and re-spawns. Reconstruct the authority form when a hostname is present;
544
- // namespace schemes (plurnk/known) fold their authority into the path and fall through.
545
- const host = typeof target.hostname === "string" && target.hostname.length > 0 ? target.hostname : null;
546
- const rendered = host !== null && typeof target.scheme === "string" && target.scheme.length > 0
547
- ? `${target.scheme}://${host}${target.port ? `:${target.port}` : ""}${encodePathParens(target.pathname ?? "")}`
548
- : PacketWire.#renderModelUri(target.scheme, target.pathname);
549
- if (rendered.length === 0)
465
+ return renderTarget({
466
+ scheme: target.scheme,
467
+ hostname: target.hostname,
468
+ port: target.port,
469
+ pathname: target.pathname,
470
+ query: target.query,
471
+ fragment: target.fragment,
472
+ });
473
+ }
474
+ static #renderSelection(target, marker) {
475
+ if (target === null || target === undefined)
476
+ return null;
477
+ const address = target.kind === "local" && typeof target.raw === "string"
478
+ ? renderTarget({ scheme: null, pathname: target.raw })
479
+ : target.scheme === "file"
480
+ ? renderTarget({ scheme: null, pathname: target.pathname ?? "" })
481
+ : PacketWire.#renderActionTarget(target);
482
+ if (address === null || address.length === 0)
550
483
  return null;
551
- // The channel fragment (#stdout/#stderr) is part of the address — a stream delta has to
552
- // say WHICH channel it is, not just the entry. §exec-stream
553
- const fragment = typeof target.fragment === "string" && target.fragment.length > 0 ? `#${target.fragment}` : "";
554
- return rendered + fragment;
484
+ if (marker === null || marker === undefined)
485
+ return address;
486
+ const validation = Validator.validateLineMarker(marker);
487
+ if (!validation.valid) {
488
+ throw new TypeError("A COPY/MOVE operand contains an invalid line marker.");
489
+ }
490
+ return `${address}<${marker.marks.join(",")}>`;
555
491
  }
556
492
  static #renderGitState(git) {
557
493
  const sync = git.ahead > 0 || git.behind > 0 ? ` (↑${git.ahead} ↓${git.behind})` : "";
558
494
  return `branch \`${git.branch}\`${sync} — ${git.staged} staged, ${git.unstaged} unstaged, ${git.untracked} untracked`;
559
495
  }
560
- static #renderTelemetryErrors(errors) {
561
- // Uniform projection off the formal TelemetryEvent envelope (grammar TelemetryEvent.json):
562
- // render each error by its typed `position`, never by sniffing `kind`. A LogCoordinate is a
563
- // terse `<status> log:///<coord>` link to the row (the row holds the term + detail); a
564
- // ContentOffset points the model at the line in its own born-OPEN emission (§model-entry).
565
- // No JSON dump — the packet teaches, the row carries the body.
566
- return errors.map((e) => {
567
- const ev = e;
568
- const pos = ev.position;
569
- if (pos?.type === "log-coordinate")
570
- return `* ${ev.status ?? ""} log:///${pos.coordinate}`.replace(/ +/g, " ");
571
- if (pos?.type === "content-offset")
572
- return `* ${ev.kind ?? "error"} ${pos.line}:${pos.column}`;
573
- return `* ${ev.kind ?? "error"}`;
574
- }).join("\n");
575
- }
576
496
  }
577
497
  //# sourceMappingURL=packet-wire.js.map