@remnic/core 10.0.95 → 10.0.96

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 (227) hide show
  1. package/dist/abstraction-nodes.d.ts.map +1 -1
  2. package/dist/abstraction-nodes.js +7 -1
  3. package/dist/access-admin-ops-surface.js +91 -90
  4. package/dist/access-audit.js +2 -2
  5. package/dist/access-cli.js +91 -90
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-extraction-force-flush.js +91 -90
  8. package/dist/access-http-briefing.js +91 -90
  9. package/dist/access-http-query.js +91 -90
  10. package/dist/access-http.js +91 -90
  11. package/dist/access-identity-continuity-surface.js +91 -90
  12. package/dist/access-lcm-surface.js +91 -90
  13. package/dist/access-mcp.js +91 -90
  14. package/dist/access-observe-write-surface.js +91 -90
  15. package/dist/access-operations-batch.js +91 -90
  16. package/dist/access-operations.js +91 -90
  17. package/dist/access-recall-concurrency.js +91 -90
  18. package/dist/access-recall-response.js +91 -90
  19. package/dist/access-recall-surface.js +91 -90
  20. package/dist/access-schema.d.ts +5 -5
  21. package/dist/access-service.js +91 -90
  22. package/dist/active-recall.js +16 -16
  23. package/dist/briefing.js +15 -15
  24. package/dist/buffer.js +7 -7
  25. package/dist/calibration.js +2 -2
  26. package/dist/{capsule-merge-6IDAYTKG.js → capsule-merge-ILR35DIB.js} +10 -10
  27. package/dist/causal-consolidation.js +16 -16
  28. package/dist/{chunk-QHARXWTC.js → chunk-2LCX6TAD.js} +2 -2
  29. package/dist/{chunk-UN4RVUG2.js → chunk-2R3ACFYN.js} +3 -3
  30. package/dist/{chunk-RQFXZYTF.js → chunk-33C4BK7I.js} +4 -4
  31. package/dist/{chunk-UBEKET2V.js → chunk-3R63SXC5.js} +4 -4
  32. package/dist/{chunk-LNGELD2M.js → chunk-3VWU3KF3.js} +2 -2
  33. package/dist/{chunk-FWRXW7CZ.js → chunk-4CXK3GT3.js} +29 -29
  34. package/dist/{chunk-DNY63QWJ.js → chunk-4HK7DYOL.js} +2 -2
  35. package/dist/{chunk-LLPMMMWG.js → chunk-4IJFJQPD.js} +2 -2
  36. package/dist/{chunk-GBJUTX44.js → chunk-4WKR4P7H.js} +2 -2
  37. package/dist/{chunk-ILY5SSOM.js → chunk-53ZVHXVZ.js} +6 -6
  38. package/dist/{chunk-FQAQ63QA.js → chunk-5AJIGWY4.js} +2 -2
  39. package/dist/{chunk-CIWMTNHW.js → chunk-5NFF2TSD.js} +2 -2
  40. package/dist/{chunk-D5XG2KPS.js → chunk-7E7J7QXB.js} +2 -2
  41. package/dist/{chunk-2WCLTWV2.js → chunk-7NVK6AGC.js} +2 -2
  42. package/dist/{chunk-PC5DRBGD.js → chunk-AP4BFWSW.js} +2 -2
  43. package/dist/{chunk-CL4JU3DF.js → chunk-B7L4ZYFY.js} +3 -3
  44. package/dist/{chunk-LQ5SS4OP.js → chunk-D6IGZYTN.js} +3 -3
  45. package/dist/{chunk-XJ2S5UBF.js → chunk-DWWX3FU3.js} +3 -3
  46. package/dist/{chunk-IL3RHBWM.js → chunk-EJ23D4JG.js} +3 -3
  47. package/dist/{chunk-F4QFU5IZ.js → chunk-EZ7XJJ5L.js} +2 -2
  48. package/dist/{chunk-FZZMLGWQ.js → chunk-F4BUSX3D.js} +2 -2
  49. package/dist/{chunk-LXATZLSC.js → chunk-FL3O34XF.js} +12 -5
  50. package/dist/chunk-FL3O34XF.js.map +1 -0
  51. package/dist/{chunk-DPPLNZ3A.js → chunk-FM4P7HSW.js} +4 -4
  52. package/dist/{chunk-QDESY76F.js → chunk-FQACH7MC.js} +2 -2
  53. package/dist/{chunk-XPXHQI6N.js → chunk-FVMEZ57K.js} +2 -2
  54. package/dist/{chunk-F2XNSIPP.js → chunk-G2CGIFRP.js} +10 -3
  55. package/dist/chunk-G2CGIFRP.js.map +1 -0
  56. package/dist/{chunk-ZDV5YWID.js → chunk-GLDIMGOS.js} +6 -6
  57. package/dist/{chunk-YIXJSW5M.js → chunk-HA5DASDG.js} +3 -3
  58. package/dist/{chunk-S3S72HRM.js → chunk-HCCRWSP7.js} +2 -2
  59. package/dist/{chunk-NMGHGTVR.js → chunk-HRMOKGQM.js} +6 -6
  60. package/dist/{chunk-OUVYYKYU.js → chunk-IBKGQ6EY.js} +2 -2
  61. package/dist/{chunk-RNG7S3FY.js → chunk-ICHLN5L6.js} +6 -6
  62. package/dist/chunk-ITAUMNQU.js +40 -0
  63. package/dist/chunk-ITAUMNQU.js.map +1 -0
  64. package/dist/{chunk-R23JWJD3.js → chunk-J426U7XK.js} +9 -9
  65. package/dist/{chunk-QMFAMOT2.js → chunk-JH5AV542.js} +2 -2
  66. package/dist/{chunk-WGGRZYSQ.js → chunk-KE3JWIZK.js} +8 -8
  67. package/dist/{chunk-SQ3GEIEL.js → chunk-KUQLUYIR.js} +5 -5
  68. package/dist/{chunk-RKABLU5M.js → chunk-KVHGCISP.js} +9 -9
  69. package/dist/{chunk-LS7PT3YD.js → chunk-LRLQ2PRP.js} +5 -5
  70. package/dist/{chunk-X5HGU2EM.js → chunk-NT772J7I.js} +2 -2
  71. package/dist/{chunk-BPK32FRM.js → chunk-NTDHSV7N.js} +61 -6
  72. package/dist/chunk-NTDHSV7N.js.map +1 -0
  73. package/dist/{chunk-4EZWSDFL.js → chunk-NX4XDDFQ.js} +3 -3
  74. package/dist/{chunk-IAYUTGF2.js → chunk-O3GZPLVY.js} +4 -4
  75. package/dist/{chunk-IGOW4TJE.js → chunk-O3JG4PD3.js} +2 -2
  76. package/dist/{chunk-UHMHJX5G.js → chunk-OIG4ZFMB.js} +4 -4
  77. package/dist/{chunk-IQ55QGY5.js → chunk-R4JYSBZE.js} +2 -2
  78. package/dist/{chunk-Z3FTSHT5.js → chunk-RUQE5C32.js} +2 -2
  79. package/dist/{chunk-ESLOLCD4.js → chunk-RUWKIPWJ.js} +2 -2
  80. package/dist/{chunk-W54Z2MOP.js → chunk-RXSGBO7Y.js} +2 -2
  81. package/dist/{chunk-DDATG2SF.js → chunk-TFMLRVMW.js} +127 -126
  82. package/dist/{chunk-DDATG2SF.js.map → chunk-TFMLRVMW.js.map} +1 -1
  83. package/dist/{chunk-BOK6BN5M.js → chunk-THV6PZ7V.js} +2 -2
  84. package/dist/{chunk-J7BIFOYP.js → chunk-U6TC6Z5B.js} +2 -2
  85. package/dist/{chunk-X2BVB74H.js → chunk-UZQXJDOE.js} +2 -2
  86. package/dist/{chunk-SUTEBYGI.js → chunk-V7T45MI6.js} +2 -2
  87. package/dist/{chunk-A4F7OIC5.js → chunk-WVIWDMFB.js} +2 -2
  88. package/dist/{chunk-BN2EBCLB.js → chunk-XPCUKWLE.js} +2 -2
  89. package/dist/{chunk-OJXK5C6Z.js → chunk-ZG4GNPRE.js} +7 -7
  90. package/dist/{chunk-TYRWSE4B.js → chunk-ZUMHQEW4.js} +2 -2
  91. package/dist/{chunk-DAOMWGO2.js → chunk-ZX5N5D6M.js} +4 -4
  92. package/dist/cli.js +91 -90
  93. package/dist/coding/export-okf-codegraph.js +14 -14
  94. package/dist/compounding/engine.js +16 -16
  95. package/dist/config.js +16 -16
  96. package/dist/connectors/codex-materialize-runner.js +15 -15
  97. package/dist/connectors/index.js +15 -15
  98. package/dist/consolidation-provenance-check.js +8 -8
  99. package/dist/consolidation-undo.js +6 -6
  100. package/dist/contradiction/index.js +6 -6
  101. package/dist/convergence-refresh.js +17 -17
  102. package/dist/corpus-watermark.js +17 -17
  103. package/dist/cue-anchors.d.ts.map +1 -1
  104. package/dist/cue-anchors.js +8 -2
  105. package/dist/{entity-canonical-id-migration-JJSTZF73.js → entity-canonical-id-migration-IG77BOGD.js} +5 -5
  106. package/dist/entity-mention-index-cache.js +2 -2
  107. package/dist/entity-retrieval.js +16 -16
  108. package/dist/explicit-capture.js +91 -90
  109. package/dist/extraction.js +21 -21
  110. package/dist/harmonic-construction.js +9 -3
  111. package/dist/harmonic-graph-version.d.ts +14 -0
  112. package/dist/harmonic-graph-version.d.ts.map +1 -0
  113. package/dist/harmonic-graph-version.js +17 -0
  114. package/dist/harmonic-retrieval.d.ts +19 -14
  115. package/dist/harmonic-retrieval.d.ts.map +1 -1
  116. package/dist/harmonic-retrieval.js +22 -17
  117. package/dist/index.js +105 -104
  118. package/dist/lcm/engine.js +3 -3
  119. package/dist/lcm/index.js +8 -8
  120. package/dist/local-llm.js +2 -2
  121. package/dist/location/index.js +16 -16
  122. package/dist/maintenance/memory-governance.js +15 -15
  123. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +15 -15
  124. package/dist/maintenance/rebuild-memory-projection-runner.js +17 -17
  125. package/dist/maintenance/rebuild-memory-projection.js +16 -16
  126. package/dist/memory-browse.js +7 -7
  127. package/dist/namespaces/migrate.js +16 -16
  128. package/dist/namespaces/search.js +6 -6
  129. package/dist/namespaces/storage.js +15 -15
  130. package/dist/operator-doctor-corpus.js +18 -18
  131. package/dist/operator-toolkit.js +24 -24
  132. package/dist/orchestration/backlog-scheduler.js +92 -91
  133. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  134. package/dist/orchestration/buffer-drain-runner.js +92 -91
  135. package/dist/orchestration/maintenance.js +25 -25
  136. package/dist/orchestrator.js +93 -92
  137. package/dist/page-versioning.js +6 -6
  138. package/dist/qmd.js +4 -4
  139. package/dist/recall-navigation.js +91 -90
  140. package/dist/recall-state-view-admission.js +7 -7
  141. package/dist/recall-why-service.js +7 -7
  142. package/dist/{resolution-OVVIF7ZY.js → resolution-G4SLHSEU.js} +7 -7
  143. package/dist/resolution-G4SLHSEU.js.map +1 -0
  144. package/dist/resume-bundles.js +17 -17
  145. package/dist/schemas.d.ts +9 -9
  146. package/dist/search/factory.js +5 -5
  147. package/dist/search/index.js +5 -5
  148. package/dist/semantic-consolidation.js +16 -16
  149. package/dist/semantic-rule-promotion.js +15 -15
  150. package/dist/semantic-rule-verifier.js +15 -15
  151. package/dist/source-agent-qualifier.js +18 -18
  152. package/dist/standup/build.js +15 -15
  153. package/dist/storage.js +14 -14
  154. package/dist/summarizer.js +3 -3
  155. package/dist/support-passport/index.js +3 -3
  156. package/dist/transfer/capsule-import.js +8 -8
  157. package/dist/transfer/export-okf.js +15 -15
  158. package/dist/verified-recall.js +15 -15
  159. package/dist/who-knows-cli.js +16 -16
  160. package/dist/who-knows.js +15 -15
  161. package/package.json +2 -2
  162. package/src/abstraction-nodes.ts +7 -2
  163. package/src/cue-anchors.ts +8 -3
  164. package/src/harmonic-graph-cache.test.ts +244 -0
  165. package/src/harmonic-graph-version.ts +46 -0
  166. package/src/harmonic-retrieval.ts +92 -2
  167. package/src/orchestration/recall-internal.ts +1 -1
  168. package/dist/chunk-BPK32FRM.js.map +0 -1
  169. package/dist/chunk-F2XNSIPP.js.map +0 -1
  170. package/dist/chunk-LXATZLSC.js.map +0 -1
  171. /package/dist/{capsule-merge-6IDAYTKG.js.map → capsule-merge-ILR35DIB.js.map} +0 -0
  172. /package/dist/{chunk-QHARXWTC.js.map → chunk-2LCX6TAD.js.map} +0 -0
  173. /package/dist/{chunk-UN4RVUG2.js.map → chunk-2R3ACFYN.js.map} +0 -0
  174. /package/dist/{chunk-RQFXZYTF.js.map → chunk-33C4BK7I.js.map} +0 -0
  175. /package/dist/{chunk-UBEKET2V.js.map → chunk-3R63SXC5.js.map} +0 -0
  176. /package/dist/{chunk-LNGELD2M.js.map → chunk-3VWU3KF3.js.map} +0 -0
  177. /package/dist/{chunk-FWRXW7CZ.js.map → chunk-4CXK3GT3.js.map} +0 -0
  178. /package/dist/{chunk-DNY63QWJ.js.map → chunk-4HK7DYOL.js.map} +0 -0
  179. /package/dist/{chunk-LLPMMMWG.js.map → chunk-4IJFJQPD.js.map} +0 -0
  180. /package/dist/{chunk-GBJUTX44.js.map → chunk-4WKR4P7H.js.map} +0 -0
  181. /package/dist/{chunk-ILY5SSOM.js.map → chunk-53ZVHXVZ.js.map} +0 -0
  182. /package/dist/{chunk-FQAQ63QA.js.map → chunk-5AJIGWY4.js.map} +0 -0
  183. /package/dist/{chunk-CIWMTNHW.js.map → chunk-5NFF2TSD.js.map} +0 -0
  184. /package/dist/{chunk-D5XG2KPS.js.map → chunk-7E7J7QXB.js.map} +0 -0
  185. /package/dist/{chunk-2WCLTWV2.js.map → chunk-7NVK6AGC.js.map} +0 -0
  186. /package/dist/{chunk-PC5DRBGD.js.map → chunk-AP4BFWSW.js.map} +0 -0
  187. /package/dist/{chunk-CL4JU3DF.js.map → chunk-B7L4ZYFY.js.map} +0 -0
  188. /package/dist/{chunk-LQ5SS4OP.js.map → chunk-D6IGZYTN.js.map} +0 -0
  189. /package/dist/{chunk-XJ2S5UBF.js.map → chunk-DWWX3FU3.js.map} +0 -0
  190. /package/dist/{chunk-IL3RHBWM.js.map → chunk-EJ23D4JG.js.map} +0 -0
  191. /package/dist/{chunk-F4QFU5IZ.js.map → chunk-EZ7XJJ5L.js.map} +0 -0
  192. /package/dist/{chunk-FZZMLGWQ.js.map → chunk-F4BUSX3D.js.map} +0 -0
  193. /package/dist/{chunk-DPPLNZ3A.js.map → chunk-FM4P7HSW.js.map} +0 -0
  194. /package/dist/{chunk-QDESY76F.js.map → chunk-FQACH7MC.js.map} +0 -0
  195. /package/dist/{chunk-XPXHQI6N.js.map → chunk-FVMEZ57K.js.map} +0 -0
  196. /package/dist/{chunk-ZDV5YWID.js.map → chunk-GLDIMGOS.js.map} +0 -0
  197. /package/dist/{chunk-YIXJSW5M.js.map → chunk-HA5DASDG.js.map} +0 -0
  198. /package/dist/{chunk-S3S72HRM.js.map → chunk-HCCRWSP7.js.map} +0 -0
  199. /package/dist/{chunk-NMGHGTVR.js.map → chunk-HRMOKGQM.js.map} +0 -0
  200. /package/dist/{chunk-OUVYYKYU.js.map → chunk-IBKGQ6EY.js.map} +0 -0
  201. /package/dist/{chunk-RNG7S3FY.js.map → chunk-ICHLN5L6.js.map} +0 -0
  202. /package/dist/{chunk-R23JWJD3.js.map → chunk-J426U7XK.js.map} +0 -0
  203. /package/dist/{chunk-QMFAMOT2.js.map → chunk-JH5AV542.js.map} +0 -0
  204. /package/dist/{chunk-WGGRZYSQ.js.map → chunk-KE3JWIZK.js.map} +0 -0
  205. /package/dist/{chunk-SQ3GEIEL.js.map → chunk-KUQLUYIR.js.map} +0 -0
  206. /package/dist/{chunk-RKABLU5M.js.map → chunk-KVHGCISP.js.map} +0 -0
  207. /package/dist/{chunk-LS7PT3YD.js.map → chunk-LRLQ2PRP.js.map} +0 -0
  208. /package/dist/{chunk-X5HGU2EM.js.map → chunk-NT772J7I.js.map} +0 -0
  209. /package/dist/{chunk-4EZWSDFL.js.map → chunk-NX4XDDFQ.js.map} +0 -0
  210. /package/dist/{chunk-IAYUTGF2.js.map → chunk-O3GZPLVY.js.map} +0 -0
  211. /package/dist/{chunk-IGOW4TJE.js.map → chunk-O3JG4PD3.js.map} +0 -0
  212. /package/dist/{chunk-UHMHJX5G.js.map → chunk-OIG4ZFMB.js.map} +0 -0
  213. /package/dist/{chunk-IQ55QGY5.js.map → chunk-R4JYSBZE.js.map} +0 -0
  214. /package/dist/{chunk-Z3FTSHT5.js.map → chunk-RUQE5C32.js.map} +0 -0
  215. /package/dist/{chunk-ESLOLCD4.js.map → chunk-RUWKIPWJ.js.map} +0 -0
  216. /package/dist/{chunk-W54Z2MOP.js.map → chunk-RXSGBO7Y.js.map} +0 -0
  217. /package/dist/{chunk-BOK6BN5M.js.map → chunk-THV6PZ7V.js.map} +0 -0
  218. /package/dist/{chunk-J7BIFOYP.js.map → chunk-U6TC6Z5B.js.map} +0 -0
  219. /package/dist/{chunk-X2BVB74H.js.map → chunk-UZQXJDOE.js.map} +0 -0
  220. /package/dist/{chunk-SUTEBYGI.js.map → chunk-V7T45MI6.js.map} +0 -0
  221. /package/dist/{chunk-A4F7OIC5.js.map → chunk-WVIWDMFB.js.map} +0 -0
  222. /package/dist/{chunk-BN2EBCLB.js.map → chunk-XPCUKWLE.js.map} +0 -0
  223. /package/dist/{chunk-OJXK5C6Z.js.map → chunk-ZG4GNPRE.js.map} +0 -0
  224. /package/dist/{chunk-TYRWSE4B.js.map → chunk-ZUMHQEW4.js.map} +0 -0
  225. /package/dist/{chunk-DAOMWGO2.js.map → chunk-ZX5N5D6M.js.map} +0 -0
  226. /package/dist/{entity-canonical-id-migration-JJSTZF73.js.map → entity-canonical-id-migration-IG77BOGD.js.map} +0 -0
  227. /package/dist/{resolution-OVVIF7ZY.js.map → harmonic-graph-version.js.map} +0 -0
