@juspay/neurolink 9.83.0 → 9.84.0

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 (117) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/agent/agent.d.ts +104 -0
  3. package/dist/agent/agent.js +401 -0
  4. package/dist/agent/agentNetwork.d.ts +87 -0
  5. package/dist/agent/agentNetwork.js +458 -0
  6. package/dist/agent/communication/index.d.ts +9 -0
  7. package/dist/agent/communication/index.js +9 -0
  8. package/dist/agent/communication/message-bus.d.ts +113 -0
  9. package/dist/agent/communication/message-bus.js +406 -0
  10. package/dist/agent/coordination/coordinator.d.ts +83 -0
  11. package/dist/agent/coordination/coordinator.js +598 -0
  12. package/dist/agent/coordination/index.d.ts +10 -0
  13. package/dist/agent/coordination/index.js +10 -0
  14. package/dist/agent/coordination/task-distributor.d.ts +113 -0
  15. package/dist/agent/coordination/task-distributor.js +585 -0
  16. package/dist/agent/index.d.ts +38 -0
  17. package/dist/agent/index.js +44 -0
  18. package/dist/agent/orchestration/index.d.ts +10 -0
  19. package/dist/agent/orchestration/index.js +10 -0
  20. package/dist/agent/orchestration/orchestrator.d.ts +114 -0
  21. package/dist/agent/orchestration/orchestrator.js +476 -0
  22. package/dist/agent/orchestration/topology.d.ts +164 -0
  23. package/dist/agent/orchestration/topology.js +520 -0
  24. package/dist/agent/prompts/routingPrompts.d.ts +63 -0
  25. package/dist/agent/prompts/routingPrompts.js +201 -0
  26. package/dist/browser/neurolink.min.js +377 -358
  27. package/dist/cli/commands/agent.d.ts +37 -0
  28. package/dist/cli/commands/agent.js +718 -0
  29. package/dist/cli/factories/commandFactory.d.ts +8 -0
  30. package/dist/cli/factories/commandFactory.js +116 -0
  31. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  32. package/dist/cli/parser.js +4 -0
  33. package/dist/index.d.ts +6 -0
  34. package/dist/index.js +19 -0
  35. package/dist/lib/agent/agent.d.ts +104 -0
  36. package/dist/lib/agent/agent.js +402 -0
  37. package/dist/lib/agent/agentNetwork.d.ts +87 -0
  38. package/dist/lib/agent/agentNetwork.js +459 -0
  39. package/dist/lib/agent/communication/index.d.ts +9 -0
  40. package/dist/lib/agent/communication/index.js +10 -0
  41. package/dist/lib/agent/communication/message-bus.d.ts +113 -0
  42. package/dist/lib/agent/communication/message-bus.js +407 -0
  43. package/dist/lib/agent/coordination/coordinator.d.ts +83 -0
  44. package/dist/lib/agent/coordination/coordinator.js +599 -0
  45. package/dist/lib/agent/coordination/index.d.ts +10 -0
  46. package/dist/lib/agent/coordination/index.js +11 -0
  47. package/dist/lib/agent/coordination/task-distributor.d.ts +113 -0
  48. package/dist/lib/agent/coordination/task-distributor.js +586 -0
  49. package/dist/lib/agent/index.d.ts +38 -0
  50. package/dist/lib/agent/index.js +45 -0
  51. package/dist/lib/agent/orchestration/index.d.ts +10 -0
  52. package/dist/lib/agent/orchestration/index.js +11 -0
  53. package/dist/lib/agent/orchestration/orchestrator.d.ts +114 -0
  54. package/dist/lib/agent/orchestration/orchestrator.js +477 -0
  55. package/dist/lib/agent/orchestration/topology.d.ts +164 -0
  56. package/dist/lib/agent/orchestration/topology.js +521 -0
  57. package/dist/lib/agent/prompts/routingPrompts.d.ts +63 -0
  58. package/dist/lib/agent/prompts/routingPrompts.js +202 -0
  59. package/dist/lib/index.d.ts +6 -0
  60. package/dist/lib/index.js +19 -0
  61. package/dist/lib/neurolink.d.ts +129 -0
  62. package/dist/lib/neurolink.js +276 -0
  63. package/dist/lib/processors/config/{fileTypes.js → fileExtensions.js} +1 -1
  64. package/dist/lib/processors/config/index.d.ts +2 -2
  65. package/dist/lib/processors/config/index.js +2 -2
  66. package/dist/lib/processors/config/{mimeTypes.js → mimeConstants.js} +1 -1
  67. package/dist/lib/processors/index.js +8 -0
  68. package/dist/lib/providers/googleAiStudio.js +3 -3
  69. package/dist/lib/providers/googleNativeGemini3.d.ts +18 -0
  70. package/dist/lib/providers/googleNativeGemini3.js +47 -1
  71. package/dist/lib/providers/googleVertex.js +5 -5
  72. package/dist/lib/types/agentNetwork.d.ts +1184 -0
  73. package/dist/lib/types/agentNetwork.js +8 -0
  74. package/dist/lib/types/cli.d.ts +66 -0
  75. package/dist/lib/types/generate.d.ts +53 -0
  76. package/dist/lib/types/index.d.ts +2 -0
  77. package/dist/lib/types/index.js +3 -0
  78. package/dist/lib/types/ioProcessor.d.ts +119 -0
  79. package/dist/lib/types/ioProcessor.js +10 -0
  80. package/dist/lib/types/stream.d.ts +36 -0
  81. package/dist/lib/utils/piiDetector.d.ts +24 -0
  82. package/dist/lib/utils/piiDetector.js +221 -0
  83. package/dist/lib/utils/responseValidator.d.ts +21 -0
  84. package/dist/lib/utils/responseValidator.js +354 -0
  85. package/dist/lib/utils/tripwireEvaluator.d.ts +73 -0
  86. package/dist/lib/utils/tripwireEvaluator.js +285 -0
  87. package/dist/neurolink.d.ts +129 -0
  88. package/dist/neurolink.js +276 -0
  89. package/dist/processors/config/index.d.ts +2 -2
  90. package/dist/processors/config/index.js +2 -2
  91. package/dist/processors/index.js +8 -0
  92. package/dist/providers/googleAiStudio.js +3 -3
  93. package/dist/providers/googleNativeGemini3.d.ts +18 -0
  94. package/dist/providers/googleNativeGemini3.js +47 -1
  95. package/dist/providers/googleVertex.js +5 -5
  96. package/dist/types/agentNetwork.d.ts +1184 -0
  97. package/dist/types/agentNetwork.js +7 -0
  98. package/dist/types/cli.d.ts +66 -0
  99. package/dist/types/generate.d.ts +53 -0
  100. package/dist/types/index.d.ts +2 -0
  101. package/dist/types/index.js +3 -0
  102. package/dist/types/ioProcessor.d.ts +119 -0
  103. package/dist/types/ioProcessor.js +9 -0
  104. package/dist/types/stream.d.ts +36 -0
  105. package/dist/utils/piiDetector.d.ts +24 -0
  106. package/dist/utils/piiDetector.js +220 -0
  107. package/dist/utils/responseValidator.d.ts +21 -0
  108. package/dist/utils/responseValidator.js +353 -0
  109. package/dist/utils/tripwireEvaluator.d.ts +73 -0
  110. package/dist/utils/tripwireEvaluator.js +284 -0
  111. package/package.json +1 -1
  112. /package/dist/lib/processors/config/{fileTypes.d.ts → fileExtensions.d.ts} +0 -0
  113. /package/dist/lib/processors/config/{mimeTypes.d.ts → mimeConstants.d.ts} +0 -0
  114. /package/dist/processors/config/{fileTypes.d.ts → fileExtensions.d.ts} +0 -0
  115. /package/dist/processors/config/{fileTypes.js → fileExtensions.js} +0 -0
  116. /package/dist/processors/config/{mimeTypes.d.ts → mimeConstants.d.ts} +0 -0
  117. /package/dist/processors/config/{mimeTypes.js → mimeConstants.js} +0 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Task Distributor - Distributes and balances tasks across agents
