Skip to content

Commit 3117f1a

Browse files
committed
Emit error instead of tool call if agent doesn't have access to tool
1 parent bbc5faa commit 3117f1a

File tree

2 files changed

+45
-48
lines changed

2 files changed

+45
-48
lines changed

packages/agent-runtime/src/__tests__/main-prompt.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe('mainPrompt', () => {
6565
includeMessageHistory: true,
6666
inheritParentSystemPrompt: false,
6767
mcpServers: {},
68-
toolNames: ['write_file', 'run_terminal_command'],
68+
toolNames: ['write_file', 'run_terminal_command', 'end_turn'],
6969
spawnableAgents: [],
7070
systemPrompt: '',
7171
instructionsPrompt: '',
@@ -81,7 +81,7 @@ describe('mainPrompt', () => {
8181
includeMessageHistory: true,
8282
inheritParentSystemPrompt: false,
8383
mcpServers: {},
84-
toolNames: ['write_file', 'run_terminal_command'],
84+
toolNames: ['write_file', 'run_terminal_command', 'end_turn'],
8585
spawnableAgents: [],
8686
systemPrompt: '',
8787
instructionsPrompt: '',
@@ -240,7 +240,7 @@ describe('mainPrompt', () => {
240240
includeMessageHistory: true,
241241
inheritParentSystemPrompt: false,
242242
mcpServers: {},
243-
toolNames: ['write_file', 'run_terminal_command'],
243+
toolNames: ['write_file', 'run_terminal_command', 'end_turn'],
244244
spawnableAgents: [],
245245
systemPrompt: '',
246246
instructionsPrompt: '',
@@ -256,7 +256,7 @@ describe('mainPrompt', () => {
256256
includeMessageHistory: true,
257257
inheritParentSystemPrompt: false,
258258
mcpServers: {},
259-
toolNames: ['write_file', 'run_terminal_command'],
259+
toolNames: ['write_file', 'run_terminal_command', 'end_turn'],
260260
spawnableAgents: [],
261261
systemPrompt: '',
262262
instructionsPrompt: '',

packages/agent-runtime/src/tools/tool-executor.ts

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,6 @@ export function executeToolCall<T extends ToolName>(
181181
requestToolCall,
182182
} = params
183183
const toolCallId = params.toolCallId ?? generateCompactId()
184-
onResponseChunk({
185-
type: 'tool_call',
186-
toolCallId,
187-
toolName,
188-
input,
189-
agentId: agentState.agentId,
190-
parentAgentId: agentState.parentId,
191-
includeToolCall: !excludeToolFromMessageHistory,
192-
})
193184

194185
const toolCall: CodebuffToolCall<T> | ToolCallError = parseRawToolCall<T>({
195186
rawToolCall: {
@@ -217,26 +208,34 @@ export function executeToolCall<T extends ToolName>(
217208
return previousToolCallFinished
218209
}
219210

220-
toolCalls.push(toolCall)
221-
222-
// Filter out restricted tools
211+
// Filter out restricted tools - emit error instead of tool call/result
212+
// This prevents the CLI from showing tool calls that the agent doesn't have permission to use
223213
if (
224214
!agentTemplate.toolNames.includes(toolCall.toolName) &&
225215
!fromHandleSteps
226216
) {
227-
const toolResult: ToolMessage = {
228-
role: 'tool',
229-
toolName,
230-
toolCallId: toolCall.toolCallId,
231-
content: jsonToolResult({
232-
errorMessage: `Tool \`${toolName}\` is not currently available. Make sure to only use tools listed in the system instructions.`,
233-
}),
234-
}
235-
toolResults.push(cloneDeep(toolResult))
236-
toolResultsToAddAfterStream.push(cloneDeep(toolResult))
217+
// Emit an error event instead of tool call/result pair
218+
// The stream parser will convert this to a user message for proper API compliance
219+
onResponseChunk({
220+
type: 'error',
221+
message: `Tool \`${toolName}\` is not currently available. Make sure to only use tools listed in the system instructions.`,
222+
})
237223
return previousToolCallFinished
238224
}
239225

226+
// Only emit tool_call event after permission check passes
227+
onResponseChunk({
228+
type: 'tool_call',
229+
toolCallId,
230+
toolName,
231+
input,
232+
agentId: agentState.agentId,
233+
parentAgentId: agentState.parentId,
234+
includeToolCall: !excludeToolFromMessageHistory,
235+
})
236+
237+
toolCalls.push(toolCall)
238+
240239
// Cast to any to avoid type errors
241240
const handler = codebuffToolHandlers[
242241
toolName
@@ -418,6 +417,26 @@ export async function executeCustomToolCall(
418417
return previousToolCallFinished
419418
}
420419

420+
// Filter out restricted tools - emit error instead of tool call/result
421+
// This prevents the CLI from showing tool calls that the agent doesn't have permission to use
422+
if (
423+
!(agentTemplate.toolNames as string[]).includes(toolCall.toolName) &&
424+
!fromHandleSteps &&
425+
!(
426+
toolCall.toolName.includes('/') &&
427+
toolCall.toolName.split('/')[0] in agentTemplate.mcpServers
428+
)
429+
) {
430+
// Emit an error event instead of tool call/result pair
431+
// The stream parser will convert this to a user message for proper API compliance
432+
onResponseChunk({
433+
type: 'error',
434+
message: `Tool \`${toolName}\` is not currently available. Make sure to only use tools listed in the system instructions.`,
435+
})
436+
return previousToolCallFinished
437+
}
438+
439+
// Only emit tool_call event after permission check passes
421440
onResponseChunk({
422441
type: 'tool_call',
423442
toolCallId: toolCall.toolCallId,
@@ -431,28 +450,6 @@ export async function executeCustomToolCall(
431450

432451
toolCalls.push(toolCall)
433452

434-
// Filter out restricted tools in ask mode unless exporting summary
435-
if (
436-
!(agentTemplate.toolNames as string[]).includes(toolCall.toolName) &&
437-
!fromHandleSteps &&
438-
!(
439-
toolCall.toolName.includes('/') &&
440-
toolCall.toolName.split('/')[0] in agentTemplate.mcpServers
441-
)
442-
) {
443-
const toolResult: ToolMessage = {
444-
role: 'tool',
445-
toolName,
446-
toolCallId: toolCall.toolCallId,
447-
content: jsonToolResult({
448-
errorMessage: `Tool \`${toolName}\` is not currently available. Make sure to only use tools listed in the system instructions.`,
449-
}),
450-
}
451-
toolResults.push(cloneDeep(toolResult))
452-
toolResultsToAddAfterStream.push(cloneDeep(toolResult))
453-
return previousToolCallFinished
454-
}
455-
456453
return previousToolCallFinished
457454
.then(async () => {
458455
if (!checkLiveUserInput(params)) {

0 commit comments

Comments
 (0)