@remnic/core 9.50.3 → 9.51.1

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 (486) hide show
  1. package/dist/access-admin-ops-surface.d.ts +13 -11
  2. package/dist/access-admin-ops-surface.js +41 -39
  3. package/dist/access-authorization-probe.d.ts +13 -11
  4. package/dist/access-authorization-probe.js +42 -40
  5. package/dist/access-boundary.d.ts +13 -11
  6. package/dist/access-boundary.js +41 -39
  7. package/dist/access-cli.js +109 -104
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +3 -1
  10. package/dist/access-extraction-force-flush.d.ts +13 -11
  11. package/dist/access-extraction-force-flush.js +41 -39
  12. package/dist/access-health-types.d.ts +3 -1
  13. package/dist/access-http-lcm-compaction.d.ts +13 -11
  14. package/dist/access-http-lifecycle-flush.d.ts +13 -11
  15. package/dist/access-http-offline-stream.d.ts +13 -11
  16. package/dist/access-http-query.js +41 -39
  17. package/dist/access-http.d.ts +13 -11
  18. package/dist/access-http.js +52 -50
  19. package/dist/access-identity-continuity-surface.d.ts +11 -9
  20. package/dist/access-identity-continuity-surface.js +41 -39
  21. package/dist/access-lcm-surface.d.ts +13 -11
  22. package/dist/access-lcm-surface.js +41 -39
  23. package/dist/access-mcp.d.ts +13 -11
  24. package/dist/access-mcp.js +47 -45
  25. package/dist/access-memory-search-fanout.d.ts +4 -2
  26. package/dist/{access-namespace-preflight-Dj2QolFE.d.ts → access-namespace-preflight-Ds6CGlCP.d.ts} +1 -1
  27. package/dist/access-namespace-preflight.d.ts +5 -3
  28. package/dist/access-namespace-preflight.js +41 -39
  29. package/dist/access-observe-write-surface.d.ts +13 -11
  30. package/dist/access-observe-write-surface.js +41 -39
  31. package/dist/access-offline-manifest.d.ts +11 -9
  32. package/dist/access-operations-batch.js +43 -41
  33. package/dist/access-operations.d.ts +16 -14
  34. package/dist/access-operations.js +46 -44
  35. package/dist/access-recall-concurrency.d.ts +13 -11
  36. package/dist/access-recall-concurrency.js +41 -39
  37. package/dist/access-recall-response.d.ts +13 -11
  38. package/dist/access-recall-response.js +41 -39
  39. package/dist/access-recall-surface.d.ts +13 -11
  40. package/dist/access-recall-surface.js +41 -39
  41. package/dist/access-schema.d.ts +88 -88
  42. package/dist/access-schema.js +3 -3
  43. package/dist/{access-service-DesbnTSa.d.ts → access-service-BY0KJ44c.d.ts} +7 -7
  44. package/dist/access-service-helpers.d.ts +13 -11
  45. package/dist/access-service.d.ts +13 -11
  46. package/dist/access-service.js +41 -39
  47. package/dist/access-surface-catalog.d.ts +13 -11
  48. package/dist/access-wearables-meetings-surface.d.ts +5 -3
  49. package/dist/action-confidence.d.ts +3 -1
  50. package/dist/active-memory-bridge.d.ts +3 -1
  51. package/dist/active-recall.d.ts +3 -1
  52. package/dist/active-recall.js +5 -3
  53. package/dist/active-recall.js.map +1 -1
  54. package/dist/adapters/index.js +4 -4
  55. package/dist/adapters/registry.js +2 -2
  56. package/dist/ambient-provenance.d.ts +3 -1
  57. package/dist/archive-mutation-version.d.ts +9 -0
  58. package/dist/archive-mutation-version.js +20 -0
  59. package/dist/artifact-search.d.ts +3 -1
  60. package/dist/behavior-learner.d.ts +3 -1
  61. package/dist/behavior-signals.d.ts +3 -1
  62. package/dist/bootstrap.d.ts +11 -9
  63. package/dist/briefing.d.ts +4 -2
  64. package/dist/briefing.js +10 -9
  65. package/dist/buffer-surprise-report.d.ts +3 -1
  66. package/dist/buffer-turn-helpers.d.ts +3 -1
  67. package/dist/buffer.d.ts +4 -2
  68. package/dist/buffer.js +5 -5
  69. package/dist/bulk-import/index.d.ts +5 -3
  70. package/dist/calibration.d.ts +3 -1
  71. package/dist/capabilities.d.ts +3 -1
  72. package/dist/{catalog-D-G7RgXR.d.ts → catalog-D3B4RDy1.d.ts} +1 -1
  73. package/dist/causal-behavior.d.ts +3 -1
  74. package/dist/causal-behavior.js +2 -2
  75. package/dist/causal-chain.js +2 -2
  76. package/dist/causal-consolidation.d.ts +3 -1
  77. package/dist/causal-consolidation.js +13 -12
  78. package/dist/causal-consolidation.js.map +1 -1
  79. package/dist/causal-retrieval.js +2 -2
  80. package/dist/causal-trajectory-graph.d.ts +4 -2
  81. package/dist/causal-trajectory-graph.js +2 -1
  82. package/dist/causal-trajectory-graph.js.map +1 -1
  83. package/dist/causal-trajectory.js +1 -1
  84. package/dist/{chunk-KDG2KTWW.js → chunk-33SV3E43.js} +4 -4
  85. package/dist/{chunk-2HGUDBRJ.js → chunk-37ZO6LLJ.js} +2 -2
  86. package/dist/{chunk-QW3WFHKU.js → chunk-45BRYON6.js} +2 -2
  87. package/dist/{chunk-GZICBRWK.js → chunk-4F6X3RXW.js} +2 -2
  88. package/dist/{chunk-7BAYBYY5.js → chunk-4PC4PRJE.js} +2 -2
  89. package/dist/{chunk-VJXY5ZCT.js → chunk-52UKFTUK.js} +2 -2
  90. package/dist/{chunk-ESG7Z255.js → chunk-5UDMFE3Q.js} +2 -2
  91. package/dist/{chunk-JPHEOE6Z.js → chunk-6MTSFX7A.js} +15 -15
  92. package/dist/chunk-6ZN34ISG.js +44 -0
  93. package/dist/chunk-6ZN34ISG.js.map +1 -0
  94. package/dist/{chunk-M4MP34AQ.js → chunk-AHZOMHWN.js} +1 -1
  95. package/dist/{chunk-Z3RHPGSY.js → chunk-AIUBBHSF.js} +3 -3
  96. package/dist/{chunk-S3AYIQCU.js → chunk-AXIDQG5A.js} +12 -12
  97. package/dist/{chunk-QN7NTKRC.js → chunk-AZ2QBGYB.js} +1 -1
  98. package/dist/{chunk-47J4EDGR.js → chunk-AZM4CCAC.js} +8 -8
  99. package/dist/{chunk-IXXABW5A.js → chunk-BHXYAVUM.js} +11 -5
  100. package/dist/chunk-BHXYAVUM.js.map +1 -0
  101. package/dist/{chunk-L2DPF4DF.js → chunk-BIJAWZYW.js} +54 -12
  102. package/dist/chunk-BIJAWZYW.js.map +1 -0
  103. package/dist/{chunk-LEJPSMKM.js → chunk-C5QLI33R.js} +3 -3
  104. package/dist/{chunk-SV3PSA7Y.js → chunk-CV6BBVYV.js} +5 -5
  105. package/dist/{chunk-V7X767RT.js → chunk-DCZBSE4F.js} +52 -52
  106. package/dist/chunk-FC67T4E6.js +70 -0
  107. package/dist/chunk-FC67T4E6.js.map +1 -0
  108. package/dist/{chunk-7PKFPLLZ.js → chunk-FXTYIA46.js} +2 -2
  109. package/dist/{chunk-64BHFFKZ.js → chunk-HLYR2TQL.js} +5 -5
  110. package/dist/{chunk-5C2HNJMO.js → chunk-J5LLX6IL.js} +32 -32
  111. package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
  112. package/dist/{chunk-PLKFO2HJ.js → chunk-JL43BVDE.js} +2 -2
  113. package/dist/{chunk-2S72LWKX.js → chunk-JYL3BVVZ.js} +2 -2
  114. package/dist/{chunk-IPLYGWQF.js → chunk-KQAFEZQX.js} +5 -5
  115. package/dist/chunk-LXY5VVGM.js +61 -0
  116. package/dist/chunk-LXY5VVGM.js.map +1 -0
  117. package/dist/{chunk-PYKKWQZ2.js → chunk-M7BXDKPR.js} +6 -6
  118. package/dist/{chunk-Z54EM5YZ.js → chunk-MAXVH44E.js} +7 -7
  119. package/dist/{chunk-LYMNZZIM.js → chunk-NIS256NW.js} +2 -2
  120. package/dist/{chunk-67QG75MJ.js → chunk-NLK2VZYZ.js} +2 -2
  121. package/dist/{chunk-A4QMKFFO.js → chunk-O5FFBO2M.js} +14 -28
  122. package/dist/chunk-O5FFBO2M.js.map +1 -0
  123. package/dist/{chunk-JYIFGGEM.js → chunk-OUKBMQ2O.js} +2 -2
  124. package/dist/{chunk-75XOWNRS.js → chunk-OYESWJ43.js} +4 -4
  125. package/dist/{chunk-TS4AJ2GI.js → chunk-PA45XCUD.js} +3 -3
  126. package/dist/{chunk-BJYET5TX.js → chunk-PRDJMOST.js} +7 -1
  127. package/dist/chunk-PRDJMOST.js.map +1 -0
  128. package/dist/{chunk-4MBPIL4V.js → chunk-PUSLMZBQ.js} +2 -2
  129. package/dist/{chunk-RVO6TCTU.js → chunk-QCECHYZL.js} +6 -6
  130. package/dist/{chunk-GFIKG4FR.js → chunk-QGR6OVGT.js} +2 -2
  131. package/dist/{chunk-NSGU76YO.js → chunk-QHFV6VNU.js} +6 -2
  132. package/dist/chunk-QHFV6VNU.js.map +1 -0
  133. package/dist/{chunk-PDEKQWDV.js → chunk-QT2FRYI4.js} +18 -2
  134. package/dist/chunk-QT2FRYI4.js.map +1 -0
  135. package/dist/{chunk-HNZKVLAN.js → chunk-S354QO4R.js} +2 -2
  136. package/dist/{chunk-NO37MR45.js → chunk-SOEN6KCF.js} +2 -2
  137. package/dist/{chunk-FXX6RQYC.js → chunk-TPTECFWR.js} +2 -2
  138. package/dist/{chunk-GDDGZVFI.js → chunk-UEVLYVNW.js} +2 -2
  139. package/dist/{chunk-YQ4IQ2GA.js → chunk-UT5BV5OG.js} +2 -2
  140. package/dist/{chunk-LAQ33B5D.js → chunk-UU3G6GUP.js} +1 -1
  141. package/dist/chunk-UU3G6GUP.js.map +1 -0
  142. package/dist/{chunk-LNTUXKBT.js → chunk-UYYW2KPU.js} +2 -2
  143. package/dist/{chunk-BOFVT6P3.js → chunk-VBHPOWBJ.js} +175 -98
  144. package/dist/chunk-VBHPOWBJ.js.map +1 -0
  145. package/dist/{chunk-QSYL5CXK.js → chunk-VKXWK44Q.js} +3 -3
  146. package/dist/{chunk-KMWYZTIE.js → chunk-VMMQDEN5.js} +2 -2
  147. package/dist/{chunk-JB6D7EFB.js → chunk-VVG2BOKA.js} +2 -2
  148. package/dist/chunk-W5BIQOAJ.js +69 -0
  149. package/dist/chunk-W5BIQOAJ.js.map +1 -0
  150. package/dist/{chunk-V5H6OJCR.js → chunk-W5XVQFLM.js} +2 -2
  151. package/dist/{chunk-OIOZ6MOP.js → chunk-W6FNX2LP.js} +2 -2
  152. package/dist/{chunk-6QPHF6RR.js → chunk-WQM326GS.js} +2 -2
  153. package/dist/{chunk-TQFHQPWD.js → chunk-XADV5IZ3.js} +8 -8
  154. package/dist/{chunk-DATDLJAO.js → chunk-XAPMEU3K.js} +2 -2
  155. package/dist/chunk-XXSOVESA.js +42 -0
  156. package/dist/chunk-XXSOVESA.js.map +1 -0
  157. package/dist/{chunk-IK6X5QPO.js → chunk-ZHY5TMJG.js} +1193 -443
  158. package/dist/chunk-ZHY5TMJG.js.map +1 -0
  159. package/dist/{chunk-N3LL5NEU.js → chunk-ZMYDYNI4.js} +2 -2
  160. package/dist/{cli-D4vvtxjY.d.ts → cli-CnOkH9hA.d.ts} +6 -6
  161. package/dist/cli.d.ts +15 -13
  162. package/dist/cli.js +74 -70
  163. package/dist/coding/pre-action-gate.d.ts +3 -1
  164. package/dist/coding/pre-action-gate.js +2 -2
  165. package/dist/compounding/engine.d.ts +4 -2
  166. package/dist/compounding/engine.js +10 -9
  167. package/dist/compounding/preference-consolidator.d.ts +3 -1
  168. package/dist/compression-optimizer.d.ts +3 -1
  169. package/dist/config.d.ts +3 -1
  170. package/dist/config.js +5 -3
  171. package/dist/connectors/codex-materialize-runner.d.ts +3 -1
  172. package/dist/connectors/codex-materialize-runner.js +10 -9
  173. package/dist/connectors/codex-materialize.d.ts +3 -1
  174. package/dist/connectors/index.d.ts +3 -1
  175. package/dist/connectors/index.js +10 -9
  176. package/dist/consolidation-provenance-check.d.ts +4 -2
  177. package/dist/consolidation-undo.d.ts +4 -2
  178. package/dist/contradiction/index.d.ts +4 -2
  179. package/dist/contradiction-config.d.ts +29 -0
  180. package/dist/contradiction-config.js +12 -0
  181. package/dist/contradiction-config.js.map +1 -0
  182. package/dist/converge-config.d.ts +3 -1
  183. package/dist/convergence-refresh.d.ts +4 -2
  184. package/dist/convergence-refresh.js +12 -11
  185. package/dist/conversation-index/backend.d.ts +3 -1
  186. package/dist/conversation-index/chunker.d.ts +3 -1
  187. package/dist/conversation-index/faiss-adapter.d.ts +3 -1
  188. package/dist/conversation-index/indexer.d.ts +3 -1
  189. package/dist/conversation-index/search.d.ts +3 -1
  190. package/dist/corpus-watermark.d.ts +3 -1
  191. package/dist/corpus-watermark.js +21 -20
  192. package/dist/day-summary.d.ts +3 -1
  193. package/dist/delinearize.d.ts +3 -1
  194. package/dist/dependency-propagation-config.d.ts +3 -1
  195. package/dist/direct-answer-wiring.d.ts +3 -1
  196. package/dist/direct-answer.d.ts +3 -1
  197. package/dist/embedding-fallback.d.ts +3 -1
  198. package/dist/enrichment/index.d.ts +3 -1
  199. package/dist/entity-retrieval.d.ts +4 -2
  200. package/dist/entity-retrieval.js +10 -9
  201. package/dist/entity-schema.d.ts +3 -1
  202. package/dist/explicit-capture.d.ts +11 -9
  203. package/dist/explicit-capture.js +3 -2
  204. package/dist/external-wiki-access.d.ts +13 -11
  205. package/dist/external-wiki-access.js +42 -40
  206. package/dist/external-wiki-collection-registration.d.ts +3 -1
  207. package/dist/external-wiki-collection.d.ts +3 -1
  208. package/dist/external-wiki-mcp-tools.d.ts +13 -11
  209. package/dist/extraction-error-classification.d.ts +3 -1
  210. package/dist/extraction-faithfulness.d.ts +3 -1
  211. package/dist/extraction-judge-telemetry.d.ts +3 -1
  212. package/dist/extraction-judge-training.d.ts +3 -1
  213. package/dist/extraction-judge.d.ts +3 -1
  214. package/dist/extraction-liveness.d.ts +3 -1
  215. package/dist/extraction-normalization.d.ts +3 -1
  216. package/dist/extraction-prompt.d.ts +3 -1
  217. package/dist/extraction-source-grounding-rules.d.ts +3 -1
  218. package/dist/extraction-source-grounding.d.ts +3 -1
  219. package/dist/extraction.d.ts +3 -1
  220. package/dist/extraction.js +22 -21
  221. package/dist/fallback-llm.d.ts +3 -1
  222. package/dist/graph-444E-DrF.d.ts +349 -0
  223. package/dist/graph-dashboard-diff.d.ts +4 -2
  224. package/dist/graph-dashboard-key.d.ts +4 -2
  225. package/dist/graph-dashboard-parser.d.ts +4 -2
  226. package/dist/{graph-edge-decay-Z6ENALU2.js → graph-edge-decay-65KMOJWY.js} +4 -3
  227. package/dist/graph-edge-decay-65KMOJWY.js.map +1 -0
  228. package/dist/graph-edge-reinforcement.d.ts +4 -2
  229. package/dist/graph-path-reconstruction.d.ts +10 -0
  230. package/dist/graph-path-reconstruction.js +12 -0
  231. package/dist/graph-path-reconstruction.js.map +1 -0
  232. package/dist/graph-path-scoring-config.d.ts +8 -0
  233. package/dist/graph-path-scoring-config.js +10 -0
  234. package/dist/graph-path-scoring-config.js.map +1 -0
  235. package/dist/graph-path-scoring.d.ts +36 -0
  236. package/dist/graph-path-scoring.js +11 -0
  237. package/dist/graph-path-scoring.js.map +1 -0
  238. package/dist/graph-snapshot.d.ts +4 -2
  239. package/dist/graph-snapshot.js +3 -2
  240. package/dist/graph.d.ts +5 -318
  241. package/dist/graph.js +5 -1
  242. package/dist/harmonic-retrieval.js +1 -1
  243. package/dist/importance.d.ts +3 -1
  244. package/dist/importers/index.d.ts +3 -1
  245. package/dist/in-flight-reads.d.ts +3 -1
  246. package/dist/index.d.ts +696 -694
  247. package/dist/index.js +149 -144
  248. package/dist/intent.d.ts +3 -1
  249. package/dist/lcm/engine.d.ts +3 -1
  250. package/dist/lcm/engine.js +3 -3
  251. package/dist/lcm/index.d.ts +3 -1
  252. package/dist/lcm/index.js +6 -6
  253. package/dist/lcm/tools.d.ts +3 -1
  254. package/dist/lifecycle.d.ts +3 -1
  255. package/dist/live-connectors-runner.d.ts +3 -1
  256. package/dist/local-llm.d.ts +3 -1
  257. package/dist/local-model-endpoint.d.ts +3 -1
  258. package/dist/maintenance/memory-governance.d.ts +3 -1
  259. package/dist/maintenance/memory-governance.js +10 -9
  260. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +4 -2
  261. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -9
  262. package/dist/maintenance/rebuild-memory-projection.d.ts +4 -2
  263. package/dist/maintenance/rebuild-memory-projection.js +11 -10
  264. package/dist/{maintenance-ClmUVpGI.d.ts → maintenance-CKwxpdRx.d.ts} +3 -3
  265. package/dist/mcp-memory-inspector-app.d.ts +13 -11
  266. package/dist/memory-action-policy.d.ts +3 -1
  267. package/dist/memory-cache.d.ts +3 -1
  268. package/dist/memory-lifecycle-ledger-utils.d.ts +3 -1
  269. package/dist/memory-projection-store.d.ts +3 -1
  270. package/dist/memory-provenance.d.ts +3 -1
  271. package/dist/memory-snapshot.d.ts +3 -1
  272. package/dist/memory-worth-outcomes.d.ts +4 -2
  273. package/dist/models-json.d.ts +3 -1
  274. package/dist/namespaces/migrate.d.ts +5 -3
  275. package/dist/namespaces/migrate.js +20 -19
  276. package/dist/namespaces/principal.d.ts +3 -1
  277. package/dist/namespaces/search.d.ts +3 -1
  278. package/dist/namespaces/search.js +7 -7
  279. package/dist/namespaces/storage.d.ts +5 -3
  280. package/dist/namespaces/storage.js +10 -9
  281. package/dist/native-knowledge.d.ts +3 -1
  282. package/dist/offline-sync-impression-drain.d.ts +3 -1
  283. package/dist/operator-doctor-corpus.d.ts +3 -1
  284. package/dist/operator-doctor-corpus.js +22 -21
  285. package/dist/operator-doctor-replica.d.ts +3 -1
  286. package/dist/operator-toolkit.d.ts +6 -4
  287. package/dist/operator-toolkit.js +33 -29
  288. package/dist/orchestration/compression-guideline-coordinator.d.ts +4 -2
  289. package/dist/orchestration/maintenance.d.ts +6 -4
  290. package/dist/orchestration/maintenance.js +15 -14
  291. package/dist/{orchestrator-qJ07zI8H.d.ts → orchestrator-BshGam-o.d.ts} +45 -19
  292. package/dist/orchestrator.d.ts +11 -9
  293. package/dist/orchestrator.js +109 -104
  294. package/dist/patterns-cli.d.ts +3 -1
  295. package/dist/{pipeline-WFCxhHOn.d.ts → pipeline-D21Rs6gW.d.ts} +1 -1
  296. package/dist/policy-runtime.d.ts +3 -1
  297. package/dist/proactive-contention.d.ts +3 -1
  298. package/dist/provenance.d.ts +3 -1
  299. package/dist/{qmd-5tzkQ44y.d.ts → qmd-Dhle1P4X.d.ts} +1 -1
  300. package/dist/qmd-preflight.d.ts +4 -2
  301. package/dist/qmd-recall-cache.d.ts +3 -1
  302. package/dist/qmd.d.ts +4 -2
  303. package/dist/qmd.js +4 -4
  304. package/dist/recall-concurrency-config.d.ts +3 -1
  305. package/dist/recall-disclosure-escalation.d.ts +3 -1
  306. package/dist/recall-explain-renderer.d.ts +3 -1
  307. package/dist/recall-explain-renderer.js +3 -3
  308. package/dist/recall-memory-map.d.ts +3 -1
  309. package/dist/recall-planner-llm.d.ts +3 -1
  310. package/dist/recall-planner-llm.js +3 -3
  311. package/dist/recall-state.d.ts +7 -1
  312. package/dist/recall-state.js +1 -1
  313. package/dist/recall-tag-filter.d.ts +3 -1
  314. package/dist/recall-timings.d.ts +3 -1
  315. package/dist/recall-xray-cli.d.ts +3 -1
  316. package/dist/recall-xray-cli.js +4 -4
  317. package/dist/recall-xray-renderer.d.ts +3 -1
  318. package/dist/recall-xray-renderer.js +3 -3
  319. package/dist/recall-xray.d.ts +7 -1
  320. package/dist/recall-xray.js +2 -2
  321. package/dist/reconcile/cursor.d.ts +3 -1
  322. package/dist/reconcile/manifest.d.ts +3 -1
  323. package/dist/reconcile/plan.d.ts +3 -1
  324. package/dist/replay/normalizers/chatgpt.d.ts +3 -1
  325. package/dist/replay/normalizers/claude.d.ts +3 -1
  326. package/dist/replay/normalizers/openclaw.d.ts +3 -1
  327. package/dist/replay/normalizers/shared.d.ts +3 -1
  328. package/dist/replay/runner.d.ts +3 -1
  329. package/dist/replay/types.d.ts +3 -1
  330. package/dist/replica-divergence.d.ts +3 -1
  331. package/dist/replica-peers-config.d.ts +3 -1
  332. package/dist/resolve-auth-token.d.ts +3 -1
  333. package/dist/resume-bundles.js +6 -4
  334. package/dist/retrieval-agents.d.ts +4 -2
  335. package/dist/retrieval-tiers.d.ts +3 -1
  336. package/dist/routing/engine.d.ts +3 -1
  337. package/dist/routing/store.d.ts +3 -1
  338. package/dist/salvage-envelope.d.ts +3 -1
  339. package/dist/schemas.d.ts +116 -116
  340. package/dist/{scope-profiles--vfXPl3X.d.ts → scope-profiles-NzGPDXLn.d.ts} +1 -1
  341. package/dist/search/embed-helper.d.ts +3 -1
  342. package/dist/search/factory.d.ts +3 -1
  343. package/dist/search/factory.js +6 -6
  344. package/dist/search/index.d.ts +3 -1
  345. package/dist/search/index.js +12 -12
  346. package/dist/search/lancedb-backend.d.ts +3 -1
  347. package/dist/search/meilisearch-backend.d.ts +3 -1
  348. package/dist/search/noop-backend.d.ts +3 -1
  349. package/dist/search/orama-backend.d.ts +3 -1
  350. package/dist/search/port.d.ts +3 -1
  351. package/dist/search/remote-backend.d.ts +3 -1
  352. package/dist/{semantic-consolidation-BBDuP9nJ.d.ts → semantic-consolidation-DLDKvyy5.d.ts} +1 -1
  353. package/dist/semantic-consolidation.d.ts +4 -2
  354. package/dist/semantic-consolidation.js +11 -10
  355. package/dist/semantic-rule-promotion.js +10 -9
  356. package/dist/semantic-rule-verifier.d.ts +3 -1
  357. package/dist/semantic-rule-verifier.js +10 -9
  358. package/dist/{service-B8DtbpCM.d.ts → service-BIgVM7fL.d.ts} +2 -2
  359. package/dist/session-observer-bands.d.ts +3 -1
  360. package/dist/session-observer-state.d.ts +3 -1
  361. package/dist/shared-context/manager.d.ts +11 -9
  362. package/dist/signal.d.ts +3 -1
  363. package/dist/source-agent-qualifier.d.ts +3 -1
  364. package/dist/source-agent-qualifier.js +18 -17
  365. package/dist/{storage-BkYN1RIy.d.ts → storage-CVfGWqCI.d.ts} +7 -6
  366. package/dist/storage.d.ts +4 -2
  367. package/dist/storage.js +9 -8
  368. package/dist/summarizer.d.ts +3 -1
  369. package/dist/summary-snapshot.d.ts +3 -1
  370. package/dist/temporal-supersession.d.ts +4 -2
  371. package/dist/temporal-timeline-recall.d.ts +3 -1
  372. package/dist/temporal-validity.d.ts +3 -1
  373. package/dist/threading.d.ts +3 -1
  374. package/dist/tier-migration.d.ts +4 -2
  375. package/dist/tier-routing.d.ts +3 -1
  376. package/dist/topics.d.ts +3 -1
  377. package/dist/transcript.d.ts +3 -1
  378. package/dist/transfer/autodetect.js +1 -1
  379. package/dist/transfer/backup.js +1 -1
  380. package/dist/transfer/capsule-export.js +2 -2
  381. package/dist/transfer/types.d.ts +66 -66
  382. package/dist/trust-score-stage.d.ts +3 -1
  383. package/dist/trust-score.d.ts +3 -1
  384. package/dist/{types-u2s9j1uD.d.ts → types-CkiDUorT.d.ts} +10 -16
  385. package/dist/types.d.ts +3 -1
  386. package/dist/types.js +1 -1
  387. package/dist/utility-runtime.d.ts +3 -1
  388. package/dist/verified-recall.js +10 -9
  389. package/dist/write-envelope.d.ts +3 -1
  390. package/package.json +2 -2
  391. package/src/archive-mutation-version.ts +59 -0
  392. package/src/config.test.ts +45 -0
  393. package/src/config.ts +10 -30
  394. package/src/contradiction/contradiction.test.ts +86 -1
  395. package/src/contradiction-config.ts +92 -0
  396. package/src/graph-path-reconstruction.ts +91 -0
  397. package/src/graph-path-scoring-config.ts +55 -0
  398. package/src/graph-path-scoring.test.ts +238 -0
  399. package/src/graph-path-scoring.ts +77 -0
  400. package/src/graph.test.ts +225 -5
  401. package/src/graph.ts +72 -5
  402. package/src/orchestration/contradiction-linking-coordinator.ts +246 -76
  403. package/src/orchestration/contradiction-localization.integration.test.ts +1953 -0
  404. package/src/orchestration/extraction-anchor-snapshot.ts +83 -0
  405. package/src/orchestration/extraction-persist-deps.ts +8 -2
  406. package/src/orchestration/extraction-persist.ts +43 -34
  407. package/src/orchestration/graph-path-state-loader.test.ts +801 -0
  408. package/src/orchestration/graph-path-state-loader.ts +318 -0
  409. package/src/orchestration/graph-recall-coordinator.integration.test.ts +1163 -0
  410. package/src/orchestration/graph-recall-coordinator.ts +206 -29
  411. package/src/orchestration/graph-recall-seam.ts +19 -0
  412. package/src/orchestration/recall-internal-deps.ts +7 -13
  413. package/src/orchestration/recall-internal.ts +5 -0
  414. package/src/orchestration/recall-introspection.ts +9 -1
  415. package/src/orchestration/recall-search-pipeline.ts +8 -13
  416. package/src/orchestration/update-localization.test.ts +784 -0
  417. package/src/orchestration/update-localization.ts +173 -0
  418. package/src/orchestrator.ts +19 -18
  419. package/src/recall-state.test.ts +29 -1
  420. package/src/recall-state.ts +13 -0
  421. package/src/recall-xray-renderer.test.ts +45 -0
  422. package/src/recall-xray-renderer.ts +16 -0
  423. package/src/recall-xray.ts +13 -0
  424. package/src/storage/supersession-side-effects.ts +97 -0
  425. package/src/storage/tombstone-blocked-capture-sync.ts +13 -5
  426. package/src/storage-contract/cache-coherence.test.ts +299 -0
  427. package/src/storage.ts +86 -88
  428. package/src/types.ts +12 -15
  429. package/dist/chunk-A4QMKFFO.js.map +0 -1
  430. package/dist/chunk-BJYET5TX.js.map +0 -1
  431. package/dist/chunk-BOFVT6P3.js.map +0 -1
  432. package/dist/chunk-IK6X5QPO.js.map +0 -1
  433. package/dist/chunk-IXXABW5A.js.map +0 -1
  434. package/dist/chunk-L2DPF4DF.js.map +0 -1
  435. package/dist/chunk-LAQ33B5D.js.map +0 -1
  436. package/dist/chunk-NSGU76YO.js.map +0 -1
  437. package/dist/chunk-PDEKQWDV.js.map +0 -1
  438. /package/dist/{graph-edge-decay-Z6ENALU2.js.map → archive-mutation-version.js.map} +0 -0
  439. /package/dist/{chunk-KDG2KTWW.js.map → chunk-33SV3E43.js.map} +0 -0
  440. /package/dist/{chunk-2HGUDBRJ.js.map → chunk-37ZO6LLJ.js.map} +0 -0
  441. /package/dist/{chunk-QW3WFHKU.js.map → chunk-45BRYON6.js.map} +0 -0
  442. /package/dist/{chunk-GZICBRWK.js.map → chunk-4F6X3RXW.js.map} +0 -0
  443. /package/dist/{chunk-7BAYBYY5.js.map → chunk-4PC4PRJE.js.map} +0 -0
  444. /package/dist/{chunk-VJXY5ZCT.js.map → chunk-52UKFTUK.js.map} +0 -0
  445. /package/dist/{chunk-ESG7Z255.js.map → chunk-5UDMFE3Q.js.map} +0 -0
  446. /package/dist/{chunk-JPHEOE6Z.js.map → chunk-6MTSFX7A.js.map} +0 -0
  447. /package/dist/{chunk-M4MP34AQ.js.map → chunk-AHZOMHWN.js.map} +0 -0
  448. /package/dist/{chunk-Z3RHPGSY.js.map → chunk-AIUBBHSF.js.map} +0 -0
  449. /package/dist/{chunk-S3AYIQCU.js.map → chunk-AXIDQG5A.js.map} +0 -0
  450. /package/dist/{chunk-QN7NTKRC.js.map → chunk-AZ2QBGYB.js.map} +0 -0
  451. /package/dist/{chunk-47J4EDGR.js.map → chunk-AZM4CCAC.js.map} +0 -0
  452. /package/dist/{chunk-LEJPSMKM.js.map → chunk-C5QLI33R.js.map} +0 -0
  453. /package/dist/{chunk-SV3PSA7Y.js.map → chunk-CV6BBVYV.js.map} +0 -0
  454. /package/dist/{chunk-V7X767RT.js.map → chunk-DCZBSE4F.js.map} +0 -0
  455. /package/dist/{chunk-7PKFPLLZ.js.map → chunk-FXTYIA46.js.map} +0 -0
  456. /package/dist/{chunk-64BHFFKZ.js.map → chunk-HLYR2TQL.js.map} +0 -0
  457. /package/dist/{chunk-5C2HNJMO.js.map → chunk-J5LLX6IL.js.map} +0 -0
  458. /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
  459. /package/dist/{chunk-PLKFO2HJ.js.map → chunk-JL43BVDE.js.map} +0 -0
  460. /package/dist/{chunk-2S72LWKX.js.map → chunk-JYL3BVVZ.js.map} +0 -0
  461. /package/dist/{chunk-IPLYGWQF.js.map → chunk-KQAFEZQX.js.map} +0 -0
  462. /package/dist/{chunk-PYKKWQZ2.js.map → chunk-M7BXDKPR.js.map} +0 -0
  463. /package/dist/{chunk-Z54EM5YZ.js.map → chunk-MAXVH44E.js.map} +0 -0
  464. /package/dist/{chunk-LYMNZZIM.js.map → chunk-NIS256NW.js.map} +0 -0
  465. /package/dist/{chunk-67QG75MJ.js.map → chunk-NLK2VZYZ.js.map} +0 -0
  466. /package/dist/{chunk-JYIFGGEM.js.map → chunk-OUKBMQ2O.js.map} +0 -0
  467. /package/dist/{chunk-75XOWNRS.js.map → chunk-OYESWJ43.js.map} +0 -0
  468. /package/dist/{chunk-TS4AJ2GI.js.map → chunk-PA45XCUD.js.map} +0 -0
  469. /package/dist/{chunk-4MBPIL4V.js.map → chunk-PUSLMZBQ.js.map} +0 -0
  470. /package/dist/{chunk-RVO6TCTU.js.map → chunk-QCECHYZL.js.map} +0 -0
  471. /package/dist/{chunk-GFIKG4FR.js.map → chunk-QGR6OVGT.js.map} +0 -0
  472. /package/dist/{chunk-HNZKVLAN.js.map → chunk-S354QO4R.js.map} +0 -0
  473. /package/dist/{chunk-NO37MR45.js.map → chunk-SOEN6KCF.js.map} +0 -0
  474. /package/dist/{chunk-FXX6RQYC.js.map → chunk-TPTECFWR.js.map} +0 -0
  475. /package/dist/{chunk-GDDGZVFI.js.map → chunk-UEVLYVNW.js.map} +0 -0
  476. /package/dist/{chunk-YQ4IQ2GA.js.map → chunk-UT5BV5OG.js.map} +0 -0
  477. /package/dist/{chunk-LNTUXKBT.js.map → chunk-UYYW2KPU.js.map} +0 -0
  478. /package/dist/{chunk-QSYL5CXK.js.map → chunk-VKXWK44Q.js.map} +0 -0
  479. /package/dist/{chunk-KMWYZTIE.js.map → chunk-VMMQDEN5.js.map} +0 -0
  480. /package/dist/{chunk-JB6D7EFB.js.map → chunk-VVG2BOKA.js.map} +0 -0
  481. /package/dist/{chunk-V5H6OJCR.js.map → chunk-W5XVQFLM.js.map} +0 -0
  482. /package/dist/{chunk-OIOZ6MOP.js.map → chunk-W6FNX2LP.js.map} +0 -0
  483. /package/dist/{chunk-6QPHF6RR.js.map → chunk-WQM326GS.js.map} +0 -0
  484. /package/dist/{chunk-TQFHQPWD.js.map → chunk-XADV5IZ3.js.map} +0 -0
  485. /package/dist/{chunk-DATDLJAO.js.map → chunk-XAPMEU3K.js.map} +0 -0
  486. /package/dist/{chunk-N3LL5NEU.js.map → chunk-ZMYDYNI4.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readAllEdges
3
- } from "./chunk-L2DPF4DF.js";
3
+ } from "./chunk-BIJAWZYW.js";
4
4
  import {
5
5
  readEdgeConfidence
6
6
  } from "./chunk-2LSZVONP.js";
