@smallironman/mcp-memory-keeper 0.12.2-fork1
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 +542 -0
- package/LICENSE +21 -0
- package/README.md +1281 -0
- package/bin/mcp-memory-keeper +54 -0
- package/dist/__tests__/e2e/issue33-reproduce.test.js +234 -0
- package/dist/__tests__/e2e/server-e2e.test.js +341 -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 +1054 -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/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 +411 -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/filterBySessionId.test.js +251 -0
- package/dist/__tests__/integration/git-integration.test.js +241 -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/issue24-final-fix.test.js +241 -0
- package/dist/__tests__/integration/issue24-fix-validation.test.js +158 -0
- package/dist/__tests__/integration/issue24-reproduce.test.js +225 -0
- package/dist/__tests__/integration/issue24-token-limit.test.js +199 -0
- package/dist/__tests__/integration/issue33-array-items-schema.test.js +165 -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 +291 -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 +305 -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/tokenLimitEnforcement.test.js +134 -0
- package/dist/__tests__/integration/tool-profiles-integration.test.js +150 -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 +192 -0
- package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
- package/dist/__tests__/utils/token-limits.test.js +225 -0
- package/dist/__tests__/utils/tool-profiles.test.js +374 -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 +4425 -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 +2017 -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/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 +780 -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/token-limits.js +350 -0
- package/dist/utils/tool-profiles.js +242 -0
- package/dist/utils/validation.js +296 -0
- package/dist/utils/vector-store.js +247 -0
- package/examples/config.json +31 -0
- package/examples/project-directory-setup.md +114 -0
- package/package.json +85 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentCoordinator = exports.SynthesizerAgent = exports.AnalyzerAgent = exports.Agent = void 0;
|
|
4
|
+
// Base Agent class
|
|
5
|
+
class Agent {
|
|
6
|
+
name;
|
|
7
|
+
capabilities;
|
|
8
|
+
constructor(name, capabilities) {
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.capabilities = capabilities;
|
|
11
|
+
}
|
|
12
|
+
canHandle(task) {
|
|
13
|
+
return this.capabilities.some(cap => cap.inputTypes.includes(task.type));
|
|
14
|
+
}
|
|
15
|
+
getName() {
|
|
16
|
+
return this.name;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.Agent = Agent;
|
|
20
|
+
// Analyzer Agent - Analyzes context to extract insights
|
|
21
|
+
class AnalyzerAgent extends Agent {
|
|
22
|
+
db;
|
|
23
|
+
knowledgeGraph;
|
|
24
|
+
vectorStore;
|
|
25
|
+
constructor(db, knowledgeGraph, vectorStore) {
|
|
26
|
+
super('analyzer', [
|
|
27
|
+
{
|
|
28
|
+
name: 'pattern_detection',
|
|
29
|
+
description: 'Detect patterns in saved context',
|
|
30
|
+
inputTypes: ['analyze'],
|
|
31
|
+
outputTypes: ['patterns', 'insights'],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'relationship_extraction',
|
|
35
|
+
description: 'Extract relationships between entities',
|
|
36
|
+
inputTypes: ['analyze'],
|
|
37
|
+
outputTypes: ['relationships'],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'trend_analysis',
|
|
41
|
+
description: 'Analyze trends over time',
|
|
42
|
+
inputTypes: ['analyze'],
|
|
43
|
+
outputTypes: ['trends'],
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
this.db = db;
|
|
47
|
+
this.knowledgeGraph = knowledgeGraph;
|
|
48
|
+
this.vectorStore = vectorStore;
|
|
49
|
+
}
|
|
50
|
+
async process(task) {
|
|
51
|
+
const startTime = Date.now();
|
|
52
|
+
switch (task.input.analysisType) {
|
|
53
|
+
case 'patterns':
|
|
54
|
+
return this.analyzePatterns(task, startTime);
|
|
55
|
+
case 'relationships':
|
|
56
|
+
return this.analyzeRelationships(task, startTime);
|
|
57
|
+
case 'trends':
|
|
58
|
+
return this.analyzeTrends(task, startTime);
|
|
59
|
+
case 'comprehensive':
|
|
60
|
+
return this.comprehensiveAnalysis(task, startTime);
|
|
61
|
+
default:
|
|
62
|
+
return {
|
|
63
|
+
taskId: task.id,
|
|
64
|
+
agentType: this.name,
|
|
65
|
+
output: { error: 'Unknown analysis type' },
|
|
66
|
+
confidence: 0,
|
|
67
|
+
processingTime: Date.now() - startTime,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async analyzePatterns(task, startTime) {
|
|
72
|
+
const { sessionId, categories, timeframe } = task.input;
|
|
73
|
+
// Get context items
|
|
74
|
+
let query = `
|
|
75
|
+
SELECT key, value, category, priority, created_at
|
|
76
|
+
FROM context_items
|
|
77
|
+
WHERE session_id = ?
|
|
78
|
+
`;
|
|
79
|
+
const params = [sessionId];
|
|
80
|
+
if (categories && categories.length > 0) {
|
|
81
|
+
query += ` AND category IN (${categories.map(() => '?').join(',')})`;
|
|
82
|
+
params.push(...categories);
|
|
83
|
+
}
|
|
84
|
+
if (timeframe) {
|
|
85
|
+
query += ` AND created_at >= datetime('now', ?)`;
|
|
86
|
+
params.push(timeframe);
|
|
87
|
+
}
|
|
88
|
+
const items = this.db.prepare(query).all(...params);
|
|
89
|
+
// Analyze patterns
|
|
90
|
+
const patterns = {
|
|
91
|
+
categoryDistribution: this.getCategoryDistribution(items),
|
|
92
|
+
priorityDistribution: this.getPriorityDistribution(items),
|
|
93
|
+
temporalPatterns: this.getTemporalPatterns(items),
|
|
94
|
+
keywordFrequency: this.getKeywordFrequency(items),
|
|
95
|
+
workflowPatterns: this.detectWorkflowPatterns(items),
|
|
96
|
+
};
|
|
97
|
+
const confidence = items.length > 10 ? 0.9 : 0.7;
|
|
98
|
+
return {
|
|
99
|
+
taskId: task.id,
|
|
100
|
+
agentType: this.name,
|
|
101
|
+
output: {
|
|
102
|
+
patterns,
|
|
103
|
+
itemCount: items.length,
|
|
104
|
+
insights: this.generatePatternInsights(patterns),
|
|
105
|
+
},
|
|
106
|
+
confidence,
|
|
107
|
+
reasoning: `Analyzed ${items.length} context items to identify patterns`,
|
|
108
|
+
processingTime: Date.now() - startTime,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async analyzeRelationships(task, startTime) {
|
|
112
|
+
const { sessionId, entityType, _maxDepth = 2 } = task.input;
|
|
113
|
+
// Get entities and relationships from knowledge graph
|
|
114
|
+
let entityQuery = 'SELECT * FROM entities WHERE session_id = ?';
|
|
115
|
+
const entityParams = [sessionId];
|
|
116
|
+
if (entityType) {
|
|
117
|
+
entityQuery += ' AND type = ?';
|
|
118
|
+
entityParams.push(entityType);
|
|
119
|
+
}
|
|
120
|
+
const entities = this.db.prepare(entityQuery).all(...entityParams);
|
|
121
|
+
const relationships = [];
|
|
122
|
+
for (const entity of entities) {
|
|
123
|
+
const relations = this.db
|
|
124
|
+
.prepare(`
|
|
125
|
+
SELECT r.*, e.name as object_name, e.type as object_type
|
|
126
|
+
FROM relations r
|
|
127
|
+
JOIN entities e ON r.object_id = e.id
|
|
128
|
+
WHERE r.subject_id = ?
|
|
129
|
+
`)
|
|
130
|
+
.all(entity.id);
|
|
131
|
+
relationships.push({
|
|
132
|
+
entity,
|
|
133
|
+
relationships: relations.map(r => ({
|
|
134
|
+
predicate: r.predicate,
|
|
135
|
+
objectId: r.object_id,
|
|
136
|
+
objectName: r.object_name,
|
|
137
|
+
objectType: r.object_type,
|
|
138
|
+
confidence: r.confidence,
|
|
139
|
+
})),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Analyze relationship patterns
|
|
143
|
+
const analysis = {
|
|
144
|
+
entityCount: entities.length,
|
|
145
|
+
relationshipTypes: this.getRelationshipTypes(relationships),
|
|
146
|
+
clusters: this.detectClusters(relationships),
|
|
147
|
+
centralNodes: this.findCentralNodes(relationships),
|
|
148
|
+
isolatedEntities: this.findIsolatedEntities(entities, relationships),
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
taskId: task.id,
|
|
152
|
+
agentType: this.name,
|
|
153
|
+
output: {
|
|
154
|
+
analysis,
|
|
155
|
+
recommendations: this.generateRelationshipRecommendations(analysis),
|
|
156
|
+
},
|
|
157
|
+
confidence: 0.85,
|
|
158
|
+
reasoning: `Analyzed ${entities.length} entities and their relationships`,
|
|
159
|
+
processingTime: Date.now() - startTime,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async analyzeTrends(task, startTime) {
|
|
163
|
+
const { sessionId, metric: _metric, timeframe = '-7 days' } = task.input;
|
|
164
|
+
// Get time-series data
|
|
165
|
+
const query = `
|
|
166
|
+
SELECT
|
|
167
|
+
date(created_at) as date,
|
|
168
|
+
COUNT(*) as count,
|
|
169
|
+
category,
|
|
170
|
+
priority
|
|
171
|
+
FROM context_items
|
|
172
|
+
WHERE session_id = ?
|
|
173
|
+
AND created_at >= datetime('now', ?)
|
|
174
|
+
GROUP BY date(created_at), category, priority
|
|
175
|
+
ORDER BY date
|
|
176
|
+
`;
|
|
177
|
+
const trends = this.db.prepare(query).all(sessionId, timeframe);
|
|
178
|
+
// Analyze trends
|
|
179
|
+
const analysis = {
|
|
180
|
+
activityTrend: this.calculateActivityTrend(trends),
|
|
181
|
+
categoryTrends: this.calculateCategoryTrends(trends),
|
|
182
|
+
priorityShifts: this.detectPriorityShifts(trends),
|
|
183
|
+
predictions: this.generatePredictions(trends),
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
taskId: task.id,
|
|
187
|
+
agentType: this.name,
|
|
188
|
+
output: {
|
|
189
|
+
trends: analysis,
|
|
190
|
+
summary: this.generateTrendSummary(analysis),
|
|
191
|
+
},
|
|
192
|
+
confidence: 0.8,
|
|
193
|
+
reasoning: `Analyzed trends over ${timeframe} period`,
|
|
194
|
+
processingTime: Date.now() - startTime,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async comprehensiveAnalysis(task, startTime) {
|
|
198
|
+
// Run all analysis types
|
|
199
|
+
const [patterns, relationships, trends] = await Promise.all([
|
|
200
|
+
this.analyzePatterns({ ...task, input: { ...task.input, analysisType: 'patterns' } }, Date.now()),
|
|
201
|
+
this.analyzeRelationships({ ...task, input: { ...task.input, analysisType: 'relationships' } }, Date.now()),
|
|
202
|
+
this.analyzeTrends({ ...task, input: { ...task.input, analysisType: 'trends' } }, Date.now()),
|
|
203
|
+
]);
|
|
204
|
+
return {
|
|
205
|
+
taskId: task.id,
|
|
206
|
+
agentType: this.name,
|
|
207
|
+
output: {
|
|
208
|
+
patterns: patterns.output,
|
|
209
|
+
relationships: relationships.output,
|
|
210
|
+
trends: trends.output,
|
|
211
|
+
overallInsights: this.generateOverallInsights(patterns.output, relationships.output, trends.output),
|
|
212
|
+
},
|
|
213
|
+
confidence: 0.9,
|
|
214
|
+
reasoning: 'Performed comprehensive analysis across patterns, relationships, and trends',
|
|
215
|
+
processingTime: Date.now() - startTime,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// Helper methods
|
|
219
|
+
getCategoryDistribution(items) {
|
|
220
|
+
const distribution = {};
|
|
221
|
+
for (const item of items) {
|
|
222
|
+
const category = item.category || 'uncategorized';
|
|
223
|
+
distribution[category] = (distribution[category] || 0) + 1;
|
|
224
|
+
}
|
|
225
|
+
return distribution;
|
|
226
|
+
}
|
|
227
|
+
getPriorityDistribution(items) {
|
|
228
|
+
const distribution = {};
|
|
229
|
+
for (const item of items) {
|
|
230
|
+
const priority = item.priority || 'normal';
|
|
231
|
+
distribution[priority] = (distribution[priority] || 0) + 1;
|
|
232
|
+
}
|
|
233
|
+
return distribution;
|
|
234
|
+
}
|
|
235
|
+
getTemporalPatterns(items) {
|
|
236
|
+
// Group by hour of day and day of week
|
|
237
|
+
const hourly = {};
|
|
238
|
+
const daily = {};
|
|
239
|
+
for (const item of items) {
|
|
240
|
+
const date = new Date(item.created_at);
|
|
241
|
+
const hour = date.getHours();
|
|
242
|
+
const day = date.toLocaleDateString('en-US', { weekday: 'long' });
|
|
243
|
+
hourly[hour] = (hourly[hour] || 0) + 1;
|
|
244
|
+
daily[day] = (daily[day] || 0) + 1;
|
|
245
|
+
}
|
|
246
|
+
return { hourly, daily };
|
|
247
|
+
}
|
|
248
|
+
getKeywordFrequency(items) {
|
|
249
|
+
const frequency = {};
|
|
250
|
+
const stopWords = new Set([
|
|
251
|
+
'the',
|
|
252
|
+
'a',
|
|
253
|
+
'an',
|
|
254
|
+
'and',
|
|
255
|
+
'or',
|
|
256
|
+
'but',
|
|
257
|
+
'in',
|
|
258
|
+
'on',
|
|
259
|
+
'at',
|
|
260
|
+
'to',
|
|
261
|
+
'for',
|
|
262
|
+
]);
|
|
263
|
+
for (const item of items) {
|
|
264
|
+
const text = `${item.key} ${item.value}`.toLowerCase();
|
|
265
|
+
const words = text.match(/\b\w+\b/g) || [];
|
|
266
|
+
for (const word of words) {
|
|
267
|
+
if (word.length > 3 && !stopWords.has(word)) {
|
|
268
|
+
frequency[word] = (frequency[word] || 0) + 1;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Return top 20 keywords
|
|
273
|
+
return Object.fromEntries(Object.entries(frequency)
|
|
274
|
+
.sort(([, a], [, b]) => b - a)
|
|
275
|
+
.slice(0, 20));
|
|
276
|
+
}
|
|
277
|
+
detectWorkflowPatterns(items) {
|
|
278
|
+
// Simple workflow detection based on temporal ordering and categories
|
|
279
|
+
const workflows = [];
|
|
280
|
+
const sortedItems = [...items].sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
|
|
281
|
+
let currentWorkflow = [];
|
|
282
|
+
let lastCategory = '';
|
|
283
|
+
for (const item of sortedItems) {
|
|
284
|
+
if (item.category !== lastCategory && currentWorkflow.length > 0) {
|
|
285
|
+
workflows.push({
|
|
286
|
+
steps: currentWorkflow.length,
|
|
287
|
+
categories: [...new Set(currentWorkflow.map(i => i.category))],
|
|
288
|
+
duration: new Date(currentWorkflow[currentWorkflow.length - 1].created_at).getTime() -
|
|
289
|
+
new Date(currentWorkflow[0].created_at).getTime(),
|
|
290
|
+
});
|
|
291
|
+
currentWorkflow = [];
|
|
292
|
+
}
|
|
293
|
+
currentWorkflow.push(item);
|
|
294
|
+
lastCategory = item.category;
|
|
295
|
+
}
|
|
296
|
+
return workflows;
|
|
297
|
+
}
|
|
298
|
+
generatePatternInsights(patterns) {
|
|
299
|
+
const insights = [];
|
|
300
|
+
// Category insights
|
|
301
|
+
const topCategory = Object.entries(patterns.categoryDistribution).sort(([, a], [, b]) => b - a)[0];
|
|
302
|
+
if (topCategory) {
|
|
303
|
+
insights.push(`Most activity in '${topCategory[0]}' category (${topCategory[1]} items)`);
|
|
304
|
+
}
|
|
305
|
+
// Priority insights
|
|
306
|
+
if (patterns.priorityDistribution.high > patterns.priorityDistribution.normal) {
|
|
307
|
+
insights.push('High concentration of high-priority items - consider delegating or breaking down tasks');
|
|
308
|
+
}
|
|
309
|
+
// Temporal insights
|
|
310
|
+
const peakHour = Object.entries(patterns.temporalPatterns.hourly).sort(([, a], [, b]) => b - a)[0];
|
|
311
|
+
if (peakHour) {
|
|
312
|
+
insights.push(`Peak activity at ${peakHour[0]}:00 hours`);
|
|
313
|
+
}
|
|
314
|
+
return insights;
|
|
315
|
+
}
|
|
316
|
+
getRelationshipTypes(relationships) {
|
|
317
|
+
const types = {};
|
|
318
|
+
for (const rel of relationships) {
|
|
319
|
+
for (const r of rel.relationships) {
|
|
320
|
+
types[r.predicate] = (types[r.predicate] || 0) + 1;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return types;
|
|
324
|
+
}
|
|
325
|
+
detectClusters(relationships) {
|
|
326
|
+
// Simple clustering based on connectivity
|
|
327
|
+
const clusters = [];
|
|
328
|
+
const visited = new Set();
|
|
329
|
+
for (const rel of relationships) {
|
|
330
|
+
if (!visited.has(rel.entity.id)) {
|
|
331
|
+
const cluster = this.buildCluster(rel.entity.id, relationships, visited);
|
|
332
|
+
if (cluster.size > 1) {
|
|
333
|
+
clusters.push(cluster);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return clusters;
|
|
338
|
+
}
|
|
339
|
+
buildCluster(entityId, relationships, visited) {
|
|
340
|
+
const cluster = {
|
|
341
|
+
entities: [entityId],
|
|
342
|
+
size: 1,
|
|
343
|
+
};
|
|
344
|
+
visited.add(entityId);
|
|
345
|
+
const relatedEntities = relationships.find(r => r.entity.id === entityId)?.relationships || [];
|
|
346
|
+
for (const related of relatedEntities) {
|
|
347
|
+
if (!visited.has(related.objectId)) {
|
|
348
|
+
const subCluster = this.buildCluster(related.objectId, relationships, visited);
|
|
349
|
+
cluster.entities.push(...subCluster.entities);
|
|
350
|
+
cluster.size += subCluster.size;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return cluster;
|
|
354
|
+
}
|
|
355
|
+
findCentralNodes(relationships) {
|
|
356
|
+
const connectionCount = {};
|
|
357
|
+
for (const rel of relationships) {
|
|
358
|
+
connectionCount[rel.entity.id] = rel.relationships.length;
|
|
359
|
+
}
|
|
360
|
+
return Object.entries(connectionCount)
|
|
361
|
+
.sort(([, a], [, b]) => b - a)
|
|
362
|
+
.slice(0, 5)
|
|
363
|
+
.map(([id, count]) => ({ entityId: id, connections: count }));
|
|
364
|
+
}
|
|
365
|
+
findIsolatedEntities(entities, relationships) {
|
|
366
|
+
const connected = new Set(relationships.map(r => r.entity.id));
|
|
367
|
+
return entities.filter(e => !connected.has(e.id));
|
|
368
|
+
}
|
|
369
|
+
generateRelationshipRecommendations(analysis) {
|
|
370
|
+
const recommendations = [];
|
|
371
|
+
if (analysis.isolatedEntities.length > 0) {
|
|
372
|
+
recommendations.push(`${analysis.isolatedEntities.length} isolated entities found - consider documenting their relationships`);
|
|
373
|
+
}
|
|
374
|
+
if (analysis.clusters.length > 1) {
|
|
375
|
+
recommendations.push(`${analysis.clusters.length} separate clusters detected - look for cross-cluster connections`);
|
|
376
|
+
}
|
|
377
|
+
if (analysis.centralNodes.length > 0) {
|
|
378
|
+
recommendations.push(`Key entities: ${analysis.centralNodes
|
|
379
|
+
.slice(0, 3)
|
|
380
|
+
.map((n) => n.entityId)
|
|
381
|
+
.join(', ')}`);
|
|
382
|
+
}
|
|
383
|
+
return recommendations;
|
|
384
|
+
}
|
|
385
|
+
calculateActivityTrend(trends) {
|
|
386
|
+
const dailyCounts = trends.reduce((acc, item) => {
|
|
387
|
+
acc[item.date] = (acc[item.date] || 0) + item.count;
|
|
388
|
+
return acc;
|
|
389
|
+
}, {});
|
|
390
|
+
const values = Object.values(dailyCounts);
|
|
391
|
+
const avg = values.reduce((a, b) => a + b, 0) / values.length;
|
|
392
|
+
const recent = values.slice(-3).reduce((a, b) => a + b, 0) / 3;
|
|
393
|
+
return {
|
|
394
|
+
direction: recent > avg ? 'increasing' : recent < avg ? 'decreasing' : 'stable',
|
|
395
|
+
averageDaily: avg,
|
|
396
|
+
recentDaily: recent,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
calculateCategoryTrends(trends) {
|
|
400
|
+
const categoryTrends = {};
|
|
401
|
+
for (const trend of trends) {
|
|
402
|
+
if (!categoryTrends[trend.category]) {
|
|
403
|
+
categoryTrends[trend.category] = { dates: [], counts: [] };
|
|
404
|
+
}
|
|
405
|
+
categoryTrends[trend.category].dates.push(trend.date);
|
|
406
|
+
categoryTrends[trend.category].counts.push(trend.count);
|
|
407
|
+
}
|
|
408
|
+
return categoryTrends;
|
|
409
|
+
}
|
|
410
|
+
detectPriorityShifts(trends) {
|
|
411
|
+
const priorityByDate = {};
|
|
412
|
+
for (const trend of trends) {
|
|
413
|
+
if (!priorityByDate[trend.date]) {
|
|
414
|
+
priorityByDate[trend.date] = {};
|
|
415
|
+
}
|
|
416
|
+
priorityByDate[trend.date][trend.priority] = trend.count;
|
|
417
|
+
}
|
|
418
|
+
return priorityByDate;
|
|
419
|
+
}
|
|
420
|
+
generatePredictions(trends) {
|
|
421
|
+
// Simple linear prediction
|
|
422
|
+
return {
|
|
423
|
+
nextDayEstimate: Math.round(trends[trends.length - 1]?.count * 1.1 || 0),
|
|
424
|
+
confidence: 0.6,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
generateTrendSummary(analysis) {
|
|
428
|
+
return `Activity is ${analysis.activityTrend.direction} with an average of ${analysis.activityTrend.averageDaily.toFixed(1)} items per day`;
|
|
429
|
+
}
|
|
430
|
+
generateOverallInsights(patterns, relationships, trends) {
|
|
431
|
+
return [
|
|
432
|
+
...(patterns.insights || []),
|
|
433
|
+
...(relationships.recommendations || []),
|
|
434
|
+
trends.summary || '',
|
|
435
|
+
].filter(Boolean);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
exports.AnalyzerAgent = AnalyzerAgent;
|
|
439
|
+
// Synthesizer Agent - Synthesizes information from multiple sources
|
|
440
|
+
class SynthesizerAgent extends Agent {
|
|
441
|
+
db;
|
|
442
|
+
vectorStore;
|
|
443
|
+
constructor(db, vectorStore) {
|
|
444
|
+
super('synthesizer', [
|
|
445
|
+
{
|
|
446
|
+
name: 'summarization',
|
|
447
|
+
description: 'Create summaries from multiple context items',
|
|
448
|
+
inputTypes: ['synthesize'],
|
|
449
|
+
outputTypes: ['summary'],
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
name: 'merge_insights',
|
|
453
|
+
description: 'Merge insights from multiple agents',
|
|
454
|
+
inputTypes: ['synthesize'],
|
|
455
|
+
outputTypes: ['merged_insights'],
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: 'generate_recommendations',
|
|
459
|
+
description: 'Generate actionable recommendations',
|
|
460
|
+
inputTypes: ['synthesize'],
|
|
461
|
+
outputTypes: ['recommendations'],
|
|
462
|
+
},
|
|
463
|
+
]);
|
|
464
|
+
this.db = db;
|
|
465
|
+
this.vectorStore = vectorStore;
|
|
466
|
+
}
|
|
467
|
+
async process(task) {
|
|
468
|
+
const startTime = Date.now();
|
|
469
|
+
switch (task.input.synthesisType) {
|
|
470
|
+
case 'summary':
|
|
471
|
+
return this.createSummary(task, startTime);
|
|
472
|
+
case 'merge':
|
|
473
|
+
return this.mergeInsights(task, startTime);
|
|
474
|
+
case 'recommendations':
|
|
475
|
+
return this.generateRecommendations(task, startTime);
|
|
476
|
+
default:
|
|
477
|
+
return {
|
|
478
|
+
taskId: task.id,
|
|
479
|
+
agentType: this.name,
|
|
480
|
+
output: { error: 'Unknown synthesis type' },
|
|
481
|
+
confidence: 0,
|
|
482
|
+
processingTime: Date.now() - startTime,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
async createSummary(task, startTime) {
|
|
487
|
+
const { sessionId, categories, maxLength = 1000 } = task.input;
|
|
488
|
+
// Get relevant context items
|
|
489
|
+
let query = `
|
|
490
|
+
SELECT key, value, category, priority, created_at
|
|
491
|
+
FROM context_items
|
|
492
|
+
WHERE session_id = ?
|
|
493
|
+
`;
|
|
494
|
+
const params = [sessionId];
|
|
495
|
+
if (categories && categories.length > 0) {
|
|
496
|
+
query += ` AND category IN (${categories.map(() => '?').join(',')})`;
|
|
497
|
+
params.push(...categories);
|
|
498
|
+
}
|
|
499
|
+
query += ` ORDER BY priority DESC, created_at DESC LIMIT 50`;
|
|
500
|
+
const items = this.db.prepare(query).all(...params);
|
|
501
|
+
// Create structured summary
|
|
502
|
+
const summary = {
|
|
503
|
+
overview: this.createOverview(items),
|
|
504
|
+
byCategory: this.summarizeByCategory(items),
|
|
505
|
+
keyDecisions: this.extractKeyDecisions(items),
|
|
506
|
+
currentTasks: this.extractCurrentTasks(items),
|
|
507
|
+
recentProgress: this.extractRecentProgress(items),
|
|
508
|
+
};
|
|
509
|
+
// Trim to maxLength
|
|
510
|
+
const formattedSummary = this.formatSummary(summary, maxLength);
|
|
511
|
+
return {
|
|
512
|
+
taskId: task.id,
|
|
513
|
+
agentType: this.name,
|
|
514
|
+
output: {
|
|
515
|
+
summary: formattedSummary,
|
|
516
|
+
itemCount: items.length,
|
|
517
|
+
categories: [...new Set(items.map((i) => i.category))].filter(Boolean),
|
|
518
|
+
},
|
|
519
|
+
confidence: 0.85,
|
|
520
|
+
reasoning: `Synthesized ${items.length} items into structured summary`,
|
|
521
|
+
processingTime: Date.now() - startTime,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
async mergeInsights(task, startTime) {
|
|
525
|
+
const { insights } = task.input;
|
|
526
|
+
if (!Array.isArray(insights) || insights.length === 0) {
|
|
527
|
+
return {
|
|
528
|
+
taskId: task.id,
|
|
529
|
+
agentType: this.name,
|
|
530
|
+
output: { error: 'No insights provided to merge' },
|
|
531
|
+
confidence: 0,
|
|
532
|
+
processingTime: Date.now() - startTime,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
// Merge and deduplicate insights
|
|
536
|
+
const merged = {
|
|
537
|
+
patterns: this.mergePatterns(insights),
|
|
538
|
+
relationships: this.mergeRelationships(insights),
|
|
539
|
+
trends: this.mergeTrends(insights),
|
|
540
|
+
overallThemes: this.identifyThemes(insights),
|
|
541
|
+
conflicts: this.identifyConflicts(insights),
|
|
542
|
+
};
|
|
543
|
+
return {
|
|
544
|
+
taskId: task.id,
|
|
545
|
+
agentType: this.name,
|
|
546
|
+
output: merged,
|
|
547
|
+
confidence: 0.8,
|
|
548
|
+
reasoning: `Merged ${insights.length} insight sources`,
|
|
549
|
+
processingTime: Date.now() - startTime,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
async generateRecommendations(task, startTime) {
|
|
553
|
+
const { analysisResults, context: _context } = task.input;
|
|
554
|
+
// If no analysisResults provided, try to extract from context (for chaining)
|
|
555
|
+
let analysis = analysisResults;
|
|
556
|
+
if (!analysis && task.context) {
|
|
557
|
+
// Extract analysis data from previous agent output
|
|
558
|
+
if (task.context.patterns || task.context.trends) {
|
|
559
|
+
analysis = {
|
|
560
|
+
highPriorityCount: task.context.patterns?.priorityDistribution?.high || 0,
|
|
561
|
+
contextSize: task.context.itemCount || 0,
|
|
562
|
+
staleTasks: false,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const recommendations = {
|
|
567
|
+
immediate: this.getImmediateRecommendations(analysis),
|
|
568
|
+
shortTerm: this.getShortTermRecommendations(analysis),
|
|
569
|
+
longTerm: this.getLongTermRecommendations(analysis),
|
|
570
|
+
warnings: this.getWarnings(analysis),
|
|
571
|
+
};
|
|
572
|
+
return {
|
|
573
|
+
taskId: task.id,
|
|
574
|
+
agentType: this.name,
|
|
575
|
+
output: recommendations,
|
|
576
|
+
confidence: 0.75,
|
|
577
|
+
reasoning: 'Generated recommendations based on analysis results',
|
|
578
|
+
processingTime: Date.now() - startTime,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
// Helper methods
|
|
582
|
+
createOverview(items) {
|
|
583
|
+
const totalItems = items.length;
|
|
584
|
+
const highPriority = items.filter(i => i.priority === 'high').length;
|
|
585
|
+
const categories = [...new Set(items.map(i => i.category))].filter(Boolean);
|
|
586
|
+
return `${totalItems} context items across ${categories.length} categories, with ${highPriority} high-priority items`;
|
|
587
|
+
}
|
|
588
|
+
summarizeByCategory(items) {
|
|
589
|
+
const byCategory = {};
|
|
590
|
+
for (const item of items) {
|
|
591
|
+
const category = item.category || 'uncategorized';
|
|
592
|
+
if (!byCategory[category]) {
|
|
593
|
+
byCategory[category] = {
|
|
594
|
+
count: 0,
|
|
595
|
+
items: [],
|
|
596
|
+
priorities: { high: 0, normal: 0, low: 0 },
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
byCategory[category].count++;
|
|
600
|
+
byCategory[category].items.push({
|
|
601
|
+
key: item.key,
|
|
602
|
+
value: item.value.substring(0, 100) + (item.value.length > 100 ? '...' : ''),
|
|
603
|
+
});
|
|
604
|
+
byCategory[category].priorities[item.priority || 'normal']++;
|
|
605
|
+
}
|
|
606
|
+
return byCategory;
|
|
607
|
+
}
|
|
608
|
+
extractKeyDecisions(items) {
|
|
609
|
+
return items
|
|
610
|
+
.filter(i => i.category === 'decision' && i.priority === 'high')
|
|
611
|
+
.map(i => `${i.key}: ${i.value}`)
|
|
612
|
+
.slice(0, 5);
|
|
613
|
+
}
|
|
614
|
+
extractCurrentTasks(items) {
|
|
615
|
+
return items
|
|
616
|
+
.filter(i => i.category === 'task')
|
|
617
|
+
.sort((a, b) => {
|
|
618
|
+
const priorityOrder = { high: 0, normal: 1, low: 2 };
|
|
619
|
+
return ((priorityOrder[a.priority] || 1) -
|
|
620
|
+
(priorityOrder[b.priority] || 1));
|
|
621
|
+
})
|
|
622
|
+
.map(i => `[${i.priority}] ${i.key}: ${i.value}`)
|
|
623
|
+
.slice(0, 10);
|
|
624
|
+
}
|
|
625
|
+
extractRecentProgress(items) {
|
|
626
|
+
const oneDayAgo = new Date();
|
|
627
|
+
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
|
|
628
|
+
return items
|
|
629
|
+
.filter(i => i.category === 'progress' && new Date(i.created_at) > oneDayAgo)
|
|
630
|
+
.map(i => i.value)
|
|
631
|
+
.slice(0, 5);
|
|
632
|
+
}
|
|
633
|
+
formatSummary(summary, maxLength) {
|
|
634
|
+
let formatted = `# Context Summary\n\n${summary.overview}\n\n`;
|
|
635
|
+
if (summary.keyDecisions.length > 0) {
|
|
636
|
+
formatted += `## Key Decisions\n${summary.keyDecisions.map((d) => `- ${d}`).join('\n')}\n\n`;
|
|
637
|
+
}
|
|
638
|
+
if (summary.currentTasks.length > 0) {
|
|
639
|
+
formatted += `## Current Tasks\n${summary.currentTasks.map((t) => `- ${t}`).join('\n')}\n\n`;
|
|
640
|
+
}
|
|
641
|
+
if (summary.recentProgress.length > 0) {
|
|
642
|
+
formatted += `## Recent Progress\n${summary.recentProgress.map((p) => `- ${p}`).join('\n')}\n\n`;
|
|
643
|
+
}
|
|
644
|
+
// Trim if needed
|
|
645
|
+
if (formatted.length > maxLength) {
|
|
646
|
+
formatted = formatted.substring(0, maxLength - 3) + '...';
|
|
647
|
+
}
|
|
648
|
+
return formatted;
|
|
649
|
+
}
|
|
650
|
+
mergePatterns(insights) {
|
|
651
|
+
const merged = {};
|
|
652
|
+
for (const insight of insights) {
|
|
653
|
+
if (insight.patterns) {
|
|
654
|
+
Object.entries(insight.patterns).forEach(([key, value]) => {
|
|
655
|
+
if (!merged[key]) {
|
|
656
|
+
merged[key] = value;
|
|
657
|
+
}
|
|
658
|
+
else if (Array.isArray(value)) {
|
|
659
|
+
merged[key] = [...new Set([...merged[key], ...value])];
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return merged;
|
|
665
|
+
}
|
|
666
|
+
mergeRelationships(insights) {
|
|
667
|
+
const entities = new Map();
|
|
668
|
+
const relationships = new Map();
|
|
669
|
+
for (const insight of insights) {
|
|
670
|
+
if (insight.relationships) {
|
|
671
|
+
// Merge logic for relationships
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return { entities: entities.size, relationships: relationships.size };
|
|
675
|
+
}
|
|
676
|
+
mergeTrends(insights) {
|
|
677
|
+
const trends = [];
|
|
678
|
+
for (const insight of insights) {
|
|
679
|
+
if (insight.trends) {
|
|
680
|
+
trends.push(insight.trends);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return trends;
|
|
684
|
+
}
|
|
685
|
+
identifyThemes(insights) {
|
|
686
|
+
const themes = new Set();
|
|
687
|
+
// Extract common themes from insights
|
|
688
|
+
for (const insight of insights) {
|
|
689
|
+
if (insight &&
|
|
690
|
+
typeof insight === 'object' &&
|
|
691
|
+
insight.themes &&
|
|
692
|
+
Array.isArray(insight.themes)) {
|
|
693
|
+
insight.themes.forEach((theme) => themes.add(theme));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return Array.from(themes);
|
|
697
|
+
}
|
|
698
|
+
identifyConflicts(_insights) {
|
|
699
|
+
// Identify conflicting information between insights
|
|
700
|
+
return [];
|
|
701
|
+
}
|
|
702
|
+
getImmediateRecommendations(analysis) {
|
|
703
|
+
const recommendations = [];
|
|
704
|
+
if (analysis && analysis.highPriorityCount > 5) {
|
|
705
|
+
recommendations.push('Consider breaking down high-priority tasks into smaller items');
|
|
706
|
+
}
|
|
707
|
+
if (analysis && analysis.staleTasks) {
|
|
708
|
+
recommendations.push("Review and update stale tasks that haven't been touched recently");
|
|
709
|
+
}
|
|
710
|
+
return recommendations;
|
|
711
|
+
}
|
|
712
|
+
getShortTermRecommendations(_analysis) {
|
|
713
|
+
return ['Create checkpoints before major changes', "Document key decisions as they're made"];
|
|
714
|
+
}
|
|
715
|
+
getLongTermRecommendations(_analysis) {
|
|
716
|
+
return [
|
|
717
|
+
'Establish regular review cycles for context cleanup',
|
|
718
|
+
'Consider archiving old sessions',
|
|
719
|
+
];
|
|
720
|
+
}
|
|
721
|
+
getWarnings(analysis) {
|
|
722
|
+
const warnings = [];
|
|
723
|
+
if (analysis && analysis.contextSize > 1000) {
|
|
724
|
+
warnings.push('Context size is large - consider compaction');
|
|
725
|
+
}
|
|
726
|
+
return warnings;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
exports.SynthesizerAgent = SynthesizerAgent;
|
|
730
|
+
// Agent Coordinator - Manages multiple agents
|
|
731
|
+
class AgentCoordinator {
|
|
732
|
+
agents = new Map();
|
|
733
|
+
taskQueue = [];
|
|
734
|
+
results = new Map();
|
|
735
|
+
registerAgent(agent) {
|
|
736
|
+
this.agents.set(agent.getName(), agent);
|
|
737
|
+
}
|
|
738
|
+
async delegate(task) {
|
|
739
|
+
// Find suitable agents
|
|
740
|
+
const suitableAgents = Array.from(this.agents.values()).filter(agent => agent.canHandle(task));
|
|
741
|
+
if (suitableAgents.length === 0) {
|
|
742
|
+
return [
|
|
743
|
+
{
|
|
744
|
+
taskId: task.id,
|
|
745
|
+
agentType: 'coordinator',
|
|
746
|
+
output: { error: 'No suitable agent found for task' },
|
|
747
|
+
confidence: 0,
|
|
748
|
+
processingTime: 0,
|
|
749
|
+
},
|
|
750
|
+
];
|
|
751
|
+
}
|
|
752
|
+
// Process with all suitable agents in parallel
|
|
753
|
+
const results = await Promise.all(suitableAgents.map(agent => agent.process(task)));
|
|
754
|
+
// Store results
|
|
755
|
+
results.forEach(result => {
|
|
756
|
+
this.results.set(`${task.id}-${result.agentType}`, result);
|
|
757
|
+
});
|
|
758
|
+
return results;
|
|
759
|
+
}
|
|
760
|
+
async processChain(tasks) {
|
|
761
|
+
const chainResults = [];
|
|
762
|
+
let previousOutput = null;
|
|
763
|
+
for (const task of tasks) {
|
|
764
|
+
// Pass previous output as context
|
|
765
|
+
if (previousOutput) {
|
|
766
|
+
task.context = previousOutput;
|
|
767
|
+
}
|
|
768
|
+
const results = await this.delegate(task);
|
|
769
|
+
chainResults.push(...results);
|
|
770
|
+
// Use the highest confidence result as input for next task
|
|
771
|
+
previousOutput = results.reduce((best, current) => current.confidence > best.confidence ? current : best).output;
|
|
772
|
+
}
|
|
773
|
+
return chainResults;
|
|
774
|
+
}
|
|
775
|
+
getBestResult(taskId) {
|
|
776
|
+
const taskResults = Array.from(this.results.entries())
|
|
777
|
+
.filter(([key]) => key.startsWith(taskId))
|
|
778
|
+
.map(([, result]) => result);
|
|
779
|
+
if (taskResults.length === 0)
|
|
780
|
+
return null;
|
|
781
|
+
return taskResults.reduce((best, current) => current.confidence > best.confidence ? current : best);
|
|
782
|
+
}
|
|
783
|
+
getAgentCapabilities() {
|
|
784
|
+
const capabilities = {};
|
|
785
|
+
this.agents.forEach((agent, name) => {
|
|
786
|
+
capabilities[name] = agent.capabilities;
|
|
787
|
+
});
|
|
788
|
+
return capabilities;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
exports.AgentCoordinator = AgentCoordinator;
|