@@ -4,20 +4,20 @@ import {
4
4
  exportOkfBundle,
5
5
  firstHeading,
6
6
  parseIncludeStatus
7
- } from "../chunk-SUTEBYGI.js";
7
+ } from "../chunk-V7T45MI6.js";
8
8
  import {
9
9
  OKF_EXPORT_VERSION
10
10
  } from "../chunk-I7TR2OCU.js";
11
11
  import "../chunk-D24OXEPB.js";
12
12
  import "../chunk-3UXOZBHV.js";
13
- import "../chunk-FWRXW7CZ.js";
13
+ import "../chunk-4CXK3GT3.js";
14
14
  import "../chunk-QGVCFVX6.js";
15
15
  import "../chunk-UHVXE24Y.js";
16
- import "../chunk-IGOW4TJE.js";
17
- import "../chunk-RFUL2ZIQ.js";
16
+ import "../chunk-O3JG4PD3.js";
18
17
  import "../chunk-G6OFEGUR.js";
19
- import "../chunk-A4F7OIC5.js";
20
- import "../chunk-XPXHQI6N.js";
18
+ import "../chunk-RFUL2ZIQ.js";
19
+ import "../chunk-WVIWDMFB.js";
20
+ import "../chunk-FVMEZ57K.js";
21
21
  import "../chunk-DOJBV6WR.js";
