diff --git a/src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts b/src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts new file mode 100644 index 00000000000..2c71dc78113 --- /dev/null +++ b/src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts @@ -0,0 +1,264 @@ +// npx vitest src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts + +import { describe, it, expect, beforeEach, vi } from "vitest" +import { presentAssistantMessage } from "../presentAssistantMessage" + +// Mock dependencies +vi.mock("../../task/Task") +vi.mock("../../tools/validateToolUse", () => ({ + validateToolUse: vi.fn(), +})) +vi.mock("@roo-code/telemetry", () => ({ + TelemetryService: { + instance: { + captureToolUsage: vi.fn(), + captureConsecutiveMistakeError: vi.fn(), + }, + }, +})) + +describe("presentAssistantMessage - Unknown Tool Handling", () => { + let mockTask: any + + beforeEach(() => { + // Create a mock Task with minimal properties needed for testing + mockTask = { + taskId: "test-task-id", + instanceId: "test-instance", + abort: false, + presentAssistantMessageLocked: false, + presentAssistantMessageHasPendingUpdates: false, + currentStreamingContentIndex: 0, + assistantMessageContent: [], + userMessageContent: [], + didCompleteReadingStream: false, + didRejectTool: false, + didAlreadyUseTool: false, + diffEnabled: false, + consecutiveMistakeCount: 0, + clineMessages: [], + api: { + getModel: () => ({ id: "test-model", info: {} }), + }, + browserSession: { + closeBrowser: vi.fn().mockResolvedValue(undefined), + }, + recordToolUsage: vi.fn(), + recordToolError: vi.fn(), + toolRepetitionDetector: { + check: vi.fn().mockReturnValue({ allowExecution: true }), + }, + providerRef: { + deref: () => ({ + getState: vi.fn().mockResolvedValue({ + mode: "code", + customModes: [], + }), + }), + }, + say: vi.fn().mockResolvedValue(undefined), + ask: vi.fn().mockResolvedValue({ response: "yesButtonClicked" }), + } + }) + + it("should return error for unknown tool in native protocol", async () => { + // Set up a tool_use block with an unknown tool name and an ID (native protocol) + const toolCallId = "tool_call_unknown_123" + mockTask.assistantMessageContent = [ + { + type: "tool_use", + id: toolCallId, // ID indicates native protocol + name: "nonexistent_tool", + params: { some: "param" }, + partial: false, + }, + ] + + // Execute presentAssistantMessage + await presentAssistantMessage(mockTask) + + // Verify that a tool_result with error was pushed + const toolResult = mockTask.userMessageContent.find( + (item: any) => item.type === "tool_result" && item.tool_use_id === toolCallId, + ) + + expect(toolResult).toBeDefined() + expect(toolResult.tool_use_id).toBe(toolCallId) + // The error is wrapped in JSON by formatResponse.toolError + expect(toolResult.content).toContain("nonexistent_tool") + expect(toolResult.content).toContain("does not exist") + expect(toolResult.content).toContain("error") + + // Verify consecutiveMistakeCount was incremented + expect(mockTask.consecutiveMistakeCount).toBe(1) + + // Verify recordToolError was called + expect(mockTask.recordToolError).toHaveBeenCalledWith( + "nonexistent_tool", + expect.stringContaining("Unknown tool"), + ) + + // Verify error message was shown to user (uses i18n key) + expect(mockTask.say).toHaveBeenCalledWith("error", "unknownToolError") + }) + + it("should return error for unknown tool in XML protocol", async () => { + // Set up a tool_use block with an unknown tool name WITHOUT an ID (XML protocol) + mockTask.assistantMessageContent = [ + { + type: "tool_use", + // No ID = XML protocol + name: "fake_tool_that_does_not_exist", + params: { param1: "value1" }, + partial: false, + }, + ] + + // Execute presentAssistantMessage + await presentAssistantMessage(mockTask) + + // For XML protocol, error is pushed as text blocks + const textBlocks = mockTask.userMessageContent.filter((item: any) => item.type === "text") + + // There should be text blocks with error message + expect(textBlocks.length).toBeGreaterThan(0) + const hasErrorMessage = textBlocks.some( + (block: any) => + block.text?.includes("fake_tool_that_does_not_exist") && block.text?.includes("does not exist"), + ) + expect(hasErrorMessage).toBe(true) + + // Verify consecutiveMistakeCount was incremented + expect(mockTask.consecutiveMistakeCount).toBe(1) + + // Verify recordToolError was called + expect(mockTask.recordToolError).toHaveBeenCalled() + + // Verify error message was shown to user (uses i18n key) + expect(mockTask.say).toHaveBeenCalledWith("error", "unknownToolError") + }) + + it("should handle unknown tool without freezing (native protocol)", async () => { + // This test ensures the extension doesn't freeze when an unknown tool is called + const toolCallId = "tool_call_freeze_test" + mockTask.assistantMessageContent = [ + { + type: "tool_use", + id: toolCallId, // Native protocol + name: "this_tool_definitely_does_not_exist", + params: {}, + partial: false, + }, + ] + + // The test will timeout if the extension freezes + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error("Test timed out - extension likely froze")), 5000) + }) + + const resultPromise = presentAssistantMessage(mockTask).then(() => true) + + // Race between the function completing and the timeout + const completed = await Promise.race([resultPromise, timeoutPromise]) + expect(completed).toBe(true) + + // Verify a tool_result was pushed (critical for API not to freeze) + const toolResult = mockTask.userMessageContent.find( + (item: any) => item.type === "tool_result" && item.tool_use_id === toolCallId, + ) + expect(toolResult).toBeDefined() + }) + + it("should increment consecutiveMistakeCount for unknown tools", async () => { + // Test with multiple unknown tools to ensure mistake count increments + const toolCallId = "tool_call_mistake_test" + mockTask.assistantMessageContent = [ + { + type: "tool_use", + id: toolCallId, + name: "unknown_tool_1", + params: {}, + partial: false, + }, + ] + + expect(mockTask.consecutiveMistakeCount).toBe(0) + + await presentAssistantMessage(mockTask) + + expect(mockTask.consecutiveMistakeCount).toBe(1) + }) + + it("should set userMessageContentReady after handling unknown tool", async () => { + const toolCallId = "tool_call_ready_test" + mockTask.assistantMessageContent = [ + { + type: "tool_use", + id: toolCallId, + name: "unknown_tool", + params: {}, + partial: false, + }, + ] + + mockTask.didCompleteReadingStream = true + mockTask.userMessageContentReady = false + + await presentAssistantMessage(mockTask) + + // userMessageContentReady should be set after processing + expect(mockTask.userMessageContentReady).toBe(true) + }) + + it("should still work with didAlreadyUseTool flag for unknown tool", async () => { + const toolCallId = "tool_call_already_used_test" + mockTask.assistantMessageContent = [ + { + type: "tool_use", + id: toolCallId, + name: "unknown_tool", + params: {}, + partial: false, + }, + ] + + mockTask.didAlreadyUseTool = true + + await presentAssistantMessage(mockTask) + + // When didAlreadyUseTool is true, should send error tool_result + const toolResult = mockTask.userMessageContent.find( + (item: any) => item.type === "tool_result" && item.tool_use_id === toolCallId, + ) + + expect(toolResult).toBeDefined() + expect(toolResult.is_error).toBe(true) + expect(toolResult.content).toContain("was not executed because a tool has already been used") + }) + + it("should still work with didRejectTool flag for unknown tool", async () => { + const toolCallId = "tool_call_rejected_test" + mockTask.assistantMessageContent = [ + { + type: "tool_use", + id: toolCallId, + name: "unknown_tool", + params: {}, + partial: false, + }, + ] + + mockTask.didRejectTool = true + + await presentAssistantMessage(mockTask) + + // When didRejectTool is true, should send error tool_result + const toolResult = mockTask.userMessageContent.find( + (item: any) => item.type === "tool_result" && item.tool_use_id === toolCallId, + ) + + expect(toolResult).toBeDefined() + expect(toolResult.is_error).toBe(true) + expect(toolResult.content).toContain("due to user rejecting a previous tool") + }) +}) diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index c21fbdb63e5..8089fdbdef4 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -8,6 +8,7 @@ import { TelemetryService } from "@roo-code/telemetry" import { defaultModeSlug, getModeBySlug } from "../../shared/modes" import type { ToolParamName, ToolResponse, ToolUse, McpToolUse } from "../../shared/tools" import { Package } from "../../shared/package" +import { t } from "../../i18n" import { fetchInstructionsTool } from "../tools/FetchInstructionsTool" import { listFilesTool } from "../tools/ListFilesTool" @@ -333,7 +334,11 @@ export async function presentAssistantMessage(cline: Task) { await cline.say("text", content, undefined, block.partial) break } - case "tool_use": + case "tool_use": { + // Fetch state early so it's available for toolDescription and validation + const state = await cline.providerRef.deref()?.getState() + const { mode, customModes, experiments: stateExperiments, apiConfiguration } = state ?? {} + const toolDescription = (): string => { switch (block.name) { case "execute_command": @@ -675,30 +680,46 @@ export async function presentAssistantMessage(cline: Task) { TelemetryService.instance.captureToolUsage(cline.taskId, block.name, toolProtocol) } - // Validate tool use before execution. - const { - mode, - customModes, - experiments: stateExperiments, - apiConfiguration, - } = (await cline.providerRef.deref()?.getState()) ?? {} - const modelInfo = cline.api.getModel() - const includedTools = modelInfo?.info?.includedTools - - try { - validateToolUse( - block.name as ToolName, - mode ?? defaultModeSlug, - customModes ?? [], - { apply_diff: cline.diffEnabled }, - block.params, - stateExperiments, - includedTools, - ) - } catch (error) { - cline.consecutiveMistakeCount++ - pushToolResult(formatResponse.toolError(error.message, toolProtocol)) - break + // Validate tool use before execution - ONLY for complete (non-partial) blocks. + // Validating partial blocks would cause validation errors to be thrown repeatedly + // during streaming, pushing multiple tool_results for the same tool_use_id and + // potentially causing the stream to appear frozen. + if (!block.partial) { + const modelInfo = cline.api.getModel() + const includedTools = modelInfo?.info?.includedTools + + try { + validateToolUse( + block.name as ToolName, + mode ?? defaultModeSlug, + customModes ?? [], + { apply_diff: cline.diffEnabled }, + block.params, + stateExperiments, + includedTools, + ) + } catch (error) { + cline.consecutiveMistakeCount++ + // For validation errors (unknown tool, tool not allowed for mode), we need to: + // 1. Send a tool_result with the error (required for native protocol) + // 2. NOT set didAlreadyUseTool = true (the tool was never executed, just failed validation) + // This prevents the stream from being interrupted with "Response interrupted by tool use result" + // which would cause the extension to appear to hang + const errorContent = formatResponse.toolError(error.message, toolProtocol) + if (toolProtocol === TOOL_PROTOCOL.NATIVE && toolCallId) { + // For native protocol, push tool_result directly without setting didAlreadyUseTool + cline.userMessageContent.push({ + type: "tool_result", + tool_use_id: toolCallId, + content: typeof errorContent === "string" ? errorContent : "(validation error)", + is_error: true, + } as Anthropic.ToolResultBlockParam) + } else { + // For XML protocol, use the standard pushToolResult + pushToolResult(errorContent) + } + break + } } // Check for identical consecutive tool calls. @@ -995,9 +1016,40 @@ export async function presentAssistantMessage(cline: Task) { toolProtocol, }) break + default: { + // Handle unknown/invalid tool names + // This is critical for native protocol where every tool_use MUST have a tool_result + // Note: This case should rarely be reached since validateToolUse now checks for unknown tools + + // CRITICAL: Don't process partial blocks for unknown tools - just let them stream in. + // If we try to show errors for partial blocks, we'd show the error on every streaming chunk, + // creating a loop that appears to freeze the extension. Only handle complete blocks. + if (block.partial) { + break + } + + const errorMessage = `Unknown tool "${block.name}". This tool does not exist. Please use one of the available tools.` + cline.consecutiveMistakeCount++ + cline.recordToolError(block.name as ToolName, errorMessage) + await cline.say("error", t("tools:unknownToolError", { toolName: block.name })) + // Push tool_result directly for native protocol WITHOUT setting didAlreadyUseTool + // This prevents the stream from being interrupted with "Response interrupted by tool use result" + if (toolProtocol === TOOL_PROTOCOL.NATIVE && toolCallId) { + cline.userMessageContent.push({ + type: "tool_result", + tool_use_id: toolCallId, + content: formatResponse.toolError(errorMessage, toolProtocol), + is_error: true, + } as Anthropic.ToolResultBlockParam) + } else { + pushToolResult(formatResponse.toolError(errorMessage, toolProtocol)) + } + break + } } break + } } // Seeing out of bounds is fine, it means that the next too call is being diff --git a/src/core/tools/__tests__/validateToolUse.spec.ts b/src/core/tools/__tests__/validateToolUse.spec.ts index ed41d286a54..91f32c1bf81 100644 --- a/src/core/tools/__tests__/validateToolUse.spec.ts +++ b/src/core/tools/__tests__/validateToolUse.spec.ts @@ -167,9 +167,17 @@ describe("mode-validator", () => { }) describe("validateToolUse", () => { - it("throws error for disallowed tools in architect mode", () => { + it("throws error for unknown/invalid tools", () => { + // Unknown tools should throw with a specific "Unknown tool" error expect(() => validateToolUse("unknown_tool" as any, "architect", [])).toThrow( - 'Tool "unknown_tool" is not allowed in architect mode.', + 'Unknown tool "unknown_tool". This tool does not exist.', + ) + }) + + it("throws error for disallowed tools in architect mode", () => { + // execute_command is a valid tool but not allowed in architect mode + expect(() => validateToolUse("execute_command", "architect", [])).toThrow( + 'Tool "execute_command" is not allowed in architect mode.', ) }) diff --git a/src/core/tools/validateToolUse.ts b/src/core/tools/validateToolUse.ts index a40b01cddec..a4ddf2ab77f 100644 --- a/src/core/tools/validateToolUse.ts +++ b/src/core/tools/validateToolUse.ts @@ -1,7 +1,27 @@ import type { ToolName, ModeConfig } from "@roo-code/types" +import { toolNames as validToolNames } from "@roo-code/types" import { Mode, isToolAllowedForMode } from "../../shared/modes" +/** + * Checks if a tool name is a valid, known tool. + * Note: This does NOT check if the tool is allowed for a specific mode, + * only that the tool actually exists. + */ +export function isValidToolName(toolName: string): toolName is ToolName { + // Check if it's a valid static tool + if ((validToolNames as readonly string[]).includes(toolName)) { + return true + } + + // Check if it's a dynamic MCP tool (mcp_serverName_toolName format) + if (toolName.startsWith("mcp_")) { + return true + } + + return false +} + export function validateToolUse( toolName: ToolName, mode: Mode, @@ -11,6 +31,15 @@ export function validateToolUse( experiments?: Record, includedTools?: string[], ): void { + // First, check if the tool name is actually a valid/known tool + // This catches completely invalid tool names like "edit_file" that don't exist + if (!isValidToolName(toolName)) { + throw new Error( + `Unknown tool "${toolName}". This tool does not exist. Please use one of the available tools: ${validToolNames.join(", ")}.`, + ) + } + + // Then check if the tool is allowed for the current mode if ( !isToolAllowedForMode( toolName, diff --git a/src/i18n/locales/ca/tools.json b/src/i18n/locales/ca/tools.json index f63bdea76e5..7e9385abf0b 100644 --- a/src/i18n/locales/ca/tools.json +++ b/src/i18n/locales/ca/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Fitxer d'imatge ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo sembla estar atrapat en un bucle, intentant la mateixa acció ({{toolName}}) repetidament. Això podria indicar un problema amb la seva estratègia actual. Considera reformular la tasca, proporcionar instruccions més específiques o guiar-lo cap a un enfocament diferent.", + "unknownToolError": "Roo ha intentat utilitzar una eina desconeguda: \"{{toolName}}\". Reintentant...", "codebaseSearch": { "approval": "Cercant '{{query}}' a la base de codi..." }, diff --git a/src/i18n/locales/de/tools.json b/src/i18n/locales/de/tools.json index 0effb1011a5..8dc5e93e702 100644 --- a/src/i18n/locales/de/tools.json +++ b/src/i18n/locales/de/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Bilddatei ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo scheint in einer Schleife festzustecken und versucht wiederholt dieselbe Aktion ({{toolName}}). Dies könnte auf ein Problem mit der aktuellen Strategie hindeuten. Überlege dir, die Aufgabe umzuformulieren, genauere Anweisungen zu geben oder Roo zu einem anderen Ansatz zu führen.", + "unknownToolError": "Roo hat versucht, ein unbekanntes Tool zu verwenden: \"{{toolName}}\". Wiederhole Versuch...", "codebaseSearch": { "approval": "Suche nach '{{query}}' im Codebase..." }, diff --git a/src/i18n/locales/en/tools.json b/src/i18n/locales/en/tools.json index 48dcbc24f12..94e1820249b 100644 --- a/src/i18n/locales/en/tools.json +++ b/src/i18n/locales/en/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Image file ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo appears to be stuck in a loop, attempting the same action ({{toolName}}) repeatedly. This might indicate a problem with its current strategy. Consider rephrasing the task, providing more specific instructions, or guiding it towards a different approach.", + "unknownToolError": "Roo tried to use an unknown tool: \"{{toolName}}\". Retrying...", "codebaseSearch": { "approval": "Searching for '{{query}}' in codebase..." }, diff --git a/src/i18n/locales/es/tools.json b/src/i18n/locales/es/tools.json index 9bd5d25c7d0..9103643cfc6 100644 --- a/src/i18n/locales/es/tools.json +++ b/src/i18n/locales/es/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Archivo de imagen ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo parece estar atrapado en un bucle, intentando la misma acción ({{toolName}}) repetidamente. Esto podría indicar un problema con su estrategia actual. Considera reformular la tarea, proporcionar instrucciones más específicas o guiarlo hacia un enfoque diferente.", + "unknownToolError": "Roo intentó usar una herramienta desconocida: \"{{toolName}}\". Reintentando...", "codebaseSearch": { "approval": "Buscando '{{query}}' en la base de código..." }, diff --git a/src/i18n/locales/fr/tools.json b/src/i18n/locales/fr/tools.json index 550cdbeb87c..df0a1136cbf 100644 --- a/src/i18n/locales/fr/tools.json +++ b/src/i18n/locales/fr/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Fichier image ({{size}} Ko)" }, "toolRepetitionLimitReached": "Roo semble être bloqué dans une boucle, tentant la même action ({{toolName}}) de façon répétée. Cela pourrait indiquer un problème avec sa stratégie actuelle. Envisage de reformuler la tâche, de fournir des instructions plus spécifiques ou de le guider vers une approche différente.", + "unknownToolError": "Roo a tenté d'utiliser un outil inconnu : \"{{toolName}}\". Nouvelle tentative...", "codebaseSearch": { "approval": "Recherche de '{{query}}' dans la base de code..." }, diff --git a/src/i18n/locales/hi/tools.json b/src/i18n/locales/hi/tools.json index 34ffea448be..73c7729a933 100644 --- a/src/i18n/locales/hi/tools.json +++ b/src/i18n/locales/hi/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "छवि फ़ाइल ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo एक लूप में फंसा हुआ लगता है, बार-बार एक ही क्रिया ({{toolName}}) को दोहरा रहा है। यह उसकी वर्तमान रणनीति में किसी समस्या का संकेत हो सकता है। कार्य को पुनः परिभाषित करने, अधिक विशिष्ट निर्देश देने, या उसे एक अलग दृष्टिकोण की ओर मार्गदर्शित करने पर विचार करें।", + "unknownToolError": "Roo ने एक अज्ञात उपकरण का उपयोग करने का प्रयास किया: \"{{toolName}}\"। पुनः प्रयास कर रहा है...", "codebaseSearch": { "approval": "कोडबेस में '{{query}}' खोज रहा है..." }, diff --git a/src/i18n/locales/id/tools.json b/src/i18n/locales/id/tools.json index bf3f076bd27..412711104b8 100644 --- a/src/i18n/locales/id/tools.json +++ b/src/i18n/locales/id/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "File gambar ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo tampaknya terjebak dalam loop, mencoba aksi yang sama ({{toolName}}) berulang kali. Ini mungkin menunjukkan masalah dengan strategi saat ini. Pertimbangkan untuk mengubah frasa tugas, memberikan instruksi yang lebih spesifik, atau mengarahkannya ke pendekatan yang berbeda.", + "unknownToolError": "Roo mencoba menggunakan alat yang tidak dikenal: \"{{toolName}}\". Mencoba lagi...", "codebaseSearch": { "approval": "Mencari '{{query}}' di codebase..." }, diff --git a/src/i18n/locales/it/tools.json b/src/i18n/locales/it/tools.json index 72439f0d29f..24022f52ae4 100644 --- a/src/i18n/locales/it/tools.json +++ b/src/i18n/locales/it/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "File immagine ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo sembra essere bloccato in un ciclo, tentando ripetutamente la stessa azione ({{toolName}}). Questo potrebbe indicare un problema con la sua strategia attuale. Considera di riformulare l'attività, fornire istruzioni più specifiche o guidarlo verso un approccio diverso.", + "unknownToolError": "Roo ha provato ad utilizzare uno strumento sconosciuto: \"{{toolName}}\". Nuovo tentativo...", "codebaseSearch": { "approval": "Ricerca di '{{query}}' nella base di codice..." }, diff --git a/src/i18n/locales/ja/tools.json b/src/i18n/locales/ja/tools.json index 66f04012a3e..f79ce9ac2f5 100644 --- a/src/i18n/locales/ja/tools.json +++ b/src/i18n/locales/ja/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "画像ファイル({{size}} KB)" }, "toolRepetitionLimitReached": "Rooが同じ操作({{toolName}})を繰り返し試みるループに陥っているようです。これは現在の方法に問題がある可能性を示しています。タスクの言い換え、より具体的な指示の提供、または別のアプローチへの誘導を検討してください。", + "unknownToolError": "Rooが不明なツールを使用しようとしました:「{{toolName}}」。再試行中...", "codebaseSearch": { "approval": "コードベースで '{{query}}' を検索中..." }, diff --git a/src/i18n/locales/ko/tools.json b/src/i18n/locales/ko/tools.json index f17cafb62da..88cf28cdcc1 100644 --- a/src/i18n/locales/ko/tools.json +++ b/src/i18n/locales/ko/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "이미지 파일 ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo가 같은 동작({{toolName}})을 반복적으로 시도하면서 루프에 갇힌 것 같습니다. 이는 현재 전략에 문제가 있을 수 있음을 나타냅니다. 작업을 다시 표현하거나, 더 구체적인 지침을 제공하거나, 다른 접근 방식으로 안내해 보세요.", + "unknownToolError": "Roo가 알 수 없는 도구를 사용하려고 했습니다: \"{{toolName}}\". 다시 시도 중...", "codebaseSearch": { "approval": "코드베이스에서 '{{query}}' 검색 중..." }, diff --git a/src/i18n/locales/nl/tools.json b/src/i18n/locales/nl/tools.json index 48d9a8c4c9f..d696a19937e 100644 --- a/src/i18n/locales/nl/tools.json +++ b/src/i18n/locales/nl/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Afbeeldingsbestand ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo lijkt vast te zitten in een lus, waarbij hij herhaaldelijk dezelfde actie ({{toolName}}) probeert. Dit kan duiden op een probleem met de huidige strategie. Overweeg de taak te herformuleren, specifiekere instructies te geven of Roo naar een andere aanpak te leiden.", + "unknownToolError": "Roo probeerde een onbekende tool te gebruiken: \"{{toolName}}\". Opnieuw proberen...", "codebaseSearch": { "approval": "Zoeken naar '{{query}}' in codebase..." }, diff --git a/src/i18n/locales/pl/tools.json b/src/i18n/locales/pl/tools.json index 6969bc5a524..d7c0d18c148 100644 --- a/src/i18n/locales/pl/tools.json +++ b/src/i18n/locales/pl/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Plik obrazu ({{size}} KB)" }, "toolRepetitionLimitReached": "Wygląda na to, że Roo utknął w pętli, wielokrotnie próbując wykonać tę samą akcję ({{toolName}}). Może to wskazywać na problem z jego obecną strategią. Rozważ przeformułowanie zadania, podanie bardziej szczegółowych instrukcji lub nakierowanie go na inne podejście.", + "unknownToolError": "Roo próbował użyć nieznanego narzędzia: \"{{toolName}}\". Ponowna próba...", "codebaseSearch": { "approval": "Wyszukiwanie '{{query}}' w bazie kodu..." }, diff --git a/src/i18n/locales/pt-BR/tools.json b/src/i18n/locales/pt-BR/tools.json index e19be77fa58..e8da6dae7f6 100644 --- a/src/i18n/locales/pt-BR/tools.json +++ b/src/i18n/locales/pt-BR/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Arquivo de imagem ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo parece estar preso em um loop, tentando a mesma ação ({{toolName}}) repetidamente. Isso pode indicar um problema com sua estratégia atual. Considere reformular a tarefa, fornecer instruções mais específicas ou guiá-lo para uma abordagem diferente.", + "unknownToolError": "Roo tentou usar uma ferramenta desconhecida: \"{{toolName}}\". Tentando novamente...", "codebaseSearch": { "approval": "Pesquisando '{{query}}' na base de código..." }, diff --git a/src/i18n/locales/ru/tools.json b/src/i18n/locales/ru/tools.json index c25b3b26bfd..0e4e1466055 100644 --- a/src/i18n/locales/ru/tools.json +++ b/src/i18n/locales/ru/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Файл изображения ({{size}} КБ)" }, "toolRepetitionLimitReached": "Похоже, что Roo застрял в цикле, многократно пытаясь выполнить одно и то же действие ({{toolName}}). Это может указывать на проблему с его текущей стратегией. Попробуйте переформулировать задачу, предоставить более конкретные инструкции или направить его к другому подходу.", + "unknownToolError": "Roo попытался использовать неизвестный инструмент: \"{{toolName}}\". Повторная попытка...", "codebaseSearch": { "approval": "Поиск '{{query}}' в кодовой базе..." }, diff --git a/src/i18n/locales/tr/tools.json b/src/i18n/locales/tr/tools.json index 03ab3840091..2f4263b20da 100644 --- a/src/i18n/locales/tr/tools.json +++ b/src/i18n/locales/tr/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Görüntü dosyası ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo bir döngüye takılmış gibi görünüyor, aynı eylemi ({{toolName}}) tekrar tekrar deniyor. Bu, mevcut stratejisinde bir sorun olduğunu gösterebilir. Görevi yeniden ifade etmeyi, daha spesifik talimatlar vermeyi veya onu farklı bir yaklaşıma yönlendirmeyi düşünün.", + "unknownToolError": "Roo bilinmeyen bir araç kullanmaya çalıştı: \"{{toolName}}\". Yeniden deneniyor...", "codebaseSearch": { "approval": "Kod tabanında '{{query}}' aranıyor..." }, diff --git a/src/i18n/locales/vi/tools.json b/src/i18n/locales/vi/tools.json index 3fb845cac8d..4bccf1ff48c 100644 --- a/src/i18n/locales/vi/tools.json +++ b/src/i18n/locales/vi/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "Tệp hình ảnh ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo dường như đang bị mắc kẹt trong một vòng lặp, liên tục cố gắng thực hiện cùng một hành động ({{toolName}}). Điều này có thể cho thấy vấn đề với chiến lược hiện tại. Hãy cân nhắc việc diễn đạt lại nhiệm vụ, cung cấp hướng dẫn cụ thể hơn, hoặc hướng Roo theo một cách tiếp cận khác.", + "unknownToolError": "Roo đã cố gắng sử dụng một công cụ không xác định: \"{{toolName}}\". Đang thử lại...", "codebaseSearch": { "approval": "Đang tìm kiếm '{{query}}' trong cơ sở mã..." }, diff --git a/src/i18n/locales/zh-CN/tools.json b/src/i18n/locales/zh-CN/tools.json index fe9f50a296c..886706ff85f 100644 --- a/src/i18n/locales/zh-CN/tools.json +++ b/src/i18n/locales/zh-CN/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "图片文件 ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo 似乎陷入循环,反复尝试同一操作 ({{toolName}})。这可能表明当前策略存在问题。请考虑重新描述任务、提供更具体的指示或引导其尝试不同的方法。", + "unknownToolError": "Roo 尝试使用未知工具:\"{{toolName}}\"。正在重试...", "codebaseSearch": { "approval": "正在搜索代码库中的 '{{query}}'..." }, diff --git a/src/i18n/locales/zh-TW/tools.json b/src/i18n/locales/zh-TW/tools.json index 1a380bcbeaa..ebd0f104ad1 100644 --- a/src/i18n/locales/zh-TW/tools.json +++ b/src/i18n/locales/zh-TW/tools.json @@ -7,6 +7,7 @@ "imageWithSize": "圖片檔案 ({{size}} KB)" }, "toolRepetitionLimitReached": "Roo 似乎陷入循環,反覆嘗試同一操作 ({{toolName}})。這可能表明目前策略存在問題。請考慮重新描述工作、提供更具體的指示或引導其嘗試不同的方法。", + "unknownToolError": "Roo 嘗試使用未知工具:「{{toolName}}」。正在重試...", "codebaseSearch": { "approval": "正在搜尋程式碼庫中的「{{query}}」..." },