@remnic/core 9.3.728 → 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 (348) 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 +7 -6
  11. package/dist/access-operations.js +17 -17
  12. package/dist/access-schema.d.ts +4 -4
  13. package/dist/{access-service-Bgpjxo4f.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-QNEUZIAH.js → chunk-OUITHS6V.js} +381 -433
  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-BLME420h.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-DqPAz8ot.d.ts → orchestrator-B4KjaOtY.d.ts} +128 -34
  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 +32 -32
  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 +1 -1
  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 +22 -22
  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/orchestration/extraction-queue-coordinator.ts +175 -0
  284. package/src/orchestrator-extraction-queue.test.ts +26 -38
  285. package/src/orchestrator-flush.test.ts +10 -15
  286. package/src/orchestrator.ts +359 -542
  287. package/src/procedural/procedure-miner.test.ts +2 -2
  288. package/src/provenance-extraction.test.ts +382 -4
  289. package/src/provenance.ts +158 -22
  290. package/src/recall-handles-wiring.test.ts +1 -1
  291. package/src/search/factory.ts +4 -3
  292. package/src/semantic-rule-promotion.ts +1 -1
  293. package/src/source-attribution-roundtrip.test.ts +2 -2
  294. package/src/storage-contract/atomicity.test.ts +1 -1
  295. package/src/storage-contract/cache-coherence.test.ts +2 -2
  296. package/src/storage-contract/content-hash.test.ts +1 -1
  297. package/src/storage-contract/namespace-routing.test.ts +1 -1
  298. package/src/storage-contract/round-trip.test.ts +3 -3
  299. package/src/storage.ts +23 -2
  300. package/src/temporal-supersession.test.ts +12 -11
  301. package/src/temporal-validity.test.ts +11 -11
  302. package/src/wearables/cli.test.ts +30 -0
  303. package/src/wearables/cli.ts +3 -0
  304. package/src/wearables/memory-gen.test.ts +57 -1
  305. package/src/wearables/memory-gen.ts +31 -7
  306. package/src/wearables/pipeline.test.ts +40 -1
  307. package/src/wearables/pipeline.ts +4 -0
  308. package/src/wearables/service.test.ts +2 -2
  309. package/src/wearables/storage-io.test.ts +3 -3
  310. package/src/wearables/types.ts +7 -0
  311. package/dist/chunk-4NR2PDZ5.js.map +0 -1
  312. package/dist/chunk-4T3BMHKD.js.map +0 -1
  313. package/dist/chunk-7RLCCNHU.js.map +0 -1
  314. package/dist/chunk-AQLOP5F4.js.map +0 -1
  315. package/dist/chunk-EH5ED3C6.js.map +0 -1
  316. package/dist/chunk-M4AYVEWS.js.map +0 -1
  317. package/dist/chunk-QNEUZIAH.js.map +0 -1
  318. package/dist/chunk-UM5AWJNQ.js.map +0 -1
  319. package/dist/chunk-UTHQPSRC.js.map +0 -1
  320. package/dist/chunk-YKBHOBJK.js.map +0 -1
  321. /package/dist/{auto-sync-F3KCSQFJ.js.map → auto-sync-G3V5AN4C.js.map} +0 -0
  322. /package/dist/{chunk-4NOI577P.js.map → chunk-24ZLC5QN.js.map} +0 -0
  323. /package/dist/{chunk-DMQWV6SQ.js.map → chunk-5RZOFIDL.js.map} +0 -0
  324. /package/dist/{chunk-36SEDFAU.js.map → chunk-C5WN74HH.js.map} +0 -0
  325. /package/dist/{chunk-MLKWJOAJ.js.map → chunk-CGPHMNL4.js.map} +0 -0
  326. /package/dist/{chunk-EY6MDMHK.js.map → chunk-F6OEXJQQ.js.map} +0 -0
  327. /package/dist/{chunk-M4V2JVSZ.js.map → chunk-FFUCCKPN.js.map} +0 -0
  328. /package/dist/{chunk-O6YH5F47.js.map → chunk-FYWUNVFZ.js.map} +0 -0
  329. /package/dist/{chunk-37436GML.js.map → chunk-GEXI357K.js.map} +0 -0
  330. /package/dist/{chunk-YGIQORJ3.js.map → chunk-GGL34GCW.js.map} +0 -0
  331. /package/dist/{chunk-WAS3NV46.js.map → chunk-H3MB3F62.js.map} +0 -0
  332. /package/dist/{chunk-76NHR6ZL.js.map → chunk-HJWQJUKQ.js.map} +0 -0
  333. /package/dist/{chunk-Y4YD5TYW.js.map → chunk-IIBFCZA7.js.map} +0 -0
  334. /package/dist/{chunk-UVPL565C.js.map → chunk-IQOEW5YV.js.map} +0 -0
  335. /package/dist/{chunk-V4GNSDFH.js.map → chunk-KQYGNGON.js.map} +0 -0
  336. /package/dist/{chunk-CBWI22JN.js.map → chunk-KSJCXXOU.js.map} +0 -0
  337. /package/dist/{chunk-I5REUWLH.js.map → chunk-NIZQH6AU.js.map} +0 -0
  338. /package/dist/{chunk-4ESAR77N.js.map → chunk-NOU77OHX.js.map} +0 -0
  339. /package/dist/{chunk-BQCHEV7O.js.map → chunk-O4ZIBPIW.js.map} +0 -0
  340. /package/dist/{chunk-HOHVIVDR.js.map → chunk-OBJUKYDO.js.map} +0 -0
  341. /package/dist/{chunk-YOLZS7NY.js.map → chunk-OTQE66IX.js.map} +0 -0
  342. /package/dist/{chunk-C2PG7IEH.js.map → chunk-P3VJCTVE.js.map} +0 -0
  343. /package/dist/{chunk-HGNOCPZX.js.map → chunk-QZHF4BWN.js.map} +0 -0
  344. /package/dist/{chunk-ASKXIAHD.js.map → chunk-WDJCZQB4.js.map} +0 -0
  345. /package/dist/{chunk-JYLFZWUX.js.map → chunk-XYUFGHUW.js.map} +0 -0
  346. /package/dist/{chunk-3TSTGI6F.js.map → chunk-ZQUAVZNG.js.map} +0 -0
  347. /package/dist/{first-start-migration-22LPFHD5.js.map → first-start-migration-OOR57AYR.js.map} +0 -0
  348. /package/dist/{resolution-MN36NW5P.js.map → orchestration/compression-guideline-coordinator.js.map} +0 -0
@@ -99,6 +99,8 @@ import {
99
99
  } from "./fallback-llm.js";
100
100
  import { MaintenanceScheduler } from "./orchestration/maintenance.js";
101
101
  import { TierMigrationCoordinator } from "./orchestration/tier-migration-coordinator.js";
