@jcyamacho/agent-memory 0.0.1 → 0.0.3
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 +115 -117
- package/dist/index.js +46 -83
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -2,34 +2,93 @@
|
|
|
2
2
|
|
|
3
3
|
Persistent memory for MCP-powered coding agents.
|
|
4
4
|
|
|
5
|
-
`agent-memory` gives your LLM
|
|
6
|
-
|
|
5
|
+
`agent-memory` is a stdio MCP server that gives your LLM durable memory backed
|
|
6
|
+
by SQLite. It exposes two tools:
|
|
7
7
|
|
|
8
8
|
- `remember` -> save facts, decisions, preferences, and project context
|
|
9
9
|
- `recall` -> retrieve the most relevant memories later
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
Use it when your agent should remember preferences, project facts, and prior
|
|
12
|
+
decisions across sessions.
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Quick Start
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
that actually matter:
|
|
16
|
+
Claude CLI:
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- Session-specific notes that should still be discoverable later
|
|
18
|
+
```bash
|
|
19
|
+
claude mcp add --scope user memory -- npx -y @jcyamacho/agent-memory
|
|
20
|
+
```
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
Codex CLI:
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
```bash
|
|
25
|
+
codex mcp add memory -- npx -y @jcyamacho/agent-memory
|
|
26
|
+
```
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
Example MCP server config:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"memory": {
|
|
34
|
+
"command": "npx",
|
|
35
|
+
"args": [
|
|
36
|
+
"-y",
|
|
37
|
+
"@jcyamacho/agent-memory"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
With a custom database path:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"memory": {
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": [
|
|
52
|
+
"-y",
|
|
53
|
+
"@jcyamacho/agent-memory"
|
|
54
|
+
],
|
|
55
|
+
"env": {
|
|
56
|
+
"AGENT_MEMORY_DB_PATH": "/absolute/path/to/memory.db"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Recommended LLM instructions to pair with this MCP:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
Use `memory_recall` at task start and whenever prior preferences, project facts,
|
|
67
|
+
or decisions may matter.
|
|
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.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## What It Stores
|
|
85
|
+
|
|
86
|
+
This MCP is useful for context that should survive across turns and sessions:
|
|
87
|
+
|
|
88
|
+
- User preferences like response style, formatting, and workflow habits
|
|
89
|
+
- Project facts like paths, architecture choices, and conventions
|
|
90
|
+
- Important decisions and constraints that should not be rediscovered
|
|
91
|
+
- Project-scoped notes that still matter later
|
|
33
92
|
|
|
34
93
|
## Tools
|
|
35
94
|
|
|
@@ -39,17 +98,13 @@ Save durable context for later recall.
|
|
|
39
98
|
|
|
40
99
|
Inputs:
|
|
41
100
|
|
|
42
|
-
- `content` ->
|
|
43
|
-
- `
|
|
44
|
-
- `workspace` -> repository or workspace path for project scoping
|
|
45
|
-
- `session` -> conversation or execution session identifier
|
|
101
|
+
- `content` -> fact, preference, decision, or context to store
|
|
102
|
+
- `workspace` -> repository or workspace path
|
|
46
103
|
|
|
47
104
|
Output:
|
|
48
105
|
|
|
49
106
|
- `id`
|
|
50
|
-
- `source`
|
|
51
107
|
- `workspace`
|
|
52
|
-
- `session`
|
|
53
108
|
- `created_at`
|
|
54
109
|
|
|
55
110
|
### `recall`
|
|
@@ -58,50 +113,30 @@ Retrieve relevant memories for the current task.
|
|
|
58
113
|
|
|
59
114
|
Inputs:
|
|
60
115
|
|
|
61
|
-
- `
|
|
62
|
-
|
|
63
|
-
- `
|
|
64
|
-
- `preferred_workspace` ->
|
|
65
|
-
- `
|
|
66
|
-
- `filter_workspace` -> only return memories from this exact workspace
|
|
116
|
+
- `terms` -> 2-5 distinctive terms or short phrases that should appear in the
|
|
117
|
+
memory content; avoid full natural-language questions
|
|
118
|
+
- `limit` -> maximum results to return
|
|
119
|
+
- `preferred_workspace` -> ranking hint for a workspace
|
|
120
|
+
- `filter_workspace` -> exact workspace filter
|
|
67
121
|
- `created_after` -> ISO 8601 lower bound
|
|
68
122
|
- `created_before` -> ISO 8601 upper bound
|
|
69
123
|
|
|
70
124
|
Output:
|
|
71
125
|
|
|
72
|
-
- `results[]` with `id`, `content`, `score`, `
|
|
73
|
-
and `created_at`
|
|
74
|
-
|
|
75
|
-
## Installation
|
|
126
|
+
- `results[]` with `id`, `content`, `score`, `workspace`, and `created_at`
|
|
76
127
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
npm install -g @jcyamacho/agent-memory
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### From source
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
bun install
|
|
87
|
-
bun run build
|
|
88
|
-
```
|
|
128
|
+
## Setup
|
|
89
129
|
|
|
90
|
-
|
|
130
|
+
For normal usage:
|
|
91
131
|
|
|
92
|
-
|
|
93
|
-
agent-memory
|
|
94
|
-
```
|
|
132
|
+
- Node.js
|
|
95
133
|
|
|
96
|
-
|
|
134
|
+
For local development or running from source:
|
|
97
135
|
|
|
136
|
+
- Bun
|
|
98
137
|
- Node.js
|
|
99
|
-
- A normal package install with `node_modules`
|
|
100
|
-
|
|
101
|
-
Important: the build keeps `better-sqlite3` external on purpose. That is
|
|
102
|
-
required for its native binding to load correctly at runtime.
|
|
103
138
|
|
|
104
|
-
|
|
139
|
+
### Database location
|
|
105
140
|
|
|
106
141
|
By default, the SQLite database is created at:
|
|
107
142
|
|
|
@@ -115,48 +150,18 @@ Override it with:
|
|
|
115
150
|
AGENT_MEMORY_DB_PATH=/absolute/path/to/memory.db
|
|
116
151
|
```
|
|
117
152
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
AGENT_MEMORY_DB_PATH="$HOME/.local/share/agent-memory/memory.db" agent-memory
|
|
122
|
-
```
|
|
153
|
+
Set `AGENT_MEMORY_DB_PATH` when you want to:
|
|
123
154
|
|
|
124
|
-
|
|
155
|
+
- keep memory in a project-specific location
|
|
156
|
+
- share a memory DB across multiple clients
|
|
157
|
+
- store the DB somewhere easier to back up or inspect
|
|
125
158
|
|
|
126
|
-
|
|
159
|
+
Beta note: schema changes are not migrated. If you are upgrading from an older
|
|
160
|
+
beta, delete the existing memory DB and let the server create a new one.
|
|
127
161
|
|
|
128
|
-
|
|
129
|
-
bun install
|
|
130
|
-
bun run build
|
|
131
|
-
node dist/index.js
|
|
132
|
-
```
|
|
162
|
+
## Run from source
|
|
133
163
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
bun lint
|
|
138
|
-
bun test
|
|
139
|
-
bun run build
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## MCP Client Configuration
|
|
143
|
-
|
|
144
|
-
Example MCP server entry:
|
|
145
|
-
|
|
146
|
-
```json
|
|
147
|
-
{
|
|
148
|
-
"mcpServers": {
|
|
149
|
-
"memory": {
|
|
150
|
-
"command": "agent-memory",
|
|
151
|
-
"env": {
|
|
152
|
-
"AGENT_MEMORY_DB_PATH": "/absolute/path/to/memory.db"
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
If you are running from source instead of a global install, use:
|
|
164
|
+
If you are developing locally instead of using the published package:
|
|
160
165
|
|
|
161
166
|
```json
|
|
162
167
|
{
|
|
@@ -174,36 +179,29 @@ If you are running from source instead of a global install, use:
|
|
|
174
179
|
}
|
|
175
180
|
```
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
Copy and paste this into your system prompt, `AGENTS.md`, `CLAUDE.md`, or
|
|
180
|
-
similar instruction file.
|
|
182
|
+
Build first:
|
|
181
183
|
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
Use `memory_remember` to save durable context that will matter later, such as
|
|
187
|
-
user preferences, project conventions, architecture decisions, constraints, and
|
|
188
|
-
stable workflow habits.
|
|
184
|
+
```bash
|
|
185
|
+
bun install
|
|
186
|
+
bun run build
|
|
187
|
+
```
|
|
189
188
|
|
|
190
|
-
|
|
191
|
-
`workspace`, and `session` when available so future retrieval is better scoped.
|
|
189
|
+
## Local Development
|
|
192
190
|
|
|
193
|
-
|
|
191
|
+
Only needed if you want to work on the project itself.
|
|
194
192
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
```bash
|
|
194
|
+
bun install
|
|
195
|
+
bun lint
|
|
196
|
+
bun test
|
|
197
|
+
bun run build
|
|
199
198
|
```
|
|
200
199
|
|
|
201
|
-
##
|
|
200
|
+
## Notes
|
|
202
201
|
|
|
203
|
-
-
|
|
204
|
-
|
|
205
|
-
-
|
|
206
|
-
- Runtime code avoids Bun-only APIs
|
|
202
|
+
- `better-sqlite3` stays external in the build so its native binding loads
|
|
203
|
+
correctly at runtime.
|
|
204
|
+
- Runtime output is Node-compatible and built to `dist/index.js`.
|
|
207
205
|
|
|
208
206
|
## License
|
|
209
207
|
|
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.3";
|
|
12468
12468
|
|
|
12469
12469
|
// src/config.ts
|
|
12470
12470
|
import { homedir } from "node:os";
|
|
@@ -19932,11 +19932,9 @@ var parseOptionalDate = (value, fieldName) => {
|
|
|
19932
19932
|
|
|
19933
19933
|
// src/tools/recall.ts
|
|
19934
19934
|
var recallInputSchema = {
|
|
19935
|
-
|
|
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
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_source: string2().optional().describe("Preferred source to rank higher when relevant, such as a client, tool, or agent name. This does not exclude other sources."),
|
|
19938
19937
|
preferred_workspace: string2().optional().describe("Preferred workspace or repository path to rank higher when relevant. This does not exclude other workspaces."),
|
|
19939
|
-
filter_source: string2().optional().describe("Only return memories from this exact source."),
|
|
19940
19938
|
filter_workspace: string2().optional().describe("Only return memories from this exact workspace or repository path."),
|
|
19941
19939
|
created_after: string2().optional().describe("Only return memories created at or after this ISO 8601 timestamp."),
|
|
19942
19940
|
created_before: string2().optional().describe("Only return memories created at or before this ISO 8601 timestamp.")
|
|
@@ -19944,11 +19942,9 @@ var recallInputSchema = {
|
|
|
19944
19942
|
var recallOutputSchema = {
|
|
19945
19943
|
results: array(object({
|
|
19946
19944
|
id: string2().describe("Stable identifier for the remembered item."),
|
|
19947
|
-
content: string2().describe("The remembered content that matched the
|
|
19945
|
+
content: string2().describe("The remembered content that matched the search terms."),
|
|
19948
19946
|
score: number2().describe("Relevance score for this result. Higher means a better match."),
|
|
19949
|
-
source: string2().optional().describe("Source associated with the memory, if available."),
|
|
19950
19947
|
workspace: string2().optional().describe("Workspace associated with the memory, if available."),
|
|
19951
|
-
session: string2().optional().describe("Session associated with the memory, if available."),
|
|
19952
19948
|
created_at: string2().describe("ISO 8601 timestamp showing when the memory was created.")
|
|
19953
19949
|
}))
|
|
19954
19950
|
};
|
|
@@ -19957,23 +19953,12 @@ var registerRecallTool = (server, memoryService) => {
|
|
|
19957
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.",
|
|
19958
19954
|
inputSchema: recallInputSchema,
|
|
19959
19955
|
outputSchema: recallOutputSchema
|
|
19960
|
-
}, async ({
|
|
19961
|
-
query,
|
|
19962
|
-
limit,
|
|
19963
|
-
preferred_source,
|
|
19964
|
-
preferred_workspace,
|
|
19965
|
-
filter_source,
|
|
19966
|
-
filter_workspace,
|
|
19967
|
-
created_after,
|
|
19968
|
-
created_before
|
|
19969
|
-
}) => {
|
|
19956
|
+
}, async ({ terms, limit, preferred_workspace, filter_workspace, created_after, created_before }) => {
|
|
19970
19957
|
try {
|
|
19971
19958
|
const results = await memoryService.search({
|
|
19972
|
-
|
|
19959
|
+
terms,
|
|
19973
19960
|
limit,
|
|
19974
|
-
preferredSource: preferred_source,
|
|
19975
19961
|
preferredWorkspace: preferred_workspace,
|
|
19976
|
-
filterSource: filter_source,
|
|
19977
19962
|
filterWorkspace: filter_workspace,
|
|
19978
19963
|
createdAfter: parseOptionalDate(created_after, "created_after"),
|
|
19979
19964
|
createdBefore: parseOptionalDate(created_before, "created_before")
|
|
@@ -19983,17 +19968,17 @@ var registerRecallTool = (server, memoryService) => {
|
|
|
19983
19968
|
id: result.id,
|
|
19984
19969
|
content: result.content,
|
|
19985
19970
|
score: result.score,
|
|
19986
|
-
source: result.source,
|
|
19987
19971
|
workspace: result.workspace,
|
|
19988
|
-
session: result.session,
|
|
19989
19972
|
created_at: result.createdAt.toISOString()
|
|
19990
19973
|
}))
|
|
19991
19974
|
};
|
|
19975
|
+
const matchCount = structuredContent.results.length;
|
|
19976
|
+
const summary = matchCount === 1 ? "Found 1 matching memory." : `Found ${matchCount} matching memories.`;
|
|
19992
19977
|
return {
|
|
19993
19978
|
content: [
|
|
19994
19979
|
{
|
|
19995
19980
|
type: "text",
|
|
19996
|
-
text:
|
|
19981
|
+
text: summary
|
|
19997
19982
|
}
|
|
19998
19983
|
],
|
|
19999
19984
|
structuredContent
|
|
@@ -20007,15 +19992,11 @@ var registerRecallTool = (server, memoryService) => {
|
|
|
20007
19992
|
// src/tools/remember.ts
|
|
20008
19993
|
var rememberInputSchema = {
|
|
20009
19994
|
content: string2().describe("The exact fact, preference, decision, or context to remember for future retrieval. Use a self-contained sentence or short note."),
|
|
20010
|
-
|
|
20011
|
-
workspace: string2().optional().describe("Repository or workspace path this memory belongs to. Use it to keep memories scoped to a project."),
|
|
20012
|
-
session: string2().optional().describe("Conversation or execution session identifier. Useful for tying memories back to one run or thread.")
|
|
19995
|
+
workspace: string2().optional().describe("Repository or workspace path this memory belongs to. Use it to keep memories scoped to a project.")
|
|
20013
19996
|
};
|
|
20014
19997
|
var rememberOutputSchema = {
|
|
20015
19998
|
id: string2().describe("Stable identifier for the saved memory."),
|
|
20016
|
-
source: string2().optional().describe("Source stored with the memory, if provided."),
|
|
20017
19999
|
workspace: string2().optional().describe("Workspace stored with the memory, if provided."),
|
|
20018
|
-
session: string2().optional().describe("Session stored with the memory, if provided."),
|
|
20019
20000
|
created_at: string2().describe("ISO 8601 timestamp showing when the memory was created.")
|
|
20020
20001
|
};
|
|
20021
20002
|
var registerRememberTool = (server, memoryService) => {
|
|
@@ -20023,26 +20004,22 @@ var registerRememberTool = (server, memoryService) => {
|
|
|
20023
20004
|
description: "Save durable context for later recall. Use this for user preferences, project facts, decisions, constraints, or other information worth remembering across turns and tools.",
|
|
20024
20005
|
inputSchema: rememberInputSchema,
|
|
20025
20006
|
outputSchema: rememberOutputSchema
|
|
20026
|
-
}, async ({ content,
|
|
20007
|
+
}, async ({ content, workspace }) => {
|
|
20027
20008
|
try {
|
|
20028
20009
|
const memory = await memoryService.save({
|
|
20029
20010
|
content,
|
|
20030
|
-
|
|
20031
|
-
workspace,
|
|
20032
|
-
session
|
|
20011
|
+
workspace
|
|
20033
20012
|
});
|
|
20034
20013
|
const structuredContent = {
|
|
20035
20014
|
id: memory.id,
|
|
20036
|
-
source: memory.source,
|
|
20037
20015
|
workspace: memory.workspace,
|
|
20038
|
-
session: memory.session,
|
|
20039
20016
|
created_at: memory.createdAt.toISOString()
|
|
20040
20017
|
};
|
|
20041
20018
|
return {
|
|
20042
20019
|
content: [
|
|
20043
20020
|
{
|
|
20044
20021
|
type: "text",
|
|
20045
|
-
text:
|
|
20022
|
+
text: "Saved memory."
|
|
20046
20023
|
}
|
|
20047
20024
|
],
|
|
20048
20025
|
structuredContent
|
|
@@ -20068,8 +20045,6 @@ var createMcpServer = (memoryService, version3) => {
|
|
|
20068
20045
|
import { randomUUID } from "node:crypto";
|
|
20069
20046
|
var DEFAULT_LIMIT = 5;
|
|
20070
20047
|
var MAX_LIMIT = 20;
|
|
20071
|
-
var SOURCE_BIAS = 0.15;
|
|
20072
|
-
var WORKSPACE_BIAS = 0.1;
|
|
20073
20048
|
|
|
20074
20049
|
class MemoryService {
|
|
20075
20050
|
repository;
|
|
@@ -20085,34 +20060,27 @@ class MemoryService {
|
|
|
20085
20060
|
const memory = {
|
|
20086
20061
|
id: randomUUID(),
|
|
20087
20062
|
content,
|
|
20088
|
-
source: normalizeOptionalString(input.source),
|
|
20089
20063
|
workspace: normalizeOptionalString(input.workspace),
|
|
20090
|
-
session: normalizeOptionalString(input.session),
|
|
20091
20064
|
createdAt: now,
|
|
20092
20065
|
updatedAt: now
|
|
20093
20066
|
};
|
|
20094
20067
|
return this.repository.save(memory);
|
|
20095
20068
|
}
|
|
20096
20069
|
async search(input) {
|
|
20097
|
-
const
|
|
20098
|
-
if (
|
|
20099
|
-
throw new ValidationError("
|
|
20070
|
+
const terms = normalizeTerms(input.terms);
|
|
20071
|
+
if (terms.length === 0) {
|
|
20072
|
+
throw new ValidationError("At least one search term is required.");
|
|
20100
20073
|
}
|
|
20101
20074
|
const normalizedQuery = {
|
|
20102
|
-
|
|
20075
|
+
terms,
|
|
20103
20076
|
limit: normalizeLimit(input.limit),
|
|
20104
|
-
preferredSource: normalizeOptionalString(input.preferredSource),
|
|
20105
20077
|
preferredWorkspace: normalizeOptionalString(input.preferredWorkspace),
|
|
20106
|
-
filterSource: normalizeOptionalString(input.filterSource),
|
|
20107
20078
|
filterWorkspace: normalizeOptionalString(input.filterWorkspace),
|
|
20108
20079
|
createdAfter: input.createdAfter,
|
|
20109
20080
|
createdBefore: input.createdBefore
|
|
20110
20081
|
};
|
|
20111
20082
|
const results = await this.repository.search(normalizedQuery);
|
|
20112
|
-
return results.
|
|
20113
|
-
...result,
|
|
20114
|
-
score: rankResult(result, normalizedQuery)
|
|
20115
|
-
})).sort((left, right) => right.score - left.score).slice(0, normalizedQuery.limit);
|
|
20083
|
+
return results.slice(0, normalizedQuery.limit);
|
|
20116
20084
|
}
|
|
20117
20085
|
}
|
|
20118
20086
|
var normalizeLimit = (value) => {
|
|
@@ -20128,15 +20096,9 @@ var normalizeOptionalString = (value) => {
|
|
|
20128
20096
|
const trimmed = value?.trim();
|
|
20129
20097
|
return trimmed ? trimmed : undefined;
|
|
20130
20098
|
};
|
|
20131
|
-
var
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
score += SOURCE_BIAS;
|
|
20135
|
-
}
|
|
20136
|
-
if (query.preferredWorkspace && result.workspace === query.preferredWorkspace) {
|
|
20137
|
-
score += WORKSPACE_BIAS;
|
|
20138
|
-
}
|
|
20139
|
-
return Number(score.toFixed(6));
|
|
20099
|
+
var normalizeTerms = (terms) => {
|
|
20100
|
+
const normalizedTerms = terms.map((term) => term.trim()).filter(Boolean);
|
|
20101
|
+
return [...new Set(normalizedTerms)];
|
|
20140
20102
|
};
|
|
20141
20103
|
|
|
20142
20104
|
// src/sqlite-db.ts
|
|
@@ -20147,22 +20109,19 @@ var MEMORY_SCHEMA = `
|
|
|
20147
20109
|
CREATE TABLE IF NOT EXISTS memories (
|
|
20148
20110
|
id TEXT PRIMARY KEY,
|
|
20149
20111
|
content TEXT NOT NULL,
|
|
20150
|
-
source TEXT,
|
|
20151
20112
|
workspace TEXT,
|
|
20152
|
-
session TEXT,
|
|
20153
20113
|
created_at INTEGER NOT NULL,
|
|
20154
20114
|
updated_at INTEGER NOT NULL
|
|
20155
20115
|
);
|
|
20156
20116
|
|
|
20157
20117
|
CREATE INDEX IF NOT EXISTS idx_memories_created_at ON memories(created_at);
|
|
20158
|
-
CREATE INDEX IF NOT EXISTS idx_memories_source ON memories(source);
|
|
20159
20118
|
CREATE INDEX IF NOT EXISTS idx_memories_workspace ON memories(workspace);
|
|
20160
20119
|
|
|
20161
20120
|
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
20162
20121
|
content,
|
|
20163
20122
|
content = 'memories',
|
|
20164
20123
|
content_rowid = 'rowid',
|
|
20165
|
-
tokenize = 'unicode61'
|
|
20124
|
+
tokenize = 'porter unicode61'
|
|
20166
20125
|
);
|
|
20167
20126
|
|
|
20168
20127
|
CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
|
|
@@ -20211,6 +20170,7 @@ var initializeMemoryDatabase = (database) => {
|
|
|
20211
20170
|
var CANDIDATE_MULTIPLIER = 5;
|
|
20212
20171
|
var MIN_CANDIDATES = 25;
|
|
20213
20172
|
var MAX_CANDIDATES = 100;
|
|
20173
|
+
var WORKSPACE_BIAS = 0.1;
|
|
20214
20174
|
|
|
20215
20175
|
class SqliteMemoryRepository {
|
|
20216
20176
|
database;
|
|
@@ -20221,9 +20181,7 @@ class SqliteMemoryRepository {
|
|
|
20221
20181
|
INSERT INTO memories (
|
|
20222
20182
|
id,
|
|
20223
20183
|
content,
|
|
20224
|
-
source,
|
|
20225
20184
|
workspace,
|
|
20226
|
-
session,
|
|
20227
20185
|
created_at,
|
|
20228
20186
|
updated_at
|
|
20229
20187
|
) VALUES (
|
|
@@ -20231,15 +20189,13 @@ class SqliteMemoryRepository {
|
|
|
20231
20189
|
?,
|
|
20232
20190
|
?,
|
|
20233
20191
|
?,
|
|
20234
|
-
?,
|
|
20235
|
-
?,
|
|
20236
20192
|
?
|
|
20237
20193
|
)
|
|
20238
20194
|
`);
|
|
20239
20195
|
}
|
|
20240
20196
|
async save(memory) {
|
|
20241
20197
|
try {
|
|
20242
|
-
this.insertStatement.run(memory.id, memory.content, memory.
|
|
20198
|
+
this.insertStatement.run(memory.id, memory.content, memory.workspace, memory.createdAt.getTime(), memory.updatedAt.getTime());
|
|
20243
20199
|
return memory;
|
|
20244
20200
|
} catch (error2) {
|
|
20245
20201
|
throw new PersistenceError("Failed to save memory.", { cause: error2 });
|
|
@@ -20247,48 +20203,48 @@ class SqliteMemoryRepository {
|
|
|
20247
20203
|
}
|
|
20248
20204
|
async search(query) {
|
|
20249
20205
|
try {
|
|
20250
|
-
const
|
|
20251
|
-
const
|
|
20252
|
-
|
|
20253
|
-
|
|
20254
|
-
|
|
20206
|
+
const selectParams = [];
|
|
20207
|
+
const whereParams = [toFtsQuery(query.terms)];
|
|
20208
|
+
let scoreExpr;
|
|
20209
|
+
if (query.preferredWorkspace) {
|
|
20210
|
+
scoreExpr = "MAX(0, -bm25(memories_fts) + CASE WHEN m.workspace = ? THEN ? ELSE 0.0 END)";
|
|
20211
|
+
selectParams.push(query.preferredWorkspace, WORKSPACE_BIAS);
|
|
20212
|
+
} else {
|
|
20213
|
+
scoreExpr = "MAX(0, -bm25(memories_fts))";
|
|
20255
20214
|
}
|
|
20215
|
+
const whereClauses = ["memories_fts MATCH ?"];
|
|
20256
20216
|
if (query.filterWorkspace) {
|
|
20257
20217
|
whereClauses.push("m.workspace = ?");
|
|
20258
|
-
|
|
20218
|
+
whereParams.push(query.filterWorkspace);
|
|
20259
20219
|
}
|
|
20260
20220
|
if (query.createdAfter) {
|
|
20261
20221
|
whereClauses.push("m.created_at >= ?");
|
|
20262
|
-
|
|
20222
|
+
whereParams.push(query.createdAfter.getTime());
|
|
20263
20223
|
}
|
|
20264
20224
|
if (query.createdBefore) {
|
|
20265
20225
|
whereClauses.push("m.created_at <= ?");
|
|
20266
|
-
|
|
20226
|
+
whereParams.push(query.createdBefore.getTime());
|
|
20267
20227
|
}
|
|
20228
|
+
const params = [...selectParams, ...whereParams, toCandidateLimit(query.limit)];
|
|
20268
20229
|
const statement = this.database.prepare(`
|
|
20269
20230
|
SELECT
|
|
20270
20231
|
m.id,
|
|
20271
20232
|
m.content,
|
|
20272
|
-
m.source,
|
|
20273
20233
|
m.workspace,
|
|
20274
|
-
m.session,
|
|
20275
20234
|
m.created_at,
|
|
20276
|
-
|
|
20235
|
+
${scoreExpr} AS score
|
|
20277
20236
|
FROM memories_fts
|
|
20278
20237
|
INNER JOIN memories AS m ON m.rowid = memories_fts.rowid
|
|
20279
20238
|
WHERE ${whereClauses.join(" AND ")}
|
|
20280
|
-
ORDER BY
|
|
20239
|
+
ORDER BY score DESC
|
|
20281
20240
|
LIMIT ?
|
|
20282
20241
|
`);
|
|
20283
|
-
params.push(toCandidateLimit(query.limit));
|
|
20284
20242
|
const rows = statement.all(...params);
|
|
20285
20243
|
return rows.map((row) => ({
|
|
20286
20244
|
id: row.id,
|
|
20287
20245
|
content: row.content,
|
|
20288
20246
|
score: row.score,
|
|
20289
|
-
source: row.source ?? undefined,
|
|
20290
20247
|
workspace: row.workspace ?? undefined,
|
|
20291
|
-
session: row.session ?? undefined,
|
|
20292
20248
|
createdAt: new Date(row.created_at)
|
|
20293
20249
|
}));
|
|
20294
20250
|
} catch (error2) {
|
|
@@ -20299,7 +20255,14 @@ class SqliteMemoryRepository {
|
|
|
20299
20255
|
}
|
|
20300
20256
|
}
|
|
20301
20257
|
var toCandidateLimit = (limit) => Math.min(Math.max(limit * CANDIDATE_MULTIPLIER, MIN_CANDIDATES), MAX_CANDIDATES);
|
|
20302
|
-
var toFtsQuery = (
|
|
20258
|
+
var toFtsQuery = (terms) => terms.map(toFtsTerm).join(" OR ");
|
|
20259
|
+
var toFtsTerm = (term) => {
|
|
20260
|
+
const escaped = term.replaceAll('"', '""');
|
|
20261
|
+
if (term.includes(" ")) {
|
|
20262
|
+
return `"${escaped}"`;
|
|
20263
|
+
}
|
|
20264
|
+
return `"${escaped}"*`;
|
|
20265
|
+
};
|
|
20303
20266
|
|
|
20304
20267
|
// src/index.ts
|
|
20305
20268
|
var { databasePath } = resolveConfig();
|
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
|
+
"version": "0.0.3",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-memory": "dist/index.js"
|
|
7
7
|
},
|
|
@@ -23,7 +23,10 @@
|
|
|
23
23
|
"build": "bun build src/index.ts --target=node --external better-sqlite3 --outfile dist/index.js --banner \"#!/usr/bin/env node\"",
|
|
24
24
|
"start": "node dist/index.js",
|
|
25
25
|
"test": "bun test",
|
|
26
|
-
"lint": "biome check --write"
|
|
26
|
+
"lint": "biome check --write && tsc --noEmit",
|
|
27
|
+
"release:patch": "npm version patch && git push origin main --follow-tags",
|
|
28
|
+
"release:minor": "npm version minor && git push origin main --follow-tags",
|
|
29
|
+
"release:major": "npm version major && git push origin main --follow-tags"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
32
|
"@biomejs/biome": "2.4.6",
|