@remnic/core 9.3.741 → 9.3.742
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 +2 -2
- package/dist/access-boundary.js +2 -2
- package/dist/access-cli.js +8 -8
- package/dist/access-http.d.ts +2 -2
- package/dist/access-http.js +6 -6
- package/dist/access-mcp.d.ts +2 -2
- package/dist/access-mcp.js +5 -5
- package/dist/access-operations-batch.js +3 -3
- package/dist/access-operations.d.ts +2 -2
- package/dist/access-operations.js +4 -4
- package/dist/{access-service-Cbk-Kv_A.d.ts → access-service-K8n12dXu.d.ts} +1 -1
- package/dist/access-service.d.ts +2 -2
- package/dist/access-service.js +1 -1
- package/dist/access-surface-catalog.d.ts +2 -2
- package/dist/{auto-sync-G3V5AN4C.js → auto-sync-XHSKKQAH.js} +2 -2
- package/dist/bootstrap.d.ts +1 -1
- package/dist/{chunk-DBVBV2RF.js → chunk-7ADHJ6SU.js} +3 -3
- package/dist/{chunk-VC7QELR6.js → chunk-7HUGLMSW.js} +346 -305
- package/dist/chunk-7HUGLMSW.js.map +1 -0
- package/dist/{chunk-QCO2QFLZ.js → chunk-AM7YKD7V.js} +3 -2
- package/dist/{chunk-QCO2QFLZ.js.map → chunk-AM7YKD7V.js.map} +1 -1
- package/dist/{chunk-OZECYVGI.js → chunk-C7LDPZJJ.js} +6 -4
- package/dist/{chunk-OZECYVGI.js.map → chunk-C7LDPZJJ.js.map} +1 -1
- package/dist/{chunk-4J53ZVPQ.js → chunk-EZZUNKMJ.js} +3 -3
- package/dist/{chunk-H3WSZWHK.js → chunk-GFJ5ABKS.js} +7 -6
- package/dist/chunk-GFJ5ABKS.js.map +1 -0
- package/dist/{chunk-LD6A7XG5.js → chunk-JMMSVRF4.js} +4 -4
- package/dist/{chunk-7Y5KKMIP.js → chunk-NHJYUIII.js} +3 -2
- package/dist/{chunk-7Y5KKMIP.js.map → chunk-NHJYUIII.js.map} +1 -1
- package/dist/{chunk-DGVM5SFL.js → chunk-PHRLJFWZ.js} +3 -2
- package/dist/{chunk-DGVM5SFL.js.map → chunk-PHRLJFWZ.js.map} +1 -1
- package/dist/{chunk-M5KLYHHI.js → chunk-QUNH5UZH.js} +2 -2
- package/dist/{chunk-3TUDKDTQ.js → chunk-UEIF7RWQ.js} +3 -3
- package/dist/{chunk-773DPI6T.js → chunk-V5RVMULT.js} +11 -8
- package/dist/chunk-V5RVMULT.js.map +1 -0
- package/dist/{chunk-XPOOY35N.js → chunk-W4H5A3TJ.js} +3 -2
- package/dist/chunk-W4H5A3TJ.js.map +1 -0
- package/dist/{cli-BQW2xKdb.d.ts → cli-BEOLy1xo.d.ts} +2 -2
- package/dist/cli.d.ts +3 -3
- package/dist/cli.js +8 -8
- package/dist/explicit-capture.d.ts +1 -1
- package/dist/graph-recall.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +13 -13
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +2 -2
- package/dist/lcm/summarizer.js +1 -1
- package/dist/mcp-memory-inspector-app.d.ts +2 -2
- package/dist/operator-toolkit.js +1 -1
- package/dist/{orchestrator-BOAFN_Yr.d.ts → orchestrator-Dr-1v5EL.d.ts} +58 -0
- package/dist/orchestrator.d.ts +1 -1
- package/dist/orchestrator.js +4 -4
- package/package.json +2 -2
- package/src/cli.ts +2 -1
- package/src/graph-recall.ts +2 -1
- package/src/lcm/engine.ts +9 -6
- package/src/lcm/summarizer.ts +2 -1
- package/src/operator-toolkit.ts +2 -1
- package/src/orchestration/qmd-result-resolver.ts +377 -0
- package/src/orchestrator.ts +14 -279
- package/src/procedural/procedure-stats.ts +2 -1
- package/src/wearables/pipeline.ts +5 -3
- package/dist/chunk-773DPI6T.js.map +0 -1
- package/dist/chunk-H3WSZWHK.js.map +0 -1
- package/dist/chunk-VC7QELR6.js.map +0 -1
- package/dist/chunk-XPOOY35N.js.map +0 -1
- /package/dist/{auto-sync-G3V5AN4C.js.map → auto-sync-XHSKKQAH.js.map} +0 -0
- /package/dist/{chunk-DBVBV2RF.js.map → chunk-7ADHJ6SU.js.map} +0 -0
- /package/dist/{chunk-4J53ZVPQ.js.map → chunk-EZZUNKMJ.js.map} +0 -0
- /package/dist/{chunk-LD6A7XG5.js.map → chunk-JMMSVRF4.js.map} +0 -0
- /package/dist/{chunk-M5KLYHHI.js.map → chunk-QUNH5UZH.js.map} +0 -0
- /package/dist/{chunk-3TUDKDTQ.js.map → chunk-UEIF7RWQ.js.map} +0 -0
package/src/orchestrator.ts
CHANGED
|
@@ -104,6 +104,7 @@ import { EntitySynthesisCoordinator } from "./orchestration/entity-synthesis-coo
|
|
|
104
104
|
import { RecallResultFormatter } from "./orchestration/recall-result-formatter.js";
|
|
105
105
|
import { ConversationIndexCoordinator } from "./orchestration/conversation-index-coordinator.js";
|
|
106
106
|
import { RecallRerankCoordinator } from "./orchestration/recall-rerank-coordinator.js";
|
|
107
|
+
import { QmdResultResolver, qmdCollectionPathParts, qmdResultPathCandidates } from "./orchestration/qmd-result-resolver.js";
|
|
107
108
|
export { hasIdentityRecoveryIntent, resolveEffectiveIdentityInjectionMode } from "./orchestration/recall-result-formatter.js";
|
|
108
109
|
import {
|
|
109
110
|
runLiveConnectorsOnce,
|
|
@@ -738,52 +739,6 @@ async function raceRecallAbort<T>(
|
|
|
738
739
|
}
|
|
739
740
|
}
|
|
740
741
|
|
|
741
|
-
function qmdCollectionPathParts(resultPath: string): {
|
|
742
|
-
collection: string;
|
|
743
|
-
relativePath: string;
|
|
744
|
-
} | null {
|
|
745
|
-
if (!resultPath || path.isAbsolute(resultPath)) return null;
|
|
746
|
-
const normalized = resultPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
747
|
-
const slashIndex = normalized.indexOf("/");
|
|
748
|
-
if (slashIndex <= 0 || slashIndex >= normalized.length - 1) return null;
|
|
749
|
-
const collection = normalized.slice(0, slashIndex);
|
|
750
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(collection)) return null;
|
|
751
|
-
return {
|
|
752
|
-
collection,
|
|
753
|
-
relativePath: normalized.slice(slashIndex + 1),
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function qmdResultPathCandidates(
|
|
758
|
-
storageDir: string,
|
|
759
|
-
resultPath: string,
|
|
760
|
-
): string[] {
|
|
761
|
-
const candidates = new Set<string>();
|
|
762
|
-
const storageRoot = path.resolve(storageDir);
|
|
763
|
-
const addCandidate = (candidate: string) => {
|
|
764
|
-
const resolved = path.resolve(candidate);
|
|
765
|
-
if (isPathInsideStorageRoot(storageRoot, resolved)) {
|
|
766
|
-
candidates.add(resolved);
|
|
767
|
-
}
|
|
768
|
-
};
|
|
769
|
-
const addRelativeCandidates = (relativePath: string) => {
|
|
770
|
-
const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
771
|
-
if (!normalized) return;
|
|
772
|
-
addCandidate(path.join(storageRoot, normalized));
|
|
773
|
-
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
774
|
-
addCandidate(path.join(storageRoot, "facts", normalized));
|
|
775
|
-
}
|
|
776
|
-
};
|
|
777
|
-
|
|
778
|
-
if (path.isAbsolute(resultPath)) {
|
|
779
|
-
addCandidate(resultPath);
|
|
780
|
-
} else {
|
|
781
|
-
addRelativeCandidates(resultPath);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
return [...candidates];
|
|
785
|
-
}
|
|
786
|
-
|
|
787
742
|
/** Maximum age (ms) before a compaction-reset signal file is considered stale and removed. */
|
|
788
743
|
const COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
|
|
789
744
|
const DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS = 10_000;
|
|
@@ -1885,6 +1840,7 @@ export class Orchestrator {
|
|
|
1885
1840
|
*/
|
|
1886
1841
|
readonly conversationIndexCoordinator: ConversationIndexCoordinator;
|
|
1887
1842
|
readonly recallRerankCoordinator: RecallRerankCoordinator;
|
|
1843
|
+
readonly qmdResultResolver: QmdResultResolver;
|
|
1888
1844
|
private heartbeatObserverChains = new Map<string, Promise<void>>();
|
|
1889
1845
|
private recentExtractionFingerprints = new Map<string, number>();
|
|
1890
1846
|
private readonly consolidationObservers = new Set<
|
|
@@ -2913,6 +2869,13 @@ export class Orchestrator {
|
|
|
2913
2869
|
fastChatCompletion: (messages, options) =>
|
|
2914
2870
|
this.fastChatCompletion(messages, options),
|
|
2915
2871
|
});
|
|
2872
|
+
this.qmdResultResolver = new QmdResultResolver({
|
|
2873
|
+
getConfig: () => this.config,
|
|
2874
|
+
storageFor: (namespace) => this.storageRouter.storageFor(namespace),
|
|
2875
|
+
storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
|
|
2876
|
+
qmdCollectionNamespaceFromPrefix: (prefix) => this.qmdCollectionNamespaceFromPrefix(prefix),
|
|
2877
|
+
namespaceFromPath: (p) => this.namespaceFromPath(p),
|
|
2878
|
+
});
|
|
2916
2879
|
|
|
2917
2880
|
this.sessionObserver = new SessionObserverState({
|
|
2918
2881
|
memoryDir: config.memoryDir,
|
|
@@ -16898,143 +16861,14 @@ export class Orchestrator {
|
|
|
16898
16861
|
return null;
|
|
16899
16862
|
}
|
|
16900
16863
|
|
|
16864
|
+
// Issue #1526 seam 11: QMD result-resolution methods moved to QmdResultResolver.
|
|
16865
|
+
// Thin delegation keeps the private API stable for callers + tests.
|
|
16901
16866
|
private async readQmdResultMemory(
|
|
16902
16867
|
resultPath: string,
|
|
16903
16868
|
fallbackStorage: StorageManager,
|
|
16904
16869
|
recallNamespaces: readonly string[] = [],
|
|
16905
16870
|
): Promise<MemoryFile | null> {
|
|
16906
|
-
|
|
16907
|
-
const storageDirFor = (storage: StorageManager): string | null =>
|
|
16908
|
-
typeof (storage as { dir?: unknown }).dir === "string" &&
|
|
16909
|
-
(storage as { dir?: string }).dir
|
|
16910
|
-
? (storage as { dir: string }).dir
|
|
16911
|
-
: null;
|
|
16912
|
-
const fallbackStorageDir = storageDirFor(fallbackStorage);
|
|
16913
|
-
const coldCollection = this.config.qmdColdCollection ?? "openclaw-engram-cold";
|
|
16914
|
-
if (parts && parts.collection === coldCollection) {
|
|
16915
|
-
const storages: StorageManager[] = [];
|
|
16916
|
-
const seenStorageDirs = new Set<string>();
|
|
16917
|
-
const addStorage = (storage: StorageManager): void => {
|
|
16918
|
-
const storageDir = storageDirFor(storage);
|
|
16919
|
-
const storageKey = storageDir
|
|
16920
|
-
? path.resolve(storageDir)
|
|
16921
|
-
: `storage-without-dir-${storages.length}`;
|
|
16922
|
-
if (seenStorageDirs.has(storageKey)) return;
|
|
16923
|
-
seenStorageDirs.add(storageKey);
|
|
16924
|
-
storages.push(storage);
|
|
16925
|
-
};
|
|
16926
|
-
|
|
16927
|
-
const fallbackNamespace =
|
|
16928
|
-
fallbackStorageDir !== null
|
|
16929
|
-
? this.storageDirNamespace(fallbackStorageDir)
|
|
16930
|
-
: this.config.defaultNamespace;
|
|
16931
|
-
if (
|
|
16932
|
-
recallNamespaces.length === 0 ||
|
|
16933
|
-
!resolveNamespaceCapabilities(this.config).namespaces ||
|
|
16934
|
-
recallNamespaces.includes(fallbackNamespace)
|
|
16935
|
-
) {
|
|
16936
|
-
addStorage(fallbackStorage);
|
|
16937
|
-
}
|
|
16938
|
-
|
|
16939
|
-
if (recallNamespaces.length > 0) {
|
|
16940
|
-
for (const namespace of recallNamespaces) {
|
|
16941
|
-
try {
|
|
16942
|
-
addStorage(await this.storageRouter.storageFor(namespace));
|
|
16943
|
-
} catch (err) {
|
|
16944
|
-
log.debug("qmd cold result namespace storage lookup skipped", {
|
|
16945
|
-
path: resultPath,
|
|
16946
|
-
namespace,
|
|
16947
|
-
error: (err as Error).message,
|
|
16948
|
-
});
|
|
16949
|
-
}
|
|
16950
|
-
}
|
|
16951
|
-
}
|
|
16952
|
-
|
|
16953
|
-
for (const storage of storages) {
|
|
16954
|
-
const storageDir = storageDirFor(storage);
|
|
16955
|
-
if (!storageDir) {
|
|
16956
|
-
const memory = await storage.readMemoryByPath(resultPath);
|
|
16957
|
-
if (memory) return memory;
|
|
16958
|
-
continue;
|
|
16959
|
-
}
|
|
16960
|
-
try {
|
|
16961
|
-
const coldRoot = path.join(storageDir, "cold");
|
|
16962
|
-
for (const candidate of qmdResultPathCandidates(
|
|
16963
|
-
coldRoot,
|
|
16964
|
-
parts.relativePath,
|
|
16965
|
-
)) {
|
|
16966
|
-
const memory = await storage.readMemoryByPath(candidate);
|
|
16967
|
-
if (memory) return memory;
|
|
16968
|
-
}
|
|
16969
|
-
} catch (err) {
|
|
16970
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
16971
|
-
log.debug("qmd cold result path lookup failed open", {
|
|
16972
|
-
path: resultPath,
|
|
16973
|
-
collection: coldCollection,
|
|
16974
|
-
error: (err as Error).message,
|
|
16975
|
-
});
|
|
16976
|
-
}
|
|
16977
|
-
}
|
|
16978
|
-
return null;
|
|
16979
|
-
}
|
|
16980
|
-
const collectionNamespace = parts
|
|
16981
|
-
? this.qmdCollectionNamespaceFromPrefix(parts.collection)
|
|
16982
|
-
: null;
|
|
16983
|
-
|
|
16984
|
-
if (parts && collectionNamespace) {
|
|
16985
|
-
try {
|
|
16986
|
-
const collectionStorage =
|
|
16987
|
-
await this.storageRouter.storageFor(collectionNamespace);
|
|
16988
|
-
for (const candidate of qmdResultPathCandidates(
|
|
16989
|
-
collectionStorage.dir,
|
|
16990
|
-
parts.relativePath,
|
|
16991
|
-
)) {
|
|
16992
|
-
const memory = await collectionStorage.readMemoryByPath(candidate);
|
|
16993
|
-
if (memory) return memory;
|
|
16994
|
-
}
|
|
16995
|
-
return null;
|
|
16996
|
-
} catch (err) {
|
|
16997
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
16998
|
-
log.debug("qmd result namespace path lookup failed open", {
|
|
16999
|
-
path: resultPath,
|
|
17000
|
-
namespace: collectionNamespace,
|
|
17001
|
-
error: (err as Error).message,
|
|
17002
|
-
});
|
|
17003
|
-
return null;
|
|
17004
|
-
}
|
|
17005
|
-
}
|
|
17006
|
-
|
|
17007
|
-
if (path.isAbsolute(resultPath)) {
|
|
17008
|
-
if (!fallbackStorageDir) {
|
|
17009
|
-
return await fallbackStorage.readMemoryByPath(resultPath);
|
|
17010
|
-
}
|
|
17011
|
-
const ownerStorage = await this.storageForAbsoluteQmdResultPath(
|
|
17012
|
-
resultPath,
|
|
17013
|
-
fallbackStorage,
|
|
17014
|
-
recallNamespaces,
|
|
17015
|
-
);
|
|
17016
|
-
if (!ownerStorage) return null;
|
|
17017
|
-
for (const candidate of qmdResultPathCandidates(
|
|
17018
|
-
ownerStorage.dir,
|
|
17019
|
-
resultPath,
|
|
17020
|
-
)) {
|
|
17021
|
-
const memory = await ownerStorage.storage.readMemoryByPath(candidate);
|
|
17022
|
-
if (memory) return memory;
|
|
17023
|
-
}
|
|
17024
|
-
return null;
|
|
17025
|
-
}
|
|
17026
|
-
|
|
17027
|
-
if (!fallbackStorageDir) {
|
|
17028
|
-
return await fallbackStorage.readMemoryByPath(resultPath);
|
|
17029
|
-
}
|
|
17030
|
-
for (const candidate of qmdResultPathCandidates(
|
|
17031
|
-
fallbackStorageDir,
|
|
17032
|
-
resultPath,
|
|
17033
|
-
)) {
|
|
17034
|
-
const memory = await fallbackStorage.readMemoryByPath(candidate);
|
|
17035
|
-
if (memory) return memory;
|
|
17036
|
-
}
|
|
17037
|
-
return null;
|
|
16871
|
+
return this.qmdResultResolver.readQmdResultMemory(resultPath, fallbackStorage, recallNamespaces);
|
|
17038
16872
|
}
|
|
17039
16873
|
|
|
17040
16874
|
private async resolveColdQmdResultForRecall(
|
|
@@ -17042,40 +16876,7 @@ export class Orchestrator {
|
|
|
17042
16876
|
fallbackStorage: StorageManager,
|
|
17043
16877
|
recallNamespaces: readonly string[] = [],
|
|
17044
16878
|
): Promise<{ namespace: string; result: QmdSearchResult } | null> {
|
|
17045
|
-
|
|
17046
|
-
result.path,
|
|
17047
|
-
fallbackStorage,
|
|
17048
|
-
recallNamespaces,
|
|
17049
|
-
);
|
|
17050
|
-
if (!memory) return null;
|
|
17051
|
-
|
|
17052
|
-
let ownerNamespace: string | null = null;
|
|
17053
|
-
if (path.isAbsolute(memory.path)) {
|
|
17054
|
-
const ownerStorage = await this.storageForAbsoluteQmdResultPath(
|
|
17055
|
-
memory.path,
|
|
17056
|
-
fallbackStorage,
|
|
17057
|
-
recallNamespaces,
|
|
17058
|
-
);
|
|
17059
|
-
ownerNamespace = ownerStorage?.namespace ?? null;
|
|
17060
|
-
if (!ownerNamespace && resolveNamespaceCapabilities(this.config).namespaces) return null;
|
|
17061
|
-
}
|
|
17062
|
-
ownerNamespace ??= this.namespaceFromPath(memory.path);
|
|
17063
|
-
if (
|
|
17064
|
-
recallNamespaces.length > 0 &&
|
|
17065
|
-
!recallNamespaces.includes(ownerNamespace)
|
|
17066
|
-
) {
|
|
17067
|
-
return null;
|
|
17068
|
-
}
|
|
17069
|
-
|
|
17070
|
-
return {
|
|
17071
|
-
namespace: ownerNamespace,
|
|
17072
|
-
result: {
|
|
17073
|
-
...result,
|
|
17074
|
-
docid: result.docid || memory.frontmatter.id,
|
|
17075
|
-
path: memory.path,
|
|
17076
|
-
snippet: result.snippet || memory.content.slice(0, 400),
|
|
17077
|
-
},
|
|
17078
|
-
};
|
|
16879
|
+
return this.qmdResultResolver.resolveColdQmdResultForRecall(result, fallbackStorage, recallNamespaces);
|
|
17079
16880
|
}
|
|
17080
16881
|
|
|
17081
16882
|
private async storageForAbsoluteQmdResultPath(
|
|
@@ -17083,73 +16884,7 @@ export class Orchestrator {
|
|
|
17083
16884
|
fallbackStorage: StorageManager,
|
|
17084
16885
|
recallNamespaces: readonly string[] = [],
|
|
17085
16886
|
): Promise<{ storage: StorageManager; dir: string; namespace: string } | null> {
|
|
17086
|
-
|
|
17087
|
-
const memoryRoot = path.resolve(this.config.memoryDir);
|
|
17088
|
-
const namespacesRoot = path.join(memoryRoot, "namespaces");
|
|
17089
|
-
const fallbackStorageDir =
|
|
17090
|
-
typeof (fallbackStorage as { dir?: unknown }).dir === "string" &&
|
|
17091
|
-
(fallbackStorage as { dir?: string }).dir
|
|
17092
|
-
? (fallbackStorage as { dir: string }).dir
|
|
17093
|
-
: null;
|
|
17094
|
-
const matches: Array<{ storage: StorageManager; dir: string; namespace: string }> = [];
|
|
17095
|
-
const seenDirs = new Set<string>();
|
|
17096
|
-
|
|
17097
|
-
const maybeAddStorage = (storage: StorageManager, namespace: string) => {
|
|
17098
|
-
const storageDir =
|
|
17099
|
-
typeof (storage as { dir?: unknown }).dir === "string" &&
|
|
17100
|
-
(storage as { dir?: string }).dir
|
|
17101
|
-
? (storage as { dir: string }).dir
|
|
17102
|
-
: null;
|
|
17103
|
-
if (!storageDir) return;
|
|
17104
|
-
const candidateRoot = path.resolve(storageDir);
|
|
17105
|
-
if (seenDirs.has(candidateRoot)) return;
|
|
17106
|
-
if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
|
|
17107
|
-
if (
|
|
17108
|
-
candidateRoot === memoryRoot &&
|
|
17109
|
-
isPathInsideStorageRoot(namespacesRoot, resolvedPath)
|
|
17110
|
-
) {
|
|
17111
|
-
return;
|
|
17112
|
-
}
|
|
17113
|
-
seenDirs.add(candidateRoot);
|
|
17114
|
-
matches.push({ storage, dir: candidateRoot, namespace });
|
|
17115
|
-
};
|
|
17116
|
-
|
|
17117
|
-
const fallbackNamespace =
|
|
17118
|
-
fallbackStorageDir !== null
|
|
17119
|
-
? this.storageDirNamespace(fallbackStorageDir)
|
|
17120
|
-
: this.config.defaultNamespace;
|
|
17121
|
-
maybeAddStorage(fallbackStorage, fallbackNamespace);
|
|
17122
|
-
|
|
17123
|
-
const candidateNamespaces = new Set<string>();
|
|
17124
|
-
candidateNamespaces.add(this.config.defaultNamespace);
|
|
17125
|
-
candidateNamespaces.add(this.config.sharedNamespace);
|
|
17126
|
-
for (const ns of recallNamespaces) {
|
|
17127
|
-
candidateNamespaces.add(ns);
|
|
17128
|
-
}
|
|
17129
|
-
if (isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
17130
|
-
const relativeToNamespaces = path.relative(namespacesRoot, resolvedPath);
|
|
17131
|
-
const [namespaceSegment] = relativeToNamespaces.split(/[\\/]/);
|
|
17132
|
-
if (namespaceSegment) {
|
|
17133
|
-
candidateNamespaces.add(
|
|
17134
|
-
namespaceIdentityFromToken(namespaceSegment) ?? namespaceSegment,
|
|
17135
|
-
);
|
|
17136
|
-
}
|
|
17137
|
-
}
|
|
17138
|
-
for (const policy of this.config.namespacePolicies ?? []) {
|
|
17139
|
-
candidateNamespaces.add(policy.name);
|
|
17140
|
-
}
|
|
17141
|
-
|
|
17142
|
-
for (const ns of candidateNamespaces) {
|
|
17143
|
-
if (!ns) continue;
|
|
17144
|
-
try {
|
|
17145
|
-
maybeAddStorage(await this.storageRouter.storageFor(ns), ns);
|
|
17146
|
-
} catch {
|
|
17147
|
-
continue;
|
|
17148
|
-
}
|
|
17149
|
-
}
|
|
17150
|
-
|
|
17151
|
-
matches.sort((a, b) => b.dir.length - a.dir.length);
|
|
17152
|
-
return matches[0] ?? null;
|
|
16887
|
+
return this.qmdResultResolver.storageForAbsoluteQmdResultPath(resultPath, fallbackStorage, recallNamespaces);
|
|
17153
16888
|
}
|
|
17154
16889
|
|
|
17155
16890
|
// Issue #1526: recall-rerank methods moved to RecallRerankCoordinator.
|
|
@@ -180,6 +180,7 @@ export async function computeProcedureStats(options: {
|
|
|
180
180
|
*/
|
|
181
181
|
export function formatProcedureStatsText(report: ProcedureStatsReport): string {
|
|
182
182
|
const { counts, recent, config } = report;
|
|
183
|
+
const { autoPromoteEnabled } = config;
|
|
183
184
|
const lines: string[] = [];
|
|
184
185
|
lines.push(`Procedural memory stats (schema v${report.schemaVersion})`);
|
|
185
186
|
lines.push(` generated: ${report.generatedAt}`);
|
|
@@ -189,7 +190,7 @@ export function formatProcedureStatsText(report: ProcedureStatsReport): string {
|
|
|
189
190
|
lines.push(` minOccurrences: ${config.minOccurrences}`);
|
|
190
191
|
lines.push(` successFloor: ${config.successFloor}`);
|
|
191
192
|
lines.push(` autoPromoteOccurrences: ${config.autoPromoteOccurrences}`);
|
|
192
|
-
lines.push(` autoPromoteEnabled: ${
|
|
193
|
+
lines.push(` autoPromoteEnabled: ${autoPromoteEnabled}`);
|
|
193
194
|
lines.push(` lookbackDays: ${config.lookbackDays}`);
|
|
194
195
|
lines.push(` recallMaxProcedures: ${config.recallMaxProcedures}`);
|
|
195
196
|
lines.push("");
|
|
@@ -260,6 +260,7 @@ function cleanDay(
|
|
|
260
260
|
userRedaction: RegExp[],
|
|
261
261
|
correctionRules: CompiledCorrectionRule[],
|
|
262
262
|
): CleanedDay {
|
|
263
|
+
const { offTheRecordEnabled, redactionEnabled } = config;
|
|
263
264
|
const out: CleanedDay = {
|
|
264
265
|
conversations: [],
|
|
265
266
|
segmentsKept: 0,
|
|
@@ -269,7 +270,7 @@ function cleanDay(
|
|
|
269
270
|
};
|
|
270
271
|
for (const conversation of raw) {
|
|
271
272
|
let current = conversation;
|
|
272
|
-
if (
|
|
273
|
+
if (offTheRecordEnabled) {
|
|
273
274
|
const otr = applyOffTheRecord(current);
|
|
274
275
|
current = otr.conversation;
|
|
275
276
|
out.segmentsDropped += otr.droppedSegments;
|
|
@@ -280,7 +281,7 @@ function cleanDay(
|
|
|
280
281
|
|
|
281
282
|
const segments = current.segments.map((segment) => {
|
|
282
283
|
let text = segment.text;
|
|
283
|
-
if (
|
|
284
|
+
if (redactionEnabled) {
|
|
284
285
|
const redacted = redactText(text, userRedaction);
|
|
285
286
|
text = redacted.text;
|
|
286
287
|
out.redactions += redacted.redactions;
|
|
@@ -483,7 +484,8 @@ export async function syncWearableSource(
|
|
|
483
484
|
// the day to re-run on the next sync (Cursor review on PR
|
|
484
485
|
// #1462).
|
|
485
486
|
passClean = generated.completed;
|
|
486
|
-
|
|
487
|
+
const { digestEnabled } = config;
|
|
488
|
+
if (digestEnabled) {
|
|
487
489
|
const wrote = await writeDailyDigestMemory(
|
|
488
490
|
connector.id,
|
|
489
491
|
date,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lcm/engine.ts"],"sourcesContent":["import type Database from \"better-sqlite3\";\nimport { openLcmDatabase, ensureLcmStateDir } from \"./schema.js\";\nimport { LcmArchive, type LcmStructuredRecallMatch } from \"./archive.js\";\nimport { LcmDag } from \"./dag.js\";\nimport { LcmSummarizer, type SummarizeFn } from \"./summarizer.js\";\nimport { assembleCompressedHistory, type LcmRecallConfig } from \"./recall.js\";\nimport { LcmWorkQueue, type LcmObserveMessage } from \"./queue.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { log } from \"../logger.js\";\n\nexport interface LcmEngineConfig {\n enabled: boolean;\n leafBatchSize: number;\n rollupFanIn: number;\n freshTailTurns: number;\n maxDepth: number;\n deterministicMaxTokens: number;\n archiveRetentionDays: number;\n recallBudgetShare: number;\n observeConcurrency: number;\n telemetryPrefilterEnabled: boolean;\n messagePartsEnabled: boolean;\n messagePartsRecallMaxResults: number;\n}\n\nfunction positiveInteger(value: unknown, fallback: number, min = 1): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n return Math.max(min, Math.floor(value));\n}\n\nexport function extractLcmConfig(cfg: PluginConfig): LcmEngineConfig {\n return {\n enabled: (cfg as any).lcmEnabled === true,\n leafBatchSize: positiveInteger((cfg as any).lcmLeafBatchSize, 8),\n rollupFanIn: positiveInteger((cfg as any).lcmRollupFanIn, 4, 2),\n freshTailTurns: positiveInteger((cfg as any).lcmFreshTailTurns, 16),\n maxDepth: positiveInteger((cfg as any).lcmMaxDepth, 5),\n deterministicMaxTokens: positiveInteger((cfg as any).lcmDeterministicMaxTokens, 512),\n archiveRetentionDays: positiveInteger((cfg as any).lcmArchiveRetentionDays, 90, 0),\n recallBudgetShare: (cfg as any).lcmRecallBudgetShare ?? 0.15,\n observeConcurrency:\n typeof (cfg as any).lcmObserveConcurrency === \"number\" &&\n Number.isFinite((cfg as any).lcmObserveConcurrency)\n ? Math.max(1, Math.floor((cfg as any).lcmObserveConcurrency))\n : 1,\n telemetryPrefilterEnabled: (cfg as any).lcmTelemetryPrefilterEnabled !== false,\n messagePartsEnabled: (cfg as any).messagePartsEnabled === true,\n messagePartsRecallMaxResults:\n typeof (cfg as any).messagePartsRecallMaxResults === \"number\"\n ? Math.max(0, Math.floor((cfg as any).messagePartsRecallMaxResults))\n : 6,\n };\n}\n\nfunction normalizeLcmSessionId(sessionId: string): string {\n return sessionId.trim();\n}\n\nfunction normalizeOptionalLcmSessionId(sessionId?: string): string | undefined {\n if (sessionId === undefined) {\n return undefined;\n }\n const normalized = normalizeLcmSessionId(sessionId);\n return normalized.length > 0 ? normalized : undefined;\n}\n\nconst EMPTY_LCM_STATS = {\n totalMessages: 0,\n totalSummaryNodes: 0,\n maxDepth: -1,\n} as const;\n\nexport class LcmEngine {\n private db: Database.Database | null = null;\n private archive: LcmArchive | null = null;\n private dag: LcmDag | null = null;\n private summarizer: LcmSummarizer | null = null;\n private observeQueue: LcmWorkQueue | null = null;\n private closed = false;\n private readonly config: LcmEngineConfig;\n private readonly memoryDir: string;\n private initPromise: Promise<void> | null = null;\n private readonly pendingObserveInitCounts = new Map<string, number>();\n private readonly pendingObserveInitWaiters = new Map<string, Array<() => void>>();\n private readonly pendingObserveInitIdleWaiters: Array<() => void> = [];\n\n constructor(\n pluginConfig: PluginConfig,\n private readonly summarizeFn: SummarizeFn,\n ) {\n this.config = extractLcmConfig(pluginConfig);\n this.memoryDir = pluginConfig.memoryDir;\n }\n\n get enabled(): boolean {\n return this.config.enabled;\n }\n\n /** Lazy init — open database on first use. */\n async ensureInitialized(): Promise<void> {\n if (this.closed) return;\n if (this.db) return;\n if (this.initPromise) {\n await this.initPromise;\n return;\n }\n this.initPromise = this.doInit().catch((err) => {\n // Reset so next call retries instead of caching the failure\n this.initPromise = null;\n throw err;\n });\n await this.initPromise;\n }\n\n private async doInit(): Promise<void> {\n await ensureLcmStateDir(this.memoryDir);\n const db = openLcmDatabase(this.memoryDir);\n const archive = new LcmArchive(db);\n const dag = new LcmDag(db);\n const summarizer = new LcmSummarizer(\n archive,\n dag,\n this.summarizeFn,\n {\n leafBatchSize: this.config.leafBatchSize,\n rollupFanIn: this.config.rollupFanIn,\n maxDepth: this.config.maxDepth,\n deterministicMaxTokens: this.config.deterministicMaxTokens,\n telemetryPrefilterEnabled: this.config.telemetryPrefilterEnabled,\n },\n );\n const observeQueue = new LcmWorkQueue({\n concurrency: this.config.observeConcurrency,\n worker: async (sessionId, messages) => {\n await this.processObserveMessages(sessionId, messages);\n },\n hooks: {\n onJobStart: ({ sessionId, depth, inFlight, waitMs }) => {\n log.debug(\n `LCM observe queue start: session=${sessionId}, depth=${depth}, inFlight=${inFlight}, wait=${waitMs}ms`,\n );\n },\n onJobFinish: ({\n sessionId,\n depth,\n inFlight,\n waitMs,\n runMs,\n totalMs,\n error,\n }) => {\n if (error) {\n log.error(\n `LCM observe queue failure: session=${sessionId}, depth=${depth}, inFlight=${inFlight}, wait=${waitMs}ms, run=${runMs}ms, total=${totalMs}ms, error=${error}`,\n );\n return;\n }\n\n log.debug(\n `LCM observe queue finish: session=${sessionId}, depth=${depth}, inFlight=${inFlight}, wait=${waitMs}ms, run=${runMs}ms, total=${totalMs}ms`,\n );\n },\n },\n });\n\n if (this.closed) {\n db.close();\n return;\n }\n\n this.db = db;\n this.archive = archive;\n this.dag = dag;\n this.summarizer = summarizer;\n this.observeQueue = observeQueue;\n log.info(\"LCM engine initialized\");\n }\n\n /**\n * Enqueue messages from agent_end hook.\n * The queue worker performs the archive append and incremental summarization.\n */\n async observeMessages(\n sessionId: string,\n messages: LcmObserveMessage[],\n ): Promise<void> {\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n this.enqueueObserveMessages(normalizedSessionId, messages);\n }\n\n /** Enqueue an observe job without waiting for worker completion. */\n enqueueObserveMessages(\n sessionId: string,\n messages: LcmObserveMessage[],\n ): void {\n if (!this.config.enabled || this.closed) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n if (messages.length === 0) return;\n\n this.reservePendingObserveInit(normalizedSessionId);\n\n void this.ensureInitialized()\n .then(() => {\n if (this.closed || !this.observeQueue) return;\n this.observeQueue.enqueue(normalizedSessionId, messages);\n log.debug(\n `LCM observe enqueued: session=${normalizedSessionId}, depth=${this.observeQueue.depth}, inFlight=${this.observeQueue.inFlightCount}`,\n );\n })\n .catch((err) => {\n if (this.closed) return;\n log.error(`LCM observe enqueue initialization error: ${err}`);\n })\n .finally(() => {\n this.releasePendingObserveInit(normalizedSessionId);\n });\n }\n\n private async processObserveMessages(\n sessionId: string,\n messages: LcmObserveMessage[],\n ): Promise<void> {\n if (this.closed) return;\n await this.ensureInitialized();\n if (this.closed || !this.archive || !this.summarizer) return;\n\n if (messages.length === 0) return;\n\n const currentMax = this.archive.getMaxTurnIndex(sessionId);\n const newMessages = messages.map((m, i) => ({\n turnIndex: currentMax + 1 + i,\n role: m.role,\n content: m.content,\n parts: this.config.messagePartsEnabled ? m.parts : undefined,\n rawContent: this.config.messagePartsEnabled ? m.rawContent : undefined,\n sourceFormat: this.config.messagePartsEnabled ? m.sourceFormat : undefined,\n }));\n\n this.archive.appendMessages(sessionId, newMessages, {\n messagePartsEnabled: this.config.messagePartsEnabled,\n });\n\n // Trigger incremental summarization inside the worker, after append.\n try {\n await this.summarizer.summarizeIncremental(sessionId);\n } catch (err) {\n log.debug(`LCM incremental summarization error: ${err}`);\n }\n }\n\n get observeQueueDepth(): number {\n return this.observeQueue?.depth ?? 0;\n }\n\n get observeQueueInFlightCount(): number {\n return this.observeQueue?.inFlightCount ?? 0;\n }\n\n async waitForObserveQueueIdle(): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n await this.ensureInitialized();\n if (this.closed) return;\n await this.waitForPendingObserveInitIdle();\n await this.observeQueue?.whenIdle();\n }\n\n async waitForSessionObserveIdle(sessionId: string): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n if (this.closed) return;\n await this.waitForPendingObserveInitIdle(normalizedSessionId);\n await this.observeQueue?.whenSessionIdle(normalizedSessionId);\n }\n\n private reservePendingObserveInit(sessionId: string): void {\n const count = this.pendingObserveInitCounts.get(sessionId) ?? 0;\n this.pendingObserveInitCounts.set(sessionId, count + 1);\n }\n\n private releasePendingObserveInit(sessionId: string): void {\n const count = this.pendingObserveInitCounts.get(sessionId);\n if (!count) return;\n\n if (count === 1) {\n this.pendingObserveInitCounts.delete(sessionId);\n const waiters = this.pendingObserveInitWaiters.get(sessionId) ?? [];\n this.pendingObserveInitWaiters.delete(sessionId);\n for (const resolve of waiters) resolve();\n if (this.pendingObserveInitCounts.size === 0) {\n const idleWaiters = this.pendingObserveInitIdleWaiters.splice(\n 0,\n this.pendingObserveInitIdleWaiters.length,\n );\n for (const resolve of idleWaiters) resolve();\n }\n return;\n }\n\n this.pendingObserveInitCounts.set(sessionId, count - 1);\n }\n\n private async waitForPendingObserveInitIdle(sessionId?: string): Promise<void> {\n if (sessionId) {\n if (!this.pendingObserveInitCounts.has(sessionId)) return;\n await new Promise<void>((resolve) => {\n const waiters = this.pendingObserveInitWaiters.get(sessionId) ?? [];\n waiters.push(resolve);\n this.pendingObserveInitWaiters.set(sessionId, waiters);\n });\n return;\n }\n\n if (this.pendingObserveInitCounts.size === 0) return;\n await new Promise<void>((resolve) => {\n this.pendingObserveInitIdleWaiters.push(resolve);\n });\n }\n\n /** Build the compressed history recall section for a session. */\n async assembleRecall(\n sessionId: string,\n budgetChars: number,\n ): Promise<string> {\n if (!this.config.enabled) return \"\";\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return \"\";\n await this.ensureInitialized();\n\n const effectiveBudget = Math.ceil(\n budgetChars * this.config.recallBudgetShare,\n );\n if (effectiveBudget <= 0) return \"\";\n\n return assembleCompressedHistory(this.dag!, this.archive!, normalizedSessionId, {\n freshTailTurns: this.config.freshTailTurns,\n budgetChars: effectiveBudget,\n });\n }\n\n async searchStructuredParts(\n sessionId: string,\n query: string,\n limit = this.config.messagePartsRecallMaxResults,\n ): Promise<LcmStructuredRecallMatch[]> {\n if (!this.config.enabled || !this.config.messagePartsEnabled) return [];\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return [];\n await this.ensureInitialized();\n if (!this.archive) return [];\n return this.archive.searchStructuredParts(query, limit, normalizedSessionId);\n }\n\n formatStructuredRecall(\n matches: LcmStructuredRecallMatch[],\n budgetChars: number,\n ): string {\n if (matches.length === 0 || budgetChars <= 0) return \"\";\n const lines: string[] = [];\n let used = \"## Structured Session Matches\\n\\n\".length;\n for (const match of matches) {\n const label = match.file_path\n ? `${match.kind} ${match.file_path}`\n : match.tool_name\n ? `${match.kind} ${match.tool_name}`\n : match.kind;\n const excerpt = match.content.replace(/\\s+/g, \" \").slice(0, 220);\n const line = `- turn ${match.turn_index} (${match.role}): ${label} — ${excerpt}`;\n if (used + line.length + 1 > budgetChars) break;\n lines.push(line);\n used += line.length + 1;\n }\n return lines.length > 0 ? `## Structured Session Matches\\n\\n${lines.join(\"\\n\")}` : \"\";\n }\n\n /** Flush pending summaries before compaction (called from before_compaction hook). */\n async preCompactionFlush(sessionId: string): Promise<void> {\n if (!this.config.enabled) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n await this.waitForSessionObserveIdle(normalizedSessionId);\n\n try {\n await this.summarizer!.summarizeIncremental(normalizedSessionId);\n } catch (err) {\n log.debug(`LCM pre-compaction flush error: ${err}`);\n }\n }\n\n /** Record a compaction event with real token counts (called from after_compaction hook). */\n async recordCompaction(\n sessionId: string,\n tokensBefore: number,\n tokensAfter: number,\n ): Promise<void> {\n if (!this.config.enabled) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n await this.waitForSessionObserveIdle(normalizedSessionId);\n\n const maxTurn = this.archive!.getMaxTurnIndex(normalizedSessionId);\n\n this.dag!.recordCompaction(normalizedSessionId, maxTurn, tokensBefore, tokensAfter);\n log.info(\n `LCM compaction recorded: session=${normalizedSessionId}, turn=${maxTurn}, tokens ${tokensBefore}→${tokensAfter}`,\n );\n }\n\n /** Verify archive coverage after compaction. */\n async verifyPostCompaction(sessionId: string): Promise<void> {\n if (!this.config.enabled) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n\n const msgCount = this.archive!.getMessageCount(normalizedSessionId);\n const nodeCount = this.dag!.getNodeCount(normalizedSessionId);\n log.debug(\n `LCM post-compaction verify: session=${normalizedSessionId}, messages=${msgCount}, summaryNodes=${nodeCount}`,\n );\n }\n\n // ── MCP Tool implementations ──\n\n /** Search across all conversation history via FTS (snippet mode). */\n async searchContext(\n query: string,\n limit: number,\n sessionId?: string,\n sessionPrefix?: string,\n ): Promise<\n Array<{\n turn_index: number;\n role: string;\n snippet: string;\n session_id: string;\n }>\n > {\n if (!this.config.enabled) return [];\n const normalizedSessionId = normalizeOptionalLcmSessionId(sessionId);\n if (sessionId !== undefined && !normalizedSessionId) return [];\n const normalizedSessionPrefix = normalizeOptionalLcmSessionId(sessionPrefix);\n if (sessionPrefix !== undefined && !normalizedSessionPrefix) return [];\n await this.ensureInitialized();\n return this.archive!.search(query, limit, normalizedSessionId, normalizedSessionPrefix);\n }\n\n /** Search via FTS returning full message content (not snippets). */\n async searchContextFull(\n query: string,\n limit: number,\n sessionId?: string,\n sessionPrefix?: string,\n ): Promise<\n Array<{\n id: number;\n turn_index: number;\n role: string;\n content: string;\n session_id: string;\n score: number;\n }>\n > {\n if (!this.config.enabled) return [];\n const normalizedSessionId = normalizeOptionalLcmSessionId(sessionId);\n if (sessionId !== undefined && !normalizedSessionId) return [];\n const normalizedSessionPrefix = normalizeOptionalLcmSessionId(sessionPrefix);\n if (sessionPrefix !== undefined && !normalizedSessionPrefix) return [];\n await this.ensureInitialized();\n return this.archive!.searchWithContent(\n query,\n limit,\n normalizedSessionId,\n 1000,\n normalizedSessionPrefix,\n );\n }\n\n /** Get a compressed summary of a turn range. */\n async describeContext(\n sessionId: string,\n fromTurn: number,\n toTurn: number,\n ): Promise<{ summary: string; turn_count: number; depth: number } | null> {\n if (!this.config.enabled) return null;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return null;\n await this.ensureInitialized();\n\n const nodes = this.dag!.getCoveringNodes(normalizedSessionId, fromTurn, toTurn);\n if (nodes.length === 0) {\n // No summary exists — build a description from raw messages\n const messages = this.archive!.getMessages(normalizedSessionId, fromTurn, toTurn);\n if (messages.length === 0) return null;\n const preview = messages\n .slice(0, 5)\n .map((m) => `[${m.role}] ${m.content.slice(0, 100)}`)\n .join(\"\\n\");\n return {\n summary: `No summary available for this range. Preview of ${messages.length} messages:\\n${preview}`,\n turn_count: messages.length,\n depth: -1,\n };\n }\n\n // Use the deepest covering node\n const best = nodes[0]; // Already sorted by depth DESC\n return {\n summary: best.summary_text,\n turn_count: best.msg_end - best.msg_start + 1,\n depth: best.depth,\n };\n }\n\n /** Retrieve raw messages for a turn range (lossless expansion). */\n async expandContext(\n sessionId: string,\n fromTurn: number,\n toTurn: number,\n maxTokens: number,\n ): Promise<Array<{ turn_index: number; role: string; content: string }>> {\n if (!this.config.enabled) return [];\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return [];\n await this.ensureInitialized();\n\n const messages = this.archive!.getMessages(normalizedSessionId, fromTurn, toTurn);\n if (messages.length === 0) return [];\n\n // Enforce token budget — keep first and last, truncate middle\n const maxChars = maxTokens * 4;\n let totalChars = 0;\n for (const m of messages) totalChars += m.content.length;\n\n if (totalChars <= maxChars) {\n return messages.map((m) => ({\n turn_index: m.turn_index,\n role: m.role,\n content: m.content,\n }));\n }\n\n // Keep first and last messages, truncate from middle\n const result: Array<{ turn_index: number; role: string; content: string }> =\n [];\n let budget = maxChars;\n\n // Reserve space for the last message\n const lastMsg = messages[messages.length - 1];\n const lastMsgChars = Math.min(\n lastMsg.content.length,\n Math.floor(maxChars * 0.3),\n );\n budget -= lastMsgChars;\n\n // Add messages from the beginning\n for (let i = 0; i < messages.length - 1; i++) {\n if (budget <= 0) break;\n const m = messages[i];\n const truncated = m.content.slice(0, budget);\n result.push({\n turn_index: m.turn_index,\n role: m.role,\n content: truncated,\n });\n budget -= truncated.length;\n }\n\n // Always append the last message\n result.push({\n turn_index: lastMsg.turn_index,\n role: lastMsg.role,\n content: lastMsg.content.slice(0, lastMsgChars + Math.max(0, budget)),\n });\n\n return result;\n }\n\n /** Get statistics about the LCM archive. */\n async getStats(sessionId?: string): Promise<{\n totalMessages: number;\n totalSummaryNodes: number;\n maxDepth: number;\n maxTurnIndex?: number;\n }> {\n if (!this.config.enabled)\n return { totalMessages: 0, totalSummaryNodes: 0, maxDepth: -1 };\n const normalizedSessionId = normalizeOptionalLcmSessionId(sessionId);\n if (sessionId !== undefined && !normalizedSessionId) {\n return { ...EMPTY_LCM_STATS };\n }\n await this.ensureInitialized();\n\n if (normalizedSessionId) {\n return {\n totalMessages: this.archive!.getMessageCount(normalizedSessionId),\n totalSummaryNodes: this.dag!.getNodeCount(normalizedSessionId),\n maxDepth: this.dag!.getMaxDepth(normalizedSessionId),\n maxTurnIndex: this.archive!.getMaxTurnIndex(normalizedSessionId),\n };\n }\n\n return {\n totalMessages: this.archive!.getTotalMessageCount(),\n totalSummaryNodes: 0, // Would need a global count query\n maxDepth: -1,\n };\n }\n\n /** Clear all LCM archive and summary state for one session. */\n async clearSession(sessionId: string): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n const normalizedSessionId = normalizeLcmSessionId(sessionId);\n if (!normalizedSessionId) return;\n await this.ensureInitialized();\n if (this.closed || !this.archive || !this.dag) return;\n await this.waitForSessionObserveIdle(normalizedSessionId);\n this.dag.deleteSession(normalizedSessionId);\n this.archive.deleteSession(normalizedSessionId);\n }\n\n /** Clear all LCM archive and summary state without closing the engine. */\n async clearAll(): Promise<void> {\n if (!this.config.enabled || this.closed) return;\n await this.ensureInitialized();\n if (this.closed || !this.archive || !this.dag) return;\n await this.waitForObserveQueueIdle();\n this.dag.deleteAll();\n this.archive.deleteAll();\n }\n\n /** Prune old data beyond retention period. */\n async prune(): Promise<{ messagesPruned: number; nodesPruned: number }> {\n if (!this.config.enabled) return { messagesPruned: 0, nodesPruned: 0 };\n await this.ensureInitialized();\n\n const messagesPruned = this.archive!.pruneOldMessages(\n this.config.archiveRetentionDays,\n );\n const nodesPruned = this.dag!.pruneOldNodes(\n this.config.archiveRetentionDays,\n );\n return { messagesPruned, nodesPruned };\n }\n\n /** Close the database connection. */\n close(): void {\n this.closed = true;\n if (this.db) {\n this.db.close();\n }\n this.db = null;\n this.archive = null;\n this.dag = null;\n this.summarizer = null;\n this.observeQueue = null;\n this.initPromise = null;\n }\n}\n"],"mappings":"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","names":[]}
|