@@ -162,4 +162,4 @@ export {
162
162
  parseGraphSnapshotSince,
163
163
  buildGraphSnapshot
164
164
  };
165
- //# sourceMappingURL=chunk-JYIFGGEM.js.map
165
+ //# sourceMappingURL=chunk-OUKBMQ2O.js.map
@@ -9,14 +9,14 @@ import {
9
9
  } from "./chunk-666A3MOW.js";
10
10
  import {
11
11
  rebuildMemoryLifecycleLedger
12
- } from "./chunk-GDDGZVFI.js";
12
+ } from "./chunk-UEVLYVNW.js";
13
13
  import {
14
14
  planNamespaceMaintenance,
15
15
  runNamespaceMaintenanceBatchPlan
16
- } from "./chunk-7BAYBYY5.js";
16
+ } from "./chunk-4PC4PRJE.js";
17
17
  import {
18
18
  rebuildMemoryProjection
19
- } from "./chunk-Z3RHPGSY.js";
19
+ } from "./chunk-AIUBBHSF.js";
20
20
  import {
21
21
  activityDayWindow,
22
22
  activityDigestPath,
@@ -1714,4 +1714,4 @@ export {
1714
1714
  ActivitySyncScheduler,
1715
1715
  MaintenanceScheduler
1716
1716
  };
1717
- //# sourceMappingURL=chunk-75XOWNRS.js.map
1717
+ //# sourceMappingURL=chunk-OYESWJ43.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  namespaceCollectionName
3
- } from "./chunk-KMWYZTIE.js";
3
+ } from "./chunk-VMMQDEN5.js";
4
4
  import {
5
5
  NamespaceStorageRouter
6
- } from "./chunk-6QPHF6RR.js";
6
+ } from "./chunk-WQM326GS.js";
7
7
  import {
8
8
  namespaceIdentityFromToken,
9
9
  namespaceIdentityToken
@@ -213,4 +213,4 @@ export {
213
213
  verifyNamespaces,
214
214
  runNamespaceMigration
215
215
  };
216
- //# sourceMappingURL=chunk-TS4AJ2GI.js.map
216
+ //# sourceMappingURL=chunk-PA45XCUD.js.map
@@ -36,6 +36,12 @@ function clampGraphRecallExpandedEntries(entries, maxEntries = 64) {
36
36
  if (typeof item.edgeConfidence === "number" && Number.isFinite(item.edgeConfidence)) {
37
37
  out.edgeConfidence = Math.min(1, Math.max(0, item.edgeConfidence));
38
38
  }
39
+ if (Array.isArray(item.pathNodeIds) && item.pathNodeIds.every((value) => typeof value === "string")) {
40
+ out.pathNodeIds = item.pathNodeIds.slice(0, 64);
41
+ }
42
+ if (typeof item.pathPenaltyApplied === "boolean") {
43
+ out.pathPenaltyApplied = item.pathPenaltyApplied;
44
+ }
39
45
  return out;
40
46
  }).filter((item) => item.path.length > 0 && item.namespace.length > 0).slice(0, limit);
41
47
  }
@@ -633,4 +639,4 @@ export {
633
639
  TierMigrationStatusStore,
634
640
  RecallHandleHistoryStore
635
641
  };