22
22
  import "../chunk-PFUXPTBT.js";
23
23
  import "../chunk-CYEPCZN5.js";
@@ -49,25 +49,20 @@ import "../chunk-3WU7XOFN.js";
49
49
  import "../chunk-P4GUQSK3.js";
50
50
  import "../chunk-4LZ7SVKM.js";
51
51
  import "../chunk-YHTG64XV.js";
52
- import "../chunk-SQ3GEIEL.js";
53
- import "../chunk-IAYUTGF2.js";
52
+ import "../chunk-KUQLUYIR.js";
53
+ import "../chunk-O3GZPLVY.js";
54
54
  import "../chunk-5WITETWU.js";
55
- import "../chunk-M5GVCJSJ.js";
56
- import "../chunk-RBOJCG4T.js";
57
55
  import "../chunk-6CIAGZ6X.js";
58
- import "../chunk-7CT3AI52.js";
59
56
  import "../chunk-G7D6GZ5J.js";
60
57
  import "../chunk-7VLBZKNB.js";
61
58
  import "../chunk-Y33KJ556.js";
62
- import "../chunk-R23JWJD3.js";
59
+ import "../chunk-J426U7XK.js";
63
60
  import "../chunk-NWA6IXR3.js";
61
+ import "../chunk-AF2XMJKD.js";
64
62
  import "../chunk-OR42ZI54.js";
