@remnic/core 9.69.10 → 9.69.12

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 (580) hide show
  1. package/dist/access-admin-ops-surface.d.ts +15 -13
  2. package/dist/access-admin-ops-surface.js +46 -34
  3. package/dist/access-authorization-probe.d.ts +13 -11
  4. package/dist/access-authorization-probe.js +47 -35
  5. package/dist/access-boundary.d.ts +13 -11
  6. package/dist/access-boundary.js +46 -34
  7. package/dist/access-cli.js +108 -97
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +2 -1
  10. package/dist/access-extraction-force-flush.d.ts +13 -11
  11. package/dist/access-extraction-force-flush.js +46 -34
  12. package/dist/access-health-types.d.ts +2 -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.d.ts +3 -2
  17. package/dist/access-http-query.js +48 -36
  18. package/dist/access-http.d.ts +14 -12
  19. package/dist/access-http.js +67 -53
  20. package/dist/access-identity-continuity-surface.d.ts +10 -9
  21. package/dist/access-identity-continuity-surface.js +46 -34
  22. package/dist/access-lcm-surface.d.ts +13 -11
  23. package/dist/access-lcm-surface.js +46 -34
  24. package/dist/access-mcp-output-schemas.js +1 -1
  25. package/dist/access-mcp.d.ts +13 -11
  26. package/dist/access-mcp.js +58 -45
  27. package/dist/access-memory-search-fanout.d.ts +3 -2
  28. package/dist/{access-namespace-preflight-S8fhQcCo.d.ts → access-namespace-preflight-Cy0yPsaE.d.ts} +1 -1
  29. package/dist/access-namespace-preflight.d.ts +4 -3
  30. package/dist/access-namespace-preflight.js +46 -34
  31. package/dist/access-observe-write-surface.d.ts +15 -13
  32. package/dist/access-observe-write-surface.js +46 -34
  33. package/dist/access-offline-manifest.d.ts +10 -9
  34. package/dist/access-offline-manifest.js +3 -3
  35. package/dist/access-operations-batch.js +49 -37
  36. package/dist/access-operations.d.ts +19 -17
  37. package/dist/access-operations.js +54 -42
  38. package/dist/access-recall-concurrency.d.ts +13 -11
  39. package/dist/access-recall-concurrency.js +46 -34
  40. package/dist/access-recall-response.d.ts +13 -11
  41. package/dist/access-recall-response.js +46 -34
  42. package/dist/access-recall-surface.d.ts +13 -11
  43. package/dist/access-recall-surface.js +46 -34
  44. package/dist/access-schema.d.ts +107 -90
  45. package/dist/access-schema.js +7 -5
  46. package/dist/{access-service-DcfM0Vf0.d.ts → access-service-Baz5xW5X.d.ts} +89 -72
  47. package/dist/access-service-helpers.d.ts +13 -11
  48. package/dist/access-service.d.ts +13 -11
  49. package/dist/access-service.js +46 -34
  50. package/dist/access-surface-catalog.d.ts +13 -11
  51. package/dist/access-surface-catalog.js +50 -36
  52. package/dist/access-surface-catalog.js.map +1 -1
  53. package/dist/access-wearables-meetings-surface.d.ts +4 -3
  54. package/dist/action-confidence.d.ts +2 -1
  55. package/dist/active-memory-bridge.d.ts +2 -1
  56. package/dist/active-recall.d.ts +2 -1
  57. package/dist/active-recall.js +6 -5
  58. package/dist/active-recall.js.map +1 -1
  59. package/dist/adapters/index.js +4 -4
  60. package/dist/adapters/registry.js +2 -2
  61. package/dist/ambient-provenance.d.ts +2 -1
  62. package/dist/artifact-search.d.ts +2 -1
  63. package/dist/behavior-learner.d.ts +2 -1
  64. package/dist/behavior-signals.d.ts +2 -1
  65. package/dist/bootstrap.d.ts +10 -9
  66. package/dist/briefing-window.d.ts +3 -2
  67. package/dist/briefing.d.ts +3 -2
  68. package/dist/briefing.js +13 -13
  69. package/dist/buffer-surprise-report.d.ts +2 -1
  70. package/dist/buffer-turn-helpers.d.ts +2 -1
  71. package/dist/buffer.d.ts +3 -2
  72. package/dist/bulk-import/index.d.ts +4 -3
  73. package/dist/calibration.d.ts +2 -1
  74. package/dist/capabilities.d.ts +2 -1
  75. package/dist/{catalog-BmBXTtOs.d.ts → catalog-9X8WpZNO.d.ts} +1 -1
  76. package/dist/causal-behavior.d.ts +2 -1
  77. package/dist/causal-behavior.js +2 -2
  78. package/dist/causal-chain.js +2 -2
  79. package/dist/causal-consolidation.d.ts +2 -1
  80. package/dist/causal-consolidation.js +16 -16
  81. package/dist/causal-retrieval.js +2 -2
  82. package/dist/causal-trajectory-graph.d.ts +2 -1
  83. package/dist/causal-trajectory-graph.js +2 -2
  84. package/dist/causal-trajectory.js +1 -1
  85. package/dist/{chunk-D5VPIJN3.js → chunk-227WJVPY.js} +2 -2
  86. package/dist/{chunk-Y3BWCIY2.js → chunk-2EHDPHMD.js} +2 -2
  87. package/dist/{chunk-2LKW6OO7.js → chunk-2XJDAEAM.js} +2 -2
  88. package/dist/{chunk-P23JWI3D.js → chunk-3ITTF7YH.js} +52 -52
  89. package/dist/{chunk-3DT2F5OI.js → chunk-3YKOFCKP.js} +11 -6
  90. package/dist/chunk-3YKOFCKP.js.map +1 -0
  91. package/dist/{chunk-NGP2FPB2.js → chunk-4UVDLNHQ.js} +2 -2
  92. package/dist/{chunk-HXWOVCS6.js → chunk-4WIKMOE3.js} +44 -22
  93. package/dist/chunk-4WIKMOE3.js.map +1 -0
  94. package/dist/{chunk-6RPB2TDG.js → chunk-5HO3QBAD.js} +11 -6
  95. package/dist/chunk-5HO3QBAD.js.map +1 -0
  96. package/dist/{chunk-AYWPIH4M.js → chunk-5P2KNFJZ.js} +149 -25
  97. package/dist/chunk-5P2KNFJZ.js.map +1 -0
  98. package/dist/{chunk-FJMIQTMH.js → chunk-5YQ7RFSO.js} +2 -2
  99. package/dist/{chunk-KQAFEZQX.js → chunk-7LCGFUMB.js} +4 -4
  100. package/dist/{chunk-EESYTBXV.js → chunk-7NCUHD5U.js} +2 -2
  101. package/dist/{chunk-X574KX52.js → chunk-7ZMU4XQT.js} +2 -2
  102. package/dist/{chunk-SG3Z67FB.js → chunk-APZXA4WI.js} +2 -2
  103. package/dist/{chunk-2QSZNTDO.js → chunk-AU7Q3LSC.js} +4 -4
  104. package/dist/{chunk-VGMRI4J6.js → chunk-B75VCEX5.js} +4 -4
  105. package/dist/{chunk-D35XLYQ7.js → chunk-BE4Y6GK6.js} +11 -11
  106. package/dist/{chunk-ELKD5PCD.js → chunk-BEXFNGQ3.js} +3 -3
  107. package/dist/{chunk-U5SRTYFS.js → chunk-BFDOBESV.js} +2 -2
  108. package/dist/{chunk-OLT3YTDH.js → chunk-BIJAWZYW.js} +4 -4
  109. package/dist/{chunk-FU2NYFX6.js → chunk-BO4PWIJY.js} +3 -3
  110. package/dist/{chunk-JMAG7WZ7.js → chunk-BX6GW4KC.js} +2 -2
  111. package/dist/chunk-CC37E3QM.js +198 -0
  112. package/dist/chunk-CC37E3QM.js.map +1 -0
  113. package/dist/{chunk-J2R275FN.js → chunk-CTCGHNWN.js} +4 -4
  114. package/dist/{chunk-X5PIDJ3L.js → chunk-DA4ZMCBU.js} +3 -3
  115. package/dist/{chunk-IVAW3HBF.js → chunk-DH3V7RCQ.js} +21 -13
  116. package/dist/chunk-DH3V7RCQ.js.map +1 -0
  117. package/dist/chunk-DXFG2VGN.js +50 -0
  118. package/dist/chunk-DXFG2VGN.js.map +1 -0
  119. package/dist/{chunk-4OFKZAJG.js → chunk-EAARGPWI.js} +2 -1
  120. package/dist/chunk-EAARGPWI.js.map +1 -0
  121. package/dist/{chunk-H6RSANGQ.js → chunk-ETZ7KKOS.js} +2 -2
  122. package/dist/{chunk-K4E3Y3ZX.js → chunk-G7VEQEMG.js} +2 -1
  123. package/dist/chunk-G7VEQEMG.js.map +1 -0
  124. package/dist/{chunk-XZBXINXZ.js → chunk-GIC6BASP.js} +4 -4
  125. package/dist/{chunk-SXQZMC7R.js → chunk-GR4C3FHT.js} +2 -2
  126. package/dist/{chunk-7DYGPGCT.js → chunk-H5IIBGDM.js} +2 -2
  127. package/dist/{chunk-CNNXMIS6.js → chunk-HFRGCNFY.js} +10 -10
  128. package/dist/chunk-HRATODNO.js +28 -0
  129. package/dist/chunk-HRATODNO.js.map +1 -0
  130. package/dist/{chunk-MAG6M3B6.js → chunk-HTOWFFAI.js} +2 -2
  131. package/dist/chunk-HUSTM6PU.js +27 -0
  132. package/dist/chunk-HUSTM6PU.js.map +1 -0
  133. package/dist/{chunk-FOCMJEHH.js → chunk-I4QAH4SY.js} +3 -3
  134. package/dist/{chunk-FSGCXGK6.js → chunk-I6DUKBUI.js} +2 -2
  135. package/dist/{chunk-VI637ILW.js → chunk-IN673LEP.js} +48 -15
  136. package/dist/chunk-IN673LEP.js.map +1 -0
  137. package/dist/{chunk-6FUMA6AA.js → chunk-JGXMA6HL.js} +2 -2
  138. package/dist/{chunk-TA7UZZMW.js → chunk-JHTF22NQ.js} +2 -2
  139. package/dist/{chunk-DZ3CNJPB.js → chunk-LDOQE34V.js} +2 -2
  140. package/dist/{chunk-MEW53VQJ.js → chunk-LDWIPYJ6.js} +7 -7
  141. package/dist/{chunk-227UFSEQ.js → chunk-LEPOP46W.js} +4 -4
  142. package/dist/{chunk-KJTSWJZN.js → chunk-LF6SHWVV.js} +2 -2
  143. package/dist/{chunk-G6OZZX7P.js → chunk-LJAP4KSW.js} +2 -2
  144. package/dist/{chunk-NV6BTBUI.js → chunk-LJNLLNEM.js} +2 -2
  145. package/dist/{chunk-FFFZEF6B.js → chunk-LUDXSSTW.js} +2 -2
  146. package/dist/{chunk-PCH2CVCS.js → chunk-LWBSEYW3.js} +6 -6
  147. package/dist/chunk-M6RORUO5.js +59 -0
  148. package/dist/chunk-M6RORUO5.js.map +1 -0
  149. package/dist/{chunk-FMUJGGKW.js → chunk-M7DGN6JZ.js} +3 -3
  150. package/dist/{chunk-TZIHB2LP.js → chunk-MNH2GM4O.js} +5 -5
  151. package/dist/{chunk-RACZB5HT.js → chunk-N37X5RU7.js} +4 -4
  152. package/dist/{chunk-LK4TOYPU.js → chunk-NF3NMAG7.js} +2 -2
  153. package/dist/{chunk-QL4U3SJA.js → chunk-NHLEFBE2.js} +11 -3
  154. package/dist/chunk-NHLEFBE2.js.map +1 -0
  155. package/dist/chunk-NN5CQTAJ.js +42 -0
  156. package/dist/chunk-NN5CQTAJ.js.map +1 -0
  157. package/dist/{chunk-AG46P24C.js → chunk-NNBZS3DY.js} +4 -4
  158. package/dist/{chunk-LZ2JTO7N.js → chunk-OASGWNYL.js} +2 -2
  159. package/dist/chunk-OEDW4ER7.js +62 -0
  160. package/dist/chunk-OEDW4ER7.js.map +1 -0
  161. package/dist/{chunk-C2JCVZWX.js → chunk-OLBZ2WGG.js} +2 -2
  162. package/dist/{chunk-Z3SQEEB6.js → chunk-OUKBMQ2O.js} +2 -2
  163. package/dist/{chunk-XXW32OL7.js → chunk-OYV6RKPM.js} +1 -1
  164. package/dist/chunk-OYV6RKPM.js.map +1 -0
  165. package/dist/{chunk-2Z3DG5CQ.js → chunk-P6GKRT2H.js} +1 -1
  166. package/dist/{chunk-UGA6PZTX.js → chunk-P7LVYPIX.js} +3 -3
  167. package/dist/{chunk-26JWBSWD.js → chunk-PGCZLHRB.js} +11 -3
  168. package/dist/chunk-PGCZLHRB.js.map +1 -0
  169. package/dist/{chunk-VE45LRCE.js → chunk-QRDSXR3V.js} +2 -2
  170. package/dist/{chunk-KS67VHXA.js → chunk-QUVWFZOU.js} +2 -2
  171. package/dist/{chunk-AI4QFDPR.js → chunk-R7FXPG4T.js} +2 -2
  172. package/dist/chunk-RF2SMMPP.js +37 -0
  173. package/dist/chunk-RF2SMMPP.js.map +1 -0
  174. package/dist/{chunk-X66BHMAU.js → chunk-RJ57OOCD.js} +2 -2
  175. package/dist/chunk-S5G2LAL7.js +354 -0
  176. package/dist/chunk-S5G2LAL7.js.map +1 -0
  177. package/dist/{chunk-VXMKW7YN.js → chunk-TRLCMLX7.js} +2 -2
  178. package/dist/{chunk-ELCMVOQT.js → chunk-V3HUBF7T.js} +2 -2
  179. package/dist/{chunk-KMIAD7JR.js → chunk-VHAKBUBD.js} +2 -2
  180. package/dist/{chunk-GS5IFIVE.js → chunk-VYD63IFH.js} +3 -3
  181. package/dist/chunk-VZIAVM72.js +49 -0
  182. package/dist/chunk-VZIAVM72.js.map +1 -0
  183. package/dist/chunk-WTWQA7BB.js +28 -0
  184. package/dist/chunk-WTWQA7BB.js.map +1 -0
  185. package/dist/{chunk-TBRG2SYY.js → chunk-X4FIZAVQ.js} +94 -52
  186. package/dist/chunk-X4FIZAVQ.js.map +1 -0
  187. package/dist/{chunk-WSFPNCRA.js → chunk-XFPHV65N.js} +2 -2
  188. package/dist/{chunk-JCUG7AXZ.js → chunk-XJJYSX6F.js} +4 -4
  189. package/dist/{chunk-I6DURYZI.js → chunk-XMIAU4CO.js} +2 -2
  190. package/dist/{chunk-PJIGYPO5.js → chunk-XQGFIAG7.js} +2 -2
  191. package/dist/{chunk-B2URC6LJ.js → chunk-YWN3GYI2.js} +2 -2
  192. package/dist/{chunk-TG6JEJDV.js → chunk-Z33VUMKY.js} +3 -3
  193. package/dist/{cli-CFpMhqz5.d.ts → cli-CMtkGBdz.d.ts} +5 -5
  194. package/dist/cli.d.ts +15 -13
  195. package/dist/cli.js +88 -77
  196. package/dist/coding/export-okf-codegraph.d.ts +2 -1
  197. package/dist/coding/export-okf-codegraph.js +13 -13
  198. package/dist/coding/pre-action-gate.d.ts +2 -1
  199. package/dist/coding/pre-action-gate.js +2 -2
  200. package/dist/compounding/engine.d.ts +3 -2
  201. package/dist/compounding/engine.js +15 -14
  202. package/dist/compounding/preference-consolidator.d.ts +2 -1
  203. package/dist/compression-optimizer.d.ts +2 -1
  204. package/dist/config.d.ts +2 -1
  205. package/dist/config.js +6 -5
  206. package/dist/connectors/codex-materialize-runner.d.ts +2 -1
  207. package/dist/connectors/codex-materialize-runner.js +13 -13
  208. package/dist/connectors/codex-materialize.d.ts +2 -1
  209. package/dist/connectors/index.d.ts +2 -1
  210. package/dist/connectors/index.js +13 -13
  211. package/dist/consolidation-provenance-check.d.ts +3 -2
  212. package/dist/consolidation-undo.d.ts +3 -2
  213. package/dist/contradiction/index.d.ts +4 -3
  214. package/dist/converge-config.d.ts +2 -1
  215. package/dist/convergence-refresh.d.ts +3 -2
  216. package/dist/convergence-refresh.js +15 -15
  217. package/dist/conversation-index/backend.d.ts +2 -1
  218. package/dist/conversation-index/backend.js +2 -2
  219. package/dist/conversation-index/chunker.d.ts +2 -1
  220. package/dist/conversation-index/faiss-adapter.d.ts +2 -1
  221. package/dist/conversation-index/indexer.d.ts +2 -1
  222. package/dist/conversation-index/search.d.ts +2 -1
  223. package/dist/corpus-watermark.d.ts +2 -1
  224. package/dist/corpus-watermark.js +15 -15
  225. package/dist/cross-lingual-recall.d.ts +2 -1
  226. package/dist/day-summary.d.ts +2 -1
  227. package/dist/deep-recall-config.d.ts +32 -0
  228. package/dist/deep-recall-config.js +12 -0
  229. package/dist/deep-recall-http-glue.d.ts +170 -0
  230. package/dist/deep-recall-http-glue.js +31 -0
  231. package/dist/deep-recall-http-glue.js.map +1 -0
  232. package/dist/deep-recall-mcp-tools.d.ts +144 -0
  233. package/dist/deep-recall-mcp-tools.js +8 -0
  234. package/dist/deep-recall-mcp-tools.js.map +1 -0
  235. package/dist/deep-recall-policy-llm.d.ts +37 -0
  236. package/dist/deep-recall-policy-llm.js +19 -0
  237. package/dist/deep-recall-policy-llm.js.map +1 -0
  238. package/dist/deep-recall-renderer.d.ts +16 -0
  239. package/dist/deep-recall-renderer.js +8 -0
  240. package/dist/deep-recall-renderer.js.map +1 -0
  241. package/dist/deep-recall-seeds.d.ts +83 -0
  242. package/dist/deep-recall-seeds.js +8 -0
  243. package/dist/deep-recall-seeds.js.map +1 -0
  244. package/dist/deep-recall.d.ts +97 -0
  245. package/dist/deep-recall.js +13 -0
  246. package/dist/deep-recall.js.map +1 -0
  247. package/dist/delinearize.d.ts +2 -1
  248. package/dist/dependency-propagation-config.d.ts +2 -1
  249. package/dist/{dependency-propagation-delivery-BC3bCEet.d.ts → dependency-propagation-delivery-BZGUecZf.d.ts} +1 -1
  250. package/dist/direct-answer-wiring.d.ts +2 -1
  251. package/dist/direct-answer.d.ts +2 -1
  252. package/dist/embedding-fallback.d.ts +2 -1
  253. package/dist/enrichment/index.d.ts +2 -1
  254. package/dist/entity-origin-fields.d.ts +2 -1
  255. package/dist/entity-retrieval.d.ts +3 -2
  256. package/dist/entity-retrieval.js +13 -13
  257. package/dist/entity-schema.d.ts +2 -1
  258. package/dist/episodic-context.d.ts +2 -1
  259. package/dist/eval-trajectory-contracts.d.ts +2 -1
  260. package/dist/eval-trajectory-store.d.ts +2 -1
  261. package/dist/eval-trajectory.d.ts +2 -1
  262. package/dist/explicit-capture.d.ts +10 -9
  263. package/dist/external-wiki-access.d.ts +13 -11
  264. package/dist/external-wiki-access.js +47 -35
  265. package/dist/external-wiki-collection-registration.d.ts +2 -1
  266. package/dist/external-wiki-collection.d.ts +2 -1
  267. package/dist/external-wiki-mcp-tools.d.ts +13 -11
  268. package/dist/extraction-error-classification.d.ts +2 -1
  269. package/dist/extraction-faithfulness.d.ts +2 -1
  270. package/dist/extraction-judge-telemetry.d.ts +2 -1
  271. package/dist/extraction-judge-training.d.ts +2 -1
  272. package/dist/extraction-judge.d.ts +2 -1
  273. package/dist/extraction-liveness.d.ts +2 -1
  274. package/dist/extraction-normalization.d.ts +2 -1
  275. package/dist/extraction-prompt.d.ts +2 -1
  276. package/dist/extraction-source-grounding-rules.d.ts +2 -1
  277. package/dist/extraction-source-grounding.d.ts +2 -1
  278. package/dist/extraction.d.ts +2 -1
  279. package/dist/extraction.js +15 -15
  280. package/dist/fallback-llm.d.ts +2 -1
  281. package/dist/graph-dashboard-diff.d.ts +2 -1
  282. package/dist/graph-dashboard-key.d.ts +2 -1
  283. package/dist/graph-dashboard-parser.d.ts +2 -1
  284. package/dist/{graph-edge-decay-DGTBFALQ.js → graph-edge-decay-FXGELBFA.js} +4 -4
  285. package/dist/graph-edge-decay-FXGELBFA.js.map +1 -0
  286. package/dist/graph-edge-reinforcement.d.ts +2 -1
  287. package/dist/graph-path-reconstruction.d.ts +2 -1
  288. package/dist/graph-path-scoring.d.ts +2 -1
  289. package/dist/graph-snapshot.d.ts +2 -1
  290. package/dist/graph-snapshot.js +3 -3
  291. package/dist/graph.d.ts +2 -1
  292. package/dist/graph.js +2 -2
  293. package/dist/harmonic-construction.js +3 -3
  294. package/dist/harmonic-retrieval.d.ts +38 -2
  295. package/dist/harmonic-retrieval.js +12 -3
  296. package/dist/importance.d.ts +2 -1
  297. package/dist/importers/index.d.ts +2 -1
  298. package/dist/in-flight-reads.d.ts +2 -1
  299. package/dist/{index-D4hS9uxK.d.ts → index-B4iTvT-w.d.ts} +2 -2
  300. package/dist/index.d.ts +701 -699
  301. package/dist/index.js +126 -115
  302. package/dist/intent.d.ts +2 -1
  303. package/dist/lcm/engine.d.ts +2 -1
  304. package/dist/lcm/index.d.ts +2 -1
  305. package/dist/lcm/tools.d.ts +2 -1
  306. package/dist/lifecycle.d.ts +2 -1
  307. package/dist/live-connectors-runner.d.ts +2 -1
  308. package/dist/local-llm.d.ts +2 -1
  309. package/dist/local-model-endpoint.d.ts +2 -1
  310. package/dist/location/index.d.ts +4 -3
  311. package/dist/location/index.js +15 -15
  312. package/dist/maintenance/memory-governance.d.ts +2 -1
  313. package/dist/maintenance/memory-governance.js +13 -13
  314. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
  315. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -13
  316. package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
  317. package/dist/maintenance/rebuild-memory-projection.js +14 -14
  318. package/dist/{maintenance-BRcc3Hx8.d.ts → maintenance-BBpNqEeJ.d.ts} +3 -3
  319. package/dist/mcp-memory-inspector-app.d.ts +13 -11
  320. package/dist/mcp-read-only-tools.js +1 -1
  321. package/dist/memory-action-policy.d.ts +2 -1
  322. package/dist/memory-cache.d.ts +2 -1
  323. package/dist/memory-controller.d.ts +2 -1
  324. package/dist/memory-lifecycle-ledger-utils.d.ts +2 -1
  325. package/dist/memory-projection-store.d.ts +2 -1
  326. package/dist/memory-provenance.d.ts +2 -1
  327. package/dist/memory-snapshot.d.ts +2 -1
  328. package/dist/memory-subject-cli.d.ts +13 -11
  329. package/dist/memory-subject.d.ts +13 -11
  330. package/dist/memory-worth-outcomes.d.ts +3 -2
  331. package/dist/models-json.d.ts +2 -1
  332. package/dist/namespaces/migrate.d.ts +4 -3
  333. package/dist/namespaces/migrate.js +14 -14
  334. package/dist/namespaces/principal.d.ts +2 -1
  335. package/dist/namespaces/search.d.ts +2 -1
  336. package/dist/namespaces/search.js +9 -9
  337. package/dist/namespaces/storage.d.ts +4 -3
  338. package/dist/namespaces/storage.js +13 -13
  339. package/dist/native-knowledge.d.ts +2 -1
  340. package/dist/offline-sync-impression-drain.d.ts +2 -1
  341. package/dist/offline-sync.js +2 -2
  342. package/dist/operator-doctor-corpus.d.ts +2 -1
  343. package/dist/operator-doctor-corpus.js +16 -16
  344. package/dist/operator-doctor-replica.d.ts +2 -1
  345. package/dist/operator-toolkit.d.ts +4 -3
  346. package/dist/operator-toolkit.js +29 -28
  347. package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
  348. package/dist/orchestration/maintenance.d.ts +5 -4
  349. package/dist/orchestration/maintenance.js +21 -21
  350. package/dist/{orchestrator-Btq9lzAN.d.ts → orchestrator-DglY1H0w.d.ts} +8 -8
  351. package/dist/orchestrator.d.ts +10 -9
  352. package/dist/orchestrator.js +108 -97
  353. package/dist/patterns-cli.d.ts +2 -1
  354. package/dist/{pipeline-CZoNdFb5.d.ts → pipeline-C7qRsy6g.d.ts} +1 -1
  355. package/dist/policy-runtime.d.ts +2 -1
  356. package/dist/proactive-contention.d.ts +2 -1
  357. package/dist/provenance.d.ts +2 -1
  358. package/dist/{public-http-C9-CHc40.d.ts → public-http-CU-Cwwdb.d.ts} +1 -1
  359. package/dist/{qmd-VIjSCKIE.d.ts → qmd-YRLTa3AE.d.ts} +1 -1
  360. package/dist/qmd-preflight.d.ts +3 -2
  361. package/dist/qmd-recall-cache.d.ts +2 -1
  362. package/dist/qmd.d.ts +3 -2
  363. package/dist/recall-concurrency-config.d.ts +2 -1
  364. package/dist/recall-deep-expand-select.js +4 -44
  365. package/dist/recall-deep-expand-select.js.map +1 -1
  366. package/dist/recall-deep-frontier.js +4 -23
  367. package/dist/recall-deep-frontier.js.map +1 -1
  368. package/dist/recall-deep-rewrite.js +4 -23
  369. package/dist/recall-deep-rewrite.js.map +1 -1
  370. package/dist/recall-disclosure-escalation.d.ts +2 -1
  371. package/dist/recall-explain-renderer.d.ts +2 -1
  372. package/dist/recall-explain-renderer.js +3 -3
  373. package/dist/recall-memory-map.d.ts +2 -1
  374. package/dist/recall-navigate-access.d.ts +2 -1
  375. package/dist/recall-navigate-access.js +2 -2
  376. package/dist/recall-navigate-disclosure.d.ts +2 -1
  377. package/dist/recall-navigate-disclosure.js +1 -1
  378. package/dist/recall-navigate.d.ts +2 -1
  379. package/dist/recall-navigate.js +2 -2
  380. package/dist/recall-planner-llm.d.ts +2 -1
  381. package/dist/recall-state.d.ts +2 -1
  382. package/dist/recall-tag-filter.d.ts +2 -1
  383. package/dist/recall-timings.d.ts +2 -1
  384. package/dist/recall-xray-cli.d.ts +2 -1
  385. package/dist/recall-xray-cli.js +4 -4
  386. package/dist/recall-xray-renderer.d.ts +2 -1
  387. package/dist/recall-xray-renderer.js +3 -3
  388. package/dist/recall-xray.d.ts +2 -1
  389. package/dist/recall-xray.js +2 -2
  390. package/dist/reconcile/cursor.d.ts +2 -1
  391. package/dist/reconcile/manifest.d.ts +2 -1
  392. package/dist/reconcile/plan.d.ts +2 -1
  393. package/dist/replay/normalizers/chatgpt.d.ts +2 -1
  394. package/dist/replay/normalizers/claude.d.ts +2 -1
  395. package/dist/replay/normalizers/openclaw.d.ts +2 -1
  396. package/dist/replay/normalizers/shared.d.ts +2 -1
  397. package/dist/replay/runner.d.ts +2 -1
  398. package/dist/replay/types.d.ts +2 -1
  399. package/dist/replica-divergence.d.ts +2 -1
  400. package/dist/replica-peers-config.d.ts +2 -1
  401. package/dist/resolve-auth-token.d.ts +2 -1
  402. package/dist/resume-bundles.js +7 -6
  403. package/dist/retrieval-agents.d.ts +3 -2
  404. package/dist/retrieval-tiers.d.ts +2 -1
  405. package/dist/routing/engine.d.ts +2 -1
  406. package/dist/routing/store.d.ts +2 -1
  407. package/dist/salvage-envelope.d.ts +2 -1
  408. package/dist/schemas.d.ts +176 -176
  409. package/dist/{scope-profiles-DqbxQXIS.d.ts → scope-profiles-BVvqVzJX.d.ts} +1 -1
  410. package/dist/search/embed-helper.d.ts +2 -1
  411. package/dist/search/factory.d.ts +2 -1
  412. package/dist/search/factory.js +8 -8
  413. package/dist/search/index.d.ts +2 -1
  414. package/dist/search/index.js +13 -13
  415. package/dist/search/lancedb-backend.d.ts +2 -1
  416. package/dist/search/lancedb-backend.js +2 -2
  417. package/dist/search/meilisearch-backend.d.ts +2 -1
  418. package/dist/search/meilisearch-backend.js +2 -2
  419. package/dist/search/noop-backend.d.ts +2 -1
  420. package/dist/search/orama-backend.d.ts +2 -1
  421. package/dist/search/orama-backend.js +2 -2
  422. package/dist/search/port.d.ts +2 -1
  423. package/dist/search/remote-backend.d.ts +2 -1
  424. package/dist/{semantic-consolidation-DW3rEUoW.d.ts → semantic-consolidation-BzCI6Kuu.d.ts} +1 -1
  425. package/dist/semantic-consolidation.d.ts +3 -2
  426. package/dist/semantic-consolidation.js +14 -14
  427. package/dist/semantic-rule-promotion.js +13 -13
  428. package/dist/semantic-rule-verifier.d.ts +2 -1
  429. package/dist/semantic-rule-verifier.js +13 -13
  430. package/dist/{service-B2vJU_Bx.d.ts → service-DQ3Gscy5.d.ts} +2 -2
  431. package/dist/session-observer-bands.d.ts +2 -1
  432. package/dist/session-observer-state.d.ts +2 -1
  433. package/dist/shared-context/manager.d.ts +43 -7
  434. package/dist/shared-context/manager.js +2 -1
  435. package/dist/signal.d.ts +2 -1
  436. package/dist/source-agent-qualifier.d.ts +2 -1
  437. package/dist/source-agent-qualifier.js +13 -13
  438. package/dist/standup/build.js +13 -13
  439. package/dist/{storage-6fn7BPmb.d.ts → storage-DmTbQdD0.d.ts} +1 -1
  440. package/dist/storage.d.ts +3 -2
  441. package/dist/storage.js +12 -12
  442. package/dist/subject-config.d.ts +2 -1
  443. package/dist/summarizer.d.ts +2 -1
  444. package/dist/summary-snapshot.d.ts +2 -1
  445. package/dist/support-passport/index.d.ts +16 -14
  446. package/dist/support-passport/index.js +49 -37
  447. package/dist/temporal-supersession.d.ts +3 -2
  448. package/dist/temporal-timeline-recall.d.ts +2 -1
  449. package/dist/temporal-validity.d.ts +2 -1
  450. package/dist/threading.d.ts +2 -1
  451. package/dist/tier-migration.d.ts +3 -2
  452. package/dist/tier-routing.d.ts +2 -1
  453. package/dist/topics.d.ts +2 -1
  454. package/dist/transcript.d.ts +2 -1
  455. package/dist/transfer/capsule-export.js +2 -2
  456. package/dist/transfer/capsule-import.js +2 -2
  457. package/dist/transfer/export-okf.d.ts +2 -1
  458. package/dist/transfer/export-okf.js +14 -14
  459. package/dist/transfer/types.d.ts +78 -78
  460. package/dist/trust-score-stage.d.ts +2 -1
  461. package/dist/trust-score.d.ts +2 -1
  462. package/dist/{types-eM7QIcX2.d.ts → types-CLRTESSD.d.ts} +3 -1
  463. package/dist/types.d.ts +2 -1
  464. package/dist/types.js +1 -1
  465. package/dist/utility-runtime.d.ts +2 -1
  466. package/dist/verified-recall.js +13 -13
  467. package/dist/who-knows-cli.d.ts +13 -11
  468. package/dist/who-knows-cli.js +14 -14
  469. package/dist/who-knows.d.ts +13 -11
  470. package/dist/who-knows.js +13 -13
  471. package/dist/write-envelope.d.ts +2 -1
  472. package/package.json +2 -2
  473. package/src/access-boundary.ts +1 -0
  474. package/src/access-http.ts +5 -5
  475. package/src/access-mcp-output-schemas.ts +1 -0
  476. package/src/access-mcp.ts +3 -3
  477. package/src/access-operations-batch.ts +3 -1
  478. package/src/access-schema.ts +12 -0
  479. package/src/access-service.ts +162 -5
  480. package/src/access-surface-catalog.ts +2 -0
  481. package/src/cli/deep-recall-commands.ts +60 -0
  482. package/src/cli.ts +2 -2
  483. package/src/config.test.ts +19 -0
  484. package/src/config.ts +7 -0
  485. package/src/deep-recall-config.ts +83 -0
  486. package/src/deep-recall-http-glue.ts +75 -0
  487. package/src/deep-recall-mcp-tools.ts +33 -0
  488. package/src/deep-recall-policy-llm.ts +65 -0
  489. package/src/deep-recall-renderer.ts +41 -0
  490. package/src/deep-recall-seeds.ts +134 -0
  491. package/src/deep-recall-surface.test.ts +902 -0
  492. package/src/deep-recall.test.ts +348 -0
  493. package/src/deep-recall.ts +572 -0
  494. package/src/harmonic-retrieval.ts +78 -25
  495. package/src/mcp-read-only-tools.ts +1 -0
  496. package/src/shared-context/envelope-at.test.ts +23 -0
  497. package/src/shared-context/envelope-at.ts +12 -6
  498. package/src/shared-context/envelope-io.ts +184 -0
  499. package/src/shared-context/envelope-wiring.test.ts +193 -0
  500. package/src/shared-context/governance.ts +28 -9
  501. package/src/shared-context/iso-instant.ts +43 -0
  502. package/src/shared-context/manager.ts +74 -15
  503. package/src/shared-context/provenance.ts +3 -26
  504. package/src/shared-context/write-origin.test.ts +145 -0
  505. package/src/types.ts +3 -1
  506. package/dist/chunk-26JWBSWD.js.map +0 -1
  507. package/dist/chunk-3DT2F5OI.js.map +0 -1
  508. package/dist/chunk-4OFKZAJG.js.map +0 -1
  509. package/dist/chunk-6RPB2TDG.js.map +0 -1
  510. package/dist/chunk-AYWPIH4M.js.map +0 -1
  511. package/dist/chunk-HXWOVCS6.js.map +0 -1
  512. package/dist/chunk-IVAW3HBF.js.map +0 -1
  513. package/dist/chunk-K4E3Y3ZX.js.map +0 -1
  514. package/dist/chunk-QL4U3SJA.js.map +0 -1
  515. package/dist/chunk-TBRG2SYY.js.map +0 -1
  516. package/dist/chunk-VI637ILW.js.map +0 -1
  517. package/dist/chunk-XXW32OL7.js.map +0 -1
  518. /package/dist/{chunk-D5VPIJN3.js.map → chunk-227WJVPY.js.map} +0 -0
  519. /package/dist/{chunk-Y3BWCIY2.js.map → chunk-2EHDPHMD.js.map} +0 -0
  520. /package/dist/{chunk-2LKW6OO7.js.map → chunk-2XJDAEAM.js.map} +0 -0
  521. /package/dist/{chunk-P23JWI3D.js.map → chunk-3ITTF7YH.js.map} +0 -0
  522. /package/dist/{chunk-NGP2FPB2.js.map → chunk-4UVDLNHQ.js.map} +0 -0
  523. /package/dist/{chunk-FJMIQTMH.js.map → chunk-5YQ7RFSO.js.map} +0 -0
  524. /package/dist/{chunk-KQAFEZQX.js.map → chunk-7LCGFUMB.js.map} +0 -0
  525. /package/dist/{chunk-EESYTBXV.js.map → chunk-7NCUHD5U.js.map} +0 -0
  526. /package/dist/{chunk-X574KX52.js.map → chunk-7ZMU4XQT.js.map} +0 -0
  527. /package/dist/{chunk-SG3Z67FB.js.map → chunk-APZXA4WI.js.map} +0 -0
  528. /package/dist/{chunk-2QSZNTDO.js.map → chunk-AU7Q3LSC.js.map} +0 -0
  529. /package/dist/{chunk-VGMRI4J6.js.map → chunk-B75VCEX5.js.map} +0 -0
  530. /package/dist/{chunk-D35XLYQ7.js.map → chunk-BE4Y6GK6.js.map} +0 -0
  531. /package/dist/{chunk-ELKD5PCD.js.map → chunk-BEXFNGQ3.js.map} +0 -0
  532. /package/dist/{chunk-U5SRTYFS.js.map → chunk-BFDOBESV.js.map} +0 -0
  533. /package/dist/{chunk-OLT3YTDH.js.map → chunk-BIJAWZYW.js.map} +0 -0
  534. /package/dist/{chunk-FU2NYFX6.js.map → chunk-BO4PWIJY.js.map} +0 -0
  535. /package/dist/{chunk-JMAG7WZ7.js.map → chunk-BX6GW4KC.js.map} +0 -0
  536. /package/dist/{chunk-J2R275FN.js.map → chunk-CTCGHNWN.js.map} +0 -0
  537. /package/dist/{chunk-X5PIDJ3L.js.map → chunk-DA4ZMCBU.js.map} +0 -0
  538. /package/dist/{chunk-H6RSANGQ.js.map → chunk-ETZ7KKOS.js.map} +0 -0
  539. /package/dist/{chunk-XZBXINXZ.js.map → chunk-GIC6BASP.js.map} +0 -0
  540. /package/dist/{chunk-SXQZMC7R.js.map → chunk-GR4C3FHT.js.map} +0 -0
  541. /package/dist/{chunk-7DYGPGCT.js.map → chunk-H5IIBGDM.js.map} +0 -0
  542. /package/dist/{chunk-CNNXMIS6.js.map → chunk-HFRGCNFY.js.map} +0 -0
  543. /package/dist/{chunk-MAG6M3B6.js.map → chunk-HTOWFFAI.js.map} +0 -0
  544. /package/dist/{chunk-FOCMJEHH.js.map → chunk-I4QAH4SY.js.map} +0 -0
  545. /package/dist/{chunk-FSGCXGK6.js.map → chunk-I6DUKBUI.js.map} +0 -0
  546. /package/dist/{chunk-6FUMA6AA.js.map → chunk-JGXMA6HL.js.map} +0 -0
  547. /package/dist/{chunk-TA7UZZMW.js.map → chunk-JHTF22NQ.js.map} +0 -0
  548. /package/dist/{chunk-DZ3CNJPB.js.map → chunk-LDOQE34V.js.map} +0 -0
  549. /package/dist/{chunk-MEW53VQJ.js.map → chunk-LDWIPYJ6.js.map} +0 -0
  550. /package/dist/{chunk-227UFSEQ.js.map → chunk-LEPOP46W.js.map} +0 -0
  551. /package/dist/{chunk-KJTSWJZN.js.map → chunk-LF6SHWVV.js.map} +0 -0
  552. /package/dist/{chunk-G6OZZX7P.js.map → chunk-LJAP4KSW.js.map} +0 -0
  553. /package/dist/{chunk-NV6BTBUI.js.map → chunk-LJNLLNEM.js.map} +0 -0
  554. /package/dist/{chunk-FFFZEF6B.js.map → chunk-LUDXSSTW.js.map} +0 -0
  555. /package/dist/{chunk-PCH2CVCS.js.map → chunk-LWBSEYW3.js.map} +0 -0
  556. /package/dist/{chunk-FMUJGGKW.js.map → chunk-M7DGN6JZ.js.map} +0 -0
  557. /package/dist/{chunk-TZIHB2LP.js.map → chunk-MNH2GM4O.js.map} +0 -0
  558. /package/dist/{chunk-RACZB5HT.js.map → chunk-N37X5RU7.js.map} +0 -0
  559. /package/dist/{chunk-LK4TOYPU.js.map → chunk-NF3NMAG7.js.map} +0 -0
  560. /package/dist/{chunk-AG46P24C.js.map → chunk-NNBZS3DY.js.map} +0 -0
  561. /package/dist/{chunk-LZ2JTO7N.js.map → chunk-OASGWNYL.js.map} +0 -0
  562. /package/dist/{chunk-C2JCVZWX.js.map → chunk-OLBZ2WGG.js.map} +0 -0
  563. /package/dist/{chunk-Z3SQEEB6.js.map → chunk-OUKBMQ2O.js.map} +0 -0
  564. /package/dist/{chunk-2Z3DG5CQ.js.map → chunk-P6GKRT2H.js.map} +0 -0
  565. /package/dist/{chunk-UGA6PZTX.js.map → chunk-P7LVYPIX.js.map} +0 -0
  566. /package/dist/{chunk-VE45LRCE.js.map → chunk-QRDSXR3V.js.map} +0 -0
  567. /package/dist/{chunk-KS67VHXA.js.map → chunk-QUVWFZOU.js.map} +0 -0
  568. /package/dist/{chunk-AI4QFDPR.js.map → chunk-R7FXPG4T.js.map} +0 -0
  569. /package/dist/{chunk-X66BHMAU.js.map → chunk-RJ57OOCD.js.map} +0 -0
  570. /package/dist/{chunk-VXMKW7YN.js.map → chunk-TRLCMLX7.js.map} +0 -0
  571. /package/dist/{chunk-ELCMVOQT.js.map → chunk-V3HUBF7T.js.map} +0 -0
  572. /package/dist/{chunk-KMIAD7JR.js.map → chunk-VHAKBUBD.js.map} +0 -0
  573. /package/dist/{chunk-GS5IFIVE.js.map → chunk-VYD63IFH.js.map} +0 -0
  574. /package/dist/{chunk-WSFPNCRA.js.map → chunk-XFPHV65N.js.map} +0 -0
  575. /package/dist/{chunk-JCUG7AXZ.js.map → chunk-XJJYSX6F.js.map} +0 -0
  576. /package/dist/{chunk-I6DURYZI.js.map → chunk-XMIAU4CO.js.map} +0 -0
  577. /package/dist/{chunk-PJIGYPO5.js.map → chunk-XQGFIAG7.js.map} +0 -0
  578. /package/dist/{chunk-B2URC6LJ.js.map → chunk-YWN3GYI2.js.map} +0 -0
  579. /package/dist/{chunk-TG6JEJDV.js.map → chunk-Z33VUMKY.js.map} +0 -0
  580. /package/dist/{graph-edge-decay-DGTBFALQ.js.map → deep-recall-config.js.map} +0 -0
