@remnic/core 10.0.7 → 10.0.8

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 (251) hide show
  1. package/dist/access-admin-ops-surface.js +40 -39
  2. package/dist/access-authorization-probe.js +41 -40
  3. package/dist/access-boundary.js +40 -39
  4. package/dist/access-cli.js +97 -96
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +40 -39
  7. package/dist/access-http-briefing.js +41 -40
  8. package/dist/access-http-query.js +42 -41
  9. package/dist/access-http.js +61 -60
  10. package/dist/access-identity-continuity-surface.js +40 -39
  11. package/dist/access-lcm-surface.js +40 -39
  12. package/dist/access-mcp.js +50 -49
  13. package/dist/access-namespace-preflight.js +40 -39
  14. package/dist/access-observe-write-category.js +8 -8
  15. package/dist/access-observe-write-surface.js +40 -39
  16. package/dist/access-offline-manifest.js +5 -5
  17. package/dist/access-operations-batch.js +41 -40
  18. package/dist/access-operations.js +48 -47
  19. package/dist/access-recall-concurrency.js +40 -39
  20. package/dist/access-recall-response.js +40 -39
  21. package/dist/access-recall-surface.js +40 -39
  22. package/dist/access-schema.js +7 -7
  23. package/dist/access-service.js +40 -39
  24. package/dist/access-surface-catalog.js +42 -41
  25. package/dist/access-surface-catalog.js.map +1 -1
  26. package/dist/active-recall.js +13 -13
  27. package/dist/adapters/index.js +4 -4
  28. package/dist/adapters/registry.js +2 -2
  29. package/dist/briefing.js +10 -10
  30. package/dist/buffer.js +6 -6
  31. package/dist/causal-consolidation.js +11 -11
  32. package/dist/{chunk-B2UK6O2O.js → chunk-2K7D6EFT.js} +2 -2
  33. package/dist/{chunk-Z4CETAL7.js → chunk-3ENX2RB4.js} +2 -2
  34. package/dist/{chunk-TPNCVFVI.js → chunk-3NFN4Y4P.js} +4 -4
  35. package/dist/{chunk-HHE57I2Z.js → chunk-3R6SLDTH.js} +2 -2
  36. package/dist/{chunk-WUUE2I2A.js → chunk-3VTNNO2B.js} +4 -4
  37. package/dist/{chunk-ODGXQUPR.js → chunk-4OKPH6FG.js} +2 -2
  38. package/dist/{chunk-M4MAVJJX.js → chunk-55ZVZBX4.js} +2 -2
  39. package/dist/{chunk-VQBGV44L.js → chunk-5EOV7ANU.js} +2 -2
  40. package/dist/{chunk-JH4XB3RX.js → chunk-5FNVK7MW.js} +2 -2
  41. package/dist/{chunk-2SKI5ZSH.js → chunk-5NOPZYNI.js} +7 -7
  42. package/dist/{chunk-E3OSDZ6M.js → chunk-5Q4NQNW7.js} +2 -2
  43. package/dist/chunk-5VC7J76U.js +134 -0
  44. package/dist/chunk-5VC7J76U.js.map +1 -0
  45. package/dist/{chunk-YMVLPVQP.js → chunk-6PHB45IU.js} +2 -2
  46. package/dist/{chunk-QHL33ZDS.js → chunk-76KHEHT2.js} +2 -2
  47. package/dist/{chunk-PBYY4WYM.js → chunk-7O252IE6.js} +2 -2
  48. package/dist/{chunk-5GN5LRQW.js → chunk-7OAIOE5P.js} +2 -2
  49. package/dist/{chunk-YV54LYEB.js → chunk-7UX44C4N.js} +2 -2
  50. package/dist/{chunk-VBNIQ6H2.js → chunk-A6G7K273.js} +3 -3
  51. package/dist/{chunk-NJTW2MUV.js → chunk-AAM66QCP.js} +5 -5
  52. package/dist/{chunk-Q67D4JYB.js → chunk-AMFCRZYB.js} +2 -2
  53. package/dist/{chunk-KMAHEIOH.js → chunk-AV46CT77.js} +2 -2
  54. package/dist/{chunk-7PZHH4KC.js → chunk-AZEZSQ5R.js} +23 -2
  55. package/dist/chunk-AZEZSQ5R.js.map +1 -0
  56. package/dist/{chunk-DSVRW5BD.js → chunk-BPY4RRTF.js} +14 -14
  57. package/dist/{chunk-BSWHZJSP.js → chunk-C3WJNRGR.js} +2 -2
  58. package/dist/{chunk-ZBDTSFRA.js → chunk-CBP4HWCL.js} +3 -3
  59. package/dist/{chunk-D6C4JWSN.js → chunk-CSUAHUOH.js} +2 -2
  60. package/dist/{chunk-JALYQUYO.js → chunk-DGRMWDQY.js} +12 -12
  61. package/dist/{chunk-4EHA327M.js → chunk-DXHVD4DS.js} +3 -3
  62. package/dist/{chunk-CRYAYFUE.js → chunk-E5274WUC.js} +2 -2
  63. package/dist/{chunk-OCIJYYCA.js → chunk-FBVLTTVV.js} +6 -6
  64. package/dist/{chunk-CG2E6WT5.js → chunk-GDI64ITR.js} +3 -3
  65. package/dist/{chunk-Z5IQTRDN.js → chunk-GSAZBZMA.js} +2 -2
  66. package/dist/{chunk-IE2J3V5N.js → chunk-HZVYDWUP.js} +2 -2
  67. package/dist/{chunk-MHGHEBTR.js → chunk-IJWEXCWF.js} +2 -2
  68. package/dist/{chunk-VIPGYPEG.js → chunk-IOM5QSNR.js} +2 -2
  69. package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
  70. package/dist/{chunk-XKEDESWC.js → chunk-JK3DYMOC.js} +2 -2
  71. package/dist/{chunk-ETGXRGSR.js → chunk-JSIQSZXI.js} +3 -3
  72. package/dist/{chunk-F2QIPGTG.js → chunk-JTUKDLAD.js} +2 -2
  73. package/dist/{chunk-M6PEFWJ4.js → chunk-K3DZ3G5I.js} +2 -2
  74. package/dist/{chunk-TVRTJZQ5.js → chunk-KDYQYMJZ.js} +2 -2
  75. package/dist/{chunk-JFGX2DKT.js → chunk-KRIEQCKE.js} +2 -2
  76. package/dist/{chunk-FRRR4KOW.js → chunk-L3Z4NXDO.js} +3 -3
  77. package/dist/{chunk-T5XG6GQI.js → chunk-LOFVW6JA.js} +2 -2
  78. package/dist/{chunk-GTSQJTNS.js → chunk-MAJ2OKE4.js} +8 -8
  79. package/dist/{chunk-J5QL66XK.js → chunk-MB5EJ2CT.js} +5 -5
  80. package/dist/{chunk-RVJPY6E4.js → chunk-MVRIW6MI.js} +2 -2
  81. package/dist/{chunk-QWDH4QNA.js → chunk-MZBWIJJN.js} +8 -8
  82. package/dist/{chunk-OH75RBYF.js → chunk-N44GZ73R.js} +7 -7
  83. package/dist/{chunk-YBSWXKCH.js → chunk-N6UMXWBA.js} +2 -2
  84. package/dist/{chunk-2PBZBNY7.js → chunk-NAHUBSUF.js} +2 -2
  85. package/dist/{chunk-Z2ZUV6FE.js → chunk-NXLQQ6WI.js} +2 -2
  86. package/dist/{chunk-QF7XX74D.js → chunk-NYVKLB2I.js} +28 -28
  87. package/dist/{chunk-V4ACBX3C.js → chunk-OC6JYPZX.js} +21 -44
  88. package/dist/chunk-OC6JYPZX.js.map +1 -0
  89. package/dist/{chunk-645A37SC.js → chunk-P2S6P7X6.js} +2 -2
  90. package/dist/{chunk-PXTZOU5F.js → chunk-PBVNVKYN.js} +2 -2
  91. package/dist/{chunk-3DSRYRGR.js → chunk-QFIPSKPC.js} +2 -2
  92. package/dist/{chunk-GH5R5BIH.js → chunk-QN77HA6P.js} +2 -2
  93. package/dist/{chunk-IJU2ZWPE.js → chunk-QW7G7PWN.js} +2 -2
  94. package/dist/{chunk-3GLQUOL2.js → chunk-R2AWYR4Q.js} +2 -2
  95. package/dist/{chunk-YRULQW5J.js → chunk-RBJQYMMT.js} +4 -4
  96. package/dist/{chunk-EBQCVOGC.js → chunk-RH6QEGCT.js} +2 -2
  97. package/dist/{chunk-PO6MNTWH.js → chunk-RNPJ4GQA.js} +2 -2
  98. package/dist/{chunk-YNSHGVXT.js → chunk-S4GUMZRB.js} +31 -31
  99. package/dist/{chunk-5O4UJ6PM.js → chunk-SQ2O52SU.js} +2 -2
  100. package/dist/{chunk-Y6IHIORY.js → chunk-V4WSQYO6.js} +63 -63
  101. package/dist/{chunk-Y6IHIORY.js.map → chunk-V4WSQYO6.js.map} +1 -1
  102. package/dist/{chunk-65FYHYBE.js → chunk-VSHRMIJH.js} +2 -2
  103. package/dist/{chunk-Y7427SL7.js → chunk-WCGJK4NQ.js} +4 -4
  104. package/dist/{chunk-OX5LMCEQ.js → chunk-WJLGUY27.js} +11 -11
  105. package/dist/{chunk-UL7WKKGZ.js → chunk-WPFSZZ35.js} +2 -2
  106. package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
  107. package/dist/{chunk-UTXWLTX5.js → chunk-YAF3NRNC.js} +2 -2
  108. package/dist/{chunk-BR75HFPC.js → chunk-ZQYAE2C5.js} +2 -2
  109. package/dist/cli.js +74 -73
  110. package/dist/coding/export-okf-codegraph.js +9 -9
  111. package/dist/compounding/engine.js +10 -10
  112. package/dist/config.js +13 -13
  113. package/dist/connectors/codex-materialize-runner.js +10 -10
  114. package/dist/connectors/index.js +10 -10
  115. package/dist/convergence-refresh.js +12 -12
  116. package/dist/corpus-watermark.js +12 -12
  117. package/dist/deep-recall-http-glue.js +8 -8
  118. package/dist/deep-recall.js +2 -2
  119. package/dist/entity-retrieval.js +10 -10
  120. package/dist/external-wiki-access.js +41 -40
  121. package/dist/extraction.js +14 -14
  122. package/dist/harmonic-retrieval.d.ts.map +1 -1
  123. package/dist/harmonic-retrieval.js +12 -11
  124. package/dist/harmonic-source-lookup.d.ts +14 -0
  125. package/dist/harmonic-source-lookup.d.ts.map +1 -0
  126. package/dist/harmonic-source-lookup.js +22 -0
  127. package/dist/harmonic-source-lookup.js.map +1 -0
  128. package/dist/index.js +109 -108
  129. package/dist/lcm/engine.js +2 -2
  130. package/dist/lcm/index.js +4 -4
  131. package/dist/location/index.js +12 -12
  132. package/dist/maintenance/memory-governance.js +10 -10
  133. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
  134. package/dist/maintenance/rebuild-memory-projection-runner.js +12 -12
  135. package/dist/maintenance/rebuild-memory-projection.js +11 -11
  136. package/dist/memory-browse-operations.js +41 -40
  137. package/dist/memory-browse.js +6 -6
  138. package/dist/memory-projection-batch.d.ts +2 -0
  139. package/dist/memory-projection-batch.d.ts.map +1 -1
  140. package/dist/memory-projection-batch.js +4 -1
  141. package/dist/namespaces/migrate.js +11 -11
  142. package/dist/namespaces/search.js +6 -6
  143. package/dist/namespaces/storage.js +10 -10
  144. package/dist/offline-sync-changeset-generations.js +3 -3
  145. package/dist/offline-sync-embedding-generation.js +2 -2
  146. package/dist/offline-sync-snapshot-stream.js +5 -5
  147. package/dist/offline-sync.js +6 -6
  148. package/dist/operator-doctor-corpus.js +13 -13
  149. package/dist/operator-toolkit.js +22 -22
  150. package/dist/orchestration/maintenance.js +19 -19
  151. package/dist/orchestrator.js +97 -96
  152. package/dist/qmd.js +6 -6
  153. package/dist/recall-navigation-operations.js +41 -40
  154. package/dist/recall-navigation.js +40 -39
  155. package/dist/recall-state-view-admission.js +6 -6
  156. package/dist/recall-why-service.js +6 -6
  157. package/dist/resume-bundles.js +13 -13
  158. package/dist/search/factory.js +5 -5
  159. package/dist/search/index.js +7 -7
  160. package/dist/semantic-consolidation.js +11 -11
  161. package/dist/semantic-rule-promotion.js +10 -10
  162. package/dist/semantic-rule-verifier.js +10 -10
  163. package/dist/source-agent-qualifier.js +13 -13
  164. package/dist/standup/build.js +10 -10
  165. package/dist/storage.js +9 -9
  166. package/dist/support-passport/index.js +43 -42
  167. package/dist/transfer/autodetect.js +1 -1
  168. package/dist/transfer/backup.js +1 -1
  169. package/dist/transfer/capsule-export.js +2 -2
  170. package/dist/transfer/export-okf.js +10 -10
  171. package/dist/verified-recall.js +10 -10
  172. package/dist/who-knows-cli.js +11 -11
  173. package/dist/who-knows.js +10 -10
  174. package/package.json +2 -2
  175. package/src/harmonic-retrieval.ts +12 -64
  176. package/src/harmonic-source-lookup.test.ts +178 -0
  177. package/src/harmonic-source-lookup.ts +142 -0
  178. package/src/memory-projection-batch.ts +20 -0
  179. package/src/orchestration/recall-internal.ts +12 -12
  180. package/src/recall-harmonic-qmd-ordering.test.ts +304 -0
  181. package/dist/chunk-7PZHH4KC.js.map +0 -1
  182. package/dist/chunk-V4ACBX3C.js.map +0 -1
  183. /package/dist/{chunk-B2UK6O2O.js.map → chunk-2K7D6EFT.js.map} +0 -0
  184. /package/dist/{chunk-Z4CETAL7.js.map → chunk-3ENX2RB4.js.map} +0 -0
  185. /package/dist/{chunk-TPNCVFVI.js.map → chunk-3NFN4Y4P.js.map} +0 -0
  186. /package/dist/{chunk-HHE57I2Z.js.map → chunk-3R6SLDTH.js.map} +0 -0
  187. /package/dist/{chunk-WUUE2I2A.js.map → chunk-3VTNNO2B.js.map} +0 -0
  188. /package/dist/{chunk-ODGXQUPR.js.map → chunk-4OKPH6FG.js.map} +0 -0
  189. /package/dist/{chunk-M4MAVJJX.js.map → chunk-55ZVZBX4.js.map} +0 -0
  190. /package/dist/{chunk-VQBGV44L.js.map → chunk-5EOV7ANU.js.map} +0 -0
  191. /package/dist/{chunk-JH4XB3RX.js.map → chunk-5FNVK7MW.js.map} +0 -0
  192. /package/dist/{chunk-2SKI5ZSH.js.map → chunk-5NOPZYNI.js.map} +0 -0
  193. /package/dist/{chunk-E3OSDZ6M.js.map → chunk-5Q4NQNW7.js.map} +0 -0
  194. /package/dist/{chunk-YMVLPVQP.js.map → chunk-6PHB45IU.js.map} +0 -0
  195. /package/dist/{chunk-QHL33ZDS.js.map → chunk-76KHEHT2.js.map} +0 -0
  196. /package/dist/{chunk-PBYY4WYM.js.map → chunk-7O252IE6.js.map} +0 -0
  197. /package/dist/{chunk-5GN5LRQW.js.map → chunk-7OAIOE5P.js.map} +0 -0
  198. /package/dist/{chunk-YV54LYEB.js.map → chunk-7UX44C4N.js.map} +0 -0
  199. /package/dist/{chunk-VBNIQ6H2.js.map → chunk-A6G7K273.js.map} +0 -0
  200. /package/dist/{chunk-NJTW2MUV.js.map → chunk-AAM66QCP.js.map} +0 -0
  201. /package/dist/{chunk-Q67D4JYB.js.map → chunk-AMFCRZYB.js.map} +0 -0
  202. /package/dist/{chunk-KMAHEIOH.js.map → chunk-AV46CT77.js.map} +0 -0
  203. /package/dist/{chunk-DSVRW5BD.js.map → chunk-BPY4RRTF.js.map} +0 -0
  204. /package/dist/{chunk-BSWHZJSP.js.map → chunk-C3WJNRGR.js.map} +0 -0
  205. /package/dist/{chunk-ZBDTSFRA.js.map → chunk-CBP4HWCL.js.map} +0 -0
  206. /package/dist/{chunk-D6C4JWSN.js.map → chunk-CSUAHUOH.js.map} +0 -0
  207. /package/dist/{chunk-JALYQUYO.js.map → chunk-DGRMWDQY.js.map} +0 -0
  208. /package/dist/{chunk-4EHA327M.js.map → chunk-DXHVD4DS.js.map} +0 -0
  209. /package/dist/{chunk-CRYAYFUE.js.map → chunk-E5274WUC.js.map} +0 -0
  210. /package/dist/{chunk-OCIJYYCA.js.map → chunk-FBVLTTVV.js.map} +0 -0
  211. /package/dist/{chunk-CG2E6WT5.js.map → chunk-GDI64ITR.js.map} +0 -0
  212. /package/dist/{chunk-Z5IQTRDN.js.map → chunk-GSAZBZMA.js.map} +0 -0
  213. /package/dist/{chunk-IE2J3V5N.js.map → chunk-HZVYDWUP.js.map} +0 -0
  214. /package/dist/{chunk-MHGHEBTR.js.map → chunk-IJWEXCWF.js.map} +0 -0
  215. /package/dist/{chunk-VIPGYPEG.js.map → chunk-IOM5QSNR.js.map} +0 -0
  216. /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
  217. /package/dist/{chunk-XKEDESWC.js.map → chunk-JK3DYMOC.js.map} +0 -0
  218. /package/dist/{chunk-ETGXRGSR.js.map → chunk-JSIQSZXI.js.map} +0 -0
  219. /package/dist/{chunk-F2QIPGTG.js.map → chunk-JTUKDLAD.js.map} +0 -0
  220. /package/dist/{chunk-M6PEFWJ4.js.map → chunk-K3DZ3G5I.js.map} +0 -0
  221. /package/dist/{chunk-TVRTJZQ5.js.map → chunk-KDYQYMJZ.js.map} +0 -0
  222. /package/dist/{chunk-JFGX2DKT.js.map → chunk-KRIEQCKE.js.map} +0 -0
  223. /package/dist/{chunk-FRRR4KOW.js.map → chunk-L3Z4NXDO.js.map} +0 -0
  224. /package/dist/{chunk-T5XG6GQI.js.map → chunk-LOFVW6JA.js.map} +0 -0
  225. /package/dist/{chunk-GTSQJTNS.js.map → chunk-MAJ2OKE4.js.map} +0 -0
  226. /package/dist/{chunk-J5QL66XK.js.map → chunk-MB5EJ2CT.js.map} +0 -0
  227. /package/dist/{chunk-RVJPY6E4.js.map → chunk-MVRIW6MI.js.map} +0 -0
  228. /package/dist/{chunk-QWDH4QNA.js.map → chunk-MZBWIJJN.js.map} +0 -0
  229. /package/dist/{chunk-OH75RBYF.js.map → chunk-N44GZ73R.js.map} +0 -0
  230. /package/dist/{chunk-YBSWXKCH.js.map → chunk-N6UMXWBA.js.map} +0 -0
  231. /package/dist/{chunk-2PBZBNY7.js.map → chunk-NAHUBSUF.js.map} +0 -0
  232. /package/dist/{chunk-Z2ZUV6FE.js.map → chunk-NXLQQ6WI.js.map} +0 -0
  233. /package/dist/{chunk-QF7XX74D.js.map → chunk-NYVKLB2I.js.map} +0 -0
  234. /package/dist/{chunk-645A37SC.js.map → chunk-P2S6P7X6.js.map} +0 -0
  235. /package/dist/{chunk-PXTZOU5F.js.map → chunk-PBVNVKYN.js.map} +0 -0
  236. /package/dist/{chunk-3DSRYRGR.js.map → chunk-QFIPSKPC.js.map} +0 -0
  237. /package/dist/{chunk-GH5R5BIH.js.map → chunk-QN77HA6P.js.map} +0 -0
  238. /package/dist/{chunk-IJU2ZWPE.js.map → chunk-QW7G7PWN.js.map} +0 -0
  239. /package/dist/{chunk-3GLQUOL2.js.map → chunk-R2AWYR4Q.js.map} +0 -0
  240. /package/dist/{chunk-YRULQW5J.js.map → chunk-RBJQYMMT.js.map} +0 -0
  241. /package/dist/{chunk-EBQCVOGC.js.map → chunk-RH6QEGCT.js.map} +0 -0
  242. /package/dist/{chunk-PO6MNTWH.js.map → chunk-RNPJ4GQA.js.map} +0 -0
  243. /package/dist/{chunk-YNSHGVXT.js.map → chunk-S4GUMZRB.js.map} +0 -0
  244. /package/dist/{chunk-5O4UJ6PM.js.map → chunk-SQ2O52SU.js.map} +0 -0
  245. /package/dist/{chunk-65FYHYBE.js.map → chunk-VSHRMIJH.js.map} +0 -0
  246. /package/dist/{chunk-Y7427SL7.js.map → chunk-WCGJK4NQ.js.map} +0 -0
  247. /package/dist/{chunk-OX5LMCEQ.js.map → chunk-WJLGUY27.js.map} +0 -0
  248. /package/dist/{chunk-UL7WKKGZ.js.map → chunk-WPFSZZ35.js.map} +0 -0
  249. /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
  250. /package/dist/{chunk-UTXWLTX5.js.map → chunk-YAF3NRNC.js.map} +0 -0
  251. /package/dist/{chunk-BR75HFPC.js.map → chunk-ZQYAE2C5.js.map} +0 -0
