@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.
- package/README.md +270 -0
- package/package.json +55 -0
- package/src/__tests__/agent-adapter.test.ts +271 -0
- package/src/__tests__/agentic-flow-agent.test.ts +176 -0
- package/src/__tests__/token-optimizer.test.ts +176 -0
- package/src/agent-adapter.ts +651 -0
- package/src/agentic-flow-agent.ts +802 -0
- package/src/agentic-flow-bridge.ts +803 -0
- package/src/attention-coordinator.ts +679 -0
- package/src/feature-flags.ts +485 -0
- package/src/index.ts +466 -0
- package/src/long-running-worker.ts +871 -0
- package/src/multi-model-router.ts +1079 -0
- package/src/provider-adapter.ts +1168 -0
- package/src/sdk-bridge.ts +435 -0
- package/src/sona-adapter.ts +824 -0
- package/src/specialized-worker.ts +864 -0
- package/src/swarm-adapter.ts +1112 -0
- package/src/token-optimizer.ts +306 -0
- package/src/types.ts +494 -0
- package/src/worker-base.ts +822 -0
- package/src/worker-pool.ts +933 -0
- package/tmp.json +0 -0
- package/tsconfig.json +9 -0
|
@@ -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
|
+
}
|