636
- //# sourceMappingURL=chunk-BJYET5TX.js.map
642
+ //# sourceMappingURL=chunk-PRDJMOST.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recall-state.ts"],"sourcesContent":["import { appendFile, mkdir, readFile, rename, rm, stat, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { log } from \"./logger.js\";\nimport { writeFileAtomically } from \"./maintenance/atomic-file.js\";\nimport { isErrnoCode } from \"./utils/errno.js\";\nimport { withHeldFileLock, type HeldFileLockOptions } from \"./utils/serialize-mutations.js\";\nimport { ensureContainedSpillDir, listContainedSpillFiles } from \"./utils/path-containment.js\";\nimport type { SearchDegradation } from \"./search/port.js\";\nimport type {\n IdentityInjectionMode,\n RecallPlanMode,\n RecallTierExplain,\n} from \"./types.js\";\n\nexport interface LastRecallBudgetSummary {\n requestedTopK?: number;\n appliedTopK: number;\n recallBudgetChars: number;\n maxMemoryTokens: number;\n qmdFetchLimit?: number;\n qmdHybridFetchLimit?: number;\n finalContextChars?: number;\n truncated?: boolean;\n includedSections?: string[];\n omittedSections?: string[];\n includedMemoryIds?: string[];\n includedMemoryPaths?: string[];\n includedMemoryNamespaces?: Array<string | undefined>;\n omittedMemoryIds?: string[];\n}\n\nexport interface LastRecallSnapshot {\n sessionKey: string;\n recordedAt: string;\n queryHash: string;\n queryLen: number;\n memoryIds: string[];\n namespace?: string;\n recallNamespaces?: string[];\n traceId?: string;\n plannerMode?: RecallPlanMode;\n requestedMode?: RecallPlanMode;\n source?: string;\n fallbackUsed?: boolean;\n sourcesUsed?: string[];\n budgetsApplied?: LastRecallBudgetSummary;\n latencyMs?: number;\n resultPaths?: string[];\n resultNamespaces?: Array<string | undefined>;\n policyVersion?: string;\n identityInjectionMode?: IdentityInjectionMode | \"none\";\n identityInjectedChars?: number;\n identityInjectionTruncated?: boolean;\n /**\n * Collision-safe write nonce. Random UUID set on every `record()`\n * call so the observation-mode direct-answer hook can detect stale\n * snapshots and avoid annotating a snapshot that a subsequent recall\n * already replaced (issue #518).\n */\n writeNonce?: string;\n /**\n * Optional tier-level explanation of how recall was served\n * (issue #518). Populated by orchestrator call sites that can\n * identify a concrete tier; surfaces expose the block via\n * `engram query --explain`, the `?explain=1` HTTP flag, and the\n * `remnic_recall_explain` MCP tool. Orthogonal to the existing\n * graph-path `recallExplain` operation.\n */\n tierExplain?: RecallTierExplain;\n /**\n * Backend degradations observed while serving this recall (issue #1536).\n * Present only when a search backend reported unavailable/loading/timeout\n * during the recall — distinguishing \"no matches\" from \"backend could not\n * answer\" (CLAUDE.md rule 34). Deliberately independent of `tierExplain`,\n * which is gated behind `recallDirectAnswerEnabled`.\n */\n backendDegradations?: SearchDegradation[];\n}\n\nexport interface GraphRecallExpandedEntry {\n path: string;\n score: number;\n namespace: string;\n seed: string;\n hopDepth: number;\n decayedWeight: number;\n graphType: \"entity\" | \"time\" | \"causal\";\n /**\n * Issue #681 PR 3/3 — confidence of the edge that produced this entry's\n * recorded provenance (strongest edge along the chosen entry path).\n * Range `[0, 1]`. Optional so persisted snapshots from older builds\n * round-trip through `clampGraphRecallExpandedEntries` without dropping.\n */\n edgeConfidence?: number;\n /** Graph-relative activation path node ids when provenance is enabled. */\n pathNodeIds?: string[];\n /** True when a graph path penalty was applied. */\n pathPenaltyApplied?: boolean;\n}\n\nexport function clampGraphRecallExpandedEntries(\n entries: unknown,\n maxEntries: number = 64,\n): GraphRecallExpandedEntry[] {\n const limit = Math.max(1, Math.floor(maxEntries));\n if (!Array.isArray(entries)) return [];\n return entries\n .filter((item): item is Record<string, unknown> => !!item && typeof item === \"object\")\n .map((item) => {\n const graphType: \"entity\" | \"time\" | \"causal\" =\n item.graphType === \"entity\" || item.graphType === \"time\" || item.graphType === \"causal\"\n ? item.graphType\n : \"entity\";\n const out: GraphRecallExpandedEntry = {\n path: typeof item.path === \"string\" ? item.path : \"\",\n score: typeof item.score === \"number\" && Number.isFinite(item.score) ? item.score : 0,\n namespace: typeof item.namespace === \"string\" ? item.namespace : \"\",\n seed: typeof item.seed === \"string\" ? item.seed : \"\",\n hopDepth:\n typeof item.hopDepth === \"number\" && Number.isFinite(item.hopDepth)\n ? Math.max(0, Math.floor(item.hopDepth))\n : 0,\n decayedWeight:\n typeof item.decayedWeight === \"number\" && Number.isFinite(item.decayedWeight)\n ? Math.max(0, item.decayedWeight)\n : 0,\n graphType,\n };\n // Issue #681 PR 3/3: clamp `edgeConfidence` into [0, 1] when present.\n // Older snapshots without the field round-trip cleanly via the\n // optional shape; legacy callers always rendered as 1.0.\n if (\n typeof item.edgeConfidence === \"number\" &&\n Number.isFinite(item.edgeConfidence)\n ) {\n out.edgeConfidence = Math.min(1, Math.max(0, item.edgeConfidence));\n }\n if (\n Array.isArray(item.pathNodeIds) &&\n item.pathNodeIds.every((value): value is string => typeof value === \"string\")\n ) {\n out.pathNodeIds = item.pathNodeIds.slice(0, 64);\n }\n if (typeof item.pathPenaltyApplied === \"boolean\") {\n out.pathPenaltyApplied = item.pathPenaltyApplied;\n }\n return out;\n })\n .filter((item) => item.path.length > 0 && item.namespace.length > 0)\n .slice(0, limit);\n}\n\ntype LastRecallState = Record<string, LastRecallSnapshot>;\ntype StateFileWriter = (filePath: string, content: string) => Promise<void>;\n\n/**\n * Deep-copy a RecallTierExplain block. Used by both the write path\n * (so caller mutation after `record()` cannot tear the persisted\n * snapshot) and the read path (so caller mutation after `get()` /\n * `getMostRecent()` cannot tear the in-memory store).\n *\n * Uses structuredClone so future additions to RecallTierExplain do\n * not silently share references through hand-enumerated fields —\n * matching the pattern used elsewhere in the codebase (e.g.,\n * qmd-recall-cache.ts). The payload is pure JSON-shaped data, so\n * structuredClone is both safe and complete here.\n */\n/**\n * Stale-snapshot identity guard shared by the annotate methods (issue #518):\n * a writeNonce match wins outright; otherwise traceId (when expected) and\n * recordedAt are compared. Extracted so every annotation surface applies\n * identical guards (CLAUDE.md rule 22).\n */\nfunction snapshotMatchesExpectedIdentity(\n current: LastRecallSnapshot,\n expected?: { writeNonce?: string; traceId?: string; recordedAt?: string },\n): boolean {\n if (!expected) return true;\n if (typeof expected.writeNonce === \"string\" && expected.writeNonce.length > 0) {\n return current.writeNonce === expected.writeNonce;\n }\n const hasExpectedTraceId =\n typeof expected.traceId === \"string\" && expected.traceId.length > 0;\n if (hasExpectedTraceId) {\n return current.traceId === expected.traceId;\n }\n if (expected.recordedAt !== undefined) {\n return current.recordedAt === expected.recordedAt;\n }\n return true;\n}\n\nfunction cloneTierExplain(\n tierExplain: RecallTierExplain | undefined,\n): RecallTierExplain | undefined {\n if (!tierExplain) return undefined;\n return structuredClone(tierExplain);\n}\n\n/**\n * Deep-copy a LastRecallSnapshot so callers that receive it cannot\n * mutate the store's internal state through mutable array/object\n * fields. Same structuredClone rationale as cloneTierExplain above.\n */\nfunction cloneLastRecallSnapshot(\n snapshot: LastRecallSnapshot | null,\n): LastRecallSnapshot | null {\n if (!snapshot) return null;\n return structuredClone(snapshot);\n}\n\nexport interface TierMigrationCycleSummary {\n trigger: \"extraction\" | \"maintenance\" | \"manual\";\n scanned: number;\n migrated: number;\n promoted: number;\n demoted: number;\n limit: number;\n dryRun: boolean;\n skipped?: string;\n errorCount?: number;\n}\n\nexport interface TierMigrationStatusSnapshot {\n updatedAt: string;\n lastCycle: TierMigrationCycleSummary | null;\n totals: {\n cycles: number;\n scanned: number;\n migrated: number;\n promoted: number;\n demoted: number;\n errors: number;\n };\n}\n\nconst DEFAULT_TIER_MIGRATION_STATUS: TierMigrationStatusSnapshot = {\n updatedAt: new Date(0).toISOString(),\n lastCycle: null,\n totals: {\n cycles: 0,\n scanned: 0,\n migrated: 0,\n promoted: 0,\n demoted: 0,\n errors: 0,\n },\n};\n\n/**\n * Suffix marking an impression spill CLAIMED for commit but not yet deleted.\n * The crash-safe drain renames `<uuid>.jsonl` -> `<uuid>.jsonl.claimed` before\n * committing its rows, mirroring the lifecycle ledger's claim/commit protocol\n * (#2033).\n */\nconst CLAIMED_IMPRESSION_SPILL_SUFFIX = \".claimed\";\n\n/**\n * Outcome of {@link LastRecallStore.drainPendingImpressions}. Distinguishes a\n * completed drain (or nothing pending) from a DEFERRED drain that left durable\n * spills in the offline-sync-EXCLUDED queue, so the caller can tell the\n * data-safe cases apart from the one where an offline-sync snapshot would\n * silently omit recorded impressions (#2033).\n */\nexport interface DrainPendingImpressionsResult {\n /** Pending spill rows were folded into the synced active file this call. */\n folded: boolean;\n /**\n * Durable spill rows remain in the offline-sync-EXCLUDED pending queue after\n * this drain: the rotation lock could not be acquired, a spill could not be\n * claimed, or the drain-only fold left rows that would only fit by rotating\n * into a sync-excluded archive. A snapshot taken now would silently omit them\n * (#2033): the caller MUST treat the active file as INCOMPLETE and retry or\n * abort the snapshot rather than report success. `false` means the active file\n * is complete — every pending row was folded or nothing was pending.\n */\n pendingDeferred: boolean;\n}\n\nexport class LastRecallStore {\n private readonly statePath: string;\n private readonly impressionsPath: string;\n private readonly writeStateFile: StateFileWriter;\n private readonly impressionsRotateBytes: number;\n private readonly impressionsRotateKeep: number;\n // Lock-acquisition timing for the cross-process impression rotation lock.\n // Only tests set this (small maxWaitMs) to force the acquisition-timeout\n // (`acquired=false`) branch deterministically; production uses the defaults.\n private readonly impressionsLockOptions?: Pick<HeldFileLockOptions, \"maxWaitMs\" | \"pollMs\">;\n private state: LastRecallState = {};\n private stateWriteChain: Promise<void> = Promise.resolve();\n // Serializes the rotate-then-append critical section so concurrent record()\n // calls never interleave rotation renames with each other's appends (#1910).\n private impressionsWriteChain: Promise<void> = Promise.resolve();\n\n constructor(\n memoryDir: string,\n options: {\n writeStateFile?: StateFileWriter;\n impressionsRotateBytes?: number;\n impressionsRotateKeep?: number;\n impressionsLockOptions?: Pick<HeldFileLockOptions, \"maxWaitMs\" | \"pollMs\">;\n } = {},\n ) {\n this.statePath = path.join(memoryDir, \"state\", \"last_recall.json\");\n this.impressionsPath = path.join(memoryDir, \"state\", \"recall_impressions.jsonl\");\n // 0 disables rotation (never coerced to a default); keep floors at 1.\n this.impressionsRotateBytes = Math.max(0, Math.floor(options.impressionsRotateBytes ?? 0));\n this.impressionsRotateKeep = Math.max(1, Math.floor(options.impressionsRotateKeep ?? 5));\n this.impressionsLockOptions = options.impressionsLockOptions;\n this.writeStateFile =\n options.writeStateFile ??\n (async (filePath, content) => {\n await writeFileAtomically(filePath, content);\n });\n }\n\n async load(): Promise<void> {\n try {\n const raw = await readFile(this.statePath, \"utf-8\");\n const parsed = JSON.parse(raw) as LastRecallState;\n if (parsed && typeof parsed === \"object\") this.state = parsed;\n } catch {\n this.state = {};\n }\n }\n\n get(sessionKey: string): LastRecallSnapshot | null {\n // Defensive copy: callers must not be able to mutate internal state\n // by reaching into array/object fields on the returned snapshot.\n return cloneLastRecallSnapshot(this.state[sessionKey] ?? null);\n }\n\n getMostRecent(): LastRecallSnapshot | null {\n const snapshots = Object.values(this.state);\n if (snapshots.length === 0) return null;\n // Secondary key on sessionKey keeps the sort stable when two\n // snapshots share a recordedAt timestamp (CLAUDE.md rule 19).\n snapshots.sort((a, b) => {\n const byTime = b.recordedAt.localeCompare(a.recordedAt);\n if (byTime !== 0) return byTime;\n return a.sessionKey.localeCompare(b.sessionKey);\n });\n return cloneLastRecallSnapshot(snapshots[0] ?? null);\n }\n\n /**\n * Persist last-recall snapshot and append an impression log entry.\n * Does not store raw query text; uses a stable hash for correlation.\n */\n async record(opts: {\n sessionKey: string;\n query: string;\n memoryIds: string[];\n namespace?: string;\n recallNamespaces?: string[];\n traceId?: string;\n plannerMode?: RecallPlanMode;\n requestedMode?: RecallPlanMode;\n source?: string;\n fallbackUsed?: boolean;\n sourcesUsed?: string[];\n budgetsApplied?: LastRecallBudgetSummary;\n latencyMs?: number;\n resultPaths?: string[];\n resultNamespaces?: Array<string | undefined>;\n policyVersion?: string;\n appendImpression?: boolean;\n identityInjection?: {\n mode: IdentityInjectionMode | \"none\";\n injectedChars: number;\n truncated: boolean;\n };\n /**\n * Per-tier explain annotation (issue #518). When supplied, the\n * snapshot carries it so downstream surfaces (CLI / HTTP / MCP)\n * can render which retrieval tier served the query.\n */\n tierExplain?: RecallTierExplain;\n /**\n * Backend degradations observed while serving this recall (issue #1536).\n * Passed at record time so the published snapshot is born annotated —\n * a post-record annotation would leave a window where readers see the\n * snapshot without them (codex review on #1544).\n */\n backendDegradations?: SearchDegradation[];\n }): Promise<void> {\n const now = new Date().toISOString();\n const queryHash = createHash(\"sha256\").update(opts.query).digest(\"hex\");\n\n // Build the snapshot from opts, then deep-copy it via\n // cloneLastRecallSnapshot so caller arrays/objects passed in\n // `opts` cannot retain a live reference to the persisted state and\n // tear it after `record()` returns.\n const liveSnapshot: LastRecallSnapshot = {\n sessionKey: opts.sessionKey,\n recordedAt: now,\n queryHash,\n writeNonce: randomUUID(),\n queryLen: opts.query.length,\n memoryIds: opts.memoryIds,\n namespace: opts.namespace,\n recallNamespaces: opts.recallNamespaces,\n traceId: opts.traceId,\n plannerMode: opts.plannerMode,\n requestedMode: opts.requestedMode,\n source: opts.source,\n fallbackUsed: opts.fallbackUsed,\n sourcesUsed: opts.sourcesUsed,\n budgetsApplied: opts.budgetsApplied,\n latencyMs: opts.latencyMs,\n resultPaths: opts.resultPaths,\n resultNamespaces: opts.resultNamespaces,\n policyVersion: opts.policyVersion,\n identityInjectionMode: opts.identityInjection?.mode,\n identityInjectedChars: opts.identityInjection?.injectedChars,\n identityInjectionTruncated: opts.identityInjection?.truncated,\n tierExplain: opts.tierExplain,\n backendDegradations:\n opts.backendDegradations && opts.backendDegradations.length > 0\n ? opts.backendDegradations\n : undefined,\n };\n // `cloneLastRecallSnapshot` handles `null` but that never applies\n // at this call site — the non-null assertion keeps the type\n // checker honest.\n const snapshot = cloneLastRecallSnapshot(liveSnapshot)!;\n\n this.state[opts.sessionKey] = snapshot;\n\n // Keep the state bounded; the impression log is append-only.\n const keys = Object.keys(this.state);\n if (keys.length > 50) {\n const ordered = keys\n .map((k) => ({ k, at: this.state[k]?.recordedAt ?? \"\" }))\n .sort((a, b) => b.at.localeCompare(a.at));\n for (const doomed of ordered.slice(50)) {\n delete this.state[doomed.k];\n }\n }\n\n try {\n await this.flushState();\n } catch (err) {\n log.debug(`last recall store write failed: ${err}`);\n }\n\n if (opts.appendImpression !== false) {\n const line = JSON.stringify(snapshot) + \"\\n\";\n // Chain onto the previous impression write so rotation + append run as one\n // serialized critical section — concurrent record() calls can never\n // interleave a rename with another call's append (#1910). `.catch` before\n // the link keeps a prior failure from poisoning the chain.\n const chained = this.impressionsWriteChain\n .catch(() => {})\n .then(() => this.appendImpressionSerialized(line));\n this.impressionsWriteChain = chained;\n try {\n await chained;\n } catch (err) {\n log.debug(`recall impressions append failed: ${err}`);\n }\n }\n }\n\n /**\n * Drain pending spills, rotate (best-effort, sized to the projected payload),\n * then append one impression line under ONE shared cross-process lock (#1910,\n * #2033). Both the archive-shift renames AND the append run inside the same\n * held lock so a peer process holding the lock can never rename the active\n * inode to `.1` between our open and write — which would land the impression in\n * a rotated archive that offline-sync excludes, silently dropping it from\n * active-state/sync consumers.\n *\n * On lock-acquisition timeout the impression is NEVER written to the active\n * file unlocked (#2033): a peer holding the lock is likely mid-rotation, so an\n * unlocked append can land in the inode it is about to rename to `.1` — the\n * exact silent-drop this lock prevents. Instead the row is spilled to a\n * durable node-local per-event file under `<impressions>.pending.d/` and\n * folded back into the active file by the next append that DOES hold the lock,\n * preserving durability without racing rotation. Rotation failure under the\n * held lock is logged and the append still runs. Callers invoke this only\n * through `impressionsWriteChain` so the sequence is serialized in-process.\n */\n private get impressionsPendingDir(): string {\n return `${this.impressionsPath}.pending.d`;\n }\n\n private async spillImpression(line: string): Promise<void> {\n await ensureContainedSpillDir(this.impressionsPendingDir);\n // Write to a temp name the drain's `.jsonl` lister ignores, then atomically\n // rename to the final `<uuid>.jsonl` (#2033). writeFile creates the path\n // before its bytes are durable, so writing straight to the final name lets a\n // concurrent lock holder's drain read/rename/delete a partial spill and\n // append malformed JSON — permanently losing that impression. The temp lives\n // in the SAME contained dir, so the rename is atomic and stays symlink-safe.\n const id = randomUUID();\n const tempPath = path.join(this.impressionsPendingDir, `${id}.jsonl.tmp`);\n const finalPath = path.join(this.impressionsPendingDir, `${id}.jsonl`);\n await writeFile(tempPath, line, \"utf-8\");\n await rename(tempPath, finalPath);\n }\n\n /**\n * Recover a claim a crashed drain left mid-flight (#2033): the crash-safe\n * drain renames `<uuid>.jsonl` -> `<uuid>.jsonl.claimed` BEFORE committing, so\n * a `.claimed` file with no matching commit is the ONLY durable copy of those\n * rows. Rename each orphan back to `<uuid>.jsonl` so it re-enters the normal\n * collect+claim+commit flow. MUST run under the held rotation lock, before\n * collecting. listContainedSpillFiles skips symlinked/escaping entries.\n */\n private async recoverOrphanedImpressionClaims(): Promise<void> {\n const orphans = await listContainedSpillFiles(\n this.impressionsPendingDir,\n `.jsonl${CLAIMED_IMPRESSION_SPILL_SUFFIX}`,\n );\n for (const claimedPath of orphans) {\n const original = claimedPath.slice(0, -CLAIMED_IMPRESSION_SPILL_SUFFIX.length);\n await rename(claimedPath, original).catch(() => undefined);\n }\n }\n\n /**\n * Recover a crash-orphaned spill temp after its write completed but before\n * the atomic `.tmp` → `.jsonl` rename. Valid JSONL temps are promoted back\n * into the normal queue; incomplete temps remain pending and keep offline sync\n * deferred rather than being discarded.\n */\n private async recoverOrphanedImpressionTemps(): Promise<void> {\n const temps = await listContainedSpillFiles(this.impressionsPendingDir, \".jsonl.tmp\");\n for (const tempPath of temps) {\n let content: string;\n try {\n content = await readFile(tempPath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n if (line.trim().length > 0) JSON.parse(line);\n }\n } catch {\n continue;\n }\n const finalPath = tempPath.slice(0, -\".tmp\".length);\n await rename(tempPath, finalPath).catch(() => undefined);\n }\n }\n\n private async appendImpressionSerialized(line: string): Promise<void> {\n await mkdir(path.dirname(this.impressionsPath), { recursive: true });\n // Every active-file write goes under the shared cross-process rotation lock,\n // even when THIS writer's local rotation is disabled (`impressionsRotateBytes\n // <= 0`). A peer process with rotation enabled shares this lock and may\n // rename the active inode to `.1` at any moment, so an unlocked append could\n // land in an offline-sync-excluded archive and be silently dropped (#2033).\n // Only the local rotate DECISION is disabled — the bounded fold skips\n // rotation when this writer's limit is 0 — the lock itself is not.\n const lockPath = `${this.impressionsPath}.lock`;\n await withHeldFileLock(\n lockPath,\n { staleMs: 30_000, ...this.impressionsLockOptions },\n async (acquired) => {\n // withHeldFileLock falls back to task(false) when it cannot acquire the\n // lock within the budget. Do NOT append to the active file unlocked: a\n // peer holding the lock may be mid-rotation, so an unlocked append could\n // land in the inode it renames to `.1` (an offline-sync-excluded\n // archive), silently dropping the impression. Spill it to the durable\n // pending queue instead — the next lock holder folds it back in (#2033).\n if (!acquired) {\n log.debug(\"recall impressions rotation lock not acquired; spilling impression to the durable pending queue\");\n await this.spillImpression(line);\n return;\n }\n await this.foldPendingImpressionsAndAppend(line);\n },\n );\n }\n\n /**\n * Fold any durable pending recall-impression spills into the synced active\n * `recall_impressions.jsonl` WITHOUT recording a new impression (#2033).\n *\n * The pending spill dir is offline-sync-excluded, so an impression that a\n * lock-timed-out `record()` spilled there is absent from an offline-sync\n * snapshot and lost if this node is discarded before the next `record()`\n * folds it back. Offline sync calls this before building a snapshot so a\n * recorded impression always reaches the remote. Fast no-op — no lock and no\n * dir/lock-file creation — when nothing is pending.\n *\n * Returns a {@link DrainPendingImpressionsResult}: `folded` is true when rows\n * were folded into the active file; `pendingDeferred` is true when durable\n * spills still remain in the offline-sync-EXCLUDED queue after this call —\n * because the rotation lock could not be acquired, a spill could not be\n * claimed, or the drain-only fold deferred rows that would only fit by\n * rotating into a sync-excluded archive (#2033). A snapshot taken while\n * `pendingDeferred` is true would silently drop those rows, so the caller MUST\n * NOT report a clean snapshot — it either retries or aborts.\n */\n async drainPendingImpressions(): Promise<DrainPendingImpressionsResult> {\n if (!(await this.pendingSpillsRemain())) {\n return { folded: false, pendingDeferred: false };\n }\n const lockPath = `${this.impressionsPath}.lock`;\n let folded = false;\n let acquiredLock = false;\n let spillsRemain = false;\n await withHeldFileLock(\n lockPath,\n { staleMs: 30_000, ...this.impressionsLockOptions },\n async (acquired) => {\n // A peer holds the lock (mid-rotation or mid-append). We MUST NOT touch\n // the active file unlocked (#2033), so the spills stay in the\n // offline-sync-EXCLUDED pending queue. Report the drain as DEFERRED\n // (pendingDeferred=true) so the caller does not build a snapshot that\n // silently omits them; the next lock holder — or a caller retry — folds\n // them in.\n if (!acquired) return;\n acquiredLock = true;\n folded = await this.foldPendingImpressionsAndAppend(null);\n // Even WITH the lock, a spill the fold could not claim or a `.claimed`\n // orphan a failed commit left behind keeps durable rows in the excluded\n // queue. Treat that as an incomplete drain so the caller retries/aborts\n // rather than snapshotting an active file that omits them (#2033).\n spillsRemain = await this.pendingSpillsRemain();\n },\n );\n return { folded, pendingDeferred: !acquiredLock || spillsRemain };\n }\n\n /**\n * Claim/commit fold of durable pending impression spills into the active file\n * under the held rotation lock (#2033). `line` is a new impression to append\n * after the drained rows, or null for a drain-only pre-sync fold.\n *\n * Each spill is folded as an INDEPENDENT claim -> append -> finalize unit\n * rather than materializing the whole queue as one joined payload and\n * splitting it in a single synchronous pass (#2033). A multi-hundred-MB drain\n * done as one join/split blocks the event loop long enough for\n * `withHeldFileLock`'s ownership-refresh timer to miss its window, so a peer\n * stale-breaks the 30s lock and rotates/appends concurrently — defeating the\n * rotation lock this drain relies on. The per-file `await`s keep the payload\n * bounded to one spill at a time and let the refresh timer fire between files.\n *\n * The DRAIN-ONLY path (`line === null`, run before an offline-sync snapshot)\n * NEVER rotates a drained row into an archive. `recall_impressions.jsonl.*`\n * is offline-sync-EXCLUDED, so rotating a just-drained row into `.1`/`.2` and\n * then deleting its spill would leave that row only in a path sync omits —\n * silently lost when the node is discarded after a \"successful\" sync (#2033).\n * It folds only the rows that fit the synced active file under the cap and\n * leaves the rest durable in the pending queue; {@link drainPendingImpressions}\n * then reports the drain DEFERRED so the caller retries or aborts. The\n * record() path (`line !== null`) keeps rotating normally — its archives stay\n * on a live node that is not being discarded.\n *\n * Recovers any crash-orphaned claim first. Each impression carries a unique\n * writeNonce, so a crash-recovered re-commit is a collapsible duplicate, never\n * a lost row. listContainedSpillFiles rejects symlinked/escaping entries; a\n * spill that cannot be claimed is skipped this pass. An append failure rolls\n * that spill's claim back so the row retries on the next lock holder's drain.\n * Returns true when the active file was written. MUST run under the held\n * rotation lock.\n */\n private async foldPendingImpressionsAndAppend(line: string | null): Promise<boolean> {\n await this.recoverOrphanedImpressionTemps();\n await this.recoverOrphanedImpressionClaims();\n const spillFiles = await listContainedSpillFiles(this.impressionsPendingDir);\n const drainOnly = line === null;\n const cap = this.impressionsRotateBytes;\n let activeSize = 0;\n if (cap > 0) {\n try {\n activeSize = (await stat(this.impressionsPath)).size;\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n }\n }\n let wrote = false;\n for (const filePath of spillFiles) {\n const content = await readFile(filePath, \"utf-8\");\n const unit = content.length === 0 || content.endsWith(\"\\n\") ? content : `${content}\\n`;\n const unitBytes = Buffer.byteLength(unit, \"utf-8\");\n // Drain-only fold must never push a drained row into a sync-excluded\n // archive (#2033). Once the active file cannot take this spill under the\n // cap without rotating, stop: the remaining spills stay durable in the\n // pending queue and the drain reports itself deferred. A lone spill larger\n // than the cap on an empty active file is still folded whole (it cannot be\n // split without corrupting JSONL) so it reaches sync rather than stranding.\n if (drainOnly && cap > 0 && activeSize > 0 && activeSize + unitBytes >= cap) break;\n const claimedPath = `${filePath}${CLAIMED_IMPRESSION_SPILL_SUFFIX}`;\n try {\n await rename(filePath, claimedPath);\n } catch {\n continue; // could not claim → do not commit; a later drain retries it.\n }\n try {\n activeSize = await this.appendImpressionUnit(unit, activeSize, drainOnly);\n } catch (err) {\n // Commit failed: roll this spill's claim back so it retries on the next\n // lock holder's drain instead of being lost.\n await rename(claimedPath, filePath).catch(() => undefined);\n throw err;\n }\n // Commit is durable: delete the claimed file. A delete that fails leaves a\n // `.claimed` orphan the next drain recovers and re-commits — a\n // writeNonce-collapsible duplicate, never a lost row (#2033).\n await unlink(claimedPath).catch(() => undefined);\n wrote = true;\n }\n if (line !== null) {\n activeSize = await this.appendImpressionUnit(line, activeSize, false);\n wrote = true;\n }\n return wrote;\n }\n\n /**\n * Append one whole JSONL unit (a claimed spill's rows or a fresh impression\n * line) to the active impressions file, returning the resulting active-file\n * byte size. On the rotating (record) path, when the unit would push the\n * current active generation to/over `impressionsRotateBytes`, the full active\n * file is rotated aside FIRST so no generation exceeds the cap (#2033); a lone\n * unit larger than the cap on an empty active file is written whole because\n * splitting a JSONL row would corrupt it. `drainOnly` and disabled rotation\n * (`impressionsRotateBytes <= 0`) both skip rotation — drain-only defers its\n * overflow in {@link foldPendingImpressionsAndAppend} rather than rotating into\n * a sync-excluded archive. A rotation error is logged and the append still\n * runs so a row is never lost; an append failure propagates so the caller\n * rolls its claim back. MUST run under the held rotation lock.\n */\n private async appendImpressionUnit(\n unit: string,\n activeSize: number,\n drainOnly: boolean,\n ): Promise<number> {\n const cap = this.impressionsRotateBytes;\n const unitBytes = Buffer.byteLength(unit, \"utf-8\");\n if (cap > 0 && !drainOnly && activeSize > 0 && activeSize + unitBytes >= cap) {\n try {\n await this.shiftImpressionArchives();\n activeSize = 0;\n } catch (err) {\n log.debug(`recall impressions rotation failed (append preserved): ${err}`);\n }\n }\n await appendFile(this.impressionsPath, unit, \"utf-8\");\n return activeSize + unitBytes;\n }\n\n /**\n * True when any live `<uuid>.jsonl` spill, crash-orphaned `.claimed` file,\n * or crash-orphaned `.jsonl.tmp` spill remains in the offline-sync-EXCLUDED\n * pending queue (#2033). After a drain acquires the rotation lock, a spill\n * the fold could not claim (rename race / read-only dir) or a `.claimed`\n * orphan a failed commit left behind means durable rows are STILL excluded\n * from a snapshot — the drain must report itself INCOMPLETE so the caller\n * retries or aborts rather than snapshotting an active file that omits them.\n */\n private async pendingSpillsRemain(): Promise<boolean> {\n if ((await listContainedSpillFiles(this.impressionsPendingDir, \".jsonl.tmp\")).length > 0) {\n return true;\n }\n const liveSpills = await listContainedSpillFiles(this.impressionsPendingDir);\n if (liveSpills.length > 0) return true;\n const claimed = await listContainedSpillFiles(\n this.impressionsPendingDir,\n `.jsonl${CLAIMED_IMPRESSION_SPILL_SUFFIX}`,\n );\n return claimed.length > 0;\n }\n\n /**\n * Shift `.1..N` archives down one slot and move the active file to `.1`,\n * dropping anything beyond `keep`. Callers invoke this only while holding the\n * cross-process rotation lock (issue #1910).\n */\n private async shiftImpressionArchives(): Promise<void> {\n const keep = this.impressionsRotateKeep;\n // Drop the archive that would fall off the end, then shift down: .(keep-1)\n // -> .keep, ..., .1 -> .2, active -> .1.\n try {\n await rm(`${this.impressionsPath}.${keep}`, { force: true });\n } catch {\n // best-effort cleanup of the oldest archive\n }\n for (let i = keep - 1; i >= 1; i -= 1) {\n try {\n await rename(`${this.impressionsPath}.${i}`, `${this.impressionsPath}.${i + 1}`);\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n }\n }\n try {\n await rename(this.impressionsPath, `${this.impressionsPath}.1`);\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n }\n }\n\n /**\n * Attach a RecallTierExplain block to the existing snapshot for a\n * session without rewriting the entire snapshot. Used by the\n * post-recall direct-answer annotation path (issue #518 slice 3c):\n * recallInternal records the snapshot first, then the orchestrator\n * fires the direct-answer tier in observation mode and annotates\n * the stored snapshot with whichever tier served the query.\n *\n * No-op when no snapshot exists for the given session; callers do\n * not need to guard on existence.\n */\n async annotateTierExplain(\n sessionKey: string,\n tierExplain: RecallTierExplain,\n expected?: { writeNonce?: string; traceId?: string; recordedAt?: string },\n ): Promise<void> {\n const current = this.state[sessionKey];\n if (!current) return;\n if (!snapshotMatchesExpectedIdentity(current, expected)) return;\n this.state[sessionKey] = {\n ...current,\n tierExplain: cloneTierExplain(tierExplain),\n };\n try {\n await this.flushState();\n } catch (err) {\n log.debug(`last recall tier-explain annotate failed: ${err}`);\n }\n }\n\n\n private flushState(): Promise<void> {\n const run = this.stateWriteChain.catch(() => undefined).then(async () => {\n await mkdir(path.dirname(this.statePath), { recursive: true });\n await this.writeStateFile(this.statePath, JSON.stringify(this.state, null, 2));\n });\n this.stateWriteChain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n}\n\nexport class TierMigrationStatusStore {\n private readonly statePath: string;\n private state: TierMigrationStatusSnapshot = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);\n\n constructor(memoryDir: string) {\n this.statePath = path.join(memoryDir, \"state\", \"tier-migration-status.json\");\n }\n\n async load(): Promise<void> {\n try {\n const raw = await readFile(this.statePath, \"utf-8\");\n const parsed = JSON.parse(raw) as Partial<TierMigrationStatusSnapshot> | null;\n if (!parsed || typeof parsed !== \"object\") {\n this.state = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);\n return;\n }\n const totals = parsed.totals && typeof parsed.totals === \"object\"\n ? parsed.totals\n : DEFAULT_TIER_MIGRATION_STATUS.totals;\n this.state = {\n updatedAt:\n typeof parsed.updatedAt === \"string\" && parsed.updatedAt.length > 0\n ? parsed.updatedAt\n : DEFAULT_TIER_MIGRATION_STATUS.updatedAt,\n lastCycle:\n parsed.lastCycle && typeof parsed.lastCycle === \"object\"\n ? (parsed.lastCycle as TierMigrationCycleSummary)\n : null,\n totals: {\n cycles: typeof totals.cycles === \"number\" && Number.isFinite(totals.cycles) ? totals.cycles : 0,\n scanned: typeof totals.scanned === \"number\" && Number.isFinite(totals.scanned) ? totals.scanned : 0,\n migrated: typeof totals.migrated === \"number\" && Number.isFinite(totals.migrated) ? totals.migrated : 0,\n promoted: typeof totals.promoted === \"number\" && Number.isFinite(totals.promoted) ? totals.promoted : 0,\n demoted: typeof totals.demoted === \"number\" && Number.isFinite(totals.demoted) ? totals.demoted : 0,\n errors: typeof totals.errors === \"number\" && Number.isFinite(totals.errors) ? totals.errors : 0,\n },\n };\n } catch {\n this.state = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);\n }\n }\n\n get(): TierMigrationStatusSnapshot {\n return {\n updatedAt: this.state.updatedAt,\n lastCycle: this.state.lastCycle ? { ...this.state.lastCycle } : null,\n totals: { ...this.state.totals },\n };\n }\n\n async recordCycle(summary: TierMigrationCycleSummary): Promise<void> {\n const now = new Date().toISOString();\n const migratedDelta = summary.dryRun ? 0 : Math.max(0, summary.migrated);\n const promotedDelta = summary.dryRun ? 0 : Math.max(0, summary.promoted);\n const demotedDelta = summary.dryRun ? 0 : Math.max(0, summary.demoted);\n const next: TierMigrationStatusSnapshot = {\n updatedAt: now,\n lastCycle: { ...summary },\n totals: {\n cycles: this.state.totals.cycles + 1,\n scanned: this.state.totals.scanned + Math.max(0, summary.scanned),\n migrated: this.state.totals.migrated + migratedDelta,\n promoted: this.state.totals.promoted + promotedDelta,\n demoted: this.state.totals.demoted + demotedDelta,\n errors: this.state.totals.errors + Math.max(0, summary.errorCount ?? 0),\n },\n };\n this.state = next;\n try {\n await mkdir(path.dirname(this.statePath), { recursive: true });\n await writeFile(this.statePath, JSON.stringify(next, null, 2), \"utf-8\");\n } catch (err) {\n log.debug(`tier migration status write failed: ${err}`);\n }\n }\n}\n\n\n/**\n * Per-session bounded history of recent recall memory-id sets, used ONLY by\n * handle resolution (issue #1582). Distinct from {@link LastRecallStore} —\n * that keeps ONE last snapshot per session; this keeps a small ring (depth N)\n * of the memory-id arrays a session has recalled, newest first, so a handle\n * cited in a later turn still resolves even after a newer recall displaced the\n * \"last\" snapshot.\n *\n * Deliberately lightweight: stores only `string[]` per entry (no full\n * snapshot), capped at `depth` per session and 50 sessions total (rule 27\n * slice/cap guards). Misses are acceptable and tagged at resolution time.\n */\nexport class RecallHandleHistoryStore {\n private readonly statePath: string;\n private readonly writeStateFile: StateFileWriter;\n private state: Record<string, Array<{ at: string; ids: string[] }>> = {};\n private writeChain: Promise<void> = Promise.resolve();\n private readonly maxDepth: number;\n\n constructor(memoryDir: string, options: { writeStateFile?: StateFileWriter; maxDepth?: number } = {}) {\n this.statePath = path.join(memoryDir, \"state\", \"handle_history.json\");\n this.writeStateFile =\n options.writeStateFile ??\n (async (filePath, content) => {\n await writeFileAtomically(filePath, content);\n });\n this.maxDepth =\n typeof options.maxDepth === \"number\" && Number.isFinite(options.maxDepth)\n ? Math.max(1, Math.min(50, Math.floor(options.maxDepth)))\n : 5;\n }\n\n async load(): Promise<void> {\n try {\n const raw = await readFile(this.statePath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") this.state = parsed as typeof this.state;\n } catch {\n this.state = {};\n }\n }\n\n /**\n * Record one recall's admitted memory ids for a session, newest first.\n * The ring is capped at {@link maxDepth}; older entries drop off the tail.\n */\n async record(sessionKey: string, memoryIds: readonly string[]): Promise<void> {\n if (!sessionKey) return;\n const ids = memoryIds.filter((id): id is string => typeof id === \"string\" && id.length > 0);\n const entry = { at: new Date().toISOString(), ids };\n const prior = this.state[sessionKey] ?? [];\n const next = [entry, ...prior].slice(0, this.maxDepth);\n this.state[sessionKey] = next;\n this.capSessions();\n try {\n await this.flush();\n } catch (err) {\n log.debug(`recall handle history write failed: ${err}`);\n }\n }\n\n /**\n * Return the memory-id arrays for a session, newest first, capped at `depth`.\n * Empty array when the session has no recorded history.\n */\n recent(sessionKey: string, depth: number = this.maxDepth): Array<readonly string[]> {\n const entries = this.state[sessionKey];\n if (!entries || entries.length === 0) return [];\n const limit = Math.max(0, Math.min(depth, entries.length));\n return entries.slice(0, limit).map((e) => e.ids);\n }\n\n /** Test seam: drop all history (used by the corpus-scan hygiene test). */\n clearForTest(): void {\n this.state = {};\n }\n\n private capSessions(): void {\n const keys = Object.keys(this.state);\n if (keys.length <= 50) return;\n const ordered = keys\n .map((k) => ({ k, at: this.state[k]?.[0]?.at ?? \"\" }))\n .sort((a, b) => b.at.localeCompare(a.at));\n for (const doomed of ordered.slice(50)) {\n delete this.state[doomed.k];\n }\n }\n\n private flush(): Promise<void> {\n const run = this.writeChain.catch(() => undefined).then(async () => {\n await mkdir(path.dirname(this.statePath), { recursive: true });\n await this.writeStateFile(this.statePath, JSON.stringify(this.state, null, 2));\n });\n this.writeChain = run.then(() => undefined, () => undefined);\n return run;\n }\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-KSXJOC4F.js";
5
5
  import {
6
6
  StorageManager
7
- } from "./chunk-BOFVT6P3.js";
7
+ } from "./chunk-VBHPOWBJ.js";
8
8
  import {
9
9
  isSafeRouteNamespace
10
10
  } from "./chunk-U3PN77QT.js";
