@smyslenny/agent-memory 5.0.1 → 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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // AgentMemory v2 — Sleep-cycle memory for AI agents
2
+ // AgentMemory — Sleep-cycle memory for AI agents
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __esm = (fn, res) => function __init() {
@@ -100,6 +100,11 @@ function migrateDatabase(db, from, to) {
100
100
  v = 7;
101
101
  continue;
102
102
  }
103
+ if (v === 7) {
104
+ migrateV7ToV8(db);
105
+ v = 8;
106
+ continue;
107
+ }
103
108
  throw new Error(`Unsupported schema migration path: v${from} \u2192 v${to} (stuck at v${v})`);
104
109
  }
105
110
  }
@@ -187,6 +192,8 @@ function inferSchemaVersion(db) {
187
192
  const hasFeedbackEvents = tableExists(db, "feedback_events");
188
193
  const hasEmotionTag = tableHasColumn(db, "memories", "emotion_tag");
189
194
  const hasProvenance = tableHasColumn(db, "memories", "source_session") && tableHasColumn(db, "memories", "source_context") && tableHasColumn(db, "memories", "observed_at");
195
+ const hasMemoryArchive = tableExists(db, "memory_archive");
196
+ if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag && hasProvenance && hasMemoryArchive) return 8;
190
197
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag && hasProvenance) return 7;
191
198
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag) return 6;
192
199
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents) return 5;
@@ -228,6 +235,11 @@ function ensureIndexes(db) {
228
235
  db.exec("CREATE INDEX IF NOT EXISTS idx_feedback_events_agent_source ON feedback_events(agent_id, source, created_at DESC);");
229
236
  }
230
237
  }
238
+ if (tableExists(db, "memory_archive")) {
239
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_agent ON memory_archive(agent_id);");
240
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_type ON memory_archive(type);");
241
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_archived_at ON memory_archive(archived_at);");
242
+ }
231
243
  }
232
244
  function ensureFeedbackEventSchema(db) {
233
245
  if (!tableExists(db, "feedback_events")) return;
@@ -403,11 +415,55 @@ function migrateV6ToV7(db) {
403
415
  throw e;
404
416
  }
405
417
  }
418
+ function migrateV7ToV8(db) {
419
+ if (tableExists(db, "memory_archive")) {
420
+ db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(8));
421
+ return;
422
+ }
423
+ try {
424
+ db.exec("BEGIN");
425
+ db.exec(`
426
+ CREATE TABLE IF NOT EXISTS memory_archive (
427
+ id TEXT PRIMARY KEY,
428
+ content TEXT NOT NULL,
429
+ type TEXT NOT NULL,
430
+ priority INTEGER NOT NULL,
431
+ emotion_val REAL NOT NULL DEFAULT 0.0,
432
+ vitality REAL NOT NULL DEFAULT 0.0,
433
+ stability REAL NOT NULL DEFAULT 1.0,
434
+ access_count INTEGER NOT NULL DEFAULT 0,
435
+ last_accessed TEXT,
436
+ created_at TEXT NOT NULL,
437
+ updated_at TEXT NOT NULL,
438
+ archived_at TEXT NOT NULL,
439
+ archive_reason TEXT NOT NULL DEFAULT 'eviction',
440
+ source TEXT,
441
+ agent_id TEXT NOT NULL DEFAULT 'default',
442
+ hash TEXT,
443
+ emotion_tag TEXT,
444
+ source_session TEXT,
445
+ source_context TEXT,
446
+ observed_at TEXT
447
+ );
448
+ `);
449
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_agent ON memory_archive(agent_id);");
450
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_type ON memory_archive(type);");
451
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_archive_archived_at ON memory_archive(archived_at);");
452
+ db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(8));
453
+ db.exec("COMMIT");
454
+ } catch (e) {
455
+ try {
456
+ db.exec("ROLLBACK");
457
+ } catch {
458
+ }
459
+ throw e;
460
+ }
461
+ }
406
462
  var SCHEMA_VERSION, SCHEMA_SQL;
