@smyslenny/agent-memory 4.0.0-alpha.1 → 4.1.0-alpha.1

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.
@@ -89,6 +89,11 @@ function migrateDatabase(db, from, to) {
89
89
  v = 5;
90
90
  continue;
91
91
  }
92
+ if (v === 5) {
93
+ migrateV5ToV6(db);
94
+ v = 6;
95
+ continue;
96
+ }
92
97
  throw new Error(`Unsupported schema migration path: v${from} \u2192 v${to} (stuck at v${v})`);
93
98
  }
94
99
  }
@@ -174,6 +179,8 @@ function inferSchemaVersion(db) {
174
179
  const hasV4Embeddings = hasEmbeddings && tableHasColumn(db, "embeddings", "provider_id") && tableHasColumn(db, "embeddings", "status") && tableHasColumn(db, "embeddings", "content_hash") && tableHasColumn(db, "embeddings", "id");
175
180
  const hasMaintenanceJobs = tableExists(db, "maintenance_jobs");
176
181
  const hasFeedbackEvents = tableExists(db, "feedback_events");
182
+ const hasEmotionTag = tableHasColumn(db, "memories", "emotion_tag");
183
+ if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents && hasEmotionTag) return 6;
177
184
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings && hasMaintenanceJobs && hasFeedbackEvents) return 5;
178
185
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasV4Embeddings) return 4;
179
186
  if (hasAgentScopedPaths && hasAgentScopedLinks && hasEmbeddings) return 3;
