@jcyamacho/agent-memory 0.0.4 → 0.0.6

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 (3) hide show
  1. package/README.md +12 -16
  2. package/dist/index.js +51 -34
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -66,19 +66,17 @@ Recommended LLM instructions to pair with this MCP:
66
66
  Use `memory_recall` at task start and whenever prior preferences, project facts,
67
67
  or decisions may matter.
68
68
 
69
- Use `memory_remember` only for durable, reusable context: preferences,
70
- conventions, decisions, constraints, and stable workflow habits. Store one
71
- concise, self-contained fact per memory. Include `workspace` when available. Do
72
- not store secrets or temporary noise.
73
-
74
- For `memory_recall`, pass `terms` as 2-5 distinctive strings that describe what
75
- you are looking for. Prefer names, identifiers, package names, file names, and
76
- short phrases. Each term is matched independently — more terms cast a wider net,
77
- and results matching multiple terms rank higher. Stemming is applied
78
- automatically, so exact word forms are not required.
79
-
80
- Use `workspace` to bias ranking toward the current project. Use `created_*`
81
- only for exact scoping.
69
+ Use `memory_remember` only for durable context worth reusing later:
70
+ preferences, conventions, decisions, constraints, and stable workflow habits.
71
+ Store one concise fact per memory, include `workspace` when relevant, and avoid
72
+ secrets or temporary notes.
73
+
74
+ For `memory_recall`, pass 2-5 short, distinctive `terms` as separate array
75
+ items. Prefer project names, file names, APIs, feature names, issue IDs, and
76
+ brief phrases. Avoid one long sentence.
77
+
78
+ Use `workspace` to bias results toward the current project. Use `created_*`
79
+ only when time range matters.
82
80
  ```
83
81
 
84
82
  ## What It Stores
@@ -104,8 +102,6 @@ Inputs:
104
102
  Output:
105
103
 
106
104
  - `id`
107
- - `workspace`
108
- - `created_at`
109
105
 
110
106
  ### `recall`
111
107
 
@@ -122,7 +118,7 @@ Inputs:
122
118
 
123
119
  Output:
124
120
 
125
- - `results[]` with `id`, `content`, `score`, `workspace`, and `created_at`
121
+ - `results[]` with `id`, `content`, `score`, `workspace`, and `updated_at`
126
122
 
127
123
  ## Setup
128
124
 
package/dist/index.js CHANGED
@@ -12464,7 +12464,7 @@ class StdioServerTransport {
12464
12464
  }
12465
12465
  }
12466
12466
  // package.json
12467
- var version2 = "0.0.4";
12467
+ var version2 = "0.0.6";
12468
12468
 
12469
12469
  // src/config.ts
12470
12470
  import { homedir } from "node:os";
@@ -19908,9 +19908,17 @@ class PersistenceError extends MemoryError {
19908
19908
  }
19909
19909
  }
19910
19910
 
19911
+ // src/memory.ts
19912
+ var toNormalizedScore = (value) => value;
19913
+
19911
19914
  // src/memory-service.ts
19912
19915
  var DEFAULT_LIMIT = 15;
19913
19916
  var MAX_LIMIT = 50;
19917
+ var RECALL_CANDIDATE_LIMIT_MULTIPLIER = 2;
19918
+ var RETRIEVAL_SCORE_WEIGHT = 8;
19919
+ var WORKSPACE_MATCH_WEIGHT = 2;
19920
+ var RECENCY_WEIGHT = 1;
19921
+ var MAX_COMPOSITE_SCORE = RETRIEVAL_SCORE_WEIGHT + WORKSPACE_MATCH_WEIGHT + RECENCY_WEIGHT;
19914
19922
 
19915
19923
  class MemoryService {
19916
19924
  repository;
@@ -19937,14 +19945,17 @@ class MemoryService {
19937
19945
  if (terms.length === 0) {
19938
19946
  throw new ValidationError("At least one search term is required.");
19939
19947
  }
19948
+ const requestedLimit = normalizeLimit(input.limit);
19949
+ const workspace = normalizeOptionalString(input.workspace);
19940
19950
  const normalizedQuery = {
19941
19951
  terms,
19942
- limit: normalizeLimit(input.limit),
19943
- workspace: normalizeOptionalString(input.workspace),
19952
+ limit: requestedLimit * RECALL_CANDIDATE_LIMIT_MULTIPLIER,
19944
19953
  createdAfter: input.createdAfter,
19945
19954
  createdBefore: input.createdBefore
19946
19955
  };
19947
- return this.repository.search(normalizedQuery);
19956
+ const results = await this.repository.search(normalizedQuery);
19957
+ const reranked = rerankSearchResults(results, workspace);
19958
+ return reranked.sort((a, b) => b.score - a.score).slice(0, requestedLimit);
19948
19959
  }
19949
19960
  }
19950
19961
  var normalizeLimit = (value) => {
@@ -19964,6 +19975,23 @@ var normalizeTerms = (terms) => {
19964
19975
  const normalizedTerms = terms.map((term) => term.trim()).filter(Boolean);
19965
19976
  return [...new Set(normalizedTerms)];
19966
19977
  };
19978
+ var rerankSearchResults = (results, workspace) => {
19979
+ if (results.length <= 1) {
19980
+ return results;
19981
+ }
19982
+ const updatedAtTimes = results.map((result) => result.updatedAt.getTime());
19983
+ const minUpdatedAt = Math.min(...updatedAtTimes);
19984
+ const maxUpdatedAt = Math.max(...updatedAtTimes);
19985
+ return [...results].map((result) => {
19986
+ const workspaceScore = workspace && result.workspace === workspace ? 1 : 0;
19987
+ const recencyScore = maxUpdatedAt === minUpdatedAt ? 0 : (result.updatedAt.getTime() - minUpdatedAt) / (maxUpdatedAt - minUpdatedAt);
19988
+ const combinedScore = (result.score * RETRIEVAL_SCORE_WEIGHT + workspaceScore * WORKSPACE_MATCH_WEIGHT + recencyScore * RECENCY_WEIGHT) / MAX_COMPOSITE_SCORE;
19989
+ return {
19990
+ ...result,
19991
+ score: toNormalizedScore(combinedScore)
19992
+ };
19993
+ });
19994
+ };
19967
19995
 
19968
19996
  // src/tools/shared.ts
19969
19997
  var toMcpError = (error2) => {
@@ -19992,24 +20020,24 @@ var parseOptionalDate = (value, fieldName) => {
19992
20020
 
19993
20021
  // src/tools/recall.ts
19994
20022
  var recallInputSchema = {
19995
- terms: array(string2()).min(1).describe("Search terms to match against remembered content. Use distinctive keywords, IDs, names, file names, or short phrases as separate items."),
19996
- limit: number2().int().min(1).max(MAX_LIMIT).optional().describe("Maximum number of memory results to return. Use a small number when you only need the best matches."),
19997
- workspace: string2().optional().describe("Workspace or repository path. Memories from this workspace rank higher, but other workspaces are not excluded."),
19998
- created_after: string2().optional().describe("Only return memories created at or after this ISO 8601 timestamp."),
19999
- created_before: string2().optional().describe("Only return memories created at or before this ISO 8601 timestamp.")
20023
+ terms: array(string2()).min(1).describe("Search terms used to find relevant memories. Pass 2-5 short, distinctive items as separate array entries, such as project names, file names, APIs, feature names, issue IDs, or brief phrases. Avoid one long sentence."),
20024
+ limit: number2().int().min(1).max(MAX_LIMIT).optional().describe("Maximum number of matches to return. Keep this small when you only need the strongest hits."),
20025
+ workspace: string2().optional().describe("Preferred repository or workspace path for ranking. Use the current project path to bias results toward local context, while still allowing cross-workspace matches."),
20026
+ created_after: string2().optional().describe("Only return memories created at or after this ISO 8601 timestamp. Use it when you need to narrow recall to newer context."),
20027
+ created_before: string2().optional().describe("Only return memories created at or before this ISO 8601 timestamp. Use it when you need to narrow recall to older context.")
20000
20028
  };
20001
20029
  var recallOutputSchema = {
20002
20030
  results: array(object({
20003
20031
  id: string2().describe("Stable identifier for the remembered item."),
20004
- content: string2().describe("The remembered content that matched the search terms."),
20005
- score: number2().describe("Relevance score for this result. Higher means a better match."),
20032
+ content: string2().describe("Saved memory text that matched one or more search terms."),
20033
+ score: number2().describe("Relative relevance score for ranking results. Higher means a stronger match."),
20006
20034
  workspace: string2().optional().describe("Workspace associated with the memory, if available."),
20007
- created_at: string2().describe("ISO 8601 timestamp showing when the memory was created.")
20035
+ updated_at: string2().describe("ISO 8601 timestamp showing when the memory was last updated.")
20008
20036
  }))
20009
20037
  };
20010
20038
  var registerRecallTool = (server, memoryService) => {
20011
20039
  server.registerTool("recall", {
20012
- description: "Retrieve previously remembered context that may help with the current task. Use it for user preferences, project facts, prior decisions, constraints, or earlier conversation details.",
20040
+ description: "Recall previously saved context for the current task. Best results usually come from 2-5 short, distinctive terms such as project names, file names, APIs, decisions, or issue IDs.",
20013
20041
  inputSchema: recallInputSchema,
20014
20042
  outputSchema: recallOutputSchema
20015
20043
  }, async ({ terms, limit, workspace, created_after, created_before }) => {
@@ -20027,7 +20055,7 @@ var registerRecallTool = (server, memoryService) => {
20027
20055
  content: result.content,
20028
20056
  score: result.score,
20029
20057
  workspace: result.workspace,
20030
- created_at: result.createdAt.toISOString()
20058
+ updated_at: result.updatedAt.toISOString()
20031
20059
  }))
20032
20060
  };
20033
20061
  const matchCount = structuredContent.results.length;
@@ -20053,9 +20081,7 @@ var rememberInputSchema = {
20053
20081
  workspace: string2().optional().describe("Repository or workspace path this memory belongs to. Use it to keep memories scoped to a project.")
20054
20082
  };
20055
20083
  var rememberOutputSchema = {
20056
- id: string2().describe("Stable identifier for the saved memory."),
20057
- workspace: string2().optional().describe("Workspace stored with the memory, if provided."),
20058
- created_at: string2().describe("ISO 8601 timestamp showing when the memory was created.")
20084
+ id: string2().describe("Stable identifier for the saved memory.")
20059
20085
  };
20060
20086
  var registerRememberTool = (server, memoryService) => {
20061
20087
  server.registerTool("remember", {
@@ -20069,9 +20095,7 @@ var registerRememberTool = (server, memoryService) => {
20069
20095
  workspace
20070
20096
  });
20071
20097
  const structuredContent = {
20072
- id: memory.id,
20073
- workspace: memory.workspace,
20074
- created_at: memory.createdAt.toISOString()
20098
+ id: memory.id
20075
20099
  };
20076
20100
  return {
20077
20101
  content: [
@@ -20165,8 +20189,6 @@ var initializeMemoryDatabase = (database) => {
20165
20189
  };
20166
20190
 
20167
20191
  // src/sqlite-repository.ts
20168
- var WORKSPACE_BIAS = 0.1;
20169
-
20170
20192
  class SqliteMemoryRepository {
20171
20193
  database;
20172
20194
  insertStatement;
@@ -20198,15 +20220,7 @@ class SqliteMemoryRepository {
20198
20220
  }
20199
20221
  async search(query) {
20200
20222
  try {
20201
- const selectParams = [];
20202
20223
  const whereParams = [toFtsQuery(query.terms)];
20203
- let scoreExpr;
20204
- if (query.workspace) {
20205
- scoreExpr = "MAX(0, -bm25(memories_fts) + CASE WHEN m.workspace = ? THEN ? ELSE 0.0 END)";
20206
- selectParams.push(query.workspace, WORKSPACE_BIAS);
20207
- } else {
20208
- scoreExpr = "MAX(0, -bm25(memories_fts))";
20209
- }
20210
20224
  const whereClauses = ["memories_fts MATCH ?"];
20211
20225
  if (query.createdAfter) {
20212
20226
  whereClauses.push("m.created_at >= ?");
@@ -20216,14 +20230,15 @@ class SqliteMemoryRepository {
20216
20230
  whereClauses.push("m.created_at <= ?");
20217
20231
  whereParams.push(query.createdBefore.getTime());
20218
20232
  }
20219
- const params = [...selectParams, ...whereParams, query.limit];
20233
+ const params = [...whereParams, query.limit];
20220
20234
  const statement = this.database.prepare(`
