@remnic/core 9.3.741 → 9.3.743

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 (286) hide show
  1. package/dist/access-boundary.d.ts +5 -5
  2. package/dist/access-boundary.js +10 -10
  3. package/dist/access-cli.js +32 -32
  4. package/dist/access-http.d.ts +5 -5
  5. package/dist/access-http.js +15 -15
  6. package/dist/access-mcp.d.ts +5 -5
  7. package/dist/access-mcp.js +14 -14
  8. package/dist/access-operations-batch.js +12 -12
  9. package/dist/access-operations.d.ts +5 -5
  10. package/dist/access-operations.js +13 -13
  11. package/dist/{access-service-Cbk-Kv_A.d.ts → access-service-BeW5GOg1.d.ts} +3 -3
  12. package/dist/access-service.d.ts +5 -5
  13. package/dist/access-service.js +9 -9
  14. package/dist/access-surface-catalog.d.ts +5 -5
  15. package/dist/action-confidence.d.ts +1 -1
  16. package/dist/active-memory-bridge.d.ts +1 -1
  17. package/dist/active-memory-bridge.js +3 -3
  18. package/dist/active-recall.d.ts +1 -1
  19. package/dist/{auto-sync-G3V5AN4C.js → auto-sync-XHSKKQAH.js} +2 -2
  20. package/dist/behavior-learner.d.ts +1 -1
  21. package/dist/behavior-signals.d.ts +1 -1
  22. package/dist/bootstrap.d.ts +4 -4
  23. package/dist/briefing.d.ts +1 -1
  24. package/dist/buffer-surprise-report.d.ts +1 -1
  25. package/dist/buffer.d.ts +1 -1
  26. package/dist/buffer.js +2 -2
  27. package/dist/calibration.d.ts +1 -1
  28. package/dist/capabilities.d.ts +10 -5
  29. package/dist/capabilities.js +1 -1
  30. package/dist/{catalog-_Bap6vfD.d.ts → catalog-CX0CjOaf.d.ts} +1 -1
  31. package/dist/causal-behavior.d.ts +1 -1
  32. package/dist/causal-consolidation.d.ts +1 -1
  33. package/dist/causal-consolidation.js +2 -2
  34. package/dist/causal-trajectory-graph.d.ts +1 -1
  35. package/dist/{chunk-X5PMDVY4.js → chunk-3EA5LIFJ.js} +4 -4
  36. package/dist/{chunk-YNHRWFAP.js → chunk-4DLFJJOQ.js} +2 -2
  37. package/dist/{chunk-WIMKOTNY.js → chunk-5QANTTY2.js} +2 -2
  38. package/dist/{chunk-QCO2QFLZ.js → chunk-5UNSGRCR.js} +7 -6
  39. package/dist/{chunk-QCO2QFLZ.js.map → chunk-5UNSGRCR.js.map} +1 -1
  40. package/dist/{chunk-Y6PNYJVO.js → chunk-A2KKLCOS.js} +2 -2
  41. package/dist/{chunk-O4IWVWSW.js → chunk-AVBT5WEW.js} +3 -3
  42. package/dist/{chunk-M5KLYHHI.js → chunk-BYLC4DKI.js} +2 -2
  43. package/dist/{chunk-OZECYVGI.js → chunk-C7LDPZJJ.js} +6 -4
  44. package/dist/{chunk-OZECYVGI.js.map → chunk-C7LDPZJJ.js.map} +1 -1
  45. package/dist/{chunk-UOVDO6TC.js → chunk-CHELXDTO.js} +3 -3
  46. package/dist/{chunk-TQQN64J2.js → chunk-DBSMLRTO.js} +2 -2
  47. package/dist/{chunk-4J53ZVPQ.js → chunk-DYK27X7V.js} +3 -3
  48. package/dist/{chunk-K2N6WIZM.js → chunk-E7RGJQWP.js} +2 -2
  49. package/dist/{chunk-M4WI23X3.js → chunk-EEKDRAWX.js} +2 -2
  50. package/dist/{chunk-H3WSZWHK.js → chunk-G5FP7VSQ.js} +13 -12
  51. package/dist/chunk-G5FP7VSQ.js.map +1 -0
  52. package/dist/{chunk-BE6NEQLF.js → chunk-HAZVVOYT.js} +3 -3
  53. package/dist/{chunk-OT5K26NG.js → chunk-HM6UJPPO.js} +2 -2
  54. package/dist/{chunk-PI4JHLDD.js → chunk-IP5SANO2.js} +2 -2
  55. package/dist/{chunk-WHEF76BQ.js → chunk-JRZV3767.js} +2 -2
  56. package/dist/{chunk-OS5RKJOH.js → chunk-JZ7P26GF.js} +2 -2
  57. package/dist/{chunk-Y62QPRKV.js → chunk-K5LLP4CY.js} +2 -2
  58. package/dist/{chunk-C3Z5YJSM.js → chunk-LOPZ2DWR.js} +2 -2
  59. package/dist/{chunk-BJPL5WAL.js → chunk-MXDG6SRH.js} +3 -3
  60. package/dist/{chunk-UYYSOMS2.js → chunk-NBP3YHHS.js} +4 -4
  61. package/dist/{chunk-USHVHI6J.js → chunk-NNL4LJAD.js} +3 -3
  62. package/dist/{chunk-GPX5E36D.js → chunk-OSMBC3JU.js} +2 -2
  63. package/dist/{chunk-OUSOMUFV.js → chunk-PE6XR4GG.js} +2 -2
  64. package/dist/{chunk-DGVM5SFL.js → chunk-PHRLJFWZ.js} +3 -2
  65. package/dist/{chunk-DGVM5SFL.js.map → chunk-PHRLJFWZ.js.map} +1 -1
  66. package/dist/{chunk-CMPXLMC3.js → chunk-PI23H7FV.js} +3 -3
  67. package/dist/{chunk-WGDE2TJP.js → chunk-QOGXUNWL.js} +4 -4
  68. package/dist/{chunk-ELL5VYXC.js → chunk-RNWWAS5V.js} +3 -3
  69. package/dist/{chunk-LD6A7XG5.js → chunk-TQPSOTC7.js} +7 -7
  70. package/dist/{chunk-AX4TRSRE.js → chunk-U5WVRMCA.js} +2 -2
  71. package/dist/{chunk-773DPI6T.js → chunk-V5RVMULT.js} +11 -8
  72. package/dist/chunk-V5RVMULT.js.map +1 -0
  73. package/dist/{chunk-DBVBV2RF.js → chunk-VE54E6RP.js} +4 -4
  74. package/dist/{chunk-VC7QELR6.js → chunk-VO4VARZL.js} +1054 -869
  75. package/dist/chunk-VO4VARZL.js.map +1 -0
  76. package/dist/{chunk-G7Q4D2G3.js → chunk-VSF7AAGE.js} +1 -3
  77. package/dist/chunk-VSF7AAGE.js.map +1 -0
  78. package/dist/{chunk-OVYBENIJ.js → chunk-W3M34XT4.js} +3 -3
  79. package/dist/{chunk-XPOOY35N.js → chunk-W4H5A3TJ.js} +3 -2
  80. package/dist/chunk-W4H5A3TJ.js.map +1 -0
  81. package/dist/{chunk-7Y5KKMIP.js → chunk-WPLRJ4XM.js} +9 -8
  82. package/dist/{chunk-7Y5KKMIP.js.map → chunk-WPLRJ4XM.js.map} +1 -1
  83. package/dist/{chunk-3TUDKDTQ.js → chunk-XEADGIAA.js} +6 -6
  84. package/dist/{cli-BQW2xKdb.d.ts → cli-BYad9YYY.d.ts} +3 -3
  85. package/dist/cli.d.ts +6 -6
  86. package/dist/cli.js +21 -21
  87. package/dist/compounding/engine.d.ts +1 -1
  88. package/dist/compounding/engine.js +3 -3
  89. package/dist/compounding/preference-consolidator.d.ts +1 -1
  90. package/dist/compression-optimizer.d.ts +1 -1
  91. package/dist/config.d.ts +1 -1
  92. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  93. package/dist/connectors/codex-materialize.d.ts +1 -1
  94. package/dist/connectors/index.d.ts +1 -1
  95. package/dist/connectors/index.js +2 -2
  96. package/dist/consolidation-provenance-check.d.ts +1 -1
  97. package/dist/consolidation-undo.d.ts +1 -1
  98. package/dist/contradiction/index.d.ts +1 -1
  99. package/dist/contradiction/index.js +2 -2
  100. package/dist/{contradiction-scan-UERKJESC.js → contradiction-scan-3FJYWS3G.js} +3 -3
  101. package/dist/conversation-index/backend.d.ts +1 -1
  102. package/dist/conversation-index/chunker.d.ts +1 -1
  103. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  104. package/dist/conversation-index/indexer.d.ts +1 -1
  105. package/dist/conversation-index/search.d.ts +1 -1
  106. package/dist/day-summary.d.ts +1 -1
  107. package/dist/day-summary.js +2 -2
  108. package/dist/delinearize.d.ts +1 -1
  109. package/dist/direct-answer-wiring.d.ts +1 -1
  110. package/dist/direct-answer.d.ts +1 -1
  111. package/dist/embedding-fallback.d.ts +1 -1
  112. package/dist/embedding-fallback.js +2 -2
  113. package/dist/enrichment/index.d.ts +1 -1
  114. package/dist/entity-retrieval.d.ts +1 -1
  115. package/dist/entity-retrieval.js +2 -2
  116. package/dist/entity-schema.d.ts +1 -1
  117. package/dist/explicit-capture.d.ts +4 -4
  118. package/dist/explicit-capture.js +2 -2
  119. package/dist/extraction-faithfulness.d.ts +1 -1
  120. package/dist/extraction-judge-telemetry.d.ts +1 -1
  121. package/dist/extraction-judge-training.d.ts +1 -1
  122. package/dist/extraction-judge.d.ts +1 -1
  123. package/dist/extraction.d.ts +1 -1
  124. package/dist/extraction.js +4 -4
  125. package/dist/fallback-llm.d.ts +1 -1
  126. package/dist/{first-start-migration-GQ3UQNIL.js → first-start-migration-4I3VDSYS.js} +2 -2
  127. package/dist/graph-dashboard-diff.d.ts +1 -1
  128. package/dist/graph-dashboard-key.d.ts +1 -1
  129. package/dist/graph-dashboard-parser.d.ts +1 -1
  130. package/dist/graph-edge-reinforcement.d.ts +1 -1
  131. package/dist/graph-recall.js +1 -1
  132. package/dist/graph-snapshot.d.ts +1 -1
  133. package/dist/graph.d.ts +1 -1
  134. package/dist/identity-continuity.d.ts +1 -1
  135. package/dist/importance.d.ts +1 -1
  136. package/dist/index.d.ts +9 -9
  137. package/dist/index.js +40 -40
  138. package/dist/intent.d.ts +1 -1
  139. package/dist/lcm/engine.d.ts +1 -1
  140. package/dist/lcm/engine.js +2 -2
  141. package/dist/lcm/index.d.ts +1 -1
  142. package/dist/lcm/index.js +2 -2
  143. package/dist/lcm/summarizer.js +1 -1
  144. package/dist/lcm/tools.d.ts +1 -1
  145. package/dist/lifecycle.d.ts +1 -1
  146. package/dist/live-connectors-runner.d.ts +1 -1
  147. package/dist/local-llm.d.ts +1 -1
  148. package/dist/local-llm.js +2 -2
  149. package/dist/local-model-endpoint.d.ts +1 -1
  150. package/dist/maintenance/memory-governance.d.ts +1 -1
  151. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  152. package/dist/memory-action-policy.d.ts +1 -1
  153. package/dist/memory-cache.d.ts +1 -1
  154. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  155. package/dist/memory-projection-store.d.ts +1 -1
  156. package/dist/memory-provenance.d.ts +1 -1
  157. package/dist/memory-worth-outcomes.d.ts +1 -1
  158. package/dist/models-json.d.ts +1 -1
  159. package/dist/namespaces/migrate.d.ts +2 -2
  160. package/dist/namespaces/migrate.js +6 -6
  161. package/dist/namespaces/principal.d.ts +1 -1
  162. package/dist/namespaces/principal.js +2 -2
  163. package/dist/namespaces/search.d.ts +1 -1
  164. package/dist/namespaces/search.js +4 -4
  165. package/dist/namespaces/storage.d.ts +2 -2
  166. package/dist/namespaces/storage.js +2 -2
  167. package/dist/native-knowledge.d.ts +1 -1
  168. package/dist/operator-toolkit.d.ts +1 -1
  169. package/dist/operator-toolkit.js +11 -11
  170. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  171. package/dist/orchestration/compression-guideline-coordinator.js +2 -2
  172. package/dist/orchestration/maintenance.d.ts +2 -2
  173. package/dist/orchestration/maintenance.js +6 -6
  174. package/dist/{orchestrator-BOAFN_Yr.d.ts → orchestrator-CupigmWu.d.ts} +221 -7
  175. package/dist/orchestrator.d.ts +4 -4
  176. package/dist/orchestrator.js +27 -27
  177. package/dist/patterns-cli.d.ts +1 -1
  178. package/dist/policy-runtime.d.ts +1 -1
  179. package/dist/provenance.d.ts +1 -1
  180. package/dist/qmd-recall-cache.d.ts +1 -1
  181. package/dist/qmd.d.ts +1 -1
  182. package/dist/recall-disclosure-escalation.d.ts +1 -1
  183. package/dist/recall-explain-renderer.d.ts +1 -1
  184. package/dist/recall-planner-llm.d.ts +1 -1
  185. package/dist/recall-state.d.ts +1 -1
  186. package/dist/recall-tag-filter.d.ts +1 -1
  187. package/dist/recall-xray-cli.d.ts +1 -1
  188. package/dist/recall-xray-renderer.d.ts +1 -1
  189. package/dist/recall-xray.d.ts +1 -1
  190. package/dist/resolve-auth-token.d.ts +1 -1
  191. package/dist/retrieval-agents.d.ts +1 -1
  192. package/dist/retrieval-tiers.d.ts +1 -1
  193. package/dist/routing/engine.d.ts +1 -1
  194. package/dist/routing/store.d.ts +1 -1
  195. package/dist/search/embed-helper.d.ts +1 -1
  196. package/dist/search/embed-helper.js +2 -2
  197. package/dist/search/factory.d.ts +1 -1
  198. package/dist/search/factory.js +3 -3
  199. package/dist/search/index.d.ts +1 -1
  200. package/dist/search/index.js +3 -3
  201. package/dist/search/lancedb-backend.d.ts +1 -1
  202. package/dist/search/meilisearch-backend.d.ts +1 -1
  203. package/dist/search/noop-backend.d.ts +1 -1
  204. package/dist/search/orama-backend.d.ts +1 -1
  205. package/dist/search/port.d.ts +1 -1
  206. package/dist/search/remote-backend.d.ts +1 -1
  207. package/dist/{semantic-consolidation-DsIEiCka.d.ts → semantic-consolidation-ByzGwC9c.d.ts} +1 -1
  208. package/dist/semantic-consolidation.d.ts +2 -2
  209. package/dist/semantic-consolidation.js +2 -2
  210. package/dist/semantic-rule-verifier.d.ts +1 -1
  211. package/dist/session-observer-bands.d.ts +1 -1
  212. package/dist/session-observer-state.d.ts +1 -1
  213. package/dist/shared-context/manager.d.ts +1 -1
  214. package/dist/shared-context/manager.js +2 -2
  215. package/dist/signal.d.ts +1 -1
  216. package/dist/storage.d.ts +1 -1
  217. package/dist/summarizer.d.ts +1 -1
  218. package/dist/summarizer.js +3 -3
  219. package/dist/summary-snapshot.d.ts +1 -1
  220. package/dist/temporal-supersession.d.ts +1 -1
  221. package/dist/temporal-validity.d.ts +1 -1
  222. package/dist/threading.d.ts +1 -1
  223. package/dist/tier-migration.d.ts +1 -1
  224. package/dist/tier-routing.d.ts +1 -1
  225. package/dist/{tier-stats-CSKL7BDG.js → tier-stats-5XF4UBRQ.js} +2 -2
  226. package/dist/topics.d.ts +1 -1
  227. package/dist/transcript.d.ts +1 -1
  228. package/dist/trust-score-stage.d.ts +1 -1
  229. package/dist/trust-score.d.ts +1 -1
  230. package/dist/{types-CglNn2V-.d.ts → types-5Jik3r4G.d.ts} +1 -1
  231. package/dist/types.d.ts +1 -1
  232. package/dist/utility-runtime.d.ts +1 -1
  233. package/package.json +2 -2
  234. package/src/capabilities.test.ts +0 -2
  235. package/src/capabilities.ts +7 -6
  236. package/src/cli.ts +2 -1
  237. package/src/graph-recall.ts +2 -1
  238. package/src/lcm/engine.ts +9 -6
  239. package/src/lcm/summarizer.ts +2 -1
  240. package/src/operator-toolkit.ts +2 -1
  241. package/src/orchestration/contradiction-linking-coordinator.ts +336 -0
  242. package/src/orchestration/qmd-result-resolver.ts +377 -0
  243. package/src/orchestration/recall-section-coordinator.ts +349 -0
  244. package/src/orchestrator.ts +81 -679
  245. package/src/procedural/procedure-stats.ts +2 -1
  246. package/src/wearables/pipeline.ts +5 -3
  247. package/dist/chunk-773DPI6T.js.map +0 -1
  248. package/dist/chunk-G7Q4D2G3.js.map +0 -1
  249. package/dist/chunk-H3WSZWHK.js.map +0 -1
  250. package/dist/chunk-VC7QELR6.js.map +0 -1
  251. package/dist/chunk-XPOOY35N.js.map +0 -1
  252. /package/dist/{auto-sync-G3V5AN4C.js.map → auto-sync-XHSKKQAH.js.map} +0 -0
  253. /package/dist/{chunk-X5PMDVY4.js.map → chunk-3EA5LIFJ.js.map} +0 -0
  254. /package/dist/{chunk-YNHRWFAP.js.map → chunk-4DLFJJOQ.js.map} +0 -0
  255. /package/dist/{chunk-WIMKOTNY.js.map → chunk-5QANTTY2.js.map} +0 -0
  256. /package/dist/{chunk-Y6PNYJVO.js.map → chunk-A2KKLCOS.js.map} +0 -0
  257. /package/dist/{chunk-O4IWVWSW.js.map → chunk-AVBT5WEW.js.map} +0 -0
  258. /package/dist/{chunk-M5KLYHHI.js.map → chunk-BYLC4DKI.js.map} +0 -0
  259. /package/dist/{chunk-UOVDO6TC.js.map → chunk-CHELXDTO.js.map} +0 -0
  260. /package/dist/{chunk-TQQN64J2.js.map → chunk-DBSMLRTO.js.map} +0 -0
  261. /package/dist/{chunk-4J53ZVPQ.js.map → chunk-DYK27X7V.js.map} +0 -0
  262. /package/dist/{chunk-K2N6WIZM.js.map → chunk-E7RGJQWP.js.map} +0 -0
  263. /package/dist/{chunk-M4WI23X3.js.map → chunk-EEKDRAWX.js.map} +0 -0
  264. /package/dist/{chunk-BE6NEQLF.js.map → chunk-HAZVVOYT.js.map} +0 -0
  265. /package/dist/{chunk-OT5K26NG.js.map → chunk-HM6UJPPO.js.map} +0 -0
  266. /package/dist/{chunk-PI4JHLDD.js.map → chunk-IP5SANO2.js.map} +0 -0
  267. /package/dist/{chunk-WHEF76BQ.js.map → chunk-JRZV3767.js.map} +0 -0
  268. /package/dist/{chunk-OS5RKJOH.js.map → chunk-JZ7P26GF.js.map} +0 -0
  269. /package/dist/{chunk-Y62QPRKV.js.map → chunk-K5LLP4CY.js.map} +0 -0
  270. /package/dist/{chunk-C3Z5YJSM.js.map → chunk-LOPZ2DWR.js.map} +0 -0
  271. /package/dist/{chunk-BJPL5WAL.js.map → chunk-MXDG6SRH.js.map} +0 -0
  272. /package/dist/{chunk-UYYSOMS2.js.map → chunk-NBP3YHHS.js.map} +0 -0
  273. /package/dist/{chunk-USHVHI6J.js.map → chunk-NNL4LJAD.js.map} +0 -0
  274. /package/dist/{chunk-GPX5E36D.js.map → chunk-OSMBC3JU.js.map} +0 -0
  275. /package/dist/{chunk-OUSOMUFV.js.map → chunk-PE6XR4GG.js.map} +0 -0
  276. /package/dist/{chunk-CMPXLMC3.js.map → chunk-PI23H7FV.js.map} +0 -0
  277. /package/dist/{chunk-WGDE2TJP.js.map → chunk-QOGXUNWL.js.map} +0 -0
  278. /package/dist/{chunk-ELL5VYXC.js.map → chunk-RNWWAS5V.js.map} +0 -0
  279. /package/dist/{chunk-LD6A7XG5.js.map → chunk-TQPSOTC7.js.map} +0 -0
  280. /package/dist/{chunk-AX4TRSRE.js.map → chunk-U5WVRMCA.js.map} +0 -0
  281. /package/dist/{chunk-DBVBV2RF.js.map → chunk-VE54E6RP.js.map} +0 -0
  282. /package/dist/{chunk-OVYBENIJ.js.map → chunk-W3M34XT4.js.map} +0 -0
  283. /package/dist/{chunk-3TUDKDTQ.js.map → chunk-XEADGIAA.js.map} +0 -0
  284. /package/dist/{contradiction-scan-UERKJESC.js.map → contradiction-scan-3FJYWS3G.js.map} +0 -0
  285. /package/dist/{first-start-migration-GQ3UQNIL.js.map → first-start-migration-4I3VDSYS.js.map} +0 -0
  286. /package/dist/{tier-stats-CSKL7BDG.js.map → tier-stats-5XF4UBRQ.js.map} +0 -0
