@agentmemory/agentmemory 0.9.3 → 0.9.4

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 loadTeamConfig, a as getConsolidationDecayDays, c as isAutoCompressEnabled, d as isGraphExtractionEnabled, f as loadClaudeBridgeConfig, g as loadSnapshotConfig, h as loadFallbackConfig, i as detectEmbeddingProvider, l as isConsolidationEnabled, m as loadEmbeddingConfig, n as getVisibleTools, o as getEnvVar, p as loadConfig, r as VERSION, t as getAllTools, u as isContextInjectionEnabled } from "./tools-registry-m8Ofn9vV.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";
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";
@@ -2018,8 +2018,10 @@ var SearchIndex = class SearchIndex {
2018
2018
  //#endregion
2019
2019
  //#region src/state/index-persistence.ts
2020
2020
  const DEBOUNCE_MS = 5e3;
2021
+ const FAILURE_LOG_THROTTLE_MS = 6e4;
2021
2022
  var IndexPersistence = class {
2022
2023
  timer = null;
2024
+ lastFailureLogAt = 0;
2023
2025
  constructor(kv, bm25, vector) {
2024
2026
  this.kv = kv;
2025
2027
  this.bm25 = bm25;
@@ -2027,15 +2029,21 @@ var IndexPersistence = class {
2027
2029
  }
2028
2030
  scheduleSave() {
2029
2031
  if (this.timer) clearTimeout(this.timer);
2030
- this.timer = setTimeout(() => this.save(), DEBOUNCE_MS);
2032
+ this.timer = setTimeout(() => {
2033
+ this.save().catch((err) => this.logFailure(err));
2034
+ }, DEBOUNCE_MS);
2031
2035
  }
2032
2036
  async save() {
2033
2037
  if (this.timer) {
2034
2038
  clearTimeout(this.timer);
2035
2039
  this.timer = null;
2036
2040
  }
2037
- await this.kv.set(KV.bm25Index, "data", this.bm25.serialize());
2038
- if (this.vector && this.vector.size > 0) await this.kv.set(KV.bm25Index, "vectors", this.vector.serialize());
2041
+ try {
2042
+ await this.kv.set(KV.bm25Index, "data", this.bm25.serialize());
2043
+ if (this.vector && this.vector.size > 0) await this.kv.set(KV.bm25Index, "vectors", this.vector.serialize());
2044
+ } catch (err) {
2045
+ this.logFailure(err);
2046
+ }
2039
2047
  }
2040
2048
  async load() {
2041
2049
  let bm25 = null;
@@ -2055,6 +2063,18 @@ var IndexPersistence = class {
2055
2063
  this.timer = null;
2056
2064
  }
2057
2065
  }
2066
+ logFailure(err) {
2067
+ const now = Date.now();
2068
+ if (now - this.lastFailureLogAt < FAILURE_LOG_THROTTLE_MS) return;
2069
+ this.lastFailureLogAt = now;
2070
+ const code = err?.code;
2071
+ const message = err instanceof Error ? err.message : String(err);
2072
+ logger.warn("index persistence: failed to save BM25/vector index", {
2073
+ code,
2074
+ message,
2075
+ hint: code === "TIMEOUT" ? "iii-engine state::set timed out; recent index updates remain in memory and will retry on the next debounce flush" : void 0
2076
+ });
2077
+ }
2058
2078
  };
2059
2079
 
2060
2080
  //#endregion
@@ -2507,10 +2527,10 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
2507
2527
  };
2508
2528
  }
2509
2529
  if (pendingImageData && (pendingImageData.startsWith("data:image/") || pendingImageData.startsWith("iVBORw0KGgo") || pendingImageData.startsWith("/9j/"))) {
2510
- const { saveImageToDisk } = await import("./image-store-DGvZMMrI.mjs");
2530
+ const { saveImageToDisk } = await import("./image-store-Cn9eD-7D.mjs");
2511
2531
  const { filePath, bytesWritten } = await saveImageToDisk(pendingImageData);
2512
2532
  raw.imageData = filePath;
2513
- const { incrementImageRef } = await import("./image-refs-CESf9ndJ.mjs");
2533
+ const { incrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
2514
2534
  await incrementImageRef(kv, filePath);
2515
2535
  sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: bytesWritten });
2516
2536
  if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] === "true") sdk.triggerVoid("mem::vision-embed", {
@@ -2523,7 +2543,7 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
2523
2543
  await kv.set(KV.observations(payload.sessionId), obsId, raw);
2524
2544
  } catch (error) {
2525
2545
  if (raw.imageData) {
2526
- const { deleteImage } = await import("./image-store-DGvZMMrI.mjs");
2546
+ const { deleteImage } = await import("./image-store-Cn9eD-7D.mjs");
2527
2547
  const { deletedBytes } = await deleteImage(raw.imageData);
2528
2548
  if (deletedBytes > 0) sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: -deletedBytes });
2529
2549
  }
