@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/README.md +407 -154
- package/dist/cli.js +826 -133
- package/dist/cli.js.map +1 -1
- package/dist/index.d.mts +320 -9
- package/dist/index.d.ts +320 -9
- package/dist/index.js +727 -123
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +722 -122
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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((
|
|
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((
|
|
1464
|
+
return new Promise((resolve6) => {
|
|
1449
1465
|
rl.question(`
|
|
1450
1466
|
[Agent asks]: ${question}
|
|
1451
1467
|
Your response: `, (answer) => {
|
|
1452
1468
|
rl.close();
|
|
1453
|
-
|
|
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
|
|
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
|
|
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", {
|
|
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:
|
|
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
|
|
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
|
-
|
|
2730
|
+
instructions: {
|
|
2669
2731
|
type: "string",
|
|
2670
|
-
description: "
|
|
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
|
-
|
|
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: '
|
|
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
|
-
|
|
2690
|
-
type: "
|
|
2691
|
-
description: "
|
|
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
|
|
2709
|
-
const
|
|
2710
|
-
const
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
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 (
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
const
|
|
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(
|
|
2784
|
+
body: JSON.stringify(createBody)
|
|
2726
2785
|
});
|
|
2727
|
-
if (!
|
|
2728
|
-
const errorText = await
|
|
2729
|
-
throw new Error(`
|
|
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
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
let
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
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 ("
|
|
2779
|
-
|
|
2838
|
+
if (statusData.status === "canceled") {
|
|
2839
|
+
throw new Error("Research was canceled");
|
|
2780
2840
|
}
|
|
2781
|
-
if (
|
|
2782
|
-
|
|
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
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
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
|
-
|
|
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
|
|
2798
|
-
var
|
|
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 =
|
|
2822
|
-
if (!
|
|
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 =
|
|
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
|
|
2931
|
-
if (
|
|
2932
|
-
agentTools.push(
|
|
3292
|
+
const instance = this.toolInstances.get(toolName);
|
|
3293
|
+
if (instance) {
|
|
3294
|
+
agentTools.push(instance);
|
|
2933
3295
|
} else {
|
|
2934
|
-
|
|
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: ${
|
|
3059
|
-
console.log(import_chalk2.default.cyan(` Tools: ${
|
|
3060
|
-
console.log(import_chalk2.default.cyan(` Entrypoint: ${
|
|
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
|