@@ -4,14 +4,15 @@ import {
4
4
  renderWhoKnows,
5
5
  renderWhoKnowsJson,
6
6
  renderWhoKnowsText
7
- } from "./chunk-JFGX2DKT.js";
8
- import "./chunk-Z4CETAL7.js";
9
- import "./chunk-OX5LMCEQ.js";
10
- import "./chunk-LZKOVXMX.js";
7
+ } from "./chunk-KRIEQCKE.js";
8
+ import "./chunk-3ENX2RB4.js";
9
+ import "./chunk-WJLGUY27.js";
11
10
  import "./chunk-UHVXE24Y.js";
11
+ import "./chunk-LZKOVXMX.js";
12
12
  import "./chunk-V4JZCTMK.js";
13
- import "./chunk-YV54LYEB.js";
14
- import "./chunk-J5QL66XK.js";
13
+ import "./chunk-7UX44C4N.js";
14
+ import "./chunk-MB5EJ2CT.js";
15
+ import "./chunk-CYEPCZN5.js";
15
16
  import "./chunk-T3FMRLWQ.js";
16
17
  import "./chunk-7VO6E5UD.js";
17
18
  import "./chunk-5BUCKFRE.js";
@@ -19,7 +20,6 @@ import "./chunk-S7LWX2PQ.js";
19
20
  import "./chunk-T2YX56QI.js";
