@remnic/core 9.52.2 → 9.54.0

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 (429) hide show
  1. package/dist/access-admin-ops-surface.d.ts +11 -11
  2. package/dist/access-admin-ops-surface.js +22 -22
  3. package/dist/access-authorization-probe.d.ts +11 -11
  4. package/dist/access-authorization-probe.js +23 -23
  5. package/dist/access-boundary.d.ts +11 -11
  6. package/dist/access-boundary.js +22 -22
  7. package/dist/access-cli.js +55 -55
  8. package/dist/access-coding-context-resolution.d.ts +1 -1
  9. package/dist/access-extraction-force-flush.d.ts +11 -11
  10. package/dist/access-extraction-force-flush.js +22 -22
  11. package/dist/access-health-types.d.ts +1 -1
  12. package/dist/access-http-lcm-compaction.d.ts +11 -11
  13. package/dist/access-http-lifecycle-flush.d.ts +11 -11
  14. package/dist/access-http-offline-stream.d.ts +11 -11
  15. package/dist/access-http-query.js +22 -22
  16. package/dist/access-http.d.ts +11 -11
  17. package/dist/access-http.js +29 -29
  18. package/dist/access-identity-continuity-surface.d.ts +9 -9
  19. package/dist/access-identity-continuity-surface.js +22 -22
  20. package/dist/access-lcm-surface.d.ts +11 -11
  21. package/dist/access-lcm-surface.js +22 -22
  22. package/dist/access-mcp.d.ts +11 -11
  23. package/dist/access-mcp.js +27 -27
  24. package/dist/access-memory-search-fanout.d.ts +2 -2
  25. package/dist/{access-namespace-preflight-Ds6CGlCP.d.ts → access-namespace-preflight-C7n4tgWC.d.ts} +1 -1
  26. package/dist/access-namespace-preflight.d.ts +3 -3
  27. package/dist/access-namespace-preflight.js +22 -22
  28. package/dist/access-observe-write-surface.d.ts +11 -11
  29. package/dist/access-observe-write-surface.js +22 -22
  30. package/dist/access-offline-manifest.d.ts +9 -9
  31. package/dist/access-offline-manifest.js +8 -4
  32. package/dist/access-operations-batch.js +24 -24
  33. package/dist/access-operations.d.ts +14 -14
  34. package/dist/access-operations.js +26 -26
  35. package/dist/access-recall-concurrency.d.ts +11 -11
  36. package/dist/access-recall-concurrency.js +22 -22
  37. package/dist/access-recall-response.d.ts +11 -11
  38. package/dist/access-recall-response.js +22 -22
  39. package/dist/access-recall-surface.d.ts +11 -11
  40. package/dist/access-recall-surface.js +22 -22
  41. package/dist/access-schema.d.ts +88 -88
  42. package/dist/{access-service-Cbk3bNbD.d.ts → access-service-CXvbxLK-.d.ts} +7 -7
  43. package/dist/access-service-helpers.d.ts +11 -11
  44. package/dist/access-service.d.ts +11 -11
  45. package/dist/access-service.js +22 -22
  46. package/dist/access-surface-catalog.d.ts +11 -11
  47. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  48. package/dist/action-confidence.d.ts +1 -1
  49. package/dist/active-memory-bridge.d.ts +1 -1
  50. package/dist/active-recall.d.ts +1 -1
  51. package/dist/active-recall.js +5 -5
  52. package/dist/ambient-provenance.d.ts +1 -1
  53. package/dist/artifact-search.d.ts +1 -1
  54. package/dist/{auto-sync-QFQGOEO2.js → auto-sync-TDHLZPVP.js} +7 -7
  55. package/dist/behavior-learner.d.ts +1 -1
  56. package/dist/behavior-signals.d.ts +1 -1
  57. package/dist/bootstrap.d.ts +9 -9
  58. package/dist/briefing.d.ts +2 -2
  59. package/dist/briefing.js +12 -12
  60. package/dist/buffer-surprise-report.d.ts +1 -1
  61. package/dist/buffer-turn-helpers.d.ts +1 -1
  62. package/dist/buffer.d.ts +2 -2
  63. package/dist/bulk-import/index.d.ts +3 -3
  64. package/dist/calibration.d.ts +1 -1
  65. package/dist/capabilities.d.ts +1 -1
  66. package/dist/{catalog-D3B4RDy1.d.ts → catalog-BOOxl-I2.d.ts} +1 -1
  67. package/dist/causal-behavior.d.ts +1 -1
  68. package/dist/causal-consolidation.d.ts +1 -1
  69. package/dist/causal-consolidation.js +13 -13
  70. package/dist/causal-trajectory-graph.d.ts +1 -1
  71. package/dist/{chunk-MLXZD4WM.js → chunk-2GFK22OZ.js} +2 -2
  72. package/dist/{chunk-RANKJI5T.js → chunk-3JLPGJ43.js} +2 -2
  73. package/dist/{chunk-O5FFBO2M.js → chunk-47P3RYZP.js} +70 -8
  74. package/dist/{chunk-O5FFBO2M.js.map → chunk-47P3RYZP.js.map} +1 -1
  75. package/dist/{chunk-X7XEDOSL.js → chunk-4IIFL7GC.js} +8 -8
  76. package/dist/chunk-5FVVY2EB.js +27 -0
  77. package/dist/chunk-5FVVY2EB.js.map +1 -0
  78. package/dist/{chunk-GXSOUIA4.js → chunk-5WQMORU5.js} +4 -4
  79. package/dist/{chunk-J272WKX2.js → chunk-6HPHEFXT.js} +2 -2
  80. package/dist/{chunk-B6LZ4IBH.js → chunk-6LAPLZOS.js} +2 -2
  81. package/dist/{chunk-WD5B5AIE.js → chunk-6R4G7N4S.js} +118 -173
  82. package/dist/chunk-6R4G7N4S.js.map +1 -0
  83. package/dist/{chunk-VXCNQYTC.js → chunk-APPFNGX6.js} +2 -2
  84. package/dist/{chunk-GXFSQ6SD.js → chunk-C4ZNGVK3.js} +2 -2
  85. package/dist/{chunk-JSEJH2YQ.js → chunk-CGVHPCP2.js} +2 -2
  86. package/dist/{chunk-BQIFZ2MP.js → chunk-CX5SW2EW.js} +41 -24
  87. package/dist/chunk-CX5SW2EW.js.map +1 -0
  88. package/dist/{chunk-RMHMUUBW.js → chunk-CYF6WVE7.js} +105 -49
  89. package/dist/{chunk-RMHMUUBW.js.map → chunk-CYF6WVE7.js.map} +1 -1
  90. package/dist/{chunk-TEMSWHQD.js → chunk-DZD576ZU.js} +5 -5
  91. package/dist/{chunk-AMTEYNYK.js → chunk-EVWWT3KE.js} +4 -4
  92. package/dist/{chunk-P2KBDJJ6.js → chunk-G2LUMAUF.js} +4 -3
  93. package/dist/{chunk-P2KBDJJ6.js.map → chunk-G2LUMAUF.js.map} +1 -1
  94. package/dist/{chunk-AXIDQG5A.js → chunk-G4T7L557.js} +5 -5
  95. package/dist/{chunk-7M6NYQWB.js → chunk-G7Z6EENO.js} +4 -4
  96. package/dist/{chunk-IARXXZU4.js → chunk-HOIRB77A.js} +99 -20
  97. package/dist/chunk-HOIRB77A.js.map +1 -0
  98. package/dist/{chunk-NDAH7BJ5.js → chunk-HVP33QVW.js} +103 -6
  99. package/dist/chunk-HVP33QVW.js.map +1 -0
  100. package/dist/{chunk-YXKVLYXU.js → chunk-HWRD23DW.js} +2 -2
  101. package/dist/{chunk-SOEN6KCF.js → chunk-IBKR2TFR.js} +2 -2
  102. package/dist/{chunk-R3DJVOSM.js → chunk-JDSVLVCG.js} +8 -1
  103. package/dist/chunk-JDSVLVCG.js.map +1 -0
  104. package/dist/{chunk-F4EU6VUC.js → chunk-JDZPNCMA.js} +2 -2
  105. package/dist/{chunk-D5YOZ66N.js → chunk-JV5YUBIZ.js} +20 -20
  106. package/dist/{chunk-DLG5V6ND.js → chunk-K4QS7LXU.js} +4 -7
  107. package/dist/chunk-K4QS7LXU.js.map +1 -0
  108. package/dist/{chunk-BAKXNQ3C.js → chunk-KRTY3ENI.js} +2 -2
  109. package/dist/{chunk-NK5RGJ65.js → chunk-L4DLLKPN.js} +2 -2
  110. package/dist/{chunk-VFSAWRMZ.js → chunk-LJS7KUPV.js} +2 -2
  111. package/dist/{chunk-JJRVPWFY.js → chunk-MLPTFODV.js} +166 -37
  112. package/dist/chunk-MLPTFODV.js.map +1 -0
  113. package/dist/{chunk-NOEMGE56.js → chunk-NH5IR2WM.js} +2 -2
  114. package/dist/{chunk-5V6IXHPI.js → chunk-NSVDUN76.js} +2 -2
  115. package/dist/{chunk-W7Y5JDVB.js → chunk-PDDTI7IC.js} +4 -4
  116. package/dist/{chunk-QINJITDS.js → chunk-PX2LSHYM.js} +2 -2
  117. package/dist/{chunk-HDIUYMSB.js → chunk-QWGE2PUH.js} +6 -6
  118. package/dist/{chunk-X57M2Q4I.js → chunk-RDJRFDJY.js} +3 -3
  119. package/dist/{chunk-RYLEDKQ2.js → chunk-RUMNY5DT.js} +3 -3
  120. package/dist/{chunk-BKIFTXHR.js → chunk-RZ4XGKOJ.js} +2 -2
  121. package/dist/{chunk-66SE3BNH.js → chunk-SUPEYLH2.js} +2 -2
  122. package/dist/{chunk-BF6SXNQJ.js → chunk-T2JHZWH6.js} +20 -20
  123. package/dist/{chunk-VHFXP27N.js → chunk-THPPCISA.js} +60 -23
  124. package/dist/chunk-THPPCISA.js.map +1 -0
  125. package/dist/{chunk-QPOHMO6M.js → chunk-UES4BHA2.js} +2 -2
  126. package/dist/{chunk-XAPMEU3K.js → chunk-UU7TRFNE.js} +2 -2
  127. package/dist/{chunk-QBSFOKY6.js → chunk-VOELBUS4.js} +4 -4
  128. package/dist/{chunk-2LY6GIIE.js → chunk-WDBX6VER.js} +2 -2
  129. package/dist/{chunk-BMEYMKWX.js → chunk-WM65LU67.js} +2 -2
  130. package/dist/{chunk-WLRD2TXF.js → chunk-WOPA3SGI.js} +4 -4
  131. package/dist/{chunk-5UZ6UCOZ.js → chunk-XUAMCLY4.js} +3 -3
  132. package/dist/{chunk-7HXZJAID.js → chunk-YGLY6QFV.js} +3 -3
  133. package/dist/{chunk-2OIQDTDC.js → chunk-ZPJ4QSGP.js} +2 -2
  134. package/dist/{chunk-3OY7KXHM.js → chunk-ZYLC7NFO.js} +3 -3
  135. package/dist/{cli-DMAFI0qt.d.ts → cli-DVUFIa5E.d.ts} +5 -5
  136. package/dist/cli.d.ts +13 -13
  137. package/dist/cli.js +42 -42
  138. package/dist/coding/pre-action-gate.d.ts +1 -1
  139. package/dist/compounding/engine.d.ts +2 -2
  140. package/dist/compounding/engine.js +13 -13
  141. package/dist/compounding/preference-consolidator.d.ts +1 -1
  142. package/dist/compression-optimizer.d.ts +1 -1
  143. package/dist/config.d.ts +1 -1
  144. package/dist/config.js +5 -5
  145. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  146. package/dist/connectors/codex-materialize-runner.js +12 -12
  147. package/dist/connectors/codex-materialize.d.ts +1 -1
  148. package/dist/connectors/index.d.ts +1 -1
  149. package/dist/connectors/index.js +12 -12
  150. package/dist/consolidation-provenance-check.d.ts +2 -2
  151. package/dist/consolidation-undo.d.ts +2 -2
  152. package/dist/content-hash.d.ts +12 -2
  153. package/dist/content-hash.js +9 -3
  154. package/dist/contradiction/index.d.ts +2 -2
  155. package/dist/contradiction/index.js +3 -3
  156. package/dist/converge-config.d.ts +1 -1
  157. package/dist/convergence-refresh.d.ts +2 -2
  158. package/dist/convergence-refresh.js +14 -14
  159. package/dist/conversation-index/backend.d.ts +1 -1
  160. package/dist/conversation-index/chunker.d.ts +1 -1
  161. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  162. package/dist/conversation-index/indexer.d.ts +1 -1
  163. package/dist/conversation-index/search.d.ts +1 -1
  164. package/dist/corpus-watermark.d.ts +1 -1
  165. package/dist/corpus-watermark.js +14 -14
  166. package/dist/day-summary.d.ts +1 -1
  167. package/dist/delinearize.d.ts +1 -1
  168. package/dist/dependency-propagation-config.d.ts +1 -1
  169. package/dist/{dependency-propagation-delivery-D63r24pH.d.ts → dependency-propagation-delivery-XzF76C9n.d.ts} +1 -1
  170. package/dist/direct-answer-wiring.d.ts +1 -1
  171. package/dist/direct-answer.d.ts +1 -1
  172. package/dist/embedding-fallback.d.ts +1 -1
  173. package/dist/enrichment/index.d.ts +1 -1
  174. package/dist/entity-retrieval.d.ts +2 -2
  175. package/dist/entity-retrieval.js +12 -12
  176. package/dist/entity-schema.d.ts +1 -1
  177. package/dist/explicit-capture.d.ts +9 -9
  178. package/dist/explicit-capture.js +8 -8
  179. package/dist/external-wiki-access.d.ts +11 -11
  180. package/dist/external-wiki-access.js +23 -23
  181. package/dist/external-wiki-collection-registration.d.ts +1 -1
  182. package/dist/external-wiki-collection.d.ts +1 -1
  183. package/dist/external-wiki-mcp-tools.d.ts +11 -11
  184. package/dist/extraction-error-classification.d.ts +1 -1
  185. package/dist/extraction-faithfulness.d.ts +1 -1
  186. package/dist/extraction-judge-telemetry.d.ts +1 -1
  187. package/dist/extraction-judge-training.d.ts +1 -1
  188. package/dist/extraction-judge.d.ts +1 -1
  189. package/dist/extraction-liveness.d.ts +1 -1
  190. package/dist/extraction-normalization.d.ts +1 -1
  191. package/dist/extraction-normalization.js +2 -2
  192. package/dist/extraction-prompt.d.ts +1 -1
  193. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  194. package/dist/extraction-source-grounding.d.ts +1 -1
  195. package/dist/extraction-source-grounding.js +3 -3
  196. package/dist/extraction.d.ts +1 -1
  197. package/dist/extraction.js +16 -16
  198. package/dist/fallback-llm.d.ts +1 -1
  199. package/dist/{forget-HDS4N6CG.js → forget-EHGGJZLX.js} +3 -2
  200. package/dist/{forget-HDS4N6CG.js.map → forget-EHGGJZLX.js.map} +1 -1
  201. package/dist/graph-dashboard-diff.d.ts +1 -1
  202. package/dist/graph-dashboard-key.d.ts +1 -1
  203. package/dist/graph-dashboard-parser.d.ts +1 -1
  204. package/dist/graph-edge-reinforcement.d.ts +1 -1
  205. package/dist/graph-path-reconstruction.d.ts +1 -1
  206. package/dist/graph-path-scoring.d.ts +1 -1
  207. package/dist/graph-snapshot.d.ts +1 -1
  208. package/dist/graph.d.ts +1 -1
  209. package/dist/harmonic-construction.js +2 -2
  210. package/dist/harmonic-retrieval.js +4 -4
  211. package/dist/importance.d.ts +1 -1
  212. package/dist/importers/index.d.ts +1 -1
  213. package/dist/in-flight-reads.d.ts +1 -1
  214. package/dist/index.d.ts +777 -705
  215. package/dist/index.js +77 -67
  216. package/dist/intent.d.ts +1 -1
  217. package/dist/lcm/engine.d.ts +1 -1
  218. package/dist/lcm/index.d.ts +1 -1
  219. package/dist/lcm/tools.d.ts +1 -1
  220. package/dist/lifecycle.d.ts +1 -1
  221. package/dist/live-connectors-runner.d.ts +1 -1
  222. package/dist/local-llm.d.ts +1 -1
  223. package/dist/local-model-endpoint.d.ts +1 -1
  224. package/dist/maintenance/memory-governance.d.ts +1 -1
  225. package/dist/maintenance/memory-governance.js +12 -12
  226. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  227. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -12
  228. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  229. package/dist/maintenance/rebuild-memory-projection.js +13 -13
  230. package/dist/{maintenance-kBII2qUI.d.ts → maintenance-BJzNIKBE.d.ts} +3 -3
  231. package/dist/mcp-memory-inspector-app.d.ts +11 -11
  232. package/dist/memory-action-policy.d.ts +1 -1
  233. package/dist/memory-cache.d.ts +1 -1
  234. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  235. package/dist/memory-projection-store.d.ts +1 -1
  236. package/dist/memory-provenance.d.ts +1 -1
  237. package/dist/memory-snapshot.d.ts +1 -1
  238. package/dist/memory-worth-outcomes.d.ts +2 -2
  239. package/dist/models-json.d.ts +1 -1
  240. package/dist/namespaces/migrate.d.ts +3 -3
  241. package/dist/namespaces/migrate.js +13 -13
  242. package/dist/namespaces/principal.d.ts +1 -1
  243. package/dist/namespaces/search.d.ts +1 -1
  244. package/dist/namespaces/storage.d.ts +3 -3
  245. package/dist/namespaces/storage.js +12 -12
  246. package/dist/native-knowledge.d.ts +1 -1
  247. package/dist/offline-sync-impression-drain.d.ts +1 -1
  248. package/dist/operator-doctor-corpus.d.ts +1 -1
  249. package/dist/operator-doctor-corpus.js +15 -15
  250. package/dist/operator-doctor-replica.d.ts +1 -1
  251. package/dist/operator-toolkit.d.ts +3 -3
  252. package/dist/operator-toolkit.js +21 -21
  253. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  254. package/dist/orchestration/maintenance.d.ts +4 -4
  255. package/dist/orchestration/maintenance.js +17 -17
  256. package/dist/{orchestrator-DHy5LtTy.d.ts → orchestrator-BiXgBJBQ.d.ts} +8 -8
  257. package/dist/orchestrator.d.ts +9 -9
  258. package/dist/orchestrator.js +55 -55
  259. package/dist/patterns-cli.d.ts +1 -1
  260. package/dist/{pipeline-D21Rs6gW.d.ts → pipeline-sz_dcuLg.d.ts} +1 -1
  261. package/dist/policy-runtime.d.ts +1 -1
  262. package/dist/proactive-contention.d.ts +1 -1
  263. package/dist/provenance.d.ts +1 -1
  264. package/dist/provenance.js +2 -2
  265. package/dist/{qmd-Dhle1P4X.d.ts → qmd-eAWObK4M.d.ts} +1 -1
  266. package/dist/qmd-preflight.d.ts +2 -2
  267. package/dist/qmd-recall-cache.d.ts +1 -1
  268. package/dist/qmd.d.ts +2 -2
  269. package/dist/recall-concurrency-config.d.ts +1 -1
  270. package/dist/recall-disclosure-escalation.d.ts +1 -1
  271. package/dist/recall-explain-renderer.d.ts +1 -1
  272. package/dist/recall-memory-map.d.ts +1 -1
  273. package/dist/recall-planner-llm.d.ts +1 -1
  274. package/dist/recall-state.d.ts +1 -1
  275. package/dist/recall-tag-filter.d.ts +1 -1
  276. package/dist/recall-timings.d.ts +1 -1
  277. package/dist/recall-xray-cli.d.ts +1 -1
  278. package/dist/recall-xray-renderer.d.ts +1 -1
  279. package/dist/recall-xray.d.ts +1 -1
  280. package/dist/reconcile/cursor.d.ts +1 -1
  281. package/dist/reconcile/manifest.d.ts +4 -2
  282. package/dist/reconcile/manifest.js +7 -3
  283. package/dist/reconcile/plan.d.ts +1 -1
  284. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  285. package/dist/replay/normalizers/claude.d.ts +1 -1
  286. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  287. package/dist/replay/normalizers/shared.d.ts +1 -1
  288. package/dist/replay/runner.d.ts +1 -1
  289. package/dist/replay/types.d.ts +1 -1
  290. package/dist/replica-divergence.d.ts +1 -1
  291. package/dist/replica-peers-config.d.ts +1 -1
  292. package/dist/{resolution-MDPNOEG6.js → resolution-MMOD5WC4.js} +4 -4
  293. package/dist/resolve-auth-token.d.ts +1 -1
  294. package/dist/resume-bundles.js +6 -6
  295. package/dist/retrieval-agents.d.ts +2 -2
  296. package/dist/retrieval-tiers.d.ts +1 -1
  297. package/dist/routing/engine.d.ts +1 -1
  298. package/dist/routing/store.d.ts +1 -1
  299. package/dist/salvage-envelope.d.ts +1 -1
  300. package/dist/salvage-envelope.js +3 -3
  301. package/dist/schemas.d.ts +138 -138
  302. package/dist/{scope-profiles-NzGPDXLn.d.ts → scope-profiles-BBQsXuyj.d.ts} +1 -1
  303. package/dist/search/embed-helper.d.ts +1 -1
  304. package/dist/search/factory.d.ts +1 -1
  305. package/dist/search/index.d.ts +1 -1
  306. package/dist/search/lancedb-backend.d.ts +1 -1
  307. package/dist/search/meilisearch-backend.d.ts +1 -1
  308. package/dist/search/noop-backend.d.ts +1 -1
  309. package/dist/search/orama-backend.d.ts +1 -1
  310. package/dist/search/port.d.ts +1 -1
  311. package/dist/search/remote-backend.d.ts +1 -1
  312. package/dist/{semantic-consolidation-DLDKvyy5.d.ts → semantic-consolidation-HD8RkzVA.d.ts} +1 -1
  313. package/dist/semantic-consolidation.d.ts +2 -2
  314. package/dist/semantic-consolidation.js +13 -13
  315. package/dist/semantic-rule-promotion.js +12 -12
  316. package/dist/semantic-rule-verifier.d.ts +1 -1
  317. package/dist/semantic-rule-verifier.js +12 -12
  318. package/dist/{service-PT2_z7xm.d.ts → service-DIrtHHEg.d.ts} +2 -2
  319. package/dist/session-observer-bands.d.ts +1 -1
  320. package/dist/session-observer-state.d.ts +1 -1
  321. package/dist/shared-context/manager.d.ts +9 -9
  322. package/dist/signal.d.ts +1 -1
  323. package/dist/source-agent-qualifier.d.ts +1 -1
  324. package/dist/source-agent-qualifier.js +14 -14
  325. package/dist/{storage-BmVudhk4.d.ts → storage-DqCzXi56.d.ts} +37 -33
  326. package/dist/storage.d.ts +2 -2
  327. package/dist/storage.js +14 -14
  328. package/dist/structured-attributes.d.ts +3 -1
  329. package/dist/structured-attributes.js +3 -1
  330. package/dist/summarizer.d.ts +1 -1
  331. package/dist/summary-snapshot.d.ts +1 -1
  332. package/dist/temporal-supersession.d.ts +2 -2
  333. package/dist/temporal-timeline-recall.d.ts +1 -1
  334. package/dist/temporal-validity.d.ts +1 -1
  335. package/dist/threading.d.ts +1 -1
  336. package/dist/tier-migration.d.ts +2 -2
  337. package/dist/tier-routing.d.ts +1 -1
  338. package/dist/topics.d.ts +1 -1
  339. package/dist/transcript.d.ts +1 -1
  340. package/dist/transfer/types.d.ts +66 -66
  341. package/dist/trust-score-stage.d.ts +1 -1
  342. package/dist/trust-score.d.ts +1 -1
  343. package/dist/{types-CkiDUorT.d.ts → types-d_j4vC6N.d.ts} +26 -1
  344. package/dist/types.d.ts +1 -1
  345. package/dist/utility-runtime.d.ts +1 -1
  346. package/dist/verified-recall.js +12 -12
  347. package/dist/write-envelope.d.ts +1 -1
  348. package/dist/write-envelope.js +2 -2
  349. package/package.json +2 -2
  350. package/src/access-offline-manifest.ts +1 -0
  351. package/src/access-service-offline-file-content.test.ts +1 -0
  352. package/src/content-hash.ts +34 -1
  353. package/src/dedup/index.test.ts +56 -38
  354. package/src/dedup/index.ts +85 -38
  355. package/src/lifecycle/tombstones.test.ts +763 -16
  356. package/src/lifecycle/tombstones.ts +217 -50
  357. package/src/reconcile/manifest.test.ts +158 -1
  358. package/src/reconcile/manifest.ts +76 -22
  359. package/src/storage/content-hash-index.ts +56 -40
  360. package/src/storage/entity-ref-repair.ts +3 -3
  361. package/src/storage/memory-read-store.ts +9 -0
  362. package/src/storage/tombstone-blocked-capture-index.ts +4 -9
  363. package/src/storage-contract/content-hash.test.ts +13 -0
  364. package/src/storage-fact-hash-index-defer.test.ts +1 -1
  365. package/src/storage.ts +107 -93
  366. package/src/structured-attributes.ts +11 -0
  367. package/src/wearables/cleanup.test.ts +71 -0
  368. package/src/wearables/cleanup.ts +163 -16
  369. package/src/wearables/config.test.ts +62 -0
  370. package/src/wearables/config.ts +99 -5
  371. package/src/wearables/index.ts +10 -0
  372. package/src/wearables/pipeline.test.ts +36 -0
  373. package/src/wearables/pipeline.ts +18 -3
  374. package/src/wearables/redaction.test.ts +156 -0
  375. package/src/wearables/redaction.ts +102 -10
  376. package/src/wearables/text-language.ts +146 -0
  377. package/src/wearables/types.ts +26 -0
  378. package/dist/chunk-BQIFZ2MP.js.map +0 -1
  379. package/dist/chunk-DLG5V6ND.js.map +0 -1
  380. package/dist/chunk-IARXXZU4.js.map +0 -1
  381. package/dist/chunk-JJRVPWFY.js.map +0 -1
  382. package/dist/chunk-NDAH7BJ5.js.map +0 -1
  383. package/dist/chunk-R3DJVOSM.js.map +0 -1
  384. package/dist/chunk-VHFXP27N.js.map +0 -1
  385. package/dist/chunk-W5H6ZRPT.js +0 -14
  386. package/dist/chunk-W5H6ZRPT.js.map +0 -1
  387. package/dist/chunk-WD5B5AIE.js.map +0 -1
  388. /package/dist/{auto-sync-QFQGOEO2.js.map → auto-sync-TDHLZPVP.js.map} +0 -0
  389. /package/dist/{chunk-MLXZD4WM.js.map → chunk-2GFK22OZ.js.map} +0 -0
  390. /package/dist/{chunk-RANKJI5T.js.map → chunk-3JLPGJ43.js.map} +0 -0
  391. /package/dist/{chunk-X7XEDOSL.js.map → chunk-4IIFL7GC.js.map} +0 -0
  392. /package/dist/{chunk-GXSOUIA4.js.map → chunk-5WQMORU5.js.map} +0 -0
  393. /package/dist/{chunk-J272WKX2.js.map → chunk-6HPHEFXT.js.map} +0 -0
  394. /package/dist/{chunk-B6LZ4IBH.js.map → chunk-6LAPLZOS.js.map} +0 -0
  395. /package/dist/{chunk-VXCNQYTC.js.map → chunk-APPFNGX6.js.map} +0 -0
  396. /package/dist/{chunk-GXFSQ6SD.js.map → chunk-C4ZNGVK3.js.map} +0 -0
  397. /package/dist/{chunk-JSEJH2YQ.js.map → chunk-CGVHPCP2.js.map} +0 -0
  398. /package/dist/{chunk-TEMSWHQD.js.map → chunk-DZD576ZU.js.map} +0 -0
  399. /package/dist/{chunk-AMTEYNYK.js.map → chunk-EVWWT3KE.js.map} +0 -0
  400. /package/dist/{chunk-AXIDQG5A.js.map → chunk-G4T7L557.js.map} +0 -0
  401. /package/dist/{chunk-7M6NYQWB.js.map → chunk-G7Z6EENO.js.map} +0 -0
  402. /package/dist/{chunk-YXKVLYXU.js.map → chunk-HWRD23DW.js.map} +0 -0
  403. /package/dist/{chunk-SOEN6KCF.js.map → chunk-IBKR2TFR.js.map} +0 -0
  404. /package/dist/{chunk-F4EU6VUC.js.map → chunk-JDZPNCMA.js.map} +0 -0
  405. /package/dist/{chunk-D5YOZ66N.js.map → chunk-JV5YUBIZ.js.map} +0 -0
  406. /package/dist/{chunk-BAKXNQ3C.js.map → chunk-KRTY3ENI.js.map} +0 -0
  407. /package/dist/{chunk-NK5RGJ65.js.map → chunk-L4DLLKPN.js.map} +0 -0
  408. /package/dist/{chunk-VFSAWRMZ.js.map → chunk-LJS7KUPV.js.map} +0 -0
  409. /package/dist/{chunk-NOEMGE56.js.map → chunk-NH5IR2WM.js.map} +0 -0
  410. /package/dist/{chunk-5V6IXHPI.js.map → chunk-NSVDUN76.js.map} +0 -0
  411. /package/dist/{chunk-W7Y5JDVB.js.map → chunk-PDDTI7IC.js.map} +0 -0
  412. /package/dist/{chunk-QINJITDS.js.map → chunk-PX2LSHYM.js.map} +0 -0
  413. /package/dist/{chunk-HDIUYMSB.js.map → chunk-QWGE2PUH.js.map} +0 -0
  414. /package/dist/{chunk-X57M2Q4I.js.map → chunk-RDJRFDJY.js.map} +0 -0
  415. /package/dist/{chunk-RYLEDKQ2.js.map → chunk-RUMNY5DT.js.map} +0 -0
  416. /package/dist/{chunk-BKIFTXHR.js.map → chunk-RZ4XGKOJ.js.map} +0 -0
  417. /package/dist/{chunk-66SE3BNH.js.map → chunk-SUPEYLH2.js.map} +0 -0
  418. /package/dist/{chunk-BF6SXNQJ.js.map → chunk-T2JHZWH6.js.map} +0 -0
  419. /package/dist/{chunk-QPOHMO6M.js.map → chunk-UES4BHA2.js.map} +0 -0
  420. /package/dist/{chunk-XAPMEU3K.js.map → chunk-UU7TRFNE.js.map} +0 -0
  421. /package/dist/{chunk-QBSFOKY6.js.map → chunk-VOELBUS4.js.map} +0 -0
  422. /package/dist/{chunk-2LY6GIIE.js.map → chunk-WDBX6VER.js.map} +0 -0
  423. /package/dist/{chunk-BMEYMKWX.js.map → chunk-WM65LU67.js.map} +0 -0
  424. /package/dist/{chunk-WLRD2TXF.js.map → chunk-WOPA3SGI.js.map} +0 -0
  425. /package/dist/{chunk-5UZ6UCOZ.js.map → chunk-XUAMCLY4.js.map} +0 -0
  426. /package/dist/{chunk-7HXZJAID.js.map → chunk-YGLY6QFV.js.map} +0 -0
  427. /package/dist/{chunk-2OIQDTDC.js.map → chunk-ZPJ4QSGP.js.map} +0 -0
  428. /package/dist/{chunk-3OY7KXHM.js.map → chunk-ZYLC7NFO.js.map} +0 -0
  429. /package/dist/{resolution-MDPNOEG6.js.map → resolution-MMOD5WC4.js.map} +0 -0
