@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,25 +1,47 @@
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
 
7
7
  import { createHash, randomUUID } from "node:crypto";
8
- import { existsSync, lstatSync, readFileSync, statSync } from "node:fs";
9
- import { dirname, join } from "node:path";
8
+ import { types as utilTypes } from "node:util";
9
+ import { parseDocument } from "yaml";
10
+ import { renderYamlMetadata } from "./tools-response.ts";
11
+ import { existsSync, lstatSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
12
+ import { basename, dirname, isAbsolute, join, normalize, resolve } from "node:path";
10
13
 
11
14
  import { withFileMutationLock, writeTextAtomic } from "./file-state.ts";
12
15
  import * as Limits from "./limits.ts";
16
+ import { decodeRunDirectoryNavigation, type RunDirectoryNavigation } from "./runs-index.ts";
17
+ import { formatInspectNextAction, formatLiteralBlock, formatMarkdownField, formatRunFileNavigationHandoff } from "./tools-response.ts";
18
+ import { decodeRunCompletionText, projectRunCompletionText, type RunCompletionText, decodeRunPrimaryTextBinding, decodeRunArtifactInventoryBinding, decodeRunResultDiagnosticsBinding, decodeRunResultDiagnosticExcerpt, type RunResultDiagnosticsReference, type RunResultDiagnosticPacketExcerpt, type RunPrimaryTextBinding, type RunArtifactInventoryReference } from "./runs-outcomes.ts";
13
19
 
14
20
  export type RunCompletionDeliveryStatus = "done" | "failed" | "killed" | "exited";
15
21
  export type RunDeliveryPhase = "pending" | "queued" | "presented";
16
22
 
23
+ export type RunCompletionIndexRange =
24
+ | Readonly<{ state: "available"; indices: readonly number[]; next_index: number }>
25
+ | Readonly<{ state: "unavailable"; reason: "range_unavailable"; next_index: number }>;
26
+
27
+ export type RunCompletionNumbering =
28
+ | Readonly<{ kind: "stream"; session_id: string; stream_id: string; first_index: number; member_order: readonly number[] }>
29
+ | Readonly<{ kind: "unavailable"; session_id: string; stream_id?: string;
30
+ reason: "counter_unavailable" | "range_unavailable" | "stream_state_unavailable"; member_order: readonly number[] }>;
31
+
17
32
  export interface RunCompletionBatchMember {
18
33
  artifacts?: Record<string, string>;
34
+ artifact_inventory?: Readonly<RunArtifactInventoryReference> | null;
35
+ artifact_inventory_unavailable?: string;
36
+ result_diagnostics?: Readonly<RunResultDiagnosticsReference> | null;
37
+ result_diagnostics_unavailable?: string;
38
+ diagnostic_excerpt?: RunResultDiagnosticPacketExcerpt;
19
39
  parent_run?: string;
20
40
  parent_run_instance_id?: string;
21
41
  parent_state_dir?: string;
22
42
  output?: string;
43
+ primary_text?: Readonly<RunPrimaryTextBinding>;
44
+ completion_text?: Readonly<RunCompletionText>;
23
45
  run: string;
24
46
  run_instance_id: string;
25
47
  state_dir: string;
@@ -28,13 +50,40 @@ export interface RunCompletionBatchMember {
28
50
  terminal_at: string;
29
51
  }
30
52
 
53
+ export interface RunCompletionInventorySource {
54
+ readonly path: string;
55
+ readonly sha256: string;
56
+ readonly bytes_total: number;
57
+ readonly members_total: number;
58
+ }
59
+
60
+ export interface RunCompletionFileNavigation {
61
+ readonly member_index: number;
62
+ readonly locations: Readonly<RunDirectoryNavigation>;
63
+ }
64
+
31
65
  export interface RunCompletionBatch {
32
66
  attempts?: number;
67
+ /** AX capture marker; numbered spellings/absence are retained codecs, never new-capture choices. */
68
+ readonly presentation_format?: "ax_records" | "ax_records_v1" | "ax_records_v2" | "ax_records_v3";
69
+ /** Retained singleton capture; absence preserves older indexed bytes. Never a new-capture selector. */
70
+ readonly single_result?: true;
71
+ /** Captured record-list shape; no public format/version choice or source authority. */
72
+ readonly result_layout?: "records" | "run_headers" | "headings" | "headings_flat_omissions" | "headings_outcomes" | "headings_shared_results" | "headings_shared_kinds" | "frontmatter_v1" | "frontmatter_v2" | "completion_yaml_core_v1";
73
+ /** Immutable scope/order/range data, not a caller-selected format or allocation authority. */
74
+ readonly numbering?: RunCompletionNumbering;
75
+ /** Formatting version/data only; no recipient ownership, read or publication capability. */
76
+ readonly navigation_handoff?: "current_session_request" | "captured_file_locations";
77
+ /** Formatting only; omission counts remain the already-admitted source facts. */
78
+ readonly excerpt_format?: "bracketed_omissions_v1";
79
+ readonly file_navigation?: readonly Readonly<RunCompletionFileNavigation>[];
80
+ readonly text_budget?: number;
81
+ readonly inventory_source?: Readonly<RunCompletionInventorySource>;
33
82
  batch_id: string;
34
83
  created_at: string;
35
84
  last_error?: string;
36
85
  last_failed_at?: string;
37
- members: RunCompletionBatchMember[];
86
+ members: readonly RunCompletionBatchMember[];
38
87
  phase: RunDeliveryPhase;
39
88
  presented_at?: string;
40
89
  queued_at?: string;
@@ -65,7 +114,32 @@ export interface RunDeliveryReceipt {
65
114
  presented_at: string;
66
115
  }
67
116
 
117
+ /** A requested fresh target only; requesting it does not activate a stream. */
118
+ export interface RunCompletionStreamIntent {
119
+ readonly pending_stream_id: string;
120
+ }
121
+
122
+ export type RunCompletionStreamCounter =
123
+ | Readonly<{ state: "available"; next_index: number }>
124
+ | Readonly<{ state: "unavailable" }>;
125
+
126
+ export interface RunCompletionStreamActive {
127
+ readonly stream_id: string;
128
+ readonly counter: RunCompletionStreamCounter;
129
+ }
130
+
131
+ export type RunCompletionStreamState =
132
+ | Readonly<RunCompletionStreamIntent & { active?: never }>
133
+ | Readonly<{ active: Readonly<RunCompletionStreamActive>; pending_stream_id?: string }>;
134
+
135
+ export type RunCompletionStreamStatus =
136
+ | Readonly<{ state: "unavailable"; reason: "stream_state_unavailable" }>
137
+ | Readonly<{ state: "pending"; session_id: string; pending_stream_id: string;
138
+ active?: Readonly<RunCompletionStreamActive>; blocking_batch_id?: string }>
139
+ | Readonly<{ state: "active"; session_id: string; active: Readonly<RunCompletionStreamActive>; blocking_batch_id?: string }>;
140
+
68
141
  export interface RunDeliveryJournal {
142
+ completion_stream?: RunCompletionStreamState;
69
143
  completion_batch?: RunCompletionBatch;
70
144
  owner_id: string;
71
145
  receipts: RunDeliveryReceipt[];
@@ -75,7 +149,8 @@ export interface RunDeliveryJournal {
75
149
 
76
150
  export interface AdmitRunCompletionBatchInput {
77
151
  batchId?: string;
78
- members: RunCompletionBatchMember[];
152
+ fileNavigation?: readonly Readonly<RunCompletionFileNavigation>[];
153
+ members: readonly RunCompletionBatchMember[];
79
154
  now?: Date;
80
155
  ownerId: string;
81
156
  tempDir: string;
@@ -111,12 +186,20 @@ export interface RunSteerTransitionInput {
111
186
  }
112
187
 
113
188
  const RUN_DELIVERY_SCHEMA = "run-delivery-v1";
189
+ 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;
114
190
  const MEMBER_FIELDS = new Set([
115
191
  "artifacts",
192
+ "artifact_inventory",
193
+ "artifact_inventory_unavailable",
194
+ "result_diagnostics",
195
+ "result_diagnostics_unavailable",
196
+ "diagnostic_excerpt",
116
197
  "parent_run",
117
198
  "parent_run_instance_id",
118
199
  "parent_state_dir",
119
200
  "output",
201
+ "primary_text",
202
+ "completion_text",
120
203
  "run",
121
204
  "run_instance_id",
122
205
  "state_dir",
@@ -126,6 +209,15 @@ const MEMBER_FIELDS = new Set([
126
209
  ]);
127
210
  const BATCH_FIELDS = new Set([
128
211
  "attempts",
212
+ "presentation_format",
213
+ "single_result",
214
+ "result_layout",
215
+ "numbering",
216
+ "excerpt_format",
217
+ "navigation_handoff",
218
+ "file_navigation",
219
+ "text_budget",
220
+ "inventory_source",
129
221
  "batch_id",
130
222
  "created_at",
131
223
  "last_error",
@@ -136,6 +228,7 @@ const BATCH_FIELDS = new Set([
136
228
  "queued_at",
137
229
  ]);
138
230
  const JOURNAL_FIELDS = new Set([
231
+ "completion_stream",
139
232
  "completion_batch",
140
233
  "owner_id",
141
234
  "receipts",
@@ -166,6 +259,59 @@ const STEER_FIELDS = new Set([
166
259
  "steer_id",
167
260
  ]);
168
261
 
262
+ /** Pure whole-capture arithmetic, not a reservation; commit with the packet under its owner lock. */
263
+ export function projectRunCompletionIndexRange(nextIndex: number, memberCount: number): RunCompletionIndexRange {
264
+ if (!Number.isSafeInteger(nextIndex) || nextIndex < 0) throw new Error("Completion stream counter is invalid");
265
+ if (!Number.isSafeInteger(memberCount) || memberCount < 1 || memberCount > Limits.RUN_DELIVERY_BATCH_MAX_MEMBERS) {
266
+ throw new Error("Completion stream member count is invalid");
267
+ }
268
+ // Subtract before adding so an unfitting range never produces rounded indices or a partial allocation.
269
+ if (memberCount > Number.MAX_SAFE_INTEGER - nextIndex) {
270
+ return Object.freeze({ state: "unavailable", reason: "range_unavailable", next_index: nextIndex });
271
+ }
272
+ return Object.freeze({ state: "available", next_index: nextIndex + memberCount,
273
+ indices: Object.freeze(Array.from({ length: memberCount }, (_, offset) => nextIndex + offset)) });
274
+ }
275
+
276
+ function parseCompletionNumbering(value: unknown, memberCount: number): RunCompletionNumbering {
277
+ const record = asRecord(value, "Completion numbering"), fields = Object.getOwnPropertyDescriptors(record);
278
+ const allowed = fields.kind?.value === "stream" ? ["kind", "session_id", "stream_id", "first_index", "member_order"]
279
+ : ["kind", "session_id", "stream_id", "reason", "member_order"];
280
+ if (Reflect.ownKeys(fields).some(key => typeof key !== "string" || !allowed.includes(key)) ||
281
+ Object.values(fields).some(field => !Object.hasOwn(field, "value") || !field.enumerable)) {
282
+ throw new Error("Completion numbering must contain only own data");
283
+ }
284
+ const kind = fields.kind?.value, session = fields.session_id?.value, stream = fields.stream_id?.value, order = fields.member_order?.value;
285
+ if ((kind !== "stream" && kind !== "unavailable") || typeof session !== "string" || !session.trim() || session.length > 4096 ||
286
+ !Array.isArray(order) || order.length !== memberCount || Reflect.ownKeys(order).length !== memberCount + 1) {
287
+ throw new Error("Completion numbering is invalid");
288
+ }
289
+ const memberOrder: number[] = [], selected = new Set<number>();
290
+ for (let slot = 0; slot < memberCount; slot++) {
291
+ const descriptor = Object.getOwnPropertyDescriptor(order, String(slot));
292
+ if (!descriptor || !Object.hasOwn(descriptor, "value") || !descriptor.enumerable || !Number.isSafeInteger(descriptor.value) ||
293
+ descriptor.value < 0 || descriptor.value >= memberCount || selected.has(descriptor.value)) {
294
+ throw new Error("Completion numbering member order is invalid");
295
+ }
296
+ selected.add(descriptor.value); memberOrder.push(descriptor.value);
297
+ }
298
+ if (kind === "stream") {
299
+ const first = fields.first_index?.value;
300
+ if (typeof stream !== "string" || !COMPLETION_STREAM_ID.test(stream) || !Number.isSafeInteger(first) || first < 0 ||
301
+ projectRunCompletionIndexRange(first, memberCount).state !== "available") {
302
+ throw new Error("Completion numbering range is invalid");
303
+ }
304
+ return Object.freeze({ kind, session_id: session, stream_id: stream, first_index: first + 0, member_order: Object.freeze(memberOrder) });
305
+ }
306
+ const reason = fields.reason?.value;
307
+ if ((reason !== "counter_unavailable" && reason !== "range_unavailable" && reason !== "stream_state_unavailable") ||
308
+ (reason === "stream_state_unavailable" ? fields.stream_id !== undefined : typeof stream !== "string" || !COMPLETION_STREAM_ID.test(stream))) {
309
+ throw new Error("Completion numbering unavailability is invalid");
310
+ }
311
+ return Object.freeze({ kind, session_id: session, ...(stream === undefined ? {} : { stream_id: stream }),
312
+ reason, member_order: Object.freeze(memberOrder) });
313
+ }
314
+
169
315
  function assertExactFields(
170
316
  value: Record<string, unknown>,
171
317
  fields: Set<string>,
@@ -248,8 +394,25 @@ function parseMember(value: unknown): RunCompletionBatchMember {
248
394
  const output = record.output === undefined
249
395
  ? undefined
250
396
  : boundedString(record.output, "Completion batch member output", 4_000);
251
- return {
252
- ...(parsedArtifacts ? { artifacts: parsedArtifacts } : {}),
397
+ const stateDir = boundedString(record.state_dir, "Completion batch member state_dir", 4_096);
398
+ const generation = boundedString(record.run_instance_id, "Completion batch member run_instance_id", 256);
399
+ const primary = record.primary_text === undefined ? undefined : decodeRunPrimaryTextBinding(stateDir, generation, record.primary_text);
400
+ const inventory = Object.hasOwn(record, "artifact_inventory") || Object.hasOwn(record, "artifact_inventory_unavailable")
401
+ ? decodeRunArtifactInventoryBinding(stateDir, generation, record) : undefined;
402
+ const diagnostics = Object.hasOwn(record, "result_diagnostics") || Object.hasOwn(record, "result_diagnostics_unavailable")
403
+ ? decodeRunResultDiagnosticsBinding(stateDir, generation, record) : undefined;
404
+ if (diagnostics && output) throw new Error("Completion result diagnostics cannot also carry a legacy display.");
405
+ if (record.diagnostic_excerpt !== undefined && !diagnostics?.result_diagnostics) throw new Error("Completion diagnostic excerpt requires its available source binding.");
406
+ const excerpt = record.diagnostic_excerpt === undefined ? undefined : decodeRunResultDiagnosticExcerpt(diagnostics!.result_diagnostics!, record.diagnostic_excerpt);
407
+ if (inventory && Object.keys(parsedArtifacts ?? {}).length) throw new Error("Completion artifact inventory cannot also carry legacy artifact paths.");
408
+ if (primary?.output && output) throw new Error("Completion primary text cannot also carry a legacy display.");
409
+ return Object.freeze({
410
+ ...(parsedArtifacts ? { artifacts: Object.freeze(parsedArtifacts) } : {}),
411
+ ...(primary ? { primary_text: primary } : {}),
412
+ ...(record.completion_text === undefined ? {} : { completion_text: decodeRunCompletionText(primary, record.completion_text) }),
413
+ ...inventory,
414
+ ...diagnostics,
415
+ ...(excerpt ? { diagnostic_excerpt: excerpt } : {}),
253
416
  ...(output ? { output } : {}),
254
417
  ...(parentRun
255
418
  ? {
@@ -259,21 +422,69 @@ function parseMember(value: unknown): RunCompletionBatchMember {
259
422
  }
260
423
  : {}),
261
424
  run: boundedString(record.run, "Completion batch member run", 120),
262
- run_instance_id: boundedString(
263
- record.run_instance_id,
264
- "Completion batch member run_instance_id",
265
- 256,
266
- ),
267
- state_dir: boundedString(record.state_dir, "Completion batch member state_dir", 4_096),
425
+ run_instance_id: generation,
426
+ state_dir: stateDir,
268
427
  status: completionStatus(record.status),
269
428
  summary: boundedString(record.summary, "Completion batch member summary", 1_000),
270
429
  terminal_at: timestamp(record.terminal_at, "Completion batch member terminal_at"),
271
- };
430
+ });
431
+ }
432
+
433
+ // Only already admitted source bindings cross this boundary; private locations and diagnostics do not.
434
+ function completionInventoryText(batch: Pick<RunCompletionBatch, "batch_id" | "created_at" | "members" | "numbering">): string {
435
+ return `${JSON.stringify({
436
+ schema: "run-completion-inventory-v1", batch_id: batch.batch_id, created_at: batch.created_at,
437
+ scope: "captured_completion_members", members_total: batch.members.length,
438
+ artifacts: "not_included", diagnostics: "not_included", private_locations: "not_included",
439
+ ...(batch.members.some(member => member.artifact_inventory !== undefined) ? { artifact_inventory: "captured_bindings_only" } : {}),
440
+ ...(batch.members.some(member => member.result_diagnostics !== undefined) ? { result_diagnostics: "captured_bindings_only" } : {}),
441
+ ...(batch.numbering ? { numbering: batch.numbering } : {}),
442
+ members: batch.members.map(member => ({
443
+ run: member.run, run_instance_id: member.run_instance_id, status: member.status, terminal_at: member.terminal_at,
444
+ ...(member.parent_run ? { parent_run: member.parent_run, parent_run_instance_id: member.parent_run_instance_id } : {}),
445
+ ...(member.primary_text ? { primary_text: member.primary_text } : {}),
446
+ ...(member.artifact_inventory === undefined ? {} : { artifact_inventory: member.artifact_inventory,
447
+ ...(member.artifact_inventory_unavailable === undefined ? {} : { artifact_inventory_unavailable: member.artifact_inventory_unavailable }) }),
448
+ ...(member.result_diagnostics === undefined ? {} : { result_diagnostics: member.result_diagnostics,
449
+ ...(member.result_diagnostics_unavailable === undefined ? {} : { result_diagnostics_unavailable: member.result_diagnostics_unavailable }) }),
450
+ })),
451
+ }, null, 2)}\n`;
452
+ }
453
+
454
+ function parseInventorySource(value: unknown, batch: RunCompletionBatch): Readonly<RunCompletionInventorySource> {
455
+ const record = asRecord(value, "Completion inventory source");
456
+ assertExactFields(record, new Set(["path", "sha256", "bytes_total", "members_total"]), "Completion inventory source");
457
+ const content = completionInventoryText(batch); const sha256 = createHash("sha256").update(content).digest("hex");
458
+ const path = boundedString(record.path, "Completion inventory source path", 4_096);
459
+ if (!isAbsolute(path) || normalize(path) !== path || basename(path) !== `inventory-${sha256}.json` ||
460
+ record.sha256 !== sha256 || record.bytes_total !== Buffer.byteLength(content) || record.members_total !== batch.members.length) {
461
+ throw new Error("Completion inventory source binding is invalid");
462
+ }
463
+ return Object.freeze({ path, sha256, bytes_total: record.bytes_total as number, members_total: batch.members.length });
464
+ }
465
+
466
+ function isFrontmatterCompletionLayout(layout: unknown): boolean {
467
+ return layout === "frontmatter_v1" || layout === "frontmatter_v2";
468
+ }
469
+
470
+ function isSharedCompletionLayout(layout: unknown): boolean {
471
+ return layout === "headings_shared_results" || layout === "headings_shared_kinds" || layout === "completion_yaml_core_v1" || isFrontmatterCompletionLayout(layout);
272
472
  }
273
473
 
274
474
  function parseBatch(value: unknown): RunCompletionBatch {
275
475
  const record = asRecord(value, "Completion batch");
276
476
  assertExactFields(record, BATCH_FIELDS, "Completion batch");
477
+ const presentationFormat = record.presentation_format;
478
+ if ((presentationFormat !== undefined && presentationFormat !== "ax_records" && presentationFormat !== "ax_records_v1" && presentationFormat !== "ax_records_v2" && presentationFormat !== "ax_records_v3") ||
479
+ (presentationFormat !== undefined && record.excerpt_format !== undefined)) {
480
+ throw new Error("Completion batch presentation format is invalid");
481
+ }
482
+ if (record.excerpt_format !== undefined && record.excerpt_format !== "bracketed_omissions_v1") {
483
+ throw new Error("Completion batch excerpt format is invalid");
484
+ }
485
+ if (record.navigation_handoff !== undefined && record.navigation_handoff !== "current_session_request" && record.navigation_handoff !== "captured_file_locations") {
486
+ throw new Error("Completion batch navigation handoff is invalid");
487
+ }
277
488
  if (!Array.isArray(record.members) || record.members.length === 0) {
278
489
  throw new Error("Completion batch members must be a non-empty array");
279
490
  }
@@ -281,6 +492,30 @@ function parseBatch(value: unknown): RunCompletionBatch {
281
492
  throw new Error("Completion batch exceeds the member limit");
282
493
  }
283
494
  const members = record.members.map(parseMember);
495
+ const singleDescriptor = Object.getOwnPropertyDescriptor(record, "single_result");
496
+ if (singleDescriptor && (!Object.hasOwn(singleDescriptor, "value") || (singleDescriptor.value !== undefined &&
497
+ (singleDescriptor.value !== true || presentationFormat !== "ax_records" || members.length !== 1)))) {
498
+ throw new Error("Completion singleton layout is invalid");
499
+ }
500
+ const singleResult = singleDescriptor?.value;
501
+ const layoutDescriptor = Object.getOwnPropertyDescriptor(record, "result_layout");
502
+ if (layoutDescriptor && (!Object.hasOwn(layoutDescriptor, "value") || (layoutDescriptor.value !== undefined &&
503
+ ((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)))) {
504
+ throw new Error("Completion result layout is invalid");
505
+ }
506
+ const resultLayout = layoutDescriptor?.value;
507
+ const budgetDescriptor = Object.getOwnPropertyDescriptor(record, "text_budget");
508
+ if (budgetDescriptor && (!Object.hasOwn(budgetDescriptor, "value") || !budgetDescriptor.enumerable ||
509
+ (budgetDescriptor.value !== undefined && (!isSharedCompletionLayout(resultLayout) ||
510
+ !Number.isSafeInteger(budgetDescriptor.value) || budgetDescriptor.value < 0 ||
511
+ budgetDescriptor.value > Math.floor(Limits.RUN_COMPLETION_MODEL_MAX_BYTES / Math.min(members.length, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)))))) {
512
+ throw new Error("Completion shared text budget is invalid");
513
+ }
514
+ const numberingDescriptor = Object.getOwnPropertyDescriptor(record, "numbering");
515
+ 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)))) {
516
+ throw new Error("Completion numbering requires a canonical captured heading layout");
517
+ }
518
+ const numbering = numberingDescriptor ? parseCompletionNumbering(numberingDescriptor.value, members.length) : undefined;
284
519
  const identities = new Set<string>();
285
520
  for (const member of members) {
286
521
  const identity = `${member.state_dir}\0${member.run_instance_id}`;
@@ -323,17 +558,48 @@ function parseBatch(value: unknown): RunCompletionBatch {
323
558
  if (phase === "presented" && (!queuedAt || !presentedAt)) {
324
559
  throw new Error("Presented completion batch requires queued_at and presented_at");
325
560
  }
326
- return {
561
+ const navigation = record.file_navigation;
562
+ if ((record.navigation_handoff === "captured_file_locations") !== (navigation !== undefined)) throw new Error("Completion file navigation version/data mismatch");
563
+ let fileNavigation: readonly Readonly<RunCompletionFileNavigation>[] | undefined;
564
+ if (navigation !== undefined) {
565
+ if (!Array.isArray(navigation) || navigation.length > members.length || Object.keys(navigation).length !== navigation.length) throw new Error("Completion file navigation is invalid");
566
+ const selected = new Set<number>();
567
+ fileNavigation = Object.freeze(Array.from({ length: navigation.length }, (_, slot) => {
568
+ const entry = Object.getOwnPropertyDescriptor(navigation, String(slot));
569
+ if (!entry || !Object.hasOwn(entry, "value")) throw new Error("Completion file navigation is invalid");
570
+ const row = asRecord(entry.value, "Completion file navigation");
571
+ assertExactFields(row, new Set(["member_index", "locations"]), "Completion file navigation");
572
+ const descriptors = Object.getOwnPropertyDescriptors(row);
573
+ if (!descriptors.member_index || !descriptors.locations || !Object.hasOwn(descriptors.member_index, "value") || !Object.hasOwn(descriptors.locations, "value")) throw new Error("Completion file navigation is invalid");
574
+ const index = descriptors.member_index.value;
575
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0 || index >= members.length || selected.has(index)) throw new Error("Completion file navigation member is invalid");
576
+ selected.add(index);
577
+ const locations = decodeRunDirectoryNavigation(descriptors.locations.value), member = members[index];
578
+ if (locations.state === "available" && (locations.run !== member.run || locations.run_instance_id !== member.run_instance_id || locations.directory !== member.state_dir)) throw new Error("Completion file navigation identity mismatch");
579
+ return Object.freeze({ member_index: index, locations });
580
+ }));
581
+ if (Buffer.byteLength(JSON.stringify(fileNavigation), "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) throw new Error("Completion file navigation exceeds byte limit");
582
+ }
583
+ const batch: RunCompletionBatch = {
584
+ ...(presentationFormat === undefined ? {} : { presentation_format: presentationFormat }),
585
+ ...(singleResult === true ? { single_result: true as const } : {}),
586
+ ...(resultLayout === undefined ? {} : { result_layout: resultLayout }),
587
+ ...(budgetDescriptor?.value === undefined ? {} : { text_budget: budgetDescriptor.value as number }),
588
+ ...(numbering ? { numbering } : {}),
327
589
  ...(attempts !== undefined ? { attempts } : {}),
590
+ ...(record.navigation_handoff === undefined ? {} : { navigation_handoff: record.navigation_handoff }),
591
+ ...(record.excerpt_format === undefined ? {} : { excerpt_format: record.excerpt_format }),
592
+ ...(fileNavigation ? { file_navigation: fileNavigation } : {}),
328
593
  batch_id: boundedString(record.batch_id, "Completion batch id", 128),
329
594
  created_at: timestamp(record.created_at, "Completion batch created_at"),
330
595
  ...(lastError ? { last_error: lastError } : {}),
331
596
  ...(lastFailedAt ? { last_failed_at: lastFailedAt } : {}),
332
- members,
597
+ members: Object.freeze(members),
333
598
  phase,
334
599
  ...(queuedAt ? { queued_at: queuedAt } : {}),
335
600
  ...(presentedAt ? { presented_at: presentedAt } : {}),
336
601
  };
602
+ return record.inventory_source === undefined ? batch : { ...batch, inventory_source: parseInventorySource(record.inventory_source, batch) };
337
603
  }
338
604
 
339
605
  function parseSteer(value: unknown): RunSteerEnvelope {
@@ -424,6 +690,58 @@ function parseReceipt(value: unknown): RunDeliveryReceipt {
424
690
  };
425
691
  }
426
692
 
693
+ function completionStreamFields(value: unknown, allowed: readonly string[]): PropertyDescriptorMap {
694
+ const fields = Object.getOwnPropertyDescriptors(asRecord(value, "Completion stream intent"));
695
+ if (Reflect.ownKeys(fields).some(key => typeof key !== "string" || !allowed.includes(key)) ||
696
+ Object.values(fields).some(field => !Object.hasOwn(field, "value") || !field.enumerable)) {
697
+ throw new Error("Completion stream intent is invalid");
698
+ }
699
+ return fields;
700
+ }
701
+
702
+ function parseCompletionStreamState(value: unknown): RunCompletionStreamState {
703
+ const fields = completionStreamFields(value, ["active", "pending_stream_id"]);
704
+ const pending = fields.pending_stream_id?.value;
705
+ if ((!fields.active && !fields.pending_stream_id) || (fields.pending_stream_id &&
706
+ (typeof pending !== "string" || !COMPLETION_STREAM_ID.test(pending)))) {
707
+ throw new Error("Completion stream intent is invalid");
708
+ }
709
+ if (!fields.active) return Object.freeze({ pending_stream_id: pending });
710
+ const active = completionStreamFields(fields.active.value, ["stream_id", "counter"]);
711
+ const id = active.stream_id?.value;
712
+ if (typeof id !== "string" || !COMPLETION_STREAM_ID.test(id) || id === pending) {
713
+ throw new Error("Completion stream active identity is invalid");
714
+ }
715
+ const counter = completionStreamFields(active.counter?.value, ["state", "next_index"]);
716
+ let retainedCounter: RunCompletionStreamCounter;
717
+ if (counter.state?.value === "available") {
718
+ const next = counter.next_index?.value;
719
+ // A newly activated stream already captured at least one member in its first commit.
720
+ if (!Number.isSafeInteger(next) || next < 1) throw new Error("Completion stream active counter is invalid");
721
+ retainedCounter = Object.freeze({ state: "available", next_index: next });
722
+ } else if (counter.state?.value === "unavailable" && !counter.next_index) {
723
+ retainedCounter = Object.freeze({ state: "unavailable" });
724
+ } else throw new Error("Completion stream active counter is invalid");
725
+ return Object.freeze({ active: Object.freeze({ stream_id: id, counter: retainedCounter }),
726
+ ...(fields.pending_stream_id ? { pending_stream_id: pending } : {}) });
727
+ }
728
+
729
+ function assertCompletionStreamBatch(stream: RunCompletionStreamState | undefined, batch: RunCompletionBatch | undefined, ownerId: string): void {
730
+ if (!batch) return;
731
+ const numbering = batch.numbering, active = stream?.active;
732
+ if (!numbering) {
733
+ if (active) throw new Error("Active completion stream cannot contain a packet-local batch");
734
+ return;
735
+ }
736
+ if (!active || numbering.session_id !== ownerId || numbering.stream_id !== active.stream_id) {
737
+ throw new Error("Completion numbering has no matching active session stream");
738
+ }
739
+ if (numbering.kind === "stream" && active.counter.state === "available" &&
740
+ active.counter.next_index < numbering.first_index + batch.members.length) {
741
+ throw new Error("Completion stream counter is behind its captured packet");
742
+ }
743
+ }
744
+
427
745
  function emptyJournal(ownerId: string): RunDeliveryJournal {
428
746
  return {
429
747
  owner_id: ownerId,
@@ -433,7 +751,7 @@ function emptyJournal(ownerId: string): RunDeliveryJournal {
433
751
  };
434
752
  }
435
753
 
436
- function parseJournal(value: unknown, ownerId: string): RunDeliveryJournal {
754
+ function parseJournal(value: unknown, ownerId: string, path: string): RunDeliveryJournal {
437
755
  const record = asRecord(value, "Run delivery journal");
438
756
  assertExactFields(record, JOURNAL_FIELDS, "Run delivery journal");
439
757
  if (record.schema !== RUN_DELIVERY_SCHEMA) {
@@ -459,10 +777,20 @@ function parseJournal(value: unknown, ownerId: string): RunDeliveryJournal {
459
777
  if (new Set(steers.map((steer) => steer.steer_id)).size !== steers.length) {
460
778
  throw new Error("Run delivery journal has duplicate urgent steer ids");
461
779
  }
780
+ const streamDescriptor = Object.getOwnPropertyDescriptor(record, "completion_stream");
781
+ if (streamDescriptor && (!Object.hasOwn(streamDescriptor, "value") || !streamDescriptor.enumerable)) {
782
+ throw new Error("Completion stream intent must be stored as own data");
783
+ }
784
+ const stream = streamDescriptor ? parseCompletionStreamState(streamDescriptor.value) : undefined;
785
+ const batch = record.completion_batch === undefined ? undefined : parseBatch(record.completion_batch);
786
+ if (batch && isSharedCompletionLayout(batch.result_layout) && batch.text_budget === undefined) throw new Error("Captured completion shared text budget is unavailable");
787
+ assertCompletionStreamBatch(stream, batch, ownerId);
788
+ if (batch?.inventory_source && batch.inventory_source.path !== resolve(dirname(path), `inventory-${batch.inventory_source.sha256}.json`)) {
789
+ throw new Error("Completion inventory source belongs to another delivery location");
790
+ }
462
791
  return {
463
- ...(record.completion_batch !== undefined
464
- ? { completion_batch: parseBatch(record.completion_batch) }
465
- : {}),
792
+ ...(stream ? { completion_stream: stream } : {}),
793
+ ...(batch ? { completion_batch: batch } : {}),
466
794
  owner_id: ownerId,
467
795
  receipts,
468
796
  schema: RUN_DELIVERY_SCHEMA,
@@ -487,8 +815,11 @@ function assertNoDeliverySymlinks(tempDir: string, path: string): void {
487
815
  }
488
816
  }
489
817
 
490
- function readJournalAtPath(path: string, ownerId: string): RunDeliveryJournal {
491
- if (!existsSync(path)) return emptyJournal(ownerId);
818
+ function readJournalAtPath(path: string, ownerId: string, requireExisting = false): RunDeliveryJournal {
819
+ if (!existsSync(path)) {
820
+ if (requireExisting) throw new Error("Completion stream request requires an existing delivery journal");
821
+ return emptyJournal(ownerId);
822
+ }
492
823
  const stat = statSync(path);
493
824
  if (!stat.isFile() || stat.size > Limits.RUN_DELIVERY_JOURNAL_MAX_BYTES) {
494
825
  throw new Error("Run delivery journal is not a bounded regular file");
@@ -499,7 +830,7 @@ function readJournalAtPath(path: string, ownerId: string): RunDeliveryJournal {
499
830
  } catch (error) {
500
831
  throw new Error("Run delivery journal is malformed", { cause: error });
501
832
  }
502
- return parseJournal(parsed, ownerId);
833
+ return parseJournal(parsed, ownerId, path);
503
834
  }
504
835
 
505
836
  export function readRunDeliveryJournal(
@@ -511,49 +842,137 @@ export function readRunDeliveryJournal(
511
842
  return readJournalAtPath(path, ownerId);
512
843
  }
513
844
 
514
- function persistJournal(path: string, journal: RunDeliveryJournal): void {
845
+ function journalText(journal: RunDeliveryJournal): string {
515
846
  const content = `${JSON.stringify(journal, null, 2)}\n`;
516
847
  if (Buffer.byteLength(content, "utf8") > Limits.RUN_DELIVERY_JOURNAL_MAX_BYTES) {
517
848
  throw new Error("Run delivery journal exceeds the byte limit");
518
849
  }
519
- writeTextAtomic(path, content);
850
+ return content;
520
851
  }
521
852
 
522
853
  function mutateJournal<T>(
523
854
  tempDir: string,
524
855
  ownerId: string,
525
856
  mutate: (journal: RunDeliveryJournal) => T,
857
+ requireExisting = false,
526
858
  ): T {
527
859
  const path = getRunDeliveryJournalPath(tempDir, ownerId);
528
860
  return withFileMutationLock(path, () => {
529
861
  assertNoDeliverySymlinks(tempDir, path);
530
- const journal = readJournalAtPath(path, ownerId);
862
+ const journal = readJournalAtPath(path, ownerId, requireExisting);
531
863
  const result = mutate(journal);
532
- persistJournal(path, parseJournal(journal, ownerId));
864
+ writeTextAtomic(path, journalText(parseJournal(journal, ownerId, path)));
533
865
  return result;
534
866
  });
535
867
  }
536
868
 
537
- export function admitRunCompletionBatch(
538
- input: AdmitRunCompletionBatchInput,
539
- ): RunCompletionBatch {
869
+ /** Read-only persisted facts, not proof of non-connection or current SDK ownership. */
870
+ export function readRunCompletionStreamStatus(tempDir: string, ownerId: string): RunCompletionStreamStatus {
871
+ const journal = readRunDeliveryJournal(tempDir, ownerId), stream = journal.completion_stream;
872
+ if (!stream) return Object.freeze({ state: "unavailable", reason: "stream_state_unavailable" });
873
+ const blocking = journal.completion_batch ? { blocking_batch_id: journal.completion_batch.batch_id } : {};
874
+ if (stream.pending_stream_id) return Object.freeze({ state: "pending", session_id: journal.owner_id,
875
+ pending_stream_id: stream.pending_stream_id, ...(stream.active ? { active: stream.active } : {}), ...blocking });
876
+ return Object.freeze({ state: "active", session_id: journal.owner_id, active: stream.active!, ...blocking });
877
+ }
878
+
879
+ /** Kernel-only explicit request; fresh-session bootstrap and public command admission remain separate. */
880
+ export function requestRunCompletionStream(input: { tempDir: string; ownerId: string }): Readonly<RunCompletionStreamIntent> {
881
+ const path = getRunDeliveryJournalPath(input.tempDir, input.ownerId);
882
+ return withFileMutationLock(path, () => {
883
+ assertNoDeliverySymlinks(input.tempDir, path);
884
+ const journal = readJournalAtPath(path, input.ownerId, true);
885
+ if (journal.completion_stream?.pending_stream_id) return Object.freeze({ pending_stream_id: journal.completion_stream.pending_stream_id });
886
+ const stream = Object.freeze({ pending_stream_id: randomUUID() });
887
+ journal.completion_stream = parseCompletionStreamState({ ...journal.completion_stream, ...stream });
888
+ writeTextAtomic(path, journalText(parseJournal(journal, input.ownerId, path)));
889
+ return stream;
890
+ });
891
+ }
892
+
893
+ export function admitRunCompletionBatch(input: AdmitRunCompletionBatchInput): RunCompletionBatch {
894
+ return captureCompletionBatch(input, false, "completion_yaml_core_v1");
895
+ }
896
+
897
+ /** Kernel-only opt-in; ordinary Pi capture stays fenced until session/lifecycle integration is admitted. */
898
+ export function captureRunCompletionStreamBatch(input: AdmitRunCompletionBatchInput): RunCompletionBatch {
899
+ return captureCompletionBatch(input, true);
900
+ }
901
+
902
+ /** Private ordinary candidate composition; stream capture retains its separately gated codec. */
903
+ export function captureRunCompletionYamlCandidateBatch(input: AdmitRunCompletionBatchInput): RunCompletionBatch {
904
+ return captureCompletionBatch(input, false, "completion_yaml_core_v1");
905
+ }
906
+
907
+ function captureCompletionBatch(input: AdmitRunCompletionBatchInput, numbered: boolean, layout: "frontmatter_v2" | "completion_yaml_core_v1" = "frontmatter_v2"): RunCompletionBatch {
540
908
  const now = (input.now ?? new Date()).toISOString();
541
- const batch = parseBatch({
909
+ if (Object.hasOwn(input, "presentationFormat")) throw new Error("Completion presentation selection is not supported; new batches use AX.");
910
+ if (Object.hasOwn(input, "numbering")) throw new Error("Completion numbering is assigned only by journal capture");
911
+ const captured = parseBatch({
912
+ presentation_format: "ax_records",
913
+ navigation_handoff: input.fileNavigation === undefined ? "current_session_request" : "captured_file_locations",
914
+ ...(input.fileNavigation === undefined ? {} : { file_navigation: input.fileNavigation }),
542
915
  batch_id: input.batchId ?? randomUUID(),
543
916
  created_at: now,
544
917
  members: input.members,
545
918
  phase: "pending",
546
919
  });
920
+ const draft: RunCompletionBatch = layout === "completion_yaml_core_v1"
921
+ ? parseBatch({ ...captured, result_layout: layout }) : { ...captured, result_layout: layout };
547
922
  return mutateJournal(input.tempDir, input.ownerId, (journal) => {
923
+ let batch = draft;
548
924
  if (journal.completion_batch) {
549
925
  throw new Error("Run delivery journal already has an active completion batch");
550
926
  }
551
927
  if (journal.receipts.some((receipt) => receipt.delivery_id === batch.batch_id)) {
552
928
  throw new Error("Run delivery batch id already has a presented receipt");
553
929
  }
554
- journal.completion_batch = batch;
555
- return batch;
556
- });
930
+ if (numbered) {
931
+ const stream = journal.completion_stream;
932
+ if (!stream) throw new Error("Completion stream capture requires retained stream state");
933
+ const target = stream.pending_stream_id, active = stream.active;
934
+ const streamId = target ?? active!.stream_id;
935
+ const counter: RunCompletionStreamCounter = target ? { state: "available", next_index: 0 } : active!.counter;
936
+ const range = counter.state === "available" ? projectRunCompletionIndexRange(counter.next_index, batch.members.length) : undefined;
937
+ const slots = new Map(batch.members.map((member, slot) => [memberIdentity(member), slot]));
938
+ const member_order = orderRunCompletionMembers(batch.members).map(member => slots.get(memberIdentity(member))!);
939
+ const numbering: RunCompletionNumbering = range?.state === "available"
940
+ ? { kind: "stream", session_id: input.ownerId, stream_id: streamId, first_index: range.indices[0]!, member_order }
941
+ : { kind: "unavailable", session_id: input.ownerId, stream_id: streamId,
942
+ reason: range ? "range_unavailable" : "counter_unavailable", member_order };
943
+ batch = parseBatch({ ...batch, numbering });
944
+ journal.completion_stream = parseCompletionStreamState({ active: { stream_id: streamId,
945
+ counter: range?.state === "available" ? { state: "available", next_index: range.next_index } : counter } });
946
+ } else if (journal.completion_stream) {
947
+ throw new Error("Completion stream capture requires the numbered kernel path; stream state is retained");
948
+ }
949
+ if (layout !== "completion_yaml_core_v1") batch = retainSharedCompletionTexts(batch);
950
+ if (projectCompletionBatchMessage(batch).membersShown === batch.members.length) {
951
+ if (layout === "completion_yaml_core_v1") batch = retainSharedCompletionTexts(batch);
952
+ journal.completion_batch = batch;
953
+ return batch;
954
+ }
955
+ const content = completionInventoryText(batch);
956
+ const sha256 = createHash("sha256").update(content).digest("hex");
957
+ const journalPath = getRunDeliveryJournalPath(input.tempDir, input.ownerId);
958
+ const path = resolve(dirname(journalPath), `inventory-${sha256}.json`);
959
+ const retained = retainSharedCompletionTexts(parseBatch({ ...batch, inventory_source: { path, sha256, bytes_total: Buffer.byteLength(content), members_total: batch.members.length } }));
960
+ journal.completion_batch = retained;
961
+ // Refuse known journal overflow before a separate source effect. Never overwrite/reuse an orphan.
962
+ journalText(parseJournal(journal, input.ownerId, journalPath));
963
+ assertNoDeliverySymlinks(input.tempDir, path);
964
+ mkdirSync(dirname(path), { recursive: true });
965
+ assertNoDeliverySymlinks(input.tempDir, path);
966
+ const directory = lstatSync(dirname(path));
967
+ if (!directory.isDirectory()) throw new Error("Completion inventory directory is invalid");
968
+ writeFileSync(path, content, { encoding: "utf8", flag: "wx", mode: 0o600 });
969
+ assertNoDeliverySymlinks(input.tempDir, path);
970
+ const current = lstatSync(dirname(path)); const source = lstatSync(path);
971
+ if (current.dev !== directory.dev || current.ino !== directory.ino || !source.isFile() || source.size !== Buffer.byteLength(content)) {
972
+ throw new Error("Completion inventory source binding changed during retention");
973
+ }
974
+ return retained;
975
+ }, numbered);
557
976
  }
558
977
 
559
978
  export function markRunCompletionBatchQueued(
@@ -803,8 +1222,10 @@ function completionDepth(
803
1222
  return depth;
804
1223
  }
805
1224
 
806
- function orderCompletionForest(
807
- members: RunCompletionBatchMember[],
1225
+ /** Captured membership ordering only; never native lineage or a complete descendant tree. */
1226
+ export function orderRunCompletionMembers(
1227
+ members: readonly RunCompletionBatchMember[],
1228
+ prioritizeFailed = false,
808
1229
  ): RunCompletionBatchMember[] {
809
1230
  const byIdentity = new Map(members.map((member) => [memberIdentity(member), member]));
810
1231
  const children = new Map<string, RunCompletionBatchMember[]>();
@@ -813,7 +1234,7 @@ function orderCompletionForest(
813
1234
  const parentIdentity = member.parent_state_dir && member.parent_run_instance_id
814
1235
  ? `${member.parent_state_dir}\0${member.parent_run_instance_id}`
815
1236
  : undefined;
816
- if (!parentIdentity || !byIdentity.has(parentIdentity)) {
1237
+ if (!parentIdentity || !byIdentity.has(parentIdentity) || (prioritizeFailed && byIdentity.get(parentIdentity)!.run !== member.parent_run)) {
817
1238
  roots.push(member);
818
1239
  continue;
819
1240
  }
@@ -821,6 +1242,17 @@ function orderCompletionForest(
821
1242
  siblings.push(member);
822
1243
  children.set(parentIdentity, siblings);
823
1244
  }
1245
+ const failed = new Set(members.filter(member => member.status === "failed").map(memberIdentity));
1246
+ if (prioritizeFailed) for (const member of members) {
1247
+ let current = member; const seen = new Set<string>();
1248
+ while (failed.has(memberIdentity(current)) && current.parent_state_dir && current.parent_run_instance_id) {
1249
+ const key = `${current.parent_state_dir}\0${current.parent_run_instance_id}`, parent = byIdentity.get(key);
1250
+ if (!parent || parent.run !== current.parent_run || seen.has(key)) break;
1251
+ seen.add(key); failed.add(key); current = parent;
1252
+ }
1253
+ }
1254
+ const peers = (items: RunCompletionBatchMember[]) => prioritizeFailed
1255
+ ? [...items.filter(member => failed.has(memberIdentity(member))), ...items.filter(member => !failed.has(memberIdentity(member)))] : items;
824
1256
  const ordered: RunCompletionBatchMember[] = [];
825
1257
  const visited = new Set<string>();
826
1258
  const visit = (member: RunCompletionBatchMember): void => {
@@ -828,7 +1260,7 @@ function orderCompletionForest(
828
1260
  if (visited.has(identity)) return;
829
1261
  visited.add(identity);
830
1262
  ordered.push(member);
831
- for (const child of children.get(identity) ?? []) visit(child);
1263
+ for (const child of peers(children.get(identity) ?? [])) visit(child);
832
1264
  };
833
1265
  for (const root of roots) visit(root);
834
1266
  for (const member of members) visit(member);
@@ -845,9 +1277,41 @@ function formatModelOutput(value: string, indent: string): string {
845
1277
  .join("\n");
846
1278
  }
847
1279
 
1280
+ function formatPrimaryText(binding: Readonly<RunPrimaryTextBinding>, indent: string, bracketed: boolean): string {
1281
+ if (!binding.output) return `\n${indent} Primary output unavailable: ${binding.output_unavailable}.`;
1282
+ const output = binding.output;
1283
+ 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" : ""}.` +
1284
+ `\n${indent} UTF-8 bytes: [${output.shown_bytes.start}, ${output.shown_bytes.end}) / ${output.bytes_total}.` +
1285
+ `\n${formatLiteralBlock(output.preview)}\n${indent} Retained source at capture:\n${formatLiteralBlock(JSON.stringify(output.source, null, 2))}`;
1286
+ }
1287
+
1288
+ function formatArtifactInventory(member: RunCompletionBatchMember, indent: string): string {
1289
+ if (member.artifact_inventory === undefined) return "";
1290
+ if (member.artifact_inventory === null) return member.artifact_inventory_unavailable === "not_declared" ? ""
1291
+ : `\n${indent} Artifact inventory unavailable: ${member.artifact_inventory_unavailable}; no legacy path fallback.`;
1292
+ const inventory = member.artifact_inventory;
1293
+ return `\n${indent} Artifact declarations: ${inventory.declarations_total}; metadata only; target availability/production not asserted; bodies not read.` +
1294
+ `\n${indent} Retained declaration inventory at capture:\n${formatLiteralBlock(JSON.stringify({ ...inventory.source,
1295
+ bytes_total: inventory.bytes_total, declarations_total: inventory.declarations_total }, null, 2))}`;
1296
+ }
1297
+
1298
+ function formatResultDiagnostics(member: RunCompletionBatchMember, indent: string, bracketed: boolean): string {
1299
+ if (member.result_diagnostics === undefined) return "";
1300
+ if (member.result_diagnostics === null) return member.result_diagnostics_unavailable === "not_recorded" ? ""
1301
+ : `\n${indent} Diagnostic metadata unavailable: ${member.result_diagnostics_unavailable}; no legacy body fallback.`;
1302
+ const reference = member.result_diagnostics; const excerpt = member.diagnostic_excerpt;
1303
+ const others = reference.fields.filter(field => field !== excerpt?.field);
1304
+ return `\n${indent} Diagnostic metadata only; not primary output; admission privacy for recorded fields.` +
1305
+ (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"}.` +
1306
+ `\n${indent} Field UTF-8 bytes: [${excerpt.shown_bytes.start}, ${excerpt.shown_bytes.end}) / ${excerpt.bytes_total}.\n${formatLiteralBlock(excerpt.text)}` : "") +
1307
+ (others.length ? `\n${indent} Recorded fields not shown: ${others.join(", ")}.` : "") +
1308
+ `\n${indent} Retained diagnostic source at capture:\n${formatLiteralBlock(JSON.stringify({ ...reference.source, bytes_total: reference.bytes_total, fields: reference.fields }, null, 2))}`;
1309
+ }
1310
+
848
1311
  function formatCompletionMember(
849
1312
  member: RunCompletionBatchMember,
850
1313
  byIdentity: Map<string, RunCompletionBatchMember>,
1314
+ bracketedExcerpts: boolean,
851
1315
  ): string {
852
1316
  const summary = compactModelText(member.summary, 200);
853
1317
  const artifactEntries = Object.entries(member.artifacts ?? {}).slice(0, 4);
@@ -858,12 +1322,13 @@ function formatCompletionMember(
858
1322
  ).join(", ")}`;
859
1323
  const indent = " ".repeat(Math.min(completionDepth(member, byIdentity), 8));
860
1324
  const output = member.output
861
- ? `\n${indent} Output:\n${formatModelOutput(member.output, indent)}`
1325
+ ? `\n${indent} ${member.primary_text ? "Legacy diagnostic display (not a complete primary source)" : "Output"}:\n${formatModelOutput(member.output, indent)}`
862
1326
  : "";
863
- return `${indent}- \`${compactModelText(member.run, 120)}\` — \`${member.status}\`: ${summary}${artifactText}${output}`;
1327
+ const primary = member.primary_text ? formatPrimaryText(member.primary_text, indent, bracketedExcerpts) : "";
1328
+ return `${indent}- \`${compactModelText(member.run, 120)}\` — \`${member.status}\`: ${summary}${artifactText}${primary}${formatArtifactInventory(member, indent)}${formatResultDiagnostics(member, indent, bracketedExcerpts)}${output}`;
864
1329
  }
865
1330
 
866
- function formatStatusCounts(members: RunCompletionBatchMember[]): string {
1331
+ function formatStatusCounts(members: readonly RunCompletionBatchMember[]): string {
867
1332
  const counts = new Map<RunCompletionDeliveryStatus, number>();
868
1333
  for (const member of members) {
869
1334
  counts.set(member.status, (counts.get(member.status) ?? 0) + 1);
@@ -874,23 +1339,1030 @@ function formatStatusCounts(members: RunCompletionBatchMember[]): string {
874
1339
  .join(" ");
875
1340
  }
876
1341
 
877
- /** Format one immutable batch for a model-bound custom message. */
878
- export function formatRunCompletionBatchMessage(batch: RunCompletionBatch): string {
1342
+ function completionOmissionField(chars: number, direct: boolean): [string, string] {
1343
+ return direct ? ["omitted", `${chars} chars`] : ["omission", `[omitted: ${chars} chars]`];
1344
+ }
1345
+
1346
+ function axCompletionExcerpt(preview: string, coverage: Pick<RunResultDiagnosticPacketExcerpt,
1347
+ "chars_shown" | "chars_total" | "chars_omitted" | "bytes_total" | "shown_bytes" | "truncated">, directOmission = false): string[] {
1348
+ return [
1349
+ formatMarkdownField("state", coverage.truncated ? "clipped" : "complete retained text"),
1350
+ formatMarkdownField("chars_shown", String(coverage.chars_shown)), formatMarkdownField("chars_total", String(coverage.chars_total)),
1351
+ formatMarkdownField("utf8_range", `[${coverage.shown_bytes.start}, ${coverage.shown_bytes.end}) / ${coverage.bytes_total}`),
1352
+ formatMarkdownField("preview", (coverage.shown_bytes.start > 0 ? "…\n" : "") + formatLiteralBlock(preview) +
1353
+ (coverage.shown_bytes.end < coverage.bytes_total ? "\n…" : "")),
1354
+ ...(coverage.chars_omitted > 0 ? [formatMarkdownField(...completionOmissionField(coverage.chars_omitted, directOmission))] : []),
1355
+ ];
1356
+ }
1357
+
1358
+ function formatRetainedCompletionMember(member: RunCompletionBatchMember, byIdentity: Map<string, RunCompletionBatchMember>, batchLegacyBasis = false): string {
1359
+ const run = member.run.startsWith("run:") ? member.run.slice(4) : member.run;
1360
+ // Keep the existing 200 UTF-16 display allowance, without normalizing opaque Markdown or leaking legacy tail lengths.
1361
+ let summary = member.summary;
1362
+ if (summary.length > 200) {
1363
+ let end = 199;
1364
+ if (/[\uD800-\uDBFF]/u.test(summary[end - 1]) && /[\uDC00-\uDFFF]/u.test(summary[end])) end--;
1365
+ summary = summary.slice(0, end) + "…";
1366
+ }
1367
+ const fields = [formatMarkdownField("run", run), formatMarkdownField("state", member.status), formatMarkdownField("summary", summary),
1368
+ ...(member.summary.length > 200 ? [formatMarkdownField("summary_omission", "[omitted: unknown]")] : []),
1369
+ ...(batchLegacyBasis ? [] : [formatMarkdownField("legacy_basis", "Recorded summary/output; original completeness/privacy unproven."),
1370
+ formatMarkdownField("legacy_upstream", "[omitted: unknown]")])];
1371
+ if (member.parent_state_dir && member.parent_run_instance_id) {
1372
+ const parent = byIdentity.get(`${member.parent_state_dir}\0${member.parent_run_instance_id}`);
1373
+ if (parent && parent.run === member.parent_run) fields.push(formatMarkdownField("parent_run", parent.run.startsWith("run:") ? parent.run.slice(4) : parent.run));
1374
+ }
1375
+ const primary = member.primary_text;
1376
+ if (primary) fields.push(formatMarkdownField("primary", primary.output ? [
1377
+ formatMarkdownField("scope", "Exported primary text at capture; admission privacy, not complete Actor output."),
1378
+ ...axCompletionExcerpt(primary.output.preview, primary.output),
1379
+ ...(primary.output.redacted ? [formatMarkdownField("redacted", "true")] : []),
1380
+ formatMarkdownField("source", formatLiteralBlock(JSON.stringify(primary.output.source, null, 2))),
1381
+ ].join("\n") : [formatMarkdownField("state", "unavailable"), formatMarkdownField("reason", primary.output_unavailable)].join("\n")));
1382
+ if (member.artifact_inventory) {
1383
+ const inventory = member.artifact_inventory;
1384
+ fields.push(formatMarkdownField("artifact_inventory", [formatMarkdownField("declarations", String(inventory.declarations_total)),
1385
+ formatMarkdownField("scope", "Metadata only; target availability/production not asserted; bodies not read."),
1386
+ formatMarkdownField("source", formatLiteralBlock(JSON.stringify({ ...inventory.source, bytes_total: inventory.bytes_total,
1387
+ declarations_total: inventory.declarations_total }, null, 2)))].join("\n")));
1388
+ } else if (member.artifact_inventory === null && member.artifact_inventory_unavailable !== "not_declared") {
1389
+ fields.push(formatMarkdownField("artifact_inventory", [formatMarkdownField("state", "unavailable"),
1390
+ formatMarkdownField("reason", member.artifact_inventory_unavailable!), formatMarkdownField("warning", "no legacy path fallback")].join("\n")));
1391
+ }
1392
+ const artifacts = Object.entries(member.artifacts ?? {});
1393
+ if (artifacts.length) fields.push(formatMarkdownField("artifacts", [formatMarkdownField("scope", "Declared paths; existence/production unproven."),
1394
+ ...artifacts.map(([name, path]) => formatMarkdownField("artifact",
1395
+ [formatMarkdownField("name", name), formatMarkdownField("path", formatLiteralBlock(JSON.stringify(path)))].join("\n")))].join("\n")));
1396
+ if (member.result_diagnostics) {
1397
+ const reference = member.result_diagnostics, excerpt = member.diagnostic_excerpt;
1398
+ const others = reference.fields.filter(field => field !== excerpt?.field);
1399
+ fields.push(formatMarkdownField("diagnostic_metadata", [
1400
+ formatMarkdownField("scope", "Diagnostic metadata only; not primary output; admission privacy for recorded fields."),
1401
+ ...(excerpt ? [formatMarkdownField("field", excerpt.field), ...axCompletionExcerpt(excerpt.text, excerpt)] : []),
1402
+ ...(others.length ? [formatMarkdownField("recorded_fields_not_shown", others.join(", "))] : []),
1403
+ formatMarkdownField("source", formatLiteralBlock(JSON.stringify({ ...reference.source, bytes_total: reference.bytes_total, fields: reference.fields }, null, 2))),
1404
+ ].join("\n")));
1405
+ } else if (member.result_diagnostics === null && member.result_diagnostics_unavailable !== "not_recorded") {
1406
+ fields.push(formatMarkdownField("diagnostic_metadata", [formatMarkdownField("state", "unavailable"),
1407
+ formatMarkdownField("reason", member.result_diagnostics_unavailable!), formatMarkdownField("warning", "no legacy body fallback")].join("\n")));
1408
+ }
1409
+ if (member.output) {
1410
+ if (primary) fields.push(formatMarkdownField("output_scope", "Legacy diagnostic display (not a complete primary source)"));
1411
+ fields.push(formatMarkdownField("output", member.output));
1412
+ }
1413
+ return fields.join("\n");
1414
+ }
1415
+
1416
+ export interface RunCompletionPresentation {
1417
+ readonly format: "ax_records" | "ax_records_v3" | "completion_frontmatter_v1";
1418
+ readonly blocks?: readonly CompletionBlock[];
1419
+ readonly content_sha256: string;
1420
+ /** UTF-16 offsets in the exact content, not UTF-8 byte or source excerpt ranges. */
1421
+ readonly compact_ranges: readonly (readonly [number, number])[];
1422
+ }
1423
+
1424
+ function completionRecordHeader(content: string): { format: string; single: boolean; length: number } | undefined {
1425
+ const single = /^- completion: [^\r\n\u2028\u2029]{1,128}\n- presentation: (ax_records)\n/u.exec(content);
1426
+ 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);
1427
+ return match ? { format: match[1]!, single: single !== null, length: match[0].length } : undefined;
1428
+ }
1429
+
1430
+ function formatCompletionRecordMarker(batchId: string): string {
1431
+ const literal = JSON.stringify(batchId).replaceAll(/[<>\u2028\u2029]/gu, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
1432
+ return `<!-- pi-actors:completion ${literal} -->`;
1433
+ }
1434
+
1435
+ function completionStreamBoundary(streamId: string): string {
1436
+ return `— Новый поток ${streamId} · отсчёт с #0 —`;
1437
+ }
1438
+
1439
+ function completionNumberingTechnical(numbering: RunCompletionNumbering): string {
1440
+ return formatMarkdownField("technical", formatLiteralBlock(JSON.stringify({ completion_numbering: numbering })));
1441
+ }
1442
+
1443
+ function readCompletionNumberingFrame(content: string, markerStart: number): RunCompletionNumbering | { malformed: true } | undefined {
1444
+ const match = /\n- technical:\n (`{3,})text\n ([^\r\n]*)\n \1$/u.exec(content.slice(0, markerStart));
1445
+ if (!match) return undefined;
1446
+ let data: Record<string, unknown>;
1447
+ try { data = JSON.parse(match[2]!); } catch { return undefined; }
1448
+ if (!data || typeof data !== "object" || !Object.hasOwn(data, "completion_numbering")) return undefined;
1449
+ try {
1450
+ const raw = asRecord(data.completion_numbering, "Completion numbering"), order = raw.member_order;
1451
+ if (Object.keys(data).length !== 1 || !Array.isArray(order) || order.length < 1 || order.length > Limits.RUN_DELIVERY_BATCH_MAX_MEMBERS) return { malformed: true };
1452
+ const numbering = parseCompletionNumbering(raw, order.length);
1453
+ return completionNumberingTechnical(numbering) === match[0].slice(1) ? numbering : { malformed: true };
1454
+ } catch { return { malformed: true }; }
1455
+ }
1456
+
1457
+ /** Compiler-bound correlation data, not authentication or permission. Body equality cannot identify an epoch. */
1458
+ export function readRunCompletionRecordMarker(content: string): { batchId: string; start: number; single: boolean; headers?: true } | undefined {
1459
+ if (Buffer.byteLength(content, "utf8") > Limits.RUN_COMPLETION_MODEL_MAX_BYTES) return undefined;
1460
+ const fresh = readFrontmatterFrame(content);
1461
+ if (fresh) return { batchId: fresh.batchId, start: fresh.markerStart, single: false, headers: true };
1462
+ const boundary = /^— Новый поток ([^\r\n]+) · отсчёт с #0 —\n/u.exec(content);
1463
+ const body = boundary ? content.slice(boundary[0].length) : content;
1464
+ const numbered = /^#(\?|0|[1-9]\d{0,15}):\n/u.exec(body);
1465
+ const headers = content.startsWith("#0:\n") || content.startsWith("Run #0:\n");
1466
+ const single = content.startsWith("- run:") || content.startsWith("- result: omitted (model byte limit)\n");
1467
+ if (!boundary && !numbered && !headers && !single && !/^- #0:\n|^- omitted_results: [1-9]\d{0,2}\n/u.test(content)) return undefined;
1468
+ const match = /\n<!-- pi-actors:completion ("[^\r\n]{0,768}") -->$/u.exec(content);
1469
+ if (!match) return undefined;
1470
+ try {
1471
+ const batchId: unknown = JSON.parse(match[1]!);
1472
+ if (typeof batchId !== "string" || !batchId.trim() || batchId.length > 128 || formatCompletionRecordMarker(batchId) !== match[0].slice(1)) return undefined;
1473
+ const frame = readCompletionNumberingFrame(content, match.index);
1474
+ if (frame && "malformed" in frame) return undefined;
1475
+ if (frame) {
1476
+ const startsStream = frame.kind === "stream" && frame.first_index === 0;
1477
+ if (startsStream !== !!boundary || (boundary && completionStreamBoundary(frame.stream_id!) + "\n" !== boundary[0])) return undefined;
1478
+ const expected = frame.kind === "stream" ? String(frame.first_index) : "?";
1479
+ if (numbered ? numbered[1] !== expected : !body.startsWith(`- omitted_results: ${frame.member_order.length}\n`)) return undefined;
1480
+ return { batchId, start: match.index, single: false, headers: true };
1481
+ }
1482
+ if (boundary || (numbered && numbered[1] !== "0")) return undefined;
1483
+ return { batchId, start: match.index, single, ...(headers ? { headers: true as const } : {}) };
1484
+ } catch { return undefined; }
1485
+ }
1486
+
1487
+ export type CompletionBlock = readonly [kind: "header" | "body" | "marker" | "service", start: number, end: number];
1488
+ type CompletionPiece = { text: string; compact: boolean; block?: CompletionBlock[0]; field?: readonly [string, string] };
1489
+ type CompletionRow = { pieces: CompletionPiece[]; technical: string; technical_data: Record<string, unknown>; legacy: boolean;
1490
+ completion_text?: Readonly<RunCompletionText> };
1491
+
1492
+ function completionPiece(key: string, value: string, depth = 0, compact = true): CompletionPiece {
1493
+ return { text: formatMarkdownField(key, value, depth), compact, field: [key, value] };
1494
+ }
1495
+
1496
+ function completionPreview(value: string, depth: number, before: boolean, after: boolean, field = "preview"): CompletionPiece[] {
1497
+ const indent = " ".repeat(depth), opening = formatLiteralBlock(value).split("\n", 1)[0]!;
1498
+ return [
1499
+ { text: `${indent}- ${field}:${before ? `\n${indent} …` : ""}`, compact: true },
1500
+ { text: `${indent} ${opening}`, compact: false },
1501
+ { text: `${indent} ${value.replaceAll(/\r\n|\r|\n/gu, newline => `${newline}${indent} `)}`, compact: true },
1502
+ { text: `${indent} ${opening.slice(0, -4)}`, compact: false },
1503
+ ...(after ? [{ text: `${indent} …`, compact: true }] : []),
1504
+ ];
1505
+ }
1506
+
1507
+ function completionCoverage(value: Pick<RunResultDiagnosticPacketExcerpt,
1508
+ "chars_shown" | "chars_total" | "chars_omitted" | "bytes_total" | "shown_bytes" | "truncated">) {
1509
+ return { state: value.truncated ? "clipped" : "complete retained text", chars_shown: value.chars_shown,
1510
+ chars_total: value.chars_total, utf8_range: `[${value.shown_bytes.start}, ${value.shown_bytes.end}) / ${value.bytes_total}` };
1511
+ }
1512
+
1513
+ function isRoutineCompletionSummary(member: RunCompletionBatchMember): boolean {
1514
+ return member.summary === (member.status === "done" ? "Run completed." : `Run ${member.status}.`);
1515
+ }
1516
+
1517
+ function completionRecordRow(member: RunCompletionBatchMember, index: number, byIdentity: Map<string, RunCompletionBatchMember>, flat = false, layout?: RunCompletionBatch["result_layout"], textBudget?: number): CompletionRow {
1518
+ const frontmatter = isFrontmatterCompletionLayout(layout) || layout === "completion_yaml_core_v1";
1519
+ const simpleKinds = frontmatter || layout === "headings_shared_kinds", shared = isSharedCompletionLayout(layout);
1520
+ const outcomes = shared || layout === "headings_outcomes", directOmission = outcomes || layout === "headings_flat_omissions";
1521
+ const run = member.run.startsWith("run:") ? member.run.slice(4) : member.run;
1522
+ const depth = flat ? 0 : 2;
1523
+ const pieces: CompletionPiece[] = [...(flat ? [] : [{ text: ` - #${index}:`, compact: true }]), completionPiece("run", run, depth), completionPiece("state", member.status, depth)];
1524
+ const technical: Record<string, unknown> = {};
1525
+ let primary = member.primary_text?.output;
1526
+ let diagnostic = outcomes && member.diagnostic_excerpt?.field === "command" ? undefined : member.diagnostic_excerpt;
1527
+ let completionText: Readonly<RunCompletionText> | undefined;
1528
+ if (shared && primary) {
1529
+ const captured = member.completion_text;
1530
+ const selected = projectRunCompletionText(captured?.text ?? primary.preview, textBudget!);
1531
+ const start = captured ? 0 : primary.shown_bytes.start, shown = [...selected.text].length;
1532
+ if (captured) completionText = selected;
1533
+ primary = { ...primary, preview: selected.text, shown_bytes: { start, end: start + selected.shown_bytes.end },
1534
+ chars_shown: shown, chars_omitted: primary.chars_total - shown, truncated: shown < primary.chars_total };
1535
+ } else if (shared && diagnostic) {
1536
+ const selected = projectRunCompletionText(diagnostic.text, textBudget!), shown = [...selected.text].length;
1537
+ diagnostic = { ...diagnostic, text: selected.text, shown_bytes: { start: diagnostic.shown_bytes.start,
1538
+ end: diagnostic.shown_bytes.start + selected.shown_bytes.end }, chars_shown: shown,
1539
+ chars_omitted: diagnostic.chars_total - shown, truncated: shown < diagnostic.chars_total };
1540
+ }
1541
+ const kind = primary ? primary.text_basis === "actor_response" ? "response" : "primary" : diagnostic ? "diagnostic" : member.output ? "legacy" : undefined;
1542
+ const boilerplate = isRoutineCompletionSummary(member);
1543
+ let legacy = !!member.output;
1544
+ if (!boilerplate) {
1545
+ legacy = true;
1546
+ let end = Math.min(member.summary.length, 200);
1547
+ if (member.summary.length > 200) {
1548
+ end = 199;
1549
+ if (/[\uD800-\uDBFF]/u.test(member.summary[end - 1]!) && /[\uDC00-\uDFFF]/u.test(member.summary[end]!)) end--;
1550
+ }
1551
+ const summary = member.summary.slice(0, end);
1552
+ if (frontmatter) pieces.push({ text: Buffer.from(summary).toString("utf8") === summary ? summary : "Summary unavailable (malformed retained text)", compact: true, block: "body" });
1553
+ else {
1554
+ pieces.push(completionPiece("summary", Buffer.from(summary).toString("utf8") === summary
1555
+ ? summary + (end < member.summary.length ? "…" : "") : "unavailable (malformed retained text)", depth));
1556
+ if (end < member.summary.length || Buffer.from(summary).toString("utf8") !== summary) pieces.push(completionPiece("summary_omission", "[omitted: unknown]", depth));
1557
+ }
1558
+ }
1559
+ const chosen = primary ?? diagnostic;
1560
+ const legacyText = shared && member.output && Buffer.from(member.output).toString("utf8") === member.output
1561
+ ? projectRunCompletionText(member.output, textBudget!).text : member.output;
1562
+ const value = primary?.preview ?? diagnostic?.text ?? legacyText;
1563
+ if (kind && value !== undefined) {
1564
+ if (!simpleKinds || kind !== "legacy") {
1565
+ pieces.push(completionPiece(simpleKinds ? "kind" : shared ? "result_kind" : "preview_kind",
1566
+ simpleKinds && kind === "response" ? "primary" : kind, depth));
1567
+ }
1568
+ if (diagnostic && !primary) {
1569
+ technical.preview_field = diagnostic.field;
1570
+ if (outcomes) pieces.push(completionPiece("preview_field", diagnostic.field, depth));
1571
+ }
1572
+ if (Buffer.from(value).toString("utf8") !== value) {
1573
+ pieces.push(frontmatter ? { text: "[omitted: unknown] (malformed retained text)", compact: true, block: "marker" }
1574
+ : completionPiece("preview_unavailable", "malformed retained text [omitted: unknown]", depth));
1575
+ } else {
1576
+ if (frontmatter) {
1577
+ if (chosen && chosen.shown_bytes.start > 0) pieces.push({ text: "[omitted: unknown] (retained excerpt starts after the source boundary)", compact: true, block: "marker" });
1578
+ pieces.push({ text: value, compact: true, block: "body" });
1579
+ if (chosen && chosen.chars_omitted > 0) pieces.push({ text: `[${chosen.chars_omitted} characters omitted]`, compact: true, block: "marker" });
1580
+ } else {
1581
+ const complete = shared && primary && primary.shown_bytes.start === 0 && primary.shown_bytes.end === primary.bytes_total && !primary.upstream_truncated;
1582
+ pieces.push(...completionPreview(value, depth, !!chosen && chosen.shown_bytes.start > 0,
1583
+ !!chosen && chosen.shown_bytes.end < chosen.bytes_total, complete ? "result" : "preview"));
1584
+ if (shared && kind === "legacy" && member.output && value !== member.output) pieces.push(completionPiece(simpleKinds ? "text_omission" : "legacy_omission", "[omitted: unknown]", depth));
1585
+ if (chosen && chosen.chars_omitted > 0) pieces.push(completionPiece(...completionOmissionField(chosen.chars_omitted, directOmission), depth));
1586
+ }
1587
+ }
1588
+ if (primary?.redacted) pieces.push(completionPiece("redacted", "true", depth));
1589
+ if (outcomes && primary?.upstream_truncated) pieces.push(frontmatter
1590
+ ? { text: "[omitted: unknown] (upstream completeness unproven)", compact: true, block: "marker" }
1591
+ : completionPiece("upstream_omission", "[omitted: unknown]", depth));
1592
+ } else pieces.push(completionPiece(shared ? "result_state" : "preview_state", "not captured", depth));
1593
+ if (member.parent_state_dir && member.parent_run_instance_id) {
1594
+ const parent = byIdentity.get(`${member.parent_state_dir}\0${member.parent_run_instance_id}`);
1595
+ if (parent && parent.run === member.parent_run) pieces.push(completionPiece("parent_run", parent.run.startsWith("run:") ? parent.run.slice(4) : parent.run, depth));
1596
+ }
1597
+ if (member.primary_text) {
1598
+ if (primary) technical.primary = { scope: primary.text_basis === "actor_response"
1599
+ ? "Executor-selected final Actor response at capture; admission privacy; prior stream clipping remains separate."
1600
+ : "Exported primary text at capture; admission privacy, not complete Actor output.",
1601
+ ...(primary.text_basis ? { text_basis: primary.text_basis, upstream_truncated: primary.upstream_truncated } : {}),
1602
+ ...completionCoverage(primary), ...(primary.redacted ? { redacted: true } : {}), source: primary.source };
1603
+ else if (!member.primary_text.output) pieces.push(completionPiece("primary_unavailable", member.primary_text.output_unavailable, depth));
1604
+ }
1605
+ if (member.result_diagnostics) {
1606
+ const reference = member.result_diagnostics;
1607
+ technical.diagnostic_metadata = { scope: "Diagnostic metadata only; not primary output; admission privacy for recorded fields.",
1608
+ ...(diagnostic ? { field: diagnostic.field, ...completionCoverage(diagnostic) } : {}),
1609
+ recorded_fields_not_shown: reference.fields.filter(field => field !== diagnostic?.field),
1610
+ source: { ...reference.source, bytes_total: reference.bytes_total, fields: reference.fields } };
1611
+ if (primary && diagnostic) technical.secondary_diagnostic_excerpt = axCompletionExcerpt(diagnostic.text, diagnostic, directOmission).join("\n");
1612
+ } else if (member.result_diagnostics === null && member.result_diagnostics_unavailable !== "not_recorded") {
1613
+ pieces.push(completionPiece("diagnostic_metadata_unavailable", member.result_diagnostics_unavailable!, depth));
1614
+ technical.diagnostic_warning = "no legacy body fallback";
1615
+ }
1616
+ if (member.artifact_inventory) {
1617
+ const inventory = member.artifact_inventory;
1618
+ pieces.push(completionPiece("artifact_declarations", String(inventory.declarations_total), depth));
1619
+ technical.artifact_inventory = { scope: "Metadata only; target availability/production not asserted; bodies not read.",
1620
+ source: { ...inventory.source, bytes_total: inventory.bytes_total, declarations_total: inventory.declarations_total } };
1621
+ } else if (member.artifact_inventory === null && member.artifact_inventory_unavailable !== "not_declared") {
1622
+ pieces.push(completionPiece("artifact_inventory_unavailable", member.artifact_inventory_unavailable!, depth));
1623
+ technical.artifact_warning = "no legacy path fallback";
1624
+ }
1625
+ const artifacts = Object.entries(member.artifacts ?? {});
1626
+ if (artifacts.length) {
1627
+ pieces.push(completionPiece("artifact_declarations", String(artifacts.length), depth));
1628
+ technical.artifacts = { scope: "Declared paths; existence/production unproven.", declarations: artifacts.map(([name, path]) => ({ name, path })) };
1629
+ }
1630
+ 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 } : {}) };
1631
+ }
1632
+
1633
+ const FRONTMATTER_FRAME = "<!-- pi-actors:frontmatter ";
1634
+
1635
+ function readFrontmatterFrame(content: string): { blocks: readonly CompletionBlock[]; batchId: string; markerStart: number } | undefined {
1636
+ try {
1637
+ if (Buffer.byteLength(content) > Limits.RUN_COMPLETION_MODEL_MAX_BYTES) return undefined;
1638
+ const match = /\n<!-- pi-actors:frontmatter ([^\r\n]+) -->\n<!-- pi-actors:completion ("[^\r\n]+") -->$/u.exec(content);
1639
+ if (!match) return undefined;
1640
+ const frame = JSON.parse(match[1]!), batchId = JSON.parse(match[2]!);
1641
+ if (typeof batchId !== "string" || !batchId.trim() || batchId.length > 128 || formatCompletionRecordMarker(batchId) !== `<!-- pi-actors:completion ${match[2]} -->`) return undefined;
1642
+ if (frontmatterJson(frame) !== match[1] || Object.keys(frame).sort().join() !== (frame.numbering ? "blocks,numbering,prefix_sha256" : "blocks,prefix_sha256")) return undefined;
1643
+ const prefix = content.slice(0, match.index);
1644
+ if (frame.prefix_sha256 !== createHash("sha256").update(prefix).digest("hex") || !Array.isArray(frame.blocks)) return undefined;
1645
+ let previous = 0, rows = 0, inRow = false;
1646
+ const numbering = frame.numbering ? parseCompletionNumbering(frame.numbering, frame.numbering.member_order.length) : undefined;
1647
+ const startsStream = numbering?.kind === "stream" && numbering.first_index === 0;
1648
+ if (startsStream && (frame.blocks[0]?.[0] !== "service" || content.slice(frame.blocks[0][1], frame.blocks[0][2]) !== completionStreamBoundary(numbering.stream_id))) return undefined;
1649
+ const boundary = (n: number) => !(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");
1650
+ for (const block of frame.blocks) {
1651
+ if (!Array.isArray(block) || block.length !== 3) return undefined;
1652
+ const [kind, start, end] = block;
1653
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start !== previous + (previous ? kind === "header" ? 2 : 1 : 0) || end < start || end > prefix.length || !boundary(start) || !boundary(end)) return undefined;
1654
+ if (content.slice(previous, start) !== (previous ? kind === "header" ? "\n\n" : "\n" : "")) return undefined;
1655
+ const text = content.slice(start, end);
1656
+ if (kind === "header") {
1657
+ const expected = numbering ? numbering.kind === "stream" ? numbering.first_index + rows : "?" : rows;
1658
+ if (!(text.startsWith(`---\nindex: ${expected}\nrun: `) || expected === "?" && text.startsWith('---\nindex: "?"\nrun: ')) || !text.endsWith("\n---\n")) return undefined;
1659
+ const lines = text.slice(4, -5).split("\n");
1660
+ if (!/^state: (done|failed|killed|exited)$/u.test(lines[2] ?? "")) return undefined;
1661
+ const keys = new Set<string>();
1662
+ for (const line of lines) {
1663
+ const field = /^([a-z_]+): (.+)$/u.exec(line);
1664
+ 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]!)) return undefined;
1665
+ if (field[1] === "kind" && (keys.size !== 3 || !/^(?:primary|diagnostic)$/u.test(field[2]!))) return undefined;
1666
+ keys.add(field[1]!);
1667
+ const value = field[2]!;
1668
+ if (!/^(?:[a-z][a-z0-9_-]*|\?|\d+)$/u.test(value) && frontmatterJson(JSON.parse(value)) !== value) return undefined;
1669
+ }
1670
+ rows++; inRow = true;
1671
+ } else if (kind === "body") { if (!inRow) return undefined; }
1672
+ else if (kind === "marker") {
1673
+ 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)) return undefined;
1674
+ } else if (kind === "service") {
1675
+ 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"))) return undefined;
1676
+ inRow = false;
1677
+ } else return undefined;
1678
+ previous = end;
1679
+ }
1680
+ if (previous !== prefix.length || rows > Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS) return undefined;
1681
+ return { blocks: frame.blocks, batchId, markerStart: content.lastIndexOf("\n<!-- pi-actors:completion ") };
1682
+ } catch { return undefined; }
1683
+ }
1684
+
1685
+ function frontmatterJson(value: unknown): string {
1686
+ return JSON.stringify(value).replaceAll(/[<>\u2028\u2029]/gu, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
1687
+ }
1688
+
1689
+ /** All prefix bytes belong to a visible block. Only this closed compiler footer can be hidden. */
1690
+ function assembleFrontmatter(safe: RunCompletionBatch, candidates: readonly { row: CompletionRow; location?: CompletionPiece }[], references: ReadonlySet<number>, ordered: readonly RunCompletionBatchMember[]) {
1691
+ let message = "";
1692
+ const blocks: CompletionBlock[] = [];
1693
+ const add = (kind: CompletionBlock[0], text: string) => {
1694
+ if (message) message += kind === "header" ? "\n\n" : "\n";
1695
+ const start = message.length; message += text;
1696
+ blocks.push(Object.freeze([kind, start, message.length]));
1697
+ };
1698
+ if (safe.numbering?.kind === "stream" && safe.numbering.first_index === 0) add("service", completionStreamBoundary(safe.numbering.stream_id));
1699
+ for (const [index, { row, location }] of candidates.entries()) {
1700
+ const displayed = safe.numbering ? safe.numbering.kind === "stream" ? safe.numbering.first_index + index : "?" : index;
1701
+ const fields = row.pieces.filter(piece => !piece.block);
1702
+ const technical = { ...row.technical_data };
1703
+ for (const key of ["primary", "diagnostic_metadata"]) {
1704
+ if (!technical[key]) continue;
1705
+ const detail = { ...technical[key] as Record<string, unknown> };
1706
+ for (const field of ["state", "chars_shown", "chars_total", "utf8_range", "upstream_truncated"]) {
1707
+ delete detail[field];
1708
+ }
1709
+ technical[key] = detail;
1710
+ }
1711
+ if (Object.hasOwn(technical, "secondary_diagnostic_excerpt")) {
1712
+ delete technical.secondary_diagnostic_excerpt;
1713
+ }
1714
+ const yaml = fields.map(piece => {
1715
+ if (!piece.field) throw new Error("Completion frontmatter field is invalid");
1716
+ const [key, value] = piece.field;
1717
+ return `${key}: ${/^[a-z][a-z0-9_-]*$/u.test(value) && !/^(?:null|true|false|yes|no|on|off)$/iu.test(value) ? value : frontmatterJson(value)}`;
1718
+ });
1719
+ if (Object.keys(technical).length) yaml.push(`references: ${frontmatterJson(technical)}`);
1720
+ if (references.has(index) && location) {
1721
+ const navigation = safe.file_navigation!.find(item => safe.members[item.member_index] === ordered[index])!;
1722
+ yaml.push(`run_files: ${frontmatterJson({ run: ordered[index]!.run, run_instance_id: ordered[index]!.run_instance_id, locations: navigation.locations })}`);
1723
+ }
1724
+ const yamlIndex = displayed === "?" && safe.result_layout === "frontmatter_v2" ? frontmatterJson(displayed) : displayed;
1725
+ add("header", `---\nindex: ${yamlIndex}\n${yaml.join("\n")}\n---\n`);
1726
+ for (const piece of row.pieces) if (piece.block) add(piece.block, piece.text);
1727
+ if (safe.file_navigation !== undefined && !references.has(index)) add("marker", "Run-file reference omitted (packet byte limit).");
1728
+ }
1729
+ const omitted = safe.members.length - candidates.length;
1730
+ if (omitted) {
1731
+ add("service", `- omitted_results: ${omitted}`);
1732
+ const states = (["failed", "killed", "exited"] as const).flatMap(status => {
1733
+ const count = ordered.slice(candidates.length).filter(member => member.status === status).length;
1734
+ return count ? [` - ${status}: ${count}`] : [];
1735
+ });
1736
+ if (states.length) add("service", "- omitted_states:\n" + states.join("\n"));
1737
+ if (!safe.inventory_source) add("service", "- inventory_warning: No inventory reference is retained in this packet.");
1738
+ }
1739
+ if (safe.numbering?.kind === "unavailable") add("service", `- numbering_unavailable: ${safe.numbering.reason}`);
1740
+ if (safe.inventory_source) add("service", `- result_inventory: ${frontmatterJson(safe.inventory_source)}`);
1741
+ const frame = { blocks, prefix_sha256: createHash("sha256").update(message).digest("hex"), ...(safe.numbering ? { numbering: safe.numbering } : {}) };
1742
+ message += `\n${FRONTMATTER_FRAME}${frontmatterJson(frame)} -->\n${formatCompletionRecordMarker(safe.batch_id)}`;
1743
+ return { message, membersShown: candidates.length, blocks: Object.freeze(blocks), compact_ranges: blocks.map(([, a, b]) => [a, b] as [number, number]) };
1744
+ }
1745
+
1746
+ type CompletionYamlProjectionReason = "invalid_snapshot" | "unsupported_source_class" | "unsupported_selected_data" | "overbudget" | "compatibility_refused";
1747
+ class CompletionYamlProjectionRefusal extends Error {
1748
+ readonly reason: CompletionYamlProjectionReason;
1749
+ constructor(reason: CompletionYamlProjectionReason) { super(reason); this.reason = reason; }
1750
+ }
1751
+
1752
+ // Only invoked at known metadata string slots; never on body text or arbitrary user objects.
1753
+ function completionYamlString(value: string): CompletionYamlData {
1754
+ if (value.isWellFormed()) return value;
1755
+ return { utf16_code_units: Array.from({ length: value.length }, (_, index) => value.charCodeAt(index).toString(16).padStart(4, "0")).join(" ") };
1756
+ }
1757
+
1758
+ function completionYamlReferences(row: CompletionRow): Record<string, CompletionYamlData> {
1759
+ const technical = { ...row.technical_data } as Record<string, CompletionYamlData>;
1760
+ for (const key of ["primary", "diagnostic_metadata"]) {
1761
+ if (!technical[key]) continue;
1762
+ const detail = { ...technical[key] as Record<string, CompletionYamlData> };
1763
+ for (const field of ["state", "chars_shown", "chars_total", "utf8_range", "upstream_truncated"]) delete detail[field];
1764
+ technical[key] = detail;
1765
+ }
1766
+ delete technical.secondary_diagnostic_excerpt;
1767
+ for (const key of ["primary", "diagnostic_metadata", "artifact_inventory"]) {
1768
+ const detail = technical[key] as Record<string, CompletionYamlData> | undefined;
1769
+ if (!detail) continue;
1770
+ const source = { ...detail.source as Record<string, CompletionYamlData> };
1771
+ for (const slot of ["path", "run_instance_id", "sha256"]) {
1772
+ if (typeof source[slot] === "string") source[slot] = completionYamlString(source[slot]);
1773
+ }
1774
+ technical[key] = { ...detail, source };
1775
+ }
1776
+ const artifacts = technical.artifacts as { scope: string; declarations: { name: string; path: string }[] } | undefined;
1777
+ if (artifacts) technical.artifacts = { scope: artifacts.scope, declarations: artifacts.declarations.map(item => ({
1778
+ name: completionYamlString(item.name), path: completionYamlString(item.path),
1779
+ })) };
1780
+ return technical;
1781
+ }
1782
+
1783
+ type PreparedCompletionYamlProjection =
1784
+ | { state: "unsupported" }
1785
+ | { state: "overbudget" }
1786
+ | { state: "prepared"; bytes: number; chunks: string[]; packet: CompletionYamlPacket };
1787
+
1788
+ function prepareCompletionYamlProjection(safe: RunCompletionBatch, candidates: readonly { row: CompletionRow; location?: CompletionPiece }[],
1789
+ references: ReadonlySet<number>, ordered: readonly RunCompletionBatchMember[]): PreparedCompletionYamlProjection {
1790
+ const packet: CompletionYamlPacket = { batchId: safe.batch_id, records: [] };
1791
+ if (!safe.batch_id.isWellFormed()) return { state: "unsupported" };
1792
+ for (const [index, { row, location }] of candidates.entries()) {
1793
+ const member = ordered[index]!;
1794
+ const displayed = safe.numbering ? safe.numbering.kind === "stream" ? safe.numbering.first_index + index : "?" : index;
1795
+ const metadata: Record<string, CompletionYamlData> = { index: displayed };
1796
+ for (const piece of row.pieces) {
1797
+ if (piece.block) continue;
1798
+ if (!piece.field) return { state: "unsupported" };
1799
+ const [key, value] = piece.field;
1800
+ if (["run", "state", "kind"].includes(key)) {
1801
+ if (!value.isWellFormed() || !value.length || (key === "kind" && value !== "primary" && value !== "diagnostic")) return { state: "unsupported" };
1802
+ metadata[key] = value;
1803
+ } else if (key === "redacted") metadata[key] = true;
1804
+ else if (key === "artifact_declarations") metadata[key] = member.artifact_inventory?.declarations_total ?? Object.keys(member.artifacts ?? {}).length;
1805
+ else metadata[key] = completionYamlString(value);
1806
+ }
1807
+ const technical = completionYamlReferences(row);
1808
+ if (Object.keys(technical).length) metadata.references = technical;
1809
+ if (references.has(index) && location) {
1810
+ const navigation = safe.file_navigation!.find(item => safe.members[item.member_index] === member)!;
1811
+ // Navigation is already decoded native data, not the JSON display field.
1812
+ const locations = navigation.locations;
1813
+ metadata.run_files = { run: completionYamlString(member.run), run_instance_id: completionYamlString(member.run_instance_id),
1814
+ locations: locations.state === "available" ? { ...locations, run: completionYamlString(locations.run),
1815
+ run_instance_id: completionYamlString(locations.run_instance_id), directory: completionYamlString(locations.directory),
1816
+ files: locations.files.map(file => ({ ...file, ...(typeof file.path === "string" ? { path: completionYamlString(file.path) } : {}) })) } as CompletionYamlData
1817
+ : { ...locations } };
1818
+ }
1819
+ const parts: CompletionYamlPart[] = [];
1820
+ const add = (part: CompletionYamlPart) => { if (parts.length) parts.push({ marker: "\n" }); parts.push(part); };
1821
+ for (const piece of row.pieces) if (piece.block) {
1822
+ if (!piece.text.isWellFormed()) return { state: "unsupported" };
1823
+ add(piece.block === "body" ? { body: piece.text } : { marker: piece.text });
1824
+ }
1825
+ if (safe.file_navigation !== undefined && !references.has(index)) add({ marker: "Run-file reference omitted (packet byte limit)." });
1826
+ packet.records.push({ metadata, parts });
1827
+ }
1828
+ const service: Record<string, CompletionYamlData> = {};
1829
+ const omitted = safe.members.length - candidates.length;
1830
+ if (omitted) {
1831
+ service.omitted_results = omitted;
1832
+ const states: Record<string, CompletionYamlData> = {};
1833
+ for (const status of ["failed", "killed", "exited"] as const) {
1834
+ const count = ordered.slice(candidates.length).filter(member => member.status === status).length;
1835
+ if (count) states[status] = count;
1836
+ }
1837
+ if (Object.keys(states).length) service.omitted_states = states;
1838
+ if (!safe.inventory_source) service.inventory_warning = "No inventory reference is retained in this packet.";
1839
+ }
1840
+ if (safe.numbering) {
1841
+ service.numbering = { ...safe.numbering, session_id: completionYamlString(safe.numbering.session_id), member_order: [...safe.numbering.member_order] };
1842
+ if (safe.numbering.kind === "unavailable") service.numbering_unavailable = safe.numbering.reason;
1843
+ }
1844
+ if (safe.inventory_source) service.result_inventory = { ...safe.inventory_source, path: completionYamlString(safe.inventory_source.path) };
1845
+ if (Object.keys(service).length) packet.service = service;
1846
+ const chunks: string[] = [];
1847
+ let bytes = 0;
1848
+ for (let index = 0; index < Math.max(1, packet.records.length); index++) {
1849
+ const row = packet.records[index];
1850
+ const bookkeeping: Record<string, CompletionYamlData> = index === 0 ? { codec: completionYamlCodec, batch_id: packet.batchId,
1851
+ ...(packet.service ? { service: packet.service } : {}) } : {};
1852
+ if (row?.parts.length) bookkeeping.parts = row.parts.map((part): CompletionYamlData => "body" in part ? { body: part.body.length } : { marker: part.marker.length });
1853
+ const header = { ...row?.metadata, ...(Object.keys(bookkeeping).length ? { _completion: bookkeeping } : {}) };
1854
+ const rendered = renderYamlMetadata(header, completionYamlMaxBytes);
1855
+ if (rendered.state !== "available") return { state: rendered.reason === "metadata_too_large" ? "overbudget" : "unsupported" };
1856
+ if (rendered.text.includes("\n\n")) return { state: "unsupported" };
1857
+ const framed = !!row?.parts.length;
1858
+ chunks.push(framed ? `---\n${rendered.text}---\n` : rendered.text);
1859
+ for (const part of row?.parts ?? []) chunks.push("body" in part ? part.body : part.marker);
1860
+ if (index + 1 < packet.records.length) chunks.push(framed ? "\n\n" : "\n");
1861
+ }
1862
+ for (const chunk of chunks) bytes += Buffer.byteLength(chunk, "utf8");
1863
+ return bytes > completionYamlMaxBytes ? { state: "overbudget" } : { state: "prepared", bytes, chunks, packet };
1864
+ }
1865
+
1866
+ function materializeCompletionYamlProjection(prepared: PreparedCompletionYamlProjection) {
1867
+ if (prepared.state !== "prepared") throw new CompletionYamlProjectionRefusal(prepared.state === "overbudget" ? "overbudget" : "unsupported_selected_data");
1868
+ const message = prepared.chunks.join("");
1869
+ const encoded = encodeCompletionYamlCarrier(prepared.packet);
1870
+ if (encoded.state !== "available" || encoded.text !== message || readCompletionYamlCarrier(message).state !== "available") {
1871
+ throw new CompletionYamlProjectionRefusal("compatibility_refused");
1872
+ }
1873
+ return { message, membersShown: prepared.packet.records.length, compact_ranges: [] as [number, number][] };
1874
+ }
1875
+
1876
+ /** Nondefault pure projection; neither a newly admitted Batch nor a rendering/acceptance grant. */
1877
+ export function projectRunCompletionYamlPrototype(batch: unknown):
1878
+ | { state: "available"; content: string; presentation: { format: "completion_yaml_core_v1"; content_sha256: string }; membersShown: number; textBudget: number }
1879
+ | { state: "unavailable"; reason: CompletionYamlProjectionReason } {
1880
+ let safe: RunCompletionBatch;
1881
+ try { safe = parseBatch(batch); } catch { return { state: "unavailable", reason: "invalid_snapshot" }; }
1882
+ if (safe.presentation_format !== "ax_records" || safe.result_layout !== "frontmatter_v2") return { state: "unavailable", reason: "unsupported_source_class" };
1883
+ try {
1884
+ const projected = projectCompletionRecords(safe, "yaml");
1885
+ return { state: "available", content: projected.message, membersShown: projected.membersShown, textBudget: projected.textBudget!,
1886
+ presentation: { format: "completion_yaml_core_v1", content_sha256: createHash("sha256").update(projected.message).digest("hex") } };
1887
+ } catch (error) {
1888
+ if (error instanceof CompletionYamlProjectionRefusal) return { state: "unavailable", reason: error.reason };
1889
+ throw error;
1890
+ }
1891
+ }
1892
+
1893
+ function projectCompletionRecords(safe: RunCompletionBatch, assembler: "legacy" | "yaml" = safe.result_layout === "completion_yaml_core_v1" ? "yaml" : "legacy"): { message: string; membersShown: number; compact_ranges: [number, number][]; textBudget?: number; blocks?: readonly CompletionBlock[] } {
1894
+ const frontmatter = isFrontmatterCompletionLayout(safe.result_layout) || assembler === "yaml";
1895
+ const shared = isSharedCompletionLayout(safe.result_layout);
1896
+ const maxBytes = shared ? Limits.RUN_COMPLETION_MODEL_MAX_BYTES : Limits.RUN_DELIVERY_MODEL_MAX_BYTES;
1897
+ 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;
1898
+ const numbering = safe.numbering, ordered = numbering ? numbering.member_order.map(slot => safe.members[slot]!) : orderRunCompletionMembers(safe.members);
1899
+ const byIdentity = new Map(safe.members.map(member => [memberIdentity(member), member]));
1900
+ const makeCandidates = (quota?: number) => ordered.slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS).map((member, index) => {
1901
+ const row = completionRecordRow(member, index, byIdentity, true, safe.result_layout, quota);
1902
+ const navigation = safe.file_navigation?.find(item => safe.members[item.member_index] === member);
1903
+ if (navigation?.locations.state === "unavailable") row.pieces.push(completionPiece("run_files_unavailable", navigation.locations.reason));
1904
+ if (row.legacy) row.pieces.push(frontmatter
1905
+ ? { text: "[omitted: unknown] (summary/fallback text: original completeness/privacy unproven)", compact: true, block: "marker" }
1906
+ : safe.result_layout === "headings_shared_kinds"
1907
+ ? completionPiece("warning", "Summary/fallback text: original completeness/privacy unproven [omitted: unknown]")
1908
+ : completionPiece("legacy_basis", "Summary/legacy output: original completeness/privacy unproven [omitted: unknown]"));
1909
+ const technical = Object.keys(row.technical_data).length ? completionPiece("technical", formatLiteralBlock(JSON.stringify(row.technical_data, null, 2)), 0, false) : undefined;
1910
+ const location = navigation ? completionPiece("run_files", formatLiteralBlock(JSON.stringify({ run: member.run,
1911
+ run_instance_id: member.run_instance_id, locations: navigation.locations })), 0, false) : undefined;
1912
+ const baseBytes = assembler === "yaml" ? 0 : Buffer.byteLength([...row.pieces, ...(technical ? [technical] : [])].map(piece => piece.text).join("\n"), "utf8");
1913
+ return { row, navigation, technical, location, baseBytes };
1914
+ });
1915
+ let candidates = makeCandidates(shared ? safe.text_budget ?? 0 : undefined);
1916
+ const assemble = (shown: number, references: ReadonlySet<number>) => {
1917
+ if (frontmatter) return assembleFrontmatter(safe, candidates.slice(0, shown), references, ordered);
1918
+ const pieces: CompletionPiece[] = [];
1919
+ if (numbering?.kind === "stream" && numbering.first_index === 0) pieces.push({ text: completionStreamBoundary(numbering.stream_id), compact: true });
1920
+ for (let index = 0; index < shown; index++) {
1921
+ const { row, technical, location } = candidates[index]!;
1922
+ const fields = [...row.pieces];
1923
+ if (safe.file_navigation !== undefined && !references.has(index)) fields.push(completionPiece("run_files_omitted", "1"));
1924
+ if (technical) fields.push(technical);
1925
+ if (references.has(index)) fields.push(location!);
1926
+ const displayed = numbering ? numbering.kind === "stream" ? numbering.first_index + index : "?" : index;
1927
+ if (headers) pieces.push({ text: `${index ? "\n" : ""}${safe.result_layout === "run_headers" ? "Run " : ""}#${displayed}:`, compact: true });
1928
+ else if (!single) pieces.push({ text: `- #${index}:`, compact: true });
1929
+ pieces.push(...fields.map(piece => headers || single ? piece : { ...piece, text: " " + piece.text.replaceAll(/\r\n|\r|\n/gu, newline => `${newline} `) }));
1930
+ }
1931
+ const omitted = safe.members.length - shown;
1932
+ if (omitted) {
1933
+ pieces.push(single && !headers ? completionPiece("result", "omitted (model byte limit)") : completionPiece("omitted_results", String(omitted)));
1934
+ const states = (["failed", "killed", "exited"] as const).flatMap(status => {
1935
+ const count = ordered.slice(shown).filter(member => member.status === status).length;
1936
+ return count ? [formatMarkdownField(status, String(count))] : [];
1937
+ });
1938
+ if (states.length) pieces.push({ text: "- omitted_states:\n" + states.map(line => " " + line).join("\n"), compact: true });
1939
+ if (!safe.inventory_source) pieces.push(completionPiece("inventory_warning", "No inventory reference is retained in this packet."));
1940
+ }
1941
+ if (numbering?.kind === "unavailable") pieces.push(completionPiece("numbering_unavailable", numbering.reason));
1942
+ if (safe.inventory_source) pieces.push(completionPiece("result_inventory", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2)), 0, false));
1943
+ if (numbering) pieces.push({ text: completionNumberingTechnical(numbering), compact: false });
1944
+ pieces.push({ text: formatCompletionRecordMarker(safe.batch_id), compact: false });
1945
+ let message = "";
1946
+ const compact_ranges: [number, number][] = [];
1947
+ for (const piece of pieces) {
1948
+ if (message) message += "\n";
1949
+ const start = message.length; message += piece.text;
1950
+ if (piece.compact) compact_ranges.push([start, message.length]);
1951
+ }
1952
+ return { message, membersShown: shown, compact_ranges };
1953
+ };
1954
+ // Closed second consumer: legacy assembly remains eager, with its original call order.
1955
+ const prepare = (count: number, refs: ReadonlySet<number>) => prepareCompletionYamlProjection(safe, candidates.slice(0, count), refs, ordered);
1956
+ const measure = (count: number, refs: ReadonlySet<number>): number => {
1957
+ if (assembler === "legacy") return Buffer.byteLength(assemble(count, refs).message, "utf8");
1958
+ const prepared = prepare(count, refs);
1959
+ if (prepared.state === "unsupported") throw new CompletionYamlProjectionRefusal("unsupported_selected_data");
1960
+ return prepared.state === "overbudget" ? Infinity : prepared.bytes;
1961
+ };
1962
+ if (assembler === "yaml") {
1963
+ // Validate selected facts even in rows that fitting may later omit.
1964
+ for (const [index, candidate] of candidates.entries()) {
1965
+ if (prepareCompletionYamlProjection(safe, [candidate], new Set([0]), [ordered[index]!]).state === "unsupported") {
1966
+ throw new CompletionYamlProjectionRefusal("unsupported_selected_data");
1967
+ }
1968
+ }
1969
+ }
1970
+ let shown = 0, textBudget = safe.text_budget;
1971
+ let references = new Set<number>();
1972
+ const fitPrefix = (): void => {
1973
+ let minimumBytes = 0;
1974
+ for (let count = 1; count <= candidates.length; count++) {
1975
+ minimumBytes += candidates[count - 1]!.baseBytes + (count > 1 ? 1 : 0);
1976
+ if (!frontmatter && minimumBytes > maxBytes) break;
1977
+ if (count > shown && measure(count, references) <= maxBytes) shown = count;
1978
+ }
1979
+ };
1980
+ fitPrefix();
1981
+ if (shared && textBudget === undefined) {
1982
+ const maximum = Math.floor(maxBytes / Math.max(1, candidates.length));
1983
+ const boundaries = [...new Set(ordered.slice(0, candidates.length).flatMap(member => {
1984
+ const output = member.primary_text?.output;
1985
+ const diagnostic = member.diagnostic_excerpt?.field === "command" ? undefined : member.diagnostic_excerpt;
1986
+ const tracked = output ?? diagnostic;
1987
+ const text = member.completion_text?.text ?? output?.preview ?? diagnostic?.text ?? member.output;
1988
+ const size = tracked?.bytes_total ?? (text === undefined ? undefined : Buffer.byteLength(text));
1989
+ const points = size !== undefined && size <= maximum ? [size] : [];
1990
+ // Two displayed omitted counts can shrink together at decimal boundaries.
1991
+ // Their scalar positions need byte coordinates, including a possible CRLF retraction.
1992
+ if (tracked && text !== undefined) for (let power = 10; power <= tracked.chars_total; power *= 10) {
1993
+ const count = tracked.chars_total - power + 1;
1994
+ if (count <= 0 || count > text.length) continue;
1995
+ const scalars = [...text]; if (count > scalars.length) continue;
1996
+ const end = Buffer.byteLength(scalars.slice(0, count).join("")) + (scalars[count - 1] === "\r" && scalars[count] === "\n" ? 1 : 0);
1997
+ if (end <= maximum) points.push(end);
1998
+ }
1999
+ return points;
2000
+ }))].sort((a, b) => a - b);
2001
+ // Completing a value removes omission scaffolding: check those discontinuities before omitting rows.
2002
+ for (const point of boundaries) {
2003
+ if (shown === candidates.length) break;
2004
+ candidates = makeCandidates(point); fitPrefix();
2005
+ }
2006
+ const fits = (quota: number): boolean => {
2007
+ candidates = makeCandidates(quota);
2008
+ return measure(shown, references) <= maxBytes;
2009
+ };
2010
+ let low = 0, high = maximum, best = 0;
2011
+ while (low <= high) {
2012
+ const middle = Math.floor((low + high) / 2);
2013
+ if (fits(middle)) { best = middle; low = middle + 1; } else high = middle - 1;
2014
+ }
2015
+ // Cost is monotone between these count/scaffolding discontinuities, not across them.
2016
+ for (let index = 0; index < boundaries.length; index++) {
2017
+ const point = boundaries[index]!;
2018
+ if (point <= best || !fits(point)) continue;
2019
+ best = point; low = point + 1; high = (boundaries[index + 1] ?? maximum + 1) - 1;
2020
+ while (low <= high) {
2021
+ const middle = Math.floor((low + high) / 2);
2022
+ if (fits(middle)) { best = middle; low = middle + 1; } else high = middle - 1;
2023
+ }
2024
+ }
2025
+ textBudget = best; candidates = makeCandidates(best);
2026
+ }
2027
+ // Outcome rows have priority. References of wholly omitted results remain covered by their result omission/inventory.
2028
+ for (const item of safe.file_navigation ?? []) {
2029
+ const index = ordered.indexOf(safe.members[item.member_index]!);
2030
+ if (index >= shown) continue;
2031
+ const next = new Set(references); next.add(index);
2032
+ if (measure(shown, next) > maxBytes) break;
2033
+ references = next;
2034
+ }
2035
+ const result = assembler === "legacy" ? assemble(shown, references) : materializeCompletionYamlProjection(prepare(shown, references));
2036
+ if (Buffer.byteLength(result.message, "utf8") > maxBytes) throw new Error("Run completion batch header exceeds the model byte limit");
2037
+ return { ...result, ...(shared ? { textBudget } : {}) };
2038
+ }
2039
+ /** Freeze the share and selected text, not 64 independent 48KiB copies in the journal. */
2040
+ function retainSharedCompletionTexts(batch: RunCompletionBatch): RunCompletionBatch {
2041
+ if (!isSharedCompletionLayout(batch.result_layout)) return batch;
2042
+ const projected = projectCompletionRecords({ ...batch, text_budget: undefined });
2043
+ const order = batch.numbering ? batch.numbering.member_order.map(slot => batch.members[slot]!) : orderRunCompletionMembers(batch.members);
2044
+ const selected = new Set(order.slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS));
2045
+ return parseBatch({ ...batch, text_budget: projected.textBudget, members: batch.members.map(member => {
2046
+ const { completion_text, ...rest } = member;
2047
+ return completion_text && selected.has(member)
2048
+ ? { ...rest, completion_text: projectRunCompletionText(completion_text.text, projected.textBudget!) } : rest;
2049
+ }) });
2050
+ }
2051
+
2052
+
2053
+ function projectRetainedRecordCompletionBatch(safe: RunCompletionBatch): { message: string; membersShown: number; compact_ranges: [number, number][] } {
2054
+ const single = safe.single_result === true;
2055
+ const ordered = orderRunCompletionMembers(safe.members), byIdentity = new Map(safe.members.map(member => [memberIdentity(member), member]));
2056
+ const times = safe.members.map(member => member.terminal_at).sort();
2057
+ const candidates = ordered.slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS).map((member, index) => {
2058
+ const row = completionRecordRow(member, index, byIdentity, single);
2059
+ const navigation = safe.file_navigation?.find(item => safe.members[item.member_index] === member);
2060
+ if (navigation?.locations.state === "unavailable") row.pieces.push(completionPiece("run_files_unavailable", navigation.locations.reason, single ? 0 : 2));
2061
+ return row;
2062
+ });
2063
+ const assemble = (rows: CompletionRow[], references: string[]) => {
2064
+ const pieces = single ? [completionPiece("completion", safe.batch_id, 0, false), completionPiece("presentation", "ax_records", 0, false)]
2065
+ : [completionPiece("completions", String(safe.members.length)), completionPiece("batch", safe.batch_id, 0, false),
2066
+ completionPiece("presentation", safe.presentation_format!, 0, false)];
2067
+ if (safe.members.length > 1) for (const status of ["done", "failed", "killed", "exited"] as const) {
2068
+ const count = safe.members.filter(member => member.status === status).length;
2069
+ if (count) pieces.push(completionPiece(`${status}_count`, String(count)));
2070
+ }
2071
+ if (!single && rows.some(row => row.legacy)) pieces.push(completionPiece("legacy_basis", "Summary/legacy output: original completeness/privacy unproven [omitted: unknown]"));
2072
+ if (rows.length) pieces.push(...(single ? [] : [{ text: "- members:", compact: true }]), ...rows.flatMap(row => row.pieces));
2073
+ if (single && rows.some(row => row.legacy)) pieces.push(completionPiece("legacy_basis", "Summary/legacy output: original completeness/privacy unproven [omitted: unknown]"));
2074
+ const omitted = safe.members.length - rows.length;
2075
+ if (omitted) {
2076
+ pieces.push(single ? completionPiece("result", "omitted (model byte limit)") : completionPiece("members_omitted", String(omitted)));
2077
+ if (!safe.inventory_source) pieces.push(completionPiece("inventory_warning", "No inventory reference is retained in this packet."));
2078
+ }
2079
+ const summaries = Object.fromEntries(ordered.slice(0, rows.length).filter(isRoutineCompletionSummary).map(member => [member.status, member.summary]));
2080
+ const technical = [formatMarkdownField("window_start", times[0]!), formatMarkdownField("window_end", times.at(-1)!),
2081
+ ...(Object.keys(summaries).length ? [formatMarkdownField("runtime_summaries", formatLiteralBlock(JSON.stringify(summaries)))] : []),
2082
+ ...(safe.inventory_source ? [formatMarkdownField("member_inventory", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2)))] : []),
2083
+ ...(rows.some(row => row.technical) ? [(single ? rows[0]!.technical : formatMarkdownField("member_details", rows.map(row => row.technical).filter(Boolean).join("\n")))] : [])];
2084
+ if (safe.navigation_handoff !== undefined) technical.push(formatMarkdownField("file_investigation", "optional; current session"), formatInspectNextAction("runtime", "runs", true));
2085
+ if (safe.file_navigation !== undefined) {
2086
+ pieces.push(completionPiece("run_files_shown", String(references.length)), completionPiece("run_files_omitted", String(safe.members.length - references.length)));
2087
+ technical.push(formatMarkdownField("run_files", [formatMarkdownField("total", String(safe.members.length)),
2088
+ formatMarkdownField("scope", "Captured completion members only; mutable locations, not body/privacy/terminal proof."), ...references].join("\n")));
2089
+ }
2090
+ pieces.push(completionPiece("technical", technical.join("\n"), 0, false));
2091
+ let message = "";
2092
+ const compact_ranges: [number, number][] = [];
2093
+ for (const piece of pieces) {
2094
+ if (message) message += "\n";
2095
+ const start = message.length; message += piece.text;
2096
+ if (piece.compact) compact_ranges.push([start, message.length]);
2097
+ }
2098
+ return { message, membersShown: rows.length, compact_ranges };
2099
+ };
2100
+ const rows: CompletionRow[] = [], references: string[] = [];
2101
+ for (const candidate of candidates) {
2102
+ if (Buffer.byteLength(assemble([...rows, candidate], references).message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) break;
2103
+ rows.push(candidate);
2104
+ }
2105
+ for (const item of safe.file_navigation ?? []) {
2106
+ const member = safe.members[item.member_index], index = ordered.indexOf(member);
2107
+ const row = formatMarkdownField(single ? "location" : `#${index}`, formatLiteralBlock(JSON.stringify({ run: member.run, run_instance_id: member.run_instance_id, locations: item.locations })));
2108
+ if (Buffer.byteLength(assemble(rows, [...references, row]).message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) break;
2109
+ references.push(row);
2110
+ }
2111
+ const result = assemble(rows, references);
2112
+ if (Buffer.byteLength(result.message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) throw new Error("Run completion batch header exceeds the model byte limit");
2113
+ return result;
2114
+ }
2115
+
2116
+ /** Single full model content plus compiler-owned UI offsets; no source reads or saved-packet mutation. */
2117
+ export function projectRunCompletionBatchPresentation(batch: RunCompletionBatch): { content: string; presentation?: RunCompletionPresentation | { format: "completion_yaml_core_v1"; content_sha256: string } } {
879
2118
  const safe = parseBatch(batch);
2119
+ if (safe.presentation_format !== "ax_records" && safe.presentation_format !== "ax_records_v3") return { content: projectCompletionBatchMessage(safe).message };
2120
+ const result = safe.result_layout !== undefined ? projectCompletionRecords(safe) : projectRetainedRecordCompletionBatch(safe);
2121
+ if (safe.result_layout === "completion_yaml_core_v1") return { content: result.message, presentation: Object.freeze({
2122
+ format: "completion_yaml_core_v1", content_sha256: createHash("sha256").update(result.message).digest("hex"),
2123
+ }) };
2124
+ 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"),
2125
+ ...("blocks" in result ? { blocks: result.blocks as readonly CompletionBlock[] } : {}),
2126
+ compact_ranges: Object.freeze(result.compact_ranges.map(range => Object.freeze(range))) }) };
2127
+ }
2128
+
2129
+ function completionRecordDetailsGap(value: string, single: boolean, terminal: boolean): boolean {
2130
+ let rest = value;
2131
+ while (rest !== (terminal ? "" : "\n")) {
2132
+ const opening = /^\n( *)- (technical|run_files|result_inventory):\n( *)(`{3,})text\n/u.exec(rest);
2133
+ if (!opening) return false;
2134
+ const [, indent, key, padding, fence] = opening;
2135
+ if (indent !== (key === "result_inventory" || single ? "" : " ") || padding !== indent + " ") return false;
2136
+ const ending = `\n${padding}${fence}`, end = rest.indexOf(ending, opening[0].length);
2137
+ if (end < 0) return false;
2138
+ const lines = rest.slice(opening[0].length, end).split("\n");
2139
+ if (lines.some(line => !line.startsWith(padding!))) return false;
2140
+ try {
2141
+ const data: unknown = JSON.parse(lines.map(line => line.slice(padding!.length)).join("\n"));
2142
+ if (!data || typeof data !== "object" || Array.isArray(data)) return false;
2143
+ } catch { return false; }
2144
+ rest = rest.slice(end + ending.length);
2145
+ }
2146
+ return true;
2147
+ }
2148
+
2149
+ /** Display consistency only, never provenance authentication or a new read/action capability. */
2150
+ function readRunCompletionCompactText(content: string, value: unknown): string | undefined {
2151
+ try {
2152
+ if (Buffer.byteLength(content, "utf8") > Limits.RUN_COMPLETION_MODEL_MAX_BYTES) return undefined;
2153
+ const record = Object.getOwnPropertyDescriptors(value as object);
2154
+ if (Object.keys(record).sort().join() !== "compact_ranges,content_sha256,format" || Object.values(record).some(field => !Object.hasOwn(field, "value"))) return undefined;
2155
+ const header = completionRecordHeader(content), records = header ? undefined : readRunCompletionRecordMarker(content);
2156
+ if ((!header && !records) || record.format!.value !== (header?.format ?? "ax_records") || record.content_sha256!.value !== createHash("sha256").update(content).digest("hex")) return undefined;
2157
+ const flat = !!records && (records.single || !!records.headers);
2158
+ const indent = records ? (flat ? " " : " ") : header!.single ? " " : " ";
2159
+ const ranges = record.compact_ranges!.value;
2160
+ if (!Array.isArray(ranges) || ranges.length === 0 || ranges.length > content.length || Object.keys(ranges).length !== ranges.length) return undefined;
2161
+ const text: string[] = []; let previous = 0, fence: string | undefined;
2162
+ const boundary = (index: number) => !(index > 0 && index < content.length && /[\uD800-\uDBFF]/u.test(content[index - 1]!) && /[\uDC00-\uDFFF]/u.test(content[index]!))
2163
+ && !(content[index - 1] === "\r" && content[index] === "\n");
2164
+ for (let index = 0; index < ranges.length; index++) {
2165
+ const descriptor = Object.getOwnPropertyDescriptor(ranges, String(index));
2166
+ if (!descriptor || !Object.hasOwn(descriptor, "value")) return undefined;
2167
+ const range = descriptor.value;
2168
+ if (!Array.isArray(range) || range.length !== 2 || Object.keys(range).length !== 2) return undefined;
2169
+ const start = Object.getOwnPropertyDescriptor(range, "0"), end = Object.getOwnPropertyDescriptor(range, "1");
2170
+ if (!start || !end || !Object.hasOwn(start, "value") || !Object.hasOwn(end, "value")) return undefined;
2171
+ const a = start.value, b = end.value;
2172
+ if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b) || a < previous || b <= a || b > content.length || !boundary(a) || !boundary(b)) return undefined;
2173
+ // Only compiler framing/fences and whole technical/reference blocks may disappear. Authored values stay padded inside ranges.
2174
+ const gap = content.slice(previous, a);
2175
+ if (index === 0) { if (a !== (header?.single ? header.length : 0)) return undefined; }
2176
+ else if (records) {
2177
+ let remaining = gap;
2178
+ if (fence) {
2179
+ const closing = `\n${indent}${fence}`;
2180
+ if (!remaining.startsWith(closing)) return undefined;
2181
+ remaining = remaining.slice(closing.length); fence = undefined;
2182
+ }
2183
+ const opening = (flat ? /^\n (`{3,})text\n$/u : /^\n (`{3,})text\n$/u).exec(remaining);
2184
+ if (opening) {
2185
+ const preview = flat ? /(?:^|\n)- (?:preview|result):(?:\n …)?$/u : /(?:^|\n) - preview:(?:\n …)?$/u;
2186
+ if (!preview.test(content.slice(0, previous))) return undefined;
2187
+ fence = opening[1]!;
2188
+ } else if (!completionRecordDetailsGap(remaining, flat, false)) return undefined;
2189
+ } else if (fence) {
2190
+ if (gap !== `\n${indent}${fence}\n`) return undefined;
2191
+ fence = undefined;
2192
+ } else if (gap !== "\n" && (header!.single || !/^\n- batch: [^\r\n\u2028\u2029]{1,128}\n- presentation: ax_records(?:_v3)?\n$/u.test(gap))) {
2193
+ const opening = (header!.single ? /^\n (`{3,})text\n$/u : /^\n (`{3,})text\n$/u).exec(gap);
2194
+ const preview = header!.single ? /(?:^|\n)- preview:(?:\n …)?$/u : /(?:^|\n) - preview:(?:\n …)?$/u;
2195
+ if (!opening || !preview.test(content.slice(0, previous))) return undefined;
2196
+ fence = opening[1]!;
2197
+ }
2198
+ const selected = content.slice(a, b);
2199
+ if (fence && [...selected.matchAll(/`+/gu)].some(run => run[0].length >= fence!.length)) return undefined;
2200
+ text.push(selected); previous = b;
2201
+ }
2202
+ if (records) {
2203
+ let remaining = content.slice(previous, records.start);
2204
+ if (fence) {
2205
+ const closing = `\n${indent}${fence}`;
2206
+ if (!remaining.startsWith(closing)) return undefined;
2207
+ remaining = remaining.slice(closing.length);
2208
+ }
2209
+ if (!completionRecordDetailsGap(remaining, flat, true)) return undefined;
2210
+ } else {
2211
+ const tail = content.slice(previous);
2212
+ if (fence ? !tail.startsWith(`\n${indent}${fence}\n- technical:\n`) : !tail.startsWith("\n- technical:\n")) return undefined;
2213
+ }
2214
+ return text.join("\n");
2215
+ } catch { return undefined; }
2216
+ }
2217
+
2218
+ function ownCompletionData(value: unknown): unknown {
2219
+ if (value === null || typeof value !== "object") return value;
2220
+ const descriptors = Object.getOwnPropertyDescriptors(value);
2221
+ if (Object.values(descriptors).some(field => !Object.hasOwn(field, "value"))) throw new Error("Accessor presentation data");
2222
+ if (Array.isArray(value)) {
2223
+ if (Object.keys(descriptors).length !== value.length + 1) throw new Error("Sparse presentation data");
2224
+ return Array.from({ length: value.length }, (_, index) => {
2225
+ if (!descriptors[String(index)]) throw new Error("Sparse presentation data");
2226
+ return ownCompletionData(descriptors[String(index)]!.value);
2227
+ });
2228
+ }
2229
+ return Object.fromEntries(Object.entries(descriptors).map(([key, field]) => [key, ownCompletionData(field.value)]));
2230
+ }
2231
+
2232
+ export interface RunCompletionPresentationView {
2233
+ text: string;
2234
+ compact: boolean;
2235
+ single?: true;
2236
+ records?: true;
2237
+ blocks?: readonly CompletionBlock[];
2238
+ yaml?: true;
2239
+ segments?: readonly Readonly<{ kind: "header" | "body" | "marker" | "service" | "separator"; text: string }>[];
2240
+ }
2241
+
2242
+ /** Invalid/missing hints reveal all content; old captures keep their existing host renderer. */
2243
+ export function readRunCompletionPresentation(content: string, value: unknown): RunCompletionPresentationView | undefined {
2244
+ const core = readCompletionYamlCarrier(content);
2245
+ let declaredCore = false;
2246
+ if (value !== null && typeof value === "object" && !utilTypes.isProxy(value)) {
2247
+ try { declaredCore = Object.getOwnPropertyDescriptor(value, "format")?.value === completionYamlCodec; } catch { /* Not own data. */ }
2248
+ }
2249
+ if (core.state === "available" || declaredCore) {
2250
+ const full: RunCompletionPresentationView = { text: content, compact: false, records: true, yaml: true };
2251
+ const hint = copyCompletionYamlData(value);
2252
+ if (core.state !== "available" || !completionYamlMapping(hint) ||
2253
+ !completionYamlKeys(hint, ["format", "content_sha256"]) || hint.format !== completionYamlCodec ||
2254
+ hint.content_sha256 !== createHash("sha256").update(content).digest("hex")) return full;
2255
+ const segments: NonNullable<RunCompletionPresentationView["segments"]>[number][] = [];
2256
+ for (const [index, record] of core.packet.records.entries()) {
2257
+ if (index) segments.push({ kind: "separator", text: "\n" });
2258
+ const metadata = Object.fromEntries(Object.entries(record.metadata).filter(([key]) => key !== "references" && key !== "run_files"));
2259
+ const header = renderYamlMetadata(metadata, completionYamlMaxBytes);
2260
+ if (header.state !== "available") return full;
2261
+ segments.push({ kind: "header", text: record.parts.length ? `---\n${header.text}---` : header.text.replace(/\n$/u, "") });
2262
+ for (const part of record.parts) segments.push("body" in part ? { kind: "body", text: part.body } : { kind: "marker", text: part.marker });
2263
+ }
2264
+ if (core.packet.service) {
2265
+ const service = renderYamlMetadata(core.packet.service, completionYamlMaxBytes);
2266
+ if (service.state !== "available") return full;
2267
+ if (segments.length) segments.push({ kind: "separator", text: "\n" });
2268
+ segments.push({ kind: "service", text: service.text.replace(/\n$/u, "") });
2269
+ }
2270
+ return { text: segments.map(segment => segment.text).join("\n"), compact: true, records: true, yaml: true,
2271
+ segments: Object.freeze(segments.map(segment => Object.freeze(segment))) };
2272
+ }
2273
+ const fresh = readFrontmatterFrame(content);
2274
+ let declaredFormat: unknown;
2275
+ try { declaredFormat = value && typeof value === "object" ? Object.getOwnPropertyDescriptor(value, "format")?.value : undefined; } catch { /* Unreadable hints fall back to full content. */ }
2276
+ if (fresh || declaredFormat === "completion_frontmatter_v1") {
2277
+ try {
2278
+ const hint = ownCompletionData(value) as RunCompletionPresentation;
2279
+ 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]))) throw new Error("Invalid completion presentation");
2280
+ 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))) };
2281
+ } catch { return { text: content, compact: false, records: true }; }
2282
+ }
2283
+ const header = completionRecordHeader(content), records = header ? undefined : readRunCompletionRecordMarker(content);
2284
+ let declared = false;
2285
+ try { const format = Object.getOwnPropertyDescriptor(value as object, "format")?.value; declared = format === "ax_records" || format === "ax_records_v3"; } catch { /* absent/unreadable UI hint */ }
2286
+ if (!header && !records && !declared) return undefined;
2287
+ const text = header || records ? readRunCompletionCompactText(content, value) : undefined;
2288
+ return { text: text ?? content, compact: text !== undefined, ...((header?.single || records?.single) ? { single: true as const } : {}),
2289
+ ...(records ? { records: true as const } : {}) };
2290
+ }
2291
+
2292
+ /** Retained draft codecs only. Journals store snapshots, not full text: replay must reproduce these exact bytes. */
2293
+ function projectRetainedCompletionBatchMessage(safe: RunCompletionBatch): { message: string; membersShown: number } {
2294
+ const batchLegacyBasis = safe.presentation_format === "ax_records_v2";
2295
+ const terminalTimes = safe.members.map(member => member.terminal_at).sort();
2296
+ const statuses = (["done", "failed", "killed", "exited"] as const).flatMap(status => {
2297
+ const count = safe.members.filter(member => member.status === status).length;
2298
+ return count ? [formatMarkdownField(batchLegacyBasis ? `${status}_count` : status, String(count))] : [];
2299
+ });
2300
+ const header = [formatMarkdownField("completions", String(safe.members.length)), formatMarkdownField("batch", safe.batch_id),
2301
+ formatMarkdownField("window_start", terminalTimes[0]), formatMarkdownField("window_end", terminalTimes.at(-1)!),
2302
+ ...(batchLegacyBasis ? [...statuses, formatMarkdownField("legacy_basis", "All legacy summary/output fields: original completeness/privacy unproven."),
2303
+ formatMarkdownField("legacy_upstream", "[omitted: unknown]")] : [formatMarkdownField("statuses", statuses.join("\n"))]),
2304
+ ...(safe.navigation_handoff ? [formatMarkdownField("file_investigation", "optional; current session"), formatInspectNextAction("runtime", "runs", true)] : []),
2305
+ ...(safe.inventory_source ? [formatMarkdownField("member_inventory", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2)))] : [])];
2306
+ const byIdentity = new Map(safe.members.map(member => [memberIdentity(member), member]));
2307
+ const assemble = (rows: string[], references: string[]): string => {
2308
+ const omitted = safe.members.length - rows.length;
2309
+ return [...header, ...(safe.members.length === 1 ? rows : rows.length ? [formatMarkdownField("members", rows.map(row => formatMarkdownField("member", row)).join("\n"))] : []),
2310
+ ...(omitted ? [formatMarkdownField("members_omitted", String(omitted)),
2311
+ ...(!safe.inventory_source ? [formatMarkdownField("inventory_warning", "No inventory reference is retained in this packet.")] : [])] : []),
2312
+ ...(safe.file_navigation === undefined ? [] : [formatMarkdownField("run_files", [
2313
+ formatMarkdownField("shown", String(references.length)), formatMarkdownField("omitted", String(safe.members.length - references.length)),
2314
+ formatMarkdownField("total", String(safe.members.length)),
2315
+ formatMarkdownField("scope", "Captured completion members only; mutable locations, not body/privacy/terminal proof."),
2316
+ ...references.map(row => formatMarkdownField("member", row)),
2317
+ ].join("\n"))])].join("\n");
2318
+ };
2319
+ const rows: string[] = [];
2320
+ for (const member of orderRunCompletionMembers(safe.members).slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)) {
2321
+ const row = formatRetainedCompletionMember(member, byIdentity, batchLegacyBasis);
2322
+ if (Buffer.byteLength(assemble([...rows, row], []), "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) break;
2323
+ rows.push(row);
2324
+ }
2325
+ const references: string[] = [];
2326
+ for (const item of safe.file_navigation ?? []) {
2327
+ const member = safe.members[item.member_index];
2328
+ const row = formatLiteralBlock(JSON.stringify({ run: member.run, run_instance_id: member.run_instance_id, locations: item.locations }));
2329
+ if (Buffer.byteLength(assemble(rows, [...references, row]), "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) break;
2330
+ references.push(row);
2331
+ }
2332
+ const message = assemble(rows, references);
2333
+ if (Buffer.byteLength(message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) throw new Error("Run completion batch header exceeds the model byte limit");
2334
+ return { message, membersShown: rows.length };
2335
+ }
2336
+
2337
+ /** Format one immutable batch for a model-bound custom message, without source reads or phase changes. */
2338
+ export function formatRunCompletionBatchMessage(batch: RunCompletionBatch): string {
2339
+ return projectCompletionBatchMessage(parseBatch(batch)).message;
2340
+ }
2341
+
2342
+ function projectCompletionBatchMessage(safe: RunCompletionBatch): { message: string; membersShown: number } {
2343
+ if (safe.result_layout !== undefined) return projectCompletionRecords(safe);
2344
+ if (safe.presentation_format === "ax_records" || safe.presentation_format === "ax_records_v3") return projectRetainedRecordCompletionBatch(safe);
2345
+ if (safe.presentation_format !== undefined) return projectRetainedCompletionBatchMessage(safe);
880
2346
  const terminalTimes = safe.members.map((member) => member.terminal_at).sort();
881
2347
  const header = [
882
2348
  `Actor completions: ${safe.members.length}`,
883
2349
  `Batch: \`${compactModelText(safe.batch_id, 128)}\``,
884
2350
  `Window: \`${terminalTimes[0]}\` → \`${terminalTimes.at(-1)}\``,
885
2351
  `Statuses: \`${formatStatusCounts(safe.members)}\``,
2352
+ ...(safe.navigation_handoff ? [formatRunFileNavigationHandoff()] : []),
2353
+ ...(safe.inventory_source ? ["Complete captured member inventory retained at capture:", formatLiteralBlock(JSON.stringify(safe.inventory_source, null, 2))] : []),
886
2354
  ];
2355
+ const overflow = (omitted: number) => omitted > 0 ? [`… ${omitted} more completion(s) retained in batch.${safe.inventory_source ? "" : " No inventory reference is retained in this packet."}`] : [];
887
2356
  const byIdentity = new Map(safe.members.map((member) => [
888
2357
  memberIdentity(member),
889
2358
  member,
890
2359
  ]));
891
- const candidateRows = orderCompletionForest(safe.members)
2360
+ const navigation = safe.file_navigation;
2361
+ const navigationCounts = (shown: number) => navigation === undefined ? [] :
2362
+ [`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.`];
2363
+ const candidateRows = orderRunCompletionMembers(safe.members)
892
2364
  .slice(0, Limits.RUN_DELIVERY_MODEL_MAX_MEMBERS)
893
- .map((member) => formatCompletionMember(member, byIdentity));
2365
+ .map((member) => formatCompletionMember(member, byIdentity, safe.excerpt_format === "bracketed_omissions_v1"));
894
2366
  const rows: string[] = [];
895
2367
  for (const row of candidateRows) {
896
2368
  const omitted = safe.members.length - rows.length - 1;
@@ -898,19 +2370,235 @@ export function formatRunCompletionBatchMessage(batch: RunCompletionBatch): stri
898
2370
  ...header,
899
2371
  ...rows,
900
2372
  row,
901
- ...(omitted > 0 ? [`… ${omitted} more completion(s) retained in batch.`] : []),
2373
+ ...overflow(omitted),
2374
+ ...navigationCounts(0),
902
2375
  ].join("\n");
903
2376
  if (Buffer.byteLength(candidate, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) break;
904
2377
  rows.push(row);
905
2378
  }
906
2379
  const omitted = safe.members.length - rows.length;
2380
+ const references: string[] = [];
2381
+ for (const item of navigation ?? []) {
2382
+ const member = safe.members[item.member_index];
2383
+ const row = formatLiteralBlock(JSON.stringify({ run: member.run, run_instance_id: member.run_instance_id, locations: item.locations }));
2384
+ const candidate = [...header, ...rows, ...overflow(omitted), ...navigationCounts(references.length + 1), ...references, row].join("\n");
2385
+ if (Buffer.byteLength(candidate, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) break;
2386
+ references.push(row);
2387
+ }
907
2388
  const message = [
908
2389
  ...header,
909
2390
  ...rows,
910
- ...(omitted > 0 ? [`… ${omitted} more completion(s) retained in batch.`] : []),
2391
+ ...overflow(omitted),
2392
+ ...navigationCounts(references.length),
2393
+ ...references,
911
2394
  ].join("\n");
912
2395
  if (Buffer.byteLength(message, "utf8") > Limits.RUN_DELIVERY_MODEL_MAX_BYTES) {
913
2396
  throw new Error("Run completion batch header exceeds the model byte limit");
914
2397
  }
915
- return message;
2398
+ return { message, membersShown: rows.length };
2399
+ }
2400
+
2401
+ // --- Unadmitted pure YAML carrier prototype ---
2402
+
2403
+ export type CompletionYamlData = null | boolean | number | string | CompletionYamlData[] | { [key: string]: CompletionYamlData };
2404
+ export type CompletionYamlPart = { body: string } | { marker: string };
2405
+ export interface CompletionYamlPacket {
2406
+ batchId: string;
2407
+ records: { metadata: Record<string, CompletionYamlData>; parts: CompletionYamlPart[] }[];
2408
+ service?: Record<string, CompletionYamlData>;
2409
+ }
2410
+ export interface CompletionYamlBlock {
2411
+ kind: "header" | "body" | "marker" | "separator";
2412
+ start: number;
2413
+ end: number;
2414
+ record: number;
2415
+ part?: number;
2416
+ }
2417
+ type CompletionYamlUnavailable = { state: "unavailable" };
2418
+ const completionYamlCodec = "completion_yaml_core_v1";
2419
+ const completionYamlServiceKey = "_completion";
2420
+ const completionYamlMaxBytes = 49152;
2421
+
2422
+ // Snapshot only own data: never coerce or invoke caller accessors/serialization hooks.
2423
+ function copyCompletionYamlData(input: unknown): CompletionYamlData | undefined {
2424
+ let remaining = completionYamlMaxBytes;
2425
+ const active = new Set<object>();
2426
+ const copy = (value: unknown, depth: number): CompletionYamlData | undefined => {
2427
+ if (--remaining < 0 || depth > 64) return undefined;
2428
+ if (value === null || typeof value === "boolean") return value;
2429
+ if (typeof value === "number") return Number.isFinite(value) && !Object.is(value, -0) ? value : undefined;
2430
+ if (typeof value === "string") {
2431
+ remaining -= Buffer.byteLength(value, "utf8");
2432
+ return remaining >= 0 && value.isWellFormed() ? value : undefined;
2433
+ }
2434
+ if (!value || typeof value !== "object" || utilTypes.isProxy(value) || active.has(value)) return undefined;
2435
+ const array = Array.isArray(value), prototype = Object.getPrototypeOf(value);
2436
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) return undefined;
2437
+ const output: CompletionYamlData[] | Record<string, CompletionYamlData> = array ? [] : {};
2438
+ const length = array ? Object.getOwnPropertyDescriptor(value, "length")?.value : undefined;
2439
+ if (array && (!Number.isSafeInteger(length) || length < 0 || length > remaining)) return undefined;
2440
+ active.add(value);
2441
+ let count = 0;
2442
+ for (const key of Reflect.ownKeys(value)) {
2443
+ if (array && key === "length") continue;
2444
+ if (typeof key !== "string" || !key.isWellFormed() || (array && key !== String(count))) return undefined;
2445
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2446
+ if (!descriptor || !Object.hasOwn(descriptor, "value") || !descriptor.enumerable) return undefined;
2447
+ remaining -= Buffer.byteLength(key, "utf8");
2448
+ const child = copy(descriptor.value, depth + 1);
2449
+ if (child === undefined) return undefined;
2450
+ Object.defineProperty(output, key, { value: child, enumerable: true, writable: true, configurable: true });
2451
+ count++;
2452
+ }
2453
+ active.delete(value);
2454
+ return array && count !== length ? undefined : output;
2455
+ };
2456
+ try { return copy(input, 0); } catch { return undefined; }
2457
+ }
2458
+
2459
+ function completionYamlMapping(value: unknown): value is Record<string, CompletionYamlData> {
2460
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2461
+ }
2462
+
2463
+ function completionYamlKeys(value: Record<string, CompletionYamlData>, required: string[], optional: string[] = []): boolean {
2464
+ return required.every(key => Object.hasOwn(value, key)) && Object.keys(value).every(key => required.includes(key) || optional.includes(key));
2465
+ }
2466
+
2467
+ /**
2468
+ * Unadmitted data-only prototype, not source/privacy/identity/acceptance authority.
2469
+ * Grammar: canonical YAML headers, no physical blank lines. Bare headers end LF;
2470
+ * another record adds LF. Body headers are --- LF / YAML / --- LF then raw parts;
2471
+ * another record adds LF LF, independently of authored trailing CR/LF. Parts are
2472
+ * ordered single-key body/marker UTF-16 lengths, never offsets. Only the first
2473
+ * _completion carries codec/batch_id/service. Absent body is DTO parts:[] (no
2474
+ * wire parts); selected empty body is body:0 and has a closed header. Zero rows
2475
+ * is one bare service-only header requiring nonempty selected service facts.
2476
+ * No clipping, fair fitting, source reads, admission or production callers.
2477
+ */
2478
+ export function encodeCompletionYamlCarrier(input: unknown): { state: "available"; text: string } | CompletionYamlUnavailable {
2479
+ const unavailable: CompletionYamlUnavailable = { state: "unavailable" };
2480
+ const packet = copyCompletionYamlData(input);
2481
+ if (!completionYamlMapping(packet) || !completionYamlKeys(packet, ["batchId", "records"], ["service"]) ||
2482
+ typeof packet.batchId !== "string" || !packet.batchId.length || !Array.isArray(packet.records) || packet.records.length > 64 ||
2483
+ (Object.hasOwn(packet, "service") && (!completionYamlMapping(packet.service) || !Object.keys(packet.service).length)) ||
2484
+ (!packet.records.length && !Object.hasOwn(packet, "service"))) return unavailable;
2485
+ const rows: string[] = [];
2486
+ for (let index = 0; index < Math.max(1, packet.records.length); index++) {
2487
+ const row: CompletionYamlData | undefined = packet.records[index];
2488
+ const header: Record<string, CompletionYamlData> = {};
2489
+ const service: Record<string, CompletionYamlData> = {};
2490
+ if (index === 0) {
2491
+ service.codec = completionYamlCodec;
2492
+ service.batch_id = packet.batchId;
2493
+ if (Object.hasOwn(packet, "service")) service.service = packet.service!;
2494
+ }
2495
+ const raw: string[] = [], lengths: CompletionYamlData[] = [];
2496
+ if (packet.records.length) {
2497
+ if (!completionYamlMapping(row) || !completionYamlKeys(row, ["metadata", "parts"]) ||
2498
+ !completionYamlMapping(row.metadata) || !Array.isArray(row.parts)) return unavailable;
2499
+ const meta = row.metadata;
2500
+ if (!(meta.index === "?" || (typeof meta.index === "number" && Number.isSafeInteger(meta.index) && meta.index >= 0)) ||
2501
+ typeof meta.run !== "string" || !meta.run.length || typeof meta.state !== "string" || !meta.state.length ||
2502
+ (Object.hasOwn(meta, "kind") && meta.kind !== "primary" && meta.kind !== "diagnostic") ||
2503
+ Object.hasOwn(meta, completionYamlServiceKey) ||
2504
+ // JS enumerates array-index keys before semantic core fields; do not silently reorder the header.
2505
+ Object.keys(meta).some(key => /^(0|[1-9]\d*)$/u.test(key) && Number(key) < 4294967295)) return unavailable;
2506
+ for (const key of ["index", "run", "state", ...(Object.hasOwn(meta, "kind") ? ["kind"] : []),
2507
+ ...Object.keys(meta).filter(key => !["index", "run", "state", "kind"].includes(key))]) {
2508
+ Object.defineProperty(header, key, { value: meta[key]!, enumerable: true });
2509
+ }
2510
+ let hasBody = false;
2511
+ for (const part of row.parts) {
2512
+ if (!completionYamlMapping(part) || Object.keys(part).length !== 1) return unavailable;
2513
+ const key = Object.keys(part)[0]!;
2514
+ if ((key !== "body" && key !== "marker") || typeof part[key] !== "string") return unavailable;
2515
+ if (key === "body") hasBody = true;
2516
+ lengths.push({ [key]: part[key].length });
2517
+ raw.push(part[key]);
2518
+ }
2519
+ // An explicit selected empty body is real; empty markers alone cannot justify a wrapper.
2520
+ if (lengths.length && !hasBody && raw.every(text => text.length === 0)) return unavailable;
2521
+ if (lengths.length) service.parts = lengths;
2522
+ }
2523
+ if (Object.keys(service).length) header[completionYamlServiceKey] = service;
2524
+ const rendered = renderYamlMetadata(header, completionYamlMaxBytes);
2525
+ if (rendered.state !== "available" || rendered.text.includes("\n\n")) return unavailable;
2526
+ const text = lengths.length ? `---\n${rendered.text}---\n${raw.join("")}` : rendered.text;
2527
+ rows.push(text + (index + 1 < packet.records.length ? (lengths.length ? "\n\n" : "\n") : ""));
2528
+ }
2529
+ const text = rows.join("");
2530
+ return Buffer.byteLength(text, "utf8") <= completionYamlMaxBytes ? { state: "available", text } : unavailable;
2531
+ }
2532
+
2533
+ /** Strict whole-carrier decoder. Derived ranges are consistency data, never authenticity. */
2534
+ export function readCompletionYamlCarrier(input: unknown):
2535
+ { state: "available"; packet: CompletionYamlPacket; blocks: CompletionYamlBlock[] } | CompletionYamlUnavailable {
2536
+ const unavailable: CompletionYamlUnavailable = { state: "unavailable" };
2537
+ if (typeof input !== "string" || !input.isWellFormed() || !input.length || Buffer.byteLength(input, "utf8") > completionYamlMaxBytes) return unavailable;
2538
+ try {
2539
+ let cursor = 0;
2540
+ const packet: CompletionYamlPacket = { batchId: "", records: [] };
2541
+ const blocks: CompletionYamlBlock[] = [];
2542
+ while (cursor < input.length) {
2543
+ if (packet.records.length >= 64) return unavailable;
2544
+ const record = packet.records.length, start = cursor, framed = input.startsWith("---\n", cursor);
2545
+ let yaml: string;
2546
+ if (framed) {
2547
+ const close = input.indexOf("\n---\n", cursor + 4);
2548
+ if (close < 0) return unavailable;
2549
+ yaml = input.slice(cursor + 4, close + 1);
2550
+ cursor = close + 5;
2551
+ } else {
2552
+ const close = input.indexOf("\n\n", cursor);
2553
+ cursor = close < 0 ? input.length : close + 1;
2554
+ yaml = input.slice(start, cursor);
2555
+ }
2556
+ const document = parseDocument(yaml, { version: "1.2", schema: "core", uniqueKeys: true });
2557
+ if (document.errors.length || document.warnings.length) return unavailable;
2558
+ const header: unknown = document.toJS({ maxAliasCount: 0 });
2559
+ if (!completionYamlMapping(header)) return unavailable;
2560
+ const canonical = renderYamlMetadata(header, completionYamlMaxBytes);
2561
+ if (canonical.state !== "available" || canonical.text !== yaml) return unavailable;
2562
+ const service = header[completionYamlServiceKey];
2563
+ if (record === 0) {
2564
+ if (!completionYamlMapping(service) || !completionYamlKeys(service, ["codec", "batch_id"], ["parts", "service"]) ||
2565
+ service.codec !== completionYamlCodec || typeof service.batch_id !== "string" || !service.batch_id.length) return unavailable;
2566
+ packet.batchId = service.batch_id;
2567
+ if (Object.hasOwn(service, "service")) {
2568
+ if (!completionYamlMapping(service.service) || !Object.keys(service.service).length) return unavailable;
2569
+ packet.service = service.service;
2570
+ }
2571
+ } else if (service !== undefined && (!completionYamlMapping(service) || !completionYamlKeys(service, ["parts"]))) return unavailable;
2572
+ blocks.push({ kind: "header", start, end: cursor, record });
2573
+ delete header[completionYamlServiceKey];
2574
+ const lengths = completionYamlMapping(service) ? service.parts : undefined;
2575
+ if (framed ? !Array.isArray(lengths) || !lengths.length : lengths !== undefined) return unavailable;
2576
+ const parts: CompletionYamlPart[] = [];
2577
+ if (Array.isArray(lengths)) {
2578
+ for (const descriptor of lengths) {
2579
+ if (!completionYamlMapping(descriptor) || Object.keys(descriptor).length !== 1) return unavailable;
2580
+ const key = Object.keys(descriptor)[0]!;
2581
+ const length = descriptor[key];
2582
+ if ((key !== "body" && key !== "marker") || typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > input.length - cursor) return unavailable;
2583
+ const text = input.slice(cursor, cursor + length);
2584
+ if (!text.isWellFormed()) return unavailable;
2585
+ parts.push(key === "body" ? { body: text } : { marker: text });
2586
+ blocks.push({ kind: key, start: cursor, end: cursor + length, record, part: parts.length - 1 });
2587
+ cursor += length;
2588
+ }
2589
+ }
2590
+ if (!Object.keys(header).length) {
2591
+ if (record !== 0 || framed || !packet.service || cursor !== input.length) return unavailable;
2592
+ } else packet.records.push({ metadata: header, parts });
2593
+ if (cursor < input.length) {
2594
+ const gap = framed ? "\n\n" : "\n";
2595
+ if (!input.startsWith(gap, cursor)) return unavailable;
2596
+ blocks.push({ kind: "separator", start: cursor, end: cursor + gap.length, record });
2597
+ cursor += gap.length;
2598
+ if (cursor === input.length) return unavailable;
2599
+ }
2600
+ }
2601
+ const encoded = encodeCompletionYamlCarrier(packet);
2602
+ return encoded.state === "available" && encoded.text === input ? { state: "available", packet, blocks } : unavailable;
2603
+ } catch { return unavailable; }
916
2604
  }