20
21
  import "./chunk-4O6WRBGE.js";
21
22
  import "./chunk-GUMZC2BO.js";
22
- import "./chunk-CYEPCZN5.js";
23
23
  import "./chunk-TFA6HPOA.js";
24
24
  import "./chunk-F4TWWUMP.js";
25
25
  import "./chunk-72A4L5JJ.js";
@@ -28,8 +28,8 @@ import "./chunk-S2V7RUCL.js";
28
28
  import "./chunk-X3724WL4.js";
29
29
  import "./chunk-MRPOZBJC.js";
30
30
  import "./chunk-S2HLHAZ4.js";
31
- import "./chunk-DM2T26WE.js";
32
31
  import "./chunk-LDXUBPMO.js";
32
+ import "./chunk-DM2T26WE.js";
33
33
  import "./chunk-FF6N7BXC.js";
34
34
  import "./chunk-2DT7PIDZ.js";
35
35
  import "./chunk-AAD2ZS6P.js";
@@ -49,12 +49,12 @@ import "./chunk-5WITETWU.js";
49
49
  import "./chunk-6CIAGZ6X.js";
50
50
  import "./chunk-7CT3AI52.js";
51
51
  import "./chunk-G7D6GZ5J.js";
52
- import "./chunk-JALYQUYO.js";
53
- import "./chunk-IUM3VSHI.js";
52
+ import "./chunk-DGRMWDQY.js";
54
53
  import "./chunk-G26YNEOC.js";