65
63
  import "../chunk-IUM3VSHI.js";
66
64
  import "../chunk-EZ47HOJ6.js";
67
- import "../chunk-AF2XMJKD.js";
68
65
  import "../chunk-23G5P7TA.js";
69
- import "../chunk-C64XATK7.js";
70
- import "../chunk-42JKGUFJ.js";
71
66
  import "../chunk-MMRTPH3J.js";
72
67
  import "../chunk-I55KAVET.js";
73
68
  import "../chunk-L2EXJQJP.js";
@@ -111,6 +106,11 @@ import "../chunk-P7FMDTKL.js";
111
106
  import "../chunk-X4OZMANG.js";
112
107
  import "../chunk-ZEFVK7XC.js";
113
108
  import "../chunk-R2MBW2S4.js";
109
+ import "../chunk-M5GVCJSJ.js";
110
+ import "../chunk-RBOJCG4T.js";
111
+ import "../chunk-7CT3AI52.js";
112
+ import "../chunk-C64XATK7.js";
113
+ import "../chunk-42JKGUFJ.js";
114
114
  import "../chunk-QUIRIG2P.js";
115
115
  import "../chunk-6KH5NLQS.js";
116
116
  import "../chunk-P2QRMMVQ.js";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  compareVerifiedEpisodeResults,
3
3
  searchVerifiedEpisodes
4
- } from "./chunk-ILY5SSOM.js";
4
+ } from "./chunk-53ZVHXVZ.js";
5
5
  import "./chunk-B7RWBZXW.js";
6
- import "./chunk-FWRXW7CZ.js";
6
+ import "./chunk-4CXK3GT3.js";
7
7
  import "./chunk-QGVCFVX6.js";
8
8
  import "./chunk-UHVXE24Y.js";
9
- import "./chunk-IGOW4TJE.js";
10
- import "./chunk-RFUL2ZIQ.js";
9
+ import "./chunk-O3JG4PD3.js";
11
10
  import "./chunk-G6OFEGUR.js";
12
- import "./chunk-A4F7OIC5.js";
13
- import "./chunk-XPXHQI6N.js";
11
+ import "./chunk-RFUL2ZIQ.js";
12
+ import "./chunk-WVIWDMFB.js";
13
+ import "./chunk-FVMEZ57K.js";
14
14
  import "./chunk-DOJBV6WR.js";
15
15
  import "./chunk-PFUXPTBT.js";
16
16
  import "./chunk-CYEPCZN5.js";
@@ -42,25 +42,20 @@ import "./chunk-3WU7XOFN.js";
42
42
  import "./chunk-P4GUQSK3.js";
43
43
  import "./chunk-4LZ7SVKM.js";
44
44
  import "./chunk-YHTG64XV.js";
45
- import "./chunk-SQ3GEIEL.js";
46
- import "./chunk-IAYUTGF2.js";
45
+ import "./chunk-KUQLUYIR.js";
46
+ import "./chunk-O3GZPLVY.js";
47
47
  import "./chunk-5WITETWU.js";
48
- import "./chunk-M5GVCJSJ.js";
49
- import "./chunk-RBOJCG4T.js";
50
48
  import "./chunk-6CIAGZ6X.js";
51
- import "./chunk-7CT3AI52.js";
52
49
  import "./chunk-G7D6GZ5J.js";
53
50
  import "./chunk-7VLBZKNB.js";
54
51
  import "./chunk-Y33KJ556.js";
55
- import "./chunk-R23JWJD3.js";
52
+ import "./chunk-J426U7XK.js";
56
53
  import "./chunk-NWA6IXR3.js";
54
+ import "./chunk-AF2XMJKD.js";
57
55
  import "./chunk-OR42ZI54.js";
58
56
  import "./chunk-IUM3VSHI.js";
59
57
  import "./chunk-EZ47HOJ6.js";
60
- import "./chunk-AF2XMJKD.js";
61
58
  import "./chunk-23G5P7TA.js";
62
- import "./chunk-C64XATK7.js";
63
- import "./chunk-42JKGUFJ.js";
64
59
  import "./chunk-MMRTPH3J.js";
65
60
  import "./chunk-I55KAVET.js";
66
61
  import "./chunk-L2EXJQJP.js";
@@ -104,6 +99,11 @@ import "./chunk-P7FMDTKL.js";
104
99
  import "./chunk-X4OZMANG.js";
105
100
  import "./chunk-ZEFVK7XC.js";
106
101
  import "./chunk-R2MBW2S4.js";
102
+ import "./chunk-M5GVCJSJ.js";
103
+ import "./chunk-RBOJCG4T.js";
104
+ import "./chunk-7CT3AI52.js";
105
+ import "./chunk-C64XATK7.js";
106
+ import "./chunk-42JKGUFJ.js";
107
107
  import "./chunk-QUIRIG2P.js";
