@jcyamacho/agent-memory 0.0.18 → 0.0.19

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 +11 -8
  2. package/dist/index.js +36 -30
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -49,10 +49,11 @@ Optional LLM instructions to reinforce the MCP's built-in guidance:
49
49
  ## Agent Memory
50
50
 
51
51
  - Use `memory_recall` at conversation start and before design choices,
52
- conventions, or edge cases.
52
+ conventions, edge cases, or saving memory.
53
53
  - Query `memory_recall` with 2-5 short anchor-heavy terms or exact phrases,
54
54
  not full questions or sentences.
55
- - Pass `workspace` for project-scoped memory. Omit it only for truly global memories.
55
+ - Pass `workspace` for project-scoped memory. Omit it only for facts that
56
+ apply across projects.
56
57
  - Use `memory_remember` to save one durable fact when the user states a stable
57
58
  preference, correction, or reusable project decision.
58
59
  - If the fact already exists, use `memory_revise` instead of creating a duplicate.
@@ -83,15 +84,15 @@ memories:
83
84
 
84
85
  1. **Text relevance** is the primary signal -- memories whose content best
85
86
  matches your search terms rank highest.
86
- 2. **Embedding similarity** is the next strongest signal. Recall builds an
87
- embedding from your normalized search terms and boosts memories whose stored
87
+ 2. **Workspace match** is the next strongest signal. When you pass
88
+ `workspace`, exact matches rank highest and all other scoped workspaces rank
89
+ below exact matches.
90
+ 3. **Embedding similarity** is a secondary signal. Recall builds an embedding
91
+ from your normalized search terms and boosts memories whose stored
88
92
  embeddings are most semantically similar.
89
- 3. **Workspace match** is a strong secondary signal. When you pass
90
- `workspace`, exact matches rank highest, sibling repositories get a small
91
- boost, and unrelated workspaces rank lowest.
92
93
  4. **Global memories** (saved without a workspace) are treated as relevant
93
94
  everywhere. When you pass `workspace`, they rank below exact workspace
94
- matches and above sibling or unrelated repositories.
95
+ matches and above memories from other workspaces.
95
96
  5. **Recency** is a minor tiebreaker -- newer memories rank slightly above older
96
97
  ones when other signals are equal.
97
98
 
@@ -102,6 +103,8 @@ memories without a workspace only when they apply across all projects.
102
103
  When you save a memory from a git worktree, `agent-memory` stores the main repo
103
104
  root as the workspace. `recall` applies the same normalization to incoming
104
105
  workspace queries so linked worktrees still match repo-scoped memories exactly.
106
+ When that happens, recall returns the queried workspace value so callers can
107
+ treat the match as belonging to their current worktree context.
105
108
 
106
109
  ## Configuration
107
110
 
package/dist/index.js CHANGED
@@ -12464,7 +12464,7 @@ class StdioServerTransport {
12464
12464
  }
12465
12465
  }
12466
12466
  // package.json
12467
- var version2 = "0.0.18";
12467
+ var version2 = "0.0.19";
12468
12468
 
12469
12469
  // src/config.ts
12470
12470
  import { homedir } from "node:os";
