@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.
- package/README.md +12 -16
- package/dist/index.js +51 -34
- 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
|
|
70
|
-
conventions, decisions, constraints, and stable workflow habits.
|
|
71
|
-
concise
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
For `memory_recall`, pass
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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 `
|
|
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.
|
|
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:
|
|
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
|
-
|
|
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
|
|
19996
|
-
limit: number2().int().min(1).max(MAX_LIMIT).optional().describe("Maximum number of
|
|
19997
|
-
workspace: string2().optional().describe("
|
|
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("
|
|
20005
|
-
score: number2().describe("
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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 = [...
|
|
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
|
-
|
|
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.", {
|