@@ -134,4 +134,4 @@ export {
134
134
  runCodexMaterialize,
135
135
  runPostConsolidationMaterialize
136
136
  };
137
- //# sourceMappingURL=chunk-4MBPIL4V.js.map
137
+ //# sourceMappingURL=chunk-PUSLMZBQ.js.map
@@ -1,11 +1,6 @@
1
1
  import {
2
2
  resolveEnsureCollectionArgs
3
3
  } from "./chunk-URS5GSM7.js";
4
- import {
5
- EMPTY_QMD_STATUS,
6
- embedQmdFiles,
7
- fetchQmdStatus
8
- } from "./chunk-IUM3VSHI.js";
9
4
  import {
10
5
  QMD_PROBE_RETRY_BACKOFF_MS,
11
6
  QMD_PROBE_TIMEOUT_MS,
@@ -18,6 +13,11 @@ import {
18
13
  qmdVersionToString,
19
14
  resolveQmdCapabilities
20
15
  } from "./chunk-ZLQ37WJX.js";
16
+ import {
17
+ EMPTY_QMD_STATUS,
18
+ embedQmdFiles,
19
+ fetchQmdStatus
20
+ } from "./chunk-IUM3VSHI.js";
21
21
  import {
22
22
  clearQmdResultCaches,
23
23
  getCachedQmdSearch,
@@ -2225,4 +2225,4 @@ export {
2225
2225
  sharedDaemonSessionCountForTest,
2226
2226
  QmdClient
2227
2227
  };
2228
- //# sourceMappingURL=chunk-RVO6TCTU.js.map
2228
+ //# sourceMappingURL=chunk-QCECHYZL.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-BOFVT6P3.js";
3
+ } from "./chunk-VBHPOWBJ.js";
4
4
  import {
5
5
  getCachedRuleMemories,
6
6
  setCachedRuleMemories
@@ -119,4 +119,4 @@ export {
119
119
  compareVerifiedSemanticRuleResults,
120
120
  searchVerifiedSemanticRules
121
121
  };
122
- //# sourceMappingURL=chunk-GFIKG4FR.js.map
122
+ //# sourceMappingURL=chunk-QGR6OVGT.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isRecallDisclosure
3
- } from "./chunk-LAQ33B5D.js";
3
+ } from "./chunk-UU3G6GUP.js";
4
4
  import {
5
5
  normalizeRetrievedMemoryProvenance
6
6
  } from "./chunk-I4WBNM3M.js";