20221
20235
  SELECT
20222
20236
  m.id,
20223
20237
  m.content,
20224
20238
  m.workspace,
20225
20239
  m.created_at,
20226
- ${scoreExpr} AS score
20240
+ m.updated_at,
20241
+ MAX(0, -bm25(memories_fts)) AS score
20227
20242
  FROM memories_fts
20228
20243
  INNER JOIN memories AS m ON m.rowid = memories_fts.rowid
20229
20244
  WHERE ${whereClauses.join(" AND ")}
@@ -20231,12 +20246,14 @@ class SqliteMemoryRepository {
20231
20246
  LIMIT ?
20232
20247
  `);
20233
20248
  const rows = statement.all(...params);
20249
+ const maxScore = Math.max(...rows.map((row) => row.score), 0);
20234
20250
  return rows.map((row) => ({
20235
20251
  id: row.id,
20236
20252
  content: row.content,
20237
- score: row.score,
20253
+ score: toNormalizedScore(maxScore > 0 ? row.score / maxScore : 0),
20238
20254
  workspace: row.workspace ?? undefined,
20239
- createdAt: new Date(row.created_at)
20255
+ createdAt: new Date(row.created_at),
20256
+ updatedAt: new Date(row.updated_at)
20240
20257
  }));
20241
20258
  } catch (error2) {
20242
20259
  throw new PersistenceError("Failed to search memories.", {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jcyamacho/agent-memory",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.4",
4
+ "version": "0.0.6",
5
5
  "bin": {
6
6
  "agent-memory": "dist/index.js"
7
7
  },