@vheins/local-memory-mcp 0.4.7 → 0.4.10
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/dist/capabilities.d.ts +7 -7
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +7 -7
- package/dist/capabilities.js.map +1 -1
- package/dist/completion.d.ts +25 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +127 -0
- package/dist/completion.js.map +1 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +8 -1
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/elicitation.d.ts +24 -0
- package/dist/mcp/elicitation.d.ts.map +1 -0
- package/dist/mcp/elicitation.js +13 -0
- package/dist/mcp/elicitation.js.map +1 -0
- package/dist/mcp/sampling.d.ts +69 -0
- package/dist/mcp/sampling.d.ts.map +1 -0
- package/dist/mcp/sampling.js +13 -0
- package/dist/mcp/sampling.js.map +1 -0
- package/dist/mcp/session.d.ts +28 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +106 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/prompts/registry.d.ts +55 -0
- package/dist/prompts/registry.d.ts.map +1 -1
- package/dist/prompts/registry.js +455 -7
- package/dist/prompts/registry.js.map +1 -1
- package/dist/resources/index.d.ts +55 -4
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +223 -52
- package/dist/resources/index.js.map +1 -1
- package/dist/router.d.ts +11 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +151 -34
- package/dist/router.js.map +1 -1
- package/dist/server.js +212 -13
- package/dist/server.js.map +1 -1
- package/dist/storage/sqlite.d.ts +2 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +81 -23
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tasks.bulk.test.js +43 -6
- package/dist/tasks.bulk.test.js.map +1 -1
- package/dist/tasks.e2e.test.js +30 -6
- package/dist/tasks.e2e.test.js.map +1 -1
- package/dist/tests/client.test.d.ts +2 -0
- package/dist/tests/client.test.d.ts.map +1 -0
- package/dist/tests/client.test.js +130 -0
- package/dist/tests/client.test.js.map +1 -0
- package/dist/tests/dashboard.test.d.ts +2 -0
- package/dist/tests/dashboard.test.d.ts.map +1 -0
- package/dist/tests/dashboard.test.js +370 -0
- package/dist/tests/dashboard.test.js.map +1 -0
- package/dist/tests/e2e.test.d.ts +2 -0
- package/dist/tests/e2e.test.d.ts.map +1 -0
- package/dist/tests/e2e.test.js +250 -0
- package/dist/tests/e2e.test.js.map +1 -0
- package/dist/tests/index.test.d.ts +2 -0
- package/dist/tests/index.test.d.ts.map +1 -0
- package/dist/tests/index.test.js +185 -0
- package/dist/tests/index.test.js.map +1 -0
- package/dist/tests/logger.test.d.ts +2 -0
- package/dist/tests/logger.test.d.ts.map +1 -0
- package/dist/tests/logger.test.js +104 -0
- package/dist/tests/logger.test.js.map +1 -0
- package/dist/tests/memory.bulk.test.d.ts +2 -0
- package/dist/tests/memory.bulk.test.d.ts.map +1 -0
- package/dist/tests/memory.bulk.test.js +52 -0
- package/dist/tests/memory.bulk.test.js.map +1 -0
- package/dist/tests/memory.search.test.d.ts +2 -0
- package/dist/tests/memory.search.test.d.ts.map +1 -0
- package/dist/tests/memory.search.test.js +181 -0
- package/dist/tests/memory.search.test.js.map +1 -0
- package/dist/tests/normalize.test.d.ts +2 -0
- package/dist/tests/normalize.test.d.ts.map +1 -0
- package/dist/tests/normalize.test.js +159 -0
- package/dist/tests/normalize.test.js.map +1 -0
- package/dist/tests/query-expander.test.d.ts +2 -0
- package/dist/tests/query-expander.test.d.ts.map +1 -0
- package/dist/tests/query-expander.test.js +33 -0
- package/dist/tests/query-expander.test.js.map +1 -0
- package/dist/tests/router.test.d.ts +2 -0
- package/dist/tests/router.test.d.ts.map +1 -0
- package/dist/tests/router.test.js +470 -0
- package/dist/tests/router.test.js.map +1 -0
- package/dist/tests/sqlite.test.d.ts +2 -0
- package/dist/tests/sqlite.test.d.ts.map +1 -0
- package/dist/tests/sqlite.test.js +367 -0
- package/dist/tests/sqlite.test.js.map +1 -0
- package/dist/tests/tasks-search.test.d.ts +2 -0
- package/dist/tests/tasks-search.test.d.ts.map +1 -0
- package/dist/tests/tasks-search.test.js +154 -0
- package/dist/tests/tasks-search.test.js.map +1 -0
- package/dist/tests/tasks-transition.test.d.ts +2 -0
- package/dist/tests/tasks-transition.test.d.ts.map +1 -0
- package/dist/tests/tasks-transition.test.js +174 -0
- package/dist/tests/tasks-transition.test.js.map +1 -0
- package/dist/tests/tasks.bulk.test.d.ts +2 -0
- package/dist/tests/tasks.bulk.test.d.ts.map +1 -0
- package/dist/tests/tasks.bulk.test.js +254 -0
- package/dist/tests/tasks.bulk.test.js.map +1 -0
- package/dist/tests/tasks.e2e.test.d.ts +2 -0
- package/dist/tests/tasks.e2e.test.d.ts.map +1 -0
- package/dist/tests/tasks.e2e.test.js +289 -0
- package/dist/tests/tasks.e2e.test.js.map +1 -0
- package/dist/tests/tasks.pending-limit-refined.test.d.ts +2 -0
- package/dist/tests/tasks.pending-limit-refined.test.d.ts.map +1 -0
- package/dist/tests/tasks.pending-limit-refined.test.js +72 -0
- package/dist/tests/tasks.pending-limit-refined.test.js.map +1 -0
- package/dist/tests/v2-features.test.d.ts +2 -0
- package/dist/tests/v2-features.test.d.ts.map +1 -0
- package/dist/tests/v2-features.test.js +209 -0
- package/dist/tests/v2-features.test.js.map +1 -0
- package/dist/tools/memory.bulk-delete.d.ts +1 -7
- package/dist/tools/memory.bulk-delete.d.ts.map +1 -1
- package/dist/tools/memory.bulk-delete.js +30 -8
- package/dist/tools/memory.bulk-delete.js.map +1 -1
- package/dist/tools/memory.delete.d.ts.map +1 -1
- package/dist/tools/memory.delete.js +18 -1
- package/dist/tools/memory.delete.js.map +1 -1
- package/dist/tools/memory.recap.d.ts.map +1 -1
- package/dist/tools/memory.recap.js +24 -1
- package/dist/tools/memory.recap.js.map +1 -1
- package/dist/tools/memory.search.d.ts.map +1 -1
- package/dist/tools/memory.search.js +16 -1
- package/dist/tools/memory.search.js.map +1 -1
- package/dist/tools/memory.store.d.ts.map +1 -1
- package/dist/tools/memory.store.js +31 -1
- package/dist/tools/memory.store.js.map +1 -1
- package/dist/tools/memory.summarize.d.ts.map +1 -1
- package/dist/tools/memory.summarize.js +19 -1
- package/dist/tools/memory.summarize.js.map +1 -1
- package/dist/tools/memory.synthesize.d.ts +14 -0
- package/dist/tools/memory.synthesize.d.ts.map +1 -0
- package/dist/tools/memory.synthesize.js +227 -0
- package/dist/tools/memory.synthesize.js.map +1 -0
- package/dist/tools/memory.update.d.ts.map +1 -1
- package/dist/tools/memory.update.js +19 -1
- package/dist/tools/memory.update.js.map +1 -1
- package/dist/tools/schemas.d.ts +1735 -248
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +471 -4
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/task.bulk-manage.d.ts +1 -7
- package/dist/tools/task.bulk-manage.d.ts.map +1 -1
- package/dist/tools/task.bulk-manage.js +60 -15
- package/dist/tools/task.bulk-manage.js.map +1 -1
- package/dist/tools/task.manage.d.ts +12 -28
- package/dist/tools/task.manage.d.ts.map +1 -1
- package/dist/tools/task.manage.js +198 -31
- package/dist/tools/task.manage.js.map +1 -1
- package/dist/utils/completion.d.ts +2 -0
- package/dist/utils/completion.d.ts.map +1 -0
- package/dist/utils/completion.js +28 -0
- package/dist/utils/completion.js.map +1 -0
- package/dist/utils/logger.d.ts +18 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +101 -29
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/mcp-response.d.ts +50 -0
- package/dist/utils/mcp-response.d.ts.map +1 -1
- package/dist/utils/mcp-response.js +54 -7
- package/dist/utils/mcp-response.js.map +1 -1
- package/dist/utils/pagination.d.ts +6 -0
- package/dist/utils/pagination.d.ts.map +1 -0
- package/dist/utils/pagination.js +32 -0
- package/dist/utils/pagination.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import { createRouter } from "../router.js";
|
|
3
|
+
import { SQLiteStore } from "../storage/sqlite.js";
|
|
4
|
+
import { RealVectorStore } from "../storage/vectors.js";
|
|
5
|
+
// Global configuration for heavy AI tests
|
|
6
|
+
vi.setConfig({ testTimeout: 90000 });
|
|
7
|
+
describe("MCP Local Memory - High-Complexity E2E Scenarios", () => {
|
|
8
|
+
let db;
|
|
9
|
+
let vectors;
|
|
10
|
+
let router;
|
|
11
|
+
const REPO = "enterprise-app-v2";
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
db = new SQLiteStore(":memory:");
|
|
14
|
+
vectors = new RealVectorStore(db);
|
|
15
|
+
router = createRouter(db, vectors);
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* SCENARIO 1: Semantic Precision & Ranking
|
|
19
|
+
* Test if the system can distinguish between closely related topics.
|
|
20
|
+
*/
|
|
21
|
+
it("should maintain high precision ranking among related but distinct technical concepts", async () => {
|
|
22
|
+
const memories = [
|
|
23
|
+
{ title: "Primary Database", content: "We use SQLite for local persistence to ensure local-first capability." },
|
|
24
|
+
{ title: "Cache Layer", content: "Redis is used for session management and fast lookups." },
|
|
25
|
+
{ title: "Database Migration", content: "Run 'npm run migrate' to update the SQLite schema safely." },
|
|
26
|
+
{ title: "Backup Policy", content: "Hourly snapshots of the .db file are stored in the backups folder." },
|
|
27
|
+
{ title: "External API Integration", content: "The system connects to Postgres only for reporting services." }
|
|
28
|
+
];
|
|
29
|
+
for (const m of memories) {
|
|
30
|
+
await router("tools/call", {
|
|
31
|
+
name: "memory-store",
|
|
32
|
+
arguments: {
|
|
33
|
+
type: "decision",
|
|
34
|
+
title: m.title,
|
|
35
|
+
content: m.content,
|
|
36
|
+
importance: 4,
|
|
37
|
+
scope: { repo: REPO },
|
|
38
|
+
agent: "test-agent",
|
|
39
|
+
model: "test-model"
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// QUERY: "How do I update the database?"
|
|
44
|
+
// EXPECT: "Database Migration" should be Top Match, not "Primary Database"
|
|
45
|
+
const searchRes = await router("tools/call", {
|
|
46
|
+
name: "memory-search",
|
|
47
|
+
arguments: { query: "How do I update the database schema?", repo: REPO }
|
|
48
|
+
});
|
|
49
|
+
const textOutput = searchRes.content[0].text;
|
|
50
|
+
expect(textOutput).toContain("Database Migration");
|
|
51
|
+
// Validate JSON ordering
|
|
52
|
+
const results = searchRes.data.results;
|
|
53
|
+
expect(results[0].title).toBe("Database Migration");
|
|
54
|
+
expect(results.length).toBeGreaterThan(1); // Should find related db facts too but lower
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* SCENARIO 2: The Correction & Learning Loop
|
|
58
|
+
* Agent makes a mistake -> Stores it -> Learns -> Replaces it with a pattern.
|
|
59
|
+
*/
|
|
60
|
+
it("should guide the agent away from past mistakes using supersedes and status", async () => {
|
|
61
|
+
// 1. Store a mistake
|
|
62
|
+
const mistakeRes = await router("tools/call", {
|
|
63
|
+
name: "memory-store",
|
|
64
|
+
arguments: {
|
|
65
|
+
type: "mistake",
|
|
66
|
+
title: "Large File Upload Failure",
|
|
67
|
+
content: "Don't use fs.readFileSync for large files, it causes OOM errors.",
|
|
68
|
+
importance: 5,
|
|
69
|
+
scope: { repo: REPO },
|
|
70
|
+
agent: "test-agent",
|
|
71
|
+
model: "test-model"
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const mistakeId = mistakeRes.data.id;
|
|
75
|
+
// 2. Store the correct pattern that replaces the mistake
|
|
76
|
+
await router("tools/call", {
|
|
77
|
+
name: "memory-store",
|
|
78
|
+
arguments: {
|
|
79
|
+
type: "pattern",
|
|
80
|
+
title: "Streaming File Upload",
|
|
81
|
+
content: "Always use streams (fs.createReadStream) for files > 10MB.",
|
|
82
|
+
importance: 5,
|
|
83
|
+
scope: { repo: REPO },
|
|
84
|
+
agent: "test-agent",
|
|
85
|
+
model: "test-model",
|
|
86
|
+
supersedes: mistakeId
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// 3. Search for "file upload"
|
|
90
|
+
const searchRes = await router("tools/call", {
|
|
91
|
+
name: "memory-search",
|
|
92
|
+
arguments: { query: "How to handle file uploads?", repo: REPO }
|
|
93
|
+
});
|
|
94
|
+
// EXPECT: Mistake is archived and NOT in search results by default
|
|
95
|
+
expect(searchRes.content[0].text).toContain("Streaming File Upload");
|
|
96
|
+
expect(searchRes.content[0].text).not.toContain("Large File Upload Failure");
|
|
97
|
+
const results = searchRes.data.results;
|
|
98
|
+
expect(results.some((r) => r.id === mistakeId)).toBe(false);
|
|
99
|
+
// 4. Audit: Verify we can still find the mistake if we EXPLICITLY ask for archived
|
|
100
|
+
const auditRes = await router("tools/call", {
|
|
101
|
+
name: "memory-search",
|
|
102
|
+
arguments: { query: "file upload", repo: REPO, include_archived: true }
|
|
103
|
+
});
|
|
104
|
+
expect(auditRes.data.results.some((r) => r.id === mistakeId)).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* SCENARIO 3: Workspace-Aware Context Nuance
|
|
108
|
+
* Boost based on folder depth and language extensions.
|
|
109
|
+
*/
|
|
110
|
+
it("should provide relevant boost when working in deep subdirectories", async () => {
|
|
111
|
+
// Memory A: Global
|
|
112
|
+
await router("tools/call", {
|
|
113
|
+
name: "memory-store",
|
|
114
|
+
arguments: {
|
|
115
|
+
type: "code_fact",
|
|
116
|
+
title: "Global Logging",
|
|
117
|
+
content: "Use logger.info for all modules.",
|
|
118
|
+
importance: 2,
|
|
119
|
+
scope: { repo: REPO },
|
|
120
|
+
agent: "test-agent",
|
|
121
|
+
model: "test-model"
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Memory B: Auth Specific
|
|
125
|
+
await router("tools/call", {
|
|
126
|
+
name: "memory-store",
|
|
127
|
+
arguments: {
|
|
128
|
+
type: "code_fact",
|
|
129
|
+
title: "Auth Security Audit",
|
|
130
|
+
content: "Auth module requires PII masking in logs.",
|
|
131
|
+
importance: 5,
|
|
132
|
+
scope: { repo: REPO, folder: "src/auth" },
|
|
133
|
+
agent: "test-agent",
|
|
134
|
+
model: "test-model"
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// Working in a deep auth file
|
|
138
|
+
const searchRes = await router("tools/call", {
|
|
139
|
+
name: "memory-search",
|
|
140
|
+
arguments: {
|
|
141
|
+
query: "How to log data?",
|
|
142
|
+
repo: REPO,
|
|
143
|
+
current_file_path: "src/auth/services/ldap/provider.ts"
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
// EXPECT: Auth Specific memory should be the Top Match despite the query being generic "log"
|
|
147
|
+
expect(searchRes.content[0].text).toContain("Auth Security Audit");
|
|
148
|
+
});
|
|
149
|
+
/**
|
|
150
|
+
* SCENARIO 4: Bulk Operations & Pagination Integrity
|
|
151
|
+
*/
|
|
152
|
+
it("should handle large volumes of memories and maintain pagination integrity", async () => {
|
|
153
|
+
// Store 15 memories about different microservices
|
|
154
|
+
for (let i = 1; i <= 15; i++) {
|
|
155
|
+
await router("tools/call", {
|
|
156
|
+
name: "memory-store",
|
|
157
|
+
arguments: {
|
|
158
|
+
type: "code_fact",
|
|
159
|
+
title: `Service ${i} Specs`,
|
|
160
|
+
content: `Documentation for microservice number ${i} in our mesh network.`,
|
|
161
|
+
importance: 3,
|
|
162
|
+
scope: { repo: "cloud-infra" },
|
|
163
|
+
agent: "test-agent",
|
|
164
|
+
model: "test-model"
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// Call memory-recap (which uses pagination)
|
|
169
|
+
const recapRes = await router("tools/call", {
|
|
170
|
+
name: "memory-recap",
|
|
171
|
+
arguments: { repo: "cloud-infra", limit: 5, offset: 0 }
|
|
172
|
+
});
|
|
173
|
+
// Based on memory-recap implementation
|
|
174
|
+
expect(recapRes.content[0].text).toContain("cloud-infra");
|
|
175
|
+
// Check Resource Pagination via URI
|
|
176
|
+
const resourceRes = await router("resources/read", {
|
|
177
|
+
uri: "memory://index?repo=cloud-infra"
|
|
178
|
+
});
|
|
179
|
+
const entries = JSON.parse(resourceRes.contents[0].text);
|
|
180
|
+
expect(entries.length).toBeLessThanOrEqual(20); // Default limit in resource.read is 20
|
|
181
|
+
});
|
|
182
|
+
/**
|
|
183
|
+
* SCENARIO 5: Semantic Conflict Denial (Deep Overlap)
|
|
184
|
+
*/
|
|
185
|
+
it("should strictly deny near-duplicate decisions to prevent prompt bloat", async () => {
|
|
186
|
+
const originalContent = "We use TailwindCSS for styling all components.";
|
|
187
|
+
await router("tools/call", {
|
|
188
|
+
name: "memory-store",
|
|
189
|
+
arguments: {
|
|
190
|
+
type: "decision",
|
|
191
|
+
title: "Styling Standard",
|
|
192
|
+
content: originalContent,
|
|
193
|
+
importance: 3,
|
|
194
|
+
scope: { repo: REPO },
|
|
195
|
+
agent: "test-agent",
|
|
196
|
+
model: "test-model"
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// Try to store almost the same thing with a different title
|
|
200
|
+
const duplicateRes = await router("tools/call", {
|
|
201
|
+
name: "memory-store",
|
|
202
|
+
arguments: {
|
|
203
|
+
type: "decision",
|
|
204
|
+
title: "CSS Rule",
|
|
205
|
+
content: "Use Tailwind CSS for styling our UI components.", // Subtly different but semantically identical
|
|
206
|
+
importance: 3,
|
|
207
|
+
scope: { repo: REPO },
|
|
208
|
+
agent: "test-agent",
|
|
209
|
+
model: "test-model"
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
expect(duplicateRes.content[0].text).toContain("conflict");
|
|
213
|
+
expect(duplicateRes.content[0].text).toContain("Hint:");
|
|
214
|
+
expect(duplicateRes.content[0].text).toContain("memory-delete");
|
|
215
|
+
expect(db.getTotalCount(REPO)).toBe(1); // Should still be 1
|
|
216
|
+
});
|
|
217
|
+
/**
|
|
218
|
+
* SCENARIO 6: Cross-Project Knowledge Sharing (Tech-Stack Affinity)
|
|
219
|
+
* Repository A has Filament knowledge. Repository B (new) should access it via tags.
|
|
220
|
+
*/
|
|
221
|
+
it("should share knowledge across projects using tech-stack affinity tags", async () => {
|
|
222
|
+
// 1. Store Filament best practice in Repo A
|
|
223
|
+
await router("tools/call", {
|
|
224
|
+
name: "memory-store",
|
|
225
|
+
arguments: {
|
|
226
|
+
type: "pattern",
|
|
227
|
+
title: "Filament Custom Action",
|
|
228
|
+
content: "Always use ->requiresConfirmation() for destructive actions in Filament.",
|
|
229
|
+
importance: 5,
|
|
230
|
+
scope: { repo: "project-a" },
|
|
231
|
+
agent: "test-agent",
|
|
232
|
+
model: "test-model",
|
|
233
|
+
tags: ["filament", "laravel"]
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
// 2. Search in Repo B (which is empty) with "filament" tag
|
|
237
|
+
const searchRes = await router("tools/call", {
|
|
238
|
+
name: "memory-search",
|
|
239
|
+
arguments: {
|
|
240
|
+
query: "how to make safe actions?",
|
|
241
|
+
repo: "project-b",
|
|
242
|
+
current_tags: ["filament"]
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
// EXPECT: Should find the "Filament Custom Action" from Project A
|
|
246
|
+
expect(searchRes.content[0].text).toContain("Filament Custom Action");
|
|
247
|
+
expect(searchRes.data.results[0].scope.repo).toBe("project-a");
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
//# sourceMappingURL=e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e.test.js","sourceRoot":"","sources":["../../src/tests/e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/tests/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Feature: memory-mcp-optimization, Property 19: memory://index filter repo
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import * as fc from "fast-check";
|
|
4
|
+
import { SQLiteStore } from "../storage/sqlite.js";
|
|
5
|
+
import { readResource, listResourceTemplates, listResources } from "../resources/index.js";
|
|
6
|
+
import { createSessionContext, updateSessionRoots } from "../mcp/session.js";
|
|
7
|
+
function makeEntry(id, repo) {
|
|
8
|
+
return {
|
|
9
|
+
id,
|
|
10
|
+
type: "code_fact",
|
|
11
|
+
title: `Memory ${id}`,
|
|
12
|
+
content: `Content for memory ${id} in repo ${repo}`,
|
|
13
|
+
importance: 3,
|
|
14
|
+
agent: "test-agent",
|
|
15
|
+
role: "unknown",
|
|
16
|
+
model: "test-model",
|
|
17
|
+
scope: { repo },
|
|
18
|
+
created_at: new Date().toISOString(),
|
|
19
|
+
updated_at: new Date().toISOString(),
|
|
20
|
+
completed_at: null,
|
|
21
|
+
hit_count: 0,
|
|
22
|
+
recall_count: 0,
|
|
23
|
+
last_used_at: null,
|
|
24
|
+
expires_at: null,
|
|
25
|
+
supersedes: null,
|
|
26
|
+
status: "active",
|
|
27
|
+
tags: [],
|
|
28
|
+
metadata: {},
|
|
29
|
+
is_global: false,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
describe("readResource memory://index", () => {
|
|
33
|
+
it("returns recent entries when no repo filter", () => {
|
|
34
|
+
const db = new SQLiteStore(":memory:");
|
|
35
|
+
db.insert(makeEntry("id-1", "repo-a"));
|
|
36
|
+
db.insert(makeEntry("id-2", "repo-b"));
|
|
37
|
+
const result = readResource("memory://index", db);
|
|
38
|
+
const entries = JSON.parse(result.contents[0].text);
|
|
39
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
40
|
+
db.close();
|
|
41
|
+
});
|
|
42
|
+
it("returns only entries for the specified repo when ?repo=X is given", () => {
|
|
43
|
+
const db = new SQLiteStore(":memory:");
|
|
44
|
+
db.insert(makeEntry("id-a1", "repo-alpha"));
|
|
45
|
+
db.insert(makeEntry("id-a2", "repo-alpha"));
|
|
46
|
+
db.insert(makeEntry("id-b1", "repo-beta"));
|
|
47
|
+
const result = readResource("memory://index?repo=repo-alpha", db);
|
|
48
|
+
const entries = JSON.parse(result.contents[0].text);
|
|
49
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
expect(entry.scope.repo).toBe("repo-alpha");
|
|
52
|
+
}
|
|
53
|
+
db.close();
|
|
54
|
+
});
|
|
55
|
+
it("returns empty array when repo has no entries", () => {
|
|
56
|
+
const db = new SQLiteStore(":memory:");
|
|
57
|
+
db.insert(makeEntry("id-1", "repo-a"));
|
|
58
|
+
const result = readResource("memory://index?repo=nonexistent", db);
|
|
59
|
+
const entries = JSON.parse(result.contents[0].text);
|
|
60
|
+
expect(entries).toEqual([]);
|
|
61
|
+
db.close();
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Property 19: memory://index dengan filter repo mengembalikan subset yang benar
|
|
65
|
+
* Validates: Requirements 19.1, 19.3
|
|
66
|
+
*/
|
|
67
|
+
it("Property 19: all returned entries have repo === queried repo", () => {
|
|
68
|
+
fc.assert(fc.property(
|
|
69
|
+
// Generate 2-4 distinct repo names
|
|
70
|
+
fc.uniqueArray(fc.stringMatching(/^[a-z][a-z0-9-]{2,8}$/), { minLength: 2, maxLength: 4 }),
|
|
71
|
+
// Generate 1-5 memories per repo
|
|
72
|
+
fc.integer({ min: 1, max: 5 }), (repos, memoriesPerRepo) => {
|
|
73
|
+
const db = new SQLiteStore(":memory:");
|
|
74
|
+
// Insert memories for each repo
|
|
75
|
+
let counter = 0;
|
|
76
|
+
for (const repo of repos) {
|
|
77
|
+
for (let i = 0; i < memoriesPerRepo; i++) {
|
|
78
|
+
db.insert(makeEntry(`id-${counter++}`, repo));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Query with the first repo as filter
|
|
82
|
+
const targetRepo = repos[0];
|
|
83
|
+
const result = readResource(`memory://index?repo=${targetRepo}`, db);
|
|
84
|
+
const entries = JSON.parse(result.contents[0].text);
|
|
85
|
+
// All returned entries must belong to targetRepo
|
|
86
|
+
const allMatch = entries.every((e) => e.scope.repo === targetRepo);
|
|
87
|
+
db.close();
|
|
88
|
+
return allMatch;
|
|
89
|
+
}), { numRuns: 100 });
|
|
90
|
+
});
|
|
91
|
+
it("Property 19 (no filter): returns entries from all repos", () => {
|
|
92
|
+
fc.assert(fc.property(fc.uniqueArray(fc.stringMatching(/^[a-z][a-z0-9-]{2,8}$/), { minLength: 2, maxLength: 3 }), (repos) => {
|
|
93
|
+
const db = new SQLiteStore(":memory:");
|
|
94
|
+
for (const repo of repos) {
|
|
95
|
+
db.insert(makeEntry(`id-${repo}`, repo));
|
|
96
|
+
}
|
|
97
|
+
const result = readResource("memory://index", db);
|
|
98
|
+
const entries = JSON.parse(result.contents[0].text);
|
|
99
|
+
// Without filter, should return entries (listRecent returns id/type/repo)
|
|
100
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
101
|
+
db.close();
|
|
102
|
+
return true;
|
|
103
|
+
}), { numRuns: 100 });
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe("MCP resource templates and session resources", () => {
|
|
107
|
+
it("supports resource list pagination with nextCursor", () => {
|
|
108
|
+
const session = createSessionContext();
|
|
109
|
+
const firstPage = listResources(session, { limit: 2 });
|
|
110
|
+
const secondPage = listResources(session, { limit: 2, cursor: firstPage.nextCursor });
|
|
111
|
+
expect(firstPage.resources).toHaveLength(2);
|
|
112
|
+
expect(firstPage.nextCursor).toBeTruthy();
|
|
113
|
+
expect(secondPage.resources).toHaveLength(1);
|
|
114
|
+
});
|
|
115
|
+
it("supports resource template pagination with nextCursor", () => {
|
|
116
|
+
const firstPage = listResourceTemplates({ limit: 2 });
|
|
117
|
+
const secondPage = listResourceTemplates({ limit: 2, cursor: firstPage.nextCursor });
|
|
118
|
+
const secondTemplates = secondPage.resourceTemplates;
|
|
119
|
+
expect(firstPage.resourceTemplates).toHaveLength(2);
|
|
120
|
+
expect(firstPage.nextCursor).toBeTruthy();
|
|
121
|
+
expect(secondTemplates.length).toBeGreaterThan(0);
|
|
122
|
+
});
|
|
123
|
+
it("rejects invalid cursors for resources/list with MCP invalid params error", () => {
|
|
124
|
+
const session = createSessionContext();
|
|
125
|
+
expect(() => listResources(session, { cursor: "%%%not-base64%%%" })).toThrowError(/Invalid cursor/);
|
|
126
|
+
try {
|
|
127
|
+
listResources(session, { cursor: "%%%not-base64%%%" });
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
expect(error.code).toBe(-32602);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
it("rejects invalid cursors for resources/templates/list with MCP invalid params error", () => {
|
|
134
|
+
expect(() => listResourceTemplates({ cursor: "%%%not-base64%%%" })).toThrowError(/Invalid cursor/);
|
|
135
|
+
try {
|
|
136
|
+
listResourceTemplates({ cursor: "%%%not-base64%%%" });
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
expect(error.code).toBe(-32602);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
it("lists parameterized resources via resources/templates/list", () => {
|
|
143
|
+
const result = listResourceTemplates();
|
|
144
|
+
const templates = result.resourceTemplates.map((entry) => entry.uriTemplate);
|
|
145
|
+
expect(templates).toContain("memory://index?repo={repo}");
|
|
146
|
+
expect(templates).toContain("tasks://current?repo={repo}");
|
|
147
|
+
expect(templates).toContain("memory://search/{base64_query}?repo={repo}");
|
|
148
|
+
});
|
|
149
|
+
it("returns active session roots as a concrete resource", () => {
|
|
150
|
+
const db = new SQLiteStore(":memory:");
|
|
151
|
+
const session = createSessionContext();
|
|
152
|
+
updateSessionRoots(session, [
|
|
153
|
+
{ uri: "file:///workspace/project-a", name: "project-a" },
|
|
154
|
+
{ uri: "file:///workspace/project-b" },
|
|
155
|
+
]);
|
|
156
|
+
const result = readResource("session://roots", db, session);
|
|
157
|
+
const payload = JSON.parse(result.contents[0].text);
|
|
158
|
+
expect(payload.roots).toHaveLength(2);
|
|
159
|
+
expect(payload.roots[0].name).toBe("project-a");
|
|
160
|
+
db.close();
|
|
161
|
+
});
|
|
162
|
+
it("adds annotations and size metadata to concrete resource content", () => {
|
|
163
|
+
const db = new SQLiteStore(":memory:");
|
|
164
|
+
const result = readResource("memory://summary/repo-a", db);
|
|
165
|
+
const content = result.contents[0];
|
|
166
|
+
expect(content.annotations.priority).toBeGreaterThan(0.5);
|
|
167
|
+
expect(content.size).toBeGreaterThan(0);
|
|
168
|
+
db.close();
|
|
169
|
+
});
|
|
170
|
+
it("throws MCP resource not found error code for unknown resources", () => {
|
|
171
|
+
const db = new SQLiteStore(":memory:");
|
|
172
|
+
expect(() => readResource("memory://missing/resource", db)).toThrowError(/Unknown resource URI/);
|
|
173
|
+
try {
|
|
174
|
+
readResource("memory://missing/resource", db);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
expect(error.code).toBe(-32002);
|
|
178
|
+
expect(error.data.uri).toBe("memory://missing/resource");
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
db.close();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/tests/index.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../../src/tests/logger.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Feature: memory-mcp-optimization
|
|
2
|
+
// Property 20: StructuredLogger output adalah JSON valid dengan field wajib
|
|
3
|
+
// Validates: Requirements 21.1, 21.2
|
|
4
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
5
|
+
import * as fc from "fast-check";
|
|
6
|
+
describe("Property 20: StructuredLogger output adalah JSON valid dengan field wajib", () => {
|
|
7
|
+
let stderrOutput = [];
|
|
8
|
+
let stderrSpy;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
stderrOutput = [];
|
|
11
|
+
stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation((chunk) => {
|
|
12
|
+
stderrOutput.push(typeof chunk === "string" ? chunk : chunk.toString());
|
|
13
|
+
return true;
|
|
14
|
+
});
|
|
15
|
+
// Reset LOG_LEVEL to ensure all levels are logged
|
|
16
|
+
process.env.LOG_LEVEL = "debug";
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
stderrSpy.mockRestore();
|
|
20
|
+
delete process.env.LOG_LEVEL;
|
|
21
|
+
});
|
|
22
|
+
it("each log call produces valid JSON output with required fields", async () => {
|
|
23
|
+
// Re-import logger after setting LOG_LEVEL
|
|
24
|
+
const { logger } = await import("../utils/logger.js?t=" + Date.now());
|
|
25
|
+
fc.assert(fc.property(fc.oneof(fc.constant("debug"), fc.constant("info"), fc.constant("warning"), fc.constant("error")), fc.string({ minLength: 1, maxLength: 100 }), (level, message) => {
|
|
26
|
+
stderrOutput = [];
|
|
27
|
+
logger[level](message);
|
|
28
|
+
expect(stderrOutput.length).toBeGreaterThanOrEqual(1);
|
|
29
|
+
const lastOutput = stderrOutput[stderrOutput.length - 1];
|
|
30
|
+
// Must be text with timestamp, level, and message
|
|
31
|
+
expect(lastOutput).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z \[.*?\] /);
|
|
32
|
+
// Extract parts
|
|
33
|
+
const match = lastOutput.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z) \[\s*(.*?)\s*\] ([\s\S]*)$/);
|
|
34
|
+
expect(match).not.toBeNull();
|
|
35
|
+
if (match) {
|
|
36
|
+
const [, timestamp, parsedLevel, messageWithCtx] = match;
|
|
37
|
+
// level must match
|
|
38
|
+
expect(parsedLevel.toLowerCase()).toBe(level);
|
|
39
|
+
// message must start with the original message
|
|
40
|
+
expect(messageWithCtx.startsWith(message)).toBe(true);
|
|
41
|
+
// timestamp must be ISO 8601
|
|
42
|
+
const ts = new Date(timestamp);
|
|
43
|
+
expect(isNaN(ts.getTime())).toBe(false);
|
|
44
|
+
}
|
|
45
|
+
}), { numRuns: 100 });
|
|
46
|
+
});
|
|
47
|
+
it("log output with context includes context field", async () => {
|
|
48
|
+
const { logger } = await import("../utils/logger.js?t=" + Date.now() + "ctx");
|
|
49
|
+
stderrOutput = [];
|
|
50
|
+
logger.info("test message", { key: "value", count: 42 });
|
|
51
|
+
expect(stderrOutput.length).toBeGreaterThanOrEqual(1);
|
|
52
|
+
const lastOutput = stderrOutput[stderrOutput.length - 1];
|
|
53
|
+
expect(lastOutput).toContain('{"key":"value","count":42}');
|
|
54
|
+
});
|
|
55
|
+
it("log output without context does not include context field", async () => {
|
|
56
|
+
const { logger } = await import("../utils/logger.js?t=" + Date.now() + "noctx");
|
|
57
|
+
stderrOutput = [];
|
|
58
|
+
logger.info("no context message");
|
|
59
|
+
expect(stderrOutput.length).toBeGreaterThanOrEqual(1);
|
|
60
|
+
const lastOutput = stderrOutput[stderrOutput.length - 1];
|
|
61
|
+
expect(lastOutput.trim()).not.toMatch(/\{.*\}/);
|
|
62
|
+
});
|
|
63
|
+
it("all four log levels produce valid JSON with correct level field", async () => {
|
|
64
|
+
const { logger } = await import("../utils/logger.js?t=" + Date.now() + "levels");
|
|
65
|
+
const levels = ["debug", "info", "warning", "error"];
|
|
66
|
+
for (const level of levels) {
|
|
67
|
+
stderrOutput = [];
|
|
68
|
+
if (level === "warning") {
|
|
69
|
+
logger.warning(`test ${level} message`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
logger[level](`test ${level} message`);
|
|
73
|
+
}
|
|
74
|
+
expect(stderrOutput.length).toBeGreaterThanOrEqual(1);
|
|
75
|
+
const lastOutput = stderrOutput[stderrOutput.length - 1];
|
|
76
|
+
expect(lastOutput).toMatch(new RegExp(`\\[\\s*${level.toUpperCase()}\\s*\\]`));
|
|
77
|
+
expect(lastOutput).toContain(`test ${level} message`);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
it("warn alias normalizes to warning", async () => {
|
|
81
|
+
const { logger } = await import("../utils/logger.js?t=" + Date.now() + "warnalias");
|
|
82
|
+
stderrOutput = [];
|
|
83
|
+
logger.warn("legacy warning");
|
|
84
|
+
expect(stderrOutput.length).toBeGreaterThanOrEqual(1);
|
|
85
|
+
const lastOutput = stderrOutput[stderrOutput.length - 1];
|
|
86
|
+
expect(lastOutput).toMatch(/\[\s*WARNING\s*\]/);
|
|
87
|
+
});
|
|
88
|
+
it("emits structured payloads to registered log sinks", async () => {
|
|
89
|
+
const module = await import("../utils/logger.js?t=" + Date.now() + "sink");
|
|
90
|
+
const sink = vi.fn();
|
|
91
|
+
module.addLogSink(sink);
|
|
92
|
+
module.logger.notice("[Server] Configuration updated", { source: "test" });
|
|
93
|
+
expect(sink).toHaveBeenCalledWith({
|
|
94
|
+
level: "notice",
|
|
95
|
+
logger: "server",
|
|
96
|
+
data: {
|
|
97
|
+
message: "[Server] Configuration updated",
|
|
98
|
+
source: "test",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
module.clearLogSinks();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=logger.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.test.js","sourceRoot":"","sources":["../../src/tests/logger.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.bulk.test.d.ts","sourceRoot":"","sources":["../../src/tests/memory.bulk.test.ts"],"names":[],"mappings":""}
|