@@ -41,6 +41,7 @@
41
41
  // ---------------------------------------------------------------------------
42
42
 
43
43
  import { serializeMutations, withHeldFileLock } from "../utils/serialize-mutations.js";
44
+ import { computeLegacyContentHash } from "../content-hash.js";
44
45
 
45
46
  /** Why a tombstone was emitted. */
46
47
  export type TombstoneReason =
@@ -72,10 +73,14 @@ export interface TombstoneEntry {
72
73
  reason: TombstoneReason;
73
74
  /** The memory that was retired. */
74
75
  sourceMemoryId: string;
75
- /** sha256 of the retired memory's rawContent (rule 23). */
76
+ /** sha256 of the retired identity; explicit contentHashSource may define it. */
76
77
  contentHash: string;
77
- /** `ContentHashIndex.normalizeContent(rawContent)` the pre-hash form. */
78
+ /** Current body hash retained alongside an ambiguous or explicit primary hash. */
79
+ currentContentHashAlias?: string;
80
+ /** Current normalized body used by the normalized lookup tier. */
78
81
  normalizedText: string;
82
+ /** Version of the current body identity fields. */
83
+ normalizerVersion?: number;
79
84
  entityRef?: string;
80
85
  /** Structured-attribute supersession key when one existed. */
81
86
  supersessionKey?: string;
@@ -111,7 +116,7 @@ export interface TombstoneLookupQuery {
111
116
  /** Single supersession key (direct/unit callers). */
112
117
  supersessionKey?: string;
113
118
  /** All derived supersession keys (write chokepoint). The keyed tier is
114
- * checked for each; the first active match wins. */
119
+ * checked for each; the first active match wins. */
115
120
  supersessionKeys?: string[];
116
121
  namespace: string;
117
122
  }
@@ -131,6 +136,15 @@ export interface TombstoneStoreOptions {
131
136
  * skipped entirely.
132
137
  */
133
138
  semanticSimilarity?: (a: string, b: string) => number;
139
+ /**
140
+ * Resolve source identity candidates for bounded legacy sourceMemoryIds.
141
+ * StorageManager reads the corpus before the tombstone write lock.
142
+ */
143
+ readonly sourceContentsForMemoryIds?: (
144
+ sourceMemoryIds: readonly string[],
145
+ ) => Promise<ReadonlyMap<string, string | readonly string[]>>;
146
+ /** Maximum number of legacy entries considered during one load. */
147
+ readonly legacyMigrationLimit?: number;
134
148
  /**
135
149
  * Cross-process write-lock timings for the tombstone JSONL mutation lock
136
150
  * (issue #1639). The secure-store append is a read-merge-write (read
@@ -168,6 +182,9 @@ export interface TombstoneStats {
168
182
  loaded: boolean;
169
183
  }
170
184
 
185
+ const TOMBSTONE_NORMALIZER_VERSION = 2;
186
+ const DEFAULT_LEGACY_MIGRATION_LIMIT = 10_000;
187
+
171
188
  const TOMBSTONE_PREFIX = "tomb";
172
189
 
173
190
  function newTombstoneId(): string {
@@ -235,6 +252,12 @@ export function parseTombstoneLine(line: string): TombstoneEntry | null {
235
252
  createdAt: e.createdAt,
236
253
  createdBy: e.createdBy,
237
254
  };
255
+ if (typeof e.currentContentHashAlias === "string") {
256
+ out.currentContentHashAlias = e.currentContentHashAlias;
257
+ }
258
+ if (typeof e.normalizerVersion === "number" && Number.isInteger(e.normalizerVersion)) {
259
+ out.normalizerVersion = e.normalizerVersion;
260
+ }
238
261
  if (typeof e.operationKey === "string") out.operationKey = e.operationKey;
239
262
  if (typeof e.entityRef === "string") out.entityRef = e.entityRef;
240
263
  if (typeof e.supersessionKey === "string") out.supersessionKey = e.supersessionKey;
@@ -279,6 +302,10 @@ export class TombstoneStore {
279
302
  private readonly options: TombstoneStoreOptions,
280
303
  private readonly io: TombstoneFileIo,
281
304
  ) {
305
+ const limit = this.options.legacyMigrationLimit;
306
+ if (limit !== undefined && (!Number.isFinite(limit) || !Number.isInteger(limit) || limit < 0)) {
307
+ throw new Error("legacyMigrationLimit must be a finite non-negative integer");
308
+ }
282
309
  this.lockStaleMs = this.options.lockStaleMs ?? 30_000;
283
310
  this.lockMaxWaitMs = this.options.lockMaxWaitMs ?? 5_000;
284
311
  this.lockHeartbeatMs =
@@ -318,39 +345,132 @@ export class TombstoneStore {
318
345
  }
319
346
  }
320
347
 
321
- private async loadInternal(): Promise<void> {
322
- // Record the file's mtime so the staleness probe does not immediately
323
- // invalidate on the next access (fileMtimeMs starts at 0; without this,
324
- // any non-zero mtime would trigger a spurious reload). Done before the
325
- // read so an ENOENT still records 0.
348
+ private async loadInternal(migrateLegacy = true): Promise<void> {
349
+ // Record the file mtime before reading so an ENOENT still records 0 and
350
+ // a successful load does not immediately trigger a staleness reload.
326
351
  this.recordFileMtime();
327
352
  let raw: string;
328
353
  try {
329
354
  raw = await this.io.read(this.filePath);
330
355
  } catch (err) {
331
- // ENOENT is fine — fresh store. Other errors leave the store empty
332
- // rather than crashing the write path (rule 34 spirit: degrade to
333
- // "no tombstones known" rather than blocking all writes).
334
356
  const code = (err as NodeJS.ErrnoException)?.code;
335
- if (code !== "ENOENT") {
336
- // Swallow — the write path must not crash on a corrupt tombstone file.
337
- // Rebuild will repair on the next doctor/maintenance run.
338
- }
357
+ if (code !== "ENOENT") throw err;
339
358
  this.loaded = true;
340
359
  return;
341
360
  }
361
+ const initial = this.parseEntries(raw);
362
+ const migrated = migrateLegacy
363
+ ? await this.migrateLegacyEntries(initial)
364
+ : initial;
342
365
  this.resetIndex();
343
- let corrupted = 0;
366
+ for (const entry of migrated.entries) this.indexEntry(entry);
367
+ this.corruptedLines = migrated.corruptedLines;
368
+ this.loaded = true;
369
+ }
370
+
371
+ private parseEntries(raw: string): {
372
+ entries: TombstoneEntry[];
373
+ corruptedLines: number;
374
+ } {
375
+ const entries: TombstoneEntry[] = [];
376
+ let corruptedLines = 0;
344
377
  for (const line of raw.split("\n")) {
345
378
  const entry = parseTombstoneLine(line);
346
379
  if (!entry) {
347
- if (line.trim().length > 0) corrupted += 1;
380
+ if (line.trim().length > 0) corruptedLines += 1;
348
381
  continue;
349
382
  }
350
- this.indexEntry(entry);
383
+ entries.push(entry);
351
384
  }
352
- this.corruptedLines = corrupted;
353
- this.loaded = true;
385
+ return { entries, corruptedLines };
386
+ }
387
+
388
+ /**
389
+ * Re-index pre-Unicode records from retired source content.
390
+ * Source reads stay bounded, but every batch finishes before the store
391
+ * publishes its in-memory index. Missing sources remain eligible on restart.
392
+ */
393
+ private async migrateLegacyEntries(initial: {
394
+ entries: TombstoneEntry[];
395
+ corruptedLines: number;
396
+ }): Promise<{ entries: TombstoneEntry[]; corruptedLines: number }> {
397
+ const limit = this.options.legacyMigrationLimit ?? DEFAULT_LEGACY_MIGRATION_LIMIT;
398
+ if (limit === 0 || !this.options.sourceContentsForMemoryIds) return initial;
399
+ const sourceMemoryIds: string[] = [];
400
+ const requested = new Set<string>();
401
+ for (const entry of initial.entries) {
402
+ if (
403
+ entry.kind !== "tombstone" ||
404
+ entry.normalizerVersion === TOMBSTONE_NORMALIZER_VERSION
405
+ ) {
406
+ continue;
407
+ }
408
+ if (requested.has(entry.sourceMemoryId)) continue;
409
+ requested.add(entry.sourceMemoryId);
410
+ sourceMemoryIds.push(entry.sourceMemoryId);
411
+ }
412
+ if (sourceMemoryIds.length === 0) return initial;
413
+ const sourceContents = new Map<string, string | readonly string[]>();
414
+ for (let offset = 0; offset < sourceMemoryIds.length; offset += limit) {
415
+ const batch = sourceMemoryIds.slice(offset, offset + limit);
416
+ const fetched = await this.options.sourceContentsForMemoryIds(batch);
417
+ for (const [sourceMemoryId, content] of fetched) sourceContents.set(sourceMemoryId, content);
418
+ }
419
+ return await serializeMutations(`tombstone:${this.filePath}`, () =>
420
+ this.withWriteLock(async () => {
421
+ const latestRaw = await this.io.read(this.filePath);
422
+ const latest = this.parseEntries(latestRaw);
423
+ let changed = false;
424
+ const migrateEntry = (entry: TombstoneEntry): TombstoneEntry => {
425
+ if (
426
+ entry.kind !== "tombstone" ||
427
+ entry.normalizerVersion === TOMBSTONE_NORMALIZER_VERSION ||
428
+ !requested.has(entry.sourceMemoryId)
429
+ ) {
430
+ return entry;
431
+ }
432
+ const resolved = sourceContents.get(entry.sourceMemoryId);
433
+ if (resolved === undefined) return entry;
434
+ const candidates = typeof resolved === "string" ? [resolved] : resolved;
435
+ const matches = candidates.filter((source) =>
436
+ entry.contentHash === this.options.hashContent(source) ||
437
+ entry.contentHash === computeLegacyContentHash(source)
438
+ );
439
+ const explicitPrimary =
440
+ matches.length === 0 &&
441
+ entry.contentHash !== this.options.hashContent(entry.normalizedText);
442
+ const source = matches[0] ?? (explicitPrimary ? candidates[0] : undefined);
443
+ if (source === undefined) return entry;
444
+ const currentHash = this.options.hashContent(source);
445
+ const alias = explicitPrimary
446
+ ? currentHash
447
+ : matches
448
+ .map((candidate) => this.options.hashContent(candidate))
449
+ .find((hash) => hash !== currentHash);
450
+ changed = true;
451
+ return {
452
+ ...entry,
453
+ contentHash: explicitPrimary ? entry.contentHash : currentHash,
454
+ ...(alias ? { currentContentHashAlias: alias } : {}),
455
+ normalizedText: this.options.normalizeText(source),
456
+ normalizerVersion: TOMBSTONE_NORMALIZER_VERSION,
457
+ };
458
+ };
459
+ const serialized = latestRaw
460
+ .split("\n")
461
+ .map((line) => {
462
+ const entry = parseTombstoneLine(line);
463
+ if (!entry) return line;
464
+ const migrated = migrateEntry(entry);
465
+ return migrated === entry ? line : JSON.stringify(migrated);
466
+ })
467
+ .join("\n");
468
+ if (!changed) return latest;
469
+ await this.io.write(this.filePath, serialized);
470
+ this.markWritten();
471
+ return this.parseEntries(serialized);
472
+ }),
473
+ );
354
474
  }
355
475
 
356
476
  private resetIndex(): void {
@@ -384,8 +504,13 @@ export class TombstoneStore {
384
504
  // entry — otherwise A's lookup finds B's id, rejects it on namespace
385
505
  // mismatch, and misses its own still-active tombstone (resurrection).
386
506
  const ns = entry.namespace;
387
- if (entry.contentHash) this.byHash.set(`${ns}\0${entry.contentHash}`, entry.id);
388
- if (entry.normalizedText) this.byNormalized.set(`${ns}\0${entry.normalizedText}`, entry.id);
507
+ if (entry.normalizerVersion === TOMBSTONE_NORMALIZER_VERSION) {
508
+ if (entry.contentHash) this.byHash.set(`${ns}\0${entry.contentHash}`, entry.id);
509
+ if (entry.currentContentHashAlias) {
510
+ this.byHash.set(`${ns}\0${entry.currentContentHashAlias}`, entry.id);
511
+ }
512
+ if (entry.normalizedText) this.byNormalized.set(`${ns}\0${entry.normalizedText}`, entry.id);
513
+ }
389
514
  if (entry.entityRef && entry.supersessionKey) {
390
515
  this.byKey.set(keyedTierKey(ns, entry.entityRef, entry.supersessionKey), entry.id);
391
516
  }
@@ -400,15 +525,11 @@ export class TombstoneStore {
400
525
 
401
526
  /**
402
527
  * Cross-process staleness probe (#1579). If the file's mtime advanced since
403
- * the last load/own-write, a peer process appended and our in-memory index is
404
- * stale — invalidate AND reload in place so the next lookup sees the new
405
- * entries. Best-effort: a stat error is swallowed (the loaded index is
406
- * retained rather than crashing the write path). No-op when no `stat` was
407
- * injected or when the mtime is unchanged. After our own append/revoke/rebuild
408
- * `markWritten` records the new mtime, so this probe does not fire for our
409
- * own writes (only for peer-process appends).
528
+ * the last load/own-write, a peer process appended and our in-memory index
529
+ * is stale — invalidate and reload before the next access. Mutation callers
530
+ * pass `migrate: false` to avoid nested serializer/lock acquisition.
410
531
  */
411
- async ensureFreshAgainstDisk(): Promise<void> {
532
+ async ensureFreshAgainstDisk(options: { migrate?: boolean } = {}): Promise<void> {
412
533
  if (!this.io.stat) return;
413
534
  let mtimeMs: number;
414
535
  try {
@@ -419,7 +540,11 @@ export class TombstoneStore {
419
540
  if (mtimeMs === this.fileMtimeMs && this.loaded) return;
420
541
  this.fileMtimeMs = mtimeMs;
421
542
  this.invalidate();
422
- await this.load();
543
+ if (options.migrate === false) {
544
+ await this.loadInternal(false);
545
+ } else {
546
+ await this.load();
547
+ }
423
548
  }
424
549
 
425
550
  /**
@@ -470,13 +595,19 @@ export class TombstoneStore {
470
595
  await this.load();
471
596
  const createdAt = input.createdAt ?? new Date().toISOString();
472
597
  const id = newTombstoneId();
598
+ const currentHash = this.options.hashContent(input.rawContent);
599
+ const currentNormalizedText = this.options.normalizeText(input.rawContent);
473
600
  const entry: TombstoneEntry = {
474
601
  id,
602
+ ...(input.contentHash && input.contentHash !== currentHash
603
+ ? { currentContentHashAlias: currentHash }
604
+ : {}),
475
605
  kind: "tombstone",
476
606
  reason: input.reason,
477
607
  sourceMemoryId: input.sourceMemoryId,
478
- contentHash: input.contentHash ?? this.options.hashContent(input.rawContent),
479
- normalizedText: this.options.normalizeText(input.rawContent),
608
+ contentHash: input.contentHash ?? currentHash,
609
+ normalizedText: currentNormalizedText,
610
+ normalizerVersion: TOMBSTONE_NORMALIZER_VERSION,
480
611
  ...(input.entityRef ? { entityRef: input.entityRef } : {}),
481
612
  ...(input.supersessionKey ? { supersessionKey: input.supersessionKey } : {}),
482
613
  ...(input.operationKey ? { operationKey: input.operationKey } : {}),
@@ -487,6 +618,7 @@ export class TombstoneStore {
487
618
  await this.serializeAppend(entry);
488
619
  this.indexEntry(entry);
489
620
  this.markWritten();
621
+ await this.migrateLoadedLegacyEntries();
490
622
  return id;
491
623
  }
492
624
 
@@ -513,6 +645,7 @@ export class TombstoneStore {
513
645
  await this.serializeAppend(entry);
514
646
  this.indexEntry(entry);
515
647
  this.markWritten();
648
+ await this.migrateLoadedLegacyEntries();
516
649
  return id;
517
650
  }
518
651
 
@@ -526,12 +659,33 @@ export class TombstoneStore {
526
659
  // disk first, so indexEntry builds on the peer's just-appended entries.
527
660
  return serializeMutations(`tombstone:${this.filePath}`, () =>
528
661
  this.withWriteLock(async () => {
529
- await this.ensureFreshAgainstDisk();
662
+ await this.ensureFreshAgainstDisk({ migrate: false });
530
663
  await this.io.append(this.filePath, line);
531
664
  }),
532
665
  );
533
666
  }
534
667
 
668
+ private async migrateLoadedLegacyEntries(): Promise<void> {
669
+ const initial = {
670
+ entries: [...this.entries],
671
+ corruptedLines: this.corruptedLines,
672
+ };
673
+ if (!initial.entries.some(
674
+ (entry) => entry.kind === "tombstone" && entry.normalizerVersion !== TOMBSTONE_NORMALIZER_VERSION,
675
+ )) {
676
+ return;
677
+ }
678
+ try {
679
+ const migrated = await this.migrateLegacyEntries(initial);
680
+ this.resetIndex();
681
+ for (const entry of migrated.entries) this.indexEntry(entry);
682
+ this.corruptedLines = migrated.corruptedLines;
683
+ this.loaded = true;
684
+ } catch {
685
+ // The durable mutation already succeeded; defer migration to next load.
686
+ }
687
+ }
688
+
535
689
  /**
536
690
  * Acquire the cross-process tombstone write lock and run `task` under it
537
691
  * (issue #1639). Strict-fail on acquisition timeout: the secure-store append
@@ -635,6 +789,7 @@ export class TombstoneStore {
635
789
  let best: { id: string; reason: TombstoneReason; score: number } | null = null;
636
790
  for (const entry of this.entries) {
637
791
  if (entry.kind !== "tombstone") continue;
792
+ if (entry.normalizerVersion !== TOMBSTONE_NORMALIZER_VERSION) continue;
638
793
  if (entry.namespace !== query.namespace) continue;
639
794
  if (this.revokedIds.has(entry.id)) continue;
640
795
  if (!entry.normalizedText) continue;
@@ -714,7 +869,7 @@ export class TombstoneStore {
714
869
  // interleave in-process either.
715
870
  const rebuiltCount = await serializeMutations(`tombstone:${this.filePath}`, () =>
716
871
  this.withWriteLock(async () => {
717
- await this.ensureFreshAgainstDisk();
872
+ await this.ensureFreshAgainstDisk({ migrate: false });
718
873
  // Preserve existing revocations (all namespaces — ids are globally
719
874
  // unique) so a rebuild does not silently un-revoke.
720
875
  const existingRevocations = this.entries.filter((e) => e.kind === "revocation");
@@ -737,21 +892,32 @@ export class TombstoneStore {
737
892
  existingBySource.set(`${e.sourceMemoryId}\u{0000}${e.supersessionKey ?? ""}`, e.id);
738
893
  }
739
894
  }
740
- const rebuilt: TombstoneEntry[] = retiredMemories.map((m) => ({
741
- id:
742
- existingBySource.get(`${m.memoryId}\u{0000}${m.supersessionKey ?? ""}`) ??
743
- newTombstoneId(),
744
- kind: "tombstone" as const,
745
- reason: m.reason,
746
- sourceMemoryId: m.memoryId,
747
- contentHash: m.contentHash ?? this.options.hashContent(m.rawContent),
748
- normalizedText: this.options.normalizeText(m.rawContent),
749
- ...(m.entityRef ? { entityRef: m.entityRef } : {}),
750
- ...(m.supersessionKey ? { supersessionKey: m.supersessionKey } : {}),
751
- namespace: this.namespace,
752
- createdAt: m.createdAt,
753
- createdBy: m.createdBy,
754
- }));
895
+ const rebuilt: TombstoneEntry[] = retiredMemories.map((m) => {
896
+ const currentHash = this.options.hashContent(m.rawContent);
897
+ const currentNormalizedText = this.options.normalizeText(m.rawContent);
898
+ const persistedHash = m.contentHash;
899
+ const preserveOverride =
900
+ persistedHash !== undefined &&
901
+ persistedHash !== currentHash &&
902
+ persistedHash !== computeLegacyContentHash(m.rawContent);
903
+ return {
904
+ id:
905
+ existingBySource.get(`${m.memoryId}\u{0000}${m.supersessionKey ?? ""}`) ??
906
+ newTombstoneId(),
907
+ ...(preserveOverride ? { currentContentHashAlias: currentHash } : {}),
908
+ kind: "tombstone" as const,
909
+ reason: m.reason,
910
+ sourceMemoryId: m.memoryId,
911
+ contentHash: preserveOverride ? persistedHash : currentHash,
912
+ normalizedText: currentNormalizedText,
913
+ normalizerVersion: TOMBSTONE_NORMALIZER_VERSION,
914
+ ...(m.entityRef ? { entityRef: m.entityRef } : {}),
915
+ ...(m.supersessionKey ? { supersessionKey: m.supersessionKey } : {}),
916
+ namespace: this.namespace,
917
+ createdAt: m.createdAt,
918
+ createdBy: m.createdBy,
919
+ };
920
+ });
755
921
  // Sort deterministically (rule 38): createdAt, then id for stability.
756
922
  rebuilt.sort((a, b) =>
757
923
  a.createdAt === b.createdAt
@@ -777,6 +943,7 @@ export class TombstoneStore {
777
943
  return rebuilt.length;
778
944
  }),
779
945
  );
946
+ await this.migrateLoadedLegacyEntries();
780
947
  return rebuiltCount;
781
948
  }
782
949
  }
@@ -1,8 +1,10 @@
1
1
  import * as assert from "node:assert/strict";
2
2
  import { createHash } from "node:crypto";
3
3
  import { test } from "node:test";
4
- import { ContentHashIndex } from "../storage/content-hash-index.js";
4
+ import { computeLegacyContentHash } from "../content-hash.js";
5
+ import { attachCitation, formatCitation } from "../source-attribution.js";
5
6
  import { parseFrontmatter } from "../storage.js";
7
+ import { ContentHashIndex } from "../storage/content-hash-index.js";
6
8
  import { type ReconcileManifest, buildReconcileManifest, collapseActiveFactDuplicates } from "./manifest.js";
7
9
  import { planReconciliation } from "./plan.js";
8
10
 
@@ -47,6 +49,7 @@ test("reconcile manifest keeps file identity separate from canonical semantic id
47
49
  id: "fact-a",
48
50
  category: "fact",
49
51
  contentHash: semanticHash,
52
+ normalizerVersion: 4,
50
53
  status: "active",
51
54
  });
52
55
  });
@@ -71,6 +74,125 @@ test("reconcile manifest hashes parsed legacy content and uses effective lifecyc
71
74
  assert.equal(manifest.files[1]?.memory?.status, "active");
72
75
  });
73
76
 
77
+ test("reconcile manifest reparses cached pre-version identities after Unicode migration", async () => {
78
+ const content = "The user prefers café.";
79
+ const legacyHash = computeLegacyContentHash(content);
80
+ const serialized = memoryFile({ id: "legacy-unicode", content, contentHash: legacyHash });
81
+ const file = { path: "facts/legacy-unicode.md", sha256: fileHash(serialized) };
82
+ let reads = 0;
83
+
84
+ const manifest = await buildReconcileManifest({
85
+ files: [file],
86
+ parseMemory: parseFrontmatter,
87
+ readFile: async () => {
88
+ reads += 1;
89
+ return serialized;
90
+ },
91
+ cachedFiles: [{
92
+ ...file,
93
+ memory: {
94
+ id: "legacy-unicode",
95
+ category: "fact",
96
+ contentHash: legacyHash,
97
+ status: "active",
98
+ },
99
+ }],
100
+ });
101
+
102
+ assert.equal(reads, 1);
103
+ assert.equal(
104
+ manifest.files[0]?.memory?.contentHash,
105
+ ContentHashIndex.computeHash(content),
106
+ );
107
+ assert.equal("contentHashAliases" in (manifest.files[0]?.memory ?? {}), false);
108
+ });
109
+
110
+ test("reconcile manifest replaces a pure-CJK legacy identity with its current hash", async () => {
111
+ const content = "利用者は紅茶を好む。";
112
+ const serialized = memoryFile({
113
+ id: "legacy-cjk",
114
+ content,
115
+ contentHash: computeLegacyContentHash(content),
116
+ });
117
+ const manifest = await buildReconcileManifest({
118
+ files: [{ path: "facts/legacy-cjk.md", sha256: fileHash(serialized) }],
119
+ parseMemory: parseFrontmatter,
120
+ readFile: async () => serialized,
121
+ });
122
+
123
+ assert.equal(
124
+ manifest.files[0]?.memory?.contentHash,
125
+ ContentHashIndex.computeHash(content),
126
+ );
127
+ assert.equal("contentHashAliases" in (manifest.files[0]?.memory ?? {}), false);
128
+ });
129
+
130
+ test("reconcile manifest recovers a raw hash source beneath citation and attributes", async () => {
131
+ const content = "The user prefers café.";
132
+ const cited = attachCitation(content, {
133
+ agent: "planner",
134
+ session: "agent:planner:main",
135
+ ts: "2026-08-11T00:00:00.000Z",
136
+ });
137
+ const storedBody = `${cited}\n[Attributes: topic: coffee]`;
138
+ const serialized = memoryFile({
139
+ id: "legacy-enriched",
140
+ content: storedBody,
141
+ contentHash: computeLegacyContentHash(content),
142
+ });
143
+ const manifest = await buildReconcileManifest({
144
+ files: [{ path: "facts/legacy-enriched.md", sha256: fileHash(serialized) }],
145
+ parseMemory: parseFrontmatter,
146
+ readFile: async () => serialized,
147
+ });
148
+
149
+ assert.equal(manifest.files[0]?.memory?.contentHash, ContentHashIndex.computeHash(content));
150
+ });
151
+
152
+ test("reconcile manifest recovers raw identity beneath a configured citation", async () => {
153
+ const content = "The user prefers café.";
154
+ const citationTemplate = "[src:{agent}/{sessionId}@{date}]";
155
+ const storedBody = `${content} ${formatCitation({
156
+ agent: "planner",
157
+ session: "agent:planner:main",
158
+ ts: "2026-08-11T00:00:00.000Z",
159
+ }, citationTemplate)}`;
160
+ const serialized = memoryFile({
161
+ id: "legacy-custom-citation",
162
+ content: storedBody,
163
+ contentHash: computeLegacyContentHash(content),
164
+ });
165
+ const manifest = await buildReconcileManifest({
166
+ files: [{ path: "facts/legacy-custom-citation.md", sha256: fileHash(serialized) }],
167
+ parseMemory: parseFrontmatter,
168
+ readFile: async () => serialized,
169
+ citationTemplate,
170
+ });
171
+
172
+ assert.equal(manifest.files[0]?.memory?.contentHash, ContentHashIndex.computeHash(content));
173
+ });
174
+
175
+ test("reconcile manifest does not add a legacy alias to a current Unicode identity", async () => {
176
+ const unicode = "The user prefers café.";
177
+ const ascii = "The user prefers caf.";
178
+ const serialized = memoryFile({
179
+ id: "current-unicode",
180
+ content: unicode,
181
+ contentHash: ContentHashIndex.computeHash(unicode),
182
+ });
183
+ const manifest = await buildReconcileManifest({
184
+ files: [{ path: "facts/current-unicode.md", sha256: fileHash(serialized) }],
185
+ parseMemory: parseFrontmatter,
186
+ readFile: async () => serialized,
187
+ });
188
+
189
+ assert.notEqual(
190
+ manifest.files[0]?.memory?.contentHash,
191
+ ContentHashIndex.computeHash(ascii),
192
+ );
193
+ assert.equal("contentHashAliases" in (manifest.files[0]?.memory ?? {}), false);
194
+ });
195
+
74
196
  test("reconcile manifest keeps file identity when memory bytes cannot be trusted", async () => {
75
197
  const readable = memoryFile({ id: "readable", content: "Readable fact" });
76
198
  const changed = memoryFile({ id: "changed", content: "Changed after census" });
@@ -148,6 +270,41 @@ test("semantic collapse prevents different active fact paths from transferring b
148
270
  assert.equal(collapsed.byNamespace[0]?.push, 0);
149
271
  });
150
272
 
273
+ test("semantic collapse matches legacy and current Unicode identities across replicas", async () => {
274
+ const content = "The user prefers café.";
275
+ const localSerialized = memoryFile({
276
+ id: "legacy-id",
277
+ content,
278
+ contentHash: computeLegacyContentHash(content),
279
+ });
280
+ const peerSerialized = memoryFile({
281
+ id: "current-id",
282
+ content,
283
+ contentHash: ContentHashIndex.computeHash(content),
284
+ });
285
+ const localFile = { path: "facts/legacy-id.md", sha256: fileHash(localSerialized) };
286
+ const peerFile = { path: "facts/current-id.md", sha256: fileHash(peerSerialized) };
287
+ const build = async (file: typeof localFile, raw: string) =>
288
+ await buildReconcileManifest({
289
+ files: [file],
290
+ parseMemory: parseFrontmatter,
291
+ readFile: async () => raw,
292
+ });
293
+ const local = await build(localFile, localSerialized);
294
+ const peer = await build(peerFile, peerSerialized);
295
+ const plan = planReconciliation([{ namespace: "default", local: [localFile], peer: [peerFile] }]);
296
+
297
+ const collapsed = collapseActiveFactDuplicates(
298
+ plan,
299
+ new Map([["default", local]]),
300
+ new Map([["default", peer]]),
301
+ );
302
+
303
+ assert.equal(collapsed.converged, true);
304
+ assert.equal(collapsed.entries.length, 1);
305
+ assert.equal(collapsed.entries[0]?.reason, "semantic_duplicate");
306
+ });
307
+
151
308
  test("semantic collapse classifies a later one-sided metadata edit from its per-side base", () => {
152
309
  const semanticHash = ContentHashIndex.computeHash("same active fact");
153
310
  const priorLocal = { path: "facts/local-id.md", sha256: "a".repeat(64) };