@@ -104,6 +104,9 @@ import { EntitySynthesisCoordinator } from "./orchestration/entity-synthesis-coo
104
104
  import { RecallResultFormatter } from "./orchestration/recall-result-formatter.js";
105
105
  import { ConversationIndexCoordinator } from "./orchestration/conversation-index-coordinator.js";
106
106
  import { RecallRerankCoordinator } from "./orchestration/recall-rerank-coordinator.js";
107
+ import { RecallSectionCoordinator } from "./orchestration/recall-section-coordinator.js";
108
+ import { QmdResultResolver, qmdCollectionPathParts, qmdResultPathCandidates } from "./orchestration/qmd-result-resolver.js";
109
+ import { ContradictionLinkingCoordinator } from "./orchestration/contradiction-linking-coordinator.js";
107
110
  export { hasIdentityRecoveryIntent, resolveEffectiveIdentityInjectionMode } from "./orchestration/recall-result-formatter.js";
108
111
  import {
109
112
  runLiveConnectorsOnce,
@@ -738,52 +741,6 @@ async function raceRecallAbort<T>(
738
741
  }
739
742
  }
740
743
 
741
- function qmdCollectionPathParts(resultPath: string): {
742
- collection: string;
743
- relativePath: string;
744
- } | null {
745
- if (!resultPath || path.isAbsolute(resultPath)) return null;
746
- const normalized = resultPath.replace(/\\/g, "/").replace(/^\/+/, "");
747
- const slashIndex = normalized.indexOf("/");
748
- if (slashIndex <= 0 || slashIndex >= normalized.length - 1) return null;
749
- const collection = normalized.slice(0, slashIndex);
750
- if (/^\d{4}-\d{2}-\d{2}$/.test(collection)) return null;
751
- return {
752
- collection,
753
- relativePath: normalized.slice(slashIndex + 1),
754
- };
755
- }
756
-
757
- function qmdResultPathCandidates(
758
- storageDir: string,
759
- resultPath: string,
760
- ): string[] {
761
- const candidates = new Set<string>();
762
- const storageRoot = path.resolve(storageDir);
763
- const addCandidate = (candidate: string) => {
764
- const resolved = path.resolve(candidate);
765
- if (isPathInsideStorageRoot(storageRoot, resolved)) {
766
- candidates.add(resolved);
767
- }
768
- };
769
- const addRelativeCandidates = (relativePath: string) => {
770
- const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
771
- if (!normalized) return;
772
- addCandidate(path.join(storageRoot, normalized));
773
- if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
774
- addCandidate(path.join(storageRoot, "facts", normalized));
775
- }
776
- };
777
-
778
- if (path.isAbsolute(resultPath)) {
779
- addCandidate(resultPath);
780
- } else {
781
- addRelativeCandidates(resultPath);
782
- }
783
-
784
- return [...candidates];
785
- }
786
-
787
744
  /** Maximum age (ms) before a compaction-reset signal file is considered stale and removed. */
