@juspay/neurolink 9.82.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.
- package/CHANGELOG.md +18 -0
- package/dist/agent/agent.d.ts +104 -0
- package/dist/agent/agent.js +401 -0
- package/dist/agent/agentNetwork.d.ts +87 -0
- package/dist/agent/agentNetwork.js +458 -0
- package/dist/agent/communication/index.d.ts +9 -0
- package/dist/agent/communication/index.js +9 -0
- package/dist/agent/communication/message-bus.d.ts +113 -0
- package/dist/agent/communication/message-bus.js +406 -0
- package/dist/agent/coordination/coordinator.d.ts +83 -0
- package/dist/agent/coordination/coordinator.js +598 -0
- package/dist/agent/coordination/index.d.ts +10 -0
- package/dist/agent/coordination/index.js +10 -0
- package/dist/agent/coordination/task-distributor.d.ts +113 -0
- package/dist/agent/coordination/task-distributor.js +585 -0
- package/dist/agent/index.d.ts +38 -0
- package/dist/agent/index.js +44 -0
- package/dist/agent/orchestration/index.d.ts +10 -0
- package/dist/agent/orchestration/index.js +10 -0
- package/dist/agent/orchestration/orchestrator.d.ts +114 -0
- package/dist/agent/orchestration/orchestrator.js +476 -0
- package/dist/agent/orchestration/topology.d.ts +164 -0
- package/dist/agent/orchestration/topology.js +520 -0
- package/dist/agent/prompts/routingPrompts.d.ts +63 -0
- package/dist/agent/prompts/routingPrompts.js +201 -0
- package/dist/browser/neurolink.min.js +377 -358
- package/dist/cli/commands/agent.d.ts +37 -0
- package/dist/cli/commands/agent.js +718 -0
- package/dist/cli/factories/commandFactory.d.ts +8 -0
- package/dist/cli/factories/commandFactory.js +116 -0
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/parser.js +4 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +19 -0
- package/dist/lib/agent/agent.d.ts +104 -0
- package/dist/lib/agent/agent.js +402 -0
- package/dist/lib/agent/agentNetwork.d.ts +87 -0
- package/dist/lib/agent/agentNetwork.js +459 -0
- package/dist/lib/agent/communication/index.d.ts +9 -0
- package/dist/lib/agent/communication/index.js +10 -0
- package/dist/lib/agent/communication/message-bus.d.ts +113 -0
- package/dist/lib/agent/communication/message-bus.js +407 -0
- package/dist/lib/agent/coordination/coordinator.d.ts +83 -0
- package/dist/lib/agent/coordination/coordinator.js +599 -0
- package/dist/lib/agent/coordination/index.d.ts +10 -0
- package/dist/lib/agent/coordination/index.js +11 -0
- package/dist/lib/agent/coordination/task-distributor.d.ts +113 -0
- package/dist/lib/agent/coordination/task-distributor.js +586 -0
- package/dist/lib/agent/index.d.ts +38 -0
- package/dist/lib/agent/index.js +45 -0
- package/dist/lib/agent/orchestration/index.d.ts +10 -0
- package/dist/lib/agent/orchestration/index.js +11 -0
- package/dist/lib/agent/orchestration/orchestrator.d.ts +114 -0
- package/dist/lib/agent/orchestration/orchestrator.js +477 -0
- package/dist/lib/agent/orchestration/topology.d.ts +164 -0
- package/dist/lib/agent/orchestration/topology.js +521 -0
- package/dist/lib/agent/prompts/routingPrompts.d.ts +63 -0
- package/dist/lib/agent/prompts/routingPrompts.js +202 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.js +19 -0
- package/dist/lib/neurolink.d.ts +129 -0
- package/dist/lib/neurolink.js +276 -0
- package/dist/lib/processors/config/{fileTypes.js → fileExtensions.js} +1 -1
- package/dist/lib/processors/config/index.d.ts +2 -2
- package/dist/lib/processors/config/index.js +2 -2
- package/dist/lib/processors/config/{mimeTypes.js → mimeConstants.js} +1 -1
- package/dist/lib/processors/index.js +8 -0
- package/dist/lib/providers/googleAiStudio.js +28 -7
- package/dist/lib/providers/googleNativeGemini3.d.ts +18 -0
- package/dist/lib/providers/googleNativeGemini3.js +69 -3
- package/dist/lib/providers/googleVertex.js +33 -9
- package/dist/lib/types/agentNetwork.d.ts +1184 -0
- package/dist/lib/types/agentNetwork.js +8 -0
- package/dist/lib/types/cli.d.ts +66 -0
- package/dist/lib/types/common.d.ts +3 -0
- package/dist/lib/types/generate.d.ts +53 -0
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.js +3 -0
- package/dist/lib/types/ioProcessor.d.ts +119 -0
- package/dist/lib/types/ioProcessor.js +10 -0
- package/dist/lib/types/providers.d.ts +8 -0
- package/dist/lib/types/stream.d.ts +36 -0
- package/dist/lib/utils/piiDetector.d.ts +24 -0
- package/dist/lib/utils/piiDetector.js +221 -0
- package/dist/lib/utils/pricing.js +144 -26
- package/dist/lib/utils/responseValidator.d.ts +21 -0
- package/dist/lib/utils/responseValidator.js +354 -0
- package/dist/lib/utils/tokenUtils.d.ts +11 -0
- package/dist/lib/utils/tokenUtils.js +33 -2
- package/dist/lib/utils/tripwireEvaluator.d.ts +73 -0
- package/dist/lib/utils/tripwireEvaluator.js +285 -0
- package/dist/neurolink.d.ts +129 -0
- package/dist/neurolink.js +276 -0
- package/dist/processors/config/index.d.ts +2 -2
- package/dist/processors/config/index.js +2 -2
- package/dist/processors/index.js +8 -0
- package/dist/providers/googleAiStudio.js +28 -7
- package/dist/providers/googleNativeGemini3.d.ts +18 -0
- package/dist/providers/googleNativeGemini3.js +69 -3
- package/dist/providers/googleVertex.js +33 -9
- package/dist/types/agentNetwork.d.ts +1184 -0
- package/dist/types/agentNetwork.js +7 -0
- package/dist/types/cli.d.ts +66 -0
- package/dist/types/common.d.ts +3 -0
- package/dist/types/generate.d.ts +53 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +3 -0
- package/dist/types/ioProcessor.d.ts +119 -0
- package/dist/types/ioProcessor.js +9 -0
- package/dist/types/providers.d.ts +8 -0
- package/dist/types/stream.d.ts +36 -0
- package/dist/utils/piiDetector.d.ts +24 -0
- package/dist/utils/piiDetector.js +220 -0
- package/dist/utils/pricing.js +144 -26
- package/dist/utils/responseValidator.d.ts +21 -0
- package/dist/utils/responseValidator.js +353 -0
- package/dist/utils/tokenUtils.d.ts +11 -0
- package/dist/utils/tokenUtils.js +33 -2
- package/dist/utils/tripwireEvaluator.d.ts +73 -0
- package/dist/utils/tripwireEvaluator.js +284 -0
- package/package.json +1 -1
- /package/dist/lib/processors/config/{fileTypes.d.ts → fileExtensions.d.ts} +0 -0
- /package/dist/lib/processors/config/{mimeTypes.d.ts → mimeConstants.d.ts} +0 -0
- /package/dist/processors/config/{fileTypes.d.ts → fileExtensions.d.ts} +0 -0
- /package/dist/processors/config/{fileTypes.js → fileExtensions.js} +0 -0
- /package/dist/processors/config/{mimeTypes.d.ts → mimeConstants.d.ts} +0 -0
- /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,586 @@
|
|
|
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
|
+
}
|
|
586
|
+
//# sourceMappingURL=task-distributor.js.map
|