@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.
@@ -1,5 +1,5 @@
1
1
  import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./cli.mjs";
2
- import { _ as loadSnapshotConfig, a as detectLlmProviderKind, d as isContextInjectionEnabled, f as isGraphExtractionEnabled, g as loadFallbackConfig, h as loadEmbeddingConfig, i as detectEmbeddingProvider, l as isAutoCompressEnabled, m as loadConfig, n as getVisibleTools, o as getConsolidationDecayDays, p as loadClaudeBridgeConfig, r as VERSION, s as getEnvVar, t as getAllTools, u as isConsolidationEnabled, v as loadTeamConfig } from "./tools-registry-Co8VIL4t.mjs";
2
+ import { _ as loadSnapshotConfig, a as detectLlmProviderKind, d as isContextInjectionEnabled, f as isGraphExtractionEnabled, g as loadFallbackConfig, h as loadEmbeddingConfig, i as detectEmbeddingProvider, l as isAutoCompressEnabled, m as loadConfig, n as getVisibleTools, o as getConsolidationDecayDays, p as loadClaudeBridgeConfig, r as VERSION, s as getEnvVar, t as getAllTools, u as isConsolidationEnabled, v as loadTeamConfig } from "./tools-registry-BWM0vWeA.mjs";
3
3
  import { execFile } from "node:child_process";
4
4
  import { constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { basename, dirname, extname, join, resolve, sep } from "node:path";
@@ -787,22 +787,38 @@ let imageEmbeddingProvider = null;
787
787
  function createImageEmbeddingProvider() {
788
788
  if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] !== "true") return null;
789
789
  if (imageEmbeddingProvider) return imageEmbeddingProvider;
790
- imageEmbeddingProvider = new ClipEmbeddingProvider();
790
+ imageEmbeddingProvider = withDimensionGuard(new ClipEmbeddingProvider());
791
791
  return imageEmbeddingProvider;
792
792
  }
793
793
  function createEmbeddingProvider() {
794
794
  const detected = detectEmbeddingProvider();
795
795
  if (!detected) return null;
796
796
  switch (detected) {
797
- case "gemini": return new GeminiEmbeddingProvider(getEnvVar("GEMINI_API_KEY"));
798
- case "openai": return new OpenAIEmbeddingProvider(getEnvVar("OPENAI_API_KEY"));
799
- case "voyage": return new VoyageEmbeddingProvider(getEnvVar("VOYAGE_API_KEY"));
800
- case "cohere": return new CohereEmbeddingProvider(getEnvVar("COHERE_API_KEY"));
801
- case "openrouter": return new OpenRouterEmbeddingProvider(getEnvVar("OPENROUTER_API_KEY"));
802
- case "local": return new LocalEmbeddingProvider();
797
+ case "gemini": return withDimensionGuard(new GeminiEmbeddingProvider(getEnvVar("GEMINI_API_KEY")));
798
+ case "openai": return withDimensionGuard(new OpenAIEmbeddingProvider(getEnvVar("OPENAI_API_KEY")));
799
+ case "voyage": return withDimensionGuard(new VoyageEmbeddingProvider(getEnvVar("VOYAGE_API_KEY")));
800
+ case "cohere": return withDimensionGuard(new CohereEmbeddingProvider(getEnvVar("COHERE_API_KEY")));
801
+ case "openrouter": return withDimensionGuard(new OpenRouterEmbeddingProvider(getEnvVar("OPENROUTER_API_KEY")));
802
+ case "local": return withDimensionGuard(new LocalEmbeddingProvider());
803
803
  default: return null;
804
804
  }
805
805
  }
806
+ function withDimensionGuard(provider) {
807
+ const expected = provider.dimensions;
808
+ const check = (v, where) => {
809
+ if (v.length !== expected) throw new Error(`Embedding dimension mismatch in ${provider.name}.${where}: expected ${expected}, got ${v.length}`);
810
+ return v;
811
+ };
812
+ const wrapped = Object.create(provider);
813
+ wrapped.embed = async (t) => check(await provider.embed(t), "embed");
814
+ wrapped.embedBatch = async (ts) => {
815
+ const out = await provider.embedBatch(ts);
816
+ out.forEach((v, i) => check(v, `embedBatch[${i}]`));
817
+ return out;
818
+ };
819
+ if (provider.embedImage) wrapped.embedImage = async (s) => check(await provider.embedImage(s), "embedImage");
820
+ return wrapped;
821
+ }
806
822
 
807
823
  //#endregion
