adaptive-memory-multi-model-router 2.14.16 → 2.14.17
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/.a3m-vault.json +23 -0
- package/.github/workflows/ci.yml +253 -5
- package/.publish-tick +1 -1
- package/README.md +15 -17
- package/benchmark-results.json +45 -43
- package/dist/ensemble.d.ts +21 -0
- package/dist/ensemble.js +85 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -4
- package/dist/tui/dashboard.js +66 -2
- package/dist/tui/dashboard.js.map +1 -1
- package/dist/utils/tokenUtils.d.ts +48 -1
- package/dist/utils/tokenUtils.js +117 -4
- package/dist/utils/tokenUtils.js.map +1 -1
- package/docs/CITATIONS.md +2 -2
- package/docs/GEO_STATUS.md +43 -157
- package/docs/ai-plugin.json +4 -4
- package/docs/llms.txt +21 -27
- package/docs/sitemap.xml +14 -20
- package/package.json +2 -2
- package/research/PUBLISH_LOG.md +2 -2
- package/sitemap.xml +57 -0
- package/src/ensemble.ts +103 -0
- package/src/index.ts +13 -3
- package/src/tui/dashboard.ts +76 -3
- package/src/utils/tokenUtils.ts +142 -4
- package/test-council/1-structure-tests.test.js +353 -0
- package/test-council/1-structure-tests.test.ts +353 -0
- package/test-council/2-edge-case-tests.test.ts +361 -0
- package/test-council/3-performance-tests.test.ts +669 -0
- package/test-council/4-integration-tests.test.ts +391 -0
- package/test-council/5-agent-council-eval.test.ts +413 -0
- package/test-council/TEST_COUNCIL_REPORT.md +201 -0
- package/test-council/agents/edge-case-agent.ts +363 -0
- package/test-council/agents/performance-agent.ts +426 -0
- package/test-council/agents/structure-agent.ts +227 -0
- package/test-council/council.md +183 -0
- package/docs/.well-known/ai-plugin.json +0 -16
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Edge Case Agent - Identifies failure modes and boundary conditions
|
|
4
|
+
*
|
|
5
|
+
* This agent identifies:
|
|
6
|
+
* - Empty/null/undefined inputs
|
|
7
|
+
* - Boundary values
|
|
8
|
+
* - Error handling paths
|
|
9
|
+
* - Race conditions
|
|
10
|
+
* - Concurrent access patterns
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
|
|
16
|
+
interface EdgeCase {
|
|
17
|
+
category: 'input' | 'boundary' | 'error' | 'concurrency' | 'timeout';
|
|
18
|
+
description: string;
|
|
19
|
+
testName: string;
|
|
20
|
+
code: string;
|
|
21
|
+
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Common edge case patterns
|
|
25
|
+
const EDGE_CASE_PATTERNS = {
|
|
26
|
+
emptyString: {
|
|
27
|
+
pattern: /function\s+\w+\s*\([^)]*text\s*:?\s*string/i,
|
|
28
|
+
cases: [
|
|
29
|
+
{ input: "''", description: 'empty string' },
|
|
30
|
+
{ input: '" "', description: 'whitespace-only string' },
|
|
31
|
+
{ input: '""', description: 'double empty quotes' },
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
nullUndefined: {
|
|
35
|
+
pattern: /function\s+\w+\s*\([^)]*\w+\s*:?\s*\w+/i,
|
|
36
|
+
cases: [
|
|
37
|
+
{ input: 'null', description: 'null value' },
|
|
38
|
+
{ input: 'undefined', description: 'undefined value' },
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
arrays: {
|
|
42
|
+
pattern: /function\s+\w+\s*\([^)]*array|items|\[\]/i,
|
|
43
|
+
cases: [
|
|
44
|
+
{ input: '[]', description: 'empty array' },
|
|
45
|
+
{ input: '[null]', description: 'array with null' },
|
|
46
|
+
{ input: '[undefined]', description: 'array with undefined' },
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
numbers: {
|
|
50
|
+
pattern: /function\s+\w+\s*\([^)]*count|size|length|index/i,
|
|
51
|
+
cases: [
|
|
52
|
+
{ input: '0', description: 'zero' },
|
|
53
|
+
{ input: '-1', description: 'negative one' },
|
|
54
|
+
{ input: 'Number.MAX_VALUE', description: 'MAX_VALUE' },
|
|
55
|
+
{ input: 'Number.MIN_VALUE', description: 'MIN_VALUE' },
|
|
56
|
+
{ input: 'Infinity', description: 'Infinity' },
|
|
57
|
+
{ input: '-Infinity', description: '-Infinity' },
|
|
58
|
+
{ input: 'NaN', description: 'NaN' },
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
functions: {
|
|
62
|
+
pattern: /async\s+function\s+\w+|function\s+\w+\s*\([^)]*callback/i,
|
|
63
|
+
cases: [
|
|
64
|
+
{ input: 'Promise.resolve()', description: 'immediately resolving promise' },
|
|
65
|
+
{ input: 'Promise.reject(new Error())', description: 'immediately rejecting promise' },
|
|
66
|
+
{ input: '() => new Promise(r => setTimeout(() => r(), 10000))', description: 'slow function (10s)' },
|
|
67
|
+
{ input: '() => new Promise((_, r) => setTimeout(() => r(new Error()), 1000))', description: 'slow error (1s)' },
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
interface AnalysisResult {
|
|
73
|
+
edgeCases: EdgeCase[];
|
|
74
|
+
coverage: number;
|
|
75
|
+
criticalPaths: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Main analysis function
|
|
79
|
+
function analyzeEdgeCases(projectRoot: string): AnalysisResult {
|
|
80
|
+
const srcDir = path.join(projectRoot, 'src');
|
|
81
|
+
const edgeCases: EdgeCase[] = [];
|
|
82
|
+
|
|
83
|
+
// Find all TypeScript files
|
|
84
|
+
const files = findTsFiles(srcDir);
|
|
85
|
+
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
88
|
+
const fileCases = analyzeFile(content, file);
|
|
89
|
+
edgeCases.push(...fileCases);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Calculate coverage based on existing tests
|
|
93
|
+
const testFiles = findTestFiles(projectRoot);
|
|
94
|
+
const testContent = testFiles.map(f => fs.readFileSync(f, 'utf-8')).join('\n');
|
|
95
|
+
|
|
96
|
+
const testedPatterns = new Set<string>();
|
|
97
|
+
for (const edge of edgeCases) {
|
|
98
|
+
if (testContent.includes(edge.testName) || testContent.includes(edge.description)) {
|
|
99
|
+
testedPatterns.add(edge.testName);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const coverage = edgeCases.length > 0
|
|
104
|
+
? (testedPatterns.size / edgeCases.length) * 100
|
|
105
|
+
: 0;
|
|
106
|
+
|
|
107
|
+
// Identify critical paths
|
|
108
|
+
const criticalPaths = identifyCriticalPaths(edgeCases);
|
|
109
|
+
|
|
110
|
+
return { edgeCases, coverage, criticalPaths };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function findTsFiles(dir: string): string[] {
|
|
114
|
+
const files: string[] = [];
|
|
115
|
+
|
|
116
|
+
function walk(d: string) {
|
|
117
|
+
const entries = fs.readdirSync(d, { withFileTypes: true });
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '__pycache__') continue;
|
|
120
|
+
const fullPath = path.join(d, entry.name);
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
walk(fullPath);
|
|
123
|
+
} else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {
|
|
124
|
+
files.push(fullPath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
walk(dir);
|
|
130
|
+
return files;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function findTestFiles(projectRoot: string): string[] {
|
|
134
|
+
const testDirs = ['test', 'tests', 'test-council'];
|
|
135
|
+
const files: string[] = [];
|
|
136
|
+
|
|
137
|
+
for (const dir of testDirs) {
|
|
138
|
+
const testDir = path.join(projectRoot, dir);
|
|
139
|
+
if (fs.existsSync(testDir)) {
|
|
140
|
+
files.push(...findInDir(testDir));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return files;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function findInDir(dir: string): string[] {
|
|
148
|
+
const files: string[] = [];
|
|
149
|
+
|
|
150
|
+
function walk(d: string) {
|
|
151
|
+
const entries = fs.readdirSync(d, { withFileTypes: true });
|
|
152
|
+
for (const entry of entries) {
|
|
153
|
+
if (entry.name === 'node_modules' || entry.name === 'dist') continue;
|
|
154
|
+
const fullPath = path.join(d, entry.name);
|
|
155
|
+
if (entry.isDirectory()) {
|
|
156
|
+
walk(fullPath);
|
|
157
|
+
} else if (entry.name.match(/\.(test|spec)\.(ts|js)$/)) {
|
|
158
|
+
files.push(fullPath);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
walk(dir);
|
|
164
|
+
return files;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function analyzeFile(content: string, file: string): EdgeCase[] {
|
|
168
|
+
const cases: EdgeCase[] = [];
|
|
169
|
+
const baseName = path.basename(file, '.ts');
|
|
170
|
+
|
|
171
|
+
// Check for patterns and generate edge cases
|
|
172
|
+
for (const [patternName, pattern] of Object.entries(EDGE_CASE_PATTERNS)) {
|
|
173
|
+
if (pattern.pattern.test(content)) {
|
|
174
|
+
for (const testCase of pattern.cases) {
|
|
175
|
+
cases.push({
|
|
176
|
+
category: getCategory(patternName),
|
|
177
|
+
description: `${testCase.description} input for ${baseName}`,
|
|
178
|
+
testName: `handles ${testCase.description}`,
|
|
179
|
+
code: generateTestCode(baseName, testCase),
|
|
180
|
+
severity: getSeverity(patternName, testCase)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Add error handling cases
|
|
187
|
+
const errorCases = analyzeErrorHandling(content, baseName);
|
|
188
|
+
cases.push(...errorCases);
|
|
189
|
+
|
|
190
|
+
// Add concurrency cases
|
|
191
|
+
const concurrencyCases = analyzeConcurrency(content, baseName);
|
|
192
|
+
cases.push(...concurrencyCases);
|
|
193
|
+
|
|
194
|
+
return cases;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function getCategory(patternName: string): EdgeCase['category'] {
|
|
198
|
+
if (patternName === 'functions') return 'timeout';
|
|
199
|
+
if (patternName === 'numbers') return 'boundary';
|
|
200
|
+
return 'input';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function getSeverity(patternName: string, testCase: { input: string }): EdgeCase['severity'] {
|
|
204
|
+
if (patternName === 'numbers') {
|
|
205
|
+
if (['0', '-1', 'NaN', 'Infinity', '-Infinity'].includes(testCase.input)) {
|
|
206
|
+
return 'high';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (testCase.input === 'null' || testCase.input === 'undefined') {
|
|
210
|
+
return 'high';
|
|
211
|
+
}
|
|
212
|
+
return 'medium';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function generateTestCode(baseName: string, testCase: { input: string; description: string }): string {
|
|
216
|
+
return `// Test for ${testCase.description}
|
|
217
|
+
it('handles ${testCase.description}', () => {
|
|
218
|
+
// TODO: Implement edge case test
|
|
219
|
+
expect(true).toBe(true);
|
|
220
|
+
});`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function analyzeErrorHandling(content: string, baseName: string): EdgeCase[] {
|
|
224
|
+
const cases: EdgeCase[] = [];
|
|
225
|
+
|
|
226
|
+
// Find throw statements
|
|
227
|
+
const throwMatches = content.matchAll(/throw\s+new\s+Error\s*\(\s*['"]([^'"]+)/g);
|
|
228
|
+
for (const match of throwMatches) {
|
|
229
|
+
cases.push({
|
|
230
|
+
category: 'error',
|
|
231
|
+
description: `error case: ${match[1]}`,
|
|
232
|
+
testName: `throws error: ${match[1]}`,
|
|
233
|
+
code: `it('throws error: ${match[1]}', () => { expect(() => subject()).toThrow(); });`,
|
|
234
|
+
severity: 'critical'
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Find if (error) patterns
|
|
239
|
+
if (content.includes('catch') || content.includes('if (err')) {
|
|
240
|
+
cases.push({
|
|
241
|
+
category: 'error',
|
|
242
|
+
description: 'error in catch block',
|
|
243
|
+
testName: 'handles catch block',
|
|
244
|
+
code: `it('handles catch block', () => { /* test error handling */ });`,
|
|
245
|
+
severity: 'high'
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return cases;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function analyzeConcurrency(content: string, baseName: string): EdgeCase[] {
|
|
253
|
+
const cases: EdgeCase[] = [];
|
|
254
|
+
|
|
255
|
+
// Check for async/await patterns
|
|
256
|
+
if (content.includes('async') && content.includes('await')) {
|
|
257
|
+
cases.push({
|
|
258
|
+
category: 'concurrency',
|
|
259
|
+
description: 'concurrent async calls',
|
|
260
|
+
testName: 'handles concurrent async calls',
|
|
261
|
+
code: `it('handles concurrent async calls', async () => {
|
|
262
|
+
await Promise.all([
|
|
263
|
+
subject(),
|
|
264
|
+
subject(),
|
|
265
|
+
subject()
|
|
266
|
+
]);
|
|
267
|
+
});`,
|
|
268
|
+
severity: 'high'
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Check for shared state
|
|
273
|
+
if (content.match(/\bthis\.\w+\s*=/g)?.length > 3) {
|
|
274
|
+
cases.push({
|
|
275
|
+
category: 'concurrency',
|
|
276
|
+
description: 'shared state mutation',
|
|
277
|
+
testName: 'handles shared state safely',
|
|
278
|
+
code: `it('handles shared state safely', async () => {
|
|
279
|
+
// Run multiple times to check for race conditions
|
|
280
|
+
await Promise.all([subject(), subject(), subject()]);
|
|
281
|
+
});`,
|
|
282
|
+
severity: 'medium'
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return cases;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function identifyCriticalPaths(edgeCases: EdgeCase[]): string[] {
|
|
290
|
+
const critical = edgeCases
|
|
291
|
+
.filter(e => e.severity === 'critical')
|
|
292
|
+
.map(e => e.testName);
|
|
293
|
+
|
|
294
|
+
return [...new Set(critical)];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Generate comprehensive edge case tests
|
|
298
|
+
function generateEdgeCaseTests(result: AnalysisResult): string {
|
|
299
|
+
const tests: string[] = [];
|
|
300
|
+
|
|
301
|
+
tests.push(`// Auto-generated edge case tests
|
|
302
|
+
// Total edge cases identified: ${result.edgeCases.length}
|
|
303
|
+
// Critical paths: ${result.criticalPaths.length}
|
|
304
|
+
|
|
305
|
+
describe('Edge Case Coverage', () => {`);
|
|
306
|
+
|
|
307
|
+
const byCategory = new Map<string, EdgeCase[]>();
|
|
308
|
+
for (const edge of result.edgeCases) {
|
|
309
|
+
if (!byCategory.has(edge.category)) {
|
|
310
|
+
byCategory.set(edge.category, []);
|
|
311
|
+
}
|
|
312
|
+
byCategory.get(edge.category)!.push(edge);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
for (const [category, cases] of byCategory) {
|
|
316
|
+
tests.push(`\n describe('${category.toUpperCase()} cases', () => {`);
|
|
317
|
+
|
|
318
|
+
for (const edge of cases.slice(0, 10)) { // Limit per category
|
|
319
|
+
tests.push(` ${edge.code.replace('it(', "it('${category}: ").replace("() => {", "', () => {")}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
tests.push(' });');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
tests.push('});');
|
|
326
|
+
|
|
327
|
+
return tests.join('\n');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Run agent if executed directly
|
|
331
|
+
if (require.main === module) {
|
|
332
|
+
const projectRoot = path.resolve(__dirname, '../..');
|
|
333
|
+
const result = analyzeEdgeCases(projectRoot);
|
|
334
|
+
|
|
335
|
+
console.log('\n========================================');
|
|
336
|
+
console.log('EDGE CASE AGENT - Analysis Report');
|
|
337
|
+
console.log('========================================\n');
|
|
338
|
+
console.log(`Total Edge Cases: ${result.edgeCases.length}`);
|
|
339
|
+
console.log(`Critical Paths: ${result.criticalPaths.length}`);
|
|
340
|
+
console.log(`Estimated Coverage: ${result.coverage.toFixed(1)}%\n`);
|
|
341
|
+
|
|
342
|
+
console.log('By Category:');
|
|
343
|
+
const byCategory = new Map<string, number>();
|
|
344
|
+
for (const edge of result.edgeCases) {
|
|
345
|
+
byCategory.set(edge.category, (byCategory.get(edge.category) || 0) + 1);
|
|
346
|
+
}
|
|
347
|
+
for (const [cat, count] of byCategory) {
|
|
348
|
+
console.log(` ${cat}: ${count}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
console.log('\nCritical Paths:');
|
|
352
|
+
for (const path of result.criticalPaths.slice(0, 10)) {
|
|
353
|
+
console.log(` - ${path}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Generate test code
|
|
357
|
+
const generated = generateEdgeCaseTests(result);
|
|
358
|
+
const outputPath = path.join(__dirname, '../2-edge-case-tests-generated.ts');
|
|
359
|
+
fs.writeFileSync(outputPath, generated);
|
|
360
|
+
console.log(`\nGenerated tests written to: ${outputPath}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export { analyzeEdgeCases, AnalysisResult, EdgeCase, generateEdgeCaseTests };
|