407
463
  var init_db = __esm({
408
464
  "src/core/db.ts"() {
409
465
  "use strict";
410
- SCHEMA_VERSION = 7;
466
+ SCHEMA_VERSION = 8;
411
467
  SCHEMA_SQL = `
412
468
  -- Memory entries
413
469
  CREATE TABLE IF NOT EXISTS memories (
@@ -513,6 +569,30 @@ CREATE TABLE IF NOT EXISTS schema_meta (
513
569
  value TEXT NOT NULL
514
570
  );
515
571
 
572
+ -- Memory archive (eviction archive)
573
+ CREATE TABLE IF NOT EXISTS memory_archive (
574
+ id TEXT PRIMARY KEY,
575
+ content TEXT NOT NULL,
576
+ type TEXT NOT NULL,
577
+ priority INTEGER NOT NULL,
578
+ emotion_val REAL NOT NULL DEFAULT 0.0,
579
+ vitality REAL NOT NULL DEFAULT 0.0,
580
+ stability REAL NOT NULL DEFAULT 1.0,
581
+ access_count INTEGER NOT NULL DEFAULT 0,
582
+ last_accessed TEXT,
583
+ created_at TEXT NOT NULL,
584
+ updated_at TEXT NOT NULL,
585
+ archived_at TEXT NOT NULL,
586
+ archive_reason TEXT NOT NULL DEFAULT 'eviction',
587
+ source TEXT,
588
+ agent_id TEXT NOT NULL DEFAULT 'default',
589
+ hash TEXT,
590
+ emotion_tag TEXT,
591
+ source_session TEXT,
592
+ source_context TEXT,
593
+ observed_at TEXT
594
+ );
595
+
516
596
  -- Indexes for common queries
517
597
  CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
518
598
  CREATE INDEX IF NOT EXISTS idx_memories_priority ON memories(priority);
@@ -523,6 +603,9 @@ CREATE INDEX IF NOT EXISTS idx_paths_memory ON paths(memory_id);
523
603
  CREATE INDEX IF NOT EXISTS idx_paths_domain ON paths(domain);
524
604
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_phase_status ON maintenance_jobs(phase, status, started_at DESC);
525
605
  CREATE INDEX IF NOT EXISTS idx_feedback_events_memory ON feedback_events(memory_id, created_at DESC);
606
+ CREATE INDEX IF NOT EXISTS idx_memory_archive_agent ON memory_archive(agent_id);
607
+ CREATE INDEX IF NOT EXISTS idx_memory_archive_type ON memory_archive(type);
608
+ CREATE INDEX IF NOT EXISTS idx_memory_archive_archived_at ON memory_archive(archived_at);
526
609
  `;
527
610
  }
528
611
  });
@@ -1152,9 +1235,54 @@ function updateMemory(db, id, input) {
1152
1235
  }
1153
1236
  function deleteMemory(db, id) {
1154
1237
  db.prepare("DELETE FROM memories_fts WHERE id = ?").run(id);
1238
+ try {
1239
+ db.prepare("DELETE FROM embeddings WHERE memory_id = ?").run(id);
1240
+ } catch {
1241
+ }
1155
1242
  const result = db.prepare("DELETE FROM memories WHERE id = ?").run(id);
1156
1243
  return result.changes > 0;
1157
1244
  }
