@remnic/core 9.3.737 → 9.3.739

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 (144) hide show
  1. package/dist/access-boundary.d.ts +6 -6
  2. package/dist/access-boundary.js +10 -10
  3. package/dist/access-cli.js +26 -26
  4. package/dist/access-http.d.ts +6 -6
  5. package/dist/access-http.js +15 -15
  6. package/dist/access-mcp.d.ts +6 -6
  7. package/dist/access-mcp.js +14 -14
  8. package/dist/access-operations-batch.js +12 -12
  9. package/dist/access-operations.d.ts +6 -6
  10. package/dist/access-operations.js +13 -13
  11. package/dist/{access-service-DwQ6BIQw.d.ts → access-service-Cbk-Kv_A.d.ts} +1 -1
  12. package/dist/access-service.d.ts +6 -6
  13. package/dist/access-service.js +9 -9
  14. package/dist/access-surface-catalog.d.ts +6 -6
  15. package/dist/active-memory-bridge.js +3 -3
  16. package/dist/bootstrap.d.ts +5 -5
  17. package/dist/buffer.js +2 -1
  18. package/dist/capabilities.d.ts +98 -1
  19. package/dist/capabilities.js +7 -1
  20. package/dist/{chunk-G54IDCD3.js → chunk-23PLOBWT.js} +30 -27
  21. package/dist/chunk-23PLOBWT.js.map +1 -0
  22. package/dist/{chunk-K3Y2SJTN.js → chunk-56LP72AD.js} +6 -6
  23. package/dist/{chunk-ZBZ4XULO.js → chunk-66RZ4EIT.js} +3 -3
  24. package/dist/{chunk-IPI45Q4S.js → chunk-75PDC7VT.js} +9 -8
  25. package/dist/chunk-75PDC7VT.js.map +1 -0
  26. package/dist/{chunk-GB2FZOWB.js → chunk-7B7IPPN5.js} +5 -5
  27. package/dist/{chunk-QNTLWH3T.js → chunk-A7INTIC5.js} +3 -3
  28. package/dist/{chunk-FLPKEMSO.js → chunk-AKKFHN3I.js} +2 -2
  29. package/dist/{chunk-AAIHDIMD.js → chunk-BHJAMSVC.js} +7 -7
  30. package/dist/{chunk-BXPG5AVX.js → chunk-BHQQVUEQ.js} +6 -6
  31. package/dist/{chunk-HFXZIF3V.js → chunk-BVG2IZOB.js} +3 -3
  32. package/dist/{chunk-NDKW67ER.js → chunk-C53JDEH3.js} +4 -4
  33. package/dist/{chunk-AESC5OQJ.js → chunk-CKHPUOAP.js} +2 -2
  34. package/dist/{chunk-OYHYBHHI.js → chunk-DS3FZ7GS.js} +4 -4
  35. package/dist/{chunk-UO6MPN77.js → chunk-EU543UDC.js} +10 -9
  36. package/dist/chunk-EU543UDC.js.map +1 -0
  37. package/dist/{chunk-PB5YON2G.js → chunk-IEWUPZ75.js} +659 -584
  38. package/dist/chunk-IEWUPZ75.js.map +1 -0
  39. package/dist/{chunk-E4LYJ7PY.js → chunk-IFCM6OLK.js} +6 -6
  40. package/dist/{chunk-5BOQNB2K.js → chunk-JQSP7YBJ.js} +3 -3
  41. package/dist/{chunk-2T2N7ISV.js → chunk-L3TYGUTH.js} +3 -3
  42. package/dist/{chunk-AB2E5DCN.js → chunk-MAU7DFCJ.js} +2 -2
  43. package/dist/{chunk-P6VLG7KE.js → chunk-N2O23WYA.js} +2 -2
  44. package/dist/{chunk-HYNHLBKA.js → chunk-OQQRWPFZ.js} +5 -2
  45. package/dist/chunk-OQQRWPFZ.js.map +1 -0
  46. package/dist/{chunk-IXGXALRV.js → chunk-QTJOORGN.js} +2 -2
  47. package/dist/{chunk-JWDE7OVJ.js → chunk-RJBW4EOU.js} +46 -2
  48. package/dist/{chunk-JWDE7OVJ.js.map → chunk-RJBW4EOU.js.map} +1 -1
  49. package/dist/{chunk-AE7NTZ75.js → chunk-TNLXCSXV.js} +3 -3
  50. package/dist/{chunk-NNJKEAP2.js → chunk-VRBBZH4U.js} +4 -4
  51. package/dist/{chunk-VOEVVIP3.js → chunk-W2FBVBQ4.js} +4 -4
  52. package/dist/{chunk-WNGXQMRI.js → chunk-WFNH4UDF.js} +5 -2
  53. package/dist/chunk-WFNH4UDF.js.map +1 -0
  54. package/dist/{chunk-CHJIWG2H.js → chunk-WVAEIKUY.js} +2 -2
  55. package/dist/{chunk-FF36YNKY.js → chunk-XIQAOGAA.js} +11 -10
  56. package/dist/chunk-XIQAOGAA.js.map +1 -0
  57. package/dist/{chunk-HLPYL6LC.js → chunk-Y5OOHER4.js} +14 -12
  58. package/dist/chunk-Y5OOHER4.js.map +1 -0
  59. package/dist/{chunk-GYICIICQ.js → chunk-YF2MMBKA.js} +9 -7
  60. package/dist/chunk-YF2MMBKA.js.map +1 -0
  61. package/dist/{chunk-IX4L7E5G.js → chunk-YXOXAQ5Y.js} +2 -2
  62. package/dist/{cli-BlEIWDQw.d.ts → cli-BQW2xKdb.d.ts} +2 -2
  63. package/dist/cli.d.ts +7 -7
  64. package/dist/cli.js +21 -21
  65. package/dist/compounding/engine.js +2 -2
  66. package/dist/contradiction/index.js +2 -2
  67. package/dist/{contradiction-scan-2B4AE4TI.js → contradiction-scan-CWO3LIWV.js} +3 -3
  68. package/dist/embedding-fallback.js +2 -2
  69. package/dist/entity-retrieval.js +2 -2
  70. package/dist/explicit-capture.d.ts +5 -5
  71. package/dist/explicit-capture.js +2 -2
  72. package/dist/extraction.js +3 -3
  73. package/dist/{first-start-migration-2RQYDIZ7.js → first-start-migration-5DXD3TF2.js} +2 -2
  74. package/dist/index.d.ts +8 -8
  75. package/dist/index.js +32 -32
  76. package/dist/local-llm.js +2 -2
  77. package/dist/mcp-memory-inspector-app.d.ts +6 -6
  78. package/dist/namespaces/migrate.js +6 -6
  79. package/dist/namespaces/principal.js +2 -2
  80. package/dist/namespaces/search.js +4 -4
  81. package/dist/namespaces/storage.js +2 -2
  82. package/dist/operator-toolkit.js +11 -11
  83. package/dist/orchestration/compression-guideline-coordinator.js +2 -2
  84. package/dist/orchestration/maintenance.js +6 -6
  85. package/dist/{orchestrator-CJZ21ZiB.d.ts → orchestrator-BOAFN_Yr.d.ts} +186 -56
  86. package/dist/orchestrator.d.ts +4 -4
  87. package/dist/orchestrator.js +21 -21
  88. package/dist/search/embed-helper.js +2 -2
  89. package/dist/search/factory.js +3 -3
  90. package/dist/search/index.js +3 -3
  91. package/dist/summarizer.js +3 -3
  92. package/dist/{tier-stats-KPDIISG7.js → tier-stats-QID73BBB.js} +2 -2
  93. package/package.json +2 -2
  94. package/src/access-service.ts +7 -8
  95. package/src/buffer.ts +2 -1
  96. package/src/capabilities.test.ts +137 -0
  97. package/src/capabilities.ts +209 -0
  98. package/src/cli/creation-ledger-commands.ts +3 -3
  99. package/src/cli/research-status-commands.ts +4 -4
  100. package/src/cli.ts +9 -10
  101. package/src/compounding/engine.ts +7 -7
  102. package/src/correction/correction-access-wiring.ts +3 -2
  103. package/src/extraction.ts +8 -10
  104. package/src/operator-toolkit.ts +6 -7
  105. package/src/orchestration/conversation-index-coordinator.ts +344 -0
  106. package/src/orchestration/entity-synthesis-coordinator.ts +2 -1
  107. package/src/orchestration/maintenance.ts +5 -7
  108. package/src/orchestration/recall-rerank-coordinator.ts +425 -0
  109. package/src/orchestration/semantic-consolidation-coordinator.ts +2 -2
  110. package/src/orchestrator.ts +88 -602
  111. package/dist/chunk-FF36YNKY.js.map +0 -1
  112. package/dist/chunk-G54IDCD3.js.map +0 -1
  113. package/dist/chunk-GYICIICQ.js.map +0 -1
  114. package/dist/chunk-HLPYL6LC.js.map +0 -1
  115. package/dist/chunk-HYNHLBKA.js.map +0 -1
  116. package/dist/chunk-IPI45Q4S.js.map +0 -1
  117. package/dist/chunk-PB5YON2G.js.map +0 -1
  118. package/dist/chunk-UO6MPN77.js.map +0 -1
  119. package/dist/chunk-WNGXQMRI.js.map +0 -1
  120. /package/dist/{chunk-K3Y2SJTN.js.map → chunk-56LP72AD.js.map} +0 -0
  121. /package/dist/{chunk-ZBZ4XULO.js.map → chunk-66RZ4EIT.js.map} +0 -0
  122. /package/dist/{chunk-GB2FZOWB.js.map → chunk-7B7IPPN5.js.map} +0 -0
  123. /package/dist/{chunk-QNTLWH3T.js.map → chunk-A7INTIC5.js.map} +0 -0
  124. /package/dist/{chunk-FLPKEMSO.js.map → chunk-AKKFHN3I.js.map} +0 -0
  125. /package/dist/{chunk-AAIHDIMD.js.map → chunk-BHJAMSVC.js.map} +0 -0
  126. /package/dist/{chunk-BXPG5AVX.js.map → chunk-BHQQVUEQ.js.map} +0 -0
  127. /package/dist/{chunk-HFXZIF3V.js.map → chunk-BVG2IZOB.js.map} +0 -0
  128. /package/dist/{chunk-NDKW67ER.js.map → chunk-C53JDEH3.js.map} +0 -0
  129. /package/dist/{chunk-AESC5OQJ.js.map → chunk-CKHPUOAP.js.map} +0 -0
  130. /package/dist/{chunk-OYHYBHHI.js.map → chunk-DS3FZ7GS.js.map} +0 -0
  131. /package/dist/{chunk-E4LYJ7PY.js.map → chunk-IFCM6OLK.js.map} +0 -0
  132. /package/dist/{chunk-5BOQNB2K.js.map → chunk-JQSP7YBJ.js.map} +0 -0
  133. /package/dist/{chunk-2T2N7ISV.js.map → chunk-L3TYGUTH.js.map} +0 -0
  134. /package/dist/{chunk-AB2E5DCN.js.map → chunk-MAU7DFCJ.js.map} +0 -0
  135. /package/dist/{chunk-P6VLG7KE.js.map → chunk-N2O23WYA.js.map} +0 -0
  136. /package/dist/{chunk-IXGXALRV.js.map → chunk-QTJOORGN.js.map} +0 -0
  137. /package/dist/{chunk-AE7NTZ75.js.map → chunk-TNLXCSXV.js.map} +0 -0
  138. /package/dist/{chunk-NNJKEAP2.js.map → chunk-VRBBZH4U.js.map} +0 -0
  139. /package/dist/{chunk-VOEVVIP3.js.map → chunk-W2FBVBQ4.js.map} +0 -0
  140. /package/dist/{chunk-CHJIWG2H.js.map → chunk-WVAEIKUY.js.map} +0 -0
  141. /package/dist/{chunk-IX4L7E5G.js.map → chunk-YXOXAQ5Y.js.map} +0 -0
  142. /package/dist/{contradiction-scan-2B4AE4TI.js.map → contradiction-scan-CWO3LIWV.js.map} +0 -0
  143. /package/dist/{first-start-migration-2RQYDIZ7.js.map → first-start-migration-5DXD3TF2.js.map} +0 -0
  144. /package/dist/{tier-stats-KPDIISG7.js.map → tier-stats-QID73BBB.js.map} +0 -0
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-OZECYVGI.js";
8
8
  import {
9
9
  CompressionGuidelineCoordinator
10
- } from "./chunk-IXGXALRV.js";
10
+ } from "./chunk-QTJOORGN.js";
11
11
  import {
12
12
  MaintenanceScheduler
13
- } from "./chunk-GYICIICQ.js";
13
+ } from "./chunk-YF2MMBKA.js";
14
14
  import {
15
15
  runPeerProfileReasoner
16
16
  } from "./chunk-I74SUMNI.js";
@@ -29,7 +29,7 @@ import {
29
29
  import {
30
30
  CompoundingEngine,
31
31
  defaultTierMigrationCycleBudget
32
- } from "./chunk-IPI45Q4S.js";
32
+ } from "./chunk-75PDC7VT.js";
33
33
  import {
34
34
  SharedContextManager
35
35
  } from "./chunk-UFS7OXGL.js";
@@ -63,7 +63,7 @@ import {
63
63
  } from "./chunk-UHGBNIOS.js";
64
64
  import {
65
65
  HourlySummarizer
66
- } from "./chunk-HFXZIF3V.js";
66
+ } from "./chunk-BVG2IZOB.js";
67
67
  import {
68
68
  buildTargetedFactRecallSection,
69
69
  shouldRecallTargetedFactEvidence
@@ -160,7 +160,7 @@ import {
160
160
  } from "./chunk-LTHVM4XN.js";
161
161
  import {
162
162
  ExtractionEngine
163
- } from "./chunk-FF36YNKY.js";
163
+ } from "./chunk-XIQAOGAA.js";
164
164
  import {
165
165
  parseMemoryActionEligibilityContext
166
166
  } from "./chunk-5GSUBCZM.js";
@@ -169,7 +169,7 @@ import {
169
169
  } from "./chunk-PWFWCGOO.js";
170
170
  import {
171
171
  LocalLlmClient
172
- } from "./chunk-IX4L7E5G.js";
172
+ } from "./chunk-YXOXAQ5Y.js";
173
173
  import {
174
174
  buildFocusedListRecallSection,
175
175
  shouldRecallFocusedListEvidence
@@ -193,12 +193,12 @@ import {
193
193
  } from "./chunk-Q6IP2ARR.js";
194
194
  import {
195
195
  EmbeddingFallback
196
- } from "./chunk-CHJIWG2H.js";
196
+ } from "./chunk-WVAEIKUY.js";
197
197
  import {
198
198
  buildEntityRecallSection,
199
199
  entityRecentTranscriptLookbackHours,
200
200
  readRecentEntityTranscriptEntries
201
- } from "./chunk-AESC5OQJ.js";
201
+ } from "./chunk-CKHPUOAP.js";
202
202
  import {
203
203
  RoutingRulesStore
204
204
  } from "./chunk-WIWPSQYU.js";
@@ -228,14 +228,14 @@ import {
228
228
  import {
229
229
  runNamespaceMaintenanceFanout,
230
230
  summarizeNamespaceMaintenanceHealth
231
- } from "./chunk-AE7NTZ75.js";
231
+ } from "./chunk-TNLXCSXV.js";
232
232
  import {
233
233
  NamespaceCatalog,
234
234
  planNamespaceMaintenance
235
- } from "./chunk-BXPG5AVX.js";
235
+ } from "./chunk-BHQQVUEQ.js";
236
236
  import {
237
237
  NamespaceStorageRouter
238
- } from "./chunk-NNJKEAP2.js";
238
+ } from "./chunk-VRBBZH4U.js";
239
239
  import {
240
240
  searchHarmonicRetrieval
241
241
  } from "./chunk-AV5ZEH4M.js";
@@ -292,7 +292,7 @@ import {
292
292
  } from "./chunk-UQ7RN5HK.js";
293
293
  import {
294
294
  SmartBuffer
295
- } from "./chunk-HYNHLBKA.js";
295
+ } from "./chunk-OQQRWPFZ.js";
296
296
  import {
297
297
  isDisagreementPrompt
298
298
  } from "./chunk-H3FZVNRN.js";
