@jcyamacho/agent-memory 0.0.3 → 0.0.5

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 -15
  2. package/dist/index.js +74 -84
  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 `preferred_workspace` to bias ranking. Use `filter_workspace` and
81
- `created_*` only for exact scoping. Keep `limit` small.
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
@@ -116,8 +114,7 @@ Inputs:
116
114
  - `terms` -> 2-5 distinctive terms or short phrases that should appear in the
117
115
  memory content; avoid full natural-language questions
118
116
  - `limit` -> maximum results to return
119
- - `preferred_workspace` -> ranking hint for a workspace
120
- - `filter_workspace` -> exact workspace filter
117
+ - `workspace` -> workspace or repo path; biases ranking toward this workspace
121
118
  - `created_after` -> ISO 8601 lower bound
122
119
  - `created_before` -> ISO 8601 upper bound
123
120
 
package/dist/index.js CHANGED
@@ -12464,7 +12464,7 @@ class StdioServerTransport {
12464
12464
  }
12465
12465
  }
12466
12466
  // package.json
12467
- var version2 = "0.0.3";
12467
+ var version2 = "0.0.5";
12468
12468
 
12469
12469
  // src/config.ts
12470
12470
  import { homedir } from "node:os";
@@ -19881,6 +19881,9 @@ var EMPTY_COMPLETION_RESULT = {
19881
19881
  }
19882
19882
  };
19883
19883
 
19884
+ // src/memory-service.ts
19885
+ import { randomUUID } from "node:crypto";
19886
+
19884
19887
  // src/errors.ts
19885
19888
  class MemoryError extends Error {
19886
19889
  code;
@@ -19905,6 +19908,63 @@ class PersistenceError extends MemoryError {
19905
19908
  }
19906
19909
  }
19907
19910
 
19911
+ // src/memory-service.ts
19912
+ var DEFAULT_LIMIT = 15;
19913
+ var MAX_LIMIT = 50;
19914
+
19915
+ class MemoryService {
19916
+ repository;
19917
+ constructor(repository) {
19918
+ this.repository = repository;
19919
+ }
19920
+ async save(input) {
19921
+ const content = input.content.trim();
19922
+ if (!content) {
19923
+ throw new ValidationError("Memory content is required.");
19924
+ }
19925
+ const now = new Date;
19926
+ const memory = {
19927
+ id: randomUUID(),
19928
+ content,
19929
+ workspace: normalizeOptionalString(input.workspace),
19930
+ createdAt: now,
19931
+ updatedAt: now
19932
+ };
19933
+ return this.repository.save(memory);
19934
+ }
19935
+ async search(input) {
19936
+ const terms = normalizeTerms(input.terms);
19937
+ if (terms.length === 0) {
19938
+ throw new ValidationError("At least one search term is required.");
19939
+ }
19940
+ const normalizedQuery = {
19941
+ terms,
19942
+ limit: normalizeLimit(input.limit),
19943
+ workspace: normalizeOptionalString(input.workspace),
19944
+ createdAfter: input.createdAfter,
19945
+ createdBefore: input.createdBefore
19946
+ };
19947
+ return this.repository.search(normalizedQuery);
19948
+ }
19949
+ }
19950
+ var normalizeLimit = (value) => {
19951
+ if (value === undefined) {
19952
+ return DEFAULT_LIMIT;
19953
+ }
19954
+ if (!Number.isInteger(value) || value < 1 || value > MAX_LIMIT) {
19955
+ throw new ValidationError(`Limit must be an integer between 1 and ${MAX_LIMIT}.`);
19956
+ }
19957
+ return value;
19958
+ };
19959
+ var normalizeOptionalString = (value) => {
19960
+ const trimmed = value?.trim();
19961
+ return trimmed ? trimmed : undefined;
19962
+ };
19963
+ var normalizeTerms = (terms) => {
19964
+ const normalizedTerms = terms.map((term) => term.trim()).filter(Boolean);
19965
+ return [...new Set(normalizedTerms)];
19966
+ };
19967
+
19908
19968
  // src/tools/shared.ts
