musubi-sdd 3.10.0 → 5.1.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/README.md +24 -19
- package/package.json +1 -1
- package/src/agents/agent-loop.js +532 -0
- package/src/agents/agentic/code-generator.js +767 -0
- package/src/agents/agentic/code-reviewer.js +698 -0
- package/src/agents/agentic/index.js +43 -0
- package/src/agents/function-tool.js +432 -0
- package/src/agents/index.js +45 -0
- package/src/agents/schema-generator.js +514 -0
- package/src/analyzers/ast-extractor.js +870 -0
- package/src/analyzers/context-optimizer.js +681 -0
- package/src/analyzers/repository-map.js +692 -0
- package/src/integrations/index.js +7 -1
- package/src/integrations/mcp/index.js +175 -0
- package/src/integrations/mcp/mcp-context-provider.js +472 -0
- package/src/integrations/mcp/mcp-discovery.js +436 -0
- package/src/integrations/mcp/mcp-tool-registry.js +467 -0
- package/src/integrations/mcp-connector.js +818 -0
- package/src/integrations/tool-discovery.js +589 -0
- package/src/managers/index.js +7 -0
- package/src/managers/skill-tools.js +565 -0
- package/src/monitoring/cost-tracker.js +7 -0
- package/src/monitoring/incident-manager.js +10 -0
- package/src/monitoring/observability.js +10 -0
- package/src/monitoring/quality-dashboard.js +491 -0
- package/src/monitoring/release-manager.js +10 -0
- package/src/orchestration/agent-skill-binding.js +655 -0
- package/src/orchestration/error-handler.js +827 -0
- package/src/orchestration/index.js +235 -1
- package/src/orchestration/mcp-tool-adapters.js +896 -0
- package/src/orchestration/reasoning/index.js +58 -0
- package/src/orchestration/reasoning/planning-engine.js +831 -0
- package/src/orchestration/reasoning/reasoning-engine.js +710 -0
- package/src/orchestration/reasoning/self-correction.js +751 -0
- package/src/orchestration/skill-executor.js +665 -0
- package/src/orchestration/skill-registry.js +650 -0
- package/src/orchestration/workflow-examples.js +1072 -0
- package/src/orchestration/workflow-executor.js +779 -0
- package/src/phase4-integration.js +248 -0
- package/src/phase5-integration.js +402 -0
- package/src/steering/steering-auto-update.js +572 -0
- package/src/steering/steering-validator.js +547 -0
- package/src/templates/template-constraints.js +646 -0
- package/src/validators/advanced-validation.js +580 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file self-correction.js
|
|
3
|
+
* @description Self-correction and error recovery system for agentic coding
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { EventEmitter } = require('events');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Error severity levels
|
|
13
|
+
* @enum {string}
|
|
14
|
+
*/
|
|
15
|
+
const SEVERITY = {
|
|
16
|
+
INFO: 'info',
|
|
17
|
+
WARNING: 'warning',
|
|
18
|
+
ERROR: 'error',
|
|
19
|
+
CRITICAL: 'critical'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Correction strategy types
|
|
24
|
+
* @enum {string}
|
|
25
|
+
*/
|
|
26
|
+
const CORRECTION_STRATEGY = {
|
|
27
|
+
RETRY: 'retry',
|
|
28
|
+
FALLBACK: 'fallback',
|
|
29
|
+
DECOMPOSE: 'decompose',
|
|
30
|
+
SIMPLIFY: 'simplify',
|
|
31
|
+
ESCALATE: 'escalate',
|
|
32
|
+
SKIP: 'skip',
|
|
33
|
+
ROLLBACK: 'rollback'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {Object} ErrorPattern
|
|
38
|
+
* @property {string} pattern - Error pattern (regex or string)
|
|
39
|
+
* @property {string} type - Error type classification
|
|
40
|
+
* @property {string} strategy - Recommended correction strategy
|
|
41
|
+
* @property {string} description - Human-readable description
|
|
42
|
+
* @property {Object} [metadata] - Additional metadata
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {Object} CorrectionAttempt
|
|
47
|
+
* @property {string} id - Attempt identifier
|
|
48
|
+
* @property {string} errorId - Original error ID
|
|
49
|
+
* @property {string} strategy - Strategy used
|
|
50
|
+
* @property {boolean} successful - Whether correction succeeded
|
|
51
|
+
* @property {string} [result] - Correction result
|
|
52
|
+
* @property {number} timestamp - Attempt timestamp
|
|
53
|
+
* @property {number} duration - Time taken
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {Object} ErrorRecord
|
|
58
|
+
* @property {string} id - Error identifier
|
|
59
|
+
* @property {string} message - Error message
|
|
60
|
+
* @property {string} type - Error type
|
|
61
|
+
* @property {string} severity - Error severity
|
|
62
|
+
* @property {string} context - Error context
|
|
63
|
+
* @property {CorrectionAttempt[]} attempts - Correction attempts
|
|
64
|
+
* @property {boolean} resolved - Whether error is resolved
|
|
65
|
+
* @property {number} timestamp - Error timestamp
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {Object} SelfCorrectionOptions
|
|
70
|
+
* @property {number} [maxRetries=3] - Maximum retry attempts
|
|
71
|
+
* @property {number} [retryDelay=1000] - Delay between retries in ms
|
|
72
|
+
* @property {boolean} [exponentialBackoff=true] - Use exponential backoff
|
|
73
|
+
* @property {number} [maxBackoff=30000] - Maximum backoff delay
|
|
74
|
+
* @property {boolean} [learnFromErrors=true] - Learn from error patterns
|
|
75
|
+
* @property {number} [memorySize=100] - Error memory size
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Default error patterns
|
|
80
|
+
*/
|
|
81
|
+
const DEFAULT_PATTERNS = [
|
|
82
|
+
{
|
|
83
|
+
pattern: /syntax\s*error/i,
|
|
84
|
+
type: 'syntax',
|
|
85
|
+
strategy: CORRECTION_STRATEGY.SIMPLIFY,
|
|
86
|
+
description: 'Syntax error in code'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
pattern: /undefined|not\s*defined/i,
|
|
90
|
+
type: 'reference',
|
|
91
|
+
strategy: CORRECTION_STRATEGY.FALLBACK,
|
|
92
|
+
description: 'Undefined reference'
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
pattern: /timeout|timed?\s*out/i,
|
|
96
|
+
type: 'timeout',
|
|
97
|
+
strategy: CORRECTION_STRATEGY.RETRY,
|
|
98
|
+
description: 'Operation timed out'
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
pattern: /permission|access\s*denied|forbidden/i,
|
|
102
|
+
type: 'permission',
|
|
103
|
+
strategy: CORRECTION_STRATEGY.ESCALATE,
|
|
104
|
+
description: 'Permission denied'
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
pattern: /not\s*found|missing|does\s*not\s*exist/i,
|
|
108
|
+
type: 'not-found',
|
|
109
|
+
strategy: CORRECTION_STRATEGY.FALLBACK,
|
|
110
|
+
description: 'Resource not found'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
pattern: /out\s*of\s*memory|heap|allocation/i,
|
|
114
|
+
type: 'memory',
|
|
115
|
+
strategy: CORRECTION_STRATEGY.DECOMPOSE,
|
|
116
|
+
description: 'Memory allocation error'
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
pattern: /network|connection|socket/i,
|
|
120
|
+
type: 'network',
|
|
121
|
+
strategy: CORRECTION_STRATEGY.RETRY,
|
|
122
|
+
description: 'Network error'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
pattern: /invalid|malformed|corrupt/i,
|
|
126
|
+
type: 'validation',
|
|
127
|
+
strategy: CORRECTION_STRATEGY.SIMPLIFY,
|
|
128
|
+
description: 'Invalid input or data'
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
pattern: /deadlock|race\s*condition/i,
|
|
132
|
+
type: 'concurrency',
|
|
133
|
+
strategy: CORRECTION_STRATEGY.RETRY,
|
|
134
|
+
description: 'Concurrency issue'
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
pattern: /assertion|expect|test.*fail/i,
|
|
138
|
+
type: 'test',
|
|
139
|
+
strategy: CORRECTION_STRATEGY.FALLBACK,
|
|
140
|
+
description: 'Test assertion failure'
|
|
141
|
+
}
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Self-Correction class for error recovery
|
|
146
|
+
* @extends EventEmitter
|
|
147
|
+
*/
|
|
148
|
+
class SelfCorrection extends EventEmitter {
|
|
149
|
+
/**
|
|
150
|
+
* Create self-correction system
|
|
151
|
+
* @param {SelfCorrectionOptions} [options={}] - System options
|
|
152
|
+
*/
|
|
153
|
+
constructor(options = {}) {
|
|
154
|
+
super();
|
|
155
|
+
|
|
156
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
157
|
+
this.retryDelay = options.retryDelay ?? 1000;
|
|
158
|
+
this.exponentialBackoff = options.exponentialBackoff ?? true;
|
|
159
|
+
this.maxBackoff = options.maxBackoff ?? 30000;
|
|
160
|
+
this.learnFromErrors = options.learnFromErrors ?? true;
|
|
161
|
+
this.memorySize = options.memorySize ?? 100;
|
|
162
|
+
|
|
163
|
+
// State
|
|
164
|
+
this.patterns = [...DEFAULT_PATTERNS];
|
|
165
|
+
this.errorMemory = [];
|
|
166
|
+
this.corrections = new Map();
|
|
167
|
+
this.successPatterns = new Map();
|
|
168
|
+
this.errorCounter = 0;
|
|
169
|
+
|
|
170
|
+
// Handlers
|
|
171
|
+
this.strategyHandlers = new Map();
|
|
172
|
+
this.registerDefaultHandlers();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Register default strategy handlers
|
|
177
|
+
* @private
|
|
178
|
+
*/
|
|
179
|
+
registerDefaultHandlers() {
|
|
180
|
+
this.strategyHandlers.set(CORRECTION_STRATEGY.RETRY, this.handleRetry.bind(this));
|
|
181
|
+
this.strategyHandlers.set(CORRECTION_STRATEGY.FALLBACK, this.handleFallback.bind(this));
|
|
182
|
+
this.strategyHandlers.set(CORRECTION_STRATEGY.DECOMPOSE, this.handleDecompose.bind(this));
|
|
183
|
+
this.strategyHandlers.set(CORRECTION_STRATEGY.SIMPLIFY, this.handleSimplify.bind(this));
|
|
184
|
+
this.strategyHandlers.set(CORRECTION_STRATEGY.ESCALATE, this.handleEscalate.bind(this));
|
|
185
|
+
this.strategyHandlers.set(CORRECTION_STRATEGY.SKIP, this.handleSkip.bind(this));
|
|
186
|
+
this.strategyHandlers.set(CORRECTION_STRATEGY.ROLLBACK, this.handleRollback.bind(this));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Analyze an error
|
|
191
|
+
* @param {Error|string} error - Error to analyze
|
|
192
|
+
* @param {Object} [context={}] - Error context
|
|
193
|
+
* @returns {ErrorRecord}
|
|
194
|
+
*/
|
|
195
|
+
analyzeError(error, context = {}) {
|
|
196
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
197
|
+
const stack = error instanceof Error ? error.stack : null;
|
|
198
|
+
|
|
199
|
+
// Classify error
|
|
200
|
+
const classification = this.classifyError(message);
|
|
201
|
+
|
|
202
|
+
const record = {
|
|
203
|
+
id: `err-${++this.errorCounter}`,
|
|
204
|
+
message,
|
|
205
|
+
type: classification.type,
|
|
206
|
+
severity: this.determineSeverity(classification.type),
|
|
207
|
+
context: JSON.stringify(context),
|
|
208
|
+
stack,
|
|
209
|
+
strategy: classification.strategy,
|
|
210
|
+
description: classification.description,
|
|
211
|
+
attempts: [],
|
|
212
|
+
resolved: false,
|
|
213
|
+
timestamp: Date.now()
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Store in memory
|
|
217
|
+
this.errorMemory.push(record);
|
|
218
|
+
if (this.errorMemory.length > this.memorySize) {
|
|
219
|
+
this.errorMemory.shift();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.emit('error:analyzed', { record });
|
|
223
|
+
|
|
224
|
+
return record;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Classify error based on patterns
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
231
|
+
classifyError(message) {
|
|
232
|
+
// Check learned patterns first
|
|
233
|
+
for (const [pattern, info] of this.successPatterns) {
|
|
234
|
+
if (message.includes(pattern)) {
|
|
235
|
+
return info;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Check default patterns
|
|
240
|
+
for (const pattern of this.patterns) {
|
|
241
|
+
const regex = pattern.pattern instanceof RegExp
|
|
242
|
+
? pattern.pattern
|
|
243
|
+
: new RegExp(pattern.pattern, 'i');
|
|
244
|
+
|
|
245
|
+
if (regex.test(message)) {
|
|
246
|
+
return {
|
|
247
|
+
type: pattern.type,
|
|
248
|
+
strategy: pattern.strategy,
|
|
249
|
+
description: pattern.description
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Default classification
|
|
255
|
+
return {
|
|
256
|
+
type: 'unknown',
|
|
257
|
+
strategy: CORRECTION_STRATEGY.RETRY,
|
|
258
|
+
description: 'Unknown error'
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Determine severity based on error type
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
determineSeverity(type) {
|
|
267
|
+
const severities = {
|
|
268
|
+
syntax: SEVERITY.ERROR,
|
|
269
|
+
reference: SEVERITY.ERROR,
|
|
270
|
+
timeout: SEVERITY.WARNING,
|
|
271
|
+
permission: SEVERITY.CRITICAL,
|
|
272
|
+
'not-found': SEVERITY.WARNING,
|
|
273
|
+
memory: SEVERITY.CRITICAL,
|
|
274
|
+
network: SEVERITY.WARNING,
|
|
275
|
+
validation: SEVERITY.ERROR,
|
|
276
|
+
concurrency: SEVERITY.ERROR,
|
|
277
|
+
test: SEVERITY.INFO,
|
|
278
|
+
unknown: SEVERITY.ERROR
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
return severities[type] || SEVERITY.ERROR;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Attempt to correct an error
|
|
286
|
+
* @param {ErrorRecord} record - Error record
|
|
287
|
+
* @param {Function} operation - Original operation to retry
|
|
288
|
+
* @param {Object} [options={}] - Correction options
|
|
289
|
+
* @returns {Promise<Object>}
|
|
290
|
+
*/
|
|
291
|
+
async correct(record, operation, options = {}) {
|
|
292
|
+
const startTime = Date.now();
|
|
293
|
+
|
|
294
|
+
this.emit('correction:start', { errorId: record.id, strategy: record.strategy });
|
|
295
|
+
|
|
296
|
+
const handler = this.strategyHandlers.get(record.strategy);
|
|
297
|
+
if (!handler) {
|
|
298
|
+
throw new Error(`No handler for strategy: ${record.strategy}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const attempt = {
|
|
302
|
+
id: `attempt-${record.attempts.length + 1}`,
|
|
303
|
+
errorId: record.id,
|
|
304
|
+
strategy: record.strategy,
|
|
305
|
+
successful: false,
|
|
306
|
+
result: null,
|
|
307
|
+
timestamp: Date.now(),
|
|
308
|
+
duration: 0
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const result = await handler(record, operation, options);
|
|
313
|
+
|
|
314
|
+
attempt.successful = true;
|
|
315
|
+
attempt.result = 'Correction successful';
|
|
316
|
+
record.resolved = true;
|
|
317
|
+
|
|
318
|
+
// Learn from success
|
|
319
|
+
if (this.learnFromErrors) {
|
|
320
|
+
this.learnSuccess(record);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this.emit('correction:success', { errorId: record.id, attempt });
|
|
324
|
+
|
|
325
|
+
return { success: true, result, attempt };
|
|
326
|
+
|
|
327
|
+
} catch (correctionError) {
|
|
328
|
+
attempt.successful = false;
|
|
329
|
+
attempt.result = correctionError.message;
|
|
330
|
+
|
|
331
|
+
this.emit('correction:failure', {
|
|
332
|
+
errorId: record.id,
|
|
333
|
+
attempt,
|
|
334
|
+
error: correctionError.message
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
success: false,
|
|
339
|
+
error: correctionError,
|
|
340
|
+
attempt,
|
|
341
|
+
canRetry: record.attempts.length < this.maxRetries
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
} finally {
|
|
345
|
+
attempt.duration = Date.now() - startTime;
|
|
346
|
+
record.attempts.push(attempt);
|
|
347
|
+
this.corrections.set(record.id, record);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Handle retry strategy
|
|
353
|
+
* @private
|
|
354
|
+
*/
|
|
355
|
+
async handleRetry(record, operation, options) {
|
|
356
|
+
const attemptNumber = record.attempts.length;
|
|
357
|
+
|
|
358
|
+
// Calculate delay
|
|
359
|
+
let delay = this.retryDelay;
|
|
360
|
+
if (this.exponentialBackoff) {
|
|
361
|
+
delay = Math.min(this.retryDelay * Math.pow(2, attemptNumber), this.maxBackoff);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Wait before retry
|
|
365
|
+
await this.delay(delay);
|
|
366
|
+
|
|
367
|
+
// Retry the operation
|
|
368
|
+
return operation();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Handle fallback strategy
|
|
373
|
+
* @private
|
|
374
|
+
*/
|
|
375
|
+
async handleFallback(record, operation, options) {
|
|
376
|
+
const fallback = options.fallback;
|
|
377
|
+
|
|
378
|
+
if (fallback) {
|
|
379
|
+
if (typeof fallback === 'function') {
|
|
380
|
+
return fallback(record);
|
|
381
|
+
}
|
|
382
|
+
return fallback;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Try simpler version of operation
|
|
386
|
+
if (options.simplifiedOperation) {
|
|
387
|
+
return options.simplifiedOperation();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
throw new Error('No fallback available');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Handle decompose strategy
|
|
395
|
+
* @private
|
|
396
|
+
*/
|
|
397
|
+
async handleDecompose(record, operation, options) {
|
|
398
|
+
const decomposer = options.decompose;
|
|
399
|
+
|
|
400
|
+
if (!decomposer) {
|
|
401
|
+
throw new Error('No decomposer provided');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Decompose into smaller operations
|
|
405
|
+
const subOperations = await decomposer(record);
|
|
406
|
+
|
|
407
|
+
// Execute sub-operations
|
|
408
|
+
const results = [];
|
|
409
|
+
for (const subOp of subOperations) {
|
|
410
|
+
try {
|
|
411
|
+
const result = await subOp();
|
|
412
|
+
results.push({ success: true, result });
|
|
413
|
+
} catch (error) {
|
|
414
|
+
results.push({ success: false, error: error.message });
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return { results, successful: results.every(r => r.success) };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Handle simplify strategy
|
|
423
|
+
* @private
|
|
424
|
+
*/
|
|
425
|
+
async handleSimplify(record, operation, options) {
|
|
426
|
+
const simplifier = options.simplify;
|
|
427
|
+
|
|
428
|
+
if (simplifier) {
|
|
429
|
+
const simplified = await simplifier(record);
|
|
430
|
+
return simplified;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Default simplification: retry with reduced scope
|
|
434
|
+
if (options.simplifiedOperation) {
|
|
435
|
+
return options.simplifiedOperation();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
throw new Error('Cannot simplify operation');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Handle escalate strategy
|
|
443
|
+
* @private
|
|
444
|
+
*/
|
|
445
|
+
async handleEscalate(record, operation, options) {
|
|
446
|
+
const escalationHandler = options.escalate;
|
|
447
|
+
|
|
448
|
+
this.emit('correction:escalated', { record });
|
|
449
|
+
|
|
450
|
+
if (escalationHandler) {
|
|
451
|
+
return escalationHandler(record);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Mark as requiring human intervention
|
|
455
|
+
record.escalated = true;
|
|
456
|
+
throw new Error('Error escalated - requires human intervention');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Handle skip strategy
|
|
461
|
+
* @private
|
|
462
|
+
*/
|
|
463
|
+
async handleSkip(record, operation, options) {
|
|
464
|
+
this.emit('correction:skipped', { record });
|
|
465
|
+
|
|
466
|
+
return { skipped: true, reason: record.message };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Handle rollback strategy
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
async handleRollback(record, operation, options) {
|
|
474
|
+
const rollbackHandler = options.rollback;
|
|
475
|
+
|
|
476
|
+
if (!rollbackHandler) {
|
|
477
|
+
throw new Error('No rollback handler provided');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
this.emit('correction:rollback', { record });
|
|
481
|
+
|
|
482
|
+
return rollbackHandler(record);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Learn from successful correction
|
|
487
|
+
* @private
|
|
488
|
+
*/
|
|
489
|
+
learnSuccess(record) {
|
|
490
|
+
// Extract key part of error message
|
|
491
|
+
const key = this.extractErrorSignature(record.message);
|
|
492
|
+
|
|
493
|
+
if (key) {
|
|
494
|
+
this.successPatterns.set(key, {
|
|
495
|
+
type: record.type,
|
|
496
|
+
strategy: record.strategy,
|
|
497
|
+
description: record.description
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Extract error signature for learning
|
|
504
|
+
* @private
|
|
505
|
+
*/
|
|
506
|
+
extractErrorSignature(message) {
|
|
507
|
+
// Remove variable parts (numbers, paths, etc.)
|
|
508
|
+
return message
|
|
509
|
+
.replace(/\d+/g, 'N')
|
|
510
|
+
.replace(/['"][^'"]+['"]/g, '"X"')
|
|
511
|
+
.replace(/\/[^\s]+/g, '/PATH')
|
|
512
|
+
.substring(0, 50);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Add custom error pattern
|
|
517
|
+
* @param {ErrorPattern} pattern - Pattern definition
|
|
518
|
+
*/
|
|
519
|
+
addPattern(pattern) {
|
|
520
|
+
this.patterns.unshift(pattern);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Register custom strategy handler
|
|
525
|
+
* @param {string} strategy - Strategy name
|
|
526
|
+
* @param {Function} handler - Handler function
|
|
527
|
+
*/
|
|
528
|
+
registerHandler(strategy, handler) {
|
|
529
|
+
this.strategyHandlers.set(strategy, handler);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Get error by ID
|
|
534
|
+
* @param {string} errorId - Error identifier
|
|
535
|
+
* @returns {ErrorRecord|null}
|
|
536
|
+
*/
|
|
537
|
+
getError(errorId) {
|
|
538
|
+
return this.corrections.get(errorId) ||
|
|
539
|
+
this.errorMemory.find(e => e.id === errorId) ||
|
|
540
|
+
null;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Get recent errors
|
|
545
|
+
* @param {number} [count=10] - Number of errors to return
|
|
546
|
+
* @returns {ErrorRecord[]}
|
|
547
|
+
*/
|
|
548
|
+
getRecentErrors(count = 10) {
|
|
549
|
+
return this.errorMemory.slice(-count);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Get unresolved errors
|
|
554
|
+
* @returns {ErrorRecord[]}
|
|
555
|
+
*/
|
|
556
|
+
getUnresolvedErrors() {
|
|
557
|
+
return this.errorMemory.filter(e => !e.resolved);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Get error statistics
|
|
562
|
+
* @returns {Object}
|
|
563
|
+
*/
|
|
564
|
+
getStats() {
|
|
565
|
+
const total = this.errorMemory.length;
|
|
566
|
+
const resolved = this.errorMemory.filter(e => e.resolved).length;
|
|
567
|
+
const byType = {};
|
|
568
|
+
const bySeverity = {};
|
|
569
|
+
const byStrategy = {};
|
|
570
|
+
|
|
571
|
+
for (const error of this.errorMemory) {
|
|
572
|
+
byType[error.type] = (byType[error.type] || 0) + 1;
|
|
573
|
+
bySeverity[error.severity] = (bySeverity[error.severity] || 0) + 1;
|
|
574
|
+
byStrategy[error.strategy] = (byStrategy[error.strategy] || 0) + 1;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Calculate success rates by strategy
|
|
578
|
+
const strategySuccess = {};
|
|
579
|
+
for (const [, record] of this.corrections) {
|
|
580
|
+
const strategy = record.strategy;
|
|
581
|
+
if (!strategySuccess[strategy]) {
|
|
582
|
+
strategySuccess[strategy] = { success: 0, total: 0 };
|
|
583
|
+
}
|
|
584
|
+
strategySuccess[strategy].total++;
|
|
585
|
+
if (record.resolved) {
|
|
586
|
+
strategySuccess[strategy].success++;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return {
|
|
591
|
+
total,
|
|
592
|
+
resolved,
|
|
593
|
+
unresolved: total - resolved,
|
|
594
|
+
resolutionRate: total > 0 ? resolved / total : 0,
|
|
595
|
+
byType,
|
|
596
|
+
bySeverity,
|
|
597
|
+
byStrategy,
|
|
598
|
+
strategySuccess,
|
|
599
|
+
learnedPatterns: this.successPatterns.size
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Get correction recommendations for an error
|
|
605
|
+
* @param {ErrorRecord} record - Error record
|
|
606
|
+
* @returns {Object}
|
|
607
|
+
*/
|
|
608
|
+
getRecommendations(record) {
|
|
609
|
+
const recommendations = [];
|
|
610
|
+
|
|
611
|
+
// Based on error type
|
|
612
|
+
const typeRecs = {
|
|
613
|
+
syntax: ['Check for typos', 'Verify code structure', 'Use linter'],
|
|
614
|
+
reference: ['Check variable declarations', 'Verify imports', 'Check scope'],
|
|
615
|
+
timeout: ['Increase timeout', 'Optimize operation', 'Check network'],
|
|
616
|
+
permission: ['Check credentials', 'Verify access rights', 'Request permissions'],
|
|
617
|
+
'not-found': ['Verify path/name', 'Check existence', 'Create resource'],
|
|
618
|
+
memory: ['Reduce data size', 'Process in chunks', 'Increase memory'],
|
|
619
|
+
network: ['Check connection', 'Retry request', 'Check firewall'],
|
|
620
|
+
validation: ['Verify input format', 'Check data types', 'Validate schema'],
|
|
621
|
+
concurrency: ['Add locking', 'Use queue', 'Retry with backoff'],
|
|
622
|
+
test: ['Review test logic', 'Check expected values', 'Update assertions']
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
recommendations.push(...(typeRecs[record.type] || ['Review error details']));
|
|
626
|
+
|
|
627
|
+
// Based on similar past errors
|
|
628
|
+
const similar = this.findSimilarErrors(record);
|
|
629
|
+
if (similar.length > 0 && similar[0].resolved) {
|
|
630
|
+
recommendations.push(`Similar error resolved using: ${similar[0].strategy}`);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return {
|
|
634
|
+
primaryStrategy: record.strategy,
|
|
635
|
+
alternatives: this.getAlternativeStrategies(record.strategy),
|
|
636
|
+
actionItems: recommendations
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Find similar past errors
|
|
642
|
+
* @private
|
|
643
|
+
*/
|
|
644
|
+
findSimilarErrors(record) {
|
|
645
|
+
return this.errorMemory
|
|
646
|
+
.filter(e => e.id !== record.id && e.type === record.type)
|
|
647
|
+
.slice(-5);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Get alternative strategies
|
|
652
|
+
* @private
|
|
653
|
+
*/
|
|
654
|
+
getAlternativeStrategies(primaryStrategy) {
|
|
655
|
+
const all = Object.values(CORRECTION_STRATEGY);
|
|
656
|
+
return all.filter(s => s !== primaryStrategy);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Delay helper
|
|
661
|
+
* @private
|
|
662
|
+
*/
|
|
663
|
+
delay(ms) {
|
|
664
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Clear error memory
|
|
669
|
+
*/
|
|
670
|
+
clearMemory() {
|
|
671
|
+
this.errorMemory = [];
|
|
672
|
+
this.corrections.clear();
|
|
673
|
+
this.errorCounter = 0;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Reset learned patterns
|
|
678
|
+
*/
|
|
679
|
+
resetLearning() {
|
|
680
|
+
this.successPatterns.clear();
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Export error report
|
|
685
|
+
* @returns {string}
|
|
686
|
+
*/
|
|
687
|
+
exportReport() {
|
|
688
|
+
const stats = this.getStats();
|
|
689
|
+
|
|
690
|
+
let report = `# Error Correction Report\n\n`;
|
|
691
|
+
report += `## Summary\n`;
|
|
692
|
+
report += `- Total Errors: ${stats.total}\n`;
|
|
693
|
+
report += `- Resolved: ${stats.resolved} (${(stats.resolutionRate * 100).toFixed(1)}%)\n`;
|
|
694
|
+
report += `- Learned Patterns: ${stats.learnedPatterns}\n\n`;
|
|
695
|
+
|
|
696
|
+
report += `## Errors by Type\n`;
|
|
697
|
+
for (const [type, count] of Object.entries(stats.byType)) {
|
|
698
|
+
report += `- ${type}: ${count}\n`;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
report += `\n## Errors by Severity\n`;
|
|
702
|
+
for (const [severity, count] of Object.entries(stats.bySeverity)) {
|
|
703
|
+
report += `- ${severity}: ${count}\n`;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
report += `\n## Strategy Effectiveness\n`;
|
|
707
|
+
for (const [strategy, data] of Object.entries(stats.strategySuccess)) {
|
|
708
|
+
const rate = data.total > 0 ? (data.success / data.total * 100).toFixed(1) : 0;
|
|
709
|
+
report += `- ${strategy}: ${rate}% success (${data.success}/${data.total})\n`;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
report += `\n## Recent Errors\n`;
|
|
713
|
+
for (const error of this.getRecentErrors(5)) {
|
|
714
|
+
const status = error.resolved ? '✅' : '❌';
|
|
715
|
+
report += `- ${status} [${error.type}] ${error.message.substring(0, 50)}...\n`;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return report;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Create self-correction system
|
|
724
|
+
* @param {SelfCorrectionOptions} [options={}] - System options
|
|
725
|
+
* @returns {SelfCorrection}
|
|
726
|
+
*/
|
|
727
|
+
function createSelfCorrection(options = {}) {
|
|
728
|
+
return new SelfCorrection(options);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Analyze and attempt to correct an error
|
|
733
|
+
* @param {Error|string} error - Error to correct
|
|
734
|
+
* @param {Function} operation - Operation to retry
|
|
735
|
+
* @param {Object} [options={}] - Correction options
|
|
736
|
+
* @returns {Promise<Object>}
|
|
737
|
+
*/
|
|
738
|
+
async function correctError(error, operation, options = {}) {
|
|
739
|
+
const corrector = createSelfCorrection(options);
|
|
740
|
+
const record = corrector.analyzeError(error, options.context || {});
|
|
741
|
+
return corrector.correct(record, operation, options);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
module.exports = {
|
|
745
|
+
SelfCorrection,
|
|
746
|
+
createSelfCorrection,
|
|
747
|
+
correctError,
|
|
748
|
+
SEVERITY,
|
|
749
|
+
CORRECTION_STRATEGY,
|
|
750
|
+
DEFAULT_PATTERNS
|
|
751
|
+
};
|