3
+ *
4
+ * The Task Distributor handles:
5
+ * - Task decomposition into subtasks
6
+ * - Load balancing across available agents
7
+ * - Priority-based task queuing
8
+ * - Task affinity and skill matching
9
+ */
10
+ import type { Agent } from "../agent.js";
11
+ import type { TaskAnalysis, DistributableTask, DistributionResult, AgentCapability, TaskDistributorConfig } from "../../types/index.js";
12
+ /**
13
+ * Task Distributor - Manages task distribution across agents
14
+ */
15
+ export declare class TaskDistributor {
16
+ private agents;
17
+ private capabilities;
18
+ private taskQueue;
19
+ private activeResults;
20
+ private config;
21
+ private emitter;
22
+ private isProcessing;
23
+ constructor(config: TaskDistributorConfig);
24
+ /**
25
+ * Register an agent with capabilities
26
+ */
27
+ registerAgent(agent: Agent, capability?: Partial<AgentCapability>): void;
28
+ /**
29
+ * Unregister an agent
30
+ */
31
+ unregisterAgent(agentId: string): void;
32
+ /**
33
+ * Update agent capability
34
+ */
35
+ updateCapability(agentId: string, update: Partial<AgentCapability>): void;
36
+ /**
37
+ * Submit a task for distribution
38
+ */
39
+ submitTask(task: DistributableTask): Promise<DistributionResult>;
40
+ /**
41
+ * Submit multiple tasks
42
+ */
43
+ submitTasks(tasks: DistributableTask[]): Promise<DistributionResult[]>;
44
+ /**
45
+ * Decompose a complex task into subtasks
46
+ */
47
+ decomposeTask(task: DistributableTask, analysis: TaskAnalysis): Promise<DistributableTask[]>;
48
+ /**
49
+ * Process the task queue
50
+ */
51
+ private processQueue;
52
+ /**
53
+ * Select an agent based on strategy
54
+ */
55
+ private selectAgent;
56
+ /**
57
+ * Select agent by skill match
58
+ */
59
+ private selectBySkill;
60
+ /**
61
+ * Select agent by load
62
+ */
63
+ private selectByLoad;
64
+ /**
65
+ * Select agent by priority matching
66
+ */
67
+ private selectByPriority;
68
+ /**
69
+ * Select agent by affinity
70
+ */
71
+ private selectByAffinity;
72
+ /**
73
+ * Execute a task on an agent
74
+ */
75
+ private executeTask;
76
+ /**
77
+ * Broadcast a task to all agents
78
+ */
79
+ broadcastTask(task: DistributableTask): Promise<Map<string, DistributionResult>>;
80
+ /**
81
+ * Get task result
82
+ */
83
+ getTaskResult(taskId: string): DistributionResult | undefined;
84
+ /**
85
+ * Get queue status
86
+ */
87
+ getQueueStatus(): {
88
+ pending: number;
89
+ active: number;
90
+ completed: number;
91
+ failed: number;
92
+ };
93
+ /**
94
+ * Clear completed/failed tasks
95
+ */
96
+ clearCompleted(): void;
97
+ /**
98
+ * Fail all queued tasks (used on deadlock detection)
99
+ */
100
+ private failAllQueuedTasks;
101
+ /**
102
+ * Helper delay function
103
+ */
104
+ private delay;
105
+ /**
106
+ * Subscribe to distributor events
107
+ */
108
+ on(event: string, handler: (...args: unknown[]) => void): void;
109
+ /**
110
+ * Unsubscribe from distributor events
111
+ */
112
+ off(event: string, handler: (...args: unknown[]) => void): void;
113
+ }
@@ -0,0 +1,585 @@
1
+ /**
2
+ * Task Distributor - Distributes and balances tasks across agents
3
+ *
4
+ * The Task Distributor handles:
5
+ * - Task decomposition into subtasks
6
+ * - Load balancing across available agents
7
+ * - Priority-based task queuing
8
+ * - Task affinity and skill matching
9
+ */
10
+ import { EventEmitter } from "events";
11
+ import { logger } from "../../utils/logger.js";
12
+ import { withTimeout } from "../../utils/async/withTimeout.js";
13
+ /**
14
+ * Priority values for sorting
15
+ */
16
+ const PRIORITY_VALUES = {
17
+ critical: 5,
18
+ high: 4,
19
+ normal: 3,
20
+ low: 2,
21
+ background: 1,
22
+ };
23
+ /**
24
+ * Task Distributor - Manages task distribution across agents
25
+ */
26
+ export class TaskDistributor {
27
+ agents = new Map();
28
+ capabilities = new Map();
29
+ taskQueue = [];
30
+ activeResults = new Map();
31
+ config;
32
+ emitter;
33
+ isProcessing = false;
34
+ constructor(config) {
35
+ this.config = {
36
+ maxQueueSize: 1000,
37
+ maxRetries: 3,
38
+ retryDelay: 1000,
39
+ taskTimeout: 60000,
40
+ enableDecomposition: false,
41
+ ...config,
42
+ };
43
+ this.emitter = new EventEmitter();
44
+ logger.debug("[TaskDistributor] Created with config", {
45
+ strategy: config.strategy,
46
+ maxQueueSize: this.config.maxQueueSize,
47
+ });
48
+ }
49
+ /**
50
+ * Register an agent with capabilities
51
+ */
52
+ registerAgent(agent, capability) {
53
+ this.agents.set(agent.id, agent);
54
+ // Extract skills from agent tools if available
55
+ const skills = capability?.skills ?? agent.tools ?? [];
56
+ this.capabilities.set(agent.id, {
57
+ agentId: agent.id,
58
+ skills,
59
+ currentLoad: 0,
60
+ avgResponseTime: 0,
61
+ successRate: 1,
62
+ affinityTags: capability?.affinityTags,
63
+ });
64
+ logger.debug(`[TaskDistributor] Registered agent: ${agent.name}`, {
65
+ skills: skills.length,
66
+ });
67
+ }
68
+ /**
69
+ * Unregister an agent
70
+ */
71
+ unregisterAgent(agentId) {
72
+ this.agents.delete(agentId);
73
+ this.capabilities.delete(agentId);
74
+ }
75
+ /**
76
+ * Update agent capability
77
+ */
78
+ updateCapability(agentId, update) {
79
+ const current = this.capabilities.get(agentId);
80
+ if (current) {
81
+ this.capabilities.set(agentId, { ...current, ...update });
82
+ }
83
+ }
84
+ /**
85
+ * Submit a task for distribution
86
+ */
87
+ async submitTask(task) {
88
+ // Check queue capacity
89
+ if (this.config.maxQueueSize &&
90
+ this.taskQueue.length >= this.config.maxQueueSize) {
91
+ throw new Error("Task queue is full");
92
+ }
93
+ // Initialize result
94
+ const result = {
95
+ taskId: task.id,
96
+ agentId: "",
97
+ distributedAt: Date.now(),
98
+ status: "pending",
99
+ };
100
+ this.activeResults.set(task.id, result);
101
+ // Create a settlement promise BEFORE queuing so we never miss the event.
102
+ // Resolves when the task reaches "completed" or "failed" via the emitter.
103
+ const settled = new Promise((resolve) => {
104
+ const onComplete = (evt) => {
105
+ if (evt.taskId === task.id) {
106
+ this.emitter.off("task:completed", onComplete);
107
+ this.emitter.off("task:failed", onFail);
108
+ resolve(this.activeResults.get(task.id) ?? result);
109
+ }
110
+ };
111
+ const onFail = (evt) => {
112
+ if (evt.taskId === task.id) {
113
+ this.emitter.off("task:completed", onComplete);
114
+ this.emitter.off("task:failed", onFail);
115
+ resolve(this.activeResults.get(task.id) ?? result);
116
+ }
117
+ };
118
+ this.emitter.on("task:completed", onComplete);
119
+ this.emitter.on("task:failed", onFail);
120
+ });
121
+ // Add to queue
122
+ this.taskQueue.push({
123
+ task,
124
+ addedAt: Date.now(),
125
+ attempts: 0,
126
+ });
127
+ this.emitter.emit("task:submitted", { taskId: task.id });
128
+ // Trigger processing (no-ops if already running; the finally block will
129
+ // re-trigger itself until the queue is drained).
130
+ await this.processQueue();
131
+ // Wait until the task actually finishes (completed or failed).
132
+ return settled;
133
+ }
134
+ /**
135
+ * Submit multiple tasks
136
+ */
137
+ async submitTasks(tasks) {
138
+ return Promise.all(tasks.map((task) => this.submitTask(task)));
139
+ }
140
+ /**
141
+ * Decompose a complex task into subtasks
142
+ */
143
+ async decomposeTask(task, analysis) {
144
+ if (!this.config.enableDecomposition) {
145
+ return [task];
146
+ }
147
+ const subtasks = [];
148
+ // Create subtasks based on requirements
149
+ for (let i = 0; i < analysis.requirements.length; i++) {
150
+ const req = analysis.requirements[i];
151
+ if (req.mandatory) {
152
+ subtasks.push({
153
+ id: `${task.id}-subtask-${i}`,
154
+ input: `${task.input}\n\nFocus on: ${req.description}`,
155
+ priority: task.priority,
156
+ requiredSkills: req.type === "tool" ? [req.description] : undefined,
157
+ parentTaskId: task.id,
158
+ metadata: {
159
+ ...task.metadata,
160
+ subtaskIndex: i,
161
+ requirementType: req.type,
162
+ },
163
+ });
164
+ }
165
+ }
166
+ // If no subtasks created, return original
167
+ if (subtasks.length === 0) {
168
+ return [task];
169
+ }
170
+ return subtasks;
171
+ }
172
+ /**
173
+ * Process the task queue
174
+ */
175
+ async processQueue() {
176
+ if (this.isProcessing || this.taskQueue.length === 0) {
177
+ return;
178
+ }
179
+ this.isProcessing = true;
180
+ try {
181
+ // Sort queue by priority and deadline
182
+ this.taskQueue.sort((a, b) => {
183
+ // Priority first
184
+ const priorityDiff = PRIORITY_VALUES[b.task.priority] - PRIORITY_VALUES[a.task.priority];
185
+ if (priorityDiff !== 0) {
186
+ return priorityDiff;
187
+ }
188
+ // Then by deadline
189
+ if (a.task.deadline && b.task.deadline) {
190
+ return a.task.deadline - b.task.deadline;
191
+ }
192
+ // Then by queue time
193
+ return a.addedAt - b.addedAt;
194
+ });
195
+ // Process tasks
196
+ // Tracks how many items have been rotated to the back of the queue
197
+ // without any task being executed. When this equals the queue length
198
+ // every waiting item has been seen at least once with no forward
199
+ // progress — a deadlock caused by circular or unresolvable dependencies.
200
+ let rotatedWithoutProgress = 0;
201
+ while (this.taskQueue.length > 0) {
202
+ const item = this.taskQueue[0];
203
+ // Check dependencies
204
+ if (item.task.dependencies && item.task.dependencies.length > 0) {
205
+ // Detect failed or missing dependencies first to avoid infinite spinning
206
+ const failedDep = item.task.dependencies.find((depId) => {
207
+ const depResult = this.activeResults.get(depId);
208
+ return depResult?.status === "failed" || !depResult;
209
+ });
210
+ if (failedDep) {
211
+ this.taskQueue.shift();
212
+ rotatedWithoutProgress = 0; // structural change — reset counter
213
+ const result = this.activeResults.get(item.task.id);
214
+ if (result) {
215
+ result.status = "failed";
216
+ result.error = `Dependency '${failedDep}' failed or not found`;
217
+ }
218
+ this.emitter.emit("task:failed", {
219
+ taskId: item.task.id,
220
+ error: `Dependency '${failedDep}' failed or not found`,
221
+ });
222
+ continue;
223
+ }
224
+ const allDepsComplete = item.task.dependencies.every((depId) => {
225
+ const depResult = this.activeResults.get(depId);
226
+ return depResult?.status === "completed";
227
+ });
228
+ if (!allDepsComplete) {
229
+ // Move to end of queue and continue
230
+ this.taskQueue.shift();
231
+ this.taskQueue.push(item);
232
+ rotatedWithoutProgress++;
233
+ // Full rotation with no progress — circular/unresolvable dependency
234
+ if (rotatedWithoutProgress >= this.taskQueue.length) {
235
+ this.failAllQueuedTasks("Circular or unresolvable dependency detected");
236
+ break;
237
+ }
238
+ continue;
239
+ }
240
+ }
241
+ // Select agent based on strategy
242
+ const agent = await this.selectAgent(item.task);
243
+ if (!agent) {
244
+ // No suitable agent found
245
+ if (item.attempts < (this.config.maxRetries ?? 3)) {
246
+ item.attempts++;
247
+ await this.delay(this.config.retryDelay ?? 1000);
248
+ continue;
249
+ }
250
+ // Max retries reached
251
+ this.taskQueue.shift();
252
+ const result = this.activeResults.get(item.task.id);
253
+ if (result) {
254
+ result.status = "failed";
255
+ result.error = "No suitable agent found";
256
+ }
257
+ this.emitter.emit("task:failed", {
258
+ taskId: item.task.id,
259
+ error: "No suitable agent found",
260
+ });
261
+ continue;
262
+ }
263
+ // Remove from queue and execute
264
+ this.taskQueue.shift();
265
+ rotatedWithoutProgress = 0; // reset: we made forward progress
266
+ await this.executeTask(item.task, agent);
267
+ }
268
+ }
269
+ finally {
270
+ this.isProcessing = false;
271
+ // Re-process any tasks that were queued while this pass was running.
272
+ // Use a zero-delay tick to avoid unbounded call-stack growth.
273
+ if (this.taskQueue.length > 0) {
274
+ await new Promise((resolve) => setTimeout(resolve, 0));
275
+ await this.processQueue();
276
+ }
277
+ }
278
+ }
279
+ /**
280
+ * Select an agent based on strategy
281
+ */
282
+ async selectAgent(task) {
283
+ const availableAgents = Array.from(this.agents.values());
284
+ if (availableAgents.length === 0) {
285
+ return undefined;
286
+ }
287
+ switch (this.config.strategy) {
288
+ case "skillBased":
289
+ return this.selectBySkill(task, availableAgents);
290
+ case "loadBalanced":
291
+ return this.selectByLoad(availableAgents);
292
+ case "priority":
293
+ return this.selectByPriority(task, availableAgents);
294
+ case "affinity":
295
+ return this.selectByAffinity(task, availableAgents);
296
+ case "broadcast":
297
+ // For broadcast, return first agent (broadcast handled separately)
298
+ return availableAgents[0];
299
+ default:
300
+ return availableAgents[0];
301
+ }
302
+ }
303
+ /**
304
+ * Select agent by skill match
305
+ */
306
+ selectBySkill(task, agents) {
307
+ if (!task.requiredSkills || task.requiredSkills.length === 0) {
308
+ return agents[0];
309
+ }
310
+ let bestAgent;
311
+ let bestScore = 0;
312
+ for (const agent of agents) {
313
+ const capability = this.capabilities.get(agent.id);
314
+ if (!capability) {
315
+ continue;
316
+ }
317
+ // Calculate skill match score
318
+ let score = 0;
319
+ if (this.config.skillMatcher) {
320
+ score = this.config.skillMatcher(task, agent);
321
+ }
322
+ else {
323
+ // Default skill matching
324
+ for (const requiredSkill of task.requiredSkills) {
325
+ const hasSkill = capability.skills.some((s) => s.toLowerCase().includes(requiredSkill.toLowerCase()) ||
326
+ requiredSkill.toLowerCase().includes(s.toLowerCase()));
327
+ if (hasSkill) {
328
+ score++;
329
+ }
330
+ }
331
+ }
332
+ // Factor in success rate
333
+ score *= capability.successRate;
334
+ // Factor in load (prefer less busy)
335
+ score *= 1 - capability.currentLoad;
336
+ if (score > bestScore) {
337
+ bestScore = score;
338
+ bestAgent = agent;
339
+ }
340
+ }
341
+ return bestAgent ?? agents[0];
342
+ }
343
+ /**
344
+ * Select agent by load
345
+ */
346
+ selectByLoad(agents) {
347
+ let leastLoadedAgent;
348
+ let lowestLoad = Infinity;
349
+ for (const agent of agents) {
350
+ const capability = this.capabilities.get(agent.id);
351
+ if (capability && capability.currentLoad < lowestLoad) {
352
+ lowestLoad = capability.currentLoad;
353
+ leastLoadedAgent = agent;
354
+ }
355
+ }
356
+ return leastLoadedAgent ?? agents[0];
357
+ }
358
+ /**
359
+ * Select agent by priority matching
360
+ */
361
+ selectByPriority(task, agents) {
362
+ // For critical/high priority, prefer agents with best success rate
363
+ if (task.priority === "critical" || task.priority === "high") {
364
+ let bestAgent;
365
+ let bestRate = 0;
366
+ for (const agent of agents) {
367
+ const capability = this.capabilities.get(agent.id);
368
+ if (capability && capability.successRate > bestRate) {
369
+ bestRate = capability.successRate;
370
+ bestAgent = agent;
371
+ }
372
+ }
373
+ return bestAgent ?? agents[0];
374
+ }
375
+ // For low/background priority, prefer agents with lowest load
376
+ return this.selectByLoad(agents);
377
+ }
378
+ /**
379
+ * Select agent by affinity
380
+ */
381
+ selectByAffinity(task, agents) {
382
+ // Check preferred agent first
383
+ if (task.preferredAgent) {
384
+ const preferred = this.agents.get(task.preferredAgent);
385
+ if (preferred) {
386
+ return preferred;
387
+ }
388
+ }
389
+ // Check affinity tags
390
+ if (task.metadata?.affinityTags) {
391
+ const taskTags = task.metadata.affinityTags;
392
+ for (const agent of agents) {
393
+ const capability = this.capabilities.get(agent.id);
394
+ if (capability?.affinityTags) {
395
+ const hasMatch = taskTags.some((tag) => capability.affinityTags.includes(tag));
396
+ if (hasMatch) {
397
+ return agent;
398
+ }
399
+ }
400
+ }
401
+ }
402
+ // Fall back to load-based selection
403
+ return this.selectByLoad(agents);
404
+ }
405
+ /**
406
+ * Execute a task on an agent
407
+ */
408
+ async executeTask(task, agent) {
409
+ const result = this.activeResults.get(task.id);
410
+ if (!result) {
411
+ return;
412
+ }
413
+ result.agentId = agent.id;
414
+ result.status = "running";
415
+ // Update agent load
416
+ const capability = this.capabilities.get(agent.id);
417
+ if (capability) {
418
+ capability.currentLoad = Math.min(1, capability.currentLoad + 0.2);
419
+ }
420
+ this.emitter.emit("task:started", { taskId: task.id, agentId: agent.id });
421
+ const startTime = Date.now();
422
+ try {
423
+ // Execute with timeout
424
+ const agentResult = await withTimeout(agent.execute(task.input, {
425
+ context: task.metadata,
426
+ timeout: task.deadline
427
+ ? task.deadline - Date.now()
428
+ : this.config.taskTimeout,
429
+ }), this.config.taskTimeout ?? 60000, "Task execution timeout");
430
+ result.result = agentResult;
431
+ result.completedAt = Date.now();
432
+ result.status = agentResult.status === "success" ? "completed" : "failed";
433
+ result.error = agentResult.error;
434
+ // Update capability stats
435
+ if (capability) {
436
+ const duration = Date.now() - startTime;
437
+ capability.avgResponseTime =
438
+ (capability.avgResponseTime + duration) / 2;
439
+ if (agentResult.status === "success") {
440
+ capability.successRate = capability.successRate * 0.9 + 1 * 0.1; // Weighted average
441
+ }
442
+ else {
443
+ capability.successRate = capability.successRate * 0.9; // Decay on failure
444
+ }
445
+ }
446
+ this.emitter.emit("task:completed", {
447
+ taskId: task.id,
448
+ agentId: agent.id,
449
+ status: result.status,
450
+ });
451
+ }
452
+ catch (error) {
453
+ result.status = "failed";
454
+ result.error = error instanceof Error ? error.message : String(error);
455
+ result.completedAt = Date.now();
456
+ // Update capability stats
457
+ if (capability) {
458
+ capability.successRate = capability.successRate * 0.9;
459
+ }
460
+ this.emitter.emit("task:failed", {
461
+ taskId: task.id,
462
+ agentId: agent.id,
463
+ error: result.error,
464
+ });
465
+ }
466
+ finally {
467
+ // Reduce agent load
468
+ if (capability) {
469
+ capability.currentLoad = Math.max(0, capability.currentLoad - 0.2);
470
+ }
471
+ }
472
+ }
473
+ /**
474
+ * Broadcast a task to all agents
475
+ */
476
+ async broadcastTask(task) {
477
+ const results = new Map();
478
+ const agents = Array.from(this.agents.values());
479
+ const promises = agents.map(async (agent) => {
480
+ const taskCopy = {
481
+ ...task,
482
+ id: `${task.id}-${agent.id}`,
483
+ };
484
+ const result = {
485
+ taskId: taskCopy.id,
486
+ agentId: agent.id,
487
+ distributedAt: Date.now(),
488
+ status: "running",
489
+ };
490
+ try {
491
+ const agentResult = await agent.execute(taskCopy.input);
492
+ result.result = agentResult;
493
+ result.status =
494
+ agentResult.status === "success" ? "completed" : "failed";
495
+ result.completedAt = Date.now();
496
+ }
497
+ catch (error) {
498
+ result.status = "failed";
499
+ result.error = error instanceof Error ? error.message : String(error);
500
+ result.completedAt = Date.now();
501
+ }
502
+ results.set(agent.id, result);
503
+ });
504
+ await Promise.all(promises);
505
+ return results;
506
+ }
507
+ /**
508
+ * Get task result
509
+ */
510
+ getTaskResult(taskId) {
511
+ return this.activeResults.get(taskId);
512
+ }
513
+ /**
514
+ * Get queue status
515
+ */
516
+ getQueueStatus() {
517
+ let completed = 0;
518
+ let failed = 0;
519
+ let active = 0;
520
+ for (const result of this.activeResults.values()) {
521
+ switch (result.status) {
522
+ case "completed":
523
+ completed++;
524
+ break;
525
+ case "failed":
526
+ failed++;
527
+ break;
528
+ case "running":
529
+ active++;
530
+ break;
531
+ }
532
+ }
533
+ return {
534
+ pending: this.taskQueue.length,
535
+ active,
536
+ completed,
537
+ failed,
538
+ };
539
+ }
540
+ /**
541
+ * Clear completed/failed tasks
542
+ */
543
+ clearCompleted() {
544
+ for (const [taskId, result] of this.activeResults) {
545
+ if (result.status === "completed" || result.status === "failed") {
546
+ this.activeResults.delete(taskId);
547
+ }
548
+ }
549
+ }
550
+ /**
551
+ * Fail all queued tasks (used on deadlock detection)
552
+ */
553
+ failAllQueuedTasks(reason) {
554
+ while (this.taskQueue.length > 0) {
555
+ const stuck = this.taskQueue.shift();
556
+ const stuckResult = this.activeResults.get(stuck.task.id);
557
+ if (stuckResult) {
558
+ stuckResult.status = "failed";
559
+ stuckResult.error = reason;
560
+ }
561
+ this.emitter.emit("task:failed", {
562
+ taskId: stuck.task.id,
563
+ error: reason,
564
+ });
565
+ }
566
+ }
567
+ /**
568
+ * Helper delay function
569
+ */
570
+ delay(ms) {
571
+ return new Promise((resolve) => setTimeout(resolve, ms));
572
+ }
573
+ /**
574
+ * Subscribe to distributor events
575
+ */
576
+ on(event, handler) {
577
+ this.emitter.on(event, handler);
578
+ }
579
+ /**
580
+ * Unsubscribe from distributor events
581
+ */
582
+ off(event, handler) {
583
+ this.emitter.off(event, handler);
584
+ }
585
+ }