@llblab/pi-kit 0.38.3 → 0.39.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 (430) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +2 -2
  3. package/node_modules/@llblab/pi-actors/AGENTS.md +108 -20
  4. package/node_modules/@llblab/pi-actors/BACKLOG.md +207 -4
  5. package/node_modules/@llblab/pi-actors/CHANGELOG.md +13 -0
  6. package/node_modules/@llblab/pi-actors/README.md +31 -1
  7. package/node_modules/@llblab/pi-actors/dist/index.js +1 -1
  8. package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +37 -0
  9. package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +131 -62
  10. package/node_modules/@llblab/pi-actors/dist/lib/automatic-review-runtime.d.ts +2 -0
  11. package/node_modules/@llblab/pi-actors/dist/lib/automatic-review-runtime.js +3 -2
  12. package/node_modules/@llblab/pi-actors/dist/lib/command-templates.d.ts +37 -0
  13. package/node_modules/@llblab/pi-actors/dist/lib/command-templates.js +263 -129
  14. package/node_modules/@llblab/pi-actors/dist/lib/execution-files.d.ts +32 -0
  15. package/node_modules/@llblab/pi-actors/dist/lib/execution-files.js +128 -0
  16. package/node_modules/@llblab/pi-actors/dist/lib/execution-output.js +5 -4
  17. package/node_modules/@llblab/pi-actors/dist/lib/execution-sessions.d.ts +19 -1
  18. package/node_modules/@llblab/pi-actors/dist/lib/execution-sessions.js +62 -26
  19. package/node_modules/@llblab/pi-actors/dist/lib/execution.d.ts +4 -1
  20. package/node_modules/@llblab/pi-actors/dist/lib/execution.js +6 -1
  21. package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.d.ts +9 -2
  22. package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +92 -14
  23. package/node_modules/@llblab/pi-actors/dist/lib/file-state.js +21 -2
  24. package/node_modules/@llblab/pi-actors/dist/lib/inspector-command.js +2 -2
  25. package/node_modules/@llblab/pi-actors/dist/lib/inspector-overlay.d.ts +25 -7
  26. package/node_modules/@llblab/pi-actors/dist/lib/inspector-overlay.js +562 -262
  27. package/node_modules/@llblab/pi-actors/dist/lib/inspector.d.ts +20 -10
  28. package/node_modules/@llblab/pi-actors/dist/lib/inspector.js +80 -91
  29. package/node_modules/@llblab/pi-actors/dist/lib/limits.d.ts +1 -0
  30. package/node_modules/@llblab/pi-actors/dist/lib/limits.js +1 -0
  31. package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +15 -1
  32. package/node_modules/@llblab/pi-actors/dist/lib/observability.js +216 -99
  33. package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +5 -4
  34. package/node_modules/@llblab/pi-actors/dist/lib/pi.js +64 -10
  35. package/node_modules/@llblab/pi-actors/dist/lib/recipe-projection.d.ts +18 -0
  36. package/node_modules/@llblab/pi-actors/dist/lib/recipe-projection.js +220 -0
  37. package/node_modules/@llblab/pi-actors/dist/lib/recipes-context.d.ts +2 -0
  38. package/node_modules/@llblab/pi-actors/dist/lib/recipes-context.js +30 -6
  39. package/node_modules/@llblab/pi-actors/dist/lib/registry.js +270 -36
  40. package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.d.ts +213 -5
  41. package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.js +1909 -40
  42. package/node_modules/@llblab/pi-actors/dist/lib/run-supervision.d.ts +107 -0
  43. package/node_modules/@llblab/pi-actors/dist/lib/run-supervision.js +412 -0
  44. package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.d.ts +9 -2
  45. package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +283 -60
  46. package/node_modules/@llblab/pi-actors/dist/lib/runs-artifacts.d.ts +26 -1
  47. package/node_modules/@llblab/pi-actors/dist/lib/runs-artifacts.js +68 -2
  48. package/node_modules/@llblab/pi-actors/dist/lib/runs-context-exports.d.ts +451 -0
  49. package/node_modules/@llblab/pi-actors/dist/lib/runs-context-exports.js +2315 -0
  50. package/node_modules/@llblab/pi-actors/dist/lib/runs-context-index.d.ts +97 -0
  51. package/node_modules/@llblab/pi-actors/dist/lib/runs-context-index.js +368 -0
  52. package/node_modules/@llblab/pi-actors/dist/lib/runs-control-delivery.js +46 -7
  53. package/node_modules/@llblab/pi-actors/dist/lib/runs-control.d.ts +78 -2
  54. package/node_modules/@llblab/pi-actors/dist/lib/runs-control.js +334 -2
  55. package/node_modules/@llblab/pi-actors/dist/lib/runs-controls.js +4 -1
  56. package/node_modules/@llblab/pi-actors/dist/lib/runs-descendant-launches.d.ts +80 -0
  57. package/node_modules/@llblab/pi-actors/dist/lib/runs-descendant-launches.js +287 -0
  58. package/node_modules/@llblab/pi-actors/dist/lib/runs-execution-evidence.d.ts +110 -0
  59. package/node_modules/@llblab/pi-actors/dist/lib/runs-execution-evidence.js +282 -0
  60. package/node_modules/@llblab/pi-actors/dist/lib/runs-index.d.ts +66 -1
  61. package/node_modules/@llblab/pi-actors/dist/lib/runs-index.js +349 -13
  62. package/node_modules/@llblab/pi-actors/dist/lib/runs-native-launches.d.ts +73 -0
  63. package/node_modules/@llblab/pi-actors/dist/lib/runs-native-launches.js +227 -0
  64. package/node_modules/@llblab/pi-actors/dist/lib/runs-outcomes.d.ts +141 -0
  65. package/node_modules/@llblab/pi-actors/dist/lib/runs-outcomes.js +292 -0
  66. package/node_modules/@llblab/pi-actors/dist/lib/runs-ownership.d.ts +10 -1
  67. package/node_modules/@llblab/pi-actors/dist/lib/runs-ownership.js +36 -4
  68. package/node_modules/@llblab/pi-actors/dist/lib/runs-process-windows-mi.d.ts +46 -0
  69. package/node_modules/@llblab/pi-actors/dist/lib/runs-process-windows-mi.js +114 -0
  70. package/node_modules/@llblab/pi-actors/dist/lib/runs-process.d.ts +34 -1
  71. package/node_modules/@llblab/pi-actors/dist/lib/runs-process.js +158 -26
  72. package/node_modules/@llblab/pi-actors/dist/lib/runs-results.d.ts +116 -0
  73. package/node_modules/@llblab/pi-actors/dist/lib/runs-results.js +518 -0
  74. package/node_modules/@llblab/pi-actors/dist/lib/runs-retention.d.ts +15 -2
  75. package/node_modules/@llblab/pi-actors/dist/lib/runs-retention.js +148 -9
  76. package/node_modules/@llblab/pi-actors/dist/lib/runs-session-exports.d.ts +95 -0
  77. package/node_modules/@llblab/pi-actors/dist/lib/runs-session-exports.js +470 -0
  78. package/node_modules/@llblab/pi-actors/dist/lib/runs-snapshot.d.ts +58 -0
  79. package/node_modules/@llblab/pi-actors/dist/lib/runs-snapshot.js +92 -0
  80. package/node_modules/@llblab/pi-actors/dist/lib/runs-start.d.ts +2 -0
  81. package/node_modules/@llblab/pi-actors/dist/lib/runs-start.js +44 -3
  82. package/node_modules/@llblab/pi-actors/dist/lib/runs-status.d.ts +15 -1
  83. package/node_modules/@llblab/pi-actors/dist/lib/runs-status.js +337 -13
  84. package/node_modules/@llblab/pi-actors/dist/lib/runs-supervision.d.ts +105 -0
  85. package/node_modules/@llblab/pi-actors/dist/lib/runs-supervision.js +501 -0
  86. package/node_modules/@llblab/pi-actors/dist/lib/runs-trace.d.ts +1 -0
  87. package/node_modules/@llblab/pi-actors/dist/lib/runs-trace.js +14 -3
  88. package/node_modules/@llblab/pi-actors/dist/lib/runtime.js +22 -3
  89. package/node_modules/@llblab/pi-actors/dist/lib/session-evidence.d.ts +37 -0
  90. package/node_modules/@llblab/pi-actors/dist/lib/session-evidence.js +59 -3
  91. package/node_modules/@llblab/pi-actors/dist/lib/state-readers.d.ts +4 -1
  92. package/node_modules/@llblab/pi-actors/dist/lib/state-readers.js +14 -8
  93. package/node_modules/@llblab/pi-actors/dist/lib/temp.d.ts +2 -4
  94. package/node_modules/@llblab/pi-actors/dist/lib/temp.js +4 -56
  95. package/node_modules/@llblab/pi-actors/dist/lib/tools-access.d.ts +3 -0
  96. package/node_modules/@llblab/pi-actors/dist/lib/tools-access.js +13 -0
  97. package/node_modules/@llblab/pi-actors/dist/lib/tools-inspect.d.ts +1 -0
  98. package/node_modules/@llblab/pi-actors/dist/lib/tools-inspect.js +110 -43
  99. package/node_modules/@llblab/pi-actors/dist/lib/tools-local.d.ts +3 -0
  100. package/node_modules/@llblab/pi-actors/dist/lib/tools-local.js +18 -6
  101. package/node_modules/@llblab/pi-actors/dist/lib/tools-message.d.ts +4 -1
  102. package/node_modules/@llblab/pi-actors/dist/lib/tools-message.js +126 -31
  103. package/node_modules/@llblab/pi-actors/dist/lib/tools-response.d.ts +31 -3
  104. package/node_modules/@llblab/pi-actors/dist/lib/tools-response.js +311 -47
  105. package/node_modules/@llblab/pi-actors/dist/lib/tools-spawn.d.ts +3 -0
  106. package/node_modules/@llblab/pi-actors/dist/lib/tools-spawn.js +29 -7
  107. package/node_modules/@llblab/pi-actors/dist/lib/trace-projection.d.ts +20 -0
  108. package/node_modules/@llblab/pi-actors/dist/lib/trace-projection.js +192 -43
  109. package/node_modules/@llblab/pi-actors/dist/scripts/async-runner.mjs +272 -194
  110. package/node_modules/@llblab/pi-actors/dist/scripts/conformance.mjs +14 -2
  111. package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +46 -2
  112. package/node_modules/@llblab/pi-actors/dist/skills/actors/references/diagnostics.md +40 -0
  113. package/node_modules/@llblab/pi-actors/dist/skills/actors/references/runs.md +55 -1
  114. package/node_modules/@llblab/pi-actors/docs/README.md +9 -1
  115. package/node_modules/@llblab/pi-actors/docs/actor-inspector.md +84 -15
  116. package/node_modules/@llblab/pi-actors/docs/agent-experience.md +216 -0
  117. package/node_modules/@llblab/pi-actors/docs/async-runs.md +18 -2
  118. package/node_modules/@llblab/pi-actors/docs/composition-transport.md +103 -0
  119. package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +204 -4
  120. package/node_modules/@llblab/pi-actors/docs/inspection.md +38 -2
  121. package/node_modules/@llblab/pi-actors/docs/output-projection-map.md +130 -0
  122. package/node_modules/@llblab/pi-actors/docs/releasing.md +19 -2
  123. package/node_modules/@llblab/pi-actors/docs/run-supervision.md +1622 -0
  124. package/node_modules/@llblab/pi-actors/docs/template-recipes.md +4 -0
  125. package/node_modules/@llblab/pi-actors/docs/windows-process-observation.md +248 -0
  126. package/node_modules/@llblab/pi-actors/index.ts +1 -1
  127. package/node_modules/@llblab/pi-actors/lib/async-runs.ts +164 -73
  128. package/node_modules/@llblab/pi-actors/lib/automatic-review-runtime.ts +4 -2
  129. package/node_modules/@llblab/pi-actors/lib/command-templates.ts +287 -138
  130. package/node_modules/@llblab/pi-actors/lib/execution-files.ts +118 -0
  131. package/node_modules/@llblab/pi-actors/lib/execution-output.ts +4 -4
  132. package/node_modules/@llblab/pi-actors/lib/execution-sessions.ts +57 -27
  133. package/node_modules/@llblab/pi-actors/lib/execution.ts +8 -0
  134. package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +102 -12
  135. package/node_modules/@llblab/pi-actors/lib/file-state.ts +16 -2
  136. package/node_modules/@llblab/pi-actors/lib/inspector-command.ts +2 -2
  137. package/node_modules/@llblab/pi-actors/lib/inspector-overlay.ts +524 -261
  138. package/node_modules/@llblab/pi-actors/lib/inspector.ts +101 -130
  139. package/node_modules/@llblab/pi-actors/lib/limits.ts +1 -0
  140. package/node_modules/@llblab/pi-actors/lib/observability.ts +235 -116
  141. package/node_modules/@llblab/pi-actors/lib/pi.ts +59 -11
  142. package/node_modules/@llblab/pi-actors/lib/recipe-projection.ts +232 -0
  143. package/node_modules/@llblab/pi-actors/lib/recipes-context.ts +27 -6
  144. package/node_modules/@llblab/pi-actors/lib/registry.ts +266 -36
  145. package/node_modules/@llblab/pi-actors/lib/run-delivery.ts +1737 -49
  146. package/node_modules/@llblab/pi-actors/lib/run-supervision.ts +411 -0
  147. package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +237 -73
  148. package/node_modules/@llblab/pi-actors/lib/runs-artifacts.ts +73 -1
  149. package/node_modules/@llblab/pi-actors/lib/runs-context-exports.ts +1871 -0
  150. package/node_modules/@llblab/pi-actors/lib/runs-context-index.ts +372 -0
  151. package/node_modules/@llblab/pi-actors/lib/runs-control-delivery.ts +36 -18
  152. package/node_modules/@llblab/pi-actors/lib/runs-control.ts +297 -1
  153. package/node_modules/@llblab/pi-actors/lib/runs-controls.ts +3 -1
  154. package/node_modules/@llblab/pi-actors/lib/runs-descendant-launches.ts +338 -0
  155. package/node_modules/@llblab/pi-actors/lib/runs-execution-evidence.ts +302 -0
  156. package/node_modules/@llblab/pi-actors/lib/runs-index.ts +345 -17
  157. package/node_modules/@llblab/pi-actors/lib/runs-native-launches.ts +270 -0
  158. package/node_modules/@llblab/pi-actors/lib/runs-outcomes.ts +377 -0
  159. package/node_modules/@llblab/pi-actors/lib/runs-ownership.ts +37 -4
  160. package/node_modules/@llblab/pi-actors/lib/runs-process-windows-mi.ts +113 -0
  161. package/node_modules/@llblab/pi-actors/lib/runs-process.ts +210 -34
  162. package/node_modules/@llblab/pi-actors/lib/runs-results.ts +601 -0
  163. package/node_modules/@llblab/pi-actors/lib/runs-retention.ts +134 -10
  164. package/node_modules/@llblab/pi-actors/lib/runs-session-exports.ts +406 -0
  165. package/node_modules/@llblab/pi-actors/lib/runs-snapshot.ts +143 -0
  166. package/node_modules/@llblab/pi-actors/lib/runs-start.ts +44 -2
  167. package/node_modules/@llblab/pi-actors/lib/runs-status.ts +252 -10
  168. package/node_modules/@llblab/pi-actors/lib/runs-supervision.ts +394 -0
  169. package/node_modules/@llblab/pi-actors/lib/runs-trace.ts +14 -3
  170. package/node_modules/@llblab/pi-actors/lib/runtime.ts +22 -3
  171. package/node_modules/@llblab/pi-actors/lib/session-evidence.ts +76 -4
  172. package/node_modules/@llblab/pi-actors/lib/state-readers.ts +16 -7
  173. package/node_modules/@llblab/pi-actors/lib/temp.ts +3 -56
  174. package/node_modules/@llblab/pi-actors/lib/tools-access.ts +13 -0
  175. package/node_modules/@llblab/pi-actors/lib/tools-inspect.ts +110 -51
  176. package/node_modules/@llblab/pi-actors/lib/tools-local.ts +37 -24
  177. package/node_modules/@llblab/pi-actors/lib/tools-message.ts +120 -37
  178. package/node_modules/@llblab/pi-actors/lib/tools-response.ts +273 -50
  179. package/node_modules/@llblab/pi-actors/lib/tools-spawn.ts +31 -10
  180. package/node_modules/@llblab/pi-actors/lib/trace-projection.ts +165 -42
  181. package/node_modules/@llblab/pi-actors/package.json +10 -6
  182. package/node_modules/@llblab/pi-actors/scripts/async-runner.mjs +272 -194
  183. package/node_modules/@llblab/pi-actors/scripts/conformance.mjs +14 -2
  184. package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +46 -2
  185. package/node_modules/@llblab/pi-actors/skills/actors/references/diagnostics.md +40 -0
  186. package/node_modules/@llblab/pi-actors/skills/actors/references/runs.md +55 -1
  187. package/node_modules/@llblab/pi-codex-usage/AGENTS.md +2 -2
  188. package/node_modules/@llblab/pi-codex-usage/CHANGELOG.md +6 -0
  189. package/node_modules/@llblab/pi-codex-usage/README.md +9 -5
  190. package/node_modules/@llblab/pi-codex-usage/lib/query.ts +229 -50
  191. package/node_modules/@llblab/pi-codex-usage/lib/usage.ts +9 -1
  192. package/node_modules/@llblab/pi-codex-usage/package.json +1 -1
  193. package/node_modules/yaml/LICENSE +13 -0
  194. package/node_modules/yaml/README.md +172 -0
  195. package/node_modules/yaml/bin.mjs +11 -0
  196. package/node_modules/yaml/browser/dist/compose/compose-collection.js +88 -0
  197. package/node_modules/yaml/browser/dist/compose/compose-doc.js +43 -0
  198. package/node_modules/yaml/browser/dist/compose/compose-node.js +109 -0
  199. package/node_modules/yaml/browser/dist/compose/compose-scalar.js +86 -0
  200. package/node_modules/yaml/browser/dist/compose/composer.js +219 -0
  201. package/node_modules/yaml/browser/dist/compose/resolve-block-map.js +115 -0
  202. package/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
  203. package/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +49 -0
  204. package/node_modules/yaml/browser/dist/compose/resolve-end.js +37 -0
  205. package/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +207 -0
  206. package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +225 -0
  207. package/node_modules/yaml/browser/dist/compose/resolve-props.js +146 -0
  208. package/node_modules/yaml/browser/dist/compose/util-contains-newline.js +34 -0
  209. package/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +26 -0
  210. package/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
  211. package/node_modules/yaml/browser/dist/compose/util-map-includes.js +13 -0
  212. package/node_modules/yaml/browser/dist/doc/Document.js +335 -0
  213. package/node_modules/yaml/browser/dist/doc/anchors.js +71 -0
  214. package/node_modules/yaml/browser/dist/doc/applyReviver.js +55 -0
  215. package/node_modules/yaml/browser/dist/doc/createNode.js +88 -0
  216. package/node_modules/yaml/browser/dist/doc/directives.js +176 -0
  217. package/node_modules/yaml/browser/dist/errors.js +57 -0
  218. package/node_modules/yaml/browser/dist/index.js +17 -0
  219. package/node_modules/yaml/browser/dist/log.js +11 -0
  220. package/node_modules/yaml/browser/dist/nodes/Alias.js +116 -0
  221. package/node_modules/yaml/browser/dist/nodes/Collection.js +147 -0
  222. package/node_modules/yaml/browser/dist/nodes/Node.js +38 -0
  223. package/node_modules/yaml/browser/dist/nodes/Pair.js +36 -0
  224. package/node_modules/yaml/browser/dist/nodes/Scalar.js +24 -0
  225. package/node_modules/yaml/browser/dist/nodes/YAMLMap.js +144 -0
  226. package/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
  227. package/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +63 -0
  228. package/node_modules/yaml/browser/dist/nodes/identity.js +36 -0
  229. package/node_modules/yaml/browser/dist/nodes/toJS.js +37 -0
  230. package/node_modules/yaml/browser/dist/parse/cst-scalar.js +214 -0
  231. package/node_modules/yaml/browser/dist/parse/cst-stringify.js +61 -0
  232. package/node_modules/yaml/browser/dist/parse/cst-visit.js +97 -0
  233. package/node_modules/yaml/browser/dist/parse/cst.js +98 -0
  234. package/node_modules/yaml/browser/dist/parse/lexer.js +721 -0
  235. package/node_modules/yaml/browser/dist/parse/line-counter.js +39 -0
  236. package/node_modules/yaml/browser/dist/parse/parser.js +975 -0
  237. package/node_modules/yaml/browser/dist/public-api.js +102 -0
  238. package/node_modules/yaml/browser/dist/schema/Schema.js +37 -0
  239. package/node_modules/yaml/browser/dist/schema/common/map.js +17 -0
  240. package/node_modules/yaml/browser/dist/schema/common/null.js +15 -0
  241. package/node_modules/yaml/browser/dist/schema/common/seq.js +17 -0
  242. package/node_modules/yaml/browser/dist/schema/common/string.js +14 -0
  243. package/node_modules/yaml/browser/dist/schema/core/bool.js +19 -0
  244. package/node_modules/yaml/browser/dist/schema/core/float.js +43 -0
  245. package/node_modules/yaml/browser/dist/schema/core/int.js +38 -0
  246. package/node_modules/yaml/browser/dist/schema/core/schema.js +23 -0
  247. package/node_modules/yaml/browser/dist/schema/json/schema.js +62 -0
  248. package/node_modules/yaml/browser/dist/schema/tags.js +96 -0
  249. package/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +58 -0
  250. package/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
  251. package/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
  252. package/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
  253. package/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +67 -0
  254. package/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
  255. package/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
  256. package/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +39 -0
  257. package/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
  258. package/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
  259. package/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
  260. package/node_modules/yaml/browser/dist/stringify/stringify.js +129 -0
  261. package/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +153 -0
  262. package/node_modules/yaml/browser/dist/stringify/stringifyComment.js +20 -0
  263. package/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
  264. package/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +25 -0
  265. package/node_modules/yaml/browser/dist/stringify/stringifyPair.js +150 -0
  266. package/node_modules/yaml/browser/dist/stringify/stringifyString.js +336 -0
  267. package/node_modules/yaml/browser/dist/util.js +11 -0
  268. package/node_modules/yaml/browser/dist/visit.js +233 -0
  269. package/node_modules/yaml/browser/index.js +5 -0
  270. package/node_modules/yaml/browser/package.json +3 -0
  271. package/node_modules/yaml/dist/cli.d.ts +8 -0
  272. package/node_modules/yaml/dist/cli.mjs +201 -0
  273. package/node_modules/yaml/dist/compose/compose-collection.d.ts +11 -0
  274. package/node_modules/yaml/dist/compose/compose-collection.js +90 -0
  275. package/node_modules/yaml/dist/compose/compose-doc.d.ts +7 -0
  276. package/node_modules/yaml/dist/compose/compose-doc.js +45 -0
  277. package/node_modules/yaml/dist/compose/compose-node.d.ts +29 -0
  278. package/node_modules/yaml/dist/compose/compose-node.js +112 -0
  279. package/node_modules/yaml/dist/compose/compose-scalar.d.ts +5 -0
  280. package/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
  281. package/node_modules/yaml/dist/compose/composer.d.ts +63 -0
  282. package/node_modules/yaml/dist/compose/composer.js +224 -0
  283. package/node_modules/yaml/dist/compose/resolve-block-map.d.ts +6 -0
  284. package/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
  285. package/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
  286. package/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
  287. package/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
  288. package/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
  289. package/node_modules/yaml/dist/compose/resolve-end.d.ts +6 -0
  290. package/node_modules/yaml/dist/compose/resolve-end.js +39 -0
  291. package/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
  292. package/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
  293. package/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
  294. package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +227 -0
  295. package/node_modules/yaml/dist/compose/resolve-props.d.ts +23 -0
  296. package/node_modules/yaml/dist/compose/resolve-props.js +148 -0
  297. package/node_modules/yaml/dist/compose/util-contains-newline.d.ts +2 -0
  298. package/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
  299. package/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
  300. package/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
  301. package/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
  302. package/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
  303. package/node_modules/yaml/dist/compose/util-map-includes.d.ts +4 -0
  304. package/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
  305. package/node_modules/yaml/dist/doc/Document.d.ts +141 -0
  306. package/node_modules/yaml/dist/doc/Document.js +337 -0
  307. package/node_modules/yaml/dist/doc/anchors.d.ts +24 -0
  308. package/node_modules/yaml/dist/doc/anchors.js +76 -0
  309. package/node_modules/yaml/dist/doc/applyReviver.d.ts +9 -0
  310. package/node_modules/yaml/dist/doc/applyReviver.js +57 -0
  311. package/node_modules/yaml/dist/doc/createNode.d.ts +17 -0
  312. package/node_modules/yaml/dist/doc/createNode.js +90 -0
  313. package/node_modules/yaml/dist/doc/directives.d.ts +49 -0
  314. package/node_modules/yaml/dist/doc/directives.js +178 -0
  315. package/node_modules/yaml/dist/errors.d.ts +21 -0
  316. package/node_modules/yaml/dist/errors.js +62 -0
  317. package/node_modules/yaml/dist/index.d.ts +25 -0
  318. package/node_modules/yaml/dist/index.js +50 -0
  319. package/node_modules/yaml/dist/log.d.ts +3 -0
  320. package/node_modules/yaml/dist/log.js +19 -0
  321. package/node_modules/yaml/dist/nodes/Alias.d.ts +29 -0
  322. package/node_modules/yaml/dist/nodes/Alias.js +118 -0
  323. package/node_modules/yaml/dist/nodes/Collection.d.ts +73 -0
  324. package/node_modules/yaml/dist/nodes/Collection.js +151 -0
  325. package/node_modules/yaml/dist/nodes/Node.d.ts +53 -0
  326. package/node_modules/yaml/dist/nodes/Node.js +40 -0
  327. package/node_modules/yaml/dist/nodes/Pair.d.ts +22 -0
  328. package/node_modules/yaml/dist/nodes/Pair.js +39 -0
  329. package/node_modules/yaml/dist/nodes/Scalar.d.ts +47 -0
  330. package/node_modules/yaml/dist/nodes/Scalar.js +27 -0
  331. package/node_modules/yaml/dist/nodes/YAMLMap.d.ts +53 -0
  332. package/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
  333. package/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
  334. package/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
  335. package/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
  336. package/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
  337. package/node_modules/yaml/dist/nodes/identity.d.ts +23 -0
  338. package/node_modules/yaml/dist/nodes/identity.js +53 -0
  339. package/node_modules/yaml/dist/nodes/toJS.d.ts +29 -0
  340. package/node_modules/yaml/dist/nodes/toJS.js +39 -0
  341. package/node_modules/yaml/dist/options.d.ts +350 -0
  342. package/node_modules/yaml/dist/parse/cst-scalar.d.ts +64 -0
  343. package/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
  344. package/node_modules/yaml/dist/parse/cst-stringify.d.ts +8 -0
  345. package/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
  346. package/node_modules/yaml/dist/parse/cst-visit.d.ts +39 -0
  347. package/node_modules/yaml/dist/parse/cst-visit.js +99 -0
  348. package/node_modules/yaml/dist/parse/cst.d.ts +109 -0
  349. package/node_modules/yaml/dist/parse/cst.js +112 -0
  350. package/node_modules/yaml/dist/parse/lexer.d.ts +87 -0
  351. package/node_modules/yaml/dist/parse/lexer.js +723 -0
  352. package/node_modules/yaml/dist/parse/line-counter.d.ts +22 -0
  353. package/node_modules/yaml/dist/parse/line-counter.js +41 -0
  354. package/node_modules/yaml/dist/parse/parser.d.ts +84 -0
  355. package/node_modules/yaml/dist/parse/parser.js +980 -0
  356. package/node_modules/yaml/dist/public-api.d.ts +44 -0
  357. package/node_modules/yaml/dist/public-api.js +107 -0
  358. package/node_modules/yaml/dist/schema/Schema.d.ts +17 -0
  359. package/node_modules/yaml/dist/schema/Schema.js +39 -0
  360. package/node_modules/yaml/dist/schema/common/map.d.ts +2 -0
  361. package/node_modules/yaml/dist/schema/common/map.js +19 -0
  362. package/node_modules/yaml/dist/schema/common/null.d.ts +4 -0
  363. package/node_modules/yaml/dist/schema/common/null.js +17 -0
  364. package/node_modules/yaml/dist/schema/common/seq.d.ts +2 -0
  365. package/node_modules/yaml/dist/schema/common/seq.js +19 -0
  366. package/node_modules/yaml/dist/schema/common/string.d.ts +2 -0
  367. package/node_modules/yaml/dist/schema/common/string.js +16 -0
  368. package/node_modules/yaml/dist/schema/core/bool.d.ts +4 -0
  369. package/node_modules/yaml/dist/schema/core/bool.js +21 -0
  370. package/node_modules/yaml/dist/schema/core/float.d.ts +4 -0
  371. package/node_modules/yaml/dist/schema/core/float.js +47 -0
  372. package/node_modules/yaml/dist/schema/core/int.d.ts +4 -0
  373. package/node_modules/yaml/dist/schema/core/int.js +42 -0
  374. package/node_modules/yaml/dist/schema/core/schema.d.ts +1 -0
  375. package/node_modules/yaml/dist/schema/core/schema.js +25 -0
  376. package/node_modules/yaml/dist/schema/json/schema.d.ts +2 -0
  377. package/node_modules/yaml/dist/schema/json/schema.js +64 -0
  378. package/node_modules/yaml/dist/schema/json-schema.d.ts +69 -0
  379. package/node_modules/yaml/dist/schema/tags.d.ts +48 -0
  380. package/node_modules/yaml/dist/schema/tags.js +99 -0
  381. package/node_modules/yaml/dist/schema/types.d.ts +92 -0
  382. package/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
  383. package/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
  384. package/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
  385. package/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
  386. package/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
  387. package/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
  388. package/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
  389. package/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
  390. package/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +9 -0
  391. package/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
  392. package/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +22 -0
  393. package/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
  394. package/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
  395. package/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
  396. package/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
  397. package/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
  398. package/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
  399. package/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
  400. package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
  401. package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
  402. package/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
  403. package/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
  404. package/node_modules/yaml/dist/stringify/stringify.d.ts +21 -0
  405. package/node_modules/yaml/dist/stringify/stringify.js +132 -0
  406. package/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
  407. package/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
  408. package/node_modules/yaml/dist/stringify/stringifyComment.d.ts +10 -0
  409. package/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
  410. package/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
  411. package/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
  412. package/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
  413. package/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
  414. package/node_modules/yaml/dist/stringify/stringifyPair.d.ts +3 -0
  415. package/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
  416. package/node_modules/yaml/dist/stringify/stringifyString.d.ts +9 -0
  417. package/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
  418. package/node_modules/yaml/dist/test-events.d.ts +4 -0
  419. package/node_modules/yaml/dist/test-events.js +134 -0
  420. package/node_modules/yaml/dist/util.d.ts +16 -0
  421. package/node_modules/yaml/dist/util.js +28 -0
  422. package/node_modules/yaml/dist/visit.d.ts +102 -0
  423. package/node_modules/yaml/dist/visit.js +236 -0
  424. package/node_modules/yaml/package.json +97 -0
  425. package/node_modules/yaml/util.js +2 -0
  426. package/package.json +3 -3
  427. package/node_modules/@llblab/pi-actors/banner.jpg +0 -0
  428. package/node_modules/@llblab/pi-actors/dist/lib/inspector-actions.d.ts +0 -16
  429. package/node_modules/@llblab/pi-actors/dist/lib/inspector-actions.js +0 -57
  430. package/node_modules/@llblab/pi-actors/lib/inspector-actions.ts +0 -79
