Skip to content

Commit 2848f84

Browse files
committed
Get rid of exception catching in tools, they should rely on toolExecutor to catch and transform errors into LLM readable objects.
1 parent ca44fe7 commit 2848f84

File tree

19 files changed

+276
-441
lines changed

19 files changed

+276
-441
lines changed

packages/agent/src/core/executeToolCall.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,25 @@ export const executeToolCall = async (
3939
}
4040

4141
// TODO: validate JSON schema for input
42-
const output = await tool.execute(toolCall.input, toolContext);
42+
let output;
43+
try {
44+
output = await tool.execute(toolCall.input, toolContext);
45+
} catch (err) {
46+
if (err instanceof Error) {
47+
logger.error(err.message);
48+
return JSON.stringify({
49+
error: true,
50+
message: err.message,
51+
stack: err.stack,
52+
});
53+
} else {
54+
logger.error(err);
55+
return JSON.stringify({
56+
error: true,
57+
message: err,
58+
});
59+
}
60+
}
4361

4462
// for each result log it and its name
4563
if (tool.logReturns) {

packages/agent/src/core/toolAgent.test.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const toolContext: ToolContext = {
1414
userSession: false,
1515
pageFilter: 'simple',
1616
tokenTracker: new TokenTracker(),
17+
githubMode: false,
1718
};
1819

1920
// Mock tool for testing
@@ -98,16 +99,21 @@ describe('toolAgent', () => {
9899
});
99100

100101
it('should handle tool execution errors', async () => {
101-
await expect(
102-
executeToolCall(
103-
{
104-
id: '1',
105-
name: 'errorTool',
106-
input: {},
107-
},
108-
[errorTool],
109-
toolContext,
110-
),
111-
).rejects.toThrow('Deliberate failure');
102+
const result = await executeToolCall(
103+
{
104+
id: '1',
105+
name: 'errorTool',
106+
input: {},
107+
},
108+
[errorTool],
109+
toolContext,
110+
);
111+
112+
// Parse the result as JSON
113+
const parsedResult = JSON.parse(result);
114+
115+
// Check that it contains the expected error properties
116+
expect(parsedResult.error).toBe(true);
117+
expect(parsedResult.message).toContain('Deliberate failure');
112118
});
113119
});

packages/agent/src/core/toolAgent/config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { execSync } from 'child_process';
22

33
import { anthropic } from '@ai-sdk/anthropic';
44

