@remnic/core 1.1.0 → 1.1.2

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 (308) hide show
  1. package/dist/access-audit.d.ts +56 -0
  2. package/dist/access-audit.js +9 -0
  3. package/dist/access-cli.js +70 -53
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +16 -9
  6. package/dist/access-http.js +26 -18
  7. package/dist/access-mcp.d.ts +16 -9
  8. package/dist/access-mcp.js +30 -8
  9. package/dist/access-schema.d.ts +124 -33
  10. package/dist/access-schema.js +5 -1
  11. package/dist/{access-service-HmO1Trrx.d.ts → access-service-Br8ZydTK.d.ts} +158 -63
  12. package/dist/access-service.d.ts +13 -6
  13. package/dist/access-service.js +23 -14
  14. package/dist/bootstrap.d.ts +6 -3
  15. package/dist/briefing.d.ts +1 -0
  16. package/dist/briefing.js +8 -6
  17. package/dist/buffer-surprise-report.d.ts +70 -0
  18. package/dist/buffer-surprise-report.js +7 -0
  19. package/dist/buffer-surprise-report.js.map +1 -0
  20. package/dist/buffer-surprise.d.ts +98 -0
  21. package/dist/buffer-surprise.js +11 -0
  22. package/dist/buffer-surprise.js.map +1 -0
  23. package/dist/buffer.d.ts +100 -2
  24. package/dist/buffer.js +1 -1
  25. package/dist/calibration.js +6 -6
  26. package/dist/causal-behavior.js +4 -4
  27. package/dist/causal-chain.js +2 -2
  28. package/dist/causal-consolidation.js +19 -18
  29. package/dist/causal-consolidation.js.map +1 -1
  30. package/dist/causal-retrieval.js +4 -4
  31. package/dist/causal-trajectory.js +1 -1
  32. package/dist/{chunk-QNJMBKFK.js → chunk-2LGMW3DJ.js} +3 -2
  33. package/dist/chunk-2LGMW3DJ.js.map +1 -0
  34. package/dist/{chunk-QDYXG4CS.js → chunk-3FPTCC3Z.js} +4 -3
  35. package/dist/chunk-3FPTCC3Z.js.map +1 -0
  36. package/dist/chunk-3GPTTA4J.js +57 -0
  37. package/dist/chunk-3GPTTA4J.js.map +1 -0
  38. package/dist/{chunk-ITRLGI2T.js → chunk-3OGMS3PE.js} +2 -2
  39. package/dist/{chunk-DEPL3635.js → chunk-3YGHKTBF.js} +1446 -196
  40. package/dist/chunk-3YGHKTBF.js.map +1 -0
  41. package/dist/{chunk-BLKTA7MM.js → chunk-4HQS2HPX.js} +54 -21
  42. package/dist/chunk-4HQS2HPX.js.map +1 -0
  43. package/dist/chunk-54V4BZWP.js +139 -0
  44. package/dist/chunk-54V4BZWP.js.map +1 -0
  45. package/dist/chunk-5JRF2PZA.js +67 -0
  46. package/dist/chunk-5JRF2PZA.js.map +1 -0
  47. package/dist/chunk-64NJRYU2.js +332 -0
  48. package/dist/chunk-64NJRYU2.js.map +1 -0
  49. package/dist/{chunk-OIT5QGG4.js → chunk-6AUUAZEX.js} +72 -2
  50. package/dist/chunk-6AUUAZEX.js.map +1 -0
  51. package/dist/{chunk-3QHL5ABG.js → chunk-6YJHX2DL.js} +191 -10
  52. package/dist/chunk-6YJHX2DL.js.map +1 -0
  53. package/dist/chunk-AJU4PJGY.js +126 -0
  54. package/dist/chunk-AJU4PJGY.js.map +1 -0
  55. package/dist/chunk-ASAITVLA.js +64 -0
  56. package/dist/chunk-ASAITVLA.js.map +1 -0
  57. package/dist/{chunk-44ICJRF3.js → chunk-AYXIPSZO.js} +5 -5
  58. package/dist/{chunk-MBJHSA7F.js → chunk-BECYBZLX.js} +265 -20
  59. package/dist/chunk-BECYBZLX.js.map +1 -0
  60. package/dist/chunk-C4SQJZAF.js +486 -0
  61. package/dist/chunk-C4SQJZAF.js.map +1 -0
  62. package/dist/{chunk-6UJ47TVX.js → chunk-CUPFXL3J.js} +2 -2
  63. package/dist/chunk-DF3RVK3X.js +119 -0
  64. package/dist/chunk-DF3RVK3X.js.map +1 -0
  65. package/dist/{chunk-N42IWANG.js → chunk-DG6YMRDC.js} +3 -3
  66. package/dist/chunk-DGVM5SFL.js +69 -0
  67. package/dist/chunk-DGVM5SFL.js.map +1 -0
  68. package/dist/{chunk-3SV6CQHO.js → chunk-DIXB44VE.js} +102 -66
  69. package/dist/chunk-DIXB44VE.js.map +1 -0
  70. package/dist/chunk-EIR5VLIH.js +90 -0
  71. package/dist/chunk-EIR5VLIH.js.map +1 -0
  72. package/dist/{chunk-GV6NLQ4X.js → chunk-F5VP6YCB.js} +374 -16
  73. package/dist/chunk-F5VP6YCB.js.map +1 -0
  74. package/dist/{chunk-6ZH4TU6I.js → chunk-FAAFWE4G.js} +2 -1
  75. package/dist/chunk-FAAFWE4G.js.map +1 -0
  76. package/dist/{chunk-7WQ6SLIE.js → chunk-FVA6TGI3.js} +2 -2
  77. package/dist/{chunk-PAORGQRI.js → chunk-GA5P7RST.js} +37 -23
  78. package/dist/chunk-GA5P7RST.js.map +1 -0
  79. package/dist/chunk-GDFS42HT.js +206 -0
  80. package/dist/chunk-GDFS42HT.js.map +1 -0
  81. package/dist/chunk-IISBCCWR.js +52 -0
  82. package/dist/chunk-IISBCCWR.js.map +1 -0
  83. package/dist/chunk-JBMSGZEQ.js +441 -0
  84. package/dist/chunk-JBMSGZEQ.js.map +1 -0
  85. package/dist/{chunk-J4IYOZZ5.js → chunk-JXS5PDQ7.js} +3 -1
  86. package/dist/chunk-JXS5PDQ7.js.map +1 -0
  87. package/dist/chunk-KVBLZUKV.js +173 -0
  88. package/dist/chunk-KVBLZUKV.js.map +1 -0
  89. package/dist/{chunk-4LACOVZX.js → chunk-L7IXWRYE.js} +10 -5
  90. package/dist/chunk-L7IXWRYE.js.map +1 -0
  91. package/dist/chunk-LBLXEFWK.js +51 -0
  92. package/dist/chunk-LBLXEFWK.js.map +1 -0
  93. package/dist/{chunk-WBSAYXVI.js → chunk-LOIMBRDE.js} +201 -45
  94. package/dist/chunk-LOIMBRDE.js.map +1 -0
  95. package/dist/{chunk-3WHVNEN7.js → chunk-LTCGGW2D.js} +1 -1
  96. package/dist/chunk-LTCGGW2D.js.map +1 -0
  97. package/dist/{chunk-ZVBB3T7V.js → chunk-NBVAS5MT.js} +25 -23
  98. package/dist/chunk-NBVAS5MT.js.map +1 -0
  99. package/dist/{chunk-UEYA6UC7.js → chunk-NZLQTHS5.js} +25 -2
  100. package/dist/chunk-NZLQTHS5.js.map +1 -0
  101. package/dist/{chunk-NQEVYWX6.js → chunk-OC5OXUQ4.js} +211 -7
  102. package/dist/chunk-OC5OXUQ4.js.map +1 -0
  103. package/dist/{chunk-LK6SGL53.js → chunk-OR64ZGRZ.js} +3 -2
  104. package/dist/chunk-OR64ZGRZ.js.map +1 -0
  105. package/dist/{chunk-SYUK3VLY.js → chunk-PVICZTKG.js} +117 -5
  106. package/dist/chunk-PVICZTKG.js.map +1 -0
  107. package/dist/chunk-PVPWZSSI.js +37 -0
  108. package/dist/chunk-PVPWZSSI.js.map +1 -0
  109. package/dist/{chunk-JL2PU6AI.js → chunk-R2XRID2N.js} +2 -2
  110. package/dist/{chunk-4NRAJUDS.js → chunk-RBBWYEFJ.js} +1 -1
  111. package/dist/chunk-RFYAYKTD.js +146 -0
  112. package/dist/chunk-RFYAYKTD.js.map +1 -0
  113. package/dist/chunk-SOBJ6NEY.js +18 -0
  114. package/dist/chunk-SOBJ6NEY.js.map +1 -0
  115. package/dist/{chunk-JIU55F3X.js → chunk-SPI27QT6.js} +2 -2
  116. package/dist/{chunk-MVTHXUBX.js → chunk-STGWEHYR.js} +479 -20
  117. package/dist/chunk-STGWEHYR.js.map +1 -0
  118. package/dist/{chunk-6LX5ORAS.js → chunk-TMYO7B5P.js} +4 -4
  119. package/dist/chunk-TVVEYCNW.js +65 -0
  120. package/dist/chunk-TVVEYCNW.js.map +1 -0
  121. package/dist/chunk-ULYOGL6R.js +322 -0
  122. package/dist/chunk-ULYOGL6R.js.map +1 -0
  123. package/dist/{chunk-37UIFYWO.js → chunk-UWB5LMWY.js} +108 -9
  124. package/dist/chunk-UWB5LMWY.js.map +1 -0
  125. package/dist/{chunk-47UU5PU2.js → chunk-VBVG2M5G.js} +18 -3
  126. package/dist/chunk-VBVG2M5G.js.map +1 -0
  127. package/dist/{chunk-7ECD5ATE.js → chunk-VDX363PS.js} +2 -2
  128. package/dist/{chunk-O5ETUNBT.js → chunk-VTU2B4VF.js} +7 -3
  129. package/dist/chunk-VTU2B4VF.js.map +1 -0
  130. package/dist/{chunk-MTLYEMJB.js → chunk-WCLICCGB.js} +18 -3
  131. package/dist/chunk-WCLICCGB.js.map +1 -0
  132. package/dist/chunk-X6GF3FX2.js +26 -0
  133. package/dist/chunk-X6GF3FX2.js.map +1 -0
  134. package/dist/{chunk-3QFQGRHO.js → chunk-XMHBH5H6.js} +4 -4
  135. package/dist/{chunk-DHHP2Z4X.js → chunk-XXVWLXSG.js} +2 -2
  136. package/dist/{chunk-XZ2TIKGC.js → chunk-Y7R2XJ5Q.js} +25 -9
  137. package/dist/chunk-Y7R2XJ5Q.js.map +1 -0
  138. package/dist/{chunk-ALXMCZEU.js → chunk-Z2E7VW55.js} +6 -3
  139. package/dist/chunk-Z2E7VW55.js.map +1 -0
  140. package/dist/chunk-ZAIM4TUE.js +488 -0
  141. package/dist/chunk-ZAIM4TUE.js.map +1 -0
  142. package/dist/chunk-ZZTOURJI.js +91 -0
  143. package/dist/chunk-ZZTOURJI.js.map +1 -0
  144. package/dist/{cli-BneVIEvh.d.ts → cli-BkeRaYfk.d.ts} +2 -2
  145. package/dist/cli.d.ts +13 -6
  146. package/dist/cli.js +42 -31
  147. package/dist/config.js +2 -2
  148. package/dist/consolidation-operator.d.ts +41 -0
  149. package/dist/consolidation-operator.js +11 -0
  150. package/dist/consolidation-operator.js.map +1 -0
  151. package/dist/consolidation-provenance-check.d.ts +68 -0
  152. package/dist/consolidation-provenance-check.js +9 -0
  153. package/dist/consolidation-provenance-check.js.map +1 -0
  154. package/dist/consolidation-undo.d.ts +123 -0
  155. package/dist/consolidation-undo.js +426 -0
  156. package/dist/consolidation-undo.js.map +1 -0
  157. package/dist/{contradiction-scan-GR33PONM.js → contradiction-scan-E3GJTI4F.js} +43 -7
  158. package/dist/contradiction-scan-E3GJTI4F.js.map +1 -0
  159. package/dist/cross-namespace-budget.d.ts +133 -0
  160. package/dist/cross-namespace-budget.js +9 -0
  161. package/dist/cross-namespace-budget.js.map +1 -0
  162. package/dist/direct-answer-wiring.js +5 -70
  163. package/dist/direct-answer-wiring.js.map +1 -1
  164. package/dist/embedding-fallback.js +2 -1
  165. package/dist/{engine-5TIQBYZR.js → engine-72LSIWQP.js} +8 -7
  166. package/dist/engine-72LSIWQP.js.map +1 -0
  167. package/dist/entity-retrieval.d.ts +1 -0
  168. package/dist/entity-retrieval.js +7 -6
  169. package/dist/explicit-capture.d.ts +6 -3
  170. package/dist/explicit-capture.js +2 -2
  171. package/dist/extraction-judge-telemetry.d.ts +113 -0
  172. package/dist/extraction-judge-telemetry.js +14 -0
  173. package/dist/extraction-judge-telemetry.js.map +1 -0
  174. package/dist/extraction-judge-training.d.ts +85 -0
  175. package/dist/extraction-judge-training.js +16 -0
  176. package/dist/extraction-judge-training.js.map +1 -0
  177. package/dist/extraction-judge.d.ts +124 -2
  178. package/dist/extraction-judge.js +11 -1
  179. package/dist/extraction.js +10 -9
  180. package/dist/fallback-llm.js +3 -3
  181. package/dist/graph-recall.d.ts +100 -0
  182. package/dist/graph-recall.js +8 -0
  183. package/dist/graph-recall.js.map +1 -0
  184. package/dist/graph-retrieval.d.ts +271 -0
  185. package/dist/graph-retrieval.js +21 -0
  186. package/dist/graph-retrieval.js.map +1 -0
  187. package/dist/importance.js +1 -1
  188. package/dist/index.d.ts +585 -20
  189. package/dist/index.js +542 -344
  190. package/dist/index.js.map +1 -1
  191. package/dist/local-llm.js +2 -2
  192. package/dist/memory-worth-bench.d.ts +51 -0
  193. package/dist/memory-worth-bench.js +131 -0
  194. package/dist/memory-worth-bench.js.map +1 -0
  195. package/dist/memory-worth-filter.d.ts +128 -0
  196. package/dist/memory-worth-filter.js +10 -0
  197. package/dist/memory-worth-filter.js.map +1 -0
  198. package/dist/memory-worth-outcomes.d.ts +118 -0
  199. package/dist/memory-worth-outcomes.js +9 -0
  200. package/dist/memory-worth-outcomes.js.map +1 -0
  201. package/dist/memory-worth.d.ts +102 -0
  202. package/dist/memory-worth.js +7 -0
  203. package/dist/memory-worth.js.map +1 -0
  204. package/dist/operator-toolkit.d.ts +40 -1
  205. package/dist/operator-toolkit.js +25 -16
  206. package/dist/{orchestrator-DRYA6_lW.d.ts → orchestrator-CmJ-NTdJ.d.ts} +233 -8
  207. package/dist/orchestrator.d.ts +6 -3
  208. package/dist/orchestrator.js +54 -44
  209. package/dist/page-versioning.d.ts +12 -1
  210. package/dist/page-versioning.js +5 -3
  211. package/dist/{port-C1GZFv8h.d.ts → port-BADbLZU5.d.ts} +2 -2
  212. package/dist/qmd-recall-cache.d.ts +1 -1
  213. package/dist/qmd.d.ts +5 -3
  214. package/dist/qmd.js +3 -3
  215. package/dist/reasoning-trace-recall.d.ts +90 -0
  216. package/dist/reasoning-trace-recall.js +13 -0
  217. package/dist/reasoning-trace-recall.js.map +1 -0
  218. package/dist/reasoning-trace-types.d.ts +54 -0
  219. package/dist/reasoning-trace-types.js +17 -0
  220. package/dist/reasoning-trace-types.js.map +1 -0
  221. package/dist/recall-audit-anomaly.d.ts +112 -0
  222. package/dist/recall-audit-anomaly.js +11 -0
  223. package/dist/recall-audit-anomaly.js.map +1 -0
  224. package/dist/recall-audit.js +5 -44
  225. package/dist/recall-audit.js.map +1 -1
  226. package/dist/recall-explain-renderer.d.ts +49 -0
  227. package/dist/recall-explain-renderer.js +18 -0
  228. package/dist/recall-explain-renderer.js.map +1 -0
  229. package/dist/recall-state.d.ts +12 -1
  230. package/dist/recall-state.js +1 -1
  231. package/dist/recall-xray-cli.d.ts +40 -0
  232. package/dist/recall-xray-cli.js +11 -0
  233. package/dist/recall-xray-cli.js.map +1 -0
  234. package/dist/recall-xray-renderer.d.ts +44 -0
  235. package/dist/recall-xray-renderer.js +18 -0
  236. package/dist/recall-xray-renderer.js.map +1 -0
  237. package/dist/recall-xray.d.ts +179 -0
  238. package/dist/recall-xray.js +13 -0
  239. package/dist/recall-xray.js.map +1 -0
  240. package/dist/resolve-provider-secret.d.ts +5 -1
  241. package/dist/resolve-provider-secret.js +3 -1
  242. package/dist/resume-bundles.js +6 -6
  243. package/dist/retrieval-agents.d.ts +1 -1
  244. package/dist/retrieval-tiers.d.ts +17 -0
  245. package/dist/retrieval-tiers.js +9 -0
  246. package/dist/retrieval-tiers.js.map +1 -0
  247. package/dist/schemas.d.ts +309 -53
  248. package/dist/schemas.js +1 -1
  249. package/dist/{semantic-consolidation-DrvSYRdB.d.ts → semantic-consolidation-CxJU6MJk.d.ts} +62 -1
  250. package/dist/semantic-consolidation.d.ts +2 -1
  251. package/dist/semantic-consolidation.js +22 -7
  252. package/dist/semantic-rule-promotion.js +7 -6
  253. package/dist/semantic-rule-verifier.js +7 -6
  254. package/dist/storage.d.ts +82 -1
  255. package/dist/storage.js +6 -5
  256. package/dist/summarizer.js +6 -6
  257. package/dist/temporal-supersession.d.ts +1 -0
  258. package/dist/tier-migration.d.ts +2 -1
  259. package/dist/tokens.js +2 -1
  260. package/dist/types.d.ts +276 -2
  261. package/dist/types.js +1 -1
  262. package/dist/verified-recall.js +7 -6
  263. package/package.json +1 -1
  264. package/dist/chunk-37UIFYWO.js.map +0 -1
  265. package/dist/chunk-3QHL5ABG.js.map +0 -1
  266. package/dist/chunk-3SV6CQHO.js.map +0 -1
  267. package/dist/chunk-3WHVNEN7.js.map +0 -1
  268. package/dist/chunk-47UU5PU2.js.map +0 -1
  269. package/dist/chunk-4LACOVZX.js.map +0 -1
  270. package/dist/chunk-6ZH4TU6I.js.map +0 -1
  271. package/dist/chunk-ALXMCZEU.js.map +0 -1
  272. package/dist/chunk-BLKTA7MM.js.map +0 -1
  273. package/dist/chunk-DEPL3635.js.map +0 -1
  274. package/dist/chunk-GV6NLQ4X.js.map +0 -1
  275. package/dist/chunk-J4IYOZZ5.js.map +0 -1
  276. package/dist/chunk-LAYN4LDC.js +0 -267
  277. package/dist/chunk-LAYN4LDC.js.map +0 -1
  278. package/dist/chunk-LK6SGL53.js.map +0 -1
  279. package/dist/chunk-MBJHSA7F.js.map +0 -1
  280. package/dist/chunk-MTLYEMJB.js.map +0 -1
  281. package/dist/chunk-MVTHXUBX.js.map +0 -1
  282. package/dist/chunk-NQEVYWX6.js.map +0 -1
  283. package/dist/chunk-O5ETUNBT.js.map +0 -1
  284. package/dist/chunk-OIT5QGG4.js.map +0 -1
  285. package/dist/chunk-PAORGQRI.js.map +0 -1
  286. package/dist/chunk-QDYXG4CS.js.map +0 -1
  287. package/dist/chunk-QNJMBKFK.js.map +0 -1
  288. package/dist/chunk-SYUK3VLY.js.map +0 -1
  289. package/dist/chunk-UEYA6UC7.js.map +0 -1
  290. package/dist/chunk-UVJFDP7P.js +0 -202
  291. package/dist/chunk-UVJFDP7P.js.map +0 -1
  292. package/dist/chunk-WBSAYXVI.js.map +0 -1
  293. package/dist/chunk-XZ2TIKGC.js.map +0 -1
  294. package/dist/chunk-ZVBB3T7V.js.map +0 -1
  295. package/dist/contradiction-scan-GR33PONM.js.map +0 -1
  296. /package/dist/{engine-5TIQBYZR.js.map → access-audit.js.map} +0 -0
  297. /package/dist/{chunk-ITRLGI2T.js.map → chunk-3OGMS3PE.js.map} +0 -0
  298. /package/dist/{chunk-44ICJRF3.js.map → chunk-AYXIPSZO.js.map} +0 -0
  299. /package/dist/{chunk-6UJ47TVX.js.map → chunk-CUPFXL3J.js.map} +0 -0
  300. /package/dist/{chunk-N42IWANG.js.map → chunk-DG6YMRDC.js.map} +0 -0
  301. /package/dist/{chunk-7WQ6SLIE.js.map → chunk-FVA6TGI3.js.map} +0 -0
  302. /package/dist/{chunk-JL2PU6AI.js.map → chunk-R2XRID2N.js.map} +0 -0
  303. /package/dist/{chunk-4NRAJUDS.js.map → chunk-RBBWYEFJ.js.map} +0 -0
  304. /package/dist/{chunk-JIU55F3X.js.map → chunk-SPI27QT6.js.map} +0 -0
  305. /package/dist/{chunk-6LX5ORAS.js.map → chunk-TMYO7B5P.js.map} +0 -0
  306. /package/dist/{chunk-7ECD5ATE.js.map → chunk-VDX363PS.js.map} +0 -0
  307. /package/dist/{chunk-3QFQGRHO.js.map → chunk-XMHBH5H6.js.map} +0 -0
  308. /package/dist/{chunk-DHHP2Z4X.js.map → chunk-XXVWLXSG.js.map} +0 -0
