@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
@@ -0,0 +1,556 @@
1
+ /**
2
+ * Action-site failure gate (issue #2382, slice 1 — core contract).
3
+ *
4
+ * The H6 study (#1963) measured that a failure memory delivered in the prompt
5
+ * preamble barely changes behaviour, while the same memory delivered at the
6
+ * moment the agent proposes the matched action removes the repeated failure.
7
+ * This module productionises that delivery point: `ActionGateService` wraps the
8
+ * experimental `PreActionFailureGate` primitive, adds failure-class memory
9
+ * candidates, a per-turn budget, cross-channel suppression, and telemetry.
10
+ *
11
+ * Scope, per the study's honest limit: the gate removes a known failure mode.
12
+ * It does not improve task completion. It is advisory only — it never blocks an
13
+ * action, never outruns its deadline, and fails open on every error path.
14
+ */
15
+ import type { ActionIntent, ActionStrategyId } from "../causal-trajectory.js";
16
+ import { coerceBool, coerceNumber } from "../connectors/coerce.js";
17
+ import type { CodingContext, MemoryFile } from "../types.js";
18
+ import {
19
+ PRE_ACTION_GATE_DEFAULT_TIMEOUT_MS,
20
+ type PreActionFailureGate,
21
+ normalizeActionIntent,
22
+ sanitizePayloadString,
23
+ } from "./pre-action-gate.js";
24
+
25
+ export const ACTION_GATE_DEFAULT_MAX_ADVISORIES_PER_TURN = 3;
26
+ /** Bound on a single advisory payload, above the primitive's citation bound. */
27
+ const MAX_ADVISORY_CHARS = 400;
28
+ /** Bounded lexical matching: terms considered from one proposed action. */
29
+ const MAX_ACTION_TERMS = 24;
30
+ const MIN_TERM_CHARS = 3;
31
+ /** Fraction of action terms a failure memory must mention to be a candidate. */
32
+ const MIN_TERM_OVERLAP_RATIO = 0.6;
33
+ /** Sessions tracked for suppression before the oldest is evicted. */
34
+ const MAX_TRACKED_SESSIONS = 200;
35
+ /** Memory ids remembered per session for cross-channel suppression. */
36
+ const MAX_TRACKED_IDS_PER_SESSION = 200;
37
+
38
+ export interface ActionGateConfig {
39
+ /**
40
+ * Master gate. Default `false`: when disabled no lookup, no matching, and no
41
+ * suppression bookkeeping runs on any path.
42
+ */
43
+ enabled: boolean;
44
+ /** Hard latency cap for a whole gate evaluation. Overrun fails open. */
45
+ timeoutMs: number;
46
+ /** Advisories injected per turn. `0` disables injection and all lookups. */
47
+ maxAdvisoriesPerTurn: number;
48
+ }
49
+
50
+ export const ACTION_GATE_DEFAULTS: ActionGateConfig = {
51
+ enabled: false,
52
+ timeoutMs: PRE_ACTION_GATE_DEFAULT_TIMEOUT_MS,
53
+ maxAdvisoriesPerTurn: ACTION_GATE_DEFAULT_MAX_ADVISORIES_PER_TURN,
54
+ };
55
+
56
+ export function parseActionGateConfig(raw: unknown): ActionGateConfig {
57
+ if (raw !== undefined && (raw === null || typeof raw !== "object" || Array.isArray(raw))) {
58
+ throw new Error(
59
+ `actionGate must be an object (got ${JSON.stringify(raw)}). Use actionGate: { enabled: true } to opt in.`
60
+ );
61
+ }
62
+ const block = (raw ?? {}) as Record<string, unknown>;
63
+ const rawEnabled = block.enabled;
64
+ let enabled = ACTION_GATE_DEFAULTS.enabled;
65
+ if (rawEnabled !== undefined) {
66
+ const coerced = coerceBool(rawEnabled);
67
+ if (coerced === undefined) {
68
+ throw new Error(
69
+ `actionGate.enabled must be a boolean or one of "true"/"false"/"1"/"0"/"yes"/"no"/"on"/"off" (got ${JSON.stringify(rawEnabled)}).`
70
+ );
71
+ }
72
+ enabled = coerced;
73
+ }
74
+ const rawTimeout = coerceNumber(block.timeoutMs);
75
+ const timeoutMs =
76
+ rawTimeout !== undefined && Number.isFinite(rawTimeout)
77
+ ? Math.min(5000, Math.max(1, Math.floor(rawTimeout)))
78
+ : ACTION_GATE_DEFAULTS.timeoutMs;
79
+ const rawMax = coerceNumber(block.maxAdvisoriesPerTurn);
80
+ // Zero-limit semantics are a compatibility contract: `0` stays `0`.
81
+ const maxAdvisoriesPerTurn =
82
+ rawMax !== undefined && Number.isFinite(rawMax)
83
+ ? Math.min(20, Math.max(0, Math.floor(rawMax)))
84
+ : ACTION_GATE_DEFAULTS.maxAdvisoriesPerTurn;
85
+ return { enabled, timeoutMs, maxAdvisoriesPerTurn };
86
+ }
87
+
88
+ export type ActionGateStatus = "DISABLED" | "NO_MATCH" | "MATCH_WARN" | "ERROR_FAIL_OPEN";
89
+
90
+ export interface ActionGateRequest {
91
+ sessionKey: string;
92
+ /** Turn identity: the per-turn budget and dedupe reset when this changes. */
93
+ turnId: string;
94
+ strategyId: ActionStrategyId;
95
+ intent: ActionIntent;
96
+ codingContext: CodingContext;
97
+ /** Memory directory ALREADY resolved for the caller's readable namespace. */
98
+ memoryDir: string;
99
+ /** Resolved readable namespace, passed verbatim to candidate sources. */
100
+ namespace?: string;
101
+ causalTrajectoryStoreDir?: string;
102
+ signal?: AbortSignal;
103
+ }
104
+
105
+ export interface ActionGateAdvisory {
106
+ /** Trajectory id or memory id — the unit of dedupe and suppression. */
107
+ memoryId: string;
108
+ sourceId: string;
109
+ text: string;
110
+ }
111
+
112
+ export interface ActionGateDecision {
113
+ status: ActionGateStatus;
114
+ advisories: ActionGateAdvisory[];
115
+ /** Candidates withheld: already delivered this turn/session, or over budget. */
116
+ suppressedIds: string[];
117
+ /** Source failures, kept distinct from "no candidates" (rule 22). */
118
+ degradations: string[];
119
+ fingerprint?: string;
120
+ reason?: string;
121
+ latencyMs: number;
122
+ }
123
+
124
+ export interface ActionGateCandidate {
125
+ memoryId: string;
126
+ advisoryText: string;
127
+ /** Deterministic ranking key; higher wins, ties break on `memoryId`. */
128
+ score?: number;
129
+ }
130
+
131
+ export interface ActionGateCandidateQuery {
132
+ sessionKey: string;
133
+ fingerprint: string;
134
+ /** Normalized, deduplicated lexical terms describing the proposed action. */
135
+ actionTerms: string[];
136
+ strategyId: ActionStrategyId;
137
+ intent: ActionIntent;
138
+ codingContext: CodingContext;
139
+ memoryDir: string;
140
+ namespace?: string;
141
+ causalTrajectoryStoreDir?: string;
142
+ signal?: AbortSignal;
143
+ }
144
+
145
+ export type ActionGateSourceResult = { ok: true; candidates: ActionGateCandidate[] } | { ok: false; reason: string };
146
+
147
+ export interface ActionGateCandidateSource {
148
+ readonly id: string;
149
+ find(query: ActionGateCandidateQuery): Promise<ActionGateSourceResult>;
150
+ }
151
+
152
+ export interface ActionGateAuditRecord {
153
+ ts: string;
154
+ sessionKey: string;
155
+ turnId: string;
156
+ status: ActionGateStatus;
157
+ latencyMs: number;
158
+ fingerprint?: string;
159
+ deliveredIds: string[];
160
+ suppressedIds: string[];
161
+ degradations: string[];
162
+ reason?: string;
163
+ }
164
+
165
+ export type ActionGateAuditSink = (record: ActionGateAuditRecord) => void | Promise<void>;
166
+
167
+ export interface ActionGateServiceDependencies {
168
+ sources?: ActionGateCandidateSource[];
169
+ audit?: ActionGateAuditSink;
170
+ clock?: () => number;
171
+ now?: () => Date;
172
+ }
173
+
174
+ /**
175
+ * Deterministic, local term extraction from a proposed action. No LLM and no
176
+ * I/O: this runs in the hot path ahead of the tool call.
177
+ */
178
+ export function actionTermsFor(intent: ActionIntent): string[] {
179
+ const raw =
180
+ intent.kind === "command"
181
+ ? [intent.command, ...(intent.args ?? [])]
182
+ : [intent.filePath, intent.editKind, intent.symbol ?? "", intent.range ?? ""];
183
+ const terms: string[] = [];
184
+ const seen = new Set<string>();
185
+ for (const piece of raw) {
186
+ for (const token of sanitizePayloadString(piece ?? "")
187
+ .toLowerCase()
188
+ .split(/[^a-z0-9._-]+/)) {
189
+ const term = token.replace(/^[._-]+|[._-]+$/g, "");
190
+ if (term.length < MIN_TERM_CHARS || seen.has(term)) continue;
191
+ seen.add(term);
192
+ terms.push(term);
193
+ if (terms.length >= MAX_ACTION_TERMS) return terms;
194
+ }
195
+ }
196
+ return terms;
197
+ }
198
+
199
+ /**
200
+ * Failure-class markers on stored memories, read from frontmatter only so the
201
+ * decision stays deterministic: explicit corrections, procedures flagged for
202
+ * repair by library maintenance, and memories whose recorded outcomes are
203
+ * failure-dominant.
204
+ */
205
+ export function isFailureClassMemory(memory: MemoryFile): boolean {
206
+ const fm = memory.frontmatter;
207
+ if (fm.status !== undefined && fm.status !== "active") return false;
208
+ if (fm.category === "correction") return true;
209
+ if (fm.structuredAttributes?.needsRepair === "true") return true;
210
+ const failures = typeof fm.mw_fail === "number" ? fm.mw_fail : 0;
211
+ const successes = typeof fm.mw_success === "number" ? fm.mw_success : 0;
212
+ return failures > 0 && failures > successes;
213
+ }
214
+
215
+ export interface FailureMemorySourceDependencies {
216
+ /**
217
+ * Namespace-scoped listing of candidate memories. Callers wire this to a
218
+ * storage read for the SAME namespace the gate request resolved, so a
219
+ * principal can never receive another principal's advisories.
220
+ */
221
+ listMemories: (options: {
222
+ namespace?: string;
223
+ memoryDir: string;
224
+ signal?: AbortSignal;
225
+ }) => Promise<MemoryFile[]>;
226
+ }
227
+
228
+ /**
229
+ * Failure-class memory candidates, matched by bounded lexical overlap against
230
+ * the proposed action's terms. Deterministic and local by construction.
231
+ */
232
+ export function createFailureMemoryCandidateSource(deps: FailureMemorySourceDependencies): ActionGateCandidateSource {
233
+ return {
234
+ id: "failure-memory",
235
+ async find(query) {
236
+ if (query.actionTerms.length === 0) return { ok: true, candidates: [] };
237
+ let memories: MemoryFile[];
238
+ try {
239
+ memories = await deps.listMemories({
240
+ namespace: query.namespace,
241
+ memoryDir: query.memoryDir,
242
+ signal: query.signal,
243
+ });
244
+ } catch (error) {
245
+ return { ok: false, reason: error instanceof Error ? error.message : String(error) };
246
+ }
247
+ const required = Math.max(1, Math.ceil(query.actionTerms.length * MIN_TERM_OVERLAP_RATIO));
248
+ const candidates: ActionGateCandidate[] = [];
249
+ for (const memory of memories) {
250
+ if (!isFailureClassMemory(memory)) continue;
251
+ const haystack = `${memory.content} ${(memory.frontmatter.tags ?? []).join(" ")}`.toLowerCase();
252
+ let hits = 0;
253
+ for (const term of query.actionTerms) if (haystack.includes(term)) hits += 1;
254
+ if (hits < required) continue;
255
+ candidates.push({
256
+ memoryId: memory.frontmatter.id,
257
+ score: hits,
258
+ advisoryText: `[ActionGate] A stored failure memory matches this action: "${memory.content}".`,
259
+ });
260
+ }
261
+ candidates.sort((left, right) => {
262
+ const byScore = (right.score ?? 0) - (left.score ?? 0);
263
+ return byScore !== 0 ? byScore : left.memoryId.localeCompare(right.memoryId);
264
+ });
265
+ return { ok: true, candidates };
266
+ },
267
+ };
268
+ }
269
+
270
+ /**
271
+ * Candidate source over recorded causal-trajectory failures, delegating the
272
+ * fingerprint match to the existing `PreActionFailureGate` primitive.
273
+ */
274
+ export function createTrajectoryCandidateSource(gate: PreActionFailureGate): ActionGateCandidateSource {
275
+ return {
276
+ id: "causal-trajectory",
277
+ async find(query) {
278
+ const result = await gate.evaluate({
279
+ sessionKey: query.sessionKey,
280
+ strategyId: query.strategyId,
281
+ intent: query.intent,
282
+ codingContext: query.codingContext,
283
+ memoryDir: query.memoryDir,
284
+ causalTrajectoryStoreDir: query.causalTrajectoryStoreDir,
285
+ signal: query.signal,
286
+ });
287
+ if (result.status === "ERROR_FAIL_OPEN") {
288
+ return { ok: false, reason: result.reason ?? "pre-action gate failed open" };
289
+ }
290
+ if (result.status !== "MATCH_WARN" || !result.matchedTrajectoryId || !result.advisoryText) {
291
+ return { ok: true, candidates: [] };
292
+ }
293
+ return {
294
+ ok: true,
295
+ candidates: [
296
+ {
297
+ memoryId: result.matchedTrajectoryId,
298
+ // Trajectory matches are exact fingerprint hits: rank above lexical ones.
299
+ score: Number.MAX_SAFE_INTEGER,
300
+ advisoryText: result.advisoryText,
301
+ },
302
+ ],
303
+ };
304
+ },
305
+ };
306
+ }
307
+
308
+ interface SessionSuppression {
309
+ turnId: string;
310
+ /** Ids the gate delivered during `turnId`. */
311
+ deliveredThisTurn: Set<string>;
312
+ /** Ids the gate delivered anywhere in the session; turn-start must skip them. */
313
+ deliveredByGate: Set<string>;
314
+ /** Ids turn-start injection already delivered; the gate must skip them. */
315
+ deliveredByTurnStart: Set<string>;
316
+ }
317
+
318
+ /**
319
+ * Advisory gate evaluated immediately before an agent action.
320
+ *
321
+ * Delivery is bounded three ways: a hard deadline, `maxAdvisoriesPerTurn`, and
322
+ * dedupe per memory per turn. Cross-channel suppression keeps one memory from
323
+ * arriving twice in a session — once here and once from turn-start recall.
324
+ */
325
+ export class ActionGateService {
326
+ private readonly config: ActionGateConfig;
327
+ private readonly sources: ActionGateCandidateSource[];
328
+ private readonly audit?: ActionGateAuditSink;
329
+ private readonly clock: () => number;
330
+ private readonly now: () => Date;
331
+ private readonly sessions = new Map<string, SessionSuppression>();
332
+
333
+ constructor(config: ActionGateConfig, deps: ActionGateServiceDependencies = {}) {
334
+ this.config = config;
335
+ this.sources = deps.sources ?? [];
336
+ this.audit = deps.audit;
337
+ this.clock = deps.clock ?? Date.now;
338
+ this.now = deps.now ?? (() => new Date());
339
+ }
340
+
341
+ /**
342
+ * Memory ids the gate already delivered in this session. Turn-start recall
343
+ * filters these out before its own cap, honouring the filter-then-cap order.
344
+ */
345
+ public suppressedForTurnStart(sessionKey: string): string[] {
346
+ if (!this.config.enabled) return [];
347
+ return [...(this.sessions.get(sessionKey)?.deliveredByGate ?? [])];
348
+ }
349
+
350
+ /** Record ids turn-start recall injected, so the gate will not repeat them. */
351
+ public noteTurnStartDelivery(sessionKey: string, memoryIds: readonly string[]): void {
352
+ if (!this.config.enabled || memoryIds.length === 0) return;
353
+ const state = this.sessionState(sessionKey, undefined);
354
+ for (const id of memoryIds) this.addTracked(state.deliveredByTurnStart, id);
355
+ }
356
+
357
+ public resetSession(sessionKey: string): void {
358
+ this.sessions.delete(sessionKey);
359
+ }
360
+
361
+ async evaluate(request: ActionGateRequest): Promise<ActionGateDecision> {
362
+ const startedAt = this.clock();
363
+ if (!this.config.enabled) {
364
+ return {
365
+ status: "DISABLED",
366
+ advisories: [],
367
+ suppressedIds: [],
368
+ degradations: [],
369
+ reason: "actionGate.enabled is false",
370
+ latencyMs: 0,
371
+ };
372
+ }
373
+ if (this.config.maxAdvisoriesPerTurn === 0) {
374
+ return this.finish(request, startedAt, {
375
+ status: "DISABLED",
376
+ advisories: [],
377
+ suppressedIds: [],
378
+ degradations: [],
379
+ reason: "actionGate.maxAdvisoriesPerTurn is 0",
380
+ });
381
+ }
382
+
383
+ let fingerprint: string;
384
+ try {
385
+ fingerprint = normalizeActionIntent(request.intent, request.strategyId, request.codingContext).fingerprint;
386
+ } catch (error) {
387
+ return this.finish(request, startedAt, {
388
+ status: "ERROR_FAIL_OPEN",
389
+ advisories: [],
390
+ suppressedIds: [],
391
+ degradations: [],
392
+ reason: error instanceof Error ? error.message : String(error),
393
+ });
394
+ }
395
+
396
+ const deadline = startedAt + this.config.timeoutMs;
397
+ const collected = await this.collect(request, fingerprint, deadline);
398
+ const state = this.sessionState(request.sessionKey, request.turnId);
399
+ const advisories: ActionGateAdvisory[] = [];
400
+ const suppressedIds: string[] = [];
401
+ for (const { sourceId, candidate } of collected.candidates) {
402
+ if (state.deliveredThisTurn.has(candidate.memoryId) || state.deliveredByTurnStart.has(candidate.memoryId)) {
403
+ suppressedIds.push(candidate.memoryId);
404
+ continue;
405
+ }
406
+ if (advisories.length >= this.config.maxAdvisoriesPerTurn) {
407
+ suppressedIds.push(candidate.memoryId);
408
+ continue;
409
+ }
410
+ advisories.push({
411
+ memoryId: candidate.memoryId,
412
+ sourceId,
413
+ text: sanitizePayloadString(candidate.advisoryText).replace(/\s+/g, " ").slice(0, MAX_ADVISORY_CHARS),
414
+ });
415
+ this.addTracked(state.deliveredThisTurn, candidate.memoryId);
416
+ this.addTracked(state.deliveredByGate, candidate.memoryId);
417
+ }
418
+
419
+ const status: ActionGateStatus =
420
+ advisories.length > 0 ? "MATCH_WARN" : collected.degradations.length > 0 ? "ERROR_FAIL_OPEN" : "NO_MATCH";
421
+ return this.finish(request, startedAt, {
422
+ status,
423
+ advisories,
424
+ suppressedIds,
425
+ degradations: collected.degradations,
426
+ fingerprint,
427
+ reason: status === "ERROR_FAIL_OPEN" ? collected.degradations[0] : undefined,
428
+ });
429
+ }
430
+
431
+ private async collect(
432
+ request: ActionGateRequest,
433
+ fingerprint: string,
434
+ deadline: number
435
+ ): Promise<{
436
+ candidates: Array<{ sourceId: string; candidate: ActionGateCandidate }>;
437
+ degradations: string[];
438
+ }> {
439
+ if (this.sources.length === 0) return { candidates: [], degradations: [] };
440
+ const timeoutAbort = new AbortController();
441
+ const signal = request.signal
442
+ ? AbortSignal.any([request.signal, timeoutAbort.signal])
443
+ : timeoutAbort.signal;
444
+ const query: ActionGateCandidateQuery = {
445
+ sessionKey: request.sessionKey,
446
+ fingerprint,
447
+ actionTerms: actionTermsFor(request.intent),
448
+ strategyId: request.strategyId,
449
+ intent: request.intent,
450
+ codingContext: request.codingContext,
451
+ memoryDir: request.memoryDir,
452
+ namespace: request.namespace,
453
+ causalTrajectoryStoreDir: request.causalTrajectoryStoreDir,
454
+ signal,
455
+ };
456
+ const remaining = Math.max(1, deadline - this.clock());
457
+ let onTimeout!: (value: ActionGateSourceResult) => void;
458
+ const timeoutResult = new Promise<ActionGateSourceResult>((resolve) => {
459
+ onTimeout = resolve;
460
+ });
461
+ const timer = setTimeout(() => {
462
+ timeoutAbort.abort();
463
+ onTimeout({ ok: false, reason: `action gate timed out after ${this.config.timeoutMs}ms` });
464
+ }, remaining);
465
+ try {
466
+ const settled = await Promise.all(
467
+ this.sources.map(async (source) => {
468
+ let result: ActionGateSourceResult;
469
+ try {
470
+ result = await Promise.race([source.find(query), timeoutResult]);
471
+ } catch (error) {
472
+ result = { ok: false, reason: error instanceof Error ? error.message : String(error) };
473
+ }
474
+ return { source, result };
475
+ })
476
+ );
477
+ const candidates: Array<{ sourceId: string; candidate: ActionGateCandidate }> = [];
478
+ const degradations: string[] = [];
479
+ for (const { source, result } of settled) {
480
+ if (!result.ok) {
481
+ degradations.push(`${source.id}: ${result.reason}`);
482
+ continue;
483
+ }
484
+ for (const candidate of result.candidates) candidates.push({ sourceId: source.id, candidate });
485
+ }
486
+ candidates.sort((left, right) => {
487
+ const byScore = (right.candidate.score ?? 0) - (left.candidate.score ?? 0);
488
+ if (byScore !== 0) return byScore;
489
+ const bySource = left.sourceId.localeCompare(right.sourceId);
490
+ return bySource !== 0 ? bySource : left.candidate.memoryId.localeCompare(right.candidate.memoryId);
491
+ });
492
+ return { candidates, degradations };
493
+ } finally {
494
+ clearTimeout(timer);
495
+ }
496
+ }
497
+
498
+ private finish(
499
+ request: ActionGateRequest,
500
+ startedAt: number,
501
+ decision: Omit<ActionGateDecision, "latencyMs">
502
+ ): ActionGateDecision {
503
+ const latencyMs = Math.max(0, this.clock() - startedAt);
504
+ const result: ActionGateDecision = { ...decision, latencyMs };
505
+ if (this.audit) {
506
+ const record: ActionGateAuditRecord = {
507
+ ts: this.now().toISOString(),
508
+ sessionKey: request.sessionKey,
509
+ turnId: request.turnId,
510
+ status: result.status,
511
+ latencyMs,
512
+ fingerprint: result.fingerprint,
513
+ deliveredIds: result.advisories.map((advisory) => advisory.memoryId),
514
+ suppressedIds: result.suppressedIds,
515
+ degradations: result.degradations,
516
+ reason: result.reason,
517
+ };
518
+ // Telemetry must never break the action it observes.
519
+ void Promise.resolve()
520
+ .then(() => this.audit?.(record))
521
+ .catch(() => undefined);
522
+ }
523
+ return result;
524
+ }
525
+
526
+ private sessionState(sessionKey: string, turnId: string | undefined): SessionSuppression {
527
+ let state = this.sessions.get(sessionKey);
528
+ if (!state) {
529
+ if (this.sessions.size >= MAX_TRACKED_SESSIONS) {
530
+ const oldest = this.sessions.keys().next().value;
531
+ if (oldest !== undefined) this.sessions.delete(oldest);
532
+ }
533
+ state = {
534
+ turnId: turnId ?? "",
535
+ deliveredThisTurn: new Set(),
536
+ deliveredByGate: new Set(),
537
+ deliveredByTurnStart: new Set(),
538
+ };
539
+ this.sessions.set(sessionKey, state);
540
+ }
541
+ if (turnId !== undefined && state.turnId !== turnId) {
542
+ state.turnId = turnId;
543
+ state.deliveredThisTurn.clear();
544
+ }
545
+ return state;
546
+ }
547
+
548
+ private addTracked(set: Set<string>, value: string): void {
549
+ if (set.has(value)) return;
550
+ if (set.size >= MAX_TRACKED_IDS_PER_SESSION) {
551
+ const oldest = set.values().next().value;
552
+ if (oldest !== undefined) set.delete(oldest);
553
+ }
554
+ set.add(value);
555
+ }
556
+ }
package/src/config.ts CHANGED
@@ -43,6 +43,7 @@ import { parseExtractionLivenessConfig } from "./extraction-liveness.js";
43
43
  import { parseReplicaPeersConfig } from "./replica-peers-config.js";