@@ -4556,7 +4576,7 @@ function registerRememberFunction(sdk, kv) {
4556
4576
  const deletedMemoryIds = [];
4557
4577
  const deletedObservationIds = [];
4558
4578
  let deletedSession = false;
4559
- const { decrementImageRef } = await import("./image-refs-CESf9ndJ.mjs");
4579
+ const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
4560
4580
  if (data.memoryId) {
4561
4581
  const mem = await kv.get(KV.memories, data.memoryId);
4562
4582
  await kv.delete(KV.memories, data.memoryId);
@@ -4615,7 +4635,7 @@ const DEFAULTS$1 = {
4615
4635
  function registerEvictFunction(sdk, kv) {
4616
4636
  sdk.registerFunction("mem::evict", async (data) => {
4617
4637
  const dryRun = data?.dryRun ?? false;
4618
- const { decrementImageRef } = await import("./image-refs-CESf9ndJ.mjs");
4638
+ const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
4619
4639
  const configOverride = await kv.get(KV.config, "eviction").catch(() => null);
4620
4640
  const cfg = {
4621
4641
  ...DEFAULTS$1,
@@ -5218,7 +5238,7 @@ function registerAutoForgetFunction(sdk, kv) {
5218
5238
  sdk.registerFunction("mem::auto-forget", async (data) => {
5219
5239
  const dryRun = data?.dryRun ?? false;
5220
5240
  const now = Date.now();
5221
- const { decrementImageRef } = await import("./image-refs-CESf9ndJ.mjs");
5241
+ const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
5222
5242
  const result = {
5223
5243
  ttlExpired: [],
5224
5244
  contradictions: [],
@@ -5450,7 +5470,8 @@ function registerExportImportFunction(sdk, kv) {
5450
5470
  "0.9.0",
5451
5471
  "0.9.1",
5452
5472
  "0.9.2",
5453
- "0.9.3"
5473
+ "0.9.3",
5474
+ "0.9.4"
5454
5475
  ]).has(importData.version)) return {
5455
5476
  success: false,
5456
5477
  error: `Unsupported export version: ${importData.version}`
@@ -11807,7 +11828,7 @@ function registerRetentionFunctions(sdk, kv) {
11807
11828
  const threshold = typeof data?.threshold === "number" && Number.isFinite(data.threshold) ? data.threshold : DEFAULT_DECAY.tierThresholds.cold;
11808
11829
  const maxEvictRaw = typeof data?.maxEvict === "number" && Number.isInteger(data.maxEvict) ? data.maxEvict : 50;
11809
11830
  const maxEvict = Math.min(1e3, Math.max(0, maxEvictRaw));
11810
- const { decrementImageRef } = await import("./image-refs-CESf9ndJ.mjs");
11831
+ const { decrementImageRef } = await import("./image-refs-BfT7XAa-.mjs");
11811
11832
  const candidates = (await kv.list(KV.retentionScores)).filter((s) => s.score < threshold).sort((a, b) => a.score - b.score).slice(0, maxEvict);
11812
11833
  if (data?.dryRun) return {
11813
11834
  success: true,
@@ -12068,7 +12089,7 @@ function parseJsonlText(text, fallbackSessionId) {
12068
12089
  const parsed = JSON.parse(line);
12069
12090
  if (parsed && typeof parsed === "object") entries.push(parsed);
12070
12091
  } catch {}
12071
- let sessionId = fallbackSessionId || "";
12092
+ let sessionId = "";
12072
12093
  let cwd = "";
12073
12094
  let firstTs = "";
12074
12095
  let lastTs = "";
@@ -12129,7 +12150,7 @@ function parseJsonlText(text, fallbackSessionId) {
12129
12150
  });
12130
12151
  } else if (entry.type === "summary" || entry.type === "system") {}
12131
12152
  }
12132
- const effectiveSessionId = sessionId || generateId("sess");
12153
+ const effectiveSessionId = sessionId || fallbackSessionId || generateId("sess");
12133
12154
  for (const obs of observations) if (obs.sessionId === "imported") obs.sessionId = effectiveSessionId;
12134
12155
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
12135
12156
  return {
@@ -12239,6 +12260,8 @@ function projectTimeline(observations) {
12239
12260
 
12240
12261
  //#endregion
12241
12262
  //#region src/functions/replay.ts
12263
+ const MAX_FILES_DEFAULT = 200;
12264
+ const MAX_FILES_UPPER_BOUND = 1e3;
12242
12265
  const SENSITIVE_PATH_PATTERNS = [
12243
12266
  /(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
12244
12267
  /(^|[\\/_.-])credentials?([\\/_.-]|$)/i,
@@ -12373,8 +12396,11 @@ async function loadObservations(kv, sessionId) {
12373
12396
  }
12374
12397
  async function findJsonlFiles(root, limit = 200) {
12375
12398
  const out = [];
12399
+ let discovered = 0;
12400
+ let walked = 0;
12401
+ const traversalCap = Math.max(limit * 50, 5e4);
12376
12402
  async function walk(dir) {
12377
- if (out.length >= limit) return;
12403
+ if (walked >= traversalCap) return;
12378
12404
  let names;
12379
12405
  try {
12380
12406
  names = await readdir(dir);
@@ -12382,7 +12408,8 @@ async function findJsonlFiles(root, limit = 200) {
12382
12408
  return;
12383
12409
  }
12384
12410
  for (const name of names) {
12385
- if (out.length >= limit) return;
12411
+ if (walked >= traversalCap) return;
12412
+ walked++;
12386
12413
  const full = join(dir, name);
12387
12414
  let st;
12388
12415
  try {
@@ -12392,11 +12419,20 @@ async function findJsonlFiles(root, limit = 200) {
12392
12419
  }
12393
12420
  if (st.isSymbolicLink()) continue;
12394
12421
  if (st.isDirectory()) await walk(full);
12395
- else if (st.isFile() && name.endsWith(".jsonl")) out.push(full);
12422
+ else if (st.isFile() && name.endsWith(".jsonl")) {
12423
+ discovered++;
12424
+ if (out.length < limit) out.push(full);
12425
+ }
12396
12426
  }
12397
12427
  }
12398
12428
  await walk(root);
12399
- return out;
12429
+ const traversalCapped = walked >= traversalCap;
12430
+ return {
12431
+ files: out,
12432
+ truncated: discovered > out.length || traversalCapped,
12433
+ discovered,
12434
+ traversalCapped
12435
+ };
12400
12436
  }
12401
12437
  function registerReplayFunctions(sdk, kv) {
12402
12438
  sdk.registerFunction("mem::replay::load", async (data) => {
@@ -12444,10 +12480,21 @@ function registerReplayFunctions(sdk, kv) {
12444
12480
  error: "path not found"
12445
12481
  };
12446
12482
  }
12483
+ const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) : MAX_FILES_DEFAULT;
12447
12484
  let files = [];
12448
- if (stat.isDirectory()) files = await findJsonlFiles(abs, data.maxFiles || 200);
12449
- else if (stat.isFile() && abs.endsWith(".jsonl")) files = [abs];
12450
- else return {
12485
+ let truncated = false;
12486
+ let discovered = 0;
12487
+ let traversalCapped = false;
12488
+ if (stat.isDirectory()) {
12489
+ const found = await findJsonlFiles(abs, maxFiles);
12490
+ files = found.files;
12491
+ truncated = found.truncated;
12492
+ discovered = found.discovered;
12493
+ traversalCapped = found.traversalCapped;
12494
+ } else if (stat.isFile() && abs.endsWith(".jsonl")) {
12495
+ files = [abs];
12496
+ discovered = 1;
12497
+ } else return {
12451
12498
  success: false,
12452
12499
  error: "path must be a .jsonl file or directory"
12453
12500
  };
@@ -12455,7 +12502,12 @@ function registerReplayFunctions(sdk, kv) {
12455
12502
  success: true,
12456
12503
  imported: 0,
12457
12504
  sessionIds: [],
12458
- observations: 0
12505
+ observations: 0,
12506
+ discovered,
12507
+ truncated,
12508
+ traversalCapped,
12509
+ maxFiles,
12510
+ maxFilesUpperBound: MAX_FILES_UPPER_BOUND
12459
12511
  };
12460
12512
  const sessionIds = [];
12461
12513
  let observationCount = 0;
@@ -12521,7 +12573,12 @@ function registerReplayFunctions(sdk, kv) {
12521
12573
  success: true,
12522
12574
  imported: files.length,
12523
12575
  sessionIds,
12524
- observations: observationCount
12576
+ observations: observationCount,
12577
+ discovered,
12578
+ truncated,
12579
+ traversalCapped,
12580
+ maxFiles,
12581
+ maxFilesUpperBound: MAX_FILES_UPPER_BOUND
12525
12582
  };
12526
12583
  });
12527
12584
  }
@@ -12824,12 +12881,11 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
12824
12881
  sdk.registerFunction("api::config-flags", async (req) => {
12825
12882
  const authErr = checkAuth(req, secret);
12826
12883
  if (authErr) return authErr;
12827
- const env = process.env;
12828
12884
  return {
12829
12885
  status_code: 200,
12830
12886
  body: {
12831
12887
  version: VERSION,
12832
- provider: env["ANTHROPIC_API_KEY"] || env["GEMINI_API_KEY"] || env["OPENROUTER_API_KEY"] || env["MINIMAX_API_KEY"] ? "llm" : "noop",
12888
+ provider: detectLlmProviderKind(),
12833
12889
  embeddingProvider: detectEmbeddingProvider() ? "embeddings" : "none",
12834
12890
  flags: [
12835
12891
  {
@@ -13127,11 +13183,12 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
13127
13183
  payload.path = body.path.trim();
13128
13184
  }
13129
13185
  if (body.maxFiles !== void 0) {
13130
- if (!Number.isInteger(body.maxFiles) || body.maxFiles < 1) return {
13186
+ const n = body.maxFiles;
13187
+ if (!Number.isInteger(n) || n < 1 || n > MAX_FILES_UPPER_BOUND) return {
13131
13188
  status_code: 400,
13132
- body: { error: "maxFiles must be a positive integer" }
13189
+ body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
13133
13190
  };
13134
- payload.maxFiles = body.maxFiles;
13191
+ payload.maxFiles = n;
13135
13192
  }
13136
13193
  return {
13137
13194
  status_code: 202,
@@ -14047,6 +14104,32 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
14047
14104
  http_method: "GET"
14048
14105
  }
14049
14106
  });
14107
+ sdk.registerFunction("api::memory-by-id", async (req) => {
14108
+ const authErr = checkAuth(req, secret);
14109
+ if (authErr) return authErr;
14110
+ const id = req.path_params?.["id"];
14111
+ if (!id || typeof id !== "string") return {
14112
+ status_code: 400,
14113
+ body: { error: "id path parameter is required" }
14114
+ };
14115
+ const memory = await kv.get(KV.memories, id);
14116
+ if (!memory) return {
14117
+ status_code: 404,
14118
+ body: { error: `memory not found: ${id}` }
14119
+ };
14120
+ return {
14121
+ status_code: 200,
14122
+ body: { memory }
14123
+ };
14124
+ });
14125
+ sdk.registerTrigger({
14126
+ type: "http",
14127
+ function_id: "api::memory-by-id",
14128
+ config: {
14129
+ api_path: "/agentmemory/memories/:id",
14130
+ http_method: "GET"
14131
+ }
14132
+ });
14050
14133
  sdk.registerFunction("api::semantic-list", async (req) => {
14051
14134
  const authErr = checkAuth(req, secret);
14052
14135
  if (authErr) return authErr;
@@ -15925,6 +16008,15 @@ function registerEventTriggers(sdk, kv) {
15925
16008
  error: err instanceof Error ? err.message : String(err)
15926
16009
  });
15927
16010
  }
16011
+ if (isGraphExtractionEnabled()) try {
16012
+ const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
16013
+ if (compressed.length > 0) sdk.triggerVoid("mem::graph-extract", { observations: compressed });
16014
+ } catch (err) {
16015
+ logger.warn("graph-extract triggerVoid failed", {
16016
+ sessionId: data.sessionId,
16017
+ error: err instanceof Error ? err.message : String(err)
16018
+ });
16019
+ }
15928
16020
  return summary;
15929
16021
  });
15930
16022
  sdk.registerTrigger({
@@ -17836,6 +17928,14 @@ function initMetrics(getMeter) {
17836
17928
  function hasGetMeter(sdk) {
17837
17929
  return typeof sdk === "object" && sdk !== null && "getMeter" in sdk && typeof sdk.getMeter === "function";
17838
17930
  }
17931
+ let lastUnhandledLogAt = 0;
17932
+ process.on("unhandledRejection", (reason) => {
17933
+ const now = Date.now();
17934
+ if (now - lastUnhandledLogAt < 6e4) return;
17935
+ lastUnhandledLogAt = now;
17936
+ const r = reason;
17937
+ console.warn(`[agentmemory] unhandledRejection (suppressed):`, r?.code ? `${r.code} ${r.function_id ?? ""} ${r.message ?? ""}`.trim() : reason);
17938
+ });
17839
17939
  async function main() {
17840
17940
  const config = loadConfig();
17841
17941
  const embeddingConfig = loadEmbeddingConfig();
@@ -18042,4 +18142,4 @@ main().catch((err) => {
18042
18142
 
18043
18143
  //#endregion
18044
18144
  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 };
18045
- //# sourceMappingURL=src-3Snd7D3T.mjs.map
18145
+ //# sourceMappingURL=src-uDy2jLO-.mjs.map