@@ -162,6 +162,8 @@ function cloneResult(result) {
162
162
  }
163
163
  }
164
164
  }
165
+ const pathNodeIds = Array.isArray(result.pathNodeIds) ? result.pathNodeIds.filter((x) => typeof x === "string") : void 0;
166
+ const pathPenaltyApplied = typeof result.pathPenaltyApplied === "boolean" ? result.pathPenaltyApplied : void 0;
165
167
  const auditEntryId = nonEmptyString(result.auditEntryId);
166
168
  const rejectedBy = nonEmptyString(result.rejectedBy);
167
169
  const scoreDecomposition = cloneScoreDecomposition(result.scoreDecomposition);
@@ -176,6 +178,8 @@ function cloneResult(result) {
176
178
  if (graphEdgeConfidences !== void 0) {
177
179
  out.graphEdgeConfidences = graphEdgeConfidences;
178
180
  }
181
+ if (pathNodeIds !== void 0) out.pathNodeIds = pathNodeIds;
182
+ if (pathPenaltyApplied !== void 0) out.pathPenaltyApplied = pathPenaltyApplied;
179
183
  if (auditEntryId !== void 0) out.auditEntryId = auditEntryId;
180
184
  if (rejectedBy !== void 0) out.rejectedBy = rejectedBy;
181
185
  if (isRecallDisclosure(result.disclosure)) {
@@ -297,4 +301,4 @@ export {
297
301
  RecallXrayBuilder,
298
302
  summarizeDisclosureTokens
299
303
  };
300
- //# sourceMappingURL=chunk-NSGU76YO.js.map
304
+ //# sourceMappingURL=chunk-QHFV6VNU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recall-xray.ts"],"sourcesContent":["/**\n * Recall X-ray snapshot schema (issue #570, PR 1).\n *\n * The X-ray surface is the unified, per-result attribution document that\n * merges the tier that served each memory, its score decomposition, any\n * graph path, an audit entry id, and the exact filter/eligibility ladder\n * that either admitted or rejected each candidate. This file defines the\n * schema and an in-memory builder + builder-state helper.\n *\n * Scope for PR 1 (this slice):\n * - Types only + pure builder functions (no IO, no rendering).\n * - Orchestrator plumbing captures a snapshot when the caller passes\n * `xrayCapture: true`. No behavior change when the flag is absent.\n * - NO new public surfaces here — CLI/HTTP/MCP land in later slices.\n *\n * The shared renderer lands in PR 2 at `recall-xray-renderer.ts`. Do not\n * fork formatting logic into other surfaces; extend the renderer.\n */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport {\n normalizeRetrievedMemoryProvenance,\n type RetrievedMemoryProvenance,\n} from \"./memory-provenance.js\";\nimport type { RecallDisclosure, RecallTierExplain } from \"./types.js\";\nimport { isRecallDisclosure } from \"./types.js\";\n\n/**\n * Estimate token cost of a payload at the rough ~4 chars/token English\n * heuristic. Non-negative integer; returns 0 for empty / null input.\n * Used by recall surfaces to attach `estimatedTokens` to X-ray results\n * (issue #677 PR 3/4). Identical to the private heuristic in\n * `chunking.ts`; kept self-contained here so X-ray callers don't pull\n * in chunking internals.\n */\nexport function estimateRecallTokens(text: string | null | undefined): number {\n if (typeof text !== \"string\" || text.length === 0) return 0;\n return Math.ceil(text.length / 4);\n}\n\n/**\n * Aggregated per-disclosure token spend summary, computed by the\n * renderer from a snapshot's results. Non-negative integers.\n */\nexport interface RecallXrayDisclosureSummary {\n chunk: { count: number; estimatedTokens: number };\n section: { count: number; estimatedTokens: number };\n raw: { count: number; estimatedTokens: number };\n /** Number of results without a recorded disclosure level. */\n unspecified: { count: number; estimatedTokens: number };\n}\n\n/**\n * Which retrieval source produced a given result. This is the X-ray\n * tier ladder called out in issue #570 and is *distinct* from the\n * `RetrievalTier` enum (which describes issue #518's tier-explain\n * block). Keeping the sets separate lets the two observability\n * surfaces evolve without conflating their vocabularies:\n *\n * - `RetrievalTier` — direct-answer eligibility ladder.\n * - `RecallXrayServedBy` — which source materialized each result.\n */\nexport type RecallXrayServedBy =\n | \"direct-answer\"\n | \"hybrid\"\n | \"graph\"\n | \"recent-scan\"\n | \"procedural\"\n | \"review-context\"\n | \"lcm-file-parts\"\n | \"lcm-tool-parts\";\n\nexport const RECALL_XRAY_SERVED_BY_VALUES: readonly RecallXrayServedBy[] = [\n \"direct-answer\",\n \"hybrid\",\n \"graph\",\n \"recent-scan\",\n \"procedural\",\n \"review-context\",\n \"lcm-file-parts\",\n \"lcm-tool-parts\",\n] as const;\n\nexport function isRecallXrayServedBy(\n value: unknown,\n): value is RecallXrayServedBy {\n return (\n typeof value === \"string\" &&\n (RECALL_XRAY_SERVED_BY_VALUES as readonly string[]).includes(value)\n );\n}\n\n/**\n * Score decomposition for a single X-ray result.\n *\n * All fields are optional because different tiers populate different\n * terms: `hybrid` reports vector + bm25 + mmr penalty, `direct-answer`\n * reports importance + tier prior, etc. The only guaranteed field is\n * `final`, which is the post-combination score used for ordering.\n */\nexport interface RecallXrayScoreDecomposition {\n vector?: number;\n bm25?: number;\n importance?: number;\n mmrPenalty?: number;\n tierPrior?: number;\n /** Additive boost from `reinforcement_count` frontmatter (issue #687 PR 3/4). */\n reinforcementBoost?: number;\n final: number;\n}\n\n/**\n * Per-result breakdown inside an X-ray snapshot.\n */\nexport interface RecallXrayResult {\n memoryId: string;\n path: string;\n servedBy: RecallXrayServedBy;\n scoreDecomposition: RecallXrayScoreDecomposition;\n graphPath?: string[];\n /**\n * Issue #681 PR 3/3 — per-edge confidence values aligned with\n * `graphPath`. When present, `graphEdgeConfidences[i]` is the\n * confidence of the edge between `graphPath[i]` and `graphPath[i+1]`,\n * so the array length is one less than `graphPath`. Legacy edges\n * without a recorded confidence render as `1.0`. Operators use this\n * to attribute floor-pruning and PageRank ranking decisions back to\n * specific edges. The renderer drops the line when the array is\n * empty or absent so legacy snapshots round-trip cleanly.\n */\n graphEdgeConfidences?: number[];\n /** Graph activation node ids recorded for path-scoring provenance. */\n pathNodeIds?: string[];\n /** True when a graph path penalty was applied. */\n pathPenaltyApplied?: boolean;\n auditEntryId?: string;\n /** Human-readable list of filters the candidate *passed*. */\n admittedBy: string[];\n /**\n * First filter that *would have* rejected the candidate, or undefined\n * when the candidate was admitted without a rejection trace. When\n * present, `admittedBy` may still contain filters the candidate passed\n * before the rejecting gate; consumers should render both.\n */\n rejectedBy?: string;\n /**\n * Disclosure depth used to render this result's payload (issue #677\n * PR 3/4). Mirrors the per-result disclosure already exposed in the\n * recall response so X-ray consumers can attribute token spend to\n * the depth that produced it.\n */\n disclosure?: RecallDisclosure;\n /**\n * Estimated token cost of the rendered payload at the chosen\n * disclosure depth. Non-negative integer. Computed by callers via\n * `estimateRecallTokens(text)`; the renderer aggregates these into\n * a per-disclosure summary so operators can see where their budget\n * went.\n */\n estimatedTokens?: number;\n /**\n * Free-form tags from the memory's YAML frontmatter (issue #689 PR 3/3).\n * Populated by the X-ray capture path when the caller passes a `tags`\n * filter so per-result tags are available alongside the filter trace\n * in `snapshot.filters`. Also populated without a filter when the\n * orchestrator decorates results via `xrayCapture: true` so all X-ray\n * consumers can inspect memory labels without a separate storage read.\n * Absent (not `[]`) when the frontmatter has no tags or the memory\n * could not be read.\n */\n tags?: string[];\n /**\n * Per-result memory provenance and safety context. Populated by recall\n * capture when the memory frontmatter was already loaded by retrieval.\n */\n provenance?: RetrievedMemoryProvenance;\n /**\n * Claim-level provenance span (issue #1575 PR 3). The first source excerpt\n * backing this memory, surfaced so X-ray consumers can see the literal\n * utterance each fact derives from alongside retrieval provenance. Absent\n * when the memory carries no source spans (legacy or `provenance: \"none\"`).\n */\n sourceSpan?: {\n /** Verbatim quote excerpt (capped at write time by `provenance.maxQuoteChars`). */\n quote: string;\n /** ISO timestamp of the source turn. */\n observedAt: string;\n /** Coarse strength tag from the frontmatter. */\n provenance: \"verified\" | \"unverified\" | \"none\";\n };\n /**\n * Issue #1577 — per-result trust score, band, and components from the\n * unified TrustScore recall stage. Present when `trustScoreEnabled` is on.\n * Quarantined items appear here with `quarantined: true` and a reason so\n * exclusion never looks like \"no result\" (rule 34).\n */\n trust?: {\n score: number;\n band: \"high\" | \"medium\" | \"low\" | \"quarantine\";\n components: Record<string, { value: number; weight: number }>;\n multiplier: number;\n quarantined: boolean;\n quarantineReason?: string;\n };\n}\n\n/**\n * Trace entry for a filter the orchestrator evaluated during recall.\n * Captures the name of the filter, how many candidates it saw, and how\n * many it let through. Used by X-ray consumers to render the filter\n * ladder above the per-result breakdown.\n */\nexport interface RecallFilterTrace {\n name: string;\n considered: number;\n admitted: number;\n /** Optional human-readable reason for any rejections. */\n reason?: string;\n}\n\n/**\n * Peer-profile injection annotation (issue #679 completion).\n *\n * When `peerProfileRecallEnabled` is true and a peer is registered for\n * the session, the orchestrator injects a `## Peer Profile` section into\n * the recall context. This annotation records which peer was injected and\n * how many profile fields were included so operators can correlate\n * retrieval quality with peer-context enrichment.\n *\n * `null` means no peer profile was injected (peer not registered, feature\n * disabled, or peer has no profile fields).\n */\nexport interface RecallXrayPeerProfileInjection {\n /** The peer id whose profile was injected. */\n peerId: string;\n /**\n * Number of profile fields included after the `peerProfileRecallMaxFields`\n * cap was applied. Zero means the profile existed but had no fields.\n */\n fieldsInjected: number;\n}\n\n/**\n * The unified X-ray snapshot. CLI, HTTP, and MCP surfaces all render\n * this same shape through the shared renderer (CLAUDE.md rule 22).\n */\nexport interface RecallXraySnapshot {\n /** Stable v1 tag so downstream consumers can version-gate their parsers. */\n schemaVersion: \"1\";\n query: string;\n /** UUID minted per capture; unique across snapshots within a process. */\n snapshotId: string;\n /** Epoch milliseconds the snapshot was captured. */\n capturedAt: number;\n /**\n * Tier-explain block from issue #518, carried verbatim when present.\n * `null` means direct-answer tier did not run (disabled, or another\n * tier served the query).\n */\n tierExplain: RecallTierExplain | null;\n results: RecallXrayResult[];\n appliedResultLimit: number;\n appliedResults: RecallXrayResult[];\n headroomResults: RecallXrayResult[];\n filters: RecallFilterTrace[];\n /**\n * Character budget accounting for the final assembled recall payload.\n * `used` is the rendered-context length; `chars` is the cap. Both are\n * non-negative integers in `[0, 2**31)`.\n */\n budget: { chars: number; used: number };\n /** Optional session-scope fields carried for downstream filtering. */\n sessionKey?: string;\n namespace?: string;\n traceId?: string;\n /**\n * Peer-profile injection metadata (issue #679 completion).\n * Non-null when `peerProfileRecallEnabled` is true and a peer profile\n * was successfully injected into this recall's context. `null` (or\n * absent) means no peer profile was injected.\n */\n peerProfileInjection?: RecallXrayPeerProfileInjection | null;\n}\n\n// ─── Builder ──────────────────────────────────────────────────────────────\n\nexport interface BuildXraySnapshotInput {\n query: string;\n tierExplain?: RecallTierExplain | null;\n results?: RecallXrayResult[];\n appliedResultLimit?: number;\n appliedResults?: RecallXrayResult[];\n headroomResults?: RecallXrayResult[];\n filters?: RecallFilterTrace[];\n budget?: { chars?: number; used?: number };\n sessionKey?: string;\n namespace?: string;\n traceId?: string;\n /** Peer-profile injection metadata (issue #679 completion). */\n peerProfileInjection?: RecallXrayPeerProfileInjection | null;\n /** Optional injected timestamp for deterministic tests. */\n now?: () => number;\n /** Optional injected id generator for deterministic tests. */\n snapshotIdGenerator?: () => string;\n}\n\n/**\n * Build a `RecallXraySnapshot` from explicit input fields. Pure\n * function; safe to call from anywhere. All array/object inputs are\n * shallow-copied so caller mutation after build cannot tear the\n * returned snapshot.\n */\nexport function buildXraySnapshot(\n input: BuildXraySnapshotInput,\n): RecallXraySnapshot {\n const now = input.now ?? Date.now;\n const snapshotIdGenerator = input.snapshotIdGenerator ?? randomUUID;\n\n const results = Array.isArray(input.results)\n ? input.results.map(cloneResult)\n : [];\n const appliedResults = Array.isArray(input.appliedResults)\n ? input.appliedResults.map(cloneResult)\n : [];\n const headroomResults = Array.isArray(input.headroomResults)\n ? input.headroomResults.map(cloneResult)\n : [];\n const filters = Array.isArray(input.filters)\n ? input.filters.map(cloneFilter)\n : [];\n\n const budgetChars = nonNegativeInt(input.budget?.chars);\n const budgetUsed = nonNegativeInt(input.budget?.used);\n\n const tierExplain =\n input.tierExplain && typeof input.tierExplain === \"object\"\n ? cloneTierExplain(input.tierExplain)\n : null;\n\n // Peer-profile injection annotation (issue #679 completion).\n // Deep-copy via structuredClone so the caller can't mutate the snapshot\n // after build. Accept null explicitly (no injection) or a valid object.\n let peerProfileInjection: RecallXrayPeerProfileInjection | null | undefined;\n if (input.peerProfileInjection && typeof input.peerProfileInjection === \"object\") {\n const raw = input.peerProfileInjection;\n const peerId = nonEmptyString(raw.peerId);\n if (peerId !== undefined) {\n peerProfileInjection = {\n peerId,\n fieldsInjected: nonNegativeInt(raw.fieldsInjected),\n };\n }\n } else if (input.peerProfileInjection === null) {\n peerProfileInjection = null;\n }\n\n const out: RecallXraySnapshot = {\n schemaVersion: \"1\",\n query: typeof input.query === \"string\" ? input.query : \"\",\n snapshotId: snapshotIdGenerator(),\n capturedAt: now(),\n tierExplain,\n results,\n appliedResultLimit: nonNegativeInt(input.appliedResultLimit),\n appliedResults,\n headroomResults,\n filters,\n budget: { chars: budgetChars, used: budgetUsed },\n sessionKey: nonEmptyString(input.sessionKey),\n namespace: nonEmptyString(input.namespace),\n traceId: nonEmptyString(input.traceId),\n };\n if (peerProfileInjection !== undefined) {\n out.peerProfileInjection = peerProfileInjection;\n }\n return out;\n}\n\n/**\n * Mutable builder used by the orchestrator to accumulate X-ray fields\n * as recall progresses. Call `build()` to get the finalized\n * immutable-ish snapshot. All inputs are validated at insert time so\n * a malformed entry cannot poison the snapshot later.\n */\nexport class RecallXrayBuilder {\n private readonly query: string;\n private readonly sessionKey: string | undefined;\n private namespace: string | undefined;\n private traceId: string | undefined;\n private tierExplain: RecallTierExplain | null = null;\n private readonly results: RecallXrayResult[] = [];\n private readonly filters: RecallFilterTrace[] = [];\n private budgetChars = 0;\n private budgetUsed = 0;\n private peerProfileInjection: RecallXrayPeerProfileInjection | null | undefined;\n\n constructor(opts: {\n query: string;\n sessionKey?: string;\n namespace?: string;\n traceId?: string;\n }) {\n this.query = typeof opts.query === \"string\" ? opts.query : \"\";\n this.sessionKey = nonEmptyString(opts.sessionKey);\n this.namespace = nonEmptyString(opts.namespace);\n this.traceId = nonEmptyString(opts.traceId);\n }\n\n setNamespace(namespace: string | undefined): void {\n this.namespace = nonEmptyString(namespace);\n }\n\n setTraceId(traceId: string | undefined): void {\n this.traceId = nonEmptyString(traceId);\n }\n\n setTierExplain(tierExplain: RecallTierExplain | null | undefined): void {\n this.tierExplain =\n tierExplain && typeof tierExplain === \"object\"\n ? cloneTierExplain(tierExplain)\n : null;\n }\n\n setBudget(budget: { chars?: number; used?: number }): void {\n this.budgetChars = nonNegativeInt(budget.chars);\n this.budgetUsed = nonNegativeInt(budget.used);\n }\n\n /**\n * Record peer-profile injection metadata for this recall snapshot\n * (issue #679 completion). Pass `null` to explicitly record that no\n * injection happened (feature enabled but no peer / no profile fields).\n */\n setPeerProfileInjection(\n injection: RecallXrayPeerProfileInjection | null,\n ): void {\n this.peerProfileInjection = injection;\n }\n\n recordResult(result: RecallXrayResult): void {\n this.results.push(cloneResult(result));\n }\n\n recordFilter(filter: RecallFilterTrace): void {\n this.filters.push(cloneFilter(filter));\n }\n\n build(opts: {\n now?: () => number;\n snapshotIdGenerator?: () => string;\n } = {}): RecallXraySnapshot {\n return buildXraySnapshot({\n query: this.query,\n tierExplain: this.tierExplain,\n results: this.results,\n filters: this.filters,\n budget: { chars: this.budgetChars, used: this.budgetUsed },\n sessionKey: this.sessionKey,\n namespace: this.namespace,\n traceId: this.traceId,\n peerProfileInjection: this.peerProfileInjection,\n now: opts.now,\n snapshotIdGenerator: opts.snapshotIdGenerator,\n });\n }\n}\n\n// ─── Internals ────────────────────────────────────────────────────────────\n\nfunction cloneResult(result: RecallXrayResult): RecallXrayResult {\n if (!result || typeof result !== \"object\") {\n throw new TypeError(\"RecallXrayResult must be an object\");\n }\n if (!isRecallXrayServedBy(result.servedBy)) {\n throw new TypeError(\n `RecallXrayResult.servedBy must be one of ${RECALL_XRAY_SERVED_BY_VALUES.join(\n \", \",\n )}; got ${JSON.stringify(result.servedBy)}`,\n );\n }\n const memoryId = typeof result.memoryId === \"string\" ? result.memoryId : \"\";\n const path = typeof result.path === \"string\" ? result.path : \"\";\n const admittedBy = Array.isArray(result.admittedBy)\n ? result.admittedBy.filter((x): x is string => typeof x === \"string\")\n : [];\n const graphPath = Array.isArray(result.graphPath)\n ? result.graphPath.filter((x): x is string => typeof x === \"string\")\n : undefined;\n // Issue #681 PR 3/3 — per-edge confidences alongside graph path.\n // Each entry is clamped into [0, 1]; the array is rejected wholesale\n // when alignment cannot be verified so downstream surfaces can rely\n // on `graphEdgeConfidences[i]` describing the edge between\n // `graphPath[i]` and `graphPath[i+1]`.\n //\n // Cursor review (#735): the input array length MUST match\n // `graphPath.length - 1` *before* any per-element filtering. The\n // earlier implementation skipped non-finite entries via `continue`\n // and then length-checked the cleaned array — that would silently\n // shift surviving values to earlier positions. Example: input\n // `[0.5, NaN, 0.7]` for a 3-edge path would collapse to\n // `[0.5, 0.7]`, length-check would pass against `expected = 2`, and\n // the renderer would mis-attribute `0.7` to edge B→C when it really\n // came from edge C→D. Reject on either size mismatch or any\n // non-finite entry so misalignment is impossible.\n let graphEdgeConfidences: number[] | undefined;\n if (Array.isArray(result.graphEdgeConfidences) && graphPath && graphPath.length > 1) {\n const expected = graphPath.length - 1;\n const raw = result.graphEdgeConfidences;\n if (raw.length === expected) {\n const cleaned: number[] = [];\n let allFinite = true;\n for (const value of raw) {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n allFinite = false;\n break;\n }\n cleaned.push(Math.min(1, Math.max(0, value)));\n }\n if (allFinite) {\n graphEdgeConfidences = cleaned;\n }\n }\n }\n const pathNodeIds = Array.isArray(result.pathNodeIds)\n ? result.pathNodeIds.filter((x): x is string => typeof x === \"string\")\n : undefined;\n const pathPenaltyApplied =\n typeof result.pathPenaltyApplied === \"boolean\"\n ? result.pathPenaltyApplied\n : undefined;\n const auditEntryId = nonEmptyString(result.auditEntryId);\n const rejectedBy = nonEmptyString(result.rejectedBy);\n const scoreDecomposition = cloneScoreDecomposition(result.scoreDecomposition);\n const out: RecallXrayResult = {\n memoryId,\n path,\n servedBy: result.servedBy,\n scoreDecomposition,\n admittedBy,\n };\n if (graphPath !== undefined) out.graphPath = graphPath;\n if (graphEdgeConfidences !== undefined) {\n out.graphEdgeConfidences = graphEdgeConfidences;\n }\n if (pathNodeIds !== undefined) out.pathNodeIds = pathNodeIds;\n if (pathPenaltyApplied !== undefined) out.pathPenaltyApplied = pathPenaltyApplied;\n if (auditEntryId !== undefined) out.auditEntryId = auditEntryId;\n if (rejectedBy !== undefined) out.rejectedBy = rejectedBy;\n // Disclosure + token telemetry (issue #677 PR 3/4). Only attach when\n // present and well-formed; unknown disclosure values are dropped so a\n // bad caller can't poison downstream renderers. Uses the shared\n // `isRecallDisclosure` guard so adding a fourth disclosure level\n // requires touching only `types.ts`.\n if (isRecallDisclosure(result.disclosure)) {\n out.disclosure = result.disclosure;\n }\n if (\n typeof result.estimatedTokens === \"number\" &&\n Number.isFinite(result.estimatedTokens) &&\n result.estimatedTokens >= 0\n ) {\n out.estimatedTokens = Math.floor(result.estimatedTokens);\n }\n // Tags from frontmatter (issue #689 PR 3/3). Normalize identically to\n // the recall-surface path: trim and drop empty strings so consumers can\n // compare tags directly without a secondary normalization step.\n if (Array.isArray(result.tags)) {\n const cleanedTags = result.tags\n .filter((t): t is string => typeof t === \"string\")\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n if (cleanedTags.length > 0) {\n out.tags = cleanedTags;\n }\n }\n const provenance = normalizeRetrievedMemoryProvenance(result.provenance);\n if (provenance !== undefined) {\n out.provenance = provenance;\n }\n // Claim-level provenance span (#1575 PR 3). Deep-copy the three scalar\n // fields so the snapshot returned by getLastXraySnapshot is independent\n // of the caller's object (matches the structuredClone contract).\n const ss = result.sourceSpan;\n if (\n ss &&\n typeof ss.quote === \"string\" &&\n typeof ss.observedAt === \"string\" &&\n (ss.provenance === \"verified\" ||\n ss.provenance === \"unverified\" ||\n ss.provenance === \"none\")\n ) {\n out.sourceSpan = {\n quote: ss.quote,\n observedAt: ss.observedAt,\n provenance: ss.provenance,\n };\n }\n // Issue #1577 — preserve the trust projection through the clone so\n // getLastXraySnapshot surfaces trust score/band/quarantine reason (review\n // Oqg_o: cloneResult was silently dropping the field). Deep-copy the scalar +\n // record fields so the snapshot is independent of the caller's object,\n // matching the structuredClone contract used for every other field above.\n if (result.trust) {\n const t = result.trust;\n const components: Record<string, { value: number; weight: number }> = {};\n for (const [k, v] of Object.entries(t.components)) {\n components[k] = { value: v.value, weight: v.weight };\n }\n out.trust = {\n score: t.score,\n band: t.band,\n components,\n multiplier: t.multiplier,\n quarantined: t.quarantined,\n ...(t.quarantineReason ? { quarantineReason: t.quarantineReason } : {}),\n };\n }\n return out;\n}\n\n/**\n * Summarize per-disclosure token spend across an X-ray snapshot's\n * results. Pure helper — used by the markdown renderer to print a\n * \"per-disclosure token spend\" line and exposed for tests / surfaces.\n */\nexport function summarizeDisclosureTokens(\n results: ReadonlyArray<RecallXrayResult>,\n): RecallXrayDisclosureSummary {\n const summary: RecallXrayDisclosureSummary = {\n chunk: { count: 0, estimatedTokens: 0 },\n section: { count: 0, estimatedTokens: 0 },\n raw: { count: 0, estimatedTokens: 0 },\n unspecified: { count: 0, estimatedTokens: 0 },\n };\n for (const result of results) {\n const tokens =\n typeof result.estimatedTokens === \"number\" &&\n Number.isFinite(result.estimatedTokens) &&\n result.estimatedTokens >= 0\n ? Math.floor(result.estimatedTokens)\n : 0;\n const bucket = isRecallDisclosure(result.disclosure)\n ? result.disclosure\n : \"unspecified\";\n summary[bucket].count += 1;\n summary[bucket].estimatedTokens += tokens;\n }\n return summary;\n}\n\nfunction cloneFilter(filter: RecallFilterTrace): RecallFilterTrace {\n if (!filter || typeof filter !== \"object\") {\n throw new TypeError(\"RecallFilterTrace must be an object\");\n }\n const out: RecallFilterTrace = {\n name: typeof filter.name === \"string\" ? filter.name : \"\",\n considered: nonNegativeInt(filter.considered),\n admitted: nonNegativeInt(filter.admitted),\n };\n const reason = nonEmptyString(filter.reason);\n if (reason !== undefined) out.reason = reason;\n return out;\n}\n\nfunction cloneScoreDecomposition(\n value: RecallXrayScoreDecomposition | undefined,\n): RecallXrayScoreDecomposition {\n if (!value || typeof value !== \"object\") {\n return { final: 0 };\n }\n const out: RecallXrayScoreDecomposition = {\n final: finiteNumber(value.final) ?? 0,\n };\n const vector = finiteNumber(value.vector);\n if (vector !== undefined) out.vector = vector;\n const bm25 = finiteNumber(value.bm25);\n if (bm25 !== undefined) out.bm25 = bm25;\n const importance = finiteNumber(value.importance);\n if (importance !== undefined) out.importance = importance;\n const mmrPenalty = finiteNumber(value.mmrPenalty);\n if (mmrPenalty !== undefined) out.mmrPenalty = mmrPenalty;\n const tierPrior = finiteNumber(value.tierPrior);\n if (tierPrior !== undefined) out.tierPrior = tierPrior;\n const reinforcementBoost = finiteNumber(value.reinforcementBoost);\n if (reinforcementBoost !== undefined && reinforcementBoost > 0) {\n out.reinforcementBoost = reinforcementBoost;\n }\n return out;\n}\n\nfunction cloneTierExplain(tierExplain: RecallTierExplain): RecallTierExplain {\n // Use structuredClone so future RecallTierExplain additions do not\n // silently share references through hand-enumerated fields. The\n // payload is JSON-shaped.\n return structuredClone(tierExplain);\n}\n\nfunction nonNegativeInt(value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return 0;\n if (value <= 0) return 0;\n return Math.floor(value);\n}\n\nfunction finiteNumber(value: unknown): number | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return undefined;\n return value;\n}\n\nfunction nonEmptyString(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n if (trimmed.length === 0) return undefined;\n return trimmed;\n}\n"],"mappings":";;;;;;;;AAmBA,SAAS,kBAAkB;AAiBpB,SAAS,qBAAqB,MAAyC;AAC5E,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG,QAAO;AAC1D,SAAO,KAAK,KAAK,KAAK,SAAS,CAAC;AAClC;AAkCO,IAAM,+BAA8D;AAAA,EACzE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,qBACd,OAC6B;AAC7B,SACE,OAAO,UAAU,YAChB,6BAAmD,SAAS,KAAK;AAEtE;AA8NO,SAAS,kBACd,OACoB;AACpB,QAAM,MAAM,MAAM,OAAO,KAAK;AAC9B,QAAM,sBAAsB,MAAM,uBAAuB;AAEzD,QAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,IACvC,MAAM,QAAQ,IAAI,WAAW,IAC7B,CAAC;AACL,QAAM,iBAAiB,MAAM,QAAQ,MAAM,cAAc,IACrD,MAAM,eAAe,IAAI,WAAW,IACpC,CAAC;AACL,QAAM,kBAAkB,MAAM,QAAQ,MAAM,eAAe,IACvD,MAAM,gBAAgB,IAAI,WAAW,IACrC,CAAC;AACL,QAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,IACvC,MAAM,QAAQ,IAAI,WAAW,IAC7B,CAAC;AAEL,QAAM,cAAc,eAAe,MAAM,QAAQ,KAAK;AACtD,QAAM,aAAa,eAAe,MAAM,QAAQ,IAAI;AAEpD,QAAM,cACJ,MAAM,eAAe,OAAO,MAAM,gBAAgB,WAC9C,iBAAiB,MAAM,WAAW,IAClC;AAKN,MAAI;AACJ,MAAI,MAAM,wBAAwB,OAAO,MAAM,yBAAyB,UAAU;AAChF,UAAM,MAAM,MAAM;AAClB,UAAM,SAAS,eAAe,IAAI,MAAM;AACxC,QAAI,WAAW,QAAW;AACxB,6BAAuB;AAAA,QACrB;AAAA,QACA,gBAAgB,eAAe,IAAI,cAAc;AAAA,MACnD;AAAA,IACF;AAAA,EACF,WAAW,MAAM,yBAAyB,MAAM;AAC9C,2BAAuB;AAAA,EACzB;AAEA,QAAM,MAA0B;AAAA,IAC9B,eAAe;AAAA,IACf,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,IACvD,YAAY,oBAAoB;AAAA,IAChC,YAAY,IAAI;AAAA,IAChB;AAAA,IACA;AAAA,IACA,oBAAoB,eAAe,MAAM,kBAAkB;AAAA,IAC3D;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,EAAE,OAAO,aAAa,MAAM,WAAW;AAAA,IAC/C,YAAY,eAAe,MAAM,UAAU;AAAA,IAC3C,WAAW,eAAe,MAAM,SAAS;AAAA,IACzC,SAAS,eAAe,MAAM,OAAO;AAAA,EACvC;AACA,MAAI,yBAAyB,QAAW;AACtC,QAAI,uBAAuB;AAAA,EAC7B;AACA,SAAO;AACT;AAQO,IAAM,oBAAN,MAAwB;AAAA,EACZ;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACA,cAAwC;AAAA,EAC/B,UAA8B,CAAC;AAAA,EAC/B,UAA+B,CAAC;AAAA,EACzC,cAAc;AAAA,EACd,aAAa;AAAA,EACb;AAAA,EAER,YAAY,MAKT;AACD,SAAK,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC3D,SAAK,aAAa,eAAe,KAAK,UAAU;AAChD,SAAK,YAAY,eAAe,KAAK,SAAS;AAC9C,SAAK,UAAU,eAAe,KAAK,OAAO;AAAA,EAC5C;AAAA,EAEA,aAAa,WAAqC;AAChD,SAAK,YAAY,eAAe,SAAS;AAAA,EAC3C;AAAA,EAEA,WAAW,SAAmC;AAC5C,SAAK,UAAU,eAAe,OAAO;AAAA,EACvC;AAAA,EAEA,eAAe,aAAyD;AACtE,SAAK,cACH,eAAe,OAAO,gBAAgB,WAClC,iBAAiB,WAAW,IAC5B;AAAA,EACR;AAAA,EAEA,UAAU,QAAiD;AACzD,SAAK,cAAc,eAAe,OAAO,KAAK;AAC9C,SAAK,aAAa,eAAe,OAAO,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,wBACE,WACM;AACN,SAAK,uBAAuB;AAAA,EAC9B;AAAA,EAEA,aAAa,QAAgC;AAC3C,SAAK,QAAQ,KAAK,YAAY,MAAM,CAAC;AAAA,EACvC;AAAA,EAEA,aAAa,QAAiC;AAC5C,SAAK,QAAQ,KAAK,YAAY,MAAM,CAAC;AAAA,EACvC;AAAA,EAEA,MAAM,OAGF,CAAC,GAAuB;AAC1B,WAAO,kBAAkB;AAAA,MACvB,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,QAAQ,EAAE,OAAO,KAAK,aAAa,MAAM,KAAK,WAAW;AAAA,MACzD,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,SAAS,KAAK;AAAA,MACd,sBAAsB,KAAK;AAAA,MAC3B,KAAK,KAAK;AAAA,MACV,qBAAqB,KAAK;AAAA,IAC5B,CAAC;AAAA,EACH;AACF;AAIA,SAAS,YAAY,QAA4C;AAC/D,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,UAAU,oCAAoC;AAAA,EAC1D;AACA,MAAI,CAAC,qBAAqB,OAAO,QAAQ,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,4CAA4C,6BAA6B;AAAA,QACvE;AAAA,MACF,CAAC,SAAS,KAAK,UAAU,OAAO,QAAQ,CAAC;AAAA,IAC3C;AAAA,EACF;AACA,QAAM,WAAW,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;AACzE,QAAM,OAAO,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAC7D,QAAM,aAAa,MAAM,QAAQ,OAAO,UAAU,IAC9C,OAAO,WAAW,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IAClE,CAAC;AACL,QAAM,YAAY,MAAM,QAAQ,OAAO,SAAS,IAC5C,OAAO,UAAU,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IACjE;AAiBJ,MAAI;AACJ,MAAI,MAAM,QAAQ,OAAO,oBAAoB,KAAK,aAAa,UAAU,SAAS,GAAG;AACnF,UAAM,WAAW,UAAU,SAAS;AACpC,UAAM,MAAM,OAAO;AACnB,QAAI,IAAI,WAAW,UAAU;AAC3B,YAAM,UAAoB,CAAC;AAC3B,UAAI,YAAY;AAChB,iBAAW,SAAS,KAAK;AACvB,YAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;AACxD,sBAAY;AACZ;AAAA,QACF;AACA,gBAAQ,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC,CAAC;AAAA,MAC9C;AACA,UAAI,WAAW;AACb,+BAAuB;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACA,QAAM,cAAc,MAAM,QAAQ,OAAO,WAAW,IAChD,OAAO,YAAY,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IACnE;AACJ,QAAM,qBACJ,OAAO,OAAO,uBAAuB,YACjC,OAAO,qBACP;AACN,QAAM,eAAe,eAAe,OAAO,YAAY;AACvD,QAAM,aAAa,eAAe,OAAO,UAAU;AACnD,QAAM,qBAAqB,wBAAwB,OAAO,kBAAkB;AAC5E,QAAM,MAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,UAAU,OAAO;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AACA,MAAI,cAAc,OAAW,KAAI,YAAY;AAC7C,MAAI,yBAAyB,QAAW;AACtC,QAAI,uBAAuB;AAAA,EAC7B;AACA,MAAI,gBAAgB,OAAW,KAAI,cAAc;AACjD,MAAI,uBAAuB,OAAW,KAAI,qBAAqB;AAC/D,MAAI,iBAAiB,OAAW,KAAI,eAAe;AACnD,MAAI,eAAe,OAAW,KAAI,aAAa;AAM/C,MAAI,mBAAmB,OAAO,UAAU,GAAG;AACzC,QAAI,aAAa,OAAO;AAAA,EAC1B;AACA,MACE,OAAO,OAAO,oBAAoB,YAClC,OAAO,SAAS,OAAO,eAAe,KACtC,OAAO,mBAAmB,GAC1B;AACA,QAAI,kBAAkB,KAAK,MAAM,OAAO,eAAe;AAAA,EACzD;AAIA,MAAI,MAAM,QAAQ,OAAO,IAAI,GAAG;AAC9B,UAAM,cAAc,OAAO,KACxB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,EAChD,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,QAAI,YAAY,SAAS,GAAG;AAC1B,UAAI,OAAO;AAAA,IACb;AAAA,EACF;AACA,QAAM,aAAa,mCAAmC,OAAO,UAAU;AACvE,MAAI,eAAe,QAAW;AAC5B,QAAI,aAAa;AAAA,EACnB;AAIA,QAAM,KAAK,OAAO;AAClB,MACE,MACA,OAAO,GAAG,UAAU,YACpB,OAAO,GAAG,eAAe,aACxB,GAAG,eAAe,cACjB,GAAG,eAAe,gBAClB,GAAG,eAAe,SACpB;AACA,QAAI,aAAa;AAAA,MACf,OAAO,GAAG;AAAA,MACV,YAAY,GAAG;AAAA,MACf,YAAY,GAAG;AAAA,IACjB;AAAA,EACF;AAMA,MAAI,OAAO,OAAO;AAChB,UAAM,IAAI,OAAO;AACjB,UAAM,aAAgE,CAAC;AACvE,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,EAAE,UAAU,GAAG;AACjD,iBAAW,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,QAAQ,EAAE,OAAO;AAAA,IACrD;AACA,QAAI,QAAQ;AAAA,MACV,OAAO,EAAE;AAAA,MACT,MAAM,EAAE;AAAA,MACR;AAAA,MACA,YAAY,EAAE;AAAA,MACd,aAAa,EAAE;AAAA,MACf,GAAI,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,IAAI,CAAC;AAAA,IACvE;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,0BACd,SAC6B;AAC7B,QAAM,UAAuC;AAAA,IAC3C,OAAO,EAAE,OAAO,GAAG,iBAAiB,EAAE;AAAA,IACtC,SAAS,EAAE,OAAO,GAAG,iBAAiB,EAAE;AAAA,IACxC,KAAK,EAAE,OAAO,GAAG,iBAAiB,EAAE;AAAA,IACpC,aAAa,EAAE,OAAO,GAAG,iBAAiB,EAAE;AAAA,EAC9C;AACA,aAAW,UAAU,SAAS;AAC5B,UAAM,SACJ,OAAO,OAAO,oBAAoB,YAClC,OAAO,SAAS,OAAO,eAAe,KACtC,OAAO,mBAAmB,IACtB,KAAK,MAAM,OAAO,eAAe,IACjC;AACN,UAAM,SAAS,mBAAmB,OAAO,UAAU,IAC/C,OAAO,aACP;AACJ,YAAQ,MAAM,EAAE,SAAS;AACzB,YAAQ,MAAM,EAAE,mBAAmB;AAAA,EACrC;AACA,SAAO;AACT;AAEA,SAAS,YAAY,QAA8C;AACjE,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,UAAU,qCAAqC;AAAA,EAC3D;AACA,QAAM,MAAyB;AAAA,IAC7B,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,IACtD,YAAY,eAAe,OAAO,UAAU;AAAA,IAC5C,UAAU,eAAe,OAAO,QAAQ;AAAA,EAC1C;AACA,QAAM,SAAS,eAAe,OAAO,MAAM;AAC3C,MAAI,WAAW,OAAW,KAAI,SAAS;AACvC,SAAO;AACT;AAEA,SAAS,wBACP,OAC8B;AAC9B,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO,EAAE,OAAO,EAAE;AAAA,EACpB;AACA,QAAM,MAAoC;AAAA,IACxC,OAAO,aAAa,MAAM,KAAK,KAAK;AAAA,EACtC;AACA,QAAM,SAAS,aAAa,MAAM,MAAM;AACxC,MAAI,WAAW,OAAW,KAAI,SAAS;AACvC,QAAM,OAAO,aAAa,MAAM,IAAI;AACpC,MAAI,SAAS,OAAW,KAAI,OAAO;AACnC,QAAM,aAAa,aAAa,MAAM,UAAU;AAChD,MAAI,eAAe,OAAW,KAAI,aAAa;AAC/C,QAAM,aAAa,aAAa,MAAM,UAAU;AAChD,MAAI,eAAe,OAAW,KAAI,aAAa;AAC/C,QAAM,YAAY,aAAa,MAAM,SAAS;AAC9C,MAAI,cAAc,OAAW,KAAI,YAAY;AAC7C,QAAM,qBAAqB,aAAa,MAAM,kBAAkB;AAChE,MAAI,uBAAuB,UAAa,qBAAqB,GAAG;AAC9D,QAAI,qBAAqB;AAAA,EAC3B;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,aAAmD;AAI3E,SAAO,gBAAgB,WAAW;AACpC;AAEA,SAAS,eAAe,OAAwB;AAC9C,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO,KAAK,MAAM,KAAK;AACzB;AAEA,SAAS,aAAa,OAAoC;AACxD,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO;AACT;AAEA,SAAS,eAAe,OAAoC;AAC1D,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO;AACT;","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-SOBJ6NEY.js";
4
4
  import {
5
5
  summarizeDisclosureTokens
6
- } from "./chunk-NSGU76YO.js";
6
+ } from "./chunk-QHFV6VNU.js";
7
7
  import {
8
8
  summarizeRetrievedMemoryProvenance
9
9
  } from "./chunk-I4WBNM3M.js";
@@ -342,6 +342,12 @@ function renderResultTextLines(result, rank) {
342
342
  );
343
343
  }
