@agentmemory/agentmemory 0.9.4 → 0.9.5
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/README.md +108 -50
- package/dist/cli.mjs +60 -20
- package/dist/cli.mjs.map +1 -1
- package/dist/docker-compose.yml +10 -1
- package/dist/hooks/session-end.mjs +4 -4
- package/dist/hooks/session-end.mjs.map +1 -1
- package/dist/hooks/stop.mjs +1 -1
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/image-refs-DRse_ePx.mjs +3 -0
- package/dist/{image-store-Cn9eD-7D.mjs → image-store-DnuCI2RB.mjs} +1 -1
- package/dist/index.mjs +206 -83
- package/dist/index.mjs.map +1 -1
- package/dist/{src-uDy2jLO-.mjs → src-xYHSzz5S.mjs} +145 -22
- package/dist/src-xYHSzz5S.mjs.map +1 -0
- package/dist/{standalone-CqqEcfNR.mjs → standalone-BvKacAId.mjs} +2 -2
- package/dist/{standalone-CqqEcfNR.mjs.map → standalone-BvKacAId.mjs.map} +1 -1
- package/dist/standalone.mjs +1 -1
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-Co8VIL4t.mjs → tools-registry-BWM0vWeA.mjs} +2 -2
- package/dist/{tools-registry-Co8VIL4t.mjs.map → tools-registry-BWM0vWeA.mjs.map} +1 -1
- package/docker-compose.yml +10 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/scripts/session-end.mjs +4 -4
- package/plugin/scripts/session-end.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +1 -1
- package/plugin/scripts/stop.mjs.map +1 -1
- package/dist/image-refs-BfT7XAa-.mjs +0 -3
- package/dist/src-uDy2jLO-.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -895,22 +895,38 @@ let imageEmbeddingProvider = null;
|
|
|
895
895
|
function createImageEmbeddingProvider() {
|
|
896
896
|
if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] !== "true") return null;
|
|
897
897
|
if (imageEmbeddingProvider) return imageEmbeddingProvider;
|
|
898
|
-
imageEmbeddingProvider = new ClipEmbeddingProvider();
|
|
898
|
+
imageEmbeddingProvider = withDimensionGuard(new ClipEmbeddingProvider());
|
|
899
899
|
return imageEmbeddingProvider;
|
|
900
900
|
}
|
|
901
901
|
function createEmbeddingProvider() {
|
|
902
902
|
const detected = detectEmbeddingProvider();
|
|
903
903
|
if (!detected) return null;
|
|
904
904
|
switch (detected) {
|
|
905
|
-
case "gemini": return new GeminiEmbeddingProvider(getEnvVar("GEMINI_API_KEY"));
|
|
906
|
-
case "openai": return new OpenAIEmbeddingProvider(getEnvVar("OPENAI_API_KEY"));
|
|
907
|
-
case "voyage": return new VoyageEmbeddingProvider(getEnvVar("VOYAGE_API_KEY"));
|
|
908
|
-
case "cohere": return new CohereEmbeddingProvider(getEnvVar("COHERE_API_KEY"));
|
|
909
|
-
case "openrouter": return new OpenRouterEmbeddingProvider(getEnvVar("OPENROUTER_API_KEY"));
|
|
910
|
-
case "local": return new LocalEmbeddingProvider();
|
|
905
|
+
case "gemini": return withDimensionGuard(new GeminiEmbeddingProvider(getEnvVar("GEMINI_API_KEY")));
|
|
906
|
+
case "openai": return withDimensionGuard(new OpenAIEmbeddingProvider(getEnvVar("OPENAI_API_KEY")));
|
|
907
|
+
case "voyage": return withDimensionGuard(new VoyageEmbeddingProvider(getEnvVar("VOYAGE_API_KEY")));
|
|
908
|
+
case "cohere": return withDimensionGuard(new CohereEmbeddingProvider(getEnvVar("COHERE_API_KEY")));
|
|
909
|
+
case "openrouter": return withDimensionGuard(new OpenRouterEmbeddingProvider(getEnvVar("OPENROUTER_API_KEY")));
|
|
910
|
+
case "local": return withDimensionGuard(new LocalEmbeddingProvider());
|
|
911
911
|
default: return null;
|
|
912
912
|
}
|
|
913
913
|
}
|
|
914
|
+
function withDimensionGuard(provider) {
|
|
915
|
+
const expected = provider.dimensions;
|
|
916
|
+
const check = (v, where) => {
|
|
917
|
+
if (v.length !== expected) throw new Error(`Embedding dimension mismatch in ${provider.name}.${where}: expected ${expected}, got ${v.length}`);
|
|
918
|
+
return v;
|
|
919
|
+
};
|
|
920
|
+
const wrapped = Object.create(provider);
|
|
921
|
+
wrapped.embed = async (t) => check(await provider.embed(t), "embed");
|
|
922
|
+
wrapped.embedBatch = async (ts) => {
|
|
923
|
+
const out = await provider.embedBatch(ts);
|
|
924
|
+
out.forEach((v, i) => check(v, `embedBatch[${i}]`));
|
|
925
|
+
return out;
|
|
926
|
+
};
|
|
927
|
+
if (provider.embedImage) wrapped.embedImage = async (s) => check(await provider.embedImage(s), "embedImage");
|
|
928
|
+
return wrapped;
|
|
929
|
+
}
|
|
914
930
|
|
|
915
931
|
//#endregion
|
|
916
932
|
//#region src/providers/index.ts
|
|
@@ -1006,6 +1022,75 @@ var StateKV = class {
|
|
|
1006
1022
|
}
|
|
1007
1023
|
};
|
|
1008
1024
|
|
|
1025
|
+
//#endregion
|
|
1026
|
+
//#region src/state/schema.ts
|
|
1027
|
+
const KV = {
|
|
1028
|
+
sessions: "mem:sessions",
|
|
1029
|
+
observations: (sessionId) => `mem:obs:${sessionId}`,
|
|
1030
|
+
memories: "mem:memories",
|
|
1031
|
+
summaries: "mem:summaries",
|
|
1032
|
+
config: "mem:config",
|
|
1033
|
+
metrics: "mem:metrics",
|
|
1034
|
+
health: "mem:health",
|
|
1035
|
+
embeddings: (obsId) => `mem:emb:${obsId}`,
|
|
1036
|
+
bm25Index: "mem:index:bm25",
|
|
1037
|
+
relations: "mem:relations",
|
|
1038
|
+
profiles: "mem:profiles",
|
|
1039
|
+
claudeBridge: "mem:claude-bridge",
|
|
1040
|
+
graphNodes: "mem:graph:nodes",
|
|
1041
|
+
graphEdges: "mem:graph:edges",
|
|
1042
|
+
semantic: "mem:semantic",
|
|
1043
|
+
procedural: "mem:procedural",
|
|
1044
|
+
teamShared: (teamId) => `mem:team:${teamId}:shared`,
|
|
1045
|
+
teamUsers: (teamId, userId) => `mem:team:${teamId}:users:${userId}`,
|
|
1046
|
+
teamProfile: (teamId) => `mem:team:${teamId}:profile`,
|
|
1047
|
+
audit: "mem:audit",
|
|
1048
|
+
actions: "mem:actions",
|
|
1049
|
+
actionEdges: "mem:action-edges",
|
|
1050
|
+
leases: "mem:leases",
|
|
1051
|
+
routines: "mem:routines",
|
|
1052
|
+
routineRuns: "mem:routine-runs",
|
|
1053
|
+
signals: "mem:signals",
|
|
1054
|
+
checkpoints: "mem:checkpoints",
|
|
1055
|
+
mesh: "mem:mesh",
|
|
1056
|
+
sketches: "mem:sketches",
|
|
1057
|
+
facets: "mem:facets",
|
|
1058
|
+
sentinels: "mem:sentinels",
|
|
1059
|
+
crystals: "mem:crystals",
|
|
1060
|
+
lessons: "mem:lessons",
|
|
1061
|
+
insights: "mem:insights",
|
|
1062
|
+
graphEdgeHistory: "mem:graph:edge-history",
|
|
1063
|
+
enrichedChunks: (sessionId) => `mem:enriched:${sessionId}`,
|
|
1064
|
+
latentEmbeddings: (obsId) => `mem:latent:${obsId}`,
|
|
1065
|
+
retentionScores: "mem:retention",
|
|
1066
|
+
accessLog: "mem:access",
|
|
1067
|
+
imageRefs: "mem:image-refs",
|
|
1068
|
+
imageEmbeddings: "mem:image-embeddings",
|
|
1069
|
+
slots: "mem:slots",
|
|
1070
|
+
globalSlots: "mem:slots:global",
|
|
1071
|
+
state: "mem:state"
|
|
1072
|
+
};
|
|
1073
|
+
const STREAM = {
|
|
1074
|
+
name: "mem-live",
|
|
1075
|
+
group: (sessionId) => sessionId,
|
|
1076
|
+
viewerGroup: "viewer"
|
|
1077
|
+
};
|
|
1078
|
+
function generateId(prefix) {
|
|
1079
|
+
return `${prefix}_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
1080
|
+
}
|
|
1081
|
+
function fingerprintId(prefix, content) {
|
|
1082
|
+
return `${prefix}_${createHash("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
1083
|
+
}
|
|
1084
|
+
function jaccardSimilarity(a, b) {
|
|
1085
|
+
const setA = new Set(a.split(/\s+/).filter((t) => t.length > 2));
|
|
1086
|
+
const setB = new Set(b.split(/\s+/).filter((t) => t.length > 2));
|
|
1087
|
+
if (setA.size === 0 && setB.size === 0) return 1;
|
|
1088
|
+
if (setA.size === 0 || setB.size === 0) return 0;
|
|
1089
|
+
let intersection = 0;
|
|
1090
|
+
for (const word of setA) if (setB.has(word)) intersection++;
|
|
1091
|
+
return intersection / (setA.size + setB.size - intersection);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1009
1094
|
//#endregion
|
|
1010
1095
|
//#region src/state/vector-index.ts
|
|
1011
1096
|
function float32ToBase64(arr) {
|
|
@@ -1069,6 +1154,22 @@ var VectorIndex = class VectorIndex {
|
|
|
1069
1154
|
get size() {
|
|
1070
1155
|
return this.vectors.size;
|
|
1071
1156
|
}
|
|
1157
|
+
validateDimensions(expected) {
|
|
1158
|
+
const mismatches = [];
|
|
1159
|
+
const seenDimensions = /* @__PURE__ */ new Set();
|
|
1160
|
+
for (const [obsId, entry] of this.vectors) {
|
|
1161
|
+
const dim = entry.embedding.length;
|
|
1162
|
+
seenDimensions.add(dim);
|
|
1163
|
+
if (dim !== expected) mismatches.push({
|
|
1164
|
+
obsId,
|
|
1165
|
+
dim
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
return {
|
|
1169
|
+
mismatches,
|
|
1170
|
+
seenDimensions
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1072
1173
|
clear() {
|
|
1073
1174
|
this.vectors.clear();
|
|
1074
1175
|
}
|
|
@@ -1112,75 +1213,6 @@ var VectorIndex = class VectorIndex {
|
|
|
1112
1213
|
}
|
|
1113
1214
|
};
|
|
1114
1215
|
|
|
1115
|
-
//#endregion
|
|
1116
|
-
//#region src/state/schema.ts
|
|
1117
|
-
const KV = {
|
|
1118
|
-
sessions: "mem:sessions",
|
|
1119
|
-
observations: (sessionId) => `mem:obs:${sessionId}`,
|
|
1120
|
-
memories: "mem:memories",
|
|
1121
|
-
summaries: "mem:summaries",
|
|
1122
|
-
config: "mem:config",
|
|
1123
|
-
metrics: "mem:metrics",
|
|
1124
|
-
health: "mem:health",
|
|
1125
|
-
embeddings: (obsId) => `mem:emb:${obsId}`,
|
|
1126
|
-
bm25Index: "mem:index:bm25",
|
|
1127
|
-
relations: "mem:relations",
|
|
1128
|
-
profiles: "mem:profiles",
|
|
1129
|
-
claudeBridge: "mem:claude-bridge",
|
|
1130
|
-
graphNodes: "mem:graph:nodes",
|
|
1131
|
-
graphEdges: "mem:graph:edges",
|
|
1132
|
-
semantic: "mem:semantic",
|
|
1133
|
-
procedural: "mem:procedural",
|
|
1134
|
-
teamShared: (teamId) => `mem:team:${teamId}:shared`,
|
|
1135
|
-
teamUsers: (teamId, userId) => `mem:team:${teamId}:users:${userId}`,
|
|
1136
|
-
teamProfile: (teamId) => `mem:team:${teamId}:profile`,
|
|
1137
|
-
audit: "mem:audit",
|
|
1138
|
-
actions: "mem:actions",
|
|
1139
|
-
actionEdges: "mem:action-edges",
|
|
1140
|
-
leases: "mem:leases",
|
|
1141
|
-
routines: "mem:routines",
|
|
1142
|
-
routineRuns: "mem:routine-runs",
|
|
1143
|
-
signals: "mem:signals",
|
|
1144
|
-
checkpoints: "mem:checkpoints",
|
|
1145
|
-
mesh: "mem:mesh",
|
|
1146
|
-
sketches: "mem:sketches",
|
|
1147
|
-
facets: "mem:facets",
|
|
1148
|
-
sentinels: "mem:sentinels",
|
|
1149
|
-
crystals: "mem:crystals",
|
|
1150
|
-
lessons: "mem:lessons",
|
|
1151
|
-
insights: "mem:insights",
|
|
1152
|
-
graphEdgeHistory: "mem:graph:edge-history",
|
|
1153
|
-
enrichedChunks: (sessionId) => `mem:enriched:${sessionId}`,
|
|
1154
|
-
latentEmbeddings: (obsId) => `mem:latent:${obsId}`,
|
|
1155
|
-
retentionScores: "mem:retention",
|
|
1156
|
-
accessLog: "mem:access",
|
|
1157
|
-
imageRefs: "mem:image-refs",
|
|
1158
|
-
imageEmbeddings: "mem:image-embeddings",
|
|
1159
|
-
slots: "mem:slots",
|
|
1160
|
-
globalSlots: "mem:slots:global",
|
|
1161
|
-
state: "mem:state"
|
|
1162
|
-
};
|
|
1163
|
-
const STREAM = {
|
|
1164
|
-
name: "mem-live",
|
|
1165
|
-
group: (sessionId) => sessionId,
|
|
1166
|
-
viewerGroup: "viewer"
|
|
1167
|
-
};
|
|
1168
|
-
function generateId(prefix) {
|
|
1169
|
-
return `${prefix}_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
1170
|
-
}
|
|
1171
|
-
function fingerprintId(prefix, content) {
|
|
1172
|
-
return `${prefix}_${createHash("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
1173
|
-
}
|
|
1174
|
-
function jaccardSimilarity(a, b) {
|
|
1175
|
-
const setA = new Set(a.split(/\s+/).filter((t) => t.length > 2));
|
|
1176
|
-
const setB = new Set(b.split(/\s+/).filter((t) => t.length > 2));
|
|
1177
|
-
if (setA.size === 0 && setB.size === 0) return 1;
|
|
1178
|
-
if (setA.size === 0 || setB.size === 0) return 0;
|
|
1179
|
-
let intersection = 0;
|
|
1180
|
-
for (const word of setA) if (setB.has(word)) intersection++;
|
|
1181
|
-
return intersection / (setA.size + setB.size - intersection);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
1216
|
//#endregion
|
|
1185
1217
|
//#region src/functions/graph-retrieval.ts
|
|
1186
1218
|
function buildGraphContext(path) {
|
|
@@ -2052,6 +2084,9 @@ var SearchIndex = class SearchIndex {
|
|
|
2052
2084
|
}
|
|
2053
2085
|
this.sortedTerms = null;
|
|
2054
2086
|
}
|
|
2087
|
+
has(id) {
|
|
2088
|
+
return this.entries.has(id);
|
|
2089
|
+
}
|
|
2055
2090
|
search(query, limit = 20) {
|
|
2056
2091
|
const rawTerms = this.tokenize(query.toLowerCase());
|
|
2057
2092
|
if (rawTerms.length === 0) return [];
|
|
@@ -2472,6 +2507,20 @@ async function deleteAccessLog(kv, memoryId) {
|
|
|
2472
2507
|
|
|
2473
2508
|
//#endregion
|
|
2474
2509
|
//#region src/functions/search.ts
|
|
2510
|
+
function memoryAsIndexable$1(memory) {
|
|
2511
|
+
return {
|
|
2512
|
+
id: memory.id,
|
|
2513
|
+
sessionId: memory.sessionIds[0] ?? "memory",
|
|
2514
|
+
timestamp: memory.createdAt,
|
|
2515
|
+
type: "decision",
|
|
2516
|
+
title: memory.title,
|
|
2517
|
+
facts: [memory.content],
|
|
2518
|
+
narrative: memory.content,
|
|
2519
|
+
concepts: memory.concepts,
|
|
2520
|
+
files: memory.files,
|
|
2521
|
+
importance: memory.strength
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2475
2524
|
let index = null;
|
|
2476
2525
|
function getSearchIndex() {
|
|
2477
2526
|
if (!index) index = new SearchIndex();
|
|
@@ -2480,9 +2529,20 @@ function getSearchIndex() {
|
|
|
2480
2529
|
async function rebuildIndex(kv) {
|
|
2481
2530
|
const idx = getSearchIndex();
|
|
2482
2531
|
idx.clear();
|
|
2483
|
-
const sessions = await kv.list(KV.sessions);
|
|
2484
|
-
if (!sessions.length) return 0;
|
|
2485
2532
|
let count = 0;
|
|
2533
|
+
try {
|
|
2534
|
+
const memories = await kv.list(KV.memories);
|
|
2535
|
+
for (const memory of memories) {
|
|
2536
|
+
if (memory.isLatest === false) continue;
|
|
2537
|
+
if (!memory.title || !memory.content) continue;
|
|
2538
|
+
idx.add(memoryAsIndexable$1(memory));
|
|
2539
|
+
count++;
|
|
2540
|
+
}
|
|
2541
|
+
} catch (err) {
|
|
2542
|
+
logger.warn("rebuildIndex: failed to load memories", { error: err instanceof Error ? err.message : String(err) });
|
|
2543
|
+
}
|
|
2544
|
+
const sessions = await kv.list(KV.sessions);
|
|
2545
|
+
if (!sessions.length) return count;
|
|
2486
2546
|
const obsPerSession = [];
|
|
2487
2547
|
const failedSessions = [];
|
|
2488
2548
|
for (let batch = 0; batch < sessions.length; batch += 10) {
|
|
@@ -4788,6 +4848,20 @@ function registerPatternsFunction(sdk, kv) {
|
|
|
4788
4848
|
|
|
4789
4849
|
//#endregion
|
|
4790
4850
|
//#region src/functions/remember.ts
|
|
4851
|
+
function memoryAsIndexable(memory) {
|
|
4852
|
+
return {
|
|
4853
|
+
id: memory.id,
|
|
4854
|
+
sessionId: memory.sessionIds[0] ?? "memory",
|
|
4855
|
+
timestamp: memory.createdAt,
|
|
4856
|
+
type: "decision",
|
|
4857
|
+
title: memory.title,
|
|
4858
|
+
facts: [memory.content],
|
|
4859
|
+
narrative: memory.content,
|
|
4860
|
+
concepts: memory.concepts,
|
|
4861
|
+
files: memory.files,
|
|
4862
|
+
importance: memory.strength
|
|
4863
|
+
};
|
|
4864
|
+
}
|
|
4791
4865
|
function registerRememberFunction(sdk, kv) {
|
|
4792
4866
|
sdk.registerFunction("mem::remember", async (data) => {
|
|
4793
4867
|
if (!data.content || typeof data.content !== "string" || !data.content.trim()) return {
|
|
@@ -4853,6 +4927,14 @@ function registerRememberFunction(sdk, kv) {
|
|
|
4853
4927
|
await kv.set(KV.memories, supersededMemory.id, supersededMemory);
|
|
4854
4928
|
}
|
|
4855
4929
|
await kv.set(KV.memories, memory.id, memory);
|
|
4930
|
+
try {
|
|
4931
|
+
getSearchIndex().add(memoryAsIndexable(memory));
|
|
4932
|
+
} catch (err) {
|
|
4933
|
+
logger.warn("Failed to index saved memory into BM25", {
|
|
4934
|
+
memId: memory.id,
|
|
4935
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4936
|
+
});
|
|
4937
|
+
}
|
|
4856
4938
|
if (supersededId) await sdk.trigger({
|
|
4857
4939
|
function_id: "mem::cascade-update",
|
|
4858
4940
|
payload: { supersededMemoryId: supersededId },
|
|
@@ -5650,7 +5732,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
5650
5732
|
|
|
5651
5733
|
//#endregion
|
|
5652
5734
|
//#region src/version.ts
|
|
5653
|
-
const VERSION = "0.9.
|
|
5735
|
+
const VERSION = "0.9.5";
|
|
5654
5736
|
|
|
5655
5737
|
//#endregion
|
|
5656
5738
|
//#region src/functions/export-import.ts
|
|
@@ -5772,7 +5854,8 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
5772
5854
|
"0.9.1",
|
|
5773
5855
|
"0.9.2",
|
|
5774
5856
|
"0.9.3",
|
|
5775
|
-
"0.9.4"
|
|
5857
|
+
"0.9.4",
|
|
5858
|
+
"0.9.5"
|
|
5776
5859
|
]).has(importData.version)) return {
|
|
5777
5860
|
success: false,
|
|
5778
5861
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -19327,6 +19410,7 @@ async function main() {
|
|
|
19327
19410
|
console.log(`[agentmemory] Streams: ws://localhost:${config.streamsPort}`);
|
|
19328
19411
|
const sdk = registerWorker(config.engineUrl, {
|
|
19329
19412
|
workerName: "agentmemory",
|
|
19413
|
+
invocationTimeoutMs: 18e4,
|
|
19330
19414
|
otel: {
|
|
19331
19415
|
serviceName: OTEL_CONFIG.serviceName,
|
|
19332
19416
|
serviceVersion: OTEL_CONFIG.serviceVersion,
|
|
@@ -19435,8 +19519,20 @@ async function main() {
|
|
|
19435
19519
|
console.log(`[agentmemory] Loaded persisted BM25 index (${bm25Index.size} docs)`);
|
|
19436
19520
|
}
|
|
19437
19521
|
if (loaded?.vector && vectorIndex && loaded.vector.size > 0) {
|
|
19438
|
-
|
|
19439
|
-
|
|
19522
|
+
const activeDim = embeddingProvider?.dimensions ?? 0;
|
|
19523
|
+
const { mismatches, seenDimensions } = activeDim > 0 ? loaded.vector.validateDimensions(activeDim) : {
|
|
19524
|
+
mismatches: [],
|
|
19525
|
+
seenDimensions: /* @__PURE__ */ new Set()
|
|
19526
|
+
};
|
|
19527
|
+
if (mismatches.length > 0) {
|
|
19528
|
+
const sample = mismatches.slice(0, 5).map((m) => `${m.obsId} (dim=${m.dim})`).join(", ");
|
|
19529
|
+
const distinct = Array.from(seenDimensions).sort((a, b) => a - b).join(", ");
|
|
19530
|
+
if (process.env["AGENTMEMORY_DROP_STALE_INDEX"] === "true") console.warn(`[agentmemory] Persisted vector index has ${mismatches.length} of ${loaded.vector.size} vectors with the wrong dimension. Active provider (${embeddingProvider?.name}) declares ${activeDim}; dimensions seen on disk: ${distinct}. AGENTMEMORY_DROP_STALE_INDEX=true is set — discarding the persisted vectors. Live observations will rebuild the index over time.`);
|
|
19531
|
+
else throw new Error(`[agentmemory] Refusing to start: persisted vector index has ${mismatches.length} of ${loaded.vector.size} vectors with the wrong dimension. Active provider (${embeddingProvider?.name}) declares ${activeDim}; dimensions seen on disk: ${distinct}. First mismatched obsIds: ${sample}. Loading would silently corrupt search (cross-dimension cosine returns 0). Choose one:\n - Re-embed the existing index against the new provider, then start.\n - Set AGENTMEMORY_DROP_STALE_INDEX=true to discard the persisted vectors and rebuild from live observations.\n - Switch the embedding provider back to the one that wrote the index.`);
|
|
19532
|
+
} else {
|
|
19533
|
+
vectorIndex.restoreFrom(loaded.vector);
|
|
19534
|
+
console.log(`[agentmemory] Loaded persisted vector index (${vectorIndex.size} vectors)`);
|
|
19535
|
+
}
|
|
19440
19536
|
}
|
|
19441
19537
|
if (bm25Index.size === 0) {
|
|
19442
19538
|
const indexCount = await rebuildIndex(kv).catch((err) => {
|
|
@@ -19444,9 +19540,36 @@ async function main() {
|
|
|
19444
19540
|
return 0;
|
|
19445
19541
|
});
|
|
19446
19542
|
if (indexCount > 0) {
|
|
19447
|
-
console.log(`[agentmemory] Search index rebuilt: ${indexCount}
|
|
19543
|
+
console.log(`[agentmemory] Search index rebuilt: ${indexCount} entries`);
|
|
19544
|
+
indexPersistence.scheduleSave();
|
|
19545
|
+
}
|
|
19546
|
+
} else try {
|
|
19547
|
+
const memories = await kv.list(KV.memories);
|
|
19548
|
+
let backfilled = 0;
|
|
19549
|
+
for (const memory of memories) {
|
|
19550
|
+
if (memory.isLatest === false) continue;
|
|
19551
|
+
if (!memory.title || !memory.content) continue;
|
|
19552
|
+
if (bm25Index.has(memory.id)) continue;
|
|
19553
|
+
bm25Index.add({
|
|
19554
|
+
id: memory.id,
|
|
19555
|
+
sessionId: memory.sessionIds[0] ?? "memory",
|
|
19556
|
+
timestamp: memory.createdAt,
|
|
19557
|
+
type: "decision",
|
|
19558
|
+
title: memory.title,
|
|
19559
|
+
facts: [memory.content],
|
|
19560
|
+
narrative: memory.content,
|
|
19561
|
+
concepts: memory.concepts,
|
|
19562
|
+
files: memory.files,
|
|
19563
|
+
importance: memory.strength
|
|
19564
|
+
});
|
|
19565
|
+
backfilled++;
|
|
19566
|
+
}
|
|
19567
|
+
if (backfilled > 0) {
|
|
19568
|
+
console.log(`[agentmemory] Backfilled ${backfilled} memories into BM25 (legacy gap before #257)`);
|
|
19448
19569
|
indexPersistence.scheduleSave();
|
|
19449
19570
|
}
|
|
19571
|
+
} catch (err) {
|
|
19572
|
+
console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
|
|
19450
19573
|
}
|
|
19451
19574
|
console.log(`[agentmemory] Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
19452
19575
|
console.log(`[agentmemory] Endpoints: 107 REST + ${getAllTools().length} MCP tools + 6 MCP resources + 3 MCP prompts`);
|