@smyslenny/agent-memory 5.0.2 → 5.1.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/CHANGELOG.md +39 -1
- package/README.md +1 -1
- package/dist/bin/agent-memory.js +197 -11
- package/dist/bin/agent-memory.js.map +1 -1
- package/dist/index.d.ts +65 -3
- package/dist/index.js +266 -11
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +336 -14
- package/dist/mcp/server.js.map +1 -1
- package/docs/README-zh.md +1 -2
- package/docs/integrations/generic.md +43 -3
- package/docs/integrations/openclaw.md +47 -7
- package/docs/migration-v3-v4.md +15 -0
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -99,6 +99,11 @@ function migrateDatabase(db, from, to) {
|
|
|
99
99
|
v = 7;
|
|
100
100
|
continue;
|
|
101
101
|
}
|
|
102
|
+
if (v === 7) {
|
|
103
|
+
migrateV7ToV8(db);
|
|
104
|
+
v = 8;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
102
107
|
throw new Error(`Unsupported schema migration path: v${from} \u2192 v${to} (stuck at v${v})`);
|
|
103
108
|
}
|
|
104
109
|
}
|
|
@@ -186,6 +191,8 @@ function inferSchemaVersion(db) {
|
|
|
186
191
|
const hasFeedbackEvents = tableExists(db, "feedback_events");
|
|
187
192
|
const hasEmotionTag = tableHasColumn(db, "memories", "emotion_tag");
|
|
188
193
|
const hasProvenance = tableHasColumn(db, "memories", "source_session") && tableHasColumn(db, "memories", "source_context") && tableHasColumn(db, "memories", "observed_at");
|
|
194
|
+
const hasMemoryArchive = tableExists(db, "memory_archive");
|
|
195
|
+
if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag && hasProvenance && hasMemoryArchive) return 8;
|
|
189
196
|
if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag && hasProvenance) return 7;
|
|
190
197
|
if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag) return 6;
|
|
191
198
|
if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents) return 5;
|
|
@@ -227,6 +234,11 @@ function ensureIndexes(db) {
|
|
|
227
234
|
db.exec("CREATE INDEX IF NOT EXISTS idx_feedback_events_agent_source ON feedback_events(agent_id, source, created_at DESC);");
|
|
228
235
|
}
|
|
229
236
|
}
|
|
237
|
+
if (tableExists(db, "memory_archive")) {
|
|
238
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_agent ON memory_archive(agent_id);");
|
|
239
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_type ON memory_archive(type);");
|
|
240
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_archived_at ON memory_archive(archived_at);");
|
|
241
|
+
}
|
|
230
242
|
}
|
|
231
243
|
function ensureFeedbackEventSchema(db) {
|
|
232
244
|
if (!tableExists(db, "feedback_events")) return;
|
|
@@ -402,11 +414,55 @@ function migrateV6ToV7(db) {
|
|
|
402
414
|
throw e;
|
|
403
415
|
}
|
|
404
416
|
}
|
|
417
|
+
function migrateV7ToV8(db) {
|
|
418
|
+
if (tableExists(db, "memory_archive")) {
|
|
419
|
+
db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(8));
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
try {
|
|
423
|
+
db.exec("BEGIN");
|
|
424
|
+
db.exec(`
|
|
425
|
+
CREATE TABLE IF NOT EXISTS memory_archive (
|
|
426
|
+
id TEXT PRIMARY KEY,
|
|
427
|
+
content TEXT NOT NULL,
|
|
428
|
+
type TEXT NOT NULL,
|
|
429
|
+
priority INTEGER NOT NULL,
|
|
430
|
+
emotion_val REAL NOT NULL DEFAULT 0.0,
|
|
431
|
+
vitality REAL NOT NULL DEFAULT 0.0,
|
|
432
|
+
stability REAL NOT NULL DEFAULT 1.0,
|
|
433
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
434
|
+
last_accessed TEXT,
|
|
435
|
+
created_at TEXT NOT NULL,
|
|
436
|
+
updated_at TEXT NOT NULL,
|
|
437
|
+
archived_at TEXT NOT NULL,
|
|
438
|
+
archive_reason TEXT NOT NULL DEFAULT 'eviction',
|
|
439
|
+
source TEXT,
|
|
440
|
+
agent_id TEXT NOT NULL DEFAULT 'default',
|
|
441
|
+
hash TEXT,
|
|
442
|
+
emotion_tag TEXT,
|
|
443
|
+
source_session TEXT,
|
|
444
|
+
source_context TEXT,
|
|
445
|
+
observed_at TEXT
|
|
446
|
+
);
|
|
447
|
+
`);
|
|
448
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_agent ON memory_archive(agent_id);");
|
|
449
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_type ON memory_archive(type);");
|
|
450
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_archived_at ON memory_archive(archived_at);");
|
|
451
|
+
db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(8));
|
|
452
|
+
db.exec("COMMIT");
|
|
453
|
+
} catch (e) {
|
|
454
|
+
try {
|
|
455
|
+
db.exec("ROLLBACK");
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
throw e;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
405
461
|
var SCHEMA_VERSION, SCHEMA_SQL;
|
|
406
462
|
var init_db = __esm({
|
|
407
463
|
"src/core/db.ts"() {
|
|
408
464
|
"use strict";
|
|
409
|
-
SCHEMA_VERSION =
|
|
465
|
+
SCHEMA_VERSION = 8;
|
|
410
466
|
SCHEMA_SQL = `
|
|
411
467
|
-- Memory entries
|
|
412
468
|
CREATE TABLE IF NOT EXISTS memories (
|
|
@@ -512,6 +568,30 @@ CREATE TABLE IF NOT EXISTS schema_meta (
|
|
|
512
568
|
value TEXT NOT NULL
|
|
513
569
|
);
|
|
514
570
|
|
|
571
|
+
-- Memory archive (eviction archive)
|
|
572
|
+
CREATE TABLE IF NOT EXISTS memory_archive (
|
|
573
|
+
id TEXT PRIMARY KEY,
|
|
574
|
+
content TEXT NOT NULL,
|
|
575
|
+
type TEXT NOT NULL,
|
|
576
|
+
priority INTEGER NOT NULL,
|
|
577
|
+
emotion_val REAL NOT NULL DEFAULT 0.0,
|
|
578
|
+
vitality REAL NOT NULL DEFAULT 0.0,
|
|
579
|
+
stability REAL NOT NULL DEFAULT 1.0,
|
|
580
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
581
|
+
last_accessed TEXT,
|
|
582
|
+
created_at TEXT NOT NULL,
|
|
583
|
+
updated_at TEXT NOT NULL,
|
|
584
|
+
archived_at TEXT NOT NULL,
|
|
585
|
+
archive_reason TEXT NOT NULL DEFAULT 'eviction',
|
|
586
|
+
source TEXT,
|
|
587
|
+
agent_id TEXT NOT NULL DEFAULT 'default',
|
|
588
|
+
hash TEXT,
|
|
589
|
+
emotion_tag TEXT,
|
|
590
|
+
source_session TEXT,
|
|
591
|
+
source_context TEXT,
|
|
592
|
+
observed_at TEXT
|
|
593
|
+
);
|
|
594
|
+
|
|
515
595
|
-- Indexes for common queries
|
|
516
596
|
CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
|
|
517
597
|
CREATE INDEX IF NOT EXISTS idx_memories_priority ON memories(priority);
|
|
@@ -522,6 +602,9 @@ CREATE INDEX IF NOT EXISTS idx_paths_memory ON paths(memory_id);
|
|
|
522
602
|
CREATE INDEX IF NOT EXISTS idx_paths_domain ON paths(domain);
|
|
523
603
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_phase_status ON maintenance_jobs(phase, status, started_at DESC);
|
|
524
604
|
CREATE INDEX IF NOT EXISTS idx_feedback_events_memory ON feedback_events(memory_id, created_at DESC);
|
|
605
|
+
CREATE INDEX IF NOT EXISTS idx_memory_archive_agent ON memory_archive(agent_id);
|
|
606
|
+
CREATE INDEX IF NOT EXISTS idx_memory_archive_type ON memory_archive(type);
|
|
607
|
+
CREATE INDEX IF NOT EXISTS idx_memory_archive_archived_at ON memory_archive(archived_at);
|
|
525
608
|
`;
|
|
526
609
|
}
|
|
527
610
|
});
|
|
@@ -1037,13 +1120,17 @@ var init_vector = __esm({
|
|
|
1037
1120
|
// src/core/memory.ts
|
|
1038
1121
|
var memory_exports = {};
|
|
1039
1122
|
__export(memory_exports, {
|
|
1123
|
+
archiveMemory: () => archiveMemory,
|
|
1040
1124
|
contentHash: () => contentHash,
|
|
1041
1125
|
countMemories: () => countMemories,
|
|
1042
1126
|
createMemory: () => createMemory,
|
|
1043
1127
|
deleteMemory: () => deleteMemory,
|
|
1044
1128
|
getMemory: () => getMemory,
|
|
1129
|
+
listArchivedMemories: () => listArchivedMemories,
|
|
1045
1130
|
listMemories: () => listMemories,
|
|
1131
|
+
purgeArchive: () => purgeArchive,
|
|
1046
1132
|
recordAccess: () => recordAccess,
|
|
1133
|
+
restoreMemory: () => restoreMemory,
|
|
1047
1134
|
updateMemory: () => updateMemory
|
|
1048
1135
|
});
|
|
1049
1136
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -1162,9 +1249,118 @@ function updateMemory(db, id, input) {
|
|
|
1162
1249
|
}
|
|
1163
1250
|
function deleteMemory(db, id) {
|
|
1164
1251
|
db.prepare("DELETE FROM memories_fts WHERE id = ?").run(id);
|
|
1252
|
+
try {
|
|
1253
|
+
db.prepare("DELETE FROM embeddings WHERE memory_id = ?").run(id);
|
|
1254
|
+
} catch {
|
|
1255
|
+
}
|
|
1165
1256
|
const result = db.prepare("DELETE FROM memories WHERE id = ?").run(id);
|
|
1166
1257
|
return result.changes > 0;
|
|
1167
1258
|
}
|
|
1259
|
+
function archiveMemory(db, id, reason, opts) {
|
|
1260
|
+
const mem = getMemory(db, id);
|
|
1261
|
+
if (!mem) return false;
|
|
1262
|
+
const minVitality = opts?.minVitality ?? 0.1;
|
|
1263
|
+
if (mem.vitality < minVitality) {
|
|
1264
|
+
deleteMemory(db, id);
|
|
1265
|
+
return "deleted";
|
|
1266
|
+
}
|
|
1267
|
+
const archivedAt = now();
|
|
1268
|
+
const archiveReason = reason ?? "eviction";
|
|
1269
|
+
db.prepare(
|
|
1270
|
+
`INSERT OR REPLACE INTO memory_archive
|
|
1271
|
+
(id, content, type, priority, emotion_val, vitality, stability, access_count,
|
|
1272
|
+
last_accessed, created_at, updated_at, archived_at, archive_reason,
|
|
1273
|
+
source, agent_id, hash, emotion_tag, source_session, source_context, observed_at)
|
|
1274
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
1275
|
+
).run(
|
|
1276
|
+
mem.id,
|
|
1277
|
+
mem.content,
|
|
1278
|
+
mem.type,
|
|
1279
|
+
mem.priority,
|
|
1280
|
+
mem.emotion_val,
|
|
1281
|
+
mem.vitality,
|
|
1282
|
+
mem.stability,
|
|
1283
|
+
mem.access_count,
|
|
1284
|
+
mem.last_accessed,
|
|
1285
|
+
mem.created_at,
|
|
1286
|
+
mem.updated_at,
|
|
1287
|
+
archivedAt,
|
|
1288
|
+
archiveReason,
|
|
1289
|
+
mem.source,
|
|
1290
|
+
mem.agent_id,
|
|
1291
|
+
mem.hash,
|
|
1292
|
+
mem.emotion_tag,
|
|
1293
|
+
mem.source_session,
|
|
1294
|
+
mem.source_context,
|
|
1295
|
+
mem.observed_at
|
|
1296
|
+
);
|
|
1297
|
+
deleteMemory(db, id);
|
|
1298
|
+
return "archived";
|
|
1299
|
+
}
|
|
1300
|
+
function restoreMemory(db, id) {
|
|
1301
|
+
const archived = db.prepare("SELECT * FROM memory_archive WHERE id = ?").get(id);
|
|
1302
|
+
if (!archived) return null;
|
|
1303
|
+
db.prepare(
|
|
1304
|
+
`INSERT INTO memories
|
|
1305
|
+
(id, content, type, priority, emotion_val, vitality, stability, access_count,
|
|
1306
|
+
last_accessed, created_at, updated_at, source, agent_id, hash, emotion_tag,
|
|
1307
|
+
source_session, source_context, observed_at)
|
|
1308
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
1309
|
+
).run(
|
|
1310
|
+
archived.id,
|
|
1311
|
+
archived.content,
|
|
1312
|
+
archived.type,
|
|
1313
|
+
archived.priority,
|
|
1314
|
+
archived.emotion_val,
|
|
1315
|
+
archived.vitality,
|
|
1316
|
+
archived.stability,
|
|
1317
|
+
archived.access_count,
|
|
1318
|
+
archived.last_accessed,
|
|
1319
|
+
archived.created_at,
|
|
1320
|
+
now(),
|
|
1321
|
+
// updated_at = restore time
|
|
1322
|
+
archived.source,
|
|
1323
|
+
archived.agent_id,
|
|
1324
|
+
archived.hash,
|
|
1325
|
+
archived.emotion_tag,
|
|
1326
|
+
archived.source_session,
|
|
1327
|
+
archived.source_context,
|
|
1328
|
+
archived.observed_at
|
|
1329
|
+
);
|
|
1330
|
+
db.prepare("INSERT INTO memories_fts (id, content) VALUES (?, ?)").run(
|
|
1331
|
+
archived.id,
|
|
1332
|
+
tokenizeForIndex(archived.content)
|
|
1333
|
+
);
|
|
1334
|
+
if (archived.hash) {
|
|
1335
|
+
const providerId = getConfiguredEmbeddingProviderId();
|
|
1336
|
+
if (providerId) {
|
|
1337
|
+
try {
|
|
1338
|
+
markMemoryEmbeddingPending(db, archived.id, providerId, archived.hash);
|
|
1339
|
+
} catch {
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
db.prepare("DELETE FROM memory_archive WHERE id = ?").run(id);
|
|
1344
|
+
return getMemory(db, archived.id);
|
|
1345
|
+
}
|
|
1346
|
+
function listArchivedMemories(db, opts) {
|
|
1347
|
+
const agentId = opts?.agent_id;
|
|
1348
|
+
const limit = opts?.limit ?? 20;
|
|
1349
|
+
if (agentId) {
|
|
1350
|
+
return db.prepare(
|
|
1351
|
+
"SELECT * FROM memory_archive WHERE agent_id = ? ORDER BY archived_at DESC LIMIT ?"
|
|
1352
|
+
).all(agentId, limit);
|
|
1353
|
+
}
|
|
1354
|
+
return db.prepare(
|
|
1355
|
+
"SELECT * FROM memory_archive ORDER BY archived_at DESC LIMIT ?"
|
|
1356
|
+
).all(limit);
|
|
1357
|
+
}
|
|
1358
|
+
function purgeArchive(db, opts) {
|
|
1359
|
+
if (opts?.agent_id) {
|
|
1360
|
+
return db.prepare("DELETE FROM memory_archive WHERE agent_id = ?").run(opts.agent_id).changes;
|
|
1361
|
+
}
|
|
1362
|
+
return db.prepare("DELETE FROM memory_archive").run().changes;
|
|
1363
|
+
}
|
|
1168
1364
|
function listMemories(db, opts) {
|
|
1169
1365
|
const conditions = [];
|
|
1170
1366
|
const params = [];
|
|
@@ -3158,13 +3354,31 @@ function rankEvictionCandidates(db, opts) {
|
|
|
3158
3354
|
return left.memory.priority - right.memory.priority;
|
|
3159
3355
|
});
|
|
3160
3356
|
}
|
|
3357
|
+
function parseEnvInt(envKey) {
|
|
3358
|
+
const raw = process.env[envKey];
|
|
3359
|
+
if (raw === void 0 || raw === "") return null;
|
|
3360
|
+
const n = Number.parseInt(raw, 10);
|
|
3361
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
3362
|
+
}
|
|
3363
|
+
function getTieredCapacity(opts) {
|
|
3364
|
+
const envMax = parseEnvInt("AGENT_MEMORY_MAX_MEMORIES");
|
|
3365
|
+
return {
|
|
3366
|
+
identity: parseEnvInt("AGENT_MEMORY_MAX_IDENTITY"),
|
|
3367
|
+
// default: null (unlimited)
|
|
3368
|
+
emotion: parseEnvInt("AGENT_MEMORY_MAX_EMOTION") ?? 50,
|
|
3369
|
+
knowledge: parseEnvInt("AGENT_MEMORY_MAX_KNOWLEDGE") ?? 250,
|
|
3370
|
+
event: parseEnvInt("AGENT_MEMORY_MAX_EVENT") ?? 50,
|
|
3371
|
+
total: opts?.maxMemories ?? (envMax ?? 350)
|
|
3372
|
+
};
|
|
3373
|
+
}
|
|
3161
3374
|
function runGovern(db, opts) {
|
|
3162
3375
|
const agentId = opts?.agent_id;
|
|
3163
|
-
const
|
|
3164
|
-
const maxMemories = opts?.maxMemories ?? (Number.isFinite(envMax) && envMax > 0 ? envMax : 200);
|
|
3376
|
+
const capacity = getTieredCapacity(opts);
|
|
3165
3377
|
let orphanPaths = 0;
|
|
3166
3378
|
let emptyMemories = 0;
|
|
3167
3379
|
let evicted = 0;
|
|
3380
|
+
let archived = 0;
|
|
3381
|
+
const evictedByType = {};
|
|
3168
3382
|
const transaction = db.transaction(() => {
|
|
3169
3383
|
const pathResult = agentId ? db.prepare(
|
|
3170
3384
|
`DELETE FROM paths
|
|
@@ -3174,17 +3388,48 @@ function runGovern(db, opts) {
|
|
|
3174
3388
|
orphanPaths = pathResult.changes;
|
|
3175
3389
|
const emptyResult = agentId ? db.prepare("DELETE FROM memories WHERE agent_id = ? AND TRIM(content) = ''").run(agentId) : db.prepare("DELETE FROM memories WHERE TRIM(content) = ''").run();
|
|
3176
3390
|
emptyMemories = emptyResult.changes;
|
|
3391
|
+
const typeLimits = [
|
|
3392
|
+
{ type: "identity", limit: capacity.identity },
|
|
3393
|
+
{ type: "emotion", limit: capacity.emotion },
|
|
3394
|
+
{ type: "knowledge", limit: capacity.knowledge },
|
|
3395
|
+
{ type: "event", limit: capacity.event }
|
|
3396
|
+
];
|
|
3397
|
+
const allCandidates = rankEvictionCandidates(db, { agent_id: agentId });
|
|
3398
|
+
const evictedIds = /* @__PURE__ */ new Set();
|
|
3399
|
+
for (const { type, limit } of typeLimits) {
|
|
3400
|
+
if (limit === null) continue;
|
|
3401
|
+
const typeCount = db.prepare(
|
|
3402
|
+
agentId ? "SELECT COUNT(*) as c FROM memories WHERE agent_id = ? AND type = ?" : "SELECT COUNT(*) as c FROM memories WHERE type = ?"
|
|
3403
|
+
).get(...agentId ? [agentId, type] : [type]).c;
|
|
3404
|
+
const excess = Math.max(0, typeCount - limit);
|
|
3405
|
+
if (excess <= 0) continue;
|
|
3406
|
+
const typeCandidates = allCandidates.filter((c) => c.memory.type === type && !evictedIds.has(c.memory.id));
|
|
3407
|
+
const toEvict = typeCandidates.slice(0, excess);
|
|
3408
|
+
for (const candidate of toEvict) {
|
|
3409
|
+
const result = archiveMemory(db, candidate.memory.id, "eviction");
|
|
3410
|
+
evictedIds.add(candidate.memory.id);
|
|
3411
|
+
evicted += 1;
|
|
3412
|
+
if (result === "archived") archived += 1;
|
|
3413
|
+
evictedByType[type] = (evictedByType[type] ?? 0) + 1;
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3177
3416
|
const total = db.prepare(agentId ? "SELECT COUNT(*) as c FROM memories WHERE agent_id = ?" : "SELECT COUNT(*) as c FROM memories").get(...agentId ? [agentId] : []).c;
|
|
3178
|
-
const
|
|
3179
|
-
if (
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3417
|
+
const globalExcess = Math.max(0, total - capacity.total);
|
|
3418
|
+
if (globalExcess > 0) {
|
|
3419
|
+
const globalCandidates = allCandidates.filter((c) => !evictedIds.has(c.memory.id));
|
|
3420
|
+
const toEvict = globalCandidates.slice(0, globalExcess);
|
|
3421
|
+
for (const candidate of toEvict) {
|
|
3422
|
+
const result = archiveMemory(db, candidate.memory.id, "eviction");
|
|
3423
|
+
evictedIds.add(candidate.memory.id);
|
|
3424
|
+
evicted += 1;
|
|
3425
|
+
if (result === "archived") archived += 1;
|
|
3426
|
+
const t = candidate.memory.type;
|
|
3427
|
+
evictedByType[t] = (evictedByType[t] ?? 0) + 1;
|
|
3428
|
+
}
|
|
3184
3429
|
}
|
|
3185
3430
|
});
|
|
3186
3431
|
transaction();
|
|
3187
|
-
return { orphanPaths, emptyMemories, evicted };
|
|
3432
|
+
return { orphanPaths, emptyMemories, evicted, archived, evictedByType };
|
|
3188
3433
|
}
|
|
3189
3434
|
|
|
3190
3435
|
// src/sleep/jobs.ts
|
|
@@ -3427,12 +3672,21 @@ function getMemoryStatus(db, input) {
|
|
|
3427
3672
|
const feedbackEvents = db.prepare(
|
|
3428
3673
|
"SELECT COUNT(*) as c FROM feedback_events WHERE agent_id = ?"
|
|
3429
3674
|
).get(agentId);
|
|
3675
|
+
const tiered = getTieredCapacity();
|
|
3676
|
+
const capacity = {
|
|
3677
|
+
identity: { count: stats.by_type.identity ?? 0, limit: tiered.identity },
|
|
3678
|
+
emotion: { count: stats.by_type.emotion ?? 0, limit: tiered.emotion },
|
|
3679
|
+
knowledge: { count: stats.by_type.knowledge ?? 0, limit: tiered.knowledge },
|
|
3680
|
+
event: { count: stats.by_type.event ?? 0, limit: tiered.event },
|
|
3681
|
+
total: { count: stats.total, limit: tiered.total }
|
|
3682
|
+
};
|
|
3430
3683
|
return {
|
|
3431
3684
|
...stats,
|
|
3432
3685
|
paths: totalPaths.c,
|
|
3433
3686
|
low_vitality: lowVitality.c,
|
|
3434
3687
|
feedback_events: feedbackEvents.c,
|
|
3435
|
-
agent_id: agentId
|
|
3688
|
+
agent_id: agentId,
|
|
3689
|
+
capacity
|
|
3436
3690
|
};
|
|
3437
3691
|
}
|
|
3438
3692
|
|
|
@@ -3617,7 +3871,7 @@ function createMcpServer(dbPath, agentId) {
|
|
|
3617
3871
|
const aid = agentId ?? AGENT_ID;
|
|
3618
3872
|
const server = new McpServer({
|
|
3619
3873
|
name: "agent-memory",
|
|
3620
|
-
version: "5.
|
|
3874
|
+
version: "5.1.0"
|
|
3621
3875
|
});
|
|
3622
3876
|
server.tool(
|
|
3623
3877
|
"remember",
|
|
@@ -3768,8 +4022,8 @@ function createMcpServer(dbPath, agentId) {
|
|
|
3768
4022
|
return { content: [{ type: "text", text: '{"error": "Memory not found"}' }] };
|
|
3769
4023
|
}
|
|
3770
4024
|
if (hard) {
|
|
3771
|
-
const { deleteMemory:
|
|
3772
|
-
|
|
4025
|
+
const { deleteMemory: deleteMemory3 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
|
|
4026
|
+
deleteMemory3(db, id);
|
|
3773
4027
|
return { content: [{ type: "text", text: JSON.stringify({ action: "deleted", id }) }] };
|
|
3774
4028
|
}
|
|
3775
4029
|
updateMemory(db, id, { vitality: Math.max(0, memory.vitality * 0.1) });
|
|
@@ -3929,6 +4183,74 @@ function createMcpServer(dbPath, agentId) {
|
|
|
3929
4183
|
};
|
|
3930
4184
|
}
|
|
3931
4185
|
);
|
|
4186
|
+
server.tool(
|
|
4187
|
+
"archive",
|
|
4188
|
+
"Manage archived memories (evicted by governance). List, restore, or purge archived memories.",
|
|
4189
|
+
{
|
|
4190
|
+
action: z.enum(["list", "restore", "purge"]).describe("list: view archived memories; restore: recover one by id; purge: permanently delete all archived"),
|
|
4191
|
+
id: z.string().optional().describe("Memory ID to restore (required for restore action)"),
|
|
4192
|
+
agent_id: z.string().optional().describe("Override agent scope (defaults to current agent)"),
|
|
4193
|
+
limit: z.number().min(1).max(100).default(20).optional().describe("Max results for list (default 20)")
|
|
4194
|
+
},
|
|
4195
|
+
async ({ action, id, agent_id, limit }) => {
|
|
4196
|
+
const effectiveAgentId = agent_id ?? aid;
|
|
4197
|
+
if (action === "list") {
|
|
4198
|
+
const archived = listArchivedMemories(db, { agent_id: effectiveAgentId, limit: limit ?? 20 });
|
|
4199
|
+
return {
|
|
4200
|
+
content: [{
|
|
4201
|
+
type: "text",
|
|
4202
|
+
text: JSON.stringify({
|
|
4203
|
+
action: "list",
|
|
4204
|
+
count: archived.length,
|
|
4205
|
+
memories: archived.map((m) => ({
|
|
4206
|
+
id: m.id,
|
|
4207
|
+
content: m.content.slice(0, 200),
|
|
4208
|
+
type: m.type,
|
|
4209
|
+
priority: m.priority,
|
|
4210
|
+
vitality: m.vitality,
|
|
4211
|
+
archived_at: m.archived_at,
|
|
4212
|
+
archive_reason: m.archive_reason
|
|
4213
|
+
}))
|
|
4214
|
+
}, null, 2)
|
|
4215
|
+
}]
|
|
4216
|
+
};
|
|
4217
|
+
}
|
|
4218
|
+
if (action === "restore") {
|
|
4219
|
+
if (!id) {
|
|
4220
|
+
return { content: [{ type: "text", text: '{"error": "id is required for restore action"}' }] };
|
|
4221
|
+
}
|
|
4222
|
+
const restored = restoreMemory(db, id);
|
|
4223
|
+
if (!restored) {
|
|
4224
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "Archived memory not found", id }) }] };
|
|
4225
|
+
}
|
|
4226
|
+
return {
|
|
4227
|
+
content: [{
|
|
4228
|
+
type: "text",
|
|
4229
|
+
text: JSON.stringify({
|
|
4230
|
+
action: "restored",
|
|
4231
|
+
memory: {
|
|
4232
|
+
id: restored.id,
|
|
4233
|
+
content: restored.content,
|
|
4234
|
+
type: restored.type,
|
|
4235
|
+
priority: restored.priority,
|
|
4236
|
+
vitality: restored.vitality
|
|
4237
|
+
}
|
|
4238
|
+
}, null, 2)
|
|
4239
|
+
}]
|
|
4240
|
+
};
|
|
4241
|
+
}
|
|
4242
|
+
if (action === "purge") {
|
|
4243
|
+
const purged = purgeArchive(db, { agent_id: effectiveAgentId });
|
|
4244
|
+
return {
|
|
4245
|
+
content: [{
|
|
4246
|
+
type: "text",
|
|
4247
|
+
text: JSON.stringify({ action: "purged", deleted: purged }, null, 2)
|
|
4248
|
+
}]
|
|
4249
|
+
};
|
|
4250
|
+
}
|
|
4251
|
+
return { content: [{ type: "text", text: '{"error": "Unknown action"}' }] };
|
|
4252
|
+
}
|
|
4253
|
+
);
|
|
3932
4254
|
return { server, db };
|
|
3933
4255
|
}
|
|
3934
4256
|
async function main() {
|