@abassey/aid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/index.cjs +741 -0
- package/dist/agents/index.d.cts +78 -0
- package/dist/agents/index.d.ts +78 -0
- package/dist/agents/index.js +741 -0
- package/dist/ai-AWJOUXFM.js +9 -0
- package/dist/ai-DOAYJKKI.cjs +9 -0
- package/dist/chunk-2TNYBUNK.js +124 -0
- package/dist/chunk-3LGKZRGY.cjs +124 -0
- package/dist/chunk-AUR2BBB5.cjs +1436 -0
- package/dist/chunk-IJLTRQF4.cjs +276 -0
- package/dist/chunk-JPD7UBAZ.js +58 -0
- package/dist/chunk-M4RQALTT.js +276 -0
- package/dist/chunk-NB65IHJE.cjs +58 -0
- package/dist/chunk-YNIEOBDF.js +1436 -0
- package/dist/client/index.cjs +18 -0
- package/dist/client/index.d.cts +8 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.js +18 -0
- package/dist/errors-CUVTnseb.d.ts +13 -0
- package/dist/errors-CgCce4cK.d.cts +158 -0
- package/dist/errors-CgCce4cK.d.ts +158 -0
- package/dist/errors-zAPbTlpe.d.cts +13 -0
- package/dist/eval/index.cjs +308 -0
- package/dist/eval/index.d.cts +106 -0
- package/dist/eval/index.d.ts +106 -0
- package/dist/eval/index.js +308 -0
- package/dist/index.cjs +35 -0
- package/dist/index.d.cts +107 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +35 -0
- package/dist/middleware/index.cjs +201 -0
- package/dist/middleware/index.d.cts +36 -0
- package/dist/middleware/index.d.ts +36 -0
- package/dist/middleware/index.js +201 -0
- package/dist/observability/index.cjs +147 -0
- package/dist/observability/index.d.cts +30 -0
- package/dist/observability/index.d.ts +30 -0
- package/dist/observability/index.js +147 -0
- package/dist/react/index.cjs +253 -0
- package/dist/react/index.d.cts +64 -0
- package/dist/react/index.d.ts +64 -0
- package/dist/react/index.js +253 -0
- package/dist/serve/index.cjs +545 -0
- package/dist/serve/index.d.cts +69 -0
- package/dist/serve/index.d.ts +69 -0
- package/dist/serve/index.js +545 -0
- package/dist/types-BJReASS-.d.cts +196 -0
- package/dist/types-BJReASS-.d.ts +196 -0
- package/dist/types-CguX3F16.d.cts +173 -0
- package/dist/types-CrFH-_qp.d.cts +68 -0
- package/dist/types-DvdzPmW0.d.ts +173 -0
- package/dist/types-qfE32ADy.d.ts +68 -0
- package/package.json +144 -0
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ai,
|
|
3
|
+
executeToolLoop
|
|
4
|
+
} from "../chunk-YNIEOBDF.js";
|
|
5
|
+
import {
|
|
6
|
+
AidError
|
|
7
|
+
} from "../chunk-2TNYBUNK.js";
|
|
8
|
+
|
|
9
|
+
// src/tools/tool.ts
|
|
10
|
+
function isRichParam(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && "description" in value;
|
|
12
|
+
}
|
|
13
|
+
function paramsToJsonSchema(params) {
|
|
14
|
+
const properties = {};
|
|
15
|
+
const required = [];
|
|
16
|
+
for (const [key, value] of Object.entries(params)) {
|
|
17
|
+
if (typeof value === "string") {
|
|
18
|
+
properties[key] = { type: "string", description: value };
|
|
19
|
+
required.push(key);
|
|
20
|
+
} else if (isRichParam(value)) {
|
|
21
|
+
const prop = {
|
|
22
|
+
type: value.type ?? "string",
|
|
23
|
+
description: value.description
|
|
24
|
+
};
|
|
25
|
+
if (value.enum) {
|
|
26
|
+
prop.enum = value.enum;
|
|
27
|
+
}
|
|
28
|
+
properties[key] = prop;
|
|
29
|
+
if (!value.optional) {
|
|
30
|
+
required.push(key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties,
|
|
37
|
+
required
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function tool(name, description, params, handler) {
|
|
41
|
+
return {
|
|
42
|
+
name,
|
|
43
|
+
description,
|
|
44
|
+
parameters: paramsToJsonSchema(params),
|
|
45
|
+
handler
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/agents/events.ts
|
|
50
|
+
var AgentEventEmitter = class {
|
|
51
|
+
listeners = /* @__PURE__ */ new Map();
|
|
52
|
+
on(event, cb) {
|
|
53
|
+
if (!this.listeners.has(event)) {
|
|
54
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
55
|
+
}
|
|
56
|
+
this.listeners.get(event).add(cb);
|
|
57
|
+
}
|
|
58
|
+
off(event, cb) {
|
|
59
|
+
this.listeners.get(event)?.delete(cb);
|
|
60
|
+
}
|
|
61
|
+
emit(event, payload) {
|
|
62
|
+
const cbs = this.listeners.get(event);
|
|
63
|
+
if (cbs) {
|
|
64
|
+
for (const cb of cbs) {
|
|
65
|
+
cb(payload);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/agents/memory.ts
|
|
72
|
+
var InMemoryProvider = class {
|
|
73
|
+
messages = [];
|
|
74
|
+
maxMessages;
|
|
75
|
+
constructor(options) {
|
|
76
|
+
this.maxMessages = options?.maxMessages ?? 50;
|
|
77
|
+
}
|
|
78
|
+
async add(messages) {
|
|
79
|
+
this.messages.push(...messages);
|
|
80
|
+
if (this.messages.length > this.maxMessages) {
|
|
81
|
+
this.messages = this.messages.slice(-this.maxMessages);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async retrieve(_query, limit) {
|
|
85
|
+
if (limit !== void 0) {
|
|
86
|
+
return this.messages.slice(-limit);
|
|
87
|
+
}
|
|
88
|
+
return [...this.messages];
|
|
89
|
+
}
|
|
90
|
+
async clear() {
|
|
91
|
+
this.messages = [];
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/agents/agent.ts
|
|
96
|
+
var AgentImpl = class _AgentImpl {
|
|
97
|
+
name;
|
|
98
|
+
config;
|
|
99
|
+
emitter = new AgentEventEmitter();
|
|
100
|
+
memory;
|
|
101
|
+
chatHistory = [];
|
|
102
|
+
chatInitialized = false;
|
|
103
|
+
aiFn;
|
|
104
|
+
constructor(config) {
|
|
105
|
+
this.name = config.name;
|
|
106
|
+
this.config = config;
|
|
107
|
+
this.aiFn = config.ai ?? ai;
|
|
108
|
+
if (config.memory === true) {
|
|
109
|
+
this.memory = new InMemoryProvider();
|
|
110
|
+
} else if (config.memory && typeof config.memory !== "boolean") {
|
|
111
|
+
this.memory = config.memory;
|
|
112
|
+
} else {
|
|
113
|
+
this.memory = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Core execution method. Makes an initial LLM call via aiFn WITHOUT tools
|
|
118
|
+
* (to prevent aiCall's internal tool loop), then runs executeToolLoop directly
|
|
119
|
+
* when the LLM requests tool use.
|
|
120
|
+
*/
|
|
121
|
+
async executeWithToolLoop(messages) {
|
|
122
|
+
const tools = this.config.tools ?? [];
|
|
123
|
+
const maxIterations = this.config.maxIterations ?? 25;
|
|
124
|
+
const steps = [];
|
|
125
|
+
let stepCount = 0;
|
|
126
|
+
this.emitter.emit("step:start", { step: stepCount, iteration: 0 });
|
|
127
|
+
const initialResponse = await this.aiFn("", {
|
|
128
|
+
model: this.config.model,
|
|
129
|
+
system: this.config.role,
|
|
130
|
+
middleware: this.config.middleware,
|
|
131
|
+
temperature: this.config.temperature,
|
|
132
|
+
maxTokens: this.config.maxTokens,
|
|
133
|
+
_messages: messages
|
|
134
|
+
// NO tools — prevents aiCall's tool loop
|
|
135
|
+
});
|
|
136
|
+
steps.push({
|
|
137
|
+
type: "llm_call",
|
|
138
|
+
iteration: 0,
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
data: {
|
|
141
|
+
model: initialResponse.model,
|
|
142
|
+
tokens: initialResponse.tokens,
|
|
143
|
+
finishReason: initialResponse.finishReason
|
|
144
|
+
},
|
|
145
|
+
durationMs: initialResponse.latencyMs
|
|
146
|
+
});
|
|
147
|
+
this.emitter.emit("step:end", { step: stepCount, iteration: 0, result: initialResponse });
|
|
148
|
+
stepCount++;
|
|
149
|
+
if (tools.length === 0 || initialResponse.finishReason !== "tool_use" || !initialResponse.toolCalls || initialResponse.toolCalls.length === 0) {
|
|
150
|
+
return {
|
|
151
|
+
response: initialResponse,
|
|
152
|
+
allMessages: messages,
|
|
153
|
+
toolCalls: [],
|
|
154
|
+
steps,
|
|
155
|
+
iterations: 0
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const callFn = async (request2) => {
|
|
159
|
+
return this.aiFn("", {
|
|
160
|
+
model: this.config.model,
|
|
161
|
+
system: this.config.role,
|
|
162
|
+
middleware: this.config.middleware,
|
|
163
|
+
temperature: this.config.temperature,
|
|
164
|
+
maxTokens: this.config.maxTokens,
|
|
165
|
+
_messages: request2.messages
|
|
166
|
+
// NO tools — just LLM transport
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
const request = {
|
|
170
|
+
model: initialResponse.model,
|
|
171
|
+
messages,
|
|
172
|
+
options: {
|
|
173
|
+
model: initialResponse.model,
|
|
174
|
+
temperature: this.config.temperature ?? 1,
|
|
175
|
+
maxTokens: this.config.maxTokens ?? 4096,
|
|
176
|
+
topP: 1,
|
|
177
|
+
stop: [],
|
|
178
|
+
timeout: 3e4,
|
|
179
|
+
tools,
|
|
180
|
+
middleware: this.config.middleware ?? []
|
|
181
|
+
},
|
|
182
|
+
tools: tools.map((t) => ({
|
|
183
|
+
name: t.name,
|
|
184
|
+
description: t.description,
|
|
185
|
+
parameters: t.parameters
|
|
186
|
+
}))
|
|
187
|
+
};
|
|
188
|
+
const loopResult = await executeToolLoop({
|
|
189
|
+
call: callFn,
|
|
190
|
+
request,
|
|
191
|
+
tools,
|
|
192
|
+
maxIterations,
|
|
193
|
+
initialResponse,
|
|
194
|
+
onToolCall: (tc) => {
|
|
195
|
+
this.emitter.emit("tool:call", { id: tc.id, name: tc.name, args: tc.args });
|
|
196
|
+
steps.push({
|
|
197
|
+
type: "tool_call",
|
|
198
|
+
iteration: stepCount,
|
|
199
|
+
timestamp: Date.now(),
|
|
200
|
+
data: { id: tc.id, name: tc.name, args: tc.args, result: "", isError: false },
|
|
201
|
+
durationMs: 0
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
onToolResult: (tc, result, durationMs) => {
|
|
205
|
+
this.emitter.emit("tool:result", { id: tc.id, name: tc.name, result, durationMs });
|
|
206
|
+
for (let i = steps.length - 1; i >= 0; i--) {
|
|
207
|
+
if (steps[i].type === "tool_call") {
|
|
208
|
+
steps[i].data.result = result;
|
|
209
|
+
steps[i].durationMs = durationMs;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
onLlmCall: (resp) => {
|
|
215
|
+
this.emitter.emit("step:start", { step: stepCount, iteration: stepCount });
|
|
216
|
+
steps.push({
|
|
217
|
+
type: "llm_call",
|
|
218
|
+
iteration: stepCount,
|
|
219
|
+
timestamp: Date.now(),
|
|
220
|
+
data: {
|
|
221
|
+
model: resp.model,
|
|
222
|
+
tokens: resp.tokens,
|
|
223
|
+
finishReason: resp.finishReason
|
|
224
|
+
},
|
|
225
|
+
durationMs: resp.latencyMs
|
|
226
|
+
});
|
|
227
|
+
this.emitter.emit("step:end", { step: stepCount, iteration: stepCount, result: resp });
|
|
228
|
+
stepCount++;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
return {
|
|
232
|
+
response: loopResult.response,
|
|
233
|
+
allMessages: loopResult.messages,
|
|
234
|
+
toolCalls: loopResult.toolCalls,
|
|
235
|
+
steps,
|
|
236
|
+
iterations: loopResult.iterations
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
async run(task) {
|
|
240
|
+
const startTime = Date.now();
|
|
241
|
+
let memoryMessages = [];
|
|
242
|
+
if (this.memory) {
|
|
243
|
+
memoryMessages = await this.memory.retrieve(task);
|
|
244
|
+
}
|
|
245
|
+
const messages = [
|
|
246
|
+
...memoryMessages,
|
|
247
|
+
{ role: "user", content: task }
|
|
248
|
+
];
|
|
249
|
+
this.emitter.emit("message", { role: "user", content: task });
|
|
250
|
+
let completed = true;
|
|
251
|
+
let error;
|
|
252
|
+
let execResult;
|
|
253
|
+
try {
|
|
254
|
+
execResult = await this.executeWithToolLoop(messages);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
if (err instanceof AidError && err.code === "tool_loop_limit") {
|
|
257
|
+
completed = false;
|
|
258
|
+
error = new AidError("agent_loop_limit", `Agent '${this.name}' hit iteration limit`);
|
|
259
|
+
execResult = {
|
|
260
|
+
response: {
|
|
261
|
+
text: "",
|
|
262
|
+
model: this.config.model ?? "unknown",
|
|
263
|
+
tokens: { input: 0, output: 0, total: 0 },
|
|
264
|
+
cost: 0,
|
|
265
|
+
latencyMs: Date.now() - startTime,
|
|
266
|
+
finishReason: "error",
|
|
267
|
+
raw: err
|
|
268
|
+
},
|
|
269
|
+
allMessages: messages,
|
|
270
|
+
toolCalls: [],
|
|
271
|
+
steps: [],
|
|
272
|
+
iterations: 0
|
|
273
|
+
};
|
|
274
|
+
} else {
|
|
275
|
+
throw err;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const resultMessages = [
|
|
279
|
+
{ role: "system", content: this.config.role },
|
|
280
|
+
...execResult.allMessages
|
|
281
|
+
];
|
|
282
|
+
if (this.memory) {
|
|
283
|
+
await this.memory.add([
|
|
284
|
+
{ role: "user", content: task },
|
|
285
|
+
{ role: "assistant", content: execResult.response.text }
|
|
286
|
+
]);
|
|
287
|
+
}
|
|
288
|
+
const result = {
|
|
289
|
+
text: execResult.response.text,
|
|
290
|
+
completed,
|
|
291
|
+
messages: resultMessages,
|
|
292
|
+
steps: execResult.steps,
|
|
293
|
+
tokens: execResult.response.tokens,
|
|
294
|
+
cost: execResult.response.cost,
|
|
295
|
+
latencyMs: Date.now() - startTime,
|
|
296
|
+
toolCalls: execResult.toolCalls,
|
|
297
|
+
error
|
|
298
|
+
};
|
|
299
|
+
this.emitter.emit("done", { result });
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
async chat(message) {
|
|
303
|
+
const startTime = Date.now();
|
|
304
|
+
const maxChatHistory = this.config.maxChatHistory ?? 50;
|
|
305
|
+
if (!this.chatInitialized && this.memory) {
|
|
306
|
+
const memoryMessages = await this.memory.retrieve(message);
|
|
307
|
+
this.chatHistory.push(...memoryMessages);
|
|
308
|
+
this.chatInitialized = true;
|
|
309
|
+
}
|
|
310
|
+
this.chatHistory.push({ role: "user", content: message });
|
|
311
|
+
this.emitter.emit("message", { role: "user", content: message });
|
|
312
|
+
const execResult = await this.executeWithToolLoop([...this.chatHistory]);
|
|
313
|
+
this.chatHistory.push({ role: "assistant", content: execResult.response.text });
|
|
314
|
+
if (this.chatHistory.length > maxChatHistory) {
|
|
315
|
+
this.chatHistory = this.chatHistory.slice(-maxChatHistory);
|
|
316
|
+
}
|
|
317
|
+
if (this.memory) {
|
|
318
|
+
await this.memory.add([
|
|
319
|
+
{ role: "user", content: message },
|
|
320
|
+
{ role: "assistant", content: execResult.response.text }
|
|
321
|
+
]);
|
|
322
|
+
}
|
|
323
|
+
const resultMessages = [
|
|
324
|
+
{ role: "system", content: this.config.role },
|
|
325
|
+
...this.chatHistory
|
|
326
|
+
];
|
|
327
|
+
const result = {
|
|
328
|
+
text: execResult.response.text,
|
|
329
|
+
completed: true,
|
|
330
|
+
messages: resultMessages,
|
|
331
|
+
steps: execResult.steps,
|
|
332
|
+
tokens: execResult.response.tokens,
|
|
333
|
+
cost: execResult.response.cost,
|
|
334
|
+
latencyMs: Date.now() - startTime,
|
|
335
|
+
toolCalls: execResult.toolCalls
|
|
336
|
+
};
|
|
337
|
+
this.emitter.emit("done", { result });
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
clone(overrides) {
|
|
341
|
+
return new _AgentImpl({ ...this.config, ...overrides });
|
|
342
|
+
}
|
|
343
|
+
on(event, cb) {
|
|
344
|
+
this.emitter.on(event, cb);
|
|
345
|
+
}
|
|
346
|
+
off(event, cb) {
|
|
347
|
+
this.emitter.off(event, cb);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
function agent(config) {
|
|
351
|
+
return new AgentImpl(config);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/agents/crew.ts
|
|
355
|
+
function aggregateTokens(results) {
|
|
356
|
+
return results.reduce(
|
|
357
|
+
(acc, r) => ({
|
|
358
|
+
input: acc.input + r.tokens.input,
|
|
359
|
+
output: acc.output + r.tokens.output,
|
|
360
|
+
total: acc.total + r.tokens.total
|
|
361
|
+
}),
|
|
362
|
+
{ input: 0, output: 0, total: 0 }
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
function aggregateCost(results) {
|
|
366
|
+
return results.reduce((acc, r) => acc + r.cost, 0);
|
|
367
|
+
}
|
|
368
|
+
function makeErrorResult(agentName, err) {
|
|
369
|
+
const error = err instanceof AidError ? err : new AidError("provider_error", err instanceof Error ? err.message : String(err));
|
|
370
|
+
return {
|
|
371
|
+
text: "",
|
|
372
|
+
completed: false,
|
|
373
|
+
messages: [],
|
|
374
|
+
steps: [],
|
|
375
|
+
tokens: { input: 0, output: 0, total: 0 },
|
|
376
|
+
cost: 0,
|
|
377
|
+
latencyMs: 0,
|
|
378
|
+
toolCalls: [],
|
|
379
|
+
error
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function crew(config) {
|
|
383
|
+
const emitter = new AgentEventEmitter();
|
|
384
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
385
|
+
for (const a of config.agents) {
|
|
386
|
+
agentMap.set(a.name, a);
|
|
387
|
+
}
|
|
388
|
+
function wireEvents(a) {
|
|
389
|
+
const events = ["step:start", "step:end", "tool:call", "tool:result", "handoff", "message", "done", "error"];
|
|
390
|
+
for (const event of events) {
|
|
391
|
+
a.on(event, (payload) => {
|
|
392
|
+
emitter.emit(event, { ...payload, agent: a.name });
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
for (const a of config.agents) {
|
|
397
|
+
wireEvents(a);
|
|
398
|
+
}
|
|
399
|
+
async function runSequential(task) {
|
|
400
|
+
const startTime = Date.now();
|
|
401
|
+
const agentResults = /* @__PURE__ */ new Map();
|
|
402
|
+
let previousOutput = "";
|
|
403
|
+
for (const a of config.agents) {
|
|
404
|
+
const input = previousOutput ? `Previous agent's output:
|
|
405
|
+
---
|
|
406
|
+
${previousOutput}
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
Original task: ${task}` : task;
|
|
410
|
+
const result = await a.run(input);
|
|
411
|
+
agentResults.set(a.name, result);
|
|
412
|
+
previousOutput = result.text;
|
|
413
|
+
}
|
|
414
|
+
const allResults = [...agentResults.values()];
|
|
415
|
+
return {
|
|
416
|
+
text: previousOutput,
|
|
417
|
+
agentResults,
|
|
418
|
+
totalTokens: aggregateTokens(allResults),
|
|
419
|
+
totalCost: aggregateCost(allResults),
|
|
420
|
+
latencyMs: Date.now() - startTime
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
async function runParallel(task) {
|
|
424
|
+
const startTime = Date.now();
|
|
425
|
+
const agentResults = /* @__PURE__ */ new Map();
|
|
426
|
+
const settled = await Promise.allSettled(
|
|
427
|
+
config.agents.map((a) => a.run(task))
|
|
428
|
+
);
|
|
429
|
+
for (let i = 0; i < config.agents.length; i++) {
|
|
430
|
+
const a = config.agents[i];
|
|
431
|
+
const s = settled[i];
|
|
432
|
+
if (s.status === "fulfilled") {
|
|
433
|
+
agentResults.set(a.name, s.value);
|
|
434
|
+
} else {
|
|
435
|
+
agentResults.set(a.name, makeErrorResult(a.name, s.reason));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const lastAgent = config.agents[config.agents.length - 1];
|
|
439
|
+
const lastResult = agentResults.get(lastAgent.name);
|
|
440
|
+
const allResults = [...agentResults.values()];
|
|
441
|
+
return {
|
|
442
|
+
text: lastResult.text,
|
|
443
|
+
agentResults,
|
|
444
|
+
totalTokens: aggregateTokens(allResults),
|
|
445
|
+
totalCost: aggregateCost(allResults),
|
|
446
|
+
latencyMs: Date.now() - startTime
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
async function runConditional(task) {
|
|
450
|
+
const startTime = Date.now();
|
|
451
|
+
if (config.strategy !== "conditional") throw new Error("unreachable");
|
|
452
|
+
const targetName = config.router(task);
|
|
453
|
+
const target = agentMap.get(targetName);
|
|
454
|
+
if (!target) {
|
|
455
|
+
throw new AidError("invalid_request", `Unknown agent in crew router: '${targetName}'`);
|
|
456
|
+
}
|
|
457
|
+
const result = await target.run(task);
|
|
458
|
+
const agentResults = /* @__PURE__ */ new Map();
|
|
459
|
+
agentResults.set(targetName, result);
|
|
460
|
+
return {
|
|
461
|
+
text: result.text,
|
|
462
|
+
agentResults,
|
|
463
|
+
totalTokens: result.tokens,
|
|
464
|
+
totalCost: result.cost,
|
|
465
|
+
latencyMs: Date.now() - startTime
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
async run(task) {
|
|
470
|
+
switch (config.strategy) {
|
|
471
|
+
case "sequential":
|
|
472
|
+
return runSequential(task);
|
|
473
|
+
case "parallel":
|
|
474
|
+
return runParallel(task);
|
|
475
|
+
case "conditional":
|
|
476
|
+
return runConditional(task);
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
on(event, cb) {
|
|
480
|
+
emitter.on(event, cb);
|
|
481
|
+
},
|
|
482
|
+
off(event, cb) {
|
|
483
|
+
emitter.off(event, cb);
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/agents/workflow.ts
|
|
489
|
+
function workflow(config) {
|
|
490
|
+
const emitter = new AgentEventEmitter();
|
|
491
|
+
const wiredAgents = /* @__PURE__ */ new Set();
|
|
492
|
+
for (const step of config.steps) {
|
|
493
|
+
if (!wiredAgents.has(step.agent.name)) {
|
|
494
|
+
const events = ["step:start", "step:end", "tool:call", "tool:result", "handoff", "message", "done", "error"];
|
|
495
|
+
for (const event of events) {
|
|
496
|
+
step.agent.on(event, (payload) => {
|
|
497
|
+
emitter.emit(event, { ...payload, agent: step.agent.name });
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
wiredAgents.add(step.agent.name);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
function getDependencies(step) {
|
|
504
|
+
if (!step.input) return [];
|
|
505
|
+
if (typeof step.input === "string") return [step.input];
|
|
506
|
+
return step.input;
|
|
507
|
+
}
|
|
508
|
+
async function run(task) {
|
|
509
|
+
const startTime = Date.now();
|
|
510
|
+
const stepResults = /* @__PURE__ */ new Map();
|
|
511
|
+
const skippedSteps = /* @__PURE__ */ new Set();
|
|
512
|
+
const failedSteps = /* @__PURE__ */ new Set();
|
|
513
|
+
const stepMap = /* @__PURE__ */ new Map();
|
|
514
|
+
for (const step of config.steps) {
|
|
515
|
+
stepMap.set(step.name, step);
|
|
516
|
+
}
|
|
517
|
+
const completed = /* @__PURE__ */ new Set();
|
|
518
|
+
const pending = new Set(config.steps.map((s) => s.name));
|
|
519
|
+
while (pending.size > 0) {
|
|
520
|
+
const ready = [];
|
|
521
|
+
for (const name of pending) {
|
|
522
|
+
const step = stepMap.get(name);
|
|
523
|
+
const deps = getDependencies(step);
|
|
524
|
+
const allDepsResolved = deps.every(
|
|
525
|
+
(d) => completed.has(d) || skippedSteps.has(d) || failedSteps.has(d)
|
|
526
|
+
);
|
|
527
|
+
if (allDepsResolved) {
|
|
528
|
+
ready.push(step);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (ready.length === 0 && pending.size > 0) {
|
|
532
|
+
throw new AidError("workflow_error", "Workflow has unresolvable dependencies");
|
|
533
|
+
}
|
|
534
|
+
const results = await Promise.allSettled(
|
|
535
|
+
ready.map(async (step) => {
|
|
536
|
+
const deps = getDependencies(step);
|
|
537
|
+
const hasFailedDep = deps.some((d) => failedSteps.has(d));
|
|
538
|
+
if (hasFailedDep) {
|
|
539
|
+
failedSteps.add(step.name);
|
|
540
|
+
pending.delete(step.name);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (step.condition && !step.condition(stepResults)) {
|
|
544
|
+
skippedSteps.add(step.name);
|
|
545
|
+
pending.delete(step.name);
|
|
546
|
+
completed.add(step.name);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
let input;
|
|
550
|
+
if (!step.input) {
|
|
551
|
+
input = task;
|
|
552
|
+
} else if (typeof step.input === "string") {
|
|
553
|
+
const depResult = stepResults.get(step.input);
|
|
554
|
+
input = depResult ? depResult.text : task;
|
|
555
|
+
} else {
|
|
556
|
+
const parts = step.input.filter((name) => stepResults.has(name)).map((name) => `[${name} output]:
|
|
557
|
+
${stepResults.get(name).text}`);
|
|
558
|
+
input = parts.length > 0 ? parts.join("\n\n") : task;
|
|
559
|
+
}
|
|
560
|
+
const result = await step.agent.run(input);
|
|
561
|
+
stepResults.set(step.name, result);
|
|
562
|
+
pending.delete(step.name);
|
|
563
|
+
completed.add(step.name);
|
|
564
|
+
})
|
|
565
|
+
);
|
|
566
|
+
for (let i = 0; i < ready.length; i++) {
|
|
567
|
+
const step = ready[i];
|
|
568
|
+
const r = results[i];
|
|
569
|
+
if (r.status === "rejected") {
|
|
570
|
+
failedSteps.add(step.name);
|
|
571
|
+
pending.delete(step.name);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const lastStepName = config.steps[config.steps.length - 1].name;
|
|
575
|
+
if (failedSteps.has(lastStepName)) {
|
|
576
|
+
throw new AidError("workflow_error", `Workflow failed: terminal step '${lastStepName}' failed`);
|
|
577
|
+
}
|
|
578
|
+
const lastStep = stepMap.get(lastStepName);
|
|
579
|
+
const lastDeps = getDependencies(lastStep);
|
|
580
|
+
if (lastDeps.some((d) => failedSteps.has(d)) && !completed.has(lastStepName)) {
|
|
581
|
+
throw new AidError("workflow_error", `Workflow failed: no path to terminal step '${lastStepName}'`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
let lastText = "";
|
|
585
|
+
for (let i = config.steps.length - 1; i >= 0; i--) {
|
|
586
|
+
const name = config.steps[i].name;
|
|
587
|
+
if (stepResults.has(name)) {
|
|
588
|
+
lastText = stepResults.get(name).text;
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const allResults = [...stepResults.values()];
|
|
593
|
+
return {
|
|
594
|
+
text: lastText,
|
|
595
|
+
stepResults,
|
|
596
|
+
totalTokens: allResults.reduce(
|
|
597
|
+
(acc, r) => ({
|
|
598
|
+
input: acc.input + r.tokens.input,
|
|
599
|
+
output: acc.output + r.tokens.output,
|
|
600
|
+
total: acc.total + r.tokens.total
|
|
601
|
+
}),
|
|
602
|
+
{ input: 0, output: 0, total: 0 }
|
|
603
|
+
),
|
|
604
|
+
totalCost: allResults.reduce((acc, r) => acc + r.cost, 0),
|
|
605
|
+
latencyMs: Date.now() - startTime
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
run,
|
|
610
|
+
on(event, cb) {
|
|
611
|
+
emitter.on(event, cb);
|
|
612
|
+
},
|
|
613
|
+
off(event, cb) {
|
|
614
|
+
emitter.off(event, cb);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/agents/swarm.ts
|
|
620
|
+
var HANDOFF_SENTINEL = "__handoff_signal__";
|
|
621
|
+
function makeHandoffTool(agentNames) {
|
|
622
|
+
return {
|
|
623
|
+
name: "__handoff",
|
|
624
|
+
description: `Transfer this conversation to another agent. Available agents: ${agentNames.join(", ")}`,
|
|
625
|
+
parameters: {
|
|
626
|
+
type: "object",
|
|
627
|
+
properties: {
|
|
628
|
+
to: { type: "string", description: "Name of the agent to hand off to" },
|
|
629
|
+
message: { type: "string", description: "Context/reason for the handoff" }
|
|
630
|
+
},
|
|
631
|
+
required: ["to", "message"]
|
|
632
|
+
},
|
|
633
|
+
handler: async (args) => {
|
|
634
|
+
return `${HANDOFF_SENTINEL}:${args.to}:${args.message}`;
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function swarm(config) {
|
|
639
|
+
const emitter = new AgentEventEmitter();
|
|
640
|
+
const maxHandoffs = config.maxHandoffs ?? 10;
|
|
641
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
642
|
+
const agentNames = config.agents.map((a) => a.name);
|
|
643
|
+
for (const a of config.agents) {
|
|
644
|
+
agentMap.set(a.name, a);
|
|
645
|
+
}
|
|
646
|
+
function wireAgentEvents(a) {
|
|
647
|
+
const events = ["step:start", "step:end", "tool:call", "tool:result", "message", "done", "error"];
|
|
648
|
+
for (const event of events) {
|
|
649
|
+
a.on(event, (payload) => {
|
|
650
|
+
emitter.emit(event, { ...payload, agent: a.name });
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async function run(task) {
|
|
655
|
+
const startTime = Date.now();
|
|
656
|
+
const handoffChain = [];
|
|
657
|
+
const agentResults = /* @__PURE__ */ new Map();
|
|
658
|
+
let handoffCount = 0;
|
|
659
|
+
const entryAgent = agentMap.get(config.entry);
|
|
660
|
+
if (!entryAgent) {
|
|
661
|
+
throw new AidError("handoff_error", `Unknown entry agent: '${config.entry}'`);
|
|
662
|
+
}
|
|
663
|
+
let currentAgentName = config.entry;
|
|
664
|
+
let currentTask = task;
|
|
665
|
+
const handoffTool = makeHandoffTool(agentNames);
|
|
666
|
+
while (true) {
|
|
667
|
+
handoffChain.push(currentAgentName);
|
|
668
|
+
const currentAgent = agentMap.get(currentAgentName);
|
|
669
|
+
const agentWithHandoff = currentAgent.clone({
|
|
670
|
+
tools: [...currentAgent.config.tools ?? [], handoffTool]
|
|
671
|
+
});
|
|
672
|
+
wireAgentEvents(agentWithHandoff);
|
|
673
|
+
const result = await agentWithHandoff.run(currentTask);
|
|
674
|
+
agentResults.set(currentAgentName, result);
|
|
675
|
+
let handoffDetected = false;
|
|
676
|
+
let handoffTo = "";
|
|
677
|
+
let handoffMessage = "";
|
|
678
|
+
for (const tc of result.toolCalls) {
|
|
679
|
+
if (tc.name === "__handoff") {
|
|
680
|
+
handoffDetected = true;
|
|
681
|
+
handoffTo = tc.args.to;
|
|
682
|
+
handoffMessage = tc.args.message;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (!handoffDetected) {
|
|
687
|
+
const allResults = [...agentResults.values()];
|
|
688
|
+
return {
|
|
689
|
+
text: result.text,
|
|
690
|
+
handoffChain,
|
|
691
|
+
agentResults,
|
|
692
|
+
totalTokens: allResults.reduce(
|
|
693
|
+
(acc, r) => ({
|
|
694
|
+
input: acc.input + r.tokens.input,
|
|
695
|
+
output: acc.output + r.tokens.output,
|
|
696
|
+
total: acc.total + r.tokens.total
|
|
697
|
+
}),
|
|
698
|
+
{ input: 0, output: 0, total: 0 }
|
|
699
|
+
),
|
|
700
|
+
totalCost: allResults.reduce((acc, r) => acc + r.cost, 0),
|
|
701
|
+
latencyMs: Date.now() - startTime
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
handoffCount++;
|
|
705
|
+
if (handoffCount > maxHandoffs) {
|
|
706
|
+
throw new AidError("handoff_error", `Exceeded maximum handoffs (${maxHandoffs})`);
|
|
707
|
+
}
|
|
708
|
+
if (!agentMap.has(handoffTo)) {
|
|
709
|
+
throw new AidError("handoff_error", `Unknown agent in handoff: '${handoffTo}'`);
|
|
710
|
+
}
|
|
711
|
+
emitter.emit("handoff", {
|
|
712
|
+
from: currentAgentName,
|
|
713
|
+
to: handoffTo,
|
|
714
|
+
message: handoffMessage,
|
|
715
|
+
agent: currentAgentName
|
|
716
|
+
});
|
|
717
|
+
currentAgentName = handoffTo;
|
|
718
|
+
currentTask = `${task}
|
|
719
|
+
|
|
720
|
+
Context from previous agent (${result.text ? "partial response" : "handoff"}): ${handoffMessage}`;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return {
|
|
724
|
+
run,
|
|
725
|
+
on(event, cb) {
|
|
726
|
+
emitter.on(event, cb);
|
|
727
|
+
},
|
|
728
|
+
off(event, cb) {
|
|
729
|
+
emitter.off(event, cb);
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
export {
|
|
734
|
+
AgentEventEmitter,
|
|
735
|
+
InMemoryProvider,
|
|
736
|
+
agent,
|
|
737
|
+
crew,
|
|
738
|
+
swarm,
|
|
739
|
+
tool,
|
|
740
|
+
workflow
|
|
741
|
+
};
|