54
+ import "./chunk-SIGAGKNE.js";
55
+ import "./chunk-IUM3VSHI.js";
55
56
  import "./chunk-WVHRNAXL.js";
56
57
  import "./chunk-TPSLVZHS.js";
57
- import "./chunk-SIGAGKNE.js";
58
58
  import "./chunk-23G5P7TA.js";
59
59
  import "./chunk-C64XATK7.js";
60
60
  import "./chunk-42JKGUFJ.js";
package/dist/who-knows.js CHANGED
@@ -9,13 +9,14 @@ import {
9
9
  loadWhoKnowsEntities,
10
10
  tokenizeTopic,
11
11
  validateWhoKnowsInput
12
- } from "./chunk-Z4CETAL7.js";
13
- import "./chunk-OX5LMCEQ.js";
14
- import "./chunk-LZKOVXMX.js";
12
+ } from "./chunk-3ENX2RB4.js";
13
+ import "./chunk-WJLGUY27.js";
15
14
  import "./chunk-UHVXE24Y.js";
15
+ import "./chunk-LZKOVXMX.js";
16
16
  import "./chunk-V4JZCTMK.js";
17
- import "./chunk-YV54LYEB.js";
18
- import "./chunk-J5QL66XK.js";
17
+ import "./chunk-7UX44C4N.js";
18
+ import "./chunk-MB5EJ2CT.js";
19
+ import "./chunk-CYEPCZN5.js";
19
20
  import "./chunk-T3FMRLWQ.js";
20
21
  import "./chunk-7VO6E5UD.js";
21
22
  import "./chunk-5BUCKFRE.js";
@@ -23,7 +24,6 @@ import "./chunk-S7LWX2PQ.js";
23
24
  import "./chunk-T2YX56QI.js";
24
25
  import "./chunk-4O6WRBGE.js";
25
26
  import "./chunk-GUMZC2BO.js";
26
- import "./chunk-CYEPCZN5.js";
27
27
  import "./chunk-TFA6HPOA.js";
28
28
  import "./chunk-F4TWWUMP.js";
29
29
  import "./chunk-72A4L5JJ.js";
@@ -32,8 +32,8 @@ import "./chunk-S2V7RUCL.js";
32
32
  import "./chunk-X3724WL4.js";
33
33
  import "./chunk-MRPOZBJC.js";
34
34
  import "./chunk-S2HLHAZ4.js";
35
- import "./chunk-DM2T26WE.js";
36
35
  import "./chunk-LDXUBPMO.js";
36
+ import "./chunk-DM2T26WE.js";
37
37
  import "./chunk-FF6N7BXC.js";
38
38
  import "./chunk-2DT7PIDZ.js";
39
39
  import "./chunk-AAD2ZS6P.js";
@@ -53,12 +53,12 @@ import "./chunk-5WITETWU.js";
53
53
  import "./chunk-6CIAGZ6X.js";
54
54
  import "./chunk-7CT3AI52.js";
55
55
  import "./chunk-G7D6GZ5J.js";
56
- import "./chunk-JALYQUYO.js";
57
- import "./chunk-IUM3VSHI.js";
56
+ import "./chunk-DGRMWDQY.js";
58
57
  import "./chunk-G26YNEOC.js";
58
+ import "./chunk-SIGAGKNE.js";
59
+ import "./chunk-IUM3VSHI.js";
59
60
  import "./chunk-WVHRNAXL.js";
60
61
  import "./chunk-TPSLVZHS.js";
61
- import "./chunk-SIGAGKNE.js";
62
62
  import "./chunk-23G5P7TA.js";
63
63
  import "./chunk-C64XATK7.js";
64
64
  import "./chunk-42JKGUFJ.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "10.0.7",
3
+ "version": "10.0.8",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -3327,7 +3327,7 @@
3327
3327
  "core"
3328
3328
  ],
3329
3329
  "peerDependencies": {
3330
- "@remnic/coding-graph": "^10.0.7"
3330
+ "@remnic/coding-graph": "^10.0.8"
3331
3331
  },
