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,304 @@
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 globals_1 = require("@jest/globals");
37
+ const database_1 = require("../../utils/database");
38
+ const ContextRepository_1 = require("../../repositories/ContextRepository");
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
41
+ const fs = __importStar(require("fs"));
42
+ const uuid_1 = require("uuid");
43
+ /**
44
+ * Tests for Issue #11: ACTUAL BUG DEMONSTRATION
45
+ *
46
+ * ROOT CAUSE IDENTIFIED:
47
+ * Both searchEnhanced and queryEnhanced incorrectly start with "WHERE session_id = ?"
48
+ * This limits them to only show items from the current session.
49
+ *
50
+ * CORRECT BEHAVIOR (as shown in other methods like getAccessibleItems):
51
+ * Should start with "WHERE (is_private = 0 OR session_id = ?)"
52
+ * This shows: public items from ANY session + private items from OWN session
53
+ *
54
+ * CURRENT INCORRECT BEHAVIOR:
55
+ * Only shows items from current session (both public and private)
56
+ * Misses public items from other sessions entirely
57
+ */
58
+ (0, globals_1.describe)('Issue #11: Actual Bug - Incorrect Session Filtering', () => {
59
+ let dbManager;
60
+ let tempDbPath;
61
+ let db;
62
+ let contextRepo;
63
+ let testSessionId;
64
+ let otherSessionId;
65
+ (0, globals_1.beforeEach)(() => {
66
+ tempDbPath = path.join(os.tmpdir(), `test-actual-bug-${Date.now()}.db`);
67
+ dbManager = new database_1.DatabaseManager({
68
+ filename: tempDbPath,
69
+ maxSize: 10 * 1024 * 1024,
70
+ walMode: true,
71
+ });
72
+ db = dbManager.getDatabase();
73
+ contextRepo = new ContextRepository_1.ContextRepository(dbManager);
74
+ // Create test sessions
75
+ testSessionId = (0, uuid_1.v4)();
76
+ otherSessionId = (0, uuid_1.v4)();
77
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Main Test Session');
78
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(otherSessionId, 'Other Session');
79
+ // Create test data that demonstrates the bug
80
+ const testItems = [
81
+ // Items in main session
82
+ {
83
+ id: (0, uuid_1.v4)(),
84
+ session_id: testSessionId,
85
+ key: 'my_public_auth',
86
+ value: 'Public auth task in my session',
87
+ category: 'task',
88
+ priority: 'high',
89
+ channel: 'main',
90
+ created_at: new Date().toISOString(),
91
+ is_private: 0, // Public - should be visible to all
92
+ },
93
+ {
94
+ id: (0, uuid_1.v4)(),
95
+ session_id: testSessionId,
96
+ key: 'my_private_auth',
97
+ value: 'Private auth notes in my session',
98
+ category: 'note',
99
+ priority: 'normal',
100
+ channel: 'main',
101
+ created_at: new Date().toISOString(),
102
+ is_private: 1, // Private - only visible to own session
103
+ },
104
+ // Items in other session
105
+ {
106
+ id: (0, uuid_1.v4)(),
107
+ session_id: otherSessionId,
108
+ key: 'other_public_auth',
109
+ value: 'Public auth documentation from other session',
110
+ category: 'docs',
111
+ priority: 'normal',
112
+ channel: 'docs',
113
+ created_at: new Date().toISOString(),
114
+ is_private: 0, // Public - SHOULD be visible to main session but currently ISN'T
115
+ },
116
+ {
117
+ id: (0, uuid_1.v4)(),
118
+ session_id: otherSessionId,
119
+ key: 'other_private_auth',
120
+ value: 'Private auth secrets from other session',
121
+ category: 'secret',
122
+ priority: 'high',
123
+ channel: 'security',
124
+ created_at: new Date().toISOString(),
125
+ is_private: 1, // Private - should NOT be visible to main session
126
+ },
127
+ ];
128
+ // Insert test data
129
+ const stmt = db.prepare(`
130
+ INSERT INTO context_items (
131
+ id, session_id, key, value, category, priority, channel,
132
+ created_at, is_private
133
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
134
+ `);
135
+ testItems.forEach(item => {
136
+ stmt.run(item.id, item.session_id, item.key, item.value, item.category, item.priority, item.channel, item.created_at, item.is_private);
137
+ });
138
+ });
139
+ (0, globals_1.afterEach)(() => {
140
+ dbManager.close();
141
+ try {
142
+ fs.unlinkSync(tempDbPath);
143
+ fs.unlinkSync(`${tempDbPath}-wal`);
144
+ fs.unlinkSync(`${tempDbPath}-shm`);
145
+ }
146
+ catch (_e) {
147
+ // Ignore cleanup errors
148
+ }
149
+ });
150
+ (0, globals_1.describe)('Demonstrate the ACTUAL bug', () => {
151
+ (0, globals_1.it)('FAILING TEST: searchEnhanced should show public items from other sessions but does not', () => {
152
+ const searchResult = contextRepo.searchEnhanced({
153
+ query: 'auth',
154
+ sessionId: testSessionId,
155
+ });
156
+ // console.log('searchEnhanced results:', {
157
+ // total: searchResult.items.length,
158
+ // items: searchResult.items.map(i => ({
159
+ // key: i.key,
160
+ // session_id: i.session_id,
161
+ // is_private: i.is_private,
162
+ // from_other_session: i.session_id !== testSessionId,
163
+ // })),
164
+ // });
165
+ // BUG: This should find the public item 'other_public_auth' from other session
166
+ const publicItemsFromOtherSession = searchResult.items.filter(item => item.session_id === otherSessionId && item.is_private === 0);
167
+ const privateItemsFromOtherSession = searchResult.items.filter(item => item.session_id === otherSessionId && item.is_private === 1);
168
+ // console.log('Analysis:', {
169
+ // publicFromOther: publicItemsFromOtherSession.length,
170
+ // privateFromOther: privateItemsFromOtherSession.length,
171
+ // shouldSeePublicFromOther: 1, // other_public_auth
172
+ // shouldSeePrivateFromOther: 0, // none
173
+ // });
174
+ // THIS TEST WILL FAIL because searchEnhanced incorrectly limits to current session only
175
+ (0, globals_1.expect)(publicItemsFromOtherSession.length).toBe(1); // Should find other_public_auth
176
+ (0, globals_1.expect)(privateItemsFromOtherSession.length).toBe(0); // Should NOT find other_private_auth
177
+ });
178
+ (0, globals_1.it)('FAILING TEST: queryEnhanced should show public items from other sessions but does not', () => {
179
+ const queryResult = contextRepo.queryEnhanced({
180
+ sessionId: testSessionId,
181
+ });
182
+ // console.log('queryEnhanced results:', {
183
+ // total: queryResult.items.length,
184
+ // items: queryResult.items.map(i => ({
185
+ // key: i.key,
186
+ // session_id: i.session_id,
187
+ // is_private: i.is_private,
188
+ // from_other_session: i.session_id !== testSessionId,
189
+ // })),
190
+ // });
191
+ // Filter for items with 'auth' to compare with searchEnhanced
192
+ const authItems = queryResult.items.filter(item => item.key.includes('auth') || item.value.includes('auth'));
193
+ const publicItemsFromOtherSession = authItems.filter(item => item.session_id === otherSessionId && item.is_private === 0);
194
+ const privateItemsFromOtherSession = authItems.filter(item => item.session_id === otherSessionId && item.is_private === 1);
195
+ // console.log('Analysis:', {
196
+ // authItems: authItems.length,
197
+ // publicFromOther: publicItemsFromOtherSession.length,
198
+ // privateFromOther: privateItemsFromOtherSession.length,
199
+ // shouldSeePublicFromOther: 1, // other_public_auth
200
+ // shouldSeePrivateFromOther: 0, // none
201
+ // });
202
+ // THIS TEST WILL FAIL because queryEnhanced incorrectly limits to current session only
203
+ (0, globals_1.expect)(publicItemsFromOtherSession.length).toBe(1); // Should find other_public_auth
204
+ (0, globals_1.expect)(privateItemsFromOtherSession.length).toBe(0); // Should NOT find other_private_auth
205
+ });
206
+ (0, globals_1.it)('CONTROL TEST: getAccessibleItems shows correct privacy behavior', () => {
207
+ // This method correctly implements the privacy filter
208
+ const accessibleItems = contextRepo.getAccessibleItems(testSessionId);
209
+ // console.log('getAccessibleItems results (CORRECT behavior):', {
210
+ // total: accessibleItems.length,
211
+ // items: accessibleItems.map(i => ({
212
+ // key: i.key,
213
+ // session_id: i.session_id,
214
+ // is_private: i.is_private,
215
+ // from_other_session: i.session_id !== testSessionId,
216
+ // })),
217
+ // });
218
+ const publicItemsFromOtherSession = accessibleItems.filter(item => item.session_id === otherSessionId && item.is_private === 0);
219
+ const privateItemsFromOtherSession = accessibleItems.filter(item => item.session_id === otherSessionId && item.is_private === 1);
220
+ const myPrivateItems = accessibleItems.filter(item => item.session_id === testSessionId && item.is_private === 1);
221
+ // console.log('Correct privacy analysis:', {
222
+ // publicFromOther: publicItemsFromOtherSession.length,
223
+ // privateFromOther: privateItemsFromOtherSession.length,
224
+ // myPrivateItems: myPrivateItems.length,
225
+ // });
226
+ // This should work correctly
227
+ (0, globals_1.expect)(publicItemsFromOtherSession.length).toBe(1); // Should find other_public_auth
228
+ (0, globals_1.expect)(privateItemsFromOtherSession.length).toBe(0); // Should NOT find other_private_auth
229
+ (0, globals_1.expect)(myPrivateItems.length).toBe(1); // Should find my_private_auth
230
+ });
231
+ });
232
+ (0, globals_1.describe)('Show what the bug causes in practice', () => {
233
+ (0, globals_1.it)('Bug impact: search with filters returns inconsistent results', () => {
234
+ // This demonstrates how the session-only filtering affects real usage
235
+ // User searches for auth-related content with filters
236
+ const searchWithCategory = contextRepo.searchEnhanced({
237
+ query: 'auth',
238
+ sessionId: testSessionId,
239
+ category: 'docs', // This category only exists in other session
240
+ });
241
+ const queryWithCategory = contextRepo.queryEnhanced({
242
+ sessionId: testSessionId,
243
+ category: 'docs',
244
+ });
245
+ // console.log('Search with category filter:', {
246
+ // searchResults: searchWithCategory.items.length,
247
+ // queryResults: queryWithCategory.items.length,
248
+ // searchItems: searchWithCategory.items.map(i => i.key),
249
+ // queryItems: queryWithCategory.items.map(i => i.key),
250
+ // });
251
+ // FIXED: Both now return 1 result because users can see public docs from other sessions
252
+ (0, globals_1.expect)(searchWithCategory.items.length).toBe(1); // Now correctly finds public docs
253
+ (0, globals_1.expect)(queryWithCategory.items.length).toBe(1); // Now correctly finds public docs
254
+ // This is why users report "filters not working" - they're not seeing expected results
255
+ });
256
+ (0, globals_1.it)('Bug impact: users miss valuable public content from other sessions', () => {
257
+ // User searches for documentation
258
+ const searchForDocs = contextRepo.searchEnhanced({
259
+ query: 'documentation',
260
+ sessionId: testSessionId,
261
+ });
262
+ const allPublicDocs = contextRepo
263
+ .getAccessibleItems(testSessionId)
264
+ .filter(item => item.value.includes('documentation') && item.is_private === 0);
265
+ // console.log('Documentation search impact:', {
266
+ // searchFound: searchForDocs.items.length,
267
+ // actualPublicDocs: allPublicDocs.length,
268
+ // missedDocs: allPublicDocs.length - searchForDocs.items.length,
269
+ // missedItems: allPublicDocs
270
+ // .filter(doc => !searchForDocs.items.some(found => found.key === doc.key))
271
+ // .map(i => ({ key: i.key, session: i.session_id })),
272
+ // });
273
+ // FIXED: Users now find all available public content
274
+ (0, globals_1.expect)(searchForDocs.items.length).toBe(allPublicDocs.length);
275
+ });
276
+ });
277
+ (0, globals_1.describe)('Expected behavior after fix', () => {
278
+ (0, globals_1.it)('After fix: searchEnhanced should work like getAccessibleItems', () => {
279
+ // Once fixed, searchEnhanced should respect privacy like getAccessibleItems
280
+ const searchResult = contextRepo.searchEnhanced({
281
+ query: '', // Empty query to get all items
282
+ sessionId: testSessionId,
283
+ });
284
+ const accessibleItems = contextRepo.getAccessibleItems(testSessionId);
285
+ // After fix, these should return the same items
286
+ (0, globals_1.expect)(searchResult.items.length).toBe(accessibleItems.length);
287
+ const searchKeys = new Set(searchResult.items.map(i => i.key));
288
+ const accessibleKeys = new Set(accessibleItems.map(i => i.key));
289
+ (0, globals_1.expect)(searchKeys).toEqual(accessibleKeys);
290
+ });
291
+ (0, globals_1.it)('After fix: queryEnhanced should work like getAccessibleItems', () => {
292
+ // Once fixed, queryEnhanced should respect privacy like getAccessibleItems
293
+ const queryResult = contextRepo.queryEnhanced({
294
+ sessionId: testSessionId,
295
+ });
296
+ const accessibleItems = contextRepo.getAccessibleItems(testSessionId);
297
+ // After fix, these should return the same items
298
+ (0, globals_1.expect)(queryResult.items.length).toBe(accessibleItems.length);
299
+ const queryKeys = new Set(queryResult.items.map(i => i.key));
300
+ const accessibleKeys = new Set(accessibleItems.map(i => i.key));
301
+ (0, globals_1.expect)(queryKeys).toEqual(accessibleKeys);
302
+ });
303
+ });
304
+ });