344
344
  }
345
+ if (result.pathPenaltyApplied !== void 0) {
346
+ lines.push(` path-penalty: ${result.pathPenaltyApplied ? "yes" : "no"}`);
347
+ }
348
+ if (result.pathNodeIds && result.pathNodeIds.length > 0) {
349
+ lines.push(` path-node-ids: ${result.pathNodeIds.join(" -> ")}`);
350
+ }
345
351
  if (result.auditEntryId) {
346
352
  lines.push(` audit-entry: ${result.auditEntryId}`);
347
353
  }
@@ -508,6 +514,16 @@ function renderResultMarkdownLines(result, rank) {
508
514
  );
509
515
  }
510
516
  }
517
+ if (result.pathPenaltyApplied !== void 0) {
518
+ lines.push(
519
+ `- **Path penalty applied:** ${mdInlineCode(result.pathPenaltyApplied ? "yes" : "no")}`
520
+ );
521
+ }
522
+ if (result.pathNodeIds && result.pathNodeIds.length > 0) {
523
+ lines.push(
524
+ `- **Path node ids:** ${result.pathNodeIds.map(mdInlineCode).join(" \u2192 ")}`
525
+ );
526
+ }
511
527
  if (result.auditEntryId) {
512
528
  lines.push(`- **Audit entry:** \`${result.auditEntryId}\``);
513
529
  }