108
108
  import "./chunk-6KH5NLQS.js";
109
109
  import "./chunk-P2QRMMVQ.js";
@@ -4,16 +4,16 @@ import {
4
4
  renderWhoKnows,
5
5
  renderWhoKnowsJson,
6
6
  renderWhoKnowsText
7
- } from "./chunk-J7BIFOYP.js";
8
- import "./chunk-IQ55QGY5.js";
9
- import "./chunk-FWRXW7CZ.js";
7
+ } from "./chunk-U6TC6Z5B.js";
8
+ import "./chunk-R4JYSBZE.js";
9
+ import "./chunk-4CXK3GT3.js";
10
10
  import "./chunk-QGVCFVX6.js";
11
11
  import "./chunk-UHVXE24Y.js";
12
- import "./chunk-IGOW4TJE.js";
13
- import "./chunk-RFUL2ZIQ.js";
12
+ import "./chunk-O3JG4PD3.js";
14
13
  import "./chunk-G6OFEGUR.js";
15
- import "./chunk-A4F7OIC5.js";
16
- import "./chunk-XPXHQI6N.js";
14
+ import "./chunk-RFUL2ZIQ.js";
15
+ import "./chunk-WVIWDMFB.js";
16
+ import "./chunk-FVMEZ57K.js";
17
17
  import "./chunk-DOJBV6WR.js";
18
18
  import "./chunk-PFUXPTBT.js";
19
19
  import "./chunk-CYEPCZN5.js";
@@ -45,25 +45,20 @@ import "./chunk-3WU7XOFN.js";
45
45
  import "./chunk-P4GUQSK3.js";
46
46
  import "./chunk-4LZ7SVKM.js";
47
47
  import "./chunk-YHTG64XV.js";
48
- import "./chunk-SQ3GEIEL.js";
49
- import "./chunk-IAYUTGF2.js";
48
+ import "./chunk-KUQLUYIR.js";
49
+ import "./chunk-O3GZPLVY.js";
50
50
  import "./chunk-5WITETWU.js";
51
- import "./chunk-M5GVCJSJ.js";
52
- import "./chunk-RBOJCG4T.js";
53
51
  import "./chunk-6CIAGZ6X.js";
54
- import "./chunk-7CT3AI52.js";
55
52
  import "./chunk-G7D6GZ5J.js";
56
53
  import "./chunk-7VLBZKNB.js";
57
54
  import "./chunk-Y33KJ556.js";
58
- import "./chunk-R23JWJD3.js";
55
+ import "./chunk-J426U7XK.js";
59
56
  import "./chunk-NWA6IXR3.js";
57
+ import "./chunk-AF2XMJKD.js";
60
58
  import "./chunk-OR42ZI54.js";
61
59
  import "./chunk-IUM3VSHI.js";
62
60
  import "./chunk-EZ47HOJ6.js";
63
- import "./chunk-AF2XMJKD.js";
64
61
  import "./chunk-23G5P7TA.js";
65
- import "./chunk-C64XATK7.js";
66
- import "./chunk-42JKGUFJ.js";
67
62
  import "./chunk-MMRTPH3J.js";
68
63
  import "./chunk-I55KAVET.js";
69
64
  import "./chunk-L2EXJQJP.js";
@@ -107,6 +102,11 @@ import "./chunk-P7FMDTKL.js";
107
102
  import "./chunk-X4OZMANG.js";
108
103
  import "./chunk-ZEFVK7XC.js";
109
104
  import "./chunk-R2MBW2S4.js";
105
+ import "./chunk-M5GVCJSJ.js";
106
+ import "./chunk-RBOJCG4T.js";
107
+ import "./chunk-7CT3AI52.js";
108
+ import "./chunk-C64XATK7.js";
109
+ import "./chunk-42JKGUFJ.js";
110
110
  import "./chunk-QUIRIG2P.js";
111
111
  import "./chunk-6KH5NLQS.js";
112
112
  import "./chunk-P2QRMMVQ.js";
package/dist/who-knows.js CHANGED
@@ -9,15 +9,15 @@ import {
9
9
  loadWhoKnowsEntities,
10
10
  tokenizeTopic,
11
11
  validateWhoKnowsInput
12
- } from "./chunk-IQ55QGY5.js";
13
- import "./chunk-FWRXW7CZ.js";
12
+ } from "./chunk-R4JYSBZE.js";
13
+ import "./chunk-4CXK3GT3.js";
14
14
  import "./chunk-QGVCFVX6.js";
15
15
  import "./chunk-UHVXE24Y.js";
16
- import "./chunk-IGOW4TJE.js";
17
- import "./chunk-RFUL2ZIQ.js";
16
+ import "./chunk-O3JG4PD3.js";
18
17
  import "./chunk-G6OFEGUR.js";
19
- import "./chunk-A4F7OIC5.js";
20
- import "./chunk-XPXHQI6N.js";
18
+ import "./chunk-RFUL2ZIQ.js";
19
+ import "./chunk-WVIWDMFB.js";
20
+ import "./chunk-FVMEZ57K.js";
21
21
  import "./chunk-DOJBV6WR.js";
22
22
  import "./chunk-PFUXPTBT.js";
23
23
  import "./chunk-CYEPCZN5.js";
@@ -49,25 +49,20 @@ import "./chunk-3WU7XOFN.js";
49
49
  import "./chunk-P4GUQSK3.js";
50
50
  import "./chunk-4LZ7SVKM.js";
51
51
  import "./chunk-YHTG64XV.js";
52
- import "./chunk-SQ3GEIEL.js";
53
- import "./chunk-IAYUTGF2.js";
52
+ import "./chunk-KUQLUYIR.js";
53
+ import "./chunk-O3GZPLVY.js";
54
54
  import "./chunk-5WITETWU.js";
55
- import "./chunk-M5GVCJSJ.js";
56
- import "./chunk-RBOJCG4T.js";
57
55
  import "./chunk-6CIAGZ6X.js";
58
- import "./chunk-7CT3AI52.js";
59
56
  import "./chunk-G7D6GZ5J.js";
60
57
  import "./chunk-7VLBZKNB.js";
61
58
  import "./chunk-Y33KJ556.js";
62
- import "./chunk-R23JWJD3.js";
59
+ import "./chunk-J426U7XK.js";
63
60
  import "./chunk-NWA6IXR3.js";
61
+ import "./chunk-AF2XMJKD.js";
64
62
  import "./chunk-OR42ZI54.js";
65
63
  import "./chunk-IUM3VSHI.js";
66
64
  import "./chunk-EZ47HOJ6.js";
67
- import "./chunk-AF2XMJKD.js";
68
65
  import "./chunk-23G5P7TA.js";
69
- import "./chunk-C64XATK7.js";
70
- import "./chunk-42JKGUFJ.js";
71
66
  import "./chunk-MMRTPH3J.js";
72
67
  import "./chunk-I55KAVET.js";
73
68
  import "./chunk-L2EXJQJP.js";