788
745
  const COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
789
746
  const DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS = 10_000;
@@ -1885,6 +1842,9 @@ export class Orchestrator {
1885
1842
  */
1886
1843
  readonly conversationIndexCoordinator: ConversationIndexCoordinator;
1887
1844
  readonly recallRerankCoordinator: RecallRerankCoordinator;
1845
+ readonly recallSectionCoordinator: RecallSectionCoordinator;
1846
+ readonly qmdResultResolver: QmdResultResolver;
1847
+ readonly contradictionLinkingCoordinator: ContradictionLinkingCoordinator;
1888
1848
  private heartbeatObserverChains = new Map<string, Promise<void>>();
1889
1849
  private recentExtractionFingerprints = new Map<string, number>();
1890
1850
  private readonly consolidationObservers = new Set<
@@ -2885,6 +2845,19 @@ export class Orchestrator {
2885
2845
  readQmdResultMemory: (resultPath, fallbackStorage, recallNamespaces) =>
2886
2846
  this.readQmdResultMemory(resultPath, fallbackStorage, recallNamespaces),
2887
2847
  });
2848
+ this.recallSectionCoordinator = new RecallSectionCoordinator({
2849
+ getConfig: () => this.config,
2850
+ resolveSectionEnabled: (id, def) => this.isRecallSectionEnabled(id, def),
2851
+ });
2852
+ this.contradictionLinkingCoordinator = new ContradictionLinkingCoordinator({
2853
+ getConfig: () => this.config,
2854
+ isSearchAvailable: () => this.isSearchAvailableForNamespaceRouting(),
2855
+ searchAcrossNamespaces: (options) => this.searchAcrossNamespaces(options),
2856
+ extractMemoryIdsFromResults: (results) => this.extractMemoryIdsFromResults(results),
2857
+ namespaceFromPath: (p) => this.namespaceFromPath(p),
2858
+ storageForNamespace: (namespace) => this.storageRouter.storageFor(namespace),
2859
+ getExtraction: () => this.extraction,
2860
+ });
2888
2861
  this.modelRegistry = new ModelRegistry(config.memoryDir);
