@remnic/core 9.6.0 → 9.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/dist/access-admin-ops-surface.d.ts +7 -8
  2. package/dist/access-admin-ops-surface.js +8 -8
  3. package/dist/access-boundary.d.ts +9 -8
  4. package/dist/access-boundary.js +9 -9
  5. package/dist/access-cli.js +40 -40
  6. package/dist/access-http.d.ts +47 -16
  7. package/dist/access-http.js +13 -13
  8. package/dist/access-identity-continuity-surface.d.ts +6 -7
  9. package/dist/access-identity-continuity-surface.js +8 -8
  10. package/dist/access-lcm-surface.d.ts +7 -8
  11. package/dist/access-lcm-surface.js +8 -8
  12. package/dist/access-mcp.d.ts +13 -8
  13. package/dist/access-mcp.js +12 -12
  14. package/dist/access-observe-write-surface.d.ts +7 -8
  15. package/dist/access-observe-write-surface.js +8 -8
  16. package/dist/access-operations-batch.js +10 -10
  17. package/dist/access-operations.d.ts +8 -9
  18. package/dist/access-operations.js +11 -11
  19. package/dist/access-recall-surface.d.ts +7 -8
  20. package/dist/access-recall-surface.js +8 -8
  21. package/dist/access-schema.d.ts +6 -6
  22. package/dist/{access-service-CXJaql3A.d.ts → access-service-B_sc1w_k.d.ts} +9 -9
  23. package/dist/access-service.d.ts +7 -8
  24. package/dist/access-service.js +8 -8
  25. package/dist/access-surface-catalog.d.ts +7 -8
  26. package/dist/action-confidence.d.ts +1 -2
  27. package/dist/active-memory-bridge.d.ts +1 -2
  28. package/dist/active-recall.d.ts +1 -2
  29. package/dist/behavior-learner.d.ts +1 -2
  30. package/dist/behavior-signals.d.ts +1 -2
  31. package/dist/bootstrap.d.ts +6 -7
  32. package/dist/briefing.d.ts +2 -3
  33. package/dist/briefing.js +3 -3
  34. package/dist/buffer-surprise-report.d.ts +1 -2
  35. package/dist/buffer.d.ts +2 -3
  36. package/dist/buffer.js +1 -1
  37. package/dist/bulk-import/index.d.ts +3 -3
  38. package/dist/bulk-import/index.js +2 -2
  39. package/dist/calibration.d.ts +1 -2
  40. package/dist/capabilities.d.ts +1 -2
  41. package/dist/{catalog-C9cu67Ig.d.ts → catalog-9WanSGtt.d.ts} +1 -1
  42. package/dist/causal-behavior.d.ts +1 -2
  43. package/dist/causal-consolidation.d.ts +1 -2
  44. package/dist/causal-consolidation.js +4 -4
  45. package/dist/causal-trajectory-graph.d.ts +1 -2
  46. package/dist/{chunk-PNLFN2PZ.js → chunk-23G3T2Q5.js} +2 -2
  47. package/dist/{chunk-SIRPLF54.js → chunk-2K3OSLHE.js} +2 -2
  48. package/dist/{chunk-JS2HKKOK.js → chunk-3AC5UYKI.js} +1 -1
  49. package/dist/{chunk-JS2HKKOK.js.map → chunk-3AC5UYKI.js.map} +1 -1
  50. package/dist/{chunk-FKCRGLRD.js → chunk-4WRKKRTD.js} +266 -160
  51. package/dist/chunk-4WRKKRTD.js.map +1 -0
  52. package/dist/{chunk-LQHDIS7L.js → chunk-54BGIBCR.js} +2 -2
  53. package/dist/{chunk-PVUJ6WVV.js → chunk-5MH5NF3U.js} +5 -5
  54. package/dist/{chunk-PCLIWBGD.js → chunk-7ARV32PI.js} +8 -6
  55. package/dist/chunk-7ARV32PI.js.map +1 -0
  56. package/dist/{chunk-Y7UNNAZD.js → chunk-7ZYBDQ7V.js} +2 -2
  57. package/dist/{chunk-2PRQG7PV.js → chunk-AJVLDJU6.js} +1 -1
  58. package/dist/chunk-AJVLDJU6.js.map +1 -0
  59. package/dist/{chunk-LR5DZ56F.js → chunk-AXALUFAH.js} +2 -2
  60. package/dist/{chunk-AYZJID4S.js → chunk-B5GBWBCZ.js} +2 -2
  61. package/dist/{chunk-TLUDFPZL.js → chunk-CAZ3Z5M6.js} +2 -2
  62. package/dist/{chunk-RGK27ELN.js → chunk-DEKIXXIG.js} +2 -2
  63. package/dist/{chunk-2KK5WNQB.js → chunk-DQ42IQSK.js} +2 -2
  64. package/dist/{chunk-UFUKHGZD.js → chunk-F4I5NYBI.js} +89 -33
  65. package/dist/chunk-F4I5NYBI.js.map +1 -0
  66. package/dist/{chunk-HOJZMQ4J.js → chunk-FGDKJJZS.js} +2 -2
  67. package/dist/{chunk-ABP6QMVM.js → chunk-FQ7HAWWC.js} +2 -2
  68. package/dist/{chunk-CYWA2CXR.js → chunk-FVTLSFWU.js} +2 -2
  69. package/dist/{chunk-JJEJJ7RK.js → chunk-FXZTL7F7.js} +2 -2
  70. package/dist/{chunk-LADFWYHY.js → chunk-GOEV7CYF.js} +6 -6
  71. package/dist/chunk-GOEV7CYF.js.map +1 -0
  72. package/dist/{chunk-DANV2HTH.js → chunk-GQGDRLDM.js} +3 -3
  73. package/dist/{chunk-7F7Z6MOS.js → chunk-H4CKS5B2.js} +2 -2
  74. package/dist/{chunk-SVOHGUMI.js → chunk-H4FHR4UM.js} +174 -197
  75. package/dist/chunk-H4FHR4UM.js.map +1 -0
  76. package/dist/{chunk-CKITWVWJ.js → chunk-HSSERO3E.js} +2 -2
  77. package/dist/{chunk-23JFRB73.js → chunk-JODHPNLN.js} +2 -2
  78. package/dist/{chunk-DS7MM2ES.js → chunk-JZKR6ZAV.js} +2 -2
  79. package/dist/{chunk-BEM4D2QG.js → chunk-KJIJNLXT.js} +3 -3
  80. package/dist/{chunk-JSNVWNGX.js → chunk-KVSVVKHA.js} +2 -2
  81. package/dist/{chunk-4GNEDXDI.js → chunk-M67TLMP6.js} +2 -2
  82. package/dist/chunk-M67TLMP6.js.map +1 -0
  83. package/dist/{chunk-MC4FJXPA.js → chunk-MMACM5G2.js} +2 -2
  84. package/dist/{chunk-OUELRE5E.js → chunk-NOUJNHWG.js} +1 -1
  85. package/dist/chunk-NOUJNHWG.js.map +1 -0
  86. package/dist/{chunk-3XIY7MDQ.js → chunk-O6YI4CVI.js} +2 -2
  87. package/dist/{chunk-F53UF7CI.js → chunk-QLVLW7UD.js} +6 -2
  88. package/dist/{chunk-F53UF7CI.js.map → chunk-QLVLW7UD.js.map} +1 -1
  89. package/dist/{chunk-XCL57M6Q.js → chunk-RRKPLLCC.js} +21 -20
  90. package/dist/chunk-RRKPLLCC.js.map +1 -0
  91. package/dist/{chunk-72WG5QKN.js → chunk-SDAXPXER.js} +2 -2
  92. package/dist/{chunk-4LV23CHK.js → chunk-VHJARF5R.js} +2 -2
  93. package/dist/{chunk-H2DXDQCQ.js → chunk-VRJG3HIZ.js} +46 -42
  94. package/dist/chunk-VRJG3HIZ.js.map +1 -0
  95. package/dist/{chunk-6MMONPHM.js → chunk-W2AHSCKY.js} +2 -2
  96. package/dist/{chunk-P454KXD6.js → chunk-YAWJMR3Y.js} +21 -21
  97. package/dist/{chunk-K5LLP4CY.js → chunk-ZGPDSEKG.js} +20 -9
  98. package/dist/chunk-ZGPDSEKG.js.map +1 -0
  99. package/dist/{cli-CqdPA6z9.d.ts → cli-CnRsWREp.d.ts} +4 -5
  100. package/dist/cli.d.ts +9 -10
  101. package/dist/cli.js +32 -32
  102. package/dist/compounding/engine.d.ts +2 -3
  103. package/dist/compounding/engine.js +3 -3
  104. package/dist/compounding/preference-consolidator.d.ts +1 -2
  105. package/dist/compression-optimizer.d.ts +1 -2
  106. package/dist/config.d.ts +1 -2
  107. package/dist/connectors/codex-materialize-runner.d.ts +1 -2
  108. package/dist/connectors/codex-materialize-runner.js +3 -3
  109. package/dist/connectors/codex-materialize.d.ts +1 -2
  110. package/dist/connectors/index.d.ts +1 -2
  111. package/dist/connectors/index.js +3 -3
  112. package/dist/consolidation-provenance-check.d.ts +2 -3
  113. package/dist/consolidation-undo.d.ts +2 -3
  114. package/dist/contradiction/index.d.ts +3 -4
  115. package/dist/conversation-index/backend.d.ts +1 -2
  116. package/dist/conversation-index/chunker.d.ts +1 -2
  117. package/dist/conversation-index/faiss-adapter.d.ts +1 -2
  118. package/dist/conversation-index/indexer.d.ts +1 -2
  119. package/dist/conversation-index/search.d.ts +1 -2
  120. package/dist/day-summary.d.ts +1 -2
  121. package/dist/delinearize.d.ts +1 -2
  122. package/dist/direct-answer-wiring.d.ts +1 -2
  123. package/dist/direct-answer.d.ts +1 -2
  124. package/dist/embedding-fallback.d.ts +1 -2
  125. package/dist/enrichment/index.d.ts +1 -2
  126. package/dist/entity-retrieval.d.ts +2 -3
  127. package/dist/entity-retrieval.js +3 -3
  128. package/dist/entity-schema.d.ts +1 -2
  129. package/dist/explicit-capture.d.ts +8 -7
  130. package/dist/explicit-capture.js +1 -1
  131. package/dist/extraction-faithfulness.d.ts +1 -2
  132. package/dist/extraction-judge-telemetry.d.ts +1 -2
  133. package/dist/extraction-judge-training.d.ts +1 -2
  134. package/dist/extraction-judge.d.ts +1 -2
  135. package/dist/extraction.d.ts +1 -2
  136. package/dist/fallback-llm.d.ts +1 -2
  137. package/dist/graph-dashboard-diff.d.ts +1 -2
  138. package/dist/graph-dashboard-key.d.ts +1 -2
  139. package/dist/graph-dashboard-parser.d.ts +1 -2
  140. package/dist/graph-edge-reinforcement.d.ts +1 -2
  141. package/dist/graph-snapshot.d.ts +1 -2
  142. package/dist/graph.d.ts +1 -2
  143. package/dist/identity-continuity.d.ts +1 -2
  144. package/dist/importance.d.ts +1 -2
  145. package/dist/importers/index.d.ts +1 -1
  146. package/dist/importers/index.js +2 -2
  147. package/dist/index.d.ts +14 -15
  148. package/dist/index.js +40 -40
  149. package/dist/intent.d.ts +1 -2
  150. package/dist/lcm/engine.d.ts +1 -2
  151. package/dist/lcm/index.d.ts +1 -2
  152. package/dist/lcm/tools.d.ts +1 -2
  153. package/dist/lifecycle.d.ts +1 -2
  154. package/dist/live-connectors-runner.d.ts +1 -2
  155. package/dist/local-llm.d.ts +1 -2
  156. package/dist/local-model-endpoint.d.ts +1 -2
  157. package/dist/maintenance/memory-governance.d.ts +1 -2
  158. package/dist/maintenance/memory-governance.js +3 -3
  159. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  160. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  161. package/dist/mcp-memory-inspector-app.d.ts +7 -8
  162. package/dist/memory-action-policy.d.ts +1 -2
  163. package/dist/memory-cache.d.ts +1 -2
  164. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -2
  165. package/dist/memory-projection-store.d.ts +1 -2
  166. package/dist/memory-provenance.d.ts +1 -2
  167. package/dist/memory-worth-outcomes.d.ts +2 -3
  168. package/dist/models-json.d.ts +1 -2
  169. package/dist/namespaces/migrate.d.ts +3 -4
  170. package/dist/namespaces/migrate.js +4 -4
  171. package/dist/namespaces/principal.d.ts +1 -2
  172. package/dist/namespaces/search.d.ts +1 -2
  173. package/dist/namespaces/storage.d.ts +3 -4
  174. package/dist/namespaces/storage.js +3 -3
  175. package/dist/native-knowledge.d.ts +1 -2
  176. package/dist/operator-toolkit.d.ts +2 -3
  177. package/dist/operator-toolkit.js +7 -7
  178. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -3
  179. package/dist/orchestration/maintenance.d.ts +2 -3
  180. package/dist/orchestration/maintenance.js +5 -5
  181. package/dist/{orchestrator-CWFrT8AF.d.ts → orchestrator-NRLVXPpH.d.ts} +40 -15
  182. package/dist/orchestrator.d.ts +6 -7
  183. package/dist/orchestrator.js +40 -40
  184. package/dist/patterns-cli.d.ts +1 -2
  185. package/dist/{pipeline-DmqtvION.d.ts → pipeline-CiD86zIk.d.ts} +1 -1
  186. package/dist/policy-runtime.d.ts +1 -2
  187. package/dist/provenance.d.ts +1 -2
  188. package/dist/{qmd--fnPMK60.d.ts → qmd-Bef7DDuQ.d.ts} +1 -1
  189. package/dist/qmd-preflight.d.ts +2 -3
  190. package/dist/qmd-recall-cache.d.ts +1 -2
  191. package/dist/qmd.d.ts +2 -3
  192. package/dist/recall-disclosure-escalation.d.ts +1 -2
  193. package/dist/recall-explain-renderer.d.ts +1 -2
  194. package/dist/recall-explain-renderer.js +3 -3
  195. package/dist/recall-planner-llm.d.ts +1 -2
  196. package/dist/recall-state.d.ts +1 -2
  197. package/dist/recall-tag-filter.d.ts +1 -2
  198. package/dist/recall-timings.d.ts +1 -2
  199. package/dist/recall-xray-cli.d.ts +1 -2
  200. package/dist/recall-xray-cli.js +4 -4
  201. package/dist/recall-xray-renderer.d.ts +1 -2
  202. package/dist/recall-xray-renderer.js +3 -3
  203. package/dist/recall-xray.d.ts +1 -2
  204. package/dist/recall-xray.js +2 -2
  205. package/dist/replay/normalizers/chatgpt.d.ts +1 -0
  206. package/dist/replay/normalizers/chatgpt.js +3 -3
  207. package/dist/replay/normalizers/claude.d.ts +1 -0
  208. package/dist/replay/normalizers/claude.js +3 -3
  209. package/dist/replay/normalizers/openclaw.d.ts +1 -0
  210. package/dist/replay/normalizers/openclaw.js +3 -3
  211. package/dist/replay/normalizers/shared.d.ts +1 -0
  212. package/dist/replay/normalizers/shared.js +2 -2
  213. package/dist/replay/runner.d.ts +1 -0
  214. package/dist/replay/runner.js +2 -2
  215. package/dist/replay/types.d.ts +2 -1
  216. package/dist/replay/types.js +1 -1
  217. package/dist/resolve-auth-token.d.ts +1 -2
  218. package/dist/retrieval-agents.d.ts +2 -3
  219. package/dist/retrieval-tiers.d.ts +1 -2
  220. package/dist/routing/engine.d.ts +1 -2
  221. package/dist/routing/store.d.ts +1 -2
  222. package/dist/schemas.d.ts +44 -44
  223. package/dist/search/embed-helper.d.ts +1 -2
  224. package/dist/search/factory.d.ts +1 -2
  225. package/dist/search/index.d.ts +1 -2
  226. package/dist/search/lancedb-backend.d.ts +1 -2
  227. package/dist/search/meilisearch-backend.d.ts +1 -2
  228. package/dist/search/noop-backend.d.ts +1 -2
  229. package/dist/search/orama-backend.d.ts +1 -2
  230. package/dist/search/port.d.ts +1 -2
  231. package/dist/search/remote-backend.d.ts +1 -2
  232. package/dist/{semantic-DJR8_DMQ.d.ts → semantic-TddhF0y7.d.ts} +17 -1
  233. package/dist/{semantic-consolidation-O5lMZ4LG.d.ts → semantic-consolidation-BTShF2Fo.d.ts} +1 -1
  234. package/dist/semantic-consolidation.d.ts +2 -3
  235. package/dist/semantic-consolidation.js +4 -4
  236. package/dist/semantic-rule-promotion.js +3 -3
  237. package/dist/semantic-rule-verifier.d.ts +1 -2
  238. package/dist/semantic-rule-verifier.js +3 -3
  239. package/dist/session-observer-bands.d.ts +1 -2
  240. package/dist/session-observer-state.d.ts +1 -2
  241. package/dist/shared-context/manager.d.ts +3 -4
  242. package/dist/signal.d.ts +1 -2
  243. package/dist/{storage-C1maCNbc.d.ts → storage-DxPDAiU9.d.ts} +5 -1
  244. package/dist/storage.d.ts +2 -3
  245. package/dist/storage.js +2 -2
  246. package/dist/summarizer.d.ts +1 -2
  247. package/dist/summary-snapshot.d.ts +1 -2
  248. package/dist/temporal-supersession.d.ts +2 -3
  249. package/dist/temporal-validity.d.ts +1 -2
  250. package/dist/threading.d.ts +1 -2
  251. package/dist/tier-migration.d.ts +2 -3
  252. package/dist/tier-routing.d.ts +1 -2
  253. package/dist/topics.d.ts +1 -2
  254. package/dist/transcript.d.ts +1 -2
  255. package/dist/transfer/types.d.ts +12 -12
  256. package/dist/trust-score-stage.d.ts +1 -2
  257. package/dist/trust-score.d.ts +1 -2
  258. package/dist/{types-CGgcNWCG.d.ts → types-DDUDgd7T.d.ts} +93 -4
  259. package/dist/types.d.ts +1 -2
  260. package/dist/types.js +1 -1
  261. package/dist/utility-runtime.d.ts +1 -2
  262. package/dist/verified-recall.js +3 -3
  263. package/package.json +2 -2
  264. package/src/access-admin-ops-surface.ts +1 -0
  265. package/src/access-boundary.ts +2 -0
  266. package/src/access-http.test.ts +129 -0
  267. package/src/access-http.ts +92 -23
  268. package/src/access-mcp.ts +11 -2
  269. package/src/access-observe-write-surface.ts +87 -113
  270. package/src/access-operations-batch.ts +2 -2
  271. package/src/access-operations.ts +17 -16
  272. package/src/access-service.ts +11 -3
  273. package/src/admin/admin-surfaces.ts +2 -0
  274. package/src/buffer.ts +6 -1
  275. package/src/bulk-import/types.ts +2 -1
  276. package/src/coding/architecture-surfaces.test.ts +79 -5
  277. package/src/coding/architecture-surfaces.ts +7 -1
  278. package/src/coding/decision-surfaces.test.ts +1 -0
  279. package/src/coding/decision-surfaces.ts +4 -0
  280. package/src/connector-provenance-lifecycle.test.ts +618 -0
  281. package/src/connector-provenance.test.ts +219 -0
  282. package/src/dedup/semantic.test.ts +105 -0
  283. package/src/dedup/semantic.ts +69 -1
  284. package/src/explicit-capture.ts +21 -4
  285. package/src/orchestration/extraction-persist.ts +154 -6
  286. package/src/orchestration/extraction-run.ts +138 -228
  287. package/src/orchestration/persistence-index.ts +40 -5
  288. package/src/orchestration/turn-ingestion.ts +16 -9
  289. package/src/orchestrator-connector-dedup.test.ts +1082 -0
  290. package/src/orchestrator.ts +7 -11
  291. package/src/replay/types.ts +2 -1
  292. package/src/storage.ts +426 -553
  293. package/src/types.ts +11 -2
  294. package/src/utils/yaml-scalar.ts +20 -0
  295. package/dist/chunk-2PRQG7PV.js.map +0 -1
  296. package/dist/chunk-4GNEDXDI.js.map +0 -1
  297. package/dist/chunk-FKCRGLRD.js.map +0 -1
  298. package/dist/chunk-H2DXDQCQ.js.map +0 -1
  299. package/dist/chunk-K5LLP4CY.js.map +0 -1
  300. package/dist/chunk-LADFWYHY.js.map +0 -1
  301. package/dist/chunk-OUELRE5E.js.map +0 -1
  302. package/dist/chunk-PCLIWBGD.js.map +0 -1
  303. package/dist/chunk-SVOHGUMI.js.map +0 -1
  304. package/dist/chunk-UFUKHGZD.js.map +0 -1
  305. package/dist/chunk-XCL57M6Q.js.map +0 -1
  306. package/dist/types-gb_SNtjR.d.ts +0 -85
  307. /package/dist/{chunk-PNLFN2PZ.js.map → chunk-23G3T2Q5.js.map} +0 -0
  308. /package/dist/{chunk-SIRPLF54.js.map → chunk-2K3OSLHE.js.map} +0 -0
  309. /package/dist/{chunk-LQHDIS7L.js.map → chunk-54BGIBCR.js.map} +0 -0
  310. /package/dist/{chunk-PVUJ6WVV.js.map → chunk-5MH5NF3U.js.map} +0 -0
  311. /package/dist/{chunk-Y7UNNAZD.js.map → chunk-7ZYBDQ7V.js.map} +0 -0
  312. /package/dist/{chunk-LR5DZ56F.js.map → chunk-AXALUFAH.js.map} +0 -0
  313. /package/dist/{chunk-AYZJID4S.js.map → chunk-B5GBWBCZ.js.map} +0 -0
  314. /package/dist/{chunk-TLUDFPZL.js.map → chunk-CAZ3Z5M6.js.map} +0 -0
  315. /package/dist/{chunk-RGK27ELN.js.map → chunk-DEKIXXIG.js.map} +0 -0
  316. /package/dist/{chunk-2KK5WNQB.js.map → chunk-DQ42IQSK.js.map} +0 -0
  317. /package/dist/{chunk-HOJZMQ4J.js.map → chunk-FGDKJJZS.js.map} +0 -0
  318. /package/dist/{chunk-ABP6QMVM.js.map → chunk-FQ7HAWWC.js.map} +0 -0
  319. /package/dist/{chunk-CYWA2CXR.js.map → chunk-FVTLSFWU.js.map} +0 -0
  320. /package/dist/{chunk-JJEJJ7RK.js.map → chunk-FXZTL7F7.js.map} +0 -0
  321. /package/dist/{chunk-DANV2HTH.js.map → chunk-GQGDRLDM.js.map} +0 -0
  322. /package/dist/{chunk-7F7Z6MOS.js.map → chunk-H4CKS5B2.js.map} +0 -0
  323. /package/dist/{chunk-CKITWVWJ.js.map → chunk-HSSERO3E.js.map} +0 -0
  324. /package/dist/{chunk-23JFRB73.js.map → chunk-JODHPNLN.js.map} +0 -0
  325. /package/dist/{chunk-DS7MM2ES.js.map → chunk-JZKR6ZAV.js.map} +0 -0
  326. /package/dist/{chunk-BEM4D2QG.js.map → chunk-KJIJNLXT.js.map} +0 -0
  327. /package/dist/{chunk-JSNVWNGX.js.map → chunk-KVSVVKHA.js.map} +0 -0
  328. /package/dist/{chunk-MC4FJXPA.js.map → chunk-MMACM5G2.js.map} +0 -0
  329. /package/dist/{chunk-3XIY7MDQ.js.map → chunk-O6YI4CVI.js.map} +0 -0
  330. /package/dist/{chunk-72WG5QKN.js.map → chunk-SDAXPXER.js.map} +0 -0
  331. /package/dist/{chunk-4LV23CHK.js.map → chunk-VHJARF5R.js.map} +0 -0
  332. /package/dist/{chunk-6MMONPHM.js.map → chunk-W2AHSCKY.js.map} +0 -0
  333. /package/dist/{chunk-P454KXD6.js.map → chunk-YAWJMR3Y.js.map} +0 -0
