@remnic/core 9.3.719 → 9.3.721

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 (387) hide show
  1. package/dist/access-boundary.d.ts +10 -7
  2. package/dist/access-boundary.js +37 -34
  3. package/dist/access-cli.js +71 -66
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +11 -8
  6. package/dist/access-http.js +44 -41
  7. package/dist/access-mcp.d.ts +11 -8
  8. package/dist/access-mcp.js +40 -37
  9. package/dist/access-operations-batch.js +38 -35
  10. package/dist/access-operations.d.ts +11 -8
  11. package/dist/access-operations.js +39 -36
  12. package/dist/access-schema.d.ts +19 -19
  13. package/dist/access-schema.js +3 -3
  14. package/dist/{access-service-CZbnt0t2.d.ts → access-service-Dj3I-VBS.d.ts} +3 -3
  15. package/dist/access-service.d.ts +11 -8
  16. package/dist/access-service.js +36 -33
  17. package/dist/access-surface-catalog.d.ts +10 -7
  18. package/dist/action-confidence.d.ts +1 -1
  19. package/dist/active-memory-bridge.d.ts +1 -1
  20. package/dist/active-recall.d.ts +1 -1
  21. package/dist/active-recall.js +4 -2
  22. package/dist/active-recall.js.map +1 -1
  23. package/dist/adapters/index.js +7 -7
  24. package/dist/adapters/registry.js +3 -3
  25. package/dist/{auto-sync-24NNS3GU.js → auto-sync-F3KCSQFJ.js} +5 -5
  26. package/dist/behavior-learner.d.ts +1 -1
  27. package/dist/behavior-signals.d.ts +1 -1
  28. package/dist/bootstrap.d.ts +11 -8
  29. package/dist/briefing.d.ts +1 -1
  30. package/dist/briefing.js +12 -10
  31. package/dist/buffer-surprise-report.d.ts +1 -1
  32. package/dist/buffer.d.ts +1 -1
  33. package/dist/calibration.d.ts +1 -1
  34. package/dist/calibration.js +2 -2
  35. package/dist/capabilities.d.ts +30 -2
  36. package/dist/capabilities.js +3 -1
  37. package/dist/{catalog-CEYlMs9X.d.ts → catalog-F-OEVbRQ.d.ts} +1 -1
  38. package/dist/causal-behavior.d.ts +1 -1
  39. package/dist/causal-behavior.js +2 -2
  40. package/dist/causal-chain.js +2 -2
  41. package/dist/causal-consolidation.d.ts +1 -1
  42. package/dist/causal-consolidation.js +15 -13
  43. package/dist/causal-consolidation.js.map +1 -1
  44. package/dist/causal-retrieval.js +2 -2
  45. package/dist/causal-trajectory-graph.d.ts +4 -0
  46. package/dist/causal-trajectory-graph.js +2 -2
  47. package/dist/causal-trajectory.js +1 -1
  48. package/dist/{chunk-TH7WMHVK.js → chunk-3NN73CM7.js} +2 -2
  49. package/dist/{chunk-A5TUPZ3N.js → chunk-4SF232E6.js} +60 -30
  50. package/dist/chunk-4SF232E6.js.map +1 -0
  51. package/dist/{chunk-DGTDRIUO.js → chunk-4T3BMHKD.js} +2 -2
  52. package/dist/{chunk-EH27LHML.js → chunk-5O42UKXI.js} +2 -2
  53. package/dist/{chunk-KRGC4EDX.js → chunk-6FAMMBQ3.js} +2 -2
  54. package/dist/{chunk-FEB7W5JG.js → chunk-6OIDDKAA.js} +27 -6
  55. package/dist/chunk-6OIDDKAA.js.map +1 -0
  56. package/dist/{chunk-67HG7KFE.js → chunk-6YKIYQU7.js} +2 -2
  57. package/dist/{chunk-LXSG4L55.js → chunk-7CTNUVHZ.js} +2 -2
  58. package/dist/{chunk-UCJ5CDTK.js → chunk-A5XNBK2D.js} +22 -14
  59. package/dist/chunk-A5XNBK2D.js.map +1 -0
  60. package/dist/{chunk-7EEVXW7X.js → chunk-A7TJZLPK.js} +2 -2
  61. package/dist/{chunk-P5IPFZVH.js → chunk-B7WISIAJ.js} +5 -5
  62. package/dist/chunk-BCDKBDSY.js +173 -0
  63. package/dist/chunk-BCDKBDSY.js.map +1 -0
  64. package/dist/{chunk-JAKYZGV4.js → chunk-BXEOHV7K.js} +3 -3
  65. package/dist/chunk-C4X4QGIO.js +60 -0
  66. package/dist/chunk-C4X4QGIO.js.map +1 -0
  67. package/dist/{chunk-S3OPGGZB.js → chunk-D7EBTQ24.js} +2 -2
  68. package/dist/{chunk-IO5NQEGZ.js → chunk-DKT3X5C3.js} +2 -2
  69. package/dist/{chunk-PBH4JUAB.js → chunk-DVRHLN2O.js} +18 -3
  70. package/dist/chunk-DVRHLN2O.js.map +1 -0
  71. package/dist/{chunk-S35FX6MU.js → chunk-EBDA5TYA.js} +2 -2
  72. package/dist/{chunk-2PTBLS2S.js → chunk-EDOQM4E4.js} +2 -2
  73. package/dist/{chunk-YTZLBBEB.js → chunk-EEJMD2L2.js} +3 -3
  74. package/dist/{chunk-YGKUAX2B.js → chunk-EHQ5HO5T.js} +11 -11
  75. package/dist/chunk-EHQ5HO5T.js.map +1 -0
  76. package/dist/{chunk-JIZGYQYH.js → chunk-EM2LDXJ2.js} +4 -4
  77. package/dist/{chunk-YYOK4GVF.js → chunk-EVPNWN7Z.js} +10 -10
  78. package/dist/{chunk-HLIR3RJ7.js → chunk-EXUCUPV2.js} +2 -2
  79. package/dist/{chunk-F3PVXKHP.js → chunk-GDUOCHCD.js} +27 -21
  80. package/dist/chunk-GDUOCHCD.js.map +1 -0
  81. package/dist/{chunk-L5ASZA2F.js → chunk-GJSL4WG2.js} +17 -2
  82. package/dist/chunk-GJSL4WG2.js.map +1 -0
  83. package/dist/{chunk-2SXMCQEQ.js → chunk-GZQXOQQ2.js} +3 -3
  84. package/dist/{chunk-2KAYTPPT.js → chunk-HCVWF657.js} +12 -12
  85. package/dist/{chunk-N3DUH6OO.js → chunk-ILFSEIRT.js} +1 -1
  86. package/dist/chunk-ILFSEIRT.js.map +1 -0
  87. package/dist/{chunk-4KZGLDJJ.js → chunk-J3JPB7ZI.js} +2 -2
  88. package/dist/{chunk-3JJWNZTT.js → chunk-L2B3EE7B.js} +102 -42
  89. package/dist/chunk-L2B3EE7B.js.map +1 -0
  90. package/dist/{chunk-NFRBADNR.js → chunk-LA3HZKES.js} +610 -369
  91. package/dist/chunk-LA3HZKES.js.map +1 -0
  92. package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
  93. package/dist/{chunk-URM3HW2R.js → chunk-LLUGAZ3B.js} +2 -2
  94. package/dist/{chunk-RVLB4E6N.js → chunk-MDBZTH7N.js} +5 -32
  95. package/dist/chunk-MDBZTH7N.js.map +1 -0
  96. package/dist/{chunk-U5H7WHTS.js → chunk-N77ELJ2D.js} +4 -4
  97. package/dist/{chunk-2VHMVWPU.js → chunk-NXRUUDUF.js} +2 -2
  98. package/dist/{chunk-DIFU33GU.js → chunk-OHTOPFRF.js} +2 -2
  99. package/dist/{chunk-74MYJSKB.js → chunk-ORAK3LNZ.js} +2 -2
  100. package/dist/{chunk-REWQAFSA.js → chunk-PD42EYQN.js} +2 -2
  101. package/dist/chunk-PHK3HARR.js +32 -0
  102. package/dist/chunk-PHK3HARR.js.map +1 -0
  103. package/dist/{chunk-773DPI6T.js → chunk-PKMQK2WY.js} +4 -4
  104. package/dist/{chunk-72VDBALG.js → chunk-QD6QP5P6.js} +63 -54
  105. package/dist/chunk-QD6QP5P6.js.map +1 -0
  106. package/dist/{chunk-CTOQEZSN.js → chunk-QR67FU5G.js} +2 -2
  107. package/dist/{chunk-AU7Q3LSC.js → chunk-RKNJBZ55.js} +7 -7
  108. package/dist/{chunk-YI7RZO7Y.js → chunk-SQNED75R.js} +1 -1
  109. package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
  110. package/dist/{chunk-IF362TF6.js → chunk-TQT3USCG.js} +2 -2
  111. package/dist/chunk-TQT3USCG.js.map +1 -0
  112. package/dist/{chunk-5RHM43BJ.js → chunk-TXQCG6SE.js} +3 -3
  113. package/dist/{chunk-ANI5DUAE.js → chunk-UIXXE6NI.js} +7 -7
  114. package/dist/{chunk-2COL35PK.js → chunk-UO5ZHIIH.js} +2 -2
  115. package/dist/{chunk-OJU73BUX.js → chunk-VCLGUIFM.js} +2 -2
  116. package/dist/{chunk-5J4OL7TJ.js → chunk-VUA4S7P6.js} +4 -4
  117. package/dist/{chunk-A66YRSOS.js → chunk-WQYSDPMA.js} +2 -2
  118. package/dist/chunk-XGTBNVIU.js +79 -0
  119. package/dist/chunk-XGTBNVIU.js.map +1 -0
  120. package/dist/{chunk-OZ2KQ7HF.js → chunk-YEE5KJQQ.js} +2 -2
  121. package/dist/chunk-YN2UN7N3.js +217 -0
  122. package/dist/chunk-YN2UN7N3.js.map +1 -0
  123. package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
  124. package/dist/{chunk-APUJIMDM.js → chunk-Z4RU5VPM.js} +2 -2
  125. package/dist/{chunk-DCWIQFNA.js → chunk-ZUDM75KG.js} +4 -4
  126. package/dist/{cli-1orcdXOY.d.ts → cli-P_GaYcCY.d.ts} +3 -3
  127. package/dist/cli.d.ts +9 -6
  128. package/dist/cli.js +67 -63
  129. package/dist/compounding/engine.d.ts +1 -1
  130. package/dist/compounding/engine.js +12 -10
  131. package/dist/compounding/preference-consolidator.d.ts +1 -1
  132. package/dist/compression-optimizer.d.ts +1 -1
  133. package/dist/config.d.ts +1 -1
  134. package/dist/config.js +4 -2
  135. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  136. package/dist/connectors/codex-materialize-runner.js +12 -10
  137. package/dist/connectors/codex-materialize.d.ts +1 -1
  138. package/dist/connectors/index.d.ts +1 -1
  139. package/dist/connectors/index.js +13 -11
  140. package/dist/consolidation-provenance-check.d.ts +1 -1
  141. package/dist/consolidation-undo.d.ts +1 -1
  142. package/dist/contradiction/index.d.ts +1 -1
  143. package/dist/conversation-index/backend.d.ts +1 -1
  144. package/dist/conversation-index/backend.js +2 -2
  145. package/dist/conversation-index/chunker.d.ts +1 -1
  146. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  147. package/dist/conversation-index/indexer.d.ts +1 -1
  148. package/dist/conversation-index/search.d.ts +1 -1
  149. package/dist/day-summary.d.ts +1 -1
  150. package/dist/delinearize.d.ts +1 -1
  151. package/dist/direct-answer-wiring.d.ts +1 -1
  152. package/dist/direct-answer.d.ts +1 -1
  153. package/dist/embedding-fallback.d.ts +1 -1
  154. package/dist/enrichment/index.d.ts +1 -1
  155. package/dist/entity-retrieval.d.ts +1 -1
  156. package/dist/entity-retrieval.js +12 -10
  157. package/dist/entity-schema.d.ts +1 -1
  158. package/dist/explicit-capture.d.ts +11 -8
  159. package/dist/extraction-faithfulness.d.ts +8 -2
  160. package/dist/extraction-faithfulness.js +4 -3
  161. package/dist/extraction-judge-telemetry.d.ts +1 -1
  162. package/dist/extraction-judge-training.d.ts +1 -1
  163. package/dist/extraction-judge.d.ts +1 -1
  164. package/dist/extraction-judge.js +3 -3
  165. package/dist/extraction.d.ts +1 -1
  166. package/dist/extraction.js +5 -4
  167. package/dist/faithfulness-config.d.ts +55 -0
  168. package/dist/faithfulness-config.js +11 -0
  169. package/dist/faithfulness-config.js.map +1 -0
  170. package/dist/fallback-llm.d.ts +1 -1
  171. package/dist/fallback-llm.js +2 -2
  172. package/dist/{forget-U66KPDPY.js → forget-OEE5OELK.js} +2 -2
  173. package/dist/graph-dashboard-diff.d.ts +4 -0
  174. package/dist/graph-dashboard-key.d.ts +4 -0
  175. package/dist/graph-dashboard-parser.d.ts +4 -0
  176. package/dist/{graph-edge-decay-JLOF3YW4.js → graph-edge-decay-QPRJQ7DL.js} +3 -3
  177. package/dist/graph-edge-reinforcement.d.ts +4 -0
  178. package/dist/graph-snapshot.d.ts +7 -3
  179. package/dist/graph-snapshot.js +3 -3
  180. package/dist/graph.d.ts +7 -1
  181. package/dist/graph.js +2 -2
  182. package/dist/identity-continuity.d.ts +1 -1
  183. package/dist/importance.d.ts +1 -1
  184. package/dist/index.d.ts +571 -570
  185. package/dist/index.js +104 -99
  186. package/dist/index.js.map +1 -1
  187. package/dist/intent.d.ts +1 -1
  188. package/dist/lcm/engine.d.ts +1 -1
  189. package/dist/lcm/engine.js +2 -2
  190. package/dist/lcm/index.d.ts +1 -1
  191. package/dist/lcm/index.js +4 -4
  192. package/dist/lcm/tools.d.ts +1 -1
  193. package/dist/lifecycle.d.ts +1 -1
  194. package/dist/live-connectors-runner.d.ts +1 -1
  195. package/dist/local-llm.d.ts +1 -1
  196. package/dist/local-model-endpoint.d.ts +96 -0
  197. package/dist/local-model-endpoint.js +12 -0
  198. package/dist/local-model-endpoint.js.map +1 -0
  199. package/dist/maintenance/memory-governance.d.ts +1 -1
  200. package/dist/maintenance/memory-governance.js +12 -10
  201. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -10
  202. package/dist/maintenance/rebuild-memory-projection.js +13 -11
  203. package/dist/mcp-memory-inspector-app.d.ts +11 -8
  204. package/dist/memory-action-policy.d.ts +1 -1
  205. package/dist/memory-cache.d.ts +1 -1
  206. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  207. package/dist/memory-projection-store.d.ts +1 -1
  208. package/dist/memory-provenance.d.ts +1 -1
  209. package/dist/memory-worth-outcomes.d.ts +1 -1
  210. package/dist/models-json.d.ts +1 -1
  211. package/dist/namespaces/migrate.d.ts +2 -2
  212. package/dist/namespaces/migrate.js +22 -20
  213. package/dist/namespaces/principal.d.ts +1 -1
  214. package/dist/namespaces/search.d.ts +1 -1
  215. package/dist/namespaces/search.js +9 -9
  216. package/dist/namespaces/storage.d.ts +2 -2
  217. package/dist/namespaces/storage.js +12 -10
  218. package/dist/native-knowledge.d.ts +1 -1
  219. package/dist/operator-toolkit.d.ts +3 -2
  220. package/dist/operator-toolkit.js +32 -28
  221. package/dist/orchestration/maintenance.d.ts +2 -2
  222. package/dist/orchestration/maintenance.js +14 -12
  223. package/dist/{orchestrator-kOxredSr.d.ts → orchestrator-f5xv19kp.d.ts} +178 -81
  224. package/dist/orchestrator.d.ts +8 -5
  225. package/dist/orchestrator.js +56 -52
  226. package/dist/patterns-cli.d.ts +1 -1
  227. package/dist/policy-runtime.d.ts +1 -1
  228. package/dist/provenance.d.ts +1 -1
  229. package/dist/provenance.js +2 -1
  230. package/dist/qmd-recall-cache.d.ts +1 -1
  231. package/dist/qmd.d.ts +1 -1
  232. package/dist/recall-disclosure-escalation.d.ts +1 -1
  233. package/dist/recall-explain-renderer.d.ts +1 -1
  234. package/dist/recall-explain-renderer.js +3 -3
  235. package/dist/recall-planner-llm.d.ts +1 -1
  236. package/dist/recall-planner-llm.js +2 -2
  237. package/dist/recall-state.d.ts +1 -1
  238. package/dist/recall-tag-filter.d.ts +1 -1
  239. package/dist/recall-xray-cli.d.ts +1 -1
  240. package/dist/recall-xray-cli.js +4 -4
  241. package/dist/recall-xray-renderer.d.ts +1 -1
  242. package/dist/recall-xray-renderer.js +3 -3
  243. package/dist/recall-xray.d.ts +18 -1
  244. package/dist/recall-xray.js +2 -2
  245. package/dist/resolve-auth-token.d.ts +1 -1
  246. package/dist/resume-bundles.js +5 -3
  247. package/dist/retrieval-agents.d.ts +1 -1
  248. package/dist/retrieval-tiers.d.ts +1 -1
  249. package/dist/routing/engine.d.ts +1 -1
  250. package/dist/routing/store.d.ts +1 -1
  251. package/dist/search/embed-helper.d.ts +1 -1
  252. package/dist/search/factory.d.ts +1 -1
  253. package/dist/search/factory.js +8 -8
  254. package/dist/search/index.d.ts +1 -1
  255. package/dist/search/index.js +14 -14
  256. package/dist/search/lancedb-backend.d.ts +1 -1
  257. package/dist/search/lancedb-backend.js +2 -2
  258. package/dist/search/meilisearch-backend.d.ts +1 -1
  259. package/dist/search/meilisearch-backend.js +2 -2
  260. package/dist/search/noop-backend.d.ts +1 -1
  261. package/dist/search/orama-backend.d.ts +1 -1
  262. package/dist/search/orama-backend.js +2 -2
  263. package/dist/search/port.d.ts +1 -1
  264. package/dist/search/remote-backend.d.ts +1 -1
  265. package/dist/{semantic-consolidation-B58v3S-a.d.ts → semantic-consolidation-BQm1e1Pn.d.ts} +1 -1
  266. package/dist/semantic-consolidation.d.ts +2 -2
  267. package/dist/semantic-consolidation.js +13 -11
  268. package/dist/semantic-rule-promotion.js +12 -10
  269. package/dist/semantic-rule-verifier.d.ts +1 -1
  270. package/dist/semantic-rule-verifier.js +12 -10
  271. package/dist/session-observer-bands.d.ts +1 -1
  272. package/dist/session-observer-state.d.ts +1 -1
  273. package/dist/shared-context/manager.d.ts +1 -1
  274. package/dist/signal.d.ts +1 -1
  275. package/dist/storage.d.ts +45 -1
  276. package/dist/storage.js +11 -9
  277. package/dist/summarizer.d.ts +1 -1
  278. package/dist/summarizer.js +3 -3
  279. package/dist/summary-snapshot.d.ts +1 -1
  280. package/dist/temporal-supersession.d.ts +1 -1
  281. package/dist/temporal-validity.d.ts +1 -1
  282. package/dist/threading.d.ts +1 -1
  283. package/dist/tier-migration.d.ts +1 -1
  284. package/dist/tier-routing.d.ts +1 -1
  285. package/dist/topics.d.ts +1 -1
  286. package/dist/transcript.d.ts +1 -1
  287. package/dist/transfer/backup.js +2 -2
  288. package/dist/transfer/capsule-export.js +2 -2
  289. package/dist/transfer/capsule-import.js +1 -1
  290. package/dist/trust-score-stage.d.ts +178 -0
  291. package/dist/trust-score-stage.js +18 -0
  292. package/dist/trust-score-stage.js.map +1 -0
  293. package/dist/trust-score.d.ts +164 -0
  294. package/dist/trust-score.js +22 -0
  295. package/dist/trust-score.js.map +1 -0
  296. package/dist/{types-ACmDOScJ.d.ts → types-DwO1Ot4n.d.ts} +56 -2
  297. package/dist/types.d.ts +1 -1
  298. package/dist/types.js +1 -1
  299. package/dist/utility-runtime.d.ts +1 -1
  300. package/dist/verified-recall.js +12 -10
  301. package/package.json +2 -2
  302. package/src/access-service.ts +8 -5
  303. package/src/capabilities-cluster-b.test.ts +78 -0
  304. package/src/capabilities.test.ts +11 -1
  305. package/src/capabilities.ts +58 -0
  306. package/src/cli/research-status-commands.ts +7 -3
  307. package/src/cli.ts +11 -6
  308. package/src/config.ts +60 -30
  309. package/src/extraction-faithfulness.test.ts +175 -0
  310. package/src/extraction-faithfulness.ts +53 -1
  311. package/src/faithfulness-config.test.ts +96 -0
  312. package/src/faithfulness-config.ts +138 -0
  313. package/src/graph-snapshot.ts +3 -3
  314. package/src/graph.ts +8 -7
  315. package/src/lifecycle/tombstones.test.ts +242 -1
  316. package/src/lifecycle/tombstones.ts +157 -70
  317. package/src/local-model-endpoint.test.ts +155 -0
  318. package/src/local-model-endpoint.ts +189 -0
  319. package/src/operator-toolkit.ts +24 -7
  320. package/src/orchestration/tier-migration-coordinator.ts +286 -0
  321. package/src/orchestrator.ts +331 -222
  322. package/src/recall-xray.test.ts +62 -0
  323. package/src/recall-xray.ts +34 -0
  324. package/src/trust-score-recall-paths.test.ts +157 -0
  325. package/src/trust-score-stage.test.ts +350 -0
  326. package/src/trust-score-stage.ts +398 -0
  327. package/src/trust-score.test.ts +282 -0
  328. package/src/trust-score.ts +449 -0
  329. package/src/types.ts +61 -1
  330. package/dist/chunk-3JJWNZTT.js.map +0 -1
  331. package/dist/chunk-72VDBALG.js.map +0 -1
  332. package/dist/chunk-A5TUPZ3N.js.map +0 -1
  333. package/dist/chunk-F3PVXKHP.js.map +0 -1
  334. package/dist/chunk-FEB7W5JG.js.map +0 -1
  335. package/dist/chunk-IF362TF6.js.map +0 -1
  336. package/dist/chunk-L5ASZA2F.js.map +0 -1
  337. package/dist/chunk-N3DUH6OO.js.map +0 -1
  338. package/dist/chunk-NFRBADNR.js.map +0 -1
  339. package/dist/chunk-PBH4JUAB.js.map +0 -1
  340. package/dist/chunk-RVLB4E6N.js.map +0 -1
  341. package/dist/chunk-UCJ5CDTK.js.map +0 -1
  342. package/dist/chunk-YGKUAX2B.js.map +0 -1
  343. /package/dist/{auto-sync-24NNS3GU.js.map → auto-sync-F3KCSQFJ.js.map} +0 -0
  344. /package/dist/{chunk-TH7WMHVK.js.map → chunk-3NN73CM7.js.map} +0 -0
  345. /package/dist/{chunk-DGTDRIUO.js.map → chunk-4T3BMHKD.js.map} +0 -0
  346. /package/dist/{chunk-EH27LHML.js.map → chunk-5O42UKXI.js.map} +0 -0
  347. /package/dist/{chunk-KRGC4EDX.js.map → chunk-6FAMMBQ3.js.map} +0 -0
  348. /package/dist/{chunk-67HG7KFE.js.map → chunk-6YKIYQU7.js.map} +0 -0
  349. /package/dist/{chunk-LXSG4L55.js.map → chunk-7CTNUVHZ.js.map} +0 -0
  350. /package/dist/{chunk-7EEVXW7X.js.map → chunk-A7TJZLPK.js.map} +0 -0
  351. /package/dist/{chunk-P5IPFZVH.js.map → chunk-B7WISIAJ.js.map} +0 -0
  352. /package/dist/{chunk-JAKYZGV4.js.map → chunk-BXEOHV7K.js.map} +0 -0
  353. /package/dist/{chunk-S3OPGGZB.js.map → chunk-D7EBTQ24.js.map} +0 -0
  354. /package/dist/{chunk-IO5NQEGZ.js.map → chunk-DKT3X5C3.js.map} +0 -0
  355. /package/dist/{chunk-S35FX6MU.js.map → chunk-EBDA5TYA.js.map} +0 -0
  356. /package/dist/{chunk-2PTBLS2S.js.map → chunk-EDOQM4E4.js.map} +0 -0
  357. /package/dist/{chunk-YTZLBBEB.js.map → chunk-EEJMD2L2.js.map} +0 -0
  358. /package/dist/{chunk-JIZGYQYH.js.map → chunk-EM2LDXJ2.js.map} +0 -0
  359. /package/dist/{chunk-YYOK4GVF.js.map → chunk-EVPNWN7Z.js.map} +0 -0
  360. /package/dist/{chunk-HLIR3RJ7.js.map → chunk-EXUCUPV2.js.map} +0 -0
  361. /package/dist/{chunk-2SXMCQEQ.js.map → chunk-GZQXOQQ2.js.map} +0 -0
  362. /package/dist/{chunk-2KAYTPPT.js.map → chunk-HCVWF657.js.map} +0 -0
  363. /package/dist/{chunk-4KZGLDJJ.js.map → chunk-J3JPB7ZI.js.map} +0 -0
  364. /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
  365. /package/dist/{chunk-URM3HW2R.js.map → chunk-LLUGAZ3B.js.map} +0 -0
  366. /package/dist/{chunk-U5H7WHTS.js.map → chunk-N77ELJ2D.js.map} +0 -0
  367. /package/dist/{chunk-2VHMVWPU.js.map → chunk-NXRUUDUF.js.map} +0 -0
  368. /package/dist/{chunk-DIFU33GU.js.map → chunk-OHTOPFRF.js.map} +0 -0
  369. /package/dist/{chunk-74MYJSKB.js.map → chunk-ORAK3LNZ.js.map} +0 -0
  370. /package/dist/{chunk-REWQAFSA.js.map → chunk-PD42EYQN.js.map} +0 -0
  371. /package/dist/{chunk-773DPI6T.js.map → chunk-PKMQK2WY.js.map} +0 -0
  372. /package/dist/{chunk-CTOQEZSN.js.map → chunk-QR67FU5G.js.map} +0 -0
  373. /package/dist/{chunk-AU7Q3LSC.js.map → chunk-RKNJBZ55.js.map} +0 -0
  374. /package/dist/{chunk-YI7RZO7Y.js.map → chunk-SQNED75R.js.map} +0 -0
  375. /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
  376. /package/dist/{chunk-5RHM43BJ.js.map → chunk-TXQCG6SE.js.map} +0 -0
  377. /package/dist/{chunk-ANI5DUAE.js.map → chunk-UIXXE6NI.js.map} +0 -0
  378. /package/dist/{chunk-2COL35PK.js.map → chunk-UO5ZHIIH.js.map} +0 -0
  379. /package/dist/{chunk-OJU73BUX.js.map → chunk-VCLGUIFM.js.map} +0 -0
  380. /package/dist/{chunk-5J4OL7TJ.js.map → chunk-VUA4S7P6.js.map} +0 -0
  381. /package/dist/{chunk-A66YRSOS.js.map → chunk-WQYSDPMA.js.map} +0 -0
  382. /package/dist/{chunk-OZ2KQ7HF.js.map → chunk-YEE5KJQQ.js.map} +0 -0
  383. /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
  384. /package/dist/{chunk-APUJIMDM.js.map → chunk-Z4RU5VPM.js.map} +0 -0
  385. /package/dist/{chunk-DCWIQFNA.js.map → chunk-ZUDM75KG.js.map} +0 -0
  386. /package/dist/{forget-U66KPDPY.js.map → forget-OEE5OELK.js.map} +0 -0
  387. /package/dist/{graph-edge-decay-JLOF3YW4.js.map → graph-edge-decay-QPRJQ7DL.js.map} +0 -0