@@ -111,6 +106,11 @@ import "./chunk-P7FMDTKL.js";
111
106
  import "./chunk-X4OZMANG.js";
112
107
  import "./chunk-ZEFVK7XC.js";
113
108
  import "./chunk-R2MBW2S4.js";
109
+ import "./chunk-M5GVCJSJ.js";
110
+ import "./chunk-RBOJCG4T.js";
111
+ import "./chunk-7CT3AI52.js";
112
+ import "./chunk-C64XATK7.js";
113
+ import "./chunk-42JKGUFJ.js";
114
114
  import "./chunk-QUIRIG2P.js";
115
115
  import "./chunk-6KH5NLQS.js";
116
116
  import "./chunk-P2QRMMVQ.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "10.0.95",
3
+ "version": "10.0.96",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -3386,7 +3386,7 @@
3386
3386
  "core"
3387
3387
  ],
3388
3388
  "peerDependencies": {
3389
- "@remnic/coding-graph": "^10.0.95"
3389
+ "@remnic/coding-graph": "^10.0.96"
3390
3390
  },
3391
3391
  "peerDependenciesMeta": {
3392
3392
  "@remnic/coding-graph": {
@@ -1,6 +1,7 @@
1
1
  import { mkdir, unlink, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { listJsonFilesStrict, readJsonFile, withJsonStoreMutationLock, writeJsonFileAtomic } from "./json-store.js";
4
+ import { bumpHarmonicGraphVersion } from "./harmonic-graph-version.js";
4
5
  import { compareDeterministicStrings, mergeSortedUniqueStrings } from "./deterministic-order.js";
5
6
  import { isNotFoundError } from "./utils/errno.js";
6
7
  import {
@@ -107,7 +108,11 @@ export async function recordAbstractionNode(options: {
107
108
  const nodesDir = path.join(rootDir, "nodes", day);
108
109
  const filePath = path.join(nodesDir, `${validated.nodeId}.json`);
109
110
  await mkdir(nodesDir, { recursive: true });
110
- await writeFile(filePath, JSON.stringify(validated, null, 2), "utf8");
111
+ try {
112
+ await writeFile(filePath, JSON.stringify(validated, null, 2), "utf8");
113
+ } finally {
114
+ bumpHarmonicGraphVersion(rootDir);
115
+ }
111
116
  return filePath;
112
117
  }
113
118
 
@@ -269,7 +274,7 @@ export async function upsertAbstractionNodes(options: {
269
274
  filePaths.push(filePath);
270
275
  }
271
276
  return filePaths;
272
- });
277
+ }).finally(() => bumpHarmonicGraphVersion(rootDir));
273
278
  }
274
279
 
275
280
  export async function upsertAbstractionNode(options: {
@@ -1,6 +1,7 @@
1
1
  import { mkdir, unlink, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { resolveAbstractionNodeStoreDir, validateAbstractionNode, type AbstractionNode } from "./abstraction-nodes.js";
4
+ import { bumpHarmonicGraphVersion } from "./harmonic-graph-version.js";
4
5
  import { listJsonFilesStrict, readJsonFile, withJsonStoreMutationLock, writeJsonFileAtomic } from "./json-store.js";
5
6
  import { compareDeterministicStrings, mergeSortedUniqueStrings } from "./deterministic-order.js";
6
7
  import { isNotFoundError } from "./utils/errno.js";
@@ -163,7 +164,11 @@ export async function recordCueAnchor(options: {
163
164
  const anchorDir = path.join(rootDir, bounded.anchorType);
164
165
  const filePath = path.join(anchorDir, `${bounded.anchorId}.json`);
165
166
  await mkdir(anchorDir, { recursive: true });
166
- await writeFile(filePath, JSON.stringify(bounded, null, 2), "utf8");
167
+ try {
168
+ await writeFile(filePath, JSON.stringify(bounded, null, 2), "utf8");
169
+ } finally {
170
+ bumpHarmonicGraphVersion(abstractionNodeStoreDir);
171
+ }
167
172
  return filePath;
168
173
  }
169
174
 
@@ -247,7 +252,7 @@ export async function upsertCueAnchors(options: {
247
252
  filePaths.push(filePath);
248
253
  }
249
254
  return filePaths;
250
- });
255
+ }).finally(() => bumpHarmonicGraphVersion(abstractionNodeStoreDir));
251
256
  }
252
257
 
253
258
  function compareIncomingCueAnchors(left: CueAnchor, right: CueAnchor): number {
@@ -351,7 +356,7 @@ export async function pruneOrphanCueAnchors(options: {
351
356
  }
352
357
  }
353
358
  return removed;
354
- });
359
+ }).finally(() => bumpHarmonicGraphVersion(abstractionNodeStoreDir));
355
360
  }
356
361
 
