mcp-memory-keeper 0.10.0

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 (98) hide show
  1. package/CHANGELOG.md +433 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1051 -0
  4. package/bin/mcp-memory-keeper +52 -0
  5. package/dist/__tests__/helpers/database-test-helper.js +160 -0
  6. package/dist/__tests__/helpers/test-server.js +92 -0
  7. package/dist/__tests__/integration/advanced-features.test.js +614 -0
  8. package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
  9. package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
  10. package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
  11. package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
  12. package/dist/__tests__/integration/channels.test.js +376 -0
  13. package/dist/__tests__/integration/checkpoint.test.js +251 -0
  14. package/dist/__tests__/integration/concurrent-access.test.js +190 -0
  15. package/dist/__tests__/integration/context-operations.test.js +243 -0
  16. package/dist/__tests__/integration/contextDiff.test.js +852 -0
  17. package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
  18. package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
  19. package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
  20. package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
  21. package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
  22. package/dist/__tests__/integration/contextSearch.test.js +938 -0
  23. package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
  24. package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
  25. package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
  26. package/dist/__tests__/integration/cross-session-sharing.test.js +302 -0
  27. package/dist/__tests__/integration/database-initialization.test.js +134 -0
  28. package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
  29. package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
  30. package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
  31. package/dist/__tests__/integration/error-cases.test.js +407 -0
  32. package/dist/__tests__/integration/export-import.test.js +367 -0
  33. package/dist/__tests__/integration/feature-flags.test.js +542 -0
  34. package/dist/__tests__/integration/file-operations.test.js +264 -0
  35. package/dist/__tests__/integration/git-integration.test.js +237 -0
  36. package/dist/__tests__/integration/index-tools.test.js +496 -0
  37. package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
  38. package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
  39. package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
  40. package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
  41. package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
  42. package/dist/__tests__/integration/migrations.test.js +528 -0
  43. package/dist/__tests__/integration/multi-agent.test.js +546 -0
  44. package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
  45. package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
  46. package/dist/__tests__/integration/project-directory.test.js +283 -0
  47. package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
  48. package/dist/__tests__/integration/retention.test.js +513 -0
  49. package/dist/__tests__/integration/search.test.js +333 -0
  50. package/dist/__tests__/integration/semantic-search.test.js +266 -0
  51. package/dist/__tests__/integration/server-initialization.test.js +307 -0
  52. package/dist/__tests__/integration/session-management.test.js +219 -0
  53. package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
  54. package/dist/__tests__/integration/smart-compaction.test.js +230 -0
  55. package/dist/__tests__/integration/summarization.test.js +308 -0
  56. package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
  57. package/dist/__tests__/security/input-validation.test.js +115 -0
  58. package/dist/__tests__/utils/agents.test.js +473 -0
  59. package/dist/__tests__/utils/database.test.js +177 -0
  60. package/dist/__tests__/utils/git.test.js +122 -0
  61. package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
  62. package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
  63. package/dist/__tests__/utils/project-directory-messages.test.js +188 -0
  64. package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
  65. package/dist/__tests__/utils/validation.test.js +200 -0
  66. package/dist/__tests__/utils/vector-store.test.js +231 -0
  67. package/dist/handlers/contextWatchHandlers.js +206 -0
  68. package/dist/index.js +4310 -0
  69. package/dist/index.phase1.backup.js +410 -0
  70. package/dist/index.phase2.backup.js +704 -0
  71. package/dist/migrations/003_add_channels.js +174 -0
  72. package/dist/migrations/004_add_context_watch.js +151 -0
  73. package/dist/migrations/005_add_context_watch.js +98 -0
  74. package/dist/migrations/simplify-sharing.js +117 -0
  75. package/dist/repositories/BaseRepository.js +30 -0
  76. package/dist/repositories/CheckpointRepository.js +140 -0
  77. package/dist/repositories/ContextRepository.js +1873 -0
  78. package/dist/repositories/FileRepository.js +104 -0
  79. package/dist/repositories/RepositoryManager.js +62 -0
  80. package/dist/repositories/SessionRepository.js +66 -0
  81. package/dist/repositories/WatcherRepository.js +252 -0
  82. package/dist/repositories/index.js +15 -0
  83. package/dist/server.js +384 -0
  84. package/dist/test-helpers/database-helper.js +128 -0
  85. package/dist/types/entities.js +3 -0
  86. package/dist/utils/agents.js +791 -0
  87. package/dist/utils/channels.js +150 -0
  88. package/dist/utils/database.js +731 -0
  89. package/dist/utils/feature-flags.js +476 -0
  90. package/dist/utils/git.js +145 -0
  91. package/dist/utils/knowledge-graph.js +264 -0
  92. package/dist/utils/migrationHealthCheck.js +373 -0
  93. package/dist/utils/migrations.js +452 -0
  94. package/dist/utils/retention.js +460 -0
  95. package/dist/utils/timestamps.js +112 -0
  96. package/dist/utils/validation.js +296 -0
  97. package/dist/utils/vector-store.js +247 -0
  98. package/package.json +84 -0
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const database_1 = require("../../utils/database");
37
+ const os = __importStar(require("os"));
38
+ const path = __importStar(require("path"));
39
+ const fs = __importStar(require("fs"));
40
+ const uuid_1 = require("uuid");
41
+ describe('Search Integration Tests', () => {
42
+ let dbManager;
43
+ let tempDbPath;
44
+ let db;
45
+ let testSessionId;
46
+ beforeEach(() => {
47
+ tempDbPath = path.join(os.tmpdir(), `test-search-${Date.now()}.db`);
48
+ dbManager = new database_1.DatabaseManager({
49
+ filename: tempDbPath,
50
+ maxSize: 10 * 1024 * 1024,
51
+ walMode: true,
52
+ });
53
+ db = dbManager.getDatabase();
54
+ // Create test session
55
+ testSessionId = (0, uuid_1.v4)();
56
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Search Test Session');
57
+ });
58
+ afterEach(() => {
59
+ dbManager.close();
60
+ try {
61
+ fs.unlinkSync(tempDbPath);
62
+ fs.unlinkSync(`${tempDbPath}-wal`);
63
+ fs.unlinkSync(`${tempDbPath}-shm`);
64
+ }
65
+ catch (_e) {
66
+ // Ignore
67
+ }
68
+ });
69
+ describe('context_search', () => {
70
+ beforeEach(() => {
71
+ // Create test session if not exists
72
+ const session = db.prepare('SELECT id FROM sessions WHERE id = ?').get(testSessionId);
73
+ if (!session) {
74
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Search Test Session');
75
+ }
76
+ // Add diverse test data
77
+ const items = [
78
+ {
79
+ key: 'auth_bug',
80
+ value: 'Fixed authentication bug in login flow',
81
+ category: 'task',
82
+ priority: 'high',
83
+ },
84
+ {
85
+ key: 'auth_decision',
86
+ value: 'Decided to use JWT for authentication',
87
+ category: 'decision',
88
+ priority: 'high',
89
+ },
90
+ {
91
+ key: 'api_design',
92
+ value: 'Design REST API endpoints for user management',
93
+ category: 'task',
94
+ priority: 'normal',
95
+ },
96
+ {
97
+ key: 'database_choice',
98
+ value: 'Selected PostgreSQL for user data storage',
99
+ category: 'decision',
100
+ priority: 'normal',
101
+ },
102
+ {
103
+ key: 'security_note',
104
+ value: 'Remember to implement rate limiting on auth endpoints',
105
+ category: 'note',
106
+ priority: 'high',
107
+ },
108
+ {
109
+ key: 'performance',
110
+ value: 'Optimize database queries for better performance',
111
+ category: 'task',
112
+ priority: 'low',
113
+ },
114
+ ];
115
+ items.forEach(item => {
116
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, category, priority) VALUES (?, ?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, item.key, item.value, item.category, item.priority);
117
+ });
118
+ });
119
+ it('should search by value content', () => {
120
+ // Search for 'authentication'
121
+ const results = db
122
+ .prepare(`SELECT * FROM context_items
123
+ WHERE session_id = ?
124
+ AND value LIKE ?
125
+ ORDER BY priority DESC, created_at DESC`)
126
+ .all(testSessionId, '%authentication%');
127
+ expect(results).toHaveLength(2);
128
+ expect(results[0].key).toBe('auth_bug'); // High priority first
129
+ expect(results[1].key).toBe('auth_decision');
130
+ });
131
+ it('should search by key pattern', () => {
132
+ // Search for keys starting with 'auth'
133
+ const results = db
134
+ .prepare(`SELECT * FROM context_items
135
+ WHERE session_id = ?
136
+ AND key LIKE ?
137
+ ORDER BY created_at DESC`)
138
+ .all(testSessionId, 'auth%');
139
+ expect(results).toHaveLength(2);
140
+ expect(results.map((r) => r.key)).toContain('auth_bug');
141
+ expect(results.map((r) => r.key)).toContain('auth_decision');
142
+ });
143
+ it('should filter by category', () => {
144
+ // Search for tasks only
145
+ const results = db
146
+ .prepare(`SELECT * FROM context_items
147
+ WHERE session_id = ?
148
+ AND category = ?
149
+ ORDER BY
150
+ CASE priority
151
+ WHEN 'critical' THEN 1
152
+ WHEN 'high' THEN 2
153
+ WHEN 'normal' THEN 3
154
+ WHEN 'low' THEN 4
155
+ END`)
156
+ .all(testSessionId, 'task');
157
+ expect(results).toHaveLength(3);
158
+ expect(results[0].key).toBe('auth_bug'); // High priority
159
+ expect(results[2].key).toBe('performance'); // Low priority
160
+ });
161
+ it('should combine search criteria', () => {
162
+ // Search for high priority items containing 'auth'
163
+ const results = db
164
+ .prepare(`SELECT * FROM context_items
165
+ WHERE session_id = ?
166
+ AND priority = ?
167
+ AND (value LIKE ? OR key LIKE ?)
168
+ ORDER BY created_at DESC`)
169
+ .all(testSessionId, 'high', '%auth%', '%auth%');
170
+ expect(results).toHaveLength(3); // auth_bug, auth_decision, security_note
171
+ });
172
+ it('should search across multiple sessions', () => {
173
+ // Create another session
174
+ const sessionId2 = (0, uuid_1.v4)();
175
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId2, 'Second Session');
176
+ db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run((0, uuid_1.v4)(), sessionId2, 'auth_other', 'Another auth-related item');
177
+ // Search across all sessions
178
+ const results = db
179
+ .prepare(`SELECT ci.*, s.name as session_name
180
+ FROM context_items ci
181
+ JOIN sessions s ON ci.session_id = s.id
182
+ WHERE ci.value LIKE ?
183
+ ORDER BY ci.created_at DESC`)
184
+ .all('%auth%');
185
+ expect(results.length).toBeGreaterThanOrEqual(3);
186
+ expect(results.some((r) => r.session_name === 'Search Test Session')).toBe(true);
187
+ expect(results.some((r) => r.session_name === 'Second Session')).toBe(true);
188
+ });
189
+ it('should handle case-insensitive search', () => {
190
+ // SQLite LIKE is case-insensitive by default
191
+ const results = db
192
+ .prepare(`SELECT * FROM context_items
193
+ WHERE session_id = ?
194
+ AND value LIKE ?`)
195
+ .all(testSessionId, '%AUTH%');
196
+ expect(results).toHaveLength(3); // Should find 'authentication' and 'auth' items
197
+ // auth_bug: "Fixed authentication bug in login flow"
198
+ // auth_decision: "Decided to use JWT for authentication"
199
+ // security_note: "Remember to implement rate limiting on auth endpoints"
200
+ });
201
+ it('should search with date range', () => {
202
+ // Add an old item
203
+ const oldDate = new Date();
204
+ oldDate.setDate(oldDate.getDate() - 7);
205
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, created_at) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'old_item', 'Old authentication method', oldDate.toISOString());
206
+ // Search for items from last 3 days
207
+ const threeDaysAgo = new Date();
208
+ threeDaysAgo.setDate(threeDaysAgo.getDate() - 3);
209
+ const results = db
210
+ .prepare(`SELECT * FROM context_items
211
+ WHERE session_id = ?
212
+ AND datetime(created_at) > datetime(?)
213
+ AND value LIKE ?`)
214
+ .all(testSessionId, threeDaysAgo.toISOString(), '%authentication%');
215
+ expect(results).toHaveLength(2); // Should not include old_item
216
+ expect(results.every((r) => r.key !== 'old_item')).toBe(true);
217
+ });
218
+ it('should rank results by relevance', () => {
219
+ // Add items with varying relevance
220
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, priority) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'exact', 'authentication', 'normal');
221
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, priority) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'partial', 'implemented authentication system', 'normal');
222
+ // Search with exact match having higher relevance
223
+ const results = db
224
+ .prepare(`SELECT *,
225
+ CASE
226
+ WHEN value = ? THEN 3
227
+ WHEN value LIKE ? AND value LIKE ? THEN 2
228
+ WHEN value LIKE ? THEN 1
229
+ END as relevance
230
+ FROM context_items
231
+ WHERE session_id = ?
232
+ AND value LIKE ?
233
+ ORDER BY relevance DESC, priority DESC`)
234
+ .all('authentication', '%authentication%', '%', '%authentication%', testSessionId, '%authentication%');
235
+ expect(results[0].key).toBe('exact'); // Exact match first
236
+ });
237
+ });
238
+ describe('File content search', () => {
239
+ beforeEach(() => {
240
+ // Ensure session exists
241
+ const session = db.prepare('SELECT id FROM sessions WHERE id = ?').get(testSessionId);
242
+ if (!session) {
243
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Search Test Session');
244
+ }
245
+ // Add file cache entries
246
+ const files = [
247
+ {
248
+ path: '/src/auth.ts',
249
+ content: 'export function authenticate(user: User) { /* auth logic */ }',
250
+ },
251
+ { path: '/src/api.ts', content: 'router.post("/login", authenticate);' },
252
+ {
253
+ path: '/tests/auth.test.ts',
254
+ content: 'describe("authentication", () => { /* tests */ })',
255
+ },
256
+ {
257
+ path: '/docs/README.md',
258
+ content: '# Authentication\nThis module handles user authentication.',
259
+ },
260
+ ];
261
+ files.forEach(file => {
262
+ const hash = require('crypto').createHash('sha256').update(file.content).digest('hex');
263
+ db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, file.path, file.content, hash);
264
+ });
265
+ // Also add context items for the combine test
266
+ const contextItems = [
267
+ { key: 'auth_feature', value: 'Implement authentication system', category: 'task' },
268
+ { key: 'auth_config', value: 'Configure auth middleware', category: 'task' },
269
+ ];
270
+ contextItems.forEach(item => {
271
+ db.prepare('INSERT OR IGNORE INTO context_items (id, session_id, key, value, category) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, item.key, item.value, item.category);
272
+ });
273
+ });
274
+ it('should search file contents', () => {
275
+ const results = db
276
+ .prepare(`SELECT * FROM file_cache
277
+ WHERE session_id = ?
278
+ AND content LIKE ?
279
+ ORDER BY file_path`)
280
+ .all(testSessionId, '%authenticate%');
281
+ expect(results).toHaveLength(2); // auth.ts, api.ts only
282
+ // auth.ts: "export function authenticate(user: User) { /* auth logic */ }"
283
+ // api.ts: "router.post("/login", authenticate);"
284
+ // tests/auth.test.ts: has "authentication" not "authenticate"
285
+ // README.md: has "authentication" not "authenticate"
286
+ expect(results.map((r) => r.file_path)).not.toContain('/tests/auth.test.ts');
287
+ });
288
+ it('should search by file path pattern', () => {
289
+ const results = db
290
+ .prepare(`SELECT * FROM file_cache
291
+ WHERE session_id = ?
292
+ AND file_path LIKE ?`)
293
+ .all(testSessionId, '/src/%');
294
+ expect(results).toHaveLength(2);
295
+ expect(results.every((r) => r.file_path.startsWith('/src/'))).toBe(true);
296
+ });
297
+ it('should combine file and context search', () => {
298
+ // Search for 'auth' in both context and files (broader search)
299
+ const contextResults = db
300
+ .prepare(`SELECT 'context' as source, key as name, value as content
301
+ FROM context_items
302
+ WHERE session_id = ? AND (value LIKE ? OR key LIKE ?)`)
303
+ .all(testSessionId, '%auth%', '%auth%');
304
+ const fileResults = db
305
+ .prepare(`SELECT 'file' as source, file_path as name, content
306
+ FROM file_cache
307
+ WHERE session_id = ? AND (content LIKE ? OR file_path LIKE ?)`)
308
+ .all(testSessionId, '%auth%', '%auth%');
309
+ const allResults = [...contextResults, ...fileResults];
310
+ expect(allResults.length).toBeGreaterThanOrEqual(4);
311
+ expect(allResults.some((r) => r.source === 'context')).toBe(true);
312
+ expect(allResults.some((r) => r.source === 'file')).toBe(true);
313
+ });
314
+ });
315
+ describe('Search performance', () => {
316
+ it('should use indexes efficiently', () => {
317
+ // Add many items to test performance
318
+ for (let i = 0; i < 100; i++) {
319
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, category) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, `key${i}`, `value ${i} with some text`, i % 2 === 0 ? 'task' : 'note');
320
+ }
321
+ const start = Date.now();
322
+ const results = db
323
+ .prepare(`SELECT * FROM context_items
324
+ WHERE session_id = ?
325
+ AND category = ?
326
+ LIMIT 10`)
327
+ .all(testSessionId, 'task');
328
+ const duration = Date.now() - start;
329
+ expect(results).toHaveLength(10);
330
+ expect(duration).toBeLessThan(100); // Should be fast with indexes
331
+ });
332
+ });
333
+ });
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const database_1 = require("../../utils/database");
37
+ const vector_store_1 = require("../../utils/vector-store");
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const fs = __importStar(require("fs"));
41
+ const uuid_1 = require("uuid");
42
+ describe('Semantic Search Integration Tests', () => {
43
+ let dbManager;
44
+ let vectorStore;
45
+ let tempDbPath;
46
+ let db;
47
+ let testSessionId;
48
+ beforeEach(() => {
49
+ tempDbPath = path.join(os.tmpdir(), `test-semantic-${Date.now()}.db`);
50
+ dbManager = new database_1.DatabaseManager({
51
+ filename: tempDbPath,
52
+ maxSize: 10 * 1024 * 1024,
53
+ walMode: true,
54
+ });
55
+ db = dbManager.getDatabase();
56
+ vectorStore = new vector_store_1.VectorStore(db);
57
+ // Create test session
58
+ testSessionId = (0, uuid_1.v4)();
59
+ db.prepare('INSERT INTO sessions (id, name, description) VALUES (?, ?, ?)').run(testSessionId, 'Semantic Search Test', 'Testing semantic search integration');
60
+ });
61
+ afterEach(() => {
62
+ dbManager.close();
63
+ try {
64
+ fs.unlinkSync(tempDbPath);
65
+ fs.unlinkSync(`${tempDbPath}-wal`);
66
+ fs.unlinkSync(`${tempDbPath}-shm`);
67
+ }
68
+ catch (_e) {
69
+ // Ignore
70
+ }
71
+ });
72
+ describe('context_semantic_search', () => {
73
+ beforeEach(async () => {
74
+ // Create diverse context items
75
+ const items = [
76
+ {
77
+ key: 'auth_implementation',
78
+ value: 'Implemented JWT authentication with refresh tokens. The tokens expire after 24 hours.',
79
+ category: 'progress',
80
+ priority: 'high',
81
+ },
82
+ {
83
+ key: 'database_decision',
84
+ value: 'Decided to use PostgreSQL for the main database due to its JSON support and reliability.',
85
+ category: 'decision',
86
+ priority: 'high',
87
+ },
88
+ {
89
+ key: 'api_design',
90
+ value: 'Designed RESTful API endpoints following OpenAPI 3.0 specification.',
91
+ category: 'task',
92
+ priority: 'normal',
93
+ },
94
+ {
95
+ key: 'security_note',
96
+ value: 'Remember to implement rate limiting on authentication endpoints to prevent brute force attacks.',
97
+ category: 'note',
98
+ priority: 'high',
99
+ },
100
+ {
101
+ key: 'testing_strategy',
102
+ value: 'Unit tests for individual functions, integration tests for API endpoints, and e2e tests for user flows.',
103
+ category: 'decision',
104
+ priority: 'normal',
105
+ },
106
+ {
107
+ key: 'performance_optimization',
108
+ value: 'Added database indexing on user email field to speed up authentication queries.',
109
+ category: 'progress',
110
+ priority: 'normal',
111
+ },
112
+ {
113
+ key: 'bug_fix',
114
+ value: 'Fixed memory leak in WebSocket connection handler by properly cleaning up event listeners.',
115
+ category: 'progress',
116
+ priority: 'high',
117
+ },
118
+ {
119
+ key: 'architecture_pattern',
120
+ value: 'Using Repository pattern for data access layer to abstract database operations.',
121
+ category: 'decision',
122
+ priority: 'normal',
123
+ },
124
+ ];
125
+ for (const item of items) {
126
+ const itemId = (0, uuid_1.v4)();
127
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, category, priority) VALUES (?, ?, ?, ?, ?, ?)').run(itemId, testSessionId, item.key, item.value, item.category, item.priority);
128
+ // Create embedding
129
+ await vectorStore.storeDocument(itemId, `${item.key}: ${item.value}`, {
130
+ key: item.key,
131
+ category: item.category,
132
+ priority: item.priority,
133
+ });
134
+ }
135
+ });
136
+ it('should find relevant results for authentication queries', async () => {
137
+ const results = await vectorStore.searchInSession(testSessionId, 'authentication security tokens', 5, 0.1);
138
+ expect(results.length).toBeGreaterThan(0);
139
+ // Should find auth implementation and security note
140
+ const authResults = results.filter(r => r.content.includes('auth_implementation') || r.content.includes('security_note'));
141
+ expect(authResults.length).toBeGreaterThanOrEqual(2);
142
+ });
143
+ it('should find relevant results for database queries', async () => {
144
+ const results = await vectorStore.searchInSession(testSessionId, 'database performance optimization', 5, 0.1);
145
+ expect(results.length).toBeGreaterThan(0);
146
+ // Should find database decision and performance optimization
147
+ const dbResults = results.filter(r => r.content.includes('database_decision') || r.content.includes('performance_optimization'));
148
+ expect(dbResults.length).toBeGreaterThanOrEqual(1);
149
+ });
150
+ it('should rank results by similarity', async () => {
151
+ const results = await vectorStore.searchInSession(testSessionId, 'JWT token authentication', 10, 0.0);
152
+ expect(results.length).toBeGreaterThan(0);
153
+ // First result should be the auth implementation
154
+ expect(results[0].content).toContain('auth_implementation');
155
+ expect(results[0].similarity).toBeGreaterThan(0.5);
156
+ // Results should be ordered by similarity
157
+ for (let i = 1; i < results.length; i++) {
158
+ expect(results[i].similarity).toBeLessThanOrEqual(results[i - 1].similarity);
159
+ }
160
+ });
161
+ it('should handle queries with no good matches', async () => {
162
+ const results = await vectorStore.searchInSession(testSessionId, 'quantum computing blockchain AI', 10, 0.5 // High threshold
163
+ );
164
+ expect(results.length).toBe(0);
165
+ });
166
+ it('should respect metadata in results', async () => {
167
+ const results = await vectorStore.searchInSession(testSessionId, 'important security decisions', 10, 0.1);
168
+ const resultsWithMetadata = results.filter(r => r.metadata);
169
+ expect(resultsWithMetadata.length).toBeGreaterThan(0);
170
+ // Check metadata structure
171
+ const firstWithMeta = resultsWithMetadata[0];
172
+ expect(firstWithMeta.metadata).toHaveProperty('key');
173
+ expect(firstWithMeta.metadata).toHaveProperty('category');
174
+ expect(firstWithMeta.metadata).toHaveProperty('priority');
175
+ });
176
+ it('should handle natural language queries', async () => {
177
+ const naturalQueries = [
178
+ 'what did we decide about the database?',
179
+ 'how are we handling user authentication?',
180
+ 'what testing approach are we using?',
181
+ 'any security concerns to remember?',
182
+ ];
183
+ for (const query of naturalQueries) {
184
+ const results = await vectorStore.searchInSession(testSessionId, query, 3, 0.1);
185
+ expect(results.length).toBeGreaterThan(0);
186
+ }
187
+ });
188
+ it('should find conceptually related items', async () => {
189
+ const results = await vectorStore.searchInSession(testSessionId, 'code quality and maintainability', 5, 0.1);
190
+ // Should find testing strategy and architecture pattern
191
+ const qualityResults = results.filter(r => r.content.includes('testing_strategy') || r.content.includes('architecture_pattern'));
192
+ expect(qualityResults.length).toBeGreaterThan(0);
193
+ });
194
+ });
195
+ describe('Performance and edge cases', () => {
196
+ it('should handle large contexts efficiently', async () => {
197
+ // Create many context items
198
+ const itemCount = 100;
199
+ const promises = [];
200
+ for (let i = 0; i < itemCount; i++) {
201
+ const itemId = (0, uuid_1.v4)();
202
+ db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run(itemId, testSessionId, `item_${i}`, `This is test content number ${i} with some variation`);
203
+ promises.push(vectorStore.storeDocument(itemId, `item_${i}: This is test content number ${i} with some variation`));
204
+ }
205
+ await Promise.all(promises);
206
+ const startTime = Date.now();
207
+ const results = await vectorStore.searchInSession(testSessionId, 'test content variation', 10, 0.1);
208
+ const endTime = Date.now();
209
+ expect(results.length).toBeGreaterThan(0);
210
+ expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
211
+ });
212
+ it('should handle special characters in queries', async () => {
213
+ const itemId = (0, uuid_1.v4)();
214
+ db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run(itemId, testSessionId, 'special_chars', 'Using C++ and C# with ASP.NET @decorators');
215
+ await vectorStore.storeDocument(itemId, 'special_chars: Using C++ and C# with ASP.NET @decorators');
216
+ const results = await vectorStore.searchInSession(testSessionId, 'C++ C# ASP.NET', 3, 0.1);
217
+ expect(results.length).toBe(1);
218
+ expect(results[0].content).toContain('special_chars');
219
+ });
220
+ it('should handle very long queries', async () => {
221
+ const longQuery = 'authentication ' + 'security '.repeat(50) + 'tokens';
222
+ const results = await vectorStore.searchInSession(testSessionId, longQuery, 5, 0.05);
223
+ // Long repetitive queries might have lower similarity scores
224
+ // Just verify it doesn't crash and returns an array
225
+ expect(Array.isArray(results)).toBe(true);
226
+ });
227
+ it('should handle empty session gracefully', async () => {
228
+ const emptySessionId = (0, uuid_1.v4)();
229
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(emptySessionId, 'Empty Session');
230
+ const results = await vectorStore.searchInSession(emptySessionId, 'any query', 10, 0.1);
231
+ expect(results).toEqual([]);
232
+ });
233
+ });
234
+ describe('Multi-session search', () => {
235
+ let otherSessionId;
236
+ beforeEach(async () => {
237
+ // Create another session with different content
238
+ otherSessionId = (0, uuid_1.v4)();
239
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(otherSessionId, 'Other Project');
240
+ const items = [
241
+ {
242
+ key: 'frontend_framework',
243
+ value: 'Using React with TypeScript for the frontend application',
244
+ },
245
+ { key: 'state_management', value: 'Redux Toolkit for global state management' },
246
+ ];
247
+ for (const item of items) {
248
+ const itemId = (0, uuid_1.v4)();
249
+ db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run(itemId, otherSessionId, item.key, item.value);
250
+ await vectorStore.storeDocument(itemId, `${item.key}: ${item.value}`);
251
+ }
252
+ });
253
+ it('should search across all sessions when not specified', async () => {
254
+ const results = await vectorStore.search('frontend React TypeScript', 10, 0.1);
255
+ // Should find results from other session
256
+ const frontendResults = results.filter(r => r.content.includes('frontend_framework'));
257
+ expect(frontendResults.length).toBe(1);
258
+ });
259
+ it('should isolate search to specific session when specified', async () => {
260
+ const results = await vectorStore.searchInSession(testSessionId, 'React Redux frontend', 10, 0.1);
261
+ // Should not find results from other session
262
+ const frontendResults = results.filter(r => r.content.includes('frontend_framework') || r.content.includes('state_management'));
263
+ expect(frontendResults.length).toBe(0);
264
+ });
265
+ });
266
+ });