@agentmemory/agentmemory 0.9.4 → 0.9.6
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/session-start.mjs +23 -10
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +1 -1
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/hooks/subagent-start.mjs +17 -18
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/image-refs-CZVd2z6E.mjs +3 -0
- package/dist/{image-store-Cn9eD-7D.mjs → image-store-CF4gfkLr.mjs} +1 -1
- package/dist/index.mjs +205 -82
- package/dist/index.mjs.map +1 -1
- package/dist/{src-uDy2jLO-.mjs → src-C7vGxttN.mjs} +147 -24
- package/dist/src-C7vGxttN.mjs.map +1 -0
- package/dist/{standalone-CqqEcfNR.mjs → standalone-DnSJzyXL.mjs} +36 -4
- package/dist/{standalone-CqqEcfNR.mjs.map → standalone-DnSJzyXL.mjs.map} +1 -1
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +35 -3
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-Co8VIL4t.mjs → tools-registry-CKMeHaPN.mjs} +2 -2
- package/dist/{tools-registry-Co8VIL4t.mjs.map → tools-registry-CKMeHaPN.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/session-start.mjs +23 -10
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +1 -1
- package/plugin/scripts/stop.mjs.map +1 -1
- package/plugin/scripts/subagent-start.mjs +17 -18
- package/plugin/scripts/subagent-start.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
|
}
|
|
@@ -1113,72 +1214,20 @@ var VectorIndex = class VectorIndex {
|
|
|
1113
1214
|
};
|
|
1114
1215
|
|
|
1115
1216
|
//#endregion
|
|
1116
|
-
//#region src/state/
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
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);
|
|
1217
|
+
//#region src/state/memory-utils.ts
|
|
1218
|
+
function memoryToObservation(memory) {
|
|
1219
|
+
return {
|
|
1220
|
+
id: memory.id,
|
|
1221
|
+
sessionId: memory.sessionIds[0] ?? "memory",
|
|
1222
|
+
timestamp: memory.createdAt,
|
|
1223
|
+
type: "decision",
|
|
1224
|
+
title: memory.title,
|
|
1225
|
+
facts: [memory.content],
|
|
1226
|
+
narrative: memory.content,
|
|
1227
|
+
concepts: memory.concepts,
|
|
1228
|
+
files: memory.files,
|
|
1229
|
+
importance: memory.strength
|
|
1230
|
+
};
|
|
1182
1231
|
}
|
|
1183
1232
|
|
|
1184
1233
|
//#endregion
|
|
@@ -1745,7 +1794,12 @@ var HybridSearch = class {
|
|
|
1745
1794
|
}
|
|
1746
1795
|
async enrichResults(results, limit) {
|
|
1747
1796
|
const sliced = results.slice(0, limit);
|
|
1748
|
-
const observations = await Promise.all(sliced.map(
|
|
1797
|
+
const observations = await Promise.all(sliced.map(async (r) => {
|
|
1798
|
+
const obs = await this.kv.get(KV.observations(r.sessionId), r.obsId).catch(() => null);
|
|
1799
|
+
if (obs) return obs;
|
|
1800
|
+
const mem = await this.kv.get(KV.memories, r.obsId).catch(() => null);
|
|
1801
|
+
return mem ? memoryToObservation(mem) : null;
|
|
1802
|
+
}));
|
|
1749
1803
|
const enriched = [];
|
|
1750
1804
|
for (let i = 0; i < sliced.length; i++) {
|
|
1751
1805
|
const obs = observations[i];
|
|
@@ -2052,6 +2106,9 @@ var SearchIndex = class SearchIndex {
|
|
|
2052
2106
|
}
|
|
2053
2107
|
this.sortedTerms = null;
|
|
2054
2108
|
}
|
|
2109
|
+
has(id) {
|
|
2110
|
+
return this.entries.has(id);
|
|
2111
|
+
}
|
|
2055
2112
|
search(query, limit = 20) {
|
|
2056
2113
|
const rawTerms = this.tokenize(query.toLowerCase());
|
|
2057
2114
|
if (rawTerms.length === 0) return [];
|
|
@@ -2480,9 +2537,20 @@ function getSearchIndex() {
|
|
|
2480
2537
|
async function rebuildIndex(kv) {
|
|
2481
2538
|
const idx = getSearchIndex();
|
|
2482
2539
|
idx.clear();
|
|
2483
|
-
const sessions = await kv.list(KV.sessions);
|
|
2484
|
-
if (!sessions.length) return 0;
|
|
2485
2540
|
let count = 0;
|
|
2541
|
+
try {
|
|
2542
|
+
const memories = await kv.list(KV.memories);
|
|
2543
|
+
for (const memory of memories) {
|
|
2544
|
+
if (memory.isLatest === false) continue;
|
|
2545
|
+
if (!memory.title || !memory.content) continue;
|
|
2546
|
+
idx.add(memoryToObservation(memory));
|
|
2547
|
+
count++;
|
|
2548
|
+
}
|
|
2549
|
+
} catch (err) {
|
|
2550
|
+
logger.warn("rebuildIndex: failed to load memories", { error: err instanceof Error ? err.message : String(err) });
|
|
2551
|
+
}
|
|
2552
|
+
const sessions = await kv.list(KV.sessions);
|
|
2553
|
+
if (!sessions.length) return count;
|
|
2486
2554
|
const obsPerSession = [];
|
|
2487
2555
|
const failedSessions = [];
|
|
2488
2556
|
for (let batch = 0; batch < sessions.length; batch += 10) {
|
|
@@ -2553,7 +2621,12 @@ function registerSearchFunction(sdk, kv) {
|
|
|
2553
2621
|
}
|
|
2554
2622
|
candidates.push(r);
|
|
2555
2623
|
}
|
|
2556
|
-
const obsResults = await Promise.all(candidates.map((r) =>
|
|
2624
|
+
const obsResults = await Promise.all(candidates.map(async (r) => {
|
|
2625
|
+
const obs = await kv.get(KV.observations(r.sessionId), r.obsId).catch(() => null);
|
|
2626
|
+
if (obs) return obs;
|
|
2627
|
+
const mem = await kv.get(KV.memories, r.obsId).catch(() => null);
|
|
2628
|
+
return mem ? memoryToObservation(mem) : null;
|
|
2629
|
+
}));
|
|
2557
2630
|
const enriched = [];
|
|
2558
2631
|
for (let i = 0; i < candidates.length; i++) {
|
|
2559
2632
|
const obs = obsResults[i];
|
|
@@ -4853,6 +4926,14 @@ function registerRememberFunction(sdk, kv) {
|
|
|
4853
4926
|
await kv.set(KV.memories, supersededMemory.id, supersededMemory);
|
|
4854
4927
|
}
|
|
4855
4928
|
await kv.set(KV.memories, memory.id, memory);
|
|
4929
|
+
try {
|
|
4930
|
+
getSearchIndex().add(memoryToObservation(memory));
|
|
4931
|
+
} catch (err) {
|
|
4932
|
+
logger.warn("Failed to index saved memory into BM25", {
|
|
4933
|
+
memId: memory.id,
|
|
4934
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4935
|
+
});
|
|
4936
|
+
}
|
|
4856
4937
|
if (supersededId) await sdk.trigger({
|
|
4857
4938
|
function_id: "mem::cascade-update",
|
|
4858
4939
|
payload: { supersededMemoryId: supersededId },
|
|
@@ -5650,7 +5731,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
5650
5731
|
|
|
5651
5732
|
//#endregion
|
|
5652
5733
|
//#region src/version.ts
|
|
5653
|
-
const VERSION = "0.9.
|
|
5734
|
+
const VERSION = "0.9.6";
|
|
5654
5735
|
|
|
5655
5736
|
//#endregion
|
|
5656
5737
|
//#region src/functions/export-import.ts
|
|
@@ -5772,7 +5853,9 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
5772
5853
|
"0.9.1",
|
|
5773
5854
|
"0.9.2",
|
|
5774
5855
|
"0.9.3",
|
|
5775
|
-
"0.9.4"
|
|
5856
|
+
"0.9.4",
|
|
5857
|
+
"0.9.5",
|
|
5858
|
+
"0.9.6"
|
|
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`);
|