@samrahimi/smol-js 0.6.5 → 0.7.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
@@ -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((resolve7) => setTimeout(resolve7, 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((resolve7) => {
1449
1465
  rl.question(`
1450
1466
  [Agent asks]: ${question}
1451
1467
  Your response: `, (answer) => {
1452
1468
  rl.close();
1453
- resolve5(answer);
1469
+ resolve7(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,390 @@ 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((resolve7) => setTimeout(resolve7, 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
+ var path6 = __toESM(require("path"));
2852
+
2853
+ // src/utils/bunInstaller.ts
2854
+ var import_child_process = require("child_process");
2855
+ var path5 = __toESM(require("path"));
2856
+ var fs5 = __toESM(require("fs"));
2857
+ var os3 = __toESM(require("os"));
2858
+ var cachedBunPath = null;
2859
+ async function ensureBunAvailable() {
2860
+ if (cachedBunPath) return cachedBunPath;
2861
+ const fromPath = whichBun();
2862
+ if (fromPath) {
2863
+ cachedBunPath = fromPath;
2864
+ return fromPath;
2865
+ }
2866
+ const localPath = path5.join(os3.homedir(), ".bun", "bin", "bun");
2867
+ if (fs5.existsSync(localPath)) {
2868
+ cachedBunPath = localPath;
2869
+ return localPath;
2870
+ }
2871
+ console.log(
2872
+ "\n[smol-js] Bun is required to run custom tools but was not found. Installing Bun automatically...\n"
2873
+ );
2874
+ try {
2875
+ (0, import_child_process.execSync)("curl --proto =https --tlsv1.2 -sSf https://bun.sh | bash", {
2876
+ stdio: "inherit",
2877
+ shell: "/bin/bash",
2878
+ env: { ...process.env, HOME: os3.homedir() }
2879
+ });
2880
+ } catch (err) {
2881
+ throw new Error(
2882
+ `[smol-js] Failed to auto-install Bun. Please install it manually: https://bun.sh
2883
+ Details: ${err.message}`
2884
+ );
2885
+ }
2886
+ const afterInstall = whichBun() || (fs5.existsSync(localPath) ? localPath : null);
2887
+ if (!afterInstall) {
2888
+ throw new Error(
2889
+ "[smol-js] Bun installation appeared to succeed but the binary was not found. Please install manually: https://bun.sh"
2890
+ );
2891
+ }
2892
+ console.log(`[smol-js] Bun installed successfully at: ${afterInstall}
2893
+ `);
2894
+ cachedBunPath = afterInstall;
2895
+ return afterInstall;
2896
+ }
2897
+ function whichBun() {
2898
+ try {
2899
+ const cmd = process.platform === "win32" ? "where bun" : "which bun";
2900
+ const result = (0, import_child_process.execSync)(cmd, { encoding: "utf8", stdio: "pipe" }).trim();
2901
+ const first = result.split("\n")[0]?.trim();
2902
+ if (first && fs5.existsSync(first)) return first;
2903
+ return null;
2904
+ } catch {
2905
+ return null;
2906
+ }
2907
+ }
2908
+
2909
+ // src/tools/ProxyTool.ts
2910
+ var TOOL_RESULT_PREFIX = "[TOOL_RESULT]";
2911
+ var TOOL_ERROR_PREFIX = "[TOOL_ERROR]";
2912
+ var DEFAULT_TOOL_TIMEOUT_MS = 6e4;
2913
+ function resolveHarnessPath() {
2914
+ return path6.resolve(__dirname, "..", "toolHarness.ts");
2915
+ }
2916
+ var ProxyTool = class extends Tool {
2917
+ name;
2918
+ description;
2919
+ inputs;
2920
+ outputType;
2921
+ toolPath;
2922
+ timeout;
2923
+ bunPath = null;
2924
+ harnessPath = null;
2925
+ constructor(config) {
2926
+ super();
2927
+ this.name = config.name;
2928
+ this.description = config.description;
2929
+ this.inputs = config.inputs;
2930
+ this.outputType = config.outputType;
2931
+ this.toolPath = config.toolPath;
2932
+ this.timeout = config.timeout ?? DEFAULT_TOOL_TIMEOUT_MS;
2933
+ }
2934
+ /**
2935
+ * Ensure Bun is available and locate the harness before first invocation.
2936
+ */
2937
+ async setup() {
2938
+ this.bunPath = await ensureBunAvailable();
2939
+ this.harnessPath = resolveHarnessPath();
2940
+ this.isSetup = true;
2941
+ }
2942
+ /**
2943
+ * Spawn the harness in a Bun child process. The harness imports the tool,
2944
+ * calls execute(args), and writes the protocol lines. Any console.log from
2945
+ * the tool flows through stdout as plain lines.
2946
+ */
2947
+ async execute(args) {
2948
+ if (!this.bunPath || !this.harnessPath) {
2949
+ await this.setup();
2950
+ }
2951
+ const serializedArgs = JSON.stringify(args);
2952
+ return new Promise((resolve7, reject) => {
2953
+ const child = (0, import_child_process2.spawn)(this.bunPath, ["run", this.harnessPath, this.toolPath, serializedArgs], {
2954
+ stdio: ["pipe", "pipe", "pipe"],
2955
+ env: { ...process.env }
2956
+ });
2957
+ let result = void 0;
2958
+ let resultReceived = false;
2959
+ let errorMessage = null;
2960
+ const logBuffer = [];
2961
+ let stderr = "";
2962
+ let partialLine = "";
2963
+ child.stdout.on("data", (chunk) => {
2964
+ partialLine += chunk.toString("utf8");
2965
+ const lines = partialLine.split("\n");
2966
+ partialLine = lines.pop();
2967
+ for (const line of lines) {
2968
+ this.processLine(line, {
2969
+ onOutput: (msg) => logBuffer.push(msg),
2970
+ onResult: (value) => {
2971
+ result = value;
2972
+ resultReceived = true;
2973
+ },
2974
+ onError: (msg) => {
2975
+ errorMessage = msg;
2976
+ }
2977
+ });
2978
+ }
2979
+ });
2980
+ child.stderr.on("data", (chunk) => {
2981
+ stderr += chunk.toString("utf8");
2982
+ });
2983
+ const timer = setTimeout(() => {
2984
+ child.kill("SIGTERM");
2985
+ reject(new Error(
2986
+ `Custom tool "${this.name}" timed out after ${this.timeout}ms. The process was terminated. Check the tool for infinite loops or slow operations.`
2987
+ ));
2988
+ }, this.timeout);
2989
+ timer.unref();
2990
+ child.on("close", (code) => {
2991
+ clearTimeout(timer);
2992
+ if (partialLine.trim()) {
2993
+ this.processLine(partialLine, {
2994
+ onOutput: (msg) => logBuffer.push(msg),
2995
+ onResult: (value) => {
2996
+ result = value;
2997
+ resultReceived = true;
2998
+ },
2999
+ onError: (msg) => {
3000
+ errorMessage = msg;
3001
+ }
3002
+ });
3003
+ }
3004
+ if (errorMessage) {
3005
+ reject(new Error(
3006
+ `Custom tool "${this.name}" reported an error: ${errorMessage}`
3007
+ ));
3008
+ return;
3009
+ }
3010
+ if (resultReceived) {
3011
+ if (logBuffer.length > 0) {
3012
+ const logPrefix = `[Tool output logs]
3013
+ ${logBuffer.join("\n")}
3014
+
3015
+ [Tool result]
3016
+ `;
3017
+ if (typeof result === "string") {
3018
+ resolve7(logPrefix + result);
3019
+ } else {
3020
+ resolve7({ logs: logBuffer.join("\n"), result });
3021
+ }
3022
+ } else {
3023
+ resolve7(result);
3024
+ }
3025
+ return;
3026
+ }
3027
+ const combined = (logBuffer.join("\n") + "\n" + stderr).trim();
3028
+ if (code !== 0) {
3029
+ reject(new Error(
3030
+ `Custom tool "${this.name}" exited with code ${code}. Output: ${combined || "(none)"}`
3031
+ ));
3032
+ } else {
3033
+ resolve7(combined || `Tool "${this.name}" produced no output.`);
3034
+ }
3035
+ });
3036
+ child.on("error", (err) => {
3037
+ clearTimeout(timer);
3038
+ reject(new Error(
3039
+ `Failed to spawn custom tool "${this.name}": ${err.message}`
3040
+ ));
3041
+ });
2791
3042
  });
2792
- return sections.join("\n");
3043
+ }
3044
+ // --- line parser: protocol is spoken by harness, interpreted here ---
3045
+ processLine(line, handlers) {
3046
+ const trimmed = line.trimEnd();
3047
+ if (!trimmed) return;
3048
+ if (trimmed.startsWith(TOOL_RESULT_PREFIX)) {
3049
+ const json = trimmed.slice(TOOL_RESULT_PREFIX.length).trim();
3050
+ try {
3051
+ handlers.onResult(JSON.parse(json));
3052
+ } catch {
3053
+ handlers.onResult(json);
3054
+ }
3055
+ } else if (trimmed.startsWith(TOOL_ERROR_PREFIX)) {
3056
+ handlers.onError(trimmed.slice(TOOL_ERROR_PREFIX.length).trim());
3057
+ } else {
3058
+ handlers.onOutput(trimmed);
3059
+ }
2793
3060
  }
2794
3061
  };
2795
3062
 
3063
+ // src/tools/CustomToolScanner.ts
3064
+ var fs6 = __toESM(require("fs"));
3065
+ var path7 = __toESM(require("path"));
3066
+ var METADATA_REGEX = /export\s+const\s+TOOL_METADATA\s*=\s*(\{[\s\S]*?\});\s*$/m;
3067
+ function scanCustomTools(folderPath) {
3068
+ if (!fs6.existsSync(folderPath)) {
3069
+ throw new Error(
3070
+ `Custom tools folder not found: ${folderPath}. Create the directory or check your --custom-tools-folder path.`
3071
+ );
3072
+ }
3073
+ const entries = fs6.readdirSync(folderPath, { withFileTypes: true });
3074
+ const discovered = [];
3075
+ for (const entry of entries) {
3076
+ if (entry.isDirectory()) continue;
3077
+ const ext = path7.extname(entry.name).toLowerCase();
3078
+ if (ext !== ".ts" && ext !== ".js") continue;
3079
+ const filePath = path7.resolve(folderPath, entry.name);
3080
+ const baseName = path7.basename(entry.name, ext);
3081
+ let metadata;
3082
+ try {
3083
+ metadata = extractMetadata(filePath);
3084
+ } catch (err) {
3085
+ throw new Error(
3086
+ `Failed to extract TOOL_METADATA from "${entry.name}": ${err.message}
3087
+ Ensure the file exports \`export const TOOL_METADATA = { name, description, inputs, outputType };\``
3088
+ );
3089
+ }
3090
+ if (metadata.name !== baseName) {
3091
+ throw new Error(
3092
+ `Tool metadata name mismatch in "${entry.name}": file is "${baseName}" but TOOL_METADATA.name is "${metadata.name}". They must match (Convention over Configuration).`
3093
+ );
3094
+ }
3095
+ discovered.push({ filePath, metadata });
3096
+ }
3097
+ return discovered;
3098
+ }
3099
+ function extractMetadata(filePath) {
3100
+ const source = fs6.readFileSync(filePath, "utf8");
3101
+ const match = source.match(METADATA_REGEX);
3102
+ if (!match) {
3103
+ throw new Error(
3104
+ "No `export const TOOL_METADATA = { ... };` block found. Add the metadata export at the bottom of your tool file."
3105
+ );
3106
+ }
3107
+ let parsed;
3108
+ try {
3109
+ parsed = new Function(`"use strict"; return (${match[1]});`)();
3110
+ } catch (err) {
3111
+ throw new Error(
3112
+ `Could not parse TOOL_METADATA object: ${err.message}. Ensure it is a valid JavaScript object literal.`
3113
+ );
3114
+ }
3115
+ if (!parsed.name || typeof parsed.name !== "string") {
3116
+ throw new Error("TOOL_METADATA.name must be a non-empty string.");
3117
+ }
3118
+ if (!parsed.description || typeof parsed.description !== "string") {
3119
+ throw new Error("TOOL_METADATA.description must be a non-empty string.");
3120
+ }
3121
+ if (!parsed.inputs || typeof parsed.inputs !== "object") {
3122
+ throw new Error("TOOL_METADATA.inputs must be an object mapping parameter names to their schemas.");
3123
+ }
3124
+ if (!parsed.outputType || typeof parsed.outputType !== "string") {
3125
+ throw new Error("TOOL_METADATA.outputType must be a non-empty string.");
3126
+ }
3127
+ return parsed;
3128
+ }
3129
+ function loadCustomTools(folderPath) {
3130
+ const discovered = scanCustomTools(folderPath);
3131
+ const tools = /* @__PURE__ */ new Map();
3132
+ for (const { filePath, metadata } of discovered) {
3133
+ const config = {
3134
+ toolPath: filePath,
3135
+ name: metadata.name,
3136
+ description: metadata.description,
3137
+ inputs: metadata.inputs,
3138
+ outputType: metadata.outputType,
3139
+ timeout: metadata.timeout
3140
+ };
3141
+ tools.set(metadata.name, new ProxyTool(config));
3142
+ }
3143
+ return tools;
3144
+ }
3145
+
2796
3146
  // src/orchestrator/YAMLLoader.ts
2797
- var fs5 = __toESM(require("fs"));
2798
- var path5 = __toESM(require("path"));
3147
+ var fs7 = __toESM(require("fs"));
3148
+ var path8 = __toESM(require("path"));
2799
3149
  var import_yaml = __toESM(require("yaml"));
2800
3150
  var TOOL_REGISTRY = {
2801
3151
  read_file: ReadFileTool,
@@ -2808,21 +3158,30 @@ var TOOL_REGISTRY = {
2808
3158
  };
2809
3159
  var YAMLLoader = class {
2810
3160
  customTools = /* @__PURE__ */ new Map();
3161
+ toolInstances = /* @__PURE__ */ new Map();
2811
3162
  /**
2812
- * Register a custom tool type for use in YAML definitions.
3163
+ * Register a custom tool type (class) for use in YAML definitions.
2813
3164
  */
2814
3165
  registerToolType(typeName, toolClass) {
2815
3166
  this.customTools.set(typeName, toolClass);
2816
3167
  }
3168
+ /**
3169
+ * Register a pre-built tool instance for use in YAML definitions.
3170
+ * Used by the custom tool system to register ProxyTool instances
3171
+ * that are created by the scanner rather than by class instantiation.
3172
+ */
3173
+ registerToolInstance(typeName, tool) {
3174
+ this.toolInstances.set(typeName, tool);
3175
+ }
2817
3176
  /**
2818
3177
  * Load a workflow from a YAML file path.
2819
3178
  */
2820
3179
  loadFromFile(filePath) {
2821
- const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
2822
- if (!fs5.existsSync(absolutePath)) {
3180
+ const absolutePath = path8.isAbsolute(filePath) ? filePath : path8.resolve(process.cwd(), filePath);
3181
+ if (!fs7.existsSync(absolutePath)) {
2823
3182
  throw new Error(`Workflow file not found: ${absolutePath}`);
2824
3183
  }
2825
- const content = fs5.readFileSync(absolutePath, "utf-8");
3184
+ const content = fs7.readFileSync(absolutePath, "utf-8");
2826
3185
  return this.loadFromString(content);
2827
3186
  }
2828
3187
  /**
@@ -2897,9 +3256,16 @@ var YAMLLoader = class {
2897
3256
  * Build a tool instance from a type name and config.
2898
3257
  */
2899
3258
  buildTool(name, type, config) {
3259
+ const existingInstance = this.toolInstances.get(type);
3260
+ if (existingInstance) {
3261
+ if (name !== type && name !== existingInstance.name) {
3262
+ Object.defineProperty(existingInstance, "name", { value: name, writable: false });
3263
+ }
3264
+ return existingInstance;
3265
+ }
2900
3266
  const ToolClass = TOOL_REGISTRY[type] ?? this.customTools.get(type);
2901
3267
  if (!ToolClass) {
2902
- throw new Error(`Unknown tool type: ${type}. Available types: ${[...Object.keys(TOOL_REGISTRY), ...this.customTools.keys()].join(", ")}`);
3268
+ throw new Error(`Unknown tool type: ${type}. Available types: ${[...Object.keys(TOOL_REGISTRY), ...this.customTools.keys(), ...this.toolInstances.keys()].join(", ")}`);
2903
3269
  }
2904
3270
  const tool = new ToolClass(config);
2905
3271
  if (name !== type && name !== tool.name) {
@@ -2927,11 +3293,16 @@ var YAMLLoader = class {
2927
3293
  if (tool) {
2928
3294
  agentTools.push(tool);
2929
3295
  } else {
2930
- const ToolClass = TOOL_REGISTRY[toolName] ?? this.customTools.get(toolName);
2931
- if (ToolClass) {
2932
- agentTools.push(new ToolClass());
3296
+ const instance = this.toolInstances.get(toolName);
3297
+ if (instance) {
3298
+ agentTools.push(instance);
2933
3299
  } else {
2934
- throw new Error(`Tool "${toolName}" not found for agent "${name}"`);
3300
+ const ToolClass = TOOL_REGISTRY[toolName] ?? this.customTools.get(toolName);
3301
+ if (ToolClass) {
3302
+ agentTools.push(new ToolClass());
3303
+ } else {
3304
+ throw new Error(`Tool "${toolName}" not found for agent "${name}"`);
3305
+ }
2935
3306
  }
2936
3307
  }
2937
3308
  }
@@ -2982,39 +3353,117 @@ var YAMLLoader = class {
2982
3353
 
2983
3354
  // src/orchestrator/Orchestrator.ts
2984
3355
  var import_chalk2 = __toESM(require("chalk"));
3356
+
3357
+ // src/output/JSONOutputHandler.ts
3358
+ var JSONOutputHandler = class {
3359
+ runId;
3360
+ startTime;
3361
+ constructor(config) {
3362
+ this.runId = config.runId;
3363
+ this.startTime = Date.now();
3364
+ }
3365
+ emit(type, data, extra = {}) {
3366
+ console.log(JSON.stringify({
3367
+ runId: this.runId,
3368
+ timestamp: Date.now(),
3369
+ type,
3370
+ ...extra,
3371
+ data
3372
+ }));
3373
+ }
3374
+ emitRunStart(workflowPath, task, cwd) {
3375
+ this.emit("run_start", { workflowPath, task, cwd });
3376
+ }
3377
+ emitWorkflowLoaded(name, description, agents, tools, entrypoint) {
3378
+ this.emit("workflow_loaded", { name, description, agents, tools, entrypoint });
3379
+ }
3380
+ emitRunEnd(success, output, totalTokens, totalSteps) {
3381
+ this.emit("run_end", {
3382
+ success,
3383
+ output,
3384
+ totalDuration: Date.now() - this.startTime,
3385
+ totalTokens,
3386
+ totalSteps
3387
+ });
3388
+ }
3389
+ emitAgentStart(agentName, depth, task, agentType, maxSteps) {
3390
+ this.emit("agent_start", { task, agentType, maxSteps }, { agentName, depth });
3391
+ }
3392
+ emitAgentEnd(agentName, depth, output, totalSteps, tokenUsage, duration, success) {
3393
+ this.emit("agent_end", { output, totalSteps, tokenUsage, duration, success }, { agentName, depth });
3394
+ }
3395
+ emitAgentStep(agentName, depth, stepNumber, maxSteps, phase) {
3396
+ this.emit("agent_step", { stepNumber, maxSteps, phase }, { agentName, depth });
3397
+ }
3398
+ emitAgentThinking(agentName, depth, stepNumber, content, isPartial) {
3399
+ this.emit("agent_thinking", { stepNumber, content, isPartial }, { agentName, depth });
3400
+ }
3401
+ emitToolCall(agentName, depth, stepNumber, toolCallId, toolName, args) {
3402
+ this.emit("agent_tool_call", { stepNumber, toolCallId, toolName, arguments: args }, { agentName, depth });
3403
+ }
3404
+ emitToolResult(agentName, depth, stepNumber, toolCallId, toolName, result, error, duration) {
3405
+ this.emit("agent_tool_result", { stepNumber, toolCallId, toolName, result, error, duration }, { agentName, depth });
3406
+ }
3407
+ emitObservation(agentName, depth, stepNumber, observation, codeAction, logs) {
3408
+ this.emit("agent_observation", { stepNumber, observation, codeAction, logs }, { agentName, depth });
3409
+ }
3410
+ emitError(message, stack, agentName, depth, stepNumber) {
3411
+ this.emit("error", { message, stack, stepNumber }, {
3412
+ ...agentName ? { agentName } : {},
3413
+ ...depth !== void 0 ? { depth } : {}
3414
+ });
3415
+ }
3416
+ };
3417
+
3418
+ // src/orchestrator/Orchestrator.ts
2985
3419
  var Orchestrator = class {
2986
3420
  loader;
2987
3421
  config;
2988
3422
  activeAgents = /* @__PURE__ */ new Map();
2989
3423
  eventLog = [];
3424
+ jsonOutput = null;
3425
+ isJsonMode = false;
2990
3426
  constructor(config = {}) {
2991
3427
  this.loader = new YAMLLoader();
3428
+ this.isJsonMode = config.outputFormat === "json";
2992
3429
  this.config = {
2993
3430
  verbose: config.verbose ?? true,
2994
- onEvent: config.onEvent
3431
+ onEvent: config.onEvent,
3432
+ outputFormat: config.outputFormat ?? "text",
3433
+ runId: config.runId,
3434
+ cwd: config.cwd
2995
3435
  };
3436
+ if (this.isJsonMode) {
3437
+ if (!config.runId) {
3438
+ throw new Error("runId is required for JSON output mode");
3439
+ }
3440
+ this.jsonOutput = new JSONOutputHandler({
3441
+ runId: config.runId,
3442
+ verbose: config.verbose ?? true
3443
+ });
3444
+ }
2996
3445
  }
2997
3446
  /**
2998
3447
  * Load a workflow from a YAML file.
2999
3448
  */
3000
3449
  loadWorkflow(filePath) {
3001
3450
  const workflow = this.loader.loadFromFile(filePath);
3002
- this.displayWorkflowInfo(workflow);
3451
+ this.displayWorkflowInfo(workflow, filePath);
3003
3452
  return workflow;
3004
3453
  }
3005
3454
  /**
3006
3455
  * Load a workflow from YAML string.
3007
3456
  */
3008
- loadWorkflowFromString(yamlContent) {
3457
+ loadWorkflowFromString(yamlContent, sourcePath) {
3009
3458
  const workflow = this.loader.loadFromString(yamlContent);
3010
- this.displayWorkflowInfo(workflow);
3459
+ this.displayWorkflowInfo(workflow, sourcePath);
3011
3460
  return workflow;
3012
3461
  }
3013
3462
  /**
3014
3463
  * Run a loaded workflow with a task.
3015
3464
  */
3016
- async runWorkflow(workflow, task) {
3017
- this.displayRunStart(workflow.name, task);
3465
+ async runWorkflow(workflow, task, workflowPath) {
3466
+ this.displayRunStart(workflow.name, task, workflowPath);
3018
3467
  this.instrumentAgent(workflow.entrypointAgent, workflow.entrypointAgent.getName(), 0);
3019
3468
  for (const [name, agent] of workflow.agents) {
3020
3469
  if (agent !== workflow.entrypointAgent) {
@@ -3023,10 +3472,13 @@ var Orchestrator = class {
3023
3472
  }
3024
3473
  try {
3025
3474
  const result = await workflow.entrypointAgent.run(task);
3026
- this.displayRunEnd(result);
3475
+ this.displayRunEnd(result, true);
3027
3476
  return result;
3028
3477
  } catch (error) {
3029
3478
  this.displayError(error);
3479
+ if (this.isJsonMode && this.jsonOutput) {
3480
+ this.jsonOutput.emitRunEnd(false, null, 0, 0);
3481
+ }
3030
3482
  throw error;
3031
3483
  }
3032
3484
  }
@@ -3043,11 +3495,128 @@ var Orchestrator = class {
3043
3495
  */
3044
3496
  instrumentAgent(agent, name, depth) {
3045
3497
  this.activeAgents.set(name, { agent, depth });
3498
+ agent.setOnEvent((event) => {
3499
+ const orchestratorEvent = {
3500
+ type: event.type,
3501
+ agentName: name,
3502
+ depth,
3503
+ data: event.data,
3504
+ timestamp: Date.now()
3505
+ };
3506
+ this.logEvent(orchestratorEvent);
3507
+ if (this.isJsonMode && this.jsonOutput) {
3508
+ this.emitAgentEventAsJSON(event, name, depth);
3509
+ }
3510
+ });
3511
+ }
3512
+ /**
3513
+ * Emit an agent event as JSON.
3514
+ */
3515
+ emitAgentEventAsJSON(event, agentName, depth) {
3516
+ if (!this.jsonOutput) return;
3517
+ const data = event.data;
3518
+ switch (event.type) {
3519
+ case "agent_start":
3520
+ this.jsonOutput.emitAgentStart(
3521
+ agentName,
3522
+ depth,
3523
+ data.task,
3524
+ data.name ? "ToolUseAgent" : "CodeAgent",
3525
+ // Will be improved
3526
+ 20
3527
+ // Default maxSteps, could be passed
3528
+ );
3529
+ break;
3530
+ case "agent_step":
3531
+ this.jsonOutput.emitAgentStep(
3532
+ agentName,
3533
+ depth,
3534
+ data.step,
3535
+ data.maxSteps,
3536
+ "start"
3537
+ );
3538
+ break;
3539
+ case "agent_thinking":
3540
+ this.jsonOutput.emitAgentThinking(
3541
+ agentName,
3542
+ depth,
3543
+ data.step,
3544
+ data.content,
3545
+ false
3546
+ );
3547
+ break;
3548
+ case "agent_tool_call":
3549
+ this.jsonOutput.emitToolCall(
3550
+ agentName,
3551
+ depth,
3552
+ data.step,
3553
+ data.toolCallId,
3554
+ data.toolName,
3555
+ data.arguments
3556
+ );
3557
+ break;
3558
+ case "agent_tool_result":
3559
+ this.jsonOutput.emitToolResult(
3560
+ agentName,
3561
+ depth,
3562
+ data.step,
3563
+ data.toolCallId,
3564
+ data.toolName,
3565
+ data.result,
3566
+ data.error,
3567
+ data.duration
3568
+ );
3569
+ break;
3570
+ case "agent_observation":
3571
+ this.jsonOutput.emitObservation(
3572
+ agentName,
3573
+ depth,
3574
+ data.step,
3575
+ data.observation,
3576
+ data.codeAction,
3577
+ data.logs
3578
+ );
3579
+ break;
3580
+ case "agent_error":
3581
+ this.jsonOutput.emitError(
3582
+ data.error,
3583
+ void 0,
3584
+ agentName,
3585
+ depth,
3586
+ data.step
3587
+ );
3588
+ break;
3589
+ case "agent_end":
3590
+ this.jsonOutput.emitAgentEnd(
3591
+ agentName,
3592
+ depth,
3593
+ data.output,
3594
+ 0,
3595
+ // totalSteps - would need to track
3596
+ data.tokenUsage,
3597
+ data.duration,
3598
+ true
3599
+ );
3600
+ break;
3601
+ }
3046
3602
  }
3047
3603
  /**
3048
3604
  * Display workflow info at startup.
3049
3605
  */
3050
- displayWorkflowInfo(workflow) {
3606
+ displayWorkflowInfo(workflow, _sourcePath) {
3607
+ const agents = Array.from(workflow.agents.keys());
3608
+ const tools = Array.from(workflow.tools.keys());
3609
+ const entrypoint = workflow.entrypointAgent.getName();
3610
+ if (this.isJsonMode && this.jsonOutput) {
3611
+ this.jsonOutput.emitWorkflowLoaded(
3612
+ workflow.name,
3613
+ workflow.description,
3614
+ agents,
3615
+ tools,
3616
+ entrypoint
3617
+ );
3618
+ return;
3619
+ }
3051
3620
  if (!this.config.verbose) return;
3052
3621
  const line = "\u2550".repeat(70);
3053
3622
  console.log(import_chalk2.default.cyan(line));
@@ -3055,16 +3624,20 @@ var Orchestrator = class {
3055
3624
  if (workflow.description) {
3056
3625
  console.log(import_chalk2.default.cyan(` ${workflow.description}`));
3057
3626
  }
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()}`));
3627
+ console.log(import_chalk2.default.cyan(` Agents: ${agents.join(", ")}`));
3628
+ console.log(import_chalk2.default.cyan(` Tools: ${tools.join(", ") || "(none defined at workflow level)"}`));
3629
+ console.log(import_chalk2.default.cyan(` Entrypoint: ${entrypoint}`));
3061
3630
  console.log(import_chalk2.default.cyan(line));
3062
3631
  console.log();
3063
3632
  }
3064
3633
  /**
3065
3634
  * Display run start info.
3066
3635
  */
3067
- displayRunStart(workflowName, task) {
3636
+ displayRunStart(workflowName, task, workflowPath) {
3637
+ if (this.isJsonMode && this.jsonOutput) {
3638
+ this.jsonOutput.emitRunStart(workflowPath || workflowName, task, this.config.cwd);
3639
+ return;
3640
+ }
3068
3641
  if (!this.config.verbose) return;
3069
3642
  console.log(import_chalk2.default.green.bold(`
3070
3643
  \u25B6 Running workflow "${workflowName}"`));
@@ -3074,7 +3647,16 @@ var Orchestrator = class {
3074
3647
  /**
3075
3648
  * Display run completion info.
3076
3649
  */
3077
- displayRunEnd(result) {
3650
+ displayRunEnd(result, success = true) {
3651
+ if (this.isJsonMode && this.jsonOutput) {
3652
+ this.jsonOutput.emitRunEnd(
3653
+ success,
3654
+ result.output,
3655
+ result.tokenUsage.totalTokens,
3656
+ result.steps.length
3657
+ );
3658
+ return;
3659
+ }
3078
3660
  if (!this.config.verbose) return;
3079
3661
  console.log(import_chalk2.default.gray("\n" + "\u2500".repeat(70)));
3080
3662
  console.log(import_chalk2.default.green.bold(`
@@ -3092,6 +3674,10 @@ var Orchestrator = class {
3092
3674
  * Display an error.
3093
3675
  */
3094
3676
  displayError(error) {
3677
+ if (this.isJsonMode && this.jsonOutput) {
3678
+ this.jsonOutput.emitError(error.message, error.stack);
3679
+ return;
3680
+ }
3095
3681
  if (!this.config.verbose) return;
3096
3682
  console.error(import_chalk2.default.red.bold(`
3097
3683
  \u274C Workflow failed: ${error.message}`));
@@ -3120,6 +3706,24 @@ var Orchestrator = class {
3120
3706
  getLoader() {
3121
3707
  return this.loader;
3122
3708
  }
3709
+ /**
3710
+ * Get the JSON output handler (if in JSON mode).
3711
+ */
3712
+ getJSONOutputHandler() {
3713
+ return this.jsonOutput;
3714
+ }
3715
+ /**
3716
+ * Check if in JSON output mode.
3717
+ */
3718
+ isJSONOutputMode() {
3719
+ return this.isJsonMode;
3720
+ }
3721
+ /**
3722
+ * Get the run ID.
3723
+ */
3724
+ getRunId() {
3725
+ return this.config.runId;
3726
+ }
3123
3727
  };
3124
3728
  // Annotate the CommonJS export names for ESM import in node:
3125
3729
  0 && (module.exports = {
@@ -3134,11 +3738,13 @@ var Orchestrator = class {
3134
3738
  ExaSearchTool,
3135
3739
  FINAL_ANSWER_PROMPT,
3136
3740
  FinalAnswerTool,
3741
+ JSONOutputHandler,
3137
3742
  LocalExecutor,
3138
3743
  LogLevel,
3139
3744
  Model,
3140
3745
  OpenAIModel,
3141
3746
  Orchestrator,
3747
+ ProxyTool,
3142
3748
  ReadFileTool,
3143
3749
  Tool,
3144
3750
  ToolUseAgent,
@@ -3151,6 +3757,8 @@ var Orchestrator = class {
3151
3757
  formatToolDescriptions,
3152
3758
  generateSystemPrompt,
3153
3759
  generateToolUseSystemPrompt,
3154
- getErrorRecoveryPrompt
3760
+ getErrorRecoveryPrompt,
3761
+ loadCustomTools,
3762
+ scanCustomTools
3155
3763
  });
3156
3764
  //# sourceMappingURL=index.js.map