@sparkleideas/integration 3.0.1

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.
@@ -0,0 +1,822 @@
1
+ /**
2
+ * WorkerBase - Abstract Base Worker Class
3
+ *
4
+ * Provides the foundation for all worker patterns in Claude Flow v3,
5
+ * aligned with @sparkleideas/agentic-flow@alpha's worker architecture.
6
+ *
7
+ * Key Features:
8
+ * - Specialization embeddings for intelligent task routing
9
+ * - Load balancing and capacity tracking
10
+ * - Capability-based task matching
11
+ * - Memory and coordination integration
12
+ *
13
+ * This implements ADR-001 by building on @sparkleideas/agentic-flow patterns
14
+ * while providing Claude Flow-specific extensions.
15
+ *
16
+ * @module v3/integration/worker-base
17
+ * @version 3.0.0-alpha.1
18
+ */
19
+
20
+ import { EventEmitter } from 'events';
21
+ import type { Task, TaskResult, AgentStatus, Message } from './agentic-flow-agent.js';
22
+
23
+ /**
24
+ * Worker configuration interface
25
+ */
26
+ export interface WorkerConfig {
27
+ /** Unique worker identifier */
28
+ id: string;
29
+ /** Worker type classification */
30
+ type: WorkerType;
31
+ /** Human-readable name */
32
+ name?: string;
33
+ /** Worker capabilities */
34
+ capabilities: string[];
35
+ /** Specialization embedding vector (for similarity-based routing) */
36
+ specialization?: Float32Array | number[];
37
+ /** Maximum concurrent tasks */
38
+ maxConcurrentTasks?: number;
39
+ /** Task execution timeout in milliseconds */
40
+ timeout?: number;
41
+ /** Worker priority (0-100, higher = more preferred) */
42
+ priority?: number;
43
+ /** Memory configuration */
44
+ memory?: WorkerMemoryConfig;
45
+ /** Coordination configuration */
46
+ coordination?: WorkerCoordinationConfig;
47
+ /** Provider configuration for multi-model support */
48
+ provider?: WorkerProviderConfig;
49
+ /** Additional metadata */
50
+ metadata?: Record<string, unknown>;
51
+ }
52
+
53
+ /**
54
+ * Worker type classification
55
+ */
56
+ export type WorkerType =
57
+ | 'coder'
58
+ | 'reviewer'
59
+ | 'tester'
60
+ | 'researcher'
61
+ | 'planner'
62
+ | 'architect'
63
+ | 'coordinator'
64
+ | 'security'
65
+ | 'performance'
66
+ | 'specialized'
67
+ | 'long-running'
68
+ | 'generic';
69
+
70
+ /**
71
+ * Worker memory configuration
72
+ */
73
+ export interface WorkerMemoryConfig {
74
+ /** Enable persistent memory */
75
+ enabled: boolean;
76
+ /** Memory namespace for isolation */
77
+ namespace?: string;
78
+ /** Maximum memory entries */
79
+ maxEntries?: number;
80
+ /** Enable embedding-based retrieval */
81
+ enableEmbeddings?: boolean;
82
+ /** Memory bank ID (for cross-session persistence) */
83
+ memoryBankId?: string;
84
+ }
85
+
86
+ /**
87
+ * Worker coordination configuration
88
+ */
89
+ export interface WorkerCoordinationConfig {
90
+ /** Enable coordination with other workers */
91
+ enabled: boolean;
92
+ /** Coordination protocol */
93
+ protocol?: 'direct' | 'broadcast' | 'pub-sub' | 'request-response';
94
+ /** Message queue capacity */
95
+ queueCapacity?: number;
96
+ /** Heartbeat interval in milliseconds */
97
+ heartbeatInterval?: number;
98
+ }
99
+
100
+ /**
101
+ * Worker provider configuration for multi-model support
102
+ */
103
+ export interface WorkerProviderConfig {
104
+ /** Provider identifier */
105
+ providerId?: string;
106
+ /** Model identifier */
107
+ modelId?: string;
108
+ /** Provider-specific options */
109
+ options?: Record<string, unknown>;
110
+ }
111
+
112
+ /**
113
+ * Agent output interface (compatible with @sparkleideas/agentic-flow)
114
+ */
115
+ export interface AgentOutput {
116
+ /** Output content */
117
+ content: string | Record<string, unknown>;
118
+ /** Success indicator */
119
+ success: boolean;
120
+ /** Error if failed */
121
+ error?: Error;
122
+ /** Execution duration in milliseconds */
123
+ duration: number;
124
+ /** Tokens used (if applicable) */
125
+ tokensUsed?: number;
126
+ /** Artifacts produced */
127
+ artifacts?: WorkerArtifact[];
128
+ /** Metadata */
129
+ metadata?: Record<string, unknown>;
130
+ }
131
+
132
+ /**
133
+ * Worker artifact - files or data produced by task execution
134
+ */
135
+ export interface WorkerArtifact {
136
+ /** Artifact identifier */
137
+ id: string;
138
+ /** Artifact type */
139
+ type: 'file' | 'data' | 'code' | 'log' | 'metric';
140
+ /** Artifact name */
141
+ name: string;
142
+ /** Artifact content or path */
143
+ content: string | Buffer | Record<string, unknown>;
144
+ /** Content size in bytes */
145
+ size?: number;
146
+ /** Creation timestamp */
147
+ createdAt: number;
148
+ }
149
+
150
+ /**
151
+ * Worker metrics for monitoring
152
+ */
153
+ export interface WorkerMetrics {
154
+ /** Total tasks executed */
155
+ tasksExecuted: number;
156
+ /** Successful task count */
157
+ tasksSucceeded: number;
158
+ /** Failed task count */
159
+ tasksFailed: number;
160
+ /** Average execution duration */
161
+ avgDuration: number;
162
+ /** Total tokens used */
163
+ totalTokensUsed: number;
164
+ /** Current load (0.0-1.0) */
165
+ currentLoad: number;
166
+ /** Uptime in milliseconds */
167
+ uptime: number;
168
+ /** Last activity timestamp */
169
+ lastActivity: number;
170
+ /** Health score (0.0-1.0) */
171
+ healthScore: number;
172
+ }
173
+
174
+ /**
175
+ * Worker health status
176
+ */
177
+ export interface WorkerHealth {
178
+ /** Health status */
179
+ status: 'healthy' | 'degraded' | 'unhealthy';
180
+ /** Health score (0.0-1.0) */
181
+ score: number;
182
+ /** Active issues */
183
+ issues: string[];
184
+ /** Last health check timestamp */
185
+ lastCheck: number;
186
+ /** Resource usage */
187
+ resources: {
188
+ memoryMb: number;
189
+ cpuPercent: number;
190
+ };
191
+ }
192
+
193
+ /**
194
+ * WorkerBase - Abstract base class for all workers
195
+ *
196
+ * This class provides the foundation for:
197
+ * - SpecializedWorker: Domain-specific task processing
198
+ * - LongRunningWorker: Checkpoint-based long-running tasks
199
+ * - Generic workers for various use cases
200
+ *
201
+ * Usage:
202
+ * ```typescript
203
+ * class CoderWorker extends WorkerBase {
204
+ * async execute(task: Task): Promise<AgentOutput> {
205
+ * // Implementation
206
+ * }
207
+ * }
208
+ *
209
+ * const worker = new CoderWorker({
210
+ * id: 'coder-1',
211
+ * type: 'coder',
212
+ * capabilities: ['code-generation', 'refactoring'],
213
+ * });
214
+ *
215
+ * await worker.initialize();
216
+ * const result = await worker.execute(task);
217
+ * ```
218
+ */
219
+ export abstract class WorkerBase extends EventEmitter {
220
+ // ===== Public Properties =====
221
+
222
+ /** Unique worker identifier */
223
+ readonly id: string;
224
+
225
+ /** Worker type classification */
226
+ readonly type: WorkerType;
227
+
228
+ /** Human-readable name */
229
+ readonly name: string;
230
+
231
+ /** Worker capabilities */
232
+ capabilities: string[];
233
+
234
+ /** Specialization embedding vector */
235
+ specialization?: Float32Array;
236
+
237
+ /** Current load factor (0.0-1.0) */
238
+ load: number = 0;
239
+
240
+ /** Current status */
241
+ status: AgentStatus = 'spawning';
242
+
243
+ /** Worker configuration (publicly readable for pool operations) */
244
+ readonly config: WorkerConfig;
245
+
246
+ /** Initialization state */
247
+ protected initialized: boolean = false;
248
+
249
+ /** Current concurrent task count */
250
+ protected currentTaskCount: number = 0;
251
+
252
+ /** Creation timestamp */
253
+ protected createdAt: number;
254
+
255
+ /** Worker metrics */
256
+ protected metrics: WorkerMetrics;
257
+
258
+ /** Message queue for coordination */
259
+ protected messageQueue: Message[] = [];
260
+
261
+ /** Memory reference (for persistent memory integration) */
262
+ protected memoryBankId?: string;
263
+
264
+ /**
265
+ * Create a new WorkerBase instance
266
+ *
267
+ * @param config - Worker configuration
268
+ */
269
+ constructor(config: WorkerConfig) {
270
+ super();
271
+
272
+ // Validate required fields
273
+ if (!config.id) {
274
+ throw new Error('Worker config must include id');
275
+ }
276
+
277
+ this.id = config.id;
278
+ this.type = config.type || 'generic';
279
+ this.name = config.name || `${this.type}-${this.id}`;
280
+ this.capabilities = config.capabilities || [];
281
+ this.config = config;
282
+ this.createdAt = Date.now();
283
+
284
+ // Set specialization embedding
285
+ if (config.specialization) {
286
+ this.specialization = config.specialization instanceof Float32Array
287
+ ? config.specialization
288
+ : new Float32Array(config.specialization);
289
+ }
290
+
291
+ // Initialize metrics
292
+ this.metrics = {
293
+ tasksExecuted: 0,
294
+ tasksSucceeded: 0,
295
+ tasksFailed: 0,
296
+ avgDuration: 0,
297
+ totalTokensUsed: 0,
298
+ currentLoad: 0,
299
+ uptime: 0,
300
+ lastActivity: Date.now(),
301
+ healthScore: 1.0,
302
+ };
303
+
304
+ // Memory configuration
305
+ if (config.memory?.enabled) {
306
+ this.memoryBankId = config.memory.memoryBankId;
307
+ }
308
+
309
+ this.emit('created', { workerId: this.id, type: this.type });
310
+ }
311
+
312
+ // ===== Abstract Methods =====
313
+
314
+ /**
315
+ * Execute a task
316
+ *
317
+ * This is the core method that subclasses must implement.
318
+ * It receives a task and returns the execution result.
319
+ *
320
+ * @param task - Task to execute
321
+ * @returns Agent output with results
322
+ */
323
+ abstract execute(task: Task): Promise<AgentOutput>;
324
+
325
+ // ===== Lifecycle Methods =====
326
+
327
+ /**
328
+ * Initialize the worker
329
+ *
330
+ * Sets up resources, connections, and prepares for task execution.
331
+ */
332
+ async initialize(): Promise<void> {
333
+ if (this.initialized) {
334
+ return;
335
+ }
336
+
337
+ this.emit('initializing', { workerId: this.id });
338
+
339
+ try {
340
+ // Initialize memory if configured
341
+ if (this.config.memory?.enabled) {
342
+ await this.initializeMemory();
343
+ }
344
+
345
+ // Initialize coordination if configured
346
+ if (this.config.coordination?.enabled) {
347
+ await this.initializeCoordination();
348
+ }
349
+
350
+ // Call subclass initialization hook
351
+ await this.onInitialize();
352
+
353
+ this.status = 'idle';
354
+ this.initialized = true;
355
+
356
+ this.emit('initialized', { workerId: this.id });
357
+ } catch (error) {
358
+ this.status = 'error';
359
+ this.emit('initialization-failed', {
360
+ workerId: this.id,
361
+ error: error as Error,
362
+ });
363
+ throw error;
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Shutdown the worker gracefully
369
+ *
370
+ * Releases resources and completes cleanup.
371
+ */
372
+ async shutdown(): Promise<void> {
373
+ this.emit('shutting-down', { workerId: this.id });
374
+
375
+ try {
376
+ // Call subclass shutdown hook
377
+ await this.onShutdown();
378
+
379
+ this.status = 'terminated';
380
+ this.initialized = false;
381
+
382
+ this.emit('shutdown', { workerId: this.id });
383
+ } catch (error) {
384
+ this.emit('shutdown-error', {
385
+ workerId: this.id,
386
+ error: error as Error,
387
+ });
388
+ throw error;
389
+ }
390
+ }
391
+
392
+ // ===== Task Execution =====
393
+
394
+ /**
395
+ * Execute a task with wrapper logic
396
+ *
397
+ * Handles load tracking, metrics, and error handling.
398
+ *
399
+ * @param task - Task to execute
400
+ * @returns Task result with metrics
401
+ */
402
+ async executeTask(task: Task): Promise<TaskResult> {
403
+ this.ensureInitialized();
404
+
405
+ // Check capacity
406
+ const maxTasks = this.config.maxConcurrentTasks || 1;
407
+ if (this.currentTaskCount >= maxTasks) {
408
+ throw new Error(`Worker ${this.id} at capacity (${maxTasks} tasks)`);
409
+ }
410
+
411
+ this.currentTaskCount++;
412
+ this.updateLoad();
413
+ this.status = 'busy';
414
+ const startTime = Date.now();
415
+
416
+ this.emit('task-started', { workerId: this.id, taskId: task.id });
417
+
418
+ try {
419
+ // Execute via subclass implementation
420
+ const output = await this.execute(task);
421
+
422
+ const duration = Date.now() - startTime;
423
+
424
+ // Update metrics
425
+ this.updateMetricsSuccess(duration, output.tokensUsed);
426
+
427
+ const result: TaskResult = {
428
+ taskId: task.id,
429
+ success: output.success,
430
+ output: output.content,
431
+ duration,
432
+ tokensUsed: output.tokensUsed,
433
+ metadata: output.metadata,
434
+ };
435
+
436
+ this.emit('task-completed', {
437
+ workerId: this.id,
438
+ taskId: task.id,
439
+ duration,
440
+ });
441
+
442
+ return result;
443
+ } catch (error) {
444
+ const duration = Date.now() - startTime;
445
+
446
+ // Update metrics for failure
447
+ this.updateMetricsFailure(duration);
448
+
449
+ const result: TaskResult = {
450
+ taskId: task.id,
451
+ success: false,
452
+ error: error as Error,
453
+ duration,
454
+ };
455
+
456
+ this.emit('task-failed', {
457
+ workerId: this.id,
458
+ taskId: task.id,
459
+ error: error as Error,
460
+ duration,
461
+ });
462
+
463
+ return result;
464
+ } finally {
465
+ this.currentTaskCount--;
466
+ this.updateLoad();
467
+ this.status = this.currentTaskCount > 0 ? 'busy' : 'idle';
468
+ this.metrics.lastActivity = Date.now();
469
+ }
470
+ }
471
+
472
+ // ===== Embedding & Matching =====
473
+
474
+ /**
475
+ * Get the specialization embedding
476
+ *
477
+ * Returns the worker's specialization vector for similarity-based routing.
478
+ * If no specialization is set, generates a default based on capabilities.
479
+ *
480
+ * @returns Specialization embedding vector
481
+ */
482
+ getEmbedding(): Float32Array {
483
+ if (this.specialization) {
484
+ return this.specialization;
485
+ }
486
+
487
+ // Generate default embedding from capabilities
488
+ return this.generateDefaultEmbedding();
489
+ }
490
+
491
+ /**
492
+ * Calculate similarity with a task embedding
493
+ *
494
+ * Uses cosine similarity to match worker specialization with task requirements.
495
+ *
496
+ * @param taskEmbedding - Task embedding vector
497
+ * @returns Similarity score (0.0-1.0)
498
+ */
499
+ calculateSimilarity(taskEmbedding: Float32Array | number[]): number {
500
+ const workerEmbedding = this.getEmbedding();
501
+ const taskArray = taskEmbedding instanceof Float32Array
502
+ ? taskEmbedding
503
+ : new Float32Array(taskEmbedding);
504
+
505
+ // Cosine similarity
506
+ return this.cosineSimilarity(workerEmbedding, taskArray);
507
+ }
508
+
509
+ /**
510
+ * Check if worker has required capabilities for a task
511
+ *
512
+ * @param requiredCapabilities - Required capability list
513
+ * @returns True if worker has all required capabilities
514
+ */
515
+ hasCapabilities(requiredCapabilities: string[]): boolean {
516
+ return requiredCapabilities.every((cap) =>
517
+ this.capabilities.includes(cap)
518
+ );
519
+ }
520
+
521
+ // ===== Load Management =====
522
+
523
+ /**
524
+ * Update the load factor
525
+ *
526
+ * @param delta - Load change (optional, recalculates if not provided)
527
+ */
528
+ updateLoad(delta?: number): void {
529
+ if (typeof delta === 'number') {
530
+ this.load = Math.max(0, Math.min(1, this.load + delta));
531
+ } else {
532
+ // Calculate based on current task count
533
+ const maxTasks = this.config.maxConcurrentTasks || 1;
534
+ this.load = this.currentTaskCount / maxTasks;
535
+ }
536
+
537
+ this.metrics.currentLoad = this.load;
538
+
539
+ this.emit('load-updated', { workerId: this.id, load: this.load });
540
+ }
541
+
542
+ /**
543
+ * Check if worker is available for tasks
544
+ */
545
+ isAvailable(): boolean {
546
+ return (
547
+ this.initialized &&
548
+ this.status !== 'terminated' &&
549
+ this.status !== 'error' &&
550
+ this.currentTaskCount < (this.config.maxConcurrentTasks || 1)
551
+ );
552
+ }
553
+
554
+ // ===== Health & Metrics =====
555
+
556
+ /**
557
+ * Get worker health status
558
+ */
559
+ getHealth(): WorkerHealth {
560
+ const uptime = Date.now() - this.createdAt;
561
+ this.metrics.uptime = uptime;
562
+
563
+ // Calculate health score
564
+ const successRate =
565
+ this.metrics.tasksExecuted > 0
566
+ ? this.metrics.tasksSucceeded / this.metrics.tasksExecuted
567
+ : 1;
568
+
569
+ const healthScore = Math.min(1, successRate * 0.7 + (1 - this.load) * 0.3);
570
+ this.metrics.healthScore = healthScore;
571
+
572
+ let status: WorkerHealth['status'] = 'healthy';
573
+ const issues: string[] = [];
574
+
575
+ if (healthScore < 0.5) {
576
+ status = 'unhealthy';
577
+ issues.push('Low health score');
578
+ } else if (healthScore < 0.8) {
579
+ status = 'degraded';
580
+ }
581
+
582
+ if (this.load > 0.9) {
583
+ issues.push('High load');
584
+ }
585
+
586
+ if (this.status === 'error') {
587
+ status = 'unhealthy';
588
+ issues.push('Worker in error state');
589
+ }
590
+
591
+ return {
592
+ status,
593
+ score: healthScore,
594
+ issues,
595
+ lastCheck: Date.now(),
596
+ resources: {
597
+ memoryMb: this.estimateMemoryUsage(),
598
+ cpuPercent: this.load * 100,
599
+ },
600
+ };
601
+ }
602
+
603
+ /**
604
+ * Get worker metrics
605
+ */
606
+ getMetrics(): WorkerMetrics {
607
+ this.metrics.uptime = Date.now() - this.createdAt;
608
+ return { ...this.metrics };
609
+ }
610
+
611
+ // ===== Coordination =====
612
+
613
+ /**
614
+ * Send a message to another worker
615
+ *
616
+ * @param to - Target worker ID
617
+ * @param message - Message to send
618
+ */
619
+ async sendMessage(to: string, message: Message): Promise<void> {
620
+ this.emit('message-send', {
621
+ from: this.id,
622
+ to,
623
+ message,
624
+ });
625
+ }
626
+
627
+ /**
628
+ * Receive a message from another worker
629
+ *
630
+ * @param message - Received message
631
+ */
632
+ async receiveMessage(message: Message): Promise<void> {
633
+ this.messageQueue.push(message);
634
+
635
+ this.emit('message-received', {
636
+ workerId: this.id,
637
+ message,
638
+ });
639
+
640
+ // Process message
641
+ await this.processMessage(message);
642
+ }
643
+
644
+ // ===== Protected Hook Methods =====
645
+
646
+ /**
647
+ * Hook called during initialization
648
+ * Override in subclasses for custom initialization
649
+ */
650
+ protected async onInitialize(): Promise<void> {
651
+ // Default: no-op
652
+ }
653
+
654
+ /**
655
+ * Hook called during shutdown
656
+ * Override in subclasses for custom cleanup
657
+ */
658
+ protected async onShutdown(): Promise<void> {
659
+ // Default: no-op
660
+ }
661
+
662
+ /**
663
+ * Process a received message
664
+ * Override in subclasses for custom message handling
665
+ *
666
+ * @param message - Message to process
667
+ */
668
+ protected async processMessage(message: Message): Promise<void> {
669
+ // Default: emit event for external handling
670
+ this.emit('message-process', { workerId: this.id, message });
671
+ }
672
+
673
+ // ===== Private Methods =====
674
+
675
+ /**
676
+ * Initialize memory integration
677
+ */
678
+ private async initializeMemory(): Promise<void> {
679
+ if (!this.memoryBankId) {
680
+ this.memoryBankId = `memory_${this.id}_${Date.now()}`;
681
+ }
682
+
683
+ this.emit('memory-initialized', {
684
+ workerId: this.id,
685
+ memoryBankId: this.memoryBankId,
686
+ });
687
+ }
688
+
689
+ /**
690
+ * Initialize coordination
691
+ */
692
+ private async initializeCoordination(): Promise<void> {
693
+ this.emit('coordination-initialized', {
694
+ workerId: this.id,
695
+ protocol: this.config.coordination?.protocol || 'direct',
696
+ });
697
+ }
698
+
699
+ /**
700
+ * Ensure worker is initialized
701
+ */
702
+ protected ensureInitialized(): void {
703
+ if (!this.initialized) {
704
+ throw new Error(`Worker ${this.id} not initialized. Call initialize() first.`);
705
+ }
706
+ }
707
+
708
+ /**
709
+ * Update metrics for successful task
710
+ */
711
+ private updateMetricsSuccess(duration: number, tokensUsed?: number): void {
712
+ this.metrics.tasksExecuted++;
713
+ this.metrics.tasksSucceeded++;
714
+
715
+ // Update average duration
716
+ const total = this.metrics.avgDuration * (this.metrics.tasksSucceeded - 1) + duration;
717
+ this.metrics.avgDuration = total / this.metrics.tasksSucceeded;
718
+
719
+ if (tokensUsed) {
720
+ this.metrics.totalTokensUsed += tokensUsed;
721
+ }
722
+ }
723
+
724
+ /**
725
+ * Update metrics for failed task
726
+ */
727
+ private updateMetricsFailure(duration: number): void {
728
+ this.metrics.tasksExecuted++;
729
+ this.metrics.tasksFailed++;
730
+ }
731
+
732
+ /**
733
+ * Generate default embedding from capabilities
734
+ */
735
+ private generateDefaultEmbedding(): Float32Array {
736
+ // Create a simple hash-based embedding from capabilities
737
+ const dimension = 64;
738
+ const embedding = new Float32Array(dimension);
739
+
740
+ for (const cap of this.capabilities) {
741
+ const hash = this.hashString(cap);
742
+ for (let i = 0; i < dimension; i++) {
743
+ embedding[i] += ((hash >> (i % 32)) & 1) ? 0.1 : -0.1;
744
+ }
745
+ }
746
+
747
+ // Normalize
748
+ const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0));
749
+ if (norm > 0) {
750
+ for (let i = 0; i < dimension; i++) {
751
+ embedding[i] /= norm;
752
+ }
753
+ }
754
+
755
+ return embedding;
756
+ }
757
+
758
+ /**
759
+ * Calculate cosine similarity between two vectors
760
+ */
761
+ private cosineSimilarity(a: Float32Array, b: Float32Array): number {
762
+ if (a.length !== b.length) {
763
+ // Pad shorter array or use minimum length
764
+ const minLen = Math.min(a.length, b.length);
765
+ a = a.slice(0, minLen);
766
+ b = b.slice(0, minLen);
767
+ }
768
+
769
+ let dot = 0;
770
+ let normA = 0;
771
+ let normB = 0;
772
+
773
+ for (let i = 0; i < a.length; i++) {
774
+ dot += a[i] * b[i];
775
+ normA += a[i] * a[i];
776
+ normB += b[i] * b[i];
777
+ }
778
+
779
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
780
+ return denominator > 0 ? dot / denominator : 0;
781
+ }
782
+
783
+ /**
784
+ * Simple string hash function
785
+ */
786
+ private hashString(str: string): number {
787
+ let hash = 0;
788
+ for (let i = 0; i < str.length; i++) {
789
+ const char = str.charCodeAt(i);
790
+ hash = ((hash << 5) - hash) + char;
791
+ hash = hash & hash;
792
+ }
793
+ return hash;
794
+ }
795
+
796
+ /**
797
+ * Estimate memory usage in MB
798
+ */
799
+ private estimateMemoryUsage(): number {
800
+ // Base estimate: 2MB + 100KB per capability + queue size
801
+ const base = 2;
802
+ const capabilityOverhead = this.capabilities.length * 0.1;
803
+ const queueOverhead = this.messageQueue.length * 0.01;
804
+ return base + capabilityOverhead + queueOverhead;
805
+ }
806
+ }
807
+
808
+ /**
809
+ * Create a worker with the given configuration
810
+ *
811
+ * @param config - Worker configuration
812
+ * @param ExecutorClass - Worker class to instantiate
813
+ * @returns Initialized worker instance
814
+ */
815
+ export async function createWorker<T extends WorkerBase>(
816
+ config: WorkerConfig,
817
+ ExecutorClass: new (config: WorkerConfig) => T
818
+ ): Promise<T> {
819
+ const worker = new ExecutorClass(config);
820
+ await worker.initialize();
821
+ return worker;
822
+ }