@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.
- package/README.md +12 -15
- package/dist/index.js +74 -84
- 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 `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
|
-
- `
|
|
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.
|
|
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
|
|
19936
|
-
limit: number2().int().min(1).max(
|
|
19937
|
-
|
|
19938
|
-
|
|
19939
|
-
|
|
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("
|
|
19946
|
-
score: number2().describe("
|
|
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: "
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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,
|
|
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('"', '""');
|