44
44
  import { parseDependencyPropagationConfig } from "./dependency-propagation-config.js";
45
45
  import { parseProceduralMaintenanceConfig } from "./procedural/maintenance-config.js";
46
+ import { parseDriftDetectionConfig } from "./preferences/drift-config.js";
46
47
  import {
47
48
  parseContradictionLocalizationConfig,
48
49
  parseContradictionScanConfig,
@@ -68,6 +69,7 @@ import { parseLocationConfig } from "./location/config.js";
68
69
  import { parseOkfConfig } from "./okf/config.js";
69
70
  import { parseBoundedJsonlStateConfig } from "./bounded-jsonl-state.js";
70
71
  import { parseCodingKnowledgeConfig } from "./coding/coding-knowledge-config.js";
72
+ import { parseActionGateConfig } from "./coding/action-gate.js";
71
73
  import { parseChatConfig } from "./chat/chat-config.js";
72
74
  import { parseSupportPassportConfig } from "./support-passport/config.js";
73
75
  import { parseCorrectionIntentConfig, parseFaithfulnessGateConfig } from "./faithfulness-config.js";
@@ -1789,6 +1791,8 @@ export function parseConfig(
1789
1791
  contradictionAutoResolve: cfg.contradictionAutoResolve !== false,
1790
1792
  // Contradiction Scan cron (issue #520)
1791
1793
  contradictionScan: parseContradictionScanConfig(cfg.contradictionScan),
1794
+ // Preference drift detection (issue #2371)
1795
+ driftDetection: parseDriftDetectionConfig(cfg.driftDetection),
1792
1796
  dependencyPropagation: parseDependencyPropagationConfig(cfg),
1793
1797
  // Temporal Supersession (issue #375)
1794
1798
  temporalSupersessionEnabled: cfg.temporalSupersessionEnabled !== false, // On by default
@@ -2329,6 +2333,7 @@ export function parseConfig(
2329
2333
  bridgeMode: cfg.bridgeMode === undefined ? "embedded" : String(cfg.bridgeMode),
2330
2334
  codexCompat,
2331
2335
  codingKnowledge: parseCodingKnowledgeConfig(cfg.codingKnowledge),
2336
+ actionGate: parseActionGateConfig(cfg.actionGate),
2332
2337
  chat: parseChatConfig(cfg.chat),
2333
2338
  supportPassport: parseSupportPassportConfig(cfg.supportPassport),
2334
2339
  hourlySummariesEnabled: cfg.hourlySummariesEnabled !== false, // default: true
@@ -17,9 +17,31 @@ import type { ContradictionVerdict } from "./contradiction-judge.js";
17
17
 
18
18
  // ── Types ──────────────────────────────────────────────────────────────────────
19
19
 
20
- export type ResolutionVerb = "keep-a" | "keep-b" | "merge" | "both-valid" | "needs-more-context";
21
-
22
- const VALID_RESOLUTION_VERBS: readonly ResolutionVerb[] = [
20
+ /**
21
+ * Resolution verbs. The first five are contradiction-pair verbs; the last
22
+ * three resolve a `kind: "preference-drift"` item (issue #2371). Verb validity
23
+ * is per-kind — `executeResolution` rejects a cross-kind verb — but the stored
24
+ * union is shared so one review queue, one `review_resolve` surface, and one
25
+ * resolution record serve both item kinds.
26
+ */
27
+ export type ResolutionVerb =
28
+ | "keep-a"
29
+ | "keep-b"
30
+ | "merge"
31
+ | "both-valid"
32
+ | "needs-more-context"
33
+ | "keep"
34
+ | "supersede"
35
+ | "archive";
36
+
37
+ /** Review-item kind. Absent on every pair written before issue #2371. */
38
+ export type ReviewItemKind = "contradiction" | "preference-drift";
39
+
40
+ /** Verbs valid for a `kind: "preference-drift"` item. */
41
+ export const PREFERENCE_DRIFT_VERBS: readonly ResolutionVerb[] = ["keep", "supersede", "archive"];
42
+
43
+ /** Verbs valid for a contradiction pair. */
44
+ export const CONTRADICTION_VERBS: readonly ResolutionVerb[] = [
23
45
  "keep-a",
24
46
  "keep-b",
25
47
  "merge",
@@ -27,11 +49,23 @@ const VALID_RESOLUTION_VERBS: readonly ResolutionVerb[] = [
27
49
  "needs-more-context",
28
50
  ];
29
51
 
52
+ const VALID_RESOLUTION_VERBS: readonly ResolutionVerb[] = [
53
+ ...CONTRADICTION_VERBS,
54
+ ...PREFERENCE_DRIFT_VERBS,
55
+ ];
56
+
30
57
  export interface ContradictionPair {
31
58
  /** Deterministic pair ID: sha256(sorted(memoryIdA, memoryIdB) plus namespace when scoped). */
32
59
  pairId: string;
33
60
  /** Memory IDs (sorted). */
34
61
  memoryIds: [string, string];
62
+ /**
63
+ * Item kind (issue #2371). Absent on every pair written before drift
64
+ * detection existed, which reads as `"contradiction"`. A
65
+ * `"preference-drift"` item pairs the aging preference (first id) with the
66
+ * recent evidence that pointed away from it (second id).
67
+ */
68
+ kind?: ReviewItemKind;
35
69
  /** Judge verdict. */
36
70
  verdict: ContradictionVerdict;
37
71
  /** Judge rationale. */
@@ -85,8 +119,20 @@ export function isDefaultReviewNamespace(
85
119
  return !requested || requested === defaultNamespace || resolvedNamespace === defaultNamespace;
86
120
  }
87
121
 
122
+ const TERMINAL_RESOLUTIONS: Partial<Record<ResolutionVerb, true>> = {
123
+ "keep-a": true,
124
+ "keep-b": true,
125
+ merge: true,
126
+ // Every preference-drift verb is terminal: the user has answered the "is
127
+ // this preference still true?" question, so the item must leave the
128
+ // unresolved queue and never be re-proposed by a later scan (issue #2371).
129
+ keep: true,
130
+ supersede: true,
131
+ archive: true,
132
+ };
133
+
88
134
  function isTerminalResolution(resolution: ResolutionVerb | undefined): boolean {
89
- return resolution === "keep-a" || resolution === "keep-b" || resolution === "merge";
135
+ return resolution !== undefined && TERMINAL_RESOLUTIONS[resolution] === true;
90
136
  }
91
137
 
92
138
  function preservesDirectResolution(resolution: ResolutionVerb | undefined): boolean {
@@ -380,8 +380,16 @@ function normalizeScanNamespace(namespace: string | undefined): string | undefin
380
380
  return trimmed ? trimmed : undefined;
381
381
  }
382
382
 
383
- async function resolveScanStorage(
384
- deps: ScanDependencies,
383
+ /**
384
+ * Resolve the namespace-scoped storage a scan must read and write through.
385
+ *
386
+ * Exported and narrowed to the fields it actually reads so sibling scans
387
+ * (preference drift, issue #2371) share ONE namespace resolver rather than
388
+ * re-deriving the read/write namespace independently — the divergence the
389
+ * namespace/ACL scenario matrix in AGENTS.md exists to prevent.
390
+ */
391
+ export async function resolveScanStorage(
392
+ deps: Pick<ScanDependencies, "config" | "storage" | "storageForNamespace">,
385
393
  namespace: string | undefined,
386
394
  ): Promise<ScanStorageResolution> {
387
395
  if (!resolveNamespaceCapabilities(deps.config).namespaces) {
@@ -427,7 +435,10 @@ function isStorageManagerLike(value: unknown): value is StorageManager {
427
435
  return typeof candidate.readAllMemories === "function";
428
436
  }
429
437
 
430
- function fallbackResolvedNamespace(deps: ScanDependencies, namespace: string | undefined): string | undefined {
438
+ function fallbackResolvedNamespace(
439
+ deps: Pick<ScanDependencies, "config">,
440
+ namespace: string | undefined,
441
+ ): string | undefined {
431
442
  if (namespace) return namespace;
432
443
  return resolveNamespaceCapabilities(deps.config).namespaces ? deps.config.defaultNamespace : undefined;
433
444
  }