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.
- package/CHANGELOG.md +433 -0
- package/LICENSE +21 -0
- package/README.md +1051 -0
- package/bin/mcp-memory-keeper +52 -0
- package/dist/__tests__/helpers/database-test-helper.js +160 -0
- package/dist/__tests__/helpers/test-server.js +92 -0
- package/dist/__tests__/integration/advanced-features.test.js +614 -0
- package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
- package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
- package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
- package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
- package/dist/__tests__/integration/channels.test.js +376 -0
- package/dist/__tests__/integration/checkpoint.test.js +251 -0
- package/dist/__tests__/integration/concurrent-access.test.js +190 -0
- package/dist/__tests__/integration/context-operations.test.js +243 -0
- package/dist/__tests__/integration/contextDiff.test.js +852 -0
- package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
- package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
- package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
- package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
- package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
- package/dist/__tests__/integration/contextSearch.test.js +938 -0
- package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
- package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
- package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
- package/dist/__tests__/integration/cross-session-sharing.test.js +302 -0
- package/dist/__tests__/integration/database-initialization.test.js +134 -0
- package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
- package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
- package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
- package/dist/__tests__/integration/error-cases.test.js +407 -0
- package/dist/__tests__/integration/export-import.test.js +367 -0
- package/dist/__tests__/integration/feature-flags.test.js +542 -0
- package/dist/__tests__/integration/file-operations.test.js +264 -0
- package/dist/__tests__/integration/git-integration.test.js +237 -0
- package/dist/__tests__/integration/index-tools.test.js +496 -0
- package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
- package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
- package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
- package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
- package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
- package/dist/__tests__/integration/migrations.test.js +528 -0
- package/dist/__tests__/integration/multi-agent.test.js +546 -0
- package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
- package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
- package/dist/__tests__/integration/project-directory.test.js +283 -0
- package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
- package/dist/__tests__/integration/retention.test.js +513 -0
- package/dist/__tests__/integration/search.test.js +333 -0
- package/dist/__tests__/integration/semantic-search.test.js +266 -0
- package/dist/__tests__/integration/server-initialization.test.js +307 -0
- package/dist/__tests__/integration/session-management.test.js +219 -0
- package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
- package/dist/__tests__/integration/smart-compaction.test.js +230 -0
- package/dist/__tests__/integration/summarization.test.js +308 -0
- package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
- package/dist/__tests__/security/input-validation.test.js +115 -0
- package/dist/__tests__/utils/agents.test.js +473 -0
- package/dist/__tests__/utils/database.test.js +177 -0
- package/dist/__tests__/utils/git.test.js +122 -0
- package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
- package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
- package/dist/__tests__/utils/project-directory-messages.test.js +188 -0
- package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
- package/dist/__tests__/utils/validation.test.js +200 -0
- package/dist/__tests__/utils/vector-store.test.js +231 -0
- package/dist/handlers/contextWatchHandlers.js +206 -0
- package/dist/index.js +4310 -0
- package/dist/index.phase1.backup.js +410 -0
- package/dist/index.phase2.backup.js +704 -0
- package/dist/migrations/003_add_channels.js +174 -0
- package/dist/migrations/004_add_context_watch.js +151 -0
- package/dist/migrations/005_add_context_watch.js +98 -0
- package/dist/migrations/simplify-sharing.js +117 -0
- package/dist/repositories/BaseRepository.js +30 -0
- package/dist/repositories/CheckpointRepository.js +140 -0
- package/dist/repositories/ContextRepository.js +1873 -0
- package/dist/repositories/FileRepository.js +104 -0
- package/dist/repositories/RepositoryManager.js +62 -0
- package/dist/repositories/SessionRepository.js +66 -0
- package/dist/repositories/WatcherRepository.js +252 -0
- package/dist/repositories/index.js +15 -0
- package/dist/server.js +384 -0
- package/dist/test-helpers/database-helper.js +128 -0
- package/dist/types/entities.js +3 -0
- package/dist/utils/agents.js +791 -0
- package/dist/utils/channels.js +150 -0
- package/dist/utils/database.js +731 -0
- package/dist/utils/feature-flags.js +476 -0
- package/dist/utils/git.js +145 -0
- package/dist/utils/knowledge-graph.js +264 -0
- package/dist/utils/migrationHealthCheck.js +373 -0
- package/dist/utils/migrations.js +452 -0
- package/dist/utils/retention.js +460 -0
- package/dist/utils/timestamps.js +112 -0
- package/dist/utils/validation.js +296 -0
- package/dist/utils/vector-store.js +247 -0
- 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
|
+
});
|