claude-flow-novice 1.5.17 → 1.5.19
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/.claude-flow-novice/dist/config/hooks/post-edit-pipeline.js +1837 -0
- package/.claude-flow-novice/dist/src/hooks/communication-integrated-post-edit.js +673 -0
- package/.claude-flow-novice/dist/src/hooks/enhanced/experience-adaptation-hooks.js +347 -0
- package/.claude-flow-novice/dist/src/hooks/enhanced/personalization-hooks.js +118 -0
- package/.claude-flow-novice/dist/src/hooks/enhanced-post-edit-pipeline.js +2044 -0
- package/.claude-flow-novice/dist/src/hooks/filter-integration.js +542 -0
- package/.claude-flow-novice/dist/src/hooks/guidance-hooks.js +629 -0
- package/.claude-flow-novice/dist/src/hooks/index.ts +239 -0
- package/.claude-flow-novice/dist/src/hooks/managers/enhanced-hook-manager.js +200 -0
- package/.claude-flow-novice/dist/src/hooks/resilient-hook-system.js +812 -0
- package/CHANGELOG.md +22 -0
- package/config/hooks/post-edit-pipeline.js +30 -0
- package/package.json +2 -1
- package/src/cli/simple-commands/init/templates/CLAUDE.md +38 -6
- package/src/hooks/communication-integrated-post-edit.js +673 -0
- package/src/hooks/enhanced/experience-adaptation-hooks.js +347 -0
- package/src/hooks/enhanced/personalization-hooks.js +118 -0
- package/src/hooks/enhanced-hooks-cli.js +168 -0
- package/src/hooks/enhanced-post-edit-pipeline.js +2044 -0
- package/src/hooks/filter-integration.js +542 -0
- package/src/hooks/guidance-hooks.js +629 -0
- package/src/hooks/index.ts +239 -0
- package/src/hooks/managers/enhanced-hook-manager.js +200 -0
- package/src/hooks/resilient-hook-system.js +812 -0
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilient Hook System - Dependency-Free Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides robust hook functionality without external dependencies.
|
|
5
|
+
* Maintains 100% functionality even in minimal environments.
|
|
6
|
+
* Critical for Byzantine consensus hook integration.
|
|
7
|
+
*
|
|
8
|
+
* @module resilient-hook-system
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
import { performance } from 'perf_hooks';
|
|
13
|
+
import { ResilientMemorySystem } from '../memory/fallback-memory-system.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Self-Contained Hook Engine
|
|
17
|
+
* Zero external dependencies beyond Node.js built-ins
|
|
18
|
+
*/
|
|
19
|
+
export class ResilientHookEngine extends EventEmitter {
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
super();
|
|
22
|
+
|
|
23
|
+
this.options = {
|
|
24
|
+
maxConcurrentHooks: options.maxConcurrentHooks || 50,
|
|
25
|
+
maxQueueSize: options.maxQueueSize || 1000,
|
|
26
|
+
hookTimeout: options.hookTimeout || 30000, // 30 seconds
|
|
27
|
+
enableMetrics: options.enableMetrics !== false,
|
|
28
|
+
enableByzantineConsensus: options.enableByzantineConsensus !== false,
|
|
29
|
+
consensusThreshold: options.consensusThreshold || 0.85,
|
|
30
|
+
retryAttempts: options.retryAttempts || 3,
|
|
31
|
+
retryDelay: options.retryDelay || 1000,
|
|
32
|
+
...options,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Core hook storage
|
|
36
|
+
this.hooks = new Map();
|
|
37
|
+
this.hookQueue = [];
|
|
38
|
+
this.activeHooks = new Map();
|
|
39
|
+
this.hookHistory = [];
|
|
40
|
+
|
|
41
|
+
// Performance metrics
|
|
42
|
+
this.metrics = {
|
|
43
|
+
hooksRegistered: 0,
|
|
44
|
+
hooksExecuted: 0,
|
|
45
|
+
hooksSucceeded: 0,
|
|
46
|
+
hooksFailed: 0,
|
|
47
|
+
totalExecutionTime: 0,
|
|
48
|
+
averageExecutionTime: 0,
|
|
49
|
+
lastExecution: null,
|
|
50
|
+
queueSize: 0,
|
|
51
|
+
activeCount: 0,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Byzantine consensus state
|
|
55
|
+
this.consensusState = {
|
|
56
|
+
validations: new Map(),
|
|
57
|
+
agreements: 0,
|
|
58
|
+
disagreements: 0,
|
|
59
|
+
accuracy: 0,
|
|
60
|
+
truthScores: new Map(),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Memory system for coordination
|
|
64
|
+
this.memory = null;
|
|
65
|
+
this.isInitialized = false;
|
|
66
|
+
this.isRunning = false;
|
|
67
|
+
|
|
68
|
+
// Hook processing interval
|
|
69
|
+
this.processingTimer = null;
|
|
70
|
+
this.processingInterval = 100; // 100ms
|
|
71
|
+
|
|
72
|
+
// Built-in hook types
|
|
73
|
+
this.HOOK_TYPES = {
|
|
74
|
+
PRE_TASK: 'pre-task',
|
|
75
|
+
POST_TASK: 'post-task',
|
|
76
|
+
PRE_EDIT: 'pre-edit',
|
|
77
|
+
POST_EDIT: 'post-edit',
|
|
78
|
+
FILE_CHANGE: 'file-change',
|
|
79
|
+
VALIDATION: 'validation',
|
|
80
|
+
CONSENSUS: 'consensus',
|
|
81
|
+
ERROR: 'error',
|
|
82
|
+
COMPLETION: 'completion',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Event binding
|
|
86
|
+
this.bindEvents();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Initialize the resilient hook system
|
|
91
|
+
*/
|
|
92
|
+
async initialize() {
|
|
93
|
+
if (this.isInitialized) return;
|
|
94
|
+
|
|
95
|
+
const startTime = performance.now();
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Initialize memory system with fallback capability
|
|
99
|
+
if (this.options.enableByzantineConsensus) {
|
|
100
|
+
this.memory = new ResilientMemorySystem({
|
|
101
|
+
enablePersistence: false, // In-memory only for resilience
|
|
102
|
+
maxMemoryMB: 50,
|
|
103
|
+
byzantineMode: true,
|
|
104
|
+
consensusThreshold: this.options.consensusThreshold || 0.85,
|
|
105
|
+
});
|
|
106
|
+
await this.memory.initialize();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.isInitialized = true;
|
|
110
|
+
this.isRunning = true;
|
|
111
|
+
|
|
112
|
+
// Start hook processing
|
|
113
|
+
this.startProcessing();
|
|
114
|
+
|
|
115
|
+
const duration = performance.now() - startTime;
|
|
116
|
+
|
|
117
|
+
this.emit('initialized', {
|
|
118
|
+
duration,
|
|
119
|
+
memoryMode: this.memory?.getSystemInfo().mode || 'none',
|
|
120
|
+
byzantineEnabled: this.options.enableByzantineConsensus,
|
|
121
|
+
resilient: true,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
console.log(`✅ Resilient Hook System initialized (${duration.toFixed(2)}ms)`);
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
resilient: true,
|
|
129
|
+
memoryMode: this.memory?.getSystemInfo().mode || 'none',
|
|
130
|
+
duration,
|
|
131
|
+
};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
this.emit('error', error);
|
|
134
|
+
throw new Error(`Failed to initialize Resilient Hook System: ${error.message}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Register a hook with full resilience
|
|
140
|
+
*/
|
|
141
|
+
register(hookConfig) {
|
|
142
|
+
this.ensureInitialized();
|
|
143
|
+
|
|
144
|
+
const hook = {
|
|
145
|
+
id: hookConfig.id || this.generateHookId(),
|
|
146
|
+
name: hookConfig.name || 'Anonymous Hook',
|
|
147
|
+
type: hookConfig.type || this.HOOK_TYPES.VALIDATION,
|
|
148
|
+
handler: hookConfig.handler,
|
|
149
|
+
priority: hookConfig.priority || 5,
|
|
150
|
+
enabled: hookConfig.enabled !== false,
|
|
151
|
+
timeout: hookConfig.timeout || this.options.hookTimeout,
|
|
152
|
+
retryAttempts: hookConfig.retryAttempts || this.options.retryAttempts,
|
|
153
|
+
conditions: hookConfig.conditions || [],
|
|
154
|
+
metadata: hookConfig.metadata || {},
|
|
155
|
+
createdAt: Date.now(),
|
|
156
|
+
executionCount: 0,
|
|
157
|
+
successCount: 0,
|
|
158
|
+
failureCount: 0,
|
|
159
|
+
averageExecutionTime: 0,
|
|
160
|
+
lastExecuted: null,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Validate hook
|
|
164
|
+
if (!this.validateHook(hook)) {
|
|
165
|
+
throw new Error(`Invalid hook configuration: ${hook.name}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Store hook
|
|
169
|
+
this.hooks.set(hook.id, hook);
|
|
170
|
+
this.metrics.hooksRegistered++;
|
|
171
|
+
|
|
172
|
+
// Emit registration event
|
|
173
|
+
this.emit('hookRegistered', { hook });
|
|
174
|
+
|
|
175
|
+
console.log(`🪝 Registered hook: ${hook.name} (${hook.type})`);
|
|
176
|
+
|
|
177
|
+
return hook.id;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Execute hooks for a specific event
|
|
182
|
+
*/
|
|
183
|
+
async executeHooks(eventType, payload = {}) {
|
|
184
|
+
this.ensureInitialized();
|
|
185
|
+
|
|
186
|
+
const startTime = performance.now();
|
|
187
|
+
const executionId = this.generateExecutionId();
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
// Find matching hooks
|
|
191
|
+
const matchingHooks = this.findMatchingHooks(eventType, payload);
|
|
192
|
+
|
|
193
|
+
if (matchingHooks.length === 0) {
|
|
194
|
+
return { executionId, results: [], duration: performance.now() - startTime };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Sort by priority
|
|
198
|
+
matchingHooks.sort((a, b) => b.priority - a.priority);
|
|
199
|
+
|
|
200
|
+
// Execute hooks
|
|
201
|
+
const results = [];
|
|
202
|
+
const byzantineValidations = [];
|
|
203
|
+
|
|
204
|
+
for (const hook of matchingHooks) {
|
|
205
|
+
try {
|
|
206
|
+
const hookResult = await this.executeHook(hook, eventType, payload);
|
|
207
|
+
results.push(hookResult);
|
|
208
|
+
|
|
209
|
+
// Byzantine consensus validation
|
|
210
|
+
if (this.options.enableByzantineConsensus && hookResult.success) {
|
|
211
|
+
byzantineValidations.push({
|
|
212
|
+
hookId: hook.id,
|
|
213
|
+
result: hookResult,
|
|
214
|
+
score: this.calculateTruthScore(hookResult),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
} catch (error) {
|
|
218
|
+
const hookResult = {
|
|
219
|
+
hookId: hook.id,
|
|
220
|
+
success: false,
|
|
221
|
+
error: error.message,
|
|
222
|
+
duration: 0,
|
|
223
|
+
};
|
|
224
|
+
results.push(hookResult);
|
|
225
|
+
this.metrics.hooksFailed++;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Process Byzantine consensus if enabled
|
|
230
|
+
if (this.options.enableByzantineConsensus && byzantineValidations.length > 0) {
|
|
231
|
+
await this.processByzantineConsensus(executionId, byzantineValidations);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const totalDuration = performance.now() - startTime;
|
|
235
|
+
|
|
236
|
+
// Update metrics
|
|
237
|
+
this.updateExecutionMetrics(totalDuration, results);
|
|
238
|
+
|
|
239
|
+
// Store execution history
|
|
240
|
+
const execution = {
|
|
241
|
+
id: executionId,
|
|
242
|
+
eventType,
|
|
243
|
+
payload,
|
|
244
|
+
results,
|
|
245
|
+
duration: totalDuration,
|
|
246
|
+
timestamp: Date.now(),
|
|
247
|
+
byzantineConsensus: byzantineValidations.length > 0,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
this.hookHistory.unshift(execution);
|
|
251
|
+
|
|
252
|
+
// Keep history size manageable
|
|
253
|
+
if (this.hookHistory.length > 1000) {
|
|
254
|
+
this.hookHistory = this.hookHistory.slice(0, 1000);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.emit('hooksExecuted', execution);
|
|
258
|
+
|
|
259
|
+
return execution;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
this.emit('error', error);
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Execute a single hook with full error handling and retries
|
|
268
|
+
*/
|
|
269
|
+
async executeHook(hook, eventType, payload) {
|
|
270
|
+
const startTime = performance.now();
|
|
271
|
+
let lastError = null;
|
|
272
|
+
let attempts = 0;
|
|
273
|
+
|
|
274
|
+
while (attempts <= hook.retryAttempts) {
|
|
275
|
+
try {
|
|
276
|
+
// Check if hook should execute
|
|
277
|
+
if (!hook.enabled || !this.evaluateConditions(hook.conditions, payload)) {
|
|
278
|
+
return {
|
|
279
|
+
hookId: hook.id,
|
|
280
|
+
success: true,
|
|
281
|
+
skipped: true,
|
|
282
|
+
reason: 'Conditions not met',
|
|
283
|
+
duration: performance.now() - startTime,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Set timeout protection
|
|
288
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
289
|
+
setTimeout(() => reject(new Error('Hook timeout')), hook.timeout);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Execute hook handler
|
|
293
|
+
const executionPromise = hook.handler({
|
|
294
|
+
eventType,
|
|
295
|
+
payload,
|
|
296
|
+
hookId: hook.id,
|
|
297
|
+
hookName: hook.name,
|
|
298
|
+
metadata: hook.metadata,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const result = await Promise.race([executionPromise, timeoutPromise]);
|
|
302
|
+
|
|
303
|
+
// Success path
|
|
304
|
+
const duration = performance.now() - startTime;
|
|
305
|
+
|
|
306
|
+
hook.executionCount++;
|
|
307
|
+
hook.successCount++;
|
|
308
|
+
hook.lastExecuted = Date.now();
|
|
309
|
+
|
|
310
|
+
const totalTime = hook.averageExecutionTime * (hook.executionCount - 1) + duration;
|
|
311
|
+
hook.averageExecutionTime = totalTime / hook.executionCount;
|
|
312
|
+
|
|
313
|
+
this.metrics.hooksExecuted++;
|
|
314
|
+
this.metrics.hooksSucceeded++;
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
hookId: hook.id,
|
|
318
|
+
success: true,
|
|
319
|
+
result,
|
|
320
|
+
duration,
|
|
321
|
+
attempts: attempts + 1,
|
|
322
|
+
};
|
|
323
|
+
} catch (error) {
|
|
324
|
+
lastError = error;
|
|
325
|
+
attempts++;
|
|
326
|
+
|
|
327
|
+
if (attempts <= hook.retryAttempts) {
|
|
328
|
+
// Wait before retry
|
|
329
|
+
await new Promise((resolve) => setTimeout(resolve, this.options.retryDelay * attempts));
|
|
330
|
+
console.warn(`🔄 Retrying hook ${hook.name} (attempt ${attempts + 1})`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// All attempts failed
|
|
336
|
+
const duration = performance.now() - startTime;
|
|
337
|
+
|
|
338
|
+
hook.executionCount++;
|
|
339
|
+
hook.failureCount++;
|
|
340
|
+
hook.lastExecuted = Date.now();
|
|
341
|
+
|
|
342
|
+
this.metrics.hooksExecuted++;
|
|
343
|
+
this.metrics.hooksFailed++;
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
hookId: hook.id,
|
|
347
|
+
success: false,
|
|
348
|
+
error: lastError.message,
|
|
349
|
+
duration,
|
|
350
|
+
attempts,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Find hooks that match the event type and payload
|
|
356
|
+
*/
|
|
357
|
+
findMatchingHooks(eventType, payload) {
|
|
358
|
+
const matches = [];
|
|
359
|
+
|
|
360
|
+
for (const hook of this.hooks.values()) {
|
|
361
|
+
if (!hook.enabled) continue;
|
|
362
|
+
|
|
363
|
+
// Type matching
|
|
364
|
+
if (hook.type === eventType || hook.type === 'all') {
|
|
365
|
+
matches.push(hook);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return matches;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Evaluate hook conditions against payload
|
|
374
|
+
*/
|
|
375
|
+
evaluateConditions(conditions, payload) {
|
|
376
|
+
if (!conditions || conditions.length === 0) {
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return conditions.every((condition) => {
|
|
381
|
+
try {
|
|
382
|
+
switch (condition.type) {
|
|
383
|
+
case 'filePattern':
|
|
384
|
+
return payload.file && this.matchPattern(payload.file, condition.pattern);
|
|
385
|
+
case 'namespace':
|
|
386
|
+
return payload.namespace === condition.value;
|
|
387
|
+
case 'property':
|
|
388
|
+
return payload[condition.property] === condition.value;
|
|
389
|
+
default:
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
} catch (error) {
|
|
393
|
+
console.warn(`Hook condition evaluation failed: ${error.message}`);
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Simple pattern matching
|
|
401
|
+
*/
|
|
402
|
+
matchPattern(text, pattern) {
|
|
403
|
+
if (!pattern) return true;
|
|
404
|
+
if (pattern === '*') return true;
|
|
405
|
+
|
|
406
|
+
// Convert glob pattern to regex
|
|
407
|
+
const regexPattern = pattern.replace(/\*/g, '.*').replace(/\?/g, '.').replace(/\./g, '\\.');
|
|
408
|
+
|
|
409
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
410
|
+
return regex.test(text);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Byzantine consensus validation
|
|
415
|
+
*/
|
|
416
|
+
async processByzantineConsensus(executionId, validations) {
|
|
417
|
+
if (!this.memory) return;
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
// Calculate consensus score
|
|
421
|
+
const scores = validations.map((v) => v.score);
|
|
422
|
+
const averageScore = scores.reduce((a, b) => a + b, 0) / scores.length;
|
|
423
|
+
const consensusReached = averageScore >= this.options.consensusThreshold;
|
|
424
|
+
|
|
425
|
+
// Record truth scores
|
|
426
|
+
for (const validation of validations) {
|
|
427
|
+
await this.memory.recordTruthScore(
|
|
428
|
+
`hook_${validation.hookId}_${executionId}`,
|
|
429
|
+
validation.score,
|
|
430
|
+
{
|
|
431
|
+
executionId,
|
|
432
|
+
hookId: validation.hookId,
|
|
433
|
+
result: validation.result,
|
|
434
|
+
consensusReached,
|
|
435
|
+
averageScore,
|
|
436
|
+
},
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Update consensus state
|
|
441
|
+
if (consensusReached) {
|
|
442
|
+
this.consensusState.agreements++;
|
|
443
|
+
} else {
|
|
444
|
+
this.consensusState.disagreements++;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const total = this.consensusState.agreements + this.consensusState.disagreements;
|
|
448
|
+
this.consensusState.accuracy = total > 0 ? this.consensusState.agreements / total : 0;
|
|
449
|
+
|
|
450
|
+
this.emit('byzantineConsensus', {
|
|
451
|
+
executionId,
|
|
452
|
+
consensusReached,
|
|
453
|
+
averageScore,
|
|
454
|
+
accuracy: this.consensusState.accuracy,
|
|
455
|
+
});
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.warn('Byzantine consensus processing failed:', error.message);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Calculate truth score for hook result
|
|
463
|
+
*/
|
|
464
|
+
calculateTruthScore(hookResult) {
|
|
465
|
+
let score = 0.5; // Base score
|
|
466
|
+
|
|
467
|
+
// Success increases score
|
|
468
|
+
if (hookResult.success) {
|
|
469
|
+
score += 0.3;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Fast execution increases score
|
|
473
|
+
if (hookResult.duration < 1000) {
|
|
474
|
+
score += 0.1;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Result presence increases score
|
|
478
|
+
if (hookResult.result) {
|
|
479
|
+
score += 0.1;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return Math.min(1.0, score);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Get comprehensive hook statistics
|
|
487
|
+
*/
|
|
488
|
+
async getStats() {
|
|
489
|
+
this.ensureInitialized();
|
|
490
|
+
|
|
491
|
+
const hookStats = [];
|
|
492
|
+
for (const hook of this.hooks.values()) {
|
|
493
|
+
hookStats.push({
|
|
494
|
+
id: hook.id,
|
|
495
|
+
name: hook.name,
|
|
496
|
+
type: hook.type,
|
|
497
|
+
enabled: hook.enabled,
|
|
498
|
+
executionCount: hook.executionCount,
|
|
499
|
+
successCount: hook.successCount,
|
|
500
|
+
failureCount: hook.failureCount,
|
|
501
|
+
successRate: hook.executionCount > 0 ? hook.successCount / hook.executionCount : 0,
|
|
502
|
+
averageExecutionTime: hook.averageExecutionTime,
|
|
503
|
+
lastExecuted: hook.lastExecuted,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
let memoryStats = null;
|
|
508
|
+
if (this.memory) {
|
|
509
|
+
try {
|
|
510
|
+
memoryStats = await this.memory.getStats();
|
|
511
|
+
} catch (error) {
|
|
512
|
+
console.warn('Failed to get memory stats:', error.message);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
system: {
|
|
518
|
+
initialized: this.isInitialized,
|
|
519
|
+
running: this.isRunning,
|
|
520
|
+
resilient: true,
|
|
521
|
+
byzantineEnabled: this.options.enableByzantineConsensus,
|
|
522
|
+
},
|
|
523
|
+
metrics: { ...this.metrics },
|
|
524
|
+
consensus: { ...this.consensusState },
|
|
525
|
+
hooks: hookStats,
|
|
526
|
+
memory: memoryStats,
|
|
527
|
+
queue: {
|
|
528
|
+
size: this.hookQueue.length,
|
|
529
|
+
active: this.activeHooks.size,
|
|
530
|
+
},
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Get truth scores from Byzantine consensus
|
|
536
|
+
*/
|
|
537
|
+
async getTruthScores(keyPattern = null) {
|
|
538
|
+
if (!this.memory) {
|
|
539
|
+
return { scores: [], consensus: this.consensusState };
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
try {
|
|
543
|
+
return await this.memory.getTruthScores(keyPattern);
|
|
544
|
+
} catch (error) {
|
|
545
|
+
console.warn('Failed to get truth scores:', error.message);
|
|
546
|
+
return { scores: [], consensus: this.consensusState };
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Shutdown the hook system
|
|
552
|
+
*/
|
|
553
|
+
async shutdown() {
|
|
554
|
+
if (!this.isRunning) return;
|
|
555
|
+
|
|
556
|
+
try {
|
|
557
|
+
this.isRunning = false;
|
|
558
|
+
|
|
559
|
+
// Stop processing
|
|
560
|
+
if (this.processingTimer) {
|
|
561
|
+
clearInterval(this.processingTimer);
|
|
562
|
+
this.processingTimer = null;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Wait for active hooks to complete
|
|
566
|
+
const activeHookPromises = Array.from(this.activeHooks.values());
|
|
567
|
+
if (activeHookPromises.length > 0) {
|
|
568
|
+
console.log(`⏳ Waiting for ${activeHookPromises.length} active hooks to complete...`);
|
|
569
|
+
await Promise.allSettled(activeHookPromises);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Close memory system
|
|
573
|
+
if (this.memory) {
|
|
574
|
+
await this.memory.close();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
this.emit('shutdown');
|
|
578
|
+
console.log('✅ Resilient Hook System shut down successfully');
|
|
579
|
+
} catch (error) {
|
|
580
|
+
this.emit('error', error);
|
|
581
|
+
throw error;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Private helper methods
|
|
586
|
+
ensureInitialized() {
|
|
587
|
+
if (!this.isInitialized) {
|
|
588
|
+
throw new Error('Resilient Hook System not initialized. Call initialize() first.');
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
validateHook(hook) {
|
|
593
|
+
return hook.id && hook.name && hook.type && typeof hook.handler === 'function';
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
generateHookId() {
|
|
597
|
+
return `hook_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
generateExecutionId() {
|
|
601
|
+
return `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
updateExecutionMetrics(duration, results) {
|
|
605
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
606
|
+
const failed = results.filter((r) => !r.success).length;
|
|
607
|
+
|
|
608
|
+
this.metrics.totalExecutionTime += duration;
|
|
609
|
+
this.metrics.averageExecutionTime =
|
|
610
|
+
this.metrics.hooksExecuted > 0
|
|
611
|
+
? this.metrics.totalExecutionTime / this.metrics.hooksExecuted
|
|
612
|
+
: 0;
|
|
613
|
+
this.metrics.lastExecution = Date.now();
|
|
614
|
+
this.metrics.queueSize = this.hookQueue.length;
|
|
615
|
+
this.metrics.activeCount = this.activeHooks.size;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
startProcessing() {
|
|
619
|
+
if (this.processingTimer) return;
|
|
620
|
+
|
|
621
|
+
this.processingTimer = setInterval(() => {
|
|
622
|
+
this.processQueue();
|
|
623
|
+
}, this.processingInterval);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
processQueue() {
|
|
627
|
+
// Process queued hooks if any
|
|
628
|
+
while (this.hookQueue.length > 0 && this.activeHooks.size < this.options.maxConcurrentHooks) {
|
|
629
|
+
const queuedHook = this.hookQueue.shift();
|
|
630
|
+
this.processQueuedHook(queuedHook);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async processQueuedHook(queuedHook) {
|
|
635
|
+
const { hook, eventType, payload, resolve, reject } = queuedHook;
|
|
636
|
+
|
|
637
|
+
try {
|
|
638
|
+
const result = await this.executeHook(hook, eventType, payload);
|
|
639
|
+
resolve(result);
|
|
640
|
+
} catch (error) {
|
|
641
|
+
reject(error);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
bindEvents() {
|
|
646
|
+
this.on('error', (error) => {
|
|
647
|
+
console.error('🚨 Resilient Hook System Error:', error.message);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Built-in resilient hooks for common scenarios
|
|
654
|
+
*/
|
|
655
|
+
export class BuiltInResilientHooks {
|
|
656
|
+
/**
|
|
657
|
+
* Pre-task validation hook
|
|
658
|
+
*/
|
|
659
|
+
static createPreTaskHook(options = {}) {
|
|
660
|
+
return {
|
|
661
|
+
name: 'Pre-Task Validation',
|
|
662
|
+
type: 'pre-task',
|
|
663
|
+
priority: 9,
|
|
664
|
+
handler: async ({ payload }) => {
|
|
665
|
+
// Validate task parameters
|
|
666
|
+
if (!payload.task) {
|
|
667
|
+
throw new Error('Task payload required');
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return {
|
|
671
|
+
validated: true,
|
|
672
|
+
task: payload.task,
|
|
673
|
+
timestamp: Date.now(),
|
|
674
|
+
};
|
|
675
|
+
},
|
|
676
|
+
conditions: [],
|
|
677
|
+
metadata: { builtin: true, ...options },
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Post-task completion hook
|
|
683
|
+
*/
|
|
684
|
+
static createPostTaskHook(options = {}) {
|
|
685
|
+
return {
|
|
686
|
+
name: 'Post-Task Completion',
|
|
687
|
+
type: 'post-task',
|
|
688
|
+
priority: 8,
|
|
689
|
+
handler: async ({ payload }) => {
|
|
690
|
+
// Record task completion
|
|
691
|
+
return {
|
|
692
|
+
completed: true,
|
|
693
|
+
task: payload.task,
|
|
694
|
+
duration: payload.duration || 0,
|
|
695
|
+
timestamp: Date.now(),
|
|
696
|
+
};
|
|
697
|
+
},
|
|
698
|
+
conditions: [],
|
|
699
|
+
metadata: { builtin: true, ...options },
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* File change validation hook
|
|
705
|
+
*/
|
|
706
|
+
static createFileChangeHook(options = {}) {
|
|
707
|
+
return {
|
|
708
|
+
name: 'File Change Validation',
|
|
709
|
+
type: 'file-change',
|
|
710
|
+
priority: 7,
|
|
711
|
+
handler: async ({ payload }) => {
|
|
712
|
+
// Validate file changes
|
|
713
|
+
if (!payload.file) {
|
|
714
|
+
throw new Error('File path required');
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return {
|
|
718
|
+
validated: true,
|
|
719
|
+
file: payload.file,
|
|
720
|
+
operation: payload.operation || 'unknown',
|
|
721
|
+
timestamp: Date.now(),
|
|
722
|
+
};
|
|
723
|
+
},
|
|
724
|
+
conditions: options.filePattern
|
|
725
|
+
? [
|
|
726
|
+
{
|
|
727
|
+
type: 'filePattern',
|
|
728
|
+
pattern: options.filePattern,
|
|
729
|
+
},
|
|
730
|
+
]
|
|
731
|
+
: [],
|
|
732
|
+
metadata: { builtin: true, ...options },
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Completion validation hook
|
|
738
|
+
*/
|
|
739
|
+
static createCompletionHook(options = {}) {
|
|
740
|
+
return {
|
|
741
|
+
name: 'Completion Validation',
|
|
742
|
+
type: 'completion',
|
|
743
|
+
priority: 10,
|
|
744
|
+
handler: async ({ payload }) => {
|
|
745
|
+
// Validate completion claims
|
|
746
|
+
const truthScore = options.truthScore || 0.85;
|
|
747
|
+
|
|
748
|
+
return {
|
|
749
|
+
validated: payload.completed === true,
|
|
750
|
+
truthScore,
|
|
751
|
+
evidence: payload.evidence || {},
|
|
752
|
+
timestamp: Date.now(),
|
|
753
|
+
};
|
|
754
|
+
},
|
|
755
|
+
conditions: [],
|
|
756
|
+
metadata: { builtin: true, critical: true, ...options },
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Factory function for creating resilient hook systems
|
|
763
|
+
*/
|
|
764
|
+
export function createResilientHookSystem(options = {}) {
|
|
765
|
+
return new ResilientHookEngine(options);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Test if hook system is functioning without dependencies
|
|
770
|
+
*/
|
|
771
|
+
export async function testHookSystemResilience() {
|
|
772
|
+
try {
|
|
773
|
+
const hookSystem = new ResilientHookEngine({
|
|
774
|
+
enableByzantineConsensus: true,
|
|
775
|
+
enableMetrics: true,
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
await hookSystem.initialize();
|
|
779
|
+
|
|
780
|
+
// Register test hook
|
|
781
|
+
const hookId = hookSystem.register({
|
|
782
|
+
name: 'Test Resilience Hook',
|
|
783
|
+
type: 'test',
|
|
784
|
+
handler: async () => ({ tested: true, timestamp: Date.now() }),
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// Execute test hook
|
|
788
|
+
const result = await hookSystem.executeHooks('test', { test: true });
|
|
789
|
+
|
|
790
|
+
// Get stats
|
|
791
|
+
const stats = await hookSystem.getStats();
|
|
792
|
+
|
|
793
|
+
// Shutdown
|
|
794
|
+
await hookSystem.shutdown();
|
|
795
|
+
|
|
796
|
+
return {
|
|
797
|
+
resilient: true,
|
|
798
|
+
tested: result.results.length > 0 && result.results[0].success,
|
|
799
|
+
memoryMode: stats.memory?.system?.mode || 'none',
|
|
800
|
+
byzantineEnabled: stats.system.byzantineEnabled,
|
|
801
|
+
error: null,
|
|
802
|
+
};
|
|
803
|
+
} catch (error) {
|
|
804
|
+
return {
|
|
805
|
+
resilient: false,
|
|
806
|
+
tested: false,
|
|
807
|
+
error: error.message,
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
export default ResilientHookEngine;
|