@@ -1,20 +1,34 @@
1
1
  /**
2
2
  * Coordinator delivery persistence.
3
- * Zones: owner-scoped completion batches, delivery phase fencing, bounded receipts
4
- * Owns durable delivery state; excludes Run discovery, Pi scheduling, and model formatting.
3
+ * Zones: owner-scoped completion streams/batches, delivery phase fencing, bounded receipts
4
+ * Owns atomic stream allocations, immutable member snapshots and bounded projections; excludes logical-session admission, Run discovery, primary authorization and Pi scheduling.
5
5
  */
6
6
  import { createHash, randomUUID } from "node:crypto";
7
- import { existsSync, lstatSync, readFileSync, statSync } from "node:fs";
8
- import { dirname, join } from "node:path";
7
+ import { types as utilTypes } from "node:util";
8
+ import { parseDocument } from "yaml";
9
+ import { renderYamlMetadata } from "./tools-response.js";
10
+ import { existsSync, lstatSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
11
+ import { basename, dirname, isAbsolute, join, normalize, resolve } from "node:path";
9
12
  import { withFileMutationLock, writeTextAtomic } from "./file-state.js";
10
13
  import * as Limits from "./limits.js";
14
+ import { decodeRunDirectoryNavigation } from "./runs-index.js";
15
+ import { formatInspectNextAction, formatLiteralBlock, formatMarkdownField, formatRunFileNavigationHandoff } from "./tools-response.js";
16
+ import { decodeRunCompletionText, projectRunCompletionText, decodeRunPrimaryTextBinding, decodeRunArtifactInventoryBinding, decodeRunResultDiagnosticsBinding, decodeRunResultDiagnosticExcerpt } from "./runs-outcomes.js";
11
17
  const RUN_DELIVERY_SCHEMA = "run-delivery-v1";
18
+ const COMPLETION_STREAM_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
12
19
  const MEMBER_FIELDS = new Set([
13
20
  "artifacts",
21
+ "artifact_inventory",
22
+ "artifact_inventory_unavailable",
23
+ "result_diagnostics",
24
+ "result_diagnostics_unavailable",
25
+ "diagnostic_excerpt",
14
26
  "parent_run",
15
27
  "parent_run_instance_id",
16
28
  "parent_state_dir",
17
29
  "output",
30
+ "primary_text",
31
+ "completion_text",
18
32
  "run",
19
33
  "run_instance_id",
20
34
  "state_dir",
@@ -24,6 +38,15 @@ const MEMBER_FIELDS = new Set([
24
38
  ]);
25
39
  const BATCH_FIELDS = new Set([
26
40
  "attempts",
41
+ "presentation_format",
42
+ "single_result",
43
+ "result_layout",
44
+ "numbering",
45
+ "excerpt_format",
46
+ "navigation_handoff",
47
+ "file_navigation",
48
+ "text_budget",
49
+ "inventory_source",
27
50
  "batch_id",
28
51
  "created_at",
29
52
  "last_error",
@@ -34,6 +57,7 @@ const BATCH_FIELDS = new Set([
34
57
  "queued_at",
35
58
  ]);
36
59
  const JOURNAL_FIELDS = new Set([
60
+ "completion_stream",
37
61
  "completion_batch",
38
62
  "owner_id",
39
63
  "receipts",
@@ -63,6 +87,59 @@ const STEER_FIELDS = new Set([
63
87
  "state_dir",
64
88
  "steer_id",
65
89
  ]);
90
+ /** Pure whole-capture arithmetic, not a reservation; commit with the packet under its owner lock. */
91
+ export function projectRunCompletionIndexRange(nextIndex, memberCount) {
92
+ if (!Number.isSafeInteger(nextIndex) || nextIndex < 0)
93
+ throw new Error("Completion stream counter is invalid");
94
+ if (!Number.isSafeInteger(memberCount) || memberCount < 1 || memberCount > Limits.RUN_DELIVERY_BATCH_MAX_MEMBERS) {
95
+ throw new Error("Completion stream member count is invalid");
96
+ }
97
+ // Subtract before adding so an unfitting range never produces rounded indices or a partial allocation.
98
+ if (memberCount > Number.MAX_SAFE_INTEGER - nextIndex) {
99
+ return Object.freeze({ state: "unavailable", reason: "range_unavailable", next_index: nextIndex });
100
+ }
101
+ return Object.freeze({ state: "available", next_index: nextIndex + memberCount,
102
+ indices: Object.freeze(Array.from({ length: memberCount }, (_, offset) => nextIndex + offset)) });
103
+ }
104
+ function parseCompletionNumbering(value, memberCount) {
105
+ const record = asRecord(value, "Completion numbering"), fields = Object.getOwnPropertyDescriptors(record);
106
+ const allowed = fields.kind?.value === "stream" ? ["kind", "session_id", "stream_id", "first_index", "member_order"]
107
+ : ["kind", "session_id", "stream_id", "reason", "member_order"];
108
+ if (Reflect.ownKeys(fields).some(key => typeof key !== "string" || !allowed.includes(key)) ||
109
+ Object.values(fields).some(field => !Object.hasOwn(field, "value") || !field.enumerable)) {
110
+ throw new Error("Completion numbering must contain only own data");
111
+ }
112
+ const kind = fields.kind?.value, session = fields.session_id?.value, stream = fields.stream_id?.value, order = fields.member_order?.value;
113
+ if ((kind !== "stream" && kind !== "unavailable") || typeof session !== "string" || !session.trim() || session.length > 4096 ||
114
+ !Array.isArray(order) || order.length !== memberCount || Reflect.ownKeys(order).length !== memberCount + 1) {
115
+ throw new Error("Completion numbering is invalid");
116
+ }
117
+ const memberOrder = [], selected = new Set();
118
+ for (let slot = 0; slot < memberCount; slot++) {
119
+ const descriptor = Object.getOwnPropertyDescriptor(order, String(slot));
120
+ if (!descriptor || !Object.hasOwn(descriptor, "value") || !descriptor.enumerable || !Number.isSafeInteger(descriptor.value) ||
121
+ descriptor.value < 0 || descriptor.value >= memberCount || selected.has(descriptor.value)) {
122
+ throw new Error("Completion numbering member order is invalid");
123
+ }
124
+ selected.add(descriptor.value);
125
+ memberOrder.push(descriptor.value);
126
+ }
127
+ if (kind === "stream") {
128
+ const first = fields.first_index?.value;
129
+ if (typeof stream !== "string" || !COMPLETION_STREAM_ID.test(stream) || !Number.isSafeInteger(first) || first < 0 ||
130
+ projectRunCompletionIndexRange(first, memberCount).state !== "available") {
131
+ throw new Error("Completion numbering range is invalid");
132
+ }
133
+ return Object.freeze({ kind, session_id: session, stream_id: stream, first_index: first + 0, member_order: Object.freeze(memberOrder) });
134
+ }
135
+ const reason = fields.reason?.value;
136
+ if ((reason !== "counter_unavailable" && reason !== "range_unavailable" && reason !== "stream_state_unavailable") ||
137
+ (reason === "stream_state_unavailable" ? fields.stream_id !== undefined : typeof stream !== "string" || !COMPLETION_STREAM_ID.test(stream))) {
138
+ throw new Error("Completion numbering unavailability is invalid");
139
+ }
140
+ return Object.freeze({ kind, session_id: session, ...(stream === undefined ? {} : { stream_id: stream }),
141
+ reason, member_order: Object.freeze(memberOrder) });
142
+ }
66
143
  function assertExactFields(value, fields, label) {
67
144
  const unknown = Object.keys(value).find((key) => !fields.has(key));
68
145
  if (unknown)
@@ -127,8 +204,29 @@ function parseMember(value) {
127
204
  const output = record.output === undefined
128
205
  ? undefined
129
206
  : boundedString(record.output, "Completion batch member output", 4_000);
130
- return {
131
- ...(parsedArtifacts ? { artifacts: parsedArtifacts } : {}),
207
+ const stateDir = boundedString(record.state_dir, "Completion batch member state_dir", 4_096);
208
+ const generation = boundedString(record.run_instance_id, "Completion batch member run_instance_id", 256);
209
+ const primary = record.primary_text === undefined ? undefined : decodeRunPrimaryTextBinding(stateDir, generation, record.primary_text);
210
+ const inventory = Object.hasOwn(record, "artifact_inventory") || Object.hasOwn(record, "artifact_inventory_unavailable")
211
+ ? decodeRunArtifactInventoryBinding(stateDir, generation, record) : undefined;
212
+ const diagnostics = Object.hasOwn(record, "result_diagnostics") || Object.hasOwn(record, "result_diagnostics_unavailable")
213
+ ? decodeRunResultDiagnosticsBinding(stateDir, generation, record) : undefined;
214
+ if (diagnostics && output)
215
+ throw new Error("Completion result diagnostics cannot also carry a legacy display.");
216
+ if (record.diagnostic_excerpt !== undefined && !diagnostics?.result_diagnostics)
217
+ throw new Error("Completion diagnostic excerpt requires its available source binding.");
218
+ const excerpt = record.diagnostic_excerpt === undefined ? undefined : decodeRunResultDiagnosticExcerpt(diagnostics.result_diagnostics, record.diagnostic_excerpt);
219
+ if (inventory && Object.keys(parsedArtifacts ?? {}).length)
220
+ throw new Error("Completion artifact inventory cannot also carry legacy artifact paths.");
221
+ if (primary?.output && output)
222
+ throw new Error("Completion primary text cannot also carry a legacy display.");
223
+ return Object.freeze({
224
+ ...(parsedArtifacts ? { artifacts: Object.freeze(parsedArtifacts) } : {}),
225
+ ...(primary ? { primary_text: primary } : {}),
226
+ ...(record.completion_text === undefined ? {} : { completion_text: decodeRunCompletionText(primary, record.completion_text) }),
227
+ ...inventory,
228
+ ...diagnostics,
229
+ ...(excerpt ? { diagnostic_excerpt: excerpt } : {}),
132
230
  ...(output ? { output } : {}),
133
231
  ...(parentRun
134
232
  ? {
@@ -138,16 +236,65 @@ function parseMember(value) {
138
236
  }
139
237
  : {}),
140
238
  run: boundedString(record.run, "Completion batch member run", 120),
141
- run_instance_id: boundedString(record.run_instance_id, "Completion batch member run_instance_id", 256),
142
- state_dir: boundedString(record.state_dir, "Completion batch member state_dir", 4_096),
239
+ run_instance_id: generation,
240
+ state_dir: stateDir,
143
241
  status: completionStatus(record.status),
144
242
  summary: boundedString(record.summary, "Completion batch member summary", 1_000),
145
243
  terminal_at: timestamp(record.terminal_at, "Completion batch member terminal_at"),
146
- };
244
+ });
245
+ }
246
+ // Only already admitted source bindings cross this boundary; private locations and diagnostics do not.
247
+ function completionInventoryText(batch) {
248
+ return `${JSON.stringify({
249
+ schema: "run-completion-inventory-v1", batch_id: batch.batch_id, created_at: batch.created_at,
250
+ scope: "captured_completion_members", members_total: batch.members.length,
251
+ artifacts: "not_included", diagnostics: "not_included", private_locations: "not_included",
252
+ ...(batch.members.some(member => member.artifact_inventory !== undefined) ? { artifact_inventory: "captured_bindings_only" } : {}),
253
+ ...(batch.members.some(member => member.result_diagnostics !== undefined) ? { result_diagnostics: "captured_bindings_only" } : {}),
254
+ ...(batch.numbering ? { numbering: batch.numbering } : {}),
255
+ members: batch.members.map(member => ({
256
+ run: member.run, run_instance_id: member.run_instance_id, status: member.status, terminal_at: member.terminal_at,
257
+ ...(member.parent_run ? { parent_run: member.parent_run, parent_run_instance_id: member.parent_run_instance_id } : {}),
258
+ ...(member.primary_text ? { primary_text: member.primary_text } : {}),
259
+ ...(member.artifact_inventory === undefined ? {} : { artifact_inventory: member.artifact_inventory,
260
+ ...(member.artifact_inventory_unavailable === undefined ? {} : { artifact_inventory_unavailable: member.artifact_inventory_unavailable }) }),
261
+ ...(member.result_diagnostics === undefined ? {} : { result_diagnostics: member.result_diagnostics,
262
+ ...(member.result_diagnostics_unavailable === undefined ? {} : { result_diagnostics_unavailable: member.result_diagnostics_unavailable }) }),
263
+ })),
264
+ }, null, 2)}\n`;
265
+ }
266
+ function parseInventorySource(value, batch) {
267
+ const record = asRecord(value, "Completion inventory source");
268
+ assertExactFields(record, new Set(["path", "sha256", "bytes_total", "members_total"]), "Completion inventory source");
269
+ const content = completionInventoryText(batch);
270
+ const sha256 = createHash("sha256").update(content).digest("hex");
271
+ const path = boundedString(record.path, "Completion inventory source path", 4_096);
272
+ if (!isAbsolute(path) || normalize(path) !== path || basename(path) !== `inventory-${sha256}.json` ||
273
+ record.sha256 !== sha256 || record.bytes_total !== Buffer.byteLength(content) || record.members_total !== batch.members.length) {
274
+ throw new Error("Completion inventory source binding is invalid");
275
+ }
276
+ return Object.freeze({ path, sha256, bytes_total: record.bytes_total, members_total: batch.members.length });
277
+ }
278
+ function isFrontmatterCompletionLayout(layout) {
279
+ return layout === "frontmatter_v1" || layout === "frontmatter_v2";
280
+ }
281
+ function isSharedCompletionLayout(layout) {
282
+ return layout === "headings_shared_results" || layout === "headings_shared_kinds" || layout === "completion_yaml_core_v1" || isFrontmatterCompletionLayout(layout);
147
283
  }
148
284
  function parseBatch(value) {
149
285
  const record = asRecord(value, "Completion batch");
150
286
  assertExactFields(record, BATCH_FIELDS, "Completion batch");
287
+ const presentationFormat = record.presentation_format;
288
+ if ((presentationFormat !== undefined && presentationFormat !== "ax_records" && presentationFormat !== "ax_records_v1" && presentationFormat !== "ax_records_v2" && presentationFormat !== "ax_records_v3") ||
289
+ (presentationFormat !== undefined && record.excerpt_format !== undefined)) {
290
+ throw new Error("Completion batch presentation format is invalid");
291
+ }
292
+ if (record.excerpt_format !== undefined && record.excerpt_format !== "bracketed_omissions_v1") {
293
+ throw new Error("Completion batch excerpt format is invalid");
294
+ }
295
+ if (record.navigation_handoff !== undefined && record.navigation_handoff !== "current_session_request" && record.navigation_handoff !== "captured_file_locations") {
296
+ throw new Error("Completion batch navigation handoff is invalid");
297
+ }
151
298
  if (!Array.isArray(record.members) || record.members.length === 0) {
152
299
  throw new Error("Completion batch members must be a non-empty array");
153
300
  }
@@ -155,6 +302,30 @@ function parseBatch(value) {
155
302
  throw new Error("Completion batch exceeds the member limit");
156
303
  }
157
304
  const members = record.members.map(parseMember);
305
+ const singleDescriptor = Object.getOwnPropertyDescriptor(record, "single_result");
306
+ if (singleDescriptor && (!Object.hasOwn(singleDescriptor, "value") || (singleDescriptor.value !== undefined &&
307
+ (singleDescriptor.value !== true || presentationFormat !== "ax_records" || members.length !== 1)))) {
308
+ throw new Error("Completion singleton layout is invalid");
309
+ }
310
+ const singleResult = singleDescriptor?.value;
311
+ const layoutDescriptor = Object.getOwnPropertyDescriptor(record, "result_layout");
312
+ if (layoutDescriptor && (!Object.hasOwn(layoutDescriptor, "value") || (layoutDescriptor.value !== undefined &&
313
+ ((layoutDescriptor.value !== "records" && layoutDescriptor.value !== "run_headers" && layoutDescriptor.value !== "headings" && layoutDescriptor.value !== "headings_flat_omissions" && layoutDescriptor.value !== "headings_outcomes" && !isSharedCompletionLayout(layoutDescriptor.value)) || presentationFormat !== "ax_records" || singleResult !== undefined)))) {
314
+ throw new Error("Completion result layout is invalid");
315
+ }
316
+ const resultLayout = layoutDescriptor?.value;
317
+ const budgetDescriptor = Object.getOwnPropertyDescriptor(record, "text_budget");
318
+ if (budgetDescriptor && (!Object.hasOwn(budgetDescriptor, "value") || !budgetDescriptor.enumerable ||
319
+ (budgetDescriptor.value !== undefined && (!isSharedCompletionLayout(resultLayout) ||
320
+ !Number.isSafeInteger(budgetDescriptor.value) || budgetDescriptor.value < 0 ||
321
+ budgetDescriptor.value > Math.floor(Limits.RUN_COMPLETION_MODEL_MAX_BYTES / Math.min(members.length, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)))))) {
322
+ throw new Error("Completion shared text budget is invalid");
323
+ }
324
+ const numberingDescriptor = Object.getOwnPropertyDescriptor(record, "numbering");
325
+ if (numberingDescriptor && (resultLayout === "completion_yaml_core_v1" || !Object.hasOwn(numberingDescriptor, "value") || presentationFormat !== "ax_records" || (resultLayout !== "headings" && resultLayout !== "headings_flat_omissions" && resultLayout !== "headings_outcomes" && !isSharedCompletionLayout(resultLayout)))) {
326
+ throw new Error("Completion numbering requires a canonical captured heading layout");
327
+ }
328
+ const numbering = numberingDescriptor ? parseCompletionNumbering(numberingDescriptor.value, members.length) : undefined;
158
329
  const identities = new Set();
159
330
  for (const member of members) {
160
331
  const identity = `${member.state_dir}\0${member.run_instance_id}`;
@@ -197,17 +368,55 @@ function parseBatch(value) {
197
368
  if (phase === "presented" && (!queuedAt || !presentedAt)) {
198
369
  throw new Error("Presented completion batch requires queued_at and presented_at");
199
370
  }
200
- return {
371
+ const navigation = record.file_navigation;
372
+ if ((record.navigation_handoff === "captured_file_locations") !== (navigation !== undefined))
373
+ throw new Error("Completion file navigation version/data mismatch");
374
+ let fileNavigation;
375
+ if (navigation !== undefined) {
376
+ if (!Array.isArray(navigation) || navigation.length > members.length || Object.keys(navigation).length !== navigation.length)
377
+ throw new Error("Completion file navigation is invalid");
378
+ const selected = new Set();
379
+ fileNavigation = Object.freeze(Array.from({ length: navigation.length }, (_, slot) => {
380
+ const entry = Object.getOwnPropertyDescriptor(navigation, String(slot));
381
+ if (!entry || !Object.hasOwn(entry, "value"))
382
+ throw new Error("Completion file navigation is invalid");
383
+ const row = asRecord(entry.value, "Completion file navigation");
384
+ assertExactFields(row, new Set(["member_index", "locations"]), "Completion file navigation");
385
+ const descriptors = Object.getOwnPropertyDescriptors(row);
386
+ if (!descriptors.member_index || !descriptors.locations || !Object.hasOwn(descriptors.member_index, "value") || !Object.hasOwn(descriptors.locations, "value"))
387
+ throw new Error("Completion file navigation is invalid");
388
+ const index = descriptors.member_index.value;
389
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0 || index >= members.length || selected.has(index))
390
+ throw new Error("Completion file navigation member is invalid");
391
+ selected.add(index);
392
+ const locations = decodeRunDirectoryNavigation(descriptors.locations.value), member = members[index];
393
+ if (locations.state === "available" && (locations.run !== member.run || locations.run_instance_id !== member.run_instance_id || locations.directory !== member.state_dir))
394
+ throw new Error("Completion file navigation identity mismatch");
395
+ return Object.freeze({ member_index: index, locations });
396
+ }));
397
+ if (Buffer.byteLength(JSON.stringify(fileNavigation), "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
398
+ throw new Error("Completion file navigation exceeds byte limit");
399
+ }
400
+ const batch = {
401
+ ...(presentationFormat === undefined ? {} : { presentation_format: presentationFormat }),
402
+ ...(singleResult === true ? { single_result: true } : {}),
403
+ ...(resultLayout === undefined ? {} : { result_layout: resultLayout }),
404
+ ...(budgetDescriptor?.value === undefined ? {} : { text_budget: budgetDescriptor.value }),
405
+ ...(numbering ? { numbering } : {}),
201
406
  ...(attempts !== undefined ? { attempts } : {}),
407
+ ...(record.navigation_handoff === undefined ? {} : { navigation_handoff: record.navigation_handoff }),
408
+ ...(record.excerpt_format === undefined ? {} : { excerpt_format: record.excerpt_format }),
409
+ ...(fileNavigation ? { file_navigation: fileNavigation } : {}),
202
410
  batch_id: boundedString(record.batch_id, "Completion batch id", 128),
203
411
  created_at: timestamp(record.created_at, "Completion batch created_at"),
204
412
  ...(lastError ? { last_error: lastError } : {}),
205
413
  ...(lastFailedAt ? { last_failed_at: lastFailedAt } : {}),
206
- members,
414
+ members: Object.freeze(members),
207
415
  phase,
208
416
  ...(queuedAt ? { queued_at: queuedAt } : {}),
209
417
  ...(presentedAt ? { presented_at: presentedAt } : {}),
210
418
  };
419
+ return record.inventory_source === undefined ? batch : { ...batch, inventory_source: parseInventorySource(record.inventory_source, batch) };
211
420
  }
212
421
  function parseSteer(value) {
213
422
  const record = asRecord(value, "Urgent steer envelope");
@@ -286,6 +495,62 @@ function parseReceipt(value) {
286
495
  presented_at: timestamp(record.presented_at, "Run delivery receipt presented_at"),
287
496
  };
288
497
  }
498
+ function completionStreamFields(value, allowed) {
499
+ const fields = Object.getOwnPropertyDescriptors(asRecord(value, "Completion stream intent"));
500
+ if (Reflect.ownKeys(fields).some(key => typeof key !== "string" || !allowed.includes(key)) ||
501
+ Object.values(fields).some(field => !Object.hasOwn(field, "value") || !field.enumerable)) {
502
+ throw new Error("Completion stream intent is invalid");
503
+ }
504
+ return fields;
505
+ }
506
+ function parseCompletionStreamState(value) {
507
+ const fields = completionStreamFields(value, ["active", "pending_stream_id"]);
508
+ const pending = fields.pending_stream_id?.value;
509
+ if ((!fields.active && !fields.pending_stream_id) || (fields.pending_stream_id &&
510
+ (typeof pending !== "string" || !COMPLETION_STREAM_ID.test(pending)))) {
511
+ throw new Error("Completion stream intent is invalid");
512
+ }
513
+ if (!fields.active)
514
+ return Object.freeze({ pending_stream_id: pending });
515
+ const active = completionStreamFields(fields.active.value, ["stream_id", "counter"]);
516
+ const id = active.stream_id?.value;
517
+ if (typeof id !== "string" || !COMPLETION_STREAM_ID.test(id) || id === pending) {
518
+ throw new Error("Completion stream active identity is invalid");
519
+ }
520
+ const counter = completionStreamFields(active.counter?.value, ["state", "next_index"]);
521
+ let retainedCounter;
522
+ if (counter.state?.value === "available") {
523
+ const next = counter.next_index?.value;
524
+ // A newly activated stream already captured at least one member in its first commit.
525
+ if (!Number.isSafeInteger(next) || next < 1)
526
+ throw new Error("Completion stream active counter is invalid");
527
+ retainedCounter = Object.freeze({ state: "available", next_index: next });
528
+ }
529
+ else if (counter.state?.value === "unavailable" && !counter.next_index) {
530
+ retainedCounter = Object.freeze({ state: "unavailable" });
531
+ }
532
+ else
533
+ throw new Error("Completion stream active counter is invalid");
534
+ return Object.freeze({ active: Object.freeze({ stream_id: id, counter: retainedCounter }),
535
+ ...(fields.pending_stream_id ? { pending_stream_id: pending } : {}) });
536
+ }
537
+ function assertCompletionStreamBatch(stream, batch, ownerId) {
538
+ if (!batch)
539
+ return;
540
+ const numbering = batch.numbering, active = stream?.active;
541
+ if (!numbering) {
542
+ if (active)
543
+ throw new Error("Active completion stream cannot contain a packet-local batch");
544
+ return;
545
+ }
546
+ if (!active || numbering.session_id !== ownerId || numbering.stream_id !== active.stream_id) {
547
+ throw new Error("Completion numbering has no matching active session stream");
548
+ }
549
+ if (numbering.kind === "stream" && active.counter.state === "available" &&
550
+ active.counter.next_index < numbering.first_index + batch.members.length) {
551
+ throw new Error("Completion stream counter is behind its captured packet");
552
+ }
553
+ }
289
554
  function emptyJournal(ownerId) {
290
555
  return {
291
556
  owner_id: ownerId,
@@ -294,7 +559,7 @@ function emptyJournal(ownerId) {
294
559
  steers: [],
295
560
  };
296
561
  }
297
- function parseJournal(value, ownerId) {
562
+ function parseJournal(value, ownerId, path) {
298
563
  const record = asRecord(value, "Run delivery journal");
299
564
  assertExactFields(record, JOURNAL_FIELDS, "Run delivery journal");
300
565
  if (record.schema !== RUN_DELIVERY_SCHEMA) {
@@ -318,10 +583,21 @@ function parseJournal(value, ownerId) {
318
583
  if (new Set(steers.map((steer) => steer.steer_id)).size !== steers.length) {
319
584
  throw new Error("Run delivery journal has duplicate urgent steer ids");
320
585
  }
586
+ const streamDescriptor = Object.getOwnPropertyDescriptor(record, "completion_stream");
587
+ if (streamDescriptor && (!Object.hasOwn(streamDescriptor, "value") || !streamDescriptor.enumerable)) {
588
+ throw new Error("Completion stream intent must be stored as own data");
589
+ }
590
+ const stream = streamDescriptor ? parseCompletionStreamState(streamDescriptor.value) : undefined;
591
+ const batch = record.completion_batch === undefined ? undefined : parseBatch(record.completion_batch);
592
+ if (batch && isSharedCompletionLayout(batch.result_layout) && batch.text_budget === undefined)
593
+ throw new Error("Captured completion shared text budget is unavailable");
594
+ assertCompletionStreamBatch(stream, batch, ownerId);
595
+ if (batch?.inventory_source && batch.inventory_source.path !== resolve(dirname(path), `inventory-${batch.inventory_source.sha256}.json`)) {
596
+ throw new Error("Completion inventory source belongs to another delivery location");
597
+ }
321
598
  return {
322
- ...(record.completion_batch !== undefined
323
- ? { completion_batch: parseBatch(record.completion_batch) }
324
- : {}),
599
+ ...(stream ? { completion_stream: stream } : {}),
600
+ ...(batch ? { completion_batch: batch } : {}),
325
601
  owner_id: ownerId,
326
602
  receipts,
327
603
  schema: RUN_DELIVERY_SCHEMA,
@@ -342,9 +618,12 @@ function assertNoDeliverySymlinks(tempDir, path) {
342
618
  }
343
619
  }
344
620
  }
345
- function readJournalAtPath(path, ownerId) {
346
- if (!existsSync(path))
621
+ function readJournalAtPath(path, ownerId, requireExisting = false) {
622
+ if (!existsSync(path)) {
623
+ if (requireExisting)
624
+ throw new Error("Completion stream request requires an existing delivery journal");
347
625
  return emptyJournal(ownerId);
626
+ }
348
627
  const stat = statSync(path);
349
628
  if (!stat.isFile() || stat.size > Limits.RUN_DELIVERY_JOURNAL_MAX_BYTES) {
350
629
  throw new Error("Run delivery journal is not a bounded regular file");
@@ -356,48 +635,143 @@ function readJournalAtPath(path, ownerId) {
356
635
  catch (error) {
357
636
  throw new Error("Run delivery journal is malformed", { cause: error });
358
637
  }
359
- return parseJournal(parsed, ownerId);
638
+ return parseJournal(parsed, ownerId, path);
360
639
  }
361
640
  export function readRunDeliveryJournal(tempDir, ownerId) {
362
641
  const path = getRunDeliveryJournalPath(tempDir, ownerId);
363
642
  assertNoDeliverySymlinks(tempDir, path);
364
643
  return readJournalAtPath(path, ownerId);
365
644
  }
366
- function persistJournal(path, journal) {
645
+ function journalText(journal) {
367
646
  const content = `${JSON.stringify(journal, null, 2)}\n`;
368
647
  if (Buffer.byteLength(content, "utf8") > Limits.RUN_DELIVERY_JOURNAL_MAX_BYTES) {
369
648
  throw new Error("Run delivery journal exceeds the byte limit");
370
649
  }
371
- writeTextAtomic(path, content);
650
+ return content;
372
651
  }
373
- function mutateJournal(tempDir, ownerId, mutate) {
652
+ function mutateJournal(tempDir, ownerId, mutate, requireExisting = false) {
374
653
  const path = getRunDeliveryJournalPath(tempDir, ownerId);
375
654
  return withFileMutationLock(path, () => {
376
655
  assertNoDeliverySymlinks(tempDir, path);
377
- const journal = readJournalAtPath(path, ownerId);
656
+ const journal = readJournalAtPath(path, ownerId, requireExisting);
378
657
  const result = mutate(journal);
379
- persistJournal(path, parseJournal(journal, ownerId));
658
+ writeTextAtomic(path, journalText(parseJournal(journal, ownerId, path)));
380
659
  return result;
381
660
  });
382
661
  }
662
+ /** Read-only persisted facts, not proof of non-connection or current SDK ownership. */
663
+ export function readRunCompletionStreamStatus(tempDir, ownerId) {
664
+ const journal = readRunDeliveryJournal(tempDir, ownerId), stream = journal.completion_stream;
665
+ if (!stream)
666
+ return Object.freeze({ state: "unavailable", reason: "stream_state_unavailable" });
667
+ const blocking = journal.completion_batch ? { blocking_batch_id: journal.completion_batch.batch_id } : {};
668
+ if (stream.pending_stream_id)
669
+ return Object.freeze({ state: "pending", session_id: journal.owner_id,
670
+ pending_stream_id: stream.pending_stream_id, ...(stream.active ? { active: stream.active } : {}), ...blocking });
671
+ return Object.freeze({ state: "active", session_id: journal.owner_id, active: stream.active, ...blocking });
672
+ }
673
+ /** Kernel-only explicit request; fresh-session bootstrap and public command admission remain separate. */
674
+ export function requestRunCompletionStream(input) {
675
+ const path = getRunDeliveryJournalPath(input.tempDir, input.ownerId);
676
+ return withFileMutationLock(path, () => {
677
+ assertNoDeliverySymlinks(input.tempDir, path);
678
+ const journal = readJournalAtPath(path, input.ownerId, true);
679
+ if (journal.completion_stream?.pending_stream_id)
680
+ return Object.freeze({ pending_stream_id: journal.completion_stream.pending_stream_id });
681
+ const stream = Object.freeze({ pending_stream_id: randomUUID() });
682
+ journal.completion_stream = parseCompletionStreamState({ ...journal.completion_stream, ...stream });
683
+ writeTextAtomic(path, journalText(parseJournal(journal, input.ownerId, path)));
684
+ return stream;
685
+ });
686
+ }
383
687
  export function admitRunCompletionBatch(input) {
688
+ return captureCompletionBatch(input, false, "completion_yaml_core_v1");
689
+ }
690
+ /** Kernel-only opt-in; ordinary Pi capture stays fenced until session/lifecycle integration is admitted. */
691
+ export function captureRunCompletionStreamBatch(input) {
692
+ return captureCompletionBatch(input, true);
693
+ }
694
+ /** Private ordinary candidate composition; stream capture retains its separately gated codec. */
695
+ export function captureRunCompletionYamlCandidateBatch(input) {
696
+ return captureCompletionBatch(input, false, "completion_yaml_core_v1");
697
+ }
698
+ function captureCompletionBatch(input, numbered, layout = "frontmatter_v2") {
384
699
  const now = (input.now ?? new Date()).toISOString();
385
- const batch = parseBatch({
700
+ if (Object.hasOwn(input, "presentationFormat"))
701
+ throw new Error("Completion presentation selection is not supported; new batches use AX.");
702
+ if (Object.hasOwn(input, "numbering"))
703
+ throw new Error("Completion numbering is assigned only by journal capture");
704
+ const captured = parseBatch({
705
+ presentation_format: "ax_records",
706
+ navigation_handoff: input.fileNavigation === undefined ? "current_session_request" : "captured_file_locations",
707
+ ...(input.fileNavigation === undefined ? {} : { file_navigation: input.fileNavigation }),
386
708
  batch_id: input.batchId ?? randomUUID(),
387
709
  created_at: now,
388
710
  members: input.members,
389
711
  phase: "pending",
390
712
  });
713
+ const draft = layout === "completion_yaml_core_v1"
714
+ ? parseBatch({ ...captured, result_layout: layout }) : { ...captured, result_layout: layout };
391
715
  return mutateJournal(input.tempDir, input.ownerId, (journal) => {
716
+ let batch = draft;
392
717
  if (journal.completion_batch) {
393
718
  throw new Error("Run delivery journal already has an active completion batch");
394
719
  }
395
720
  if (journal.receipts.some((receipt) => receipt.delivery_id === batch.batch_id)) {
396
721
  throw new Error("Run delivery batch id already has a presented receipt");
397
722
  }
398
- journal.completion_batch = batch;
399
- return batch;
400
- });
723
+ if (numbered) {
724
+ const stream = journal.completion_stream;
725
+ if (!stream)
726
+ throw new Error("Completion stream capture requires retained stream state");
727
+ const target = stream.pending_stream_id, active = stream.active;
728
+ const streamId = target ?? active.stream_id;
729
+ const counter = target ? { state: "available", next_index: 0 } : active.counter;
730
+ const range = counter.state === "available" ? projectRunCompletionIndexRange(counter.next_index, batch.members.length) : undefined;
731
+ const slots = new Map(batch.members.map((member, slot) => [memberIdentity(member), slot]));
732
+ const member_order = orderRunCompletionMembers(batch.members).map(member => slots.get(memberIdentity(member)));
733
+ const numbering = range?.state === "available"
734
+ ? { kind: "stream", session_id: input.ownerId, stream_id: streamId, first_index: range.indices[0], member_order }
735
+ : { kind: "unavailable", session_id: input.ownerId, stream_id: streamId,
736
+ reason: range ? "range_unavailable" : "counter_unavailable", member_order };
737
+ batch = parseBatch({ ...batch, numbering });
738
+ journal.completion_stream = parseCompletionStreamState({ active: { stream_id: streamId,
739
+ counter: range?.state === "available" ? { state: "available", next_index: range.next_index } : counter } });
740
+ }
741
+ else if (journal.completion_stream) {
742
+ throw new Error("Completion stream capture requires the numbered kernel path; stream state is retained");
743
+ }
744
+ if (layout !== "completion_yaml_core_v1")
745
+ batch = retainSharedCompletionTexts(batch);
746
+ if (projectCompletionBatchMessage(batch).membersShown === batch.members.length) {
747
+ if (layout === "completion_yaml_core_v1")
748
+ batch = retainSharedCompletionTexts(batch);
749
+ journal.completion_batch = batch;
750
+ return batch;
751
+ }
752
+ const content = completionInventoryText(batch);
753
+ const sha256 = createHash("sha256").update(content).digest("hex");
754
+ const journalPath = getRunDeliveryJournalPath(input.tempDir, input.ownerId);
755
+ const path = resolve(dirname(journalPath), `inventory-${sha256}.json`);
756
+ const retained = retainSharedCompletionTexts(parseBatch({ ...batch, inventory_source: { path, sha256, bytes_total: Buffer.byteLength(content), members_total: batch.members.length } }));
757
+ journal.completion_batch = retained;
758
+ // Refuse known journal overflow before a separate source effect. Never overwrite/reuse an orphan.
759
+ journalText(parseJournal(journal, input.ownerId, journalPath));
760
+ assertNoDeliverySymlinks(input.tempDir, path);
761
+ mkdirSync(dirname(path), { recursive: true });
762
+ assertNoDeliverySymlinks(input.tempDir, path);
763
+ const directory = lstatSync(dirname(path));
764
+ if (!directory.isDirectory())
765
+ throw new Error("Completion inventory directory is invalid");
766
+ writeFileSync(path, content, { encoding: "utf8", flag: "wx", mode: 0o600 });
767
+ assertNoDeliverySymlinks(input.tempDir, path);
768
+ const current = lstatSync(dirname(path));
769
+ const source = lstatSync(path);
770
+ if (current.dev !== directory.dev || current.ino !== directory.ino || !source.isFile() || source.size !== Buffer.byteLength(content)) {
771
+ throw new Error("Completion inventory source binding changed during retention");
772
+ }
773
+ return retained;
774
+ }, numbered);
401
775
  }
402
776
  export function markRunCompletionBatchQueued(input) {
403
777
  const now = (input.now ?? new Date()).toISOString();
@@ -614,7 +988,8 @@ function completionDepth(member, byIdentity) {
614
988
  }
615
989
  return depth;
616
990
  }
617
- function orderCompletionForest(members) {
991
+ /** Captured membership ordering only; never native lineage or a complete descendant tree. */
992
+ export function orderRunCompletionMembers(members, prioritizeFailed = false) {
618
993
  const byIdentity = new Map(members.map((member) => [memberIdentity(member), member]));
619
994
  const children = new Map();
620
995
  const roots = [];
@@ -622,7 +997,7 @@ function orderCompletionForest(members) {
622
997
  const parentIdentity = member.parent_state_dir && member.parent_run_instance_id
623
998
  ? `${member.parent_state_dir}\0${member.parent_run_instance_id}`
624
999
  : undefined;
625
- if (!parentIdentity || !byIdentity.has(parentIdentity)) {
1000
+ if (!parentIdentity || !byIdentity.has(parentIdentity) || (prioritizeFailed && byIdentity.get(parentIdentity).run !== member.parent_run)) {
626
1001
  roots.push(member);
627
1002
  continue;
628
1003
  }
@@ -630,6 +1005,22 @@ function orderCompletionForest(members) {
630
1005
  siblings.push(member);
631
1006
  children.set(parentIdentity, siblings);
632
1007
  }
1008
+ const failed = new Set(members.filter(member => member.status === "failed").map(memberIdentity));
1009
+ if (prioritizeFailed)
1010
+ for (const member of members) {
1011
+ let current = member;
1012
+ const seen = new Set();
1013
+ while (failed.has(memberIdentity(current)) && current.parent_state_dir && current.parent_run_instance_id) {
1014
+ const key = `${current.parent_state_dir}\0${current.parent_run_instance_id}`, parent = byIdentity.get(key);
1015
+ if (!parent || parent.run !== current.parent_run || seen.has(key))
1016
+ break;
1017
+ seen.add(key);
1018
+ failed.add(key);
1019
+ current = parent;
1020
+ }
1021
+ }
1022
+ const peers = (items) => prioritizeFailed
1023
+ ? [...items.filter(member => failed.has(memberIdentity(member))), ...items.filter(member => !failed.has(memberIdentity(member)))] : items;
633
1024
  const ordered = [];
634
1025
  const visited = new Set();
635
1026
  const visit = (member) => {
@@ -638,7 +1029,7 @@ function orderCompletionForest(members) {
638
1029
  return;
639
1030
  visited.add(identity);
640
1031
  ordered.push(member);
641
- for (const child of children.get(identity) ?? [])
1032
+ for (const child of peers(children.get(identity) ?? []))
642
1033
  visit(child);
643
1034
  };
644
1035
  for (const root of roots)
@@ -656,7 +1047,41 @@ function formatModelOutput(value, indent) {
656
1047
  .map((line) => `${indent} ${line}`)
657
1048
  .join("\n");
658
1049
  }
659
- function formatCompletionMember(member, byIdentity) {
1050
+ function formatPrimaryText(binding, indent, bracketed) {
1051
+ if (!binding.output)
1052
+ return `\n${indent} Primary output unavailable: ${binding.output_unavailable}.`;
1053
+ const output = binding.output;
1054
+ return `\n${indent} Primary output: ${output.chars_shown}/${output.chars_total} code points; ${bracketed ? `[${output.chars_omitted} characters omitted]` : `${output.chars_omitted} omitted`}; ${output.truncated ? "clipped" : "complete exported text"}${output.redacted ? "; redacted" : ""}.` +
1055
+ `\n${indent} UTF-8 bytes: [${output.shown_bytes.start}, ${output.shown_bytes.end}) / ${output.bytes_total}.` +
1056
+ `\n${formatLiteralBlock(output.preview)}\n${indent} Retained source at capture:\n${formatLiteralBlock(JSON.stringify(output.source, null, 2))}`;
1057
+ }
1058
+ function formatArtifactInventory(member, indent) {
1059
+ if (member.artifact_inventory === undefined)
1060
+ return "";
1061
+ if (member.artifact_inventory === null)
1062
+ return member.artifact_inventory_unavailable === "not_declared" ? ""
1063
+ : `\n${indent} Artifact inventory unavailable: ${member.artifact_inventory_unavailable}; no legacy path fallback.`;
1064
+ const inventory = member.artifact_inventory;
1065
+ return `\n${indent} Artifact declarations: ${inventory.declarations_total}; metadata only; target availability/production not asserted; bodies not read.` +
1066
+ `\n${indent} Retained declaration inventory at capture:\n${formatLiteralBlock(JSON.stringify({ ...inventory.source,
1067
+ bytes_total: inventory.bytes_total, declarations_total: inventory.declarations_total }, null, 2))}`;
1068
+ }
1069
+ function formatResultDiagnostics(member, indent, bracketed) {
1070
+ if (member.result_diagnostics === undefined)
1071
+ return "";
1072
+ if (member.result_diagnostics === null)
1073
+ return member.result_diagnostics_unavailable === "not_recorded" ? ""
1074
+ : `\n${indent} Diagnostic metadata unavailable: ${member.result_diagnostics_unavailable}; no legacy body fallback.`;
1075
+ const reference = member.result_diagnostics;
1076
+ const excerpt = member.diagnostic_excerpt;
1077
+ const others = reference.fields.filter(field => field !== excerpt?.field);
1078
+ return `\n${indent} Diagnostic metadata only; not primary output; admission privacy for recorded fields.` +
1079
+ (excerpt ? `\n${indent} Field ${excerpt.field}: ${excerpt.chars_shown}/${excerpt.chars_total} code points; ${bracketed ? `[${excerpt.chars_omitted} characters omitted]` : `${excerpt.chars_omitted} omitted`}; ${excerpt.truncated ? "clipped" : "complete field"}.` +
1080
+ `\n${indent} Field UTF-8 bytes: [${excerpt.shown_bytes.start}, ${excerpt.shown_bytes.end}) / ${excerpt.bytes_total}.\n${formatLiteralBlock(excerpt.text)}` : "") +
1081
+ (others.length ? `\n${indent} Recorded fields not shown: ${others.join(", ")}.` : "") +
1082
+ `\n${indent} Retained diagnostic source at capture:\n${formatLiteralBlock(JSON.stringify({ ...reference.source, bytes_total: reference.bytes_total, fields: reference.fields }, null, 2))}`;
1083
+ }
1084
+ function formatCompletionMember(member, byIdentity, bracketedExcerpts) {
660
1085
  const summary = compactModelText(member.summary, 200);
661
1086
  const artifactEntries = Object.entries(member.artifacts ?? {}).slice(0, 4);
662
1087
  const artifactText = artifactEntries.length === 0
@@ -664,9 +1089,10 @@ function formatCompletionMember(member, byIdentity) {
664
1089
  : `; artifacts: ${artifactEntries.map(([name, path]) => `${compactModelText(name, 120)}=\`${compactModelText(path, 320)}\``).join(", ")}`;
665
1090
  const indent = " ".repeat(Math.min(completionDepth(member, byIdentity), 8));
666
1091
  const output = member.output
667
- ? `\n${indent} Output:\n${formatModelOutput(member.output, indent)}`
1092
+ ? `\n${indent} ${member.primary_text ? "Legacy diagnostic display (not a complete primary source)" : "Output"}:\n${formatModelOutput(member.output, indent)}`
668
1093
  : "";
669
- return `${indent}- \`${compactModelText(member.run, 120)}\` — \`${member.status}\`: ${summary}${artifactText}${output}`;
1094
+ const primary = member.primary_text ? formatPrimaryText(member.primary_text, indent, bracketedExcerpts) : "";
1095
+ return `${indent}- \`${compactModelText(member.run, 120)}\` — \`${member.status}\`: ${summary}${artifactText}${primary}${formatArtifactInventory(member, indent)}${formatResultDiagnostics(member, indent, bracketedExcerpts)}${output}`;
670
1096
  }
671
1097
  function formatStatusCounts(members) {
672
1098
  const counts = new Map();
@@ -678,23 +1104,1224 @@ function formatStatusCounts(members) {
678
1104
  .map((status) => `${status}=${counts.get(status)}`)
679
1105
  .join(" ");
680
1106
  }
681
- /** Format one immutable batch for a model-bound custom message. */
682
- export function formatRunCompletionBatchMessage(batch) {
1107
+ function completionOmissionField(chars, direct) {
1108
+ return direct ? ["omitted", `${chars} chars`] : ["omission", `[omitted: ${chars} chars]`];
1109
+ }
1110
+ function axCompletionExcerpt(preview, coverage, directOmission = false) {
1111
+ return [
1112
+ formatMarkdownField("state", coverage.truncated ? "clipped" : "complete retained text"),
1113
+ formatMarkdownField("chars_shown", String(coverage.chars_shown)), formatMarkdownField("chars_total", String(coverage.chars_total)),
1114
+ formatMarkdownField("utf8_range", `[${coverage.shown_bytes.start}, ${coverage.shown_bytes.end}) / ${coverage.bytes_total}`),
1115
+ formatMarkdownField("preview", (coverage.shown_bytes.start > 0 ? "…\n" : "") + formatLiteralBlock(preview) +
1116
+ (coverage.shown_bytes.end < coverage.bytes_total ? "\n…" : "")),
1117
+ ...(coverage.chars_omitted > 0 ? [formatMarkdownField(...completionOmissionField(coverage.chars_omitted, directOmission))] : []),
1118
+ ];
1119
+ }
1120
+ function formatRetainedCompletionMember(member, byIdentity, batchLegacyBasis = false) {
1121
+ const run = member.run.startsWith("run:") ? member.run.slice(4) : member.run;
1122
+ // Keep the existing 200 UTF-16 display allowance, without normalizing opaque Markdown or leaking legacy tail lengths.
1123
+ let summary = member.summary;
1124
+ if (summary.length > 200) {
1125
+ let end = 199;
1126
+ if (/[\uD800-\uDBFF]/u.test(summary[end - 1]) && /[\uDC00-\uDFFF]/u.test(summary[end]))
1127
+ end--;
1128
+ summary = summary.slice(0, end) + "…";
1129
+ }
1130
+ const fields = [formatMarkdownField("run", run), formatMarkdownField("state", member.status), formatMarkdownField("summary", summary),
1131
+ ...(member.summary.length > 200 ? [formatMarkdownField("summary_omission", "[omitted: unknown]")] : []),
1132
+ ...(batchLegacyBasis ? [] : [formatMarkdownField("legacy_basis", "Recorded summary/output; original completeness/privacy unproven."),
1133
+ formatMarkdownField("legacy_upstream", "[omitted: unknown]")])];
1134
+ if (member.parent_state_dir && member.parent_run_instance_id) {
1135
+ const parent = byIdentity.get(`${member.parent_state_dir}\0${member.parent_run_instance_id}`);
1136
+ if (parent && parent.run === member.parent_run)
1137
+ fields.push(formatMarkdownField("parent_run", parent.run.startsWith("run:") ? parent.run.slice(4) : parent.run));
1138
+ }
1139
+ const primary = member.primary_text;
1140
+ if (primary)
1141
+ fields.push(formatMarkdownField("primary", primary.output ? [
1142
+ formatMarkdownField("scope", "Exported primary text at capture; admission privacy, not complete Actor output."),
1143
+ ...axCompletionExcerpt(primary.output.preview, primary.output),
1144
+ ...(primary.output.redacted ? [formatMarkdownField("redacted", "true")] : []),
1145
+ formatMarkdownField("source", formatLiteralBlock(JSON.stringify(primary.output.source, null, 2))),
1146
+ ].join("\n") : [formatMarkdownField("state", "unavailable"), formatMarkdownField("reason", primary.output_unavailable)].join("\n")));
1147
+ if (member.artifact_inventory) {
1148
+ const inventory = member.artifact_inventory;
1149
+ fields.push(formatMarkdownField("artifact_inventory", [formatMarkdownField("declarations", String(inventory.declarations_total)),
1150
+ formatMarkdownField("scope", "Metadata only; target availability/production not asserted; bodies not read."),
1151
+ formatMarkdownField("source", formatLiteralBlock(JSON.stringify({ ...inventory.source, bytes_total: inventory.bytes_total,
1152
+ declarations_total: inventory.declarations_total }, null, 2)))].join("\n")));
1153
+ }
1154
+ else if (member.artifact_inventory === null && member.artifact_inventory_unavailable !== "not_declared") {
1155
+ fields.push(formatMarkdownField("artifact_inventory", [formatMarkdownField("state", "unavailable"),
1156
+ formatMarkdownField("reason", member.artifact_inventory_unavailable), formatMarkdownField("warning", "no legacy path fallback")].join("\n")));
1157
+ }
1158
+ const artifacts = Object.entries(member.artifacts ?? {});
1159
+ if (artifacts.length)
1160
+ fields.push(formatMarkdownField("artifacts", [formatMarkdownField("scope", "Declared paths; existence/production unproven."),
1161
+ ...artifacts.map(([name, path]) => formatMarkdownField("artifact", [formatMarkdownField("name", name), formatMarkdownField("path", formatLiteralBlock(JSON.stringify(path)))].join("\n")))].join("\n")));
1162
+ if (member.result_diagnostics) {
1163
+ const reference = member.result_diagnostics, excerpt = member.diagnostic_excerpt;
1164
+ const others = reference.fields.filter(field => field !== excerpt?.field);
1165
+ fields.push(formatMarkdownField("diagnostic_metadata", [
1166
+ formatMarkdownField("scope", "Diagnostic metadata only; not primary output; admission privacy for recorded fields."),
1167
+ ...(excerpt ? [formatMarkdownField("field", excerpt.field), ...axCompletionExcerpt(excerpt.text, excerpt)] : []),
1168
+ ...(others.length ? [formatMarkdownField("recorded_fields_not_shown", others.join(", "))] : []),
1169
+ formatMarkdownField("source", formatLiteralBlock(JSON.stringify({ ...reference.source, bytes_total: reference.bytes_total, fields: reference.fields }, null, 2))),
1170
+ ].join("\n")));
1171
+ }
1172
+ else if (member.result_diagnostics === null && member.result_diagnostics_unavailable !== "not_recorded") {
1173
+ fields.push(formatMarkdownField("diagnostic_metadata", [formatMarkdownField("state", "unavailable"),
1174
+ formatMarkdownField("reason", member.result_diagnostics_unavailable), formatMarkdownField("warning", "no legacy body fallback")].join("\n")));
1175
+ }
1176
+ if (member.output) {
1177
+ if (primary)
1178
+ fields.push(formatMarkdownField("output_scope", "Legacy diagnostic display (not a complete primary source)"));
1179
+ fields.push(formatMarkdownField("output", member.output));
1180
+ }
1181
+ return fields.join("\n");
1182
+ }
1183
+ function completionRecordHeader(content) {
1184
+ const single = /^- completion: [^\r\n\u2028\u2029]{1,128}\n- presentation: (ax_records)\n/u.exec(content);
1185
+ const match = single ?? /^- completions: [1-9]\d{0,2}\n- batch: [^\r\n\u2028\u2029]{1,128}\n- presentation: (ax_records(?:_v3)?)\n/u.exec(content);
1186
+ return match ? { format: match[1], single: single !== null, length: match[0].length } : undefined;
1187
+ }
1188
+ function formatCompletionRecordMarker(batchId) {
1189
+ const literal = JSON.stringify(batchId).replaceAll(/[<>\u2028\u2029]/gu, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
1190
+ return `<!-- pi-actors:completion ${literal} -->`;
1191
+ }
1192
+ function completionStreamBoundary(streamId) {
1193
+ return `— Новый поток ${streamId} · отсчёт с #0 —`;
1194
+ }
1195
+ function completionNumberingTechnical(numbering) {
1196
+ return formatMarkdownField("technical", formatLiteralBlock(JSON.stringify({ completion_numbering: numbering })));
1197
+ }
1198
+ function readCompletionNumberingFrame(content, markerStart) {
1199
+ const match = /\n- technical:\n (`{3,})text\n ([^\r\n]*)\n \1$/u.exec(content.slice(0, markerStart));
1200
+ if (!match)
1201
+ return undefined;
1202
+ let data;
1203
+ try {
1204
+ data = JSON.parse(match[2]);
1205
+ }
1206
+ catch {
1207
+ return undefined;
1208
+ }
1209
+ if (!data || typeof data !== "object" || !Object.hasOwn(data, "completion_numbering"))
1210
+ return undefined;
1211
+ try {
1212
+ const raw = asRecord(data.completion_numbering, "Completion numbering"), order = raw.member_order;
1213
+ if (Object.keys(data).length !== 1 || !Array.isArray(order) || order.length < 1 || order.length > Limits.RUN_DELIVERY_BATCH_MAX_MEMBERS)
1214
+ return { malformed: true };
1215
+ const numbering = parseCompletionNumbering(raw, order.length);
1216
+ return completionNumberingTechnical(numbering) === match[0].slice(1) ? numbering : { malformed: true };
1217
+ }
1218
+ catch {
1219
+ return { malformed: true };
1220
+ }
1221
+ }
1222
+ /** Compiler-bound correlation data, not authentication or permission. Body equality cannot identify an epoch. */
1223
+ export function readRunCompletionRecordMarker(content) {
1224
+ if (Buffer.byteLength(content, "utf8") > Limits.RUN_COMPLETION_MODEL_MAX_BYTES)
1225
+ return undefined;
1226
+ const fresh = readFrontmatterFrame(content);
1227
+ if (fresh)
1228
+ return { batchId: fresh.batchId, start: fresh.markerStart, single: false, headers: true };
1229
+ const boundary = /^— Новый поток ([^\r\n]+) · отсчёт с #0 —\n/u.exec(content);
1230
+ const body = boundary ? content.slice(boundary[0].length) : content;
1231
+ const numbered = /^#(\?|0|[1-9]\d{0,15}):\n/u.exec(body);
1232
+ const headers = content.startsWith("#0:\n") || content.startsWith("Run #0:\n");
1233
+ const single = content.startsWith("- run:") || content.startsWith("- result: omitted (model byte limit)\n");
1234
+ if (!boundary && !numbered && !headers && !single && !/^- #0:\n|^- omitted_results: [1-9]\d{0,2}\n/u.test(content))
1235
+ return undefined;
1236
+ const match = /\n<!-- pi-actors:completion ("[^\r\n]{0,768}") -->$/u.exec(content);
1237
+ if (!match)
1238
+ return undefined;
1239
+ try {
1240
+ const batchId = JSON.parse(match[1]);
1241
+ if (typeof batchId !== "string" || !batchId.trim() || batchId.length > 128 || formatCompletionRecordMarker(batchId) !== match[0].slice(1))
1242
+ return undefined;
1243
+ const frame = readCompletionNumberingFrame(content, match.index);
1244
+ if (frame && "malformed" in frame)
1245
+ return undefined;
1246
+ if (frame) {
1247
+ const startsStream = frame.kind === "stream" && frame.first_index === 0;
1248
+ if (startsStream !== !!boundary || (boundary && completionStreamBoundary(frame.stream_id) + "\n" !== boundary[0]))
1249
+ return undefined;
1250
+ const expected = frame.kind === "stream" ? String(frame.first_index) : "?";
1251
+ if (numbered ? numbered[1] !== expected : !body.startsWith(`- omitted_results: ${frame.member_order.length}\n`))
1252
+ return undefined;
1253
+ return { batchId, start: match.index, single: false, headers: true };
1254
+ }
1255
+ if (boundary || (numbered && numbered[1] !== "0"))
1256
+ return undefined;
1257
+ return { batchId, start: match.index, single, ...(headers ? { headers: true } : {}) };
1258
+ }
1259
+ catch {
1260
+ return undefined;
1261
+ }
1262
+ }
1263
+ function completionPiece(key, value, depth = 0, compact = true) {
1264
+ return { text: formatMarkdownField(key, value, depth), compact, field: [key, value] };
1265
+ }
1266
+ function completionPreview(value, depth, before, after, field = "preview") {
1267
+ const indent = " ".repeat(depth), opening = formatLiteralBlock(value).split("\n", 1)[0];
1268
+ return [
1269
+ { text: `${indent}- ${field}:${before ? `\n${indent} …` : ""}`, compact: true },
1270
+ { text: `${indent} ${opening}`, compact: false },
1271
+ { text: `${indent} ${value.replaceAll(/\r\n|\r|\n/gu, newline => `${newline}${indent} `)}`, compact: true },
1272
+ { text: `${indent} ${opening.slice(0, -4)}`, compact: false },
1273
+ ...(after ? [{ text: `${indent} …`, compact: true }] : []),
1274
+ ];
1275
+ }
1276
+ function completionCoverage(value) {
1277
+ return { state: value.truncated ? "clipped" : "complete retained text", chars_shown: value.chars_shown,
1278
+ chars_total: value.chars_total, utf8_range: `[${value.shown_bytes.start}, ${value.shown_bytes.end}) / ${value.bytes_total}` };
1279
+ }
1280
+ function isRoutineCompletionSummary(member) {
1281
+ return member.summary === (member.status === "done" ? "Run completed." : `Run ${member.status}.`);
1282
+ }
1283
+ function completionRecordRow(member, index, byIdentity, flat = false, layout, textBudget) {
1284
+ const frontmatter = isFrontmatterCompletionLayout(layout) || layout === "completion_yaml_core_v1";
1285
+ const simpleKinds = frontmatter || layout === "headings_shared_kinds", shared = isSharedCompletionLayout(layout);
1286
+ const outcomes = shared || layout === "headings_outcomes", directOmission = outcomes || layout === "headings_flat_omissions";
1287
+ const run = member.run.startsWith("run:") ? member.run.slice(4) : member.run;
1288
+ const depth = flat ? 0 : 2;
1289
+ const pieces = [...(flat ? [] : [{ text: ` - #${index}:`, compact: true }]), completionPiece("run", run, depth), completionPiece("state", member.status, depth)];
1290
+ const technical = {};
1291
+ let primary = member.primary_text?.output;
1292
+ let diagnostic = outcomes && member.diagnostic_excerpt?.field === "command" ? undefined : member.diagnostic_excerpt;
1293
+ let completionText;
1294
+ if (shared && primary) {
1295
+ const captured = member.completion_text;
1296
+ const selected = projectRunCompletionText(captured?.text ?? primary.preview, textBudget);
1297
+ const start = captured ? 0 : primary.shown_bytes.start, shown = [...selected.text].length;
1298
+ if (captured)
1299
+ completionText = selected;
1300
+ primary = { ...primary, preview: selected.text, shown_bytes: { start, end: start + selected.shown_bytes.end },
1301
+ chars_shown: shown, chars_omitted: primary.chars_total - shown, truncated: shown < primary.chars_total };
1302
+ }
1303
+ else if (shared && diagnostic) {
1304
+ const selected = projectRunCompletionText(diagnostic.text, textBudget), shown = [...selected.text].length;
1305
+ diagnostic = { ...diagnostic, text: selected.text, shown_bytes: { start: diagnostic.shown_bytes.start,
1306
+ end: diagnostic.shown_bytes.start + selected.shown_bytes.end }, chars_shown: shown,
1307
+ chars_omitted: diagnostic.chars_total - shown, truncated: shown < diagnostic.chars_total };
1308
+ }
1309
+ const kind = primary ? primary.text_basis === "actor_response" ? "response" : "primary" : diagnostic ? "diagnostic" : member.output ? "legacy" : undefined;
1310
+ const boilerplate = isRoutineCompletionSummary(member);
1311
+ let legacy = !!member.output;
1312
+ if (!boilerplate) {
1313
+ legacy = true;
1314
+ let end = Math.min(member.summary.length, 200);
1315
+ if (member.summary.length > 200) {
1316
+ end = 199;
1317
+ if (/[\uD800-\uDBFF]/u.test(member.summary[end - 1]) && /[\uDC00-\uDFFF]/u.test(member.summary[end]))
1318
+ end--;
1319
+ }
1320
+ const summary = member.summary.slice(0, end);
1321
+ if (frontmatter)
1322
+ pieces.push({ text: Buffer.from(summary).toString("utf8") === summary ? summary : "Summary unavailable (malformed retained text)", compact: true, block: "body" });
1323
+ else {
1324
+ pieces.push(completionPiece("summary", Buffer.from(summary).toString("utf8") === summary
1325
+ ? summary + (end < member.summary.length ? "…" : "") : "unavailable (malformed retained text)", depth));
1326
+ if (end < member.summary.length || Buffer.from(summary).toString("utf8") !== summary)
1327
+ pieces.push(completionPiece("summary_omission", "[omitted: unknown]", depth));
1328
+ }
1329
+ }
1330
+ const chosen = primary ?? diagnostic;
1331
+ const legacyText = shared && member.output && Buffer.from(member.output).toString("utf8") === member.output
1332
+ ? projectRunCompletionText(member.output, textBudget).text : member.output;
1333
+ const value = primary?.preview ?? diagnostic?.text ?? legacyText;
1334
+ if (kind && value !== undefined) {
1335
+ if (!simpleKinds || kind !== "legacy") {
1336
+ pieces.push(completionPiece(simpleKinds ? "kind" : shared ? "result_kind" : "preview_kind", simpleKinds && kind === "response" ? "primary" : kind, depth));
1337
+ }
1338
+ if (diagnostic && !primary) {
1339
+ technical.preview_field = diagnostic.field;
1340
+ if (outcomes)
1341
+ pieces.push(completionPiece("preview_field", diagnostic.field, depth));
1342
+ }
1343
+ if (Buffer.from(value).toString("utf8") !== value) {
1344
+ pieces.push(frontmatter ? { text: "[omitted: unknown] (malformed retained text)", compact: true, block: "marker" }
1345
+ : completionPiece("preview_unavailable", "malformed retained text [omitted: unknown]", depth));
1346
+ }
1347
+ else {
1348
+ if (frontmatter) {
1349
+ if (chosen && chosen.shown_bytes.start > 0)
1350
+ pieces.push({ text: "[omitted: unknown] (retained excerpt starts after the source boundary)", compact: true, block: "marker" });
1351
+ pieces.push({ text: value, compact: true, block: "body" });
1352
+ if (chosen && chosen.chars_omitted > 0)
1353
+ pieces.push({ text: `[${chosen.chars_omitted} characters omitted]`, compact: true, block: "marker" });
1354
+ }
1355
+ else {
1356
+ const complete = shared && primary && primary.shown_bytes.start === 0 && primary.shown_bytes.end === primary.bytes_total && !primary.upstream_truncated;
1357
+ pieces.push(...completionPreview(value, depth, !!chosen && chosen.shown_bytes.start > 0, !!chosen && chosen.shown_bytes.end < chosen.bytes_total, complete ? "result" : "preview"));
1358
+ if (shared && kind === "legacy" && member.output && value !== member.output)
1359
+ pieces.push(completionPiece(simpleKinds ? "text_omission" : "legacy_omission", "[omitted: unknown]", depth));
1360
+ if (chosen && chosen.chars_omitted > 0)
1361
+ pieces.push(completionPiece(...completionOmissionField(chosen.chars_omitted, directOmission), depth));
1362
+ }
1363
+ }
1364
+ if (primary?.redacted)
1365
+ pieces.push(completionPiece("redacted", "true", depth));
1366
+ if (outcomes && primary?.upstream_truncated)
1367
+ pieces.push(frontmatter
1368
+ ? { text: "[omitted: unknown] (upstream completeness unproven)", compact: true, block: "marker" }
1369
+ : completionPiece("upstream_omission", "[omitted: unknown]", depth));
1370
+ }
1371
+ else
1372
+ pieces.push(completionPiece(shared ? "result_state" : "preview_state", "not captured", depth));
1373
+ if (member.parent_state_dir && member.parent_run_instance_id) {
1374
+ const parent = byIdentity.get(`${member.parent_state_dir}\0${member.parent_run_instance_id}`);
1375
+ if (parent && parent.run === member.parent_run)
1376
+ pieces.push(completionPiece("parent_run", parent.run.startsWith("run:") ? parent.run.slice(4) : parent.run, depth));
1377
+ }
1378
+ if (member.primary_text) {
1379
+ if (primary)
1380
+ technical.primary = { scope: primary.text_basis === "actor_response"
1381
+ ? "Executor-selected final Actor response at capture; admission privacy; prior stream clipping remains separate."
1382
+ : "Exported primary text at capture; admission privacy, not complete Actor output.",
1383
+ ...(primary.text_basis ? { text_basis: primary.text_basis, upstream_truncated: primary.upstream_truncated } : {}),
1384
+ ...completionCoverage(primary), ...(primary.redacted ? { redacted: true } : {}), source: primary.source };
1385
+ else if (!member.primary_text.output)
1386
+ pieces.push(completionPiece("primary_unavailable", member.primary_text.output_unavailable, depth));
1387
+ }
1388
+ if (member.result_diagnostics) {
1389
+ const reference = member.result_diagnostics;
1390
+ technical.diagnostic_metadata = { scope: "Diagnostic metadata only; not primary output; admission privacy for recorded fields.",
1391
+ ...(diagnostic ? { field: diagnostic.field, ...completionCoverage(diagnostic) } : {}),
1392
+ recorded_fields_not_shown: reference.fields.filter(field => field !== diagnostic?.field),
1393
+ source: { ...reference.source, bytes_total: reference.bytes_total, fields: reference.fields } };
1394
+ if (primary && diagnostic)
1395
+ technical.secondary_diagnostic_excerpt = axCompletionExcerpt(diagnostic.text, diagnostic, directOmission).join("\n");
1396
+ }
1397
+ else if (member.result_diagnostics === null && member.result_diagnostics_unavailable !== "not_recorded") {
1398
+ pieces.push(completionPiece("diagnostic_metadata_unavailable", member.result_diagnostics_unavailable, depth));
1399
+ technical.diagnostic_warning = "no legacy body fallback";
1400
+ }
1401
+ if (member.artifact_inventory) {
1402
+ const inventory = member.artifact_inventory;
1403
+ pieces.push(completionPiece("artifact_declarations", String(inventory.declarations_total), depth));
1404
+ technical.artifact_inventory = { scope: "Metadata only; target availability/production not asserted; bodies not read.",
1405
+ source: { ...inventory.source, bytes_total: inventory.bytes_total, declarations_total: inventory.declarations_total } };
1406
+ }
1407
+ else if (member.artifact_inventory === null && member.artifact_inventory_unavailable !== "not_declared") {
1408
+ pieces.push(completionPiece("artifact_inventory_unavailable", member.artifact_inventory_unavailable, depth));
1409
+ technical.artifact_warning = "no legacy path fallback";
1410
+ }
1411
+ const artifacts = Object.entries(member.artifacts ?? {});
1412
+ if (artifacts.length) {
1413
+ pieces.push(completionPiece("artifact_declarations", String(artifacts.length), depth));
1414
+ technical.artifacts = { scope: "Declared paths; existence/production unproven.", declarations: artifacts.map(([name, path]) => ({ name, path })) };
1415
+ }
1416
+ return { pieces, technical: Object.keys(technical).length ? formatMarkdownField(flat ? "result_details" : `#${index}`, formatLiteralBlock(JSON.stringify(technical, null, 2))) : "", technical_data: technical, legacy, ...(completionText ? { completion_text: completionText } : {}) };
1417
+ }
1418
+ const FRONTMATTER_FRAME = "<!-- pi-actors:frontmatter ";
1419
+ function readFrontmatterFrame(content) {
1420
+ try {
1421
+ if (Buffer.byteLength(content) > Limits.RUN_COMPLETION_MODEL_MAX_BYTES)
1422
+ return undefined;
1423
+ const match = /\n<!-- pi-actors:frontmatter ([^\r\n]+) -->\n<!-- pi-actors:completion ("[^\r\n]+") -->$/u.exec(content);
1424
+ if (!match)
1425
+ return undefined;
1426
+ const frame = JSON.parse(match[1]), batchId = JSON.parse(match[2]);
1427
+ if (typeof batchId !== "string" || !batchId.trim() || batchId.length > 128 || formatCompletionRecordMarker(batchId) !== `<!-- pi-actors:completion ${match[2]} -->`)
1428
+ return undefined;
1429
+ if (frontmatterJson(frame) !== match[1] || Object.keys(frame).sort().join() !== (frame.numbering ? "blocks,numbering,prefix_sha256" : "blocks,prefix_sha256"))
1430
+ return undefined;
1431
+ const prefix = content.slice(0, match.index);
1432
+ if (frame.prefix_sha256 !== createHash("sha256").update(prefix).digest("hex") || !Array.isArray(frame.blocks))
1433
+ return undefined;
1434
+ let previous = 0, rows = 0, inRow = false;
1435
+ const numbering = frame.numbering ? parseCompletionNumbering(frame.numbering, frame.numbering.member_order.length) : undefined;
1436
+ const startsStream = numbering?.kind === "stream" && numbering.first_index === 0;
1437
+ if (startsStream && (frame.blocks[0]?.[0] !== "service" || content.slice(frame.blocks[0][1], frame.blocks[0][2]) !== completionStreamBoundary(numbering.stream_id)))
1438
+ return undefined;
1439
+ const boundary = (n) => !(n > 0 && n < content.length && /[\uD800-\uDBFF]/u.test(content[n - 1]) && /[\uDC00-\uDFFF]/u.test(content[n])) && !(content[n - 1] === "\r" && content[n] === "\n");
1440
+ for (const block of frame.blocks) {
1441
+ if (!Array.isArray(block) || block.length !== 3)
1442
+ return undefined;
1443
+ const [kind, start, end] = block;
1444
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start !== previous + (previous ? kind === "header" ? 2 : 1 : 0) || end < start || end > prefix.length || !boundary(start) || !boundary(end))
1445
+ return undefined;
1446
+ if (content.slice(previous, start) !== (previous ? kind === "header" ? "\n\n" : "\n" : ""))
1447
+ return undefined;
1448
+ const text = content.slice(start, end);
1449
+ if (kind === "header") {
1450
+ const expected = numbering ? numbering.kind === "stream" ? numbering.first_index + rows : "?" : rows;
1451
+ if (!(text.startsWith(`---\nindex: ${expected}\nrun: `) || expected === "?" && text.startsWith('---\nindex: "?"\nrun: ')) || !text.endsWith("\n---\n"))
1452
+ return undefined;
1453
+ const lines = text.slice(4, -5).split("\n");
1454
+ if (!/^state: (done|failed|killed|exited)$/u.test(lines[2] ?? ""))
1455
+ return undefined;
1456
+ const keys = new Set();
1457
+ for (const line of lines) {
1458
+ const field = /^([a-z_]+): (.+)$/u.exec(line);
1459
+ if (!field || keys.has(field[1]) || !/^(?:index|run|state|kind|preview_field|redacted|result_state|parent_run|primary_unavailable|diagnostic_metadata_unavailable|artifact_declarations|artifact_inventory_unavailable|run_files_unavailable|references|run_files)$/u.test(field[1]))
1460
+ return undefined;
1461
+ if (field[1] === "kind" && (keys.size !== 3 || !/^(?:primary|diagnostic)$/u.test(field[2])))
1462
+ return undefined;
1463
+ keys.add(field[1]);
1464
+ const value = field[2];
1465
+ if (!/^(?:[a-z][a-z0-9_-]*|\?|\d+)$/u.test(value) && frontmatterJson(JSON.parse(value)) !== value)
1466
+ return undefined;
1467
+ }
1468
+ rows++;
1469
+ inRow = true;
1470
+ }
1471
+ else if (kind === "body") {
1472
+ if (!inRow)
1473
+ return undefined;
1474
+ }
1475
+ else if (kind === "marker") {
1476
+ if (!inRow || !/^(?:\[[1-9]\d* characters omitted\]|\[omitted: unknown\] \((?:retained excerpt starts after the source boundary|upstream completeness unproven|summary\/fallback text: original completeness\/privacy unproven|malformed retained text)\)|Run-file reference omitted \(packet byte limit\)\.)$/u.test(text))
1477
+ return undefined;
1478
+ }
1479
+ else if (kind === "service") {
1480
+ if (!(text === (numbering?.kind === "stream" && numbering.first_index === 0 ? completionStreamBoundary(numbering.stream_id) : "") || /^- (?:omitted_results|omitted_states|inventory_warning|numbering_unavailable|result_inventory): /u.test(text) || text.startsWith("- omitted_states:\n")))
1481
+ return undefined;
1482
+ inRow = false;
1483
+ }
1484
+ else
1485
+ return undefined;
1486
+ previous = end;
1487
+ }
1488
+ if (previous !== prefix.length || rows > Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)
1489
+ return undefined;
1490
+ return { blocks: frame.blocks, batchId, markerStart: content.lastIndexOf("\n<!-- pi-actors:completion ") };
1491
+ }
1492
+ catch {
1493
+ return undefined;
1494
+ }
1495
+ }
1496
+ function frontmatterJson(value) {
1497
+ return JSON.stringify(value).replaceAll(/[<>\u2028\u2029]/gu, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
1498
+ }
1499
+ /** All prefix bytes belong to a visible block. Only this closed compiler footer can be hidden. */
1500
+ function assembleFrontmatter(safe, candidates, references, ordered) {
1501
+ let message = "";
1502
+ const blocks = [];
1503
+ const add = (kind, text) => {
1504
+ if (message)
1505
+ message += kind === "header" ? "\n\n" : "\n";
1506
+ const start = message.length;
1507
+ message += text;
1508
+ blocks.push(Object.freeze([kind, start, message.length]));
1509
+ };
1510
+ if (safe.numbering?.kind === "stream" && safe.numbering.first_index === 0)
1511
+ add("service", completionStreamBoundary(safe.numbering.stream_id));
1512
+ for (const [index, { row, location }] of candidates.entries()) {
1513
+ const displayed = safe.numbering ? safe.numbering.kind === "stream" ? safe.numbering.first_index + index : "?" : index;
1514
+ const fields = row.pieces.filter(piece => !piece.block);
1515
+ const technical = { ...row.technical_data };
1516
+ for (const key of ["primary", "diagnostic_metadata"]) {
1517
+ if (!technical[key])
1518
+ continue;
1519
+ const detail = { ...technical[key] };
1520
+ for (const field of ["state", "chars_shown", "chars_total", "utf8_range", "upstream_truncated"]) {
1521
+ delete detail[field];
1522
+ }
1523
+ technical[key] = detail;
1524
+ }
1525
+ if (Object.hasOwn(technical, "secondary_diagnostic_excerpt")) {
1526
+ delete technical.secondary_diagnostic_excerpt;
1527
+ }
1528
+ const yaml = fields.map(piece => {
1529
+ if (!piece.field)
1530
+ throw new Error("Completion frontmatter field is invalid");
1531
+ const [key, value] = piece.field;
1532
+ return `${key}: ${/^[a-z][a-z0-9_-]*$/u.test(value) && !/^(?:null|true|false|yes|no|on|off)$/iu.test(value) ? value : frontmatterJson(value)}`;
1533
+ });
1534
+ if (Object.keys(technical).length)
1535
+ yaml.push(`references: ${frontmatterJson(technical)}`);
1536
+ if (references.has(index) && location) {
1537
+ const navigation = safe.file_navigation.find(item => safe.members[item.member_index] === ordered[index]);
1538
+ yaml.push(`run_files: ${frontmatterJson({ run: ordered[index].run, run_instance_id: ordered[index].run_instance_id, locations: navigation.locations })}`);
1539
+ }
1540
+ const yamlIndex = displayed === "?" && safe.result_layout === "frontmatter_v2" ? frontmatterJson(displayed) : displayed;
1541
+ add("header", `---\nindex: ${yamlIndex}\n${yaml.join("\n")}\n---\n`);
1542
+ for (const piece of row.pieces)
1543
+ if (piece.block)
1544
+ add(piece.block, piece.text);
1545
+ if (safe.file_navigation !== undefined && !references.has(index))
1546
+ add("marker", "Run-file reference omitted (packet byte limit).");
1547
+ }
1548
+ const omitted = safe.members.length - candidates.length;
1549
+ if (omitted) {
1550
+ add("service", `- omitted_results: ${omitted}`);
1551
+ const states = ["failed", "killed", "exited"].flatMap(status => {
1552
+ const count = ordered.slice(candidates.length).filter(member => member.status === status).length;
1553
+ return count ? [` - ${status}: ${count}`] : [];
1554
+ });
1555
+ if (states.length)
1556
+ add("service", "- omitted_states:\n" + states.join("\n"));
1557
+ if (!safe.inventory_source)
1558
+ add("service", "- inventory_warning: No inventory reference is retained in this packet.");
1559
+ }
1560
+ if (safe.numbering?.kind === "unavailable")
1561
+ add("service", `- numbering_unavailable: ${safe.numbering.reason}`);
1562
+ if (safe.inventory_source)
1563
+ add("service", `- result_inventory: ${frontmatterJson(safe.inventory_source)}`);
1564
+ const frame = { blocks, prefix_sha256: createHash("sha256").update(message).digest("hex"), ...(safe.numbering ? { numbering: safe.numbering } : {}) };
1565
+ message += `\n${FRONTMATTER_FRAME}${frontmatterJson(frame)} -->\n${formatCompletionRecordMarker(safe.batch_id)}`;
1566
+ return { message, membersShown: candidates.length, blocks: Object.freeze(blocks), compact_ranges: blocks.map(([, a, b]) => [a, b]) };
1567
+ }
1568
+ class CompletionYamlProjectionRefusal extends Error {
1569
+ reason;
1570
+ constructor(reason) { super(reason); this.reason = reason; }
1571
+ }
1572
+ // Only invoked at known metadata string slots; never on body text or arbitrary user objects.
1573
+ function completionYamlString(value) {
1574
+ if (value.isWellFormed())
1575
+ return value;
1576
+ return { utf16_code_units: Array.from({ length: value.length }, (_, index) => value.charCodeAt(index).toString(16).padStart(4, "0")).join(" ") };
1577
+ }
1578
+ function completionYamlReferences(row) {
1579
+ const technical = { ...row.technical_data };
1580
+ for (const key of ["primary", "diagnostic_metadata"]) {
1581
+ if (!technical[key])
1582
+ continue;
1583
+ const detail = { ...technical[key] };
1584
+ for (const field of ["state", "chars_shown", "chars_total", "utf8_range", "upstream_truncated"])
1585
+ delete detail[field];
1586
+ technical[key] = detail;
1587
+ }
1588
+ delete technical.secondary_diagnostic_excerpt;
1589
+ for (const key of ["primary", "diagnostic_metadata", "artifact_inventory"]) {
1590
+ const detail = technical[key];
1591
+ if (!detail)
1592
+ continue;
1593
+ const source = { ...detail.source };
1594
+ for (const slot of ["path", "run_instance_id", "sha256"]) {
1595
+ if (typeof source[slot] === "string")
1596
+ source[slot] = completionYamlString(source[slot]);
1597
+ }
1598
+ technical[key] = { ...detail, source };
1599
+ }
1600
+ const artifacts = technical.artifacts;
1601
+ if (artifacts)
1602
+ technical.artifacts = { scope: artifacts.scope, declarations: artifacts.declarations.map(item => ({
1603
+ name: completionYamlString(item.name), path: completionYamlString(item.path),
1604
+ })) };
1605
+ return technical;
1606
+ }
1607
+ function prepareCompletionYamlProjection(safe, candidates, references, ordered) {
1608
+ const packet = { batchId: safe.batch_id, records: [] };
1609
+ if (!safe.batch_id.isWellFormed())
1610
+ return { state: "unsupported" };
1611
+ for (const [index, { row, location }] of candidates.entries()) {
1612
+ const member = ordered[index];
1613
+ const displayed = safe.numbering ? safe.numbering.kind === "stream" ? safe.numbering.first_index + index : "?" : index;
1614
+ const metadata = { index: displayed };
1615
+ for (const piece of row.pieces) {
1616
+ if (piece.block)
1617
+ continue;
1618
+ if (!piece.field)
1619
+ return { state: "unsupported" };
1620
+ const [key, value] = piece.field;
1621
+ if (["run", "state", "kind"].includes(key)) {
1622
+ if (!value.isWellFormed() || !value.length || (key === "kind" && value !== "primary" && value !== "diagnostic"))
1623
+ return { state: "unsupported" };
1624
+ metadata[key] = value;
1625
+ }
1626
+ else if (key === "redacted")
1627
+ metadata[key] = true;
1628
+ else if (key === "artifact_declarations")
1629
+ metadata[key] = member.artifact_inventory?.declarations_total ?? Object.keys(member.artifacts ?? {}).length;
1630
+ else
1631
+ metadata[key] = completionYamlString(value);
1632
+ }
1633
+ const technical = completionYamlReferences(row);
1634
+ if (Object.keys(technical).length)
1635
+ metadata.references = technical;
1636
+ if (references.has(index) && location) {
1637
+ const navigation = safe.file_navigation.find(item => safe.members[item.member_index] === member);
1638
+ // Navigation is already decoded native data, not the JSON display field.
1639
+ const locations = navigation.locations;
1640
+ metadata.run_files = { run: completionYamlString(member.run), run_instance_id: completionYamlString(member.run_instance_id),
1641
+ locations: locations.state === "available" ? { ...locations, run: completionYamlString(locations.run),
1642
+ run_instance_id: completionYamlString(locations.run_instance_id), directory: completionYamlString(locations.directory),
1643
+ files: locations.files.map(file => ({ ...file, ...(typeof file.path === "string" ? { path: completionYamlString(file.path) } : {}) })) }
1644
+ : { ...locations } };
1645
+ }
1646
+ const parts = [];
1647
+ const add = (part) => { if (parts.length)
1648
+ parts.push({ marker: "\n" }); parts.push(part); };
1649
+ for (const piece of row.pieces)
1650
+ if (piece.block) {
1651
+ if (!piece.text.isWellFormed())
1652
+ return { state: "unsupported" };
1653
+ add(piece.block === "body" ? { body: piece.text } : { marker: piece.text });
1654
+ }
1655
+ if (safe.file_navigation !== undefined && !references.has(index))
1656
+ add({ marker: "Run-file reference omitted (packet byte limit)." });
1657
+ packet.records.push({ metadata, parts });
1658
+ }
1659
+ const service = {};
1660
+ const omitted = safe.members.length - candidates.length;
1661
+ if (omitted) {
1662
+ service.omitted_results = omitted;
1663
+ const states = {};
1664
+ for (const status of ["failed", "killed", "exited"]) {
1665
+ const count = ordered.slice(candidates.length).filter(member => member.status === status).length;
1666
+ if (count)
1667
+ states[status] = count;
1668
+ }
1669
+ if (Object.keys(states).length)
1670
+ service.omitted_states = states;
1671
+ if (!safe.inventory_source)
1672
+ service.inventory_warning = "No inventory reference is retained in this packet.";
1673
+ }
1674
+ if (safe.numbering) {
1675
+ service.numbering = { ...safe.numbering, session_id: completionYamlString(safe.numbering.session_id), member_order: [...safe.numbering.member_order] };
1676
+ if (safe.numbering.kind === "unavailable")
1677
+ service.numbering_unavailable = safe.numbering.reason;
1678
+ }
1679
+ if (safe.inventory_source)
1680
+ service.result_inventory = { ...safe.inventory_source, path: completionYamlString(safe.inventory_source.path) };
1681
+ if (Object.keys(service).length)
1682
+ packet.service = service;
1683
+ const chunks = [];
1684
+ let bytes = 0;
1685
+ for (let index = 0; index < Math.max(1, packet.records.length); index++) {
1686
+ const row = packet.records[index];
1687
+ const bookkeeping = index === 0 ? { codec: completionYamlCodec, batch_id: packet.batchId,
1688
+ ...(packet.service ? { service: packet.service } : {}) } : {};
1689
+ if (row?.parts.length)
1690
+ bookkeeping.parts = row.parts.map((part) => "body" in part ? { body: part.body.length } : { marker: part.marker.length });
1691
+ const header = { ...row?.metadata, ...(Object.keys(bookkeeping).length ? { _completion: bookkeeping } : {}) };
1692
+ const rendered = renderYamlMetadata(header, completionYamlMaxBytes);
1693
+ if (rendered.state !== "available")
1694
+ return { state: rendered.reason === "metadata_too_large" ? "overbudget" : "unsupported" };
1695
+ if (rendered.text.includes("\n\n"))
1696
+ return { state: "unsupported" };
1697
+ const framed = !!row?.parts.length;
1698
+ chunks.push(framed ? `---\n${rendered.text}---\n` : rendered.text);
1699
+ for (const part of row?.parts ?? [])
1700
+ chunks.push("body" in part ? part.body : part.marker);
1701
+ if (index + 1 < packet.records.length)
1702
+ chunks.push(framed ? "\n\n" : "\n");
1703
+ }
1704
+ for (const chunk of chunks)
1705
+ bytes += Buffer.byteLength(chunk, "utf8");
1706
+ return bytes > completionYamlMaxBytes ? { state: "overbudget" } : { state: "prepared", bytes, chunks, packet };
1707
+ }
1708
+ function materializeCompletionYamlProjection(prepared) {
1709
+ if (prepared.state !== "prepared")
1710
+ throw new CompletionYamlProjectionRefusal(prepared.state === "overbudget" ? "overbudget" : "unsupported_selected_data");
1711
+ const message = prepared.chunks.join("");
1712
+ const encoded = encodeCompletionYamlCarrier(prepared.packet);
1713
+ if (encoded.state !== "available" || encoded.text !== message || readCompletionYamlCarrier(message).state !== "available") {
1714
+ throw new CompletionYamlProjectionRefusal("compatibility_refused");
1715
+ }
1716
+ return { message, membersShown: prepared.packet.records.length, compact_ranges: [] };
1717
+ }
1718
+ /** Nondefault pure projection; neither a newly admitted Batch nor a rendering/acceptance grant. */
1719
+ export function projectRunCompletionYamlPrototype(batch) {
1720
+ let safe;
1721
+ try {
1722
+ safe = parseBatch(batch);
1723
+ }
1724
+ catch {
1725
+ return { state: "unavailable", reason: "invalid_snapshot" };
1726
+ }
1727
+ if (safe.presentation_format !== "ax_records" || safe.result_layout !== "frontmatter_v2")
1728
+ return { state: "unavailable", reason: "unsupported_source_class" };
1729
+ try {
1730
+ const projected = projectCompletionRecords(safe, "yaml");
1731
+ return { state: "available", content: projected.message, membersShown: projected.membersShown, textBudget: projected.textBudget,
1732
+ presentation: { format: "completion_yaml_core_v1", content_sha256: createHash("sha256").update(projected.message).digest("hex") } };
1733
+ }
1734
+ catch (error) {
1735
+ if (error instanceof CompletionYamlProjectionRefusal)
1736
+ return { state: "unavailable", reason: error.reason };
1737
+ throw error;
1738
+ }
1739
+ }
1740
+ function projectCompletionRecords(safe, assembler = safe.result_layout === "completion_yaml_core_v1" ? "yaml" : "legacy") {
1741
+ const frontmatter = isFrontmatterCompletionLayout(safe.result_layout) || assembler === "yaml";
1742
+ const shared = isSharedCompletionLayout(safe.result_layout);
1743
+ const maxBytes = shared ? Limits.RUN_COMPLETION_MODEL_MAX_BYTES : Limits.RUN_DELIVERY_MODEL_MAX_BYTES;
1744
+ const headers = shared || safe.result_layout === "headings_outcomes" || safe.result_layout === "headings_flat_omissions" || safe.result_layout === "run_headers" || safe.result_layout === "headings", single = safe.members.length === 1;
1745
+ const numbering = safe.numbering, ordered = numbering ? numbering.member_order.map(slot => safe.members[slot]) : orderRunCompletionMembers(safe.members);
1746
+ const byIdentity = new Map(safe.members.map(member => [memberIdentity(member), member]));
1747
+ const makeCandidates = (quota) => ordered.slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS).map((member, index) => {
1748
+ const row = completionRecordRow(member, index, byIdentity, true, safe.result_layout, quota);
1749
+ const navigation = safe.file_navigation?.find(item => safe.members[item.member_index] === member);
1750
+ if (navigation?.locations.state === "unavailable")
1751
+ row.pieces.push(completionPiece("run_files_unavailable", navigation.locations.reason));
1752
+ if (row.legacy)
1753
+ row.pieces.push(frontmatter
1754
+ ? { text: "[omitted: unknown] (summary/fallback text: original completeness/privacy unproven)", compact: true, block: "marker" }
1755
+ : safe.result_layout === "headings_shared_kinds"
1756
+ ? completionPiece("warning", "Summary/fallback text: original completeness/privacy unproven [omitted: unknown]")
1757
+ : completionPiece("legacy_basis", "Summary/legacy output: original completeness/privacy unproven [omitted: unknown]"));
1758
+ const technical = Object.keys(row.technical_data).length ? completionPiece("technical", formatLiteralBlock(JSON.stringify(row.technical_data, null, 2)), 0, false) : undefined;
1759
+ const location = navigation ? completionPiece("run_files", formatLiteralBlock(JSON.stringify({ run: member.run,
1760
+ run_instance_id: member.run_instance_id, locations: navigation.locations })), 0, false) : undefined;
1761
+ const baseBytes = assembler === "yaml" ? 0 : Buffer.byteLength([...row.pieces, ...(technical ? [technical] : [])].map(piece => piece.text).join("\n"), "utf8");
1762
+ return { row, navigation, technical, location, baseBytes };
1763
+ });
1764
+ let candidates = makeCandidates(shared ? safe.text_budget ?? 0 : undefined);
1765
+ const assemble = (shown, references) => {
1766
+ if (frontmatter)
1767
+ return assembleFrontmatter(safe, candidates.slice(0, shown), references, ordered);
1768
+ const pieces = [];
1769
+ if (numbering?.kind === "stream" && numbering.first_index === 0)
1770
+ pieces.push({ text: completionStreamBoundary(numbering.stream_id), compact: true });
1771
+ for (let index = 0; index < shown; index++) {
1772
+ const { row, technical, location } = candidates[index];
1773
+ const fields = [...row.pieces];
1774
+ if (safe.file_navigation !== undefined && !references.has(index))
1775
+ fields.push(completionPiece("run_files_omitted", "1"));
1776
+ if (technical)
1777
+ fields.push(technical);
1778
+ if (references.has(index))
1779
+ fields.push(location);
1780
+ const displayed = numbering ? numbering.kind === "stream" ? numbering.first_index + index : "?" : index;
1781
+ if (headers)
1782
+ pieces.push({ text: `${index ? "\n" : ""}${safe.result_layout === "run_headers" ? "Run " : ""}#${displayed}:`, compact: true });
1783
+ else if (!single)
1784
+ pieces.push({ text: `- #${index}:`, compact: true });
1785
+ pieces.push(...fields.map(piece => headers || single ? piece : { ...piece, text: " " + piece.text.replaceAll(/\r\n|\r|\n/gu, newline => `${newline} `) }));
1786
+ }
1787
+ const omitted = safe.members.length - shown;
1788
+ if (omitted) {
1789
+ pieces.push(single && !headers ? completionPiece("result", "omitted (model byte limit)") : completionPiece("omitted_results", String(omitted)));
1790
+ const states = ["failed", "killed", "exited"].flatMap(status => {
1791
+ const count = ordered.slice(shown).filter(member => member.status === status).length;
1792
+ return count ? [formatMarkdownField(status, String(count))] : [];
1793
+ });
1794
+ if (states.length)
1795
+ pieces.push({ text: "- omitted_states:\n" + states.map(line => " " + line).join("\n"), compact: true });
1796
+ if (!safe.inventory_source)
1797
+ pieces.push(completionPiece("inventory_warning", "No inventory reference is retained in this packet."));
1798
+ }
1799
+ if (numbering?.kind === "unavailable")
1800
+ pieces.push(completionPiece("numbering_unavailable", numbering.reason));
1801
+ if (safe.inventory_source)
1802
+ pieces.push(completionPiece("result_inventory", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2)), 0, false));
1803
+ if (numbering)
1804
+ pieces.push({ text: completionNumberingTechnical(numbering), compact: false });
1805
+ pieces.push({ text: formatCompletionRecordMarker(safe.batch_id), compact: false });
1806
+ let message = "";
1807
+ const compact_ranges = [];
1808
+ for (const piece of pieces) {
1809
+ if (message)
1810
+ message += "\n";
1811
+ const start = message.length;
1812
+ message += piece.text;
1813
+ if (piece.compact)
1814
+ compact_ranges.push([start, message.length]);
1815
+ }
1816
+ return { message, membersShown: shown, compact_ranges };
1817
+ };
1818
+ // Closed second consumer: legacy assembly remains eager, with its original call order.
1819
+ const prepare = (count, refs) => prepareCompletionYamlProjection(safe, candidates.slice(0, count), refs, ordered);
1820
+ const measure = (count, refs) => {
1821
+ if (assembler === "legacy")
1822
+ return Buffer.byteLength(assemble(count, refs).message, "utf8");
1823
+ const prepared = prepare(count, refs);
1824
+ if (prepared.state === "unsupported")
1825
+ throw new CompletionYamlProjectionRefusal("unsupported_selected_data");
1826
+ return prepared.state === "overbudget" ? Infinity : prepared.bytes;
1827
+ };
1828
+ if (assembler === "yaml") {
1829
+ // Validate selected facts even in rows that fitting may later omit.
1830
+ for (const [index, candidate] of candidates.entries()) {
1831
+ if (prepareCompletionYamlProjection(safe, [candidate], new Set([0]), [ordered[index]]).state === "unsupported") {
1832
+ throw new CompletionYamlProjectionRefusal("unsupported_selected_data");
1833
+ }
1834
+ }
1835
+ }
1836
+ let shown = 0, textBudget = safe.text_budget;
1837
+ let references = new Set();
1838
+ const fitPrefix = () => {
1839
+ let minimumBytes = 0;
1840
+ for (let count = 1; count <= candidates.length; count++) {
1841
+ minimumBytes += candidates[count - 1].baseBytes + (count > 1 ? 1 : 0);
1842
+ if (!frontmatter && minimumBytes > maxBytes)
1843
+ break;
1844
+ if (count > shown && measure(count, references) <= maxBytes)
1845
+ shown = count;
1846
+ }
1847
+ };
1848
+ fitPrefix();
1849
+ if (shared && textBudget === undefined) {
1850
+ const maximum = Math.floor(maxBytes / Math.max(1, candidates.length));
1851
+ const boundaries = [...new Set(ordered.slice(0, candidates.length).flatMap(member => {
1852
+ const output = member.primary_text?.output;
1853
+ const diagnostic = member.diagnostic_excerpt?.field === "command" ? undefined : member.diagnostic_excerpt;
1854
+ const tracked = output ?? diagnostic;
1855
+ const text = member.completion_text?.text ?? output?.preview ?? diagnostic?.text ?? member.output;
1856
+ const size = tracked?.bytes_total ?? (text === undefined ? undefined : Buffer.byteLength(text));
1857
+ const points = size !== undefined && size <= maximum ? [size] : [];
1858
+ // Two displayed omitted counts can shrink together at decimal boundaries.
1859
+ // Their scalar positions need byte coordinates, including a possible CRLF retraction.
1860
+ if (tracked && text !== undefined)
1861
+ for (let power = 10; power <= tracked.chars_total; power *= 10) {
1862
+ const count = tracked.chars_total - power + 1;
1863
+ if (count <= 0 || count > text.length)
1864
+ continue;
1865
+ const scalars = [...text];
1866
+ if (count > scalars.length)
1867
+ continue;
1868
+ const end = Buffer.byteLength(scalars.slice(0, count).join("")) + (scalars[count - 1] === "\r" && scalars[count] === "\n" ? 1 : 0);
1869
+ if (end <= maximum)
1870
+ points.push(end);
1871
+ }
1872
+ return points;
1873
+ }))].sort((a, b) => a - b);
1874
+ // Completing a value removes omission scaffolding: check those discontinuities before omitting rows.
1875
+ for (const point of boundaries) {
1876
+ if (shown === candidates.length)
1877
+ break;
1878
+ candidates = makeCandidates(point);
1879
+ fitPrefix();
1880
+ }
1881
+ const fits = (quota) => {
1882
+ candidates = makeCandidates(quota);
1883
+ return measure(shown, references) <= maxBytes;
1884
+ };
1885
+ let low = 0, high = maximum, best = 0;
1886
+ while (low <= high) {
1887
+ const middle = Math.floor((low + high) / 2);
1888
+ if (fits(middle)) {
1889
+ best = middle;
1890
+ low = middle + 1;
1891
+ }
1892
+ else
1893
+ high = middle - 1;
1894
+ }
1895
+ // Cost is monotone between these count/scaffolding discontinuities, not across them.
1896
+ for (let index = 0; index < boundaries.length; index++) {
1897
+ const point = boundaries[index];
1898
+ if (point <= best || !fits(point))
1899
+ continue;
1900
+ best = point;
1901
+ low = point + 1;
1902
+ high = (boundaries[index + 1] ?? maximum + 1) - 1;
1903
+ while (low <= high) {
1904
+ const middle = Math.floor((low + high) / 2);
1905
+ if (fits(middle)) {
1906
+ best = middle;
1907
+ low = middle + 1;
1908
+ }
1909
+ else
1910
+ high = middle - 1;
1911
+ }
1912
+ }
1913
+ textBudget = best;
1914
+ candidates = makeCandidates(best);
1915
+ }
1916
+ // Outcome rows have priority. References of wholly omitted results remain covered by their result omission/inventory.
1917
+ for (const item of safe.file_navigation ?? []) {
1918
+ const index = ordered.indexOf(safe.members[item.member_index]);
1919
+ if (index >= shown)
1920
+ continue;
1921
+ const next = new Set(references);
1922
+ next.add(index);
1923
+ if (measure(shown, next) > maxBytes)
1924
+ break;
1925
+ references = next;
1926
+ }
1927
+ const result = assembler === "legacy" ? assemble(shown, references) : materializeCompletionYamlProjection(prepare(shown, references));
1928
+ if (Buffer.byteLength(result.message, "utf8") > maxBytes)
1929
+ throw new Error("Run completion batch header exceeds the model byte limit");
1930
+ return { ...result, ...(shared ? { textBudget } : {}) };
1931
+ }
1932
+ /** Freeze the share and selected text, not 64 independent 48KiB copies in the journal. */
1933
+ function retainSharedCompletionTexts(batch) {
1934
+ if (!isSharedCompletionLayout(batch.result_layout))
1935
+ return batch;
1936
+ const projected = projectCompletionRecords({ ...batch, text_budget: undefined });
1937
+ const order = batch.numbering ? batch.numbering.member_order.map(slot => batch.members[slot]) : orderRunCompletionMembers(batch.members);
1938
+ const selected = new Set(order.slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS));
1939
+ return parseBatch({ ...batch, text_budget: projected.textBudget, members: batch.members.map(member => {
1940
+ const { completion_text, ...rest } = member;
1941
+ return completion_text && selected.has(member)
1942
+ ? { ...rest, completion_text: projectRunCompletionText(completion_text.text, projected.textBudget) } : rest;
1943
+ }) });
1944
+ }
1945
+ function projectRetainedRecordCompletionBatch(safe) {
1946
+ const single = safe.single_result === true;
1947
+ const ordered = orderRunCompletionMembers(safe.members), byIdentity = new Map(safe.members.map(member => [memberIdentity(member), member]));
1948
+ const times = safe.members.map(member => member.terminal_at).sort();
1949
+ const candidates = ordered.slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS).map((member, index) => {
1950
+ const row = completionRecordRow(member, index, byIdentity, single);
1951
+ const navigation = safe.file_navigation?.find(item => safe.members[item.member_index] === member);
1952
+ if (navigation?.locations.state === "unavailable")
1953
+ row.pieces.push(completionPiece("run_files_unavailable", navigation.locations.reason, single ? 0 : 2));
1954
+ return row;
1955
+ });
1956
+ const assemble = (rows, references) => {
1957
+ const pieces = single ? [completionPiece("completion", safe.batch_id, 0, false), completionPiece("presentation", "ax_records", 0, false)]
1958
+ : [completionPiece("completions", String(safe.members.length)), completionPiece("batch", safe.batch_id, 0, false),
1959
+ completionPiece("presentation", safe.presentation_format, 0, false)];
1960
+ if (safe.members.length > 1)
1961
+ for (const status of ["done", "failed", "killed", "exited"]) {
1962
+ const count = safe.members.filter(member => member.status === status).length;
1963
+ if (count)
1964
+ pieces.push(completionPiece(`${status}_count`, String(count)));
1965
+ }
1966
+ if (!single && rows.some(row => row.legacy))
1967
+ pieces.push(completionPiece("legacy_basis", "Summary/legacy output: original completeness/privacy unproven [omitted: unknown]"));
1968
+ if (rows.length)
1969
+ pieces.push(...(single ? [] : [{ text: "- members:", compact: true }]), ...rows.flatMap(row => row.pieces));
1970
+ if (single && rows.some(row => row.legacy))
1971
+ pieces.push(completionPiece("legacy_basis", "Summary/legacy output: original completeness/privacy unproven [omitted: unknown]"));
1972
+ const omitted = safe.members.length - rows.length;
1973
+ if (omitted) {
1974
+ pieces.push(single ? completionPiece("result", "omitted (model byte limit)") : completionPiece("members_omitted", String(omitted)));
1975
+ if (!safe.inventory_source)
1976
+ pieces.push(completionPiece("inventory_warning", "No inventory reference is retained in this packet."));
1977
+ }
1978
+ const summaries = Object.fromEntries(ordered.slice(0, rows.length).filter(isRoutineCompletionSummary).map(member => [member.status, member.summary]));
1979
+ const technical = [formatMarkdownField("window_start", times[0]), formatMarkdownField("window_end", times.at(-1)),
1980
+ ...(Object.keys(summaries).length ? [formatMarkdownField("runtime_summaries", formatLiteralBlock(JSON.stringify(summaries)))] : []),
1981
+ ...(safe.inventory_source ? [formatMarkdownField("member_inventory", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2)))] : []),
1982
+ ...(rows.some(row => row.technical) ? [(single ? rows[0].technical : formatMarkdownField("member_details", rows.map(row => row.technical).filter(Boolean).join("\n")))] : [])];
1983
+ if (safe.navigation_handoff !== undefined)
1984
+ technical.push(formatMarkdownField("file_investigation", "optional; current session"), formatInspectNextAction("runtime", "runs", true));
1985
+ if (safe.file_navigation !== undefined) {
1986
+ pieces.push(completionPiece("run_files_shown", String(references.length)), completionPiece("run_files_omitted", String(safe.members.length - references.length)));
1987
+ technical.push(formatMarkdownField("run_files", [formatMarkdownField("total", String(safe.members.length)),
1988
+ formatMarkdownField("scope", "Captured completion members only; mutable locations, not body/privacy/terminal proof."), ...references].join("\n")));
1989
+ }
1990
+ pieces.push(completionPiece("technical", technical.join("\n"), 0, false));
1991
+ let message = "";
1992
+ const compact_ranges = [];
1993
+ for (const piece of pieces) {
1994
+ if (message)
1995
+ message += "\n";
1996
+ const start = message.length;
1997
+ message += piece.text;
1998
+ if (piece.compact)
1999
+ compact_ranges.push([start, message.length]);
2000
+ }
2001
+ return { message, membersShown: rows.length, compact_ranges };
2002
+ };
2003
+ const rows = [], references = [];
2004
+ for (const candidate of candidates) {
2005
+ if (Buffer.byteLength(assemble([...rows, candidate], references).message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
2006
+ break;
2007
+ rows.push(candidate);
2008
+ }
2009
+ for (const item of safe.file_navigation ?? []) {
2010
+ const member = safe.members[item.member_index], index = ordered.indexOf(member);
2011
+ const row = formatMarkdownField(single ? "location" : `#${index}`, formatLiteralBlock(JSON.stringify({ run: member.run, run_instance_id: member.run_instance_id, locations: item.locations })));
2012
+ if (Buffer.byteLength(assemble(rows, [...references, row]).message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
2013
+ break;
2014
+ references.push(row);
2015
+ }
2016
+ const result = assemble(rows, references);
2017
+ if (Buffer.byteLength(result.message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
2018
+ throw new Error("Run completion batch header exceeds the model byte limit");
2019
+ return result;
2020
+ }
2021
+ /** Single full model content plus compiler-owned UI offsets; no source reads or saved-packet mutation. */
2022
+ export function projectRunCompletionBatchPresentation(batch) {
683
2023
  const safe = parseBatch(batch);
2024
+ if (safe.presentation_format !== "ax_records" && safe.presentation_format !== "ax_records_v3")
2025
+ return { content: projectCompletionBatchMessage(safe).message };
2026
+ const result = safe.result_layout !== undefined ? projectCompletionRecords(safe) : projectRetainedRecordCompletionBatch(safe);
2027
+ if (safe.result_layout === "completion_yaml_core_v1")
2028
+ return { content: result.message, presentation: Object.freeze({
2029
+ format: "completion_yaml_core_v1", content_sha256: createHash("sha256").update(result.message).digest("hex"),
2030
+ }) };
2031
+ return { content: result.message, presentation: Object.freeze({ format: isFrontmatterCompletionLayout(safe.result_layout) ? "completion_frontmatter_v1" : safe.presentation_format, content_sha256: createHash("sha256").update(result.message).digest("hex"),
2032
+ ...("blocks" in result ? { blocks: result.blocks } : {}),
2033
+ compact_ranges: Object.freeze(result.compact_ranges.map(range => Object.freeze(range))) }) };
2034
+ }
2035
+ function completionRecordDetailsGap(value, single, terminal) {
2036
+ let rest = value;
2037
+ while (rest !== (terminal ? "" : "\n")) {
2038
+ const opening = /^\n( *)- (technical|run_files|result_inventory):\n( *)(`{3,})text\n/u.exec(rest);
2039
+ if (!opening)
2040
+ return false;
2041
+ const [, indent, key, padding, fence] = opening;
2042
+ if (indent !== (key === "result_inventory" || single ? "" : " ") || padding !== indent + " ")
2043
+ return false;
2044
+ const ending = `\n${padding}${fence}`, end = rest.indexOf(ending, opening[0].length);
2045
+ if (end < 0)
2046
+ return false;
2047
+ const lines = rest.slice(opening[0].length, end).split("\n");
2048
+ if (lines.some(line => !line.startsWith(padding)))
2049
+ return false;
2050
+ try {
2051
+ const data = JSON.parse(lines.map(line => line.slice(padding.length)).join("\n"));
2052
+ if (!data || typeof data !== "object" || Array.isArray(data))
2053
+ return false;
2054
+ }
2055
+ catch {
2056
+ return false;
2057
+ }
2058
+ rest = rest.slice(end + ending.length);
2059
+ }
2060
+ return true;
2061
+ }
2062
+ /** Display consistency only, never provenance authentication or a new read/action capability. */
2063
+ function readRunCompletionCompactText(content, value) {
2064
+ try {
2065
+ if (Buffer.byteLength(content, "utf8") > Limits.RUN_COMPLETION_MODEL_MAX_BYTES)
2066
+ return undefined;
2067
+ const record = Object.getOwnPropertyDescriptors(value);
2068
+ if (Object.keys(record).sort().join() !== "compact_ranges,content_sha256,format" || Object.values(record).some(field => !Object.hasOwn(field, "value")))
2069
+ return undefined;
2070
+ const header = completionRecordHeader(content), records = header ? undefined : readRunCompletionRecordMarker(content);
2071
+ if ((!header && !records) || record.format.value !== (header?.format ?? "ax_records") || record.content_sha256.value !== createHash("sha256").update(content).digest("hex"))
2072
+ return undefined;
2073
+ const flat = !!records && (records.single || !!records.headers);
2074
+ const indent = records ? (flat ? " " : " ") : header.single ? " " : " ";
2075
+ const ranges = record.compact_ranges.value;
2076
+ if (!Array.isArray(ranges) || ranges.length === 0 || ranges.length > content.length || Object.keys(ranges).length !== ranges.length)
2077
+ return undefined;
2078
+ const text = [];
2079
+ let previous = 0, fence;
2080
+ const boundary = (index) => !(index > 0 && index < content.length && /[\uD800-\uDBFF]/u.test(content[index - 1]) && /[\uDC00-\uDFFF]/u.test(content[index]))
2081
+ && !(content[index - 1] === "\r" && content[index] === "\n");
2082
+ for (let index = 0; index < ranges.length; index++) {
2083
+ const descriptor = Object.getOwnPropertyDescriptor(ranges, String(index));
2084
+ if (!descriptor || !Object.hasOwn(descriptor, "value"))
2085
+ return undefined;
2086
+ const range = descriptor.value;
2087
+ if (!Array.isArray(range) || range.length !== 2 || Object.keys(range).length !== 2)
2088
+ return undefined;
2089
+ const start = Object.getOwnPropertyDescriptor(range, "0"), end = Object.getOwnPropertyDescriptor(range, "1");
2090
+ if (!start || !end || !Object.hasOwn(start, "value") || !Object.hasOwn(end, "value"))
2091
+ return undefined;
2092
+ const a = start.value, b = end.value;
2093
+ if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b) || a < previous || b <= a || b > content.length || !boundary(a) || !boundary(b))
2094
+ return undefined;
2095
+ // Only compiler framing/fences and whole technical/reference blocks may disappear. Authored values stay padded inside ranges.
2096
+ const gap = content.slice(previous, a);
2097
+ if (index === 0) {
2098
+ if (a !== (header?.single ? header.length : 0))
2099
+ return undefined;
2100
+ }
2101
+ else if (records) {
2102
+ let remaining = gap;
2103
+ if (fence) {
2104
+ const closing = `\n${indent}${fence}`;
2105
+ if (!remaining.startsWith(closing))
2106
+ return undefined;
2107
+ remaining = remaining.slice(closing.length);
2108
+ fence = undefined;
2109
+ }
2110
+ const opening = (flat ? /^\n (`{3,})text\n$/u : /^\n (`{3,})text\n$/u).exec(remaining);
2111
+ if (opening) {
2112
+ const preview = flat ? /(?:^|\n)- (?:preview|result):(?:\n …)?$/u : /(?:^|\n) - preview:(?:\n …)?$/u;
2113
+ if (!preview.test(content.slice(0, previous)))
2114
+ return undefined;
2115
+ fence = opening[1];
2116
+ }
2117
+ else if (!completionRecordDetailsGap(remaining, flat, false))
2118
+ return undefined;
2119
+ }
2120
+ else if (fence) {
2121
+ if (gap !== `\n${indent}${fence}\n`)
2122
+ return undefined;
2123
+ fence = undefined;
2124
+ }
2125
+ else if (gap !== "\n" && (header.single || !/^\n- batch: [^\r\n\u2028\u2029]{1,128}\n- presentation: ax_records(?:_v3)?\n$/u.test(gap))) {
2126
+ const opening = (header.single ? /^\n (`{3,})text\n$/u : /^\n (`{3,})text\n$/u).exec(gap);
2127
+ const preview = header.single ? /(?:^|\n)- preview:(?:\n …)?$/u : /(?:^|\n) - preview:(?:\n …)?$/u;
2128
+ if (!opening || !preview.test(content.slice(0, previous)))
2129
+ return undefined;
2130
+ fence = opening[1];
2131
+ }
2132
+ const selected = content.slice(a, b);
2133
+ if (fence && [...selected.matchAll(/`+/gu)].some(run => run[0].length >= fence.length))
2134
+ return undefined;
2135
+ text.push(selected);
2136
+ previous = b;
2137
+ }
2138
+ if (records) {
2139
+ let remaining = content.slice(previous, records.start);
2140
+ if (fence) {
2141
+ const closing = `\n${indent}${fence}`;
2142
+ if (!remaining.startsWith(closing))
2143
+ return undefined;
2144
+ remaining = remaining.slice(closing.length);
2145
+ }
2146
+ if (!completionRecordDetailsGap(remaining, flat, true))
2147
+ return undefined;
2148
+ }
2149
+ else {
2150
+ const tail = content.slice(previous);
2151
+ if (fence ? !tail.startsWith(`\n${indent}${fence}\n- technical:\n`) : !tail.startsWith("\n- technical:\n"))
2152
+ return undefined;
2153
+ }
2154
+ return text.join("\n");
2155
+ }
2156
+ catch {
2157
+ return undefined;
2158
+ }
2159
+ }
2160
+ function ownCompletionData(value) {
2161
+ if (value === null || typeof value !== "object")
2162
+ return value;
2163
+ const descriptors = Object.getOwnPropertyDescriptors(value);
2164
+ if (Object.values(descriptors).some(field => !Object.hasOwn(field, "value")))
2165
+ throw new Error("Accessor presentation data");
2166
+ if (Array.isArray(value)) {
2167
+ if (Object.keys(descriptors).length !== value.length + 1)
2168
+ throw new Error("Sparse presentation data");
2169
+ return Array.from({ length: value.length }, (_, index) => {
2170
+ if (!descriptors[String(index)])
2171
+ throw new Error("Sparse presentation data");
2172
+ return ownCompletionData(descriptors[String(index)].value);
2173
+ });
2174
+ }
2175
+ return Object.fromEntries(Object.entries(descriptors).map(([key, field]) => [key, ownCompletionData(field.value)]));
2176
+ }
2177
+ /** Invalid/missing hints reveal all content; old captures keep their existing host renderer. */
2178
+ export function readRunCompletionPresentation(content, value) {
2179
+ const core = readCompletionYamlCarrier(content);
2180
+ let declaredCore = false;
2181
+ if (value !== null && typeof value === "object" && !utilTypes.isProxy(value)) {
2182
+ try {
2183
+ declaredCore = Object.getOwnPropertyDescriptor(value, "format")?.value === completionYamlCodec;
2184
+ }
2185
+ catch { /* Not own data. */ }
2186
+ }
2187
+ if (core.state === "available" || declaredCore) {
2188
+ const full = { text: content, compact: false, records: true, yaml: true };
2189
+ const hint = copyCompletionYamlData(value);
2190
+ if (core.state !== "available" || !completionYamlMapping(hint) ||
2191
+ !completionYamlKeys(hint, ["format", "content_sha256"]) || hint.format !== completionYamlCodec ||
2192
+ hint.content_sha256 !== createHash("sha256").update(content).digest("hex"))
2193
+ return full;
2194
+ const segments = [];
2195
+ for (const [index, record] of core.packet.records.entries()) {
2196
+ if (index)
2197
+ segments.push({ kind: "separator", text: "\n" });
2198
+ const metadata = Object.fromEntries(Object.entries(record.metadata).filter(([key]) => key !== "references" && key !== "run_files"));
2199
+ const header = renderYamlMetadata(metadata, completionYamlMaxBytes);
2200
+ if (header.state !== "available")
2201
+ return full;
2202
+ segments.push({ kind: "header", text: record.parts.length ? `---\n${header.text}---` : header.text.replace(/\n$/u, "") });
2203
+ for (const part of record.parts)
2204
+ segments.push("body" in part ? { kind: "body", text: part.body } : { kind: "marker", text: part.marker });
2205
+ }
2206
+ if (core.packet.service) {
2207
+ const service = renderYamlMetadata(core.packet.service, completionYamlMaxBytes);
2208
+ if (service.state !== "available")
2209
+ return full;
2210
+ if (segments.length)
2211
+ segments.push({ kind: "separator", text: "\n" });
2212
+ segments.push({ kind: "service", text: service.text.replace(/\n$/u, "") });
2213
+ }
2214
+ return { text: segments.map(segment => segment.text).join("\n"), compact: true, records: true, yaml: true,
2215
+ segments: Object.freeze(segments.map(segment => Object.freeze(segment))) };
2216
+ }
2217
+ const fresh = readFrontmatterFrame(content);
2218
+ let declaredFormat;
2219
+ try {
2220
+ declaredFormat = value && typeof value === "object" ? Object.getOwnPropertyDescriptor(value, "format")?.value : undefined;
2221
+ }
2222
+ catch { /* Unreadable hints fall back to full content. */ }
2223
+ if (fresh || declaredFormat === "completion_frontmatter_v1") {
2224
+ try {
2225
+ const hint = ownCompletionData(value);
2226
+ if (!fresh || !hint || Object.keys(hint).sort().join() !== "blocks,compact_ranges,content_sha256,format" || hint.format !== "completion_frontmatter_v1" || hint.content_sha256 !== createHash("sha256").update(content).digest("hex") || JSON.stringify(hint.blocks) !== JSON.stringify(fresh.blocks) || JSON.stringify(hint.compact_ranges) !== JSON.stringify(fresh.blocks.map(([, a, b]) => [a, b])))
2227
+ throw new Error("Invalid completion presentation");
2228
+ return { text: fresh.blocks.map(([, a, b]) => content.slice(a, b)).join("\n"), compact: true, records: true, blocks: Object.freeze(fresh.blocks.map(block => Object.freeze(block))) };
2229
+ }
2230
+ catch {
2231
+ return { text: content, compact: false, records: true };
2232
+ }
2233
+ }
2234
+ const header = completionRecordHeader(content), records = header ? undefined : readRunCompletionRecordMarker(content);
2235
+ let declared = false;
2236
+ try {
2237
+ const format = Object.getOwnPropertyDescriptor(value, "format")?.value;
2238
+ declared = format === "ax_records" || format === "ax_records_v3";
2239
+ }
2240
+ catch { /* absent/unreadable UI hint */ }
2241
+ if (!header && !records && !declared)
2242
+ return undefined;
2243
+ const text = header || records ? readRunCompletionCompactText(content, value) : undefined;
2244
+ return { text: text ?? content, compact: text !== undefined, ...((header?.single || records?.single) ? { single: true } : {}),
2245
+ ...(records ? { records: true } : {}) };
2246
+ }
2247
+ /** Retained draft codecs only. Journals store snapshots, not full text: replay must reproduce these exact bytes. */
2248
+ function projectRetainedCompletionBatchMessage(safe) {
2249
+ const batchLegacyBasis = safe.presentation_format === "ax_records_v2";
2250
+ const terminalTimes = safe.members.map(member => member.terminal_at).sort();
2251
+ const statuses = ["done", "failed", "killed", "exited"].flatMap(status => {
2252
+ const count = safe.members.filter(member => member.status === status).length;
2253
+ return count ? [formatMarkdownField(batchLegacyBasis ? `${status}_count` : status, String(count))] : [];
2254
+ });
2255
+ const header = [formatMarkdownField("completions", String(safe.members.length)), formatMarkdownField("batch", safe.batch_id),
2256
+ formatMarkdownField("window_start", terminalTimes[0]), formatMarkdownField("window_end", terminalTimes.at(-1)),
2257
+ ...(batchLegacyBasis ? [...statuses, formatMarkdownField("legacy_basis", "All legacy summary/output fields: original completeness/privacy unproven."),
2258
+ formatMarkdownField("legacy_upstream", "[omitted: unknown]")] : [formatMarkdownField("statuses", statuses.join("\n"))]),
2259
+ ...(safe.navigation_handoff ? [formatMarkdownField("file_investigation", "optional; current session"), formatInspectNextAction("runtime", "runs", true)] : []),
2260
+ ...(safe.inventory_source ? [formatMarkdownField("member_inventory", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2)))] : [])];
2261
+ const byIdentity = new Map(safe.members.map(member => [memberIdentity(member), member]));
2262
+ const assemble = (rows, references) => {
2263
+ const omitted = safe.members.length - rows.length;
2264
+ return [...header, ...(safe.members.length === 1 ? rows : rows.length ? [formatMarkdownField("members", rows.map(row => formatMarkdownField("member", row)).join("\n"))] : []),
2265
+ ...(omitted ? [formatMarkdownField("members_omitted", String(omitted)),
2266
+ ...(!safe.inventory_source ? [formatMarkdownField("inventory_warning", "No inventory reference is retained in this packet.")] : [])] : []),
2267
+ ...(safe.file_navigation === undefined ? [] : [formatMarkdownField("run_files", [
2268
+ formatMarkdownField("shown", String(references.length)), formatMarkdownField("omitted", String(safe.members.length - references.length)),
2269
+ formatMarkdownField("total", String(safe.members.length)),
2270
+ formatMarkdownField("scope", "Captured completion members only; mutable locations, not body/privacy/terminal proof."),
2271
+ ...references.map(row => formatMarkdownField("member", row)),
2272
+ ].join("\n"))])].join("\n");
2273
+ };
2274
+ const rows = [];
2275
+ for (const member of orderRunCompletionMembers(safe.members).slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)) {
2276
+ const row = formatRetainedCompletionMember(member, byIdentity, batchLegacyBasis);
2277
+ if (Buffer.byteLength(assemble([...rows, row], []), "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
2278
+ break;
2279
+ rows.push(row);
2280
+ }
2281
+ const references = [];
2282
+ for (const item of safe.file_navigation ?? []) {
2283
+ const member = safe.members[item.member_index];
2284
+ const row = formatLiteralBlock(JSON.stringify({ run: member.run, run_instance_id: member.run_instance_id, locations: item.locations }));
2285
+ if (Buffer.byteLength(assemble(rows, [...references, row]), "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
2286
+ break;
2287
+ references.push(row);
2288
+ }
2289
+ const message = assemble(rows, references);
2290
+ if (Buffer.byteLength(message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
2291
+ throw new Error("Run completion batch header exceeds the model byte limit");
2292
+ return { message, membersShown: rows.length };
2293
+ }
2294
+ /** Format one immutable batch for a model-bound custom message, without source reads or phase changes. */
2295
+ export function formatRunCompletionBatchMessage(batch) {
2296
+ return projectCompletionBatchMessage(parseBatch(batch)).message;
2297
+ }
2298
+ function projectCompletionBatchMessage(safe) {
2299
+ if (safe.result_layout !== undefined)
2300
+ return projectCompletionRecords(safe);
2301
+ if (safe.presentation_format === "ax_records" || safe.presentation_format === "ax_records_v3")
2302
+ return projectRetainedRecordCompletionBatch(safe);
2303
+ if (safe.presentation_format !== undefined)
2304
+ return projectRetainedCompletionBatchMessage(safe);
684
2305
  const terminalTimes = safe.members.map((member) => member.terminal_at).sort();
685
2306
  const header = [
686
2307
  `Actor completions: ${safe.members.length}`,
687
2308
  `Batch: \`${compactModelText(safe.batch_id, 128)}\``,
688
2309
  `Window: \`${terminalTimes[0]}\` → \`${terminalTimes.at(-1)}\``,
689
2310
  `Statuses: \`${formatStatusCounts(safe.members)}\``,
2311
+ ...(safe.navigation_handoff ? [formatRunFileNavigationHandoff()] : []),
2312
+ ...(safe.inventory_source ? ["Complete captured member inventory retained at capture:", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2))] : []),
690
2313
  ];
2314
+ const overflow = (omitted) => omitted > 0 ? [`… ${omitted} more completion(s) retained in batch.${safe.inventory_source ? "" : " No inventory reference is retained in this packet."}`] : [];
691
2315
  const byIdentity = new Map(safe.members.map((member) => [
692
2316
  memberIdentity(member),
693
2317
  member,
694
2318
  ]));
695
- const candidateRows = orderCompletionForest(safe.members)
2319
+ const navigation = safe.file_navigation;
2320
+ const navigationCounts = (shown) => navigation === undefined ? [] :
2321
+ [`Run files (captured locations; mutable): shown=${shown} omitted=${safe.members.length - shown} total=${safe.members.length}; captured completion members only; not body/privacy/terminal proof.`];
2322
+ const candidateRows = orderRunCompletionMembers(safe.members)
696
2323
  .slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)
697
- .map((member) => formatCompletionMember(member, byIdentity));
2324
+ .map((member) => formatCompletionMember(member, byIdentity, safe.excerpt_format === "bracketed_omissions_v1"));
698
2325
  const rows = [];
699
2326
  for (const row of candidateRows) {
700
2327
  const omitted = safe.members.length - rows.length - 1;
@@ -702,20 +2329,262 @@ export function formatRunCompletionBatchMessage(batch) {
702
2329
  ...header,
703
2330
  ...rows,
704
2331
  row,
705
- ...(omitted > 0 ? [`… ${omitted} more completion(s) retained in batch.`] : []),
2332
+ ...overflow(omitted),
2333
+ ...navigationCounts(0),
706
2334
  ].join("\n");
707
2335
  if (Buffer.byteLength(candidate, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
708
2336
  break;
709
2337
  rows.push(row);
710
2338
  }
711
2339
  const omitted = safe.members.length - rows.length;
2340
+ const references = [];
2341
+ for (const item of navigation ?? []) {
2342
+ const member = safe.members[item.member_index];
2343
+ const row = formatLiteralBlock(JSON.stringify({ run: member.run, run_instance_id: member.run_instance_id, locations: item.locations }));
2344
+ const candidate = [...header, ...rows, ...overflow(omitted), ...navigationCounts(references.length + 1), ...references, row].join("\n");
2345
+ if (Buffer.byteLength(candidate, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES)
2346
+ break;
2347
+ references.push(row);
2348
+ }
712
2349
  const message = [
713
2350
  ...header,
714
2351
  ...rows,
715
- ...(omitted > 0 ? [`… ${omitted} more completion(s) retained in batch.`] : []),
2352
+ ...overflow(omitted),
2353
+ ...navigationCounts(references.length),
2354
+ ...references,
716
2355
  ].join("\n");
717
2356
  if (Buffer.byteLength(message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) {
718
2357
  throw new Error("Run completion batch header exceeds the model byte limit");
719
2358
  }
720
- return message;
2359
+ return { message, membersShown: rows.length };
2360
+ }
2361
+ const completionYamlCodec = "completion_yaml_core_v1";
2362
+ const completionYamlServiceKey = "_completion";
2363
+ const completionYamlMaxBytes = 49152;
2364
+ // Snapshot only own data: never coerce or invoke caller accessors/serialization hooks.
2365
+ function copyCompletionYamlData(input) {
2366
+ let remaining = completionYamlMaxBytes;
2367
+ const active = new Set();
2368
+ const copy = (value, depth) => {
2369
+ if (--remaining < 0 || depth > 64)
2370
+ return undefined;
2371
+ if (value === null || typeof value === "boolean")
2372
+ return value;
2373
+ if (typeof value === "number")
2374
+ return Number.isFinite(value) && !Object.is(value, -0) ? value : undefined;
2375
+ if (typeof value === "string") {
2376
+ remaining -= Buffer.byteLength(value, "utf8");
2377
+ return remaining >= 0 && value.isWellFormed() ? value : undefined;
2378
+ }
2379
+ if (!value || typeof value !== "object" || utilTypes.isProxy(value) || active.has(value))
2380
+ return undefined;
2381
+ const array = Array.isArray(value), prototype = Object.getPrototypeOf(value);
2382
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null)
2383
+ return undefined;
2384
+ const output = array ? [] : {};
2385
+ const length = array ? Object.getOwnPropertyDescriptor(value, "length")?.value : undefined;
2386
+ if (array && (!Number.isSafeInteger(length) || length < 0 || length > remaining))
2387
+ return undefined;
2388
+ active.add(value);
2389
+ let count = 0;
2390
+ for (const key of Reflect.ownKeys(value)) {
2391
+ if (array && key === "length")
2392
+ continue;
2393
+ if (typeof key !== "string" || !key.isWellFormed() || (array && key !== String(count)))
2394
+ return undefined;
2395
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2396
+ if (!descriptor || !Object.hasOwn(descriptor, "value") || !descriptor.enumerable)
2397
+ return undefined;
2398
+ remaining -= Buffer.byteLength(key, "utf8");
2399
+ const child = copy(descriptor.value, depth + 1);
2400
+ if (child === undefined)
2401
+ return undefined;
2402
+ Object.defineProperty(output, key, { value: child, enumerable: true, writable: true, configurable: true });
2403
+ count++;
2404
+ }
2405
+ active.delete(value);
2406
+ return array && count !== length ? undefined : output;
2407
+ };
2408
+ try {
2409
+ return copy(input, 0);
2410
+ }
2411
+ catch {
2412
+ return undefined;
2413
+ }
2414
+ }
2415
+ function completionYamlMapping(value) {
2416
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2417
+ }
2418
+ function completionYamlKeys(value, required, optional = []) {
2419
+ return required.every(key => Object.hasOwn(value, key)) && Object.keys(value).every(key => required.includes(key) || optional.includes(key));
2420
+ }
2421
+ /**
2422
+ * Unadmitted data-only prototype, not source/privacy/identity/acceptance authority.
2423
+ * Grammar: canonical YAML headers, no physical blank lines. Bare headers end LF;
2424
+ * another record adds LF. Body headers are --- LF / YAML / --- LF then raw parts;
2425
+ * another record adds LF LF, independently of authored trailing CR/LF. Parts are
2426
+ * ordered single-key body/marker UTF-16 lengths, never offsets. Only the first
2427
+ * _completion carries codec/batch_id/service. Absent body is DTO parts:[] (no
2428
+ * wire parts); selected empty body is body:0 and has a closed header. Zero rows
2429
+ * is one bare service-only header requiring nonempty selected service facts.
2430
+ * No clipping, fair fitting, source reads, admission or production callers.
2431
+ */
2432
+ export function encodeCompletionYamlCarrier(input) {
2433
+ const unavailable = { state: "unavailable" };
2434
+ const packet = copyCompletionYamlData(input);
2435
+ if (!completionYamlMapping(packet) || !completionYamlKeys(packet, ["batchId", "records"], ["service"]) ||
2436
+ typeof packet.batchId !== "string" || !packet.batchId.length || !Array.isArray(packet.records) || packet.records.length > 64 ||
2437
+ (Object.hasOwn(packet, "service") && (!completionYamlMapping(packet.service) || !Object.keys(packet.service).length)) ||
2438
+ (!packet.records.length && !Object.hasOwn(packet, "service")))
2439
+ return unavailable;
2440
+ const rows = [];
2441
+ for (let index = 0; index < Math.max(1, packet.records.length); index++) {
2442
+ const row = packet.records[index];
2443
+ const header = {};
2444
+ const service = {};
2445
+ if (index === 0) {
2446
+ service.codec = completionYamlCodec;
2447
+ service.batch_id = packet.batchId;
2448
+ if (Object.hasOwn(packet, "service"))
2449
+ service.service = packet.service;
2450
+ }
2451
+ const raw = [], lengths = [];
2452
+ if (packet.records.length) {
2453
+ if (!completionYamlMapping(row) || !completionYamlKeys(row, ["metadata", "parts"]) ||
2454
+ !completionYamlMapping(row.metadata) || !Array.isArray(row.parts))
2455
+ return unavailable;
2456
+ const meta = row.metadata;
2457
+ if (!(meta.index === "?" || (typeof meta.index === "number" && Number.isSafeInteger(meta.index) && meta.index >= 0)) ||
2458
+ typeof meta.run !== "string" || !meta.run.length || typeof meta.state !== "string" || !meta.state.length ||
2459
+ (Object.hasOwn(meta, "kind") && meta.kind !== "primary" && meta.kind !== "diagnostic") ||
2460
+ Object.hasOwn(meta, completionYamlServiceKey) ||
2461
+ // JS enumerates array-index keys before semantic core fields; do not silently reorder the header.
2462
+ Object.keys(meta).some(key => /^(0|[1-9]\d*)$/u.test(key) && Number(key) < 4294967295))
2463
+ return unavailable;
2464
+ for (const key of ["index", "run", "state", ...(Object.hasOwn(meta, "kind") ? ["kind"] : []),
2465
+ ...Object.keys(meta).filter(key => !["index", "run", "state", "kind"].includes(key))]) {
2466
+ Object.defineProperty(header, key, { value: meta[key], enumerable: true });
2467
+ }
2468
+ let hasBody = false;
2469
+ for (const part of row.parts) {
2470
+ if (!completionYamlMapping(part) || Object.keys(part).length !== 1)
2471
+ return unavailable;
2472
+ const key = Object.keys(part)[0];
2473
+ if ((key !== "body" && key !== "marker") || typeof part[key] !== "string")
2474
+ return unavailable;
2475
+ if (key === "body")
2476
+ hasBody = true;
2477
+ lengths.push({ [key]: part[key].length });
2478
+ raw.push(part[key]);
2479
+ }
2480
+ // An explicit selected empty body is real; empty markers alone cannot justify a wrapper.
2481
+ if (lengths.length && !hasBody && raw.every(text => text.length === 0))
2482
+ return unavailable;
2483
+ if (lengths.length)
2484
+ service.parts = lengths;
2485
+ }
2486
+ if (Object.keys(service).length)
2487
+ header[completionYamlServiceKey] = service;
2488
+ const rendered = renderYamlMetadata(header, completionYamlMaxBytes);
2489
+ if (rendered.state !== "available" || rendered.text.includes("\n\n"))
2490
+ return unavailable;
2491
+ const text = lengths.length ? `---\n${rendered.text}---\n${raw.join("")}` : rendered.text;
2492
+ rows.push(text + (index + 1 < packet.records.length ? (lengths.length ? "\n\n" : "\n") : ""));
2493
+ }
2494
+ const text = rows.join("");
2495
+ return Buffer.byteLength(text, "utf8") <= completionYamlMaxBytes ? { state: "available", text } : unavailable;
2496
+ }
2497
+ /** Strict whole-carrier decoder. Derived ranges are consistency data, never authenticity. */
2498
+ export function readCompletionYamlCarrier(input) {
2499
+ const unavailable = { state: "unavailable" };
2500
+ if (typeof input !== "string" || !input.isWellFormed() || !input.length || Buffer.byteLength(input, "utf8") > completionYamlMaxBytes)
2501
+ return unavailable;
2502
+ try {
2503
+ let cursor = 0;
2504
+ const packet = { batchId: "", records: [] };
2505
+ const blocks = [];
2506
+ while (cursor < input.length) {
2507
+ if (packet.records.length >= 64)
2508
+ return unavailable;
2509
+ const record = packet.records.length, start = cursor, framed = input.startsWith("---\n", cursor);
2510
+ let yaml;
2511
+ if (framed) {
2512
+ const close = input.indexOf("\n---\n", cursor + 4);
2513
+ if (close < 0)
2514
+ return unavailable;
2515
+ yaml = input.slice(cursor + 4, close + 1);
2516
+ cursor = close + 5;
2517
+ }
2518
+ else {
2519
+ const close = input.indexOf("\n\n", cursor);
2520
+ cursor = close < 0 ? input.length : close + 1;
2521
+ yaml = input.slice(start, cursor);
2522
+ }
2523
+ const document = parseDocument(yaml, { version: "1.2", schema: "core", uniqueKeys: true });
2524
+ if (document.errors.length || document.warnings.length)
2525
+ return unavailable;
2526
+ const header = document.toJS({ maxAliasCount: 0 });
2527
+ if (!completionYamlMapping(header))
2528
+ return unavailable;
2529
+ const canonical = renderYamlMetadata(header, completionYamlMaxBytes);
2530
+ if (canonical.state !== "available" || canonical.text !== yaml)
2531
+ return unavailable;
2532
+ const service = header[completionYamlServiceKey];
2533
+ if (record === 0) {
2534
+ if (!completionYamlMapping(service) || !completionYamlKeys(service, ["codec", "batch_id"], ["parts", "service"]) ||
2535
+ service.codec !== completionYamlCodec || typeof service.batch_id !== "string" || !service.batch_id.length)
2536
+ return unavailable;
2537
+ packet.batchId = service.batch_id;
2538
+ if (Object.hasOwn(service, "service")) {
2539
+ if (!completionYamlMapping(service.service) || !Object.keys(service.service).length)
2540
+ return unavailable;
2541
+ packet.service = service.service;
2542
+ }
2543
+ }
2544
+ else if (service !== undefined && (!completionYamlMapping(service) || !completionYamlKeys(service, ["parts"])))
2545
+ return unavailable;
2546
+ blocks.push({ kind: "header", start, end: cursor, record });
2547
+ delete header[completionYamlServiceKey];
2548
+ const lengths = completionYamlMapping(service) ? service.parts : undefined;
2549
+ if (framed ? !Array.isArray(lengths) || !lengths.length : lengths !== undefined)
2550
+ return unavailable;
2551
+ const parts = [];
2552
+ if (Array.isArray(lengths)) {
2553
+ for (const descriptor of lengths) {
2554
+ if (!completionYamlMapping(descriptor) || Object.keys(descriptor).length !== 1)
2555
+ return unavailable;
2556
+ const key = Object.keys(descriptor)[0];
2557
+ const length = descriptor[key];
2558
+ if ((key !== "body" && key !== "marker") || typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > input.length - cursor)
2559
+ return unavailable;
2560
+ const text = input.slice(cursor, cursor + length);
2561
+ if (!text.isWellFormed())
2562
+ return unavailable;
2563
+ parts.push(key === "body" ? { body: text } : { marker: text });
2564
+ blocks.push({ kind: key, start: cursor, end: cursor + length, record, part: parts.length - 1 });
2565
+ cursor += length;
2566
+ }
2567
+ }
2568
+ if (!Object.keys(header).length) {
2569
+ if (record !== 0 || framed || !packet.service || cursor !== input.length)
2570
+ return unavailable;
2571
+ }
2572
+ else
2573
+ packet.records.push({ metadata: header, parts });
2574
+ if (cursor < input.length) {
2575
+ const gap = framed ? "\n\n" : "\n";
2576
+ if (!input.startsWith(gap, cursor))
2577
+ return unavailable;
2578
+ blocks.push({ kind: "separator", start: cursor, end: cursor + gap.length, record });
2579
+ cursor += gap.length;
2580
+ if (cursor === input.length)
2581
+ return unavailable;
2582
+ }
2583
+ }
2584
+ const encoded = encodeCompletionYamlCarrier(packet);
2585
+ return encoded.state === "available" && encoded.text === input ? { state: "available", packet, blocks } : unavailable;
2586
+ }
2587
+ catch {
2588
+ return unavailable;
2589
+ }
721
2590
  }