@remnic/core 9.65.1 → 9.65.3

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 (576) hide show
  1. package/dist/access-admin-ops-surface.d.ts +44 -44
  2. package/dist/access-admin-ops-surface.js +36 -35
  3. package/dist/access-authorization-probe.d.ts +39 -40
  4. package/dist/access-authorization-probe.js +37 -36
  5. package/dist/access-boundary.d.ts +41 -185
  6. package/dist/access-boundary.js +36 -35
  7. package/dist/access-cli.js +110 -107
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +3 -1
  10. package/dist/access-extraction-force-flush.d.ts +25 -25
  11. package/dist/access-extraction-force-flush.js +36 -35
  12. package/dist/access-health-types.d.ts +3 -1
  13. package/dist/access-http-lcm-compaction.d.ts +39 -39
  14. package/dist/access-http-lifecycle-flush.d.ts +40 -41
  15. package/dist/access-http-offline-stream.d.ts +25 -25
  16. package/dist/access-http-query.d.ts +4 -2
  17. package/dist/access-http-query.js +38 -37
  18. package/dist/access-http.d.ts +51 -44
  19. package/dist/access-http.js +57 -56
  20. package/dist/access-identity-continuity-surface.d.ts +12 -12
  21. package/dist/access-identity-continuity-surface.js +36 -35
  22. package/dist/access-lcm-surface.d.ts +27 -27
  23. package/dist/access-lcm-surface.js +36 -35
  24. package/dist/access-mcp-admin-tools.d.ts +9 -1
  25. package/dist/access-mcp-admin-tools.js +3 -1
  26. package/dist/access-mcp-output-schemas.js +1 -1
  27. package/dist/access-mcp.d.ts +39 -39
  28. package/dist/access-mcp.js +50 -47
  29. package/dist/access-memory-search-fanout.d.ts +4 -2
  30. package/dist/{access-namespace-preflight-BKUnKsBv.d.ts → access-namespace-preflight-B7Usyz-K.d.ts} +1 -1
  31. package/dist/access-namespace-preflight.d.ts +5 -3
  32. package/dist/access-namespace-preflight.js +36 -35
  33. package/dist/access-observe-write-surface.d.ts +39 -39
  34. package/dist/access-observe-write-surface.js +36 -35
  35. package/dist/access-offline-manifest.d.ts +12 -12
  36. package/dist/access-offline-manifest.js +5 -5
  37. package/dist/access-operations-batch.js +41 -40
  38. package/dist/access-operations.d.ts +46 -47
  39. package/dist/access-operations.js +44 -43
  40. package/dist/access-recall-concurrency.d.ts +40 -40
  41. package/dist/access-recall-concurrency.js +36 -35
  42. package/dist/access-recall-response.d.ts +24 -24
  43. package/dist/access-recall-response.js +36 -35
  44. package/dist/access-recall-surface.d.ts +24 -24
  45. package/dist/access-recall-surface.js +36 -35
  46. package/dist/access-schema.d.ts +139 -139
  47. package/dist/access-schema.js +3 -3
  48. package/dist/{access-service-B1Lbb-nQ.d.ts → access-service-DkaIG3n8.d.ts} +688 -140
  49. package/dist/access-service-helpers.d.ts +40 -40
  50. package/dist/access-service.d.ts +47 -47
  51. package/dist/access-service.js +36 -35
  52. package/dist/access-surface-catalog.d.ts +41 -42
  53. package/dist/access-surface-catalog.js +44 -37
  54. package/dist/access-surface-catalog.js.map +1 -1
  55. package/dist/access-wearables-meetings-surface.d.ts +5 -3
  56. package/dist/action-confidence.d.ts +3 -1
  57. package/dist/active-memory-bridge.d.ts +3 -1
  58. package/dist/active-recall.d.ts +3 -1
  59. package/dist/active-recall.js +11 -1
  60. package/dist/active-recall.js.map +1 -1
  61. package/dist/adapters/index.js +4 -4
  62. package/dist/adapters/registry.js +2 -2
  63. package/dist/ambient-provenance.d.ts +3 -1
  64. package/dist/artifact-search.d.ts +3 -1
  65. package/dist/{auto-sync-5L7EUZ27.js → auto-sync-H64ZCNIR.js} +13 -5
  66. package/dist/{auto-sync-5L7EUZ27.js.map → auto-sync-H64ZCNIR.js.map} +1 -1
  67. package/dist/behavior-learner.d.ts +3 -1
  68. package/dist/behavior-signals.d.ts +3 -1
  69. package/dist/bootstrap.d.ts +12 -12
  70. package/dist/briefing-window.d.ts +4 -2
  71. package/dist/briefing.d.ts +4 -2
  72. package/dist/briefing.js +16 -15
  73. package/dist/buffer-surprise-report.d.ts +3 -1
  74. package/dist/buffer-turn-helpers.d.ts +3 -1
  75. package/dist/buffer.d.ts +4 -2
  76. package/dist/buffer.js +4 -4
  77. package/dist/bulk-import/index.d.ts +5 -3
  78. package/dist/calibration.d.ts +3 -1
  79. package/dist/calibration.js +1 -1
  80. package/dist/capabilities.d.ts +3 -1
  81. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  82. package/dist/{catalog-DjVhJoRM.d.ts → catalog-CX2BZEJh.d.ts} +1 -1
  83. package/dist/causal-behavior.d.ts +1 -1
  84. package/dist/causal-consolidation.d.ts +3 -1
  85. package/dist/causal-consolidation.js +20 -19
  86. package/dist/causal-consolidation.js.map +1 -1
  87. package/dist/causal-trajectory-graph.d.ts +1 -1
  88. package/dist/{chunk-MIBKEW7A.js → chunk-2VXWFYJE.js} +3 -3
  89. package/dist/{chunk-3YTTAGPB.js → chunk-3DVBKTSO.js} +2 -2
  90. package/dist/{chunk-EIR5VLIH.js → chunk-4K7IW3F7.js} +2 -2
  91. package/dist/chunk-4K7IW3F7.js.map +1 -0
  92. package/dist/{chunk-JTSVMJHV.js → chunk-4XDGYCZI.js} +3 -3
  93. package/dist/chunk-573TEZCD.js +88 -0
  94. package/dist/chunk-573TEZCD.js.map +1 -0
  95. package/dist/{chunk-HQSBYI5X.js → chunk-57HVIG5U.js} +2 -2
  96. package/dist/{chunk-PMVPCZJZ.js → chunk-6433HV72.js} +2 -2
  97. package/dist/{chunk-N6TRHP72.js → chunk-6TAX66IA.js} +2 -2
  98. package/dist/{chunk-AGKIG3YA.js → chunk-6VALIZKG.js} +2 -2
  99. package/dist/{chunk-YQ3CBLL4.js → chunk-76PL4XLG.js} +512 -8
  100. package/dist/chunk-76PL4XLG.js.map +1 -0
  101. package/dist/{chunk-4NZHHGVL.js → chunk-7BDZTALN.js} +4 -1
  102. package/dist/chunk-7BDZTALN.js.map +1 -0
  103. package/dist/{chunk-NS5ETDYD.js → chunk-7BEPVIH3.js} +3 -3
  104. package/dist/{chunk-JG22O53B.js → chunk-7CBKAQIG.js} +2 -1
  105. package/dist/chunk-7CBKAQIG.js.map +1 -0
  106. package/dist/{chunk-LYIZXQYY.js → chunk-7KFIMGMP.js} +2 -2
  107. package/dist/{chunk-LWVO2QZV.js → chunk-7QOO4LGC.js} +56 -56
  108. package/dist/chunk-AAXJNINV.js +55 -0
  109. package/dist/chunk-AAXJNINV.js.map +1 -0
  110. package/dist/{chunk-56BSXSHC.js → chunk-AB3A7EFH.js} +31 -2
  111. package/dist/{chunk-56BSXSHC.js.map → chunk-AB3A7EFH.js.map} +1 -1
  112. package/dist/{chunk-CCTVAVTT.js → chunk-AUO2LJ3K.js} +2 -2
  113. package/dist/{chunk-ALCJRQ72.js → chunk-AYDDB3XI.js} +2 -2
  114. package/dist/{chunk-3TMYV3LP.js → chunk-AYYGRVRP.js} +5 -5
  115. package/dist/{chunk-W6TSGWYS.js → chunk-AZAA5DNP.js} +2 -2
  116. package/dist/{chunk-WTOE7CW4.js → chunk-BKYPJUQL.js} +122 -16
  117. package/dist/chunk-BKYPJUQL.js.map +1 -0
  118. package/dist/{chunk-6JJT5AKI.js → chunk-BQUEAVNW.js} +2 -2
  119. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  120. package/dist/{chunk-CEH2AFXG.js → chunk-C24UZSFW.js} +6 -6
  121. package/dist/{chunk-ZPN7W3OP.js → chunk-CDUUDBWJ.js} +3 -3
  122. package/dist/{chunk-35NN3ZFA.js → chunk-CTVWELNK.js} +21 -3
  123. package/dist/chunk-CTVWELNK.js.map +1 -0
  124. package/dist/{chunk-Y23B2QXP.js → chunk-D27J626P.js} +2 -2
  125. package/dist/{chunk-2572MRP2.js → chunk-DHTQP334.js} +2 -2
  126. package/dist/{chunk-HJQZQUPC.js → chunk-DJZWNKFO.js} +5 -5
  127. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  128. package/dist/{chunk-VJ6ECBIP.js → chunk-E4MVQQVX.js} +2 -2
  129. package/dist/{chunk-SYJ6EKKE.js → chunk-EIVIB73I.js} +2 -2
  130. package/dist/{chunk-HUNSEO5U.js → chunk-EV3YFLCB.js} +2 -2
  131. package/dist/{chunk-GEULB2VI.js → chunk-FC7TIRFS.js} +21 -1
  132. package/dist/chunk-FC7TIRFS.js.map +1 -0
  133. package/dist/{chunk-VPGRXEAB.js → chunk-G57LQRP6.js} +2 -2
  134. package/dist/{chunk-BC4QD3FM.js → chunk-GN55KU2P.js} +2 -2
  135. package/dist/{chunk-4W6INNXO.js → chunk-GUXS322H.js} +2 -2
  136. package/dist/{chunk-DBXZZIDU.js → chunk-H5FW4S5Q.js} +434 -693
  137. package/dist/chunk-H5FW4S5Q.js.map +1 -0
  138. package/dist/chunk-I32NKXE2.js +879 -0
  139. package/dist/chunk-I32NKXE2.js.map +1 -0
  140. package/dist/{chunk-3MADV4YC.js → chunk-IQSC3V2E.js} +2 -2
  141. package/dist/{chunk-UYCIS6V5.js → chunk-JCWY73FK.js} +2 -2
  142. package/dist/{chunk-T42L3S6A.js → chunk-LBWQ65SX.js} +4 -4
  143. package/dist/{chunk-MSTNSVXI.js → chunk-LDBFXD4F.js} +2 -1
  144. package/dist/chunk-LDBFXD4F.js.map +1 -0
  145. package/dist/{chunk-CEQF6L5K.js → chunk-LUNJ3WFO.js} +9 -7
  146. package/dist/chunk-LUNJ3WFO.js.map +1 -0
  147. package/dist/{chunk-256FBCVX.js → chunk-M4AHBM72.js} +7 -7
  148. package/dist/{chunk-YEQEGSJA.js → chunk-MBDJJTAT.js} +3 -3
  149. package/dist/{chunk-IAY7YBXS.js → chunk-MRTUN665.js} +2 -2
  150. package/dist/{chunk-JFJ3MHGV.js → chunk-NXKJHSDN.js} +1 -1
  151. package/dist/chunk-NXKJHSDN.js.map +1 -0
  152. package/dist/{chunk-R3AAOKR2.js → chunk-NZWEEALH.js} +2 -2
  153. package/dist/{chunk-I2HTKOOP.js → chunk-ODZG724P.js} +2 -2
  154. package/dist/{chunk-7QFBLYFP.js → chunk-OJD4WS2E.js} +3 -3
  155. package/dist/{chunk-CUS6S7PN.js → chunk-OLWTCWQ2.js} +9 -9
  156. package/dist/chunk-OLWTCWQ2.js.map +1 -0
  157. package/dist/{chunk-53ENM6F6.js → chunk-P4DHENRE.js} +2 -2
  158. package/dist/{chunk-DEFI2QBT.js → chunk-PAYGC3BD.js} +52 -22
  159. package/dist/chunk-PAYGC3BD.js.map +1 -0
  160. package/dist/{chunk-KPBLJCCV.js → chunk-PDVQ5JU3.js} +2 -2
  161. package/dist/{chunk-7BY7V4YX.js → chunk-Q4WONDMJ.js} +4 -3
  162. package/dist/{chunk-7BY7V4YX.js.map → chunk-Q4WONDMJ.js.map} +1 -1
  163. package/dist/{chunk-ESCQ6LY7.js → chunk-QFMO32TO.js} +3 -3
  164. package/dist/{chunk-HQSPXTQO.js → chunk-QXIQPCCN.js} +1 -1
  165. package/dist/{chunk-CFRBTATE.js → chunk-R47WINCY.js} +3 -3
  166. package/dist/{chunk-G72IPMR7.js → chunk-R4OCGPSH.js} +2 -2
  167. package/dist/{chunk-LCU5R6TQ.js → chunk-RAIUTAZZ.js} +22 -3
  168. package/dist/chunk-RAIUTAZZ.js.map +1 -0
  169. package/dist/chunk-RIAM7DND.js +167 -0
  170. package/dist/chunk-RIAM7DND.js.map +1 -0
  171. package/dist/{chunk-VZVP2W4O.js → chunk-RK4WXUI2.js} +2 -2
  172. package/dist/{chunk-JBPKEARU.js → chunk-RKNJBZ55.js} +4 -4
  173. package/dist/{chunk-6L27N6YI.js → chunk-S7Q5CM7E.js} +2 -2
  174. package/dist/{chunk-CTQRPANJ.js → chunk-SVSRPG6M.js} +2 -2
  175. package/dist/{chunk-GXWDWOWP.js → chunk-TDKSCV62.js} +2 -2
  176. package/dist/{chunk-S2VBSTTA.js → chunk-THG4GYIE.js} +2 -2
  177. package/dist/{chunk-IXRC7EQG.js → chunk-TR7DGXGI.js} +2 -2
  178. package/dist/{chunk-63FHSH7E.js → chunk-TYUS5TLG.js} +4 -4
  179. package/dist/{chunk-7PWWGYGO.js → chunk-URHCUXKI.js} +2 -2
  180. package/dist/{chunk-Z54TXZ2F.js → chunk-VA5KMSAE.js} +16 -7
  181. package/dist/chunk-VA5KMSAE.js.map +1 -0
  182. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  183. package/dist/{chunk-MUJ4QFUQ.js → chunk-W4BMEGQA.js} +4 -4
  184. package/dist/{chunk-5HK3CJAR.js → chunk-W675UJCZ.js} +2 -2
  185. package/dist/{chunk-3PXVYJMC.js → chunk-WN5FSGLE.js} +2077 -215
  186. package/dist/chunk-WN5FSGLE.js.map +1 -0
  187. package/dist/{chunk-3RHFGGI5.js → chunk-YF6UHWOV.js} +30 -10
  188. package/dist/chunk-YF6UHWOV.js.map +1 -0
  189. package/dist/{chunk-X5SDZMO7.js → chunk-YHCBJULX.js} +2 -2
  190. package/dist/{chunk-5UYSCYR7.js → chunk-Z4RPXHJH.js} +4 -4
  191. package/dist/{cli-Pulnz-qm.d.ts → cli-ChKeYMZv.d.ts} +5 -5
  192. package/dist/cli.d.ts +25 -25
  193. package/dist/cli.js +81 -78
  194. package/dist/coding/pre-action-gate.d.ts +2 -60
  195. package/dist/compounding/engine.d.ts +4 -2
  196. package/dist/compounding/engine.js +17 -16
  197. package/dist/compounding/preference-consolidator.d.ts +3 -1
  198. package/dist/compression-optimizer.d.ts +3 -1
  199. package/dist/config.d.ts +3 -1
  200. package/dist/config.js +11 -1
  201. package/dist/connectors/codex-materialize-runner.d.ts +3 -1
  202. package/dist/connectors/codex-materialize-runner.js +16 -15
  203. package/dist/connectors/codex-materialize.d.ts +3 -1
  204. package/dist/connectors/index.d.ts +3 -1
  205. package/dist/connectors/index.js +16 -15
  206. package/dist/consolidation-provenance-check.d.ts +4 -2
  207. package/dist/consolidation-undo.d.ts +4 -2
  208. package/dist/contradiction/index.d.ts +10 -270
  209. package/dist/contradiction/index.js +20 -7
  210. package/dist/{contradiction-review-QKJH5DKD.js → contradiction-review-WRVLRWHS.js} +6 -2
  211. package/dist/{contradiction-scan-VOTQRRSX.js → contradiction-scan-AKUWPX5N.js} +5 -3
  212. package/dist/converge-config.d.ts +3 -1
  213. package/dist/convergence-refresh.d.ts +4 -2
  214. package/dist/convergence-refresh.js +18 -17
  215. package/dist/conversation-index/backend.d.ts +3 -1
  216. package/dist/conversation-index/chunker.d.ts +3 -1
  217. package/dist/conversation-index/faiss-adapter.d.ts +3 -1
  218. package/dist/conversation-index/indexer.d.ts +3 -1
  219. package/dist/conversation-index/search.d.ts +3 -1
  220. package/dist/corpus-watermark.d.ts +3 -1
  221. package/dist/corpus-watermark.js +18 -17
  222. package/dist/cross-lingual-recall.d.ts +3 -1
  223. package/dist/day-summary.d.ts +3 -1
  224. package/dist/delinearize.d.ts +3 -1
  225. package/dist/dependency-propagation-config.d.ts +3 -1
  226. package/dist/{dependency-propagation-delivery-Co17gxYb.d.ts → dependency-propagation-delivery-B8ZMBBCY.d.ts} +1 -1
  227. package/dist/direct-answer-wiring.d.ts +3 -1
  228. package/dist/direct-answer.d.ts +3 -1
  229. package/dist/embedding-fallback.d.ts +3 -1
  230. package/dist/enrichment/index.d.ts +3 -1
  231. package/dist/entity-origin-fields.d.ts +3 -1
  232. package/dist/entity-retrieval.d.ts +4 -2
  233. package/dist/entity-retrieval.js +16 -15
  234. package/dist/entity-schema.d.ts +3 -1
  235. package/dist/episodic-context.d.ts +3 -1
  236. package/dist/explicit-capture.d.ts +12 -12
  237. package/dist/explicit-capture.js +7 -6
  238. package/dist/external-wiki-access.d.ts +41 -42
  239. package/dist/external-wiki-access.js +37 -36
  240. package/dist/external-wiki-collection-registration.d.ts +3 -1
  241. package/dist/external-wiki-collection.d.ts +3 -1
  242. package/dist/external-wiki-mcp-tools.d.ts +40 -40
  243. package/dist/extraction-error-classification.d.ts +3 -1
  244. package/dist/extraction-faithfulness.d.ts +3 -1
  245. package/dist/extraction-judge-telemetry.d.ts +3 -1
  246. package/dist/extraction-judge-training.d.ts +3 -1
  247. package/dist/extraction-judge.d.ts +3 -1
  248. package/dist/extraction-judge.js +2 -2
  249. package/dist/extraction-liveness.d.ts +3 -1
  250. package/dist/extraction-normalization.d.ts +3 -1
  251. package/dist/extraction-normalization.js +2 -2
  252. package/dist/extraction-prompt.d.ts +4 -2
  253. package/dist/extraction-prompt.js +1 -1
  254. package/dist/extraction-source-grounding-rules.d.ts +3 -1
  255. package/dist/extraction-source-grounding.d.ts +3 -1
  256. package/dist/extraction.d.ts +3 -1
  257. package/dist/extraction.js +22 -21
  258. package/dist/fallback-llm.d.ts +3 -1
  259. package/dist/graph-dashboard-diff.d.ts +3 -1
  260. package/dist/graph-dashboard-key.d.ts +3 -1
  261. package/dist/graph-dashboard-parser.d.ts +3 -1
  262. package/dist/graph-edge-reinforcement.d.ts +3 -1
  263. package/dist/graph-path-reconstruction.d.ts +3 -1
  264. package/dist/graph-path-scoring.d.ts +3 -1
  265. package/dist/graph-snapshot.d.ts +3 -1
  266. package/dist/graph.d.ts +3 -1
  267. package/dist/harmonic-retrieval.js +1 -1
  268. package/dist/importance.d.ts +3 -1
  269. package/dist/importers/index.d.ts +3 -1
  270. package/dist/in-flight-reads.d.ts +3 -1
  271. package/dist/index-CexDrJzC.d.ts +284 -0
  272. package/dist/index.d.ts +946 -707
  273. package/dist/index.js +253 -151
  274. package/dist/intent.d.ts +3 -1
  275. package/dist/lcm/engine.d.ts +3 -1
  276. package/dist/lcm/engine.js +3 -3
  277. package/dist/lcm/index.d.ts +3 -1
  278. package/dist/lcm/index.js +6 -6
  279. package/dist/lcm/tools.d.ts +3 -1
  280. package/dist/lifecycle.d.ts +3 -1
  281. package/dist/live-connectors-runner.d.ts +3 -1
  282. package/dist/local-llm.d.ts +3 -1
  283. package/dist/local-model-endpoint.d.ts +3 -1
  284. package/dist/maintenance/memory-governance.d.ts +3 -1
  285. package/dist/maintenance/memory-governance.js +16 -15
  286. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +4 -2
  287. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +16 -15
  288. package/dist/maintenance/rebuild-memory-projection.d.ts +4 -2
  289. package/dist/maintenance/rebuild-memory-projection.js +17 -16
  290. package/dist/{maintenance-DiSLtVN7.d.ts → maintenance-nkdtSrVr.d.ts} +3 -3
  291. package/dist/mcp-memory-inspector-app.d.ts +37 -37
  292. package/dist/mcp-read-only-tools.js +1 -1
  293. package/dist/memory-action-policy.d.ts +3 -1
  294. package/dist/memory-cache.d.ts +3 -1
  295. package/dist/memory-lifecycle-ledger-utils.d.ts +3 -1
  296. package/dist/memory-projection-store.d.ts +3 -1
  297. package/dist/memory-provenance.d.ts +3 -1
  298. package/dist/memory-snapshot.d.ts +3 -1
  299. package/dist/memory-subject-cli.d.ts +177 -0
  300. package/dist/memory-subject-cli.js +16 -0
  301. package/dist/memory-subject.d.ts +128 -0
  302. package/dist/memory-subject.js +44 -0
  303. package/dist/memory-subject.js.map +1 -0
  304. package/dist/memory-worth-outcomes.d.ts +7 -4
  305. package/dist/memory-worth-outcomes.js +1 -1
  306. package/dist/models-json.d.ts +3 -1
  307. package/dist/namespaces/migrate.d.ts +5 -3
  308. package/dist/namespaces/migrate.js +17 -16
  309. package/dist/namespaces/principal.d.ts +3 -1
  310. package/dist/namespaces/search.d.ts +3 -1
  311. package/dist/namespaces/search.js +6 -6
  312. package/dist/namespaces/storage.d.ts +5 -3
  313. package/dist/namespaces/storage.js +16 -15
  314. package/dist/native-knowledge.d.ts +3 -1
  315. package/dist/offline-sync-impression-drain.d.ts +3 -1
  316. package/dist/operator-doctor-corpus.d.ts +3 -1
  317. package/dist/operator-doctor-corpus.js +19 -18
  318. package/dist/operator-doctor-replica.d.ts +3 -1
  319. package/dist/operator-toolkit.d.ts +5 -3
  320. package/dist/operator-toolkit.js +29 -24
  321. package/dist/orchestration/compression-guideline-coordinator.d.ts +4 -2
  322. package/dist/orchestration/maintenance.d.ts +6 -4
  323. package/dist/orchestration/maintenance.js +22 -21
  324. package/dist/{orchestrator-DzCaxNS4.d.ts → orchestrator-Bvq1-RQs.d.ts} +23 -9
  325. package/dist/orchestrator.d.ts +12 -12
  326. package/dist/orchestrator.js +111 -108
  327. package/dist/patterns-cli.d.ts +3 -1
  328. package/dist/{pipeline-HJ8DKbFl.d.ts → pipeline-C8vfIiTX.d.ts} +1 -1
  329. package/dist/policy-runtime.d.ts +3 -1
  330. package/dist/proactive-contention.d.ts +3 -1
  331. package/dist/provenance.d.ts +3 -1
  332. package/dist/{public-http-DwXauTX3.d.ts → public-http-Cy0aYbys.d.ts} +1 -1
  333. package/dist/{qmd-BzxluoLt.d.ts → qmd-B9p1SZTZ.d.ts} +1 -1
  334. package/dist/qmd-preflight.d.ts +4 -2
  335. package/dist/qmd-recall-cache.d.ts +3 -1
  336. package/dist/qmd.d.ts +4 -2
  337. package/dist/qmd.js +4 -4
  338. package/dist/recall-concurrency-config.d.ts +3 -1
  339. package/dist/recall-disclosure-escalation.d.ts +3 -1
  340. package/dist/recall-explain-renderer.d.ts +3 -1
  341. package/dist/recall-explain-renderer.js +3 -3
  342. package/dist/recall-memory-map.d.ts +3 -1
  343. package/dist/recall-planner-llm.d.ts +3 -1
  344. package/dist/recall-state.d.ts +3 -1
  345. package/dist/recall-tag-filter.d.ts +3 -1
  346. package/dist/recall-timings.d.ts +3 -1
  347. package/dist/recall-xray-cli.d.ts +3 -1
  348. package/dist/recall-xray-cli.js +4 -4
  349. package/dist/recall-xray-renderer.d.ts +3 -1
  350. package/dist/recall-xray-renderer.js +3 -3
  351. package/dist/recall-xray.d.ts +3 -1
  352. package/dist/recall-xray.js +2 -2
  353. package/dist/reconcile/cursor.d.ts +3 -1
  354. package/dist/reconcile/manifest.d.ts +3 -1
  355. package/dist/reconcile/manifest.js +3 -3
  356. package/dist/reconcile/plan.d.ts +3 -1
  357. package/dist/replay/normalizers/chatgpt.d.ts +3 -1
  358. package/dist/replay/normalizers/claude.d.ts +3 -1
  359. package/dist/replay/normalizers/openclaw.d.ts +3 -1
  360. package/dist/replay/normalizers/shared.d.ts +3 -1
  361. package/dist/replay/runner.d.ts +3 -1
  362. package/dist/replay/types.d.ts +3 -1
  363. package/dist/replica-divergence.d.ts +3 -1
  364. package/dist/replica-peers-config.d.ts +3 -1
  365. package/dist/resolution-4552BNRV.js +36 -0
  366. package/dist/resolution-4552BNRV.js.map +1 -0
  367. package/dist/resolve-auth-token.d.ts +3 -1
  368. package/dist/resume-bundles.js +9 -2
  369. package/dist/retrieval-agents.d.ts +4 -2
  370. package/dist/retrieval-tiers.d.ts +3 -1
  371. package/dist/routing/engine.d.ts +3 -1
  372. package/dist/routing/store.d.ts +3 -1
  373. package/dist/salvage-envelope.d.ts +3 -1
  374. package/dist/salvage-envelope.js +10 -2
  375. package/dist/schemas.d.ts +130 -116
  376. package/dist/schemas.js +1 -1
  377. package/dist/{scope-profiles-CEMZz0t5.d.ts → scope-profiles-DLrRxFWR.d.ts} +1 -1
  378. package/dist/search/embed-helper.d.ts +3 -1
  379. package/dist/search/factory.d.ts +3 -1
  380. package/dist/search/factory.js +5 -5
  381. package/dist/search/index.d.ts +3 -1
  382. package/dist/search/index.js +9 -9
  383. package/dist/search/lancedb-backend.d.ts +3 -1
  384. package/dist/search/meilisearch-backend.d.ts +3 -1
  385. package/dist/search/noop-backend.d.ts +3 -1
  386. package/dist/search/orama-backend.d.ts +3 -1
  387. package/dist/search/port.d.ts +3 -1
  388. package/dist/search/remote-backend.d.ts +3 -1
  389. package/dist/{semantic-TddhF0y7.d.ts → semantic-BalR6EXU.d.ts} +1 -1
  390. package/dist/{semantic-consolidation-D9dYj6vu.d.ts → semantic-consolidation-DlN6xe20.d.ts} +1 -1
  391. package/dist/semantic-consolidation.d.ts +4 -2
  392. package/dist/semantic-consolidation.js +17 -16
  393. package/dist/semantic-rule-promotion.js +16 -15
  394. package/dist/semantic-rule-verifier.d.ts +3 -1
  395. package/dist/semantic-rule-verifier.js +16 -15
  396. package/dist/{service-_qgST_0y.d.ts → service-GkYyVfm9.d.ts} +2 -2
  397. package/dist/session-observer-bands.d.ts +3 -1
  398. package/dist/session-observer-state.d.ts +3 -1
  399. package/dist/shared-context/manager.d.ts +13 -11
  400. package/dist/signal.d.ts +3 -1
  401. package/dist/source-agent-qualifier.d.ts +3 -1
  402. package/dist/source-agent-qualifier.js +18 -17
  403. package/dist/{storage-B9lbNTYE.d.ts → storage-BIDzuKIn.d.ts} +3 -2
  404. package/dist/storage.d.ts +4 -2
  405. package/dist/storage.js +18 -17
  406. package/dist/subject-config.d.ts +40 -0
  407. package/dist/subject-config.js +20 -0
  408. package/dist/subject-config.js.map +1 -0
  409. package/dist/summarizer.d.ts +3 -1
  410. package/dist/summary-snapshot.d.ts +3 -1
  411. package/dist/support-passport/index.d.ts +43 -43
  412. package/dist/support-passport/index.js +39 -38
  413. package/dist/temporal-supersession.d.ts +4 -2
  414. package/dist/temporal-timeline-recall.d.ts +3 -1
  415. package/dist/temporal-validity.d.ts +3 -1
  416. package/dist/threading.d.ts +3 -1
  417. package/dist/tier-migration.d.ts +4 -2
  418. package/dist/tier-routing.d.ts +3 -1
  419. package/dist/topics.d.ts +3 -1
  420. package/dist/transcript.d.ts +3 -1
  421. package/dist/transfer/backup.js +2 -2
  422. package/dist/transfer/capsule-export.js +2 -2
  423. package/dist/transfer/capsule-import.js +2 -2
  424. package/dist/transfer/types.d.ts +86 -86
  425. package/dist/trust-score-stage.d.ts +3 -1
  426. package/dist/trust-score.d.ts +3 -1
  427. package/dist/{types-BrEzuxPA.d.ts → types-BxAcOl5Q.d.ts} +360 -32
  428. package/dist/types.d.ts +3 -1
  429. package/dist/types.js +1 -1
  430. package/dist/utility-runtime.d.ts +3 -1
  431. package/dist/verified-recall.js +16 -15
  432. package/dist/who-knows-cli.d.ts +40 -40
  433. package/dist/who-knows-cli.js +17 -16
  434. package/dist/who-knows.d.ts +42 -42
  435. package/dist/who-knows.js +16 -15
  436. package/dist/write-envelope.d.ts +7 -2
  437. package/dist/write-envelope.js +10 -1
  438. package/package.json +2 -2
  439. package/scripts/build-with-heap.mjs +1 -1
  440. package/src/access-boundary.ts +6 -0
  441. package/src/access-http.ts +6 -5
  442. package/src/access-mcp-admin-tools.ts +38 -0
  443. package/src/access-mcp-output-schemas.ts +20 -0
  444. package/src/access-mcp.ts +8 -8
  445. package/src/access-operations-batch.ts +13 -0
  446. package/src/access-service.ts +112 -0
  447. package/src/access-surface-catalog.test.ts +24 -0
  448. package/src/access-surface-catalog.ts +6 -0
  449. package/src/coding/action-gate.test.ts +341 -0
  450. package/src/coding/action-gate.ts +556 -0
  451. package/src/config.ts +15 -17
  452. package/src/contradiction/contradiction-review.ts +50 -4
  453. package/src/contradiction/contradiction-scan.ts +14 -3
  454. package/src/contradiction/resolution.ts +50 -2
  455. package/src/extraction-prompt.ts +1 -0
  456. package/src/extraction.ts +4 -1
  457. package/src/index.ts +9 -9
  458. package/src/mcp-read-only-tools.ts +1 -0
  459. package/src/memory-subject-cli.ts +135 -0
  460. package/src/memory-subject.test.ts +48 -0
  461. package/src/memory-subject.ts +314 -0
  462. package/src/memory-worth-outcomes.test.ts +6 -2
  463. package/src/memory-worth-outcomes.ts +11 -7
  464. package/src/operator-toolkit.ts +1 -1
  465. package/src/orchestration/extraction-persist.ts +48 -48
  466. package/src/orchestration/recall-internal.ts +6 -5
  467. package/src/orchestration/recall-rerank-coordinator.ts +123 -4
  468. package/src/orchestration/recall-result-formatter.ts +6 -2
  469. package/src/orchestration/recall-search-pipeline.ts +14 -1
  470. package/src/preferences/drift-config.ts +118 -0
  471. package/src/preferences/drift-recall.ts +133 -0
  472. package/src/preferences/drift-types.ts +87 -0
  473. package/src/preferences/index.ts +12 -0
  474. package/src/preferences/preference-drift.test.ts +690 -0
  475. package/src/preferences/preference-drift.ts +724 -0
  476. package/src/procedural/library-maintenance.test.ts +582 -0
  477. package/src/procedural/library-maintenance.ts +546 -0
  478. package/src/procedural/maintenance-config.ts +78 -0
  479. package/src/procedural/procedure-recall.ts +29 -1
  480. package/src/procedural/procedure-stats.ts +20 -0
  481. package/src/review/review-deck-access.test.ts +325 -0
  482. package/src/review/review-deck-http-base.ts +133 -0
  483. package/src/review/review-deck-mutation.test.ts +417 -0
  484. package/src/review/review-deck-mutation.ts +677 -0
  485. package/src/review/review-deck-snapshot.ts +196 -0
  486. package/src/review/review-deck-surface.ts +117 -0
  487. package/src/review/review-deck.test.ts +347 -0
  488. package/src/review/review-deck.ts +311 -0
  489. package/src/schemas.ts +7 -0
  490. package/src/spaces/index.ts +38 -2
  491. package/src/storage.ts +10 -8
  492. package/src/subject-config.ts +80 -0
  493. package/src/types.ts +33 -32
  494. package/src/write-envelope.test.ts +14 -0
  495. package/src/write-envelope.ts +24 -3
  496. package/dist/chunk-35NN3ZFA.js.map +0 -1
  497. package/dist/chunk-3PXVYJMC.js.map +0 -1
  498. package/dist/chunk-3RHFGGI5.js.map +0 -1
  499. package/dist/chunk-4NZHHGVL.js.map +0 -1
  500. package/dist/chunk-CEQF6L5K.js.map +0 -1
  501. package/dist/chunk-CUS6S7PN.js.map +0 -1
  502. package/dist/chunk-DBXZZIDU.js.map +0 -1
  503. package/dist/chunk-DEFI2QBT.js.map +0 -1
  504. package/dist/chunk-EIR5VLIH.js.map +0 -1
  505. package/dist/chunk-GEULB2VI.js.map +0 -1
  506. package/dist/chunk-JFJ3MHGV.js.map +0 -1
  507. package/dist/chunk-JG22O53B.js.map +0 -1
  508. package/dist/chunk-LCU5R6TQ.js.map +0 -1
  509. package/dist/chunk-MDVBCPOS.js +0 -402
  510. package/dist/chunk-MDVBCPOS.js.map +0 -1
  511. package/dist/chunk-MSTNSVXI.js.map +0 -1
  512. package/dist/chunk-WTOE7CW4.js.map +0 -1
  513. package/dist/chunk-YQ3CBLL4.js.map +0 -1
  514. package/dist/chunk-Z54TXZ2F.js.map +0 -1
  515. package/dist/resolution-DKMBPXHI.js +0 -18
  516. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  517. /package/dist/{chunk-MIBKEW7A.js.map → chunk-2VXWFYJE.js.map} +0 -0
  518. /package/dist/{chunk-3YTTAGPB.js.map → chunk-3DVBKTSO.js.map} +0 -0
  519. /package/dist/{chunk-JTSVMJHV.js.map → chunk-4XDGYCZI.js.map} +0 -0
  520. /package/dist/{chunk-HQSBYI5X.js.map → chunk-57HVIG5U.js.map} +0 -0
  521. /package/dist/{chunk-PMVPCZJZ.js.map → chunk-6433HV72.js.map} +0 -0
  522. /package/dist/{chunk-N6TRHP72.js.map → chunk-6TAX66IA.js.map} +0 -0
  523. /package/dist/{chunk-AGKIG3YA.js.map → chunk-6VALIZKG.js.map} +0 -0
  524. /package/dist/{chunk-NS5ETDYD.js.map → chunk-7BEPVIH3.js.map} +0 -0
  525. /package/dist/{chunk-LYIZXQYY.js.map → chunk-7KFIMGMP.js.map} +0 -0
  526. /package/dist/{chunk-LWVO2QZV.js.map → chunk-7QOO4LGC.js.map} +0 -0
  527. /package/dist/{chunk-CCTVAVTT.js.map → chunk-AUO2LJ3K.js.map} +0 -0
  528. /package/dist/{chunk-ALCJRQ72.js.map → chunk-AYDDB3XI.js.map} +0 -0
  529. /package/dist/{chunk-3TMYV3LP.js.map → chunk-AYYGRVRP.js.map} +0 -0
  530. /package/dist/{chunk-W6TSGWYS.js.map → chunk-AZAA5DNP.js.map} +0 -0
  531. /package/dist/{chunk-6JJT5AKI.js.map → chunk-BQUEAVNW.js.map} +0 -0
  532. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  533. /package/dist/{chunk-CEH2AFXG.js.map → chunk-C24UZSFW.js.map} +0 -0
  534. /package/dist/{chunk-ZPN7W3OP.js.map → chunk-CDUUDBWJ.js.map} +0 -0
  535. /package/dist/{chunk-Y23B2QXP.js.map → chunk-D27J626P.js.map} +0 -0
  536. /package/dist/{chunk-2572MRP2.js.map → chunk-DHTQP334.js.map} +0 -0
  537. /package/dist/{chunk-HJQZQUPC.js.map → chunk-DJZWNKFO.js.map} +0 -0
  538. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  539. /package/dist/{chunk-VJ6ECBIP.js.map → chunk-E4MVQQVX.js.map} +0 -0
  540. /package/dist/{chunk-SYJ6EKKE.js.map → chunk-EIVIB73I.js.map} +0 -0
  541. /package/dist/{chunk-HUNSEO5U.js.map → chunk-EV3YFLCB.js.map} +0 -0
  542. /package/dist/{chunk-VPGRXEAB.js.map → chunk-G57LQRP6.js.map} +0 -0
  543. /package/dist/{chunk-BC4QD3FM.js.map → chunk-GN55KU2P.js.map} +0 -0
  544. /package/dist/{chunk-4W6INNXO.js.map → chunk-GUXS322H.js.map} +0 -0
  545. /package/dist/{chunk-3MADV4YC.js.map → chunk-IQSC3V2E.js.map} +0 -0
  546. /package/dist/{chunk-UYCIS6V5.js.map → chunk-JCWY73FK.js.map} +0 -0
  547. /package/dist/{chunk-T42L3S6A.js.map → chunk-LBWQ65SX.js.map} +0 -0
  548. /package/dist/{chunk-256FBCVX.js.map → chunk-M4AHBM72.js.map} +0 -0
  549. /package/dist/{chunk-YEQEGSJA.js.map → chunk-MBDJJTAT.js.map} +0 -0
  550. /package/dist/{chunk-IAY7YBXS.js.map → chunk-MRTUN665.js.map} +0 -0
  551. /package/dist/{chunk-R3AAOKR2.js.map → chunk-NZWEEALH.js.map} +0 -0
  552. /package/dist/{chunk-I2HTKOOP.js.map → chunk-ODZG724P.js.map} +0 -0
  553. /package/dist/{chunk-7QFBLYFP.js.map → chunk-OJD4WS2E.js.map} +0 -0
  554. /package/dist/{chunk-53ENM6F6.js.map → chunk-P4DHENRE.js.map} +0 -0
  555. /package/dist/{chunk-KPBLJCCV.js.map → chunk-PDVQ5JU3.js.map} +0 -0
  556. /package/dist/{chunk-ESCQ6LY7.js.map → chunk-QFMO32TO.js.map} +0 -0
  557. /package/dist/{chunk-HQSPXTQO.js.map → chunk-QXIQPCCN.js.map} +0 -0
  558. /package/dist/{chunk-CFRBTATE.js.map → chunk-R47WINCY.js.map} +0 -0
  559. /package/dist/{chunk-G72IPMR7.js.map → chunk-R4OCGPSH.js.map} +0 -0
  560. /package/dist/{chunk-VZVP2W4O.js.map → chunk-RK4WXUI2.js.map} +0 -0
  561. /package/dist/{chunk-JBPKEARU.js.map → chunk-RKNJBZ55.js.map} +0 -0
  562. /package/dist/{chunk-6L27N6YI.js.map → chunk-S7Q5CM7E.js.map} +0 -0
  563. /package/dist/{chunk-CTQRPANJ.js.map → chunk-SVSRPG6M.js.map} +0 -0
  564. /package/dist/{chunk-GXWDWOWP.js.map → chunk-TDKSCV62.js.map} +0 -0
  565. /package/dist/{chunk-S2VBSTTA.js.map → chunk-THG4GYIE.js.map} +0 -0
  566. /package/dist/{chunk-IXRC7EQG.js.map → chunk-TR7DGXGI.js.map} +0 -0
  567. /package/dist/{chunk-63FHSH7E.js.map → chunk-TYUS5TLG.js.map} +0 -0
  568. /package/dist/{chunk-7PWWGYGO.js.map → chunk-URHCUXKI.js.map} +0 -0
  569. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  570. /package/dist/{chunk-MUJ4QFUQ.js.map → chunk-W4BMEGQA.js.map} +0 -0
  571. /package/dist/{chunk-5HK3CJAR.js.map → chunk-W675UJCZ.js.map} +0 -0
  572. /package/dist/{chunk-X5SDZMO7.js.map → chunk-YHCBJULX.js.map} +0 -0
  573. /package/dist/{chunk-5UYSCYR7.js.map → chunk-Z4RPXHJH.js.map} +0 -0
  574. /package/dist/{contradiction-review-QKJH5DKD.js.map → contradiction-review-WRVLRWHS.js.map} +0 -0
  575. /package/dist/{contradiction-scan-VOTQRRSX.js.map → contradiction-scan-AKUWPX5N.js.map} +0 -0
  576. /package/dist/{resolution-DKMBPXHI.js.map → memory-subject-cli.js.map} +0 -0
