@sparkleideas/shared 3.0.0-alpha.7
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 +323 -0
- package/__tests__/hooks/bash-safety.test.ts +289 -0
- package/__tests__/hooks/file-organization.test.ts +335 -0
- package/__tests__/hooks/git-commit.test.ts +336 -0
- package/__tests__/hooks/index.ts +23 -0
- package/__tests__/hooks/session-hooks.test.ts +357 -0
- package/__tests__/hooks/task-hooks.test.ts +193 -0
- package/docs/EVENTS_IMPLEMENTATION_SUMMARY.md +388 -0
- package/docs/EVENTS_QUICK_REFERENCE.md +470 -0
- package/docs/EVENTS_README.md +352 -0
- package/package.json +39 -0
- package/src/core/config/defaults.ts +207 -0
- package/src/core/config/index.ts +15 -0
- package/src/core/config/loader.ts +271 -0
- package/src/core/config/schema.ts +188 -0
- package/src/core/config/validator.ts +209 -0
- package/src/core/event-bus.ts +236 -0
- package/src/core/index.ts +22 -0
- package/src/core/interfaces/agent.interface.ts +251 -0
- package/src/core/interfaces/coordinator.interface.ts +363 -0
- package/src/core/interfaces/event.interface.ts +267 -0
- package/src/core/interfaces/index.ts +19 -0
- package/src/core/interfaces/memory.interface.ts +332 -0
- package/src/core/interfaces/task.interface.ts +223 -0
- package/src/core/orchestrator/event-coordinator.ts +122 -0
- package/src/core/orchestrator/health-monitor.ts +214 -0
- package/src/core/orchestrator/index.ts +89 -0
- package/src/core/orchestrator/lifecycle-manager.ts +263 -0
- package/src/core/orchestrator/session-manager.ts +279 -0
- package/src/core/orchestrator/task-manager.ts +317 -0
- package/src/events/domain-events.ts +584 -0
- package/src/events/event-store.test.ts +387 -0
- package/src/events/event-store.ts +588 -0
- package/src/events/example-usage.ts +293 -0
- package/src/events/index.ts +90 -0
- package/src/events/projections.ts +561 -0
- package/src/events/state-reconstructor.ts +349 -0
- package/src/events.ts +367 -0
- package/src/hooks/INTEGRATION.md +658 -0
- package/src/hooks/README.md +532 -0
- package/src/hooks/example-usage.ts +499 -0
- package/src/hooks/executor.ts +379 -0
- package/src/hooks/hooks.test.ts +421 -0
- package/src/hooks/index.ts +131 -0
- package/src/hooks/registry.ts +333 -0
- package/src/hooks/safety/bash-safety.ts +604 -0
- package/src/hooks/safety/file-organization.ts +473 -0
- package/src/hooks/safety/git-commit.ts +623 -0
- package/src/hooks/safety/index.ts +46 -0
- package/src/hooks/session-hooks.ts +559 -0
- package/src/hooks/task-hooks.ts +513 -0
- package/src/hooks/types.ts +357 -0
- package/src/hooks/verify-exports.test.ts +125 -0
- package/src/index.ts +195 -0
- package/src/mcp/connection-pool.ts +438 -0
- package/src/mcp/index.ts +183 -0
- package/src/mcp/server.ts +774 -0
- package/src/mcp/session-manager.ts +428 -0
- package/src/mcp/tool-registry.ts +566 -0
- package/src/mcp/transport/http.ts +557 -0
- package/src/mcp/transport/index.ts +294 -0
- package/src/mcp/transport/stdio.ts +324 -0
- package/src/mcp/transport/websocket.ts +484 -0
- package/src/mcp/types.ts +565 -0
- package/src/plugin-interface.ts +663 -0
- package/src/plugin-loader.ts +638 -0
- package/src/plugin-registry.ts +604 -0
- package/src/plugins/index.ts +34 -0
- package/src/plugins/official/hive-mind-plugin.ts +330 -0
- package/src/plugins/official/index.ts +24 -0
- package/src/plugins/official/maestro-plugin.ts +508 -0
- package/src/plugins/types.ts +108 -0
- package/src/resilience/bulkhead.ts +277 -0
- package/src/resilience/circuit-breaker.ts +326 -0
- package/src/resilience/index.ts +26 -0
- package/src/resilience/rate-limiter.ts +420 -0
- package/src/resilience/retry.ts +224 -0
- package/src/security/index.ts +39 -0
- package/src/security/input-validation.ts +265 -0
- package/src/security/secure-random.ts +159 -0
- package/src/services/index.ts +16 -0
- package/src/services/v3-progress.service.ts +505 -0
- package/src/types/agent.types.ts +144 -0
- package/src/types/index.ts +22 -0
- package/src/types/mcp.types.ts +300 -0
- package/src/types/memory.types.ts +263 -0
- package/src/types/swarm.types.ts +255 -0
- package/src/types/task.types.ts +205 -0
- package/src/types.ts +367 -0
- package/src/utils/secure-logger.d.ts +69 -0
- package/src/utils/secure-logger.d.ts.map +1 -0
- package/src/utils/secure-logger.js +208 -0
- package/src/utils/secure-logger.js.map +1 -0
- package/src/utils/secure-logger.ts +257 -0
- package/tmp.json +0 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 Hooks System - Hook Executor
|
|
3
|
+
*
|
|
4
|
+
* Executes hooks in priority order with timeout handling and error recovery.
|
|
5
|
+
* Integrates with event bus for coordination and monitoring.
|
|
6
|
+
*
|
|
7
|
+
* @module v3/shared/hooks/executor
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { IEventBus } from '../core/interfaces/event.interface.js';
|
|
11
|
+
import { HookRegistry } from './registry.js';
|
|
12
|
+
import {
|
|
13
|
+
HookEvent,
|
|
14
|
+
HookContext,
|
|
15
|
+
HookResult,
|
|
16
|
+
HookExecutionOptions,
|
|
17
|
+
} from './types.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook execution result aggregation
|
|
21
|
+
*/
|
|
22
|
+
export interface AggregatedHookResult {
|
|
23
|
+
/** Whether all hooks succeeded */
|
|
24
|
+
success: boolean;
|
|
25
|
+
|
|
26
|
+
/** Individual hook results */
|
|
27
|
+
results: HookResult[];
|
|
28
|
+
|
|
29
|
+
/** Total execution time in ms */
|
|
30
|
+
totalExecutionTime: number;
|
|
31
|
+
|
|
32
|
+
/** Number of hooks executed */
|
|
33
|
+
hooksExecuted: number;
|
|
34
|
+
|
|
35
|
+
/** Number of hooks failed */
|
|
36
|
+
hooksFailed: number;
|
|
37
|
+
|
|
38
|
+
/** Whether operation was aborted */
|
|
39
|
+
aborted: boolean;
|
|
40
|
+
|
|
41
|
+
/** Final merged context (from all hooks) */
|
|
42
|
+
finalContext?: Partial<HookContext>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Hook executor implementation
|
|
47
|
+
*/
|
|
48
|
+
export class HookExecutor {
|
|
49
|
+
private registry: HookRegistry;
|
|
50
|
+
private eventBus?: IEventBus;
|
|
51
|
+
|
|
52
|
+
constructor(registry: HookRegistry, eventBus?: IEventBus) {
|
|
53
|
+
this.registry = registry;
|
|
54
|
+
this.eventBus = eventBus;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Execute all hooks for an event
|
|
59
|
+
*
|
|
60
|
+
* @param event - Hook event type
|
|
61
|
+
* @param context - Hook context
|
|
62
|
+
* @param options - Execution options
|
|
63
|
+
* @returns Aggregated results
|
|
64
|
+
*/
|
|
65
|
+
async execute(
|
|
66
|
+
event: HookEvent,
|
|
67
|
+
context: HookContext,
|
|
68
|
+
options: HookExecutionOptions = {}
|
|
69
|
+
): Promise<AggregatedHookResult> {
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
const results: HookResult[] = [];
|
|
72
|
+
let aborted = false;
|
|
73
|
+
let finalContext: Partial<HookContext> = {};
|
|
74
|
+
|
|
75
|
+
// Get enabled hooks for this event
|
|
76
|
+
const hooks = this.registry.getHandlers(event, false);
|
|
77
|
+
|
|
78
|
+
// Emit pre-execution event
|
|
79
|
+
this.eventBus?.emit('hooks:pre-execute', {
|
|
80
|
+
event,
|
|
81
|
+
hookCount: hooks.length,
|
|
82
|
+
context,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Execute hooks in priority order
|
|
86
|
+
for (const hook of hooks) {
|
|
87
|
+
if (aborted) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const result = await this.executeSingleHook(
|
|
93
|
+
hook.handler,
|
|
94
|
+
context,
|
|
95
|
+
hook.timeout || options.timeout
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
results.push(result);
|
|
99
|
+
|
|
100
|
+
// Record execution statistics
|
|
101
|
+
this.registry.recordExecution(result.success, result.executionTime || 0);
|
|
102
|
+
|
|
103
|
+
// Merge context modifications
|
|
104
|
+
if (result.data) {
|
|
105
|
+
finalContext = { ...finalContext, ...result.data };
|
|
106
|
+
// Update context for next hooks
|
|
107
|
+
Object.assign(context, result.data);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check if we should abort
|
|
111
|
+
if (result.abort) {
|
|
112
|
+
aborted = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check if we should stop the chain
|
|
117
|
+
if (result.continueChain === false) {
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check if we should stop on error
|
|
122
|
+
if (!result.success && !options.continueOnError) {
|
|
123
|
+
aborted = true;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
const errorResult: HookResult = {
|
|
128
|
+
success: false,
|
|
129
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
130
|
+
continueChain: options.continueOnError,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
results.push(errorResult);
|
|
134
|
+
this.registry.recordExecution(false, 0);
|
|
135
|
+
|
|
136
|
+
// Emit error event
|
|
137
|
+
this.eventBus?.emit('hooks:error', {
|
|
138
|
+
event,
|
|
139
|
+
hookId: hook.id,
|
|
140
|
+
error,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (!options.continueOnError) {
|
|
144
|
+
aborted = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const totalExecutionTime = Date.now() - startTime;
|
|
151
|
+
const hooksFailed = results.filter(r => !r.success).length;
|
|
152
|
+
|
|
153
|
+
// Build aggregated result
|
|
154
|
+
const aggregatedResult: AggregatedHookResult = {
|
|
155
|
+
success: hooksFailed === 0 && !aborted,
|
|
156
|
+
results: options.collectResults ? results : [],
|
|
157
|
+
totalExecutionTime,
|
|
158
|
+
hooksExecuted: results.length,
|
|
159
|
+
hooksFailed,
|
|
160
|
+
aborted,
|
|
161
|
+
finalContext,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Emit post-execution event
|
|
165
|
+
this.eventBus?.emit('hooks:post-execute', {
|
|
166
|
+
event,
|
|
167
|
+
...aggregatedResult,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return aggregatedResult;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Execute hooks with timeout
|
|
175
|
+
*
|
|
176
|
+
* @param event - Hook event type
|
|
177
|
+
* @param context - Hook context
|
|
178
|
+
* @param timeout - Timeout in ms
|
|
179
|
+
* @returns Aggregated results
|
|
180
|
+
*/
|
|
181
|
+
async executeWithTimeout(
|
|
182
|
+
event: HookEvent,
|
|
183
|
+
context: HookContext,
|
|
184
|
+
timeout: number
|
|
185
|
+
): Promise<AggregatedHookResult> {
|
|
186
|
+
return this.withTimeout(
|
|
187
|
+
this.execute(event, context, { timeout }),
|
|
188
|
+
timeout
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Execute a single hook with timeout and error handling
|
|
194
|
+
*
|
|
195
|
+
* @param handler - Hook handler function
|
|
196
|
+
* @param context - Hook context
|
|
197
|
+
* @param timeout - Optional timeout in ms
|
|
198
|
+
* @returns Hook result
|
|
199
|
+
*/
|
|
200
|
+
private async executeSingleHook(
|
|
201
|
+
handler: (context: HookContext) => Promise<HookResult> | HookResult,
|
|
202
|
+
context: HookContext,
|
|
203
|
+
timeout?: number
|
|
204
|
+
): Promise<HookResult> {
|
|
205
|
+
const startTime = Date.now();
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
let resultPromise = Promise.resolve(handler(context));
|
|
209
|
+
|
|
210
|
+
// Apply timeout if specified
|
|
211
|
+
if (timeout && timeout > 0) {
|
|
212
|
+
resultPromise = this.withTimeout(resultPromise, timeout);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const result = await resultPromise;
|
|
216
|
+
const executionTime = Date.now() - startTime;
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
...result,
|
|
220
|
+
executionTime,
|
|
221
|
+
};
|
|
222
|
+
} catch (error) {
|
|
223
|
+
const executionTime = Date.now() - startTime;
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
success: false,
|
|
227
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
228
|
+
executionTime,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Execute multiple hooks in parallel
|
|
235
|
+
*
|
|
236
|
+
* @param events - Array of hook events
|
|
237
|
+
* @param contexts - Array of contexts (matched by index)
|
|
238
|
+
* @param options - Execution options
|
|
239
|
+
* @returns Array of aggregated results
|
|
240
|
+
*/
|
|
241
|
+
async executeParallel(
|
|
242
|
+
events: HookEvent[],
|
|
243
|
+
contexts: HookContext[],
|
|
244
|
+
options: HookExecutionOptions = {}
|
|
245
|
+
): Promise<AggregatedHookResult[]> {
|
|
246
|
+
if (events.length !== contexts.length) {
|
|
247
|
+
throw new Error('Events and contexts arrays must have same length');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const maxParallel = options.maxParallel || events.length;
|
|
251
|
+
const results: AggregatedHookResult[] = [];
|
|
252
|
+
|
|
253
|
+
// Execute in batches
|
|
254
|
+
for (let i = 0; i < events.length; i += maxParallel) {
|
|
255
|
+
const batch = events.slice(i, i + maxParallel);
|
|
256
|
+
const batchContexts = contexts.slice(i, i + maxParallel);
|
|
257
|
+
|
|
258
|
+
const batchResults = await Promise.allSettled(
|
|
259
|
+
batch.map((event, index) =>
|
|
260
|
+
this.execute(event, batchContexts[index], options)
|
|
261
|
+
)
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
for (const result of batchResults) {
|
|
265
|
+
if (result.status === 'fulfilled') {
|
|
266
|
+
results.push(result.value);
|
|
267
|
+
} else {
|
|
268
|
+
// Create error result for rejected promises
|
|
269
|
+
results.push({
|
|
270
|
+
success: false,
|
|
271
|
+
results: [{
|
|
272
|
+
success: false,
|
|
273
|
+
error: result.reason instanceof Error
|
|
274
|
+
? result.reason
|
|
275
|
+
: new Error(String(result.reason)),
|
|
276
|
+
}],
|
|
277
|
+
totalExecutionTime: 0,
|
|
278
|
+
hooksExecuted: 0,
|
|
279
|
+
hooksFailed: 1,
|
|
280
|
+
aborted: true,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return results;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Execute hooks sequentially with context chaining
|
|
291
|
+
*
|
|
292
|
+
* @param events - Array of hook events
|
|
293
|
+
* @param initialContext - Initial context
|
|
294
|
+
* @param options - Execution options
|
|
295
|
+
* @returns Final aggregated result with chained context
|
|
296
|
+
*/
|
|
297
|
+
async executeSequential(
|
|
298
|
+
events: HookEvent[],
|
|
299
|
+
initialContext: HookContext,
|
|
300
|
+
options: HookExecutionOptions = {}
|
|
301
|
+
): Promise<AggregatedHookResult> {
|
|
302
|
+
const results: HookResult[] = [];
|
|
303
|
+
let currentContext = { ...initialContext };
|
|
304
|
+
let totalExecutionTime = 0;
|
|
305
|
+
let aborted = false;
|
|
306
|
+
|
|
307
|
+
for (const event of events) {
|
|
308
|
+
if (aborted) {
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const result = await this.execute(event, currentContext, options);
|
|
313
|
+
|
|
314
|
+
results.push(...result.results);
|
|
315
|
+
totalExecutionTime += result.totalExecutionTime;
|
|
316
|
+
|
|
317
|
+
// Merge context for next event
|
|
318
|
+
if (result.finalContext) {
|
|
319
|
+
currentContext = { ...currentContext, ...result.finalContext };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (result.aborted || !result.success) {
|
|
323
|
+
aborted = true;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const hooksFailed = results.filter(r => !r.success).length;
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
success: hooksFailed === 0 && !aborted,
|
|
332
|
+
results: options.collectResults ? results : [],
|
|
333
|
+
totalExecutionTime,
|
|
334
|
+
hooksExecuted: results.length,
|
|
335
|
+
hooksFailed,
|
|
336
|
+
aborted,
|
|
337
|
+
finalContext: currentContext,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Wrap a promise with timeout
|
|
343
|
+
*/
|
|
344
|
+
private async withTimeout<T>(
|
|
345
|
+
promise: Promise<T>,
|
|
346
|
+
timeout: number
|
|
347
|
+
): Promise<T> {
|
|
348
|
+
return Promise.race([
|
|
349
|
+
promise,
|
|
350
|
+
new Promise<T>((_, reject) =>
|
|
351
|
+
setTimeout(() => reject(new Error(`Hook execution timeout after ${timeout}ms`)), timeout)
|
|
352
|
+
),
|
|
353
|
+
]);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Set event bus for coordination
|
|
358
|
+
*/
|
|
359
|
+
setEventBus(eventBus: IEventBus): void {
|
|
360
|
+
this.eventBus = eventBus;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get hook registry
|
|
365
|
+
*/
|
|
366
|
+
getRegistry(): HookRegistry {
|
|
367
|
+
return this.registry;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Create a new hook executor
|
|
373
|
+
*/
|
|
374
|
+
export function createHookExecutor(
|
|
375
|
+
registry: HookRegistry,
|
|
376
|
+
eventBus?: IEventBus
|
|
377
|
+
): HookExecutor {
|
|
378
|
+
return new HookExecutor(registry, eventBus);
|
|
379
|
+
}
|