@remnic/core 9.65.2 → 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 (449) hide show
  1. package/dist/access-admin-ops-surface.d.ts +44 -44
  2. package/dist/access-admin-ops-surface.js +14 -14
  3. package/dist/access-authorization-probe.d.ts +38 -38
  4. package/dist/access-authorization-probe.js +15 -15
  5. package/dist/access-boundary.d.ts +40 -40
  6. package/dist/access-boundary.js +14 -14
  7. package/dist/access-cli.js +52 -52
  8. package/dist/access-coding-context-resolution.d.ts +3 -1
  9. package/dist/access-extraction-force-flush.d.ts +25 -25
  10. package/dist/access-extraction-force-flush.js +14 -14
  11. package/dist/access-health-types.d.ts +3 -1
  12. package/dist/access-http-lcm-compaction.d.ts +39 -39
  13. package/dist/access-http-lifecycle-flush.d.ts +39 -39
  14. package/dist/access-http-offline-stream.d.ts +25 -25
  15. package/dist/access-http-query.d.ts +4 -2
  16. package/dist/access-http-query.js +16 -16
  17. package/dist/access-http.d.ts +51 -44
  18. package/dist/access-http.js +30 -30
  19. package/dist/access-identity-continuity-surface.d.ts +12 -12
  20. package/dist/access-identity-continuity-surface.js +14 -14
  21. package/dist/access-lcm-surface.d.ts +27 -27
  22. package/dist/access-lcm-surface.js +14 -14
  23. package/dist/access-mcp-admin-tools.d.ts +9 -1
  24. package/dist/access-mcp-admin-tools.js +3 -1
  25. package/dist/access-mcp-output-schemas.js +1 -1
  26. package/dist/access-mcp.d.ts +39 -39
  27. package/dist/access-mcp.js +24 -24
  28. package/dist/access-memory-search-fanout.d.ts +4 -2
  29. package/dist/{access-namespace-preflight-Dhvcl-T0.d.ts → access-namespace-preflight-B7Usyz-K.d.ts} +1 -1
  30. package/dist/access-namespace-preflight.d.ts +5 -3
  31. package/dist/access-namespace-preflight.js +14 -14
  32. package/dist/access-observe-write-surface.d.ts +39 -39
  33. package/dist/access-observe-write-surface.js +14 -14
  34. package/dist/access-offline-manifest.d.ts +12 -12
  35. package/dist/access-operations-batch.js +18 -18
  36. package/dist/access-operations.d.ts +40 -40
  37. package/dist/access-operations.js +21 -21
  38. package/dist/access-recall-concurrency.d.ts +40 -40
  39. package/dist/access-recall-concurrency.js +14 -14
  40. package/dist/access-recall-response.d.ts +24 -24
  41. package/dist/access-recall-response.js +14 -14
  42. package/dist/access-recall-surface.d.ts +24 -24
  43. package/dist/access-recall-surface.js +14 -14
  44. package/dist/access-schema.d.ts +45 -45
  45. package/dist/{access-service-Bke1alZO.d.ts → access-service-DkaIG3n8.d.ts} +219 -75
  46. package/dist/access-service-helpers.d.ts +40 -40
  47. package/dist/access-service.d.ts +47 -47
  48. package/dist/access-service.js +14 -14
  49. package/dist/access-surface-catalog.d.ts +40 -40
  50. package/dist/access-surface-catalog.js +20 -16
  51. package/dist/access-surface-catalog.js.map +1 -1
  52. package/dist/access-wearables-meetings-surface.d.ts +5 -3
  53. package/dist/action-confidence.d.ts +3 -1
  54. package/dist/active-memory-bridge.d.ts +3 -1
  55. package/dist/active-recall.d.ts +3 -1
  56. package/dist/active-recall.js +6 -1
  57. package/dist/active-recall.js.map +1 -1
  58. package/dist/ambient-provenance.d.ts +3 -1
  59. package/dist/artifact-search.d.ts +3 -1
  60. package/dist/behavior-learner.d.ts +3 -1
  61. package/dist/behavior-signals.d.ts +3 -1
  62. package/dist/bootstrap.d.ts +12 -12
  63. package/dist/briefing-window.d.ts +4 -2
  64. package/dist/briefing.d.ts +4 -2
  65. package/dist/briefing.js +3 -3
  66. package/dist/buffer-surprise-report.d.ts +3 -1
  67. package/dist/buffer-turn-helpers.d.ts +3 -1
  68. package/dist/buffer.d.ts +4 -2
  69. package/dist/bulk-import/index.d.ts +5 -3
  70. package/dist/calibration.d.ts +3 -1
  71. package/dist/capabilities.d.ts +3 -1
  72. package/dist/{catalog-DV6qnbr7.d.ts → catalog-CX2BZEJh.d.ts} +1 -1
  73. package/dist/causal-behavior.d.ts +1 -1
  74. package/dist/causal-consolidation.d.ts +3 -1
  75. package/dist/causal-consolidation.js +7 -7
  76. package/dist/causal-trajectory-graph.d.ts +1 -1
  77. package/dist/{chunk-R7JAM2XF.js → chunk-2VXWFYJE.js} +3 -3
  78. package/dist/{chunk-UXO5IMBK.js → chunk-4XDGYCZI.js} +2 -2
  79. package/dist/{chunk-OTHZWLUD.js → chunk-57HVIG5U.js} +2 -2
  80. package/dist/{chunk-EILZG5VC.js → chunk-6TAX66IA.js} +2 -2
  81. package/dist/{chunk-NKKD4ZAU.js → chunk-6VALIZKG.js} +2 -2
  82. package/dist/{chunk-BWLRUMOG.js → chunk-76PL4XLG.js} +435 -1
  83. package/dist/chunk-76PL4XLG.js.map +1 -0
  84. package/dist/{chunk-2E36UXVW.js → chunk-7KFIMGMP.js} +2 -2
  85. package/dist/{chunk-LGO6A7GJ.js → chunk-7QOO4LGC.js} +28 -28
  86. package/dist/{chunk-TBOJ76ER.js → chunk-AB3A7EFH.js} +18 -2
  87. package/dist/{chunk-TBOJ76ER.js.map → chunk-AB3A7EFH.js.map} +1 -1
  88. package/dist/{chunk-DZUJ46DO.js → chunk-AUO2LJ3K.js} +2 -2
  89. package/dist/{chunk-ZO6AXLEH.js → chunk-AYDDB3XI.js} +2 -2
  90. package/dist/{chunk-NTWN6HB2.js → chunk-AYYGRVRP.js} +2 -2
  91. package/dist/{chunk-7YQ4IYS2.js → chunk-AZAA5DNP.js} +2 -2
  92. package/dist/{chunk-4KYOLI6M.js → chunk-BKYPJUQL.js} +120 -14
  93. package/dist/chunk-BKYPJUQL.js.map +1 -0
  94. package/dist/{chunk-Z7PBQPJ4.js → chunk-BQUEAVNW.js} +2 -2
  95. package/dist/{chunk-35NN3ZFA.js → chunk-CTVWELNK.js} +21 -3
  96. package/dist/chunk-CTVWELNK.js.map +1 -0
  97. package/dist/{chunk-5YQ63T4B.js → chunk-D27J626P.js} +2 -2
  98. package/dist/{chunk-LZH5FR2W.js → chunk-DHTQP334.js} +2 -2
  99. package/dist/{chunk-Y7DK7HHJ.js → chunk-E4MVQQVX.js} +2 -2
  100. package/dist/{chunk-JYYBA7TF.js → chunk-EIVIB73I.js} +2 -2
  101. package/dist/{chunk-S2DPMJXH.js → chunk-EV3YFLCB.js} +2 -2
  102. package/dist/{chunk-TZNQUNKL.js → chunk-FC7TIRFS.js} +15 -1
  103. package/dist/chunk-FC7TIRFS.js.map +1 -0
  104. package/dist/{chunk-3I7ASZN3.js → chunk-G57LQRP6.js} +2 -2
  105. package/dist/{chunk-UIN2BWDD.js → chunk-GN55KU2P.js} +2 -2
  106. package/dist/{chunk-PE255F4T.js → chunk-GUXS322H.js} +2 -2
  107. package/dist/{chunk-V3MXUL3D.js → chunk-H5FW4S5Q.js} +319 -643
  108. package/dist/chunk-H5FW4S5Q.js.map +1 -0
  109. package/dist/chunk-I32NKXE2.js +879 -0
  110. package/dist/chunk-I32NKXE2.js.map +1 -0
  111. package/dist/{chunk-YB42WZCY.js → chunk-IQSC3V2E.js} +2 -2
  112. package/dist/{chunk-MN4KB2A4.js → chunk-MBDJJTAT.js} +3 -3
  113. package/dist/{chunk-SQI57UCJ.js → chunk-MRTUN665.js} +2 -2
  114. package/dist/{chunk-SBDCZ2AX.js → chunk-NXKJHSDN.js} +1 -1
  115. package/dist/chunk-NXKJHSDN.js.map +1 -0
  116. package/dist/{chunk-SPUJNJJC.js → chunk-NZWEEALH.js} +2 -2
  117. package/dist/{chunk-2XDWWPTN.js → chunk-OJD4WS2E.js} +2 -2
  118. package/dist/{chunk-ZWXJBFZU.js → chunk-OLWTCWQ2.js} +8 -8
  119. package/dist/{chunk-XRYZRW54.js → chunk-P4DHENRE.js} +2 -2
  120. package/dist/{chunk-J275IYMG.js → chunk-PAYGC3BD.js} +29 -2
  121. package/dist/chunk-PAYGC3BD.js.map +1 -0
  122. package/dist/{chunk-IID2CMUU.js → chunk-PDVQ5JU3.js} +2 -2
  123. package/dist/{chunk-7BY7V4YX.js → chunk-Q4WONDMJ.js} +4 -3
  124. package/dist/{chunk-7BY7V4YX.js.map → chunk-Q4WONDMJ.js.map} +1 -1
  125. package/dist/{chunk-5NM4OH4P.js → chunk-QFMO32TO.js} +3 -3
  126. package/dist/{chunk-SIHSCCUQ.js → chunk-QXIQPCCN.js} +1 -1
  127. package/dist/{chunk-2YI72KM3.js → chunk-R47WINCY.js} +2 -2
  128. package/dist/{chunk-ZQHI2BZB.js → chunk-RK4WXUI2.js} +2 -2
  129. package/dist/{chunk-QOS5LBZN.js → chunk-S7Q5CM7E.js} +2 -2
  130. package/dist/{chunk-GDFVPUJ5.js → chunk-TDKSCV62.js} +2 -2
  131. package/dist/{chunk-OCZIYAE5.js → chunk-TYUS5TLG.js} +3 -3
  132. package/dist/{chunk-GNCCUR6K.js → chunk-URHCUXKI.js} +2 -2
  133. package/dist/{chunk-TF77C7SV.js → chunk-VA5KMSAE.js} +7 -5
  134. package/dist/chunk-VA5KMSAE.js.map +1 -0
  135. package/dist/{chunk-SB7D4BT7.js → chunk-WN5FSGLE.js} +1545 -105
  136. package/dist/chunk-WN5FSGLE.js.map +1 -0
  137. package/dist/{chunk-YLDXWCZP.js → chunk-YF6UHWOV.js} +14 -9
  138. package/dist/chunk-YF6UHWOV.js.map +1 -0
  139. package/dist/{chunk-CT56C22C.js → chunk-Z4RPXHJH.js} +4 -4
  140. package/dist/{cli-cShQ13XG.d.ts → cli-ChKeYMZv.d.ts} +5 -5
  141. package/dist/cli.d.ts +25 -25
  142. package/dist/cli.js +44 -43
  143. package/dist/coding/pre-action-gate.d.ts +2 -60
  144. package/dist/compounding/engine.d.ts +4 -2
  145. package/dist/compounding/engine.js +3 -3
  146. package/dist/compounding/preference-consolidator.d.ts +3 -1
  147. package/dist/compression-optimizer.d.ts +3 -1
  148. package/dist/config.d.ts +3 -1
  149. package/dist/config.js +6 -1
  150. package/dist/connectors/codex-materialize-runner.d.ts +3 -1
  151. package/dist/connectors/codex-materialize-runner.js +3 -3
  152. package/dist/connectors/codex-materialize.d.ts +3 -1
  153. package/dist/connectors/index.d.ts +3 -1
  154. package/dist/connectors/index.js +3 -3
  155. package/dist/consolidation-provenance-check.d.ts +4 -2
  156. package/dist/consolidation-undo.d.ts +4 -2
  157. package/dist/contradiction/index.d.ts +10 -270
  158. package/dist/contradiction/index.js +11 -6
  159. package/dist/{contradiction-review-QKJH5DKD.js → contradiction-review-WRVLRWHS.js} +6 -2
  160. package/dist/{contradiction-scan-VOTQRRSX.js → contradiction-scan-AKUWPX5N.js} +5 -3
  161. package/dist/converge-config.d.ts +3 -1
  162. package/dist/convergence-refresh.d.ts +4 -2
  163. package/dist/convergence-refresh.js +5 -5
  164. package/dist/conversation-index/backend.d.ts +3 -1
  165. package/dist/conversation-index/chunker.d.ts +3 -1
  166. package/dist/conversation-index/faiss-adapter.d.ts +3 -1
  167. package/dist/conversation-index/indexer.d.ts +3 -1
  168. package/dist/conversation-index/search.d.ts +3 -1
  169. package/dist/corpus-watermark.d.ts +3 -1
  170. package/dist/corpus-watermark.js +5 -5
  171. package/dist/cross-lingual-recall.d.ts +3 -1
  172. package/dist/day-summary.d.ts +3 -1
  173. package/dist/delinearize.d.ts +3 -1
  174. package/dist/dependency-propagation-config.d.ts +3 -1
  175. package/dist/{dependency-propagation-delivery-CsGg-VwS.d.ts → dependency-propagation-delivery-B8ZMBBCY.d.ts} +1 -1
  176. package/dist/direct-answer-wiring.d.ts +3 -1
  177. package/dist/direct-answer.d.ts +3 -1
  178. package/dist/embedding-fallback.d.ts +3 -1
  179. package/dist/enrichment/index.d.ts +3 -1
  180. package/dist/entity-origin-fields.d.ts +3 -1
  181. package/dist/entity-retrieval.d.ts +4 -2
  182. package/dist/entity-retrieval.js +3 -3
  183. package/dist/entity-schema.d.ts +3 -1
  184. package/dist/episodic-context.d.ts +3 -1
  185. package/dist/explicit-capture.d.ts +12 -12
  186. package/dist/external-wiki-access.d.ts +40 -40
  187. package/dist/external-wiki-access.js +15 -15
  188. package/dist/external-wiki-collection-registration.d.ts +3 -1
  189. package/dist/external-wiki-collection.d.ts +3 -1
  190. package/dist/external-wiki-mcp-tools.d.ts +40 -40
  191. package/dist/extraction-error-classification.d.ts +3 -1
  192. package/dist/extraction-faithfulness.d.ts +3 -1
  193. package/dist/extraction-judge-telemetry.d.ts +3 -1
  194. package/dist/extraction-judge-training.d.ts +3 -1
  195. package/dist/extraction-judge.d.ts +3 -1
  196. package/dist/extraction-liveness.d.ts +3 -1
  197. package/dist/extraction-normalization.d.ts +3 -1
  198. package/dist/extraction-prompt.d.ts +3 -1
  199. package/dist/extraction-source-grounding-rules.d.ts +3 -1
  200. package/dist/extraction-source-grounding.d.ts +3 -1
  201. package/dist/extraction.d.ts +3 -1
  202. package/dist/extraction.js +3 -3
  203. package/dist/fallback-llm.d.ts +3 -1
  204. package/dist/graph-dashboard-diff.d.ts +3 -1
  205. package/dist/graph-dashboard-key.d.ts +3 -1
  206. package/dist/graph-dashboard-parser.d.ts +3 -1
  207. package/dist/graph-edge-reinforcement.d.ts +3 -1
  208. package/dist/graph-path-reconstruction.d.ts +3 -1
  209. package/dist/graph-path-scoring.d.ts +3 -1
  210. package/dist/graph-snapshot.d.ts +3 -1
  211. package/dist/graph.d.ts +3 -1
  212. package/dist/harmonic-retrieval.js +1 -1
  213. package/dist/importance.d.ts +3 -1
  214. package/dist/importers/index.d.ts +3 -1
  215. package/dist/in-flight-reads.d.ts +3 -1
  216. package/dist/index-CexDrJzC.d.ts +284 -0
  217. package/dist/index.d.ts +251 -29
  218. package/dist/index.js +129 -75
  219. package/dist/intent.d.ts +3 -1
  220. package/dist/lcm/engine.d.ts +3 -1
  221. package/dist/lcm/index.d.ts +3 -1
  222. package/dist/lcm/tools.d.ts +3 -1
  223. package/dist/lifecycle.d.ts +3 -1
  224. package/dist/live-connectors-runner.d.ts +3 -1
  225. package/dist/local-llm.d.ts +3 -1
  226. package/dist/local-model-endpoint.d.ts +3 -1
  227. package/dist/maintenance/memory-governance.d.ts +3 -1
  228. package/dist/maintenance/memory-governance.js +3 -3
  229. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +4 -2
  230. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  231. package/dist/maintenance/rebuild-memory-projection.d.ts +4 -2
  232. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  233. package/dist/{maintenance-jhMpeGne.d.ts → maintenance-nkdtSrVr.d.ts} +3 -3
  234. package/dist/mcp-memory-inspector-app.d.ts +37 -37
  235. package/dist/memory-action-policy.d.ts +3 -1
  236. package/dist/memory-cache.d.ts +3 -1
  237. package/dist/memory-lifecycle-ledger-utils.d.ts +3 -1
  238. package/dist/memory-projection-store.d.ts +3 -1
  239. package/dist/memory-provenance.d.ts +3 -1
  240. package/dist/memory-snapshot.d.ts +3 -1
  241. package/dist/memory-subject-cli.d.ts +40 -40
  242. package/dist/memory-subject.d.ts +42 -42
  243. package/dist/memory-worth-outcomes.d.ts +4 -2
  244. package/dist/models-json.d.ts +3 -1
  245. package/dist/namespaces/migrate.d.ts +5 -3
  246. package/dist/namespaces/migrate.js +4 -4
  247. package/dist/namespaces/principal.d.ts +3 -1
  248. package/dist/namespaces/search.d.ts +3 -1
  249. package/dist/namespaces/storage.d.ts +5 -3
  250. package/dist/namespaces/storage.js +3 -3
  251. package/dist/native-knowledge.d.ts +3 -1
  252. package/dist/offline-sync-impression-drain.d.ts +3 -1
  253. package/dist/operator-doctor-corpus.d.ts +3 -1
  254. package/dist/operator-doctor-corpus.js +6 -6
  255. package/dist/operator-doctor-replica.d.ts +3 -1
  256. package/dist/operator-toolkit.d.ts +5 -3
  257. package/dist/operator-toolkit.js +15 -12
  258. package/dist/orchestration/compression-guideline-coordinator.d.ts +4 -2
  259. package/dist/orchestration/maintenance.d.ts +6 -4
  260. package/dist/orchestration/maintenance.js +9 -9
  261. package/dist/{orchestrator-DyGUC7re.d.ts → orchestrator-Bvq1-RQs.d.ts} +23 -9
  262. package/dist/orchestrator.d.ts +12 -12
  263. package/dist/orchestrator.js +52 -52
  264. package/dist/patterns-cli.d.ts +3 -1
  265. package/dist/{pipeline-C25Oxtp1.d.ts → pipeline-C8vfIiTX.d.ts} +1 -1
  266. package/dist/policy-runtime.d.ts +3 -1
  267. package/dist/proactive-contention.d.ts +3 -1
  268. package/dist/provenance.d.ts +3 -1
  269. package/dist/{public-http-CMe7NQzj.d.ts → public-http-Cy0aYbys.d.ts} +1 -1
  270. package/dist/{qmd-DkDVCbgF.d.ts → qmd-B9p1SZTZ.d.ts} +1 -1
  271. package/dist/qmd-preflight.d.ts +4 -2
  272. package/dist/qmd-recall-cache.d.ts +3 -1
  273. package/dist/qmd.d.ts +4 -2
  274. package/dist/recall-concurrency-config.d.ts +3 -1
  275. package/dist/recall-disclosure-escalation.d.ts +3 -1
  276. package/dist/recall-explain-renderer.d.ts +3 -1
  277. package/dist/recall-explain-renderer.js +3 -3
  278. package/dist/recall-memory-map.d.ts +3 -1
  279. package/dist/recall-planner-llm.d.ts +3 -1
  280. package/dist/recall-state.d.ts +3 -1
  281. package/dist/recall-tag-filter.d.ts +3 -1
  282. package/dist/recall-timings.d.ts +3 -1
  283. package/dist/recall-xray-cli.d.ts +3 -1
  284. package/dist/recall-xray-cli.js +4 -4
  285. package/dist/recall-xray-renderer.d.ts +3 -1
  286. package/dist/recall-xray-renderer.js +3 -3
  287. package/dist/recall-xray.d.ts +3 -1
  288. package/dist/recall-xray.js +2 -2
  289. package/dist/reconcile/cursor.d.ts +3 -1
  290. package/dist/reconcile/manifest.d.ts +3 -1
  291. package/dist/reconcile/plan.d.ts +3 -1
  292. package/dist/replay/normalizers/chatgpt.d.ts +3 -1
  293. package/dist/replay/normalizers/claude.d.ts +3 -1
  294. package/dist/replay/normalizers/openclaw.d.ts +3 -1
  295. package/dist/replay/normalizers/shared.d.ts +3 -1
  296. package/dist/replay/runner.d.ts +3 -1
  297. package/dist/replay/types.d.ts +3 -1
  298. package/dist/replica-divergence.d.ts +3 -1
  299. package/dist/replica-peers-config.d.ts +3 -1
  300. package/dist/{resolution-MFY72STR.js → resolution-4552BNRV.js} +13 -3
  301. package/dist/resolve-auth-token.d.ts +3 -1
  302. package/dist/resume-bundles.js +4 -2
  303. package/dist/retrieval-agents.d.ts +4 -2
  304. package/dist/retrieval-tiers.d.ts +3 -1
  305. package/dist/routing/engine.d.ts +3 -1
  306. package/dist/routing/store.d.ts +3 -1
  307. package/dist/salvage-envelope.d.ts +3 -1
  308. package/dist/schemas.d.ts +44 -44
  309. package/dist/{scope-profiles-B9xMoFO-.d.ts → scope-profiles-DLrRxFWR.d.ts} +1 -1
  310. package/dist/search/embed-helper.d.ts +3 -1
  311. package/dist/search/factory.d.ts +3 -1
  312. package/dist/search/index.d.ts +3 -1
  313. package/dist/search/lancedb-backend.d.ts +3 -1
  314. package/dist/search/meilisearch-backend.d.ts +3 -1
  315. package/dist/search/noop-backend.d.ts +3 -1
  316. package/dist/search/orama-backend.d.ts +3 -1
  317. package/dist/search/port.d.ts +3 -1
  318. package/dist/search/remote-backend.d.ts +3 -1
  319. package/dist/{semantic-TddhF0y7.d.ts → semantic-BalR6EXU.d.ts} +1 -1
  320. package/dist/{semantic-consolidation-C4uykW84.d.ts → semantic-consolidation-DlN6xe20.d.ts} +1 -1
  321. package/dist/semantic-consolidation.d.ts +4 -2
  322. package/dist/semantic-consolidation.js +4 -4
  323. package/dist/semantic-rule-promotion.js +3 -3
  324. package/dist/semantic-rule-verifier.d.ts +3 -1
  325. package/dist/semantic-rule-verifier.js +3 -3
  326. package/dist/{service-DCZDZNcV.d.ts → service-GkYyVfm9.d.ts} +2 -2
  327. package/dist/session-observer-bands.d.ts +3 -1
  328. package/dist/session-observer-state.d.ts +3 -1
  329. package/dist/shared-context/manager.d.ts +3 -1
  330. package/dist/signal.d.ts +3 -1
  331. package/dist/source-agent-qualifier.d.ts +3 -1
  332. package/dist/source-agent-qualifier.js +3 -3
  333. package/dist/{storage-CcKmEjL8.d.ts → storage-BIDzuKIn.d.ts} +1 -1
  334. package/dist/storage.d.ts +4 -2
  335. package/dist/storage.js +2 -2
  336. package/dist/subject-config.d.ts +3 -1
  337. package/dist/summarizer.d.ts +3 -1
  338. package/dist/summary-snapshot.d.ts +3 -1
  339. package/dist/support-passport/index.d.ts +43 -43
  340. package/dist/support-passport/index.js +17 -17
  341. package/dist/temporal-supersession.d.ts +4 -2
  342. package/dist/temporal-timeline-recall.d.ts +3 -1
  343. package/dist/temporal-validity.d.ts +3 -1
  344. package/dist/threading.d.ts +3 -1
  345. package/dist/tier-migration.d.ts +4 -2
  346. package/dist/tier-routing.d.ts +3 -1
  347. package/dist/topics.d.ts +3 -1
  348. package/dist/transcript.d.ts +3 -1
  349. package/dist/transfer/types.d.ts +20 -20
  350. package/dist/trust-score-stage.d.ts +3 -1
  351. package/dist/trust-score.d.ts +3 -1
  352. package/dist/{types-Pi0lfaEt.d.ts → types-BxAcOl5Q.d.ts} +323 -4
  353. package/dist/types.d.ts +3 -1
  354. package/dist/types.js +1 -1
  355. package/dist/utility-runtime.d.ts +3 -1
  356. package/dist/verified-recall.js +3 -3
  357. package/dist/who-knows-cli.d.ts +40 -40
  358. package/dist/who-knows-cli.js +4 -4
  359. package/dist/who-knows.d.ts +42 -42
  360. package/dist/who-knows.js +3 -3
  361. package/dist/write-envelope.d.ts +3 -1
  362. package/package.json +2 -2
  363. package/src/access-boundary.ts +4 -0
  364. package/src/access-http.ts +6 -5
  365. package/src/access-mcp-admin-tools.ts +24 -0
  366. package/src/access-mcp-output-schemas.ts +14 -0
  367. package/src/access-mcp.ts +2 -2
  368. package/src/access-operations-batch.ts +5 -0
  369. package/src/access-service.ts +27 -0
  370. package/src/access-surface-catalog.test.ts +24 -0
  371. package/src/access-surface-catalog.ts +4 -0
  372. package/src/coding/action-gate.test.ts +341 -0
  373. package/src/coding/action-gate.ts +556 -0
  374. package/src/config.ts +5 -0
  375. package/src/contradiction/contradiction-review.ts +50 -4
  376. package/src/contradiction/contradiction-scan.ts +14 -3
  377. package/src/contradiction/resolution.ts +50 -2
  378. package/src/index.ts +4 -3
  379. package/src/orchestration/recall-rerank-coordinator.ts +123 -4
  380. package/src/orchestration/recall-result-formatter.ts +6 -2
  381. package/src/orchestration/recall-search-pipeline.ts +14 -1
  382. package/src/preferences/drift-config.ts +118 -0
  383. package/src/preferences/drift-recall.ts +133 -0
  384. package/src/preferences/drift-types.ts +87 -0
  385. package/src/preferences/index.ts +12 -0
  386. package/src/preferences/preference-drift.test.ts +690 -0
  387. package/src/preferences/preference-drift.ts +724 -0
  388. package/src/review/review-deck-access.test.ts +325 -0
  389. package/src/review/review-deck-http-base.ts +133 -0
  390. package/src/review/review-deck-mutation.test.ts +417 -0
  391. package/src/review/review-deck-mutation.ts +677 -0
  392. package/src/review/review-deck-snapshot.ts +196 -0
  393. package/src/review/review-deck-surface.ts +117 -0
  394. package/src/review/review-deck.test.ts +347 -0
  395. package/src/review/review-deck.ts +311 -0
  396. package/src/storage.ts +4 -6
  397. package/src/types.ts +7 -3
  398. package/dist/chunk-35NN3ZFA.js.map +0 -1
  399. package/dist/chunk-4KYOLI6M.js.map +0 -1
  400. package/dist/chunk-BWLRUMOG.js.map +0 -1
  401. package/dist/chunk-J275IYMG.js.map +0 -1
  402. package/dist/chunk-SB7D4BT7.js.map +0 -1
  403. package/dist/chunk-SBDCZ2AX.js.map +0 -1
  404. package/dist/chunk-TF77C7SV.js.map +0 -1
  405. package/dist/chunk-TZNQUNKL.js.map +0 -1
  406. package/dist/chunk-V3MXUL3D.js.map +0 -1
  407. package/dist/chunk-XCTXS7VY.js +0 -402
  408. package/dist/chunk-XCTXS7VY.js.map +0 -1
  409. package/dist/chunk-YLDXWCZP.js.map +0 -1
  410. /package/dist/{chunk-R7JAM2XF.js.map → chunk-2VXWFYJE.js.map} +0 -0
  411. /package/dist/{chunk-UXO5IMBK.js.map → chunk-4XDGYCZI.js.map} +0 -0
  412. /package/dist/{chunk-OTHZWLUD.js.map → chunk-57HVIG5U.js.map} +0 -0
  413. /package/dist/{chunk-EILZG5VC.js.map → chunk-6TAX66IA.js.map} +0 -0
  414. /package/dist/{chunk-NKKD4ZAU.js.map → chunk-6VALIZKG.js.map} +0 -0
  415. /package/dist/{chunk-2E36UXVW.js.map → chunk-7KFIMGMP.js.map} +0 -0
  416. /package/dist/{chunk-LGO6A7GJ.js.map → chunk-7QOO4LGC.js.map} +0 -0
  417. /package/dist/{chunk-DZUJ46DO.js.map → chunk-AUO2LJ3K.js.map} +0 -0
  418. /package/dist/{chunk-ZO6AXLEH.js.map → chunk-AYDDB3XI.js.map} +0 -0
  419. /package/dist/{chunk-NTWN6HB2.js.map → chunk-AYYGRVRP.js.map} +0 -0
  420. /package/dist/{chunk-7YQ4IYS2.js.map → chunk-AZAA5DNP.js.map} +0 -0
  421. /package/dist/{chunk-Z7PBQPJ4.js.map → chunk-BQUEAVNW.js.map} +0 -0
  422. /package/dist/{chunk-5YQ63T4B.js.map → chunk-D27J626P.js.map} +0 -0
  423. /package/dist/{chunk-LZH5FR2W.js.map → chunk-DHTQP334.js.map} +0 -0
  424. /package/dist/{chunk-Y7DK7HHJ.js.map → chunk-E4MVQQVX.js.map} +0 -0
  425. /package/dist/{chunk-JYYBA7TF.js.map → chunk-EIVIB73I.js.map} +0 -0
  426. /package/dist/{chunk-S2DPMJXH.js.map → chunk-EV3YFLCB.js.map} +0 -0
  427. /package/dist/{chunk-3I7ASZN3.js.map → chunk-G57LQRP6.js.map} +0 -0
  428. /package/dist/{chunk-UIN2BWDD.js.map → chunk-GN55KU2P.js.map} +0 -0
  429. /package/dist/{chunk-PE255F4T.js.map → chunk-GUXS322H.js.map} +0 -0
  430. /package/dist/{chunk-YB42WZCY.js.map → chunk-IQSC3V2E.js.map} +0 -0
  431. /package/dist/{chunk-MN4KB2A4.js.map → chunk-MBDJJTAT.js.map} +0 -0
  432. /package/dist/{chunk-SQI57UCJ.js.map → chunk-MRTUN665.js.map} +0 -0
  433. /package/dist/{chunk-SPUJNJJC.js.map → chunk-NZWEEALH.js.map} +0 -0
  434. /package/dist/{chunk-2XDWWPTN.js.map → chunk-OJD4WS2E.js.map} +0 -0
  435. /package/dist/{chunk-ZWXJBFZU.js.map → chunk-OLWTCWQ2.js.map} +0 -0
  436. /package/dist/{chunk-XRYZRW54.js.map → chunk-P4DHENRE.js.map} +0 -0
  437. /package/dist/{chunk-IID2CMUU.js.map → chunk-PDVQ5JU3.js.map} +0 -0
  438. /package/dist/{chunk-5NM4OH4P.js.map → chunk-QFMO32TO.js.map} +0 -0
  439. /package/dist/{chunk-SIHSCCUQ.js.map → chunk-QXIQPCCN.js.map} +0 -0
  440. /package/dist/{chunk-2YI72KM3.js.map → chunk-R47WINCY.js.map} +0 -0
  441. /package/dist/{chunk-ZQHI2BZB.js.map → chunk-RK4WXUI2.js.map} +0 -0
  442. /package/dist/{chunk-QOS5LBZN.js.map → chunk-S7Q5CM7E.js.map} +0 -0
  443. /package/dist/{chunk-GDFVPUJ5.js.map → chunk-TDKSCV62.js.map} +0 -0
  444. /package/dist/{chunk-OCZIYAE5.js.map → chunk-TYUS5TLG.js.map} +0 -0
  445. /package/dist/{chunk-GNCCUR6K.js.map → chunk-URHCUXKI.js.map} +0 -0
  446. /package/dist/{chunk-CT56C22C.js.map → chunk-Z4RPXHJH.js.map} +0 -0
  447. /package/dist/{contradiction-review-QKJH5DKD.js.map → contradiction-review-WRVLRWHS.js.map} +0 -0
  448. /package/dist/{contradiction-scan-VOTQRRSX.js.map → contradiction-scan-AKUWPX5N.js.map} +0 -0
  449. /package/dist/{resolution-MFY72STR.js.map → resolution-4552BNRV.js.map} +0 -0
