@samrahimi/smol-js 0.6.5 → 0.7.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.js CHANGED
@@ -41,11 +41,13 @@ __export(index_exports, {
41
41
  ExaSearchTool: () => ExaSearchTool,
42
42
  FINAL_ANSWER_PROMPT: () => FINAL_ANSWER_PROMPT,
43
43
  FinalAnswerTool: () => FinalAnswerTool,
44
+ JSONOutputHandler: () => JSONOutputHandler,
44
45
  LocalExecutor: () => LocalExecutor,
45
46
  LogLevel: () => LogLevel,
46
47
  Model: () => Model,
47
48
  OpenAIModel: () => OpenAIModel,
48
49
  Orchestrator: () => Orchestrator,
50
+ ProxyTool: () => ProxyTool,
49
51
  ReadFileTool: () => ReadFileTool,
50
52
  Tool: () => Tool,
51
53
  ToolUseAgent: () => ToolUseAgent,
@@ -58,7 +60,9 @@ __export(index_exports, {
58
60
  formatToolDescriptions: () => formatToolDescriptions,
59
61
  generateSystemPrompt: () => generateSystemPrompt,
60
62
  generateToolUseSystemPrompt: () => generateToolUseSystemPrompt,
61
- getErrorRecoveryPrompt: () => getErrorRecoveryPrompt
63
+ getErrorRecoveryPrompt: () => getErrorRecoveryPrompt,
64
+ loadCustomTools: () => loadCustomTools,
65
+ scanCustomTools: () => scanCustomTools
62
66
  });
63
67
  module.exports = __toCommonJS(index_exports);
64
68
 
@@ -774,9 +778,21 @@ Total time: ${(duration / 1e3).toFixed(2)}s`);
774
778
  getName() {
775
779
  return this.config.name;
776
780
  }
781
+ /** Get agent type identifier */
782
+ getType() {
783
+ return this.constructor.name;
784
+ }
785
+ /** Get max steps configuration */
786
+ getMaxSteps() {
787
+ return this.config.maxSteps;
788
+ }
789
+ /** Set the event callback (useful for orchestration) */
790
+ setOnEvent(callback) {
791
+ this.config.onEvent = callback;
792
+ }
777
793
  /** Sleep for a specified duration */
778
794
  sleep(ms) {
779
- return new Promise((resolve5) => setTimeout(resolve5, ms));
795
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
780
796
  }
781
797
  };
782
798
 
@@ -1445,12 +1461,12 @@ var UserInputTool = class extends Tool {
1445
1461
  input: process.stdin,
1446
1462
  output: process.stdout
1447
1463
  });
1448
- return new Promise((resolve5) => {
1464
+ return new Promise((resolve6) => {
1449
1465
  rl.question(`
1450
1466
  [Agent asks]: ${question}
1451
1467
  Your response: `, (answer) => {
1452
1468
  rl.close();
1453
- resolve5(answer);
1469
+ resolve6(answer);
1454
1470
  });
1455
1471
  });
1456
1472
  }
@@ -1929,6 +1945,10 @@ Please try a different approach.`
1929
1945
  memoryStep.tokenUsage = response.tokenUsage;
1930
1946
  if (response.content && response.content.trim()) {
1931
1947
  this.logger.reasoning(response.content.trim());
1948
+ this.emitEvent("agent_thinking", {
1949
+ step: this.currentStep,
1950
+ content: response.content.trim()
1951
+ });
1932
1952
  }
1933
1953
  if (!response.toolCalls || response.toolCalls.length === 0) {
1934
1954
  this.logger.warn("No tool calls in response. Prompting model to use tools.");
@@ -1961,42 +1981,84 @@ Please try a different approach.`
1961
1981
  async processToolCalls(toolCalls) {
1962
1982
  const results = [];
1963
1983
  const executeTool = async (tc) => {
1984
+ const startTime = Date.now();
1964
1985
  const toolName = tc.function.name;
1965
1986
  const tool = this.tools.get(toolName);
1966
1987
  if (!tool) {
1988
+ const error = `Unknown tool: ${toolName}. Available tools: ${Array.from(this.tools.keys()).join(", ")}`;
1989
+ this.emitEvent("agent_tool_result", {
1990
+ step: this.currentStep,
1991
+ toolCallId: tc.id,
1992
+ toolName,
1993
+ result: null,
1994
+ error,
1995
+ duration: Date.now() - startTime
1996
+ });
1967
1997
  return {
1968
1998
  toolCallId: tc.id,
1969
1999
  toolName,
1970
2000
  result: null,
1971
- error: `Unknown tool: ${toolName}. Available tools: ${Array.from(this.tools.keys()).join(", ")}`
2001
+ error
1972
2002
  };
1973
2003
  }
1974
2004
  let args;
1975
2005
  try {
1976
2006
  args = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
1977
2007
  } catch {
2008
+ const error = `Failed to parse tool arguments: ${tc.function.arguments}`;
2009
+ this.emitEvent("agent_tool_result", {
2010
+ step: this.currentStep,
2011
+ toolCallId: tc.id,
2012
+ toolName,
2013
+ result: null,
2014
+ error,
2015
+ duration: Date.now() - startTime
2016
+ });
1978
2017
  return {
1979
2018
  toolCallId: tc.id,
1980
2019
  toolName,
1981
2020
  result: null,
1982
- error: `Failed to parse tool arguments: ${tc.function.arguments}`
2021
+ error
1983
2022
  };
1984
2023
  }
1985
2024
  this.logger.info(` Calling tool: ${toolName}(${JSON.stringify(args).slice(0, 100)}...)`);
1986
- this.emitEvent("agent_tool_call", { tool: toolName, args });
2025
+ this.emitEvent("agent_tool_call", {
2026
+ step: this.currentStep,
2027
+ toolCallId: tc.id,
2028
+ toolName,
2029
+ arguments: args
2030
+ });
1987
2031
  try {
1988
2032
  const result = await tool.call(args);
2033
+ const duration = Date.now() - startTime;
2034
+ this.emitEvent("agent_tool_result", {
2035
+ step: this.currentStep,
2036
+ toolCallId: tc.id,
2037
+ toolName,
2038
+ result,
2039
+ duration
2040
+ });
1989
2041
  return {
1990
2042
  toolCallId: tc.id,
1991
2043
  toolName,
1992
2044
  result
1993
2045
  };
1994
2046
  } catch (error) {
2047
+ const errorMsg = `Tool execution error: ${error.message}`;
2048
+ const duration = Date.now() - startTime;
2049
+ this.emitEvent("agent_tool_result", {
2050
+ step: this.currentStep,
2051
+ toolCallId: tc.id,
2052
+ toolName,
2053
+ result: null,
2054
+ error: errorMsg,
2055
+ duration
2056
+ });
1995
2057
  return {
1996
2058
  toolCallId: tc.id,
1997
2059
  toolName,
1998
2060
  result: null,
1999
- error: `Tool execution error: ${error.message}`
2061
+ error: errorMsg
2000
2062
  };
2001
2063
  }
2002
2064
  };
@@ -2074,7 +2136,7 @@ var Model = class {
2074
2136
  };
2075
2137
 
2076
2138
  // src/models/OpenAIModel.ts
2077
- var import_openai = __toESM(require("openai"));
2139
+ var import_openai = __toESM(require("openai/index.mjs"));
2078
2140
  var DEFAULT_MODEL_ID = "anthropic/claude-sonnet-4.5";
2079
2141
  var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
2080
2142
  var DEFAULT_TIMEOUT = 12e4;
@@ -2663,40 +2725,35 @@ var ExaGetContentsTool = class extends Tool {
2663
2725
  // src/tools/ExaResearchTool.ts
2664
2726
  var ExaResearchTool = class extends Tool {
2665
2727
  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.";
2728
+ description = "Perform comprehensive web research using Exa.ai's agentic research system. Provide natural-language instructions about what to research, and the AI research agent will plan searches, gather sources, extract facts, and synthesize findings into a detailed markdown report with citations. Ideal for deep research on any topic. Results typically complete in 20-90 seconds.";
2667
2729
  inputs = {
2668
- topic: {
2730
+ instructions: {
2669
2731
  type: "string",
2670
- description: "The research topic or question to investigate",
2732
+ description: "Natural-language task description of what to research (max 4096 characters). Be clear about what information you want, how research should be conducted, and what the output should contain.",
2671
2733
  required: true
2672
2734
  },
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: {
2735
+ model: {
2680
2736
  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",
2737
+ description: 'Research model: "exa-research-fast" (faster, cheaper), "exa-research" (balanced, default), or "exa-research-pro" (most thorough)',
2687
2738
  required: false
2688
2739
  },
2689
- startPublishedDate: {
2690
- type: "string",
2691
- description: "Only include results published after this date (ISO 8601)",
2740
+ outputSchema: {
2741
+ type: "object",
2742
+ description: "Optional JSON Schema to enforce structured output format (max 8 root fields, 5 levels deep). If omitted, returns markdown report.",
2692
2743
  required: false
2693
2744
  }
2694
2745
  };
2695
2746
  outputType = "string";
2696
2747
  apiKey;
2748
+ defaultModel;
2749
+ pollInterval;
2750
+ maxPollTime;
2697
2751
  constructor(config) {
2698
2752
  super();
2699
2753
  this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
2754
+ this.defaultModel = config?.model ?? "exa-research";
2755
+ this.pollInterval = config?.pollInterval ?? 2e3;
2756
+ this.maxPollTime = config?.maxPollTime ?? 3e5;
2700
2757
  }
2701
2758
  async setup() {
2702
2759
  if (!this.apiKey) {
@@ -2705,97 +2762,386 @@ var ExaResearchTool = class extends Tool {
2705
2762
  this.isSetup = true;
2706
2763
  }
2707
2764
  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 }
2765
+ const instructions = args.instructions;
2766
+ const model = args.model ?? this.defaultModel;
2767
+ const outputSchema = args.outputSchema;
2768
+ if (!instructions || instructions.length > 4096) {
2769
+ throw new Error("Instructions are required and must be <= 4096 characters");
2770
+ }
2771
+ const createBody = {
2772
+ instructions,
2773
+ model
2715
2774
  };
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", {
2775
+ if (outputSchema) {
2776
+ createBody.outputSchema = outputSchema;
2777
+ }
2778
+ const createResponse = await fetch("https://api.exa.ai/research/v1", {
2720
2779
  method: "POST",
2721
2780
  headers: {
2722
2781
  "x-api-key": this.apiKey,
2723
2782
  "Content-Type": "application/json"
2724
2783
  },
2725
- body: JSON.stringify(searchBody)
2784
+ body: JSON.stringify(createBody)
2726
2785
  });
2727
- if (!searchResponse.ok) {
2728
- const errorText = await searchResponse.text();
2729
- throw new Error(`Exa research search failed (${searchResponse.status}): ${errorText}`);
2786
+ if (!createResponse.ok) {
2787
+ const errorText = await createResponse.text();
2788
+ throw new Error(`Failed to create research task (${createResponse.status}): ${errorText}`);
2730
2789
  }
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 ?? [];
2790
+ const createData = await createResponse.json();
2791
+ const researchId = createData.researchId;
2792
+ console.log(`Research task created: ${researchId}. Polling for results...`);
2793
+ const startTime = Date.now();
2794
+ let attempts = 0;
2795
+ while (Date.now() - startTime < this.maxPollTime) {
2796
+ attempts++;
2797
+ if (attempts > 1) {
2798
+ await new Promise((resolve6) => setTimeout(resolve6, this.pollInterval));
2799
+ }
2800
+ const statusResponse = await fetch(`https://api.exa.ai/research/v1/${researchId}`, {
2801
+ method: "GET",
2802
+ headers: {
2803
+ "x-api-key": this.apiKey
2754
2804
  }
2755
- } catch {
2805
+ });
2806
+ if (!statusResponse.ok) {
2807
+ const errorText = await statusResponse.text();
2808
+ throw new Error(`Failed to check research status (${statusResponse.status}): ${errorText}`);
2756
2809
  }
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}`);
2810
+ const statusData = await statusResponse.json();
2811
+ if (statusData.status === "completed") {
2812
+ const output = statusData.output;
2813
+ if (!output) {
2814
+ throw new Error("Research completed but no output was returned");
2815
+ }
2816
+ const sections = [];
2817
+ if (outputSchema && output.parsed) {
2818
+ sections.push("# Research Results (Structured)\n");
2819
+ sections.push(JSON.stringify(output.parsed, null, 2));
2820
+ } else {
2821
+ sections.push(output.content);
2822
+ }
2823
+ if (statusData.costDollars) {
2824
+ const cost = statusData.costDollars;
2825
+ sections.push("\n---\n");
2826
+ sections.push("## Research Metrics\n");
2827
+ sections.push(`- **Cost**: $${cost.total.toFixed(4)}`);
2828
+ sections.push(`- **Searches**: ${cost.numSearches}`);
2829
+ sections.push(`- **Pages analyzed**: ${cost.numPages}`);
2830
+ sections.push(`- **Reasoning tokens**: ${cost.reasoningTokens.toLocaleString()}`);
2831
+ }
2832
+ console.log(`Research completed in ${attempts} polls (${((Date.now() - startTime) / 1e3).toFixed(1)}s)`);
2833
+ return sections.join("\n");
2834
+ }
2835
+ if (statusData.status === "failed") {
2836
+ throw new Error(`Research failed: ${statusData.error ?? "Unknown error"}`);
2777
2837
  }
2778
- if ("author" in source && source.author) {
2779
- sections.push(`Author: ${source.author}`);
2838
+ if (statusData.status === "canceled") {
2839
+ throw new Error("Research was canceled");
2780
2840
  }
2781
- if (source.text) {
2782
- sections.push(`
2783
- Content:
2784
- ${source.text.slice(0, 2e3)}`);
2841
+ if (attempts % 10 === 0) {
2842
+ console.log(`Still researching... (${attempts} polls, ${((Date.now() - startTime) / 1e3).toFixed(0)}s elapsed)`);
2785
2843
  }
2786
- sections.push("");
2787
2844
  }
2788
- sections.push("## Source URLs\n");
2789
- uniqueSources.forEach((s, i) => {
2790
- sections.push(`${i + 1}. ${s.url}`);
2845
+ throw new Error(`Research timed out after ${this.maxPollTime / 1e3}s. Task ID: ${researchId}`);
2846
+ }
2847
+ };
2848
+
2849
+ // src/tools/ProxyTool.ts
2850
+ var import_child_process2 = require("child_process");
2851
+
2852
+ // src/utils/bunInstaller.ts
2853
+ var import_child_process = require("child_process");
2854
+ var path5 = __toESM(require("path"));
2855
+ var fs5 = __toESM(require("fs"));
2856
+ var os3 = __toESM(require("os"));
2857
+ var cachedBunPath = null;
2858
+ async function ensureBunAvailable() {
2859
+ if (cachedBunPath) return cachedBunPath;
2860
+ const fromPath = whichBun();
2861
+ if (fromPath) {
2862
+ cachedBunPath = fromPath;
2863
+ return fromPath;
2864
+ }
2865
+ const localPath = path5.join(os3.homedir(), ".bun", "bin", "bun");
2866
+ if (fs5.existsSync(localPath)) {
2867
+ cachedBunPath = localPath;
2868
+ return localPath;
2869
+ }
2870
+ console.log(
2871
+ "\n[smol-js] Bun is required to run custom tools but was not found. Installing Bun automatically...\n"
2872
+ );
2873
+ try {
2874
+ (0, import_child_process.execSync)("curl --proto =https --tlsv1.2 -sSf https://bun.sh | bash", {
2875
+ stdio: "inherit",
2876
+ shell: "/bin/bash",
2877
+ env: { ...process.env, HOME: os3.homedir() }
2791
2878
  });
2792
- return sections.join("\n");
2879
+ } catch (err) {
2880
+ throw new Error(
2881
+ `[smol-js] Failed to auto-install Bun. Please install it manually: https://bun.sh
2882
+ Details: ${err.message}`
2883
+ );
2884
+ }
2885
+ const afterInstall = whichBun() || (fs5.existsSync(localPath) ? localPath : null);
2886
+ if (!afterInstall) {
2887
+ throw new Error(
2888
+ "[smol-js] Bun installation appeared to succeed but the binary was not found. Please install manually: https://bun.sh"
2889
+ );
2890
+ }
2891
+ console.log(`[smol-js] Bun installed successfully at: ${afterInstall}
2892
+ `);
2893
+ cachedBunPath = afterInstall;
2894
+ return afterInstall;
2895
+ }
2896
+ function whichBun() {
2897
+ try {
2898
+ const cmd = process.platform === "win32" ? "where bun" : "which bun";
2899
+ const result = (0, import_child_process.execSync)(cmd, { encoding: "utf8", stdio: "pipe" }).trim();
2900
+ const first = result.split("\n")[0]?.trim();
2901
+ if (first && fs5.existsSync(first)) return first;
2902
+ return null;
2903
+ } catch {
2904
+ return null;
2905
+ }
2906
+ }
2907
+
2908
+ // src/tools/ProxyTool.ts
2909
+ var TOOL_OUTPUT_PREFIX = "[TOOL_OUTPUT]";
2910
+ var TOOL_RESULT_PREFIX = "[TOOL_RESULT]";
2911
+ var TOOL_ERROR_PREFIX = "[TOOL_ERROR]";
2912
+ var DEFAULT_TOOL_TIMEOUT_MS = 6e4;
2913
+ var ProxyTool = class extends Tool {
2914
+ name;
2915
+ description;
2916
+ inputs;
2917
+ outputType;
2918
+ toolPath;
2919
+ timeout;
2920
+ bunPath = null;
2921
+ constructor(config) {
2922
+ super();
2923
+ this.name = config.name;
2924
+ this.description = config.description;
2925
+ this.inputs = config.inputs;
2926
+ this.outputType = config.outputType;
2927
+ this.toolPath = config.toolPath;
2928
+ this.timeout = config.timeout ?? DEFAULT_TOOL_TIMEOUT_MS;
2929
+ }
2930
+ /**
2931
+ * Ensure Bun is available before first invocation.
2932
+ */
2933
+ async setup() {
2934
+ this.bunPath = await ensureBunAvailable();
2935
+ this.isSetup = true;
2936
+ }
2937
+ /**
2938
+ * Spawn the tool in a Bun child process, pass serialized args via CLI,
2939
+ * stream stdout back as log lines, and parse the final result.
2940
+ */
2941
+ async execute(args) {
2942
+ if (!this.bunPath) {
2943
+ await this.setup();
2944
+ }
2945
+ const serializedArgs = JSON.stringify(args);
2946
+ return new Promise((resolve6, reject) => {
2947
+ const child = (0, import_child_process2.spawn)(this.bunPath, ["run", this.toolPath, serializedArgs], {
2948
+ stdio: ["pipe", "pipe", "pipe"],
2949
+ env: { ...process.env }
2950
+ });
2951
+ let result = void 0;
2952
+ let resultReceived = false;
2953
+ let errorMessage = null;
2954
+ const logBuffer = [];
2955
+ let stderr = "";
2956
+ let partialLine = "";
2957
+ child.stdout.on("data", (chunk) => {
2958
+ partialLine += chunk.toString("utf8");
2959
+ const lines = partialLine.split("\n");
2960
+ partialLine = lines.pop();
2961
+ for (const line of lines) {
2962
+ this.processLine(line, {
2963
+ onOutput: (msg) => logBuffer.push(msg),
2964
+ onResult: (value) => {
2965
+ result = value;
2966
+ resultReceived = true;
2967
+ },
2968
+ onError: (msg) => {
2969
+ errorMessage = msg;
2970
+ }
2971
+ });
2972
+ }
2973
+ });
2974
+ child.stderr.on("data", (chunk) => {
2975
+ stderr += chunk.toString("utf8");
2976
+ });
2977
+ const timer = setTimeout(() => {
2978
+ child.kill("SIGTERM");
2979
+ reject(new Error(
2980
+ `Custom tool "${this.name}" timed out after ${this.timeout}ms. The process was terminated. Check the tool for infinite loops or slow operations.`
2981
+ ));
2982
+ }, this.timeout);
2983
+ timer.unref();
2984
+ child.on("close", (code) => {
2985
+ clearTimeout(timer);
2986
+ if (partialLine.trim()) {
2987
+ this.processLine(partialLine, {
2988
+ onOutput: (msg) => logBuffer.push(msg),
2989
+ onResult: (value) => {
2990
+ result = value;
2991
+ resultReceived = true;
2992
+ },
2993
+ onError: (msg) => {
2994
+ errorMessage = msg;
2995
+ }
2996
+ });
2997
+ }
2998
+ if (errorMessage) {
2999
+ reject(new Error(
3000
+ `Custom tool "${this.name}" reported an error: ${errorMessage}`
3001
+ ));
3002
+ return;
3003
+ }
3004
+ if (resultReceived) {
3005
+ if (logBuffer.length > 0) {
3006
+ const logPrefix = `[Tool output logs]
3007
+ ${logBuffer.join("\n")}
3008
+
3009
+ [Tool result]
3010
+ `;
3011
+ if (typeof result === "string") {
3012
+ resolve6(logPrefix + result);
3013
+ } else {
3014
+ resolve6({ logs: logBuffer.join("\n"), result });
3015
+ }
3016
+ } else {
3017
+ resolve6(result);
3018
+ }
3019
+ return;
3020
+ }
3021
+ const combined = (logBuffer.join("\n") + "\n" + stderr).trim();
3022
+ if (code !== 0) {
3023
+ reject(new Error(
3024
+ `Custom tool "${this.name}" exited with code ${code}. Output: ${combined || "(none)"}`
3025
+ ));
3026
+ } else {
3027
+ resolve6(combined || `Tool "${this.name}" produced no output.`);
3028
+ }
3029
+ });
3030
+ child.on("error", (err) => {
3031
+ clearTimeout(timer);
3032
+ reject(new Error(
3033
+ `Failed to spawn custom tool "${this.name}": ${err.message}`
3034
+ ));
3035
+ });
3036
+ });
3037
+ }
3038
+ // --- internal line parser ---
3039
+ processLine(line, handlers) {
3040
+ const trimmed = line.trimEnd();
3041
+ if (!trimmed) return;
3042
+ if (trimmed.startsWith(TOOL_RESULT_PREFIX)) {
3043
+ const json = trimmed.slice(TOOL_RESULT_PREFIX.length).trim();
3044
+ try {
3045
+ handlers.onResult(JSON.parse(json));
3046
+ } catch {
3047
+ handlers.onResult(json);
3048
+ }
3049
+ } else if (trimmed.startsWith(TOOL_ERROR_PREFIX)) {
3050
+ handlers.onError(trimmed.slice(TOOL_ERROR_PREFIX.length).trim());
3051
+ } else if (trimmed.startsWith(TOOL_OUTPUT_PREFIX)) {
3052
+ handlers.onOutput(trimmed.slice(TOOL_OUTPUT_PREFIX.length).trim());
3053
+ } else {
3054
+ handlers.onOutput(trimmed);
3055
+ }
2793
3056
  }
2794
3057
  };
2795
3058
 
3059
+ // src/tools/CustomToolScanner.ts
3060
+ var fs6 = __toESM(require("fs"));
3061
+ var path6 = __toESM(require("path"));
3062
+ var METADATA_REGEX = /export\s+const\s+TOOL_METADATA\s*=\s*(\{[\s\S]*?\});\s*$/m;
3063
+ function scanCustomTools(folderPath) {
3064
+ if (!fs6.existsSync(folderPath)) {
3065
+ throw new Error(
3066
+ `Custom tools folder not found: ${folderPath}. Create the directory or check your --custom-tools-folder path.`
3067
+ );
3068
+ }
3069
+ const entries = fs6.readdirSync(folderPath, { withFileTypes: true });
3070
+ const discovered = [];
3071
+ for (const entry of entries) {
3072
+ if (entry.isDirectory()) continue;
3073
+ const ext = path6.extname(entry.name).toLowerCase();
3074
+ if (ext !== ".ts" && ext !== ".js") continue;
3075
+ const filePath = path6.resolve(folderPath, entry.name);
3076
+ const baseName = path6.basename(entry.name, ext);
3077
+ let metadata;
3078
+ try {
3079
+ metadata = extractMetadata(filePath);
3080
+ } catch (err) {
3081
+ throw new Error(
3082
+ `Failed to extract TOOL_METADATA from "${entry.name}": ${err.message}
3083
+ Ensure the file exports \`export const TOOL_METADATA = { name, description, inputs, outputType };\``
3084
+ );
3085
+ }
3086
+ if (metadata.name !== baseName) {
3087
+ throw new Error(
3088
+ `Tool metadata name mismatch in "${entry.name}": file is "${baseName}" but TOOL_METADATA.name is "${metadata.name}". They must match (Convention over Configuration).`
3089
+ );
3090
+ }
3091
+ discovered.push({ filePath, metadata });
3092
+ }
3093
+ return discovered;
3094
+ }
3095
+ function extractMetadata(filePath) {
3096
+ const source = fs6.readFileSync(filePath, "utf8");
3097
+ const match = source.match(METADATA_REGEX);
3098
+ if (!match) {
3099
+ throw new Error(
3100
+ "No `export const TOOL_METADATA = { ... };` block found. Add the metadata export at the bottom of your tool file."
3101
+ );
3102
+ }
3103
+ let parsed;
3104
+ try {
3105
+ parsed = new Function(`"use strict"; return (${match[1]});`)();
3106
+ } catch (err) {
3107
+ throw new Error(
3108
+ `Could not parse TOOL_METADATA object: ${err.message}. Ensure it is a valid JavaScript object literal.`
3109
+ );
3110
+ }
3111
+ if (!parsed.name || typeof parsed.name !== "string") {
3112
+ throw new Error("TOOL_METADATA.name must be a non-empty string.");
3113
+ }
3114
+ if (!parsed.description || typeof parsed.description !== "string") {
3115
+ throw new Error("TOOL_METADATA.description must be a non-empty string.");
3116
+ }
3117
+ if (!parsed.inputs || typeof parsed.inputs !== "object") {
3118
+ throw new Error("TOOL_METADATA.inputs must be an object mapping parameter names to their schemas.");
3119
+ }
3120
+ if (!parsed.outputType || typeof parsed.outputType !== "string") {
3121
+ throw new Error("TOOL_METADATA.outputType must be a non-empty string.");
3122
+ }
3123
+ return parsed;
3124
+ }
3125
+ function loadCustomTools(folderPath) {
3126
+ const discovered = scanCustomTools(folderPath);
3127
+ const tools = /* @__PURE__ */ new Map();
3128
+ for (const { filePath, metadata } of discovered) {
3129
+ const config = {
3130
+ toolPath: filePath,
3131
+ name: metadata.name,
3132
+ description: metadata.description,
3133
+ inputs: metadata.inputs,
3134
+ outputType: metadata.outputType,
3135
+ timeout: metadata.timeout
3136
+ };
3137
+ tools.set(metadata.name, new ProxyTool(config));
3138
+ }
3139
+ return tools;
3140
+ }
3141
+
2796
3142
  // src/orchestrator/YAMLLoader.ts
