@remnic/core 9.3.733 → 9.3.734

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 (294) hide show
  1. package/dist/access-boundary.d.ts +10 -10
  2. package/dist/access-boundary.js +9 -9
  3. package/dist/access-cli.js +25 -23
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +10 -10
  6. package/dist/access-http.js +14 -14
  7. package/dist/access-mcp.d.ts +10 -10
  8. package/dist/access-mcp.js +13 -13
  9. package/dist/access-operations-batch.js +11 -11
  10. package/dist/access-operations.d.ts +10 -10
  11. package/dist/access-operations.js +12 -12
  12. package/dist/{access-service-Byoe3HsT.d.ts → access-service-rjFfmfKB.d.ts} +4 -4
  13. package/dist/access-service.d.ts +10 -10
  14. package/dist/access-service.js +8 -8
  15. package/dist/access-surface-catalog.d.ts +10 -10
  16. package/dist/action-confidence.d.ts +1 -1
  17. package/dist/active-memory-bridge.d.ts +1 -1
  18. package/dist/active-memory-bridge.js +3 -2
  19. package/dist/active-recall.d.ts +1 -1
  20. package/dist/behavior-learner.d.ts +1 -1
  21. package/dist/behavior-signals.d.ts +1 -1
  22. package/dist/bootstrap.d.ts +9 -9
  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/calibration.d.ts +1 -1
  27. package/dist/capabilities.d.ts +97 -2
  28. package/dist/capabilities.js +11 -3
  29. package/dist/{catalog-DM1nk7cM.d.ts → catalog-DHYXzM4-.d.ts} +2 -2
  30. package/dist/causal-behavior.d.ts +1 -1
  31. package/dist/causal-consolidation.d.ts +1 -1
  32. package/dist/causal-trajectory-graph.d.ts +1 -1
  33. package/dist/{chunk-24D66MMN.js → chunk-2L5RTVMO.js} +7 -4
  34. package/dist/chunk-2L5RTVMO.js.map +1 -0
  35. package/dist/{chunk-KQYGNGON.js → chunk-3QETM5NG.js} +6 -3
  36. package/dist/chunk-3QETM5NG.js.map +1 -0
  37. package/dist/{chunk-ZQUAVZNG.js → chunk-54RCE5Z3.js} +7 -4
  38. package/dist/chunk-54RCE5Z3.js.map +1 -0
  39. package/dist/{chunk-NOU77OHX.js → chunk-5DIKZKOD.js} +2 -2
  40. package/dist/{chunk-CGPHMNL4.js → chunk-5OYHJIJN.js} +10 -6
  41. package/dist/chunk-5OYHJIJN.js.map +1 -0
  42. package/dist/{chunk-LUCARK4W.js → chunk-6U7TES2M.js} +29 -28
  43. package/dist/chunk-6U7TES2M.js.map +1 -0
  44. package/dist/{chunk-FFUCCKPN.js → chunk-7FPJFCXL.js} +10 -7
  45. package/dist/chunk-7FPJFCXL.js.map +1 -0
  46. package/dist/{chunk-PMAQRUN4.js → chunk-CILX2XW7.js} +5 -2
  47. package/dist/chunk-CILX2XW7.js.map +1 -0
  48. package/dist/{chunk-ZVHRSR2R.js → chunk-ET5HXSQL.js} +6 -3
  49. package/dist/chunk-ET5HXSQL.js.map +1 -0
  50. package/dist/{chunk-KSJCXXOU.js → chunk-FAGERSH2.js} +6 -3
  51. package/dist/chunk-FAGERSH2.js.map +1 -0
  52. package/dist/{chunk-FYWUNVFZ.js → chunk-IDIXGGDN.js} +6 -6
  53. package/dist/{chunk-CX77GNMB.js → chunk-IWMGBNXY.js} +6 -3
  54. package/dist/chunk-IWMGBNXY.js.map +1 -0
  55. package/dist/{chunk-GGL34GCW.js → chunk-IZLTOS5L.js} +7 -4
  56. package/dist/chunk-IZLTOS5L.js.map +1 -0
  57. package/dist/{chunk-HHNSLPX7.js → chunk-K7PAILHO.js} +10 -9
  58. package/dist/chunk-K7PAILHO.js.map +1 -0
  59. package/dist/{chunk-UZYLX7M6.js → chunk-LV32K4OP.js} +9 -6
  60. package/dist/chunk-LV32K4OP.js.map +1 -0
  61. package/dist/{chunk-HJWQJUKQ.js → chunk-NDYQIN6C.js} +3 -3
  62. package/dist/{chunk-DKT3X5C3.js → chunk-PEF2XOIV.js} +7 -4
  63. package/dist/chunk-PEF2XOIV.js.map +1 -0
  64. package/dist/{chunk-646B5ERJ.js → chunk-PKO5NJ65.js} +51 -48
  65. package/dist/chunk-PKO5NJ65.js.map +1 -0
  66. package/dist/{chunk-QZHF4BWN.js → chunk-Q5LGXWNG.js} +6 -6
  67. package/dist/{chunk-ESF7TRYJ.js → chunk-SHJGBAIW.js} +37 -2
  68. package/dist/{chunk-ESF7TRYJ.js.map → chunk-SHJGBAIW.js.map} +1 -1
  69. package/dist/{chunk-O4ZIBPIW.js → chunk-T7CGPTFU.js} +3 -3
  70. package/dist/{chunk-PLBIPT6I.js → chunk-TJSQHO4Q.js} +7 -4
  71. package/dist/chunk-TJSQHO4Q.js.map +1 -0
  72. package/dist/{chunk-6TUWSUUD.js → chunk-UF3N7IRP.js} +26 -24
  73. package/dist/chunk-UF3N7IRP.js.map +1 -0
  74. package/dist/{chunk-P3VJCTVE.js → chunk-UH3NYOHN.js} +4 -3
  75. package/dist/chunk-UH3NYOHN.js.map +1 -0
  76. package/dist/{chunk-J6BH2OBA.js → chunk-UK6U5C3N.js} +8 -5
  77. package/dist/chunk-UK6U5C3N.js.map +1 -0
  78. package/dist/{chunk-OTQE66IX.js → chunk-V5OVSGB6.js} +6 -3
  79. package/dist/chunk-V5OVSGB6.js.map +1 -0
  80. package/dist/{chunk-H3MB3F62.js → chunk-WHQ2JB35.js} +4 -3
  81. package/dist/chunk-WHQ2JB35.js.map +1 -0
  82. package/dist/{chunk-NIZQH6AU.js → chunk-WTTVFSCO.js} +8 -7
  83. package/dist/chunk-WTTVFSCO.js.map +1 -0
  84. package/dist/{chunk-OUITHS6V.js → chunk-Z7J3OAPR.js} +357 -325
  85. package/dist/chunk-Z7J3OAPR.js.map +1 -0
  86. package/dist/{cli-Dt-tuiYH.d.ts → cli-XVpmSoQs.d.ts} +3 -3
  87. package/dist/cli.d.ts +11 -11
  88. package/dist/cli.js +20 -20
  89. package/dist/compounding/engine.d.ts +3 -2
  90. package/dist/compounding/engine.js +2 -1
  91. package/dist/compounding/preference-consolidator.d.ts +1 -1
  92. package/dist/compression-optimizer.d.ts +1 -1
  93. package/dist/config.d.ts +1 -1
  94. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  95. package/dist/connectors/codex-materialize.d.ts +1 -1
  96. package/dist/connectors/index.d.ts +1 -1
  97. package/dist/consolidation-provenance-check.d.ts +1 -1
  98. package/dist/consolidation-undo.d.ts +1 -1
  99. package/dist/contradiction/index.d.ts +2 -2
  100. package/dist/contradiction/index.js +2 -1
  101. package/dist/{contradiction-scan-HWGEOUDS.js → contradiction-scan-OUHMCUBT.js} +3 -2
  102. package/dist/conversation-index/backend.d.ts +1 -1
  103. package/dist/conversation-index/chunker.d.ts +1 -1
  104. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  105. package/dist/conversation-index/indexer.d.ts +1 -1
  106. package/dist/conversation-index/search.d.ts +1 -1
  107. package/dist/day-summary.d.ts +1 -1
  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 -1
  116. package/dist/entity-schema.d.ts +1 -1
  117. package/dist/explicit-capture.d.ts +9 -9
  118. package/dist/explicit-capture.js +2 -1
  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 +3 -3
  125. package/dist/fallback-llm.d.ts +1 -1
  126. package/dist/{first-start-migration-OOR57AYR.js → first-start-migration-W4U6XDKI.js} +7 -6
  127. package/dist/first-start-migration-W4U6XDKI.js.map +1 -0
  128. package/dist/graph-dashboard-diff.d.ts +1 -1
  129. package/dist/graph-dashboard-key.d.ts +1 -1
  130. package/dist/graph-dashboard-parser.d.ts +1 -1
  131. package/dist/graph-edge-reinforcement.d.ts +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 +12 -12
  137. package/dist/index.js +43 -43
  138. package/dist/intent.d.ts +1 -1
  139. package/dist/lcm/engine.d.ts +1 -1
  140. package/dist/lcm/index.d.ts +1 -1
  141. package/dist/lcm/tools.d.ts +1 -1
  142. package/dist/lifecycle.d.ts +1 -1
  143. package/dist/live-connectors-runner.d.ts +1 -1
  144. package/dist/local-llm.d.ts +1 -1
  145. package/dist/local-llm.js +2 -1
  146. package/dist/local-model-endpoint.d.ts +1 -1
  147. package/dist/maintenance/memory-governance.d.ts +1 -1
  148. package/dist/mcp-memory-inspector-app.d.ts +10 -10
  149. package/dist/memory-action-policy.d.ts +1 -1
  150. package/dist/memory-cache.d.ts +1 -1
  151. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  152. package/dist/memory-projection-store.d.ts +1 -1
  153. package/dist/memory-provenance.d.ts +1 -1
  154. package/dist/memory-worth-outcomes.d.ts +1 -1
  155. package/dist/models-json.d.ts +1 -1
  156. package/dist/namespaces/migrate.d.ts +2 -2
  157. package/dist/namespaces/migrate.js +6 -6
  158. package/dist/namespaces/principal.d.ts +1 -1
  159. package/dist/namespaces/principal.js +2 -1
  160. package/dist/namespaces/search.d.ts +1 -1
  161. package/dist/namespaces/search.js +4 -4
  162. package/dist/namespaces/storage.d.ts +2 -2
  163. package/dist/namespaces/storage.js +2 -1
  164. package/dist/native-knowledge.d.ts +1 -1
  165. package/dist/operator-toolkit.d.ts +1 -1
  166. package/dist/operator-toolkit.js +11 -11
  167. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  168. package/dist/orchestration/maintenance.d.ts +2 -2
  169. package/dist/orchestration/maintenance.js +6 -5
  170. package/dist/{orchestrator-B4KjaOtY.d.ts → orchestrator-BXi-gadS.d.ts} +66 -5
  171. package/dist/orchestrator.d.ts +5 -5
  172. package/dist/orchestrator.js +20 -18
  173. package/dist/patterns-cli.d.ts +1 -1
  174. package/dist/policy-runtime.d.ts +1 -1
  175. package/dist/provenance.d.ts +1 -1
  176. package/dist/qmd-recall-cache.d.ts +1 -1
  177. package/dist/qmd.d.ts +1 -1
  178. package/dist/recall-disclosure-escalation.d.ts +1 -1
  179. package/dist/recall-explain-renderer.d.ts +1 -1
  180. package/dist/recall-planner-llm.d.ts +1 -1
  181. package/dist/recall-state.d.ts +1 -1
  182. package/dist/recall-tag-filter.d.ts +1 -1
  183. package/dist/recall-xray-cli.d.ts +1 -1
  184. package/dist/recall-xray-renderer.d.ts +1 -1
  185. package/dist/recall-xray.d.ts +1 -1
  186. package/dist/resolve-auth-token.d.ts +1 -1
  187. package/dist/retrieval-agents.d.ts +1 -1
  188. package/dist/retrieval-tiers.d.ts +1 -1
  189. package/dist/routing/engine.d.ts +1 -1
  190. package/dist/routing/store.d.ts +1 -1
  191. package/dist/schemas.d.ts +22 -22
  192. package/dist/search/embed-helper.d.ts +1 -1
  193. package/dist/search/embed-helper.js +2 -2
  194. package/dist/search/factory.d.ts +1 -1
  195. package/dist/search/factory.js +3 -3
  196. package/dist/search/index.d.ts +1 -1
  197. package/dist/search/index.js +3 -3
  198. package/dist/search/lancedb-backend.d.ts +1 -1
  199. package/dist/search/meilisearch-backend.d.ts +1 -1
  200. package/dist/search/noop-backend.d.ts +1 -1
  201. package/dist/search/orama-backend.d.ts +1 -1
  202. package/dist/search/port.d.ts +1 -1
  203. package/dist/search/remote-backend.d.ts +1 -1
  204. package/dist/{semantic-SLAa_prH.d.ts → semantic-DJR8_DMQ.d.ts} +1 -1
  205. package/dist/{semantic-consolidation-CstTvHPo.d.ts → semantic-consolidation-DeL5grLx.d.ts} +1 -1
  206. package/dist/semantic-consolidation.d.ts +2 -2
  207. package/dist/semantic-rule-verifier.d.ts +1 -1
  208. package/dist/session-observer-bands.d.ts +1 -1
  209. package/dist/session-observer-state.d.ts +1 -1
  210. package/dist/shared-context/manager.d.ts +1 -1
  211. package/dist/signal.d.ts +1 -1
  212. package/dist/storage.d.ts +1 -1
  213. package/dist/summarizer.d.ts +1 -1
  214. package/dist/summarizer.js +3 -2
  215. package/dist/summary-snapshot.d.ts +1 -1
  216. package/dist/temporal-supersession.d.ts +1 -1
  217. package/dist/temporal-validity.d.ts +1 -1
  218. package/dist/threading.d.ts +1 -1
  219. package/dist/tier-migration.d.ts +1 -1
  220. package/dist/tier-routing.d.ts +1 -1
  221. package/dist/{tier-stats-3LYQ3VV5.js → tier-stats-LSM35HZY.js} +5 -2
  222. package/dist/tier-stats-LSM35HZY.js.map +1 -0
  223. package/dist/topics.d.ts +1 -1
  224. package/dist/transcript.d.ts +1 -1
  225. package/dist/transfer/types.d.ts +12 -12
  226. package/dist/trust-score-stage.d.ts +1 -1
  227. package/dist/trust-score.d.ts +1 -1
  228. package/dist/{types-VkxElCHR.d.ts → types-CpFz51Pv.d.ts} +1 -1
  229. package/dist/types.d.ts +1 -1
  230. package/dist/utility-runtime.d.ts +1 -1
  231. package/package.json +2 -2
  232. package/src/access-service.ts +42 -37
  233. package/src/active-memory-bridge.ts +2 -1
  234. package/src/admin/admin-surfaces.ts +2 -1
  235. package/src/capabilities.test.ts +178 -0
  236. package/src/capabilities.ts +197 -0
  237. package/src/cli.ts +13 -9
  238. package/src/compounding/engine.ts +4 -3
  239. package/src/contradiction/contradiction-scan.ts +4 -3
  240. package/src/embedding-fallback.ts +5 -2
  241. package/src/entity-retrieval.ts +3 -2
  242. package/src/explicit-capture.ts +2 -1
  243. package/src/extraction.ts +6 -3
  244. package/src/local-llm.ts +4 -3
  245. package/src/maintenance/first-start-migration.ts +6 -3
  246. package/src/maintenance/namespace-maintenance-fanout.ts +2 -1
  247. package/src/maintenance/namespace-planner.ts +4 -3
  248. package/src/maintenance/tier-stats.ts +2 -1
  249. package/src/namespaces/catalog.ts +2 -1
  250. package/src/namespaces/migrate.ts +2 -1
  251. package/src/namespaces/principal.ts +6 -5
  252. package/src/namespaces/scope-profiles.ts +2 -1
  253. package/src/namespaces/search.ts +2 -1
  254. package/src/namespaces/storage.ts +4 -3
  255. package/src/operator-toolkit.ts +19 -18
  256. package/src/orchestration/maintenance.ts +8 -4
  257. package/src/orchestration/semantic-consolidation-coordinator.ts +454 -0
  258. package/src/orchestration/tier-migration-coordinator.ts +5 -4
  259. package/src/orchestrator.ts +78 -417
  260. package/src/scopes/scope-plan.ts +3 -2
  261. package/src/search/embed-helper.ts +5 -2
  262. package/src/search/factory.ts +9 -6
  263. package/src/summarizer.ts +3 -2
  264. package/dist/chunk-24D66MMN.js.map +0 -1
  265. package/dist/chunk-646B5ERJ.js.map +0 -1
  266. package/dist/chunk-6TUWSUUD.js.map +0 -1
  267. package/dist/chunk-CGPHMNL4.js.map +0 -1
  268. package/dist/chunk-CX77GNMB.js.map +0 -1
  269. package/dist/chunk-DKT3X5C3.js.map +0 -1
  270. package/dist/chunk-FFUCCKPN.js.map +0 -1
  271. package/dist/chunk-GGL34GCW.js.map +0 -1
  272. package/dist/chunk-H3MB3F62.js.map +0 -1
  273. package/dist/chunk-HHNSLPX7.js.map +0 -1
  274. package/dist/chunk-J6BH2OBA.js.map +0 -1
  275. package/dist/chunk-KQYGNGON.js.map +0 -1
  276. package/dist/chunk-KSJCXXOU.js.map +0 -1
  277. package/dist/chunk-LUCARK4W.js.map +0 -1
  278. package/dist/chunk-NIZQH6AU.js.map +0 -1
  279. package/dist/chunk-OTQE66IX.js.map +0 -1
  280. package/dist/chunk-OUITHS6V.js.map +0 -1
  281. package/dist/chunk-P3VJCTVE.js.map +0 -1
  282. package/dist/chunk-PLBIPT6I.js.map +0 -1
  283. package/dist/chunk-PMAQRUN4.js.map +0 -1
  284. package/dist/chunk-UZYLX7M6.js.map +0 -1
  285. package/dist/chunk-ZQUAVZNG.js.map +0 -1
  286. package/dist/chunk-ZVHRSR2R.js.map +0 -1
  287. package/dist/first-start-migration-OOR57AYR.js.map +0 -1
  288. package/dist/tier-stats-3LYQ3VV5.js.map +0 -1
  289. /package/dist/{chunk-NOU77OHX.js.map → chunk-5DIKZKOD.js.map} +0 -0
  290. /package/dist/{chunk-FYWUNVFZ.js.map → chunk-IDIXGGDN.js.map} +0 -0
  291. /package/dist/{chunk-HJWQJUKQ.js.map → chunk-NDYQIN6C.js.map} +0 -0
  292. /package/dist/{chunk-QZHF4BWN.js.map → chunk-Q5LGXWNG.js.map} +0 -0
  293. /package/dist/{chunk-O4ZIBPIW.js.map → chunk-T7CGPTFU.js.map} +0 -0
  294. /package/dist/{contradiction-scan-HWGEOUDS.js.map → contradiction-scan-OUHMCUBT.js.map} +0 -0