357
362
  export async function getCueAnchorStoreStatus(options: {
@@ -0,0 +1,244 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+ import { recordAbstractionNode, upsertAbstractionNodes, type AbstractionNode } from "./abstraction-nodes.js";
7
+ import { pruneOrphanCueAnchors, recordCueAnchor, upsertCueAnchors, type CueAnchor } from "./cue-anchors.js";
8
+ import {
9
+ clearHarmonicGraphCache,
10
+ harmonicGraphCacheStats,
11
+ readProjectedHarmonicGraph,
12
+ searchHarmonicRetrieval,
13
+ } from "./harmonic-retrieval.js";
14
+ import { bumpMemoryCorpusVersionForDir } from "./memory-corpus-version.js";
15
+ import { StorageManager } from "./storage.js";
16
+
17
+ function node(nodeId: string, title: string, sourceMemoryIds: string[]): AbstractionNode {
18
+ return {
19
+ schemaVersion: 1,
20
+ nodeId,
21
+ recordedAt: new Date().toISOString(),
22
+ sessionKey: "agent:test",
23
+ kind: "topic",
24
+ abstractionLevel: "meso",
25
+ title,
26
+ summary: title,
27
+ sourceMemoryIds,
28
+ };
29
+ }
30
+
31
+ async function withDir(run: (memoryDir: string, storage: StorageManager) => Promise<void>) {
32
+ const memoryDir = await mkdtemp(path.join(tmpdir(), "remnic-harmonic-cache-"));
33
+ clearHarmonicGraphCache();
34
+ try {
35
+ const storage = new StorageManager(memoryDir);
36
+ await storage.ensureDirectories();
37
+ await run(memoryDir, storage);
38
+ } finally {
39
+ clearHarmonicGraphCache();
40
+ await rm(memoryDir, { recursive: true, force: true });
41
+ }
42
+ }
43
+
44
+ const read = (memoryDir: string, extra: { nowMs?: number } = {}) =>
45
+ readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: true, useCache: true, ...extra });
46
+
47
+ test("a second cached read does no file reads", async () => {
48
+ await withDir(async (memoryDir, storage) => {
49
+ const first = await storage.writeMemory("fact", "cache source one", { source: "test" });
50
+ const nodeFile = await recordAbstractionNode({ memoryDir, node: node("n1", "cache source one", [first.id]) });
51
+ const before = { ...harmonicGraphCacheStats };
52
+ const a = await read(memoryDir);
53
+ assert.equal(a.nodes.length, 1);
54
+ assert.equal(harmonicGraphCacheStats.misses, before.misses + 1);
55
+ // Remove every file the projection reads. A real read would now return nothing.
56
+ await rm(nodeFile, { force: true });
57
+ await rm((await storage.getMemoryById(first.id))!.path, { force: true });
58
+ const b = await read(memoryDir);
59
+ assert.equal(harmonicGraphCacheStats.hits, before.hits + 1);
60
+ assert.equal(b, a);
61
+ assert.equal(b.nodes.length, 1);
62
+ });
63
+ });
64
+
65
+ test("a recall after a new memory is saved sees it", async () => {
66
+ await withDir(async (memoryDir, storage) => {
67
+ const first = await storage.writeMemory("fact", "alpha harmonic", { source: "test" });
68
+ await recordAbstractionNode({ memoryDir, node: node("n1", "alpha harmonic", [first.id]) });
69
+ const hits = () => harmonicGraphCacheStats.hits;
70
+ const query = { memoryDir, query: "bravo cache", maxResults: 3, anchorsEnabled: true, useGraphCache: true };
71
+ assert.equal((await searchHarmonicRetrieval(query)).length, 0);
72
+ const h = hits();
73
+ assert.equal((await searchHarmonicRetrieval(query)).length, 0);
74
+ assert.equal(hits(), h + 1);
75
+
76
+ const second = await storage.writeMemory("fact", "bravo cache", { source: "test" });
77
+ await recordAbstractionNode({ memoryDir, node: node("n2", "bravo cache", [second.id]) });
78
+ const results = await searchHarmonicRetrieval(query);
79
+ assert.deepEqual(results.map((result) => result.node.nodeId), ["n2"]);
80
+ });
81
+ });
82
+
83
+ test("an out-of-band corpus bump invalidates, so a source that leaves the active set drops out", async () => {
84
+ await withDir(async (memoryDir, storage) => {
85
+ const memory = await storage.writeMemory("fact", "gamma status fact", { source: "test" });
86
+ await recordAbstractionNode({ memoryDir, node: node("n1", "gamma status fact", [memory.id]) });
87
+ assert.equal((await read(memoryDir)).nodes.length, 1);
88
+ const memoryPath = (await storage.getMemoryById(memory.id))!.path;
89
+ await writeFile(
90
+ memoryPath,
91
+ (await readFile(memoryPath, "utf8")).replace("category: fact", "category: fact\nstatus: quarantined"),
92
+ );
93
+ // The documented out-of-band bump: the same call review and governance writers make.
94
+ bumpMemoryCorpusVersionForDir(memoryDir);
95
+ assert.equal((await read(memoryDir)).nodes.length, 0);
96
+ });
97
+ });
98
+
99
+ test("an explicit nowMs bypasses the cache", async () => {
100
+ await withDir(async (memoryDir, storage) => {
101
+ const memory = await storage.writeMemory("fact", "delta bypass fact", { source: "test" });
102
+ const nodeFile = await recordAbstractionNode({ memoryDir, node: node("n1", "delta bypass fact", [memory.id]) });
103
+ assert.equal((await read(memoryDir)).nodes.length, 1);
104
+ await rm(nodeFile, { force: true });
105
+ assert.equal((await read(memoryDir, { nowMs: Date.now() })).nodes.length, 0);
106
+ });
107
+ });
108
+
109
+ test("cached arrays are frozen", async () => {
110
+ await withDir(async (memoryDir, storage) => {
111
+ const memory = await storage.writeMemory("fact", "epsilon frozen fact", { source: "test" });
112
+ await recordAbstractionNode({ memoryDir, node: node("n1", "epsilon frozen fact", [memory.id]) });
113
+ const graph = await read(memoryDir);
114
+ assert.throws(() => graph.nodes.push(node("x", "x", [])), TypeError);
115
+ });
116
+ });
117
+
118
+ test("cached node records are deep frozen, so a caller cannot change later reads", async () => {
119
+ await withDir(async (memoryDir, storage) => {
120
+ const memory = await storage.writeMemory("fact", "zeta deep frozen fact", { source: "test" });
121
+ await recordAbstractionNode({ memoryDir, node: node("n1", "zeta deep frozen fact", [memory.id]) });
122
+ const graph = await read(memoryDir);
123
+ const cached = graph.nodes[0]!;
124
+ assert.throws(() => { cached.title = "changed"; }, TypeError);
125
+ assert.throws(() => cached.sourceMemoryIds?.push("x"), TypeError);
126
+ assert.equal((await read(memoryDir)).nodes[0]!.title, "zeta deep frozen fact");
127
+ });
128
+ });
129
+
130
+ test("an aborted signal rejects even when the graph is cached", async () => {
131
+ await withDir(async (memoryDir, storage) => {
132
+ const memory = await storage.writeMemory("fact", "eta abort fact", { source: "test" });
133
+ await recordAbstractionNode({ memoryDir, node: node("n1", "eta abort fact", [memory.id]) });
134
+ await read(memoryDir);
135
+ const controller = new AbortController();
136
+ controller.abort();
137
+ await assert.rejects(
138
+ readProjectedHarmonicGraph({ memoryDir, anchorsEnabled: true, useCache: true, abortSignal: controller.signal }),
139
+ );
140
+ });
141
+ });
142
+
143
+ function anchor(anchorId: string, nodeRef: string, memoryId: string): CueAnchor {
144
+ return {
145
+ schemaVersion: 1,
146
+ anchorId,
147
+ anchorType: "entity",
148
+ anchorValue: anchorId,
149
+ normalizedCue: anchorId,
150
+ recordedAt: new Date().toISOString(),
151
+ sessionKey: "agent:test",
152
+ nodeRefs: [nodeRef],
153
+ sourceMemoryIdsByNodeRef: { [nodeRef]: [memoryId] },
154
+ };
155
+ }
156
+
157
+ // Each case seeds node n1 (source m1) and, for the anchor cases, anchor a1, fills the cache, then runs ONE
158
+ // store write with no memory write after it. The next read must miss and show the new count; with the
159
+ // graph version bump removed these fail because the corpus sentinels never move.
160
+ async function seedForWrite(memoryDir: string, storage: StorageManager) {
161
+ const m1 = await storage.writeMemory("fact", "seed one", { source: "test" });
162
+ const m2 = await storage.writeMemory("fact", "seed two", { source: "test" });
163
+ await recordAbstractionNode({ memoryDir, node: node("n1", "seed one", [m1.id]) });
164
+ await recordAbstractionNode({ memoryDir, node: node("n2", "seed two", [m2.id]) });
165
+ await recordCueAnchor({ memoryDir, anchor: anchor("a1", "n1", m1.id) });
166
+ return { m1, m2 };
167
+ }
168
+
169
+ async function counts(memoryDir: string) {
170
+ const graph = await read(memoryDir);
171
+ return { nodes: graph.nodes.length, anchors: graph.anchors.length };
172
+ }
173
+
174
+ const storeWrites: Array<{
175
+ name: string;
176
+ write: (memoryDir: string, ids: { m1: string; m2: string }) => Promise<void>;
177
+ expected: { nodes: number; anchors: number };
178
+ }> = [
179
+ {
180
+ name: "recordAbstractionNode",
181
+ write: async (memoryDir, ids) => { await recordAbstractionNode({ memoryDir, node: node("n3", "seed three", [ids.m1]) }); },
182
+ expected: { nodes: 3, anchors: 1 },
183
+ },
184
+ {
185
+ name: "upsertAbstractionNodes",
186
+ write: async (memoryDir, ids) => { await upsertAbstractionNodes({ memoryDir, nodes: [node("n3", "seed three", [ids.m1])] }); },
187
+ expected: { nodes: 3, anchors: 1 },
188
+ },
189
+ {
190
+ name: "recordCueAnchor",
191
+ write: async (memoryDir, ids) => { await recordCueAnchor({ memoryDir, anchor: anchor("a2", "n2", ids.m2) }); },
192
+ expected: { nodes: 2, anchors: 2 },
193
+ },
194
+ {
195
+ name: "upsertCueAnchors",
196
+ write: async (memoryDir, ids) => { await upsertCueAnchors({ memoryDir, anchors: [anchor("a2", "n2", ids.m2)] }); },
197
+ expected: { nodes: 2, anchors: 2 },
198
+ },
199
+ ];
200
+
201
+ for (const { name, write, expected } of storeWrites) {
202
+ test(`${name} alone invalidates the cached graph`, async () => {
203
+ await withDir(async (memoryDir, storage) => {
204
+ const { m1, m2 } = await seedForWrite(memoryDir, storage);
205
+ assert.deepEqual(await counts(memoryDir), { nodes: 2, anchors: 1 });
206
+ const hitsBefore = harmonicGraphCacheStats.hits;
207
+ assert.deepEqual(await counts(memoryDir), { nodes: 2, anchors: 1 });
208
+ assert.equal(harmonicGraphCacheStats.hits, hitsBefore + 1);
209
+
210
+ await write(memoryDir, { m1: m1.id, m2: m2.id });
211
+ const missesBefore = harmonicGraphCacheStats.misses;
212
+ assert.deepEqual(await counts(memoryDir), expected);
213
+ assert.equal(harmonicGraphCacheStats.misses, missesBefore + 1);
214
+ });
215
+ });
216
+ }
217
+
218
+ test("pruneOrphanCueAnchors alone invalidates the cached graph", async () => {
219
+ await withDir(async (memoryDir, storage) => {
220
+ const { m2 } = await seedForWrite(memoryDir, storage);
221
+ await recordCueAnchor({ memoryDir, anchor: anchor("a2", "n2", m2.id) });
222
+ assert.deepEqual(await counts(memoryDir), { nodes: 2, anchors: 2 });
223
+ // n2's file goes away behind the cache's back, which makes a2 an orphan. Only the prune below bumps.
224
+ await rm(path.join(memoryDir, "state", "abstraction-nodes", "nodes", new Date().toISOString().slice(0, 10), "n2.json"), { force: true });
225
+ assert.deepEqual(await counts(memoryDir), { nodes: 2, anchors: 2 });
226
+ assert.equal(await pruneOrphanCueAnchors({ memoryDir }), 1);
227
+ assert.deepEqual(await counts(memoryDir), { nodes: 1, anchors: 1 });
228
+ });
229
+ });
230
+
231
+ test("an entry older than the TTL is rebuilt", async (t) => {
232
+ await withDir(async (memoryDir, storage) => {
233
+ const memory = await storage.writeMemory("fact", "zeta ttl fact", { source: "test" });
234
+ const nodeFile = await recordAbstractionNode({ memoryDir, node: node("n1", "zeta ttl fact", [memory.id]) });
235
+ const start = Date.now();
236
+ const clock = t.mock.method(Date, "now", () => start);
237
+ assert.equal((await read(memoryDir)).nodes.length, 1);
238
+ await rm(nodeFile, { force: true });
239
+ clock.mock.mockImplementation(() => start + 29_000);
240
+ assert.equal((await read(memoryDir)).nodes.length, 1, "still cached inside the TTL");
241
+ clock.mock.mockImplementation(() => start + 31_000);
242
+ assert.equal((await read(memoryDir)).nodes.length, 0, "rebuilt past the TTL");
243
+ });
244
+ });
@@ -0,0 +1,46 @@
1
+ import { appendFileSync, mkdirSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { MEMORY_CORPUS_VERSION_SENTINEL } from "./memory-corpus-version.js";
4
+
5
+ /**
6
+ * Append-only sentinel in the abstraction node store root. The byte size is the
7
+ * version of the node and cue anchor stores, the same scheme the memory corpus
8
+ * sentinel uses, so a bump in one process is seen by every other process.
9
+ */
10
+ export const HARMONIC_GRAPH_VERSION_SENTINEL = ".harmonic-graph-version.log";
11
+
12
+ // Names must match StorageManager.versionFilePath("memory-status" | "cold-write").
13
+ const MEMORY_STATUS_VERSION_SENTINEL = ".memory-status-version.log";
14
+ const COLD_WRITE_VERSION_SENTINEL = ".cold-write-version.log";
15
+
16
+ /**
17
+ * Record that a node or cue anchor was written. Fail-open: a failed bump must not
18
+ * fail the write; the harmonic graph cache TTL bounds the stale window.
19
+ */
20
+ export function bumpHarmonicGraphVersion(abstractionNodeStoreDir: string): void {
21
+ try {
22
+ mkdirSync(abstractionNodeStoreDir, { recursive: true });
23
+ appendFileSync(path.join(abstractionNodeStoreDir, HARMONIC_GRAPH_VERSION_SENTINEL), "x");
24
+ } catch {
25
+ /* fail-open */
26
+ }
27
+ }
28
+
29
+ function sentinelSize(filePath: string): number {
30
+ try {
31
+ return statSync(filePath).size;
32
+ } catch {
33
+ return 0;
34
+ }
35
+ }
36
+
37
+ /** Four stats; changes whenever a memory, a node or a cue anchor is written. */
38
+ export function readHarmonicGraphStamp(memoryDir: string, abstractionNodeStoreDir: string): string {
39
+ const stateDir = path.join(memoryDir, "state");
40
+ return [
41
+ sentinelSize(path.join(stateDir, MEMORY_CORPUS_VERSION_SENTINEL)),
42
+ sentinelSize(path.join(stateDir, MEMORY_STATUS_VERSION_SENTINEL)),
43
+ sentinelSize(path.join(stateDir, COLD_WRITE_VERSION_SENTINEL)),
44
+ sentinelSize(path.join(abstractionNodeStoreDir, HARMONIC_GRAPH_VERSION_SENTINEL)),
45
+ ].join(":");
46
+ }