@agentmemory/agentmemory 0.9.3 → 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 +116 -53
- package/dist/cli.mjs +163 -23
- 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-DGvZMMrI.mjs → image-store-DnuCI2RB.mjs} +1 -1
- package/dist/index.mjs +338 -103
- package/dist/index.mjs.map +1 -1
- package/dist/{src-3Snd7D3T.mjs → src-xYHSzz5S.mjs} +264 -41
- package/dist/src-xYHSzz5S.mjs.map +1 -0
- package/dist/{standalone-BG9uPsDK.mjs → standalone-BvKacAId.mjs} +2 -2
- package/dist/{standalone-BG9uPsDK.mjs.map → standalone-BvKacAId.mjs.map} +1 -1
- package/dist/standalone.mjs +9 -2
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-m8Ofn9vV.mjs → tools-registry-BWM0vWeA.mjs} +16 -4
- package/dist/tools-registry-BWM0vWeA.mjs.map +1 -0
- 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-CESf9ndJ.mjs +0 -3
- package/dist/src-3Snd7D3T.mjs.map +0 -1
- package/dist/tools-registry-m8Ofn9vV.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
|
|
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 [];
|
|
@@ -2018,8 +2053,10 @@ var SearchIndex = class SearchIndex {
|
|
|
2018
2053
|
//#endregion
|
|
2019
2054
|
//#region src/state/index-persistence.ts
|
|
2020
2055
|
const DEBOUNCE_MS = 5e3;
|
|
2056
|
+
const FAILURE_LOG_THROTTLE_MS = 6e4;
|
|
2021
2057
|
var IndexPersistence = class {
|
|
2022
2058
|
timer = null;
|
|
2059
|
+
lastFailureLogAt = 0;
|
|
2023
2060
|
constructor(kv, bm25, vector) {
|
|
2024
2061
|
this.kv = kv;
|
|
2025
2062
|
this.bm25 = bm25;
|
|
@@ -2027,15 +2064,21 @@ var IndexPersistence = class {
|
|
|
2027
2064
|
}
|
|
2028
2065
|
scheduleSave() {
|
|
2029
2066
|
if (this.timer) clearTimeout(this.timer);
|
|
2030
|
-
this.timer = setTimeout(() =>
|
|
2067
|
+
this.timer = setTimeout(() => {
|
|
2068
|
+
this.save().catch((err) => this.logFailure(err));
|
|
2069
|
+
}, DEBOUNCE_MS);
|
|
2031
2070
|
}
|
|
2032
2071
|
async save() {
|
|
2033
2072
|
if (this.timer) {
|
|
2034
2073
|
clearTimeout(this.timer);
|
|
2035
2074
|
this.timer = null;
|
|
2036
2075
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
2076
|
+
try {
|
|
2077
|
+
await this.kv.set(KV.bm25Index, "data", this.bm25.serialize());
|
|
2078
|
+
if (this.vector && this.vector.size > 0) await this.kv.set(KV.bm25Index, "vectors", this.vector.serialize());
|
|
2079
|
+
} catch (err) {
|
|
2080
|
+
this.logFailure(err);
|
|
2081
|
+
}
|
|
2039
2082
|
}
|
|
2040
2083
|
async load() {
|
|
2041
2084
|
let bm25 = null;
|
|
@@ -2055,6 +2098,18 @@ var IndexPersistence = class {
|
|
|
2055
2098
|
this.timer = null;
|
|
2056
2099
|
}
|
|
2057
2100
|
}
|
|
2101
|
+
logFailure(err) {
|
|
2102
|
+
const now = Date.now();
|
|
2103
|
+
if (now - this.lastFailureLogAt < FAILURE_LOG_THROTTLE_MS) return;
|
|
2104
|
+
this.lastFailureLogAt = now;
|
|
2105
|
+
const code = err?.code;
|
|
2106
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2107
|
+
logger.warn("index persistence: failed to save BM25/vector index", {
|
|
2108
|
+
code,
|
|
2109
|
+
message,
|
|
2110
|
+
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
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2058
2113
|
};
|
|
2059
2114
|
|
|
2060
2115
|
//#endregion
|
|
@@ -2262,6 +2317,20 @@ async function deleteAccessLog(kv, memoryId) {
|
|
|
2262
2317
|
|
|
2263
2318
|
//#endregion
|
|
2264
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
|
+
}
|
|
2265
2334
|
let index = null;
|
|
2266
2335
|
function getSearchIndex() {
|
|
2267
2336
|
if (!index) index = new SearchIndex();
|
|
@@ -2270,9 +2339,20 @@ function getSearchIndex() {
|
|
|
2270
2339
|
async function rebuildIndex(kv) {
|
|
2271
2340
|
const idx = getSearchIndex();
|
|
2272
2341
|
idx.clear();
|
|
2273
|
-
const sessions = await kv.list(KV.sessions);
|
|
2274
|
-
if (!sessions.length) return 0;
|
|
2275
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;
|
|
2276
2356
|
const obsPerSession = [];
|
|
2277
2357
|
const failedSessions = [];
|
|
2278
2358
|
for (let batch = 0; batch < sessions.length; batch += 10) {
|
|
@@ -2507,10 +2587,10 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
2507
2587
|
};
|
|
2508
2588
|
}
|
|
2509
2589
|
if (pendingImageData && (pendingImageData.startsWith("data:image/") || pendingImageData.startsWith("iVBORw0KGgo") || pendingImageData.startsWith("/9j/"))) {
|
|
2510
|
-
const { saveImageToDisk } = await import("./image-store-
|
|
2590
|
+
const { saveImageToDisk } = await import("./image-store-DnuCI2RB.mjs");
|
|
2511
2591
|
const { filePath, bytesWritten } = await saveImageToDisk(pendingImageData);
|
|
2512
2592
|
raw.imageData = filePath;
|
|
2513
|
-
const { incrementImageRef } = await import("./image-refs-
|
|
2593
|
+
const { incrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
|
|
2514
2594
|
await incrementImageRef(kv, filePath);
|
|
2515
2595
|
sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: bytesWritten });
|
|
2516
2596
|
if (process.env["AGENTMEMORY_IMAGE_EMBEDDINGS"] === "true") sdk.triggerVoid("mem::vision-embed", {
|
|
@@ -2523,7 +2603,7 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
2523
2603
|
await kv.set(KV.observations(payload.sessionId), obsId, raw);
|
|
2524
2604
|
} catch (error) {
|
|
2525
2605
|
if (raw.imageData) {
|
|
2526
|
-
const { deleteImage } = await import("./image-store-
|
|
2606
|
+
const { deleteImage } = await import("./image-store-DnuCI2RB.mjs");
|
|
2527
2607
|
const { deletedBytes } = await deleteImage(raw.imageData);
|
|
2528
2608
|
if (deletedBytes > 0) sdk.triggerVoid("mem::disk-size-delta", { deltaBytes: -deletedBytes });
|
|
2529
2609
|
}
|
|
@@ -4471,6 +4551,20 @@ function registerPatternsFunction(sdk, kv) {
|
|
|
4471
4551
|
|
|
4472
4552
|
//#endregion
|
|
4473
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
|
+
}
|
|
4474
4568
|
function registerRememberFunction(sdk, kv) {
|
|
4475
4569
|
sdk.registerFunction("mem::remember", async (data) => {
|
|
4476
4570
|
if (!data.content || typeof data.content !== "string" || !data.content.trim()) return {
|
|
@@ -4536,6 +4630,14 @@ function registerRememberFunction(sdk, kv) {
|
|
|
4536
4630
|
await kv.set(KV.memories, supersededMemory.id, supersededMemory);
|
|
4537
4631
|
}
|
|
4538
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
|
+
}
|
|
4539
4641
|
if (supersededId) await sdk.trigger({
|
|
4540
4642
|
function_id: "mem::cascade-update",
|
|
4541
4643
|
payload: { supersededMemoryId: supersededId },
|
|
@@ -4556,7 +4658,7 @@ function registerRememberFunction(sdk, kv) {
|
|
|
4556
4658
|
const deletedMemoryIds = [];
|
|
4557
4659
|
const deletedObservationIds = [];
|
|
4558
4660
|
let deletedSession = false;
|
|
4559
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
4661
|
+
const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
|
|
4560
4662
|
if (data.memoryId) {
|
|
4561
4663
|
const mem = await kv.get(KV.memories, data.memoryId);
|
|
4562
4664
|
await kv.delete(KV.memories, data.memoryId);
|
|
@@ -4615,7 +4717,7 @@ const DEFAULTS$1 = {
|
|
|
4615
4717
|
function registerEvictFunction(sdk, kv) {
|
|
4616
4718
|
sdk.registerFunction("mem::evict", async (data) => {
|
|
4617
4719
|
const dryRun = data?.dryRun ?? false;
|
|
4618
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
4720
|
+
const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
|
|
4619
4721
|
const configOverride = await kv.get(KV.config, "eviction").catch(() => null);
|
|
4620
4722
|
const cfg = {
|
|
4621
4723
|
...DEFAULTS$1,
|
|
@@ -5218,7 +5320,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
5218
5320
|
sdk.registerFunction("mem::auto-forget", async (data) => {
|
|
5219
5321
|
const dryRun = data?.dryRun ?? false;
|
|
5220
5322
|
const now = Date.now();
|
|
5221
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
5323
|
+
const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
|
|
5222
5324
|
const result = {
|
|
5223
5325
|
ttlExpired: [],
|
|
5224
5326
|
contradictions: [],
|
|
@@ -5450,7 +5552,9 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
5450
5552
|
"0.9.0",
|
|
5451
5553
|
"0.9.1",
|
|
5452
5554
|
"0.9.2",
|
|
5453
|
-
"0.9.3"
|
|
5555
|
+
"0.9.3",
|
|
5556
|
+
"0.9.4",
|
|
5557
|
+
"0.9.5"
|
|
5454
5558
|
]).has(importData.version)) return {
|
|
5455
5559
|
success: false,
|
|
5456
5560
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -11807,7 +11911,7 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
11807
11911
|
const threshold = typeof data?.threshold === "number" && Number.isFinite(data.threshold) ? data.threshold : DEFAULT_DECAY.tierThresholds.cold;
|
|
11808
11912
|
const maxEvictRaw = typeof data?.maxEvict === "number" && Number.isInteger(data.maxEvict) ? data.maxEvict : 50;
|
|
11809
11913
|
const maxEvict = Math.min(1e3, Math.max(0, maxEvictRaw));
|
|
11810
|
-
const { decrementImageRef } = await import("./image-refs-
|
|
11914
|
+
const { decrementImageRef } = await import("./image-refs-DRse_ePx.mjs");
|
|
11811
11915
|
const candidates = (await kv.list(KV.retentionScores)).filter((s) => s.score < threshold).sort((a, b) => a.score - b.score).slice(0, maxEvict);
|
|
11812
11916
|
if (data?.dryRun) return {
|
|
11813
11917
|
success: true,
|
|
@@ -12068,7 +12172,7 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
12068
12172
|
const parsed = JSON.parse(line);
|
|
12069
12173
|
if (parsed && typeof parsed === "object") entries.push(parsed);
|
|
12070
12174
|
} catch {}
|
|
12071
|
-
let sessionId =
|
|
12175
|
+
let sessionId = "";
|
|
12072
12176
|
let cwd = "";
|
|
12073
12177
|
let firstTs = "";
|
|
12074
12178
|
let lastTs = "";
|
|
@@ -12129,7 +12233,7 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
12129
12233
|
});
|
|
12130
12234
|
} else if (entry.type === "summary" || entry.type === "system") {}
|
|
12131
12235
|
}
|
|
12132
|
-
const effectiveSessionId = sessionId || generateId("sess");
|
|
12236
|
+
const effectiveSessionId = sessionId || fallbackSessionId || generateId("sess");
|
|
12133
12237
|
for (const obs of observations) if (obs.sessionId === "imported") obs.sessionId = effectiveSessionId;
|
|
12134
12238
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
12135
12239
|
return {
|
|
@@ -12239,6 +12343,8 @@ function projectTimeline(observations) {
|
|
|
12239
12343
|
|
|
12240
12344
|
//#endregion
|
|
12241
12345
|
//#region src/functions/replay.ts
|
|
12346
|
+
const MAX_FILES_DEFAULT = 200;
|
|
12347
|
+
const MAX_FILES_UPPER_BOUND = 1e3;
|
|
12242
12348
|
const SENSITIVE_PATH_PATTERNS = [
|
|
12243
12349
|
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
12244
12350
|
/(^|[\\/_.-])credentials?([\\/_.-]|$)/i,
|
|
@@ -12373,8 +12479,11 @@ async function loadObservations(kv, sessionId) {
|
|
|
12373
12479
|
}
|
|
12374
12480
|
async function findJsonlFiles(root, limit = 200) {
|
|
12375
12481
|
const out = [];
|
|
12482
|
+
let discovered = 0;
|
|
12483
|
+
let walked = 0;
|
|
12484
|
+
const traversalCap = Math.max(limit * 50, 5e4);
|
|
12376
12485
|
async function walk(dir) {
|
|
12377
|
-
if (
|
|
12486
|
+
if (walked >= traversalCap) return;
|
|
12378
12487
|
let names;
|
|
12379
12488
|
try {
|
|
12380
12489
|
names = await readdir(dir);
|
|
@@ -12382,7 +12491,8 @@ async function findJsonlFiles(root, limit = 200) {
|
|
|
12382
12491
|
return;
|
|
12383
12492
|
}
|
|
12384
12493
|
for (const name of names) {
|
|
12385
|
-
if (
|
|
12494
|
+
if (walked >= traversalCap) return;
|
|
12495
|
+
walked++;
|
|
12386
12496
|
const full = join(dir, name);
|
|
12387
12497
|
let st;
|
|
12388
12498
|
try {
|
|
@@ -12392,11 +12502,20 @@ async function findJsonlFiles(root, limit = 200) {
|
|
|
12392
12502
|
}
|
|
12393
12503
|
if (st.isSymbolicLink()) continue;
|
|
12394
12504
|
if (st.isDirectory()) await walk(full);
|
|
12395
|
-
else if (st.isFile() && name.endsWith(".jsonl"))
|
|
12505
|
+
else if (st.isFile() && name.endsWith(".jsonl")) {
|
|
12506
|
+
discovered++;
|
|
12507
|
+
if (out.length < limit) out.push(full);
|
|
12508
|
+
}
|
|
12396
12509
|
}
|
|
12397
12510
|
}
|
|
12398
12511
|
await walk(root);
|
|
12399
|
-
|
|
12512
|
+
const traversalCapped = walked >= traversalCap;
|
|
12513
|
+
return {
|
|
12514
|
+
files: out,
|
|
12515
|
+
truncated: discovered > out.length || traversalCapped,
|
|
12516
|
+
discovered,
|
|
12517
|
+
traversalCapped
|
|
12518
|
+
};
|
|
12400
12519
|
}
|
|
12401
12520
|
function registerReplayFunctions(sdk, kv) {
|
|
12402
12521
|
sdk.registerFunction("mem::replay::load", async (data) => {
|
|
@@ -12444,10 +12563,21 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
12444
12563
|
error: "path not found"
|
|
12445
12564
|
};
|
|
12446
12565
|
}
|
|
12566
|
+
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) : MAX_FILES_DEFAULT;
|
|
12447
12567
|
let files = [];
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12568
|
+
let truncated = false;
|
|
12569
|
+
let discovered = 0;
|
|
12570
|
+
let traversalCapped = false;
|
|
12571
|
+
if (stat.isDirectory()) {
|
|
12572
|
+
const found = await findJsonlFiles(abs, maxFiles);
|
|
12573
|
+
files = found.files;
|
|
12574
|
+
truncated = found.truncated;
|
|
12575
|
+
discovered = found.discovered;
|
|
12576
|
+
traversalCapped = found.traversalCapped;
|
|
12577
|
+
} else if (stat.isFile() && abs.endsWith(".jsonl")) {
|
|
12578
|
+
files = [abs];
|
|
12579
|
+
discovered = 1;
|
|
12580
|
+
} else return {
|
|
12451
12581
|
success: false,
|
|
12452
12582
|
error: "path must be a .jsonl file or directory"
|
|
12453
12583
|
};
|
|
@@ -12455,7 +12585,12 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
12455
12585
|
success: true,
|
|
12456
12586
|
imported: 0,
|
|
12457
12587
|
sessionIds: [],
|
|
12458
|
-
observations: 0
|
|
12588
|
+
observations: 0,
|
|
12589
|
+
discovered,
|
|
12590
|
+
truncated,
|
|
12591
|
+
traversalCapped,
|
|
12592
|
+
maxFiles,
|
|
12593
|
+
maxFilesUpperBound: MAX_FILES_UPPER_BOUND
|
|
12459
12594
|
};
|
|
12460
12595
|
const sessionIds = [];
|
|
12461
12596
|
let observationCount = 0;
|
|
@@ -12521,7 +12656,12 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
12521
12656
|
success: true,
|
|
12522
12657
|
imported: files.length,
|
|
12523
12658
|
sessionIds,
|
|
12524
|
-
observations: observationCount
|
|
12659
|
+
observations: observationCount,
|
|
12660
|
+
discovered,
|
|
12661
|
+
truncated,
|
|
12662
|
+
traversalCapped,
|
|
12663
|
+
maxFiles,
|
|
12664
|
+
maxFilesUpperBound: MAX_FILES_UPPER_BOUND
|
|
12525
12665
|
};
|
|
12526
12666
|
});
|
|
12527
12667
|
}
|
|
@@ -12824,12 +12964,11 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
12824
12964
|
sdk.registerFunction("api::config-flags", async (req) => {
|
|
12825
12965
|
const authErr = checkAuth(req, secret);
|
|
12826
12966
|
if (authErr) return authErr;
|
|
12827
|
-
const env = process.env;
|
|
12828
12967
|
return {
|
|
12829
12968
|
status_code: 200,
|
|
12830
12969
|
body: {
|
|
12831
12970
|
version: VERSION,
|
|
12832
|
-
provider:
|
|
12971
|
+
provider: detectLlmProviderKind(),
|
|
12833
12972
|
embeddingProvider: detectEmbeddingProvider() ? "embeddings" : "none",
|
|
12834
12973
|
flags: [
|
|
12835
12974
|
{
|
|
@@ -13127,11 +13266,12 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
13127
13266
|
payload.path = body.path.trim();
|
|
13128
13267
|
}
|
|
13129
13268
|
if (body.maxFiles !== void 0) {
|
|
13130
|
-
|
|
13269
|
+
const n = body.maxFiles;
|
|
13270
|
+
if (!Number.isInteger(n) || n < 1 || n > MAX_FILES_UPPER_BOUND) return {
|
|
13131
13271
|
status_code: 400,
|
|
13132
|
-
body: { error:
|
|
13272
|
+
body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
|
|
13133
13273
|
};
|
|
13134
|
-
payload.maxFiles =
|
|
13274
|
+
payload.maxFiles = n;
|
|
13135
13275
|
}
|
|
13136
13276
|
return {
|
|
13137
13277
|
status_code: 202,
|
|
@@ -14047,6 +14187,32 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14047
14187
|
http_method: "GET"
|
|
14048
14188
|
}
|
|
14049
14189
|
});
|
|
14190
|
+
sdk.registerFunction("api::memory-by-id", async (req) => {
|
|
14191
|
+
const authErr = checkAuth(req, secret);
|
|
14192
|
+
if (authErr) return authErr;
|
|
14193
|
+
const id = req.path_params?.["id"];
|
|
14194
|
+
if (!id || typeof id !== "string") return {
|
|
14195
|
+
status_code: 400,
|
|
14196
|
+
body: { error: "id path parameter is required" }
|
|
14197
|
+
};
|
|
14198
|
+
const memory = await kv.get(KV.memories, id);
|
|
14199
|
+
if (!memory) return {
|
|
14200
|
+
status_code: 404,
|
|
14201
|
+
body: { error: `memory not found: ${id}` }
|
|
14202
|
+
};
|
|
14203
|
+
return {
|
|
14204
|
+
status_code: 200,
|
|
14205
|
+
body: { memory }
|
|
14206
|
+
};
|
|
14207
|
+
});
|
|
14208
|
+
sdk.registerTrigger({
|
|
14209
|
+
type: "http",
|
|
14210
|
+
function_id: "api::memory-by-id",
|
|
14211
|
+
config: {
|
|
14212
|
+
api_path: "/agentmemory/memories/:id",
|
|
14213
|
+
http_method: "GET"
|
|
14214
|
+
}
|
|
14215
|
+
});
|
|
14050
14216
|
sdk.registerFunction("api::semantic-list", async (req) => {
|
|
14051
14217
|
const authErr = checkAuth(req, secret);
|
|
14052
14218
|
if (authErr) return authErr;
|
|
@@ -15925,6 +16091,15 @@ function registerEventTriggers(sdk, kv) {
|
|
|
15925
16091
|
error: err instanceof Error ? err.message : String(err)
|
|
15926
16092
|
});
|
|
15927
16093
|
}
|
|
16094
|
+
if (isGraphExtractionEnabled()) try {
|
|
16095
|
+
const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
|
|
16096
|
+
if (compressed.length > 0) sdk.triggerVoid("mem::graph-extract", { observations: compressed });
|
|
16097
|
+
} catch (err) {
|
|
16098
|
+
logger.warn("graph-extract triggerVoid failed", {
|
|
16099
|
+
sessionId: data.sessionId,
|
|
16100
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16101
|
+
});
|
|
16102
|
+
}
|
|
15928
16103
|
return summary;
|
|
15929
16104
|
});
|
|
15930
16105
|
sdk.registerTrigger({
|
|
@@ -17836,6 +18011,14 @@ function initMetrics(getMeter) {
|
|
|
17836
18011
|
function hasGetMeter(sdk) {
|
|
17837
18012
|
return typeof sdk === "object" && sdk !== null && "getMeter" in sdk && typeof sdk.getMeter === "function";
|
|
17838
18013
|
}
|
|
18014
|
+
let lastUnhandledLogAt = 0;
|
|
18015
|
+
process.on("unhandledRejection", (reason) => {
|
|
18016
|
+
const now = Date.now();
|
|
18017
|
+
if (now - lastUnhandledLogAt < 6e4) return;
|
|
18018
|
+
lastUnhandledLogAt = now;
|
|
18019
|
+
const r = reason;
|
|
18020
|
+
console.warn(`[agentmemory] unhandledRejection (suppressed):`, r?.code ? `${r.code} ${r.function_id ?? ""} ${r.message ?? ""}`.trim() : reason);
|
|
18021
|
+
});
|
|
17839
18022
|
async function main() {
|
|
17840
18023
|
const config = loadConfig();
|
|
17841
18024
|
const embeddingConfig = loadEmbeddingConfig();
|
|
@@ -17853,6 +18036,7 @@ async function main() {
|
|
|
17853
18036
|
console.log(`[agentmemory] Streams: ws://localhost:${config.streamsPort}`);
|
|
17854
18037
|
const sdk = registerWorker(config.engineUrl, {
|
|
17855
18038
|
workerName: "agentmemory",
|
|
18039
|
+
invocationTimeoutMs: 18e4,
|
|
17856
18040
|
otel: {
|
|
17857
18041
|
serviceName: OTEL_CONFIG.serviceName,
|
|
17858
18042
|
serviceVersion: OTEL_CONFIG.serviceVersion,
|
|
@@ -17961,8 +18145,20 @@ async function main() {
|
|
|
17961
18145
|
console.log(`[agentmemory] Loaded persisted BM25 index (${bm25Index.size} docs)`);
|
|
17962
18146
|
}
|
|
17963
18147
|
if (loaded?.vector && vectorIndex && loaded.vector.size > 0) {
|
|
17964
|
-
|
|
17965
|
-
|
|
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
|
+
}
|
|
17966
18162
|
}
|
|
17967
18163
|
if (bm25Index.size === 0) {
|
|
17968
18164
|
const indexCount = await rebuildIndex(kv).catch((err) => {
|
|
@@ -17970,9 +18166,36 @@ async function main() {
|
|
|
17970
18166
|
return 0;
|
|
17971
18167
|
});
|
|
17972
18168
|
if (indexCount > 0) {
|
|
17973
|
-
console.log(`[agentmemory] Search index rebuilt: ${indexCount}
|
|
18169
|
+
console.log(`[agentmemory] Search index rebuilt: ${indexCount} entries`);
|
|
17974
18170
|
indexPersistence.scheduleSave();
|
|
17975
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);
|
|
17976
18199
|
}
|
|
17977
18200
|
console.log(`[agentmemory] Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
17978
18201
|
console.log(`[agentmemory] Endpoints: 107 REST + ${getAllTools().length} MCP tools + 6 MCP resources + 3 MCP prompts`);
|
|
@@ -18042,4 +18265,4 @@ main().catch((err) => {
|
|
|
18042
18265
|
|
|
18043
18266
|
//#endregion
|
|
18044
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 };
|
|
18045
|
-
//# sourceMappingURL=src-
|
|
18268
|
+
//# sourceMappingURL=src-xYHSzz5S.mjs.map
|