1245
+ function archiveMemory(db, id, reason, opts) {
1246
+ const mem = getMemory(db, id);
1247
+ if (!mem) return false;
1248
+ const minVitality = opts?.minVitality ?? 0.1;
1249
+ if (mem.vitality < minVitality) {
1250
+ deleteMemory(db, id);
1251
+ return "deleted";
1252
+ }
1253
+ const archivedAt = now();
1254
+ const archiveReason = reason ?? "eviction";
1255
+ db.prepare(
1256
+ `INSERT OR REPLACE INTO memory_archive
1257
+ (id, content, type, priority, emotion_val, vitality, stability, access_count,
1258
+ last_accessed, created_at, updated_at, archived_at, archive_reason,
1259
+ source, agent_id, hash, emotion_tag, source_session, source_context, observed_at)
1260
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
1261
+ ).run(
1262
+ mem.id,
1263
+ mem.content,
1264
+ mem.type,
1265
+ mem.priority,
1266
+ mem.emotion_val,
1267
+ mem.vitality,
1268
+ mem.stability,
1269
+ mem.access_count,
1270
+ mem.last_accessed,
1271
+ mem.created_at,
1272
+ mem.updated_at,
1273
+ archivedAt,
1274
+ archiveReason,
1275
+ mem.source,
1276
+ mem.agent_id,
1277
+ mem.hash,
1278
+ mem.emotion_tag,
1279
+ mem.source_session,
1280
+ mem.source_context,
1281
+ mem.observed_at
1282
+ );
1283
+ deleteMemory(db, id);
1284
+ return "archived";
1285
+ }
1158
1286
  function listMemories(db, opts) {
1159
1287
  const conditions = [];
1160
1288
  const params = [];
@@ -1649,21 +1777,20 @@ async function searchVectorBranch(db, query, opts) {
1649
1777
  before: opts.before
1650
1778
  });
1651
1779
  }
1652
- function expandRelated(db, results, agentId, maxTotal) {
1653
- const existingIds = new Set(results.map((r) => r.memory.id));
1780
+ function fetchRelatedLinks(db, sourceIds, agentId, excludeIds, maxPerSource = 5) {
1654
1781
  const related = [];
1655
- for (const result of results) {
1782
+ for (const sourceId of sourceIds) {
1656
1783
  const links = db.prepare(
1657
1784
  `SELECT l.target_id, l.weight, m.*
1658
1785
  FROM links l
1659
1786
  JOIN memories m ON m.id = l.target_id
1660
1787
  WHERE l.agent_id = ? AND l.source_id = ?
1661
1788
  ORDER BY l.weight DESC
1662
- LIMIT 5`
1663
- ).all(agentId, result.memory.id);
1789
+ LIMIT ?`
1790
+ ).all(agentId, sourceId, maxPerSource);
1664
1791
  for (const link of links) {
1665
- if (existingIds.has(link.target_id)) continue;
1666
- existingIds.add(link.target_id);
1792
+ if (excludeIds.has(link.target_id)) continue;
1793
+ excludeIds.add(link.target_id);
1667
1794
  const relatedMemory = {
1668
1795
  id: link.id,
1669
1796
  content: link.content,
@@ -1686,12 +1813,30 @@ function expandRelated(db, results, agentId, maxTotal) {
1686
1813
  };
1687
1814
  related.push({
1688
1815
  memory: relatedMemory,
1689
- score: result.score * link.weight * 0.6,
1690
- related_source_id: result.memory.id,
1691
- match_type: "related"
1816
+ sourceId,
1817
+ weight: link.weight
1692
1818
  });
1693
1819
  }
1694
1820
  }
1821
+ return related;
1822
+ }
1823
+ function expandRelated(db, results, agentId, maxTotal) {
1824
+ const existingIds = new Set(results.map((r) => r.memory.id));
1825
+ const links = fetchRelatedLinks(
1826
+ db,
1827
+ results.map((r) => r.memory.id),
1828
+ agentId,
1829
+ existingIds
1830
+ );
1831
+ const related = links.map((link) => {
1832
+ const sourceResult = results.find((r) => r.memory.id === link.sourceId);
1833
+ return {
1834
+ memory: link.memory,
1835
+ score: (sourceResult?.score ?? 0) * link.weight * 0.6,
1836
+ related_source_id: link.sourceId,
1837
+ match_type: "related"
1838
+ };
1839
+ });
1695
1840
  const directResults = results.map((r) => ({
1696
1841
  ...r,
1697
1842
  match_type: "direct"
@@ -2192,7 +2337,8 @@ async function surfaceMemories(db, input) {
2192
2337
  }),
2193
2338
  lexical_rank: signal.queryRank ?? signal.recentRank ?? signal.taskRank,
2194
2339
  semantic_rank: signal.semanticRank,
2195
- semantic_similarity: signal.semanticSimilarity
2340
+ semantic_similarity: signal.semanticSimilarity,
2341
+ match_type: "direct"
2196
2342
  };
2197
2343
  }).sort((left, right) => {
2198
2344
  if (right.score !== left.score) return right.score - left.score;
@@ -2201,6 +2347,42 @@ async function surfaceMemories(db, input) {
2201
2347
  if (left.memory.priority !== right.memory.priority) return left.memory.priority - right.memory.priority;
2202
2348
  return right.memory.updated_at.localeCompare(left.memory.updated_at);
2203
2349
  }).slice(0, limit);
2350
+ if (input.related) {
2351
+ const existingIds = new Set(results.map((r) => r.memory.id));
2352
+ const links = fetchRelatedLinks(
2353
+ db,
2354
+ results.map((r) => r.memory.id),
2355
+ agentId,
2356
+ existingIds
2357
+ );
2358
+ const relatedResults = links.map((link) => {
2359
+ const sourceResult = results.find((r) => r.memory.id === link.sourceId);
2360
+ const feedbackSummary = getFeedbackSummary(db, link.memory.id, agentId);
2361
+ return {
2362
+ memory: link.memory,
2363
+ score: (sourceResult?.score ?? 0) * link.weight * 0.6,
2364
+ semantic_score: 0,
2365
+ lexical_score: 0,
2366
+ task_match: 0,
2367
+ vitality: link.memory.vitality,
2368
+ priority_prior: priorityPrior(link.memory.priority),
2369
+ feedback_score: feedbackSummary.score,
2370
+ feedback_summary: feedbackSummary,
2371
+ reason_codes: [`type:${link.memory.type}`, "related"],
2372
+ related_source_id: link.sourceId,
2373
+ match_type: "related"
2374
+ };
2375
+ });
2376
+ const maxTotal = Math.floor(limit * 1.5);
2377
+ const combined = [...results, ...relatedResults].sort((a, b) => b.score - a.score).slice(0, maxTotal);
2378
+ return {
2379
+ count: combined.length,
2380
+ query: trimmedQuery,
2381
+ task: trimmedTask,
2382
+ intent: input.intent,
2383
+ results: combined
2384
+ };
2385
+ }
2204
2386
  return {
2205
2387
  count: results.length,
2206
2388
  query: trimmedQuery,
@@ -2949,13 +3131,31 @@ function rankEvictionCandidates(db, opts) {
2949
3131
  return left.memory.priority - right.memory.priority;
2950
3132
  });
2951
3133
  }