package/src/storage.ts CHANGED
@@ -1,25 +1,41 @@
1
- import { access, lstat, readdir, readFile, realpath, stat, writeFile, mkdir, unlink, rename, appendFile, open } from "node:fs/promises";
1
+ import {
2
+ access,
3
+ lstat,
4
+ readdir,
5
+ readFile,
6
+ realpath,
7
+ stat,
8
+ writeFile,
9
+ mkdir,
10
+ unlink,
11
+ rename,
12
+ appendFile,
13
+ open,
14
+ } from "node:fs/promises";
2
15
  import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync, type Dirent } from "node:fs";
3
- import { createHash } from "node:crypto"
4
- import { normalizeContent, computeContentHash } from "./content-hash.js";;
16
+ import { createHash } from "node:crypto";
17
+ import { normalizeContent, computeContentHash } from "./content-hash.js";
5
18
  import path from "node:path";
6
19
  import { log } from "./logger.js";
7
20
  import { assertMemoryFrontmatterId, warnProjectionFallback } from "./storage-guards.js";
8
21
  import { MemoryReadStore } from "./storage/memory-read-store.js";
9
- import {
10
- readMaybeEncryptedLines,
11
- readMemoryActionEventRowsFromLines,
12
- } from "./storage/secure-line-reader.js";
22
+ import { readMaybeEncryptedLines, readMemoryActionEventRowsFromLines } from "./storage/secure-line-reader.js";
13
23
  import { selfDeps } from "./orchestration/self-deps.js";
14
24
  import { EntityStore } from "./storage/entity-store.js";
15
25
  import { IdentityContinuityStore } from "./storage/identity-continuity-store.js";
16
26
  import { isErrnoCode } from "./utils/errno.js";
17
27
  import { RECALL_FALLBACK_DIRS, getCategoryDir, categoryDirName } from "./utils/category-dir.js";
18
28
  import { assertPathInsideRoot } from "./utils/path-containment.js";
29
+ import { decodeYamlScalar } from "./utils/yaml-scalar.js";
19
30
  import { getCachedEntities, invalidateAllForDir, setCachedEntities } from "./memory-cache.js";
20
31
  import { rotateMarkdownFileToArchive } from "./hygiene.js";
21
32
  import { sanitizeMemoryContent } from "./sanitize.js";
22
- import { serializeProvenanceFields, parseProvenanceSources, parseProvenanceTag, reconcileProvenanceRead } from "./provenance.js";
33
+ import {
34
+ serializeProvenanceFields,
35
+ parseProvenanceSources,
36
+ parseProvenanceTag,
37
+ reconcileProvenanceRead,
38
+ } from "./provenance.js";
23
39
  import { serializeFaithfulnessFields, parseFaithfulnessField } from "./extraction-faithfulness.js";
24
40
  import { createVersion as createPageVersion, type VersioningConfig, type VersionTrigger } from "./page-versioning.js";
25
41
  import { isValidTranscriptDate, WEARABLES_DIR_NAME } from "./wearables/day-store.js";
