@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.
Files changed (96) hide show
  1. package/README.md +323 -0
  2. package/__tests__/hooks/bash-safety.test.ts +289 -0
  3. package/__tests__/hooks/file-organization.test.ts +335 -0
  4. package/__tests__/hooks/git-commit.test.ts +336 -0
  5. package/__tests__/hooks/index.ts +23 -0
  6. package/__tests__/hooks/session-hooks.test.ts +357 -0
  7. package/__tests__/hooks/task-hooks.test.ts +193 -0
  8. package/docs/EVENTS_IMPLEMENTATION_SUMMARY.md +388 -0
  9. package/docs/EVENTS_QUICK_REFERENCE.md +470 -0
  10. package/docs/EVENTS_README.md +352 -0
  11. package/package.json +39 -0
  12. package/src/core/config/defaults.ts +207 -0
  13. package/src/core/config/index.ts +15 -0
  14. package/src/core/config/loader.ts +271 -0
  15. package/src/core/config/schema.ts +188 -0
  16. package/src/core/config/validator.ts +209 -0
  17. package/src/core/event-bus.ts +236 -0
  18. package/src/core/index.ts +22 -0
  19. package/src/core/interfaces/agent.interface.ts +251 -0
  20. package/src/core/interfaces/coordinator.interface.ts +363 -0
  21. package/src/core/interfaces/event.interface.ts +267 -0
  22. package/src/core/interfaces/index.ts +19 -0
  23. package/src/core/interfaces/memory.interface.ts +332 -0
  24. package/src/core/interfaces/task.interface.ts +223 -0
  25. package/src/core/orchestrator/event-coordinator.ts +122 -0
  26. package/src/core/orchestrator/health-monitor.ts +214 -0
  27. package/src/core/orchestrator/index.ts +89 -0
  28. package/src/core/orchestrator/lifecycle-manager.ts +263 -0
  29. package/src/core/orchestrator/session-manager.ts +279 -0
  30. package/src/core/orchestrator/task-manager.ts +317 -0
  31. package/src/events/domain-events.ts +584 -0
  32. package/src/events/event-store.test.ts +387 -0
  33. package/src/events/event-store.ts +588 -0
  34. package/src/events/example-usage.ts +293 -0
  35. package/src/events/index.ts +90 -0
  36. package/src/events/projections.ts +561 -0
  37. package/src/events/state-reconstructor.ts +349 -0
  38. package/src/events.ts +367 -0
  39. package/src/hooks/INTEGRATION.md +658 -0
  40. package/src/hooks/README.md +532 -0
  41. package/src/hooks/example-usage.ts +499 -0
  42. package/src/hooks/executor.ts +379 -0
  43. package/src/hooks/hooks.test.ts +421 -0
  44. package/src/hooks/index.ts +131 -0
  45. package/src/hooks/registry.ts +333 -0
  46. package/src/hooks/safety/bash-safety.ts +604 -0
  47. package/src/hooks/safety/file-organization.ts +473 -0
  48. package/src/hooks/safety/git-commit.ts +623 -0
  49. package/src/hooks/safety/index.ts +46 -0
  50. package/src/hooks/session-hooks.ts +559 -0
  51. package/src/hooks/task-hooks.ts +513 -0
  52. package/src/hooks/types.ts +357 -0
  53. package/src/hooks/verify-exports.test.ts +125 -0
  54. package/src/index.ts +195 -0
  55. package/src/mcp/connection-pool.ts +438 -0
  56. package/src/mcp/index.ts +183 -0
  57. package/src/mcp/server.ts +774 -0
  58. package/src/mcp/session-manager.ts +428 -0
  59. package/src/mcp/tool-registry.ts +566 -0
  60. package/src/mcp/transport/http.ts +557 -0
  61. package/src/mcp/transport/index.ts +294 -0
  62. package/src/mcp/transport/stdio.ts +324 -0
  63. package/src/mcp/transport/websocket.ts +484 -0
  64. package/src/mcp/types.ts +565 -0
  65. package/src/plugin-interface.ts +663 -0
  66. package/src/plugin-loader.ts +638 -0
  67. package/src/plugin-registry.ts +604 -0
  68. package/src/plugins/index.ts +34 -0
  69. package/src/plugins/official/hive-mind-plugin.ts +330 -0
  70. package/src/plugins/official/index.ts +24 -0
  71. package/src/plugins/official/maestro-plugin.ts +508 -0
  72. package/src/plugins/types.ts +108 -0
  73. package/src/resilience/bulkhead.ts +277 -0
  74. package/src/resilience/circuit-breaker.ts +326 -0
  75. package/src/resilience/index.ts +26 -0
  76. package/src/resilience/rate-limiter.ts +420 -0
  77. package/src/resilience/retry.ts +224 -0
  78. package/src/security/index.ts +39 -0
  79. package/src/security/input-validation.ts +265 -0
  80. package/src/security/secure-random.ts +159 -0
  81. package/src/services/index.ts +16 -0
  82. package/src/services/v3-progress.service.ts +505 -0
  83. package/src/types/agent.types.ts +144 -0
  84. package/src/types/index.ts +22 -0
  85. package/src/types/mcp.types.ts +300 -0
  86. package/src/types/memory.types.ts +263 -0
  87. package/src/types/swarm.types.ts +255 -0
  88. package/src/types/task.types.ts +205 -0
  89. package/src/types.ts +367 -0
  90. package/src/utils/secure-logger.d.ts +69 -0
  91. package/src/utils/secure-logger.d.ts.map +1 -0
  92. package/src/utils/secure-logger.js +208 -0
  93. package/src/utils/secure-logger.js.map +1 -0
  94. package/src/utils/secure-logger.ts +257 -0
  95. package/tmp.json +0 -0
  96. package/tsconfig.json +9 -0