@@ -0,0 +1,724 @@
1
+ /**
2
+ * Preference drift detection (issue #2371).
3
+ *
4
+ * Remnic already handles *explicit* preference change: the contradiction scan
5
+ * catches a new statement that conflicts with a stored one, and temporal
6
+ * supersession retires a fact that gets restated. Both require new evidence to
7
+ * arrive. This module covers the opposite failure mode the agent-memory survey
8
+ * calls stale preference reuse: a `category: preference` memory keeps being
9
+ * injected at full prominence even though the last N days of conversation
10
+ * contain no corroboration for it — or contain behavioral evidence pointing the
11
+ * other way that never rose to an explicit contradiction.
12
+ *
13
+ * Per aging preference the scan gathers recent same-namespace evidence, then
14
+ * classifies:
15
+ *
16
+ * - `corroborated` — recent evidence restates or is consistent with it.
17
+ * - `stale` — the lookback window held nothing either way.
18
+ * - `drifted` — recent evidence points away from it (judge verdict
19
+ * `contradicts`), so a review item is opened.
20
+ * - `skipped` — the classification could not be made honestly:
21
+ * `backend_unavailable` (evidence lookup failed — §22:
22
+ * never counted as "no evidence") or
23
+ * `verification_unavailable` (evidence exists but no LLM
24
+ * could judge it, so neither a positive nor a negative
25
+ * claim is warranted).
26
+ *
27
+ * Apply mode stamps `lastCorroborated` / `driftState` and opens review items.
28
+ * It never auto-deletes and never auto-supersedes: drift is inference, and the
29
+ * least-privileged default for an inferred change to user state is "ask"
30
+ * (Review Prevention Checklist §36). Resolution runs through the existing
31
+ * review queue and `review_resolve`, with the drift verbs `keep`, `supersede`,
32
+ * and `archive`.
33
+ *
34
+ * Deliberately NOT part of `runMemoryGovernance` — same isolation decision as
35
+ * procedure mining and procedure-library maintenance.
36
+ */
37
+
38
+ import path from "node:path";
39
+ import type { MemoryFile, MemoryStatus, PluginConfig } from "../types.js";
40
+ import type { StorageManager } from "../storage.js";
41
+ import type { LocalLlmClient } from "../local-llm.js";
42
+ import type { FallbackLlmClient } from "../fallback-llm.js";
43
+ import type { SemanticDedupHit, SemanticDedupLookup } from "../dedup/semantic.js";
44
+ import {
45
+ judgeContradictionPairs,
46
+ type ContradictionJudgeInput,
47
+ type ContradictionJudgeResult,
48
+ } from "../contradiction/contradiction-judge.js";
49
+ import {
50
+ computeMemoryContentHash,
51
+ writePairs,
52
+ type ContradictionPair,
53
+ } from "../contradiction/contradiction-review.js";
54
+ import { resolveScanStorage, type ScanStorageResolution } from "../contradiction/contradiction-scan.js";
55
+ import { bumpMemoryCorpusVersionForDir } from "../memory-corpus-version.js";
56
+ import { composeMemoryEnvelope } from "../write-envelope.js";
57
+ import { readJsonFile, writeJsonFileAtomic } from "../json-store.js";
58
+ import { log } from "../logger.js";
59
+
60
+ const DRIFT_ACTOR = "preference-drift";
61
+ const DRIFT_RULE_VERSION = "preference-drift:1";
62
+ const DAY_MS = 24 * 60 * 60 * 1000;
63
+
64
+ /** Evidence hits fetched per preference. Bounded so one scan cannot fan out. */
65
+ const EVIDENCE_LOOKUP_LIMIT = 5;
66
+
67
+ export type PreferenceDriftClassification =
68
+ | "corroborated"
69
+ | "stale"
70
+ | "drifted"
71
+ | "skipped";
72
+
73
+ export type PreferenceDriftSkipReason =
74
+ | "backend_unavailable"
75
+ | "verification_unavailable";
76
+
77
+ export interface PreferenceDriftEvidence {
78
+ memoryId: string;
79
+ /** Similarity score reported by the evidence lookup. */
80
+ score: number;
81
+ /** `updated` (or `created`) of the evidence memory, ISO 8601. */
82
+ observedAt: string;
83
+ }
84
+
85
+ export interface PreferenceDriftFinding {
86
+ memoryId: string;
87
+ classification: PreferenceDriftClassification;
88
+ /** Set only for `classification: "skipped"`. */
89
+ skipped?: PreferenceDriftSkipReason;
90
+ /** Age of the preference in whole days at the run instant. */
91
+ ageDays: number;
92
+ /** Existing `lastCorroborated` stamp, or null when never corroborated. */
93
+ lastCorroborated: string | null;
94
+ reason: string;
95
+ evidence: PreferenceDriftEvidence[];
96
+ /** Review pair id opened for a `drifted` finding in apply mode. */
97
+ reviewPairId?: string;
98
+ }
99
+
100
+ export interface PreferenceDriftReport {
101
+ schemaVersion: 1;
102
+ generatedAt: string;
103
+ mode: "shadow" | "apply";
104
+ namespace?: string;
105
+ /** Active preferences examined after the min-age filter and per-run cap. */
106
+ scanned: number;
107
+ /** Active preferences that passed the category/status filter before the cap. */
108
+ eligible: number;
109
+ findings: PreferenceDriftFinding[];
110
+ counts: Record<PreferenceDriftClassification, number>;
111
+ /** Frontmatter stamps + review items actually written. Always 0 in shadow mode. */
112
+ appliedCount: number;
113
+ reviewItemsOpened: number;
114
+ elapsedMs: number;
115
+ skippedReason?: "drift_disabled" | "scan_disabled";
116
+ }
117
+
118
+ export interface PreferenceDriftMarker {
119
+ schemaVersion: 1;
120
+ lastRunAt: string;
121
+ lastApplyAt: string | null;
122
+ appliedCount: number;
123
+ }
124
+
125
+ export interface PreferenceDriftDependencies {
126
+ storage: StorageManager;
127
+ config: PluginConfig;
128
+ memoryDir: string;
129
+ /** Pre-built evidence lookup. Superseded by `embeddingLookupFactory`. */
130
+ embeddingLookup?: SemanticDedupLookup;
131
+ /** Builds a namespace-scoped evidence lookup from the scan's own storage. */
132
+ embeddingLookupFactory?: (storage: StorageManager) => SemanticDedupLookup | undefined;
133
+ storageForNamespace?: (
134
+ namespace: string | undefined,
135
+ ) => StorageManager | ScanStorageResolution | Promise<StorageManager | ScanStorageResolution>;
136
+ localLlm: LocalLlmClient | null;
137
+ fallbackLlm: FallbackLlmClient | null;
138
+ namespace?: string;
139
+ /** When true (and `driftDetection.enabled`), stamps and review items are written. */
140
+ apply?: boolean;
141
+ now?: Date;
142
+ }
143
+
144
+ function driftMarkerPath(memoryDir: string): string {
145
+ return path.join(memoryDir, "state", "preference-drift.json");
146
+ }
147
+
148
+ /** Read the last-run marker. Absent marker (or shadow-only history) → null apply timestamp. */
149
+ export async function readPreferenceDriftMarker(
150
+ memoryDir: string,
151
+ ): Promise<PreferenceDriftMarker | null> {
152
+ try {
153
+ const raw = await readJsonFile(driftMarkerPath(memoryDir));
154
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
155
+ const m = raw as Record<string, unknown>;
156
+ if (typeof m.lastRunAt !== "string") return null;
157
+ return {
158
+ schemaVersion: 1,
159
+ lastRunAt: m.lastRunAt,
160
+ lastApplyAt: typeof m.lastApplyAt === "string" ? m.lastApplyAt : null,
161
+ appliedCount: typeof m.appliedCount === "number" ? m.appliedCount : 0,
162
+ };
163
+ } catch {
164
+ return null;
165
+ }
166
+ }
167
+
168
+ function tsMs(value: unknown): number | null {
169
+ if (typeof value !== "string") return null;
170
+ const ms = Date.parse(value);
171
+ return Number.isFinite(ms) ? ms : null;
172
+ }
173
+
174
+ /**
175
+ * Statuses that make a memory a drift candidate. Stated as an inclusion set,
176
+ * never an exclusion list, so a newly added status can never leak through
177
+ * (§41): `superseded`, `archived`, `quarantined`, `rejected`, and
178
+ * `pending_review` preferences are all excluded by construction.
179
+ */
180
+ const CANDIDATE_STATUSES: Partial<Record<MemoryStatus, true>> = { active: true };
181
+
182
+ /**
183
+ * Deterministic candidate order (§12): oldest preference first, stable id
184
+ * tiebreak, so the per-run cap always selects the same slice on identical
185
+ * fixtures.
186
+ */
187
+ function compareCandidates(a: MemoryFile, b: MemoryFile): number {
188
+ const aMs = tsMs(a.frontmatter.created) ?? tsMs(a.frontmatter.updated) ?? 0;
189
+ const bMs = tsMs(b.frontmatter.created) ?? tsMs(b.frontmatter.updated) ?? 0;
190
+ if (aMs !== bMs) return aMs < bMs ? -1 : 1;
191
+ const aId = a.frontmatter.id ?? "";
192
+ const bId = b.frontmatter.id ?? "";
193
+ if (aId !== bId) return aId < bId ? -1 : 1;
194
+ return 0;
195
+ }
196
+
197
+ type EvidenceResult =
198
+ | { ok: true; hits: PreferenceDriftEvidence[]; hitMemories: MemoryFile[] }
199
+ | { ok: false; reason: "backend_unavailable" };
200
+
201
+ /**
202
+ * Gather recent same-namespace evidence for one preference.
203
+ *
204
+ * §22: an empty result and a failed backend are DIFFERENT outcomes. The
205
+ * `SemanticDedupLookup` contract is that implementations throw when the
206
+ * embedding backend is unavailable and return `[]` only when a reachable
207
+ * backend reports no hits, so the throw is the failure signal — it must never
208
+ * be swallowed into "no evidence", which would mark a live preference stale.
209
+ */
210
+ async function gatherEvidence(
211
+ preference: MemoryFile,
212
+ lookup: SemanticDedupLookup,
213
+ byId: ReadonlyMap<string, MemoryFile>,
214
+ windowStartMs: number,
215
+ nowMs: number,
216
+ ): Promise<EvidenceResult> {
217
+ let raw: SemanticDedupHit[];
218
+ try {
219
+ raw = await lookup(preference.content, EVIDENCE_LOOKUP_LIMIT);
220
+ } catch (err) {
221
+ log.debug("[preference-drift] evidence lookup failed", {
222
+ memoryId: preference.frontmatter.id,
223
+ error: err instanceof Error ? err.message : String(err),
224
+ });
225
+ return { ok: false, reason: "backend_unavailable" };
226
+ }
227
+
228
+ const hits: PreferenceDriftEvidence[] = [];
229
+ const hitMemories: MemoryFile[] = [];
230
+ for (const hit of raw) {
231
+ if (hit.id === preference.frontmatter.id) continue;
232
+ const memory = byId.get(hit.id);
233
+ if (!memory) continue;
234
+ const observedMs = tsMs(memory.frontmatter.updated) ?? tsMs(memory.frontmatter.created);
235
+ if (observedMs === null) continue;
236
+ // Half-open window [windowStart, now): a memory observed exactly at the
237
+ // window start counts, one observed exactly at the run instant does not
238
+ // (§23).
239
+ if (observedMs < windowStartMs || observedMs >= nowMs) continue;
240
+ hits.push({
241
+ memoryId: hit.id,
242
+ score: hit.score,
243
+ observedAt: memory.frontmatter.updated ?? memory.frontmatter.created ?? "",
244
+ });
245
+ hitMemories.push(memory);
246
+ }
247
+ return { ok: true, hits, hitMemories };
248
+ }
249
+
250
+ /**
251
+ * Map judge verdicts over a preference's evidence to a drift classification.
252
+ *
253
+ * - `contradicts` on any hit wins: the preference has drifted.
254
+ * - `duplicates` means the evidence restates the preference → corroborated.
255
+ * - `independent` is evidence about something else, so it corroborates
256
+ * nothing; with no better verdict the preference is stale.
257
+ * - `needs-user` (which is also what the judge returns when no LLM is
258
+ * reachable) leaves the question genuinely unanswered. Stamping
259
+ * `lastCorroborated` would assert something unverified and stamping `stale`
260
+ * would contradict the evidence we did find, so the honest outcome is
261
+ * `skipped: verification_unavailable`.
262
+ */
263
+ function classifyFromVerdicts(
264
+ verdicts: readonly ContradictionJudgeResult[],
265
+ ): { classification: PreferenceDriftClassification; skipped?: PreferenceDriftSkipReason; reason: string; driftedAgainst?: string } {
266
+ const contradiction = verdicts.find((v) => v.verdict === "contradicts");
267
+ if (contradiction) {
268
+ return {
269
+ classification: "drifted",
270
+ reason: `recent evidence points away from this preference: ${contradiction.rationale}`,
271
+ driftedAgainst: contradiction.memoryIdB,
272
+ };
273
+ }
274
+ const restatement = verdicts.find((v) => v.verdict === "duplicates");
275
+ if (restatement) {
276
+ return {
277
+ classification: "corroborated",
278
+ reason: `recent evidence restates this preference: ${restatement.rationale}`,
279
+ };
280
+ }
281
+ if (verdicts.some((v) => v.verdict === "needs-user")) {
282
+ return {
283
+ classification: "skipped",
284
+ skipped: "verification_unavailable",
285
+ reason: "recent evidence found but no judge verdict was available to classify it",
286
+ };
287
+ }
288
+ return {
289
+ classification: "stale",
290
+ reason: "recent evidence is independent of this preference — no corroboration either way",
291
+ };
292
+ }
293
+
294
+ /**
295
+ * Run one preference-drift pass over a namespace.
296
+ *
297
+ * Shadow mode (the default) performs NO writes — not even the run marker.
298
+ * Applying requires `apply: true` AND `driftDetection.enabled: true`.
299
+ */
300
+ export async function runPreferenceDriftScan(
301
+ deps: PreferenceDriftDependencies,
302
+ ): Promise<PreferenceDriftReport> {
303
+ const startTime = Date.now();
304
+ const now = deps.now ?? new Date();
305
+ const nowIso = now.toISOString();
306
+ const nowMs = now.getTime();
307
+ const mode: "shadow" | "apply" = deps.apply === true ? "apply" : "shadow";
308
+ const cfg = deps.config.driftDetection;
309
+
310
+ const emptyCounts: Record<PreferenceDriftClassification, number> = {
311
+ corroborated: 0,
312
+ stale: 0,
313
+ drifted: 0,
314
+ skipped: 0,
315
+ };
316
+
317
+ if (cfg?.enabled !== true) {
318
+ return {
319
+ schemaVersion: 1,
320
+ generatedAt: nowIso,
321
+ mode,
322
+ scanned: 0,
323
+ eligible: 0,
324
+ findings: [],
325
+ counts: emptyCounts,
326
+ appliedCount: 0,
327
+ reviewItemsOpened: 0,
328
+ elapsedMs: Date.now() - startTime,
329
+ skippedReason: "drift_disabled",
330
+ };
331
+ }
332
+ // `0` is the documented disable value and must behave as a true zero (§33).
333
+ if (cfg.maxCandidatesPerRun === 0) {
334
+ return {
335
+ schemaVersion: 1,
336
+ generatedAt: nowIso,
337
+ mode,
338
+ scanned: 0,
339
+ eligible: 0,
340
+ findings: [],
341
+ counts: emptyCounts,
342
+ appliedCount: 0,
343
+ reviewItemsOpened: 0,
344
+ elapsedMs: Date.now() - startTime,
345
+ skippedReason: "scan_disabled",
346
+ };
347
+ }
348
+
349
+ const { storage, namespace } = await resolveScanStorage(deps, deps.namespace?.trim() || undefined);
350
+ const lookup = deps.embeddingLookupFactory
351
+ ? deps.embeddingLookupFactory(storage)
352
+ : deps.embeddingLookup;
353
+
354
+ let all: MemoryFile[];
355
+ try {
356
+ all = await storage.readAllMemories();
357
+ } catch (err) {
358
+ log.warn(
359
+ `[preference-drift] failed to read memories: ${err instanceof Error ? err.message : String(err)}`,
360
+ );
361
+ all = [];
362
+ }
363
+
364
+ const byId = new Map<string, MemoryFile>();
365
+ for (const m of all) {
366
+ const id = m.frontmatter.id;
367
+ if (typeof id === "string" && id.length > 0) byId.set(id, m);
368
+ }
369
+
370
+ const minAgeMs = cfg.minAgeDays * DAY_MS;
371
+ const eligible = all.filter((m) => {
372
+ const fm = m.frontmatter;
373
+ if (fm.category !== "preference") return false;
374
+ if (CANDIDATE_STATUSES[fm.status ?? "active"] !== true) return false;
375
+ if (typeof fm.id !== "string" || fm.id.length === 0) return false;
376
+ if (!m.content || m.content.trim().length === 0) return false;
377
+ const bornMs = tsMs(fm.created) ?? tsMs(fm.updated);
378
+ if (bornMs === null) return false;
379
+ return nowMs - bornMs >= minAgeMs;
380
+ });
381
+
382
+ const candidates = [...eligible].sort(compareCandidates).slice(0, cfg.maxCandidatesPerRun);
383
+
384
+ const report: PreferenceDriftReport = {
385
+ schemaVersion: 1,
386
+ generatedAt: nowIso,
387
+ mode,
388
+ namespace,
389
+ scanned: candidates.length,
390
+ eligible: eligible.length,
391
+ findings: [],
392
+ counts: { ...emptyCounts },
393
+ appliedCount: 0,
394
+ reviewItemsOpened: 0,
395
+ elapsedMs: 0,
396
+ };
397
+
398
+ const windowStartMs = nowMs - cfg.lookbackDays * DAY_MS;
399
+ // One judge cache per run so the module-level singleton never leaks across
400
+ // namespaces or scans.
401
+ const judgeCache = new Map<string, ContradictionJudgeResult>();
402
+ const driftedPairs: Array<{ preference: MemoryFile; evidenceId: string; finding: PreferenceDriftFinding; rationale: string; confidence: number }> = [];
403
+ const stamps: Array<{ memory: MemoryFile; patch: { driftState?: "stale"; lastCorroborated?: string }; reasonCode: string }> = [];
404
+
405
+ for (const preference of candidates) {
406
+ const bornMs = tsMs(preference.frontmatter.created) ?? tsMs(preference.frontmatter.updated) ?? nowMs;
407
+ const finding: PreferenceDriftFinding = {
408
+ memoryId: preference.frontmatter.id,
409
+ classification: "stale",
410
+ ageDays: Math.floor((nowMs - bornMs) / DAY_MS),
411
+ lastCorroborated: preference.frontmatter.lastCorroborated ?? null,
412
+ reason: "",
413
+ evidence: [],
414
+ };
415
+
416
+ if (!lookup) {
417
+ finding.classification = "skipped";
418
+ finding.skipped = "backend_unavailable";
419
+ finding.reason = "no evidence lookup is configured, so corroboration cannot be checked";
420
+ report.findings.push(finding);
421
+ report.counts.skipped += 1;
422
+ continue;
423
+ }
424
+
425
+ const evidence = await gatherEvidence(preference, lookup, byId, windowStartMs, nowMs);
426
+ if (!evidence.ok) {
427
+ finding.classification = "skipped";
428
+ finding.skipped = evidence.reason;
429
+ finding.reason = "evidence lookup backend was unavailable — not classified as stale";
430
+ report.findings.push(finding);
431
+ report.counts.skipped += 1;
432
+ continue;
433
+ }
434
+ finding.evidence = evidence.hits;
435
+
436
+ if (evidence.hits.length === 0) {
437
+ finding.classification = "stale";
438
+ finding.reason = `no corroborating evidence in the last ${cfg.lookbackDays} days`;
439
+ report.findings.push(finding);
440
+ report.counts.stale += 1;
441
+ stamps.push({ memory: preference, patch: { driftState: "stale" }, reasonCode: "drift_stale" });
442
+ continue;
443
+ }
444
+
445
+ // The judge call happens only when candidate evidence exists.
446
+ const judgeInputs: ContradictionJudgeInput[] = evidence.hitMemories.map((hit) => ({
447
+ memoryIdA: preference.frontmatter.id,
448
+ memoryIdB: hit.frontmatter.id,
449
+ textA: preference.content,
450
+ textB: hit.content,
451
+ categoryA: preference.frontmatter.category,
452
+ categoryB: hit.frontmatter.category,
453
+ }));
454
+ const judged = await judgeContradictionPairs(
455
+ judgeInputs,
456
+ deps.config,
457
+ deps.localLlm,
458
+ deps.fallbackLlm,
459
+ judgeCache,
460
+ );
461
+ // Preserve evidence order (score desc) so the verdict scan is deterministic
462
+ // rather than dependent on Map iteration of the judge's keyed results.
463
+ const orderedVerdicts: ContradictionJudgeResult[] = [];
464
+ for (const input of judgeInputs) {
465
+ for (const result of judged.results.values()) {
466
+ if (result.memoryIdA === input.memoryIdA && result.memoryIdB === input.memoryIdB) {
467
+ orderedVerdicts.push(result);
468
+ break;
469
+ }
470
+ }
471
+ }
472
+
473
+ const outcome = classifyFromVerdicts(orderedVerdicts);
474
+ finding.classification = outcome.classification;
475
+ finding.reason = outcome.reason;
476
+ if (outcome.skipped) finding.skipped = outcome.skipped;
477
+ report.findings.push(finding);
478
+ report.counts[outcome.classification] += 1;
479
+
480
+ if (outcome.classification === "corroborated") {
481
+ stamps.push({
482
+ memory: preference,
483
+ patch: { lastCorroborated: nowIso, driftState: undefined },
484
+ reasonCode: "drift_corroborated",
485
+ });
486
+ } else if (outcome.classification === "stale") {
487
+ stamps.push({ memory: preference, patch: { driftState: "stale" }, reasonCode: "drift_stale" });
488
+ } else if (outcome.classification === "drifted" && outcome.driftedAgainst) {
489
+ const verdict = orderedVerdicts.find((v) => v.verdict === "contradicts");
490
+ driftedPairs.push({
491
+ preference,
492
+ evidenceId: outcome.driftedAgainst,
493
+ finding,
494
+ rationale: verdict?.rationale ?? outcome.reason,
495
+ confidence: verdict?.confidence ?? 0,
496
+ });
497
+ }
498
+ }
499
+
500
+ if (mode === "shadow") {
501
+ report.elapsedMs = Date.now() - startTime;
502
+ return report;
503
+ }
504
+
505
+ let writes = 0;
506
+ for (const stamp of stamps) {
507
+ try {
508
+ // `driftState: undefined` is an explicit clear: writeMemoryFrontmatter
509
+ // merges the patch over existing frontmatter, and the serializer omits
510
+ // undefined fields, so a corroborated preference loses its stale mark.
511
+ await storage.writeMemoryFrontmatter(stamp.memory, { ...stamp.patch, updated: nowIso }, {
512
+ at: now,
513
+ actor: DRIFT_ACTOR,
514
+ reasonCode: stamp.reasonCode,
515
+ ruleVersion: DRIFT_RULE_VERSION,
516
+ });
517
+ writes += 1;
518
+ report.appliedCount += 1;
519
+ } catch (err) {
520
+ log.warn(
521
+ `[preference-drift] stamp ${stamp.reasonCode} on ${stamp.memory.frontmatter.id} failed: ${err instanceof Error ? err.message : String(err)}`,
522
+ );
523
+ }
524
+ }
525
+
526
+ if (driftedPairs.length > 0) {
527
+ const queueEntries: Array<Omit<ContradictionPair, "pairId"> & { memoryIds: [string, string] }> =
528
+ driftedPairs.map((d) => ({
529
+ memoryIds: [d.preference.frontmatter.id, d.evidenceId],
530
+ kind: "preference-drift",
531
+ // A drift finding IS a detected conflict, so the honest verdict is
532
+ // `contradicts`; `kind` is what distinguishes it from a pair the
533
+ // contradiction scan produced. Existing review surfaces render it
534
+ // unchanged.
535
+ verdict: "contradicts",
536
+ rationale: d.rationale,
537
+ confidence: d.confidence,
538
+ detectedAt: nowIso,
539
+ memoryContentHashes: {
540
+ [d.preference.frontmatter.id]: computeMemoryContentHash(
541
+ d.preference.content,
542
+ d.preference.frontmatter.category,
543
+ ),
544
+ },
545
+ namespace,
546
+ }));
547
+ const written = writePairs(deps.memoryDir, queueEntries);
548
+ report.reviewItemsOpened = written.length;
549
+ report.appliedCount += written.length;
550
+ for (const entry of written) {
551
+ const match = driftedPairs.find((d) => d.preference.frontmatter.id === entry.memoryIds[0]);
552
+ if (match) match.finding.reviewPairId = entry.pairId;
553
+ }
554
+ }
555
+
556
+ if (writes > 0) {
557
+ // Frontmatter stamps bypass the extraction write pipeline, so bump the
558
+ // shared corpus version out of band — otherwise hot recall caches keep
559
+ // serving pre-stamp frontmatter and the damping stage never sees the new
560
+ // `driftState` (§25/§31).
561
+ bumpMemoryCorpusVersionForDir(storage.dir);
562
+ if (storage.dir !== deps.memoryDir) bumpMemoryCorpusVersionForDir(deps.memoryDir);
563
+ }
564
+ try {
565
+ await writeJsonFileAtomic(driftMarkerPath(deps.memoryDir), {
566
+ schemaVersion: 1,
567
+ lastRunAt: nowIso,
568
+ lastApplyAt: nowIso,
569
+ appliedCount: report.appliedCount,
570
+ } satisfies PreferenceDriftMarker);
571
+ } catch (err) {
572
+ log.warn(
573
+ `[preference-drift] failed to persist run marker: ${err instanceof Error ? err.message : String(err)}`,
574
+ );
575
+ }
576
+
577
+ report.elapsedMs = Date.now() - startTime;
578
+ return report;
579
+ }
580
+
581
+ // ── Review resolution ────────────────────────────────────────────────────────
582
+
583
+ export interface PreferenceDriftResolutionOptions {
584
+ /** Replacement preference text. Required for `supersede`. */
585
+ mergedContent?: string;
586
+ /** Fires after a resolution leaves a durable memory mutation in the namespace. */
587
+ onMemoryWritten?: (namespace: string | undefined, storageDir: string) => void;
588
+ now?: Date;
589
+ }
590
+
591
+ export interface PreferenceDriftResolutionResult {
592
+ affectedIds: string[];
593
+ message: string;
594
+ }
595
+
596
+ /**
597
+ * Execute a `kind: "preference-drift"` resolution verb.
598
+ *
599
+ * The first memory id on the item is the aging preference; the second is the
600
+ * evidence that pointed away from it. Only the preference is ever mutated —
601
+ * the evidence memory is a witness, not a target.
602
+ *
603
+ * - `keep` — the preference still holds: stamp `lastCorroborated`, clear
604
+ * `driftState`.
605
+ * - `supersede` — write the corrected preference and retire the old one
606
+ * through the normal supersession path.
607
+ * - `archive` — demote the preference to `archived`. Demotion, never
608
+ * deletion; page versioning snapshots the overwrite.
609
+ *
610
+ * Callers resolve the pair record itself (`resolvePair`); this function owns
611
+ * only the memory-side effects.
612
+ */
613
+ export async function resolvePreferenceDrift(
614
+ storage: StorageManager,
615
+ pair: Pick<ContradictionPair, "pairId" | "memoryIds" | "namespace">,
616
+ verb: "keep" | "supersede" | "archive",
617
+ options: PreferenceDriftResolutionOptions = {},
618
+ ): Promise<PreferenceDriftResolutionResult> {
619
+ const now = options.now ?? new Date();
620
+ const nowIso = now.toISOString();
621
+ const preferenceId = pair.memoryIds[0];
622
+ const preference = await storage.getMemoryById(preferenceId);
623
+ if (!preference) {
624
+ return { affectedIds: [], message: `Preference ${preferenceId} not found; not resolving ${pair.pairId}` };
625
+ }
626
+
627
+ const touched = (): void => {
628
+ try {
629
+ options.onMemoryWritten?.(pair.namespace, storage.dir);
630
+ } catch (err) {
631
+ log.warn(
632
+ `[preference-drift] catalog write touch failed for ${pair.pairId}: ${err instanceof Error ? err.message : String(err)}`,
633
+ );
634
+ }
635
+ };
636
+
637
+ if (verb === "keep") {
638
+ await storage.writeMemoryFrontmatter(
639
+ preference,
640
+ { lastCorroborated: nowIso, driftState: undefined, updated: nowIso },
641
+ { at: now, actor: DRIFT_ACTOR, reasonCode: "drift_kept", ruleVersion: DRIFT_RULE_VERSION },
642
+ );
643
+ bumpMemoryCorpusVersionForDir(storage.dir);
644
+ touched();
645
+ return { affectedIds: [preferenceId], message: `Preference ${preferenceId} confirmed still current` };
646
+ }
647
+
648
+ if (verb === "archive") {
649
+ const archivedPath = await storage.archiveMemory(preference, {
650
+ at: now,
651
+ actor: DRIFT_ACTOR,
652
+ reasonCode: "drift_archived",
653
+ ruleVersion: DRIFT_RULE_VERSION,
654
+ relatedMemoryIds: [pair.memoryIds[1]],
655
+ });
656
+ if (archivedPath === null) {
657
+ return { affectedIds: [], message: `Preference ${preferenceId} could not be archived; not resolving` };
658
+ }
659
+ bumpMemoryCorpusVersionForDir(storage.dir);
660
+ touched();
661
+ return { affectedIds: [preferenceId], message: `Preference ${preferenceId} archived` };
662
+ }
663
+
664
+ const replacementText = options.mergedContent?.trim();
665
+ if (!replacementText) {
666
+ return {
667
+ affectedIds: [],
668
+ message: "supersede requires mergedContent carrying the updated preference; no memories changed",
669
+ };
670
+ }
671
+
672
+ // Sealed-envelope write: the replacement text is user- or LLM-supplied, so
673
+ // compose with salvage and warn-log any dropped optional field rather than
674
+ // failing the whole resolution on a cosmetic value.
675
+ const envelope = composeMemoryEnvelope(
676
+ {
677
+ content: replacementText,
678
+ category: "preference",
679
+ confidence: preference.frontmatter.confidence ?? 0.8,
680
+ tags: ["preference-drift", "supersede"],
681
+ entityRef: preference.frontmatter.entityRef,
682
+ },
683
+ { source: DRIFT_ACTOR },
684
+ { salvage: true },
685
+ );
686
+ if (envelope.salvageNotes.length > 0) {
687
+ log.warn(`[preference-drift] supersede write salvaged invalid fields: ${envelope.salvageNotes.join("; ")}`);
688
+ }
689
+ const written = await storage.writeSealedMemory(envelope, {
690
+ actor: DRIFT_ACTOR,
691
+ lineage: [preferenceId],
692
+ // `update` is the operator for "newer version of the same claim" — the
693
+ // drift replacement restates one preference, it does not merge two.
694
+ derivedVia: "update",
695
+ });
696
+ // A tombstone-blocked replacement is NOT active, so superseding the old
697
+ // preference to it would retire the only live copy (§14: never destroy old
698
+ // state before the new state is confirmed viable).
699
+ if (written.tombstoneBlocked) {
700
+ return {
701
+ affectedIds: [],
702
+ message: `Replacement preference for ${pair.pairId} was tombstone-blocked (pending_review); not resolving — original kept active`,
703
+ };
704
+ }
705
+ const superseded = await storage.supersedeMemory(
706
+ preferenceId,
707
+ written.id,
708
+ `preference drift resolved by supersede (${pair.pairId})`,
709
+ undefined,
710
+ { actor: DRIFT_ACTOR },
711
+ );
712
+ bumpMemoryCorpusVersionForDir(storage.dir);
713
+ touched();
714
+ if (!superseded) {
715
+ return {
716
+ affectedIds: [written.id],
717
+ message: `Replacement ${written.id} written but superseding ${preferenceId} failed; both remain — re-run resolution`,
718
+ };
719
+ }
720
+ return {
721
+ affectedIds: [preferenceId, written.id],
722
+ message: `Preference ${preferenceId} superseded by ${written.id}`,
723
+ };
724
+ }