@remnic/core 9.3.729 → 9.3.730

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 (345) hide show
  1. package/dist/access-boundary.d.ts +6 -5
  2. package/dist/access-boundary.js +13 -13
  3. package/dist/access-cli.js +34 -33
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +6 -5
  6. package/dist/access-http.js +19 -19
  7. package/dist/access-mcp.d.ts +6 -5
  8. package/dist/access-mcp.js +18 -18
  9. package/dist/access-operations-batch.js +16 -16
  10. package/dist/access-operations.d.ts +11 -10
  11. package/dist/access-operations.js +17 -17
  12. package/dist/access-schema.d.ts +42 -42
  13. package/dist/{access-service-BXMJqkTA.d.ts → access-service-Byoe3HsT.d.ts} +18 -3
  14. package/dist/access-service.d.ts +6 -5
  15. package/dist/access-service.js +12 -12
  16. package/dist/access-surface-catalog.d.ts +6 -5
  17. package/dist/action-confidence.d.ts +1 -1
  18. package/dist/active-memory-bridge.d.ts +1 -1
  19. package/dist/active-recall.d.ts +1 -1
  20. package/dist/active-recall.js +2 -2
  21. package/dist/{auto-sync-F3KCSQFJ.js → auto-sync-G3V5AN4C.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 +5 -4
  25. package/dist/briefing.d.ts +1 -1
  26. package/dist/briefing.js +3 -3
  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 +55 -2
  31. package/dist/capabilities.js +5 -1
  32. package/dist/{catalog-bMczlO2G.d.ts → catalog-DM1nk7cM.d.ts} +1 -1
  33. package/dist/causal-behavior.d.ts +1 -1
  34. package/dist/causal-consolidation.d.ts +1 -1
  35. package/dist/causal-consolidation.js +4 -4
  36. package/dist/causal-trajectory-graph.d.ts +1 -1
  37. package/dist/{chunk-4NOI577P.js → chunk-24ZLC5QN.js} +2 -2
  38. package/dist/{chunk-DMQWV6SQ.js → chunk-5RZOFIDL.js} +2 -2
  39. package/dist/{chunk-UM5AWJNQ.js → chunk-646B5ERJ.js} +62 -37
  40. package/dist/chunk-646B5ERJ.js.map +1 -0
  41. package/dist/{chunk-H5BWGCRZ.js → chunk-6TUWSUUD.js} +51 -47
  42. package/dist/{chunk-H5BWGCRZ.js.map → chunk-6TUWSUUD.js.map} +1 -1
  43. package/dist/{chunk-36SEDFAU.js → chunk-C5WN74HH.js} +2 -2
  44. package/dist/{chunk-MLKWJOAJ.js → chunk-CGPHMNL4.js} +2 -2
  45. package/dist/{chunk-R6OVFAX6.js → chunk-DJECGBBM.js} +7 -2
  46. package/dist/{chunk-R6OVFAX6.js.map → chunk-DJECGBBM.js.map} +1 -1
  47. package/dist/{chunk-3RCBIOOJ.js → chunk-ESF7TRYJ.js} +18 -2
  48. package/dist/{chunk-3RCBIOOJ.js.map → chunk-ESF7TRYJ.js.map} +1 -1
  49. package/dist/{chunk-EY6MDMHK.js → chunk-F6OEXJQQ.js} +2 -2
  50. package/dist/{chunk-M4V2JVSZ.js → chunk-FFUCCKPN.js} +2 -2
  51. package/dist/{chunk-O6YH5F47.js → chunk-FYWUNVFZ.js} +8 -8
  52. package/dist/{chunk-37436GML.js → chunk-GEXI357K.js} +2 -2
  53. package/dist/{chunk-YGIQORJ3.js → chunk-GGL34GCW.js} +3 -3
  54. package/dist/{chunk-4H4F3BFL.js → chunk-GXTYEG7Q.js} +3 -3
  55. package/dist/{chunk-4H4F3BFL.js.map → chunk-GXTYEG7Q.js.map} +1 -1
  56. package/dist/{chunk-WAS3NV46.js → chunk-H3MB3F62.js} +2 -2
  57. package/dist/{chunk-UTHQPSRC.js → chunk-HHNSLPX7.js} +11 -8
  58. package/dist/chunk-HHNSLPX7.js.map +1 -0
  59. package/dist/{chunk-76NHR6ZL.js → chunk-HJWQJUKQ.js} +4 -4
  60. package/dist/{chunk-Y4YD5TYW.js → chunk-IIBFCZA7.js} +3 -3
  61. package/dist/{chunk-UVPL565C.js → chunk-IQOEW5YV.js} +2 -2
  62. package/dist/{chunk-V4GNSDFH.js → chunk-KQYGNGON.js} +2 -2
  63. package/dist/{chunk-CBWI22JN.js → chunk-KSJCXXOU.js} +2 -2
  64. package/dist/{chunk-M4AYVEWS.js → chunk-LUCARK4W.js} +12 -11
  65. package/dist/chunk-LUCARK4W.js.map +1 -0
  66. package/dist/{chunk-I5REUWLH.js → chunk-NIZQH6AU.js} +3 -3
  67. package/dist/{chunk-4ESAR77N.js → chunk-NOU77OHX.js} +2 -2
  68. package/dist/{chunk-BQCHEV7O.js → chunk-O4ZIBPIW.js} +3 -3
  69. package/dist/{chunk-HOHVIVDR.js → chunk-OBJUKYDO.js} +2 -2
  70. package/dist/{chunk-YKBHOBJK.js → chunk-OEHCMALU.js} +56 -4
  71. package/dist/chunk-OEHCMALU.js.map +1 -0
  72. package/dist/{chunk-YOLZS7NY.js → chunk-OTQE66IX.js} +2 -2
  73. package/dist/{chunk-HEDZUSCS.js → chunk-OUITHS6V.js} +246 -368
  74. package/dist/chunk-OUITHS6V.js.map +1 -0
  75. package/dist/{chunk-4T3BMHKD.js → chunk-OZECYVGI.js} +20 -7
  76. package/dist/chunk-OZECYVGI.js.map +1 -0
  77. package/dist/{chunk-C2PG7IEH.js → chunk-P3VJCTVE.js} +2 -2
  78. package/dist/{chunk-EH5ED3C6.js → chunk-PMAQRUN4.js} +4 -4
  79. package/dist/chunk-PMAQRUN4.js.map +1 -0
  80. package/dist/{chunk-7RLCCNHU.js → chunk-Q6BAA57B.js} +4 -3
  81. package/dist/chunk-Q6BAA57B.js.map +1 -0
  82. package/dist/{chunk-HGNOCPZX.js → chunk-QZHF4BWN.js} +6 -6
  83. package/dist/chunk-SYX73XD7.js +283 -0
  84. package/dist/chunk-SYX73XD7.js.map +1 -0
  85. package/dist/{chunk-ASKXIAHD.js → chunk-WDJCZQB4.js} +2 -2
  86. package/dist/{chunk-AQLOP5F4.js → chunk-WNGXQMRI.js} +95 -6
  87. package/dist/chunk-WNGXQMRI.js.map +1 -0
  88. package/dist/{chunk-JYLFZWUX.js → chunk-XYUFGHUW.js} +2 -2
  89. package/dist/{chunk-3TSTGI6F.js → chunk-ZQUAVZNG.js} +2 -2
  90. package/dist/{chunk-4NR2PDZ5.js → chunk-ZVHRSR2R.js} +14 -4
  91. package/dist/chunk-ZVHRSR2R.js.map +1 -0
  92. package/dist/{cli-DmAiDd8A.d.ts → cli-Dt-tuiYH.d.ts} +3 -3
  93. package/dist/cli.d.ts +7 -6
  94. package/dist/cli.js +35 -35
  95. package/dist/compounding/engine.d.ts +12 -1
  96. package/dist/compounding/engine.js +3 -3
  97. package/dist/compounding/preference-consolidator.d.ts +1 -1
  98. package/dist/compression-optimizer.d.ts +1 -1
  99. package/dist/config.d.ts +1 -1
  100. package/dist/config.js +2 -2
  101. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  102. package/dist/connectors/codex-materialize-runner.js +3 -3
  103. package/dist/connectors/codex-materialize.d.ts +1 -1
  104. package/dist/connectors/index.d.ts +1 -1
  105. package/dist/connectors/index.js +3 -3
  106. package/dist/consolidation-provenance-check.d.ts +1 -1
  107. package/dist/consolidation-undo.d.ts +1 -1
  108. package/dist/contradiction/index.d.ts +1 -1
  109. package/dist/contradiction/index.js +1 -1
  110. package/dist/conversation-index/backend.d.ts +1 -1
  111. package/dist/conversation-index/chunker.d.ts +1 -1
  112. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  113. package/dist/conversation-index/indexer.d.ts +1 -1
  114. package/dist/conversation-index/search.d.ts +1 -1
  115. package/dist/day-summary.d.ts +1 -1
  116. package/dist/delinearize.d.ts +1 -1
  117. package/dist/direct-answer-wiring.d.ts +1 -1
  118. package/dist/direct-answer.d.ts +1 -1
  119. package/dist/embedding-fallback.d.ts +1 -1
  120. package/dist/embedding-fallback.js +2 -2
  121. package/dist/enrichment/index.d.ts +1 -1
  122. package/dist/entity-retrieval.d.ts +1 -1
  123. package/dist/entity-retrieval.js +3 -3
  124. package/dist/entity-schema.d.ts +1 -1
  125. package/dist/explicit-capture.d.ts +6 -4
  126. package/dist/explicit-capture.js +1 -1
  127. package/dist/extraction-faithfulness.d.ts +1 -1
  128. package/dist/extraction-judge-telemetry.d.ts +1 -1
  129. package/dist/extraction-judge-training.d.ts +1 -1
  130. package/dist/extraction-judge.d.ts +1 -1
  131. package/dist/extraction.d.ts +1 -1
  132. package/dist/extraction.js +3 -3
  133. package/dist/fallback-llm.d.ts +1 -1
  134. package/dist/{first-start-migration-22LPFHD5.js → first-start-migration-OOR57AYR.js} +2 -2
  135. package/dist/graph-dashboard-diff.d.ts +1 -1
  136. package/dist/graph-dashboard-key.d.ts +1 -1
  137. package/dist/graph-dashboard-parser.d.ts +1 -1
  138. package/dist/graph-edge-reinforcement.d.ts +1 -1
  139. package/dist/graph-snapshot.d.ts +1 -1
  140. package/dist/graph.d.ts +1 -1
  141. package/dist/identity-continuity.d.ts +1 -1
  142. package/dist/importance.d.ts +1 -1
  143. package/dist/index.d.ts +296 -11
  144. package/dist/index.js +359 -47
  145. package/dist/index.js.map +1 -1
  146. package/dist/intent.d.ts +1 -1
  147. package/dist/lcm/engine.d.ts +1 -1
  148. package/dist/lcm/index.d.ts +1 -1
  149. package/dist/lcm/tools.d.ts +1 -1
  150. package/dist/lifecycle.d.ts +1 -1
  151. package/dist/live-connectors-runner.d.ts +1 -1
  152. package/dist/local-llm.d.ts +1 -1
  153. package/dist/local-model-endpoint.d.ts +1 -1
  154. package/dist/maintenance/memory-governance.d.ts +1 -1
  155. package/dist/maintenance/memory-governance.js +3 -3
  156. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  157. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  158. package/dist/mcp-memory-inspector-app.d.ts +6 -5
  159. package/dist/memory-action-policy.d.ts +1 -1
  160. package/dist/memory-cache.d.ts +1 -1
  161. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  162. package/dist/memory-projection-store.d.ts +1 -1
  163. package/dist/memory-provenance.d.ts +1 -1
  164. package/dist/memory-worth-outcomes.d.ts +1 -1
  165. package/dist/models-json.d.ts +1 -1
  166. package/dist/namespaces/migrate.d.ts +2 -2
  167. package/dist/namespaces/migrate.js +9 -9
  168. package/dist/namespaces/principal.d.ts +1 -1
  169. package/dist/namespaces/search.d.ts +1 -1
  170. package/dist/namespaces/search.js +5 -5
  171. package/dist/namespaces/storage.d.ts +2 -2
  172. package/dist/namespaces/storage.js +3 -3
  173. package/dist/native-knowledge.d.ts +1 -1
  174. package/dist/operator-toolkit.d.ts +1 -1
  175. package/dist/operator-toolkit.js +14 -14
  176. package/dist/orchestration/compression-guideline-coordinator.d.ts +105 -0
  177. package/dist/orchestration/compression-guideline-coordinator.js +12 -0
  178. package/dist/orchestration/maintenance.d.ts +2 -2
  179. package/dist/orchestration/maintenance.js +5 -5
  180. package/dist/{orchestrator-BR0sGsME.d.ts → orchestrator-B4KjaOtY.d.ts} +24 -8
  181. package/dist/orchestrator.d.ts +5 -4
  182. package/dist/orchestrator.js +28 -26
  183. package/dist/patterns-cli.d.ts +1 -1
  184. package/dist/policy-runtime.d.ts +1 -1
  185. package/dist/provenance.d.ts +1 -1
  186. package/dist/provenance.js +1 -1
  187. package/dist/qmd-recall-cache.d.ts +1 -1
  188. package/dist/qmd.d.ts +1 -1
  189. package/dist/recall-disclosure-escalation.d.ts +1 -1
  190. package/dist/recall-explain-renderer.d.ts +1 -1
  191. package/dist/recall-planner-llm.d.ts +1 -1
  192. package/dist/recall-state.d.ts +1 -1
  193. package/dist/recall-tag-filter.d.ts +1 -1
  194. package/dist/recall-xray-cli.d.ts +1 -1
  195. package/dist/recall-xray-renderer.d.ts +1 -1
  196. package/dist/recall-xray.d.ts +1 -1
  197. package/dist/{resolution-MN36NW5P.js → resolution-33PIX3TT.js} +2 -2
  198. package/dist/resolution-33PIX3TT.js.map +1 -0
  199. package/dist/resolve-auth-token.d.ts +1 -1
  200. package/dist/resume-bundles.js +3 -3
  201. package/dist/retrieval-agents.d.ts +1 -1
  202. package/dist/retrieval-tiers.d.ts +1 -1
  203. package/dist/routing/engine.d.ts +1 -1
  204. package/dist/routing/store.d.ts +1 -1
  205. package/dist/schemas.d.ts +84 -84
  206. package/dist/search/embed-helper.d.ts +1 -1
  207. package/dist/search/embed-helper.js +2 -2
  208. package/dist/search/factory.d.ts +1 -1
  209. package/dist/search/factory.js +4 -4
  210. package/dist/search/index.d.ts +1 -1
  211. package/dist/search/index.js +8 -8
  212. package/dist/search/lancedb-backend.d.ts +1 -1
  213. package/dist/search/meilisearch-backend.d.ts +1 -1
  214. package/dist/search/noop-backend.d.ts +1 -1
  215. package/dist/search/orama-backend.d.ts +1 -1
  216. package/dist/search/port.d.ts +1 -1
  217. package/dist/search/remote-backend.d.ts +1 -1
  218. package/dist/{semantic-consolidation-Cl15ksZy.d.ts → semantic-consolidation-CstTvHPo.d.ts} +1 -1
  219. package/dist/semantic-consolidation.d.ts +2 -2
  220. package/dist/semantic-consolidation.js +4 -4
  221. package/dist/semantic-rule-promotion.js +3 -3
  222. package/dist/semantic-rule-verifier.d.ts +1 -1
  223. package/dist/semantic-rule-verifier.js +3 -3
  224. package/dist/session-observer-bands.d.ts +1 -1
  225. package/dist/session-observer-state.d.ts +1 -1
  226. package/dist/shared-context/manager.d.ts +3 -3
  227. package/dist/signal.d.ts +1 -1
  228. package/dist/storage.d.ts +22 -3
  229. package/dist/storage.js +2 -2
  230. package/dist/summarizer.d.ts +1 -1
  231. package/dist/summary-snapshot.d.ts +1 -1
  232. package/dist/temporal-supersession.d.ts +1 -1
  233. package/dist/temporal-validity.d.ts +1 -1
  234. package/dist/threading.d.ts +1 -1
  235. package/dist/tier-migration.d.ts +1 -1
  236. package/dist/tier-routing.d.ts +1 -1
  237. package/dist/topics.d.ts +1 -1
  238. package/dist/transcript.d.ts +1 -1
  239. package/dist/transfer/types.d.ts +46 -46
  240. package/dist/trust-score-stage.d.ts +1 -1
  241. package/dist/trust-score.d.ts +1 -1
  242. package/dist/{types-ChQb9D6s.d.ts → types-VkxElCHR.d.ts} +8 -1
  243. package/dist/types.d.ts +1 -1
  244. package/dist/utility-runtime.d.ts +1 -1
  245. package/dist/verified-recall.js +3 -3
  246. package/package.json +12 -2
  247. package/src/access-service-coding-write.test.ts +2 -2
  248. package/src/access-service.ts +33 -14
  249. package/src/bitemporal-dedup-backfill.test.ts +10 -10
  250. package/src/bitemporal-frontmatter.test.ts +3 -3
  251. package/src/capabilities.test.ts +185 -0
  252. package/src/capabilities.ts +110 -0
  253. package/src/cli/creation-ledger-commands.ts +22 -21
  254. package/src/coding/architecture-surfaces.test.ts +25 -1
  255. package/src/coding/architecture-surfaces.ts +47 -19
  256. package/src/coding/decision-surfaces.ts +12 -3
  257. package/src/coding/review-context-structural.test.ts +303 -0
  258. package/src/coding/review-context.ts +79 -0
  259. package/src/coding/structural-context.test.ts +259 -0
  260. package/src/coding/structural-context.ts +315 -0
  261. package/src/coding/structural-subprocess-provider.test.ts +430 -0
  262. package/src/coding/structural-subprocess-provider.ts +355 -0
  263. package/src/compounding/engine.test.ts +90 -0
  264. package/src/compounding/engine.ts +25 -1
  265. package/src/contradiction/contradiction.test.ts +1 -1
  266. package/src/contradiction/resolution.ts +9 -1
  267. package/src/correction/correction-access-wiring.ts +21 -3
  268. package/src/correction/correction-contract.ts +7 -0
  269. package/src/correction/correction-planner.test.ts +231 -0
  270. package/src/correction/correction-planner.ts +73 -0
  271. package/src/correction/correction-service.ts +29 -0
  272. package/src/correction/correction-storage-integration.test.ts +2 -2
  273. package/src/dedup/semantic.test.ts +1 -1
  274. package/src/explicit-capture.ts +7 -4
  275. package/src/extraction-eventtime-wiring.test.ts +3 -3
  276. package/src/extraction-redaction-rules.test.ts +172 -0
  277. package/src/index.ts +29 -0
  278. package/src/maintenance/forget.test.ts +1 -1
  279. package/src/maintenance/memory-governance.test.ts +1 -1
  280. package/src/maintenance/pattern-reinforcement.test.ts +2 -2
  281. package/src/operator-toolkit.ts +4 -3
  282. package/src/orchestration/compression-guideline-coordinator.ts +449 -0
  283. package/src/orchestrator.ts +345 -464
  284. package/src/procedural/procedure-miner.test.ts +2 -2
  285. package/src/provenance-extraction.test.ts +382 -4
  286. package/src/provenance.ts +158 -22
  287. package/src/recall-handles-wiring.test.ts +1 -1
  288. package/src/search/factory.ts +4 -3
  289. package/src/semantic-rule-promotion.ts +1 -1
  290. package/src/source-attribution-roundtrip.test.ts +2 -2
  291. package/src/storage-contract/atomicity.test.ts +1 -1
  292. package/src/storage-contract/cache-coherence.test.ts +2 -2
  293. package/src/storage-contract/content-hash.test.ts +1 -1
  294. package/src/storage-contract/namespace-routing.test.ts +1 -1
  295. package/src/storage-contract/round-trip.test.ts +3 -3
  296. package/src/storage.ts +23 -2
  297. package/src/temporal-supersession.test.ts +12 -11
  298. package/src/temporal-validity.test.ts +11 -11
  299. package/src/wearables/cli.test.ts +30 -0
  300. package/src/wearables/cli.ts +3 -0
  301. package/src/wearables/memory-gen.test.ts +57 -1
  302. package/src/wearables/memory-gen.ts +31 -7
  303. package/src/wearables/pipeline.test.ts +40 -1
  304. package/src/wearables/pipeline.ts +4 -0
  305. package/src/wearables/service.test.ts +2 -2
  306. package/src/wearables/storage-io.test.ts +3 -3
  307. package/src/wearables/types.ts +7 -0
  308. package/dist/chunk-4NR2PDZ5.js.map +0 -1
  309. package/dist/chunk-4T3BMHKD.js.map +0 -1
  310. package/dist/chunk-7RLCCNHU.js.map +0 -1
  311. package/dist/chunk-AQLOP5F4.js.map +0 -1
  312. package/dist/chunk-EH5ED3C6.js.map +0 -1
  313. package/dist/chunk-HEDZUSCS.js.map +0 -1
  314. package/dist/chunk-M4AYVEWS.js.map +0 -1
  315. package/dist/chunk-UM5AWJNQ.js.map +0 -1
  316. package/dist/chunk-UTHQPSRC.js.map +0 -1
  317. package/dist/chunk-YKBHOBJK.js.map +0 -1
  318. /package/dist/{auto-sync-F3KCSQFJ.js.map → auto-sync-G3V5AN4C.js.map} +0 -0
  319. /package/dist/{chunk-4NOI577P.js.map → chunk-24ZLC5QN.js.map} +0 -0
  320. /package/dist/{chunk-DMQWV6SQ.js.map → chunk-5RZOFIDL.js.map} +0 -0
  321. /package/dist/{chunk-36SEDFAU.js.map → chunk-C5WN74HH.js.map} +0 -0
  322. /package/dist/{chunk-MLKWJOAJ.js.map → chunk-CGPHMNL4.js.map} +0 -0
  323. /package/dist/{chunk-EY6MDMHK.js.map → chunk-F6OEXJQQ.js.map} +0 -0
  324. /package/dist/{chunk-M4V2JVSZ.js.map → chunk-FFUCCKPN.js.map} +0 -0
  325. /package/dist/{chunk-O6YH5F47.js.map → chunk-FYWUNVFZ.js.map} +0 -0
  326. /package/dist/{chunk-37436GML.js.map → chunk-GEXI357K.js.map} +0 -0
  327. /package/dist/{chunk-YGIQORJ3.js.map → chunk-GGL34GCW.js.map} +0 -0
  328. /package/dist/{chunk-WAS3NV46.js.map → chunk-H3MB3F62.js.map} +0 -0
  329. /package/dist/{chunk-76NHR6ZL.js.map → chunk-HJWQJUKQ.js.map} +0 -0
  330. /package/dist/{chunk-Y4YD5TYW.js.map → chunk-IIBFCZA7.js.map} +0 -0
  331. /package/dist/{chunk-UVPL565C.js.map → chunk-IQOEW5YV.js.map} +0 -0
  332. /package/dist/{chunk-V4GNSDFH.js.map → chunk-KQYGNGON.js.map} +0 -0
  333. /package/dist/{chunk-CBWI22JN.js.map → chunk-KSJCXXOU.js.map} +0 -0
  334. /package/dist/{chunk-I5REUWLH.js.map → chunk-NIZQH6AU.js.map} +0 -0
  335. /package/dist/{chunk-4ESAR77N.js.map → chunk-NOU77OHX.js.map} +0 -0
  336. /package/dist/{chunk-BQCHEV7O.js.map → chunk-O4ZIBPIW.js.map} +0 -0
  337. /package/dist/{chunk-HOHVIVDR.js.map → chunk-OBJUKYDO.js.map} +0 -0
  338. /package/dist/{chunk-YOLZS7NY.js.map → chunk-OTQE66IX.js.map} +0 -0
  339. /package/dist/{chunk-C2PG7IEH.js.map → chunk-P3VJCTVE.js.map} +0 -0
  340. /package/dist/{chunk-HGNOCPZX.js.map → chunk-QZHF4BWN.js.map} +0 -0
  341. /package/dist/{chunk-ASKXIAHD.js.map → chunk-WDJCZQB4.js.map} +0 -0
  342. /package/dist/{chunk-JYLFZWUX.js.map → chunk-XYUFGHUW.js.map} +0 -0
  343. /package/dist/{chunk-3TSTGI6F.js.map → chunk-ZQUAVZNG.js.map} +0 -0
  344. /package/dist/{first-start-migration-22LPFHD5.js.map → first-start-migration-OOR57AYR.js.map} +0 -0
  345. /package/dist/{resolution-MN36NW5P.js.map → orchestration/compression-guideline-coordinator.js.map} +0 -0
@@ -100,6 +100,7 @@ import {
100
100
  import { MaintenanceScheduler } from "./orchestration/maintenance.js";
101
101
  import { TierMigrationCoordinator } from "./orchestration/tier-migration-coordinator.js";
102
102
  import { ExtractionQueueCoordinator } from "./orchestration/extraction-queue-coordinator.js";
103
+ import { CompressionGuidelineCoordinator } from "./orchestration/compression-guideline-coordinator.js";
103
104
  import {
104
105
  runLiveConnectorsOnce,
105
106
  type LiveConnectorsRunSummary,
@@ -224,10 +225,8 @@ import { parseMemoryActionEligibilityContext } from "./schemas.js";
224
225
  import { evaluateMemoryActionPolicy } from "./memory-action-policy.js";
225
226
  import {
226
227
  buildCompressionGuidelinesMarkdown as buildCompressionGuidelinesMarkdownV2,
227
- computeCompressionGuidelineCandidate,
228
- refineCompressionGuidelineCandidateSemantically,
229
- renderCompressionGuidelinesMarkdown,
230
228
  } from "./compression-optimizer.js";
229
+ export { formatCompressionGuidelinesForRecall } from "./orchestration/compression-guideline-coordinator.js";
231
230
  import { createRecallSectionMetricRecorder } from "./recall-qos.js";
232
231
  import { BoxBuilder, type BoxFrontmatter } from "./boxes.js";
233
232
  import { classifyMemoryKind } from "./himem.js";
@@ -270,9 +269,13 @@ import {
270
269
  resolveCapabilities,
271
270
  resolveGraphConstructionCapabilities,
272
271
  resolveMemoryLifecycleCapabilities,
272
+ resolveIndexingCapabilities,
273
+ resolveCreationMemoryCapabilities,
273
274
  type CapabilitySet,
274
275
  type GraphConstructionCapabilitySet,
275
276
  type MemoryLifecycleCapabilitySet,
277
+ type IndexingCapabilitySet,
278
+ type CreationMemoryCapabilitySet,
276
279
  } from "./capabilities.js";
277
280
  import { DEFAULT_TAXONOMY } from "./taxonomy/index.js";
278
281
  import {
@@ -1202,30 +1205,6 @@ export function buildCompressionGuidelinesMarkdown(
1202
1205
  return buildCompressionGuidelinesMarkdownV2(events, generatedAtIso);
1203
1206
  }
1204
1207
 
1205
- export function formatCompressionGuidelinesForRecall(
1206
- raw: string,
1207
- maxLines: number = 5,
1208
- ): string | null {
1209
- if (typeof raw !== "string" || raw.trim().length === 0) return null;
1210
- const sectionMatch = raw.match(
1211
- // End the section at `\n## ` or end-of-string. Plain $ (not \s*$): the
1212
- // \s* branch overlapped the lazy body and backtracked polynomially
1213
- // (CodeQL js/polynomial-redos). Captured lines are trimmed/filtered below,
1214
- // so trailing whitespace handling is unchanged.
1215
- /## Suggested Guidelines\s*\n([\s\S]*?)(?:\n##\s+|$)/i,
1216
- );
1217
- if (!sectionMatch) return null;
1218
-
1219
- const lines = sectionMatch[1]
1220
- .split("\n")
1221
- .map((line) => line.trim())
1222
- .filter((line) => line.startsWith("- "))
1223
- .slice(0, Math.max(1, Math.floor(maxLines)));
1224
- if (lines.length === 0) return null;
1225
-
1226
- return lines.join("\n");
1227
- }
1228
-
1229
1208
  export function filterRecallCandidates(
1230
1209
  candidates: QmdSearchResult[],
1231
1210
  options: {
@@ -1916,6 +1895,7 @@ export class Orchestrator {
1916
1895
  readonly handleHistory: RecallHandleHistoryStore;
1917
1896
  readonly tierMigrationStatus: TierMigrationStatusStore;
1918
1897
  readonly tierMigrationCoordinator: TierMigrationCoordinator;
1898
+ readonly compressionGuidelineCoordinator: CompressionGuidelineCoordinator;
1919
1899
  /**
1920
1900
  * In-memory X-ray snapshot from the most recent `recall()` call that
1921
1901
  * was invoked with `xrayCapture: true` (issue #570 PR 1). Scope is
@@ -3033,6 +3013,12 @@ export class Orchestrator {
3033
3013
  getCompounding: () => this.compounding,
3034
3014
  createColdStorage: (parentDir) => new StorageManager(parentDir),
3035
3015
  });
3016
+ this.compressionGuidelineCoordinator = new CompressionGuidelineCoordinator({
3017
+ config,
3018
+ getStorage: () => this.storage,
3019
+ fastChatCompletion: (messages, options) =>
3020
+ this.fastChatCompletion(messages, options),
3021
+ });
3036
3022
  this.sessionObserver = new SessionObserverState({
3037
3023
  memoryDir: config.memoryDir,
3038
3024
  debounceMs: config.sessionObserverDebounceMs ?? 120_000,
@@ -3474,6 +3460,31 @@ export class Orchestrator {
3474
3460
  // never break initialization (rule #13, #40).
3475
3461
  await this.namespaceCatalog.registerConfiguredNamespaces().catch(() => undefined);
3476
3462
  }
3463
+ // #1713 Item 2: recover stale `applying` correction plans left behind
3464
+ // by a process that died mid-apply. Best-effort — a failure here must
3465
+ // never block initialization (rule 13). Runs for every configured
3466
+ // namespace since correction plans can exist in any of them.
3467
+ try {
3468
+ // #1713 Item 2 + P2 (cursor): sweep ALL known namespaces — configured
3469
+ // + catalog-discovered — so stale applying plans in derived namespaces
3470
+ // (coding-scoped, session-derived) are also recovered.
3471
+ const correctionNamespaces = new Set(this.configuredNamespaceList());
3472
+ if (this.namespaceCatalog.enabled) {
3473
+ try {
3474
+ for (const rec of await this.namespaceCatalog.listNamespaces()) {
3475
+ correctionNamespaces.add(rec.namespace);
3476
+ }
3477
+ } catch { /* best-effort */ }
3478
+ }
3479
+ const recovered = await this.passiveCorrectionService().recoverStaleApplyingPlans(
3480
+ [...correctionNamespaces],
3481
+ );
3482
+ if (recovered > 0) {
3483
+ log.info(`correction: recovered ${recovered} stale applying plan(s) on startup`);
3484
+ }
3485
+ } catch (staleErr) {
3486
+ log.debug(`correction: stale-plan recovery skipped: ${staleErr instanceof Error ? staleErr.message : String(staleErr)}`);
3487
+ }
3477
3488
  await this.relevance.load();
3478
3489
  await this.negatives.load();
3479
3490
  await this.lastRecall.load();
@@ -3756,7 +3767,7 @@ export class Orchestrator {
3756
3767
  await Promise.all(cacheWarmups);
3757
3768
  if (signal.aborted) return;
3758
3769
 
3759
- if (this.config.conversationIndexEnabled && this.conversationIndexBackend) {
3770
+ if (resolveIndexingCapabilities(this.config).conversationIndex && this.conversationIndexBackend) {
3760
3771
  try {
3761
3772
  const init = await this.conversationIndexBackend.initialize();
3762
3773
  if (!init.enabled) {
@@ -4566,7 +4577,7 @@ export class Orchestrator {
4566
4577
  }
4567
4578
 
4568
4579
  // Write the canonical memory
4569
- const canonicalId = await targetStorage.writeMemory(
4580
+ const { id: canonicalId, tombstoneBlocked: canonicalBlocked } = await targetStorage.writeMemory(
4570
4581
  newest.frontmatter.category,
4571
4582
  canonicalContent,
4572
4583
  {
@@ -4583,6 +4594,12 @@ export class Orchestrator {
4583
4594
  derivedVia: operator,
4584
4595
  },
4585
4596
  );
4597
+ if (canonicalBlocked) {
4598
+ // #1645: canonical matched a tombstone (pending_review). Don't archive
4599
+ // the sources — consolidation must never delete the only active copy.
4600
+ log.info(`[semantic-consolidation] cluster in "${cluster.category}" tombstone-blocked — keeping ${cluster.memories.length} source(s) active (pending_review ${canonicalId})`);
4601
+ continue;
4602
+ }
4586
4603
  canonicalWriteCompleted = true;
4587
4604
 
4588
4605
  result.memoriesConsolidated++;
@@ -4609,7 +4626,7 @@ export class Orchestrator {
4609
4626
  try {
4610
4627
  await this.embeddingFallback.removeFromIndex(m.frontmatter.id);
4611
4628
  if (
4612
- this.config.queryAwareIndexingEnabled &&
4629
+ resolveIndexingCapabilities(this.config).queryAwareIndexing &&
4613
4630
  m.path &&
4614
4631
  m.frontmatter?.created
4615
4632
  ) {
@@ -5648,7 +5665,7 @@ export class Orchestrator {
5648
5665
  const lastUpdateAt =
5649
5666
  lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
5650
5667
 
5651
- if (!this.config.conversationIndexEnabled) {
5668
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5652
5669
  return {
5653
5670
  enabled: false,
5654
5671
  backend: this.config.conversationIndexBackend,
@@ -5688,7 +5705,7 @@ export class Orchestrator {
5688
5705
  const lastUpdateAt =
5689
5706
  lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
5690
5707
 
5691
- if (!this.config.conversationIndexEnabled) {
5708
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5692
5709
  return {
5693
5710
  enabled: false,
5694
5711
  backend: this.config.conversationIndexBackend,
@@ -5750,7 +5767,7 @@ export class Orchestrator {
5750
5767
  retryAfterMs?: number;
5751
5768
  embedded?: boolean;
5752
5769
  }> {
5753
- if (!this.config.conversationIndexEnabled) {
5770
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5754
5771
  return { chunks: 0, skipped: true, reason: "disabled", embedded: false };
5755
5772
  }
5756
5773
  const enforceMinInterval = opts?.enforceMinInterval !== false;
@@ -5811,7 +5828,7 @@ export class Orchestrator {
5811
5828
  embedded?: boolean;
5812
5829
  rebuilt?: boolean;
5813
5830
  }> {
5814
- if (!this.config.conversationIndexEnabled) {
5831
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5815
5832
  return {
5816
5833
  chunks: 0,
5817
5834
  skipped: true,
@@ -6416,7 +6433,7 @@ export class Orchestrator {
6416
6433
  prompt: string,
6417
6434
  recallNamespaces: string[],
6418
6435
  ): Promise<QueryAwarePrefilter> {
6419
- if (!this.config.queryAwareIndexingEnabled || !prompt.trim()) {
6436
+ if (!resolveIndexingCapabilities(this.config).queryAwareIndexing || !prompt.trim()) {
6420
6437
  return {
6421
6438
  candidatePaths: null,
6422
6439
  temporalFromDate: null,
@@ -9208,7 +9225,7 @@ export class Orchestrator {
9208
9225
  const workProductsPromise = (async (): Promise<string | null> => {
9209
9226
  const t0 = Date.now();
9210
9227
  if (
9211
- !this.config.creationMemoryEnabled ||
9228
+ !resolveCreationMemoryCapabilities(this.config).creationMemory ||
9212
9229
  !this.config.workProductRecallEnabled ||
9213
9230
  !this.isRecallSectionEnabled(
9214
9231
  "work-products",
@@ -9283,7 +9300,7 @@ export class Orchestrator {
9283
9300
  const queryAwarePrefilterPromise =
9284
9301
  (async (): Promise<QueryAwarePrefilter> => {
9285
9302
  const t0 = Date.now();
9286
- if (!this.config.queryAwareIndexingEnabled || !prompt.trim()) {
9303
+ if (!resolveIndexingCapabilities(this.config).queryAwareIndexing || !prompt.trim()) {
9287
9304
  recordRecallSectionMetric({
9288
9305
  section: "queryAware",
9289
9306
  priority: "enrichment",
@@ -10029,7 +10046,7 @@ export class Orchestrator {
10029
10046
  const conversationRecallPromise = (async (): Promise<string | null> => {
10030
10047
  const t0 = Date.now();
10031
10048
  if (
10032
- !this.config.conversationIndexEnabled ||
10049
+ !resolveIndexingCapabilities(this.config).conversationIndex ||
10033
10050
  queryPolicy.skipConversationRecall ||
10034
10051
  !this.isRecallSectionEnabled("conversation-recall", true)
10035
10052
  ) {
@@ -14173,6 +14190,10 @@ export class Orchestrator {
14173
14190
  sharedProfileLayer.writable &&
14174
14191
  sharedPromotionTarget?.authorized,
14175
14192
  );
14193
+ // #1713: hoist namespaces-enabled once for the scope-routing mirrors
14194
+ // (pre-judge + write-loop) so no new scattered config.*Enabled read is
14195
+ // introduced (ratchet scatteredConfigFlagReads; see #1523).
14196
+ const namespacesEnabled = this.config.namespacesEnabled;
14176
14197
  const profileAutoPromotionAllows = (
14177
14198
  category: string,
14178
14199
  confidence: number,
@@ -14305,7 +14326,7 @@ export class Orchestrator {
14305
14326
  }
14306
14327
  continue;
14307
14328
  }
14308
- const promotedId = await targetStorage.writeMemory(
14329
+ const targetPromotion = await targetStorage.writeMemory(
14309
14330
  options.category as any,
14310
14331
  citedContent,
14311
14332
  {
@@ -14331,7 +14352,11 @@ export class Orchestrator {
14331
14352
  ...(options.provenance ? { provenance: options.provenance } : {}),
14332
14353
  },
14333
14354
  );
14355
+ const promotedId = targetPromotion.id;
14356
+ // #1645: if the TARGET namespace's own tombstone blocked this promotion,
14357
+ // the row lands pending_review — do NOT supersede active target memories.
14334
14358
  if (
14359
+ !targetPromotion.tombstoneBlocked &&
14335
14360
  lifecycleCaps.temporalSupersession &&
14336
14361
  options.category === "fact" &&
14337
14362
  options.entityRef &&
@@ -14353,19 +14378,23 @@ export class Orchestrator {
14353
14378
  );
14354
14379
  }
14355
14380
  }
14356
- trackPersistedId(targetStorage, promotedId, { includeReturnedIds: false });
14357
- await this.indexPersistedMemory(targetStorage, promotedId);
14358
- trackBehaviorSignals(
14359
- targetStorage,
14360
- buildBehaviorSignalsForMemory({
14361
- memoryId: promotedId,
14362
- category: options.category as any,
14363
- content: options.content,
14364
- namespace: target.namespace,
14365
- confidence: options.confidence,
14366
- source: "extraction",
14367
- }),
14368
- );
14381
+ // #1645 TV6: a tombstone-blocked promotion is pending_review (no
14382
+ // active copy) — skip catalog/index/behavior like postWriteGuard.
14383
+ if (!targetPromotion.tombstoneBlocked) {
14384
+ trackPersistedId(targetStorage, promotedId, { includeReturnedIds: false });
14385
+ await this.indexPersistedMemory(targetStorage, promotedId);
14386
+ trackBehaviorSignals(
14387
+ targetStorage,
14388
+ buildBehaviorSignalsForMemory({
14389
+ memoryId: promotedId,
14390
+ category: options.category as any,
14391
+ content: options.content,
14392
+ namespace: target.namespace,
14393
+ confidence: options.confidence,
14394
+ source: "extraction",
14395
+ }),
14396
+ );
14397
+ }
14369
14398
  } catch (err) {
14370
14399
  log.warn(
14371
14400
  `persistExtraction: ${target.target} promotion failed open for ${options.sourceMemoryId}: ${err}`,
@@ -14624,7 +14653,7 @@ export class Orchestrator {
14624
14653
  return;
14625
14654
  }
14626
14655
  }
14627
- const promotedId = await sharedStorage.writeMemory(
14656
+ const sharedPromotion = await sharedStorage.writeMemory(
14628
14657
  options.category as any,
14629
14658
  citedContent,
14630
14659
  {
@@ -14651,6 +14680,9 @@ export class Orchestrator {
14651
14680
  ...(options.provenance ? { provenance: options.provenance } : {}),
14652
14681
  },
14653
14682
  );
14683
+ const promotedId = sharedPromotion.id;
14684
+ // #1645: if the shared namespace's own tombstone blocked this promotion,
14685
+ // leave the row pending_review but do NOT supersede active shared memories.
14654
14686
  // PR #402 Finding 3 fix: run temporal supersession against the shared
14655
14687
  // namespace after the promoted write lands so stale shared-namespace
14656
14688
  // copies of the same entity attribute are retired. Without this,
@@ -14658,6 +14690,7 @@ export class Orchestrator {
14658
14690
  // shared recall continues returning the stale state. Reuses the same
14659
14691
  // applyTemporalSupersession helper — no logic duplication.
14660
14692
  if (
14693
+ !sharedPromotion.tombstoneBlocked &&
14661
14694
  lifecycleCaps.temporalSupersession &&
14662
14695
  options.entityRef &&
14663
14696
  options.structuredAttributes &&
@@ -14687,21 +14720,24 @@ export class Orchestrator {
14687
14720
  // the same promotion pass. The hot-path source-namespace touch uses a
14688
14721
  // different storage dir, so this does not double-count the source.
14689
14722
  // Best-effort and failure-tolerant — it must never crash the promotion.
14690
- trackPersistedId(sharedStorage, promotedId, {
14691
- includeReturnedIds: false,
14692
- });
14693
- await this.indexPersistedMemory(sharedStorage, promotedId);
14694
- trackBehaviorSignals(
14695
- sharedStorage,
14696
- buildBehaviorSignalsForMemory({
14697
- memoryId: promotedId,
14698
- category: options.category as any,
14699
- content: options.content,
14700
- namespace: this.config.sharedNamespace,
14701
- confidence: options.confidence,
14702
- source: "extraction",
14703
- }),
14704
- );
14723
+ // #1645 TV6: same guard as the profile-target promotion above.
14724
+ if (!sharedPromotion.tombstoneBlocked) {
14725
+ trackPersistedId(sharedStorage, promotedId, {
14726
+ includeReturnedIds: false,
14727
+ });
14728
+ await this.indexPersistedMemory(sharedStorage, promotedId);
14729
+ trackBehaviorSignals(
14730
+ sharedStorage,
14731
+ buildBehaviorSignalsForMemory({
14732
+ memoryId: promotedId,
14733
+ category: options.category as any,
14734
+ content: options.content,
14735
+ namespace: this.config.sharedNamespace,
14736
+ confidence: options.confidence,
14737
+ source: "extraction",
14738
+ }),
14739
+ );
14740
+ }
14705
14741
  } catch (err) {
14706
14742
  log.warn(
14707
14743
  `persistExtraction: shared promotion failed open for ${options.sourceMemoryId}: ${err}`,
@@ -14928,6 +14964,10 @@ export class Orchestrator {
14928
14964
  // these pre-computed results so routing is evaluated exactly once per
14929
14965
  // fact (no duplicated logic).
14930
14966
  const preRoutedCategories: Array<string | undefined> = new Array(facts.length);
14967
+ // #1713 Item 1: collect routed target namespaces so the pre-judge redaction
14968
+ // filter can consult rules from cross-namespace targets, not just the source.
14969
+ // #1713 P2 (cursor): track per-fact routed namespace for per-fact pre-judge rules
14970
+ const preRoutedNamespaceByFact: Array<string | undefined> = new Array(facts.length);
14931
14971
  if (routeRules.length > 0) {
14932
14972
  for (let fi = 0; fi < facts.length; fi++) {
14933
14973
  const f = facts[fi];
@@ -14947,6 +14987,9 @@ export class Orchestrator {
14947
14987
  if (selected?.target.category) {
14948
14988
  preRoutedCategories[fi] = selected.target.category;
14949
14989
  }
14990
+ if (selected?.target.namespace) {
14991
+ preRoutedNamespaceByFact[fi] = selected.target.namespace;
14992
+ }
14950
14993
  } catch {
14951
14994
  // Fail-open: routing errors fall through to the extracted category.
14952
14995
  }
@@ -14969,10 +15012,18 @@ export class Orchestrator {
14969
15012
  // into the caller's buffer-retention decision.
14970
15013
  this.lastPersistExtractionDeferredCount = 0;
14971
15014
  if (lifecycleCaps.extractionJudge) {
14972
- // #1669 P1: pre-filter redacted facts from judge candidates so never-store
14973
- // content is not persisted as judge training data.
15015
+ // #1669 P1 + #1713 Item 1: pre-filter redacted facts from judge candidates
15016
+ // so never-store content is not persisted as judge training data.
15017
+ // Consult source + shared + routed target namespace rules so a
15018
+ // never-store pattern registered under a cross-namespace target is
15019
+ // caught at the batch pre-filter point, not just at the persist gate.
14974
15020
  let preJudgeRedactionRules: CompiledRedactionRule[] = [];
14975
- try { preJudgeRedactionRules = await redactionRulesFor(storage.dir); } catch { /* fail open */ }
15021
+ try {
15022
+ // #1713: base rules are source-only. Per-fact routed target rules
15023
+ // (including shared when a fact is routed there) are checked in the
15024
+ // fact loop, matching the write-time gate's per-fact scoping exactly.
15025
+ preJudgeRedactionRules = await redactionRulesFor(storage.dir);
15026
+ } catch { /* fail open */ }
14976
15027
  try {
14977
15028
  const judgeCandidates: JudgeCandidate[] = [];
14978
15029
  const candidateToFactIndex: number[] = [];
@@ -15013,10 +15064,42 @@ export class Orchestrator {
15013
15064
  ) {
15014
15065
  continue;
15015
15066
  }
15016
- if (preJudgeRedactionRules.length > 0) {
15067
+ // #1713 P2 (cursor): per-fact rules — load this fact's routed target
15068
+ // namespace rules FIRST, then check. This catches target-only rules
15069
+ // even when the base (source+shared) rules are empty (threads acc58c42
15070
+ // + PBJEe/PBKAj).
15071
+ let factRedactionRules = preJudgeRedactionRules;
15072
+ let factNs = preRoutedNamespaceByFact[fi];
15073
+ // #1713 (codex PRRT_kwDORJXyws6PBj5X): scope classification can route
15074
+ // a scope=global fact to the shared namespace independent of routing
15075
+ // rules. If so, the pre-judge filter must consult the shared
15076
+ // namespace's rules too, or a never-store pattern under shared is
15077
+ // missed at pre-judge and only caught at the write gate — letting the
15078
+ // content reach the extraction judge/training path. Mirror the write
15079
+ // loop's scope-routing conditions exactly.
15080
+ if (
15081
+ !factNs &&
15082
+ lifecycleCaps.extractionScopeClassification &&
15083
+ namespacesEnabled &&
15084
+ f.scope === "global" &&
15085
+ profileAllowsSharedWrites &&
15086
+ this.storageDirNamespace(storage.dir) !== this.config.sharedNamespace
15087
+ ) {
15088
+ factNs = this.config.sharedNamespace;
15089
+ }
15090
+ if (factNs) {
15091
+ try {
15092
+ const factDir = (await this.storageRouter.storageFor(factNs)).dir;
15093
+ if (factDir !== storage.dir) {
15094
+ const targetRules = await redactionRulesFor(factDir);
15095
+ if (targetRules.length) factRedactionRules = [...preJudgeRedactionRules, ...targetRules];
15096
+ }
15097
+ } catch { /* fail open */ }
15098
+ }
15099
+ if (factRedactionRules.length > 0) {
15017
15100
  const rc = f.content + (f.structuredAttributes ? " " + JSON.stringify(f.structuredAttributes) : "")
15018
15101
  + (f.procedureSteps ? " " + f.procedureSteps.map((s) => `${s.intent} ${s.expectedOutcome ?? ""} ${s.toolCall ? `${s.toolCall.kind} ${s.toolCall.signature}` : ""}`.trim()).join(" ") : "");
15019
- if (contentMatchesRedactionRules(rc, preJudgeRedactionRules)) continue;
15102
+ if (contentMatchesRedactionRules(rc, factRedactionRules)) continue;
15020
15103
  }
15021
15104
  judgeCandidates.push({
15022
15105
  text: f.content,
@@ -15246,7 +15329,7 @@ export class Orchestrator {
15246
15329
  // extractionScopeClassificationEnabled.
15247
15330
  if (
15248
15331
  lifecycleCaps.extractionScopeClassification &&
15249
- this.config.namespacesEnabled &&
15332
+ namespacesEnabled &&
15250
15333
  fact.scope === "global" &&
15251
15334
  !routedNamespaceExplicit
15252
15335
  ) {
@@ -15603,6 +15686,16 @@ export class Orchestrator {
15603
15686
  // `supersedes` is intentionally left unset to avoid retiring the old
15604
15687
  // memory without user confirmation).
15605
15688
  let contradictionDetected = false;
15689
+ // #1645: hoist the contradiction result so the deferred auto-resolve
15690
+ // (post-write, gated on tombstone status) can read its fields.
15691
+ let contradiction: {
15692
+ supersededId: string;
15693
+ confidence: number;
15694
+ reason: string;
15695
+ supersededPath: string;
15696
+ supersededCreated: string;
15697
+ supersededTags: string[];
15698
+ } | null | undefined;
15606
15699
 
15607
15700
  // Faithfulness gate (#1576, chatgpt P2): skip contradiction detection
15608
15701
  // for a pending_review fact — an unfaithful extraction in the review queue
@@ -15613,7 +15706,7 @@ export class Orchestrator {
15613
15706
  faithfulnessEnforceStatus !== "pending_review"
15614
15707
  ) {
15615
15708
  const targetNamespace = this.storageDirNamespace(targetStorage.dir);
15616
- const contradiction = await this.checkForContradiction(
15709
+ contradiction = await this.checkForContradiction(
15617
15710
  fact.content,
15618
15711
  writeCategory,
15619
15712
  targetNamespace,
@@ -15635,22 +15728,10 @@ export class Orchestrator {
15635
15728
  strength: contradiction.confidence,
15636
15729
  reason: contradiction.reason,
15637
15730
  });
15638
- // Deindex the superseded memory so stale paths don't remain in
15639
- // index_time.json / index_tags.json after the incremental update.
15640
- // Only applicable when auto-resolve is on and the old memory is
15641
- // actually being retired; skip when manual review is required.
15642
- if (
15643
- this.config.contradictionAutoResolve &&
15644
- this.config.queryAwareIndexingEnabled &&
15645
- contradiction.supersededPath
15646
- ) {
15647
- deindexMemory(
15648
- this.config.memoryDir,
15649
- contradiction.supersededPath,
15650
- contradiction.supersededCreated,
15651
- contradiction.supersededTags,
15652
- );
15653
- }
15731
+ // #1645: deindex + supersede are deferred to after writeMemory so the
15732
+ // caller can gate them on the new write's tombstone status. A
15733
+ // tombstone-blocked write (pending_review) must not deindex or retire
15734
+ // the existing active memory see the post-write guard below.
15654
15735
  }
15655
15736
  }
15656
15737
 
@@ -15765,7 +15846,7 @@ export class Orchestrator {
15765
15846
  ? stripCitationForTemplate(fact.content, citationTemplate)
15766
15847
  : fact.content;
15767
15848
  const citedChunkedContent = applyInlineCitation(rawChunkedContent);
15768
- const parentId = await targetStorage.writeMemory(
15849
+ const parentWrite = await targetStorage.writeMemory(
15769
15850
  writeCategory,
15770
15851
  citedChunkedContent,
15771
15852
  {
@@ -15792,6 +15873,20 @@ export class Orchestrator {
15792
15873
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
15793
15874
  },
15794
15875
  );
15876
+ const parentId = parentWrite.id;
15877
+ // #1645: surface the tombstone block and gate active post-write paths
15878
+ // (chunks, supersession, shared promotion, graph/artifact) like #1576.
15879
+ const tombstoneBlocked = parentWrite.tombstoneBlocked;
15880
+ const postWriteGuard =
15881
+ faithfulnessEnforceStatus === "pending_review" || tombstoneBlocked;
15882
+ // #1645: defer contradiction auto-resolve until tombstone status is
15883
+ // known (see applyDeferredContradictionResolve).
15884
+ await this.applyDeferredContradictionResolve(
15885
+ contradiction,
15886
+ targetStorage,
15887
+ parentId,
15888
+ postWriteGuard,
15889
+ );
15795
15890
  try {
15796
15891
  // Write individual chunks with parent reference
15797
15892
  for (const chunk of chunkResult.chunks) {
@@ -15839,7 +15934,13 @@ export class Orchestrator {
15839
15934
  // fact's verdict + enforce status so a pending_review fact
15840
15935
  // is not indexed as active through its chunks (chatgpt P2).
15841
15936
  ...(faithfulnessFm ? { faithfulness: faithfulnessFm } : {}),
15842
- ...(faithfulnessEnforceStatus ? { status: faithfulnessEnforceStatus } : {}),
15937
+ // #1645 (OchiE): inherit pending_review + blockedBy so no chunk lands active.
15938
+ ...(postWriteGuard
15939
+ ? { status: "pending_review" as const }
15940
+ : (faithfulnessEnforceStatus ? { status: faithfulnessEnforceStatus } : {})),
15941
+ ...(tombstoneBlocked && parentWrite.blockedBy
15942
+ ? { blockedBy: parentWrite.blockedBy }
15943
+ : {}),
15843
15944
  // Claim-level provenance (issue #1575 PR 2): mirror the
15844
15945
  // parent's spans onto each chunk so a chunk surfaced
15845
15946
  // independently (memory_get/x-ray on a chunk ID) preserves
@@ -15866,24 +15967,28 @@ export class Orchestrator {
15866
15967
  `chunked memory ${parentId} into ${chunkResult.chunks.length} chunks`,
15867
15968
  );
15868
15969
  trackPersistedId(targetStorage, parentId, {
15869
- pendingReview: faithfulnessEnforceStatus === "pending_review",
15970
+ pendingReview: postWriteGuard,
15870
15971
  });
15871
15972
  // #1576 (cursor Medium): keep pending_review ids out of threadEpisodeIdsForGraph — else later active facts build thread-predecessor edges to an unfaithful memory.
15872
15973
  if (
15873
- faithfulnessEnforceStatus !== "pending_review" &&
15974
+ !postWriteGuard &&
15874
15975
  threadEpisodeIdsForGraph &&
15875
15976
  !threadEpisodeIdsForGraph.includes(parentId)
15876
15977
  ) {
15877
15978
  threadEpisodeIdsForGraph.push(parentId);
15878
15979
  }
15879
- await this.indexPersistedMemory(targetStorage, parentId);
15980
+ // #1645: same gate as the non-chunked path — a blocked chunked parent
15981
+ // must not enter the embedding-fallback index (resurrection).
15982
+ if (!postWriteGuard) {
15983
+ await this.indexPersistedMemory(targetStorage, parentId);
15984
+ }
15880
15985
  // PR #402 Thread 1 fix: run source-namespace temporal supersession for
15881
15986
  // chunked writes, matching the non-chunked path. Without this the
15882
15987
  // source namespace retains stale facts that should have been superseded.
15883
15988
  // Faithfulness gate (#1576, cursor High): skip supersession for a
15884
15989
  // pending_review fact — an unfaithful extraction in the review queue
15885
15990
  // must NOT retire older active memories.
15886
- if (faithfulnessEnforceStatus !== "pending_review") {
15991
+ if (!postWriteGuard) {
15887
15992
  try {
15888
15993
  const supersessionEntityRef =
15889
15994
  typeof (fact as any).entityRef === "string"
@@ -15908,7 +16013,7 @@ export class Orchestrator {
15908
16013
  // Faithfulness gate (#1576, chatgpt P2): do not promote a
15909
16014
  // pending_review fact to shared/profile — it must enter the review
15910
16015
  // queue without active copies that bypass the gate.
15911
- if (faithfulnessEnforceStatus !== "pending_review") await promoteMemoryToShared({
16016
+ if (!postWriteGuard) await promoteMemoryToShared({
15912
16017
  sourceStorage: targetStorage,
15913
16018
  category: writeCategory,
15914
16019
  content: fact.content,
@@ -15943,7 +16048,13 @@ export class Orchestrator {
15943
16048
  hasCitationForTemplate(fact.content, citationTemplate)
15944
16049
  ? stripCitationForTemplate(fact.content, citationTemplate)
15945
16050
  : fact.content;
15946
- await this.addContentHashDedup(targetStorage, canonicalChunkedContent);
16051
+ // #1645: do NOT register a tombstone-blocked fact's content in the
16052
+ // dedup index — writeMemory already skipped it (rule 44). Re-adding
16053
+ // would let the next extraction dedup-skip the tombstone chokepoint
16054
+ // and silently ban the retired content (no pending_review row).
16055
+ if (!tombstoneBlocked) {
16056
+ await this.addContentHashDedup(targetStorage, canonicalChunkedContent);
16057
+ }
15947
16058
  } catch (err) {
15948
16059
  log.warn(
15949
16060
  `content-hash dedup registration failed for chunked memory ${parentId}: ${err}`,
@@ -15956,14 +16067,18 @@ export class Orchestrator {
15956
16067
  // into boxBuilder.onExtraction() or threading.processTurn(), which
15957
16068
  // only expect canonical parent memory IDs. Call indexPersistedMemory
15958
16069
  // directly for embedding-fallback sync of each chunk document.
15959
- await this.indexPersistedMemory(targetStorage, chunkId);
16070
+ // #1645: chunks inherit pending_review under postWriteGuard — don't
16071
+ // index them into the embedding fallback (resurrection).
16072
+ if (!postWriteGuard) {
16073
+ await this.indexPersistedMemory(targetStorage, chunkId);
16074
+ }
15960
16075
  }
15961
16076
  try {
15962
16077
  if (
15963
16078
  this.config.verbatimArtifactsEnabled &&
15964
16079
  this.config.verbatimArtifactCategories.includes(writeCategory) &&
15965
16080
  fact.confidence >= this.config.verbatimArtifactsMinConfidence &&
15966
- faithfulnessEnforceStatus !== "pending_review"
16081
+ !postWriteGuard
15967
16082
  ) {
15968
16083
  // Reuse citedChunkedContent so the artifact carries the same citation
15969
16084
  // timestamp as the parent memory write above (Fix #3 — duplicate-citation).
@@ -15978,7 +16093,7 @@ export class Orchestrator {
15978
16093
  });
15979
16094
  }
15980
16095
  // v8.2: graph edge building for chunked memories. #1576: skip pending_review.
15981
- if (graphCaps.multiGraphMemory && faithfulnessEnforceStatus !== "pending_review") {
16096
+ if (graphCaps.multiGraphMemory && !postWriteGuard) {
15982
16097
  try {
15983
16098
  const graphContext = await ensureGraphContext(targetStorage);
15984
16099
  const entityRef =
@@ -16080,7 +16195,7 @@ export class Orchestrator {
16080
16195
  ? buildProcedurePersistBody(fact.content, fact.procedureSteps)
16081
16196
  : fact.content;
16082
16197
  const citedFactContent = applyInlineCitation(rawPersistBody);
16083
- const memoryId = await targetStorage.writeMemory(
16198
+ const factWrite = await targetStorage.writeMemory(
16084
16199
  writeCategory,
16085
16200
  citedFactContent,
16086
16201
  {
@@ -16112,6 +16227,20 @@ export class Orchestrator {
16112
16227
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
16113
16228
  },
16114
16229
  );
16230
+ const memoryId = factWrite.id;
16231
+ // #1645: surface the tombstone block; gate active post-write paths like #1576
16232
+ // so a blocked fact creates no active shared copy / supersession / graph entry.
16233
+ const tombstoneBlocked = factWrite.tombstoneBlocked;
16234
+ const postWriteGuard =
16235
+ faithfulnessEnforceStatus === "pending_review" || tombstoneBlocked;
16236
+ // #1645: defer contradiction auto-resolve until tombstone status is
16237
+ // known (see applyDeferredContradictionResolve).
16238
+ await this.applyDeferredContradictionResolve(
16239
+ contradiction,
16240
+ targetStorage,
16241
+ memoryId,
16242
+ postWriteGuard,
16243
+ );
16115
16244
  if (routedRuleId) {
16116
16245
  log.debug(
16117
16246
  `routing applied for memory ${memoryId}: rule=${routedRuleId} category=${writeCategory} storage=${targetStorage.dir}`,
@@ -16122,7 +16251,7 @@ export class Orchestrator {
16122
16251
  // key that has a conflicting value. Faithfulness gate (#1576, cursor
16123
16252
  // High): skip for a pending_review fact — an unfaithful extraction in
16124
16253
  // the review queue must NOT retire older active memories.
16125
- if (faithfulnessEnforceStatus !== "pending_review") {
16254
+ if (!postWriteGuard) {
16126
16255
  try {
16127
16256
  const supersessionEntityRef =
16128
16257
  typeof (fact as any).entityRef === "string"
@@ -16154,19 +16283,25 @@ export class Orchestrator {
16154
16283
  }),
16155
16284
  );
16156
16285
  trackPersistedId(targetStorage, memoryId, {
16157
- pendingReview: faithfulnessEnforceStatus === "pending_review",
16286
+ pendingReview: postWriteGuard,
16158
16287
  });
16159
16288
  if (
16160
- faithfulnessEnforceStatus !== "pending_review" &&
16289
+ !postWriteGuard &&
16161
16290
  threadEpisodeIdsForGraph &&
16162
16291
  !threadEpisodeIdsForGraph.includes(memoryId)
16163
16292
  ) {
16164
16293
  threadEpisodeIdsForGraph.push(memoryId);
16165
16294
  }
16166
- await this.indexPersistedMemory(targetStorage, memoryId);
16295
+ // #1645: a tombstone-blocked / pending_review fact must NOT enter the
16296
+ // embedding-fallback index — otherwise embedding recall surfaces the
16297
+ // pending_review row (resurrection). Gate on postWriteGuard like the
16298
+ // surrounding supersession / promotion / graph paths.
16299
+ if (!postWriteGuard) {
16300
+ await this.indexPersistedMemory(targetStorage, memoryId);
16301
+ }
16167
16302
  // Faithfulness gate (#1576, chatgpt P2): skip promotion for a
16168
16303
  // pending_review fact so no active shared/profile copy bypasses the gate.
16169
- if (faithfulnessEnforceStatus !== "pending_review") await promoteMemoryToShared({
16304
+ if (!postWriteGuard) await promoteMemoryToShared({
16170
16305
  sourceStorage: targetStorage,
16171
16306
  category: writeCategory,
16172
16307
  content: fact.content,
@@ -16196,7 +16331,7 @@ export class Orchestrator {
16196
16331
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
16197
16332
  });
16198
16333
  // v8.2: graph edge building (fail-open). #1576: skip pending_review facts.
16199
- if (graphCaps.multiGraphMemory && faithfulnessEnforceStatus !== "pending_review") {
16334
+ if (graphCaps.multiGraphMemory && !postWriteGuard) {
16200
16335
  try {
16201
16336
  const graphContext = await ensureGraphContext(targetStorage);
16202
16337
  const entityRef =
@@ -16240,7 +16375,7 @@ export class Orchestrator {
16240
16375
  this.config.verbatimArtifactsEnabled &&
16241
16376
  this.config.verbatimArtifactCategories.includes(writeCategory) &&
16242
16377
  fact.confidence >= this.config.verbatimArtifactsMinConfidence &&
16243
- faithfulnessEnforceStatus !== "pending_review"
16378
+ !postWriteGuard
16244
16379
  ) {
16245
16380
  // Reuse citedFactContent so the artifact carries the same citation
16246
16381
  // timestamp as the memory write above (Fix #3 — duplicate-citation).
@@ -16267,7 +16402,11 @@ export class Orchestrator {
16267
16402
  writeCategory === "procedure"
16268
16403
  ? buildProcedurePersistBody(fact.content, fact.procedureSteps)
16269
16404
  : canonicalFactContent;
16270
- await this.addContentHashDedup(targetStorage, hashRegisterKey);
16405
+ // #1645: do NOT register a tombstone-blocked fact's content in the dedup
16406
+ // index (rule 44 defeat) — see chunked path comment.
16407
+ if (!tombstoneBlocked) {
16408
+ await this.addContentHashDedup(targetStorage, hashRegisterKey);
16409
+ }
16271
16410
  } catch (err) {
16272
16411
  log.warn(
16273
16412
  `content-hash dedup registration failed for memory ${memoryId}: ${err}`,
@@ -16617,7 +16756,7 @@ export class Orchestrator {
16617
16756
  // Enabling only parallelRetrievalEnabled without queryAwareIndexingEnabled would silently
16618
16757
  // produce an empty temporal index, leaving the temporal agent with no data to work from.
16619
16758
  if (
16620
- !this.config.queryAwareIndexingEnabled &&
16759
+ !resolveIndexingCapabilities(this.config).queryAwareIndexing &&
16621
16760
  !caps.parallelRetrieval
16622
16761
  )
16623
16762
  return;
@@ -16732,7 +16871,7 @@ export class Orchestrator {
16732
16871
 
16733
16872
  // Build a lookup map from the already-loaded corpus to avoid repeated
16734
16873
  // readAllMemories() scans inside getMemoryById for pre-action deindex reads.
16735
- const memoryLookup = this.config.queryAwareIndexingEnabled
16874
+ const memoryLookup = resolveIndexingCapabilities(this.config).queryAwareIndexing
16736
16875
  ? new Map(allMemories.map((m) => [m.frontmatter.id, m]))
16737
16876
  : null;
16738
16877
 
@@ -16740,7 +16879,7 @@ export class Orchestrator {
16740
16879
  switch (item.action) {
16741
16880
  case "INVALIDATE": {
16742
16881
  // Capture path/frontmatter before invalidation for index cleanup
16743
- const toInvalidate = this.config.queryAwareIndexingEnabled
16882
+ const toInvalidate = resolveIndexingCapabilities(this.config).queryAwareIndexing
16744
16883
  ? (memoryLookup?.get(item.existingId) ?? null)
16745
16884
  : null;
16746
16885
  if (await this.storage.invalidateMemory(item.existingId)) {
@@ -16788,7 +16927,7 @@ export class Orchestrator {
16788
16927
  // updateMemory() only changes content/updated/supersedes/lineage — path, created, and tags
16789
16928
  // are preserved, so the temporal/tag index entry for the survivor is already correct.
16790
16929
  // Capture before invalidation for index cleanup
16791
- const toMergeInvalidate = this.config.queryAwareIndexingEnabled
16930
+ const toMergeInvalidate = resolveIndexingCapabilities(this.config).queryAwareIndexing
16792
16931
  ? (memoryLookup?.get(item.mergeWith) ?? null)
16793
16932
  : null;
16794
16933
  if (await this.storage.invalidateMemory(item.mergeWith)) {
@@ -16874,7 +17013,7 @@ export class Orchestrator {
16874
17013
  if (deletedCommitments.length > 0) {
16875
17014
  memoryItemMutated = true;
16876
17015
  log.info(`cleaned ${deletedCommitments.length} expired commitments`);
16877
- if (this.config.queryAwareIndexingEnabled) {
17016
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing) {
16878
17017
  for (const m of deletedCommitments) {
16879
17018
  deindexMemory(
16880
17019
  this.config.memoryDir,
@@ -16887,9 +17026,9 @@ export class Orchestrator {
16887
17026
  }
16888
17027
 
16889
17028
  if (
16890
- this.config.creationMemoryEnabled &&
16891
- this.config.commitmentLedgerEnabled &&
16892
- this.config.commitmentLifecycleEnabled
17029
+ resolveCreationMemoryCapabilities(this.config).creationMemory &&
17030
+ resolveCreationMemoryCapabilities(this.config).commitmentLedger &&
17031
+ resolveCreationMemoryCapabilities(this.config).commitmentLifecycle
16893
17032
  ) {
16894
17033
  try {
16895
17034
  const lifecycle = await applyCommitmentLedgerLifecycle({
@@ -16917,7 +17056,7 @@ export class Orchestrator {
16917
17056
  if (deletedTTL.length > 0) {
16918
17057
  memoryItemMutated = true;
16919
17058
  log.info(`cleaned ${deletedTTL.length} TTL-expired memories`);
16920
- if (this.config.queryAwareIndexingEnabled) {
17059
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing) {
16921
17060
  for (const m of deletedTTL) {
16922
17061
  deindexMemory(
16923
17062
  this.config.memoryDir,
@@ -17195,347 +17334,26 @@ export class Orchestrator {
17195
17334
  async optimizeCompressionGuidelines(options?: {
17196
17335
  dryRun?: boolean;
17197
17336
  eventLimit?: number;
17198
- }): Promise<{
17199
- enabled: boolean;
17200
- dryRun: boolean;
17201
- eventCount: number;
17202
- previousGuidelineVersion: number | null;
17203
- nextGuidelineVersion: number;
17204
- changedRules: number;
17205
- semanticRefinementApplied: boolean;
17206
- persisted: boolean;
17207
- draftContentHash: string | null;
17208
- }> {
17209
- const dryRun = options?.dryRun === true;
17210
- const eventLimit =
17211
- typeof options?.eventLimit === "number"
17212
- ? Math.max(0, Math.floor(options.eventLimit))
17213
- : 500;
17214
-
17215
- const [activeState, draftState] = await Promise.all([
17216
- this.storage.readCompressionGuidelineOptimizerState(),
17217
- this.storage.readCompressionGuidelineDraftState().catch(() => null),
17218
- ]);
17219
- const previousState =
17220
- draftState &&
17221
- ((activeState?.guidelineVersion ?? 0) < draftState.guidelineVersion ||
17222
- (activeState?.guidelineVersion ?? 0) === draftState.guidelineVersion)
17223
- ? draftState
17224
- : activeState;
17225
-
17226
- if (!this.config.compressionGuidelineLearningEnabled) {
17227
- return {
17228
- enabled: false,
17229
- dryRun,
17230
- eventCount: 0,
17231
- previousGuidelineVersion: previousState?.guidelineVersion ?? null,
17232
- nextGuidelineVersion: previousState?.guidelineVersion ?? 0,
17233
- changedRules: 0,
17234
- semanticRefinementApplied: false,
17235
- persisted: false,
17236
- draftContentHash: null,
17237
- };
17238
- }
17239
-
17240
- let events = await this.storage.readMemoryActionEvents(eventLimit);
17241
- if (eventLimit > 0) {
17242
- let effectiveEvents = events.filter((event) => event.dryRun !== true);
17243
- let fetchLimit = eventLimit;
17244
- while (
17245
- effectiveEvents.length < eventLimit &&
17246
- events.length === fetchLimit
17247
- ) {
17248
- fetchLimit = Math.min(fetchLimit * 2, fetchLimit + 1000);
17249
- if (fetchLimit <= events.length) break;
17250
- events = await this.storage.readMemoryActionEvents(fetchLimit);
17251
- effectiveEvents = events.filter((event) => event.dryRun !== true);
17252
- }
17253
- events = effectiveEvents.slice(-eventLimit);
17254
- }
17255
- const generatedAt = new Date().toISOString();
17256
- const candidate = computeCompressionGuidelineCandidate(events, {
17257
- generatedAtIso: generatedAt,
17258
- previousState,
17259
- });
17260
- if (candidate.eventCounts.total === 0) {
17261
- return {
17262
- enabled: true,
17263
- dryRun,
17264
- eventCount: 0,
17265
- previousGuidelineVersion: previousState?.guidelineVersion ?? null,
17266
- nextGuidelineVersion: previousState?.guidelineVersion ?? 0,
17267
- changedRules: 0,
17268
- semanticRefinementApplied: false,
17269
- persisted: false,
17270
- draftContentHash: null,
17271
- };
17272
- }
17273
- const refinedCandidate =
17274
- await refineCompressionGuidelineCandidateSemantically(candidate, {
17275
- enabled: this.config.compressionGuidelineSemanticRefinementEnabled,
17276
- timeoutMs: this.config.compressionGuidelineSemanticTimeoutMs,
17277
- runRefinement: async (baseline) => {
17278
- const prompt = [
17279
- "You refine compression policy suggestions conservatively.",
17280
- "Return JSON only in this shape:",
17281
- '{"updates":[{"action":"summarize_node","delta":0.02,"confidence":"medium","note":"..."}]}',
17282
- "Constraints:",
17283
- "- Keep updates sparse and conservative.",
17284
- "- delta must stay between -0.15 and 0.15.",
17285
- "- Only include actions present in the input.",
17286
- "Input candidate:",
17287
- JSON.stringify(baseline),
17288
- ].join("\n");
17289
-
17290
- const response = await this.fastChatCompletion(
17291
- [
17292
- {
17293
- role: "system",
17294
- content: "Respond with strict JSON only. No markdown.",
17295
- },
17296
- { role: "user", content: prompt },
17297
- ],
17298
- {
17299
- temperature: 0.1,
17300
- maxTokens: 400,
17301
- operation: "compression_guideline_semantic_refinement",
17302
- priority: "background",
17303
- },
17304
- );
17305
-
17306
- return this.parseCompressionSemanticRefinement(
17307
- response?.content ?? "",
17308
- );
17309
- },
17310
- });
17311
-
17312
- const content = renderCompressionGuidelinesMarkdown(refinedCandidate);
17313
- const contentHash = createHash("sha256").update(content).digest("hex");
17314
- const semanticRefinementApplied =
17315
- JSON.stringify(refinedCandidate.ruleUpdates) !==
17316
- JSON.stringify(candidate.ruleUpdates);
17317
- const changedRules = refinedCandidate.ruleUpdates.filter(
17318
- (rule) => rule.delta !== 0,
17319
- ).length;
17320
-
17321
- if (!dryRun) {
17322
- await this.storage.writeCompressionGuidelineDraft(content);
17323
- await this.storage.writeCompressionGuidelineDraftState({
17324
- version: refinedCandidate.optimizerVersion,
17325
- updatedAt: refinedCandidate.generatedAt,
17326
- sourceWindow: refinedCandidate.sourceWindow,
17327
- eventCounts: refinedCandidate.eventCounts,
17328
- guidelineVersion: refinedCandidate.guidelineVersion,
17329
- contentHash,
17330
- activationState: "draft",
17331
- actionSummaries: refinedCandidate.actionSummaries,
17332
- ruleUpdates: refinedCandidate.ruleUpdates,
17333
- });
17334
- }
17335
-
17336
- return {
17337
- enabled: true,
17338
- dryRun,
17339
- eventCount: candidate.eventCounts.total,
17340
- previousGuidelineVersion: previousState?.guidelineVersion ?? null,
17341
- nextGuidelineVersion: refinedCandidate.guidelineVersion,
17342
- changedRules,
17343
- semanticRefinementApplied,
17344
- persisted: !dryRun,
17345
- draftContentHash: dryRun ? null : contentHash,
17346
- };
17337
+ }) {
17338
+ return this.compressionGuidelineCoordinator.optimizeCompressionGuidelines(options);
17347
17339
  }
17348
17340
 
17349
17341
  async activateCompressionGuidelineDraft(options?: {
17350
17342
  expectedContentHash?: string;
17351
17343
  expectedGuidelineVersion?: number;
17352
- }): Promise<{
17353
- enabled: boolean;
17354
- activated: boolean;
17355
- guidelineVersion: number | null;
17356
- reason?:
17357
- | "disabled"
17358
- | "missing_draft"
17359
- | "expected_revision_required"
17360
- | "content_hash_mismatch"
17361
- | "guideline_version_mismatch"
17362
- | "draft_changed";
17363
- }> {
17364
- if (!this.config.compressionGuidelineLearningEnabled) {
17365
- return {
17366
- enabled: false,
17367
- activated: false,
17368
- guidelineVersion: null,
17369
- reason: "disabled",
17370
- };
17371
- }
17372
-
17373
- const draftState = await this.storage.readCompressionGuidelineDraftState();
17374
- if (!draftState) {
17375
- return {
17376
- enabled: true,
17377
- activated: false,
17378
- guidelineVersion: null,
17379
- reason: "missing_draft",
17380
- };
17381
- }
17382
-
17383
- const expectedContentHash = options?.expectedContentHash?.trim();
17384
- const expectedGuidelineVersion = options?.expectedGuidelineVersion;
17385
-
17386
- if (
17387
- (!expectedContentHash || expectedContentHash.length === 0) &&
17388
- typeof expectedGuidelineVersion !== "number"
17389
- ) {
17390
- return {
17391
- enabled: true,
17392
- activated: false,
17393
- guidelineVersion: null,
17394
- reason: "expected_revision_required",
17395
- };
17396
- }
17397
-
17398
- if (expectedContentHash && draftState.contentHash !== expectedContentHash) {
17399
- return {
17400
- enabled: true,
17401
- activated: false,
17402
- guidelineVersion: null,
17403
- reason: "content_hash_mismatch",
17404
- };
17405
- }
17406
-
17407
- if (
17408
- typeof expectedGuidelineVersion === "number" &&
17409
- draftState.guidelineVersion !== expectedGuidelineVersion
17410
- ) {
17411
- return {
17412
- enabled: true,
17413
- activated: false,
17414
- guidelineVersion: null,
17415
- reason: "guideline_version_mismatch",
17416
- };
17417
- }
17418
-
17419
- const activated = await this.storage.activateCompressionGuidelineDraft({
17420
- ...(expectedContentHash ? { expectedContentHash } : {}),
17421
- ...(typeof expectedGuidelineVersion === "number"
17422
- ? { expectedGuidelineVersion }
17423
- : {}),
17424
- });
17425
- return {
17426
- enabled: true,
17427
- activated,
17428
- guidelineVersion: activated ? draftState.guidelineVersion : null,
17429
- ...(activated ? {} : { reason: "draft_changed" as const }),
17430
- };
17344
+ }) {
17345
+ return this.compressionGuidelineCoordinator.activateCompressionGuidelineDraft(options);
17431
17346
  }
17432
17347
 
17348
+ // Issue #1526 (seam 4): compression-guideline learning moved to
17349
+ // CompressionGuidelineCoordinator. Thin delegation keeps the
17350
+ // consolidation-loop hook + recall-section hook call sites stable.
17433
17351
  private async runCompressionGuidelineLearningPass(): Promise<void> {
17434
- if (!this.config.compressionGuidelineLearningEnabled) return;
17435
- try {
17436
- const result = await this.optimizeCompressionGuidelines({
17437
- dryRun: false,
17438
- eventLimit: 500,
17439
- });
17440
- log.info(
17441
- `compression guideline learning updated (${result.eventCount} events)`,
17442
- );
17443
- } catch (err) {
17444
- log.warn(`compression guideline learning failed (ignored): ${err}`);
17445
- }
17352
+ return this.compressionGuidelineCoordinator.runCompressionGuidelineLearningPass();
17446
17353
  }
17447
17354
 
17448
- private async buildCompressionGuidelineRecallSection(): Promise<
17449
- string | null
17450
- > {
17451
- if (!this.config.contextCompressionActionsEnabled) return null;
17452
- if (!this.config.compressionGuidelineLearningEnabled) return null;
17453
-
17454
- const state = await this.storage
17455
- .readCompressionGuidelineOptimizerState()
17456
- .catch(() => null);
17457
- if (!state || state.guidelineVersion <= 0) return null;
17458
-
17459
- const raw = await this.storage
17460
- .readCompressionGuidelines()
17461
- .catch(() => null);
17462
- const summary = raw ? formatCompressionGuidelinesForRecall(raw, 5) : null;
17463
- if (!summary) return null;
17464
-
17465
- return [
17466
- "## Active Compression Guidelines",
17467
- "",
17468
- `Guideline version: ${state.guidelineVersion}`,
17469
- `Updated: ${state.updatedAt}`,
17470
- "",
17471
- summary,
17472
- ].join("\n");
17473
- }
17474
-
17475
- private parseCompressionSemanticRefinement(raw: string): {
17476
- updates: Array<{
17477
- action: MemoryActionType;
17478
- delta?: number;
17479
- confidence?: "low" | "medium" | "high";
17480
- note?: string;
17481
- }>;
17482
- } | null {
17483
- if (typeof raw !== "string" || raw.trim().length === 0) return null;
17484
- const trimmed = raw.trim();
17485
- const start = trimmed.indexOf("{");
17486
- const end = trimmed.lastIndexOf("}");
17487
- if (start === -1 || end === -1 || end <= start) return null;
17488
-
17489
- try {
17490
- const parsed = JSON.parse(trimmed.slice(start, end + 1)) as {
17491
- updates?: Array<{
17492
- action?: unknown;
17493
- delta?: unknown;
17494
- confidence?: unknown;
17495
- note?: unknown;
17496
- }>;
17497
- };
17498
- if (!Array.isArray(parsed?.updates)) return null;
17499
-
17500
- const validActions = new Set<MemoryActionType>([
17501
- "store_episode",
17502
- "store_note",
17503
- "update_note",
17504
- "create_artifact",
17505
- "summarize_node",
17506
- "discard",
17507
- "link_graph",
17508
- ]);
17509
-
17510
- const updates = parsed.updates
17511
- .filter(
17512
- (item) =>
17513
- item &&
17514
- typeof item.action === "string" &&
17515
- validActions.has(item.action as MemoryActionType),
17516
- )
17517
- .map((item) => {
17518
- const confidence: "low" | "medium" | "high" | undefined =
17519
- item.confidence === "low" ||
17520
- item.confidence === "medium" ||
17521
- item.confidence === "high"
17522
- ? item.confidence
17523
- : undefined;
17524
- return {
17525
- action: item.action as MemoryActionType,
17526
- delta:
17527
- typeof item.delta === "number" && Number.isFinite(item.delta)
17528
- ? item.delta
17529
- : undefined,
17530
- confidence,
17531
- note: typeof item.note === "string" ? item.note : undefined,
17532
- };
17533
- });
17534
-
17535
- return { updates };
17536
- } catch {
17537
- return null;
17538
- }
17355
+ private async buildCompressionGuidelineRecallSection(): Promise<string | null> {
17356
+ return this.compressionGuidelineCoordinator.buildCompressionGuidelineRecallSection();
17539
17357
  }
17540
17358
 
17541
17359
  private actionOutcomePriorDelta(event: MemoryActionEvent): number {
@@ -17805,7 +17623,7 @@ export class Orchestrator {
17805
17623
  );
17806
17624
  await this.embeddingFallback.removeFromIndex(memory.frontmatter.id);
17807
17625
  if (
17808
- this.config.queryAwareIndexingEnabled &&
17626
+ resolveIndexingCapabilities(this.config).queryAwareIndexing &&
17809
17627
  memory.path &&
17810
17628
  memory.frontmatter?.created
17811
17629
  ) {
@@ -20190,7 +20008,7 @@ export class Orchestrator {
20190
20008
  );
20191
20009
  let temporalFromDate: string | null = null;
20192
20010
  let promptTags: string[] = [];
20193
- if (this.config.queryAwareIndexingEnabled && prompt) {
20011
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing && prompt) {
20194
20012
  if (isTemporalQuery(prompt)) {
20195
20013
  temporalFromDate = recencyWindowFromPrompt(prompt, now);
20196
20014
  }
@@ -20215,7 +20033,7 @@ export class Orchestrator {
20215
20033
  // Scope to result paths first so cross-namespace paths don't consume the cap.
20216
20034
  let temporalCandidates: Set<string> | null = null;
20217
20035
  let tagCandidates: Set<string> | null = null;
20218
- if (this.config.queryAwareIndexingEnabled && prompt) {
20036
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing && prompt) {
20219
20037
  const maxCandidates = this.config.queryAwareIndexingMaxCandidates;
20220
20038
  const capSet = (s: Set<string> | null): Set<string> | null => {
20221
20039
  if (!s) return null;
@@ -20324,7 +20142,7 @@ export class Orchestrator {
20324
20142
 
20325
20143
  // v8.1: Temporal + Tag index boost
20326
20144
  // Results that match the detected temporal window or tag query get a small additive boost.
20327
- if (this.config.queryAwareIndexingEnabled && r.path) {
20145
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing && r.path) {
20328
20146
  if (temporalCandidates?.has(r.path)) {
20329
20147
  score += applyUtilityRankingRuntimeDelta(
20330
20148
  0.08,
@@ -20561,15 +20379,13 @@ export class Orchestrator {
20561
20379
  }
20562
20380
 
20563
20381
  // The new fact is newer than the existing one. When auto-resolve is
20564
- // enabled, immediately retire the old memory. When disabled, leave the
20565
- // old memory active for manual review.
20566
- if (this.config.contradictionAutoResolve) {
20567
- await resultStorage.supersedeMemory(
20568
- existingMemory.frontmatter.id,
20569
- "pending-new", // Will be updated after the new memory is written
20570
- verification.reasoning,
20571
- );
20572
- }
20382
+ // enabled, the caller retires the old memory AFTER the new write lands
20383
+ // and its tombstone status is known (#1645: defer auto-resolve until
20384
+ // tombstone status is known — a tombstone-blocked new write must not
20385
+ // retire the only active copy, leaving neither memory active). When
20386
+ // disabled, the old memory stays active for manual review. The
20387
+ // contradiction info is returned in both cases so the caller can apply
20388
+ // the deferred retire post-write.
20573
20389
 
20574
20390
  // Return the contradiction info regardless of auto-resolve setting.
20575
20391
  // The caller uses this to set `contradictionDetected=true` which
@@ -20592,6 +20408,71 @@ export class Orchestrator {
20592
20408
  return null;
20593
20409
  }
20594
20410
 
20411
+ /**
20412
+ * #1645: Complete the deferred contradiction auto-resolve after writeMemory
20413
+ * returns and the new write's tombstone status is known. Retires the old
20414
+ * memory + deindexes it ONLY when the new write is genuinely active (not
20415
+ * tombstone-blocked / pending_review). A blocked write must not retire the
20416
+ * only active copy — deferring here closes the "contradictionAutoResolve
20417
+ * supersedes before tombstone status is known" defect class.
20418
+ */
20419
+ private async applyDeferredContradictionResolve(
20420
+ contradiction: {
20421
+ supersededId: string;
20422
+ reason: string;
20423
+ supersededPath: string;
20424
+ supersededCreated: string;
20425
+ supersededTags: string[];
20426
+ } | null | undefined,
20427
+ storage: StorageManager,
20428
+ newMemoryId: string,
20429
+ postWriteGuard: boolean,
20430
+ ): Promise<void> {
20431
+ // #1645 yG2: clear the pre-write `supersedes` link from a blocked row so
20432
+ // it doesn't claim to supersede a still-active memory (best-effort).
20433
+ if (postWriteGuard && contradiction && this.config.contradictionAutoResolve) {
20434
+ try {
20435
+ const blockedRow = await storage.getMemoryById(newMemoryId);
20436
+ if (blockedRow?.frontmatter?.supersedes) {
20437
+ await storage.writeMemoryFrontmatter(blockedRow, { supersedes: undefined });
20438
+ }
20439
+ } catch (err) {
20440
+ log.warn(
20441
+ `contradiction auto-resolve supersedes clear failed for blocked ${newMemoryId}: ${err}`,
20442
+ );
20443
+ }
20444
+ }
20445
+ if (
20446
+ !contradiction ||
20447
+ !this.config.contradictionAutoResolve ||
20448
+ postWriteGuard
20449
+ ) {
20450
+ return;
20451
+ }
20452
+ try {
20453
+ await storage.supersedeMemory(
20454
+ contradiction.supersededId,
20455
+ newMemoryId,
20456
+ contradiction.reason,
20457
+ );
20458
+ if (
20459
+ resolveIndexingCapabilities(this.config).queryAwareIndexing &&
20460
+ contradiction.supersededPath
20461
+ ) {
20462
+ deindexMemory(
20463
+ this.config.memoryDir,
20464
+ contradiction.supersededPath,
20465
+ contradiction.supersededCreated,
20466
+ contradiction.supersededTags,
20467
+ );
20468
+ }
20469
+ } catch (err) {
20470
+ log.warn(
20471
+ `contradiction auto-resolve supersede failed for ${contradiction.supersededId}: ${err}`,
20472
+ );
20473
+ }
20474
+ }
20475
+
20595
20476
  // ---------------------------------------------------------------------------
20596
20477
  // Memory Linking (Phase 3A)
20597
20478
  // ---------------------------------------------------------------------------