@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.
- package/README.md +8 -3
- package/dist/cli.mjs +107 -7
- package/dist/cli.mjs.map +1 -1
- package/dist/image-refs-BfT7XAa-.mjs +3 -0
- package/dist/{image-store-DGvZMMrI.mjs → image-store-Cn9eD-7D.mjs} +1 -1
- package/dist/index.mjs +134 -22
- package/dist/index.mjs.map +1 -1
- package/dist/{src-3Snd7D3T.mjs → src-uDy2jLO-.mjs} +129 -29
- package/dist/src-uDy2jLO-.mjs.map +1 -0
- package/dist/{standalone-BG9uPsDK.mjs → standalone-CqqEcfNR.mjs} +2 -2
- package/dist/{standalone-BG9uPsDK.mjs.map → standalone-CqqEcfNR.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-Co8VIL4t.mjs} +16 -4
- package/dist/tools-registry-Co8VIL4t.mjs.map +1 -0
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +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
package/dist/index.mjs
CHANGED
|
@@ -50,7 +50,14 @@ function loadEnvFile() {
|
|
|
50
50
|
if (eqIdx === -1) continue;
|
|
51
51
|
const key = trimmed.slice(0, eqIdx).trim();
|
|
52
52
|
let val = trimmed.slice(eqIdx + 1).trim();
|
|
53
|
-
|
|
53
|
+
const quoteChar = val[0] === "\"" || val[0] === "'" ? val[0] : "";
|
|
54
|
+
if (quoteChar) {
|
|
55
|
+
const closeIdx = val.indexOf(quoteChar, 1);
|
|
56
|
+
if (closeIdx !== -1) val = val.slice(1, closeIdx);
|
|
57
|
+
} else {
|
|
58
|
+
const hashIdx = val.indexOf(" #");
|
|
59
|
+
if (hashIdx !== -1) val = val.slice(0, hashIdx).trim();
|
|
60
|
+
}
|
|
54
61
|
vars[key] = val;
|
|
55
62
|
}
|
|
56
63
|
return vars;
|
|
@@ -123,6 +130,11 @@ function getMergedEnv(overrides) {
|
|
|
123
130
|
function getEnvVar(key) {
|
|
124
131
|
return getMergedEnv()[key];
|
|
125
132
|
}
|
|
133
|
+
function detectLlmProviderKind() {
|
|
134
|
+
const env = getMergedEnv();
|
|
135
|
+
if (hasRealValue(env["ANTHROPIC_API_KEY"]) || hasRealValue(env["GEMINI_API_KEY"]) || hasRealValue(env["GOOGLE_API_KEY"]) || hasRealValue(env["OPENROUTER_API_KEY"]) || hasRealValue(env["MINIMAX_API_KEY"])) return "llm";
|
|
136
|
+
return "noop";
|
|
137
|
+
}
|
|
126
138
|
function loadEmbeddingConfig() {
|
|
127
139
|
const env = getMergedEnv();
|
|
128
140
|
let bm25Weight = parseFloat(env["BM25_WEIGHT"] || "0.4");
|
|
@@ -2183,8 +2195,10 @@ var SearchIndex = class SearchIndex {
|
|
|
2183
2195
|
//#endregion
|
|
2184
2196
|
//#region src/state/index-persistence.ts
|
|
2185
2197
|
const DEBOUNCE_MS = 5e3;
|
|
2198
|
+
const FAILURE_LOG_THROTTLE_MS = 6e4;
|
|
2186
2199
|
var IndexPersistence = class {
|
|
2187
2200
|
timer = null;
|
|
2201
|
+
lastFailureLogAt = 0;
|
|
2188
2202
|
constructor(kv, bm25, vector) {
|
|
2189
2203
|
this.kv = kv;
|
|
2190
2204
|
this.bm25 = bm25;
|
|
@@ -2192,15 +2206,21 @@ var IndexPersistence = class {
|
|
|
2192
2206
|
}
|
|
2193
2207
|
scheduleSave() {
|
|
2194
2208
|
if (this.timer) clearTimeout(this.timer);
|
|
2195
|
-
this.timer = setTimeout(() =>
|
|
2209
|
+
this.timer = setTimeout(() => {
|
|
2210
|
+
this.save().catch((err) => this.logFailure(err));
|
|
2211
|
+
}, DEBOUNCE_MS);
|
|
2196
2212
|
}
|
|
2197
2213
|
async save() {
|
|
2198
2214
|
if (this.timer) {
|
|
2199
2215
|
clearTimeout(this.timer);
|
|
2200
2216
|
this.timer = null;
|
|
2201
2217
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2218
|
+
try {
|
|
2219
|
+
await this.kv.set(KV.bm25Index, "data", this.bm25.serialize());
|
|
2220
|
+
if (this.vector && this.vector.size > 0) await this.kv.set(KV.bm25Index, "vectors", this.vector.serialize());
|
|
2221
|
+
} catch (err) {
|
|
2222
|
+
this.logFailure(err);
|
|
2223
|
+
}
|
|
2204
2224
|
}
|
|
2205
2225
|
async load() {
|
|
2206
2226
|
let bm25 = null;
|
|
@@ -2220,6 +2240,18 @@ var IndexPersistence = class {
|
|
|
2220
2240
|
this.timer = null;
|
|
2221
2241
|
}
|
|
2222
2242
|
}
|
|
2243
|
+
logFailure(err) {
|
|
2244
|
+
const now = Date.now();
|
|
2245
|
+
if (now - this.lastFailureLogAt < FAILURE_LOG_THROTTLE_MS) return;
|
|
2246
|
+
this.lastFailureLogAt = now;
|
|
2247
|
+
const code = err?.code;
|
|
2248
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2249
|
+
logger.warn("index persistence: failed to save BM25/vector index", {
|
|
2250
|
+
code,
|
|
2251
|
+
message,
|
|
2252
|
+
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
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2223
2255
|
};
|
|
2224
2256
|
|
|
2225
2257
|
//#endregion
|
|
@@ -5618,7 +5650,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
5618
5650
|
|
|
5619
5651
|
//#endregion
|
|
5620
5652
|
//#region src/version.ts
|
|
5621
|
-
const VERSION = "0.9.
|
|
5653
|
+
const VERSION = "0.9.4";
|
|
5622
5654
|
|
|
5623
5655
|
//#endregion
|
|
5624
5656
|
//#region src/functions/export-import.ts
|
|
@@ -5739,7 +5771,8 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
5739
5771
|
"0.9.0",
|
|
5740
5772
|
"0.9.1",
|
|
5741
5773
|
"0.9.2",
|
|
5742
|
-
"0.9.3"
|
|
5774
|
+
"0.9.3",
|
|
5775
|
+
"0.9.4"
|
|
5743
5776
|
]).has(importData.version)) return {
|
|
5744
5777
|
success: false,
|
|
5745
5778
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -12357,7 +12390,7 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
12357
12390
|
const parsed = JSON.parse(line);
|
|
12358
12391
|
if (parsed && typeof parsed === "object") entries.push(parsed);
|
|
12359
12392
|
} catch {}
|
|
12360
|
-
let sessionId =
|
|
12393
|
+
let sessionId = "";
|
|
12361
12394
|
let cwd = "";
|
|
12362
12395
|
let firstTs = "";
|
|
12363
12396
|
let lastTs = "";
|
|
@@ -12418,7 +12451,7 @@ function parseJsonlText(text, fallbackSessionId) {
|
|
|
12418
12451
|
});
|
|
12419
12452
|
} else if (entry.type === "summary" || entry.type === "system") {}
|
|
12420
12453
|
}
|
|
12421
|
-
const effectiveSessionId = sessionId || generateId("sess");
|
|
12454
|
+
const effectiveSessionId = sessionId || fallbackSessionId || generateId("sess");
|
|
12422
12455
|
for (const obs of observations) if (obs.sessionId === "imported") obs.sessionId = effectiveSessionId;
|
|
12423
12456
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
12424
12457
|
return {
|
|
@@ -12528,6 +12561,8 @@ function projectTimeline(observations) {
|
|
|
12528
12561
|
|
|
12529
12562
|
//#endregion
|
|
12530
12563
|
//#region src/functions/replay.ts
|
|
12564
|
+
const MAX_FILES_DEFAULT = 200;
|
|
12565
|
+
const MAX_FILES_UPPER_BOUND = 1e3;
|
|
12531
12566
|
const SENSITIVE_PATH_PATTERNS = [
|
|
12532
12567
|
/(^|[\\/_.-])secret([\\/_.-]|s?$)/i,
|
|
12533
12568
|
/(^|[\\/_.-])credentials?([\\/_.-]|$)/i,
|
|
@@ -12662,8 +12697,11 @@ async function loadObservations(kv, sessionId) {
|
|
|
12662
12697
|
}
|
|
12663
12698
|
async function findJsonlFiles(root, limit = 200) {
|
|
12664
12699
|
const out = [];
|
|
12700
|
+
let discovered = 0;
|
|
12701
|
+
let walked = 0;
|
|
12702
|
+
const traversalCap = Math.max(limit * 50, 5e4);
|
|
12665
12703
|
async function walk(dir) {
|
|
12666
|
-
if (
|
|
12704
|
+
if (walked >= traversalCap) return;
|
|
12667
12705
|
let names;
|
|
12668
12706
|
try {
|
|
12669
12707
|
names = await readdir(dir);
|
|
@@ -12671,7 +12709,8 @@ async function findJsonlFiles(root, limit = 200) {
|
|
|
12671
12709
|
return;
|
|
12672
12710
|
}
|
|
12673
12711
|
for (const name of names) {
|
|
12674
|
-
if (
|
|
12712
|
+
if (walked >= traversalCap) return;
|
|
12713
|
+
walked++;
|
|
12675
12714
|
const full = join(dir, name);
|
|
12676
12715
|
let st;
|
|
12677
12716
|
try {
|
|
@@ -12681,11 +12720,20 @@ async function findJsonlFiles(root, limit = 200) {
|
|
|
12681
12720
|
}
|
|
12682
12721
|
if (st.isSymbolicLink()) continue;
|
|
12683
12722
|
if (st.isDirectory()) await walk(full);
|
|
12684
|
-
else if (st.isFile() && name.endsWith(".jsonl"))
|
|
12723
|
+
else if (st.isFile() && name.endsWith(".jsonl")) {
|
|
12724
|
+
discovered++;
|
|
12725
|
+
if (out.length < limit) out.push(full);
|
|
12726
|
+
}
|
|
12685
12727
|
}
|
|
12686
12728
|
}
|
|
12687
12729
|
await walk(root);
|
|
12688
|
-
|
|
12730
|
+
const traversalCapped = walked >= traversalCap;
|
|
12731
|
+
return {
|
|
12732
|
+
files: out,
|
|
12733
|
+
truncated: discovered > out.length || traversalCapped,
|
|
12734
|
+
discovered,
|
|
12735
|
+
traversalCapped
|
|
12736
|
+
};
|
|
12689
12737
|
}
|
|
12690
12738
|
function registerReplayFunctions(sdk, kv) {
|
|
12691
12739
|
sdk.registerFunction("mem::replay::load", async (data) => {
|
|
@@ -12733,10 +12781,21 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
12733
12781
|
error: "path not found"
|
|
12734
12782
|
};
|
|
12735
12783
|
}
|
|
12784
|
+
const maxFiles = Number.isInteger(data.maxFiles) && data.maxFiles > 0 ? Math.min(data.maxFiles, MAX_FILES_UPPER_BOUND) : MAX_FILES_DEFAULT;
|
|
12736
12785
|
let files = [];
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12786
|
+
let truncated = false;
|
|
12787
|
+
let discovered = 0;
|
|
12788
|
+
let traversalCapped = false;
|
|
12789
|
+
if (stat.isDirectory()) {
|
|
12790
|
+
const found = await findJsonlFiles(abs, maxFiles);
|
|
12791
|
+
files = found.files;
|
|
12792
|
+
truncated = found.truncated;
|
|
12793
|
+
discovered = found.discovered;
|
|
12794
|
+
traversalCapped = found.traversalCapped;
|
|
12795
|
+
} else if (stat.isFile() && abs.endsWith(".jsonl")) {
|
|
12796
|
+
files = [abs];
|
|
12797
|
+
discovered = 1;
|
|
12798
|
+
} else return {
|
|
12740
12799
|
success: false,
|
|
12741
12800
|
error: "path must be a .jsonl file or directory"
|
|
12742
12801
|
};
|
|
@@ -12744,7 +12803,12 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
12744
12803
|
success: true,
|
|
12745
12804
|
imported: 0,
|
|
12746
12805
|
sessionIds: [],
|
|
12747
|
-
observations: 0
|
|
12806
|
+
observations: 0,
|
|
12807
|
+
discovered,
|
|
12808
|
+
truncated,
|
|
12809
|
+
traversalCapped,
|
|
12810
|
+
maxFiles,
|
|
12811
|
+
maxFilesUpperBound: MAX_FILES_UPPER_BOUND
|
|
12748
12812
|
};
|
|
12749
12813
|
const sessionIds = [];
|
|
12750
12814
|
let observationCount = 0;
|
|
@@ -12810,7 +12874,12 @@ function registerReplayFunctions(sdk, kv) {
|
|
|
12810
12874
|
success: true,
|
|
12811
12875
|
imported: files.length,
|
|
12812
12876
|
sessionIds,
|
|
12813
|
-
observations: observationCount
|
|
12877
|
+
observations: observationCount,
|
|
12878
|
+
discovered,
|
|
12879
|
+
truncated,
|
|
12880
|
+
traversalCapped,
|
|
12881
|
+
maxFiles,
|
|
12882
|
+
maxFilesUpperBound: MAX_FILES_UPPER_BOUND
|
|
12814
12883
|
};
|
|
12815
12884
|
});
|
|
12816
12885
|
}
|
|
@@ -13113,12 +13182,11 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
13113
13182
|
sdk.registerFunction("api::config-flags", async (req) => {
|
|
13114
13183
|
const authErr = checkAuth(req, secret);
|
|
13115
13184
|
if (authErr) return authErr;
|
|
13116
|
-
const env = process.env;
|
|
13117
13185
|
return {
|
|
13118
13186
|
status_code: 200,
|
|
13119
13187
|
body: {
|
|
13120
13188
|
version: VERSION,
|
|
13121
|
-
provider:
|
|
13189
|
+
provider: detectLlmProviderKind(),
|
|
13122
13190
|
embeddingProvider: detectEmbeddingProvider() ? "embeddings" : "none",
|
|
13123
13191
|
flags: [
|
|
13124
13192
|
{
|
|
@@ -13416,11 +13484,12 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
13416
13484
|
payload.path = body.path.trim();
|
|
13417
13485
|
}
|
|
13418
13486
|
if (body.maxFiles !== void 0) {
|
|
13419
|
-
|
|
13487
|
+
const n = body.maxFiles;
|
|
13488
|
+
if (!Number.isInteger(n) || n < 1 || n > MAX_FILES_UPPER_BOUND) return {
|
|
13420
13489
|
status_code: 400,
|
|
13421
|
-
body: { error:
|
|
13490
|
+
body: { error: `maxFiles must be an integer between 1 and ${MAX_FILES_UPPER_BOUND}` }
|
|
13422
13491
|
};
|
|
13423
|
-
payload.maxFiles =
|
|
13492
|
+
payload.maxFiles = n;
|
|
13424
13493
|
}
|
|
13425
13494
|
return {
|
|
13426
13495
|
status_code: 202,
|
|
@@ -14336,6 +14405,32 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14336
14405
|
http_method: "GET"
|
|
14337
14406
|
}
|
|
14338
14407
|
});
|
|
14408
|
+
sdk.registerFunction("api::memory-by-id", async (req) => {
|
|
14409
|
+
const authErr = checkAuth(req, secret);
|
|
14410
|
+
if (authErr) return authErr;
|
|
14411
|
+
const id = req.path_params?.["id"];
|
|
14412
|
+
if (!id || typeof id !== "string") return {
|
|
14413
|
+
status_code: 400,
|
|
14414
|
+
body: { error: "id path parameter is required" }
|
|
14415
|
+
};
|
|
14416
|
+
const memory = await kv.get(KV.memories, id);
|
|
14417
|
+
if (!memory) return {
|
|
14418
|
+
status_code: 404,
|
|
14419
|
+
body: { error: `memory not found: ${id}` }
|
|
14420
|
+
};
|
|
14421
|
+
return {
|
|
14422
|
+
status_code: 200,
|
|
14423
|
+
body: { memory }
|
|
14424
|
+
};
|
|
14425
|
+
});
|
|
14426
|
+
sdk.registerTrigger({
|
|
14427
|
+
type: "http",
|
|
14428
|
+
function_id: "api::memory-by-id",
|
|
14429
|
+
config: {
|
|
14430
|
+
api_path: "/agentmemory/memories/:id",
|
|
14431
|
+
http_method: "GET"
|
|
14432
|
+
}
|
|
14433
|
+
});
|
|
14339
14434
|
sdk.registerFunction("api::semantic-list", async (req) => {
|
|
14340
14435
|
const authErr = checkAuth(req, secret);
|
|
14341
14436
|
if (authErr) return authErr;
|
|
@@ -16214,6 +16309,15 @@ function registerEventTriggers(sdk, kv) {
|
|
|
16214
16309
|
error: err instanceof Error ? err.message : String(err)
|
|
16215
16310
|
});
|
|
16216
16311
|
}
|
|
16312
|
+
if (isGraphExtractionEnabled()) try {
|
|
16313
|
+
const compressed = (await kv.list(KV.observations(data.sessionId))).filter((o) => o.title);
|
|
16314
|
+
if (compressed.length > 0) sdk.triggerVoid("mem::graph-extract", { observations: compressed });
|
|
16315
|
+
} catch (err) {
|
|
16316
|
+
logger.warn("graph-extract triggerVoid failed", {
|
|
16317
|
+
sessionId: data.sessionId,
|
|
16318
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16319
|
+
});
|
|
16320
|
+
}
|
|
16217
16321
|
return summary;
|
|
16218
16322
|
});
|
|
16219
16323
|
sdk.registerTrigger({
|
|
@@ -19198,6 +19302,14 @@ function initMetrics(getMeter) {
|
|
|
19198
19302
|
function hasGetMeter(sdk) {
|
|
19199
19303
|
return typeof sdk === "object" && sdk !== null && "getMeter" in sdk && typeof sdk.getMeter === "function";
|
|
19200
19304
|
}
|
|
19305
|
+
let lastUnhandledLogAt = 0;
|
|
19306
|
+
process.on("unhandledRejection", (reason) => {
|
|
19307
|
+
const now = Date.now();
|
|
19308
|
+
if (now - lastUnhandledLogAt < 6e4) return;
|
|
19309
|
+
lastUnhandledLogAt = now;
|
|
19310
|
+
const r = reason;
|
|
19311
|
+
console.warn(`[agentmemory] unhandledRejection (suppressed):`, r?.code ? `${r.code} ${r.function_id ?? ""} ${r.message ?? ""}`.trim() : reason);
|
|
19312
|
+
});
|
|
19201
19313
|
async function main() {
|
|
19202
19314
|
const config = loadConfig();
|
|
19203
19315
|
const embeddingConfig = loadEmbeddingConfig();
|