2889
2862
  this.relevance = new RelevanceStore(config.memoryDir);
2890
2863
  this.negatives = new NegativeExampleStore(config.memoryDir);
@@ -2913,6 +2886,13 @@ export class Orchestrator {
2913
2886
  fastChatCompletion: (messages, options) =>
2914
2887
  this.fastChatCompletion(messages, options),
2915
2888
  });
2889
+ this.qmdResultResolver = new QmdResultResolver({
2890
+ getConfig: () => this.config,
2891
+ storageFor: (namespace) => this.storageRouter.storageFor(namespace),
2892
+ storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
2893
+ qmdCollectionNamespaceFromPrefix: (prefix) => this.qmdCollectionNamespaceFromPrefix(prefix),
2894
+ namespaceFromPath: (p) => this.namespaceFromPath(p),
2895
+ });
2916
2896
 
2917
2897
  this.sessionObserver = new SessionObserverState({
2918
2898
  memoryDir: config.memoryDir,
@@ -6414,52 +6394,46 @@ export class Orchestrator {
6414
6394
  }));
6415
6395
  }
6416
6396
 
6397
+ // Issue #1526 (seam 13): recall section budgeting/assembly moved to
6398
+ // RecallSectionCoordinator. Thin delegation keeps the orchestrator's
6399
+ // recallInternal call sites stable.
6417
6400
  private getRecallSectionEntry(
6418
6401
  sectionId: string,
6419
6402
  ): RecallSectionConfig | undefined {
6420
- const pipeline = Array.isArray(this.config.recallPipeline)
6421
- ? this.config.recallPipeline
6422
- : [];
6423
- return pipeline.find((entry) => entry.id === sectionId);
6403
+ return this.recallSectionCoordinator.getRecallSectionEntry(sectionId);
6424
6404
  }
6425
6405
 
6426
6406
  private isRecallSectionEnabled(
6427
6407
  sectionId: string,
6428
6408
  defaultEnabled: boolean = true,
6429
6409
  ): boolean {
6430
- const entry = this.getRecallSectionEntry(sectionId);
6431
- if (!entry) return defaultEnabled;
6432
- return entry.enabled !== false;
6410
+ return this.recallSectionCoordinator.isRecallSectionEnabled(
6411
+ sectionId,
6412
+ defaultEnabled,
6413
+ );
6433
6414
  }
6434
6415
 
6435
6416
  private isSpecializedRecallSectionEnabled(
6436
6417
  sectionId: string,
6437
6418
  topLevelEnabled: boolean,
6438
6419
  ): boolean {
6439
- const entry = this.getRecallSectionEntry(sectionId);
6440
- if (!entry) return topLevelEnabled;
6441
- return entry.enabled === true || (topLevelEnabled && entry.enabled !== false);
6420
+ return this.recallSectionCoordinator.isSpecializedRecallSectionEnabled(
6421
+ sectionId,
6422
+ topLevelEnabled,
6423
+ );
6442
6424
  }
6443
6425
 
6444
6426
  private getRecallSectionMaxChars(
6445
6427
  sectionId: string,
6446
6428
  ): number | null | undefined {
6447
- const entry = this.getRecallSectionEntry(sectionId);
6448
- if (!entry) return undefined;
6449
- if (entry.maxChars === null) return null;
6450
- if (typeof entry.maxChars !== "number") return undefined;
6451
- return Math.max(0, Math.floor(entry.maxChars));
6429
+ return this.recallSectionCoordinator.getRecallSectionMaxChars(sectionId);
6452
6430
  }
6453
6431
 
