@agentmemory/agentmemory 0.9.22 → 0.9.23
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/AGENTS.md +7 -2
- package/README.md +144 -32
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +32 -18
- package/dist/cli.mjs.map +1 -1
- package/dist/{connect-BQQXpyDS.mjs → connect-Cf9bmBqO.mjs} +290 -33
- package/dist/connect-Cf9bmBqO.mjs.map +1 -0
- package/dist/hooks/notification.mjs +46 -21
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +47 -21
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +57 -22
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +26 -2
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +19 -12
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +39 -16
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +26 -33
- package/dist/hooks/session-end.mjs.map +1 -1
- package/dist/hooks/session-start.mjs +28 -3
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +14 -17
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/hooks/subagent-start.mjs +31 -4
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +45 -20
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +44 -21
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/iii-config.docker.yaml +3 -2
- package/dist/iii-config.yaml +11 -2
- package/dist/index.mjs +335 -57
- package/dist/index.mjs.map +1 -1
- package/dist/{src-gpTAJuBy.mjs → src-DvS3bhMe.mjs} +322 -58
- package/dist/src-DvS3bhMe.mjs.map +1 -0
- package/dist/{standalone-C4i7ktpn.mjs → standalone-DHQcPX_g.mjs} +92 -11
- package/dist/standalone-DHQcPX_g.mjs.map +1 -0
- package/dist/standalone.mjs +94 -9
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-B7Y6nJsr.mjs → tools-registry-DJizX9Az.mjs} +16 -2
- package/dist/tools-registry-DJizX9Az.mjs.map +1 -0
- package/dist/version-BPfyI4Kc.mjs +6 -0
- package/dist/version-BPfyI4Kc.mjs.map +1 -0
- package/dist/viewer/index.html +9 -2
- package/iii-config.docker.yaml +3 -2
- package/iii-config.yaml +11 -2
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/.codex-plugin/plugin.json +2 -2
- package/plugin/.mcp.copilot.json +15 -0
- package/plugin/hooks/hooks.copilot.json +72 -0
- package/plugin/plugin.json +15 -0
- package/plugin/scripts/notification.mjs +46 -21
- package/plugin/scripts/notification.mjs.map +1 -1
- package/plugin/scripts/post-tool-failure.mjs +47 -21
- package/plugin/scripts/post-tool-failure.mjs.map +1 -1
- package/plugin/scripts/post-tool-use.mjs +57 -22
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/pre-compact.mjs +26 -2
- package/plugin/scripts/pre-compact.mjs.map +1 -1
- package/plugin/scripts/pre-tool-use.mjs +19 -12
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/prompt-submit.mjs +39 -16
- package/plugin/scripts/prompt-submit.mjs.map +1 -1
- package/plugin/scripts/session-end.mjs +26 -33
- package/plugin/scripts/session-end.mjs.map +1 -1
- package/plugin/scripts/session-start.mjs +28 -3
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +14 -17
- package/plugin/scripts/stop.mjs.map +1 -1
- package/plugin/scripts/subagent-start.mjs +31 -4
- package/plugin/scripts/subagent-start.mjs.map +1 -1
- package/plugin/scripts/subagent-stop.mjs +45 -20
- package/plugin/scripts/subagent-stop.mjs.map +1 -1
- package/plugin/scripts/task-completed.mjs +44 -21
- package/plugin/scripts/task-completed.mjs.map +1 -1
- package/dist/connect-BQQXpyDS.mjs.map +0 -1
- package/dist/src-gpTAJuBy.mjs.map +0 -1
- package/dist/standalone-C4i7ktpn.mjs.map +0 -1
- package/dist/tools-registry-B7Y6nJsr.mjs.map +0 -1
- package/dist/version-DvQMNbEH.mjs +0 -6
- package/dist/version-DvQMNbEH.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -233,7 +233,17 @@ function isGraphExtractionEnabled() {
|
|
|
233
233
|
return getMergedEnv()["GRAPH_EXTRACTION_ENABLED"] === "true";
|
|
234
234
|
}
|
|
235
235
|
function isConsolidationEnabled() {
|
|
236
|
-
|
|
236
|
+
const env = getMergedEnv();
|
|
237
|
+
const explicit = env["CONSOLIDATION_ENABLED"];
|
|
238
|
+
if (explicit === "false" || explicit === "0") return false;
|
|
239
|
+
if (explicit === "true" || explicit === "1") return true;
|
|
240
|
+
return hasLLMProviderConfigured(env);
|
|
241
|
+
}
|
|
242
|
+
function hasLLMProviderConfigured(env) {
|
|
243
|
+
const provider = (env["AGENTMEMORY_PROVIDER"] || "").toLowerCase();
|
|
244
|
+
if (provider === "noop") return false;
|
|
245
|
+
const openaiKeyForLlm = env["OPENAI_API_KEY"] && (env["OPENAI_API_KEY_FOR_LLM"] || "").toLowerCase() !== "false";
|
|
246
|
+
return Boolean(env["ANTHROPIC_API_KEY"] || openaiKeyForLlm || env["OPENROUTER_API_KEY"] || env["GEMINI_API_KEY"] || env["GOOGLE_API_KEY"] || env["MINIMAX_API_KEY"] || env["OPENAI_BASE_URL"] || provider === "agent-sdk");
|
|
237
247
|
}
|
|
238
248
|
function isAutoCompressEnabled() {
|
|
239
249
|
return getMergedEnv()["AGENTMEMORY_AUTO_COMPRESS"] === "true";
|
|
@@ -886,16 +896,30 @@ function resolveDimensions(model, override) {
|
|
|
886
896
|
* `api-key` header instead of `Authorization: Bearer`.
|
|
887
897
|
*
|
|
888
898
|
* Required env vars:
|
|
889
|
-
* OPENAI_API_KEY
|
|
899
|
+
* OPENAI_API_KEY — API key (fallback for OPENAI_EMBEDDING_API_KEY)
|
|
890
900
|
*
|
|
891
901
|
* Optional:
|
|
892
|
-
* OPENAI_BASE_URL
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
*
|
|
902
|
+
* OPENAI_BASE_URL — base URL without path (default: https://api.openai.com).
|
|
903
|
+
* Azure: https://<resource>.openai.azure.com/openai/deployments/<deployment>
|
|
904
|
+
* OPENAI_EMBEDDING_BASE_URL — embedding-specific base URL override (defaults
|
|
905
|
+
* to OPENAI_BASE_URL). Lets operators run
|
|
906
|
+
* embeddings on a separate endpoint from chat —
|
|
907
|
+
* e.g. local Ollama / LM Studio / llama.cpp /
|
|
908
|
+
* vLLM at http://localhost:1234 for unlimited
|
|
909
|
+
* free embeddings, while keeping chat
|
|
910
|
+
* completions on a rate-limited but high-quality
|
|
911
|
+
* hosted provider. Azure detection runs on
|
|
912
|
+
* whichever URL ends up selected.
|
|
913
|
+
* OPENAI_EMBEDDING_API_KEY — separate API key for the embedding endpoint
|
|
914
|
+
* (defaults to OPENAI_API_KEY). Useful when the
|
|
915
|
+
* embedding endpoint requires a different key
|
|
916
|
+
* or no key at all (set to e.g. "local" for
|
|
917
|
+
* endpoints that ignore Authorization).
|
|
918
|
+
* OPENAI_API_VERSION — Azure api-version query param (default: 2024-08-01-preview)
|
|
919
|
+
* OPENAI_EMBEDDING_MODEL — model name (default: text-embedding-3-small)
|
|
920
|
+
* OPENAI_EMBEDDING_DIMENSIONS — override reported dimensions (required for
|
|
921
|
+
* custom / self-hosted models not in the
|
|
922
|
+
* MODEL_DIMENSIONS table above)
|
|
899
923
|
*/
|
|
900
924
|
var OpenAIEmbeddingProvider = class {
|
|
901
925
|
name = "openai";
|
|
@@ -906,9 +930,9 @@ var OpenAIEmbeddingProvider = class {
|
|
|
906
930
|
isAzure;
|
|
907
931
|
azureApiVersion;
|
|
908
932
|
constructor(apiKey) {
|
|
909
|
-
this.apiKey = apiKey || getEnvVar("OPENAI_API_KEY") || "";
|
|
910
|
-
if (!this.apiKey) throw new Error("
|
|
911
|
-
this.baseUrl = normalizeBaseUrl(getEnvVar("OPENAI_BASE_URL"));
|
|
933
|
+
this.apiKey = apiKey || getEnvVar("OPENAI_EMBEDDING_API_KEY") || getEnvVar("OPENAI_API_KEY") || "";
|
|
934
|
+
if (!this.apiKey) throw new Error("API key is required (via constructor, OPENAI_EMBEDDING_API_KEY, or OPENAI_API_KEY)");
|
|
935
|
+
this.baseUrl = normalizeBaseUrl(getEnvVar("OPENAI_EMBEDDING_BASE_URL") || getEnvVar("OPENAI_BASE_URL"));
|
|
912
936
|
this.model = getEnvVar("OPENAI_EMBEDDING_MODEL") || DEFAULT_MODEL$1;
|
|
913
937
|
this.dimensions = resolveDimensions(this.model, getEnvVar("OPENAI_EMBEDDING_DIMENSIONS"));
|
|
914
938
|
this.isAzure = detectAzure(this.baseUrl);
|
|
@@ -1355,10 +1379,11 @@ function jaccardSimilarity(a, b) {
|
|
|
1355
1379
|
//#endregion
|
|
1356
1380
|
//#region src/state/vector-index.ts
|
|
1357
1381
|
function float32ToBase64(arr) {
|
|
1358
|
-
return Buffer.from(arr.buffer).toString("base64");
|
|
1382
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).toString("base64");
|
|
1359
1383
|
}
|
|
1360
1384
|
function base64ToFloat32(b64) {
|
|
1361
|
-
|
|
1385
|
+
const buf = Buffer.from(b64, "base64");
|
|
1386
|
+
return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
1362
1387
|
}
|
|
1363
1388
|
function cosineSimilarity(a, b) {
|
|
1364
1389
|
if (a.length !== b.length) return 0;
|
|
@@ -1479,7 +1504,7 @@ var VectorIndex = class VectorIndex {
|
|
|
1479
1504
|
function memoryToObservation(memory) {
|
|
1480
1505
|
return {
|
|
1481
1506
|
id: memory.id,
|
|
1482
|
-
sessionId: memory.sessionIds[0] ?? "memory",
|
|
1507
|
+
sessionId: memory.sessionIds?.[0] ?? "memory",
|
|
1483
1508
|
timestamp: memory.createdAt,
|
|
1484
1509
|
type: "decision",
|
|
1485
1510
|
title: memory.title,
|
|
@@ -3151,7 +3176,7 @@ async function rebuildIndex(kv) {
|
|
|
3151
3176
|
idx.add(memoryToObservation(memory));
|
|
3152
3177
|
await enqueue({
|
|
3153
3178
|
id: memory.id,
|
|
3154
|
-
sessionId: memory.sessionIds[0] ?? "memory",
|
|
3179
|
+
sessionId: memory.sessionIds?.[0] ?? "memory",
|
|
3155
3180
|
text: memory.title + " " + memory.content,
|
|
3156
3181
|
context: {
|
|
3157
3182
|
kind: "memory",
|
|
@@ -3210,8 +3235,8 @@ function registerSearchFunction(sdk, kv) {
|
|
|
3210
3235
|
if (!Number.isInteger(data.limit) || data.limit < 1) throw new Error("mem::search: limit must be a positive integer");
|
|
3211
3236
|
effectiveLimit = Math.min(data.limit, MAX_LIMIT);
|
|
3212
3237
|
}
|
|
3213
|
-
const projectFilter = typeof data.project === "string" && data.project.length > 0 ? data.project : void 0;
|
|
3214
|
-
const cwdFilter = typeof data.cwd === "string" && data.cwd.length > 0 ? data.cwd : void 0;
|
|
3238
|
+
const projectFilter = typeof data.project === "string" && data.project.trim().length > 0 ? data.project.trim() : void 0;
|
|
3239
|
+
const cwdFilter = typeof data.cwd === "string" && data.cwd.trim().length > 0 ? data.cwd.trim() : void 0;
|
|
3215
3240
|
const format = typeof data.format === "string" ? data.format : "full";
|
|
3216
3241
|
if (![
|
|
3217
3242
|
"full",
|
|
@@ -3237,14 +3262,25 @@ function registerSearchFunction(sdk, kv) {
|
|
|
3237
3262
|
sessionCache.set(sessionId, s ?? null);
|
|
3238
3263
|
return s ?? null;
|
|
3239
3264
|
};
|
|
3265
|
+
const memoryProjectCache = /* @__PURE__ */ new Map();
|
|
3266
|
+
const loadMemoryProject = async (obsId) => {
|
|
3267
|
+
if (memoryProjectCache.has(obsId)) return memoryProjectCache.get(obsId);
|
|
3268
|
+
const proj = (await kv.get(KV.memories, obsId).catch(() => null))?.project ?? null;
|
|
3269
|
+
memoryProjectCache.set(obsId, proj);
|
|
3270
|
+
return proj;
|
|
3271
|
+
};
|
|
3240
3272
|
const candidates = [];
|
|
3241
3273
|
for (const r of results) {
|
|
3242
3274
|
if (candidates.length >= effectiveLimit) break;
|
|
3243
3275
|
if (filtering) {
|
|
3244
3276
|
const s = await loadSession(r.sessionId);
|
|
3245
|
-
if (
|
|
3246
|
-
|
|
3247
|
-
|
|
3277
|
+
if (s) {
|
|
3278
|
+
if (projectFilter && s.project !== projectFilter) continue;
|
|
3279
|
+
if (cwdFilter && s.cwd !== cwdFilter) continue;
|
|
3280
|
+
} else if (projectFilter) {
|
|
3281
|
+
const memProject = await loadMemoryProject(r.obsId);
|
|
3282
|
+
if (memProject !== null && memProject !== projectFilter) continue;
|
|
3283
|
+
}
|
|
3248
3284
|
}
|
|
3249
3285
|
candidates.push(r);
|
|
3250
3286
|
}
|
|
@@ -3996,10 +4032,10 @@ const DEFAULT_SLOTS = [
|
|
|
3996
4032
|
}
|
|
3997
4033
|
];
|
|
3998
4034
|
function isSlotsEnabled() {
|
|
3999
|
-
return
|
|
4035
|
+
return getEnvVar("AGENTMEMORY_SLOTS") === "true";
|
|
4000
4036
|
}
|
|
4001
4037
|
function isReflectEnabled() {
|
|
4002
|
-
return
|
|
4038
|
+
return getEnvVar("AGENTMEMORY_REFLECT") === "true";
|
|
4003
4039
|
}
|
|
4004
4040
|
function scopeKv(scope) {
|
|
4005
4041
|
return scope === "global" ? KV.globalSlots : KV.slots;
|
|
@@ -5264,8 +5300,78 @@ function isAllowedPath(dbPath) {
|
|
|
5264
5300
|
const resolved = resolve(dbPath);
|
|
5265
5301
|
return ALLOWED_DIRS.some((dir) => resolved.startsWith(dir + "/"));
|
|
5266
5302
|
}
|
|
5303
|
+
async function inferMemoryProjects(kv, dryRun = false) {
|
|
5304
|
+
const memories = await kv.list(KV.memories);
|
|
5305
|
+
const sessionCache = /* @__PURE__ */ new Map();
|
|
5306
|
+
const loadSession = async (sid) => {
|
|
5307
|
+
if (sessionCache.has(sid)) return sessionCache.get(sid);
|
|
5308
|
+
const s = await kv.get(KV.sessions, sid).catch(() => null);
|
|
5309
|
+
sessionCache.set(sid, s);
|
|
5310
|
+
return s;
|
|
5311
|
+
};
|
|
5312
|
+
let updated = 0;
|
|
5313
|
+
let skipped = 0;
|
|
5314
|
+
let ambiguous = 0;
|
|
5315
|
+
for (const memory of memories) {
|
|
5316
|
+
if (memory.project) {
|
|
5317
|
+
skipped++;
|
|
5318
|
+
continue;
|
|
5319
|
+
}
|
|
5320
|
+
const sessionIds = memory.sessionIds ?? [];
|
|
5321
|
+
if (sessionIds.length === 0) {
|
|
5322
|
+
ambiguous++;
|
|
5323
|
+
continue;
|
|
5324
|
+
}
|
|
5325
|
+
const projects = [];
|
|
5326
|
+
for (const sid of sessionIds) {
|
|
5327
|
+
const session = await loadSession(sid);
|
|
5328
|
+
if (session?.project) projects.push(session.project);
|
|
5329
|
+
}
|
|
5330
|
+
if (projects.length === 0) {
|
|
5331
|
+
ambiguous++;
|
|
5332
|
+
continue;
|
|
5333
|
+
}
|
|
5334
|
+
const freq = /* @__PURE__ */ new Map();
|
|
5335
|
+
for (const p of projects) freq.set(p, (freq.get(p) ?? 0) + 1);
|
|
5336
|
+
const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1]);
|
|
5337
|
+
const [topProject, topCount] = sorted[0];
|
|
5338
|
+
if (topCount <= projects.length / 2 && sorted.length > 1) {
|
|
5339
|
+
ambiguous++;
|
|
5340
|
+
continue;
|
|
5341
|
+
}
|
|
5342
|
+
if (!dryRun) {
|
|
5343
|
+
memory.project = topProject;
|
|
5344
|
+
await kv.set(KV.memories, memory.id, memory);
|
|
5345
|
+
}
|
|
5346
|
+
updated++;
|
|
5347
|
+
}
|
|
5348
|
+
logger.info("inferMemoryProjects complete", {
|
|
5349
|
+
updated,
|
|
5350
|
+
skipped,
|
|
5351
|
+
ambiguous,
|
|
5352
|
+
dryRun
|
|
5353
|
+
});
|
|
5354
|
+
return {
|
|
5355
|
+
updated,
|
|
5356
|
+
skipped,
|
|
5357
|
+
ambiguous
|
|
5358
|
+
};
|
|
5359
|
+
}
|
|
5267
5360
|
function registerMigrateFunction(sdk, kv) {
|
|
5268
5361
|
sdk.registerFunction("mem::migrate", async (data) => {
|
|
5362
|
+
if (data.step === "infer-memory-projects") {
|
|
5363
|
+
const dryRun = data.dryRun ?? false;
|
|
5364
|
+
logger.info("Migration step: infer-memory-projects", { dryRun });
|
|
5365
|
+
return {
|
|
5366
|
+
success: true,
|
|
5367
|
+
step: "infer-memory-projects",
|
|
5368
|
+
...await inferMemoryProjects(kv, dryRun)
|
|
5369
|
+
};
|
|
5370
|
+
}
|
|
5371
|
+
if (!data.dbPath) return {
|
|
5372
|
+
success: false,
|
|
5373
|
+
error: "Either step or dbPath is required"
|
|
5374
|
+
};
|
|
5269
5375
|
logger.info("Migration started", { dbPath: data.dbPath });
|
|
5270
5376
|
if (!isAllowedPath(data.dbPath)) return {
|
|
5271
5377
|
success: false,
|
|
@@ -5541,9 +5647,10 @@ function registerConsolidateFunction(sdk, kv, provider) {
|
|
|
5541
5647
|
llmCallCount++;
|
|
5542
5648
|
const parsed = parseMemoryXml(response, sessionIds);
|
|
5543
5649
|
if (!parsed) continue;
|
|
5544
|
-
const existingMatch = existingMemories.find((m) => m.title.toLowerCase() === parsed.title.toLowerCase());
|
|
5545
5650
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5546
5651
|
const obsIds = [...new Set(top.map((o) => o.id))];
|
|
5652
|
+
const scopedProject = typeof data.project === "string" && data.project.trim().length > 0 ? data.project.trim() : void 0;
|
|
5653
|
+
const existingMatch = existingMemories.find((m) => m.title.toLowerCase() === parsed.title.toLowerCase() && (!scopedProject || !m.project || m.project === scopedProject));
|
|
5547
5654
|
if (existingMatch) {
|
|
5548
5655
|
existingMatch.isLatest = false;
|
|
5549
5656
|
await kv.set(KV.memories, existingMatch.id, existingMatch);
|
|
@@ -5560,7 +5667,8 @@ function registerConsolidateFunction(sdk, kv, provider) {
|
|
|
5560
5667
|
parentId: existingMatch.id,
|
|
5561
5668
|
supersedes: [existingMatch.id, ...existingMatch.supersedes || []],
|
|
5562
5669
|
sourceObservationIds: obsIds,
|
|
5563
|
-
isLatest: true
|
|
5670
|
+
isLatest: true,
|
|
5671
|
+
...scopedProject !== void 0 && { project: scopedProject }
|
|
5564
5672
|
};
|
|
5565
5673
|
await kv.set(KV.memories, evolved.id, evolved);
|
|
5566
5674
|
await recordAudit(kv, "evolve", "mem::consolidate", [evolved.id], {
|
|
@@ -5579,7 +5687,8 @@ function registerConsolidateFunction(sdk, kv, provider) {
|
|
|
5579
5687
|
...parsed,
|
|
5580
5688
|
sourceObservationIds: obsIds,
|
|
5581
5689
|
version: 1,
|
|
5582
|
-
isLatest: true
|
|
5690
|
+
isLatest: true,
|
|
5691
|
+
...scopedProject !== void 0 && { project: scopedProject }
|
|
5583
5692
|
};
|
|
5584
5693
|
await kv.set(KV.memories, memory.id, memory);
|
|
5585
5694
|
await recordAudit(kv, "remember", "mem::consolidate", [memory.id], {
|
|
@@ -5720,6 +5829,7 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5720
5829
|
"fact"
|
|
5721
5830
|
]).has(data.type || "") ? data.type : "fact";
|
|
5722
5831
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5832
|
+
const project = typeof data.project === "string" && data.project.trim().length > 0 ? data.project.trim() : void 0;
|
|
5723
5833
|
return withKeyedLock("mem:remember", async () => {
|
|
5724
5834
|
const existingMemories = await kv.list(KV.memories);
|
|
5725
5835
|
let supersededId;
|
|
@@ -5728,6 +5838,7 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5728
5838
|
const lowerContent = data.content.toLowerCase();
|
|
5729
5839
|
for (const existing of existingMemories) {
|
|
5730
5840
|
if (existing.isLatest === false) continue;
|
|
5841
|
+
if (project && existing.project && existing.project !== project) continue;
|
|
5731
5842
|
if (jaccardSimilarity(lowerContent, existing.content.toLowerCase()) > .7) {
|
|
5732
5843
|
supersededId = existing.id;
|
|
5733
5844
|
supersededVersion = existing.version ?? 1;
|
|
@@ -5752,7 +5863,8 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5752
5863
|
supersedes: supersededId ? [supersededId] : [],
|
|
5753
5864
|
sourceObservationIds: (data.sourceObservationIds || []).filter((id) => typeof id === "string" && id.length > 0),
|
|
5754
5865
|
isLatest: true,
|
|
5755
|
-
...callAgentId ? { agentId: callAgentId } : {}
|
|
5866
|
+
...callAgentId ? { agentId: callAgentId } : {},
|
|
5867
|
+
...project !== void 0 && { project }
|
|
5756
5868
|
};
|
|
5757
5869
|
if (data.ttlDays && typeof data.ttlDays === "number" && data.ttlDays > 0) memory.forgetAfter = new Date(Date.now() + data.ttlDays * 864e5).toISOString();
|
|
5758
5870
|
if (supersededMemory) {
|
|
@@ -5768,7 +5880,7 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5768
5880
|
error: err instanceof Error ? err.message : String(err)
|
|
5769
5881
|
});
|
|
5770
5882
|
}
|
|
5771
|
-
await vectorIndexAddGuarded(memory.id, memory.sessionIds[0] ?? "memory", memory.title + " " + memory.content, {
|
|
5883
|
+
await vectorIndexAddGuarded(memory.id, memory.sessionIds?.[0] ?? "memory", memory.title + " " + memory.content, {
|
|
5772
5884
|
kind: "memory",
|
|
5773
5885
|
logId: memory.id
|
|
5774
5886
|
});
|
|
@@ -5779,7 +5891,8 @@ function registerRememberFunction(sdk, kv) {
|
|
|
5779
5891
|
});
|
|
5780
5892
|
logger.info("Memory saved", {
|
|
5781
5893
|
memId: memory.id,
|
|
5782
|
-
type: memory.type
|
|
5894
|
+
type: memory.type,
|
|
5895
|
+
project: memory.project
|
|
5783
5896
|
});
|
|
5784
5897
|
return {
|
|
5785
5898
|
success: true,
|
|
@@ -6680,7 +6793,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
6680
6793
|
|
|
6681
6794
|
//#endregion
|
|
6682
6795
|
//#region src/version.ts
|
|
6683
|
-
const VERSION = "0.9.
|
|
6796
|
+
const VERSION = "0.9.23";
|
|
6684
6797
|
|
|
6685
6798
|
//#endregion
|
|
6686
6799
|
//#region src/functions/export-import.ts
|
|
@@ -6820,7 +6933,8 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6820
6933
|
"0.9.19",
|
|
6821
6934
|
"0.9.20",
|
|
6822
6935
|
"0.9.21",
|
|
6823
|
-
"0.9.22"
|
|
6936
|
+
"0.9.22",
|
|
6937
|
+
"0.9.23"
|
|
6824
6938
|
]).has(importData.version)) return {
|
|
6825
6939
|
success: false,
|
|
6826
6940
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -6943,6 +7057,7 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6943
7057
|
continue;
|
|
6944
7058
|
}
|
|
6945
7059
|
}
|
|
7060
|
+
if (!Array.isArray(memory.sessionIds)) memory.sessionIds = [];
|
|
6946
7061
|
await kv.set(KV.memories, memory.id, memory);
|
|
6947
7062
|
stats.memories++;
|
|
6948
7063
|
}
|
|
@@ -7146,6 +7261,7 @@ function escapeXml(s) {
|
|
|
7146
7261
|
}
|
|
7147
7262
|
function registerEnrichFunction(sdk, kv) {
|
|
7148
7263
|
sdk.registerFunction("mem::enrich", async (data) => {
|
|
7264
|
+
const project = typeof data.project === "string" && data.project.trim().length > 0 ? data.project.trim() : void 0;
|
|
7149
7265
|
const parts = [];
|
|
7150
7266
|
const fileContextPromise = sdk.trigger({
|
|
7151
7267
|
function_id: "mem::file-context",
|
|
@@ -7159,10 +7275,11 @@ function registerEnrichFunction(sdk, kv) {
|
|
|
7159
7275
|
function_id: "mem::search",
|
|
7160
7276
|
payload: {
|
|
7161
7277
|
query: searchQueries.join(" "),
|
|
7162
|
-
limit: 5
|
|
7278
|
+
limit: 5,
|
|
7279
|
+
...project !== void 0 && { project }
|
|
7163
7280
|
}
|
|
7164
7281
|
}).catch(() => ({ results: [] })) : Promise.resolve({ results: [] });
|
|
7165
|
-
const bugMemoriesPromise = kv.list(KV.memories).then((memories) => memories.filter((m) => m.type === "bug" && m.isLatest && m.files.some((f) => data.files.some((df) => f.includes(df) || df.includes(f)))).sort((a, b) => new Date(b.updatedAt || b.createdAt).getTime() - new Date(a.updatedAt || a.createdAt).getTime())).catch(() => []);
|
|
7282
|
+
const bugMemoriesPromise = kv.list(KV.memories).then((memories) => memories.filter((m) => m.type === "bug" && m.isLatest && (!project || !m.project || m.project === project) && m.files.some((f) => data.files.some((df) => f.includes(df) || df.includes(f)))).sort((a, b) => new Date(b.updatedAt || b.createdAt).getTime() - new Date(a.updatedAt || a.createdAt).getTime())).catch(() => []);
|
|
7166
7283
|
const [fileContext, searchResult, bugMemories] = await Promise.all([
|
|
7167
7284
|
fileContextPromise,
|
|
7168
7285
|
searchPromise,
|
|
@@ -7185,6 +7302,7 @@ function registerEnrichFunction(sdk, kv) {
|
|
|
7185
7302
|
}
|
|
7186
7303
|
logger.info("Enrichment completed", {
|
|
7187
7304
|
sessionId: data.sessionId,
|
|
7305
|
+
project,
|
|
7188
7306
|
fileCount: data.files.length,
|
|
7189
7307
|
contextLength: context.length,
|
|
7190
7308
|
truncated
|
|
@@ -7342,16 +7460,24 @@ function buildGraphExtractionPrompt(observations) {
|
|
|
7342
7460
|
|
|
7343
7461
|
//#endregion
|
|
7344
7462
|
//#region src/functions/graph.ts
|
|
7463
|
+
function parseAttrs(raw) {
|
|
7464
|
+
const attrs = {};
|
|
7465
|
+
const attrRegex = /([A-Za-z_][\w:-]*)="([^"]*)"/g;
|
|
7466
|
+
let m;
|
|
7467
|
+
while ((m = attrRegex.exec(raw)) !== null) attrs[m[1]] = m[2];
|
|
7468
|
+
return attrs;
|
|
7469
|
+
}
|
|
7345
7470
|
function parseGraphXml(xml, observationIds) {
|
|
7346
7471
|
const nodes = [];
|
|
7347
7472
|
const edges = [];
|
|
7348
7473
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7349
|
-
const
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
const
|
|
7353
|
-
const
|
|
7354
|
-
const
|
|
7474
|
+
const entitySelfClose = /<entity\b([^>]*?)\/>/g;
|
|
7475
|
+
const entityWithBody = /<entity\b([^>]*[^/])>([\s\S]*?)<\/entity>/g;
|
|
7476
|
+
const addEntity = (rawAttrs, propsBlock = "") => {
|
|
7477
|
+
const attrs = parseAttrs(rawAttrs);
|
|
7478
|
+
const type = attrs["type"];
|
|
7479
|
+
const name = attrs["name"];
|
|
7480
|
+
if (!type || !name) return;
|
|
7355
7481
|
const properties = {};
|
|
7356
7482
|
const propRegex = /<property\s+key="([^"]+)">([^<]*)<\/property>/g;
|
|
7357
7483
|
let propMatch;
|
|
@@ -7364,17 +7490,23 @@ function parseGraphXml(xml, observationIds) {
|
|
|
7364
7490
|
sourceObservationIds: observationIds,
|
|
7365
7491
|
createdAt: now
|
|
7366
7492
|
});
|
|
7367
|
-
}
|
|
7368
|
-
|
|
7493
|
+
};
|
|
7494
|
+
let match;
|
|
7495
|
+
while ((match = entitySelfClose.exec(xml)) !== null) addEntity(match[1]);
|
|
7496
|
+
while ((match = entityWithBody.exec(xml)) !== null) addEntity(match[1], match[2]);
|
|
7497
|
+
const relRegex = /<relationship\b([^>]*?)\/>/g;
|
|
7369
7498
|
while ((match = relRegex.exec(xml)) !== null) {
|
|
7370
|
-
const
|
|
7371
|
-
const
|
|
7372
|
-
const
|
|
7373
|
-
const
|
|
7374
|
-
|
|
7499
|
+
const attrs = parseAttrs(match[1]);
|
|
7500
|
+
const type = attrs["type"];
|
|
7501
|
+
const sourceName = attrs["source"];
|
|
7502
|
+
const targetName = attrs["target"];
|
|
7503
|
+
if (!type || !sourceName || !targetName) continue;
|
|
7504
|
+
const parsedWeight = parseFloat(attrs["weight"] ?? "");
|
|
7505
|
+
const weight = Number.isFinite(parsedWeight) ? parsedWeight : .5;
|
|
7375
7506
|
const sourceNode = nodes.find((n) => n.name === sourceName);
|
|
7376
7507
|
const targetNode = nodes.find((n) => n.name === targetName);
|
|
7377
|
-
if (sourceNode
|
|
7508
|
+
if (!sourceNode || !targetNode) continue;
|
|
7509
|
+
edges.push({
|
|
7378
7510
|
id: generateId("ge"),
|
|
7379
7511
|
type,
|
|
7380
7512
|
sourceNodeId: sourceNode.id,
|
|
@@ -7588,7 +7720,7 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
|
|
|
7588
7720
|
if (!data?.force && !isConsolidationEnabled()) return {
|
|
7589
7721
|
success: false,
|
|
7590
7722
|
skipped: true,
|
|
7591
|
-
reason: "CONSOLIDATION_ENABLED
|
|
7723
|
+
reason: "Consolidation disabled: set CONSOLIDATION_ENABLED=true or configure an LLM provider (ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY / GEMINI_API_KEY / GOOGLE_API_KEY / MINIMAX_API_KEY / OPENAI_BASE_URL / AGENTMEMORY_PROVIDER=agent-sdk)"
|
|
7592
7724
|
};
|
|
7593
7725
|
const tier = data?.tier || "all";
|
|
7594
7726
|
const decayDays = getConsolidationDecayDays();
|
|
@@ -10758,11 +10890,34 @@ function registerDiagnosticsFunction(sdk, kv) {
|
|
|
10758
10890
|
});
|
|
10759
10891
|
memoryIssues++;
|
|
10760
10892
|
}
|
|
10893
|
+
const latestMemories = memories.filter((m) => m.isLatest);
|
|
10894
|
+
const unscopedCount = latestMemories.filter((m) => !m.project).length;
|
|
10895
|
+
if (unscopedCount === 0) checks.push({
|
|
10896
|
+
name: "memory-project-coverage",
|
|
10897
|
+
category: "memories",
|
|
10898
|
+
status: "pass",
|
|
10899
|
+
message: `All ${latestMemories.length} latest memories have a project scope`,
|
|
10900
|
+
fixable: false
|
|
10901
|
+
});
|
|
10902
|
+
else if (unscopedCount <= 10) checks.push({
|
|
10903
|
+
name: "memory-project-coverage",
|
|
10904
|
+
category: "memories",
|
|
10905
|
+
status: "warn",
|
|
10906
|
+
message: `${unscopedCount} of ${latestMemories.length} latest memories have no project scope — run POST /agentmemory/migrate {"step":"infer-memory-projects"} to backfill`,
|
|
10907
|
+
fixable: true
|
|
10908
|
+
});
|
|
10909
|
+
else checks.push({
|
|
10910
|
+
name: "memory-project-coverage",
|
|
10911
|
+
category: "memories",
|
|
10912
|
+
status: "fail",
|
|
10913
|
+
message: `${unscopedCount} of ${latestMemories.length} latest memories have no project scope — run POST /agentmemory/migrate {"step":"infer-memory-projects"} to backfill`,
|
|
10914
|
+
fixable: true
|
|
10915
|
+
});
|
|
10761
10916
|
if (memoryIssues === 0) checks.push({
|
|
10762
10917
|
name: "memories-ok",
|
|
10763
10918
|
category: "memories",
|
|
10764
10919
|
status: "pass",
|
|
10765
|
-
message: `All ${memories.length} memories are consistent`,
|
|
10920
|
+
message: `All ${memories.length} memories are structurally consistent`,
|
|
10766
10921
|
fixable: false
|
|
10767
10922
|
});
|
|
10768
10923
|
}
|
|
@@ -14535,6 +14690,22 @@ function consolidationDisabledResponse() {
|
|
|
14535
14690
|
docsHref: "https://github.com/rohitg00/agentmemory#consolidation"
|
|
14536
14691
|
});
|
|
14537
14692
|
}
|
|
14693
|
+
function slotsDisabledResponse() {
|
|
14694
|
+
return flagDisabledResponse({
|
|
14695
|
+
error: "Memory slots not enabled",
|
|
14696
|
+
flag: "AGENTMEMORY_SLOTS",
|
|
14697
|
+
enableHow: "Set AGENTMEMORY_SLOTS=true (in ~/.agentmemory/.env or the shell) and restart.",
|
|
14698
|
+
docsHref: "https://github.com/rohitg00/agentmemory#memory-slots"
|
|
14699
|
+
});
|
|
14700
|
+
}
|
|
14701
|
+
function reflectDisabledResponse() {
|
|
14702
|
+
return flagDisabledResponse({
|
|
14703
|
+
error: "Slot reflection not enabled",
|
|
14704
|
+
flag: "AGENTMEMORY_REFLECT",
|
|
14705
|
+
enableHow: "Set AGENTMEMORY_REFLECT=true (in ~/.agentmemory/.env or the shell) and restart. Requires AGENTMEMORY_SLOTS=true.",
|
|
14706
|
+
docsHref: "https://github.com/rohitg00/agentmemory#memory-slots"
|
|
14707
|
+
});
|
|
14708
|
+
}
|
|
14538
14709
|
function asNonEmptyString$1(value) {
|
|
14539
14710
|
if (typeof value !== "string") return null;
|
|
14540
14711
|
const trimmed = value.trim();
|
|
@@ -14979,6 +15150,14 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
14979
15150
|
path: "status",
|
|
14980
15151
|
value: "completed"
|
|
14981
15152
|
}]);
|
|
15153
|
+
try {
|
|
15154
|
+
sdk.triggerVoid("event::session::stopped", { sessionId });
|
|
15155
|
+
} catch (err) {
|
|
15156
|
+
logger.warn("event::session::stopped triggerVoid failed", {
|
|
15157
|
+
sessionId,
|
|
15158
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15159
|
+
});
|
|
15160
|
+
}
|
|
14982
15161
|
return {
|
|
14983
15162
|
status_code: 200,
|
|
14984
15163
|
body: { success: true }
|
|
@@ -15197,11 +15376,21 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15197
15376
|
status_code: 400,
|
|
15198
15377
|
body: { error: "terms must be an array of strings" }
|
|
15199
15378
|
};
|
|
15379
|
+
if (req.body.project !== void 0 && (typeof req.body.project !== "string" || !req.body.project.trim())) return {
|
|
15380
|
+
status_code: 400,
|
|
15381
|
+
body: { error: "project must be a non-empty string" }
|
|
15382
|
+
};
|
|
15200
15383
|
return {
|
|
15201
15384
|
status_code: 200,
|
|
15202
15385
|
body: await sdk.trigger({
|
|
15203
15386
|
function_id: "mem::enrich",
|
|
15204
|
-
payload:
|
|
15387
|
+
payload: {
|
|
15388
|
+
sessionId: req.body.sessionId,
|
|
15389
|
+
files: req.body.files,
|
|
15390
|
+
...req.body.terms !== void 0 && { terms: req.body.terms },
|
|
15391
|
+
...req.body.toolName !== void 0 && { toolName: req.body.toolName },
|
|
15392
|
+
...req.body.project !== void 0 && { project: req.body.project }
|
|
15393
|
+
}
|
|
15205
15394
|
})
|
|
15206
15395
|
};
|
|
15207
15396
|
});
|
|
@@ -15220,11 +15409,23 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15220
15409
|
status_code: 400,
|
|
15221
15410
|
body: { error: "content is required" }
|
|
15222
15411
|
};
|
|
15412
|
+
if (req.body.project !== void 0 && (typeof req.body.project !== "string" || !req.body.project.trim())) return {
|
|
15413
|
+
status_code: 400,
|
|
15414
|
+
body: { error: "project must be a non-empty string" }
|
|
15415
|
+
};
|
|
15223
15416
|
return {
|
|
15224
15417
|
status_code: 201,
|
|
15225
15418
|
body: await sdk.trigger({
|
|
15226
15419
|
function_id: "mem::remember",
|
|
15227
|
-
payload:
|
|
15420
|
+
payload: {
|
|
15421
|
+
content: req.body.content,
|
|
15422
|
+
...req.body.type !== void 0 && { type: req.body.type },
|
|
15423
|
+
...req.body.concepts !== void 0 && { concepts: req.body.concepts },
|
|
15424
|
+
...req.body.files !== void 0 && { files: req.body.files },
|
|
15425
|
+
...req.body.ttlDays !== void 0 && { ttlDays: req.body.ttlDays },
|
|
15426
|
+
...req.body.sourceObservationIds !== void 0 && { sourceObservationIds: req.body.sourceObservationIds },
|
|
15427
|
+
...req.body.project !== void 0 && { project: req.body.project }
|
|
15428
|
+
}
|
|
15228
15429
|
})
|
|
15229
15430
|
};
|
|
15230
15431
|
});
|
|
@@ -15319,15 +15520,21 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15319
15520
|
sdk.registerFunction("api::migrate", async (req) => {
|
|
15320
15521
|
const authErr = checkAuth(req, secret);
|
|
15321
15522
|
if (authErr) return authErr;
|
|
15322
|
-
|
|
15523
|
+
const hasStep = typeof req.body?.step === "string" && req.body.step.trim().length > 0;
|
|
15524
|
+
const hasDbPath = typeof req.body?.dbPath === "string" && req.body.dbPath.trim().length > 0;
|
|
15525
|
+
if (!hasStep && !hasDbPath) return {
|
|
15323
15526
|
status_code: 400,
|
|
15324
|
-
body: { error: "dbPath is required" }
|
|
15527
|
+
body: { error: "Either step (string) or dbPath (string) is required" }
|
|
15325
15528
|
};
|
|
15326
15529
|
return {
|
|
15327
15530
|
status_code: 200,
|
|
15328
15531
|
body: await sdk.trigger({
|
|
15329
15532
|
function_id: "mem::migrate",
|
|
15330
|
-
payload:
|
|
15533
|
+
payload: {
|
|
15534
|
+
...req.body.step !== void 0 && { step: req.body.step },
|
|
15535
|
+
...req.body.dbPath !== void 0 && { dbPath: req.body.dbPath },
|
|
15536
|
+
...req.body.dryRun !== void 0 && { dryRun: req.body.dryRun }
|
|
15537
|
+
}
|
|
15331
15538
|
})
|
|
15332
15539
|
};
|
|
15333
15540
|
});
|
|
@@ -15673,6 +15880,63 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15673
15880
|
http_method: "POST"
|
|
15674
15881
|
}
|
|
15675
15882
|
});
|
|
15883
|
+
sdk.registerFunction("api::graph-build", async (req) => {
|
|
15884
|
+
const authErr = checkAuth(req, secret);
|
|
15885
|
+
if (authErr) return authErr;
|
|
15886
|
+
const batchSize = Math.max(1, Math.min(100, Number(req.body?.batchSize) || 25));
|
|
15887
|
+
try {
|
|
15888
|
+
const sessions = await kv.list(KV.sessions);
|
|
15889
|
+
let totalNodes = 0;
|
|
15890
|
+
let totalEdges = 0;
|
|
15891
|
+
let batchesRun = 0;
|
|
15892
|
+
for (const session of sessions) {
|
|
15893
|
+
const sid = session?.id;
|
|
15894
|
+
if (typeof sid !== "string" || sid.length === 0) continue;
|
|
15895
|
+
const compressed = (await kv.list(KV.observations(sid))).filter((o) => o && typeof o.title === "string" && o.title.length > 0);
|
|
15896
|
+
if (compressed.length === 0) continue;
|
|
15897
|
+
for (let i = 0; i < compressed.length; i += batchSize) {
|
|
15898
|
+
const batch = compressed.slice(i, i + batchSize);
|
|
15899
|
+
try {
|
|
15900
|
+
const result = await sdk.trigger({
|
|
15901
|
+
function_id: "mem::graph-extract",
|
|
15902
|
+
payload: { observations: batch }
|
|
15903
|
+
});
|
|
15904
|
+
if (result?.success) {
|
|
15905
|
+
totalNodes += Number(result.nodesAdded) || 0;
|
|
15906
|
+
totalEdges += Number(result.edgesAdded) || 0;
|
|
15907
|
+
}
|
|
15908
|
+
batchesRun++;
|
|
15909
|
+
} catch (err) {
|
|
15910
|
+
logger.warn("graph-build batch failed", {
|
|
15911
|
+
sessionId: sid,
|
|
15912
|
+
batchIndex: Math.floor(i / batchSize),
|
|
15913
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15914
|
+
});
|
|
15915
|
+
}
|
|
15916
|
+
}
|
|
15917
|
+
}
|
|
15918
|
+
return {
|
|
15919
|
+
status_code: 200,
|
|
15920
|
+
body: {
|
|
15921
|
+
success: true,
|
|
15922
|
+
sessions: sessions.length,
|
|
15923
|
+
batches: batchesRun,
|
|
15924
|
+
nodes: totalNodes,
|
|
15925
|
+
edges: totalEdges
|
|
15926
|
+
}
|
|
15927
|
+
};
|
|
15928
|
+
} catch {
|
|
15929
|
+
return graphDisabledResponse();
|
|
15930
|
+
}
|
|
15931
|
+
});
|
|
15932
|
+
sdk.registerTrigger({
|
|
15933
|
+
type: "http",
|
|
15934
|
+
function_id: "api::graph-build",
|
|
15935
|
+
config: {
|
|
15936
|
+
api_path: "/agentmemory/graph/build",
|
|
15937
|
+
http_method: "POST"
|
|
15938
|
+
}
|
|
15939
|
+
});
|
|
15676
15940
|
sdk.registerFunction("api::consolidate-pipeline", async (req) => {
|
|
15677
15941
|
const authErr = checkAuth(req, secret);
|
|
15678
15942
|
if (authErr) return authErr;
|
|
@@ -16130,6 +16394,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16130
16394
|
sdk.registerFunction("api::slot-list", async (req) => {
|
|
16131
16395
|
const authErr = checkAuth(req, secret);
|
|
16132
16396
|
if (authErr) return authErr;
|
|
16397
|
+
if (!isSlotsEnabled()) return slotsDisabledResponse();
|
|
16133
16398
|
return {
|
|
16134
16399
|
status_code: 200,
|
|
16135
16400
|
body: await sdk.trigger({
|
|
@@ -16149,6 +16414,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16149
16414
|
sdk.registerFunction("api::slot-get", async (req) => {
|
|
16150
16415
|
const authErr = checkAuth(req, secret);
|
|
16151
16416
|
if (authErr) return authErr;
|
|
16417
|
+
if (!isSlotsEnabled()) return slotsDisabledResponse();
|
|
16152
16418
|
const label = asNonEmptyString$1(req.query_params?.["label"]);
|
|
16153
16419
|
if (!label) return {
|
|
16154
16420
|
status_code: 400,
|
|
@@ -16179,6 +16445,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16179
16445
|
sdk.registerFunction("api::slot-create", async (req) => {
|
|
16180
16446
|
const authErr = checkAuth(req, secret);
|
|
16181
16447
|
if (authErr) return authErr;
|
|
16448
|
+
if (!isSlotsEnabled()) return slotsDisabledResponse();
|
|
16182
16449
|
const body = req.body ?? {};
|
|
16183
16450
|
const label = asNonEmptyString$1(body["label"]);
|
|
16184
16451
|
if (!label) return {
|
|
@@ -16241,6 +16508,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16241
16508
|
sdk.registerFunction("api::slot-append", async (req) => {
|
|
16242
16509
|
const authErr = checkAuth(req, secret);
|
|
16243
16510
|
if (authErr) return authErr;
|
|
16511
|
+
if (!isSlotsEnabled()) return slotsDisabledResponse();
|
|
16244
16512
|
const body = req.body ?? {};
|
|
16245
16513
|
const label = asNonEmptyString$1(body["label"]);
|
|
16246
16514
|
const text = typeof body["text"] === "string" ? body["text"] : null;
|
|
@@ -16280,6 +16548,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16280
16548
|
sdk.registerFunction("api::slot-replace", async (req) => {
|
|
16281
16549
|
const authErr = checkAuth(req, secret);
|
|
16282
16550
|
if (authErr) return authErr;
|
|
16551
|
+
if (!isSlotsEnabled()) return slotsDisabledResponse();
|
|
16283
16552
|
const body = req.body ?? {};
|
|
16284
16553
|
const label = asNonEmptyString$1(body["label"]);
|
|
16285
16554
|
const content = body["content"];
|
|
@@ -16319,6 +16588,7 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16319
16588
|
sdk.registerFunction("api::slot-delete", async (req) => {
|
|
16320
16589
|
const authErr = checkAuth(req, secret);
|
|
16321
16590
|
if (authErr) return authErr;
|
|
16591
|
+
if (!isSlotsEnabled()) return slotsDisabledResponse();
|
|
16322
16592
|
const label = asNonEmptyString$1(req.query_params?.["label"]);
|
|
16323
16593
|
if (!label) return {
|
|
16324
16594
|
status_code: 400,
|
|
@@ -16349,6 +16619,8 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16349
16619
|
sdk.registerFunction("api::slot-reflect", async (req) => {
|
|
16350
16620
|
const authErr = checkAuth(req, secret);
|
|
16351
16621
|
if (authErr) return authErr;
|
|
16622
|
+
if (!isSlotsEnabled()) return slotsDisabledResponse();
|
|
16623
|
+
if (!isReflectEnabled()) return reflectDisabledResponse();
|
|
16352
16624
|
const body = req.body ?? {};
|
|
16353
16625
|
const sessionId = asNonEmptyString$1(body["sessionId"]);
|
|
16354
16626
|
if (!sessionId) return {
|
|
@@ -18001,6 +18273,10 @@ const CORE_TOOLS = [
|
|
|
18001
18273
|
files: {
|
|
18002
18274
|
type: "string",
|
|
18003
18275
|
description: "Comma-separated relevant file paths"
|
|
18276
|
+
},
|
|
18277
|
+
project: {
|
|
18278
|
+
type: "string",
|
|
18279
|
+
description: "Stable canonical project identifier this memory belongs to (e.g. a slug, UUID, or registry key). Must match the value used when the session was started. Do not use filesystem paths or ad-hoc display names — those change across machines and will silently break project scoping."
|
|
18004
18280
|
}
|
|
18005
18281
|
},
|
|
18006
18282
|
required: ["content"]
|
|
@@ -19157,13 +19433,15 @@ function registerMcpEndpoints(sdk, kv, secret) {
|
|
|
19157
19433
|
const type = args.type || "fact";
|
|
19158
19434
|
const concepts = typeof args.concepts === "string" ? args.concepts.split(",").map((c) => c.trim()).filter(Boolean) : [];
|
|
19159
19435
|
const files = typeof args.files === "string" ? args.files.split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
19436
|
+
const project = typeof args.project === "string" && args.project.trim().length > 0 ? args.project.trim() : void 0;
|
|
19160
19437
|
const result = await sdk.trigger({
|
|
19161
19438
|
function_id: "mem::remember",
|
|
19162
19439
|
payload: {
|
|
19163
19440
|
content: args.content,
|
|
19164
19441
|
type,
|
|
19165
19442
|
concepts,
|
|
19166
|
-
files
|
|
19443
|
+
files,
|
|
19444
|
+
...project !== void 0 && { project }
|
|
19167
19445
|
}
|
|
19168
19446
|
});
|
|
19169
19447
|
return {
|
|
@@ -21010,7 +21288,7 @@ async function main() {
|
|
|
21010
21288
|
if (bm25Index.has(memory.id)) continue;
|
|
21011
21289
|
bm25Index.add({
|
|
21012
21290
|
id: memory.id,
|
|
21013
|
-
sessionId: memory.sessionIds[0] ?? "memory",
|
|
21291
|
+
sessionId: memory.sessionIds?.[0] ?? "memory",
|
|
21014
21292
|
timestamp: memory.createdAt,
|
|
21015
21293
|
type: "decision",
|
|
21016
21294
|
title: memory.title,
|
|
@@ -21030,7 +21308,7 @@ async function main() {
|
|
|
21030
21308
|
console.warn(`[agentmemory] Failed to backfill memories into BM25:`, err);
|
|
21031
21309
|
}
|
|
21032
21310
|
bootLog(`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
21033
|
-
bootLog(`REST API:
|
|
21311
|
+
bootLog(`REST API: 125 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
|
|
21034
21312
|
bootLog(`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`);
|
|
21035
21313
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
21036
21314
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|