@remnic/core 9.3.755 → 9.3.757

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 (219) hide show
  1. package/dist/access-admin-ops-surface.d.ts +1 -1
  2. package/dist/access-admin-ops-surface.js +45 -45
  3. package/dist/access-audit.js +2 -2
  4. package/dist/access-boundary.d.ts +1 -1
  5. package/dist/access-boundary.js +46 -46
  6. package/dist/access-cli.js +105 -104
  7. package/dist/access-cli.js.map +1 -1
  8. package/dist/access-http.d.ts +1 -1
  9. package/dist/access-http.js +55 -55
  10. package/dist/access-lcm-surface.d.ts +1 -1
  11. package/dist/access-lcm-surface.js +45 -45
  12. package/dist/access-mcp.d.ts +1 -1
  13. package/dist/access-mcp.js +52 -52
  14. package/dist/access-observe-write-surface.d.ts +225 -0
  15. package/dist/access-observe-write-surface.js +128 -0
  16. package/dist/access-operations-batch.js +49 -49
  17. package/dist/access-operations.d.ts +5 -5
  18. package/dist/access-operations.js +51 -51
  19. package/dist/access-recall-surface.d.ts +1 -1
  20. package/dist/access-recall-surface.js +45 -45
  21. package/dist/access-schema.d.ts +70 -70
  22. package/dist/access-schema.js +4 -4
  23. package/dist/{access-service-C3jkLi2U.d.ts → access-service-CJwcDoQp.d.ts} +4 -5
  24. package/dist/access-service.d.ts +1 -1
  25. package/dist/access-service.js +45 -45
  26. package/dist/access-surface-catalog.d.ts +1 -1
  27. package/dist/active-recall.js +1 -1
  28. package/dist/adapters/index.js +4 -4
  29. package/dist/adapters/registry.js +2 -2
  30. package/dist/{auto-sync-G4YKBPOU.js → auto-sync-2SOUW4JW.js} +6 -6
  31. package/dist/briefing.js +12 -12
  32. package/dist/calibration.js +2 -2
  33. package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-RAARDY3M.js} +2 -2
  34. package/dist/causal-behavior.js +2 -2
  35. package/dist/causal-chain.js +2 -2
  36. package/dist/causal-consolidation.js +20 -20
  37. package/dist/causal-retrieval.js +2 -2
  38. package/dist/causal-trajectory-graph.js +2 -2
  39. package/dist/causal-trajectory.js +1 -1
  40. package/dist/{chunk-T6WHI3JW.js → chunk-2NTWULEQ.js} +6 -6
  41. package/dist/chunk-3I4FUAVJ.js +20 -0
  42. package/dist/chunk-3I4FUAVJ.js.map +1 -0
  43. package/dist/{chunk-TH7WMHVK.js → chunk-3NN73CM7.js} +2 -2
  44. package/dist/{chunk-GCEV6SXP.js → chunk-4L2KHKLY.js} +2300 -2220
  45. package/dist/chunk-4L2KHKLY.js.map +1 -0
  46. package/dist/{chunk-TAPSNIMA.js → chunk-5CEJH5ZN.js} +4 -4
  47. package/dist/{chunk-XBM5DG4O.js → chunk-5PHPNUZN.js} +684 -599
  48. package/dist/chunk-5PHPNUZN.js.map +1 -0
  49. package/dist/{chunk-CIDGUFAL.js → chunk-6A76LSX6.js} +2 -2
  50. package/dist/{chunk-UQJ6YTKR.js → chunk-6DMTYBNY.js} +7 -7
  51. package/dist/{chunk-S6UCPBPL.js → chunk-6FYRCQ7K.js} +2 -2
  52. package/dist/{chunk-7XH7VJN4.js → chunk-6T4LTI2F.js} +4 -4
  53. package/dist/{chunk-PJ7DIEFC.js → chunk-7HJ3XBMD.js} +5 -5
  54. package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
  55. package/dist/{chunk-I3UWEZTK.js → chunk-AINK3KDE.js} +2 -2
  56. package/dist/{chunk-ZQNCHPTZ.js → chunk-CTGK5VK3.js} +2 -2
  57. package/dist/{chunk-G7LCG56O.js → chunk-CXW6Y7SI.js} +4 -4
  58. package/dist/{chunk-M5QKGHCR.js → chunk-CYWCKFCL.js} +5 -5
  59. package/dist/{chunk-T422SYM6.js → chunk-DCWIQFNA.js} +5 -5
  60. package/dist/{chunk-2BFLDH5F.js → chunk-EEIROWEG.js} +2 -2
  61. package/dist/{chunk-DS4QM6I7.js → chunk-EEKDRAWX.js} +4 -4
  62. package/dist/{chunk-OCFITZRV.js → chunk-FBS6V6LJ.js} +4 -4
  63. package/dist/{chunk-I6IAJCJS.js → chunk-FFPH47IX.js} +7 -7
  64. package/dist/{chunk-N4AGSRVA.js → chunk-GI7PRVMV.js} +9 -9
  65. package/dist/{chunk-FQLM6ZUP.js → chunk-GIJDW2YO.js} +3 -3
  66. package/dist/{chunk-LFKEFIJA.js → chunk-I65IQD7Y.js} +4 -4
  67. package/dist/{chunk-3DOD6ICJ.js → chunk-IBAU2RZN.js} +6 -6
  68. package/dist/{chunk-24463PIV.js → chunk-IF6BTZCB.js} +2 -2
  69. package/dist/{chunk-GMWKWA6N.js → chunk-IR6ATBUI.js} +5 -5
  70. package/dist/{chunk-TJATYB3F.js → chunk-IZP62CEF.js} +7 -7
  71. package/dist/{chunk-FLVL4L3L.js → chunk-KTULX4YO.js} +4 -4
  72. package/dist/{chunk-XCXN3D2U.js → chunk-L5DU7THE.js} +6 -6
  73. package/dist/{chunk-437S3G37.js → chunk-LNQUAJNC.js} +1 -1
  74. package/dist/{chunk-LD53WPMU.js → chunk-LQ6JI4VH.js} +4 -4
  75. package/dist/{chunk-NCNOYM6F.js → chunk-LVMVYLNB.js} +2 -2
  76. package/dist/{chunk-XD6JVK5P.js → chunk-MQQO56LN.js} +47 -47
  77. package/dist/{chunk-PJQMISPD.js → chunk-NNL4LJAD.js} +6 -6
  78. package/dist/{chunk-GGYUR4M2.js → chunk-PHWBKQHK.js} +12 -12
  79. package/dist/{chunk-TQT3USCG.js → chunk-PMBWVQ7D.js} +2 -2
  80. package/dist/{chunk-JITAVROY.js → chunk-PXHY2E3S.js} +4 -4
  81. package/dist/{chunk-GPFXPC2V.js → chunk-QOGXUNWL.js} +8 -8
  82. package/dist/{chunk-SP42M3XP.js → chunk-QVQG7W2H.js} +7 -7
  83. package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
  84. package/dist/{chunk-VL5QHTJI.js → chunk-RQXVVENY.js} +128 -128
  85. package/dist/{chunk-KKOCFUI5.js → chunk-SUN6XVNI.js} +10 -10
  86. package/dist/{chunk-FOJY75BL.js → chunk-T3UW6NRQ.js} +13 -13
  87. package/dist/{chunk-2MR3MFQB.js → chunk-UPSAT4PY.js} +2 -2
  88. package/dist/{chunk-5J4OL7TJ.js → chunk-VUA4S7P6.js} +4 -4
  89. package/dist/{chunk-YQGHW5ML.js → chunk-WRFKZEO6.js} +4 -4
  90. package/dist/{chunk-H6S3DJVE.js → chunk-XKUYMM34.js} +2 -2
  91. package/dist/{chunk-EHQ5HO5T.js → chunk-XROP4ZHB.js} +4 -4
  92. package/dist/{chunk-2OZ2JEUP.js → chunk-YTU7WKYN.js} +2 -2
  93. package/dist/{chunk-V5UV7BOA.js → chunk-ZE7GJ76H.js} +1 -1
  94. package/dist/{chunk-ER3CCWK2.js → chunk-ZG2WP425.js} +2 -2
  95. package/dist/{chunk-4LQUYBZM.js → chunk-ZL3Y3ZTQ.js} +7 -7
  96. package/dist/{cli-BJQ2ahrh.d.ts → cli-cZyANcmY.d.ts} +1 -1
  97. package/dist/cli.d.ts +2 -2
  98. package/dist/cli.js +72 -72
  99. package/dist/compounding/engine.js +13 -13
  100. package/dist/config-record.d.ts +11 -0
  101. package/dist/config-record.js +8 -0
  102. package/dist/config.js +1 -1
  103. package/dist/connectors/codex-materialize-runner.js +13 -13
  104. package/dist/connectors/index.js +14 -14
  105. package/dist/consolidation-provenance-check.js +2 -2
  106. package/dist/contradiction/index.js +6 -6
  107. package/dist/{contradiction-scan-2SODRNUF.js → contradiction-scan-3FJYWS3G.js} +3 -3
  108. package/dist/contradiction-scan-3FJYWS3G.js.map +1 -0
  109. package/dist/conversation-index/backend.js +2 -2
  110. package/dist/entity-retrieval.js +13 -13
  111. package/dist/extraction-faithfulness.js +3 -3
  112. package/dist/extraction-judge.js +3 -3
  113. package/dist/extraction.js +4 -4
  114. package/dist/fallback-llm.js +2 -2
  115. package/dist/{first-start-migration-DY6YCRC2.js → first-start-migration-RYY7PPGF.js} +4 -4
  116. package/dist/{graph-edge-decay-RXUCJIN4.js → graph-edge-decay-U2GKDAVM.js} +4 -4
  117. package/dist/graph-edge-decay-U2GKDAVM.js.map +1 -0
  118. package/dist/graph-snapshot.js +3 -3
  119. package/dist/graph.js +2 -2
  120. package/dist/index.d.ts +3 -2
  121. package/dist/index.js +162 -158
  122. package/dist/lcm/engine.js +2 -2
  123. package/dist/lcm/index.js +2 -2
  124. package/dist/maintenance/memory-governance.js +13 -13
  125. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -12
  126. package/dist/maintenance/rebuild-memory-projection.js +14 -14
  127. package/dist/mcp-memory-inspector-app.d.ts +1 -1
  128. package/dist/namespaces/migrate.js +24 -24
  129. package/dist/namespaces/search.js +10 -10
  130. package/dist/namespaces/storage.js +14 -14
  131. package/dist/operator-toolkit.js +32 -32
  132. package/dist/orchestration/maintenance.js +17 -17
  133. package/dist/orchestrator.js +107 -106
  134. package/dist/recall-planner-llm.js +2 -2
  135. package/dist/resume-bundles.js +2 -2
  136. package/dist/schemas.d.ts +100 -100
  137. package/dist/search/factory.js +9 -9
  138. package/dist/search/index.js +13 -13
  139. package/dist/search/lancedb-backend.js +2 -2
  140. package/dist/search/meilisearch-backend.js +2 -2
  141. package/dist/search/orama-backend.js +2 -2
  142. package/dist/semantic-consolidation.js +16 -16
  143. package/dist/semantic-rule-promotion.js +12 -12
  144. package/dist/semantic-rule-verifier.js +13 -13
  145. package/dist/shared-context/manager.d.ts +8 -8
  146. package/dist/storage.d.ts +60 -78
  147. package/dist/storage.js +19 -11
  148. package/dist/summarizer.js +4 -4
  149. package/dist/transfer/backup.js +2 -2
  150. package/dist/transfer/capsule-export.js +3 -3
  151. package/dist/transfer/capsule-import.js +3 -3
  152. package/dist/transfer/types.d.ts +66 -66
  153. package/dist/verified-recall.js +13 -13
  154. package/package.json +2 -2
  155. package/src/access-observe-write-surface.ts +1007 -0
  156. package/src/access-service.ts +48 -794
  157. package/src/config-record.ts +25 -0
  158. package/src/index.ts +1 -0
  159. package/src/orchestration/self-deps.ts +9 -1
  160. package/src/storage/entity-store.ts +4 -2
  161. package/src/storage/memory-read-store.ts +756 -0
  162. package/src/storage.ts +67 -627
  163. package/dist/chunk-GCEV6SXP.js.map +0 -1
  164. package/dist/chunk-XBM5DG4O.js.map +0 -1
  165. /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-observe-write-surface.js.map} +0 -0
  166. /package/dist/{auto-sync-G4YKBPOU.js.map → auto-sync-2SOUW4JW.js.map} +0 -0
  167. /package/dist/{contradiction-scan-2SODRNUF.js.map → capsule-crypto-RAARDY3M.js.map} +0 -0
  168. /package/dist/{chunk-T6WHI3JW.js.map → chunk-2NTWULEQ.js.map} +0 -0
  169. /package/dist/{chunk-TH7WMHVK.js.map → chunk-3NN73CM7.js.map} +0 -0
  170. /package/dist/{chunk-TAPSNIMA.js.map → chunk-5CEJH5ZN.js.map} +0 -0
  171. /package/dist/{chunk-CIDGUFAL.js.map → chunk-6A76LSX6.js.map} +0 -0
  172. /package/dist/{chunk-UQJ6YTKR.js.map → chunk-6DMTYBNY.js.map} +0 -0
  173. /package/dist/{chunk-S6UCPBPL.js.map → chunk-6FYRCQ7K.js.map} +0 -0
  174. /package/dist/{chunk-7XH7VJN4.js.map → chunk-6T4LTI2F.js.map} +0 -0
  175. /package/dist/{chunk-PJ7DIEFC.js.map → chunk-7HJ3XBMD.js.map} +0 -0
  176. /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
  177. /package/dist/{chunk-I3UWEZTK.js.map → chunk-AINK3KDE.js.map} +0 -0
  178. /package/dist/{chunk-ZQNCHPTZ.js.map → chunk-CTGK5VK3.js.map} +0 -0
  179. /package/dist/{chunk-G7LCG56O.js.map → chunk-CXW6Y7SI.js.map} +0 -0
  180. /package/dist/{chunk-M5QKGHCR.js.map → chunk-CYWCKFCL.js.map} +0 -0
  181. /package/dist/{chunk-T422SYM6.js.map → chunk-DCWIQFNA.js.map} +0 -0
  182. /package/dist/{chunk-2BFLDH5F.js.map → chunk-EEIROWEG.js.map} +0 -0
  183. /package/dist/{chunk-DS4QM6I7.js.map → chunk-EEKDRAWX.js.map} +0 -0
  184. /package/dist/{chunk-OCFITZRV.js.map → chunk-FBS6V6LJ.js.map} +0 -0
  185. /package/dist/{chunk-I6IAJCJS.js.map → chunk-FFPH47IX.js.map} +0 -0
  186. /package/dist/{chunk-N4AGSRVA.js.map → chunk-GI7PRVMV.js.map} +0 -0
  187. /package/dist/{chunk-FQLM6ZUP.js.map → chunk-GIJDW2YO.js.map} +0 -0
  188. /package/dist/{chunk-LFKEFIJA.js.map → chunk-I65IQD7Y.js.map} +0 -0
  189. /package/dist/{chunk-3DOD6ICJ.js.map → chunk-IBAU2RZN.js.map} +0 -0
  190. /package/dist/{chunk-24463PIV.js.map → chunk-IF6BTZCB.js.map} +0 -0
  191. /package/dist/{chunk-GMWKWA6N.js.map → chunk-IR6ATBUI.js.map} +0 -0
  192. /package/dist/{chunk-TJATYB3F.js.map → chunk-IZP62CEF.js.map} +0 -0
  193. /package/dist/{chunk-FLVL4L3L.js.map → chunk-KTULX4YO.js.map} +0 -0
  194. /package/dist/{chunk-XCXN3D2U.js.map → chunk-L5DU7THE.js.map} +0 -0
  195. /package/dist/{chunk-437S3G37.js.map → chunk-LNQUAJNC.js.map} +0 -0
  196. /package/dist/{chunk-LD53WPMU.js.map → chunk-LQ6JI4VH.js.map} +0 -0
  197. /package/dist/{chunk-NCNOYM6F.js.map → chunk-LVMVYLNB.js.map} +0 -0
  198. /package/dist/{chunk-XD6JVK5P.js.map → chunk-MQQO56LN.js.map} +0 -0
  199. /package/dist/{chunk-PJQMISPD.js.map → chunk-NNL4LJAD.js.map} +0 -0
  200. /package/dist/{chunk-GGYUR4M2.js.map → chunk-PHWBKQHK.js.map} +0 -0
  201. /package/dist/{chunk-TQT3USCG.js.map → chunk-PMBWVQ7D.js.map} +0 -0
  202. /package/dist/{chunk-JITAVROY.js.map → chunk-PXHY2E3S.js.map} +0 -0
  203. /package/dist/{chunk-GPFXPC2V.js.map → chunk-QOGXUNWL.js.map} +0 -0
  204. /package/dist/{chunk-SP42M3XP.js.map → chunk-QVQG7W2H.js.map} +0 -0
  205. /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
  206. /package/dist/{chunk-VL5QHTJI.js.map → chunk-RQXVVENY.js.map} +0 -0
  207. /package/dist/{chunk-KKOCFUI5.js.map → chunk-SUN6XVNI.js.map} +0 -0
  208. /package/dist/{chunk-FOJY75BL.js.map → chunk-T3UW6NRQ.js.map} +0 -0
  209. /package/dist/{chunk-2MR3MFQB.js.map → chunk-UPSAT4PY.js.map} +0 -0
  210. /package/dist/{chunk-5J4OL7TJ.js.map → chunk-VUA4S7P6.js.map} +0 -0
  211. /package/dist/{chunk-YQGHW5ML.js.map → chunk-WRFKZEO6.js.map} +0 -0
  212. /package/dist/{chunk-H6S3DJVE.js.map → chunk-XKUYMM34.js.map} +0 -0
  213. /package/dist/{chunk-EHQ5HO5T.js.map → chunk-XROP4ZHB.js.map} +0 -0
  214. /package/dist/{chunk-2OZ2JEUP.js.map → chunk-YTU7WKYN.js.map} +0 -0
  215. /package/dist/{chunk-V5UV7BOA.js.map → chunk-ZE7GJ76H.js.map} +0 -0
  216. /package/dist/{chunk-ER3CCWK2.js.map → chunk-ZG2WP425.js.map} +0 -0
  217. /package/dist/{chunk-4LQUYBZM.js.map → chunk-ZL3Y3ZTQ.js.map} +0 -0
  218. /package/dist/{graph-edge-decay-RXUCJIN4.js.map → config-record.js.map} +0 -0
  219. /package/dist/{first-start-migration-DY6YCRC2.js.map → first-start-migration-RYY7PPGF.js.map} +0 -0
