@agentmemory/agentmemory 0.8.11 → 0.9.0
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 +2 -2
- package/README.md +80 -9
- package/dist/cli.mjs +47 -5
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +866 -33
- package/dist/index.mjs.map +1 -1
- package/dist/{src-M6V9yZW5.mjs → src-B3pEsBSb.mjs} +838 -29
- package/dist/src-B3pEsBSb.mjs.map +1 -0
- package/dist/standalone-DXc-BEqr.mjs +457 -0
- package/dist/standalone-DXc-BEqr.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +230 -66
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-DmTkd0if.mjs → tools-registry-DXIK5CxQ.mjs} +31 -7
- package/dist/tools-registry-DXIK5CxQ.mjs.map +1 -0
- package/dist/viewer/index.html +264 -0
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/dist/src-M6V9yZW5.mjs.map +0 -1
- package/dist/standalone-XB9gPYmo.mjs +0 -313
- package/dist/standalone-XB9gPYmo.mjs.map +0 -1
- package/dist/tools-registry-DmTkd0if.mjs.map +0 -1
package/dist/standalone.mjs
CHANGED
|
@@ -154,11 +154,31 @@ const CORE_TOOLS = [
|
|
|
154
154
|
limit: {
|
|
155
155
|
type: "number",
|
|
156
156
|
description: "Max results to return (default 10)"
|
|
157
|
+
},
|
|
158
|
+
format: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "Result format: full, compact, or narrative (default full)"
|
|
161
|
+
},
|
|
162
|
+
token_budget: {
|
|
163
|
+
type: "number",
|
|
164
|
+
description: "Optional token budget to trim returned results"
|
|
157
165
|
}
|
|
158
166
|
},
|
|
159
167
|
required: ["query"]
|
|
160
168
|
}
|
|
161
169
|
},
|
|
170
|
+
{
|
|
171
|
+
name: "memory_compress_file",
|
|
172
|
+
description: "Compress a markdown file to reduce token usage while preserving headings, URLs, and code blocks. Creates a .original.md backup before writing.",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: { filePath: {
|
|
176
|
+
type: "string",
|
|
177
|
+
description: "Path to the markdown file to compress"
|
|
178
|
+
} },
|
|
179
|
+
required: ["filePath"]
|
|
180
|
+
}
|
|
181
|
+
},
|
|
162
182
|
{
|
|
163
183
|
name: "memory_save",
|
|
164
184
|
description: "Explicitly save an important insight, decision, or pattern to long-term memory.",
|
|
@@ -1090,7 +1110,7 @@ function getStandalonePersistPath() {
|
|
|
1090
1110
|
|
|
1091
1111
|
//#endregion
|
|
1092
1112
|
//#region src/version.ts
|
|
1093
|
-
const VERSION = "0.
|
|
1113
|
+
const VERSION = "0.9.0";
|
|
1094
1114
|
|
|
1095
1115
|
//#endregion
|
|
1096
1116
|
//#region src/state/schema.ts
|
|
@@ -1098,6 +1118,81 @@ function generateId(prefix) {
|
|
|
1098
1118
|
return `${prefix}_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
1099
1119
|
}
|
|
1100
1120
|
|
|
1121
|
+
//#endregion
|
|
1122
|
+
//#region src/mcp/rest-proxy.ts
|
|
1123
|
+
const DEFAULT_URL = "http://localhost:3111";
|
|
1124
|
+
const HEALTH_PROBE_TIMEOUT_MS = 500;
|
|
1125
|
+
const CALL_TIMEOUT_MS = 15e3;
|
|
1126
|
+
const LOCAL_MODE_TTL_MS = 3e4;
|
|
1127
|
+
let cached = null;
|
|
1128
|
+
let cachedAt = 0;
|
|
1129
|
+
let probeInFlight = null;
|
|
1130
|
+
function baseUrl() {
|
|
1131
|
+
return (process.env["AGENTMEMORY_URL"] || DEFAULT_URL).replace(/\/+$/, "");
|
|
1132
|
+
}
|
|
1133
|
+
function authHeader() {
|
|
1134
|
+
const secret = process.env["AGENTMEMORY_SECRET"];
|
|
1135
|
+
return secret ? { authorization: `Bearer ${secret}` } : {};
|
|
1136
|
+
}
|
|
1137
|
+
async function probe(url) {
|
|
1138
|
+
try {
|
|
1139
|
+
return (await fetch(`${url}/agentmemory/livez`, {
|
|
1140
|
+
method: "GET",
|
|
1141
|
+
headers: authHeader(),
|
|
1142
|
+
signal: AbortSignal.timeout(HEALTH_PROBE_TIMEOUT_MS)
|
|
1143
|
+
})).ok;
|
|
1144
|
+
} catch {
|
|
1145
|
+
return false;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
function invalidateHandle() {
|
|
1149
|
+
cached = null;
|
|
1150
|
+
cachedAt = 0;
|
|
1151
|
+
}
|
|
1152
|
+
async function resolveHandle() {
|
|
1153
|
+
const now = Date.now();
|
|
1154
|
+
if (cached) if (cached.mode === "local" && now - cachedAt >= LOCAL_MODE_TTL_MS) {
|
|
1155
|
+
cached = null;
|
|
1156
|
+
cachedAt = 0;
|
|
1157
|
+
} else return cached;
|
|
1158
|
+
if (probeInFlight) return probeInFlight;
|
|
1159
|
+
const url = baseUrl();
|
|
1160
|
+
probeInFlight = (async () => {
|
|
1161
|
+
if (await probe(url)) {
|
|
1162
|
+
const handle = {
|
|
1163
|
+
mode: "proxy",
|
|
1164
|
+
baseUrl: url,
|
|
1165
|
+
call: async (path, init) => {
|
|
1166
|
+
const res = await fetch(`${url}${path}`, {
|
|
1167
|
+
...init,
|
|
1168
|
+
headers: {
|
|
1169
|
+
"content-type": "application/json",
|
|
1170
|
+
...authHeader(),
|
|
1171
|
+
...init?.headers
|
|
1172
|
+
},
|
|
1173
|
+
signal: AbortSignal.timeout(CALL_TIMEOUT_MS)
|
|
1174
|
+
});
|
|
1175
|
+
if (!res.ok) throw new Error(`${init?.method || "GET"} ${path} -> ${res.status} ${res.statusText}`);
|
|
1176
|
+
const text = await res.text();
|
|
1177
|
+
return text ? JSON.parse(text) : null;
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
cached = handle;
|
|
1181
|
+
cachedAt = Date.now();
|
|
1182
|
+
return handle;
|
|
1183
|
+
}
|
|
1184
|
+
const local = { mode: "local" };
|
|
1185
|
+
cached = local;
|
|
1186
|
+
cachedAt = Date.now();
|
|
1187
|
+
return local;
|
|
1188
|
+
})();
|
|
1189
|
+
try {
|
|
1190
|
+
return await probeInFlight;
|
|
1191
|
+
} finally {
|
|
1192
|
+
probeInFlight = null;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1101
1196
|
//#endregion
|
|
1102
1197
|
//#region src/mcp/standalone.ts
|
|
1103
1198
|
const IMPLEMENTED_TOOLS = new Set([
|
|
@@ -1115,6 +1210,13 @@ const SERVER_INFO = {
|
|
|
1115
1210
|
protocolVersion: "2024-11-05"
|
|
1116
1211
|
};
|
|
1117
1212
|
const kv = new InMemoryKV(getStandalonePersistPath());
|
|
1213
|
+
let modeAnnounced = false;
|
|
1214
|
+
function announceMode(handle) {
|
|
1215
|
+
if (modeAnnounced) return;
|
|
1216
|
+
modeAnnounced = true;
|
|
1217
|
+
if (handle.mode === "proxy") process.stderr.write(`[@agentmemory/mcp] proxying to agentmemory server at ${handle.baseUrl}\n`);
|
|
1218
|
+
else process.stderr.write(`[@agentmemory/mcp] no server reachable at ${process.env["AGENTMEMORY_URL"] || "http://localhost:3111"}; falling back to local InMemoryKV\n`);
|
|
1219
|
+
}
|
|
1118
1220
|
function normalizeList(value) {
|
|
1119
1221
|
if (!value) return [];
|
|
1120
1222
|
if (Array.isArray(value)) return value.map((v) => typeof v === "string" ? v.trim() : "").filter((v) => v.length > 0);
|
|
@@ -1129,21 +1231,94 @@ function parseLimit(raw, fallback = DEFAULT_LIMIT) {
|
|
|
1129
1231
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
1130
1232
|
return Math.min(Math.floor(n), MAX_LIMIT);
|
|
1131
1233
|
}
|
|
1132
|
-
|
|
1234
|
+
function textResponse(payload, pretty = false) {
|
|
1235
|
+
return { content: [{
|
|
1236
|
+
type: "text",
|
|
1237
|
+
text: JSON.stringify(payload, null, pretty ? 2 : 0)
|
|
1238
|
+
}] };
|
|
1239
|
+
}
|
|
1240
|
+
function validate(toolName, args) {
|
|
1241
|
+
if (!IMPLEMENTED_TOOLS.has(toolName)) throw new Error(`Unknown tool: ${toolName}`);
|
|
1242
|
+
const v = { tool: toolName };
|
|
1133
1243
|
switch (toolName) {
|
|
1134
1244
|
case "memory_save": {
|
|
1135
|
-
const
|
|
1136
|
-
if (typeof
|
|
1137
|
-
|
|
1245
|
+
const content = args["content"];
|
|
1246
|
+
if (typeof content !== "string" || !content.trim()) throw new Error("content is required");
|
|
1247
|
+
v.content = content;
|
|
1248
|
+
v.type = args["type"] || "fact";
|
|
1249
|
+
v.concepts = normalizeList(args["concepts"]);
|
|
1250
|
+
v.files = normalizeList(args["files"]);
|
|
1251
|
+
return v;
|
|
1252
|
+
}
|
|
1253
|
+
case "memory_recall":
|
|
1254
|
+
case "memory_smart_search": {
|
|
1255
|
+
const query = args["query"];
|
|
1256
|
+
if (typeof query !== "string" || !query.trim()) throw new Error("query is required");
|
|
1257
|
+
v.query = query.trim();
|
|
1258
|
+
v.limit = parseLimit(args["limit"]);
|
|
1259
|
+
return v;
|
|
1260
|
+
}
|
|
1261
|
+
case "memory_sessions":
|
|
1262
|
+
v.limit = parseLimit(args["limit"], 20);
|
|
1263
|
+
return v;
|
|
1264
|
+
case "memory_governance_delete": {
|
|
1265
|
+
const ids = normalizeList(args["memoryIds"]);
|
|
1266
|
+
if (ids.length === 0) throw new Error("memoryIds is required");
|
|
1267
|
+
v.memoryIds = ids;
|
|
1268
|
+
v.reason = args["reason"] || "plugin skill request";
|
|
1269
|
+
return v;
|
|
1270
|
+
}
|
|
1271
|
+
case "memory_export": return v;
|
|
1272
|
+
case "memory_audit":
|
|
1273
|
+
v.limit = parseLimit(args["limit"], 50);
|
|
1274
|
+
return v;
|
|
1275
|
+
default: throw new Error(`Unknown tool: ${toolName}`);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
async function handleProxy(v, handle) {
|
|
1279
|
+
switch (v.tool) {
|
|
1280
|
+
case "memory_save": return textResponse(await handle.call("/agentmemory/remember", {
|
|
1281
|
+
method: "POST",
|
|
1282
|
+
body: JSON.stringify({
|
|
1283
|
+
content: v.content,
|
|
1284
|
+
type: v.type,
|
|
1285
|
+
concepts: v.concepts,
|
|
1286
|
+
files: v.files
|
|
1287
|
+
})
|
|
1288
|
+
}));
|
|
1289
|
+
case "memory_recall":
|
|
1290
|
+
case "memory_smart_search": return textResponse(await handle.call("/agentmemory/smart-search", {
|
|
1291
|
+
method: "POST",
|
|
1292
|
+
body: JSON.stringify({
|
|
1293
|
+
query: v.query,
|
|
1294
|
+
limit: v.limit
|
|
1295
|
+
})
|
|
1296
|
+
}), true);
|
|
1297
|
+
case "memory_sessions": return textResponse(await handle.call(`/agentmemory/sessions?limit=${v.limit}`, { method: "GET" }), true);
|
|
1298
|
+
case "memory_governance_delete": return textResponse(await handle.call("/agentmemory/governance/memories", {
|
|
1299
|
+
method: "POST",
|
|
1300
|
+
body: JSON.stringify({
|
|
1301
|
+
memoryIds: v.memoryIds,
|
|
1302
|
+
reason: v.reason
|
|
1303
|
+
})
|
|
1304
|
+
}));
|
|
1305
|
+
case "memory_export": return textResponse(await handle.call("/agentmemory/export", { method: "GET" }), true);
|
|
1306
|
+
case "memory_audit": return textResponse(await handle.call(`/agentmemory/audit?limit=${v.limit}`, { method: "GET" }), true);
|
|
1307
|
+
default: throw new Error(`Unknown tool: ${v.tool}`);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
async function handleLocal(v, kvInstance) {
|
|
1311
|
+
switch (v.tool) {
|
|
1312
|
+
case "memory_save": {
|
|
1138
1313
|
const id = generateId("mem");
|
|
1139
1314
|
const isoNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
1140
1315
|
await kvInstance.set("mem:memories", id, {
|
|
1141
1316
|
id,
|
|
1142
|
-
type:
|
|
1143
|
-
title: content.slice(0, 80),
|
|
1144
|
-
content,
|
|
1145
|
-
concepts:
|
|
1146
|
-
files:
|
|
1317
|
+
type: v.type,
|
|
1318
|
+
title: (v.content || "").slice(0, 80),
|
|
1319
|
+
content: v.content,
|
|
1320
|
+
concepts: v.concepts,
|
|
1321
|
+
files: v.files,
|
|
1147
1322
|
createdAt: isoNow,
|
|
1148
1323
|
updatedAt: isoNow,
|
|
1149
1324
|
strength: 7,
|
|
@@ -1152,80 +1327,69 @@ async function handleToolCall(toolName, args, kvInstance = kv) {
|
|
|
1152
1327
|
sessionIds: []
|
|
1153
1328
|
});
|
|
1154
1329
|
kvInstance.persist();
|
|
1155
|
-
return {
|
|
1156
|
-
type: "text",
|
|
1157
|
-
text: JSON.stringify({ saved: id })
|
|
1158
|
-
}] };
|
|
1330
|
+
return textResponse({ saved: id });
|
|
1159
1331
|
}
|
|
1160
1332
|
case "memory_recall":
|
|
1161
1333
|
case "memory_smart_search": {
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
text: JSON.stringify(results, null, 2)
|
|
1179
|
-
}] };
|
|
1334
|
+
const query = (v.query || "").toLowerCase();
|
|
1335
|
+
const limit = v.limit ?? DEFAULT_LIMIT;
|
|
1336
|
+
return textResponse({
|
|
1337
|
+
mode: "compact",
|
|
1338
|
+
results: (await kvInstance.list("mem:memories")).filter((m) => {
|
|
1339
|
+
const text = [
|
|
1340
|
+
typeof m["title"] === "string" ? m["title"] : "",
|
|
1341
|
+
typeof m["content"] === "string" ? m["content"] : "",
|
|
1342
|
+
Array.isArray(m["files"]) ? m["files"].join(" ") : "",
|
|
1343
|
+
Array.isArray(m["concepts"]) ? m["concepts"].join(" ") : "",
|
|
1344
|
+
Array.isArray(m["sessionIds"]) ? m["sessionIds"].join(" ") : "",
|
|
1345
|
+
typeof m["id"] === "string" ? m["id"] : ""
|
|
1346
|
+
].join(" ").toLowerCase();
|
|
1347
|
+
return query.split(/\s+/).every((word) => text.includes(word));
|
|
1348
|
+
}).slice(0, limit)
|
|
1349
|
+
}, true);
|
|
1180
1350
|
}
|
|
1181
1351
|
case "memory_sessions": {
|
|
1182
1352
|
const sessions = await kvInstance.list("mem:sessions");
|
|
1183
|
-
const limit =
|
|
1184
|
-
return {
|
|
1185
|
-
type: "text",
|
|
1186
|
-
text: JSON.stringify({ sessions: sessions.slice(0, limit) }, null, 2)
|
|
1187
|
-
}] };
|
|
1353
|
+
const limit = v.limit ?? 20;
|
|
1354
|
+
return textResponse({ sessions: sessions.slice(0, limit) }, true);
|
|
1188
1355
|
}
|
|
1189
1356
|
case "memory_governance_delete": {
|
|
1190
|
-
const ids = normalizeList(args.memoryIds);
|
|
1191
|
-
if (ids.length === 0) throw new Error("memoryIds is required");
|
|
1192
1357
|
let deleted = 0;
|
|
1193
|
-
for (const id of
|
|
1358
|
+
for (const id of v.memoryIds || []) if (await kvInstance.get("mem:memories", id)) {
|
|
1194
1359
|
await kvInstance.delete("mem:memories", id);
|
|
1195
1360
|
deleted++;
|
|
1196
1361
|
}
|
|
1197
1362
|
kvInstance.persist();
|
|
1198
|
-
return {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
reason: args.reason || "plugin skill request"
|
|
1204
|
-
})
|
|
1205
|
-
}] };
|
|
1206
|
-
}
|
|
1207
|
-
case "memory_export": {
|
|
1208
|
-
const memories = await kvInstance.list("mem:memories");
|
|
1209
|
-
const sessions = await kvInstance.list("mem:sessions");
|
|
1210
|
-
return { content: [{
|
|
1211
|
-
type: "text",
|
|
1212
|
-
text: JSON.stringify({
|
|
1213
|
-
version: VERSION,
|
|
1214
|
-
memories,
|
|
1215
|
-
sessions
|
|
1216
|
-
}, null, 2)
|
|
1217
|
-
}] };
|
|
1363
|
+
return textResponse({
|
|
1364
|
+
deleted,
|
|
1365
|
+
requested: (v.memoryIds || []).length,
|
|
1366
|
+
reason: v.reason
|
|
1367
|
+
});
|
|
1218
1368
|
}
|
|
1369
|
+
case "memory_export": return textResponse({
|
|
1370
|
+
version: VERSION,
|
|
1371
|
+
memories: await kvInstance.list("mem:memories"),
|
|
1372
|
+
sessions: await kvInstance.list("mem:sessions")
|
|
1373
|
+
}, true);
|
|
1219
1374
|
case "memory_audit": {
|
|
1220
1375
|
const entries = await kvInstance.list("mem:audit");
|
|
1221
|
-
const limit =
|
|
1222
|
-
return {
|
|
1223
|
-
type: "text",
|
|
1224
|
-
text: JSON.stringify(entries.slice(0, limit), null, 2)
|
|
1225
|
-
}] };
|
|
1376
|
+
const limit = v.limit ?? 50;
|
|
1377
|
+
return textResponse({ entries: entries.slice(0, limit) }, true);
|
|
1226
1378
|
}
|
|
1227
|
-
default: throw new Error(`Unknown tool: ${
|
|
1379
|
+
default: throw new Error(`Unknown tool: ${v.tool}`);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
async function handleToolCall(toolName, args, kvInstance = kv) {
|
|
1383
|
+
const validated = validate(toolName, args);
|
|
1384
|
+
const handle = await resolveHandle();
|
|
1385
|
+
announceMode(handle);
|
|
1386
|
+
if (handle.mode === "proxy") try {
|
|
1387
|
+
return await handleProxy(validated, handle);
|
|
1388
|
+
} catch (err) {
|
|
1389
|
+
process.stderr.write(`[@agentmemory/mcp] proxy call failed for ${toolName}: ${err instanceof Error ? err.message : String(err)}; invalidating handle and falling back to local KV\n`);
|
|
1390
|
+
invalidateHandle();
|
|
1228
1391
|
}
|
|
1392
|
+
return handleLocal(validated, kvInstance);
|
|
1229
1393
|
}
|
|
1230
1394
|
const transport = createStdioTransport(async (method, params) => {
|
|
1231
1395
|
switch (method) {
|