@girardmedia/bootspring 2.0.36 → 2.0.37
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/cli/plan.js +602 -2
- package/core/planning/adaptive-engine.js +958 -0
- package/core/planning/feature-decomposer.js +772 -0
- package/core/planning/index.js +49 -0
- package/core/planning/simulator.js +1328 -0
- package/core/planning/stage-planner.js +624 -0
- package/intelligence/agent-router.js +795 -0
- package/intelligence/index.js +10 -0
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intelligent Agent Router
|
|
3
|
+
*
|
|
4
|
+
* Analyzes tasks and routes them to the most appropriate agent(s).
|
|
5
|
+
* Supports single-agent routing and multi-agent collaboration planning.
|
|
6
|
+
*
|
|
7
|
+
* @package bootspring
|
|
8
|
+
* @module intelligence/agent-router
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs').promises;
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Expertise domains and their indicators
|
|
16
|
+
*/
|
|
17
|
+
const EXPERTISE_DOMAINS = {
|
|
18
|
+
database: {
|
|
19
|
+
name: 'Database & Data Modeling',
|
|
20
|
+
keywords: ['database', 'schema', 'model', 'migration', 'prisma', 'sql', 'table', 'relation', 'index', 'query'],
|
|
21
|
+
filePatterns: ['prisma/**', 'models/**', 'schema/**', 'migrations/**'],
|
|
22
|
+
agents: ['database-expert', 'prisma-expert', 'sql-expert']
|
|
23
|
+
},
|
|
24
|
+
security: {
|
|
25
|
+
name: 'Security & Authentication',
|
|
26
|
+
keywords: ['auth', 'security', 'password', 'token', 'jwt', 'session', 'permission', 'role', 'csrf', 'xss', 'encryption'],
|
|
27
|
+
filePatterns: ['**/auth/**', '**/security/**', 'middleware/**'],
|
|
28
|
+
agents: ['security-expert', 'auth-expert', 'crypto-expert']
|
|
29
|
+
},
|
|
30
|
+
frontend: {
|
|
31
|
+
name: 'Frontend & UI',
|
|
32
|
+
keywords: ['component', 'ui', 'react', 'vue', 'angular', 'css', 'style', 'responsive', 'animation', 'layout'],
|
|
33
|
+
filePatterns: ['components/**', 'pages/**', 'views/**', 'styles/**', '**/*.tsx', '**/*.jsx'],
|
|
34
|
+
agents: ['frontend-expert', 'react-expert', 'ui-expert', 'css-expert']
|
|
35
|
+
},
|
|
36
|
+
backend: {
|
|
37
|
+
name: 'Backend & API',
|
|
38
|
+
keywords: ['api', 'endpoint', 'route', 'controller', 'service', 'rest', 'graphql', 'server', 'middleware'],
|
|
39
|
+
filePatterns: ['api/**', 'routes/**', 'controllers/**', 'services/**'],
|
|
40
|
+
agents: ['backend-expert', 'api-expert', 'nodejs-expert']
|
|
41
|
+
},
|
|
42
|
+
testing: {
|
|
43
|
+
name: 'Testing & QA',
|
|
44
|
+
keywords: ['test', 'spec', 'mock', 'jest', 'vitest', 'cypress', 'coverage', 'e2e', 'unit', 'integration'],
|
|
45
|
+
filePatterns: ['**/*.test.*', '**/*.spec.*', '__tests__/**', 'tests/**'],
|
|
46
|
+
agents: ['testing-expert', 'qa-expert', 'e2e-expert']
|
|
47
|
+
},
|
|
48
|
+
devops: {
|
|
49
|
+
name: 'DevOps & Deployment',
|
|
50
|
+
keywords: ['deploy', 'docker', 'kubernetes', 'ci', 'cd', 'pipeline', 'aws', 'vercel', 'netlify', 'nginx'],
|
|
51
|
+
filePatterns: ['Dockerfile', 'docker-compose.*', '.github/**', 'deploy/**', 'infra/**'],
|
|
52
|
+
agents: ['devops-expert', 'docker-expert', 'aws-expert']
|
|
53
|
+
},
|
|
54
|
+
performance: {
|
|
55
|
+
name: 'Performance & Optimization',
|
|
56
|
+
keywords: ['performance', 'optimize', 'cache', 'lazy', 'bundle', 'speed', 'memory', 'profil', 'benchmark'],
|
|
57
|
+
filePatterns: [],
|
|
58
|
+
agents: ['performance-expert', 'optimization-expert']
|
|
59
|
+
},
|
|
60
|
+
architecture: {
|
|
61
|
+
name: 'Architecture & Design',
|
|
62
|
+
keywords: ['architecture', 'design', 'pattern', 'structure', 'refactor', 'module', 'layer', 'clean', 'solid'],
|
|
63
|
+
filePatterns: [],
|
|
64
|
+
agents: ['architect', 'design-expert', 'refactoring-expert']
|
|
65
|
+
},
|
|
66
|
+
payment: {
|
|
67
|
+
name: 'Payments & Commerce',
|
|
68
|
+
keywords: ['payment', 'stripe', 'checkout', 'subscription', 'billing', 'invoice', 'cart', 'order', 'commerce'],
|
|
69
|
+
filePatterns: ['**/payment/**', '**/checkout/**', '**/billing/**'],
|
|
70
|
+
agents: ['payment-expert', 'stripe-expert', 'ecommerce-expert']
|
|
71
|
+
},
|
|
72
|
+
realtime: {
|
|
73
|
+
name: 'Real-time & WebSockets',
|
|
74
|
+
keywords: ['websocket', 'socket', 'realtime', 'live', 'stream', 'push', 'notification', 'sse'],
|
|
75
|
+
filePatterns: ['**/socket/**', '**/realtime/**', '**/ws/**'],
|
|
76
|
+
agents: ['realtime-expert', 'websocket-expert']
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Complexity assessment factors
|
|
82
|
+
*/
|
|
83
|
+
const COMPLEXITY_FACTORS = {
|
|
84
|
+
scope: {
|
|
85
|
+
single_file: 1,
|
|
86
|
+
few_files: 2,
|
|
87
|
+
module: 3,
|
|
88
|
+
cross_module: 4,
|
|
89
|
+
system_wide: 5
|
|
90
|
+
},
|
|
91
|
+
expertise: {
|
|
92
|
+
single_domain: 1,
|
|
93
|
+
two_domains: 2,
|
|
94
|
+
multi_domain: 3,
|
|
95
|
+
specialized: 4
|
|
96
|
+
},
|
|
97
|
+
risk: {
|
|
98
|
+
low: 1,
|
|
99
|
+
medium: 2,
|
|
100
|
+
high: 3,
|
|
101
|
+
critical: 4
|
|
102
|
+
},
|
|
103
|
+
dependencies: {
|
|
104
|
+
none: 1,
|
|
105
|
+
few: 2,
|
|
106
|
+
many: 3,
|
|
107
|
+
complex: 4
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Collaboration patterns
|
|
113
|
+
*/
|
|
114
|
+
const COLLABORATION_PATTERNS = {
|
|
115
|
+
sequential: {
|
|
116
|
+
name: 'Sequential',
|
|
117
|
+
description: 'Agents work one after another, passing results forward',
|
|
118
|
+
bestFor: ['layered implementations', 'dependency chains', 'build pipelines']
|
|
119
|
+
},
|
|
120
|
+
parallel: {
|
|
121
|
+
name: 'Parallel',
|
|
122
|
+
description: 'Agents work simultaneously on independent parts',
|
|
123
|
+
bestFor: ['independent components', 'multi-module features', 'test suites']
|
|
124
|
+
},
|
|
125
|
+
iterative: {
|
|
126
|
+
name: 'Iterative',
|
|
127
|
+
description: 'Agents collaborate in review cycles until quality met',
|
|
128
|
+
bestFor: ['security reviews', 'performance optimization', 'refactoring']
|
|
129
|
+
},
|
|
130
|
+
hierarchical: {
|
|
131
|
+
name: 'Hierarchical',
|
|
132
|
+
description: 'Lead agent coordinates specialist agents',
|
|
133
|
+
bestFor: ['complex features', 'full-stack implementations', 'major refactors']
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* IntelligentAgentRouter class
|
|
139
|
+
*/
|
|
140
|
+
class IntelligentAgentRouter {
|
|
141
|
+
/**
|
|
142
|
+
* @param {Object} options - Configuration options
|
|
143
|
+
*/
|
|
144
|
+
constructor(options = {}) {
|
|
145
|
+
this.projectRoot = options.projectRoot || process.cwd();
|
|
146
|
+
this.agentsDir = options.agentsDir || path.join(this.projectRoot, 'agents');
|
|
147
|
+
this.availableAgents = [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Initialize the router by loading available agents
|
|
152
|
+
*/
|
|
153
|
+
async initialize() {
|
|
154
|
+
await this.loadAvailableAgents();
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Load available agents from the agents directory
|
|
160
|
+
*/
|
|
161
|
+
async loadAvailableAgents() {
|
|
162
|
+
try {
|
|
163
|
+
const files = await fs.readdir(this.agentsDir);
|
|
164
|
+
const agentFiles = files.filter(f => f.endsWith('.json'));
|
|
165
|
+
|
|
166
|
+
this.availableAgents = [];
|
|
167
|
+
for (const file of agentFiles) {
|
|
168
|
+
try {
|
|
169
|
+
const content = await fs.readFile(path.join(this.agentsDir, file), 'utf-8');
|
|
170
|
+
const agent = JSON.parse(content);
|
|
171
|
+
this.availableAgents.push({
|
|
172
|
+
id: path.basename(file, '.json'),
|
|
173
|
+
...agent
|
|
174
|
+
});
|
|
175
|
+
} catch {
|
|
176
|
+
// Skip invalid agent files
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
this.availableAgents = [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Route a task to appropriate agent(s)
|
|
186
|
+
* @param {string} task - Task description
|
|
187
|
+
* @param {Object} context - Additional context
|
|
188
|
+
*/
|
|
189
|
+
async route(task, context = {}) {
|
|
190
|
+
// Analyze the task
|
|
191
|
+
const complexity = await this.assessComplexity(task, context);
|
|
192
|
+
const expertise = await this.identifyExpertise(task, context);
|
|
193
|
+
|
|
194
|
+
// Check if collaboration is needed
|
|
195
|
+
const needsCollaboration = this.needsMultipleAgents(task, expertise, complexity);
|
|
196
|
+
|
|
197
|
+
if (needsCollaboration) {
|
|
198
|
+
return this.planAgentCollaboration(task, expertise, complexity, context);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Single agent routing
|
|
202
|
+
return this.routeToSingleAgent(task, expertise, complexity, context);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Assess task complexity
|
|
207
|
+
* @param {string} task - Task description
|
|
208
|
+
* @param {Object} context - Additional context
|
|
209
|
+
*/
|
|
210
|
+
async assessComplexity(task, context = {}) {
|
|
211
|
+
const analysis = {
|
|
212
|
+
score: 0,
|
|
213
|
+
factors: {},
|
|
214
|
+
level: 'low'
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const taskLower = task.toLowerCase();
|
|
218
|
+
|
|
219
|
+
// Assess scope
|
|
220
|
+
if (taskLower.includes('entire') || taskLower.includes('all') || taskLower.includes('system')) {
|
|
221
|
+
analysis.factors.scope = COMPLEXITY_FACTORS.scope.system_wide;
|
|
222
|
+
} else if (taskLower.includes('module') || taskLower.includes('feature')) {
|
|
223
|
+
analysis.factors.scope = COMPLEXITY_FACTORS.scope.cross_module;
|
|
224
|
+
} else if (taskLower.includes('component') || taskLower.includes('file')) {
|
|
225
|
+
analysis.factors.scope = COMPLEXITY_FACTORS.scope.few_files;
|
|
226
|
+
} else {
|
|
227
|
+
analysis.factors.scope = COMPLEXITY_FACTORS.scope.single_file;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Assess expertise breadth
|
|
231
|
+
const expertiseAreas = this.detectExpertiseAreas(task);
|
|
232
|
+
if (expertiseAreas.length > 2) {
|
|
233
|
+
analysis.factors.expertise = COMPLEXITY_FACTORS.expertise.multi_domain;
|
|
234
|
+
} else if (expertiseAreas.length === 2) {
|
|
235
|
+
analysis.factors.expertise = COMPLEXITY_FACTORS.expertise.two_domains;
|
|
236
|
+
} else {
|
|
237
|
+
analysis.factors.expertise = COMPLEXITY_FACTORS.expertise.single_domain;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Assess risk
|
|
241
|
+
const riskKeywords = ['security', 'payment', 'data', 'migration', 'production', 'delete', 'remove'];
|
|
242
|
+
const hasRiskKeywords = riskKeywords.some(kw => taskLower.includes(kw));
|
|
243
|
+
if (hasRiskKeywords) {
|
|
244
|
+
analysis.factors.risk = COMPLEXITY_FACTORS.risk.high;
|
|
245
|
+
} else if (taskLower.includes('refactor') || taskLower.includes('change')) {
|
|
246
|
+
analysis.factors.risk = COMPLEXITY_FACTORS.risk.medium;
|
|
247
|
+
} else {
|
|
248
|
+
analysis.factors.risk = COMPLEXITY_FACTORS.risk.low;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Assess dependencies
|
|
252
|
+
const depKeywords = ['integrate', 'connect', 'sync', 'depend', 'require'];
|
|
253
|
+
if (depKeywords.some(kw => taskLower.includes(kw))) {
|
|
254
|
+
analysis.factors.dependencies = COMPLEXITY_FACTORS.dependencies.many;
|
|
255
|
+
} else {
|
|
256
|
+
analysis.factors.dependencies = COMPLEXITY_FACTORS.dependencies.few;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Calculate total score
|
|
260
|
+
analysis.score = Object.values(analysis.factors).reduce((sum, val) => sum + val, 0);
|
|
261
|
+
|
|
262
|
+
// Determine level
|
|
263
|
+
if (analysis.score >= 12) {
|
|
264
|
+
analysis.level = 'very_high';
|
|
265
|
+
} else if (analysis.score >= 9) {
|
|
266
|
+
analysis.level = 'high';
|
|
267
|
+
} else if (analysis.score >= 6) {
|
|
268
|
+
analysis.level = 'medium';
|
|
269
|
+
} else {
|
|
270
|
+
analysis.level = 'low';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Determine context needed
|
|
274
|
+
analysis.contextNeeded = analysis.level === 'very_high' ? 'deep' :
|
|
275
|
+
analysis.level === 'high' ? 'standard' : 'minimal';
|
|
276
|
+
|
|
277
|
+
return analysis;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Detect expertise areas from task
|
|
282
|
+
* @param {string} task - Task description
|
|
283
|
+
*/
|
|
284
|
+
detectExpertiseAreas(task) {
|
|
285
|
+
const taskLower = task.toLowerCase();
|
|
286
|
+
const areas = [];
|
|
287
|
+
|
|
288
|
+
for (const [domain, config] of Object.entries(EXPERTISE_DOMAINS)) {
|
|
289
|
+
const matchCount = config.keywords.filter(kw => taskLower.includes(kw)).length;
|
|
290
|
+
if (matchCount > 0) {
|
|
291
|
+
areas.push({
|
|
292
|
+
domain,
|
|
293
|
+
name: config.name,
|
|
294
|
+
matchCount,
|
|
295
|
+
confidence: Math.min(1.0, matchCount / 3)
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return areas.sort((a, b) => b.matchCount - a.matchCount);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Identify required expertise for a task
|
|
305
|
+
* @param {string} task - Task description
|
|
306
|
+
* @param {Object} context - Additional context
|
|
307
|
+
*/
|
|
308
|
+
async identifyExpertise(task, context = {}) {
|
|
309
|
+
const areas = this.detectExpertiseAreas(task);
|
|
310
|
+
const affectedFiles = context.affectedFiles || [];
|
|
311
|
+
|
|
312
|
+
// Enhance with file pattern matching
|
|
313
|
+
for (const file of affectedFiles) {
|
|
314
|
+
for (const [domain, config] of Object.entries(EXPERTISE_DOMAINS)) {
|
|
315
|
+
for (const pattern of config.filePatterns) {
|
|
316
|
+
const regex = this.globToRegex(pattern);
|
|
317
|
+
if (regex.test(file)) {
|
|
318
|
+
const existing = areas.find(a => a.domain === domain);
|
|
319
|
+
if (existing) {
|
|
320
|
+
existing.confidence = Math.min(1.0, existing.confidence + 0.2);
|
|
321
|
+
} else {
|
|
322
|
+
areas.push({
|
|
323
|
+
domain,
|
|
324
|
+
name: config.name,
|
|
325
|
+
matchCount: 1,
|
|
326
|
+
confidence: 0.5
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
primary: areas[0] || null,
|
|
336
|
+
secondary: areas.slice(1, 3),
|
|
337
|
+
all: areas,
|
|
338
|
+
isMultiDomain: areas.length > 1
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Convert glob pattern to regex
|
|
344
|
+
* @param {string} glob - Glob pattern
|
|
345
|
+
*/
|
|
346
|
+
globToRegex(glob) {
|
|
347
|
+
const escaped = glob
|
|
348
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
349
|
+
.replace(/\*\*/g, '.*')
|
|
350
|
+
.replace(/\*/g, '[^/]*');
|
|
351
|
+
return new RegExp(escaped);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Check if task needs multiple agents
|
|
356
|
+
* @param {string} task - Task description
|
|
357
|
+
* @param {Object} expertise - Expertise analysis
|
|
358
|
+
* @param {Object} complexity - Complexity analysis
|
|
359
|
+
*/
|
|
360
|
+
needsMultipleAgents(task, expertise, complexity) {
|
|
361
|
+
// Multi-domain tasks need collaboration
|
|
362
|
+
if (expertise.isMultiDomain && expertise.all.length >= 2) {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// High complexity tasks may need collaboration
|
|
367
|
+
if (complexity.level === 'very_high' || complexity.level === 'high') {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Risk-heavy tasks benefit from review
|
|
372
|
+
if (complexity.factors.risk >= 3) {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// System-wide scope needs coordination
|
|
377
|
+
if (complexity.factors.scope >= 4) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Route to a single agent
|
|
386
|
+
* @param {string} task - Task description
|
|
387
|
+
* @param {Object} expertise - Expertise analysis
|
|
388
|
+
* @param {Object} complexity - Complexity analysis
|
|
389
|
+
* @param {Object} context - Additional context
|
|
390
|
+
*/
|
|
391
|
+
routeToSingleAgent(task, expertise, complexity, context) {
|
|
392
|
+
const primaryDomain = expertise.primary?.domain || 'backend';
|
|
393
|
+
const domainConfig = EXPERTISE_DOMAINS[primaryDomain];
|
|
394
|
+
|
|
395
|
+
// Find best matching available agent
|
|
396
|
+
const primaryAgent = this.findBestAgent(domainConfig.agents, expertise);
|
|
397
|
+
|
|
398
|
+
// Find fallback agents
|
|
399
|
+
const fallbackAgents = this.findFallbackAgents(domainConfig.agents, primaryAgent);
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
mode: 'single',
|
|
403
|
+
primaryAgent,
|
|
404
|
+
fallbackAgents,
|
|
405
|
+
expertise: expertise.primary,
|
|
406
|
+
complexity,
|
|
407
|
+
contextLevel: complexity.contextNeeded,
|
|
408
|
+
suggestedApproach: this.planApproach(task, complexity),
|
|
409
|
+
estimatedEffort: this.estimateEffort(complexity)
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Plan multi-agent collaboration
|
|
415
|
+
* @param {string} task - Task description
|
|
416
|
+
* @param {Object} expertise - Expertise analysis
|
|
417
|
+
* @param {Object} complexity - Complexity analysis
|
|
418
|
+
* @param {Object} context - Additional context
|
|
419
|
+
*/
|
|
420
|
+
planAgentCollaboration(task, expertise, complexity, context) {
|
|
421
|
+
// Determine collaboration pattern
|
|
422
|
+
const pattern = this.selectCollaborationPattern(task, expertise, complexity);
|
|
423
|
+
|
|
424
|
+
// Build agent sequence
|
|
425
|
+
const agents = this.buildAgentSequence(expertise, pattern, context);
|
|
426
|
+
|
|
427
|
+
// Define handoff points
|
|
428
|
+
const handoffs = this.defineHandoffs(agents, pattern);
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
mode: 'collaboration',
|
|
432
|
+
collaborationType: pattern.name.toLowerCase(),
|
|
433
|
+
pattern,
|
|
434
|
+
agents,
|
|
435
|
+
handoffPoints: handoffs,
|
|
436
|
+
coordinatorAgent: this.selectCoordinator(agents, complexity),
|
|
437
|
+
complexity,
|
|
438
|
+
suggestedApproach: this.planCollaborativeApproach(task, pattern, agents),
|
|
439
|
+
estimatedEffort: this.estimateCollaborativeEffort(agents, complexity)
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Select best collaboration pattern
|
|
445
|
+
* @param {string} task - Task description
|
|
446
|
+
* @param {Object} expertise - Expertise analysis
|
|
447
|
+
* @param {Object} complexity - Complexity analysis
|
|
448
|
+
*/
|
|
449
|
+
selectCollaborationPattern(task, expertise, complexity) {
|
|
450
|
+
const taskLower = task.toLowerCase();
|
|
451
|
+
|
|
452
|
+
// Security or review tasks -> iterative
|
|
453
|
+
if (taskLower.includes('review') || taskLower.includes('security') || taskLower.includes('audit')) {
|
|
454
|
+
return COLLABORATION_PATTERNS.iterative;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Independent components -> parallel
|
|
458
|
+
if (taskLower.includes('component') || expertise.all.length >= 3) {
|
|
459
|
+
return COLLABORATION_PATTERNS.parallel;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Complex full-stack -> hierarchical
|
|
463
|
+
if (complexity.level === 'very_high') {
|
|
464
|
+
return COLLABORATION_PATTERNS.hierarchical;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Default to sequential
|
|
468
|
+
return COLLABORATION_PATTERNS.sequential;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Build agent sequence for collaboration
|
|
473
|
+
* @param {Object} expertise - Expertise analysis
|
|
474
|
+
* @param {Object} pattern - Collaboration pattern
|
|
475
|
+
* @param {Object} context - Additional context
|
|
476
|
+
*/
|
|
477
|
+
buildAgentSequence(expertise, pattern, context) {
|
|
478
|
+
const agents = [];
|
|
479
|
+
let phase = 1;
|
|
480
|
+
|
|
481
|
+
// Add agents based on expertise areas
|
|
482
|
+
for (const area of expertise.all) {
|
|
483
|
+
const domainConfig = EXPERTISE_DOMAINS[area.domain];
|
|
484
|
+
const agent = this.findBestAgent(domainConfig.agents, expertise);
|
|
485
|
+
|
|
486
|
+
if (agent && !agents.find(a => a.agent === agent)) {
|
|
487
|
+
agents.push({
|
|
488
|
+
agent,
|
|
489
|
+
domain: area.domain,
|
|
490
|
+
phase: pattern.name === 'Parallel' ? 1 : phase++,
|
|
491
|
+
focus: `Handle ${area.name} aspects`,
|
|
492
|
+
confidence: area.confidence,
|
|
493
|
+
input: phase > 1 ? agents[agents.length - 1]?.output : null,
|
|
494
|
+
output: `${area.domain} implementation`
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Ensure at least 2 agents for collaboration
|
|
500
|
+
if (agents.length < 2 && expertise.primary) {
|
|
501
|
+
const reviewAgent = this.findReviewAgent(expertise.primary.domain);
|
|
502
|
+
if (reviewAgent) {
|
|
503
|
+
agents.push({
|
|
504
|
+
agent: reviewAgent,
|
|
505
|
+
domain: 'review',
|
|
506
|
+
phase: agents.length + 1,
|
|
507
|
+
focus: 'Review and validate implementation',
|
|
508
|
+
input: agents[agents.length - 1]?.output,
|
|
509
|
+
output: 'Validated implementation'
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return agents;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Find best matching agent from candidates
|
|
519
|
+
* @param {Array} candidates - Candidate agent names
|
|
520
|
+
* @param {Object} expertise - Expertise analysis
|
|
521
|
+
*/
|
|
522
|
+
findBestAgent(candidates, expertise) {
|
|
523
|
+
// Check against available agents
|
|
524
|
+
for (const candidate of candidates) {
|
|
525
|
+
const available = this.availableAgents.find(a =>
|
|
526
|
+
a.id === candidate ||
|
|
527
|
+
a.name?.toLowerCase().includes(candidate.replace('-expert', '')) ||
|
|
528
|
+
a.expertise?.includes(expertise.primary?.domain)
|
|
529
|
+
);
|
|
530
|
+
if (available) {
|
|
531
|
+
return available.id;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Return first candidate as default
|
|
536
|
+
return candidates[0] || 'general-expert';
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Find fallback agents
|
|
541
|
+
* @param {Array} candidates - Candidate agent names
|
|
542
|
+
* @param {string} primaryAgent - Primary agent already selected
|
|
543
|
+
*/
|
|
544
|
+
findFallbackAgents(candidates, primaryAgent) {
|
|
545
|
+
return candidates
|
|
546
|
+
.filter(c => c !== primaryAgent)
|
|
547
|
+
.slice(0, 2);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Find a review agent for a domain
|
|
552
|
+
* @param {string} domain - Primary domain
|
|
553
|
+
*/
|
|
554
|
+
findReviewAgent(domain) {
|
|
555
|
+
const reviewAgents = {
|
|
556
|
+
database: 'database-reviewer',
|
|
557
|
+
security: 'security-auditor',
|
|
558
|
+
frontend: 'ui-reviewer',
|
|
559
|
+
backend: 'code-reviewer',
|
|
560
|
+
default: 'code-reviewer'
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
return reviewAgents[domain] || reviewAgents.default;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Define handoff points between agents
|
|
568
|
+
* @param {Array} agents - Agent sequence
|
|
569
|
+
* @param {Object} pattern - Collaboration pattern
|
|
570
|
+
*/
|
|
571
|
+
defineHandoffs(agents, pattern) {
|
|
572
|
+
const handoffs = [];
|
|
573
|
+
|
|
574
|
+
if (pattern.name === 'Sequential' || pattern.name === 'Hierarchical') {
|
|
575
|
+
for (let i = 0; i < agents.length - 1; i++) {
|
|
576
|
+
handoffs.push({
|
|
577
|
+
from: agents[i].agent,
|
|
578
|
+
to: agents[i + 1].agent,
|
|
579
|
+
artifact: agents[i].output,
|
|
580
|
+
validation: this.getHandoffValidation(agents[i], agents[i + 1])
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
} else if (pattern.name === 'Iterative') {
|
|
584
|
+
// Create review loop
|
|
585
|
+
if (agents.length >= 2) {
|
|
586
|
+
handoffs.push({
|
|
587
|
+
from: agents[0].agent,
|
|
588
|
+
to: agents[1].agent,
|
|
589
|
+
artifact: 'Implementation',
|
|
590
|
+
validation: 'Review and feedback'
|
|
591
|
+
});
|
|
592
|
+
handoffs.push({
|
|
593
|
+
from: agents[1].agent,
|
|
594
|
+
to: agents[0].agent,
|
|
595
|
+
artifact: 'Feedback',
|
|
596
|
+
validation: 'Address feedback',
|
|
597
|
+
isLoop: true
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return handoffs;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Get validation requirements for a handoff
|
|
607
|
+
* @param {Object} fromAgent - Source agent
|
|
608
|
+
* @param {Object} toAgent - Target agent
|
|
609
|
+
*/
|
|
610
|
+
getHandoffValidation(fromAgent, toAgent) {
|
|
611
|
+
const validations = {
|
|
612
|
+
'database-review': 'Schema validation and migration check',
|
|
613
|
+
'security-review': 'Security audit and vulnerability scan',
|
|
614
|
+
'code-review': 'Code quality and pattern compliance',
|
|
615
|
+
'test-review': 'Test coverage and passing status'
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
return validations[`${fromAgent.domain}-${toAgent.domain}`] ||
|
|
619
|
+
`Validate ${fromAgent.output}`;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Select coordinator for hierarchical collaboration
|
|
624
|
+
* @param {Array} agents - Agent sequence
|
|
625
|
+
* @param {Object} complexity - Complexity analysis
|
|
626
|
+
*/
|
|
627
|
+
selectCoordinator(agents, complexity) {
|
|
628
|
+
// For very complex tasks, use architect
|
|
629
|
+
if (complexity.level === 'very_high') {
|
|
630
|
+
return 'architect';
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Otherwise, the first agent coordinates
|
|
634
|
+
return agents[0]?.agent || 'project-lead';
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Plan approach for single agent
|
|
639
|
+
* @param {string} task - Task description
|
|
640
|
+
* @param {Object} complexity - Complexity analysis
|
|
641
|
+
*/
|
|
642
|
+
planApproach(task, complexity) {
|
|
643
|
+
if (complexity.level === 'low') {
|
|
644
|
+
return {
|
|
645
|
+
steps: ['Implement directly', 'Test', 'Complete'],
|
|
646
|
+
reviewNeeded: false
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return {
|
|
651
|
+
steps: [
|
|
652
|
+
'Analyze existing code',
|
|
653
|
+
'Plan implementation',
|
|
654
|
+
'Implement changes',
|
|
655
|
+
'Write/update tests',
|
|
656
|
+
'Review and refine',
|
|
657
|
+
'Complete'
|
|
658
|
+
],
|
|
659
|
+
reviewNeeded: complexity.factors.risk >= 2
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Plan collaborative approach
|
|
665
|
+
* @param {string} task - Task description
|
|
666
|
+
* @param {Object} pattern - Collaboration pattern
|
|
667
|
+
* @param {Array} agents - Agent sequence
|
|
668
|
+
*/
|
|
669
|
+
planCollaborativeApproach(task, pattern, agents) {
|
|
670
|
+
return {
|
|
671
|
+
pattern: pattern.name,
|
|
672
|
+
steps: agents.map(a => ({
|
|
673
|
+
agent: a.agent,
|
|
674
|
+
phase: a.phase,
|
|
675
|
+
action: a.focus
|
|
676
|
+
})),
|
|
677
|
+
coordination: pattern.name === 'Hierarchical' ? 'Lead agent coordinates' : 'Self-coordinated',
|
|
678
|
+
reviewCycles: pattern.name === 'Iterative' ? 2 : 1
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Estimate effort for single agent
|
|
684
|
+
* @param {Object} complexity - Complexity analysis
|
|
685
|
+
*/
|
|
686
|
+
estimateEffort(complexity) {
|
|
687
|
+
const baseHours = {
|
|
688
|
+
low: 1,
|
|
689
|
+
medium: 3,
|
|
690
|
+
high: 8,
|
|
691
|
+
very_high: 16
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
const hours = baseHours[complexity.level] || 4;
|
|
695
|
+
|
|
696
|
+
return {
|
|
697
|
+
hours,
|
|
698
|
+
range: `${Math.round(hours * 0.7)}-${Math.round(hours * 1.5)} hours`,
|
|
699
|
+
confidence: complexity.level === 'low' ? 'high' : 'medium'
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Estimate effort for collaboration
|
|
705
|
+
* @param {Array} agents - Agent sequence
|
|
706
|
+
* @param {Object} complexity - Complexity analysis
|
|
707
|
+
*/
|
|
708
|
+
estimateCollaborativeEffort(agents, complexity) {
|
|
709
|
+
const singleEstimate = this.estimateEffort(complexity);
|
|
710
|
+
const agentCount = agents.length;
|
|
711
|
+
|
|
712
|
+
// Collaboration overhead
|
|
713
|
+
const overheadFactor = 1 + (agentCount - 1) * 0.3;
|
|
714
|
+
|
|
715
|
+
const hours = singleEstimate.hours * overheadFactor;
|
|
716
|
+
|
|
717
|
+
return {
|
|
718
|
+
hours: Math.round(hours),
|
|
719
|
+
range: `${Math.round(hours * 0.7)}-${Math.round(hours * 1.5)} hours`,
|
|
720
|
+
confidence: 'medium',
|
|
721
|
+
breakdown: agents.map(a => ({
|
|
722
|
+
agent: a.agent,
|
|
723
|
+
estimatedHours: Math.round(hours / agentCount)
|
|
724
|
+
}))
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Get routing recommendation for a task
|
|
730
|
+
* @param {string} task - Task description
|
|
731
|
+
* @param {Object} context - Additional context
|
|
732
|
+
*/
|
|
733
|
+
async getRecommendation(task, context = {}) {
|
|
734
|
+
const routing = await this.route(task, context);
|
|
735
|
+
|
|
736
|
+
return {
|
|
737
|
+
routing,
|
|
738
|
+
reasoning: this.explainRouting(routing),
|
|
739
|
+
alternatives: this.suggestAlternatives(routing)
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Explain routing decision
|
|
745
|
+
* @param {Object} routing - Routing result
|
|
746
|
+
*/
|
|
747
|
+
explainRouting(routing) {
|
|
748
|
+
const reasons = [];
|
|
749
|
+
|
|
750
|
+
if (routing.mode === 'single') {
|
|
751
|
+
reasons.push(`Task matches ${routing.expertise?.name || 'general'} expertise`);
|
|
752
|
+
reasons.push(`Complexity level: ${routing.complexity.level}`);
|
|
753
|
+
reasons.push(`Single agent sufficient for this task`);
|
|
754
|
+
} else {
|
|
755
|
+
reasons.push(`Multi-domain task requires collaboration`);
|
|
756
|
+
reasons.push(`${routing.pattern.name} pattern selected: ${routing.pattern.description}`);
|
|
757
|
+
reasons.push(`${routing.agents.length} agents involved`);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return reasons;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Suggest alternative routing options
|
|
765
|
+
* @param {Object} routing - Current routing result
|
|
766
|
+
*/
|
|
767
|
+
suggestAlternatives(routing) {
|
|
768
|
+
const alternatives = [];
|
|
769
|
+
|
|
770
|
+
if (routing.mode === 'single' && routing.complexity.level !== 'low') {
|
|
771
|
+
alternatives.push({
|
|
772
|
+
mode: 'collaboration',
|
|
773
|
+
reason: 'Consider adding a reviewer for higher quality',
|
|
774
|
+
agents: [routing.primaryAgent, 'code-reviewer']
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (routing.mode === 'collaboration' && routing.agents.length > 2) {
|
|
779
|
+
alternatives.push({
|
|
780
|
+
mode: 'single',
|
|
781
|
+
reason: 'Could simplify by using primary expert only',
|
|
782
|
+
agent: routing.agents[0]?.agent
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return alternatives;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
module.exports = {
|
|
791
|
+
IntelligentAgentRouter,
|
|
792
|
+
EXPERTISE_DOMAINS,
|
|
793
|
+
COMPLEXITY_FACTORS,
|
|
794
|
+
COLLABORATION_PATTERNS
|
|
795
|
+
};
|