@samrahimi/smol-js 0.5.0 → 0.6.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/cli.js +3159 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.mts +459 -111
- package/dist/index.d.ts +459 -111
- package/dist/index.js +1444 -140
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1433 -140
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -4
package/dist/index.js
CHANGED
|
@@ -35,18 +35,29 @@ __export(index_exports, {
|
|
|
35
35
|
AgentMemory: () => AgentMemory,
|
|
36
36
|
AgentTool: () => AgentTool,
|
|
37
37
|
CodeAgent: () => CodeAgent,
|
|
38
|
+
CurlTool: () => CurlTool,
|
|
39
|
+
ExaGetContentsTool: () => ExaGetContentsTool,
|
|
40
|
+
ExaResearchTool: () => ExaResearchTool,
|
|
41
|
+
ExaSearchTool: () => ExaSearchTool,
|
|
38
42
|
FINAL_ANSWER_PROMPT: () => FINAL_ANSWER_PROMPT,
|
|
39
43
|
FinalAnswerTool: () => FinalAnswerTool,
|
|
40
44
|
LocalExecutor: () => LocalExecutor,
|
|
41
45
|
LogLevel: () => LogLevel,
|
|
42
46
|
Model: () => Model,
|
|
43
47
|
OpenAIModel: () => OpenAIModel,
|
|
48
|
+
Orchestrator: () => Orchestrator,
|
|
49
|
+
ReadFileTool: () => ReadFileTool,
|
|
44
50
|
Tool: () => Tool,
|
|
51
|
+
ToolUseAgent: () => ToolUseAgent,
|
|
45
52
|
UserInputTool: () => UserInputTool,
|
|
53
|
+
WriteFileTool: () => WriteFileTool,
|
|
54
|
+
YAMLLoader: () => YAMLLoader,
|
|
46
55
|
agentAsTool: () => agentAsTool,
|
|
47
56
|
createTool: () => createTool,
|
|
48
57
|
finalAnswerTool: () => finalAnswerTool,
|
|
58
|
+
formatToolDescriptions: () => formatToolDescriptions,
|
|
49
59
|
generateSystemPrompt: () => generateSystemPrompt,
|
|
60
|
+
generateToolUseSystemPrompt: () => generateToolUseSystemPrompt,
|
|
50
61
|
getErrorRecoveryPrompt: () => getErrorRecoveryPrompt
|
|
51
62
|
});
|
|
52
63
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -61,31 +72,43 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
|
61
72
|
})(LogLevel || {});
|
|
62
73
|
|
|
63
74
|
// src/memory/AgentMemory.ts
|
|
75
|
+
function estimateTokens(text) {
|
|
76
|
+
return Math.ceil(text.length / 4);
|
|
77
|
+
}
|
|
78
|
+
function estimateMessagesTokens(messages) {
|
|
79
|
+
let total = 0;
|
|
80
|
+
for (const msg of messages) {
|
|
81
|
+
total += estimateTokens(msg.content ?? "");
|
|
82
|
+
if (msg.toolCalls) {
|
|
83
|
+
total += estimateTokens(JSON.stringify(msg.toolCalls));
|
|
84
|
+
}
|
|
85
|
+
total += 4;
|
|
86
|
+
}
|
|
87
|
+
return total;
|
|
88
|
+
}
|
|
64
89
|
var AgentMemory = class {
|
|
65
|
-
/**
|
|
66
|
-
* System prompt step (always first)
|
|
67
|
-
*/
|
|
90
|
+
/** System prompt step (always first) */
|
|
68
91
|
systemPrompt;
|
|
69
|
-
/**
|
|
70
|
-
* All execution steps
|
|
71
|
-
*/
|
|
92
|
+
/** All execution steps */
|
|
72
93
|
steps = [];
|
|
73
|
-
|
|
94
|
+
maxContextLength;
|
|
95
|
+
memoryStrategy;
|
|
96
|
+
model;
|
|
97
|
+
constructor(systemPrompt, config) {
|
|
74
98
|
this.systemPrompt = {
|
|
75
99
|
type: "system",
|
|
76
100
|
content: systemPrompt,
|
|
77
101
|
timestamp: Date.now()
|
|
78
102
|
};
|
|
103
|
+
this.maxContextLength = config?.maxContextLength ?? 1e5;
|
|
104
|
+
this.memoryStrategy = config?.memoryStrategy ?? "truncate";
|
|
105
|
+
this.model = config?.model;
|
|
79
106
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Reset memory, keeping only the system prompt.
|
|
82
|
-
*/
|
|
107
|
+
/** Reset memory, keeping only the system prompt */
|
|
83
108
|
reset() {
|
|
84
109
|
this.steps = [];
|
|
85
110
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Add a task step.
|
|
88
|
-
*/
|
|
111
|
+
/** Add a task step */
|
|
89
112
|
addTask(task) {
|
|
90
113
|
const step = {
|
|
91
114
|
type: "task",
|
|
@@ -95,25 +118,19 @@ var AgentMemory = class {
|
|
|
95
118
|
this.steps.push(step);
|
|
96
119
|
return step;
|
|
97
120
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Create a new action step.
|
|
100
|
-
*/
|
|
121
|
+
/** Create a new action step */
|
|
101
122
|
createActionStep(stepNumber) {
|
|
102
123
|
const step = {
|
|
103
124
|
type: "action",
|
|
104
125
|
stepNumber,
|
|
105
|
-
timing: {
|
|
106
|
-
startTime: Date.now()
|
|
107
|
-
},
|
|
126
|
+
timing: { startTime: Date.now() },
|
|
108
127
|
modelInputMessages: [],
|
|
109
128
|
timestamp: Date.now()
|
|
110
129
|
};
|
|
111
130
|
this.steps.push(step);
|
|
112
131
|
return step;
|
|
113
132
|
}
|
|
114
|
-
/**
|
|
115
|
-
* Add a final answer step.
|
|
116
|
-
*/
|
|
133
|
+
/** Add a final answer step */
|
|
117
134
|
addFinalAnswer(answer) {
|
|
118
135
|
const step = {
|
|
119
136
|
type: "final",
|
|
@@ -123,20 +140,17 @@ var AgentMemory = class {
|
|
|
123
140
|
this.steps.push(step);
|
|
124
141
|
return step;
|
|
125
142
|
}
|
|
126
|
-
/**
|
|
127
|
-
* Get the last step.
|
|
128
|
-
*/
|
|
143
|
+
/** Get the last step */
|
|
129
144
|
getLastStep() {
|
|
130
145
|
return this.steps[this.steps.length - 1];
|
|
131
146
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Get all action steps.
|
|
134
|
-
*/
|
|
147
|
+
/** Get all action steps */
|
|
135
148
|
getActionSteps() {
|
|
136
149
|
return this.steps.filter((s) => s.type === "action");
|
|
137
150
|
}
|
|
138
151
|
/**
|
|
139
152
|
* Convert memory to messages for LLM context.
|
|
153
|
+
* Handles both CodeAgent (observation-based) and ToolUseAgent (tool_call-based) patterns.
|
|
140
154
|
*/
|
|
141
155
|
toMessages() {
|
|
142
156
|
const messages = [];
|
|
@@ -153,23 +167,40 @@ var AgentMemory = class {
|
|
|
153
167
|
});
|
|
154
168
|
break;
|
|
155
169
|
case "action":
|
|
156
|
-
if (step.
|
|
170
|
+
if (step.toolCalls && step.toolCalls.length > 0) {
|
|
157
171
|
messages.push({
|
|
158
172
|
role: "assistant",
|
|
159
|
-
content: step.modelOutputMessage
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
if (step.observation) {
|
|
163
|
-
messages.push({
|
|
164
|
-
role: "user",
|
|
165
|
-
content: step.observation
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
if (step.error) {
|
|
169
|
-
messages.push({
|
|
170
|
-
role: "user",
|
|
171
|
-
content: `Error: ${step.error.message}`
|
|
173
|
+
content: step.modelOutputMessage?.content ?? null,
|
|
174
|
+
toolCalls: step.toolCalls
|
|
172
175
|
});
|
|
176
|
+
if (step.toolResults) {
|
|
177
|
+
for (const result of step.toolResults) {
|
|
178
|
+
messages.push({
|
|
179
|
+
role: "tool",
|
|
180
|
+
content: result.error ? `Error: ${result.error}` : typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2),
|
|
181
|
+
toolCallId: result.toolCallId
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
if (step.modelOutputMessage) {
|
|
187
|
+
messages.push({
|
|
188
|
+
role: "assistant",
|
|
189
|
+
content: step.modelOutputMessage.content
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (step.observation) {
|
|
193
|
+
messages.push({
|
|
194
|
+
role: "user",
|
|
195
|
+
content: step.observation
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (step.error && !step.observation) {
|
|
199
|
+
messages.push({
|
|
200
|
+
role: "user",
|
|
201
|
+
content: `Error: ${step.error.message}`
|
|
202
|
+
});
|
|
203
|
+
}
|
|
173
204
|
}
|
|
174
205
|
break;
|
|
175
206
|
case "final":
|
|
@@ -179,8 +210,90 @@ var AgentMemory = class {
|
|
|
179
210
|
return messages;
|
|
180
211
|
}
|
|
181
212
|
/**
|
|
182
|
-
*
|
|
213
|
+
* Manage context length - truncate or compact if exceeded.
|
|
214
|
+
*/
|
|
215
|
+
async manageContext() {
|
|
216
|
+
const messages = this.toMessages();
|
|
217
|
+
const tokenCount = estimateMessagesTokens(messages);
|
|
218
|
+
if (tokenCount <= this.maxContextLength) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (this.memoryStrategy === "truncate") {
|
|
222
|
+
this.truncateOlderMessages();
|
|
223
|
+
} else if (this.memoryStrategy === "compact") {
|
|
224
|
+
await this.compactMessages();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Truncate older action steps to fit within context.
|
|
229
|
+
*/
|
|
230
|
+
truncateOlderMessages() {
|
|
231
|
+
const actionSteps = this.getActionSteps();
|
|
232
|
+
if (actionSteps.length <= 2) return;
|
|
233
|
+
const targetTokens = this.maxContextLength * 0.75;
|
|
234
|
+
let currentTokens = estimateMessagesTokens(this.toMessages());
|
|
235
|
+
while (currentTokens > targetTokens && this.steps.length > 2) {
|
|
236
|
+
const idx = this.steps.findIndex((s) => s.type === "action");
|
|
237
|
+
if (idx === -1) break;
|
|
238
|
+
this.steps.splice(idx, 1);
|
|
239
|
+
currentTokens = estimateMessagesTokens(this.toMessages());
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Compact older messages into a summary.
|
|
183
244
|
*/
|
|
245
|
+
async compactMessages() {
|
|
246
|
+
if (!this.model) {
|
|
247
|
+
this.truncateOlderMessages();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const actionSteps = this.getActionSteps();
|
|
251
|
+
if (actionSteps.length <= 2) return;
|
|
252
|
+
const stepsToSummarize = actionSteps.slice(0, -2);
|
|
253
|
+
const summaryContent = stepsToSummarize.map((step) => {
|
|
254
|
+
const parts = [];
|
|
255
|
+
if (step.modelOutputMessage?.content) {
|
|
256
|
+
parts.push(`Action: ${step.modelOutputMessage.content.slice(0, 200)}`);
|
|
257
|
+
}
|
|
258
|
+
if (step.observation) {
|
|
259
|
+
parts.push(`Observation: ${step.observation.slice(0, 200)}`);
|
|
260
|
+
}
|
|
261
|
+
if (step.toolResults) {
|
|
262
|
+
for (const r of step.toolResults) {
|
|
263
|
+
const resultStr = typeof r.result === "string" ? r.result : JSON.stringify(r.result);
|
|
264
|
+
parts.push(`Tool ${r.toolName}: ${resultStr.slice(0, 200)}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return parts.join("\n");
|
|
268
|
+
}).join("\n---\n");
|
|
269
|
+
try {
|
|
270
|
+
const summaryResponse = await this.model.generate([
|
|
271
|
+
{
|
|
272
|
+
role: "system",
|
|
273
|
+
content: "Summarize the following agent execution history concisely, preserving key findings and results. Be brief but complete."
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
role: "user",
|
|
277
|
+
content: summaryContent
|
|
278
|
+
}
|
|
279
|
+
]);
|
|
280
|
+
const recentSteps = this.steps.filter(
|
|
281
|
+
(s) => s.type === "task" || s.type === "final" || s.type === "action" && actionSteps.indexOf(s) >= actionSteps.length - 2
|
|
282
|
+
);
|
|
283
|
+
this.steps = [
|
|
284
|
+
{
|
|
285
|
+
type: "task",
|
|
286
|
+
task: `[Context Summary from previous steps]
|
|
287
|
+
${summaryResponse.content}`,
|
|
288
|
+
timestamp: Date.now()
|
|
289
|
+
},
|
|
290
|
+
...recentSteps
|
|
291
|
+
];
|
|
292
|
+
} catch {
|
|
293
|
+
this.truncateOlderMessages();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** Get total token usage across all steps */
|
|
184
297
|
getTotalTokenUsage() {
|
|
185
298
|
let inputTokens = 0;
|
|
186
299
|
let outputTokens = 0;
|
|
@@ -196,9 +309,11 @@ var AgentMemory = class {
|
|
|
196
309
|
totalTokens: inputTokens + outputTokens
|
|
197
310
|
};
|
|
198
311
|
}
|
|
199
|
-
/**
|
|
200
|
-
|
|
201
|
-
|
|
312
|
+
/** Get current estimated token count */
|
|
313
|
+
getEstimatedTokenCount() {
|
|
314
|
+
return estimateMessagesTokens(this.toMessages());
|
|
315
|
+
}
|
|
316
|
+
/** Get a summary of the memory for logging */
|
|
202
317
|
getSummary() {
|
|
203
318
|
const actionSteps = this.getActionSteps();
|
|
204
319
|
const lines = [
|
|
@@ -212,9 +327,7 @@ var AgentMemory = class {
|
|
|
212
327
|
}
|
|
213
328
|
return lines.join("\n");
|
|
214
329
|
}
|
|
215
|
-
/**
|
|
216
|
-
* Serialize memory to JSON.
|
|
217
|
-
*/
|
|
330
|
+
/** Serialize memory to JSON */
|
|
218
331
|
toJSON() {
|
|
219
332
|
return {
|
|
220
333
|
systemPrompt: this.systemPrompt,
|
|
@@ -503,35 +616,24 @@ ${"\u2550".repeat(60)}
|
|
|
503
616
|
};
|
|
504
617
|
|
|
505
618
|
// src/agents/Agent.ts
|
|
619
|
+
var DEFAULT_MAX_CONTEXT_LENGTH = 1e5;
|
|
506
620
|
var Agent = class {
|
|
507
|
-
/**
|
|
508
|
-
* The LLM model for generation
|
|
509
|
-
*/
|
|
621
|
+
/** The LLM model for generation */
|
|
510
622
|
model;
|
|
511
|
-
/**
|
|
512
|
-
* Available tools mapped by name
|
|
513
|
-
*/
|
|
623
|
+
/** Available tools mapped by name */
|
|
514
624
|
tools = /* @__PURE__ */ new Map();
|
|
515
|
-
/**
|
|
516
|
-
* Agent memory tracking all steps
|
|
517
|
-
*/
|
|
625
|
+
/** Agent memory tracking all steps */
|
|
518
626
|
memory;
|
|
519
|
-
/**
|
|
520
|
-
* Logger for formatted output
|
|
521
|
-
*/
|
|
627
|
+
/** Logger for formatted output */
|
|
522
628
|
logger;
|
|
523
|
-
/**
|
|
524
|
-
* Configuration options
|
|
525
|
-
*/
|
|
629
|
+
/** Configuration options */
|
|
526
630
|
config;
|
|
527
|
-
/**
|
|
528
|
-
* Current step number
|
|
529
|
-
*/
|
|
631
|
+
/** Current step number */
|
|
530
632
|
currentStep = 0;
|
|
531
|
-
/**
|
|
532
|
-
* Whether the agent is currently running
|
|
533
|
-
*/
|
|
633
|
+
/** Whether the agent is currently running */
|
|
534
634
|
isRunning = false;
|
|
635
|
+
/** Whether the agent has been initialized at least once */
|
|
636
|
+
initialized = false;
|
|
535
637
|
constructor(config) {
|
|
536
638
|
this.model = config.model;
|
|
537
639
|
this.logger = new AgentLogger(config.verboseLevel ?? 1 /* INFO */);
|
|
@@ -540,7 +642,14 @@ var Agent = class {
|
|
|
540
642
|
codeExecutionDelay: config.codeExecutionDelay ?? 5e3,
|
|
541
643
|
customInstructions: config.customInstructions ?? "",
|
|
542
644
|
verboseLevel: config.verboseLevel ?? 1 /* INFO */,
|
|
543
|
-
streamOutputs: config.streamOutputs ?? true
|
|
645
|
+
streamOutputs: config.streamOutputs ?? true,
|
|
646
|
+
persistent: config.persistent ?? false,
|
|
647
|
+
maxContextLength: config.maxContextLength ?? DEFAULT_MAX_CONTEXT_LENGTH,
|
|
648
|
+
memoryStrategy: config.memoryStrategy ?? "truncate",
|
|
649
|
+
maxTokens: config.maxTokens,
|
|
650
|
+
temperature: config.temperature,
|
|
651
|
+
name: config.name ?? "Agent",
|
|
652
|
+
onEvent: config.onEvent
|
|
544
653
|
};
|
|
545
654
|
if (config.tools) {
|
|
546
655
|
for (const tool of config.tools) {
|
|
@@ -550,27 +659,31 @@ var Agent = class {
|
|
|
550
659
|
}
|
|
551
660
|
/**
|
|
552
661
|
* Run the agent on a task.
|
|
553
|
-
*
|
|
554
|
-
* @param task - The task description
|
|
555
|
-
* @param reset - Whether to reset memory before running
|
|
556
|
-
* @returns The final result
|
|
557
662
|
*/
|
|
558
663
|
async run(task, reset = true) {
|
|
559
664
|
const startTime = Date.now();
|
|
560
|
-
|
|
665
|
+
const shouldReset = !this.config.persistent ? reset || !this.memory : !this.initialized;
|
|
666
|
+
if (shouldReset) {
|
|
561
667
|
const systemPrompt = this.initializeSystemPrompt();
|
|
562
|
-
this.memory = new AgentMemory(systemPrompt
|
|
668
|
+
this.memory = new AgentMemory(systemPrompt, {
|
|
669
|
+
maxContextLength: this.config.maxContextLength,
|
|
670
|
+
memoryStrategy: this.config.memoryStrategy,
|
|
671
|
+
model: this.model
|
|
672
|
+
});
|
|
563
673
|
this.currentStep = 0;
|
|
674
|
+
this.initialized = true;
|
|
564
675
|
}
|
|
565
676
|
this.memory.addTask(task);
|
|
566
677
|
this.isRunning = true;
|
|
567
|
-
this.
|
|
678
|
+
this.emitEvent("agent_start", { task, name: this.config.name });
|
|
679
|
+
this.logger.header(`Starting ${this.config.name}: ${task.slice(0, 80)}${task.length > 80 ? "..." : ""}`);
|
|
568
680
|
let finalOutput = null;
|
|
569
681
|
let isFinalAnswer = false;
|
|
570
682
|
try {
|
|
571
683
|
while (this.currentStep < this.config.maxSteps && this.isRunning) {
|
|
572
684
|
this.currentStep++;
|
|
573
685
|
this.logger.stepProgress(this.currentStep, this.config.maxSteps);
|
|
686
|
+
this.emitEvent("agent_step", { step: this.currentStep, maxSteps: this.config.maxSteps });
|
|
574
687
|
const memoryStep = this.memory.createActionStep(this.currentStep);
|
|
575
688
|
try {
|
|
576
689
|
const actionOutput = await this.executeStep(memoryStep);
|
|
@@ -589,7 +702,9 @@ var Agent = class {
|
|
|
589
702
|
memoryStep.timing.endTime = Date.now();
|
|
590
703
|
memoryStep.timing.duration = memoryStep.timing.endTime - memoryStep.timing.startTime;
|
|
591
704
|
this.logger.error("Step execution failed", error);
|
|
705
|
+
this.emitEvent("agent_error", { error: error.message, step: this.currentStep });
|
|
592
706
|
}
|
|
707
|
+
await this.memory.manageContext();
|
|
593
708
|
}
|
|
594
709
|
if (!isFinalAnswer && this.currentStep >= this.config.maxSteps) {
|
|
595
710
|
this.logger.warn(`Max steps (${this.config.maxSteps}) reached without final answer`);
|
|
@@ -601,13 +716,10 @@ var Agent = class {
|
|
|
601
716
|
const duration = Date.now() - startTime;
|
|
602
717
|
const tokenUsage = this.memory.getTotalTokenUsage();
|
|
603
718
|
this.memory.addFinalAnswer(finalOutput);
|
|
719
|
+
this.emitEvent("agent_end", { output: finalOutput, duration, tokenUsage });
|
|
604
720
|
this.logger.info(`
|
|
605
|
-
|
|
606
|
-
this.logger.info(
|
|
607
|
-
const logPath = this.logger.getLogPath();
|
|
608
|
-
if (logPath) {
|
|
609
|
-
this.logger.info(`\u{1F4C1} Log file: ${logPath}`);
|
|
610
|
-
}
|
|
721
|
+
Total time: ${(duration / 1e3).toFixed(2)}s`);
|
|
722
|
+
this.logger.info(`Total tokens: ${tokenUsage.totalTokens}`);
|
|
611
723
|
return {
|
|
612
724
|
output: finalOutput,
|
|
613
725
|
steps: this.memory.steps,
|
|
@@ -623,49 +735,48 @@ var Agent = class {
|
|
|
623
735
|
const messages = this.memory.toMessages();
|
|
624
736
|
messages.push({
|
|
625
737
|
role: "user",
|
|
626
|
-
content: `You have reached the maximum number of steps. Based on your work so far, provide the best answer you can for the original task: "${task}"
|
|
627
|
-
|
|
628
|
-
|
|
738
|
+
content: `You have reached the maximum number of steps. Based on your work so far, provide the best answer you can for the original task: "${task}". Summarize what you accomplished and provide a final answer.`
|
|
739
|
+
});
|
|
740
|
+
const response = await this.model.generate(messages, {
|
|
741
|
+
maxTokens: this.config.maxTokens,
|
|
742
|
+
temperature: this.config.temperature
|
|
629
743
|
});
|
|
630
|
-
const response = await this.model.generate(messages);
|
|
631
744
|
return response.content;
|
|
632
745
|
}
|
|
633
|
-
/**
|
|
634
|
-
|
|
635
|
-
|
|
746
|
+
/** Emit an orchestration event */
|
|
747
|
+
emitEvent(type, data) {
|
|
748
|
+
if (this.config.onEvent) {
|
|
749
|
+
this.config.onEvent({ type, data });
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
/** Stop the agent */
|
|
636
753
|
stop() {
|
|
637
754
|
this.isRunning = false;
|
|
638
755
|
this.logger.info("Agent stopped by user");
|
|
639
756
|
}
|
|
640
|
-
/**
|
|
641
|
-
* Get the current memory.
|
|
642
|
-
*/
|
|
757
|
+
/** Get the current memory */
|
|
643
758
|
getMemory() {
|
|
644
759
|
return this.memory;
|
|
645
760
|
}
|
|
646
|
-
/**
|
|
647
|
-
* Get registered tools.
|
|
648
|
-
*/
|
|
761
|
+
/** Get registered tools */
|
|
649
762
|
getTools() {
|
|
650
763
|
return this.tools;
|
|
651
764
|
}
|
|
652
|
-
/**
|
|
653
|
-
* Add a tool to the agent.
|
|
654
|
-
*/
|
|
765
|
+
/** Add a tool to the agent */
|
|
655
766
|
addTool(tool) {
|
|
656
767
|
this.tools.set(tool.name, tool);
|
|
657
768
|
}
|
|
658
|
-
/**
|
|
659
|
-
* Remove a tool from the agent.
|
|
660
|
-
*/
|
|
769
|
+
/** Remove a tool from the agent */
|
|
661
770
|
removeTool(name) {
|
|
662
771
|
return this.tools.delete(name);
|
|
663
772
|
}
|
|
664
|
-
/**
|
|
665
|
-
|
|
666
|
-
|
|
773
|
+
/** Get agent name */
|
|
774
|
+
getName() {
|
|
775
|
+
return this.config.name;
|
|
776
|
+
}
|
|
777
|
+
/** Sleep for a specified duration */
|
|
667
778
|
sleep(ms) {
|
|
668
|
-
return new Promise((
|
|
779
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
669
780
|
}
|
|
670
781
|
};
|
|
671
782
|
|
|
@@ -1149,9 +1260,6 @@ var Tool = class {
|
|
|
1149
1260
|
}
|
|
1150
1261
|
providedKeys.delete(key);
|
|
1151
1262
|
}
|
|
1152
|
-
if (providedKeys.size > 0) {
|
|
1153
|
-
console.warn(`Unknown arguments provided to ${this.name}: ${[...providedKeys].join(", ")}`);
|
|
1154
|
-
}
|
|
1155
1263
|
}
|
|
1156
1264
|
/**
|
|
1157
1265
|
* Check if a value matches the expected type.
|
|
@@ -1194,6 +1302,62 @@ ${argsDoc}
|
|
|
1194
1302
|
async function ${this.name}(${argsSignature}): Promise<${this.typeToJsType(this.outputType)}> { ... }
|
|
1195
1303
|
`.trim();
|
|
1196
1304
|
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Generate an OpenAI-compatible tool definition for function calling.
|
|
1307
|
+
*/
|
|
1308
|
+
toOpenAITool() {
|
|
1309
|
+
const properties = {};
|
|
1310
|
+
const required = [];
|
|
1311
|
+
for (const [key, input] of Object.entries(this.inputs)) {
|
|
1312
|
+
const prop = {
|
|
1313
|
+
type: this.typeToJsonSchemaType(input.type),
|
|
1314
|
+
description: input.description
|
|
1315
|
+
};
|
|
1316
|
+
if (input.enum) {
|
|
1317
|
+
prop.enum = input.enum;
|
|
1318
|
+
}
|
|
1319
|
+
if (input.default !== void 0) {
|
|
1320
|
+
prop.default = input.default;
|
|
1321
|
+
}
|
|
1322
|
+
properties[key] = prop;
|
|
1323
|
+
if (input.required !== false) {
|
|
1324
|
+
required.push(key);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
type: "function",
|
|
1329
|
+
function: {
|
|
1330
|
+
name: this.name,
|
|
1331
|
+
description: this.description,
|
|
1332
|
+
parameters: {
|
|
1333
|
+
type: "object",
|
|
1334
|
+
properties,
|
|
1335
|
+
...required.length > 0 && { required }
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Convert tool input type to JSON Schema type.
|
|
1342
|
+
*/
|
|
1343
|
+
typeToJsonSchemaType(type) {
|
|
1344
|
+
switch (type) {
|
|
1345
|
+
case "string":
|
|
1346
|
+
return "string";
|
|
1347
|
+
case "number":
|
|
1348
|
+
return "number";
|
|
1349
|
+
case "boolean":
|
|
1350
|
+
return "boolean";
|
|
1351
|
+
case "array":
|
|
1352
|
+
return "array";
|
|
1353
|
+
case "object":
|
|
1354
|
+
return "object";
|
|
1355
|
+
case "any":
|
|
1356
|
+
return "string";
|
|
1357
|
+
default:
|
|
1358
|
+
return "string";
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1197
1361
|
/**
|
|
1198
1362
|
* Convert tool input type to JS/TS type string.
|
|
1199
1363
|
*/
|
|
@@ -1281,12 +1445,12 @@ var UserInputTool = class extends Tool {
|
|
|
1281
1445
|
input: process.stdin,
|
|
1282
1446
|
output: process.stdout
|
|
1283
1447
|
});
|
|
1284
|
-
return new Promise((
|
|
1448
|
+
return new Promise((resolve5) => {
|
|
1285
1449
|
rl.question(`
|
|
1286
1450
|
[Agent asks]: ${question}
|
|
1287
1451
|
Your response: `, (answer) => {
|
|
1288
1452
|
rl.close();
|
|
1289
|
-
|
|
1453
|
+
resolve5(answer);
|
|
1290
1454
|
});
|
|
1291
1455
|
});
|
|
1292
1456
|
}
|
|
@@ -1551,7 +1715,9 @@ ${result.error.message}`;
|
|
|
1551
1715
|
this.logger.subheader("Agent thinking...");
|
|
1552
1716
|
let fullContent = "";
|
|
1553
1717
|
const generator = this.model.generateStream(messages, {
|
|
1554
|
-
stopSequences: ["Observation:", "Observation:\n"]
|
|
1718
|
+
stopSequences: ["Observation:", "Observation:\n"],
|
|
1719
|
+
maxTokens: this.config.maxTokens,
|
|
1720
|
+
temperature: this.config.temperature
|
|
1555
1721
|
});
|
|
1556
1722
|
for await (const chunk of generator) {
|
|
1557
1723
|
this.logger.streamChar(chunk);
|
|
@@ -1565,7 +1731,9 @@ ${result.error.message}`;
|
|
|
1565
1731
|
} else {
|
|
1566
1732
|
this.logger.subheader("Agent thinking...");
|
|
1567
1733
|
return this.model.generate(messages, {
|
|
1568
|
-
stopSequences: ["Observation:", "Observation:\n"]
|
|
1734
|
+
stopSequences: ["Observation:", "Observation:\n"],
|
|
1735
|
+
maxTokens: this.config.maxTokens,
|
|
1736
|
+
temperature: this.config.temperature
|
|
1569
1737
|
});
|
|
1570
1738
|
}
|
|
1571
1739
|
}
|
|
@@ -1621,6 +1789,263 @@ ${parts.join("\n\n")}`;
|
|
|
1621
1789
|
}
|
|
1622
1790
|
};
|
|
1623
1791
|
|
|
1792
|
+
// src/prompts/toolUseAgent.ts
|
|
1793
|
+
function generateToolUseSystemPrompt(variables) {
|
|
1794
|
+
const { tools, customInstructions, hasSubAgents, hasFileTools } = variables;
|
|
1795
|
+
let contentGuidelines = "";
|
|
1796
|
+
if (hasFileTools) {
|
|
1797
|
+
contentGuidelines += `
|
|
1798
|
+
## Content Output Guidelines
|
|
1799
|
+
|
|
1800
|
+
When you produce long-form content (reports, articles, analyses, or any output longer than a few paragraphs):
|
|
1801
|
+
1. **Save to file**: Use \`write_file\` to save the full content to a descriptively-named file (e.g., \`research_report.md\`, \`analysis_results.md\`).
|
|
1802
|
+
2. **Return summary + filename**: In your \`final_answer\`, provide a brief summary of what you produced AND the filename where the full content is saved. Example: "Completed the research report covering X, Y, Z. Full report saved to: research_report.md"
|
|
1803
|
+
|
|
1804
|
+
This ensures managing agents can access your full output via \`read_file\` without it being truncated in message passing.
|
|
1805
|
+
`;
|
|
1806
|
+
}
|
|
1807
|
+
if (hasSubAgents) {
|
|
1808
|
+
contentGuidelines += `
|
|
1809
|
+
## Working with Sub-Agents
|
|
1810
|
+
|
|
1811
|
+
When you delegate tasks to sub-agents:
|
|
1812
|
+
- Sub-agents return a **summary and filename** rather than the full content of long-form outputs.
|
|
1813
|
+
- To access the full content a sub-agent produced, use \`read_file\` with the filename they provide.
|
|
1814
|
+
- **Do NOT re-invoke a sub-agent to retrieve content it already created.** Instead, read the file directly.
|
|
1815
|
+
- When composing your own final output from sub-agent results, read their files as needed and synthesize.
|
|
1816
|
+
`;
|
|
1817
|
+
}
|
|
1818
|
+
if (hasSubAgents && hasFileTools) {
|
|
1819
|
+
contentGuidelines += `
|
|
1820
|
+
## When You Are Both a Manager and a Sub-Agent
|
|
1821
|
+
|
|
1822
|
+
If you manage sub-agents AND are yourself delegated tasks by a parent agent:
|
|
1823
|
+
- Follow the sub-agent content guidelines: save your own long-form output to a file, return summary + filename.
|
|
1824
|
+
- Follow the manager guidelines: read sub-agent output files rather than re-calling them.
|
|
1825
|
+
`;
|
|
1826
|
+
}
|
|
1827
|
+
return `You are an expert assistant and problem-solving agent. You solve tasks by reasoning step by step and using the tools available to you.
|
|
1828
|
+
|
|
1829
|
+
## How You Work
|
|
1830
|
+
|
|
1831
|
+
You follow a ReAct (Reasoning + Acting) framework:
|
|
1832
|
+
1. **Think**: Analyze the current situation and decide what to do next
|
|
1833
|
+
2. **Act**: Call one or more tools to perform actions
|
|
1834
|
+
3. **Observe**: Review the results of your tool calls
|
|
1835
|
+
4. Repeat until you have the final answer
|
|
1836
|
+
|
|
1837
|
+
## Available Tools
|
|
1838
|
+
|
|
1839
|
+
${tools}
|
|
1840
|
+
|
|
1841
|
+
## Rules
|
|
1842
|
+
|
|
1843
|
+
1. **Always use final_answer**: When you have the complete answer, call the \`final_answer\` tool to return it. This is mandatory - you must always end by calling final_answer.
|
|
1844
|
+
|
|
1845
|
+
2. **Think before acting**: Provide your reasoning in the content of your response before making tool calls. This helps track your thought process.
|
|
1846
|
+
|
|
1847
|
+
3. **One logical action per step**: Focus on one logical action per step. You may call multiple tools in a single step if they are independent, but avoid chaining dependent operations without reviewing intermediate results.
|
|
1848
|
+
|
|
1849
|
+
4. **Handle errors gracefully**: If a tool call fails, analyze the error and try a different approach.
|
|
1850
|
+
|
|
1851
|
+
5. **Be concise**: Keep your reasoning brief and focused. Don't over-explain.
|
|
1852
|
+
|
|
1853
|
+
6. **Use the right tool**: Choose the most appropriate tool for each action. Don't try to accomplish something a tool can do through other means.
|
|
1854
|
+
${contentGuidelines}
|
|
1855
|
+
${customInstructions ? `## Additional Instructions
|
|
1856
|
+
|
|
1857
|
+
${customInstructions}` : ""}
|
|
1858
|
+
|
|
1859
|
+
Now, let's solve the task step by step. Think carefully about what tools to use and in what order.`;
|
|
1860
|
+
}
|
|
1861
|
+
function formatToolDescriptions(tools) {
|
|
1862
|
+
return tools.map((tool) => {
|
|
1863
|
+
const params = Object.entries(tool.inputs).map(([name, input]) => {
|
|
1864
|
+
const req = input.required !== false ? " (required)" : " (optional)";
|
|
1865
|
+
return ` - ${name}${req}: ${input.description}`;
|
|
1866
|
+
}).join("\n");
|
|
1867
|
+
return `### ${tool.name}
|
|
1868
|
+
${tool.description}
|
|
1869
|
+
Parameters:
|
|
1870
|
+
${params}`;
|
|
1871
|
+
}).join("\n\n");
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
// src/agents/ToolUseAgent.ts
|
|
1875
|
+
var ToolUseAgent = class extends Agent {
|
|
1876
|
+
parallelToolCalls;
|
|
1877
|
+
constructor(config) {
|
|
1878
|
+
super(config);
|
|
1879
|
+
this.parallelToolCalls = config.parallelToolCalls ?? true;
|
|
1880
|
+
if (!this.tools.has("final_answer")) {
|
|
1881
|
+
this.tools.set("final_answer", new FinalAnswerTool());
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Initialize the system prompt with tool descriptions.
|
|
1886
|
+
*/
|
|
1887
|
+
initializeSystemPrompt() {
|
|
1888
|
+
const toolList = Array.from(this.tools.values());
|
|
1889
|
+
const toolDescriptions = formatToolDescriptions(
|
|
1890
|
+
toolList.map((t) => ({
|
|
1891
|
+
name: t.name,
|
|
1892
|
+
description: t.description,
|
|
1893
|
+
inputs: t.inputs
|
|
1894
|
+
}))
|
|
1895
|
+
);
|
|
1896
|
+
const toolNames = new Set(Array.from(this.tools.keys()));
|
|
1897
|
+
const hasSubAgents = toolList.some((t) => t.constructor.name === "AgentTool");
|
|
1898
|
+
const hasFileTools = toolNames.has("read_file") || toolNames.has("write_file") || toolNames.has("read") || toolNames.has("write");
|
|
1899
|
+
return generateToolUseSystemPrompt({
|
|
1900
|
+
tools: toolDescriptions,
|
|
1901
|
+
customInstructions: this.config.customInstructions,
|
|
1902
|
+
hasSubAgents,
|
|
1903
|
+
hasFileTools
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Execute a single step: send messages with tool definitions, process tool calls.
|
|
1908
|
+
*/
|
|
1909
|
+
async executeStep(memoryStep) {
|
|
1910
|
+
const messages = this.memory.toMessages();
|
|
1911
|
+
memoryStep.modelInputMessages = [...messages];
|
|
1912
|
+
const actionSteps = this.memory.getActionSteps();
|
|
1913
|
+
const prevStep = actionSteps.length >= 2 ? actionSteps[actionSteps.length - 2] : void 0;
|
|
1914
|
+
if (prevStep?.error) {
|
|
1915
|
+
messages.push({
|
|
1916
|
+
role: "user",
|
|
1917
|
+
content: `Your previous action encountered an error: ${prevStep.error.message}
|
|
1918
|
+
Please try a different approach.`
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
const toolDefinitions = Array.from(this.tools.values()).map((t) => t.toOpenAITool());
|
|
1922
|
+
this.logger.subheader("Agent thinking...");
|
|
1923
|
+
const response = await this.model.generate(messages, {
|
|
1924
|
+
toolDefinitions,
|
|
1925
|
+
maxTokens: this.config.maxTokens,
|
|
1926
|
+
temperature: this.config.temperature
|
|
1927
|
+
});
|
|
1928
|
+
memoryStep.modelOutputMessage = response;
|
|
1929
|
+
memoryStep.tokenUsage = response.tokenUsage;
|
|
1930
|
+
if (response.content && response.content.trim()) {
|
|
1931
|
+
this.logger.reasoning(response.content.trim());
|
|
1932
|
+
}
|
|
1933
|
+
if (!response.toolCalls || response.toolCalls.length === 0) {
|
|
1934
|
+
this.logger.warn("No tool calls in response. Prompting model to use tools.");
|
|
1935
|
+
memoryStep.observation = "You must use tools to complete the task. Please call the appropriate tool(s). When you have the final answer, call the final_answer tool.";
|
|
1936
|
+
return { output: null, isFinalAnswer: false };
|
|
1937
|
+
}
|
|
1938
|
+
memoryStep.toolCalls = response.toolCalls;
|
|
1939
|
+
const toolResults = await this.processToolCalls(response.toolCalls);
|
|
1940
|
+
memoryStep.toolResults = toolResults;
|
|
1941
|
+
for (const result of toolResults) {
|
|
1942
|
+
if (result.toolName === "final_answer") {
|
|
1943
|
+
return { output: result.result, isFinalAnswer: true };
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
for (const result of toolResults) {
|
|
1947
|
+
if (result.error) {
|
|
1948
|
+
this.logger.error(`Tool ${result.toolName} failed: ${result.error}`);
|
|
1949
|
+
this.emitEvent("agent_error", { tool: result.toolName, error: result.error });
|
|
1950
|
+
} else {
|
|
1951
|
+
const resultStr = typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2);
|
|
1952
|
+
this.logger.output(`[${result.toolName}]: ${resultStr.slice(0, 500)}${resultStr.length > 500 ? "..." : ""}`);
|
|
1953
|
+
this.emitEvent("agent_observation", { tool: result.toolName, result: resultStr.slice(0, 500) });
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
return { output: null, isFinalAnswer: false };
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Process tool calls from the model response.
|
|
1960
|
+
*/
|
|
1961
|
+
async processToolCalls(toolCalls) {
|
|
1962
|
+
const results = [];
|
|
1963
|
+
const executeTool = async (tc) => {
|
|
1964
|
+
const toolName = tc.function.name;
|
|
1965
|
+
const tool = this.tools.get(toolName);
|
|
1966
|
+
if (!tool) {
|
|
1967
|
+
return {
|
|
1968
|
+
toolCallId: tc.id,
|
|
1969
|
+
toolName,
|
|
1970
|
+
result: null,
|
|
1971
|
+
error: `Unknown tool: ${toolName}. Available tools: ${Array.from(this.tools.keys()).join(", ")}`
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
let args;
|
|
1975
|
+
try {
|
|
1976
|
+
args = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
|
|
1977
|
+
} catch {
|
|
1978
|
+
return {
|
|
1979
|
+
toolCallId: tc.id,
|
|
1980
|
+
toolName,
|
|
1981
|
+
result: null,
|
|
1982
|
+
error: `Failed to parse tool arguments: ${tc.function.arguments}`
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
this.logger.info(` Calling tool: ${toolName}(${JSON.stringify(args).slice(0, 100)}...)`);
|
|
1986
|
+
this.emitEvent("agent_tool_call", { tool: toolName, args });
|
|
1987
|
+
try {
|
|
1988
|
+
const result = await tool.call(args);
|
|
1989
|
+
return {
|
|
1990
|
+
toolCallId: tc.id,
|
|
1991
|
+
toolName,
|
|
1992
|
+
result
|
|
1993
|
+
};
|
|
1994
|
+
} catch (error) {
|
|
1995
|
+
return {
|
|
1996
|
+
toolCallId: tc.id,
|
|
1997
|
+
toolName,
|
|
1998
|
+
result: null,
|
|
1999
|
+
error: `Tool execution error: ${error.message}`
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
if (this.parallelToolCalls) {
|
|
2004
|
+
const promises = toolCalls.map((tc) => executeTool(tc));
|
|
2005
|
+
const resolvedResults = await Promise.all(promises);
|
|
2006
|
+
results.push(...resolvedResults);
|
|
2007
|
+
} else {
|
|
2008
|
+
for (const tc of toolCalls) {
|
|
2009
|
+
results.push(await executeTool(tc));
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
return results;
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Override provideFinalAnswer to use tool calling format.
|
|
2016
|
+
*/
|
|
2017
|
+
async provideFinalAnswer(task) {
|
|
2018
|
+
this.logger.subheader("Generating final answer from accumulated context");
|
|
2019
|
+
const messages = this.memory.toMessages();
|
|
2020
|
+
messages.push({
|
|
2021
|
+
role: "user",
|
|
2022
|
+
content: `You have reached the maximum number of steps. Based on your work so far, provide the best answer for the task: "${task}". Call the final_answer tool with your response.`
|
|
2023
|
+
});
|
|
2024
|
+
const toolDefinitions = [new FinalAnswerTool().toOpenAITool()];
|
|
2025
|
+
const response = await this.model.generate(messages, {
|
|
2026
|
+
toolDefinitions,
|
|
2027
|
+
maxTokens: this.config.maxTokens,
|
|
2028
|
+
temperature: this.config.temperature
|
|
2029
|
+
});
|
|
2030
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
2031
|
+
const tc = response.toolCalls[0];
|
|
2032
|
+
try {
|
|
2033
|
+
const args = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
|
|
2034
|
+
return args.answer;
|
|
2035
|
+
} catch {
|
|
2036
|
+
return response.content;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return response.content;
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Add a tool, which can also be an Agent instance (auto-wraps with AgentTool).
|
|
2043
|
+
*/
|
|
2044
|
+
addTool(tool) {
|
|
2045
|
+
super.addTool(tool);
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
|
|
1624
2049
|
// src/models/Model.ts
|
|
1625
2050
|
var Model = class {
|
|
1626
2051
|
/**
|
|
@@ -1650,25 +2075,18 @@ var Model = class {
|
|
|
1650
2075
|
|
|
1651
2076
|
// src/models/OpenAIModel.ts
|
|
1652
2077
|
var import_openai = __toESM(require("openai"));
|
|
1653
|
-
var
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
maxTokens: 65e3,
|
|
1657
|
-
temperature: 1,
|
|
1658
|
-
timeout: 12e4
|
|
1659
|
-
};
|
|
2078
|
+
var DEFAULT_MODEL_ID = "anthropic/claude-sonnet-4.5";
|
|
2079
|
+
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
2080
|
+
var DEFAULT_TIMEOUT = 12e4;
|
|
1660
2081
|
var OpenAIModel = class extends Model {
|
|
1661
2082
|
modelId;
|
|
1662
2083
|
client;
|
|
1663
2084
|
config;
|
|
1664
2085
|
constructor(config = {}) {
|
|
1665
2086
|
super();
|
|
1666
|
-
this.config =
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
};
|
|
1670
|
-
this.modelId = this.config.modelId ?? DEFAULT_CONFIG.modelId;
|
|
1671
|
-
const apiKey = this.config.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
|
|
2087
|
+
this.config = config;
|
|
2088
|
+
this.modelId = config.modelId ?? DEFAULT_MODEL_ID;
|
|
2089
|
+
const apiKey = config.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
|
|
1672
2090
|
if (!apiKey) {
|
|
1673
2091
|
throw new Error(
|
|
1674
2092
|
"API key is required. Set OPENAI_API_KEY or OPENROUTER_API_KEY environment variable, or pass apiKey in config."
|
|
@@ -1676,22 +2094,40 @@ var OpenAIModel = class extends Model {
|
|
|
1676
2094
|
}
|
|
1677
2095
|
this.client = new import_openai.default({
|
|
1678
2096
|
apiKey,
|
|
1679
|
-
baseURL:
|
|
1680
|
-
timeout:
|
|
1681
|
-
defaultHeaders:
|
|
2097
|
+
baseURL: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
2098
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT,
|
|
2099
|
+
defaultHeaders: config.defaultHeaders
|
|
1682
2100
|
});
|
|
1683
2101
|
}
|
|
1684
2102
|
/**
|
|
1685
|
-
* Generate a response from the model.
|
|
2103
|
+
* Generate a response from the model (supports tool calling).
|
|
1686
2104
|
*/
|
|
1687
2105
|
async generate(messages, options = {}) {
|
|
1688
2106
|
const formattedMessages = this.formatMessages(messages);
|
|
2107
|
+
const requestParams = {
|
|
2108
|
+
model: this.modelId,
|
|
2109
|
+
messages: formattedMessages
|
|
2110
|
+
};
|
|
2111
|
+
const maxTokens = options.maxTokens ?? this.config.maxTokens;
|
|
2112
|
+
if (maxTokens !== void 0) {
|
|
2113
|
+
requestParams.max_tokens = maxTokens;
|
|
2114
|
+
}
|
|
2115
|
+
const temperature = options.temperature ?? this.config.temperature;
|
|
2116
|
+
if (temperature !== void 0) {
|
|
2117
|
+
requestParams.temperature = temperature;
|
|
2118
|
+
}
|
|
2119
|
+
if (options.stopSequences) {
|
|
2120
|
+
requestParams.stop = options.stopSequences;
|
|
2121
|
+
}
|
|
2122
|
+
if (options.toolDefinitions && options.toolDefinitions.length > 0) {
|
|
2123
|
+
requestParams.tools = options.toolDefinitions;
|
|
2124
|
+
} else if (options.tools && options.tools.length > 0) {
|
|
2125
|
+
requestParams.tools = options.tools.map((t) => t.toOpenAITool());
|
|
2126
|
+
}
|
|
1689
2127
|
const response = await this.client.chat.completions.create({
|
|
2128
|
+
...requestParams,
|
|
1690
2129
|
model: this.modelId,
|
|
1691
|
-
messages: formattedMessages
|
|
1692
|
-
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
1693
|
-
temperature: options.temperature ?? this.config.temperature,
|
|
1694
|
-
...options.stopSequences && { stop: options.stopSequences }
|
|
2130
|
+
messages: formattedMessages
|
|
1695
2131
|
});
|
|
1696
2132
|
const choice = response.choices[0];
|
|
1697
2133
|
const message = choice?.message;
|
|
@@ -1700,12 +2136,21 @@ var OpenAIModel = class extends Model {
|
|
|
1700
2136
|
}
|
|
1701
2137
|
const tokenUsage = response.usage ? {
|
|
1702
2138
|
inputTokens: response.usage.prompt_tokens,
|
|
1703
|
-
outputTokens: response.usage.completion_tokens,
|
|
2139
|
+
outputTokens: response.usage.completion_tokens ?? 0,
|
|
1704
2140
|
totalTokens: response.usage.total_tokens
|
|
1705
2141
|
} : void 0;
|
|
2142
|
+
const toolCalls = message.tool_calls?.map((tc) => ({
|
|
2143
|
+
id: tc.id,
|
|
2144
|
+
type: "function",
|
|
2145
|
+
function: {
|
|
2146
|
+
name: tc.function.name,
|
|
2147
|
+
arguments: tc.function.arguments
|
|
2148
|
+
}
|
|
2149
|
+
}));
|
|
1706
2150
|
return {
|
|
1707
2151
|
role: "assistant",
|
|
1708
2152
|
content: message.content ?? "",
|
|
2153
|
+
toolCalls,
|
|
1709
2154
|
tokenUsage
|
|
1710
2155
|
};
|
|
1711
2156
|
}
|
|
@@ -1714,12 +2159,26 @@ var OpenAIModel = class extends Model {
|
|
|
1714
2159
|
*/
|
|
1715
2160
|
async *generateStream(messages, options = {}) {
|
|
1716
2161
|
const formattedMessages = this.formatMessages(messages);
|
|
2162
|
+
const requestParams = {
|
|
2163
|
+
model: this.modelId,
|
|
2164
|
+
messages: formattedMessages,
|
|
2165
|
+
stream: true
|
|
2166
|
+
};
|
|
2167
|
+
const maxTokens = options.maxTokens ?? this.config.maxTokens;
|
|
2168
|
+
if (maxTokens !== void 0) {
|
|
2169
|
+
requestParams.max_tokens = maxTokens;
|
|
2170
|
+
}
|
|
2171
|
+
const temperature = options.temperature ?? this.config.temperature;
|
|
2172
|
+
if (temperature !== void 0) {
|
|
2173
|
+
requestParams.temperature = temperature;
|
|
2174
|
+
}
|
|
2175
|
+
if (options.stopSequences) {
|
|
2176
|
+
requestParams.stop = options.stopSequences;
|
|
2177
|
+
}
|
|
1717
2178
|
const stream = await this.client.chat.completions.create({
|
|
2179
|
+
...requestParams,
|
|
1718
2180
|
model: this.modelId,
|
|
1719
2181
|
messages: formattedMessages,
|
|
1720
|
-
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
1721
|
-
temperature: options.temperature ?? this.config.temperature,
|
|
1722
|
-
...options.stopSequences && { stop: options.stopSequences },
|
|
1723
2182
|
stream: true
|
|
1724
2183
|
});
|
|
1725
2184
|
let fullContent = "";
|
|
@@ -1736,16 +2195,37 @@ var OpenAIModel = class extends Model {
|
|
|
1736
2195
|
};
|
|
1737
2196
|
}
|
|
1738
2197
|
/**
|
|
1739
|
-
* Format messages for the OpenAI API.
|
|
2198
|
+
* Format messages for the OpenAI API, including tool call/response messages.
|
|
1740
2199
|
*/
|
|
1741
2200
|
formatMessages(messages) {
|
|
1742
2201
|
return messages.map((msg) => {
|
|
2202
|
+
if (msg.role === "tool" && msg.toolCallId) {
|
|
2203
|
+
return {
|
|
2204
|
+
role: "tool",
|
|
2205
|
+
content: msg.content ?? "",
|
|
2206
|
+
tool_call_id: msg.toolCallId
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
1743
2209
|
if (msg.role === "tool") {
|
|
1744
2210
|
return {
|
|
1745
2211
|
role: "user",
|
|
1746
2212
|
content: msg.content ?? ""
|
|
1747
2213
|
};
|
|
1748
2214
|
}
|
|
2215
|
+
if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
|
|
2216
|
+
return {
|
|
2217
|
+
role: "assistant",
|
|
2218
|
+
content: msg.content || null,
|
|
2219
|
+
tool_calls: msg.toolCalls.map((tc) => ({
|
|
2220
|
+
id: tc.id,
|
|
2221
|
+
type: "function",
|
|
2222
|
+
function: {
|
|
2223
|
+
name: tc.function.name,
|
|
2224
|
+
arguments: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
2225
|
+
}
|
|
2226
|
+
}))
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
1749
2229
|
return {
|
|
1750
2230
|
role: msg.role,
|
|
1751
2231
|
content: msg.content ?? ""
|
|
@@ -1828,6 +2308,819 @@ async function ${this.name}(task: string): Promise<string> { ... }
|
|
|
1828
2308
|
function agentAsTool(agent, options) {
|
|
1829
2309
|
return new AgentTool({ agent, ...options });
|
|
1830
2310
|
}
|
|
2311
|
+
|
|
2312
|
+
// src/tools/ReadFileTool.ts
|
|
2313
|
+
var fs3 = __toESM(require("fs"));
|
|
2314
|
+
var path3 = __toESM(require("path"));
|
|
2315
|
+
var ReadFileTool = class extends Tool {
|
|
2316
|
+
name = "read_file";
|
|
2317
|
+
description = "Read the contents of a file at the specified path. Returns the file content as a string.";
|
|
2318
|
+
inputs = {
|
|
2319
|
+
path: {
|
|
2320
|
+
type: "string",
|
|
2321
|
+
description: "The file path to read (absolute or relative to working directory)",
|
|
2322
|
+
required: true
|
|
2323
|
+
},
|
|
2324
|
+
encoding: {
|
|
2325
|
+
type: "string",
|
|
2326
|
+
description: "File encoding (default: utf-8)",
|
|
2327
|
+
required: false,
|
|
2328
|
+
default: "utf-8"
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2331
|
+
outputType = "string";
|
|
2332
|
+
workingDirectory;
|
|
2333
|
+
constructor(config) {
|
|
2334
|
+
super();
|
|
2335
|
+
this.workingDirectory = config?.workingDirectory ?? process.cwd();
|
|
2336
|
+
}
|
|
2337
|
+
async execute(args) {
|
|
2338
|
+
const filePath = args.path;
|
|
2339
|
+
const encoding = args.encoding ?? "utf-8";
|
|
2340
|
+
const resolvedPath = path3.isAbsolute(filePath) ? filePath : path3.resolve(this.workingDirectory, filePath);
|
|
2341
|
+
if (!fs3.existsSync(resolvedPath)) {
|
|
2342
|
+
throw new Error(`File not found: ${resolvedPath}`);
|
|
2343
|
+
}
|
|
2344
|
+
const stat = fs3.statSync(resolvedPath);
|
|
2345
|
+
if (stat.isDirectory()) {
|
|
2346
|
+
throw new Error(`Path is a directory, not a file: ${resolvedPath}`);
|
|
2347
|
+
}
|
|
2348
|
+
const content = fs3.readFileSync(resolvedPath, encoding);
|
|
2349
|
+
const maxLength = 1e5;
|
|
2350
|
+
if (content.length > maxLength) {
|
|
2351
|
+
return content.slice(0, maxLength) + `
|
|
2352
|
+
|
|
2353
|
+
[... truncated, file is ${content.length} characters total]`;
|
|
2354
|
+
}
|
|
2355
|
+
return content;
|
|
2356
|
+
}
|
|
2357
|
+
};
|
|
2358
|
+
|
|
2359
|
+
// src/tools/WriteFileTool.ts
|
|
2360
|
+
var fs4 = __toESM(require("fs"));
|
|
2361
|
+
var path4 = __toESM(require("path"));
|
|
2362
|
+
var WriteFileTool = class extends Tool {
|
|
2363
|
+
name = "write_file";
|
|
2364
|
+
description = "Write content to a file at the specified path. Creates the file if it does not exist, and creates parent directories as needed. Overwrites existing content by default.";
|
|
2365
|
+
inputs = {
|
|
2366
|
+
path: {
|
|
2367
|
+
type: "string",
|
|
2368
|
+
description: "The file path to write to (absolute or relative to working directory)",
|
|
2369
|
+
required: true
|
|
2370
|
+
},
|
|
2371
|
+
content: {
|
|
2372
|
+
type: "string",
|
|
2373
|
+
description: "The content to write to the file",
|
|
2374
|
+
required: true
|
|
2375
|
+
},
|
|
2376
|
+
append: {
|
|
2377
|
+
type: "boolean",
|
|
2378
|
+
description: "If true, append to the file instead of overwriting (default: false)",
|
|
2379
|
+
required: false,
|
|
2380
|
+
default: false
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
outputType = "string";
|
|
2384
|
+
workingDirectory;
|
|
2385
|
+
constructor(config) {
|
|
2386
|
+
super();
|
|
2387
|
+
this.workingDirectory = config?.workingDirectory ?? process.cwd();
|
|
2388
|
+
}
|
|
2389
|
+
async execute(args) {
|
|
2390
|
+
const filePath = args.path;
|
|
2391
|
+
const content = args.content;
|
|
2392
|
+
const append = args.append ?? false;
|
|
2393
|
+
const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(this.workingDirectory, filePath);
|
|
2394
|
+
const dir = path4.dirname(resolvedPath);
|
|
2395
|
+
if (!fs4.existsSync(dir)) {
|
|
2396
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
2397
|
+
}
|
|
2398
|
+
if (append) {
|
|
2399
|
+
fs4.appendFileSync(resolvedPath, content, "utf-8");
|
|
2400
|
+
return `Successfully appended ${content.length} characters to ${resolvedPath}`;
|
|
2401
|
+
} else {
|
|
2402
|
+
fs4.writeFileSync(resolvedPath, content, "utf-8");
|
|
2403
|
+
return `Successfully wrote ${content.length} characters to ${resolvedPath}`;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
|
|
2408
|
+
// src/tools/CurlTool.ts
|
|
2409
|
+
var CurlTool = class extends Tool {
|
|
2410
|
+
name = "curl";
|
|
2411
|
+
description = "Make HTTP requests to any URL. Supports GET and POST methods with custom headers and body. Returns the response body as text.";
|
|
2412
|
+
inputs = {
|
|
2413
|
+
url: {
|
|
2414
|
+
type: "string",
|
|
2415
|
+
description: "The URL to request",
|
|
2416
|
+
required: true
|
|
2417
|
+
},
|
|
2418
|
+
method: {
|
|
2419
|
+
type: "string",
|
|
2420
|
+
description: "HTTP method: GET or POST (default: GET)",
|
|
2421
|
+
required: false,
|
|
2422
|
+
default: "GET",
|
|
2423
|
+
enum: ["GET", "POST"]
|
|
2424
|
+
},
|
|
2425
|
+
headers: {
|
|
2426
|
+
type: "object",
|
|
2427
|
+
description: 'Optional HTTP headers as key-value pairs (e.g., {"Content-Type": "application/json"})',
|
|
2428
|
+
required: false
|
|
2429
|
+
},
|
|
2430
|
+
body: {
|
|
2431
|
+
type: "string",
|
|
2432
|
+
description: "Request body for POST requests (typically JSON string)",
|
|
2433
|
+
required: false
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
outputType = "string";
|
|
2437
|
+
timeout;
|
|
2438
|
+
constructor(config) {
|
|
2439
|
+
super();
|
|
2440
|
+
this.timeout = config?.timeout ?? 3e4;
|
|
2441
|
+
}
|
|
2442
|
+
async execute(args) {
|
|
2443
|
+
const url = args.url;
|
|
2444
|
+
const method = (args.method ?? "GET").toUpperCase();
|
|
2445
|
+
const headers = args.headers ?? {};
|
|
2446
|
+
const body = args.body;
|
|
2447
|
+
const controller = new AbortController();
|
|
2448
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2449
|
+
try {
|
|
2450
|
+
const fetchOptions = {
|
|
2451
|
+
method,
|
|
2452
|
+
headers,
|
|
2453
|
+
signal: controller.signal
|
|
2454
|
+
};
|
|
2455
|
+
if (method === "POST" && body) {
|
|
2456
|
+
fetchOptions.body = body;
|
|
2457
|
+
if (!headers["Content-Type"] && !headers["content-type"]) {
|
|
2458
|
+
fetchOptions.headers["Content-Type"] = "application/json";
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
const response = await fetch(url, fetchOptions);
|
|
2462
|
+
const responseText = await response.text();
|
|
2463
|
+
const statusLine = `HTTP ${response.status} ${response.statusText}`;
|
|
2464
|
+
const maxLength = 5e4;
|
|
2465
|
+
const truncatedBody = responseText.length > maxLength ? responseText.slice(0, maxLength) + `
|
|
2466
|
+
|
|
2467
|
+
[... truncated, response is ${responseText.length} characters total]` : responseText;
|
|
2468
|
+
return `${statusLine}
|
|
2469
|
+
|
|
2470
|
+
${truncatedBody}`;
|
|
2471
|
+
} catch (error) {
|
|
2472
|
+
if (error.name === "AbortError") {
|
|
2473
|
+
throw new Error(`Request timed out after ${this.timeout}ms: ${url}`);
|
|
2474
|
+
}
|
|
2475
|
+
throw new Error(`HTTP request failed: ${error.message}`);
|
|
2476
|
+
} finally {
|
|
2477
|
+
clearTimeout(timeoutId);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
|
|
2482
|
+
// src/tools/ExaSearchTool.ts
|
|
2483
|
+
var ExaSearchTool = class extends Tool {
|
|
2484
|
+
name = "exa_search";
|
|
2485
|
+
description = "Search the web using Exa.ai semantic search. Returns relevant web pages with titles, URLs, and optionally content snippets. Use this for finding information, research, and fact-checking.";
|
|
2486
|
+
inputs = {
|
|
2487
|
+
query: {
|
|
2488
|
+
type: "string",
|
|
2489
|
+
description: "The search query. Be specific and descriptive for best results.",
|
|
2490
|
+
required: true
|
|
2491
|
+
},
|
|
2492
|
+
numResults: {
|
|
2493
|
+
type: "number",
|
|
2494
|
+
description: "Number of results to return (default: 10, max: 30)",
|
|
2495
|
+
required: false,
|
|
2496
|
+
default: 10
|
|
2497
|
+
},
|
|
2498
|
+
type: {
|
|
2499
|
+
type: "string",
|
|
2500
|
+
description: 'Search type: "auto" (default), "neural" (embeddings-based), or "keyword"',
|
|
2501
|
+
required: false,
|
|
2502
|
+
default: "auto",
|
|
2503
|
+
enum: ["auto", "neural", "keyword"]
|
|
2504
|
+
},
|
|
2505
|
+
category: {
|
|
2506
|
+
type: "string",
|
|
2507
|
+
description: 'Optional category filter: "research paper", "news", "pdf", "github", "tweet", "company", "blog"',
|
|
2508
|
+
required: false
|
|
2509
|
+
},
|
|
2510
|
+
includeDomains: {
|
|
2511
|
+
type: "array",
|
|
2512
|
+
description: 'Only include results from these domains (e.g., ["arxiv.org", "github.com"])',
|
|
2513
|
+
required: false
|
|
2514
|
+
},
|
|
2515
|
+
excludeDomains: {
|
|
2516
|
+
type: "array",
|
|
2517
|
+
description: "Exclude results from these domains",
|
|
2518
|
+
required: false
|
|
2519
|
+
},
|
|
2520
|
+
startPublishedDate: {
|
|
2521
|
+
type: "string",
|
|
2522
|
+
description: 'Filter results published after this ISO 8601 date (e.g., "2024-01-01")',
|
|
2523
|
+
required: false
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
outputType = "string";
|
|
2527
|
+
apiKey;
|
|
2528
|
+
constructor(config) {
|
|
2529
|
+
super();
|
|
2530
|
+
this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
|
|
2531
|
+
}
|
|
2532
|
+
async setup() {
|
|
2533
|
+
if (!this.apiKey) {
|
|
2534
|
+
throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
|
|
2535
|
+
}
|
|
2536
|
+
this.isSetup = true;
|
|
2537
|
+
}
|
|
2538
|
+
async execute(args) {
|
|
2539
|
+
const query = args.query;
|
|
2540
|
+
const numResults = Math.min(args.numResults ?? 10, 30);
|
|
2541
|
+
const type = args.type ?? "auto";
|
|
2542
|
+
const requestBody = {
|
|
2543
|
+
query,
|
|
2544
|
+
numResults,
|
|
2545
|
+
type,
|
|
2546
|
+
text: { maxCharacters: 1e3 }
|
|
2547
|
+
};
|
|
2548
|
+
if (args.category) requestBody.category = args.category;
|
|
2549
|
+
if (args.includeDomains) requestBody.includeDomains = args.includeDomains;
|
|
2550
|
+
if (args.excludeDomains) requestBody.excludeDomains = args.excludeDomains;
|
|
2551
|
+
if (args.startPublishedDate) requestBody.startPublishedDate = args.startPublishedDate;
|
|
2552
|
+
const response = await fetch("https://api.exa.ai/search", {
|
|
2553
|
+
method: "POST",
|
|
2554
|
+
headers: {
|
|
2555
|
+
"x-api-key": this.apiKey,
|
|
2556
|
+
"Content-Type": "application/json"
|
|
2557
|
+
},
|
|
2558
|
+
body: JSON.stringify(requestBody)
|
|
2559
|
+
});
|
|
2560
|
+
if (!response.ok) {
|
|
2561
|
+
const errorText = await response.text();
|
|
2562
|
+
throw new Error(`Exa search failed (${response.status}): ${errorText}`);
|
|
2563
|
+
}
|
|
2564
|
+
const data = await response.json();
|
|
2565
|
+
if (!data.results || data.results.length === 0) {
|
|
2566
|
+
return "No results found for the query.";
|
|
2567
|
+
}
|
|
2568
|
+
const formattedResults = data.results.map((result, i) => {
|
|
2569
|
+
const parts = [`[${i + 1}] ${result.title ?? "Untitled"}`];
|
|
2570
|
+
parts.push(` URL: ${result.url}`);
|
|
2571
|
+
if (result.publishedDate) parts.push(` Date: ${result.publishedDate}`);
|
|
2572
|
+
if (result.author) parts.push(` Author: ${result.author}`);
|
|
2573
|
+
if (result.text) {
|
|
2574
|
+
const snippet = result.text.slice(0, 300).trim();
|
|
2575
|
+
parts.push(` Snippet: ${snippet}${result.text.length > 300 ? "..." : ""}`);
|
|
2576
|
+
}
|
|
2577
|
+
return parts.join("\n");
|
|
2578
|
+
}).join("\n\n");
|
|
2579
|
+
return `Search results for "${query}" (${data.results.length} results):
|
|
2580
|
+
|
|
2581
|
+
${formattedResults}`;
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
|
|
2585
|
+
// src/tools/ExaGetContentsTool.ts
|
|
2586
|
+
var ExaGetContentsTool = class extends Tool {
|
|
2587
|
+
name = "exa_get_contents";
|
|
2588
|
+
description = "Get the full text content of one or more web pages using Exa.ai. Returns cleaned, readable text extracted from the HTML. Use this to read articles, documentation, or any web page content.";
|
|
2589
|
+
inputs = {
|
|
2590
|
+
urls: {
|
|
2591
|
+
type: "array",
|
|
2592
|
+
description: "Array of URLs to fetch content from (max 10)",
|
|
2593
|
+
required: true
|
|
2594
|
+
},
|
|
2595
|
+
maxCharacters: {
|
|
2596
|
+
type: "number",
|
|
2597
|
+
description: "Maximum characters of content to return per page (default: 10000)",
|
|
2598
|
+
required: false,
|
|
2599
|
+
default: 1e4
|
|
2600
|
+
},
|
|
2601
|
+
livecrawl: {
|
|
2602
|
+
type: "string",
|
|
2603
|
+
description: 'Crawl strategy: "fallback" (use cache, fetch live if unavailable), "always" (always fetch live), "never" (cache only). Default: "fallback"',
|
|
2604
|
+
required: false,
|
|
2605
|
+
default: "fallback",
|
|
2606
|
+
enum: ["fallback", "always", "never"]
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
outputType = "string";
|
|
2610
|
+
apiKey;
|
|
2611
|
+
constructor(config) {
|
|
2612
|
+
super();
|
|
2613
|
+
this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
|
|
2614
|
+
}
|
|
2615
|
+
async setup() {
|
|
2616
|
+
if (!this.apiKey) {
|
|
2617
|
+
throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
|
|
2618
|
+
}
|
|
2619
|
+
this.isSetup = true;
|
|
2620
|
+
}
|
|
2621
|
+
async execute(args) {
|
|
2622
|
+
const urls = args.urls.slice(0, 10);
|
|
2623
|
+
const maxCharacters = args.maxCharacters ?? 1e4;
|
|
2624
|
+
const livecrawl = args.livecrawl ?? "fallback";
|
|
2625
|
+
const requestBody = {
|
|
2626
|
+
urls,
|
|
2627
|
+
text: { maxCharacters },
|
|
2628
|
+
livecrawl
|
|
2629
|
+
};
|
|
2630
|
+
const response = await fetch("https://api.exa.ai/contents", {
|
|
2631
|
+
method: "POST",
|
|
2632
|
+
headers: {
|
|
2633
|
+
"x-api-key": this.apiKey,
|
|
2634
|
+
"Content-Type": "application/json"
|
|
2635
|
+
},
|
|
2636
|
+
body: JSON.stringify(requestBody)
|
|
2637
|
+
});
|
|
2638
|
+
if (!response.ok) {
|
|
2639
|
+
const errorText = await response.text();
|
|
2640
|
+
throw new Error(`Exa get contents failed (${response.status}): ${errorText}`);
|
|
2641
|
+
}
|
|
2642
|
+
const data = await response.json();
|
|
2643
|
+
if (!data.results || data.results.length === 0) {
|
|
2644
|
+
return "No content could be retrieved from the provided URLs.";
|
|
2645
|
+
}
|
|
2646
|
+
const formattedResults = data.results.map((result) => {
|
|
2647
|
+
const parts = [`## ${result.title ?? result.url}`];
|
|
2648
|
+
parts.push(`URL: ${result.url}`);
|
|
2649
|
+
if (result.author) parts.push(`Author: ${result.author}`);
|
|
2650
|
+
if (result.publishedDate) parts.push(`Date: ${result.publishedDate}`);
|
|
2651
|
+
parts.push("");
|
|
2652
|
+
if (result.text) {
|
|
2653
|
+
parts.push(result.text);
|
|
2654
|
+
} else {
|
|
2655
|
+
parts.push("[No text content available]");
|
|
2656
|
+
}
|
|
2657
|
+
return parts.join("\n");
|
|
2658
|
+
}).join("\n\n---\n\n");
|
|
2659
|
+
return formattedResults;
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
|
|
2663
|
+
// src/tools/ExaResearchTool.ts
|
|
2664
|
+
var ExaResearchTool = class extends Tool {
|
|
2665
|
+
name = "exa_research";
|
|
2666
|
+
description = "Perform deep research on a single topic using Exa.ai. Searches for relevant sources, retrieves their content, and finds similar pages for comprehensive coverage. Returns a structured research summary with sources. Use this for thorough research on any topic.";
|
|
2667
|
+
inputs = {
|
|
2668
|
+
topic: {
|
|
2669
|
+
type: "string",
|
|
2670
|
+
description: "The research topic or question to investigate",
|
|
2671
|
+
required: true
|
|
2672
|
+
},
|
|
2673
|
+
numSources: {
|
|
2674
|
+
type: "number",
|
|
2675
|
+
description: "Number of primary sources to retrieve (default: 5, max: 10)",
|
|
2676
|
+
required: false,
|
|
2677
|
+
default: 5
|
|
2678
|
+
},
|
|
2679
|
+
category: {
|
|
2680
|
+
type: "string",
|
|
2681
|
+
description: 'Optional category: "research paper", "news", "blog", "company"',
|
|
2682
|
+
required: false
|
|
2683
|
+
},
|
|
2684
|
+
includeDomains: {
|
|
2685
|
+
type: "array",
|
|
2686
|
+
description: "Only include results from these domains",
|
|
2687
|
+
required: false
|
|
2688
|
+
},
|
|
2689
|
+
startPublishedDate: {
|
|
2690
|
+
type: "string",
|
|
2691
|
+
description: "Only include results published after this date (ISO 8601)",
|
|
2692
|
+
required: false
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
outputType = "string";
|
|
2696
|
+
apiKey;
|
|
2697
|
+
constructor(config) {
|
|
2698
|
+
super();
|
|
2699
|
+
this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
|
|
2700
|
+
}
|
|
2701
|
+
async setup() {
|
|
2702
|
+
if (!this.apiKey) {
|
|
2703
|
+
throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
|
|
2704
|
+
}
|
|
2705
|
+
this.isSetup = true;
|
|
2706
|
+
}
|
|
2707
|
+
async execute(args) {
|
|
2708
|
+
const topic = args.topic;
|
|
2709
|
+
const numSources = Math.min(args.numSources ?? 5, 10);
|
|
2710
|
+
const searchBody = {
|
|
2711
|
+
query: topic,
|
|
2712
|
+
numResults: numSources,
|
|
2713
|
+
type: "auto",
|
|
2714
|
+
text: { maxCharacters: 3e3 }
|
|
2715
|
+
};
|
|
2716
|
+
if (args.category) searchBody.category = args.category;
|
|
2717
|
+
if (args.includeDomains) searchBody.includeDomains = args.includeDomains;
|
|
2718
|
+
if (args.startPublishedDate) searchBody.startPublishedDate = args.startPublishedDate;
|
|
2719
|
+
const searchResponse = await fetch("https://api.exa.ai/search", {
|
|
2720
|
+
method: "POST",
|
|
2721
|
+
headers: {
|
|
2722
|
+
"x-api-key": this.apiKey,
|
|
2723
|
+
"Content-Type": "application/json"
|
|
2724
|
+
},
|
|
2725
|
+
body: JSON.stringify(searchBody)
|
|
2726
|
+
});
|
|
2727
|
+
if (!searchResponse.ok) {
|
|
2728
|
+
const errorText = await searchResponse.text();
|
|
2729
|
+
throw new Error(`Exa research search failed (${searchResponse.status}): ${errorText}`);
|
|
2730
|
+
}
|
|
2731
|
+
const searchData = await searchResponse.json();
|
|
2732
|
+
if (!searchData.results || searchData.results.length === 0) {
|
|
2733
|
+
return `No research sources found for topic: "${topic}"`;
|
|
2734
|
+
}
|
|
2735
|
+
let similarResults = [];
|
|
2736
|
+
if (searchData.results.length > 0) {
|
|
2737
|
+
try {
|
|
2738
|
+
const similarBody = {
|
|
2739
|
+
url: searchData.results[0].url,
|
|
2740
|
+
numResults: 3,
|
|
2741
|
+
text: { maxCharacters: 2e3 }
|
|
2742
|
+
};
|
|
2743
|
+
const similarResponse = await fetch("https://api.exa.ai/findSimilar", {
|
|
2744
|
+
method: "POST",
|
|
2745
|
+
headers: {
|
|
2746
|
+
"x-api-key": this.apiKey,
|
|
2747
|
+
"Content-Type": "application/json"
|
|
2748
|
+
},
|
|
2749
|
+
body: JSON.stringify(similarBody)
|
|
2750
|
+
});
|
|
2751
|
+
if (similarResponse.ok) {
|
|
2752
|
+
const similarData = await similarResponse.json();
|
|
2753
|
+
similarResults = similarData.results ?? [];
|
|
2754
|
+
}
|
|
2755
|
+
} catch {
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
const allSources = [...searchData.results, ...similarResults];
|
|
2759
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
2760
|
+
const uniqueSources = allSources.filter((s) => {
|
|
2761
|
+
if (seenUrls.has(s.url)) return false;
|
|
2762
|
+
seenUrls.add(s.url);
|
|
2763
|
+
return true;
|
|
2764
|
+
});
|
|
2765
|
+
const sections = [];
|
|
2766
|
+
sections.push(`# Research: ${topic}
|
|
2767
|
+
`);
|
|
2768
|
+
sections.push(`Found ${uniqueSources.length} sources.
|
|
2769
|
+
`);
|
|
2770
|
+
sections.push("## Key Sources\n");
|
|
2771
|
+
for (let i = 0; i < uniqueSources.length; i++) {
|
|
2772
|
+
const source = uniqueSources[i];
|
|
2773
|
+
sections.push(`### ${i + 1}. ${source.title ?? "Untitled"}`);
|
|
2774
|
+
sections.push(`URL: ${source.url}`);
|
|
2775
|
+
if ("publishedDate" in source && source.publishedDate) {
|
|
2776
|
+
sections.push(`Date: ${source.publishedDate}`);
|
|
2777
|
+
}
|
|
2778
|
+
if ("author" in source && source.author) {
|
|
2779
|
+
sections.push(`Author: ${source.author}`);
|
|
2780
|
+
}
|
|
2781
|
+
if (source.text) {
|
|
2782
|
+
sections.push(`
|
|
2783
|
+
Content:
|
|
2784
|
+
${source.text.slice(0, 2e3)}`);
|
|
2785
|
+
}
|
|
2786
|
+
sections.push("");
|
|
2787
|
+
}
|
|
2788
|
+
sections.push("## Source URLs\n");
|
|
2789
|
+
uniqueSources.forEach((s, i) => {
|
|
2790
|
+
sections.push(`${i + 1}. ${s.url}`);
|
|
2791
|
+
});
|
|
2792
|
+
return sections.join("\n");
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
|
|
2796
|
+
// src/orchestrator/YAMLLoader.ts
|
|
2797
|
+
var fs5 = __toESM(require("fs"));
|
|
2798
|
+
var path5 = __toESM(require("path"));
|
|
2799
|
+
var import_yaml = __toESM(require("yaml"));
|
|
2800
|
+
var TOOL_REGISTRY = {
|
|
2801
|
+
read_file: ReadFileTool,
|
|
2802
|
+
write_file: WriteFileTool,
|
|
2803
|
+
curl: CurlTool,
|
|
2804
|
+
exa_search: ExaSearchTool,
|
|
2805
|
+
exa_get_contents: ExaGetContentsTool,
|
|
2806
|
+
exa_research: ExaResearchTool,
|
|
2807
|
+
final_answer: FinalAnswerTool
|
|
2808
|
+
};
|
|
2809
|
+
var YAMLLoader = class {
|
|
2810
|
+
customTools = /* @__PURE__ */ new Map();
|
|
2811
|
+
/**
|
|
2812
|
+
* Register a custom tool type for use in YAML definitions.
|
|
2813
|
+
*/
|
|
2814
|
+
registerToolType(typeName, toolClass) {
|
|
2815
|
+
this.customTools.set(typeName, toolClass);
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Load a workflow from a YAML file path.
|
|
2819
|
+
*/
|
|
2820
|
+
loadFromFile(filePath) {
|
|
2821
|
+
const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
|
|
2822
|
+
if (!fs5.existsSync(absolutePath)) {
|
|
2823
|
+
throw new Error(`Workflow file not found: ${absolutePath}`);
|
|
2824
|
+
}
|
|
2825
|
+
const content = fs5.readFileSync(absolutePath, "utf-8");
|
|
2826
|
+
return this.loadFromString(content);
|
|
2827
|
+
}
|
|
2828
|
+
/**
|
|
2829
|
+
* Load a workflow from a YAML string.
|
|
2830
|
+
*/
|
|
2831
|
+
loadFromString(yamlContent) {
|
|
2832
|
+
const definition = import_yaml.default.parse(yamlContent);
|
|
2833
|
+
return this.buildWorkflow(definition);
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* Build a runnable workflow from a parsed definition.
|
|
2837
|
+
*/
|
|
2838
|
+
buildWorkflow(definition) {
|
|
2839
|
+
if (!definition.name) {
|
|
2840
|
+
throw new Error("Workflow must have a name");
|
|
2841
|
+
}
|
|
2842
|
+
if (!definition.entrypoint) {
|
|
2843
|
+
throw new Error("Workflow must have an entrypoint agent");
|
|
2844
|
+
}
|
|
2845
|
+
if (!definition.agents) {
|
|
2846
|
+
throw new Error("Workflow must define at least one agent");
|
|
2847
|
+
}
|
|
2848
|
+
const tools = /* @__PURE__ */ new Map();
|
|
2849
|
+
if (definition.tools) {
|
|
2850
|
+
for (const [name, toolDef] of Object.entries(definition.tools)) {
|
|
2851
|
+
const tool = this.buildTool(name, toolDef.type, toolDef.config);
|
|
2852
|
+
tools.set(name, tool);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
const agents = /* @__PURE__ */ new Map();
|
|
2856
|
+
const agentDefs = definition.agents;
|
|
2857
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
2858
|
+
const maxIterations = Object.keys(agentDefs).length * 2;
|
|
2859
|
+
let iterations = 0;
|
|
2860
|
+
while (resolved.size < Object.keys(agentDefs).length && iterations < maxIterations) {
|
|
2861
|
+
iterations++;
|
|
2862
|
+
for (const [agentName, agentDef] of Object.entries(agentDefs)) {
|
|
2863
|
+
if (resolved.has(agentName)) continue;
|
|
2864
|
+
const agentDeps = agentDef.agents ?? [];
|
|
2865
|
+
const allDepsResolved = agentDeps.every((dep) => resolved.has(dep));
|
|
2866
|
+
if (allDepsResolved) {
|
|
2867
|
+
const agent = this.buildAgent(
|
|
2868
|
+
agentName,
|
|
2869
|
+
agentDef,
|
|
2870
|
+
definition.model,
|
|
2871
|
+
tools,
|
|
2872
|
+
agents,
|
|
2873
|
+
definition.globalMaxContextLength
|
|
2874
|
+
);
|
|
2875
|
+
agents.set(agentName, agent);
|
|
2876
|
+
resolved.add(agentName);
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
if (resolved.size < Object.keys(agentDefs).length) {
|
|
2881
|
+
const unresolved = Object.keys(agentDefs).filter((n) => !resolved.has(n));
|
|
2882
|
+
throw new Error(`Circular or unresolvable agent dependencies: ${unresolved.join(", ")}`);
|
|
2883
|
+
}
|
|
2884
|
+
const entrypointAgent = agents.get(definition.entrypoint);
|
|
2885
|
+
if (!entrypointAgent) {
|
|
2886
|
+
throw new Error(`Entrypoint agent "${definition.entrypoint}" not found in agents`);
|
|
2887
|
+
}
|
|
2888
|
+
return {
|
|
2889
|
+
name: definition.name,
|
|
2890
|
+
description: definition.description,
|
|
2891
|
+
entrypointAgent,
|
|
2892
|
+
agents,
|
|
2893
|
+
tools
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Build a tool instance from a type name and config.
|
|
2898
|
+
*/
|
|
2899
|
+
buildTool(name, type, config) {
|
|
2900
|
+
const ToolClass = TOOL_REGISTRY[type] ?? this.customTools.get(type);
|
|
2901
|
+
if (!ToolClass) {
|
|
2902
|
+
throw new Error(`Unknown tool type: ${type}. Available types: ${[...Object.keys(TOOL_REGISTRY), ...this.customTools.keys()].join(", ")}`);
|
|
2903
|
+
}
|
|
2904
|
+
const tool = new ToolClass(config);
|
|
2905
|
+
if (name !== type && name !== tool.name) {
|
|
2906
|
+
Object.defineProperty(tool, "name", { value: name, writable: false });
|
|
2907
|
+
}
|
|
2908
|
+
return tool;
|
|
2909
|
+
}
|
|
2910
|
+
/**
|
|
2911
|
+
* Build an agent instance from a YAML definition.
|
|
2912
|
+
*/
|
|
2913
|
+
buildAgent(name, definition, globalModel, availableTools, resolvedAgents, globalMaxContextLength) {
|
|
2914
|
+
const modelConfig = definition.model ?? globalModel;
|
|
2915
|
+
const model = new OpenAIModel({
|
|
2916
|
+
modelId: modelConfig?.modelId,
|
|
2917
|
+
apiKey: modelConfig?.apiKey,
|
|
2918
|
+
baseUrl: modelConfig?.baseUrl,
|
|
2919
|
+
maxTokens: definition.maxTokens ?? modelConfig?.maxTokens,
|
|
2920
|
+
temperature: definition.temperature ?? modelConfig?.temperature,
|
|
2921
|
+
timeout: modelConfig?.timeout
|
|
2922
|
+
});
|
|
2923
|
+
const agentTools = [];
|
|
2924
|
+
if (definition.tools && availableTools) {
|
|
2925
|
+
for (const toolName of definition.tools) {
|
|
2926
|
+
const tool = availableTools.get(toolName);
|
|
2927
|
+
if (tool) {
|
|
2928
|
+
agentTools.push(tool);
|
|
2929
|
+
} else {
|
|
2930
|
+
const ToolClass = TOOL_REGISTRY[toolName] ?? this.customTools.get(toolName);
|
|
2931
|
+
if (ToolClass) {
|
|
2932
|
+
agentTools.push(new ToolClass());
|
|
2933
|
+
} else {
|
|
2934
|
+
throw new Error(`Tool "${toolName}" not found for agent "${name}"`);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
if (definition.agents && resolvedAgents) {
|
|
2940
|
+
for (const subAgentName of definition.agents) {
|
|
2941
|
+
const subAgent = resolvedAgents.get(subAgentName);
|
|
2942
|
+
if (!subAgent) {
|
|
2943
|
+
throw new Error(`Sub-agent "${subAgentName}" not found for agent "${name}"`);
|
|
2944
|
+
}
|
|
2945
|
+
agentTools.push(new AgentTool({
|
|
2946
|
+
agent: subAgent,
|
|
2947
|
+
name: subAgentName,
|
|
2948
|
+
description: definition.description ? `Sub-agent: ${subAgentName}` : `Delegate tasks to the ${subAgentName} agent`
|
|
2949
|
+
}));
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
const maxContextLength = definition.maxContextLength ?? globalMaxContextLength;
|
|
2953
|
+
if (definition.type === "CodeAgent") {
|
|
2954
|
+
return new CodeAgent({
|
|
2955
|
+
model,
|
|
2956
|
+
tools: agentTools,
|
|
2957
|
+
maxSteps: definition.maxSteps,
|
|
2958
|
+
customInstructions: definition.customInstructions,
|
|
2959
|
+
persistent: definition.persistent,
|
|
2960
|
+
maxContextLength,
|
|
2961
|
+
memoryStrategy: definition.memoryStrategy,
|
|
2962
|
+
maxTokens: definition.maxTokens,
|
|
2963
|
+
temperature: definition.temperature,
|
|
2964
|
+
name
|
|
2965
|
+
});
|
|
2966
|
+
} else {
|
|
2967
|
+
return new ToolUseAgent({
|
|
2968
|
+
model,
|
|
2969
|
+
tools: agentTools,
|
|
2970
|
+
maxSteps: definition.maxSteps,
|
|
2971
|
+
customInstructions: definition.customInstructions,
|
|
2972
|
+
persistent: definition.persistent,
|
|
2973
|
+
maxContextLength,
|
|
2974
|
+
memoryStrategy: definition.memoryStrategy,
|
|
2975
|
+
maxTokens: definition.maxTokens,
|
|
2976
|
+
temperature: definition.temperature,
|
|
2977
|
+
name
|
|
2978
|
+
});
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
};
|
|
2982
|
+
|
|
2983
|
+
// src/orchestrator/Orchestrator.ts
|
|
2984
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
2985
|
+
var Orchestrator = class {
|
|
2986
|
+
loader;
|
|
2987
|
+
config;
|
|
2988
|
+
activeAgents = /* @__PURE__ */ new Map();
|
|
2989
|
+
eventLog = [];
|
|
2990
|
+
constructor(config = {}) {
|
|
2991
|
+
this.loader = new YAMLLoader();
|
|
2992
|
+
this.config = {
|
|
2993
|
+
verbose: config.verbose ?? true,
|
|
2994
|
+
onEvent: config.onEvent
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
/**
|
|
2998
|
+
* Load a workflow from a YAML file.
|
|
2999
|
+
*/
|
|
3000
|
+
loadWorkflow(filePath) {
|
|
3001
|
+
const workflow = this.loader.loadFromFile(filePath);
|
|
3002
|
+
this.displayWorkflowInfo(workflow);
|
|
3003
|
+
return workflow;
|
|
3004
|
+
}
|
|
3005
|
+
/**
|
|
3006
|
+
* Load a workflow from YAML string.
|
|
3007
|
+
*/
|
|
3008
|
+
loadWorkflowFromString(yamlContent) {
|
|
3009
|
+
const workflow = this.loader.loadFromString(yamlContent);
|
|
3010
|
+
this.displayWorkflowInfo(workflow);
|
|
3011
|
+
return workflow;
|
|
3012
|
+
}
|
|
3013
|
+
/**
|
|
3014
|
+
* Run a loaded workflow with a task.
|
|
3015
|
+
*/
|
|
3016
|
+
async runWorkflow(workflow, task) {
|
|
3017
|
+
this.displayRunStart(workflow.name, task);
|
|
3018
|
+
this.instrumentAgent(workflow.entrypointAgent, workflow.entrypointAgent.getName(), 0);
|
|
3019
|
+
for (const [name, agent] of workflow.agents) {
|
|
3020
|
+
if (agent !== workflow.entrypointAgent) {
|
|
3021
|
+
this.instrumentAgent(agent, name, 1);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
try {
|
|
3025
|
+
const result = await workflow.entrypointAgent.run(task);
|
|
3026
|
+
this.displayRunEnd(result);
|
|
3027
|
+
return result;
|
|
3028
|
+
} catch (error) {
|
|
3029
|
+
this.displayError(error);
|
|
3030
|
+
throw error;
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Run a standalone agent with a task.
|
|
3035
|
+
*/
|
|
3036
|
+
async runAgent(agent, task) {
|
|
3037
|
+
this.instrumentAgent(agent, agent.getName(), 0);
|
|
3038
|
+
const result = await agent.run(task);
|
|
3039
|
+
return result;
|
|
3040
|
+
}
|
|
3041
|
+
/**
|
|
3042
|
+
* Instrument an agent with orchestrator event tracking.
|
|
3043
|
+
*/
|
|
3044
|
+
instrumentAgent(agent, name, depth) {
|
|
3045
|
+
this.activeAgents.set(name, { agent, depth });
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* Display workflow info at startup.
|
|
3049
|
+
*/
|
|
3050
|
+
displayWorkflowInfo(workflow) {
|
|
3051
|
+
if (!this.config.verbose) return;
|
|
3052
|
+
const line = "\u2550".repeat(70);
|
|
3053
|
+
console.log(import_chalk2.default.cyan(line));
|
|
3054
|
+
console.log(import_chalk2.default.cyan.bold(` Workflow: ${workflow.name}`));
|
|
3055
|
+
if (workflow.description) {
|
|
3056
|
+
console.log(import_chalk2.default.cyan(` ${workflow.description}`));
|
|
3057
|
+
}
|
|
3058
|
+
console.log(import_chalk2.default.cyan(` Agents: ${Array.from(workflow.agents.keys()).join(", ")}`));
|
|
3059
|
+
console.log(import_chalk2.default.cyan(` Tools: ${Array.from(workflow.tools.keys()).join(", ") || "(none defined at workflow level)"}`));
|
|
3060
|
+
console.log(import_chalk2.default.cyan(` Entrypoint: ${workflow.entrypointAgent.getName()}`));
|
|
3061
|
+
console.log(import_chalk2.default.cyan(line));
|
|
3062
|
+
console.log();
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* Display run start info.
|
|
3066
|
+
*/
|
|
3067
|
+
displayRunStart(workflowName, task) {
|
|
3068
|
+
if (!this.config.verbose) return;
|
|
3069
|
+
console.log(import_chalk2.default.green.bold(`
|
|
3070
|
+
\u25B6 Running workflow "${workflowName}"`));
|
|
3071
|
+
console.log(import_chalk2.default.green(` Task: ${task}`));
|
|
3072
|
+
console.log(import_chalk2.default.gray("\u2500".repeat(70)));
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Display run completion info.
|
|
3076
|
+
*/
|
|
3077
|
+
displayRunEnd(result) {
|
|
3078
|
+
if (!this.config.verbose) return;
|
|
3079
|
+
console.log(import_chalk2.default.gray("\n" + "\u2500".repeat(70)));
|
|
3080
|
+
console.log(import_chalk2.default.green.bold(`
|
|
3081
|
+
\u2705 Workflow complete`));
|
|
3082
|
+
console.log(import_chalk2.default.green(` Duration: ${(result.duration / 1e3).toFixed(2)}s`));
|
|
3083
|
+
console.log(import_chalk2.default.green(` Tokens: ${result.tokenUsage.totalTokens}`));
|
|
3084
|
+
console.log(import_chalk2.default.green(` Steps: ${result.steps.length}`));
|
|
3085
|
+
const outputStr = typeof result.output === "string" ? result.output : JSON.stringify(result.output, null, 2);
|
|
3086
|
+
console.log(import_chalk2.default.magenta.bold("\n Final Output:"));
|
|
3087
|
+
const indentedOutput = outputStr.split("\n").map((line) => ` ${line}`).join("\n");
|
|
3088
|
+
console.log(import_chalk2.default.magenta(indentedOutput));
|
|
3089
|
+
console.log();
|
|
3090
|
+
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Display an error.
|
|
3093
|
+
*/
|
|
3094
|
+
displayError(error) {
|
|
3095
|
+
if (!this.config.verbose) return;
|
|
3096
|
+
console.error(import_chalk2.default.red.bold(`
|
|
3097
|
+
\u274C Workflow failed: ${error.message}`));
|
|
3098
|
+
if (error.stack) {
|
|
3099
|
+
console.error(import_chalk2.default.red.dim(error.stack));
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
/**
|
|
3103
|
+
* Log an orchestration event.
|
|
3104
|
+
*/
|
|
3105
|
+
logEvent(event) {
|
|
3106
|
+
this.eventLog.push(event);
|
|
3107
|
+
if (this.config.onEvent) {
|
|
3108
|
+
this.config.onEvent(event);
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
/**
|
|
3112
|
+
* Get the event log.
|
|
3113
|
+
*/
|
|
3114
|
+
getEventLog() {
|
|
3115
|
+
return [...this.eventLog];
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Get the YAML loader for registering custom tools.
|
|
3119
|
+
*/
|
|
3120
|
+
getLoader() {
|
|
3121
|
+
return this.loader;
|
|
3122
|
+
}
|
|
3123
|
+
};
|
|
1831
3124
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1832
3125
|
0 && (module.exports = {
|
|
1833
3126
|
Agent,
|
|
@@ -1835,18 +3128,29 @@ function agentAsTool(agent, options) {
|
|
|
1835
3128
|
AgentMemory,
|
|
1836
3129
|
AgentTool,
|
|
1837
3130
|
CodeAgent,
|
|
3131
|
+
CurlTool,
|
|
3132
|
+
ExaGetContentsTool,
|
|
3133
|
+
ExaResearchTool,
|
|
3134
|
+
ExaSearchTool,
|
|
1838
3135
|
FINAL_ANSWER_PROMPT,
|
|
1839
3136
|
FinalAnswerTool,
|
|
1840
3137
|
LocalExecutor,
|
|
1841
3138
|
LogLevel,
|
|
1842
3139
|
Model,
|
|
1843
3140
|
OpenAIModel,
|
|
3141
|
+
Orchestrator,
|
|
3142
|
+
ReadFileTool,
|
|
1844
3143
|
Tool,
|
|
3144
|
+
ToolUseAgent,
|
|
1845
3145
|
UserInputTool,
|
|
3146
|
+
WriteFileTool,
|
|
3147
|
+
YAMLLoader,
|
|
1846
3148
|
agentAsTool,
|
|
1847
3149
|
createTool,
|
|
1848
3150
|
finalAnswerTool,
|
|
3151
|
+
formatToolDescriptions,
|
|
1849
3152
|
generateSystemPrompt,
|
|
3153
|
+
generateToolUseSystemPrompt,
|
|
1850
3154
|
getErrorRecoveryPrompt
|
|
1851
3155
|
});
|
|
1852
3156
|
//# sourceMappingURL=index.js.map
|