@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.
Files changed (38) hide show
  1. package/README.md +108 -50
  2. package/dist/cli.mjs +60 -20
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/docker-compose.yml +10 -1
  5. package/dist/hooks/session-end.mjs +4 -4
  6. package/dist/hooks/session-end.mjs.map +1 -1
  7. package/dist/hooks/session-start.mjs +23 -10
  8. package/dist/hooks/session-start.mjs.map +1 -1
  9. package/dist/hooks/stop.mjs +1 -1
  10. package/dist/hooks/stop.mjs.map +1 -1
  11. package/dist/hooks/subagent-start.mjs +17 -18
  12. package/dist/hooks/subagent-start.mjs.map +1 -1
  13. package/dist/image-refs-CZVd2z6E.mjs +3 -0
  14. package/dist/{image-store-Cn9eD-7D.mjs → image-store-CF4gfkLr.mjs} +1 -1
  15. package/dist/index.mjs +205 -82
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/{src-uDy2jLO-.mjs → src-C7vGxttN.mjs} +147 -24
  18. package/dist/src-C7vGxttN.mjs.map +1 -0
  19. package/dist/{standalone-CqqEcfNR.mjs → standalone-DnSJzyXL.mjs} +36 -4
  20. package/dist/{standalone-CqqEcfNR.mjs.map → standalone-DnSJzyXL.mjs.map} +1 -1
  21. package/dist/standalone.d.mts.map +1 -1
  22. package/dist/standalone.mjs +35 -3
  23. package/dist/standalone.mjs.map +1 -1
  24. package/dist/{tools-registry-Co8VIL4t.mjs → tools-registry-CKMeHaPN.mjs} +2 -2
  25. package/dist/{tools-registry-Co8VIL4t.mjs.map → tools-registry-CKMeHaPN.mjs.map} +1 -1
  26. package/docker-compose.yml +10 -1
  27. package/package.json +1 -1
  28. package/plugin/.claude-plugin/plugin.json +1 -1
  29. package/plugin/scripts/session-end.mjs +4 -4
  30. package/plugin/scripts/session-end.mjs.map +1 -1
  31. package/plugin/scripts/session-start.mjs +23 -10
  32. package/plugin/scripts/session-start.mjs.map +1 -1
  33. package/plugin/scripts/stop.mjs +1 -1
  34. package/plugin/scripts/stop.mjs.map +1 -1
  35. package/plugin/scripts/subagent-start.mjs +17 -18
  36. package/plugin/scripts/subagent-start.mjs.map +1 -1
  37. package/dist/image-refs-BfT7XAa-.mjs +0 -3
  38. package/dist/src-uDy2jLO-.mjs.map +0 -1
@@ -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-CKMeHaPN.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
  }
@@ -1004,6 +1036,23 @@ var VectorIndex = class VectorIndex {
1004
1036
  }
1005
1037
  };
1006
1038
 
1039
+ //#endregion
1040
+ //#region src/state/memory-utils.ts
1041
+ function memoryToObservation(memory) {
1042
+ return {
1043
+ id: memory.id,
1044
+ sessionId: memory.sessionIds[0] ?? "memory",
1045
+ timestamp: memory.createdAt,
1046
+ type: "decision",
1047
+ title: memory.title,
1048
+ facts: [memory.content],
1049
+ narrative: memory.content,
1050
+ concepts: memory.concepts,
1051
+ files: memory.files,
1052
+ importance: memory.strength
1053
+ };
1054
+ }
1055
+
1007
1056
  //#endregion
1008
1057
  //#region src/functions/graph-retrieval.ts