@@ -590,4 +606,4 @@ export {
590
606
  renderRecallExplain,
591
607
  parseRecallExplainFormat
592
608
  };
593
- //# sourceMappingURL=chunk-PDEKQWDV.js.map
609
+ //# sourceMappingURL=chunk-QT2FRYI4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recall-explain-renderer.ts","../src/recall-xray-renderer.ts"],"sourcesContent":["/**\n * Renderers for RecallTierExplain (issue #518).\n *\n * Pure functions that format a `LastRecallSnapshot` and its\n * optional `tierExplain` field for human text and machine JSON\n * consumption. CLI / HTTP / MCP surfaces consume these — they do\n * not format explain output themselves, so rendering is tested in\n * one place.\n */\n\nimport type { LastRecallSnapshot } from \"./recall-state.js\";\nimport type { RecallTierExplain } from \"./types.js\";\nimport { isRetrievalTier } from \"./retrieval-tiers.js\";\nimport type {\n RecallXraySnapshot,\n RecallFilterTrace,\n RecallXrayResult,\n} from \"./recall-xray.js\";\nimport { renderXrayMarkdown } from \"./recall-xray-renderer.js\";\n\nfunction sanitizeString(v: unknown): string | null {\n return typeof v === \"string\" && v.length > 0 ? v : null;\n}\n\nfunction sanitizeFiniteNumber(v: unknown): number | null {\n return typeof v === \"number\" && Number.isFinite(v) ? v : null;\n}\n\n/**\n * `text` and `json` are the original formats (backwards-compatible\n * since issue #518). `markdown` was added in issue #570 PR 7 and\n * delegates to the shared X-ray renderer so the three observability\n * surfaces stay in lock-step (CLAUDE.md rule 22).\n */\nexport type RecallExplainFormat = \"text\" | \"json\" | \"markdown\";\n\nexport interface RecallExplainJsonPayload {\n hasExplain: boolean;\n snapshotFound: boolean;\n sessionKey: string | null;\n recordedAt: string | null;\n namespace: string | null;\n memoryIds: string[];\n source: string | null;\n sourcesUsed: string[] | null;\n latencyMs: number | null;\n tierExplain: RecallTierExplain | null;\n}\n\nfunction normalizeTierExplain(value: unknown): RecallTierExplain | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const raw = value as Record<string, unknown>;\n const filteredBy = Array.isArray(raw.filteredBy)\n ? raw.filteredBy.filter((x): x is string => typeof x === \"string\")\n : [];\n const sourceAnchors = Array.isArray(raw.sourceAnchors)\n ? raw.sourceAnchors\n .filter(\n (a): a is { path: string; lineRange?: unknown } =>\n !!a && typeof a === \"object\" && typeof (a as { path?: unknown }).path === \"string\",\n )\n .map((a) => {\n const lr = (a as { lineRange?: unknown }).lineRange;\n const lineRange =\n Array.isArray(lr) &&\n lr.length === 2 &&\n Number.isFinite(lr[0]) &&\n Number.isFinite(lr[1])\n ? ([lr[0] as number, lr[1] as number] as [number, number])\n : undefined;\n return lineRange\n ? { path: (a as { path: string }).path, lineRange }\n : { path: (a as { path: string }).path };\n })\n : undefined;\n return {\n tier: isRetrievalTier(raw.tier) ? raw.tier : \"hybrid\",\n tierReason: typeof raw.tierReason === \"string\" ? raw.tierReason : \"\",\n filteredBy,\n candidatesConsidered: sanitizeFiniteNumber(raw.candidatesConsidered) ?? 0,\n latencyMs: sanitizeFiniteNumber(raw.latencyMs) ?? 0,\n ...(sourceAnchors !== undefined ? { sourceAnchors } : {}),\n };\n}\n\nexport function toRecallExplainJson(\n snapshot: LastRecallSnapshot | null,\n): RecallExplainJsonPayload {\n if (!snapshot) {\n return {\n hasExplain: false,\n snapshotFound: false,\n sessionKey: null,\n recordedAt: null,\n namespace: null,\n memoryIds: [],\n source: null,\n sourcesUsed: null,\n latencyMs: null,\n tierExplain: null,\n };\n }\n const normalizedExplain = normalizeTierExplain(snapshot.tierExplain);\n return {\n hasExplain: normalizedExplain !== null,\n snapshotFound: true,\n sessionKey: sanitizeString(snapshot.sessionKey),\n recordedAt: sanitizeString(snapshot.recordedAt),\n namespace: sanitizeString(snapshot.namespace),\n memoryIds: Array.isArray(snapshot.memoryIds)\n ? snapshot.memoryIds.filter((x): x is string => typeof x === \"string\")\n : [],\n source: sanitizeString(snapshot.source),\n sourcesUsed: Array.isArray(snapshot.sourcesUsed)\n ? snapshot.sourcesUsed.filter((x): x is string => typeof x === \"string\")\n : null,\n latencyMs: sanitizeFiniteNumber(snapshot.latencyMs),\n tierExplain: normalizedExplain,\n };\n}\n\n/**\n * Render the shared \"--- tier explain ---\" text block used by both the\n * recall-explain surface and the Recall X-ray surface. Callers provide\n * the normalized `RecallTierExplain` (or `null` for the\n * not-populated/disabled case) so the block stays character-for-character\n * identical across surfaces (CLAUDE.md rule 22). The returned strings do\n * NOT include leading blank lines or headers — callers own that framing.\n */\nexport function renderTierExplainTextLines(\n tierExplain: RecallTierExplain | null,\n): string[] {\n const lines: string[] = [];\n if (!tierExplain) {\n lines.push(\n \"(not populated — direct-answer tier disabled or did not fire)\",\n );\n return lines;\n }\n lines.push(`tier: ${tierExplain.tier}`);\n lines.push(`reason: ${tierExplain.tierReason}`);\n lines.push(`candidates-considered: ${tierExplain.candidatesConsidered}`);\n lines.push(`latency-ms: ${tierExplain.latencyMs}`);\n if (tierExplain.filteredBy.length > 0) {\n lines.push(`filtered-by: ${tierExplain.filteredBy.join(\", \")}`);\n } else {\n lines.push(\"filtered-by: (none)\");\n }\n if (tierExplain.sourceAnchors && tierExplain.sourceAnchors.length > 0) {\n lines.push(\"source-anchors:\");\n for (const anchor of tierExplain.sourceAnchors) {\n const range = anchor.lineRange\n ? `:${anchor.lineRange[0]}-${anchor.lineRange[1]}`\n : \"\";\n lines.push(` - ${anchor.path}${range}`);\n }\n }\n return lines;\n}\n\nexport function toRecallExplainText(\n snapshot: LastRecallSnapshot | null,\n): string {\n const lines: string[] = [\"=== Recall Explain ===\"];\n\n if (!snapshot) {\n lines.push(\"No recall snapshot recorded yet.\");\n return lines.join(\"\\n\");\n }\n\n const sessionKey = sanitizeString(snapshot.sessionKey);\n const recordedAt = sanitizeString(snapshot.recordedAt);\n const namespace = sanitizeString(snapshot.namespace);\n const source = sanitizeString(snapshot.source);\n lines.push(`session: ${sessionKey ?? \"(unknown)\"}`);\n lines.push(`recorded: ${recordedAt ?? \"(unknown)\"}`);\n if (namespace) lines.push(`namespace: ${namespace}`);\n if (source) lines.push(`source: ${source}`);\n const sourcesUsed = Array.isArray(snapshot.sourcesUsed)\n ? snapshot.sourcesUsed.filter((x): x is string => typeof x === \"string\")\n : [];\n if (sourcesUsed.length > 0) {\n lines.push(`sources-used: ${sourcesUsed.join(\", \")}`);\n }\n const latencyMs = sanitizeFiniteNumber(snapshot.latencyMs);\n if (latencyMs !== null) {\n lines.push(`latency-ms: ${latencyMs}`);\n }\n const memoryIds = Array.isArray(snapshot.memoryIds)\n ? snapshot.memoryIds.filter((x): x is string => typeof x === \"string\")\n : [];\n if (memoryIds.length > 0) {\n lines.push(`memories: ${memoryIds.join(\", \")}`);\n }\n\n const ex = normalizeTierExplain(snapshot.tierExplain);\n if (!ex) {\n lines.push(\"\");\n lines.push(\n \"tier-explain: (not populated — direct-answer tier disabled or did not fire)\",\n );\n return lines.join(\"\\n\");\n }\n\n lines.push(\"\");\n lines.push(\"--- tier explain ---\");\n for (const line of renderTierExplainTextLines(ex)) {\n lines.push(line);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Adapter: convert a `LastRecallSnapshot` into a best-effort\n * `RecallXraySnapshot` so the markdown renderer can produce a\n * consistent, richly-formatted document for callers that have asked\n * for `markdown` format. The LastRecallSnapshot and the X-ray\n * snapshot share session/namespace/memoryIds; additional X-ray-only\n * fields (filters, score decomposition, graph path, audit id) are\n * left empty because the legacy snapshot doesn't carry them. The\n * renderer handles missing fields gracefully.\n */\n/**\n * Strip backticks, pipes, and newlines from a host-provided value so it\n * cannot escape its enclosing markdown code span, break the surrounding\n * table row, or inject extra rows when it lands in\n * `renderXrayMarkdown`. Applied at the adapter boundary because\n * `LastRecallSnapshot` is hydrated from on-disk JSON without schema\n * validation (codex P2 review on #605).\n *\n * Accepts `unknown` so non-string truthy values (numbers, objects,\n * booleans, arrays) coming from a corrupted snapshot are coerced to\n * the empty string rather than crashing on `.replace(...)`. Callers\n * should treat an empty return as \"drop this field.\"\n */\nfunction sanitizeForMarkdownInline(value: unknown): string {\n if (typeof value !== \"string\") return \"\";\n return value.replace(/[`|\\r\\n]/g, \" \").trim();\n}\n\n/**\n * Map the legacy `LastRecallSnapshot.source` field to the\n * `RecallXrayServedBy` union used by the unified x-ray renderer.\n * Mirrors the `mapRecallSourceToXrayServedBy` helper inside\n * `orchestrator.ts` (which is private). Keep the two in sync when a\n * new source lands — unknown values collapse to `\"hybrid\"` to preserve\n * backwards compatibility with older on-disk snapshots.\n */\nfunction mapLegacySourceToServedBy(\n source: unknown,\n): \"hybrid\" | \"recent-scan\" {\n if (source === \"recent_scan\") return \"recent-scan\";\n return \"hybrid\";\n}\n\nexport function toRecallXraySnapshotFromLegacy(\n snapshot: LastRecallSnapshot | null,\n): RecallXraySnapshot | null {\n if (!snapshot) return null;\n const capturedAt = (() => {\n if (typeof snapshot.recordedAt !== \"string\") return 0;\n const ms = Date.parse(snapshot.recordedAt);\n return Number.isFinite(ms) && ms >= 0 ? ms : 0;\n })();\n const memoryIds = Array.isArray(snapshot.memoryIds)\n ? snapshot.memoryIds.filter((x): x is string => typeof x === \"string\")\n : [];\n // Codex P2 + Cursor Medium on #605: every converted result used to\n // be stamped with `servedBy: \"hybrid\"`, misattributing legacy\n // snapshots that came from `recent_scan` (or any future source).\n // Propagate the recorded `source` so markdown `recall-explain`\n // output matches the `served-by=` string the native x-ray capture\n // would emit for the same recall.\n const servedBy = mapLegacySourceToServedBy(snapshot.source);\n const results: RecallXrayResult[] = memoryIds.map((memoryId) => ({\n memoryId,\n path: \"\",\n servedBy,\n scoreDecomposition: { final: 0 },\n admittedBy: [],\n }));\n const filters: RecallFilterTrace[] = [];\n return {\n schemaVersion: \"1\",\n // `LastRecallSnapshot` does not preserve the original query text;\n // synthesize a placeholder so the renderer has a non-empty\n // string to print. `queryHash` + `queryLen` stay in the JSON\n // payload via `toRecallExplainJson` for callers that need them.\n query:\n snapshot.queryHash\n ? `(legacy explain; queryHash=${snapshot.queryHash})`\n : \"(legacy explain)\",\n // `snapshotId` is synthesized here; `sessionKey` is already\n // sanitized before it reaches the ID because we re-use the\n // sanitized string below.\n snapshotId: `legacy-${sanitizeForMarkdownInline(snapshot.sessionKey ?? \"unknown\") || \"unknown\"}-${capturedAt}`,\n capturedAt,\n // Run the raw on-disk value through the same normalizer the text\n // and JSON paths use so the markdown adapter cannot render\n // unvalidated tier-explain payloads (cursor / codex review on\n // #605). A malformed tierExplain is dropped to null, matching the\n // behavior of the non-markdown surfaces.\n tierExplain: normalizeTierExplain(snapshot.tierExplain) ?? null,\n results,\n appliedResultLimit: 0,\n appliedResults: [],\n headroomResults: [],\n filters,\n budget: { chars: 0, used: 0 },\n // Sanitize legacy session metadata at the adapter boundary so a\n // malformed on-disk value (containing backticks, pipes, or\n // newlines) cannot break the enclosing markdown table when\n // `renderXrayMarkdown` prints it in a raw code-span cell (codex P2\n // review on #605).\n ...(snapshot.sessionKey\n ? (() => {\n const clean = sanitizeForMarkdownInline(snapshot.sessionKey);\n return clean ? { sessionKey: clean } : {};\n })()\n : {}),\n ...(snapshot.namespace\n ? (() => {\n const clean = sanitizeForMarkdownInline(snapshot.namespace);\n return clean ? { namespace: clean } : {};\n })()\n : {}),\n };\n}\n\nexport function renderRecallExplain(\n snapshot: LastRecallSnapshot | null,\n format: RecallExplainFormat,\n): string {\n if (format === \"json\") {\n return JSON.stringify(toRecallExplainJson(snapshot), null, 2);\n }\n if (format === \"markdown\") {\n // Delegate to the shared X-ray renderer so CLI / HTTP / MCP\n // markdown output all share one implementation (CLAUDE.md rule\n // 22). The JSON and text paths remain byte-for-byte\n // backwards-compatible with pre-#570 behavior.\n return renderXrayMarkdown(toRecallXraySnapshotFromLegacy(snapshot));\n }\n return toRecallExplainText(snapshot);\n}\n\nexport function parseRecallExplainFormat(value: unknown): RecallExplainFormat {\n if (value === undefined || value === null) return \"text\";\n if (typeof value !== \"string\") {\n throw new Error(\n `--format expects \"text\", \"json\", or \"markdown\", got ${typeof value}`,\n );\n }\n const v = value.trim().toLowerCase();\n if (v === \"text\" || v === \"json\" || v === \"markdown\") return v;\n throw new Error(\n `--format expects \"text\", \"json\", or \"markdown\", got ${JSON.stringify(value)}`,\n );\n}\n","/**\n * Unified Recall X-ray renderer (issue #570, PR 2).\n *\n * Pure functions that format a `RecallXraySnapshot` for human text,\n * GitHub-flavored markdown, and machine JSON consumption. CLI / HTTP\n * / MCP surfaces all call into this module — they do NOT format X-ray\n * output themselves, so rendering is tested in one place (CLAUDE.md\n * rule 22).\n *\n * Scope for PR 2 (this slice):\n * - Pure rendering. No IO, no transport, no capture.\n * - `renderXray(snapshot, format)` with format ∈\n * `{\"json\", \"text\", \"markdown\"}`.\n * - `parseXrayFormat(value)` — input validator that rejects unknown\n * formats with a listed-options error (CLAUDE.md rule 51).\n * - Golden-file-style tests in `recall-xray-renderer.test.ts`.\n */\n\nimport type {\n RecallFilterTrace,\n RecallXrayResult,\n RecallXraySnapshot,\n RecallXrayServedBy,\n} from \"./recall-xray.js\";\nimport { summarizeDisclosureTokens } from \"./recall-xray.js\";\nimport { renderTierExplainTextLines } from \"./recall-explain-renderer.js\";\nimport { summarizeRetrievedMemoryProvenance } from \"./memory-provenance.js\";\n\nexport type RecallXrayFormat = \"json\" | \"text\" | \"markdown\";\n\nexport const RECALL_XRAY_FORMATS: readonly RecallXrayFormat[] = [\n \"json\",\n \"text\",\n \"markdown\",\n] as const;\n\n/**\n * Validate and coerce a user-provided `--format` / `format` argument to\n * `RecallXrayFormat`. Unknown values throw an error listing valid\n * options (CLAUDE.md rule 51). `undefined`/`null` defaults to `\"text\"`.\n */\nexport function parseXrayFormat(value: unknown): RecallXrayFormat {\n if (value === undefined || value === null) return \"text\";\n if (typeof value !== \"string\") {\n throw new Error(\n `--format expects one of ${RECALL_XRAY_FORMATS.join(\", \")}; got ${typeof value}`,\n );\n }\n const v = value.trim().toLowerCase();\n if (v === \"json\" || v === \"text\" || v === \"markdown\") return v;\n throw new Error(\n `--format expects one of ${RECALL_XRAY_FORMATS.join(\", \")}; got ${JSON.stringify(value)}`,\n );\n}\n\n/**\n * Top-level dispatcher. CLI / HTTP / MCP callers should always route\n * through this function so the three formats stay in lock-step.\n */\nexport function renderXray(\n snapshot: RecallXraySnapshot | null,\n format: RecallXrayFormat,\n): string {\n if (format === \"json\") return renderXrayJson(snapshot);\n if (format === \"markdown\") return renderXrayMarkdown(snapshot);\n return renderXrayText(snapshot);\n}\n\n// ─── JSON ─────────────────────────────────────────────────────────────────\n\n/**\n * Deterministic JSON encoding of an X-ray snapshot. Returns a stable\n * v1 envelope when the snapshot is absent so consumers can pattern-match\n * on `snapshotFound` rather than distinguishing `null` vs `{}`.\n */\nexport function renderXrayJson(snapshot: RecallXraySnapshot | null): string {\n if (!snapshot) {\n return JSON.stringify(\n { schemaVersion: \"1\", snapshotFound: false },\n null,\n 2,\n );\n }\n // `snapshotFound` is injected *before* the rest so downstream JSON\n // consumers see it near the top of the document.\n return JSON.stringify(\n { snapshotFound: true, ...snapshot },\n null,\n 2,\n );\n}\n\n// ─── Text ─────────────────────────────────────────────────────────────────\n\nexport function renderXrayText(snapshot: RecallXraySnapshot | null): string {\n const lines: string[] = [\"=== Recall X-ray ===\"];\n if (!snapshot) {\n lines.push(\"No X-ray snapshot captured.\");\n return lines.join(\"\\n\");\n }\n\n lines.push(`query: ${snapshot.query}`);\n lines.push(`snapshot-id: ${snapshot.snapshotId}`);\n lines.push(`captured-at: ${formatCapturedAt(snapshot.capturedAt)}`);\n if (snapshot.sessionKey) lines.push(`session: ${snapshot.sessionKey}`);\n if (snapshot.namespace) lines.push(`namespace: ${snapshot.namespace}`);\n if (snapshot.traceId) lines.push(`trace-id: ${snapshot.traceId}`);\n lines.push(\n `budget: ${snapshot.budget.used} / ${snapshot.budget.chars} chars`,\n );\n\n lines.push(\"\");\n lines.push(\"--- filters ---\");\n if (snapshot.filters.length === 0) {\n lines.push(\"(no filter traces recorded)\");\n } else {\n for (const f of snapshot.filters) {\n lines.push(renderFilterTextLine(f));\n }\n }\n\n lines.push(\"\");\n lines.push(\"--- results ---\");\n if (snapshot.results.length === 0) {\n lines.push(\"(no results admitted)\");\n } else {\n snapshot.results.forEach((result, idx) => {\n for (const line of renderResultTextLines(result, idx + 1)) {\n lines.push(line);\n }\n });\n }\n\n lines.push(\"\");\n lines.push(\"--- tier explain ---\");\n for (const line of renderTierExplainTextLines(snapshot.tierExplain ?? null)) {\n lines.push(line);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction renderFilterTextLine(f: RecallFilterTrace): string {\n const base = `- ${f.name}: ${f.admitted}/${f.considered} admitted`;\n return f.reason ? `${base} (${f.reason})` : base;\n}\n\nfunction renderResultTextLines(\n result: RecallXrayResult,\n rank: number,\n): string[] {\n const lines: string[] = [];\n lines.push(`[${rank}] ${result.memoryId} — ${servedByLabel(result.servedBy)}`);\n if (result.path) lines.push(` path: ${result.path}`);\n lines.push(` score: ${renderScoreDecomposition(result)}`);\n if (result.provenance) {\n lines.push(\n ` provenance: ${summarizeRetrievedMemoryProvenance(result.provenance)}`,\n );\n lines.push(` retrieval-reason: ${result.provenance.retrievalReason}`);\n if (result.provenance.userContextScopes.length > 0) {\n lines.push(\n ` context-scopes: ${result.provenance.userContextScopes.join(\", \")}`,\n );\n }\n if (\n result.provenance.safety !== \"safe\" ||\n result.provenance.safetyReasons.length > 0\n ) {\n const reasonSuffix =\n result.provenance.safetyReasons.length > 0\n ? ` (${result.provenance.safetyReasons.join(\", \")})`\n : \"\";\n lines.push(` safety: ${result.provenance.safety}${reasonSuffix}`);\n }\n }\n if (result.sourceSpan) {\n const q = truncateForXray(result.sourceSpan.quote, 120);\n lines.push(\n ` source: \"${q}\" (observed ${result.sourceSpan.observedAt}) [${result.sourceSpan.provenance}]`,\n );\n }\n if (result.admittedBy.length > 0) {\n lines.push(` admitted-by: ${result.admittedBy.join(\", \")}`);\n }\n if (result.rejectedBy) {\n lines.push(` rejected-by: ${result.rejectedBy}`);\n }\n if (result.graphPath && result.graphPath.length > 0) {\n lines.push(` graph-path: ${result.graphPath.join(\" -> \")}`);\n // Issue #681 PR 3/3 — surface per-edge confidence inline so\n // operators can attribute floor-pruning / PageRank ranking\n // decisions to specific edges. Skipped when no confidences were\n // recorded (legacy snapshot or single-node path).\n if (\n result.graphEdgeConfidences &&\n result.graphEdgeConfidences.length > 0\n ) {\n lines.push(\n ` edge-confidences: ${result.graphEdgeConfidences\n .map((c) => c.toFixed(2))\n .join(\", \")}`,\n );\n }\n }\n if (result.pathPenaltyApplied !== undefined) {\n lines.push(` path-penalty: ${result.pathPenaltyApplied ? \"yes\" : \"no\"}`);\n }\n if (result.pathNodeIds && result.pathNodeIds.length > 0) {\n lines.push(` path-node-ids: ${result.pathNodeIds.join(\" -> \")}`);\n }\n if (result.auditEntryId) {\n lines.push(` audit-entry: ${result.auditEntryId}`);\n }\n return lines;\n}\n\n// ─── Markdown ─────────────────────────────────────────────────────────────\n\nexport function renderXrayMarkdown(\n snapshot: RecallXraySnapshot | null,\n): string {\n const lines: string[] = [\"# Recall X-ray\"];\n if (!snapshot) {\n lines.push(\"\");\n lines.push(\"_No X-ray snapshot captured._\");\n return lines.join(\"\\n\");\n }\n\n lines.push(\"\");\n lines.push(`**Query:** ${mdInlineCode(snapshot.query)}`);\n lines.push(\"\");\n lines.push(\"| Field | Value |\");\n lines.push(\"| --- | --- |\");\n lines.push(`| Snapshot ID | \\`${snapshot.snapshotId}\\` |`);\n lines.push(`| Captured at | ${formatCapturedAt(snapshot.capturedAt)} |`);\n if (snapshot.sessionKey) {\n lines.push(`| Session | \\`${snapshot.sessionKey}\\` |`);\n }\n if (snapshot.namespace) {\n lines.push(`| Namespace | \\`${snapshot.namespace}\\` |`);\n }\n if (snapshot.traceId) {\n lines.push(`| Trace ID | \\`${snapshot.traceId}\\` |`);\n }\n lines.push(\n `| Budget | ${snapshot.budget.used} / ${snapshot.budget.chars} chars |`,\n );\n\n lines.push(\"\");\n lines.push(\"## Filters\");\n if (snapshot.filters.length === 0) {\n lines.push(\"\");\n lines.push(\"_No filter traces recorded._\");\n } else {\n lines.push(\"\");\n lines.push(\"| Filter | Considered | Admitted | Reason |\");\n lines.push(\"| --- | ---: | ---: | --- |\");\n for (const f of snapshot.filters) {\n const reason = f.reason ? mdEscape(f.reason) : \"\";\n lines.push(`| ${mdEscape(f.name)} | ${f.considered} | ${f.admitted} | ${reason} |`);\n }\n }\n\n lines.push(\"\");\n lines.push(\"## Results\");\n if (snapshot.results.length === 0) {\n lines.push(\"\");\n lines.push(\"_No results admitted._\");\n } else {\n snapshot.results.forEach((result, idx) => {\n for (const line of renderResultMarkdownLines(result, idx + 1)) {\n lines.push(line);\n }\n });\n\n // Per-disclosure token-spend summary (issue #677 PR 3/4). Only\n // emitted when at least one result carries a disclosure level so\n // we don't pollute the snapshot for callers who haven't wired the\n // depth knob through. Counts and tokens default to 0 for buckets\n // with no contributions.\n const summary = summarizeDisclosureTokens(snapshot.results);\n const hasAnyDisclosure =\n summary.chunk.count + summary.section.count + summary.raw.count > 0;\n if (hasAnyDisclosure) {\n lines.push(\"\");\n lines.push(\"### Token spend by disclosure\");\n lines.push(\"\");\n lines.push(\"| Disclosure | Results | Estimated tokens |\");\n lines.push(\"| --- | ---: | ---: |\");\n lines.push(\n `| chunk | ${summary.chunk.count} | ${summary.chunk.estimatedTokens} |`,\n );\n lines.push(\n `| section | ${summary.section.count} | ${summary.section.estimatedTokens} |`,\n );\n lines.push(\n `| raw | ${summary.raw.count} | ${summary.raw.estimatedTokens} |`,\n );\n if (summary.unspecified.count > 0) {\n lines.push(\n `| _(unspecified)_ | ${summary.unspecified.count} | ${summary.unspecified.estimatedTokens} |`,\n );\n }\n }\n }\n\n lines.push(\"\");\n lines.push(\"## Tier Explain\");\n if (!snapshot.tierExplain) {\n lines.push(\"\");\n lines.push(\n \"_Not populated — direct-answer tier disabled or did not fire._\",\n );\n } else {\n const te = snapshot.tierExplain;\n lines.push(\"\");\n lines.push(\"| Field | Value |\");\n lines.push(\"| --- | --- |\");\n lines.push(`| Tier | \\`${te.tier}\\` |`);\n lines.push(`| Reason | ${mdEscape(te.tierReason)} |`);\n lines.push(`| Candidates considered | ${te.candidatesConsidered} |`);\n lines.push(`| Latency (ms) | ${te.latencyMs} |`);\n lines.push(\n `| Filtered by | ${\n te.filteredBy.length > 0\n ? te.filteredBy.map(mdInlineCode).join(\", \")\n : \"_(none)_\"\n } |`,\n );\n if (te.sourceAnchors && te.sourceAnchors.length > 0) {\n lines.push(\"\");\n lines.push(\"**Source anchors:**\");\n for (const anchor of te.sourceAnchors) {\n const range = anchor.lineRange\n ? `:${anchor.lineRange[0]}-${anchor.lineRange[1]}`\n : \"\";\n lines.push(`- \\`${anchor.path}${range}\\``);\n }\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction renderResultMarkdownLines(\n result: RecallXrayResult,\n rank: number,\n): string[] {\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(\n `### ${rank}. \\`${result.memoryId}\\` — ${servedByLabel(result.servedBy)}`,\n );\n if (result.path) {\n lines.push(\"\");\n lines.push(`- **Path:** \\`${result.path}\\``);\n } else {\n lines.push(\"\");\n }\n lines.push(`- **Score:** ${renderScoreDecomposition(result)}`);\n if (result.provenance) {\n lines.push(\n `- **Provenance:** ${mdEscape(summarizeRetrievedMemoryProvenance(result.provenance))}`,\n );\n lines.push(\n `- **Retrieval reason:** ${mdInlineCode(result.provenance.retrievalReason)}`,\n );\n if (result.provenance.userContextScopes.length > 0) {\n lines.push(\n `- **Context scopes:** ${result.provenance.userContextScopes\n .map(mdInlineCode)\n .join(\", \")}`,\n );\n }\n if (\n result.provenance.safety !== \"safe\" ||\n result.provenance.safetyReasons.length > 0\n ) {\n const reasonSuffix =\n result.provenance.safetyReasons.length > 0\n ? ` (${result.provenance.safetyReasons.map(mdInlineCode).join(\", \")})`\n : \"\";\n lines.push(\n `- **Safety:** ${mdInlineCode(result.provenance.safety)}${reasonSuffix}`,\n );\n }\n }\n if (result.sourceSpan) {\n const q = truncateForXray(result.sourceSpan.quote, 120);\n lines.push(\n `- **Source:** \"${mdEscape(q)}\" (${mdInlineCode(result.sourceSpan.observedAt)}) \\u2014 ${result.sourceSpan.provenance}`,\n );\n }\n if (result.admittedBy.length > 0) {\n lines.push(\n `- **Admitted by:** ${result.admittedBy.map(mdInlineCode).join(\", \")}`,\n );\n }\n if (result.rejectedBy) {\n lines.push(`- **Rejected by:** ${mdInlineCode(result.rejectedBy)}`);\n }\n if (result.graphPath && result.graphPath.length > 0) {\n lines.push(\n `- **Graph path:** ${result.graphPath\n .map(mdInlineCode)\n .join(\" → \")}`,\n );\n // Issue #681 PR 3/3 — render per-edge confidences as a parallel\n // markdown line so operators can correlate them with the path\n // arrows above. Skipped when no confidences were recorded.\n if (\n result.graphEdgeConfidences &&\n result.graphEdgeConfidences.length > 0\n ) {\n lines.push(\n `- **Edge confidences:** ${result.graphEdgeConfidences\n .map((c) => mdInlineCode(c.toFixed(2)))\n .join(\", \")}`,\n );\n }\n }\n if (result.pathPenaltyApplied !== undefined) {\n lines.push(\n `- **Path penalty applied:** ${mdInlineCode(result.pathPenaltyApplied ? \"yes\" : \"no\")}`,\n );\n }\n if (result.pathNodeIds && result.pathNodeIds.length > 0) {\n lines.push(\n `- **Path node ids:** ${result.pathNodeIds.map(mdInlineCode).join(\" → \")}`,\n );\n }\n if (result.auditEntryId) {\n lines.push(`- **Audit entry:** \\`${result.auditEntryId}\\``);\n }\n if (result.disclosure !== undefined) {\n const tokenLine =\n typeof result.estimatedTokens === \"number\"\n ? ` (~${result.estimatedTokens} tokens)`\n : \"\";\n lines.push(`- **Disclosure:** \\`${result.disclosure}\\`${tokenLine}`);\n } else if (typeof result.estimatedTokens === \"number\") {\n // Disclosure unspecified but tokens recorded — still surface the\n // budget so the operator can attribute spend.\n lines.push(`- **Estimated tokens:** ${result.estimatedTokens}`);\n }\n return lines;\n}\n\n// ─── Shared helpers ───────────────────────────────────────────────────────\n\nfunction servedByLabel(servedBy: RecallXrayServedBy): string {\n return `served-by=${servedBy}`;\n}\n\nfunction renderScoreDecomposition(result: RecallXrayResult): string {\n const parts: string[] = [`final=${formatScore(result.scoreDecomposition.final)}`];\n const s = result.scoreDecomposition;\n if (s.vector !== undefined) parts.push(`vector=${formatScore(s.vector)}`);\n if (s.bm25 !== undefined) parts.push(`bm25=${formatScore(s.bm25)}`);\n if (s.importance !== undefined) {\n parts.push(`importance=${formatScore(s.importance)}`);\n }\n if (s.mmrPenalty !== undefined) {\n parts.push(`mmr_penalty=${formatScore(s.mmrPenalty)}`);\n }\n if (s.tierPrior !== undefined) {\n parts.push(`tier_prior=${formatScore(s.tierPrior)}`);\n }\n if (s.reinforcementBoost !== undefined && s.reinforcementBoost > 0) {\n parts.push(`reinforcement_boost=${formatScore(s.reinforcementBoost)}`);\n }\n return parts.join(\" \");\n}\n\nfunction formatScore(value: number): string {\n // Deterministic 4-decimal formatting keeps golden files stable\n // without printing spurious trailing zeros via toString().\n if (!Number.isFinite(value)) return \"0.0000\";\n return value.toFixed(4);\n}\n\nfunction formatCapturedAt(ts: number): string {\n if (!Number.isFinite(ts) || ts < 0) return \"(unknown)\";\n // `new Date(n).toISOString()` throws a RangeError for finite numbers\n // outside the valid Date range (roughly |n| > 8.64e15). That case\n // can surface when snapshots are corrupted or captured with a\n // custom clock, so coerce it to the same \"(unknown)\" fallback\n // rather than crashing the renderer.\n try {\n return new Date(ts).toISOString();\n } catch {\n return \"(unknown)\";\n }\n}\n\nfunction mdEscape(value: string): string {\n // Pipe is the only character that breaks GFM table rendering; escape\n // backslash first so we do not re-escape the escape character.\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\\|/g, \"\\\\|\");\n}\n\n/**\n * Truncate a quote for X-ray display (issue #1575 PR 3). Collapses internal\n * whitespace runs (including newlines/carriage returns) to single spaces so\n * a multi-line source excerpt cannot split what is meant to be a single X-ray\n * row, then caps at `maxChars` on a word boundary with an ellipsis marker.\n * The JSON \\`sourceSpan.quote\\` stays verbatim — this is display-only.\n */\nfunction truncateForXray(text: string, maxChars: number): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n if (collapsed.length <= maxChars) return collapsed;\n const slice = collapsed.slice(0, maxChars);\n const lastSpace = slice.lastIndexOf(\" \");\n const cut = lastSpace > Math.floor(maxChars * 0.5) ? slice.slice(0, lastSpace) : slice;\n return `${cut}\\u2026`;\n}\n\nfunction mdInlineCode(value: string): string {\n if (value.length === 0) return \"``\";\n // Use exactly enough backticks to unambiguously wrap content that\n // itself contains backticks (GFM rule).\n const longestRun = /`+/g;\n let maxLen = 0;\n for (const match of value.matchAll(longestRun)) {\n if (match[0].length > maxLen) maxLen = match[0].length;\n }\n const fence = \"`\".repeat(maxLen + 1);\n const pad = value.startsWith(\"`\") || value.endsWith(\"`\") ? \" \" : \"\";\n return `${fence}${pad}${value}${pad}${fence}`;\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  graphsDir,
4
4
  readEdgesStrict,
