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,473 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const agents_1 = require("../../utils/agents");
40
+ const knowledge_graph_1 = require("../../utils/knowledge-graph");
41
+ const vector_store_1 = require("../../utils/vector-store");
42
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
43
+ const os = __importStar(require("os"));
44
+ const path = __importStar(require("path"));
45
+ const fs = __importStar(require("fs"));
46
+ const uuid_1 = require("uuid");
47
+ describe('Agents', () => {
48
+ let db;
49
+ let knowledgeGraph;
50
+ let vectorStore;
51
+ let tempDbPath;
52
+ let testSessionId;
53
+ beforeEach(() => {
54
+ tempDbPath = path.join(os.tmpdir(), `test-agents-${Date.now()}.db`);
55
+ db = new better_sqlite3_1.default(tempDbPath);
56
+ // Create tables
57
+ db.exec(`
58
+ CREATE TABLE IF NOT EXISTS sessions (
59
+ id TEXT PRIMARY KEY,
60
+ name TEXT,
61
+ description TEXT,
62
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
63
+ );
64
+
65
+ CREATE TABLE IF NOT EXISTS context_items (
66
+ id TEXT PRIMARY KEY,
67
+ session_id TEXT NOT NULL,
68
+ key TEXT NOT NULL,
69
+ value TEXT NOT NULL,
70
+ category TEXT,
71
+ priority TEXT DEFAULT 'normal',
72
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
73
+ FOREIGN KEY (session_id) REFERENCES sessions(id)
74
+ );
75
+
76
+ CREATE TABLE IF NOT EXISTS entities (
77
+ id TEXT PRIMARY KEY,
78
+ session_id TEXT NOT NULL,
79
+ type TEXT NOT NULL,
80
+ name TEXT NOT NULL,
81
+ attributes TEXT,
82
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
83
+ FOREIGN KEY (session_id) REFERENCES sessions(id)
84
+ );
85
+
86
+ CREATE TABLE IF NOT EXISTS relations (
87
+ id TEXT PRIMARY KEY,
88
+ session_id TEXT NOT NULL,
89
+ subject_id TEXT NOT NULL,
90
+ predicate TEXT NOT NULL,
91
+ object_id TEXT NOT NULL,
92
+ confidence REAL DEFAULT 1.0,
93
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
94
+ FOREIGN KEY (session_id) REFERENCES sessions(id),
95
+ FOREIGN KEY (subject_id) REFERENCES entities(id),
96
+ FOREIGN KEY (object_id) REFERENCES entities(id)
97
+ );
98
+
99
+ CREATE TABLE IF NOT EXISTS embeddings (
100
+ id TEXT PRIMARY KEY,
101
+ content TEXT NOT NULL,
102
+ embedding TEXT NOT NULL,
103
+ metadata TEXT,
104
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
105
+ );
106
+ `);
107
+ knowledgeGraph = new knowledge_graph_1.KnowledgeGraphManager(db);
108
+ vectorStore = new vector_store_1.VectorStore(db);
109
+ // Create test session
110
+ testSessionId = (0, uuid_1.v4)();
111
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Test Session');
112
+ });
113
+ afterEach(() => {
114
+ db.close();
115
+ try {
116
+ fs.unlinkSync(tempDbPath);
117
+ }
118
+ catch (_e) {
119
+ // Ignore
120
+ }
121
+ });
122
+ describe('AnalyzerAgent', () => {
123
+ let analyzerAgent;
124
+ beforeEach(() => {
125
+ analyzerAgent = new agents_1.AnalyzerAgent(db, knowledgeGraph, vectorStore);
126
+ // Add test data
127
+ const items = [
128
+ { key: 'task_1', value: 'Implement authentication', category: 'task', priority: 'high' },
129
+ { key: 'task_2', value: 'Write unit tests', category: 'task', priority: 'normal' },
130
+ { key: 'decision_1', value: 'Use JWT tokens', category: 'decision', priority: 'high' },
131
+ {
132
+ key: 'progress_1',
133
+ value: 'Completed login form',
134
+ category: 'progress',
135
+ priority: 'normal',
136
+ },
137
+ {
138
+ key: 'note_1',
139
+ value: 'Remember to add rate limiting',
140
+ category: 'note',
141
+ priority: 'high',
142
+ },
143
+ ];
144
+ for (const item of items) {
145
+ 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);
146
+ }
147
+ });
148
+ it('should analyze patterns in context', async () => {
149
+ const task = {
150
+ id: (0, uuid_1.v4)(),
151
+ type: 'analyze',
152
+ input: {
153
+ analysisType: 'patterns',
154
+ sessionId: testSessionId,
155
+ },
156
+ };
157
+ const result = await analyzerAgent.process(task);
158
+ expect(result.taskId).toBe(task.id);
159
+ expect(result.agentType).toBe('analyzer');
160
+ expect(result.confidence).toBeGreaterThan(0);
161
+ expect(result.output.patterns).toBeDefined();
162
+ expect(result.output.patterns.categoryDistribution).toBeDefined();
163
+ expect(result.output.patterns.priorityDistribution).toBeDefined();
164
+ expect(result.output.itemCount).toBe(5);
165
+ });
166
+ it('should analyze relationships', async () => {
167
+ // Add some entities
168
+ const entity1 = knowledgeGraph.createEntity(testSessionId, 'class', 'AuthService');
169
+ const entity2 = knowledgeGraph.createEntity(testSessionId, 'class', 'TokenManager');
170
+ knowledgeGraph.createRelation(testSessionId, entity1.id, 'uses', entity2.id);
171
+ const task = {
172
+ id: (0, uuid_1.v4)(),
173
+ type: 'analyze',
174
+ input: {
175
+ analysisType: 'relationships',
176
+ sessionId: testSessionId,
177
+ },
178
+ };
179
+ const result = await analyzerAgent.process(task);
180
+ expect(result.output.analysis).toBeDefined();
181
+ expect(result.output.analysis.entityCount).toBeGreaterThan(0);
182
+ expect(result.output.recommendations).toBeDefined();
183
+ });
184
+ it('should analyze trends', async () => {
185
+ const task = {
186
+ id: (0, uuid_1.v4)(),
187
+ type: 'analyze',
188
+ input: {
189
+ analysisType: 'trends',
190
+ sessionId: testSessionId,
191
+ timeframe: '-7 days',
192
+ },
193
+ };
194
+ const result = await analyzerAgent.process(task);
195
+ expect(result.output.trends).toBeDefined();
196
+ expect(result.output.trends.activityTrend).toBeDefined();
197
+ expect(result.output.summary).toBeDefined();
198
+ });
199
+ it('should perform comprehensive analysis', async () => {
200
+ const task = {
201
+ id: (0, uuid_1.v4)(),
202
+ type: 'analyze',
203
+ input: {
204
+ analysisType: 'comprehensive',
205
+ sessionId: testSessionId,
206
+ },
207
+ };
208
+ const result = await analyzerAgent.process(task);
209
+ expect(result.output.patterns).toBeDefined();
210
+ expect(result.output.relationships).toBeDefined();
211
+ expect(result.output.trends).toBeDefined();
212
+ expect(result.output.overallInsights).toBeDefined();
213
+ expect(result.confidence).toBe(0.9);
214
+ });
215
+ it('should handle unknown analysis type', async () => {
216
+ const task = {
217
+ id: (0, uuid_1.v4)(),
218
+ type: 'analyze',
219
+ input: {
220
+ analysisType: 'unknown',
221
+ sessionId: testSessionId,
222
+ },
223
+ };
224
+ const result = await analyzerAgent.process(task);
225
+ expect(result.output.error).toBeDefined();
226
+ expect(result.confidence).toBe(0);
227
+ });
228
+ it('should filter by categories', async () => {
229
+ const task = {
230
+ id: (0, uuid_1.v4)(),
231
+ type: 'analyze',
232
+ input: {
233
+ analysisType: 'patterns',
234
+ sessionId: testSessionId,
235
+ categories: ['task'],
236
+ },
237
+ };
238
+ const result = await analyzerAgent.process(task);
239
+ expect(result.output.itemCount).toBe(2); // Only 2 tasks
240
+ expect(Object.keys(result.output.patterns.categoryDistribution)).toEqual(['task']);
241
+ });
242
+ });
243
+ describe('SynthesizerAgent', () => {
244
+ let synthesizerAgent;
245
+ beforeEach(() => {
246
+ synthesizerAgent = new agents_1.SynthesizerAgent(db, vectorStore);
247
+ // Add test data
248
+ const items = [
249
+ {
250
+ key: 'task_1',
251
+ value: 'Implement authentication with OAuth2',
252
+ category: 'task',
253
+ priority: 'high',
254
+ },
255
+ {
256
+ key: 'task_2',
257
+ value: 'Add rate limiting to API endpoints',
258
+ category: 'task',
259
+ priority: 'high',
260
+ },
261
+ {
262
+ key: 'decision_1',
263
+ value: 'Use Redis for session storage',
264
+ category: 'decision',
265
+ priority: 'high',
266
+ },
267
+ {
268
+ key: 'progress_1',
269
+ value: 'Completed user registration flow',
270
+ category: 'progress',
271
+ priority: 'normal',
272
+ },
273
+ ];
274
+ for (const item of items) {
275
+ 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);
276
+ }
277
+ });
278
+ it('should create summary', async () => {
279
+ const task = {
280
+ id: (0, uuid_1.v4)(),
281
+ type: 'synthesize',
282
+ input: {
283
+ synthesisType: 'summary',
284
+ sessionId: testSessionId,
285
+ maxLength: 1000,
286
+ },
287
+ };
288
+ const result = await synthesizerAgent.process(task);
289
+ expect(result.output.summary).toBeDefined();
290
+ expect(result.output.summary).toContain('Context Summary');
291
+ expect(result.output.itemCount).toBe(4);
292
+ expect(result.output.categories).toContain('task');
293
+ expect(result.output.categories).toContain('decision');
294
+ });
295
+ it('should create summary with category filter', async () => {
296
+ const task = {
297
+ id: (0, uuid_1.v4)(),
298
+ type: 'synthesize',
299
+ input: {
300
+ synthesisType: 'summary',
301
+ sessionId: testSessionId,
302
+ categories: ['task'],
303
+ },
304
+ };
305
+ const result = await synthesizerAgent.process(task);
306
+ expect(result.output.summary).toContain('Current Tasks');
307
+ expect(result.output.categories).toEqual(['task']);
308
+ });
309
+ it('should merge insights', async () => {
310
+ const insights = [
311
+ { patterns: { keywords: ['auth', 'security'] }, themes: ['authentication'] },
312
+ { patterns: { keywords: ['api', 'rate'] }, themes: ['performance'] },
313
+ ];
314
+ const task = {
315
+ id: (0, uuid_1.v4)(),
316
+ type: 'synthesize',
317
+ input: {
318
+ synthesisType: 'merge',
319
+ insights,
320
+ },
321
+ };
322
+ const result = await synthesizerAgent.process(task);
323
+ expect(result.output.patterns).toBeDefined();
324
+ expect(result.output.overallThemes).toContain('authentication');
325
+ expect(result.output.overallThemes).toContain('performance');
326
+ });
327
+ it('should generate recommendations', async () => {
328
+ const analysisResults = {
329
+ highPriorityCount: 10,
330
+ staleTasks: true,
331
+ contextSize: 1500,
332
+ };
333
+ const task = {
334
+ id: (0, uuid_1.v4)(),
335
+ type: 'synthesize',
336
+ input: {
337
+ synthesisType: 'recommendations',
338
+ analysisResults,
339
+ },
340
+ };
341
+ const result = await synthesizerAgent.process(task);
342
+ expect(result.output.immediate).toBeDefined();
343
+ expect(result.output.shortTerm).toBeDefined();
344
+ expect(result.output.longTerm).toBeDefined();
345
+ expect(result.output.warnings).toBeDefined();
346
+ expect(result.output.warnings).toContain('Context size is large - consider compaction');
347
+ });
348
+ it('should handle unknown synthesis type', async () => {
349
+ const task = {
350
+ id: (0, uuid_1.v4)(),
351
+ type: 'synthesize',
352
+ input: {
353
+ synthesisType: 'unknown',
354
+ },
355
+ };
356
+ const result = await synthesizerAgent.process(task);
357
+ expect(result.output.error).toBeDefined();
358
+ expect(result.confidence).toBe(0);
359
+ });
360
+ });
361
+ describe('AgentCoordinator', () => {
362
+ let coordinator;
363
+ let analyzerAgent;
364
+ let synthesizerAgent;
365
+ beforeEach(() => {
366
+ coordinator = new agents_1.AgentCoordinator();
367
+ analyzerAgent = new agents_1.AnalyzerAgent(db, knowledgeGraph, vectorStore);
368
+ synthesizerAgent = new agents_1.SynthesizerAgent(db, vectorStore);
369
+ coordinator.registerAgent(analyzerAgent);
370
+ coordinator.registerAgent(synthesizerAgent);
371
+ });
372
+ it('should delegate to appropriate agent', async () => {
373
+ const task = {
374
+ id: (0, uuid_1.v4)(),
375
+ type: 'analyze',
376
+ input: {
377
+ analysisType: 'patterns',
378
+ sessionId: testSessionId,
379
+ },
380
+ };
381
+ const results = await coordinator.delegate(task);
382
+ expect(results.length).toBe(1);
383
+ expect(results[0].agentType).toBe('analyzer');
384
+ });
385
+ it('should handle no suitable agent', async () => {
386
+ const task = {
387
+ id: (0, uuid_1.v4)(),
388
+ type: 'unknown',
389
+ input: {},
390
+ };
391
+ const results = await coordinator.delegate(task);
392
+ expect(results.length).toBe(1);
393
+ expect(results[0].output.error).toBeDefined();
394
+ expect(results[0].agentType).toBe('coordinator');
395
+ });
396
+ it('should process task chain', async () => {
397
+ // Add test data
398
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, category, priority) VALUES (?, ?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'test_task', 'Test task description', 'task', 'high');
399
+ const tasks = [
400
+ {
401
+ id: (0, uuid_1.v4)(),
402
+ type: 'analyze',
403
+ input: {
404
+ analysisType: 'patterns',
405
+ sessionId: testSessionId,
406
+ },
407
+ },
408
+ {
409
+ id: (0, uuid_1.v4)(),
410
+ type: 'synthesize',
411
+ input: {
412
+ synthesisType: 'recommendations',
413
+ },
414
+ },
415
+ ];
416
+ const results = await coordinator.processChain(tasks);
417
+ expect(results.length).toBe(2);
418
+ expect(results[0].agentType).toBe('analyzer');
419
+ expect(results[1].agentType).toBe('synthesizer');
420
+ expect(results[1].output).toBeDefined(); // Should have used previous output as context
421
+ });
422
+ it('should get best result', async () => {
423
+ const task = {
424
+ id: (0, uuid_1.v4)(),
425
+ type: 'analyze',
426
+ input: {
427
+ analysisType: 'patterns',
428
+ sessionId: testSessionId,
429
+ },
430
+ };
431
+ await coordinator.delegate(task);
432
+ const best = coordinator.getBestResult(task.id);
433
+ expect(best).toBeDefined();
434
+ expect(best?.agentType).toBe('analyzer');
435
+ });
436
+ it('should get agent capabilities', () => {
437
+ const capabilities = coordinator.getAgentCapabilities();
438
+ expect(capabilities.analyzer).toBeDefined();
439
+ expect(capabilities.synthesizer).toBeDefined();
440
+ expect(capabilities.analyzer.length).toBeGreaterThan(0);
441
+ expect(capabilities.synthesizer.length).toBeGreaterThan(0);
442
+ });
443
+ });
444
+ describe('Agent base class', () => {
445
+ class TestAgent extends agents_1.Agent {
446
+ async process(task) {
447
+ return {
448
+ taskId: task.id,
449
+ agentType: this.name,
450
+ output: { test: true },
451
+ confidence: 1.0,
452
+ processingTime: 0,
453
+ };
454
+ }
455
+ }
456
+ it('should check if agent can handle task', () => {
457
+ const agent = new TestAgent('test', [
458
+ {
459
+ name: 'test_capability',
460
+ description: 'Test capability',
461
+ inputTypes: ['test'],
462
+ outputTypes: ['result'],
463
+ },
464
+ ]);
465
+ expect(agent.canHandle({ id: '1', type: 'test', input: {} })).toBe(true);
466
+ expect(agent.canHandle({ id: '2', type: 'other', input: {} })).toBe(false);
467
+ });
468
+ it('should return agent name', () => {
469
+ const agent = new TestAgent('test-agent', []);
470
+ expect(agent.getName()).toBe('test-agent');
471
+ });
472
+ });
473
+ });
@@ -0,0 +1,177 @@
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 fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ describe('DatabaseManager', () => {
41
+ let dbManager;
42
+ let tempDbPath;
43
+ beforeEach(() => {
44
+ // Create a temporary database file for testing
45
+ tempDbPath = path.join(os.tmpdir(), `test-db-${Date.now()}.db`);
46
+ dbManager = new database_1.DatabaseManager({
47
+ filename: tempDbPath,
48
+ maxSize: 10 * 1024 * 1024, // 10MB for testing
49
+ walMode: true,
50
+ });
51
+ });
52
+ afterEach(() => {
53
+ // Clean up
54
+ if (dbManager) {
55
+ dbManager.close();
56
+ }
57
+ try {
58
+ fs.unlinkSync(tempDbPath);
59
+ fs.unlinkSync(`${tempDbPath}-wal`);
60
+ fs.unlinkSync(`${tempDbPath}-shm`);
61
+ }
62
+ catch (_e) {
63
+ // Ignore errors if files don't exist
64
+ }
65
+ });
66
+ describe('Database initialization', () => {
67
+ it('should create all required tables', () => {
68
+ const db = dbManager.getDatabase();
69
+ // Check if tables exist
70
+ const tables = db
71
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
72
+ .all();
73
+ const tableNames = tables.map(t => t.name);
74
+ expect(tableNames).toContain('sessions');
75
+ expect(tableNames).toContain('context_items');
76
+ expect(tableNames).toContain('file_cache');
77
+ expect(tableNames).toContain('checkpoints');
78
+ expect(tableNames).toContain('checkpoint_items');
79
+ expect(tableNames).toContain('checkpoint_files');
80
+ });
81
+ it('should enable WAL mode', () => {
82
+ const db = dbManager.getDatabase();
83
+ const result = db.pragma('journal_mode');
84
+ expect(result[0].journal_mode).toBe('wal');
85
+ });
86
+ it('should enable foreign keys', () => {
87
+ const db = dbManager.getDatabase();
88
+ const result = db.pragma('foreign_keys');
89
+ expect(result[0].foreign_keys).toBe(1);
90
+ });
91
+ });
92
+ describe('Database size management', () => {
93
+ it('should calculate database size correctly', () => {
94
+ const size = dbManager.getDatabaseSize();
95
+ expect(size).toBeGreaterThan(0);
96
+ expect(typeof size).toBe('number');
97
+ });
98
+ it('should detect when database is full', () => {
99
+ // With a 10MB limit, it should not be full initially
100
+ expect(dbManager.isDatabaseFull()).toBe(false);
101
+ // Note: Actually filling the database would be slow, so we'll trust the logic
102
+ });
103
+ it('should calculate session size', () => {
104
+ const db = dbManager.getDatabase();
105
+ // Create a test session
106
+ const sessionId = 'test-session-123';
107
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
108
+ // Add some context items
109
+ db.prepare('INSERT INTO context_items (id, session_id, key, value, size) VALUES (?, ?, ?, ?, ?)').run('item1', sessionId, 'key1', 'value1', 6); // 'value1'.length = 6
110
+ const size = dbManager.getSessionSize(sessionId);
111
+ expect(size.items).toBe(1);
112
+ expect(size.files).toBe(0);
113
+ expect(size.totalSize).toBeGreaterThan(0);
114
+ });
115
+ });
116
+ describe('Cleanup operations', () => {
117
+ it('should cleanup old sessions', () => {
118
+ const db = dbManager.getDatabase();
119
+ // Create old session (31 days ago)
120
+ const oldDate = new Date();
121
+ oldDate.setDate(oldDate.getDate() - 31);
122
+ db.prepare('INSERT INTO sessions (id, name, created_at, updated_at) VALUES (?, ?, ?, ?)').run('old-session', 'Old Session', oldDate.toISOString(), oldDate.toISOString());
123
+ // Create recent session
124
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('new-session', 'New Session');
125
+ const deleted = dbManager.cleanupOldSessions(30);
126
+ expect(deleted).toBe(1);
127
+ // Verify old session was deleted
128
+ const sessions = db.prepare('SELECT id FROM sessions').all();
129
+ expect(sessions).toHaveLength(1);
130
+ expect(sessions[0]).toHaveProperty('id', 'new-session');
131
+ });
132
+ it('should not cleanup sessions with checkpoints', () => {
133
+ const db = dbManager.getDatabase();
134
+ // Create old session with checkpoint
135
+ const oldDate = new Date();
136
+ oldDate.setDate(oldDate.getDate() - 31);
137
+ db.prepare('INSERT INTO sessions (id, name, created_at) VALUES (?, ?, ?)').run('old-session', 'Old Session', oldDate.toISOString());
138
+ db.prepare('INSERT INTO checkpoints (id, session_id, name) VALUES (?, ?, ?)').run('checkpoint1', 'old-session', 'Important Checkpoint');
139
+ const deleted = dbManager.cleanupOldSessions(30);
140
+ expect(deleted).toBe(0);
141
+ });
142
+ });
143
+ describe('Transaction support', () => {
144
+ it('should execute transactions atomically', () => {
145
+ const db = dbManager.getDatabase();
146
+ // Successful transaction
147
+ const result = dbManager.transaction(() => {
148
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('trans-session', 'Transaction Session');
149
+ return 'success';
150
+ });
151
+ expect(result).toBe('success');
152
+ const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get('trans-session');
153
+ expect(session).toBeTruthy();
154
+ });
155
+ it('should rollback failed transactions', () => {
156
+ const db = dbManager.getDatabase();
157
+ // Insert a session first
158
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('existing-session', 'Existing Session');
159
+ // Failed transaction (duplicate key)
160
+ expect(() => {
161
+ dbManager.transaction(() => {
162
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('new-session', 'New Session');
163
+ // This should fail due to duplicate key
164
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('existing-session', 'Duplicate');
165
+ });
166
+ }).toThrow();
167
+ // Verify the first insert was rolled back
168
+ const newSession = db.prepare('SELECT * FROM sessions WHERE id = ?').get('new-session');
169
+ expect(newSession).toBeFalsy();
170
+ });
171
+ });
172
+ describe('Vacuum operation', () => {
173
+ it('should execute vacuum without errors', () => {
174
+ expect(() => dbManager.vacuum()).not.toThrow();
175
+ });
176
+ });
177
+ });