1009
1058
  function buildGraphContext(path) {
@@ -1568,7 +1617,12 @@ var HybridSearch = class {
1568
1617
  }
1569
1618
  async enrichResults(results, limit) {
1570
1619
  const sliced = results.slice(0, limit);
1571
- const observations = await Promise.all(sliced.map((r) => this.kv.get(KV.observations(r.sessionId), r.obsId).catch(() => null)));
1620
+ const observations = await Promise.all(sliced.map(async (r) => {
1621
+ const obs = await this.kv.get(KV.observations(r.sessionId), r.obsId).catch(() => null);
1622
+ if (obs) return obs;
1623
+ const mem = await this.kv.get(KV.memories, r.obsId).catch(() => null);
1624
+ return mem ? memoryToObservation(mem) : null;
1625
+ }));
1572
1626
  const enriched = [];
1573
1627
  for (let i = 0; i < sliced.length; i++) {
1574
1628
  const obs = observations[i];
@@ -1875,6 +1929,9 @@ var SearchIndex = class SearchIndex {
1875
1929
  }
1876
1930
  this.sortedTerms = null;
1877
1931
  }
1932
+ has(id) {
1933
+ return this.entries.has(id);
1934
+ }
1878
1935
  search(query, limit = 20) {
1879
1936
  const rawTerms = this.tokenize(query.toLowerCase());
1880
1937
  if (rawTerms.length === 0) return [];
@@ -2290,9 +2347,20 @@ function getSearchIndex() {
2290
2347
  async function rebuildIndex(kv) {
2291
2348
  const idx = getSearchIndex();
2292
2349
  idx.clear();
2293
- const sessions = await kv.list(KV.sessions);
2294
- if (!sessions.length) return 0;
2295
2350
  let count = 0;
2351
+ try {
2352
+ const memories = await kv.list(KV.memories);
2353
+ for (const memory of memories) {
2354
+ if (memory.isLatest === false) continue;
2355
+ if (!memory.title || !memory.content) continue;
2356
+ idx.add(memoryToObservation(memory));
2357
+ count++;
2358
+ }
2359
+ } catch (err) {
2360
+ logger.warn("rebuildIndex: failed to load memories", { error: err instanceof Error ? err.message : String(err) });
2361
+ }
2362
+ const sessions = await kv.list(KV.sessions);
2363
+ if (!sessions.length) return count;
2296
2364
  const obsPerSession = [];
2297
2365
  const failedSessions = [];
2298
2366
  for (let batch = 0; batch < sessions.length; batch += 10) {
@@ -2363,7 +2431,12 @@ function registerSearchFunction(sdk, kv) {
2363
2431
  }
2364
2432
  candidates.push(r);
2365
2433
  }
2366
- const obsResults = await Promise.all(candidates.map((r) => kv.get(KV.observations(r.sessionId), r.obsId)));
2434
+ const obsResults = await Promise.all(candidates.map(async (r) => {
2435
+ const obs = await kv.get(KV.observations(r.sessionId), r.obsId).catch(() => null);
2436
+ if (obs) return obs;
2437
+ const mem = await kv.get(KV.memories, r.obsId).catch(() => null);
2438
+ return mem ? memoryToObservation(mem) : null;
2439
+ }));
2367
2440
  const enriched = [];
2368
2441
  for (let i = 0; i < candidates.length; i++) {
2369
2442
  const obs = obsResults[i];
@@ -2527,10 +2600,10 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
2527
2600
  };
2528
2601
  }
2529
2602
  if (pendingImageData && (pendingImageData.startsWith("data:image/") || pendingImageData.startsWith("iVBORw0KGgo") || pendingImageData.startsWith("/9j/"))) {
2530
- const { saveImageToDisk } = await import("./image-store-Cn9eD-7D.mjs");
2603
+ const { saveImageToDisk } = await import("./image-store-CF4gfkLr.mjs");
2531
2604
  const { filePath, bytesWritten } = await saveImageToDisk(pendingImageData);
2532
2605
  raw.imageData = filePath;
2533
- const { incrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
2606
+ const { incrementImageRef } = await import("./image-refs-CZVd2z6E.mjs");
2534
2607
  await incrementImageRef(kv, filePath);
2535
2608
  sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: bytesWritten });
2536
2609
  if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] === "true") sdk.triggerVoid("mem::vision-embed", {
@@ -2543,7 +2616,7 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
2543
2616
  await kv.set(KV.observations(payload.sessionId), obsId, raw);
2544
2617
  } catch (error) {
2545
2618
  if (raw.imageData) {
2546
- const { deleteImage } = await import("./image-store-Cn9eD-7D.mjs");
2619
+ const { deleteImage } = await import("./image-store-CF4gfkLr.mjs");
2547
2620
  const { deletedBytes } = await deleteImage(raw.imageData);
2548
2621
  if (deletedBytes > 0) sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: -deletedBytes });
2549
2622
  }
@@ -4556,6 +4629,14 @@ function registerRememberFunction(sdk, kv) {
4556
4629
  await kv.set(KV.memories, supersededMemory.id, supersededMemory);
4557
4630
  }
4558
4631
  await kv.set(KV.memories, memory.id, memory);
4632
+ try {
4633
+ getSearchIndex().add(memoryToObservation(memory));
4634
+ } catch (err) {
4635
+ logger.warn("Failed to index saved memory into BM25", {
4636
+ memId: memory.id,
4637
+ error: err instanceof Error ? err.message : String(err)
4638
+ });
4639
+ }
4559
4640
  if (supersededId) await sdk.trigger({
4560
4641
  function_id: "mem::cascade-update",
4561
4642
  payload: { supersededMemoryId: supersededId },
@@ -4576,7 +4657,7 @@ function registerRememberFunction(sdk, kv) {
4576
4657
  const deletedMemoryIds = [];
4577
4658
  const deletedObservationIds = [];
4578
4659
  let deletedSession = false;
4579
- const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
4660
+ const { decrementImageRef } = await import("./image-refs-CZVd2z6E.mjs");
4580
4661
  if (data.memoryId) {
4581
4662
  const mem = await kv.get(KV.memories, data.memoryId);
4582
4663
  await kv.delete(KV.memories, data.memoryId);
@@ -4635,7 +4716,7 @@ const DEFAULTS$1 = {
4635
4716
  function registerEvictFunction(sdk, kv) {
4636
4717
  sdk.registerFunction("mem::evict", async (data) => {
4637
4718
  const dryRun = data?.dryRun ?? false;
4638
- const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
4719
+ const { decrementImageRef } = await import("./image-refs-CZVd2z6E.mjs");
4639
4720
  const configOverride = await kv.get(KV.config, "eviction").catch(() => null);
4640
4721
  const cfg = {
4641
4722
  ...DEFAULTS$1,
@@ -5238,7 +5319,7 @@ function registerAutoForgetFunction(sdk, kv) {
5238
5319
  sdk.registerFunction("mem::auto-forget", async (data) => {
5239
5320
  const dryRun = data?.dryRun ?? false;
5240
5321
  const now = Date.now();
5241
- const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
5322
+ const { decrementImageRef } = await import("./image-refs-CZVd2z6E.mjs");
5242
5323
  const result = {
5243
5324
  ttlExpired: [],
5244
5325
  contradictions: [],
@@ -5471,7 +5552,9 @@ function registerExportImportFunction(sdk, kv) {
5471
5552
  "0.9.1",
5472
5553
  "0.9.2",
5473
5554
  "0.9.3",
5474
- "0.9.4"
5555
+ "0.9.4",
5556
+ "0.9.5",
5557
+ "0.9.6"
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-CZVd2z6E.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-C7vGxttN.mjs.map