@hasna/mementos 0.10.16 → 0.10.17

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.
Files changed (2) hide show
  1. package/dist/mcp/index.js +22 -5
  2. package/package.json +1 -1
package/dist/mcp/index.js CHANGED
@@ -10820,11 +10820,13 @@ Summary: ${newMems.length} new, ${updatedMems.length} updated, ${expiredMems.len
10820
10820
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
10821
10821
  }
10822
10822
  });
10823
- server.tool("memory_context", "Get memories relevant to current context, filtered by scope/importance/recency.", {
10823
+ server.tool("memory_context", "Get memories relevant to current context. Uses time-weighted scoring: score = importance \xD7 decay(age). Pinned memories are exempt. Returns effective_score on each memory.", {
10824
10824
  agent_id: exports_external.string().optional(),
10825
10825
  project_id: exports_external.string().optional(),
10826
10826
  scope: exports_external.enum(["global", "shared", "private"]).optional(),
10827
- limit: exports_external.coerce.number().optional()
10827
+ limit: exports_external.coerce.number().optional(),
10828
+ decay_halflife_days: exports_external.coerce.number().optional().describe("Importance half-life in days (default: 90). Lower = more weight on recent memories."),
10829
+ no_decay: exports_external.coerce.boolean().optional().describe("Set true to disable decay and sort purely by importance.")
10828
10830
  }, async (args) => {
10829
10831
  try {
10830
10832
  const filter = {
@@ -10832,16 +10834,31 @@ server.tool("memory_context", "Get memories relevant to current context, filtere
10832
10834
  agent_id: args.agent_id,
10833
10835
  project_id: args.project_id,
10834
10836
  status: "active",
10835
- limit: args.limit || 30
10837
+ limit: (args.limit || 30) * 2
10836
10838
  };
10837
10839
  const memories = listMemories(filter);
10838
10840
  if (memories.length === 0) {
10839
10841
  return { content: [{ type: "text", text: "No memories in current context." }] };
10840
10842
  }
10841
- for (const m of memories) {
10843
+ const halflifeDays = args.decay_halflife_days ?? 90;
10844
+ const now2 = Date.now();
10845
+ const scored = memories.map((m) => {
10846
+ let effectiveScore = m.importance;
10847
+ if (!args.no_decay && !m.pinned) {
10848
+ const ageMs = now2 - new Date(m.updated_at).getTime();
10849
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
10850
+ const decayFactor = Math.pow(0.5, ageDays / halflifeDays);
10851
+ effectiveScore = m.importance * decayFactor;
10852
+ }
10853
+ return { ...m, effective_score: Math.round(effectiveScore * 100) / 100 };
10854
+ });
10855
+ const limit = args.limit || 30;
10856
+ scored.sort((a, b) => b.effective_score - a.effective_score);
10857
+ const top = scored.slice(0, limit);
10858
+ for (const m of top) {
10842
10859
  touchMemory(m.id);
10843
10860
  }
10844
- const lines = memories.map((m) => `[${m.scope}/${m.category}] ${m.key}: ${m.value} (importance: ${m.importance})`);
10861
+ const lines = top.map((m) => `[${m.scope}/${m.category}] ${m.key}: ${m.value} (score: ${m.effective_score}, raw: ${m.importance}${m.pinned ? ", pinned" : ""})`);
10845
10862
  return { content: [{ type: "text", text: lines.join(`
10846
10863
  `) }] };
10847
10864
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.10.16",
3
+ "version": "0.10.17",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",