@samrahimi/smol-js 0.4.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/index.mjs CHANGED
@@ -8,31 +8,43 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
8
8
  })(LogLevel || {});
9
9
 
10
10
  // src/memory/AgentMemory.ts
11
+ function estimateTokens(text) {
12
+ return Math.ceil(text.length / 4);
13
+ }
14
+ function estimateMessagesTokens(messages) {
15
+ let total = 0;
16
+ for (const msg of messages) {
17
+ total += estimateTokens(msg.content ?? "");
18
+ if (msg.toolCalls) {
19
+ total += estimateTokens(JSON.stringify(msg.toolCalls));
20
+ }
21
+ total += 4;
22
+ }
23
+ return total;
24
+ }
11
25
  var AgentMemory = class {
12
- /**
13
- * System prompt step (always first)
14
- */
26
+ /** System prompt step (always first) */
15
27
  systemPrompt;
16
- /**
17
- * All execution steps
18
- */
28
+ /** All execution steps */
19
29
  steps = [];
20
- constructor(systemPrompt) {
30
+ maxContextLength;
31
+ memoryStrategy;
32
+ model;
33
+ constructor(systemPrompt, config) {
21
34
  this.systemPrompt = {
22
35
  type: "system",
23
36
  content: systemPrompt,
24
37
  timestamp: Date.now()
25
38
  };
39
+ this.maxContextLength = config?.maxContextLength ?? 1e5;
40
+ this.memoryStrategy = config?.memoryStrategy ?? "truncate";
41
+ this.model = config?.model;
26
42
  }
27
- /**
28
- * Reset memory, keeping only the system prompt.
29
- */
43
+ /** Reset memory, keeping only the system prompt */
30
44
  reset() {
31
45
  this.steps = [];
32
46
  }
33
- /**
34
- * Add a task step.
35
- */
47
+ /** Add a task step */
36
48
  addTask(task) {
37
49
  const step = {
38
50
  type: "task",
@@ -42,25 +54,19 @@ var AgentMemory = class {
42
54
  this.steps.push(step);
43
55
  return step;
44
56
  }
45
- /**
46
- * Create a new action step.
47
- */
57
+ /** Create a new action step */
48
58
  createActionStep(stepNumber) {
49
59
  const step = {
50
60
  type: "action",
51
61
  stepNumber,
52
- timing: {
53
- startTime: Date.now()
54
- },
62
+ timing: { startTime: Date.now() },
55
63
  modelInputMessages: [],
56
64
  timestamp: Date.now()
57
65
  };
58
66
  this.steps.push(step);
59
67
  return step;
60
68
  }
61
- /**
62
- * Add a final answer step.
63
- */
69
+ /** Add a final answer step */
64
70
  addFinalAnswer(answer) {
65
71
  const step = {
66
72
  type: "final",
@@ -70,20 +76,17 @@ var AgentMemory = class {
70
76
  this.steps.push(step);
71
77
  return step;
72
78
  }
73
- /**
74
- * Get the last step.
75
- */
79
+ /** Get the last step */
76
80
  getLastStep() {
77
81
  return this.steps[this.steps.length - 1];
78
82
  }
79
- /**
80
- * Get all action steps.
81
- */
83
+ /** Get all action steps */
82
84
  getActionSteps() {
83
85
  return this.steps.filter((s) => s.type === "action");
84
86
  }
85
87
  /**
86
88
  * Convert memory to messages for LLM context.
89
+ * Handles both CodeAgent (observation-based) and ToolUseAgent (tool_call-based) patterns.
87
90
  */
88
91
  toMessages() {
89
92
  const messages = [];
@@ -100,23 +103,40 @@ var AgentMemory = class {
100
103
  });
101
104
  break;
102
105
  case "action":
103
- if (step.modelOutputMessage) {
106
+ if (step.toolCalls && step.toolCalls.length > 0) {
104
107
  messages.push({
105
108
  role: "assistant",
106
- content: step.modelOutputMessage.content
107
- });
108
- }
109
- if (step.observation) {
110
- messages.push({
111
- role: "user",
112
- content: step.observation
113
- });
114
- }
115
- if (step.error) {
116
- messages.push({
117
- role: "user",
118
- content: `Error: ${step.error.message}`
109
+ content: step.modelOutputMessage?.content ?? null,
110
+ toolCalls: step.toolCalls
119
111
  });
112
+ if (step.toolResults) {
113
+ for (const result of step.toolResults) {
114
+ messages.push({
115
+ role: "tool",
116
+ content: result.error ? `Error: ${result.error}` : typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2),
117
+ toolCallId: result.toolCallId
118
+ });
119
+ }
120
+ }
121
+ } else {
122
+ if (step.modelOutputMessage) {
123
+ messages.push({
124
+ role: "assistant",
125
+ content: step.modelOutputMessage.content
126
+ });
127
+ }
128
+ if (step.observation) {
129
+ messages.push({
130
+ role: "user",
131
+ content: step.observation
132
+ });
133
+ }
134
+ if (step.error && !step.observation) {
135
+ messages.push({
136
+ role: "user",
137
+ content: `Error: ${step.error.message}`
138
+ });
139
+ }
120
140
  }
121
141
  break;
122
142
  case "final":
@@ -126,8 +146,90 @@ var AgentMemory = class {
126
146
  return messages;
127
147
  }
128
148
  /**
129
- * Get total token usage across all steps.
149
+ * Manage context length - truncate or compact if exceeded.
150
+ */
151
+ async manageContext() {
152
+ const messages = this.toMessages();
153
+ const tokenCount = estimateMessagesTokens(messages);
154
+ if (tokenCount <= this.maxContextLength) {
155
+ return;
156
+ }
157
+ if (this.memoryStrategy === "truncate") {
158
+ this.truncateOlderMessages();
159
+ } else if (this.memoryStrategy === "compact") {
160
+ await this.compactMessages();
161
+ }
162
+ }
163
+ /**
164
+ * Truncate older action steps to fit within context.
165
+ */
166
+ truncateOlderMessages() {
167
+ const actionSteps = this.getActionSteps();
168
+ if (actionSteps.length <= 2) return;
169
+ const targetTokens = this.maxContextLength * 0.75;
170
+ let currentTokens = estimateMessagesTokens(this.toMessages());
171
+ while (currentTokens > targetTokens && this.steps.length > 2) {
172
+ const idx = this.steps.findIndex((s) => s.type === "action");
173
+ if (idx === -1) break;
174
+ this.steps.splice(idx, 1);
175
+ currentTokens = estimateMessagesTokens(this.toMessages());
176
+ }
177
+ }
178
+ /**
179
+ * Compact older messages into a summary.
130
180
  */
181
+ async compactMessages() {
182
+ if (!this.model) {
183
+ this.truncateOlderMessages();
184
+ return;
185
+ }
186
+ const actionSteps = this.getActionSteps();
187
+ if (actionSteps.length <= 2) return;
188
+ const stepsToSummarize = actionSteps.slice(0, -2);
189
+ const summaryContent = stepsToSummarize.map((step) => {
190
+ const parts = [];
191
+ if (step.modelOutputMessage?.content) {
192
+ parts.push(`Action: ${step.modelOutputMessage.content.slice(0, 200)}`);
193
+ }
194
+ if (step.observation) {
195
+ parts.push(`Observation: ${step.observation.slice(0, 200)}`);
196
+ }
197
+ if (step.toolResults) {
198
+ for (const r of step.toolResults) {
199
+ const resultStr = typeof r.result === "string" ? r.result : JSON.stringify(r.result);
200
+ parts.push(`Tool ${r.toolName}: ${resultStr.slice(0, 200)}`);
201
+ }
202
+ }
203
+ return parts.join("\n");
204
+ }).join("\n---\n");
205
+ try {
206
+ const summaryResponse = await this.model.generate([
207
+ {
208
+ role: "system",
209
+ content: "Summarize the following agent execution history concisely, preserving key findings and results. Be brief but complete."
210
+ },
211
+ {
212
+ role: "user",
213
+ content: summaryContent
214
+ }
215
+ ]);
216
+ const recentSteps = this.steps.filter(
217
+ (s) => s.type === "task" || s.type === "final" || s.type === "action" && actionSteps.indexOf(s) >= actionSteps.length - 2
218
+ );
219
+ this.steps = [
220
+ {
221
+ type: "task",
222
+ task: `[Context Summary from previous steps]
223
+ ${summaryResponse.content}`,
224
+ timestamp: Date.now()
225
+ },
226
+ ...recentSteps
227
+ ];
228
+ } catch {
229
+ this.truncateOlderMessages();
230
+ }
231
+ }
232
+ /** Get total token usage across all steps */
131
233
  getTotalTokenUsage() {
132
234
  let inputTokens = 0;
133
235
  let outputTokens = 0;
@@ -143,9 +245,11 @@ var AgentMemory = class {
143
245
  totalTokens: inputTokens + outputTokens
144
246
  };
145
247
  }
146
- /**
147
- * Get a summary of the memory for logging.
148
- */
248
+ /** Get current estimated token count */
249
+ getEstimatedTokenCount() {
250
+ return estimateMessagesTokens(this.toMessages());
251
+ }
252
+ /** Get a summary of the memory for logging */
149
253
  getSummary() {
150
254
  const actionSteps = this.getActionSteps();
151
255
  const lines = [
@@ -159,9 +263,7 @@ var AgentMemory = class {
159
263
  }
160
264
  return lines.join("\n");
161
265
  }
162
- /**
163
- * Serialize memory to JSON.
164
- */
266
+ /** Serialize memory to JSON */
165
267
  toJSON() {
166
268
  return {
167
269
  systemPrompt: this.systemPrompt,
@@ -450,35 +552,24 @@ ${"\u2550".repeat(60)}
450
552
  };
451
553
 
452
554
  // src/agents/Agent.ts
555
+ var DEFAULT_MAX_CONTEXT_LENGTH = 1e5;
453
556
  var Agent = class {
454
- /**
455
- * The LLM model for generation
456
- */
557
+ /** The LLM model for generation */
457
558
  model;
458
- /**
459
- * Available tools mapped by name
460
- */
559
+ /** Available tools mapped by name */
461
560
  tools = /* @__PURE__ */ new Map();
462
- /**
463
- * Agent memory tracking all steps
464
- */
561
+ /** Agent memory tracking all steps */
465
562
  memory;
466
- /**
467
- * Logger for formatted output
468
- */
563
+ /** Logger for formatted output */
469
564
  logger;
470
- /**
471
- * Configuration options
472
- */
565
+ /** Configuration options */
473
566
  config;
474
- /**
475
- * Current step number
476
- */
567
+ /** Current step number */
477
568
  currentStep = 0;
478
- /**
479
- * Whether the agent is currently running
480
- */
569
+ /** Whether the agent is currently running */
481
570
  isRunning = false;
571
+ /** Whether the agent has been initialized at least once */
572
+ initialized = false;
482
573
  constructor(config) {
483
574
  this.model = config.model;
484
575
  this.logger = new AgentLogger(config.verboseLevel ?? 1 /* INFO */);
@@ -487,7 +578,14 @@ var Agent = class {
487
578
  codeExecutionDelay: config.codeExecutionDelay ?? 5e3,
488
579
  customInstructions: config.customInstructions ?? "",
489
580
  verboseLevel: config.verboseLevel ?? 1 /* INFO */,
490
- streamOutputs: config.streamOutputs ?? true
581
+ streamOutputs: config.streamOutputs ?? true,
582
+ persistent: config.persistent ?? false,
583
+ maxContextLength: config.maxContextLength ?? DEFAULT_MAX_CONTEXT_LENGTH,
584
+ memoryStrategy: config.memoryStrategy ?? "truncate",
585
+ maxTokens: config.maxTokens,
586
+ temperature: config.temperature,
587
+ name: config.name ?? "Agent",
588
+ onEvent: config.onEvent
491
589
  };
492
590
  if (config.tools) {
493
591
  for (const tool of config.tools) {
@@ -497,27 +595,31 @@ var Agent = class {
497
595
  }
498
596
  /**
499
597
  * Run the agent on a task.
500
- *
501
- * @param task - The task description
502
- * @param reset - Whether to reset memory before running
503
- * @returns The final result
504
598
  */
505
599
  async run(task, reset = true) {
506
600
  const startTime = Date.now();
507
- if (reset || !this.memory) {
601
+ const shouldReset = !this.config.persistent ? reset || !this.memory : !this.initialized;
602
+ if (shouldReset) {
508
603
  const systemPrompt = this.initializeSystemPrompt();
509
- this.memory = new AgentMemory(systemPrompt);
604
+ this.memory = new AgentMemory(systemPrompt, {
605
+ maxContextLength: this.config.maxContextLength,
606
+ memoryStrategy: this.config.memoryStrategy,
607
+ model: this.model
608
+ });
510
609
  this.currentStep = 0;
610
+ this.initialized = true;
511
611
  }
512
612
  this.memory.addTask(task);
513
613
  this.isRunning = true;
514
- this.logger.header(`\u{1F680} Starting Agent: ${task.slice(0, 50)}${task.length > 50 ? "..." : ""}`);
614
+ this.emitEvent("agent_start", { task, name: this.config.name });
615
+ this.logger.header(`Starting ${this.config.name}: ${task.slice(0, 80)}${task.length > 80 ? "..." : ""}`);
515
616
  let finalOutput = null;
516
617
  let isFinalAnswer = false;
517
618
  try {
518
619
  while (this.currentStep < this.config.maxSteps && this.isRunning) {
519
620
  this.currentStep++;
520
621
  this.logger.stepProgress(this.currentStep, this.config.maxSteps);
622
+ this.emitEvent("agent_step", { step: this.currentStep, maxSteps: this.config.maxSteps });
521
623
  const memoryStep = this.memory.createActionStep(this.currentStep);
522
624
  try {
523
625
  const actionOutput = await this.executeStep(memoryStep);
@@ -536,7 +638,9 @@ var Agent = class {
536
638
  memoryStep.timing.endTime = Date.now();
537
639
  memoryStep.timing.duration = memoryStep.timing.endTime - memoryStep.timing.startTime;
538
640
  this.logger.error("Step execution failed", error);
641
+ this.emitEvent("agent_error", { error: error.message, step: this.currentStep });
539
642
  }
643
+ await this.memory.manageContext();
540
644
  }
541
645
  if (!isFinalAnswer && this.currentStep >= this.config.maxSteps) {
542
646
  this.logger.warn(`Max steps (${this.config.maxSteps}) reached without final answer`);
@@ -548,13 +652,10 @@ var Agent = class {
548
652
  const duration = Date.now() - startTime;
549
653
  const tokenUsage = this.memory.getTotalTokenUsage();
550
654
  this.memory.addFinalAnswer(finalOutput);
655
+ this.emitEvent("agent_end", { output: finalOutput, duration, tokenUsage });
551
656
  this.logger.info(`
552
- \u23F1\uFE0F Total time: ${(duration / 1e3).toFixed(2)}s`);
553
- this.logger.info(`\u{1F4CA} Total tokens: ${tokenUsage.totalTokens}`);
554
- const logPath = this.logger.getLogPath();
555
- if (logPath) {
556
- this.logger.info(`\u{1F4C1} Log file: ${logPath}`);
557
- }
657
+ Total time: ${(duration / 1e3).toFixed(2)}s`);
658
+ this.logger.info(`Total tokens: ${tokenUsage.totalTokens}`);
558
659
  return {
559
660
  output: finalOutput,
560
661
  steps: this.memory.steps,
@@ -570,49 +671,48 @@ var Agent = class {
570
671
  const messages = this.memory.toMessages();
571
672
  messages.push({
572
673
  role: "user",
573
- 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}"
574
-
575
- Summarize what you accomplished and provide a final answer. Call final_answer() with your response.`
674
+ 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.`
675
+ });
676
+ const response = await this.model.generate(messages, {
677
+ maxTokens: this.config.maxTokens,
678
+ temperature: this.config.temperature
576
679
  });
577
- const response = await this.model.generate(messages);
578
680
  return response.content;
579
681
  }
580
- /**
581
- * Stop the agent.
582
- */
682
+ /** Emit an orchestration event */
683
+ emitEvent(type, data) {
684
+ if (this.config.onEvent) {
685
+ this.config.onEvent({ type, data });
686
+ }
687
+ }
688
+ /** Stop the agent */
583
689
  stop() {
584
690
  this.isRunning = false;
585
691
  this.logger.info("Agent stopped by user");
586
692
  }
587
- /**
588
- * Get the current memory.
589
- */
693
+ /** Get the current memory */
590
694
  getMemory() {
591
695
  return this.memory;
592
696
  }
593
- /**
594
- * Get registered tools.
595
- */
697
+ /** Get registered tools */
596
698
  getTools() {
597
699
  return this.tools;
598
700
  }
599
- /**
600
- * Add a tool to the agent.
601
- */
701
+ /** Add a tool to the agent */
602
702
  addTool(tool) {
603
703
  this.tools.set(tool.name, tool);
604
704
  }
605
- /**
606
- * Remove a tool from the agent.
607
- */
705
+ /** Remove a tool from the agent */
608
706
  removeTool(name) {
609
707
  return this.tools.delete(name);
610
708
  }
611
- /**
612
- * Sleep for a specified duration.
613
- */
709
+ /** Get agent name */
710
+ getName() {
711
+ return this.config.name;
712
+ }
713
+ /** Sleep for a specified duration */
614
714
  sleep(ms) {
615
- return new Promise((resolve2) => setTimeout(resolve2, ms));
715
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
616
716
  }
617
717
  };
618
718
 
@@ -1096,9 +1196,6 @@ var Tool = class {
1096
1196
  }
1097
1197
  providedKeys.delete(key);
1098
1198
  }
1099
- if (providedKeys.size > 0) {
1100
- console.warn(`Unknown arguments provided to ${this.name}: ${[...providedKeys].join(", ")}`);
1101
- }
1102
1199
  }
1103
1200
  /**
1104
1201
  * Check if a value matches the expected type.
@@ -1141,6 +1238,62 @@ ${argsDoc}
1141
1238
  async function ${this.name}(${argsSignature}): Promise<${this.typeToJsType(this.outputType)}> { ... }
1142
1239
  `.trim();
1143
1240
  }
1241
+ /**
1242
+ * Generate an OpenAI-compatible tool definition for function calling.
1243
+ */
1244
+ toOpenAITool() {
1245
+ const properties = {};
1246
+ const required = [];
1247
+ for (const [key, input] of Object.entries(this.inputs)) {
1248
+ const prop = {
1249
+ type: this.typeToJsonSchemaType(input.type),
1250
+ description: input.description
1251
+ };
1252
+ if (input.enum) {
1253
+ prop.enum = input.enum;
1254
+ }
1255
+ if (input.default !== void 0) {
1256
+ prop.default = input.default;
1257
+ }
1258
+ properties[key] = prop;
1259
+ if (input.required !== false) {
1260
+ required.push(key);
1261
+ }
1262
+ }
1263
+ return {
1264
+ type: "function",
1265
+ function: {
1266
+ name: this.name,
1267
+ description: this.description,
1268
+ parameters: {
1269
+ type: "object",
1270
+ properties,
1271
+ ...required.length > 0 && { required }
1272
+ }
1273
+ }
1274
+ };
1275
+ }
1276
+ /**
1277
+ * Convert tool input type to JSON Schema type.
1278
+ */
1279
+ typeToJsonSchemaType(type) {
1280
+ switch (type) {
1281
+ case "string":
1282
+ return "string";
1283
+ case "number":
1284
+ return "number";
1285
+ case "boolean":
1286
+ return "boolean";
1287
+ case "array":
1288
+ return "array";
1289
+ case "object":
1290
+ return "object";
1291
+ case "any":
1292
+ return "string";
1293
+ default:
1294
+ return "string";
1295
+ }
1296
+ }
1144
1297
  /**
1145
1298
  * Convert tool input type to JS/TS type string.
1146
1299
  */
@@ -1228,12 +1381,12 @@ var UserInputTool = class extends Tool {
1228
1381
  input: process.stdin,
1229
1382
  output: process.stdout
1230
1383
  });
1231
- return new Promise((resolve2) => {
1384
+ return new Promise((resolve5) => {
1232
1385
  rl.question(`
1233
1386
  [Agent asks]: ${question}
1234
1387
  Your response: `, (answer) => {
1235
1388
  rl.close();
1236
- resolve2(answer);
1389
+ resolve5(answer);
1237
1390
  });
1238
1391
  });
1239
1392
  }
@@ -1498,7 +1651,9 @@ ${result.error.message}`;
1498
1651
  this.logger.subheader("Agent thinking...");
1499
1652
  let fullContent = "";
1500
1653
  const generator = this.model.generateStream(messages, {
1501
- stopSequences: ["Observation:", "Observation:\n"]
1654
+ stopSequences: ["Observation:", "Observation:\n"],
1655
+ maxTokens: this.config.maxTokens,
1656
+ temperature: this.config.temperature
1502
1657
  });
1503
1658
  for await (const chunk of generator) {
1504
1659
  this.logger.streamChar(chunk);
@@ -1512,7 +1667,9 @@ ${result.error.message}`;
1512
1667
  } else {
1513
1668
  this.logger.subheader("Agent thinking...");
1514
1669
  return this.model.generate(messages, {
1515
- stopSequences: ["Observation:", "Observation:\n"]
1670
+ stopSequences: ["Observation:", "Observation:\n"],
1671
+ maxTokens: this.config.maxTokens,
1672
+ temperature: this.config.temperature
1516
1673
  });
1517
1674
  }
1518
1675
  }
@@ -1568,6 +1725,263 @@ ${parts.join("\n\n")}`;
1568
1725
  }
1569
1726
  };
1570
1727
 
1728
+ // src/prompts/toolUseAgent.ts
1729
+ function generateToolUseSystemPrompt(variables) {
1730
+ const { tools, customInstructions, hasSubAgents, hasFileTools } = variables;
1731
+ let contentGuidelines = "";
1732
+ if (hasFileTools) {
1733
+ contentGuidelines += `
1734
+ ## Content Output Guidelines
1735
+
1736
+ When you produce long-form content (reports, articles, analyses, or any output longer than a few paragraphs):
1737
+ 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\`).
1738
+ 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"
1739
+
1740
+ This ensures managing agents can access your full output via \`read_file\` without it being truncated in message passing.
1741
+ `;
1742
+ }
1743
+ if (hasSubAgents) {
1744
+ contentGuidelines += `
1745
+ ## Working with Sub-Agents
1746
+
1747
+ When you delegate tasks to sub-agents:
1748
+ - Sub-agents return a **summary and filename** rather than the full content of long-form outputs.
1749
+ - To access the full content a sub-agent produced, use \`read_file\` with the filename they provide.
1750
+ - **Do NOT re-invoke a sub-agent to retrieve content it already created.** Instead, read the file directly.
1751
+ - When composing your own final output from sub-agent results, read their files as needed and synthesize.
1752
+ `;
1753
+ }
1754
+ if (hasSubAgents && hasFileTools) {
1755
+ contentGuidelines += `
1756
+ ## When You Are Both a Manager and a Sub-Agent
1757
+
1758
+ If you manage sub-agents AND are yourself delegated tasks by a parent agent:
1759
+ - Follow the sub-agent content guidelines: save your own long-form output to a file, return summary + filename.
1760
+ - Follow the manager guidelines: read sub-agent output files rather than re-calling them.
1761
+ `;
1762
+ }
1763
+ 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.
1764
+
1765
+ ## How You Work
1766
+
1767
+ You follow a ReAct (Reasoning + Acting) framework:
1768
+ 1. **Think**: Analyze the current situation and decide what to do next
1769
+ 2. **Act**: Call one or more tools to perform actions
1770
+ 3. **Observe**: Review the results of your tool calls
1771
+ 4. Repeat until you have the final answer
1772
+
1773
+ ## Available Tools
1774
+
1775
+ ${tools}
1776
+
1777
+ ## Rules
1778
+
1779
+ 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.
1780
+
1781
+ 2. **Think before acting**: Provide your reasoning in the content of your response before making tool calls. This helps track your thought process.
1782
+
1783
+ 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.
1784
+
1785
+ 4. **Handle errors gracefully**: If a tool call fails, analyze the error and try a different approach.
1786
+
1787
+ 5. **Be concise**: Keep your reasoning brief and focused. Don't over-explain.
1788
+
1789
+ 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.
1790
+ ${contentGuidelines}
1791
+ ${customInstructions ? `## Additional Instructions
1792
+
1793
+ ${customInstructions}` : ""}
1794
+
1795
+ Now, let's solve the task step by step. Think carefully about what tools to use and in what order.`;
1796
+ }
1797
+ function formatToolDescriptions(tools) {
1798
+ return tools.map((tool) => {
1799
+ const params = Object.entries(tool.inputs).map(([name, input]) => {
1800
+ const req = input.required !== false ? " (required)" : " (optional)";
1801
+ return ` - ${name}${req}: ${input.description}`;
1802
+ }).join("\n");
1803
+ return `### ${tool.name}
1804
+ ${tool.description}
1805
+ Parameters:
1806
+ ${params}`;
1807
+ }).join("\n\n");
1808
+ }
1809
+
1810
+ // src/agents/ToolUseAgent.ts
1811
+ var ToolUseAgent = class extends Agent {
1812
+ parallelToolCalls;
1813
+ constructor(config) {
1814
+ super(config);
1815
+ this.parallelToolCalls = config.parallelToolCalls ?? true;
1816
+ if (!this.tools.has("final_answer")) {
1817
+ this.tools.set("final_answer", new FinalAnswerTool());
1818
+ }
1819
+ }
1820
+ /**
1821
+ * Initialize the system prompt with tool descriptions.
1822
+ */
1823
+ initializeSystemPrompt() {
1824
+ const toolList = Array.from(this.tools.values());
1825
+ const toolDescriptions = formatToolDescriptions(
1826
+ toolList.map((t) => ({
1827
+ name: t.name,
1828
+ description: t.description,
1829
+ inputs: t.inputs
1830
+ }))
1831
+ );
1832
+ const toolNames = new Set(Array.from(this.tools.keys()));
1833
+ const hasSubAgents = toolList.some((t) => t.constructor.name === "AgentTool");
1834
+ const hasFileTools = toolNames.has("read_file") || toolNames.has("write_file") || toolNames.has("read") || toolNames.has("write");
1835
+ return generateToolUseSystemPrompt({
1836
+ tools: toolDescriptions,
1837
+ customInstructions: this.config.customInstructions,
1838
+ hasSubAgents,
1839
+ hasFileTools
1840
+ });
1841
+ }
1842
+ /**
1843
+ * Execute a single step: send messages with tool definitions, process tool calls.
1844
+ */
1845
+ async executeStep(memoryStep) {
1846
+ const messages = this.memory.toMessages();
1847
+ memoryStep.modelInputMessages = [...messages];
1848
+ const actionSteps = this.memory.getActionSteps();
1849
+ const prevStep = actionSteps.length >= 2 ? actionSteps[actionSteps.length - 2] : void 0;
1850
+ if (prevStep?.error) {
1851
+ messages.push({
1852
+ role: "user",
1853
+ content: `Your previous action encountered an error: ${prevStep.error.message}
1854
+ Please try a different approach.`
1855
+ });
1856
+ }
1857
+ const toolDefinitions = Array.from(this.tools.values()).map((t) => t.toOpenAITool());
1858
+ this.logger.subheader("Agent thinking...");
1859
+ const response = await this.model.generate(messages, {
1860
+ toolDefinitions,
1861
+ maxTokens: this.config.maxTokens,
1862
+ temperature: this.config.temperature
1863
+ });
1864
+ memoryStep.modelOutputMessage = response;
1865
+ memoryStep.tokenUsage = response.tokenUsage;
1866
+ if (response.content && response.content.trim()) {
1867
+ this.logger.reasoning(response.content.trim());
1868
+ }
1869
+ if (!response.toolCalls || response.toolCalls.length === 0) {
1870
+ this.logger.warn("No tool calls in response. Prompting model to use tools.");
1871
+ 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.";
1872
+ return { output: null, isFinalAnswer: false };
1873
+ }
1874
+ memoryStep.toolCalls = response.toolCalls;
1875
+ const toolResults = await this.processToolCalls(response.toolCalls);
1876
+ memoryStep.toolResults = toolResults;
1877
+ for (const result of toolResults) {
1878
+ if (result.toolName === "final_answer") {
1879
+ return { output: result.result, isFinalAnswer: true };
1880
+ }
1881
+ }
1882
+ for (const result of toolResults) {
1883
+ if (result.error) {
1884
+ this.logger.error(`Tool ${result.toolName} failed: ${result.error}`);
1885
+ this.emitEvent("agent_error", { tool: result.toolName, error: result.error });
1886
+ } else {
1887
+ const resultStr = typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2);
1888
+ this.logger.output(`[${result.toolName}]: ${resultStr.slice(0, 500)}${resultStr.length > 500 ? "..." : ""}`);
1889
+ this.emitEvent("agent_observation", { tool: result.toolName, result: resultStr.slice(0, 500) });
1890
+ }
1891
+ }
1892
+ return { output: null, isFinalAnswer: false };
1893
+ }
1894
+ /**
1895
+ * Process tool calls from the model response.
1896
+ */
1897
+ async processToolCalls(toolCalls) {
1898
+ const results = [];
1899
+ const executeTool = async (tc) => {
1900
+ const toolName = tc.function.name;
1901
+ const tool = this.tools.get(toolName);
1902
+ if (!tool) {
1903
+ return {
1904
+ toolCallId: tc.id,
1905
+ toolName,
1906
+ result: null,
1907
+ error: `Unknown tool: ${toolName}. Available tools: ${Array.from(this.tools.keys()).join(", ")}`
1908
+ };
1909
+ }
1910
+ let args;
1911
+ try {
1912
+ args = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
1913
+ } catch {
1914
+ return {
1915
+ toolCallId: tc.id,
1916
+ toolName,
1917
+ result: null,
1918
+ error: `Failed to parse tool arguments: ${tc.function.arguments}`
1919
+ };
1920
+ }
1921
+ this.logger.info(` Calling tool: ${toolName}(${JSON.stringify(args).slice(0, 100)}...)`);
1922
+ this.emitEvent("agent_tool_call", { tool: toolName, args });
1923
+ try {
1924
+ const result = await tool.call(args);
1925
+ return {
1926
+ toolCallId: tc.id,
1927
+ toolName,
1928
+ result
1929
+ };
1930
+ } catch (error) {
1931
+ return {
1932
+ toolCallId: tc.id,
1933
+ toolName,
1934
+ result: null,
1935
+ error: `Tool execution error: ${error.message}`
1936
+ };
1937
+ }
1938
+ };
1939
+ if (this.parallelToolCalls) {
1940
+ const promises = toolCalls.map((tc) => executeTool(tc));
1941
+ const resolvedResults = await Promise.all(promises);
1942
+ results.push(...resolvedResults);
1943
+ } else {
1944
+ for (const tc of toolCalls) {
1945
+ results.push(await executeTool(tc));
1946
+ }
1947
+ }
1948
+ return results;
1949
+ }
1950
+ /**
1951
+ * Override provideFinalAnswer to use tool calling format.
1952
+ */
1953
+ async provideFinalAnswer(task) {
1954
+ this.logger.subheader("Generating final answer from accumulated context");
1955
+ const messages = this.memory.toMessages();
1956
+ messages.push({
1957
+ role: "user",
1958
+ 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.`
1959
+ });
1960
+ const toolDefinitions = [new FinalAnswerTool().toOpenAITool()];
1961
+ const response = await this.model.generate(messages, {
1962
+ toolDefinitions,
1963
+ maxTokens: this.config.maxTokens,
1964
+ temperature: this.config.temperature
1965
+ });
1966
+ if (response.toolCalls && response.toolCalls.length > 0) {
1967
+ const tc = response.toolCalls[0];
1968
+ try {
1969
+ const args = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
1970
+ return args.answer;
1971
+ } catch {
1972
+ return response.content;
1973
+ }
1974
+ }
1975
+ return response.content;
1976
+ }
1977
+ /**
1978
+ * Add a tool, which can also be an Agent instance (auto-wraps with AgentTool).
1979
+ */
1980
+ addTool(tool) {
1981
+ super.addTool(tool);
1982
+ }
1983
+ };
1984
+
1571
1985
  // src/models/Model.ts
1572
1986
  var Model = class {
1573
1987
  /**
@@ -1597,25 +2011,18 @@ var Model = class {
1597
2011
 
1598
2012
  // src/models/OpenAIModel.ts
1599
2013
  import OpenAI from "openai";
1600
- var DEFAULT_CONFIG = {
1601
- modelId: "anthropic/claude-sonnet-4.5",
1602
- baseUrl: "https://openrouter.ai/api/v1",
1603
- maxTokens: 65e3,
1604
- temperature: 1,
1605
- timeout: 12e4
1606
- };
2014
+ var DEFAULT_MODEL_ID = "anthropic/claude-sonnet-4.5";
2015
+ var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
2016
+ var DEFAULT_TIMEOUT = 12e4;
1607
2017
  var OpenAIModel = class extends Model {
1608
2018
  modelId;
1609
2019
  client;
1610
2020
  config;
1611
2021
  constructor(config = {}) {
1612
2022
  super();
1613
- this.config = {
1614
- ...DEFAULT_CONFIG,
1615
- ...config
1616
- };
1617
- this.modelId = this.config.modelId ?? DEFAULT_CONFIG.modelId;
1618
- const apiKey = this.config.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
2023
+ this.config = config;
2024
+ this.modelId = config.modelId ?? DEFAULT_MODEL_ID;
2025
+ const apiKey = config.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
1619
2026
  if (!apiKey) {
1620
2027
  throw new Error(
1621
2028
  "API key is required. Set OPENAI_API_KEY or OPENROUTER_API_KEY environment variable, or pass apiKey in config."
@@ -1623,22 +2030,40 @@ var OpenAIModel = class extends Model {
1623
2030
  }
1624
2031
  this.client = new OpenAI({
1625
2032
  apiKey,
1626
- baseURL: this.config.baseUrl,
1627
- timeout: this.config.timeout,
1628
- defaultHeaders: this.config.defaultHeaders
2033
+ baseURL: config.baseUrl ?? DEFAULT_BASE_URL,
2034
+ timeout: config.timeout ?? DEFAULT_TIMEOUT,
2035
+ defaultHeaders: config.defaultHeaders
1629
2036
  });
1630
2037
  }
1631
2038
  /**
1632
- * Generate a response from the model.
2039
+ * Generate a response from the model (supports tool calling).
1633
2040
  */
1634
2041
  async generate(messages, options = {}) {
1635
2042
  const formattedMessages = this.formatMessages(messages);
2043
+ const requestParams = {
2044
+ model: this.modelId,
2045
+ messages: formattedMessages
2046
+ };
2047
+ const maxTokens = options.maxTokens ?? this.config.maxTokens;
2048
+ if (maxTokens !== void 0) {
2049
+ requestParams.max_tokens = maxTokens;
2050
+ }
2051
+ const temperature = options.temperature ?? this.config.temperature;
2052
+ if (temperature !== void 0) {
2053
+ requestParams.temperature = temperature;
2054
+ }
2055
+ if (options.stopSequences) {
2056
+ requestParams.stop = options.stopSequences;
2057
+ }
2058
+ if (options.toolDefinitions && options.toolDefinitions.length > 0) {
2059
+ requestParams.tools = options.toolDefinitions;
2060
+ } else if (options.tools && options.tools.length > 0) {
2061
+ requestParams.tools = options.tools.map((t) => t.toOpenAITool());
2062
+ }
1636
2063
  const response = await this.client.chat.completions.create({
2064
+ ...requestParams,
1637
2065
  model: this.modelId,
1638
- messages: formattedMessages,
1639
- max_tokens: options.maxTokens ?? this.config.maxTokens,
1640
- temperature: options.temperature ?? this.config.temperature,
1641
- ...options.stopSequences && { stop: options.stopSequences }
2066
+ messages: formattedMessages
1642
2067
  });
1643
2068
  const choice = response.choices[0];
1644
2069
  const message = choice?.message;
@@ -1647,12 +2072,21 @@ var OpenAIModel = class extends Model {
1647
2072
  }
1648
2073
  const tokenUsage = response.usage ? {
1649
2074
  inputTokens: response.usage.prompt_tokens,
1650
- outputTokens: response.usage.completion_tokens,
2075
+ outputTokens: response.usage.completion_tokens ?? 0,
1651
2076
  totalTokens: response.usage.total_tokens
1652
2077
  } : void 0;
2078
+ const toolCalls = message.tool_calls?.map((tc) => ({
2079
+ id: tc.id,
2080
+ type: "function",
2081
+ function: {
2082
+ name: tc.function.name,
2083
+ arguments: tc.function.arguments
2084
+ }
2085
+ }));
1653
2086
  return {
1654
2087
  role: "assistant",
1655
2088
  content: message.content ?? "",
2089
+ toolCalls,
1656
2090
  tokenUsage
1657
2091
  };
1658
2092
  }
@@ -1661,12 +2095,26 @@ var OpenAIModel = class extends Model {
1661
2095
  */
1662
2096
  async *generateStream(messages, options = {}) {
1663
2097
  const formattedMessages = this.formatMessages(messages);
2098
+ const requestParams = {
2099
+ model: this.modelId,
2100
+ messages: formattedMessages,
2101
+ stream: true
2102
+ };
2103
+ const maxTokens = options.maxTokens ?? this.config.maxTokens;
2104
+ if (maxTokens !== void 0) {
2105
+ requestParams.max_tokens = maxTokens;
2106
+ }
2107
+ const temperature = options.temperature ?? this.config.temperature;
2108
+ if (temperature !== void 0) {
2109
+ requestParams.temperature = temperature;
2110
+ }
2111
+ if (options.stopSequences) {
2112
+ requestParams.stop = options.stopSequences;
2113
+ }
1664
2114
  const stream = await this.client.chat.completions.create({
2115
+ ...requestParams,
1665
2116
  model: this.modelId,
1666
2117
  messages: formattedMessages,
1667
- max_tokens: options.maxTokens ?? this.config.maxTokens,
1668
- temperature: options.temperature ?? this.config.temperature,
1669
- ...options.stopSequences && { stop: options.stopSequences },
1670
2118
  stream: true
1671
2119
  });
1672
2120
  let fullContent = "";
@@ -1683,16 +2131,37 @@ var OpenAIModel = class extends Model {
1683
2131
  };
1684
2132
  }
1685
2133
  /**
1686
- * Format messages for the OpenAI API.
2134
+ * Format messages for the OpenAI API, including tool call/response messages.
1687
2135
  */
1688
2136
  formatMessages(messages) {
1689
2137
  return messages.map((msg) => {
2138
+ if (msg.role === "tool" && msg.toolCallId) {
2139
+ return {
2140
+ role: "tool",
2141
+ content: msg.content ?? "",
2142
+ tool_call_id: msg.toolCallId
2143
+ };
2144
+ }
1690
2145
  if (msg.role === "tool") {
1691
2146
  return {
1692
2147
  role: "user",
1693
2148
  content: msg.content ?? ""
1694
2149
  };
1695
2150
  }
2151
+ if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
2152
+ return {
2153
+ role: "assistant",
2154
+ content: msg.content || null,
2155
+ tool_calls: msg.toolCalls.map((tc) => ({
2156
+ id: tc.id,
2157
+ type: "function",
2158
+ function: {
2159
+ name: tc.function.name,
2160
+ arguments: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2161
+ }
2162
+ }))
2163
+ };
2164
+ }
1696
2165
  return {
1697
2166
  role: msg.role,
1698
2167
  content: msg.content ?? ""
@@ -1775,24 +2244,848 @@ async function ${this.name}(task: string): Promise<string> { ... }
1775
2244
  function agentAsTool(agent, options) {
1776
2245
  return new AgentTool({ agent, ...options });
1777
2246
  }
2247
+
2248
+ // src/tools/ReadFileTool.ts
2249
+ import * as fs3 from "fs";
2250
+ import * as path3 from "path";
2251
+ var ReadFileTool = class extends Tool {
2252
+ name = "read_file";
2253
+ description = "Read the contents of a file at the specified path. Returns the file content as a string.";
2254
+ inputs = {
2255
+ path: {
2256
+ type: "string",
2257
+ description: "The file path to read (absolute or relative to working directory)",
2258
+ required: true
2259
+ },
2260
+ encoding: {
2261
+ type: "string",
2262
+ description: "File encoding (default: utf-8)",
2263
+ required: false,
2264
+ default: "utf-8"
2265
+ }
2266
+ };
2267
+ outputType = "string";
2268
+ workingDirectory;
2269
+ constructor(config) {
2270
+ super();
2271
+ this.workingDirectory = config?.workingDirectory ?? process.cwd();
2272
+ }
2273
+ async execute(args) {
2274
+ const filePath = args.path;
2275
+ const encoding = args.encoding ?? "utf-8";
2276
+ const resolvedPath = path3.isAbsolute(filePath) ? filePath : path3.resolve(this.workingDirectory, filePath);
2277
+ if (!fs3.existsSync(resolvedPath)) {
2278
+ throw new Error(`File not found: ${resolvedPath}`);
2279
+ }
2280
+ const stat = fs3.statSync(resolvedPath);
2281
+ if (stat.isDirectory()) {
2282
+ throw new Error(`Path is a directory, not a file: ${resolvedPath}`);
2283
+ }
2284
+ const content = fs3.readFileSync(resolvedPath, encoding);
2285
+ const maxLength = 1e5;
2286
+ if (content.length > maxLength) {
2287
+ return content.slice(0, maxLength) + `
2288
+
2289
+ [... truncated, file is ${content.length} characters total]`;
2290
+ }
2291
+ return content;
2292
+ }
2293
+ };
2294
+
2295
+ // src/tools/WriteFileTool.ts
2296
+ import * as fs4 from "fs";
2297
+ import * as path4 from "path";
2298
+ var WriteFileTool = class extends Tool {
2299
+ name = "write_file";
2300
+ 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.";
2301
+ inputs = {
2302
+ path: {
2303
+ type: "string",
2304
+ description: "The file path to write to (absolute or relative to working directory)",
2305
+ required: true
2306
+ },
2307
+ content: {
2308
+ type: "string",
2309
+ description: "The content to write to the file",
2310
+ required: true
2311
+ },
2312
+ append: {
2313
+ type: "boolean",
2314
+ description: "If true, append to the file instead of overwriting (default: false)",
2315
+ required: false,
2316
+ default: false
2317
+ }
2318
+ };
2319
+ outputType = "string";
2320
+ workingDirectory;
2321
+ constructor(config) {
2322
+ super();
2323
+ this.workingDirectory = config?.workingDirectory ?? process.cwd();
2324
+ }
2325
+ async execute(args) {
2326
+ const filePath = args.path;
2327
+ const content = args.content;
2328
+ const append = args.append ?? false;
2329
+ const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(this.workingDirectory, filePath);
2330
+ const dir = path4.dirname(resolvedPath);
2331
+ if (!fs4.existsSync(dir)) {
2332
+ fs4.mkdirSync(dir, { recursive: true });
2333
+ }
2334
+ if (append) {
2335
+ fs4.appendFileSync(resolvedPath, content, "utf-8");
2336
+ return `Successfully appended ${content.length} characters to ${resolvedPath}`;
2337
+ } else {
2338
+ fs4.writeFileSync(resolvedPath, content, "utf-8");
2339
+ return `Successfully wrote ${content.length} characters to ${resolvedPath}`;
2340
+ }
2341
+ }
2342
+ };
2343
+
2344
+ // src/tools/CurlTool.ts
2345
+ var CurlTool = class extends Tool {
2346
+ name = "curl";
2347
+ description = "Make HTTP requests to any URL. Supports GET and POST methods with custom headers and body. Returns the response body as text.";
2348
+ inputs = {
2349
+ url: {
2350
+ type: "string",
2351
+ description: "The URL to request",
2352
+ required: true
2353
+ },
2354
+ method: {
2355
+ type: "string",
2356
+ description: "HTTP method: GET or POST (default: GET)",
2357
+ required: false,
2358
+ default: "GET",
2359
+ enum: ["GET", "POST"]
2360
+ },
2361
+ headers: {
2362
+ type: "object",
2363
+ description: 'Optional HTTP headers as key-value pairs (e.g., {"Content-Type": "application/json"})',
2364
+ required: false
2365
+ },
2366
+ body: {
2367
+ type: "string",
2368
+ description: "Request body for POST requests (typically JSON string)",
2369
+ required: false
2370
+ }
2371
+ };
2372
+ outputType = "string";
2373
+ timeout;
2374
+ constructor(config) {
2375
+ super();
2376
+ this.timeout = config?.timeout ?? 3e4;
2377
+ }
2378
+ async execute(args) {
2379
+ const url = args.url;
2380
+ const method = (args.method ?? "GET").toUpperCase();
2381
+ const headers = args.headers ?? {};
2382
+ const body = args.body;
2383
+ const controller = new AbortController();
2384
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2385
+ try {
2386
+ const fetchOptions = {
2387
+ method,
2388
+ headers,
2389
+ signal: controller.signal
2390
+ };
2391
+ if (method === "POST" && body) {
2392
+ fetchOptions.body = body;
2393
+ if (!headers["Content-Type"] && !headers["content-type"]) {
2394
+ fetchOptions.headers["Content-Type"] = "application/json";
2395
+ }
2396
+ }
2397
+ const response = await fetch(url, fetchOptions);
2398
+ const responseText = await response.text();
2399
+ const statusLine = `HTTP ${response.status} ${response.statusText}`;
2400
+ const maxLength = 5e4;
2401
+ const truncatedBody = responseText.length > maxLength ? responseText.slice(0, maxLength) + `
2402
+
2403
+ [... truncated, response is ${responseText.length} characters total]` : responseText;
2404
+ return `${statusLine}
2405
+
2406
+ ${truncatedBody}`;
2407
+ } catch (error) {
2408
+ if (error.name === "AbortError") {
2409
+ throw new Error(`Request timed out after ${this.timeout}ms: ${url}`);
2410
+ }
2411
+ throw new Error(`HTTP request failed: ${error.message}`);
2412
+ } finally {
2413
+ clearTimeout(timeoutId);
2414
+ }
2415
+ }
2416
+ };
2417
+
2418
+ // src/tools/ExaSearchTool.ts
2419
+ var ExaSearchTool = class extends Tool {
2420
+ name = "exa_search";
2421
+ 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.";
2422
+ inputs = {
2423
+ query: {
2424
+ type: "string",
2425
+ description: "The search query. Be specific and descriptive for best results.",
2426
+ required: true
2427
+ },
2428
+ numResults: {
2429
+ type: "number",
2430
+ description: "Number of results to return (default: 10, max: 30)",
2431
+ required: false,
2432
+ default: 10
2433
+ },
2434
+ type: {
2435
+ type: "string",
2436
+ description: 'Search type: "auto" (default), "neural" (embeddings-based), or "keyword"',
2437
+ required: false,
2438
+ default: "auto",
2439
+ enum: ["auto", "neural", "keyword"]
2440
+ },
2441
+ category: {
2442
+ type: "string",
2443
+ description: 'Optional category filter: "research paper", "news", "pdf", "github", "tweet", "company", "blog"',
2444
+ required: false
2445
+ },
2446
+ includeDomains: {
2447
+ type: "array",
2448
+ description: 'Only include results from these domains (e.g., ["arxiv.org", "github.com"])',
2449
+ required: false
2450
+ },
2451
+ excludeDomains: {
2452
+ type: "array",
2453
+ description: "Exclude results from these domains",
2454
+ required: false
2455
+ },
2456
+ startPublishedDate: {
2457
+ type: "string",
2458
+ description: 'Filter results published after this ISO 8601 date (e.g., "2024-01-01")',
2459
+ required: false
2460
+ }
2461
+ };
2462
+ outputType = "string";
2463
+ apiKey;
2464
+ constructor(config) {
2465
+ super();
2466
+ this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
2467
+ }
2468
+ async setup() {
2469
+ if (!this.apiKey) {
2470
+ throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
2471
+ }
2472
+ this.isSetup = true;
2473
+ }
2474
+ async execute(args) {
2475
+ const query = args.query;
2476
+ const numResults = Math.min(args.numResults ?? 10, 30);
2477
+ const type = args.type ?? "auto";
2478
+ const requestBody = {
2479
+ query,
2480
+ numResults,
2481
+ type,
2482
+ text: { maxCharacters: 1e3 }
2483
+ };
2484
+ if (args.category) requestBody.category = args.category;
2485
+ if (args.includeDomains) requestBody.includeDomains = args.includeDomains;
2486
+ if (args.excludeDomains) requestBody.excludeDomains = args.excludeDomains;
2487
+ if (args.startPublishedDate) requestBody.startPublishedDate = args.startPublishedDate;
2488
+ const response = await fetch("https://api.exa.ai/search", {
2489
+ method: "POST",
2490
+ headers: {
2491
+ "x-api-key": this.apiKey,
2492
+ "Content-Type": "application/json"
2493
+ },
2494
+ body: JSON.stringify(requestBody)
2495
+ });
2496
+ if (!response.ok) {
2497
+ const errorText = await response.text();
2498
+ throw new Error(`Exa search failed (${response.status}): ${errorText}`);
2499
+ }
2500
+ const data = await response.json();
2501
+ if (!data.results || data.results.length === 0) {
2502
+ return "No results found for the query.";
2503
+ }
2504
+ const formattedResults = data.results.map((result, i) => {
2505
+ const parts = [`[${i + 1}] ${result.title ?? "Untitled"}`];
2506
+ parts.push(` URL: ${result.url}`);
2507
+ if (result.publishedDate) parts.push(` Date: ${result.publishedDate}`);
2508
+ if (result.author) parts.push(` Author: ${result.author}`);
2509
+ if (result.text) {
2510
+ const snippet = result.text.slice(0, 300).trim();
2511
+ parts.push(` Snippet: ${snippet}${result.text.length > 300 ? "..." : ""}`);
2512
+ }
2513
+ return parts.join("\n");
2514
+ }).join("\n\n");
2515
+ return `Search results for "${query}" (${data.results.length} results):
2516
+
2517
+ ${formattedResults}`;
2518
+ }
2519
+ };
2520
+
2521
+ // src/tools/ExaGetContentsTool.ts
2522
+ var ExaGetContentsTool = class extends Tool {
2523
+ name = "exa_get_contents";
2524
+ 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.";
2525
+ inputs = {
2526
+ urls: {
2527
+ type: "array",
2528
+ description: "Array of URLs to fetch content from (max 10)",
2529
+ required: true
2530
+ },
2531
+ maxCharacters: {
2532
+ type: "number",
2533
+ description: "Maximum characters of content to return per page (default: 10000)",
2534
+ required: false,
2535
+ default: 1e4
2536
+ },
2537
+ livecrawl: {
2538
+ type: "string",
2539
+ description: 'Crawl strategy: "fallback" (use cache, fetch live if unavailable), "always" (always fetch live), "never" (cache only). Default: "fallback"',
2540
+ required: false,
2541
+ default: "fallback",
2542
+ enum: ["fallback", "always", "never"]
2543
+ }
2544
+ };
2545
+ outputType = "string";
2546
+ apiKey;
2547
+ constructor(config) {
2548
+ super();
2549
+ this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
2550
+ }
2551
+ async setup() {
2552
+ if (!this.apiKey) {
2553
+ throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
2554
+ }
2555
+ this.isSetup = true;
2556
+ }
2557
+ async execute(args) {
2558
+ const urls = args.urls.slice(0, 10);
2559
+ const maxCharacters = args.maxCharacters ?? 1e4;
2560
+ const livecrawl = args.livecrawl ?? "fallback";
2561
+ const requestBody = {
2562
+ urls,
2563
+ text: { maxCharacters },
2564
+ livecrawl
2565
+ };
2566
+ const response = await fetch("https://api.exa.ai/contents", {
2567
+ method: "POST",
2568
+ headers: {
2569
+ "x-api-key": this.apiKey,
2570
+ "Content-Type": "application/json"
2571
+ },
2572
+ body: JSON.stringify(requestBody)
2573
+ });
2574
+ if (!response.ok) {
2575
+ const errorText = await response.text();
2576
+ throw new Error(`Exa get contents failed (${response.status}): ${errorText}`);
2577
+ }
2578
+ const data = await response.json();
2579
+ if (!data.results || data.results.length === 0) {
2580
+ return "No content could be retrieved from the provided URLs.";
2581
+ }
2582
+ const formattedResults = data.results.map((result) => {
2583
+ const parts = [`## ${result.title ?? result.url}`];
2584
+ parts.push(`URL: ${result.url}`);
2585
+ if (result.author) parts.push(`Author: ${result.author}`);
2586
+ if (result.publishedDate) parts.push(`Date: ${result.publishedDate}`);
2587
+ parts.push("");
2588
+ if (result.text) {
2589
+ parts.push(result.text);
2590
+ } else {
2591
+ parts.push("[No text content available]");
2592
+ }
2593
+ return parts.join("\n");
2594
+ }).join("\n\n---\n\n");
2595
+ return formattedResults;
2596
+ }
2597
+ };
2598
+
2599
+ // src/tools/ExaResearchTool.ts
2600
+ var ExaResearchTool = class extends Tool {
2601
+ name = "exa_research";
2602
+ 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.";
2603
+ inputs = {
2604
+ topic: {
2605
+ type: "string",
2606
+ description: "The research topic or question to investigate",
2607
+ required: true
2608
+ },
2609
+ numSources: {
2610
+ type: "number",
2611
+ description: "Number of primary sources to retrieve (default: 5, max: 10)",
2612
+ required: false,
2613
+ default: 5
2614
+ },
2615
+ category: {
2616
+ type: "string",
2617
+ description: 'Optional category: "research paper", "news", "blog", "company"',
2618
+ required: false
2619
+ },
2620
+ includeDomains: {
2621
+ type: "array",
2622
+ description: "Only include results from these domains",
2623
+ required: false
2624
+ },
2625
+ startPublishedDate: {
2626
+ type: "string",
2627
+ description: "Only include results published after this date (ISO 8601)",
2628
+ required: false
2629
+ }
2630
+ };
2631
+ outputType = "string";
2632
+ apiKey;
2633
+ constructor(config) {
2634
+ super();
2635
+ this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
2636
+ }
2637
+ async setup() {
2638
+ if (!this.apiKey) {
2639
+ throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
2640
+ }
2641
+ this.isSetup = true;
2642
+ }
2643
+ async execute(args) {
2644
+ const topic = args.topic;
2645
+ const numSources = Math.min(args.numSources ?? 5, 10);
2646
+ const searchBody = {
2647
+ query: topic,
2648
+ numResults: numSources,
2649
+ type: "auto",
2650
+ text: { maxCharacters: 3e3 }
2651
+ };
2652
+ if (args.category) searchBody.category = args.category;
2653
+ if (args.includeDomains) searchBody.includeDomains = args.includeDomains;
2654
+ if (args.startPublishedDate) searchBody.startPublishedDate = args.startPublishedDate;
2655
+ const searchResponse = await fetch("https://api.exa.ai/search", {
2656
+ method: "POST",
2657
+ headers: {
2658
+ "x-api-key": this.apiKey,
2659
+ "Content-Type": "application/json"
2660
+ },
2661
+ body: JSON.stringify(searchBody)
2662
+ });
2663
+ if (!searchResponse.ok) {
2664
+ const errorText = await searchResponse.text();
2665
+ throw new Error(`Exa research search failed (${searchResponse.status}): ${errorText}`);
2666
+ }
2667
+ const searchData = await searchResponse.json();
2668
+ if (!searchData.results || searchData.results.length === 0) {
2669
+ return `No research sources found for topic: "${topic}"`;
2670
+ }
2671
+ let similarResults = [];
2672
+ if (searchData.results.length > 0) {
2673
+ try {
2674
+ const similarBody = {
2675
+ url: searchData.results[0].url,
2676
+ numResults: 3,
2677
+ text: { maxCharacters: 2e3 }
2678
+ };
2679
+ const similarResponse = await fetch("https://api.exa.ai/findSimilar", {
2680
+ method: "POST",
2681
+ headers: {
2682
+ "x-api-key": this.apiKey,
2683
+ "Content-Type": "application/json"
2684
+ },
2685
+ body: JSON.stringify(similarBody)
2686
+ });
2687
+ if (similarResponse.ok) {
2688
+ const similarData = await similarResponse.json();
2689
+ similarResults = similarData.results ?? [];
2690
+ }
2691
+ } catch {
2692
+ }
2693
+ }
2694
+ const allSources = [...searchData.results, ...similarResults];
2695
+ const seenUrls = /* @__PURE__ */ new Set();
2696
+ const uniqueSources = allSources.filter((s) => {
2697
+ if (seenUrls.has(s.url)) return false;
2698
+ seenUrls.add(s.url);
2699
+ return true;
2700
+ });
2701
+ const sections = [];
2702
+ sections.push(`# Research: ${topic}
2703
+ `);
2704
+ sections.push(`Found ${uniqueSources.length} sources.
2705
+ `);
2706
+ sections.push("## Key Sources\n");
2707
+ for (let i = 0; i < uniqueSources.length; i++) {
2708
+ const source = uniqueSources[i];
2709
+ sections.push(`### ${i + 1}. ${source.title ?? "Untitled"}`);
2710
+ sections.push(`URL: ${source.url}`);
2711
+ if ("publishedDate" in source && source.publishedDate) {
2712
+ sections.push(`Date: ${source.publishedDate}`);
2713
+ }
2714
+ if ("author" in source && source.author) {
2715
+ sections.push(`Author: ${source.author}`);
2716
+ }
2717
+ if (source.text) {
2718
+ sections.push(`
2719
+ Content:
2720
+ ${source.text.slice(0, 2e3)}`);
2721
+ }
2722
+ sections.push("");
2723
+ }
2724
+ sections.push("## Source URLs\n");
2725
+ uniqueSources.forEach((s, i) => {
2726
+ sections.push(`${i + 1}. ${s.url}`);
2727
+ });
2728
+ return sections.join("\n");
2729
+ }
2730
+ };
2731
+
2732
+ // src/orchestrator/YAMLLoader.ts
2733
+ import * as fs5 from "fs";
2734
+ import * as path5 from "path";
2735
+ import YAML from "yaml";
2736
+ var TOOL_REGISTRY = {
2737
+ read_file: ReadFileTool,
2738
+ write_file: WriteFileTool,
2739
+ curl: CurlTool,
2740
+ exa_search: ExaSearchTool,
2741
+ exa_get_contents: ExaGetContentsTool,
2742
+ exa_research: ExaResearchTool,
2743
+ final_answer: FinalAnswerTool
2744
+ };
2745
+ var YAMLLoader = class {
2746
+ customTools = /* @__PURE__ */ new Map();
2747
+ /**
2748
+ * Register a custom tool type for use in YAML definitions.
2749
+ */
2750
+ registerToolType(typeName, toolClass) {
2751
+ this.customTools.set(typeName, toolClass);
2752
+ }
2753
+ /**
2754
+ * Load a workflow from a YAML file path.
2755
+ */
2756
+ loadFromFile(filePath) {
2757
+ const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
2758
+ if (!fs5.existsSync(absolutePath)) {
2759
+ throw new Error(`Workflow file not found: ${absolutePath}`);
2760
+ }
2761
+ const content = fs5.readFileSync(absolutePath, "utf-8");
2762
+ return this.loadFromString(content);
2763
+ }
2764
+ /**
2765
+ * Load a workflow from a YAML string.
2766
+ */
2767
+ loadFromString(yamlContent) {
2768
+ const definition = YAML.parse(yamlContent);
2769
+ return this.buildWorkflow(definition);
2770
+ }
2771
+ /**
2772
+ * Build a runnable workflow from a parsed definition.
2773
+ */
2774
+ buildWorkflow(definition) {
2775
+ if (!definition.name) {
2776
+ throw new Error("Workflow must have a name");
2777
+ }
2778
+ if (!definition.entrypoint) {
2779
+ throw new Error("Workflow must have an entrypoint agent");
2780
+ }
2781
+ if (!definition.agents) {
2782
+ throw new Error("Workflow must define at least one agent");
2783
+ }
2784
+ const tools = /* @__PURE__ */ new Map();
2785
+ if (definition.tools) {
2786
+ for (const [name, toolDef] of Object.entries(definition.tools)) {
2787
+ const tool = this.buildTool(name, toolDef.type, toolDef.config);
2788
+ tools.set(name, tool);
2789
+ }
2790
+ }
2791
+ const agents = /* @__PURE__ */ new Map();
2792
+ const agentDefs = definition.agents;
2793
+ const resolved = /* @__PURE__ */ new Set();
2794
+ const maxIterations = Object.keys(agentDefs).length * 2;
2795
+ let iterations = 0;
2796
+ while (resolved.size < Object.keys(agentDefs).length && iterations < maxIterations) {
2797
+ iterations++;
2798
+ for (const [agentName, agentDef] of Object.entries(agentDefs)) {
2799
+ if (resolved.has(agentName)) continue;
2800
+ const agentDeps = agentDef.agents ?? [];
2801
+ const allDepsResolved = agentDeps.every((dep) => resolved.has(dep));
2802
+ if (allDepsResolved) {
2803
+ const agent = this.buildAgent(
2804
+ agentName,
2805
+ agentDef,
2806
+ definition.model,
2807
+ tools,
2808
+ agents,
2809
+ definition.globalMaxContextLength
2810
+ );
2811
+ agents.set(agentName, agent);
2812
+ resolved.add(agentName);
2813
+ }
2814
+ }
2815
+ }
2816
+ if (resolved.size < Object.keys(agentDefs).length) {
2817
+ const unresolved = Object.keys(agentDefs).filter((n) => !resolved.has(n));
2818
+ throw new Error(`Circular or unresolvable agent dependencies: ${unresolved.join(", ")}`);
2819
+ }
2820
+ const entrypointAgent = agents.get(definition.entrypoint);
2821
+ if (!entrypointAgent) {
2822
+ throw new Error(`Entrypoint agent "${definition.entrypoint}" not found in agents`);
2823
+ }
2824
+ return {
2825
+ name: definition.name,
2826
+ description: definition.description,
2827
+ entrypointAgent,
2828
+ agents,
2829
+ tools
2830
+ };
2831
+ }
2832
+ /**
2833
+ * Build a tool instance from a type name and config.
2834
+ */
2835
+ buildTool(name, type, config) {
2836
+ const ToolClass = TOOL_REGISTRY[type] ?? this.customTools.get(type);
2837
+ if (!ToolClass) {
2838
+ throw new Error(`Unknown tool type: ${type}. Available types: ${[...Object.keys(TOOL_REGISTRY), ...this.customTools.keys()].join(", ")}`);
2839
+ }
2840
+ const tool = new ToolClass(config);
2841
+ if (name !== type && name !== tool.name) {
2842
+ Object.defineProperty(tool, "name", { value: name, writable: false });
2843
+ }
2844
+ return tool;
2845
+ }
2846
+ /**
2847
+ * Build an agent instance from a YAML definition.
2848
+ */
2849
+ buildAgent(name, definition, globalModel, availableTools, resolvedAgents, globalMaxContextLength) {
2850
+ const modelConfig = definition.model ?? globalModel;
2851
+ const model = new OpenAIModel({
2852
+ modelId: modelConfig?.modelId,
2853
+ apiKey: modelConfig?.apiKey,
2854
+ baseUrl: modelConfig?.baseUrl,
2855
+ maxTokens: definition.maxTokens ?? modelConfig?.maxTokens,
2856
+ temperature: definition.temperature ?? modelConfig?.temperature,
2857
+ timeout: modelConfig?.timeout
2858
+ });
2859
+ const agentTools = [];
2860
+ if (definition.tools && availableTools) {
2861
+ for (const toolName of definition.tools) {
2862
+ const tool = availableTools.get(toolName);
2863
+ if (tool) {
2864
+ agentTools.push(tool);
2865
+ } else {
2866
+ const ToolClass = TOOL_REGISTRY[toolName] ?? this.customTools.get(toolName);
2867
+ if (ToolClass) {
2868
+ agentTools.push(new ToolClass());
2869
+ } else {
2870
+ throw new Error(`Tool "${toolName}" not found for agent "${name}"`);
2871
+ }
2872
+ }
2873
+ }
2874
+ }
2875
+ if (definition.agents && resolvedAgents) {
2876
+ for (const subAgentName of definition.agents) {
2877
+ const subAgent = resolvedAgents.get(subAgentName);
2878
+ if (!subAgent) {
2879
+ throw new Error(`Sub-agent "${subAgentName}" not found for agent "${name}"`);
2880
+ }
2881
+ agentTools.push(new AgentTool({
2882
+ agent: subAgent,
2883
+ name: subAgentName,
2884
+ description: definition.description ? `Sub-agent: ${subAgentName}` : `Delegate tasks to the ${subAgentName} agent`
2885
+ }));
2886
+ }
2887
+ }
2888
+ const maxContextLength = definition.maxContextLength ?? globalMaxContextLength;
2889
+ if (definition.type === "CodeAgent") {
2890
+ return new CodeAgent({
2891
+ model,
2892
+ tools: agentTools,
2893
+ maxSteps: definition.maxSteps,
2894
+ customInstructions: definition.customInstructions,
2895
+ persistent: definition.persistent,
2896
+ maxContextLength,
2897
+ memoryStrategy: definition.memoryStrategy,
2898
+ maxTokens: definition.maxTokens,
2899
+ temperature: definition.temperature,
2900
+ name
2901
+ });
2902
+ } else {
2903
+ return new ToolUseAgent({
2904
+ model,
2905
+ tools: agentTools,
2906
+ maxSteps: definition.maxSteps,
2907
+ customInstructions: definition.customInstructions,
2908
+ persistent: definition.persistent,
2909
+ maxContextLength,
2910
+ memoryStrategy: definition.memoryStrategy,
2911
+ maxTokens: definition.maxTokens,
2912
+ temperature: definition.temperature,
2913
+ name
2914
+ });
2915
+ }
2916
+ }
2917
+ };
2918
+
2919
+ // src/orchestrator/Orchestrator.ts
2920
+ import chalk2 from "chalk";
2921
+ var Orchestrator = class {
2922
+ loader;
2923
+ config;
2924
+ activeAgents = /* @__PURE__ */ new Map();
2925
+ eventLog = [];
2926
+ constructor(config = {}) {
2927
+ this.loader = new YAMLLoader();
2928
+ this.config = {
2929
+ verbose: config.verbose ?? true,
2930
+ onEvent: config.onEvent
2931
+ };
2932
+ }
2933
+ /**
2934
+ * Load a workflow from a YAML file.
2935
+ */
2936
+ loadWorkflow(filePath) {
2937
+ const workflow = this.loader.loadFromFile(filePath);
2938
+ this.displayWorkflowInfo(workflow);
2939
+ return workflow;
2940
+ }
2941
+ /**
2942
+ * Load a workflow from YAML string.
2943
+ */
2944
+ loadWorkflowFromString(yamlContent) {
2945
+ const workflow = this.loader.loadFromString(yamlContent);
2946
+ this.displayWorkflowInfo(workflow);
2947
+ return workflow;
2948
+ }
2949
+ /**
2950
+ * Run a loaded workflow with a task.
2951
+ */
2952
+ async runWorkflow(workflow, task) {
2953
+ this.displayRunStart(workflow.name, task);
2954
+ this.instrumentAgent(workflow.entrypointAgent, workflow.entrypointAgent.getName(), 0);
2955
+ for (const [name, agent] of workflow.agents) {
2956
+ if (agent !== workflow.entrypointAgent) {
2957
+ this.instrumentAgent(agent, name, 1);
2958
+ }
2959
+ }
2960
+ try {
2961
+ const result = await workflow.entrypointAgent.run(task);
2962
+ this.displayRunEnd(result);
2963
+ return result;
2964
+ } catch (error) {
2965
+ this.displayError(error);
2966
+ throw error;
2967
+ }
2968
+ }
2969
+ /**
2970
+ * Run a standalone agent with a task.
2971
+ */
2972
+ async runAgent(agent, task) {
2973
+ this.instrumentAgent(agent, agent.getName(), 0);
2974
+ const result = await agent.run(task);
2975
+ return result;
2976
+ }
2977
+ /**
2978
+ * Instrument an agent with orchestrator event tracking.
2979
+ */
2980
+ instrumentAgent(agent, name, depth) {
2981
+ this.activeAgents.set(name, { agent, depth });
2982
+ }
2983
+ /**
2984
+ * Display workflow info at startup.
2985
+ */
2986
+ displayWorkflowInfo(workflow) {
2987
+ if (!this.config.verbose) return;
2988
+ const line = "\u2550".repeat(70);
2989
+ console.log(chalk2.cyan(line));
2990
+ console.log(chalk2.cyan.bold(` Workflow: ${workflow.name}`));
2991
+ if (workflow.description) {
2992
+ console.log(chalk2.cyan(` ${workflow.description}`));
2993
+ }
2994
+ console.log(chalk2.cyan(` Agents: ${Array.from(workflow.agents.keys()).join(", ")}`));
2995
+ console.log(chalk2.cyan(` Tools: ${Array.from(workflow.tools.keys()).join(", ") || "(none defined at workflow level)"}`));
2996
+ console.log(chalk2.cyan(` Entrypoint: ${workflow.entrypointAgent.getName()}`));
2997
+ console.log(chalk2.cyan(line));
2998
+ console.log();
2999
+ }
3000
+ /**
3001
+ * Display run start info.
3002
+ */
3003
+ displayRunStart(workflowName, task) {
3004
+ if (!this.config.verbose) return;
3005
+ console.log(chalk2.green.bold(`
3006
+ \u25B6 Running workflow "${workflowName}"`));
3007
+ console.log(chalk2.green(` Task: ${task}`));
3008
+ console.log(chalk2.gray("\u2500".repeat(70)));
3009
+ }
3010
+ /**
3011
+ * Display run completion info.
3012
+ */
3013
+ displayRunEnd(result) {
3014
+ if (!this.config.verbose) return;
3015
+ console.log(chalk2.gray("\n" + "\u2500".repeat(70)));
3016
+ console.log(chalk2.green.bold(`
3017
+ \u2705 Workflow complete`));
3018
+ console.log(chalk2.green(` Duration: ${(result.duration / 1e3).toFixed(2)}s`));
3019
+ console.log(chalk2.green(` Tokens: ${result.tokenUsage.totalTokens}`));
3020
+ console.log(chalk2.green(` Steps: ${result.steps.length}`));
3021
+ const outputStr = typeof result.output === "string" ? result.output : JSON.stringify(result.output, null, 2);
3022
+ console.log(chalk2.magenta.bold("\n Final Output:"));
3023
+ const indentedOutput = outputStr.split("\n").map((line) => ` ${line}`).join("\n");
3024
+ console.log(chalk2.magenta(indentedOutput));
3025
+ console.log();
3026
+ }
3027
+ /**
3028
+ * Display an error.
3029
+ */
3030
+ displayError(error) {
3031
+ if (!this.config.verbose) return;
3032
+ console.error(chalk2.red.bold(`
3033
+ \u274C Workflow failed: ${error.message}`));
3034
+ if (error.stack) {
3035
+ console.error(chalk2.red.dim(error.stack));
3036
+ }
3037
+ }
3038
+ /**
3039
+ * Log an orchestration event.
3040
+ */
3041
+ logEvent(event) {
3042
+ this.eventLog.push(event);
3043
+ if (this.config.onEvent) {
3044
+ this.config.onEvent(event);
3045
+ }
3046
+ }
3047
+ /**
3048
+ * Get the event log.
3049
+ */
3050
+ getEventLog() {
3051
+ return [...this.eventLog];
3052
+ }
3053
+ /**
3054
+ * Get the YAML loader for registering custom tools.
3055
+ */
3056
+ getLoader() {
3057
+ return this.loader;
3058
+ }
3059
+ };
1778
3060
  export {
1779
3061
  Agent,
1780
3062
  AgentLogger,
1781
3063
  AgentMemory,
1782
3064
  AgentTool,
1783
3065
  CodeAgent,
3066
+ CurlTool,
3067
+ ExaGetContentsTool,
3068
+ ExaResearchTool,
3069
+ ExaSearchTool,
1784
3070
  FINAL_ANSWER_PROMPT,
1785
3071
  FinalAnswerTool,
1786
3072
  LocalExecutor,
1787
3073
  LogLevel,
1788
3074
  Model,
1789
3075
  OpenAIModel,
3076
+ Orchestrator,
3077
+ ReadFileTool,
1790
3078
  Tool,
3079
+ ToolUseAgent,
1791
3080
  UserInputTool,
3081
+ WriteFileTool,
3082
+ YAMLLoader,
1792
3083
  agentAsTool,
1793
3084
  createTool,
1794
3085
  finalAnswerTool,
3086
+ formatToolDescriptions,
1795
3087
  generateSystemPrompt,
3088
+ generateToolUseSystemPrompt,
1796
3089
  getErrorRecoveryPrompt
1797
3090
  };
1798
3091
  //# sourceMappingURL=index.mjs.map