@@ -16,28 +16,18 @@ import {
16
16
  import {
17
17
  supersessionKeysForFact
18
18
  } from "./chunk-TKZ3AL5V.js";
19
- import {
20
- stripAttributesSuffix
21
- } from "./chunk-SVOZFLIQ.js";
22
19
  import {
23
20
  DEFAULT_CITATION_FORMAT,
24
21
  hasCitation,
25
22
  stripCitationForTemplate
26
23
  } from "./chunk-J6A3CX5N.js";
24
+ import {
25
+ stripAttributesSuffix
26
+ } from "./chunk-SVOZFLIQ.js";
27
27
  import {
28
28
  SPECULATIVE_TTL_DAYS,
29
29
  confidenceTier
30
30
  } from "./chunk-JS2HKKOK.js";
31
- import {
32
- normalizeProjectionPreview,
33
- normalizeProjectionTags
34
- } from "./chunk-SCU65EZI.js";
35
- import {
36
- readProjectedGovernanceRecord,
37
- readProjectedMemoryBrowse,
38
- readProjectedMemoryState,
39
- readProjectedMemoryTimeline
40
- } from "./chunk-MB5RSUW6.js";
41
31
  import {
42
32
  getCachedEntities,
43
33
  invalidateAllForDir,
@@ -49,6 +39,16 @@ import {
49
39
  sortMemoryLifecycleEvents,
50
40
  toMemoryPathRel
51
41
  } from "./chunk-RULE4VG5.js";
42
+ import {
43
+ normalizeProjectionPreview,
44
+ normalizeProjectionTags
45
+ } from "./chunk-SCU65EZI.js";
46
+ import {
47
+ readProjectedGovernanceRecord,
48
+ readProjectedMemoryBrowse,
49
+ readProjectedMemoryState,
50
+ readProjectedMemoryTimeline
51
+ } from "./chunk-MB5RSUW6.js";
52
52
  import {
53
53
  rotateMarkdownFileToArchive
54
54
  } from "./chunk-DM2T26WE.js";
@@ -64,32 +64,32 @@ import {
64
64
  import {
65
65
  parseFaithfulnessField,
66
66
  serializeFaithfulnessFields
67
- } from "./chunk-2OZ2JEUP.js";
67
+ } from "./chunk-YTU7WKYN.js";
68
68
  import {
69
- isConsolidationOperator,
70
- isValidDerivedFromEntry
71
- } from "./chunk-G7D6GZ5J.js";
69
+ computeContentHash,
70
+ normalizeContent
71
+ } from "./chunk-W5H6ZRPT.js";
72
72
  import {
73
73
  createVersion
74
74
  } from "./chunk-VF4XKTX3.js";
75
75
  import {
76
- computeContentHash,
77
- normalizeContent
78
- } from "./chunk-W5H6ZRPT.js";
76
+ isConsolidationOperator,
77
+ isValidDerivedFromEntry
78
+ } from "./chunk-G7D6GZ5J.js";
79
79
  import {
80
80
  parseProvenanceSources,
81
81
  parseProvenanceTag,
82
82
  reconcileProvenanceRead,
83
83
  serializeProvenanceFields
84
84
  } from "./chunk-OEHCMALU.js";
85
+ import {
86
+ sanitizeMemoryContent
87
+ } from "./chunk-5GKPFL3S.js";
85
88
  import {
86
89
  matchEntitySchemaSection,
87
90
  normalizeEntityStructuredSection,
88
91
  sortStructuredSectionsBySchema
89
92
  } from "./chunk-4DJQYKMN.js";
90
- import {
91
- sanitizeMemoryContent
92
- } from "./chunk-5GKPFL3S.js";
93
93
  import {
94
94
  log
95
95
  } from "./chunk-NU3CSQ4H.js";
@@ -111,15 +111,480 @@ import {
111
111
  getCategoryDir
112
112
  } from "./chunk-VS2IYZRU.js";
113
113
 