102
+ import { ExtractionQueueCoordinator } from "./orchestration/extraction-queue-coordinator.js";
103
+ import { CompressionGuidelineCoordinator } from "./orchestration/compression-guideline-coordinator.js";
102
104
  import {
103
105
  runLiveConnectorsOnce,
104
106
  type LiveConnectorsRunSummary,
@@ -171,7 +173,6 @@ import {
171
173
  import { SessionObserverState } from "./session-observer-state.js";
172
174
  import {
173
175
  abortError as sharedAbortError,
174
- isAbortError,
175
176
  throwIfAborted as sharedThrowIfAborted,
176
177
  } from "./abort-error.js";
177
178
  import { CODEX_THREAD_KEY_PREFIX } from "./thread-key.js";
@@ -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
@@ -2021,10 +2001,14 @@ export class Orchestrator {
2021
2001
  suppressedReasonCounts: Record<string, number>;
2022
2002
  } = { detected: 0, queued: 0, autoApplied: 0, suppressedReasonCounts: {} };
2023
2003
 
2024
- // Background serial queue for extractions (agent_end optimization)
2025
- // Queue stores promises that resolve when extraction should run
2026
- private extractionQueue: Array<() => Promise<void>> = [];
2027
- private queueProcessing = false;
2004
+ /**
2005
+ * Background serial extraction queue coordinator (issue #1526 moved
2006
+ * from inline `extractionQueue`/`queueProcessing` fields). Owns queue
2007
+ * state + scheduling + the serial drain + failure classification; the
2008
+ * orchestrator builds each task closure in `queueBufferedExtraction` and
2009
+ * hands it to `enqueue`.
2010
+ */
2011
+ readonly extractionQueueCoordinator: ExtractionQueueCoordinator;
2028
2012
  private heartbeatObserverChains = new Map<string, Promise<void>>();
2029
2013
  private recentExtractionFingerprints = new Map<string, number>();
2030
2014
  private readonly consolidationObservers = new Set<
@@ -2991,6 +2975,8 @@ export class Orchestrator {
2991
2975
  namespaceSearchRouter: this.namespaceSearchRouter,
2992
2976
  namespaceCatalog: this.namespaceCatalog,
2993
2977
  });
2978
+ // Issue #1526: background extraction queue lives on its own coordinator.
2979
+ this.extractionQueueCoordinator = new ExtractionQueueCoordinator();
2994
2980
  const conversationIndexRuntime = createConversationIndexRuntime(config, {
2995
2981
  getQmd: () => this.conversationQmd,
2996
2982
  getFaiss: () => this.conversationFaiss,
@@ -3027,6 +3013,12 @@ export class Orchestrator {
3027
3013
  getCompounding: () => this.compounding,
3028
3014
  createColdStorage: (parentDir) => new StorageManager(parentDir),
3029
3015
  });
3016
+ this.compressionGuidelineCoordinator = new CompressionGuidelineCoordinator({
3017
+ config,
3018
+ getStorage: () => this.storage,
3019
+ fastChatCompletion: (messages, options) =>
3020
+ this.fastChatCompletion(messages, options),
3021
+ });
3030
3022
  this.sessionObserver = new SessionObserverState({
3031
3023
  memoryDir: config.memoryDir,
3032
3024
  debounceMs: config.sessionObserverDebounceMs ?? 120_000,
@@ -3468,6 +3460,31 @@ export class Orchestrator {
3468
3460
  // never break initialization (rule #13, #40).
3469
3461
  await this.namespaceCatalog.registerConfiguredNamespaces().catch(() => undefined);
3470
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
+ }
3471
3488
  await this.relevance.load();
3472
3489
  await this.negatives.load();
3473
3490
  await this.lastRecall.load();
@@ -3750,7 +3767,7 @@ export class Orchestrator {
3750
3767
  await Promise.all(cacheWarmups);
3751
3768
  if (signal.aborted) return;
3752
3769
 
3753
- if (this.config.conversationIndexEnabled && this.conversationIndexBackend) {
3770
+ if (resolveIndexingCapabilities(this.config).conversationIndex && this.conversationIndexBackend) {
3754
3771
  try {
3755
3772
  const init = await this.conversationIndexBackend.initialize();
3756
3773
  if (!init.enabled) {
@@ -4560,7 +4577,7 @@ export class Orchestrator {
4560
4577
  }
4561
4578
 
4562
4579
  // Write the canonical memory
4563
- const canonicalId = await targetStorage.writeMemory(
4580
+ const { id: canonicalId, tombstoneBlocked: canonicalBlocked } = await targetStorage.writeMemory(
4564
4581
  newest.frontmatter.category,
4565
4582
  canonicalContent,
4566
4583
  {
@@ -4577,6 +4594,12 @@ export class Orchestrator {
4577
4594
  derivedVia: operator,
4578
4595
  },
4579
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
+ }
4580
4603
  canonicalWriteCompleted = true;
4581
4604
 
4582
4605
  result.memoriesConsolidated++;
@@ -4603,7 +4626,7 @@ export class Orchestrator {
4603
4626
  try {
4604
4627
  await this.embeddingFallback.removeFromIndex(m.frontmatter.id);
4605
4628
  if (
4606
- this.config.queryAwareIndexingEnabled &&
4629
+ resolveIndexingCapabilities(this.config).queryAwareIndexing &&
4607
4630
  m.path &&
4608
4631
  m.frontmatter?.created
4609
4632
  ) {
@@ -4720,15 +4743,8 @@ export class Orchestrator {
4720
4743
  }
4721
4744
 
4722
4745
  async waitForExtractionIdle(timeoutMs: number = 60_000): Promise<boolean> {
4723
- const started = Date.now();
4724
- while (this.queueProcessing || this.extractionQueue.length > 0) {
4725
- if (Date.now() - started > timeoutMs) {
4726
- log.warn(`waitForExtractionIdle timed out after ${timeoutMs}ms`);
4727
- return false;
4728
- }
4729
- await new Promise((resolve) => setTimeout(resolve, 50));
4730
- }
4731
- return true;
4746
+ // Issue #1526: queue state + idle wait moved to ExtractionQueueCoordinator.
4747
+ return this.extractionQueueCoordinator.waitForIdle(timeoutMs);
4732
4748
  }
4733
4749
 
4734
4750
  async waitForConsolidationIdle(timeoutMs: number = 60_000): Promise<boolean> {
@@ -5649,7 +5665,7 @@ export class Orchestrator {
5649
5665
  const lastUpdateAt =
5650
5666
  lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
5651
5667
 
5652
- if (!this.config.conversationIndexEnabled) {
5668
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5653
5669
  return {
5654
5670
  enabled: false,
5655
5671
  backend: this.config.conversationIndexBackend,
@@ -5689,7 +5705,7 @@ export class Orchestrator {
5689
5705
  const lastUpdateAt =
5690
5706
  lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
5691
5707
 
5692
- if (!this.config.conversationIndexEnabled) {
5708
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5693
5709
  return {
5694
5710
  enabled: false,
5695
5711
  backend: this.config.conversationIndexBackend,
@@ -5751,7 +5767,7 @@ export class Orchestrator {
5751
5767
  retryAfterMs?: number;
5752
5768
  embedded?: boolean;
5753
5769
  }> {
5754
- if (!this.config.conversationIndexEnabled) {
5770
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5755
5771
  return { chunks: 0, skipped: true, reason: "disabled", embedded: false };
5756
5772
  }
5757
5773
  const enforceMinInterval = opts?.enforceMinInterval !== false;
@@ -5812,7 +5828,7 @@ export class Orchestrator {
5812
5828
  embedded?: boolean;
5813
5829
  rebuilt?: boolean;
5814
5830
  }> {
5815
- if (!this.config.conversationIndexEnabled) {
5831
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
5816
5832
  return {
5817
5833
  chunks: 0,
5818
5834
  skipped: true,
@@ -6417,7 +6433,7 @@ export class Orchestrator {
6417
6433
  prompt: string,
6418
6434
  recallNamespaces: string[],
6419
6435
  ): Promise<QueryAwarePrefilter> {
6420
- if (!this.config.queryAwareIndexingEnabled || !prompt.trim()) {
6436
+ if (!resolveIndexingCapabilities(this.config).queryAwareIndexing || !prompt.trim()) {
6421
6437
  return {
6422
6438
  candidatePaths: null,
6423
6439
  temporalFromDate: null,
@@ -9209,7 +9225,7 @@ export class Orchestrator {
9209
9225
  const workProductsPromise = (async (): Promise<string | null> => {
9210
9226
  const t0 = Date.now();
9211
9227
  if (
9212
- !this.config.creationMemoryEnabled ||
9228
+ !resolveCreationMemoryCapabilities(this.config).creationMemory ||
9213
9229
  !this.config.workProductRecallEnabled ||
9214
9230
  !this.isRecallSectionEnabled(
9215
9231
  "work-products",
@@ -9284,7 +9300,7 @@ export class Orchestrator {
9284
9300
  const queryAwarePrefilterPromise =
9285
9301
  (async (): Promise<QueryAwarePrefilter> => {
9286
9302
  const t0 = Date.now();
9287
- if (!this.config.queryAwareIndexingEnabled || !prompt.trim()) {
9303
+ if (!resolveIndexingCapabilities(this.config).queryAwareIndexing || !prompt.trim()) {
9288
9304
  recordRecallSectionMetric({
9289
9305
  section: "queryAware",
9290
9306
  priority: "enrichment",
@@ -10030,7 +10046,7 @@ export class Orchestrator {
10030
10046
  const conversationRecallPromise = (async (): Promise<string | null> => {
10031
10047
  const t0 = Date.now();
10032
10048
  if (
10033
- !this.config.conversationIndexEnabled ||
10049
+ !resolveIndexingCapabilities(this.config).conversationIndex ||
10034
10050
  queryPolicy.skipConversationRecall ||
10035
10051
  !this.isRecallSectionEnabled("conversation-recall", true)
10036
10052
  ) {
@@ -13165,7 +13181,7 @@ export class Orchestrator {
13165
13181
  }, remainingMs);
13166
13182
  }
13167
13183
 
13168
- this.extractionQueue.push(async () => {
13184
+ this.extractionQueueCoordinator.enqueue(async () => {
13169
13185
  if (settled) return;
13170
13186
  if (
13171
13187
  typeof extractionDeadlineMs === "number" &&
@@ -13196,13 +13212,6 @@ export class Orchestrator {
13196
13212
  }
13197
13213
  });
13198
13214
 
13199
- if (!this.queueProcessing) {
13200
- this.queueProcessing = true;
13201
- this.processQueue().catch((err) => {
13202
- this.logExtractionQueueFailure(err, "processor");
13203
- this.queueProcessing = false;
13204
- });
13205
- }
13206
13215
  log.debug(`queued extraction from ${reason}`);
13207
13216
  }
13208
13217
 
@@ -13270,62 +13279,6 @@ export class Orchestrator {
13270
13279
  return true;
13271
13280
  }
13272
13281
 
13273
- /**
13274
- * Background serial queue processor.
13275
- * Processes extractions one at a time to avoid race conditions.
13276
- * Called automatically when items are queued.
13277
- */
13278
- private async processQueue(): Promise<void> {
13279
- while (this.extractionQueue.length > 0) {
13280
- const task = this.extractionQueue.shift();
13281
- if (task) {
13282
- try {
13283
- await task();
13284
- } catch (err) {
13285
- this.logExtractionQueueFailure(err, "task");
13286
- }
13287
- }
13288
- }
13289
-
13290
- this.queueProcessing = false;
13291
- }
13292
-
13293
- /**
13294
- * Classify + log a failure from either the per-task catch inside
13295
- * `processQueue()` or the outer `processQueue().catch(...)` in
13296
- * `queueBufferedExtraction()`. Issue #549: `throwIfRecallAborted`
13297
- * (used throughout `runExtraction`) raises an Error whose `name` is
13298
- * `"AbortError"`. That path fires when `before_reset` aborts a
13299
- * queued task to avoid duplicate extraction — it is intentional
13300
- * cancellation, not a failure. Downgrading the log to debug
13301
- * prevents spurious `error`-level lines that routinely appear
13302
- * right next to a successful `persisted: N facts, M entities` log
13303
- * and that confuse operators into thinking extraction is broken.
13304
- * Genuine extraction failures (network, parse, I/O) still log at
13305
- * `error`.
13306
- *
13307
- * Source differentiates the two call sites so the log message
13308
- * names the right layer (`task` vs `processor`).
13309
- */
13310
- private logExtractionQueueFailure(
13311
- err: unknown,
13312
- source: "task" | "processor",
13313
- ): void {
13314
- const aborted =
13315
- source === "task"
13316
- ? "background extraction task aborted (session transition)"
13317
- : "background extraction queue processor aborted (session transition)";
13318
- const failed =
13319
- source === "task"
13320
- ? "background extraction task failed"
13321
- : "background extraction queue processor failed";
13322
- if (isAbortError(err)) {
13323
- log.debug(aborted);
13324
- } else {
13325
- log.error(failed, err);
13326
- }
13327
- }
13328
-
13329
13282
  /**
13330
13283
  * Passive correction capture (issue #1581) — detects corrections expressed
13331
13284
  * passively in conversation turns and routes them to the Correction Contract
@@ -14237,6 +14190,10 @@ export class Orchestrator {
14237
14190
  sharedProfileLayer.writable &&
14238
14191
  sharedPromotionTarget?.authorized,
14239
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;
14240
14197
  const profileAutoPromotionAllows = (
14241
14198
  category: string,
14242
14199
  confidence: number,
@@ -14369,7 +14326,7 @@ export class Orchestrator {
14369
14326
  }
14370
14327
  continue;
14371
14328
  }
14372
- const promotedId = await targetStorage.writeMemory(
14329
+ const targetPromotion = await targetStorage.writeMemory(
14373
14330
  options.category as any,
14374
14331
  citedContent,
14375
14332
  {
@@ -14395,7 +14352,11 @@ export class Orchestrator {
14395
14352
  ...(options.provenance ? { provenance: options.provenance } : {}),
14396
14353
  },
14397
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.
14398
14358
  if (
14359
+ !targetPromotion.tombstoneBlocked &&
14399
14360
  lifecycleCaps.temporalSupersession &&
14400
14361
  options.category === "fact" &&
14401
14362
  options.entityRef &&
@@ -14417,19 +14378,23 @@ export class Orchestrator {
14417
14378
  );
14418
14379
  }
14419
14380
  }
14420
- trackPersistedId(targetStorage, promotedId, { includeReturnedIds: false });
14421
- await this.indexPersistedMemory(targetStorage, promotedId);
14422
- trackBehaviorSignals(
14423
- targetStorage,
14424
- buildBehaviorSignalsForMemory({
14425
- memoryId: promotedId,
14426
- category: options.category as any,
14427
- content: options.content,
14428
- namespace: target.namespace,
14429
- confidence: options.confidence,
14430
- source: "extraction",
14431
- }),
14432
- );
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
+ }
14433
14398
  } catch (err) {
14434
14399
  log.warn(
14435
14400
  `persistExtraction: ${target.target} promotion failed open for ${options.sourceMemoryId}: ${err}`,
@@ -14688,7 +14653,7 @@ export class Orchestrator {
14688
14653
  return;
14689
14654
  }
14690
14655
  }
14691
- const promotedId = await sharedStorage.writeMemory(
14656
+ const sharedPromotion = await sharedStorage.writeMemory(
14692
14657
  options.category as any,
14693
14658
  citedContent,
14694
14659
  {
@@ -14715,6 +14680,9 @@ export class Orchestrator {
14715
14680
  ...(options.provenance ? { provenance: options.provenance } : {}),
14716
14681
  },
14717
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.
14718
14686
  // PR #402 Finding 3 fix: run temporal supersession against the shared
14719
14687
  // namespace after the promoted write lands so stale shared-namespace
14720
14688
  // copies of the same entity attribute are retired. Without this,
@@ -14722,6 +14690,7 @@ export class Orchestrator {
14722
14690
  // shared recall continues returning the stale state. Reuses the same
14723
14691
  // applyTemporalSupersession helper — no logic duplication.
14724
14692
  if (
14693
+ !sharedPromotion.tombstoneBlocked &&
14725
14694
  lifecycleCaps.temporalSupersession &&
14726
14695
  options.entityRef &&
14727
14696
  options.structuredAttributes &&
@@ -14751,21 +14720,24 @@ export class Orchestrator {
14751
14720
  // the same promotion pass. The hot-path source-namespace touch uses a
14752
14721
  // different storage dir, so this does not double-count the source.
14753
14722
  // Best-effort and failure-tolerant — it must never crash the promotion.
14754
- trackPersistedId(sharedStorage, promotedId, {
14755
- includeReturnedIds: false,
14756
- });
14757
- await this.indexPersistedMemory(sharedStorage, promotedId);
14758
- trackBehaviorSignals(
14759
- sharedStorage,
14760
- buildBehaviorSignalsForMemory({
14761
- memoryId: promotedId,
14762
- category: options.category as any,
14763
- content: options.content,
14764
- namespace: this.config.sharedNamespace,
14765
- confidence: options.confidence,
14766
- source: "extraction",
14767
- }),
14768
- );
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
+ }
14769
14741
  } catch (err) {
14770
14742
  log.warn(
14771
14743
  `persistExtraction: shared promotion failed open for ${options.sourceMemoryId}: ${err}`,
@@ -14992,6 +14964,10 @@ export class Orchestrator {
14992
14964
  // these pre-computed results so routing is evaluated exactly once per
14993
14965
  // fact (no duplicated logic).
14994
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);
14995
14971
  if (routeRules.length > 0) {
14996
14972
  for (let fi = 0; fi < facts.length; fi++) {
14997
14973
  const f = facts[fi];
@@ -15011,6 +14987,9 @@ export class Orchestrator {
15011
14987
  if (selected?.target.category) {
15012
14988
  preRoutedCategories[fi] = selected.target.category;
15013
14989
  }
14990
+ if (selected?.target.namespace) {
14991
+ preRoutedNamespaceByFact[fi] = selected.target.namespace;
14992
+ }
15014
14993
  } catch {
15015
14994
  // Fail-open: routing errors fall through to the extracted category.
15016
14995
  }
@@ -15033,10 +15012,18 @@ export class Orchestrator {
15033
15012
  // into the caller's buffer-retention decision.
15034
15013
  this.lastPersistExtractionDeferredCount = 0;
15035
15014
  if (lifecycleCaps.extractionJudge) {
15036
- // #1669 P1: pre-filter redacted facts from judge candidates so never-store
15037
- // 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.
15038
15020
  let preJudgeRedactionRules: CompiledRedactionRule[] = [];
15039
- 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 */ }
15040
15027
  try {
15041
15028
  const judgeCandidates: JudgeCandidate[] = [];
15042
15029
  const candidateToFactIndex: number[] = [];
@@ -15077,10 +15064,42 @@ export class Orchestrator {
15077
15064
  ) {
15078
15065
  continue;
15079
15066
  }
15080
- 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) {
15081
15100
  const rc = f.content + (f.structuredAttributes ? " " + JSON.stringify(f.structuredAttributes) : "")
15082
15101
  + (f.procedureSteps ? " " + f.procedureSteps.map((s) => `${s.intent} ${s.expectedOutcome ?? ""} ${s.toolCall ? `${s.toolCall.kind} ${s.toolCall.signature}` : ""}`.trim()).join(" ") : "");
15083
- if (contentMatchesRedactionRules(rc, preJudgeRedactionRules)) continue;
15102
+ if (contentMatchesRedactionRules(rc, factRedactionRules)) continue;
15084
15103
  }
15085
15104
  judgeCandidates.push({
15086
15105
  text: f.content,
@@ -15310,7 +15329,7 @@ export class Orchestrator {
15310
15329
  // extractionScopeClassificationEnabled.
15311
15330
  if (
15312
15331
  lifecycleCaps.extractionScopeClassification &&
15313
- this.config.namespacesEnabled &&
15332
+ namespacesEnabled &&
15314
15333
  fact.scope === "global" &&
15315
15334
  !routedNamespaceExplicit
15316
15335
  ) {
@@ -15667,6 +15686,16 @@ export class Orchestrator {
15667
15686
  // `supersedes` is intentionally left unset to avoid retiring the old
15668
15687
  // memory without user confirmation).
15669
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;
15670
15699
 
15671
15700
  // Faithfulness gate (#1576, chatgpt P2): skip contradiction detection
15672
15701
  // for a pending_review fact — an unfaithful extraction in the review queue
@@ -15677,7 +15706,7 @@ export class Orchestrator {
15677
15706
  faithfulnessEnforceStatus !== "pending_review"
15678
15707
  ) {
15679
15708
  const targetNamespace = this.storageDirNamespace(targetStorage.dir);
15680
- const contradiction = await this.checkForContradiction(
15709
+ contradiction = await this.checkForContradiction(
15681
15710
  fact.content,
15682
15711
  writeCategory,
15683
15712
  targetNamespace,
@@ -15699,22 +15728,10 @@ export class Orchestrator {
15699
15728
  strength: contradiction.confidence,
15700
15729
  reason: contradiction.reason,
15701
15730
  });
15702
- // Deindex the superseded memory so stale paths don't remain in
15703
- // index_time.json / index_tags.json after the incremental update.
15704
- // Only applicable when auto-resolve is on and the old memory is
15705
- // actually being retired; skip when manual review is required.
15706
- if (
15707
- this.config.contradictionAutoResolve &&
15708
- this.config.queryAwareIndexingEnabled &&
15709
- contradiction.supersededPath
15710
- ) {
15711
- deindexMemory(
15712
- this.config.memoryDir,
15713
- contradiction.supersededPath,
15714
- contradiction.supersededCreated,
15715
- contradiction.supersededTags,
15716
- );
15717
- }
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.
15718
15735
  }
15719
15736
  }
15720
15737
 
@@ -15829,7 +15846,7 @@ export class Orchestrator {
15829
15846
  ? stripCitationForTemplate(fact.content, citationTemplate)
15830
15847
  : fact.content;
15831
15848
  const citedChunkedContent = applyInlineCitation(rawChunkedContent);
15832
- const parentId = await targetStorage.writeMemory(
15849
+ const parentWrite = await targetStorage.writeMemory(
15833
15850
  writeCategory,
15834
15851
  citedChunkedContent,
15835
15852
  {
@@ -15856,6 +15873,20 @@ export class Orchestrator {
15856
15873
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
15857
15874
  },
15858
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
+ );
15859
15890
  try {
15860
15891
  // Write individual chunks with parent reference
15861
15892
  for (const chunk of chunkResult.chunks) {
@@ -15903,7 +15934,13 @@ export class Orchestrator {
15903
15934
  // fact's verdict + enforce status so a pending_review fact
15904
15935
  // is not indexed as active through its chunks (chatgpt P2).
15905
15936
  ...(faithfulnessFm ? { faithfulness: faithfulnessFm } : {}),
15906
- ...(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
+ : {}),
15907
15944
  // Claim-level provenance (issue #1575 PR 2): mirror the
15908
15945
  // parent's spans onto each chunk so a chunk surfaced
15909
15946
  // independently (memory_get/x-ray on a chunk ID) preserves
@@ -15930,24 +15967,28 @@ export class Orchestrator {
15930
15967
  `chunked memory ${parentId} into ${chunkResult.chunks.length} chunks`,
15931
15968
  );
15932
15969
  trackPersistedId(targetStorage, parentId, {
15933
- pendingReview: faithfulnessEnforceStatus === "pending_review",
15970
+ pendingReview: postWriteGuard,
15934
15971
  });
15935
15972
  // #1576 (cursor Medium): keep pending_review ids out of threadEpisodeIdsForGraph — else later active facts build thread-predecessor edges to an unfaithful memory.
15936
15973
  if (
15937
- faithfulnessEnforceStatus !== "pending_review" &&
15974
+ !postWriteGuard &&
15938
15975
  threadEpisodeIdsForGraph &&
15939
15976
  !threadEpisodeIdsForGraph.includes(parentId)
15940
15977
  ) {
15941
15978
  threadEpisodeIdsForGraph.push(parentId);
15942
15979
  }
15943
- 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
+ }
15944
15985
  // PR #402 Thread 1 fix: run source-namespace temporal supersession for
15945
15986
  // chunked writes, matching the non-chunked path. Without this the
15946
15987
  // source namespace retains stale facts that should have been superseded.
15947
15988
  // Faithfulness gate (#1576, cursor High): skip supersession for a
15948
15989
  // pending_review fact — an unfaithful extraction in the review queue
15949
15990
  // must NOT retire older active memories.
15950
- if (faithfulnessEnforceStatus !== "pending_review") {
15991
+ if (!postWriteGuard) {
15951
15992
  try {
15952
15993
  const supersessionEntityRef =
15953
15994
  typeof (fact as any).entityRef === "string"
@@ -15972,7 +16013,7 @@ export class Orchestrator {
15972
16013
  // Faithfulness gate (#1576, chatgpt P2): do not promote a
15973
16014
  // pending_review fact to shared/profile — it must enter the review
15974
16015
  // queue without active copies that bypass the gate.
15975
- if (faithfulnessEnforceStatus !== "pending_review") await promoteMemoryToShared({
16016
+ if (!postWriteGuard) await promoteMemoryToShared({
15976
16017
  sourceStorage: targetStorage,
15977
16018
  category: writeCategory,
15978
16019
  content: fact.content,
@@ -16007,7 +16048,13 @@ export class Orchestrator {
16007
16048
  hasCitationForTemplate(fact.content, citationTemplate)
16008
16049
  ? stripCitationForTemplate(fact.content, citationTemplate)
16009
16050
  : fact.content;
16010
- 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
+ }
16011
16058
  } catch (err) {
16012
16059
  log.warn(
16013
16060
  `content-hash dedup registration failed for chunked memory ${parentId}: ${err}`,
@@ -16020,14 +16067,18 @@ export class Orchestrator {
16020
16067
  // into boxBuilder.onExtraction() or threading.processTurn(), which
16021
16068
  // only expect canonical parent memory IDs. Call indexPersistedMemory
16022
16069
  // directly for embedding-fallback sync of each chunk document.
16023
- 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
+ }
16024
16075
  }
16025
16076
  try {
16026
16077
  if (
16027
16078
  this.config.verbatimArtifactsEnabled &&
16028
16079
  this.config.verbatimArtifactCategories.includes(writeCategory) &&
16029
16080
  fact.confidence >= this.config.verbatimArtifactsMinConfidence &&
16030
- faithfulnessEnforceStatus !== "pending_review"
16081
+ !postWriteGuard
16031
16082
  ) {
16032
16083
  // Reuse citedChunkedContent so the artifact carries the same citation
16033
16084
  // timestamp as the parent memory write above (Fix #3 — duplicate-citation).
@@ -16042,7 +16093,7 @@ export class Orchestrator {
16042
16093
  });
16043
16094
  }
16044
16095
  // v8.2: graph edge building for chunked memories. #1576: skip pending_review.
16045
- if (graphCaps.multiGraphMemory && faithfulnessEnforceStatus !== "pending_review") {
16096
+ if (graphCaps.multiGraphMemory && !postWriteGuard) {
16046
16097
  try {
16047
16098
  const graphContext = await ensureGraphContext(targetStorage);
16048
16099
  const entityRef =
@@ -16144,7 +16195,7 @@ export class Orchestrator {
16144
16195
  ? buildProcedurePersistBody(fact.content, fact.procedureSteps)
16145
16196
  : fact.content;
16146
16197
  const citedFactContent = applyInlineCitation(rawPersistBody);
16147
- const memoryId = await targetStorage.writeMemory(
16198
+ const factWrite = await targetStorage.writeMemory(
16148
16199
  writeCategory,
16149
16200
  citedFactContent,
16150
16201
  {
@@ -16176,6 +16227,20 @@ export class Orchestrator {
16176
16227
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
16177
16228
  },
16178
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
+ );
16179
16244
  if (routedRuleId) {
16180
16245
  log.debug(
16181
16246
  `routing applied for memory ${memoryId}: rule=${routedRuleId} category=${writeCategory} storage=${targetStorage.dir}`,
@@ -16186,7 +16251,7 @@ export class Orchestrator {
16186
16251
  // key that has a conflicting value. Faithfulness gate (#1576, cursor
16187
16252
  // High): skip for a pending_review fact — an unfaithful extraction in
16188
16253
  // the review queue must NOT retire older active memories.
16189
- if (faithfulnessEnforceStatus !== "pending_review") {
16254
+ if (!postWriteGuard) {
16190
16255
  try {
16191
16256
  const supersessionEntityRef =
16192
16257
  typeof (fact as any).entityRef === "string"
@@ -16218,19 +16283,25 @@ export class Orchestrator {
16218
16283
  }),
16219
16284
  );
16220
16285
  trackPersistedId(targetStorage, memoryId, {
16221
- pendingReview: faithfulnessEnforceStatus === "pending_review",
16286
+ pendingReview: postWriteGuard,
16222
16287
  });
16223
16288
  if (
16224
- faithfulnessEnforceStatus !== "pending_review" &&
16289
+ !postWriteGuard &&
16225
16290
  threadEpisodeIdsForGraph &&
16226
16291
  !threadEpisodeIdsForGraph.includes(memoryId)
16227
16292
  ) {
16228
16293
  threadEpisodeIdsForGraph.push(memoryId);
16229
16294
  }
16230
- 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
+ }
16231
16302
  // Faithfulness gate (#1576, chatgpt P2): skip promotion for a
16232
16303
  // pending_review fact so no active shared/profile copy bypasses the gate.
16233
- if (faithfulnessEnforceStatus !== "pending_review") await promoteMemoryToShared({
16304
+ if (!postWriteGuard) await promoteMemoryToShared({
16234
16305
  sourceStorage: targetStorage,
16235
16306
  category: writeCategory,
16236
16307
  content: fact.content,
@@ -16260,7 +16331,7 @@ export class Orchestrator {
16260
16331
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
16261
16332
  });
16262
16333
  // v8.2: graph edge building (fail-open). #1576: skip pending_review facts.
16263
- if (graphCaps.multiGraphMemory && faithfulnessEnforceStatus !== "pending_review") {
16334
+ if (graphCaps.multiGraphMemory && !postWriteGuard) {
16264
16335
  try {
16265
16336
  const graphContext = await ensureGraphContext(targetStorage);
16266
16337
  const entityRef =
@@ -16304,7 +16375,7 @@ export class Orchestrator {
16304
16375
  this.config.verbatimArtifactsEnabled &&
16305
16376
  this.config.verbatimArtifactCategories.includes(writeCategory) &&
16306
16377
  fact.confidence >= this.config.verbatimArtifactsMinConfidence &&
16307
- faithfulnessEnforceStatus !== "pending_review"
16378
+ !postWriteGuard
16308
16379
  ) {
16309
16380
  // Reuse citedFactContent so the artifact carries the same citation
16310
16381
  // timestamp as the memory write above (Fix #3 — duplicate-citation).
@@ -16331,7 +16402,11 @@ export class Orchestrator {
16331
16402
  writeCategory === "procedure"
16332
16403
  ? buildProcedurePersistBody(fact.content, fact.procedureSteps)
16333
16404
  : canonicalFactContent;
16334
- 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
+ }
16335
16410
  } catch (err) {
16336
16411
  log.warn(
16337
16412
  `content-hash dedup registration failed for memory ${memoryId}: ${err}`,
@@ -16681,7 +16756,7 @@ export class Orchestrator {
16681
16756
  // Enabling only parallelRetrievalEnabled without queryAwareIndexingEnabled would silently
16682
16757
  // produce an empty temporal index, leaving the temporal agent with no data to work from.
16683
16758
  if (
16684
- !this.config.queryAwareIndexingEnabled &&
16759
+ !resolveIndexingCapabilities(this.config).queryAwareIndexing &&
16685
16760
  !caps.parallelRetrieval
16686
16761
  )
16687
16762
  return;
@@ -16796,7 +16871,7 @@ export class Orchestrator {
16796
16871
 
16797
16872
  // Build a lookup map from the already-loaded corpus to avoid repeated
16798
16873
  // readAllMemories() scans inside getMemoryById for pre-action deindex reads.
16799
- const memoryLookup = this.config.queryAwareIndexingEnabled
16874
+ const memoryLookup = resolveIndexingCapabilities(this.config).queryAwareIndexing
16800
16875
  ? new Map(allMemories.map((m) => [m.frontmatter.id, m]))
16801
16876
  : null;
16802
16877
 
@@ -16804,7 +16879,7 @@ export class Orchestrator {
16804
16879
  switch (item.action) {
16805
16880
  case "INVALIDATE": {
16806
16881
  // Capture path/frontmatter before invalidation for index cleanup
16807
- const toInvalidate = this.config.queryAwareIndexingEnabled
16882
+ const toInvalidate = resolveIndexingCapabilities(this.config).queryAwareIndexing
16808
16883
  ? (memoryLookup?.get(item.existingId) ?? null)
16809
16884
  : null;
16810
16885
  if (await this.storage.invalidateMemory(item.existingId)) {
@@ -16852,7 +16927,7 @@ export class Orchestrator {
16852
16927
  // updateMemory() only changes content/updated/supersedes/lineage — path, created, and tags
16853
16928
  // are preserved, so the temporal/tag index entry for the survivor is already correct.
16854
16929
  // Capture before invalidation for index cleanup
16855
- const toMergeInvalidate = this.config.queryAwareIndexingEnabled
16930
+ const toMergeInvalidate = resolveIndexingCapabilities(this.config).queryAwareIndexing
16856
16931
  ? (memoryLookup?.get(item.mergeWith) ?? null)
16857
16932
  : null;
16858
16933
  if (await this.storage.invalidateMemory(item.mergeWith)) {
@@ -16938,7 +17013,7 @@ export class Orchestrator {
16938
17013
  if (deletedCommitments.length > 0) {
16939
17014
  memoryItemMutated = true;
16940
17015
  log.info(`cleaned ${deletedCommitments.length} expired commitments`);
16941
- if (this.config.queryAwareIndexingEnabled) {
17016
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing) {
16942
17017
  for (const m of deletedCommitments) {
16943
17018
  deindexMemory(
16944
17019
  this.config.memoryDir,
@@ -16951,9 +17026,9 @@ export class Orchestrator {
16951
17026
  }
16952
17027
 
16953
17028
  if (
16954
- this.config.creationMemoryEnabled &&
16955
- this.config.commitmentLedgerEnabled &&
16956
- this.config.commitmentLifecycleEnabled
17029
+ resolveCreationMemoryCapabilities(this.config).creationMemory &&
17030
+ resolveCreationMemoryCapabilities(this.config).commitmentLedger &&
17031
+ resolveCreationMemoryCapabilities(this.config).commitmentLifecycle
16957
17032
  ) {
16958
17033
  try {
16959
17034
  const lifecycle = await applyCommitmentLedgerLifecycle({
@@ -16981,7 +17056,7 @@ export class Orchestrator {
16981
17056
  if (deletedTTL.length > 0) {
16982
17057
  memoryItemMutated = true;
16983
17058
  log.info(`cleaned ${deletedTTL.length} TTL-expired memories`);
16984
- if (this.config.queryAwareIndexingEnabled) {
17059
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing) {
16985
17060
  for (const m of deletedTTL) {
16986
17061
  deindexMemory(
16987
17062
  this.config.memoryDir,
@@ -17259,347 +17334,26 @@ export class Orchestrator {
17259
17334
  async optimizeCompressionGuidelines(options?: {
17260
17335
  dryRun?: boolean;
17261
17336
  eventLimit?: number;
17262
- }): Promise<{
17263
- enabled: boolean;
17264
- dryRun: boolean;
17265
- eventCount: number;
17266
- previousGuidelineVersion: number | null;
17267
- nextGuidelineVersion: number;
17268
- changedRules: number;
17269
- semanticRefinementApplied: boolean;
17270
- persisted: boolean;
17271
- draftContentHash: string | null;
17272
- }> {
17273
- const dryRun = options?.dryRun === true;
17274
- const eventLimit =
17275
- typeof options?.eventLimit === "number"
17276
- ? Math.max(0, Math.floor(options.eventLimit))
17277
- : 500;
17278
-
17279
- const [activeState, draftState] = await Promise.all([
17280
- this.storage.readCompressionGuidelineOptimizerState(),
17281
- this.storage.readCompressionGuidelineDraftState().catch(() => null),
17282
- ]);
17283
- const previousState =
17284
- draftState &&
17285
- ((activeState?.guidelineVersion ?? 0) < draftState.guidelineVersion ||
17286
- (activeState?.guidelineVersion ?? 0) === draftState.guidelineVersion)
17287
- ? draftState
17288
- : activeState;
17289
-
17290
- if (!this.config.compressionGuidelineLearningEnabled) {
17291
- return {
17292
- enabled: false,
17293
- dryRun,
17294
- eventCount: 0,
17295
- previousGuidelineVersion: previousState?.guidelineVersion ?? null,
17296
- nextGuidelineVersion: previousState?.guidelineVersion ?? 0,
17297
- changedRules: 0,
17298
- semanticRefinementApplied: false,
17299
- persisted: false,
17300
- draftContentHash: null,
17301
- };
17302
- }
17303
-
17304
- let events = await this.storage.readMemoryActionEvents(eventLimit);
17305
- if (eventLimit > 0) {
17306
- let effectiveEvents = events.filter((event) => event.dryRun !== true);
17307
- let fetchLimit = eventLimit;
17308
- while (
17309
- effectiveEvents.length < eventLimit &&
17310
- events.length === fetchLimit
17311
- ) {
17312
- fetchLimit = Math.min(fetchLimit * 2, fetchLimit + 1000);
17313
- if (fetchLimit <= events.length) break;
17314
- events = await this.storage.readMemoryActionEvents(fetchLimit);
17315
- effectiveEvents = events.filter((event) => event.dryRun !== true);
17316
- }
17317
- events = effectiveEvents.slice(-eventLimit);
17318
- }
17319
- const generatedAt = new Date().toISOString();
17320
- const candidate = computeCompressionGuidelineCandidate(events, {
17321
- generatedAtIso: generatedAt,
17322
- previousState,
17323
- });
17324
- if (candidate.eventCounts.total === 0) {
17325
- return {
17326
- enabled: true,
17327
- dryRun,
17328
- eventCount: 0,
17329
- previousGuidelineVersion: previousState?.guidelineVersion ?? null,
17330
- nextGuidelineVersion: previousState?.guidelineVersion ?? 0,
17331
- changedRules: 0,
17332
- semanticRefinementApplied: false,
17333
- persisted: false,
17334
- draftContentHash: null,
17335
- };
17336
- }
17337
- const refinedCandidate =
17338
- await refineCompressionGuidelineCandidateSemantically(candidate, {
17339
- enabled: this.config.compressionGuidelineSemanticRefinementEnabled,
17340
- timeoutMs: this.config.compressionGuidelineSemanticTimeoutMs,
17341
- runRefinement: async (baseline) => {
17342
- const prompt = [
17343
- "You refine compression policy suggestions conservatively.",
17344
- "Return JSON only in this shape:",
17345
- '{"updates":[{"action":"summarize_node","delta":0.02,"confidence":"medium","note":"..."}]}',
17346
- "Constraints:",
17347
- "- Keep updates sparse and conservative.",
17348
- "- delta must stay between -0.15 and 0.15.",
17349
- "- Only include actions present in the input.",
17350
- "Input candidate:",
17351
- JSON.stringify(baseline),
17352
- ].join("\n");
17353
-
17354
- const response = await this.fastChatCompletion(
17355
- [
17356
- {
17357
- role: "system",
17358
- content: "Respond with strict JSON only. No markdown.",
17359
- },
17360
- { role: "user", content: prompt },
17361
- ],
17362
- {
17363
- temperature: 0.1,
17364
- maxTokens: 400,
17365
- operation: "compression_guideline_semantic_refinement",
17366
- priority: "background",
17367
- },
17368
- );
17369
-
17370
- return this.parseCompressionSemanticRefinement(
17371
- response?.content ?? "",
17372
- );
17373
- },
17374
- });
17375
-
17376
- const content = renderCompressionGuidelinesMarkdown(refinedCandidate);
17377
- const contentHash = createHash("sha256").update(content).digest("hex");
17378
- const semanticRefinementApplied =
17379
- JSON.stringify(refinedCandidate.ruleUpdates) !==
17380
- JSON.stringify(candidate.ruleUpdates);
17381
- const changedRules = refinedCandidate.ruleUpdates.filter(
17382
- (rule) => rule.delta !== 0,
17383
- ).length;
17384
-
17385
- if (!dryRun) {
17386
- await this.storage.writeCompressionGuidelineDraft(content);
17387
- await this.storage.writeCompressionGuidelineDraftState({
17388
- version: refinedCandidate.optimizerVersion,
17389
- updatedAt: refinedCandidate.generatedAt,
17390
- sourceWindow: refinedCandidate.sourceWindow,
17391
- eventCounts: refinedCandidate.eventCounts,
17392
- guidelineVersion: refinedCandidate.guidelineVersion,
17393
- contentHash,
17394
- activationState: "draft",
17395
- actionSummaries: refinedCandidate.actionSummaries,
17396
- ruleUpdates: refinedCandidate.ruleUpdates,
17397
- });
17398
- }
17399
-
17400
- return {
17401
- enabled: true,
17402
- dryRun,
17403
- eventCount: candidate.eventCounts.total,
17404
- previousGuidelineVersion: previousState?.guidelineVersion ?? null,
17405
- nextGuidelineVersion: refinedCandidate.guidelineVersion,
17406
- changedRules,
17407
- semanticRefinementApplied,
17408
- persisted: !dryRun,
17409
- draftContentHash: dryRun ? null : contentHash,
17410
- };
17337
+ }) {
17338
+ return this.compressionGuidelineCoordinator.optimizeCompressionGuidelines(options);
17411
17339
  }
17412
17340
 
17413
17341
  async activateCompressionGuidelineDraft(options?: {
17414
17342
  expectedContentHash?: string;
17415
17343
  expectedGuidelineVersion?: number;
17416
- }): Promise<{
17417
- enabled: boolean;
17418
- activated: boolean;
17419
- guidelineVersion: number | null;
17420
- reason?:
17421
- | "disabled"
17422
- | "missing_draft"
17423
- | "expected_revision_required"
17424
- | "content_hash_mismatch"
17425
- | "guideline_version_mismatch"
17426
- | "draft_changed";
17427
- }> {
17428
- if (!this.config.compressionGuidelineLearningEnabled) {
17429
- return {
17430
- enabled: false,
17431
- activated: false,
17432
- guidelineVersion: null,
17433
- reason: "disabled",
17434
- };
17435
- }
17436
-
17437
- const draftState = await this.storage.readCompressionGuidelineDraftState();
17438
- if (!draftState) {
17439
- return {
17440
- enabled: true,
17441
- activated: false,
17442
- guidelineVersion: null,
17443
- reason: "missing_draft",
17444
- };
17445
- }
17446
-
17447
- const expectedContentHash = options?.expectedContentHash?.trim();
17448
- const expectedGuidelineVersion = options?.expectedGuidelineVersion;
17449
-
17450
- if (
17451
- (!expectedContentHash || expectedContentHash.length === 0) &&
17452
- typeof expectedGuidelineVersion !== "number"
17453
- ) {
17454
- return {
17455
- enabled: true,
17456
- activated: false,
17457
- guidelineVersion: null,
17458
- reason: "expected_revision_required",
17459
- };
17460
- }
17461
-
17462
- if (expectedContentHash && draftState.contentHash !== expectedContentHash) {
17463
- return {
17464
- enabled: true,
17465
- activated: false,
17466
- guidelineVersion: null,
17467
- reason: "content_hash_mismatch",
17468
- };
17469
- }
17470
-
17471
- if (
17472
- typeof expectedGuidelineVersion === "number" &&
17473
- draftState.guidelineVersion !== expectedGuidelineVersion
17474
- ) {
17475
- return {
17476
- enabled: true,
17477
- activated: false,
17478
- guidelineVersion: null,
17479
- reason: "guideline_version_mismatch",
17480
- };
17481
- }
17482
-
17483
- const activated = await this.storage.activateCompressionGuidelineDraft({
17484
- ...(expectedContentHash ? { expectedContentHash } : {}),
17485
- ...(typeof expectedGuidelineVersion === "number"
17486
- ? { expectedGuidelineVersion }
17487
- : {}),
17488
- });
17489
- return {
17490
- enabled: true,
17491
- activated,
17492
- guidelineVersion: activated ? draftState.guidelineVersion : null,
17493
- ...(activated ? {} : { reason: "draft_changed" as const }),
17494
- };
17344
+ }) {
17345
+ return this.compressionGuidelineCoordinator.activateCompressionGuidelineDraft(options);
17495
17346
  }
17496
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.
17497
17351
  private async runCompressionGuidelineLearningPass(): Promise<void> {
17498
- if (!this.config.compressionGuidelineLearningEnabled) return;
17499
- try {
17500
- const result = await this.optimizeCompressionGuidelines({
17501
- dryRun: false,
17502
- eventLimit: 500,
17503
- });
17504
- log.info(
17505
- `compression guideline learning updated (${result.eventCount} events)`,
17506
- );
17507
- } catch (err) {
17508
- log.warn(`compression guideline learning failed (ignored): ${err}`);
17509
- }
17510
- }
17511
-
17512
- private async buildCompressionGuidelineRecallSection(): Promise<
17513
- string | null
17514
- > {
17515
- if (!this.config.contextCompressionActionsEnabled) return null;
17516
- if (!this.config.compressionGuidelineLearningEnabled) return null;
17517
-
17518
- const state = await this.storage
17519
- .readCompressionGuidelineOptimizerState()
17520
- .catch(() => null);
17521
- if (!state || state.guidelineVersion <= 0) return null;
17522
-
17523
- const raw = await this.storage
17524
- .readCompressionGuidelines()
17525
- .catch(() => null);
17526
- const summary = raw ? formatCompressionGuidelinesForRecall(raw, 5) : null;
17527
- if (!summary) return null;
17528
-
17529
- return [
17530
- "## Active Compression Guidelines",
17531
- "",
17532
- `Guideline version: ${state.guidelineVersion}`,
17533
- `Updated: ${state.updatedAt}`,
17534
- "",
17535
- summary,
17536
- ].join("\n");
17352
+ return this.compressionGuidelineCoordinator.runCompressionGuidelineLearningPass();
17537
17353
  }
17538
17354
 
17539
- private parseCompressionSemanticRefinement(raw: string): {
17540
- updates: Array<{
17541
- action: MemoryActionType;
17542
- delta?: number;
17543
- confidence?: "low" | "medium" | "high";
17544
- note?: string;
17545
- }>;
17546
- } | null {
17547
- if (typeof raw !== "string" || raw.trim().length === 0) return null;
17548
- const trimmed = raw.trim();
17549
- const start = trimmed.indexOf("{");
17550
- const end = trimmed.lastIndexOf("}");
17551
- if (start === -1 || end === -1 || end <= start) return null;
17552
-
17553
- try {
17554
- const parsed = JSON.parse(trimmed.slice(start, end + 1)) as {
17555
- updates?: Array<{
17556
- action?: unknown;
17557
- delta?: unknown;
17558
- confidence?: unknown;
17559
- note?: unknown;
17560
- }>;
17561
- };
17562
- if (!Array.isArray(parsed?.updates)) return null;
17563
-
17564
- const validActions = new Set<MemoryActionType>([
17565
- "store_episode",
17566
- "store_note",
17567
- "update_note",
17568
- "create_artifact",
17569
- "summarize_node",
17570
- "discard",
17571
- "link_graph",
17572
- ]);
17573
-
17574
- const updates = parsed.updates
17575
- .filter(
17576
- (item) =>
17577
- item &&
17578
- typeof item.action === "string" &&
17579
- validActions.has(item.action as MemoryActionType),
17580
- )
17581
- .map((item) => {
17582
- const confidence: "low" | "medium" | "high" | undefined =
17583
- item.confidence === "low" ||
17584
- item.confidence === "medium" ||
17585
- item.confidence === "high"
17586
- ? item.confidence
17587
- : undefined;
17588
- return {
17589
- action: item.action as MemoryActionType,
17590
- delta:
17591
- typeof item.delta === "number" && Number.isFinite(item.delta)
17592
- ? item.delta
17593
- : undefined,
17594
- confidence,
17595
- note: typeof item.note === "string" ? item.note : undefined,
17596
- };
17597
- });
17598
-
17599
- return { updates };
17600
- } catch {
17601
- return null;
17602
- }
17355
+ private async buildCompressionGuidelineRecallSection(): Promise<string | null> {
17356
+ return this.compressionGuidelineCoordinator.buildCompressionGuidelineRecallSection();
17603
17357
  }
17604
17358
 
17605
17359
  private actionOutcomePriorDelta(event: MemoryActionEvent): number {
@@ -17869,7 +17623,7 @@ export class Orchestrator {
17869
17623
  );
17870
17624
  await this.embeddingFallback.removeFromIndex(memory.frontmatter.id);
17871
17625
  if (
17872
- this.config.queryAwareIndexingEnabled &&
17626
+ resolveIndexingCapabilities(this.config).queryAwareIndexing &&
17873
17627
  memory.path &&
17874
17628
  memory.frontmatter?.created
17875
17629
  ) {
@@ -20254,7 +20008,7 @@ export class Orchestrator {
20254
20008
  );
20255
20009
  let temporalFromDate: string | null = null;
20256
20010
  let promptTags: string[] = [];
20257
- if (this.config.queryAwareIndexingEnabled && prompt) {
20011
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing && prompt) {
20258
20012
  if (isTemporalQuery(prompt)) {
20259
20013
  temporalFromDate = recencyWindowFromPrompt(prompt, now);
20260
20014
  }
@@ -20279,7 +20033,7 @@ export class Orchestrator {
20279
20033
  // Scope to result paths first so cross-namespace paths don't consume the cap.
20280
20034
  let temporalCandidates: Set<string> | null = null;
20281
20035
  let tagCandidates: Set<string> | null = null;
20282
- if (this.config.queryAwareIndexingEnabled && prompt) {
20036
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing && prompt) {
20283
20037
  const maxCandidates = this.config.queryAwareIndexingMaxCandidates;
20284
20038
  const capSet = (s: Set<string> | null): Set<string> | null => {
20285
20039
  if (!s) return null;
@@ -20388,7 +20142,7 @@ export class Orchestrator {
20388
20142
 
20389
20143
  // v8.1: Temporal + Tag index boost
20390
20144
  // Results that match the detected temporal window or tag query get a small additive boost.
20391
- if (this.config.queryAwareIndexingEnabled && r.path) {
20145
+ if (resolveIndexingCapabilities(this.config).queryAwareIndexing && r.path) {
20392
20146
  if (temporalCandidates?.has(r.path)) {
20393
20147
  score += applyUtilityRankingRuntimeDelta(
20394
20148
  0.08,
@@ -20625,15 +20379,13 @@ export class Orchestrator {
20625
20379
  }
20626
20380
 
20627
20381
  // The new fact is newer than the existing one. When auto-resolve is
20628
- // enabled, immediately retire the old memory. When disabled, leave the
20629
- // old memory active for manual review.
20630
- if (this.config.contradictionAutoResolve) {
20631
- await resultStorage.supersedeMemory(
20632
- existingMemory.frontmatter.id,
20633
- "pending-new", // Will be updated after the new memory is written
20634
- verification.reasoning,
20635
- );
20636
- }
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.
20637
20389
 
20638
20390
  // Return the contradiction info regardless of auto-resolve setting.
20639
20391
  // The caller uses this to set `contradictionDetected=true` which
@@ -20656,6 +20408,71 @@ export class Orchestrator {
20656
20408
  return null;
20657
20409
  }
20658
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
+
20659
20476
  // ---------------------------------------------------------------------------
20660
20477
  // Memory Linking (Phase 3A)
20661
20478
  // ---------------------------------------------------------------------------