@remnic/core 9.3.738 → 9.3.740
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.
- package/dist/access-boundary.d.ts +6 -6
- package/dist/access-boundary.js +13 -13
- package/dist/access-cli.js +35 -35
- package/dist/access-http.d.ts +6 -6
- package/dist/access-http.js +18 -18
- package/dist/access-mcp.d.ts +6 -6
- package/dist/access-mcp.js +17 -17
- package/dist/access-operations-batch.js +15 -15
- package/dist/access-operations.d.ts +6 -6
- package/dist/access-operations.js +16 -16
- package/dist/{access-service-DwQ6BIQw.d.ts → access-service-Cbk-Kv_A.d.ts} +1 -1
- package/dist/access-service.d.ts +6 -6
- package/dist/access-service.js +12 -12
- package/dist/access-surface-catalog.d.ts +6 -6
- package/dist/active-memory-bridge.js +3 -3
- package/dist/bootstrap.d.ts +5 -5
- package/dist/briefing.js +2 -2
- package/dist/buffer.js +2 -2
- package/dist/capabilities.d.ts +97 -1
- package/dist/capabilities.js +7 -1
- package/dist/causal-consolidation.js +4 -3
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/{chunk-IFCM6OLK.js → chunk-3TUDKDTQ.js} +7 -7
- package/dist/{chunk-A7INTIC5.js → chunk-4J53ZVPQ.js} +3 -3
- package/dist/{chunk-XYUFGHUW.js → chunk-56F6EAQ6.js} +2 -2
- package/dist/{chunk-F6OEXJQQ.js → chunk-6XG3NQRC.js} +2 -2
- package/dist/{chunk-24ZLC5QN.js → chunk-7N7S2RKF.js} +2 -2
- package/dist/{chunk-Y5OOHER4.js → chunk-7Y5KKMIP.js} +12 -11
- package/dist/chunk-7Y5KKMIP.js.map +1 -0
- package/dist/{chunk-VRBBZH4U.js → chunk-AX4TRSRE.js} +3 -3
- package/dist/{chunk-66RZ4EIT.js → chunk-BE6NEQLF.js} +3 -3
- package/dist/{chunk-YF2MMBKA.js → chunk-BJPL5WAL.js} +3 -3
- package/dist/{chunk-EO5QWINU.js → chunk-C3Z5YJSM.js} +5 -2
- package/dist/chunk-C3Z5YJSM.js.map +1 -0
- package/dist/{chunk-GEXI357K.js → chunk-C7C5WIIF.js} +2 -2
- package/dist/{chunk-L3TYGUTH.js → chunk-CMPXLMC3.js} +3 -3
- package/dist/{chunk-W2FBVBQ4.js → chunk-DBVBV2RF.js} +4 -4
- package/dist/{chunk-JQSP7YBJ.js → chunk-ELL5VYXC.js} +5 -4
- package/dist/chunk-ELL5VYXC.js.map +1 -0
- package/dist/{chunk-IQOEW5YV.js → chunk-FCQ7ZYCQ.js} +2 -2
- package/dist/{chunk-IIBFCZA7.js → chunk-FWXLKH2H.js} +3 -3
- package/dist/{chunk-RJBW4EOU.js → chunk-G7Q4D2G3.js} +74 -2
- package/dist/{chunk-RJBW4EOU.js.map → chunk-G7Q4D2G3.js.map} +1 -1
- package/dist/{chunk-WVAEIKUY.js → chunk-GPX5E36D.js} +5 -4
- package/dist/chunk-GPX5E36D.js.map +1 -0
- package/dist/{chunk-23PLOBWT.js → chunk-H3WSZWHK.js} +22 -20
- package/dist/chunk-H3WSZWHK.js.map +1 -0
- package/dist/{chunk-Q6BAA57B.js → chunk-JFCS6PQU.js} +2 -2
- package/dist/chunk-JFCS6PQU.js.map +1 -0
- package/dist/{chunk-CKHPUOAP.js → chunk-K2N6WIZM.js} +3 -3
- package/dist/{chunk-BHJAMSVC.js → chunk-LD6A7XG5.js} +7 -7
- package/dist/{chunk-AKKFHN3I.js → chunk-M4WI23X3.js} +2 -2
- package/dist/{chunk-N2O23WYA.js → chunk-M5KLYHHI.js} +2 -2
- package/dist/{chunk-TNLXCSXV.js → chunk-O4IWVWSW.js} +5 -4
- package/dist/chunk-O4IWVWSW.js.map +1 -0
- package/dist/{chunk-OQQRWPFZ.js → chunk-OS5RKJOH.js} +2 -2
- package/dist/{chunk-MAU7DFCJ.js → chunk-OT5K26NG.js} +5 -4
- package/dist/chunk-OT5K26NG.js.map +1 -0
- package/dist/{chunk-DS3FZ7GS.js → chunk-OUSOMUFV.js} +2 -2
- package/dist/{chunk-7B7IPPN5.js → chunk-OVYBENIJ.js} +3 -3
- package/dist/{chunk-5RZOFIDL.js → chunk-PI4JHLDD.js} +6 -3
- package/dist/chunk-PI4JHLDD.js.map +1 -0
- package/dist/{chunk-EU543UDC.js → chunk-QCO2QFLZ.js} +7 -7
- package/dist/chunk-QCO2QFLZ.js.map +1 -0
- package/dist/{chunk-YXOXAQ5Y.js → chunk-TQQN64J2.js} +5 -4
- package/dist/chunk-TQQN64J2.js.map +1 -0
- package/dist/{chunk-WDJCZQB4.js → chunk-UGFPGWJB.js} +2 -2
- package/dist/{chunk-75PDC7VT.js → chunk-UOVDO6TC.js} +6 -5
- package/dist/chunk-UOVDO6TC.js.map +1 -0
- package/dist/{chunk-BVG2IZOB.js → chunk-USHVHI6J.js} +7 -6
- package/dist/chunk-USHVHI6J.js.map +1 -0
- package/dist/{chunk-56LP72AD.js → chunk-UYYSOMS2.js} +4 -4
- package/dist/{chunk-EKQ3LWBM.js → chunk-VC7QELR6.js} +659 -584
- package/dist/chunk-VC7QELR6.js.map +1 -0
- package/dist/{chunk-XIQAOGAA.js → chunk-WGDE2TJP.js} +7 -6
- package/dist/chunk-WGDE2TJP.js.map +1 -0
- package/dist/{chunk-UFS7OXGL.js → chunk-WHEF76BQ.js} +5 -2
- package/dist/chunk-WHEF76BQ.js.map +1 -0
- package/dist/{chunk-QTJOORGN.js → chunk-WIMKOTNY.js} +2 -2
- package/dist/{chunk-GXTYEG7Q.js → chunk-WO22UEM3.js} +2 -2
- package/dist/{chunk-BHQQVUEQ.js → chunk-X5PMDVY4.js} +6 -5
- package/dist/{chunk-BHQQVUEQ.js.map → chunk-X5PMDVY4.js.map} +1 -1
- package/dist/{chunk-C53JDEH3.js → chunk-Y62QPRKV.js} +2 -2
- package/dist/{chunk-WFNH4UDF.js → chunk-Y6PNYJVO.js} +3 -3
- package/dist/{chunk-6YKIYQU7.js → chunk-YNHRWFAP.js} +5 -2
- package/dist/{chunk-6YKIYQU7.js.map → chunk-YNHRWFAP.js.map} +1 -1
- package/dist/{cli-BlEIWDQw.d.ts → cli-BQW2xKdb.d.ts} +2 -2
- package/dist/cli.d.ts +7 -7
- package/dist/cli.js +29 -29
- package/dist/compounding/engine.js +4 -4
- package/dist/connectors/codex-materialize-runner.js +2 -2
- package/dist/connectors/index.js +4 -3
- package/dist/contradiction/index.js +2 -2
- package/dist/{contradiction-scan-CWO3LIWV.js → contradiction-scan-UERKJESC.js} +3 -3
- package/dist/day-summary.js +2 -1
- package/dist/embedding-fallback.js +2 -2
- package/dist/entity-retrieval.js +3 -3
- package/dist/explicit-capture.d.ts +5 -5
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction.js +4 -4
- package/dist/{first-start-migration-5DXD3TF2.js → first-start-migration-GQ3UQNIL.js} +2 -2
- package/dist/index.d.ts +8 -8
- package/dist/index.js +45 -45
- package/dist/local-llm.js +2 -2
- package/dist/maintenance/memory-governance.js +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +6 -6
- package/dist/namespaces/migrate.js +7 -7
- package/dist/namespaces/principal.js +2 -2
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.js +3 -3
- package/dist/operator-toolkit.js +13 -13
- package/dist/orchestration/compression-guideline-coordinator.js +2 -2
- package/dist/orchestration/maintenance.js +7 -7
- package/dist/{orchestrator-CJZ21ZiB.d.ts → orchestrator-BOAFN_Yr.d.ts} +186 -56
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +29 -29
- package/dist/search/embed-helper.js +2 -2
- package/dist/search/factory.js +3 -3
- package/dist/search/index.js +3 -3
- package/dist/semantic-consolidation.js +4 -3
- package/dist/semantic-rule-promotion.js +2 -2
- package/dist/semantic-rule-verifier.js +2 -2
- package/dist/shared-context/manager.js +2 -1
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/{tier-stats-QID73BBB.js → tier-stats-CSKL7BDG.js} +2 -2
- package/dist/verified-recall.js +2 -2
- package/package.json +2 -2
- package/src/access-service.ts +2 -1
- package/src/capabilities.test.ts +166 -0
- package/src/capabilities.ts +267 -0
- package/src/cli/research-status-commands.ts +2 -1
- package/src/cli.ts +3 -2
- package/src/compounding/engine.ts +2 -1
- package/src/connectors/codex-marketplace.ts +2 -1
- package/src/day-summary.ts +2 -1
- package/src/embedding-fallback.ts +2 -1
- package/src/extraction.ts +3 -2
- package/src/local-llm.ts +2 -1
- package/src/maintenance/namespace-maintenance-fanout.ts +2 -1
- package/src/maintenance/namespace-planner.ts +2 -1
- package/src/operator-toolkit.ts +1 -0
- package/src/orchestration/conversation-index-coordinator.ts +344 -0
- package/src/orchestration/recall-rerank-coordinator.ts +425 -0
- package/src/orchestration/semantic-consolidation-coordinator.ts +3 -2
- package/src/orchestrator.ts +89 -598
- package/src/search/embed-helper.ts +2 -1
- package/src/search/factory.ts +2 -1
- package/src/semantic-consolidation.ts +2 -1
- package/src/shared-context/manager.ts +3 -2
- package/src/storage.ts +1 -1
- package/src/summarizer.ts +3 -2
- package/dist/chunk-23PLOBWT.js.map +0 -1
- package/dist/chunk-5RZOFIDL.js.map +0 -1
- package/dist/chunk-75PDC7VT.js.map +0 -1
- package/dist/chunk-BVG2IZOB.js.map +0 -1
- package/dist/chunk-EKQ3LWBM.js.map +0 -1
- package/dist/chunk-EO5QWINU.js.map +0 -1
- package/dist/chunk-EU543UDC.js.map +0 -1
- package/dist/chunk-JQSP7YBJ.js.map +0 -1
- package/dist/chunk-MAU7DFCJ.js.map +0 -1
- package/dist/chunk-Q6BAA57B.js.map +0 -1
- package/dist/chunk-TNLXCSXV.js.map +0 -1
- package/dist/chunk-UFS7OXGL.js.map +0 -1
- package/dist/chunk-WVAEIKUY.js.map +0 -1
- package/dist/chunk-XIQAOGAA.js.map +0 -1
- package/dist/chunk-Y5OOHER4.js.map +0 -1
- package/dist/chunk-YXOXAQ5Y.js.map +0 -1
- /package/dist/{chunk-IFCM6OLK.js.map → chunk-3TUDKDTQ.js.map} +0 -0
- /package/dist/{chunk-A7INTIC5.js.map → chunk-4J53ZVPQ.js.map} +0 -0
- /package/dist/{chunk-XYUFGHUW.js.map → chunk-56F6EAQ6.js.map} +0 -0
- /package/dist/{chunk-F6OEXJQQ.js.map → chunk-6XG3NQRC.js.map} +0 -0
- /package/dist/{chunk-24ZLC5QN.js.map → chunk-7N7S2RKF.js.map} +0 -0
- /package/dist/{chunk-VRBBZH4U.js.map → chunk-AX4TRSRE.js.map} +0 -0
- /package/dist/{chunk-66RZ4EIT.js.map → chunk-BE6NEQLF.js.map} +0 -0
- /package/dist/{chunk-YF2MMBKA.js.map → chunk-BJPL5WAL.js.map} +0 -0
- /package/dist/{chunk-GEXI357K.js.map → chunk-C7C5WIIF.js.map} +0 -0
- /package/dist/{chunk-L3TYGUTH.js.map → chunk-CMPXLMC3.js.map} +0 -0
- /package/dist/{chunk-W2FBVBQ4.js.map → chunk-DBVBV2RF.js.map} +0 -0
- /package/dist/{chunk-IQOEW5YV.js.map → chunk-FCQ7ZYCQ.js.map} +0 -0
- /package/dist/{chunk-IIBFCZA7.js.map → chunk-FWXLKH2H.js.map} +0 -0
- /package/dist/{chunk-CKHPUOAP.js.map → chunk-K2N6WIZM.js.map} +0 -0
- /package/dist/{chunk-BHJAMSVC.js.map → chunk-LD6A7XG5.js.map} +0 -0
- /package/dist/{chunk-AKKFHN3I.js.map → chunk-M4WI23X3.js.map} +0 -0
- /package/dist/{chunk-N2O23WYA.js.map → chunk-M5KLYHHI.js.map} +0 -0
- /package/dist/{chunk-OQQRWPFZ.js.map → chunk-OS5RKJOH.js.map} +0 -0
- /package/dist/{chunk-DS3FZ7GS.js.map → chunk-OUSOMUFV.js.map} +0 -0
- /package/dist/{chunk-7B7IPPN5.js.map → chunk-OVYBENIJ.js.map} +0 -0
- /package/dist/{chunk-WDJCZQB4.js.map → chunk-UGFPGWJB.js.map} +0 -0
- /package/dist/{chunk-56LP72AD.js.map → chunk-UYYSOMS2.js.map} +0 -0
- /package/dist/{chunk-QTJOORGN.js.map → chunk-WIMKOTNY.js.map} +0 -0
- /package/dist/{chunk-GXTYEG7Q.js.map → chunk-WO22UEM3.js.map} +0 -0
- /package/dist/{chunk-C53JDEH3.js.map → chunk-Y62QPRKV.js.map} +0 -0
- /package/dist/{chunk-WFNH4UDF.js.map → chunk-Y6PNYJVO.js.map} +0 -0
- /package/dist/{contradiction-scan-CWO3LIWV.js.map → contradiction-scan-UERKJESC.js.map} +0 -0
- /package/dist/{first-start-migration-5DXD3TF2.js.map → first-start-migration-GQ3UQNIL.js.map} +0 -0
- /package/dist/{tier-stats-QID73BBB.js.map → tier-stats-CSKL7BDG.js.map} +0 -0
package/src/orchestrator.ts
CHANGED
|
@@ -102,6 +102,8 @@ import { SemanticConsolidationCoordinator } from "./orchestration/semantic-conso
|
|
|
102
102
|
import { LifecyclePolicyCoordinator } from "./orchestration/lifecycle-policy-coordinator.js";
|
|
103
103
|
import { EntitySynthesisCoordinator } from "./orchestration/entity-synthesis-coordinator.js";
|
|
104
104
|
import { RecallResultFormatter } from "./orchestration/recall-result-formatter.js";
|
|
105
|
+
import { ConversationIndexCoordinator } from "./orchestration/conversation-index-coordinator.js";
|
|
106
|
+
import { RecallRerankCoordinator } from "./orchestration/recall-rerank-coordinator.js";
|
|
105
107
|
export { hasIdentityRecoveryIntent, resolveEffectiveIdentityInjectionMode } from "./orchestration/recall-result-formatter.js";
|
|
106
108
|
import {
|
|
107
109
|
runLiveConnectorsOnce,
|
|
@@ -121,16 +123,8 @@ import {
|
|
|
121
123
|
type ParallelSearchResult,
|
|
122
124
|
} from "./retrieval-agents.js";
|
|
123
125
|
import { RerankCache, rerankLocalOrNoop } from "./rerank.js";
|
|
124
|
-
import {
|
|
125
|
-
applyMemoryWorthFilter,
|
|
126
|
-
buildMemoryWorthCounterMap,
|
|
127
|
-
type MemoryWorthCounters,
|
|
128
|
-
} from "./memory-worth-filter.js";
|
|
129
|
-
import { applyTrustScoreStage, buildTrustSignalsForRerank, projectTrustForXray } from "./trust-score-stage.js";
|
|
126
|
+
import { projectTrustForXray } from "./trust-score-stage.js";
|
|
130
127
|
import type { TrustStageResultItem } from "./trust-score-stage.js";
|
|
131
|
-
import { renderEpistemicHedge, type TrustSignals } from "./trust-score.js";
|
|
132
|
-
import { reorderRecallResultsWithMmr } from "./recall-mmr.js";
|
|
133
|
-
import { applyReasoningTraceBoost } from "./reasoning-trace-recall.js";
|
|
134
128
|
import { buildRetrievedMemoryProvenance } from "./memory-provenance.js";
|
|
135
129
|
import {
|
|
136
130
|
applyTemporalSupersession,
|
|
@@ -281,7 +275,11 @@ import { resolveNamespaceCapabilities,
|
|
|
281
275
|
resolveQmdCapabilities,
|
|
282
276
|
resolveIdentityContinuityCapabilities,
|
|
283
277
|
resolveLocalLlmCapabilities,
|
|
284
|
-
resolveSecurityCapabilities, resolveEvalCapabilities, resolveUtilityLearningCapabilities, resolveObjectiveStateCapabilities, resolveCompressionCapabilities, resolvePresentationCapabilities, resolveConsolidationCapabilities, resolveRecallAuxiliaryCapabilities
|
|
278
|
+
resolveSecurityCapabilities, resolveEvalCapabilities, resolveUtilityLearningCapabilities, resolveObjectiveStateCapabilities, resolveCompressionCapabilities, resolvePresentationCapabilities, resolveConsolidationCapabilities, resolveRecallAuxiliaryCapabilities ,
|
|
279
|
+
resolveRecallEnhancementCapabilities,
|
|
280
|
+
resolvePipelineProcessingCapabilities,
|
|
281
|
+
resolveConversationContextCapabilities,
|
|
282
|
+
} from "./capabilities.js";
|
|
285
283
|
import { DEFAULT_TAXONOMY } from "./taxonomy/index.js";
|
|
286
284
|
import {
|
|
287
285
|
searchHarmonicRetrieval,
|
|
@@ -330,9 +328,6 @@ import {
|
|
|
330
328
|
type SemanticConsolidationLlmOperator,
|
|
331
329
|
type SemanticConsolidationResult,
|
|
332
330
|
} from "./semantic-consolidation.js";
|
|
333
|
-
import { chunkTranscriptEntries } from "./conversation-index/chunker.js";
|
|
334
|
-
import { writeConversationChunks } from "./conversation-index/indexer.js";
|
|
335
|
-
import { cleanupConversationChunks } from "./conversation-index/cleanup.js";
|
|
336
331
|
import {
|
|
337
332
|
type ConversationIndexBackend,
|
|
338
333
|
type ConversationIndexBackendInspection,
|
|
@@ -1817,31 +1812,6 @@ export class Orchestrator {
|
|
|
1817
1812
|
readonly lcmEngine: LcmEngine | null = null;
|
|
1818
1813
|
private readonly rerankCache = new RerankCache();
|
|
1819
1814
|
|
|
1820
|
-
/**
|
|
1821
|
-
* Short-TTL cache for Memory Worth counter lookups so interactive recall
|
|
1822
|
-
* doesn't trigger a full `readAllMemories` scan per query. Keyed by
|
|
1823
|
-
* namespace; the filter unions across namespaces at query time. The TTL
|
|
1824
|
-
* is intentionally short (seconds, not minutes) because counters are
|
|
1825
|
-
* mutated by `recordMemoryOutcome` asynchronously and we'd rather serve
|
|
1826
|
-
* a 30-second-stale worth score than a stable-but-wrong one.
|
|
1827
|
-
*/
|
|
1828
|
-
private readonly memoryWorthCounterCache = new Map<
|
|
1829
|
-
string,
|
|
1830
|
-
{ at: number; counters: ReadonlyMap<string, MemoryWorthCounters> }
|
|
1831
|
-
>();
|
|
1832
|
-
private static readonly MEMORY_WORTH_CACHE_TTL_MS = 30_000;
|
|
1833
|
-
/**
|
|
1834
|
-
* Issue #1577 — per-namespace TrustScore signal map cache. Same TTL/shape
|
|
1835
|
-
* discipline as {@link memoryWorthCounterCache}: seconds-scale, evicted on
|
|
1836
|
-
* every call, keyed by namespace. Holds the frontmatter-derived signals
|
|
1837
|
-
* (worth, provenance, faithfulness, corroboration, recency) so the trust
|
|
1838
|
-
* stage doesn't trigger a fresh `readAllMemories` scan per query.
|
|
1839
|
-
*/
|
|
1840
|
-
private readonly trustSignalCache = new Map<
|
|
1841
|
-
string,
|
|
1842
|
-
{ at: number; signals: ReadonlyMap<string, TrustSignals> }
|
|
1843
|
-
>();
|
|
1844
|
-
private static readonly TRUST_SIGNAL_CACHE_TTL_MS = 30_000;
|
|
1845
1815
|
/**
|
|
1846
1816
|
* Per-session workspace selections keyed by sessionKey.
|
|
1847
1817
|
* Set by the before_agent_start hook so recall() uses the correct
|
|
@@ -1909,6 +1879,12 @@ export class Orchestrator {
|
|
|
1909
1879
|
* to RecallResultFormatter.
|
|
1910
1880
|
*/
|
|
1911
1881
|
readonly recallResultFormatter: RecallResultFormatter;
|
|
1882
|
+
/**
|
|
1883
|
+
* Issue #1526: conversation-index subsystem moved to
|
|
1884
|
+
* ConversationIndexCoordinator.
|
|
1885
|
+
*/
|
|
1886
|
+
readonly conversationIndexCoordinator: ConversationIndexCoordinator;
|
|
1887
|
+
readonly recallRerankCoordinator: RecallRerankCoordinator;
|
|
1912
1888
|
private heartbeatObserverChains = new Map<string, Promise<void>>();
|
|
1913
1889
|
private recentExtractionFingerprints = new Map<string, number>();
|
|
1914
1890
|
private readonly consolidationObservers = new Set<
|
|
@@ -1917,7 +1893,6 @@ export class Orchestrator {
|
|
|
1917
1893
|
private wearablesServiceInstance: WearablesService | null = null;
|
|
1918
1894
|
private wearablesAutoSyncHandle: { stop(): Promise<void> } | null = null;
|
|
1919
1895
|
private lastQmdReprobeAtMs = 0;
|
|
1920
|
-
private readonly conversationIndexLastUpdateAtMs = new Map<string, number>();
|
|
1921
1896
|
private lastFileHygieneRunAtMs = 0;
|
|
1922
1897
|
// Pattern-reinforcement cadence gate (issue #687 PR 2/4). Tracks the
|
|
1923
1898
|
// last successful run so `runPatternReinforcement` can short-circuit
|
|
@@ -2796,7 +2771,7 @@ export class Orchestrator {
|
|
|
2796
2771
|
constructor(config: PluginConfig) {
|
|
2797
2772
|
this.config = config;
|
|
2798
2773
|
this.profiler = new ProfilingCollector({
|
|
2799
|
-
enabled: config.
|
|
2774
|
+
enabled: resolvePipelineProcessingCapabilities(this.config).profiling,
|
|
2800
2775
|
storageDir: config.profilingStorageDir || path.join(config.memoryDir, "profiling"),
|
|
2801
2776
|
maxTraces: config.profilingMaxTraces,
|
|
2802
2777
|
});
|
|
@@ -2885,7 +2860,7 @@ export class Orchestrator {
|
|
|
2885
2860
|
this.conversationQmd = conversationIndexRuntime.qmd;
|
|
2886
2861
|
this.conversationFaiss = conversationIndexRuntime.faiss;
|
|
2887
2862
|
this.conversationIndexBackend = conversationIndexRuntime.backend;
|
|
2888
|
-
this.sharedContext = config.
|
|
2863
|
+
this.sharedContext = resolveConversationContextCapabilities(this.config).sharedContext
|
|
2889
2864
|
? new SharedContextManager(config)
|
|
2890
2865
|
: undefined;
|
|
2891
2866
|
this.compounding = resolveConsolidationCapabilities(config).compounding
|
|
@@ -2898,6 +2873,18 @@ export class Orchestrator {
|
|
|
2898
2873
|
"conversation-index",
|
|
2899
2874
|
"chunks",
|
|
2900
2875
|
);
|
|
2876
|
+
this.conversationIndexCoordinator = new ConversationIndexCoordinator({
|
|
2877
|
+
config,
|
|
2878
|
+
getTranscript: () => this.transcript,
|
|
2879
|
+
getBackend: () => this.conversationIndexBackend,
|
|
2880
|
+
indexDir: this.conversationIndexDir,
|
|
2881
|
+
});
|
|
2882
|
+
this.recallRerankCoordinator = new RecallRerankCoordinator({
|
|
2883
|
+
getConfig: () => this.config,
|
|
2884
|
+
getStorage: (namespace) => this.getStorage(namespace),
|
|
2885
|
+
readQmdResultMemory: (resultPath, fallbackStorage, recallNamespaces) =>
|
|
2886
|
+
this.readQmdResultMemory(resultPath, fallbackStorage, recallNamespaces),
|
|
2887
|
+
});
|
|
2901
2888
|
this.modelRegistry = new ModelRegistry(config.memoryDir);
|
|
2902
2889
|
this.relevance = new RelevanceStore(config.memoryDir);
|
|
2903
2890
|
this.negatives = new NegativeExampleStore(config.memoryDir);
|
|
@@ -2951,7 +2938,7 @@ export class Orchestrator {
|
|
|
2951
2938
|
// timeout. Operators can set `localLlmDisableThinking: false`
|
|
2952
2939
|
// when they want thinking enabled for narrative paths.
|
|
2953
2940
|
this.localLlm.disableThinking = config.localLlmDisableThinking;
|
|
2954
|
-
this.fastLlm = config.
|
|
2941
|
+
this.fastLlm = resolvePipelineProcessingCapabilities(this.config).localLlmFast
|
|
2955
2942
|
? (() => {
|
|
2956
2943
|
const client = new LocalLlmClient(
|
|
2957
2944
|
{
|
|
@@ -3024,7 +3011,7 @@ export class Orchestrator {
|
|
|
3024
3011
|
});
|
|
3025
3012
|
|
|
3026
3013
|
// Lossless Context Management (LCM) — proactive session archive + DAG summarization
|
|
3027
|
-
if (config.
|
|
3014
|
+
if (resolvePipelineProcessingCapabilities(this.config).lcm) {
|
|
3028
3015
|
const summarizeFn = async (
|
|
3029
3016
|
text: string,
|
|
3030
3017
|
targetTokens: number,
|
|
@@ -3081,7 +3068,7 @@ export class Orchestrator {
|
|
|
3081
3068
|
dir,
|
|
3082
3069
|
new BoxBuilder(dir, {
|
|
3083
3070
|
memoryBoxesEnabled: resolvePresentationCapabilities(this.config).memoryBoxes,
|
|
3084
|
-
traceWeaverEnabled: this.config.
|
|
3071
|
+
traceWeaverEnabled: resolvePipelineProcessingCapabilities(this.config).traceWeaver,
|
|
3085
3072
|
boxTopicShiftThreshold: this.config.boxTopicShiftThreshold,
|
|
3086
3073
|
boxTimeGapMs: this.config.boxTimeGapMs,
|
|
3087
3074
|
boxMaxMemories: this.config.boxMaxMemories,
|
|
@@ -3174,7 +3161,7 @@ export class Orchestrator {
|
|
|
3174
3161
|
}
|
|
3175
3162
|
|
|
3176
3163
|
private async loadRoutingRules(): Promise<RouteRule[]> {
|
|
3177
|
-
if (!this.config.
|
|
3164
|
+
if (!resolvePipelineProcessingCapabilities(this.config).routingRules) return [];
|
|
3178
3165
|
try {
|
|
3179
3166
|
return await this.getRoutingRulesStore().read(this.routeEngineOptions());
|
|
3180
3167
|
} catch (err) {
|
|
@@ -5006,63 +4993,21 @@ export class Orchestrator {
|
|
|
5006
4993
|
retrievalQuery: string,
|
|
5007
4994
|
topK: number,
|
|
5008
4995
|
): Promise<Array<{ path: string; snippet: string; score: number }>> {
|
|
5009
|
-
|
|
5010
|
-
return this.conversationIndexBackend.search(retrievalQuery, topK);
|
|
5011
|
-
}
|
|
5012
|
-
return [];
|
|
4996
|
+
return this.conversationIndexCoordinator.search(retrievalQuery, topK);
|
|
5013
4997
|
}
|
|
5014
4998
|
|
|
5015
4999
|
private formatConversationRecallSection(
|
|
5016
5000
|
results: Array<{ path: string; snippet: string; score: number }>,
|
|
5017
5001
|
maxChars: number,
|
|
5018
5002
|
): string | null {
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
if (!r?.snippet) continue;
|
|
5024
|
-
const chunk =
|
|
5025
|
-
`### ${r.path}\n` +
|
|
5026
|
-
`Score: ${r.score.toFixed(3)}\n\n` +
|
|
5027
|
-
`${r.snippet.trim()}\n`;
|
|
5028
|
-
if (used + chunk.length > maxChars) break;
|
|
5029
|
-
lines.push(chunk);
|
|
5030
|
-
used += chunk.length;
|
|
5031
|
-
}
|
|
5032
|
-
return used > 0 ? lines.join("\n") : null;
|
|
5033
|
-
}
|
|
5034
|
-
|
|
5035
|
-
private async countConversationChunkDocs(dir: string): Promise<number> {
|
|
5036
|
-
try {
|
|
5037
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
5038
|
-
let total = 0;
|
|
5039
|
-
for (const entry of entries) {
|
|
5040
|
-
const fullPath = path.join(dir, entry.name);
|
|
5041
|
-
if (entry.isDirectory()) {
|
|
5042
|
-
total += await this.countConversationChunkDocs(fullPath);
|
|
5043
|
-
continue;
|
|
5044
|
-
}
|
|
5045
|
-
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
5046
|
-
total += 1;
|
|
5047
|
-
}
|
|
5048
|
-
}
|
|
5049
|
-
return total;
|
|
5050
|
-
} catch {
|
|
5051
|
-
return 0;
|
|
5052
|
-
}
|
|
5003
|
+
return this.conversationIndexCoordinator.formatRecallSection(
|
|
5004
|
+
results,
|
|
5005
|
+
maxChars,
|
|
5006
|
+
);
|
|
5053
5007
|
}
|
|
5054
5008
|
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
hours: number = 24,
|
|
5058
|
-
): Promise<ReturnType<typeof chunkTranscriptEntries>> {
|
|
5059
|
-
const entries = await this.transcript.readRecent(hours, sessionKey);
|
|
5060
|
-
const effectiveSessionKey = sessionKey ?? "all-sessions";
|
|
5061
|
-
return chunkTranscriptEntries(effectiveSessionKey, entries, {
|
|
5062
|
-
maxChars: this.config.conversationRecallMaxChars * 2,
|
|
5063
|
-
maxTurns: Math.max(10, this.config.hourlySummariesMaxTurnsPerRun),
|
|
5064
|
-
});
|
|
5065
|
-
}
|
|
5009
|
+
// Issue #1526: countConversationChunkDocs / buildConversationIndexChunks moved
|
|
5010
|
+
// to ConversationIndexCoordinator (internal helpers, no orchestrator callers).
|
|
5066
5011
|
|
|
5067
5012
|
async getConversationIndexHealth(): Promise<{
|
|
5068
5013
|
enabled: boolean;
|
|
@@ -5087,37 +5032,7 @@ export class Orchestrator {
|
|
|
5087
5032
|
};
|
|
5088
5033
|
};
|
|
5089
5034
|
}> {
|
|
5090
|
-
|
|
5091
|
-
this.conversationIndexDir,
|
|
5092
|
-
);
|
|
5093
|
-
const lastUpdateAtMs = Math.max(
|
|
5094
|
-
0,
|
|
5095
|
-
...this.conversationIndexLastUpdateAtMs.values(),
|
|
5096
|
-
);
|
|
5097
|
-
const lastUpdateAt =
|
|
5098
|
-
lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
|
|
5099
|
-
|
|
5100
|
-
if (!resolveIndexingCapabilities(this.config).conversationIndex) {
|
|
5101
|
-
return {
|
|
5102
|
-
enabled: false,
|
|
5103
|
-
backend: this.config.conversationIndexBackend,
|
|
5104
|
-
status: "disabled",
|
|
5105
|
-
chunkDocCount,
|
|
5106
|
-
lastUpdateAt,
|
|
5107
|
-
};
|
|
5108
|
-
}
|
|
5109
|
-
const backendHealth = this.conversationIndexBackend
|
|
5110
|
-
? await this.conversationIndexBackend.health()
|
|
5111
|
-
: {
|
|
5112
|
-
backend: this.config.conversationIndexBackend,
|
|
5113
|
-
status: "degraded" as const,
|
|
5114
|
-
};
|
|
5115
|
-
return {
|
|
5116
|
-
enabled: true,
|
|
5117
|
-
chunkDocCount,
|
|
5118
|
-
lastUpdateAt,
|
|
5119
|
-
...backendHealth,
|
|
5120
|
-
};
|
|
5035
|
+
return this.conversationIndexCoordinator.getHealth();
|
|
5121
5036
|
}
|
|
5122
5037
|
|
|
5123
5038
|
async inspectConversationIndex(): Promise<
|
|
@@ -5127,53 +5042,7 @@ export class Orchestrator {
|
|
|
5127
5042
|
lastUpdateAt: string | null;
|
|
5128
5043
|
}
|
|
5129
5044
|
> {
|
|
5130
|
-
|
|
5131
|
-
this.conversationIndexDir,
|
|
5132
|
-
);
|
|
5133
|
-
const lastUpdateAtMs = Math.max(
|
|
5134
|
-
0,
|
|
5135
|
-
...this.conversationIndexLastUpdateAtMs.values(),
|
|
5136
|
-
);
|
|
5137
|
-
const lastUpdateAt =
|
|
5138
|
-
lastUpdateAtMs > 0 ? new Date(lastUpdateAtMs).toISOString() : null;
|
|
5139
|
-
|
|
5140
|
-
if (!resolveIndexingCapabilities(this.config).conversationIndex) {
|
|
5141
|
-
return {
|
|
5142
|
-
enabled: false,
|
|
5143
|
-
backend: this.config.conversationIndexBackend,
|
|
5144
|
-
status: "disabled",
|
|
5145
|
-
available: false,
|
|
5146
|
-
indexPath: this.conversationIndexDir,
|
|
5147
|
-
supportsIncrementalUpdate: true,
|
|
5148
|
-
message: "Conversation index disabled by config",
|
|
5149
|
-
metadata: {
|
|
5150
|
-
chunkCount: chunkDocCount,
|
|
5151
|
-
},
|
|
5152
|
-
chunkDocCount,
|
|
5153
|
-
lastUpdateAt,
|
|
5154
|
-
};
|
|
5155
|
-
}
|
|
5156
|
-
|
|
5157
|
-
const inspection = this.conversationIndexBackend
|
|
5158
|
-
? await this.conversationIndexBackend.inspect()
|
|
5159
|
-
: {
|
|
5160
|
-
backend: this.config.conversationIndexBackend,
|
|
5161
|
-
status: "degraded" as const,
|
|
5162
|
-
available: false,
|
|
5163
|
-
indexPath: this.conversationIndexDir,
|
|
5164
|
-
supportsIncrementalUpdate: true,
|
|
5165
|
-
message: "Conversation index backend unavailable",
|
|
5166
|
-
metadata: {
|
|
5167
|
-
chunkCount: chunkDocCount,
|
|
5168
|
-
},
|
|
5169
|
-
};
|
|
5170
|
-
|
|
5171
|
-
return {
|
|
5172
|
-
enabled: true,
|
|
5173
|
-
chunkDocCount,
|
|
5174
|
-
lastUpdateAt,
|
|
5175
|
-
...inspection,
|
|
5176
|
-
};
|
|
5045
|
+
return this.conversationIndexCoordinator.inspect();
|
|
5177
5046
|
}
|
|
5178
5047
|
|
|
5179
5048
|
async getRecoverySummary(sessionKey?: string): Promise<{
|
|
@@ -5199,54 +5068,7 @@ export class Orchestrator {
|
|
|
5199
5068
|
retryAfterMs?: number;
|
|
5200
5069
|
embedded?: boolean;
|
|
5201
5070
|
}> {
|
|
5202
|
-
|
|
5203
|
-
return { chunks: 0, skipped: true, reason: "disabled", embedded: false };
|
|
5204
|
-
}
|
|
5205
|
-
const enforceMinInterval = opts?.enforceMinInterval !== false;
|
|
5206
|
-
if (enforceMinInterval) {
|
|
5207
|
-
const minIntervalMs = Math.max(
|
|
5208
|
-
0,
|
|
5209
|
-
this.config.conversationIndexMinUpdateIntervalMs,
|
|
5210
|
-
);
|
|
5211
|
-
const now = Date.now();
|
|
5212
|
-
const last = this.conversationIndexLastUpdateAtMs.get(sessionKey) ?? 0;
|
|
5213
|
-
const elapsed = now - last;
|
|
5214
|
-
if (minIntervalMs > 0 && elapsed < minIntervalMs) {
|
|
5215
|
-
return {
|
|
5216
|
-
chunks: 0,
|
|
5217
|
-
skipped: true,
|
|
5218
|
-
reason: "min_interval",
|
|
5219
|
-
retryAfterMs: minIntervalMs - elapsed,
|
|
5220
|
-
embedded: false,
|
|
5221
|
-
};
|
|
5222
|
-
}
|
|
5223
|
-
}
|
|
5224
|
-
const chunks = await this.buildConversationIndexChunks(sessionKey, hours);
|
|
5225
|
-
await writeConversationChunks(this.conversationIndexDir, chunks);
|
|
5226
|
-
const retentionCutoffMs =
|
|
5227
|
-
Number.isFinite(this.config.conversationIndexRetentionDays) &&
|
|
5228
|
-
this.config.conversationIndexRetentionDays > 0
|
|
5229
|
-
? Date.now() -
|
|
5230
|
-
this.config.conversationIndexRetentionDays * 24 * 60 * 60 * 1000
|
|
5231
|
-
: undefined;
|
|
5232
|
-
await cleanupConversationChunks(
|
|
5233
|
-
this.conversationIndexDir,
|
|
5234
|
-
this.config.conversationIndexRetentionDays,
|
|
5235
|
-
);
|
|
5236
|
-
const shouldEmbed =
|
|
5237
|
-
opts?.embed ?? this.config.conversationIndexEmbedOnUpdate;
|
|
5238
|
-
let embedded = false;
|
|
5239
|
-
|
|
5240
|
-
if (this.conversationIndexBackend) {
|
|
5241
|
-
const result = await this.conversationIndexBackend.update(chunks, {
|
|
5242
|
-
embed: shouldEmbed,
|
|
5243
|
-
...(retentionCutoffMs !== undefined ? { retentionCutoffMs } : {}),
|
|
5244
|
-
});
|
|
5245
|
-
embedded = result.embedded;
|
|
5246
|
-
}
|
|
5247
|
-
|
|
5248
|
-
this.conversationIndexLastUpdateAtMs.set(sessionKey, Date.now());
|
|
5249
|
-
return { chunks: chunks.length, skipped: false, embedded };
|
|
5071
|
+
return this.conversationIndexCoordinator.update(sessionKey, hours, opts);
|
|
5250
5072
|
}
|
|
5251
5073
|
|
|
5252
5074
|
async rebuildConversationIndex(
|
|
@@ -5260,42 +5082,7 @@ export class Orchestrator {
|
|
|
5260
5082
|
embedded?: boolean;
|
|
5261
5083
|
rebuilt?: boolean;
|
|
5262
5084
|
}> {
|
|
5263
|
-
|
|
5264
|
-
return {
|
|
5265
|
-
chunks: 0,
|
|
5266
|
-
skipped: true,
|
|
5267
|
-
reason: "disabled",
|
|
5268
|
-
embedded: false,
|
|
5269
|
-
rebuilt: false,
|
|
5270
|
-
};
|
|
5271
|
-
}
|
|
5272
|
-
|
|
5273
|
-
const chunks = await this.buildConversationIndexChunks(sessionKey, hours);
|
|
5274
|
-
await writeConversationChunks(this.conversationIndexDir, chunks);
|
|
5275
|
-
await cleanupConversationChunks(
|
|
5276
|
-
this.conversationIndexDir,
|
|
5277
|
-
this.config.conversationIndexRetentionDays,
|
|
5278
|
-
);
|
|
5279
|
-
|
|
5280
|
-
const shouldEmbed =
|
|
5281
|
-
opts?.embed ?? this.config.conversationIndexEmbedOnUpdate;
|
|
5282
|
-
let embedded = false;
|
|
5283
|
-
let rebuilt = false;
|
|
5284
|
-
if (this.conversationIndexBackend) {
|
|
5285
|
-
const result = await this.conversationIndexBackend.rebuild(chunks, {
|
|
5286
|
-
embed: shouldEmbed,
|
|
5287
|
-
});
|
|
5288
|
-
embedded = result.embedded;
|
|
5289
|
-
rebuilt = result.rebuilt;
|
|
5290
|
-
}
|
|
5291
|
-
|
|
5292
|
-
const stamp = Date.now();
|
|
5293
|
-
if (sessionKey) {
|
|
5294
|
-
this.conversationIndexLastUpdateAtMs.set(sessionKey, stamp);
|
|
5295
|
-
} else {
|
|
5296
|
-
this.conversationIndexLastUpdateAtMs.set("__rebuild__", stamp);
|
|
5297
|
-
}
|
|
5298
|
-
return { chunks: chunks.length, skipped: false, embedded, rebuilt };
|
|
5085
|
+
return this.conversationIndexCoordinator.rebuild(sessionKey, hours, opts);
|
|
5299
5086
|
}
|
|
5300
5087
|
|
|
5301
5088
|
/**
|
|
@@ -7089,7 +6876,7 @@ export class Orchestrator {
|
|
|
7089
6876
|
signal: options.abortSignal,
|
|
7090
6877
|
});
|
|
7091
6878
|
if (
|
|
7092
|
-
this.config.
|
|
6879
|
+
resolveRecallEnhancementCapabilities(this.config).recallPlannerTelemetry &&
|
|
7093
6880
|
recallDecision.plannerSource &&
|
|
7094
6881
|
recallDecision.plannerSource !== "heuristic"
|
|
7095
6882
|
) {
|
|
@@ -7623,7 +7410,7 @@ export class Orchestrator {
|
|
|
7623
7410
|
if (
|
|
7624
7411
|
!this.isRecallSectionEnabled(
|
|
7625
7412
|
"shared-context",
|
|
7626
|
-
this.config.
|
|
7413
|
+
resolveConversationContextCapabilities(this.config).sharedContext === true,
|
|
7627
7414
|
)
|
|
7628
7415
|
)
|
|
7629
7416
|
return null;
|
|
@@ -7747,7 +7534,7 @@ export class Orchestrator {
|
|
|
7747
7534
|
| null
|
|
7748
7535
|
| undefined = undefined;
|
|
7749
7536
|
const peerProfileRecallPromise = (async (): Promise<string | null> => {
|
|
7750
|
-
if (!this.config.
|
|
7537
|
+
if (!resolveRecallEnhancementCapabilities(this.config).peerProfileRecall) return null;
|
|
7751
7538
|
if (this.config.peerProfileRecallMaxFields <= 0) return null;
|
|
7752
7539
|
const peerId = this.getPeerIdForSession(sessionKey);
|
|
7753
7540
|
if (!peerId) return null;
|
|
@@ -8154,11 +7941,11 @@ export class Orchestrator {
|
|
|
8154
7941
|
const causalTrajectoryPromise = (async (): Promise<string | null> => {
|
|
8155
7942
|
const t0 = Date.now();
|
|
8156
7943
|
if (
|
|
8157
|
-
!this.config.
|
|
8158
|
-
!this.config.
|
|
7944
|
+
!resolveRecallEnhancementCapabilities(this.config).causalTrajectoryMemory ||
|
|
7945
|
+
!resolveRecallEnhancementCapabilities(this.config).causalTrajectoryRecall ||
|
|
8159
7946
|
!this.isRecallSectionEnabled(
|
|
8160
7947
|
"causal-trajectories",
|
|
8161
|
-
this.config.
|
|
7948
|
+
resolveRecallEnhancementCapabilities(this.config).causalTrajectoryRecall === true,
|
|
8162
7949
|
)
|
|
8163
7950
|
) {
|
|
8164
7951
|
recordRecallSectionMetric({
|
|
@@ -8211,10 +7998,10 @@ export class Orchestrator {
|
|
|
8211
7998
|
const cmcRetrievalPromise = (async (): Promise<string | null> => {
|
|
8212
7999
|
const t0 = Date.now();
|
|
8213
8000
|
if (
|
|
8214
|
-
!this.config.
|
|
8001
|
+
!resolveRecallEnhancementCapabilities(this.config).cmcRetrieval ||
|
|
8215
8002
|
!this.isRecallSectionEnabled(
|
|
8216
8003
|
"cmc-causal-chains",
|
|
8217
|
-
this.config.
|
|
8004
|
+
resolveRecallEnhancementCapabilities(this.config).cmcRetrieval === true,
|
|
8218
8005
|
)
|
|
8219
8006
|
) {
|
|
8220
8007
|
recordRecallSectionMetric({
|
|
@@ -9212,7 +8999,7 @@ export class Orchestrator {
|
|
|
9212
8999
|
let section: string | null = null;
|
|
9213
9000
|
// Try checkpoint first (post-compaction recovery)
|
|
9214
9001
|
let checkpointInjected = false;
|
|
9215
|
-
if (this.config.
|
|
9002
|
+
if (resolvePipelineProcessingCapabilities(this.config).checkpoint) {
|
|
9216
9003
|
const checkpoint = await this.transcript.loadCheckpoint(sessionKey);
|
|
9217
9004
|
log.debug(
|
|
9218
9005
|
`recall: checkpoint loaded, turns=${checkpoint?.turns?.length ?? 0}`,
|
|
@@ -9345,7 +9132,7 @@ export class Orchestrator {
|
|
|
9345
9132
|
const summariesPromise = (async (): Promise<string | null> => {
|
|
9346
9133
|
const t0 = Date.now();
|
|
9347
9134
|
if (
|
|
9348
|
-
!this.config.
|
|
9135
|
+
!resolvePipelineProcessingCapabilities(this.config).hourlySummaries ||
|
|
9349
9136
|
!sessionKey ||
|
|
9350
9137
|
!this.isRecallSectionEnabled("summaries", true)
|
|
9351
9138
|
) {
|
|
@@ -9575,7 +9362,7 @@ export class Orchestrator {
|
|
|
9575
9362
|
const t0 = Date.now();
|
|
9576
9363
|
if (
|
|
9577
9364
|
!this.compounding ||
|
|
9578
|
-
!this.config.
|
|
9365
|
+
!resolveRecallEnhancementCapabilities(this.config).compoundingInject ||
|
|
9579
9366
|
!this.isRecallSectionEnabled("compounding", true)
|
|
9580
9367
|
) {
|
|
9581
9368
|
recordRecallSectionMetric({
|
|
@@ -9916,7 +9703,7 @@ export class Orchestrator {
|
|
|
9916
9703
|
this.getRecallSectionMaxChars("explicit-cue") ??
|
|
9917
9704
|
this.config.explicitCueRecallMaxChars;
|
|
9918
9705
|
if (
|
|
9919
|
-
this.config.
|
|
9706
|
+
resolveRecallEnhancementCapabilities(this.config).explicitCueRecall &&
|
|
9920
9707
|
this.isRecallSectionEnabled("explicit-cue") &&
|
|
9921
9708
|
explicitCueMaxChars !== 0 &&
|
|
9922
9709
|
this.lcmEngine?.enabled &&
|
|
@@ -9959,7 +9746,7 @@ export class Orchestrator {
|
|
|
9959
9746
|
if (
|
|
9960
9747
|
this.isSpecializedRecallSectionEnabled(
|
|
9961
9748
|
"targeted-facts",
|
|
9962
|
-
this.config.
|
|
9749
|
+
resolveRecallEnhancementCapabilities(this.config).targetedFactRecall,
|
|
9963
9750
|
) &&
|
|
9964
9751
|
targetedFactMaxChars !== 0 &&
|
|
9965
9752
|
this.lcmEngine?.enabled &&
|
|
@@ -10008,7 +9795,7 @@ export class Orchestrator {
|
|
|
10008
9795
|
if (
|
|
10009
9796
|
this.isSpecializedRecallSectionEnabled(
|
|
10010
9797
|
"focused-list",
|
|
10011
|
-
this.config.
|
|
9798
|
+
resolveRecallEnhancementCapabilities(this.config).focusedListRecall,
|
|
10012
9799
|
) &&
|
|
10013
9800
|
focusedListMaxChars !== 0 &&
|
|
10014
9801
|
this.lcmEngine?.enabled &&
|
|
@@ -10062,7 +9849,7 @@ export class Orchestrator {
|
|
|
10062
9849
|
if (
|
|
10063
9850
|
this.isSpecializedRecallSectionEnabled(
|
|
10064
9851
|
"response-guidance",
|
|
10065
|
-
this.config.
|
|
9852
|
+
resolveRecallEnhancementCapabilities(this.config).responseGuidanceRecall,
|
|
10066
9853
|
) &&
|
|
10067
9854
|
responseGuidanceMaxChars !== 0 &&
|
|
10068
9855
|
this.lcmEngine?.enabled &&
|
|
@@ -10111,7 +9898,7 @@ export class Orchestrator {
|
|
|
10111
9898
|
if (
|
|
10112
9899
|
this.isSpecializedRecallSectionEnabled(
|
|
10113
9900
|
"event-order",
|
|
10114
|
-
this.config.
|
|
9901
|
+
resolveRecallEnhancementCapabilities(this.config).eventOrderRecall,
|
|
10115
9902
|
) &&
|
|
10116
9903
|
eventOrderMaxChars !== 0 &&
|
|
10117
9904
|
this.lcmEngine?.enabled &&
|
|
@@ -10537,7 +10324,7 @@ export class Orchestrator {
|
|
|
10537
10324
|
recallMode === "graph_mode" || isFullModeGraphAssist;
|
|
10538
10325
|
const graphShadowEvalEnabled =
|
|
10539
10326
|
isFullModeGraphAssist &&
|
|
10540
|
-
this.config.
|
|
10327
|
+
resolveRecallEnhancementCapabilities(this.config).graphAssistShadowEval === true;
|
|
10541
10328
|
if (shouldRunGraphExpansion) {
|
|
10542
10329
|
shouldPersistGraphSnapshot = true;
|
|
10543
10330
|
graphDecisionShadowMode = graphShadowEvalEnabled;
|
|
@@ -10801,7 +10588,7 @@ export class Orchestrator {
|
|
|
10801
10588
|
);
|
|
10802
10589
|
|
|
10803
10590
|
// E-Mem-inspired memory reconstruction: fill gaps for referenced entities
|
|
10804
|
-
if (this.config.
|
|
10591
|
+
if (resolveRecallEnhancementCapabilities(this.config).memoryReconstruction && memoryResults.length > 0) {
|
|
10805
10592
|
try {
|
|
10806
10593
|
const snippets = memoryResults.map((r) => r.snippet);
|
|
10807
10594
|
// Extract entity paths already present in recall results to avoid duplicates
|
|
@@ -12465,7 +12252,7 @@ export class Orchestrator {
|
|
|
12465
12252
|
sessionKey: string,
|
|
12466
12253
|
options: { bufferKey?: string } = {},
|
|
12467
12254
|
): Promise<void> {
|
|
12468
|
-
if (this.config.
|
|
12255
|
+
if (resolvePipelineProcessingCapabilities(this.config).sessionObserver !== true) return;
|
|
12469
12256
|
if (!sessionKey || sessionKey.length === 0) return;
|
|
12470
12257
|
|
|
12471
12258
|
const bufferKey =
|
|
@@ -13489,7 +13276,7 @@ export class Orchestrator {
|
|
|
13489
13276
|
// the citation survives hostile memory text, copy/paste, and LLM quoting.
|
|
13490
13277
|
// The helper is a no-op when the feature flag is off, so legacy pipelines
|
|
13491
13278
|
// see zero behavioral change.
|
|
13492
|
-
const citationEnabled = this.config.
|
|
13279
|
+
const citationEnabled = resolvePipelineProcessingCapabilities(this.config).inlineSourceAttribution === true;
|
|
13493
13280
|
const citationTemplate = this.config.inlineSourceAttributionFormat;
|
|
13494
13281
|
// The stable fields (agent, session) are computed once; `ts` is intentionally
|
|
13495
13282
|
// omitted here and added fresh per invocation so each fact in a large batch
|
|
@@ -13648,7 +13435,7 @@ export class Orchestrator {
|
|
|
13648
13435
|
const actualTier = confidenceTier(confidence);
|
|
13649
13436
|
const actualRank = confidenceTierOrder.indexOf(actualTier);
|
|
13650
13437
|
if (actualRank === -1) return false;
|
|
13651
|
-
if (!this.config.
|
|
13438
|
+
if (!resolveRecallEnhancementCapabilities(this.config).autoPromoteToShared) return false;
|
|
13652
13439
|
if (!this.config.autoPromoteToSharedCategories.includes(category as any))
|
|
13653
13440
|
return false;
|
|
13654
13441
|
const minimumRank = confidenceTierOrder.indexOf(
|
|
@@ -15048,7 +14835,7 @@ export class Orchestrator {
|
|
|
15048
14835
|
// a high-similarity update/correction is linked as a superseding
|
|
15049
14836
|
// contradiction rather than silently dropped.
|
|
15050
14837
|
let pendingSemanticSkip: (SemanticDedupDecision & { action: "skip" }) | null = null;
|
|
15051
|
-
if (this.config.
|
|
14838
|
+
if (resolvePipelineProcessingCapabilities(this.config).semanticDedup) {
|
|
15052
14839
|
let semanticDecision: SemanticDedupDecision;
|
|
15053
14840
|
// UUI2: skip embedding lookup for the rest of this batch once we know
|
|
15054
14841
|
// the backend is unavailable. The flag is reset per-batch (set to false
|
|
@@ -15093,7 +14880,7 @@ export class Orchestrator {
|
|
|
15093
14880
|
}
|
|
15094
14881
|
}
|
|
15095
14882
|
|
|
15096
|
-
const inferredIntent = this.config.
|
|
14883
|
+
const inferredIntent = resolveConversationContextCapabilities(this.config).intentRouting
|
|
15097
14884
|
? inferIntentFromText(
|
|
15098
14885
|
`${writeCategory} ${fact.tags.join(" ")} ${fact.content}`,
|
|
15099
14886
|
)
|
|
@@ -15133,7 +14920,7 @@ export class Orchestrator {
|
|
|
15133
14920
|
// for a pending_review fact — an unfaithful extraction in the review queue
|
|
15134
14921
|
// must not trigger auto-resolve and retire an existing active memory.
|
|
15135
14922
|
if (
|
|
15136
|
-
this.config.
|
|
14923
|
+
resolveRecallEnhancementCapabilities(this.config).contradictionDetection &&
|
|
15137
14924
|
this.qmd.isAvailable() &&
|
|
15138
14925
|
faithfulnessEnforceStatus !== "pending_review"
|
|
15139
14926
|
) {
|
|
@@ -15218,10 +15005,10 @@ export class Orchestrator {
|
|
|
15218
15005
|
// When semanticChunkingEnabled is true, prefer the embedding-based
|
|
15219
15006
|
// semantic chunker which produces more coherent topic-aligned segments.
|
|
15220
15007
|
// Falls back to the recursive sentence-boundary chunker on failure.
|
|
15221
|
-
if (this.config.
|
|
15008
|
+
if (resolvePipelineProcessingCapabilities(this.config).chunking && writeCategory !== "procedure") {
|
|
15222
15009
|
let chunkResult: { chunked: boolean; chunks: { content: string; index: number; tokenCount: number }[] };
|
|
15223
15010
|
|
|
15224
|
-
if (this.config.
|
|
15011
|
+
if (resolvePipelineProcessingCapabilities(this.config).semanticChunking) {
|
|
15225
15012
|
try {
|
|
15226
15013
|
const embedFn = this.embeddingFallback.embedTexts.bind(this.embeddingFallback);
|
|
15227
15014
|
const semanticResult: SemanticChunkResult = await semanticChunkContent(
|
|
@@ -15588,7 +15375,7 @@ export class Orchestrator {
|
|
|
15588
15375
|
}
|
|
15589
15376
|
|
|
15590
15377
|
// Suggest links for this memory (Phase 3A)
|
|
15591
|
-
if (this.config.
|
|
15378
|
+
if (resolveRecallEnhancementCapabilities(this.config).memoryLinking && this.qmd.isAvailable()) {
|
|
15592
15379
|
const targetNamespace = this.storageDirNamespace(targetStorage.dir);
|
|
15593
15380
|
const suggestedLinks = await this.suggestLinksForMemory(
|
|
15594
15381
|
fact.content,
|
|
@@ -15908,7 +15695,7 @@ export class Orchestrator {
|
|
|
15908
15695
|
|
|
15909
15696
|
// Persist entity relationships (v7.0)
|
|
15910
15697
|
if (
|
|
15911
|
-
this.config.
|
|
15698
|
+
resolveRecallEnhancementCapabilities(this.config).entityRelationships &&
|
|
15912
15699
|
Array.isArray(result.relationships)
|
|
15913
15700
|
) {
|
|
15914
15701
|
for (const rel of result.relationships.slice(0, 5)) {
|
|
@@ -15932,7 +15719,7 @@ export class Orchestrator {
|
|
|
15932
15719
|
}
|
|
15933
15720
|
|
|
15934
15721
|
// Persist entity activity (v7.0)
|
|
15935
|
-
if (this.config.
|
|
15722
|
+
if (resolveRecallEnhancementCapabilities(this.config).entityActivityLog) {
|
|
15936
15723
|
const today = new Date().toISOString().slice(0, 10);
|
|
15937
15724
|
for (const entity of entities) {
|
|
15938
15725
|
const name = (entity as any)?.name;
|
|
@@ -15970,7 +15757,7 @@ export class Orchestrator {
|
|
|
15970
15757
|
// counts as a durable non-fact write for the catalog touch below (NIIly).
|
|
15971
15758
|
// Only count it when the write actually succeeds (best-effort write); the
|
|
15972
15759
|
// touch is recorded AFTER this so a rolled-back/failed write never touches.
|
|
15973
|
-
if (this.config.
|
|
15760
|
+
if (resolveRecallEnhancementCapabilities(this.config).identity && result.identityReflection) {
|
|
15974
15761
|
try {
|
|
15975
15762
|
await storage.appendIdentityReflection(result.identityReflection);
|
|
15976
15763
|
recordDurableNonFactWrite();
|
|
@@ -16537,7 +16324,7 @@ export class Orchestrator {
|
|
|
16537
16324
|
allMemories = await this.storage.readAllMemories();
|
|
16538
16325
|
|
|
16539
16326
|
// Fact archival pass (v6.0) — move old, low-importance, rarely-accessed facts to archive/
|
|
16540
|
-
if (this.config.
|
|
16327
|
+
if (resolveRecallEnhancementCapabilities(this.config).factArchival) {
|
|
16541
16328
|
const archived = await this.runFactArchival(allMemories);
|
|
16542
16329
|
if (archived > 0) {
|
|
16543
16330
|
memoryItemMutated = true;
|
|
@@ -16635,7 +16422,7 @@ export class Orchestrator {
|
|
|
16635
16422
|
}
|
|
16636
16423
|
|
|
16637
16424
|
// Auto-consolidate IDENTITY.md if it's getting large
|
|
16638
|
-
if (this.config.
|
|
16425
|
+
if (resolveRecallEnhancementCapabilities(this.config).identity) {
|
|
16639
16426
|
await this.autoConsolidateIdentity();
|
|
16640
16427
|
}
|
|
16641
16428
|
|
|
@@ -16675,12 +16462,12 @@ export class Orchestrator {
|
|
|
16675
16462
|
}
|
|
16676
16463
|
|
|
16677
16464
|
// Memory Summarization (Phase 4A)
|
|
16678
|
-
if (this.config.
|
|
16465
|
+
if (resolvePipelineProcessingCapabilities(this.config).summarization) {
|
|
16679
16466
|
await this.runSummarization(allMemories);
|
|
16680
16467
|
}
|
|
16681
16468
|
|
|
16682
16469
|
// Topic Extraction (Phase 4B)
|
|
16683
|
-
if (this.config.
|
|
16470
|
+
if (resolvePipelineProcessingCapabilities(this.config).topicExtraction) {
|
|
16684
16471
|
await this.runTopicExtraction(allMemories);
|
|
16685
16472
|
}
|
|
16686
16473
|
|
|
@@ -17365,156 +17152,15 @@ export class Orchestrator {
|
|
|
17365
17152
|
return matches[0] ?? null;
|
|
17366
17153
|
}
|
|
17367
17154
|
|
|
17155
|
+
// Issue #1526: recall-rerank methods moved to RecallRerankCoordinator.
|
|
17156
|
+
// Thin delegation keeps the private API stable for callers + tests.
|
|
17368
17157
|
private async applyMemoryWorthRerank(
|
|
17369
17158
|
results: QmdSearchResult[],
|
|
17370
17159
|
namespaces: string[],
|
|
17371
17160
|
): Promise<QmdSearchResult[]> {
|
|
17372
|
-
|
|
17373
|
-
// namespace the recall spans — the recall path itself already
|
|
17374
|
-
// aggregates candidates from multiple namespaces, so we must do the
|
|
17375
|
-
// same when looking up counters. Per-namespace results are cached with
|
|
17376
|
-
// a short TTL so interactive recall doesn't trigger a full
|
|
17377
|
-
// `readAllMemories` scan per query (addresses codex P2 on PR 4).
|
|
17378
|
-
const counters = new Map<string, MemoryWorthCounters>();
|
|
17379
|
-
const seenNamespaces = new Set<string>();
|
|
17380
|
-
const nowMs = Date.now();
|
|
17381
|
-
|
|
17382
|
-
// Evict all expired entries on every call so long-running processes
|
|
17383
|
-
// touching a high-cardinality namespace set (coding/project overlays,
|
|
17384
|
-
// per-branch) don't grow the cache unboundedly. Without this, an entry
|
|
17385
|
-
// for a namespace that's never looked up again would pin its full
|
|
17386
|
-
// counter map forever.
|
|
17387
|
-
for (const [key, entry] of this.memoryWorthCounterCache) {
|
|
17388
|
-
if (nowMs - entry.at >= Orchestrator.MEMORY_WORTH_CACHE_TTL_MS) {
|
|
17389
|
-
this.memoryWorthCounterCache.delete(key);
|
|
17390
|
-
}
|
|
17391
|
-
}
|
|
17392
|
-
|
|
17393
|
-
for (const ns of namespaces) {
|
|
17394
|
-
if (seenNamespaces.has(ns)) continue;
|
|
17395
|
-
seenNamespaces.add(ns);
|
|
17396
|
-
try {
|
|
17397
|
-
const cached = this.memoryWorthCounterCache.get(ns);
|
|
17398
|
-
let nsMap: ReadonlyMap<string, MemoryWorthCounters> | undefined;
|
|
17399
|
-
if (
|
|
17400
|
-
cached &&
|
|
17401
|
-
nowMs - cached.at < Orchestrator.MEMORY_WORTH_CACHE_TTL_MS
|
|
17402
|
-
) {
|
|
17403
|
-
nsMap = cached.counters;
|
|
17404
|
-
} else {
|
|
17405
|
-
const storage = await this.getStorage(ns);
|
|
17406
|
-
const memories = await storage.readAllMemories();
|
|
17407
|
-
nsMap = buildMemoryWorthCounterMap(memories);
|
|
17408
|
-
this.memoryWorthCounterCache.set(ns, { at: nowMs, counters: nsMap });
|
|
17409
|
-
}
|
|
17410
|
-
for (const [path, c] of nsMap) counters.set(path, c);
|
|
17411
|
-
} catch (err) {
|
|
17412
|
-
log.debug("memory-worth: failed to read namespace, skipping", {
|
|
17413
|
-
namespace: ns,
|
|
17414
|
-
error: (err as Error).message,
|
|
17415
|
-
});
|
|
17416
|
-
}
|
|
17417
|
-
}
|
|
17418
|
-
|
|
17419
|
-
// For candidates whose path didn't show up in any hot-tier namespace
|
|
17420
|
-
// scan (typical of cold-tier / archive fallback), try a direct
|
|
17421
|
-
// per-path read. Without this, cold-tier candidates always stay at
|
|
17422
|
-
// multiplier 1.0 even when they have outcome history. Errors are
|
|
17423
|
-
// swallowed so a single unreadable archive entry can't break the
|
|
17424
|
-
// whole recall.
|
|
17425
|
-
const missing = results.filter((r) => !counters.has(r.path));
|
|
17426
|
-
if (missing.length > 0) {
|
|
17427
|
-
// Use the first-seen namespace's storage as the reader — all
|
|
17428
|
-
// StorageManagers share the same on-disk format, and
|
|
17429
|
-
// `readMemoryByPath` takes an absolute path so the baseDir doesn't
|
|
17430
|
-
// have to match.
|
|
17431
|
-
let reader: StorageManager | null = null;
|
|
17432
|
-
for (const ns of namespaces) {
|
|
17433
|
-
try {
|
|
17434
|
-
reader = await this.getStorage(ns);
|
|
17435
|
-
break;
|
|
17436
|
-
} catch {
|
|
17437
|
-
// try next namespace
|
|
17438
|
-
}
|
|
17439
|
-
}
|
|
17440
|
-
if (reader) {
|
|
17441
|
-
for (const r of missing) {
|
|
17442
|
-
try {
|
|
17443
|
-
const memory = await this.readQmdResultMemory(r.path, reader, namespaces);
|
|
17444
|
-
if (!memory) continue;
|
|
17445
|
-
const fm = memory.frontmatter;
|
|
17446
|
-
if (fm.mw_success === undefined && fm.mw_fail === undefined) continue;
|
|
17447
|
-
counters.set(r.path, {
|
|
17448
|
-
mw_success: fm.mw_success,
|
|
17449
|
-
mw_fail: fm.mw_fail,
|
|
17450
|
-
lastAccessed: fm.lastAccessed,
|
|
17451
|
-
});
|
|
17452
|
-
} catch (err) {
|
|
17453
|
-
log.debug("memory-worth: direct path lookup failed", {
|
|
17454
|
-
path: r.path,
|
|
17455
|
-
error: (err as Error).message,
|
|
17456
|
-
});
|
|
17457
|
-
}
|
|
17458
|
-
}
|
|
17459
|
-
}
|
|
17460
|
-
}
|
|
17461
|
-
|
|
17462
|
-
// If no memory in the candidate set has any counter data, the filter
|
|
17463
|
-
// would be a no-op — skip the reorder to avoid spurious log spam.
|
|
17464
|
-
if (counters.size === 0) return results;
|
|
17465
|
-
|
|
17466
|
-
// Preserve upstream ordering (reranker, specialized tiers, etc.) for
|
|
17467
|
-
// neutral candidates. The upstream stages set `memoryResults` in their
|
|
17468
|
-
// intended order but often leave `r.score` as the raw QMD score. If we
|
|
17469
|
-
// sorted by `r.score * multiplier` directly, neutral candidates
|
|
17470
|
-
// (multiplier 1.0) would snap back to raw-QMD order and silently undo
|
|
17471
|
-
// the reranker. Feed the filter a synthetic monotone-decreasing rank
|
|
17472
|
-
// score so it uses input position as the baseline, then applies the
|
|
17473
|
-
// multiplier on top. Ties fall back to the stable secondary key in
|
|
17474
|
-
// `applyMemoryWorthFilter`.
|
|
17475
|
-
const rankedInputs = results.map((r, i) => ({
|
|
17476
|
-
path: r.path,
|
|
17477
|
-
// Large positive rank score so multiplier math stays well-scaled and
|
|
17478
|
-
// we never hit zero; descending so earlier items rank higher.
|
|
17479
|
-
score: results.length - i,
|
|
17480
|
-
}));
|
|
17481
|
-
const filtered = applyMemoryWorthFilter(rankedInputs, {
|
|
17482
|
-
counters,
|
|
17483
|
-
now: new Date(),
|
|
17484
|
-
halfLifeMs:
|
|
17485
|
-
this.config.recallMemoryWorthHalfLifeMs > 0
|
|
17486
|
-
? this.config.recallMemoryWorthHalfLifeMs
|
|
17487
|
-
: undefined,
|
|
17488
|
-
});
|
|
17489
|
-
|
|
17490
|
-
// Reconstruct the QmdSearchResult list in the new order. `.score` is
|
|
17491
|
-
// preserved from the upstream pipeline (rerank, tier scoring, etc.) —
|
|
17492
|
-
// we only reorder. Writing the synthetic rank-weighted score back
|
|
17493
|
-
// would contaminate downstream logic (telemetry, confidence gates)
|
|
17494
|
-
// that expects the original QMD/rerank score semantics.
|
|
17495
|
-
const byPath = new Map(results.map((r) => [r.path, r]));
|
|
17496
|
-
const reordered: QmdSearchResult[] = [];
|
|
17497
|
-
for (const item of filtered) {
|
|
17498
|
-
const original = byPath.get(item.path);
|
|
17499
|
-
if (original) reordered.push(original);
|
|
17500
|
-
}
|
|
17501
|
-
return reordered;
|
|
17161
|
+
return this.recallRerankCoordinator.applyMemoryWorthRerank(results, namespaces);
|
|
17502
17162
|
}
|
|
17503
17163
|
|
|
17504
|
-
/**
|
|
17505
|
-
* Issue #1577 — unified TrustScore recall stage. Thin wiring over the pure
|
|
17506
|
-
* {@link applyTrustScoreStage} scorer + the {@link buildTrustSignalsForRerank}
|
|
17507
|
-
* signal builder. The stage subsumes the Memory Worth multiplier — the
|
|
17508
|
-
* orchestrator runs exactly one of the two (mutual exclusion, rule 39; the
|
|
17509
|
-
* double-multiplier test in trust-score-stage.test.ts pins it structurally).
|
|
17510
|
-
*
|
|
17511
|
-
* Returns the admitted results AND the per-path trust map (including
|
|
17512
|
-
* quarantined items) so the caller can: (a) render epistemic hedges, (b)
|
|
17513
|
-
* surface quarantined items in X-ray with a reason (rule 34), and (c) filter
|
|
17514
|
-
* quarantined paths from fallback recall branches. The trust map is a
|
|
17515
|
-
* per-recall local — never instance state — so concurrent recalls cannot
|
|
17516
|
-
* race on it (review: shared-trust-map concurrency).
|
|
17517
|
-
*/
|
|
17518
17164
|
private async applyTrustScoreRerank(
|
|
17519
17165
|
results: QmdSearchResult[],
|
|
17520
17166
|
namespaces: string[],
|
|
@@ -17522,74 +17168,9 @@ export class Orchestrator {
|
|
|
17522
17168
|
results: QmdSearchResult[];
|
|
17523
17169
|
trustByPath: Map<string, TrustStageResultItem> | null;
|
|
17524
17170
|
}> {
|
|
17525
|
-
|
|
17526
|
-
const now = new Date();
|
|
17527
|
-
const halfLifeDays =
|
|
17528
|
-
this.config.recallMemoryWorthHalfLifeMs > 0
|
|
17529
|
-
? this.config.recallMemoryWorthHalfLifeMs / (24 * 60 * 60 * 1000)
|
|
17530
|
-
: undefined;
|
|
17531
|
-
// Cold-tier direct-fallback reader: resolve once, reuse for every missing
|
|
17532
|
-
// candidate (mirrors the memory-worth filter's reader selection).
|
|
17533
|
-
let fallbackReader: StorageManager | null = null;
|
|
17534
|
-
const signals = await buildTrustSignalsForRerank(
|
|
17535
|
-
results.map((r) => r.path),
|
|
17536
|
-
namespaces,
|
|
17537
|
-
{
|
|
17538
|
-
readNamespaceMemories: async (ns) => (await this.getStorage(ns)).readAllMemories(),
|
|
17539
|
-
readMemoryFrontmatter: async (path) => {
|
|
17540
|
-
if (!fallbackReader) {
|
|
17541
|
-
for (const ns of namespaces) {
|
|
17542
|
-
try {
|
|
17543
|
-
fallbackReader = await this.getStorage(ns);
|
|
17544
|
-
break;
|
|
17545
|
-
} catch {
|
|
17546
|
-
// try next namespace
|
|
17547
|
-
}
|
|
17548
|
-
}
|
|
17549
|
-
}
|
|
17550
|
-
if (!fallbackReader) return null;
|
|
17551
|
-
const memory = await this.readQmdResultMemory(path, fallbackReader, namespaces);
|
|
17552
|
-
return memory ? memory.frontmatter : null;
|
|
17553
|
-
},
|
|
17554
|
-
},
|
|
17555
|
-
{ cache: this.trustSignalCache, ttlMs: Orchestrator.TRUST_SIGNAL_CACHE_TTL_MS },
|
|
17556
|
-
now,
|
|
17557
|
-
{
|
|
17558
|
-
recencyHalfLifeDays: halfLifeDays,
|
|
17559
|
-
logDebug: (message, context) => log.debug(message, context),
|
|
17560
|
-
},
|
|
17561
|
-
);
|
|
17562
|
-
if (signals.size === 0) {
|
|
17563
|
-
return { results, trustByPath: null };
|
|
17564
|
-
}
|
|
17565
|
-
// Synthetic monotone-decreasing rank so the multiplier rebias is applied
|
|
17566
|
-
// on top of upstream ordering, not raw QMD scores (see applyMemoryWorthRerank).
|
|
17567
|
-
const rankedInputs = results.map((r, i) => ({ path: r.path, score: results.length - i }));
|
|
17568
|
-
const stage = applyTrustScoreStage(rankedInputs, {
|
|
17569
|
-
signals,
|
|
17570
|
-
weights: this.config.trustScoreWeights,
|
|
17571
|
-
minMultiplier: this.config.trustScoreMinMultiplier,
|
|
17572
|
-
maxMultiplier: this.config.trustScoreMaxMultiplier,
|
|
17573
|
-
quarantine: this.config.trustScoreQuarantine,
|
|
17574
|
-
});
|
|
17575
|
-
const trustByPath = new Map(stage.all.map((item) => [item.path, item]));
|
|
17576
|
-
const byPath = new Map(results.map((r) => [r.path, r]));
|
|
17577
|
-
const admitted = stage.admitted
|
|
17578
|
-
.map((item) => byPath.get(item.path))
|
|
17579
|
-
.filter((r): r is QmdSearchResult => r !== undefined);
|
|
17580
|
-
return { results: admitted, trustByPath };
|
|
17171
|
+
return this.recallRerankCoordinator.applyTrustScoreRerank(results, namespaces);
|
|
17581
17172
|
}
|
|
17582
17173
|
|
|
17583
|
-
/**
|
|
17584
|
-
* Issue #1577 — apply the TrustScore stage (or, when trust is off, the Memory
|
|
17585
|
-
* Worth multiplier fallback) to ONE recall branch's results, returning the
|
|
17586
|
-
* scored results + the per-path trust map. Thin wiring over
|
|
17587
|
-
* {@link applyTrustScoreRerank} so every recall path — hot QMD, embedding
|
|
17588
|
-
* fallback, recent scan — applies the SAME multiplier gate (rule 41: a
|
|
17589
|
-
* feature gate must apply across ALL parallel recall paths). TrustScore
|
|
17590
|
-
* subsumes Memory Worth; exactly one runs (rule 39). Fail-open on lookup
|
|
17591
|
-
* errors so a storage hiccup never breaks a fallback path.
|
|
17592
|
-
*/
|
|
17593
17174
|
private async applyTrustScoreToBranch(
|
|
17594
17175
|
results: QmdSearchResult[],
|
|
17595
17176
|
namespaces: string[],
|
|
@@ -17599,25 +17180,7 @@ export class Orchestrator {
|
|
|
17599
17180
|
results: QmdSearchResult[];
|
|
17600
17181
|
trustByPath: Map<string, TrustStageResultItem> | null;
|
|
17601
17182
|
}> {
|
|
17602
|
-
|
|
17603
|
-
try {
|
|
17604
|
-
return await this.applyTrustScoreRerank(results, namespaces);
|
|
17605
|
-
} catch (err) {
|
|
17606
|
-
log.debug(`trust-score stage (${label}) failed open`, {
|
|
17607
|
-
error: (err as Error).message,
|
|
17608
|
-
});
|
|
17609
|
-
}
|
|
17610
|
-
} else if (caps.recallMemoryWorthFilter && results.length > 0) {
|
|
17611
|
-
try {
|
|
17612
|
-
const filtered = await this.applyMemoryWorthRerank(results, namespaces);
|
|
17613
|
-
return { results: filtered, trustByPath: null };
|
|
17614
|
-
} catch (err) {
|
|
17615
|
-
log.debug(`memory-worth filter (${label}) failed open`, {
|
|
17616
|
-
error: (err as Error).message,
|
|
17617
|
-
});
|
|
17618
|
-
}
|
|
17619
|
-
}
|
|
17620
|
-
return { results, trustByPath: null };
|
|
17183
|
+
return this.recallRerankCoordinator.applyTrustScoreToBranch(results, namespaces, caps, label);
|
|
17621
17184
|
}
|
|
17622
17185
|
|
|
17623
17186
|
private diversifyAndLimitRecallResults(
|
|
@@ -17625,89 +17188,17 @@ export class Orchestrator {
|
|
|
17625
17188
|
results: QmdSearchResult[],
|
|
17626
17189
|
limit: number,
|
|
17627
17190
|
retrievalQuery?: string,
|
|
17628
|
-
// `caps` is additive AND last (issue #1523) so the positional call shape
|
|
17629
|
-
// stays backward-compatible: the recall pipeline threads a resolved set,
|
|
17630
|
-
// but callers that omit it (e.g. direct unit-test invocations) get an
|
|
17631
|
-
// equivalent set derived from the same config — behavior-preserving.
|
|
17632
17191
|
caps: CapabilitySet = resolveCapabilities(this.config),
|
|
17633
17192
|
): QmdSearchResult[] {
|
|
17634
|
-
|
|
17635
|
-
typeof limit === "number" && Number.isFinite(limit)
|
|
17636
|
-
? Math.max(0, Math.floor(limit))
|
|
17637
|
-
: 0;
|
|
17638
|
-
if (!Array.isArray(results) || results.length === 0) return [];
|
|
17639
|
-
// `recallResultLimit === 0` is a true zero limit (e.g. when
|
|
17640
|
-
// `memoriesSectionEnabled` is false) and must return an empty array so
|
|
17641
|
-
// the memories section is genuinely skipped. This mirrors the
|
|
17642
|
-
// `slice(0, 0)` semantics of every call site this helper replaced.
|
|
17643
|
-
if (safeLimit === 0) return [];
|
|
17644
|
-
// Issue #564 PR 3: when the feature flag is on, boost reasoning_trace
|
|
17645
|
-
// memories for problem-solving asks so they bubble up ahead of ordinary
|
|
17646
|
-
// facts/decisions before MMR picks the final section. No-op when the
|
|
17647
|
-
// flag is off or the query is not a problem-solving ask.
|
|
17648
|
-
const boosted =
|
|
17649
|
-
caps.recallReasoningTraceBoost && typeof retrievalQuery === "string"
|
|
17650
|
-
? applyReasoningTraceBoost(results, {
|
|
17651
|
-
enabled: true,
|
|
17652
|
-
query: retrievalQuery,
|
|
17653
|
-
})
|
|
17654
|
-
: results;
|
|
17655
|
-
const diversified = this.applyMmrToQmdResults(sectionId, boosted, caps);
|
|
17656
|
-
return diversified.slice(0, safeLimit);
|
|
17193
|
+
return this.recallRerankCoordinator.diversifyAndLimitRecallResults(sectionId, results, limit, retrievalQuery, caps);
|
|
17657
17194
|
}
|
|
17658
17195
|
|
|
17659
|
-
/**
|
|
17660
|
-
* Apply Maximal Marginal Relevance to a section's ordered candidate list.
|
|
17661
|
-
*
|
|
17662
|
-
* Operates per-section so one redundant cluster cannot dominate a section,
|
|
17663
|
-
* and so one section's MMR pass cannot starve other sections. Returns the
|
|
17664
|
-
* input unchanged when disabled, when there are fewer than 2 candidates, or
|
|
17665
|
-
* when no budget information is available.
|
|
17666
|
-
*/
|
|
17667
17196
|
private applyMmrToQmdResults(
|
|
17668
17197
|
sectionId: string,
|
|
17669
17198
|
results: QmdSearchResult[],
|
|
17670
|
-
// Additive `caps` (issue #1523); defaults to a config-derived set so direct
|
|
17671
|
-
// callers that omit it behave identically to the threaded recall path.
|
|
17672
17199
|
caps: CapabilitySet = resolveCapabilities(this.config),
|
|
17673
17200
|
): QmdSearchResult[] {
|
|
17674
|
-
|
|
17675
|
-
if (!Array.isArray(results) || results.length < 2) return results;
|
|
17676
|
-
|
|
17677
|
-
// Config is runtime API (see AGENTS.md §4): preserve `0` as a true zero
|
|
17678
|
-
// limit rather than coercing it to a non-zero value. A configured topN of
|
|
17679
|
-
// 0 means "apply MMR over an empty window" — i.e. skip the reorder and
|
|
17680
|
-
// return the upstream candidates unchanged. This keeps read-time
|
|
17681
|
-
// behavior symmetric with the write-time semantics parseConfig exposes.
|
|
17682
|
-
const configuredTopN = this.config.recallMmrTopN;
|
|
17683
|
-
const topN =
|
|
17684
|
-
typeof configuredTopN === "number" && Number.isFinite(configuredTopN)
|
|
17685
|
-
? Math.max(0, Math.floor(configuredTopN))
|
|
17686
|
-
: 40;
|
|
17687
|
-
if (topN === 0) return results;
|
|
17688
|
-
const lambda = this.config.recallMmrLambda ?? 0.7;
|
|
17689
|
-
|
|
17690
|
-
// Delegate to the pure helper so candidate keying (path-first, index
|
|
17691
|
-
// suffixed for uniqueness) and the head-of-list diversity metric are
|
|
17692
|
-
// exercised by the same code path that the unit tests cover.
|
|
17693
|
-
const { reordered, diversity } = reorderRecallResultsWithMmr(results, {
|
|
17694
|
-
lambda,
|
|
17695
|
-
topN,
|
|
17696
|
-
});
|
|
17697
|
-
|
|
17698
|
-
try {
|
|
17699
|
-
log.info(
|
|
17700
|
-
`recall_mmr: section=${sectionId} kept=${diversity.kept}/${diversity.considered} ` +
|
|
17701
|
-
`headReorderCount=${diversity.headReorderCount} ` +
|
|
17702
|
-
`avgSimBefore=${diversity.avgPairwiseSimBefore.toFixed(3)} ` +
|
|
17703
|
-
`avgSimAfter=${diversity.avgPairwiseSimAfter.toFixed(3)} ` +
|
|
17704
|
-
`lambda=${lambda.toFixed(2)}`,
|
|
17705
|
-
);
|
|
17706
|
-
} catch {
|
|
17707
|
-
// Metrics must never break recall.
|
|
17708
|
-
}
|
|
17709
|
-
|
|
17710
|
-
return reordered;
|
|
17201
|
+
return this.recallRerankCoordinator.applyMmrToQmdResults(sectionId, results, caps);
|
|
17711
17202
|
}
|
|
17712
17203
|
|
|
17713
17204
|
private buildLastRecallBudgetSummary(options: {
|
|
@@ -18389,7 +17880,7 @@ export class Orchestrator {
|
|
|
18389
17880
|
* Updates are batched in memory and flushed during consolidation.
|
|
18390
17881
|
*/
|
|
18391
17882
|
trackMemoryAccess(memoryIds: string[]): void {
|
|
18392
|
-
if (!this.config.
|
|
17883
|
+
if (!resolveRecallEnhancementCapabilities(this.config).accessTracking) return;
|
|
18393
17884
|
|
|
18394
17885
|
const now = new Date().toISOString();
|
|
18395
17886
|
for (const id of memoryIds) {
|
|
@@ -18824,7 +18315,7 @@ export class Orchestrator {
|
|
|
18824
18315
|
]);
|
|
18825
18316
|
|
|
18826
18317
|
const queryIntent =
|
|
18827
|
-
this.config.
|
|
18318
|
+
resolveConversationContextCapabilities(this.config).intentRouting && prompt
|
|
18828
18319
|
? inferIntentFromText(prompt)
|
|
18829
18320
|
: null;
|
|
18830
18321
|
|
|
@@ -18892,7 +18383,7 @@ export class Orchestrator {
|
|
|
18892
18383
|
}
|
|
18893
18384
|
|
|
18894
18385
|
// Feedback bias (v2.2): apply small user-provided up/down vote adjustments.
|
|
18895
|
-
if (this.config.
|
|
18386
|
+
if (resolveRecallEnhancementCapabilities(this.config).feedback) {
|
|
18896
18387
|
const match = memory.path.match(/([^/]+)\.md$/);
|
|
18897
18388
|
const memoryId = match ? match[1] : null;
|
|
18898
18389
|
if (memoryId) {
|
|
@@ -18906,7 +18397,7 @@ export class Orchestrator {
|
|
|
18906
18397
|
}
|
|
18907
18398
|
|
|
18908
18399
|
// Negative examples (v2.2): apply a small penalty for memories repeatedly marked "not useful".
|
|
18909
|
-
if (this.config.
|
|
18400
|
+
if (resolvePipelineProcessingCapabilities(this.config).negativeExamples) {
|
|
18910
18401
|
const match = memory.path.match(/([^/]+)\.md$/);
|
|
18911
18402
|
const memoryId = match ? match[1] : null;
|
|
18912
18403
|
if (memoryId) {
|
|
@@ -18978,7 +18469,7 @@ export class Orchestrator {
|
|
|
18978
18469
|
// Gated by reinforcementRecallBoostEnabled (default false).
|
|
18979
18470
|
let reinforcementBoost = 0;
|
|
18980
18471
|
if (
|
|
18981
|
-
this.config.
|
|
18472
|
+
resolveRecallEnhancementCapabilities(this.config).reinforcementRecallBoost &&
|
|
18982
18473
|
typeof memory.frontmatter.reinforcement_count === "number" &&
|
|
18983
18474
|
memory.frontmatter.reinforcement_count > 0
|
|
18984
18475
|
) {
|