808
824
  //#region src/providers/index.ts
@@ -961,6 +977,22 @@ var VectorIndex = class VectorIndex {
961
977
  get size() {
962
978
  return this.vectors.size;
963
979
  }
980
+ validateDimensions(expected) {
981
+ const mismatches = [];
982
+ const seenDimensions = /* @__PURE__ */ new Set();
983
+ for (const [obsId, entry] of this.vectors) {
984
+ const dim = entry.embedding.length;
985
+ seenDimensions.add(dim);
986
+ if (dim !== expected) mismatches.push({
987
+ obsId,
988
+ dim
989
+ });
990
+ }
991
+ return {
992
+ mismatches,
993
+ seenDimensions
994
+ };
995
+ }
964
996
  clear() {
965
997
  this.vectors.clear();
966
998
  }
@@ -1875,6 +1907,9 @@ var SearchIndex = class SearchIndex {
1875
1907
  }
1876
1908
  this.sortedTerms = null;
1877
1909
  }
1910
+ has(id) {
1911
+ return this.entries.has(id);
1912
+ }
1878
1913
  search(query, limit = 20) {
1879
1914
  const rawTerms = this.tokenize(query.toLowerCase());
1880
1915
  if (rawTerms.length === 0) return [];
@@ -2282,6 +2317,20 @@ async function deleteAccessLog(kv, memoryId) {
2282
2317
 
2283
2318
  //#endregion
2284
2319
  //#region src/functions/search.ts
2320
+ function memoryAsIndexable$1(memory) {
2321
+ return {
2322
+ id: memory.id,
2323
+ sessionId: memory.sessionIds[0] ?? "memory",
2324
+ timestamp: memory.createdAt,
2325
+ type: "decision",
2326
+ title: memory.title,
2327
+ facts: [memory.content],
2328
+ narrative: memory.content,
2329
+ concepts: memory.concepts,
2330
+ files: memory.files,
2331
+ importance: memory.strength
2332
+ };
2333
+ }
2285
2334
  let index = null;
2286
2335
  function getSearchIndex() {
2287
2336
  if (!index) index = new SearchIndex();
@@ -2290,9 +2339,20 @@ function getSearchIndex() {
2290
2339
  async function rebuildIndex(kv) {
2291
2340
  const idx = getSearchIndex();
2292
2341
  idx.clear();
2293
- const sessions = await kv.list(KV.sessions);
2294
- if (!sessions.length) return 0;
2295
2342
  let count = 0;
2343
+ try {
2344
+ const memories = await kv.list(KV.memories);
2345
+ for (const memory of memories) {
2346
+ if (memory.isLatest === false) continue;
2347
+ if (!memory.title || !memory.content) continue;
2348
+ idx.add(memoryAsIndexable$1(memory));
2349
+ count++;
2350
+ }
2351
+ } catch (err) {
2352
+ logger.warn("rebuildIndex: failed to load memories", { error: err instanceof Error ? err.message : String(err) });
2353
+ }
2354
+ const sessions = await kv.list(KV.sessions);
2355
+ if (!sessions.length) return count;
2296
2356
  const obsPerSession = [];
2297
2357
  const failedSessions = [];
2298
2358
  for (let batch = 0; batch < sessions.length; batch += 10) {
@@ -2527,10 +2587,10 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
2527
2587
  };
2528
2588
  }
2529
2589
  if (pendingImageData && (pendingImageData.startsWith("data:image/") || pendingImageData.startsWith("iVBORw0KGgo") || pendingImageData.startsWith("/9j/"))) {
2530
- const { saveImageToDisk } = await import("./image-store-Cn9eD-7D.mjs");
2590
+ const { saveImageToDisk } = await import("./image-store-DnuCI2RB.mjs");
2531
2591
  const { filePath, bytesWritten } = await saveImageToDisk(pendingImageData);
2532
2592
  raw.imageData = filePath;
2533
- const { incrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
2593
+ const { incrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
2534
2594
  await incrementImageRef(kv, filePath);
2535
2595
  sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: bytesWritten });
2536
2596
  if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] === "true") sdk.triggerVoid("mem::vision-embed", {
@@ -2543,7 +2603,7 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
2543
2603
  await kv.set(KV.observations(payload.sessionId), obsId, raw);
2544
2604
  } catch (error) {
2545
2605
  if (raw.imageData) {
2546
- const { deleteImage } = await import("./image-store-Cn9eD-7D.mjs");
2606
+ const { deleteImage } = await import("./image-store-DnuCI2RB.mjs");
2547
2607
  const { deletedBytes } = await deleteImage(raw.imageData);
2548
2608
  if (deletedBytes > 0) sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: -deletedBytes });
2549
2609
  }
@@ -4491,6 +4551,20 @@ function registerPatternsFunction(sdk, kv) {
4491
4551
 
4492
4552
  //#endregion
4493
4553
  //#region src/functions/remember.ts
4554
+ function memoryAsIndexable(memory) {
4555
+ return {
4556
+ id: memory.id,
4557
+ sessionId: memory.sessionIds[0] ?? "memory",
4558
+ timestamp: memory.createdAt,
4559
+ type: "decision",
4560
+ title: memory.title,
4561
+ facts: [memory.content],
4562
+ narrative: memory.content,
4563
+ concepts: memory.concepts,
4564
+ files: memory.files,
4565
+ importance: memory.strength
4566
+ };
4567
+ }
4494
4568
  function registerRememberFunction(sdk, kv) {
4495
4569
  sdk.registerFunction("mem::remember", async (data) => {
4496
4570
  if (!data.content || typeof data.content !== "string" || !data.content.trim()) return {
@@ -4556,6 +4630,14 @@ function registerRememberFunction(sdk, kv) {
4556
4630
  await kv.set(KV.memories, supersededMemory.id, supersededMemory);
4557
4631
  }
4558
4632
  await kv.set(KV.memories, memory.id, memory);
4633
+ try {
4634
+ getSearchIndex().add(memoryAsIndexable(memory));
4635
+ } catch (err) {
4636
+ logger.warn("Failed to index saved memory into BM25", {
4637
+ memId: memory.id,
4638
+ error: err instanceof Error ? err.message : String(err)
4639
+ });
4640
+ }
4559
4641
  if (supersededId) await sdk.trigger({
4560
4642
  function_id: "mem::cascade-update",
4561
4643
  payload: { supersededMemoryId: supersededId },
@@ -4576,7 +4658,7 @@ function registerRememberFunction(sdk, kv) {
4576
4658
  const deletedMemoryIds = [];
4577
4659
  const deletedObservationIds = [];
4578
4660
  let deletedSession = false;
4579
- const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
4661
+ const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
4580
4662
  if (data.memoryId) {
4581
4663
  const mem = await kv.get(KV.memories, data.memoryId);
4582
4664
  await kv.delete(KV.memories, data.memoryId);
@@ -4635,7 +4717,7 @@ const DEFAULTS$1 = {
4635
4717
  function registerEvictFunction(sdk, kv) {
4636
4718
  sdk.registerFunction("mem::evict", async (data) => {
4637
4719
  const dryRun = data?.dryRun ?? false;
4638
- const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
4720
+ const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
4639
4721
  const configOverride = await kv.get(KV.config, "eviction").catch(() => null);
4640
4722
  const cfg = {
4641
4723
  ...DEFAULTS$1,
@@ -5238,7 +5320,7 @@ function registerAutoForgetFunction(sdk, kv) {
5238
5320
  sdk.registerFunction("mem::auto-forget", async (data) => {
5239
5321
  const dryRun = data?.dryRun ?? false;
5240
5322
  const now = Date.now();
5241
- const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
5323
+ const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
5242
5324
  const result = {
5243
5325
  ttlExpired: [],
5244
5326
  contradictions: [],
@@ -5471,7 +5553,8 @@ function registerExportImportFunction(sdk, kv) {
5471
5553
  "0.9.1",
5472
5554
  "0.9.2",
5473
5555
  "0.9.3",
5474
- "0.9.4"
5556
+ "0.9.4",
5557
+ "0.9.5"
5475
5558
  ]).has(importData.version)) return {
5476
5559
  success: false,
5477
5560
  error: `Unsupported export version: ${importData.version}`
@@ -11828,7 +11911,7 @@ function registerRetentionFunctions(sdk, kv) {
11828
11911
  const threshold = typeof data?.threshold === "number" && Number.isFinite(data.threshold) ? data.threshold : DEFAULT_DECAY.tierThresholds.cold;
11829
11912
  const maxEvictRaw = typeof data?.maxEvict === "number" && Number.isInteger(data.maxEvict) ? data.maxEvict : 50;
11830
11913
  const maxEvict = Math.min(1e3, Math.max(0, maxEvictRaw));
11831
- const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
11914
+ const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
11832
11915
  const candidates = (await kv.list(KV.retentionScores)).filter((s) => s.score < threshold).sort((a, b) => a.score - b.score).slice(0, maxEvict);
11833
11916
  if (data?.dryRun) return {
11834
11917
  success: true,
@@ -17953,6 +18036,7 @@ async function main() {
17953
18036
  console.log(`[agentmemory] Streams: ws://localhost:${config.streamsPort}`);
17954
18037
  const sdk = registerWorker(config.engineUrl, {
17955
18038
  workerName: "agentmemory",
18039
+ invocationTimeoutMs: 18e4,
17956
18040
  otel: {
17957
18041
  serviceName: OTEL_CONFIG.serviceName,
17958
18042
  serviceVersion: OTEL_CONFIG.serviceVersion,
@@ -18061,8 +18145,20 @@ async function main() {
18061
18145
  console.log(`[agentmemory] Loaded persisted BM25 index (${bm25Index.size} docs)`);
18062
18146
  }
18063
18147
  if (loaded?.vector && vectorIndex && loaded.vector.size > 0) {
18064
- vectorIndex.restoreFrom(loaded.vector);
18065
- console.log(`[agentmemory] Loaded persisted vector index (${vectorIndex.size} vectors)`);
18148
+ const activeDim = embeddingProvider?.dimensions ?? 0;
18149
+ const { mismatches, seenDimensions } = activeDim > 0 ? loaded.vector.validateDimensions(activeDim) : {
18150
+ mismatches: [],
18151
+ seenDimensions: /* @__PURE__ */ new Set()
18152
+ };
18153
+ if (mismatches.length > 0) {
18154
+ const sample = mismatches.slice(0, 5).map((m) => `${m.obsId} (dim=${m.dim})`).join(", ");
18155
+ const distinct = Array.from(seenDimensions).sort((a, b) => a - b).join(", ");
18156
+ 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.`);
18157
+ 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.`);
18158
+ } else {
18159
+ vectorIndex.restoreFrom(loaded.vector);
18160
+ console.log(`[agentmemory] Loaded persisted vector index (${vectorIndex.size} vectors)`);
18161
+ }
18066
18162
  }
18067
18163
  if (bm25Index.size === 0) {
18068
18164
  const indexCount = await rebuildIndex(kv).catch((err) => {
@@ -18070,9 +18166,36 @@ async function main() {
18070
18166
  return 0;
18071
18167
  });
18072
18168
  if (indexCount > 0) {
18073
- console.log(`[agentmemory] Search index rebuilt: ${indexCount} observations`);
18169
+ console.log(`[agentmemory] Search index rebuilt: ${indexCount} entries`);
18074
18170
  indexPersistence.scheduleSave();
18075
18171
  }
18172
+ } else try {
18173
+ const memories = await kv.list(KV.memories);
18174
+ let backfilled = 0;
18175
+ for (const memory of memories) {
18176
+ if (memory.isLatest === false) continue;
18177
+ if (!memory.title || !memory.content) continue;
18178
+ if (bm25Index.has(memory.id)) continue;
18179
+ bm25Index.add({
18180
+ id: memory.id,
18181
+ sessionId: memory.sessionIds[0] ?? "memory",
18182
+ timestamp: memory.createdAt,
18183
+ type: "decision",
18184
+ title: memory.title,
18185
+ facts: [memory.content],
18186
+ narrative: memory.content,
18187
+ concepts: memory.concepts,
18188
+ files: memory.files,
18189
+ importance: memory.strength
18190
+ });
18191
+ backfilled++;
18192
+ }
18193
+ if (backfilled > 0) {
18194
+ console.log(`[agentmemory] Backfilled ${backfilled} memories into BM25 (legacy gap before #257)`);
18195
+ indexPersistence.scheduleSave();
18196
+ }
18197
+ } catch (err) {
18198
+ console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
18076
18199
  }
18077
18200
  console.log(`[agentmemory] Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
18078
18201
  console.log(`[agentmemory] Endpoints: 107 REST + ${getAllTools().length} MCP tools + 6 MCP resources + 3 MCP prompts`);
@@ -18142,4 +18265,4 @@ main().catch((err) => {
18142
18265
 
18143
18266
  //#endregion
18144
18267
  export { deleteImage as a, saveImageToDisk as c, IMAGES_DIR as i, touchImage as l, getImageRefCount as n, getMaxBytes as o, incrementImageRef as r, isManagedImagePath as s, decrementImageRef as t };
18145
- //# sourceMappingURL=src-uDy2jLO-.mjs.map
18268
+ //# sourceMappingURL=src-xYHSzz5S.mjs.map