@@ -311,7 +311,7 @@ import {
311
311
  clusterByKey,
312
312
  createCorrectionService,
313
313
  detectPassiveCorrections
314
- } from "./chunk-WNGXQMRI.js";
314
+ } from "./chunk-WFNH4UDF.js";
315
315
  import {
316
316
  listTrustZoneRecords,
317
317
  searchTrustZoneRecords
@@ -333,14 +333,14 @@ import {
333
333
  resolveNamespaceFromStorageDir,
334
334
  resolveScopePlan,
335
335
  resolveScopeProfilePlan
336
- } from "./chunk-GB2FZOWB.js";
336
+ } from "./chunk-7B7IPPN5.js";
337
337
  import {
338
338
  NamespaceSearchRouter
339
- } from "./chunk-ZBZ4XULO.js";
339
+ } from "./chunk-66RZ4EIT.js";
340
340
  import {
341
341
  createConversationIndexRuntime,
342
342
  createSearchBackend
343
- } from "./chunk-5BOQNB2K.js";
343
+ } from "./chunk-JQSP7YBJ.js";
344
344
  import {
345
345
  NoopSearchBackend
346
346
  } from "./chunk-CYEPCZN5.js";
@@ -363,7 +363,7 @@ import {
363
363
  } from "./chunk-QDW3E4RD.js";
364
364
  import {
365
365
  shouldSkipImplicitExtraction
366
- } from "./chunk-NDKW67ER.js";
366
+ } from "./chunk-C53JDEH3.js";
367
367
  import {
368
368
  searchCausalTrajectories
369
369
  } from "./chunk-SQNED75R.js";
@@ -465,10 +465,15 @@ import {
465
465
  defaultNamespaceForPrincipal,
466
466
  recallNamespacesForPrincipal,
467
467
  resolvePrincipal
468
- } from "./chunk-OYHYBHHI.js";
468
+ } from "./chunk-DS3FZ7GS.js";
469
+ import {
470
+ isSafeRouteNamespace,
471
+ selectRouteRule
472
+ } from "./chunk-U3PN77QT.js";
469
473
  import {
470
474
  resolveCapabilities,
471
475
  resolveCompressionCapabilities,
476
+ resolveConsolidationCapabilities,
472
477
  resolveCreationMemoryCapabilities,
473
478
  resolveEvalCapabilities,
474
479
  resolveGraphConstructionCapabilities,
@@ -478,14 +483,12 @@ import {
478
483
  resolveMemoryLifecycleCapabilities,
479
484
  resolveNamespaceCapabilities,
480
485
  resolveObjectiveStateCapabilities,
486
+ resolvePresentationCapabilities,
481
487
  resolveQmdCapabilities,
488
+ resolveRecallAuxiliaryCapabilities,
482
489
  resolveSecurityCapabilities,
483
490
  resolveUtilityLearningCapabilities
484
- } from "./chunk-JWDE7OVJ.js";
485
- import {
486
- isSafeRouteNamespace,
487
- selectRouteRule
488
- } from "./chunk-U3PN77QT.js";
491
+ } from "./chunk-RJBW4EOU.js";
489
492
  import {
490
493
  sanitizeMemoryContent
491
494
  } from "./chunk-5GKPFL3S.js";
@@ -506,14 +509,14 @@ import {
506
509
  } from "./chunk-PVGDJXVK.js";
507
510
 
508
511
  // src/orchestrator.ts
509
- import path4 from "path";
512
+ import path5 from "path";
510
513
  import os2 from "os";
511
514
  import { createHash as createHash2, randomBytes } from "crypto";
512
515
  import { existsSync, readFileSync } from "fs";
