@sparkleideas/integration 3.5.2-patch.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,864 @@
1
+ /**
2
+ * SpecializedWorker - Domain-Specialized Worker Implementation
3
+ *
4
+ * Extends WorkerBase with domain-specific capabilities and
5
+ * intelligent task matching using embedding-based similarity.
6
+ *
7
+ * Features:
8
+ * - Domain specialization with configurable focus areas
9
+ * - Embedding-based task matching for intelligent routing
10
+ * - Capability verification and scoring
11
+ * - Domain-specific execution strategies
12
+ *
13
+ * Compatible with @sparkleideas/agentic-flow's SpecializedAgent pattern.
14
+ *
15
+ * @module v3/integration/specialized-worker
16
+ * @version 3.0.0-alpha.1
17
+ */
18
+
19
+ import {
20
+ WorkerBase,
21
+ WorkerConfig,
22
+ WorkerType,
23
+ AgentOutput,
24
+ WorkerArtifact,
25
+ } from './worker-base.js';
26
+ import type { Task } from './agentic-flow-agent.js';
27
+
28
+ /**
29
+ * Domain specialization types
30
+ */
31
+ export type DomainSpecialization =
32
+ | 'frontend'
33
+ | 'backend'
34
+ | 'database'
35
+ | 'devops'
36
+ | 'security'
37
+ | 'performance'
38
+ | 'testing'
39
+ | 'documentation'
40
+ | 'architecture'
41
+ | 'machine-learning'
42
+ | 'data-engineering'
43
+ | 'mobile'
44
+ | 'infrastructure'
45
+ | 'api-design'
46
+ | 'code-review'
47
+ | 'custom';
48
+
49
+ /**
50
+ * Specialized worker configuration
51
+ */
52
+ export interface SpecializedWorkerConfig extends WorkerConfig {
53
+ /** Primary domain specialization */
54
+ domain: DomainSpecialization;
55
+ /** Secondary domains (ordered by proficiency) */
56
+ secondaryDomains?: DomainSpecialization[];
57
+ /** Domain-specific skills with proficiency levels (0.0-1.0) */
58
+ skills?: Map<string, number> | Record<string, number>;
59
+ /** Preferred programming languages */
60
+ languages?: string[];
61
+ /** Preferred frameworks */
62
+ frameworks?: string[];
63
+ /** Preferred tools */
64
+ tools?: string[];
65
+ /** Domain expertise level (0.0-1.0) */
66
+ expertiseLevel?: number;
67
+ /** Enable domain-specific preprocessing */
68
+ enablePreprocessing?: boolean;
69
+ /** Enable domain-specific postprocessing */
70
+ enablePostprocessing?: boolean;
71
+ /** Custom domain handlers */
72
+ handlers?: DomainHandlers;
73
+ }
74
+
75
+ /**
76
+ * Domain-specific handlers for specialized processing
77
+ */
78
+ export interface DomainHandlers {
79
+ /** Preprocess task before execution */
80
+ preprocess?: (task: Task, worker: SpecializedWorker) => Promise<Task>;
81
+ /** Postprocess output after execution */
82
+ postprocess?: (output: AgentOutput, task: Task, worker: SpecializedWorker) => Promise<AgentOutput>;
83
+ /** Validate task for domain compatibility */
84
+ validate?: (task: Task, worker: SpecializedWorker) => Promise<boolean>;
85
+ /** Generate domain-specific artifacts */
86
+ generateArtifacts?: (output: AgentOutput, task: Task, worker: SpecializedWorker) => Promise<WorkerArtifact[]>;
87
+ }
88
+
89
+ /**
90
+ * Task matching result with detailed scoring
91
+ */
92
+ export interface TaskMatchResult {
93
+ /** Overall match score (0.0-1.0) */
94
+ score: number;
95
+ /** Breakdown of scoring components */
96
+ breakdown: {
97
+ /** Capability match score */
98
+ capabilityScore: number;
99
+ /** Domain match score */
100
+ domainScore: number;
101
+ /** Embedding similarity score */
102
+ embeddingScore: number;
103
+ /** Skill match score */
104
+ skillScore: number;
105
+ };
106
+ /** Whether worker meets minimum requirements */
107
+ meetsRequirements: boolean;
108
+ /** Missing capabilities */
109
+ missingCapabilities: string[];
110
+ /** Matched capabilities */
111
+ matchedCapabilities: string[];
112
+ /** Recommendations for better matching */
113
+ recommendations?: string[];
114
+ }
115
+
116
+ /**
117
+ * Domain embedding configurations
118
+ */
119
+ const DOMAIN_EMBEDDINGS: Record<DomainSpecialization, number[]> = {
120
+ frontend: [1, 0, 0, 0, 0, 0.2, 0.3, 0.5, 0.2, 0, 0, 0.4, 0, 0.3, 0.2, 0],
121
+ backend: [0, 1, 0.3, 0, 0, 0.3, 0.2, 0.3, 0.5, 0, 0.3, 0, 0.2, 0.5, 0.2, 0],
122
+ database: [0, 0.3, 1, 0, 0, 0.4, 0.2, 0.2, 0.3, 0, 0.5, 0, 0.3, 0.4, 0.1, 0],
123
+ devops: [0, 0.2, 0.2, 1, 0.3, 0.3, 0.2, 0.4, 0.3, 0, 0.2, 0, 0.8, 0.2, 0.1, 0],
124
+ security: [0, 0.3, 0.3, 0.4, 1, 0.4, 0.5, 0.3, 0.3, 0, 0, 0, 0.3, 0.4, 0.6, 0],
125
+ performance: [0.3, 0.4, 0.4, 0.3, 0.2, 1, 0.3, 0.2, 0.3, 0, 0, 0, 0.2, 0.2, 0.2, 0],
126
+ testing: [0.3, 0.3, 0.2, 0.3, 0.4, 0.3, 1, 0.4, 0.2, 0, 0, 0.3, 0.2, 0.2, 0.3, 0],
127
+ documentation: [0.4, 0.3, 0.2, 0.2, 0.2, 0.1, 0.3, 1, 0.3, 0, 0, 0.3, 0.1, 0.5, 0.2, 0],
128
+ architecture: [0.3, 0.4, 0.4, 0.4, 0.4, 0.4, 0.3, 0.5, 1, 0.2, 0.3, 0.2, 0.4, 0.6, 0.4, 0],
129
+ 'machine-learning': [0.2, 0.3, 0.3, 0.2, 0.2, 0.5, 0.3, 0.3, 0.3, 1, 0.6, 0.2, 0.3, 0.3, 0.2, 0],
130
+ 'data-engineering': [0, 0.3, 0.6, 0.3, 0.2, 0.4, 0.2, 0.2, 0.4, 0.5, 1, 0, 0.4, 0.4, 0.2, 0],
131
+ mobile: [0.5, 0.3, 0.2, 0.3, 0.3, 0.4, 0.4, 0.3, 0.3, 0.2, 0, 1, 0.2, 0.3, 0.2, 0],
132
+ infrastructure: [0, 0.2, 0.3, 0.7, 0.4, 0.3, 0.2, 0.3, 0.5, 0, 0.3, 0, 1, 0.2, 0.3, 0],
133
+ 'api-design': [0.2, 0.6, 0.3, 0.2, 0.3, 0.3, 0.2, 0.6, 0.5, 0, 0.2, 0.2, 0.2, 1, 0.3, 0],
134
+ 'code-review': [0.4, 0.4, 0.3, 0.3, 0.5, 0.4, 0.5, 0.4, 0.4, 0.2, 0.2, 0.3, 0.2, 0.4, 1, 0],
135
+ custom: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
136
+ };
137
+
138
+ /**
139
+ * SpecializedWorker - Domain-focused worker with intelligent matching
140
+ *
141
+ * Usage:
142
+ * ```typescript
143
+ * const worker = new SpecializedWorker({
144
+ * id: 'frontend-1',
145
+ * type: 'specialized',
146
+ * domain: 'frontend',
147
+ * capabilities: ['react', 'typescript', 'css'],
148
+ * skills: { react: 0.9, typescript: 0.85, css: 0.8 },
149
+ * languages: ['typescript', 'javascript'],
150
+ * frameworks: ['react', 'next.js'],
151
+ * });
152
+ *
153
+ * await worker.initialize();
154
+ *
155
+ * // Match a task
156
+ * const match = worker.matchTask(task);
157
+ * if (match.score > 0.7) {
158
+ * const result = await worker.execute(task);
159
+ * }
160
+ * ```
161
+ */
162
+ export class SpecializedWorker extends WorkerBase {
163
+ /** Primary domain specialization */
164
+ readonly domain: DomainSpecialization;
165
+
166
+ /** Secondary domains */
167
+ readonly secondaryDomains: DomainSpecialization[];
168
+
169
+ /** Domain-specific skills with proficiency levels */
170
+ protected skills: Map<string, number>;
171
+
172
+ /** Preferred programming languages */
173
+ protected languages: string[];
174
+
175
+ /** Preferred frameworks */
176
+ protected frameworks: string[];
177
+
178
+ /** Preferred tools */
179
+ protected tools: string[];
180
+
181
+ /** Domain expertise level */
182
+ protected expertiseLevel: number;
183
+
184
+ /** Domain-specific handlers */
185
+ protected handlers: DomainHandlers;
186
+
187
+ /** Enable preprocessing */
188
+ protected enablePreprocessing: boolean;
189
+
190
+ /** Enable postprocessing */
191
+ protected enablePostprocessing: boolean;
192
+
193
+ /**
194
+ * Create a new SpecializedWorker instance
195
+ *
196
+ * @param config - Specialized worker configuration
197
+ */
198
+ constructor(config: SpecializedWorkerConfig) {
199
+ // Extend type to specialized if not already set
200
+ const baseConfig: WorkerConfig = {
201
+ ...config,
202
+ type: config.type || 'specialized',
203
+ };
204
+
205
+ super(baseConfig);
206
+
207
+ this.domain = config.domain;
208
+ this.secondaryDomains = config.secondaryDomains || [];
209
+ this.languages = config.languages || [];
210
+ this.frameworks = config.frameworks || [];
211
+ this.tools = config.tools || [];
212
+ this.expertiseLevel = config.expertiseLevel ?? 0.8;
213
+ this.enablePreprocessing = config.enablePreprocessing ?? true;
214
+ this.enablePostprocessing = config.enablePostprocessing ?? true;
215
+ this.handlers = config.handlers || {};
216
+
217
+ // Convert skills to Map
218
+ if (config.skills instanceof Map) {
219
+ this.skills = config.skills;
220
+ } else if (config.skills) {
221
+ this.skills = new Map(Object.entries(config.skills));
222
+ } else {
223
+ this.skills = new Map();
224
+ }
225
+
226
+ // Generate domain-specific embedding
227
+ this.specialization = this.generateDomainEmbedding();
228
+
229
+ this.emit('specialized-worker-created', {
230
+ workerId: this.id,
231
+ domain: this.domain,
232
+ expertiseLevel: this.expertiseLevel,
233
+ });
234
+ }
235
+
236
+ /**
237
+ * Match a task to this worker
238
+ *
239
+ * Calculates a comprehensive match score based on:
240
+ * - Capability overlap
241
+ * - Domain compatibility
242
+ * - Embedding similarity
243
+ * - Skill proficiency
244
+ *
245
+ * @param task - Task to match
246
+ * @returns Detailed match result with scores
247
+ */
248
+ matchTask(task: Task): TaskMatchResult {
249
+ const requiredCapabilities = this.extractRequiredCapabilities(task);
250
+ const taskDomain = this.inferTaskDomain(task);
251
+ const taskEmbedding = this.generateTaskEmbedding(task);
252
+
253
+ // Calculate capability score
254
+ const { matched, missing, score: capabilityScore } =
255
+ this.calculateCapabilityMatch(requiredCapabilities);
256
+
257
+ // Calculate domain score
258
+ const domainScore = this.calculateDomainMatch(taskDomain);
259
+
260
+ // Calculate embedding similarity
261
+ const embeddingScore = this.calculateSimilarity(taskEmbedding);
262
+
263
+ // Calculate skill match score
264
+ const skillScore = this.calculateSkillMatch(task);
265
+
266
+ // Weighted overall score
267
+ const overallScore =
268
+ capabilityScore * 0.3 +
269
+ domainScore * 0.25 +
270
+ embeddingScore * 0.25 +
271
+ skillScore * 0.2;
272
+
273
+ // Generate recommendations
274
+ const recommendations = this.generateRecommendations(
275
+ missing,
276
+ taskDomain,
277
+ overallScore
278
+ );
279
+
280
+ return {
281
+ score: overallScore,
282
+ breakdown: {
283
+ capabilityScore,
284
+ domainScore,
285
+ embeddingScore,
286
+ skillScore,
287
+ },
288
+ meetsRequirements: capabilityScore >= 0.5 && missing.length === 0,
289
+ missingCapabilities: missing,
290
+ matchedCapabilities: matched,
291
+ recommendations,
292
+ };
293
+ }
294
+
295
+ /**
296
+ * Execute a task with domain-specific processing
297
+ *
298
+ * @param task - Task to execute
299
+ * @returns Agent output with results
300
+ */
301
+ async execute(task: Task): Promise<AgentOutput> {
302
+ const startTime = Date.now();
303
+ let processedTask = task;
304
+ let artifacts: WorkerArtifact[] = [];
305
+
306
+ try {
307
+ // Validate task for domain compatibility
308
+ if (this.handlers.validate) {
309
+ const isValid = await this.handlers.validate(task, this);
310
+ if (!isValid) {
311
+ return {
312
+ content: { error: 'Task validation failed for domain' },
313
+ success: false,
314
+ error: new Error(`Task not compatible with ${this.domain} domain`),
315
+ duration: Date.now() - startTime,
316
+ };
317
+ }
318
+ }
319
+
320
+ // Preprocess task
321
+ if (this.enablePreprocessing && this.handlers.preprocess) {
322
+ processedTask = await this.handlers.preprocess(task, this);
323
+ }
324
+
325
+ // Execute the core task
326
+ const output = await this.executeCore(processedTask);
327
+
328
+ // Generate domain-specific artifacts
329
+ if (this.handlers.generateArtifacts) {
330
+ artifacts = await this.handlers.generateArtifacts(output, processedTask, this);
331
+ }
332
+
333
+ // Postprocess output
334
+ let finalOutput = output;
335
+ if (this.enablePostprocessing && this.handlers.postprocess) {
336
+ finalOutput = await this.handlers.postprocess(output, processedTask, this);
337
+ }
338
+
339
+ // Add artifacts to output
340
+ if (artifacts.length > 0) {
341
+ finalOutput.artifacts = [...(finalOutput.artifacts || []), ...artifacts];
342
+ }
343
+
344
+ return {
345
+ ...finalOutput,
346
+ duration: Date.now() - startTime,
347
+ metadata: {
348
+ ...finalOutput.metadata,
349
+ domain: this.domain,
350
+ expertiseLevel: this.expertiseLevel,
351
+ workerId: this.id,
352
+ },
353
+ };
354
+ } catch (error) {
355
+ return {
356
+ content: { error: (error as Error).message },
357
+ success: false,
358
+ error: error as Error,
359
+ duration: Date.now() - startTime,
360
+ metadata: {
361
+ domain: this.domain,
362
+ workerId: this.id,
363
+ },
364
+ };
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Core task execution logic
370
+ *
371
+ * Override this in subclasses for domain-specific implementations.
372
+ *
373
+ * @param task - Preprocessed task
374
+ * @returns Execution output
375
+ */
376
+ protected async executeCore(task: Task): Promise<AgentOutput> {
377
+ // Default implementation with domain-aware processing
378
+ const content = await this.processTaskForDomain(task);
379
+
380
+ return {
381
+ content,
382
+ success: true,
383
+ duration: 0, // Will be set by execute()
384
+ metadata: {
385
+ processedBy: this.id,
386
+ domain: this.domain,
387
+ },
388
+ };
389
+ }
390
+
391
+ /**
392
+ * Process task with domain-specific logic
393
+ *
394
+ * @param task - Task to process
395
+ * @returns Processed content
396
+ */
397
+ protected async processTaskForDomain(task: Task): Promise<Record<string, unknown>> {
398
+ // Domain-specific processing logic
399
+ const result: Record<string, unknown> = {
400
+ taskId: task.id,
401
+ domain: this.domain,
402
+ processed: true,
403
+ input: task.input,
404
+ };
405
+
406
+ // Add domain-specific processing
407
+ switch (this.domain) {
408
+ case 'frontend':
409
+ result.components = [];
410
+ result.styles = {};
411
+ break;
412
+ case 'backend':
413
+ result.endpoints = [];
414
+ result.services = [];
415
+ break;
416
+ case 'database':
417
+ result.queries = [];
418
+ result.migrations = [];
419
+ break;
420
+ case 'testing':
421
+ result.testCases = [];
422
+ result.coverage = 0;
423
+ break;
424
+ case 'security':
425
+ result.vulnerabilities = [];
426
+ result.recommendations = [];
427
+ break;
428
+ case 'architecture':
429
+ result.diagrams = [];
430
+ result.decisions = [];
431
+ break;
432
+ default:
433
+ result.output = task.description;
434
+ }
435
+
436
+ return result;
437
+ }
438
+
439
+ /**
440
+ * Get worker's domain expertise
441
+ */
442
+ getDomainExpertise(): {
443
+ primary: DomainSpecialization;
444
+ secondary: DomainSpecialization[];
445
+ expertiseLevel: number;
446
+ skills: Record<string, number>;
447
+ } {
448
+ return {
449
+ primary: this.domain,
450
+ secondary: this.secondaryDomains,
451
+ expertiseLevel: this.expertiseLevel,
452
+ skills: Object.fromEntries(this.skills),
453
+ };
454
+ }
455
+
456
+ /**
457
+ * Update skill proficiency
458
+ *
459
+ * @param skill - Skill name
460
+ * @param level - Proficiency level (0.0-1.0)
461
+ */
462
+ updateSkill(skill: string, level: number): void {
463
+ const clampedLevel = Math.max(0, Math.min(1, level));
464
+ this.skills.set(skill, clampedLevel);
465
+
466
+ // Regenerate embedding with updated skills
467
+ this.specialization = this.generateDomainEmbedding();
468
+
469
+ this.emit('skill-updated', {
470
+ workerId: this.id,
471
+ skill,
472
+ level: clampedLevel,
473
+ });
474
+ }
475
+
476
+ // ===== Private Methods =====
477
+
478
+ /**
479
+ * Generate domain-specific embedding
480
+ */
481
+ private generateDomainEmbedding(): Float32Array {
482
+ const baseDimension = 64;
483
+ const embedding = new Float32Array(baseDimension);
484
+
485
+ // Start with domain embedding
486
+ const domainVec = DOMAIN_EMBEDDINGS[this.domain] || DOMAIN_EMBEDDINGS.custom;
487
+ for (let i = 0; i < domainVec.length && i < baseDimension; i++) {
488
+ embedding[i] = domainVec[i] * this.expertiseLevel;
489
+ }
490
+
491
+ // Add secondary domain influence
492
+ for (let j = 0; j < this.secondaryDomains.length; j++) {
493
+ const secondaryVec = DOMAIN_EMBEDDINGS[this.secondaryDomains[j]];
494
+ const weight = 0.5 / (j + 1); // Diminishing weight
495
+ for (let i = 0; i < secondaryVec.length && i < baseDimension; i++) {
496
+ embedding[i] += secondaryVec[i] * weight * this.expertiseLevel;
497
+ }
498
+ }
499
+
500
+ // Add skill influence
501
+ let skillIdx = 16;
502
+ for (const [skill, level] of Array.from(this.skills.entries())) {
503
+ if (skillIdx < baseDimension) {
504
+ embedding[skillIdx] = level;
505
+ skillIdx++;
506
+ }
507
+ }
508
+
509
+ // Normalize
510
+ const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0));
511
+ if (norm > 0) {
512
+ for (let i = 0; i < baseDimension; i++) {
513
+ embedding[i] /= norm;
514
+ }
515
+ }
516
+
517
+ return embedding;
518
+ }
519
+
520
+ /**
521
+ * Extract required capabilities from a task
522
+ */
523
+ private extractRequiredCapabilities(task: Task): string[] {
524
+ const capabilities: string[] = [];
525
+
526
+ // From metadata
527
+ if (task.metadata?.requiredCapabilities) {
528
+ capabilities.push(...(task.metadata.requiredCapabilities as string[]));
529
+ }
530
+
531
+ // Infer from task type
532
+ const typeCapabilities = this.inferCapabilitiesFromType(task.type);
533
+ capabilities.push(...typeCapabilities);
534
+
535
+ // Infer from description keywords
536
+ const descriptionCapabilities = this.inferCapabilitiesFromDescription(task.description);
537
+ capabilities.push(...descriptionCapabilities);
538
+
539
+ // Deduplicate
540
+ return Array.from(new Set(capabilities));
541
+ }
542
+
543
+ /**
544
+ * Infer capabilities from task type
545
+ */
546
+ private inferCapabilitiesFromType(type: string): string[] {
547
+ const typeMap: Record<string, string[]> = {
548
+ code: ['code-generation'],
549
+ review: ['code-review'],
550
+ test: ['testing'],
551
+ fix: ['debugging', 'code-generation'],
552
+ refactor: ['refactoring', 'code-review'],
553
+ document: ['documentation'],
554
+ design: ['architecture'],
555
+ security: ['security-audit'],
556
+ performance: ['performance-analysis'],
557
+ };
558
+
559
+ return typeMap[type.toLowerCase()] || [];
560
+ }
561
+
562
+ /**
563
+ * Infer capabilities from task description
564
+ */
565
+ private inferCapabilitiesFromDescription(description: string): string[] {
566
+ const capabilities: string[] = [];
567
+ const lowerDesc = description.toLowerCase();
568
+
569
+ const keywordMap: Record<string, string> = {
570
+ react: 'react',
571
+ vue: 'vue',
572
+ angular: 'angular',
573
+ typescript: 'typescript',
574
+ javascript: 'javascript',
575
+ python: 'python',
576
+ api: 'api-design',
577
+ database: 'database',
578
+ sql: 'sql',
579
+ test: 'testing',
580
+ security: 'security',
581
+ performance: 'performance',
582
+ docker: 'docker',
583
+ kubernetes: 'kubernetes',
584
+ aws: 'aws',
585
+ graphql: 'graphql',
586
+ rest: 'rest-api',
587
+ };
588
+
589
+ for (const [keyword, capability] of Object.entries(keywordMap)) {
590
+ if (lowerDesc.includes(keyword)) {
591
+ capabilities.push(capability);
592
+ }
593
+ }
594
+
595
+ return capabilities;
596
+ }
597
+
598
+ /**
599
+ * Infer domain from task
600
+ */
601
+ private inferTaskDomain(task: Task): DomainSpecialization {
602
+ const lowerDesc = task.description.toLowerCase();
603
+ const lowerType = task.type.toLowerCase();
604
+
605
+ const domainKeywords: Record<DomainSpecialization, string[]> = {
606
+ frontend: ['ui', 'component', 'react', 'vue', 'angular', 'css', 'html', 'frontend'],
607
+ backend: ['api', 'server', 'endpoint', 'service', 'backend', 'rest', 'graphql'],
608
+ database: ['database', 'sql', 'query', 'migration', 'schema', 'postgres', 'mysql'],
609
+ devops: ['deploy', 'ci', 'cd', 'pipeline', 'docker', 'kubernetes', 'terraform'],
610
+ security: ['security', 'auth', 'vulnerability', 'penetration', 'encryption'],
611
+ performance: ['performance', 'optimize', 'benchmark', 'profiling', 'speed'],
612
+ testing: ['test', 'spec', 'coverage', 'unit', 'integration', 'e2e'],
613
+ documentation: ['document', 'readme', 'api-doc', 'comment', 'guide'],
614
+ architecture: ['architecture', 'design', 'pattern', 'structure', 'diagram'],
615
+ 'machine-learning': ['ml', 'model', 'training', 'neural', 'ai', 'tensorflow', 'pytorch'],
616
+ 'data-engineering': ['etl', 'pipeline', 'data', 'warehouse', 'spark', 'kafka'],
617
+ mobile: ['mobile', 'ios', 'android', 'react-native', 'flutter', 'app'],
618
+ infrastructure: ['infrastructure', 'cloud', 'aws', 'gcp', 'azure', 'network'],
619
+ 'api-design': ['api', 'rest', 'graphql', 'endpoint', 'schema', 'openapi'],
620
+ 'code-review': ['review', 'pr', 'pull-request', 'feedback', 'audit'],
621
+ custom: [],
622
+ };
623
+
624
+ for (const [domain, keywords] of Object.entries(domainKeywords)) {
625
+ for (const keyword of keywords) {
626
+ if (lowerDesc.includes(keyword) || lowerType.includes(keyword)) {
627
+ return domain as DomainSpecialization;
628
+ }
629
+ }
630
+ }
631
+
632
+ return 'custom';
633
+ }
634
+
635
+ /**
636
+ * Generate embedding for a task
637
+ */
638
+ private generateTaskEmbedding(task: Task): Float32Array {
639
+ const dimension = 64;
640
+ const embedding = new Float32Array(dimension);
641
+
642
+ // Get domain embedding
643
+ const taskDomain = this.inferTaskDomain(task);
644
+ const domainVec = DOMAIN_EMBEDDINGS[taskDomain];
645
+
646
+ for (let i = 0; i < domainVec.length && i < dimension; i++) {
647
+ embedding[i] = domainVec[i];
648
+ }
649
+
650
+ // Add task type influence
651
+ const typeHash = this.hashStringSpecialized(task.type);
652
+ for (let i = 16; i < 32 && i < dimension; i++) {
653
+ embedding[i] = ((typeHash >> (i % 32)) & 1) ? 0.5 : -0.5;
654
+ }
655
+
656
+ // Normalize
657
+ const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0));
658
+ if (norm > 0) {
659
+ for (let i = 0; i < dimension; i++) {
660
+ embedding[i] /= norm;
661
+ }
662
+ }
663
+
664
+ return embedding;
665
+ }
666
+
667
+ /**
668
+ * Calculate capability match
669
+ */
670
+ private calculateCapabilityMatch(required: string[]): {
671
+ matched: string[];
672
+ missing: string[];
673
+ score: number;
674
+ } {
675
+ if (required.length === 0) {
676
+ return { matched: [], missing: [], score: 1.0 };
677
+ }
678
+
679
+ const matched = required.filter((cap) => this.capabilities.includes(cap));
680
+ const missing = required.filter((cap) => !this.capabilities.includes(cap));
681
+ const score = matched.length / required.length;
682
+
683
+ return { matched, missing, score };
684
+ }
685
+
686
+ /**
687
+ * Calculate domain match score
688
+ */
689
+ private calculateDomainMatch(taskDomain: DomainSpecialization): number {
690
+ if (taskDomain === this.domain) {
691
+ return 1.0;
692
+ }
693
+
694
+ if (this.secondaryDomains.includes(taskDomain)) {
695
+ const index = this.secondaryDomains.indexOf(taskDomain);
696
+ return 0.8 - index * 0.1; // Diminishing score for later secondary domains
697
+ }
698
+
699
+ // Check domain embedding similarity
700
+ const taskEmbedding = DOMAIN_EMBEDDINGS[taskDomain];
701
+ const workerEmbedding = DOMAIN_EMBEDDINGS[this.domain];
702
+
703
+ let similarity = 0;
704
+ for (let i = 0; i < taskEmbedding.length; i++) {
705
+ similarity += taskEmbedding[i] * workerEmbedding[i];
706
+ }
707
+
708
+ return Math.max(0, similarity * 0.5);
709
+ }
710
+
711
+ /**
712
+ * Calculate skill match score
713
+ */
714
+ private calculateSkillMatch(task: Task): number {
715
+ const requiredSkills = this.extractSkillsFromTask(task);
716
+
717
+ if (requiredSkills.length === 0) {
718
+ return 1.0;
719
+ }
720
+
721
+ let totalScore = 0;
722
+ let matchedCount = 0;
723
+
724
+ for (const skill of requiredSkills) {
725
+ if (this.skills.has(skill)) {
726
+ totalScore += this.skills.get(skill)!;
727
+ matchedCount++;
728
+ }
729
+ }
730
+
731
+ if (matchedCount === 0) {
732
+ return 0;
733
+ }
734
+
735
+ return totalScore / requiredSkills.length;
736
+ }
737
+
738
+ /**
739
+ * Extract skills from task
740
+ */
741
+ private extractSkillsFromTask(task: Task): string[] {
742
+ const skills: string[] = [];
743
+ const lowerDesc = task.description.toLowerCase();
744
+
745
+ // Check for language/framework mentions
746
+ const allSkillsArray = [
747
+ ...this.languages,
748
+ ...this.frameworks,
749
+ ...this.tools,
750
+ ...Array.from(this.skills.keys()),
751
+ ];
752
+
753
+ for (const skill of allSkillsArray) {
754
+ if (lowerDesc.includes(skill.toLowerCase())) {
755
+ skills.push(skill);
756
+ }
757
+ }
758
+
759
+ return Array.from(new Set(skills));
760
+ }
761
+
762
+ /**
763
+ * Generate recommendations for better matching
764
+ */
765
+ private generateRecommendations(
766
+ missing: string[],
767
+ taskDomain: DomainSpecialization,
768
+ score: number
769
+ ): string[] {
770
+ const recommendations: string[] = [];
771
+
772
+ if (missing.length > 0) {
773
+ recommendations.push(`Consider acquiring capabilities: ${missing.join(', ')}`);
774
+ }
775
+
776
+ if (taskDomain !== this.domain && !this.secondaryDomains.includes(taskDomain)) {
777
+ recommendations.push(`Task domain '${taskDomain}' differs from specialization '${this.domain}'`);
778
+ }
779
+
780
+ if (score < 0.5) {
781
+ recommendations.push('Low match score - consider routing to a more specialized worker');
782
+ }
783
+
784
+ return recommendations;
785
+ }
786
+
787
+ /**
788
+ * Simple string hash function for specialized worker
789
+ */
790
+ protected hashStringSpecialized(str: string): number {
791
+ let hash = 0;
792
+ for (let i = 0; i < str.length; i++) {
793
+ const char = str.charCodeAt(i);
794
+ hash = ((hash << 5) - hash) + char;
795
+ hash = hash & hash;
796
+ }
797
+ return hash;
798
+ }
799
+ }
800
+
801
+ /**
802
+ * Create a specialized worker factory
803
+ *
804
+ * @param domain - Primary domain specialization
805
+ * @param config - Additional configuration
806
+ * @returns Configured SpecializedWorker
807
+ */
808
+ export function createSpecializedWorker(
809
+ domain: DomainSpecialization,
810
+ config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {}
811
+ ): SpecializedWorker {
812
+ return new SpecializedWorker({
813
+ id: config.id || `${domain}-worker-${Date.now()}`,
814
+ type: 'specialized',
815
+ domain,
816
+ capabilities: config.capabilities || [],
817
+ ...config,
818
+ });
819
+ }
820
+
821
+ /**
822
+ * Create a frontend specialized worker
823
+ */
824
+ export function createFrontendWorker(
825
+ config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {}
826
+ ): SpecializedWorker {
827
+ return createSpecializedWorker('frontend', {
828
+ capabilities: ['react', 'typescript', 'css', 'html', 'code-generation'],
829
+ languages: ['typescript', 'javascript'],
830
+ frameworks: ['react', 'next.js', 'vue'],
831
+ skills: { react: 0.9, typescript: 0.85, css: 0.8, html: 0.9 },
832
+ ...config,
833
+ });
834
+ }
835
+
836
+ /**
837
+ * Create a backend specialized worker
838
+ */
839
+ export function createBackendWorker(
840
+ config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {}
841
+ ): SpecializedWorker {
842
+ return createSpecializedWorker('backend', {
843
+ capabilities: ['api-design', 'database', 'typescript', 'code-generation'],
844
+ languages: ['typescript', 'python', 'go'],
845
+ frameworks: ['express', 'fastify', 'nest.js'],
846
+ skills: { typescript: 0.9, 'api-design': 0.85, database: 0.8 },
847
+ ...config,
848
+ });
849
+ }
850
+
851
+ /**
852
+ * Create a testing specialized worker
853
+ */
854
+ export function createTestingWorker(
855
+ config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {}
856
+ ): SpecializedWorker {
857
+ return createSpecializedWorker('testing', {
858
+ capabilities: ['testing', 'code-review', 'typescript'],
859
+ languages: ['typescript', 'javascript'],
860
+ frameworks: ['jest', 'vitest', 'playwright'],
861
+ skills: { testing: 0.95, 'test-automation': 0.9, 'code-review': 0.8 },
862
+ ...config,
863
+ });
864
+ }