@@ -104,11 +120,13 @@ import type {
104
120
  import { confidenceTier, SPECULATIVE_TTL_DAYS } from "./types.js";
105
121
  import {
106
122
  type ProjectedMemoryBrowseOptions,
107
- type ProjectedMemoryBrowsePage, markProjectedMemoryPathInvalid,
123
+ type ProjectedMemoryBrowsePage,
124
+ markProjectedMemoryPathInvalid,
108
125
  readProjectedMemoryState,
109
126
  readProjectedMemoryBrowse,
110
127
  readProjectedGovernanceRecord,
111
- readProjectedMemoryTimeline, updateProjectedMemoryPath,
128
+ readProjectedMemoryTimeline,
129
+ updateProjectedMemoryPath,
112
130
  } from "./memory-projection-store.js";
113
131
  import {
114
132
  inferMemoryStatus,
@@ -116,10 +134,7 @@ import {
116
134
  sortMemoryLifecycleEvents,
117
135
  toMemoryPathRel,
118
136
  } from "./memory-lifecycle-ledger-utils.js";
119
- import {
120
- normalizeProjectionPreview,
121
- normalizeProjectionTags,
122
- } from "./memory-projection-format.js";
137
+ import { normalizeProjectionPreview, normalizeProjectionTags } from "./memory-projection-format.js";
123
138
  import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
124
139
  // stripCitation import removed: legacy rebuild fallback was replaced by a
125
140
  // skip-with-warning strategy (Finding 1 — Uhol). See ensureFactHashIndexAuthoritative.
@@ -207,10 +222,7 @@ function assertMemoryWorthCounter(field: "mw_success" | "mw_fail", value: number
207
222
  }
208
223
  }
209
224
 
210
- function normalizeMemoryWriteTimestamp(
211
- field: string,
212
- value: string | undefined,
213
- ): string | undefined {
225
+ function normalizeMemoryWriteTimestamp(field: string, value: string | undefined): string | undefined {
214
226
  if (value === undefined) return undefined;
215
227
  if (typeof value !== "string") {
216
228
  throw new Error(`${field} must be an ISO timestamp string, got ${String(value)}`);
@@ -323,6 +335,14 @@ function serializeFrontmatter(fm: MemoryFrontmatter): string {
323
335
  `tags: [${fm.tags.map((t) => `"${t}"`).join(", ")}]`,
324
336
  ];
325
337
  if (fm.entityRef) lines.push(`entityRef: ${fm.entityRef}`);
338
+ if (fm.sourceConnector) {
339
+ // YAML-injection guard (review thread QMPsP): emit unquoted for
340
+ // safe connector IDs (matching the connectors/index.ts validator),
341
+ // or JSON.stringify for values with special characters so newlines,
342
+ // colons, and spaces cannot inject extra frontmatter lines.
343
+ const safe = /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(fm.sourceConnector);
344
+ lines.push(`sourceConnector: ${safe ? fm.sourceConnector : JSON.stringify(fm.sourceConnector)}`);
345
+ }
326
346
  if (fm.supersedes) lines.push(`supersedes: ${fm.supersedes}`);
327
347
  if (fm.expiresAt) lines.push(`expiresAt: ${fm.expiresAt}`);
328
348
  if (fm.lineage && fm.lineage.length > 0) {
@@ -353,7 +373,7 @@ function serializeFrontmatter(fm: MemoryFrontmatter): string {
353
373
  if (fm.eventTimeSource) {
354
374
  if (fm.eventTimeSource !== "extracted" && fm.eventTimeSource !== "assumed") {
355
375
  throw new Error(
356
- `serializeFrontmatter: invalid eventTimeSource ${JSON.stringify(fm.eventTimeSource)} — expected "extracted" | "assumed"`,
376
+ `serializeFrontmatter: invalid eventTimeSource ${JSON.stringify(fm.eventTimeSource)} — expected "extracted" | "assumed"`
357
377
  );
358
378
  }
359
379
  lines.push(`eventTimeSource: ${fm.eventTimeSource}`);
@@ -392,7 +412,7 @@ function serializeFrontmatter(fm: MemoryFrontmatter): string {
392
412
  lines.push(
393
413
  `importanceReasons: [${fm.importance.reasons
394
414
  .map((r) => `"${r.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`)
395
- .join(", ")}]`,
415
+ .join(", ")}]`
396
416
  );
397
417
  }
398
418
  if (fm.importance.keywords.length > 0) {
@@ -434,29 +454,25 @@ function serializeFrontmatter(fm: MemoryFrontmatter): string {
434
454
  // permissive; only writes go through the validator.
435
455
  if (fm.derived_from !== undefined) {
436
456
  if (!Array.isArray(fm.derived_from)) {
437
- throw new Error(
438
- `serializeFrontmatter: derived_from must be an array of "<path>:<version>" strings`,
439
- );
457
+ throw new Error(`serializeFrontmatter: derived_from must be an array of "<path>:<version>" strings`);
440
458
  }
441
459
  for (const entry of fm.derived_from) {
442
460
  if (!isValidDerivedFromEntry(entry)) {
443
461
  throw new Error(
444
- `serializeFrontmatter: invalid derived_from entry ${JSON.stringify(entry)} — expected "<path>:<version>" with version >= 0`,
462
+ `serializeFrontmatter: invalid derived_from entry ${JSON.stringify(entry)} — expected "<path>:<version>" with version >= 0`
445
463
  );
446
464
  }
447
465
  }
448
466
  if (fm.derived_from.length > 0) {
449
467
  lines.push(
450
- `derived_from: [${fm.derived_from
451
- .map((e) => `"${e.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`)
452
- .join(", ")}]`,
468
+ `derived_from: [${fm.derived_from.map((e) => `"${e.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`).join(", ")}]`
453
469
  );
454
470
  }
455
471
  }
456
472
  if (fm.derived_via !== undefined) {
457
473
  if (!isConsolidationOperator(fm.derived_via)) {
458
474
  throw new Error(
459
- `serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} — expected one of "split" | "merge" | "update" | "pattern-reinforcement"`,
475
+ `serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} — expected one of "split" | "merge" | "update" | "pattern-reinforcement"`
460
476
  );
461
477
  }
462
478
  lines.push(`derived_via: ${fm.derived_via}`);
@@ -465,12 +481,9 @@ function serializeFrontmatter(fm: MemoryFrontmatter): string {
465
481
  // present so memories never touched by reinforcement round-trip
466
482
  // unchanged; matches the `archivedAt` / `forgottenAt` precedent.
467
483
  if (fm.reinforcement_count !== undefined) {
468
- if (
469
- !Number.isInteger(fm.reinforcement_count) ||
470
- fm.reinforcement_count <= 0
471
- ) {
484
+ if (!Number.isInteger(fm.reinforcement_count) || fm.reinforcement_count <= 0) {
472
485
  throw new Error(
473
- `serializeFrontmatter: reinforcement_count must be a positive integer (got ${JSON.stringify(fm.reinforcement_count)})`,
486
+ `serializeFrontmatter: reinforcement_count must be a positive integer (got ${JSON.stringify(fm.reinforcement_count)})`
474
487
  );
475
488
  }
476
489
  lines.push(`reinforcement_count: ${fm.reinforcement_count}`);
@@ -542,6 +555,32 @@ export function parseFrontmatterStringValue(rawValue: string | undefined): strin
542
555
  * corrupt stored counter fails safely rather than poisoning downstream
543
556
  * scoring. Pair with `assertMemoryWorthCounter` on the write path.
544
557
  */
558
+ function bracketListInner(raw: string): string | undefined {
559
+ const trimmed = raw.trim();
560
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return undefined;
561
+ return trimmed.slice(1, -1);
562
+ }
563
+
564
+ function quotedArrayValues(raw: string): string[] {
565
+ const inner = bracketListInner(raw);
566
+ if (inner === undefined) return [];
567
+ const values: string[] = [];
568
+ let i = 0;
569
+ while (i < inner.length) {
570
+ while (i < inner.length && inner[i] !== '"') i++;
571
+ if (i >= inner.length) break;
572
+ i++;
573
+ let value = "";
574
+ while (i < inner.length) {
575
+ const ch = inner[i++];
576
+ if (ch === "\\" && i < inner.length) value += ch + inner[i++];
577
+ else if (ch === '"') { values.push(value); break; }
578
+ else value += ch;
579
+ }
580
+ }
581
+ return values;
582
+ }
583
+
545
584
  function parseMemoryWorthCounterField(raw: string | undefined): number | undefined {
546
585
  if (raw === undefined) return undefined;
547
586
  const trimmed = raw.trim();
@@ -567,9 +606,7 @@ function parseReinforcementCountField(raw: string | undefined): number | undefin
567
606
  return n;
568
607
  }
569
608
 
570
- export function parseFrontmatter(
571
- raw: string,
572
- ): { frontmatter: MemoryFrontmatter; content: string } | null {
609
+ export function parseFrontmatter(raw: string): { frontmatter: MemoryFrontmatter; content: string } | null {
573
610
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
574
611
  if (!match) return null;
575
612
 
@@ -608,10 +645,7 @@ export function parseFrontmatter(
608
645
  // single-quoted: `''` → `'` (YAML's native escape)
609
646
  let item = m[2].trim();
610
647
  if (item.startsWith('"') && item.endsWith('"') && item.length >= 2) {
611
- item = item
612
- .slice(1, -1)
613
- .replace(/\\"/g, '"')
614
- .replace(/\\\\/g, "\\");
648
+ item = item.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
615
649
  } else if (item.startsWith("'") && item.endsWith("'") && item.length >= 2) {
616
650
  item = item.slice(1, -1).replace(/''/g, "'");
617
651
  }
@@ -619,9 +653,7 @@ export function parseFrontmatter(
619
653
  j++;
620
654
  }
621
655
  if (items.length > 0) {
622
- const inline = items
623
- .map((v) => `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`)
624
- .join(", ");
656
+ const inline = items.map((v) => `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`).join(", ");
625
657
  lines.push(`${line.slice(0, colonIdx + 1)} [${inline}]`);
626
658
  i = j;
627
659
  continue;
@@ -641,9 +673,9 @@ export function parseFrontmatter(
641
673
 
642
674
  let tags: string[] = [];
643
675
  const tagsStr = fm.tags ?? "";
644
- const tagMatch = tagsStr.match(/\[(.*)]/);
645
- if (tagMatch) {
646
- tags = tagMatch[1]
676
+ const tagInner = bracketListInner(tagsStr);
677
+ if (tagInner !== undefined) {
678
+ tags = tagInner
647
679
  .split(",")
648
680
  .map((t) => t.trim().replace(/^"|"$/g, ""))
649
681
  .filter(Boolean);
@@ -651,9 +683,9 @@ export function parseFrontmatter(
651
683
 
652
684
  let intentEntityTypes: string[] | undefined;
653
685
  const intentEntityTypesStr = fm.intentEntityTypes ?? "";
654
- const intentEntityTypesMatch = intentEntityTypesStr.match(/\[(.*)]/);
655
- if (intentEntityTypesMatch) {
656
- intentEntityTypes = intentEntityTypesMatch[1]
686
+ const intentEntityTypesInner = bracketListInner(intentEntityTypesStr);
687
+ if (intentEntityTypesInner !== undefined) {
688
+ intentEntityTypes = intentEntityTypesInner
657
689
  .split(",")
658
690
  .map((t) => t.trim().replace(/^"|"$/g, ""))
659
691
  .filter(Boolean);
@@ -664,9 +696,9 @@ export function parseFrontmatter(
664
696
  // Parse lineage array if present
665
697
  let lineage: string[] | undefined;
666
698
  const lineageStr = fm.lineage ?? "";
667
- const lineageMatch = lineageStr.match(/\[(.*)]/);
668
- if (lineageMatch) {
669
- lineage = lineageMatch[1]
699
+ const lineageInner = bracketListInner(lineageStr);
700
+ if (lineageInner !== undefined) {
701
+ lineage = lineageInner
670
702
  .split(",")
671
703
  .map((l) => l.trim().replace(/^"|"$/g, ""))
672
704
  .filter(Boolean);
@@ -801,21 +833,18 @@ export function parseFrontmatter(
801
833
  let reasons: string[] = [];
802
834
  const reasonsStr = fm.importanceReasons ?? "";
803
835
  if (reasonsStr.trim().startsWith("[") && reasonsStr.trim().endsWith("]")) {
804
- const reasonMatches = reasonsStr.matchAll(/"((?:\\.|[^"\\])*)"/g);
805
- for (const match of reasonMatches) {
806
- const reason = parseLinkReasonValue(match[1]);
807
- if (reason.length > 0) {
808
- reasons.push(reason);
809
- }
836
+ for (const rawReason of quotedArrayValues(reasonsStr)) {
837
+ const reason = parseLinkReasonValue(rawReason);
838
+ if (reason.length > 0) reasons.push(reason);
810
839
  }
811
840
  }
812
841
 
813
842
  // Parse importance keywords array
814
843
  let keywords: string[] = [];
815
844
  const keywordsStr = fm.importanceKeywords ?? "";
816
- const keywordsMatch = keywordsStr.match(/\[(.*)]/);
817
- if (keywordsMatch) {
818
- keywords = keywordsMatch[1]
845
+ const keywordsInner = bracketListInner(keywordsStr);
846
+ if (keywordsInner !== undefined) {
847
+ keywords = keywordsInner
819
848
  .split(",")
820
849
  .map((k) => k.trim().replace(/^"|"$/g, ""))
821
850
  .filter(Boolean);
@@ -835,6 +864,7 @@ export function parseFrontmatter(
835
864
  confidenceTier: (fm.confidenceTier as ConfidenceTier) || confidenceTier(conf),
836
865
  tags,
837
866
  entityRef: fm.entityRef || undefined,
867
+ sourceConnector: fm.sourceConnector ? decodeYamlScalar(fm.sourceConnector) || undefined : undefined,
838
868
  supersedes: fm.supersedes || undefined,
839
869
  expiresAt: fm.expiresAt || undefined,
840
870
  lineage: lineage && lineage.length > 0 ? lineage : undefined,
@@ -858,9 +888,7 @@ export function parseFrontmatter(
858
888
  // Issue #1578 — bi-temporal ingestion provenance round-trip.
859
889
  observedAt: fm.observedAt || undefined,
860
890
  eventTimeSource:
861
- fm.eventTimeSource === "extracted" || fm.eventTimeSource === "assumed"
862
- ? fm.eventTimeSource
863
- : undefined,
891
+ fm.eventTimeSource === "extracted" || fm.eventTimeSource === "assumed" ? fm.eventTimeSource : undefined,
864
892
  forgottenAt: fm.forgottenAt || undefined,
865
893
  forgottenReason: parseFrontmatterStringValue(fm.forgottenReason),
866
894
  lifecycleState: (fm.lifecycleState as LifecycleState) || undefined,
@@ -913,7 +941,7 @@ export function parseFrontmatter(
913
941
  if (fmBlock.includes("links:")) {
914
942
  const links: MemoryLink[] = [];
915
943
  const linkMatches = fmBlock.matchAll(
916
- /- targetId: (\S+)\s+linkType: (\S+)\s+strength: ([\d.]+)(?:\s+reason: "((?:\\.|[^"\\])*)")?/g,
944
+ /- targetId: (\S+)\s+linkType: (\S+)\s+strength: ([\d.]+)(?:\s+reason: "((?:\\.|[^"\\])*)")?/g
917
945
  );
918
946
  for (const match of linkMatches) {
919
947
  links.push({
@@ -932,19 +960,14 @@ export function parseFrontmatter(
932
960
  }
933
961
 
934
962
  function inferEntityTypeFromContent(content: string): string | undefined {
935
- const typeMatch = content.match(/^\*\*Type:\*\*\s*([^\n]+)/m)?.[1]?.trim().toLowerCase();
963
+ const typeMatch = content
964
+ .match(/^\*\*Type:\*\*\s*([^\n]+)/m)?.[1]
965
+ ?.trim()
966
+ .toLowerCase();
936
967
  return typeMatch || undefined;
937
968
  }
938
969
 
939
- const KNOWN_ENTITY_FILENAME_PREFIXES = new Set([
940
- "company",
941
- "other",
942
- "person",
943
- "place",
944
- "project",
945
- "tool",
946
- "topic",
947
- ]);
970
+ const KNOWN_ENTITY_FILENAME_PREFIXES = new Set(["company", "other", "person", "place", "project", "tool", "topic"]);
948
971
 
949
972
  function inferEntityTypeFromFilename(pathRel: string): string | undefined {
950
973
  const basename = path.basename(pathRel, ".md").toLowerCase();
@@ -954,19 +977,28 @@ function inferEntityTypeFromFilename(pathRel: string): string | undefined {
954
977
  return KNOWN_ENTITY_FILENAME_PREFIXES.has(candidate) ? candidate : undefined;
955
978
  }
956
979
 
957
- export function normalizeFrontmatterForPath(frontmatter: MemoryFrontmatter, pathRel: string, content: string = ""): MemoryFrontmatter {
980
+ export function normalizeFrontmatterForPath(
981
+ frontmatter: MemoryFrontmatter,
982
+ pathRel: string,
983
+ content: string = ""
984
+ ): MemoryFrontmatter {
958
985
  const normalizedPath = pathRel.split(path.sep).join("/");
959
986
  let normalizedFrontmatter = frontmatter;
960
987
 
961
- if (normalizedPath === "entities" || normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/")) {
988
+ if (
989
+ normalizedPath === "entities" ||
990
+ normalizedPath.startsWith("entities/") ||
991
+ normalizedPath.includes("/entities/")
992
+ ) {
962
993
  const basename = path.basename(pathRel, ".md");
963
994
  const inferredType = inferEntityTypeFromContent(content) || inferEntityTypeFromFilename(pathRel) || "entity";
964
995
  const existingTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
965
996
  normalizedFrontmatter = {
966
997
  ...normalizedFrontmatter,
967
- id: typeof normalizedFrontmatter.id === "string" && normalizedFrontmatter.id.trim().length > 0
968
- ? normalizedFrontmatter.id
969
- : basename,
998
+ id:
999
+ typeof normalizedFrontmatter.id === "string" && normalizedFrontmatter.id.trim().length > 0
1000
+ ? normalizedFrontmatter.id
1001
+ : basename,
970
1002
  category: "entity",
971
1003
  tags: existingTags.includes(inferredType) ? existingTags : [...existingTags, inferredType],
972
1004
  };
@@ -985,7 +1017,7 @@ export function normalizeFrontmatterForPath(frontmatter: MemoryFrontmatter, path
985
1017
  function inferCurrentStateStatus(
986
1018
  frontmatter: MemoryFrontmatter,
987
1019
  pathRel: string,
988
- fallbackStatus: MemoryStatus,
1020
+ fallbackStatus: MemoryStatus
989
1021
  ): MemoryStatus {
990
1022
  return inferMemoryStatus(frontmatter, pathRel, fallbackStatus);
991
1023
  }
@@ -1007,11 +1039,7 @@ const BUILTIN_ALIASES: Record<string, string> = {
1007
1039
  * store, or pass `storageManager.entityAliases` explicitly. Without an
1008
1040
  * aliases argument only the built-in structural aliases apply.
1009
1041
  */
1010
- export function normalizeEntityName(
1011
- raw: string,
1012
- type: string,
1013
- aliases?: Readonly<Record<string, string>>,
1014
- ): string {
1042
+ export function normalizeEntityName(raw: string, type: string, aliases?: Readonly<Record<string, string>>): string {
1015
1043
  // Strip type prefix if present (e.g. name="person-jane-doe", type="person")
1016
1044
  const rawStr = typeof raw === "string" ? raw : "";
1017
1045
  const typeStr = typeof type === "string" && type.trim().length > 0 ? type : "entity";
@@ -1031,8 +1059,7 @@ export function normalizeEntityName(
1031
1059
  // Check caller-provided user aliases first, then built-in. Own-property and
1032
1060
  // string guards keep inherited object keys (e.g. an entity literally named
1033
1061
  // "constructor") and malformed alias values from corrupting canonical ids.
1034
- const userAlias =
1035
- aliases !== undefined && Object.hasOwn(aliases, normalized) ? aliases[normalized] : undefined;
1062
+ const userAlias = aliases !== undefined && Object.hasOwn(aliases, normalized) ? aliases[normalized] : undefined;
1036
1063
  if (typeof userAlias === "string" && userAlias.length > 0) {
1037
1064
  normalized = userAlias;
1038
1065
  } else if (Object.hasOwn(BUILTIN_ALIASES, normalized)) {
@@ -1057,9 +1084,7 @@ function levenshtein(a: string, b: string): number {
1057
1084
 
1058
1085
  for (let i = 1; i <= m; i++) {
1059
1086
  for (let j = 1; j <= n; j++) {
1060
- dp[i][j] = a[i - 1] === b[j - 1]
1061
- ? dp[i - 1][j - 1]
1062
- : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
1087
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
1063
1088
  }
1064
1089
  }
1065
1090
  return dp[m][n];
@@ -1088,7 +1113,7 @@ export class ContentHashIndex {
1088
1113
  stateDir: string,
1089
1114
  secureStoreKeyProvider: () => Buffer | null = () => null,
1090
1115
  secureStoreWriteKeyProvider: () => Buffer | null = secureStoreKeyProvider,
1091
- memoryDir: string = path.dirname(stateDir),
1116
+ memoryDir: string = path.dirname(stateDir)
1092
1117
  ) {
1093
1118
  this.filePath = path.join(stateDir, "fact-hashes.txt");
1094
1119
  this.secureStoreKeyProvider = secureStoreKeyProvider;
@@ -1149,7 +1174,7 @@ export class ContentHashIndex {
1149
1174
  [...this.hashes].join("\n") + "\n",
1150
1175
  this.secureStoreWriteKeyProvider(),
1151
1176
  {},
1152
- this.memoryDir,
1177
+ this.memoryDir
1153
1178
  );
1154
1179
  this.dirty = false;
1155
1180
  log.debug(`content-hash index: saved ${this.hashes.size} hashes`);
@@ -1243,9 +1268,7 @@ export function normalizeAttributePairs(pairs: Record<string, string>): string {
1243
1268
  // Entity file parsing / serialization (Knowledge Graph v7.0)
1244
1269
  // ---------------------------------------------------------------------------
1245
1270
 
1246
- function parseEntityFrontmatter(
1247
- raw: string,
1248
- ): {
1271
+ function parseEntityFrontmatter(raw: string): {
1249
1272
  frontmatter: {
1250
1273
  created?: string;
1251
1274
  updated?: string;
@@ -1302,7 +1325,9 @@ function parseEntityFrontmatter(
1302
1325
  updated: values.updated || undefined,
1303
1326
  synthesisUpdatedAt: values.synthesis_updated_at || undefined,
1304
1327
  synthesisTimelineCount: Number.isFinite(synthesisTimelineCount) ? synthesisTimelineCount : undefined,
1305
- synthesisStructuredFactCount: Number.isFinite(synthesisStructuredFactCount) ? synthesisStructuredFactCount : undefined,
1328
+ synthesisStructuredFactCount: Number.isFinite(synthesisStructuredFactCount)
1329
+ ? synthesisStructuredFactCount
1330
+ : undefined,
1306
1331
  synthesisStructuredFactDigest: values.synthesis_structured_fact_digest || undefined,
1307
1332
  synthesisVersion: Number.isFinite(synthesisVersion) ? synthesisVersion : undefined,
1308
1333
  extraLines,
@@ -1347,7 +1372,7 @@ function readEntitySectionText(
1347
1372
  options: {
1348
1373
  preserveBullets?: boolean;
1349
1374
  skipTimelineBullets?: boolean;
1350
- } = {},
1375
+ } = {}
1351
1376
  ): string | undefined {
1352
1377
  const normalizedSections = new Set(sectionNames.map((name) => name.toLowerCase()));
1353
1378
  let section = "";
@@ -1368,9 +1393,9 @@ function readEntitySectionText(
1368
1393
  continue;
1369
1394
  }
1370
1395
  if (
1371
- options.skipTimelineBullets === true
1372
- && trimmed.startsWith("- ")
1373
- && isEntitySynthesisTimelinePromotionBullet(trimmed.slice(2))
1396
+ options.skipTimelineBullets === true &&
1397
+ trimmed.startsWith("- ") &&
1398
+ isEntitySynthesisTimelinePromotionBullet(trimmed.slice(2))
1374
1399
  ) {
1375
1400
  continue;
1376
1401
  }
@@ -1384,10 +1409,7 @@ function readEntitySectionText(
1384
1409
  return sectionLines.join(options.preserveBullets === true ? "\n" : " ");
1385
1410
  }
1386
1411
 
1387
- function parseEntityTimelineBullet(
1388
- bullet: string,
1389
- fallbackTimestamp: string,
1390
- ): EntityTimelineEntry | null {
1412
+ function parseEntityTimelineBullet(bullet: string, fallbackTimestamp: string): EntityTimelineEntry | null {
1391
1413
  const trimmed = bullet.trim();
1392
1414
  if (!trimmed) return null;
1393
1415
 
@@ -1440,10 +1462,10 @@ function parseEntityTimelineBullet(
1440
1462
  break;
1441
1463
  case "source":
1442
1464
  if (
1443
- consumedMetadataSegments.length === 0
1444
- && !nextRest.startsWith("[")
1445
- && nextRest.length > 0
1446
- && !isManagedEntityTimelineSource(value)
1465
+ consumedMetadataSegments.length === 0 &&
1466
+ !nextRest.startsWith("[") &&
1467
+ nextRest.length > 0 &&
1468
+ !isManagedEntityTimelineSource(value)
1447
1469
  ) {
1448
1470
  literalSingleSourceSegment = rawSegment;
1449
1471
  rest = nextRest;
@@ -1642,11 +1664,7 @@ function serializeEntityTimelineEntry(entry: EntityTimelineEntry): string {
1642
1664
  }
1643
1665
 
1644
1666
  function dedupeEntityTimelineFacts(timeline: EntityTimelineEntry[]): string[] {
1645
- return [...new Set(
1646
- timeline
1647
- .map((entry) => entry.text.trim())
1648
- .filter((entry) => entry.length > 0),
1649
- )];
1667
+ return [...new Set(timeline.map((entry) => entry.text.trim()).filter((entry) => entry.length > 0))];
1650
1668
  }
1651
1669
 
1652
1670
  function normalizeEntitySectionFact(value: string): string {
@@ -1654,11 +1672,7 @@ function normalizeEntitySectionFact(value: string): string {
1654
1672
  }
1655
1673
 
1656
1674
  export function normalizeStructuredSectionFacts(facts: string[]): string[] {
1657
- return [...new Set(
1658
- facts
1659
- .map((fact) => normalizeEntitySectionFact(fact))
1660
- .filter((fact) => fact.length > 0),
1661
- )];
1675
+ return [...new Set(facts.map((fact) => normalizeEntitySectionFact(fact)).filter((fact) => fact.length > 0))];
1662
1676
  }
1663
1677
 
1664
1678
  function collectStructuredSectionFacts(structuredSections: EntityStructuredSection[]): string[] {
@@ -1675,7 +1689,7 @@ function collectStructuredSectionFacts(structuredSections: EntityStructuredSecti
1675
1689
 
1676
1690
  export function compileEntityFacts(
1677
1691
  timeline: EntityTimelineEntry[],
1678
- structuredSections: EntityStructuredSection[],
1692
+ structuredSections: EntityStructuredSection[]
1679
1693
  ): string[] {
1680
1694
  const facts: string[] = [];
1681
1695
  const seen = new Set<string>();
@@ -1728,7 +1742,7 @@ function looksLikeStructuredSectionFactList(lines: string[]): boolean {
1728
1742
  function partitionEntityStructuredSections(
1729
1743
  entityType: string,
1730
1744
  extraSections: Array<{ title: string; lines: string[] }>,
1731
- entitySchemas?: PluginConfig["entitySchemas"],
1745
+ entitySchemas?: PluginConfig["entitySchemas"]
1732
1746
  ): {
1733
1747
  structuredSections: EntityStructuredSection[];
1734
1748
  remainingExtraSections: Array<{ title: string; lines: string[] }>;
@@ -1750,11 +1764,7 @@ function partitionEntityStructuredSections(
1750
1764
  }
1751
1765
  const normalizedSection = matchedSection
1752
1766
  ? { key: matchedSection.key, title: matchedSection.title }
1753
- : normalizeEntityStructuredSection(
1754
- entityType,
1755
- { key: section.title, title: section.title },
1756
- entitySchemas,
1757
- );
1767
+ : normalizeEntityStructuredSection(entityType, { key: section.title, title: section.title }, entitySchemas);
1758
1768
  if (facts.length === 0) {
1759
1769
  remainingExtraSections.push(section);
1760
1770
  continue;
@@ -1822,13 +1832,17 @@ export function fingerprintEntityStructuredFacts(entity: Pick<EntityFile, "struc
1822
1832
  .map((section) => ({
1823
1833
  key: section.key.trim().toLowerCase(),
1824
1834
  title: section.title.replace(/\s+/g, " ").trim(),
1825
- facts: normalizeStructuredSectionFacts(section.facts).slice().sort((left, right) => left.localeCompare(right)),
1835
+ facts: normalizeStructuredSectionFacts(section.facts)
1836
+ .slice()
1837
+ .sort((left, right) => left.localeCompare(right)),
1826
1838
  }))
1827
1839
  .filter((section) => section.facts.length > 0)
1828
- .sort((left, right) =>
1829
- left.key.localeCompare(right.key)
1830
- || left.title.localeCompare(right.title)
1831
- || left.facts.join("\n").localeCompare(right.facts.join("\n")));
1840
+ .sort(
1841
+ (left, right) =>
1842
+ left.key.localeCompare(right.key) ||
1843
+ left.title.localeCompare(right.title) ||
1844
+ left.facts.join("\n").localeCompare(right.facts.join("\n"))
1845
+ );
1832
1846
  if (normalizedSections.length === 0) return undefined;
1833
1847
  return createHash("sha256").update(JSON.stringify(normalizedSections)).digest("hex");
1834
1848
  }
@@ -1844,26 +1858,27 @@ export function isEntitySynthesisStale(entity: EntityFile): boolean {
1844
1858
  if (structuredFactCount > 0 && !storedStructuredFactDigest) return true;
1845
1859
  const latestTimelineTimestamp = latestEntityTimelineTimestamp(entity);
1846
1860
  if (!latestTimelineTimestamp) {
1847
- return entity.timeline.length > entity.synthesisTimelineCount
1848
- || structuredFactCount > (entity.synthesisStructuredFactCount ?? 0)
1849
- || structuredFactDigest !== storedStructuredFactDigest;
1861
+ return (
1862
+ entity.timeline.length > entity.synthesisTimelineCount ||
1863
+ structuredFactCount > (entity.synthesisStructuredFactCount ?? 0) ||
1864
+ structuredFactDigest !== storedStructuredFactDigest
1865
+ );
1850
1866
  }
1851
1867
  if (!entity.synthesisUpdatedAt?.trim()) return true;
1852
1868
  const timelineFreshness = compareEntityTimestamps(latestTimelineTimestamp, entity.synthesisUpdatedAt);
1853
1869
  if (timelineFreshness > 0) return true;
1854
- return entity.timeline.length > entity.synthesisTimelineCount
1855
- || structuredFactCount > (entity.synthesisStructuredFactCount ?? 0)
1856
- || structuredFactDigest !== storedStructuredFactDigest;
1870
+ return (
1871
+ entity.timeline.length > entity.synthesisTimelineCount ||
1872
+ structuredFactCount > (entity.synthesisStructuredFactCount ?? 0) ||
1873
+ structuredFactDigest !== storedStructuredFactDigest
1874
+ );
1857
1875
  }
1858
1876
 
1859
1877
  /**
1860
1878
  * Parse an entity markdown file into a structured EntityFile.
1861
1879
  * Backward compatible: old files without new sections get empty arrays.
1862
1880
  */
1863
- export function parseEntityFile(
1864
- content: string,
1865
- entitySchemas?: PluginConfig["entitySchemas"],
1866
- ): EntityFile {
1881
+ export function parseEntityFile(content: string, entitySchemas?: PluginConfig["entitySchemas"]): EntityFile {
1867
1882
  const { frontmatter, body } = parseEntityFrontmatter(content);
1868
1883
  const lines = body.split("\n");
1869
1884
  const recognizedSections = new Set([
@@ -1905,11 +1920,10 @@ export function parseEntityFile(
1905
1920
  const headingLineIndex = lines.findIndex((l) => l.startsWith("# "));
1906
1921
  const firstSectionIndex = lines.findIndex((l) => l.startsWith("## "));
1907
1922
  const preSectionStartIndex = headingLineIndex > -1 ? headingLineIndex + 1 : 0;
1908
- const preSectionCandidates = firstSectionIndex > -1
1909
- ? lines.slice(preSectionStartIndex, firstSectionIndex)
1910
- : lines.slice(preSectionStartIndex);
1923
+ const preSectionCandidates =
1924
+ firstSectionIndex > -1 ? lines.slice(preSectionStartIndex, firstSectionIndex) : lines.slice(preSectionStartIndex);
1911
1925
  const preSectionLines = preSectionCandidates.filter(
1912
- (line) => !line.startsWith("**Type:**") && !line.startsWith("**Updated:**"),
1926
+ (line) => !line.startsWith("**Type:**") && !line.startsWith("**Updated:**")
1913
1927
  );
1914
1928
  const normalizedPreSectionLines = [...preSectionLines];
1915
1929
  while (normalizedPreSectionLines[0] === "") {
@@ -1949,20 +1963,14 @@ export function parseEntityFile(
1949
1963
  legacyFacts.push(bullet);
1950
1964
  break;
1951
1965
  case "timeline": {
1952
- const parsed = parseEntityTimelineBullet(
1953
- bullet,
1954
- fallbackTimestamp,
1955
- );
1966
+ const parsed = parseEntityTimelineBullet(bullet, fallbackTimestamp);
1956
1967
  if (parsed) timeline.push(parsed);
1957
1968
  break;
1958
1969
  }
1959
1970
  case "summary":
1960
1971
  case "synthesis":
1961
1972
  if (isEntitySynthesisTimelinePromotionBullet(bullet)) {
1962
- const parsed = parseEntityTimelineBullet(
1963
- bullet,
1964
- fallbackTimestamp,
1965
- );
1973
+ const parsed = parseEntityTimelineBullet(bullet, fallbackTimestamp);
1966
1974
  if (parsed) timeline.push(parsed);
1967
1975
  }
1968
1976
  // Summary/synthesis is typically a paragraph after the heading, not a bullet.
@@ -2006,9 +2014,7 @@ export function parseEntityFile(
2006
2014
 
2007
2015
  if (legacyFactTimelineEntries.length > 0) {
2008
2016
  const existingTimelineFacts = new Set(
2009
- timeline
2010
- .map((entry) => entry.text.trim())
2011
- .filter((entry) => entry.length > 0),
2017
+ timeline.map((entry) => entry.text.trim()).filter((entry) => entry.length > 0)
2012
2018
  );
2013
2019
  for (const fact of legacyFactTimelineEntries) {
2014
2020
  const normalizedFact = fact.text.trim();
@@ -2019,8 +2025,8 @@ export function parseEntityFile(
2019
2025
  }
2020
2026
 
2021
2027
  const synthesis =
2022
- readEntitySectionText(lines, ["Synthesis"], { preserveBullets: true, skipTimelineBullets: true })
2023
- ?? readEntitySectionText(lines, ["Summary"], { preserveBullets: true, skipTimelineBullets: true });
2028
+ readEntitySectionText(lines, ["Synthesis"], { preserveBullets: true, skipTimelineBullets: true }) ??
2029
+ readEntitySectionText(lines, ["Summary"], { preserveBullets: true, skipTimelineBullets: true });
2024
2030
  const synthesisUpdatedAt = frontmatter.synthesisUpdatedAt || undefined;
2025
2031
  const synthesisTimelineCount = frontmatter.synthesisTimelineCount;
2026
2032
  const synthesisStructuredFactCount = frontmatter.synthesisStructuredFactCount;
@@ -2028,7 +2034,7 @@ export function parseEntityFile(
2028
2034
  const { structuredSections, remainingExtraSections } = partitionEntityStructuredSections(
2029
2035
  type,
2030
2036
  extraSections,
2031
- entitySchemas,
2037
+ entitySchemas
2032
2038
  );
2033
2039
  const facts = compileEntityFacts(timeline, structuredSections);
2034
2040
 
@@ -2061,30 +2067,32 @@ export function parseEntityFile(
2061
2067
  * Writes the compiled-truth + timeline format while remaining parse-compatible
2062
2068
  * with the legacy in-memory `summary` and `facts` fields.
2063
2069
  */
2064
- export function serializeEntityFile(
2065
- entity: EntityFile,
2066
- entitySchemas?: PluginConfig["entitySchemas"],
2067
- ): string {
2070
+ export function serializeEntityFile(entity: EntityFile, entitySchemas?: PluginConfig["entitySchemas"]): string {
2068
2071
  const synthesis = entity.synthesis || entity.summary || "";
2069
2072
  const created = entity.created?.trim() || entity.updated || new Date().toISOString();
2070
2073
  const updated = entity.updated || created;
2071
2074
  const timeline = entity.timeline;
2072
2075
  const structuredSections = sortStructuredSectionsBySchema(
2073
2076
  entity.type,
2074
- (entity.structuredSections ?? []).map((section) => ({
2075
- ...section,
2076
- facts: normalizeStructuredSectionFacts(section.facts),
2077
- })).filter((section) => section.facts.length > 0),
2078
- entitySchemas,
2077
+ (entity.structuredSections ?? [])
2078
+ .map((section) => ({
2079
+ ...section,
2080
+ facts: normalizeStructuredSectionFacts(section.facts),
2081
+ }))
2082
+ .filter((section) => section.facts.length > 0),
2083
+ entitySchemas
2079
2084
  );
2080
2085
  const sectionFacts = new Set(collectStructuredSectionFacts(structuredSections));
2081
- const legacyFacts = timeline.length === 0
2082
- ? [...new Set(
2083
- entity.facts
2084
- .map((fact) => normalizeEntitySectionFact(fact))
2085
- .filter((fact) => fact.length > 0 && !sectionFacts.has(fact)),
2086
- )]
2087
- : [];
2086
+ const legacyFacts =
2087
+ timeline.length === 0
2088
+ ? [
2089
+ ...new Set(
2090
+ entity.facts
2091
+ .map((fact) => normalizeEntitySectionFact(fact))
2092
+ .filter((fact) => fact.length > 0 && !sectionFacts.has(fact))
2093
+ ),
2094
+ ]
2095
+ : [];
2088
2096
  const synthesisUpdatedAt = entity.synthesisUpdatedAt?.trim() || "";
2089
2097
  const synthesisTimelineCount = entity.synthesisTimelineCount;
2090
2098
  const synthesisStructuredFactCount = entity.synthesisStructuredFactCount;
@@ -2096,15 +2104,11 @@ export function serializeEntityFile(
2096
2104
  `created: ${created}`,
2097
2105
  `updated: ${updated}`,
2098
2106
  `synthesis_updated_at: "${synthesisUpdatedAt}"`,
2099
- ...(synthesisTimelineCount === undefined
2100
- ? []
2101
- : [`synthesis_timeline_count: ${synthesisTimelineCount}`]),
2107
+ ...(synthesisTimelineCount === undefined ? [] : [`synthesis_timeline_count: ${synthesisTimelineCount}`]),
2102
2108
  ...(synthesisStructuredFactCount === undefined
2103
2109
  ? []
2104
2110
  : [`synthesis_structured_fact_count: ${synthesisStructuredFactCount}`]),
2105
- ...(synthesisStructuredFactDigest
2106
- ? [`synthesis_structured_fact_digest: "${synthesisStructuredFactDigest}"`]
2107
- : []),
2111
+ ...(synthesisStructuredFactDigest ? [`synthesis_structured_fact_digest: "${synthesisStructuredFactDigest}"`] : []),
2108
2112
  `synthesis_version: ${synthesisVersion}`,
2109
2113
  ...(entity.extraFrontmatterLines ?? []),
2110
2114
  "---",
@@ -2297,7 +2301,10 @@ export class StorageManager {
2297
2301
  /** Read by storage/memory-read-store.ts (decomposition). */
2298
2302
  static readonly COLD_SCAN_CACHE_TTL_MS = 30_000; // 30 seconds
2299
2303
  /** Read by storage/memory-read-store.ts (decomposition). */
2300
- static readonly coldMemoriesCache = new Map<string, { memories: MemoryFile[]; loadedAt: number; coldVersion: number }>();
2304
+ static readonly coldMemoriesCache = new Map<
2305
+ string,
2306
+ { memories: MemoryFile[]; loadedAt: number; coldVersion: number }
2307
+ >();
2301
2308
 
2302
2309
  // Cache for readQuestions() — avoids serially re-reading tens of thousands of
2303
2310
  // question files on every recall. 60-second TTL is intentionally short so that
@@ -2334,7 +2341,11 @@ export class StorageManager {
2334
2341
  /** Post-write catalog hook (#1522). Installed by the namespace router; fire-and-forget. */
2335
2342
  onCatalogWrite?: () => void;
2336
2343
  private notifyCatalogWrite(): void {
2337
- try { this.onCatalogWrite?.(); } catch { /* gotcha #13 */ }
2344
+ try {
2345
+ this.onCatalogWrite?.();
2346
+ } catch {
2347
+ /* gotcha #13 */
2348
+ }
2338
2349
  }
2339
2350
 
2340
2351
  // ── Tombstone store (issue #1579) ───────────────────────────────────────
@@ -2445,7 +2456,7 @@ export class StorageManager {
2445
2456
  if (this._secureStoreRequired) {
2446
2457
  throw new SecureStoreLockedError(
2447
2458
  "secure-store is locked — cannot write memory file. " +
2448
- "Run `remnic secure-store unlock` to decrypt, or restart the daemon after unlocking.",
2459
+ "Run `remnic secure-store unlock` to decrypt, or restart the daemon after unlocking."
2449
2460
  );
2450
2461
  }
2451
2462
  return null;
@@ -2497,13 +2508,13 @@ export class StorageManager {
2497
2508
  this._versioningConfig,
2498
2509
  log,
2499
2510
  undefined,
2500
- this.baseDir,
2511
+ this.baseDir
2501
2512
  );
2502
2513
  const rel = path.relative(this.baseDir, filePath).split(path.sep).join("/");
2503
2514
  return `${rel}:${version.versionId}`;
2504
2515
  } catch (err) {
2505
2516
  log.warn(
2506
- `storage.snapshotForProvenance: failed to snapshot ${filePath}: ${err instanceof Error ? err.message : String(err)}`,
2517
+ `storage.snapshotForProvenance: failed to snapshot ${filePath}: ${err instanceof Error ? err.message : String(err)}`
2507
2518
  );
2508
2519
  return null;
2509
2520
  }
@@ -2511,7 +2522,7 @@ export class StorageManager {
2511
2522
 
2512
2523
  constructor(
2513
2524
  private readonly baseDir: string,
2514
- private readonly entitySchemas?: PluginConfig["entitySchemas"],
2525
+ private readonly entitySchemas?: PluginConfig["entitySchemas"]
2515
2526
  ) {
2516
2527
  // Load this store's alias table at construction (#1534): StorageManager
2517
2528
  // is created in a dozen places (namespace router, operator toolkit,
@@ -2540,9 +2551,7 @@ export class StorageManager {
2540
2551
 
2541
2552
  private get memoryReadStore(): MemoryReadStore {
2542
2553
  if (!this._memoryReadStore) {
2543
- this._memoryReadStore = new MemoryReadStore(
2544
- selfDeps<ConstructorParameters<typeof MemoryReadStore>[0]>(this),
2545
- );
2554
+ this._memoryReadStore = new MemoryReadStore(selfDeps<ConstructorParameters<typeof MemoryReadStore>[0]>(this));
2546
2555
  }
2547
2556
  return this._memoryReadStore;
2548
2557
  }
@@ -2552,9 +2561,7 @@ export class StorageManager {
2552
2561
 
2553
2562
  private get entityStore(): EntityStore {
2554
2563
  if (!this._entityStore) {
2555
- this._entityStore = new EntityStore(
2556
- selfDeps<ConstructorParameters<typeof EntityStore>[0]>(this),
2557
- );
2564
+ this._entityStore = new EntityStore(selfDeps<ConstructorParameters<typeof EntityStore>[0]>(this));
2558
2565
  }
2559
2566
  return this._entityStore;
2560
2567
  }
@@ -2565,7 +2572,7 @@ export class StorageManager {
2565
2572
  private get identityContinuityStore(): IdentityContinuityStore {
2566
2573
  if (!this._identityContinuityStore) {
2567
2574
  this._identityContinuityStore = new IdentityContinuityStore(
2568
- selfDeps<ConstructorParameters<typeof IdentityContinuityStore>[0]>(this),
2575
+ selfDeps<ConstructorParameters<typeof IdentityContinuityStore>[0]>(this)
2569
2576
  );
2570
2577
  }
2571
2578
  return this._identityContinuityStore;
@@ -2594,7 +2601,7 @@ export class StorageManager {
2594
2601
 
2595
2602
  private bumpSharedVersion(
2596
2603
  kind: "memory-status" | "artifact-write" | "cold-write",
2597
- fallbackMap: Map<string, number>,
2604
+ fallbackMap: Map<string, number>
2598
2605
  ): number {
2599
2606
  const filePath = this.versionFilePath(kind);
2600
2607
  try {
@@ -2612,7 +2619,7 @@ export class StorageManager {
2612
2619
 
2613
2620
  private readSharedVersion(
2614
2621
  kind: "memory-status" | "artifact-write" | "cold-write",
2615
- fallbackMap: Map<string, number>,
2622
+ fallbackMap: Map<string, number>
2616
2623
  ): number {
2617
2624
  const filePath = this.versionFilePath(kind);
2618
2625
  try {
@@ -2667,32 +2674,23 @@ export class StorageManager {
2667
2674
  wearableTranscriptPath(sourceId: string, date: string): string {
2668
2675
  if (typeof sourceId !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(sourceId)) {
2669
2676
  throw new Error(
2670
- `invalid wearable source id '${String(sourceId)}' — expected lowercase letters, digits, and dashes`,
2677
+ `invalid wearable source id '${String(sourceId)}' — expected lowercase letters, digits, and dashes`
2671
2678
  );
2672
2679
  }
2673
2680
  if (!isValidTranscriptDate(date)) {
2674
- throw new Error(
2675
- `invalid wearable transcript date '${String(date)}' — expected YYYY-MM-DD`,
2676
- );
2681
+ throw new Error(`invalid wearable transcript date '${String(date)}' — expected YYYY-MM-DD`);
2677
2682
  }
2678
2683
  return path.join(this.wearablesDir, sourceId, `${date}.md`);
2679
2684
  }
2680
2685
 
2681
- async writeWearableDayTranscript(
2682
- sourceId: string,
2683
- date: string,
2684
- serialized: string,
2685
- ): Promise<void> {
2686
+ async writeWearableDayTranscript(sourceId: string, date: string, serialized: string): Promise<void> {
2686
2687
  const targetPath = this.wearableTranscriptPath(sourceId, date);
2687
2688
  // writeMaybeEncryptedFile handles mkdir + atomic temp→rename.
2688
2689
  await this.writeStorageSecureFile(targetPath, serialized);
2689
2690
  }
2690
2691
 
2691
2692
  /** Read a stored day transcript; null when the day has no file. */
2692
- async readWearableDayTranscript(
2693
- sourceId: string,
2694
- date: string,
2695
- ): Promise<string | null> {
2693
+ async readWearableDayTranscript(sourceId: string, date: string): Promise<string | null> {
2696
2694
  const targetPath = this.wearableTranscriptPath(sourceId, date);
2697
2695
  try {
2698
2696
  return await readMaybeEncryptedFile(targetPath, this._secureStoreKey, this.baseDir);
@@ -2702,12 +2700,8 @@ export class StorageManager {
2702
2700
  }
2703
2701
  }
2704
2702
 
2705
- async listWearableTranscriptDays(
2706
- sourceId?: string,
2707
- ): Promise<Array<{ source: string; date: string }>> {
2708
- return this.memoryReadStore.listWearableTranscriptDays(
2709
- sourceId,
2710
- );
2703
+ async listWearableTranscriptDays(sourceId?: string): Promise<Array<{ source: string; date: string }>> {
2704
+ return this.memoryReadStore.listWearableTranscriptDays(sourceId);
2711
2705
  }
2712
2706
 
2713
2707
  private _fusionStore?: FusionArtifactStore;
@@ -2732,9 +2726,7 @@ export class StorageManager {
2732
2726
  * smart trust pipeline to find an earlier borderline write when the
2733
2727
  * same fact re-extracts with stronger evidence.
2734
2728
  */
2735
- async findWearableMemoryByContent(
2736
- content: string,
2737
- ): Promise<{ id: string; status: MemoryStatus | undefined } | null> {
2729
+ async findWearableMemoryByContent(content: string): Promise<{ id: string; status: MemoryStatus | undefined } | null> {
2738
2730
  const needle = stripAttributesSuffix(content);
2739
2731
  const memories = await this.readAllMemories();
2740
2732
  for (const memory of memories) {
@@ -2761,7 +2753,7 @@ export class StorageManager {
2761
2753
  async promoteWearableMemory(
2762
2754
  id: string,
2763
2755
  attributeUpdates: Record<string, string>,
2764
- confidence?: number,
2756
+ confidence?: number
2765
2757
  ): Promise<boolean> {
2766
2758
  const memories = await this.readAllMemories();
2767
2759
  const memory = memories.find((entry) => entry.frontmatter.id === id);
@@ -2792,10 +2784,7 @@ export class StorageManager {
2792
2784
  * approvals and accrued recall signals win; contradiction scans and
2793
2785
  * supersession own active-row retirement).
2794
2786
  */
2795
- async demoteWearableMemory(
2796
- id: string,
2797
- attributeUpdates: Record<string, string>,
2798
- ): Promise<boolean> {
2787
+ async demoteWearableMemory(id: string, attributeUpdates: Record<string, string>): Promise<boolean> {
2799
2788
  const memories = await this.readAllMemories();
2800
2789
  const memory = memories.find((entry) => entry.frontmatter.id === id);
2801
2790
  if (!memory) return false;
@@ -2837,12 +2826,14 @@ export class StorageManager {
2837
2826
  return readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
2838
2827
  }
2839
2828
  private writeStorageSecureFile(filePath: string, content: string | Buffer): Promise<void> {
2840
- return writeMaybeEncryptedFile(filePath, content, this.resolveWriteKey(), {}, this.baseDir)
2841
- .then(() => this.notifyCatalogWrite());
2829
+ return writeMaybeEncryptedFile(filePath, content, this.resolveWriteKey(), {}, this.baseDir).then(() =>
2830
+ this.notifyCatalogWrite()
2831
+ );
2842
2832
  }
2843
2833
  private writeStorageSecureFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
2844
- return writeMaybeEncryptedFileFromChunks(filePath, chunks, this.resolveWriteKey(), {}, this.baseDir)
2845
- .then(() => this.notifyCatalogWrite());
2834
+ return writeMaybeEncryptedFileFromChunks(filePath, chunks, this.resolveWriteKey(), {}, this.baseDir).then(() =>
2835
+ this.notifyCatalogWrite()
2836
+ );
2846
2837
  }
2847
2838
 
2848
2839
  private assertManagedStoragePath(filePath: string, method: string): string {
@@ -2951,7 +2942,7 @@ export class StorageManager {
2951
2942
  } catch (err) {
2952
2943
  if (!isErrnoCode(err, "ENOENT")) {
2953
2944
  log.warn(
2954
- `storage.offlineSyncDigestCache: ignoring unreadable cache: ${err instanceof Error ? err.message : String(err)}`,
2945
+ `storage.offlineSyncDigestCache: ignoring unreadable cache: ${err instanceof Error ? err.message : String(err)}`
2955
2946
  );
2956
2947
  }
2957
2948
  }
@@ -2961,7 +2952,7 @@ export class StorageManager {
2961
2952
  private rememberOfflineSyncDigest(
2962
2953
  relPath: string,
2963
2954
  st: { size: number; mtimeMs: number; ctimeMs: number },
2964
- digest: { sha256: string; bytes: number },
2955
+ digest: { sha256: string; bytes: number }
2965
2956
  ): void {
2966
2957
  const cache = this.offlineSyncDigestCache;
2967
2958
  if (!cache) return;
@@ -2997,15 +2988,11 @@ export class StorageManager {
2997
2988
  .sort(([a], [b]) => a.localeCompare(b))
2998
2989
  .map(([entryPath, entry]) => ({ path: entryPath, ...entry }));
2999
2990
  await mkdir(path.dirname(this.offlineSyncDigestCachePath), { recursive: true });
3000
- await writeFile(
3001
- this.offlineSyncDigestCachePath,
3002
- `${JSON.stringify({ version: 1, entries })}\n`,
3003
- "utf-8",
3004
- );
2991
+ await writeFile(this.offlineSyncDigestCachePath, `${JSON.stringify({ version: 1, entries })}\n`, "utf-8");
3005
2992
  })
3006
2993
  .catch((err) => {
3007
2994
  log.warn(
3008
- `storage.offlineSyncDigestCache: failed to write cache: ${err instanceof Error ? err.message : String(err)}`,
2995
+ `storage.offlineSyncDigestCache: failed to write cache: ${err instanceof Error ? err.message : String(err)}`
3009
2996
  );
3010
2997
  });
3011
2998
  }
@@ -3071,15 +3058,13 @@ export class StorageManager {
3071
3058
  this.stateDir,
3072
3059
  () => this._secureStoreKey,
3073
3060
  () => this.resolveWriteKey(),
3074
- this.baseDir,
3061
+ this.baseDir
3075
3062
  );
3076
3063
  }
3077
3064
 
3078
3065
  private async appendStorageSecureFile(filePath: string, content: string): Promise<void> {
3079
3066
  const previous = this.secureAppendChains.get(filePath) ?? Promise.resolve();
3080
- const current = previous
3081
- .catch(() => undefined)
3082
- .then(() => this.appendStorageSecureFileUnlocked(filePath, content));
3067
+ const current = previous.catch(() => undefined).then(() => this.appendStorageSecureFileUnlocked(filePath, content));
3083
3068
  const next = current.catch(() => undefined);
3084
3069
  this.secureAppendChains.set(filePath, next);
3085
3070
  try {
@@ -3184,12 +3169,7 @@ export class StorageManager {
3184
3169
  // stat lets the store own its cross-process staleness probe (#1579).
3185
3170
  stat: (filePath) => statSync(filePath),
3186
3171
  };
3187
- return new TombstoneStore(
3188
- this.tombstonesPath,
3189
- this.tombstonesConfig.namespace,
3190
- options,
3191
- io,
3192
- );
3172
+ return new TombstoneStore(this.tombstonesPath, this.tombstonesConfig.namespace, options, io);
3193
3173
  }
3194
3174
 
3195
3175
  /**
@@ -3397,9 +3377,7 @@ export class StorageManager {
3397
3377
  const stripped = stripCitationForTemplate(content, this.citationTemplate);
3398
3378
  if (stripped !== content) {
3399
3379
  // Citation was stripped — index the bare body.
3400
- factHashIndex.addByHash(
3401
- ContentHashIndex.computeHash(sanitizeMemoryContent(stripped).text),
3402
- );
3380
+ factHashIndex.addByHash(ContentHashIndex.computeHash(sanitizeMemoryContent(stripped).text));
3403
3381
  continue;
3404
3382
  }
3405
3383
  // No citation was removed. Decide whether to index or skip.
@@ -3410,9 +3388,7 @@ export class StorageManager {
3410
3388
  // means the citation is from an unknown/custom template we cannot strip.
3411
3389
  if (!hasCitation(content)) {
3412
3390
  // Content has no recognisable citation marker — index raw body.
3413
- factHashIndex.addByHash(
3414
- ContentHashIndex.computeHash(sanitizeMemoryContent(content).text),
3415
- );
3391
+ factHashIndex.addByHash(ContentHashIndex.computeHash(sanitizeMemoryContent(content).text));
3416
3392
  continue;
3417
3393
  }
3418
3394
  // Content carries a citation from an unknown/custom template
@@ -3422,7 +3398,7 @@ export class StorageManager {
3422
3398
  }
3423
3399
  if (legacyRecovered > 0) {
3424
3400
  log.info(
3425
- `ensureFactHashIndexAuthoritative: skipped ${legacyRecovered} legacy fact(s) with no contentHash in frontmatter`,
3401
+ `ensureFactHashIndexAuthoritative: skipped ${legacyRecovered} legacy fact(s) with no contentHash in frontmatter`
3426
3402
  );
3427
3403
  }
3428
3404
  await factHashIndex.save();
@@ -3509,10 +3485,7 @@ export class StorageManager {
3509
3485
  private userAliases: Record<string, string> = {};
3510
3486
 
3511
3487
  normalizeEntityName(raw: string, type: string): string {
3512
- return this.entityStore.normalizeEntityName(
3513
- raw,
3514
- type,
3515
- );
3488
+ return this.entityStore.normalizeEntityName(raw, type);
3516
3489
  }
3517
3490
 
3518
3491
  /**
@@ -3552,9 +3525,7 @@ export class StorageManager {
3552
3525
  this.userAliases = cleaned;
3553
3526
  log.debug(`loaded ${Object.keys(cleaned).length} entity aliases from ${aliasPath}`);
3554
3527
  } else {
3555
- log.warn(
3556
- `ignoring ${aliasPath}: payload must be a JSON object mapping variant → canonical strings`,
3557
- );
3528
+ log.warn(`ignoring ${aliasPath}: payload must be a JSON object mapping variant → canonical strings`);
3558
3529
  }
3559
3530
  } catch {
3560
3531
  // No aliases file — that's fine, use built-in only
@@ -3591,11 +3562,7 @@ export class StorageManager {
3591
3562
  * as `<dir>/<date>/`. Read/scan/reindex already iterate every category dir
3592
3563
  * (RECALL_FALLBACK_DIRS; QMD scans baseDir recursively), so writes stay found.
3593
3564
  */
3594
- private async resolveCategoryWritePath(
3595
- category: MemoryCategory,
3596
- id: string,
3597
- today: string,
3598
- ): Promise<string> {
3565
+ private async resolveCategoryWritePath(category: MemoryCategory, id: string, today: string): Promise<string> {
3599
3566
  if (category === "correction") {
3600
3567
  await mkdir(this.correctionsDir, { recursive: true });
3601
3568
  return path.join(this.correctionsDir, `${id}.md`);
@@ -3671,7 +3638,8 @@ export class StorageManager {
3671
3638
  */
3672
3639
  sources?: ProvenanceSource[];
3673
3640
  provenance?: "verified" | "unverified" | "none";
3674
- } = {},
3641
+ sourceConnector?: string;
3642
+ } = {}
3675
3643
  ): Promise<MemoryWriteResult> {
3676
3644
  await this.ensureDirectories();
3677
3645
  const now = new Date();
@@ -3680,10 +3648,7 @@ export class StorageManager {
3680
3648
  const conf = options.confidence ?? 0.8;
3681
3649
  const tier = confidenceTier(conf);
3682
3650
  const validAt = normalizeMemoryWriteTimestamp("validAt", options.validAt);
3683
- const observedAt = normalizeMemoryWriteTimestamp(
3684
- "observedAt",
3685
- options.observedAt,
3686
- );
3651
+ const observedAt = normalizeMemoryWriteTimestamp("observedAt", options.observedAt);
3687
3652
 
3688
3653
  // Auto-set TTL for speculative memories
3689
3654
  let expiresAt: string | undefined;
@@ -3753,6 +3718,9 @@ export class StorageManager {
3753
3718
  if (options.provenance !== undefined) {
3754
3719
  fm.provenance = options.provenance;
3755
3720
  }
3721
+ if (options.sourceConnector !== undefined) {
3722
+ fm.sourceConnector = options.sourceConnector;
3723
+ }
3756
3724
 
3757
3725
  // Append structured attributes as searchable suffix so QMD indexes them.
3758
3726
  // normalizeAttributePairs sorts and lowercases keys so the enriched content
@@ -3830,7 +3798,7 @@ export class StorageManager {
3830
3798
  fm.blockedBy = match.tombstoneId;
3831
3799
  fm.tombstoneBlockTier = match.matchedTier;
3832
3800
  log.info(
3833
- `tombstone: blocked resurrection of fact ${id} (tier=${match.matchedTier}, tombstone=${match.tombstoneId}, reason=${match.reason})`,
3801
+ `tombstone: blocked resurrection of fact ${id} (tier=${match.matchedTier}, tombstone=${match.tombstoneId}, reason=${match.reason})`
3834
3802
  );
3835
3803
  }
3836
3804
  } catch (err) {
@@ -3856,7 +3824,7 @@ export class StorageManager {
3856
3824
  after: this.summarizeLifecycleState(fm, filePath),
3857
3825
  relatedMemoryIds: [
3858
3826
  ...(options.supersedes ? [options.supersedes] : []),
3859
- ...((options.lineage ?? []).filter(Boolean)),
3827
+ ...(options.lineage ?? []).filter(Boolean),
3860
3828
  ],
3861
3829
  });
3862
3830
  if (category === "fact" && !tombstoneBlocked) {
@@ -3957,7 +3925,7 @@ export class StorageManager {
3957
3925
  const removedIds = new Set(
3958
3926
  memories
3959
3927
  .map((memory) => memory.frontmatter.id)
3960
- .filter((id): id is string => typeof id === "string" && id.length > 0),
3928
+ .filter((id): id is string => typeof id === "string" && id.length > 0)
3961
3929
  );
3962
3930
  const removedHashes = new Map<MemoryFile, string>();
3963
3931
  for (const memory of memories) {
@@ -3969,10 +3937,7 @@ export class StorageManager {
3969
3937
  if (removedHashes.size === 0) return;
3970
3938
 
3971
3939
  const remainingActiveHashes = new Set<string>();
3972
- const remainingMemories = [
3973
- ...await this.readAllMemories(),
3974
- ...await this.readAllColdMemories(),
3975
- ];
3940
+ const remainingMemories = [...(await this.readAllMemories()), ...(await this.readAllColdMemories())];
3976
3941
  for (const memory of remainingMemories) {
3977
3942
  if (memory.frontmatter.category !== "fact") continue;
3978
3943
  if (removedIds.has(memory.frontmatter.id)) continue;
@@ -4008,7 +3973,8 @@ export class StorageManager {
4008
3973
  intentGoal?: string;
4009
3974
  intentActionType?: string;
4010
3975
  intentEntityTypes?: string[];
4011
- } = {},
3976
+ sourceConnector?: string;
3977
+ } = {}
4012
3978
  ): Promise<string> {
4013
3979
  await this.ensureDirectories();
4014
3980
  const now = new Date();
@@ -4032,6 +3998,7 @@ export class StorageManager {
4032
3998
  intentGoal: options.intentGoal,
4033
3999
  intentActionType: options.intentActionType,
4034
4000
  intentEntityTypes: options.intentEntityTypes,
4001
+ ...(options.sourceConnector ? { sourceConnector: options.sourceConnector } : {}),
4035
4002
  };
4036
4003
 
4037
4004
  const sanitized = sanitizeMemoryContent(quote);
@@ -4042,9 +4009,7 @@ export class StorageManager {
4042
4009
  const filePath = path.join(dir, `${id}.md`);
4043
4010
  await this.writeStorageSecureFile(filePath, `${serializeFrontmatter(fm)}\n\n${sanitized.text}\n`);
4044
4011
  const actor =
4045
- typeof options.actor === "string" && options.actor.length > 0
4046
- ? options.actor
4047
- : "storage.writeArtifact";
4012
+ typeof options.actor === "string" && options.actor.length > 0 ? options.actor : "storage.writeArtifact";
4048
4013
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeArtifact", {
4049
4014
  memoryId: id,
4050
4015
  eventType: "created",
@@ -4061,8 +4026,7 @@ export class StorageManager {
4061
4026
  }
4062
4027
 
4063
4028
  private async readAllArtifactsCached(): Promise<MemoryFile[]> {
4064
- return this.memoryReadStore.readAllArtifactsCached(
4065
- );
4029
+ return this.memoryReadStore.readAllArtifactsCached();
4066
4030
  }
4067
4031
 
4068
4032
  async searchArtifacts(query: string, maxResults: number): Promise<MemoryFile[]> {
@@ -4073,7 +4037,7 @@ export class StorageManager {
4073
4037
  const hits: Array<{ score: number; memory: MemoryFile }> = [];
4074
4038
  for (const memory of artifacts) {
4075
4039
  const indexedTokens = new Set(
4076
- tokenizeArtifactSearchText(`${memory.content} ${(memory.frontmatter.tags ?? []).join(" ")}`),
4040
+ tokenizeArtifactSearchText(`${memory.content} ${(memory.frontmatter.tags ?? []).join(" ")}`)
4077
4041
  );
4078
4042
  const score = tokens.reduce((sum, t) => sum + (indexedTokens.has(t) ? 1 : 0), 0);
4079
4043
  if (score > 0) {
@@ -4094,14 +4058,9 @@ export class StorageManager {
4094
4058
  sessionKey?: string;
4095
4059
  principal?: string;
4096
4060
  structuredSections?: EntityStructuredSection[];
4097
- } = {},
4061
+ } = {}
4098
4062
  ): Promise<string> {
4099
- return this.entityStore.writeEntity(
4100
- name,
4101
- type,
4102
- facts,
4103
- options,
4104
- );
4063
+ return this.entityStore.writeEntity(name, type, facts, options);
4105
4064
  }
4106
4065
 
4107
4066
  async readProfile(): Promise<string> {
@@ -4161,9 +4120,7 @@ export class StorageManager {
4161
4120
  const existing = await this.readProfile();
4162
4121
 
4163
4122
  const lines = existing ? existing.split("\n") : [];
4164
- const existingBulletRaw = lines
4165
- .filter((l) => l.startsWith("- "))
4166
- .map((l) => l.slice(2).trim());
4123
+ const existingBulletRaw = lines.filter((l) => l.startsWith("- ")).map((l) => l.slice(2).trim());
4167
4124
  const existingNorms = existingBulletRaw.map(StorageManager.normalizeForDedup);
4168
4125
 
4169
4126
  const newBullets = updates.filter((u) => {
@@ -4183,10 +4140,7 @@ export class StorageManager {
4183
4140
  ].join("\n");
4184
4141
  await this.writeProfile(content);
4185
4142
  } else {
4186
- const updatedTimestamp = existing.replace(
4187
- /\*Last updated:.*\*/,
4188
- `*Last updated: ${new Date().toISOString()}*`,
4189
- );
4143
+ const updatedTimestamp = existing.replace(/\*Last updated:.*\*/, `*Last updated: ${new Date().toISOString()}*`);
4190
4144
  const withBullets = updatedTimestamp.trimEnd() + "\n" + newBullets.map((b) => `- ${b}`).join("\n") + "\n";
4191
4145
  await this.writeProfile(withBullets);
4192
4146
  }
@@ -4197,9 +4151,8 @@ export class StorageManager {
4197
4151
  const profile = await this.readProfile();
4198
4152
  if (!profile) return false;
4199
4153
  const lineCount = profile.split("\n").length;
4200
- const threshold = typeof triggerLines === "number"
4201
- ? Math.max(0, Math.floor(triggerLines))
4202
- : StorageManager.PROFILE_MAX_LINES;
4154
+ const threshold =
4155
+ typeof triggerLines === "number" ? Math.max(0, Math.floor(triggerLines)) : StorageManager.PROFILE_MAX_LINES;
4203
4156
  return lineCount > threshold;
4204
4157
  }
4205
4158
 
@@ -4333,14 +4286,10 @@ export class StorageManager {
4333
4286
  }
4334
4287
 
4335
4288
  private async collectActiveMemoryPaths(): Promise<string[]> {
4336
- return this.memoryReadStore.collectActiveMemoryPaths(
4337
- );
4289
+ return this.memoryReadStore.collectActiveMemoryPaths();
4338
4290
  }
4339
4291
 
4340
- private async readParsedMemoriesFromPaths(
4341
- filePaths: string[],
4342
- batchSize?: number,
4343
- ): Promise<MemoryFile[]> {
4292
+ private async readParsedMemoriesFromPaths(filePaths: string[], batchSize?: number): Promise<MemoryFile[]> {
4344
4293
  if (filePaths.length === 0) return [];
4345
4294
 
4346
4295
  const normalizedBatchSize = this.normalizeMemoryReadBatchSize(batchSize);
@@ -4358,7 +4307,7 @@ export class StorageManager {
4358
4307
  frontmatter: normalizeFrontmatterForPath(
4359
4308
  parsed.frontmatter,
4360
4309
  toMemoryPathRel(this.baseDir, fullPath),
4361
- parsed.content,
4310
+ parsed.content
4362
4311
  ),
4363
4312
  content: parsed.content,
4364
4313
  } satisfies MemoryFile;
@@ -4369,7 +4318,7 @@ export class StorageManager {
4369
4318
  if (err instanceof SecureStoreLockedError) throw err;
4370
4319
  return null;
4371
4320
  }
4372
- }),
4321
+ })
4373
4322
  );
4374
4323
  for (const memory of results) {
4375
4324
  if (memory !== null) memories.push(memory);
@@ -4385,9 +4334,9 @@ export class StorageManager {
4385
4334
  if (!match) return null;
4386
4335
  const frontmatterBlock = match[1];
4387
4336
  const rawUpdated =
4388
- frontmatterBlock.match(/^updated:\s*"?([^"\n]*)"?/m)?.[1]
4389
- ?? frontmatterBlock.match(/^created:\s*"?([^"\n]*)"?/m)?.[1]
4390
- ?? null;
4337
+ frontmatterBlock.match(/^updated:\s*"?([^"\n]*)"?/m)?.[1] ??
4338
+ frontmatterBlock.match(/^created:\s*"?([^"\n]*)"?/m)?.[1] ??
4339
+ null;
4391
4340
  const updatedMs = rawUpdated ? Date.parse(rawUpdated) : Number.NaN;
4392
4341
  return Number.isFinite(updatedMs) ? updatedMs : null;
4393
4342
  } catch {
@@ -4396,18 +4345,20 @@ export class StorageManager {
4396
4345
  }
4397
4346
 
4398
4347
  private async filterWindowPathsByUpdatedAfter(filePaths: string[], updatedAfterMs: number): Promise<string[]> {
4399
- const results = await Promise.all(filePaths.map(async (filePath) => {
4400
- const updatedMs = await this.readWindowUpdatedMs(filePath);
4401
- if (updatedMs !== null) {
4402
- return updatedMs >= updatedAfterMs ? filePath : null;
4403
- }
4404
- try {
4405
- const fileStat = await stat(filePath);
4406
- return fileStat.mtimeMs >= updatedAfterMs ? filePath : null;
4407
- } catch {
4408
- return filePath;
4409
- }
4410
- }));
4348
+ const results = await Promise.all(
4349
+ filePaths.map(async (filePath) => {
4350
+ const updatedMs = await this.readWindowUpdatedMs(filePath);
4351
+ if (updatedMs !== null) {
4352
+ return updatedMs >= updatedAfterMs ? filePath : null;
4353
+ }
4354
+ try {
4355
+ const fileStat = await stat(filePath);
4356
+ return fileStat.mtimeMs >= updatedAfterMs ? filePath : null;
4357
+ } catch {
4358
+ return filePath;
4359
+ }
4360
+ })
4361
+ );
4411
4362
  return results.filter((filePath): filePath is string => filePath !== null);
4412
4363
  }
4413
4364
 
@@ -4444,7 +4395,7 @@ export class StorageManager {
4444
4395
  candidateBatchPaths: string[],
4445
4396
  remainingSlots: number,
4446
4397
  remainingInspectionBudget: number,
4447
- readBatchSize: number,
4398
+ readBatchSize: number
4448
4399
  ): Promise<{ memories: MemoryFile[]; filePaths: string[] }> {
4449
4400
  const memories: MemoryFile[] = [];
4450
4401
  const filePaths: string[] = [];
@@ -4455,12 +4406,10 @@ export class StorageManager {
4455
4406
  const availableSlots = remainingSlots - memories.length;
4456
4407
  const availableInspectionBudget = remainingInspectionBudget - filePaths.length;
4457
4408
  const parallelWindow =
4458
- availableSlots >= 4 && availableInspectionBudget >= 4
4459
- ? Math.min(normalizedReadBatchSize, 4)
4460
- : 1;
4409
+ availableSlots >= 4 && availableInspectionBudget >= 4 ? Math.min(normalizedReadBatchSize, 4) : 1;
4461
4410
  const candidatePaths = candidateBatchPaths.slice(
4462
4411
  index,
4463
- index + Math.min(parallelWindow, availableInspectionBudget),
4412
+ index + Math.min(parallelWindow, availableInspectionBudget)
4464
4413
  );
4465
4414
  index += candidatePaths.length;
4466
4415
  if (candidatePaths.length === 0) break;
@@ -4473,14 +4422,14 @@ export class StorageManager {
4473
4422
  return { memories, filePaths };
4474
4423
  }
4475
4424
 
4476
- async readMemoriesWindow(options: {
4477
- maxMemories?: number;
4478
- batchSize?: number;
4479
- updatedAfter?: Date;
4480
- } = {}): Promise<{ memories: MemoryFile[]; filePaths: string[] }> {
4481
- return this.memoryReadStore.readMemoriesWindow(
4482
- options,
4483
- );
4425
+ async readMemoriesWindow(
4426
+ options: {
4427
+ maxMemories?: number;
4428
+ batchSize?: number;
4429
+ updatedAfter?: Date;
4430
+ } = {}
4431
+ ): Promise<{ memories: MemoryFile[]; filePaths: string[] }> {
4432
+ return this.memoryReadStore.readMemoriesWindow(options);
4484
4433
  }
4485
4434
 
4486
4435
  private async _readAllMemoriesFromDisk(): Promise<MemoryFile[]> {
@@ -4489,8 +4438,7 @@ export class StorageManager {
4489
4438
  }
4490
4439
 
4491
4440
  async readAllColdMemories(): Promise<MemoryFile[]> {
4492
- return this.memoryReadStore.readAllColdMemories(
4493
- );
4441
+ return this.memoryReadStore.readAllColdMemories();
4494
4442
  }
4495
4443
 
4496
4444
  /**
@@ -4518,7 +4466,7 @@ export class StorageManager {
4518
4466
  frontmatter: normalizeFrontmatterForPath(
4519
4467
  parsed.frontmatter,
4520
4468
  toMemoryPathRel(this.baseDir, fullPath),
4521
- parsed.content,
4469
+ parsed.content
4522
4470
  ),
4523
4471
  content: parsed.content,
4524
4472
  });
@@ -4541,9 +4489,7 @@ export class StorageManager {
4541
4489
  }
4542
4490
 
4543
4491
  async readMemoryByPath(filePath: string): Promise<MemoryFile | null> {
4544
- return this.memoryReadStore.readMemoryByPath(
4545
- filePath,
4546
- );
4492
+ return this.memoryReadStore.readMemoryByPath(filePath);
4547
4493
  }
4548
4494
 
4549
4495
  private resolveTierRootDir(tier: "hot" | "cold"): string {
@@ -4553,9 +4499,7 @@ export class StorageManager {
4553
4499
  private resolveMemoryDateDir(memory: MemoryFile): string {
4554
4500
  const preferred = memory.frontmatter.created || memory.frontmatter.updated;
4555
4501
  const dateToken = (preferred ?? "").slice(0, 10);
4556
- return /^\d{4}-\d{2}-\d{2}$/.test(dateToken)
4557
- ? dateToken
4558
- : new Date().toISOString().slice(0, 10);
4502
+ return /^\d{4}-\d{2}-\d{2}$/.test(dateToken) ? dateToken : new Date().toISOString().slice(0, 10);
4559
4503
  }
4560
4504
 
4561
4505
  private isArtifactMemory(memory: MemoryFile): boolean {
@@ -4611,7 +4555,7 @@ export class StorageManager {
4611
4555
 
4612
4556
  async migrateMemoryToTier(
4613
4557
  memory: MemoryFile,
4614
- targetTier: "hot" | "cold",
4558
+ targetTier: "hot" | "cold"
4615
4559
  ): Promise<{ changed: boolean; targetPath: string }> {
4616
4560
  const targetPath = this.buildTierMemoryPath(memory, targetTier);
4617
4561
  const sourcePath = path.resolve(memory.path);
@@ -4655,10 +4599,7 @@ export class StorageManager {
4655
4599
  * Updates frontmatter with archived status before moving.
4656
4600
  * Returns the new file path on success, null on failure.
4657
4601
  */
4658
- async archiveMemory(
4659
- memory: MemoryFile,
4660
- lifecycle?: MemoryLifecycleEventWriteOptions,
4661
- ): Promise<string | null> {
4602
+ async archiveMemory(memory: MemoryFile, lifecycle?: MemoryLifecycleEventWriteOptions): Promise<string | null> {
4662
4603
  try {
4663
4604
  const now = lifecycle?.at ?? new Date();
4664
4605
  const today = now.toISOString().slice(0, 10);
@@ -4678,7 +4619,8 @@ export class StorageManager {
4678
4619
 
4679
4620
  // Write to archive location first (encrypted if applicable), then remove original.
4680
4621
  await this.writeStorageSecureFile(destPath, fileContent);
4681
- await unlink(memory.path); markProjectedMemoryPathInvalid(this.baseDir, memory.frontmatter.id);
4622
+ await unlink(memory.path);
4623
+ markProjectedMemoryPathInvalid(this.baseDir, memory.frontmatter.id);
4682
4624
  this.invalidateAllMemoriesCache();
4683
4625
  await this.appendGeneratedMemoryLifecycleEventFailOpen(
4684
4626
  "storage.archiveMemory",
@@ -4693,7 +4635,7 @@ export class StorageManager {
4693
4635
  relatedMemoryIds: lifecycle?.relatedMemoryIds,
4694
4636
  correlationId: lifecycle?.correlationId,
4695
4637
  },
4696
- lifecycle?.ruleVersion,
4638
+ lifecycle?.ruleVersion
4697
4639
  );
4698
4640
  this.bumpMemoryStatusVersion();
4699
4641
 
@@ -4715,14 +4657,11 @@ export class StorageManager {
4715
4657
  }
4716
4658
 
4717
4659
  async readEntity(name: string): Promise<string> {
4718
- return this.entityStore.readEntity(
4719
- name,
4720
- );
4660
+ return this.entityStore.readEntity(name);
4721
4661
  }
4722
4662
 
4723
4663
  async listEntityNames(): Promise<string[]> {
4724
- return this.entityStore.listEntityNames(
4725
- );
4664
+ return this.entityStore.listEntityNames();
4726
4665
  }
4727
4666
 
4728
4667
  /**
@@ -4742,9 +4681,7 @@ export class StorageManager {
4742
4681
  const typePrefix = `${type.toLowerCase()}-`;
4743
4682
  // Extract the name part from the proposed normalized name
4744
4683
  const proposedFull = this.normalizeEntityName(proposedName, type);
4745
- const proposedNamePart = proposedFull.startsWith(typePrefix)
4746
- ? proposedFull.slice(typePrefix.length)
4747
- : proposedFull;
4684
+ const proposedNamePart = proposedFull.startsWith(typePrefix) ? proposedFull.slice(typePrefix.length) : proposedFull;
4748
4685
  const proposedDehyph = dehyphenate(proposedNamePart);
4749
4686
 
4750
4687
  // Only compare against entities of the same type
@@ -4783,7 +4720,8 @@ export class StorageManager {
4783
4720
  if (!memory) return false;
4784
4721
 
4785
4722
  try {
4786
- await unlink(memory.path); markProjectedMemoryPathInvalid(this.baseDir, id);
4723
+ await unlink(memory.path);
4724
+ markProjectedMemoryPathInvalid(this.baseDir, id);
4787
4725
  this.invalidateAllMemoriesCache();
4788
4726
  this.bumpMemoryStatusVersion();
4789
4727
  log.debug(`invalidated memory ${id}`);
@@ -4796,22 +4734,24 @@ export class StorageManager {
4796
4734
  async updateMemory(
4797
4735
  id: string,
4798
4736
  newContent: string,
4799
- options?: { supersedes?: string; lineage?: string[]; actor?: string },
4737
+ options?: { supersedes?: string; lineage?: string[]; actor?: string; sourceConnector?: string }
4800
4738
  ): Promise<boolean> {
4801
4739
  const memories = await this.readAllMemories();
4802
4740
  const memory = memories.find((m) => m.frontmatter.id === id);
4803
4741
  if (!memory) return false;
4804
4742
 
4805
- const mergedLineage = [
4806
- ...(memory.frontmatter.lineage ?? []),
4807
- ...(options?.lineage ?? []),
4808
- ].filter((v, i, a) => a.indexOf(v) === i); // dedupe
4743
+ const mergedLineage = [...(memory.frontmatter.lineage ?? []), ...(options?.lineage ?? [])].filter(
4744
+ (v, i, a) => a.indexOf(v) === i
4745
+ ); // dedupe
4809
4746
 
4810
4747
  const updated: MemoryFrontmatter = {
4811
4748
  ...memory.frontmatter,
4812
4749
  updated: new Date().toISOString(),
4813
4750
  supersedes: options?.supersedes ?? memory.frontmatter.supersedes,
4814
4751
  lineage: mergedLineage.length > 0 ? mergedLineage : undefined,
4752
+ ...(memory.frontmatter.sourceConnector === undefined && options?.sourceConnector
4753
+ ? { sourceConnector: options.sourceConnector }
4754
+ : {}),
4815
4755
  };
4816
4756
  const sanitized = sanitizeMemoryContent(newContent);
4817
4757
  if (!sanitized.clean) {
@@ -4829,7 +4769,7 @@ export class StorageManager {
4829
4769
  after: this.summarizeLifecycleState(updated, memory.path),
4830
4770
  relatedMemoryIds: [
4831
4771
  ...(updated.supersedes ? [updated.supersedes] : []),
4832
- ...((updated.lineage ?? []).filter(Boolean)),
4772
+ ...(updated.lineage ?? []).filter(Boolean),
4833
4773
  ],
4834
4774
  });
4835
4775
  log.debug(`updated memory ${id}`);
@@ -4843,7 +4783,7 @@ export class StorageManager {
4843
4783
  async writeMemoryFrontmatter(
4844
4784
  memory: MemoryFile,
4845
4785
  patch: Partial<MemoryFrontmatter>,
4846
- lifecycle?: MemoryLifecycleEventWriteOptions,
4786
+ lifecycle?: MemoryLifecycleEventWriteOptions
4847
4787
  ): Promise<boolean> {
4848
4788
  const beforeStatus = memory.frontmatter.status ?? "active";
4849
4789
  const updated: MemoryFrontmatter = {
@@ -4862,13 +4802,10 @@ export class StorageManager {
4862
4802
  this.invalidateColdMemoriesCache();
4863
4803
  }
4864
4804
  try {
4865
- await this.syncFactHashIndexAfterRewrite(
4866
- memory,
4867
- {
4868
- ...memory,
4869
- frontmatter: updated,
4870
- },
4871
- );
4805
+ await this.syncFactHashIndexAfterRewrite(memory, {
4806
+ ...memory,
4807
+ frontmatter: updated,
4808
+ });
4872
4809
  } catch (err) {
4873
4810
  log.warn(`storage.writeMemoryFrontmatter completed but failed to update fact hash index: ${err}`);
4874
4811
  }
@@ -4889,7 +4826,7 @@ export class StorageManager {
4889
4826
  ],
4890
4827
  correlationId: lifecycle?.correlationId,
4891
4828
  },
4892
- lifecycle?.ruleVersion,
4829
+ lifecycle?.ruleVersion
4893
4830
  );
4894
4831
  if (beforeStatus !== afterStatus) {
4895
4832
  this.bumpMemoryStatusVersion();
@@ -4901,10 +4838,7 @@ export class StorageManager {
4901
4838
  * Update frontmatter by memory ID.
4902
4839
  * Prefer writeMemoryFrontmatter(memory, patch) in batch loops to avoid full-corpus rescans.
4903
4840
  */
4904
- async updateMemoryFrontmatter(
4905
- id: string,
4906
- patch: Partial<MemoryFrontmatter>,
4907
- ): Promise<boolean> {
4841
+ async updateMemoryFrontmatter(id: string, patch: Partial<MemoryFrontmatter>): Promise<boolean> {
4908
4842
  const memories = await this.readAllMemories();
4909
4843
  const memory = memories.find((m) => m.frontmatter.id === id);
4910
4844
  if (!memory) return false;
@@ -4922,7 +4856,8 @@ export class StorageManager {
4922
4856
  const expiresAt = new Date(m.frontmatter.expiresAt).getTime();
4923
4857
  if (expiresAt < now) {
4924
4858
  try {
4925
- await unlink(m.path); markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
4859
+ await unlink(m.path);
4860
+ markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
4926
4861
  deleted.push(m);
4927
4862
  log.debug(`cleaned expired memory ${m.frontmatter.id} (TTL expired)`);
4928
4863
  } catch {
@@ -4963,26 +4898,19 @@ export class StorageManager {
4963
4898
  const raw = await this.readStorageSecureFile(metaPath);
4964
4899
  const parsed = JSON.parse(raw) as MetaState;
4965
4900
  return {
4966
- extractionCount:
4967
- typeof parsed.extractionCount === "number" ? parsed.extractionCount : 0,
4901
+ extractionCount: typeof parsed.extractionCount === "number" ? parsed.extractionCount : 0,
4968
4902
  lastExtractionAt: parsed.lastExtractionAt ?? null,
4969
4903
  lastConsolidationAt: parsed.lastConsolidationAt ?? null,
4970
- totalMemories:
4971
- typeof parsed.totalMemories === "number" ? parsed.totalMemories : 0,
4972
- totalEntities:
4973
- typeof parsed.totalEntities === "number" ? parsed.totalEntities : 0,
4974
- processedExtractionFingerprints: Array.isArray(
4975
- parsed.processedExtractionFingerprints,
4976
- )
4904
+ totalMemories: typeof parsed.totalMemories === "number" ? parsed.totalMemories : 0,
4905
+ totalEntities: typeof parsed.totalEntities === "number" ? parsed.totalEntities : 0,
4906
+ processedExtractionFingerprints: Array.isArray(parsed.processedExtractionFingerprints)
4977
4907
  ? parsed.processedExtractionFingerprints
4978
4908
  .filter(
4979
4909
  (entry) =>
4980
4910
  entry &&
4981
4911
  typeof entry === "object" &&
4982
- typeof (entry as { fingerprint?: unknown }).fingerprint ===
4983
- "string" &&
4984
- typeof (entry as { observedAt?: unknown }).observedAt ===
4985
- "string",
4912
+ typeof (entry as { fingerprint?: unknown }).fingerprint === "string" &&
4913
+ typeof (entry as { observedAt?: unknown }).observedAt === "string"
4986
4914
  )
4987
4915
  .map((entry) => ({
4988
4916
  fingerprint: (entry as { fingerprint: string }).fingerprint,
@@ -5015,13 +4943,15 @@ export class StorageManager {
5015
4943
  await this.ensureDirectories();
5016
4944
 
5017
4945
  const nowIso = new Date().toISOString();
5018
- const payload = events.map((event) => {
5019
- const normalized: MemoryActionEvent = {
5020
- ...event,
5021
- timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : nowIso,
5022
- };
5023
- return `${JSON.stringify(normalized)}\n`;
5024
- }).join("");
4946
+ const payload = events
4947
+ .map((event) => {
4948
+ const normalized: MemoryActionEvent = {
4949
+ ...event,
4950
+ timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : nowIso,
4951
+ };
4952
+ return `${JSON.stringify(normalized)}\n`;
4953
+ })
4954
+ .join("");
5025
4955
 
5026
4956
  await this.appendStorageSecureFile(this.memoryActionsPath, payload);
5027
4957
  return events.length;
@@ -5032,32 +4962,26 @@ export class StorageManager {
5032
4962
  await this.ensureDirectories();
5033
4963
 
5034
4964
  const nowIso = new Date().toISOString();
5035
- const payload = events.map((event) => {
5036
- const normalized: MemoryLifecycleEvent = {
5037
- ...event,
5038
- timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : nowIso,
5039
- };
5040
- return `${JSON.stringify(normalized)}\n`;
5041
- }).join("");
4965
+ const payload = events
4966
+ .map((event) => {
4967
+ const normalized: MemoryLifecycleEvent = {
4968
+ ...event,
4969
+ timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : nowIso,
4970
+ };
4971
+ return `${JSON.stringify(normalized)}\n`;
4972
+ })
4973
+ .join("");
5042
4974
 
5043
4975
  await this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, payload);
5044
4976
  return events.length;
5045
4977
  }
5046
4978
 
5047
- async appendBufferSurpriseEvents(
5048
- events: BufferSurpriseEvent[],
5049
- ): Promise<number> {
5050
- return this.memoryReadStore.appendBufferSurpriseEvents(
5051
- events,
5052
- );
4979
+ async appendBufferSurpriseEvents(events: BufferSurpriseEvent[]): Promise<number> {
4980
+ return this.memoryReadStore.appendBufferSurpriseEvents(events);
5053
4981
  }
5054
4982
 
5055
- async readBufferSurpriseEvents(
5056
- options: { limit?: number } = {},
5057
- ): Promise<BufferSurpriseEvent[]> {
5058
- return this.memoryReadStore.readBufferSurpriseEvents(
5059
- options,
5060
- );
4983
+ async readBufferSurpriseEvents(options: { limit?: number } = {}): Promise<BufferSurpriseEvent[]> {
4984
+ return this.memoryReadStore.readBufferSurpriseEvents(options);
5061
4985
  }
5062
4986
 
5063
4987
  async appendBehaviorSignals(events: BehaviorSignalEvent[]): Promise<number> {
@@ -5206,11 +5130,8 @@ export class StorageManager {
5206
5130
 
5207
5131
  try {
5208
5132
  return await readMemoryActionEventRowsFromLines(
5209
- readMaybeEncryptedLines(
5210
- this.memoryActionsPath,
5211
- () => this.readStorageSecureFile(this.memoryActionsPath),
5212
- ),
5213
- cappedLimit,
5133
+ readMaybeEncryptedLines(this.memoryActionsPath, () => this.readStorageSecureFile(this.memoryActionsPath)),
5134
+ cappedLimit
5214
5135
  );
5215
5136
  } catch (err) {
5216
5137
  if (err instanceof SecureStoreLockedError) throw err;
@@ -5222,9 +5143,8 @@ export class StorageManager {
5222
5143
  async readAllMemoryLifecycleEvents(): Promise<MemoryLifecycleEvent[]> {
5223
5144
  try {
5224
5145
  const out: MemoryLifecycleEvent[] = [];
5225
- for await (const line of readMaybeEncryptedLines(
5226
- this.memoryLifecycleLedgerPath,
5227
- () => this.readStorageSecureFile(this.memoryLifecycleLedgerPath),
5146
+ for await (const line of readMaybeEncryptedLines(this.memoryLifecycleLedgerPath, () =>
5147
+ this.readStorageSecureFile(this.memoryLifecycleLedgerPath)
5228
5148
  )) {
5229
5149
  const row = line.trim();
5230
5150
  if (!row) continue;
@@ -5289,16 +5209,12 @@ export class StorageManager {
5289
5209
  }
5290
5210
  }
5291
5211
 
5292
- async writeCompressionGuidelineOptimizerState(
5293
- state: CompressionGuidelineOptimizerState,
5294
- ): Promise<void> {
5212
+ async writeCompressionGuidelineOptimizerState(state: CompressionGuidelineOptimizerState): Promise<void> {
5295
5213
  await this.ensureDirectories();
5296
5214
  await this.writeStorageSecureFile(this.compressionGuidelineStatePath, `${JSON.stringify(state, null, 2)}\n`);
5297
5215
  }
5298
5216
 
5299
- async writeCompressionGuidelineDraftState(
5300
- state: CompressionGuidelineOptimizerState,
5301
- ): Promise<void> {
5217
+ async writeCompressionGuidelineDraftState(state: CompressionGuidelineOptimizerState): Promise<void> {
5302
5218
  await this.ensureDirectories();
5303
5219
  await this.writeStorageSecureFile(this.compressionGuidelineDraftStatePath, `${JSON.stringify(state, null, 2)}\n`);
5304
5220
  }
@@ -5352,11 +5268,9 @@ export class StorageManager {
5352
5268
  }
5353
5269
 
5354
5270
  private async readCompressionGuidelineStateFile(
5355
- filePath: string,
5271
+ filePath: string
5356
5272
  ): Promise<CompressionGuidelineOptimizerState | null> {
5357
- return this.memoryReadStore.readCompressionGuidelineStateFile(
5358
- filePath,
5359
- );
5273
+ return this.memoryReadStore.readCompressionGuidelineStateFile(filePath);
5360
5274
  }
5361
5275
 
5362
5276
  async writeIdentityAnchor(content: string): Promise<void> {
@@ -5373,14 +5287,14 @@ export class StorageManager {
5373
5287
 
5374
5288
  async readContinuityIncidents(
5375
5289
  limit: number = 200,
5376
- state: "open" | "closed" | "all" = "all",
5290
+ state: "open" | "closed" | "all" = "all"
5377
5291
  ): Promise<ContinuityIncidentRecord[]> {
5378
5292
  return this.identityContinuityStore.readContinuityIncidents(limit, state);
5379
5293
  }
5380
5294
 
5381
5295
  async closeContinuityIncident(
5382
5296
  id: string,
5383
- closure: ContinuityIncidentCloseInput,
5297
+ closure: ContinuityIncidentCloseInput
5384
5298
  ): Promise<ContinuityIncidentRecord | null> {
5385
5299
  return this.identityContinuityStore.closeContinuityIncident(id, closure);
5386
5300
  }
@@ -5411,7 +5325,7 @@ export class StorageManager {
5411
5325
 
5412
5326
  async reviewIdentityImprovementLoop(
5413
5327
  id: string,
5414
- input: ContinuityLoopReviewInput,
5328
+ input: ContinuityLoopReviewInput
5415
5329
  ): Promise<ContinuityImprovementLoop | null> {
5416
5330
  return this.identityContinuityStore.reviewIdentityImprovementLoop(id, input);
5417
5331
  }
@@ -5426,11 +5340,7 @@ export class StorageManager {
5426
5340
  return `${prefix}-${ts}-${rand}`;
5427
5341
  }
5428
5342
 
5429
- async writeQuestion(
5430
- question: string,
5431
- context: string,
5432
- priority: number,
5433
- ): Promise<string> {
5343
+ async writeQuestion(question: string, context: string, priority: number): Promise<string> {
5434
5344
  await mkdir(this.questionsDir, { recursive: true });
5435
5345
 
5436
5346
  const id = this.generateId("q");
@@ -5441,7 +5351,9 @@ export class StorageManager {
5441
5351
  resolved: false,
5442
5352
  };
5443
5353
 
5444
- const content = `---\n${Object.entries(frontmatter).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join("\n")}\n---\n\n${question}\n\n**Context:** ${context}\n`;
5354
+ const content = `---\n${Object.entries(frontmatter)
5355
+ .map(([k, v]) => `${k}: ${JSON.stringify(v)}`)
5356
+ .join("\n")}\n---\n\n${question}\n\n**Context:** ${context}\n`;
5445
5357
 
5446
5358
  const filePath = path.join(this.questionsDir, `${id}.md`);
5447
5359
  await this.writeStorageSecureFile(filePath, content);
@@ -5451,9 +5363,7 @@ export class StorageManager {
5451
5363
  return id;
5452
5364
  }
5453
5365
 
5454
- async readQuestions(
5455
- opts?: { unresolvedOnly?: boolean },
5456
- ): Promise<
5366
+ async readQuestions(opts?: { unresolvedOnly?: boolean }): Promise<
5457
5367
  Array<{
5458
5368
  id: string;
5459
5369
  question: string;
@@ -5474,7 +5384,7 @@ export class StorageManager {
5474
5384
 
5475
5385
  private parseQuestionFile(
5476
5386
  raw: string,
5477
- filePath: string,
5387
+ filePath: string
5478
5388
  ): {
5479
5389
  id: string;
5480
5390
  question: string;
@@ -5491,34 +5401,21 @@ export class StorageManager {
5491
5401
  const body = match[2].trim();
5492
5402
 
5493
5403
  // Parse frontmatter
5494
- const id =
5495
- this.extractFrontmatterValue(frontmatterStr, "id") ??
5496
- path.basename(filePath, ".md");
5497
- const created =
5498
- this.extractFrontmatterValue(frontmatterStr, "created") ?? "";
5499
- const priority = parseFloat(
5500
- this.extractFrontmatterValue(frontmatterStr, "priority") ?? "0.5",
5501
- );
5502
- const resolved =
5503
- this.extractFrontmatterValue(frontmatterStr, "resolved") === "true";
5404
+ const id = this.extractFrontmatterValue(frontmatterStr, "id") ?? path.basename(filePath, ".md");
5405
+ const created = this.extractFrontmatterValue(frontmatterStr, "created") ?? "";
5406
+ const priority = parseFloat(this.extractFrontmatterValue(frontmatterStr, "priority") ?? "0.5");
5407
+ const resolved = this.extractFrontmatterValue(frontmatterStr, "resolved") === "true";
5504
5408
 
5505
5409
  // Extract question and context from body
5506
5410
  const contextMatch = body.match(/\*\*Context:\*\*\s*(.*)/);
5507
- const question = contextMatch
5508
- ? body.slice(0, contextMatch.index).trim()
5509
- : body;
5411
+ const question = contextMatch ? body.slice(0, contextMatch.index).trim() : body;
5510
5412
  const context = contextMatch ? contextMatch[1].trim() : "";
5511
5413
 
5512
5414
  return { id, question, context, priority, resolved, created, filePath };
5513
5415
  }
5514
5416
 
5515
- private extractFrontmatterValue(
5516
- frontmatter: string,
5517
- key: string,
5518
- ): string | null {
5519
- const match = frontmatter.match(
5520
- new RegExp(`^${key}:\\s*"?([^"\\n]*)"?`, "m"),
5521
- );
5417
+ private extractFrontmatterValue(frontmatter: string, key: string): string | null {
5418
+ const match = frontmatter.match(new RegExp(`^${key}:\\s*"?([^"\\n]*)"?`, "m"));
5522
5419
  return match ? match[1] : null;
5523
5420
  }
5524
5421
 
@@ -5529,10 +5426,7 @@ export class StorageManager {
5529
5426
 
5530
5427
  let raw = await this.readStorageSecureFile(q.filePath);
5531
5428
  raw = raw.replace(/resolved: false/, "resolved: true");
5532
- raw = raw.replace(
5533
- /---\n\n/,
5534
- `resolvedAt: "${new Date().toISOString()}"\n---\n\n`,
5535
- );
5429
+ raw = raw.replace(/---\n\n/, `resolvedAt: "${new Date().toISOString()}"\n---\n\n`);
5536
5430
  await this.writeStorageSecureFile(q.filePath, raw);
5537
5431
  this.invalidateQuestionsCache();
5538
5432
  log.debug(`resolved question ${id}`);
@@ -5566,7 +5460,7 @@ export class StorageManager {
5566
5460
  async appendToIdentity(
5567
5461
  workspaceDir: string,
5568
5462
  reflection: string,
5569
- opts?: { hygiene?: FileHygieneConfig; namespace?: string },
5463
+ opts?: { hygiene?: FileHygieneConfig; namespace?: string }
5570
5464
  ): Promise<void> {
5571
5465
  const identityPath = this.identityFilePath(workspaceDir, opts?.namespace);
5572
5466
 
@@ -5597,14 +5491,14 @@ export class StorageManager {
5597
5491
  });
5598
5492
  await writeFile(identityPath, newContent, "utf-8");
5599
5493
  existing = newContent;
5600
- log.info(
5601
- `rotated IDENTITY.md to archive (size=${existing.length} chars, maxBytes=${maxBytes})`,
5602
- );
5494
+ log.info(`rotated IDENTITY.md to archive (size=${existing.length} chars, maxBytes=${maxBytes})`);
5603
5495
  }
5604
5496
  } else {
5605
5497
  // Legacy behavior: skip if file is too large
5606
5498
  if (existing.length > StorageManager.IDENTITY_MAX_BYTES) {
5607
- log.debug(`IDENTITY.md is ${existing.length} chars (limit ${StorageManager.IDENTITY_MAX_BYTES}); skipping reflection`);
5499
+ log.debug(
5500
+ `IDENTITY.md is ${existing.length} chars (limit ${StorageManager.IDENTITY_MAX_BYTES}); skipping reflection`
5501
+ );
5608
5502
  return;
5609
5503
  }
5610
5504
  }
@@ -5618,7 +5512,9 @@ export class StorageManager {
5618
5512
  const lastTimestamp = allMatches[allMatches.length - 1][1];
5619
5513
  const elapsed = Date.now() - new Date(lastTimestamp).getTime();
5620
5514
  if (elapsed < StorageManager.REFLECTION_COOLDOWN_MS) {
5621
- log.debug(`reflection cooldown: ${Math.round(elapsed / 1000)}s since last (need ${StorageManager.REFLECTION_COOLDOWN_MS / 1000}s)`);
5515
+ log.debug(
5516
+ `reflection cooldown: ${Math.round(elapsed / 1000)}s since last (need ${StorageManager.REFLECTION_COOLDOWN_MS / 1000}s)`
5517
+ );
5622
5518
  return;
5623
5519
  }
5624
5520
  }
@@ -5656,7 +5552,7 @@ export class StorageManager {
5656
5552
 
5657
5553
  if (existing.length > StorageManager.IDENTITY_MAX_BYTES) {
5658
5554
  log.debug(
5659
- `identity/reflections.md is ${existing.length} chars (limit ${StorageManager.IDENTITY_MAX_BYTES}); skipping reflection`,
5555
+ `identity/reflections.md is ${existing.length} chars (limit ${StorageManager.IDENTITY_MAX_BYTES}); skipping reflection`
5660
5556
  );
5661
5557
  return;
5662
5558
  }
@@ -5667,7 +5563,7 @@ export class StorageManager {
5667
5563
  const elapsed = Date.now() - new Date(lastTimestamp).getTime();
5668
5564
  if (elapsed < StorageManager.REFLECTION_COOLDOWN_MS) {
5669
5565
  log.debug(
5670
- `reflection cooldown: ${Math.round(elapsed / 1000)}s since last (need ${StorageManager.REFLECTION_COOLDOWN_MS / 1000}s)`,
5566
+ `reflection cooldown: ${Math.round(elapsed / 1000)}s since last (need ${StorageManager.REFLECTION_COOLDOWN_MS / 1000}s)`
5671
5567
  );
5672
5568
  return;
5673
5569
  }
@@ -5702,9 +5598,7 @@ export class StorageManager {
5702
5598
  }
5703
5599
 
5704
5600
  // Dedupe by target+label
5705
- const exists = entity.relationships.some(
5706
- (r) => r.target === rel.target && r.label === rel.label,
5707
- );
5601
+ const exists = entity.relationships.some((r) => r.target === rel.target && r.label === rel.label);
5708
5602
  if (exists) return;
5709
5603
 
5710
5604
  entity.relationships.push(rel);
@@ -5717,11 +5611,7 @@ export class StorageManager {
5717
5611
  * Add an activity entry to an entity file.
5718
5612
  * Prepends to the beginning, prunes oldest entries beyond maxEntries.
5719
5613
  */
5720
- async addEntityActivity(
5721
- name: string,
5722
- entry: EntityActivityEntry,
5723
- maxEntries: number,
5724
- ): Promise<void> {
5614
+ async addEntityActivity(name: string, entry: EntityActivityEntry, maxEntries: number): Promise<void> {
5725
5615
  const filePath = path.join(this.entitiesDir, `${name}.md`);
5726
5616
  let entity: EntityFile;
5727
5617
  try {
@@ -5776,13 +5666,9 @@ export class StorageManager {
5776
5666
  synthesisTimelineCount?: number;
5777
5667
  updatedAt?: string;
5778
5668
  incrementVersion?: boolean;
5779
- } = {},
5669
+ } = {}
5780
5670
  ): Promise<void> {
5781
- return this.entityStore.updateEntitySynthesis(
5782
- name,
5783
- synthesis,
5784
- options,
5785
- );
5671
+ return this.entityStore.updateEntitySynthesis(name, synthesis, options);
5786
5672
  }
5787
5673
 
5788
5674
  /**
@@ -5831,7 +5717,7 @@ export class StorageManager {
5831
5717
  entityName,
5832
5718
  entity: parseEntityFile(raw, this.entitySchemas),
5833
5719
  };
5834
- }),
5720
+ })
5835
5721
  );
5836
5722
  const staleEntityNames = entityQueueEntries
5837
5723
  .filter((entry): entry is { entityName: string; entity: EntityFile } => entry !== null)
@@ -5852,8 +5738,8 @@ export class StorageManager {
5852
5738
  entityNames: staleEntityNames,
5853
5739
  },
5854
5740
  null,
5855
- 2,
5856
- ) + "\n",
5741
+ 2
5742
+ ) + "\n"
5857
5743
  );
5858
5744
  return staleEntityNames;
5859
5745
  }
@@ -5873,8 +5759,8 @@ export class StorageManager {
5873
5759
  entityNames: nextQueue,
5874
5760
  },
5875
5761
  null,
5876
- 2,
5877
- ) + "\n",
5762
+ 2
5763
+ ) + "\n"
5878
5764
  );
5879
5765
  }
5880
5766
 
@@ -5907,8 +5793,7 @@ export class StorageManager {
5907
5793
  // ---------------------------------------------------------------------------
5908
5794
 
5909
5795
  async readAllEntityFiles(): Promise<EntityFile[]> {
5910
- return this.entityStore.readAllEntityFiles(
5911
- );
5796
+ return this.entityStore.readAllEntityFiles();
5912
5797
  }
5913
5798
 
5914
5799
  /**
@@ -5943,23 +5828,14 @@ export class StorageManager {
5943
5828
  // Relationship density: min(relationships.length / 8, 1.0)
5944
5829
  const relDensity = Math.min(entity.relationships.length / 8, 1.0);
5945
5830
 
5946
- return (
5947
- recency * 0.40 +
5948
- frequency * 0.25 +
5949
- activityScore * 0.15 +
5950
- typePriority * 0.10 +
5951
- relDensity * 0.10
5952
- );
5831
+ return recency * 0.4 + frequency * 0.25 + activityScore * 0.15 + typePriority * 0.1 + relDensity * 0.1;
5953
5832
  }
5954
5833
 
5955
5834
  async buildKnowledgeIndex(
5956
5835
  config: PluginConfig,
5957
- overrides?: { maxEntities?: number; maxChars?: number },
5836
+ overrides?: { maxEntities?: number; maxChars?: number }
5958
5837
  ): Promise<{ result: string; cached: boolean }> {
5959
- return this.entityStore.buildKnowledgeIndex(
5960
- config,
5961
- overrides,
5962
- );
5838
+ return this.entityStore.buildKnowledgeIndex(config, overrides);
5963
5839
  }
5964
5840
 
5965
5841
  /** Invalidate the Knowledge Index cache (call after entity mutations). */
@@ -5975,8 +5851,7 @@ export class StorageManager {
5975
5851
  private static readonly PROFILE_MAX_LINES = 300;
5976
5852
 
5977
5853
  async mergeFragmentedEntities(): Promise<number> {
5978
- return this.entityStore.mergeFragmentedEntities(
5979
- );
5854
+ return this.entityStore.mergeFragmentedEntities();
5980
5855
  }
5981
5856
 
5982
5857
  async cleanExpiredCommitments(decayDays: number): Promise<MemoryFile[]> {
@@ -5988,16 +5863,15 @@ export class StorageManager {
5988
5863
  if (m.frontmatter.category !== "commitment") continue;
5989
5864
  // Only decay commitments that have been marked as resolved/expired
5990
5865
  // (indicated by tags containing "fulfilled" or "expired")
5991
- const isResolved = m.frontmatter.tags.some(
5992
- (t) => t === "fulfilled" || t === "expired",
5993
- );
5866
+ const isResolved = m.frontmatter.tags.some((t) => t === "fulfilled" || t === "expired");
5994
5867
  if (!isResolved) continue;
5995
5868
 
5996
5869
  const updatedAt = new Date(m.frontmatter.updated).getTime();
5997
5870
  if (updatedAt < cutoff) {
5998
5871
  // Remove the file
5999
5872
  try {
6000
- await unlink(m.path); markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
5873
+ await unlink(m.path);
5874
+ markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
6001
5875
  deleted.push(m);
6002
5876
  log.debug(`cleaned expired commitment ${m.frontmatter.id}`);
6003
5877
  } catch {
@@ -6088,7 +5962,8 @@ export class StorageManager {
6088
5962
 
6089
5963
  async getProjectedMemoryState(id: string): Promise<MemoryProjectionCurrentState | null> {
6090
5964
  const projected = readProjectedMemoryState(this.baseDir, id);
6091
- if (projected) return projected; warnProjectionFallback(this.baseDir, "getProjectedMemoryState");
5965
+ if (projected) return projected;
5966
+ warnProjectionFallback(this.baseDir, "getProjectedMemoryState");
6092
5967
  const active = await this.getMemoryById(id);
6093
5968
  if (active) return this.toProjectedCurrentState(active, "active");
6094
5969
 
@@ -6098,21 +5973,18 @@ export class StorageManager {
6098
5973
  return this.toProjectedCurrentState(archived, "archived");
6099
5974
  }
6100
5975
 
6101
- async browseProjectedMemories(
6102
- options: ProjectedMemoryBrowseOptions,
6103
- ): Promise<ProjectedMemoryBrowsePage | null> {
6104
- return readProjectedMemoryBrowse(this.baseDir, options)
6105
- ?? warnProjectionFallback(this.baseDir, "browseProjectedMemories");
5976
+ async browseProjectedMemories(options: ProjectedMemoryBrowseOptions): Promise<ProjectedMemoryBrowsePage | null> {
5977
+ return (
5978
+ readProjectedMemoryBrowse(this.baseDir, options) ??
5979
+ warnProjectionFallback(this.baseDir, "browseProjectedMemories")
5980
+ );
6106
5981
  }
6107
5982
 
6108
5983
  async getProjectedGovernanceRecord(): Promise<ReturnType<typeof readProjectedGovernanceRecord>> {
6109
5984
  return readProjectedGovernanceRecord(this.baseDir);
6110
5985
  }
6111
5986
 
6112
- private toProjectedCurrentState(
6113
- memory: MemoryFile,
6114
- fallbackStatus: MemoryStatus,
6115
- ): MemoryProjectionCurrentState {
5987
+ private toProjectedCurrentState(memory: MemoryFile, fallbackStatus: MemoryStatus): MemoryProjectionCurrentState {
6116
5988
  const pathRel = toMemoryPathRel(this.baseDir, memory.path);
6117
5989
  return {
6118
5990
  memoryId: memory.frontmatter.id,
@@ -6142,7 +6014,8 @@ export class StorageManager {
6142
6014
  if (cappedLimit === 0) return [];
6143
6015
 
6144
6016
  const projected = readProjectedMemoryTimeline(this.baseDir, memoryId, cappedLimit);
6145
- if (projected && projected.length > 0) return projected; warnProjectionFallback(this.baseDir, "getMemoryTimeline");
6017
+ if (projected && projected.length > 0) return projected;
6018
+ warnProjectionFallback(this.baseDir, "getMemoryTimeline");
6146
6019
  const events = await this.readAllMemoryLifecycleEvents();
6147
6020
  return events.filter((event) => event.memoryId === memoryId).slice(-cappedLimit);
6148
6021
  }
@@ -6193,7 +6066,8 @@ export class StorageManager {
6193
6066
  invalidAt?: string;
6194
6067
  observedAt?: string;
6195
6068
  eventTimeSource?: "extracted" | "assumed";
6196
- } = {},
6069
+ sourceConnector?: string;
6070
+ } = {}
6197
6071
  ): Promise<string> {
6198
6072
  await this.ensureDirectories();
6199
6073
  const now = new Date();
@@ -6232,6 +6106,7 @@ export class StorageManager {
6232
6106
  ...(options.faithfulness ? { faithfulness: options.faithfulness } : {}),
6233
6107
  ...(options.sources ? { sources: options.sources } : {}),
6234
6108
  ...(options.provenance ? { provenance: options.provenance } : {}),
6109
+ ...(options.sourceConnector ? { sourceConnector: options.sourceConnector } : {}),
6235
6110
  };
6236
6111
 
6237
6112
  const sanitized = sanitizeMemoryContent(content);
@@ -6266,11 +6141,7 @@ export class StorageManager {
6266
6141
  * Mark a memory as superseded by another.
6267
6142
  * Updates the old memory's status and adds the supersededBy link.
6268
6143
  */
6269
- async supersedeMemory(
6270
- oldMemoryId: string,
6271
- newMemoryId: string,
6272
- reason: string,
6273
- ): Promise<boolean> {
6144
+ async supersedeMemory(oldMemoryId: string, newMemoryId: string, reason: string): Promise<boolean> {
6274
6145
  const memories = await this.readAllMemories();
6275
6146
  const oldMemory = memories.find((m) => m.frontmatter.id === oldMemoryId);
6276
6147
  if (!oldMemory) return false;
@@ -6317,7 +6188,12 @@ export class StorageManager {
6317
6188
  entityRef: oldMemory.frontmatter.entityRef,
6318
6189
  structuredAttributes: oldMemory.frontmatter.structuredAttributes,
6319
6190
  },
6320
- { reason: "contradiction_resolution", createdBy: "contradiction_resolution", createdAt: now, supersessionKeysForFact },
6191
+ {
6192
+ reason: "contradiction_resolution",
6193
+ createdBy: "contradiction_resolution",
6194
+ createdAt: now,
6195
+ supersessionKeysForFact,
6196
+ }
6321
6197
  )) {
6322
6198
  await this.appendTombstone(input);
6323
6199
  }
@@ -6435,7 +6311,10 @@ export class StorageManager {
6435
6311
  async saveTopics(topics: TopicScore[]): Promise<void> {
6436
6312
  const metaPath = path.join(this.stateDir, "topics.json");
6437
6313
  await mkdir(this.stateDir, { recursive: true });
6438
- await this.writeStorageSecureFile(metaPath, JSON.stringify({ topics, updatedAt: new Date().toISOString() }, null, 2));
6314
+ await this.writeStorageSecureFile(
6315
+ metaPath,
6316
+ JSON.stringify({ topics, updatedAt: new Date().toISOString() }, null, 2)
6317
+ );
6439
6318
  log.debug(`saved ${topics.length} topic scores`);
6440
6319
  }
6441
6320
 
@@ -6460,7 +6339,7 @@ export class StorageManager {
6460
6339
  async addLinksToMemory(
6461
6340
  memoryId: string,
6462
6341
  links: MemoryLink[],
6463
- lifecycle?: MemoryLifecycleEventWriteOptions,
6342
+ lifecycle?: MemoryLifecycleEventWriteOptions
6464
6343
  ): Promise<boolean> {
6465
6344
  const memories = await this.readAllMemories();
6466
6345
  const memory = memories.find((m) => m.frontmatter.id === memoryId);
@@ -6483,7 +6362,7 @@ export class StorageManager {
6483
6362
  links: mergedLinks,
6484
6363
  updated: new Date().toISOString(),
6485
6364
  },
6486
- lifecycle,
6365
+ lifecycle
6487
6366
  );
6488
6367
  log.debug(`added ${links.length} links to memory ${memoryId}`);
6489
6368
  return true;
@@ -6493,10 +6372,7 @@ export class StorageManager {
6493
6372
  }
6494
6373
  }
6495
6374
 
6496
- private summarizeLifecycleState(
6497
- frontmatter: MemoryFrontmatter,
6498
- filePath: string,
6499
- ): MemoryLifecycleStateSummary {
6375
+ private summarizeLifecycleState(frontmatter: MemoryFrontmatter, filePath: string): MemoryLifecycleStateSummary {
6500
6376
  return {
6501
6377
  category: frontmatter.category,
6502
6378
  path: filePath,
@@ -6505,10 +6381,7 @@ export class StorageManager {
6505
6381
  };
6506
6382
  }
6507
6383
 
6508
- private frontmatterPatchEventType(
6509
- before: MemoryFrontmatter,
6510
- after: MemoryFrontmatter,
6511
- ): MemoryLifecycleEventType {
6384
+ private frontmatterPatchEventType(before: MemoryFrontmatter, after: MemoryFrontmatter): MemoryLifecycleEventType {
6512
6385
  const beforeStatus = before.status ?? "active";
6513
6386
  const afterStatus = after.status ?? "active";
6514
6387
  if (beforeStatus !== "archived" && afterStatus === "archived") return "archived";
@@ -6522,7 +6395,7 @@ export class StorageManager {
6522
6395
 
6523
6396
  private async appendGeneratedMemoryLifecycleEvent(
6524
6397
  input: Omit<MemoryLifecycleEvent, "eventId" | "ruleVersion">,
6525
- ruleVersion = "memory-lifecycle-ledger.v1",
6398
+ ruleVersion = "memory-lifecycle-ledger.v1"
6526
6399
  ): Promise<void> {
6527
6400
  await this.appendMemoryLifecycleEvents([
6528
6401
  {
@@ -6536,7 +6409,7 @@ export class StorageManager {
6536
6409
  private async appendGeneratedMemoryLifecycleEventFailOpen(
6537
6410
  operation: string,
6538
6411
  input: Omit<MemoryLifecycleEvent, "eventId" | "ruleVersion">,
6539
- ruleVersion?: string,
6412
+ ruleVersion?: string
6540
6413
  ): Promise<void> {
6541
6414
  try {
6542
6415
  await this.appendGeneratedMemoryLifecycleEvent(input, ruleVersion);