2797
- var fs5 = __toESM(require("fs"));
2798
- var path5 = __toESM(require("path"));
3143
+ var fs7 = __toESM(require("fs"));
3144
+ var path7 = __toESM(require("path"));
2799
3145
  var import_yaml = __toESM(require("yaml"));
2800
3146
  var TOOL_REGISTRY = {
2801
3147
  read_file: ReadFileTool,
@@ -2808,21 +3154,30 @@ var TOOL_REGISTRY = {
2808
3154
  };
2809
3155
  var YAMLLoader = class {
2810
3156
  customTools = /* @__PURE__ */ new Map();
3157
+ toolInstances = /* @__PURE__ */ new Map();
2811
3158
  /**
2812
- * Register a custom tool type for use in YAML definitions.
3159
+ * Register a custom tool type (class) for use in YAML definitions.
2813
3160
  */
2814
3161
  registerToolType(typeName, toolClass) {
2815
3162
  this.customTools.set(typeName, toolClass);
2816
3163
  }
3164
+ /**
3165
+ * Register a pre-built tool instance for use in YAML definitions.
3166
+ * Used by the custom tool system to register ProxyTool instances
3167
+ * that are created by the scanner rather than by class instantiation.
3168
+ */
3169
+ registerToolInstance(typeName, tool) {
3170
+ this.toolInstances.set(typeName, tool);
3171
+ }
2817
3172
  /**
2818
3173
  * Load a workflow from a YAML file path.
2819
3174
  */
2820
3175
  loadFromFile(filePath) {
2821
- const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
2822
- if (!fs5.existsSync(absolutePath)) {
3176
+ const absolutePath = path7.isAbsolute(filePath) ? filePath : path7.resolve(process.cwd(), filePath);
3177
+ if (!fs7.existsSync(absolutePath)) {
2823
3178
  throw new Error(`Workflow file not found: ${absolutePath}`);
2824
3179
  }
2825
- const content = fs5.readFileSync(absolutePath, "utf-8");
3180
+ const content = fs7.readFileSync(absolutePath, "utf-8");
2826
3181
  return this.loadFromString(content);
2827
3182
  }
2828
3183
  /**
@@ -2897,9 +3252,16 @@ var YAMLLoader = class {
2897
3252
  * Build a tool instance from a type name and config.
2898
3253
  */
2899
3254
  buildTool(name, type, config) {
3255
+ const existingInstance = this.toolInstances.get(type);
3256
+ if (existingInstance) {
3257
+ if (name !== type && name !== existingInstance.name) {
3258
+ Object.defineProperty(existingInstance, "name", { value: name, writable: false });
3259
+ }
3260
+ return existingInstance;
3261
+ }
2900
3262
  const ToolClass = TOOL_REGISTRY[type] ?? this.customTools.get(type);
2901
3263
  if (!ToolClass) {
2902
- throw new Error(`Unknown tool type: ${type}. Available types: ${[...Object.keys(TOOL_REGISTRY), ...this.customTools.keys()].join(", ")}`);
3264
+ throw new Error(`Unknown tool type: ${type}. Available types: ${[...Object.keys(TOOL_REGISTRY), ...this.customTools.keys(), ...this.toolInstances.keys()].join(", ")}`);
2903
3265
  }
2904
3266
  const tool = new ToolClass(config);
2905
3267
  if (name !== type && name !== tool.name) {
@@ -2927,11 +3289,16 @@ var YAMLLoader = class {
2927
3289
  if (tool) {
2928
3290
  agentTools.push(tool);
2929
3291
  } else {
2930
- const ToolClass = TOOL_REGISTRY[toolName] ?? this.customTools.get(toolName);
2931
- if (ToolClass) {
2932
- agentTools.push(new ToolClass());
3292
+ const instance = this.toolInstances.get(toolName);
3293
+ if (instance) {
3294
+ agentTools.push(instance);
2933
3295
  } else {
2934
- throw new Error(`Tool "${toolName}" not found for agent "${name}"`);
3296
+ const ToolClass = TOOL_REGISTRY[toolName] ?? this.customTools.get(toolName);
3297
+ if (ToolClass) {
3298
+ agentTools.push(new ToolClass());
3299
+ } else {
3300
+ throw new Error(`Tool "${toolName}" not found for agent "${name}"`);
3301
+ }
2935
3302
  }
2936
3303
  }
2937
3304
  }
@@ -2982,39 +3349,117 @@ var YAMLLoader = class {
2982
3349
 
2983
3350
  // src/orchestrator/Orchestrator.ts
2984
3351
  var import_chalk2 = __toESM(require("chalk"));
3352
+
3353
+ // src/output/JSONOutputHandler.ts
3354
+ var JSONOutputHandler = class {
3355
+ runId;
3356
+ startTime;
3357
+ constructor(config) {
3358
+ this.runId = config.runId;
3359
+ this.startTime = Date.now();
3360
+ }
3361
+ emit(type, data, extra = {}) {
3362
+ console.log(JSON.stringify({
3363
+ runId: this.runId,
3364
+ timestamp: Date.now(),
3365
+ type,
3366
+ ...extra,
3367
+ data
3368
+ }));
3369
+ }
3370
+ emitRunStart(workflowPath, task, cwd) {
3371
+ this.emit("run_start", { workflowPath, task, cwd });
3372
+ }
3373
+ emitWorkflowLoaded(name, description, agents, tools, entrypoint) {
3374
+ this.emit("workflow_loaded", { name, description, agents, tools, entrypoint });
3375
+ }
3376
+ emitRunEnd(success, output, totalTokens, totalSteps) {
3377
+ this.emit("run_end", {
3378
+ success,
3379
+ output,
3380
+ totalDuration: Date.now() - this.startTime,
3381
+ totalTokens,
3382
+ totalSteps
3383
+ });
3384
+ }
3385
+ emitAgentStart(agentName, depth, task, agentType, maxSteps) {
3386
+ this.emit("agent_start", { task, agentType, maxSteps }, { agentName, depth });
3387
+ }
3388
+ emitAgentEnd(agentName, depth, output, totalSteps, tokenUsage, duration, success) {
3389
+ this.emit("agent_end", { output, totalSteps, tokenUsage, duration, success }, { agentName, depth });
3390
+ }
3391
+ emitAgentStep(agentName, depth, stepNumber, maxSteps, phase) {
3392
+ this.emit("agent_step", { stepNumber, maxSteps, phase }, { agentName, depth });
3393
+ }
3394
+ emitAgentThinking(agentName, depth, stepNumber, content, isPartial) {
3395
+ this.emit("agent_thinking", { stepNumber, content, isPartial }, { agentName, depth });
3396
+ }
3397
+ emitToolCall(agentName, depth, stepNumber, toolCallId, toolName, args) {
3398
+ this.emit("agent_tool_call", { stepNumber, toolCallId, toolName, arguments: args }, { agentName, depth });
3399
+ }
3400
+ emitToolResult(agentName, depth, stepNumber, toolCallId, toolName, result, error, duration) {
3401
+ this.emit("agent_tool_result", { stepNumber, toolCallId, toolName, result, error, duration }, { agentName, depth });
3402
+ }
3403
+ emitObservation(agentName, depth, stepNumber, observation, codeAction, logs) {
3404
+ this.emit("agent_observation", { stepNumber, observation, codeAction, logs }, { agentName, depth });
3405
+ }
3406
+ emitError(message, stack, agentName, depth, stepNumber) {
3407
+ this.emit("error", { message, stack, stepNumber }, {
3408
+ ...agentName ? { agentName } : {},
3409
+ ...depth !== void 0 ? { depth } : {}
3410
+ });
3411
+ }
3412
+ };
3413
+
3414
+ // src/orchestrator/Orchestrator.ts
2985
3415
  var Orchestrator = class {
2986
3416
  loader;
2987
3417
  config;
2988
3418
  activeAgents = /* @__PURE__ */ new Map();
2989
3419
  eventLog = [];
3420
+ jsonOutput = null;
3421
+ isJsonMode = false;
2990
3422
  constructor(config = {}) {
2991
3423
  this.loader = new YAMLLoader();
3424
+ this.isJsonMode = config.outputFormat === "json";
2992
3425
  this.config = {
2993
3426
  verbose: config.verbose ?? true,
2994
- onEvent: config.onEvent
3427
+ onEvent: config.onEvent,
3428
+ outputFormat: config.outputFormat ?? "text",
3429
+ runId: config.runId,
3430
+ cwd: config.cwd
2995
3431
  };
3432
+ if (this.isJsonMode) {
3433
+ if (!config.runId) {
3434
+ throw new Error("runId is required for JSON output mode");
3435
+ }
3436
+ this.jsonOutput = new JSONOutputHandler({
3437
+ runId: config.runId,
3438
+ verbose: config.verbose ?? true
3439
+ });
3440
+ }
2996
3441
  }
2997
3442
  /**
2998
3443
  * Load a workflow from a YAML file.
2999
3444
  */
3000
3445
  loadWorkflow(filePath) {
3001
3446
  const workflow = this.loader.loadFromFile(filePath);
3002
- this.displayWorkflowInfo(workflow);
3447
+ this.displayWorkflowInfo(workflow, filePath);
3003
3448
  return workflow;
3004
3449
  }
3005
3450
  /**
3006
3451
  * Load a workflow from YAML string.
3007
3452
  */
3008
- loadWorkflowFromString(yamlContent) {
3453
+ loadWorkflowFromString(yamlContent, sourcePath) {
3009
3454
  const workflow = this.loader.loadFromString(yamlContent);
3010
- this.displayWorkflowInfo(workflow);
3455
+ this.displayWorkflowInfo(workflow, sourcePath);
3011
3456
  return workflow;
3012
3457
  }
3013
3458
  /**
3014
3459
  * Run a loaded workflow with a task.
3015
3460
  */
3016
- async runWorkflow(workflow, task) {
3017
- this.displayRunStart(workflow.name, task);
3461
+ async runWorkflow(workflow, task, workflowPath) {
3462
+ this.displayRunStart(workflow.name, task, workflowPath);
3018
3463
  this.instrumentAgent(workflow.entrypointAgent, workflow.entrypointAgent.getName(), 0);
3019
3464
  for (const [name, agent] of workflow.agents) {
3020
3465
  if (agent !== workflow.entrypointAgent) {
@@ -3023,10 +3468,13 @@ var Orchestrator = class {
3023
3468
  }
3024
3469
  try {
3025
3470
  const result = await workflow.entrypointAgent.run(task);
3026
- this.displayRunEnd(result);
3471
+ this.displayRunEnd(result, true);
3027
3472
  return result;
3028
3473
  } catch (error) {
3029
3474
  this.displayError(error);
3475
+ if (this.isJsonMode && this.jsonOutput) {
3476
+ this.jsonOutput.emitRunEnd(false, null, 0, 0);
3477
+ }
3030
3478
  throw error;
3031
3479
  }
3032
3480
  }
@@ -3043,11 +3491,128 @@ var Orchestrator = class {
3043
3491
  */
3044
3492
  instrumentAgent(agent, name, depth) {
3045
3493
  this.activeAgents.set(name, { agent, depth });
3494
+ agent.setOnEvent((event) => {
3495
+ const orchestratorEvent = {
3496
+ type: event.type,
3497
+ agentName: name,
3498
+ depth,
3499
+ data: event.data,
3500
+ timestamp: Date.now()
3501
+ };
3502
+ this.logEvent(orchestratorEvent);
3503
+ if (this.isJsonMode && this.jsonOutput) {
3504
+ this.emitAgentEventAsJSON(event, name, depth);
3505
+ }
3506
+ });
3507
+ }
3508
+ /**
3509
+ * Emit an agent event as JSON.
3510
+ */
3511
+ emitAgentEventAsJSON(event, agentName, depth) {
3512
+ if (!this.jsonOutput) return;
3513
+ const data = event.data;
3514
+ switch (event.type) {
3515
+ case "agent_start":
3516
+ this.jsonOutput.emitAgentStart(
3517
+ agentName,
3518
+ depth,
3519
+ data.task,
3520
+ data.name ? "ToolUseAgent" : "CodeAgent",
3521
+ // Will be improved
3522
+ 20
3523
+ // Default maxSteps, could be passed
3524
+ );
3525
+ break;
3526
+ case "agent_step":
3527
+ this.jsonOutput.emitAgentStep(
3528
+ agentName,
3529
+ depth,
3530
+ data.step,
3531
+ data.maxSteps,
3532
+ "start"
3533
+ );
3534
+ break;
3535
+ case "agent_thinking":
3536
+ this.jsonOutput.emitAgentThinking(
3537
+ agentName,
3538
+ depth,
3539
+ data.step,
3540
+ data.content,
3541
+ false
3542
+ );
3543
+ break;
3544
+ case "agent_tool_call":
3545
+ this.jsonOutput.emitToolCall(
3546
+ agentName,
3547
+ depth,
3548
+ data.step,
3549
+ data.toolCallId,
3550
+ data.toolName,
3551
+ data.arguments
3552
+ );
3553
+ break;
3554
+ case "agent_tool_result":
3555
+ this.jsonOutput.emitToolResult(
3556
+ agentName,
3557
+ depth,
3558
+ data.step,
3559
+ data.toolCallId,
3560
+ data.toolName,
3561
+ data.result,
3562
+ data.error,
3563
+ data.duration
3564
+ );
3565
+ break;
3566
+ case "agent_observation":
3567
+ this.jsonOutput.emitObservation(
3568
+ agentName,
3569
+ depth,
3570
+ data.step,
3571
+ data.observation,
3572
+ data.codeAction,
3573
+ data.logs
3574
+ );
3575
+ break;
3576
+ case "agent_error":
3577
+ this.jsonOutput.emitError(
3578
+ data.error,
3579
+ void 0,
3580
+ agentName,
3581
+ depth,
3582
+ data.step
3583
+ );
3584
+ break;
3585
+ case "agent_end":
3586
+ this.jsonOutput.emitAgentEnd(
3587
+ agentName,
3588
+ depth,
3589
+ data.output,
3590
+ 0,
3591
+ // totalSteps - would need to track
3592
+ data.tokenUsage,
3593
+ data.duration,
3594
+ true
3595
+ );
3596
+ break;
3597
+ }
3046
3598
  }
3047
3599
  /**
3048
3600
  * Display workflow info at startup.
3049
3601
  */
3050
- displayWorkflowInfo(workflow) {
3602
+ displayWorkflowInfo(workflow, _sourcePath) {
3603
+ const agents = Array.from(workflow.agents.keys());
3604
+ const tools = Array.from(workflow.tools.keys());
3605
+ const entrypoint = workflow.entrypointAgent.getName();
3606
+ if (this.isJsonMode && this.jsonOutput) {
3607
+ this.jsonOutput.emitWorkflowLoaded(
3608
+ workflow.name,
3609
+ workflow.description,
3610
+ agents,
3611
+ tools,
3612
+ entrypoint
3613
+ );
3614
+ return;
3615
+ }
3051
3616
  if (!this.config.verbose) return;
3052
3617
  const line = "\u2550".repeat(70);
3053
3618
  console.log(import_chalk2.default.cyan(line));
@@ -3055,16 +3620,20 @@ var Orchestrator = class {
3055
3620
  if (workflow.description) {
3056
3621
  console.log(import_chalk2.default.cyan(` ${workflow.description}`));
3057
3622
  }
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()}`));
3623
+ console.log(import_chalk2.default.cyan(` Agents: ${agents.join(", ")}`));
3624
+ console.log(import_chalk2.default.cyan(` Tools: ${tools.join(", ") || "(none defined at workflow level)"}`));
3625
+ console.log(import_chalk2.default.cyan(` Entrypoint: ${entrypoint}`));
3061
3626
  console.log(import_chalk2.default.cyan(line));
3062
3627
  console.log();
3063
3628
  }
3064
3629
  /**
3065
3630
  * Display run start info.
3066
3631
  */
3067
- displayRunStart(workflowName, task) {
3632
+ displayRunStart(workflowName, task, workflowPath) {
3633
+ if (this.isJsonMode && this.jsonOutput) {
3634
+ this.jsonOutput.emitRunStart(workflowPath || workflowName, task, this.config.cwd);
3635
+ return;
3636
+ }
3068
3637
  if (!this.config.verbose) return;
3069
3638
  console.log(import_chalk2.default.green.bold(`
3070
3639
  \u25B6 Running workflow "${workflowName}"`));
@@ -3074,7 +3643,16 @@ var Orchestrator = class {
3074
3643
  /**
3075
3644
  * Display run completion info.
3076
3645
  */
3077
- displayRunEnd(result) {
3646
+ displayRunEnd(result, success = true) {
3647
+ if (this.isJsonMode && this.jsonOutput) {
3648
+ this.jsonOutput.emitRunEnd(
3649
+ success,
3650
+ result.output,
3651
+ result.tokenUsage.totalTokens,
3652
+ result.steps.length
3653
+ );
3654
+ return;
3655
+ }
3078
3656
  if (!this.config.verbose) return;
3079
3657
  console.log(import_chalk2.default.gray("\n" + "\u2500".repeat(70)));
3080
3658
  console.log(import_chalk2.default.green.bold(`
@@ -3092,6 +3670,10 @@ var Orchestrator = class {
3092
3670
  * Display an error.
3093
3671
  */
3094
3672
  displayError(error) {
3673
+ if (this.isJsonMode && this.jsonOutput) {
3674
+ this.jsonOutput.emitError(error.message, error.stack);
3675
+ return;
3676
+ }
3095
3677
  if (!this.config.verbose) return;
3096
3678
  console.error(import_chalk2.default.red.bold(`
3097
3679
  \u274C Workflow failed: ${error.message}`));
@@ -3120,6 +3702,24 @@ var Orchestrator = class {
3120
3702
  getLoader() {
3121
3703
  return this.loader;
3122
3704
  }
3705
+ /**
3706
+ * Get the JSON output handler (if in JSON mode).
3707
+ */
3708
+ getJSONOutputHandler() {
3709
+ return this.jsonOutput;
3710
+ }
3711
+ /**
3712
+ * Check if in JSON output mode.
3713
+ */
3714
+ isJSONOutputMode() {
3715
+ return this.isJsonMode;
3716
+ }
3717
+ /**
3718
+ * Get the run ID.
3719
+ */
3720
+ getRunId() {
3721
+ return this.config.runId;
3722
+ }
3123
3723
  };
3124
3724
  // Annotate the CommonJS export names for ESM import in node:
3125
3725
  0 && (module.exports = {
@@ -3134,11 +3734,13 @@ var Orchestrator = class {
3134
3734
  ExaSearchTool,
3135
3735
  FINAL_ANSWER_PROMPT,
3136
3736
  FinalAnswerTool,
3737
+ JSONOutputHandler,
3137
3738
  LocalExecutor,
3138
3739
  LogLevel,
3139
3740
  Model,
3140
3741
  OpenAIModel,
3141
3742
  Orchestrator,
3743
+ ProxyTool,
3142
3744
  ReadFileTool,
3143
3745
  Tool,
3144
3746
  ToolUseAgent,
@@ -3151,6 +3753,8 @@ var Orchestrator = class {
3151
3753
  formatToolDescriptions,
3152
3754
  generateSystemPrompt,
3153
3755
  generateToolUseSystemPrompt,
3154
- getErrorRecoveryPrompt
3756
+ getErrorRecoveryPrompt,
3757
+ loadCustomTools,
3758
+ scanCustomTools
3155
3759
  });
3156
3760
  //# sourceMappingURL=index.js.map