@@ -0,0 +1,566 @@
1
+ /**
2
+ * V3 MCP Tool Registry
3
+ *
4
+ * High-performance tool management with:
5
+ * - Fast O(1) lookup using Map
6
+ * - Category-based organization
7
+ * - Tool validation on registration
8
+ * - Dynamic registration/unregistration
9
+ * - Caching for frequently used tools
10
+ *
11
+ * Performance Targets:
12
+ * - Tool registration: <10ms
13
+ * - Tool lookup: <1ms
14
+ * - Tool validation: <5ms
15
+ */
16
+
17
+ import { EventEmitter } from 'events';
18
+ import {
19
+ MCPTool,
20
+ JSONSchema,
21
+ ToolHandler,
22
+ ToolContext,
23
+ ToolCallResult,
24
+ ToolRegistrationOptions,
25
+ ILogger,
26
+ } from './types.js';
27
+
28
+ /**
29
+ * Tool metadata for enhanced lookup
30
+ */
31
+ interface ToolMetadata {
32
+ tool: MCPTool;
33
+ registeredAt: Date;
34
+ callCount: number;
35
+ lastCalled?: Date;
36
+ avgExecutionTime: number;
37
+ errorCount: number;
38
+ }
39
+
40
+ /**
41
+ * Tool search options
42
+ */
43
+ interface ToolSearchOptions {
44
+ category?: string;
45
+ tags?: string[];
46
+ deprecated?: boolean;
47
+ cacheable?: boolean;
48
+ }
49
+
50
+ /**
51
+ * Tool Registry
52
+ *
53
+ * Manages registration, lookup, and execution of MCP tools
54
+ */
55
+ export class ToolRegistry extends EventEmitter {
56
+ private readonly tools: Map<string, ToolMetadata> = new Map();
57
+ private readonly categoryIndex: Map<string, Set<string>> = new Map();
58
+ private readonly tagIndex: Map<string, Set<string>> = new Map();
59
+ private defaultContext?: ToolContext;
60
+
61
+ // Performance tracking
62
+ private totalRegistrations = 0;
63
+ private totalLookups = 0;
64
+ private totalExecutions = 0;
65
+
66
+ constructor(private readonly logger: ILogger) {
67
+ super();
68
+ }
69
+
70
+ /**
71
+ * Register a tool
72
+ */
73
+ register(tool: MCPTool, options: ToolRegistrationOptions = {}): boolean {
74
+ const startTime = performance.now();
75
+
76
+ // Check for existing tool
77
+ if (this.tools.has(tool.name) && !options.override) {
78
+ this.logger.warn('Tool already registered', { name: tool.name });
79
+ return false;
80
+ }
81
+
82
+ // Validate tool if requested
83
+ if (options.validate !== false) {
84
+ const validation = this.validateTool(tool);
85
+ if (!validation.valid) {
86
+ this.logger.error('Tool validation failed', {
87
+ name: tool.name,
88
+ errors: validation.errors,
89
+ });
90
+ return false;
91
+ }
92
+ }
93
+
94
+ // Create metadata
95
+ const metadata: ToolMetadata = {
96
+ tool,
97
+ registeredAt: new Date(),
98
+ callCount: 0,
99
+ avgExecutionTime: 0,
100
+ errorCount: 0,
101
+ };
102
+
103
+ // Register tool
104
+ this.tools.set(tool.name, metadata);
105
+ this.totalRegistrations++;
106
+
107
+ // Update category index
108
+ if (tool.category) {
109
+ if (!this.categoryIndex.has(tool.category)) {
110
+ this.categoryIndex.set(tool.category, new Set());
111
+ }
112
+ this.categoryIndex.get(tool.category)!.add(tool.name);
113
+ }
114
+
115
+ // Update tag index
116
+ if (tool.tags) {
117
+ for (const tag of tool.tags) {
118
+ if (!this.tagIndex.has(tag)) {
119
+ this.tagIndex.set(tag, new Set());
120
+ }
121
+ this.tagIndex.get(tag)!.add(tool.name);
122
+ }
123
+ }
124
+
125
+ const duration = performance.now() - startTime;
126
+ this.logger.debug('Tool registered', {
127
+ name: tool.name,
128
+ category: tool.category,
129
+ duration: `${duration.toFixed(2)}ms`,
130
+ });
131
+
132
+ this.emit('tool:registered', tool.name);
133
+ return true;
134
+ }
135
+
136
+ /**
137
+ * Register multiple tools at once
138
+ */
139
+ registerBatch(tools: MCPTool[], options: ToolRegistrationOptions = {}): {
140
+ registered: number;
141
+ failed: string[];
142
+ } {
143
+ const startTime = performance.now();
144
+ const failed: string[] = [];
145
+ let registered = 0;
146
+
147
+ for (const tool of tools) {
148
+ if (this.register(tool, options)) {
149
+ registered++;
150
+ } else {
151
+ failed.push(tool.name);
152
+ }
153
+ }
154
+
155
+ const duration = performance.now() - startTime;
156
+ this.logger.info('Batch registration complete', {
157
+ total: tools.length,
158
+ registered,
159
+ failed: failed.length,
160
+ duration: `${duration.toFixed(2)}ms`,
161
+ });
162
+
163
+ return { registered, failed };
164
+ }
165
+
166
+ /**
167
+ * Unregister a tool
168
+ */
169
+ unregister(name: string): boolean {
170
+ const metadata = this.tools.get(name);
171
+ if (!metadata) {
172
+ return false;
173
+ }
174
+
175
+ // Remove from category index
176
+ if (metadata.tool.category) {
177
+ const categoryTools = this.categoryIndex.get(metadata.tool.category);
178
+ categoryTools?.delete(name);
179
+ if (categoryTools?.size === 0) {
180
+ this.categoryIndex.delete(metadata.tool.category);
181
+ }
182
+ }
183
+
184
+ // Remove from tag index
185
+ if (metadata.tool.tags) {
186
+ for (const tag of metadata.tool.tags) {
187
+ const tagTools = this.tagIndex.get(tag);
188
+ tagTools?.delete(name);
189
+ if (tagTools?.size === 0) {
190
+ this.tagIndex.delete(tag);
191
+ }
192
+ }
193
+ }
194
+
195
+ this.tools.delete(name);
196
+ this.logger.debug('Tool unregistered', { name });
197
+ this.emit('tool:unregistered', name);
198
+
199
+ return true;
200
+ }
201
+
202
+ /**
203
+ * Get a tool by name
204
+ */
205
+ getTool(name: string): MCPTool | undefined {
206
+ this.totalLookups++;
207
+ return this.tools.get(name)?.tool;
208
+ }
209
+
210
+ /**
211
+ * Check if a tool exists
212
+ */
213
+ hasTool(name: string): boolean {
214
+ return this.tools.has(name);
215
+ }
216
+
217
+ /**
218
+ * Get tool count
219
+ */
220
+ getToolCount(): number {
221
+ return this.tools.size;
222
+ }
223
+
224
+ /**
225
+ * Get all tool names
226
+ */
227
+ getToolNames(): string[] {
228
+ return Array.from(this.tools.keys());
229
+ }
230
+
231
+ /**
232
+ * List all tools with metadata
233
+ */
234
+ listTools(): Array<{
235
+ name: string;
236
+ description: string;
237
+ category?: string;
238
+ tags?: string[];
239
+ deprecated?: boolean;
240
+ }> {
241
+ return Array.from(this.tools.values()).map(({ tool }) => ({
242
+ name: tool.name,
243
+ description: tool.description,
244
+ category: tool.category,
245
+ tags: tool.tags,
246
+ deprecated: tool.deprecated,
247
+ }));
248
+ }
249
+
250
+ /**
251
+ * Search tools by criteria
252
+ */
253
+ search(options: ToolSearchOptions): MCPTool[] {
254
+ let results: Set<string> | undefined;
255
+
256
+ // Filter by category
257
+ if (options.category) {
258
+ const categoryTools = this.categoryIndex.get(options.category);
259
+ if (!categoryTools) return [];
260
+ results = new Set(categoryTools);
261
+ }
262
+
263
+ // Filter by tags (intersection)
264
+ if (options.tags && options.tags.length > 0) {
265
+ for (const tag of options.tags) {
266
+ const tagTools = this.tagIndex.get(tag);
267
+ if (!tagTools) return [];
268
+
269
+ if (results) {
270
+ results = new Set([...results].filter((name) => tagTools.has(name)));
271
+ } else {
272
+ results = new Set(tagTools);
273
+ }
274
+ }
275
+ }
276
+
277
+ // Get all tools if no filters applied
278
+ if (!results) {
279
+ results = new Set(this.tools.keys());
280
+ }
281
+
282
+ // Convert to tools and apply additional filters
283
+ const tools: MCPTool[] = [];
284
+ for (const name of results) {
285
+ const metadata = this.tools.get(name);
286
+ if (!metadata) continue;
287
+
288
+ const tool = metadata.tool;
289
+
290
+ // Filter by deprecated status
291
+ if (options.deprecated !== undefined && tool.deprecated !== options.deprecated) {
292
+ continue;
293
+ }
294
+
295
+ // Filter by cacheable status
296
+ if (options.cacheable !== undefined && tool.cacheable !== options.cacheable) {
297
+ continue;
298
+ }
299
+
300
+ tools.push(tool);
301
+ }
302
+
303
+ return tools;
304
+ }
305
+
306
+ /**
307
+ * Get tools by category
308
+ */
309
+ getByCategory(category: string): MCPTool[] {
310
+ const toolNames = this.categoryIndex.get(category);
311
+ if (!toolNames) return [];
312
+
313
+ return Array.from(toolNames)
314
+ .map((name) => this.tools.get(name)?.tool)
315
+ .filter((tool): tool is MCPTool => tool !== undefined);
316
+ }
317
+
318
+ /**
319
+ * Get tools by tag
320
+ */
321
+ getByTag(tag: string): MCPTool[] {
322
+ const toolNames = this.tagIndex.get(tag);
323
+ if (!toolNames) return [];
324
+
325
+ return Array.from(toolNames)
326
+ .map((name) => this.tools.get(name)?.tool)
327
+ .filter((tool): tool is MCPTool => tool !== undefined);
328
+ }
329
+
330
+ /**
331
+ * Get all categories
332
+ */
333
+ getCategories(): string[] {
334
+ return Array.from(this.categoryIndex.keys());
335
+ }
336
+
337
+ /**
338
+ * Get all tags
339
+ */
340
+ getTags(): string[] {
341
+ return Array.from(this.tagIndex.keys());
342
+ }
343
+
344
+ /**
345
+ * Execute a tool
346
+ */
347
+ async execute(
348
+ name: string,
349
+ input: Record<string, unknown>,
350
+ context?: ToolContext
351
+ ): Promise<ToolCallResult> {
352
+ const startTime = performance.now();
353
+ const metadata = this.tools.get(name);
354
+
355
+ if (!metadata) {
356
+ return {
357
+ content: [{ type: 'text', text: `Tool not found: ${name}` }],
358
+ isError: true,
359
+ };
360
+ }
361
+
362
+ // Build execution context with required sessionId
363
+ const execContext: ToolContext = {
364
+ sessionId: context?.sessionId || this.defaultContext?.sessionId || 'default-session',
365
+ ...this.defaultContext,
366
+ ...context,
367
+ };
368
+ this.totalExecutions++;
369
+ metadata.callCount++;
370
+ metadata.lastCalled = new Date();
371
+
372
+ try {
373
+ this.emit('tool:called', { name, input });
374
+
375
+ const result = await metadata.tool.handler(input, execContext);
376
+
377
+ const duration = performance.now() - startTime;
378
+ this.updateAverageExecutionTime(metadata, duration);
379
+
380
+ this.logger.debug('Tool executed', {
381
+ name,
382
+ duration: `${duration.toFixed(2)}ms`,
383
+ success: true,
384
+ });
385
+
386
+ this.emit('tool:completed', { name, duration, success: true });
387
+
388
+ // Format result
389
+ return {
390
+ content: [{
391
+ type: 'text',
392
+ text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
393
+ }],
394
+ isError: false,
395
+ };
396
+ } catch (error) {
397
+ const duration = performance.now() - startTime;
398
+ metadata.errorCount++;
399
+
400
+ this.logger.error('Tool execution failed', { name, error });
401
+ this.emit('tool:error', { name, error, duration });
402
+
403
+ return {
404
+ content: [{
405
+ type: 'text',
406
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
407
+ }],
408
+ isError: true,
409
+ };
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Set default execution context
415
+ */
416
+ setDefaultContext(context: ToolContext): void {
417
+ this.defaultContext = context;
418
+ }
419
+
420
+ /**
421
+ * Get tool metadata
422
+ */
423
+ getMetadata(name: string): ToolMetadata | undefined {
424
+ return this.tools.get(name);
425
+ }
426
+
427
+ /**
428
+ * Get registry statistics
429
+ */
430
+ getStats(): {
431
+ totalTools: number;
432
+ totalCategories: number;
433
+ totalTags: number;
434
+ totalRegistrations: number;
435
+ totalLookups: number;
436
+ totalExecutions: number;
437
+ topTools: Array<{ name: string; calls: number }>;
438
+ } {
439
+ // Get top 10 most used tools
440
+ const topTools = Array.from(this.tools.entries())
441
+ .map(([name, metadata]) => ({ name, calls: metadata.callCount }))
442
+ .sort((a, b) => b.calls - a.calls)
443
+ .slice(0, 10);
444
+
445
+ return {
446
+ totalTools: this.tools.size,
447
+ totalCategories: this.categoryIndex.size,
448
+ totalTags: this.tagIndex.size,
449
+ totalRegistrations: this.totalRegistrations,
450
+ totalLookups: this.totalLookups,
451
+ totalExecutions: this.totalExecutions,
452
+ topTools,
453
+ };
454
+ }
455
+
456
+ /**
457
+ * Validate a tool definition
458
+ */
459
+ validateTool(tool: MCPTool): { valid: boolean; errors: string[] } {
460
+ const errors: string[] = [];
461
+
462
+ if (!tool.name || typeof tool.name !== 'string') {
463
+ errors.push('Tool name is required and must be a string');
464
+ } else if (!/^[a-zA-Z][a-zA-Z0-9_/:-]*$/.test(tool.name)) {
465
+ errors.push('Tool name must start with a letter and contain only alphanumeric characters, underscores, slashes, colons, and hyphens');
466
+ }
467
+
468
+ if (!tool.description || typeof tool.description !== 'string') {
469
+ errors.push('Tool description is required and must be a string');
470
+ }
471
+
472
+ if (!tool.inputSchema || typeof tool.inputSchema !== 'object') {
473
+ errors.push('Tool inputSchema is required and must be an object');
474
+ } else {
475
+ const schemaErrors = this.validateSchema(tool.inputSchema);
476
+ errors.push(...schemaErrors);
477
+ }
478
+
479
+ if (typeof tool.handler !== 'function') {
480
+ errors.push('Tool handler is required and must be a function');
481
+ }
482
+
483
+ return {
484
+ valid: errors.length === 0,
485
+ errors,
486
+ };
487
+ }
488
+
489
+ /**
490
+ * Validate JSON Schema
491
+ */
492
+ private validateSchema(schema: JSONSchema, path = ''): string[] {
493
+ const errors: string[] = [];
494
+
495
+ if (!schema.type) {
496
+ errors.push(`${path || 'schema'}: type is required`);
497
+ }
498
+
499
+ if (schema.type === 'object' && schema.properties) {
500
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
501
+ const propPath = path ? `${path}.${key}` : key;
502
+ errors.push(...this.validateSchema(propSchema, propPath));
503
+ }
504
+ }
505
+
506
+ if (schema.type === 'array' && schema.items) {
507
+ errors.push(...this.validateSchema(schema.items, `${path}[]`));
508
+ }
509
+
510
+ return errors;
511
+ }
512
+
513
+ /**
514
+ * Update average execution time
515
+ */
516
+ private updateAverageExecutionTime(metadata: ToolMetadata, duration: number): void {
517
+ const n = metadata.callCount;
518
+ metadata.avgExecutionTime =
519
+ ((metadata.avgExecutionTime * (n - 1)) + duration) / n;
520
+ }
521
+
522
+ /**
523
+ * Clear all tools
524
+ */
525
+ clear(): void {
526
+ this.tools.clear();
527
+ this.categoryIndex.clear();
528
+ this.tagIndex.clear();
529
+ this.logger.info('Tool registry cleared');
530
+ this.emit('registry:cleared');
531
+ }
532
+ }
533
+
534
+ /**
535
+ * Create a tool registry
536
+ */
537
+ export function createToolRegistry(logger: ILogger): ToolRegistry {
538
+ return new ToolRegistry(logger);
539
+ }
540
+
541
+ /**
542
+ * Helper to create a tool definition
543
+ */
544
+ export function defineTool<TInput = Record<string, unknown>, TOutput = unknown>(
545
+ name: string,
546
+ description: string,
547
+ inputSchema: JSONSchema,
548
+ handler: ToolHandler<TInput, TOutput>,
549
+ options?: {
550
+ category?: string;
551
+ tags?: string[];
552
+ version?: string;
553
+ deprecated?: boolean;
554
+ cacheable?: boolean;
555
+ cacheTTL?: number;
556
+ timeout?: number;
557
+ }
558
+ ): MCPTool<TInput, TOutput> {
559
+ return {
560
+ name,
561
+ description,
562
+ inputSchema,
563
+ handler,
564
+ ...options,
565
+ };
566
+ }