3134
+ function parseEnvInt(envKey) {
3135
+ const raw = process.env[envKey];
3136
+ if (raw === void 0 || raw === "") return null;
3137
+ const n = Number.parseInt(raw, 10);
3138
+ return Number.isFinite(n) && n > 0 ? n : null;
3139
+ }
3140
+ function getTieredCapacity(opts) {
3141
+ const envMax = parseEnvInt("AGENT_MEMORY_MAX_MEMORIES");
3142
+ return {
3143
+ identity: parseEnvInt("AGENT_MEMORY_MAX_IDENTITY"),
3144
+ // default: null (unlimited)
3145
+ emotion: parseEnvInt("AGENT_MEMORY_MAX_EMOTION") ?? 50,
3146
+ knowledge: parseEnvInt("AGENT_MEMORY_MAX_KNOWLEDGE") ?? 250,
3147
+ event: parseEnvInt("AGENT_MEMORY_MAX_EVENT") ?? 50,
3148
+ total: opts?.maxMemories ?? (envMax ?? 350)
3149
+ };
3150
+ }
2952
3151
  function runGovern(db, opts) {
2953
3152
  const agentId = opts?.agent_id;
2954
- const envMax = Number.parseInt(process.env.AGENT_MEMORY_MAX_MEMORIES ?? "", 10);
2955
- const maxMemories = opts?.maxMemories ?? (Number.isFinite(envMax) && envMax > 0 ? envMax : 200);
3153
+ const capacity = getTieredCapacity(opts);
2956
3154
  let orphanPaths = 0;
2957
3155
  let emptyMemories = 0;
2958
3156
  let evicted = 0;
3157
+ let archived = 0;
3158
+ const evictedByType = {};
2959
3159
  const transaction = db.transaction(() => {
2960
3160
  const pathResult = agentId ? db.prepare(
2961
3161
  `DELETE FROM paths
@@ -2965,17 +3165,48 @@ function runGovern(db, opts) {
2965
3165
  orphanPaths = pathResult.changes;
2966
3166
  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();
2967
3167
  emptyMemories = emptyResult.changes;
3168
+ const typeLimits = [
3169
+ { type: "identity", limit: capacity.identity },
3170
+ { type: "emotion", limit: capacity.emotion },
3171
+ { type: "knowledge", limit: capacity.knowledge },
3172
+ { type: "event", limit: capacity.event }
3173
+ ];
3174
+ const allCandidates = rankEvictionCandidates(db, { agent_id: agentId });
3175
+ const evictedIds = /* @__PURE__ */ new Set();
3176
+ for (const { type, limit } of typeLimits) {
3177
+ if (limit === null) continue;
3178
+ const typeCount = db.prepare(
3179
+ agentId ? "SELECT COUNT(*) as c FROM memories WHERE agent_id = ? AND type = ?" : "SELECT COUNT(*) as c FROM memories WHERE type = ?"
3180
+ ).get(...agentId ? [agentId, type] : [type]).c;
3181
+ const excess = Math.max(0, typeCount - limit);
3182
+ if (excess <= 0) continue;
3183
+ const typeCandidates = allCandidates.filter((c) => c.memory.type === type && !evictedIds.has(c.memory.id));
3184
+ const toEvict = typeCandidates.slice(0, excess);
3185
+ for (const candidate of toEvict) {
3186
+ const result = archiveMemory(db, candidate.memory.id, "eviction");
3187
+ evictedIds.add(candidate.memory.id);
3188
+ evicted += 1;
3189
+ if (result === "archived") archived += 1;
3190
+ evictedByType[type] = (evictedByType[type] ?? 0) + 1;
3191
+ }
3192
+ }
2968
3193
  const total = db.prepare(agentId ? "SELECT COUNT(*) as c FROM memories WHERE agent_id = ?" : "SELECT COUNT(*) as c FROM memories").get(...agentId ? [agentId] : []).c;
2969
- const excess = Math.max(0, total - maxMemories);
2970
- if (excess <= 0) return;
2971
- const candidates = rankEvictionCandidates(db, { agent_id: agentId }).slice(0, excess);
2972
- for (const candidate of candidates) {
2973
- deleteMemory(db, candidate.memory.id);
2974
- evicted += 1;
3194
+ const globalExcess = Math.max(0, total - capacity.total);
3195
+ if (globalExcess > 0) {
3196
+ const globalCandidates = allCandidates.filter((c) => !evictedIds.has(c.memory.id));
3197
+ const toEvict = globalCandidates.slice(0, globalExcess);
3198
+ for (const candidate of toEvict) {
3199
+ const result = archiveMemory(db, candidate.memory.id, "eviction");
3200
+ evictedIds.add(candidate.memory.id);
3201
+ evicted += 1;
3202
+ if (result === "archived") archived += 1;
3203
+ const t = candidate.memory.type;
3204
+ evictedByType[t] = (evictedByType[t] ?? 0) + 1;
3205
+ }
2975
3206
  }
2976
3207
  });
2977
3208
  transaction();
2978
- return { orphanPaths, emptyMemories, evicted };
3209
+ return { orphanPaths, emptyMemories, evicted, archived, evictedByType };
2979
3210
  }
2980
3211
 
2981
3212
  // src/sleep/jobs.ts
@@ -3218,12 +3449,21 @@ function getMemoryStatus(db, input) {
3218
3449
  const feedbackEvents = db.prepare(
3219
3450
  "SELECT COUNT(*) as c FROM feedback_events WHERE agent_id = ?"
3220
3451
  ).get(agentId);
3452
+ const tiered = getTieredCapacity();
3453
+ const capacity = {
3454
+ identity: { count: stats.by_type.identity ?? 0, limit: tiered.identity },
3455
+ emotion: { count: stats.by_type.emotion ?? 0, limit: tiered.emotion },
3456
+ knowledge: { count: stats.by_type.knowledge ?? 0, limit: tiered.knowledge },
3457
+ event: { count: stats.by_type.event ?? 0, limit: tiered.event },
3458
+ total: { count: stats.total, limit: tiered.total }
3459
+ };
3221
3460
  return {
3222
3461
  ...stats,
3223
3462
  paths: totalPaths.c,
3224
3463
  low_vitality: lowVitality.c,
3225
3464
  feedback_events: feedbackEvents.c,
3226
- agent_id: agentId
3465
+ agent_id: agentId,
3466
+ capacity
3227
3467
  };
3228
3468
  }
3229
3469
 
@@ -3357,6 +3597,8 @@ function formatRecallResponse(result) {
3357
3597
  vector_rank: row.vector_rank,
3358
3598
  bm25_score: row.bm25_score,
3359
3599
  vector_score: row.vector_score,
3600
+ related_source_id: row.related_source_id,
3601
+ match_type: row.match_type,
3360
3602
  updated_at: row.memory.updated_at
3361
3603
  }))
3362
3604
  };
@@ -3381,6 +3623,8 @@ function formatSurfaceResponse(result) {
3381
3623
  feedback_score: row.feedback_score,
3382
3624
  feedback_summary: row.feedback_summary,
3383
3625
  reason_codes: row.reason_codes,
3626
+ related_source_id: row.related_source_id,
3627
+ match_type: row.match_type,
3384
3628
  updated_at: row.memory.updated_at
3385
3629
  }))
3386
3630
  };
@@ -3550,7 +3794,11 @@ function createHttpServer(options) {
3550
3794
  emotion_val: asNumber(body.emotion_val),
3551
3795
  agent_id: asString(body.agent_id) ?? defaultAgentId,
3552
3796
  conservative: asBoolean(body.conservative),
3553
- provider: options?.provider
3797
+ provider: options?.provider,
3798
+ emotion_tag: asString(body.emotion_tag),
3799
+ source_session: asString(body.source_session),
3800
+ source_context: asString(body.source_context),
3801
+ observed_at: asString(body.observed_at)
3554
3802
  });
3555
3803
  sendJson(res, 200, result);
3556
3804
  return;
@@ -3565,7 +3813,12 @@ function createHttpServer(options) {
3565
3813
  query,
3566
3814
  limit: asNumber(body.limit),
3567
3815
  agent_id: asString(body.agent_id) ?? defaultAgentId,
3568
- provider: options?.provider
3816
+ provider: options?.provider,
3817
+ related: asBoolean(body.related),
3818
+ after: asString(body.after),
3819
+ before: asString(body.before),
3820
+ recency_boost: asNumber(body.recency_boost),
3821
+ emotion_tag: asString(body.emotion_tag)
3569
3822
  });
3570
3823
  sendJson(res, 200, formatRecallResponse(result));
3571
3824
  return;
@@ -3585,7 +3838,12 @@ function createHttpServer(options) {
3585
3838
  types,
3586
3839
  limit: asNumber(body.limit),
3587
3840
  agent_id: asString(body.agent_id) ?? defaultAgentId,
3588
- provider: options?.provider
3841
+ provider: options?.provider,
3842
+ related: asBoolean(body.related),
3843
+ after: asString(body.after),
3844
+ before: asString(body.before),
3845
+ recency_boost: asNumber(body.recency_boost),
3846
+ emotion_tag: asString(body.emotion_tag)
3589
3847
  });
3590
3848
  sendJson(res, 200, formatSurfaceResponse(result));
3591
3849
  return;
@@ -3719,7 +3977,7 @@ function getAgentId() {
3719
3977
  }
3720
3978
  function printHelp() {
3721
3979
  console.log(`
3722
- \u{1F9E0} AgentMemory v4 \u2014 Sleep-cycle memory for AI agents
3980
+ \u{1F9E0} AgentMemory \u2014 Sleep-cycle memory for AI agents
3723
3981
 
3724
3982
  Usage: agent-memory <command> [options]
3725
3983