@@ -0,0 +1,449 @@
1
+ /**
2
+ * trust-score.ts — unified TrustScore pure module (issue #1577 PR 1).
3
+ *
4
+ * Combines the trust-relevant signals Remnic already computes — memory worth
5
+ * (Laplace-smoothed outcome probability + recency decay), provenance strength,
6
+ * faithfulness verdict, corroboration count, contradiction status, and optional
7
+ * domain calibration — into ONE deterministic score in `[0, 1]` plus a band
8
+ * (`high` / `medium` / `low` / `quarantine`) and a fully-explained component
9
+ * breakdown for X-ray.
10
+ *
11
+ * Contract rules (mirror the battle-tested `memory-worth.ts` "corrupt inputs
12
+ * fail safely" header — read it before changing anything here):
13
+ *
14
+ * 1. **No signals → neutral.** An empty `TrustSignals` yields score `0.5`,
15
+ * band `medium`, and a recall multiplier of exactly `1.0`. Uninstrumented
16
+ * memories are NEVER penalized (rule 39 — byte-identical ranking when the
17
+ * feature is off; neutral prior when on but a memory has no data).
18
+ * 2. **Corrupt / out-of-range component → that component collapses to
19
+ * neutral**, never to an extreme. A `NaN` weight or a `corroborationCount`
20
+ * of `-3` is treated as "no signal", not as damning evidence. Precedent:
21
+ * `memory-worth.ts` `classifyCounter`.
22
+ * 3. **Deterministic.** Same inputs → identical result. All weights come from
23
+ * the caller (config); no wall clock, no RNG, no hidden constants.
24
+ * 4. **Explainable.** Every active component is echoed in the result with its
25
+ * normalized weight and contribution, so X-ray can render the basis
26
+ * without re-deriving the math.
27
+ * 5. **`quarantine` is reserved for hard negatives** — `faithfulness:
28
+ * "contradicted"` or `contradiction: "pending_review"`. Quarantined items
29
+ * are excluded from injection but MUST stay visible in X-ray with the
30
+ * reason (rule 34 — exclusion must never look like "no result").
31
+ *
32
+ * This module is deliberately framework-free: it takes already-collected
33
+ * signals and config weights, returns a score. The signal ADAPTERS (reading
34
+ * frontmatter / counters / review queues) live in `trust-score-stage.ts` so
35
+ * this file stays pure and trivially property-testable.
36
+ */
37
+
38
+ /**
39
+ * Per-component weights. Defined canonically in `types.ts` (the PluginConfig
40
+ * home) and re-exported here so callers of the pure scorer don't need a second
41
+ * import path. Every field is optional and defaults to
42
+ * `DEFAULT_TRUST_WEIGHTS`; weights are sum-normalized at score time, so only
43
+ * their relative magnitudes matter (rule 51: invalid weight rejected at config
44
+ * parse, here we only defend against runtime corruption).
45
+ */
46
+ export type { TrustWeights } from "./types.js";
47
+ import type { TrustWeights } from "./types.js";
48
+
49
+ /**
50
+ * Default per-component weights. Chosen so that, with all signals at neutral,
51
+ * the score is exactly `0.5`. Sum-normalized at compute time. These are the
52
+ * documented defaults; operators override via `trustScore.weights.*` config.
53
+ */
54
+ export const DEFAULT_TRUST_WEIGHTS: Required<TrustWeights> = {
55
+ memoryWorth: 0.28,
56
+ provenance: 0.16,
57
+ faithfulness: 0.20,
58
+ corroboration: 0.12,
59
+ contradiction: 0.10,
60
+ domainCalibration: 0.04,
61
+ feedback: 0.05,
62
+ recency: 0.05,
63
+ };
64
+
65
+ /**
66
+ * All trust-relevant signals for ONE memory. Every field is optional — the
67
+ * scorer degrades to neutral for any absent or corrupt signal (rule 34).
68
+ */
69
+ export interface TrustSignals {
70
+ /** Memory-worth result (Laplace success prob + confidence). */
71
+ memoryWorth?: { score: number; confidence: number };
72
+ /** Thumbs feedback tallies. */
73
+ feedback?: { up: number; down: number };
74
+ /** Contradiction review queue status. */
75
+ contradiction?: "none" | "pending_review" | "resolved_kept" | "resolved_superseded";
76
+ /** Claim-level provenance strength (#1575). */
77
+ provenance?: "verified" | "unverified" | "none";
78
+ /** Faithfulness gate verdict (#1576). */
79
+ faithfulness?: "entailed" | "contradicted" | "unsupported" | "unchecked";
80
+ /** Count of distinct source turns/sessions corroborating the fact. */
81
+ corroborationCount?: number;
82
+ /** Belief-ledger accuracy for the memory's domain, `0..1`. */
83
+ domainCalibration?: number;
84
+ /** Age in days vs a per-category half-life (caller computes). */
85
+ ageDays?: number;
86
+ /** Half-life (days) for recency decay; the caller supplies the per-category value. */
87
+ recencyHalfLifeDays?: number;
88
+ }
89
+
90
+ /** Trust band, coarse-grained for injection hedging and X-ray grouping. */
91
+ export type TrustBand = "high" | "medium" | "low" | "quarantine";
92
+
93
+ /**
94
+ * One component's contribution to the final score, for full explainability.
95
+ * `value` is the normalized `[0,1]` signal contribution; `weight` is the
96
+ * sum-normalized weight that was applied.
97
+ */
98
+ export interface TrustScoreComponent {
99
+ value: number;
100
+ weight: number;
101
+ }
102
+
103
+ /**
104
+ * Full TrustScore result. `score` is the weighted blend in `[0,1]`; `band` is
105
+ * the coarse category; `components` echoes every active component so X-ray /
106
+ * epistemic rendering can explain the basis without re-deriving it.
107
+ */
108
+ export interface TrustScoreResult {
109
+ score: number;
110
+ band: TrustBand;
111
+ components: Record<string, TrustScoreComponent>;
112
+ /**
113
+ * `true` when the result is the neutral prior (no usable signals). Lets the
114
+ * multiplier short-circuit to exactly `1.0` and the epistemic renderer skip
115
+ * the hedge for the common uninstrumented case.
116
+ */
117
+ neutral: boolean;
118
+ }
119
+
120
+ /** Band thresholds on the blended score. */
121
+ export const TRUST_BAND_THRESHOLDS = {
122
+ high: 0.7,
123
+ medium: 0.45,
124
+ low: 0.2,
125
+ } as const;
126
+
127
+ /** Neutral prior: the score an empty-signal memory receives. */
128
+ export const NEUTRAL_TRUST_SCORE = 0.5;
129
+
130
+ /**
131
+ * Validate a single weight: a finite number in `[0, 1]`. Returns the value or
132
+ * `undefined` when corrupt (the caller falls back to the default).
133
+ */
134
+ function sanitizeWeight(value: number | undefined): number | undefined {
135
+ if (value === undefined) return undefined;
136
+ if (typeof value !== "number") return undefined;
137
+ if (!Number.isFinite(value)) return undefined;
138
+ if (value < 0 || value > 1) return undefined;
139
+ return value;
140
+ }
141
+
142
+ /**
143
+ * Merge caller weights over the defaults, dropping corrupt entries. Exposed for
144
+ * config parse so an invalid weight is rejected once at the boundary (rule 51
145
+ * spirit) — but the scorer defends again here so a hand-built bench fixture
146
+ * cannot poison the math.
147
+ */
148
+ export function resolveTrustWeights(
149
+ override: Readonly<TrustWeights> | undefined,
150
+ ): Required<TrustWeights> {
151
+ const out: Required<TrustWeights> = { ...DEFAULT_TRUST_WEIGHTS };
152
+ if (!override) return out;
153
+ (Object.keys(DEFAULT_TRUST_WEIGHTS) as Array<keyof TrustWeights>).forEach((key) => {
154
+ const v = sanitizeWeight(override[key]);
155
+ if (v !== undefined) out[key] = v;
156
+ });
157
+ return out;
158
+ }
159
+
160
+ /** Clamp a value to `[0,1]`; NaN/Infinity collapse to the neutral `0.5`. */
161
+ function clampUnit(value: number): number {
162
+ if (typeof value !== "number" || !Number.isFinite(value)) return NEUTRAL_TRUST_SCORE;
163
+ if (value < 0) return 0;
164
+ if (value > 1) return 1;
165
+ return value;
166
+ }
167
+
168
+ /**
169
+ * Coerce an already-`[0,1]` signal (domain calibration, etc.) for the scorer.
170
+ * Returns `undefined` when absent or corrupt so the component is DROPPED — a
171
+ * single corrupt signal must not be read as damning evidence nor as a neutral
172
+ * contribution that masquerades as "we measured this". Mirrors the
173
+ * `classifyCounter` precedent from `memory-worth.ts`.
174
+ */
175
+ function unitValue(value: number | undefined): number | undefined {
176
+ if (value === undefined) return undefined;
177
+ if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
178
+ if (value < 0 || value > 1) return undefined;
179
+ return value;
180
+ }
181
+
182
+ /** Map a provenance tag to a `[0,1]` contribution. Absent → neutral. */
183
+ function provenanceValue(tag: TrustSignals["provenance"]): number | undefined {
184
+ if (tag === undefined) return undefined;
185
+ if (tag === "verified") return 1;
186
+ if (tag === "unverified") return 0.5;
187
+ // "none" is meaningful negative evidence (no surviving source span), not
188
+ // neutral. It pulls toward 0 but gently so a single unprovenanced fact is
189
+ // not damned on its own.
190
+ if (tag === "none") return 0.25;
191
+ return undefined;
192
+ }
193
+
194
+ /** Map a faithfulness verdict to a `[0,1]` contribution. Absent → dropped. */
195
+ function faithfulnessValue(verdict: TrustSignals["faithfulness"]): number | undefined {
196
+ if (verdict === undefined) return undefined;
197
+ if (verdict === "entailed") return 1;
198
+ // "unchecked" means the gate ran but could not verify — it carries NO signal.
199
+ // Return undefined (absent) so a memory whose ONLY signal is unchecked
200
+ // faithfulness scores truly neutral, not "active 0.5". This stops the
201
+ // epistemic hedge from spuriously firing on unverifiable memories (review
202
+ // Oqg_0: unchecked-as-absent for hedging) and stops the component from
203
+ // masquerading as measured evidence.
204
+ if (verdict === "unchecked") return undefined;
205
+ if (verdict === "unsupported") return 0.35;
206
+ // "contradicted" is a hard negative — it also forces quarantine (see below),
207
+ // but as a component it contributes 0 so the blended score sinks.
208
+ if (verdict === "contradicted") return 0;
209
+ return undefined;
210
+ }
211
+
212
+ /** Map contradiction review status to a `[0,1]` contribution. */
213
+ function contradictionValue(
214
+ status: TrustSignals["contradiction"],
215
+ ): number | undefined {
216
+ if (status === undefined) return undefined;
217
+ if (status === "none") return NEUTRAL_TRUST_SCORE;
218
+ if (status === "resolved_kept") return 0.7;
219
+ if (status === "resolved_superseded") return 0.1;
220
+ if (status === "pending_review") return 0.15;
221
+ return undefined;
222
+ }
223
+
224
+ /** Feedback balance → `[0,1]` via Laplace smoothing (mirrors memory-worth). */
225
+ function feedbackValue(fb: TrustSignals["feedback"]): number | undefined {
226
+ if (fb === undefined) return undefined;
227
+ const up = typeof fb.up === "number" && Number.isFinite(fb.up) && fb.up >= 0 ? fb.up : 0;
228
+ const down =
229
+ typeof fb.down === "number" && Number.isFinite(fb.down) && fb.down >= 0 ? fb.down : 0;
230
+ if (up === 0 && down === 0) return undefined;
231
+ // Laplace: (up+1)/(up+down+2). Clamped defensively.
232
+ return clampUnit((up + 1) / (up + down + 2));
233
+ }
234
+
235
+ /** Corroboration count → `[0,1]` via log-saturation (3+ sources ≈ 1.0). */
236
+ function corroborationValue(count: TrustSignals["corroborationCount"]): number | undefined {
237
+ if (count === undefined) return undefined;
238
+ if (typeof count !== "number" || !Number.isFinite(count) || count < 0) return undefined;
239
+ if (count === 0) return 0.3; // single mention is mild negative evidence vs neutral
240
+ // log2(1 + count) saturates: 1→0.5, 2→0.63, 3→0.66, 4→0.68 ... capped at 1.
241
+ return clampUnit(Math.log2(1 + count) / 2);
242
+ }
243
+
244
+ /** Recency decay vs a half-life, mapped so "fresh" → high, "stale" → low. */
245
+ function recencyValue(signals: TrustSignals): number | undefined {
246
+ if (signals.ageDays === undefined) return undefined;
247
+ if (typeof signals.ageDays !== "number" || !Number.isFinite(signals.ageDays) || signals.ageDays < 0)
248
+ return undefined;
249
+ const halfLife = signals.recencyHalfLifeDays;
250
+ if (typeof halfLife !== "number" || !Number.isFinite(halfLife) || halfLife <= 0) return undefined;
251
+ // 2^(-age/halfLife): age 0 → 1, age=halfLife → 0.5, age≫halfLife → ~0.
252
+ return clampUnit(Math.pow(2, -signals.ageDays / halfLife));
253
+ }
254
+
255
+ /**
256
+ * Determine whether the signals warrant the `quarantine` band: a hard negative
257
+ * that excludes the item from injection (but never from X-ray — rule 34).
258
+ */
259
+ function isHardNegative(signals: TrustSignals): boolean {
260
+ if (signals.faithfulness === "contradicted") return true;
261
+ if (signals.contradiction === "pending_review") return true;
262
+ return false;
263
+ }
264
+
265
+ /**
266
+ * Compute the unified TrustScore for one memory.
267
+ *
268
+ * Returns the neutral prior (`0.5`, band `medium`, `neutral: true`) when no
269
+ * usable signal is present, so uninstrumented memories are untouched.
270
+ */
271
+ export function computeTrustScore(
272
+ signals: TrustSignals = {},
273
+ weights: Readonly<TrustWeights> = DEFAULT_TRUST_WEIGHTS,
274
+ ): TrustScoreResult {
275
+ const resolved = resolveTrustWeights(weights);
276
+
277
+ // Build the (component → value) map, skipping absent/corrupt signals.
278
+ // Each value is already clamped to [0,1] by its mapper.
279
+ const candidates: Array<{ key: keyof TrustWeights; value: number }> = [];
280
+ const push = (key: keyof TrustWeights, value: number | undefined) => {
281
+ if (value === undefined) return;
282
+ candidates.push({ key, value: clampUnit(value) });
283
+ };
284
+
285
+ push("memoryWorth", signals.memoryWorth ? clampUnit(signals.memoryWorth.score) : undefined);
286
+ push("provenance", provenanceValue(signals.provenance));
287
+ push("faithfulness", faithfulnessValue(signals.faithfulness));
288
+ push("corroboration", corroborationValue(signals.corroborationCount));
289
+ push("contradiction", contradictionValue(signals.contradiction));
290
+ push("domainCalibration", unitValue(signals.domainCalibration));
291
+ push("feedback", feedbackValue(signals.feedback));
292
+ push("recency", recencyValue(signals));
293
+
294
+ // No usable signal → neutral prior, multiplier exactly 1.0.
295
+ if (candidates.length === 0) {
296
+ return {
297
+ score: NEUTRAL_TRUST_SCORE,
298
+ band: "medium",
299
+ components: {},
300
+ neutral: true,
301
+ };
302
+ }
303
+
304
+ // Sum-normalize the active weights so absent components redistribute their
305
+ // weight to the present ones (a memory with only memory-worth data is scored
306
+ // purely on memory-worth, not half-scored because other signals are absent).
307
+ const activeWeightSum = candidates.reduce((acc, c) => acc + resolved[c.key], 0);
308
+ // When every present signal's weight is 0 (operator explicitly disabled
309
+ // the only components the memory has), fall back to the neutral prior
310
+ // rather than damning the memory to score 0. A disabled signal is not a
311
+ // negative signal — it carries no information (review P2: zero-weight).
312
+ if (activeWeightSum <= 0) {
313
+ return {
314
+ score: NEUTRAL_TRUST_SCORE,
315
+ band: "medium",
316
+ components: {},
317
+ neutral: true,
318
+ };
319
+ }
320
+ const safeWeightSum = activeWeightSum;
321
+
322
+ let blended = 0;
323
+ const components: Record<string, TrustScoreComponent> = {};
324
+ for (const c of candidates) {
325
+ const w = resolved[c.key] / safeWeightSum;
326
+ blended += c.value * w;
327
+ components[c.key] = { value: c.value, weight: w };
328
+ }
329
+
330
+ // Clamp the blend: floating-point noise can drift a hair outside [0,1].
331
+ const score = clampUnit(blended);
332
+
333
+ // Hard negatives are quarantined regardless of the blended score, so a
334
+ // corroborated-but-contradicted fact is still excluded from injection.
335
+ const band = bandForScore(score, isHardNegative(signals));
336
+
337
+ return { score, band, components, neutral: false };
338
+ }
339
+
340
+ /**
341
+ * Resolve the band from the blended score, accounting for hard-negative
342
+ * quarantine. Extracted so the thresholds live in ONE place (X-ray, injection,
343
+ * and tests all agree on the mapping).
344
+ */
345
+ export function bandForScore(score: number, hardNegative: boolean): TrustBand {
346
+ if (hardNegative) return "quarantine";
347
+ const s = clampUnit(score);
348
+ if (s >= TRUST_BAND_THRESHOLDS.high) return "high";
349
+ if (s >= TRUST_BAND_THRESHOLDS.medium) return "medium";
350
+ if (s >= TRUST_BAND_THRESHOLDS.low) return "low";
351
+ return "low";
352
+ }
353
+
354
+ /**
355
+ * Map a `[0,1]` trust score to a recall multiplier in
356
+ * `[minMultiplier, maxMultiplier]`. The neutral prior (`0.5`) maps to exactly
357
+ * `1.0` so uninstrumented memories are untouched. Linear interpolation centered
358
+ * at neutral:
359
+ *
360
+ * score < 0.5 → multiplier in [min, 1)
361
+ * score = 0.5 → 1.0
362
+ * score > 0.5 → multiplier in (1, max]
363
+ */
364
+ export function trustMultiplier(
365
+ score: number,
366
+ minMultiplier: number = 0.5,
367
+ maxMultiplier: number = 1.25,
368
+ ): number {
369
+ const min = typeof minMultiplier === "number" && Number.isFinite(minMultiplier) ? minMultiplier : 0.5;
370
+ const max = typeof maxMultiplier === "number" && Number.isFinite(maxMultiplier) ? maxMultiplier : 1.25;
371
+ // Defend against an inverted contract (min > max): pick the tighter bound.
372
+ const lo = Math.min(min, max);
373
+ const hi = Math.max(min, max);
374
+ const s = clampUnit(score);
375
+ if (s === NEUTRAL_TRUST_SCORE) return 1;
376
+ if (s < NEUTRAL_TRUST_SCORE) {
377
+ // Map [0, 0.5) → [lo, 1). score 0 → lo, score →0.5 → 1.
378
+ const t = s / NEUTRAL_TRUST_SCORE; // [0,1)
379
+ return lo + (1 - lo) * t;
380
+ }
381
+ // Map (0.5, 1] → (1, hi]. score 0.5 → 1, score 1 → hi.
382
+ const t = (s - NEUTRAL_TRUST_SCORE) / (1 - NEUTRAL_TRUST_SCORE); // (0,1]
383
+ return 1 + (hi - 1) * t;
384
+ }
385
+
386
+ /**
387
+ * Deterministic epistemic hedge suffix for an injected memory line, generated
388
+ * from the trust components (template, not LLM). Returns the empty string for
389
+ * the `high` band and for neutral results so the common case wastes no tokens.
390
+ *
391
+ * Examples:
392
+ * high → ""
393
+ * medium → "(unconfirmed — single mention, 2025-11)"
394
+ * low → "(low confidence — contradicted once, uncorroborated)"
395
+ */
396
+ export function renderEpistemicHedge(result: TrustScoreResult): string {
397
+ if (result.neutral) return "";
398
+ switch (result.band) {
399
+ case "high":
400
+ return "";
401
+ case "quarantine":
402
+ case "low":
403
+ return `(low confidence — ${describeWeakness(result.components)})`;
404
+ case "medium":
405
+ return `(unconfirmed — ${describeWeakness(result.components)})`;
406
+ default:
407
+ return "";
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Build a short, deterministic weakness description from the components. Reads
413
+ * the lowest-contributing signals so the hedge names the actual reason the
414
+ * model should hedge. Pure and order-stable.
415
+ */
416
+ function describeWeakness(components: Record<string, TrustScoreComponent>): string {
417
+ const parts: string[] = [];
418
+ const f = components.faithfulness;
419
+ if (f && f.value <= 0.35) parts.push(f.value === 0 ? "contradicted" : "unsupported");
420
+ const c = components.contradiction;
421
+ if (c && c.value <= 0.2) parts.push("under review");
422
+ const cor = components.corroboration;
423
+ if (cor && cor.value <= 0.4) parts.push("single mention");
424
+ const prov = components.provenance;
425
+ if (prov && prov.value <= 0.3) parts.push("unprovenanced");
426
+ const rec = components.recency;
427
+ if (rec && rec.value <= 0.3) parts.push("stale");
428
+ if (parts.length === 0) {
429
+ // No single component is weak but the blend is below high — name the
430
+ // weakest one explicitly so the hedge is never vacuous.
431
+ let weakest: { key: string; value: number } | null = null;
432
+ for (const [key, comp] of Object.entries(components)) {
433
+ if (!weakest || comp.value < weakest.value) weakest = { key, value: comp.value };
434
+ }
435
+ if (weakest) parts.push(`weak ${humanizeComponentName(weakest.key)}`);
436
+ }
437
+ return parts.slice(0, 2).join(", ");
438
+ }
439
+
440
+ function humanizeComponentName(key: string): string {
441
+ switch (key) {
442
+ case "memoryWorth":
443
+ return "outcome history";
444
+ case "domainCalibration":
445
+ return "domain calibration";
446
+ default:
447
+ return key;
448
+ }
449
+ }
package/src/types.ts CHANGED
@@ -993,6 +993,31 @@ export interface PluginConfig {
993
993
  * once the benchmark shows precision tie-or-win.
994
994
  */
995
995
  recallMemoryWorthFilterEnabled: boolean;
996
+ // Unified TrustScore recall stage (issue #1577). Combines memory-worth,
997
+ // provenance, faithfulness, corroboration, contradiction, feedback, recency,
998
+ // and optional domain calibration into one [0,1] trust score applied as a
999
+ // bounded recall multiplier. Subsumes the Memory Worth multiplier when on
1000
+ // (the orchestrator runs exactly one of the two — rule 39).
1001
+ /**
1002
+ * Master gate for the unified TrustScore recall stage. Default `false`: off
1003
+ * = the stage is absent and ranking is byte-identical to the pre-feature
1004
+ * pipeline (rule 39). Flip via #1574 ablation evidence only.
1005
+ */
1006
+ trustScoreEnabled: boolean;
1007
+ /** Append epistemic hedge suffixes to injected memory lines. Separate gate. */
1008
+ trustScoreEpistemicRendering: boolean;
1009
+ /**
1010
+ * Exclude hard-negative (quarantine-band) items from injection. Default
1011
+ * `true`; only effective under the master gate. Quarantined items stay
1012
+ * visible in X-ray with a reason (rule 34).
1013
+ */
1014
+ trustScoreQuarantine: boolean;
1015
+ /** Lower bound of the trust multiplier (default 0.5). */
1016
+ trustScoreMinMultiplier: number;
1017
+ /** Upper bound of the trust multiplier (default 1.25). */
1018
+ trustScoreMaxMultiplier: number;
1019
+ /** Per-component weights for the TrustScore blend; invalid weights rejected at parse. */
1020
+ trustScoreWeights: TrustWeights;
996
1021
  /**
997
1022
  * Recall-audit anomaly detector (issue #565 PR 5/5). When true,
998
1023
  * access surfaces run the anomaly detector over a tail of the audit
@@ -1182,12 +1207,30 @@ export interface PluginConfig {
1182
1207
  // extracted facts against their verified source spans from #1575.
1183
1208
  /** Gate mode: "off" (default, byte-identical pre-feature), "shadow" (record only), "enforce" (route to pending_review). */
1184
1209
  extractionFaithfulnessGate: "off" | "shadow" | "enforce";
1185
- /** Override model/endpoint; empty string = default routing chain. */
1210
+ /** Override model name; empty string = default routing chain. */
1186
1211
  extractionFaithfulnessModel: string;
1212
+ /**
1213
+ * Base URL of a local openai-compatible endpoint serving a fine-tuned
1214
+ * faithfulness gate (issue #1585 model-lab), e.g.
1215
+ * `http://localhost:11434/v1` (Ollama) or `http://localhost:8000/v1` (vLLM).
1216
+ * Empty string (default) preserves the existing routing chain exactly;
1217
+ * set together with `extractionFaithfulnessModel` to route the gate to the
1218
+ * local model first, falling back to the chain on failure.
1219
+ */
1220
+ extractionFaithfulnessBaseUrl: string;
1187
1221
  /** Context window (chars) around the quote sent to the verifier. Default 400. */
1188
1222
  extractionFaithfulnessContextChars: number;
1189
1223
  /** Per-batch timeout in ms; timeout → tagged error, never blocks writes. Default 8000. */
1190
1224
  extractionFaithfulnessTimeoutMs: number;
1225
+ /**
1226
+ * Correction-intent model-lab pointer (issue #1581 / #1585). When both this
1227
+ * and `correctionIntentBaseUrl` are set, the detection path can route to a
1228
+ * local fine-tuned correction-intent classifier. Empty default keeps the
1229
+ * rule-based passive-correction detector as the sole path (byte-identical).
1230
+ */
1231
+ correctionIntentModel: string;
1232
+ /** Base URL of a local openai-compatible endpoint serving the correction-intent classifier (issue #1585). Empty default = rule-based detection. */
1233
+ correctionIntentBaseUrl: string;
1191
1234
  // Hourly summaries
1192
1235
  hourlySummariesEnabled: boolean;
1193
1236
  daySummaryEnabled: boolean;
@@ -2785,6 +2828,23 @@ export interface FaithfulnessFrontmatter {
2785
2828
  at?: string;
2786
2829
  }
2787
2830
 
2831
+ /**
2832
+ * Per-component weights for the unified TrustScore blend (issue #1577). Every
2833
+ * field is optional and defaults to the documented value in `trust-score.ts`;
2834
+ * weights are sum-normalized at score time so only relative magnitudes matter.
2835
+ * Invalid weights (non-finite, outside `[0,1]`) are rejected at config parse.
2836
+ */
2837
+ export interface TrustWeights {
2838
+ memoryWorth?: number;
2839
+ provenance?: number;
2840
+ faithfulness?: number;
2841
+ corroboration?: number;
2842
+ contradiction?: number;
2843
+ domainCalibration?: number;
2844
+ feedback?: number;
2845
+ recency?: number;
2846
+ }
2847
+
2788
2848
  /** Memory link relationship types */
2789
2849
  export type MemoryLinkType = "follows" | "references" | "contradicts" | "supports" | "related";
2790
2850
 
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lifecycle/tombstones.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// lifecycle/tombstones.ts — Tombstone store + non-resurrection invariant\n// (issue #1579)\n//\n// A \"tombstone\" records that a fact has been retired (corrected, superseded,\n// or retracted) so that the SAME fact cannot silently come back to life through\n// any of the five resurrection paths:\n//\n// 1. Re-extraction (session replay, re-observation)\n// 2. Importers (capsule / import-* payloads)\n// 3. Consolidation merges\n// 4. Dreams (REM re-derivation)\n// 5. Pattern reinforcement (duplicate promotion)\n//\n// The invariant is enforced at the SINGLE storage persist path\n// (`StorageManager.writeMemory` — the same chokepoint that records catalog\n// writes, issue #1522). Every write path funnels through it, so paths (a)–(e)\n// are blocked WITHOUT per-path code.\n//\n// File-first / rebuildable (the repo's storage philosophy): the JSONL at\n// `<stateDir>/tombstones.jsonl` is a cache of truth. The authoritative sources\n// are the memory files themselves (status: superseded/retracted) and the\n// `corrections/` records. `rebuildTombstonesFromStorage` reconstructs the\n// JSONL from those sources.\n//\n// Design rules honored (issue #1579 design section):\n// - Hash `rawContent` exactly as the dedup index does — one helper\n// (`ContentHashIndex.computeHash` / `normalizeContent`), never a second\n// (checklist §13; rule 23).\n// - Append-only log — never rewrite history; a later `kind: \"revocation\"`\n// entry re-allows (rule 25).\n// - Instance-scoped, never module-level (rule 11).\n// - Namespace-scoped — a tombstone in namespace A never blocks namespace B\n// (rule 42).\n// - Serialized appends with rejection recovery (rule 40).\n// - Never silent drop — a blocked write lands as pending_review + blockedBy\n// (rule 34).\n// - Do NOT register a blocked fact as an active dedup/index entry (rule 44).\n// - Off = pre-feature behavior for rollback safety (rule 30).\n// - Semantic tier (4) ships dark, off by default (rule 48).\n// ---------------------------------------------------------------------------\n\nimport { serializeMutations } from \"../utils/serialize-mutations.js\";\n\n/** Why a tombstone was emitted. */\nexport type TombstoneReason =\n | \"correction\"\n | \"supersession\"\n | \"retraction\"\n | \"contradiction_resolution\";\n\n/** Who emitted the tombstone. */\nexport type TombstoneCreatedBy =\n | \"user_correction\"\n | \"contradiction_resolution\"\n | \"supersession\"\n | \"chat\";\n\n/** Tier that matched on lookup. */\nexport type TombstoneMatchTier = \"exact\" | \"normalized\" | \"keyed\" | \"semantic\";\n\n/**\n * A single append-only tombstone log entry.\n *\n * `kind: \"tombstone\"` blocks; `kind: \"revocation\"` re-allows. The log is\n * never rewritten — the NEWEST matching entry wins at lookup (rule 25).\n */\nexport interface TombstoneEntry {\n /** Stable tombstone id (`tomb-<ts>-<rand>`). */\n id: string;\n kind: \"tombstone\" | \"revocation\";\n reason: TombstoneReason;\n /** The memory that was retired. */\n sourceMemoryId: string;\n /** sha256 of the retired memory's rawContent (rule 23). */\n contentHash: string;\n /** `ContentHashIndex.normalizeContent(rawContent)` — the pre-hash form. */\n normalizedText: string;\n entityRef?: string;\n /** Structured-attribute supersession key when one existed. */\n supersessionKey?: string;\n /** Namespace scope (rule 42). */\n namespace: string;\n createdAt: string;\n createdBy: TombstoneCreatedBy;\n /** For `kind: \"revocation\"`: the tombstone id being revoked. */\n revokes?: string;\n}\n\n/** A positive block decision returned by `TombstoneStore.lookup`. */\nexport interface TombstoneMatch {\n tombstoneId: string;\n matchedTier: TombstoneMatchTier;\n reason: TombstoneReason;\n}\n\n/** Inputs to a lookup. At least one discriminator must be present.\n *\n * Issue #1579 thread Ociag/Oci-W: `supersessionKeys` (plural) lets the write\n * chokepoint check EVERY derived key, not just the first. Emitters register\n * one tombstone per matched key (temporal-supersession, rebuild), so a block\n * can live on any later key; querying only `supersessionKeys[0]` missed it\n * and the retired fact resurrected as active. `supersessionKey` (singular)\n * remains for direct/unit callers; `lookup` checks the union of both. */\nexport interface TombstoneLookupQuery {\n contentHash?: string;\n normalizedText?: string;\n entityRef?: string;\n /** Single supersession key (direct/unit callers). */\n supersessionKey?: string;\n /** All derived supersession keys (write chokepoint). The keyed tier is\n * checked for each; the first active match wins. */\n supersessionKeys?: string[];\n namespace: string;\n}\n\n/** Configuration for a tombstone store instance. */\nexport interface TombstoneStoreOptions {\n enabled: boolean;\n semanticMatch: boolean;\n semanticThreshold: number;\n /** sha256 of raw content — wired to `ContentHashIndex.computeHash`. */\n hashContent: (raw: string) => string;\n /** Normalize raw content — wired to `ContentHashIndex.normalizeContent`. */\n normalizeText: (raw: string) => string;\n /**\n * Optional cosine similarity in [0, 1] for the semantic tier. When\n * undefined or when `semanticMatch` is false, the semantic tier is\n * skipped entirely.\n */\n semanticSimilarity?: (a: string, b: string) => number;\n}\n\n/** Injected file I/O — the StorageManager wires its secure-store-aware\n * implementations so tombstones are encrypted at rest alongside other state.\n * `stat` is optional; when provided the store tracks the file mtime itself and\n * runs a cross-process staleness probe on each access (#1579). */\nexport interface TombstoneFileIo {\n read: (filePath: string) => Promise<string>;\n append: (filePath: string, content: string) => Promise<void>;\n write: (filePath: string, content: string) => Promise<void>;\n stat?: (filePath: string) => { mtimeMs: number };\n}\n\n/** Aggregate stats for `remnic doctor`. */\nexport interface TombstoneStats {\n count: number;\n revoked: number;\n lastAppendAt: string | null;\n corruptedLines: number;\n /** Whether the in-memory index matches the on-disk file (rebuild check). */\n loaded: boolean;\n}\n\nconst TOMBSTONE_PREFIX = \"tomb\";\n\nfunction newTombstoneId(): string {\n return `${TOMBSTONE_PREFIX}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/** Deterministic key for the keyed tier (namespace + entityRef + supersessionKey).\n *\n * The namespace is part of the discriminator so two namespace-scoped stores\n * that share the same backing `tombstones.jsonl` (namespaces disabled, or the\n * same directory used with different namespace configs) cannot overwrite each\n * other's index entries — a later tombstone for namespace B with the same\n * entity/key no longer evicts namespace A's entry (issue #1579 thread Ocs-O). */\nfunction keyedTierKey(namespace: string, entityRef: string, supersessionKey: string): string {\n return `${namespace}\\0${entityRef}\\0${supersessionKey}`;\n}\n\n/**\n * Parse one JSONL line into a TombstoneEntry, validating the discriminated\n * union. Returns `null` for malformed lines (rule 34 — skip with a counter,\n * never crash).\n */\nexport function parseTombstoneLine(line: string): TombstoneEntry | null {\n const trimmed = line.trim();\n if (trimmed.length === 0) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== \"object\") return null;\n const e = parsed as Record<string, unknown>;\n if (typeof e.id !== \"string\" || e.id.length === 0) return null;\n if (e.kind !== \"tombstone\" && e.kind !== \"revocation\") return null;\n if (\n e.reason !== \"correction\" &&\n e.reason !== \"supersession\" &&\n e.reason !== \"retraction\" &&\n e.reason !== \"contradiction_resolution\"\n ) {\n return null;\n }\n if (typeof e.sourceMemoryId !== \"string\") return null;\n if (typeof e.contentHash !== \"string\") return null;\n if (typeof e.normalizedText !== \"string\") return null;\n if (typeof e.namespace !== \"string\") return null;\n if (typeof e.createdAt !== \"string\") return null;\n if (\n e.createdBy !== \"user_correction\" &&\n e.createdBy !== \"contradiction_resolution\" &&\n e.createdBy !== \"supersession\" &&\n e.createdBy !== \"chat\"\n ) {\n return null;\n }\n const out: TombstoneEntry = {\n id: e.id,\n kind: e.kind,\n reason: e.reason,\n sourceMemoryId: e.sourceMemoryId,\n contentHash: e.contentHash,\n normalizedText: e.normalizedText,\n namespace: e.namespace,\n createdAt: e.createdAt,\n createdBy: e.createdBy,\n };\n if (typeof e.entityRef === \"string\") out.entityRef = e.entityRef;\n if (typeof e.supersessionKey === \"string\") out.supersessionKey = e.supersessionKey;\n if (typeof e.revokes === \"string\") out.revokes = e.revokes;\n return out;\n}\n\n/**\n * The instance-scoped tombstone store. One per StorageManager (rule 11),\n * namespace-scoped via `namespace` + the on-disk path (rule 42).\n *\n * The in-memory index is built lazily on first access and invalidated on\n * append + by the StorageManager's cache-invalidation chokepoint (rule 25).\n */\nexport class TombstoneStore {\n private entries: TombstoneEntry[] = [];\n /** contentHash → tombstone id (newest) — exact tier. */\n private readonly byHash = new Map<string, string>();\n /** normalizedText → tombstone id (newest) — normalized tier. */\n private readonly byNormalized = new Map<string, string>();\n /** entityRef\\0supersessionKey → tombstone id (newest) — keyed tier. */\n private readonly byKey = new Map<string, string>();\n /** Tombstone ids that have a later revocation entry. */\n private readonly revokedIds = new Set<string>();\n /** id → entry index (newest only, for revocation lookups). */\n private readonly byId = new Map<string, TombstoneEntry>();\n private loaded = false;\n private loadPromise: Promise<void> | null = null;\n private corruptedLines = 0;\n private lastAppendAt: string | null = null;\n /**\n * Last-seen mtime of the JSONL file (cross-process staleness probe, #1579).\n * Tracked inside the store so the StorageManager wiring stays thin. When the\n * file's mtime advances past this value between accesses, the in-memory index\n * is stale (a peer process appended) and is reloaded before the next lookup.\n */\n private fileMtimeMs = 0;\n\n constructor(\n private readonly filePath: string,\n private readonly namespace: string,\n private readonly options: TombstoneStoreOptions,\n private readonly io: TombstoneFileIo,\n ) {}\n\n /** Lazy load + build the in-memory index. Idempotent. */\n async load(): Promise<void> {\n if (this.loaded) return;\n if (this.loadPromise) return this.loadPromise;\n this.loadPromise = this.loadInternal();\n try {\n await this.loadPromise;\n } finally {\n this.loadPromise = null;\n }\n }\n\n private async loadInternal(): Promise<void> {\n // Record the file's mtime so the staleness probe does not immediately\n // invalidate on the next access (fileMtimeMs starts at 0; without this,\n // any non-zero mtime would trigger a spurious reload). Done before the\n // read so an ENOENT still records 0.\n this.recordFileMtime();\n let raw: string;\n try {\n raw = await this.io.read(this.filePath);\n } catch (err) {\n // ENOENT is fine — fresh store. Other errors leave the store empty\n // rather than crashing the write path (rule 34 spirit: degrade to\n // \"no tombstones known\" rather than blocking all writes).\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== \"ENOENT\") {\n // Swallow — the write path must not crash on a corrupt tombstone file.\n // Rebuild will repair on the next doctor/maintenance run.\n }\n this.loaded = true;\n return;\n }\n this.resetIndex();\n let corrupted = 0;\n for (const line of raw.split(\"\\n\")) {\n const entry = parseTombstoneLine(line);\n if (!entry) {\n if (line.trim().length > 0) corrupted += 1;\n continue;\n }\n this.indexEntry(entry);\n }\n this.corruptedLines = corrupted;\n this.loaded = true;\n }\n\n private resetIndex(): void {\n this.entries = [];\n this.byHash.clear();\n this.byNormalized.clear();\n this.byKey.clear();\n this.revokedIds.clear();\n this.byId.clear();\n }\n\n /**\n * Index an entry. Later entries (higher array index = newer createdAt)\n * OVERRIDE earlier ones at each tier key — the newest matching entry wins\n * (rule 25). Revocation entries mark their target as revoked.\n */\n private indexEntry(entry: TombstoneEntry): void {\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n if (entry.createdAt > (this.lastAppendAt ?? \"\")) {\n this.lastAppendAt = entry.createdAt;\n }\n if (entry.kind === \"revocation\") {\n if (entry.revokes) this.revokedIds.add(entry.revokes);\n return;\n }\n // Tombstone entries populate the lookup maps. The namespace is part of\n // every discriminator key (issue #1579 thread Ocs-O): when two\n // namespace-scoped stores share the same backing file, a later tombstone\n // for namespace B with identical content must NOT evict namespace A's map\n // entry — otherwise A's lookup finds B's id, rejects it on namespace\n // mismatch, and misses its own still-active tombstone (resurrection).\n const ns = entry.namespace;\n if (entry.contentHash) this.byHash.set(`${ns}\\0${entry.contentHash}`, entry.id);\n if (entry.normalizedText) this.byNormalized.set(`${ns}\\0${entry.normalizedText}`, entry.id);\n if (entry.entityRef && entry.supersessionKey) {\n this.byKey.set(keyedTierKey(ns, entry.entityRef, entry.supersessionKey), entry.id);\n }\n }\n\n /** Invalidate the in-memory cache (rule 25). The next access reloads. */\n invalidate(): void {\n this.loaded = false;\n this.loadPromise = null;\n this.resetIndex();\n }\n\n /**\n * Cross-process staleness probe (#1579). If the file's mtime advanced since\n * the last load/own-write, a peer process appended and our in-memory index is\n * stale — invalidate AND reload in place so the next lookup sees the new\n * entries. Best-effort: a stat error is swallowed (the loaded index is\n * retained rather than crashing the write path). No-op when no `stat` was\n * injected or when the mtime is unchanged. After our own append/revoke/rebuild\n * `markWritten` records the new mtime, so this probe does not fire for our\n * own writes (only for peer-process appends).\n */\n async ensureFreshAgainstDisk(): Promise<void> {\n if (!this.io.stat) return;\n let mtimeMs: number;\n try {\n mtimeMs = Math.floor(this.io.stat(this.filePath).mtimeMs);\n } catch {\n return; // ENOENT / permission — keep the loaded index (fail-open).\n }\n if (mtimeMs === this.fileMtimeMs && this.loaded) return;\n this.fileMtimeMs = mtimeMs;\n this.invalidate();\n await this.load();\n }\n\n /**\n * Record the file mtime after THIS process writes (append / revoke / rebuild)\n * so `ensureFreshAgainstDisk` does not treat our own write as a peer append\n * and throw away the just-updated in-memory index (#1579 — avoids a needless\n * invalidate+reload in the hot write path).\n */\n private markWritten(): void {\n this.recordFileMtime();\n }\n\n private recordFileMtime(): void {\n if (!this.io.stat) return;\n try {\n this.fileMtimeMs = Math.floor(this.io.stat(this.filePath).mtimeMs);\n } catch {\n // ENOENT — fresh store with no file yet; mtime stays at its current value.\n }\n }\n\n /**\n * Append a tombstone entry. Serialized via `serializeMutations` keyed by\n * file path so concurrent appends do not interleave, with rejection recovery\n * (rule 40 — a single failed append never poisons the chain). The in-memory\n * index is updated AFTER the durable append succeeds.\n *\n * Returns the new tombstone id.\n */\n async appendTombstone(input: {\n reason: TombstoneReason;\n createdBy: TombstoneCreatedBy;\n sourceMemoryId: string;\n rawContent: string;\n entityRef?: string;\n supersessionKey?: string;\n createdAt?: string;\n /**\n * Pre-computed canonical contentHash from the retired memory's frontmatter.\n * When provided, used directly so the tombstone's exact tier matches the\n * hash writeMemory computes on re-extraction (issue #1579 review: cited\n * facts must not slip past the chokepoint because the emitter hashed the\n * citation-annotated body instead of the canonical source).\n */\n contentHash?: string;\n }): Promise<string> {\n await this.load();\n const createdAt = input.createdAt ?? new Date().toISOString();\n const id = newTombstoneId();\n const entry: TombstoneEntry = {\n id,\n kind: \"tombstone\",\n reason: input.reason,\n sourceMemoryId: input.sourceMemoryId,\n contentHash: input.contentHash ?? this.options.hashContent(input.rawContent),\n normalizedText: this.options.normalizeText(input.rawContent),\n ...(input.entityRef ? { entityRef: input.entityRef } : {}),\n ...(input.supersessionKey ? { supersessionKey: input.supersessionKey } : {}),\n namespace: this.namespace,\n createdAt,\n createdBy: input.createdBy,\n };\n await this.serializeAppend(entry);\n this.indexEntry(entry);\n this.markWritten();\n return id;\n }\n\n /**\n * Append a revocation entry referencing `tombstoneId`. The log is\n * append-only; lookup treats a revoked tombstone as re-allowed (rule 25).\n */\n async revoke(tombstoneId: string, createdBy: TombstoneCreatedBy): Promise<string> {\n await this.load();\n const createdAt = new Date().toISOString();\n const id = newTombstoneId();\n const entry: TombstoneEntry = {\n id,\n kind: \"revocation\",\n reason: \"correction\",\n sourceMemoryId: \"\",\n contentHash: \"\",\n normalizedText: \"\",\n namespace: this.namespace,\n createdAt,\n createdBy,\n revokes: tombstoneId,\n };\n await this.serializeAppend(entry);\n this.indexEntry(entry);\n this.markWritten();\n return id;\n }\n\n private serializeAppend(entry: TombstoneEntry): Promise<void> {\n const line = JSON.stringify(entry) + \"\\n\";\n // serializeMutations recovers after rejection (rule 40): a failed append\n // surfaces to THIS caller but the next append is not poisoned.\n return serializeMutations(`tombstone:${this.filePath}`, () =>\n this.io.append(this.filePath, line),\n );\n }\n\n /**\n * Look up whether `query` is blocked by an active (non-revoked) tombstone.\n * Tiers are checked in order: exact (contentHash) → normalized → keyed →\n * semantic (off by default). Returns the first active match, or `null`.\n *\n * Namespace isolation (rule 42): only entries with `namespace === query.namespace`\n * match. The store is per-namespace, so this is belt-and-suspenders.\n */\n lookup(query: TombstoneLookupQuery): TombstoneMatch | null {\n // We intentionally do NOT await load() here — lookup is called from the\n // hot write path. Callers MUST ensure the store is loaded before lookup\n // (StorageManager does this in ensureTombstoneStoreLoaded). If not loaded,\n // lookup returns null (fail-open: a missing tombstone check is preferable\n // to crashing every write — see rule 34 / the \"degrade gracefully\" note).\n if (!this.options.enabled) return null;\n if (!this.loaded) return null;\n\n // Tier 1: exact contentHash. Namespace is part of the map key (thread\n // Ocs-O), so the lookup finds only this namespace's tombstone even when\n // the backing file is shared. The namespace equality check below is kept\n // as defense-in-depth.\n const ns = query.namespace;\n if (query.contentHash) {\n const id = this.byHash.get(`${ns}\\0${query.contentHash}`);\n if (id && !this.revokedIds.has(id)) {\n const entry = this.byId.get(id);\n if (entry && entry.namespace === ns) {\n return { tombstoneId: id, matchedTier: \"exact\", reason: entry.reason };\n }\n }\n }\n // Tier 2: normalized text.\n if (query.normalizedText) {\n const id = this.byNormalized.get(`${ns}\\0${query.normalizedText}`);\n if (id && !this.revokedIds.has(id)) {\n const entry = this.byId.get(id);\n if (entry && entry.namespace === ns) {\n return { tombstoneId: id, matchedTier: \"normalized\", reason: entry.reason };\n }\n }\n }\n // Tier 3: keyed (entityRef + supersessionKey). Issue #1579 thread\n // Ociag/Oci-W: check EVERY supplied key, not just the first — emitters\n // append one tombstone per matched supersession key, so the active block\n // can live on any later key. The union of `supersessionKey` (singular,\n // direct callers) and `supersessionKeys` (array, write chokepoint) is\n // checked; the first active match wins (tiers are equality-based, so\n // ordering across keys does not affect correctness).\n if (query.entityRef) {\n const keysToCheck: string[] = [];\n if (query.supersessionKey) keysToCheck.push(query.supersessionKey);\n if (query.supersessionKeys) {\n for (const k of query.supersessionKeys) {\n if (!keysToCheck.includes(k)) keysToCheck.push(k);\n }\n }\n for (const key of keysToCheck) {\n const id = this.byKey.get(keyedTierKey(ns, query.entityRef, key));\n if (id && !this.revokedIds.has(id)) {\n const entry = this.byId.get(id);\n if (entry && entry.namespace === ns) {\n return { tombstoneId: id, matchedTier: \"keyed\", reason: entry.reason };\n }\n }\n }\n }\n // Tier 4: semantic (off by default, rule 48).\n if (this.options.semanticMatch && this.options.semanticSimilarity && query.normalizedText) {\n const threshold = this.options.semanticThreshold;\n let best: { id: string; reason: TombstoneReason; score: number } | null = null;\n for (const entry of this.entries) {\n if (entry.kind !== \"tombstone\") continue;\n if (entry.namespace !== query.namespace) continue;\n if (this.revokedIds.has(entry.id)) continue;\n if (!entry.normalizedText) continue;\n const score = this.options.semanticSimilarity(query.normalizedText, entry.normalizedText);\n if (score >= threshold && (!best || score > best.score)) {\n best = { id: entry.id, reason: entry.reason, score };\n }\n }\n if (best) {\n return { tombstoneId: best.id, matchedTier: \"semantic\", reason: best.reason };\n }\n }\n return null;\n }\n\n /** Aggregate stats for the doctor / x-ray surfaces. */\n stats(): TombstoneStats {\n let active = 0;\n for (const entry of this.entries) {\n if (entry.kind === \"tombstone\" && !this.revokedIds.has(entry.id)) active += 1;\n }\n return {\n count: active,\n revoked: this.revokedIds.size,\n lastAppendAt: this.lastAppendAt,\n corruptedLines: this.corruptedLines,\n loaded: this.loaded,\n };\n }\n\n /** Read-only snapshot of all entries (for rebuild + tests). */\n snapshot(): readonly TombstoneEntry[] {\n return this.entries;\n }\n\n /**\n * Rebuild the in-memory index + JSONL from the supplied retired-memory\n * records + existing entries (preserving revocations). Byte-stable: entries\n * are sorted by (createdAt, id) before write (rule 38).\n *\n * Returns the count of tombstone entries written.\n */\n async rebuild(retiredMemories: ReadonlyArray<RetiredMemoryRecord>): Promise<number> {\n // Preserve existing revocations (all namespaces — ids are globally\n // unique) so a rebuild does not silently un-revoke.\n const existingRevocations = this.entries.filter((e) => e.kind === \"revocation\");\n // Preserve tombstone entries from OTHER namespaces when the backing file\n // is shared (issue #1579 thread Oc2MJ). rebuild rewrites the entire file;\n // without preserving foreign entries, rebuilding namespace A would\n // silently delete namespace B's tombstones, allowing resurrection in B.\n const foreignTombstones = this.entries.filter(\n (e) => e.kind === \"tombstone\" && e.namespace !== this.namespace,\n );\n // Reuse existing tombstone ids for source-equivalent entries so a prior\n // revocation (which references the tombstone id) survives rebuild — minting\n // fresh ids would orphan the revocation and silently un-revoke the content.\n // Issue #1579 thread Oci-T: key the reuse map by (sourceMemoryId,\n // supersessionKey), not just sourceMemoryId. A retired fact with multiple\n // structured-attribute keys emits one record per key (see\n // collectRetiredMemoriesForRebuild); keying only by sourceMemoryId made\n // every rebuilt record share one id, so a revocation of key A silently\n // revoked key B (over-revoke) — or, if a prior single-key tombstone was\n // revoked, the new multi-key records all inherited a revoked id and\n // silently un-blocked (orphan). Including the supersession-key\n // discriminator keeps each keyed tombstone's id (and thus its revocation)\n // independent. Records without a supersession key fall back to a stable\n // empty-string discriminator so they still reuse by sourceMemoryId alone.\n const existingBySource = new Map<string, string>();\n for (const e of this.entries) {\n if (e.kind === \"tombstone\") {\n existingBySource.set(`${e.sourceMemoryId}\\u{0000}${e.supersessionKey ?? \"\"}`, e.id);\n }\n }\n const rebuilt: TombstoneEntry[] = retiredMemories.map((m) => ({\n id:\n existingBySource.get(`${m.memoryId}\\u{0000}${m.supersessionKey ?? \"\"}`) ??\n newTombstoneId(),\n kind: \"tombstone\" as const,\n reason: m.reason,\n sourceMemoryId: m.memoryId,\n contentHash: m.contentHash ?? this.options.hashContent(m.rawContent),\n normalizedText: this.options.normalizeText(m.rawContent),\n ...(m.entityRef ? { entityRef: m.entityRef } : {}),\n ...(m.supersessionKey ? { supersessionKey: m.supersessionKey } : {}),\n namespace: this.namespace,\n createdAt: m.createdAt,\n createdBy: m.createdBy,\n }));\n // Sort deterministically (rule 38): createdAt, then id for stability.\n rebuilt.sort((a, b) =>\n a.createdAt === b.createdAt\n ? a.id < b.id ? -1 : a.id > b.id ? 1 : 0\n : a.createdAt < b.createdAt ? -1 : 1,\n );\n const all = [...rebuilt, ...existingRevocations, ...foreignTombstones].sort((a, b) =>\n a.createdAt === b.createdAt\n ? a.id < b.id ? -1 : a.id > b.id ? 1 : 0\n : a.createdAt < b.createdAt ? -1 : 1,\n );\n const serialized = all.map((e) => JSON.stringify(e)).join(\"\\n\") + (all.length > 0 ? \"\\n\" : \"\");\n await serializeMutations(`tombstone-rebuild:${this.filePath}`, () =>\n this.io.write(this.filePath, serialized),\n );\n this.resetIndex();\n for (const entry of all) this.indexEntry(entry);\n this.corruptedLines = 0;\n this.loaded = true;\n this.markWritten();\n return rebuilt.length;\n }\n}\n\n/** A retired memory projected into the shape `TombstoneStore.rebuild` consumes. */\nexport interface RetiredMemoryRecord {\n memoryId: string;\n rawContent: string;\n entityRef?: string;\n supersessionKey?: string;\n reason: TombstoneReason;\n createdBy: TombstoneCreatedBy;\n createdAt: string;\n /** Canonical contentHash from the retired memory's frontmatter (#1579). */\n contentHash?: string;\n}\n\n/**\n * Project a corpus of memories into the retired-memory records `rebuild`\n * consumes. Pure (no I/O) so the StorageManager wiring stays thin (#1579,\n * #1520 god-file ratchet). Only superseded / forgotten / rejected FACTS\n * participate — entities, questions, and artifacts have their own lifecycle\n * (issue pitfall). The supersession key is derived from structured attributes\n * via the injected helper so there is one keyed-tier definition (rule 23).\n */\nexport function collectRetiredMemoriesForRebuild(\n memories: ReadonlyArray<{\n frontmatter: {\n id: string;\n status?: string;\n category?: string;\n contentHash?: string;\n entityRef?: string;\n structuredAttributes?: Record<string, string>;\n updated?: string;\n created?: string;\n };\n content: string;\n }>,\n deps: {\n /** Strip citation annotations from the body before hashing/normalizing. */\n stripCitation: (text: string) => string;\n /** Derive the keyed-tier supersession key (one helper, rule 23). */\n supersessionKeysForFact: (spec: {\n entityRef?: string;\n structuredAttributes?: Record<string, string>;\n }) => string[];\n },\n): RetiredMemoryRecord[] {\n const retired: RetiredMemoryRecord[] = [];\n for (const m of memories) {\n const status = m.frontmatter.status;\n if (status !== \"superseded\" && status !== \"rejected\" && status !== \"forgotten\") continue;\n if (m.frontmatter.category !== \"fact\") continue;\n const reason: TombstoneReason =\n status === \"superseded\" ? \"supersession\" : status === \"forgotten\" ? \"retraction\" : \"correction\";\n const createdBy: TombstoneCreatedBy =\n reason === \"supersession\" ? \"supersession\" : \"user_correction\";\n const entityRef = m.frontmatter.entityRef;\n const keys =\n entityRef && m.frontmatter.structuredAttributes\n ? deps.supersessionKeysForFact({\n entityRef,\n structuredAttributes: m.frontmatter.structuredAttributes,\n })\n : [];\n // Issue #1579 thread Ocgjz: emit one record per matched supersession key\n // (not just keys[0]) so rebuild reproduces the same keyed tombstones as\n // live temporal-supersession (which now appends one per key — thread ObteS).\n // Without this, a rebuild would under-rebuild the JSONL and keyed-tier\n // blocks would disappear until rediscovered.\n const keysToEmit = keys.length > 0 ? keys : [undefined];\n for (const key of keysToEmit) {\n retired.push({\n memoryId: m.frontmatter.id,\n rawContent: deps.stripCitation(m.content),\n contentHash: m.frontmatter.contentHash,\n ...(entityRef ? { entityRef } : {}),\n ...(key ? { supersessionKey: key } : {}),\n reason,\n createdBy,\n createdAt: m.frontmatter.updated || m.frontmatter.created || new Date().toISOString(),\n });\n }\n }\n return retired;\n}\n\n/**\n * Build the live-emission tombstone inputs for a single retired FACT — one\n * input per derived supersession key (or a single keyless record when no\n * structured attributes are present). Pure (no I/O) so the emitters in\n * `StorageManager.supersedeMemory` (contradiction) and `forgetMemory`\n * (retraction) stay thin (#1579, #1520 god-file ratchet). Mirrors the\n * temporal-supersession emitter and `collectRetiredMemoriesForRebuild` so\n * every retire path emits the same keyed-tombstone shape (issue #1579\n * threads Oci-Y / OchiF: without per-key tombstones, a paraphrased\n * re-observation missed the keyed tier and the fact resurrected active).\n */\nexport function buildRetiredFactTombstoneInputs(\n memory: {\n id: string;\n content: string;\n contentHash?: string;\n entityRef?: string;\n structuredAttributes?: Record<string, string>;\n },\n opts: {\n reason: TombstoneReason;\n createdBy: TombstoneCreatedBy;\n createdAt: string;\n supersessionKeysForFact: (spec: {\n entityRef?: string;\n structuredAttributes?: Record<string, string>;\n }) => string[];\n },\n): Array<{\n reason: TombstoneReason;\n createdBy: TombstoneCreatedBy;\n sourceMemoryId: string;\n rawContent: string;\n contentHash?: string;\n entityRef?: string;\n supersessionKey?: string;\n createdAt: string;\n}> {\n const keys =\n memory.entityRef && memory.structuredAttributes\n ? opts.supersessionKeysForFact({\n entityRef: memory.entityRef,\n structuredAttributes: memory.structuredAttributes,\n })\n : [];\n const keysToEmit = keys.length > 0 ? keys : [undefined];\n return keysToEmit.map((key) => ({\n reason: opts.reason,\n createdBy: opts.createdBy,\n sourceMemoryId: memory.id,\n rawContent: memory.content,\n ...(memory.contentHash ? { contentHash: memory.contentHash } : {}),\n ...(memory.entityRef ? { entityRef: memory.entityRef } : {}),\n ...(key ? { supersessionKey: key } : {}),\n createdAt: opts.createdAt,\n }));\n}\n"],"mappings":";;;;;AA0JA,IAAM,mBAAmB;AAEzB,SAAS,iBAAyB;AAChC,SAAO,GAAG,gBAAgB,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AACjG;AASA,SAAS,aAAa,WAAmB,WAAmB,iBAAiC;AAC3F,SAAO,GAAG,SAAS,KAAK,SAAS,KAAK,eAAe;AACvD;AAOO,SAAS,mBAAmB,MAAqC;AACtE,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,OAAO,YAAY,EAAE,GAAG,WAAW,EAAG,QAAO;AAC1D,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,aAAc,QAAO;AAC9D,MACE,EAAE,WAAW,gBACb,EAAE,WAAW,kBACb,EAAE,WAAW,gBACb,EAAE,WAAW,4BACb;AACA,WAAO;AAAA,EACT;AACA,MAAI,OAAO,EAAE,mBAAmB,SAAU,QAAO;AACjD,MAAI,OAAO,EAAE,gBAAgB,SAAU,QAAO;AAC9C,MAAI,OAAO,EAAE,mBAAmB,SAAU,QAAO;AACjD,MAAI,OAAO,EAAE,cAAc,SAAU,QAAO;AAC5C,MAAI,OAAO,EAAE,cAAc,SAAU,QAAO;AAC5C,MACE,EAAE,cAAc,qBAChB,EAAE,cAAc,8BAChB,EAAE,cAAc,kBAChB,EAAE,cAAc,QAChB;AACA,WAAO;AAAA,EACT;AACA,QAAM,MAAsB;AAAA,IAC1B,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,QAAQ,EAAE;AAAA,IACV,gBAAgB,EAAE;AAAA,IAClB,aAAa,EAAE;AAAA,IACf,gBAAgB,EAAE;AAAA,IAClB,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,EACf;AACA,MAAI,OAAO,EAAE,cAAc,SAAU,KAAI,YAAY,EAAE;AACvD,MAAI,OAAO,EAAE,oBAAoB,SAAU,KAAI,kBAAkB,EAAE;AACnE,MAAI,OAAO,EAAE,YAAY,SAAU,KAAI,UAAU,EAAE;AACnD,SAAO;AACT;AASO,IAAM,iBAAN,MAAqB;AAAA,EAwB1B,YACmB,UACA,WACA,SACA,IACjB;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EA3BX,UAA4B,CAAC;AAAA;AAAA,EAEpB,SAAS,oBAAI,IAAoB;AAAA;AAAA,EAEjC,eAAe,oBAAI,IAAoB;AAAA;AAAA,EAEvC,QAAQ,oBAAI,IAAoB;AAAA;AAAA,EAEhC,aAAa,oBAAI,IAAY;AAAA;AAAA,EAE7B,OAAO,oBAAI,IAA4B;AAAA,EAChD,SAAS;AAAA,EACT,cAAoC;AAAA,EACpC,iBAAiB;AAAA,EACjB,eAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9B,cAAc;AAAA;AAAA,EAUtB,MAAM,OAAsB;AAC1B,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK,YAAa,QAAO,KAAK;AAClC,SAAK,cAAc,KAAK,aAAa;AACrC,QAAI;AACF,YAAM,KAAK;AAAA,IACb,UAAE;AACA,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,eAA8B;AAK1C,SAAK,gBAAgB;AACrB,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,KAAK,GAAG,KAAK,KAAK,QAAQ;AAAA,IACxC,SAAS,KAAK;AAIZ,YAAM,OAAQ,KAA+B;AAC7C,UAAI,SAAS,UAAU;AAAA,MAGvB;AACA,WAAK,SAAS;AACd;AAAA,IACF;AACA,SAAK,WAAW;AAChB,QAAI,YAAY;AAChB,eAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,YAAM,QAAQ,mBAAmB,IAAI;AACrC,UAAI,CAAC,OAAO;AACV,YAAI,KAAK,KAAK,EAAE,SAAS,EAAG,cAAa;AACzC;AAAA,MACF;AACA,WAAK,WAAW,KAAK;AAAA,IACvB;AACA,SAAK,iBAAiB;AACtB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEQ,aAAmB;AACzB,SAAK,UAAU,CAAC;AAChB,SAAK,OAAO,MAAM;AAClB,SAAK,aAAa,MAAM;AACxB,SAAK,MAAM,MAAM;AACjB,SAAK,WAAW,MAAM;AACtB,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,OAA6B;AAC9C,SAAK,QAAQ,KAAK,KAAK;AACvB,SAAK,KAAK,IAAI,MAAM,IAAI,KAAK;AAC7B,QAAI,MAAM,aAAa,KAAK,gBAAgB,KAAK;AAC/C,WAAK,eAAe,MAAM;AAAA,IAC5B;AACA,QAAI,MAAM,SAAS,cAAc;AAC/B,UAAI,MAAM,QAAS,MAAK,WAAW,IAAI,MAAM,OAAO;AACpD;AAAA,IACF;AAOA,UAAM,KAAK,MAAM;AACjB,QAAI,MAAM,YAAa,MAAK,OAAO,IAAI,GAAG,EAAE,KAAK,MAAM,WAAW,IAAI,MAAM,EAAE;AAC9E,QAAI,MAAM,eAAgB,MAAK,aAAa,IAAI,GAAG,EAAE,KAAK,MAAM,cAAc,IAAI,MAAM,EAAE;AAC1F,QAAI,MAAM,aAAa,MAAM,iBAAiB;AAC5C,WAAK,MAAM,IAAI,aAAa,IAAI,MAAM,WAAW,MAAM,eAAe,GAAG,MAAM,EAAE;AAAA,IACnF;AAAA,EACF;AAAA;AAAA,EAGA,aAAmB;AACjB,SAAK,SAAS;AACd,SAAK,cAAc;AACnB,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,yBAAwC;AAC5C,QAAI,CAAC,KAAK,GAAG,KAAM;AACnB,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,KAAK,GAAG,KAAK,KAAK,QAAQ,EAAE,OAAO;AAAA,IAC1D,QAAQ;AACN;AAAA,IACF;AACA,QAAI,YAAY,KAAK,eAAe,KAAK,OAAQ;AACjD,SAAK,cAAc;AACnB,SAAK,WAAW;AAChB,UAAM,KAAK,KAAK;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cAAoB;AAC1B,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,CAAC,KAAK,GAAG,KAAM;AACnB,QAAI;AACF,WAAK,cAAc,KAAK,MAAM,KAAK,GAAG,KAAK,KAAK,QAAQ,EAAE,OAAO;AAAA,IACnE,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,gBAAgB,OAgBF;AAClB,UAAM,KAAK,KAAK;AAChB,UAAM,YAAY,MAAM,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC5D,UAAM,KAAK,eAAe;AAC1B,UAAM,QAAwB;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,gBAAgB,MAAM;AAAA,MACtB,aAAa,MAAM,eAAe,KAAK,QAAQ,YAAY,MAAM,UAAU;AAAA,MAC3E,gBAAgB,KAAK,QAAQ,cAAc,MAAM,UAAU;AAAA,MAC3D,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,MACxD,GAAI,MAAM,kBAAkB,EAAE,iBAAiB,MAAM,gBAAgB,IAAI,CAAC;AAAA,MAC1E,WAAW,KAAK;AAAA,MAChB;AAAA,MACA,WAAW,MAAM;AAAA,IACnB;AACA,UAAM,KAAK,gBAAgB,KAAK;AAChC,SAAK,WAAW,KAAK;AACrB,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,aAAqB,WAAgD;AAChF,UAAM,KAAK,KAAK;AAChB,UAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,UAAM,KAAK,eAAe;AAC1B,UAAM,QAAwB;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,WAAW,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX;AACA,UAAM,KAAK,gBAAgB,KAAK;AAChC,SAAK,WAAW,KAAK;AACrB,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAgB,OAAsC;AAC5D,UAAM,OAAO,KAAK,UAAU,KAAK,IAAI;AAGrC,WAAO;AAAA,MAAmB,aAAa,KAAK,QAAQ;AAAA,MAAI,MACtD,KAAK,GAAG,OAAO,KAAK,UAAU,IAAI;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAO,OAAoD;AAMzD,QAAI,CAAC,KAAK,QAAQ,QAAS,QAAO;AAClC,QAAI,CAAC,KAAK,OAAQ,QAAO;AAMzB,UAAM,KAAK,MAAM;AACjB,QAAI,MAAM,aAAa;AACrB,YAAM,KAAK,KAAK,OAAO,IAAI,GAAG,EAAE,KAAK,MAAM,WAAW,EAAE;AACxD,UAAI,MAAM,CAAC,KAAK,WAAW,IAAI,EAAE,GAAG;AAClC,cAAM,QAAQ,KAAK,KAAK,IAAI,EAAE;AAC9B,YAAI,SAAS,MAAM,cAAc,IAAI;AACnC,iBAAO,EAAE,aAAa,IAAI,aAAa,SAAS,QAAQ,MAAM,OAAO;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,gBAAgB;AACxB,YAAM,KAAK,KAAK,aAAa,IAAI,GAAG,EAAE,KAAK,MAAM,cAAc,EAAE;AACjE,UAAI,MAAM,CAAC,KAAK,WAAW,IAAI,EAAE,GAAG;AAClC,cAAM,QAAQ,KAAK,KAAK,IAAI,EAAE;AAC9B,YAAI,SAAS,MAAM,cAAc,IAAI;AACnC,iBAAO,EAAE,aAAa,IAAI,aAAa,cAAc,QAAQ,MAAM,OAAO;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAQA,QAAI,MAAM,WAAW;AACnB,YAAM,cAAwB,CAAC;AAC/B,UAAI,MAAM,gBAAiB,aAAY,KAAK,MAAM,eAAe;AACjE,UAAI,MAAM,kBAAkB;AAC1B,mBAAW,KAAK,MAAM,kBAAkB;AACtC,cAAI,CAAC,YAAY,SAAS,CAAC,EAAG,aAAY,KAAK,CAAC;AAAA,QAClD;AAAA,MACF;AACA,iBAAW,OAAO,aAAa;AAC7B,cAAM,KAAK,KAAK,MAAM,IAAI,aAAa,IAAI,MAAM,WAAW,GAAG,CAAC;AAChE,YAAI,MAAM,CAAC,KAAK,WAAW,IAAI,EAAE,GAAG;AAClC,gBAAM,QAAQ,KAAK,KAAK,IAAI,EAAE;AAC9B,cAAI,SAAS,MAAM,cAAc,IAAI;AACnC,mBAAO,EAAE,aAAa,IAAI,aAAa,SAAS,QAAQ,MAAM,OAAO;AAAA,UACvE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,KAAK,QAAQ,iBAAiB,KAAK,QAAQ,sBAAsB,MAAM,gBAAgB;AACzF,YAAM,YAAY,KAAK,QAAQ;AAC/B,UAAI,OAAsE;AAC1E,iBAAW,SAAS,KAAK,SAAS;AAChC,YAAI,MAAM,SAAS,YAAa;AAChC,YAAI,MAAM,cAAc,MAAM,UAAW;AACzC,YAAI,KAAK,WAAW,IAAI,MAAM,EAAE,EAAG;AACnC,YAAI,CAAC,MAAM,eAAgB;AAC3B,cAAM,QAAQ,KAAK,QAAQ,mBAAmB,MAAM,gBAAgB,MAAM,cAAc;AACxF,YAAI,SAAS,cAAc,CAAC,QAAQ,QAAQ,KAAK,QAAQ;AACvD,iBAAO,EAAE,IAAI,MAAM,IAAI,QAAQ,MAAM,QAAQ,MAAM;AAAA,QACrD;AAAA,MACF;AACA,UAAI,MAAM;AACR,eAAO,EAAE,aAAa,KAAK,IAAI,aAAa,YAAY,QAAQ,KAAK,OAAO;AAAA,MAC9E;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAwB;AACtB,QAAI,SAAS;AACb,eAAW,SAAS,KAAK,SAAS;AAChC,UAAI,MAAM,SAAS,eAAe,CAAC,KAAK,WAAW,IAAI,MAAM,EAAE,EAAG,WAAU;AAAA,IAC9E;AACA,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS,KAAK,WAAW;AAAA,MACzB,cAAc,KAAK;AAAA,MACnB,gBAAgB,KAAK;AAAA,MACrB,QAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAAA;AAAA,EAGA,WAAsC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QAAQ,iBAAsE;AAGlF,UAAM,sBAAsB,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY;AAK9E,UAAM,oBAAoB,KAAK,QAAQ;AAAA,MACrC,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,cAAc,KAAK;AAAA,IACxD;AAeA,UAAM,mBAAmB,oBAAI,IAAoB;AACjD,eAAW,KAAK,KAAK,SAAS;AAC5B,UAAI,EAAE,SAAS,aAAa;AAC1B,yBAAiB,IAAI,GAAG,EAAE,cAAc,KAAW,EAAE,mBAAmB,EAAE,IAAI,EAAE,EAAE;AAAA,MACpF;AAAA,IACF;AACA,UAAM,UAA4B,gBAAgB,IAAI,CAAC,OAAO;AAAA,MAC5D,IACE,iBAAiB,IAAI,GAAG,EAAE,QAAQ,KAAW,EAAE,mBAAmB,EAAE,EAAE,KACtE,eAAe;AAAA,MACjB,MAAM;AAAA,MACN,QAAQ,EAAE;AAAA,MACV,gBAAgB,EAAE;AAAA,MAClB,aAAa,EAAE,eAAe,KAAK,QAAQ,YAAY,EAAE,UAAU;AAAA,MACnE,gBAAgB,KAAK,QAAQ,cAAc,EAAE,UAAU;AAAA,MACvD,GAAI,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,IAAI,CAAC;AAAA,MAChD,GAAI,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,IAAI,CAAC;AAAA,MAClE,WAAW,KAAK;AAAA,MAChB,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,IACf,EAAE;AAEF,YAAQ;AAAA,MAAK,CAAC,GAAG,MACf,EAAE,cAAc,EAAE,YACd,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,IACrC,EAAE,YAAY,EAAE,YAAY,KAAK;AAAA,IACvC;AACA,UAAM,MAAM,CAAC,GAAG,SAAS,GAAG,qBAAqB,GAAG,iBAAiB,EAAE;AAAA,MAAK,CAAC,GAAG,MAC9E,EAAE,cAAc,EAAE,YACd,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,IACrC,EAAE,YAAY,EAAE,YAAY,KAAK;AAAA,IACvC;AACA,UAAM,aAAa,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,KAAK,IAAI,SAAS,IAAI,OAAO;AAC3F,UAAM;AAAA,MAAmB,qBAAqB,KAAK,QAAQ;AAAA,MAAI,MAC7D,KAAK,GAAG,MAAM,KAAK,UAAU,UAAU;AAAA,IACzC;AACA,SAAK,WAAW;AAChB,eAAW,SAAS,IAAK,MAAK,WAAW,KAAK;AAC9C,SAAK,iBAAiB;AACtB,SAAK,SAAS;AACd,SAAK,YAAY;AACjB,WAAO,QAAQ;AAAA,EACjB;AACF;AAuBO,SAAS,iCACd,UAaA,MASuB;AACvB,QAAM,UAAiC,CAAC;AACxC,aAAW,KAAK,UAAU;AACxB,UAAM,SAAS,EAAE,YAAY;AAC7B,QAAI,WAAW,gBAAgB,WAAW,cAAc,WAAW,YAAa;AAChF,QAAI,EAAE,YAAY,aAAa,OAAQ;AACvC,UAAM,SACJ,WAAW,eAAe,iBAAiB,WAAW,cAAc,eAAe;AACrF,UAAM,YACJ,WAAW,iBAAiB,iBAAiB;AAC/C,UAAM,YAAY,EAAE,YAAY;AAChC,UAAM,OACJ,aAAa,EAAE,YAAY,uBACvB,KAAK,wBAAwB;AAAA,MAC3B;AAAA,MACA,sBAAsB,EAAE,YAAY;AAAA,IACtC,CAAC,IACD,CAAC;AAMP,UAAM,aAAa,KAAK,SAAS,IAAI,OAAO,CAAC,MAAS;AACtD,eAAW,OAAO,YAAY;AAC5B,cAAQ,KAAK;AAAA,QACX,UAAU,EAAE,YAAY;AAAA,QACxB,YAAY,KAAK,cAAc,EAAE,OAAO;AAAA,QACxC,aAAa,EAAE,YAAY;AAAA,QAC3B,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,QACjC,GAAI,MAAM,EAAE,iBAAiB,IAAI,IAAI,CAAC;AAAA,QACtC;AAAA,QACA;AAAA,QACA,WAAW,EAAE,YAAY,WAAW,EAAE,YAAY,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACtF,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,gCACd,QAOA,MAkBC;AACD,QAAM,OACJ,OAAO,aAAa,OAAO,uBACvB,KAAK,wBAAwB;AAAA,IAC3B,WAAW,OAAO;AAAA,IAClB,sBAAsB,OAAO;AAAA,EAC/B,CAAC,IACD,CAAC;AACP,QAAM,aAAa,KAAK,SAAS,IAAI,OAAO,CAAC,MAAS;AACtD,SAAO,WAAW,IAAI,CAAC,SAAS;AAAA,IAC9B,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK;AAAA,IAChB,gBAAgB,OAAO;AAAA,IACvB,YAAY,OAAO;AAAA,IACnB,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,IAChE,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,IAC1D,GAAI,MAAM,EAAE,iBAAiB,IAAI,IAAI,CAAC;AAAA,IACtC,WAAW,KAAK;AAAA,EAClB,EAAE;AACJ;","names":[]}