5
5
  withGraphWriteLock
6
- } from "./chunk-L2DPF4DF.js";
6
+ } from "./chunk-BIJAWZYW.js";
7
7
  import {
8
8
  DEFAULT_DECAY_FLOOR,
9
9
  DEFAULT_DECAY_PER_WINDOW,
@@ -203,4 +203,4 @@ export {
203
203
  discoverGraphNamespaceRoots,
204
204
  runGraphEdgeDecayMaintenanceAcrossNamespaces
205
205
  };
206
- //# sourceMappingURL=chunk-HNZKVLAN.js.map
206
+ //# sourceMappingURL=chunk-S354QO4R.js.map
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-RZRUV6N2.js";
11
11
  import {
12
12
  deriveSourceConnector
13
- } from "./chunk-N3LL5NEU.js";
13
+ } from "./chunk-ZMYDYNI4.js";
14
14
  import {
15
15
  resolvePipelineProcessingCapabilities
16
16
  } from "./chunk-2XRE6FLM.js";
@@ -108,4 +108,4 @@ export {
108
108
  renderExtractionConversation,
109
109
  applyGroundingWithConnector
110
110
  };
111
- //# sourceMappingURL=chunk-NO37MR45.js.map
111
+ //# sourceMappingURL=chunk-SOEN6KCF.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-BOFVT6P3.js";
3
+ } from "./chunk-VBHPOWBJ.js";
4
4
  import {
5
5
  bumpMemoryCorpusVersionForDir
6
6
  } from "./chunk-KMAP7SNQ.js";
@@ -759,4 +759,4 @@ export {
759
759
  listMemoryGovernanceRuns,
760
760
  readMemoryGovernanceRunArtifact
761
761
  };
762
- //# sourceMappingURL=chunk-FXX6RQYC.js.map
762
+ //# sourceMappingURL=chunk-TPTECFWR.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-KJTKLXTH.js";
4
4
  import {
5
5
  StorageManager
6
- } from "./chunk-BOFVT6P3.js";
6
+ } from "./chunk-VBHPOWBJ.js";
7
7
  import {
8
8
  copyExistingFileToBackup,
9
9
  writeFileAtomically
@@ -260,4 +260,4 @@ export {
260
260
  backupExistingLedger,
261
261
  rebuildMemoryLifecycleLedger
262
262
  };
263
- //# sourceMappingURL=chunk-GDDGZVFI.js.map
263
+ //# sourceMappingURL=chunk-UEVLYVNW.js.map