6454
6432
  private getRecallSectionNumber(
6455
6433
  sectionId: string,
6456
6434
  key: keyof RecallSectionConfig,
6457
6435
  ): number | undefined {
6458
- const entry = this.getRecallSectionEntry(sectionId);
6459
- if (!entry) return undefined;
6460
- const value = entry[key];
6461
- if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
6462
- return Math.max(0, Math.floor(value));
6436
+ return this.recallSectionCoordinator.getRecallSectionNumber(sectionId, key);
6463
6437
  }
6464
6438
 
6465
6439
  private appendRecallSection(
@@ -6467,104 +6441,25 @@ export class Orchestrator {
6467
6441
  sectionId: string,
6468
6442
  content: string,
6469
6443
  ): boolean {
6470
- // Returns true when the section was actually appended to sectionBuckets,
6471
- // false when it was dropped (disabled, empty, or maxChars===0). Callers
6472
- // that need to know whether injection occurred (e.g. xray annotation for
6473
- // peer-profile) must gate on this return value rather than on whether the
6474
- // section text was computed (Codex P2 finding, PR #764).
6475
- if (!this.isRecallSectionEnabled(sectionId)) return false;
6476
- const trimmed = content.trim();
6477
- if (trimmed.length === 0) return false;
6478
-
6479
- const maxChars = this.getRecallSectionMaxChars(sectionId);
6480
- let finalContent = trimmed;
6481
- if (maxChars === 0) return false;
6482
- if (typeof maxChars === "number" && finalContent.length > maxChars) {
6483
- finalContent = `${finalContent.slice(0, maxChars)}\n\n...(trimmed)\n`;
6484
- }
6485
-
6486
- const existing = sectionBuckets.get(sectionId) ?? [];
6487
- existing.push(finalContent);
6488
- sectionBuckets.set(sectionId, existing);
6489
- return true;
6444
+ return this.recallSectionCoordinator.appendRecallSection(
6445
+ sectionBuckets,
6446
+ sectionId,
6447
+ content,
6448
+ );
6490
6449
  }
6491
6450
 
6492
6451
  private truncateRecallSectionToBudget(
6493
6452
  content: string,
6494
6453
  maxChars: number,
6495
6454
  ): string {
6496
- if (maxChars <= 0) return "";
6497
- if (content.length <= maxChars) return content;
6498
- const suffix = "\n\n...(memory context trimmed)";
6499
- if (maxChars <= suffix.length) {
6500
- return content.slice(0, maxChars);
6501
- }
6502
- return `${content.slice(0, maxChars - suffix.length)}${suffix}`;
6503
- }
6504
-
6505
- private protectedRecallSectionIds(
6506
- sectionBuckets: Map<string, string[]>,
6507
- ): Set<string> {
6508
- const protectedIds = new Set<string>();
6509
- if ((sectionBuckets.get("memories")?.length ?? 0) > 0) {
6510
- protectedIds.add("memories");
6511
- }
6512
- return protectedIds;
6513
- }
6514
-
6515
- private protectedRecallReservationChars(content: string): number {
6516
- const headingBoundary = content.indexOf("\n\n");
6517
- const headingChars =
6518
- headingBoundary >= 0 ? headingBoundary + 2 : Math.min(content.length, 24);
6519
- return Math.min(content.length, Math.max(headingChars, 24));
6520
- }
6521
-
6522
- private estimateReservedRecallBudget(
6523
- entries: Array<{ id: string; content: string }>,
6524
- startIndex: number,
6525
- protectedIds: Set<string>,
6526
- alreadyIncludedCount: number,
6527
- ): number {
6528
- const separatorLength = "\n\n---\n\n".length;
6529
- let reserved = 0;
6530
- let simulatedIncluded = alreadyIncludedCount;
6531
- for (let i = startIndex; i < entries.length; i += 1) {
6532
- const entry = entries[i];
6533
- if (!entry || !protectedIds.has(entry.id)) continue;
6534
- if (simulatedIncluded > 0) {
6535
- reserved += separatorLength;
6536
- }
6537
- reserved += this.protectedRecallReservationChars(entry.content);
6538
- simulatedIncluded += 1;
6539
- }
6540
- return reserved;
6455
+ return this.recallSectionCoordinator.truncateRecallSectionToBudget(
6456
+ content,
6457
+ maxChars,
6458
+ );
6541
6459
  }
6542
6460
 
6543
6461
  private getRecallBudgetChars(override?: number): number {
6544
- if (
6545
- typeof override === "number" &&
6546
- Number.isFinite(override) &&
6547
- override >= 0
6548
- ) {
6549
- return Math.floor(override);
6550
- }
6551
- const configuredBudget = this.config.recallBudgetChars;
6552
- if (
6553
- typeof configuredBudget === "number" &&
6554
- Number.isFinite(configuredBudget) &&
6555
- configuredBudget >= 0
6556
- ) {
6557
- return Math.floor(configuredBudget);
6558
- }
6559
- const tokenBudget = this.config.maxMemoryTokens;
6560
- if (
6561
- typeof tokenBudget === "number" &&
6562
- Number.isFinite(tokenBudget) &&
6563
- tokenBudget >= 0
6564
- ) {
6565
- return Math.floor(tokenBudget * 4);
6566
- }
6567
- return 0;
6462
+ return this.recallSectionCoordinator.getRecallBudgetChars(override);
6568
6463
  }
6569
6464
 
6570
6465
  private assembleRecallSections(
@@ -6577,89 +6472,13 @@ export class Orchestrator {
6577
6472
  truncated: boolean;
6578
6473
  finalChars: number;
6579
6474
  } {
6580
- const orderedEntries: Array<{ id: string; content: string }> = [];
6581
- const pipeline = Array.isArray(this.config.recallPipeline)
6582
- ? this.config.recallPipeline
6583
- : [];
6584
- const orderedIds = pipeline
6585
- .filter((entry) => entry.enabled !== false)
6586
- .map((entry) => entry.id);
6587
- const seen = new Set<string>();
6588
-
6589
- for (const id of orderedIds) {
6590
- const chunks = sectionBuckets.get(id);
6591
- if (!chunks || chunks.length === 0) continue;
6592
- orderedEntries.push({ id, content: chunks.join("\n\n") });
6593
- seen.add(id);
6594
- }
6595
-
6596
- for (const [id, chunks] of sectionBuckets.entries()) {
6597
- if (seen.has(id)) continue;
6598
- if (chunks.length === 0) continue;
6599
- orderedEntries.push({ id, content: chunks.join("\n\n") });
6600
- }
6601
-
6602
- const budget = this.getRecallBudgetChars(budgetOverride);
6603
- if (budget === 0) {
6604
- return {
6605
- sections: [],
6606
- includedIds: [],
6607
- omittedIds: orderedEntries.map((entry) => entry.id),
6608
- truncated: orderedEntries.length > 0,
6609
- finalChars: 0,
6610
- };
6611
- }
6612
-
6613
- const separator = "\n\n---\n\n";
6614
- const protectedIds = this.protectedRecallSectionIds(sectionBuckets);
6615
- const sections: string[] = [];
6616
- const includedIds: string[] = [];
6617
- const omittedIds: string[] = [];
6618
- let usedChars = 0;
6619
- let truncated = false;
6475
+ return this.recallSectionCoordinator.assembleRecallSections(
6476
+ sectionBuckets,
6477
+ budgetOverride,
6478
+ );
6479
+ }
6620
6480
 
6621
- for (let index = 0; index < orderedEntries.length; index += 1) {
6622
- const entry = orderedEntries[index]!;
6623
- const separatorChars = sections.length > 0 ? separator.length : 0;
6624
- const reserve = protectedIds.has(entry.id)
6625
- ? 0
6626
- : this.estimateReservedRecallBudget(
6627
- orderedEntries,
6628
- index + 1,
6629
- protectedIds,
6630
- sections.length + 1,
6631
- );
6632
- const availableForEntry = budget - usedChars - separatorChars - reserve;
6633
- if (availableForEntry <= 0) {
6634
- omittedIds.push(entry.id);
6635
- truncated = true;
6636
- continue;
6637
- }
6638
- const finalContent = this.truncateRecallSectionToBudget(
6639
- entry.content,
6640
- availableForEntry,
6641
- );
6642
- if (!finalContent) {
6643
- omittedIds.push(entry.id);
6644
- truncated = true;
6645
- continue;
6646
- }
6647
- if (finalContent.length < entry.content.length) {
6648
- truncated = true;
6649
- }
6650
- sections.push(finalContent);
6651
- includedIds.push(entry.id);
6652
- usedChars += separatorChars + finalContent.length;
6653
- }
6654
6481
 
6655
- return {
6656
- sections,
6657
- includedIds,
6658
- omittedIds,
6659
- truncated,
6660
- finalChars: usedChars,
6661
- };
6662
- }
6663
6482
 
6664
6483
  /**
6665
6484
  * Clock source for the shared post-retrieval assembly/enrichment budget. The
@@ -16898,143 +16717,14 @@ export class Orchestrator {
16898
16717
  return null;
16899
16718
  }
16900
16719
 
16720
+ // Issue #1526 seam 11: QMD result-resolution methods moved to QmdResultResolver.
16721
+ // Thin delegation keeps the private API stable for callers + tests.
16901
16722
  private async readQmdResultMemory(
16902
16723
  resultPath: string,
16903
16724
  fallbackStorage: StorageManager,
16904
16725
  recallNamespaces: readonly string[] = [],
16905
16726
  ): Promise<MemoryFile | null> {
16906
- const parts = qmdCollectionPathParts(resultPath);
16907
- const storageDirFor = (storage: StorageManager): string | null =>
16908
- typeof (storage as { dir?: unknown }).dir === "string" &&
16909
- (storage as { dir?: string }).dir
16910
- ? (storage as { dir: string }).dir
16911
- : null;
16912
- const fallbackStorageDir = storageDirFor(fallbackStorage);
16913
- const coldCollection = this.config.qmdColdCollection ?? "openclaw-engram-cold";
16914
- if (parts && parts.collection === coldCollection) {
16915
- const storages: StorageManager[] = [];
16916
- const seenStorageDirs = new Set<string>();
16917
- const addStorage = (storage: StorageManager): void => {
16918
- const storageDir = storageDirFor(storage);
16919
- const storageKey = storageDir
16920
- ? path.resolve(storageDir)
16921
- : `storage-without-dir-${storages.length}`;
16922
- if (seenStorageDirs.has(storageKey)) return;
16923
- seenStorageDirs.add(storageKey);
16924
- storages.push(storage);
16925
- };
16926
-
16927
- const fallbackNamespace =
16928
- fallbackStorageDir !== null
16929
- ? this.storageDirNamespace(fallbackStorageDir)
16930
- : this.config.defaultNamespace;
16931
- if (
16932
- recallNamespaces.length === 0 ||
16933
- !resolveNamespaceCapabilities(this.config).namespaces ||
16934
- recallNamespaces.includes(fallbackNamespace)
16935
- ) {
16936
- addStorage(fallbackStorage);
16937
- }
16938
-
16939
- if (recallNamespaces.length > 0) {
16940
- for (const namespace of recallNamespaces) {
16941
- try {
16942
- addStorage(await this.storageRouter.storageFor(namespace));
16943
- } catch (err) {
16944
- log.debug("qmd cold result namespace storage lookup skipped", {
16945
- path: resultPath,
16946
- namespace,
16947
- error: (err as Error).message,
16948
- });
16949
- }
16950
- }
16951
- }
16952
-
16953
- for (const storage of storages) {
16954
- const storageDir = storageDirFor(storage);
16955
- if (!storageDir) {
16956
- const memory = await storage.readMemoryByPath(resultPath);
16957
- if (memory) return memory;
16958
- continue;
16959
- }
16960
- try {
16961
- const coldRoot = path.join(storageDir, "cold");
16962
- for (const candidate of qmdResultPathCandidates(
16963
- coldRoot,
16964
- parts.relativePath,
16965
- )) {
16966
- const memory = await storage.readMemoryByPath(candidate);
16967
- if (memory) return memory;
16968
- }
16969
- } catch (err) {
16970
- if (err instanceof SecureStoreLockedError) throw err;
16971
- log.debug("qmd cold result path lookup failed open", {
16972
- path: resultPath,
16973
- collection: coldCollection,
16974
- error: (err as Error).message,
16975
- });
16976
- }
16977
- }
16978
- return null;
16979
- }
16980
- const collectionNamespace = parts
16981
- ? this.qmdCollectionNamespaceFromPrefix(parts.collection)
16982
- : null;
16983
-
16984
- if (parts && collectionNamespace) {
16985
- try {
16986
- const collectionStorage =
16987
- await this.storageRouter.storageFor(collectionNamespace);
16988
- for (const candidate of qmdResultPathCandidates(
16989
- collectionStorage.dir,
16990
- parts.relativePath,
16991
- )) {
16992
- const memory = await collectionStorage.readMemoryByPath(candidate);
16993
- if (memory) return memory;
16994
- }
16995
- return null;
16996
- } catch (err) {
16997
- if (err instanceof SecureStoreLockedError) throw err;
16998
- log.debug("qmd result namespace path lookup failed open", {
16999
- path: resultPath,
17000
- namespace: collectionNamespace,
17001
- error: (err as Error).message,
17002
- });
17003
- return null;
17004
- }
17005
- }
17006
-
17007
- if (path.isAbsolute(resultPath)) {
17008
- if (!fallbackStorageDir) {
17009
- return await fallbackStorage.readMemoryByPath(resultPath);
17010
- }
17011
- const ownerStorage = await this.storageForAbsoluteQmdResultPath(
17012
- resultPath,
17013
- fallbackStorage,
17014
- recallNamespaces,
17015
- );
17016
- if (!ownerStorage) return null;
17017
- for (const candidate of qmdResultPathCandidates(
17018
- ownerStorage.dir,
17019
- resultPath,
17020
- )) {
17021
- const memory = await ownerStorage.storage.readMemoryByPath(candidate);
17022
- if (memory) return memory;
17023
- }
17024
- return null;
17025
- }
17026
-
17027
- if (!fallbackStorageDir) {
17028
- return await fallbackStorage.readMemoryByPath(resultPath);
17029
- }
17030
- for (const candidate of qmdResultPathCandidates(
17031
- fallbackStorageDir,
17032
- resultPath,
17033
- )) {
17034
- const memory = await fallbackStorage.readMemoryByPath(candidate);
17035
- if (memory) return memory;
17036
- }
17037
- return null;
16727
+ return this.qmdResultResolver.readQmdResultMemory(resultPath, fallbackStorage, recallNamespaces);
17038
16728
  }
17039
16729
 
17040
16730
  private async resolveColdQmdResultForRecall(
@@ -17042,40 +16732,7 @@ export class Orchestrator {
17042
16732
  fallbackStorage: StorageManager,
17043
16733
  recallNamespaces: readonly string[] = [],
17044
16734
  ): Promise<{ namespace: string; result: QmdSearchResult } | null> {
17045
- const memory = await this.readQmdResultMemory(
17046
- result.path,
17047
- fallbackStorage,
17048
- recallNamespaces,
17049
- );
17050
- if (!memory) return null;
17051
-
17052
- let ownerNamespace: string | null = null;
17053
- if (path.isAbsolute(memory.path)) {
17054
- const ownerStorage = await this.storageForAbsoluteQmdResultPath(
17055
- memory.path,
17056
- fallbackStorage,
17057
- recallNamespaces,
17058
- );
17059
- ownerNamespace = ownerStorage?.namespace ?? null;
17060
- if (!ownerNamespace && resolveNamespaceCapabilities(this.config).namespaces) return null;
17061
- }
17062
- ownerNamespace ??= this.namespaceFromPath(memory.path);
17063
- if (
17064
- recallNamespaces.length > 0 &&
17065
- !recallNamespaces.includes(ownerNamespace)
17066
- ) {
17067
- return null;
17068
- }
17069
-
17070
- return {
17071
- namespace: ownerNamespace,
17072
- result: {
17073
- ...result,
17074
- docid: result.docid || memory.frontmatter.id,
17075
- path: memory.path,
17076
- snippet: result.snippet || memory.content.slice(0, 400),
17077
- },
17078
- };
16735
+ return this.qmdResultResolver.resolveColdQmdResultForRecall(result, fallbackStorage, recallNamespaces);
17079
16736
  }
17080
16737
 
17081
16738
  private async storageForAbsoluteQmdResultPath(
@@ -17083,73 +16740,7 @@ export class Orchestrator {
17083
16740
  fallbackStorage: StorageManager,
17084
16741
  recallNamespaces: readonly string[] = [],
17085
16742
  ): Promise<{ storage: StorageManager; dir: string; namespace: string } | null> {
17086
- const resolvedPath = path.resolve(resultPath);
17087
- const memoryRoot = path.resolve(this.config.memoryDir);
17088
- const namespacesRoot = path.join(memoryRoot, "namespaces");
17089
- const fallbackStorageDir =
17090
- typeof (fallbackStorage as { dir?: unknown }).dir === "string" &&
17091
- (fallbackStorage as { dir?: string }).dir
17092
- ? (fallbackStorage as { dir: string }).dir
17093
- : null;
17094
- const matches: Array<{ storage: StorageManager; dir: string; namespace: string }> = [];
17095
- const seenDirs = new Set<string>();
17096
-
17097
- const maybeAddStorage = (storage: StorageManager, namespace: string) => {
17098
- const storageDir =
17099
- typeof (storage as { dir?: unknown }).dir === "string" &&
17100
- (storage as { dir?: string }).dir
17101
- ? (storage as { dir: string }).dir
17102
- : null;
17103
- if (!storageDir) return;
17104
- const candidateRoot = path.resolve(storageDir);
17105
- if (seenDirs.has(candidateRoot)) return;
17106
- if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
17107
- if (
17108
- candidateRoot === memoryRoot &&
17109
- isPathInsideStorageRoot(namespacesRoot, resolvedPath)
17110
- ) {
17111
- return;
17112
- }
17113
- seenDirs.add(candidateRoot);
17114
- matches.push({ storage, dir: candidateRoot, namespace });
17115
- };
17116
-
17117
- const fallbackNamespace =
17118
- fallbackStorageDir !== null
17119
- ? this.storageDirNamespace(fallbackStorageDir)
17120
- : this.config.defaultNamespace;
17121
- maybeAddStorage(fallbackStorage, fallbackNamespace);
17122
-
17123
- const candidateNamespaces = new Set<string>();
17124
- candidateNamespaces.add(this.config.defaultNamespace);
17125
- candidateNamespaces.add(this.config.sharedNamespace);
17126
- for (const ns of recallNamespaces) {
17127
- candidateNamespaces.add(ns);
17128
- }
17129
- if (isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
17130
- const relativeToNamespaces = path.relative(namespacesRoot, resolvedPath);
17131
- const [namespaceSegment] = relativeToNamespaces.split(/[\\/]/);
17132
- if (namespaceSegment) {
17133
- candidateNamespaces.add(
17134
- namespaceIdentityFromToken(namespaceSegment) ?? namespaceSegment,
17135
- );
17136
- }
17137
- }
17138
- for (const policy of this.config.namespacePolicies ?? []) {
17139
- candidateNamespaces.add(policy.name);
17140
- }
17141
-
17142
- for (const ns of candidateNamespaces) {
17143
- if (!ns) continue;
17144
- try {
17145
- maybeAddStorage(await this.storageRouter.storageFor(ns), ns);
17146
- } catch {
17147
- continue;
17148
- }
17149
- }
17150
-
17151
- matches.sort((a, b) => b.dir.length - a.dir.length);
17152
- return matches[0] ?? null;
16743
+ return this.qmdResultResolver.storageForAbsoluteQmdResultPath(resultPath, fallbackStorage, recallNamespaces);
17153
16744
  }
17154
16745
 
17155
16746
  // Issue #1526: recall-rerank methods moved to RecallRerankCoordinator.
@@ -17211,18 +16802,7 @@ export class Orchestrator {
17211
16802
  includedSections?: string[];
17212
16803
  omittedSections?: string[];
17213
16804
  }): LastRecallBudgetSummary {
17214
- return {
17215
- requestedTopK: options.requestedTopK,
17216
- appliedTopK: options.recallResultLimit,
17217
- recallBudgetChars: this.getRecallBudgetChars(),
17218
- maxMemoryTokens: this.config.maxMemoryTokens,
17219
- qmdFetchLimit: options.qmdFetchLimit,
17220
- qmdHybridFetchLimit: options.qmdHybridFetchLimit,
17221
- finalContextChars: options.finalContextChars,
17222
- truncated: options.truncated,
17223
- includedSections: [...(options.includedSections ?? [])],
17224
- omittedSections: [...(options.omittedSections ?? [])],
17225
- };
16805
+ return this.recallSectionCoordinator.buildLastRecallBudgetSummary(options);
17226
16806
  }
17227
16807
 
17228
16808
  private collectLastRecallSources(
@@ -17234,16 +16814,10 @@ export class Orchestrator {
17234
16814
  | "cold_fallback"
17235
16815
  | "recent_scan",
17236
16816
  ): string[] {
17237
- const used = new Set<string>();
17238
- if (recallSource !== "none") {
17239
- used.add(recallSource);
17240
- }
17241
- for (const [sectionId, chunks] of sectionBuckets.entries()) {
17242
- if (chunks.length > 0) {
17243
- used.add(sectionId);
17244
- }
17245
- }
17246
- return [...used];
16817
+ return this.recallSectionCoordinator.collectLastRecallSources(
16818
+ sectionBuckets,
16819
+ recallSource,
16820
+ );
17247
16821
  }
17248
16822
 
17249
16823
  /**
@@ -18603,99 +18177,11 @@ export class Orchestrator {
18603
18177
  supersededCreated: string;
18604
18178
  supersededTags: string[];
18605
18179
  } | null> {
18606
- if (!this.isSearchAvailableForNamespaceRouting()) return null;
18607
-
18608
- // Search for similar memories
18609
- const results = await this.searchAcrossNamespaces({
18610
- query: content,
18611
- namespaces: [namespaceScope],
18612
- maxResults: 5,
18613
- mode: "search",
18614
- });
18615
-
18616
- for (const result of results) {
18617
- // Check similarity threshold
18618
- if (result.score < this.config.contradictionSimilarityThreshold) {
18619
- continue;
18620
- }
18621
-
18622
- // Get the existing memory
18623
- const memoryId = this.extractMemoryIdsFromResults([result])[0];
18624
- if (!memoryId) continue;
18625
-
18626
- const resultNamespace = this.namespaceFromPath(result.path);
18627
- if (resultNamespace !== namespaceScope) continue;
18628
- const resultStorage =
18629
- await this.storageRouter.storageFor(resultNamespace);
18630
- const existingMemory = await resultStorage.getMemoryById(memoryId);
18631
- if (!existingMemory) continue;
18632
-
18633
- // Skip memories already resolved or explicitly forgotten. Other
18634
- // non-active statuses remain valid contradiction candidates.
18635
- if (
18636
- existingMemory.frontmatter.status === "superseded" ||
18637
- existingMemory.frontmatter.status === "forgotten"
18638
- ) {
18639
- continue;
18640
- }
18641
-
18642
- // Verify contradiction with LLM
18643
- const verification = await this.extraction.verifyContradiction(
18644
- { content, category },
18645
- {
18646
- id: existingMemory.frontmatter.id,
18647
- content: existingMemory.content,
18648
- category: existingMemory.frontmatter.category,
18649
- created: existingMemory.frontmatter.created,
18650
- },
18651
- );
18652
-
18653
- if (!verification) continue;
18654
-
18655
- // Check if it's a real contradiction with high confidence
18656
- if (
18657
- verification.isContradiction &&
18658
- verification.confidence >= this.config.contradictionMinConfidence
18659
- ) {
18660
- // When the LLM says the existing memory is newer (whichIsNewer ===
18661
- // "first") the incoming fact is the stale one in both resolve modes —
18662
- // log and continue so the caller never marks contradictionDetected and
18663
- // the semantic-skip gate can discard the outdated write normally.
18664
- if (verification.whichIsNewer === "first") {
18665
- log.info(
18666
- `detected contradiction (confidence: ${verification.confidence}): ${existingMemory.frontmatter.id} vs new memory — existing is newer, incoming fact is stale`,
18667
- );
18668
- continue;
18669
- }
18670
-
18671
- // The new fact is newer than the existing one. When auto-resolve is
18672
- // enabled, the caller retires the old memory AFTER the new write lands
18673
- // and its tombstone status is known (#1645: defer auto-resolve until
18674
- // tombstone status is known — a tombstone-blocked new write must not
18675
- // retire the only active copy, leaving neither memory active). When
18676
- // disabled, the old memory stays active for manual review. The
18677
- // contradiction info is returned in both cases so the caller can apply
18678
- // the deferred retire post-write.
18679
-
18680
- // Return the contradiction info regardless of auto-resolve setting.
18681
- // The caller uses this to set `contradictionDetected=true` which
18682
- // prevents the semantic-skip guard from silently dropping a
18683
- // legitimately contradictory update (the regression this fixes).
18684
- log.info(
18685
- `detected contradiction (confidence: ${verification.confidence}): ${existingMemory.frontmatter.id} vs new memory${this.config.contradictionAutoResolve ? " (auto-resolved)" : " (queued for manual review)"}`,
18686
- );
18687
- return {
18688
- supersededId: existingMemory.frontmatter.id,
18689
- confidence: verification.confidence,
18690
- reason: verification.reasoning,
18691
- supersededPath: existingMemory.path,
18692
- supersededCreated: existingMemory.frontmatter.created,
18693
- supersededTags: existingMemory.frontmatter.tags ?? [],
18694
- };
18695
- }
18696
- }
18697
-
18698
- return null;
18180
+ return this.contradictionLinkingCoordinator.checkForContradiction(
18181
+ content,
18182
+ category,
18183
+ namespaceScope,
18184
+ );
18699
18185
  }
18700
18186
 
18701
18187
  /**
@@ -18718,49 +18204,12 @@ export class Orchestrator {
18718
18204
  newMemoryId: string,
18719
18205
  postWriteGuard: boolean,
18720
18206
  ): Promise<void> {
18721
- // #1645 yG2: clear the pre-write `supersedes` link from a blocked row so
18722
- // it doesn't claim to supersede a still-active memory (best-effort).
18723
- if (postWriteGuard && contradiction && this.config.contradictionAutoResolve) {
18724
- try {
18725
- const blockedRow = await storage.getMemoryById(newMemoryId);
18726
- if (blockedRow?.frontmatter?.supersedes) {
18727
- await storage.writeMemoryFrontmatter(blockedRow, { supersedes: undefined });
18728
- }
18729
- } catch (err) {
18730
- log.warn(
18731
- `contradiction auto-resolve supersedes clear failed for blocked ${newMemoryId}: ${err}`,
18732
- );
18733
- }
18734
- }
18735
- if (
18736
- !contradiction ||
18737
- !this.config.contradictionAutoResolve ||
18738
- postWriteGuard
18739
- ) {
18740
- return;
18741
- }
18742
- try {
18743
- await storage.supersedeMemory(
18744
- contradiction.supersededId,
18745
- newMemoryId,
18746
- contradiction.reason,
18747
- );
18748
- if (
18749
- resolveIndexingCapabilities(this.config).queryAwareIndexing &&
18750
- contradiction.supersededPath
18751
- ) {
18752
- deindexMemory(
18753
- this.config.memoryDir,
18754
- contradiction.supersededPath,
18755
- contradiction.supersededCreated,
18756
- contradiction.supersededTags,
18757
- );
18758
- }
18759
- } catch (err) {
18760
- log.warn(
18761
- `contradiction auto-resolve supersede failed for ${contradiction.supersededId}: ${err}`,
18762
- );
18763
- }
18207
+ return this.contradictionLinkingCoordinator.applyDeferredContradictionResolve(
18208
+ contradiction,
18209
+ storage,
18210
+ newMemoryId,
18211
+ postWriteGuard,
18212
+ );
18764
18213
  }
18765
18214
 
18766
18215
  // ---------------------------------------------------------------------------
@@ -18775,61 +18224,14 @@ export class Orchestrator {
18775
18224
  category: string,
18776
18225
  namespaceScope: string,
18777
18226
  ): Promise<MemoryLink[]> {
18778
- if (!this.isSearchAvailableForNamespaceRouting()) return [];
18779
-
18780
- // Search for related memories
18781
- const results = await this.searchAcrossNamespaces({
18782
- query: content,
18783
- namespaces: [namespaceScope],
18784
- maxResults: 5,
18785
- mode: "search",
18786
- });
18787
- if (results.length === 0) return [];
18788
-
18789
- // Get full memory details for candidates
18790
- const candidates: Array<{ id: string; content: string; category: string }> =
18791
- [];
18792
- for (const result of results) {
18793
- const memoryId = this.extractMemoryIdsFromResults([result])[0];
18794
- if (!memoryId) continue;
18795
-
18796
- const resultNamespace = this.namespaceFromPath(result.path);
18797
- if (resultNamespace !== namespaceScope) continue;
18798
- const resultStorage =
18799
- await this.storageRouter.storageFor(resultNamespace);
18800
- const memory = await resultStorage.getMemoryById(memoryId);
18801
- if (
18802
- memory &&
18803
- memory.frontmatter.status !== "superseded" &&
18804
- memory.frontmatter.status !== "forgotten"
18805
- ) {
18806
- candidates.push({
18807
- id: memory.frontmatter.id,
18808
- content: memory.content,
18809
- category: memory.frontmatter.category,
18810
- });
18811
- }
18812
- }
18813
-
18814
- if (candidates.length === 0) return [];
18815
-
18816
- // Ask LLM for link suggestions
18817
- const suggestions = await this.extraction.suggestLinks(
18818
- { content, category },
18819
- candidates,
18227
+ return this.contradictionLinkingCoordinator.suggestLinksForMemory(
18228
+ content,
18229
+ category,
18230
+ namespaceScope,
18820
18231
  );
18821
-
18822
- if (!suggestions || suggestions.links.length === 0) return [];
18823
-
18824
- // Convert to MemoryLink format
18825
- return suggestions.links.map((link) => ({
18826
- targetId: link.targetId,
18827
- linkType: link.linkType,
18828
- strength: link.strength,
18829
- reason: link.reason || undefined,
18830
- }));
18831
18232
  }
18832
18233
 
18234
+
18833
18235
  private async graphSeedPathRelativeToStorage(
18834
18236
  storage: StorageManager,
18835
18237
  result: QmdSearchResult,