@@ -101,6 +101,7 @@ import { MaintenanceScheduler } from "./orchestration/maintenance.js";
101
101
  import { TierMigrationCoordinator } from "./orchestration/tier-migration-coordinator.js";
102
102
  import { ExtractionQueueCoordinator } from "./orchestration/extraction-queue-coordinator.js";
103
103
  import { CompressionGuidelineCoordinator } from "./orchestration/compression-guideline-coordinator.js";
104
+ import { SemanticConsolidationCoordinator } from "./orchestration/semantic-consolidation-coordinator.js";
104
105
  import {
105
106
  runLiveConnectorsOnce,
106
107
  type LiveConnectorsRunSummary,
@@ -266,6 +267,7 @@ import {
266
267
  } from "./trust-zones.js";
267
268
  import { tryDirectAnswer, type DirectAnswerSources } from "./direct-answer-wiring.js";
268
269
  import {
270
+ resolveNamespaceCapabilities,
269
271
  resolveCapabilities,
270
272
  resolveGraphConstructionCapabilities,
271
273
  resolveMemoryLifecycleCapabilities,
@@ -276,6 +278,9 @@ import {
276
278
  type MemoryLifecycleCapabilitySet,
277
279
  type IndexingCapabilitySet,
278
280
  type CreationMemoryCapabilitySet,
281
+ resolveQmdCapabilities,
282
+ resolveIdentityContinuityCapabilities,
283
+ resolveLocalLlmCapabilities,
279
284
  } from "./capabilities.js";
280
285
  import { DEFAULT_TAXONOMY } from "./taxonomy/index.js";
281
286
  import {
@@ -1896,6 +1901,7 @@ export class Orchestrator {
1896
1901
  readonly tierMigrationStatus: TierMigrationStatusStore;
1897
1902
  readonly tierMigrationCoordinator: TierMigrationCoordinator;
1898
1903
  readonly compressionGuidelineCoordinator: CompressionGuidelineCoordinator;
1904
+ readonly semanticConsolidationCoordinator: SemanticConsolidationCoordinator;
1899
1905
  /**
1900
1906
  * In-memory X-ray snapshot from the most recent `recall()` call that
1901
1907
  * was invoked with `xrayCapture: true` (issue #570 PR 1). Scope is
@@ -2251,7 +2257,7 @@ export class Orchestrator {
2251
2257
  * @internal
2252
2258
  */
2253
2259
  applyCodingNamespaceOverlay(sessionKey: string | undefined, baseNamespace: string): string {
2254
- if (!this.config.namespacesEnabled) return baseNamespace;
2260
+ if (!resolveNamespaceCapabilities(this.config).namespaces) return baseNamespace;
2255
2261
  const codingContext = this.getCodingContextForSession(sessionKey);
2256
2262
  const overlay = resolveCodingNamespaceOverlay(codingContext, this.config.codingMode, this.config.defaultNamespace);
2257
2263
  if (!overlay) return baseNamespace;
@@ -2331,7 +2337,7 @@ export class Orchestrator {
2331
2337
  * @internal
2332
2338
  */
2333
2339
  applyCodingRecallOverlay(sessionKey: string | undefined): { namespace: string; readFallbacks: string[] } | null {
2334
- if (!this.config.namespacesEnabled) return null;
2340
+ if (!resolveNamespaceCapabilities(this.config).namespaces) return null;
2335
2341
  const codingContext = this.getCodingContextForSession(sessionKey);
2336
2342
  const overlay = resolveCodingNamespaceOverlay(codingContext, this.config.codingMode, this.config.defaultNamespace);
2337
2343
  if (!overlay) return null;
@@ -2354,7 +2360,7 @@ export class Orchestrator {
2354
2360
  }
2355
2361
 
2356
2362
  private rememberNamespaceStorageDirHint(namespace: string, storageDir?: string): void {
2357
- if (!this.config.namespacesEnabled || !storageDir) return;
2363
+ if (!resolveNamespaceCapabilities(this.config).namespaces || !storageDir) return;
2358
2364
  const ns = normalizeNamespaceIdentity(namespace);
2359
2365
  if (!ns) return;
2360
2366
  const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
@@ -2558,10 +2564,10 @@ export class Orchestrator {
2558
2564
  private buildConfiguredQmdSearchOptions(
2559
2565
  queryText: string,
2560
2566
  ): SearchQueryOptions | undefined {
2561
- const intentHint = this.config.qmdIntentHintsEnabled
2567
+ const intentHint = resolveQmdCapabilities(this.config).qmdIntentHints
2562
2568
  ? buildQmdIntentHint(inferIntentFromText(queryText))
2563
2569
  : undefined;
2564
- const explain = this.config.qmdExplainEnabled === true;
2570
+ const explain = resolveQmdCapabilities(this.config).qmdExplain === true;
2565
2571
  const searchOptions: SearchQueryOptions = {};
2566
2572
  if (intentHint) {
2567
2573
  searchOptions.intent = intentHint;
@@ -2581,13 +2587,13 @@ export class Orchestrator {
2581
2587
  execution?: SearchExecutionOptions;
2582
2588
  }): Promise<QmdSearchResult[]> {
2583
2589
  if (
2584
- this.config.namespacesEnabled &&
2590
+ resolveNamespaceCapabilities(this.config).namespaces &&
2585
2591
  options.namespaces !== undefined &&
2586
2592
  options.namespaces.length === 0
2587
2593
  ) {
2588
2594
  return [];
2589
2595
  }
2590
- const namespaces = this.config.namespacesEnabled
2596
+ const namespaces = resolveNamespaceCapabilities(this.config).namespaces
2591
2597
  ? Array.from(
2592
2598
  new Set(
2593
2599
  (options.namespaces?.length
@@ -2600,7 +2606,7 @@ export class Orchestrator {
2600
2606
  )
2601
2607
  : [this.config.defaultNamespace];
2602
2608
 
2603
- if (!this.config.namespacesEnabled) {
2609
+ if (!resolveNamespaceCapabilities(this.config).namespaces) {
2604
2610
  switch (options.mode) {
2605
2611
  case "hybrid":
2606
2612
  return await this.qmd.hybridSearch(
@@ -2652,7 +2658,7 @@ export class Orchestrator {
2652
2658
  }
2653
2659
 
2654
2660
  private isSearchAvailableForNamespaceRouting(): boolean {
2655
- if (this.config.namespacesEnabled) return true;
2661
+ if (resolveNamespaceCapabilities(this.config).namespaces) return true;
2656
2662
  return this.qmd.isAvailable();
2657
2663
  }
2658
2664
 
@@ -3019,6 +3025,7 @@ export class Orchestrator {
3019
3025
  fastChatCompletion: (messages, options) =>
3020
3026
  this.fastChatCompletion(messages, options),
3021
3027
  });
3028
+
3022
3029
  this.sessionObserver = new SessionObserverState({
3023
3030
  memoryDir: config.memoryDir,
3024
3031
  debounceMs: config.sessionObserverDebounceMs ?? 120_000,
@@ -3061,6 +3068,15 @@ export class Orchestrator {
3061
3068
  return client;
3062
3069
  })()
3063
3070
  : this.localLlm;
3071
+ this.semanticConsolidationCoordinator = new SemanticConsolidationCoordinator({
3072
+ config,
3073
+ getStorage: () => this.storage,
3074
+ getFastLlm: () => this.fastLlm,
3075
+ embeddingFallback: this.embeddingFallback,
3076
+ removeContentHashForMemory: (targetStorage, memory, context) =>
3077
+ this.removeContentHashForMemory(targetStorage, memory, context),
3078
+ saveContentHashIndexes: () => this.saveContentHashIndexes(),
3079
+ });
3064
3080
  // Initialize gateway fast LLM for fast-tier ops when modelSource is "gateway"
3065
3081
  this._fastGatewayLlm = config.modelSource === "gateway"
3066
3082
  ? new FallbackLlmClient(
@@ -3224,7 +3240,7 @@ export class Orchestrator {
3224
3240
  }
3225
3241
 
3226
3242
  private routeEngineOptions(): RoutingEngineOptions {
3227
- const allowedNamespaces = this.config.namespacesEnabled
3243
+ const allowedNamespaces = resolveNamespaceCapabilities(this.config).namespaces
3228
3244
  ? Array.from(
3229
3245
  new Set([
3230
3246
  this.config.defaultNamespace,
@@ -3418,7 +3434,7 @@ export class Orchestrator {
3418
3434
  ? this._fastGatewayLlm?.isAvailable(
3419
3435
  this.config.fastGatewayAgentId || this.config.gatewayAgentId || undefined,
3420
3436
  ) === true
3421
- : this.config.localLlmEnabled;
3437
+ : resolveLocalLlmCapabilities(this.config).localLlm;
3422
3438
  if (!chainAvailable) return undefined;
3423
3439
  return buildChainFollowupGenerator(this.fastLlmForRerank);
3424
3440
  }
@@ -3440,7 +3456,7 @@ export class Orchestrator {
3440
3456
  });
3441
3457
  await this.storage.ensureDirectories();
3442
3458
  await this.storage.loadAliases();
3443
- if (this.config.namespacesEnabled) {
3459
+ if (resolveNamespaceCapabilities(this.config).namespaces) {
3444
3460
  const namespaces = new Set<string>([
3445
3461
  this.config.defaultNamespace,
3446
3462
  this.config.sharedNamespace,
@@ -3560,14 +3576,14 @@ export class Orchestrator {
3560
3576
  // after a restart. `registerConfiguredNamespaces()` already seeded the
3561
3577
  // catalog above, so `maintenanceNamespaces()` is readable here; it falls
3562
3578
  // back to the configured set on any catalog read failure.
3563
- const namespaces = this.config.namespacesEnabled
3579
+ const namespaces = resolveNamespaceCapabilities(this.config).namespaces
3564
3580
  ? await this.maintenanceNamespaces()
3565
3581
  : [this.config.defaultNamespace];
3566
3582
  const states = await Promise.all(
3567
3583
  namespaces.map(async (namespace) => {
3568
3584
  const collectionCheckAbort = new AbortController();
3569
3585
  const state = await qmdStartupCollectionCheckWithTimeout(
3570
- this.config.namespacesEnabled
3586
+ resolveNamespaceCapabilities(this.config).namespaces
3571
3587
  ? this.namespaceSearchRouter.ensureNamespaceCollection(
3572
3588
  namespace,
3573
3589
  { signal: collectionCheckAbort.signal },
@@ -3681,10 +3697,10 @@ export class Orchestrator {
3681
3697
  // triggered update — which can be days ago if the daemon restarted without
3682
3698
  // new extractions. This is the root cause of "0 memories" recall results
3683
3699
  // despite thousands of facts on disk.
3684
- if (this.qmd.isAvailable() && this.config.qmdMaintenanceEnabled) {
3700
+ if (this.qmd.isAvailable() && resolveQmdCapabilities(this.config).qmdMaintenance) {
3685
3701
  try {
3686
3702
  log.info("QMD startup sync: updating index to match current disk state");
3687
- if (this.config.namespacesEnabled) {
3703
+ if (resolveNamespaceCapabilities(this.config).namespaces) {
3688
3704
  // Cover cataloged dynamic namespaces at startup too (NHZEV, codex P2):
3689
3705
  // a dynamic namespace written before a daemon restart must be synced on
3690
3706
  // boot, not only by the debounced runQmdMaintenance() path. Same union +
@@ -3788,7 +3804,7 @@ export class Orchestrator {
3788
3804
 
3789
3805
  if (signal.aborted) return;
3790
3806
 
3791
- if (this.config.localLlmEnabled) {
3807
+ if (resolveLocalLlmCapabilities(this.config).localLlm) {
3792
3808
  try {
3793
3809
  await this.validateLocalLlmModel();
3794
3810
  } catch (err) {
@@ -3813,7 +3829,7 @@ export class Orchestrator {
3813
3829
  // sweep (capped at 50 demotions) so the hot tier isn't flooded on the
3814
3830
  // first real cron pass. Non-fatal — a failure here must not break init.
3815
3831
  if (signal.aborted) return;
3816
- if (lifecycleCaps.lifecyclePolicy && this.config.qmdTierMigrationEnabled) {
3832
+ if (lifecycleCaps.lifecyclePolicy && resolveQmdCapabilities(this.config).qmdTierMigration) {
3817
3833
  try {
3818
3834
  const { runFirstStartMigration } = await import(
3819
3835
  "./maintenance/first-start-migration.js"
@@ -3918,7 +3934,7 @@ export class Orchestrator {
3918
3934
  log.info(`startupSearchSync: backend now available ${this.qmd.debugStatus()}`);
3919
3935
 
3920
3936
  // Clear namespace router cache so re-probe picks up newly available backends
3921
- if (this.config.namespacesEnabled) {
3937
+ if (resolveNamespaceCapabilities(this.config).namespaces) {
3922
3938
  this.namespaceSearchRouter.clearCache();
3923
3939
  }
3924
3940
 
@@ -3930,14 +3946,14 @@ export class Orchestrator {
3930
3946
  // re-synced here too, otherwise after a backend-was-unavailable-at-boot
3931
3947
  // recovery its collection stays stale. Falls back to the configured set on any
3932
3948
  // catalog read failure.
3933
- const namespaces = this.config.namespacesEnabled
3949
+ const namespaces = resolveNamespaceCapabilities(this.config).namespaces
3934
3950
  ? await this.maintenanceNamespaces()
3935
3951
  : [this.config.defaultNamespace];
3936
3952
 
3937
3953
  const states = await Promise.all(
3938
3954
  namespaces.map(async (namespace) => ({
3939
3955
  namespace,
3940
- state: this.config.namespacesEnabled
3956
+ state: resolveNamespaceCapabilities(this.config).namespaces
3941
3957
  ? await this.namespaceSearchRouter.ensureNamespaceCollection(namespace, { signal })
3942
3958
  : await this.qmd.ensureCollection(this.config.memoryDir, this.config.qmdCollection, { signal }),
3943
3959
  })),
@@ -3972,7 +3988,7 @@ export class Orchestrator {
3972
3988
  // and didn't fail silently.
3973
3989
  // The abort signal is forwarded into the QMD subprocess call so the
3974
3990
  // long-running `qmd update` process is killed promptly on shutdown.
3975
- if (this.config.qmdMaintenanceEnabled) {
3991
+ if (resolveQmdCapabilities(this.config).qmdMaintenance) {
3976
3992
  try {
3977
3993
  const failTsBefore = "lastUpdateFailedAtMs" in this.qmd
3978
3994
  ? (this.qmd as any).lastUpdateFailedAtMs as number | null
@@ -3983,7 +3999,7 @@ export class Orchestrator {
3983
3999
  }
3984
4000
  log.info("startupSearchSync: updating index to match current disk state");
3985
4001
  let namespacesUpdated = 0;
3986
- if (this.config.namespacesEnabled) {
4002
+ if (resolveNamespaceCapabilities(this.config).namespaces) {
3987
4003
  namespacesUpdated = await this.namespaceSearchRouter.updateNamespaces(
3988
4004
  namespaces,
3989
4005
  { signal },
@@ -4005,7 +4021,7 @@ export class Orchestrator {
4005
4021
  log.warn("startupSearchSync: update silently failed (detected via fail timestamp)");
4006
4022
  return false;
4007
4023
  }
4008
- if (this.config.namespacesEnabled) {
4024
+ if (resolveNamespaceCapabilities(this.config).namespaces) {
4009
4025
  if (namespacesUpdated === 0) {
4010
4026
  log.warn("startupSearchSync: no namespace backends were eligible for update (all unavailable or collections missing)");
4011
4027
  return false;
@@ -4374,372 +4390,17 @@ export class Orchestrator {
4374
4390
  return summarizeGovernanceResultForDreams(govResult, options?.dryRun === true);
4375
4391
  }
4376
4392
 
4393
+ // Issue #1526 (seam 5): semantic consolidation moved to
4394
+ // SemanticConsolidationCoordinator. Thin delegation keeps all
4395
+ // call sites (runConsolidation, runSemanticConsolidationNow,
4396
+ // runSemanticConsolidationFanout) stable.
4377
4397
  private async runSemanticConsolidation(options?: {
4378
4398
  dryRun?: boolean;
4379
4399
  thresholdOverride?: number;
4380
4400
  force?: boolean;
4381
4401
  storage?: StorageManager;
4382
4402
  }): Promise<SemanticConsolidationResult> {
4383
- const targetStorage = options?.storage ?? this.storage;
4384
- const result: SemanticConsolidationResult = {
4385
- clustersFound: 0,
4386
- memoriesConsolidated: 0,
4387
- memoriesArchived: 0,
4388
- errors: 0,
4389
- clusters: [],
4390
- };
4391
-
4392
- if (!this.config.semanticConsolidationEnabled && !options?.force) {
4393
- log.debug("[semantic-consolidation] disabled in config");
4394
- return result;
4395
- }
4396
-
4397
- log.info("[semantic-consolidation] starting run");
4398
-
4399
- const allMemories = await targetStorage.readAllMemories();
4400
- if (allMemories.length < 10) {
4401
- log.debug("[semantic-consolidation] too few memories, skipping");
4402
- return result;
4403
- }
4404
-
4405
- const threshold =
4406
- options?.thresholdOverride ?? this.config.semanticConsolidationThreshold;
4407
- const clusters = findSimilarClusters(allMemories, {
4408
- threshold,
4409
- minClusterSize: this.config.semanticConsolidationMinClusterSize,
4410
- excludeCategories: this.config.semanticConsolidationExcludeCategories,
4411
- maxPerRun: this.config.semanticConsolidationMaxPerRun,
4412
- });
4413
-
4414
- result.clustersFound = clusters.length;
4415
- result.clusters = clusters;
4416
-
4417
- if (clusters.length === 0) {
4418
- log.info("[semantic-consolidation] no clusters found");
4419
- return result;
4420
- }
4421
-
4422
- log.info(`[semantic-consolidation] found ${clusters.length} cluster(s)`);
4423
-
4424
- if (options?.dryRun) {
4425
- log.info(
4426
- "[semantic-consolidation] dry run — skipping LLM synthesis and archival",
4427
- );
4428
- return result;
4429
- }
4430
-
4431
- // Use FallbackLlmClient for LLM calls (same pattern as causal-consolidation.ts)
4432
- // Honor semanticConsolidationModel: "auto" = primary, "fast" = local fast, or specific model
4433
- const { FallbackLlmClient, gatewayTaskChainOptions } = await import("./fallback-llm.js");
4434
- const useGateway = this.config.modelSource === "gateway";
4435
- const modelSetting = this.config.semanticConsolidationModel;
4436
- if (modelSetting === "fast" && this.fastLlm && !useGateway) {
4437
- log.info("[semantic-consolidation] using fast local LLM for synthesis");
4438
- }
4439
- // Gateway routing: an explicit "fast" setting keeps the fast persona chain
4440
- // (the operator's deliberate fast-tier choice). Otherwise route through the
4441
- // shared task-chain resolution so taskModelChain applies to semantic
4442
- // consolidation like every other background task (gotcha #22). Issue #1365.
4443
- const gatewayChainOptions: { modelChain?: AgentPersonaModelConfig; agentId?: string } =
4444
- !useGateway
4445
- ? {}
4446
- : modelSetting === "fast"
4447
- ? (this.config.fastGatewayAgentId
4448
- ? { agentId: this.config.fastGatewayAgentId }
4449
- : this.config.gatewayAgentId
4450
- ? { agentId: this.config.gatewayAgentId }
4451
- : {})
4452
- : gatewayTaskChainOptions(this.config);
4453
- const llm = new FallbackLlmClient(
4454
- this.config.gatewayConfig,
4455
- fallbackLlmRuntimeContextFromConfig(this.config),
4456
- );
4457
- if (!llm.isAvailable(gatewayChainOptions) && !(modelSetting === "fast" && this.fastLlm && !useGateway)) {
4458
- log.warn(
4459
- "[semantic-consolidation] no LLM available — skipping synthesis",
4460
- );
4461
- return result;
4462
- }
4463
-
4464
- // Discover memory extensions once for all clusters (#382)
4465
- let extensionsBlock = "";
4466
- try {
4467
- extensionsBlock = await buildExtensionsBlockForConsolidation(this.config);
4468
- } catch (err) {
4469
- log.warn(`[semantic-consolidation] extension discovery failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
4470
- }
4471
-
4472
- for (const cluster of clusters) {
4473
- let canonicalWriteCompleted = false;
4474
- try {
4475
- // Operator-aware prompt (issue #561 PR 3): ask the LLM to pick the
4476
- // SPLIT/MERGE/UPDATE operator alongside the canonical output. Falls
4477
- // back to the legacy plain-blob prompt when operator-aware
4478
- // consolidation is explicitly disabled via config, so rollbacks stay
4479
- // clean.
4480
- // Use the `=== true` idiom for default-false flags (PR #632
4481
- // review, cursor Low): sibling disabled-by-default flags like
4482
- // `semanticConsolidationEnabled` follow the same convention,
4483
- // while `!== false` is reserved for default-on flags.
4484
- const operatorAwareEnabled =
4485
- this.config.operatorAwareConsolidationEnabled === true;
4486
- let prompt = operatorAwareEnabled
4487
- ? buildOperatorAwareConsolidationPrompt(cluster)
4488
- : buildConsolidationPrompt(cluster);
4489
- if (extensionsBlock.length > 0) {
4490
- prompt += "\n\n" + extensionsBlock;
4491
- }
4492
- const messages = [
4493
- {
4494
- role: "system" as const,
4495
- content: operatorAwareEnabled
4496
- ? 'You are a memory consolidation system. Return ONLY a JSON object with two keys, "operator" and "output". The "operator" value MUST be one of the exact strings "merge", "update", or "split" — never a pipe-separated placeholder, never prose. The "output" value is the canonical memory text.'
4497
- : "You are a memory consolidation system. Output only the consolidated memory text.",
4498
- },
4499
- { role: "user" as const, content: prompt },
4500
- ];
4501
- const llmOpts = { temperature: 0.2, maxTokens: 2000 };
4502
-
4503
- // Route to the configured model
4504
- let response: { content: string } | null = null;
4505
- if (useGateway) {
4506
- // Gateway model source — use the appropriate agent chain
4507
- response = await llm.chatCompletion(messages, { ...llmOpts, ...gatewayChainOptions });
4508
- } else if (modelSetting === "fast" && this.fastLlm) {
4509
- const fastResult = await this.fastLlm.chatCompletion(messages, {
4510
- operation: "semantic-consolidation",
4511
- maxTokens: llmOpts.maxTokens,
4512
- temperature: llmOpts.temperature,
4513
- priority: "background",
4514
- forceDisableThinking: true,
4515
- });
4516
- response = fastResult ? { content: fastResult.content } : null;
4517
- } else {
4518
- response = await llm.chatCompletion(messages, llmOpts);
4519
- }
4520
-
4521
- if (!response?.content) {
4522
- log.warn(
4523
- `[semantic-consolidation] empty LLM response for cluster in "${cluster.category}"`,
4524
- );
4525
- result.errors++;
4526
- continue;
4527
- }
4528
-
4529
- // Operator-aware parse (issue #561 PR 3). In legacy mode we fall
4530
- // back to the plain-text parser and derive the operator from the
4531
- // cluster-shape heuristic so `derived_via` still lands.
4532
- // Restricted to `SemanticConsolidationLlmOperator`
4533
- // (split/merge/update) — `pattern-reinforcement` joined the
4534
- // wider `ConsolidationOperator` type in #687 PR 2/4 but is
4535
- // reserved for the maintenance job and must never be assignable
4536
- // here (Cursor Bugbot review, PR #730).
4537
- let canonicalContent: string;
4538
- let operator: SemanticConsolidationLlmOperator;
4539
- if (operatorAwareEnabled) {
4540
- const parsed = parseOperatorAwareConsolidationResponse(
4541
- response.content,
4542
- cluster,
4543
- );
4544
- canonicalContent = parsed.output;
4545
- operator = parsed.operator;
4546
- } else {
4547
- canonicalContent = parseConsolidationResponse(response.content);
4548
- operator = chooseConsolidationOperator(cluster);
4549
- }
4550
- cluster.canonicalContent = canonicalContent;
4551
-
4552
- // Pick the most recent memory's metadata as the basis for lineage
4553
- const sorted = [...cluster.memories].sort(
4554
- (a, b) =>
4555
- new Date(b.frontmatter.created).getTime() -
4556
- new Date(a.frontmatter.created).getTime(),
4557
- );
4558
- const newest = sorted[0];
4559
- const lineageIds = cluster.memories.map((m) => m.frontmatter.id);
4560
-
4561
- // Consolidation provenance (issue #561 PR 2+3): snapshot each
4562
- // source memory BEFORE archiving it, collecting
4563
- // "<relpath>:<versionId>" pointers for the new canonical memory's
4564
- // `derived_from` frontmatter field. Snapshots are best-effort — if
4565
- // page-versioning is disabled (default in `config.ts`) or a single
4566
- // source fails to snapshot we simply omit that entry rather than
4567
- // abort the consolidation. The `derived_via` operator is chosen
4568
- // above (PR 3) from the LLM response or the cluster-shape
4569
- // heuristic fallback and emitted unconditionally so consolidation
4570
- // outputs stay identifiable even when no snapshots are captured
4571
- // (PR #624 review feedback).
4572
- const derivedFromEntries: string[] = [];
4573
- for (const m of cluster.memories) {
4574
- if (!m.path) continue;
4575
- const entry = await targetStorage.snapshotForProvenance(m.path);
4576
- if (entry) derivedFromEntries.push(entry);
4577
- }
4578
-
4579
- // Write the canonical memory
4580
- const { id: canonicalId, tombstoneBlocked: canonicalBlocked } = await targetStorage.writeMemory(
4581
- newest.frontmatter.category,
4582
- canonicalContent,
4583
- {
4584
- actor: "semantic-consolidation",
4585
- confidence: newest.frontmatter.confidence,
4586
- tags: [
4587
- ...new Set(
4588
- cluster.memories.flatMap((m) => m.frontmatter.tags ?? []),
4589
- ),
4590
- ],
4591
- source: "semantic-consolidation",
4592
- lineage: lineageIds,
4593
- derivedFrom: derivedFromEntries.length > 0 ? derivedFromEntries : undefined,
4594
- derivedVia: operator,
4595
- },
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
- }
4603
- canonicalWriteCompleted = true;
4604
-
4605
- result.memoriesConsolidated++;
4606
-
4607
- // Archive originals
4608
- for (const m of cluster.memories) {
4609
- const archiveResult = await targetStorage.archiveMemory(m, {
4610
- actor: "semantic-consolidation",
4611
- reasonCode: "semantic-consolidation",
4612
- relatedMemoryIds: [canonicalId],
4613
- });
4614
- if (archiveResult) {
4615
- // Remove from the same storage-scoped content-hash index that
4616
- // originally deduped this memory.
4617
- await this.removeContentHashForMemory(
4618
- targetStorage,
4619
- m,
4620
- "semantic-consolidation",
4621
- );
4622
- // Best-effort index cleanup: a failure here (e.g. on-disk index save
4623
- // under disk-full) must NOT abort the archival loop and thereby skip
4624
- // the catalog write touch below for an already-durable canonical write
4625
- // (kilo NV0mh).
4626
- try {
4627
- await this.embeddingFallback.removeFromIndex(m.frontmatter.id);
4628
- if (
4629
- resolveIndexingCapabilities(this.config).queryAwareIndexing &&
4630
- m.path &&
4631
- m.frontmatter?.created
4632
- ) {
4633
- deindexMemory(
4634
- targetStorage.dir,
4635
- m.path,
4636
- m.frontmatter.created,
4637
- m.frontmatter.tags ?? [],
4638
- );
4639
- }
4640
- } catch (cleanupErr) {
4641
- log.warn(
4642
- `[semantic-consolidation] index cleanup failed (non-fatal): ${cleanupErr}`,
4643
- );
4644
- }
4645
- result.memoriesArchived++;
4646
- }
4647
- }
4648
-
4649
- log.info(
4650
- `[semantic-consolidation] consolidated ${cluster.memories.length} memories → ${canonicalId}`,
4651
- );
4652
- } catch (err) {
4653
- log.warn(
4654
- `[semantic-consolidation] cluster processing failed: ${err instanceof Error ? err.message : String(err)}`,
4655
- );
4656
- result.errors++;
4657
- } finally {
4658
- if (canonicalWriteCompleted) {
4659
- // Catalog write touch (issue #1499 sweep): record after the canonical
4660
- // write and, on the happy path, after archival of superseded cluster
4661
- // memories, so `lastWriteAt` reflects every durable mutation in this
4662
- // consolidation (cursor NUtCK). The `finally` also covers partial
4663
- // failures where the canonical memory was written but a later archive
4664
- // step throws and the cluster catch continues (codex NY-dK).
4665
- // Best-effort; namespace decoded from the storage dir since this path
4666
- // #1522: catalog touch handled at the storage chokepoint.
4667
- }
4668
- }
4669
- }
4670
-
4671
- // Save hash indexes if we modified them.
4672
- if (result.memoriesArchived > 0) {
4673
- await this.saveContentHashIndexes().catch((err) =>
4674
- log.warn(
4675
- `[semantic-consolidation] content-hash index save failed: ${err}`,
4676
- ),
4677
- );
4678
- }
4679
-
4680
- log.info(
4681
- `[semantic-consolidation] complete: clusters=${result.clustersFound}, consolidated=${result.memoriesConsolidated}, archived=${result.memoriesArchived}, errors=${result.errors}`,
4682
- );
4683
-
4684
- // #378: fire the Codex materialize post-hook so `codexMaterializeOnConsolidation`
4685
- // actually has a runtime effect. The helper silently no-ops when the
4686
- // feature flag or the per-trigger toggle is off, when the sentinel is
4687
- // missing, or when nothing has changed since the previous run, so it's
4688
- // safe to always call here. Wrapped in a try/catch because a failed
4689
- // materialize must never abort the consolidation result — consolidation
4690
- // is the load-bearing operation; materialization is an optional mirror.
4691
- try {
4692
- await materializeAfterSemanticConsolidation({
4693
- config: this.config,
4694
- memoryDir: targetStorage.dir,
4695
- });
4696
- } catch (err) {
4697
- log.warn(
4698
- `[semantic-consolidation] Codex materialize post-hook failed (non-fatal): ${
4699
- err instanceof Error ? err.message : String(err)
4700
- }`,
4701
- );
4702
- }
4703
-
4704
- // Issue #679 PR 2/5 — async peer profile reasoner runs as part of
4705
- // the REM phase, immediately after semantic consolidation. Gated
4706
- // on `peerProfileReasonerEnabled` (default false, opt-in). Wrapped
4707
- // in a try/catch because reasoner I/O (LLM call, peer-profile
4708
- // writes) must never abort the consolidation result. The reasoner
4709
- // itself also defends against partial failure — see profile-reasoner.ts.
4710
- if (this.config.peerProfileReasonerEnabled) {
4711
- try {
4712
- const { runPeerProfileReasoner } = await import("./peers/index.js");
4713
- const llm = new FallbackLlmClient(
4714
- this.config.gatewayConfig,
4715
- fallbackLlmRuntimeContextFromConfig(this.config),
4716
- );
4717
- const peerResult = await runPeerProfileReasoner({
4718
- memoryDir: targetStorage.dir,
4719
- enabled: true,
4720
- llm,
4721
- model: this.config.peerProfileReasonerModel,
4722
- minInteractions: this.config.peerProfileReasonerMinInteractions,
4723
- maxFieldsPerRun: this.config.peerProfileReasonerMaxFieldsPerRun,
4724
- log: {
4725
- debug: (msg) => log.debug(msg),
4726
- info: (msg) => log.info(msg),
4727
- warn: (msg) => log.warn(msg),
4728
- },
4729
- });
4730
- log.info(
4731
- `[peer-profile-reasoner] complete: peers=${peerResult.peersConsidered}, processed=${peerResult.peersProcessed}, fields=${peerResult.fieldsApplied}`,
4732
- );
4733
- } catch (err) {
4734
- log.warn(
4735
- `[peer-profile-reasoner] post-consolidation hook failed (non-fatal): ${
4736
- err instanceof Error ? err.message : String(err)
4737
- }`,
4738
- );
4739
- }
4740
- }
4741
-
4742
- return result;
4403
+ return this.semanticConsolidationCoordinator.runSemanticConsolidation(options);
4743
4404
  }
4744
4405
 
4745
4406
  async waitForExtractionIdle(timeoutMs: number = 60_000): Promise<boolean> {
@@ -5950,7 +5611,7 @@ export class Orchestrator {
5950
5611
  options.principalOverride.length > 0
5951
5612
  ? options.principalOverride
5952
5613
  : resolvePrincipal(sessionKey, this.config);
5953
- const namespacesEnabled = this.config.namespacesEnabled;
5614
+ const namespacesEnabled = resolveNamespaceCapabilities(this.config).namespaces;
5954
5615
  if (namespacesEnabled && !principal) {
5955
5616
  throw new Error("authentication required: namespaces are enabled and no principal was supplied");
5956
5617
  }
@@ -6419,7 +6080,7 @@ export class Orchestrator {
6419
6080
  for (const memoryPath of paths) {
6420
6081
  if (!memoryPath || isArtifactMemoryPath(memoryPath)) continue;
6421
6082
  if (
6422
- this.config.namespacesEnabled &&
6083
+ resolveNamespaceCapabilities(this.config).namespaces &&
6423
6084
  !recallNamespaces.includes(this.namespaceFromPath(memoryPath))
6424
6085
  ) {
6425
6086
  continue;
@@ -6536,7 +6197,7 @@ export class Orchestrator {
6536
6197
  const memories = (
6537
6198
  await Promise.all(
6538
6199
  Array.from(candidatePaths).map(async (memoryPath) => {
6539
- const namespace = this.config.namespacesEnabled
6200
+ const namespace = resolveNamespaceCapabilities(this.config).namespaces
6540
6201
  ? this.namespaceFromPath(memoryPath)
6541
6202
  : this.config.defaultNamespace;
6542
6203
  const storage = await this.storageRouter.storageFor(namespace);
@@ -7160,7 +6821,7 @@ export class Orchestrator {
7160
6821
  private async resolveStateDirForNamespace(
7161
6822
  namespace: string,
7162
6823
  ): Promise<string> {
7163
- if (!this.config.namespacesEnabled) {
6824
+ if (!resolveNamespaceCapabilities(this.config).namespaces) {
7164
6825
  return path.join(this.config.memoryDir, "state");
7165
6826
  }
7166
6827
  if (namespace !== this.config.defaultNamespace) {
@@ -7480,7 +7141,7 @@ export class Orchestrator {
7480
7141
  }
7481
7142
  const timings: Record<string, string> = {};
7482
7143
  const profileTraceId = this.profiler.startTrace("recall", sessionKey, {
7483
- qmdEnabled: this.config.qmdEnabled,
7144
+ qmdEnabled: resolveQmdCapabilities(this.config).qmd,
7484
7145
  rerankEnabled: caps.rerank,
7485
7146
  parallelRetrieval: caps.parallelRetrieval,
7486
7147
  });
@@ -7753,7 +7414,7 @@ export class Orchestrator {
7753
7414
  && options.principalOverride.length > 0
7754
7415
  ? options.principalOverride
7755
7416
  : resolvePrincipal(sessionKey, this.config);
7756
- const namespacesEnabled = this.config.namespacesEnabled;
7417
+ const namespacesEnabled = resolveNamespaceCapabilities(this.config).namespaces;
7757
7418
  if (namespacesEnabled && !principal) {
7758
7419
  throw new Error("authentication required: namespaces are enabled and no principal was supplied");
7759
7420
  }
@@ -7998,7 +7659,7 @@ export class Orchestrator {
7998
7659
  retrievalQueryLength: retrievalQuery.length,
7999
7660
  recallMode,
8000
7661
  recallResultLimit,
8001
- qmdEnabled: this.config.qmdEnabled,
7662
+ qmdEnabled: resolveQmdCapabilities(this.config).qmd,
8002
7663
  qmdAvailable: this.qmd.isAvailable(),
8003
7664
  recallNamespaces: [],
8004
7665
  source: recallSource,
@@ -8404,7 +8065,7 @@ export class Orchestrator {
8404
8065
  if (
8405
8066
  !this.isRecallSectionEnabled(
8406
8067
  "identity-continuity",
8407
- this.config.identityContinuityEnabled === true,
8068
+ resolveIdentityContinuityCapabilities(this.config).identityContinuity === true,
8408
8069
  )
8409
8070
  )
8410
8071
  return null;
@@ -8679,17 +8340,17 @@ export class Orchestrator {
8679
8340
 
8680
8341
  const objectiveStateSearches = await Promise.all(
8681
8342
  recallNamespaces.map(async (namespace) => {
8682
- const storage = this.config.namespacesEnabled
8343
+ const storage = resolveNamespaceCapabilities(this.config).namespaces
8683
8344
  ? await this.getStorage(namespace)
8684
8345
  : null;
8685
8346
  return searchObjectiveStateSnapshots({
8686
- memoryDir: this.config.namespacesEnabled
8347
+ memoryDir: resolveNamespaceCapabilities(this.config).namespaces
8687
8348
  ? storage!.dir
8688
8349
  : this.config.memoryDir,
8689
8350
  objectiveStateStoreDir: objectiveStateStoreOverrideForNamespace({
8690
8351
  memoryDir: this.config.memoryDir,
8691
8352
  configuredStoreDir: this.config.objectiveStateStoreDir,
8692
- namespacesEnabled: this.config.namespacesEnabled,
8353
+ namespacesEnabled: resolveNamespaceCapabilities(this.config).namespaces,
8693
8354
  namespace,
8694
8355
  }),
8695
8356
  query: retrievalQuery,
@@ -9593,7 +9254,7 @@ export class Orchestrator {
9593
9254
  qmdFetchLimit,
9594
9255
  qmdHybridFetchLimit,
9595
9256
  {
9596
- namespacesEnabled: this.config.namespacesEnabled,
9257
+ namespacesEnabled: resolveNamespaceCapabilities(this.config).namespaces,
9597
9258
  recallNamespaces,
9598
9259
  resolveNamespace: (p) => this.namespaceFromPath(p),
9599
9260
  queryAwarePrefilter,
@@ -10011,7 +9672,7 @@ export class Orchestrator {
10011
9672
  workspaceDir: this.config.workspaceDir,
10012
9673
  memoryDir: this.config.memoryDir,
10013
9674
  config: this.config.nativeKnowledge,
10014
- recallNamespaces: this.config.namespacesEnabled
9675
+ recallNamespaces: resolveNamespaceCapabilities(this.config).namespaces
10015
9676
  ? recallNamespaces
10016
9677
  : undefined,
10017
9678
  defaultNamespace: this.config.defaultNamespace,
@@ -11085,7 +10746,7 @@ export class Orchestrator {
11085
10746
  let memoryResults = memoryResultsRaw;
11086
10747
 
11087
10748
  // Enforce namespace read policies by filtering paths.
11088
- if (this.config.namespacesEnabled) {
10749
+ if (resolveNamespaceCapabilities(this.config).namespaces) {
11089
10750
  memoryResults = memoryResults.filter((r) =>
11090
10751
  recallNamespaces.includes(this.namespaceFromPath(r.path)),
11091
10752
  );
@@ -11459,7 +11120,7 @@ export class Orchestrator {
11459
11120
  const scopedCandidates = filterRecallCandidates(
11460
11121
  prefilteredEmbeddingResults,
11461
11122
  {
11462
- namespacesEnabled: this.config.namespacesEnabled,
11123
+ namespacesEnabled: resolveNamespaceCapabilities(this.config).namespaces,
11463
11124
  recallNamespaces,
11464
11125
  resolveNamespace: (p) => this.namespaceFromPath(p),
11465
11126
  limit: embeddingFetchLimit,
@@ -11638,7 +11299,7 @@ export class Orchestrator {
11638
11299
  const scopedCandidates = filterRecallCandidates(
11639
11300
  prefilteredEmbeddingResults,
11640
11301
  {
11641
- namespacesEnabled: this.config.namespacesEnabled,
11302
+ namespacesEnabled: resolveNamespaceCapabilities(this.config).namespaces,
11642
11303
  recallNamespaces,
11643
11304
  resolveNamespace: (p) => this.namespaceFromPath(p),
11644
11305
  limit: embeddingFetchLimit,
@@ -12476,7 +12137,7 @@ export class Orchestrator {
12476
12137
  retrievalQueryLength: retrievalQuery.length,
12477
12138
  recallMode,
12478
12139
  recallResultLimit,
12479
- qmdEnabled: this.config.qmdEnabled,
12140
+ qmdEnabled: resolveQmdCapabilities(this.config).qmd,
12480
12141
  qmdAvailable: this.qmd.isAvailable(),
12481
12142
  recallNamespaces,
12482
12143
  source: recallSource,
@@ -14193,7 +13854,7 @@ export class Orchestrator {
14193
13854
  // #1713: hoist namespaces-enabled once for the scope-routing mirrors
14194
13855
  // (pre-judge + write-loop) so no new scattered config.*Enabled read is
14195
13856
  // introduced (ratchet scatteredConfigFlagReads; see #1523).
14196
- const namespacesEnabled = this.config.namespacesEnabled;
13857
+ const namespacesEnabled = resolveNamespaceCapabilities(this.config).namespaces;
14197
13858
  const profileAutoPromotionAllows = (
14198
13859
  category: string,
14199
13860
  confidence: number,
@@ -14235,7 +13896,7 @@ export class Orchestrator {
14235
13896
  confidence: number,
14236
13897
  ): boolean => {
14237
13898
  if (
14238
- !this.config.namespacesEnabled ||
13899
+ !resolveNamespaceCapabilities(this.config).namespaces ||
14239
13900
  !profileAllowsSharedWrites ||
14240
13901
  !sharedAutoPromotionAllows(category, confidence)
14241
13902
  )
@@ -16769,7 +16430,7 @@ export class Orchestrator {
16769
16430
  // On full rebuild with namespaces enabled, span all configured namespaces so
16770
16431
  // memories written to other namespaces before the index existed are also captured.
16771
16432
  const allMemories =
16772
- needsFullRebuild && this.config.namespacesEnabled
16433
+ needsFullRebuild && resolveNamespaceCapabilities(this.config).namespaces
16773
16434
  ? await this.readAllMemoriesForNamespaces(
16774
16435
  Array.from(
16775
16436
  new Set<string>([
@@ -17782,14 +17443,14 @@ export class Orchestrator {
17782
17443
  // namespace that accumulated IDENTITY.md reflections must also be eligible for
17783
17444
  // auto-consolidation, otherwise its identity file grows unbounded and is never
17784
17445
  // consolidated. Falls back to the configured set on any catalog read failure.
17785
- const namespaces = this.config.namespacesEnabled
17446
+ const namespaces = resolveNamespaceCapabilities(this.config).namespaces
17786
17447
  ? await this.maintenanceNamespaces()
17787
17448
  : [this.config.defaultNamespace];
17788
17449
 
17789
17450
  for (const namespace of namespaces) {
17790
17451
  const storage = await this.storageRouter.storageFor(namespace);
17791
17452
  const identityNamespace =
17792
- this.config.namespacesEnabled &&
17453
+ resolveNamespaceCapabilities(this.config).namespaces &&
17793
17454
  namespace !== this.config.defaultNamespace
17794
17455
  ? namespace
17795
17456
  : undefined;
@@ -18138,7 +17799,7 @@ export class Orchestrator {
18138
17799
  injectedChars: number;
18139
17800
  truncated: boolean;
18140
17801
  } | null> {
18141
- if (!this.config.identityContinuityEnabled) return null;
17802
+ if (!resolveIdentityContinuityCapabilities(this.config).identityContinuity) return null;
18142
17803
  if (this.config.identityMaxInjectChars <= 0) return null;
18143
17804
 
18144
17805
  const resolved = resolveEffectiveIdentityInjectionMode({
@@ -18344,7 +18005,7 @@ export class Orchestrator {
18344
18005
  : this.config.defaultNamespace;
18345
18006
  if (
18346
18007
  recallNamespaces.length === 0 ||
18347
- !this.config.namespacesEnabled ||
18008
+ !resolveNamespaceCapabilities(this.config).namespaces ||
18348
18009
  recallNamespaces.includes(fallbackNamespace)
18349
18010
  ) {
18350
18011
  addStorage(fallbackStorage);
@@ -18471,7 +18132,7 @@ export class Orchestrator {
18471
18132
  recallNamespaces,
18472
18133
  );
18473
18134
  ownerNamespace = ownerStorage?.namespace ?? null;
18474
- if (!ownerNamespace && this.config.namespacesEnabled) return null;
18135
+ if (!ownerNamespace && resolveNamespaceCapabilities(this.config).namespaces) return null;
18475
18136
  }
18476
18137
  ownerNamespace ??= this.namespaceFromPath(memory.path);
18477
18138
  if (
@@ -19030,7 +18691,7 @@ export class Orchestrator {
19030
18691
  pathPrefix?: string;
19031
18692
  pathExcludePrefixes?: readonly string[];
19032
18693
  } {
19033
- if (!this.config.namespacesEnabled) return {};
18694
+ if (!resolveNamespaceCapabilities(this.config).namespaces) return {};
19034
18695
  const memoryDir = path.resolve(this.config.memoryDir);
19035
18696
  const storageDir = path.resolve(targetStorage.dir);
19036
18697
  if (storageDir === memoryDir) {
@@ -19267,7 +18928,7 @@ export class Orchestrator {
19267
18928
  }
19268
18929
  };
19269
18930
 
19270
- const coldQmdEnabled = this.config.qmdColdTierEnabled === true;
18931
+ const coldQmdEnabled = resolveQmdCapabilities(this.config).qmdColdTier === true;
19271
18932
  const coldCollection =
19272
18933
  this.config.qmdColdCollection ?? "openclaw-engram-cold";
19273
18934
  const coldMaxResults =
@@ -19312,7 +18973,7 @@ export class Orchestrator {
19312
18973
  coldFetchLimit,
19313
18974
  coldHybridLimit,
19314
18975
  {
19315
- namespacesEnabled: this.config.namespacesEnabled,
18976
+ namespacesEnabled: resolveNamespaceCapabilities(this.config).namespaces,
19316
18977
  recallNamespaces: options.recallNamespaces,
19317
18978
  resolveNamespace: (p) => this.namespaceFromPath(p),
19318
18979
  collection: coldCollection,
@@ -19359,7 +19020,7 @@ export class Orchestrator {
19359
19020
  if (longTerm.length === 0) return [];
19360
19021
 
19361
19022
  let results = longTerm;
19362
- if (this.config.namespacesEnabled) {
19023
+ if (resolveNamespaceCapabilities(this.config).namespaces) {
19363
19024
  const recallRoots: string[] = [];
19364
19025
  const seenRecallRoots = new Set<string>();
19365
19026
  for (const namespace of options.recallNamespaces) {
@@ -19417,13 +19078,13 @@ export class Orchestrator {
19417
19078
  if (results.length === 0) return [];
19418
19079
 
19419
19080
  const isFullModeGraphAssist =
19420
- this.config.qmdTierParityGraphEnabled &&
19081
+ resolveQmdCapabilities(this.config).qmdTierParityGraph &&
19421
19082
  graphCaps.multiGraphMemory &&
19422
19083
  caps.graphAssistInFullMode &&
19423
19084
  options.recallMode === "full" &&
19424
19085
  results.length >= Math.max(1, this.config.graphAssistMinSeedResults ?? 3);
19425
19086
  const shouldRunGraphExpansion =
19426
- this.config.qmdTierParityGraphEnabled &&
19087
+ resolveQmdCapabilities(this.config).qmdTierParityGraph &&
19427
19088
  (options.recallMode === "graph_mode" || isFullModeGraphAssist);
19428
19089
 
19429
19090
  if (shouldRunGraphExpansion) {
@@ -19620,7 +19281,7 @@ export class Orchestrator {
19620
19281
 
19621
19282
  // Build entries from buffer, merging with existing counts
19622
19283
  const entries: AccessTrackingEntry[] = [];
19623
- const namespaces = this.config.namespacesEnabled
19284
+ const namespaces = resolveNamespaceCapabilities(this.config).namespaces
19624
19285
  ? Array.from(
19625
19286
  new Set<string>([
19626
19287
  this.config.defaultNamespace,
@@ -20580,7 +20241,7 @@ export class Orchestrator {
20580
20241
  }
20581
20242
 
20582
20243
  private namespaceFromPath(p: string): string {
20583
- if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
20244
+ if (!resolveNamespaceCapabilities(this.config).namespaces) return this.config.defaultNamespace;
20584
20245
  const parts = qmdCollectionPathParts(p);
20585
20246
  const collectionNamespace = parts
20586
20247
  ? this.qmdCollectionNamespaceFromPrefix(parts.collection)