@@ -20086,15 +20086,14 @@ function registerForgetTool(server, memory) {
20086
20086
  var toNormalizedScore = (value) => value;
20087
20087
 
20088
20088
  // src/ranking.ts
20089
- var RETRIEVAL_SCORE_WEIGHT = 8;
20090
- var EMBEDDING_SIMILARITY_WEIGHT = 5;
20091
- var WORKSPACE_MATCH_WEIGHT = 4;
20092
- var RECENCY_WEIGHT = 2;
20089
+ var RETRIEVAL_SCORE_WEIGHT = 9;
20090
+ var EMBEDDING_SIMILARITY_WEIGHT = 4;
20091
+ var WORKSPACE_MATCH_WEIGHT = 5;
20092
+ var RECENCY_WEIGHT = 1;
20093
20093
  var MAX_COMPOSITE_SCORE = RETRIEVAL_SCORE_WEIGHT + EMBEDDING_SIMILARITY_WEIGHT + WORKSPACE_MATCH_WEIGHT + RECENCY_WEIGHT;
20094
20094
  var GLOBAL_WORKSPACE_SCORE = 0.5;
20095
- var SIBLING_WORKSPACE_SCORE = 0.25;
20096
20095
  function rerankSearchResults(results, workspace, queryEmbedding) {
20097
- if (results.length <= 1) {
20096
+ if (results.length === 0) {
20098
20097
  return results;
20099
20098
  }
20100
20099
  const normalizedQueryWs = workspace ? normalizeWorkspacePath(workspace) : undefined;
@@ -20132,14 +20131,7 @@ function computeWorkspaceScore(memoryWs, queryWs) {
20132
20131
  if (normalizedMemoryWs === queryWs) {
20133
20132
  return 1;
20134
20133
  }
20135
- const queryLastSlashIndex = queryWs.lastIndexOf("/");
20136
- const memoryLastSlashIndex = normalizedMemoryWs.lastIndexOf("/");
20137
- if (queryLastSlashIndex <= 0 || memoryLastSlashIndex <= 0) {
20138
- return 0;
20139
- }
20140
- const queryParent = queryWs.slice(0, queryLastSlashIndex);
20141
- const memoryParent = normalizedMemoryWs.slice(0, memoryLastSlashIndex);
20142
- return memoryParent === queryParent ? SIBLING_WORKSPACE_SCORE : 0;
20134
+ return 0;
20143
20135
  }
20144
20136
 
20145
20137
  // src/memory-service.ts
@@ -20211,6 +20203,7 @@ class MemoryService {
20211
20203
  throw new ValidationError("At least one search term is required.");
20212
20204
  }
20213
20205
  const requestedLimit = normalizeLimit(input.limit);
20206
+ const queryWorkspace = normalizeOptionalString(input.workspace);
20214
20207
  const workspace = await this.workspaceResolver.resolve(input.workspace);
20215
20208
  const normalizedQuery = {
20216
20209
  terms,
@@ -20222,7 +20215,7 @@ class MemoryService {
20222
20215
  this.repository.search(normalizedQuery),
20223
20216
  this.embeddingService.createVector(terms.join(" "))
20224
20217
  ]);
20225
- return rerankSearchResults(results, workspace, queryEmbedding).slice(0, requestedLimit).map(toPublicSearchResult);
20218
+ return rerankSearchResults(results, workspace, queryEmbedding).slice(0, requestedLimit).map((result) => toPublicSearchResult(remapSearchResultWorkspace(result, workspace, queryWorkspace)));
20226
20219
  }
20227
20220
  }
20228
20221
  function toPublicMemoryRecord(memory) {
@@ -20272,12 +20265,25 @@ function normalizeTerms(terms) {
20272
20265
  const normalizedTerms = terms.map((term) => term.trim()).filter(Boolean);
20273
20266
  return [...new Set(normalizedTerms)];
20274
20267
  }
20268
+ function normalizeOptionalString(value) {
20269
+ const trimmed = value?.trim();
20270
+ return trimmed ? trimmed : undefined;
20271
+ }
20272
+ function remapSearchResultWorkspace(result, canonicalWorkspace, queryWorkspace) {
20273
+ if (!queryWorkspace || !canonicalWorkspace || result.workspace !== canonicalWorkspace) {
20274
+ return result;
20275
+ }
20276
+ return {
20277
+ ...result,
20278
+ workspace: queryWorkspace
20279
+ };
20280
+ }
20275
20281
 
20276
20282
  // src/mcp/tools/recall.ts
20277
20283
  var recallInputSchema = {
20278
- terms: array(string2()).min(1).describe("Pass 2-5 short anchor-heavy terms or exact phrases as separate entries. Prefer identifiers, commands, file paths, package names, and conventions likely to appear verbatim. Avoid vague words, full questions, and repeating the workspace name."),
20284
+ terms: array(string2()).min(1).describe("2-5 short anchor-heavy terms or exact phrases. Prefer identifiers, commands, file paths, and exact wording likely to appear in the memory."),
20279
20285
  limit: number2().int().min(1).max(MAX_RECALL_LIMIT).optional().describe("Maximum matches to return. Keep this small when you only need the strongest hits."),
20280
- workspace: string2().optional().describe("Pass the current working directory to prefer memories from the active project. Git worktree paths are normalized to the main repo root for matching."),
20286
+ workspace: string2().optional().describe("Current working directory for project-scoped recall. Omit for cross-project recall."),
20281
20287
  updated_after: string2().optional().describe("Only return memories updated on or after this ISO 8601 timestamp."),
20282
20288
  updated_before: string2().optional().describe("Only return memories updated on or before this ISO 8601 timestamp.")
20283
20289
  };
@@ -20296,7 +20302,7 @@ function registerRecallTool(server, memory) {
20296
20302
  readOnlyHint: true,
20297
20303
  openWorldHint: false
20298
20304
  },
20299
- description: "Find memories relevant to the current task or check whether a fact already exists before saving. Use at conversation start and before design choices. Pass short anchor-heavy `terms` and `workspace` when available. Returns `<memories>...</memories>` or a no-match hint.",
20305
+ description: "Retrieve memories relevant to the current task or check whether a fact already exists before saving. Use at conversation start and before design choices. Pass short anchor-heavy `terms` and `workspace` when available. Results reflect the queried workspace context when applicable. Returns `<memories>...</memories>` or a no-match hint.",
20300
20306
  inputSchema: recallInputSchema
20301
20307
  }, async ({ terms, limit, workspace, updated_after, updated_before }) => {
20302
20308
  try {
@@ -20323,7 +20329,7 @@ ${results.map(toMemoryXml).join(`
20323
20329
  // src/mcp/tools/remember.ts
20324
20330
  var rememberInputSchema = {
20325
20331
  content: string2().describe("One new durable fact to save. Use a self-contained sentence or short note."),
20326
- workspace: string2().optional().describe("Pass the current working directory for project-scoped memory. Git worktree paths are saved as the main repo root. Omit for truly global memory.")
20332
+ workspace: string2().optional().describe("Current working directory for project-scoped memory. Omit for facts that apply across projects.")
20327
20333
  };
20328
20334
  function registerRememberTool(server, memory) {
20329
20335
  server.registerTool("remember", {
@@ -20333,7 +20339,7 @@ function registerRememberTool(server, memory) {
20333
20339
  idempotentHint: false,
20334
20340
  openWorldHint: false
20335
20341
  },
20336
- description: 'Save one new durable fact for later recall. Use for stable preferences, corrections, reusable decisions, and project context not obvious from code or git history. Save exactly one fact. If the memory already exists, use `revise` instead. Do not store secrets, temporary task state, or facts obvious from code or git history. Returns `<memory id="..." />`.',
20342
+ description: 'Save one new durable fact for later recall. Use for stable preferences, reusable decisions, and project context not obvious from code or git history. If the fact already exists, use `revise` instead. Returns `<memory id="..." />`.',
20337
20343
  inputSchema: rememberInputSchema
20338
20344
  }, async ({ content, workspace }) => {
20339
20345
  try {
@@ -20353,7 +20359,7 @@ function registerRememberTool(server, memory) {
20353
20359
  // src/mcp/tools/revise.ts
20354
20360
  var reviseInputSchema = {
20355
20361
  id: string2().describe("Memory id to update. Use an id returned by `recall`."),
20356
- content: string2().describe("Corrected replacement text for that memory. Keep it to one durable fact.")
20362
+ content: string2().describe("Corrected replacement text for that memory.")
20357
20363
  };
20358
20364
  function registerReviseTool(server, memory) {
20359
20365
  server.registerTool("revise", {
@@ -20363,7 +20369,7 @@ function registerReviseTool(server, memory) {
20363
20369
  idempotentHint: false,
20364
20370
  openWorldHint: false
20365
20371
  },
20366
- description: 'Update one existing memory when the same fact still applies but its wording or details changed. Use after `recall` when you already have the memory id. Keep it to one fact and do not merge unrelated facts. Returns `<memory id="..." updated_at="..." />`.',
20372
+ description: 'Update one existing memory when the same fact still applies but its wording or details changed. Use after `recall` when you already have the memory id. Returns `<memory id="..." updated_at="..." />`.',
20367
20373
  inputSchema: reviseInputSchema
20368
20374
  }, async ({ id, content }) => {
20369
20375
  try {
@@ -20385,11 +20391,11 @@ function registerReviseTool(server, memory) {
20385
20391
  // src/mcp/server.ts
20386
20392
  var SERVER_INSTRUCTIONS = [
20387
20393
  "Use this server only for durable memory that should survive across turns: stable preferences, corrections, reusable decisions, and project context not obvious from code or git history.",
20388
- "Use `recall` at conversation start, before design choices, and before saving memory.",
20389
- "Use `remember` only for a new durable fact. Use `revise` when the fact already exists but needs correction.",
20390
- "Use `forget` only when a memory is wrong or no longer relevant.",
20391
- "Pass workspace for project-scoped memory. Omit it only for truly global memory.",
20392
- "Do not store secrets or temporary task state in memory."
20394
+ "Use `recall` at conversation start, before design choices, and before saving or revising memory.",
20395
+ "Use `remember` for one new durable fact. Use `revise` when the fact already exists but needs correction.",
20396
+ "Use `forget` only when a memory is wrong or obsolete.",
20397
+ "Pass workspace for project-scoped memory. Omit it only for facts that apply across projects.",
20398
+ "Do not store secrets, temporary task state, or facts obvious from current code or git history."
20393
20399
  ].join(" ");
20394
20400
  function createMcpServer(memory, version3) {
20395
20401
  const server = new McpServer({
@@ -24244,7 +24250,7 @@ function createGitWorkspaceResolver(options = {}) {
24244
24250
  const cache = new Map;
24245
24251
  return {
24246
24252
  async resolve(workspace) {
24247
- const trimmed = normalizeOptionalString(workspace);
24253
+ const trimmed = normalizeOptionalString2(workspace);
24248
24254
  if (!trimmed) {
24249
24255
  return;
24250
24256
  }
@@ -24275,7 +24281,7 @@ async function defaultGetGitCommonDir(cwd) {
24275
24281
  });
24276
24282
  return stdout.trim();
24277
24283
  }
24278
- function normalizeOptionalString(value) {
24284
+ function normalizeOptionalString2(value) {
24279
24285
  const trimmed = value?.trim();
24280
24286
  return trimmed ? trimmed : undefined;
24281
24287
  }
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.18",
4
+ "version": "0.0.19",
5
5
  "bin": {
6
6
  "agent-memory": "dist/index.js"
7
7
  },