114
- // src/storage.ts
115
- import { access, lstat, readdir as readdir2, readFile, realpath, stat, writeFile, mkdir, unlink as unlink2, appendFile, open } from "fs/promises";
116
- import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync } from "fs";
117
- import { createHash } from "crypto";
118
- import path2 from "path";
114
+ // src/storage.ts
115
+ import { access, readdir as readdir3, readFile, stat as stat2, writeFile, mkdir, unlink as unlink2, appendFile, open } from "fs/promises";
116
+ import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync } from "fs";
117
+ import { createHash } from "crypto";
118
+ import path3 from "path";
119
+
120
+ // src/storage/memory-read-store.ts
121
+ import { lstat, readdir, realpath, stat } from "fs/promises";
122
+ import path from "path";
123
+ var MemoryReadStore = class {
124
+ constructor(deps) {
125
+ this.deps = deps;
126
+ }
127
+ deps;
128
+ /**
129
+ * List stored transcript days, newest first, optionally scoped to one
130
+ * source. Non-transcript files in the tree are ignored.
131
+ */
132
+ async listWearableTranscriptDays(sourceId) {
133
+ const days = [];
134
+ let sources;
135
+ if (sourceId !== void 0) {
136
+ sources = [sourceId];
137
+ } else {
138
+ try {
139
+ const entries = await readdir(this.deps.wearablesDir, { withFileTypes: true });
140
+ sources = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
141
+ } catch (err) {
142
+ if (err.code === "ENOENT") return [];
143
+ throw err;
144
+ }
145
+ }
146
+ for (const source of sources) {
147
+ if (!/^[a-z][a-z0-9-]{0,63}$/.test(source)) continue;
148
+ let entries;
149
+ try {
150
+ entries = await readdir(path.join(this.deps.wearablesDir, source));
151
+ } catch (err) {
152
+ if (err.code === "ENOENT") continue;
153
+ throw err;
154
+ }
155
+ for (const entry of entries) {
156
+ if (!entry.endsWith(".md")) continue;
157
+ const date = entry.slice(0, -3);
158
+ if (!isValidTranscriptDate(date)) continue;
159
+ days.push({ source, date });
160
+ }
161
+ }
162
+ days.sort((a, b) => {
163
+ if (a.date > b.date) return -1;
164
+ if (a.date < b.date) return 1;
165
+ if (a.source < b.source) return -1;
166
+ if (a.source > b.source) return 1;
167
+ return 0;
168
+ });
169
+ return days;
170
+ }
171
+ async readAllArtifactsCached() {
172
+ if (this.deps.artifactIndexCache && Date.now() - this.deps.artifactIndexCache.loadedAtMs <= this.deps.storageManagerClass.ARTIFACT_INDEX_CACHE_TTL_MS && this.deps.artifactIndexCache.writeVersion === this.deps.getArtifactWriteVersion()) {
173
+ return this.deps.artifactIndexCache.memories;
174
+ }
175
+ const scanArtifacts = async () => {
176
+ const artifacts = [];
177
+ const readDir = async (dir) => {
178
+ try {
179
+ const entries = await readdir(dir, { withFileTypes: true });
180
+ for (const entry of entries) {
181
+ const fullPath = path.join(dir, entry.name);
182
+ if (entry.isDirectory()) {
183
+ await readDir(fullPath);
184
+ continue;
185
+ }
186
+ if (!entry.name.endsWith(".md")) continue;
187
+ const memory = await this.deps.readMemoryByPath(fullPath);
188
+ if (!memory) continue;
189
+ artifacts.push(memory);
190
+ }
191
+ } catch {
192
+ }
193
+ };
194
+ await readDir(this.deps.artifactsDir);
195
+ return artifacts;
196
+ };
197
+ const MAX_REBUILD_RETRIES = 2;
198
+ let latestArtifacts = [];
199
+ for (let attempt = 0; attempt <= MAX_REBUILD_RETRIES; attempt += 1) {
200
+ const versionBefore = this.deps.getArtifactWriteVersion();
201
+ const artifacts = await scanArtifacts();
202
+ const versionAfter = this.deps.getArtifactWriteVersion();
203
+ latestArtifacts = artifacts;
204
+ if (versionAfter === versionBefore) {
205
+ this.deps.artifactIndexCache = { memories: artifacts, loadedAtMs: Date.now(), writeVersion: versionAfter };
206
+ return artifacts;
207
+ }
208
+ }
209
+ this.deps.artifactIndexCache = null;
210
+ return latestArtifacts;
211
+ }
212
+ async collectActiveMemoryPaths() {
213
+ const filePaths = [];
214
+ let memoryRootReal;
215
+ try {
216
+ memoryRootReal = await realpath(this.deps.baseDir);
217
+ } catch {
218
+ return filePaths;
219
+ }
220
+ const collectPaths = async (dir) => {
221
+ let entries;
222
+ try {
223
+ const dirStat = await lstat(dir);
224
+ if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) return;
225
+ assertPathInsideRoot(memoryRootReal, await realpath(dir), dir);
226
+ entries = await readdir(dir, { withFileTypes: true });
227
+ } catch {
228
+ return;
229
+ }
230
+ const subdirs = [];
231
+ for (const entry of entries) {
232
+ if (entry.isSymbolicLink()) continue;
233
+ const fullPath = path.join(dir, entry.name);
234
+ if (entry.isDirectory()) {
235
+ subdirs.push(fullPath);
236
+ } else if (entry.name.endsWith(".md")) {
237
+ try {
238
+ assertPathInsideRoot(memoryRootReal, await realpath(fullPath), fullPath);
239
+ filePaths.push(fullPath);
240
+ } catch {
241
+ }
242
+ }
243
+ }
244
+ for (const subdir of subdirs) {
245
+ await collectPaths(subdir);
246
+ }
247
+ };
248
+ for (const dir of RECALL_FALLBACK_DIRS) {
249
+ await collectPaths(path.join(this.deps.baseDir, dir));
250
+ }
251
+ return filePaths;
252
+ }
253
+ async readMemoriesWindow(options = {}) {
254
+ const allPaths = await this.deps.collectActiveMemoryPaths();
255
+ const sortedPaths = this.deps.orderWindowPaths(allPaths);
256
+ const maxMemories = typeof options.maxMemories === "number" && Number.isFinite(options.maxMemories) ? Math.max(1, Math.floor(options.maxMemories)) : void 0;
257
+ const maxCandidatePaths = maxMemories === void 0 ? void 0 : maxMemories * 2;
258
+ const updatedAfterMs = options.updatedAfter?.getTime();
259
+ const normalizedBatchSize = this.deps.normalizeMemoryReadBatchSize(options.batchSize);
260
+ const memories = [];
261
+ const selectedPaths = [];
262
+ for (let i = 0; i < sortedPaths.length; i += normalizedBatchSize) {
263
+ if (maxMemories !== void 0 && (memories.length >= maxMemories || maxCandidatePaths !== void 0 && selectedPaths.length >= maxCandidatePaths)) {
264
+ return { memories, filePaths: selectedPaths };
265
+ }
266
+ const batchPaths = sortedPaths.slice(i, i + normalizedBatchSize);
267
+ const candidateBatchPaths = updatedAfterMs === void 0 ? batchPaths : await this.deps.filterWindowPathsByUpdatedAfter(batchPaths, updatedAfterMs);
268
+ const remainingSlots = maxMemories === void 0 ? void 0 : Math.max(0, maxMemories - memories.length);
269
+ const remainingInspectionBudget = maxCandidatePaths === void 0 ? void 0 : Math.max(0, maxCandidatePaths - selectedPaths.length);
270
+ const { memories: batchMemories, filePaths: parsedCandidatePaths } = remainingSlots === void 0 ? {
271
+ memories: await this.deps.readParsedMemoriesFromPaths(candidateBatchPaths, normalizedBatchSize),
272
+ filePaths: candidateBatchPaths
273
+ } : await this.deps.readWindowBoundedBatch(
274
+ candidateBatchPaths,
275
+ remainingSlots,
276
+ remainingInspectionBudget ?? remainingSlots,
277
+ normalizedBatchSize
278
+ );
279
+ selectedPaths.push(...parsedCandidatePaths);
280
+ for (const memory of batchMemories) {
281
+ memories.push(memory);
282
+ if (maxMemories !== void 0 && memories.length >= maxMemories) {
283
+ return { memories, filePaths: selectedPaths };
284
+ }
285
+ }
286
+ }
287
+ return { memories, filePaths: selectedPaths };
288
+ }
289
+ /**
290
+ * Read all memories from the cold tier by scanning the entire cold/ root
291
+ * tree. Previously this only scanned cold/facts/ and cold/corrections/, but
292
+ * structuredAttributes can appear on any MemoryCategory (preference, decision,
293
+ * entity, etc.). buildTierMemoryPath now routes each category to its own
294
+ * cold/<dir>/ subtree via the shared categoryDirName() chokepoint (issue
295
+ * #1546), so cold decisions/preferences/... live outside cold/facts/.
296
+ * Scanning the full coldRoot covers every category dir and guards against
297
+ * files placed in unexpected subdirectories during manual operations or future
298
+ * refactors.
299
+ *
300
+ * Broadened in PR #402 round-6 (Finding UTsP): scanning only facts/ and
301
+ * corrections/ was a narrower-than-necessary subset of the cold directory
302
+ * tree. Correctness trumps the minor performance difference — cold scans
303
+ * already happen at most once per supersession write.
304
+ *
305
+ * Used by applyTemporalSupersession so that memories already demoted to
306
+ * cold/ can still be marked superseded when a newer hot fact arrives.
307
+ *
308
+ * Cached with a TTL (Finding UOGi, PR #402 round-6): back-to-back
309
+ * structured-attribute writes in the same burst reuse the cached result
310
+ * instead of re-scanning the cold tree on every call. The cache is
311
+ * invalidated whenever a write calls invalidateAllMemoriesCache() (which
312
+ * covers any hot→cold demotion that changes cold-tier contents) and
313
+ * expires after COLD_SCAN_CACHE_TTL_MS as a safety net.
314
+ */
315
+ async readAllColdMemories() {
316
+ const coldRoot = this.deps.resolveTierRootDir("cold");
317
+ const currentColdVersion = this.deps.readColdWriteVersion();
318
+ const cached = this.deps.storageManagerClass.coldMemoriesCache.get(coldRoot);
319
+ if (cached && Date.now() - cached.loadedAt < this.deps.storageManagerClass.COLD_SCAN_CACHE_TTL_MS && cached.coldVersion === currentColdVersion) {
320
+ return cached.memories;
321
+ }
322
+ const filePaths = [];
323
+ const collectPaths = async (dir) => {
324
+ try {
325
+ const entries = await readdir(dir, { withFileTypes: true });
326
+ const subdirs = [];
327
+ for (const entry of entries) {
328
+ const fullPath = path.join(dir, entry.name);
329
+ if (entry.isDirectory()) {
330
+ subdirs.push(fullPath);
331
+ } else if (entry.name.endsWith(".md")) {
332
+ filePaths.push(fullPath);
333
+ }
334
+ }
335
+ for (const subdir of subdirs) {
336
+ await collectPaths(subdir);
337
+ }
338
+ } catch {
339
+ }
340
+ };
341
+ await collectPaths(coldRoot);
342
+ const memories = await this.deps.readParsedMemoriesFromPaths(filePaths, 50);
343
+ this.deps.storageManagerClass.coldMemoriesCache.set(coldRoot, { memories, loadedAt: Date.now(), coldVersion: currentColdVersion });
344
+ return memories;
345
+ }
346
+ /** Read a single memory file by its absolute path. Returns null if unreadable. */
347
+ async readMemoryByPath(filePath) {
348
+ try {
349
+ const raw = await readMaybeEncryptedFile(filePath, this.deps._secureStoreKey, this.deps.baseDir);
350
+ const parsed = parseFrontmatter(raw);
351
+ if (parsed) {
352
+ return {
353
+ path: filePath,
354
+ frontmatter: normalizeFrontmatterForPath(
355
+ parsed.frontmatter,
356
+ toMemoryPathRel(this.deps.baseDir, filePath),
357
+ parsed.content
358
+ ),
359
+ content: parsed.content
360
+ };
361
+ }
362
+ const normalizedPath = filePath.split(path.sep).join("/");
363
+ if (normalizedPath.includes("/entities/") && filePath.endsWith(".md")) {
364
+ const entity = parseEntityFile(raw, this.deps.entitySchemas);
365
+ if (!entity.name) return null;
366
+ const nameWithoutExt = path.basename(filePath, ".md");
367
+ const fileMtime = entity.updated || await stat(filePath).then((s) => s.mtime.toISOString()).catch(() => (/* @__PURE__ */ new Date(0)).toISOString());
368
+ return {
369
+ path: filePath,
370
+ frontmatter: {
371
+ id: nameWithoutExt,
372
+ category: "entity",
373
+ created: fileMtime,
374
+ updated: fileMtime,
375
+ source: "entity_extraction",
376
+ confidence: 0.9,
377
+ confidenceTier: confidenceTier(0.9),
378
+ tags: entity.type ? [entity.type] : []
379
+ },
380
+ content: raw
381
+ };
382
+ }
383
+ return null;
384
+ } catch (err) {
385
+ if (err instanceof SecureStoreLockedError) throw err;
386
+ return null;
387
+ }
388
+ }
389
+ /**
390
+ * Append a batch of `BUFFER_SURPRISE` telemetry events (issue #563 PR 3).
391
+ *
392
+ * Each event records a single buffer flush decision driven by the
393
+ * surprise gate. The ledger is consumed by
394
+ * `reportBufferSurpriseDistribution` (Doctor report) and by downstream
395
+ * benchmark analysis. This method is fire-and-forget by contract:
396
+ * callers log but do not fail the hot path if the append throws.
397
+ */
398
+ async appendBufferSurpriseEvents(events) {
399
+ if (events.length === 0) return 0;
400
+ await this.deps.ensureDirectories();
401
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
402
+ const payload = events.map((event) => {
403
+ const normalized = {
404
+ ...event,
405
+ event: "BUFFER_SURPRISE",
406
+ timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : nowIso
407
+ };
408
+ return `${JSON.stringify(normalized)}
409
+ `;
410
+ }).join("");
411
+ await this.deps.appendStorageSecureFile(this.deps.bufferSurpriseLedgerPath, payload);
412
+ return events.length;
413
+ }
414
+ /**
415
+ * Read the buffer-surprise ledger, most recent rows last.
416
+ *
417
+ * `limit` bounds the number of **valid rows** returned (not the
418
+ * number of raw lines parsed). We parse every row, discard malformed
419
+ * ones, then take the tail — so a partial/truncated trailing line
420
+ * (the common failure mode after an interrupted append) cannot hide
421
+ * otherwise-valid recent data above it.
422
+ *
423
+ * Non-positive / non-integer / non-finite limits return `[]` rather
424
+ * than the entire file, matching the other ledger readers in this
425
+ * class and protecting against `slice(-0.5)` → `slice(-0)` silently
426
+ * devolving into an unbounded parse.
427
+ *
428
+ * # Performance note
429
+ *
430
+ * For very large ledgers (issue #563 follow-up), a tail-first reader
431
+ * would avoid parsing the full file when only a recent window is
432
+ * needed. We keep the full-scan implementation here because:
433
+ *
434
+ * - the ledger is opt-in (flag off by default), so early deployments
435
+ * accumulate rows slowly;
436
+ * - telemetry rows are small (~200 bytes), so even 100k rows parse
437
+ * in well under a second;
438
+ * - the governance archive/cleanup flow can trim the ledger when
439
+ * size becomes a concern, reusing the existing maintenance hooks.
440
+ *
441
+ * Swap to a chunked tail-reader if production logs show this is a
442
+ * hot path — leaving that work for a follow-up keeps this PR scoped
443
+ * to correctness, not optimization.
444
+ */
445
+ async readBufferSurpriseEvents(options = {}) {
446
+ let raw;
447
+ try {
448
+ raw = await this.deps.readStorageSecureFile(this.deps.bufferSurpriseLedgerPath);
449
+ } catch (err) {
450
+ if (err instanceof SecureStoreLockedError) throw err;
451
+ const code = err.code;
452
+ if (code === "ENOENT") return [];
453
+ throw err;
454
+ }
455
+ let effectiveLimit = null;
456
+ if (options.limit !== void 0) {
457
+ if (typeof options.limit !== "number" || !Number.isFinite(options.limit) || options.limit <= 0) {
458
+ return [];
459
+ }
460
+ const floored = Math.floor(options.limit);
461
+ if (floored <= 0) return [];
462
+ effectiveLimit = floored;
463
+ }
464
+ const lines = raw.split("\n");
465
+ const events = [];
466
+ for (const line of lines) {
467
+ const trimmed = line.trim();
468
+ if (trimmed.length === 0) continue;
469
+ try {
470
+ const parsed = JSON.parse(trimmed);
471
+ if (isValidBufferSurpriseEvent(parsed)) {
472
+ events.push(parsed);
473
+ }
474
+ } catch {
475
+ }
476
+ }
477
+ events.sort(
478
+ (left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp)
479
+ );
480
+ if (effectiveLimit === null) return events;
481
+ return events.slice(-effectiveLimit);
482
+ }
483
+ async readCompressionGuidelineStateFile(filePath) {
484
+ const isFiniteNonNegativeInteger = (value) => typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0;
485
+ const isValidActionSummary = (value) => {
486
+ if (!value || typeof value !== "object") return false;
487
+ const summary = value;
488
+ return typeof summary.action === "string" && isFiniteNonNegativeInteger(summary.total) && summary.outcomes !== null && typeof summary.outcomes === "object" && isFiniteNonNegativeInteger(summary.outcomes.applied) && isFiniteNonNegativeInteger(summary.outcomes.skipped) && isFiniteNonNegativeInteger(summary.outcomes.failed) && summary.quality !== null && typeof summary.quality === "object" && isFiniteNonNegativeInteger(summary.quality.good) && isFiniteNonNegativeInteger(summary.quality.poor) && isFiniteNonNegativeInteger(summary.quality.unknown);
489
+ };
490
+ const isValidRuleUpdate = (value) => {
491
+ if (!value || typeof value !== "object") return false;
492
+ const rule = value;
493
+ return typeof rule.action === "string" && typeof rule.delta === "number" && Number.isFinite(rule.delta) && (rule.direction === "increase" || rule.direction === "decrease" || rule.direction === "hold") && (rule.confidence === "low" || rule.confidence === "medium" || rule.confidence === "high") && Array.isArray(rule.notes) && rule.notes.every((note) => typeof note === "string");
494
+ };
495
+ try {
496
+ const raw = await this.deps.readStorageSecureFile(filePath);
497
+ const parsed = JSON.parse(raw);
498
+ const sourceWindow = parsed?.sourceWindow;
499
+ const eventCounts = parsed?.eventCounts;
500
+ const activationState = parsed?.activationState === "draft" || parsed?.activationState === "active" ? parsed.activationState : void 0;
501
+ const contentHash = typeof parsed?.contentHash === "string" && parsed.contentHash.length > 0 ? parsed.contentHash : void 0;
502
+ const actionSummaries = Array.isArray(parsed?.actionSummaries) ? parsed.actionSummaries.filter(isValidActionSummary) : void 0;
503
+ const ruleUpdates = Array.isArray(parsed?.ruleUpdates) ? parsed.ruleUpdates.filter(isValidRuleUpdate) : void 0;
504
+ if (!isFiniteNonNegativeInteger(parsed?.version) || typeof parsed?.updatedAt !== "string" || parsed.updatedAt.length === 0 || !sourceWindow || typeof sourceWindow.from !== "string" || sourceWindow.from.length === 0 || typeof sourceWindow.to !== "string" || sourceWindow.to.length === 0 || !eventCounts || !isFiniteNonNegativeInteger(eventCounts.total) || !isFiniteNonNegativeInteger(eventCounts.applied) || !isFiniteNonNegativeInteger(eventCounts.skipped) || !isFiniteNonNegativeInteger(eventCounts.failed) || !isFiniteNonNegativeInteger(parsed?.guidelineVersion)) {
505
+ return null;
506
+ }
507
+ return {
508
+ version: parsed.version,
509
+ updatedAt: parsed.updatedAt,
510
+ sourceWindow: {
511
+ from: sourceWindow.from,
512
+ to: sourceWindow.to
513
+ },
514
+ eventCounts: {
515
+ total: eventCounts.total,
516
+ applied: eventCounts.applied,
517
+ skipped: eventCounts.skipped,
518
+ failed: eventCounts.failed
519
+ },
520
+ guidelineVersion: parsed.guidelineVersion,
521
+ ...contentHash ? { contentHash } : {},
522
+ ...activationState ? { activationState } : {},
523
+ ...actionSummaries ? { actionSummaries } : {},
524
+ ...ruleUpdates ? { ruleUpdates } : {}
525
+ };
526
+ } catch (err) {
527
+ if (err instanceof SecureStoreLockedError) throw err;
528
+ if (!isErrnoCode(err, "ENOENT")) throw err;
529
+ return null;
530
+ }
531
+ }
532
+ async readQuestions(opts) {
533
+ const cacheKey = this.deps.questionsDir;
534
+ const cached = this.deps.storageManagerClass.questionsCache.get(cacheKey);
535
+ if (cached && Date.now() - cached.loadedAt < this.deps.storageManagerClass.QUESTIONS_CACHE_TTL_MS) {
536
+ try {
537
+ const dirStat = await stat(this.deps.questionsDir);
538
+ if (dirStat.mtimeMs <= cached.loadedAt) {
539
+ const all = cached.questions;
540
+ return opts?.unresolvedOnly ? all.filter((q) => !q.resolved) : all;
541
+ }
542
+ } catch {
543
+ }
544
+ }
545
+ try {
546
+ const files = await readdir(this.deps.questionsDir);
547
+ const questions = [];
548
+ for (const file of files) {
549
+ if (!file.endsWith(".md")) continue;
550
+ const filePath = path.join(this.deps.questionsDir, file);
551
+ const raw = await readMaybeEncryptedFile(filePath, this.deps._secureStoreKey, this.deps.baseDir);
552
+ const parsed = this.deps.parseQuestionFile(raw, filePath);
553
+ if (parsed) {
554
+ questions.push(parsed);
555
+ }
556
+ }
557
+ const sorted = questions.sort((a, b) => b.priority - a.priority);
558
+ this.deps.storageManagerClass.questionsCache.set(cacheKey, { questions: sorted, loadedAt: Date.now() });
559
+ return opts?.unresolvedOnly ? sorted.filter((q) => !q.resolved) : sorted;
560
+ } catch {
561
+ return [];
562
+ }
563
+ }
564
+ };
565
+
566
+ // src/orchestration/self-deps.ts
567
+ function selfDeps(self) {
568
+ const source = self;
569
+ return new Proxy(/* @__PURE__ */ Object.create(null), {
570
+ get(_target, prop) {
571
+ const value = source[prop];
572
+ return typeof value === "function" && !Object.prototype.hasOwnProperty.call(value, "prototype") ? value.bind(self) : value;
573
+ },
574
+ set(_target, prop, value) {
575
+ if (!(prop in source)) return false;
576
+ source[prop] = value;
577
+ return true;
578
+ },
579
+ has(_target, prop) {
580
+ return prop in source;
581
+ }
582
+ });
583
+ }
119
584
 
120
585
  // src/storage/entity-store.ts
