@remnic/core 9.3.725 → 9.3.727

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 (352) hide show
  1. package/dist/access-boundary.d.ts +5 -5
  2. package/dist/access-boundary.js +28 -27
  3. package/dist/access-cli.js +65 -63
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +20 -5
  6. package/dist/access-http.js +34 -33
  7. package/dist/access-mcp.d.ts +5 -5
  8. package/dist/access-mcp.js +31 -30
  9. package/dist/access-operations-batch.js +29 -28
  10. package/dist/access-operations.d.ts +5 -5
  11. package/dist/access-operations.js +30 -29
  12. package/dist/{access-service-B2PlPtxh.d.ts → access-service-Bgpjxo4f.d.ts} +36 -4
  13. package/dist/access-service.d.ts +5 -5
  14. package/dist/access-service.js +27 -26
  15. package/dist/access-surface-catalog.d.ts +5 -5
  16. package/dist/action-confidence.d.ts +1 -1
  17. package/dist/active-memory-bridge.d.ts +1 -1
  18. package/dist/active-recall.d.ts +1 -1
  19. package/dist/active-recall.js +3 -3
  20. package/dist/adapters/index.js +4 -4
  21. package/dist/adapters/registry.js +2 -2
  22. package/dist/behavior-learner.d.ts +1 -1
  23. package/dist/behavior-signals.d.ts +1 -1
  24. package/dist/bootstrap.d.ts +4 -4
  25. package/dist/briefing.d.ts +1 -1
  26. package/dist/briefing.js +9 -8
  27. package/dist/buffer-surprise-report.d.ts +1 -1
  28. package/dist/buffer.d.ts +1 -1
  29. package/dist/calibration.d.ts +1 -1
  30. package/dist/capabilities.d.ts +1 -1
  31. package/dist/{catalog-CzUb-GyD.d.ts → catalog-bMczlO2G.d.ts} +1 -1
  32. package/dist/causal-behavior.d.ts +1 -1
  33. package/dist/causal-consolidation.d.ts +1 -1
  34. package/dist/causal-consolidation.js +10 -9
  35. package/dist/causal-consolidation.js.map +1 -1
  36. package/dist/causal-trajectory-graph.d.ts +1 -1
  37. package/dist/{chunk-KGZF2VO3.js → chunk-27QQC7ZX.js} +80 -19
  38. package/dist/{chunk-KGZF2VO3.js.map → chunk-27QQC7ZX.js.map} +1 -1
  39. package/dist/{chunk-HCVWF657.js → chunk-2KAYTPPT.js} +12 -12
  40. package/dist/{chunk-4SF232E6.js → chunk-36SEDFAU.js} +19 -7
  41. package/dist/chunk-36SEDFAU.js.map +1 -0
  42. package/dist/{chunk-ZK2L72VN.js → chunk-37436GML.js} +2 -2
  43. package/dist/{chunk-SC2YAWRF.js → chunk-3TSTGI6F.js} +2 -2
  44. package/dist/{chunk-FACTVMXQ.js → chunk-4H4F3BFL.js} +2 -2
  45. package/dist/{chunk-WAFDHKES.js → chunk-4NOI577P.js} +2 -2
  46. package/dist/{chunk-YQMPUYHE.js → chunk-4NR2PDZ5.js} +2 -2
  47. package/dist/{chunk-TAPSNIMA.js → chunk-5CEJH5ZN.js} +4 -4
  48. package/dist/{chunk-FN6QP7JQ.js → chunk-6PY5YJAV.js} +100 -14
  49. package/dist/{chunk-FN6QP7JQ.js.map → chunk-6PY5YJAV.js.map} +1 -1
  50. package/dist/{chunk-PKMQK2WY.js → chunk-773DPI6T.js} +4 -4
  51. package/dist/{chunk-4R3CGCI5.js → chunk-7RLCCNHU.js} +15 -12
  52. package/dist/chunk-7RLCCNHU.js.map +1 -0
  53. package/dist/{chunk-XGTBNVIU.js → chunk-AAD2ZS6P.js} +11 -1
  54. package/dist/chunk-AAD2ZS6P.js.map +1 -0
  55. package/dist/{chunk-Z3FGC3QE.js → chunk-AQLOP5F4.js} +139 -30
  56. package/dist/chunk-AQLOP5F4.js.map +1 -0
  57. package/dist/{chunk-FXCIOLRB.js → chunk-ASKXIAHD.js} +2 -2
  58. package/dist/{chunk-5OPYYPJN.js → chunk-CBWI22JN.js} +2 -2
  59. package/dist/{chunk-ZOHMPFOB.js → chunk-CKFLLAIS.js} +3 -3
  60. package/dist/{chunk-GZQXOQQ2.js → chunk-CSRXQ6NB.js} +5 -5
  61. package/dist/{chunk-QR67FU5G.js → chunk-CTOQEZSN.js} +2 -2
  62. package/dist/{chunk-ZUDM75KG.js → chunk-DCWIQFNA.js} +4 -4
  63. package/dist/{chunk-6CWGS2LY.js → chunk-DKJCRGWM.js} +3 -3
  64. package/dist/{chunk-JJJCVCCQ.js → chunk-DMQWV6SQ.js} +2 -2
  65. package/dist/{chunk-F3XZN24Z.js → chunk-DRUDC2UX.js} +2 -2
  66. package/dist/{chunk-4WZ3J7M6.js → chunk-EY6MDMHK.js} +2 -2
  67. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  68. package/dist/{chunk-C4X4QGIO.js → chunk-G6NKALRN.js} +5 -2
  69. package/dist/chunk-G6NKALRN.js.map +1 -0
  70. package/dist/{chunk-6SQQOJ4U.js → chunk-GKRLPHPU.js} +2 -2
  71. package/dist/{chunk-D7EBTQ24.js → chunk-HOHVIVDR.js} +2 -2
  72. package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
  73. package/dist/{chunk-UBHVAGO7.js → chunk-JYLFZWUX.js} +2 -2
  74. package/dist/{chunk-LD53WPMU.js → chunk-LQ6JI4VH.js} +4 -4
  75. package/dist/chunk-LTHVM4XN.js +109 -0
  76. package/dist/chunk-LTHVM4XN.js.map +1 -0
  77. package/dist/{chunk-WM7DNKBD.js → chunk-M4V2JVSZ.js} +2 -2
  78. package/dist/{chunk-I2HL5M6T.js → chunk-MJIHJ7OG.js} +52 -52
  79. package/dist/{chunk-J334N6QV.js → chunk-MLKWJOAJ.js} +2 -2
  80. package/dist/{chunk-C6BWLYW5.js → chunk-N3VNUM4Z.js} +171 -79
  81. package/dist/chunk-N3VNUM4Z.js.map +1 -0
  82. package/dist/{chunk-DF3I6JGP.js → chunk-NSA5ZRUL.js} +15 -15
  83. package/dist/{chunk-VI5SGGXB.js → chunk-OEEW4KJA.js} +4 -4
  84. package/dist/{chunk-7AG54AF3.js → chunk-PUR2CKX2.js} +1 -1
  85. package/dist/chunk-PUR2CKX2.js.map +1 -0
  86. package/dist/{chunk-NT36GU6X.js → chunk-R7IYU5DZ.js} +2 -2
  87. package/dist/{chunk-TT5U3UDO.js → chunk-SNKPUJHR.js} +4 -4
  88. package/dist/{chunk-T55EGZSJ.js → chunk-UVPL565C.js} +2 -2
  89. package/dist/chunk-W5H6ZRPT.js +14 -0
  90. package/dist/chunk-W5H6ZRPT.js.map +1 -0
  91. package/dist/{chunk-YQGHW5ML.js → chunk-WRFKZEO6.js} +4 -4
  92. package/dist/{chunk-KLFYKC3K.js → chunk-XPHVAOBT.js} +195 -74
  93. package/dist/chunk-XPHVAOBT.js.map +1 -0
  94. package/dist/{chunk-H7HUQIY7.js → chunk-Y4YD5TYW.js} +3 -3
  95. package/dist/{chunk-MDBZTH7N.js → chunk-YKBHOBJK.js} +44 -10
  96. package/dist/chunk-YKBHOBJK.js.map +1 -0
  97. package/dist/{chunk-YUYPWJD3.js → chunk-YOLZS7NY.js} +2 -2
  98. package/dist/{chunk-6OIDDKAA.js → chunk-YTU7WKYN.js} +35 -9
  99. package/dist/chunk-YTU7WKYN.js.map +1 -0
  100. package/dist/{cli-IOlDHSBT.d.ts → cli-BLME420h.d.ts} +3 -3
  101. package/dist/cli.d.ts +6 -6
  102. package/dist/cli.js +60 -59
  103. package/dist/compat/codex-cli-fallback.d.ts +1 -0
  104. package/dist/compat/codex-cli-fallback.js +22 -0
  105. package/dist/compat/codex-cli-fallback.js.map +1 -0
  106. package/dist/compat/codex-thread-key.d.ts +1 -0
  107. package/dist/compat/codex-thread-key.js +18 -0
  108. package/dist/compat/codex-thread-key.js.map +1 -0
  109. package/dist/compounding/engine.d.ts +1 -1
  110. package/dist/compounding/engine.js +9 -8
  111. package/dist/compounding/preference-consolidator.d.ts +1 -1
  112. package/dist/compression-optimizer.d.ts +1 -1
  113. package/dist/config.d.ts +1 -1
  114. package/dist/config.js +3 -3
  115. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  116. package/dist/connectors/codex-materialize-runner.js +9 -8
  117. package/dist/connectors/codex-materialize.d.ts +1 -1
  118. package/dist/connectors/index.d.ts +1 -1
  119. package/dist/connectors/index.js +9 -8
  120. package/dist/consolidation-provenance-check.d.ts +1 -1
  121. package/dist/consolidation-undo.d.ts +1 -1
  122. package/dist/content-hash.d.ts +14 -0
  123. package/dist/content-hash.js +10 -0
  124. package/dist/content-hash.js.map +1 -0
  125. package/dist/contradiction/index.d.ts +1 -1
  126. package/dist/conversation-index/backend.d.ts +1 -1
  127. package/dist/conversation-index/backend.js +2 -2
  128. package/dist/conversation-index/chunker.d.ts +1 -1
  129. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  130. package/dist/conversation-index/indexer.d.ts +1 -1
  131. package/dist/conversation-index/search.d.ts +1 -1
  132. package/dist/dashboard-runtime.js +2 -2
  133. package/dist/day-summary.d.ts +1 -1
  134. package/dist/delinearize.d.ts +1 -1
  135. package/dist/direct-answer-wiring.d.ts +1 -1
  136. package/dist/direct-answer.d.ts +1 -1
  137. package/dist/embedding-fallback.d.ts +1 -1
  138. package/dist/enrichment/index.d.ts +1 -1
  139. package/dist/entity-retrieval.d.ts +1 -1
  140. package/dist/entity-retrieval.js +9 -8
  141. package/dist/entity-schema.d.ts +1 -1
  142. package/dist/explicit-capture.d.ts +4 -4
  143. package/dist/extraction-faithfulness.d.ts +1 -1
  144. package/dist/extraction-faithfulness.js +2 -2
  145. package/dist/extraction-judge-telemetry.d.ts +1 -1
  146. package/dist/extraction-judge-training.d.ts +1 -1
  147. package/dist/extraction-judge.d.ts +1 -1
  148. package/dist/extraction-redaction-rules.d.ts +33 -0
  149. package/dist/extraction-redaction-rules.js +14 -0
  150. package/dist/extraction-redaction-rules.js.map +1 -0
  151. package/dist/extraction.d.ts +1 -1
  152. package/dist/extraction.js +3 -3
  153. package/dist/faithfulness-config.d.ts +1 -0
  154. package/dist/faithfulness-config.js +1 -1
  155. package/dist/fallback-llm.d.ts +1 -1
  156. package/dist/{forget-OEE5OELK.js → forget-WYLR4ZFV.js} +2 -2
  157. package/dist/graph-dashboard-diff.d.ts +1 -1
  158. package/dist/graph-dashboard-key.d.ts +1 -1
  159. package/dist/graph-dashboard-parser.d.ts +1 -1
  160. package/dist/graph-edge-reinforcement.d.ts +1 -1
  161. package/dist/graph-snapshot.d.ts +1 -1
  162. package/dist/graph.d.ts +1 -1
  163. package/dist/identity-continuity.d.ts +1 -1
  164. package/dist/importance.d.ts +1 -1
  165. package/dist/index.d.ts +9 -9
  166. package/dist/index.js +107 -105
  167. package/dist/index.js.map +1 -1
  168. package/dist/intent.d.ts +1 -1
  169. package/dist/lcm/engine.d.ts +1 -1
  170. package/dist/lcm/engine.js +2 -2
  171. package/dist/lcm/index.d.ts +1 -1
  172. package/dist/lcm/index.js +4 -4
  173. package/dist/lcm/tools.d.ts +1 -1
  174. package/dist/lifecycle.d.ts +1 -1
  175. package/dist/live-connectors-runner.d.ts +1 -1
  176. package/dist/local-llm.d.ts +1 -1
  177. package/dist/local-model-endpoint.d.ts +9 -4
  178. package/dist/local-model-endpoint.js +1 -1
  179. package/dist/maintenance/memory-governance.d.ts +1 -1
  180. package/dist/maintenance/memory-governance.js +9 -8
  181. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +9 -8
  182. package/dist/maintenance/rebuild-memory-projection.js +10 -9
  183. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  184. package/dist/memory-action-policy.d.ts +1 -1
  185. package/dist/memory-cache.d.ts +1 -1
  186. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  187. package/dist/memory-projection-store.d.ts +1 -1
  188. package/dist/memory-provenance.d.ts +1 -1
  189. package/dist/memory-worth-outcomes.d.ts +1 -1
  190. package/dist/models-json.d.ts +1 -1
  191. package/dist/namespaces/migrate.d.ts +2 -2
  192. package/dist/namespaces/migrate.js +19 -18
  193. package/dist/namespaces/principal.d.ts +1 -1
  194. package/dist/namespaces/search.d.ts +1 -1
  195. package/dist/namespaces/search.js +9 -9
  196. package/dist/namespaces/storage.d.ts +2 -2
  197. package/dist/namespaces/storage.js +9 -8
  198. package/dist/native-knowledge.d.ts +1 -1
  199. package/dist/operator-toolkit.d.ts +1 -1
  200. package/dist/operator-toolkit.js +28 -27
  201. package/dist/orchestration/maintenance.d.ts +2 -2
  202. package/dist/orchestration/maintenance.js +11 -10
  203. package/dist/{orchestrator-C_OMSIMZ.d.ts → orchestrator-DqPAz8ot.d.ts} +3 -3
  204. package/dist/orchestrator.d.ts +4 -4
  205. package/dist/orchestrator.js +51 -49
  206. package/dist/patterns-cli.d.ts +1 -1
  207. package/dist/policy-runtime.d.ts +1 -1
  208. package/dist/provenance.d.ts +1 -1
  209. package/dist/provenance.js +1 -1
  210. package/dist/qmd-recall-cache.d.ts +1 -1
  211. package/dist/qmd.d.ts +1 -1
  212. package/dist/recall-disclosure-escalation.d.ts +1 -1
  213. package/dist/recall-explain-renderer.d.ts +1 -1
  214. package/dist/recall-explain-renderer.js +3 -3
  215. package/dist/recall-planner-llm.d.ts +1 -1
  216. package/dist/recall-state.d.ts +1 -1
  217. package/dist/recall-tag-filter.d.ts +1 -1
  218. package/dist/recall-xray-cli.d.ts +1 -1
  219. package/dist/recall-xray-cli.js +4 -4
  220. package/dist/recall-xray-renderer.d.ts +1 -1
  221. package/dist/recall-xray-renderer.js +3 -3
  222. package/dist/recall-xray.d.ts +1 -1
  223. package/dist/recall-xray.js +2 -2
  224. package/dist/resolve-auth-token.d.ts +1 -1
  225. package/dist/resume-bundles.js +4 -4
  226. package/dist/retrieval-agents.d.ts +1 -1
  227. package/dist/retrieval-tiers.d.ts +1 -1
  228. package/dist/routing/engine.d.ts +1 -1
  229. package/dist/routing/store.d.ts +1 -1
  230. package/dist/schemas.d.ts +22 -22
  231. package/dist/search/embed-helper.d.ts +1 -1
  232. package/dist/search/factory.d.ts +1 -1
  233. package/dist/search/factory.js +8 -8
  234. package/dist/search/index.d.ts +1 -1
  235. package/dist/search/index.js +14 -14
  236. package/dist/search/lancedb-backend.d.ts +1 -1
  237. package/dist/search/lancedb-backend.js +2 -2
  238. package/dist/search/meilisearch-backend.d.ts +1 -1
  239. package/dist/search/meilisearch-backend.js +2 -2
  240. package/dist/search/noop-backend.d.ts +1 -1
  241. package/dist/search/orama-backend.d.ts +1 -1
  242. package/dist/search/orama-backend.js +2 -2
  243. package/dist/search/port.d.ts +1 -1
  244. package/dist/search/remote-backend.d.ts +1 -1
  245. package/dist/{semantic-consolidation-B8LiYtvx.d.ts → semantic-consolidation-Cl15ksZy.d.ts} +1 -1
  246. package/dist/semantic-consolidation.d.ts +2 -2
  247. package/dist/semantic-consolidation.js +10 -9
  248. package/dist/semantic-rule-promotion.js +9 -8
  249. package/dist/semantic-rule-verifier.d.ts +1 -1
  250. package/dist/semantic-rule-verifier.js +9 -8
  251. package/dist/session-observer-bands.d.ts +1 -1
  252. package/dist/session-observer-state.d.ts +1 -1
  253. package/dist/shared-context/manager.d.ts +1 -1
  254. package/dist/signal.d.ts +1 -1
  255. package/dist/storage.d.ts +3 -3
  256. package/dist/storage.js +9 -8
  257. package/dist/summarizer.d.ts +1 -1
  258. package/dist/summary-snapshot.d.ts +1 -1
  259. package/dist/temporal-supersession.d.ts +1 -1
  260. package/dist/temporal-validity.d.ts +1 -1
  261. package/dist/threading.d.ts +1 -1
  262. package/dist/tier-migration.d.ts +1 -1
  263. package/dist/tier-routing.d.ts +1 -1
  264. package/dist/topics.d.ts +1 -1
  265. package/dist/transcript.d.ts +1 -1
  266. package/dist/transfer/types.d.ts +12 -12
  267. package/dist/trust-score-stage.d.ts +1 -1
  268. package/dist/trust-score.d.ts +1 -1
  269. package/dist/{types-DmTaqob2.d.ts → types-ChQb9D6s.d.ts} +8 -0
  270. package/dist/types.d.ts +1 -1
  271. package/dist/types.js +1 -1
  272. package/dist/utility-runtime.d.ts +1 -1
  273. package/dist/verified-recall.js +9 -8
  274. package/package.json +26 -2
  275. package/src/access-http.ts +68 -1
  276. package/src/admin/admin-surfaces.test.ts +289 -0
  277. package/src/admin/admin-surfaces.ts +138 -7
  278. package/src/bitemporal-dedup-backfill.test.ts +294 -0
  279. package/src/chat/chat-engine.test.ts +66 -1
  280. package/src/chat/chat-http.test.ts +119 -1
  281. package/src/chat/chat-http.ts +252 -97
  282. package/src/chat/chat-session.ts +143 -7
  283. package/src/compat/codex-cli-fallback.ts +23 -0
  284. package/src/compat/codex-subpath-aliases.test.ts +93 -0
  285. package/src/compat/codex-thread-key.ts +23 -0
  286. package/src/config.test.ts +50 -0
  287. package/src/config.ts +21 -4
  288. package/src/content-hash.ts +24 -0
  289. package/src/correction/correction-access-wiring.ts +104 -19
  290. package/src/correction/correction-contract.ts +68 -10
  291. package/src/correction/correction-executor.test.ts +112 -2
  292. package/src/correction/correction-executor.ts +65 -5
  293. package/src/correction/correction-planner.test.ts +47 -0
  294. package/src/correction/correction-planner.ts +40 -1
  295. package/src/correction/correction-storage-integration.test.ts +183 -0
  296. package/src/extraction-faithfulness.test.ts +240 -0
  297. package/src/extraction-faithfulness.ts +58 -6
  298. package/src/extraction-redaction-rules.test.ts +213 -0
  299. package/src/extraction-redaction-rules.ts +208 -0
  300. package/src/faithfulness-config.ts +16 -1
  301. package/src/local-model-endpoint.ts +24 -3
  302. package/src/orchestrator.ts +252 -9
  303. package/src/provenance-extraction.test.ts +266 -0
  304. package/src/provenance.ts +66 -16
  305. package/src/storage.ts +6 -10
  306. package/src/types.ts +8 -0
  307. package/dist/chunk-4R3CGCI5.js.map +0 -1
  308. package/dist/chunk-4SF232E6.js.map +0 -1
  309. package/dist/chunk-6OIDDKAA.js.map +0 -1
  310. package/dist/chunk-7AG54AF3.js.map +0 -1
  311. package/dist/chunk-C4X4QGIO.js.map +0 -1
  312. package/dist/chunk-C6BWLYW5.js.map +0 -1
  313. package/dist/chunk-KLFYKC3K.js.map +0 -1
  314. package/dist/chunk-MDBZTH7N.js.map +0 -1
  315. package/dist/chunk-XGTBNVIU.js.map +0 -1
  316. package/dist/chunk-Z3FGC3QE.js.map +0 -1
  317. /package/dist/{chunk-HCVWF657.js.map → chunk-2KAYTPPT.js.map} +0 -0
  318. /package/dist/{chunk-ZK2L72VN.js.map → chunk-37436GML.js.map} +0 -0
  319. /package/dist/{chunk-SC2YAWRF.js.map → chunk-3TSTGI6F.js.map} +0 -0
  320. /package/dist/{chunk-FACTVMXQ.js.map → chunk-4H4F3BFL.js.map} +0 -0
  321. /package/dist/{chunk-WAFDHKES.js.map → chunk-4NOI577P.js.map} +0 -0
  322. /package/dist/{chunk-YQMPUYHE.js.map → chunk-4NR2PDZ5.js.map} +0 -0
  323. /package/dist/{chunk-TAPSNIMA.js.map → chunk-5CEJH5ZN.js.map} +0 -0
  324. /package/dist/{chunk-PKMQK2WY.js.map → chunk-773DPI6T.js.map} +0 -0
  325. /package/dist/{chunk-FXCIOLRB.js.map → chunk-ASKXIAHD.js.map} +0 -0
  326. /package/dist/{chunk-5OPYYPJN.js.map → chunk-CBWI22JN.js.map} +0 -0
  327. /package/dist/{chunk-ZOHMPFOB.js.map → chunk-CKFLLAIS.js.map} +0 -0
  328. /package/dist/{chunk-GZQXOQQ2.js.map → chunk-CSRXQ6NB.js.map} +0 -0
  329. /package/dist/{chunk-QR67FU5G.js.map → chunk-CTOQEZSN.js.map} +0 -0
  330. /package/dist/{chunk-ZUDM75KG.js.map → chunk-DCWIQFNA.js.map} +0 -0
  331. /package/dist/{chunk-6CWGS2LY.js.map → chunk-DKJCRGWM.js.map} +0 -0
  332. /package/dist/{chunk-JJJCVCCQ.js.map → chunk-DMQWV6SQ.js.map} +0 -0
  333. /package/dist/{chunk-F3XZN24Z.js.map → chunk-DRUDC2UX.js.map} +0 -0
  334. /package/dist/{chunk-4WZ3J7M6.js.map → chunk-EY6MDMHK.js.map} +0 -0
  335. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  336. /package/dist/{chunk-6SQQOJ4U.js.map → chunk-GKRLPHPU.js.map} +0 -0
  337. /package/dist/{chunk-D7EBTQ24.js.map → chunk-HOHVIVDR.js.map} +0 -0
  338. /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
  339. /package/dist/{chunk-UBHVAGO7.js.map → chunk-JYLFZWUX.js.map} +0 -0
  340. /package/dist/{chunk-LD53WPMU.js.map → chunk-LQ6JI4VH.js.map} +0 -0
  341. /package/dist/{chunk-WM7DNKBD.js.map → chunk-M4V2JVSZ.js.map} +0 -0
  342. /package/dist/{chunk-I2HL5M6T.js.map → chunk-MJIHJ7OG.js.map} +0 -0
  343. /package/dist/{chunk-J334N6QV.js.map → chunk-MLKWJOAJ.js.map} +0 -0
  344. /package/dist/{chunk-DF3I6JGP.js.map → chunk-NSA5ZRUL.js.map} +0 -0
  345. /package/dist/{chunk-VI5SGGXB.js.map → chunk-OEEW4KJA.js.map} +0 -0
  346. /package/dist/{chunk-NT36GU6X.js.map → chunk-R7IYU5DZ.js.map} +0 -0
  347. /package/dist/{chunk-TT5U3UDO.js.map → chunk-SNKPUJHR.js.map} +0 -0
  348. /package/dist/{chunk-T55EGZSJ.js.map → chunk-UVPL565C.js.map} +0 -0
  349. /package/dist/{chunk-YQGHW5ML.js.map → chunk-WRFKZEO6.js.map} +0 -0
  350. /package/dist/{chunk-H7HUQIY7.js.map → chunk-Y4YD5TYW.js.map} +0 -0
  351. /package/dist/{chunk-YUYPWJD3.js.map → chunk-YOLZS7NY.js.map} +0 -0
  352. /package/dist/{forget-OEE5OELK.js.map → forget-WYLR4ZFV.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/provenance.ts"],"sourcesContent":["/**\n * Claim-level provenance spans (issue #1575 PR 1).\n *\n * Centralizes the parse/serialize logic for the `sources` array and the\n * coarse `provenance` strength tag, plus the `provenance` config-block\n * parser. Extracted from `storage.ts` and `config.ts` so those files keep\n * only thin delegation call-sites — the frontmatter round-trip and config\n * parsing growth lives here (issue #1520 ratchet discipline).\n *\n * Contract:\n * - When no provenance fields are present, output is byte-identical to\n * pre-feature behavior (rule 39).\n * - Corrupt `sources` lines / unknown `provenance` tags drop to\n * `undefined` on read so a malformed frontmatter never poisons\n * downstream readers (rule 34 spirit — drop corrupt rather than poison).\n * - Validation lives on the write path; this module only parses.\n * - Invariant (review round 5, cursor thread KQN): a `verified` or\n * `unverified` tag is NEVER persisted/read without surviving sources —\n * without excerpts the tag is indistinguishable from a grounded fact to\n * downstream faithfulness/correction/TrustScore surfaces. The invariant\n * is enforced symmetrically: `serializeProvenanceFields` (write) and\n * `reconcileProvenanceRead` (read) both downgrade the tag to `none`\n * when no source survives.\n */\n\nimport { z } from \"zod\";\n\nimport { coerceBool, coerceNumber } from \"./connectors/coerce.js\";\nimport { readEnvVar } from \"./runtime/env.js\";\nimport { collapseWhitespace } from \"./whitespace.js\";\nimport type { MemoryFrontmatter, ProvenanceConfig, ProvenanceSource } from \"./types.js\";\nimport { isSafeMemoryContent } from \"./sanitize.js\";\n\n/**\n * Canonical key order for a serialized `ProvenanceSource` (issue #1575).\n * Deterministic emission (rule 38) — readers and the byte-identical-when-off\n * contract (rule 39) depend on this order never drifting.\n */\nconst PROVENANCE_SOURCE_KEY_ORDER = [\n \"sessionKey\",\n \"turnId\",\n \"observedAt\",\n \"quote\",\n \"charStart\",\n \"charEnd\",\n] as const;\n\n/**\n * Build a single `ProvenanceSource` object whose keys appear in the canonical\n * order, omitting absent optional fields. The result is what gets fed to\n * `JSON.stringify` so the on-disk line is deterministic.\n */\nfunction canonicalProvenanceSource(src: ProvenanceSource): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of PROVENANCE_SOURCE_KEY_ORDER) {\n const value = src[key];\n if (value !== undefined) out[key] = value;\n }\n return out;\n}\n\n/**\n * Serialize the `sources` array (issue #1575) as a single JSON line, matching\n * the `structuredAttributes` precedent. Each entry is rebuilt in canonical key\n * order (rule 38) so the output is byte-stable. The `provenance` enum is\n * emitted bare (same style as `status`) — only the three documented values\n * are ever written.\n *\n * Verified-requires-evidence invariant (review round 5, cursor thread KQN):\n * a `verified`/`unverified` tag is downgraded to `none` whenever no source\n * survives write validation — whether sources were absent, an empty array,\n * or all entries failed the schema. This covers all three failure shapes the\n * earlier 3-branch logic left open (`{provenance:\"verified\"}`,\n * `{sources:[],provenance:\"verified\"}`, `{sources:[invalid…],provenance:\"verified\"}`).\n */\nexport function serializeProvenanceFields(fm: MemoryFrontmatter, lines: string[]): void {\n let hasValidSources = false;\n if (fm.sources && fm.sources.length > 0) {\n // Validate each entry against the same schema used on read so invalid\n // in-memory sources are dropped at write time, not silently lost on the\n // next read (review thread 4 — write-path validation parity).\n const canonical: Record<string, unknown>[] = [];\n for (const src of fm.sources) {\n const result = ProvenanceSourceSchema.safeParse(src);\n if (result.success) canonical.push(canonicalProvenanceSource(result.data));\n }\n if (canonical.length > 0) {\n lines.push(`sources: ${JSON.stringify(canonical)}`);\n hasValidSources = true;\n }\n }\n // A verified/unverified tag requires surviving evidence; without it the\n // tag is meaningless downstream (faithfulness/TrustScore cannot distinguish\n // it from a grounded fact). Downgrade to \"none\" regardless of WHY no source\n // survived (absent / empty / all-invalid) — single invariant, all cases.\n const tag =\n (fm.provenance === \"verified\" || fm.provenance === \"unverified\") && !hasValidSources\n ? \"none\"\n : fm.provenance;\n if (tag) {\n lines.push(`provenance: ${tag}`);\n }\n}\n\n/**\n * Parse the coarse `provenance` strength tag (issue #1575). Returns\n * `undefined` for missing/blank/unknown values so a corrupt or hand-edited\n * field fails safely to the legacy-equivalent `\"none\"` semantics on read\n * (rule 34 spirit — drop corrupt rather than poison).\n */\nexport function parseProvenanceTag(\n raw: string | undefined,\n): \"verified\" | \"unverified\" | \"none\" | undefined {\n if (raw === undefined) return undefined;\n const trimmed = raw.trim();\n if (trimmed.length === 0) return undefined;\n if (trimmed === \"verified\" || trimmed === \"unverified\" || trimmed === \"none\") {\n return trimmed;\n }\n return undefined;\n}\n\n/**\n * Enforce the verified-requires-evidence invariant on the READ path.\n * `parseProvenanceTag` and `parseProvenanceSources` are independent (they\n * parse separate frontmatter lines), so a hand-edited or imported memory may\n * carry `provenance: verified` with no surviving `sources` — a corrupt line,\n * an empty array, or all-invalid entries. Downgrade such a tag to `none` so\n * the in-memory object never exposes an ungrounded \"verified\" fact\n * (review round 5, cursor thread KQN — read-path parity with the write-path\n * downgrade in `serializeProvenanceFields`). `none`/`undefined` tags pass\n * through unchanged.\n */\nexport function reconcileProvenanceRead(\n tag: \"verified\" | \"unverified\" | \"none\" | undefined,\n sources: ProvenanceSource[] | undefined,\n): \"verified\" | \"unverified\" | \"none\" | undefined {\n if ((tag === \"verified\" || tag === \"unverified\") && (!sources || sources.length === 0)) {\n return \"none\";\n }\n return tag;\n}\n\n/**\n * Strict ISO-8601 timestamp check (review round 6, codex thread OXPAp).\n * `Date.parse` accepts non-ISO strings (bare years like `\"123\"`) and\n * silently normalizes calendar overflow (`2026-02-30` -> March 2, hour 25\n * -> next day), so malformed provenance survives as if valid. Require the\n * full `YYYY-MM-DDTHH:MM:SS[.fff](Z|±HH:MM)` shape and reject overflow via a\n * `Date.UTC` round-trip component check — the offset does not affect whether\n * a wall-clock field overflows, so this is correct for any timezone suffix.\n */\nfunction isStrictIsoTimestamp(s: string): boolean {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/.exec(s);\n if (!m) return false;\n const y = Number(m[1]), mo = Number(m[2]), da = Number(m[3]);\n const h = Number(m[4]), mi = Number(m[5]), se = Number(m[6]);\n // Date.UTC normalizes overflow (Feb 30 -> Mar 2); a component round-trip\n // catches what Date.parse silently accepts.\n const d = new Date(Date.UTC(y, mo - 1, da, h, mi, se));\n // The component round-trip validates wall-clock overflow (Feb 30 -> Mar 2);\n // Date.parse additionally rejects impossible timezone offsets such as\n // +99:99, which the regex accepts but the runtime treats as NaN\n // (codex thread OXQ0e).\n return (\n !Number.isNaN(Date.parse(s)) &&\n d.getUTCFullYear() === y &&\n d.getUTCMonth() === mo - 1 &&\n d.getUTCDate() === da &&\n d.getUTCHours() === h &&\n d.getUTCMinutes() === mi &&\n d.getUTCSeconds() === se\n );\n}\n\n/**\n * Coerce a turn timestamp to a strict ISO-8601 string (cursor thread Ocveu —\n * \"Non-ISO turn timestamps drop sources\"). The write-path `ProvenanceSourceSchema`\n * rejects anything `isStrictIsoTimestamp` fails, so a turn whose `timestamp`\n * parses via `new Date(...)` but isn't already strict ISO (e.g.\n * `\"2026/01/15 12:00:00\"` or `\"Jan 15 2026\"`) would be silently dropped at\n * serialization — clearing the whole `sources` array and downgrading the tag\n * to `\"none\"`. Normalizing at extraction time preserves the source.\n *\n * Returns the strict ISO string when the timestamp already passes or\n * `Date.parse` can round-trip it; `undefined` for empty / unparseable input\n * so callers can decide whether to skip the source or fall back.\n */\nfunction toStrictIsoTimestamp(ts: string | undefined | null): string | undefined {\n if (typeof ts !== \"string\" || ts.length === 0) return undefined;\n if (isStrictIsoTimestamp(ts)) return ts;\n const parsed = Date.parse(ts);\n if (Number.isNaN(parsed)) return undefined;\n // Reject bare-year / numeric-only strings that Date.parse accepts (e.g.\n // \"123\") — isStrictIsoTimestamp already rejected them, and the round-trip\n // below would otherwise resurrect them. Require at least one date/time\n // separator so a plain number never round-trips into a fake epoch.\n if (!/[-:T]/.test(ts)) return undefined;\n // Reject calendar-overflow dates (chatgpt-codex-connector thread dANc):\n // Date.parse silently rolls over invalid components — \"2026-02-30\" becomes\n // 2026-03-02 — fabricating the observation date. For strings that carry an\n // explicit numeric Y/M/D prefix (the common import/provider shape), validate\n // the calendar components are in range before accepting the shifted result.\n // isStrictIsoTimestamp already does this for full ISO strings; this closes\n // the gap for the non-ISO normalization path.\n const ymd = /^(\\d{4})\\D(\\d{1,2})\\D(\\d{1,2})/.exec(ts);\n if (ymd) {\n const [_, ys, ms, ds] = ymd;\n const y = Number(ys), mo = Number(ms), da = Number(ds);\n if (!isValidCalendarDate(y, mo, da)) return undefined;\n }\n // Also validate M/D/Y or D/M/Y formats (provider/import common shapes:\n // 02/30/2026, 30-02-2026, etc.). Date.parse silently shifts overflow in\n // these too. Try both month-first and day-first interpretations; if\n // neither is a valid calendar date, reject (chatgpt-codex-connector thread\n // dH47 — non-YMD overflow timestamps).\n const mdy = /^(\\d{1,2})\\D(\\d{1,2})\\D(\\d{4})/.exec(ts);\n if (mdy) {\n const a = Number(mdy[1]), b = Number(mdy[2]), yr = Number(mdy[3]);\n if (!isValidCalendarDate(yr, a, b) && !isValidCalendarDate(yr, b, a)) {\n return undefined;\n }\n }\n const iso = new Date(parsed).toISOString();\n return isStrictIsoTimestamp(iso) ? iso : undefined;\n}\n\n/**\n * Validate numeric calendar components without Date overflow (review thread\n * dANc). Date.UTC silently normalizes Feb 30 -> Mar 2; a component round-trip\n * catches what Date.parse accepts. Mirrors the same technique isStrictIsoTimestamp\n * uses for full ISO strings, applied here to the non-ISO normalization path.\n */\nfunction isValidCalendarDate(y: number, mo: number, da: number): boolean {\n if (mo < 1 || mo > 12 || da < 1) return false;\n const leap = (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0)) ? 29 : 28;\n const daysInMonth = [31, leap, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n return da <= daysInMonth[mo - 1]!;\n}\n\n/**\n * Zod schema for a single `ProvenanceSource` entry (issue #1575). Parsed\n * JSON from frontmatter is external data, so each entry is validated here\n * rather than trusted via a cast (rule: no inline-cast-access on parsed\n * blobs). `safeParse` lets us drop corrupt entries individually instead of\n * failing the whole field.\n */\nconst ProvenanceSourceSchema = z\n .object({\n sessionKey: z.string().min(1),\n observedAt: z\n .string()\n .min(1)\n .refine(isStrictIsoTimestamp, \"must be a valid ISO 8601 timestamp (YYYY-MM-DDTHH:MM:SS[Z|±HH:MM], no calendar overflow)\"),\n quote: z.string().min(1),\n turnId: z.string().min(1).optional(),\n charStart: z.number().finite().nonnegative().int().optional(),\n charEnd: z.number().finite().nonnegative().int().optional(),\n })\n .refine(\n (src) => src.charStart === undefined || src.charEnd === undefined || src.charEnd >= src.charStart,\n { message: \"charEnd must be >= charStart (half-open interval, rule 35)\" },\n );\n\n/**\n * Parse the `sources` array (issue #1575) from its single-line JSON form.\n * Mirrors `parseStructuredAttributes` (JSON.parse) but validates each entry\n * against `ProvenanceSourceSchema` and DROPS corrupt ones rather than\n * poisoning downstream readers — the same \"drop corrupt rather than poison\"\n * contract as `parseMemoryWorthCounterField`. If no entry survives, the\n * whole field is `undefined` (legacy-equivalent).\n */\nexport function parseProvenanceSources(raw: string | undefined): ProvenanceSource[] | undefined {\n if (raw === undefined) return undefined;\n const trimmed = raw.trim();\n if (trimmed.length === 0) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n return undefined;\n }\n if (!Array.isArray(parsed)) return undefined;\n const sources: ProvenanceSource[] = [];\n for (const entry of parsed) {\n const result = ProvenanceSourceSchema.safeParse(entry);\n if (result.success) sources.push(result.data);\n }\n return sources.length > 0 ? sources : undefined;\n}\n\n/**\n * Parse the `provenance` config block (issue #1575 PR 1). Validates the\n * shape before applying defaults — a shorthand like `provenance: false` must\n * reject loudly rather than normalize to `{}` and silently enable the feature\n * (rule 51). Booleans coerce via `coerceBool` (rule 36); numeric cap clamps\n * at 1 (rule 28). `REMNIC_PROVENANCE_ENABLED` / `ENGRAM_PROVENANCE_ENABLED`\n * are honored only when the `enabled` key is omitted (explicit config wins).\n *\n * Schema-default note (review rounds 1–3, settled): `provenance.enabled` has\n * NO `\"default\"` in any plugin.json schema. OpenClaw's loader runs\n * `applyDefaults: true` before exposing `api.pluginConfig` (src/index.ts:1345,\n * PR #1593 round 8), so a schema default would be materialized into the\n * merged config and mask the `REMNIC_`/`ENGRAM_` env override. The code-level\n * default-on here (`return true` when `enabled` is omitted) supplies the\n * default-on behavior without that materialization. This matches the\n * emitLegacyTools/namespaceCatalogEnabled precedent, which omits the env\n * override only after a raw-vs-effective split — overkill for a single\n * boolean, so this field uses the simpler omit-the-default approach.\n */\nexport function parseProvenanceConfig(raw: unknown): ProvenanceConfig {\n if (\n raw !== undefined &&\n (raw === null || typeof raw !== \"object\" || Array.isArray(raw))\n ) {\n throw new Error(\n `provenance must be an object (got ${JSON.stringify(raw)}). Use provenance: { enabled: false } to opt out; omit the key to use the default-on behavior (issue #1575).`,\n );\n }\n const rawProvenance =\n raw && typeof raw === \"object\" && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : {};\n return {\n enabled: (() => {\n if (rawProvenance.enabled === undefined) {\n const envEnabled =\n readEnvVar(\"REMNIC_PROVENANCE_ENABLED\") ?? readEnvVar(\"ENGRAM_PROVENANCE_ENABLED\");\n if (envEnabled !== undefined) {\n const coerced = coerceBool(envEnabled);\n if (coerced === undefined) {\n throw new Error(\n `REMNIC_PROVENANCE_ENABLED must be a boolean-like value (true/false/1/0/yes/no/on/off); got ${JSON.stringify(envEnabled)}`,\n );\n }\n return coerced;\n }\n return true;\n }\n const coerced = coerceBool(rawProvenance.enabled);\n if (coerced === undefined) {\n throw new Error(\n `provenance.enabled must be a boolean or one of \"true\"/\"false\"/\"1\"/\"0\"/\"yes\"/\"no\"/\"on\"/\"off\" (got ${JSON.stringify(rawProvenance.enabled)}). Omit the key to use the default-on behavior (issue #1575).`,\n );\n }\n return coerced;\n })(),\n maxQuoteChars: (() => {\n if (rawProvenance.maxQuoteChars === undefined) return 300;\n const rawCap = coerceNumber(rawProvenance.maxQuoteChars);\n // Reject present-but-invalid rather than silently widening the cap\n // (AGENTS.md input-validation rule — a typo should not persist more\n // text than the operator configured).\n if (rawCap === undefined || !Number.isFinite(rawCap) || rawCap < 1 || !Number.isInteger(rawCap)) {\n throw new Error(\n `provenance.maxQuoteChars must be a positive integer >= 1 (got ${JSON.stringify(rawProvenance.maxQuoteChars)}).`,\n );\n }\n return rawCap;\n })(),\n requireSpans: (() => {\n if (rawProvenance.requireSpans === undefined) return false;\n const coerced = coerceBool(rawProvenance.requireSpans);\n if (coerced === undefined) {\n throw new Error(\n `provenance.requireSpans must be a boolean or one of \"true\"/\"false\"/\"1\"/\"0\"/\"yes\"/\"no\"/\"on\"/\"off\" (got ${JSON.stringify(rawProvenance.requireSpans)}).`,\n );\n }\n return coerced;\n })(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Issue #1575 PR 2 — extraction-side post-parse validator.\n// ---------------------------------------------------------------------------\n//\n// This runs once at write time (inside `ExtractionEngine.extract`, after the\n// LLM output is parsed and sanitized, before the result is returned for\n// persistence). Its job: locate each fact's LLM-provided `quote` in the\n// buffered turn texts and build a `ProvenanceSource[]` with verified\n// offsets. Never throws, never drops a fact (rule 34 spirit — an\n// unverifiable span is a tagged state, not a silent failure).\n//\n// Reuses `collapseWhitespace` from `whitespace.ts` for normalization so there\n// is exactly one normalizer in the codebase (issue #1575 pitfall: \"do not\n// write a second normalizer\").\n// ---------------------------------------------------------------------------\n\n/**\n * A turn in the buffered conversation, reduced to the fields the provenance\n * validator needs. `ExtractionEngine.extract` maps its `BufferTurn[]` to this\n * shape so the validator is pure and testable without the full BufferTurn type.\n */\nexport interface ProvenanceTurnInput {\n content: string;\n sessionKey?: string;\n logicalSessionKey?: string;\n timestamp: string;\n turnId?: string;\n}\n\n/**\n * Result of building provenance for a single extracted fact.\n */\nexport interface ProvenanceBuildResult {\n /** Verified sources (one per matching turn). Absent when no quote survives. */\n sources?: ProvenanceSource[];\n /** Coarse strength tag persisted to frontmatter. */\n provenance: \"verified\" | \"unverified\" | \"none\";\n /**\n * Transient signal (never persisted): `true` when `config.requireSpans`\n * is enabled and the LLM-provided quote could not be located in ANY source\n * turn (the strict case `ProvenanceConfig.requireSpans` documents: \"facts\n * whose quote cannot be located are routed to `pending_review`\"). The\n * extraction consumer carries this onto the in-memory `ExtractedFact` so\n * the persist path can route the fact to the review queue. A quote that\n * WAS located but whose source was dropped (e.g. un-coercible timestamp)\n * does NOT set this flag — the span was found, so requireSpans is\n * satisfied (chatgpt-codex-connector thread 4xB).\n */\n requireSpansPending?: boolean;\n}\n\n/**\n * Cap a quote string at `maxChars`, truncating at the last word boundary that\n * fits and appending an ellipsis marker. Quotes at or under the cap pass\n * through unchanged. Operates on Unicode code points (not UTF-16 units) so\n * emoji and astral-plane characters are not split mid-glyph.\n */\nfunction capQuote(quote: string, maxChars: number): string {\n const glyphs = Array.from(quote);\n if (glyphs.length <= maxChars) return quote;\n // Walk backward from the cap to find a word boundary (space). If the entire\n // span is one long token (no spaces), cut at the cap — a hard cut is better\n // than no quote at all.\n let cut = maxChars;\n while (cut > 0 && !/\\s/.test(glyphs[cut - 1]!)) cut--;\n if (cut === 0) cut = maxChars; // no word boundary found\n return glyphs.slice(0, cut).join(\"\").trimEnd() + \"\\u2026\";\n}\n\n/**\n * Casefold a string for normalized matching. Uses `toLowerCase` (not\n * `toLocaleLowerCase`) for determinism across runtime locales — the existing\n * `normalizeFactKey` helper in extraction.ts follows the same convention.\n */\nfunction casefold(s: string): string {\n return s.toLowerCase();\n}\n\n/**\n * Locate `quote` within `text`, returning whether a match was found and, when\n * recoverable, the half-open `[start, end)` offsets. Tries exact substring\n * first, then whitespace/case-normalized match.\n *\n * For the normalized path, the mapping from the normalized string back to\n * original offsets is recovered by walking both strings in lockstep: we know\n * `collapseWhitespace(text)` is a subsequence of `text` with whitespace runs\n * collapsed, so we can track the original offset as we scan.\n *\n * Returns `{ matched: false }` when neither match succeeds. Returns\n * `{ matched: true, offsets }` when offsets are recoverable. Returns\n * `{ matched: true }` (offsets omitted) when the normalized substring was\n * found but original offsets could not be recovered — callers still treat\n * this as a verified match and record a source without offsets (cursor\n * thread Ocver — \"Normalized match drops verified provenance\").\n */\ntype LocateQuoteResult =\n | { matched: false }\n | { matched: true; offsets?: { charStart: number; charEnd: number } };\n\nfunction locateQuoteOffsets(quote: string, text: string): LocateQuoteResult {\n // 1. Exact substring match (handles unicode, curly quotes, emoji verbatim).\n const exactIdx = text.indexOf(quote);\n if (exactIdx >= 0) {\n return { matched: true, offsets: { charStart: exactIdx, charEnd: exactIdx + quote.length } };\n }\n\n // 2. Whitespace/case-normalized match. Collapse runs of whitespace and\n // casefold both sides, then find the normalized quote in the normalized\n // text. Recover original offsets by scanning forward from the normalized\n // match start through the original text, accumulating non-whitespace\n // glyphs until we've consumed the normalized quote length.\n const normQuote = collapseWhitespace(casefold(quote));\n if (normQuote.length === 0) return { matched: false };\n const normText = collapseWhitespace(casefold(text));\n const normIdx = normText.indexOf(normQuote);\n if (normIdx < 0) return { matched: false };\n\n // Recover original offsets: walk the original text, skipping leading\n // whitespace to align with the collapsed form, then track how many\n // normalized chars we've consumed.\n const normQuoteLen = normQuote.length;\n let origIdx = 0;\n // Skip leading whitespace in original text (collapseWhitespace trims both ends).\n while (origIdx < text.length && /\\s/.test(text[origIdx]!)) origIdx++;\n // Walk through original text, counting normalized chars consumed.\n // Each non-whitespace char in original = 1 normalized char.\n // Whitespace runs in original = 1 space in normalized (but only between non-ws).\n let normPos = 0;\n let origStart = -1;\n let origEnd = -1;\n let prevWasWs = true; // suppress the leading space in collapsed form\n while (origIdx < text.length && origEnd < 0) {\n const ch = text[origIdx]!;\n if (/\\s/.test(ch)) {\n if (!prevWasWs) {\n // This whitespace run collapses to a single space in normalized text.\n if (normPos === normIdx) origStart = origIdx; // normalized space aligns\n normPos++;\n prevWasWs = true;\n }\n origIdx++;\n continue;\n }\n // Non-whitespace char.\n if (normPos === normIdx && origStart < 0) {\n origStart = origIdx;\n }\n normPos++;\n prevWasWs = false;\n origIdx++;\n if (normPos >= normIdx + normQuoteLen) {\n origEnd = origIdx;\n }\n }\n if (origStart >= 0 && origEnd >= 0) {\n return { matched: true, offsets: { charStart: origStart, charEnd: origEnd } };\n }\n // Normalized substring was found, but original offsets are not recoverable\n // (edge case in the normalized mapping). The match still counts as verified\n // — record a source without offsets rather than dropping the turn entirely\n // (cursor thread Ocver). charStart/charEnd are best-effort debugging aids,\n // not a precondition for a verified source.\n return { matched: true };\n}\n\n/**\n * Build provenance sources for a single extracted fact by locating its\n * LLM-provided `quote` in the buffered turns (issue #1575 PR 2).\n *\n * Matching strategy (per issue design):\n * 1. Exact substring match in a turn → `provenance: \"verified\"` with\n * `charStart`/`charEnd` offsets.\n * 2. Whitespace/case-normalized match → `\"verified\"`, offsets recovered\n * when possible, else omitted.\n * 3. No match in any turn → `\"unverified\"` — the quote survives as a\n * source (the LLM vouched for it) but without located offsets.\n * 4. No quote provided by the LLM → `\"none\"` — no evidence to record.\n *\n * A quote appearing in multiple turns produces multiple sources (one per\n * matching turn) so a repeated utterance backs the fact from each occurrence.\n *\n * The quote is capped at `config.maxQuoteChars` (truncated at a word boundary\n * with an ellipsis marker) BEFORE storage. Locating uses the original\n * (untruncated) quote for maximum match fidelity; the capped excerpt is what\n * gets persisted.\n *\n * When `config.enabled === false`, returns `{ provenance: \"none\" }`\n * immediately — byte-identical to pre-feature extraction (rule 39).\n *\n * Never throws. An unexpected error degrades to `{ provenance: \"none\" }` so\n * extraction never crashes on a provenance hiccup (rule 13/18).\n */\n/**\n * Strip a leading extraction-prompt role label from a quote (cursor thread Oc3Z2\n * — \"Quote prompt mismatches validator\"). The extraction prompt renders each\n * buffered turn as `[role] content` (or `[context role] content`), so a\n * faithful LLM may include that prefix in its verbatim quote. buildFactProvenance\n * searches the raw `turn.content` (no prefix), so a quote carrying the label\n * would never match. Stripping the leading label lets the actual utterance\n * verify against the turn text. Only a SINGLE leading label is stripped — a\n * multi-turn quote (with embedded labels) is left untouched and will simply\n * fail to match a single turn, as before.\n *\n * The regex is constrained to the labels the prompt actually emits — `user`,\n * `assistant`, optionally prefixed with `context ` (extraction.ts renders\n * `[user]`, `[assistant]`, `[context user]`, `[context assistant]`). A\n * real utterance that happens to start with other bracketed text (e.g.\n * `[do not] deploy before approval`, `[P1] fix the cache`) is preserved\n * verbatim so the quote can match the turn and the persisted span retains its\n * original meaning (chatgpt-codex-connector thread 4xA — limiting role-prefix\n * stripping to actual prompt labels).\n */\nfunction stripLeadingRolePrefix(quote: string): string {\n return quote.replace(/^\\s*\\[(?:context\\s+)?(?:user|assistant)\\]\\s+/i, \"\");\n}\n\nexport function buildFactProvenance(\n factQuote: string | null | undefined,\n turns: ReadonlyArray<ProvenanceTurnInput>,\n config: ProvenanceConfig,\n): ProvenanceBuildResult {\n if (!config.enabled) return { provenance: \"none\" };\n const rawQuote = typeof factQuote === \"string\" ? factQuote.trim() : \"\";\n // requireSpans (chatgpt-codex-connector thread dEsu + cursor thread dGKJ):\n // every early exit that drops a fact's span (no quote, empty after label\n // strip, or unsafe quote) must flag requireSpansPending when the operator\n // opted into requireSpans, so the persist path routes the fact to\n // pending_review instead of active. Only the disabled-feature exit (above)\n // and the unexpected-error catch (below) omit the flag — those are\n // policy-neutral degradations, not a missing-span decision.\n const noneResult = (): ProvenanceBuildResult =>\n config.requireSpans === true\n ? { provenance: \"none\", requireSpansPending: true }\n : { provenance: \"none\" };\n if (rawQuote.length === 0) return noneResult();\n // Strip a leading prompt role label so a faithful quote verifies against\n // the raw turn content (cursor thread Oc3Z2). Prefer the RAW quote when it\n // matches at least one turn — an utterance that literally begins with\n // [user]/[assistant] must verify as-is, not be truncated to the post-label\n // text (cursor/codex thread dEsw). The strip handles the common case where\n // the LLM includes the prompt label; this preserves the rare case where the\n // utterance itself starts with that text.\n const strippedQuote = stripLeadingRolePrefix(rawQuote);\n const useRawQuote =\n rawQuote === strippedQuote ||\n turns.some(\n (t) =>\n typeof t?.content === \"string\" &&\n t.content.length > 0 &&\n locateQuoteOffsets(rawQuote, t.content).matched,\n );\n const quote = useRawQuote ? rawQuote : strippedQuote;\n if (quote.length === 0) return noneResult();\n // Sanitize the quote before persisting it as a provenance span\n // (chatgpt-codex-connector thread dANZ): the fact body is sanitized via\n // sanitizeMemoryContent, but sources[].quote was persisted verbatim,\n // reintroducing unsafe memory text (e.g. \"ignore previous instructions\")\n // through the provenance field exposed to memory_get/x-ray/faithfulness.\n // An unsafe quote cannot serve as evidence — drop the source entirely\n // (consistent with how the body is sanitized: unsafe text is redacted,\n // and a redacted quote is useless as a verbatim span).\n if (!isSafeMemoryContent(quote)) return noneResult();\n\n try {\n // Search every turn for the quote. Collect verified sources.\n const sources: ProvenanceSource[] = [];\n // Track the first turn where the quote was located even when its\n // timestamp can't be coerced (cursor thread 4Pj — \"Bad timestamp drops\n // matched source\"). Without this, a located-but-unverifiable quote falls\n // through to the unverified branch and is attributed to the *last* turn's\n // session, mislabeling the source's origin session. Also drives the\n // requireSpans signal: only a quote that was NOT located in any turn\n // qualifies for pending_review routing under requireSpans (thread 4xB).\n let locatedTurn: ProvenanceTurnInput | undefined;\n for (const turn of turns) {\n if (!turn || typeof turn.content !== \"string\" || turn.content.length === 0) continue;\n const located = locateQuoteOffsets(quote, turn.content);\n // cursor thread Ocver: a normalized match counts as verified even when\n // original offsets are unrecoverable — record the source without\n // charStart/charEnd instead of skipping the turn.\n if (!located.matched) continue;\n if (!locatedTurn) locatedTurn = turn;\n // cursor thread Ocveu: normalize the turn timestamp to strict ISO so\n // the write-path ProvenanceSourceSchema keeps the source. Skip the turn\n // when the timestamp can't be coerced — pushing it would guarantee a\n // serialization drop (and the tag downgrade) for this source.\n const observedAt = toStrictIsoTimestamp(turn.timestamp);\n if (!observedAt) continue;\n sources.push({\n sessionKey: turn.sessionKey ?? turn.logicalSessionKey ?? \"unknown\",\n ...(turn.turnId ? { turnId: turn.turnId } : {}),\n observedAt,\n quote: capQuote(quote, config.maxQuoteChars),\n ...(located.offsets\n ? { charStart: located.offsets.charStart, charEnd: located.offsets.charEnd }\n : {}),\n });\n }\n\n if (sources.length > 0) {\n return { sources, provenance: \"verified\" };\n }\n\n // Quote provided but could not be turned into a verified source.\n // Determine the session to attribute: prefer the turn where the quote was\n // LOCATED even if its timestamp couldn't be coerced (cursor thread 4Pj — a\n // located quote must not be tied to the *last* turn's session). If the\n // quote was not located in any turn at all, fall back to the last turn's\n // session (the documented unverified behavior: the LLM vouched for the\n // excerpt but we couldn't pin it to a character offset). Normalize the\n // timestamp (thread Ocveu); fall back to epoch when no turn supplies a\n // coercible timestamp so the unverified source still survives the\n // write-path schema.\n const fallbackTurn = locatedTurn ?? turns[turns.length - 1];\n const fallbackSessionKey = fallbackTurn\n ? (fallbackTurn.sessionKey ?? fallbackTurn.logicalSessionKey ?? \"unknown\")\n : \"unknown\";\n const fallbackObservedAt = fallbackTurn\n ? (toStrictIsoTimestamp(fallbackTurn.timestamp) ?? new Date(0).toISOString())\n : new Date(0).toISOString();\n // requireSpans signal (chatgpt-codex-connector thread 4xB): when an\n // operator opts into provenance.requireSpans, a fact whose quote could\n // not be located in ANY turn (locatedTurn undefined) is flagged so the\n // persist path routes it to pending_review instead of active. A quote\n // that WAS located (locatedTurn set) satisfies requireSpans even when\n // its source was dropped for an un-coercible timestamp — the span was\n // found, so the fact has the grounding requireSpans demands.\n const requireSpansPending =\n config.requireSpans === true && locatedTurn === undefined;\n return {\n sources: [\n {\n sessionKey: fallbackSessionKey,\n observedAt: fallbackObservedAt,\n quote: capQuote(quote, config.maxQuoteChars),\n },\n ],\n provenance: \"unverified\",\n ...(requireSpansPending ? { requireSpansPending: true } : {}),\n };\n } catch {\n // Never crash extraction on a provenance error (rule 13/18).\n return { provenance: \"none\" };\n }\n}\n"],"mappings":"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","names":["coerced"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/local-model-endpoint.ts"],"sourcesContent":["/**\n * Local fine-tuned model endpoint resolver + openai-compatible caller\n * (issue #1585 model-lab integration).\n *\n * Remnic's two classification workloads — the extraction faithfulness gate\n * (#1576) and passive-correction intent detection (#1581) — can run on small\n * fine-tuned models served locally (Ollama / vLLM) instead of prompted\n * frontier LLMs. The model-lab (`model-lab/`) produces those models; this\n * module is the config-side half: it resolves the optional local endpoint\n * pointer from config and, when set, makes a minimal openai-compatible\n * chat-completions call to it.\n *\n * Design:\n * - **Pointers are optional.** Empty defaults preserve the existing routing\n * chain exactly (rule 39 / byte-identical pre-feature). A resolver returns\n * `null` when the pointer is unset, so callers short-circuit before any\n * network.\n * - **Graceful degradation.** A local-endpoint failure (network, non-2xx,\n * malformed body, timeout) returns `null`; callers fall back to the\n * configured chain (checklist §4 — never block writes on a classifier).\n * - **No inline casts.** The fetch JSON is narrowed with `in` / `typeof`\n * guards (project rule: never assert a shape to read one property).\n * - **Injectable fetch.** `callOpenAiCompatibleChat` takes an optional\n * `fetchImpl` so the caller is unit-testable without a live server.\n */\n\nimport type { PluginConfig } from \"./types.js\";\n\n/** A resolved local endpoint: where to call + which model to name. */\nexport interface LocalModelEndpoint {\n /** Base URL, e.g. `http://localhost:11434/v1` (no trailing slash required). */\n baseUrl: string;\n /** Model name the server expects, e.g. `remnic-faithfulness-gate-v1`. */\n model: string;\n}\n\n/** A chat message in the openai-compatible shape this caller sends. */\nexport interface EndpointChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\n/** Options for a single local-endpoint chat call. */\nexport interface EndpointChatOptions {\n /** Per-call timeout in ms. The caller's batch AbortSignal is NOT forwarded\n * (matches the local-LLM precedent: each attempt is bounded by its own\n * timer so one slow server can't starve the batch). */\n timeoutMs: number;\n /** Sampling temperature — classification wants low entropy (0.0–0.2). */\n temperature?: number;\n /** Max output tokens. */\n maxTokens?: number;\n /** Request JSON-object output (the gate emits a JSON verdict array). */\n responseFormatJson?: boolean;\n}\n\n/** Result of a local-endpoint chat call. */\nexport interface EndpointChatResult {\n content: string;\n /** The model the server reports it used (falls back to the requested name). */\n modelUsed: string;\n}\n\n/**\n * Resolve the faithfulness-gate local endpoint from config (issue #1585).\n *\n * Returns the endpoint only when BOTH a base URL and a model name are\n * configured; otherwise `null` (gate uses its existing routing chain). The\n * gate's verifier needs a model identity, so a base URL alone is intentionally\n * insufficient — silently calling whatever the server's default model is would\n * be an unreviewed behavioral change.\n */\nexport function resolveFaithfulnessGateEndpoint(\n config: Pick<PluginConfig, \"extractionFaithfulnessModel\" | \"extractionFaithfulnessBaseUrl\">,\n): LocalModelEndpoint | null {\n const baseUrl = config.extractionFaithfulnessBaseUrl.trim();\n const model = config.extractionFaithfulnessModel.trim();\n if (!baseUrl || !model) return null;\n return { baseUrl, model };\n}\n\n/**\n * Resolve the correction-intent local endpoint from config (issue #1581/#1585).\n *\n * Same contract as the gate resolver. Consumption by the model-backed\n * detector is the consuming child's job — the rule-based\n * `detectPassiveCorrections` remains the default path; this pointer lets a\n * future model-backed detector route to a fine-tuned local classifier when an\n * operator configures one.\n */\nexport function resolveCorrectionIntentEndpoint(\n config: Pick<PluginConfig, \"correctionIntentModel\" | \"correctionIntentBaseUrl\">,\n): LocalModelEndpoint | null {\n const baseUrl = config.correctionIntentBaseUrl.trim();\n const model = config.correctionIntentModel.trim();\n if (!baseUrl || !model) return null;\n return { baseUrl, model };\n}\n\ntype FetchLike = typeof fetch;\n\n/**\n * Make an openai-compatible chat-completions call to a local endpoint.\n *\n * POSTs to `${baseUrl}/chat/completions` with `{ model, messages, ... }` and\n * returns the first choice's content. Any failure (non-2xx, network error,\n * malformed body, timeout) returns `null` so callers fall back gracefully.\n *\n * The response is narrowed with runtime guards — never an unchecked cast — so\n * a surprising server response fails closed (`null`) rather than reading a\n * fabricated field.\n */\nexport async function callOpenAiCompatibleChat(\n endpoint: LocalModelEndpoint,\n messages: EndpointChatMessage[],\n options: EndpointChatOptions,\n fetchImpl: FetchLike = fetch,\n): Promise<EndpointChatResult | null> {\n const url = joinBaseUrl(endpoint.baseUrl, \"/chat/completions\");\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), options.timeoutMs);\n try {\n const body: Record<string, unknown> = {\n model: endpoint.model,\n messages,\n temperature: options.temperature ?? 0.1,\n };\n if (typeof options.maxTokens === \"number\") {\n body.max_tokens = options.maxTokens;\n }\n if (options.responseFormatJson) {\n body.response_format = { type: \"json_object\" };\n }\n let response: Response;\n try {\n response = await fetchImpl(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch {\n // Network error or abort — fail closed, let the caller fall back.\n return null;\n }\n if (!response.ok) return null;\n const parsed: unknown = await response.json().catch(() => null);\n const content = extractFirstChoiceContent(parsed);\n if (content === null) return null;\n return { content, modelUsed: extractModel(parsed) ?? endpoint.model };\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Join a base URL and a path, tolerating a trailing slash on the base. */\nfunction joinBaseUrl(base: string, path: string): string {\n return base.endsWith(\"/\") ? `${base.slice(0, -1)}${path}` : `${base}${path}`;\n}\n\n/**\n * Narrow the openai-compatible response and pull the first choice's content.\n *\n * `in` / `typeof` guards only — no `as` cast (project rule). Returns `null`\n * for any shape that isn't `{ choices: [{ message: { content: string } }, …] }`.\n */\nfunction extractFirstChoiceContent(data: unknown): string | null {\n if (!data || typeof data !== \"object\") return null;\n // After `in`, TS narrows the property to `unknown` — read it directly, no cast.\n if (!(\"choices\" in data) || !Array.isArray(data.choices)) return null;\n const choices: unknown[] = data.choices;\n for (const choice of choices) {\n if (!choice || typeof choice !== \"object\") continue;\n if (!(\"message\" in choice)) continue;\n const message = choice.message;\n if (!message || typeof message !== \"object\") continue;\n if (!(\"content\" in message)) continue;\n const content = message.content;\n if (typeof content === \"string\" && content.length > 0) return content;\n }\n return null;\n}\n\n/** Narrow the `model` field from the response (best-effort, may be absent). */\nfunction extractModel(data: unknown): string | null {\n if (!data || typeof data !== \"object\" || !(\"model\" in data)) return null;\n const model = data.model;\n return typeof model === \"string\" && model.length > 0 ? model : null;\n}\n"],"mappings":";AAwEO,SAAS,gCACd,QAC2B;AAC3B,QAAM,UAAU,OAAO,8BAA8B,KAAK;AAC1D,QAAM,QAAQ,OAAO,4BAA4B,KAAK;AACtD,MAAI,CAAC,WAAW,CAAC,MAAO,QAAO;AAC/B,SAAO,EAAE,SAAS,MAAM;AAC1B;AAWO,SAAS,gCACd,QAC2B;AAC3B,QAAM,UAAU,OAAO,wBAAwB,KAAK;AACpD,QAAM,QAAQ,OAAO,sBAAsB,KAAK;AAChD,MAAI,CAAC,WAAW,CAAC,MAAO,QAAO;AAC/B,SAAO,EAAE,SAAS,MAAM;AAC1B;AAeA,eAAsB,yBACpB,UACA,UACA,SACA,YAAuB,OACa;AACpC,QAAM,MAAM,YAAY,SAAS,SAAS,mBAAmB;AAC7D,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,QAAQ,SAAS;AACpE,MAAI;AACF,UAAM,OAAgC;AAAA,MACpC,OAAO,SAAS;AAAA,MAChB;AAAA,MACA,aAAa,QAAQ,eAAe;AAAA,IACtC;AACA,QAAI,OAAO,QAAQ,cAAc,UAAU;AACzC,WAAK,aAAa,QAAQ;AAAA,IAC5B;AACA,QAAI,QAAQ,oBAAoB;AAC9B,WAAK,kBAAkB,EAAE,MAAM,cAAc;AAAA,IAC/C;AACA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,UAAU,KAAK;AAAA,QAC9B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,QAAQ;AAEN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,SAAS,GAAI,QAAO;AACzB,UAAM,SAAkB,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AAC9D,UAAM,UAAU,0BAA0B,MAAM;AAChD,QAAI,YAAY,KAAM,QAAO;AAC7B,WAAO,EAAE,SAAS,WAAW,aAAa,MAAM,KAAK,SAAS,MAAM;AAAA,EACtE,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AACF;AAGA,SAAS,YAAY,MAAc,MAAsB;AACvD,SAAO,KAAK,SAAS,GAAG,IAAI,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;AAC5E;AAQA,SAAS,0BAA0B,MAA8B;AAC/D,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAE9C,MAAI,EAAE,aAAa,SAAS,CAAC,MAAM,QAAQ,KAAK,OAAO,EAAG,QAAO;AACjE,QAAM,UAAqB,KAAK;AAChC,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU;AAC3C,QAAI,EAAE,aAAa,QAAS;AAC5B,UAAM,UAAU,OAAO;AACvB,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU;AAC7C,QAAI,EAAE,aAAa,SAAU;AAC7B,UAAM,UAAU,QAAQ;AACxB,QAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAAG,QAAO;AAAA,EAChE;AACA,SAAO;AACT;AAGA,SAAS,aAAa,MAA8B;AAClD,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,EAAE,WAAW,MAAO,QAAO;AACpE,QAAM,QAAQ,KAAK;AACnB,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/correction/correction-contract.ts","../src/correction/correction-access-wiring.ts","../src/correction/correction-planner.ts","../src/correction/correction-executor.ts","../src/correction/correction-service.ts","../src/correction/passive-correction-detector.ts","../src/correction/passive-capture.ts","../src/procedural/reinforcement-core.ts"],"sourcesContent":["/**\n * correction/correction-contract.ts — Types for the Correction Contract\n * (issue #1580).\n *\n * The Correction Contract is the SINGLE plan/apply pipeline every memory\n * correction flows through: supersession, invalidation, tombstone, edit,\n * rescope, redaction. A correction arrives as a plain-language statement\n * (\"we migrated to MySQL in March\"); the planner turns it into a structured\n * {@link CorrectionPlan}; the executor applies the plan in non-destructive\n * order through the existing storage/orchestrator chokepoints.\n *\n * This module is PURE types + validation helpers — no I/O, no side effects.\n * The planner and executor live in sibling modules and inject their own\n * collaborators so the contract is testable in isolation (rule 33).\n *\n * Design rules honored (issue #1580 design section):\n * - Plans are per-request state on disk, never module-level (rules 11/47).\n * - Caller-supplied namespaces are NEVER trusted raw — the service\n * resolves them through the normal namespace policy (rule 42).\n * - Bulk operations refuse past maxAffected (no silent truncation, §39).\n * - `never_store` redaction patterns must be bounded and safe (§34).\n */\n\nimport type { MemoryCategory, MemoryFrontmatter } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Request\n// ---------------------------------------------------------------------------\n\n/**\n * The user-facing input: a plain-language correction plus optional explicit\n * targets. This is what every surface (MCP / HTTP / CLI) collects before\n * handing off to the planner.\n */\nexport interface CorrectionRequest {\n /** Natural-language correction statement. Required, non-empty. */\n text: string;\n /**\n * Explicit target memory ids (or handles via #1582). When present the\n * planner resolves these directly; an unknown id is an explicit error\n * (rule 34), never a silent empty plan. When absent the planner searches.\n */\n targetIds?: string[];\n /** Session key for namespace/principal resolution. */\n sessionKey?: string;\n /** Authenticated principal (resolved by the service, never trusted raw). */\n principal?: string;\n /**\n * Caller-suggested namespace. ALWAYS re-resolved through the namespace\n * policy by the service before reaching the planner (rule 42). The\n * planner only sees the ALREADY-AUTHORIZED namespace.\n */\n namespace?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Classification + actions\n// ---------------------------------------------------------------------------\n\nexport type CorrectionClassification =\n | \"wrong\"\n | \"outdated\"\n | \"incomplete\"\n | \"wrong_scope\"\n | \"never_store\";\n\nexport const CORRECTION_CLASSIFICATIONS: readonly CorrectionClassification[] = [\n \"wrong\",\n \"outdated\",\n \"incomplete\",\n \"wrong_scope\",\n \"never_store\",\n];\n\n/**\n * A draft for a replacement memory (supersede action). Mirrors the subset of\n * {@link MemoryFrontmatter} a correction caller may legitimately set; the\n * executor fills in id/created/updated/source.\n */\nexport interface MemoryDraft {\n content: string;\n category?: MemoryCategory;\n confidence?: number;\n tags?: string[];\n entityRef?: string;\n /** Optional event-time anchor for bi-temporal supersession (#1578). */\n validAt?: string;\n /** Optional observed-at anchor for bi-temporal supersession (#1578). */\n observedAt?: string;\n structuredAttributes?: Record<string, string>;\n}\n\n/**\n * The known {@link MemoryCategory} values. A replacement/rescope category must\n * be one of these — rejecting path-like or unexpected strings before they reach\n * `StorageManager.writeMemory`, which incorporates the category into the\n * generated memory id/path (review thread Of-XJ).\n */\nexport const MEMORY_CATEGORIES: readonly MemoryCategory[] = [\n \"fact\", \"preference\", \"correction\", \"entity\", \"decision\",\n \"relationship\", \"principle\", \"commitment\", \"moment\",\n \"skill\", \"rule\", \"procedure\", \"reasoning_trace\",\n];\n\nexport type CorrectionAction =\n /** Outdated: a new fact replaces the loser. */\n | { kind: \"supersede\"; loserId: string; replacement?: MemoryDraft }\n /** Incomplete / minor-wrong: a versioned edit to an existing memory. */\n | { kind: \"edit\"; memoryId: string; patch: string }\n /** Wrong: retire + tombstone. */\n | { kind: \"retract\"; memoryId: string }\n /** Wrong scope: move to a different namespace. */\n | { kind: \"rescope\"; memoryId: string; toNamespace: string }\n /** Never-store: a future-extraction redaction rule. */\n | { kind: \"redaction_rule\"; pattern: string };\n\nexport const CORRECTION_ACTION_KINDS: readonly CorrectionAction[\"kind\"][] = [\n \"supersede\",\n \"edit\",\n \"retract\",\n \"rescope\",\n \"redaction_rule\",\n];\n\n// ---------------------------------------------------------------------------\n// Plan + outcome\n// ---------------------------------------------------------------------------\n\n/**\n * One affected memory in a plan: where it lives, an excerpt, why it is\n * affected, and (when available) the source quote that supports the\n * correction (#1575 provenance).\n */\nexport interface CorrectionAffectedEntry {\n memoryId: string;\n /** File path relative to the storage dir, for diff rendering. */\n path: string;\n excerpt: string;\n why: string;\n sourceQuote?: string;\n}\n\n/**\n * A persisted, expiring correction plan. Read-only artifact produced by the\n * planner; consumed (once) by the executor.\n */\nexport interface CorrectionPlan {\n planId: string;\n request: CorrectionRequest;\n /** Authorized namespace the plan is scoped to (resolved by the service). */\n namespace: string;\n affected: CorrectionAffectedEntry[];\n classification: CorrectionClassification;\n actions: CorrectionAction[];\n /** Human-readable diff preview rendered via page-versioning. */\n diff: string;\n /** Planner confidence in [0, 1]. `0` means manual selection required. */\n confidence: number;\n warnings: string[];\n createdAt: string;\n /** ISO timestamp after which apply rejects the plan as expired. */\n expiresAt: string;\n /**\n * Lifecycle: `pending` → `applying` → `applied`|`partial` (or `discarded`).\n * The executor flips a plan to `applying` BEFORE running any mutation\n * (review thread OgIqt): if the process dies mid-apply, the plan stays\n * `applying` and is NOT silently retryable — a partially-applied plan must\n * never be re-applied wholesale (it would duplicate succeeded actions).\n */\n status?: \"pending\" | \"applying\" | \"applied\" | \"discarded\" | \"partial\";\n}\n\n/**\n * Per-action outcome recorded by the executor. An action whose new-state\n * write failed is `failed`; the executor never destroys old state for a\n * failed action (rule 25 / checklist §14).\n */\nexport interface CorrectionActionResult {\n action: CorrectionAction;\n status: \"applied\" | \"failed\" | \"skipped\";\n /** For supersede: the new memory id. For edit: the memory id. */\n memoryId?: string;\n /** For supersede/retract: the emitted tombstone id (if any). */\n tombstoneId?: string;\n error?: string;\n}\n\nexport interface CorrectionOutcome {\n planId: string;\n status: \"applied\" | \"partial\";\n results: CorrectionActionResult[];\n /** Audit-record memory id (corrections are themselves memories). */\n auditMemoryId: string;\n /** ISO timestamp of the apply. */\n appliedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Validation helpers (pure) — shared by planner, executor, and surface tests\n// ---------------------------------------------------------------------------\n\n/** Maximum supported `text` length. Surfaces bound this earlier; the planner\n * re-validates so a direct-service caller cannot bypass. */\nexport const CORRECTION_TEXT_MAX = 10_000;\n\n/** Maximum supported redaction pattern length (§34 — bounded patterns). */\nexport const REDACTION_PATTERN_MAX = 256;\n\n/**\n * Validate a {@link CorrectionRequest}'s invariants. Returns the cleaned\n * request or throws with a field-specific message (rule 51 — list valid\n * options, never silently default).\n */\nexport function validateCorrectionRequest(request: CorrectionRequest): CorrectionRequest {\n if (!request || typeof request !== \"object\") {\n throw new CorrectionContractError(\"CorrectionRequest must be an object.\");\n }\n const text = typeof request.text === \"string\" ? request.text.trim() : \"\";\n if (text.length === 0) {\n throw new CorrectionContractError(\"CorrectionRequest.text is required and must be non-empty.\");\n }\n if (text.length > CORRECTION_TEXT_MAX) {\n throw new CorrectionContractError(\n `CorrectionRequest.text exceeds the ${CORRECTION_TEXT_MAX}-character limit (${text.length}).`,\n );\n }\n const targetIds = Array.isArray(request.targetIds)\n ? request.targetIds.filter((id): id is string => typeof id === \"string\" && id.length > 0)\n : undefined;\n if (targetIds !== undefined && targetIds.length === 0) {\n // An empty array is treated as \"not provided\" so callers can forward\n // optional fields without a separate presence flag.\n return {\n text,\n ...(request.sessionKey ? { sessionKey: request.sessionKey } : {}),\n ...(request.principal ? { principal: request.principal } : {}),\n ...(request.namespace ? { namespace: request.namespace } : {}),\n };\n }\n return {\n text,\n ...(targetIds ? { targetIds } : {}),\n ...(request.sessionKey ? { sessionKey: request.sessionKey } : {}),\n ...(request.principal ? { principal: request.principal } : {}),\n ...(request.namespace ? { namespace: request.namespace } : {}),\n };\n}\n\n/**\n * Validate a redaction pattern (§34 — bounded, literal-or-safe-regex; reject\n * catastrophic patterns). Returns the cleaned pattern or throws.\n */\nexport function validateRedactionPattern(pattern: string): string {\n if (typeof pattern !== \"string\") {\n throw new CorrectionContractError(\"redaction_rule.pattern must be a string.\");\n }\n const trimmed = pattern.trim();\n if (trimmed.length === 0) {\n throw new CorrectionContractError(\"redaction_rule.pattern is required.\");\n }\n if (trimmed.length > REDACTION_PATTERN_MAX) {\n throw new CorrectionContractError(\n `redaction_rule.pattern exceeds the ${REDACTION_PATTERN_MAX}-character bound.`,\n );\n }\n // Reject patterns that could match an unbounded string (catastrophic /\n // overly-broad). A literal or a bounded regex is fine; a bare `.*` / `.`\n // regex is not. We do NOT execute the regex (ReDoS); we only inspect shape.\n if (isRegexLike(trimmed) && isOverlyBroadRegex(trimmed)) {\n throw new CorrectionContractError(\n \"redaction_rule.pattern is too broad — use a bounded literal or a more specific pattern.\",\n );\n }\n return trimmed;\n}\n\n/** Heuristic: treat `/.../` or presence of regex metacharacters as regex. */\nfunction isRegexLike(pattern: string): boolean {\n if (pattern.startsWith(\"/\") && pattern.endsWith(\"/\") && pattern.length >= 2) return true;\n return /[\\\\^$.|?*+()[\\]{}]/.test(pattern);\n}\n\n/** Reject a regex that would match an arbitrary-length run of any character. */\nfunction isOverlyBroadRegex(pattern: string): boolean {\n const body = pattern.startsWith(\"/\") && pattern.endsWith(\"/\")\n ? pattern.slice(1, -1)\n : pattern;\n // `.*`, `.+`, `.`, or `(.*)` etc. anywhere → overly broad.\n if (/(?:^|[^\\\\])\\(\\.\\*\\)|(?:^|[^\\\\])\\.\\*|(?:^|[^\\\\])\\.\\+|^\\.([^*+]?)$/.test(body)) {\n return true;\n }\n return false;\n}\n\n/**\n * Validate the shape of a {@link CorrectionAction}. Used by the executor\n * before applying and by the surface layer to reject malformed client input\n * (rule 51 — list valid kinds).\n */\nexport function validateCorrectionAction(action: unknown): asserts action is CorrectionAction {\n if (!action || typeof action !== \"object\") {\n throw new CorrectionContractError(\"CorrectionAction must be an object.\");\n }\n const a = action as Record<string, unknown>;\n if (typeof a.kind !== \"string\" || !CORRECTION_ACTION_KINDS.includes(a.kind as CorrectionAction[\"kind\"])) {\n throw new CorrectionContractError(\n `CorrectionAction.kind must be one of: ${CORRECTION_ACTION_KINDS.join(\", \")}.`,\n );\n }\n switch (a.kind) {\n case \"supersede\":\n if (typeof a.loserId !== \"string\" || a.loserId.length === 0) {\n throw new CorrectionContractError(\"supersede.loserId is required.\");\n }\n if (a.replacement !== undefined && a.replacement !== null) {\n validateMemoryDraft(a.replacement);\n }\n break;\n case \"edit\":\n if (typeof a.memoryId !== \"string\" || a.memoryId.length === 0) {\n throw new CorrectionContractError(\"edit.memoryId is required.\");\n }\n if (typeof a.patch !== \"string\" || a.patch.length === 0) {\n throw new CorrectionContractError(\"edit.patch is required and must be non-empty.\");\n }\n break;\n case \"retract\":\n if (typeof a.memoryId !== \"string\" || a.memoryId.length === 0) {\n throw new CorrectionContractError(\"retract.memoryId is required.\");\n }\n break;\n case \"rescope\":\n if (typeof a.memoryId !== \"string\" || a.memoryId.length === 0) {\n throw new CorrectionContractError(\"rescope.memoryId is required.\");\n }\n if (typeof a.toNamespace !== \"string\" || a.toNamespace.trim().length === 0) {\n throw new CorrectionContractError(\"rescope.toNamespace is required.\");\n }\n break;\n case \"redaction_rule\":\n if (typeof a.pattern !== \"string\") {\n throw new CorrectionContractError(\"redaction_rule.pattern is required.\");\n }\n validateRedactionPattern(a.pattern);\n break;\n }\n}\n\n/** Validate a {@link MemoryDraft}. Throws on invalid shape. */\nexport function validateMemoryDraft(draft: unknown): asserts draft is MemoryDraft {\n if (!draft || typeof draft !== \"object\") {\n throw new CorrectionContractError(\"MemoryDraft must be an object.\");\n }\n const d = draft as Record<string, unknown>;\n if (typeof d.content !== \"string\" || d.content.trim().length === 0) {\n throw new CorrectionContractError(\"MemoryDraft.content is required and must be non-empty.\");\n }\n if (d.category !== undefined) {\n if (typeof d.category !== \"string\" || !(MEMORY_CATEGORIES as readonly string[]).includes(d.category)) {\n throw new CorrectionContractError(\n `MemoryDraft.category must be one of: ${MEMORY_CATEGORIES.join(\", \")}.`,\n );\n }\n }\n if (d.confidence !== undefined && (typeof d.confidence !== \"number\" || d.confidence < 0 || d.confidence > 1)) {\n throw new CorrectionContractError(\"MemoryDraft.confidence must be a number in [0, 1].\");\n }\n if (d.tags !== undefined && !Array.isArray(d.tags)) {\n throw new CorrectionContractError(\"MemoryDraft.tags must be an array.\");\n }\n if (d.structuredAttributes !== undefined && (typeof d.structuredAttributes !== \"object\" || d.structuredAttributes === null)) {\n throw new CorrectionContractError(\"MemoryDraft.structuredAttributes must be an object.\");\n }\n}\n\n/**\n * Deterministic fallback plan (rule 13): when the planner's LLM is\n * unavailable, the plan degrades to a search result, never an error page.\n * Classification `outdated`, confidence `0`, actions empty.\n */\nexport function deterministicFallbackPlan(args: {\n request: CorrectionRequest;\n namespace: string;\n affected: CorrectionAffectedEntry[];\n warnings: string[];\n createdAt: string;\n expiresAt: string;\n}): CorrectionPlan {\n return {\n planId: newPlanId(),\n request: args.request,\n namespace: args.namespace,\n affected: args.affected,\n classification: \"outdated\",\n actions: [],\n diff: \"\",\n confidence: 0,\n warnings: [...args.warnings, \"planner LLM unavailable — manual action selection required\"],\n createdAt: args.createdAt,\n expiresAt: args.expiresAt,\n status: \"pending\",\n };\n}\n\n/** Generate a stable plan id. Exposed for tests + deterministic fallback. */\nexport function newPlanId(): string {\n return `corr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Error class for contract violations. Surfaces map it to a 400 / input error. */\nexport class CorrectionContractError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CorrectionContractError\";\n }\n}\n","/**\n * correction/correction-access-wiring.ts — wires the CorrectionService to the\n * orchestrator's storage / tombstone / search surfaces (issue #1580 PR 3).\n *\n * Keeps the access-service god-file thin: the service calls `createCorrectionService`\n * with two namespace-policy callbacks and gets back a fully-wired\n * {@link CorrectionService}. All the heavy lifting (search, LLM classify,\n * tombstone emission, audit-record write, propagation) lives here, in a\n * dedicated module, so the only growth in access-service.ts is the four\n * one-line delegators.\n *\n * Design rules honored:\n * - Caller-supplied namespaces are NEVER trusted raw — the service resolves\n * them through the injected policy (rule 42).\n * - Corrections flow through the existing storage chokepoints: writeMemory\n * (catalog/dedup/reindex fire — rule 43), appendTombstone (#1579),\n * writeMemoryFrontmatter (status flip + validUntil — #1578).\n * - The LLM classify+draft adapter routes through the existing extraction\n * engine (Responses API only, gotcha 1). On any LLM failure the adapter\n * returns a deterministic fallback so the planner never throws (rule 13).\n */\n\nimport path from \"node:path\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport type { Orchestrator } from \"../orchestrator.js\";\nimport type { MemoryFile, MemoryStatus, PluginConfig } from \"../types.js\";\nimport { stripAttributesSuffix } from \"../structured-attributes.js\";\nimport {\n CorrectionContractError,\n validateCorrectionAction,\n type CorrectionAction,\n type CorrectionOutcome,\n type CorrectionPlan,\n} from \"./correction-contract.js\";\nimport {\n type LlmClassificationResult,\n type PlannerCandidate,\n type PlannerDeps,\n} from \"./correction-planner.js\";\nimport { type ExecutorDeps, type ExecutorMemory } from \"./correction-executor.js\";\nimport { CorrectionService, type CorrectionServiceDeps } from \"./correction-service.js\";\n\n// ---------------------------------------------------------------------------\n// Public entry: build a fully-wired CorrectionService\n// ---------------------------------------------------------------------------\n\n/**\n * The two namespace-policy callbacks the access-service supplies. These are\n * the ONLY surface-area touch-points between the correction modules and the\n * access-service god-file — everything else is wired from the orchestrator.\n */\nexport interface CorrectionAccessWiring {\n orchestrator: Orchestrator;\n /** Resolve the AUTHORIZED namespace for a plan/apply request (write ACL). */\n resolveAuthorizedNamespace(request: {\n namespace?: string;\n sessionKey?: string;\n principal?: string;\n }): Promise<string>;\n /** Resolve the caller's READABLE namespaces (scopes the planner's search). */\n resolveReadableNamespaces(request: {\n namespace?: string;\n sessionKey?: string;\n principal?: string;\n }): readonly string[];\n /** Whether the caller may WRITE a namespace — authorizes rescope destinations. */\n canWriteNamespace(request: {\n namespace: string;\n sessionKey?: string;\n principal?: string;\n }): Promise<boolean>;\n /**\n * Optional LLM-complete callback (Responses API only — gotcha 1). When\n * absent, the planner's classify+draft step falls back to deterministic\n * mode (rule 13). The access-service wires the orchestrator's extraction\n * LLM here once it exposes a public accessor; until then the contract\n * ships with the safe fallback.\n */\n llmComplete?(request: {\n system: string;\n user: string;\n }): Promise<string>;\n}\n\nexport function createCorrectionService(wiring: CorrectionAccessWiring): CorrectionService {\n const cfg = wiring.orchestrator.config;\n // parseConfig now owns these (review thread Txp): the orchestrator's config\n // carries correctionEnabled / correctionApplyRequiresConfirm /\n // correctionMaxAffected / correctionPlanTtlHours as parsed fields. The\n // loose-read helpers stay as a fallback only for tests that construct a\n // PluginConfig-shaped object without running parseConfig.\n const correctionEnabled = isCorrectionFeatureEnabled(cfg);\n const applyRequiresConfirm =\n typeof cfg.correctionApplyRequiresConfirm === \"boolean\"\n ? cfg.correctionApplyRequiresConfirm\n : readCorrectionFlag(cfg, \"applyRequiresConfirm\", true);\n const maxAffected =\n typeof cfg.correctionMaxAffected === \"number\" && cfg.correctionMaxAffected >= 1\n ? Math.floor(cfg.correctionMaxAffected)\n : readCorrectionNumber(cfg, \"maxAffected\", 10);\n const planTtlHours =\n typeof cfg.correctionPlanTtlHours === \"number\" && cfg.correctionPlanTtlHours > 0\n ? cfg.correctionPlanTtlHours\n : readCorrectionNumber(cfg, \"planTtlHours\", 24);\n const biTemporalEnabled = cfg.temporalBiTemporal === true;\n\n const serviceDeps: CorrectionServiceDeps = {\n policy: {\n resolveAuthorizedNamespace: (req) => wiring.resolveAuthorizedNamespace(req),\n canWriteNamespace: (req) => wiring.canWriteNamespace(req),\n readableNamespaces: (req) =>\n Promise.resolve(wiring.resolveReadableNamespaces(req)),\n },\n plannerDeps: () =>\n makePlannerDeps(wiring, { maxAffected, planTtlHours }),\n executorDeps: () => makeExecutorDeps(wiring, { biTemporalEnabled }),\n isEnabled: () => correctionEnabled,\n applyRequiresConfirm: () => applyRequiresConfirm,\n };\n return new CorrectionService(serviceDeps);\n}\n\n// ---------------------------------------------------------------------------\n// Planner deps\n// ---------------------------------------------------------------------------\n\nfunction makePlannerDeps(\n wiring: CorrectionAccessWiring,\n opts: { maxAffected: number; planTtlHours: number },\n): PlannerDeps {\n return {\n searchCorpus: async ({ text, namespaces, limit }) =>\n searchMemories(wiring, text, namespaces, limit),\n resolveTargets: async ({ targetIds, namespaces }) =>\n resolveTargetMemories(wiring, targetIds, namespaces),\n expandNeighbors: async ({ seedIds, namespaces, limit }) =>\n expandEntityNeighbors(wiring, seedIds, namespaces, limit),\n classifyAndDraft: async ({ text, candidates }) =>\n classifyAndDraft(wiring, text, candidates),\n renderDiff: async ({ candidates, actions }) =>\n renderCorrectionDiff(wiring, candidates, actions),\n storageDir: async (namespace) => (await wiring.orchestrator.getStorage(namespace)).dir,\n maxAffected: opts.maxAffected,\n planTtlHours: opts.planTtlHours,\n now: () => new Date(),\n };\n}\n\nasync function searchMemories(\n wiring: CorrectionAccessWiring,\n text: string,\n namespaces: readonly string[],\n limit: number,\n): Promise<PlannerCandidate[]> {\n // Tokenized keyword search (review thread OgIql): natural-language\n // corrections describe the new truth, not quote the old memory verbatim\n // (\"we migrated from Postgres to MySQL\" vs a memory saying \"Postgres is\n // the database\"). The old 32-char exact-prefix substring missed these.\n // We tokenize the correction into keywords, score each active memory by\n // keyword overlap, and return memories above a minimum threshold. This is\n // deliberately lightweight (no embedding/QMD dependency) — the planner's\n // job is to LOCATE candidates for the LLM classify+draft, not to rank with\n // the full recall pipeline.\n const tokens = tokenize(text);\n if (tokens.length === 0) return [];\n const scored: Array<{ m: MemoryFile; ns: string; score: number }> = [];\n for (const ns of namespaces) {\n const storage = await wiring.orchestrator.getStorage(ns);\n const all = await storage.readAllMemories();\n for (const m of all) {\n if (m.frontmatter.status && m.frontmatter.status !== \"active\") continue;\n const hay = `${m.content} ${m.frontmatter.tags?.join(\" \") ?? \"\"}`.toLowerCase();\n let hits = 0;\n for (const tok of tokens) {\n if (hay.includes(tok)) hits++;\n }\n if (hits === 0) continue;\n // Overlap ratio: how many query tokens the memory covers.\n scored.push({ m, ns, score: hits / tokens.length });\n }\n }\n scored.sort((a, b) => b.score - a.score);\n return scored.slice(0, limit).map((s, i) => toCandidate(s.m, s.ns, s.score - i * 0.01));\n}\n\n/** Tokenize correction text into lowercase search keywords (OgIql). */\nfunction tokenize(text: string): string[] {\n const STOP = new Set([\n \"the\", \"a\", \"an\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\",\n \"to\", \"of\", \"in\", \"on\", \"at\", \"by\", \"for\", \"with\", \"from\", \"into\",\n \"and\", \"or\", \"but\", \"not\", \"no\", \"yes\", \"this\", \"that\", \"these\",\n \"those\", \"it\", \"its\", \"we\", \"you\", \"i\", \"he\", \"she\", \"they\", \"them\",\n \"our\", \"your\", \"my\", \"his\", \"her\", \"their\", \"as\", \"so\", \"if\", \"then\",\n \"than\", \"too\", \"very\", \"can\", \"will\", \"just\", \"should\", \"now\", \"has\",\n \"have\", \"had\", \"do\", \"does\", \"did\", \"about\", \"which\", \"what\", \"who\",\n \"when\", \"where\", \"why\", \"how\", \"all\", \"each\", \"every\", \"both\", \"few\",\n \"more\", \"most\", \"other\", \"some\", \"such\", \"only\", \"own\", \"same\", \"up\",\n ]);\n const raw = text.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);\n const out = raw.filter((w) => w.length >= 3 && !STOP.has(w));\n // De-dup while preserving order.\n return [...new Set(out)];\n}\n\nasync function resolveTargetMemories(\n wiring: CorrectionAccessWiring,\n targetIds: readonly string[],\n namespaces: readonly string[],\n): Promise<PlannerCandidate[]> {\n const out: PlannerCandidate[] = [];\n const missing: string[] = [];\n for (const id of targetIds) {\n let found: PlannerCandidate | null = null;\n for (const ns of namespaces) {\n const storage = await wiring.orchestrator.getStorage(ns);\n const m = await storage.getMemoryById(id);\n if (m) {\n found = toCandidate(m, ns, 1);\n break;\n }\n }\n if (found) out.push(found);\n else missing.push(id);\n }\n if (missing.length > 0) {\n throw new CorrectionContractError(`target memory not found: ${missing[0]}`);\n }\n return out;\n}\n\nasync function expandEntityNeighbors(\n wiring: CorrectionAccessWiring,\n seedIds: readonly string[],\n namespaces: readonly string[],\n limit: number,\n): Promise<PlannerCandidate[]> {\n // 1-hop entity-graph neighbors via entityRef-tagged siblings. We expand\n // across the AUTHORIZED namespaces the planner supplied (review thread PG8),\n // NOT only `config.defaultNamespace` — a correction planned in a non-default\n // writable namespace must only ever draft siblings from that same authorized\n // scope, or apply will fail as not-found / mutate a same-ID memory in the\n // wrong namespace. The conservative entityRef fallback searches every\n // readable namespace and tags each candidate with the namespace it was read\n // from; a richer graph expansion can ride on top later (rule 57 — additive).\n if (seedIds.length === 0 || limit <= 0 || namespaces.length === 0) return [];\n const out: PlannerCandidate[] = [];\n const seen = new Set<string>(seedIds);\n // Collect the seed entityRefs across every authorized namespace (a seed\n // memory may live in any of them), then surface active siblings sharing one.\n const seedRefs = new Set<string>();\n for (const ns of namespaces) {\n const storage = await wiring.orchestrator.getStorage(ns);\n const all = await storage.readAllMemories();\n for (const m of all) {\n if (seedIds.includes(m.frontmatter.id) && m.frontmatter.entityRef) {\n seedRefs.add(m.frontmatter.entityRef);\n }\n }\n }\n if (seedRefs.size === 0) return [];\n for (const ns of namespaces) {\n if (out.length >= limit) break;\n const storage = await wiring.orchestrator.getStorage(ns);\n const all = await storage.readAllMemories();\n for (const m of all) {\n if (out.length >= limit) break;\n if (seen.has(m.frontmatter.id)) continue;\n if (m.frontmatter.status && m.frontmatter.status !== \"active\") continue;\n if (m.frontmatter.entityRef && seedRefs.has(m.frontmatter.entityRef)) {\n out.push(toCandidate(m, ns, 0.5));\n seen.add(m.frontmatter.id);\n }\n }\n }\n return out;\n}\n\nasync function classifyAndDraft(\n wiring: CorrectionAccessWiring,\n text: string,\n candidates: PlannerCandidate[],\n): Promise<LlmClassificationResult> {\n // Route through the injected LLM callback (Responses API only — gotcha 1).\n // On any failure (or when no callback is wired), return the deterministic\n // fallback (rule 13) — the planner never throws on an LLM outage.\n try {\n if (!wiring.llmComplete) {\n return fallbackClassification(candidates, \"no LLM client available\");\n }\n const user = buildClassifyPrompt(text, candidates);\n const raw = await wiring.llmComplete({ system: CLASSIFY_SYSTEM_PROMPT, user });\n return parseClassifyResponse(raw, candidates);\n } catch (err) {\n return fallbackClassification(candidates, `LLM unavailable: ${errMsg(err)}`);\n }\n}\n\nasync function renderCorrectionDiff(\n _wiring: CorrectionAccessWiring,\n candidates: PlannerCandidate[],\n actions: CorrectionAction[],\n): Promise<string> {\n // Render a human-readable unified-diff-style preview. We snapshot each\n // affected memory via page-versioning (reuse, don't fork — rule 23) when\n // versioning is configured; otherwise fall back to a textual summary.\n const lines: string[] = [];\n for (const action of actions) {\n const id =\n action.kind === \"supersede\"\n ? action.loserId\n : action.kind === \"edit\" || action.kind === \"retract\" || action.kind === \"rescope\"\n ? action.memoryId\n : null;\n const candidate = id ? candidates.find((c) => c.memoryId === id) : null;\n const before = candidate?.content ?? \"(new)\";\n const after = describeAfterState(action);\n lines.push(`--- ${id ?? \"redaction-rule\"} (${action.kind})`);\n lines.push(`- ${before.slice(0, 120)}`);\n lines.push(`+ ${after.slice(0, 120)}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction describeAfterState(action: CorrectionAction): string {\n switch (action.kind) {\n case \"supersede\":\n return action.replacement?.content ?? \"(superseded without replacement)\";\n case \"edit\":\n return action.patch;\n case \"retract\":\n return \"(retracted + tombstoned)\";\n case \"rescope\":\n return `(moved to namespace '${action.toNamespace}')`;\n case \"redaction_rule\":\n return `(redaction rule persisted for pattern '${action.pattern}')`;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Executor deps\n// ---------------------------------------------------------------------------\n\nfunction makeExecutorDeps(\n wiring: CorrectionAccessWiring,\n opts: { biTemporalEnabled: boolean },\n): ExecutorDeps {\n return {\n getMemory: async (namespace, memoryId) => getExecutorMemory(wiring, namespace, memoryId),\n writeReplacement: async (namespace, draft) =>\n writeReplacementMemory(wiring, namespace, draft),\n applyEdit: async (namespace, memoryId, patch) =>\n applyEditMemory(wiring, namespace, memoryId, patch),\n retireMemory: async (namespace, memoryId, retireOpts) =>\n retireMemoryFn(wiring, namespace, memoryId, retireOpts),\n rescopeMemory: async (namespace, memoryId, toNamespace) =>\n rescopeMemoryFn(wiring, namespace, memoryId, toNamespace),\n appendTombstone: async (namespace, input) =>\n appendTombstoneFn(wiring, namespace, input),\n registerRedactionRule: async (namespace, pattern) =>\n registerRedactionRuleFn(wiring, namespace, pattern),\n appendAuditRecord: async (namespace, record) =>\n appendAuditRecordFn(wiring, namespace, record),\n propagate: async (namespace, touchedMemoryIds) =>\n propagateFn(wiring, namespace, touchedMemoryIds),\n biTemporalEnabled: opts.biTemporalEnabled,\n now: () => new Date(),\n };\n}\n\nasync function getExecutorMemory(\n wiring: CorrectionAccessWiring,\n namespace: string,\n memoryId: string,\n): Promise<ExecutorMemory | null> {\n const storage = await wiring.orchestrator.getStorage(namespace);\n const m = await storage.getMemoryById(memoryId);\n if (!m) return null;\n return toExecutorMemory(m);\n}\n\nasync function writeReplacementMemory(\n wiring: CorrectionAccessWiring,\n namespace: string,\n draft: {\n content: string;\n category?: string;\n confidence?: number;\n tags?: string[];\n entityRef?: string;\n validAt?: string;\n observedAt?: string;\n structuredAttributes?: Record<string, string>;\n supersedes?: string;\n },\n): Promise<string> {\n const storage = await wiring.orchestrator.getStorage(namespace);\n // writeMemory is the single storage chokepoint — catalog/dedup/reindex fire\n // here (rule 43). Tombstone blocking also fires here (#1579), so a\n // resurrected fact lands as pending_review rather than silently overwriting.\n const id = await storage.writeMemory(\n (draft.category ?? \"fact\") as Parameters<typeof storage.writeMemory>[0],\n draft.content,\n {\n source: \"correction\",\n confidence: draft.confidence ?? 0.9,\n tags: draft.tags ?? [],\n ...(draft.entityRef ? { entityRef: draft.entityRef } : {}),\n ...(draft.validAt ? { validAt: draft.validAt } : {}),\n ...(draft.observedAt ? { observedAt: draft.observedAt } : {}),\n ...(draft.structuredAttributes ? { structuredAttributes: draft.structuredAttributes } : {}),\n ...(draft.supersedes ? { supersedes: draft.supersedes } : {}),\n },\n );\n return id;\n}\n\nasync function applyEditMemory(\n wiring: CorrectionAccessWiring,\n namespace: string,\n memoryId: string,\n patch: string,\n): Promise<string> {\n const storage = await wiring.orchestrator.getStorage(namespace);\n const existing = await storage.getMemoryById(memoryId);\n if (!existing) throw new CorrectionContractError(`memory not found for edit: ${memoryId}`);\n // Apply the patch by overwriting content through the storage chokepoint.\n // The StorageManager's writeMemoryFrontmatter snapshots the prior version\n // internally when page-versioning is configured (issue #371), so every edit\n // is revertable without the correction layer forking versioning logic.\n await storage.writeMemoryFrontmatter(\n { ...existing, content: patch },\n { updated: new Date().toISOString() },\n );\n return memoryId;\n}\n\nasync function retireMemoryFn(\n wiring: CorrectionAccessWiring,\n namespace: string,\n memoryId: string,\n opts: { status: \"superseded\" | \"retracted\"; supersededBy?: string; validUntil?: string },\n): Promise<void> {\n const storage = await wiring.orchestrator.getStorage(namespace);\n const memory = await storage.getMemoryById(memoryId);\n if (!memory) throw new CorrectionContractError(`memory not found for retire: ${memoryId}`);\n // Map the correction-domain status to the storage-domain MemoryStatus.\n // `retracted` (a correction concept) becomes `forgotten` — the soft-delete\n // status that excludes the memory from recall/browse/attribution while\n // keeping a page-version snapshot for reversibility (#686). `superseded`\n // maps to itself.\n const storageStatus: MemoryStatus = opts.status === \"retracted\" ? \"forgotten\" : \"superseded\";\n // Flip status + stamp validUntil (when bi-temporal is on, #1578) +\n // link the superseder. writeMemoryFrontmatter is the chokepoint.\n await storage.writeMemoryFrontmatter(memory, {\n status: storageStatus,\n ...(opts.supersededBy ? { supersededBy: opts.supersededBy } : {}),\n ...(opts.status === \"superseded\" ? { supersededAt: new Date().toISOString() } : {}),\n // validUntil is the bi-temporal end; absent when the gate is off.\n ...(opts.validUntil ? { invalid_at: opts.validUntil } : {}),\n });\n}\n\nasync function rescopeMemoryFn(\n wiring: CorrectionAccessWiring,\n namespace: string,\n memoryId: string,\n toNamespace: string,\n): Promise<string> {\n // The destination namespace is re-authorized by the service before the\n // executor runs; here we perform the move atomically (write-then-unlink).\n const sourceStorage = await wiring.orchestrator.getStorage(namespace);\n const memory = await sourceStorage.getMemoryById(memoryId);\n if (!memory) throw new CorrectionContractError(`memory not found for rescope: ${memoryId}`);\n if (memory.frontmatter.status && memory.frontmatter.status !== \"active\") {\n // Don't copy a stale source: rescoping a superseded/retracted/archived\n // memory duplicates outdated content into the destination (thread Ohjwb).\n throw new CorrectionContractError(\n `cannot rescope memory ${memoryId}: source is ${memory.frontmatter.status}, not active`,\n );\n }\n const destStorage = await wiring.orchestrator.getStorage(toNamespace);\n const fm = memory.frontmatter;\n // Strip the `[Attributes: …]` suffix writeMemory appended to the source body\n // (review thread Of0p6): we forward `structuredAttributes` separately, so\n // writeMemory will re-append the suffix on the destination. Without this\n // strip the destination would carry the suffix TWICE, producing duplicated\n // attribute text and a different content hash/index entry.\n const destContent = fm.structuredAttributes\n ? stripAttributesSuffix(memory.content)\n : memory.content;\n const destId = await destStorage.writeMemory(fm.category, destContent, {\n source: `correction:rescope:${namespace}`,\n ...(typeof fm.confidence === \"number\" ? { confidence: fm.confidence } : {}),\n ...(Array.isArray(fm.tags) ? { tags: fm.tags } : {}),\n ...(fm.entityRef ? { entityRef: fm.entityRef } : {}),\n ...(fm.structuredAttributes ? { structuredAttributes: fm.structuredAttributes } : {}),\n ...(fm.valid_at ? { validAt: fm.valid_at } : {}),\n ...(fm.observedAt ? { observedAt: fm.observedAt } : {}),\n ...(fm.memoryKind ? { memoryKind: fm.memoryKind } : {}),\n ...(Array.isArray(fm.links) ? { links: fm.links } : {}),\n ...(fm.intentGoal ? { intentGoal: fm.intentGoal } : {}),\n });\n // Unlink the source by archiving (non-destructive — rule 25). If the archive\n // fails AFTER the destination write succeeded, compensate by archiving the\n // destination too so no duplicate ACTIVE fact remains, then re-throw so the\n // executor records the action as failed (review: rescope-duplicates-on-fail).\n try {\n await sourceStorage.writeMemoryFrontmatter(memory, {\n status: \"archived\",\n archivedAt: new Date().toISOString(),\n });\n } catch (err) {\n try {\n const destMem = await destStorage.getMemoryById(destId);\n if (destMem) {\n await destStorage.writeMemoryFrontmatter(destMem, {\n status: \"archived\",\n archivedAt: new Date().toISOString(),\n });\n }\n } catch {\n // best-effort compensation\n }\n throw err;\n }\n return destId;\n}\n\nasync function appendTombstoneFn(\n wiring: CorrectionAccessWiring,\n namespace: string,\n input: {\n reason: \"correction\" | \"supersession\" | \"retraction\";\n sourceMemoryId: string;\n rawContent: string;\n entityRef?: string;\n supersessionKey?: string;\n },\n): Promise<string | null> {\n const storage = await wiring.orchestrator.getStorage(namespace);\n // storage.appendTombstone returns null for TWO reasons: tombstones disabled\n // (off = pre-feature behavior) OR a swallowed store error (it catches I/O\n // failures and returns null — review thread OgIqp). The executor writes the\n // tombstone BEFORE retiring the source (PG9), so when tombstones are\n // enabled a null return means persistence failed and the retire must NOT\n // proceed (no tombstone → resurrection window). Distinguish the two cases\n // via the public isTombstonesEnabled() accessor; when disabled, null is the\n // expected pre-feature return and the action may still succeed.\n const enabled =\n typeof storage.isTombstonesEnabled === \"function\"\n ? storage.isTombstonesEnabled()\n : true;\n const result = await storage.appendTombstone({\n reason: input.reason,\n createdBy: \"user_correction\",\n sourceMemoryId: input.sourceMemoryId,\n rawContent: input.rawContent,\n ...(input.entityRef ? { entityRef: input.entityRef } : {}),\n ...(input.supersessionKey ? { supersessionKey: input.supersessionKey } : {}),\n });\n if (result === null && enabled) {\n throw new CorrectionContractError(\n `tombstone persistence failed for memory ${input.sourceMemoryId} (tombstones enabled but store returned null — I/O error swallowed)`,\n );\n }\n return result;\n}\n\nasync function registerRedactionRuleFn(\n wiring: CorrectionAccessWiring,\n namespace: string,\n pattern: string,\n): Promise<void> {\n // Persist the redaction rule under state/ so extraction consults it the\n // same way tombstones are consulted (route through the same chokepoint).\n const storage = await wiring.orchestrator.getStorage(namespace);\n const dir = path.join(storage.dir, \"state\", \"corrections\", \"redaction-rules\");\n await mkdir(dir, { recursive: true });\n // Idempotent: filename is a slug of the pattern so re-registering the same\n // pattern overwrites rather than duplicates.\n const slug = pattern.replace(/[^a-zA-Z0-9]+/g, \"-\").slice(0, 64) || \"rule\";\n await writeFile(\n path.join(dir, `${slug}.json`),\n `${JSON.stringify({ pattern, namespace, createdAt: new Date().toISOString() })}\\n`,\n \"utf-8\",\n );\n}\n\nasync function appendAuditRecordFn(\n wiring: CorrectionAccessWiring,\n namespace: string,\n record: {\n planId: string;\n classification: CorrectionPlan[\"classification\"];\n outcome: CorrectionOutcome;\n requestText: string;\n },\n): Promise<string> {\n const storage = await wiring.orchestrator.getStorage(namespace);\n // Corrections are themselves memories, searchable and namespaced (issue\n // #1580 design §4). Write a correction-category memory capturing the\n // plan + outcome as the audit trail.\n const id = await storage.writeMemory(\"correction\", buildAuditBody(record), {\n source: \"correction-contract\",\n confidence: 1.0,\n tags: [\"correction-audit\", `plan:${record.planId}`, `classification:${record.classification}`],\n });\n return id;\n}\n\nfunction buildAuditBody(record: {\n planId: string;\n classification: CorrectionPlan[\"classification\"];\n outcome: CorrectionOutcome;\n requestText: string;\n}): string {\n // Never-store / redaction corrections carry the very secret/pattern the user\n // asked Remnic NOT to retain — withhold the request text from the durable\n // audit memory so we don't persist it verbatim (#1580 review, P1).\n const sensitive =\n record.classification === \"never_store\" ||\n record.outcome.results.some((r) => r.action.kind === \"redaction_rule\");\n const safeRequest = sensitive\n ? \"[redacted — never-store/redaction correction text withheld from the audit trail]\"\n : record.requestText.slice(0, 200);\n const lines = [\n `Correction plan ${record.planId} applied (${record.outcome.status}).`,\n \"\",\n `Request: ${safeRequest}`,\n `Classification: ${record.classification}`,\n `Applied at: ${record.outcome.appliedAt}`,\n \"\",\n \"Actions:\",\n ];\n for (const r of record.outcome.results) {\n // Never-store/redaction action errors can echo the secret/pattern — withhold\n // the error text for those actions (review thread OhjwW).\n const withhold =\n r.action.kind === \"redaction_rule\" || record.classification === \"never_store\";\n const errPart = r.error ? (withhold ? \" (error withheld)\" : ` (${r.error})`) : \"\";\n lines.push(` - ${r.action.kind}: ${r.status}${errPart}`);\n }\n return lines.join(\"\\n\");\n}\n\nasync function propagateFn(\n wiring: CorrectionAccessWiring,\n namespace: string,\n touchedMemoryIds: readonly string[],\n): Promise<void> {\n // Best-effort post-write propagation. The orchestrator's indexPersistedMemory\n // fires the QMD reindex for a touched file (checklist §31). A failure here\n // is non-fatal — the executor records it as a warning, never a failed action.\n for (const id of touchedMemoryIds) {\n try {\n // indexPersistedMemory is keyed by the namespace's storage, NOT the\n // default namespace (review thread: propagation-hardcodes-default-ns).\n const storage = await wiring.orchestrator.getStorage(namespace);\n const orchestrator = wiring.orchestrator as unknown as {\n indexPersistedMemory?(storage: unknown, memoryId: string): Promise<void>;\n };\n if (typeof orchestrator.indexPersistedMemory === \"function\") {\n await orchestrator.indexPersistedMemory(storage, id);\n }\n } catch {\n // Swallow — propagation is best-effort.\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n\nfunction toCandidate(m: MemoryFile, namespace: string, score: number): PlannerCandidate {\n return {\n memoryId: m.frontmatter.id,\n path: m.path,\n content: m.content,\n excerpt: m.content.slice(0, 160),\n ...(m.frontmatter.entityRef ? { entityRef: m.frontmatter.entityRef } : {}),\n score,\n // namespace is implicit (the planner scopes by namespace); keep it on the\n // candidate for diff rendering if needed.\n } satisfies PlannerCandidate & { namespace?: string };\n}\n\nfunction toExecutorMemory(m: MemoryFile): ExecutorMemory {\n const fm = m.frontmatter;\n // The tombstone hash must use the ORIGINAL unsuffixed body: writeMemory\n // appends an `[Attributes: …]` suffix when structuredAttributes are set, so\n // hashing m.content would never match the pre-suffix content hash (thread\n // OhX2N, rule 23). Strip the suffix when attributes are present.\n const rawBody = fm.structuredAttributes ? stripAttributesSuffix(m.content) : m.content;\n return {\n memoryId: fm.id,\n content: m.content,\n category: fm.category,\n rawContent: rawBody,\n ...(fm.entityRef ? { entityRef: fm.entityRef } : {}),\n } satisfies ExecutorMemory;\n}\n\n/**\n * The single source of truth for whether the Correction Contract feature is\n * enabled. Reads BOTH config shapes so tool visibility and the runtime gate\n * can never drift out of sync (review thread: correction-gate-config-mismatch):\n * - nested: `config.correction.enabled`\n * - flat: the `correctionEnabled` legacy key (ratchet-safe shape)\n * Nested wins when present; both default to `true` (plan is read-only, safe on).\n */\nexport function isCorrectionFeatureEnabled(config: PluginConfig): boolean {\n // parseConfig now resolves this into `config.correctionEnabled` (review\n // thread Txp) — prefer the parsed boolean so operator config actually takes\n // effect. The loose nested/flat read stays as a fallback for PluginConfig-\n // shaped objects built without parseConfig (unit tests).\n if (typeof config.correctionEnabled === \"boolean\") return config.correctionEnabled;\n const nested = (config as unknown as Record<string, unknown>).correction as\n | Record<string, unknown>\n | undefined;\n if (nested && typeof nested.enabled === \"boolean\") return nested.enabled;\n if (nested && typeof nested.enabled === \"string\") return nested.enabled === \"true\" || nested.enabled === \"1\";\n return readCorrectionFlag(config, \"enabled\", true);\n}\n\n/** Read a boolean correction flag from the loosely-typed config (ratchet-safe). */\nfunction readCorrectionFlag(config: PluginConfig, key: string, fallback: boolean): boolean {\n // Nested shape wins: config.correction.<key> (review thread: nested-correction-settings).\n const nested = (config as unknown as Record<string, unknown>).correction as\n | Record<string, unknown>\n | undefined;\n if (nested && typeof nested[key] === \"boolean\") return nested[key] as boolean;\n if (nested && typeof nested[key] === \"string\") return (nested[key] as string) === \"true\" || (nested[key] as string) === \"1\";\n // Flat legacy shape: config.correction<Key>.\n const raw = (config as unknown as Record<string, unknown>)[`correction${capitalize(key)}`];\n if (typeof raw === \"boolean\") return raw;\n if (typeof raw === \"string\") return raw === \"true\" || raw === \"1\";\n return fallback;\n}\n\n/** Read a numeric correction value from the loosely-typed config (ratchet-safe). */\nfunction readCorrectionNumber(config: PluginConfig, key: string, fallback: number): number {\n // Nested shape wins: config.correction.<key> (review thread: nested-correction-settings).\n const nested = (config as unknown as Record<string, unknown>).correction as\n | Record<string, unknown>\n | undefined;\n if (nested) {\n const nv = nested[key];\n if (typeof nv === \"number\" && Number.isFinite(nv)) return nv;\n if (typeof nv === \"string\") {\n const nn = Number(nv);\n if (Number.isFinite(nn)) return nn;\n }\n }\n const raw = (config as unknown as Record<string, unknown>)[`correction${capitalize(key)}`];\n if (typeof raw === \"number\" && Number.isFinite(raw)) return raw;\n if (typeof raw === \"string\") {\n const n = Number(raw);\n if (Number.isFinite(n)) return n;\n }\n return fallback;\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction errMsg(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\n// ---------------------------------------------------------------------------\n// LLM classify+draft (Responses API only — gotcha 1)\n// ---------------------------------------------------------------------------\n\nconst CLASSIFY_SYSTEM_PROMPT = `You classify memory corrections and draft per-memory actions.\n\nGiven a correction statement and candidate memories, respond with a JSON object:\n{\n \"classification\": \"wrong\" | \"outdated\" | \"incomplete\" | \"wrong_scope\" | \"never_store\",\n \"confidence\": <number 0..1>,\n \"actions\": [<one or more correction actions>],\n \"relevance\": [{\"memoryId\": \"<id>\", \"why\": \"<one short sentence>\"}]\n}\n\nAction shapes:\n- {\"kind\":\"supersede\",\"loserId\":\"<id>\",\"replacement\":{\"content\":\"<new fact>\"}}\n- {\"kind\":\"edit\",\"memoryId\":\"<id>\",\"patch\":\"<new full content>\"}\n- {\"kind\":\"retract\",\"memoryId\":\"<id>\"}\n- {\"kind\":\"rescope\",\"memoryId\":\"<id>\",\"toNamespace\":\"<ns>\"}\n- {\"kind\":\"redaction_rule\",\"pattern\":\"<bounded literal or regex>\"}\n\nOnly emit actions you are confident in. If uncertain, return confidence < 0.5 and few actions.`;\n\nfunction buildClassifyPrompt(text: string, candidates: PlannerCandidate[]): string {\n const lines = [\n `Correction: ${text}`,\n \"\",\n \"Candidate memories:\",\n ];\n for (const c of candidates.slice(0, 20)) {\n lines.push(`[${c.memoryId}] ${c.excerpt}`);\n }\n lines.push(\"\", \"Respond with the JSON object only.\");\n return lines.join(\"\\n\");\n}\n\nfunction parseClassifyResponse(\n raw: string,\n candidates: PlannerCandidate[],\n): LlmClassificationResult {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return fallbackClassification(candidates, \"LLM returned non-JSON response\");\n }\n if (!parsed || typeof parsed !== \"object\") {\n return fallbackClassification(candidates, \"LLM returned non-object response\");\n }\n const obj = parsed as Record<string, unknown>;\n const classification = isClassification(obj.classification) ? obj.classification : \"outdated\";\n const confidence = typeof obj.confidence === \"number\" ? Math.min(1, Math.max(0, obj.confidence)) : 0.5;\n const rawActions = Array.isArray(obj.actions) ? obj.actions : [];\n const actions: CorrectionAction[] = [];\n const warnings: string[] = [];\n for (const rawAction of rawActions) {\n try {\n validateCorrectionAction(rawAction);\n actions.push(rawAction);\n } catch (err) {\n warnings.push(`dropped malformed action: ${errMsg(err)}`);\n }\n }\n const relevance = Array.isArray(obj.relevance)\n ? (obj.relevance as unknown[])\n .filter((r): r is Record<string, unknown> => !!r && typeof r === \"object\")\n .map((r) => ({\n memoryId: typeof r.memoryId === \"string\" ? r.memoryId : \"\",\n why: typeof r.why === \"string\" ? r.why : \"\",\n }))\n .filter((r) => r.memoryId.length > 0)\n : [];\n return {\n classification,\n confidence,\n actions,\n relevance,\n warnings,\n };\n}\n\nfunction isClassification(value: unknown): value is CorrectionPlan[\"classification\"] {\n return (\n value === \"wrong\" ||\n value === \"outdated\" ||\n value === \"incomplete\" ||\n value === \"wrong_scope\" ||\n value === \"never_store\"\n );\n}\n\nfunction fallbackClassification(\n candidates: PlannerCandidate[],\n reason: string,\n): LlmClassificationResult {\n return {\n classification: \"outdated\",\n confidence: 0,\n actions: [],\n relevance: candidates.map((c) => ({ memoryId: c.memoryId, why: \"located for review\" })),\n warnings: [reason],\n fallback: true,\n };\n}\n\n// Re-export the local helpers for tests that import this module directly.\n// (deterministicFallbackPlan and newPlanId live in correction-contract.ts and\n// are re-exported by the barrel from there — not duplicated here.)\nexport {\n buildAuditBody,\n buildClassifyPrompt,\n CLASSIFY_SYSTEM_PROMPT,\n parseClassifyResponse,\n};\n","/**\n * correction/correction-planner.ts — read-only planner (issue #1580 PR 1).\n *\n * The planner turns a {@link CorrectionRequest} into a {@link CorrectionPlan}:\n *\n * 1. LOCATE. `targetIds` present → resolve directly (not-found → explicit\n * error, rule 34). Else: search via the injected search path scoped to\n * the caller's readable namespaces, plus 1-hop entity-graph neighbors of\n * top hits. Cap candidates at `maxAffected`.\n * 2. CLASSIFY + DRAFT via one LLM call (Responses API only, gotcha 1). On\n * LLM failure → deterministic fallback plan (rule 13): classification\n * `outdated`, `confidence: 0`, empty actions, warning set.\n * 3. RENDER DIFF by materializing what each action would do, using the\n * injected diff renderer (page-versioning — reuse, don't fork).\n * 4. PERSIST the plan atomically under\n * `<memoryDir>/state/corrections/pending/<planId>.json` (rule 54). TTL\n * default 24h; expired plans are rejected at apply with a clear error.\n *\n * The planner NEVER writes memory state — that is the executor's job. The\n * only thing it persists is the plan document itself.\n */\n\nimport { mkdir, readFile, rename, unlink, readdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { serializeMutations } from \"../utils/serialize-mutations.js\";\nimport {\n CORRECTION_TEXT_MAX,\n CorrectionContractError,\n deterministicFallbackPlan,\n newPlanId,\n validateCorrectionAction,\n validateCorrectionRequest,\n validateRedactionPattern,\n type CorrectionAction,\n type CorrectionAffectedEntry,\n type CorrectionClassification,\n type CorrectionPlan,\n type CorrectionRequest,\n type MemoryDraft,\n} from \"./correction-contract.js\";\n\n// ---------------------------------------------------------------------------\n// Injected collaborators — kept narrow so the planner is unit-testable.\n// ---------------------------------------------------------------------------\n\n/** A memory the planner located for the user. */\nexport interface PlannerCandidate {\n memoryId: string;\n /** Path relative to the storage dir (for diff rendering). */\n path: string;\n content: string;\n excerpt: string;\n category?: string;\n entityRef?: string;\n /** Provenance source quote (#1575), if available. */\n sourceQuote?: string;\n /** Search relevance / neighbor score, descending. */\n score: number;\n}\n\nexport interface PlannerDeps {\n // readableNamespaces is passed per-call to plan() so a single planner\n // instance serves requests from callers with different read scopes.\n /**\n * Search memories for the correction text. The implementation is supplied\n * by the service and reuses the existing recall/QMD search path scoped to\n * `readableNamespaces`. Returns at most `limit` candidates.\n */\n searchCorpus(request: {\n text: string;\n namespaces: readonly string[];\n limit: number;\n }): Promise<PlannerCandidate[]>;\n /**\n * Resolve explicit `targetIds` to candidates. Not-found is an explicit\n * error (rule 34): the implementation throws with the missing id.\n */\n resolveTargets(request: {\n targetIds: readonly string[];\n namespaces: readonly string[];\n }): Promise<PlannerCandidate[]>;\n /**\n * One-hop entity-graph neighbors of the supplied candidates. Returns the\n * union (deduped by memoryId); neighbors not already in `seedIds` only.\n */\n expandNeighbors(request: {\n seedIds: readonly string[];\n namespaces: readonly string[];\n limit: number;\n }): Promise<PlannerCandidate[]>;\n /**\n * Single LLM call (Responses API). Classifies the correction and drafts\n * per-memory actions. Implementations MUST return a deterministic fallback\n * shape on LLM failure (see {@link LlmClassificationResult.fallback}) so the\n * planner never throws on an LLM outage (rule 13).\n */\n classifyAndDraft(request: {\n text: string;\n candidates: PlannerCandidate[];\n }): Promise<LlmClassificationResult>;\n /**\n * Render a human-readable diff preview for the planned actions, using\n * page-versioning snapshot/diff (reuse, don't fork). Pure — no side effects.\n */\n renderDiff(request: {\n candidates: PlannerCandidate[];\n actions: CorrectionAction[];\n }): Promise<string>;\n /** Per-namespace storage dir root (so the plan lands in the right state/). */\n storageDir(namespace: string): Promise<string>;\n /** Max affected memories per plan (issue config: default 10). */\n readonly maxAffected: number;\n /** Plan TTL in hours (issue config: default 24). */\n readonly planTtlHours: number;\n /** Injected clock for deterministic tests. */\n now(): Date;\n}\n\n/** Result of the single LLM classify+draft call. */\nexport interface LlmClassificationResult {\n classification: CorrectionClassification;\n confidence: number;\n /** Per-memory drafted actions (already validated by the adapter). */\n actions: CorrectionAction[];\n /** Per-memory relevance notes — why each affected memory is in the plan. */\n relevance: ReadonlyArray<{ memoryId: string; why: string }>;\n warnings: string[];\n /** True when the adapter fell back due to an LLM outage (rule 13). */\n fallback?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Planner\n// ---------------------------------------------------------------------------\n\nexport class CorrectionPlanner {\n constructor(private readonly deps: PlannerDeps) {}\n\n /**\n * Plan a correction. `readableNamespaces` is the AUTHORIZED read scope the\n * service resolved for this caller; the planner never trusts a caller string.\n */\n async plan(\n request: CorrectionRequest,\n readableNamespaces: readonly string[],\n ): Promise<CorrectionPlan> {\n const cleaned = validateCorrectionRequest(request);\n if (cleaned.text.length > CORRECTION_TEXT_MAX) {\n // Defensive double-check; validateCorrectionRequest already throws.\n throw new CorrectionContractError(\"CorrectionRequest.text too long.\");\n }\n\n if (cleaned.targetIds && cleaned.targetIds.length > this.deps.maxAffected) {\n throw new CorrectionContractError(\n `Correction target list (${cleaned.targetIds.length}) exceeds maxAffected (${this.deps.maxAffected}) — narrow the target set.`,\n );\n }\n\n const namespaces = readableNamespaces;\n if (namespaces.length === 0) {\n throw new CorrectionContractError(\n \"Correction requires at least one readable namespace — principal has no read scope.\",\n );\n }\n\n // 1. LOCATE\n const located = await this.locateCandidates(cleaned, namespaces);\n if (located.candidates.length === 0) {\n // No candidates AND no explicit targets → empty plan is valid (the user\n // can still discard). But explicit-target-not-found was already raised\n // inside locateCandidates (rule 34).\n return this.persist(\n this.emptyPlan(cleaned, namespaces[0], \"no matching memories found for the correction text\"),\n );\n }\n\n // 2. CLASSIFY + DRAFT\n const llm = await this.deps.classifyAndDraft({\n text: cleaned.text,\n candidates: located.candidates,\n });\n\n // Validate every action shape before persisting — the adapter is trusted\n // to validate, but a malformed action must never reach the executor\n // (defense in depth, rule 51).\n for (const action of llm.actions) {\n validateCorrectionAction(action);\n if (action.kind === \"redaction_rule\") {\n validateRedactionPattern(action.pattern);\n }\n }\n\n// 3. Map actions → affected entries (only memories the planner located).\n const affected = this.deriveAffected(located.candidates, llm);\n\n // Bulk guard (§39): refuse past maxAffected without silent truncation.\n const touchedCount = new Set(\n llm.actions\n .map((a) =>\n a.kind === \"supersede\"\n ? a.loserId\n : a.kind === \"edit\" || a.kind === \"retract\" || a.kind === \"rescope\"\n ? a.memoryId\n : null,\n )\n .filter((id): id is string => id !== null),\n ).size;\n if (touchedCount > this.deps.maxAffected) {\n throw new CorrectionContractError(\n `Correction touches ${touchedCount} memories, exceeding the maxAffected limit of ${this.deps.maxAffected}. Narrow the correction text or supply explicit targetIds.`,\n );\n }\n\n // Defense in depth (review thread: reject-actions-outside-candidates):\n // an LLM that hallucinates or is prompt-injected must never target a\n // memory the planner did not locate. NOW that the bulk guard has had its\n // chance to reject over-limit plans, drop actions whose target ID is\n // absent from the candidate set and warn.\n const candidateIds = new Set(located.candidates.map((c) => c.memoryId));\n if (llm.actions.length > 0 && candidateIds.size > 0) {\n const filtered = llm.actions.filter((action) => {\n const id =\n action.kind === \"supersede\"\n ? action.loserId\n : action.kind === \"edit\" || action.kind === \"retract\" || action.kind === \"rescope\"\n ? action.memoryId\n : null;\n return id === null || candidateIds.has(id);\n });\n if (filtered.length < llm.actions.length) {\n const dropped = llm.actions.length - filtered.length;\n llm.warnings = [...llm.warnings, `${dropped} action(s) targeted memories outside the located candidate set and were dropped (prompt-injection guard).`];\n llm.actions = filtered;\n }\n }\n\n // 4. RENDER DIFF\n const diff = llm.fallback\n ? \"\"\n : await this.deps.renderDiff({ candidates: located.candidates, actions: llm.actions });\n\n const createdAt = this.deps.now().toISOString();\n const expiresAt = new Date(\n this.deps.now().getTime() + this.deps.planTtlHours * 60 * 60 * 1000,\n ).toISOString();\n\n const plan: CorrectionPlan = llm.fallback\n ? deterministicFallbackPlan({\n request: cleaned,\n namespace: namespaces[0],\n affected,\n warnings: llm.warnings,\n createdAt,\n expiresAt,\n })\n : {\n planId: newPlanId(),\n request: cleaned,\n namespace: namespaces[0],\n affected,\n classification: llm.classification,\n actions: llm.actions,\n diff,\n confidence: clampConfidence(llm.confidence),\n warnings: llm.warnings,\n createdAt,\n expiresAt,\n status: \"pending\",\n };\n\n return this.persist(plan);\n }\n\n private async locateCandidates(\n request: CorrectionRequest,\n namespaces: readonly string[],\n ): Promise<{ candidates: PlannerCandidate[] }> {\n if (request.targetIds && request.targetIds.length > 0) {\n const targets = await this.deps.resolveTargets({\n targetIds: request.targetIds,\n namespaces,\n });\n // Expand 1-hop neighbors and merge (dedup by memoryId, keep highest score).\n const seedIds = targets.map((c) => c.memoryId);\n const neighbors = await this.deps.expandNeighbors({\n seedIds,\n namespaces,\n limit: this.deps.maxAffected,\n });\n return { candidates: mergeCandidates(targets, neighbors).slice(0, this.deps.maxAffected) };\n }\n\n const searched = await this.deps.searchCorpus({\n text: request.text,\n namespaces,\n limit: this.deps.maxAffected,\n });\n if (searched.length === 0) return { candidates: [] };\n const seedIds = searched.slice(0, Math.min(5, searched.length)).map((c) => c.memoryId);\n const neighbors = await this.deps.expandNeighbors({\n seedIds,\n namespaces,\n limit: this.deps.maxAffected,\n });\n return { candidates: mergeCandidates(searched, neighbors).slice(0, this.deps.maxAffected) };\n }\n\n private deriveAffected(\n candidates: PlannerCandidate[],\n llm: LlmClassificationResult,\n ): CorrectionAffectedEntry[] {\n const byId = new Map(candidates.map((c) => [c.memoryId, c]));\n const out: CorrectionAffectedEntry[] = [];\n for (const rel of llm.relevance) {\n const c = byId.get(rel.memoryId);\n if (!c) continue;\n out.push({\n memoryId: c.memoryId,\n path: c.path,\n excerpt: c.excerpt,\n why: rel.why,\n ...(c.sourceQuote ? { sourceQuote: c.sourceQuote } : {}),\n });\n }\n // Include touched memories that the LLM did not annotate but whose actions\n // reference them (e.g. an `edit` drafted against a located candidate).\n const annotated = new Set(out.map((e) => e.memoryId));\n for (const action of llm.actions) {\n const id =\n action.kind === \"supersede\"\n ? action.loserId\n : action.kind === \"edit\" || action.kind === \"retract\" || action.kind === \"rescope\"\n ? action.memoryId\n : null;\n if (id && !annotated.has(id)) {\n const c = byId.get(id);\n if (c) {\n out.push({\n memoryId: c.memoryId,\n path: c.path,\n excerpt: c.excerpt,\n why: \"touched by a drafted action\",\n ...(c.sourceQuote ? { sourceQuote: c.sourceQuote } : {}),\n });\n annotated.add(id);\n }\n }\n }\n return out;\n }\n\n private emptyPlan(request: CorrectionRequest, namespace: string, warning: string): CorrectionPlan {\n const createdAt = this.deps.now().toISOString();\n const expiresAt = new Date(\n this.deps.now().getTime() + this.deps.planTtlHours * 60 * 60 * 1000,\n ).toISOString();\n return {\n planId: newPlanId(),\n request,\n namespace,\n affected: [],\n classification: \"outdated\",\n actions: [],\n diff: \"\",\n confidence: 0,\n warnings: [warning],\n createdAt,\n expiresAt,\n status: \"pending\",\n };\n }\n\n // -------------------------------------------------------------------------\n // Plan persistence — atomic write (rule 54), serialized per plan id (rule 40).\n // -------------------------------------------------------------------------\n\n /** Directory holding pending plans for one namespace. */\n private async pendingDir(namespace: string): Promise<string> {\n return path.join(await this.deps.storageDir(namespace), \"state\", \"corrections\", \"pending\");\n }\n\n private async persist(plan: CorrectionPlan): Promise<CorrectionPlan> {\n const dir = await this.pendingDir(plan.namespace);\n const target = path.join(dir, `${plan.planId}.json`);\n await serializeMutations(`correction-plan:${target}`, async () => {\n await mkdir(dir, { recursive: true });\n const tmp = `${target}.${process.pid}.${Date.now().toString(36)}.tmp`;\n await writeFile(tmp, `${JSON.stringify(plan)}\\n`, \"utf-8\");\n // rename() is atomic on POSIX for same-filesystem renames (rule 54).\n await rename(tmp, target);\n });\n return plan;\n }\n\n /** Load a pending plan by id (used by the service / executor). */\n async loadPlan(namespace: string, planId: string): Promise<CorrectionPlan | null> {\n assertSafePlanId(planId);\n const file = path.join(await this.pendingDir(namespace), `${planId}.json`);\n return serializeMutations(`correction-plan:${file}`, async () => {\n let raw: string;\n try {\n raw = await readFile(file, \"utf-8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") return null;\n throw err;\n }\n return parsePlan(raw);\n });\n }\n\n /** List pending plans (newest first), excluding consumed (applied/discarded). */\n async listPending(namespace: string): Promise<CorrectionPlan[]> {\n const dir = await this.pendingDir(namespace);\n let files: string[];\n try {\n files = await readdir(dir);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") return [];\n throw err;\n }\n const plans: CorrectionPlan[] = [];\n for (const f of files) {\n if (!f.endsWith(\".json\")) continue;\n let plan: CorrectionPlan | null = null;\n try {\n plan = await this.loadPlan(namespace, f.replace(/\\.json$/, \"\"));\n } catch {\n continue; // rule 34 — skip malformed plans with a counter (best-effort).\n }\n if (plan && plan.status === \"pending\") plans.push(plan);\n }\n plans.sort((a, b) => (a.createdAt < b.createdAt ? 1 : a.createdAt > b.createdAt ? -1 : 0));\n return plans;\n }\n\n async markConsumed(namespace: string, planId: string, status: \"applying\" | \"applied\" | \"discarded\" | \"partial\"): Promise<void> {\n assertSafePlanId(planId);\n const file = path.join(await this.pendingDir(namespace), `${planId}.json`);\n await serializeMutations(`correction-plan:${file}`, async () => {\n let raw: string;\n try {\n raw = await readFile(file, \"utf-8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") return; // idempotent\n throw err;\n }\n const plan = parsePlan(raw);\n if (!plan) return;\n plan.status = status;\n const tmp = `${file}.${process.pid}.${Date.now().toString(36)}.tmp`;\n await writeFile(tmp, `${JSON.stringify(plan)}\\n`, \"utf-8\");\n await rename(tmp, file);\n });\n }\n\n async deletePlan(namespace: string, planId: string): Promise<void> {\n assertSafePlanId(planId);\n const file = path.join(await this.pendingDir(namespace), `${planId}.json`);\n await serializeMutations(`correction-plan:${file}`, async () => {\n try {\n await unlink(file);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") return;\n throw err;\n }\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Reject caller-supplied plan ids that would escape the pending-plan dir\n * (review thread Ug8): `correct --discard --plan-id ../../meta` must not let\n * `path.join(pendingDir, \"../../meta.json\")` unlink arbitrary `.json` files\n * under the storage root. A safe plan id is a bare basename with no path\n * separators and no parent-segment shape. The canonical format is\n * `corr-<base36>-<base36>` (see {@link newPlanId}); this guard accepts any\n * single-segment id so future formats need not touch it, but blocks every\n * traversal vector (`/`, `\\`, `..`, leading dots, NUL).\n */\nfunction assertSafePlanId(planId: string): void {\n if (\n typeof planId !== \"string\" ||\n planId.length === 0 ||\n planId.includes(\"/\") ||\n planId.includes(\"\\\\\") ||\n planId.includes(\"\\0\") ||\n planId === \".\" ||\n planId === \"..\" ||\n planId.startsWith(\".\") ||\n planId.includes(\"..\")\n ) {\n throw new CorrectionContractError(\n `Invalid plan id: ${JSON.stringify(planId)} — must be a bare file basename with no path separators or parent segments.`,\n );\n }\n}\nfunction clampConfidence(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.min(1, Math.max(0, n));\n}\n\n/** Merge two candidate lists, deduping by memoryId and keeping the highest score. */\nfunction mergeCandidates(a: readonly PlannerCandidate[], b: readonly PlannerCandidate[]): PlannerCandidate[] {\n const map = new Map<string, PlannerCandidate>();\n for (const c of a) {\n const existing = map.get(c.memoryId);\n if (!existing || c.score > existing.score) map.set(c.memoryId, c);\n }\n for (const c of b) {\n const existing = map.get(c.memoryId);\n if (!existing || c.score > existing.score) map.set(c.memoryId, c);\n }\n return [...map.values()].sort((x, y) => y.score - x.score);\n}\n\n/** Parse + structurally validate a plan document. Returns null on malformed. */\nexport function parsePlan(raw: string): CorrectionPlan | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== \"object\") return null;\n const p = parsed as Record<string, unknown>;\n if (typeof p.planId !== \"string\" || typeof p.namespace !== \"string\") return null;\n if (typeof p.createdAt !== \"string\" || typeof p.expiresAt !== \"string\") return null;\n if (!Array.isArray(p.actions) || !Array.isArray(p.affected)) return null;\n // Re-validate each action shape; a single malformed action poisons the plan.\n for (const action of p.actions as unknown[]) {\n try {\n validateCorrectionAction(action);\n } catch {\n return null;\n }\n }\n return parsed as CorrectionPlan;\n}\n","/**\n * correction/correction-executor.ts — the only writer for corrections\n * (issue #1580 PR 2).\n *\n * Applies a {@link CorrectionPlan} in NON-DESTRUCTIVE ORDER\n * (rule 25 / checklist §14 — new state confirmed before old state destroyed):\n *\n * 1. Write replacement / edited memories first (through the injected\n * persist pipeline so catalog/reindex/dedup fire — rule 43; edits go\n * through page-versioning so every change is revertable).\n * 2. Then supersede / retract losers: status flip + `validUntil` stamp\n * (#1578, when gated on) + tombstone append (#1579).\n * 3. Then propagation: QMD reindex for touched files, graph edge updates,\n * belief-ledger claim status (optional-package dynamic import, rule 57),\n * profile.md line removal when an affected memory was profile-sourced.\n * 4. Append an audit record to `corrections/` capturing plan + outcome —\n * corrections are themselves memories, searchable and namespaced.\n * 5. Partial failure → tagged partial result (rule 34; checklist §22):\n * never a half-applied plan reported as success, and never old state\n * destroyed for an action whose replacement write failed.\n *\n * Concurrency: a plan may be applied exactly once. A second apply of the same\n * plan is rejected (plan consumed). Backed by the planner's atomic plan-store\n * (`markConsumed`) under `serializeMutations` keyed by plan id (rule 40).\n */\n\nimport { serializeMutations } from \"../utils/serialize-mutations.js\";\nimport type { CorrectionPlanner } from \"./correction-planner.js\";\nimport {\n CorrectionContractError,\n validateCorrectionAction,\n validateRedactionPattern,\n type CorrectionAction,\n type CorrectionActionResult,\n type CorrectionOutcome,\n type CorrectionPlan,\n} from \"./correction-contract.js\";\n\n// ---------------------------------------------------------------------------\n// Injected collaborators\n// ---------------------------------------------------------------------------\n\n/** A memory the executor is operating on (subset of MemoryFile). */\nexport interface ExecutorMemory {\n memoryId: string;\n content: string;\n category: string;\n /** True when the memory has frontmatter provenance (#1575 sourceQuote). */\n sourceQuote?: string;\n /** Structured-attribute supersession key, when one exists. */\n supersessionKey?: string;\n entityRef?: string;\n /** Raw content used for the tombstone hash (rule 23). */\n rawContent: string;\n}\n\nexport interface ExecutorDeps {\n /** Lookup a memory by id within the plan's namespace. null if not found. */\n getMemory(namespace: string, memoryId: string): Promise<ExecutorMemory | null>;\n /**\n * Persist a NEW memory through the orchestrator's normal write pipeline\n * (catalog/reindex/dedup fire — rule 43). Returns the new memory id.\n * Used by `supersede` (the replacement).\n */\n writeReplacement(\n namespace: string,\n draft: {\n content: string;\n category?: string;\n confidence?: number;\n tags?: string[];\n entityRef?: string;\n validAt?: string;\n observedAt?: string;\n structuredAttributes?: Record<string, string>;\n supersedes?: string;\n },\n ): Promise<string>;\n /**\n * Apply a versioned edit to an existing memory (page-versioning — every\n * change revertable). Returns the memory id. The patch is the NEW full\n * content; the implementation snapshots the prior version.\n */\n applyEdit(\n namespace: string,\n memoryId: string,\n patch: string,\n ): Promise<string>;\n /**\n * Flip a memory's status to superseded/retracted and stamp `validUntil`\n * when bi-temporal is gated on (#1578). Idempotent.\n */\n retireMemory(\n namespace: string,\n memoryId: string,\n opts: {\n status: \"superseded\" | \"retracted\";\n supersededBy?: string;\n validUntil?: string;\n },\n ): Promise<void>;\n /**\n * Move a memory to a different namespace (rescope). The destination\n * namespace is re-authorized by the service before the executor runs; the\n * implementation performs the move atomically (write-then-unlink).\n */\n rescopeMemory(namespace: string, memoryId: string, toNamespace: string): Promise<string>;\n /**\n * Append a tombstone (#1579) for a retired memory. Returns the tombstone\n * id, or null if tombstones are disabled (off = pre-feature behavior).\n */\n appendTombstone(\n namespace: string,\n input: {\n reason: \"correction\" | \"supersession\" | \"retraction\";\n sourceMemoryId: string;\n rawContent: string;\n entityRef?: string;\n supersessionKey?: string;\n },\n ): Promise<string | null>;\n /**\n * Persist a redaction rule so extraction consults it the same way tombstones\n * are consulted (route through the same chokepoint check). Idempotent.\n */\n registerRedactionRule(namespace: string, pattern: string): Promise<void>;\n /**\n * Append an audit record under `corrections/` (existing storage category)\n * capturing plan + outcome. Returns the audit memory id.\n */\n appendAuditRecord(\n namespace: string,\n record: {\n planId: string;\n classification: CorrectionPlan[\"classification\"];\n outcome: CorrectionOutcome;\n requestText: string;\n },\n ): Promise<string>;\n /**\n * Post-write propagation: QMD reindex for touched files (checklist §31),\n * graph edge updates, belief-ledger claim status. Best-effort — a failure\n * here is recorded as a warning, never as a failed action (propagation is\n * not part of the §14 non-destructive-order guarantee; it runs after).\n */\n propagate(namespace: string, touchedMemoryIds: readonly string[]): Promise<void>;\n /** Whether the bi-temporal gate (#1578) is on. When off, validUntil is omitted. */\n readonly biTemporalEnabled: boolean;\n /** Injected clock for deterministic tests. */\n now(): Date;\n}\n\n// ---------------------------------------------------------------------------\n// Executor\n// ---------------------------------------------------------------------------\n\nexport class CorrectionExecutor {\n constructor(\n private readonly deps: ExecutorDeps,\n private readonly planner: CorrectionPlanner,\n ) {}\n\n /**\n * Apply a persisted plan by id. Idempotent-once: a second apply of the same\n * plan is rejected. Returns the {@link CorrectionOutcome}.\n */\n async apply(\n namespace: string,\n planId: string,\n opts: {\n confirm: boolean;\n /**\n * Authorize a rescope destination namespace. Bound per-request by the\n * service from the namespace policy + principal so a plan can never\n * write into a namespace the caller lacks write scope for (review\n * thread: authorize-rescope-destination). Defaults to allow when the\n * source namespace already resolves to a writable scope (single-tenant).\n */\n canWriteDestination?: (namespace: string) => Promise<boolean>;\n },\n ): Promise<CorrectionOutcome> {\n if (!opts.confirm) {\n throw new CorrectionContractError(\n \"Correction apply requires explicit confirmation (correction.applyRequiresConfirm).\",\n );\n }\n // serializeMutations on the plan id so two concurrent applies of the SAME\n // plan serialize — the second observes the consumed status and rejects.\n return serializeMutations(`correction-apply:${namespace}:${planId}`, () =>\n this.applyInternal(namespace, planId, opts.canWriteDestination),\n );\n }\n\n private async applyInternal(namespace: string, planId: string, canWriteDestination?: (namespace: string) => Promise<boolean>): Promise<CorrectionOutcome> {\n const plan = await this.planner.loadPlan(namespace, planId);\n if (!plan) {\n throw new CorrectionContractError(`Correction plan not found: ${planId}`);\n }\n if (plan.namespace !== namespace) {\n // Cross-namespace foreign-id guard (rule 42 / checklist §16).\n throw new CorrectionContractError(\n `Correction plan ${planId} belongs to namespace '${plan.namespace}', not '${namespace}'.`,\n );\n }\n if (plan.status === \"applied\" || plan.status === \"partial\" || plan.status === \"applying\") {\n throw new CorrectionContractError(\n `Correction plan ${planId} has already been applied or is in progress (status=${plan.status}).`,\n );\n }\n if (plan.status === \"discarded\") {\n throw new CorrectionContractError(`Correction plan ${planId} has been discarded.`);\n }\n // TTL check — expired plans are rejected with a clear error.\n if (this.deps.now().getTime() > new Date(plan.expiresAt).getTime()) {\n await this.planner.markConsumed(namespace, planId, \"discarded\");\n throw new CorrectionContractError(\n `Correction plan ${planId} expired at ${plan.expiresAt} and has been discarded.`,\n );\n }\n\n // Re-validate every action shape before applying (defense in depth).\n for (const action of plan.actions) {\n validateCorrectionAction(action);\n if (action.kind === \"redaction_rule\") {\n validateRedactionPattern(action.pattern);\n }\n }\n\n // Optimistically mark the plan `applying` BEFORE any mutation (review\n // thread OgIqt). If the process dies mid-apply — or the final\n // markConsumed(\"applied\"|\"partial\") fails — the plan stays `applying`\n // and is NOT silently retryable. A partially-applied plan must never be\n // re-applied wholesale: re-running succeeded actions would duplicate\n // replacements, tombstones, and audits. The operator inspects the\n // outcome and files a NEW plan for any failed actions. This mark runs\n // inside the serializeMutations lock so concurrent applies serialize.\n try {\n await this.planner.markConsumed(namespace, planId, \"applying\");\n } catch {\n // If we cannot even mark the plan in-progress, the filesystem is\n // likely unwritable and mutations would fail too — fail closed now.\n throw new CorrectionContractError(\n `Correction plan ${planId}: cannot mark in-progress (filesystem unwritable?) — aborting before any mutation.`,\n );\n }\n\n const results: CorrectionActionResult[] = [];\n const appliedTouched: string[] = [];\n\n // ── Phase 1: replacement / edit writes (new state first) ───────────────\n // For each supersede with a replacement, write the replacement FIRST. If\n // the write fails, the loser is NOT superseded (§14: never destroy old\n // state for an action whose replacement write failed).\n for (const action of plan.actions) {\n if (action.kind === \"supersede\" && action.replacement) {\n // Preflight the loser BEFORE writing the replacement (review thread\n // Of0pz): if the loser was deleted between plan and apply, writing a\n // replacement creates an orphan fact that supersedes nothing. Phase 2\n // (retireAndTombstone) re-checks via getMemory, so verifying here\n // keeps the two phases in agreement and avoids the orphan write.\n const loser = await this.deps.getMemory(namespace, action.loserId);\n if (!loser) {\n results.push({\n action,\n status: \"failed\",\n error: `supersede loser not found: ${action.loserId}`,\n });\n continue;\n }\n try {\n const newId = await this.deps.writeReplacement(namespace, {\n content: action.replacement.content,\n ...(action.replacement.category ? { category: action.replacement.category } : {}),\n ...(action.replacement.confidence !== undefined ? { confidence: action.replacement.confidence } : {}),\n ...(action.replacement.tags ? { tags: action.replacement.tags } : {}),\n ...(action.replacement.entityRef ? { entityRef: action.replacement.entityRef } : {}),\n ...(action.replacement.validAt ? { validAt: action.replacement.validAt } : {}),\n ...(action.replacement.observedAt ? { observedAt: action.replacement.observedAt } : {}),\n ...(action.replacement.structuredAttributes\n ? { structuredAttributes: action.replacement.structuredAttributes }\n : {}),\n supersedes: action.loserId,\n });\n results.push({ action, status: \"applied\", memoryId: newId });\n appliedTouched.push(newId);\n } catch (err) {\n results.push({\n action,\n status: \"failed\",\n error: errMsg(err),\n });\n }\n } else if (action.kind === \"edit\") {\n try {\n const editedId = await this.deps.applyEdit(namespace, action.memoryId, action.patch);\n results.push({ action, status: \"applied\", memoryId: editedId });\n appliedTouched.push(editedId);\n } catch (err) {\n results.push({ action, status: \"failed\", error: errMsg(err) });\n }\n } else if (action.kind === \"redaction_rule\") {\n try {\n await this.deps.registerRedactionRule(namespace, action.pattern);\n results.push({ action, status: \"applied\" });\n } catch (err) {\n results.push({ action, status: \"failed\", error: errMsg(err) });\n }\n }\n }\n\n // ── Phase 2: retire losers + tombstones ───────────────────────────────\n // Only run for supersede/retract actions whose replacement write (if any)\n // succeeded. A supersede WITHOUT a replacement is a pure retract.\n for (const action of plan.actions) {\n if (action.kind === \"supersede\") {\n const replacementResult = results.find(\n (r) => r.action === action && r.status === \"applied\",\n );\n // If the replacement write failed, skip retirement (§14).\n if (action.replacement && !replacementResult) {\n continue;\n }\n await this.retireAndTombstone(namespace, action, \"supersession\", results, appliedTouched, {\n supersededBy: replacementResult?.memoryId,\n });\n } else if (action.kind === \"retract\") {\n await this.retireAndTombstone(namespace, action, \"retraction\", results, appliedTouched);\n } else if (action.kind === \"rescope\") {\n try {\n // Authorize the destination namespace BEFORE the move — the plan's\n // toNamespace comes from the LLM/persisted plan and must not bypass\n // the write ACL (review thread: authorize-rescope-destination).\n // canWriteDestination defaults to allow when absent (single-tenant,\n // where the source namespace already resolved to a writable scope).\n const allowed = canWriteDestination ? await canWriteDestination(action.toNamespace) : true;\n if (!allowed) {\n results.push({\n action,\n status: \"failed\",\n error: `rescope destination namespace not writable: ${action.toNamespace}`,\n });\n continue;\n }\n const destId = await this.deps.rescopeMemory(namespace, action.memoryId, action.toNamespace);\n results.push({ action, status: \"applied\", memoryId: action.memoryId });\n appliedTouched.push(action.memoryId);\n // Propagate the destination memory in its namespace too (review\n // thread: propagate-rescoped-destination) — best-effort.\n try {\n await this.deps.propagate(action.toNamespace, [destId]);\n } catch {\n // non-fatal — the source propagation still fires.\n }\n } catch (err) {\n results.push({ action, status: \"failed\", error: errMsg(err) });\n }\n }\n }\n\n // ── Phase 3: propagation (post-write reindex + graph) ─────────────────\n // Best-effort: a propagation failure is recorded as a warning on the\n // outcome, never as a failed action (it runs AFTER the §14 guarantee).\n const propagationWarnings: string[] = [];\n if (appliedTouched.length > 0) {\n try {\n await this.deps.propagate(namespace, appliedTouched);\n } catch (err) {\n propagationWarnings.push(`propagation failed (non-fatal): ${errMsg(err)}`);\n }\n }\n\n // ── Phase 4: audit record ─────────────────────────────────────────────\n const anyFailed = results.some((r) => r.status === \"failed\");\n const status: CorrectionOutcome[\"status\"] = anyFailed ? \"partial\" : \"applied\";\n const appliedAt = this.deps.now().toISOString();\n const outcome: CorrectionOutcome = {\n planId,\n status,\n results,\n auditMemoryId: \"\", // filled after the audit write\n appliedAt,\n };\n if (propagationWarnings.length > 0) {\n (outcome as CorrectionOutcome & { warnings?: string[] }).warnings = propagationWarnings;\n }\n try {\n const auditId = await this.deps.appendAuditRecord(namespace, {\n planId,\n classification: plan.classification,\n outcome,\n requestText: plan.request.text,\n });\n outcome.auditMemoryId = auditId;\n } catch (err) {\n // The audit record is part of the contract but a failure to write it\n // must NOT undo the applied corrections. Record as a warning.\n (outcome as CorrectionOutcome & { warnings?: string[] }).warnings = [\n ...propagationWarnings,\n `audit record write failed (non-fatal): ${errMsg(err)}`,\n ];\n }\n\n // ── Phase 5: mark plan consumed ───────────────────────────────────────\n // Corrections are already applied (phases 1-4 succeeded). A markConsumed\n // failure must NOT propagate — that would make a client retry re-apply all\n // corrections (review thread: applyInternal-plan-unconsumed). Record as a\n // warning; the pending plan is reconciled by TTL discard on the next pass.\n try {\n await this.planner.markConsumed(namespace, planId, status);\n } catch (err) {\n const w = outcome as CorrectionOutcome & { warnings?: string[] };\n w.warnings = [...(w.warnings ?? []), `plan mark-consumed failed (non-fatal): ${errMsg(err)}`];\n }\n return outcome;\n }\n\n private async retireAndTombstone(\n namespace: string,\n action: CorrectionAction,\n reason: \"supersession\" | \"retraction\",\n results: CorrectionActionResult[],\n appliedTouched: string[],\n opts: { supersededBy?: string } = {},\n ): Promise<void> {\n const memoryId =\n action.kind === \"supersede\" ? action.loserId : action.kind === \"retract\" ? action.memoryId : null;\n if (!memoryId) return;\n try {\n const memory = await this.deps.getMemory(namespace, memoryId);\n if (!memory) {\n results.push({\n action,\n status: \"failed\",\n error: `memory not found: ${memoryId}`,\n });\n return;\n }\n const validUntil = this.deps.biTemporalEnabled ? this.deps.now().toISOString() : undefined;\n // Write the tombstone BEFORE retiring the source memory (review thread\n // PG9): if appendTombstone throws here, retireMemory has not run yet, so\n // the source stays active, the action fails cleanly, and a retry\n // operates on un-mutated state with no resurrection window. A tombstone\n // for a still-active memory (if retire later fails) is benign — it only\n // blocks re-ingestion of the same content, which is exactly the intent.\n const tombstoneId = await this.deps.appendTombstone(namespace, {\n reason,\n sourceMemoryId: memoryId,\n rawContent: memory.rawContent,\n ...(memory.entityRef ? { entityRef: memory.entityRef } : {}),\n ...(memory.supersessionKey ? { supersessionKey: memory.supersessionKey } : {}),\n });\n await this.deps.retireMemory(namespace, memoryId, {\n status: reason === \"supersession\" ? \"superseded\" : \"retracted\",\n ...(opts.supersededBy ? { supersededBy: opts.supersededBy } : {}),\n ...(validUntil ? { validUntil } : {}),\n });\n results.push({\n action,\n status: \"applied\",\n memoryId,\n ...(tombstoneId ? { tombstoneId } : {}),\n });\n appliedTouched.push(memoryId);\n } catch (err) {\n results.push({ action, status: \"failed\", error: errMsg(err) });\n }\n }\n}\n\nfunction errMsg(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n","/**\n * correction/correction-service.ts — thin orchestration for the Correction\n * Contract (issue #1580 PR 3).\n *\n * Owns the single plan/apply pipeline every correction surface (MCP / HTTP /\n * CLI) calls. Authorizes the caller through the existing namespace policy,\n * constructs the planner + executor with their collaborators, and exposes\n * `plan` / `apply` / `listPending` / `discard`.\n *\n * The service NEVER accepts a caller-supplied namespace without policy\n * validation (rule 42; checklist §16 foreign-ID guard): a principal may only\n * PLAN against namespaces they can READ and APPLY against namespaces they can\n * WRITE — both resolved through the injected namespace policy.\n */\n\nimport {\n CorrectionContractError,\n type CorrectionOutcome,\n type CorrectionPlan,\n type CorrectionRequest,\n} from \"./correction-contract.js\";\nimport { CorrectionPlanner, type PlannerDeps } from \"./correction-planner.js\";\nimport { CorrectionExecutor, type ExecutorDeps } from \"./correction-executor.js\";\n\n// ---------------------------------------------------------------------------\n// Injected collaborators — the access-service wires these from the orchestrator.\n// ---------------------------------------------------------------------------\n\n/**\n * Namespace policy façade. The access-service already implements these via\n * `resolveWritableNamespace` / `canReadNamespace` / `canWriteNamespace`; the\n * correction service consumes them through this narrow interface so the\n * correction modules stay decoupled from the access-service's surface area.\n */\nexport interface CorrectionNamespacePolicy {\n /**\n * Resolve the AUTHORIZED effective namespace for a plan/apply request.\n * Throws when the caller lacks read+write scope. The returned namespace is\n * the ONE the planner + executor operate in.\n */\n resolveAuthorizedNamespace(request: {\n namespace?: string;\n sessionKey?: string;\n principal?: string;\n }): Promise<string>;\n /**\n * Whether the caller may WRITE a given namespace. Used to authorize rescope\n * destinations before the executor moves a memory across namespaces.\n */\n canWriteNamespace(request: {\n namespace: string;\n sessionKey?: string;\n principal?: string;\n }): Promise<boolean>;\n /** Namespaces the caller may read — scopes the planner's search. */\n readableNamespaces(request: {\n namespace?: string;\n sessionKey?: string;\n principal?: string;\n }): Promise<readonly string[]>;\n}\n\n/**\n * Factory wiring for the planner + executor deps. The service holds ONE\n * planner + ONE executor for its lifetime; both are stateless across requests\n * (per-request scope is threaded through method parameters, rules 11/47).\n */\nexport interface CorrectionServiceDeps {\n readonly policy: CorrectionNamespacePolicy;\n /**\n * Planner collaborators. The planner takes `readableNamespaces` per call to\n * `plan()`, so a single deps instance serves callers with different scopes.\n */\n plannerDeps(): PlannerDeps;\n /** Executor collaborators (one per service). */\n executorDeps(): ExecutorDeps;\n /** Whether the correction feature is enabled (gate). */\n isEnabled(): boolean;\n /** Whether apply requires explicit confirmation (rule 48). */\n applyRequiresConfirm(): boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------\n\nexport class CorrectionService {\n private readonly planner: CorrectionPlanner;\n private readonly executor: CorrectionExecutor;\n\n constructor(private readonly deps: CorrectionServiceDeps) {\n this.planner = new CorrectionPlanner(deps.plannerDeps());\n this.executor = new CorrectionExecutor(deps.executorDeps(), this.planner);\n }\n\n /**\n * Plan a correction. Read-only — produces + persists a {@link CorrectionPlan}.\n * Safe to run with the gate ON (default) since the plan writes no memory state.\n *\n * The plan is stored under the AUTHORIZED (writable) namespace — the same\n * namespace apply/listPending/discard resolve — so the plan is always\n * found by the caller that created it (review threads: plan-stored-wrong-\n * namespace + cross-namespace candidate mismatch). Candidates are located\n * within that single namespace too: a correction can only affect memories\n * the principal can write, so reading from a non-writable namespace to\n * draft a correction that can never be applied would be misleading.\n */\n async plan(request: CorrectionRequest): Promise<CorrectionPlan> {\n this.requireEnabled();\n const authorized = await this.deps.policy.resolveAuthorizedNamespace(request);\n return this.planner.plan(request, [authorized]);\n }\n\n /**\n * Apply a persisted plan by id. Requires `confirm: true` (or interactive\n * yes at the CLI) when `applyRequiresConfirm` is on (default).\n */\n async apply(\n planId: string,\n opts: {\n confirm?: boolean;\n namespace?: string;\n sessionKey?: string;\n principal?: string;\n },\n ): Promise<CorrectionOutcome> {\n this.requireEnabled();\n if (this.deps.applyRequiresConfirm() && opts.confirm !== true) {\n throw new CorrectionContractError(\n \"Correction apply requires explicit confirmation (pass `confirm: true`).\",\n );\n }\n const namespace = await this.deps.policy.resolveAuthorizedNamespace({\n ...(opts.namespace ? { namespace: opts.namespace } : {}),\n ...(opts.sessionKey ? { sessionKey: opts.sessionKey } : {}),\n ...(opts.principal ? { principal: opts.principal } : {}),\n });\n return this.executor.apply(namespace, planId, {\n confirm: true,\n // Authorize rescope destinations through the namespace policy bound to\n // THIS caller's principal (review thread: authorize-rescope-destination).\n canWriteDestination: (dest) =>\n this.deps.policy.canWriteNamespace({\n namespace: dest,\n ...(opts.sessionKey ? { sessionKey: opts.sessionKey } : {}),\n ...(opts.principal ? { principal: opts.principal } : {}),\n }),\n });\n }\n\n /** List pending (not-yet-applied) plans for the caller's namespace. */\n async listPending(opts: {\n namespace?: string;\n sessionKey?: string;\n principal?: string;\n }): Promise<CorrectionPlan[]> {\n this.requireEnabled();\n const namespace = await this.deps.policy.resolveAuthorizedNamespace(opts);\n return this.planner.listPending(namespace);\n }\n\n /** Discard a pending plan. Idempotent. */\n async discard(\n planId: string,\n opts: { namespace?: string; sessionKey?: string; principal?: string },\n ): Promise<void> {\n this.requireEnabled();\n const namespace = await this.deps.policy.resolveAuthorizedNamespace(opts);\n await this.planner.markConsumed(namespace, planId, \"discarded\");\n await this.planner.deletePlan(namespace, planId);\n }\n\n private requireEnabled(): void {\n if (!this.deps.isEnabled()) {\n throw new CorrectionContractError(\n \"Correction Contract is disabled (set correction.enabled = true to enable).\",\n );\n }\n }\n}\n","/**\n * correction/passive-correction-detector.ts — morphology-aware heuristic detector\n * for corrections expressed passively in conversation turns (issue #1581).\n *\n * Users correct their agents constantly in conversation: \"no, we renamed that\n * service\", \"I don't use Vim anymore\", \"the deadline moved to Friday\". This\n * module detects those corrections from turn text — NO extra LLM call — so the\n * passive-capture module can route them to the Correction Contract (#1580).\n *\n * Design:\n * - Pure function: `detectPassiveCorrections(turns)` → `PassiveCorrection[]`.\n * - Only USER turns are scanned (assistants don't correct their own memory).\n * - Morphology-aware: matches conjugations/variants per AGENTS.md intent\n * guardrail (\"heuristics must be morphology-aware and precedence-tested\").\n * - Anti-fixture guards reject: hypotheticals, third-party corrections,\n * quoted-speech corrections, and self-resolving corrections.\n *\n * The detector is deliberately CONSERVATIVE — it prefers false negatives\n * (missing a correction) over false positives (flooding the review queue).\n * The nightly contradiction scan and the interactive correction chat (#1583)\n * catch what passive detection misses.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type PassiveCorrectionPolarity = \"update\" | \"retract\" | \"stop_storing\";\n\nexport interface PassiveCorrection {\n /** What the user says is wrong (\"Redis\", \"the deadline\", \"my editor preference\"). */\n targetHint: string;\n /** The new truth, or \"\" for pure retractions. */\n correctedAssertion: string;\n polarity: PassiveCorrectionPolarity;\n /** `[m:xxxx]` handle references found verbatim in the turn (#1582). */\n handles: string[];\n /** Heuristic confidence in [0, 1] — influences auto-apply gating. */\n confidence: number;\n /** The verbatim source text that triggered the detection. */\n sourceExcerpt: string;\n /** Index of the turn in the input array that produced this correction. */\n turnIndex: number;\n}\n\nexport interface DetectorTurn {\n role: \"user\" | \"assistant\" | \"other\";\n content: string;\n}\n\n// ---------------------------------------------------------------------------\n// Pattern definitions\n// ---------------------------------------------------------------------------\n\ninterface CorrectionPattern {\n polarity: PassiveCorrectionPolarity;\n /** Regex MUST be case-insensitive and use word boundaries where appropriate. */\n regex: RegExp;\n /** Confidence assigned when this pattern is the sole signal. */\n confidence: number;\n}\n\n/**\n * Strong correction signals — these phrases reliably indicate the user is\n * correcting a stored memory about their own world. Each is morphology-aware\n * (covers present/progressive/past tenses and common variants).\n */\nconst PATTERNS: readonly CorrectionPattern[] = [\n // ── update: new fact replaces / corrects an old one ──────────────────────\n {\n polarity: \"update\",\n // \"actually, it's X not Y\" / \"actually we use X now\"\n regex: /\\bactually[,\\s]+(?:it'?s|we|the|i|our|my|\\[m:)/i,\n confidence: 0.85,\n },\n {\n polarity: \"update\",\n // \"we switched/switching/switched to X\"\n regex: /\\bwe\\b\\s*(?:(?:'re|'ve|are|have)\\s+)?(?:switch(?:ed|ing)?|migrat(?:ed|ing)?|mov(?:ed|ing)?|chang(?:ed|ing)?)\\s+(?:to|over to|from)\\b/i,\n confidence: 0.9,\n },\n {\n polarity: \"update\",\n // \"we renamed X to Y\" / \"i renamed\"\n regex: /\\b(?:we|i)\\s+renam(?:ed|ing)\\b/i,\n confidence: 0.9,\n },\n {\n polarity: \"update\",\n // \"it's now X\" / \"the deadline moved to X\" / \"the date changed to X\"\n regex: /\\b(?:it'?s now|the\\s+(?:deadline|date|meeting|standup|review)\\s+(?:moved|chang(?:ed|ing))?\\s*to)\\b/i,\n confidence: 0.85,\n },\n {\n polarity: \"update\",\n // \"no, \" at the start of a turn or clause — strong correction signal\n regex: /(?:^|\\.\\s+|\\n)\\s*no[,\\s]+(?:we|i|it|the|our|my|that)\\b/i,\n confidence: 0.8,\n },\n {\n polarity: \"update\",\n // \"no longer\" / \"not anymore\" + an activity\n regex: /\\b(?:no longer|not anymore)\\b/i,\n confidence: 0.8,\n },\n {\n polarity: \"update\",\n // Standalone \"outdated\" — catches \"are both outdated\", \"is outdated\", etc.\n regex: /\\boutdated\\b/i,\n confidence: 0.75,\n },\n {\n polarity: \"update\",\n // \"that's outdated\" / \"that's old\"\n regex: /\\bthat'?s\\s+(?:outdated|old|no longer (?:true|correct|accurate))\\b/i,\n confidence: 0.85,\n },\n // ── retract: user says a fact is simply wrong ────────────────────────────\n {\n polarity: \"retract\",\n regex: /\\bthat'?s\\s+(?:wrong|not right|incorrect|not true|false)\\b/i,\n confidence: 0.85,\n },\n {\n polarity: \"retract\",\n // \"we don't use X anymore\" / \"I don't use X anymore\"\n regex: /\\b(?:we|i)\\s+don'?t\\s+use\\s+\\S+\\s+anymore\\b/i,\n confidence: 0.9,\n },\n {\n polarity: \"retract\",\n regex: /\\bforget about\\b/i,\n confidence: 0.8,\n },\n // ── stop_storing: user wants the agent to stop a behavior ────────────────\n {\n polarity: \"stop_storing\",\n regex: /\\bstop\\s+(?:suggesting|bringing\\s+up|recommending|reminding\\s+me\\s+(?:about)?)\\b/i,\n confidence: 0.9,\n },\n {\n polarity: \"stop_storing\",\n regex: /\\b(?:don'?t|never)\\s+(?:mention|suggest|recommend|store|bring\\s+up)\\b/i,\n confidence: 0.85,\n },\n];\n\n// ---------------------------------------------------------------------------\n// Anti-fixture guards\n// ---------------------------------------------------------------------------\n\n/**\n * Hypothetical markers — if present near the correction signal, the turn is\n * discussing a hypothetical scenario, not correcting a stored fact.\n */\nconst HYPOTHETICAL_REGEX =\n /\\b(?:if\\s+(?:we|you|i|they)\\s+(?:ever|were|could|might|should|decide)|what\\s+if|suppose|hypothetically|in\\s+(?:theory|principle)|let'?s\\s+say|imagine\\s+if)\\b/i;\n\n/**\n * Third-party correction markers — the user is correcting someone else, not\n * their agent's memory of their own world.\n */\nconst THIRD_PARTY_REGEX =\n /\\b(?:tell\\s+\\w+\\s+(?:he|she|they|you)'?re?\\s+wrong|he'?s\\s+wrong\\s+about|she'?s\\s+wrong\\s+about|they'?re\\s+wrong\\s+about|correct\\s+\\w+\\s+on)\\b/i;\n\n/**\n * Tool-output correction — the user is saying a tool's output is wrong, not\n * that their stored memory is wrong.\n */\nconst TOOL_OUTPUT_REGEX =\n /\\b(?:the\\s+(?:output|result|response|error|log)|your\\s+(?:output|result|response|code|answer))\\s+(?:is\\s+)?(?:wrong|incorrect|bad|off)\\b/i;\n\n/**\n * Self-resolving correction — the user corrects themselves within the same\n * turn (\"actually wait, no, the original was right\"). These turn back and\n * cancel out.\n */\nconst SELF_RESOLVE_REGEX =\n /(?:actually|wait|no)[,\\s]+(?:never\\s*mind|forget\\s*(?:that|it)|the\\s+(?:original|first)\\s+(?:was|is)\\s+(?:right|correct)|scratch\\s+that|disregard)/i;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Extract `[m:xxxx]` handle references from text (#1582). */\nexport function extractHandles(text: string): string[] {\n const matches = text.match(/\\[m:[0-9a-fA-F]{4,}\\]/g);\n return matches ?? [];\n}\n\n/**\n * Extract a target hint from the matched correction text. Returns a short\n * phrase (≤80 chars) the planner can use to locate the affected memory.\n */\nfunction extractTargetHint(text: string, match: RegExpMatchArray): string {\n const matchStart = match.index ?? 0;\n // Take a window around the match: 20 chars before to 60 chars after.\n const start = Math.max(0, matchStart - 20);\n const end = Math.min(text.length, matchStart + match[0].length + 60);\n const window = text.slice(start, end).replace(/\\s+/g, \" \").trim();\n return window.length > 80 ? window.slice(0, 77) + \"...\" : window;\n}\n\n/**\n * Extract the corrected assertion — the new truth the user is expressing.\n * For \"update\" polarity, this is the full turn text (the planner searches for\n * the old fact and drafts a replacement). For \"retract\", it's \"\" (no\n * replacement). For \"stop_storing\", it's the behavior to suppress.\n */\nfunction extractCorrectedAssertion(\n text: string,\n polarity: PassiveCorrectionPolarity,\n): string {\n if (polarity === \"retract\") return \"\";\n // For update/stop_storing, the full turn IS the assertion (truncated).\n const trimmed = text.replace(/\\s+/g, \" \").trim();\n return trimmed.length > 200 ? trimmed.slice(0, 197) + \"...\" : trimmed;\n}\n\n/**\n * Check whether a regex match at the given index is within quotation marks.\n * Used to reject corrections that are actually quoted speech (someone else's\n * words the user is relaying, not the user correcting their own memory).\n */\nfunction isWithinQuotes(text: string, matchIndex: number): boolean {\n // Only DOUBLE quotes delimit quoted speech. Single quotes (ASCII apostrophe\n // and smart single quotes) are deliberately NOT treated as delimiters: in\n // English they are overwhelmingly contractions/possessives\n // (\"I don't think we switched\"), which would falsely mark a\n // correction as quoted speech and suppress a valid detection (false\n // negative). A contraction before the correction phrase must not flip the\n // detector into quoted mode.\n let inQuote = false;\n for (let i = 0; i < matchIndex && i < text.length; i++) {\n const c = text[i];\n if (c === '\"' || c === '\\u201c' || c === '\\u201d') {\n inQuote = !inQuote;\n }\n }\n return inQuote;\n}\n\n/**\n * Check whether any anti-fixture guard fires on the turn text.\n * Returns the guard name that blocked detection, or null if clean.\n */\nfunction detectAntiFixture(text: string): string | null {\n if (SELF_RESOLVE_REGEX.test(text)) return \"self_resolving\";\n if (HYPOTHETICAL_REGEX.test(text)) return \"hypothetical\";\n if (THIRD_PARTY_REGEX.test(text)) return \"third_party\";\n if (TOOL_OUTPUT_REGEX.test(text)) return \"tool_output\";\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Main entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Detect passive corrections in a sequence of conversation turns.\n *\n * Only USER turns are scanned — assistant turns don't express corrections to\n * the agent's stored memory. Each user turn is checked against the correction\n * patterns; anti-fixture guards reject turns that look like hypotheticals,\n * third-party corrections, tool-output complaints, or self-resolving double-\n * corrections.\n *\n * A single turn may produce multiple corrections if different patterns match\n * different parts of the text. Dedup by (turnIndex, targetHint) so overlapping\n * patterns on the same phrase (e.g. \"actually, we don't use Redis anymore\"\n * matching both update + retract) produce one correction.\n */\nexport function detectPassiveCorrections(\n turns: readonly DetectorTurn[],\n): PassiveCorrection[] {\n const results: PassiveCorrection[] = [];\n const seen = new Set<string>();\n\n for (let i = 0; i < turns.length; i++) {\n const turn = turns[i];\n if (turn.role !== \"user\") continue;\n const content = turn.content?.trim();\n if (!content) continue;\n\n // Anti-fixture guards take precedence — one guard kills ALL corrections\n // from this turn (a hypothetical turn can't contain a real correction).\n const guard = detectAntiFixture(content);\n if (guard) continue;\n\n const handles = extractHandles(content);\n\n for (const pattern of PATTERNS) {\n const match = pattern.regex.exec(content);\n if (!match) continue;\n\n // Reject corrections that appear within quotation marks — the user is\n // relaying someone else's words, not correcting their own stored memory.\n if (match.index !== undefined && isWithinQuotes(content, match.index)) {\n continue;\n }\n\n const targetHint = extractTargetHint(content, match);\n const correctedAssertion = extractCorrectedAssertion(content, pattern.polarity);\n // Polarity-agnostic: \"actually, we don't use Redis anymore\" matches\n // both the update (\"actually\") and retract (\"don't use ... anymore\")\n // patterns with the same targetHint — emit one correction, not two\n // (review: \"deduplicate overlapping passive-correction cues\").\n const dedupKey = `${i}:${targetHint}`;\n if (seen.has(dedupKey)) continue;\n seen.add(dedupKey);\n\n results.push({\n targetHint,\n correctedAssertion,\n polarity: pattern.polarity,\n handles,\n confidence: pattern.confidence,\n sourceExcerpt: content.slice(0, 200),\n turnIndex: i,\n });\n }\n }\n\n return results;\n}\n","/**\n * correction/passive-capture.ts — routes detected passive corrections to the\n * Correction Contract (#1580) by mode: queue, auto, or off (issue #1581).\n *\n * This module is the thin handoff between the extraction post-processing step\n * (which calls `detectPassiveCorrections`) and the CorrectionService's\n * `plan()` / `apply()` pipeline. It adds NO parallel correction logic (rule 22)\n * — the planner does all the locating/classifying; this module only decides\n * whether to queue the resulting plan for review or auto-apply it.\n *\n * Guards (auto mode):\n * - plan confidence ≥ confidenceFloor\n * - classification ∈ {outdated, wrong}\n * - affected.length ≤ autoApplyMaxAffected\n * - every action ∈ {supersede, edit, retract} (never auto-apply\n * rescope/redaction_rule/never_store — those are policy changes)\n * If ANY guard fails, the plan falls back to queue.\n *\n * Session scoping (rule 42): a correction detected in session S plans only\n * against S's readable namespaces. The CorrectionService already enforces this\n * through its namespace policy; this module passes the session-scoped context.\n *\n * Dedup: fingerprint detected corrections by `bufferKey + normalizedCorrectionText`\n * so parallel sessions / re-flushes don't double-plan.\n */\n\nimport { log } from \"../logger.js\";\nimport type { PassiveCorrection } from \"./passive-correction-detector.js\";\nimport type {\n CorrectionClassification,\n CorrectionOutcome,\n CorrectionPlan,\n CorrectionRequest,\n CorrectionAction,\n} from \"./correction-contract.js\";\nimport {\n enqueuePassiveCorrectionNotification,\n} from \"./passive-correction-notifications.js\";\n\n// ---------------------------------------------------------------------------\n// Config + context\n// ---------------------------------------------------------------------------\n\nexport type CorrectionCaptureMode = \"off\" | \"queue\" | \"auto\";\n\nexport interface PassiveCaptureConfig {\n mode: CorrectionCaptureMode;\n confidenceFloor: number;\n autoApplyMaxAffected: number;\n}\n\nexport interface PassiveCaptureContext {\n /** The correction feature must be enabled for capture to run. */\n correctionEnabled: boolean;\n /** Whether this is a live session (not replay/import). Auto-apply is\n * live-session only — replayed history may contain corrections the user\n * later re-reverted; queue those. */\n isLiveSession: boolean;\n /** Buffer key for dedup fingerprinting. */\n bufferKey: string;\n /** Session key for namespace/principal resolution. */\n sessionKey?: string;\n /** Authenticated principal. */\n principal?: string;\n /** Authorized namespace the extraction wrote to. */\n namespace: string;\n}\n\n// ---------------------------------------------------------------------------\n// Deps (injected — the orchestrator wires these)\n// ---------------------------------------------------------------------------\n\nexport interface PassiveCaptureDeps {\n /** Plan a correction through the Correction Contract. */\n planCorrection(request: CorrectionRequest): Promise<CorrectionPlan>;\n /** Apply a planned correction (auto mode only). */\n applyCorrection(\n planId: string,\n opts: { confirm: true; namespace?: string; sessionKey?: string; principal?: string },\n ): Promise<CorrectionOutcome>;\n /** Resolve the storage dir for notification enqueue (per namespace). */\n storageDir(namespace: string): Promise<string>;\n /**\n * Resolve a memory handle (`[m:xxxx]`) to a concrete memory id, returning\n * null when the handle is unresolvable in the session (graceful — the\n * planner then falls back to text search). Wired to the orchestrator single\n * shared `resolveMemoryIdOrHandle` helper (#1582) so handle resolution has\n * one path (rule 22). Review: \"memory handles not resolved\".\n */\n resolveHandle?(ref: string, sessionKey?: string): string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Telemetry\n// ---------------------------------------------------------------------------\n\nexport interface PassiveCaptureTelemetry {\n detected: number;\n queued: number;\n autoApplied: number;\n /** Reason → count for auto-apply suppressions. */\n suppressedReasons: Record<string, number>;\n}\n\nexport function emptyTelemetry(): PassiveCaptureTelemetry {\n return { detected: 0, queued: 0, autoApplied: 0, suppressedReasons: {} };\n}\n\n// ---------------------------------------------------------------------------\n// Dedup fingerprint\n// ---------------------------------------------------------------------------\n\n/** Normalize correction text for fingerprinting (case + whitespace + punctuation). */\nfunction normalizeForFingerprint(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim()\n .slice(0, 200);\n}\n\nfunction correctionFingerprint(bufferKey: string, correction: PassiveCorrection): string {\n return `${bufferKey}::${correction.polarity}::${normalizeForFingerprint(correction.targetHint)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Auto-apply guards\n// ---------------------------------------------------------------------------\n\n/** Action kinds that are safe to auto-apply (state-preserving, not policy changes). */\nconst AUTO_APPLICABLE_ACTION_KINDS = new Set([\"supersede\", \"edit\", \"retract\"]);\n\n/** Classifications safe for auto-apply. */\nconst AUTO_APPLICABLE_CLASSIFICATIONS = new Set<CorrectionClassification>([\"outdated\", \"wrong\"]);\n\nexport type AutoApplySuppressionReason =\n | \"non_live_session\"\n | \"confidence_below_floor\"\n | \"classification_not_allowed\"\n | \"affected_too_large\"\n | \"disallowed_action_kind\"\n | \"empty_plan\";\n\n/**\n * Evaluate all auto-apply guards. Returns the suppression reason (if any), or\n * null if auto-apply may proceed.\n */\nexport function evaluateAutoApplyGuards(\n plan: CorrectionPlan,\n config: PassiveCaptureConfig,\n isLiveSession: boolean,\n): AutoApplySuppressionReason | null {\n if (!isLiveSession) return \"non_live_session\";\n if (plan.confidence < config.confidenceFloor) return \"confidence_below_floor\";\n if (!AUTO_APPLICABLE_CLASSIFICATIONS.has(plan.classification)) {\n return \"classification_not_allowed\";\n }\n if (plan.affected.length > config.autoApplyMaxAffected) {\n return \"affected_too_large\";\n }\n // An empty action list is a no-op plan (review: \"queue plans that contain no\n // actions\"). Auto-applying it would mark a correction as applied, write an\n // audit record + notification, and record the dedup fingerprint — yet nothing\n // changed. Suppress so it queues for human review instead.\n if (plan.actions.length === 0) {\n return \"empty_plan\";\n }\n for (const action of plan.actions) {\n if (!AUTO_APPLICABLE_ACTION_KINDS.has(action.kind)) {\n return \"disallowed_action_kind\";\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Main entry: capture passive corrections\n// ---------------------------------------------------------------------------\n\nexport interface PassiveCaptureResult {\n telemetry: PassiveCaptureTelemetry;\n /** Plans created (both queued and auto-applied). */\n plans: CorrectionPlan[];\n}\n\n/**\n * Process a batch of detected passive corrections. For each correction:\n * 1. Check dedup fingerprint — skip if already processed for this bufferKey.\n * 2. Build a CorrectionRequest and call the planner.\n * 3. Dispatch by mode: queue → plan is left pending; auto → apply if all\n * guards pass, else fall back to queue.\n * 4. Record telemetry.\n *\n * Fail-open: a planning or apply error for ONE correction never blocks the\n * others. The error is logged and the correction is counted as suppressed.\n */\nexport async function capturePassiveCorrections(\n corrections: readonly PassiveCorrection[],\n ctx: PassiveCaptureContext,\n config: PassiveCaptureConfig,\n deps: PassiveCaptureDeps,\n dedupState: Set<string>,\n): Promise<PassiveCaptureResult> {\n const telemetry = emptyTelemetry();\n const plans: CorrectionPlan[] = [];\n telemetry.detected = corrections.length;\n\n for (const correction of corrections) {\n // 1. Dedup — checked before planning, but the fingerprint is only\n // recorded AFTER a successful plan so a transient planning failure\n // can be retried on a later flush (review: \"dedup blocks retry\n // after failure\").\n const fp = correctionFingerprint(ctx.bufferKey, correction);\n if (dedupState.has(fp)) {\n continue;\n }\n\n // 2. Resolve `[m:xxxx]` handles to concrete memory ids (review:\n // \"memory handles not resolved\"). The planner resolveTargetMemories\n // expects memory ids, not raw handle tokens — passing a handle\n // string would throw \"target memory not found\". Unresolvable handles\n // are dropped so the planner falls back to text search (graceful\n // degradation; a correction without a resolved target is still valid).\n let targetIds: string[] | undefined;\n if (correction.handles.length > 0 && deps.resolveHandle && ctx.sessionKey) {\n const resolved: string[] = [];\n for (const handle of correction.handles) {\n const id = deps.resolveHandle(handle, ctx.sessionKey);\n if (id) resolved.push(id);\n }\n if (resolved.length > 0) targetIds = resolved;\n }\n\n // 3. Build + plan\n const request: CorrectionRequest = {\n text: correction.correctedAssertion || correction.sourceExcerpt,\n ...(targetIds ? { targetIds } : {}),\n ...(ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {}),\n ...(ctx.principal ? { principal: ctx.principal } : {}),\n namespace: ctx.namespace,\n };\n\n let plan: CorrectionPlan;\n try {\n plan = await deps.planCorrection(request);\n } catch (err) {\n log.warn(\n `passive-correction: planning failed for \"${correction.targetHint.slice(0, 60)}\": ${err instanceof Error ? err.message : String(err)}`,\n );\n continue;\n }\n plans.push(plan);\n // Record the fingerprint only after a successful plan.\n dedupState.add(fp);\n\n // 3. Dispatch by mode\n if (config.mode === \"queue\") {\n telemetry.queued += 1;\n continue;\n }\n\n // mode === \"auto\"\n const suppression = evaluateAutoApplyGuards(plan, config, ctx.isLiveSession);\n if (suppression) {\n telemetry.suppressedReasons[suppression] =\n (telemetry.suppressedReasons[suppression] ?? 0) + 1;\n telemetry.queued += 1;\n continue;\n }\n\n // Guards passed — auto-apply\n try {\n const outcome = await deps.applyCorrection(plan.planId, {\n confirm: true,\n namespace: ctx.namespace,\n ...(ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {}),\n ...(ctx.principal ? { principal: ctx.principal } : {}),\n });\n // A partial outcome means some actions failed (per-action races or\n // storage failures). Don't count it as auto-applied or notify — queue\n // for human review instead (review: \"partial outcomes as failures\").\n if (outcome.status !== \"applied\") {\n // Partial outcome: some actions applied, some failed. The plan is\n // consumed (status 'partial', not 'pending'), so it won't appear in\n // listPending — do NOT increment queued (review: \"partial not\n // re-queued\"). The audit record captures which actions failed.\n log.warn(\n `passive-correction: plan ${plan.planId} applied partially — some actions failed (see audit record)`,\n );\n continue;\n }\n telemetry.autoApplied += 1;\n\n // Enqueue notification for the next briefing\n try {\n const dir = await deps.storageDir(ctx.namespace);\n await enqueuePassiveCorrectionNotification(dir, {\n planId: plan.planId,\n summary: buildNotificationSummary(plan),\n undoCommand: `auto-applied (plan ${plan.planId})`,\n createdAt: new Date().toISOString(),\n });\n } catch (err) {\n // Notification is best-effort — never block on it.\n log.debug(`passive-correction: notification enqueue failed (non-fatal): ${err}`);\n }\n } catch (err) {\n log.warn(\n `passive-correction: auto-apply failed for plan ${plan.planId}: ${err instanceof Error ? err.message : String(err)} — plan left in queue`,\n );\n telemetry.queued += 1;\n }\n }\n\n return { telemetry, plans };\n}\n\n// ---------------------------------------------------------------------------\n// Notification summary builder\n// ---------------------------------------------------------------------------\n\nfunction buildNotificationSummary(plan: CorrectionPlan): string {\n const action = plan.actions[0];\n if (!action) {\n return plan.request.text.slice(0, 80);\n }\n const target =\n action.kind === \"supersede\"\n ? action.loserId\n : action.kind === \"edit\" || action.kind === \"retract\" || action.kind === \"rescope\"\n ? action.memoryId\n : \"pattern\";\n return `${plan.classification}: ${plan.request.text.slice(0, 60)}${plan.request.text.length > 60 ? \"...\" : \"\"} (${target})`;\n}\n","/**\n * Generic reinforcement-core primitives extracted from `procedure-miner.ts`\n * (issue #687 PR 1/4). Procedure-specific scoring (success rate, step\n * normalization) intentionally stays in the miner — this module only\n * exposes category-agnostic clustering and cluster summarization helpers\n * so future PRs can run reinforcement across non-procedural categories.\n *\n * Pure refactor — no behavior change.\n */\n\n/**\n * Group `items` into clusters keyed by `keyFn(item)`.\n *\n * - Preserves the original input order within each cluster's array.\n * - The returned `Map` insertion order matches first-seen key order, so\n * downstream iteration is deterministic for a given input.\n * - Throws `TypeError` if `keyFn` returns a non-string (e.g. `undefined`,\n * `null`, or a number). Callers must produce a stable string key.\n */\nexport function clusterByKey<T>(items: readonly T[], keyFn: (item: T) => string): Map<string, T[]> {\n const clusters = new Map<string, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n if (typeof key !== \"string\") {\n throw new TypeError(\n `clusterByKey: keyFn must return a string, got ${key === null ? \"null\" : typeof key}`,\n );\n }\n const existing = clusters.get(key);\n if (existing) {\n existing.push(item);\n } else {\n clusters.set(key, [item]);\n }\n }\n return clusters;\n}\n\nexport interface ClusterSummary {\n /** Number of items in the cluster. */\n count: number;\n /** Earliest timestamp seen in the cluster (string min via `localeCompare`). */\n firstSeen: string;\n /** Latest timestamp seen in the cluster (string max via `localeCompare`). */\n lastSeen: string;\n}\n\n/**\n * Summarize a cluster by counting items and tracking earliest/latest\n * timestamps. Timestamp comparison uses `String#localeCompare`, which is\n * correct for ISO-8601 strings (lexicographic order matches chronological\n * order).\n *\n * - Throws `RangeError` on empty clusters — `firstSeen`/`lastSeen` are not\n * meaningful without at least one item.\n * - When all timestamps are equal, `firstSeen === lastSeen`.\n */\nexport function summarizeCluster<T>(\n cluster: readonly T[],\n extractTimestamp: (item: T) => string,\n): ClusterSummary {\n if (cluster.length === 0) {\n throw new RangeError(\"summarizeCluster: cluster must contain at least one item\");\n }\n let firstSeen = extractTimestamp(cluster[0]);\n let lastSeen = firstSeen;\n for (let i = 1; i < cluster.length; i += 1) {\n const ts = extractTimestamp(cluster[i]);\n if (ts.localeCompare(firstSeen) < 0) firstSeen = ts;\n if (ts.localeCompare(lastSeen) > 0) lastSeen = ts;\n }\n return { count: cluster.length, firstSeen, lastSeen };\n}\n"],"mappings":";;;;;;;;;;;;;;AAkGO,IAAM,oBAA+C;AAAA,EAC1D;AAAA,EAAQ;AAAA,EAAc;AAAA,EAAc;AAAA,EAAU;AAAA,EAC9C;AAAA,EAAgB;AAAA,EAAa;AAAA,EAAc;AAAA,EAC3C;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAa;AAChC;AAcO,IAAM,0BAA+D;AAAA,EAC1E;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAiFO,IAAM,sBAAsB;AAG5B,IAAM,wBAAwB;AAO9B,SAAS,0BAA0B,SAA+C;AACvF,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,wBAAwB,sCAAsC;AAAA,EAC1E;AACA,QAAM,OAAO,OAAO,QAAQ,SAAS,WAAW,QAAQ,KAAK,KAAK,IAAI;AACtE,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,wBAAwB,2DAA2D;AAAA,EAC/F;AACA,MAAI,KAAK,SAAS,qBAAqB;AACrC,UAAM,IAAI;AAAA,MACR,sCAAsC,mBAAmB,qBAAqB,KAAK,MAAM;AAAA,IAC3F;AAAA,EACF;AACA,QAAM,YAAY,MAAM,QAAQ,QAAQ,SAAS,IAC7C,QAAQ,UAAU,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,IACtF;AACJ,MAAI,cAAc,UAAa,UAAU,WAAW,GAAG;AAGrD,WAAO;AAAA,MACL;AAAA,MACA,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;AAAA,MAC/D,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MAC5D,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IACjC,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;AAAA,IAC/D,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IAC5D,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,EAC9D;AACF;AAMO,SAAS,yBAAyB,SAAyB;AAChE,MAAI,OAAO,YAAY,UAAU;AAC/B,UAAM,IAAI,wBAAwB,0CAA0C;AAAA,EAC9E;AACA,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI,wBAAwB,qCAAqC;AAAA,EACzE;AACA,MAAI,QAAQ,SAAS,uBAAuB;AAC1C,UAAM,IAAI;AAAA,MACR,sCAAsC,qBAAqB;AAAA,IAC7D;AAAA,EACF;AAIA,MAAI,YAAY,OAAO,KAAK,mBAAmB,OAAO,GAAG;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,YAAY,SAA0B;AAC7C,MAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,KAAK,QAAQ,UAAU,EAAG,QAAO;AACpF,SAAO,qBAAqB,KAAK,OAAO;AAC1C;AAGA,SAAS,mBAAmB,SAA0B;AACpD,QAAM,OAAO,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,IACxD,QAAQ,MAAM,GAAG,EAAE,IACnB;AAEJ,MAAI,mEAAmE,KAAK,IAAI,GAAG;AACjF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOO,SAAS,yBAAyB,QAAqD;AAC5F,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,wBAAwB,qCAAqC;AAAA,EACzE;AACA,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,SAAS,YAAY,CAAC,wBAAwB,SAAS,EAAE,IAAgC,GAAG;AACvG,UAAM,IAAI;AAAA,MACR,yCAAyC,wBAAwB,KAAK,IAAI,CAAC;AAAA,IAC7E;AAAA,EACF;AACA,UAAQ,EAAE,MAAM;AAAA,IACd,KAAK;AACH,UAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAQ,WAAW,GAAG;AAC3D,cAAM,IAAI,wBAAwB,gCAAgC;AAAA,MACpE;AACA,UAAI,EAAE,gBAAgB,UAAa,EAAE,gBAAgB,MAAM;AACzD,4BAAoB,EAAE,WAAW;AAAA,MACnC;AACA;AAAA,IACF,KAAK;AACH,UAAI,OAAO,EAAE,aAAa,YAAY,EAAE,SAAS,WAAW,GAAG;AAC7D,cAAM,IAAI,wBAAwB,4BAA4B;AAAA,MAChE;AACA,UAAI,OAAO,EAAE,UAAU,YAAY,EAAE,MAAM,WAAW,GAAG;AACvD,cAAM,IAAI,wBAAwB,+CAA+C;AAAA,MACnF;AACA;AAAA,IACF,KAAK;AACH,UAAI,OAAO,EAAE,aAAa,YAAY,EAAE,SAAS,WAAW,GAAG;AAC7D,cAAM,IAAI,wBAAwB,+BAA+B;AAAA,MACnE;AACA;AAAA,IACF,KAAK;AACH,UAAI,OAAO,EAAE,aAAa,YAAY,EAAE,SAAS,WAAW,GAAG;AAC7D,cAAM,IAAI,wBAAwB,+BAA+B;AAAA,MACnE;AACA,UAAI,OAAO,EAAE,gBAAgB,YAAY,EAAE,YAAY,KAAK,EAAE,WAAW,GAAG;AAC1E,cAAM,IAAI,wBAAwB,kCAAkC;AAAA,MACtE;AACA;AAAA,IACF,KAAK;AACH,UAAI,OAAO,EAAE,YAAY,UAAU;AACjC,cAAM,IAAI,wBAAwB,qCAAqC;AAAA,MACzE;AACA,+BAAyB,EAAE,OAAO;AAClC;AAAA,EACJ;AACF;AAGO,SAAS,oBAAoB,OAA8C;AAChF,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,UAAM,IAAI,wBAAwB,gCAAgC;AAAA,EACpE;AACA,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAQ,KAAK,EAAE,WAAW,GAAG;AAClE,UAAM,IAAI,wBAAwB,wDAAwD;AAAA,EAC5F;AACA,MAAI,EAAE,aAAa,QAAW;AAC5B,QAAI,OAAO,EAAE,aAAa,YAAY,CAAE,kBAAwC,SAAS,EAAE,QAAQ,GAAG;AACpG,YAAM,IAAI;AAAA,QACR,wCAAwC,kBAAkB,KAAK,IAAI,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AACA,MAAI,EAAE,eAAe,WAAc,OAAO,EAAE,eAAe,YAAY,EAAE,aAAa,KAAK,EAAE,aAAa,IAAI;AAC5G,UAAM,IAAI,wBAAwB,oDAAoD;AAAA,EACxF;AACA,MAAI,EAAE,SAAS,UAAa,CAAC,MAAM,QAAQ,EAAE,IAAI,GAAG;AAClD,UAAM,IAAI,wBAAwB,oCAAoC;AAAA,EACxE;AACA,MAAI,EAAE,yBAAyB,WAAc,OAAO,EAAE,yBAAyB,YAAY,EAAE,yBAAyB,OAAO;AAC3H,UAAM,IAAI,wBAAwB,qDAAqD;AAAA,EACzF;AACF;AAOO,SAAS,0BAA0B,MAOvB;AACjB,SAAO;AAAA,IACL,QAAQ,UAAU;AAAA,IAClB,SAAS,KAAK;AAAA,IACd,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,gBAAgB;AAAA,IAChB,SAAS,CAAC;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU,CAAC,GAAG,KAAK,UAAU,iEAA4D;AAAA,IACzF,WAAW,KAAK;AAAA,IAChB,WAAW,KAAK;AAAA,IAChB,QAAQ;AAAA,EACV;AACF;AAGO,SAAS,YAAoB;AAClC,SAAO,QAAQ,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACnF;AAGO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACjD,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACzYA,OAAOA,WAAU;AACjB,SAAS,SAAAC,QAAO,aAAAC,kBAAiB;;;ACDjC,SAAS,OAAO,UAAU,QAAQ,QAAQ,SAAS,iBAAiB;AACpE,OAAO,UAAU;AAgHV,IAAM,oBAAN,MAAwB;AAAA,EAC7B,YAA6B,MAAmB;AAAnB;AAAA,EAAoB;AAAA,EAApB;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,MAAM,KACJ,SACA,oBACyB;AACzB,UAAM,UAAU,0BAA0B,OAAO;AACjD,QAAI,QAAQ,KAAK,SAAS,qBAAqB;AAE7C,YAAM,IAAI,wBAAwB,kCAAkC;AAAA,IACtE;AAEA,QAAI,QAAQ,aAAa,QAAQ,UAAU,SAAS,KAAK,KAAK,aAAa;AACzE,YAAM,IAAI;AAAA,QACR,2BAA2B,QAAQ,UAAU,MAAM,0BAA0B,KAAK,KAAK,WAAW;AAAA,MACpG;AAAA,IACF;AAEA,UAAM,aAAa;AACnB,QAAI,WAAW,WAAW,GAAG;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,UAAM,UAAU,MAAM,KAAK,iBAAiB,SAAS,UAAU;AAC/D,QAAI,QAAQ,WAAW,WAAW,GAAG;AAInC,aAAO,KAAK;AAAA,QACV,KAAK,UAAU,SAAS,WAAW,CAAC,GAAG,oDAAoD;AAAA,MAC7F;AAAA,IACF;AAGA,UAAM,MAAM,MAAM,KAAK,KAAK,iBAAiB;AAAA,MAC3C,MAAM,QAAQ;AAAA,MACd,YAAY,QAAQ;AAAA,IACtB,CAAC;AAKD,eAAW,UAAU,IAAI,SAAS;AAChC,+BAAyB,MAAM;AAC/B,UAAI,OAAO,SAAS,kBAAkB;AACpC,iCAAyB,OAAO,OAAO;AAAA,MACzC;AAAA,IACF;AAGA,UAAM,WAAW,KAAK,eAAe,QAAQ,YAAY,GAAG;AAG5D,UAAM,eAAe,IAAI;AAAA,MACvB,IAAI,QACD;AAAA,QAAI,CAAC,MACJ,EAAE,SAAS,cACP,EAAE,UACF,EAAE,SAAS,UAAU,EAAE,SAAS,aAAa,EAAE,SAAS,YACtD,EAAE,WACF;AAAA,MACR,EACC,OAAO,CAAC,OAAqB,OAAO,IAAI;AAAA,IAC7C,EAAE;AACF,QAAI,eAAe,KAAK,KAAK,aAAa;AACxC,YAAM,IAAI;AAAA,QACR,sBAAsB,YAAY,iDAAiD,KAAK,KAAK,WAAW;AAAA,MAC1G;AAAA,IACF;AAOA,UAAM,eAAe,IAAI,IAAI,QAAQ,WAAW,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC;AACtE,QAAI,IAAI,QAAQ,SAAS,KAAK,aAAa,OAAO,GAAG;AACnD,YAAM,WAAW,IAAI,QAAQ,OAAO,CAAC,WAAW;AAC9C,cAAM,KACJ,OAAO,SAAS,cACZ,OAAO,UACP,OAAO,SAAS,UAAU,OAAO,SAAS,aAAa,OAAO,SAAS,YACrE,OAAO,WACP;AACR,eAAO,OAAO,QAAQ,aAAa,IAAI,EAAE;AAAA,MAC3C,CAAC;AACD,UAAI,SAAS,SAAS,IAAI,QAAQ,QAAQ;AACxC,cAAM,UAAU,IAAI,QAAQ,SAAS,SAAS;AAC9C,YAAI,WAAW,CAAC,GAAG,IAAI,UAAU,GAAG,OAAO,2GAA2G;AACtJ,YAAI,UAAU;AAAA,MAChB;AAAA,IACF;AAGA,UAAM,OAAO,IAAI,WACb,KACA,MAAM,KAAK,KAAK,WAAW,EAAE,YAAY,QAAQ,YAAY,SAAS,IAAI,QAAQ,CAAC;AAEvF,UAAM,YAAY,KAAK,KAAK,IAAI,EAAE,YAAY;AAC9C,UAAM,YAAY,IAAI;AAAA,MACpB,KAAK,KAAK,IAAI,EAAE,QAAQ,IAAI,KAAK,KAAK,eAAe,KAAK,KAAK;AAAA,IACjE,EAAE,YAAY;AAEd,UAAM,OAAuB,IAAI,WAC7B,0BAA0B;AAAA,MACxB,SAAS;AAAA,MACT,WAAW,WAAW,CAAC;AAAA,MACvB;AAAA,MACA,UAAU,IAAI;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC,IACD;AAAA,MACE,QAAQ,UAAU;AAAA,MAClB,SAAS;AAAA,MACT,WAAW,WAAW,CAAC;AAAA,MACvB;AAAA,MACA,gBAAgB,IAAI;AAAA,MACpB,SAAS,IAAI;AAAA,MACb;AAAA,MACA,YAAY,gBAAgB,IAAI,UAAU;AAAA,MAC1C,UAAU,IAAI;AAAA,MACd;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV;AAEJ,WAAO,KAAK,QAAQ,IAAI;AAAA,EAC1B;AAAA,EAEA,MAAc,iBACZ,SACA,YAC6C;AAC7C,QAAI,QAAQ,aAAa,QAAQ,UAAU,SAAS,GAAG;AACrD,YAAM,UAAU,MAAM,KAAK,KAAK,eAAe;AAAA,QAC7C,WAAW,QAAQ;AAAA,QACnB;AAAA,MACF,CAAC;AAED,YAAMC,WAAU,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ;AAC7C,YAAMC,aAAY,MAAM,KAAK,KAAK,gBAAgB;AAAA,QAChD,SAAAD;AAAA,QACA;AAAA,QACA,OAAO,KAAK,KAAK;AAAA,MACnB,CAAC;AACD,aAAO,EAAE,YAAY,gBAAgB,SAASC,UAAS,EAAE,MAAM,GAAG,KAAK,KAAK,WAAW,EAAE;AAAA,IAC3F;AAEA,UAAM,WAAW,MAAM,KAAK,KAAK,aAAa;AAAA,MAC5C,MAAM,QAAQ;AAAA,MACd;AAAA,MACA,OAAO,KAAK,KAAK;AAAA,IACnB,CAAC;AACD,QAAI,SAAS,WAAW,EAAG,QAAO,EAAE,YAAY,CAAC,EAAE;AACnD,UAAM,UAAU,SAAS,MAAM,GAAG,KAAK,IAAI,GAAG,SAAS,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ;AACrF,UAAM,YAAY,MAAM,KAAK,KAAK,gBAAgB;AAAA,MAChD;AAAA,MACA;AAAA,MACA,OAAO,KAAK,KAAK;AAAA,IACnB,CAAC;AACD,WAAO,EAAE,YAAY,gBAAgB,UAAU,SAAS,EAAE,MAAM,GAAG,KAAK,KAAK,WAAW,EAAE;AAAA,EAC5F;AAAA,EAEQ,eACN,YACA,KAC2B;AAC3B,UAAM,OAAO,IAAI,IAAI,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AAC3D,UAAM,MAAiC,CAAC;AACxC,eAAW,OAAO,IAAI,WAAW;AAC/B,YAAM,IAAI,KAAK,IAAI,IAAI,QAAQ;AAC/B,UAAI,CAAC,EAAG;AACR,UAAI,KAAK;AAAA,QACP,UAAU,EAAE;AAAA,QACZ,MAAM,EAAE;AAAA,QACR,SAAS,EAAE;AAAA,QACX,KAAK,IAAI;AAAA,QACT,GAAI,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,IAAI,CAAC;AAAA,MACxD,CAAC;AAAA,IACH;AAGA,UAAM,YAAY,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC;AACpD,eAAW,UAAU,IAAI,SAAS;AAChC,YAAM,KACJ,OAAO,SAAS,cACZ,OAAO,UACP,OAAO,SAAS,UAAU,OAAO,SAAS,aAAa,OAAO,SAAS,YACrE,OAAO,WACP;AACR,UAAI,MAAM,CAAC,UAAU,IAAI,EAAE,GAAG;AAC5B,cAAM,IAAI,KAAK,IAAI,EAAE;AACrB,YAAI,GAAG;AACL,cAAI,KAAK;AAAA,YACP,UAAU,EAAE;AAAA,YACZ,MAAM,EAAE;AAAA,YACR,SAAS,EAAE;AAAA,YACX,KAAK;AAAA,YACL,GAAI,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,IAAI,CAAC;AAAA,UACxD,CAAC;AACD,oBAAU,IAAI,EAAE;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,SAA4B,WAAmB,SAAiC;AAChG,UAAM,YAAY,KAAK,KAAK,IAAI,EAAE,YAAY;AAC9C,UAAM,YAAY,IAAI;AAAA,MACpB,KAAK,KAAK,IAAI,EAAE,QAAQ,IAAI,KAAK,KAAK,eAAe,KAAK,KAAK;AAAA,IACjE,EAAE,YAAY;AACd,WAAO;AAAA,MACL,QAAQ,UAAU;AAAA,MAClB;AAAA,MACA;AAAA,MACA,UAAU,CAAC;AAAA,MACX,gBAAgB;AAAA,MAChB,SAAS,CAAC;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,UAAU,CAAC,OAAO;AAAA,MAClB;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,WAAW,WAAoC;AAC3D,WAAO,KAAK,KAAK,MAAM,KAAK,KAAK,WAAW,SAAS,GAAG,SAAS,eAAe,SAAS;AAAA,EAC3F;AAAA,EAEA,MAAc,QAAQ,MAA+C;AACnE,UAAM,MAAM,MAAM,KAAK,WAAW,KAAK,SAAS;AAChD,UAAM,SAAS,KAAK,KAAK,KAAK,GAAG,KAAK,MAAM,OAAO;AACnD,UAAM,mBAAmB,mBAAmB,MAAM,IAAI,YAAY;AAChE,YAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,YAAM,MAAM,GAAG,MAAM,IAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC;AAC/D,YAAM,UAAU,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC;AAAA,GAAM,OAAO;AAEzD,YAAM,OAAO,KAAK,MAAM;AAAA,IAC1B,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,SAAS,WAAmB,QAAgD;AAChF,qBAAiB,MAAM;AACvB,UAAM,OAAO,KAAK,KAAK,MAAM,KAAK,WAAW,SAAS,GAAG,GAAG,MAAM,OAAO;AACzE,WAAO,mBAAmB,mBAAmB,IAAI,IAAI,YAAY;AAC/D,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,SAAS,MAAM,OAAO;AAAA,MACpC,SAAS,KAAK;AACZ,cAAM,OAAQ,KAA+B;AAC7C,YAAI,SAAS,SAAU,QAAO;AAC9B,cAAM;AAAA,MACR;AACA,aAAO,UAAU,GAAG;AAAA,IACtB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,YAAY,WAA8C;AAC9D,UAAM,MAAM,MAAM,KAAK,WAAW,SAAS;AAC3C,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,QAAQ,GAAG;AAAA,IAC3B,SAAS,KAAK;AACZ,YAAM,OAAQ,KAA+B;AAC7C,UAAI,SAAS,SAAU,QAAO,CAAC;AAC/B,YAAM;AAAA,IACR;AACA,UAAM,QAA0B,CAAC;AACjC,eAAW,KAAK,OAAO;AACrB,UAAI,CAAC,EAAE,SAAS,OAAO,EAAG;AAC1B,UAAI,OAA8B;AAClC,UAAI;AACF,eAAO,MAAM,KAAK,SAAS,WAAW,EAAE,QAAQ,WAAW,EAAE,CAAC;AAAA,MAChE,QAAQ;AACN;AAAA,MACF;AACA,UAAI,QAAQ,KAAK,WAAW,UAAW,OAAM,KAAK,IAAI;AAAA,IACxD;AACA,UAAM,KAAK,CAAC,GAAG,MAAO,EAAE,YAAY,EAAE,YAAY,IAAI,EAAE,YAAY,EAAE,YAAY,KAAK,CAAE;AACzF,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,WAAmB,QAAgB,QAAyE;AAC7H,qBAAiB,MAAM;AACvB,UAAM,OAAO,KAAK,KAAK,MAAM,KAAK,WAAW,SAAS,GAAG,GAAG,MAAM,OAAO;AACzE,UAAM,mBAAmB,mBAAmB,IAAI,IAAI,YAAY;AAC9D,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,SAAS,MAAM,OAAO;AAAA,MACpC,SAAS,KAAK;AACZ,cAAM,OAAQ,KAA+B;AAC7C,YAAI,SAAS,SAAU;AACvB,cAAM;AAAA,MACR;AACA,YAAM,OAAO,UAAU,GAAG;AAC1B,UAAI,CAAC,KAAM;AACX,WAAK,SAAS;AACd,YAAM,MAAM,GAAG,IAAI,IAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7D,YAAM,UAAU,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC;AAAA,GAAM,OAAO;AACzD,YAAM,OAAO,KAAK,IAAI;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,WAAW,WAAmB,QAA+B;AACjE,qBAAiB,MAAM;AACvB,UAAM,OAAO,KAAK,KAAK,MAAM,KAAK,WAAW,SAAS,GAAG,GAAG,MAAM,OAAO;AACzE,UAAM,mBAAmB,mBAAmB,IAAI,IAAI,YAAY;AAC9D,UAAI;AACF,cAAM,OAAO,IAAI;AAAA,MACnB,SAAS,KAAK;AACZ,cAAM,OAAQ,KAA+B;AAC7C,YAAI,SAAS,SAAU;AACvB,cAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAgBA,SAAS,iBAAiB,QAAsB;AAC9C,MACE,OAAO,WAAW,YAClB,OAAO,WAAW,KAClB,OAAO,SAAS,GAAG,KACnB,OAAO,SAAS,IAAI,KACpB,OAAO,SAAS,IAAI,KACpB,WAAW,OACX,WAAW,QACX,OAAO,WAAW,GAAG,KACrB,OAAO,SAAS,IAAI,GACpB;AACA,UAAM,IAAI;AAAA,MACR,oBAAoB,KAAK,UAAU,MAAM,CAAC;AAAA,IAC5C;AAAA,EACF;AACF;AACA,SAAS,gBAAgB,GAAmB;AAC1C,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AACnC;AAGA,SAAS,gBAAgB,GAAgC,GAAoD;AAC3G,QAAM,MAAM,oBAAI,IAA8B;AAC9C,aAAW,KAAK,GAAG;AACjB,UAAM,WAAW,IAAI,IAAI,EAAE,QAAQ;AACnC,QAAI,CAAC,YAAY,EAAE,QAAQ,SAAS,MAAO,KAAI,IAAI,EAAE,UAAU,CAAC;AAAA,EAClE;AACA,aAAW,KAAK,GAAG;AACjB,UAAM,WAAW,IAAI,IAAI,EAAE,QAAQ;AACnC,QAAI,CAAC,YAAY,EAAE,QAAQ,SAAS,MAAO,KAAI,IAAI,EAAE,UAAU,CAAC;AAAA,EAClE;AACA,SAAO,CAAC,GAAG,IAAI,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC3D;AAGO,SAAS,UAAU,KAAoC;AAC5D,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,WAAW,YAAY,OAAO,EAAE,cAAc,SAAU,QAAO;AAC5E,MAAI,OAAO,EAAE,cAAc,YAAY,OAAO,EAAE,cAAc,SAAU,QAAO;AAC/E,MAAI,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK,CAAC,MAAM,QAAQ,EAAE,QAAQ,EAAG,QAAO;AAEpE,aAAW,UAAU,EAAE,SAAsB;AAC3C,QAAI;AACF,+BAAyB,MAAM;AAAA,IACjC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ACrYO,IAAM,qBAAN,MAAyB;AAAA,EAC9B,YACmB,MACA,SACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnB,MAAM,MACJ,WACA,QACA,MAW4B;AAC5B,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,WAAO;AAAA,MAAmB,oBAAoB,SAAS,IAAI,MAAM;AAAA,MAAI,MACnE,KAAK,cAAc,WAAW,QAAQ,KAAK,mBAAmB;AAAA,IAChE;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,WAAmB,QAAgB,qBAA2F;AACxJ,UAAM,OAAO,MAAM,KAAK,QAAQ,SAAS,WAAW,MAAM;AAC1D,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,wBAAwB,8BAA8B,MAAM,EAAE;AAAA,IAC1E;AACA,QAAI,KAAK,cAAc,WAAW;AAEhC,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,0BAA0B,KAAK,SAAS,WAAW,SAAS;AAAA,MACvF;AAAA,IACF;AACA,QAAI,KAAK,WAAW,aAAa,KAAK,WAAW,aAAa,KAAK,WAAW,YAAY;AACxF,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,uDAAuD,KAAK,MAAM;AAAA,MAC7F;AAAA,IACF;AACA,QAAI,KAAK,WAAW,aAAa;AAC/B,YAAM,IAAI,wBAAwB,mBAAmB,MAAM,sBAAsB;AAAA,IACnF;AAEA,QAAI,KAAK,KAAK,IAAI,EAAE,QAAQ,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,QAAQ,GAAG;AAClE,YAAM,KAAK,QAAQ,aAAa,WAAW,QAAQ,WAAW;AAC9D,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,eAAe,KAAK,SAAS;AAAA,MACxD;AAAA,IACF;AAGA,eAAW,UAAU,KAAK,SAAS;AACjC,+BAAyB,MAAM;AAC/B,UAAI,OAAO,SAAS,kBAAkB;AACpC,iCAAyB,OAAO,OAAO;AAAA,MACzC;AAAA,IACF;AAUA,QAAI;AACF,YAAM,KAAK,QAAQ,aAAa,WAAW,QAAQ,UAAU;AAAA,IAC/D,QAAQ;AAGN,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM;AAAA,MAC3B;AAAA,IACF;AAEA,UAAM,UAAoC,CAAC;AAC3C,UAAM,iBAA2B,CAAC;AAMlC,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,OAAO,SAAS,eAAe,OAAO,aAAa;AAMrD,cAAM,QAAQ,MAAM,KAAK,KAAK,UAAU,WAAW,OAAO,OAAO;AACjE,YAAI,CAAC,OAAO;AACV,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,QAAQ;AAAA,YACR,OAAO,8BAA8B,OAAO,OAAO;AAAA,UACrD,CAAC;AACD;AAAA,QACF;AACA,YAAI;AACF,gBAAM,QAAQ,MAAM,KAAK,KAAK,iBAAiB,WAAW;AAAA,YACxD,SAAS,OAAO,YAAY;AAAA,YAC5B,GAAI,OAAO,YAAY,WAAW,EAAE,UAAU,OAAO,YAAY,SAAS,IAAI,CAAC;AAAA,YAC/E,GAAI,OAAO,YAAY,eAAe,SAAY,EAAE,YAAY,OAAO,YAAY,WAAW,IAAI,CAAC;AAAA,YACnG,GAAI,OAAO,YAAY,OAAO,EAAE,MAAM,OAAO,YAAY,KAAK,IAAI,CAAC;AAAA,YACnE,GAAI,OAAO,YAAY,YAAY,EAAE,WAAW,OAAO,YAAY,UAAU,IAAI,CAAC;AAAA,YAClF,GAAI,OAAO,YAAY,UAAU,EAAE,SAAS,OAAO,YAAY,QAAQ,IAAI,CAAC;AAAA,YAC5E,GAAI,OAAO,YAAY,aAAa,EAAE,YAAY,OAAO,YAAY,WAAW,IAAI,CAAC;AAAA,YACrF,GAAI,OAAO,YAAY,uBACnB,EAAE,sBAAsB,OAAO,YAAY,qBAAqB,IAChE,CAAC;AAAA,YACL,YAAY,OAAO;AAAA,UACrB,CAAC;AACD,kBAAQ,KAAK,EAAE,QAAQ,QAAQ,WAAW,UAAU,MAAM,CAAC;AAC3D,yBAAe,KAAK,KAAK;AAAA,QAC3B,SAAS,KAAK;AACZ,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,QAAQ;AAAA,YACR,OAAO,OAAO,GAAG;AAAA,UACnB,CAAC;AAAA,QACH;AAAA,MACF,WAAW,OAAO,SAAS,QAAQ;AACjC,YAAI;AACF,gBAAM,WAAW,MAAM,KAAK,KAAK,UAAU,WAAW,OAAO,UAAU,OAAO,KAAK;AACnF,kBAAQ,KAAK,EAAE,QAAQ,QAAQ,WAAW,UAAU,SAAS,CAAC;AAC9D,yBAAe,KAAK,QAAQ;AAAA,QAC9B,SAAS,KAAK;AACZ,kBAAQ,KAAK,EAAE,QAAQ,QAAQ,UAAU,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,QAC/D;AAAA,MACF,WAAW,OAAO,SAAS,kBAAkB;AAC3C,YAAI;AACF,gBAAM,KAAK,KAAK,sBAAsB,WAAW,OAAO,OAAO;AAC/D,kBAAQ,KAAK,EAAE,QAAQ,QAAQ,UAAU,CAAC;AAAA,QAC5C,SAAS,KAAK;AACZ,kBAAQ,KAAK,EAAE,QAAQ,QAAQ,UAAU,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAKA,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,OAAO,SAAS,aAAa;AAC/B,cAAM,oBAAoB,QAAQ;AAAA,UAChC,CAAC,MAAM,EAAE,WAAW,UAAU,EAAE,WAAW;AAAA,QAC7C;AAEA,YAAI,OAAO,eAAe,CAAC,mBAAmB;AAC5C;AAAA,QACF;AACA,cAAM,KAAK,mBAAmB,WAAW,QAAQ,gBAAgB,SAAS,gBAAgB;AAAA,UACxF,cAAc,mBAAmB;AAAA,QACnC,CAAC;AAAA,MACH,WAAW,OAAO,SAAS,WAAW;AACpC,cAAM,KAAK,mBAAmB,WAAW,QAAQ,cAAc,SAAS,cAAc;AAAA,MACxF,WAAW,OAAO,SAAS,WAAW;AACpC,YAAI;AAMF,gBAAM,UAAU,sBAAsB,MAAM,oBAAoB,OAAO,WAAW,IAAI;AACtF,cAAI,CAAC,SAAS;AACZ,oBAAQ,KAAK;AAAA,cACX;AAAA,cACA,QAAQ;AAAA,cACR,OAAO,+CAA+C,OAAO,WAAW;AAAA,YAC1E,CAAC;AACD;AAAA,UACF;AACA,gBAAM,SAAS,MAAM,KAAK,KAAK,cAAc,WAAW,OAAO,UAAU,OAAO,WAAW;AAC3F,kBAAQ,KAAK,EAAE,QAAQ,QAAQ,WAAW,UAAU,OAAO,SAAS,CAAC;AACrE,yBAAe,KAAK,OAAO,QAAQ;AAGnC,cAAI;AACF,kBAAM,KAAK,KAAK,UAAU,OAAO,aAAa,CAAC,MAAM,CAAC;AAAA,UACxD,QAAQ;AAAA,UAER;AAAA,QACF,SAAS,KAAK;AACZ,kBAAQ,KAAK,EAAE,QAAQ,QAAQ,UAAU,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAKA,UAAM,sBAAgC,CAAC;AACvC,QAAI,eAAe,SAAS,GAAG;AAC7B,UAAI;AACF,cAAM,KAAK,KAAK,UAAU,WAAW,cAAc;AAAA,MACrD,SAAS,KAAK;AACZ,4BAAoB,KAAK,mCAAmC,OAAO,GAAG,CAAC,EAAE;AAAA,MAC3E;AAAA,IACF;AAGA,UAAM,YAAY,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ;AAC3D,UAAM,SAAsC,YAAY,YAAY;AACpE,UAAM,YAAY,KAAK,KAAK,IAAI,EAAE,YAAY;AAC9C,UAAM,UAA6B;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA;AAAA,MACf;AAAA,IACF;AACA,QAAI,oBAAoB,SAAS,GAAG;AAClC,MAAC,QAAwD,WAAW;AAAA,IACtE;AACA,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,KAAK,kBAAkB,WAAW;AAAA,QAC3D;AAAA,QACA,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA,aAAa,KAAK,QAAQ;AAAA,MAC5B,CAAC;AACD,cAAQ,gBAAgB;AAAA,IAC1B,SAAS,KAAK;AAGZ,MAAC,QAAwD,WAAW;AAAA,QAClE,GAAG;AAAA,QACH,0CAA0C,OAAO,GAAG,CAAC;AAAA,MACvD;AAAA,IACF;AAOA,QAAI;AACF,YAAM,KAAK,QAAQ,aAAa,WAAW,QAAQ,MAAM;AAAA,IAC3D,SAAS,KAAK;AACZ,YAAM,IAAI;AACV,QAAE,WAAW,CAAC,GAAI,EAAE,YAAY,CAAC,GAAI,0CAA0C,OAAO,GAAG,CAAC,EAAE;AAAA,IAC9F;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,mBACZ,WACA,QACA,QACA,SACA,gBACA,OAAkC,CAAC,GACpB;AACf,UAAM,WACJ,OAAO,SAAS,cAAc,OAAO,UAAU,OAAO,SAAS,YAAY,OAAO,WAAW;AAC/F,QAAI,CAAC,SAAU;AACf,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,KAAK,UAAU,WAAW,QAAQ;AAC5D,UAAI,CAAC,QAAQ;AACX,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA,QAAQ;AAAA,UACR,OAAO,qBAAqB,QAAQ;AAAA,QACtC,CAAC;AACD;AAAA,MACF;AACA,YAAM,aAAa,KAAK,KAAK,oBAAoB,KAAK,KAAK,IAAI,EAAE,YAAY,IAAI;AAOjF,YAAM,cAAc,MAAM,KAAK,KAAK,gBAAgB,WAAW;AAAA,QAC7D;AAAA,QACA,gBAAgB;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,QAC1D,GAAI,OAAO,kBAAkB,EAAE,iBAAiB,OAAO,gBAAgB,IAAI,CAAC;AAAA,MAC9E,CAAC;AACD,YAAM,KAAK,KAAK,aAAa,WAAW,UAAU;AAAA,QAChD,QAAQ,WAAW,iBAAiB,eAAe;AAAA,QACnD,GAAI,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC;AAAA,QAC/D,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MACrC,CAAC;AACD,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,QACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,MACvC,CAAC;AACD,qBAAe,KAAK,QAAQ;AAAA,IAC9B,SAAS,KAAK;AACZ,cAAQ,KAAK,EAAE,QAAQ,QAAQ,UAAU,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;AAEA,SAAS,OAAO,KAAsB;AACpC,MAAI,eAAe,MAAO,QAAO,IAAI;AACrC,SAAO,OAAO,GAAG;AACnB;;;AClYO,IAAM,oBAAN,MAAwB;AAAA,EAI7B,YAA6B,MAA6B;AAA7B;AAC3B,SAAK,UAAU,IAAI,kBAAkB,KAAK,YAAY,CAAC;AACvD,SAAK,WAAW,IAAI,mBAAmB,KAAK,aAAa,GAAG,KAAK,OAAO;AAAA,EAC1E;AAAA,EAH6B;AAAA,EAHZ;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBjB,MAAM,KAAK,SAAqD;AAC9D,SAAK,eAAe;AACpB,UAAM,aAAa,MAAM,KAAK,KAAK,OAAO,2BAA2B,OAAO;AAC5E,WAAO,KAAK,QAAQ,KAAK,SAAS,CAAC,UAAU,CAAC;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MACJ,QACA,MAM4B;AAC5B,SAAK,eAAe;AACpB,QAAI,KAAK,KAAK,qBAAqB,KAAK,KAAK,YAAY,MAAM;AAC7D,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,YAAY,MAAM,KAAK,KAAK,OAAO,2BAA2B;AAAA,MAClE,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,MACtD,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,MACzD,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,IACxD,CAAC;AACD,WAAO,KAAK,SAAS,MAAM,WAAW,QAAQ;AAAA,MAC5C,SAAS;AAAA;AAAA;AAAA,MAGT,qBAAqB,CAAC,SACpB,KAAK,KAAK,OAAO,kBAAkB;AAAA,QACjC,WAAW;AAAA,QACX,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,QACzD,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,MACxD,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,YAAY,MAIY;AAC5B,SAAK,eAAe;AACpB,UAAM,YAAY,MAAM,KAAK,KAAK,OAAO,2BAA2B,IAAI;AACxE,WAAO,KAAK,QAAQ,YAAY,SAAS;AAAA,EAC3C;AAAA;AAAA,EAGA,MAAM,QACJ,QACA,MACe;AACf,SAAK,eAAe;AACpB,UAAM,YAAY,MAAM,KAAK,KAAK,OAAO,2BAA2B,IAAI;AACxE,UAAM,KAAK,QAAQ,aAAa,WAAW,QAAQ,WAAW;AAC9D,UAAM,KAAK,QAAQ,WAAW,WAAW,MAAM;AAAA,EACjD;AAAA,EAEQ,iBAAuB;AAC7B,QAAI,CAAC,KAAK,KAAK,UAAU,GAAG;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AH/FO,SAAS,wBAAwB,QAAmD;AACzF,QAAM,MAAM,OAAO,aAAa;AAMhC,QAAM,oBAAoB,2BAA2B,GAAG;AACxD,QAAM,uBACJ,OAAO,IAAI,mCAAmC,YAC1C,IAAI,iCACJ,mBAAmB,KAAK,wBAAwB,IAAI;AAC1D,QAAM,cACJ,OAAO,IAAI,0BAA0B,YAAY,IAAI,yBAAyB,IAC1E,KAAK,MAAM,IAAI,qBAAqB,IACpC,qBAAqB,KAAK,eAAe,EAAE;AACjD,QAAM,eACJ,OAAO,IAAI,2BAA2B,YAAY,IAAI,yBAAyB,IAC3E,IAAI,yBACJ,qBAAqB,KAAK,gBAAgB,EAAE;AAClD,QAAM,oBAAoB,IAAI,uBAAuB;AAErD,QAAM,cAAqC;AAAA,IACzC,QAAQ;AAAA,MACN,4BAA4B,CAAC,QAAQ,OAAO,2BAA2B,GAAG;AAAA,MAC1E,mBAAmB,CAAC,QAAQ,OAAO,kBAAkB,GAAG;AAAA,MACxD,oBAAoB,CAAC,QACnB,QAAQ,QAAQ,OAAO,0BAA0B,GAAG,CAAC;AAAA,IACzD;AAAA,IACA,aAAa,MACX,gBAAgB,QAAQ,EAAE,aAAa,aAAa,CAAC;AAAA,IACvD,cAAc,MAAM,iBAAiB,QAAQ,EAAE,kBAAkB,CAAC;AAAA,IAClE,WAAW,MAAM;AAAA,IACjB,sBAAsB,MAAM;AAAA,EAC9B;AACA,SAAO,IAAI,kBAAkB,WAAW;AAC1C;AAMA,SAAS,gBACP,QACA,MACa;AACb,SAAO;AAAA,IACL,cAAc,OAAO,EAAE,MAAM,YAAY,MAAM,MAC7C,eAAe,QAAQ,MAAM,YAAY,KAAK;AAAA,IAChD,gBAAgB,OAAO,EAAE,WAAW,WAAW,MAC7C,sBAAsB,QAAQ,WAAW,UAAU;AAAA,IACrD,iBAAiB,OAAO,EAAE,SAAS,YAAY,MAAM,MACnD,sBAAsB,QAAQ,SAAS,YAAY,KAAK;AAAA,IAC1D,kBAAkB,OAAO,EAAE,MAAM,WAAW,MAC1C,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C,YAAY,OAAO,EAAE,YAAY,QAAQ,MACvC,qBAAqB,QAAQ,YAAY,OAAO;AAAA,IAClD,YAAY,OAAO,eAAe,MAAM,OAAO,aAAa,WAAW,SAAS,GAAG;AAAA,IACnF,aAAa,KAAK;AAAA,IAClB,cAAc,KAAK;AAAA,IACnB,KAAK,MAAM,oBAAI,KAAK;AAAA,EACtB;AACF;AAEA,eAAe,eACb,QACA,MACA,YACA,OAC6B;AAU7B,QAAM,SAAS,SAAS,IAAI;AAC5B,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,QAAM,SAA8D,CAAC;AACrE,aAAW,MAAM,YAAY;AAC3B,UAAM,UAAU,MAAM,OAAO,aAAa,WAAW,EAAE;AACvD,UAAM,MAAM,MAAM,QAAQ,gBAAgB;AAC1C,eAAW,KAAK,KAAK;AACnB,UAAI,EAAE,YAAY,UAAU,EAAE,YAAY,WAAW,SAAU;AAC/D,YAAM,MAAM,GAAG,EAAE,OAAO,IAAI,EAAE,YAAY,MAAM,KAAK,GAAG,KAAK,EAAE,GAAG,YAAY;AAC9E,UAAI,OAAO;AACX,iBAAW,OAAO,QAAQ;AACxB,YAAI,IAAI,SAAS,GAAG,EAAG;AAAA,MACzB;AACA,UAAI,SAAS,EAAG;AAEhB,aAAO,KAAK,EAAE,GAAG,IAAI,OAAO,OAAO,OAAO,OAAO,CAAC;AAAA,IACpD;AAAA,EACF;AACA,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACvC,SAAO,OAAO,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,MAAM,YAAY,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;AACxF;AAGA,SAAS,SAAS,MAAwB;AACxC,QAAM,OAAO,oBAAI,IAAI;AAAA,IACnB;AAAA,IAAO;AAAA,IAAK;AAAA,IAAM;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAM;AAAA,IAAQ;AAAA,IAC5D;AAAA,IAAM;AAAA,IAAM;AAAA,IAAM;AAAA,IAAM;AAAA,IAAM;AAAA,IAAM;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAC3D;AAAA,IAAO;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAM;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAQ;AAAA,IACxD;AAAA,IAAS;AAAA,IAAM;AAAA,IAAO;AAAA,IAAM;AAAA,IAAO;AAAA,IAAK;AAAA,IAAM;AAAA,IAAO;AAAA,IAAQ;AAAA,IAC7D;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAS;AAAA,IAAM;AAAA,IAAM;AAAA,IAAM;AAAA,IAC9D;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAU;AAAA,IAAO;AAAA,IAC/D;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAM;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAS;AAAA,IAAS;AAAA,IAAQ;AAAA,IAC9D;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAO;AAAA,IAAO;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAQ;AAAA,IAC/D;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ;AAAA,EAClE,CAAC;AACD,QAAM,MAAM,KAAK,YAAY,EAAE,MAAM,YAAY,EAAE,OAAO,OAAO;AACjE,QAAM,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;AAE3D,SAAO,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC;AACzB;AAEA,eAAe,sBACb,QACA,WACA,YAC6B;AAC7B,QAAM,MAA0B,CAAC;AACjC,QAAM,UAAoB,CAAC;AAC3B,aAAW,MAAM,WAAW;AAC1B,QAAI,QAAiC;AACrC,eAAW,MAAM,YAAY;AAC3B,YAAM,UAAU,MAAM,OAAO,aAAa,WAAW,EAAE;AACvD,YAAM,IAAI,MAAM,QAAQ,cAAc,EAAE;AACxC,UAAI,GAAG;AACL,gBAAQ,YAAY,GAAG,IAAI,CAAC;AAC5B;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAO,KAAI,KAAK,KAAK;AAAA,QACpB,SAAQ,KAAK,EAAE;AAAA,EACtB;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI,wBAAwB,4BAA4B,QAAQ,CAAC,CAAC,EAAE;AAAA,EAC5E;AACA,SAAO;AACT;AAEA,eAAe,sBACb,QACA,SACA,YACA,OAC6B;AAS7B,MAAI,QAAQ,WAAW,KAAK,SAAS,KAAK,WAAW,WAAW,EAAG,QAAO,CAAC;AAC3E,QAAM,MAA0B,CAAC;AACjC,QAAM,OAAO,IAAI,IAAY,OAAO;AAGpC,QAAM,WAAW,oBAAI,IAAY;AACjC,aAAW,MAAM,YAAY;AAC3B,UAAM,UAAU,MAAM,OAAO,aAAa,WAAW,EAAE;AACvD,UAAM,MAAM,MAAM,QAAQ,gBAAgB;AAC1C,eAAW,KAAK,KAAK;AACnB,UAAI,QAAQ,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,WAAW;AACjE,iBAAS,IAAI,EAAE,YAAY,SAAS;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,SAAS,EAAG,QAAO,CAAC;AACjC,aAAW,MAAM,YAAY;AAC3B,QAAI,IAAI,UAAU,MAAO;AACzB,UAAM,UAAU,MAAM,OAAO,aAAa,WAAW,EAAE;AACvD,UAAM,MAAM,MAAM,QAAQ,gBAAgB;AAC1C,eAAW,KAAK,KAAK;AACnB,UAAI,IAAI,UAAU,MAAO;AACzB,UAAI,KAAK,IAAI,EAAE,YAAY,EAAE,EAAG;AAChC,UAAI,EAAE,YAAY,UAAU,EAAE,YAAY,WAAW,SAAU;AAC/D,UAAI,EAAE,YAAY,aAAa,SAAS,IAAI,EAAE,YAAY,SAAS,GAAG;AACpE,YAAI,KAAK,YAAY,GAAG,IAAI,GAAG,CAAC;AAChC,aAAK,IAAI,EAAE,YAAY,EAAE;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,iBACb,QACA,MACA,YACkC;AAIlC,MAAI;AACF,QAAI,CAAC,OAAO,aAAa;AACvB,aAAO,uBAAuB,YAAY,yBAAyB;AAAA,IACrE;AACA,UAAM,OAAO,oBAAoB,MAAM,UAAU;AACjD,UAAM,MAAM,MAAM,OAAO,YAAY,EAAE,QAAQ,wBAAwB,KAAK,CAAC;AAC7E,WAAO,sBAAsB,KAAK,UAAU;AAAA,EAC9C,SAAS,KAAK;AACZ,WAAO,uBAAuB,YAAY,oBAAoBC,QAAO,GAAG,CAAC,EAAE;AAAA,EAC7E;AACF;AAEA,eAAe,qBACb,SACA,YACA,SACiB;AAIjB,QAAM,QAAkB,CAAC;AACzB,aAAW,UAAU,SAAS;AAC5B,UAAM,KACJ,OAAO,SAAS,cACZ,OAAO,UACP,OAAO,SAAS,UAAU,OAAO,SAAS,aAAa,OAAO,SAAS,YACrE,OAAO,WACP;AACR,UAAM,YAAY,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI;AACnE,UAAM,SAAS,WAAW,WAAW;AACrC,UAAM,QAAQ,mBAAmB,MAAM;AACvC,UAAM,KAAK,OAAO,MAAM,gBAAgB,KAAK,OAAO,IAAI,GAAG;AAC3D,UAAM,KAAK,KAAK,OAAO,MAAM,GAAG,GAAG,CAAC,EAAE;AACtC,UAAM,KAAK,KAAK,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EACvC;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,mBAAmB,QAAkC;AAC5D,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO,OAAO,aAAa,WAAW;AAAA,IACxC,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,wBAAwB,OAAO,WAAW;AAAA,IACnD,KAAK;AACH,aAAO,0CAA0C,OAAO,OAAO;AAAA,EACnE;AACF;AAMA,SAAS,iBACP,QACA,MACc;AACd,SAAO;AAAA,IACL,WAAW,OAAO,WAAW,aAAa,kBAAkB,QAAQ,WAAW,QAAQ;AAAA,IACvF,kBAAkB,OAAO,WAAW,UAClC,uBAAuB,QAAQ,WAAW,KAAK;AAAA,IACjD,WAAW,OAAO,WAAW,UAAU,UACrC,gBAAgB,QAAQ,WAAW,UAAU,KAAK;AAAA,IACpD,cAAc,OAAO,WAAW,UAAU,eACxC,eAAe,QAAQ,WAAW,UAAU,UAAU;AAAA,IACxD,eAAe,OAAO,WAAW,UAAU,gBACzC,gBAAgB,QAAQ,WAAW,UAAU,WAAW;AAAA,IAC1D,iBAAiB,OAAO,WAAW,UACjC,kBAAkB,QAAQ,WAAW,KAAK;AAAA,IAC5C,uBAAuB,OAAO,WAAW,YACvC,wBAAwB,QAAQ,WAAW,OAAO;AAAA,IACpD,mBAAmB,OAAO,WAAW,WACnC,oBAAoB,QAAQ,WAAW,MAAM;AAAA,IAC/C,WAAW,OAAO,WAAW,qBAC3B,YAAY,QAAQ,WAAW,gBAAgB;AAAA,IACjD,mBAAmB,KAAK;AAAA,IACxB,KAAK,MAAM,oBAAI,KAAK;AAAA,EACtB;AACF;AAEA,eAAe,kBACb,QACA,WACA,UACgC;AAChC,QAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAC9D,QAAM,IAAI,MAAM,QAAQ,cAAc,QAAQ;AAC9C,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,iBAAiB,CAAC;AAC3B;AAEA,eAAe,uBACb,QACA,WACA,OAWiB;AACjB,QAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAI9D,QAAM,KAAK,MAAM,QAAQ;AAAA,IACtB,MAAM,YAAY;AAAA,IACnB,MAAM;AAAA,IACN;AAAA,MACE,QAAQ;AAAA,MACR,YAAY,MAAM,cAAc;AAAA,MAChC,MAAM,MAAM,QAAQ,CAAC;AAAA,MACrB,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,MACxD,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;AAAA,MAClD,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;AAAA,MAC3D,GAAI,MAAM,uBAAuB,EAAE,sBAAsB,MAAM,qBAAqB,IAAI,CAAC;AAAA,MACzF,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;AAAA,IAC7D;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,gBACb,QACA,WACA,UACA,OACiB;AACjB,QAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAC9D,QAAM,WAAW,MAAM,QAAQ,cAAc,QAAQ;AACrD,MAAI,CAAC,SAAU,OAAM,IAAI,wBAAwB,8BAA8B,QAAQ,EAAE;AAKzF,QAAM,QAAQ;AAAA,IACZ,EAAE,GAAG,UAAU,SAAS,MAAM;AAAA,IAC9B,EAAE,UAAS,oBAAI,KAAK,GAAE,YAAY,EAAE;AAAA,EACtC;AACA,SAAO;AACT;AAEA,eAAe,eACb,QACA,WACA,UACA,MACe;AACf,QAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAC9D,QAAM,SAAS,MAAM,QAAQ,cAAc,QAAQ;AACnD,MAAI,CAAC,OAAQ,OAAM,IAAI,wBAAwB,gCAAgC,QAAQ,EAAE;AAMzF,QAAM,gBAA8B,KAAK,WAAW,cAAc,cAAc;AAGhF,QAAM,QAAQ,uBAAuB,QAAQ;AAAA,IAC3C,QAAQ;AAAA,IACR,GAAI,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC;AAAA,IAC/D,GAAI,KAAK,WAAW,eAAe,EAAE,eAAc,oBAAI,KAAK,GAAE,YAAY,EAAE,IAAI,CAAC;AAAA;AAAA,IAEjF,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,EAC3D,CAAC;AACH;AAEA,eAAe,gBACb,QACA,WACA,UACA,aACiB;AAGjB,QAAM,gBAAgB,MAAM,OAAO,aAAa,WAAW,SAAS;AACpE,QAAM,SAAS,MAAM,cAAc,cAAc,QAAQ;AACzD,MAAI,CAAC,OAAQ,OAAM,IAAI,wBAAwB,iCAAiC,QAAQ,EAAE;AAC1F,MAAI,OAAO,YAAY,UAAU,OAAO,YAAY,WAAW,UAAU;AAGvE,UAAM,IAAI;AAAA,MACR,yBAAyB,QAAQ,eAAe,OAAO,YAAY,MAAM;AAAA,IAC3E;AAAA,EACF;AACA,QAAM,cAAc,MAAM,OAAO,aAAa,WAAW,WAAW;AACpE,QAAM,KAAK,OAAO;AAMlB,QAAM,cAAc,GAAG,uBACnB,sBAAsB,OAAO,OAAO,IACpC,OAAO;AACX,QAAM,SAAS,MAAM,YAAY,YAAY,GAAG,UAAU,aAAa;AAAA,IACrE,QAAQ,sBAAsB,SAAS;AAAA,IACvC,GAAI,OAAO,GAAG,eAAe,WAAW,EAAE,YAAY,GAAG,WAAW,IAAI,CAAC;AAAA,IACzE,GAAI,MAAM,QAAQ,GAAG,IAAI,IAAI,EAAE,MAAM,GAAG,KAAK,IAAI,CAAC;AAAA,IAClD,GAAI,GAAG,YAAY,EAAE,WAAW,GAAG,UAAU,IAAI,CAAC;AAAA,IAClD,GAAI,GAAG,uBAAuB,EAAE,sBAAsB,GAAG,qBAAqB,IAAI,CAAC;AAAA,IACnF,GAAI,GAAG,WAAW,EAAE,SAAS,GAAG,SAAS,IAAI,CAAC;AAAA,IAC9C,GAAI,GAAG,aAAa,EAAE,YAAY,GAAG,WAAW,IAAI,CAAC;AAAA,IACrD,GAAI,GAAG,aAAa,EAAE,YAAY,GAAG,WAAW,IAAI,CAAC;AAAA,IACrD,GAAI,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE,OAAO,GAAG,MAAM,IAAI,CAAC;AAAA,IACrD,GAAI,GAAG,aAAa,EAAE,YAAY,GAAG,WAAW,IAAI,CAAC;AAAA,EACvD,CAAC;AAKD,MAAI;AACF,UAAM,cAAc,uBAAuB,QAAQ;AAAA,MACjD,QAAQ;AAAA,MACR,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI;AACF,YAAM,UAAU,MAAM,YAAY,cAAc,MAAM;AACtD,UAAI,SAAS;AACX,cAAM,YAAY,uBAAuB,SAAS;AAAA,UAChD,QAAQ;AAAA,UACR,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,QACrC,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AACA,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,eAAe,kBACb,QACA,WACA,OAOwB;AACxB,QAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAS9D,QAAM,UACJ,OAAO,QAAQ,wBAAwB,aACnC,QAAQ,oBAAoB,IAC5B;AACN,QAAM,SAAS,MAAM,QAAQ,gBAAgB;AAAA,IAC3C,QAAQ,MAAM;AAAA,IACd,WAAW;AAAA,IACX,gBAAgB,MAAM;AAAA,IACtB,YAAY,MAAM;AAAA,IAClB,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IACxD,GAAI,MAAM,kBAAkB,EAAE,iBAAiB,MAAM,gBAAgB,IAAI,CAAC;AAAA,EAC5E,CAAC;AACD,MAAI,WAAW,QAAQ,SAAS;AAC9B,UAAM,IAAI;AAAA,MACR,2CAA2C,MAAM,cAAc;AAAA,IACjE;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,wBACb,QACA,WACA,SACe;AAGf,QAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAC9D,QAAM,MAAMC,MAAK,KAAK,QAAQ,KAAK,SAAS,eAAe,iBAAiB;AAC5E,QAAMC,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAGpC,QAAM,OAAO,QAAQ,QAAQ,kBAAkB,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK;AACpE,QAAMC;AAAA,IACJF,MAAK,KAAK,KAAK,GAAG,IAAI,OAAO;AAAA,IAC7B,GAAG,KAAK,UAAU,EAAE,SAAS,WAAW,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC,CAAC;AAAA;AAAA,IAC9E;AAAA,EACF;AACF;AAEA,eAAe,oBACb,QACA,WACA,QAMiB;AACjB,QAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAI9D,QAAM,KAAK,MAAM,QAAQ,YAAY,cAAc,eAAe,MAAM,GAAG;AAAA,IACzE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,MAAM,CAAC,oBAAoB,QAAQ,OAAO,MAAM,IAAI,kBAAkB,OAAO,cAAc,EAAE;AAAA,EAC/F,CAAC;AACD,SAAO;AACT;AAEA,SAAS,eAAe,QAKb;AAIT,QAAM,YACJ,OAAO,mBAAmB,iBAC1B,OAAO,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,gBAAgB;AACvE,QAAM,cAAc,YAChB,0FACA,OAAO,YAAY,MAAM,GAAG,GAAG;AACnC,QAAM,QAAQ;AAAA,IACZ,mBAAmB,OAAO,MAAM,aAAa,OAAO,QAAQ,MAAM;AAAA,IAClE;AAAA,IACA,YAAY,WAAW;AAAA,IACvB,mBAAmB,OAAO,cAAc;AAAA,IACxC,eAAe,OAAO,QAAQ,SAAS;AAAA,IACvC;AAAA,IACA;AAAA,EACF;AACA,aAAW,KAAK,OAAO,QAAQ,SAAS;AAGtC,UAAM,WACJ,EAAE,OAAO,SAAS,oBAAoB,OAAO,mBAAmB;AAClE,UAAM,UAAU,EAAE,QAAS,WAAW,sBAAsB,KAAK,EAAE,KAAK,MAAO;AAC/E,UAAM,KAAK,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE;AAAA,EAC1D;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,eAAe,YACb,QACA,WACA,kBACe;AAIf,aAAW,MAAM,kBAAkB;AACjC,QAAI;AAGF,YAAM,UAAU,MAAM,OAAO,aAAa,WAAW,SAAS;AAC9D,YAAM,eAAe,OAAO;AAG5B,UAAI,OAAO,aAAa,yBAAyB,YAAY;AAC3D,cAAM,aAAa,qBAAqB,SAAS,EAAE;AAAA,MACrD;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAOA,SAAS,YAAY,GAAe,WAAmB,OAAiC;AACtF,SAAO;AAAA,IACL,UAAU,EAAE,YAAY;AAAA,IACxB,MAAM,EAAE;AAAA,IACR,SAAS,EAAE;AAAA,IACX,SAAS,EAAE,QAAQ,MAAM,GAAG,GAAG;AAAA,IAC/B,GAAI,EAAE,YAAY,YAAY,EAAE,WAAW,EAAE,YAAY,UAAU,IAAI,CAAC;AAAA,IACxE;AAAA;AAAA;AAAA,EAGF;AACF;AAEA,SAAS,iBAAiB,GAA+B;AACvD,QAAM,KAAK,EAAE;AAKb,QAAM,UAAU,GAAG,uBAAuB,sBAAsB,EAAE,OAAO,IAAI,EAAE;AAC/E,SAAO;AAAA,IACL,UAAU,GAAG;AAAA,IACb,SAAS,EAAE;AAAA,IACX,UAAU,GAAG;AAAA,IACb,YAAY;AAAA,IACZ,GAAI,GAAG,YAAY,EAAE,WAAW,GAAG,UAAU,IAAI,CAAC;AAAA,EACpD;AACF;AAUO,SAAS,2BAA2B,QAA+B;AAKxE,MAAI,OAAO,OAAO,sBAAsB,UAAW,QAAO,OAAO;AACjE,QAAM,SAAU,OAA8C;AAG9D,MAAI,UAAU,OAAO,OAAO,YAAY,UAAW,QAAO,OAAO;AACjE,MAAI,UAAU,OAAO,OAAO,YAAY,SAAU,QAAO,OAAO,YAAY,UAAU,OAAO,YAAY;AACzG,SAAO,mBAAmB,QAAQ,WAAW,IAAI;AACnD;AAGA,SAAS,mBAAmB,QAAsB,KAAa,UAA4B;AAEzF,QAAM,SAAU,OAA8C;AAG9D,MAAI,UAAU,OAAO,OAAO,GAAG,MAAM,UAAW,QAAO,OAAO,GAAG;AACjE,MAAI,UAAU,OAAO,OAAO,GAAG,MAAM,SAAU,QAAQ,OAAO,GAAG,MAAiB,UAAW,OAAO,GAAG,MAAiB;AAExH,QAAM,MAAO,OAA8C,aAAa,WAAW,GAAG,CAAC,EAAE;AACzF,MAAI,OAAO,QAAQ,UAAW,QAAO;AACrC,MAAI,OAAO,QAAQ,SAAU,QAAO,QAAQ,UAAU,QAAQ;AAC9D,SAAO;AACT;AAGA,SAAS,qBAAqB,QAAsB,KAAa,UAA0B;AAEzF,QAAM,SAAU,OAA8C;AAG9D,MAAI,QAAQ;AACV,UAAM,KAAK,OAAO,GAAG;AACrB,QAAI,OAAO,OAAO,YAAY,OAAO,SAAS,EAAE,EAAG,QAAO;AAC1D,QAAI,OAAO,OAAO,UAAU;AAC1B,YAAM,KAAK,OAAO,EAAE;AACpB,UAAI,OAAO,SAAS,EAAE,EAAG,QAAO;AAAA,IAClC;AAAA,EACF;AACA,QAAM,MAAO,OAA8C,aAAa,WAAW,GAAG,CAAC,EAAE;AACzF,MAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,GAAG,EAAG,QAAO;AAC5D,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,SAAS,CAAC,EAAG,QAAO;AAAA,EACjC;AACA,SAAO;AACT;AAEA,SAAS,WAAW,GAAmB;AACrC,SAAO,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAC9C;AAEA,SAASD,QAAO,KAAsB;AACpC,MAAI,eAAe,MAAO,QAAO,IAAI;AACrC,SAAO,OAAO,GAAG;AACnB;AAMA,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmB/B,SAAS,oBAAoB,MAAc,YAAwC;AACjF,QAAM,QAAQ;AAAA,IACZ,eAAe,IAAI;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACA,aAAW,KAAK,WAAW,MAAM,GAAG,EAAE,GAAG;AACvC,UAAM,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,OAAO,EAAE;AAAA,EAC3C;AACA,QAAM,KAAK,IAAI,oCAAoC;AACnD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,sBACP,KACA,YACyB;AACzB,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,WAAO,uBAAuB,YAAY,gCAAgC;AAAA,EAC5E;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,WAAO,uBAAuB,YAAY,kCAAkC;AAAA,EAC9E;AACA,QAAM,MAAM;AACZ,QAAM,iBAAiB,iBAAiB,IAAI,cAAc,IAAI,IAAI,iBAAiB;AACnF,QAAM,aAAa,OAAO,IAAI,eAAe,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI;AACnG,QAAM,aAAa,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,UAAU,CAAC;AAC/D,QAAM,UAA8B,CAAC;AACrC,QAAM,WAAqB,CAAC;AAC5B,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,+BAAyB,SAAS;AAClC,cAAQ,KAAK,SAAS;AAAA,IACxB,SAAS,KAAK;AACZ,eAAS,KAAK,6BAA6BA,QAAO,GAAG,CAAC,EAAE;AAAA,IAC1D;AAAA,EACF;AACA,QAAM,YAAY,MAAM,QAAQ,IAAI,SAAS,IACxC,IAAI,UACF,OAAO,CAAC,MAAoC,CAAC,CAAC,KAAK,OAAO,MAAM,QAAQ,EACxE,IAAI,CAAC,OAAO;AAAA,IACX,UAAU,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAAA,IACxD,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM;AAAA,EAC3C,EAAE,EACD,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC,IACtC,CAAC;AACL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,OAA2D;AACnF,SACE,UAAU,WACV,UAAU,cACV,UAAU,gBACV,UAAU,iBACV,UAAU;AAEd;AAEA,SAAS,uBACP,YACA,QACyB;AACzB,SAAO;AAAA,IACL,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,SAAS,CAAC;AAAA,IACV,WAAW,WAAW,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,KAAK,qBAAqB,EAAE;AAAA,IACtF,UAAU,CAAC,MAAM;AAAA,IACjB,UAAU;AAAA,EACZ;AACF;;;AIzyBA,IAAM,WAAyC;AAAA;AAAA,EAE7C;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA;AAAA,EAEA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,IAEV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA;AAAA,EAEA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AACF;AAUA,IAAM,qBACJ;AAMF,IAAM,oBACJ;AAMF,IAAM,oBACJ;AAOF,IAAM,qBACJ;AAOK,SAAS,eAAe,MAAwB;AACrD,QAAM,UAAU,KAAK,MAAM,wBAAwB;AACnD,SAAO,WAAW,CAAC;AACrB;AAMA,SAAS,kBAAkB,MAAc,OAAiC;AACxE,QAAM,aAAa,MAAM,SAAS;AAElC,QAAM,QAAQ,KAAK,IAAI,GAAG,aAAa,EAAE;AACzC,QAAM,MAAM,KAAK,IAAI,KAAK,QAAQ,aAAa,MAAM,CAAC,EAAE,SAAS,EAAE;AACnE,QAAM,SAAS,KAAK,MAAM,OAAO,GAAG,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAChE,SAAO,OAAO,SAAS,KAAK,OAAO,MAAM,GAAG,EAAE,IAAI,QAAQ;AAC5D;AAQA,SAAS,0BACP,MACA,UACQ;AACR,MAAI,aAAa,UAAW,QAAO;AAEnC,QAAM,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC/C,SAAO,QAAQ,SAAS,MAAM,QAAQ,MAAM,GAAG,GAAG,IAAI,QAAQ;AAChE;AAOA,SAAS,eAAe,MAAc,YAA6B;AAQjE,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,cAAc,IAAI,KAAK,QAAQ,KAAK;AACtD,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,MAAM,OAAO,MAAM,YAAY,MAAM,UAAU;AACjD,gBAAU,CAAC;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,kBAAkB,MAA6B;AACtD,MAAI,mBAAmB,KAAK,IAAI,EAAG,QAAO;AAC1C,MAAI,mBAAmB,KAAK,IAAI,EAAG,QAAO;AAC1C,MAAI,kBAAkB,KAAK,IAAI,EAAG,QAAO;AACzC,MAAI,kBAAkB,KAAK,IAAI,EAAG,QAAO;AACzC,SAAO;AACT;AAoBO,SAAS,yBACd,OACqB;AACrB,QAAM,UAA+B,CAAC;AACtC,QAAM,OAAO,oBAAI,IAAY;AAE7B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,KAAK,SAAS,OAAQ;AAC1B,UAAM,UAAU,KAAK,SAAS,KAAK;AACnC,QAAI,CAAC,QAAS;AAId,UAAM,QAAQ,kBAAkB,OAAO;AACvC,QAAI,MAAO;AAEX,UAAM,UAAU,eAAe,OAAO;AAEtC,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,QAAQ,MAAM,KAAK,OAAO;AACxC,UAAI,CAAC,MAAO;AAIZ,UAAI,MAAM,UAAU,UAAa,eAAe,SAAS,MAAM,KAAK,GAAG;AACrE;AAAA,MACF;AAEA,YAAM,aAAa,kBAAkB,SAAS,KAAK;AACnD,YAAM,qBAAqB,0BAA0B,SAAS,QAAQ,QAAQ;AAK9E,YAAM,WAAW,GAAG,CAAC,IAAI,UAAU;AACnC,UAAI,KAAK,IAAI,QAAQ,EAAG;AACxB,WAAK,IAAI,QAAQ;AAEjB,cAAQ,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,UAAU,QAAQ;AAAA,QAClB;AAAA,QACA,YAAY,QAAQ;AAAA,QACpB,eAAe,QAAQ,MAAM,GAAG,GAAG;AAAA,QACnC,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;AC5NO,SAAS,iBAA0C;AACxD,SAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,aAAa,GAAG,mBAAmB,CAAC,EAAE;AACzE;AAOA,SAAS,wBAAwB,MAAsB;AACrD,SAAO,KACJ,YAAY,EACZ,QAAQ,YAAY,EAAE,EACtB,QAAQ,QAAQ,GAAG,EACnB,KAAK,EACL,MAAM,GAAG,GAAG;AACjB;AAEA,SAAS,sBAAsB,WAAmB,YAAuC;AACvF,SAAO,GAAG,SAAS,KAAK,WAAW,QAAQ,KAAK,wBAAwB,WAAW,UAAU,CAAC;AAChG;AAOA,IAAM,+BAA+B,oBAAI,IAAI,CAAC,aAAa,QAAQ,SAAS,CAAC;AAG7E,IAAM,kCAAkC,oBAAI,IAA8B,CAAC,YAAY,OAAO,CAAC;AAcxF,SAAS,wBACd,MACA,QACA,eACmC;AACnC,MAAI,CAAC,cAAe,QAAO;AAC3B,MAAI,KAAK,aAAa,OAAO,gBAAiB,QAAO;AACrD,MAAI,CAAC,gCAAgC,IAAI,KAAK,cAAc,GAAG;AAC7D,WAAO;AAAA,EACT;AACA,MAAI,KAAK,SAAS,SAAS,OAAO,sBAAsB;AACtD,WAAO;AAAA,EACT;AAKA,MAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,WAAO;AAAA,EACT;AACA,aAAW,UAAU,KAAK,SAAS;AACjC,QAAI,CAAC,6BAA6B,IAAI,OAAO,IAAI,GAAG;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAuBA,eAAsB,0BACpB,aACA,KACA,QACA,MACA,YAC+B;AAC/B,QAAM,YAAY,eAAe;AACjC,QAAM,QAA0B,CAAC;AACjC,YAAU,WAAW,YAAY;AAEjC,aAAW,cAAc,aAAa;AAKpC,UAAM,KAAK,sBAAsB,IAAI,WAAW,UAAU;AAC1D,QAAI,WAAW,IAAI,EAAE,GAAG;AACtB;AAAA,IACF;AAQA,QAAI;AACJ,QAAI,WAAW,QAAQ,SAAS,KAAK,KAAK,iBAAiB,IAAI,YAAY;AACzE,YAAM,WAAqB,CAAC;AAC5B,iBAAW,UAAU,WAAW,SAAS;AACvC,cAAM,KAAK,KAAK,cAAc,QAAQ,IAAI,UAAU;AACpD,YAAI,GAAI,UAAS,KAAK,EAAE;AAAA,MAC1B;AACA,UAAI,SAAS,SAAS,EAAG,aAAY;AAAA,IACvC;AAGA,UAAM,UAA6B;AAAA,MACjC,MAAM,WAAW,sBAAsB,WAAW;AAAA,MAClD,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,IAAI,aAAa,EAAE,YAAY,IAAI,WAAW,IAAI,CAAC;AAAA,MACvD,GAAI,IAAI,YAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,MACpD,WAAW,IAAI;AAAA,IACjB;AAEA,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,eAAe,OAAO;AAAA,IAC1C,SAAS,KAAK;AACZ,UAAI;AAAA,QACF,4CAA4C,WAAW,WAAW,MAAM,GAAG,EAAE,CAAC,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACtI;AACA;AAAA,IACF;AACA,UAAM,KAAK,IAAI;AAEf,eAAW,IAAI,EAAE;AAGjB,QAAI,OAAO,SAAS,SAAS;AAC3B,gBAAU,UAAU;AACpB;AAAA,IACF;AAGA,UAAM,cAAc,wBAAwB,MAAM,QAAQ,IAAI,aAAa;AAC3E,QAAI,aAAa;AACf,gBAAU,kBAAkB,WAAW,KACpC,UAAU,kBAAkB,WAAW,KAAK,KAAK;AACpD,gBAAU,UAAU;AACpB;AAAA,IACF;AAGA,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,gBAAgB,KAAK,QAAQ;AAAA,QACtD,SAAS;AAAA,QACT,WAAW,IAAI;AAAA,QACf,GAAI,IAAI,aAAa,EAAE,YAAY,IAAI,WAAW,IAAI,CAAC;AAAA,QACvD,GAAI,IAAI,YAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,MACtD,CAAC;AAID,UAAI,QAAQ,WAAW,WAAW;AAKhC,YAAI;AAAA,UACF,4BAA4B,KAAK,MAAM;AAAA,QACzC;AACA;AAAA,MACF;AACA,gBAAU,eAAe;AAGzB,UAAI;AACF,cAAM,MAAM,MAAM,KAAK,WAAW,IAAI,SAAS;AAC/C,cAAM,qCAAqC,KAAK;AAAA,UAC9C,QAAQ,KAAK;AAAA,UACb,SAAS,yBAAyB,IAAI;AAAA,UACtC,aAAa,sBAAsB,KAAK,MAAM;AAAA,UAC9C,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC,CAAC;AAAA,MACH,SAAS,KAAK;AAEZ,YAAI,MAAM,gEAAgE,GAAG,EAAE;AAAA,MACjF;AAAA,IACF,SAAS,KAAK;AACZ,UAAI;AAAA,QACF,kDAAkD,KAAK,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACpH;AACA,gBAAU,UAAU;AAAA,IACtB;AAAA,EACF;AAEA,SAAO,EAAE,WAAW,MAAM;AAC5B;AAMA,SAAS,yBAAyB,MAA8B;AAC9D,QAAM,SAAS,KAAK,QAAQ,CAAC;AAC7B,MAAI,CAAC,QAAQ;AACX,WAAO,KAAK,QAAQ,KAAK,MAAM,GAAG,EAAE;AAAA,EACtC;AACA,QAAM,SACJ,OAAO,SAAS,cACZ,OAAO,UACP,OAAO,SAAS,UAAU,OAAO,SAAS,aAAa,OAAO,SAAS,YACrE,OAAO,WACP;AACR,SAAO,GAAG,KAAK,cAAc,KAAK,KAAK,QAAQ,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,QAAQ,KAAK,SAAS,KAAK,QAAQ,EAAE,KAAK,MAAM;AAC1H;;;AC3TO,SAAS,aAAgB,OAAqB,OAA8C;AACjG,QAAM,WAAW,oBAAI,IAAiB;AACtC,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,MAAM,IAAI;AACtB,QAAI,OAAO,QAAQ,UAAU;AAC3B,YAAM,IAAI;AAAA,QACR,iDAAiD,QAAQ,OAAO,SAAS,OAAO,GAAG;AAAA,MACrF;AAAA,IACF;AACA,UAAM,WAAW,SAAS,IAAI,GAAG;AACjC,QAAI,UAAU;AACZ,eAAS,KAAK,IAAI;AAAA,IACpB,OAAO;AACL,eAAS,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;","names":["path","mkdir","writeFile","seedIds","neighbors","errMsg","path","mkdir","writeFile"]}