3332
3332
  "peerDependenciesMeta": {
3333
3333
  "@remnic/coding-graph": {
@@ -1,5 +1,4 @@
1
1
  import path from "node:path";
2
- import { existsSync } from "node:fs";
3
2
  import { realpath } from "node:fs/promises";
4
3
  import { listJsonFilesStrict, readJsonFile } from "./json-store.js";
5
4
  import { throwIfAborted } from "./abort-error.js";
@@ -22,31 +21,11 @@ import { readProjectedMemoryStates } from "./memory-projection-batch.js";
22
21
  import { SecureStoreLockedError } from "./secure-store/secure-fs.js";
23
22
  import { StorageManager } from "./storage.js";
24
23
  import { RECALL_FALLBACK_DIRS } from "./utils/category-dir.js";
25
- import { isPathInsideStorageRoot } from "./storage-paths.js";
24
+ import { readHarmonicSourceMemory, readUnresolvedHarmonicSources } from "./harmonic-source-lookup.js";
26
25
 
27
26
 
28
27
  type SourceMemoryMap = Map<string, MemoryFile>;
29
28
 
30
- // `rootReal` set: a direct probe, which skips the corpus walk's symlink
31
- // hardening, so the resolved file must stay inside the memory root. Null: the
32
- // path came from the contained corpus walk and is already trusted.
33
- async function readMemoryFile(
34
- storage: StorageManager,
35
- rootReal: string | null,
36
- filePath: string,
37
- abortSignal?: AbortSignal,
38
- ): Promise<MemoryFile | null> {
39
- throwIfAborted(abortSignal, "harmonic retrieval aborted");
40
- if (!existsSync(filePath)) return null;
41
- try {
42
- if (rootReal !== null && !isPathInsideStorageRoot(rootReal, await realpath(filePath))) return null;
43
- return await storage.readMemoryByPath(filePath);
44
- } catch (err) {
45
- throwIfAborted(abortSignal, "harmonic retrieval aborted");
46
- if (err instanceof SecureStoreLockedError) throw err;
47
- return null;
48
- }
49
- }
50
29
 
51
30
  // An index older than this is treated as unusable: the date window needed to
52
31
  // cover "created since the last rebuild" would stop being cheap.
@@ -61,13 +40,6 @@ function utcDaysSince(rebuiltMs: number, nowMs: number): string[] {
61
40
  return days;
62
41
  }
63
42
 
64
- // Source ids confirmed absent by a corpus path walk. A node only references a
65
- // source after the source file exists, so a confirmed absence stays true unless
66
- // the file is restored later; the short TTL bounds that. Without this, one
67
- // dangling id (a hard-deleted fact) would force a walk on every recall.
68
- const ABSENT_SOURCE_TTL_MS = 10 * 60_000;
69
- const ABSENT_SOURCE_MAX_ENTRIES = 4096;
70
- const absentSourceIds = new Map<string, number>(); // `${memoryDir}\0${id}` -> expiry ms
71
43
 
72
44
  // Memories the projection has no row for were written after its last rebuild, so
73
45
  // their dated directory is normally one of `days`. Hot and cold tiers are probed
@@ -84,16 +56,7 @@ function unprojectedCandidatePaths(memoryDir: string, id: string, days: string[]
84
56
  return paths;
85
57
  }
86
58
 
87
- /**
88
- * Resolve only the source ids the abstraction nodes reference. Returns null when
89
- * the projection is absent, unreadable, too old, or not known to cover every
90
- * active memory (a scoped or pre-`complete` rebuild), so the caller keeps the
91
- * full-corpus scan. Every file is read from disk, so status, tags and validity
92
- * are current; the index only says where to look. An id the cheap probes cannot
93
- * place (an import filed under an old date, a tier root outside the memory dir)
94
- * is looked up by one contained path-only walk, never a frontmatter parse of the
95
- * corpus.
96
- */
59
+ /** Resolve indexed sources first, then use the durable unresolved-id path cache. */
97
60
  async function readIndexedSourceMemories(options: {
98
61
  memoryDir: string;
99
62
  abortSignal?: AbortSignal;
@@ -103,7 +66,7 @@ async function readIndexedSourceMemories(options: {
103
66
  // The ids go to SQL as bound parameters, so any id the projection holds is safe to look up.
104
67
  const batch = readProjectedMemoryStates(options.memoryDir, options.sourceMemoryIds);
105
68
  const rebuiltMs = Date.parse(batch?.rebuiltAt ?? "");
106
- if (!batch?.complete || !Number.isFinite(rebuiltMs) || options.nowMs - rebuiltMs > INDEX_MAX_AGE_MS) return null;
69
+ if (!batch?.complete || !batch.rebuiltAt || !Number.isFinite(rebuiltMs) || options.nowMs - rebuiltMs > INDEX_MAX_AGE_MS) return null;
107
70
  const rootReal = await realpath(options.memoryDir).catch(() => null);
108
71
  if (rootReal === null) return null;
109
72
 
@@ -120,42 +83,27 @@ async function readIndexedSourceMemories(options: {
120
83
  } catch {
121
84
  safeId = false;
122
85
  }
123
- // An unprojected id that is not a single path segment cannot be probed or
124
- // matched by filename, and the scan keys sources by frontmatter id, so only
125
- // the scan can place it. Rare (legacy/imported ids); worst case is the old cost.
126
- if (!state && !safeId) return null;
127
86
  // state.path was containment-checked by the projection reader.
128
87
  const candidates = state
129
88
  ? [state.path, path.join(options.memoryDir, "cold", state.pathRel)]
130
- : unprojectedCandidatePaths(options.memoryDir, id, days);
89
+ : safeId ? unprojectedCandidatePaths(options.memoryDir, id, days) : [];
131
90
  let found = false;
132
91
  for (const candidate of candidates) {
133
- const memory = await readMemoryFile(storage, rootReal, candidate, options.abortSignal);
92
+ const memory = await readHarmonicSourceMemory(storage, rootReal, candidate, options.abortSignal);
134
93
  if (memory?.frontmatter.id !== id) continue;
135
94
  sourceMemories.set(id, memory);
136
95
  found = true;
137
96
  break;
138
97
  }
139
- const absentKey = `${options.memoryDir}\0${id}`;
140
- if (!found && safeId && (absentSourceIds.get(absentKey) ?? 0) <= options.nowMs) pending.push(id);
98
+ if (!found) pending.push(id);
141
99
  }
142
100
  if (pending.length === 0) return sourceMemories;
143
101
 
144
- const wanted = new Set(pending);
145
- const walked = [
146
- ...(await storage.collectActiveMemoryPaths({ abortSignal: options.abortSignal })),
147
- ...(await storage.collectColdMemoryPaths({ abortSignal: options.abortSignal })),
148
- ];
149
- for (const filePath of walked) {
150
- throwIfAborted(options.abortSignal, "harmonic retrieval aborted");
151
- if (!wanted.has(path.basename(filePath, ".md"))) continue;
152
- const memory = await readMemoryFile(storage, null, filePath, options.abortSignal);
153
- if (memory && wanted.has(memory.frontmatter.id)) sourceMemories.set(memory.frontmatter.id, memory);
154
- }
155
- if (absentSourceIds.size > ABSENT_SOURCE_MAX_ENTRIES) absentSourceIds.clear();
156
- for (const id of pending) {
157
- if (!sourceMemories.has(id)) absentSourceIds.set(`${options.memoryDir}\0${id}`, options.nowMs + ABSENT_SOURCE_TTL_MS);
158
- }
102
+ const unresolved = await readUnresolvedHarmonicSources({
103
+ storage, rootReal, rebuiltAt: batch.rebuiltAt, sourceMemoryIds: pending,
104
+ nowMs: options.nowMs, abortSignal: options.abortSignal,
105
+ });
106
+ for (const [id, memory] of unresolved) sourceMemories.set(id, memory);
159
107
  return sourceMemories;
160
108
  }
161
109
 
@@ -168,7 +116,7 @@ async function readCorpusSourceMemories(options: {
168
116
  const storage = new StorageManager(options.memoryDir);
169
117
  const [hotMemories, coldMemories] = await Promise.all([
170
118
  storage.readAllMemories({ abortSignal: options.abortSignal }),
171
- storage.readAllColdMemories(),
119
+ storage.readAllColdMemories({ abortSignal: options.abortSignal }),
172
120
  ]);
173
121
  for (const memory of [...hotMemories, ...coldMemories]) {
174
122
  throwIfAborted(options.abortSignal, "harmonic retrieval aborted");
@@ -0,0 +1,178 @@
1
+ import assert from "node:assert/strict";
2
+ import { execFile } from "node:child_process";
3
+ import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import path from "node:path";
6
+ import { promisify } from "node:util";
7
+ import test from "node:test";
8
+ import { recordAbstractionNode } from "./abstraction-nodes.js";
9
+ import { readProjectedHarmonicGraph } from "./harmonic-retrieval.js";
10
+ import { rebuildMemoryProjection } from "./maintenance/rebuild-memory-projection.js";
11
+ import { StorageManager } from "./storage.js";
12
+ import { readUnresolvedHarmonicSources } from "./harmonic-source-lookup.js";
13
+ import { bumpMemoryCorpusVersionForDir } from "./memory-corpus-version.js";
14
+
15
+ async function seed(memoryDir: string) {
16
+ const storage = new StorageManager(memoryDir);
17
+ await storage.ensureDirectories();
18
+ const real = await storage.writeMemory("fact", "real harmonic source", { source: "test" });
19
+ await recordAbstractionNode({ memoryDir, node: {
20
+ schemaVersion: 1, nodeId: "source-lookup", recordedAt: new Date().toISOString(),
21
+ sessionKey: "agent:test", kind: "topic", abstractionLevel: "meso",
22
+ title: "source lookup", summary: "source lookup",
23
+ sourceMemoryIds: [real.id, "missing-source", "hot-import", "cold/import"],
24
+ } });
25
+ await rebuildMemoryProjection({ memoryDir, storage, dryRun: false });
26
+ return { storage, real };
27
+ }
28
+
29
+ async function importFile(memoryDir: string, tier: string, name: string, id: string) {
30
+ const dir = path.join(memoryDir, tier, "facts", "2000-01-01");
31
+ await mkdir(dir, { recursive: true });
32
+ await writeFile(path.join(dir, name),
33
+ `---\nid: ${id}\ncategory: fact\ncreated: 2000-01-01T00:00:00.000Z\nupdated: 2000-01-01T00:00:00.000Z\nsource: test\nconfidence: 0.8\nconfidenceTier: implied\ntags: []\n---\n\nImported ${id} body.\n`);
34
+ }
35
+
36
+ test("old-dated imports with non-id filenames resolve in both tiers without a corpus parse (#3188)", async (t) => {
37
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-lookup-"));
38
+ try {
39
+ const { real } = await seed(memoryDir);
40
+ await importFile(memoryDir, "", "unrelated-hot-name.md", "hot-import");
41
+ await importFile(memoryDir, "cold", "unrelated-cold-name.md", "cold/import");
42
+ let scans = 0;
43
+ t.mock.method(StorageManager.prototype, "readAllMemories", async () => { scans += 1; return []; });
44
+ const graph = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
45
+ assert.deepEqual(graph.nodes[0]?.sourceMemoryIds?.slice().sort(), [real.id, "hot-import", "cold/import"].sort());
46
+ assert.equal(scans, 0, "unprojected legacy ids must not force the full frontmatter corpus scan");
47
+ } finally {
48
+ t.mock.restoreAll();
49
+ await rm(memoryDir, { recursive: true, force: true });
50
+ }
51
+ });
52
+
53
+ test("confirmed missing sources survive a fresh process without another path walk (#3188)", async () => {
54
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-durable-"));
55
+ try {
56
+ const { real } = await seed(memoryDir);
57
+ const graph = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
58
+ assert.deepEqual(graph.nodes[0]?.sourceMemoryIds, [real.id]);
59
+ const script = `
60
+ import { StorageManager } from ${JSON.stringify(new URL("./storage.ts", import.meta.url).href)};
61
+ import { readProjectedHarmonicGraph } from ${JSON.stringify(new URL("./harmonic-retrieval.ts", import.meta.url).href)};
62
+ StorageManager.prototype.collectActiveMemoryPaths = async () => { throw new Error("unexpected hot walk after restart"); };
63
+ StorageManager.prototype.collectColdMemoryPaths = async () => { throw new Error("unexpected cold walk after restart"); };
64
+ StorageManager.prototype.readAllMemories = async () => { throw new Error("unexpected corpus scan after restart"); };
65
+ const graph = await readProjectedHarmonicGraph({ memoryDir: ${JSON.stringify(memoryDir)}, anchorsEnabled: false });
66
+ if (graph.nodes[0]?.sourceMemoryIds?.join() !== ${JSON.stringify(real.id)}) throw new Error("source lost after restart");
67
+ `;
68
+ await promisify(execFile)(process.execPath, ["--import", "tsx", "--input-type=module", "-e", script]);
69
+ } finally {
70
+ await rm(memoryDir, { recursive: true, force: true });
71
+ }
72
+ });
73
+
74
+ for (const invalidation of ["ttl", "rebuiltAt", "corpusVersion"]) {
75
+ test(`durable misses invalidate on ${invalidation} without losing an imported source (#3188)`, async () => {
76
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-epoch-"));
77
+ try {
78
+ const { storage } = await seed(memoryDir);
79
+ const nowMs = Date.now();
80
+ const options = {
81
+ storage, rootReal: await realpath(memoryDir), rebuiltAt: new Date(nowMs).toISOString(),
82
+ nowMs, sourceMemoryIds: ["restored-id"],
83
+ };
84
+ assert.equal((await readUnresolvedHarmonicSources(options)).size, 0);
85
+ await importFile(memoryDir, "", "not-the-source-id.md", "restored-id");
86
+ assert.equal((await readUnresolvedHarmonicSources(options)).size, 0, "the existing ten-minute miss window is preserved");
87
+ if (invalidation === "ttl") options.nowMs += 10 * 60_000;
88
+ if (invalidation === "rebuiltAt") options.rebuiltAt = new Date(nowMs + 1).toISOString();
89
+ if (invalidation === "corpusVersion") bumpMemoryCorpusVersionForDir(memoryDir);
90
+ const restored = await readUnresolvedHarmonicSources(options);
91
+ assert.equal(restored.get("restored-id")?.content.trim(), "Imported restored-id body.");
92
+ } finally {
93
+ await rm(memoryDir, { recursive: true, force: true });
94
+ }
95
+ });
96
+ }
97
+
98
+ test("harmonic cold walk receives cancellation and cannot publish a partial absence cache (#3188)", async (t) => {
99
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-cancel-"));
100
+ try {
101
+ await seed(memoryDir);
102
+ await importFile(memoryDir, "cold", "cold-import.md", "cold/import");
103
+ const controller = new AbortController();
104
+ const original = StorageManager.prototype.collectColdMemoryPaths;
105
+ t.mock.method(StorageManager.prototype, "collectColdMemoryPaths", function (
106
+ this: StorageManager, options?: Parameters<StorageManager["collectColdMemoryPaths"]>[0],
107
+ ) {
108
+ assert.equal(options?.abortSignal, controller.signal);
109
+ controller.abort();
110
+ return original.call(this, options);
111
+ });
112
+ await assert.rejects(
113
+ () => readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false, abortSignal: controller.signal }),
114
+ { name: "AbortError" },
115
+ );
116
+ await assert.rejects(() => readFile(path.join(memoryDir, "state/harmonic-source-paths.json")), { code: "ENOENT" });
117
+ t.mock.restoreAll();
118
+ const graph = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
119
+ assert.equal(graph.nodes[0]?.sourceMemoryIds?.includes("cold/import"), true);
120
+ } finally {
121
+ t.mock.restoreAll();
122
+ await rm(memoryDir, { recursive: true, force: true });
123
+ }
124
+ });
125
+
126
+ test("an unreadable/unwritable path cache never suppresses an imported source (#3188)", async () => {
127
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-cache-errors-"));
128
+ try {
129
+ await seed(memoryDir);
130
+ await importFile(memoryDir, "", "not-id.md", "hot-import");
131
+ const cachePath = path.join(memoryDir, "state/harmonic-source-paths.json");
132
+ await mkdir(cachePath);
133
+ const graph = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
134
+ assert.equal(graph.nodes[0]?.sourceMemoryIds?.includes("hot-import"), true);
135
+ await rm(cachePath, { recursive: true });
136
+ await writeFile(cachePath, "{broken");
137
+ const recovered = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
138
+ assert.equal(recovered.nodes[0]?.sourceMemoryIds?.includes("hot-import"), true);
139
+ } finally {
140
+ await rm(memoryDir, { recursive: true, force: true });
141
+ }
142
+ });
143
+
144
+ test("an unresolved opaque id keeps the old scan candidate set for an edited projected file (#3188)", async () => {
145
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-opaque-"));
146
+ try {
147
+ const { storage, real } = await seed(memoryDir);
148
+ const memory = await storage.getMemoryById(real.id);
149
+ assert.ok(memory);
150
+ await writeFile(memory.path,
151
+ `---\nid: cold/import\ncategory: fact\ncreated: 2000-01-01T00:00:00.000Z\nupdated: 2000-01-01T00:00:00.000Z\nsource: test\nconfidence: 0.8\nconfidenceTier: implied\ntags: []\n---\n\nEdited projected legacy source.\n`);
152
+ const graph = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
153
+ assert.deepEqual(graph.nodes[0]?.sourceMemoryIds, ["cold/import"]);
154
+ assert.equal(graph.nodes[0]?.summary, "Edited projected legacy source.");
155
+ } finally {
156
+ await rm(memoryDir, { recursive: true, force: true });
157
+ }
158
+ });
159
+
160
+ test("cached import paths reread source status without another walk (#3188)", async (t) => {
161
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-fresh-status-"));
162
+ try {
163
+ const { real } = await seed(memoryDir);
164
+ await importFile(memoryDir, "", "not-id.md", "hot-import");
165
+ const first = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
166
+ assert.equal(first.nodes[0]?.sourceMemoryIds?.includes("hot-import"), true);
167
+ const filePath = path.join(memoryDir, "facts/2000-01-01/not-id.md");
168
+ await writeFile(filePath, (await readFile(filePath, "utf8")).replace("category: fact", "category: fact\nstatus: quarantined"));
169
+ t.mock.method(StorageManager.prototype, "collectActiveMemoryPaths", async () => { throw new Error("unexpected hot walk"); });
170
+ t.mock.method(StorageManager.prototype, "collectColdMemoryPaths", async () => { throw new Error("unexpected cold walk"); });
171
+ t.mock.method(StorageManager.prototype, "readAllMemories", async () => { throw new Error("unexpected corpus parse"); });
172
+ const second = await readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: false });
173
+ assert.deepEqual(second.nodes[0]?.sourceMemoryIds, [real.id]);
174
+ } finally {
175
+ t.mock.restoreAll();
176
+ await rm(memoryDir, { recursive: true, force: true });
177
+ }
178
+ });
@@ -0,0 +1,142 @@
1
+ import path from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir, realpath } from "node:fs/promises";
4
+ import { throwIfAborted } from "./abort-error.js";
5
+ import { readJsonFile, writeJsonFileAtomic } from "./json-store.js";
6
+ import { readProjectedMemoryPaths } from "./memory-projection-batch.js";
7
+ import { SecureStoreLockedError } from "./secure-store/secure-fs.js";
8
+ import { isPathInsideStorageRoot, isSafeLegacyPathSegment } from "./storage-paths.js";
9
+ import { isRecord } from "./store-contract.js";
10
+ import type { StorageManager } from "./storage.js";
11
+ import type { MemoryFile } from "./types.js";
12
+ import { RECALL_FALLBACK_DIRS } from "./utils/category-dir.js";
13
+
14
+ const LOOKUP_TTL_MS = 10 * 60_000;
15
+ const MAX_CACHE_ENTRIES = 4096;
16
+ type PathEntry = { path: string | null; expiresAt: number };
17
+
18
+ /** Direct and cached probes must retain the walk's realpath containment check. */
19
+ export async function readHarmonicSourceMemory(
20
+ storage: StorageManager,
21
+ rootReal: string,
22
+ filePath: string,
23
+ abortSignal?: AbortSignal,
24
+ ): Promise<MemoryFile | null> {
25
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
26
+ if (!existsSync(filePath)) return null;
27
+ try {
28
+ if (!isPathInsideStorageRoot(rootReal, await realpath(filePath))) return null;
29
+ return await storage.readMemoryByPath(filePath);
30
+ } catch (err) {
31
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
32
+ if (err instanceof SecureStoreLockedError) throw err;
33
+ return null;
34
+ }
35
+ }
36
+
37
+ function isCorpusPath(memoryDir: string, filePath: string): boolean {
38
+ if (!isPathInsideStorageRoot(memoryDir, filePath)) return false;
39
+ const [dir] = path.relative(memoryDir, filePath).split(path.sep);
40
+ return dir === "cold" || RECALL_FALLBACK_DIRS.includes(dir);
41
+ }
42
+
43
+ /** A cache miss is not authoritative: unreadable/invalid cache state keeps the walk. */
44
+ async function readPathCache(
45
+ filePath: string, rebuiltAt: string, corpusVersion: string, nowMs: number,
46
+ ): Promise<Map<string, PathEntry>> {
47
+ try {
48
+ const raw = await readJsonFile(filePath);
49
+ if (!isRecord(raw) || raw.rebuiltAt !== rebuiltAt || raw.corpusVersion !== corpusVersion || !Array.isArray(raw.entries)) return new Map();
50
+ const entries = new Map<string, PathEntry>();
51
+ for (const row of raw.entries) {
52
+ if (!Array.isArray(row) || row.length !== 3) continue;
53
+ const [id, storedPath, expiresAt] = row;
54
+ if (typeof id !== "string" || (storedPath !== null && typeof storedPath !== "string")) continue;
55
+ if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt) || expiresAt <= nowMs || expiresAt > nowMs + LOOKUP_TTL_MS) continue;
56
+ entries.set(id, { path: storedPath, expiresAt });
57
+ }
58
+ return entries;
59
+ } catch {
60
+ return new Map();
61
+ }
62
+ }
63
+
64
+ /** Resolve only unresolved ids; persist paths/absence, never memory content. */
65
+ export async function readUnresolvedHarmonicSources(options: {
66
+ storage: StorageManager;
67
+ rootReal: string;
68
+ rebuiltAt: string;
69
+ sourceMemoryIds: string[];
70
+ nowMs: number;
71
+ abortSignal?: AbortSignal;
72
+ }): Promise<Map<string, MemoryFile>> {
73
+ const { storage, rootReal, rebuiltAt, nowMs, abortSignal } = options;
74
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
75
+ const corpusVersion = storage.getCorpusScanVersion();
76
+ const stateDir = path.join(storage.dir, "state");
77
+ const cachePath = path.join(stateDir, "harmonic-source-paths.json");
78
+ const stateReal = await realpath(stateDir).catch(() => null);
79
+ const safeCache = stateReal !== null && isPathInsideStorageRoot(rootReal, stateReal);
80
+ const cache = safeCache ? await readPathCache(cachePath, rebuiltAt, corpusVersion, nowMs) : new Map<string, PathEntry>();
81
+ const found = new Map<string, MemoryFile>();
82
+ const pending = new Set<string>();
83
+ for (const id of options.sourceMemoryIds) {
84
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
85
+ const entry = cache.get(id);
86
+ if (entry?.path === null) continue;
87
+ if (entry?.path) {
88
+ const candidate = path.resolve(storage.dir, entry.path);
89
+ const memory = isCorpusPath(storage.dir, candidate)
90
+ ? await readHarmonicSourceMemory(storage, rootReal, candidate, abortSignal) : null;
91
+ if (memory?.frontmatter.id === id) {
92
+ found.set(id, memory);
93
+ continue;
94
+ }
95
+ }
96
+ pending.add(id);
97
+ }
98
+ if (pending.size === 0) return found;
99
+
100
+ // Opaque legacy ids previously used the full scan, including edited projected files.
101
+ const inspectProjected = [...pending].some((id) => !isSafeLegacyPathSegment(id));
102
+ const projectedPaths = readProjectedMemoryPaths(storage.dir);
103
+ const walked = [
104
+ ...(await storage.collectActiveMemoryPaths({ abortSignal, propagateReadErrors: true })),
105
+ ...(await storage.collectColdMemoryPaths({ abortSignal, propagateReadErrors: true })),
106
+ ];
107
+ for (const filePath of walked) {
108
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
109
+ // Existing projected files need no parse unless their filename names a wanted id.
110
+ // New/imported files must be keyed by frontmatter id, not their basename (#3188).
111
+ if (!inspectProjected && !pending.has(path.basename(filePath, ".md")) && projectedPaths?.has(filePath)) continue;
112
+ const memory = await readHarmonicSourceMemory(storage, rootReal, filePath, abortSignal);
113
+ if (!memory) continue;
114
+ const id = memory.frontmatter.id;
115
+ if (pending.has(id)) {
116
+ found.set(id, memory);
117
+ cache.set(id, { path: path.relative(storage.dir, filePath), expiresAt: nowMs + LOOKUP_TTL_MS });
118
+ }
119
+ }
120
+ for (const id of pending) {
121
+ if (!found.has(id)) cache.set(id, { path: null, expiresAt: nowMs + LOOKUP_TTL_MS });
122
+ }
123
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
124
+ // A concurrent mutation must not publish absence for a different corpus epoch.
125
+ if (storage.getCorpusScanVersion() === corpusVersion) {
126
+ try {
127
+ await mkdir(stateDir, { recursive: true });
128
+ if (isPathInsideStorageRoot(rootReal, await realpath(stateDir))) {
129
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
130
+ // ponytail: atomic last-writer-wins cache; contention can repeat a walk, not lose sources.
131
+ await writeJsonFileAtomic(cachePath, {
132
+ rebuiltAt, corpusVersion,
133
+ entries: [...cache].slice(-MAX_CACHE_ENTRIES).map(([id, entry]) => [id, entry.path, entry.expiresAt]),
134
+ });
135
+ }
136
+ } catch {
137
+ throwIfAborted(abortSignal, "harmonic retrieval aborted");
138
+ // Cache publication is optional; resolved memories remain usable on a read-only store.
139
+ }
140
+ }
141
+ return found;
142
+ }
@@ -1,4 +1,6 @@
1
+ import path from "node:path";
1
2
  import type { MemoryProjectionCurrentState } from "./types.js";
3
+ import { isRecord } from "./store-contract.js";
2
4
  import {
3
5
  memoryCurrentSelectExpressions,
4
6
  openProjectionReadonly,
@@ -80,3 +82,21 @@ export function readProjectedMemoryStates(
80
82
  db.close();
81
83
  }
82
84
  }
85
+
86
+ /** Path-only census used to avoid parsing already-projected files during a fallback lookup. */
87
+ export function readProjectedMemoryPaths(memoryDir: string): Set<string> | null {
88
+ const db = openProjectionReadonly(memoryDir);
89
+ if (!db) return null;
90
+ try {
91
+ const paths = new Set<string>();
92
+ for (const row of db.prepare("SELECT path_rel FROM memory_current").iterate()) {
93
+ const relative = isRecord(row) ? row.path_rel : undefined;
94
+ if (typeof relative === "string") paths.add(path.resolve(memoryDir, relative));
95
+ }
96
+ return paths;
97
+ } catch {
98
+ return null;
99
+ } finally {
100
+ db.close();
101
+ }
102
+ }
@@ -3648,18 +3648,6 @@ export class RecallInternalCoordinator {
3648
3648
  this.deps.appendRecallSection(sectionBuckets, "trust-zones", trustZoneSection);
3649
3649
  }
3650
3650
 
3651
- const harmonicRetrievalSection = await awaitEnrichmentSection(
3652
- "harmonic",
3653
- harmonicRetrievalPromise,
3654
- );
3655
- if (harmonicRetrievalSection) {
3656
- this.deps.appendRecallSection(
3657
- sectionBuckets,
3658
- "harmonic-retrieval",
3659
- harmonicRetrievalSection,
3660
- );
3661
- }
3662
-
3663
3651
  if (verifiedRecallSection) {
3664
3652
  this.deps.appendRecallSection(
3665
3653
  sectionBuckets,
@@ -4780,6 +4768,18 @@ export class RecallInternalCoordinator {
4780
4768
  }
4781
4769
  }
4782
4770
  const phase2AfterQmdMs = Date.now() - recallStart;
4771
+
4772
+ const harmonicRetrievalSection = await awaitEnrichmentSection(
4773
+ "harmonic",
4774
+ harmonicRetrievalPromise,
4775
+ );
4776
+ if (harmonicRetrievalSection) {
4777
+ this.deps.appendRecallSection(
4778
+ sectionBuckets,
4779
+ "harmonic-retrieval",
4780
+ harmonicRetrievalSection,
4781
+ );
4782
+ }
4783
4783
  if (shouldPersistGraphSnapshot) {
4784
4784
  if (!graphSnapshotStatus) {
4785
4785
  graphSnapshotStatus = "skipped";