19909
19969
  var toMcpError = (error2) => {
19910
19970
  if (error2 instanceof McpError) {
@@ -19932,34 +19992,32 @@ var parseOptionalDate = (value, fieldName) => {
19932
19992
 
19933
19993
  // src/tools/recall.ts
19934
19994
  var recallInputSchema = {
19935
- 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."),
19936
- limit: number2().int().min(1).max(20).optional().describe("Maximum number of memory results to return. Use a small number when you only need the best matches."),
19937
- preferred_workspace: string2().optional().describe("Preferred workspace or repository path to rank higher when relevant. This does not exclude other workspaces."),
19938
- filter_workspace: string2().optional().describe("Only return memories from this exact workspace or repository path."),
19939
- created_after: string2().optional().describe("Only return memories created at or after this ISO 8601 timestamp."),
19940
- created_before: string2().optional().describe("Only return memories created at or before this ISO 8601 timestamp.")
19995
+ 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."),
19996
+ 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."),
19997
+ 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."),
19998
+ 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."),
19999
+ 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.")
19941
20000
  };
19942
20001
  var recallOutputSchema = {
19943
20002
  results: array(object({
19944
20003
  id: string2().describe("Stable identifier for the remembered item."),
19945
- content: string2().describe("The remembered content that matched the search terms."),
19946
- score: number2().describe("Relevance score for this result. Higher means a better match."),
20004
+ content: string2().describe("Saved memory text that matched one or more search terms."),
20005
+ score: number2().describe("Relative relevance score for ranking results. Higher means a stronger match."),
19947
20006
  workspace: string2().optional().describe("Workspace associated with the memory, if available."),
19948
20007
  created_at: string2().describe("ISO 8601 timestamp showing when the memory was created.")
19949
20008
  }))
19950
20009
  };
19951
20010
  var registerRecallTool = (server, memoryService) => {
19952
20011
  server.registerTool("recall", {
19953
- 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.",
20012
+ 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.",
19954
20013
  inputSchema: recallInputSchema,
19955
20014
  outputSchema: recallOutputSchema
19956
- }, async ({ terms, limit, preferred_workspace, filter_workspace, created_after, created_before }) => {
20015
+ }, async ({ terms, limit, workspace, created_after, created_before }) => {
19957
20016
  try {
19958
20017
  const results = await memoryService.search({
19959
20018
  terms,
19960
20019
  limit,
19961
- preferredWorkspace: preferred_workspace,
19962
- filterWorkspace: filter_workspace,
20020
+ workspace,
19963
20021
  createdAfter: parseOptionalDate(created_after, "created_after"),
19964
20022
  createdBefore: parseOptionalDate(created_before, "created_before")
19965
20023
  });
@@ -20041,66 +20099,6 @@ var createMcpServer = (memoryService, version3) => {
20041
20099
  return server;
20042
20100
  };
20043
20101
 
20044
- // src/memory-service.ts
20045
- import { randomUUID } from "node:crypto";
20046
- var DEFAULT_LIMIT = 5;
20047
- var MAX_LIMIT = 20;
20048
-
20049
- class MemoryService {
20050
- repository;
20051
- constructor(repository) {
20052
- this.repository = repository;
20053
- }
20054
- async save(input) {
20055
- const content = input.content.trim();
20056
- if (!content) {
20057
- throw new ValidationError("Memory content is required.");
20058
- }
20059
- const now = new Date;
20060
- const memory = {
20061
- id: randomUUID(),
20062
- content,
20063
- workspace: normalizeOptionalString(input.workspace),
20064
- createdAt: now,
20065
- updatedAt: now
20066
- };
20067
- return this.repository.save(memory);
20068
- }
20069
- async search(input) {
20070
- const terms = normalizeTerms(input.terms);
20071
- if (terms.length === 0) {
20072
- throw new ValidationError("At least one search term is required.");
20073
- }
20074
- const normalizedQuery = {
20075
- terms,
20076
- limit: normalizeLimit(input.limit),
20077
- preferredWorkspace: normalizeOptionalString(input.preferredWorkspace),
20078
- filterWorkspace: normalizeOptionalString(input.filterWorkspace),
20079
- createdAfter: input.createdAfter,
20080
- createdBefore: input.createdBefore
20081
- };
20082
- const results = await this.repository.search(normalizedQuery);
20083
- return results.slice(0, normalizedQuery.limit);
20084
- }
20085
- }
20086
- var normalizeLimit = (value) => {
20087
- if (value === undefined) {
20088
- return DEFAULT_LIMIT;
20089
- }
20090
- if (!Number.isInteger(value) || value < 1 || value > MAX_LIMIT) {
20091
- throw new ValidationError(`Limit must be an integer between 1 and ${MAX_LIMIT}.`);
20092
- }
20093
- return value;
20094
- };
20095
- var normalizeOptionalString = (value) => {
20096
- const trimmed = value?.trim();
20097
- return trimmed ? trimmed : undefined;
20098
- };
20099
- var normalizeTerms = (terms) => {
20100
- const normalizedTerms = terms.map((term) => term.trim()).filter(Boolean);
20101
- return [...new Set(normalizedTerms)];
20102
- };
20103
-
20104
20102
  // src/sqlite-db.ts
20105
20103
  import { mkdirSync } from "node:fs";
20106
20104
  import { dirname } from "node:path";
@@ -20167,9 +20165,6 @@ var initializeMemoryDatabase = (database) => {
20167
20165
  };
20168
20166
 
20169
20167
  // src/sqlite-repository.ts
20170
- var CANDIDATE_MULTIPLIER = 5;
20171
- var MIN_CANDIDATES = 25;
20172
- var MAX_CANDIDATES = 100;
20173
20168
  var WORKSPACE_BIAS = 0.1;
20174
20169
 
20175
20170
  class SqliteMemoryRepository {
@@ -20206,17 +20201,13 @@ class SqliteMemoryRepository {
20206
20201
  const selectParams = [];
20207
20202
  const whereParams = [toFtsQuery(query.terms)];
20208
20203
  let scoreExpr;
20209
- if (query.preferredWorkspace) {
20204
+ if (query.workspace) {
20210
20205
  scoreExpr = "MAX(0, -bm25(memories_fts) + CASE WHEN m.workspace = ? THEN ? ELSE 0.0 END)";
20211
- selectParams.push(query.preferredWorkspace, WORKSPACE_BIAS);
20206
+ selectParams.push(query.workspace, WORKSPACE_BIAS);
20212
20207
  } else {
20213
20208
  scoreExpr = "MAX(0, -bm25(memories_fts))";
20214
20209
  }
20215
20210
  const whereClauses = ["memories_fts MATCH ?"];
20216
- if (query.filterWorkspace) {
20217
- whereClauses.push("m.workspace = ?");
20218
- whereParams.push(query.filterWorkspace);
20219
- }
20220
20211
  if (query.createdAfter) {
20221
20212
  whereClauses.push("m.created_at >= ?");
20222
20213
  whereParams.push(query.createdAfter.getTime());
@@ -20225,7 +20216,7 @@ class SqliteMemoryRepository {
20225
20216
  whereClauses.push("m.created_at <= ?");
20226
20217
  whereParams.push(query.createdBefore.getTime());
20227
20218
  }
20228
- const params = [...selectParams, ...whereParams, toCandidateLimit(query.limit)];
20219
+ const params = [...selectParams, ...whereParams, query.limit];
20229
20220
  const statement = this.database.prepare(`
20230
20221
  SELECT
20231
20222
  m.id,
@@ -20254,7 +20245,6 @@ class SqliteMemoryRepository {
20254
20245
  }
20255
20246
  }
20256
20247
  }
20257
- var toCandidateLimit = (limit) => Math.min(Math.max(limit * CANDIDATE_MULTIPLIER, MIN_CANDIDATES), MAX_CANDIDATES);
20258
20248
  var toFtsQuery = (terms) => terms.map(toFtsTerm).join(" OR ");
20259
20249
  var toFtsTerm = (term) => {
20260
20250
  const escaped = term.replaceAll('"', '""');
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.3",
4
+ "version": "0.0.5",
5
5
  "bin": {
6
6
  "agent-memory": "dist/index.js"
7
7
  },