@@ -10,7 +10,13 @@ import { composeMemoryEnvelope } from "../write-envelope.js";
10
10
  import type { StorageManager } from "../storage.js";
11
11
  import type { MemoryCategory, MemoryFile } from "../types.js";
12
12
  import type { ResolutionVerb } from "./contradiction-review.js";
13
- import { readPair, resolvePair } from "./contradiction-review.js";
13
+ import {
14
+ CONTRADICTION_VERBS,
15
+ PREFERENCE_DRIFT_VERBS,
16
+ readPair,
17
+ resolvePair,
18
+ } from "./contradiction-review.js";
19
+ import { resolvePreferenceDrift } from "../preferences/preference-drift.js";
14
20
 
15
21
  export interface ResolutionResult {
16
22
  pairId: string;
@@ -50,7 +56,14 @@ export interface ExecuteResolutionOptions {
50
56
  onMergedMemoryWritten?: (namespace: string | undefined, storageDir: string) => void;
51
57
  }
52
58
 
53
- const VALID_VERBS: ResolutionVerb[] = ["keep-a", "keep-b", "merge", "both-valid", "needs-more-context"];
59
+ /**
60
+ * Every verb the `review_resolve` surface accepts, across both review-item
61
+ * kinds (issue #2371). This is a *surface-level* gate only: per-kind validity
62
+ * is enforced inside {@link executeResolution}, which is the first place the
63
+ * item's `kind` is known. Callers therefore reject an unknown verb early and
64
+ * a cross-kind verb precisely.
65
+ */
66
+ const VALID_VERBS: readonly ResolutionVerb[] = [...CONTRADICTION_VERBS, ...PREFERENCE_DRIFT_VERBS];
54
67
 
55
68
  export function isValidResolutionVerb(value: string): value is ResolutionVerb {
56
69
  return VALID_VERBS.includes(value as ResolutionVerb);
@@ -94,6 +107,41 @@ export async function executeResolution(
94
107
  if (pair.resolution && pair.resolution !== "needs-more-context") {
95
108
  return { pairId, verb, affectedIds: [], message: `Pair already resolved with verb "${pair.resolution}"` };
96
109
  }
110
+ // Verb validity is per review-item kind (issue #2371). A preference-drift
111
+ // item never accepts keep-a/keep-b/merge (there is no "B" to keep — the
112
+ // second id is witness evidence, not a competing claim), and a contradiction
113
+ // pair never accepts keep/supersede/archive. Rejecting the cross-kind verb
114
+ // here, after the record is loaded, is the only place both kinds are known.
115
+ const isDriftItem = pair.kind === "preference-drift";
116
+ const verbMatchesKind = isDriftItem
117
+ ? PREFERENCE_DRIFT_VERBS.includes(verb)
118
+ : CONTRADICTION_VERBS.includes(verb);
119
+ if (!verbMatchesKind) {
120
+ const allowed = (isDriftItem ? PREFERENCE_DRIFT_VERBS : CONTRADICTION_VERBS).join(", ");
121
+ return {
122
+ pairId,
123
+ verb,
124
+ affectedIds: [],
125
+ message: `Verb "${verb}" is not valid for a ${isDriftItem ? "preference-drift" : "contradiction"} item. Valid: ${allowed}`,
126
+ };
127
+ }
128
+
129
+ if (isDriftItem) {
130
+ const outcome = await resolvePreferenceDrift(
131
+ resolutionStorage,
132
+ pair,
133
+ verb as "keep" | "supersede" | "archive",
134
+ {
135
+ mergedContent: options.mergedContent,
136
+ onMemoryWritten: options.onMergedMemoryWritten,
137
+ },
138
+ );
139
+ // Only record the terminal resolution when memories actually changed;
140
+ // a refused resolution must stay actionable in the queue.
141
+ if (outcome.affectedIds.length > 0) resolvePair(memoryDir, pairId, verb);
142
+ return { pairId, verb, affectedIds: outcome.affectedIds, message: outcome.message };
143
+ }
144
+
97
145
 
98
146
  const [idA, idB] = pair.memoryIds;
99
147
  const affectedIds: string[] = [];
package/src/index.ts CHANGED
@@ -230,6 +230,7 @@ export type {
230
230
  ProcedureStatsRecent,
231
231
  } from "./procedural/procedure-stats.js";
232
232
  export { runProcedureLibraryMaintenance, type ProcedureLibraryMaintenanceReport, type ProcedureMaintenanceAction, type ProcedureMaintenanceActionKind, type ProcedureMaintenanceReasonCode, type ProcedureMaintenanceEvidence, type ProcedureMaintenanceMarker } from "./procedural/library-maintenance.js";
233
+ export * from "./preferences/index.js";
233
234
  // ---------------------------------------------------------------------------
234
235
  // Direct-answer retrieval tier (issue #518)
235
236
  // ---------------------------------------------------------------------------
@@ -515,6 +516,7 @@ export {
515
516
  type RemnicChatGptMemoryInspectorResult,
516
517
  } from "./mcp-memory-inspector-app.js";
517
518
  export * from "./coding/pre-action-gate.js";
519
+ export * from "./coding/action-gate.js";
518
520
  export * from "./causal-trajectory.js";
519
521
  // Coding-agent subsystem (issue #569)
520
522
  export {
@@ -805,10 +807,7 @@ export {
805
807
  type SemanticDedupDecision,
806
808
  } from "./dedup/semantic.js";
807
809
 
808
- // ---------------------------------------------------------------------------
809
810
  // Review Inbox
810
- // ---------------------------------------------------------------------------
811
-
812
811
  export {
813
812
  listReviewItems,
814
813
  performReview,
@@ -818,6 +817,8 @@ export {
818
817
  type ReviewListResult,
819
818
  type ReviewOptions,
820
819
  } from "./review/index.js";
820
+ export * from "./review/review-deck.js";
821
+ export * from "./review/review-deck-snapshot.js";
821
822
 
822
823
  // ---------------------------------------------------------------------------
823
824
  // Diff-Aware Sync
@@ -24,6 +24,11 @@ import {
24
24
  buildMemoryWorthCounterMap,
25
25
  type MemoryWorthCounters,
26
26
  } from "../memory-worth-filter.js";
27
+ import {
28
+ applyPreferenceDriftRanking,
29
+ isPreferenceDriftStageActive,
30
+ type DriftRecallFrontmatter,
31
+ } from "../preferences/drift-recall.js";
27
32
  import {
28
33
  applyTrustScoreStage,
29
34
  buildTrustSignalsForRerank,
@@ -470,9 +475,16 @@ export class RecallRerankCoordinator {
470
475
  results: QmdSearchResult[];
471
476
  trustByPath: Map<string, TrustStageResultItem> | null;
472
477
  }> {
478
+ // The trust stage and the Memory Worth multiplier are mutually exclusive
479
+ // (rule 39); the preference-drift stage runs AFTER whichever one applied,
480
+ // on every branch, so its gate is uniform across recall paths (§27).
481
+ let staged = results;
482
+ let trustByPath: Map<string, TrustStageResultItem> | null = null;
473
483
  if (caps.recallTrustScore && results.length > 0) {
474
484
  try {
475
- return await this.applyTrustScoreRerank(results, namespaces, preloadedFrontmatter, abortSignal);
485
+ const outcome = await this.applyTrustScoreRerank(results, namespaces, preloadedFrontmatter, abortSignal);
486
+ staged = outcome.results;
487
+ trustByPath = outcome.trustByPath;
476
488
  } catch (err) {
477
489
  log.debug(`trust-score stage (${label}) failed open`, {
478
490
  error: (err as Error).message,
@@ -480,20 +492,127 @@ export class RecallRerankCoordinator {
480
492
  }
481
493
  } else if (caps.recallMemoryWorthFilter && results.length > 0) {
482
494
  try {
483
- const filtered = await this.applyMemoryWorthRerank(
495
+ staged = await this.applyMemoryWorthRerank(
484
496
  results,
485
497
  namespaces,
486
498
  preloadedFrontmatter,
487
499
  abortSignal,
488
500
  );
489
- return { results: filtered, trustByPath: null };
490
501
  } catch (err) {
491
502
  log.debug(`memory-worth filter (${label}) failed open`, {
492
503
  error: (err as Error).message,
493
504
  });
494
505
  }
495
506
  }
496
- return { results, trustByPath: null };
507
+ const damped = await this.applyPreferenceDriftStage(
508
+ staged,
509
+ namespaces,
510
+ preloadedFrontmatter,
511
+ abortSignal,
512
+ );
513
+ return { results: damped, trustByPath };
514
+ }
515
+
516
+ /**
517
+ * Issue #2371 — stale-preference damping + injection annotation.
518
+ *
519
+ * Runs immediately after the trust/Memory-Worth stage on EVERY recall branch
520
+ * (hot QMD, embedding fallback, recent scan, cold fallback), so the feature
521
+ * gate applies uniformly and fallback parity holds (§27, AGENTS.md retrieval
522
+ * guardrails). Fully inert on the default config: when neither damping nor
523
+ * annotation is active the input array is returned untouched and NO
524
+ * frontmatter is read, so recall ordering and injected text stay
525
+ * byte-identical to pre-#2371.
526
+ *
527
+ * Fail-open: any lookup error leaves the branch's results unchanged.
528
+ */
529
+ async applyPreferenceDriftStage(
530
+ results: QmdSearchResult[],
531
+ namespaces: string[],
532
+ preloadedFrontmatter?: ReadonlyMap<string, MemoryFile>,
533
+ abortSignal?: AbortSignal,
534
+ ): Promise<QmdSearchResult[]> {
535
+ const config = this.getConfig();
536
+ if (!isPreferenceDriftStageActive(config.driftDetection)) return results;
537
+ if (results.length === 0) return results;
538
+
539
+ try {
540
+ const frontmatterByKey = new Map<string, DriftRecallFrontmatter>();
541
+ const missing: QmdSearchResult[] = [];
542
+ for (const r of results) {
543
+ const key = memoryMapKey(r);
544
+ const preloaded = preloadedFrontmatter?.get(key) ?? preloadedFrontmatter?.get(r.path);
545
+ if (preloaded) {
546
+ frontmatterByKey.set(key, preloaded.frontmatter);
547
+ } else {
548
+ missing.push(r);
549
+ }
550
+ }
551
+
552
+ // Branches without a preloaded map (embedding fallback) read the
553
+ // candidates directly. Bounded-parallel at the same batch size the
554
+ // Memory Worth stage uses, and only for the handful of candidates the
555
+ // branch actually returned — never a corpus scan.
556
+ if (missing.length > 0) {
557
+ let reader: StorageManager | null = null;
558
+ for (const ns of namespaces) {
559
+ try {
560
+ reader = await this.getStorage(ns);
561
+ break;
562
+ } catch {
563
+ // try next namespace
564
+ }
565
+ }
566
+ if (reader) {
567
+ const readerNn = reader;
568
+ const BATCH = 16;
569
+ for (let off = 0; off < missing.length; off += BATCH) {
570
+ if (abortSignal?.aborted) break;
571
+ await Promise.all(
572
+ missing.slice(off, off + BATCH).map(async (r) => {
573
+ try {
574
+ const memory = await this.readQmdResultMemory(r.path, readerNn, namespaces, r.namespace);
575
+ if (memory) frontmatterByKey.set(memoryMapKey(r), memory.frontmatter);
576
+ } catch (err) {
577
+ log.debug("preference-drift: direct path lookup failed", {
578
+ path: r.path,
579
+ error: (err as Error).message,
580
+ });
581
+ }
582
+ }),
583
+ );
584
+ }
585
+ }
586
+ }
587
+
588
+ // Synthetic monotone-decreasing rank score so neutral candidates keep
589
+ // their upstream order and only the damped ones move — same technique the
590
+ // Memory Worth stage uses, for the same reason.
591
+ const ranked = results.map((r, i) => ({
592
+ key: memoryMapKey(r),
593
+ rank: results.length - i,
594
+ frontmatter: frontmatterByKey.get(memoryMapKey(r)),
595
+ }));
596
+ const ordered = applyPreferenceDriftRanking(ranked, {
597
+ config: config.driftDetection,
598
+ now: new Date(),
599
+ });
600
+
601
+ const byKey = new Map(results.map((r) => [memoryMapKey(r), r]));
602
+ const reordered: QmdSearchResult[] = [];
603
+ for (const item of ordered) {
604
+ const original = byKey.get(item.key);
605
+ if (!original) continue;
606
+ // `driftNote` is copied onto a NEW result object — never mutated in
607
+ // place — so a shared upstream result can't leak an annotation into an
608
+ // unrelated branch.
609
+ reordered.push(item.note ? { ...original, driftNote: item.note } : original);
610
+ }
611
+ return reordered;
612
+ } catch (err) {
613
+ log.debug("preference-drift stage failed open", { error: (err as Error).message });
614
+ return results;
615
+ }
497
616
  }
498
617
 
499
618
  diversifyRecallResultsWithHeadroom(
@@ -230,14 +230,18 @@ export class RecallResultFormatter {
230
230
  const withHandle = handle ? `${head.trimEnd()} ${handle}` : head.trimEnd();
231
231
  const connectorLabel = renderConnectorLabel(r.sourceConnector);
232
232
  const withConnector = connectorLabel ? `${withHandle} [agent: ${connectorLabel}]` : withHandle;
233
+ // Preference age note (issue #2371). Set only when
234
+ // `driftDetection.annotateAfterDays` is on, so this suffix is absent —
235
+ // and the rendered line byte-identical to pre-#2371 — by default.
236
+ const withDrift = r.driftNote ? `${withConnector} ${r.driftNote}` : withConnector;
233
237
  if (hedgeMap) {
234
238
  const item = trustResultFor(hedgeMap, r);
235
239
  if (item) {
236
240
  const hedge = renderEpistemicHedge(item.trust);
237
- if (hedge.length > 0) return `${withConnector} ${hedge}`;
241
+ if (hedge.length > 0) return `${withDrift} ${hedge}`;
238
242
  }
239
243
  }
240
- return withConnector;
244
+ return withDrift;
241
245
  });
242
246
  return { heading: `## ${title}`, entries };
243
247
  }
@@ -113,7 +113,10 @@ export interface RecallSearchPipelineDeps {
113
113
  recallNamespaces: string[],
114
114
  ): Promise<QueryAwarePrefilter>;
115
115
  readonly config: PluginConfig;
116
- readonly recallRerankCoordinator: Pick<RecallRerankCoordinator, "diversifyRecallResultsWithHeadroom">;
116
+ readonly recallRerankCoordinator: Pick<
117
+ RecallRerankCoordinator,
118
+ "diversifyRecallResultsWithHeadroom" | "applyPreferenceDriftStage"
119
+ >;
117
120
  diversifyAndLimitRecallResults(
118
121
  sectionId: string,
119
122
  results: QmdSearchResult[],
@@ -954,6 +957,16 @@ export class RecallSearchPipelineCoordinator {
954
957
  });
955
958
  }
956
959
  }
960
+ // Cold fallback boosts inline rather than through applyTrustScoreToBranch,
961
+ // so the preference-drift stage is applied here explicitly — otherwise
962
+ // damping would silently not apply to this branch (§27 fallback parity).
963
+ if (results.length > 0) {
964
+ results = await this.deps.recallRerankCoordinator.applyPreferenceDriftStage(
965
+ results,
966
+ options.recallNamespaces,
967
+ boostInput.memoryByPath,
968
+ );
969
+ }
957
970
 
958
971
  if (options.xrayPoolSizeSink) {
959
972
  options.xrayPoolSizeSink.size = Math.max(
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Preference drift detection config (issue #2371).
3
+ *
4
+ * Standalone top-level block, parsed the same way `contradictionScan` and
5
+ * `procedural.maintenance` are: shape-validated, string-coercion safe (§24),
6
+ * and honoring documented zero/no-op values (§33) rather than clamping them
7
+ * to something non-zero.
8
+ */
9
+
10
+ import { coerceBool, coerceNumber } from "../connectors/coerce.js";
11
+
12
+ export interface DriftDetectionConfig {
13
+ /** Master switch for drift classification and its apply mode. Default false. */
14
+ enabled: boolean;
15
+ /** A preference younger than this is never a scan candidate. Default 60. */
16
+ minAgeDays: number;
17
+ /** Evidence-gathering window, in days back from the run instant. Default 45. */
18
+ lookbackDays: number;
19
+ /** Cap on preferences classified per run. `0` disables the scan entirely. Default 25. */
20
+ maxCandidatesPerRun: number;
21
+ /** When true, recall damps `driftState: stale` preferences. Default false. */
22
+ recallDamping: boolean;
23
+ /** Multiplier applied to stale preferences in (0, 1]; `1` is a documented no-op. Default 0.8. */
24
+ stalePenalty: number;
25
+ /** Annotate injected preferences uncorroborated for this many days. `0` = off. Default 0. */
26
+ annotateAfterDays: number;
27
+ }
28
+
29
+ export const DRIFT_DETECTION_DEFAULTS: DriftDetectionConfig = {
30
+ enabled: false,
31
+ minAgeDays: 60,
32
+ lookbackDays: 45,
33
+ maxCandidatesPerRun: 25,
34
+ recallDamping: false,
35
+ stalePenalty: 0.8,
36
+ annotateAfterDays: 0,
37
+ };
38
+
39
+ function parseFlag(src: Record<string, unknown>, key: string, fallback: boolean): boolean {
40
+ const value = src[key];
41
+ if (value === undefined || value === null) return fallback;
42
+ const coerced = coerceBool(value);
43
+ if (coerced === undefined) {
44
+ throw new Error(
45
+ `driftDetection.${key} must be a boolean or one of "true"/"false"/"1"/"0"/"yes"/"no"/"on"/"off" (got ${JSON.stringify(value)}).`,
46
+ );
47
+ }
48
+ return coerced;
49
+ }
50
+
51
+ function parseDayCount(src: Record<string, unknown>, key: string, fallback: number): number {
52
+ const value = src[key];
53
+ if (value === undefined || value === null) return fallback;
54
+ const coerced = coerceNumber(value);
55
+ if (coerced === undefined || !Number.isFinite(coerced)) {
56
+ throw new Error(`driftDetection.${key} must be a finite number (got ${JSON.stringify(value)}).`);
57
+ }
58
+ if (!Number.isInteger(coerced)) {
59
+ throw new Error(`driftDetection.${key} must be an integer (got ${JSON.stringify(value)}).`);
60
+ }
61
+ if (coerced < 0 || coerced > 36_500) {
62
+ throw new Error(`driftDetection.${key} must be between 0 and 36500 (got ${JSON.stringify(value)}).`);
63
+ }
64
+ return coerced;
65
+ }
66
+
67
+ export function parseDriftDetectionConfig(raw: unknown): DriftDetectionConfig {
68
+ if (raw === undefined || raw === null) return { ...DRIFT_DETECTION_DEFAULTS };
69
+ if (typeof raw !== "object" || Array.isArray(raw)) {
70
+ throw new Error(
71
+ `driftDetection must be an object (got ${JSON.stringify(raw)}). Omit the key to keep drift detection disabled (issue #2371).`,
72
+ );
73
+ }
74
+ const src = raw as Record<string, unknown>;
75
+
76
+ // `0` disables the scan and MUST survive as zero — the documented disable
77
+ // value, never coerced up to a minimum (§33).
78
+ const rawMax = src.maxCandidatesPerRun;
79
+ let maxCandidatesPerRun = DRIFT_DETECTION_DEFAULTS.maxCandidatesPerRun;
80
+ if (rawMax !== undefined && rawMax !== null) {
81
+ const coerced = coerceNumber(rawMax);
82
+ if (coerced === undefined || !Number.isInteger(coerced) || coerced < 0) {
83
+ throw new Error(
84
+ `driftDetection.maxCandidatesPerRun must be an integer >= 0 (got ${JSON.stringify(rawMax)}). Use 0 to disable the scan.`,
85
+ );
86
+ }
87
+ maxCandidatesPerRun = coerced;
88
+ }
89
+
90
+ // stalePenalty is a multiplier in (0, 1]. `1` is the documented no-op; `0`
91
+ // would erase a memory's rank entirely and is rejected rather than accepted
92
+ // as a silent "delete from recall".
93
+ const rawPenalty = src.stalePenalty;
94
+ let stalePenalty = DRIFT_DETECTION_DEFAULTS.stalePenalty;
95
+ if (rawPenalty !== undefined && rawPenalty !== null) {
96
+ const coerced = coerceNumber(rawPenalty);
97
+ if (coerced === undefined || !Number.isFinite(coerced) || coerced <= 0 || coerced > 1) {
98
+ throw new Error(
99
+ `driftDetection.stalePenalty must be a number in (0, 1] (got ${JSON.stringify(rawPenalty)}). Use 1 to disable damping without changing the flag.`,
100
+ );
101
+ }
102
+ stalePenalty = coerced;
103
+ }
104
+
105
+ return {
106
+ enabled: parseFlag(src, "enabled", DRIFT_DETECTION_DEFAULTS.enabled),
107
+ minAgeDays: parseDayCount(src, "minAgeDays", DRIFT_DETECTION_DEFAULTS.minAgeDays),
108
+ lookbackDays: parseDayCount(src, "lookbackDays", DRIFT_DETECTION_DEFAULTS.lookbackDays),
109
+ maxCandidatesPerRun,
110
+ recallDamping: parseFlag(src, "recallDamping", DRIFT_DETECTION_DEFAULTS.recallDamping),
111
+ stalePenalty,
112
+ annotateAfterDays: parseDayCount(
113
+ src,
114
+ "annotateAfterDays",
115
+ DRIFT_DETECTION_DEFAULTS.annotateAfterDays,
116
+ ),
117
+ };
118
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Stale-preference recall stage (issue #2371).
3
+ *
4
+ * Pure ranking + annotation helpers, deliberately free of storage, LLM, and
5
+ * config-loading imports so the recall hot path can import them without
6
+ * pulling in the drift scan's dependency graph.
7
+ *
8
+ * Two independent, independently-gated effects:
9
+ *
10
+ * 1. `driftDetection.recallDamping` multiplies the rank score of a
11
+ * `category: preference` memory carrying `driftState: stale` by
12
+ * `stalePenalty`, so a stale preference sinks below an otherwise-equal
13
+ * fresh memory. Applied in the same boost stage as the Memory Worth
14
+ * multiplier, so the pipeline-order contract (retrieve → filter →
15
+ * rerank/boost → cap → format) is untouched.
16
+ * 2. `driftDetection.annotateAfterDays` emits a compact age note for a
17
+ * preference whose last corroboration is older than the configured
18
+ * window. Formatting-stage only — it never mutates the stored memory.
19
+ *
20
+ * Both are off by default, and with both off this module returns the input
21
+ * order and no notes, so recall output is byte-identical to pre-#2371.
22
+ */
23
+
24
+ import type { DriftDetectionConfig } from "./drift-config.js";
25
+ import type { MemoryDriftState } from "./drift-types.js";
26
+
27
+ const DAY_MS = 24 * 60 * 60 * 1000;
28
+
29
+ /** The only frontmatter fields this stage reads. */
30
+ export interface DriftRecallFrontmatter {
31
+ category?: string;
32
+ driftState?: MemoryDriftState;
33
+ lastCorroborated?: string;
34
+ created?: string;
35
+ }
36
+
37
+ export interface DriftRankInput {
38
+ /** Opaque candidate identity — the caller's composite (namespace, path) key. */
39
+ key: string;
40
+ /** Monotone-decreasing rank score preserving upstream order. */
41
+ rank: number;
42
+ frontmatter?: DriftRecallFrontmatter;
43
+ }
44
+
45
+ export interface DriftRankOutput {
46
+ key: string;
47
+ /** `1` when untouched, `stalePenalty` when damped. */
48
+ multiplier: number;
49
+ /** Injection annotation, when the age gate fired. */
50
+ note?: string;
51
+ }
52
+
53
+ /**
54
+ * True when this stage would change nothing. Callers use it to skip the
55
+ * frontmatter lookups entirely on the default (all-off) configuration.
56
+ *
57
+ * `stalePenalty === 1` is the documented no-op (§33): damping stays "enabled"
58
+ * but multiplies by one, so it must not by itself activate the stage.
59
+ */
60
+ export function isPreferenceDriftStageActive(config: DriftDetectionConfig | undefined): boolean {
61
+ if (!config) return false;
62
+ // `recallDamping` is a real boolean here: parseDriftDetectionConfig already
63
+ // coerced the string forms and threw on anything unrecognized, so no further
64
+ // coercion is needed at this boundary.
65
+ const damping = config.recallDamping && config.stalePenalty < 1;
66
+ return damping || config.annotateAfterDays > 0;
67
+ }
68
+
69
+ /**
70
+ * Render the age note for an uncorroborated preference, or `undefined` when
71
+ * the gate is off or the preference is inside the window.
72
+ *
73
+ * `lastCorroborated` absent falls back to `created`: a preference the scan has
74
+ * never corroborated is measured from when it was stated, which is exactly the
75
+ * "stated 2026-01; not corroborated since" case the survey describes.
76
+ */
77
+ export function driftAgeNote(
78
+ frontmatter: DriftRecallFrontmatter | undefined,
79
+ config: DriftDetectionConfig,
80
+ now: Date,
81
+ ): string | undefined {
82
+ if (config.annotateAfterDays <= 0) return undefined;
83
+ if (!frontmatter || frontmatter.category !== "preference") return undefined;
84
+ const anchor = frontmatter.lastCorroborated ?? frontmatter.created;
85
+ if (typeof anchor !== "string") return undefined;
86
+ const anchorMs = Date.parse(anchor);
87
+ if (!Number.isFinite(anchorMs)) return undefined;
88
+ const ageDays = Math.floor((now.getTime() - anchorMs) / DAY_MS);
89
+ if (ageDays < config.annotateAfterDays) return undefined;
90
+ const stated = new Date(anchorMs).toISOString().slice(0, 7);
91
+ return frontmatter.lastCorroborated
92
+ ? `(corroborated ${stated}; not since)`
93
+ : `(stated ${stated}; not corroborated since)`;
94
+ }
95
+
96
+ /**
97
+ * Rank and annotate one recall branch's candidates.
98
+ *
99
+ * Returns one output per input, ordered by damped rank descending with a
100
+ * stable key tiebreak so the comparator is total and the ordering is identical
101
+ * across runs (§12).
102
+ */
103
+ export function applyPreferenceDriftRanking(
104
+ inputs: readonly DriftRankInput[],
105
+ options: { config: DriftDetectionConfig; now: Date },
106
+ ): DriftRankOutput[] {
107
+ const { config, now } = options;
108
+ const damping = config.recallDamping && config.stalePenalty < 1;
109
+
110
+ const scored = inputs.map((input, index) => {
111
+ const fm = input.frontmatter;
112
+ const isStalePreference = fm?.category === "preference" && fm.driftState === "stale";
113
+ const multiplier = damping && isStalePreference ? config.stalePenalty : 1;
114
+ return {
115
+ key: input.key,
116
+ index,
117
+ multiplier,
118
+ damped: input.rank * multiplier,
119
+ note: driftAgeNote(fm, config, now),
120
+ };
121
+ });
122
+
123
+ scored.sort((a, b) => {
124
+ if (a.damped !== b.damped) return a.damped > b.damped ? -1 : 1;
125
+ // Equal damped score → preserve upstream order, then key, so the
126
+ // comparator returns 0 only for genuinely identical entries.
127
+ if (a.index !== b.index) return a.index < b.index ? -1 : 1;
128
+ if (a.key !== b.key) return a.key < b.key ? -1 : 1;
129
+ return 0;
130
+ });
131
+
132
+ return scored.map((s) => (s.note ? { key: s.key, multiplier: s.multiplier, note: s.note } : { key: s.key, multiplier: s.multiplier }));
133
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Shared preference-drift type surfaces (issue #2371).
3
+ *
4
+ * These live in the preferences module rather than in `types.ts` so the
5
+ * cross-cutting fields drift detection introduces are declared next to the
6
+ * subsystem that owns them, and `types.ts` composes them through `extends`
7
+ * instead of growing another inline block.
8
+ */
9
+
10
+ import type { DriftDetectionConfig } from "./drift-config.js";
11
+
12
+ /**
13
+ * Drift verdict persisted on `preference` memories. `corroborated` is
14
+ * deliberately NOT a member: a corroborated preference clears `driftState` and
15
+ * stamps `lastCorroborated`, so the stored enum only carries a negative
16
+ * verdict.
17
+ */
18
+ export type MemoryDriftState = "stale" | "drifted";
19
+
20
+ /**
21
+ * Derived drift provenance stamped by the scan AFTER a memory exists — never
22
+ * supplied at compose time, which is why these fields stay outside the sealed
23
+ * write envelope for exactly the reason `mw_success` / `mw_fail` do.
24
+ *
25
+ * Mixed into `MemoryFrontmatter`.
26
+ */
27
+ export interface MemoryDriftProvenance {
28
+ /**
29
+ * `stale` means the lookback window held no corroborating evidence either
30
+ * way; `drifted` means recent evidence pointed away from the preference and
31
+ * a review item was opened. Absent means "not classified".
32
+ */
33
+ driftState?: MemoryDriftState;
34
+ /** ISO 8601 timestamp of the last run that found corroborating evidence. */
35
+ lastCorroborated?: string;
36
+ }
37
+
38
+ /**
39
+ * Per-recall render hint, mixed into `QmdSearchResult`. Set by the
40
+ * preference-drift recall stage when `driftDetection.annotateAfterDays` is on
41
+ * and absent otherwise. Never persisted — this annotates one injection, it is
42
+ * not memory state.
43
+ */
44
+ export interface RecallDriftAnnotation {
45
+ driftNote?: string;
46
+ }
47
+
48
+ /** Mixed into `PluginConfig`. */
49
+ export interface DriftDetectionSettings {
50
+ driftDetection: DriftDetectionConfig;
51
+ }
52
+
53
+ /**
54
+ * Frontmatter codec for the drift stamps. Lives here, beside the interface it
55
+ * serializes, so `storage.ts` stays a caller rather than growing another
56
+ * field-specific block.
57
+ */
58
+ export function serializeDriftProvenance(fm: MemoryDriftProvenance): string[] {
59
+ const lines: string[] = [];
60
+ if (fm.driftState !== undefined) {
61
+ // An unrecognized verdict on write is a caller bug that must surface, not
62
+ // something to persist silently.
63
+ if (fm.driftState !== "stale" && fm.driftState !== "drifted") {
64
+ throw new Error(
65
+ `serializeFrontmatter: invalid driftState ${JSON.stringify(fm.driftState)} — expected "stale" | "drifted"`,
66
+ );
67
+ }
68
+ lines.push(`driftState: ${fm.driftState}`);
69
+ }
70
+ if (fm.lastCorroborated) lines.push(`lastCorroborated: ${fm.lastCorroborated}`);
71
+ return lines;
72
+ }
73
+
74
+ /**
75
+ * Read the drift stamps back. A hand-edited or unrecognized stored verdict
76
+ * round-trips to `undefined` rather than poisoning recall damping — the same
77
+ * policy the Memory Worth counters use for corrupt values.
78
+ */
79
+ export function parseDriftProvenance(
80
+ fm: Record<string, string | undefined>,
81
+ ): MemoryDriftProvenance {
82
+ return {
83
+ driftState:
84
+ fm.driftState === "stale" || fm.driftState === "drifted" ? fm.driftState : undefined,
85
+ lastCorroborated: fm.lastCorroborated || undefined,
86
+ };
87
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Preference drift detection (issue #2371) — public barrel.
3
+ *
4
+ * Ordered leaf-first (types → config → pure recall stage → scan/resolution) so
5
+ * the scan module, which is the only member with storage and contradiction
6
+ * imports, initializes last.
7
+ */
8
+
9
+ export * from "./drift-types.js";
10
+ export * from "./drift-config.js";
11
+ export * from "./drift-recall.js";
12
+ export * from "./preference-drift.js";