121
- import { readdir, unlink } from "fs/promises";
122
- import path from "path";
586
+ import { readdir as readdir2, unlink } from "fs/promises";
587
+ import path2 from "path";
123
588
  var EntityStore = class {
124
589
  constructor(deps) {
125
590
  this.deps = deps;
@@ -147,7 +612,7 @@ var EntityStore = class {
147
612
  log.debug(`fuzzy match: "${normalized}" \u2192 existing "${match}"`);
148
613
  normalized = match;
149
614
  }
150
- const filePath = path.join(this.deps.entitiesDir, `${normalized}.md`);
615
+ const filePath = path2.join(this.deps.entitiesDir, `${normalized}.md`);
151
616
  let entity = {
152
617
  name,
153
618
  type,
@@ -248,7 +713,7 @@ var EntityStore = class {
248
713
  /** Return sorted list of entity filenames (without .md extension) */
249
714
  async listEntityNames() {
250
715
  try {
251
- const entries = await readdir(this.deps.entitiesDir);
716
+ const entries = await readdir2(this.deps.entitiesDir);
252
717
  return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", "")).sort();
253
718
  } catch {
254
719
  return [];
@@ -258,7 +723,7 @@ var EntityStore = class {
258
723
  * Set or rewrite the synthesis layer of an entity file.
259
724
  */
260
725
  async updateEntitySynthesis(name, synthesis, options = {}) {
261
- const filePath = path.join(this.deps.entitiesDir, `${name}.md`);
726
+ const filePath = path2.join(this.deps.entitiesDir, `${name}.md`);
262
727
  let entity;
263
728
  try {
264
729
  const content = await this.deps.readStorageSecureFile(filePath);
@@ -300,7 +765,7 @@ var EntityStore = class {
300
765
  const cached = getCachedEntities(this.deps.baseDir, currentVersion, cacheKey);
301
766
  if (cached) return cached;
302
767
  try {
303
- const entries = await readdir(this.deps.entitiesDir);
768
+ const entries = await readdir2(this.deps.entitiesDir);
304
769
  const mdFiles = entries.filter((e) => e.endsWith(".md"));
305
770
  if (mdFiles.length === 0) return [];
306
771
  const BATCH_SIZE = 100;
@@ -310,7 +775,7 @@ var EntityStore = class {
310
775
  const results = await Promise.all(
311
776
  batch.map(async (entry) => {
312
777
  try {
313
- return await this.deps.readStorageSecureFile(path.join(this.deps.entitiesDir, entry));
778
+ return await this.deps.readStorageSecureFile(path2.join(this.deps.entitiesDir, entry));
314
779
  } catch (err) {
315
780
  if (err instanceof SecureStoreLockedError) throw err;
316
781
  if (!isErrnoCode(err, "ENOENT")) throw err;
@@ -335,7 +800,7 @@ var EntityStore = class {
335
800
  */
336
801
  async buildKnowledgeIndex(config, overrides) {
337
802
  const useDefaultLimits = overrides?.maxEntities === void 0 && overrides?.maxChars === void 0;
338
- if (useDefaultLimits && this.deps.knowledgeIndexCache && Date.now() - this.deps.knowledgeIndexCache.builtAt < StorageManager.KNOWLEDGE_INDEX_CACHE_TTL_MS) {
803
+ if (useDefaultLimits && this.deps.knowledgeIndexCache && Date.now() - this.deps.knowledgeIndexCache.builtAt < this.deps.storageManagerClass.KNOWLEDGE_INDEX_CACHE_TTL_MS) {
339
804
  return { result: this.deps.knowledgeIndexCache.result, cached: true };
340
805
  }
341
806
  const entities = await this.deps.readAllEntityFiles();
@@ -347,7 +812,7 @@ var EntityStore = class {
347
812
  const scored = entities.map((e) => ({
348
813
  name: e.name,
349
814
  type: e.type,
350
- score: StorageManager.scoreEntity(e, now),
815
+ score: this.deps.storageManagerClass.scoreEntity(e, now),
351
816
  factCount: e.facts.length,
352
817
  summary: e.synthesis ?? e.summary,
353
818
  topRelationships: e.relationships.slice(0, 3).map((r) => r.target)
@@ -385,7 +850,7 @@ ${rows.join("\n")}
385
850
  async mergeFragmentedEntities() {
386
851
  let merged = 0;
387
852
  try {
388
- const entries = await readdir(this.deps.entitiesDir);
853
+ const entries = await readdir2(this.deps.entitiesDir);
389
854
  const mdFiles = entries.filter((e) => e.endsWith(".md"));
390
855
  const groups = /* @__PURE__ */ new Map();
391
856
  for (const file of mdFiles) {
@@ -423,7 +888,7 @@ ${rows.join("\n")}
423
888
  extraSections: []
424
889
  };
425
890
  for (const file of files) {
426
- const filePath = path.join(this.deps.entitiesDir, file);
891
+ const filePath = path2.join(this.deps.entitiesDir, file);
427
892
  try {
428
893
  const content = await this.deps.readStorageSecureFile(filePath);
429
894
  const parsed = parseEntityFile(content, this.deps.entitySchemas);
@@ -554,10 +1019,10 @@ ${rows.join("\n")}
554
1019
  }
555
1020
  mergedEntity.created = mergedEntity.created || mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
556
1021
  mergedEntity.updated = mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
557
- const canonicalPath = path.join(this.deps.entitiesDir, `${canonical}.md`);
1022
+ const canonicalPath = path2.join(this.deps.entitiesDir, `${canonical}.md`);
558
1023
  await this.deps.writeStorageSecureFile(canonicalPath, serializeEntityFile(mergedEntity, this.deps.entitySchemas));
559
1024
  for (const file of files) {
560
- const filePath = path.join(this.deps.entitiesDir, file);
1025
+ const filePath = path2.join(this.deps.entitiesDir, file);
561
1026
  if (filePath !== canonicalPath) {
562
1027
  try {
563
1028
  await unlink(filePath);
@@ -575,25 +1040,6 @@ ${rows.join("\n")}
575
1040
  }
576
1041
  };
577
1042
 
578
- // src/orchestration/self-deps.ts
579
- function selfDeps(self) {
580
- const source = self;
581
- return new Proxy(/* @__PURE__ */ Object.create(null), {
582
- get(_target, prop) {
583
- const value = source[prop];
584
- return typeof value === "function" ? value.bind(self) : value;
585
- },
586
- set(_target, prop, value) {
587
- if (!(prop in source)) return false;
588
- source[prop] = value;
589
- return true;
590
- },
591
- has(_target, prop) {
592
- return prop in source;
593
- }
594
- });
595
- }
596
-
597
1043
  // src/storage.ts
598
1044
  var ARTIFACT_SEARCH_STOPWORDS = /* @__PURE__ */ new Set([
599
1045
  "a",
@@ -1199,17 +1645,17 @@ var KNOWN_ENTITY_FILENAME_PREFIXES = /* @__PURE__ */ new Set([
1199
1645
  "topic"
1200
1646
  ]);
1201
1647
  function inferEntityTypeFromFilename(pathRel) {
1202
- const basename = path2.basename(pathRel, ".md").toLowerCase();
1648
+ const basename = path3.basename(pathRel, ".md").toLowerCase();
1203
1649
  const separator = basename.indexOf("-");
1204
1650
  if (separator <= 0) return void 0;
1205
1651
  const candidate = basename.slice(0, separator);
1206
1652
  return KNOWN_ENTITY_FILENAME_PREFIXES.has(candidate) ? candidate : void 0;
1207
1653
  }
1208
1654
  function normalizeFrontmatterForPath(frontmatter, pathRel, content = "") {
1209
- const normalizedPath = pathRel.split(path2.sep).join("/");
1655
+ const normalizedPath = pathRel.split(path3.sep).join("/");
1210
1656
  let normalizedFrontmatter = frontmatter;
1211
1657
  if (normalizedPath === "entities" || normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/")) {
1212
- const basename = path2.basename(pathRel, ".md");
1658
+ const basename = path3.basename(pathRel, ".md");
1213
1659
  const inferredType = inferEntityTypeFromContent(content) || inferEntityTypeFromFilename(pathRel) || "entity";
1214
1660
  const existingTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
1215
1661
  normalizedFrontmatter = {
@@ -1276,8 +1722,8 @@ var ContentHashIndex = class _ContentHashIndex {
1276
1722
  secureStoreKeyProvider;
1277
1723
  secureStoreWriteKeyProvider;
1278
1724
  memoryDir;
1279
- constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir = path2.dirname(stateDir)) {
1280
- this.filePath = path2.join(stateDir, "fact-hashes.txt");
1725
+ constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir = path3.dirname(stateDir)) {
1726
+ this.filePath = path3.join(stateDir, "fact-hashes.txt");
1281
1727
  this.secureStoreKeyProvider = secureStoreKeyProvider;
1282
1728
  this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;
1283
1729
  this.memoryDir = memoryDir;
@@ -1324,7 +1770,7 @@ var ContentHashIndex = class _ContentHashIndex {
1324
1770
  /** Persist index to disk if changed. */
1325
1771
  async save() {
1326
1772
  if (!this.dirty) return;
1327
- await mkdir(path2.dirname(this.filePath), { recursive: true });
1773
+ await mkdir(path3.dirname(this.filePath), { recursive: true });
1328
1774
  await writeMaybeEncryptedFile(
1329
1775
  this.filePath,
1330
1776
  [...this.hashes].join("\n") + "\n",
@@ -2178,7 +2624,7 @@ function isValidBufferSurpriseEvent(value) {
2178
2624
  }
2179
2625
  return true;
2180
2626
  }
2181
- var StorageManager = class _StorageManager {
2627
+ var StorageManager3 = class _StorageManager {
2182
2628
  constructor(baseDir, entitySchemas) {
2183
2629
  this.baseDir = baseDir;
2184
2630
  this.entitySchemas = entitySchemas;
@@ -2191,6 +2637,7 @@ var StorageManager = class _StorageManager {
2191
2637
  /** Read by storage/entity-store.ts (decomposition), hence not `private`. */
2192
2638
  static KNOWLEDGE_INDEX_CACHE_TTL_MS = 6e5;
2193
2639
  // 10 minutes (entity mutations invalidate)
2640
+ /** Read by storage/memory-read-store.ts (decomposition). */
2194
2641
  static ARTIFACT_INDEX_CACHE_TTL_MS = 6e4;
2195
2642
  // 1 minute
2196
2643
  static artifactWriteVersionByDir = /* @__PURE__ */ new Map();
@@ -2219,14 +2666,18 @@ var StorageManager = class _StorageManager {
2219
2666
  // on every cold-tier write, making the cache correct across process boundaries
2220
2667
  // (gateway + CLI). After Finding UTsP broadened the scan to the entire cold/
2221
2668
  // subtree, amortizing across back-to-back writes is even more important.
2669
+ /** Read by storage/memory-read-store.ts (decomposition). */
2222
2670
  static COLD_SCAN_CACHE_TTL_MS = 3e4;
2223
2671
  // 30 seconds
2672
+ /** Read by storage/memory-read-store.ts (decomposition). */
2224
2673
  static coldMemoriesCache = /* @__PURE__ */ new Map();
2225
2674
  // Cache for readQuestions() — avoids serially re-reading tens of thousands of
2226
2675
  // question files on every recall. 60-second TTL is intentionally short so that
2227
2676
  // newly written questions surface quickly.
2677
+ /** Read by storage/memory-read-store.ts (decomposition). */
2228
2678
  static QUESTIONS_CACHE_TTL_MS = 6e4;
2229
2679
  // 1 minute
2680
+ /** Read by storage/memory-read-store.ts (decomposition). */
2230
2681
  static questionsCache = /* @__PURE__ */ new Map();
2231
2682
  factHashIndex = null;
2232
2683
  factHashIndexLoadPromise = null;
@@ -2385,7 +2836,7 @@ var StorageManager = class _StorageManager {
2385
2836
  void 0,
2386
2837
  this.baseDir
2387
2838
  );
2388
- const rel = path2.relative(this.baseDir, filePath).split(path2.sep).join("/");
2839
+ const rel = path3.relative(this.baseDir, filePath).split(path3.sep).join("/");
2389
2840
  return `${rel}:${version.versionId}`;
2390
2841
  } catch (err) {
2391
2842
  log.warn(
@@ -2394,7 +2845,29 @@ var StorageManager = class _StorageManager {
2394
2845
  return null;
2395
2846
  }
2396
2847
  }
2848
+ /**
2849
+ * The LIVE class object of this instance (issue #1809 review of the
2850
+ * storage decomposition): extracted store modules must read/write the
2851
+ * shared static caches (questionsCache, coldMemoriesCache, ...) through
2852
+ * the SAME class binding the host methods use. Referencing the imported
2853
+ * `StorageManager` symbol from a split bundle chunk can yield a second
2854
+ * class copy with its own statics, split-braining the caches
2855
+ * (resolveQuestion clears copy A while readQuestions reads copy B).
2856
+ */
2857
+ get storageManagerClass() {
2858
+ return this.constructor;
2859
+ }
2397
2860
  /** The root directory of this storage instance. */
2861
+ /** MemoryReadStore (storage.ts decomposition). Lazy; selfDeps live wiring. */
2862
+ _memoryReadStore;
2863
+ get memoryReadStore() {
2864
+ if (!this._memoryReadStore) {
2865
+ this._memoryReadStore = new MemoryReadStore(
2866
+ selfDeps(this)
2867
+ );
2868
+ }
2869
+ return this._memoryReadStore;
2870
+ }
2398
2871
  /** EntityStore (storage.ts decomposition). Lazy; selfDeps live wiring. */
2399
2872
  _entityStore;
2400
2873
  get entityStore() {
@@ -2410,13 +2883,13 @@ var StorageManager = class _StorageManager {
2410
2883
  }
2411
2884
  identityFilePath(workspaceDir, namespace) {
2412
2885
  const rawNamespace = typeof namespace === "string" ? namespace.trim() : "";
2413
- if (!rawNamespace) return path2.join(workspaceDir, "IDENTITY.md");
2886
+ if (!rawNamespace) return path3.join(workspaceDir, "IDENTITY.md");
2414
2887
  const safeNamespace = rawNamespace.replace(/[^a-zA-Z0-9._-]/g, "-");
2415
- return path2.join(workspaceDir, `IDENTITY.${safeNamespace}.md`);
2888
+ return path3.join(workspaceDir, `IDENTITY.${safeNamespace}.md`);
2416
2889
  }
2417
2890
  versionFilePath(kind) {
2418
2891
  const fileName = kind === "memory-status" ? ".memory-status-version.log" : kind === "artifact-write" ? ".artifact-write-version.log" : ".cold-write-version.log";
2419
- return path2.join(this.stateDir, fileName);
2892
+ return path3.join(this.stateDir, fileName);
2420
2893
  }
2421
2894
  bumpSharedVersion(kind, fallbackMap) {
2422
2895
  const filePath = this.versionFilePath(kind);
@@ -2463,7 +2936,7 @@ var StorageManager = class _StorageManager {
2463
2936
  // memories.
2464
2937
  // -------------------------------------------------------------------------
2465
2938
  get wearablesDir() {
2466
- return path2.join(this.baseDir, WEARABLES_DIR_NAME);
2939
+ return path3.join(this.baseDir, WEARABLES_DIR_NAME);
2467
2940
  }
2468
2941
  /**
2469
2942
  * Resolve the on-disk path for a source/day transcript. Throws on
@@ -2481,7 +2954,7 @@ var StorageManager = class _StorageManager {
2481
2954
  `invalid wearable transcript date '${String(date)}' \u2014 expected YYYY-MM-DD`
2482
2955
  );
2483
2956
  }
2484
- return path2.join(this.wearablesDir, sourceId, `${date}.md`);
2957
+ return path3.join(this.wearablesDir, sourceId, `${date}.md`);
2485
2958
  }
2486
2959
  async writeWearableDayTranscript(sourceId, date, serialized) {
2487
2960
  const targetPath = this.wearableTranscriptPath(sourceId, date);
@@ -2497,48 +2970,10 @@ var StorageManager = class _StorageManager {
2497
2970
  throw err;
2498
2971
  }
2499
2972
  }
2500
- /**
2501
- * List stored transcript days, newest first, optionally scoped to one
2502
- * source. Non-transcript files in the tree are ignored.
2503
- */
2504
2973
  async listWearableTranscriptDays(sourceId) {
2505
- const days = [];
2506
- let sources;
2507
- if (sourceId !== void 0) {
2508
- sources = [sourceId];
2509
- } else {
2510
- try {
2511
- const entries = await readdir2(this.wearablesDir, { withFileTypes: true });
2512
- sources = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
2513
- } catch (err) {
2514
- if (err.code === "ENOENT") return [];
2515
- throw err;
2516
- }
2517
- }
2518
- for (const source of sources) {
2519
- if (!/^[a-z][a-z0-9-]{0,63}$/.test(source)) continue;
2520
- let entries;
2521
- try {
2522
- entries = await readdir2(path2.join(this.wearablesDir, source));
2523
- } catch (err) {
2524
- if (err.code === "ENOENT") continue;
2525
- throw err;
2526
- }
2527
- for (const entry of entries) {
2528
- if (!entry.endsWith(".md")) continue;
2529
- const date = entry.slice(0, -3);
2530
- if (!isValidTranscriptDate(date)) continue;
2531
- days.push({ source, date });
2532
- }
2533
- }
2534
- days.sort((a, b) => {
2535
- if (a.date > b.date) return -1;
2536
- if (a.date < b.date) return 1;
2537
- if (a.source < b.source) return -1;
2538
- if (a.source > b.source) return 1;
2539
- return 0;
2540
- });
2541
- return days;
2974
+ return this.memoryReadStore.listWearableTranscriptDays(
2975
+ sourceId
2976
+ );
2542
2977
  }
2543
2978
  /**
2544
2979
  * Locate a wearable-sourced memory by exact (trimmed) content,
@@ -2606,25 +3041,25 @@ var StorageManager = class _StorageManager {
2606
3041
  });
2607
3042
  }
2608
3043
  get factsDir() {
2609
- return path2.join(this.baseDir, "facts");
3044
+ return path3.join(this.baseDir, "facts");
2610
3045
  }
2611
3046
  get correctionsDir() {
2612
- return path2.join(this.baseDir, "corrections");
3047
+ return path3.join(this.baseDir, "corrections");
2613
3048
  }
2614
3049
  get proceduresDir() {
2615
- return path2.join(this.baseDir, "procedures");
3050
+ return path3.join(this.baseDir, "procedures");
2616
3051
  }
2617
3052
  get reasoningTracesDir() {
2618
- return path2.join(this.baseDir, "reasoning-traces");
3053
+ return path3.join(this.baseDir, "reasoning-traces");
2619
3054
  }
2620
3055
  get entitiesDir() {
2621
- return path2.join(this.baseDir, "entities");
3056
+ return path3.join(this.baseDir, "entities");
2622
3057
  }
2623
3058
  resolveEntityFilePath(name) {
2624
3059
  if (typeof name !== "string") return null;
2625
- const filePath = path2.resolve(this.entitiesDir, `${name}.md`);
2626
- const relative = path2.relative(this.entitiesDir, filePath);
2627
- if (relative === "" || relative.startsWith("..") || path2.isAbsolute(relative)) {
3060
+ const filePath = path3.resolve(this.entitiesDir, `${name}.md`);
3061
+ const relative = path3.relative(this.entitiesDir, filePath);
3062
+ if (relative === "" || relative.startsWith("..") || path3.isAbsolute(relative)) {
2628
3063
  return null;
2629
3064
  }
2630
3065
  return filePath;
@@ -2639,10 +3074,10 @@ var StorageManager = class _StorageManager {
2639
3074
  return writeMaybeEncryptedFileFromChunks(filePath, chunks, this.resolveWriteKey(), {}, this.baseDir).then(() => this.notifyCatalogWrite());
2640
3075
  }
2641
3076
  assertManagedStoragePath(filePath, method) {
2642
- const resolved = path2.resolve(filePath);
2643
- const base = path2.resolve(this.baseDir);
2644
- const rel = path2.relative(base, resolved);
2645
- if (rel === "" || rel === ".." || rel.startsWith(`..${path2.sep}`) || path2.isAbsolute(rel)) {
3077
+ const resolved = path3.resolve(filePath);
3078
+ const base = path3.resolve(this.baseDir);
3079
+ const rel = path3.relative(base, resolved);
3080
+ if (rel === "" || rel === ".." || rel.startsWith(`..${path3.sep}`) || path3.isAbsolute(rel)) {
2646
3081
  throw new Error(`${method}: file path escapes memory dir`);
2647
3082
  }
2648
3083
  return resolved;
@@ -2653,8 +3088,8 @@ var StorageManager = class _StorageManager {
2653
3088
  }
2654
3089
  async digestOfflineSyncFile(filePath) {
2655
3090
  const target = this.assertManagedStoragePath(filePath, "storage.digestOfflineSyncFile");
2656
- const st = await stat(target);
2657
- const relPath = path2.relative(this.baseDir, target).split(path2.sep).join("/");
3091
+ const st = await stat2(target);
3092
+ const relPath = path3.relative(this.baseDir, target).split(path3.sep).join("/");
2658
3093
  const cache = await this.loadOfflineSyncDigestCache();
2659
3094
  const cached = cache.get(relPath);
2660
3095
  if (cached && cached.statBytes === st.size && cached.mtimeMs === st.mtimeMs && cached.ctimeMs === st.ctimeMs && !cached.encrypted) {
@@ -2715,7 +3150,7 @@ var StorageManager = class _StorageManager {
2715
3150
  const bytes = typeof record.bytes === "number" ? record.bytes : NaN;
2716
3151
  const sha256 = typeof record.sha256 === "string" ? record.sha256 : "";
2717
3152
  const encrypted = record.encrypted === true;
2718
- if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") || path2.isAbsolute(cachePath) || !Number.isFinite(statBytes) || !Number.isFinite(mtimeMs) || !Number.isFinite(ctimeMs) || !Number.isFinite(bytes) || !/^[a-f0-9]{64}$/i.test(sha256)) {
3153
+ if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") || path3.isAbsolute(cachePath) || !Number.isFinite(statBytes) || !Number.isFinite(mtimeMs) || !Number.isFinite(ctimeMs) || !Number.isFinite(bytes) || !/^[a-f0-9]{64}$/i.test(sha256)) {
2719
3154
  continue;
2720
3155
  }
2721
3156
  cache.set(cachePath, { statBytes, mtimeMs, ctimeMs, encrypted, sha256, bytes });
@@ -2757,7 +3192,7 @@ var StorageManager = class _StorageManager {
2757
3192
  const cache = this.offlineSyncDigestCache;
2758
3193
  if (!cache) return;
2759
3194
  const entries = [...cache.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([entryPath, entry]) => ({ path: entryPath, ...entry }));
2760
- await mkdir(path2.dirname(this.offlineSyncDigestCachePath), { recursive: true });
3195
+ await mkdir(path3.dirname(this.offlineSyncDigestCachePath), { recursive: true });
2761
3196
  await writeFile(
2762
3197
  this.offlineSyncDigestCachePath,
2763
3198
  `${JSON.stringify({ version: 1, entries })}
@@ -2810,10 +3245,10 @@ var StorageManager = class _StorageManager {
2810
3245
  if (isErrnoCode(error, "ENOENT")) return;
2811
3246
  throw error;
2812
3247
  });
2813
- if (filePath.includes(`${path2.sep}cold${path2.sep}`)) {
3248
+ if (filePath.includes(`${path3.sep}cold${path3.sep}`)) {
2814
3249
  this.invalidateColdMemoriesCache();
2815
3250
  }
2816
- if (filePath.includes(`${path2.sep}artifacts${path2.sep}`)) {
3251
+ if (filePath.includes(`${path3.sep}artifacts${path3.sep}`)) {
2817
3252
  this.bumpArtifactWriteVersion();
2818
3253
  }
2819
3254
  this.bumpMemoryStatusVersion();
@@ -2841,7 +3276,7 @@ var StorageManager = class _StorageManager {
2841
3276
  }
2842
3277
  async appendStorageSecureFileUnlocked(filePath, content) {
2843
3278
  const writeKey = this.resolveWriteKey();
2844
- await mkdir(path2.dirname(filePath), { recursive: true });
3279
+ await mkdir(path3.dirname(filePath), { recursive: true });
2845
3280
  if (writeKey === null) {
2846
3281
  try {
2847
3282
  if (isEncryptedFile(await readFile(filePath))) {
@@ -2867,16 +3302,16 @@ var StorageManager = class _StorageManager {
2867
3302
  this.notifyCatalogWrite();
2868
3303
  }
2869
3304
  get stateDir() {
2870
- return path2.join(this.baseDir, "state");
3305
+ return path3.join(this.baseDir, "state");
2871
3306
  }
2872
3307
  get offlineSyncDigestCachePath() {
2873
- return path2.join(this.baseDir, ".offline-sync", "digest-cache.v1.json");
3308
+ return path3.join(this.baseDir, ".offline-sync", "digest-cache.v1.json");
2874
3309
  }
2875
3310
  get entitySynthesisQueuePath() {
2876
- return path2.join(this.stateDir, "entity-synthesis-queue.json");
3311
+ return path3.join(this.stateDir, "entity-synthesis-queue.json");
2877
3312
  }
2878
3313
  get factHashIndexReadyPath() {
2879
- return path2.join(this.stateDir, "fact-hashes.ready");
3314
+ return path3.join(this.stateDir, "fact-hashes.ready");
2880
3315
  }
2881
3316
  // ── Tombstone store access (issue #1579) ─────────────────────────────────
2882
3317
  /**
@@ -2886,7 +3321,7 @@ var StorageManager = class _StorageManager {
2886
3321
  * helpers are injected into the store).
2887
3322
  */
2888
3323
  get tombstonesPath() {
2889
- return path2.join(this.stateDir, "tombstones.jsonl");
3324
+ return path3.join(this.stateDir, "tombstones.jsonl");
2890
3325
  }
2891
3326
  /**
2892
3327
  * Configure the tombstone invariant for this storage instance. Installed
@@ -3085,7 +3520,7 @@ var StorageManager = class _StorageManager {
3085
3520
  );
3086
3521
  }
3087
3522
  await factHashIndex.save();
3088
- await mkdir(path2.dirname(this.factHashIndexReadyPath), { recursive: true });
3523
+ await mkdir(path3.dirname(this.factHashIndexReadyPath), { recursive: true });
3089
3524
  await writeFile(this.factHashIndexReadyPath, "v1\n", "utf-8");
3090
3525
  this.factHashIndexAuthoritative = true;
3091
3526
  })().finally(() => {
@@ -3094,55 +3529,55 @@ var StorageManager = class _StorageManager {
3094
3529
  await this.factHashIndexAuthoritativePromise;
3095
3530
  }
3096
3531
  get questionsDir() {
3097
- return path2.join(this.baseDir, "questions");
3532
+ return path3.join(this.baseDir, "questions");
3098
3533
  }
3099
3534
  get artifactsDir() {
3100
- return path2.join(this.baseDir, "artifacts");
3535
+ return path3.join(this.baseDir, "artifacts");
3101
3536
  }
3102
3537
  get identityDir() {
3103
- return path2.join(this.baseDir, "identity");
3538
+ return path3.join(this.baseDir, "identity");
3104
3539
  }
3105
3540
  get identityAnchorPath() {
3106
- return path2.join(this.identityDir, "identity-anchor.md");
3541
+ return path3.join(this.identityDir, "identity-anchor.md");
3107
3542
  }
3108
3543
  get identityIncidentsDir() {
3109
- return path2.join(this.identityDir, "incidents");
3544
+ return path3.join(this.identityDir, "incidents");
3110
3545
  }
3111
3546
  get identityAuditsWeeklyDir() {
3112
- return path2.join(this.identityDir, "audits", "weekly");
3547
+ return path3.join(this.identityDir, "audits", "weekly");
3113
3548
  }
3114
3549
  get identityAuditsMonthlyDir() {
3115
- return path2.join(this.identityDir, "audits", "monthly");
3550
+ return path3.join(this.identityDir, "audits", "monthly");
3116
3551
  }
3117
3552
  get identityImprovementLoopsPath() {
3118
- return path2.join(this.identityDir, "improvement-loops.md");
3553
+ return path3.join(this.identityDir, "improvement-loops.md");
3119
3554
  }
3120
3555
  get identityReflectionsPath() {
3121
- return path2.join(this.identityDir, "reflections.md");
3556
+ return path3.join(this.identityDir, "reflections.md");
3122
3557
  }
3123
3558
  get profilePath() {
3124
- return path2.join(this.baseDir, "profile.md");
3559
+ return path3.join(this.baseDir, "profile.md");
3125
3560
  }
3126
3561
  get memoryActionsPath() {
3127
- return path2.join(this.stateDir, "memory-actions.jsonl");
3562
+ return path3.join(this.stateDir, "memory-actions.jsonl");
3128
3563
  }
3129
3564
  get memoryLifecycleLedgerPath() {
3130
- return path2.join(this.stateDir, "memory-lifecycle-ledger.jsonl");
3565
+ return path3.join(this.stateDir, "memory-lifecycle-ledger.jsonl");
3131
3566
  }
3132
3567
  get compressionGuidelinesPath() {
3133
- return path2.join(this.stateDir, "compression-guidelines.md");
3568
+ return path3.join(this.stateDir, "compression-guidelines.md");
3134
3569
  }
3135
3570
  get compressionGuidelineDraftPath() {
3136
- return path2.join(this.stateDir, "compression-guidelines.draft.md");
3571
+ return path3.join(this.stateDir, "compression-guidelines.draft.md");
3137
3572
  }
3138
3573
  get compressionGuidelineStatePath() {
3139
- return path2.join(this.stateDir, "compression-guideline-state.json");
3574
+ return path3.join(this.stateDir, "compression-guideline-state.json");
3140
3575
  }
3141
3576
  get compressionGuidelineDraftStatePath() {
3142
- return path2.join(this.stateDir, "compression-guideline-draft-state.json");
3577
+ return path3.join(this.stateDir, "compression-guideline-draft-state.json");
3143
3578
  }
3144
3579
  get behaviorSignalsPath() {
3145
- return path2.join(this.stateDir, "behavior-signals.jsonl");
3580
+ return path3.join(this.stateDir, "behavior-signals.jsonl");
3146
3581
  }
3147
3582
  /**
3148
3583
  * Buffer surprise telemetry ledger (issue #563 PR 3).
@@ -3155,7 +3590,7 @@ var StorageManager = class _StorageManager {
3155
3590
  * governance sweeps can treat it uniformly.
3156
3591
  */
3157
3592
  get bufferSurpriseLedgerPath() {
3158
- return path2.join(this.stateDir, "buffer-surprise-ledger.jsonl");
3593
+ return path3.join(this.stateDir, "buffer-surprise-ledger.jsonl");
3159
3594
  }
3160
3595
  /**
3161
3596
  * Entity alias table loaded from THIS store's config/aliases.json.
@@ -3189,7 +3624,7 @@ var StorageManager = class _StorageManager {
3189
3624
  this.loadAliasesSync();
3190
3625
  }
3191
3626
  loadAliasesSync() {
3192
- const aliasPath = path2.join(this.baseDir, "config", "aliases.json");
3627
+ const aliasPath = path3.join(this.baseDir, "config", "aliases.json");
3193
3628
  this.userAliases = {};
3194
3629
  try {
3195
3630
  const raw = readFileSync(aliasPath, "utf-8");
@@ -3214,9 +3649,9 @@ var StorageManager = class _StorageManager {
3214
3649
  }
3215
3650
  async ensureDirectories() {
3216
3651
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3217
- await mkdir(path2.join(this.factsDir, today), { recursive: true });
3218
- await mkdir(path2.join(this.proceduresDir, today), { recursive: true });
3219
- await mkdir(path2.join(this.reasoningTracesDir, today), { recursive: true });
3652
+ await mkdir(path3.join(this.factsDir, today), { recursive: true });
3653
+ await mkdir(path3.join(this.proceduresDir, today), { recursive: true });
3654
+ await mkdir(path3.join(this.reasoningTracesDir, today), { recursive: true });
3220
3655
  await mkdir(this.correctionsDir, { recursive: true });
3221
3656
  await mkdir(this.entitiesDir, { recursive: true });
3222
3657
  await mkdir(this.stateDir, { recursive: true });
@@ -3226,7 +3661,7 @@ var StorageManager = class _StorageManager {
3226
3661
  await mkdir(this.identityIncidentsDir, { recursive: true });
3227
3662
  await mkdir(this.identityAuditsWeeklyDir, { recursive: true });
3228
3663
  await mkdir(this.identityAuditsMonthlyDir, { recursive: true });
3229
- await mkdir(path2.join(this.baseDir, "config"), { recursive: true });
3664
+ await mkdir(path3.join(this.baseDir, "config"), { recursive: true });
3230
3665
  }
3231
3666
  /**
3232
3667
  * Resolve the on-disk write path for a memory of the given category, creating
@@ -3243,11 +3678,11 @@ var StorageManager = class _StorageManager {
3243
3678
  async resolveCategoryWritePath(category, id, today) {
3244
3679
  if (category === "correction") {
3245
3680
  await mkdir(this.correctionsDir, { recursive: true });
3246
- return path2.join(this.correctionsDir, `${id}.md`);
3681
+ return path3.join(this.correctionsDir, `${id}.md`);
3247
3682
  }
3248
- const datedDir = path2.join(getCategoryDir(this.baseDir, category), today);
3683
+ const datedDir = path3.join(getCategoryDir(this.baseDir, category), today);
3249
3684
  await mkdir(datedDir, { recursive: true });
3250
- return path2.join(datedDir, `${id}.md`);
3685
+ return path3.join(datedDir, `${id}.md`);
3251
3686
  }
3252
3687
  async writeMemory(category, content, options = {}) {
3253
3688
  await this.ensureDirectories();
@@ -3497,7 +3932,7 @@ ${sanitized.text}
3497
3932
  await this.ensureDirectories();
3498
3933
  const now = /* @__PURE__ */ new Date();
3499
3934
  const day = now.toISOString().slice(0, 10);
3500
- const dir = path2.join(this.artifactsDir, day);
3935
+ const dir = path3.join(this.artifactsDir, day);
3501
3936
  await mkdir(dir, { recursive: true });
3502
3937
  const id = `artifact-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
3503
3938
  const fm = {
@@ -3521,7 +3956,7 @@ ${sanitized.text}
3521
3956
  log.warn(`artifact content rejected for ${id}; violations=${sanitized.violations.join(", ")}`);
3522
3957
  return "";
3523
3958
  }
3524
- const filePath = path2.join(dir, `${id}.md`);
3959
+ const filePath = path3.join(dir, `${id}.md`);
3525
3960
  await this.writeStorageSecureFile(filePath, `${serializeFrontmatter(fm)}
3526
3961
 
3527
3962
  ${sanitized.text}
@@ -3540,45 +3975,7 @@ ${sanitized.text}
3540
3975
  return id;
3541
3976
  }
3542
3977
  async readAllArtifactsCached() {
3543
- if (this.artifactIndexCache && Date.now() - this.artifactIndexCache.loadedAtMs <= _StorageManager.ARTIFACT_INDEX_CACHE_TTL_MS && this.artifactIndexCache.writeVersion === this.getArtifactWriteVersion()) {
3544
- return this.artifactIndexCache.memories;
3545
- }
3546
- const scanArtifacts = async () => {
3547
- const artifacts = [];
3548
- const readDir = async (dir) => {
3549
- try {
3550
- const entries = await readdir2(dir, { withFileTypes: true });
3551
- for (const entry of entries) {
3552
- const fullPath = path2.join(dir, entry.name);
3553
- if (entry.isDirectory()) {
3554
- await readDir(fullPath);
3555
- continue;
3556
- }
3557
- if (!entry.name.endsWith(".md")) continue;
3558
- const memory = await this.readMemoryByPath(fullPath);
3559
- if (!memory) continue;
3560
- artifacts.push(memory);
3561
- }
3562
- } catch {
3563
- }
3564
- };
3565
- await readDir(this.artifactsDir);
3566
- return artifacts;
3567
- };
3568
- const MAX_REBUILD_RETRIES = 2;
3569
- let latestArtifacts = [];
3570
- for (let attempt = 0; attempt <= MAX_REBUILD_RETRIES; attempt += 1) {
3571
- const versionBefore = this.getArtifactWriteVersion();
3572
- const artifacts = await scanArtifacts();
3573
- const versionAfter = this.getArtifactWriteVersion();
3574
- latestArtifacts = artifacts;
3575
- if (versionAfter === versionBefore) {
3576
- this.artifactIndexCache = { memories: artifacts, loadedAtMs: Date.now(), writeVersion: versionAfter };
3577
- return artifacts;
3578
- }
3579
- }
3580
- this.artifactIndexCache = null;
3581
- return latestArtifacts;
3978
+ return this.memoryReadStore.readAllArtifactsCached();
3582
3979
  }
3583
3980
  async searchArtifacts(query, maxResults) {
3584
3981
  const tokens = tokenizeArtifactSearchText(query);
@@ -3761,7 +4158,7 @@ ${sanitized.text}
3761
4158
  * per-process in-memory cache safe across process boundaries.
3762
4159
  */
3763
4160
  invalidateColdMemoriesCache() {
3764
- const coldRoot = path2.join(this.baseDir, "cold");
4161
+ const coldRoot = path3.join(this.baseDir, "cold");
3765
4162
  _StorageManager.coldMemoriesCache.delete(coldRoot);
3766
4163
  this.bumpColdWriteVersion();
3767
4164
  invalidateAllForDir(this.baseDir);
@@ -3784,45 +4181,7 @@ ${sanitized.text}
3784
4181
  return Math.max(1, Math.floor(batchSize));
3785
4182
  }
3786
4183
  async collectActiveMemoryPaths() {
3787
- const filePaths = [];
3788
- let memoryRootReal;
3789
- try {
3790
- memoryRootReal = await realpath(this.baseDir);
3791
- } catch {
3792
- return filePaths;
3793
- }
3794
- const collectPaths = async (dir) => {
3795
- let entries;
3796
- try {
3797
- const dirStat = await lstat(dir);
3798
- if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) return;
3799
- assertPathInsideRoot(memoryRootReal, await realpath(dir), dir);
3800
- entries = await readdir2(dir, { withFileTypes: true });
3801
- } catch {
3802
- return;
3803
- }
3804
- const subdirs = [];
3805
- for (const entry of entries) {
3806
- if (entry.isSymbolicLink()) continue;
3807
- const fullPath = path2.join(dir, entry.name);
3808
- if (entry.isDirectory()) {
3809
- subdirs.push(fullPath);
3810
- } else if (entry.name.endsWith(".md")) {
3811
- try {
3812
- assertPathInsideRoot(memoryRootReal, await realpath(fullPath), fullPath);
3813
- filePaths.push(fullPath);
3814
- } catch {
3815
- }
3816
- }
3817
- }
3818
- for (const subdir of subdirs) {
3819
- await collectPaths(subdir);
3820
- }
3821
- };
3822
- for (const dir of RECALL_FALLBACK_DIRS) {
3823
- await collectPaths(path2.join(this.baseDir, dir));
3824
- }
3825
- return filePaths;
4184
+ return this.memoryReadStore.collectActiveMemoryPaths();
3826
4185
  }
3827
4186
  async readParsedMemoriesFromPaths(filePaths, batchSize) {
3828
4187
  if (filePaths.length === 0) return [];
@@ -3877,7 +4236,7 @@ ${sanitized.text}
3877
4236
  return updatedMs >= updatedAfterMs ? filePath : null;
3878
4237
  }
3879
4238
  try {
3880
- const fileStat = await stat(filePath);
4239
+ const fileStat = await stat2(filePath);
3881
4240
  return fileStat.mtimeMs >= updatedAfterMs ? filePath : null;
3882
4241
  } catch {
3883
4242
  return filePath;
@@ -3889,7 +4248,7 @@ ${sanitized.text}
3889
4248
  const correctionPaths = [];
3890
4249
  const factPaths = [];
3891
4250
  for (const filePath of filePaths) {
3892
- if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${path2.sep}`)) {
4251
+ if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${path3.sep}`)) {
3893
4252
  correctionPaths.push(filePath);
3894
4253
  } else {
3895
4254
  factPaths.push(filePath);
@@ -3922,111 +4281,26 @@ ${sanitized.text}
3922
4281
  index,
3923
4282
  index + Math.min(parallelWindow, availableInspectionBudget)
3924
4283
  );
3925
- index += candidatePaths.length;
3926
- if (candidatePaths.length === 0) break;
3927
- filePaths.push(...candidatePaths);
3928
- const parsedMemories = await this.readParsedMemoriesFromPaths(candidatePaths, candidatePaths.length);
3929
- if (parsedMemories.length === 0) continue;
3930
- memories.push(...parsedMemories.slice(0, availableSlots));
3931
- }
3932
- return { memories, filePaths };
3933
- }
3934
- async readMemoriesWindow(options = {}) {
3935
- const allPaths = await this.collectActiveMemoryPaths();
3936
- const sortedPaths = this.orderWindowPaths(allPaths);
3937
- const maxMemories = typeof options.maxMemories === "number" && Number.isFinite(options.maxMemories) ? Math.max(1, Math.floor(options.maxMemories)) : void 0;
3938
- const maxCandidatePaths = maxMemories === void 0 ? void 0 : maxMemories * 2;
3939
- const updatedAfterMs = options.updatedAfter?.getTime();
3940
- const normalizedBatchSize = this.normalizeMemoryReadBatchSize(options.batchSize);
3941
- const memories = [];
3942
- const selectedPaths = [];
3943
- for (let i = 0; i < sortedPaths.length; i += normalizedBatchSize) {
3944
- if (maxMemories !== void 0 && (memories.length >= maxMemories || maxCandidatePaths !== void 0 && selectedPaths.length >= maxCandidatePaths)) {
3945
- return { memories, filePaths: selectedPaths };
3946
- }
3947
- const batchPaths = sortedPaths.slice(i, i + normalizedBatchSize);
3948
- const candidateBatchPaths = updatedAfterMs === void 0 ? batchPaths : await this.filterWindowPathsByUpdatedAfter(batchPaths, updatedAfterMs);
3949
- const remainingSlots = maxMemories === void 0 ? void 0 : Math.max(0, maxMemories - memories.length);
3950
- const remainingInspectionBudget = maxCandidatePaths === void 0 ? void 0 : Math.max(0, maxCandidatePaths - selectedPaths.length);
3951
- const { memories: batchMemories, filePaths: parsedCandidatePaths } = remainingSlots === void 0 ? {
3952
- memories: await this.readParsedMemoriesFromPaths(candidateBatchPaths, normalizedBatchSize),
3953
- filePaths: candidateBatchPaths
3954
- } : await this.readWindowBoundedBatch(
3955
- candidateBatchPaths,
3956
- remainingSlots,
3957
- remainingInspectionBudget ?? remainingSlots,
3958
- normalizedBatchSize
3959
- );
3960
- selectedPaths.push(...parsedCandidatePaths);
3961
- for (const memory of batchMemories) {
3962
- memories.push(memory);
3963
- if (maxMemories !== void 0 && memories.length >= maxMemories) {
3964
- return { memories, filePaths: selectedPaths };
3965
- }
3966
- }
4284
+ index += candidatePaths.length;
4285
+ if (candidatePaths.length === 0) break;
4286
+ filePaths.push(...candidatePaths);
4287
+ const parsedMemories = await this.readParsedMemoriesFromPaths(candidatePaths, candidatePaths.length);
4288
+ if (parsedMemories.length === 0) continue;
4289
+ memories.push(...parsedMemories.slice(0, availableSlots));
3967
4290
  }
3968
- return { memories, filePaths: selectedPaths };
4291
+ return { memories, filePaths };
4292
+ }
4293
+ async readMemoriesWindow(options = {}) {
4294
+ return this.memoryReadStore.readMemoriesWindow(
4295
+ options
4296
+ );
3969
4297
  }
3970
4298
  async _readAllMemoriesFromDisk() {
3971
4299
  const filePaths = await this.collectActiveMemoryPaths();
3972
4300
  return this.readParsedMemoriesFromPaths(filePaths, 50);
3973
4301
  }
3974
- /**
3975
- * Read all memories from the cold tier by scanning the entire cold/ root
3976
- * tree. Previously this only scanned cold/facts/ and cold/corrections/, but
3977
- * structuredAttributes can appear on any MemoryCategory (preference, decision,
3978
- * entity, etc.). buildTierMemoryPath now routes each category to its own
3979
- * cold/<dir>/ subtree via the shared categoryDirName() chokepoint (issue
3980
- * #1546), so cold decisions/preferences/... live outside cold/facts/.
3981
- * Scanning the full coldRoot covers every category dir and guards against
3982
- * files placed in unexpected subdirectories during manual operations or future
3983
- * refactors.
3984
- *
3985
- * Broadened in PR #402 round-6 (Finding UTsP): scanning only facts/ and
3986
- * corrections/ was a narrower-than-necessary subset of the cold directory
3987
- * tree. Correctness trumps the minor performance difference — cold scans
3988
- * already happen at most once per supersession write.
3989
- *
3990
- * Used by applyTemporalSupersession so that memories already demoted to
3991
- * cold/ can still be marked superseded when a newer hot fact arrives.
3992
- *
3993
- * Cached with a TTL (Finding UOGi, PR #402 round-6): back-to-back
3994
- * structured-attribute writes in the same burst reuse the cached result
3995
- * instead of re-scanning the cold tree on every call. The cache is
3996
- * invalidated whenever a write calls invalidateAllMemoriesCache() (which
3997
- * covers any hot→cold demotion that changes cold-tier contents) and
3998
- * expires after COLD_SCAN_CACHE_TTL_MS as a safety net.
3999
- */
4000
4302
  async readAllColdMemories() {
4001
- const coldRoot = this.resolveTierRootDir("cold");
4002
- const currentColdVersion = this.readColdWriteVersion();
4003
- const cached = _StorageManager.coldMemoriesCache.get(coldRoot);
4004
- if (cached && Date.now() - cached.loadedAt < _StorageManager.COLD_SCAN_CACHE_TTL_MS && cached.coldVersion === currentColdVersion) {
4005
- return cached.memories;
4006
- }
4007
- const filePaths = [];
4008
- const collectPaths = async (dir) => {
4009
- try {
4010
- const entries = await readdir2(dir, { withFileTypes: true });
4011
- const subdirs = [];
4012
- for (const entry of entries) {
4013
- const fullPath = path2.join(dir, entry.name);
4014
- if (entry.isDirectory()) {
4015
- subdirs.push(fullPath);
4016
- } else if (entry.name.endsWith(".md")) {
4017
- filePaths.push(fullPath);
4018
- }
4019
- }
4020
- for (const subdir of subdirs) {
4021
- await collectPaths(subdir);
4022
- }
4023
- } catch {
4024
- }
4025
- };
4026
- await collectPaths(coldRoot);
4027
- const memories = await this.readParsedMemoriesFromPaths(filePaths, 50);
4028
- _StorageManager.coldMemoriesCache.set(coldRoot, { memories, loadedAt: Date.now(), coldVersion: currentColdVersion });
4029
- return memories;
4303
+ return this.memoryReadStore.readAllColdMemories();
4030
4304
  }
4031
4305
  /**
4032
4306
  * Read archived memory markdown files under archive/.
@@ -4037,9 +4311,9 @@ ${sanitized.text}
4037
4311
  const root = this.archiveDir;
4038
4312
  const readDir = async (dir) => {
4039
4313
  try {
4040
- const entries = await readdir2(dir, { withFileTypes: true });
4314
+ const entries = await readdir3(dir, { withFileTypes: true });
4041
4315
  for (const entry of entries) {
4042
- const fullPath = path2.join(dir, entry.name);
4316
+ const fullPath = path3.join(dir, entry.name);
4043
4317
  if (entry.isDirectory()) {
4044
4318
  await readDir(fullPath);
4045
4319
  } else if (entry.name.endsWith(".md")) {
@@ -4068,51 +4342,13 @@ ${sanitized.text}
4068
4342
  await readDir(root);
4069
4343
  return memories;
4070
4344
  }
4071
- /** Read a single memory file by its absolute path. Returns null if unreadable. */
4072
4345
  async readMemoryByPath(filePath) {
4073
- try {
4074
- const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
4075
- const parsed = parseFrontmatter(raw);
4076
- if (parsed) {
4077
- return {
4078
- path: filePath,
4079
- frontmatter: normalizeFrontmatterForPath(
4080
- parsed.frontmatter,
4081
- toMemoryPathRel(this.baseDir, filePath),
4082
- parsed.content
4083
- ),
4084
- content: parsed.content
4085
- };
4086
- }
4087
- const normalizedPath = filePath.split(path2.sep).join("/");
4088
- if (normalizedPath.includes("/entities/") && filePath.endsWith(".md")) {
4089
- const entity = parseEntityFile(raw, this.entitySchemas);
4090
- if (!entity.name) return null;
4091
- const nameWithoutExt = path2.basename(filePath, ".md");
4092
- const fileMtime = entity.updated || await stat(filePath).then((s) => s.mtime.toISOString()).catch(() => (/* @__PURE__ */ new Date(0)).toISOString());
4093
- return {
4094
- path: filePath,
4095
- frontmatter: {
4096
- id: nameWithoutExt,
4097
- category: "entity",
4098
- created: fileMtime,
4099
- updated: fileMtime,
4100
- source: "entity_extraction",
4101
- confidence: 0.9,
4102
- confidenceTier: confidenceTier(0.9),
4103
- tags: entity.type ? [entity.type] : []
4104
- },
4105
- content: raw
4106
- };
4107
- }
4108
- return null;
4109
- } catch (err) {
4110
- if (err instanceof SecureStoreLockedError) throw err;
4111
- return null;
4112
- }
4346
+ return this.memoryReadStore.readMemoryByPath(
4347
+ filePath
4348
+ );
4113
4349
  }
4114
4350
  resolveTierRootDir(tier) {
4115
- return tier === "cold" ? path2.join(this.baseDir, "cold") : this.baseDir;
4351
+ return tier === "cold" ? path3.join(this.baseDir, "cold") : this.baseDir;
4116
4352
  }
4117
4353
  resolveMemoryDateDir(memory) {
4118
4354
  const preferred = memory.frontmatter.created || memory.frontmatter.updated;
@@ -4127,13 +4363,13 @@ ${sanitized.text}
4127
4363
  buildTierMemoryPath(memory, tier) {
4128
4364
  const root = this.resolveTierRootDir(tier);
4129
4365
  if (this.isArtifactMemory(memory)) {
4130
- return path2.join(root, "artifacts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
4366
+ return path3.join(root, "artifacts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
4131
4367
  }
4132
4368
  if (memory.frontmatter.category === "correction") {
4133
- return path2.join(root, "corrections", `${memory.frontmatter.id}.md`);
4369
+ return path3.join(root, "corrections", `${memory.frontmatter.id}.md`);
4134
4370
  }
4135
4371
  const dir = categoryDirName(memory.frontmatter.category);
4136
- return path2.join(root, dir, this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
4372
+ return path3.join(root, dir, this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
4137
4373
  }
4138
4374
  async writeMemoryFileAtomic(targetPath, memory) {
4139
4375
  const fileContent = `${serializeFrontmatter(memory.frontmatter)}
@@ -4146,8 +4382,8 @@ ${memory.content}
4146
4382
  }
4147
4383
  async moveMemoryToPath(memory, targetPath) {
4148
4384
  await this.writeMemoryFileAtomic(targetPath, memory);
4149
- const sourcePath = path2.resolve(memory.path);
4150
- const destPath = path2.resolve(targetPath);
4385
+ const sourcePath = path3.resolve(memory.path);
4386
+ const destPath = path3.resolve(targetPath);
4151
4387
  if (sourcePath !== destPath) {
4152
4388
  try {
4153
4389
  await unlink2(memory.path);
@@ -4162,8 +4398,8 @@ ${memory.content}
4162
4398
  }
4163
4399
  async migrateMemoryToTier(memory, targetTier) {
4164
4400
  const targetPath = this.buildTierMemoryPath(memory, targetTier);
4165
- const sourcePath = path2.resolve(memory.path);
4166
- const destPath = path2.resolve(targetPath);
4401
+ const sourcePath = path3.resolve(memory.path);
4402
+ const destPath = path3.resolve(targetPath);
4167
4403
  if (sourcePath === destPath) {
4168
4404
  return { changed: false, targetPath };
4169
4405
  }
@@ -4189,7 +4425,7 @@ ${memory.content}
4189
4425
  return { changed: true, targetPath };
4190
4426
  }
4191
4427
  get archiveDir() {
4192
- return path2.join(this.baseDir, "archive");
4428
+ return path3.join(this.baseDir, "archive");
4193
4429
  }
4194
4430
  /**
4195
4431
  * Archive a memory by moving it from facts/ to archive/YYYY-MM-DD/.
@@ -4200,7 +4436,7 @@ ${memory.content}
4200
4436
  try {
4201
4437
  const now = lifecycle?.at ?? /* @__PURE__ */ new Date();
4202
4438
  const today = now.toISOString().slice(0, 10);
4203
- const destDir = path2.join(this.archiveDir, today);
4439
+ const destDir = path3.join(this.archiveDir, today);
4204
4440
  await mkdir(destDir, { recursive: true });
4205
4441
  const updatedFm = {
4206
4442
  ...memory.frontmatter,
@@ -4212,7 +4448,7 @@ ${memory.content}
4212
4448
 
4213
4449
  ${memory.content}
4214
4450
  `;
4215
- const destPath = path2.join(destDir, path2.basename(memory.path));
4451
+ const destPath = path3.join(destDir, path3.basename(memory.path));
4216
4452
  await this.writeStorageSecureFile(destPath, fileContent);
4217
4453
  await unlink2(memory.path);
4218
4454
  this.invalidateAllMemoriesCache();
@@ -4241,7 +4477,7 @@ ${memory.content}
4241
4477
  }
4242
4478
  async readEntities() {
4243
4479
  try {
4244
- const entries = await readdir2(this.entitiesDir);
4480
+ const entries = await readdir3(this.entitiesDir);
4245
4481
  return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", ""));
4246
4482
  } catch {
4247
4483
  return [];
@@ -4360,7 +4596,7 @@ ${memory.content}
4360
4596
  `;
4361
4597
  await this.writeStorageSecureFile(memory.path, fileContent);
4362
4598
  this.invalidateAllMemoriesCache();
4363
- if (memory.path.includes(`${path2.sep}cold${path2.sep}`)) {
4599
+ if (memory.path.includes(`${path3.sep}cold${path3.sep}`)) {
4364
4600
  this.invalidateColdMemoriesCache();
4365
4601
  }
4366
4602
  try {
@@ -4432,7 +4668,7 @@ ${memory.content}
4432
4668
  return deleted;
4433
4669
  }
4434
4670
  async loadBuffer() {
4435
- const bufferPath = path2.join(this.stateDir, "buffer.json");
4671
+ const bufferPath = path3.join(this.stateDir, "buffer.json");
4436
4672
  try {
4437
4673
  const raw = await this.readStorageSecureFile(bufferPath);
4438
4674
  return JSON.parse(raw);
@@ -4444,11 +4680,11 @@ ${memory.content}
4444
4680
  }
4445
4681
  async saveBuffer(state) {
4446
4682
  await this.ensureDirectories();
4447
- const bufferPath = path2.join(this.stateDir, "buffer.json");
4683
+ const bufferPath = path3.join(this.stateDir, "buffer.json");
4448
4684
  await this.writeStorageSecureFile(bufferPath, JSON.stringify(state, null, 2));
4449
4685
  }
4450
4686
  async loadMeta() {
4451
- const metaPath = path2.join(this.stateDir, "meta.json");
4687
+ const metaPath = path3.join(this.stateDir, "meta.json");
4452
4688
  try {
4453
4689
  const raw = await this.readStorageSecureFile(metaPath);
4454
4690
  const parsed = JSON.parse(raw);
@@ -4482,7 +4718,7 @@ ${memory.content}
4482
4718
  }
4483
4719
  async saveMeta(state) {
4484
4720
  await this.ensureDirectories();
4485
- const metaPath = path2.join(this.stateDir, "meta.json");
4721
+ const metaPath = path3.join(this.stateDir, "meta.json");
4486
4722
  await this.writeStorageSecureFile(metaPath, JSON.stringify(state, null, 2));
4487
4723
  }
4488
4724
  async appendMemoryActionEvents(events) {
@@ -4515,99 +4751,15 @@ ${memory.content}
4515
4751
  await this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, payload);
4516
4752
  return events.length;
4517
4753
  }
4518
- /**
4519
- * Append a batch of `BUFFER_SURPRISE` telemetry events (issue #563 PR 3).
4520
- *
4521
- * Each event records a single buffer flush decision driven by the
4522
- * surprise gate. The ledger is consumed by
4523
- * `reportBufferSurpriseDistribution` (Doctor report) and by downstream
4524
- * benchmark analysis. This method is fire-and-forget by contract:
4525
- * callers log but do not fail the hot path if the append throws.
4526
- */
4527
4754
  async appendBufferSurpriseEvents(events) {
4528
- if (events.length === 0) return 0;
4529
- await this.ensureDirectories();
4530
- const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4531
- const payload = events.map((event) => {
4532
- const normalized = {
4533
- ...event,
4534
- event: "BUFFER_SURPRISE",
4535
- timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : nowIso
4536
- };
4537
- return `${JSON.stringify(normalized)}
4538
- `;
4539
- }).join("");
4540
- await this.appendStorageSecureFile(this.bufferSurpriseLedgerPath, payload);
4541
- return events.length;
4755
+ return this.memoryReadStore.appendBufferSurpriseEvents(
4756
+ events
4757
+ );
4542
4758
  }
4543
- /**
4544
- * Read the buffer-surprise ledger, most recent rows last.
4545
- *
4546
- * `limit` bounds the number of **valid rows** returned (not the
4547
- * number of raw lines parsed). We parse every row, discard malformed
4548
- * ones, then take the tail — so a partial/truncated trailing line
4549
- * (the common failure mode after an interrupted append) cannot hide
4550
- * otherwise-valid recent data above it.
4551
- *
4552
- * Non-positive / non-integer / non-finite limits return `[]` rather
4553
- * than the entire file, matching the other ledger readers in this
4554
- * class and protecting against `slice(-0.5)` → `slice(-0)` silently
4555
- * devolving into an unbounded parse.
4556
- *
4557
- * # Performance note
4558
- *
4559
- * For very large ledgers (issue #563 follow-up), a tail-first reader
4560
- * would avoid parsing the full file when only a recent window is
4561
- * needed. We keep the full-scan implementation here because:
4562
- *
4563
- * - the ledger is opt-in (flag off by default), so early deployments
4564
- * accumulate rows slowly;
4565
- * - telemetry rows are small (~200 bytes), so even 100k rows parse
4566
- * in well under a second;
4567
- * - the governance archive/cleanup flow can trim the ledger when
4568
- * size becomes a concern, reusing the existing maintenance hooks.
4569
- *
4570
- * Swap to a chunked tail-reader if production logs show this is a
4571
- * hot path — leaving that work for a follow-up keeps this PR scoped
4572
- * to correctness, not optimization.
4573
- */
4574
4759
  async readBufferSurpriseEvents(options = {}) {
4575
- let raw;
4576
- try {
4577
- raw = await this.readStorageSecureFile(this.bufferSurpriseLedgerPath);
4578
- } catch (err) {
4579
- if (err instanceof SecureStoreLockedError) throw err;
4580
- const code = err.code;
4581
- if (code === "ENOENT") return [];
4582
- throw err;
4583
- }
4584
- let effectiveLimit = null;
4585
- if (options.limit !== void 0) {
4586
- if (typeof options.limit !== "number" || !Number.isFinite(options.limit) || options.limit <= 0) {
4587
- return [];
4588
- }
4589
- const floored = Math.floor(options.limit);
4590
- if (floored <= 0) return [];
4591
- effectiveLimit = floored;
4592
- }
4593
- const lines = raw.split("\n");
4594
- const events = [];
4595
- for (const line of lines) {
4596
- const trimmed = line.trim();
4597
- if (trimmed.length === 0) continue;
4598
- try {
4599
- const parsed = JSON.parse(trimmed);
4600
- if (isValidBufferSurpriseEvent(parsed)) {
4601
- events.push(parsed);
4602
- }
4603
- } catch {
4604
- }
4605
- }
4606
- events.sort(
4607
- (left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp)
4760
+ return this.memoryReadStore.readBufferSurpriseEvents(
4761
+ options
4608
4762
  );
4609
- if (effectiveLimit === null) return events;
4610
- return events.slice(-effectiveLimit);
4611
4763
  }
4612
4764
  async appendBehaviorSignals(events) {
4613
4765
  if (events.length === 0) return 0;
@@ -4652,7 +4804,7 @@ ${memory.content}
4652
4804
  async appendReextractJobs(events) {
4653
4805
  if (events.length === 0) return 0;
4654
4806
  await this.ensureDirectories();
4655
- const filePath = path2.join(this.stateDir, "reextract-jobs.jsonl");
4807
+ const filePath = path3.join(this.stateDir, "reextract-jobs.jsonl");
4656
4808
  const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n";
4657
4809
  try {
4658
4810
  await this.appendStorageSecureFile(filePath, lines);
@@ -4665,7 +4817,7 @@ ${memory.content}
4665
4817
  }
4666
4818
  async readReextractJobs(limit = 200) {
4667
4819
  const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(1e3, Math.floor(limit))) : 200;
4668
- const filePath = path2.join(this.stateDir, "reextract-jobs.jsonl");
4820
+ const filePath = path3.join(this.stateDir, "reextract-jobs.jsonl");
4669
4821
  try {
4670
4822
  const raw = await this.readStorageSecureFile(filePath);
4671
4823
  const lines = raw.split("\n").filter((line) => line.trim().length > 0);
@@ -4852,53 +5004,9 @@ ${memory.content}
4852
5004
  return true;
4853
5005
  }
4854
5006
  async readCompressionGuidelineStateFile(filePath) {
4855
- const isFiniteNonNegativeInteger = (value) => typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0;
4856
- const isValidActionSummary = (value) => {
4857
- if (!value || typeof value !== "object") return false;
4858
- const summary = value;
4859
- return typeof summary.action === "string" && isFiniteNonNegativeInteger(summary.total) && summary.outcomes !== null && typeof summary.outcomes === "object" && isFiniteNonNegativeInteger(summary.outcomes.applied) && isFiniteNonNegativeInteger(summary.outcomes.skipped) && isFiniteNonNegativeInteger(summary.outcomes.failed) && summary.quality !== null && typeof summary.quality === "object" && isFiniteNonNegativeInteger(summary.quality.good) && isFiniteNonNegativeInteger(summary.quality.poor) && isFiniteNonNegativeInteger(summary.quality.unknown);
4860
- };
4861
- const isValidRuleUpdate = (value) => {
4862
- if (!value || typeof value !== "object") return false;
4863
- const rule = value;
4864
- return typeof rule.action === "string" && typeof rule.delta === "number" && Number.isFinite(rule.delta) && (rule.direction === "increase" || rule.direction === "decrease" || rule.direction === "hold") && (rule.confidence === "low" || rule.confidence === "medium" || rule.confidence === "high") && Array.isArray(rule.notes) && rule.notes.every((note) => typeof note === "string");
4865
- };
4866
- try {
4867
- const raw = await this.readStorageSecureFile(filePath);
4868
- const parsed = JSON.parse(raw);
4869
- const sourceWindow = parsed?.sourceWindow;
4870
- const eventCounts = parsed?.eventCounts;
4871
- const activationState = parsed?.activationState === "draft" || parsed?.activationState === "active" ? parsed.activationState : void 0;
4872
- const contentHash = typeof parsed?.contentHash === "string" && parsed.contentHash.length > 0 ? parsed.contentHash : void 0;
4873
- const actionSummaries = Array.isArray(parsed?.actionSummaries) ? parsed.actionSummaries.filter(isValidActionSummary) : void 0;
4874
- const ruleUpdates = Array.isArray(parsed?.ruleUpdates) ? parsed.ruleUpdates.filter(isValidRuleUpdate) : void 0;
4875
- if (!isFiniteNonNegativeInteger(parsed?.version) || typeof parsed?.updatedAt !== "string" || parsed.updatedAt.length === 0 || !sourceWindow || typeof sourceWindow.from !== "string" || sourceWindow.from.length === 0 || typeof sourceWindow.to !== "string" || sourceWindow.to.length === 0 || !eventCounts || !isFiniteNonNegativeInteger(eventCounts.total) || !isFiniteNonNegativeInteger(eventCounts.applied) || !isFiniteNonNegativeInteger(eventCounts.skipped) || !isFiniteNonNegativeInteger(eventCounts.failed) || !isFiniteNonNegativeInteger(parsed?.guidelineVersion)) {
4876
- return null;
4877
- }
4878
- return {
4879
- version: parsed.version,
4880
- updatedAt: parsed.updatedAt,
4881
- sourceWindow: {
4882
- from: sourceWindow.from,
4883
- to: sourceWindow.to
4884
- },
4885
- eventCounts: {
4886
- total: eventCounts.total,
4887
- applied: eventCounts.applied,
4888
- skipped: eventCounts.skipped,
4889
- failed: eventCounts.failed
4890
- },
4891
- guidelineVersion: parsed.guidelineVersion,
4892
- ...contentHash ? { contentHash } : {},
4893
- ...activationState ? { activationState } : {},
4894
- ...actionSummaries ? { actionSummaries } : {},
4895
- ...ruleUpdates ? { ruleUpdates } : {}
4896
- };
4897
- } catch (err) {
4898
- if (err instanceof SecureStoreLockedError) throw err;
4899
- if (!isErrnoCode(err, "ENOENT")) throw err;
4900
- return null;
4901
- }
5007
+ return this.memoryReadStore.readCompressionGuidelineStateFile(
5008
+ filePath
5009
+ );
4902
5010
  }
4903
5011
  async writeIdentityAnchor(content) {
4904
5012
  await this.ensureDirectories();
@@ -4919,7 +5027,7 @@ ${memory.content}
4919
5027
  const date = nowIso.slice(0, 10);
4920
5028
  const id = this.generateId("incident");
4921
5029
  const incident = createContinuityIncidentRecord(id, input, nowIso);
4922
- const filePath = path2.join(this.identityIncidentsDir, `${date}-${id}.md`);
5030
+ const filePath = path3.join(this.identityIncidentsDir, `${date}-${id}.md`);
4923
5031
  await this.writeStorageSecureFile(filePath, serializeContinuityIncident(incident));
4924
5032
  return { ...incident, filePath };
4925
5033
  }
@@ -4932,7 +5040,7 @@ ${memory.content}
4932
5040
  const incidents = [];
4933
5041
  for (const file of candidates) {
4934
5042
  if (incidents.length >= cappedLimit) break;
4935
- const filePath = path2.join(this.identityIncidentsDir, file);
5043
+ const filePath = path3.join(this.identityIncidentsDir, file);
4936
5044
  try {
4937
5045
  const raw = await this.readStorageSecureFile(filePath);
4938
5046
  const parsed = parseContinuityIncident(raw);
@@ -4963,7 +5071,7 @@ ${memory.content}
4963
5071
  await this.ensureDirectories();
4964
5072
  const safeKey = this.sanitizeIdentityAuditKey(key);
4965
5073
  const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
4966
- const filePath = path2.join(dir, `${safeKey}.md`);
5074
+ const filePath = path3.join(dir, `${safeKey}.md`);
4967
5075
  await this.writeStorageSecureFile(filePath, content);
4968
5076
  return filePath;
4969
5077
  }
@@ -4971,7 +5079,7 @@ ${memory.content}
4971
5079
  try {
4972
5080
  const safeKey = this.sanitizeIdentityAuditKey(key);
4973
5081
  const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
4974
- return await this.readStorageSecureFile(path2.join(dir, `${safeKey}.md`));
5082
+ return await this.readStorageSecureFile(path3.join(dir, `${safeKey}.md`));
4975
5083
  } catch (err) {
4976
5084
  if (err instanceof Error && err.message === "Invalid identity audit key") return null;
4977
5085
  if (!isErrnoCode(err, "ENOENT")) throw err;
@@ -5018,7 +5126,7 @@ ${memory.content}
5018
5126
  return `${prefix}-${ts}-${rand}`;
5019
5127
  }
5020
5128
  async readContinuityIncidentFileNames() {
5021
- const files = await readdir2(this.identityIncidentsDir);
5129
+ const files = await readdir3(this.identityIncidentsDir);
5022
5130
  return files.filter((file) => file.endsWith(".md")).sort().reverse();
5023
5131
  }
5024
5132
  async readContinuityIncidentFile(filePath) {
@@ -5035,12 +5143,12 @@ ${memory.content}
5035
5143
  const fileNames = await this.readContinuityIncidentFileNames();
5036
5144
  const directMatch = fileNames.find((name) => name.endsWith(`-${id}.md`));
5037
5145
  if (directMatch) {
5038
- const directPath = path2.join(this.identityIncidentsDir, directMatch);
5146
+ const directPath = path3.join(this.identityIncidentsDir, directMatch);
5039
5147
  const parsed = await this.readContinuityIncidentFile(directPath);
5040
5148
  if (parsed?.id === id) return directPath;
5041
5149
  }
5042
5150
  for (const fileName of fileNames) {
5043
- const filePath = path2.join(this.identityIncidentsDir, fileName);
5151
+ const filePath = path3.join(this.identityIncidentsDir, fileName);
5044
5152
  const parsed = await this.readContinuityIncidentFile(filePath);
5045
5153
  if (parsed?.id === id) return filePath;
5046
5154
  }
@@ -5070,43 +5178,16 @@ ${question}
5070
5178
 
5071
5179
  **Context:** ${context}
5072
5180
  `;
5073
- const filePath = path2.join(this.questionsDir, `${id}.md`);
5181
+ const filePath = path3.join(this.questionsDir, `${id}.md`);
5074
5182
  await this.writeStorageSecureFile(filePath, content);
5075
5183
  log.debug(`wrote question ${id} to ${filePath}`);
5076
5184
  this.invalidateQuestionsCache();
5077
5185
  return id;
5078
5186
  }
5079
5187
  async readQuestions(opts) {
5080
- const cacheKey = this.questionsDir;
5081
- const cached = _StorageManager.questionsCache.get(cacheKey);
5082
- if (cached && Date.now() - cached.loadedAt < _StorageManager.QUESTIONS_CACHE_TTL_MS) {
5083
- try {
5084
- const dirStat = await stat(this.questionsDir);
5085
- if (dirStat.mtimeMs <= cached.loadedAt) {
5086
- const all = cached.questions;
5087
- return opts?.unresolvedOnly ? all.filter((q) => !q.resolved) : all;
5088
- }
5089
- } catch {
5090
- }
5091
- }
5092
- try {
5093
- const files = await readdir2(this.questionsDir);
5094
- const questions = [];
5095
- for (const file of files) {
5096
- if (!file.endsWith(".md")) continue;
5097
- const filePath = path2.join(this.questionsDir, file);
5098
- const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
5099
- const parsed = this.parseQuestionFile(raw, filePath);
5100
- if (parsed) {
5101
- questions.push(parsed);
5102
- }
5103
- }
5104
- const sorted = questions.sort((a, b) => b.priority - a.priority);
5105
- _StorageManager.questionsCache.set(cacheKey, { questions: sorted, loadedAt: Date.now() });
5106
- return opts?.unresolvedOnly ? sorted.filter((q) => !q.resolved) : sorted;
5107
- } catch {
5108
- return [];
5109
- }
5188
+ return this.memoryReadStore.readQuestions(
5189
+ opts
5190
+ );
5110
5191
  }
5111
5192
  /** Invalidate the questions cache (call after writing a question). */
5112
5193
  invalidateQuestionsCache() {
@@ -5117,7 +5198,7 @@ ${question}
5117
5198
  if (!match) return null;
5118
5199
  const frontmatterStr = match[1];
5119
5200
  const body = match[2].trim();
5120
- const id = this.extractFrontmatterValue(frontmatterStr, "id") ?? path2.basename(filePath, ".md");
5201
+ const id = this.extractFrontmatterValue(frontmatterStr, "id") ?? path3.basename(filePath, ".md");
5121
5202
  const created = this.extractFrontmatterValue(frontmatterStr, "created") ?? "";
5122
5203
  const priority = parseFloat(
5123
5204
  this.extractFrontmatterValue(frontmatterStr, "priority") ?? "0.5"
@@ -5179,11 +5260,11 @@ ${question}
5179
5260
  } catch {
5180
5261
  }
5181
5262
  const hygiene = opts?.hygiene;
5182
- const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(path2.basename(identityPath));
5263
+ const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(path3.basename(identityPath));
5183
5264
  if (rotateEnabled) {
5184
5265
  const maxBytes = hygiene.rotateMaxBytes;
5185
5266
  if (existing.length > maxBytes) {
5186
- const archiveDir = path2.join(workspaceDir, hygiene.archiveDir);
5267
+ const archiveDir = path3.join(workspaceDir, hygiene.archiveDir);
5187
5268
  const { newContent } = await rotateMarkdownFileToArchive({
5188
5269
  filePath: identityPath,
5189
5270
  archiveDir,
@@ -5277,7 +5358,7 @@ ${reflection}
5277
5358
  * Deduplicates by target+label.
5278
5359
  */
5279
5360
  async addEntityRelationship(name, rel) {
5280
- const filePath = path2.join(this.entitiesDir, `${name}.md`);
5361
+ const filePath = path3.join(this.entitiesDir, `${name}.md`);
5281
5362
  let entity;
5282
5363
  try {
5283
5364
  const content = await this.readStorageSecureFile(filePath);
@@ -5302,7 +5383,7 @@ ${reflection}
5302
5383
  * Prepends to the beginning, prunes oldest entries beyond maxEntries.
5303
5384
  */
5304
5385
  async addEntityActivity(name, entry, maxEntries) {
5305
- const filePath = path2.join(this.entitiesDir, `${name}.md`);
5386
+ const filePath = path3.join(this.entitiesDir, `${name}.md`);
5306
5387
  let entity;
5307
5388
  try {
5308
5389
  const content = await this.readStorageSecureFile(filePath);
@@ -5325,7 +5406,7 @@ ${reflection}
5325
5406
  * Add an alias to an entity file. Deduplicates.
5326
5407
  */
5327
5408
  async addEntityAlias(name, alias) {
5328
- const filePath = path2.join(this.entitiesDir, `${name}.md`);
5409
+ const filePath = path3.join(this.entitiesDir, `${name}.md`);
5329
5410
  let entity;
5330
5411
  try {
5331
5412
  const content = await this.readStorageSecureFile(filePath);
@@ -5356,7 +5437,7 @@ ${reflection}
5356
5437
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
5357
5438
  let synthesisTimelineCount;
5358
5439
  try {
5359
- const filePath = path2.join(this.entitiesDir, `${name}.md`);
5440
+ const filePath = path3.join(this.entitiesDir, `${name}.md`);
5360
5441
  const content = await this.readStorageSecureFile(filePath);
5361
5442
  synthesisTimelineCount = parseEntityFile(content, this.entitySchemas).timeline.length;
5362
5443
  } catch (err) {
@@ -5439,7 +5520,7 @@ ${reflection}
5439
5520
  if (!raw) continue;
5440
5521
  const serialized = serializeEntityFile(parseEntityFile(raw, this.entitySchemas), this.entitySchemas);
5441
5522
  if (raw.trimEnd() === serialized.trimEnd()) continue;
5442
- await this.writeStorageSecureFile(path2.join(this.entitiesDir, `${entityName}.md`), serialized);
5523
+ await this.writeStorageSecureFile(path3.join(this.entitiesDir, `${entityName}.md`), serialized);
5443
5524
  migrated += 1;
5444
5525
  }
5445
5526
  if (migrated > 0) {
@@ -5582,7 +5663,7 @@ ${memory.content}
5582
5663
  const filePaths = await this.collectActiveMemoryPaths();
5583
5664
  const foundIds = /* @__PURE__ */ new Set();
5584
5665
  for (const filePath of filePaths) {
5585
- const basename = path2.basename(filePath, ".md");
5666
+ const basename = path3.basename(filePath, ".md");
5586
5667
  if (wantedIds.has(basename)) {
5587
5668
  foundIds.add(basename);
5588
5669
  if (foundIds.size === wantedIds.size) break;
@@ -5772,14 +5853,14 @@ Reason: ${reason}`, {
5772
5853
  // Memory Summarization (Phase 4A)
5773
5854
  // ---------------------------------------------------------------------------
5774
5855
  get summariesDir() {
5775
- return path2.join(this.baseDir, "summaries");
5856
+ return path3.join(this.baseDir, "summaries");
5776
5857
  }
5777
5858
  /**
5778
5859
  * Write a memory summary.
5779
5860
  */
5780
5861
  async writeSummary(summary) {
5781
5862
  await mkdir(this.summariesDir, { recursive: true });
5782
- const filePath = path2.join(this.summariesDir, `${summary.id}.json`);
5863
+ const filePath = path3.join(this.summariesDir, `${summary.id}.json`);
5783
5864
  await this.writeStorageSecureFile(filePath, JSON.stringify(summary, null, 2));
5784
5865
  log.debug(`wrote summary ${summary.id}`);
5785
5866
  }
@@ -5788,11 +5869,11 @@ Reason: ${reason}`, {
5788
5869
  */
5789
5870
  async readSummaries() {
5790
5871
  try {
5791
- const files = await readdir2(this.summariesDir);
5872
+ const files = await readdir3(this.summariesDir);
5792
5873
  const summaries = [];
5793
5874
  for (const file of files) {
5794
5875
  if (!file.endsWith(".json")) continue;
5795
- const filePath = path2.join(this.summariesDir, file);
5876
+ const filePath = path3.join(this.summariesDir, file);
5796
5877
  const raw = await this.readStorageSecureFile(filePath);
5797
5878
  summaries.push(JSON.parse(raw));
5798
5879
  }
@@ -5853,7 +5934,7 @@ ${memory.content}
5853
5934
  * Save topic scores to meta.json.
5854
5935
  */
5855
5936
  async saveTopics(topics) {
5856
- const metaPath = path2.join(this.stateDir, "topics.json");
5937
+ const metaPath = path3.join(this.stateDir, "topics.json");
5857
5938
  await mkdir(this.stateDir, { recursive: true });
5858
5939
  await this.writeStorageSecureFile(metaPath, JSON.stringify({ topics, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
5859
5940
  log.debug(`saved ${topics.length} topic scores`);
@@ -5862,7 +5943,7 @@ ${memory.content}
5862
5943
  * Load topic scores from meta.json.
5863
5944
  */
5864
5945
  async loadTopics() {
5865
- const metaPath = path2.join(this.stateDir, "topics.json");
5946
+ const metaPath = path3.join(this.stateDir, "topics.json");
5866
5947
  try {
5867
5948
  const raw = await this.readStorageSecureFile(metaPath);
5868
5949
  return JSON.parse(raw);
@@ -5941,6 +6022,9 @@ ${memory.content}
5941
6022
 
5942
6023
  export {
5943
6024
  selfDeps,
6025
+ parseFrontmatterStringValue,
6026
+ parseFrontmatter,
6027
+ normalizeFrontmatterForPath,
5944
6028
  normalizeEntityName,
5945
6029
  ContentHashIndex,
5946
6030
  normalizeAttributePairs,
@@ -5953,6 +6037,7 @@ export {
5953
6037
  parseEntityFile,
5954
6038
  serializeEntityFile,
5955
6039
  buildEntitySchemaCacheKey,
5956
- StorageManager
6040
+ isValidBufferSurpriseEvent,
6041
+ StorageManager3 as StorageManager
5957
6042
  };
5958
- //# sourceMappingURL=chunk-XBM5DG4O.js.map
6043
+ //# sourceMappingURL=chunk-5PHPNUZN.js.map