@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.
- package/dist/access-boundary.d.ts +6 -6
- package/dist/access-boundary.js +10 -10
- package/dist/access-cli.js +26 -26
- package/dist/access-http.d.ts +6 -6
- package/dist/access-http.js +15 -15
- package/dist/access-mcp.d.ts +6 -6
- package/dist/access-mcp.js +14 -14
- package/dist/access-operations-batch.js +12 -12
- package/dist/access-operations.d.ts +6 -6
- package/dist/access-operations.js +13 -13
- 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 +9 -9
- 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/buffer.js +2 -1
- package/dist/capabilities.d.ts +98 -1
- package/dist/capabilities.js +7 -1
- package/dist/{chunk-G54IDCD3.js → chunk-23PLOBWT.js} +30 -27
- package/dist/chunk-23PLOBWT.js.map +1 -0
- package/dist/{chunk-K3Y2SJTN.js → chunk-56LP72AD.js} +6 -6
- package/dist/{chunk-ZBZ4XULO.js → chunk-66RZ4EIT.js} +3 -3
- package/dist/{chunk-IPI45Q4S.js → chunk-75PDC7VT.js} +9 -8
- package/dist/chunk-75PDC7VT.js.map +1 -0
- package/dist/{chunk-GB2FZOWB.js → chunk-7B7IPPN5.js} +5 -5
- package/dist/{chunk-QNTLWH3T.js → chunk-A7INTIC5.js} +3 -3
- package/dist/{chunk-FLPKEMSO.js → chunk-AKKFHN3I.js} +2 -2
- package/dist/{chunk-AAIHDIMD.js → chunk-BHJAMSVC.js} +7 -7
- package/dist/{chunk-BXPG5AVX.js → chunk-BHQQVUEQ.js} +6 -6
- package/dist/{chunk-HFXZIF3V.js → chunk-BVG2IZOB.js} +3 -3
- package/dist/{chunk-NDKW67ER.js → chunk-C53JDEH3.js} +4 -4
- package/dist/{chunk-AESC5OQJ.js → chunk-CKHPUOAP.js} +2 -2
- package/dist/{chunk-OYHYBHHI.js → chunk-DS3FZ7GS.js} +4 -4
- package/dist/{chunk-UO6MPN77.js → chunk-EU543UDC.js} +10 -9
- package/dist/chunk-EU543UDC.js.map +1 -0
- package/dist/{chunk-PB5YON2G.js → chunk-IEWUPZ75.js} +659 -584
- package/dist/chunk-IEWUPZ75.js.map +1 -0
- package/dist/{chunk-E4LYJ7PY.js → chunk-IFCM6OLK.js} +6 -6
- package/dist/{chunk-5BOQNB2K.js → chunk-JQSP7YBJ.js} +3 -3
- package/dist/{chunk-2T2N7ISV.js → chunk-L3TYGUTH.js} +3 -3
- package/dist/{chunk-AB2E5DCN.js → chunk-MAU7DFCJ.js} +2 -2
- package/dist/{chunk-P6VLG7KE.js → chunk-N2O23WYA.js} +2 -2
- package/dist/{chunk-HYNHLBKA.js → chunk-OQQRWPFZ.js} +5 -2
- package/dist/chunk-OQQRWPFZ.js.map +1 -0
- package/dist/{chunk-IXGXALRV.js → chunk-QTJOORGN.js} +2 -2
- package/dist/{chunk-JWDE7OVJ.js → chunk-RJBW4EOU.js} +46 -2
- package/dist/{chunk-JWDE7OVJ.js.map → chunk-RJBW4EOU.js.map} +1 -1
- package/dist/{chunk-AE7NTZ75.js → chunk-TNLXCSXV.js} +3 -3
- package/dist/{chunk-NNJKEAP2.js → chunk-VRBBZH4U.js} +4 -4
- package/dist/{chunk-VOEVVIP3.js → chunk-W2FBVBQ4.js} +4 -4
- package/dist/{chunk-WNGXQMRI.js → chunk-WFNH4UDF.js} +5 -2
- package/dist/chunk-WFNH4UDF.js.map +1 -0
- package/dist/{chunk-CHJIWG2H.js → chunk-WVAEIKUY.js} +2 -2
- package/dist/{chunk-FF36YNKY.js → chunk-XIQAOGAA.js} +11 -10
- package/dist/chunk-XIQAOGAA.js.map +1 -0
- package/dist/{chunk-HLPYL6LC.js → chunk-Y5OOHER4.js} +14 -12
- package/dist/chunk-Y5OOHER4.js.map +1 -0
- package/dist/{chunk-GYICIICQ.js → chunk-YF2MMBKA.js} +9 -7
- package/dist/chunk-YF2MMBKA.js.map +1 -0
- package/dist/{chunk-IX4L7E5G.js → chunk-YXOXAQ5Y.js} +2 -2
- package/dist/{cli-BlEIWDQw.d.ts → cli-BQW2xKdb.d.ts} +2 -2
- package/dist/cli.d.ts +7 -7
- package/dist/cli.js +21 -21
- package/dist/compounding/engine.js +2 -2
- package/dist/contradiction/index.js +2 -2
- package/dist/{contradiction-scan-2B4AE4TI.js → contradiction-scan-CWO3LIWV.js} +3 -3
- package/dist/embedding-fallback.js +2 -2
- package/dist/entity-retrieval.js +2 -2
- package/dist/explicit-capture.d.ts +5 -5
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction.js +3 -3
- package/dist/{first-start-migration-2RQYDIZ7.js → first-start-migration-5DXD3TF2.js} +2 -2
- package/dist/index.d.ts +8 -8
- package/dist/index.js +32 -32
- package/dist/local-llm.js +2 -2
- package/dist/mcp-memory-inspector-app.d.ts +6 -6
- package/dist/namespaces/migrate.js +6 -6
- package/dist/namespaces/principal.js +2 -2
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.js +2 -2
- package/dist/operator-toolkit.js +11 -11
- package/dist/orchestration/compression-guideline-coordinator.js +2 -2
- package/dist/orchestration/maintenance.js +6 -6
- package/dist/{orchestrator-CJZ21ZiB.d.ts → orchestrator-BOAFN_Yr.d.ts} +186 -56
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +21 -21
- package/dist/search/embed-helper.js +2 -2
- package/dist/search/factory.js +3 -3
- package/dist/search/index.js +3 -3
- package/dist/summarizer.js +3 -3
- package/dist/{tier-stats-KPDIISG7.js → tier-stats-QID73BBB.js} +2 -2
- package/package.json +2 -2
- package/src/access-service.ts +7 -8
- package/src/buffer.ts +2 -1
- package/src/capabilities.test.ts +137 -0
- package/src/capabilities.ts +209 -0
- package/src/cli/creation-ledger-commands.ts +3 -3
- package/src/cli/research-status-commands.ts +4 -4
- package/src/cli.ts +9 -10
- package/src/compounding/engine.ts +7 -7
- package/src/correction/correction-access-wiring.ts +3 -2
- package/src/extraction.ts +8 -10
- package/src/operator-toolkit.ts +6 -7
- package/src/orchestration/conversation-index-coordinator.ts +344 -0
- package/src/orchestration/entity-synthesis-coordinator.ts +2 -1
- package/src/orchestration/maintenance.ts +5 -7
- package/src/orchestration/recall-rerank-coordinator.ts +425 -0
- package/src/orchestration/semantic-consolidation-coordinator.ts +2 -2
- package/src/orchestrator.ts +88 -602
- package/dist/chunk-FF36YNKY.js.map +0 -1
- package/dist/chunk-G54IDCD3.js.map +0 -1
- package/dist/chunk-GYICIICQ.js.map +0 -1
- package/dist/chunk-HLPYL6LC.js.map +0 -1
- package/dist/chunk-HYNHLBKA.js.map +0 -1
- package/dist/chunk-IPI45Q4S.js.map +0 -1
- package/dist/chunk-PB5YON2G.js.map +0 -1
- package/dist/chunk-UO6MPN77.js.map +0 -1
- package/dist/chunk-WNGXQMRI.js.map +0 -1
- /package/dist/{chunk-K3Y2SJTN.js.map → chunk-56LP72AD.js.map} +0 -0
- /package/dist/{chunk-ZBZ4XULO.js.map → chunk-66RZ4EIT.js.map} +0 -0
- /package/dist/{chunk-GB2FZOWB.js.map → chunk-7B7IPPN5.js.map} +0 -0
- /package/dist/{chunk-QNTLWH3T.js.map → chunk-A7INTIC5.js.map} +0 -0
- /package/dist/{chunk-FLPKEMSO.js.map → chunk-AKKFHN3I.js.map} +0 -0
- /package/dist/{chunk-AAIHDIMD.js.map → chunk-BHJAMSVC.js.map} +0 -0
- /package/dist/{chunk-BXPG5AVX.js.map → chunk-BHQQVUEQ.js.map} +0 -0
- /package/dist/{chunk-HFXZIF3V.js.map → chunk-BVG2IZOB.js.map} +0 -0
- /package/dist/{chunk-NDKW67ER.js.map → chunk-C53JDEH3.js.map} +0 -0
- /package/dist/{chunk-AESC5OQJ.js.map → chunk-CKHPUOAP.js.map} +0 -0
- /package/dist/{chunk-OYHYBHHI.js.map → chunk-DS3FZ7GS.js.map} +0 -0
- /package/dist/{chunk-E4LYJ7PY.js.map → chunk-IFCM6OLK.js.map} +0 -0
- /package/dist/{chunk-5BOQNB2K.js.map → chunk-JQSP7YBJ.js.map} +0 -0
- /package/dist/{chunk-2T2N7ISV.js.map → chunk-L3TYGUTH.js.map} +0 -0
- /package/dist/{chunk-AB2E5DCN.js.map → chunk-MAU7DFCJ.js.map} +0 -0
- /package/dist/{chunk-P6VLG7KE.js.map → chunk-N2O23WYA.js.map} +0 -0
- /package/dist/{chunk-IXGXALRV.js.map → chunk-QTJOORGN.js.map} +0 -0
- /package/dist/{chunk-AE7NTZ75.js.map → chunk-TNLXCSXV.js.map} +0 -0
- /package/dist/{chunk-NNJKEAP2.js.map → chunk-VRBBZH4U.js.map} +0 -0
- /package/dist/{chunk-VOEVVIP3.js.map → chunk-W2FBVBQ4.js.map} +0 -0
- /package/dist/{chunk-CHJIWG2H.js.map → chunk-WVAEIKUY.js.map} +0 -0
- /package/dist/{chunk-IX4L7E5G.js.map → chunk-YXOXAQ5Y.js.map} +0 -0
- /package/dist/{contradiction-scan-2B4AE4TI.js.map → contradiction-scan-CWO3LIWV.js.map} +0 -0
- /package/dist/{first-start-migration-2RQYDIZ7.js.map → first-start-migration-5DXD3TF2.js.map} +0 -0
- /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-
|
|
10
|
+
} from "./chunk-QTJOORGN.js";
|
|
11
11
|
import {
|
|
12
12
|
MaintenanceScheduler
|
|
13
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
196
|
+
} from "./chunk-WVAEIKUY.js";
|
|
197
197
|
import {
|
|
198
198
|
buildEntityRecallSection,
|
|
199
199
|
entityRecentTranscriptLookbackHours,
|
|
200
200
|
readRecentEntityTranscriptEntries
|
|
201
|
-
} from "./chunk-
|
|
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-
|
|
231
|
+
} from "./chunk-TNLXCSXV.js";
|
|
232
232
|
import {
|
|
233
233
|
NamespaceCatalog,
|
|
234
234
|
planNamespaceMaintenance
|
|
235
|
-
} from "./chunk-
|
|
235
|
+
} from "./chunk-BHQQVUEQ.js";
|
|
236
236
|
import {
|
|
237
237
|
NamespaceStorageRouter
|
|
238
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
336
|
+
} from "./chunk-7B7IPPN5.js";
|
|
337
337
|
import {
|
|
338
338
|
NamespaceSearchRouter
|
|
339
|
-
} from "./chunk-
|
|
339
|
+
} from "./chunk-66RZ4EIT.js";
|
|
340
340
|
import {
|
|
341
341
|
createConversationIndexRuntime,
|
|
342
342
|
createSearchBackend
|
|
343
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
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 =
|
|
2872
|
+
const dir = path4.join(memoryDir, TAXONOMY_DIR);
|
|
2424
2873
|
await mkdir2(dir, { recursive: true });
|
|
2425
|
-
const filePath =
|
|
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
|
|
2878
|
+
return path4.join(memoryDir, TAXONOMY_DIR);
|
|
2430
2879
|
}
|
|
2431
2880
|
function getTaxonomyFilePath(memoryDir) {
|
|
2432
|
-
return
|
|
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 ||
|
|
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 =
|
|
3795
|
+
const storageRoot = path5.resolve(storageDir);
|
|
3347
3796
|
const addCandidate = (candidate) => {
|
|
3348
|
-
const resolved =
|
|
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(
|
|
3805
|
+
addCandidate(path5.join(storageRoot, normalized));
|
|
3357
3806
|
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
3358
|
-
addCandidate(
|
|
3807
|
+
addCandidate(path5.join(storageRoot, "facts", normalized));
|
|
3359
3808
|
}
|
|
3360
3809
|
};
|
|
3361
|
-
if (
|
|
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
|
|
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 =
|
|
3756
|
-
const rel =
|
|
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(
|
|
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,
|
|
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:
|
|
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
|
|
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
|
|
4388
|
+
return path5.join(subtree, day, `${options.memoryId}.md`);
|
|
3940
4389
|
}
|
|
3941
|
-
return
|
|
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 =
|
|
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 =
|
|
4421
|
-
const resolvedMemoryDir =
|
|
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 =
|
|
4859
|
+
const resolvedNamespacesDir = path5.join(resolvedMemoryDir, "namespaces");
|
|
4425
4860
|
if (!isPathInsideStorageRoot(resolvedNamespacesDir, resolvedStorageDir)) return false;
|
|
4426
|
-
const rawRoot =
|
|
4427
|
-
const tokenRoot =
|
|
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 ===
|
|
4866
|
+
if (resolvedStorageDir === path5.resolve(this.config.memoryDir)) {
|
|
4432
4867
|
return record.namespace === normalizeNamespaceIdentity(this.config.defaultNamespace) ? 0 : 3;
|
|
4433
4868
|
}
|
|
4434
|
-
const leaf =
|
|
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 =
|
|
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 =
|
|
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.
|
|
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 ||
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
5691
|
+
if (resolveRecallAuxiliaryCapabilities(this.config).compactionReset) {
|
|
5246
5692
|
try {
|
|
5247
5693
|
const wsDir = this.config.workspaceDir || defaultWorkspaceDir();
|
|
5248
|
-
const files = await
|
|
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 =
|
|
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.
|
|
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-
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
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 =
|
|
6210
|
+
const archiveDir = path5.join(
|
|
5765
6211
|
this.config.workspaceDir,
|
|
5766
6212
|
hygiene.archiveDir
|
|
5767
6213
|
);
|
|
5768
|
-
const base =
|
|
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 =
|
|
5794
|
-
await mkdir3(
|
|
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 =
|
|
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
|
|
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 =
|
|
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 ||
|
|
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 =
|
|
6433
|
+
const hourlyBaseDir = path5.join(storage.dir, "summaries", "hourly");
|
|
5988
6434
|
try {
|
|
5989
|
-
const sessionKeys = await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
return
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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 (!
|
|
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 (
|
|
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) =>
|
|
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 =
|
|
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:
|
|
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 =
|
|
7629
|
+
const snapshotPath = path5.join(
|
|
7359
7630
|
options.storage.dir,
|
|
7360
7631
|
"state",
|
|
7361
7632
|
"last_graph_recall.json"
|
|
7362
7633
|
);
|
|
7363
|
-
await mkdir3(
|
|
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 =
|
|
7668
|
+
const snapshotPath = path5.join(
|
|
7398
7669
|
options.storage.dir,
|
|
7399
7670
|
"state",
|
|
7400
7671
|
"last_intent.json"
|
|
7401
7672
|
);
|
|
7402
|
-
await mkdir3(
|
|
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 =
|
|
7685
|
+
const snapshotPath = path5.join(
|
|
7415
7686
|
options.storage.dir,
|
|
7416
7687
|
"state",
|
|
7417
7688
|
"last_qmd_recall.json"
|
|
7418
7689
|
);
|
|
7419
|
-
await mkdir3(
|
|
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 =
|
|
7435
|
-
await mkdir3(
|
|
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
|
|
7718
|
+
return path5.join(this.config.memoryDir, "state");
|
|
7448
7719
|
}
|
|
7449
7720
|
if (namespace !== this.config.defaultNamespace) {
|
|
7450
|
-
return
|
|
7721
|
+
return path5.join(this.config.memoryDir, "namespaces", namespace, "state");
|
|
7451
7722
|
}
|
|
7452
|
-
const candidate =
|
|
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
|
|
7731
|
+
return path5.join(candidate, "state");
|
|
7461
7732
|
}
|
|
7462
7733
|
} catch {
|
|
7463
7734
|
}
|
|
7464
|
-
return
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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:
|
|
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.
|
|
8593
|
+
resolveRecallAuxiliaryCapabilities(this.config).entityRetrieval
|
|
8323
8594
|
))
|
|
8324
8595
|
return null;
|
|
8325
|
-
if (!this.config.
|
|
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.
|
|
8651
|
+
resolveRecallAuxiliaryCapabilities(this.config).knowledgeIndex
|
|
8381
8652
|
))
|
|
8382
8653
|
return null;
|
|
8383
|
-
if (!this.config.
|
|
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.
|
|
8730
|
+
resolvePresentationCapabilities(this.config).verbatimArtifacts === true
|
|
8460
8731
|
))
|
|
8461
8732
|
return [];
|
|
8462
|
-
if (!this.config.
|
|
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.
|
|
8930
|
+
if (!resolveConsolidationCapabilities(this.config).calibration || !this.isRecallSectionEnabled(
|
|
8660
8931
|
"calibration-rules",
|
|
8661
|
-
this.config.
|
|
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.
|
|
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.
|
|
9107
|
+
if (!resolveRecallAuxiliaryCapabilities(this.config).verifiedRecall || !this.isRecallSectionEnabled(
|
|
8837
9108
|
"verified-episodes",
|
|
8838
|
-
this.config.
|
|
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.
|
|
9187
|
+
if (!resolveRecallAuxiliaryCapabilities(this.config).semanticRuleVerification || !this.isRecallSectionEnabled(
|
|
8917
9188
|
"verified-rules",
|
|
8918
|
-
this.config.
|
|
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.
|
|
9266
|
+
if (!resolveCreationMemoryCapabilities(this.config).creationMemory || !resolveRecallAuxiliaryCapabilities(this.config).workProductRecall || !this.isRecallSectionEnabled(
|
|
8996
9267
|
"work-products",
|
|
8997
|
-
this.config.
|
|
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.
|
|
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.
|
|
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 =
|
|
9763
|
+
const signalPath = path5.join(
|
|
9493
9764
|
workspaceDir,
|
|
9494
9765
|
`.compaction-reset-signal-${safeSessionKey}`
|
|
9495
9766
|
);
|
|
9496
|
-
const bootPath =
|
|
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.
|
|
9774
|
-
) && this.config.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
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.
|
|
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.
|
|
14691
|
+
if (resolveConsolidationCapabilities(this.config).semanticConsolidation) {
|
|
14421
14692
|
try {
|
|
14422
|
-
const stateFilePath =
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 =
|
|
14926
|
-
const memoryRoot =
|
|
14927
|
-
const namespacesRoot =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
15262
|
-
const storageDir =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
15507
|
-
const resolvedPath =
|
|
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-
|
|
16516
|
+
//# sourceMappingURL=chunk-IEWUPZ75.js.map
|