@@ -9,13 +9,13 @@ import {
9
9
  resolveObjectiveStateStoreDir,
10
10
  validateObjectiveStateSnapshot
11
11
  } from "./chunk-LOBRX7VD.js";
12
+ import {
13
+ parseConfig
14
+ } from "./chunk-BECYBZLX.js";
12
15
  import {
13
16
  resolveCommitmentLedgerDir,
14
17
  validateCommitmentLedgerEntry
15
18
  } from "./chunk-FYIYMQ5N.js";
16
- import {
17
- parseConfig
18
- } from "./chunk-MBJHSA7F.js";
19
19
  import {
20
20
  assertIsoRecordedAt,
21
21
  assertSafePathSegment,
@@ -268,4 +268,4 @@ export {
268
268
  recordResumeBundle,
269
269
  getResumeBundleStatus
270
270
  };
271
- //# sourceMappingURL=chunk-6LX5ORAS.js.map
271
+ //# sourceMappingURL=chunk-TMYO7B5P.js.map
@@ -0,0 +1,65 @@
1
+ import {
2
+ detectRecallAnomalies
3
+ } from "./chunk-RFYAYKTD.js";
4
+ import {
5
+ appendRecallAuditEntry
6
+ } from "./chunk-LBLXEFWK.js";
7
+
8
+ // src/access-audit.ts
9
+ var AccessAuditAdapter = class {
10
+ constructor(config) {
11
+ this.config = config;
12
+ const n = config.trailBufferSize;
13
+ if (typeof n === "number" && Number.isFinite(n) && n > 0) {
14
+ const floored = Math.floor(n);
15
+ this.trailBufferSize = floored >= 1 ? floored : 256;
16
+ } else {
17
+ this.trailBufferSize = 256;
18
+ }
19
+ }
20
+ config;
21
+ trails = /* @__PURE__ */ new Map();
22
+ trailBufferSize;
23
+ /**
24
+ * Record an audit entry and (when enabled) run the anomaly detector
25
+ * over the principal's tail of entries. The principal key is used
26
+ * purely for tail-buffer bucketing — it need not match the production
27
+ * namespace resolver; sessionKey is the safe default.
28
+ */
29
+ async record(principalKey, entry, now = Date.now()) {
30
+ const result = {};
31
+ if (this.config.detection.enabled) {
32
+ const key = principalKey.length > 0 ? principalKey : "__anonymous__";
33
+ const tail = this.trails.get(key) ?? { entries: [] };
34
+ tail.entries.push(entry);
35
+ if (tail.entries.length > this.trailBufferSize) {
36
+ tail.entries.splice(0, tail.entries.length - this.trailBufferSize);
37
+ }
38
+ this.trails.set(key, tail);
39
+ result.anomalies = detectRecallAnomalies({
40
+ entries: tail.entries,
41
+ now,
42
+ config: this.config.detection
43
+ });
44
+ }
45
+ if (this.config.audit.enabled) {
46
+ try {
47
+ result.appendedAt = await appendRecallAuditEntry(
48
+ this.config.audit.rootDir,
49
+ entry
50
+ );
51
+ } catch {
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+ /** Clear all in-memory tail state. Intended for tests / before_reset. */
57
+ reset() {
58
+ this.trails.clear();
59
+ }
60
+ };
61
+
62
+ export {
63
+ AccessAuditAdapter
64
+ };
65
+ //# sourceMappingURL=chunk-TVVEYCNW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/access-audit.ts"],"sourcesContent":["/**\n * Access-layer audit adapter (issue #565 PR 5/5).\n *\n * Wraps `appendRecallAuditEntry` + `detectRecallAnomalies` into a single\n * entry point that MCP (`access-mcp.ts`) and HTTP (`access-http.ts`)\n * surfaces can call once per recall. Closes the gap called out in the\n * memory-extraction threat model §5: recall-audit previously only ran\n * on the Openclaw hook, so MCP/HTTP callers bypassed the trail entirely.\n *\n * The adapter is a per-instance class so the tail-of-trail buffer\n * (used by the anomaly detector) is scoped per-service, not global.\n *\n * No I/O unless `audit.enabled` is true; no detector invocation unless\n * `detection.enabled` is true. Either flag may be enabled independently.\n */\n\nimport {\n appendRecallAuditEntry,\n type RecallAuditEntry,\n} from \"./recall-audit.js\";\nimport {\n detectRecallAnomalies,\n type AnomalyDetectorConfig,\n type AnomalyDetectorResult,\n} from \"./recall-audit-anomaly.js\";\n\nexport interface AccessAuditConfig {\n audit: {\n enabled: boolean;\n /** Root directory the audit adapter writes JSONL shards into. */\n rootDir: string;\n };\n detection: AnomalyDetectorConfig;\n /**\n * How many entries the adapter retains in memory for the detector.\n * Defaults to 256 — enough to cover the threat model's default 5-minute\n * window at high recall rates without unbounded growth.\n */\n trailBufferSize?: number;\n}\n\nexport interface AccessAuditResult {\n /** Path of the JSONL shard the entry was appended to (only when audit enabled). */\n appendedAt?: string;\n /** Result of the anomaly detector (only when detection enabled). */\n anomalies?: AnomalyDetectorResult;\n}\n\n/**\n * Per-principal tail buffer. Distinct principals do not pollute each\n * other's detection windows — otherwise one noisy legitimate client\n * could mask an actual attacker.\n */\ninterface PrincipalTail {\n entries: RecallAuditEntry[];\n}\n\nexport class AccessAuditAdapter {\n private readonly trails = new Map<string, PrincipalTail>();\n private readonly trailBufferSize: number;\n\n constructor(private readonly config: AccessAuditConfig) {\n const n = config.trailBufferSize;\n if (typeof n === \"number\" && Number.isFinite(n) && n > 0) {\n const floored = Math.floor(n);\n // Guard against fractional inputs (e.g. 0.5) that floor to 0.\n this.trailBufferSize = floored >= 1 ? floored : 256;\n } else {\n this.trailBufferSize = 256;\n }\n }\n\n /**\n * Record an audit entry and (when enabled) run the anomaly detector\n * over the principal's tail of entries. The principal key is used\n * purely for tail-buffer bucketing — it need not match the production\n * namespace resolver; sessionKey is the safe default.\n */\n async record(\n principalKey: string,\n entry: RecallAuditEntry,\n now: number = Date.now(),\n ): Promise<AccessAuditResult> {\n const result: AccessAuditResult = {};\n\n // Update the in-memory tail BEFORE the async audit write so a failed\n // append never leaves the tail in a stale state. The detector must see\n // every record regardless of whether the JSONL shard accepted it.\n if (this.config.detection.enabled) {\n const key = principalKey.length > 0 ? principalKey : \"__anonymous__\";\n const tail = this.trails.get(key) ?? { entries: [] };\n tail.entries.push(entry);\n if (tail.entries.length > this.trailBufferSize) {\n tail.entries.splice(0, tail.entries.length - this.trailBufferSize);\n }\n this.trails.set(key, tail);\n\n result.anomalies = detectRecallAnomalies({\n entries: tail.entries,\n now,\n config: this.config.detection,\n });\n }\n\n if (this.config.audit.enabled) {\n try {\n result.appendedAt = await appendRecallAuditEntry(\n this.config.audit.rootDir,\n entry,\n );\n } catch {\n // Audit write failures must never crash the enclosing recall.\n // Swallow — operators can surface the ENOSPC / permission error\n // via the usual filesystem monitoring.\n }\n }\n\n return result;\n }\n\n /** Clear all in-memory tail state. Intended for tests / before_reset. */\n reset(): void {\n this.trails.clear();\n }\n}\n"],"mappings":";;;;;;;;AAyDO,IAAM,qBAAN,MAAyB;AAAA,EAI9B,YAA6B,QAA2B;AAA3B;AAC3B,UAAM,IAAI,OAAO;AACjB,QAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,IAAI,GAAG;AACxD,YAAM,UAAU,KAAK,MAAM,CAAC;AAE5B,WAAK,kBAAkB,WAAW,IAAI,UAAU;AAAA,IAClD,OAAO;AACL,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAT6B;AAAA,EAHZ,SAAS,oBAAI,IAA2B;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBjB,MAAM,OACJ,cACA,OACA,MAAc,KAAK,IAAI,GACK;AAC5B,UAAM,SAA4B,CAAC;AAKnC,QAAI,KAAK,OAAO,UAAU,SAAS;AACjC,YAAM,MAAM,aAAa,SAAS,IAAI,eAAe;AACrD,YAAM,OAAO,KAAK,OAAO,IAAI,GAAG,KAAK,EAAE,SAAS,CAAC,EAAE;AACnD,WAAK,QAAQ,KAAK,KAAK;AACvB,UAAI,KAAK,QAAQ,SAAS,KAAK,iBAAiB;AAC9C,aAAK,QAAQ,OAAO,GAAG,KAAK,QAAQ,SAAS,KAAK,eAAe;AAAA,MACnE;AACA,WAAK,OAAO,IAAI,KAAK,IAAI;AAEzB,aAAO,YAAY,sBAAsB;AAAA,QACvC,SAAS,KAAK;AAAA,QACd;AAAA,QACA,QAAQ,KAAK,OAAO;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,OAAO,MAAM,SAAS;AAC7B,UAAI;AACF,eAAO,aAAa,MAAM;AAAA,UACxB,KAAK,OAAO,MAAM;AAAA,UAClB;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAIR;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,OAAO,MAAM;AAAA,EACpB;AACF;","names":[]}
@@ -0,0 +1,322 @@
1
+ import {
2
+ isConsolidationOperator
3
+ } from "./chunk-X6GF3FX2.js";
4
+ import {
5
+ sidecarKey
6
+ } from "./chunk-FAAFWE4G.js";
7
+
8
+ // src/consolidation-provenance-check.ts
9
+ import path from "path";
10
+ import { readdir, readFile, stat } from "fs/promises";
11
+ var DERIVED_VIA_RAW_RE = /^[\t ]*derived_via:[\t ]*(.*)$/mu;
12
+ var DERIVED_FROM_RAW_RE = /^[\t ]*derived_from:[\t ]*(.*)$/mu;
13
+ function tokenizeRawBlockList(fmSlice, key) {
14
+ const lines = fmSlice.split("\n");
15
+ const keyRe = new RegExp(`^[\\t ]*${key}:[\\t ]*(.*)$`, "u");
16
+ let startIdx = -1;
17
+ for (let i = 0; i < lines.length; i++) {
18
+ const m = lines[i].match(keyRe);
19
+ if (m) {
20
+ if (m[1].trim().length === 0) {
21
+ startIdx = i + 1;
22
+ }
23
+ break;
24
+ }
25
+ }
26
+ if (startIdx < 0) return null;
27
+ const items = [];
28
+ for (let i = startIdx; i < lines.length; i++) {
29
+ const line = lines[i];
30
+ if (!/^\s+-/.test(line)) break;
31
+ const m = line.match(/^\s+-\s*(.*)$/u);
32
+ if (!m) break;
33
+ let tok = m[1].trim();
34
+ if (tok.startsWith('"') && tok.endsWith('"') && tok.length >= 2 || tok.startsWith("'") && tok.endsWith("'") && tok.length >= 2) {
35
+ tok = tok.slice(1, -1);
36
+ }
37
+ items.push(tok);
38
+ }
39
+ return items.length > 0 ? items : null;
40
+ }
41
+ function tokenizeRawFlowList(raw) {
42
+ const trimmed = raw.trim();
43
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
44
+ const inner = trimmed.slice(1, -1);
45
+ const parts = [];
46
+ let current = "";
47
+ let inSingle = false;
48
+ let inDouble = false;
49
+ for (let i = 0; i < inner.length; i++) {
50
+ const ch = inner[i];
51
+ if (inDouble) {
52
+ if (ch === "\\" && i + 1 < inner.length) {
53
+ current += inner[++i];
54
+ continue;
55
+ }
56
+ if (ch === '"') {
57
+ inDouble = false;
58
+ continue;
59
+ }
60
+ current += ch;
61
+ } else if (inSingle) {
62
+ if (ch === "'" && inner[i + 1] === "'") {
63
+ current += "'";
64
+ i++;
65
+ continue;
66
+ }
67
+ if (ch === "'") {
68
+ inSingle = false;
69
+ continue;
70
+ }
71
+ current += ch;
72
+ } else if (ch === '"') {
73
+ inDouble = true;
74
+ } else if (ch === "'") {
75
+ inSingle = true;
76
+ } else if (ch === ",") {
77
+ parts.push(current.trim());
78
+ current = "";
79
+ } else {
80
+ current += ch;
81
+ }
82
+ }
83
+ if (current.trim().length > 0 || parts.length > 0) {
84
+ parts.push(current.trim());
85
+ }
86
+ return parts;
87
+ }
88
+ var DERIVED_FROM_ENTRY_RE = /^(.+):(\d+)$/;
89
+ function resolveSnapshotPath(memoryDir, sidecarDir, entry) {
90
+ const match = entry.match(DERIVED_FROM_ENTRY_RE);
91
+ if (!match) {
92
+ return { ok: false, reason: `malformed entry (expected "<path>:<version>")` };
93
+ }
94
+ const pagePath = match[1];
95
+ const versionId = match[2];
96
+ const ext = path.extname(pagePath) || ".md";
97
+ const key = sidecarKey(pagePath);
98
+ const snapshotPath = path.join(memoryDir, sidecarDir, key, `${versionId}${ext}`);
99
+ return { ok: true, snapshotPath };
100
+ }
101
+ async function runConsolidationProvenanceCheck(options) {
102
+ const { storage, memoryDir } = options;
103
+ const sidecarDir = options.sidecarDir ?? ".versions";
104
+ const report = {
105
+ scanned: 0,
106
+ withProvenance: 0,
107
+ issues: []
108
+ };
109
+ let memories;
110
+ try {
111
+ memories = await storage.readAllMemories();
112
+ } catch {
113
+ return {
114
+ scanned: 0,
115
+ withProvenance: 0,
116
+ issues: [
117
+ {
118
+ memoryPath: memoryDir,
119
+ memoryId: "(unreadable)",
120
+ kind: "derived_from_malformed_entry",
121
+ detail: "Could not enumerate memory directory to scan provenance."
122
+ }
123
+ ]
124
+ };
125
+ }
126
+ for (const memory of memories) {
127
+ report.scanned += 1;
128
+ const fm = memory.frontmatter;
129
+ const derivedFrom = fm.derived_from;
130
+ const derivedVia = fm.derived_via;
131
+ let rawDerivedVia;
132
+ let rawDerivedFrom;
133
+ let rawDerivedViaKeyPresent = false;
134
+ let rawDerivedFromKeyPresent = false;
135
+ let duplicateViaKeys = false;
136
+ let duplicateFromKeys = false;
137
+ let viaMatchCount = 0;
138
+ let fromMatchCount = 0;
139
+ let fmSlice = "";
140
+ try {
141
+ const raw = await readFile(memory.path, "utf-8");
142
+ const frontmatterEnd = raw.indexOf("\n---", raw.indexOf("---") + 3);
143
+ fmSlice = frontmatterEnd > 0 ? raw.slice(0, frontmatterEnd) : raw;
144
+ const viaMatches = [...fmSlice.matchAll(new RegExp(DERIVED_VIA_RAW_RE.source, DERIVED_VIA_RAW_RE.flags + "g"))];
145
+ viaMatchCount = viaMatches.length;
146
+ duplicateViaKeys = viaMatches.length > 1;
147
+ if (viaMatches.length > 0) {
148
+ rawDerivedViaKeyPresent = true;
149
+ const lastVia = viaMatches[viaMatches.length - 1];
150
+ let val = lastVia[1].trim();
151
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
152
+ val = val.slice(1, -1);
153
+ }
154
+ rawDerivedVia = val;
155
+ }
156
+ const fromMatches = [...fmSlice.matchAll(new RegExp(DERIVED_FROM_RAW_RE.source, DERIVED_FROM_RAW_RE.flags + "g"))];
157
+ fromMatchCount = fromMatches.length;
158
+ duplicateFromKeys = fromMatches.length > 1;
159
+ if (fromMatches.length > 0) {
160
+ rawDerivedFromKeyPresent = true;
161
+ const lastFrom = fromMatches[fromMatches.length - 1];
162
+ rawDerivedFrom = lastFrom[1].trim();
163
+ }
164
+ } catch {
165
+ }
166
+ const hasFrom = Array.isArray(derivedFrom) && derivedFrom.length > 0;
167
+ const hasVia = derivedVia !== void 0 && derivedVia !== null;
168
+ const hasRawVia = rawDerivedVia !== void 0 && rawDerivedVia.length > 0;
169
+ const hasRawMalformedFrom = rawDerivedFromKeyPresent && !hasFrom;
170
+ const hasBlankRawVia = rawDerivedViaKeyPresent && (rawDerivedVia === void 0 || rawDerivedVia.length === 0) && !hasVia;
171
+ if (!hasFrom && !hasVia && !hasRawVia && !hasRawMalformedFrom && !hasBlankRawVia) continue;
172
+ report.withProvenance += 1;
173
+ if (duplicateViaKeys) {
174
+ report.issues.push({
175
+ memoryPath: memory.path,
176
+ memoryId: fm.id,
177
+ kind: "derived_via_unknown_operator",
178
+ detail: `raw YAML contains ${viaMatchCount} "derived_via" keys; parseFrontmatter uses the last occurrence`
179
+ });
180
+ }
181
+ if (duplicateFromKeys) {
182
+ report.issues.push({
183
+ memoryPath: memory.path,
184
+ memoryId: fm.id,
185
+ kind: "derived_from_malformed_entry",
186
+ detail: `raw YAML contains ${fromMatchCount} "derived_from" keys; parseFrontmatter uses the last occurrence`
187
+ });
188
+ }
189
+ if (hasRawMalformedFrom) {
190
+ const display = rawDerivedFrom ?? "(blank)";
191
+ report.issues.push({
192
+ memoryPath: memory.path,
193
+ memoryId: fm.id,
194
+ kind: "derived_from_malformed_entry",
195
+ detail: `raw YAML "derived_from: ${display}" could not be parsed as a list`
196
+ });
197
+ }
198
+ if (hasFrom && rawDerivedFromKeyPresent) {
199
+ let rawList = null;
200
+ if (rawDerivedFrom && rawDerivedFrom.length > 0) {
201
+ rawList = tokenizeRawFlowList(rawDerivedFrom);
202
+ }
203
+ if (rawList === null) {
204
+ rawList = tokenizeRawBlockList(fmSlice, "derived_from");
205
+ }
206
+ if (rawList !== null && rawList.length > derivedFrom.length) {
207
+ for (const tok of rawList) {
208
+ if (tok.length === 0) {
209
+ report.issues.push({
210
+ memoryPath: memory.path,
211
+ memoryId: fm.id,
212
+ kind: "derived_from_malformed_entry",
213
+ detail: `raw YAML derived_from contains an empty entry (mixed list)`
214
+ });
215
+ continue;
216
+ }
217
+ if (!derivedFrom.includes(tok)) {
218
+ if (!/^(.+):(\d+)$/u.test(tok)) {
219
+ report.issues.push({
220
+ memoryPath: memory.path,
221
+ memoryId: fm.id,
222
+ kind: "derived_from_malformed_entry",
223
+ detail: `raw YAML derived_from contains a malformed entry: ${JSON.stringify(tok)}`
224
+ });
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ if (hasBlankRawVia) {
231
+ report.issues.push({
232
+ memoryPath: memory.path,
233
+ memoryId: fm.id,
234
+ kind: "derived_via_unknown_operator",
235
+ detail: "raw YAML has `derived_via:` key with empty value"
236
+ });
237
+ }
238
+ if (hasFrom) {
239
+ for (const entry of derivedFrom) {
240
+ const resolved = resolveSnapshotPath(memoryDir, sidecarDir, entry);
241
+ if (!resolved.ok) {
242
+ report.issues.push({
243
+ memoryPath: memory.path,
244
+ memoryId: fm.id,
245
+ kind: "derived_from_malformed_entry",
246
+ detail: `${JSON.stringify(entry)}: ${resolved.reason}`
247
+ });
248
+ continue;
249
+ }
250
+ let snapshotOk = false;
251
+ try {
252
+ const st = await stat(resolved.snapshotPath);
253
+ snapshotOk = st.isFile();
254
+ } catch {
255
+ snapshotOk = false;
256
+ }
257
+ if (!snapshotOk) {
258
+ report.issues.push({
259
+ memoryPath: memory.path,
260
+ memoryId: fm.id,
261
+ kind: "derived_from_missing_snapshot",
262
+ detail: `${entry} \u2192 ${resolved.snapshotPath} (not a regular file)`
263
+ });
264
+ }
265
+ }
266
+ }
267
+ if (hasRawVia && !isConsolidationOperator(rawDerivedVia)) {
268
+ report.issues.push({
269
+ memoryPath: memory.path,
270
+ memoryId: fm.id,
271
+ kind: "derived_via_unknown_operator",
272
+ detail: `unknown operator: ${JSON.stringify(rawDerivedVia)}`
273
+ });
274
+ }
275
+ }
276
+ try {
277
+ const seenPaths = new Set(memories.map((m) => m.path));
278
+ const scanRoots = ["facts", "corrections", "procedures", "reasoning-traces"];
279
+ for (const rootName of scanRoots) {
280
+ const rootPath = path.join(memoryDir, rootName);
281
+ for await (const file of walkMarkdownFiles(rootPath)) {
282
+ if (seenPaths.has(file)) continue;
283
+ try {
284
+ const raw = await readFile(file, "utf-8");
285
+ if (DERIVED_FROM_RAW_RE.test(raw) || DERIVED_VIA_RAW_RE.test(raw)) {
286
+ report.withProvenance += 1;
287
+ report.issues.push({
288
+ memoryPath: file,
289
+ memoryId: "(parse failed)",
290
+ kind: "derived_from_malformed_entry",
291
+ detail: "frontmatter could not be parsed by storage reader; provenance fields visible in raw YAML"
292
+ });
293
+ }
294
+ } catch {
295
+ }
296
+ }
297
+ }
298
+ } catch {
299
+ }
300
+ return report;
301
+ }
302
+ async function* walkMarkdownFiles(root) {
303
+ let entries;
304
+ try {
305
+ entries = await readdir(root, { withFileTypes: true });
306
+ } catch {
307
+ return;
308
+ }
309
+ for (const entry of entries) {
310
+ const full = path.join(root, entry.name);
311
+ if (entry.isDirectory()) {
312
+ yield* walkMarkdownFiles(full);
313
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
314
+ yield full;
315
+ }
316
+ }
317
+ }
318
+
319
+ export {
320
+ runConsolidationProvenanceCheck
321
+ };
322
+ //# sourceMappingURL=chunk-ULYOGL6R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/consolidation-provenance-check.ts"],"sourcesContent":["/**\n * Consolidation provenance integrity check (issue #561 PR 4).\n *\n * Validates that every memory carrying consolidation provenance frontmatter\n * (`derived_from`, `derived_via`) resolves to real data:\n *\n * - Each `derived_from` entry `\"<path>:<version>\"` must name a\n * page-version snapshot that exists on disk (via the sidecar layout\n * documented in `page-versioning.ts`).\n * - Each `derived_via` must be one of the known\n * `ConsolidationOperator` values — malformed values are surfaced as\n * warnings rather than crashes so legacy or future operators survive a\n * rollback.\n *\n * Non-fatal: every failure renders a warning with the offending file path\n * and a human-readable reason. Integrity problems are informational for\n * now — we do not auto-heal or archive broken memories.\n */\n\nimport path from \"node:path\";\nimport { access, readdir, readFile, stat } from \"node:fs/promises\";\nimport { constants as fsConstants } from \"node:fs\";\nimport type { StorageManager } from \"./storage.js\";\nimport { isConsolidationOperator } from \"./consolidation-operator.js\";\n// Import the canonical `sidecarKey` from page-versioning (PR #634\n// review, cursor Medium) so a future key-format change stays in\n// lock-step with the doctor scan.\nimport { sidecarKey } from \"./page-versioning.js\";\n\n/**\n * Regex to spot a `derived_via: <value>` line in the raw YAML frontmatter\n * between the opening and first closing `---` delimiters. We use the raw\n * text rather than the parsed `frontmatter.derived_via` because the\n * read-path parser coerces unknown values back to `undefined` — that\n * would silently hide corrupted-or-future operators from the doctor scan\n * (PR #634 review feedback, codex P2).\n */\n// Allow empty capture groups so truncated/blank `derived_via:` and\n// `derived_from:` lines (key present, no value) are distinguishable\n// from \"key missing entirely\" (regex returns null). Optional\n// leading whitespace accepts indented keys which `parseFrontmatter`\n// also accepts (PR #634 round-6 review, codex P2).\nconst DERIVED_VIA_RAW_RE = /^[\\t ]*derived_via:[\\t ]*(.*)$/mu;\nconst DERIVED_FROM_RAW_RE = /^[\\t ]*derived_from:[\\t ]*(.*)$/mu;\n\n/**\n * Tokenize a YAML-block-style list under `key:` in the given\n * frontmatter slice. Looks for lines matching `^ - <value>` after a\n * `key:` line and before the next non-list line. Returns `null` when\n * the key is missing or the value is a scalar / flow list (no block\n * entries found).\n *\n * Only used for the mixed-list malformed-entry detection — it does\n * not try to decode YAML escape sequences since we only need the\n * entry count + raw token text to compare against the parsed array.\n */\nfunction tokenizeRawBlockList(fmSlice: string, key: string): string[] | null {\n const lines = fmSlice.split(\"\\n\");\n // Accept indented keys too — parseFrontmatter does (PR #634 round-7\n // review, codex P2 / cursor Low).\n const keyRe = new RegExp(`^[\\\\t ]*${key}:[\\\\t ]*(.*)$`, \"u\");\n let startIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(keyRe);\n if (m) {\n if (m[1].trim().length === 0) {\n startIdx = i + 1;\n }\n break;\n }\n }\n if (startIdx < 0) return null;\n const items: string[] = [];\n for (let i = startIdx; i < lines.length; i++) {\n const line = lines[i];\n if (!/^\\s+-/.test(line)) break; // not a block-list entry\n const m = line.match(/^\\s+-\\s*(.*)$/u);\n if (!m) break;\n let tok = m[1].trim();\n if (\n (tok.startsWith('\"') && tok.endsWith('\"') && tok.length >= 2) ||\n (tok.startsWith(\"'\") && tok.endsWith(\"'\") && tok.length >= 2)\n ) {\n tok = tok.slice(1, -1);\n }\n items.push(tok);\n }\n return items.length > 0 ? items : null;\n}\n\n/**\n * Tokenize a YAML-flow-style list (`[\"a\", \"b\", ...]`) into a flat\n * string array. Returns `null` when the input isn't a flow list.\n * Best-effort — we don't implement a full YAML parser, just enough to\n * detect mixed valid/invalid entries for the doctor integrity check.\n */\nfunction tokenizeRawFlowList(raw: string): string[] | null {\n const trimmed = raw.trim();\n if (!trimmed.startsWith(\"[\") || !trimmed.endsWith(\"]\")) return null;\n const inner = trimmed.slice(1, -1);\n const parts: string[] = [];\n let current = \"\";\n let inSingle = false;\n let inDouble = false;\n for (let i = 0; i < inner.length; i++) {\n const ch = inner[i];\n if (inDouble) {\n if (ch === \"\\\\\" && i + 1 < inner.length) {\n current += inner[++i];\n continue;\n }\n if (ch === '\"') {\n inDouble = false;\n continue;\n }\n current += ch;\n } else if (inSingle) {\n if (ch === \"'\" && inner[i + 1] === \"'\") {\n current += \"'\";\n i++;\n continue;\n }\n if (ch === \"'\") {\n inSingle = false;\n continue;\n }\n current += ch;\n } else if (ch === '\"') {\n inDouble = true;\n } else if (ch === \"'\") {\n inSingle = true;\n } else if (ch === \",\") {\n parts.push(current.trim());\n current = \"\";\n } else {\n current += ch;\n }\n }\n if (current.trim().length > 0 || parts.length > 0) {\n parts.push(current.trim());\n }\n return parts;\n}\n\n/**\n * One integrity warning attached to a specific memory.\n */\nexport interface ConsolidationProvenanceIssue {\n /** Absolute path to the memory markdown file. */\n memoryPath: string;\n /** Memory id from frontmatter. */\n memoryId: string;\n /** Type of integrity issue. */\n kind:\n | \"derived_from_missing_snapshot\"\n | \"derived_from_malformed_entry\"\n | \"derived_via_unknown_operator\";\n /** Human-readable detail — includes the offending value when relevant. */\n detail: string;\n}\n\n/**\n * Summary of a provenance-integrity scan. Used by the operator-doctor\n * report and surfaced in the CLI output.\n */\nexport interface ConsolidationProvenanceReport {\n /** Total memories inspected. */\n scanned: number;\n /** Memories that carry `derived_from` and/or `derived_via`. */\n withProvenance: number;\n /** One entry per problem detected (may be empty). */\n issues: ConsolidationProvenanceIssue[];\n}\n\nconst DERIVED_FROM_ENTRY_RE = /^(.+):(\\d+)$/;\n\n/**\n * Build the on-disk snapshot path for a `\"<relpath>:<version>\"` entry,\n * relative to the given memory directory. Mirrors the layout documented\n * in `page-versioning.ts`:\n *\n * memoryDir/<sidecarDir>/<sidecarKey>/<version><ext>\n */\nfunction resolveSnapshotPath(\n memoryDir: string,\n sidecarDir: string,\n entry: string,\n): { ok: true; snapshotPath: string } | { ok: false; reason: string } {\n const match = entry.match(DERIVED_FROM_ENTRY_RE);\n if (!match) {\n return { ok: false, reason: `malformed entry (expected \"<path>:<version>\")` };\n }\n const pagePath = match[1];\n const versionId = match[2];\n const ext = path.extname(pagePath) || \".md\";\n const key = sidecarKey(pagePath);\n const snapshotPath = path.join(memoryDir, sidecarDir, key, `${versionId}${ext}`);\n return { ok: true, snapshotPath };\n}\n\n/**\n * Scan every memory under `storage` and flag consolidation-provenance\n * problems. Does not throw on individual failures — collects them in the\n * returned report.\n */\nexport async function runConsolidationProvenanceCheck(options: {\n storage: StorageManager;\n memoryDir: string;\n /**\n * Page-versioning sidecar directory name. Defaults to `.versions` —\n * matches the baked-in default used by `setVersioningConfig` when\n * versioning is enabled via config.\n */\n sidecarDir?: string;\n}): Promise<ConsolidationProvenanceReport> {\n const { storage, memoryDir } = options;\n const sidecarDir = options.sidecarDir ?? \".versions\";\n\n const report: ConsolidationProvenanceReport = {\n scanned: 0,\n withProvenance: 0,\n issues: [],\n };\n\n let memories;\n try {\n memories = await storage.readAllMemories();\n } catch {\n // If we can't enumerate memories at all, surface a single synthetic\n // issue rather than throwing — the doctor wrapper treats an empty\n // issues list as \"ok\" and we don't want a filesystem hiccup to crash\n // the whole diagnostic.\n return {\n scanned: 0,\n withProvenance: 0,\n issues: [\n {\n memoryPath: memoryDir,\n memoryId: \"(unreadable)\",\n kind: \"derived_from_malformed_entry\",\n detail: \"Could not enumerate memory directory to scan provenance.\",\n },\n ],\n };\n }\n\n for (const memory of memories) {\n report.scanned += 1;\n const fm = memory.frontmatter;\n const derivedFrom = fm.derived_from;\n const derivedVia = fm.derived_via;\n\n // Raw frontmatter values from disk — the read-path parser coerces\n // malformed `derived_from` and unknown `derived_via` back to\n // `undefined`, which would silently hide on-disk corruption from\n // the doctor scan (PR #634 review feedback, codex P2). We\n // re-extract both via regex so integrity issues are reported even\n // when the parser normalized them away. `rawDerivedVia` /\n // `rawDerivedFrom` being `\"\"` (empty string) represents a\n // corrupted file with the key present but the value truncated —\n // that's distinct from \"key missing entirely\" (undefined).\n let rawDerivedVia: string | undefined;\n let rawDerivedFrom: string | undefined;\n let rawDerivedViaKeyPresent = false;\n let rawDerivedFromKeyPresent = false;\n let duplicateViaKeys = false;\n let duplicateFromKeys = false;\n let viaMatchCount = 0;\n let fromMatchCount = 0;\n let fmSlice = \"\";\n try {\n const raw = await readFile(memory.path, \"utf-8\");\n const frontmatterEnd = raw.indexOf(\"\\n---\", raw.indexOf(\"---\") + 3);\n fmSlice = frontmatterEnd > 0 ? raw.slice(0, frontmatterEnd) : raw;\n // Use matchAll to find ALL occurrences of `derived_via` / `derived_from`\n // in the raw YAML. `parseFrontmatter` keeps the LAST assignment when\n // duplicate keys appear, so the doctor must read the last occurrence\n // to match what the storage reader actually uses (PR #634 review,\n // codex P2 — duplicate `derived_via` keys caused false-clean or\n // false-unknown-operator warnings depending on order).\n const viaMatches = [...fmSlice.matchAll(new RegExp(DERIVED_VIA_RAW_RE.source, DERIVED_VIA_RAW_RE.flags + \"g\"))];\n viaMatchCount = viaMatches.length;\n duplicateViaKeys = viaMatches.length > 1;\n if (viaMatches.length > 0) {\n rawDerivedViaKeyPresent = true;\n // Use the last occurrence — `parseFrontmatter` keeps the last\n // assignment when duplicate keys appear, so the doctor must\n // match that behavior to produce accurate warnings (PR #634\n // review, codex P2).\n const lastVia = viaMatches[viaMatches.length - 1];\n let val = lastVia[1].trim();\n if (\n (val.startsWith('\"') && val.endsWith('\"')) ||\n (val.startsWith(\"'\") && val.endsWith(\"'\"))\n ) {\n val = val.slice(1, -1);\n }\n rawDerivedVia = val;\n }\n const fromMatches = [...fmSlice.matchAll(new RegExp(DERIVED_FROM_RAW_RE.source, DERIVED_FROM_RAW_RE.flags + \"g\"))];\n fromMatchCount = fromMatches.length;\n duplicateFromKeys = fromMatches.length > 1;\n if (fromMatches.length > 0) {\n rawDerivedFromKeyPresent = true;\n const lastFrom = fromMatches[fromMatches.length - 1];\n rawDerivedFrom = lastFrom[1].trim();\n }\n } catch {\n // Fall through to the parsed values.\n }\n\n const hasFrom = Array.isArray(derivedFrom) && derivedFrom.length > 0;\n const hasVia = derivedVia !== undefined && derivedVia !== null;\n const hasRawVia = rawDerivedVia !== undefined && rawDerivedVia.length > 0;\n // A raw `derived_from` that the parser dropped indicates on-disk\n // corruption we must surface. We detect this by: (a) the raw YAML\n // contains a `derived_from:` key, AND (b) the parsed frontmatter\n // has no valid array. A scalar like `derived_from: facts/a.md:7`\n // (list brackets omitted) or a blank `derived_from:` both hit this\n // branch.\n const hasRawMalformedFrom = rawDerivedFromKeyPresent && !hasFrom;\n // A blank `derived_via:` with no value is also corrupt — the\n // parser drops it to undefined, but the raw key is still present\n // on disk (PR #634 round-3 review, codex P2).\n const hasBlankRawVia =\n rawDerivedViaKeyPresent &&\n (rawDerivedVia === undefined || rawDerivedVia.length === 0) &&\n !hasVia;\n if (\n !hasFrom && !hasVia && !hasRawVia &&\n !hasRawMalformedFrom && !hasBlankRawVia\n ) continue;\n report.withProvenance += 1;\n\n // Duplicate-key detection (PR #634 review, codex P2): when the raw\n // YAML contains multiple `derived_via` or `derived_from` lines,\n // `parseFrontmatter` silently uses the last one. Flag this as a\n // malformed entry so operators can inspect and fix the file.\n if (duplicateViaKeys) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_via_unknown_operator\",\n detail: `raw YAML contains ${viaMatchCount} \"derived_via\" keys; parseFrontmatter uses the last occurrence`,\n });\n }\n if (duplicateFromKeys) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_from_malformed_entry\",\n detail: `raw YAML contains ${fromMatchCount} \"derived_from\" keys; parseFrontmatter uses the last occurrence`,\n });\n }\n\n if (hasRawMalformedFrom) {\n const display = rawDerivedFrom ?? \"(blank)\";\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_from_malformed_entry\",\n detail: `raw YAML \"derived_from: ${display}\" could not be parsed as a list`,\n });\n }\n\n // Mixed-list detection (PR #634 round-4 + round-5 review, codex\n // P2): when the parser DID return a valid list but the raw YAML\n // includes additional tokens that got dropped, flag those as\n // malformed. Handles both flow-style (`[\"a\", \"\", \"b\"]`) and\n // block-style (`\\n - a\\n - \\n - b`) YAML lists.\n if (hasFrom && rawDerivedFromKeyPresent) {\n let rawList: string[] | null = null;\n if (rawDerivedFrom && rawDerivedFrom.length > 0) {\n rawList = tokenizeRawFlowList(rawDerivedFrom);\n }\n if (rawList === null) {\n // Fall back to block-list tokenization by re-reading the full\n // frontmatter (already loaded above as `raw`) and scanning\n // the lines following `derived_from:`.\n rawList = tokenizeRawBlockList(fmSlice, \"derived_from\");\n }\n if (rawList !== null && rawList.length > derivedFrom!.length) {\n for (const tok of rawList) {\n if (tok.length === 0) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_from_malformed_entry\",\n detail: `raw YAML derived_from contains an empty entry (mixed list)`,\n });\n continue;\n }\n if (!derivedFrom!.includes(tok)) {\n if (!/^(.+):(\\d+)$/u.test(tok)) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_from_malformed_entry\",\n detail: `raw YAML derived_from contains a malformed entry: ${JSON.stringify(tok)}`,\n });\n }\n }\n }\n }\n }\n if (hasBlankRawVia) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_via_unknown_operator\",\n detail: \"raw YAML has `derived_via:` key with empty value\",\n });\n }\n\n if (hasFrom) {\n for (const entry of derivedFrom!) {\n const resolved = resolveSnapshotPath(memoryDir, sidecarDir, entry);\n if (!resolved.ok) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_from_malformed_entry\",\n detail: `${JSON.stringify(entry)}: ${resolved.reason}`,\n });\n continue;\n }\n // Require a regular file at the snapshot path (PR #634\n // round-8 review, codex P2) — a directory or device node at\n // that path means the sidecar was corrupted and the snapshot\n // is effectively missing.\n let snapshotOk = false;\n try {\n const st = await stat(resolved.snapshotPath);\n snapshotOk = st.isFile();\n } catch {\n snapshotOk = false;\n }\n if (!snapshotOk) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_from_missing_snapshot\",\n detail: `${entry} → ${resolved.snapshotPath} (not a regular file)`,\n });\n }\n }\n }\n\n // Check the RAW YAML value for unknown operators. The parsed value\n // (`fm.derived_via`) is always known-good because the read-path\n // normalizer dropped anything else to undefined.\n if (hasRawVia && !isConsolidationOperator(rawDerivedVia)) {\n report.issues.push({\n memoryPath: memory.path,\n memoryId: fm.id,\n kind: \"derived_via_unknown_operator\",\n detail: `unknown operator: ${JSON.stringify(rawDerivedVia)}`,\n });\n }\n }\n\n // Parse-failure detection (PR #634 round-4 review, codex P2):\n // `readAllMemories()` silently drops files whose frontmatter\n // doesn't parse. Walk the facts/ and corrections/ directories for\n // `.md` files that DO reference provenance frontmatter but didn't\n // come back from the reader — those are the corruption cases the\n // doctor is meant to surface.\n try {\n const seenPaths = new Set(memories.map((m) => m.path));\n const scanRoots = [\"facts\", \"corrections\", \"procedures\", \"reasoning-traces\"];\n for (const rootName of scanRoots) {\n const rootPath = path.join(memoryDir, rootName);\n for await (const file of walkMarkdownFiles(rootPath)) {\n if (seenPaths.has(file)) continue;\n try {\n const raw = await readFile(file, \"utf-8\");\n if (\n DERIVED_FROM_RAW_RE.test(raw) ||\n DERIVED_VIA_RAW_RE.test(raw)\n ) {\n report.withProvenance += 1;\n report.issues.push({\n memoryPath: file,\n memoryId: \"(parse failed)\",\n kind: \"derived_from_malformed_entry\",\n detail:\n \"frontmatter could not be parsed by storage reader; provenance fields visible in raw YAML\",\n });\n }\n } catch {\n // Unreadable file — skip.\n }\n }\n }\n } catch {\n // Best-effort; don't fail the whole scan on a filesystem hiccup.\n }\n\n return report;\n}\n\n/**\n * Recursively yield all `.md` file paths under `root`. Silent on\n * missing directories — the facts/corrections dirs may not exist in\n * fresh installs.\n */\nasync function* walkMarkdownFiles(root: string): AsyncGenerator<string> {\n let entries;\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = path.join(root, entry.name);\n if (entry.isDirectory()) {\n yield* walkMarkdownFiles(full);\n } else if (entry.isFile() && entry.name.endsWith(\".md\")) {\n yield full;\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  EngramMcpServer
3
- } from "./chunk-NQEVYWX6.js";
4
- import {
5
- validateRequest
6
- } from "./chunk-MTLYEMJB.js";
3
+ } from "./chunk-OC5OXUQ4.js";
7
4
  import {
8
5
  EngramAccessInputError
9
- } from "./chunk-MVTHXUBX.js";
6
+ } from "./chunk-STGWEHYR.js";
10
7
  import {
11
8
  isTrustZoneName
12
9
  } from "./chunk-EQINRHYR.js";
13
10
  import {
14
11
  log
15
12
  } from "./chunk-2ODBA7MQ.js";
13
+ import {
14
+ validateRequest
15
+ } from "./chunk-WCLICCGB.js";
16
16
 
17
17
  // src/access-http.ts
18
18
  import { createServer } from "http";
@@ -402,17 +402,28 @@ var EngramAccessHttpServer = class {
402
402
  }
403
403
  if (req.method === "POST" && pathname === "/engram/v1/recall") {
404
404
  const body = await this.readValidatedBody(req, "recall");
405
+ const codingContext = "codingContext" in body ? body.codingContext : void 0;
405
406
  const response = await this.service.recall({
406
407
  query: body.query ?? "",
407
408
  sessionKey: body.sessionKey,
408
409
  namespace: this.resolveNamespace(req, body.namespace),
409
410
  topK: body.topK,
410
411
  mode: body.mode,
411
- includeDebug: body.includeDebug === true
412
+ includeDebug: body.includeDebug === true,
413
+ codingContext
412
414
  });
413
415
  this.respondJson(res, 200, response);
414
416
  return;
415
417
  }
418
+ if (req.method === "POST" && pathname === "/engram/v1/coding-context") {
419
+ const body = await this.readValidatedBody(req, "setCodingContext");
420
+ this.service.setCodingContext({
421
+ sessionKey: body.sessionKey,
422
+ codingContext: body.codingContext
423
+ });
424
+ this.respondJson(res, 200, { ok: true });
425
+ return;
426
+ }
416
427
  if (req.method === "POST" && pathname === "/engram/v1/recall/explain") {
417
428
  const body = await this.readValidatedBody(req, "recallExplain");
418
429
  const response = await this.service.recallExplain({
@@ -422,6 +433,77 @@ var EngramAccessHttpServer = class {
422
433
  this.respondJson(res, 200, response);
423
434
  return;
424
435
  }
436
+ if (req.method === "GET" && pathname === "/engram/v1/recall/tier-explain") {
437
+ const sessionParam = parsed.searchParams.get("session");
438
+ const sessionKey = sessionParam && sessionParam.length > 0 ? sessionParam : void 0;
439
+ const namespaceParam = parsed.searchParams.get("namespace");
440
+ const namespace = this.resolveNamespace(
441
+ req,
442
+ namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
443
+ );
444
+ const payload = await this.service.recallTierExplain(
445
+ sessionKey,
446
+ namespace,
447
+ this.resolveRequestPrincipal(req)
448
+ );
449
+ this.respondJson(res, 200, payload);
450
+ return;
451
+ }
452
+ if (req.method === "GET" && pathname === "/engram/v1/recall/xray") {
453
+ const queryParam = parsed.searchParams.get("q");
454
+ if (!queryParam || queryParam.trim().length === 0) {
455
+ this.respondJson(res, 400, {
456
+ error: "missing_query",
457
+ code: "missing_query",
458
+ message: "q search parameter is required and must be non-empty"
459
+ });
460
+ return;
461
+ }
462
+ const sessionParam = parsed.searchParams.get("session");
463
+ const sessionKey = sessionParam && sessionParam.length > 0 ? sessionParam : void 0;
464
+ const namespaceParam = parsed.searchParams.get("namespace");
465
+ const namespace = this.resolveNamespace(
466
+ req,
467
+ namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
468
+ );
469
+ const budgetParam = parsed.searchParams.get("budget");
470
+ let budget;
471
+ if (budgetParam !== null && budgetParam !== "") {
472
+ const parsedBudget = Number(budgetParam);
473
+ if (!Number.isFinite(parsedBudget) || parsedBudget <= 0 || !Number.isInteger(parsedBudget)) {
474
+ this.respondJson(res, 400, {
475
+ error: "invalid_budget",
476
+ code: "invalid_budget",
477
+ message: "budget expects a positive integer"
478
+ });
479
+ return;
480
+ }
481
+ budget = parsedBudget;
482
+ }
483
+ let payload;
484
+ try {
485
+ payload = await this.service.recallXray({
486
+ query: queryParam,
487
+ sessionKey,
488
+ namespace,
489
+ budget,
490
+ authenticatedPrincipal: this.resolveRequestPrincipal(req)
491
+ });
492
+ } catch (err) {
493
+ const message = err instanceof Error ? err.message : String(err);
494
+ if (message.startsWith("recallXray:")) {
495
+ this.respondJson(res, 400, {
496
+ error: "invalid_request",
497
+ code: "invalid_request",
498
+ message
499
+ });
500
+ return;
501
+ }
502
+ throw err;
503
+ }
504
+ this.respondJson(res, 200, payload);
505
+ return;
506
+ }
425
507
  if (req.method === "POST" && pathname === "/engram/v1/observe") {
426
508
  const body = await this.readValidatedBody(req, "observe");
427
509
  this.ensureWriteRateLimitAvailable();
@@ -588,6 +670,23 @@ var EngramAccessHttpServer = class {
588
670
  );
589
671
  return;
590
672
  }
673
+ if (req.method === "GET" && pathname === "/engram/v1/procedural/stats") {
674
+ const namespaceParam = parsed.searchParams.get("namespace");
675
+ this.respondJson(
676
+ res,
677
+ 200,
678
+ await this.service.procedureStats(
679
+ {
680
+ namespace: this.resolveNamespace(
681
+ req,
682
+ namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
683
+ )
684
+ },
685
+ this.resolveRequestPrincipal(req)
686
+ )
687
+ );
688
+ return;
689
+ }
591
690
  if (req.method === "GET" && pathname === "/engram/v1/trust-zones/records") {
592
691
  const limitRaw = parseInt(parsed.searchParams.get("limit") ?? "25", 10);
593
692
  const offsetRaw = parseInt(parsed.searchParams.get("offset") ?? "0", 10);
@@ -767,12 +866,12 @@ var EngramAccessHttpServer = class {
767
866
  }
768
867
  if (req.method === "POST" && pathname === "/engram/v1/contradiction-scan") {
769
868
  const body = await this.readJsonBody(req);
770
- const { runContradictionScan } = await import("./contradiction-scan-GR33PONM.js");
869
+ const { runContradictionScan } = await import("./contradiction-scan-E3GJTI4F.js");
771
870
  const result = await runContradictionScan({
772
871
  storage: this.service.storageRef,
773
872
  config: this.service.configRef,
774
873
  memoryDir: this.service.memoryDir,
775
- embeddingLookup: this.service.embeddingLookupRef,
874
+ embeddingLookupFactory: this.service.embeddingLookupFactoryRef,
776
875
  localLlm: this.service.localLlmRef,
777
876
  fallbackLlm: this.service.fallbackLlmRef,
778
877
  namespace: typeof body.namespace === "string" ? body.namespace : void 0
@@ -944,4 +1043,4 @@ var EngramAccessHttpServer = class {
944
1043
  export {
945
1044
  EngramAccessHttpServer
946
1045
  };
947
- //# sourceMappingURL=chunk-37UIFYWO.js.map
1046
+ //# sourceMappingURL=chunk-UWB5LMWY.js.map