@@ -0,0 +1,902 @@
1
+ /**
2
+ * Deep-recall surface regressions (issue #2332 review).
3
+ *
4
+ * 1. HTTP body namespace: a namespace-scoped bearer that passes an allowed
5
+ * `?namespace=` while setting a DIFFERENT `namespace` in the JSON body must
6
+ * be refused, because the body value is what the service ends up scoped to.
7
+ * 2. Seed routing: a non-default namespace must search ITS OWN collection, not
8
+ * the base (default-namespace) collection.
9
+ * 3. Graph routing: a non-default namespace must read ITS OWN abstraction-node
10
+ * and cue-anchor store, never the default namespace's configured override.
11
+ * 4. Degradation: an unavailable namespace index is a `backend_unavailable`
12
+ * failure, never a healthy empty result.
13
+ * 5. Principal: authorization derives from the presenting authenticated
14
+ * principal, never from the client-supplied `sessionKey`.
15
+ * 6. Governance: graph nodes and anchors are projected against the current
16
+ * active memories BEFORE anything reaches the policy LLM's state prompt.
17
+ *
18
+ * All paths are synthetic temp dirs; no operator data.
19
+ */
20
+
21
+ import assert from "node:assert/strict";
22
+ import { mkdir, mkdtemp, rm } from "node:fs/promises";
23
+ import { tmpdir } from "node:os";
24
+ import path from "node:path";
25
+ import test from "node:test";
26
+
27
+ import { recordAbstractionNode } from "./abstraction-nodes.js";
28
+ import { EngramAccessHttpServer } from "./access-http.js";
29
+ import { EngramAccessService } from "./access-service.js";
30
+ import { parseConfig } from "./config.js";
31
+ import { recordCueAnchor } from "./cue-anchors.js";
32
+ import { DEEP_RECALL_CONFIG_DEFAULTS } from "./deep-recall-config.js";
33
+ import { createDeepRecallSeedSearch } from "./deep-recall-seeds.js";
34
+ import { runBudgetedDeepRecall } from "./deep-recall.js";
35
+ import { NamespaceSearchRouter, namespaceCollectionName } from "./namespaces/search.js";
36
+ import { qmdCollectionNamespaceFromPrefix } from "./orchestration/orchestrator-namespace-scope.js";
37
+ import { QmdResultResolver } from "./orchestration/qmd-result-resolver.js";
38
+ import { StorageManager } from "./storage.js";
39
+ import type { PluginConfig } from "./types.js";
40
+ import type { SearchBackend } from "./search/port.js";
41
+
42
+ /**
43
+ * Real QMD result resolver over the test's real on-disk storage — the member
44
+ * the seed search now reads off the orchestrator. Wires only the lookups
45
+ * readQmdResultMemory needs; single-namespace fixtures have one storage.
46
+ */
47
+ function testSeedResultResolver(
48
+ config: PluginConfig,
49
+ storage: StorageManager,
50
+ ): QmdResultResolver {
51
+ return new QmdResultResolver({
52
+ getConfig: () => config,
53
+ storageFor: async () => storage,
54
+ storageDirNamespace: () => config.defaultNamespace,
55
+ qmdCollectionNamespaceFromPrefix: (prefix) => qmdCollectionNamespaceFromPrefix(prefix, config),
56
+ namespaceFromPath: () => config.defaultNamespace,
57
+ });
58
+ }
59
+
60
+ test("HTTP deep recall authorizes with the presenting principal, not the client's sessionKey (issue #2332 review)", async () => {
61
+ // The route dropped `scope.authenticatedPrincipal`, so the service fell back
62
+ // to deriving the principal from the client-supplied `sessionKey`. Both
63
+ // directions broke: a crafted key matching another principal's rule read
64
+ // that principal's namespace, and a legitimate namespace-enabled request
65
+ // carrying no session key was refused as unauthenticated.
66
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-principal-"));
67
+ try {
68
+ const namespaceDir = path.join(memoryDir, "namespaces", "ns_alice");
69
+ await mkdir(namespaceDir, { recursive: true });
70
+ const config = parseConfig({
71
+ memoryDir,
72
+ namespacesEnabled: true,
73
+ defaultNamespace: "default",
74
+ qmdCollection: "remnic-test",
75
+ deepRecall: { enabled: true, maxSteps: 0 },
76
+ principalFromSessionKeyMode: "map",
77
+ principalFromSessionKeyRules: [{ match: "crafted-alice-key", principal: "alice" }],
78
+ namespacePolicies: [{ name: "ns_alice", readPrincipals: ["alice"], writePrincipals: ["alice"] }],
79
+ });
80
+ const storageCalls: string[] = [];
81
+ const service = Object.create(EngramAccessService.prototype) as EngramAccessService;
82
+ // Test double: deepRecall touches only these orchestrator members, and the
83
+ // real Orchestrator cannot be constructed without a live backend.
84
+ const host = service as unknown as { orchestrator: unknown };
85
+ host.orchestrator = {
86
+ config,
87
+ async getStorage(namespace: string) {
88
+ storageCalls.push(namespace);
89
+ return {
90
+ dir: namespaceDir,
91
+ async readMemoryByPath() {
92
+ return null;
93
+ },
94
+ async getMemoryById() {
95
+ return null;
96
+ },
97
+ };
98
+ },
99
+ async searchAcrossNamespaces() {
100
+ return [];
101
+ },
102
+ localLlm: null,
103
+ fastGatewayLlm: null,
104
+ };
105
+ const server = new EngramAccessHttpServer({
106
+ service,
107
+ port: 0,
108
+ trustPrincipalHeader: true,
109
+ adminConsoleEnabled: false,
110
+ authTokenEntriesGetter: () => [{ token: "operator-token", capabilities: { version: 1 } }],
111
+ });
112
+ const status = await server.start();
113
+ const postDeep = (principal: string, body: Record<string, unknown>) =>
114
+ fetch(`http://127.0.0.1:${status.port}/engram/v1/recall/deep`, {
115
+ method: "POST",
116
+ headers: {
117
+ "content-type": "application/json",
118
+ authorization: "Bearer operator-token",
119
+ "x-engram-principal": principal,
120
+ },
121
+ body: JSON.stringify({ query: "who owns the routing decision", ...body }),
122
+ });
123
+ try {
124
+ // The escalation this fix closes: principal `bob` presents a session key
125
+ // whose configured rule names `alice`, and asks for alice's namespace.
126
+ const escalation = await postDeep("bob", {
127
+ namespace: "ns_alice",
128
+ sessionKey: "crafted-alice-key",
129
+ });
130
+ assert.notEqual(
131
+ escalation.status,
132
+ 200,
133
+ "a crafted sessionKey must not buy another principal's namespace",
134
+ );
135
+ assert.deepEqual(storageCalls, [], "the denied namespace must never be opened");
136
+
137
+ // The other direction: an authenticated principal with NO session key is
138
+ // authenticated, and must not be refused for lack of one.
139
+ const legitimate = await postDeep("alice", { namespace: "ns_alice" });
140
+ assert.equal(
141
+ legitimate.status,
142
+ 200,
143
+ "a namespace-enabled request from an authenticated principal needs no sessionKey",
144
+ );
145
+ const body: unknown = await legitimate.json();
146
+ assert.ok(body && typeof body === "object" && "ok" in body && body.ok === true);
147
+ assert.deepEqual(
148
+ storageCalls,
149
+ ["ns_alice"],
150
+ "the presenting principal's namespace is the one opened",
151
+ );
152
+ } finally {
153
+ await server.stop();
154
+ }
155
+ } finally {
156
+ await rm(memoryDir, { recursive: true, force: true });
157
+ }
158
+ });
159
+
160
+ test("deep recall projects graph nodes against active memories before the policy prompt (issue #2332 review)", async () => {
161
+ // `loadGraph` read nodes and anchors raw, so a node built from a memory that
162
+ // was later rejected still carried its stored title and its anchor value.
163
+ // Both reached the policy LLM through the frontier in the state prompt, long
164
+ // before `loadMemory(...).active` could drop the memory from the entries:
165
+ // governed metadata left the store anyway. The graph now flows through the
166
+ // SAME active-source projection `searchHarmonicRetrieval` uses.
167
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-governed-"));
168
+ try {
169
+ const config = parseConfig({
170
+ memoryDir,
171
+ qmdEnabled: false,
172
+ qmdCollection: "remnic-test",
173
+ deepRecall: { enabled: true, maxSteps: 1 },
174
+ });
175
+ const storage = new StorageManager(memoryDir);
176
+ await storage.ensureDirectories();
177
+ const seed = await storage.writeMemory("fact", "Alpha holds the payments routing decision.", {
178
+ tags: ["payments"],
179
+ });
180
+ const governed = await storage.writeMemory(
181
+ "fact",
182
+ "GOVERNED-CONTENT names the fallback processor contract.",
183
+ { tags: ["payments"] },
184
+ );
185
+ assert.equal(seed.tombstoneBlocked, false, "the seed fixture must actually persist");
186
+ assert.equal(governed.tombstoneBlocked, false, "the governed fixture must actually persist");
187
+ assert.ok(
188
+ await storage.updateMemoryFrontmatter(governed.id, { status: "rejected" }),
189
+ "the governed source memory is moved out of the active set",
190
+ );
191
+
192
+ const recordedAt = "2026-08-01T00:00:00.000Z";
193
+ const node = (nodeId: string, title: string, sourceMemoryIds: string[]) => ({
194
+ schemaVersion: 1 as const,
195
+ nodeId,
196
+ recordedAt,
197
+ sessionKey: "test-session",
198
+ kind: "topic" as const,
199
+ abstractionLevel: "meso" as const,
200
+ title,
201
+ summary: `Synthetic ${nodeId} summary`,
202
+ sourceMemoryIds,
203
+ });
204
+ await recordAbstractionNode({
205
+ memoryDir,
206
+ node: node("node-seed", "Seeded payments routing topic", [seed.id]),
207
+ });
208
+ await recordAbstractionNode({
209
+ memoryDir,
210
+ node: node("node-governed", "GOVERNED-TITLE rejected fallback topic", [governed.id]),
211
+ });
212
+ await recordCueAnchor({
213
+ memoryDir,
214
+ anchor: {
215
+ schemaVersion: 1,
216
+ anchorId: "anchor-shared",
217
+ anchorType: "entity",
218
+ anchorValue: "GOVERNED-ANCHOR+payments",
219
+ normalizedCue: "governed anchor payments",
220
+ recordedAt,
221
+ sessionKey: "test-session",
222
+ nodeRefs: ["node-seed", "node-governed"],
223
+ },
224
+ });
225
+
226
+ const prompts: string[] = [];
227
+ const service = Object.create(EngramAccessService.prototype) as EngramAccessService;
228
+ // Test double: the real Orchestrator needs a live backend, but the graph
229
+ // and the source memories below are read from the REAL store on disk.
230
+ const host = service as unknown as { orchestrator: unknown };
231
+ host.orchestrator = {
232
+ config,
233
+ async getStorage() {
234
+ return storage;
235
+ },
236
+ async searchAcrossNamespaces() {
237
+ return [{ docid: seed.id, path: seed.memory.path, score: 0.9, snippet: "" }];
238
+ },
239
+ localLlm: {
240
+ async chatCompletion(messages: Array<{ content: string }>) {
241
+ prompts.push(messages.map((message) => message.content).join("\n"));
242
+ return { content: JSON.stringify({ action: "STOP", reason: "sufficient" }) };
243
+ },
244
+ },
245
+ qmdResultResolver: testSeedResultResolver(config, storage),
246
+ fastGatewayLlm: null,
247
+ };
248
+
249
+ const result = await service.deepRecall({ query: "acme payments routing decision" });
250
+
251
+ assert.equal(result.ok, true);
252
+ assert.ok(prompts.length > 0, "the policy loop must actually have rendered a state prompt");
253
+ const rendered = prompts.join("\n");
254
+ assert.ok(
255
+ rendered.includes("Alpha holds the payments routing decision"),
256
+ "the active seed really is in the state prompt (the assertions below are not vacuous)",
257
+ );
258
+ assert.ok(
259
+ !rendered.includes("GOVERNED-TITLE"),
260
+ "a node whose only source memory was rejected must not reach the policy prompt",
261
+ );
262
+ assert.ok(
263
+ !rendered.includes("GOVERNED-ANCHOR"),
264
+ "the anchor value attributed through a rejected source must not reach the policy prompt",
265
+ );
266
+ assert.ok(
267
+ !rendered.includes("node-governed"),
268
+ "the governed node must not be offered as a frontier candidate",
269
+ );
270
+ assert.deepEqual(
271
+ result.entries.map((entry) => entry.memoryId),
272
+ [seed.id],
273
+ "only the active memory survives to the entries",
274
+ );
275
+ } finally {
276
+ await rm(memoryDir, { recursive: true, force: true });
277
+ }
278
+ });
279
+ test("deep recall reports private support-passport records as absent, in entries and the policy prompt (issue #2332)", async () => {
280
+ // `loadMemory` handed back the memory view for an ACTIVE private record —
281
+ // the same record memoryGet and the recall pipeline report as absent — so a
282
+ // QMD hit on a `support-passport-card` tag or a `support-passport-*`
283
+ // attribute carried owner-private content into the working set, the state
284
+ // prompt, and the final entries. Private records must behave exactly like
285
+ // the other read surfaces: absent, never an error and never content.
286
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-private-"));
287
+ try {
288
+ const config = parseConfig({
289
+ memoryDir,
290
+ qmdEnabled: false,
291
+ qmdCollection: "remnic-test",
292
+ deepRecall: { enabled: true, maxSteps: 1 },
293
+ });
294
+ const storage = new StorageManager(memoryDir);
295
+ await storage.ensureDirectories();
296
+ const seed = await storage.writeMemory("fact", "Alpha holds the payments routing decision.", {
297
+ tags: ["payments"],
298
+ });
299
+ const card = await storage.writeMemory("fact", "PRIVATE-CARD-CONTENT names the owner card number.", {
300
+ tags: ["support-passport-card"],
301
+ });
302
+ const audit = await storage.writeMemory("fact", "PRIVATE-AUDIT-CONTENT names the audit trail.", {
303
+ structuredAttributes: { "support-passport-owner": "operator" },
304
+ });
305
+ assert.equal(seed.tombstoneBlocked, false, "the seed fixture must actually persist");
306
+ assert.equal(card.tombstoneBlocked, false, "the tagged card fixture must actually persist");
307
+ assert.equal(audit.tombstoneBlocked, false, "the attribute-carrying audit fixture must actually persist");
308
+
309
+ const prompts: string[] = [];
310
+ const service = Object.create(EngramAccessService.prototype) as EngramAccessService;
311
+ const host = service as unknown as { orchestrator: unknown };
312
+ host.orchestrator = {
313
+ config,
314
+ async getStorage() {
315
+ return storage;
316
+ },
317
+ // QMD matches BOTH private variants alongside the public seed.
318
+ async searchAcrossNamespaces() {
319
+ return [
320
+ { docid: seed.id, path: seed.memory.path, score: 0.9, snippet: "" },
321
+ { docid: card.id, path: card.memory.path, score: 0.8, snippet: "" },
322
+ { docid: audit.id, path: audit.memory.path, score: 0.7, snippet: "" },
323
+ ];
324
+ },
325
+ localLlm: {
326
+ async chatCompletion(messages: Array<{ content: string }>) {
327
+ prompts.push(messages.map((message) => message.content).join("\n"));
328
+ return { content: JSON.stringify({ action: "STOP", reason: "sufficient" }) };
329
+ },
330
+ },
331
+ qmdResultResolver: testSeedResultResolver(config, storage),
332
+ fastGatewayLlm: null,
333
+ };
334
+
335
+ const result = await service.deepRecall({ query: "acme payments routing decision" });
336
+
337
+ assert.equal(result.ok, true, "a private record is absent, never an invocation error");
338
+ assert.deepEqual(
339
+ result.entries.map((entry) => entry.memoryId),
340
+ [seed.id],
341
+ "private records must not survive to the entries (tag AND attribute variants)",
342
+ );
343
+ assert.ok(
344
+ result.entries.every((entry) => !/PRIVATE-(CARD|AUDIT)-CONTENT/.test(entry.content)),
345
+ "no entry content carries private card or audit text",
346
+ );
347
+ assert.ok(prompts.length > 0, "the policy loop must actually have rendered a state prompt");
348
+ const rendered = prompts.join("\n");
349
+ assert.ok(
350
+ rendered.includes("Alpha holds the payments routing decision"),
351
+ "the public seed really is in the state prompt (the assertions below are not vacuous)",
352
+ );
353
+ assert.ok(
354
+ !rendered.includes("PRIVATE-CARD-CONTENT"),
355
+ "tagged card content must not reach the policy prompt",
356
+ );
357
+ assert.ok(
358
+ !rendered.includes("PRIVATE-AUDIT-CONTENT"),
359
+ "attribute-carrying audit content must not reach the policy prompt",
360
+ );
361
+ assert.ok(
362
+ !rendered.includes(card.id) && !rendered.includes(audit.id),
363
+ "a private record's id must not appear in the state prompt",
364
+ );
365
+ } finally {
366
+ await rm(memoryDir, { recursive: true, force: true });
367
+ }
368
+ });
369
+
370
+ test("deep recall projects private support-passport sources out of the graph before the policy prompt (issue #2332)", async () => {
371
+ // The active-source projection rebuilt node titles and anchor attribution
372
+ // from every ACTIVE source, which included private support-passport
373
+ // records: a node sourced from an active private memory carried its content
374
+ // into the frontier handed to the policy LLM even when the memory itself
375
+ // never became a seed hit. The projection must treat a private record the
376
+ // way it treats a rejected one — as if it left the eligible set.
377
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-private-graph-"));
378
+ try {
379
+ const config = parseConfig({
380
+ memoryDir,
381
+ qmdEnabled: false,
382
+ qmdCollection: "remnic-test",
383
+ deepRecall: { enabled: true, maxSteps: 1 },
384
+ });
385
+ const storage = new StorageManager(memoryDir);
386
+ await storage.ensureDirectories();
387
+ const seed = await storage.writeMemory("fact", "Alpha holds the payments routing decision.", {
388
+ tags: ["payments"],
389
+ });
390
+ const audit = await storage.writeMemory("fact", "PRIVATE-AUDIT-CONTENT names the audit trail.", {
391
+ structuredAttributes: { "support-passport-owner": "operator" },
392
+ });
393
+ assert.equal(seed.tombstoneBlocked, false, "the seed fixture must actually persist");
394
+ assert.equal(audit.tombstoneBlocked, false, "the attribute-carrying audit fixture must actually persist");
395
+
396
+ const recordedAt = "2026-08-01T00:00:00.000Z";
397
+ const node = (nodeId: string, title: string, sourceMemoryIds: string[]) => ({
398
+ schemaVersion: 1 as const,
399
+ nodeId,
400
+ recordedAt,
401
+ sessionKey: "test-session",
402
+ kind: "topic" as const,
403
+ abstractionLevel: "meso" as const,
404
+ title,
405
+ summary: `Synthetic ${nodeId} summary`,
406
+ sourceMemoryIds,
407
+ });
408
+ await recordAbstractionNode({
409
+ memoryDir,
410
+ node: node("node-seed", "Seeded payments routing topic", [seed.id]),
411
+ });
412
+ await recordAbstractionNode({
413
+ memoryDir,
414
+ node: node("node-private", "PRIVATE-AUDIT-NODE stored title", [audit.id]),
415
+ });
416
+ await recordCueAnchor({
417
+ memoryDir,
418
+ anchor: {
419
+ schemaVersion: 1,
420
+ anchorId: "anchor-shared",
421
+ anchorType: "entity",
422
+ anchorValue: "PRIVATE-AUDIT-ANCHOR+payments",
423
+ normalizedCue: "private audit anchor payments",
424
+ recordedAt,
425
+ sessionKey: "test-session",
426
+ nodeRefs: ["node-seed", "node-private"],
427
+ sourceMemoryIdsByNodeRef: { "node-seed": [seed.id], "node-private": [audit.id] },
428
+ },
429
+ });
430
+
431
+ const prompts: string[] = [];
432
+ const service = Object.create(EngramAccessService.prototype) as EngramAccessService;
433
+ const host = service as unknown as { orchestrator: unknown };
434
+ host.orchestrator = {
435
+ config,
436
+ async getStorage() {
437
+ return storage;
438
+ },
439
+ // QMD matches only the public seed; the private record reaches the
440
+ // invocation only through the graph.
441
+ async searchAcrossNamespaces() {
442
+ return [{ docid: seed.id, path: seed.memory.path, score: 0.9, snippet: "" }];
443
+ },
444
+ localLlm: {
445
+ async chatCompletion(messages: Array<{ content: string }>) {
446
+ prompts.push(messages.map((message) => message.content).join("\n"));
447
+ return { content: JSON.stringify({ action: "STOP", reason: "sufficient" }) };
448
+ },
449
+ },
450
+ qmdResultResolver: testSeedResultResolver(config, storage),
451
+ fastGatewayLlm: null,
452
+ };
453
+
454
+ const result = await service.deepRecall({ query: "acme payments routing decision" });
455
+
456
+ assert.equal(result.ok, true);
457
+ assert.deepEqual(
458
+ result.entries.map((entry) => entry.memoryId),
459
+ [seed.id],
460
+ "the private-sourced graph path adds no entry",
461
+ );
462
+ assert.ok(prompts.length > 0, "the policy loop must actually have rendered a state prompt");
463
+ const rendered = prompts.join("\n");
464
+ assert.ok(
465
+ rendered.includes("Alpha holds the payments routing decision"),
466
+ "the public seed really is in the state prompt (the assertions below are not vacuous)",
467
+ );
468
+ assert.ok(
469
+ !rendered.includes("PRIVATE-AUDIT-CONTENT"),
470
+ "a node title rebuilt from a private source must not reach the policy prompt",
471
+ );
472
+ assert.ok(
473
+ !rendered.includes("node-private"),
474
+ "a node whose only source is private must not be a frontier candidate",
475
+ );
476
+ assert.ok(
477
+ !rendered.includes("PRIVATE-AUDIT-ANCHOR"),
478
+ "an anchor attributed through a private source must not reach the policy prompt",
479
+ );
480
+ } finally {
481
+ await rm(memoryDir, { recursive: true, force: true });
482
+ }
483
+ });
484
+
485
+ test("HTTP deep recall gates the body namespace, not just the query string (issue #2332 review)", async () => {
486
+ // The route used to resolve the scope from `?namespace=` only and then let
487
+ // `body.namespace` override it, so an allowed query namespace plus a foreign
488
+ // body namespace read another tenant. The body value must pass the SAME
489
+ // effective-namespace allow-list gate: 403 before the service is reached.
490
+ const calls: Array<Record<string, unknown>> = [];
491
+ const service = {
492
+ configRef: parseConfig({
493
+ memoryDir: path.join(tmpdir(), "remnic-deep-recall-ns"),
494
+ namespacesEnabled: true,
495
+ defaultNamespace: "default",
496
+ }),
497
+ deepRecall: async (request: Record<string, unknown>) => {
498
+ calls.push(request);
499
+ return { ok: true, entries: [], trace: [], rendered: "" };
500
+ },
501
+ } as unknown as EngramAccessService;
502
+ const server = new EngramAccessHttpServer({
503
+ service,
504
+ port: 0,
505
+ authTokenEntriesGetter: () => [
506
+ { token: "scoped-ns-a", capabilities: { version: 1, namespaces: ["ns_a"] } },
507
+ { token: "operator", capabilities: { version: 1 } },
508
+ ],
509
+ adminConsoleEnabled: false,
510
+ });
511
+ const status = await server.start();
512
+ const postDeep = (token: string, queryNamespace?: string, bodyNamespace?: string) =>
513
+ fetch(
514
+ `http://127.0.0.1:${status.port}/engram/v1/recall/deep${queryNamespace ? `?namespace=${queryNamespace}` : ""}`,
515
+ {
516
+ method: "POST",
517
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
518
+ body: JSON.stringify({ query: "who owns the routing decision", ...(bodyNamespace ? { namespace: bodyNamespace } : {}) }),
519
+ },
520
+ );
521
+ try {
522
+ // The escalation this fix closes: allowed query namespace, foreign body namespace.
523
+ assert.equal(
524
+ (await postDeep("scoped-ns-a", "ns_a", "ns_b")).status,
525
+ 403,
526
+ "scoped deep recall: a body namespace outside the allow-list must be denied (403)",
527
+ );
528
+ assert.equal(calls.length, 0, "scoped deep recall: the service must NOT be reached for a denied namespace");
529
+
530
+ // A body namespace inside the allow-list still works, and still wins.
531
+ const allowed = await postDeep("scoped-ns-a", "ns_a", "ns_a");
532
+ assert.notEqual(allowed.status, 403, "scoped deep recall: an allowed body namespace must proceed");
533
+ assert.equal(calls.at(-1)?.namespace, "ns_a", "the gated body namespace reaches the service");
534
+
535
+ // No body namespace: the query-string fallback is preserved.
536
+ assert.notEqual((await postDeep("scoped-ns-a", "ns_a")).status, 403);
537
+ assert.equal(calls.at(-1)?.namespace, "ns_a", "the query-string namespace still reaches the service");
538
+
539
+ // Unrestricted token: namespace scoping stays a no-op.
540
+ assert.notEqual(
541
+ (await postDeep("operator", "ns_a", "ns_b")).status,
542
+ 403,
543
+ "unrestricted deep recall: namespace scoping must be a no-op",
544
+ );
545
+ assert.equal(calls.at(-1)?.namespace, "ns_b");
546
+ } finally {
547
+ await server.stop();
548
+ }
549
+ });
550
+
551
+ test("deep recall seeds search the resolved namespace's collection, not the base one (issue #2332 review)", async () => {
552
+ // Storage and graph reads resolve through the caller's namespace, so seed
553
+ // retrieval must too: the base `qmdCollection` belongs to the DEFAULT
554
+ // namespace, and querying it from `ns_b` returns default-namespace doc ids
555
+ // (cross-tenant) while missing the caller's own indexed memories.
556
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-seeds-"));
557
+ try {
558
+ const namespace = "ns_b";
559
+ const namespaceDir = path.join(memoryDir, "namespaces", namespace);
560
+ await mkdir(namespaceDir, { recursive: true });
561
+ const config = parseConfig({
562
+ memoryDir,
563
+ namespacesEnabled: true,
564
+ defaultNamespace: "default",
565
+ qmdCollection: "remnic-test",
566
+ });
567
+ const searched: Array<{ query: string; collection?: string }> = [];
568
+ const hitPath = path.join(namespaceDir, "facts", "2026-08-01", "fact-1.md");
569
+ const router = new NamespaceSearchRouter(
570
+ config,
571
+ { storageFor: async () => ({ dir: namespaceDir }) },
572
+ () =>
573
+ ({
574
+ probe: async () => true,
575
+ checkCollection: async () => "present" as const,
576
+ search: async (query: string, collection?: string) => {
577
+ searched.push({ query, collection });
578
+ return [{ docid: "doc-1", path: hitPath, snippet: "", score: 0.7 }];
579
+ },
580
+ }) as unknown as SearchBackend,
581
+ );
582
+
583
+ const storage = {
584
+ dir: namespaceDir,
585
+ readMemoryByPath: async (filePath: string) =>
586
+ filePath === hitPath ? { frontmatter: { id: "mem-alpha" } } : null,
587
+ };
588
+ const searchSeed = createDeepRecallSeedSearch({
589
+ namespace,
590
+ storage,
591
+ router,
592
+ resolver: new QmdResultResolver({
593
+ getConfig: () => config,
594
+ storageFor: async () => storage as unknown as StorageManager,
595
+ storageDirNamespace: () => namespace,
596
+ qmdCollectionNamespaceFromPrefix: (prefix) => qmdCollectionNamespaceFromPrefix(prefix, config),
597
+ namespaceFromPath: () => namespace,
598
+ }),
599
+ });
600
+ const seeds = await searchSeed("payments routing", 5);
601
+
602
+ const expectedCollection = namespaceCollectionName(config.qmdCollection, namespace, {
603
+ defaultNamespace: config.defaultNamespace,
604
+ useLegacyDefaultCollection: false,
605
+ });
606
+ assert.notEqual(
607
+ expectedCollection,
608
+ config.qmdCollection,
609
+ "fixture sanity: a non-default namespace has its own suffixed collection",
610
+ );
611
+ assert.equal(searched.length, 1, "exactly one namespace-routed search per seed retrieval");
612
+ assert.equal(
613
+ searched[0]?.collection,
614
+ expectedCollection,
615
+ "seeds must be retrieved from the resolved namespace's collection",
616
+ );
617
+ assert.deepEqual(
618
+ seeds,
619
+ [{ memoryId: "mem-alpha", score: 0.7 }],
620
+ "each hit resolves to the namespace memory id through the shared result resolver",
621
+ );
622
+ } finally {
623
+ await rm(memoryDir, { recursive: true, force: true });
624
+ }
625
+ });
626
+
627
+ test("deep recall reads the graph from the resolved namespace, not the default store override (issue #2332 review)", async () => {
628
+ // `abstractionNodeStoreDir` defaults to the ROOT memory dir's graph store and
629
+ // wins over `memoryDir` inside the resolver, so passing it for every caller
630
+ // made a non-default namespace load the DEFAULT namespace's nodes/anchors:
631
+ // its own anchor expansions were invisible and a colliding memory id exposed
632
+ // default-namespace graph metadata. The harmonic WRITE path already sends the
633
+ // override to the base namespace only.
634
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-graph-"));
635
+ try {
636
+ const namespace = "ns_b";
637
+ const namespaceDir = path.join(memoryDir, "namespaces", namespace);
638
+ await mkdir(namespaceDir, { recursive: true });
639
+ const defaultStoreOverride = path.join(memoryDir, "default-harmonic-store");
640
+ const config = parseConfig({
641
+ memoryDir,
642
+ namespacesEnabled: true,
643
+ defaultNamespace: "default",
644
+ qmdCollection: "remnic-test",
645
+ abstractionNodeStoreDir: defaultStoreOverride,
646
+ deepRecall: { enabled: true, maxSteps: 2 },
647
+ namespacePolicies: [{ name: namespace, readPrincipals: ["operator"], writePrincipals: ["operator"] }],
648
+ });
649
+
650
+ // Real source memories in the tenant's store: the graph projection every
651
+ // reader shares resolves node sources against the CURRENT active memories,
652
+ // so a node whose sources are not really there is not eligible at all.
653
+ const storage = new StorageManager(namespaceDir);
654
+ await storage.ensureDirectories();
655
+ const seed = await storage.writeMemory("fact", "Alpha holds the payments routing decision.", {
656
+ tags: ["payments"],
657
+ });
658
+ const nsOnly = await storage.writeMemory(
659
+ "fact",
660
+ "The tenant's own anchor-linked fallback contract.",
661
+ { tags: ["payments"] },
662
+ );
663
+ // Reachable ONLY through the default namespace's graph: routing the graph
664
+ // read to that store is what would surface it.
665
+ const defaultLeak = await storage.writeMemory(
666
+ "fact",
667
+ "Metadata reachable only through the default namespace's graph.",
668
+ { tags: ["payments"] },
669
+ );
670
+ for (const written of [seed, nsOnly, defaultLeak]) {
671
+ assert.equal(written.tombstoneBlocked, false, "every fixture memory must actually persist");
672
+ }
673
+
674
+ const recordedAt = "2026-08-01T00:00:00.000Z";
675
+ const node = (nodeId: string, sourceMemoryIds: string[]) => ({
676
+ schemaVersion: 1 as const,
677
+ nodeId,
678
+ recordedAt,
679
+ sessionKey: "test-session",
680
+ kind: "topic" as const,
681
+ abstractionLevel: "meso" as const,
682
+ title: `Topic ${nodeId}`,
683
+ summary: `Synthetic ${nodeId} summary`,
684
+ sourceMemoryIds,
685
+ });
686
+ const anchor = (anchorId: string, nodeRefs: string[]) => ({
687
+ schemaVersion: 1 as const,
688
+ anchorId,
689
+ anchorType: "entity" as const,
690
+ anchorValue: `acme+${anchorId}`,
691
+ normalizedCue: `acme ${anchorId}`,
692
+ recordedAt,
693
+ sessionKey: "test-session",
694
+ nodeRefs,
695
+ });
696
+ // The namespace's own store: the seed's node shares an anchor with the
697
+ // node holding the tenant-only memory.
698
+ for (const own of [node("node-ns-b-seed", [seed.id]), node("node-ns-b-linked", [nsOnly.id])]) {
699
+ await recordAbstractionNode({ memoryDir: namespaceDir, node: own });
700
+ }
701
+ await recordCueAnchor({
702
+ memoryDir: namespaceDir,
703
+ anchor: anchor("anchor-ns-b", ["node-ns-b-seed", "node-ns-b-linked"]),
704
+ });
705
+ // The DEFAULT namespace's store, reached only through the override. It
706
+ // collides on the seed memory and links to the default-only metadata.
707
+ for (const foreign of [node("node-default-seed", [seed.id]), node("node-default-linked", [defaultLeak.id])]) {
708
+ await recordAbstractionNode({ memoryDir, abstractionNodeStoreDir: defaultStoreOverride, node: foreign });
709
+ }
710
+ await recordCueAnchor({
711
+ memoryDir,
712
+ abstractionNodeStoreDir: defaultStoreOverride,
713
+ anchor: anchor("anchor-default", ["node-default-seed", "node-default-linked"]),
714
+ });
715
+
716
+ const seedPath = seed.memory.path;
717
+ const policyScript = [
718
+ JSON.stringify({ action: "EXPAND", expandNodeIds: ["node-ns-b-linked"], reason: "follow the tenant anchor" }),
719
+ JSON.stringify({ action: "STOP", reason: "sufficient" }),
720
+ ];
721
+ const service = Object.create(EngramAccessService.prototype) as EngramAccessService;
722
+ // Test double: deepRecall touches only these orchestrator members, and the
723
+ // real Orchestrator cannot be constructed without a live backend.
724
+ const host = service as unknown as { orchestrator: unknown };
725
+ host.orchestrator = {
726
+ config,
727
+ async getStorage() {
728
+ return storage;
729
+ },
730
+ async searchAcrossNamespaces() {
731
+ return [{ docid: seed.id, path: seedPath, score: 0.9, snippet: "" }];
732
+ },
733
+ localLlm: {
734
+ async chatCompletion() {
735
+ return { content: policyScript.shift() ?? JSON.stringify({ action: "STOP", reason: "script spent" }) };
736
+ },
737
+ },
738
+ qmdResultResolver: testSeedResultResolver(config, storage),
739
+ fastGatewayLlm: null,
740
+ };
741
+
742
+ const result = await service.deepRecall({
743
+ query: "acme payments routing",
744
+ namespace,
745
+ authenticatedPrincipal: "operator",
746
+ });
747
+
748
+ assert.equal(result.ok, true);
749
+ const ids = result.entries.map((entry) => entry.memoryId).sort();
750
+ assert.deepEqual(
751
+ ids,
752
+ [seed.id, nsOnly.id].sort(),
753
+ "the namespace's own anchor expansion is reachable and no default-namespace memory is",
754
+ );
755
+ assert.equal(result.trace[0]?.action, "EXPAND", "the tenant's frontier node was a valid selection");
756
+ assert.ok(
757
+ !JSON.stringify(result).includes(defaultLeak.id),
758
+ "default-namespace graph metadata must never reach a non-default caller",
759
+ );
760
+ } finally {
761
+ await rm(memoryDir, { recursive: true, force: true });
762
+ }
763
+ });
764
+
765
+ test("deep recall reports an unavailable namespace index as backend_unavailable, not empty success (issue #2332 review)", async () => {
766
+ // `searchAcrossNamespaces` contributes [] for an unavailable backend or a
767
+ // missing collection and reports the condition ONLY through
768
+ // `execution.onDegradation`. With no observer the surface answered
769
+ // `ok: true` with zero entries — indistinguishable from a healthy empty
770
+ // index, and contradicting the advertised `backend_unavailable` result.
771
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-degraded-"));
772
+ try {
773
+ const namespace = "ns_b";
774
+ const namespaceDir = path.join(memoryDir, "namespaces", namespace);
775
+ await mkdir(namespaceDir, { recursive: true });
776
+ const config = parseConfig({
777
+ memoryDir,
778
+ namespacesEnabled: true,
779
+ defaultNamespace: "default",
780
+ qmdCollection: "remnic-test",
781
+ });
782
+ const router = new NamespaceSearchRouter(
783
+ config,
784
+ { storageFor: async () => ({ dir: namespaceDir }) },
785
+ () =>
786
+ ({
787
+ probe: async () => false,
788
+ checkCollection: async () => "missing" as const,
789
+ search: async () => {
790
+ throw new Error("an unavailable backend must never be searched");
791
+ },
792
+ }) as unknown as SearchBackend,
793
+ );
794
+ const searchSeed = createDeepRecallSeedSearch({
795
+ namespace,
796
+ storage: { dir: namespaceDir, readMemoryByPath: async () => null },
797
+ router,
798
+ resolver: {
799
+ readQmdResultMemory: async () => {
800
+ throw new Error("the resolver must not run after a seed backend failure");
801
+ },
802
+ },
803
+ });
804
+
805
+ await assert.rejects(
806
+ () => searchSeed("payments routing", 5),
807
+ /deep recall seed search unavailable/,
808
+ "a degraded namespace search must not resolve to an empty seed list",
809
+ );
810
+
811
+ const result = await runBudgetedDeepRecall(
812
+ {
813
+ config: { ...DEEP_RECALL_CONFIG_DEFAULTS, enabled: true, maxSteps: 0 },
814
+ searchSeed,
815
+ loadGraph: async () => ({ nodes: [], anchors: [] }),
816
+ loadMemory: async () => null,
817
+ callPolicy: async () => {
818
+ throw new Error("the policy must not run after a seed backend failure");
819
+ },
820
+ },
821
+ "payments routing",
822
+ );
823
+ assert.equal(result.ok, false, "an unavailable index is a failure, not a healthy empty index");
824
+ assert.equal(result.error, "backend_unavailable");
825
+ assert.deepEqual(result.entries, []);
826
+ } finally {
827
+ await rm(memoryDir, { recursive: true, force: true });
828
+ }
829
+ });
830
+
831
+ test("deep recall seeds resolve collection-qualified paths when namespaces are disabled (issue #2332 review)", async () => {
832
+ // The namespaces-disabled fanout queries the QMD backend directly, so hits
833
+ // keep the indexer's raw path forms instead of the router-resolved absolute
834
+ // paths the enabled path produces: `qmd://<collection>/...`,
835
+ // `<collection>/...`, an empty-hostname `qmd:///...`, and a root-relative
836
+ // `/...`. A miss falls back to the QMD docid, which no storage lookup
837
+ // hydrates, so the default standalone deployment answers with no memories.
838
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-deep-recall-raw-paths-"));
839
+ try {
840
+ const config = parseConfig({
841
+ memoryDir,
842
+ namespacesEnabled: false,
843
+ qmdCollection: "remnic-test",
844
+ });
845
+ const factPath = path.join(memoryDir, "facts", "2026-08-01", "fact-1.md");
846
+ const storage = {
847
+ dir: memoryDir,
848
+ readMemoryByPath: async (filePath: string) =>
849
+ path.resolve(filePath) === path.resolve(factPath)
850
+ ? { frontmatter: { id: "mem-seed-1" } }
851
+ : null,
852
+ };
853
+ const resolver = new QmdResultResolver({
854
+ getConfig: () => config,
855
+ storageFor: async () => storage as unknown as StorageManager,
856
+ storageDirNamespace: () => config.defaultNamespace,
857
+ qmdCollectionNamespaceFromPrefix: (prefix) => qmdCollectionNamespaceFromPrefix(prefix, config),
858
+ namespaceFromPath: () => config.defaultNamespace,
859
+ });
860
+ const rawPaths = [
861
+ `qmd://${config.qmdCollection}/facts/2026-08-01/fact-1.md`,
862
+ `${config.qmdCollection}/facts/2026-08-01/fact-1.md`,
863
+ "qmd:///facts/2026-08-01/fact-1.md",
864
+ "/facts/2026-08-01/fact-1.md",
865
+ ];
866
+ const router = {
867
+ searchAcrossNamespaces: async () => [
868
+ ...rawPaths.map((rawPath, index) => ({
869
+ docid: `hash-doc-${index}`,
870
+ path: rawPath,
871
+ score: 0.5 + index / 10,
872
+ })),
873
+ // A foreign-collection hit stays unresolvable: it keeps its docid and
874
+ // still seeds the working set, but never hydrates into the graph.
875
+ { docid: "hash-doc-foreign", path: "foreign-coll/facts/2026-08-01/fact-1.md", score: 0.9 },
876
+ ],
877
+ };
878
+ const searchSeed = createDeepRecallSeedSearch({
879
+ namespace: config.defaultNamespace,
880
+ storage,
881
+ router,
882
+ resolver,
883
+ });
884
+ const seeds = await searchSeed("payments routing", rawPaths.length + 1);
885
+
886
+ assert.deepEqual(
887
+ seeds.map((seed) => seed.memoryId),
888
+ [
889
+ ...rawPaths.map(() => "mem-seed-1"),
890
+ "hash-doc-foreign",
891
+ ],
892
+ "every collection-qualified raw path form must resolve to the frontmatter id; only a foreign collection keeps its docid",
893
+ );
894
+ assert.deepEqual(
895
+ seeds.map((seed) => seed.score),
896
+ [0.5, 0.6, 0.7, 0.8, 0.9],
897
+ "resolution must not disturb the hit scores",
898
+ );
899
+ } finally {
900
+ await rm(memoryDir, { recursive: true, force: true });
901
+ }
902
+ });