@samrahimi/smol-js 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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
- constructor(systemPrompt) {
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.modelOutputMessage) {
170
+ if (step.toolCalls && step.toolCalls.length > 0) {
157
171
  messages.push({
158
172
  role: "assistant",
159
- content: step.modelOutputMessage.content
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
- * Get total token usage across all steps.
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
- * Get a summary of the memory for logging.
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
- if (reset || !this.memory) {
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.logger.header(`\u{1F680} Starting Agent: ${task.slice(0, 50)}${task.length > 50 ? "..." : ""}`);
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
- \u23F1\uFE0F Total time: ${(duration / 1e3).toFixed(2)}s`);
606
- this.logger.info(`\u{1F4CA} Total tokens: ${tokenUsage.totalTokens}`);
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
- Summarize what you accomplished and provide a final answer. Call final_answer() with your response.`
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
- * Stop the agent.
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
- * Sleep for a specified duration.
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((resolve2) => setTimeout(resolve2, ms));
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((resolve2) => {
1448
+ return new Promise((resolve5) => {
1285
1449
  rl.question(`
1286
1450
  [Agent asks]: ${question}
1287
1451
  Your response: `, (answer) => {
1288
1452
  rl.close();
1289
- resolve2(answer);
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 DEFAULT_CONFIG = {
1654
- modelId: "anthropic/claude-sonnet-4.5",
1655
- baseUrl: "https://openrouter.ai/api/v1",
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
- ...DEFAULT_CONFIG,
1668
- ...config
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: this.config.baseUrl,
1680
- timeout: this.config.timeout,
1681
- defaultHeaders: this.config.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