@lov3kaizen/agentsea-crews 0.5.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/LICENSE +21 -0
- package/README.md +325 -0
- package/dist/chunk-4PF73ECN.mjs +587 -0
- package/dist/chunk-G3PAPYOI.mjs +1070 -0
- package/dist/chunk-QMK3HWFX.mjs +4584 -0
- package/dist/index.js +8761 -0
- package/dist/index.mjs +2528 -0
- package/dist/nestjs/index.js +5589 -0
- package/dist/nestjs/index.mjs +453 -0
- package/dist/templates/index.js +5625 -0
- package/dist/templates/index.mjs +29 -0
- package/package.json +89 -0
|
@@ -0,0 +1,4584 @@
|
|
|
1
|
+
// src/core/Role.ts
|
|
2
|
+
var Role = class _Role {
|
|
3
|
+
name;
|
|
4
|
+
description;
|
|
5
|
+
capabilities;
|
|
6
|
+
systemPrompt;
|
|
7
|
+
goals;
|
|
8
|
+
constraints;
|
|
9
|
+
backstory;
|
|
10
|
+
canDelegate;
|
|
11
|
+
canReceiveDelegation;
|
|
12
|
+
maxConcurrentTasks;
|
|
13
|
+
capabilityMap;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.name = config.name;
|
|
16
|
+
this.description = config.description;
|
|
17
|
+
this.capabilities = config.capabilities;
|
|
18
|
+
this.systemPrompt = config.systemPrompt;
|
|
19
|
+
this.goals = config.goals ?? [];
|
|
20
|
+
this.constraints = config.constraints ?? [];
|
|
21
|
+
this.backstory = config.backstory ?? "";
|
|
22
|
+
this.canDelegate = config.canDelegate ?? false;
|
|
23
|
+
this.canReceiveDelegation = config.canReceiveDelegation ?? true;
|
|
24
|
+
this.maxConcurrentTasks = config.maxConcurrentTasks ?? 1;
|
|
25
|
+
this.capabilityMap = /* @__PURE__ */ new Map();
|
|
26
|
+
for (const cap of this.capabilities) {
|
|
27
|
+
this.capabilityMap.set(cap.name.toLowerCase(), cap);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Generate a complete system prompt incorporating all role aspects
|
|
32
|
+
*/
|
|
33
|
+
generateSystemPrompt() {
|
|
34
|
+
const parts = [];
|
|
35
|
+
parts.push(this.systemPrompt);
|
|
36
|
+
parts.push(`
|
|
37
|
+
|
|
38
|
+
You are acting as a ${this.name}: ${this.description}`);
|
|
39
|
+
if (this.backstory) {
|
|
40
|
+
parts.push(`
|
|
41
|
+
|
|
42
|
+
Background: ${this.backstory}`);
|
|
43
|
+
}
|
|
44
|
+
if (this.goals.length > 0) {
|
|
45
|
+
parts.push("\n\nYour goals:");
|
|
46
|
+
for (const goal of this.goals) {
|
|
47
|
+
parts.push(`- ${goal}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (this.constraints.length > 0) {
|
|
51
|
+
parts.push("\n\nConstraints you must respect:");
|
|
52
|
+
for (const constraint of this.constraints) {
|
|
53
|
+
parts.push(`- ${constraint}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (this.capabilities.length > 0) {
|
|
57
|
+
parts.push("\n\nYour capabilities:");
|
|
58
|
+
for (const cap of this.capabilities) {
|
|
59
|
+
parts.push(`- ${cap.name} (${cap.proficiency}): ${cap.description}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return parts.join("\n");
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if this role has a specific capability
|
|
66
|
+
*/
|
|
67
|
+
hasCapability(name) {
|
|
68
|
+
return this.capabilityMap.has(name.toLowerCase());
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get a specific capability by name
|
|
72
|
+
*/
|
|
73
|
+
getCapability(name) {
|
|
74
|
+
return this.capabilityMap.get(name.toLowerCase());
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get proficiency level for a capability
|
|
78
|
+
*/
|
|
79
|
+
getProficiency(name) {
|
|
80
|
+
const cap = this.getCapability(name);
|
|
81
|
+
return cap?.proficiency;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get proficiency score (0-1) for a capability
|
|
85
|
+
*/
|
|
86
|
+
getProficiencyScore(name) {
|
|
87
|
+
const cap = this.getCapability(name);
|
|
88
|
+
if (!cap) return 0;
|
|
89
|
+
const weights = {
|
|
90
|
+
novice: 0.25,
|
|
91
|
+
intermediate: 0.5,
|
|
92
|
+
expert: 0.75,
|
|
93
|
+
master: 1
|
|
94
|
+
};
|
|
95
|
+
return weights[cap.proficiency];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get all tools required by this role's capabilities
|
|
99
|
+
*/
|
|
100
|
+
getRequiredTools() {
|
|
101
|
+
const tools = /* @__PURE__ */ new Set();
|
|
102
|
+
for (const cap of this.capabilities) {
|
|
103
|
+
if (cap.tools) {
|
|
104
|
+
for (const tool of cap.tools) {
|
|
105
|
+
tools.add(tool);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return Array.from(tools);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get all keywords from this role's capabilities
|
|
113
|
+
*/
|
|
114
|
+
getKeywords() {
|
|
115
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
116
|
+
for (const cap of this.capabilities) {
|
|
117
|
+
if (cap.keywords) {
|
|
118
|
+
for (const keyword of cap.keywords) {
|
|
119
|
+
keywords.add(keyword.toLowerCase());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return Array.from(keywords);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Match required capabilities against this role
|
|
127
|
+
*/
|
|
128
|
+
matchCapabilities(required) {
|
|
129
|
+
const matched = [];
|
|
130
|
+
const missing = [];
|
|
131
|
+
for (const req of required) {
|
|
132
|
+
const available = this.getCapability(req.name);
|
|
133
|
+
if (available) {
|
|
134
|
+
matched.push(available);
|
|
135
|
+
} else {
|
|
136
|
+
missing.push(req);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const score = required.length > 0 ? matched.length / required.length : 1;
|
|
140
|
+
const canExecute = missing.length === 0;
|
|
141
|
+
return {
|
|
142
|
+
matched,
|
|
143
|
+
missing,
|
|
144
|
+
score,
|
|
145
|
+
canExecute
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Calculate relevance score for a task description
|
|
150
|
+
*/
|
|
151
|
+
calculateRelevanceScore(taskDescription) {
|
|
152
|
+
const descLower = taskDescription.toLowerCase();
|
|
153
|
+
const keywords = this.getKeywords();
|
|
154
|
+
if (keywords.length === 0) {
|
|
155
|
+
let matchCount2 = 0;
|
|
156
|
+
for (const cap of this.capabilities) {
|
|
157
|
+
if (descLower.includes(cap.name.toLowerCase())) {
|
|
158
|
+
matchCount2++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return this.capabilities.length > 0 ? matchCount2 / this.capabilities.length : 0;
|
|
162
|
+
}
|
|
163
|
+
let matchCount = 0;
|
|
164
|
+
for (const keyword of keywords) {
|
|
165
|
+
if (descLower.includes(keyword)) {
|
|
166
|
+
matchCount++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return keywords.length > 0 ? matchCount / keywords.length : 0;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Serialize to JSON
|
|
173
|
+
*/
|
|
174
|
+
toJSON() {
|
|
175
|
+
return {
|
|
176
|
+
name: this.name,
|
|
177
|
+
description: this.description,
|
|
178
|
+
capabilities: this.capabilities,
|
|
179
|
+
systemPrompt: this.systemPrompt,
|
|
180
|
+
goals: this.goals.length > 0 ? this.goals : void 0,
|
|
181
|
+
constraints: this.constraints.length > 0 ? this.constraints : void 0,
|
|
182
|
+
backstory: this.backstory || void 0,
|
|
183
|
+
canDelegate: this.canDelegate,
|
|
184
|
+
canReceiveDelegation: this.canReceiveDelegation,
|
|
185
|
+
maxConcurrentTasks: this.maxConcurrentTasks
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create from JSON
|
|
190
|
+
*/
|
|
191
|
+
static fromJSON(json) {
|
|
192
|
+
return new _Role(json);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Create a simple role with minimal configuration
|
|
196
|
+
*/
|
|
197
|
+
static simple(name, description, systemPrompt, capabilities = []) {
|
|
198
|
+
return new _Role({
|
|
199
|
+
name,
|
|
200
|
+
description,
|
|
201
|
+
systemPrompt,
|
|
202
|
+
capabilities: capabilities.map((cap) => ({
|
|
203
|
+
name: cap,
|
|
204
|
+
description: cap,
|
|
205
|
+
proficiency: "intermediate"
|
|
206
|
+
}))
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function createRole(config) {
|
|
211
|
+
return new Role(config);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/core/Task.ts
|
|
215
|
+
import { nanoid } from "nanoid";
|
|
216
|
+
var Task = class _Task {
|
|
217
|
+
id;
|
|
218
|
+
description;
|
|
219
|
+
expectedOutput;
|
|
220
|
+
priority;
|
|
221
|
+
dependencies;
|
|
222
|
+
deadline;
|
|
223
|
+
context;
|
|
224
|
+
assignTo;
|
|
225
|
+
requiredCapabilities;
|
|
226
|
+
estimatedTokens;
|
|
227
|
+
maxRetries;
|
|
228
|
+
timeoutMs;
|
|
229
|
+
tags;
|
|
230
|
+
_status;
|
|
231
|
+
_assignedAgent;
|
|
232
|
+
_result;
|
|
233
|
+
_metadata;
|
|
234
|
+
constructor(config) {
|
|
235
|
+
this.id = config.id ?? nanoid();
|
|
236
|
+
this.description = config.description;
|
|
237
|
+
this.expectedOutput = config.expectedOutput;
|
|
238
|
+
this.priority = config.priority ?? "medium";
|
|
239
|
+
this.dependencies = config.dependencies ?? [];
|
|
240
|
+
this.deadline = config.deadline;
|
|
241
|
+
this.context = config.context ?? {};
|
|
242
|
+
this.assignTo = config.assignTo;
|
|
243
|
+
this.requiredCapabilities = config.requiredCapabilities ?? [];
|
|
244
|
+
this.estimatedTokens = config.estimatedTokens;
|
|
245
|
+
this.maxRetries = config.maxRetries ?? 3;
|
|
246
|
+
this.timeoutMs = config.timeoutMs;
|
|
247
|
+
this.tags = config.tags ?? [];
|
|
248
|
+
this._status = "pending";
|
|
249
|
+
this._assignedAgent = void 0;
|
|
250
|
+
this._result = void 0;
|
|
251
|
+
this._metadata = {
|
|
252
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
253
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
254
|
+
attempts: 0,
|
|
255
|
+
statusHistory: [{ status: "pending", timestamp: /* @__PURE__ */ new Date() }]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
// ============ Getters ============
|
|
259
|
+
get status() {
|
|
260
|
+
return this._status;
|
|
261
|
+
}
|
|
262
|
+
get assignedAgent() {
|
|
263
|
+
return this._assignedAgent;
|
|
264
|
+
}
|
|
265
|
+
get result() {
|
|
266
|
+
return this._result;
|
|
267
|
+
}
|
|
268
|
+
get metadata() {
|
|
269
|
+
return { ...this._metadata };
|
|
270
|
+
}
|
|
271
|
+
get isCompleted() {
|
|
272
|
+
return this._status === "completed";
|
|
273
|
+
}
|
|
274
|
+
get isFailed() {
|
|
275
|
+
return this._status === "failed";
|
|
276
|
+
}
|
|
277
|
+
get isPending() {
|
|
278
|
+
return this._status === "pending";
|
|
279
|
+
}
|
|
280
|
+
get isBlocked() {
|
|
281
|
+
return this._status === "blocked";
|
|
282
|
+
}
|
|
283
|
+
get isInProgress() {
|
|
284
|
+
return this._status === "in_progress";
|
|
285
|
+
}
|
|
286
|
+
get isAssigned() {
|
|
287
|
+
return this._status === "assigned";
|
|
288
|
+
}
|
|
289
|
+
get attempts() {
|
|
290
|
+
return this._metadata.attempts;
|
|
291
|
+
}
|
|
292
|
+
// ============ Lifecycle Methods ============
|
|
293
|
+
/**
|
|
294
|
+
* Assign the task to an agent
|
|
295
|
+
*/
|
|
296
|
+
assign(agentName) {
|
|
297
|
+
if (this._status !== "pending" && this._status !== "blocked") {
|
|
298
|
+
throw new Error(`Cannot assign task in status: ${this._status}`);
|
|
299
|
+
}
|
|
300
|
+
this._assignedAgent = agentName;
|
|
301
|
+
this.updateStatus("assigned");
|
|
302
|
+
this._metadata.assignedAt = /* @__PURE__ */ new Date();
|
|
303
|
+
this._metadata.attempts++;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Start task execution
|
|
307
|
+
*/
|
|
308
|
+
start() {
|
|
309
|
+
if (this._status !== "assigned") {
|
|
310
|
+
throw new Error(`Cannot start task in status: ${this._status}`);
|
|
311
|
+
}
|
|
312
|
+
this.updateStatus("in_progress");
|
|
313
|
+
this._metadata.startedAt = /* @__PURE__ */ new Date();
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Complete the task with a result
|
|
317
|
+
*/
|
|
318
|
+
complete(result) {
|
|
319
|
+
if (this._status !== "in_progress") {
|
|
320
|
+
throw new Error(`Cannot complete task in status: ${this._status}`);
|
|
321
|
+
}
|
|
322
|
+
this._result = {
|
|
323
|
+
...result,
|
|
324
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
325
|
+
completedBy: this._assignedAgent
|
|
326
|
+
};
|
|
327
|
+
this._metadata.actualDuration = this._metadata.startedAt ? Date.now() - this._metadata.startedAt.getTime() : void 0;
|
|
328
|
+
this.updateStatus("completed");
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Mark the task as failed
|
|
332
|
+
*/
|
|
333
|
+
fail(error) {
|
|
334
|
+
if (this._status !== "in_progress" && this._status !== "assigned") {
|
|
335
|
+
throw new Error(`Cannot fail task in status: ${this._status}`);
|
|
336
|
+
}
|
|
337
|
+
this._result = {
|
|
338
|
+
output: "",
|
|
339
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
340
|
+
completedBy: this._assignedAgent || "unknown",
|
|
341
|
+
iterations: 0,
|
|
342
|
+
tokensUsed: 0,
|
|
343
|
+
error
|
|
344
|
+
};
|
|
345
|
+
this.updateStatus("failed");
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Block the task (e.g., due to unmet dependencies)
|
|
349
|
+
*/
|
|
350
|
+
block(reason) {
|
|
351
|
+
this.updateStatus("blocked", reason);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Unblock the task
|
|
355
|
+
*/
|
|
356
|
+
unblock() {
|
|
357
|
+
if (this._status !== "blocked") {
|
|
358
|
+
throw new Error(`Cannot unblock task in status: ${this._status}`);
|
|
359
|
+
}
|
|
360
|
+
this.updateStatus("pending");
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Cancel the task
|
|
364
|
+
*/
|
|
365
|
+
cancel(reason) {
|
|
366
|
+
if (this._status === "completed") {
|
|
367
|
+
throw new Error("Cannot cancel completed task");
|
|
368
|
+
}
|
|
369
|
+
this.updateStatus("cancelled", reason);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Reset the task for retry
|
|
373
|
+
*/
|
|
374
|
+
reset() {
|
|
375
|
+
if (this._status !== "failed") {
|
|
376
|
+
throw new Error("Can only reset failed tasks");
|
|
377
|
+
}
|
|
378
|
+
this._status = "pending";
|
|
379
|
+
this._assignedAgent = void 0;
|
|
380
|
+
this._result = void 0;
|
|
381
|
+
this._metadata.statusHistory?.push({
|
|
382
|
+
status: "pending",
|
|
383
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
384
|
+
reason: "reset for retry"
|
|
385
|
+
});
|
|
386
|
+
this._metadata.updatedAt = /* @__PURE__ */ new Date();
|
|
387
|
+
}
|
|
388
|
+
// ============ Query Methods ============
|
|
389
|
+
/**
|
|
390
|
+
* Check if the task can start (dependencies satisfied)
|
|
391
|
+
*/
|
|
392
|
+
canStart(completedTaskIds = /* @__PURE__ */ new Set()) {
|
|
393
|
+
if (this._status !== "pending" && this._status !== "blocked") {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
for (const depId of this.dependencies) {
|
|
397
|
+
if (!completedTaskIds.has(depId)) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Check if all dependencies are satisfied
|
|
405
|
+
*/
|
|
406
|
+
dependenciesSatisfied(completedTaskIds) {
|
|
407
|
+
for (const depId of this.dependencies) {
|
|
408
|
+
if (!completedTaskIds.has(depId)) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Check if task can be retried
|
|
416
|
+
*/
|
|
417
|
+
canRetry() {
|
|
418
|
+
return this._status === "failed" && this._metadata.attempts < this.maxRetries;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Check if task is past deadline
|
|
422
|
+
*/
|
|
423
|
+
isPastDeadline() {
|
|
424
|
+
if (!this.deadline) return false;
|
|
425
|
+
return /* @__PURE__ */ new Date() > this.deadline;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Get time remaining until deadline (ms)
|
|
429
|
+
*/
|
|
430
|
+
getTimeRemaining() {
|
|
431
|
+
if (!this.deadline) return null;
|
|
432
|
+
return Math.max(0, this.deadline.getTime() - Date.now());
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get priority weight for queue ordering
|
|
436
|
+
*/
|
|
437
|
+
getPriorityWeight() {
|
|
438
|
+
const weights = {
|
|
439
|
+
critical: 100,
|
|
440
|
+
high: 75,
|
|
441
|
+
medium: 50,
|
|
442
|
+
low: 25
|
|
443
|
+
};
|
|
444
|
+
return weights[this.priority];
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Compare to another task for priority ordering
|
|
448
|
+
*/
|
|
449
|
+
compareTo(other) {
|
|
450
|
+
const priorityDiff = other.getPriorityWeight() - this.getPriorityWeight();
|
|
451
|
+
if (priorityDiff !== 0) return priorityDiff;
|
|
452
|
+
if (this.deadline && other.deadline) {
|
|
453
|
+
return this.deadline.getTime() - other.deadline.getTime();
|
|
454
|
+
}
|
|
455
|
+
if (this.deadline) return -1;
|
|
456
|
+
if (other.deadline) return 1;
|
|
457
|
+
return this._metadata.createdAt.getTime() - other._metadata.createdAt.getTime();
|
|
458
|
+
}
|
|
459
|
+
// ============ Private Methods ============
|
|
460
|
+
updateStatus(status, reason) {
|
|
461
|
+
this._status = status;
|
|
462
|
+
this._metadata.updatedAt = /* @__PURE__ */ new Date();
|
|
463
|
+
this._metadata.statusHistory?.push({
|
|
464
|
+
status,
|
|
465
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
466
|
+
reason
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
// ============ Serialization ============
|
|
470
|
+
/**
|
|
471
|
+
* Get the full task state
|
|
472
|
+
*/
|
|
473
|
+
getState() {
|
|
474
|
+
return {
|
|
475
|
+
config: this.toConfig(),
|
|
476
|
+
status: this._status,
|
|
477
|
+
assignedAgent: this._assignedAgent,
|
|
478
|
+
result: this._result,
|
|
479
|
+
metadata: { ...this._metadata }
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Convert to config object
|
|
484
|
+
*/
|
|
485
|
+
toConfig() {
|
|
486
|
+
return {
|
|
487
|
+
id: this.id,
|
|
488
|
+
description: this.description,
|
|
489
|
+
expectedOutput: this.expectedOutput,
|
|
490
|
+
priority: this.priority,
|
|
491
|
+
dependencies: this.dependencies.length > 0 ? this.dependencies : void 0,
|
|
492
|
+
deadline: this.deadline,
|
|
493
|
+
context: Object.keys(this.context).length > 0 ? this.context : void 0,
|
|
494
|
+
assignTo: this.assignTo,
|
|
495
|
+
requiredCapabilities: this.requiredCapabilities.length > 0 ? this.requiredCapabilities : void 0,
|
|
496
|
+
estimatedTokens: this.estimatedTokens,
|
|
497
|
+
maxRetries: this.maxRetries,
|
|
498
|
+
timeoutMs: this.timeoutMs,
|
|
499
|
+
tags: this.tags.length > 0 ? this.tags : void 0
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Serialize to JSON
|
|
504
|
+
*/
|
|
505
|
+
toJSON() {
|
|
506
|
+
return this.getState();
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Create from state
|
|
510
|
+
*/
|
|
511
|
+
static fromState(state) {
|
|
512
|
+
const task = new _Task(state.config);
|
|
513
|
+
task._status = state.status;
|
|
514
|
+
task._assignedAgent = state.assignedAgent;
|
|
515
|
+
task._result = state.result;
|
|
516
|
+
task._metadata = state.metadata;
|
|
517
|
+
return task;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
function createTask(config) {
|
|
521
|
+
return new Task(config);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/core/TaskQueue.ts
|
|
525
|
+
var TaskQueue = class {
|
|
526
|
+
tasks = [];
|
|
527
|
+
taskMap = /* @__PURE__ */ new Map();
|
|
528
|
+
maxSize;
|
|
529
|
+
autoSort;
|
|
530
|
+
dirty = false;
|
|
531
|
+
constructor(config = {}) {
|
|
532
|
+
this.maxSize = config.maxSize ?? Infinity;
|
|
533
|
+
this.autoSort = config.autoSort ?? true;
|
|
534
|
+
}
|
|
535
|
+
// ============ Basic Operations ============
|
|
536
|
+
/**
|
|
537
|
+
* Add a task to the queue
|
|
538
|
+
*/
|
|
539
|
+
enqueue(task) {
|
|
540
|
+
if (this.taskMap.has(task.id)) {
|
|
541
|
+
throw new Error(`Task ${task.id} already in queue`);
|
|
542
|
+
}
|
|
543
|
+
if (this.tasks.length >= this.maxSize) {
|
|
544
|
+
throw new Error("Queue is full");
|
|
545
|
+
}
|
|
546
|
+
this.tasks.push(task);
|
|
547
|
+
this.taskMap.set(task.id, task);
|
|
548
|
+
this.dirty = true;
|
|
549
|
+
if (this.autoSort) {
|
|
550
|
+
this.sort();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Add multiple tasks to the queue
|
|
555
|
+
*/
|
|
556
|
+
enqueueMany(tasks) {
|
|
557
|
+
for (const task of tasks) {
|
|
558
|
+
if (!this.taskMap.has(task.id)) {
|
|
559
|
+
if (this.tasks.length < this.maxSize) {
|
|
560
|
+
this.tasks.push(task);
|
|
561
|
+
this.taskMap.set(task.id, task);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
this.dirty = true;
|
|
566
|
+
if (this.autoSort) {
|
|
567
|
+
this.sort();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Remove and return the highest priority task
|
|
572
|
+
*/
|
|
573
|
+
dequeue() {
|
|
574
|
+
if (this.dirty && this.autoSort) {
|
|
575
|
+
this.sort();
|
|
576
|
+
}
|
|
577
|
+
const task = this.tasks.shift();
|
|
578
|
+
if (task) {
|
|
579
|
+
this.taskMap.delete(task.id);
|
|
580
|
+
}
|
|
581
|
+
return task;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Peek at the highest priority task without removing
|
|
585
|
+
*/
|
|
586
|
+
peek() {
|
|
587
|
+
if (this.dirty && this.autoSort) {
|
|
588
|
+
this.sort();
|
|
589
|
+
}
|
|
590
|
+
return this.tasks[0];
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Get a specific task by ID
|
|
594
|
+
*/
|
|
595
|
+
get(id) {
|
|
596
|
+
return this.taskMap.get(id);
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Remove a specific task by ID
|
|
600
|
+
*/
|
|
601
|
+
remove(id) {
|
|
602
|
+
const task = this.taskMap.get(id);
|
|
603
|
+
if (task) {
|
|
604
|
+
this.tasks = this.tasks.filter((t) => t.id !== id);
|
|
605
|
+
this.taskMap.delete(id);
|
|
606
|
+
}
|
|
607
|
+
return task;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Check if a task is in the queue
|
|
611
|
+
*/
|
|
612
|
+
has(id) {
|
|
613
|
+
return this.taskMap.has(id);
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Clear all tasks from the queue
|
|
617
|
+
*/
|
|
618
|
+
clear() {
|
|
619
|
+
this.tasks = [];
|
|
620
|
+
this.taskMap.clear();
|
|
621
|
+
this.dirty = false;
|
|
622
|
+
}
|
|
623
|
+
// ============ Query Methods ============
|
|
624
|
+
/**
|
|
625
|
+
* Get tasks by status
|
|
626
|
+
*/
|
|
627
|
+
getByStatus(status) {
|
|
628
|
+
return this.tasks.filter((t) => t.status === status);
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get tasks by priority
|
|
632
|
+
*/
|
|
633
|
+
getByPriority(priority) {
|
|
634
|
+
return this.tasks.filter((t) => t.priority === priority);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Get tasks that are ready to start (dependencies satisfied)
|
|
638
|
+
*/
|
|
639
|
+
getReadyTasks(completedTaskIds = /* @__PURE__ */ new Set()) {
|
|
640
|
+
return this.tasks.filter((t) => t.canStart(completedTaskIds));
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get tasks assigned to a specific agent
|
|
644
|
+
*/
|
|
645
|
+
getByAgent(agentName) {
|
|
646
|
+
return this.tasks.filter((t) => t.assignedAgent === agentName);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Get tasks with a specific tag
|
|
650
|
+
*/
|
|
651
|
+
getByTag(tag) {
|
|
652
|
+
return this.tasks.filter((t) => t.tags.includes(tag));
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Get pending tasks
|
|
656
|
+
*/
|
|
657
|
+
getPending() {
|
|
658
|
+
return this.getByStatus("pending");
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Get blocked tasks
|
|
662
|
+
*/
|
|
663
|
+
getBlocked() {
|
|
664
|
+
return this.getByStatus("blocked");
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Get in-progress tasks
|
|
668
|
+
*/
|
|
669
|
+
getInProgress() {
|
|
670
|
+
return this.getByStatus("in_progress");
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Get failed tasks
|
|
674
|
+
*/
|
|
675
|
+
getFailed() {
|
|
676
|
+
return this.getByStatus("failed");
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Get tasks that can be retried
|
|
680
|
+
*/
|
|
681
|
+
getRetryable() {
|
|
682
|
+
return this.tasks.filter((t) => t.canRetry());
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Get tasks past their deadline
|
|
686
|
+
*/
|
|
687
|
+
getOverdue() {
|
|
688
|
+
return this.tasks.filter((t) => t.isPastDeadline());
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Get the next N ready tasks
|
|
692
|
+
*/
|
|
693
|
+
getNextReady(n, completedTaskIds = /* @__PURE__ */ new Set()) {
|
|
694
|
+
if (this.dirty && this.autoSort) {
|
|
695
|
+
this.sort();
|
|
696
|
+
}
|
|
697
|
+
const ready = [];
|
|
698
|
+
for (const task of this.tasks) {
|
|
699
|
+
if (ready.length >= n) break;
|
|
700
|
+
if (task.canStart(completedTaskIds)) {
|
|
701
|
+
ready.push(task);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return ready;
|
|
705
|
+
}
|
|
706
|
+
// ============ State Methods ============
|
|
707
|
+
/**
|
|
708
|
+
* Get queue size
|
|
709
|
+
*/
|
|
710
|
+
get size() {
|
|
711
|
+
return this.tasks.length;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Check if queue is empty
|
|
715
|
+
*/
|
|
716
|
+
isEmpty() {
|
|
717
|
+
return this.tasks.length === 0;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Check if queue is full
|
|
721
|
+
*/
|
|
722
|
+
isFull() {
|
|
723
|
+
return this.tasks.length >= this.maxSize;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Get all tasks (copy)
|
|
727
|
+
*/
|
|
728
|
+
getAll() {
|
|
729
|
+
return [...this.tasks];
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Get all task IDs
|
|
733
|
+
*/
|
|
734
|
+
getAllIds() {
|
|
735
|
+
return this.tasks.map((t) => t.id);
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Get queue statistics
|
|
739
|
+
*/
|
|
740
|
+
getStats() {
|
|
741
|
+
const byStatus = {
|
|
742
|
+
pending: 0,
|
|
743
|
+
assigned: 0,
|
|
744
|
+
in_progress: 0,
|
|
745
|
+
completed: 0,
|
|
746
|
+
failed: 0,
|
|
747
|
+
blocked: 0,
|
|
748
|
+
cancelled: 0
|
|
749
|
+
};
|
|
750
|
+
const byPriority = {
|
|
751
|
+
critical: 0,
|
|
752
|
+
high: 0,
|
|
753
|
+
medium: 0,
|
|
754
|
+
low: 0
|
|
755
|
+
};
|
|
756
|
+
for (const task of this.tasks) {
|
|
757
|
+
byStatus[task.status]++;
|
|
758
|
+
byPriority[task.priority]++;
|
|
759
|
+
}
|
|
760
|
+
return {
|
|
761
|
+
total: this.tasks.length,
|
|
762
|
+
byStatus,
|
|
763
|
+
byPriority,
|
|
764
|
+
overdue: this.getOverdue().length,
|
|
765
|
+
retryable: this.getRetryable().length
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
// ============ Manipulation Methods ============
|
|
769
|
+
/**
|
|
770
|
+
* Sort the queue by priority
|
|
771
|
+
*/
|
|
772
|
+
sort() {
|
|
773
|
+
this.tasks.sort((a, b) => a.compareTo(b));
|
|
774
|
+
this.dirty = false;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Update blocked status for tasks based on completed dependencies
|
|
778
|
+
*/
|
|
779
|
+
updateBlockedTasks(completedTaskIds) {
|
|
780
|
+
let unblocked = 0;
|
|
781
|
+
for (const task of this.tasks) {
|
|
782
|
+
if (task.isBlocked && task.dependenciesSatisfied(completedTaskIds)) {
|
|
783
|
+
task.unblock();
|
|
784
|
+
unblocked++;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return unblocked;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Block tasks with unmet dependencies
|
|
791
|
+
*/
|
|
792
|
+
blockDependentTasks(failedTaskId) {
|
|
793
|
+
let blocked = 0;
|
|
794
|
+
for (const task of this.tasks) {
|
|
795
|
+
if (task.dependencies.includes(failedTaskId) && task.isPending) {
|
|
796
|
+
task.block(`Dependency ${failedTaskId} failed`);
|
|
797
|
+
blocked++;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return blocked;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Reprioritize a task
|
|
804
|
+
*/
|
|
805
|
+
reprioritize(taskId, _newPriority) {
|
|
806
|
+
const task = this.taskMap.get(taskId);
|
|
807
|
+
if (task) {
|
|
808
|
+
this.dirty = true;
|
|
809
|
+
return true;
|
|
810
|
+
}
|
|
811
|
+
return false;
|
|
812
|
+
}
|
|
813
|
+
// ============ Iterator ============
|
|
814
|
+
/**
|
|
815
|
+
* Iterate over tasks
|
|
816
|
+
*/
|
|
817
|
+
*[Symbol.iterator]() {
|
|
818
|
+
for (const task of this.tasks) {
|
|
819
|
+
yield task;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* For each task
|
|
824
|
+
*/
|
|
825
|
+
forEach(callback) {
|
|
826
|
+
this.tasks.forEach(callback);
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Filter tasks
|
|
830
|
+
*/
|
|
831
|
+
filter(predicate) {
|
|
832
|
+
return this.tasks.filter(predicate);
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Find a task
|
|
836
|
+
*/
|
|
837
|
+
find(predicate) {
|
|
838
|
+
return this.tasks.find(predicate);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
function createTaskQueue(config) {
|
|
842
|
+
return new TaskQueue(config);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// src/core/ExecutionContext.ts
|
|
846
|
+
import EventEmitter from "eventemitter3";
|
|
847
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
848
|
+
var ExecutionContext = class {
|
|
849
|
+
crewId;
|
|
850
|
+
crewName;
|
|
851
|
+
state;
|
|
852
|
+
completedTasks;
|
|
853
|
+
agentStates;
|
|
854
|
+
variables;
|
|
855
|
+
eventEmitter;
|
|
856
|
+
eventBuffer;
|
|
857
|
+
bufferEvents;
|
|
858
|
+
maxBufferSize;
|
|
859
|
+
abortController;
|
|
860
|
+
status;
|
|
861
|
+
startTime;
|
|
862
|
+
endTime;
|
|
863
|
+
constructor(config) {
|
|
864
|
+
this.crewId = config.crewId ?? nanoid2();
|
|
865
|
+
this.crewName = config.crewName;
|
|
866
|
+
this.state = /* @__PURE__ */ new Map();
|
|
867
|
+
this.completedTasks = /* @__PURE__ */ new Map();
|
|
868
|
+
this.agentStates = /* @__PURE__ */ new Map();
|
|
869
|
+
this.variables = {};
|
|
870
|
+
this.eventEmitter = new EventEmitter();
|
|
871
|
+
this.eventBuffer = [];
|
|
872
|
+
this.bufferEvents = config.bufferEvents ?? false;
|
|
873
|
+
this.maxBufferSize = config.maxBufferSize ?? 1e3;
|
|
874
|
+
this.abortController = new AbortController();
|
|
875
|
+
this.status = "idle";
|
|
876
|
+
if (config.globalContext) {
|
|
877
|
+
for (const [key, value] of Object.entries(config.globalContext)) {
|
|
878
|
+
this.state.set(key, value);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
// ============ State Management ============
|
|
883
|
+
/**
|
|
884
|
+
* Set a value in shared state
|
|
885
|
+
*/
|
|
886
|
+
set(key, value) {
|
|
887
|
+
this.state.set(key, value);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Get a value from shared state
|
|
891
|
+
*/
|
|
892
|
+
get(key) {
|
|
893
|
+
return this.state.get(key);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Check if a key exists in shared state
|
|
897
|
+
*/
|
|
898
|
+
has(key) {
|
|
899
|
+
return this.state.has(key);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Delete a key from shared state
|
|
903
|
+
*/
|
|
904
|
+
delete(key) {
|
|
905
|
+
return this.state.delete(key);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Get all state keys
|
|
909
|
+
*/
|
|
910
|
+
keys() {
|
|
911
|
+
return Array.from(this.state.keys());
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Get all state entries
|
|
915
|
+
*/
|
|
916
|
+
entries() {
|
|
917
|
+
return Array.from(this.state.entries());
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Clear all shared state
|
|
921
|
+
*/
|
|
922
|
+
clearState() {
|
|
923
|
+
this.state.clear();
|
|
924
|
+
}
|
|
925
|
+
// ============ Variables ============
|
|
926
|
+
/**
|
|
927
|
+
* Set a variable
|
|
928
|
+
*/
|
|
929
|
+
setVariable(name, value) {
|
|
930
|
+
this.variables[name] = value;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Get a variable
|
|
934
|
+
*/
|
|
935
|
+
getVariable(name) {
|
|
936
|
+
return this.variables[name];
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Get all variables
|
|
940
|
+
*/
|
|
941
|
+
getVariables() {
|
|
942
|
+
return { ...this.variables };
|
|
943
|
+
}
|
|
944
|
+
// ============ Task Tracking ============
|
|
945
|
+
/**
|
|
946
|
+
* Mark a task as completed
|
|
947
|
+
*/
|
|
948
|
+
markTaskCompleted(taskId, result) {
|
|
949
|
+
this.completedTasks.set(taskId, result);
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Check if a task is completed
|
|
953
|
+
*/
|
|
954
|
+
isTaskCompleted(taskId) {
|
|
955
|
+
return this.completedTasks.has(taskId);
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Get a completed task result
|
|
959
|
+
*/
|
|
960
|
+
getTaskResult(taskId) {
|
|
961
|
+
return this.completedTasks.get(taskId);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Get all completed tasks
|
|
965
|
+
*/
|
|
966
|
+
getCompletedTasks() {
|
|
967
|
+
return new Map(this.completedTasks);
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Get completed task IDs
|
|
971
|
+
*/
|
|
972
|
+
getCompletedTaskIds() {
|
|
973
|
+
return new Set(this.completedTasks.keys());
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Get completed task count
|
|
977
|
+
*/
|
|
978
|
+
getCompletedTaskCount() {
|
|
979
|
+
return this.completedTasks.size;
|
|
980
|
+
}
|
|
981
|
+
// ============ Agent State ============
|
|
982
|
+
/**
|
|
983
|
+
* Set agent state
|
|
984
|
+
*/
|
|
985
|
+
setAgentState(agentName, state) {
|
|
986
|
+
this.agentStates.set(agentName, state);
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Get agent state
|
|
990
|
+
*/
|
|
991
|
+
getAgentState(agentName) {
|
|
992
|
+
return this.agentStates.get(agentName);
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Get all agent states
|
|
996
|
+
*/
|
|
997
|
+
getAgentStates() {
|
|
998
|
+
return new Map(this.agentStates);
|
|
999
|
+
}
|
|
1000
|
+
// ============ Event System ============
|
|
1001
|
+
/**
|
|
1002
|
+
* Emit an event
|
|
1003
|
+
* Uses a permissive type that accepts any object with a `type` property
|
|
1004
|
+
* to avoid TypeScript's strict excess property checking on union types
|
|
1005
|
+
*/
|
|
1006
|
+
emit(event) {
|
|
1007
|
+
const fullEvent = {
|
|
1008
|
+
...event,
|
|
1009
|
+
crewName: this.crewName,
|
|
1010
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1011
|
+
};
|
|
1012
|
+
if (this.bufferEvents) {
|
|
1013
|
+
this.eventBuffer.push(fullEvent);
|
|
1014
|
+
if (this.eventBuffer.length > this.maxBufferSize) {
|
|
1015
|
+
this.eventBuffer.shift();
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
this.eventEmitter.emit(event.type, fullEvent);
|
|
1019
|
+
this.eventEmitter.emit("*", fullEvent);
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Subscribe to events of a specific type
|
|
1023
|
+
*/
|
|
1024
|
+
on(type, handler) {
|
|
1025
|
+
this.eventEmitter.on(type, handler);
|
|
1026
|
+
return {
|
|
1027
|
+
unsubscribe: () => this.eventEmitter.off(type, handler)
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Subscribe to events once
|
|
1032
|
+
*/
|
|
1033
|
+
once(type, handler) {
|
|
1034
|
+
this.eventEmitter.once(type, handler);
|
|
1035
|
+
return {
|
|
1036
|
+
unsubscribe: () => this.eventEmitter.off(type, handler)
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Remove all listeners for a type
|
|
1041
|
+
*/
|
|
1042
|
+
off(type) {
|
|
1043
|
+
this.eventEmitter.removeAllListeners(type);
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Get buffered events
|
|
1047
|
+
*/
|
|
1048
|
+
getEventBuffer() {
|
|
1049
|
+
return [...this.eventBuffer];
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Clear event buffer
|
|
1053
|
+
*/
|
|
1054
|
+
clearEventBuffer() {
|
|
1055
|
+
this.eventBuffer = [];
|
|
1056
|
+
}
|
|
1057
|
+
// ============ Lifecycle ============
|
|
1058
|
+
/**
|
|
1059
|
+
* Get abort signal
|
|
1060
|
+
*/
|
|
1061
|
+
get signal() {
|
|
1062
|
+
return this.abortController.signal;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Check if execution is aborted
|
|
1066
|
+
*/
|
|
1067
|
+
get isAborted() {
|
|
1068
|
+
return this.abortController.signal.aborted;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Abort execution
|
|
1072
|
+
*/
|
|
1073
|
+
abort(reason) {
|
|
1074
|
+
this.abortController.abort(reason);
|
|
1075
|
+
this.status = "aborted";
|
|
1076
|
+
this.endTime = /* @__PURE__ */ new Date();
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Get current status
|
|
1080
|
+
*/
|
|
1081
|
+
getStatus() {
|
|
1082
|
+
return this.status;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Set status
|
|
1086
|
+
*/
|
|
1087
|
+
setStatus(status) {
|
|
1088
|
+
this.status = status;
|
|
1089
|
+
if (status === "running" && !this.startTime) {
|
|
1090
|
+
this.startTime = /* @__PURE__ */ new Date();
|
|
1091
|
+
}
|
|
1092
|
+
if (status === "completed" || status === "failed" || status === "aborted") {
|
|
1093
|
+
this.endTime = /* @__PURE__ */ new Date();
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Get execution duration in milliseconds
|
|
1098
|
+
*/
|
|
1099
|
+
getDuration() {
|
|
1100
|
+
if (!this.startTime) return void 0;
|
|
1101
|
+
const end = this.endTime ?? /* @__PURE__ */ new Date();
|
|
1102
|
+
return end.getTime() - this.startTime.getTime();
|
|
1103
|
+
}
|
|
1104
|
+
// ============ Checkpointing ============
|
|
1105
|
+
/**
|
|
1106
|
+
* Create a checkpoint of current state
|
|
1107
|
+
*/
|
|
1108
|
+
createCheckpoint() {
|
|
1109
|
+
return {
|
|
1110
|
+
id: nanoid2(),
|
|
1111
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1112
|
+
crewId: this.crewId,
|
|
1113
|
+
crewName: this.crewName,
|
|
1114
|
+
state: new Map(this.state),
|
|
1115
|
+
completedTasks: new Map(this.completedTasks),
|
|
1116
|
+
agentStates: new Map(this.agentStates),
|
|
1117
|
+
variables: { ...this.variables }
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Restore from a checkpoint
|
|
1122
|
+
*/
|
|
1123
|
+
restoreCheckpoint(checkpoint) {
|
|
1124
|
+
this.state = new Map(checkpoint.state);
|
|
1125
|
+
this.completedTasks = new Map(checkpoint.completedTasks);
|
|
1126
|
+
this.agentStates = new Map(checkpoint.agentStates);
|
|
1127
|
+
this.variables = { ...checkpoint.variables };
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Export state for serialization
|
|
1131
|
+
*/
|
|
1132
|
+
exportState() {
|
|
1133
|
+
return {
|
|
1134
|
+
crewId: this.crewId,
|
|
1135
|
+
crewName: this.crewName,
|
|
1136
|
+
status: this.status,
|
|
1137
|
+
state: Object.fromEntries(this.state),
|
|
1138
|
+
completedTasks: Object.fromEntries(
|
|
1139
|
+
Array.from(this.completedTasks.entries())
|
|
1140
|
+
),
|
|
1141
|
+
agentStates: Object.fromEntries(this.agentStates),
|
|
1142
|
+
variables: this.variables,
|
|
1143
|
+
startTime: this.startTime?.toISOString(),
|
|
1144
|
+
endTime: this.endTime?.toISOString()
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Import state from serialized data
|
|
1149
|
+
*/
|
|
1150
|
+
importState(data) {
|
|
1151
|
+
if (data.state && typeof data.state === "object") {
|
|
1152
|
+
this.state = new Map(
|
|
1153
|
+
Object.entries(data.state)
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
if (data.completedTasks && typeof data.completedTasks === "object") {
|
|
1157
|
+
this.completedTasks = new Map(
|
|
1158
|
+
Object.entries(data.completedTasks)
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
if (data.agentStates && typeof data.agentStates === "object") {
|
|
1162
|
+
this.agentStates = new Map(
|
|
1163
|
+
Object.entries(data.agentStates)
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
if (data.variables && typeof data.variables === "object") {
|
|
1167
|
+
this.variables = data.variables;
|
|
1168
|
+
}
|
|
1169
|
+
if (data.status) {
|
|
1170
|
+
this.status = data.status;
|
|
1171
|
+
}
|
|
1172
|
+
if (data.startTime) {
|
|
1173
|
+
this.startTime = new Date(data.startTime);
|
|
1174
|
+
}
|
|
1175
|
+
if (data.endTime) {
|
|
1176
|
+
this.endTime = new Date(data.endTime);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
// ============ Reset ============
|
|
1180
|
+
/**
|
|
1181
|
+
* Reset context for new execution
|
|
1182
|
+
*/
|
|
1183
|
+
reset() {
|
|
1184
|
+
this.state.clear();
|
|
1185
|
+
this.completedTasks.clear();
|
|
1186
|
+
this.agentStates.clear();
|
|
1187
|
+
this.variables = {};
|
|
1188
|
+
this.eventBuffer = [];
|
|
1189
|
+
this.abortController = new AbortController();
|
|
1190
|
+
this.status = "idle";
|
|
1191
|
+
this.startTime = void 0;
|
|
1192
|
+
this.endTime = void 0;
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
function createExecutionContext(config) {
|
|
1196
|
+
return new ExecutionContext(config);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/agents/AgentCapabilities.ts
|
|
1200
|
+
var PROFICIENCY_WEIGHTS = {
|
|
1201
|
+
novice: 0.25,
|
|
1202
|
+
intermediate: 0.5,
|
|
1203
|
+
expert: 0.75,
|
|
1204
|
+
master: 1
|
|
1205
|
+
};
|
|
1206
|
+
var AgentCapabilities = class {
|
|
1207
|
+
/**
|
|
1208
|
+
* Match required capabilities against available ones
|
|
1209
|
+
*/
|
|
1210
|
+
static match(required, available) {
|
|
1211
|
+
const availableMap = /* @__PURE__ */ new Map();
|
|
1212
|
+
for (const cap of available) {
|
|
1213
|
+
availableMap.set(cap.name.toLowerCase(), cap);
|
|
1214
|
+
}
|
|
1215
|
+
const matched = [];
|
|
1216
|
+
const missing = [];
|
|
1217
|
+
for (const req of required) {
|
|
1218
|
+
const avail = availableMap.get(req.name.toLowerCase());
|
|
1219
|
+
if (avail) {
|
|
1220
|
+
matched.push(avail);
|
|
1221
|
+
} else {
|
|
1222
|
+
missing.push(req);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const score = required.length > 0 ? matched.length / required.length : 1;
|
|
1226
|
+
const canExecute = missing.length === 0;
|
|
1227
|
+
return {
|
|
1228
|
+
matched,
|
|
1229
|
+
missing,
|
|
1230
|
+
score,
|
|
1231
|
+
canExecute
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Score a capability against a task
|
|
1236
|
+
*/
|
|
1237
|
+
static score(capability, task) {
|
|
1238
|
+
let score = PROFICIENCY_WEIGHTS[capability.proficiency];
|
|
1239
|
+
if (capability.keywords && capability.keywords.length > 0) {
|
|
1240
|
+
const descLower = task.description.toLowerCase();
|
|
1241
|
+
let keywordMatches = 0;
|
|
1242
|
+
for (const keyword of capability.keywords) {
|
|
1243
|
+
if (descLower.includes(keyword.toLowerCase())) {
|
|
1244
|
+
keywordMatches++;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
const keywordBoost = keywordMatches / capability.keywords.length;
|
|
1248
|
+
score = score * 0.7 + keywordBoost * 0.3;
|
|
1249
|
+
}
|
|
1250
|
+
return score;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Calculate overall capability score for an agent on a task
|
|
1254
|
+
*/
|
|
1255
|
+
static calculateAgentScore(agent, task) {
|
|
1256
|
+
if (agent.capabilities.length === 0) {
|
|
1257
|
+
return 0;
|
|
1258
|
+
}
|
|
1259
|
+
if (task.requiredCapabilities && task.requiredCapabilities.length > 0) {
|
|
1260
|
+
const requiredCaps = task.requiredCapabilities.map((name) => ({
|
|
1261
|
+
name,
|
|
1262
|
+
description: "",
|
|
1263
|
+
proficiency: "intermediate"
|
|
1264
|
+
}));
|
|
1265
|
+
const match = this.match(requiredCaps, agent.capabilities);
|
|
1266
|
+
if (!match.canExecute) {
|
|
1267
|
+
return 0;
|
|
1268
|
+
}
|
|
1269
|
+
let totalScore = 0;
|
|
1270
|
+
for (const cap of match.matched) {
|
|
1271
|
+
totalScore += PROFICIENCY_WEIGHTS[cap.proficiency];
|
|
1272
|
+
}
|
|
1273
|
+
return totalScore / task.requiredCapabilities.length;
|
|
1274
|
+
}
|
|
1275
|
+
let maxScore = 0;
|
|
1276
|
+
for (const cap of agent.capabilities) {
|
|
1277
|
+
const capScore = this.score(cap, task);
|
|
1278
|
+
maxScore = Math.max(maxScore, capScore);
|
|
1279
|
+
}
|
|
1280
|
+
return maxScore;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Rank agents by their suitability for a task
|
|
1284
|
+
*/
|
|
1285
|
+
static rank(agents, task) {
|
|
1286
|
+
const ranked = [];
|
|
1287
|
+
for (const agent of agents) {
|
|
1288
|
+
const requiredCaps = (task.requiredCapabilities ?? []).map((name) => ({
|
|
1289
|
+
name,
|
|
1290
|
+
description: "",
|
|
1291
|
+
proficiency: "intermediate"
|
|
1292
|
+
}));
|
|
1293
|
+
const match = this.match(requiredCaps, agent.capabilities);
|
|
1294
|
+
const score = this.calculateAgentScore(agent, task);
|
|
1295
|
+
ranked.push({
|
|
1296
|
+
agentName: agent.name,
|
|
1297
|
+
score,
|
|
1298
|
+
matchedCapabilities: match.matched,
|
|
1299
|
+
missingCapabilities: match.missing
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
ranked.sort((a, b) => b.score - a.score);
|
|
1303
|
+
return ranked;
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Find the best matching agent for a task
|
|
1307
|
+
*/
|
|
1308
|
+
static findBestMatch(agents, task) {
|
|
1309
|
+
const ranked = this.rank(agents, task);
|
|
1310
|
+
if (ranked.length === 0) return void 0;
|
|
1311
|
+
const best = ranked[0];
|
|
1312
|
+
if (best.score > 0 && best.missingCapabilities.length === 0) {
|
|
1313
|
+
return best;
|
|
1314
|
+
}
|
|
1315
|
+
return ranked.find((r) => r.missingCapabilities.length === 0);
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Find all agents that can handle a task
|
|
1319
|
+
*/
|
|
1320
|
+
static findCapableAgents(agents, task) {
|
|
1321
|
+
return this.rank(agents, task).filter(
|
|
1322
|
+
(r) => r.missingCapabilities.length === 0
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Check if any agent can handle a task
|
|
1327
|
+
*/
|
|
1328
|
+
static canAnyHandle(agents, task) {
|
|
1329
|
+
return this.findCapableAgents(agents, task).length > 0;
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Calculate capability overlap between two agents
|
|
1333
|
+
*/
|
|
1334
|
+
static calculateOverlap(agent1, agent2) {
|
|
1335
|
+
const caps1 = new Set(agent1.capabilities.map((c) => c.name.toLowerCase()));
|
|
1336
|
+
const caps2 = new Set(agent2.capabilities.map((c) => c.name.toLowerCase()));
|
|
1337
|
+
let overlap = 0;
|
|
1338
|
+
for (const cap of caps1) {
|
|
1339
|
+
if (caps2.has(cap)) {
|
|
1340
|
+
overlap++;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
const totalUnique = caps1.size + caps2.size - overlap;
|
|
1344
|
+
return totalUnique > 0 ? overlap / totalUnique : 0;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Get all unique capabilities across agents
|
|
1348
|
+
*/
|
|
1349
|
+
static getAllCapabilities(agents) {
|
|
1350
|
+
const capMap = /* @__PURE__ */ new Map();
|
|
1351
|
+
for (const agent of agents) {
|
|
1352
|
+
for (const cap of agent.capabilities) {
|
|
1353
|
+
const key = cap.name.toLowerCase();
|
|
1354
|
+
const existing = capMap.get(key);
|
|
1355
|
+
if (!existing || PROFICIENCY_WEIGHTS[cap.proficiency] > PROFICIENCY_WEIGHTS[existing.proficiency]) {
|
|
1356
|
+
capMap.set(key, cap);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return Array.from(capMap.values());
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Get capabilities that only one agent has
|
|
1364
|
+
*/
|
|
1365
|
+
static getUniqueCapabilities(agents) {
|
|
1366
|
+
const capCounts = /* @__PURE__ */ new Map();
|
|
1367
|
+
for (const agent of agents) {
|
|
1368
|
+
for (const cap of agent.capabilities) {
|
|
1369
|
+
const key = cap.name.toLowerCase();
|
|
1370
|
+
const existing = capCounts.get(key);
|
|
1371
|
+
if (existing) {
|
|
1372
|
+
existing.agents.push(agent.name);
|
|
1373
|
+
} else {
|
|
1374
|
+
capCounts.set(key, { cap, agents: [agent.name] });
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
const unique = /* @__PURE__ */ new Map();
|
|
1379
|
+
for (const [_key, { cap, agents: agentNames }] of capCounts) {
|
|
1380
|
+
if (agentNames.length === 1) {
|
|
1381
|
+
const existing = unique.get(agentNames[0]) ?? [];
|
|
1382
|
+
existing.push(cap);
|
|
1383
|
+
unique.set(agentNames[0], existing);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return unique;
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
|
|
1390
|
+
// src/agents/CrewAgent.ts
|
|
1391
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
1392
|
+
var CrewAgent = class _CrewAgent {
|
|
1393
|
+
id;
|
|
1394
|
+
name;
|
|
1395
|
+
role;
|
|
1396
|
+
capabilities;
|
|
1397
|
+
model;
|
|
1398
|
+
provider;
|
|
1399
|
+
tools;
|
|
1400
|
+
temperature;
|
|
1401
|
+
maxTokens;
|
|
1402
|
+
maxIterations;
|
|
1403
|
+
parallelCapable;
|
|
1404
|
+
executeFunc;
|
|
1405
|
+
currentTaskId;
|
|
1406
|
+
tasksCompleted = 0;
|
|
1407
|
+
tasksFailed = 0;
|
|
1408
|
+
totalTokensUsed = 0;
|
|
1409
|
+
constructor(options) {
|
|
1410
|
+
const { config, execute } = options;
|
|
1411
|
+
this.id = nanoid3();
|
|
1412
|
+
this.name = config.name;
|
|
1413
|
+
this.role = new Role(config.role);
|
|
1414
|
+
this.capabilities = config.role.capabilities;
|
|
1415
|
+
this.model = config.model;
|
|
1416
|
+
this.provider = config.provider;
|
|
1417
|
+
this.tools = config.tools ?? this.role.getRequiredTools();
|
|
1418
|
+
this.temperature = config.temperature ?? 0.7;
|
|
1419
|
+
this.maxTokens = config.maxTokens;
|
|
1420
|
+
this.maxIterations = config.maxIterations ?? 10;
|
|
1421
|
+
this.parallelCapable = config.parallelCapable ?? false;
|
|
1422
|
+
this.executeFunc = execute;
|
|
1423
|
+
}
|
|
1424
|
+
// ============ Execution ============
|
|
1425
|
+
/**
|
|
1426
|
+
* Execute a task
|
|
1427
|
+
*/
|
|
1428
|
+
async execute(input) {
|
|
1429
|
+
if (!this.executeFunc) {
|
|
1430
|
+
const mockResult = {
|
|
1431
|
+
output: `[Mock response from ${this.name}]: ${input.slice(0, 100)}...`,
|
|
1432
|
+
tokensUsed: Math.floor(Math.random() * 500) + 100,
|
|
1433
|
+
latencyMs: Math.floor(Math.random() * 2e3) + 500,
|
|
1434
|
+
iterations: 1
|
|
1435
|
+
};
|
|
1436
|
+
this.totalTokensUsed += mockResult.tokensUsed;
|
|
1437
|
+
return mockResult;
|
|
1438
|
+
}
|
|
1439
|
+
const systemPrompt = this.role.generateSystemPrompt();
|
|
1440
|
+
const result = await this.executeFunc(input, systemPrompt);
|
|
1441
|
+
this.totalTokensUsed += result.tokensUsed;
|
|
1442
|
+
return result;
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Execute with task context
|
|
1446
|
+
*/
|
|
1447
|
+
async executeTask(task) {
|
|
1448
|
+
this.currentTaskId = task.id;
|
|
1449
|
+
try {
|
|
1450
|
+
const input = this.formatTaskInput(task);
|
|
1451
|
+
const result = await this.execute(input);
|
|
1452
|
+
this.tasksCompleted++;
|
|
1453
|
+
return {
|
|
1454
|
+
output: result.output,
|
|
1455
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
1456
|
+
completedBy: this.name,
|
|
1457
|
+
iterations: result.iterations,
|
|
1458
|
+
tokensUsed: result.tokensUsed,
|
|
1459
|
+
metadata: {
|
|
1460
|
+
latencyMs: result.latencyMs,
|
|
1461
|
+
toolCalls: result.toolCalls
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
this.tasksFailed++;
|
|
1466
|
+
throw error;
|
|
1467
|
+
} finally {
|
|
1468
|
+
this.currentTaskId = void 0;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Format task input for the agent
|
|
1473
|
+
*/
|
|
1474
|
+
formatTaskInput(task) {
|
|
1475
|
+
const parts = [];
|
|
1476
|
+
parts.push(`# Task: ${task.description}`);
|
|
1477
|
+
parts.push(`
|
|
1478
|
+
## Expected Output:
|
|
1479
|
+
${task.expectedOutput}`);
|
|
1480
|
+
if (task.context && Object.keys(task.context).length > 0) {
|
|
1481
|
+
parts.push(`
|
|
1482
|
+
## Context:
|
|
1483
|
+
${JSON.stringify(task.context, null, 2)}`);
|
|
1484
|
+
}
|
|
1485
|
+
if (task.deadline) {
|
|
1486
|
+
parts.push(`
|
|
1487
|
+
## Deadline: ${task.deadline.toISOString()}`);
|
|
1488
|
+
}
|
|
1489
|
+
return parts.join("\n");
|
|
1490
|
+
}
|
|
1491
|
+
// ============ Capability Matching ============
|
|
1492
|
+
/**
|
|
1493
|
+
* Check if agent has a specific capability
|
|
1494
|
+
*/
|
|
1495
|
+
hasCapability(name) {
|
|
1496
|
+
return this.role.hasCapability(name);
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Get proficiency score for a capability
|
|
1500
|
+
*/
|
|
1501
|
+
getProficiencyScore(name) {
|
|
1502
|
+
return this.role.getProficiencyScore(name);
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Match required capabilities
|
|
1506
|
+
*/
|
|
1507
|
+
matchesCapabilities(required) {
|
|
1508
|
+
return AgentCapabilities.match(required, this.capabilities);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Calculate suitability score for a task
|
|
1512
|
+
*/
|
|
1513
|
+
calculateTaskScore(task) {
|
|
1514
|
+
return AgentCapabilities.calculateAgentScore(this, task);
|
|
1515
|
+
}
|
|
1516
|
+
// ============ Bidding ============
|
|
1517
|
+
/**
|
|
1518
|
+
* Generate a bid for a task
|
|
1519
|
+
*/
|
|
1520
|
+
bidOnTask(task) {
|
|
1521
|
+
const score = this.calculateTaskScore(task);
|
|
1522
|
+
const hasRequired = this.hasRequiredCapabilities(task);
|
|
1523
|
+
let confidence = score;
|
|
1524
|
+
if (!hasRequired) {
|
|
1525
|
+
confidence *= 0.5;
|
|
1526
|
+
}
|
|
1527
|
+
if (this.currentTaskId) {
|
|
1528
|
+
confidence *= 0.7;
|
|
1529
|
+
}
|
|
1530
|
+
const matchedCaps = this.capabilities.filter(
|
|
1531
|
+
(c) => task.requiredCapabilities?.some(
|
|
1532
|
+
(req) => req.toLowerCase() === c.name.toLowerCase()
|
|
1533
|
+
) ?? true
|
|
1534
|
+
).map((c) => c.name);
|
|
1535
|
+
const estimatedTime = this.estimateTaskTime(task);
|
|
1536
|
+
const reasoning = this.generateBidReasoning(task, confidence, matchedCaps);
|
|
1537
|
+
return Promise.resolve({
|
|
1538
|
+
agentName: this.name,
|
|
1539
|
+
taskId: task.id ?? "unknown",
|
|
1540
|
+
confidence,
|
|
1541
|
+
estimatedTime,
|
|
1542
|
+
reasoning,
|
|
1543
|
+
capabilities: matchedCaps
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Check if agent has all required capabilities
|
|
1548
|
+
*/
|
|
1549
|
+
hasRequiredCapabilities(task) {
|
|
1550
|
+
if (!task.requiredCapabilities || task.requiredCapabilities.length === 0) {
|
|
1551
|
+
return true;
|
|
1552
|
+
}
|
|
1553
|
+
for (const required of task.requiredCapabilities) {
|
|
1554
|
+
if (!this.hasCapability(required)) {
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
return true;
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Estimate time to complete a task
|
|
1562
|
+
*/
|
|
1563
|
+
estimateTaskTime(task) {
|
|
1564
|
+
let estimate = 3e4;
|
|
1565
|
+
const outputWords = task.expectedOutput.split(" ").length;
|
|
1566
|
+
estimate += outputWords * 100;
|
|
1567
|
+
estimate += (task.requiredCapabilities?.length ?? 0) * 5e3;
|
|
1568
|
+
return estimate;
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Generate reasoning for a bid
|
|
1572
|
+
*/
|
|
1573
|
+
generateBidReasoning(task, confidence, matchedCaps) {
|
|
1574
|
+
const parts = [];
|
|
1575
|
+
if (matchedCaps.length > 0) {
|
|
1576
|
+
parts.push(`I have relevant capabilities: ${matchedCaps.join(", ")}`);
|
|
1577
|
+
}
|
|
1578
|
+
if (confidence > 0.8) {
|
|
1579
|
+
parts.push("I am highly suited for this task");
|
|
1580
|
+
} else if (confidence > 0.5) {
|
|
1581
|
+
parts.push("I can handle this task adequately");
|
|
1582
|
+
} else {
|
|
1583
|
+
parts.push("This task is outside my primary expertise");
|
|
1584
|
+
}
|
|
1585
|
+
if (this.currentTaskId) {
|
|
1586
|
+
parts.push("Note: I am currently working on another task");
|
|
1587
|
+
}
|
|
1588
|
+
return parts.join(". ") + ".";
|
|
1589
|
+
}
|
|
1590
|
+
// ============ Collaboration ============
|
|
1591
|
+
/**
|
|
1592
|
+
* Create a help request
|
|
1593
|
+
*/
|
|
1594
|
+
createHelpRequest(taskId, request) {
|
|
1595
|
+
return {
|
|
1596
|
+
requestId: nanoid3(),
|
|
1597
|
+
fromAgent: this.name,
|
|
1598
|
+
taskId,
|
|
1599
|
+
request
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Respond to a help request
|
|
1604
|
+
*/
|
|
1605
|
+
async respondToHelpRequest(request) {
|
|
1606
|
+
const input = `
|
|
1607
|
+
Another agent needs help with their task.
|
|
1608
|
+
|
|
1609
|
+
Their request: ${request.request}
|
|
1610
|
+
|
|
1611
|
+
Please provide a helpful response based on your expertise as a ${this.role.name}.
|
|
1612
|
+
`;
|
|
1613
|
+
const result = await this.execute(input);
|
|
1614
|
+
return {
|
|
1615
|
+
requestId: request.requestId,
|
|
1616
|
+
fromAgent: this.name,
|
|
1617
|
+
response: result.output,
|
|
1618
|
+
helpful: true
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Provide help for a task (called by CollaborationManager)
|
|
1623
|
+
*/
|
|
1624
|
+
async provideHelp(task, question, _context) {
|
|
1625
|
+
const score = this.calculateTaskScore(task);
|
|
1626
|
+
const canHelp = score > 0.3;
|
|
1627
|
+
if (!canHelp) {
|
|
1628
|
+
return {
|
|
1629
|
+
helpful: false,
|
|
1630
|
+
response: `I may not be the best agent to help with this. My expertise in ${this.role.name} might not be directly applicable.`
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
const input = `
|
|
1634
|
+
# Help Request
|
|
1635
|
+
|
|
1636
|
+
## Task Context:
|
|
1637
|
+
${task.description}
|
|
1638
|
+
|
|
1639
|
+
## Question:
|
|
1640
|
+
${question}
|
|
1641
|
+
|
|
1642
|
+
## Your Role:
|
|
1643
|
+
You are a ${this.role.name}. ${this.role.description}
|
|
1644
|
+
|
|
1645
|
+
Please provide helpful guidance based on your expertise.
|
|
1646
|
+
`;
|
|
1647
|
+
try {
|
|
1648
|
+
const result = await this.execute(input);
|
|
1649
|
+
const suggestions = this.extractSuggestions(result.output);
|
|
1650
|
+
return {
|
|
1651
|
+
helpful: true,
|
|
1652
|
+
response: result.output,
|
|
1653
|
+
suggestions: suggestions.length > 0 ? suggestions : void 0
|
|
1654
|
+
};
|
|
1655
|
+
} catch (error) {
|
|
1656
|
+
return {
|
|
1657
|
+
helpful: false,
|
|
1658
|
+
response: `I encountered an error while trying to help: ${error instanceof Error ? error.message : String(error)}`
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Extract suggestions from help response
|
|
1664
|
+
*/
|
|
1665
|
+
extractSuggestions(response) {
|
|
1666
|
+
const suggestions = [];
|
|
1667
|
+
const numberPattern = /^\d+\.\s+(.+)$/gm;
|
|
1668
|
+
let match;
|
|
1669
|
+
while ((match = numberPattern.exec(response)) !== null) {
|
|
1670
|
+
suggestions.push(match[1].trim());
|
|
1671
|
+
}
|
|
1672
|
+
const bulletPattern = /^[-*]\s+(.+)$/gm;
|
|
1673
|
+
while ((match = bulletPattern.exec(response)) !== null) {
|
|
1674
|
+
suggestions.push(match[1].trim());
|
|
1675
|
+
}
|
|
1676
|
+
return suggestions.slice(0, 5);
|
|
1677
|
+
}
|
|
1678
|
+
// ============ State ============
|
|
1679
|
+
/**
|
|
1680
|
+
* Check if agent is busy
|
|
1681
|
+
*/
|
|
1682
|
+
get isBusy() {
|
|
1683
|
+
return this.currentTaskId !== void 0;
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Get current task ID
|
|
1687
|
+
*/
|
|
1688
|
+
getCurrentTask() {
|
|
1689
|
+
return this.currentTaskId;
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Get agent statistics
|
|
1693
|
+
*/
|
|
1694
|
+
getStats() {
|
|
1695
|
+
return {
|
|
1696
|
+
name: this.name,
|
|
1697
|
+
role: this.role.name,
|
|
1698
|
+
tasksCompleted: this.tasksCompleted,
|
|
1699
|
+
tasksFailed: this.tasksFailed,
|
|
1700
|
+
totalTokensUsed: this.totalTokensUsed,
|
|
1701
|
+
successRate: this.tasksCompleted + this.tasksFailed > 0 ? this.tasksCompleted / (this.tasksCompleted + this.tasksFailed) : 0,
|
|
1702
|
+
isBusy: this.isBusy,
|
|
1703
|
+
currentTask: this.currentTaskId
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
// ============ Serialization ============
|
|
1707
|
+
/**
|
|
1708
|
+
* Convert to config
|
|
1709
|
+
*/
|
|
1710
|
+
toConfig() {
|
|
1711
|
+
return {
|
|
1712
|
+
name: this.name,
|
|
1713
|
+
role: this.role.toJSON(),
|
|
1714
|
+
model: this.model,
|
|
1715
|
+
provider: this.provider,
|
|
1716
|
+
tools: this.tools.length > 0 ? this.tools : void 0,
|
|
1717
|
+
temperature: this.temperature,
|
|
1718
|
+
maxTokens: this.maxTokens,
|
|
1719
|
+
maxIterations: this.maxIterations,
|
|
1720
|
+
parallelCapable: this.parallelCapable
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Create from config
|
|
1725
|
+
*/
|
|
1726
|
+
static fromConfig(config) {
|
|
1727
|
+
return new _CrewAgent({ config });
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1730
|
+
function createCrewAgent(options) {
|
|
1731
|
+
return new CrewAgent(options);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// src/agents/AgentRegistry.ts
|
|
1735
|
+
var AgentRegistry = class {
|
|
1736
|
+
agents = /* @__PURE__ */ new Map();
|
|
1737
|
+
allowDuplicates;
|
|
1738
|
+
trackStats;
|
|
1739
|
+
constructor(config = {}) {
|
|
1740
|
+
this.allowDuplicates = config.allowDuplicates ?? false;
|
|
1741
|
+
this.trackStats = config.trackStats ?? true;
|
|
1742
|
+
}
|
|
1743
|
+
// ============ Registration ============
|
|
1744
|
+
/**
|
|
1745
|
+
* Register an agent
|
|
1746
|
+
*/
|
|
1747
|
+
register(agent, metadata) {
|
|
1748
|
+
if (this.agents.has(agent.name) && !this.allowDuplicates) {
|
|
1749
|
+
throw new Error(`Agent "${agent.name}" is already registered`);
|
|
1750
|
+
}
|
|
1751
|
+
this.agents.set(agent.name, {
|
|
1752
|
+
agent,
|
|
1753
|
+
status: "available",
|
|
1754
|
+
registeredAt: /* @__PURE__ */ new Date(),
|
|
1755
|
+
tasksCompleted: 0,
|
|
1756
|
+
tasksFailed: 0,
|
|
1757
|
+
metadata
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Register multiple agents
|
|
1762
|
+
*/
|
|
1763
|
+
registerMany(agents) {
|
|
1764
|
+
for (const agent of agents) {
|
|
1765
|
+
this.register(agent);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Unregister an agent
|
|
1770
|
+
*/
|
|
1771
|
+
unregister(name) {
|
|
1772
|
+
return this.agents.delete(name);
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Check if an agent is registered
|
|
1776
|
+
*/
|
|
1777
|
+
has(name) {
|
|
1778
|
+
return this.agents.has(name);
|
|
1779
|
+
}
|
|
1780
|
+
// ============ Retrieval ============
|
|
1781
|
+
/**
|
|
1782
|
+
* Get an agent by name
|
|
1783
|
+
*/
|
|
1784
|
+
get(name) {
|
|
1785
|
+
return this.agents.get(name)?.agent;
|
|
1786
|
+
}
|
|
1787
|
+
/**
|
|
1788
|
+
* Get registered agent entry
|
|
1789
|
+
*/
|
|
1790
|
+
getEntry(name) {
|
|
1791
|
+
return this.agents.get(name);
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Get all agents
|
|
1795
|
+
*/
|
|
1796
|
+
getAll() {
|
|
1797
|
+
return Array.from(this.agents.values()).map((entry) => entry.agent);
|
|
1798
|
+
}
|
|
1799
|
+
/**
|
|
1800
|
+
* Get all registered entries
|
|
1801
|
+
*/
|
|
1802
|
+
getAllEntries() {
|
|
1803
|
+
return Array.from(this.agents.values());
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Get agent names
|
|
1807
|
+
*/
|
|
1808
|
+
getNames() {
|
|
1809
|
+
return Array.from(this.agents.keys());
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* Get agent count
|
|
1813
|
+
*/
|
|
1814
|
+
get size() {
|
|
1815
|
+
return this.agents.size;
|
|
1816
|
+
}
|
|
1817
|
+
// ============ Status Management ============
|
|
1818
|
+
/**
|
|
1819
|
+
* Get agent status
|
|
1820
|
+
*/
|
|
1821
|
+
getStatus(name) {
|
|
1822
|
+
return this.agents.get(name)?.status;
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* Set agent status
|
|
1826
|
+
*/
|
|
1827
|
+
setStatus(name, status) {
|
|
1828
|
+
const entry = this.agents.get(name);
|
|
1829
|
+
if (entry) {
|
|
1830
|
+
entry.status = status;
|
|
1831
|
+
entry.lastActiveAt = /* @__PURE__ */ new Date();
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Mark agent as busy with a task
|
|
1836
|
+
*/
|
|
1837
|
+
markBusy(name, taskId) {
|
|
1838
|
+
const entry = this.agents.get(name);
|
|
1839
|
+
if (entry) {
|
|
1840
|
+
entry.status = "busy";
|
|
1841
|
+
entry.currentTask = taskId;
|
|
1842
|
+
entry.lastActiveAt = /* @__PURE__ */ new Date();
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Mark agent as available
|
|
1847
|
+
*/
|
|
1848
|
+
markAvailable(name) {
|
|
1849
|
+
const entry = this.agents.get(name);
|
|
1850
|
+
if (entry) {
|
|
1851
|
+
entry.status = "available";
|
|
1852
|
+
entry.currentTask = void 0;
|
|
1853
|
+
entry.lastActiveAt = /* @__PURE__ */ new Date();
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Record task completion
|
|
1858
|
+
*/
|
|
1859
|
+
recordTaskCompletion(name, success) {
|
|
1860
|
+
const entry = this.agents.get(name);
|
|
1861
|
+
if (entry && this.trackStats) {
|
|
1862
|
+
if (success) {
|
|
1863
|
+
entry.tasksCompleted++;
|
|
1864
|
+
} else {
|
|
1865
|
+
entry.tasksFailed++;
|
|
1866
|
+
}
|
|
1867
|
+
entry.currentTask = void 0;
|
|
1868
|
+
entry.status = "available";
|
|
1869
|
+
entry.lastActiveAt = /* @__PURE__ */ new Date();
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
// ============ Discovery ============
|
|
1873
|
+
/**
|
|
1874
|
+
* Find agents by capability
|
|
1875
|
+
*/
|
|
1876
|
+
findByCapability(capabilityName) {
|
|
1877
|
+
const results = [];
|
|
1878
|
+
for (const entry of this.agents.values()) {
|
|
1879
|
+
const hasCapability = entry.agent.capabilities.some(
|
|
1880
|
+
(c) => c.name.toLowerCase() === capabilityName.toLowerCase()
|
|
1881
|
+
);
|
|
1882
|
+
if (hasCapability) {
|
|
1883
|
+
results.push(entry.agent);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
return results;
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Find agents by role name
|
|
1890
|
+
*/
|
|
1891
|
+
findByRole(roleName) {
|
|
1892
|
+
const results = [];
|
|
1893
|
+
for (const entry of this.agents.values()) {
|
|
1894
|
+
const agent = entry.agent;
|
|
1895
|
+
if (agent.role?.name?.toLowerCase() === roleName.toLowerCase()) {
|
|
1896
|
+
results.push(entry.agent);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
return results;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Find the best matching agent for a task
|
|
1903
|
+
*/
|
|
1904
|
+
findBestMatch(task) {
|
|
1905
|
+
const available = this.getAvailable();
|
|
1906
|
+
if (available.length === 0) return void 0;
|
|
1907
|
+
const result = AgentCapabilities.findBestMatch(available, task);
|
|
1908
|
+
return result ? this.get(result.agentName) : void 0;
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* Rank agents for a task
|
|
1912
|
+
*/
|
|
1913
|
+
rankForTask(task) {
|
|
1914
|
+
return AgentCapabilities.rank(this.getAll(), task);
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Find agents that can handle a task
|
|
1918
|
+
*/
|
|
1919
|
+
findCapableAgents(task) {
|
|
1920
|
+
const ranked = AgentCapabilities.findCapableAgents(this.getAll(), task);
|
|
1921
|
+
return ranked.map((r) => this.get(r.agentName)).filter(Boolean);
|
|
1922
|
+
}
|
|
1923
|
+
// ============ Filtering ============
|
|
1924
|
+
/**
|
|
1925
|
+
* Get available agents
|
|
1926
|
+
*/
|
|
1927
|
+
getAvailable() {
|
|
1928
|
+
return Array.from(this.agents.values()).filter((entry) => entry.status === "available").map((entry) => entry.agent);
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Get busy agents
|
|
1932
|
+
*/
|
|
1933
|
+
getBusy() {
|
|
1934
|
+
return Array.from(this.agents.values()).filter((entry) => entry.status === "busy").map((entry) => entry.agent);
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Get agents by status
|
|
1938
|
+
*/
|
|
1939
|
+
getByStatus(status) {
|
|
1940
|
+
return Array.from(this.agents.values()).filter((entry) => entry.status === status).map((entry) => entry.agent);
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Get available count
|
|
1944
|
+
*/
|
|
1945
|
+
getAvailableCount() {
|
|
1946
|
+
let count = 0;
|
|
1947
|
+
for (const entry of this.agents.values()) {
|
|
1948
|
+
if (entry.status === "available") count++;
|
|
1949
|
+
}
|
|
1950
|
+
return count;
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Check if any agent is available
|
|
1954
|
+
*/
|
|
1955
|
+
hasAvailable() {
|
|
1956
|
+
for (const entry of this.agents.values()) {
|
|
1957
|
+
if (entry.status === "available") return true;
|
|
1958
|
+
}
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
// ============ Statistics ============
|
|
1962
|
+
/**
|
|
1963
|
+
* Get registry statistics
|
|
1964
|
+
*/
|
|
1965
|
+
getStats() {
|
|
1966
|
+
const stats = {
|
|
1967
|
+
total: this.agents.size,
|
|
1968
|
+
available: 0,
|
|
1969
|
+
busy: 0,
|
|
1970
|
+
unavailable: 0,
|
|
1971
|
+
error: 0,
|
|
1972
|
+
totalTasksCompleted: 0,
|
|
1973
|
+
totalTasksFailed: 0
|
|
1974
|
+
};
|
|
1975
|
+
for (const entry of this.agents.values()) {
|
|
1976
|
+
stats[entry.status]++;
|
|
1977
|
+
stats.totalTasksCompleted += entry.tasksCompleted;
|
|
1978
|
+
stats.totalTasksFailed += entry.tasksFailed;
|
|
1979
|
+
}
|
|
1980
|
+
return stats;
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Get agent statistics
|
|
1984
|
+
*/
|
|
1985
|
+
getAgentStats(name) {
|
|
1986
|
+
const entry = this.agents.get(name);
|
|
1987
|
+
if (!entry) return void 0;
|
|
1988
|
+
return {
|
|
1989
|
+
name: entry.agent.name,
|
|
1990
|
+
status: entry.status,
|
|
1991
|
+
currentTask: entry.currentTask,
|
|
1992
|
+
tasksCompleted: entry.tasksCompleted,
|
|
1993
|
+
tasksFailed: entry.tasksFailed,
|
|
1994
|
+
successRate: entry.tasksCompleted + entry.tasksFailed > 0 ? entry.tasksCompleted / (entry.tasksCompleted + entry.tasksFailed) : 0,
|
|
1995
|
+
registeredAt: entry.registeredAt,
|
|
1996
|
+
lastActiveAt: entry.lastActiveAt
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
// ============ Iteration ============
|
|
2000
|
+
/**
|
|
2001
|
+
* Iterate over agents
|
|
2002
|
+
*/
|
|
2003
|
+
*[Symbol.iterator]() {
|
|
2004
|
+
for (const entry of this.agents.values()) {
|
|
2005
|
+
yield entry.agent;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* For each agent
|
|
2010
|
+
*/
|
|
2011
|
+
forEach(callback) {
|
|
2012
|
+
for (const [name, entry] of this.agents) {
|
|
2013
|
+
callback(entry.agent, name);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
// ============ Utilities ============
|
|
2017
|
+
/**
|
|
2018
|
+
* Clear all agents
|
|
2019
|
+
*/
|
|
2020
|
+
clear() {
|
|
2021
|
+
this.agents.clear();
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Reset all agent statuses to available
|
|
2025
|
+
*/
|
|
2026
|
+
resetAllStatuses() {
|
|
2027
|
+
for (const entry of this.agents.values()) {
|
|
2028
|
+
entry.status = "available";
|
|
2029
|
+
entry.currentTask = void 0;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
};
|
|
2033
|
+
function createAgentRegistry(config) {
|
|
2034
|
+
return new AgentRegistry(config);
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
// src/coordination/strategies/DelegationStrategy.ts
|
|
2038
|
+
var BaseDelegationStrategy = class {
|
|
2039
|
+
/**
|
|
2040
|
+
* Default batch assignment - sequential selection
|
|
2041
|
+
*/
|
|
2042
|
+
async assignTasks(tasks, agents, context) {
|
|
2043
|
+
const assignments = /* @__PURE__ */ new Map();
|
|
2044
|
+
const availableAgents = [...agents];
|
|
2045
|
+
for (const task of tasks) {
|
|
2046
|
+
if (availableAgents.length === 0) {
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
2049
|
+
try {
|
|
2050
|
+
const result = await this.selectAgent(task, availableAgents, context);
|
|
2051
|
+
assignments.set(task.id, result.selectedAgent);
|
|
2052
|
+
const assignedIndex = availableAgents.findIndex(
|
|
2053
|
+
(a) => a.name === result.selectedAgent
|
|
2054
|
+
);
|
|
2055
|
+
if (assignedIndex >= 0 && !availableAgents[assignedIndex].parallelCapable) {
|
|
2056
|
+
availableAgents.splice(assignedIndex, 1);
|
|
2057
|
+
}
|
|
2058
|
+
} catch {
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
return assignments;
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* Filter available agents (not busy)
|
|
2065
|
+
*/
|
|
2066
|
+
filterAvailable(agents) {
|
|
2067
|
+
return agents.filter((a) => !a.isBusy);
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* Create a failure result
|
|
2071
|
+
*/
|
|
2072
|
+
createFailure(reason, agents) {
|
|
2073
|
+
const failure = {
|
|
2074
|
+
reason,
|
|
2075
|
+
consideredAgents: agents.map((a) => a.name)
|
|
2076
|
+
};
|
|
2077
|
+
throw new DelegationError(failure);
|
|
2078
|
+
}
|
|
2079
|
+
reset() {
|
|
2080
|
+
}
|
|
2081
|
+
};
|
|
2082
|
+
var DelegationError = class extends Error {
|
|
2083
|
+
failure;
|
|
2084
|
+
constructor(failureOrReason, agents, lastError) {
|
|
2085
|
+
if (typeof failureOrReason === "string") {
|
|
2086
|
+
const failure = {
|
|
2087
|
+
reason: failureOrReason,
|
|
2088
|
+
consideredAgents: agents?.map((a) => a.name) ?? [],
|
|
2089
|
+
suggestions: lastError ? [`Last error: ${lastError}`] : void 0
|
|
2090
|
+
};
|
|
2091
|
+
super(failureOrReason);
|
|
2092
|
+
this.failure = failure;
|
|
2093
|
+
} else {
|
|
2094
|
+
super(`Delegation failed: ${failureOrReason.reason}`);
|
|
2095
|
+
this.failure = failureOrReason;
|
|
2096
|
+
}
|
|
2097
|
+
this.name = "DelegationError";
|
|
2098
|
+
}
|
|
2099
|
+
};
|
|
2100
|
+
|
|
2101
|
+
// src/coordination/strategies/RoundRobin.ts
|
|
2102
|
+
var RoundRobinStrategy = class extends BaseDelegationStrategy {
|
|
2103
|
+
name = "round-robin";
|
|
2104
|
+
currentIndex = 0;
|
|
2105
|
+
skipBusy;
|
|
2106
|
+
wrapAround;
|
|
2107
|
+
constructor(config = {}) {
|
|
2108
|
+
super();
|
|
2109
|
+
this.skipBusy = config.skipBusy ?? true;
|
|
2110
|
+
this.wrapAround = config.wrapAround ?? true;
|
|
2111
|
+
}
|
|
2112
|
+
async selectAgent(task, agents, _context) {
|
|
2113
|
+
const startTime = Date.now();
|
|
2114
|
+
if (agents.length === 0) {
|
|
2115
|
+
this.createFailure("No agents available", []);
|
|
2116
|
+
}
|
|
2117
|
+
let available = this.skipBusy ? this.filterAvailable(agents) : agents;
|
|
2118
|
+
if (available.length === 0) {
|
|
2119
|
+
this.createFailure("All agents are busy", agents);
|
|
2120
|
+
}
|
|
2121
|
+
if (task.requiredCapabilities && task.requiredCapabilities.length > 0) {
|
|
2122
|
+
available = available.filter((agent) => {
|
|
2123
|
+
const requiredCaps = task.requiredCapabilities.map((name) => ({
|
|
2124
|
+
name,
|
|
2125
|
+
description: "",
|
|
2126
|
+
proficiency: "intermediate"
|
|
2127
|
+
}));
|
|
2128
|
+
const match = AgentCapabilities.match(requiredCaps, agent.capabilities);
|
|
2129
|
+
return match.canExecute;
|
|
2130
|
+
});
|
|
2131
|
+
if (available.length === 0) {
|
|
2132
|
+
this.createFailure("No agents have all required capabilities", agents);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
if (this.currentIndex >= available.length) {
|
|
2136
|
+
if (this.wrapAround) {
|
|
2137
|
+
this.currentIndex = 0;
|
|
2138
|
+
} else {
|
|
2139
|
+
this.createFailure("No more agents in rotation", agents);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
const selectedAgent = available[this.currentIndex];
|
|
2143
|
+
this.currentIndex = (this.currentIndex + 1) % available.length;
|
|
2144
|
+
return Promise.resolve({
|
|
2145
|
+
selectedAgent: selectedAgent.name,
|
|
2146
|
+
reason: `Round-robin selection (position ${this.currentIndex})`,
|
|
2147
|
+
confidence: 1,
|
|
2148
|
+
// Round-robin is deterministic
|
|
2149
|
+
alternativeAgents: available.filter((a) => a.name !== selectedAgent.name).map((a) => a.name),
|
|
2150
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2151
|
+
metadata: {
|
|
2152
|
+
strategy: "round-robin",
|
|
2153
|
+
position: this.currentIndex,
|
|
2154
|
+
totalAgents: available.length
|
|
2155
|
+
}
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
/**
|
|
2159
|
+
* Batch assignment with round-robin distribution
|
|
2160
|
+
*/
|
|
2161
|
+
assignTasks(tasks, agents, _context) {
|
|
2162
|
+
const assignments = /* @__PURE__ */ new Map();
|
|
2163
|
+
const available = this.skipBusy ? this.filterAvailable(agents) : agents;
|
|
2164
|
+
if (available.length === 0) {
|
|
2165
|
+
return Promise.resolve(assignments);
|
|
2166
|
+
}
|
|
2167
|
+
for (const task of tasks) {
|
|
2168
|
+
const agent = available[this.currentIndex % available.length];
|
|
2169
|
+
assignments.set(task.id, agent.name);
|
|
2170
|
+
this.currentIndex++;
|
|
2171
|
+
}
|
|
2172
|
+
return Promise.resolve(assignments);
|
|
2173
|
+
}
|
|
2174
|
+
/**
|
|
2175
|
+
* Reset the rotation
|
|
2176
|
+
*/
|
|
2177
|
+
reset() {
|
|
2178
|
+
this.currentIndex = 0;
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Get current position in rotation
|
|
2182
|
+
*/
|
|
2183
|
+
getCurrentPosition() {
|
|
2184
|
+
return this.currentIndex;
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Set position in rotation
|
|
2188
|
+
*/
|
|
2189
|
+
setPosition(position) {
|
|
2190
|
+
this.currentIndex = position;
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
function createRoundRobinStrategy(config) {
|
|
2194
|
+
return new RoundRobinStrategy(config);
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// src/coordination/strategies/BestMatch.ts
|
|
2198
|
+
var BestMatchStrategy = class extends BaseDelegationStrategy {
|
|
2199
|
+
name = "best-match";
|
|
2200
|
+
minimumScore;
|
|
2201
|
+
useKeywordMatching;
|
|
2202
|
+
capabilityWeight;
|
|
2203
|
+
preferAvailable;
|
|
2204
|
+
constructor(config = {}) {
|
|
2205
|
+
super();
|
|
2206
|
+
this.minimumScore = config.minimumScore ?? 0;
|
|
2207
|
+
this.useKeywordMatching = config.useKeywordMatching ?? true;
|
|
2208
|
+
this.capabilityWeight = config.capabilityWeight ?? 0.7;
|
|
2209
|
+
this.preferAvailable = config.preferAvailable ?? true;
|
|
2210
|
+
}
|
|
2211
|
+
async selectAgent(task, agents, _context) {
|
|
2212
|
+
const startTime = Date.now();
|
|
2213
|
+
if (agents.length === 0) {
|
|
2214
|
+
this.createFailure("No agents available", []);
|
|
2215
|
+
}
|
|
2216
|
+
const ranked = this.rankAgents(task, agents);
|
|
2217
|
+
if (ranked.length === 0) {
|
|
2218
|
+
this.createFailure("No agents can handle this task", agents);
|
|
2219
|
+
}
|
|
2220
|
+
const hasRequirements = task.requiredCapabilities && task.requiredCapabilities.length > 0;
|
|
2221
|
+
const qualified = ranked.filter(
|
|
2222
|
+
(r) => r.score >= this.minimumScore && (!hasRequirements || r.missingCapabilities.length === 0)
|
|
2223
|
+
);
|
|
2224
|
+
if (qualified.length === 0) {
|
|
2225
|
+
if (hasRequirements) {
|
|
2226
|
+
this.createFailure("No agents have all required capabilities", agents);
|
|
2227
|
+
}
|
|
2228
|
+
this.createFailure(
|
|
2229
|
+
`No agents meet minimum score threshold (${this.minimumScore})`,
|
|
2230
|
+
agents
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
let selected = qualified[0];
|
|
2234
|
+
if (this.preferAvailable) {
|
|
2235
|
+
const availableQualified = qualified.filter(
|
|
2236
|
+
(r) => !agents.find((a) => a.name === r.agentName)?.isBusy
|
|
2237
|
+
);
|
|
2238
|
+
if (availableQualified.length > 0) {
|
|
2239
|
+
selected = availableQualified[0];
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
const reason = this.buildReason(selected, task);
|
|
2243
|
+
return Promise.resolve({
|
|
2244
|
+
selectedAgent: selected.agentName,
|
|
2245
|
+
reason,
|
|
2246
|
+
confidence: selected.score,
|
|
2247
|
+
alternativeAgents: qualified.filter((r) => r.agentName !== selected.agentName).slice(0, 3).map((r) => r.agentName),
|
|
2248
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2249
|
+
metadata: {
|
|
2250
|
+
strategy: "best-match",
|
|
2251
|
+
score: selected.score,
|
|
2252
|
+
matchedCapabilities: selected.matchedCapabilities.map((c) => c.name),
|
|
2253
|
+
missingCapabilities: selected.missingCapabilities.map((c) => c.name),
|
|
2254
|
+
rankedCount: qualified.length
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
/**
|
|
2259
|
+
* Rank agents for a task
|
|
2260
|
+
*/
|
|
2261
|
+
rankAgents(task, agents) {
|
|
2262
|
+
const rankings = [];
|
|
2263
|
+
for (const agent of agents) {
|
|
2264
|
+
const capScore = AgentCapabilities.calculateAgentScore(agent, task);
|
|
2265
|
+
let finalScore = capScore;
|
|
2266
|
+
if (this.useKeywordMatching) {
|
|
2267
|
+
const keywordScore = agent.role.calculateRelevanceScore(
|
|
2268
|
+
task.description
|
|
2269
|
+
);
|
|
2270
|
+
finalScore = capScore * this.capabilityWeight + keywordScore * (1 - this.capabilityWeight);
|
|
2271
|
+
}
|
|
2272
|
+
if (agent.isBusy) {
|
|
2273
|
+
finalScore *= 0.5;
|
|
2274
|
+
}
|
|
2275
|
+
const requiredCaps = (task.requiredCapabilities ?? []).map((name) => ({
|
|
2276
|
+
name,
|
|
2277
|
+
description: "",
|
|
2278
|
+
proficiency: "intermediate"
|
|
2279
|
+
}));
|
|
2280
|
+
const match = AgentCapabilities.match(requiredCaps, agent.capabilities);
|
|
2281
|
+
rankings.push({
|
|
2282
|
+
agentName: agent.name,
|
|
2283
|
+
score: finalScore,
|
|
2284
|
+
matchedCapabilities: match.matched,
|
|
2285
|
+
missingCapabilities: match.missing
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2288
|
+
rankings.sort((a, b) => b.score - a.score);
|
|
2289
|
+
return rankings;
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Build explanation for selection
|
|
2293
|
+
*/
|
|
2294
|
+
buildReason(selected, _task) {
|
|
2295
|
+
const parts = [];
|
|
2296
|
+
parts.push(`Best match with score ${(selected.score * 100).toFixed(1)}%`);
|
|
2297
|
+
if (selected.matchedCapabilities.length > 0) {
|
|
2298
|
+
const capNames = selected.matchedCapabilities.map((c) => c.name).join(", ");
|
|
2299
|
+
parts.push(`Matched capabilities: ${capNames}`);
|
|
2300
|
+
}
|
|
2301
|
+
if (selected.missingCapabilities.length > 0) {
|
|
2302
|
+
const missingNames = selected.missingCapabilities.map((c) => c.name).join(", ");
|
|
2303
|
+
parts.push(`Missing: ${missingNames}`);
|
|
2304
|
+
}
|
|
2305
|
+
return parts.join(". ");
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Find agents that can definitely handle a task
|
|
2309
|
+
*/
|
|
2310
|
+
findCapableAgents(task, agents) {
|
|
2311
|
+
const ranked = this.rankAgents(task, agents);
|
|
2312
|
+
return ranked.filter(
|
|
2313
|
+
(r) => r.missingCapabilities.length === 0 && r.score >= this.minimumScore
|
|
2314
|
+
).map((r) => agents.find((a) => a.name === r.agentName)).filter(Boolean);
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
function createBestMatchStrategy(config) {
|
|
2318
|
+
return new BestMatchStrategy(config);
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
// src/coordination/strategies/Auction.ts
|
|
2322
|
+
var AuctionStrategy = class extends BaseDelegationStrategy {
|
|
2323
|
+
name = "auction";
|
|
2324
|
+
biddingTimeMs;
|
|
2325
|
+
minimumBid;
|
|
2326
|
+
selectionCriteria;
|
|
2327
|
+
allowRebidding;
|
|
2328
|
+
bidTimeoutMs;
|
|
2329
|
+
constructor(config = {}) {
|
|
2330
|
+
super();
|
|
2331
|
+
this.biddingTimeMs = config.biddingTimeMs ?? 5e3;
|
|
2332
|
+
this.minimumBid = config.minimumBid ?? 0.1;
|
|
2333
|
+
this.selectionCriteria = config.selectionCriteria ?? "confidence";
|
|
2334
|
+
this.allowRebidding = config.allowRebidding ?? false;
|
|
2335
|
+
this.bidTimeoutMs = config.bidTimeoutMs ?? 2e3;
|
|
2336
|
+
}
|
|
2337
|
+
async selectAgent(task, agents, context) {
|
|
2338
|
+
const startTime = Date.now();
|
|
2339
|
+
if (agents.length === 0) {
|
|
2340
|
+
this.createFailure("No agents available", []);
|
|
2341
|
+
}
|
|
2342
|
+
let available = this.filterAvailable(agents);
|
|
2343
|
+
if (available.length === 0) {
|
|
2344
|
+
this.createFailure("All agents are busy", agents);
|
|
2345
|
+
}
|
|
2346
|
+
if (task.requiredCapabilities && task.requiredCapabilities.length > 0) {
|
|
2347
|
+
available = available.filter((agent) => {
|
|
2348
|
+
const requiredCaps = task.requiredCapabilities.map((name) => ({
|
|
2349
|
+
name,
|
|
2350
|
+
description: "",
|
|
2351
|
+
proficiency: "intermediate"
|
|
2352
|
+
}));
|
|
2353
|
+
const match = AgentCapabilities.match(requiredCaps, agent.capabilities);
|
|
2354
|
+
return match.canExecute;
|
|
2355
|
+
});
|
|
2356
|
+
if (available.length === 0) {
|
|
2357
|
+
this.createFailure("No agents have all required capabilities", agents);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
const bids = await this.collectBids(task, available);
|
|
2361
|
+
if (bids.length === 0) {
|
|
2362
|
+
this.createFailure("No agents submitted bids", agents);
|
|
2363
|
+
}
|
|
2364
|
+
const qualifiedBids = bids.filter((b) => b.confidence >= this.minimumBid);
|
|
2365
|
+
if (qualifiedBids.length === 0) {
|
|
2366
|
+
this.createFailure(
|
|
2367
|
+
`No bids met minimum threshold (${this.minimumBid})`,
|
|
2368
|
+
agents
|
|
2369
|
+
);
|
|
2370
|
+
}
|
|
2371
|
+
const winner = this.selectWinner(qualifiedBids);
|
|
2372
|
+
context.emit({
|
|
2373
|
+
type: "delegation:decision",
|
|
2374
|
+
taskId: task.id,
|
|
2375
|
+
toAgent: winner.agentName,
|
|
2376
|
+
strategy: "auction",
|
|
2377
|
+
reason: `Won auction with confidence ${winner.confidence.toFixed(2)}`,
|
|
2378
|
+
confidence: winner.confidence,
|
|
2379
|
+
alternatives: qualifiedBids.filter((b) => b.agentName !== winner.agentName).map((b) => b.agentName)
|
|
2380
|
+
});
|
|
2381
|
+
return {
|
|
2382
|
+
selectedAgent: winner.agentName,
|
|
2383
|
+
reason: this.buildReason(winner, qualifiedBids),
|
|
2384
|
+
confidence: winner.confidence,
|
|
2385
|
+
alternativeAgents: qualifiedBids.filter((b) => b.agentName !== winner.agentName).map((b) => b.agentName),
|
|
2386
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2387
|
+
metadata: {
|
|
2388
|
+
strategy: "auction",
|
|
2389
|
+
totalBids: bids.length,
|
|
2390
|
+
qualifiedBids: qualifiedBids.length,
|
|
2391
|
+
winningBid: winner,
|
|
2392
|
+
allBids: bids
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Collect bids from agents
|
|
2398
|
+
*/
|
|
2399
|
+
async collectBids(task, agents) {
|
|
2400
|
+
const bidPromises = agents.map(
|
|
2401
|
+
(agent) => this.getBidWithTimeout(agent, task)
|
|
2402
|
+
);
|
|
2403
|
+
const results = await Promise.allSettled(bidPromises);
|
|
2404
|
+
const bids = [];
|
|
2405
|
+
for (const result of results) {
|
|
2406
|
+
if (result.status === "fulfilled" && result.value) {
|
|
2407
|
+
bids.push(result.value);
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return bids;
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Get a bid from an agent with timeout
|
|
2414
|
+
*/
|
|
2415
|
+
async getBidWithTimeout(agent, task) {
|
|
2416
|
+
return new Promise((resolve) => {
|
|
2417
|
+
const timeout = setTimeout(() => resolve(null), this.bidTimeoutMs);
|
|
2418
|
+
agent.bidOnTask(task).then((bid) => {
|
|
2419
|
+
clearTimeout(timeout);
|
|
2420
|
+
resolve(bid);
|
|
2421
|
+
}).catch(() => {
|
|
2422
|
+
clearTimeout(timeout);
|
|
2423
|
+
resolve(null);
|
|
2424
|
+
});
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Select the winning bid
|
|
2429
|
+
*/
|
|
2430
|
+
selectWinner(bids) {
|
|
2431
|
+
switch (this.selectionCriteria) {
|
|
2432
|
+
case "fastest":
|
|
2433
|
+
return bids.reduce((best, bid) => {
|
|
2434
|
+
const bestTime = best.estimatedTime ?? Infinity;
|
|
2435
|
+
const bidTime = bid.estimatedTime ?? Infinity;
|
|
2436
|
+
return bidTime < bestTime ? bid : best;
|
|
2437
|
+
}, bids[0]);
|
|
2438
|
+
case "cheapest":
|
|
2439
|
+
return bids.reduce((best, bid) => {
|
|
2440
|
+
const bestTime = best.estimatedTime ?? Infinity;
|
|
2441
|
+
const bidTime = bid.estimatedTime ?? Infinity;
|
|
2442
|
+
return bidTime < bestTime ? bid : best;
|
|
2443
|
+
}, bids[0]);
|
|
2444
|
+
case "confidence":
|
|
2445
|
+
default:
|
|
2446
|
+
return bids.reduce(
|
|
2447
|
+
(best, bid) => bid.confidence > best.confidence ? bid : best,
|
|
2448
|
+
bids[0]
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* Build explanation for auction result
|
|
2454
|
+
*/
|
|
2455
|
+
buildReason(winner, allBids) {
|
|
2456
|
+
const parts = [];
|
|
2457
|
+
parts.push(`Won auction (${this.selectionCriteria} criteria)`);
|
|
2458
|
+
parts.push(`Confidence: ${(winner.confidence * 100).toFixed(1)}%`);
|
|
2459
|
+
if (winner.estimatedTime) {
|
|
2460
|
+
parts.push(`Est. time: ${winner.estimatedTime}ms`);
|
|
2461
|
+
}
|
|
2462
|
+
parts.push(`Capabilities: ${winner.capabilities.join(", ")}`);
|
|
2463
|
+
if (allBids.length > 1) {
|
|
2464
|
+
parts.push(`Competed against ${allBids.length - 1} other bid(s)`);
|
|
2465
|
+
}
|
|
2466
|
+
return parts.join(". ");
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Run a multi-round auction with rebidding
|
|
2470
|
+
*/
|
|
2471
|
+
async runMultiRoundAuction(task, agents, rounds = 2) {
|
|
2472
|
+
let currentBids = await this.collectBids(task, agents);
|
|
2473
|
+
if (!this.allowRebidding || rounds <= 1) {
|
|
2474
|
+
return currentBids;
|
|
2475
|
+
}
|
|
2476
|
+
for (let round = 1; round < rounds; round++) {
|
|
2477
|
+
const currentBest = currentBids.reduce(
|
|
2478
|
+
(best, bid) => bid.confidence > best.confidence ? bid : best,
|
|
2479
|
+
currentBids[0]
|
|
2480
|
+
);
|
|
2481
|
+
const rebidPromises = agents.map(async (agent) => {
|
|
2482
|
+
const currentBid = currentBids.find((b) => b.agentName === agent.name);
|
|
2483
|
+
if (!currentBid) return null;
|
|
2484
|
+
if (currentBid.agentName === currentBest.agentName) {
|
|
2485
|
+
return currentBid;
|
|
2486
|
+
}
|
|
2487
|
+
return this.getBidWithTimeout(agent, task);
|
|
2488
|
+
});
|
|
2489
|
+
const results = await Promise.allSettled(rebidPromises);
|
|
2490
|
+
currentBids = results.filter((r) => r.status === "fulfilled" && r.value).map((r) => r.value);
|
|
2491
|
+
}
|
|
2492
|
+
return currentBids;
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
function createAuctionStrategy(config) {
|
|
2496
|
+
return new AuctionStrategy(config);
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// src/coordination/strategies/Hierarchical.ts
|
|
2500
|
+
var HierarchicalStrategy = class extends BaseDelegationStrategy {
|
|
2501
|
+
name = "hierarchical";
|
|
2502
|
+
hierarchy;
|
|
2503
|
+
managersCanExecute;
|
|
2504
|
+
escalateToManager;
|
|
2505
|
+
useCapabilityMatching;
|
|
2506
|
+
/** Role priority order (for array-based hierarchy) */
|
|
2507
|
+
rolePriority = [];
|
|
2508
|
+
constructor(config = {}) {
|
|
2509
|
+
super();
|
|
2510
|
+
if (Array.isArray(config.hierarchy)) {
|
|
2511
|
+
this.rolePriority = config.hierarchy;
|
|
2512
|
+
this.hierarchy = {
|
|
2513
|
+
managers: [],
|
|
2514
|
+
workers: /* @__PURE__ */ new Map()
|
|
2515
|
+
};
|
|
2516
|
+
} else {
|
|
2517
|
+
this.hierarchy = config.hierarchy ?? {
|
|
2518
|
+
managers: [],
|
|
2519
|
+
workers: /* @__PURE__ */ new Map()
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
this.managersCanExecute = config.managersCanExecute ?? false;
|
|
2523
|
+
this.escalateToManager = config.escalateToManager ?? true;
|
|
2524
|
+
this.useCapabilityMatching = config.useCapabilityMatching ?? true;
|
|
2525
|
+
}
|
|
2526
|
+
/**
|
|
2527
|
+
* Set the hierarchy
|
|
2528
|
+
*/
|
|
2529
|
+
setHierarchy(hierarchy) {
|
|
2530
|
+
this.hierarchy = hierarchy;
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Add a manager
|
|
2534
|
+
*/
|
|
2535
|
+
addManager(managerName, workers = []) {
|
|
2536
|
+
if (!this.hierarchy.managers.includes(managerName)) {
|
|
2537
|
+
this.hierarchy.managers.push(managerName);
|
|
2538
|
+
}
|
|
2539
|
+
this.hierarchy.workers.set(managerName, workers);
|
|
2540
|
+
}
|
|
2541
|
+
/**
|
|
2542
|
+
* Add a worker to a manager
|
|
2543
|
+
*/
|
|
2544
|
+
addWorker(managerName, workerName) {
|
|
2545
|
+
const workers = this.hierarchy.workers.get(managerName) ?? [];
|
|
2546
|
+
if (!workers.includes(workerName)) {
|
|
2547
|
+
workers.push(workerName);
|
|
2548
|
+
this.hierarchy.workers.set(managerName, workers);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
async selectAgent(task, agents, _context) {
|
|
2552
|
+
const startTime = Date.now();
|
|
2553
|
+
if (agents.length === 0) {
|
|
2554
|
+
this.createFailure("No agents available", []);
|
|
2555
|
+
}
|
|
2556
|
+
if (this.rolePriority.length > 0) {
|
|
2557
|
+
return this.selectByRolePriority(task, agents, startTime);
|
|
2558
|
+
}
|
|
2559
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
2560
|
+
for (const agent of agents) {
|
|
2561
|
+
agentMap.set(agent.name, agent);
|
|
2562
|
+
}
|
|
2563
|
+
if (this.hierarchy.managers.length === 0) {
|
|
2564
|
+
this.autoDetectHierarchy(agents);
|
|
2565
|
+
}
|
|
2566
|
+
const manager = this.findManagerForTask(task, agents);
|
|
2567
|
+
if (!manager) {
|
|
2568
|
+
return this.fallbackSelection(task, agents, startTime);
|
|
2569
|
+
}
|
|
2570
|
+
const workerNames = this.hierarchy.workers.get(manager.name) ?? [];
|
|
2571
|
+
const workers = workerNames.map((name) => agentMap.get(name)).filter((w) => w !== void 0);
|
|
2572
|
+
const availableWorkers = workers.filter((w) => !w.isBusy);
|
|
2573
|
+
if (availableWorkers.length > 0) {
|
|
2574
|
+
const selectedWorker = this.selectWorker(task, availableWorkers);
|
|
2575
|
+
return {
|
|
2576
|
+
selectedAgent: selectedWorker.name,
|
|
2577
|
+
reason: `Delegated by ${manager.name} (hierarchical)`,
|
|
2578
|
+
confidence: AgentCapabilities.calculateAgentScore(selectedWorker, task),
|
|
2579
|
+
alternativeAgents: availableWorkers.filter((w) => w.name !== selectedWorker.name).map((w) => w.name),
|
|
2580
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2581
|
+
metadata: {
|
|
2582
|
+
strategy: "hierarchical",
|
|
2583
|
+
manager: manager.name,
|
|
2584
|
+
delegatedTo: selectedWorker.name,
|
|
2585
|
+
totalWorkers: workers.length,
|
|
2586
|
+
availableWorkers: availableWorkers.length
|
|
2587
|
+
}
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
if (this.managersCanExecute && !manager.isBusy) {
|
|
2591
|
+
return {
|
|
2592
|
+
selectedAgent: manager.name,
|
|
2593
|
+
reason: "No workers available, manager handling directly",
|
|
2594
|
+
confidence: AgentCapabilities.calculateAgentScore(manager, task),
|
|
2595
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2596
|
+
metadata: {
|
|
2597
|
+
strategy: "hierarchical",
|
|
2598
|
+
manager: manager.name,
|
|
2599
|
+
escalated: true,
|
|
2600
|
+
reason: "no_available_workers"
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
if (this.escalateToManager) {
|
|
2605
|
+
for (const managerName of this.hierarchy.managers) {
|
|
2606
|
+
if (managerName === manager.name) continue;
|
|
2607
|
+
const otherManager = agentMap.get(managerName);
|
|
2608
|
+
if (otherManager && !otherManager.isBusy) {
|
|
2609
|
+
const otherWorkerNames = this.hierarchy.workers.get(managerName) ?? [];
|
|
2610
|
+
const otherWorkers = otherWorkerNames.map((name) => agentMap.get(name)).filter((w) => w !== void 0 && !w.isBusy);
|
|
2611
|
+
if (otherWorkers.length > 0) {
|
|
2612
|
+
const selectedWorker = this.selectWorker(task, otherWorkers);
|
|
2613
|
+
return {
|
|
2614
|
+
selectedAgent: selectedWorker.name,
|
|
2615
|
+
reason: `Escalated to ${managerName}, delegated to ${selectedWorker.name}`,
|
|
2616
|
+
confidence: AgentCapabilities.calculateAgentScore(
|
|
2617
|
+
selectedWorker,
|
|
2618
|
+
task
|
|
2619
|
+
),
|
|
2620
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2621
|
+
metadata: {
|
|
2622
|
+
strategy: "hierarchical",
|
|
2623
|
+
originalManager: manager.name,
|
|
2624
|
+
escalatedTo: managerName,
|
|
2625
|
+
delegatedTo: selectedWorker.name
|
|
2626
|
+
}
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
this.createFailure("No available agents in hierarchy", agents);
|
|
2633
|
+
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Auto-detect hierarchy based on agent roles
|
|
2636
|
+
*/
|
|
2637
|
+
autoDetectHierarchy(agents) {
|
|
2638
|
+
const managers = [];
|
|
2639
|
+
const workers = [];
|
|
2640
|
+
for (const agent of agents) {
|
|
2641
|
+
const roleName = agent.role.name.toLowerCase();
|
|
2642
|
+
const isManager = roleName.includes("manager") || roleName.includes("lead") || roleName.includes("supervisor") || roleName.includes("director") || agent.role.canDelegate;
|
|
2643
|
+
if (isManager) {
|
|
2644
|
+
managers.push(agent.name);
|
|
2645
|
+
} else {
|
|
2646
|
+
workers.push(agent.name);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
if (managers.length === 0 && agents.length > 0) {
|
|
2650
|
+
managers.push(agents[0].name);
|
|
2651
|
+
workers.splice(workers.indexOf(agents[0].name), 1);
|
|
2652
|
+
}
|
|
2653
|
+
this.hierarchy = {
|
|
2654
|
+
managers,
|
|
2655
|
+
workers: /* @__PURE__ */ new Map([[managers[0], workers]])
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* Find the best manager for a task
|
|
2660
|
+
*/
|
|
2661
|
+
findManagerForTask(task, agents) {
|
|
2662
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
2663
|
+
for (const agent of agents) {
|
|
2664
|
+
agentMap.set(agent.name, agent);
|
|
2665
|
+
}
|
|
2666
|
+
let bestManager;
|
|
2667
|
+
let bestScore = 0;
|
|
2668
|
+
for (const managerName of this.hierarchy.managers) {
|
|
2669
|
+
const manager = agentMap.get(managerName);
|
|
2670
|
+
if (!manager) continue;
|
|
2671
|
+
const workerNames = this.hierarchy.workers.get(managerName) ?? [];
|
|
2672
|
+
let totalScore = 0;
|
|
2673
|
+
for (const workerName of workerNames) {
|
|
2674
|
+
const worker = agentMap.get(workerName);
|
|
2675
|
+
if (worker) {
|
|
2676
|
+
totalScore += AgentCapabilities.calculateAgentScore(worker, task);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
const avgScore = workerNames.length > 0 ? totalScore / workerNames.length : 0;
|
|
2680
|
+
if (avgScore > bestScore) {
|
|
2681
|
+
bestScore = avgScore;
|
|
2682
|
+
bestManager = manager;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
return bestManager;
|
|
2686
|
+
}
|
|
2687
|
+
/**
|
|
2688
|
+
* Select the best worker for a task
|
|
2689
|
+
*/
|
|
2690
|
+
selectWorker(task, workers) {
|
|
2691
|
+
if (!this.useCapabilityMatching) {
|
|
2692
|
+
return workers[0];
|
|
2693
|
+
}
|
|
2694
|
+
const ranked = AgentCapabilities.rank(workers, task);
|
|
2695
|
+
if (ranked.length > 0) {
|
|
2696
|
+
const best = workers.find((w) => w.name === ranked[0].agentName);
|
|
2697
|
+
if (best) return best;
|
|
2698
|
+
}
|
|
2699
|
+
return workers[0];
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Fallback to simple best-match selection
|
|
2703
|
+
*/
|
|
2704
|
+
fallbackSelection(task, agents, startTime) {
|
|
2705
|
+
const available = this.filterAvailable(agents);
|
|
2706
|
+
if (available.length === 0) {
|
|
2707
|
+
this.createFailure("No available agents", agents);
|
|
2708
|
+
}
|
|
2709
|
+
const ranked = AgentCapabilities.rank(available, task);
|
|
2710
|
+
const selected = available.find((a) => a.name === ranked[0]?.agentName) ?? available[0];
|
|
2711
|
+
return Promise.resolve({
|
|
2712
|
+
selectedAgent: selected.name,
|
|
2713
|
+
reason: "No hierarchy defined, using best match",
|
|
2714
|
+
confidence: AgentCapabilities.calculateAgentScore(selected, task),
|
|
2715
|
+
alternativeAgents: available.filter((a) => a.name !== selected.name).map((a) => a.name),
|
|
2716
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2717
|
+
metadata: {
|
|
2718
|
+
strategy: "hierarchical",
|
|
2719
|
+
fallback: true
|
|
2720
|
+
}
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
/**
|
|
2724
|
+
* Select agent based on role priority order
|
|
2725
|
+
*/
|
|
2726
|
+
selectByRolePriority(task, agents, startTime) {
|
|
2727
|
+
let available = this.filterAvailable(agents);
|
|
2728
|
+
if (task.requiredCapabilities && task.requiredCapabilities.length > 0) {
|
|
2729
|
+
available = available.filter((agent) => {
|
|
2730
|
+
const requiredCaps = task.requiredCapabilities.map((name) => ({
|
|
2731
|
+
name,
|
|
2732
|
+
description: "",
|
|
2733
|
+
proficiency: "intermediate"
|
|
2734
|
+
}));
|
|
2735
|
+
const match = AgentCapabilities.match(requiredCaps, agent.capabilities);
|
|
2736
|
+
return match.canExecute;
|
|
2737
|
+
});
|
|
2738
|
+
if (available.length === 0) {
|
|
2739
|
+
this.createFailure("No agents have all required capabilities", agents);
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
const sortedAgents = [...available].sort((a, b) => {
|
|
2743
|
+
const aRoleName = a.role.name.toLowerCase();
|
|
2744
|
+
const bRoleName = b.role.name.toLowerCase();
|
|
2745
|
+
const aPriority = this.rolePriority.findIndex(
|
|
2746
|
+
(r) => r.toLowerCase() === aRoleName
|
|
2747
|
+
);
|
|
2748
|
+
const bPriority = this.rolePriority.findIndex(
|
|
2749
|
+
(r) => r.toLowerCase() === bRoleName
|
|
2750
|
+
);
|
|
2751
|
+
const aScore = aPriority === -1 ? Infinity : aPriority;
|
|
2752
|
+
const bScore = bPriority === -1 ? Infinity : bPriority;
|
|
2753
|
+
return aScore - bScore;
|
|
2754
|
+
});
|
|
2755
|
+
if (sortedAgents.length === 0) {
|
|
2756
|
+
this.createFailure("No available agents", agents);
|
|
2757
|
+
}
|
|
2758
|
+
const selected = sortedAgents[0];
|
|
2759
|
+
return Promise.resolve({
|
|
2760
|
+
selectedAgent: selected.name,
|
|
2761
|
+
reason: `Selected by role priority (${selected.role.name})`,
|
|
2762
|
+
confidence: AgentCapabilities.calculateAgentScore(selected, task),
|
|
2763
|
+
alternativeAgents: sortedAgents.slice(1, 4).map((a) => a.name),
|
|
2764
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2765
|
+
metadata: {
|
|
2766
|
+
strategy: "hierarchical",
|
|
2767
|
+
rolePriority: this.rolePriority,
|
|
2768
|
+
selectedRole: selected.role.name
|
|
2769
|
+
}
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
reset() {
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Get current hierarchy
|
|
2776
|
+
*/
|
|
2777
|
+
getHierarchy() {
|
|
2778
|
+
return {
|
|
2779
|
+
managers: [...this.hierarchy.managers],
|
|
2780
|
+
workers: new Map(this.hierarchy.workers)
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
function createHierarchicalStrategy(config) {
|
|
2785
|
+
return new HierarchicalStrategy(config);
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
// src/coordination/strategies/Consensus.ts
|
|
2789
|
+
var ConsensusStrategy = class extends BaseDelegationStrategy {
|
|
2790
|
+
name = "consensus";
|
|
2791
|
+
requiredAgreement;
|
|
2792
|
+
maxRounds;
|
|
2793
|
+
tieBreaker;
|
|
2794
|
+
voteTimeoutMs;
|
|
2795
|
+
weightedVoting;
|
|
2796
|
+
minimumVotes;
|
|
2797
|
+
constructor(config = {}) {
|
|
2798
|
+
super();
|
|
2799
|
+
this.requiredAgreement = config.requiredAgreement ?? 0.5;
|
|
2800
|
+
this.maxRounds = config.maxRounds ?? 3;
|
|
2801
|
+
this.tieBreaker = config.tieBreaker ?? "highest-capability";
|
|
2802
|
+
this.voteTimeoutMs = config.voteTimeoutMs ?? 3e3;
|
|
2803
|
+
this.weightedVoting = config.weightedVoting ?? false;
|
|
2804
|
+
this.minimumVotes = config.minimumVotes ?? 1;
|
|
2805
|
+
}
|
|
2806
|
+
async selectAgent(task, agents, context) {
|
|
2807
|
+
const startTime = Date.now();
|
|
2808
|
+
if (agents.length === 0) {
|
|
2809
|
+
this.createFailure("No agents available", []);
|
|
2810
|
+
}
|
|
2811
|
+
const candidates = this.filterAvailable(agents);
|
|
2812
|
+
if (candidates.length === 0) {
|
|
2813
|
+
this.createFailure("All agents are busy", agents);
|
|
2814
|
+
}
|
|
2815
|
+
if (candidates.length === 1) {
|
|
2816
|
+
return {
|
|
2817
|
+
selectedAgent: candidates[0].name,
|
|
2818
|
+
reason: "Only one candidate available",
|
|
2819
|
+
confidence: 1,
|
|
2820
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2821
|
+
metadata: {
|
|
2822
|
+
strategy: "consensus",
|
|
2823
|
+
noVotingNeeded: true
|
|
2824
|
+
}
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
context.emit({
|
|
2828
|
+
type: "consensus:requested",
|
|
2829
|
+
taskId: task.id,
|
|
2830
|
+
candidates: candidates.map((c) => c.name),
|
|
2831
|
+
voters: agents.map((a) => a.name)
|
|
2832
|
+
});
|
|
2833
|
+
let finalResult;
|
|
2834
|
+
const allRounds = [];
|
|
2835
|
+
for (let round = 1; round <= this.maxRounds; round++) {
|
|
2836
|
+
const roundResult = await this.runVotingRound(
|
|
2837
|
+
round,
|
|
2838
|
+
task,
|
|
2839
|
+
agents,
|
|
2840
|
+
candidates,
|
|
2841
|
+
context
|
|
2842
|
+
);
|
|
2843
|
+
allRounds.push(roundResult);
|
|
2844
|
+
if (roundResult.consensusReached || roundResult.winner) {
|
|
2845
|
+
finalResult = roundResult;
|
|
2846
|
+
break;
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
if (!finalResult?.winner) {
|
|
2850
|
+
const lastRound = allRounds[allRounds.length - 1];
|
|
2851
|
+
const winner = this.breakTie(lastRound.tally, candidates, task);
|
|
2852
|
+
context.emit({
|
|
2853
|
+
type: "consensus:reached",
|
|
2854
|
+
taskId: task.id,
|
|
2855
|
+
winner: winner.name,
|
|
2856
|
+
rounds: allRounds.length,
|
|
2857
|
+
consensusReached: false,
|
|
2858
|
+
tieBreaker: this.tieBreaker
|
|
2859
|
+
});
|
|
2860
|
+
return {
|
|
2861
|
+
selectedAgent: winner.name,
|
|
2862
|
+
reason: `No consensus after ${this.maxRounds} rounds, resolved by ${this.tieBreaker}`,
|
|
2863
|
+
confidence: lastRound.agreementRatio,
|
|
2864
|
+
alternativeAgents: candidates.filter((c) => c.name !== winner.name).map((c) => c.name),
|
|
2865
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2866
|
+
metadata: {
|
|
2867
|
+
strategy: "consensus",
|
|
2868
|
+
rounds: allRounds.length,
|
|
2869
|
+
consensusReached: false,
|
|
2870
|
+
tieBreaker: this.tieBreaker,
|
|
2871
|
+
finalTally: Object.fromEntries(lastRound.tally)
|
|
2872
|
+
}
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
context.emit({
|
|
2876
|
+
type: "consensus:reached",
|
|
2877
|
+
taskId: task.id,
|
|
2878
|
+
winner: finalResult.winner,
|
|
2879
|
+
rounds: allRounds.length,
|
|
2880
|
+
consensusReached: true,
|
|
2881
|
+
agreementRatio: finalResult.agreementRatio
|
|
2882
|
+
});
|
|
2883
|
+
return {
|
|
2884
|
+
selectedAgent: finalResult.winner,
|
|
2885
|
+
reason: `Consensus reached in round ${finalResult.round} with ${(finalResult.agreementRatio * 100).toFixed(1)}% agreement`,
|
|
2886
|
+
confidence: finalResult.agreementRatio,
|
|
2887
|
+
alternativeAgents: candidates.filter((c) => c.name !== finalResult.winner).map((c) => c.name),
|
|
2888
|
+
decisionTimeMs: Date.now() - startTime,
|
|
2889
|
+
metadata: {
|
|
2890
|
+
strategy: "consensus",
|
|
2891
|
+
rounds: allRounds.length,
|
|
2892
|
+
consensusReached: true,
|
|
2893
|
+
finalRound: finalResult,
|
|
2894
|
+
allRounds
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
/**
|
|
2899
|
+
* Run a single voting round
|
|
2900
|
+
*/
|
|
2901
|
+
async runVotingRound(round, task, voters, candidates, _context) {
|
|
2902
|
+
const votes = await this.collectVotes(task, voters, candidates);
|
|
2903
|
+
const tally = this.tallyVotes(votes);
|
|
2904
|
+
const totalWeight = votes.reduce((sum, v) => sum + v.weight, 0);
|
|
2905
|
+
const maxVotes = Math.max(...Array.from(tally.values()));
|
|
2906
|
+
const agreementRatio = totalWeight > 0 ? maxVotes / totalWeight : 0;
|
|
2907
|
+
const consensusReached = agreementRatio >= this.requiredAgreement;
|
|
2908
|
+
let winner;
|
|
2909
|
+
if (consensusReached) {
|
|
2910
|
+
for (const [candidate, voteCount] of tally) {
|
|
2911
|
+
if (voteCount === maxVotes) {
|
|
2912
|
+
winner = candidate;
|
|
2913
|
+
break;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return {
|
|
2918
|
+
round,
|
|
2919
|
+
votes,
|
|
2920
|
+
tally,
|
|
2921
|
+
winner,
|
|
2922
|
+
consensusReached,
|
|
2923
|
+
agreementRatio
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
/**
|
|
2927
|
+
* Collect votes from agents
|
|
2928
|
+
*/
|
|
2929
|
+
async collectVotes(task, voters, candidates) {
|
|
2930
|
+
const votes = [];
|
|
2931
|
+
const candidateNames = candidates.map((c) => c.name);
|
|
2932
|
+
for (const voter of voters) {
|
|
2933
|
+
const vote = await this.getVote(voter, task, candidateNames);
|
|
2934
|
+
if (vote) {
|
|
2935
|
+
votes.push(vote);
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
return votes;
|
|
2939
|
+
}
|
|
2940
|
+
/**
|
|
2941
|
+
* Get a vote from an agent
|
|
2942
|
+
*/
|
|
2943
|
+
getVote(voter, task, candidates) {
|
|
2944
|
+
const scores = [];
|
|
2945
|
+
for (const candidateName of candidates) {
|
|
2946
|
+
if (candidateName === voter.name) {
|
|
2947
|
+
scores.push({ name: candidateName, score: 0.7 });
|
|
2948
|
+
} else {
|
|
2949
|
+
const randomFactor = 0.8 + Math.random() * 0.4;
|
|
2950
|
+
scores.push({
|
|
2951
|
+
name: candidateName,
|
|
2952
|
+
score: Math.random() * randomFactor
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
scores.sort((a, b) => b.score - a.score);
|
|
2957
|
+
const selected = scores[0];
|
|
2958
|
+
if (!selected) return Promise.resolve(null);
|
|
2959
|
+
let weight = 1;
|
|
2960
|
+
if (this.weightedVoting) {
|
|
2961
|
+
weight = voter.calculateTaskScore(task) || 0.5;
|
|
2962
|
+
}
|
|
2963
|
+
return Promise.resolve({
|
|
2964
|
+
voter: voter.name,
|
|
2965
|
+
candidate: selected.name,
|
|
2966
|
+
weight,
|
|
2967
|
+
reasoning: `Selected based on perceived suitability (score: ${selected.score.toFixed(2)})`
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* Tally votes
|
|
2972
|
+
*/
|
|
2973
|
+
tallyVotes(votes) {
|
|
2974
|
+
const tally = /* @__PURE__ */ new Map();
|
|
2975
|
+
for (const vote of votes) {
|
|
2976
|
+
const current = tally.get(vote.candidate) ?? 0;
|
|
2977
|
+
tally.set(vote.candidate, current + vote.weight);
|
|
2978
|
+
}
|
|
2979
|
+
return tally;
|
|
2980
|
+
}
|
|
2981
|
+
/**
|
|
2982
|
+
* Break a tie between candidates
|
|
2983
|
+
*/
|
|
2984
|
+
breakTie(tally, candidates, task) {
|
|
2985
|
+
const maxVotes = Math.max(...Array.from(tally.values()));
|
|
2986
|
+
const tied = candidates.filter(
|
|
2987
|
+
(c) => (tally.get(c.name) ?? 0) === maxVotes
|
|
2988
|
+
);
|
|
2989
|
+
if (tied.length === 1) {
|
|
2990
|
+
return tied[0];
|
|
2991
|
+
}
|
|
2992
|
+
switch (this.tieBreaker) {
|
|
2993
|
+
case "random":
|
|
2994
|
+
return tied[Math.floor(Math.random() * tied.length)];
|
|
2995
|
+
case "first":
|
|
2996
|
+
return tied[0];
|
|
2997
|
+
case "manager":
|
|
2998
|
+
for (const candidate of tied) {
|
|
2999
|
+
if (candidate.role.canDelegate) {
|
|
3000
|
+
return candidate;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return tied[0];
|
|
3004
|
+
case "highest-capability":
|
|
3005
|
+
default: {
|
|
3006
|
+
const ranked = AgentCapabilities.rank(tied, task);
|
|
3007
|
+
if (ranked.length > 0) {
|
|
3008
|
+
const best = tied.find((c) => c.name === ranked[0].agentName);
|
|
3009
|
+
if (best) return best;
|
|
3010
|
+
}
|
|
3011
|
+
return tied[0];
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
/**
|
|
3016
|
+
* Run a full consensus process with deliberation
|
|
3017
|
+
*/
|
|
3018
|
+
async runWithDeliberation(task, agents, context) {
|
|
3019
|
+
const startTime = Date.now();
|
|
3020
|
+
const candidates = this.filterAvailable(agents);
|
|
3021
|
+
const allRounds = [];
|
|
3022
|
+
let consensusReached = false;
|
|
3023
|
+
for (let round = 1; round <= this.maxRounds; round++) {
|
|
3024
|
+
const roundResult = await this.runVotingRound(
|
|
3025
|
+
round,
|
|
3026
|
+
task,
|
|
3027
|
+
agents,
|
|
3028
|
+
candidates,
|
|
3029
|
+
context
|
|
3030
|
+
);
|
|
3031
|
+
allRounds.push(roundResult);
|
|
3032
|
+
context.emit({
|
|
3033
|
+
type: "delegation:decision",
|
|
3034
|
+
taskId: task.id,
|
|
3035
|
+
toAgent: roundResult.winner ?? "",
|
|
3036
|
+
strategy: "consensus",
|
|
3037
|
+
reason: `Round ${round}: ${roundResult.consensusReached ? "consensus" : "no consensus"}`,
|
|
3038
|
+
confidence: roundResult.agreementRatio
|
|
3039
|
+
});
|
|
3040
|
+
if (roundResult.consensusReached && roundResult.winner) {
|
|
3041
|
+
consensusReached = true;
|
|
3042
|
+
break;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
const lastRound = allRounds[allRounds.length - 1];
|
|
3046
|
+
const winner = lastRound.winner ?? this.breakTie(lastRound.tally, candidates, task).name;
|
|
3047
|
+
return {
|
|
3048
|
+
result: {
|
|
3049
|
+
selectedAgent: winner,
|
|
3050
|
+
reason: consensusReached ? `Consensus reached after ${allRounds.length} round(s)` : `No consensus, resolved by ${this.tieBreaker}`,
|
|
3051
|
+
confidence: lastRound.agreementRatio,
|
|
3052
|
+
decisionTimeMs: Date.now() - startTime,
|
|
3053
|
+
metadata: {
|
|
3054
|
+
strategy: "consensus",
|
|
3055
|
+
rounds: allRounds.length,
|
|
3056
|
+
consensusReached
|
|
3057
|
+
}
|
|
3058
|
+
},
|
|
3059
|
+
deliberation: allRounds,
|
|
3060
|
+
consensus: consensusReached
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
3064
|
+
function createConsensusStrategy(config) {
|
|
3065
|
+
return new ConsensusStrategy(config);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
// src/coordination/strategies/index.ts
|
|
3069
|
+
function createStrategy(type, config) {
|
|
3070
|
+
switch (type) {
|
|
3071
|
+
case "round-robin":
|
|
3072
|
+
return new RoundRobinStrategy(config);
|
|
3073
|
+
case "best-match":
|
|
3074
|
+
return new BestMatchStrategy(config);
|
|
3075
|
+
case "auction":
|
|
3076
|
+
return new AuctionStrategy(config);
|
|
3077
|
+
case "hierarchical":
|
|
3078
|
+
return new HierarchicalStrategy(config);
|
|
3079
|
+
case "consensus":
|
|
3080
|
+
return new ConsensusStrategy(config);
|
|
3081
|
+
default:
|
|
3082
|
+
throw new Error(`Unknown delegation strategy type: ${String(type)}`);
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
var STRATEGY_TYPES = [
|
|
3086
|
+
"round-robin",
|
|
3087
|
+
"best-match",
|
|
3088
|
+
"auction",
|
|
3089
|
+
"hierarchical",
|
|
3090
|
+
"consensus"
|
|
3091
|
+
];
|
|
3092
|
+
|
|
3093
|
+
// src/coordination/Delegation.ts
|
|
3094
|
+
var DelegationCoordinator = class {
|
|
3095
|
+
strategies = /* @__PURE__ */ new Map();
|
|
3096
|
+
defaultStrategy;
|
|
3097
|
+
enableFallback;
|
|
3098
|
+
fallbackOrder;
|
|
3099
|
+
maxAttempts;
|
|
3100
|
+
trackHistory;
|
|
3101
|
+
history = [];
|
|
3102
|
+
constructor(config = {}) {
|
|
3103
|
+
this.defaultStrategy = config.defaultStrategy ?? "best-match";
|
|
3104
|
+
this.enableFallback = config.enableFallback ?? true;
|
|
3105
|
+
this.fallbackOrder = config.fallbackOrder ?? [
|
|
3106
|
+
"best-match",
|
|
3107
|
+
"round-robin",
|
|
3108
|
+
"auction"
|
|
3109
|
+
];
|
|
3110
|
+
this.maxAttempts = config.maxAttempts ?? 3;
|
|
3111
|
+
this.trackHistory = config.trackHistory ?? true;
|
|
3112
|
+
for (const type of STRATEGY_TYPES) {
|
|
3113
|
+
const strategyConfig = config.strategyConfigs?.[type] ?? {};
|
|
3114
|
+
this.strategies.set(type, createStrategy(type, strategyConfig));
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Register a custom strategy
|
|
3119
|
+
*/
|
|
3120
|
+
registerStrategy(type, strategy) {
|
|
3121
|
+
this.strategies.set(type, strategy);
|
|
3122
|
+
}
|
|
3123
|
+
/**
|
|
3124
|
+
* Get a strategy by type
|
|
3125
|
+
*/
|
|
3126
|
+
getStrategy(type) {
|
|
3127
|
+
return this.strategies.get(type);
|
|
3128
|
+
}
|
|
3129
|
+
/**
|
|
3130
|
+
* Delegate a task to an agent
|
|
3131
|
+
*/
|
|
3132
|
+
async delegate(task, agents, context, strategyType) {
|
|
3133
|
+
const strategy = strategyType ?? this.defaultStrategy;
|
|
3134
|
+
let lastError;
|
|
3135
|
+
let attempt = 0;
|
|
3136
|
+
const strategyOrder = this.buildStrategyOrder(strategy);
|
|
3137
|
+
for (const currentStrategy of strategyOrder) {
|
|
3138
|
+
attempt++;
|
|
3139
|
+
if (attempt > this.maxAttempts) {
|
|
3140
|
+
break;
|
|
3141
|
+
}
|
|
3142
|
+
const delegationStrategy = this.strategies.get(currentStrategy);
|
|
3143
|
+
if (!delegationStrategy) {
|
|
3144
|
+
continue;
|
|
3145
|
+
}
|
|
3146
|
+
try {
|
|
3147
|
+
const result = await delegationStrategy.selectAgent(
|
|
3148
|
+
task,
|
|
3149
|
+
agents,
|
|
3150
|
+
context
|
|
3151
|
+
);
|
|
3152
|
+
if (this.trackHistory) {
|
|
3153
|
+
this.history.push({
|
|
3154
|
+
taskId: task.id,
|
|
3155
|
+
strategy: currentStrategy,
|
|
3156
|
+
result,
|
|
3157
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
3158
|
+
attempt
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
context.emit({
|
|
3162
|
+
type: "delegation:decision",
|
|
3163
|
+
taskId: task.id,
|
|
3164
|
+
toAgent: result.selectedAgent,
|
|
3165
|
+
strategy: currentStrategy,
|
|
3166
|
+
reason: result.reason,
|
|
3167
|
+
confidence: result.confidence,
|
|
3168
|
+
alternatives: result.alternativeAgents
|
|
3169
|
+
});
|
|
3170
|
+
return result;
|
|
3171
|
+
} catch (error) {
|
|
3172
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3173
|
+
context.emit({
|
|
3174
|
+
type: "delegation:failed",
|
|
3175
|
+
taskId: task.id,
|
|
3176
|
+
strategy: currentStrategy,
|
|
3177
|
+
reason: lastError.message,
|
|
3178
|
+
willRetry: this.enableFallback && attempt < this.maxAttempts
|
|
3179
|
+
});
|
|
3180
|
+
if (!this.enableFallback) {
|
|
3181
|
+
throw error;
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
throw new DelegationError(
|
|
3186
|
+
`Failed to delegate task after ${attempt} attempts`,
|
|
3187
|
+
agents,
|
|
3188
|
+
lastError?.message
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
/**
|
|
3192
|
+
* Delegate multiple tasks at once
|
|
3193
|
+
*/
|
|
3194
|
+
async delegateBatch(tasks, agents, context, strategyType) {
|
|
3195
|
+
const results = /* @__PURE__ */ new Map();
|
|
3196
|
+
for (const task of tasks) {
|
|
3197
|
+
try {
|
|
3198
|
+
const result = await this.delegate(task, agents, context, strategyType);
|
|
3199
|
+
results.set(task.id, result);
|
|
3200
|
+
} catch (error) {
|
|
3201
|
+
results.set(task.id, {
|
|
3202
|
+
selectedAgent: "",
|
|
3203
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
3204
|
+
confidence: 0,
|
|
3205
|
+
decisionTimeMs: 0,
|
|
3206
|
+
metadata: {
|
|
3207
|
+
failed: true,
|
|
3208
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3209
|
+
}
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
return results;
|
|
3214
|
+
}
|
|
3215
|
+
/**
|
|
3216
|
+
* Find the best agent for a task without delegating
|
|
3217
|
+
*/
|
|
3218
|
+
async findBestAgent(task, agents, context) {
|
|
3219
|
+
try {
|
|
3220
|
+
const result = await this.delegate(task, agents, context, "best-match");
|
|
3221
|
+
return agents.find((a) => a.name === result.selectedAgent);
|
|
3222
|
+
} catch {
|
|
3223
|
+
return void 0;
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
/**
|
|
3227
|
+
* Get delegation recommendations for a task
|
|
3228
|
+
*/
|
|
3229
|
+
async getRecommendations(task, agents, context, limit = 3) {
|
|
3230
|
+
const recommendations = [];
|
|
3231
|
+
for (const [strategyType, strategy] of this.strategies) {
|
|
3232
|
+
try {
|
|
3233
|
+
const result = await strategy.selectAgent(task, agents, context);
|
|
3234
|
+
const agent = agents.find((a) => a.name === result.selectedAgent);
|
|
3235
|
+
if (agent) {
|
|
3236
|
+
const existing = recommendations.find(
|
|
3237
|
+
(r) => r.agent.name === agent.name
|
|
3238
|
+
);
|
|
3239
|
+
if (!existing) {
|
|
3240
|
+
recommendations.push({
|
|
3241
|
+
agent,
|
|
3242
|
+
score: result.confidence,
|
|
3243
|
+
reason: `${strategyType}: ${result.reason}`
|
|
3244
|
+
});
|
|
3245
|
+
} else {
|
|
3246
|
+
if (result.confidence > existing.score) {
|
|
3247
|
+
existing.score = result.confidence;
|
|
3248
|
+
existing.reason = `${strategyType}: ${result.reason}`;
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
} catch {
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
return recommendations.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
3256
|
+
}
|
|
3257
|
+
/**
|
|
3258
|
+
* Get delegation history
|
|
3259
|
+
*/
|
|
3260
|
+
getHistory(taskId) {
|
|
3261
|
+
if (taskId) {
|
|
3262
|
+
return this.history.filter((h) => h.taskId === taskId);
|
|
3263
|
+
}
|
|
3264
|
+
return [...this.history];
|
|
3265
|
+
}
|
|
3266
|
+
/**
|
|
3267
|
+
* Get delegation statistics
|
|
3268
|
+
*/
|
|
3269
|
+
getStatistics() {
|
|
3270
|
+
const byStrategy = {};
|
|
3271
|
+
let totalConfidence = 0;
|
|
3272
|
+
let totalAttempts = 0;
|
|
3273
|
+
for (const entry of this.history) {
|
|
3274
|
+
byStrategy[entry.strategy] = (byStrategy[entry.strategy] ?? 0) + 1;
|
|
3275
|
+
totalConfidence += entry.result.confidence;
|
|
3276
|
+
totalAttempts += entry.attempt;
|
|
3277
|
+
}
|
|
3278
|
+
return {
|
|
3279
|
+
totalDelegations: this.history.length,
|
|
3280
|
+
byStrategy,
|
|
3281
|
+
averageConfidence: this.history.length > 0 ? totalConfidence / this.history.length : 0,
|
|
3282
|
+
averageAttempts: this.history.length > 0 ? totalAttempts / this.history.length : 0
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
/**
|
|
3286
|
+
* Clear delegation history
|
|
3287
|
+
*/
|
|
3288
|
+
clearHistory() {
|
|
3289
|
+
this.history.length = 0;
|
|
3290
|
+
}
|
|
3291
|
+
/**
|
|
3292
|
+
* Reset all strategies
|
|
3293
|
+
*/
|
|
3294
|
+
reset() {
|
|
3295
|
+
for (const strategy of this.strategies.values()) {
|
|
3296
|
+
strategy.reset?.();
|
|
3297
|
+
}
|
|
3298
|
+
this.clearHistory();
|
|
3299
|
+
}
|
|
3300
|
+
/**
|
|
3301
|
+
* Build the strategy order for delegation
|
|
3302
|
+
*/
|
|
3303
|
+
buildStrategyOrder(primary) {
|
|
3304
|
+
const order = [primary];
|
|
3305
|
+
if (this.enableFallback) {
|
|
3306
|
+
for (const fallback of this.fallbackOrder) {
|
|
3307
|
+
if (!order.includes(fallback)) {
|
|
3308
|
+
order.push(fallback);
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
return order;
|
|
3313
|
+
}
|
|
3314
|
+
};
|
|
3315
|
+
function createDelegationCoordinator(config) {
|
|
3316
|
+
return new DelegationCoordinator(config);
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
// src/coordination/Collaboration.ts
|
|
3320
|
+
var CollaborationManager = class {
|
|
3321
|
+
agents = /* @__PURE__ */ new Map();
|
|
3322
|
+
channels = /* @__PURE__ */ new Map();
|
|
3323
|
+
pendingHelpRequests = /* @__PURE__ */ new Map();
|
|
3324
|
+
knowledge = [];
|
|
3325
|
+
config;
|
|
3326
|
+
messageCounter = 0;
|
|
3327
|
+
constructor(config = {}) {
|
|
3328
|
+
this.config = {
|
|
3329
|
+
persistMessages: config.persistMessages ?? true,
|
|
3330
|
+
maxMessagesPerChannel: config.maxMessagesPerChannel ?? 1e3,
|
|
3331
|
+
helpRequestTimeoutMs: config.helpRequestTimeoutMs ?? 3e4,
|
|
3332
|
+
autoExtractKnowledge: config.autoExtractKnowledge ?? false
|
|
3333
|
+
};
|
|
3334
|
+
this.createChannel("broadcast", []);
|
|
3335
|
+
}
|
|
3336
|
+
/**
|
|
3337
|
+
* Register an agent for collaboration
|
|
3338
|
+
*/
|
|
3339
|
+
registerAgent(agent) {
|
|
3340
|
+
this.agents.set(agent.name, agent);
|
|
3341
|
+
}
|
|
3342
|
+
/**
|
|
3343
|
+
* Unregister an agent
|
|
3344
|
+
*/
|
|
3345
|
+
unregisterAgent(agentName) {
|
|
3346
|
+
this.agents.delete(agentName);
|
|
3347
|
+
}
|
|
3348
|
+
/**
|
|
3349
|
+
* Create a collaboration channel
|
|
3350
|
+
*/
|
|
3351
|
+
createChannel(name, participants) {
|
|
3352
|
+
const channel = {
|
|
3353
|
+
name,
|
|
3354
|
+
participants,
|
|
3355
|
+
messages: [],
|
|
3356
|
+
created: /* @__PURE__ */ new Date()
|
|
3357
|
+
};
|
|
3358
|
+
this.channels.set(name, channel);
|
|
3359
|
+
return channel;
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* Send a message between agents
|
|
3363
|
+
*/
|
|
3364
|
+
sendMessage(from, to, content, context, options = {}) {
|
|
3365
|
+
const message = {
|
|
3366
|
+
id: `msg-${++this.messageCounter}`,
|
|
3367
|
+
type: options.type ?? "request",
|
|
3368
|
+
from,
|
|
3369
|
+
to,
|
|
3370
|
+
content,
|
|
3371
|
+
metadata: options.metadata,
|
|
3372
|
+
replyTo: options.replyTo,
|
|
3373
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3374
|
+
};
|
|
3375
|
+
if (this.config.persistMessages) {
|
|
3376
|
+
const channelName = to === "all" ? "broadcast" : `${from}-${to}`;
|
|
3377
|
+
let channel = this.channels.get(channelName);
|
|
3378
|
+
if (!channel) {
|
|
3379
|
+
channel = this.createChannel(channelName, [from, to]);
|
|
3380
|
+
}
|
|
3381
|
+
channel.messages.push(message);
|
|
3382
|
+
if (channel.messages.length > this.config.maxMessagesPerChannel) {
|
|
3383
|
+
channel.messages = channel.messages.slice(
|
|
3384
|
+
-this.config.maxMessagesPerChannel
|
|
3385
|
+
);
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
context.emit({
|
|
3389
|
+
type: "collaboration:message",
|
|
3390
|
+
from,
|
|
3391
|
+
to,
|
|
3392
|
+
messageType: message.type,
|
|
3393
|
+
content
|
|
3394
|
+
});
|
|
3395
|
+
return Promise.resolve(message);
|
|
3396
|
+
}
|
|
3397
|
+
/**
|
|
3398
|
+
* Broadcast a message to all agents
|
|
3399
|
+
*/
|
|
3400
|
+
async broadcast(from, content, context) {
|
|
3401
|
+
return this.sendMessage(from, "all", content, context, {
|
|
3402
|
+
type: "broadcast"
|
|
3403
|
+
});
|
|
3404
|
+
}
|
|
3405
|
+
/**
|
|
3406
|
+
* Request help from other agents
|
|
3407
|
+
*/
|
|
3408
|
+
async requestHelp(requester, task, question, context, priority = "normal") {
|
|
3409
|
+
const request = {
|
|
3410
|
+
id: `help-${++this.messageCounter}`,
|
|
3411
|
+
requester,
|
|
3412
|
+
task,
|
|
3413
|
+
question,
|
|
3414
|
+
priority,
|
|
3415
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3416
|
+
};
|
|
3417
|
+
this.pendingHelpRequests.set(request.id, request);
|
|
3418
|
+
context.emit({
|
|
3419
|
+
type: "collaboration:help_request",
|
|
3420
|
+
requestId: request.id,
|
|
3421
|
+
requester,
|
|
3422
|
+
taskId: task.id,
|
|
3423
|
+
question
|
|
3424
|
+
});
|
|
3425
|
+
const responses = [];
|
|
3426
|
+
const otherAgents = Array.from(this.agents.values()).filter(
|
|
3427
|
+
(a) => a.name !== requester && !a.isBusy
|
|
3428
|
+
);
|
|
3429
|
+
const helpPromises = otherAgents.map(async (agent) => {
|
|
3430
|
+
try {
|
|
3431
|
+
const response = await this.getHelpFromAgent(agent, request, context);
|
|
3432
|
+
return response;
|
|
3433
|
+
} catch {
|
|
3434
|
+
return null;
|
|
3435
|
+
}
|
|
3436
|
+
});
|
|
3437
|
+
const results = await Promise.race([
|
|
3438
|
+
Promise.allSettled(helpPromises),
|
|
3439
|
+
new Promise(
|
|
3440
|
+
(resolve) => setTimeout(() => resolve([]), this.config.helpRequestTimeoutMs)
|
|
3441
|
+
)
|
|
3442
|
+
]);
|
|
3443
|
+
for (const result of results) {
|
|
3444
|
+
if (result.status === "fulfilled" && result.value) {
|
|
3445
|
+
responses.push(result.value);
|
|
3446
|
+
context.emit({
|
|
3447
|
+
type: "collaboration:help_response",
|
|
3448
|
+
requestId: request.id,
|
|
3449
|
+
responder: result.value.responder,
|
|
3450
|
+
helpful: result.value.helpful
|
|
3451
|
+
});
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
this.pendingHelpRequests.delete(request.id);
|
|
3455
|
+
return responses;
|
|
3456
|
+
}
|
|
3457
|
+
/**
|
|
3458
|
+
* Get help from a specific agent
|
|
3459
|
+
*/
|
|
3460
|
+
async getHelpFromAgent(agent, request, context) {
|
|
3461
|
+
const response = await agent.provideHelp(
|
|
3462
|
+
request.task,
|
|
3463
|
+
request.question,
|
|
3464
|
+
context
|
|
3465
|
+
);
|
|
3466
|
+
return {
|
|
3467
|
+
requestId: request.id,
|
|
3468
|
+
responder: agent.name,
|
|
3469
|
+
helpful: response.helpful,
|
|
3470
|
+
response: response.response,
|
|
3471
|
+
suggestions: response.suggestions,
|
|
3472
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3473
|
+
};
|
|
3474
|
+
}
|
|
3475
|
+
/**
|
|
3476
|
+
* Share knowledge with the crew
|
|
3477
|
+
*/
|
|
3478
|
+
shareKnowledge(contributor, type, content, tags, context, confidence = 0.8) {
|
|
3479
|
+
const knowledge = {
|
|
3480
|
+
id: `know-${++this.messageCounter}`,
|
|
3481
|
+
contributor,
|
|
3482
|
+
type,
|
|
3483
|
+
content,
|
|
3484
|
+
tags,
|
|
3485
|
+
confidence,
|
|
3486
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3487
|
+
};
|
|
3488
|
+
this.knowledge.push(knowledge);
|
|
3489
|
+
context.emit({
|
|
3490
|
+
type: "collaboration:knowledge_shared",
|
|
3491
|
+
contributor,
|
|
3492
|
+
knowledgeId: knowledge.id,
|
|
3493
|
+
knowledgeType: type
|
|
3494
|
+
});
|
|
3495
|
+
return knowledge;
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Search shared knowledge
|
|
3499
|
+
*/
|
|
3500
|
+
searchKnowledge(query, limit = 10) {
|
|
3501
|
+
const queryLower = query.toLowerCase();
|
|
3502
|
+
const results = [];
|
|
3503
|
+
for (const item of this.knowledge) {
|
|
3504
|
+
let score = 0;
|
|
3505
|
+
if (item.content.toLowerCase().includes(queryLower)) {
|
|
3506
|
+
score += 0.5;
|
|
3507
|
+
}
|
|
3508
|
+
for (const tag of item.tags) {
|
|
3509
|
+
if (tag.toLowerCase().includes(queryLower)) {
|
|
3510
|
+
score += 0.3;
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
if (score > 0) {
|
|
3514
|
+
results.push({ knowledge: item, score: score * item.confidence });
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit).map((r) => r.knowledge);
|
|
3518
|
+
}
|
|
3519
|
+
/**
|
|
3520
|
+
* Get knowledge by type
|
|
3521
|
+
*/
|
|
3522
|
+
getKnowledgeByType(type) {
|
|
3523
|
+
return this.knowledge.filter((k) => k.type === type);
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3526
|
+
* Get knowledge by contributor
|
|
3527
|
+
*/
|
|
3528
|
+
getKnowledgeByContributor(contributor) {
|
|
3529
|
+
return this.knowledge.filter((k) => k.contributor === contributor);
|
|
3530
|
+
}
|
|
3531
|
+
/**
|
|
3532
|
+
* Get conversation history between agents
|
|
3533
|
+
*/
|
|
3534
|
+
getConversation(agent1, agent2) {
|
|
3535
|
+
const channelName1 = `${agent1}-${agent2}`;
|
|
3536
|
+
const channelName2 = `${agent2}-${agent1}`;
|
|
3537
|
+
const messages = [];
|
|
3538
|
+
const channel1 = this.channels.get(channelName1);
|
|
3539
|
+
if (channel1) {
|
|
3540
|
+
messages.push(...channel1.messages);
|
|
3541
|
+
}
|
|
3542
|
+
const channel2 = this.channels.get(channelName2);
|
|
3543
|
+
if (channel2) {
|
|
3544
|
+
messages.push(...channel2.messages);
|
|
3545
|
+
}
|
|
3546
|
+
return messages.sort(
|
|
3547
|
+
(a, b) => a.timestamp.getTime() - b.timestamp.getTime()
|
|
3548
|
+
);
|
|
3549
|
+
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Get all channels an agent participates in
|
|
3552
|
+
*/
|
|
3553
|
+
getAgentChannels(agentName) {
|
|
3554
|
+
return Array.from(this.channels.values()).filter(
|
|
3555
|
+
(c) => c.participants.includes(agentName) || c.name === "broadcast"
|
|
3556
|
+
);
|
|
3557
|
+
}
|
|
3558
|
+
/**
|
|
3559
|
+
* Clear all collaboration data
|
|
3560
|
+
*/
|
|
3561
|
+
clear() {
|
|
3562
|
+
this.channels.clear();
|
|
3563
|
+
this.pendingHelpRequests.clear();
|
|
3564
|
+
this.knowledge.length = 0;
|
|
3565
|
+
this.createChannel("broadcast", []);
|
|
3566
|
+
}
|
|
3567
|
+
/**
|
|
3568
|
+
* Get collaboration statistics
|
|
3569
|
+
*/
|
|
3570
|
+
getStatistics() {
|
|
3571
|
+
const messagesByAgent = {};
|
|
3572
|
+
const knowledgeByType = {};
|
|
3573
|
+
let totalMessages = 0;
|
|
3574
|
+
for (const channel of this.channels.values()) {
|
|
3575
|
+
for (const message of channel.messages) {
|
|
3576
|
+
totalMessages++;
|
|
3577
|
+
messagesByAgent[message.from] = (messagesByAgent[message.from] ?? 0) + 1;
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
for (const item of this.knowledge) {
|
|
3581
|
+
knowledgeByType[item.type] = (knowledgeByType[item.type] ?? 0) + 1;
|
|
3582
|
+
}
|
|
3583
|
+
return {
|
|
3584
|
+
totalMessages,
|
|
3585
|
+
totalChannels: this.channels.size,
|
|
3586
|
+
totalKnowledge: this.knowledge.length,
|
|
3587
|
+
messagesByAgent,
|
|
3588
|
+
knowledgeByType
|
|
3589
|
+
};
|
|
3590
|
+
}
|
|
3591
|
+
};
|
|
3592
|
+
function createCollaborationManager(config) {
|
|
3593
|
+
return new CollaborationManager(config);
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
// src/coordination/ConflictResolution.ts
|
|
3597
|
+
var ConflictResolver = class {
|
|
3598
|
+
config;
|
|
3599
|
+
history = [];
|
|
3600
|
+
conflictCounts = /* @__PURE__ */ new Map();
|
|
3601
|
+
conflictCounter = 0;
|
|
3602
|
+
constructor(config = {}) {
|
|
3603
|
+
this.config = {
|
|
3604
|
+
defaultStrategy: config.defaultStrategy ?? "highest-confidence",
|
|
3605
|
+
autoResolveThreshold: config.autoResolveThreshold ?? 0.3,
|
|
3606
|
+
autoDetect: config.autoDetect ?? true,
|
|
3607
|
+
escalateOnRepeated: config.escalateOnRepeated ?? 3,
|
|
3608
|
+
trackHistory: config.trackHistory ?? true
|
|
3609
|
+
};
|
|
3610
|
+
}
|
|
3611
|
+
/**
|
|
3612
|
+
* Detect conflicts between agent responses
|
|
3613
|
+
*/
|
|
3614
|
+
detectConflict(responses, task, context) {
|
|
3615
|
+
if (responses.length < 2) {
|
|
3616
|
+
return null;
|
|
3617
|
+
}
|
|
3618
|
+
const disagreement = this.detectDisagreement(responses);
|
|
3619
|
+
if (disagreement) {
|
|
3620
|
+
const conflict = this.createConflict(
|
|
3621
|
+
"disagreement",
|
|
3622
|
+
disagreement.description,
|
|
3623
|
+
responses,
|
|
3624
|
+
task,
|
|
3625
|
+
disagreement.severity
|
|
3626
|
+
);
|
|
3627
|
+
context.emit({
|
|
3628
|
+
type: "conflict:detected",
|
|
3629
|
+
conflictId: conflict.id,
|
|
3630
|
+
conflictType: "disagreement",
|
|
3631
|
+
participants: conflict.participants,
|
|
3632
|
+
taskId: task.id
|
|
3633
|
+
});
|
|
3634
|
+
return conflict;
|
|
3635
|
+
}
|
|
3636
|
+
const contradiction = this.detectContradiction(responses);
|
|
3637
|
+
if (contradiction) {
|
|
3638
|
+
const conflict = this.createConflict(
|
|
3639
|
+
"assertion",
|
|
3640
|
+
contradiction.description,
|
|
3641
|
+
responses,
|
|
3642
|
+
task,
|
|
3643
|
+
contradiction.severity
|
|
3644
|
+
);
|
|
3645
|
+
context.emit({
|
|
3646
|
+
type: "conflict:detected",
|
|
3647
|
+
conflictId: conflict.id,
|
|
3648
|
+
conflictType: "assertion",
|
|
3649
|
+
participants: conflict.participants,
|
|
3650
|
+
taskId: task.id
|
|
3651
|
+
});
|
|
3652
|
+
return conflict;
|
|
3653
|
+
}
|
|
3654
|
+
return null;
|
|
3655
|
+
}
|
|
3656
|
+
/**
|
|
3657
|
+
* Resolve a conflict
|
|
3658
|
+
*/
|
|
3659
|
+
async resolve(conflict, context, strategy) {
|
|
3660
|
+
const resolveStrategy = strategy ?? this.config.defaultStrategy;
|
|
3661
|
+
const conflictKey = `${conflict.type}-${conflict.participants.sort().join(",")}`;
|
|
3662
|
+
const repeatCount = (this.conflictCounts.get(conflictKey) ?? 0) + 1;
|
|
3663
|
+
this.conflictCounts.set(conflictKey, repeatCount);
|
|
3664
|
+
if (repeatCount >= this.config.escalateOnRepeated) {
|
|
3665
|
+
return this.escalate(
|
|
3666
|
+
conflict,
|
|
3667
|
+
context,
|
|
3668
|
+
"Repeated conflicts between same agents"
|
|
3669
|
+
);
|
|
3670
|
+
}
|
|
3671
|
+
let resolution;
|
|
3672
|
+
switch (resolveStrategy) {
|
|
3673
|
+
case "voting":
|
|
3674
|
+
resolution = await this.resolveByVoting(conflict, context);
|
|
3675
|
+
break;
|
|
3676
|
+
case "authority":
|
|
3677
|
+
resolution = await this.resolveByAuthority(conflict, context);
|
|
3678
|
+
break;
|
|
3679
|
+
case "consensus":
|
|
3680
|
+
resolution = await this.resolveByConsensus(conflict, context);
|
|
3681
|
+
break;
|
|
3682
|
+
case "merge":
|
|
3683
|
+
resolution = await this.resolveByMerge(conflict, context);
|
|
3684
|
+
break;
|
|
3685
|
+
case "human":
|
|
3686
|
+
resolution = await this.escalate(
|
|
3687
|
+
conflict,
|
|
3688
|
+
context,
|
|
3689
|
+
"Human resolution requested"
|
|
3690
|
+
);
|
|
3691
|
+
break;
|
|
3692
|
+
case "newest":
|
|
3693
|
+
resolution = this.resolveByNewest(conflict);
|
|
3694
|
+
break;
|
|
3695
|
+
case "highest-confidence":
|
|
3696
|
+
default:
|
|
3697
|
+
resolution = this.resolveByConfidence(conflict);
|
|
3698
|
+
break;
|
|
3699
|
+
}
|
|
3700
|
+
if (this.config.trackHistory) {
|
|
3701
|
+
this.history.push(resolution);
|
|
3702
|
+
}
|
|
3703
|
+
context.emit({
|
|
3704
|
+
type: "conflict:resolved",
|
|
3705
|
+
conflictId: conflict.id,
|
|
3706
|
+
strategy: resolveStrategy,
|
|
3707
|
+
winner: resolution.winner?.agentName,
|
|
3708
|
+
escalated: resolution.escalated
|
|
3709
|
+
});
|
|
3710
|
+
return resolution;
|
|
3711
|
+
}
|
|
3712
|
+
/**
|
|
3713
|
+
* Create a conflict object
|
|
3714
|
+
*/
|
|
3715
|
+
createConflict(type, description, responses, task, severity) {
|
|
3716
|
+
return {
|
|
3717
|
+
id: `conflict-${++this.conflictCounter}`,
|
|
3718
|
+
type,
|
|
3719
|
+
description,
|
|
3720
|
+
participants: responses.map((r) => r.agentName),
|
|
3721
|
+
responses,
|
|
3722
|
+
task,
|
|
3723
|
+
severity,
|
|
3724
|
+
detected: /* @__PURE__ */ new Date()
|
|
3725
|
+
};
|
|
3726
|
+
}
|
|
3727
|
+
/**
|
|
3728
|
+
* Detect disagreement between responses
|
|
3729
|
+
*/
|
|
3730
|
+
detectDisagreement(responses) {
|
|
3731
|
+
const contents = responses.map((r) => r.content.toLowerCase());
|
|
3732
|
+
const lengths = contents.map((c) => c.length);
|
|
3733
|
+
const avgLength = lengths.reduce((a, b) => a + b, 0) / lengths.length;
|
|
3734
|
+
const maxDiff = Math.max(...lengths.map((l) => Math.abs(l - avgLength)));
|
|
3735
|
+
if (maxDiff > avgLength * 0.5) {
|
|
3736
|
+
return {
|
|
3737
|
+
description: "Responses have significantly different lengths",
|
|
3738
|
+
severity: "low"
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3741
|
+
const contradictoryPairs = [
|
|
3742
|
+
["yes", "no"],
|
|
3743
|
+
["true", "false"],
|
|
3744
|
+
["correct", "incorrect"],
|
|
3745
|
+
["should", "should not"],
|
|
3746
|
+
["can", "cannot"],
|
|
3747
|
+
["will", "will not"],
|
|
3748
|
+
["agree", "disagree"]
|
|
3749
|
+
];
|
|
3750
|
+
for (const [word1, word2] of contradictoryPairs) {
|
|
3751
|
+
const hasWord1 = contents.some((c) => c.includes(word1));
|
|
3752
|
+
const hasWord2 = contents.some((c) => c.includes(word2));
|
|
3753
|
+
if (hasWord1 && hasWord2) {
|
|
3754
|
+
return {
|
|
3755
|
+
description: `Contradictory statements detected (${word1} vs ${word2})`,
|
|
3756
|
+
severity: "medium"
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
const confidences = responses.map((r) => r.confidence);
|
|
3761
|
+
const minConfidence = Math.min(...confidences);
|
|
3762
|
+
const maxConfidence = Math.max(...confidences);
|
|
3763
|
+
if (maxConfidence - minConfidence > this.config.autoResolveThreshold) {
|
|
3764
|
+
return {
|
|
3765
|
+
description: "Large confidence spread between responses",
|
|
3766
|
+
severity: "low"
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3769
|
+
return null;
|
|
3770
|
+
}
|
|
3771
|
+
/**
|
|
3772
|
+
* Detect contradictory assertions
|
|
3773
|
+
*/
|
|
3774
|
+
detectContradiction(responses) {
|
|
3775
|
+
for (let i = 0; i < responses.length; i++) {
|
|
3776
|
+
for (let j = i + 1; j < responses.length; j++) {
|
|
3777
|
+
const r1 = responses[i];
|
|
3778
|
+
const r2 = responses[j];
|
|
3779
|
+
const assertions1 = r1.metadata?.assertions;
|
|
3780
|
+
const assertions2 = r2.metadata?.assertions;
|
|
3781
|
+
if (assertions1 && assertions2) {
|
|
3782
|
+
for (const a1 of assertions1) {
|
|
3783
|
+
for (const a2 of assertions2) {
|
|
3784
|
+
if (this.areContradictory(a1, a2)) {
|
|
3785
|
+
return {
|
|
3786
|
+
description: `Contradictory assertions: "${a1}" vs "${a2}"`,
|
|
3787
|
+
severity: "high"
|
|
3788
|
+
};
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
return null;
|
|
3796
|
+
}
|
|
3797
|
+
/**
|
|
3798
|
+
* Check if two assertions are contradictory
|
|
3799
|
+
*/
|
|
3800
|
+
areContradictory(a1, a2) {
|
|
3801
|
+
const norm1 = a1.toLowerCase().trim();
|
|
3802
|
+
const norm2 = a2.toLowerCase().trim();
|
|
3803
|
+
if (norm1.startsWith("not ") && norm1.slice(4) === norm2) return true;
|
|
3804
|
+
if (norm2.startsWith("not ") && norm2.slice(4) === norm1) return true;
|
|
3805
|
+
return false;
|
|
3806
|
+
}
|
|
3807
|
+
/**
|
|
3808
|
+
* Resolve by highest confidence
|
|
3809
|
+
*/
|
|
3810
|
+
resolveByConfidence(conflict) {
|
|
3811
|
+
const sorted = [...conflict.responses].sort(
|
|
3812
|
+
(a, b) => b.confidence - a.confidence
|
|
3813
|
+
);
|
|
3814
|
+
const winner = sorted[0];
|
|
3815
|
+
return {
|
|
3816
|
+
conflictId: conflict.id,
|
|
3817
|
+
strategy: "highest-confidence",
|
|
3818
|
+
winner,
|
|
3819
|
+
explanation: `Selected response with highest confidence (${(winner.confidence * 100).toFixed(1)}%)`,
|
|
3820
|
+
successful: true,
|
|
3821
|
+
escalated: false,
|
|
3822
|
+
resolved: /* @__PURE__ */ new Date()
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
* Resolve by newest response
|
|
3827
|
+
*/
|
|
3828
|
+
resolveByNewest(conflict) {
|
|
3829
|
+
const winner = conflict.responses[conflict.responses.length - 1];
|
|
3830
|
+
return {
|
|
3831
|
+
conflictId: conflict.id,
|
|
3832
|
+
strategy: "newest",
|
|
3833
|
+
winner,
|
|
3834
|
+
explanation: "Selected most recent response",
|
|
3835
|
+
successful: true,
|
|
3836
|
+
escalated: false,
|
|
3837
|
+
resolved: /* @__PURE__ */ new Date()
|
|
3838
|
+
};
|
|
3839
|
+
}
|
|
3840
|
+
/**
|
|
3841
|
+
* Resolve by voting
|
|
3842
|
+
*/
|
|
3843
|
+
resolveByVoting(conflict, _context) {
|
|
3844
|
+
const votes = /* @__PURE__ */ new Map();
|
|
3845
|
+
for (const response of conflict.responses) {
|
|
3846
|
+
const key = response.content.substring(0, 100);
|
|
3847
|
+
const current = votes.get(key) ?? 0;
|
|
3848
|
+
votes.set(key, current + response.confidence);
|
|
3849
|
+
}
|
|
3850
|
+
let maxVotes = 0;
|
|
3851
|
+
let winningKey = "";
|
|
3852
|
+
for (const [key, voteCount] of votes) {
|
|
3853
|
+
if (voteCount > maxVotes) {
|
|
3854
|
+
maxVotes = voteCount;
|
|
3855
|
+
winningKey = key;
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
const winner = conflict.responses.find(
|
|
3859
|
+
(r) => r.content.substring(0, 100) === winningKey
|
|
3860
|
+
);
|
|
3861
|
+
return Promise.resolve({
|
|
3862
|
+
conflictId: conflict.id,
|
|
3863
|
+
strategy: "voting",
|
|
3864
|
+
winner,
|
|
3865
|
+
explanation: `Selected by weighted voting (${maxVotes.toFixed(2)} total weight)`,
|
|
3866
|
+
successful: !!winner,
|
|
3867
|
+
escalated: false,
|
|
3868
|
+
resolved: /* @__PURE__ */ new Date()
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
/**
|
|
3872
|
+
* Resolve by authority
|
|
3873
|
+
*/
|
|
3874
|
+
resolveByAuthority(conflict, _context) {
|
|
3875
|
+
return Promise.resolve(this.resolveByConfidence(conflict));
|
|
3876
|
+
}
|
|
3877
|
+
/**
|
|
3878
|
+
* Resolve by consensus
|
|
3879
|
+
*/
|
|
3880
|
+
resolveByConsensus(conflict, _context) {
|
|
3881
|
+
const contentGroups = /* @__PURE__ */ new Map();
|
|
3882
|
+
for (const response of conflict.responses) {
|
|
3883
|
+
const key = response.content.substring(0, 100);
|
|
3884
|
+
const group = contentGroups.get(key) ?? [];
|
|
3885
|
+
group.push(response);
|
|
3886
|
+
contentGroups.set(key, group);
|
|
3887
|
+
}
|
|
3888
|
+
let largestGroup = [];
|
|
3889
|
+
for (const group of contentGroups.values()) {
|
|
3890
|
+
if (group.length > largestGroup.length) {
|
|
3891
|
+
largestGroup = group;
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
if (largestGroup.length > conflict.responses.length / 2) {
|
|
3895
|
+
const winner = largestGroup.reduce(
|
|
3896
|
+
(best, r) => r.confidence > best.confidence ? r : best
|
|
3897
|
+
);
|
|
3898
|
+
return Promise.resolve({
|
|
3899
|
+
conflictId: conflict.id,
|
|
3900
|
+
strategy: "consensus",
|
|
3901
|
+
winner,
|
|
3902
|
+
explanation: `Consensus reached with ${largestGroup.length}/${conflict.responses.length} agreement`,
|
|
3903
|
+
successful: true,
|
|
3904
|
+
escalated: false,
|
|
3905
|
+
resolved: /* @__PURE__ */ new Date()
|
|
3906
|
+
});
|
|
3907
|
+
}
|
|
3908
|
+
return Promise.resolve({
|
|
3909
|
+
...this.resolveByConfidence(conflict),
|
|
3910
|
+
strategy: "consensus",
|
|
3911
|
+
explanation: "No consensus reached, fell back to highest confidence"
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
/**
|
|
3915
|
+
* Resolve by merging responses
|
|
3916
|
+
*/
|
|
3917
|
+
resolveByMerge(conflict, _context) {
|
|
3918
|
+
const mergedParts = [];
|
|
3919
|
+
const usedResponses = [];
|
|
3920
|
+
for (const response of conflict.responses) {
|
|
3921
|
+
const contentLower = response.content.toLowerCase();
|
|
3922
|
+
const isNew = !mergedParts.some(
|
|
3923
|
+
(part) => part.toLowerCase().includes(contentLower) || contentLower.includes(part.toLowerCase())
|
|
3924
|
+
);
|
|
3925
|
+
if (isNew) {
|
|
3926
|
+
mergedParts.push(response.content);
|
|
3927
|
+
usedResponses.push(response.agentName);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
if (mergedParts.length > 1) {
|
|
3931
|
+
return Promise.resolve({
|
|
3932
|
+
conflictId: conflict.id,
|
|
3933
|
+
strategy: "merge",
|
|
3934
|
+
merged: mergedParts.join("\n\n"),
|
|
3935
|
+
explanation: `Merged responses from ${usedResponses.join(", ")}`,
|
|
3936
|
+
successful: true,
|
|
3937
|
+
escalated: false,
|
|
3938
|
+
resolved: /* @__PURE__ */ new Date()
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
return Promise.resolve({
|
|
3942
|
+
...this.resolveByConfidence(conflict),
|
|
3943
|
+
strategy: "merge",
|
|
3944
|
+
explanation: "Could not merge responses, fell back to highest confidence"
|
|
3945
|
+
});
|
|
3946
|
+
}
|
|
3947
|
+
/**
|
|
3948
|
+
* Escalate to human
|
|
3949
|
+
*/
|
|
3950
|
+
escalate(conflict, context, reason) {
|
|
3951
|
+
context.emit({
|
|
3952
|
+
type: "conflict:escalated",
|
|
3953
|
+
conflictId: conflict.id,
|
|
3954
|
+
reason,
|
|
3955
|
+
participants: conflict.participants
|
|
3956
|
+
});
|
|
3957
|
+
return Promise.resolve({
|
|
3958
|
+
conflictId: conflict.id,
|
|
3959
|
+
strategy: "human",
|
|
3960
|
+
explanation: `Escalated to human: ${reason}`,
|
|
3961
|
+
successful: false,
|
|
3962
|
+
escalated: true,
|
|
3963
|
+
resolved: /* @__PURE__ */ new Date()
|
|
3964
|
+
});
|
|
3965
|
+
}
|
|
3966
|
+
/**
|
|
3967
|
+
* Get resolution history
|
|
3968
|
+
*/
|
|
3969
|
+
getHistory(conflictId) {
|
|
3970
|
+
if (conflictId) {
|
|
3971
|
+
return this.history.filter((r) => r.conflictId === conflictId);
|
|
3972
|
+
}
|
|
3973
|
+
return [...this.history];
|
|
3974
|
+
}
|
|
3975
|
+
/**
|
|
3976
|
+
* Get resolution statistics
|
|
3977
|
+
*/
|
|
3978
|
+
getStatistics() {
|
|
3979
|
+
const byStrategy = {};
|
|
3980
|
+
let successful = 0;
|
|
3981
|
+
let escalated = 0;
|
|
3982
|
+
for (const resolution of this.history) {
|
|
3983
|
+
byStrategy[resolution.strategy] = (byStrategy[resolution.strategy] ?? 0) + 1;
|
|
3984
|
+
if (resolution.successful) successful++;
|
|
3985
|
+
if (resolution.escalated) escalated++;
|
|
3986
|
+
}
|
|
3987
|
+
return {
|
|
3988
|
+
totalConflicts: this.history.length,
|
|
3989
|
+
byStrategy,
|
|
3990
|
+
successRate: this.history.length > 0 ? successful / this.history.length : 0,
|
|
3991
|
+
escalationRate: this.history.length > 0 ? escalated / this.history.length : 0
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
/**
|
|
3995
|
+
* Clear history and counts
|
|
3996
|
+
*/
|
|
3997
|
+
clear() {
|
|
3998
|
+
this.history.length = 0;
|
|
3999
|
+
this.conflictCounts.clear();
|
|
4000
|
+
}
|
|
4001
|
+
};
|
|
4002
|
+
function createConflictResolver(config) {
|
|
4003
|
+
return new ConflictResolver(config);
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
// src/core/Crew.ts
|
|
4007
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
4008
|
+
var Crew = class {
|
|
4009
|
+
id;
|
|
4010
|
+
name;
|
|
4011
|
+
description;
|
|
4012
|
+
config;
|
|
4013
|
+
agents;
|
|
4014
|
+
taskQueue;
|
|
4015
|
+
delegation;
|
|
4016
|
+
collaboration;
|
|
4017
|
+
conflictResolver;
|
|
4018
|
+
context;
|
|
4019
|
+
state = "idle";
|
|
4020
|
+
startTime;
|
|
4021
|
+
endTime;
|
|
4022
|
+
currentIteration = 0;
|
|
4023
|
+
maxIterations;
|
|
4024
|
+
timeline = [];
|
|
4025
|
+
results = /* @__PURE__ */ new Map();
|
|
4026
|
+
constructor(config) {
|
|
4027
|
+
this.id = nanoid4();
|
|
4028
|
+
this.name = config.name;
|
|
4029
|
+
this.description = config.description;
|
|
4030
|
+
this.config = config;
|
|
4031
|
+
this.maxIterations = config.maxIterations ?? 100;
|
|
4032
|
+
this.agents = new AgentRegistry();
|
|
4033
|
+
this.taskQueue = new TaskQueue({ defaultPriority: "medium" });
|
|
4034
|
+
this.delegation = new DelegationCoordinator({
|
|
4035
|
+
defaultStrategy: config.delegationStrategy
|
|
4036
|
+
});
|
|
4037
|
+
this.collaboration = new CollaborationManager();
|
|
4038
|
+
this.conflictResolver = new ConflictResolver();
|
|
4039
|
+
this.initializeAgents();
|
|
4040
|
+
}
|
|
4041
|
+
// ============ Agent Management ============
|
|
4042
|
+
/**
|
|
4043
|
+
* Initialize agents from config
|
|
4044
|
+
*/
|
|
4045
|
+
initializeAgents() {
|
|
4046
|
+
for (const agentConfig of this.config.agents) {
|
|
4047
|
+
const agent = createCrewAgent({ config: agentConfig });
|
|
4048
|
+
this.addAgent(agent);
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
/**
|
|
4052
|
+
* Add an agent to the crew
|
|
4053
|
+
*/
|
|
4054
|
+
addAgent(agent) {
|
|
4055
|
+
this.agents.register(agent);
|
|
4056
|
+
this.collaboration.registerAgent(agent);
|
|
4057
|
+
}
|
|
4058
|
+
/**
|
|
4059
|
+
* Remove an agent from the crew
|
|
4060
|
+
*/
|
|
4061
|
+
removeAgent(name) {
|
|
4062
|
+
this.agents.unregister(name);
|
|
4063
|
+
this.collaboration.unregisterAgent(name);
|
|
4064
|
+
}
|
|
4065
|
+
/**
|
|
4066
|
+
* Get an agent by name
|
|
4067
|
+
*/
|
|
4068
|
+
getAgent(name) {
|
|
4069
|
+
return this.agents.get(name);
|
|
4070
|
+
}
|
|
4071
|
+
/**
|
|
4072
|
+
* Get all agents
|
|
4073
|
+
*/
|
|
4074
|
+
getAgents() {
|
|
4075
|
+
return this.agents.getAll();
|
|
4076
|
+
}
|
|
4077
|
+
// ============ Task Management ============
|
|
4078
|
+
/**
|
|
4079
|
+
* Add a task to the crew
|
|
4080
|
+
*/
|
|
4081
|
+
addTask(taskConfig) {
|
|
4082
|
+
const task = new Task(taskConfig);
|
|
4083
|
+
this.taskQueue.enqueue(task);
|
|
4084
|
+
return task;
|
|
4085
|
+
}
|
|
4086
|
+
/**
|
|
4087
|
+
* Add multiple tasks
|
|
4088
|
+
*/
|
|
4089
|
+
addTasks(tasks) {
|
|
4090
|
+
return tasks.map((t) => this.addTask(t));
|
|
4091
|
+
}
|
|
4092
|
+
/**
|
|
4093
|
+
* Get task by ID
|
|
4094
|
+
*/
|
|
4095
|
+
getTask(taskId) {
|
|
4096
|
+
return this.taskQueue.get(taskId);
|
|
4097
|
+
}
|
|
4098
|
+
/**
|
|
4099
|
+
* Get all tasks
|
|
4100
|
+
*/
|
|
4101
|
+
getTasks() {
|
|
4102
|
+
return this.taskQueue.getAll();
|
|
4103
|
+
}
|
|
4104
|
+
// ============ Execution ============
|
|
4105
|
+
/**
|
|
4106
|
+
* Start crew execution
|
|
4107
|
+
*/
|
|
4108
|
+
async kickoff(options = {}) {
|
|
4109
|
+
const events = [];
|
|
4110
|
+
for await (const event of this.kickoffStream(options)) {
|
|
4111
|
+
events.push(event);
|
|
4112
|
+
}
|
|
4113
|
+
const taskResults = Array.from(this.results.values());
|
|
4114
|
+
return {
|
|
4115
|
+
success: this.state === "completed",
|
|
4116
|
+
taskResults,
|
|
4117
|
+
metrics: this.getMetrics(),
|
|
4118
|
+
timeline: [...this.timeline],
|
|
4119
|
+
finalOutput: this.buildFinalOutput(taskResults),
|
|
4120
|
+
events
|
|
4121
|
+
};
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Start crew execution with streaming events
|
|
4125
|
+
*/
|
|
4126
|
+
async *kickoffStream(options = {}) {
|
|
4127
|
+
this.context = new ExecutionContext({
|
|
4128
|
+
crewName: this.name
|
|
4129
|
+
});
|
|
4130
|
+
if (options.context) {
|
|
4131
|
+
for (const [key, value] of Object.entries(options.context)) {
|
|
4132
|
+
this.context.set(key, value);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
if (options.input) {
|
|
4136
|
+
this.context.set("input", options.input);
|
|
4137
|
+
}
|
|
4138
|
+
const eventQueue = [];
|
|
4139
|
+
this.context.on("*", (event) => {
|
|
4140
|
+
eventQueue.push(event);
|
|
4141
|
+
});
|
|
4142
|
+
this.state = "running";
|
|
4143
|
+
this.startTime = /* @__PURE__ */ new Date();
|
|
4144
|
+
this.currentIteration = 0;
|
|
4145
|
+
yield this.createEvent({
|
|
4146
|
+
type: "crew:started",
|
|
4147
|
+
agentCount: this.agents.getAll().length,
|
|
4148
|
+
taskCount: this.taskQueue.size,
|
|
4149
|
+
strategy: this.config.delegationStrategy
|
|
4150
|
+
});
|
|
4151
|
+
this.addTimelineEntry("crew_started", this.name);
|
|
4152
|
+
let timeoutId;
|
|
4153
|
+
if (options.timeoutMs) {
|
|
4154
|
+
timeoutId = setTimeout(() => {
|
|
4155
|
+
this.abort();
|
|
4156
|
+
}, options.timeoutMs);
|
|
4157
|
+
}
|
|
4158
|
+
try {
|
|
4159
|
+
while (this.state === "running" && this.currentIteration < this.maxIterations) {
|
|
4160
|
+
this.currentIteration++;
|
|
4161
|
+
const readyTasks = this.taskQueue.getReadyTasks(
|
|
4162
|
+
this.context.getCompletedTaskIds()
|
|
4163
|
+
);
|
|
4164
|
+
if (readyTasks.length === 0) {
|
|
4165
|
+
if (this.taskQueue.getByStatus("pending").length === 0 && this.taskQueue.getByStatus("assigned").length === 0 && this.taskQueue.getByStatus("in_progress").length === 0) {
|
|
4166
|
+
break;
|
|
4167
|
+
}
|
|
4168
|
+
await this.sleep(100);
|
|
4169
|
+
continue;
|
|
4170
|
+
}
|
|
4171
|
+
for (const task of readyTasks) {
|
|
4172
|
+
if (this.state !== "running") break;
|
|
4173
|
+
const delegationResult = await this.delegateTask(task, options);
|
|
4174
|
+
yield this.createEvent({
|
|
4175
|
+
type: "task:assigned",
|
|
4176
|
+
taskId: task.id,
|
|
4177
|
+
agentName: delegationResult.selectedAgent,
|
|
4178
|
+
reason: delegationResult.reason,
|
|
4179
|
+
strategy: this.config.delegationStrategy
|
|
4180
|
+
});
|
|
4181
|
+
const taskResult = await this.executeTask(task, delegationResult);
|
|
4182
|
+
yield this.createEvent({
|
|
4183
|
+
type: "task:completed",
|
|
4184
|
+
taskId: task.id,
|
|
4185
|
+
result: taskResult,
|
|
4186
|
+
agentName: delegationResult.selectedAgent,
|
|
4187
|
+
durationMs: taskResult.latencyMs ?? 0
|
|
4188
|
+
});
|
|
4189
|
+
while (eventQueue.length > 0) {
|
|
4190
|
+
yield eventQueue.shift();
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
while (this.state === "paused") {
|
|
4194
|
+
await this.sleep(100);
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
if (this.state === "aborted") {
|
|
4198
|
+
} else {
|
|
4199
|
+
const failedTasks = this.taskQueue.getByStatus("failed");
|
|
4200
|
+
if (failedTasks.length > 0) {
|
|
4201
|
+
this.state = "failed";
|
|
4202
|
+
yield this.createEvent({
|
|
4203
|
+
type: "crew:error",
|
|
4204
|
+
error: `${failedTasks.length} task(s) failed`,
|
|
4205
|
+
fatal: true
|
|
4206
|
+
});
|
|
4207
|
+
} else {
|
|
4208
|
+
this.state = "completed";
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
} catch (error) {
|
|
4212
|
+
if (this.state !== "aborted") {
|
|
4213
|
+
this.state = "failed";
|
|
4214
|
+
}
|
|
4215
|
+
yield this.createEvent({
|
|
4216
|
+
type: "crew:error",
|
|
4217
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4218
|
+
fatal: true
|
|
4219
|
+
});
|
|
4220
|
+
} finally {
|
|
4221
|
+
if (timeoutId) {
|
|
4222
|
+
clearTimeout(timeoutId);
|
|
4223
|
+
}
|
|
4224
|
+
this.endTime = /* @__PURE__ */ new Date();
|
|
4225
|
+
}
|
|
4226
|
+
yield this.createEvent({
|
|
4227
|
+
type: "crew:completed",
|
|
4228
|
+
metrics: this.getMetrics(),
|
|
4229
|
+
success: this.state === "completed",
|
|
4230
|
+
results: Array.from(this.results.values())
|
|
4231
|
+
});
|
|
4232
|
+
this.addTimelineEntry("crew_completed", this.name);
|
|
4233
|
+
}
|
|
4234
|
+
/**
|
|
4235
|
+
* Delegate a task to an agent
|
|
4236
|
+
*/
|
|
4237
|
+
async delegateTask(task, options) {
|
|
4238
|
+
const agents = this.agents.getAll();
|
|
4239
|
+
const strategy = options.delegationStrategy ?? this.config.delegationStrategy;
|
|
4240
|
+
const result = await this.delegation.delegate(
|
|
4241
|
+
task.toConfig(),
|
|
4242
|
+
agents,
|
|
4243
|
+
this.context,
|
|
4244
|
+
strategy
|
|
4245
|
+
);
|
|
4246
|
+
task.assign(result.selectedAgent);
|
|
4247
|
+
this.addTimelineEntry("task_delegated", task.id, {
|
|
4248
|
+
agent: result.selectedAgent,
|
|
4249
|
+
strategy
|
|
4250
|
+
});
|
|
4251
|
+
return result;
|
|
4252
|
+
}
|
|
4253
|
+
/**
|
|
4254
|
+
* Execute a task with assigned agent
|
|
4255
|
+
*/
|
|
4256
|
+
async executeTask(task, delegation) {
|
|
4257
|
+
const agent = this.agents.get(delegation.selectedAgent);
|
|
4258
|
+
if (!agent) {
|
|
4259
|
+
const error = new Error(`Agent ${delegation.selectedAgent} not found`);
|
|
4260
|
+
task.fail(error.message);
|
|
4261
|
+
throw error;
|
|
4262
|
+
}
|
|
4263
|
+
task.start();
|
|
4264
|
+
this.addTimelineEntry("task_started", task.id, { agent: agent.name });
|
|
4265
|
+
this.context.emit({
|
|
4266
|
+
type: "task:started",
|
|
4267
|
+
taskId: task.id,
|
|
4268
|
+
agentName: agent.name
|
|
4269
|
+
});
|
|
4270
|
+
try {
|
|
4271
|
+
const result = await agent.executeTask(task.toConfig());
|
|
4272
|
+
task.complete(result);
|
|
4273
|
+
this.results.set(task.id, result);
|
|
4274
|
+
this.context.markTaskCompleted(task.id, result);
|
|
4275
|
+
this.agents.recordTaskCompletion(agent.name, true);
|
|
4276
|
+
this.addTimelineEntry("task_completed", task.id, {
|
|
4277
|
+
agent: agent.name,
|
|
4278
|
+
success: true
|
|
4279
|
+
});
|
|
4280
|
+
return result;
|
|
4281
|
+
} catch (error) {
|
|
4282
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4283
|
+
if (task.canRetry()) {
|
|
4284
|
+
this.addTimelineEntry("task_retried", task.id, {
|
|
4285
|
+
agent: agent.name,
|
|
4286
|
+
error: errorMsg
|
|
4287
|
+
});
|
|
4288
|
+
this.context.emit({
|
|
4289
|
+
type: "task:retried",
|
|
4290
|
+
taskId: task.id,
|
|
4291
|
+
agentName: agent.name,
|
|
4292
|
+
attempt: task.attempts,
|
|
4293
|
+
maxAttempts: task.maxRetries + 1,
|
|
4294
|
+
reason: errorMsg
|
|
4295
|
+
});
|
|
4296
|
+
task.reset();
|
|
4297
|
+
return this.executeTask(task, delegation);
|
|
4298
|
+
}
|
|
4299
|
+
task.fail(errorMsg);
|
|
4300
|
+
this.agents.recordTaskCompletion(agent.name, false);
|
|
4301
|
+
this.addTimelineEntry("task_failed", task.id, {
|
|
4302
|
+
agent: agent.name,
|
|
4303
|
+
error: errorMsg
|
|
4304
|
+
});
|
|
4305
|
+
throw error;
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
/**
|
|
4309
|
+
* Build final output from task results
|
|
4310
|
+
*/
|
|
4311
|
+
buildFinalOutput(results) {
|
|
4312
|
+
if (results.length === 0) {
|
|
4313
|
+
return "";
|
|
4314
|
+
}
|
|
4315
|
+
if (results.length === 1) {
|
|
4316
|
+
return results[0].output;
|
|
4317
|
+
}
|
|
4318
|
+
return results.map((r, i) => `## Task ${i + 1} Result
|
|
4319
|
+
|
|
4320
|
+
${r.output}`).join("\n\n---\n\n");
|
|
4321
|
+
}
|
|
4322
|
+
// ============ Control ============
|
|
4323
|
+
/**
|
|
4324
|
+
* Pause crew execution
|
|
4325
|
+
*/
|
|
4326
|
+
pause() {
|
|
4327
|
+
if (this.state === "running") {
|
|
4328
|
+
this.state = "paused";
|
|
4329
|
+
this.context.emit({
|
|
4330
|
+
type: "crew:paused",
|
|
4331
|
+
pendingTasks: this.taskQueue.getByStatus("pending").length
|
|
4332
|
+
});
|
|
4333
|
+
this.addTimelineEntry("crew_paused", this.name);
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4336
|
+
/**
|
|
4337
|
+
* Resume crew execution
|
|
4338
|
+
*/
|
|
4339
|
+
resume() {
|
|
4340
|
+
if (this.state === "paused") {
|
|
4341
|
+
this.state = "running";
|
|
4342
|
+
this.context.emit({
|
|
4343
|
+
type: "crew:resumed"
|
|
4344
|
+
});
|
|
4345
|
+
this.addTimelineEntry("crew_resumed", this.name);
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
/**
|
|
4349
|
+
* Abort crew execution
|
|
4350
|
+
*/
|
|
4351
|
+
abort() {
|
|
4352
|
+
if (this.state === "running" || this.state === "paused") {
|
|
4353
|
+
this.state = "aborted";
|
|
4354
|
+
this.context.abort();
|
|
4355
|
+
this.context.emit({
|
|
4356
|
+
type: "crew:aborted",
|
|
4357
|
+
reason: "User requested abort",
|
|
4358
|
+
completedTasks: this.taskQueue.getByStatus("completed").length,
|
|
4359
|
+
pendingTasks: this.taskQueue.getByStatus("pending").length
|
|
4360
|
+
});
|
|
4361
|
+
this.addTimelineEntry("crew_aborted", this.name);
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
// ============ State & Metrics ============
|
|
4365
|
+
/**
|
|
4366
|
+
* Get current crew status
|
|
4367
|
+
*/
|
|
4368
|
+
getStatus() {
|
|
4369
|
+
return {
|
|
4370
|
+
state: this.state,
|
|
4371
|
+
currentIteration: this.currentIteration,
|
|
4372
|
+
maxIterations: this.maxIterations,
|
|
4373
|
+
startTime: this.startTime,
|
|
4374
|
+
endTime: this.endTime,
|
|
4375
|
+
tasksPending: this.taskQueue.getByStatus("pending").length,
|
|
4376
|
+
tasksInProgress: this.taskQueue.getByStatus("in_progress").length,
|
|
4377
|
+
tasksCompleted: this.taskQueue.getByStatus("completed").length,
|
|
4378
|
+
tasksFailed: this.taskQueue.getByStatus("failed").length,
|
|
4379
|
+
agentsBusy: this.agents.getAll().filter((a) => a.isBusy).length,
|
|
4380
|
+
agentsAvailable: this.agents.getAll().filter((a) => !a.isBusy).length
|
|
4381
|
+
};
|
|
4382
|
+
}
|
|
4383
|
+
/**
|
|
4384
|
+
* Get crew metrics
|
|
4385
|
+
*/
|
|
4386
|
+
getMetrics() {
|
|
4387
|
+
const allTasks = this.taskQueue.getAll();
|
|
4388
|
+
const completedTasks = this.taskQueue.getByStatus("completed");
|
|
4389
|
+
const failedTasks = this.taskQueue.getByStatus("failed");
|
|
4390
|
+
const agents = this.agents.getAll();
|
|
4391
|
+
const totalExecutionTimeMs = this.startTime && this.endTime ? this.endTime.getTime() - this.startTime.getTime() : 0;
|
|
4392
|
+
let totalTokens = 0;
|
|
4393
|
+
for (const result of this.results.values()) {
|
|
4394
|
+
totalTokens += result.tokensUsed ?? 0;
|
|
4395
|
+
}
|
|
4396
|
+
const agentMetrics = {};
|
|
4397
|
+
for (const agent of agents) {
|
|
4398
|
+
const stats = agent.getStats();
|
|
4399
|
+
agentMetrics[agent.name] = {
|
|
4400
|
+
tasksAssigned: stats.tasksCompleted + stats.tasksFailed,
|
|
4401
|
+
tasksCompleted: stats.tasksCompleted,
|
|
4402
|
+
tasksFailed: stats.tasksFailed,
|
|
4403
|
+
tokensUsed: stats.totalTokensUsed,
|
|
4404
|
+
averageLatencyMs: 0
|
|
4405
|
+
// Would need to track this separately
|
|
4406
|
+
};
|
|
4407
|
+
}
|
|
4408
|
+
const delegationStats = this.delegation.getStatistics();
|
|
4409
|
+
return {
|
|
4410
|
+
totalTasks: allTasks.length,
|
|
4411
|
+
completedTasks: completedTasks.length,
|
|
4412
|
+
failedTasks: failedTasks.length,
|
|
4413
|
+
totalExecutionTimeMs,
|
|
4414
|
+
averageTaskTimeMs: completedTasks.length > 0 ? totalExecutionTimeMs / completedTasks.length : 0,
|
|
4415
|
+
totalIterations: this.currentIteration,
|
|
4416
|
+
totalTokens,
|
|
4417
|
+
agentMetrics,
|
|
4418
|
+
delegationStats: {
|
|
4419
|
+
totalDelegations: delegationStats.totalDelegations,
|
|
4420
|
+
byStrategy: delegationStats.byStrategy,
|
|
4421
|
+
averageConfidence: delegationStats.averageConfidence
|
|
4422
|
+
}
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* Get crew progress
|
|
4427
|
+
*/
|
|
4428
|
+
getProgress() {
|
|
4429
|
+
const allTasks = this.taskQueue.getAll();
|
|
4430
|
+
const completed = this.taskQueue.getByStatus("completed").length;
|
|
4431
|
+
const total = allTasks.length;
|
|
4432
|
+
return {
|
|
4433
|
+
percentage: total > 0 ? completed / total * 100 : 0,
|
|
4434
|
+
tasksCompleted: completed,
|
|
4435
|
+
totalTasks: total,
|
|
4436
|
+
currentTask: this.taskQueue.getByStatus("in_progress")[0]?.description,
|
|
4437
|
+
estimatedRemainingMs: 0
|
|
4438
|
+
// Would need historical data
|
|
4439
|
+
};
|
|
4440
|
+
}
|
|
4441
|
+
/**
|
|
4442
|
+
* Get timeline
|
|
4443
|
+
*/
|
|
4444
|
+
getTimeline() {
|
|
4445
|
+
return [...this.timeline];
|
|
4446
|
+
}
|
|
4447
|
+
// ============ Checkpointing ============
|
|
4448
|
+
/**
|
|
4449
|
+
* Create a checkpoint of current state
|
|
4450
|
+
*/
|
|
4451
|
+
createCheckpoint() {
|
|
4452
|
+
return {
|
|
4453
|
+
id: nanoid4(),
|
|
4454
|
+
crewId: this.id,
|
|
4455
|
+
crewName: this.name,
|
|
4456
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
4457
|
+
state: this.state,
|
|
4458
|
+
contextState: this.context ? this.context.exportState() : void 0,
|
|
4459
|
+
taskQueue: this.taskQueue.getAll().map((t) => t.toConfig()),
|
|
4460
|
+
results: Object.fromEntries(this.results),
|
|
4461
|
+
timeline: [...this.timeline],
|
|
4462
|
+
iteration: this.currentIteration
|
|
4463
|
+
};
|
|
4464
|
+
}
|
|
4465
|
+
/**
|
|
4466
|
+
* Restore from checkpoint
|
|
4467
|
+
*/
|
|
4468
|
+
restoreCheckpoint(checkpoint) {
|
|
4469
|
+
if (this.state !== "idle") {
|
|
4470
|
+
throw new Error("Cannot restore checkpoint while crew is running");
|
|
4471
|
+
}
|
|
4472
|
+
this.taskQueue.clear();
|
|
4473
|
+
this.results.clear();
|
|
4474
|
+
this.timeline.length = 0;
|
|
4475
|
+
if (checkpoint.taskQueue) {
|
|
4476
|
+
for (const taskConfig of checkpoint.taskQueue) {
|
|
4477
|
+
this.addTask(taskConfig);
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
if (checkpoint.results) {
|
|
4481
|
+
for (const [taskId, result] of Object.entries(checkpoint.results)) {
|
|
4482
|
+
this.results.set(taskId, result);
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
if (checkpoint.timeline) {
|
|
4486
|
+
this.timeline.push(...checkpoint.timeline);
|
|
4487
|
+
}
|
|
4488
|
+
this.currentIteration = checkpoint.iteration ?? 0;
|
|
4489
|
+
}
|
|
4490
|
+
// ============ Utilities ============
|
|
4491
|
+
/**
|
|
4492
|
+
* Add a timeline entry
|
|
4493
|
+
*/
|
|
4494
|
+
addTimelineEntry(event, entityId, data) {
|
|
4495
|
+
this.timeline.push({
|
|
4496
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
4497
|
+
event,
|
|
4498
|
+
entityId,
|
|
4499
|
+
data
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
/**
|
|
4503
|
+
* Sleep utility
|
|
4504
|
+
*/
|
|
4505
|
+
sleep(ms) {
|
|
4506
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4507
|
+
}
|
|
4508
|
+
/**
|
|
4509
|
+
* Create a properly typed event
|
|
4510
|
+
*/
|
|
4511
|
+
createEvent(event) {
|
|
4512
|
+
return {
|
|
4513
|
+
...event,
|
|
4514
|
+
crewName: this.name,
|
|
4515
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
/**
|
|
4519
|
+
* Reset crew for re-execution
|
|
4520
|
+
*/
|
|
4521
|
+
reset() {
|
|
4522
|
+
if (this.state === "running" || this.state === "paused") {
|
|
4523
|
+
throw new Error("Cannot reset while crew is running");
|
|
4524
|
+
}
|
|
4525
|
+
this.state = "idle";
|
|
4526
|
+
this.startTime = void 0;
|
|
4527
|
+
this.endTime = void 0;
|
|
4528
|
+
this.currentIteration = 0;
|
|
4529
|
+
this.timeline.length = 0;
|
|
4530
|
+
this.results.clear();
|
|
4531
|
+
this.taskQueue.clear();
|
|
4532
|
+
this.delegation.reset();
|
|
4533
|
+
this.collaboration.clear();
|
|
4534
|
+
this.conflictResolver.clear();
|
|
4535
|
+
}
|
|
4536
|
+
// ============ Serialization ============
|
|
4537
|
+
/**
|
|
4538
|
+
* Get crew configuration
|
|
4539
|
+
*/
|
|
4540
|
+
getConfig() {
|
|
4541
|
+
return { ...this.config };
|
|
4542
|
+
}
|
|
4543
|
+
};
|
|
4544
|
+
function createCrew(config) {
|
|
4545
|
+
return new Crew(config);
|
|
4546
|
+
}
|
|
4547
|
+
|
|
4548
|
+
export {
|
|
4549
|
+
Role,
|
|
4550
|
+
createRole,
|
|
4551
|
+
Task,
|
|
4552
|
+
createTask,
|
|
4553
|
+
TaskQueue,
|
|
4554
|
+
createTaskQueue,
|
|
4555
|
+
ExecutionContext,
|
|
4556
|
+
createExecutionContext,
|
|
4557
|
+
AgentCapabilities,
|
|
4558
|
+
CrewAgent,
|
|
4559
|
+
createCrewAgent,
|
|
4560
|
+
AgentRegistry,
|
|
4561
|
+
createAgentRegistry,
|
|
4562
|
+
BaseDelegationStrategy,
|
|
4563
|
+
DelegationError,
|
|
4564
|
+
RoundRobinStrategy,
|
|
4565
|
+
createRoundRobinStrategy,
|
|
4566
|
+
BestMatchStrategy,
|
|
4567
|
+
createBestMatchStrategy,
|
|
4568
|
+
AuctionStrategy,
|
|
4569
|
+
createAuctionStrategy,
|
|
4570
|
+
HierarchicalStrategy,
|
|
4571
|
+
createHierarchicalStrategy,
|
|
4572
|
+
ConsensusStrategy,
|
|
4573
|
+
createConsensusStrategy,
|
|
4574
|
+
createStrategy,
|
|
4575
|
+
STRATEGY_TYPES,
|
|
4576
|
+
DelegationCoordinator,
|
|
4577
|
+
createDelegationCoordinator,
|
|
4578
|
+
CollaborationManager,
|
|
4579
|
+
createCollaborationManager,
|
|
4580
|
+
ConflictResolver,
|
|
4581
|
+
createConflictResolver,
|
|
4582
|
+
Crew,
|
|
4583
|
+
createCrew
|
|
4584
|
+
};
|