@@ -200,6 +207,9 @@ function ensureIndexes(db) {
200
207
  if (tableExists(db, "maintenance_jobs")) {
201
208
  db.exec("CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_phase_status ON maintenance_jobs(phase, status, started_at DESC);");
202
209
  }
210
+ if (tableHasColumn(db, "memories", "emotion_tag")) {
211
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_emotion_tag ON memories(emotion_tag) WHERE emotion_tag IS NOT NULL;");
212
+ }
203
213
  if (tableExists(db, "feedback_events")) {
204
214
  db.exec("CREATE INDEX IF NOT EXISTS idx_feedback_events_memory ON feedback_events(memory_id, created_at DESC);");
205
215
  if (tableHasColumn(db, "feedback_events", "agent_id") && tableHasColumn(db, "feedback_events", "source")) {
@@ -333,11 +343,30 @@ function migrateV4ToV5(db) {
333
343
  throw e;
334
344
  }
335
345
  }
346
+ function migrateV5ToV6(db) {
347
+ if (tableHasColumn(db, "memories", "emotion_tag")) {
348
+ db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(6));
349
+ return;
350
+ }
351
+ try {
352
+ db.exec("BEGIN");
353
+ db.exec("ALTER TABLE memories ADD COLUMN emotion_tag TEXT;");
354
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_emotion_tag ON memories(emotion_tag) WHERE emotion_tag IS NOT NULL;");
355
+ db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('version', ?)").run(String(6));
356
+ db.exec("COMMIT");
357
+ } catch (e) {
358
+ try {
359
+ db.exec("ROLLBACK");
360
+ } catch {
361
+ }
362
+ throw e;
363
+ }
364
+ }
336
365
  var SCHEMA_VERSION, SCHEMA_SQL;
337
366
  var init_db = __esm({
338
367
  "src/core/db.ts"() {
339
368
  "use strict";
340
- SCHEMA_VERSION = 5;
369
+ SCHEMA_VERSION = 6;
341
370
  SCHEMA_SQL = `
342
371
  -- Memory entries
343
372
  CREATE TABLE IF NOT EXISTS memories (
@@ -355,6 +384,7 @@ CREATE TABLE IF NOT EXISTS memories (
355
384
  source TEXT,
356
385
  agent_id TEXT NOT NULL DEFAULT 'default',
357
386
  hash TEXT,
387
+ emotion_tag TEXT,
358
388
  UNIQUE(hash, agent_id)
359
389
  );
360
390
 
@@ -934,8 +964,8 @@ function createMemory(db, input) {
934
964
  const timestamp = now();
935
965
  db.prepare(
936
966
  `INSERT INTO memories (id, content, type, priority, emotion_val, vitality, stability,
937
- access_count, created_at, updated_at, source, agent_id, hash)
938
- VALUES (?, ?, ?, ?, ?, 1.0, ?, 0, ?, ?, ?, ?, ?)`
967
+ access_count, created_at, updated_at, source, agent_id, hash, emotion_tag)
968
+ VALUES (?, ?, ?, ?, ?, 1.0, ?, 0, ?, ?, ?, ?, ?, ?)`
939
969
  ).run(
940
970
  id,
941
971
  input.content,
@@ -947,7 +977,8 @@ function createMemory(db, input) {
947
977
  timestamp,
948
978
  input.source ?? null,
949
979
  agentId,
950
- hash2
980
+ hash2,
981
+ input.emotion_tag ?? null
951
982
  );
952
983
  db.prepare("INSERT INTO memories_fts (id, content) VALUES (?, ?)").run(id, tokenizeForIndex(input.content));
953
984
  markEmbeddingDirtyIfNeeded(db, id, hash2, resolveEmbeddingProviderId(input.embedding_provider_id));
@@ -991,6 +1022,10 @@ function updateMemory(db, id, input) {
991
1022
  fields.push("source = ?");
992
1023
  values.push(input.source);
993
1024
  }
1025
+ if (input.emotion_tag !== void 0) {
1026
+ fields.push("emotion_tag = ?");
1027
+ values.push(input.emotion_tag);
1028
+ }
994
1029
  fields.push("updated_at = ?");
995
1030
  values.push(now());
996
1031
  values.push(id);
@@ -1032,6 +1067,10 @@ function listMemories(db, opts) {
1032
1067
  conditions.push("vitality >= ?");
1033
1068
  params.push(opts.min_vitality);
1034
1069
  }
1070
+ if (opts?.emotion_tag) {
1071
+ conditions.push("emotion_tag = ?");
1072
+ params.push(opts.emotion_tag);
1073
+ }
1035
1074
  const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
1036
1075
  const limit = opts?.limit ?? 100;
1037
1076
  const offset = opts?.offset ?? 0;
@@ -14901,8 +14940,68 @@ function getPathsByPrefix(db, prefix, agent_id = "default") {
14901
14940
 
14902
14941
  // src/sleep/boot.ts
14903
14942
  init_memory();
14943
+ function formatRelativeDate(isoDate) {
14944
+ const diffMs = Date.now() - new Date(isoDate).getTime();
14945
+ const diffDays = Math.floor(diffMs / 864e5);
14946
+ if (diffDays <= 0) return "\u4ECA\u5929";
14947
+ if (diffDays === 1) return "\u6628\u5929";
14948
+ if (diffDays <= 7) return `${diffDays}\u5929\u524D`;
14949
+ return isoDate.slice(0, 10);
14950
+ }
14951
+ function loadWarmBootLayers(db, agentId) {
14952
+ const identity = listMemories(db, { agent_id: agentId, type: "identity", limit: 50 });
14953
+ const emotion = listMemories(db, { agent_id: agentId, type: "emotion", limit: 5 });
14954
+ const event = listMemories(db, { agent_id: agentId, type: "event", limit: 7 });
14955
+ const knowledge = listMemories(db, {
14956
+ agent_id: agentId,
14957
+ type: "knowledge",
14958
+ min_vitality: 0.5,
14959
+ limit: 10
14960
+ });
14961
+ return { identity, emotion, event, knowledge };
14962
+ }
14963
+ function formatNarrativeBoot(layers, agentName) {
14964
+ const lines = [];
14965
+ lines.push(`# ${agentName}\u7684\u56DE\u5FC6`);
14966
+ lines.push("");
14967
+ if (layers.identity.length > 0) {
14968
+ lines.push("## \u6211\u662F\u8C01");
14969
+ for (const mem of layers.identity) {
14970
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)}`);
14971
+ }
14972
+ lines.push("");
14973
+ }
14974
+ if (layers.emotion.length > 0) {
14975
+ lines.push("## \u6700\u8FD1\u7684\u5FC3\u60C5");
14976
+ for (const mem of layers.emotion) {
14977
+ const tag = mem.emotion_tag;
14978
+ const time3 = formatRelativeDate(mem.updated_at);
14979
+ const tagStr = tag ? `${tag}, ${time3}` : time3;
14980
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)} (${tagStr})`);
14981
+ }
14982
+ lines.push("");
14983
+ }
14984
+ if (layers.event.length > 0) {
14985
+ lines.push("## \u6700\u8FD1\u53D1\u751F\u7684\u4E8B");
14986
+ for (const mem of layers.event) {
14987
+ const time3 = formatRelativeDate(mem.updated_at);
14988
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)} (${time3})`);
14989
+ }
14990
+ lines.push("");
14991
+ }
14992
+ if (layers.knowledge.length > 0) {
14993
+ lines.push("## \u8FD8\u8BB0\u5F97\u7684\u77E5\u8BC6");
14994
+ for (const mem of layers.knowledge) {
14995
+ lines.push(`- ${mem.content.split("\n")[0].slice(0, 200)}`);
14996
+ }
14997
+ lines.push("");
14998
+ }
14999
+ return lines.join("\n");
15000
+ }
14904
15001
  function boot(db, opts) {
14905
15002
  const agentId = opts?.agent_id ?? "default";
15003
+ const format = opts?.format ?? "json";
15004
+ const agentName = opts?.agent_name ?? "Agent";
14906
15005
  const corePaths = opts?.corePaths ?? [
14907
15006
  "core://agent",
14908
15007
  "core://user",
@@ -14946,10 +15045,16 @@ function boot(db, opts) {
14946
15045
  }
14947
15046
  }
14948
15047
  }
14949
- return {
15048
+ const result = {
14950
15049
  identityMemories: [...memories.values()],
14951
15050
  bootPaths
14952
15051
  };
15052
+ if (format === "narrative") {
15053
+ const layers = loadWarmBootLayers(db, agentId);
15054
+ result.layers = layers;
15055
+ result.narrative = formatNarrativeBoot(layers, agentName);
15056
+ }
15057
+ return result;
14953
15058
  }
14954
15059
 
14955
15060
  // src/sleep/sync.ts
@@ -15550,7 +15655,8 @@ async function syncOne(db, input) {
15550
15655
  agent_id: input.agent_id,
15551
15656
  uri: input.uri,
15552
15657
  provider: input.provider,
15553
- conservative: input.conservative
15658
+ conservative: input.conservative,
15659
+ emotion_tag: input.emotion_tag
15554
15660
  };
15555
15661
  const guardResult = await guard(db, memInput);
15556
15662
  switch (guardResult.action) {
@@ -15830,21 +15936,26 @@ async function rememberMemory(db, input) {
15830
15936
  source: input.source,
15831
15937
  agent_id: input.agent_id,
15832
15938
  provider: input.provider,
15833
- conservative: input.conservative
15939
+ conservative: input.conservative,
15940
+ emotion_tag: input.emotion_tag
15834
15941
  });
15835
15942
  }
15836
15943
 
15837
15944
  // src/app/recall.ts
15838
15945
  async function recallMemory(db, input) {
15839
- return recallMemories(db, input.query, {
15946
+ const result = await recallMemories(db, input.query, {
15840
15947
  agent_id: input.agent_id,
15841
- limit: input.limit,
15948
+ limit: input.emotion_tag ? (input.limit ?? 10) * 3 : input.limit,
15842
15949
  min_vitality: input.min_vitality,
15843
15950
  lexicalLimit: input.lexicalLimit,
15844
15951
  vectorLimit: input.vectorLimit,
15845
15952
  provider: input.provider,
15846
15953
  recordAccess: input.recordAccess
15847
15954
  });
15955
+ if (input.emotion_tag) {
15956
+ result.results = result.results.filter((r) => r.memory.emotion_tag === input.emotion_tag).slice(0, input.limit ?? 10);
15957
+ }
15958
+ return result;
15848
15959
  }
15849
15960
 
15850
15961
  // src/app/surface.ts
@@ -16093,7 +16204,7 @@ async function surfaceMemories(db, input) {
16093
16204
  signals.set(memory.id, { memory });
16094
16205
  }
16095
16206
  }
16096
- const results = [...signals.values()].map((signal) => signal.memory).filter((memory) => memory.vitality >= minVitality).filter((memory) => input.types?.length ? input.types.includes(memory.type) : true).map((memory) => {
16207
+ const results = [...signals.values()].map((signal) => signal.memory).filter((memory) => memory.vitality >= minVitality).filter((memory) => input.types?.length ? input.types.includes(memory.type) : true).filter((memory) => input.emotion_tag ? memory.emotion_tag === input.emotion_tag : true).map((memory) => {
16097
16208
  const signal = signals.get(memory.id) ?? { memory };
16098
16209
  const memoryTokens = new Set(tokenize(memory.content));
16099
16210
  const lexicalOverlap = overlapScore2(memoryTokens, queryTokens);
@@ -16763,10 +16874,11 @@ function createMcpServer(dbPath, agentId) {
16763
16874
  uri: external_exports.string().optional().describe("URI path (e.g. core://user/name, emotion://2026-02-20/love)"),
16764
16875
  emotion_val: external_exports.number().min(-1).max(1).default(0).describe("Emotional valence (-1 negative to +1 positive)"),
16765
16876
  source: external_exports.string().optional().describe("Source annotation (e.g. session ID, date)"),
16766
- agent_id: external_exports.string().optional().describe("Override agent scope (defaults to current agent)")
16877
+ agent_id: external_exports.string().optional().describe("Override agent scope (defaults to current agent)"),
16878
+ emotion_tag: external_exports.string().optional().describe("Emotion label for emotion-type memories (e.g. \u5B89\u5FC3, \u5F00\u5FC3, \u62C5\u5FC3)")
16767
16879
  },
16768
- async ({ content, type, uri, emotion_val, source, agent_id }) => {
16769
- const result = await rememberMemory(db, { content, type, uri, emotion_val, source, agent_id: agent_id ?? aid });
16880
+ async ({ content, type, uri, emotion_val, source, agent_id, emotion_tag }) => {
16881
+ const result = await rememberMemory(db, { content, type, uri, emotion_val, source, agent_id: agent_id ?? aid, emotion_tag });
16770
16882
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
16771
16883
  }
16772
16884
  );
@@ -16776,10 +16888,11 @@ function createMcpServer(dbPath, agentId) {
16776
16888
  {
16777
16889
  query: external_exports.string().describe("Search query (natural language)"),
16778
16890
  limit: external_exports.number().default(10).describe("Max results to return"),
16779
- agent_id: external_exports.string().optional().describe("Override agent scope (defaults to current agent)")
16891
+ agent_id: external_exports.string().optional().describe("Override agent scope (defaults to current agent)"),
16892
+ emotion_tag: external_exports.string().optional().describe("Filter results by emotion tag (e.g. \u5B89\u5FC3, \u5F00\u5FC3)")
16780
16893
  },
16781
- async ({ query, limit, agent_id }) => {
16782
- const result = await recallMemory(db, { query, limit, agent_id: agent_id ?? aid });
16894
+ async ({ query, limit, agent_id, emotion_tag }) => {
16895
+ const result = await recallMemory(db, { query, limit, agent_id: agent_id ?? aid, emotion_tag });
16783
16896
  return { content: [{ type: "text", text: JSON.stringify(formatRecallPayload(result), null, 2) }] };
16784
16897
  }
16785
16898
  );
@@ -16827,19 +16940,20 @@ function createMcpServer(dbPath, agentId) {
16827
16940
  "boot",
16828
16941
  "Load startup memories. Default output is narrative markdown; pass format=json for legacy output.",
16829
16942
  {
16830
- format: external_exports.enum(["narrative", "json"]).default("narrative").optional()
16943
+ format: external_exports.enum(["narrative", "json"]).default("narrative").optional(),
16944
+ agent_name: external_exports.string().optional().describe("Agent name for narrative header (default: Agent)")
16831
16945
  },
16832
- async ({ format }) => {
16946
+ async ({ format, agent_name }) => {
16833
16947
  const outputFormat = format ?? "narrative";
16834
- const base = boot(db, { agent_id: aid });
16948
+ const result = boot(db, { agent_id: aid, format: outputFormat, agent_name: agent_name ?? void 0 });
16835
16949
  if (outputFormat === "json") {
16836
16950
  return {
16837
16951
  content: [{
16838
16952
  type: "text",
16839
16953
  text: JSON.stringify({
16840
- count: base.identityMemories.length,
16841
- bootPaths: base.bootPaths,
16842
- memories: base.identityMemories.map((memory) => ({
16954
+ count: result.identityMemories.length,
16955
+ bootPaths: result.bootPaths,
16956
+ memories: result.identityMemories.map((memory) => ({
16843
16957
  id: memory.id,
16844
16958
  content: memory.content,
16845
16959
  type: memory.type,
@@ -16849,12 +16963,15 @@ function createMcpServer(dbPath, agentId) {
16849
16963
  }]
16850
16964
  };
16851
16965
  }
16966
+ if (result.narrative) {
16967
+ return { content: [{ type: "text", text: result.narrative }] };
16968
+ }
16852
16969
  const identity = listMemories(db, { agent_id: aid, type: "identity", limit: 12 });
16853
16970
  const emotion = listMemories(db, { agent_id: aid, type: "emotion", min_vitality: 0.1, limit: 12 }).sort((a, b) => b.vitality - a.vitality);
16854
16971
  const knowledge = listMemories(db, { agent_id: aid, type: "knowledge", min_vitality: 0.1, limit: 16 }).sort((a, b) => b.vitality - a.vitality);
16855
16972
  const event = listMemories(db, { agent_id: aid, type: "event", min_vitality: 0, limit: 24 }).sort((a, b) => b.vitality - a.vitality);
16856
16973
  const stats = countMemories(db, aid);
16857
- return { content: [{ type: "text", text: formatWarmBootNarrative(identity.length > 0 ? identity : base.identityMemories, emotion, knowledge, event, stats) }] };
16974
+ return { content: [{ type: "text", text: formatWarmBootNarrative(identity.length > 0 ? identity : result.identityMemories, emotion, knowledge, event, stats) }] };
16858
16975
  }
16859
16976
  );
16860
16977
  server.tool(