5+
import { ToolContext } from '../types';
6+
57
/**
68
* Default configuration for the tool agent
79
*/
@@ -16,9 +18,7 @@ export const DEFAULT_CONFIG = {
1618
/**
1719
* Gets the default system prompt with contextual information about the environment
1820
*/
19-
export function getDefaultSystemPrompt(options?: {
20-
githubMode?: boolean;
21-
}): string {
21+
export function getDefaultSystemPrompt(toolContext: ToolContext): string {
2222
// Gather context with error handling
2323
const getCommandOutput = (command: string, label: string): string => {
2424
try {
@@ -33,7 +33,7 @@ export function getDefaultSystemPrompt(options?: {
3333
files: getCommandOutput('ls -la', 'file listing'),
3434
system: getCommandOutput('uname -a', 'system information'),
3535
datetime: new Date().toString(),
36-
githubMode: options?.githubMode || false,
36+
githubMode: toolContext.githubMode,
3737
};
3838

3939
const githubModeInstructions = context.githubMode
@@ -82,7 +82,7 @@ export function getDefaultSystemPrompt(options?: {
8282
'3. Update documentation as needed',
8383
'4. Consider adding documentation if you encountered setup/understanding challenges',
8484
'',
85-
'Feel free to use Google and Bing via the browser tools to search for information or for ideas when you get stuck.',
85+
'Feel free to use AI friendly search engines via the browser tools to search for information or for ideas when you get stuck.',
8686
'',
8787
'When you run into issues or unexpected results, take a step back and read the project documentation and configuration files and look at other source files in the project for examples of what works.',
8888
'',

packages/agent/src/core/toolAgent/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const toolAgent = async (
4343
logger.debug('User message:', initialPrompt);
4444

4545
// Get the system prompt once at the start
46-
const systemPrompt = config.getSystemPrompt();
46+
const systemPrompt = config.getSystemPrompt(context);
4747

4848
for (let i = 0; i < config.maxIterations; i++) {
4949
logger.verbose(

packages/agent/src/core/toolAgent/toolExecutor.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,18 @@ export async function executeTools(
4949
...context,
5050
tokenTracker: new TokenTracker(call.name, context.tokenTracker),
5151
});
52-
} catch (error: any) {
53-
toolResult = JSON.stringify({
54-
errorMessage: error.message,
55-
errorType: error.constructor.name,
56-
});
52+
} catch (errorStr: any) {
53+
if (errorStr instanceof Error) {
54+
if (errorStr.stack) {
55+
context.logger.error(`Tool error stack trace: ${errorStr.stack}`);
56+
}
57+
toolResult = JSON.stringify(errorStr);
58+
} else {
59+
toolResult = JSON.stringify({
60+
errorMessage: errorStr.message,
61+
errorType: errorStr.name,
62+
});
63+
}
5764
}
5865

5966
return {

packages/agent/src/core/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type ToolContext = {
1616
userSession: boolean;
1717
pageFilter: pageFilter;
1818
tokenTracker: TokenTracker;
19+
githubMode: boolean;
1920
};
2021

2122
export type Tool<TParams = Record<string, any>, TReturn = any> = {

packages/agent/src/tools/interaction/subAgent.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { anthropic } from '@ai-sdk/anthropic';
22
import { z } from 'zod';
33
import { zodToJsonSchema } from 'zod-to-json-schema';
44

5+
import { getDefaultSystemPrompt } from '../../core/toolAgent/index.js';
56
import { toolAgent } from '../../core/toolAgent.js';
6-
import { Tool } from '../../core/types.js';
7+
import { Tool, ToolContext } from '../../core/types.js';
78
import { getTools } from '../getTools.js';
89

910
const parameterSchema = z.object({
@@ -53,8 +54,9 @@ const subAgentConfig = {
5354
model: anthropic('claude-3-7-sonnet-20250219'),
5455
maxTokens: 4096,
5556
temperature: 0.7,
56-
getSystemPrompt: () => {
57+
getSystemPrompt: (context: ToolContext) => {
5758
return [
59+
getDefaultSystemPrompt(context),
5860
'You are a focused AI sub-agent handling a specific task.',
5961
'You have access to the same tools as the main agent but should focus only on your assigned task.',
6062
'When complete, call the sequenceComplete tool with your results.',

packages/agent/src/tools/io/textEditor.test.ts

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const toolContext: ToolContext = {
1919
userSession: false,
2020
pageFilter: 'simple',
2121
tokenTracker: new TokenTracker(),
22+
githubMode: false,
2223
};
2324

2425
describe('textEditor', () => {
@@ -259,18 +260,16 @@ describe('textEditor', () => {
259260
const testPath = join(testDir, `${randomUUID()}.txt`);
260261

261262
// Try to view a non-existent file
262-
const result = await textEditorTool.execute(
263-
{
264-
command: 'view',
265-
path: testPath,
266-
description: 'test',
267-
},
268-
toolContext,
269-
);
270-
271-
// Verify return value
272-
expect(result.success).toBe(false);
273-
expect(result.message).toContain('not found');
263+
await expect(async () => {
264+
await textEditorTool.execute(
265+
{
266+
command: 'view',
267+
path: testPath,
268+
description: 'test',
269+
},
270+
toolContext,
271+
);
272+
}).rejects.toThrow(/not found/);
274273
});
275274

276275
it('should handle errors for duplicate string replacements', async () => {
@@ -291,19 +290,17 @@ describe('textEditor', () => {
291290
);
292291

293292
// Try to replace text with multiple occurrences
294-
const result = await textEditorTool.execute(
295-
{
296-
command: 'str_replace',
297-
path: testPath,
298-
old_str: oldStr,
299-
new_str: newStr,
300-
description: 'test',
301-
},
302-
toolContext,
303-
);
304-
305-
// Verify return value
306-
expect(result.success).toBe(false);
307-
expect(result.message).toContain('Found 2 occurrences');
293+
await expect(async () => {
294+
await textEditorTool.execute(
295+
{
296+
command: 'str_replace',
297+
path: testPath,
298+
old_str: oldStr,
299+
new_str: newStr,
300+
description: 'test',
301+
},
302+
toolContext,
303+
);
304+
}).rejects.toThrow(/Found 2 occurrences/);
308305
});
309306
});

0 commit comments

Comments
 (0)