513
516
  import {
514
517
  lstat,
515
518
  mkdir as mkdir3,
516
- readdir,
519
+ readdir as readdir2,
517
520
  readFile as readFile2,
518
521
  realpath,
519
522
  stat,
@@ -902,7 +905,7 @@ var SemanticConsolidationCoordinator = class {
902
905
  errors: 0,
903
906
  clusters: []
904
907
  };
905
- if (!this.config.semanticConsolidationEnabled && !options?.force) {
908
+ if (!resolveConsolidationCapabilities(this.config).semanticConsolidation && !options?.force) {
906
909
  log.debug("[semantic-consolidation] disabled in config");
907
910
  return result;
908
911
  }
@@ -1515,7 +1518,7 @@ var EntitySynthesisCoordinator = class {
1515
1518
  */
1516
1519
  async processQueue(namespace, maxEntities = 5) {
1517
1520
  const { config } = this.deps;
1518
- if (!config.entitySummaryEnabled || maxEntities <= 0 || config.entitySynthesisMaxTokens <= 0) return 0;
1521
+ if (!resolvePresentationCapabilities(config).entitySummary || maxEntities <= 0 || config.entitySynthesisMaxTokens <= 0) return 0;
1519
1522
  const storage = await this.deps.getStorage(namespace);
1520
1523
  const queued = await storage.refreshEntitySynthesisQueue();
1521
1524
  let processed = 0;
@@ -1939,6 +1942,452 @@ ${lines.join("\n\n")}`;
1939
1942
  }
1940
1943
  };
1941
1944
 
1945
+ // src/orchestration/conversation-index-coordinator.ts
1946
+ import { readdir } from "fs/promises";
1947
+ import path3 from "path";
1948
+ var ConversationIndexCoordinator = class {
1949
+ config;
1950
+ getTranscript;
1951
+ getBackend;
1952
+ indexDir;
1953
+ lastUpdateAtMs = /* @__PURE__ */ new Map();
1954
+ constructor(options) {
1955
+ this.config = options.config;
1956
+ this.getTranscript = options.getTranscript;
1957
+ this.getBackend = options.getBackend;
1958
+ this.indexDir = options.indexDir;
1959
+ }
1960
+ /** Semantic recall over past-conversation chunks (fail-open: empty on miss). */
1961
+ async search(retrievalQuery, topK) {
1962
+ const backend = this.getBackend();
1963
+ if (backend) {
1964
+ return backend.search(retrievalQuery, topK);
1965
+ }
1966
+ return [];
1967
+ }
1968
+ /** Render conversation-recall search hits as a budgeted markdown section. */
1969
+ formatRecallSection(results, maxChars) {
1970
+ if (!Array.isArray(results) || results.length === 0) return null;
1971
+ const lines = ["## Semantic Recall (Past Conversations)", ""];
1972
+ let used = 0;
1973
+ for (const r of results) {
1974
+ if (!r?.snippet) continue;
1975
+ const chunk = `### ${r.path}
1976
+ Score: ${r.score.toFixed(3)}
1977
+
1978
+ ${r.snippet.trim()}
1979
+ `;
1980
+ if (used + chunk.length > maxChars) break;
1981
+ lines.push(chunk);
1982
+ used += chunk.length;
1983
+ }
1984
+ return used > 0 ? lines.join("\n") : null;
1985
+ }
1986
+ /** Recursively count `.md` chunk documents under a directory. */
1987
+ async countChunkDocs(dir) {
1988
+ try {
1989
+ const entries = await readdir(dir, { withFileTypes: true });
1990
+ let total = 0;
1991
+ for (const entry of entries) {
1992
+ const fullPath = path3.join(dir, entry.name);
1993
+ if (entry.isDirectory()) {
1994
+ total += await this.countChunkDocs(fullPath);
1995
+ continue;
1996
+ }
1997
+ if (entry.isFile() && entry.name.endsWith(".md")) {
1998
+ total += 1;
1999
+ }
2000
+ }
2001
+ return total;
2002
+ } catch {
2003
+ return 0;
2004
+ }
2005
+ }
2006
+ /** Read recent transcript entries and chunk them for indexing. */
2007
+ async buildChunks(sessionKey, hours = 24) {
2008
+ const entries = await this.getTranscript().readRecent(hours, sessionKey);
2009
+ const effectiveSessionKey = sessionKey ?? "all-sessions";
2010
+ return chunkTranscriptEntries(effectiveSessionKey, entries, {
2011
+ maxChars: this.config.conversationRecallMaxChars * 2,
2012
+ maxTurns: Math.max(10, this.config.hourlySummariesMaxTurnsPerRun)
2013
+ });
2014
+ }
2015
+ async getHealth() {
2016
+ const chunkDocCount = await this.countChunkDocs(this.indexDir);
2017
+ const lastUpdateAtMs = Math.max(0, ...this.lastUpdateAtMs.values());
2018
+ const lastUpdateAt = lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
2019
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
2020
+ return {
2021
+ enabled: false,
2022
+ backend: this.config.conversationIndexBackend,
2023
+ status: "disabled",
2024
+ chunkDocCount,
2025
+ lastUpdateAt
2026
+ };
2027
+ }
2028
+ const backend = this.getBackend();
2029
+ const backendHealth = backend ? await backend.health() : {
2030
+ backend: this.config.conversationIndexBackend,
2031
+ status: "degraded"
2032
+ };
2033
+ return {
2034
+ enabled: true,
2035
+ chunkDocCount,
2036
+ lastUpdateAt,
2037
+ ...backendHealth
2038
+ };
2039
+ }
2040
+ async inspect() {
2041
+ const chunkDocCount = await this.countChunkDocs(this.indexDir);
2042
+ const lastUpdateAtMs = Math.max(0, ...this.lastUpdateAtMs.values());
2043
+ const lastUpdateAt = lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
2044
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
2045
+ return {
2046
+ enabled: false,
2047
+ backend: this.config.conversationIndexBackend,
2048
+ status: "disabled",
2049
+ available: false,
2050
+ indexPath: this.indexDir,
2051
+ supportsIncrementalUpdate: true,
2052
+ message: "Conversation index disabled by config",
2053
+ metadata: {
2054
+ chunkCount: chunkDocCount
2055
+ },
2056
+ chunkDocCount,
2057
+ lastUpdateAt
2058
+ };
2059
+ }
2060
+ const backend = this.getBackend();
2061
+ const inspection = backend ? await backend.inspect() : {
2062
+ backend: this.config.conversationIndexBackend,
2063
+ status: "degraded",
2064
+ available: false,
2065
+ indexPath: this.indexDir,
2066
+ supportsIncrementalUpdate: true,
2067
+ message: "Conversation index backend unavailable",
2068
+ metadata: {
2069
+ chunkCount: chunkDocCount
2070
+ }
2071
+ };
2072
+ return {
2073
+ enabled: true,
2074
+ chunkDocCount,
2075
+ lastUpdateAt,
2076
+ ...inspection
2077
+ };
2078
+ }
2079
+ async update(sessionKey, hours = 24, opts) {
2080
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
2081
+ return { chunks: 0, skipped: true, reason: "disabled", embedded: false };
2082
+ }
2083
+ const enforceMinInterval = opts?.enforceMinInterval !== false;
2084
+ if (enforceMinInterval) {
2085
+ const minIntervalMs = Math.max(
2086
+ 0,
2087
+ this.config.conversationIndexMinUpdateIntervalMs
2088
+ );
2089
+ const now = Date.now();
2090
+ const last = this.lastUpdateAtMs.get(sessionKey) ?? 0;
2091
+ const elapsed = now - last;
2092
+ if (minIntervalMs > 0 && elapsed < minIntervalMs) {
2093
+ return {
2094
+ chunks: 0,
2095
+ skipped: true,
2096
+ reason: "min_interval",
2097
+ retryAfterMs: minIntervalMs - elapsed,
2098
+ embedded: false
2099
+ };
2100
+ }
2101
+ }
2102
+ const chunks = await this.buildChunks(sessionKey, hours);
2103
+ await writeConversationChunks(this.indexDir, chunks);
2104
+ const retentionCutoffMs = Number.isFinite(this.config.conversationIndexRetentionDays) && this.config.conversationIndexRetentionDays > 0 ? Date.now() - this.config.conversationIndexRetentionDays * 24 * 60 * 60 * 1e3 : void 0;
2105
+ await cleanupConversationChunks(
2106
+ this.indexDir,
2107
+ this.config.conversationIndexRetentionDays
2108
+ );
2109
+ const shouldEmbed = opts?.embed ?? this.config.conversationIndexEmbedOnUpdate;
2110
+ let embedded = false;
2111
+ const backend = this.getBackend();
2112
+ if (backend) {
2113
+ const result = await backend.update(chunks, {
2114
+ embed: shouldEmbed,
2115
+ ...retentionCutoffMs !== void 0 ? { retentionCutoffMs } : {}
2116
+ });
2117
+ embedded = result.embedded;
2118
+ }
2119
+ this.lastUpdateAtMs.set(sessionKey, Date.now());
2120
+ return { chunks: chunks.length, skipped: false, embedded };
2121
+ }
2122
+ async rebuild(sessionKey, hours = 24, opts) {
2123
+ if (!resolveIndexingCapabilities(this.config).conversationIndex) {
2124
+ return {
2125
+ chunks: 0,
2126
+ skipped: true,
2127
+ reason: "disabled",
2128
+ embedded: false,
2129
+ rebuilt: false
2130
+ };
2131
+ }
2132
+ const chunks = await this.buildChunks(sessionKey, hours);
2133
+ await writeConversationChunks(this.indexDir, chunks);
2134
+ await cleanupConversationChunks(
2135
+ this.indexDir,
2136
+ this.config.conversationIndexRetentionDays
2137
+ );
2138
+ const shouldEmbed = opts?.embed ?? this.config.conversationIndexEmbedOnUpdate;
2139
+ let embedded = false;
2140
+ let rebuilt = false;
2141
+ const backend = this.getBackend();
2142
+ if (backend) {
2143
+ const result = await backend.rebuild(chunks, {
2144
+ embed: shouldEmbed
2145
+ });
2146
+ embedded = result.embedded;
2147
+ rebuilt = result.rebuilt;
2148
+ }
2149
+ const stamp = Date.now();
2150
+ if (sessionKey) {
2151
+ this.lastUpdateAtMs.set(sessionKey, stamp);
2152
+ } else {
2153
+ this.lastUpdateAtMs.set("__rebuild__", stamp);
2154
+ }
2155
+ return { chunks: chunks.length, skipped: false, embedded, rebuilt };
2156
+ }
2157
+ };
2158
+
2159
+ // src/orchestration/recall-rerank-coordinator.ts
2160
+ var RecallRerankCoordinator = class _RecallRerankCoordinator {
2161
+ getConfig;
2162
+ getStorage;
2163
+ readQmdResultMemory;
2164
+ memoryWorthCounterCache = /* @__PURE__ */ new Map();
2165
+ static MEMORY_WORTH_CACHE_TTL_MS = 3e4;
2166
+ trustSignalCache = /* @__PURE__ */ new Map();
2167
+ static TRUST_SIGNAL_CACHE_TTL_MS = 3e4;
2168
+ constructor(options) {
2169
+ this.getConfig = options.getConfig;
2170
+ this.getStorage = options.getStorage;
2171
+ this.readQmdResultMemory = options.readQmdResultMemory;
2172
+ }
2173
+ async applyMemoryWorthRerank(results, namespaces) {
2174
+ const counters = /* @__PURE__ */ new Map();
2175
+ const seenNamespaces = /* @__PURE__ */ new Set();
2176
+ const nowMs = Date.now();
2177
+ for (const [key, entry] of this.memoryWorthCounterCache) {
2178
+ if (nowMs - entry.at >= _RecallRerankCoordinator.MEMORY_WORTH_CACHE_TTL_MS) {
2179
+ this.memoryWorthCounterCache.delete(key);
2180
+ }
2181
+ }
2182
+ for (const ns of namespaces) {
2183
+ if (seenNamespaces.has(ns)) continue;
2184
+ seenNamespaces.add(ns);
2185
+ try {
2186
+ const cached = this.memoryWorthCounterCache.get(ns);
2187
+ let nsMap;
2188
+ if (cached && nowMs - cached.at < _RecallRerankCoordinator.MEMORY_WORTH_CACHE_TTL_MS) {
2189
+ nsMap = cached.counters;
2190
+ } else {
2191
+ const storage = await this.getStorage(ns);
2192
+ const memories = await storage.readAllMemories();
2193
+ nsMap = buildMemoryWorthCounterMap(memories);
2194
+ this.memoryWorthCounterCache.set(ns, { at: nowMs, counters: nsMap });
2195
+ }
2196
+ for (const [path6, c] of nsMap) counters.set(path6, c);
2197
+ } catch (err) {
2198
+ log.debug("memory-worth: failed to read namespace, skipping", {
2199
+ namespace: ns,
2200
+ error: err.message
2201
+ });
2202
+ }
2203
+ }
2204
+ const missing = results.filter((r) => !counters.has(r.path));
2205
+ if (missing.length > 0) {
2206
+ let reader = null;
2207
+ for (const ns of namespaces) {
2208
+ try {
2209
+ reader = await this.getStorage(ns);
2210
+ break;
2211
+ } catch {
2212
+ }
2213
+ }
2214
+ if (reader) {
2215
+ for (const r of missing) {
2216
+ try {
2217
+ const memory = await this.readQmdResultMemory(r.path, reader, namespaces);
2218
+ if (!memory) continue;
2219
+ const fm = memory.frontmatter;
2220
+ if (fm.mw_success === void 0 && fm.mw_fail === void 0) continue;
2221
+ counters.set(r.path, {
2222
+ mw_success: fm.mw_success,
2223
+ mw_fail: fm.mw_fail,
2224
+ lastAccessed: fm.lastAccessed
2225
+ });
2226
+ } catch (err) {
2227
+ log.debug("memory-worth: direct path lookup failed", {
2228
+ path: r.path,
2229
+ error: err.message
2230
+ });
2231
+ }
2232
+ }
2233
+ }
2234
+ }
2235
+ if (counters.size === 0) return results;
2236
+ const rankedInputs = results.map((r, i) => ({
2237
+ path: r.path,
2238
+ // Large positive rank score so multiplier math stays well-scaled and
2239
+ // we never hit zero; descending so earlier items rank higher.
2240
+ score: results.length - i
2241
+ }));
2242
+ const config = this.getConfig();
2243
+ const filtered = applyMemoryWorthFilter(rankedInputs, {
2244
+ counters,
2245
+ now: /* @__PURE__ */ new Date(),
2246
+ halfLifeMs: config.recallMemoryWorthHalfLifeMs > 0 ? config.recallMemoryWorthHalfLifeMs : void 0
2247
+ });
2248
+ const byPath = new Map(results.map((r) => [r.path, r]));
2249
+ const reordered = [];
2250
+ for (const item of filtered) {
2251
+ const original = byPath.get(item.path);
2252
+ if (original) reordered.push(original);
2253
+ }
2254
+ return reordered;
2255
+ }
2256
+ /**
2257
+ * Issue #1577 — unified TrustScore recall stage. Thin wiring over the pure
2258
+ * {@link applyTrustScoreStage} scorer + the {@link buildTrustSignalsForRerank}
2259
+ * signal builder. The stage subsumes the Memory Worth multiplier — the
2260
+ * orchestrator runs exactly one of the two (mutual exclusion, rule 39; the
2261
+ * double-multiplier test in trust-score-stage.test.ts pins it structurally).
2262
+ *
2263
+ * Returns the admitted results AND the per-path trust map (including
2264
+ * quarantined items) so the caller can: (a) render epistemic hedges, (b)
2265
+ * surface quarantined items in X-ray with a reason (rule 34), and (c) filter
2266
+ * quarantined paths from fallback recall branches. The trust map is a
2267
+ * per-recall local — never instance state — so concurrent recalls cannot
2268
+ * race on it (review: shared-trust-map concurrency).
2269
+ */
2270
+ async applyTrustScoreRerank(results, namespaces) {
2271
+ if (results.length === 0) return { results, trustByPath: null };
2272
+ const config = this.getConfig();
2273
+ const now = /* @__PURE__ */ new Date();
2274
+ const halfLifeDays = config.recallMemoryWorthHalfLifeMs > 0 ? config.recallMemoryWorthHalfLifeMs / (24 * 60 * 60 * 1e3) : void 0;
2275
+ let fallbackReader = null;
2276
+ const signals = await buildTrustSignalsForRerank(
2277
+ results.map((r) => r.path),
2278
+ namespaces,
2279
+ {
2280
+ readNamespaceMemories: async (ns) => (await this.getStorage(ns)).readAllMemories(),
2281
+ readMemoryFrontmatter: async (path6) => {
2282
+ if (!fallbackReader) {
2283
+ for (const ns of namespaces) {
2284
+ try {
2285
+ fallbackReader = await this.getStorage(ns);
2286
+ break;
2287
+ } catch {
2288
+ }
2289
+ }
2290
+ }
2291
+ if (!fallbackReader) return null;
2292
+ const memory = await this.readQmdResultMemory(path6, fallbackReader, namespaces);
2293
+ return memory ? memory.frontmatter : null;
2294
+ }
2295
+ },
2296
+ { cache: this.trustSignalCache, ttlMs: _RecallRerankCoordinator.TRUST_SIGNAL_CACHE_TTL_MS },
2297
+ now,
2298
+ {
2299
+ recencyHalfLifeDays: halfLifeDays,
2300
+ logDebug: (message, context) => log.debug(message, context)
2301
+ }
2302
+ );
2303
+ if (signals.size === 0) {
2304
+ return { results, trustByPath: null };
2305
+ }
2306
+ const rankedInputs = results.map((r, i) => ({ path: r.path, score: results.length - i }));
2307
+ const stage = applyTrustScoreStage(rankedInputs, {
2308
+ signals,
2309
+ weights: config.trustScoreWeights,
2310
+ minMultiplier: config.trustScoreMinMultiplier,
2311
+ maxMultiplier: config.trustScoreMaxMultiplier,
2312
+ quarantine: config.trustScoreQuarantine
2313
+ });
2314
+ const trustByPath = new Map(stage.all.map((item) => [item.path, item]));
2315
+ const byPath = new Map(results.map((r) => [r.path, r]));
2316
+ const admitted = stage.admitted.map((item) => byPath.get(item.path)).filter((r) => r !== void 0);
2317
+ return { results: admitted, trustByPath };
2318
+ }
2319
+ /**
2320
+ * Issue #1577 — apply the TrustScore stage (or, when trust is off, the Memory
2321
+ * Worth multiplier fallback) to ONE recall branch's results, returning the
2322
+ * scored results + the per-path trust map. Thin wiring over
2323
+ * {@link applyTrustScoreRerank} so every recall path — hot QMD, embedding
2324
+ * fallback, recent scan — applies the SAME multiplier gate (rule 41: a
2325
+ * feature gate must apply across ALL parallel recall paths). TrustScore
2326
+ * subsumes Memory Worth; exactly one runs (rule 39). Fail-open on lookup
2327
+ * errors so a storage hiccup never breaks a fallback path.
2328
+ */
2329
+ async applyTrustScoreToBranch(results, namespaces, caps, label) {
2330
+ if (caps.recallTrustScore && results.length > 0) {
2331
+ try {
2332
+ return await this.applyTrustScoreRerank(results, namespaces);
2333
+ } catch (err) {
2334
+ log.debug(`trust-score stage (${label}) failed open`, {
2335
+ error: err.message
2336
+ });
2337
+ }
2338
+ } else if (caps.recallMemoryWorthFilter && results.length > 0) {
2339
+ try {
2340
+ const filtered = await this.applyMemoryWorthRerank(results, namespaces);
2341
+ return { results: filtered, trustByPath: null };
2342
+ } catch (err) {
2343
+ log.debug(`memory-worth filter (${label}) failed open`, {
2344
+ error: err.message
2345
+ });
2346
+ }
2347
+ }
2348
+ return { results, trustByPath: null };
2349
+ }
2350
+ diversifyAndLimitRecallResults(sectionId, results, limit, retrievalQuery, caps = resolveCapabilities(this.getConfig())) {
2351
+ const safeLimit = typeof limit === "number" && Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : 0;
2352
+ if (!Array.isArray(results) || results.length === 0) return [];
2353
+ if (safeLimit === 0) return [];
2354
+ const boosted = caps.recallReasoningTraceBoost && typeof retrievalQuery === "string" ? applyReasoningTraceBoost(results, {
2355
+ enabled: true,
2356
+ query: retrievalQuery
2357
+ }) : results;
2358
+ const diversified = this.applyMmrToQmdResults(sectionId, boosted, caps);
2359
+ return diversified.slice(0, safeLimit);
2360
+ }
2361
+ /**
2362
+ * Apply Maximal Marginal Relevance to a section's ordered candidate list.
2363
+ *
2364
+ * Operates per-section so one redundant cluster cannot dominate a section,
2365
+ * and so one section's MMR pass cannot starve other sections. Returns the
2366
+ * input unchanged when disabled, when there are fewer than 2 candidates, or
2367
+ * when no budget information is available.
2368
+ */
2369
+ applyMmrToQmdResults(sectionId, results, caps = resolveCapabilities(this.getConfig())) {
2370
+ if (!caps.recallMmr) return results;
2371
+ if (!Array.isArray(results) || results.length < 2) return results;
2372
+ const config = this.getConfig();
2373
+ const configuredTopN = config.recallMmrTopN;
2374
+ const topN = typeof configuredTopN === "number" && Number.isFinite(configuredTopN) ? Math.max(0, Math.floor(configuredTopN)) : 40;
2375
+ if (topN === 0) return results;
2376
+ const lambda = config.recallMmrLambda ?? 0.7;
2377
+ const { reordered, diversity } = reorderRecallResultsWithMmr(results, {
2378
+ lambda,
2379
+ topN
2380
+ });
2381
+ try {
2382
+ log.info(
2383
+ `recall_mmr: section=${sectionId} kept=${diversity.kept}/${diversity.considered} headReorderCount=${diversity.headReorderCount} avgSimBefore=${diversity.avgPairwiseSimBefore.toFixed(3)} avgSimAfter=${diversity.avgPairwiseSimAfter.toFixed(3)} lambda=${lambda.toFixed(2)}`
2384
+ );
2385
+ } catch {
2386
+ }
2387
+ return reordered;
2388
+ }
2389
+ };
2390
+
1942
2391
  // src/maintenance/pattern-reinforcement.ts
1943
2392
  function patternReinforcementKey(content) {
1944
2393
  return content.trim().toLowerCase().replace(/\s+/g, " ").slice(0, 200);
@@ -2272,7 +2721,7 @@ function generateResolverDocument(taxonomy) {
2272
2721
 
2273
2722
  // src/taxonomy/taxonomy-loader.ts
2274
2723
  import { readFile, mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
2275
- import path3 from "path";
2724
+ import path4 from "path";
2276
2725
  var TAXONOMY_DIR = ".taxonomy";
2277
2726
  var TAXONOMY_FILE = "taxonomy.json";
2278
2727
  var MAX_SLUG_LENGTH = 32;
@@ -2370,7 +2819,7 @@ function validateTaxonomy(taxonomy) {
2370
2819
  }
2371
2820
  }
2372
2821
  async function loadTaxonomy(memoryDir) {
2373
- const taxonomyPath = path3.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
2822
+ const taxonomyPath = path4.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
2374
2823
  let raw;
2375
2824
  try {
2376
2825
  raw = await readFile(taxonomyPath, "utf-8");
@@ -2420,16 +2869,16 @@ async function loadTaxonomy(memoryDir) {
2420
2869
  }
2421
2870
  async function saveTaxonomy(memoryDir, taxonomy) {
2422
2871
  validateTaxonomy(taxonomy);
2423
- const dir = path3.join(memoryDir, TAXONOMY_DIR);
2872
+ const dir = path4.join(memoryDir, TAXONOMY_DIR);
2424
2873
  await mkdir2(dir, { recursive: true });
2425
- const filePath = path3.join(dir, TAXONOMY_FILE);
2874
+ const filePath = path4.join(dir, TAXONOMY_FILE);
2426
2875
  await writeFile2(filePath, JSON.stringify(taxonomy, null, 2) + "\n", "utf-8");
2427
2876
  }
2428
2877
  function getTaxonomyDir(memoryDir) {
2429
- return path3.join(memoryDir, TAXONOMY_DIR);
2878
+ return path4.join(memoryDir, TAXONOMY_DIR);
2430
2879
  }
2431
2880
  function getTaxonomyFilePath(memoryDir) {
2432
- return path3.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
2881
+ return path4.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
2433
2882
  }
2434
2883
 
2435
2884
  // src/wearables/registry.ts
@@ -3330,7 +3779,7 @@ async function raceRecallAbort(promise, signal, message = "recall aborted") {
3330
3779
  }
3331
3780
  }
3332
3781
  function qmdCollectionPathParts(resultPath) {
3333
- if (!resultPath || path4.isAbsolute(resultPath)) return null;
3782
+ if (!resultPath || path5.isAbsolute(resultPath)) return null;
3334
3783
  const normalized = resultPath.replace(/\\/g, "/").replace(/^\/+/, "");
3335
3784
  const slashIndex = normalized.indexOf("/");
3336
3785
  if (slashIndex <= 0 || slashIndex >= normalized.length - 1) return null;
@@ -3343,9 +3792,9 @@ function qmdCollectionPathParts(resultPath) {
3343
3792
  }
3344
3793
  function qmdResultPathCandidates(storageDir, resultPath) {
3345
3794
  const candidates = /* @__PURE__ */ new Set();
3346
- const storageRoot = path4.resolve(storageDir);
3795
+ const storageRoot = path5.resolve(storageDir);
3347
3796
  const addCandidate = (candidate) => {
3348
- const resolved = path4.resolve(candidate);
3797
+ const resolved = path5.resolve(candidate);
3349
3798
  if (isPathInsideStorageRoot(storageRoot, resolved)) {
3350
3799
  candidates.add(resolved);
3351
3800
  }
@@ -3353,12 +3802,12 @@ function qmdResultPathCandidates(storageDir, resultPath) {
3353
3802
  const addRelativeCandidates = (relativePath) => {
3354
3803
  const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
3355
3804
  if (!normalized) return;
3356
- addCandidate(path4.join(storageRoot, normalized));
3805
+ addCandidate(path5.join(storageRoot, normalized));
3357
3806
  if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
3358
- addCandidate(path4.join(storageRoot, "facts", normalized));
3807
+ addCandidate(path5.join(storageRoot, "facts", normalized));
3359
3808
  }
3360
3809
  };
3361
- if (path4.isAbsolute(resultPath)) {
3810
+ if (path5.isAbsolute(resultPath)) {
3362
3811
  addCandidate(resultPath);
3363
3812
  } else {
3364
3813
  addRelativeCandidates(resultPath);
@@ -3475,7 +3924,7 @@ async function qmdStartupCollectionCheckWithTimeout(promise, controller, label)
3475
3924
  return await Promise.race([checkedPromise, timeoutPromise]);
3476
3925
  }
3477
3926
  function defaultWorkspaceDir() {
3478
- return path4.join(os2.homedir(), ".openclaw", "workspace");
3927
+ return path5.join(os2.homedir(), ".openclaw", "workspace");
3479
3928
  }
3480
3929
  function sanitizeSessionKeyForFilename(sessionKey) {
3481
3930
  const readable = sessionKey.replace(/[^a-zA-Z0-9._-]/g, "_");
@@ -3752,11 +4201,11 @@ function mergeGraphExpandedResults(primary, expanded) {
3752
4201
  return Array.from(mergedByPath.values());
3753
4202
  }
3754
4203
  function graphPathRelativeToStorage(storageDir, candidatePath) {
3755
- const absolutePath = path4.isAbsolute(candidatePath) ? candidatePath : path4.resolve(storageDir, candidatePath);
3756
- const rel = path4.relative(storageDir, absolutePath);
4204
+ const absolutePath = path5.isAbsolute(candidatePath) ? candidatePath : path5.resolve(storageDir, candidatePath);
4205
+ const rel = path5.relative(storageDir, absolutePath);
3757
4206
  if (!rel || rel === ".") return null;
3758
4207
  if (rel.startsWith("..")) return null;
3759
- return rel.split(path4.sep).join("/");
4208
+ return rel.split(path5.sep).join("/");
3760
4209
  }
3761
4210
  function normalizeGraphActivationScore(score) {
3762
4211
  const bounded = Number.isFinite(score) && score > 0 ? score : 0;
@@ -3901,7 +4350,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
3901
4350
  for (const mem of allMemsForGraph ?? []) {
3902
4351
  const id = mem.frontmatter.id;
3903
4352
  if (!id) continue;
3904
- pathById.set(id, path4.relative(storageDir, mem.path));
4353
+ pathById.set(id, path5.relative(storageDir, mem.path));
3905
4354
  }
3906
4355
  return pathById;
3907
4356
  }
@@ -3909,7 +4358,7 @@ function appendMemoryToGraphContext(options) {
3909
4358
  if (!Array.isArray(options.allMemsForGraph)) return;
3910
4359
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
3911
4360
  options.allMemsForGraph.push({
3912
- path: path4.join(options.storageDir, options.memoryRelPath),
4361
+ path: path5.join(options.storageDir, options.memoryRelPath),
3913
4362
  content: options.content,
3914
4363
  frontmatter: {
3915
4364
  id: options.memoryId,
@@ -3929,16 +4378,16 @@ function resolvePersistedMemoryRelativePath(options) {
3929
4378
  const persisted = options.pathById.get(options.memoryId);
3930
4379
  if (persisted) return persisted;
3931
4380
  if (options.category === "correction") {
3932
- return path4.join("corrections", `${options.memoryId}.md`);
4381
+ return path5.join("corrections", `${options.memoryId}.md`);
3933
4382
  }
3934
4383
  const subtree = categoryDirName(options.category);
3935
4384
  const idParts = options.memoryId.split("-");
3936
4385
  const maybeTimestamp = Number(idParts[1]);
3937
4386
  if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
3938
4387
  const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
3939
- return path4.join(subtree, day, `${options.memoryId}.md`);
4388
+ return path5.join(subtree, day, `${options.memoryId}.md`);
3940
4389
  }
3941
- return path4.join(subtree, `${options.memoryId}.md`);
4390
+ return path5.join(subtree, `${options.memoryId}.md`);
3942
4391
  }
3943
4392
  var Orchestrator = class _Orchestrator {
3944
4393
  storage;
@@ -4045,25 +4494,6 @@ var Orchestrator = class _Orchestrator {
4045
4494
  /** Lossless Context Management engine — proactive session archive + DAG summarization. */
4046
4495
  lcmEngine = null;
4047
4496
  rerankCache = new RerankCache();
4048
- /**
4049
- * Short-TTL cache for Memory Worth counter lookups so interactive recall
4050
- * doesn't trigger a full `readAllMemories` scan per query. Keyed by
4051
- * namespace; the filter unions across namespaces at query time. The TTL
4052
- * is intentionally short (seconds, not minutes) because counters are
4053
- * mutated by `recordMemoryOutcome` asynchronously and we'd rather serve
4054
- * a 30-second-stale worth score than a stable-but-wrong one.
4055
- */
4056
- memoryWorthCounterCache = /* @__PURE__ */ new Map();
4057
- static MEMORY_WORTH_CACHE_TTL_MS = 3e4;
4058
- /**
4059
- * Issue #1577 — per-namespace TrustScore signal map cache. Same TTL/shape
4060
- * discipline as {@link memoryWorthCounterCache}: seconds-scale, evicted on
4061
- * every call, keyed by namespace. Holds the frontmatter-derived signals
4062
- * (worth, provenance, faithfulness, corroboration, recency) so the trust
4063
- * stage doesn't trigger a fresh `readAllMemories` scan per query.
4064
- */
4065
- trustSignalCache = /* @__PURE__ */ new Map();
4066
- static TRUST_SIGNAL_CACHE_TTL_MS = 3e4;
4067
4497
  /**
4068
4498
  * Per-session workspace selections keyed by sessionKey.
4069
4499
  * Set by the before_agent_start hook so recall() uses the correct
@@ -4113,13 +4543,18 @@ var Orchestrator = class _Orchestrator {
4113
4543
  * to RecallResultFormatter.
4114
4544
  */
4115
4545
  recallResultFormatter;
4546
+ /**
4547
+ * Issue #1526: conversation-index subsystem moved to
4548
+ * ConversationIndexCoordinator.
4549
+ */
4550
+ conversationIndexCoordinator;
4551
+ recallRerankCoordinator;
4116
4552
  heartbeatObserverChains = /* @__PURE__ */ new Map();
4117
4553
  recentExtractionFingerprints = /* @__PURE__ */ new Map();
4118
4554
  consolidationObservers = /* @__PURE__ */ new Set();
4119
4555
  wearablesServiceInstance = null;
4120
4556
  wearablesAutoSyncHandle = null;
4121
4557
  lastQmdReprobeAtMs = 0;
4122
- conversationIndexLastUpdateAtMs = /* @__PURE__ */ new Map();
4123
4558
  lastFileHygieneRunAtMs = 0;
4124
4559
  // Pattern-reinforcement cadence gate (issue #687 PR 2/4). Tracks the
4125
4560
  // last successful run so `runPatternReinforcement` can short-circuit
@@ -4406,7 +4841,7 @@ var Orchestrator = class _Orchestrator {
4406
4841
  const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
4407
4842
  if (ns !== defaultNs && !isSafeRouteNamespace(ns)) return;
4408
4843
  if (!this.storageDirMatchesNamespaceHint(ns, storageDir)) return;
4409
- const resolvedStorageDir = path4.resolve(storageDir);
4844
+ const resolvedStorageDir = path5.resolve(storageDir);
4410
4845
  let hints = this.namespaceStorageDirHints.get(resolvedStorageDir);
4411
4846
  if (!hints) {
4412
4847
  hints = /* @__PURE__ */ new Set();
@@ -4417,21 +4852,21 @@ var Orchestrator = class _Orchestrator {
4417
4852
  storageDirMatchesNamespaceHint(namespace, storageDir) {
4418
4853
  const ns = normalizeNamespaceIdentity(namespace);
4419
4854
  if (!ns) return false;
4420
- const resolvedStorageDir = path4.resolve(storageDir);
4421
- const resolvedMemoryDir = path4.resolve(this.config.memoryDir);
4855
+ const resolvedStorageDir = path5.resolve(storageDir);
4856
+ const resolvedMemoryDir = path5.resolve(this.config.memoryDir);
4422
4857
  const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
4423
4858
  if (resolvedStorageDir === resolvedMemoryDir) return ns === defaultNs;
4424
- const resolvedNamespacesDir = path4.join(resolvedMemoryDir, "namespaces");
4859
+ const resolvedNamespacesDir = path5.join(resolvedMemoryDir, "namespaces");
4425
4860
  if (!isPathInsideStorageRoot(resolvedNamespacesDir, resolvedStorageDir)) return false;
4426
- const rawRoot = path4.resolve(resolvedNamespacesDir, ns);
4427
- const tokenRoot = path4.resolve(resolvedNamespacesDir, namespaceIdentityToken(ns));
4861
+ const rawRoot = path5.resolve(resolvedNamespacesDir, ns);
4862
+ const tokenRoot = path5.resolve(resolvedNamespacesDir, namespaceIdentityToken(ns));
4428
4863
  return resolvedStorageDir === rawRoot || resolvedStorageDir === tokenRoot;
4429
4864
  }
4430
4865
  namespaceStorageDirHintOwnershipRank(record, resolvedStorageDir, configured) {
4431
- if (resolvedStorageDir === path4.resolve(this.config.memoryDir)) {
4866
+ if (resolvedStorageDir === path5.resolve(this.config.memoryDir)) {
4432
4867
  return record.namespace === normalizeNamespaceIdentity(this.config.defaultNamespace) ? 0 : 3;
4433
4868
  }
4434
- const leaf = path4.basename(resolvedStorageDir);
4869
+ const leaf = path5.basename(resolvedStorageDir);
4435
4870
  const tokenOwnsRoot = namespaceIdentityToken(record.namespace) === leaf;
4436
4871
  if (tokenOwnsRoot && configured.has(record.namespace)) return 0;
4437
4872
  if (record.namespace === leaf) return 1;
@@ -4459,7 +4894,7 @@ var Orchestrator = class _Orchestrator {
4459
4894
  loadNamespaceStorageDirHintsFromCatalog() {
4460
4895
  if (this.namespaceStorageDirHintsLoaded || !this.namespaceCatalog.enabled) return;
4461
4896
  this.namespaceStorageDirHintsLoaded = true;
4462
- const catalogPath = path4.join(this.config.memoryDir, "state", "namespaces.jsonl");
4897
+ const catalogPath = path5.join(this.config.memoryDir, "state", "namespaces.jsonl");
4463
4898
  if (!existsSync(catalogPath)) return;
4464
4899
  let body;
4465
4900
  try {
@@ -4496,7 +4931,7 @@ var Orchestrator = class _Orchestrator {
4496
4931
  if (!this.storageDirMatchesNamespaceHint(record.namespace, record.storageDir)) {
4497
4932
  continue;
4498
4933
  }
4499
- const resolvedStorageDir = path4.resolve(record.storageDir);
4934
+ const resolvedStorageDir = path5.resolve(record.storageDir);
4500
4935
  const current = preferredByStorageDir.get(resolvedStorageDir);
4501
4936
  preferredByStorageDir.set(
4502
4937
  resolvedStorageDir,
@@ -4625,7 +5060,7 @@ var Orchestrator = class _Orchestrator {
4625
5060
  this.contentHashIndexesByStorageDir.clear();
4626
5061
  }
4627
5062
  async contentHashIndexForStorage(targetStorage) {
4628
- if (!this.config.factDeduplicationEnabled) return null;
5063
+ if (!resolveRecallAuxiliaryCapabilities(this.config).factDeduplication) return null;
4629
5064
  if (targetStorage.dir === this.storage.dir) {
4630
5065
  if (!this.contentHashIndex) {
4631
5066
  this.contentHashIndex = this.storage.createContentHashIndex();
@@ -4760,7 +5195,7 @@ var Orchestrator = class _Orchestrator {
4760
5195
  this.config = config;
4761
5196
  this.profiler = new ProfilingCollector({
4762
5197
  enabled: config.profilingEnabled,
4763
- storageDir: config.profilingStorageDir || path4.join(config.memoryDir, "profiling"),
5198
+ storageDir: config.profilingStorageDir || path5.join(config.memoryDir, "profiling"),
4764
5199
  maxTraces: config.profilingMaxTraces
4765
5200
  });
4766
5201
  this.namespaceCatalog = new NamespaceCatalog(config);
@@ -4782,7 +5217,7 @@ var Orchestrator = class _Orchestrator {
4782
5217
  this.storage.citationTemplate = config.inlineSourceAttributionFormat;
4783
5218
  if (config.defaultNamespace) this.storageRouter.bindCatalogWriteHook(this.storage, config.defaultNamespace);
4784
5219
  this.storage.setVersioningConfig({
4785
- enabled: config.versioningEnabled,
5220
+ enabled: resolveRecallAuxiliaryCapabilities(config).versioning,
4786
5221
  maxVersionsPerPage: config.versioningMaxPerPage,
4787
5222
  sidecarDir: config.versioningSidecarDir
4788
5223
  });
@@ -4798,7 +5233,7 @@ var Orchestrator = class _Orchestrator {
4798
5233
  semanticMatch: tombstonesSemanticMatch,
4799
5234
  semanticThreshold: tombstonesSemanticThreshold
4800
5235
  });
4801
- if (config.secureStoreEnabled) {
5236
+ if (resolveRecallAuxiliaryCapabilities(config).secureStore) {
4802
5237
  this.storage.setSecureStoreRequired(true);
4803
5238
  const storeId = secureStoreDir(config.memoryDir);
4804
5239
  const existingKey = keyring_exports.getKey(storeId);
@@ -4827,14 +5262,25 @@ var Orchestrator = class _Orchestrator {
4827
5262
  this.conversationFaiss = conversationIndexRuntime.faiss;
4828
5263
  this.conversationIndexBackend = conversationIndexRuntime.backend;
4829
5264
  this.sharedContext = config.sharedContextEnabled ? new SharedContextManager(config) : void 0;
4830
- this.compounding = config.compoundingEnabled ? new CompoundingEngine(config, this.storage) : void 0;
5265
+ this.compounding = resolveConsolidationCapabilities(config).compounding ? new CompoundingEngine(config, this.storage) : void 0;
4831
5266
  this.buffer = new SmartBuffer(config, this.storage);
4832
5267
  this.transcript = new TranscriptManager(config);
4833
- this.conversationIndexDir = path4.join(
5268
+ this.conversationIndexDir = path5.join(
4834
5269
  config.memoryDir,
4835
5270
  "conversation-index",
4836
5271
  "chunks"
4837
5272
  );
5273
+ this.conversationIndexCoordinator = new ConversationIndexCoordinator({
5274
+ config,
5275
+ getTranscript: () => this.transcript,
5276
+ getBackend: () => this.conversationIndexBackend,
5277
+ indexDir: this.conversationIndexDir
5278
+ });
5279
+ this.recallRerankCoordinator = new RecallRerankCoordinator({
5280
+ getConfig: () => this.config,
5281
+ getStorage: (namespace) => this.getStorage(namespace),
5282
+ readQmdResultMemory: (resultPath, fallbackStorage, recallNamespaces) => this.readQmdResultMemory(resultPath, fallbackStorage, recallNamespaces)
5283
+ });
4838
5284
  this.modelRegistry = new ModelRegistry(config.memoryDir);
4839
5285
  this.relevance = new RelevanceStore(config.memoryDir);
4840
5286
  this.negatives = new NegativeExampleStore(config.memoryDir);
@@ -4925,7 +5371,7 @@ var Orchestrator = class _Orchestrator {
4925
5371
  saveContentHashIndexes: () => this.saveContentHashIndexes()
4926
5372
  });
4927
5373
  this.threading = new ThreadingManager(
4928
- path4.join(config.memoryDir, "threads"),
5374
+ path5.join(config.memoryDir, "threads"),
4929
5375
  config.threadingGapMinutes
4930
5376
  );
4931
5377
  const lifecycleCaps = resolveMemoryLifecycleCapabilities(config);
@@ -4972,7 +5418,7 @@ var Orchestrator = class _Orchestrator {
4972
5418
  this.boxBuilders.set(
4973
5419
  dir,
4974
5420
  new BoxBuilder(dir, {
4975
- memoryBoxesEnabled: this.config.memoryBoxesEnabled,
5421
+ memoryBoxesEnabled: resolvePresentationCapabilities(this.config).memoryBoxes,
4976
5422
  traceWeaverEnabled: this.config.traceWeaverEnabled,
4977
5423
  boxTopicShiftThreshold: this.config.boxTopicShiftThreshold,
4978
5424
  boxTimeGapMs: this.config.boxTimeGapMs,
@@ -5219,7 +5665,7 @@ var Orchestrator = class _Orchestrator {
5219
5665
  memoryUtilityLearningEnabled: resolveUtilityLearningCapabilities(this.config).memoryUtilityLearning,
5220
5666
  promotionByOutcomeEnabled: resolveUtilityLearningCapabilities(this.config).promotionByOutcome
5221
5667
  });
5222
- if (this.config.factDeduplicationEnabled) {
5668
+ if (resolveRecallAuxiliaryCapabilities(this.config).factDeduplication) {
5223
5669
  this.contentHashIndex = this.storage.createContentHashIndex();
5224
5670
  await this.contentHashIndex.load();
5225
5671
  log.info(
@@ -5242,13 +5688,13 @@ var Orchestrator = class _Orchestrator {
5242
5688
  );
5243
5689
  this.buffer.resetToEmpty();
5244
5690
  }
5245
- if (this.config.compactionResetEnabled) {
5691
+ if (resolveRecallAuxiliaryCapabilities(this.config).compactionReset) {
5246
5692
  try {
5247
5693
  const wsDir = this.config.workspaceDir || defaultWorkspaceDir();
5248
- const files = await readdir(wsDir).catch(() => []);
5694
+ const files = await readdir2(wsDir).catch(() => []);
5249
5695
  for (const f of files) {
5250
5696
  if (!f.startsWith(".compaction-reset-signal-")) continue;
5251
- const fp = path4.join(wsDir, f);
5697
+ const fp = path5.join(wsDir, f);
5252
5698
  const s = await stat(fp).catch(() => null);
5253
5699
  if (s && Date.now() - s.mtimeMs >= COMPACTION_SIGNAL_MAX_AGE_MS) {
5254
5700
  await unlink(fp).catch(() => {
@@ -5391,7 +5837,7 @@ var Orchestrator = class _Orchestrator {
5391
5837
  await Promise.all(warmupPromises);
5392
5838
  if (signal.aborted) return;
5393
5839
  const cacheWarmups = [];
5394
- if (this.config.knowledgeIndexEnabled) {
5840
+ if (resolveRecallAuxiliaryCapabilities(this.config).knowledgeIndex) {
5395
5841
  cacheWarmups.push(
5396
5842
  (async () => {
5397
5843
  try {
@@ -5443,7 +5889,7 @@ var Orchestrator = class _Orchestrator {
5443
5889
  if (signal.aborted) return;
5444
5890
  if (lifecycleCaps.lifecyclePolicy && resolveQmdCapabilities(this.config).qmdTierMigration) {
5445
5891
  try {
5446
- const { runFirstStartMigration } = await import("./first-start-migration-2RQYDIZ7.js");
5892
+ const { runFirstStartMigration } = await import("./first-start-migration-5DXD3TF2.js");
5447
5893
  const result = await runFirstStartMigration({
5448
5894
  storage: this.storage,
5449
5895
  config: this.config,
@@ -5617,7 +6063,7 @@ var Orchestrator = class _Orchestrator {
5617
6063
  */
5618
6064
  async runPatternReinforcement(options = {}) {
5619
6065
  const cadenceKey = options.namespace ?? "";
5620
- if (!this.config.patternReinforcementEnabled) {
6066
+ if (!resolveConsolidationCapabilities(this.config).patternReinforcement) {
5621
6067
  return { ran: false, skippedReason: "disabled", namespace: cadenceKey };
5622
6068
  }
5623
6069
  const cadence = this.config.patternReinforcementCadenceMs;
@@ -5658,7 +6104,7 @@ var Orchestrator = class _Orchestrator {
5658
6104
  }
5659
6105
  return result.result ? { itemCount: result.result.clustersFound } : { itemCount: 0 };
5660
6106
  },
5661
- { enabled: this.config.patternReinforcementEnabled }
6107
+ { enabled: resolveConsolidationCapabilities(this.config).patternReinforcement }
5662
6108
  );
5663
6109
  }
5664
6110
  /**
@@ -5698,7 +6144,7 @@ var Orchestrator = class _Orchestrator {
5698
6144
  });
5699
6145
  return { itemCount: result.clustersFound };
5700
6146
  },
5701
- { enabled: this.config.semanticConsolidationEnabled }
6147
+ { enabled: resolveConsolidationCapabilities(this.config).semanticConsolidation }
5702
6148
  );
5703
6149
  }
5704
6150
  /**
@@ -5757,15 +6203,15 @@ ${doc.content}` : doc.content,
5757
6203
  this.lastFileHygieneRunAtMs = now;
5758
6204
  if (hygiene.rotateEnabled) {
5759
6205
  for (const rel of hygiene.rotatePaths) {
5760
- const abs = path4.isAbsolute(rel) ? rel : path4.join(this.config.workspaceDir, rel);
6206
+ const abs = path5.isAbsolute(rel) ? rel : path5.join(this.config.workspaceDir, rel);
5761
6207
  try {
5762
6208
  const raw = await readFile2(abs, "utf-8");
5763
6209
  if (raw.length > hygiene.rotateMaxBytes) {
5764
- const archiveDir = path4.join(
6210
+ const archiveDir = path5.join(
5765
6211
  this.config.workspaceDir,
5766
6212
  hygiene.archiveDir
5767
6213
  );
5768
- const base = path4.basename(abs);
6214
+ const base = path5.basename(abs);
5769
6215
  const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
5770
6216
  const { newContent } = await rotateMarkdownFileToArchive({
5771
6217
  filePath: abs,
@@ -5790,8 +6236,8 @@ ${doc.content}` : doc.content,
5790
6236
  log.warn(w.message);
5791
6237
  }
5792
6238
  if (hygiene.warningsLogEnabled && warnings.length > 0) {
5793
- const fp = path4.join(this.config.memoryDir, hygiene.warningsLogPath);
5794
- await mkdir3(path4.dirname(fp), { recursive: true });
6239
+ const fp = path5.join(this.config.memoryDir, hygiene.warningsLogPath);
6240
+ await mkdir3(path5.dirname(fp), { recursive: true });
5795
6241
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
5796
6242
  const block = `
5797
6243
 
@@ -5930,16 +6376,16 @@ ${doc.content}` : doc.content,
5930
6376
  for (const categoryDir of RECALL_FALLBACK_DIRS) {
5931
6377
  if (memoryRootReal === null) break;
5932
6378
  for (const date of datesToScan) {
5933
- const dateDir = path4.join(storage.dir, categoryDir, date);
6379
+ const dateDir = path5.join(storage.dir, categoryDir, date);
5934
6380
  try {
5935
6381
  const dirStat = await lstat(dateDir);
5936
6382
  if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) continue;
5937
6383
  assertPathInsideRoot(memoryRootReal, await realpath(dateDir), dateDir);
5938
- const entries = await readdir(dateDir, { withFileTypes: true });
6384
+ const entries = await readdir2(dateDir, { withFileTypes: true });
5939
6385
  for (const entry of entries) {
5940
6386
  if (entry.isSymbolicLink()) continue;
5941
6387
  if (!entry.name.endsWith(".md")) continue;
5942
- const fullPath = path4.join(dateDir, entry.name);
6388
+ const fullPath = path5.join(dateDir, entry.name);
5943
6389
  try {
5944
6390
  assertPathInsideRoot(memoryRootReal, await realpath(fullPath), fullPath);
5945
6391
  const raw = await readFile2(fullPath, "utf-8");
@@ -5961,7 +6407,7 @@ ${doc.content}` : doc.content,
5961
6407
  facts.push({
5962
6408
  path: fullPath,
5963
6409
  frontmatter: {
5964
- id: fm.id || path4.basename(entry.name, ".md"),
6410
+ id: fm.id || path5.basename(entry.name, ".md"),
5965
6411
  category: fm.category || "fact",
5966
6412
  created,
5967
6413
  updated: fm.updated || created,
@@ -5984,13 +6430,13 @@ ${doc.content}` : doc.content,
5984
6430
  return a.frontmatter.created < b.frontmatter.created ? -1 : 1;
5985
6431
  });
5986
6432
  const hourlySummaries = [];
5987
- const hourlyBaseDir = path4.join(storage.dir, "summaries", "hourly");
6433
+ const hourlyBaseDir = path5.join(storage.dir, "summaries", "hourly");
5988
6434
  try {
5989
- const sessionKeys = await readdir(hourlyBaseDir, { withFileTypes: true });
6435
+ const sessionKeys = await readdir2(hourlyBaseDir, { withFileTypes: true });
5990
6436
  for (const sk of sessionKeys) {
5991
6437
  if (!sk.isDirectory()) continue;
5992
6438
  for (const date of datesToScan) {
5993
- const summaryFile = path4.join(hourlyBaseDir, sk.name, `${date}.md`);
6439
+ const summaryFile = path5.join(hourlyBaseDir, sk.name, `${date}.md`);
5994
6440
  try {
5995
6441
  const raw = await readFile2(summaryFile, "utf-8");
5996
6442
  const filtered = filterHourlySummaryMarkdownForLocalDay(
@@ -6094,7 +6540,7 @@ ${doc.content}` : doc.content,
6094
6540
  }
6095
6541
  async getLastGraphRecallSnapshot(namespace) {
6096
6542
  const storage = await this.getStorage(namespace);
6097
- const snapshotPath = path4.join(
6543
+ const snapshotPath = path5.join(
6098
6544
  storage.dir,
6099
6545
  "state",
6100
6546
  "last_graph_recall.json"
@@ -6133,7 +6579,7 @@ ${doc.content}` : doc.content,
6133
6579
  }
6134
6580
  async getLastIntentSnapshot(namespace) {
6135
6581
  const storage = await this.getStorage(namespace);
6136
- const snapshotPath = path4.join(storage.dir, "state", "last_intent.json");
6582
+ const snapshotPath = path5.join(storage.dir, "state", "last_intent.json");
6137
6583
  try {
6138
6584
  const raw = await readFile2(snapshotPath, "utf-8");
6139
6585
  const parsed = JSON.parse(raw);
@@ -6166,7 +6612,7 @@ ${doc.content}` : doc.content,
6166
6612
  }
6167
6613
  async getLastQmdRecallSnapshot(namespace) {
6168
6614
  const storage = await this.getStorage(namespace);
6169
- const snapshotPath = path4.join(
6615
+ const snapshotPath = path5.join(
6170
6616
  storage.dir,
6171
6617
  "state",
6172
6618
  "last_qmd_recall.json"
@@ -6291,206 +6737,31 @@ ${doc.content}` : doc.content,
6291
6737
  })
6292
6738
  ].join("\n");
6293
6739
  }
6294
- async searchConversationRecallResults(retrievalQuery, topK) {
6295
- if (this.conversationIndexBackend) {
6296
- return this.conversationIndexBackend.search(retrievalQuery, topK);
6297
- }
6298
- return [];
6299
- }
6300
- formatConversationRecallSection(results, maxChars) {
6301
- if (!Array.isArray(results) || results.length === 0) return null;
6302
- const lines = ["## Semantic Recall (Past Conversations)", ""];
6303
- let used = 0;
6304
- for (const r of results) {
6305
- if (!r?.snippet) continue;
6306
- const chunk = `### ${r.path}
6307
- Score: ${r.score.toFixed(3)}
6308
-
6309
- ${r.snippet.trim()}
6310
- `;
6311
- if (used + chunk.length > maxChars) break;
6312
- lines.push(chunk);
6313
- used += chunk.length;
6314
- }
6315
- return used > 0 ? lines.join("\n") : null;
6316
- }
6317
- async countConversationChunkDocs(dir) {
6318
- try {
6319
- const entries = await readdir(dir, { withFileTypes: true });
6320
- let total = 0;
6321
- for (const entry of entries) {
6322
- const fullPath = path4.join(dir, entry.name);
6323
- if (entry.isDirectory()) {
6324
- total += await this.countConversationChunkDocs(fullPath);
6325
- continue;
6326
- }
6327
- if (entry.isFile() && entry.name.endsWith(".md")) {
6328
- total += 1;
6329
- }
6330
- }
6331
- return total;
6332
- } catch {
6333
- return 0;
6334
- }
6335
- }
6336
- async buildConversationIndexChunks(sessionKey, hours = 24) {
6337
- const entries = await this.transcript.readRecent(hours, sessionKey);
6338
- const effectiveSessionKey = sessionKey ?? "all-sessions";
6339
- return chunkTranscriptEntries(effectiveSessionKey, entries, {
6340
- maxChars: this.config.conversationRecallMaxChars * 2,
6341
- maxTurns: Math.max(10, this.config.hourlySummariesMaxTurnsPerRun)
6342
- });
6343
- }
6344
- async getConversationIndexHealth() {
6345
- const chunkDocCount = await this.countConversationChunkDocs(
6346
- this.conversationIndexDir
6347
- );
6348
- const lastUpdateAtMs = Math.max(
6349
- 0,
6350
- ...this.conversationIndexLastUpdateAtMs.values()
6351
- );
6352
- const lastUpdateAt = lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
6353
- if (!resolveIndexingCapabilities(this.config).conversationIndex) {
6354
- return {
6355
- enabled: false,
6356
- backend: this.config.conversationIndexBackend,
6357
- status: "disabled",
6358
- chunkDocCount,
6359
- lastUpdateAt
6360
- };
6361
- }
6362
- const backendHealth = this.conversationIndexBackend ? await this.conversationIndexBackend.health() : {
6363
- backend: this.config.conversationIndexBackend,
6364
- status: "degraded"
6365
- };
6366
- return {
6367
- enabled: true,
6368
- chunkDocCount,
6369
- lastUpdateAt,
6370
- ...backendHealth
6371
- };
6372
- }
6373
- async inspectConversationIndex() {
6374
- const chunkDocCount = await this.countConversationChunkDocs(
6375
- this.conversationIndexDir
6376
- );
6377
- const lastUpdateAtMs = Math.max(
6378
- 0,
6379
- ...this.conversationIndexLastUpdateAtMs.values()
6380
- );
6381
- const lastUpdateAt = lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
6382
- if (!resolveIndexingCapabilities(this.config).conversationIndex) {
6383
- return {
6384
- enabled: false,
6385
- backend: this.config.conversationIndexBackend,
6386
- status: "disabled",
6387
- available: false,
6388
- indexPath: this.conversationIndexDir,
6389
- supportsIncrementalUpdate: true,
6390
- message: "Conversation index disabled by config",
6391
- metadata: {
6392
- chunkCount: chunkDocCount
6393
- },
6394
- chunkDocCount,
6395
- lastUpdateAt
6396
- };
6397
- }
6398
- const inspection = this.conversationIndexBackend ? await this.conversationIndexBackend.inspect() : {
6399
- backend: this.config.conversationIndexBackend,
6400
- status: "degraded",
6401
- available: false,
6402
- indexPath: this.conversationIndexDir,
6403
- supportsIncrementalUpdate: true,
6404
- message: "Conversation index backend unavailable",
6405
- metadata: {
6406
- chunkCount: chunkDocCount
6407
- }
6408
- };
6409
- return {
6410
- enabled: true,
6411
- chunkDocCount,
6412
- lastUpdateAt,
6413
- ...inspection
6414
- };
6740
+ async searchConversationRecallResults(retrievalQuery, topK) {
6741
+ return this.conversationIndexCoordinator.search(retrievalQuery, topK);
6742
+ }
6743
+ formatConversationRecallSection(results, maxChars) {
6744
+ return this.conversationIndexCoordinator.formatRecallSection(
6745
+ results,
6746
+ maxChars
6747
+ );
6748
+ }
6749
+ // Issue #1526: countConversationChunkDocs / buildConversationIndexChunks moved
6750
+ // to ConversationIndexCoordinator (internal helpers, no orchestrator callers).
6751
+ async getConversationIndexHealth() {
6752
+ return this.conversationIndexCoordinator.getHealth();
6753
+ }
6754
+ async inspectConversationIndex() {
6755
+ return this.conversationIndexCoordinator.inspect();
6415
6756
  }
6416
6757
  async getRecoverySummary(sessionKey) {
6417
6758
  return this.transcript.getRecoverySummary(sessionKey);
6418
6759
  }
6419
6760
  async updateConversationIndex(sessionKey, hours = 24, opts) {
6420
- if (!resolveIndexingCapabilities(this.config).conversationIndex) {
6421
- return { chunks: 0, skipped: true, reason: "disabled", embedded: false };
6422
- }
6423
- const enforceMinInterval = opts?.enforceMinInterval !== false;
6424
- if (enforceMinInterval) {
6425
- const minIntervalMs = Math.max(
6426
- 0,
6427
- this.config.conversationIndexMinUpdateIntervalMs
6428
- );
6429
- const now = Date.now();
6430
- const last = this.conversationIndexLastUpdateAtMs.get(sessionKey) ?? 0;
6431
- const elapsed = now - last;
6432
- if (minIntervalMs > 0 && elapsed < minIntervalMs) {
6433
- return {
6434
- chunks: 0,
6435
- skipped: true,
6436
- reason: "min_interval",
6437
- retryAfterMs: minIntervalMs - elapsed,
6438
- embedded: false
6439
- };
6440
- }
6441
- }
6442
- const chunks = await this.buildConversationIndexChunks(sessionKey, hours);
6443
- await writeConversationChunks(this.conversationIndexDir, chunks);
6444
- const retentionCutoffMs = Number.isFinite(this.config.conversationIndexRetentionDays) && this.config.conversationIndexRetentionDays > 0 ? Date.now() - this.config.conversationIndexRetentionDays * 24 * 60 * 60 * 1e3 : void 0;
6445
- await cleanupConversationChunks(
6446
- this.conversationIndexDir,
6447
- this.config.conversationIndexRetentionDays
6448
- );
6449
- const shouldEmbed = opts?.embed ?? this.config.conversationIndexEmbedOnUpdate;
6450
- let embedded = false;
6451
- if (this.conversationIndexBackend) {
6452
- const result = await this.conversationIndexBackend.update(chunks, {
6453
- embed: shouldEmbed,
6454
- ...retentionCutoffMs !== void 0 ? { retentionCutoffMs } : {}
6455
- });
6456
- embedded = result.embedded;
6457
- }
6458
- this.conversationIndexLastUpdateAtMs.set(sessionKey, Date.now());
6459
- return { chunks: chunks.length, skipped: false, embedded };
6761
+ return this.conversationIndexCoordinator.update(sessionKey, hours, opts);
6460
6762
  }
6461
6763
  async rebuildConversationIndex(sessionKey, hours = 24, opts) {
6462
- if (!resolveIndexingCapabilities(this.config).conversationIndex) {
6463
- return {
6464
- chunks: 0,
6465
- skipped: true,
6466
- reason: "disabled",
6467
- embedded: false,
6468
- rebuilt: false
6469
- };
6470
- }
6471
- const chunks = await this.buildConversationIndexChunks(sessionKey, hours);
6472
- await writeConversationChunks(this.conversationIndexDir, chunks);
6473
- await cleanupConversationChunks(
6474
- this.conversationIndexDir,
6475
- this.config.conversationIndexRetentionDays
6476
- );
6477
- const shouldEmbed = opts?.embed ?? this.config.conversationIndexEmbedOnUpdate;
6478
- let embedded = false;
6479
- let rebuilt = false;
6480
- if (this.conversationIndexBackend) {
6481
- const result = await this.conversationIndexBackend.rebuild(chunks, {
6482
- embed: shouldEmbed
6483
- });
6484
- embedded = result.embedded;
6485
- rebuilt = result.rebuilt;
6486
- }
6487
- const stamp = Date.now();
6488
- if (sessionKey) {
6489
- this.conversationIndexLastUpdateAtMs.set(sessionKey, stamp);
6490
- } else {
6491
- this.conversationIndexLastUpdateAtMs.set("__rebuild__", stamp);
6492
- }
6493
- return { chunks: chunks.length, skipped: false, embedded, rebuilt };
6764
+ return this.conversationIndexCoordinator.rebuild(sessionKey, hours, opts);
6494
6765
  }
6495
6766
  /**
6496
6767
  * Validate local LLM model availability and context window compatibility.
@@ -6582,7 +6853,7 @@ ${r.snippet.trim()}
6582
6853
  log.warn("recall: init gate timed out \u2014 proceeding without full init");
6583
6854
  }
6584
6855
  }
6585
- if (this.config.secureStoreEnabled && !this.storage.isSecureStoreUnlocked()) {
6856
+ if (resolveRecallAuxiliaryCapabilities(this.config).secureStore && !this.storage.isSecureStoreUnlocked()) {
6586
6857
  const lockedMsg = "[secure-store locked] Memory store is encrypted and locked. Unlock the secure-store inside this daemon process, or restart the daemon through a secure-store aware launcher that installs the key.";
6587
6858
  log.warn("recall blocked: secure-store is locked");
6588
6859
  return lockedMsg;
@@ -6686,7 +6957,7 @@ ${r.snippet.trim()}
6686
6957
  });
6687
6958
  const observationNamespaces = observationScopePlan.readNamespaces;
6688
6959
  const observationQueryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
6689
- cronRecallPolicyEnabled: this.config.cronRecallPolicyEnabled,
6960
+ cronRecallPolicyEnabled: resolveRecallAuxiliaryCapabilities(this.config).cronRecallPolicy,
6690
6961
  cronRecallNormalizedQueryMaxChars: this.config.cronRecallNormalizedQueryMaxChars,
6691
6962
  cronRecallInstructionHeavyTokenCap: this.effectiveCronRecallInstructionHeavyTokenCap(),
6692
6963
  cronConversationRecallMode: this.config.cronConversationRecallMode
@@ -7225,7 +7496,7 @@ ${r.snippet.trim()}
7225
7496
  resolvedPath = resolvedCold.result.path;
7226
7497
  resolvedResult = resolvedCold.result;
7227
7498
  }
7228
- if (!path4.isAbsolute(resolvedPath)) {
7499
+ if (!path5.isAbsolute(resolvedPath)) {
7229
7500
  resolvedAmbiguousSeeds.set(result.path, null);
7230
7501
  return null;
7231
7502
  }
@@ -7250,7 +7521,7 @@ ${r.snippet.trim()}
7250
7521
  }
7251
7522
  continue;
7252
7523
  }
7253
- if (path4.isAbsolute(result.path)) {
7524
+ if (path5.isAbsolute(result.path)) {
7254
7525
  const resolved = await resolveAmbiguousSeedOwner(result, null);
7255
7526
  if (resolved) {
7256
7527
  addResultForNamespace(resolved.namespace, resolved.result);
@@ -7293,7 +7564,7 @@ ${r.snippet.trim()}
7293
7564
  0
7294
7565
  );
7295
7566
  seedPaths.push(
7296
- ...seedRelativePaths.map((rel) => path4.join(storage.dir, rel))
7567
+ ...seedRelativePaths.map((rel) => path5.join(storage.dir, rel))
7297
7568
  );
7298
7569
  const seedSet = new Set(seedRelativePaths);
7299
7570
  const expanded = await this.graphIndexFor(storage).spreadingActivation(
@@ -7309,7 +7580,7 @@ ${r.snippet.trim()}
7309
7580
  for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
7310
7581
  if (deadlineExpired()) break;
7311
7582
  if (seedSet.has(candidate.path)) continue;
7312
- const memoryPath = path4.resolve(storage.dir, candidate.path);
7583
+ const memoryPath = path5.resolve(storage.dir, candidate.path);
7313
7584
  const memory = await storage.readMemoryByPath(memoryPath);
7314
7585
  if (deadlineExpired()) break;
7315
7586
  if (!memory) continue;
@@ -7334,7 +7605,7 @@ ${r.snippet.trim()}
7334
7605
  path: memory.path,
7335
7606
  score,
7336
7607
  namespace,
7337
- seed: path4.resolve(storage.dir, candidate.seed),
7608
+ seed: path5.resolve(storage.dir, candidate.seed),
7338
7609
  hopDepth: candidate.hopDepth,
7339
7610
  decayedWeight: candidate.decayedWeight,
7340
7611
  graphType: candidate.graphType,
@@ -7355,12 +7626,12 @@ ${r.snippet.trim()}
7355
7626
  }
7356
7627
  async recordLastGraphRecallSnapshot(options) {
7357
7628
  try {
7358
- const snapshotPath = path4.join(
7629
+ const snapshotPath = path5.join(
7359
7630
  options.storage.dir,
7360
7631
  "state",
7361
7632
  "last_graph_recall.json"
7362
7633
  );
7363
- await mkdir3(path4.dirname(snapshotPath), { recursive: true });
7634
+ await mkdir3(path5.dirname(snapshotPath), { recursive: true });
7364
7635
  const now = (/* @__PURE__ */ new Date()).toISOString();
7365
7636
  const totalSeedCount = options.seedPaths.length;
7366
7637
  const totalExpandedCount = options.expandedPaths.length;
@@ -7394,12 +7665,12 @@ ${r.snippet.trim()}
7394
7665
  }
7395
7666
  async recordLastIntentSnapshot(options) {
7396
7667
  try {
7397
- const snapshotPath = path4.join(
7668
+ const snapshotPath = path5.join(
7398
7669
  options.storage.dir,
7399
7670
  "state",
7400
7671
  "last_intent.json"
7401
7672
  );
7402
- await mkdir3(path4.dirname(snapshotPath), { recursive: true });
7673
+ await mkdir3(path5.dirname(snapshotPath), { recursive: true });
7403
7674
  await writeFile3(
7404
7675
  snapshotPath,
7405
7676
  JSON.stringify(options.snapshot, null, 2),
@@ -7411,12 +7682,12 @@ ${r.snippet.trim()}
7411
7682
  }
7412
7683
  async recordLastQmdRecallSnapshot(options) {
7413
7684
  try {
7414
- const snapshotPath = path4.join(
7685
+ const snapshotPath = path5.join(
7415
7686
  options.storage.dir,
7416
7687
  "state",
7417
7688
  "last_qmd_recall.json"
7418
7689
  );
7419
- await mkdir3(path4.dirname(snapshotPath), { recursive: true });
7690
+ await mkdir3(path5.dirname(snapshotPath), { recursive: true });
7420
7691
  await writeFile3(
7421
7692
  snapshotPath,
7422
7693
  JSON.stringify(options.snapshot, null, 2),
@@ -7431,8 +7702,8 @@ ${r.snippet.trim()}
7431
7702
  const stateDir = await this.resolveStateDirForNamespace(
7432
7703
  options.namespace
7433
7704
  );
7434
- const snapshotPath = path4.join(stateDir, "last_intent.json");
7435
- await mkdir3(path4.dirname(snapshotPath), { recursive: true });
7705
+ const snapshotPath = path5.join(stateDir, "last_intent.json");
7706
+ await mkdir3(path5.dirname(snapshotPath), { recursive: true });
7436
7707
  await writeFile3(
7437
7708
  snapshotPath,
7438
7709
  JSON.stringify(options.snapshot, null, 2),
@@ -7444,12 +7715,12 @@ ${r.snippet.trim()}
7444
7715
  }
7445
7716
  async resolveStateDirForNamespace(namespace) {
7446
7717
  if (!resolveNamespaceCapabilities(this.config).namespaces) {
7447
- return path4.join(this.config.memoryDir, "state");
7718
+ return path5.join(this.config.memoryDir, "state");
7448
7719
  }
7449
7720
  if (namespace !== this.config.defaultNamespace) {
7450
- return path4.join(this.config.memoryDir, "namespaces", namespace, "state");
7721
+ return path5.join(this.config.memoryDir, "namespaces", namespace, "state");
7451
7722
  }
7452
- const candidate = path4.join(
7723
+ const candidate = path5.join(
7453
7724
  this.config.memoryDir,
7454
7725
  "namespaces",
7455
7726
  this.config.defaultNamespace
@@ -7457,11 +7728,11 @@ ${r.snippet.trim()}
7457
7728
  try {
7458
7729
  const candidateStat = await stat(candidate);
7459
7730
  if (candidateStat.isDirectory()) {
7460
- return path4.join(candidate, "state");
7731
+ return path5.join(candidate, "state");
7461
7732
  }
7462
7733
  } catch {
7463
7734
  }
7464
- return path4.join(this.config.memoryDir, "state");
7735
+ return path5.join(this.config.memoryDir, "state");
7465
7736
  }
7466
7737
  buildGraphRecallRankedResults(results, sourceLabelResolver, limit = 64) {
7467
7738
  return results.slice(0, limit).map((result) => ({
@@ -7718,7 +7989,7 @@ ${r.snippet.trim()}
7718
7989
  const traceId = createHash2("sha256").update(`${sessionKey ?? "default"}:${recallStart}:${promptHash}`).digest("hex").slice(0, 16);
7719
7990
  const sectionBuckets = /* @__PURE__ */ new Map();
7720
7991
  const queryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
7721
- cronRecallPolicyEnabled: this.config.cronRecallPolicyEnabled,
7992
+ cronRecallPolicyEnabled: resolveRecallAuxiliaryCapabilities(this.config).cronRecallPolicy,
7722
7993
  cronRecallNormalizedQueryMaxChars: this.config.cronRecallNormalizedQueryMaxChars,
7723
7994
  cronRecallInstructionHeavyTokenCap: this.effectiveCronRecallInstructionHeavyTokenCap(),
7724
7995
  cronConversationRecallMode: this.config.cronConversationRecallMode
@@ -7803,7 +8074,7 @@ ${r.snippet.trim()}
7803
8074
  }
7804
8075
  return limit;
7805
8076
  })() : 0;
7806
- const recallHeadroom = this.config.verbatimArtifactsEnabled ? Math.max(12, this.config.verbatimArtifactsMaxRecall * 4) : 12;
8077
+ const recallHeadroom = resolvePresentationCapabilities(this.config).verbatimArtifacts ? Math.max(12, this.config.verbatimArtifactsMaxRecall * 4) : 12;
7807
8078
  const computedFetchLimit = recallResultLimit === 0 ? 0 : Math.max(
7808
8079
  recallResultLimit,
7809
8080
  Math.min(200, recallResultLimit + recallHeadroom)
@@ -7811,7 +8082,7 @@ ${r.snippet.trim()}
7811
8082
  const qmdFetchLimit = computedFetchLimit;
7812
8083
  const qmdHybridFetchLimit = computeQmdHybridFetchLimit(
7813
8084
  qmdFetchLimit,
7814
- this.config.verbatimArtifactsEnabled,
8085
+ resolvePresentationCapabilities(this.config).verbatimArtifacts,
7815
8086
  this.config.verbatimArtifactsMaxRecall
7816
8087
  );
7817
8088
  const embeddingFetchLimit = computedFetchLimit;
@@ -7863,7 +8134,7 @@ ${r.snippet.trim()}
7863
8134
  const graphExpandedResultPaths = /* @__PURE__ */ new Set();
7864
8135
  const graphSourceLabelsForPath = (resultPath) => {
7865
8136
  const labels = [];
7866
- const normalizedPath = resultPath.split(path4.sep).join("/");
8137
+ const normalizedPath = resultPath.split(path5.sep).join("/");
7867
8138
  const isEntityPath = normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/");
7868
8139
  if (graphBaselinePaths.has(resultPath)) labels.push("baseline");
7869
8140
  if (graphExpandedResultPaths.has(resultPath))
@@ -8013,7 +8284,7 @@ ${r.snippet.trim()}
8013
8284
  profileStorageNamespaces.map((namespace) => this.storageRouter.storageFor(namespace))
8014
8285
  );
8015
8286
  const emptyProfileStorage = new Proxy(
8016
- { dir: path4.join(this.config.memoryDir, ".empty-scope-profile") },
8287
+ { dir: path5.join(this.config.memoryDir, ".empty-scope-profile") },
8017
8288
  {
8018
8289
  get(target, prop) {
8019
8290
  if (prop in target) return target[prop];
@@ -8319,10 +8590,10 @@ ${lines.join("\n\n")}`;
8319
8590
  const entityRetrievalPromise = (async () => {
8320
8591
  if (!this.isRecallSectionEnabled(
8321
8592
  "entity-retrieval",
8322
- this.config.entityRetrievalEnabled
8593
+ resolveRecallAuxiliaryCapabilities(this.config).entityRetrieval
8323
8594
  ))
8324
8595
  return null;
8325
- if (!this.config.entityRetrievalEnabled) return null;
8596
+ if (!resolveRecallAuxiliaryCapabilities(this.config).entityRetrieval) return null;
8326
8597
  const maxChars = this.getRecallSectionMaxChars("entity-retrieval") ?? this.config.entityRetrievalMaxChars;
8327
8598
  const maxHints = this.getRecallSectionNumber("entity-retrieval", "maxHints") ?? this.config.entityRetrievalMaxHints;
8328
8599
  const maxSupportingFacts = this.getRecallSectionNumber("entity-retrieval", "maxSupportingFacts") ?? this.config.entityRetrievalMaxSupportingFacts;
@@ -8377,10 +8648,10 @@ ${lines.join("\n\n")}`;
8377
8648
  const knowledgeIndexPromise = (async () => {
8378
8649
  if (!this.isRecallSectionEnabled(
8379
8650
  "knowledge-index",
8380
- this.config.knowledgeIndexEnabled
8651
+ resolveRecallAuxiliaryCapabilities(this.config).knowledgeIndex
8381
8652
  ))
8382
8653
  return null;
8383
- if (!this.config.knowledgeIndexEnabled) return null;
8654
+ if (!resolveRecallAuxiliaryCapabilities(this.config).knowledgeIndex) return null;
8384
8655
  const t0 = Date.now();
8385
8656
  try {
8386
8657
  const knowledgeIndexMaxChars = this.getRecallSectionNumber("knowledge-index", "maxChars") ?? this.config.knowledgeIndexMaxChars;
@@ -8456,10 +8727,10 @@ ${rows.join("\n")}
8456
8727
  const artifactsPromise = (async () => {
8457
8728
  if (!this.isRecallSectionEnabled(
8458
8729
  "verbatim-artifacts",
8459
- this.config.verbatimArtifactsEnabled === true
8730
+ resolvePresentationCapabilities(this.config).verbatimArtifacts === true
8460
8731
  ))
8461
8732
  return [];
8462
- if (!this.config.verbatimArtifactsEnabled) return [];
8733
+ if (!resolvePresentationCapabilities(this.config).verbatimArtifacts) return [];
8463
8734
  const t0 = Date.now();
8464
8735
  const targetCount = computeArtifactRecallLimit(
8465
8736
  recallMode,
@@ -8656,9 +8927,9 @@ ${rows.join("\n")}
8656
8927
  })();
8657
8928
  const calibrationPromise = (async () => {
8658
8929
  const t0 = Date.now();
8659
- if (!this.config.calibrationEnabled || !this.isRecallSectionEnabled(
8930
+ if (!resolveConsolidationCapabilities(this.config).calibration || !this.isRecallSectionEnabled(
8660
8931
  "calibration-rules",
8661
- this.config.calibrationEnabled === true
8932
+ resolveConsolidationCapabilities(this.config).calibration === true
8662
8933
  )) {
8663
8934
  recordRecallSectionMetric({
8664
8935
  section: "calibrationRules",
@@ -8804,7 +9075,7 @@ ${rows.join("\n")}
8804
9075
  query: retrievalQuery,
8805
9076
  maxResults,
8806
9077
  sessionKey,
8807
- anchorsEnabled: this.config.abstractionAnchorsEnabled,
9078
+ anchorsEnabled: resolveConsolidationCapabilities(this.config).abstractionAnchors,
8808
9079
  abortSignal: harmonicRetrievalAbort.signal
8809
9080
  })
8810
9081
  )
@@ -8833,9 +9104,9 @@ ${rows.join("\n")}
8833
9104
  );
8834
9105
  const verifiedRecallPromise = (async () => {
8835
9106
  const t0 = Date.now();
8836
- if (!this.config.verifiedRecallEnabled || !this.isRecallSectionEnabled(
9107
+ if (!resolveRecallAuxiliaryCapabilities(this.config).verifiedRecall || !this.isRecallSectionEnabled(
8837
9108
  "verified-episodes",
8838
- this.config.verifiedRecallEnabled === true
9109
+ resolveRecallAuxiliaryCapabilities(this.config).verifiedRecall === true
8839
9110
  )) {
8840
9111
  recordRecallSectionMetric({
8841
9112
  section: "verifiedRecall",
@@ -8913,9 +9184,9 @@ ${rows.join("\n")}
8913
9184
  })();
8914
9185
  const verifiedRulesPromise = (async () => {
8915
9186
  const t0 = Date.now();
8916
- if (!this.config.semanticRuleVerificationEnabled || !this.isRecallSectionEnabled(
9187
+ if (!resolveRecallAuxiliaryCapabilities(this.config).semanticRuleVerification || !this.isRecallSectionEnabled(
8917
9188
  "verified-rules",
8918
- this.config.semanticRuleVerificationEnabled === true
9189
+ resolveRecallAuxiliaryCapabilities(this.config).semanticRuleVerification === true
8919
9190
  )) {
8920
9191
  recordRecallSectionMetric({
8921
9192
  section: "verifiedRules",
@@ -8992,9 +9263,9 @@ ${rows.join("\n")}
8992
9263
  })();
8993
9264
  const workProductsPromise = (async () => {
8994
9265
  const t0 = Date.now();
8995
- if (!resolveCreationMemoryCapabilities(this.config).creationMemory || !this.config.workProductRecallEnabled || !this.isRecallSectionEnabled(
9266
+ if (!resolveCreationMemoryCapabilities(this.config).creationMemory || !resolveRecallAuxiliaryCapabilities(this.config).workProductRecall || !this.isRecallSectionEnabled(
8996
9267
  "work-products",
8997
- this.config.workProductRecallEnabled === true
9268
+ resolveRecallAuxiliaryCapabilities(this.config).workProductRecall === true
8998
9269
  )) {
8999
9270
  recordRecallSectionMetric({
9000
9271
  section: "workProducts",
@@ -9404,7 +9675,7 @@ ${rows.join("\n")}
9404
9675
  );
9405
9676
  const transcriptPromise = (async () => {
9406
9677
  const t0 = Date.now();
9407
- if (!this.config.transcriptEnabled || !this.isRecallSectionEnabled("transcript", true)) {
9678
+ if (!resolvePresentationCapabilities(this.config).transcript || !this.isRecallSectionEnabled("transcript", true)) {
9408
9679
  recordRecallSectionMetric({
9409
9680
  section: "transcript",
9410
9681
  priority: "core",
@@ -9486,14 +9757,14 @@ ${formatted}`;
9486
9757
  const effectiveSessionKey = sessionKey ?? "default";
9487
9758
  const compactionWorkspaceDir = this._recallWorkspaceOverrides.get(effectiveSessionKey);
9488
9759
  this._recallWorkspaceOverrides.delete(effectiveSessionKey);
9489
- if (!this.config.compactionResetEnabled) return null;
9760
+ if (!resolveRecallAuxiliaryCapabilities(this.config).compactionReset) return null;
9490
9761
  const workspaceDir = compactionWorkspaceDir || this.config.workspaceDir || defaultWorkspaceDir();
9491
9762
  const safeSessionKey = sanitizeSessionKeyForFilename(effectiveSessionKey);
9492
- const signalPath = path4.join(
9763
+ const signalPath = path5.join(
9493
9764
  workspaceDir,
9494
9765
  `.compaction-reset-signal-${safeSessionKey}`
9495
9766
  );
9496
- const bootPath = path4.join(workspaceDir, "BOOT.md");
9767
+ const bootPath = path5.join(workspaceDir, "BOOT.md");
9497
9768
  try {
9498
9769
  const signalStat = await stat(signalPath).catch(() => null);
9499
9770
  if (!signalStat) return null;
@@ -9770,8 +10041,8 @@ ${formatted}`;
9770
10041
  const recentBoxesPromise = observeEnrichmentPromise(
9771
10042
  this.isRecallSectionEnabled(
9772
10043
  "memory-boxes",
9773
- this.config.memoryBoxesEnabled === true
9774
- ) && this.config.memoryBoxesEnabled && this.config.boxRecallDays > 0 ? Promise.all(
10044
+ resolvePresentationCapabilities(this.config).memoryBoxes === true
10045
+ ) && resolvePresentationCapabilities(this.config).memoryBoxes && this.config.boxRecallDays > 0 ? Promise.all(
9775
10046
  profileStorages.map(
9776
10047
  (storage) => this.boxBuilderFor(storage).readRecentBoxes(this.config.boxRecallDays).catch(() => [])
9777
10048
  )
@@ -12056,7 +12327,7 @@ ${normalized}`).digest("hex");
12056
12327
  async maybeCapturePassiveCorrections(turns, opts) {
12057
12328
  const mode = this.config.correctionCaptureMode;
12058
12329
  if (mode === "off") return;
12059
- if (!this.config.correctionEnabled) return;
12330
+ if (!resolveRecallAuxiliaryCapabilities(this.config).correction) return;
12060
12331
  try {
12061
12332
  const corrections = detectPassiveCorrections(
12062
12333
  turns.map((t) => ({ role: t.role, content: t.content }))
@@ -12072,7 +12343,7 @@ ${normalized}`).digest("hex");
12072
12343
  const result = await capturePassiveCorrections(
12073
12344
  corrections,
12074
12345
  {
12075
- correctionEnabled: this.config.correctionEnabled,
12346
+ correctionEnabled: resolveRecallAuxiliaryCapabilities(this.config).correction,
12076
12347
  isLiveSession: opts.isLiveSession,
12077
12348
  bufferKey: opts.bufferKey,
12078
12349
  sessionKey: opts.sessionKey,
@@ -12386,7 +12657,7 @@ ${normalized}`).digest("hex");
12386
12657
  return { status: "skipped", reason: "empty_extraction_result", persistedCount: 0, durableOutputCount: 0 };
12387
12658
  }
12388
12659
  let threadIdForExtraction = null;
12389
- if (this.config.threadingEnabled && turns.length > 0) {
12660
+ if (resolvePresentationCapabilities(this.config).threading && turns.length > 0) {
12390
12661
  const lastTurn = turns[turns.length - 1];
12391
12662
  try {
12392
12663
  threadIdForExtraction = await this.threading.processTurn(lastTurn, []);
@@ -12467,7 +12738,7 @@ ${normalized}`).digest("hex");
12467
12738
  bufferKey,
12468
12739
  isLiveSession: clearBufferAfterExtraction
12469
12740
  });
12470
- if (this.config.memoryBoxesEnabled && persistedIds.length > 0) {
12741
+ if (resolvePresentationCapabilities(this.config).memoryBoxes && persistedIds.length > 0) {
12471
12742
  const extractionTopics = deriveTopicsFromExtraction(result);
12472
12743
  const firstUserTurn = turns.find((t) => t.role === "user");
12473
12744
  const boxGoal = firstUserTurn?.content?.slice(0, 100)?.trim() || void 0;
@@ -12480,13 +12751,13 @@ ${normalized}`).digest("hex");
12480
12751
  (err) => log.warn("[boxes] onExtraction failed (non-fatal)", err)
12481
12752
  );
12482
12753
  }
12483
- if (this.config.threadingEnabled && threadIdForExtraction && persistedIds.length > 0) {
12754
+ if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction && persistedIds.length > 0) {
12484
12755
  await this.appendPersistedThreadEpisodes(
12485
12756
  threadIdForExtraction,
12486
12757
  persistedIds
12487
12758
  );
12488
12759
  }
12489
- if (this.config.threadingEnabled && threadIdForExtraction) {
12760
+ if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction) {
12490
12761
  const conversationContent = turns.map((t) => t.content).join(" ");
12491
12762
  try {
12492
12763
  await this.threading.updateThreadTitle(
@@ -13520,7 +13791,7 @@ ${normalized}`).digest("hex");
13520
13791
  chunkResult = chunkContent(fact.content, chunkingConfig);
13521
13792
  }
13522
13793
  if (chunkResult.chunked && chunkResult.chunks.length > 1) {
13523
- const memoryKind2 = this.config.episodeNoteModeEnabled ? classifyMemoryKind(fact.content, fact.tags ?? [], writeCategory) : void 0;
13794
+ const memoryKind2 = resolvePresentationCapabilities(this.config).episodeNoteMode ? classifyMemoryKind(fact.content, fact.tags ?? [], writeCategory) : void 0;
13524
13795
  const rawChunkedContent = citationEnabled && hasCitationForTemplate(fact.content, citationTemplate) ? stripCitationForTemplate(fact.content, citationTemplate) : fact.content;
13525
13796
  const citedChunkedContent = applyInlineCitation(rawChunkedContent);
13526
13797
  const parentWrite = await targetStorage.writeMemory(
@@ -13686,7 +13957,7 @@ ${normalized}`).digest("hex");
13686
13957
  }
13687
13958
  }
13688
13959
  try {
13689
- if (this.config.verbatimArtifactsEnabled && this.config.verbatimArtifactCategories.includes(writeCategory) && fact.confidence >= this.config.verbatimArtifactsMinConfidence && !postWriteGuard2) {
13960
+ if (resolvePresentationCapabilities(this.config).verbatimArtifacts && this.config.verbatimArtifactCategories.includes(writeCategory) && fact.confidence >= this.config.verbatimArtifactsMinConfidence && !postWriteGuard2) {
13690
13961
  await targetStorage.writeArtifact(citedChunkedContent, {
13691
13962
  confidence: fact.confidence,
13692
13963
  tags: [...fact.tags, "artifact", "chunked-parent"],
@@ -13760,7 +14031,7 @@ ${normalized}`).digest("hex");
13760
14031
  links.push(...suggestedLinks);
13761
14032
  }
13762
14033
  }
13763
- const memoryKind = writeCategory === "procedure" ? void 0 : this.config.episodeNoteModeEnabled ? classifyMemoryKind(fact.content, fact.tags ?? [], writeCategory) : void 0;
14034
+ const memoryKind = writeCategory === "procedure" ? void 0 : resolvePresentationCapabilities(this.config).episodeNoteMode ? classifyMemoryKind(fact.content, fact.tags ?? [], writeCategory) : void 0;
13764
14035
  const rawPersistBody = writeCategory === "procedure" ? buildProcedurePersistBody(fact.content, fact.procedureSteps) : fact.content;
13765
14036
  const citedFactContent = applyInlineCitation(rawPersistBody);
13766
14037
  const factWrite = await targetStorage.writeMemory(
@@ -13902,7 +14173,7 @@ ${normalized}`).digest("hex");
13902
14173
  } catch {
13903
14174
  }
13904
14175
  }
13905
- if (this.config.verbatimArtifactsEnabled && this.config.verbatimArtifactCategories.includes(writeCategory) && fact.confidence >= this.config.verbatimArtifactsMinConfidence && !postWriteGuard) {
14176
+ if (resolvePresentationCapabilities(this.config).verbatimArtifacts && this.config.verbatimArtifactCategories.includes(writeCategory) && fact.confidence >= this.config.verbatimArtifactsMinConfidence && !postWriteGuard) {
13906
14177
  await targetStorage.writeArtifact(citedFactContent, {
13907
14178
  confidence: fact.confidence,
13908
14179
  tags: [...fact.tags, "artifact"],
@@ -14095,7 +14366,7 @@ ${normalized}`).digest("hex");
14095
14366
  const allMems = allMemsForGraph ?? [];
14096
14367
  for (const m of allMems) {
14097
14368
  if (m.frontmatter.entityRef === entityRef) {
14098
- const rel = path4.relative(storage.dir, m.path);
14369
+ const rel = path5.relative(storage.dir, m.path);
14099
14370
  if (rel !== memoryRelPath) entitySiblings.push(rel);
14100
14371
  }
14101
14372
  }
@@ -14302,7 +14573,7 @@ ${normalized}`).digest("hex");
14302
14573
  memoryItemMutated = true;
14303
14574
  log.info(`merged ${entitiesMerged} fragmented entity files`);
14304
14575
  }
14305
- if (this.config.entitySummaryEnabled) {
14576
+ if (resolvePresentationCapabilities(this.config).entitySummary) {
14306
14577
  try {
14307
14578
  const synthesized = await this.processEntitySynthesisQueue(
14308
14579
  this.config.defaultNamespace,
@@ -14417,9 +14688,9 @@ ${normalized}`).digest("hex");
14417
14688
  throw err;
14418
14689
  }
14419
14690
  }
14420
- if (this.config.semanticConsolidationEnabled) {
14691
+ if (resolveConsolidationCapabilities(this.config).semanticConsolidation) {
14421
14692
  try {
14422
- const stateFilePath = path4.join(
14693
+ const stateFilePath = path5.join(
14423
14694
  this.config.memoryDir,
14424
14695
  "state",
14425
14696
  "semantic-consolidation-last-run.json"
@@ -14467,7 +14738,7 @@ ${normalized}`).digest("hex");
14467
14738
  );
14468
14739
  }
14469
14740
  if (semResult.errors === 0 || semResult.memoriesArchived > 0) {
14470
- const stateDir = path4.join(this.config.memoryDir, "state");
14741
+ const stateDir = path5.join(this.config.memoryDir, "state");
14471
14742
  await mkdir3(stateDir, { recursive: true });
14472
14743
  await writeFile3(
14473
14744
  stateFilePath,
@@ -14788,7 +15059,7 @@ ${reflectionsContent.trim()}
14788
15059
  const seenStorageDirs = /* @__PURE__ */ new Set();
14789
15060
  const addStorage = (storage) => {
14790
15061
  const storageDir = storageDirFor(storage);
14791
- const storageKey = storageDir ? path4.resolve(storageDir) : `storage-without-dir-${storages.length}`;
15062
+ const storageKey = storageDir ? path5.resolve(storageDir) : `storage-without-dir-${storages.length}`;
14792
15063
  if (seenStorageDirs.has(storageKey)) return;
14793
15064
  seenStorageDirs.add(storageKey);
14794
15065
  storages.push(storage);
@@ -14818,7 +15089,7 @@ ${reflectionsContent.trim()}
14818
15089
  continue;
14819
15090
  }
14820
15091
  try {
14821
- const coldRoot = path4.join(storageDir, "cold");
15092
+ const coldRoot = path5.join(storageDir, "cold");
14822
15093
  for (const candidate of qmdResultPathCandidates(
14823
15094
  coldRoot,
14824
15095
  parts.relativePath
@@ -14859,7 +15130,7 @@ ${reflectionsContent.trim()}
14859
15130
  return null;
14860
15131
  }
14861
15132
  }
14862
- if (path4.isAbsolute(resultPath)) {
15133
+ if (path5.isAbsolute(resultPath)) {
14863
15134
  if (!fallbackStorageDir) {
14864
15135
  return await fallbackStorage.readMemoryByPath(resultPath);
14865
15136
  }
@@ -14898,7 +15169,7 @@ ${reflectionsContent.trim()}
14898
15169
  );
14899
15170
  if (!memory) return null;
14900
15171
  let ownerNamespace = null;
14901
- if (path4.isAbsolute(memory.path)) {
15172
+ if (path5.isAbsolute(memory.path)) {
14902
15173
  const ownerStorage = await this.storageForAbsoluteQmdResultPath(
14903
15174
  memory.path,
14904
15175
  fallbackStorage,
@@ -14922,16 +15193,16 @@ ${reflectionsContent.trim()}
14922
15193
  };
14923
15194
  }
14924
15195
  async storageForAbsoluteQmdResultPath(resultPath, fallbackStorage, recallNamespaces = []) {
14925
- const resolvedPath = path4.resolve(resultPath);
14926
- const memoryRoot = path4.resolve(this.config.memoryDir);
14927
- const namespacesRoot = path4.join(memoryRoot, "namespaces");
15196
+ const resolvedPath = path5.resolve(resultPath);
15197
+ const memoryRoot = path5.resolve(this.config.memoryDir);
15198
+ const namespacesRoot = path5.join(memoryRoot, "namespaces");
14928
15199
  const fallbackStorageDir = typeof fallbackStorage.dir === "string" && fallbackStorage.dir ? fallbackStorage.dir : null;
14929
15200
  const matches = [];
14930
15201
  const seenDirs = /* @__PURE__ */ new Set();
14931
15202
  const maybeAddStorage = (storage, namespace) => {
14932
15203
  const storageDir = typeof storage.dir === "string" && storage.dir ? storage.dir : null;
14933
15204
  if (!storageDir) return;
14934
- const candidateRoot = path4.resolve(storageDir);
15205
+ const candidateRoot = path5.resolve(storageDir);
14935
15206
  if (seenDirs.has(candidateRoot)) return;
14936
15207
  if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
14937
15208
  if (candidateRoot === memoryRoot && isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
@@ -14949,7 +15220,7 @@ ${reflectionsContent.trim()}
14949
15220
  candidateNamespaces.add(ns);
14950
15221
  }
14951
15222
  if (isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
14952
- const relativeToNamespaces = path4.relative(namespacesRoot, resolvedPath);
15223
+ const relativeToNamespaces = path5.relative(namespacesRoot, resolvedPath);
14953
15224
  const [namespaceSegment] = relativeToNamespaces.split(/[\\/]/);
14954
15225
  if (namespaceSegment) {
14955
15226
  candidateNamespaces.add(
@@ -14971,218 +15242,22 @@ ${reflectionsContent.trim()}
14971
15242
  matches.sort((a, b) => b.dir.length - a.dir.length);
14972
15243
  return matches[0] ?? null;
14973
15244
  }
15245
+ // Issue #1526: recall-rerank methods moved to RecallRerankCoordinator.
15246
+ // Thin delegation keeps the private API stable for callers + tests.
14974
15247
  async applyMemoryWorthRerank(results, namespaces) {
14975
- const counters = /* @__PURE__ */ new Map();
14976
- const seenNamespaces = /* @__PURE__ */ new Set();
14977
- const nowMs = Date.now();
14978
- for (const [key, entry] of this.memoryWorthCounterCache) {
14979
- if (nowMs - entry.at >= _Orchestrator.MEMORY_WORTH_CACHE_TTL_MS) {
14980
- this.memoryWorthCounterCache.delete(key);
14981
- }
14982
- }
14983
- for (const ns of namespaces) {
14984
- if (seenNamespaces.has(ns)) continue;
14985
- seenNamespaces.add(ns);
14986
- try {
14987
- const cached = this.memoryWorthCounterCache.get(ns);
14988
- let nsMap;
14989
- if (cached && nowMs - cached.at < _Orchestrator.MEMORY_WORTH_CACHE_TTL_MS) {
14990
- nsMap = cached.counters;
14991
- } else {
14992
- const storage = await this.getStorage(ns);
14993
- const memories = await storage.readAllMemories();
14994
- nsMap = buildMemoryWorthCounterMap(memories);
14995
- this.memoryWorthCounterCache.set(ns, { at: nowMs, counters: nsMap });
14996
- }
14997
- for (const [path5, c] of nsMap) counters.set(path5, c);
14998
- } catch (err) {
14999
- log.debug("memory-worth: failed to read namespace, skipping", {
15000
- namespace: ns,
15001
- error: err.message
15002
- });
15003
- }
15004
- }
15005
- const missing = results.filter((r) => !counters.has(r.path));
15006
- if (missing.length > 0) {
15007
- let reader = null;
15008
- for (const ns of namespaces) {
15009
- try {
15010
- reader = await this.getStorage(ns);
15011
- break;
15012
- } catch {
15013
- }
15014
- }
15015
- if (reader) {
15016
- for (const r of missing) {
15017
- try {
15018
- const memory = await this.readQmdResultMemory(r.path, reader, namespaces);
15019
- if (!memory) continue;
15020
- const fm = memory.frontmatter;
15021
- if (fm.mw_success === void 0 && fm.mw_fail === void 0) continue;
15022
- counters.set(r.path, {
15023
- mw_success: fm.mw_success,
15024
- mw_fail: fm.mw_fail,
15025
- lastAccessed: fm.lastAccessed
15026
- });
15027
- } catch (err) {
15028
- log.debug("memory-worth: direct path lookup failed", {
15029
- path: r.path,
15030
- error: err.message
15031
- });
15032
- }
15033
- }
15034
- }
15035
- }
15036
- if (counters.size === 0) return results;
15037
- const rankedInputs = results.map((r, i) => ({
15038
- path: r.path,
15039
- // Large positive rank score so multiplier math stays well-scaled and
15040
- // we never hit zero; descending so earlier items rank higher.
15041
- score: results.length - i
15042
- }));
15043
- const filtered = applyMemoryWorthFilter(rankedInputs, {
15044
- counters,
15045
- now: /* @__PURE__ */ new Date(),
15046
- halfLifeMs: this.config.recallMemoryWorthHalfLifeMs > 0 ? this.config.recallMemoryWorthHalfLifeMs : void 0
15047
- });
15048
- const byPath = new Map(results.map((r) => [r.path, r]));
15049
- const reordered = [];
15050
- for (const item of filtered) {
15051
- const original = byPath.get(item.path);
15052
- if (original) reordered.push(original);
15053
- }
15054
- return reordered;
15248
+ return this.recallRerankCoordinator.applyMemoryWorthRerank(results, namespaces);
15055
15249
  }
15056
- /**
15057
- * Issue #1577 — unified TrustScore recall stage. Thin wiring over the pure
15058
- * {@link applyTrustScoreStage} scorer + the {@link buildTrustSignalsForRerank}
15059
- * signal builder. The stage subsumes the Memory Worth multiplier — the
15060
- * orchestrator runs exactly one of the two (mutual exclusion, rule 39; the
15061
- * double-multiplier test in trust-score-stage.test.ts pins it structurally).
15062
- *
15063
- * Returns the admitted results AND the per-path trust map (including
15064
- * quarantined items) so the caller can: (a) render epistemic hedges, (b)
15065
- * surface quarantined items in X-ray with a reason (rule 34), and (c) filter
15066
- * quarantined paths from fallback recall branches. The trust map is a
15067
- * per-recall local — never instance state — so concurrent recalls cannot
15068
- * race on it (review: shared-trust-map concurrency).
15069
- */
15070
15250
  async applyTrustScoreRerank(results, namespaces) {
15071
- if (results.length === 0) return { results, trustByPath: null };
15072
- const now = /* @__PURE__ */ new Date();
15073
- const halfLifeDays = this.config.recallMemoryWorthHalfLifeMs > 0 ? this.config.recallMemoryWorthHalfLifeMs / (24 * 60 * 60 * 1e3) : void 0;
15074
- let fallbackReader = null;
15075
- const signals = await buildTrustSignalsForRerank(
15076
- results.map((r) => r.path),
15077
- namespaces,
15078
- {
15079
- readNamespaceMemories: async (ns) => (await this.getStorage(ns)).readAllMemories(),
15080
- readMemoryFrontmatter: async (path5) => {
15081
- if (!fallbackReader) {
15082
- for (const ns of namespaces) {
15083
- try {
15084
- fallbackReader = await this.getStorage(ns);
15085
- break;
15086
- } catch {
15087
- }
15088
- }
15089
- }
15090
- if (!fallbackReader) return null;
15091
- const memory = await this.readQmdResultMemory(path5, fallbackReader, namespaces);
15092
- return memory ? memory.frontmatter : null;
15093
- }
15094
- },
15095
- { cache: this.trustSignalCache, ttlMs: _Orchestrator.TRUST_SIGNAL_CACHE_TTL_MS },
15096
- now,
15097
- {
15098
- recencyHalfLifeDays: halfLifeDays,
15099
- logDebug: (message, context) => log.debug(message, context)
15100
- }
15101
- );
15102
- if (signals.size === 0) {
15103
- return { results, trustByPath: null };
15104
- }
15105
- const rankedInputs = results.map((r, i) => ({ path: r.path, score: results.length - i }));
15106
- const stage = applyTrustScoreStage(rankedInputs, {
15107
- signals,
15108
- weights: this.config.trustScoreWeights,
15109
- minMultiplier: this.config.trustScoreMinMultiplier,
15110
- maxMultiplier: this.config.trustScoreMaxMultiplier,
15111
- quarantine: this.config.trustScoreQuarantine
15112
- });
15113
- const trustByPath = new Map(stage.all.map((item) => [item.path, item]));
15114
- const byPath = new Map(results.map((r) => [r.path, r]));
15115
- const admitted = stage.admitted.map((item) => byPath.get(item.path)).filter((r) => r !== void 0);
15116
- return { results: admitted, trustByPath };
15251
+ return this.recallRerankCoordinator.applyTrustScoreRerank(results, namespaces);
15117
15252
  }
15118
- /**
15119
- * Issue #1577 — apply the TrustScore stage (or, when trust is off, the Memory
15120
- * Worth multiplier fallback) to ONE recall branch's results, returning the
15121
- * scored results + the per-path trust map. Thin wiring over
15122
- * {@link applyTrustScoreRerank} so every recall path — hot QMD, embedding
15123
- * fallback, recent scan — applies the SAME multiplier gate (rule 41: a
15124
- * feature gate must apply across ALL parallel recall paths). TrustScore
15125
- * subsumes Memory Worth; exactly one runs (rule 39). Fail-open on lookup
15126
- * errors so a storage hiccup never breaks a fallback path.
15127
- */
15128
15253
  async applyTrustScoreToBranch(results, namespaces, caps, label) {
15129
- if (caps.recallTrustScore && results.length > 0) {
15130
- try {
15131
- return await this.applyTrustScoreRerank(results, namespaces);
15132
- } catch (err) {
15133
- log.debug(`trust-score stage (${label}) failed open`, {
15134
- error: err.message
15135
- });
15136
- }
15137
- } else if (caps.recallMemoryWorthFilter && results.length > 0) {
15138
- try {
15139
- const filtered = await this.applyMemoryWorthRerank(results, namespaces);
15140
- return { results: filtered, trustByPath: null };
15141
- } catch (err) {
15142
- log.debug(`memory-worth filter (${label}) failed open`, {
15143
- error: err.message
15144
- });
15145
- }
15146
- }
15147
- return { results, trustByPath: null };
15254
+ return this.recallRerankCoordinator.applyTrustScoreToBranch(results, namespaces, caps, label);
15148
15255
  }
15149
15256
  diversifyAndLimitRecallResults(sectionId, results, limit, retrievalQuery, caps = resolveCapabilities(this.config)) {
15150
- const safeLimit = typeof limit === "number" && Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : 0;
15151
- if (!Array.isArray(results) || results.length === 0) return [];
15152
- if (safeLimit === 0) return [];
15153
- const boosted = caps.recallReasoningTraceBoost && typeof retrievalQuery === "string" ? applyReasoningTraceBoost(results, {
15154
- enabled: true,
15155
- query: retrievalQuery
15156
- }) : results;
15157
- const diversified = this.applyMmrToQmdResults(sectionId, boosted, caps);
15158
- return diversified.slice(0, safeLimit);
15257
+ return this.recallRerankCoordinator.diversifyAndLimitRecallResults(sectionId, results, limit, retrievalQuery, caps);
15159
15258
  }
15160
- /**
15161
- * Apply Maximal Marginal Relevance to a section's ordered candidate list.
15162
- *
15163
- * Operates per-section so one redundant cluster cannot dominate a section,
15164
- * and so one section's MMR pass cannot starve other sections. Returns the
15165
- * input unchanged when disabled, when there are fewer than 2 candidates, or
15166
- * when no budget information is available.
15167
- */
15168
15259
  applyMmrToQmdResults(sectionId, results, caps = resolveCapabilities(this.config)) {
15169
- if (!caps.recallMmr) return results;
15170
- if (!Array.isArray(results) || results.length < 2) return results;
15171
- const configuredTopN = this.config.recallMmrTopN;
15172
- const topN = typeof configuredTopN === "number" && Number.isFinite(configuredTopN) ? Math.max(0, Math.floor(configuredTopN)) : 40;
15173
- if (topN === 0) return results;
15174
- const lambda = this.config.recallMmrLambda ?? 0.7;
15175
- const { reordered, diversity } = reorderRecallResultsWithMmr(results, {
15176
- lambda,
15177
- topN
15178
- });
15179
- try {
15180
- log.info(
15181
- `recall_mmr: section=${sectionId} kept=${diversity.kept}/${diversity.considered} headReorderCount=${diversity.headReorderCount} avgSimBefore=${diversity.avgPairwiseSimBefore.toFixed(3)} avgSimAfter=${diversity.avgPairwiseSimAfter.toFixed(3)} lambda=${lambda.toFixed(2)}`
15182
- );
15183
- } catch {
15184
- }
15185
- return reordered;
15260
+ return this.recallRerankCoordinator.applyMmrToQmdResults(sectionId, results, caps);
15186
15261
  }
15187
15262
  buildLastRecallBudgetSummary(options) {
15188
15263
  return {
@@ -15258,12 +15333,12 @@ ${reflectionsContent.trim()}
15258
15333
  */
15259
15334
  semanticDedupScopeFor(targetStorage) {
15260
15335
  if (!resolveNamespaceCapabilities(this.config).namespaces) return {};
15261
- const memoryDir = path4.resolve(this.config.memoryDir);
15262
- const storageDir = path4.resolve(targetStorage.dir);
15336
+ const memoryDir = path5.resolve(this.config.memoryDir);
15337
+ const storageDir = path5.resolve(targetStorage.dir);
15263
15338
  if (storageDir === memoryDir) {
15264
15339
  return { pathExcludePrefixes: ["namespaces/"] };
15265
15340
  }
15266
- let rel = path4.relative(memoryDir, storageDir);
15341
+ let rel = path5.relative(memoryDir, storageDir);
15267
15342
  if (!rel || rel.startsWith("..")) {
15268
15343
  log.debug(
15269
15344
  `semantic dedup: target storage dir ${storageDir} is outside memoryDir ${memoryDir}; scoping lookup to absolute path prefix`
@@ -15282,7 +15357,7 @@ ${reflectionsContent.trim()}
15282
15357
  if (hits.length === 0) return [];
15283
15358
  const results = [];
15284
15359
  for (const hit of hits) {
15285
- const fullPath = path4.isAbsolute(hit.path) ? hit.path : path4.join(this.config.memoryDir, hit.path);
15360
+ const fullPath = path5.isAbsolute(hit.path) ? hit.path : path5.join(this.config.memoryDir, hit.path);
15286
15361
  const memory = await this.storage.readMemoryByPath(fullPath);
15287
15362
  if (!memory) continue;
15288
15363
  results.push({
@@ -15479,7 +15554,7 @@ ${reflectionsContent.trim()}
15479
15554
  const storage = await this.storageRouter.storageFor(namespace);
15480
15555
  const storageDir = typeof storage.dir === "string" && storage.dir ? storage.dir : null;
15481
15556
  if (!storageDir) continue;
15482
- const recallRoot = path4.resolve(storageDir);
15557
+ const recallRoot = path5.resolve(storageDir);
15483
15558
  if (seenRecallRoots.has(recallRoot)) continue;
15484
15559
  seenRecallRoots.add(recallRoot);
15485
15560
  recallRoots.push(recallRoot);
@@ -15503,8 +15578,8 @@ ${reflectionsContent.trim()}
15503
15578
  if (resolvedCold) scopedResults.push(resolvedCold.result);
15504
15579
  continue;
15505
15580
  }
15506
- if (path4.isAbsolute(result.path)) {
15507
- const resolvedPath = path4.resolve(result.path);
15581
+ if (path5.isAbsolute(result.path)) {
15582
+ const resolvedPath = path5.resolve(result.path);
15508
15583
  if (recallRoots.some(
15509
15584
  (recallRoot) => isPathInsideStorageRoot(recallRoot, resolvedPath)
15510
15585
  )) {
@@ -16438,4 +16513,4 @@ export {
16438
16513
  resolvePersistedMemoryRelativePath,
16439
16514
  Orchestrator
16440
16515
  };
16441
- //# sourceMappingURL=chunk-PB5YON2G.js.map
16516
+ //# sourceMappingURL=chunk-IEWUPZ75.js.map