diff --git a/src/core/assistant-message/NativeToolCallParser.ts b/src/core/assistant-message/NativeToolCallParser.ts index bda7c71eb8d..a358c99ed7e 100644 --- a/src/core/assistant-message/NativeToolCallParser.ts +++ b/src/core/assistant-message/NativeToolCallParser.ts @@ -794,7 +794,7 @@ export class NativeToolCallParser { break case "apply_diff": - if (args.path !== undefined && args.diff !== undefined) { + if (args.path !== undefined || args.diff !== undefined) { nativeArgs = { path: args.path, diff: args.diff, @@ -805,8 +805,8 @@ export class NativeToolCallParser { case "edit": case "search_and_replace": if ( - args.file_path !== undefined && - args.old_string !== undefined && + args.file_path !== undefined || + args.old_string !== undefined || args.new_string !== undefined ) { nativeArgs = { @@ -819,7 +819,7 @@ export class NativeToolCallParser { break case "ask_followup_question": - if (args.question !== undefined && args.follow_up !== undefined) { + if (args.question !== undefined || args.follow_up !== undefined) { nativeArgs = { question: args.question, follow_up: args.follow_up, @@ -837,7 +837,7 @@ export class NativeToolCallParser { break case "generate_image": - if (args.prompt !== undefined && args.path !== undefined) { + if (args.prompt !== undefined || args.path !== undefined) { nativeArgs = { prompt: args.prompt, path: args.path, @@ -865,7 +865,7 @@ export class NativeToolCallParser { break case "search_files": - if (args.path !== undefined && args.regex !== undefined) { + if (args.path !== undefined || args.regex !== undefined) { nativeArgs = { path: args.path, regex: args.regex, @@ -875,7 +875,7 @@ export class NativeToolCallParser { break case "switch_mode": - if (args.mode_slug !== undefined && args.reason !== undefined) { + if (args.mode_slug !== undefined || args.reason !== undefined) { nativeArgs = { mode_slug: args.mode_slug, reason: args.reason, @@ -903,7 +903,7 @@ export class NativeToolCallParser { break case "write_to_file": - if (args.path !== undefined && args.content !== undefined) { + if (args.path !== undefined || args.content !== undefined) { nativeArgs = { path: args.path, content: args.content, @@ -912,7 +912,7 @@ export class NativeToolCallParser { break case "use_mcp_tool": - if (args.server_name !== undefined && args.tool_name !== undefined) { + if (args.server_name !== undefined || args.tool_name !== undefined) { nativeArgs = { server_name: args.server_name, tool_name: args.tool_name, @@ -922,7 +922,7 @@ export class NativeToolCallParser { break case "access_mcp_resource": - if (args.server_name !== undefined && args.uri !== undefined) { + if (args.server_name !== undefined || args.uri !== undefined) { nativeArgs = { server_name: args.server_name, uri: args.uri, @@ -940,8 +940,8 @@ export class NativeToolCallParser { case "search_replace": if ( - args.file_path !== undefined && - args.old_string !== undefined && + args.file_path !== undefined || + args.old_string !== undefined || args.new_string !== undefined ) { nativeArgs = { @@ -954,8 +954,8 @@ export class NativeToolCallParser { case "edit_file": if ( - args.file_path !== undefined && - args.old_string !== undefined && + args.file_path !== undefined || + args.old_string !== undefined || args.new_string !== undefined ) { nativeArgs = { @@ -977,7 +977,7 @@ export class NativeToolCallParser { break case "new_task": - if (args.mode !== undefined && args.message !== undefined) { + if (args.mode !== undefined || args.message !== undefined) { nativeArgs = { mode: args.mode, message: args.message, diff --git a/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts b/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts index 2c15e12069c..65bfd5c99ae 100644 --- a/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts +++ b/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts @@ -313,6 +313,91 @@ describe("NativeToolCallParser", () => { }) }) + describe("write_to_file tool", () => { + it("should parse write_to_file with both path and content", () => { + const toolCall = { + id: "toolu_wtf_1", + name: "write_to_file" as const, + arguments: JSON.stringify({ + path: "src/test.ts", + content: "console.log('hello')", + }), + } + + const result = NativeToolCallParser.parseToolCall(toolCall) + + expect(result).not.toBeNull() + expect(result?.type).toBe("tool_use") + if (result?.type === "tool_use") { + const nativeArgs = result.nativeArgs as { path: string; content: string } + expect(nativeArgs.path).toBe("src/test.ts") + expect(nativeArgs.content).toBe("console.log('hello')") + } + }) + + it("should parse write_to_file with missing content (truncated response)", () => { + const toolCall = { + id: "toolu_wtf_2", + name: "write_to_file" as const, + arguments: JSON.stringify({ + path: "src/test.ts", + }), + } + + const result = NativeToolCallParser.parseToolCall(toolCall) + + expect(result).not.toBeNull() + expect(result?.type).toBe("tool_use") + if (result?.type === "tool_use") { + const nativeArgs = result.nativeArgs as { path: string; content?: string } + expect(nativeArgs.path).toBe("src/test.ts") + expect(nativeArgs.content).toBeUndefined() + } + }) + + it("should parse write_to_file with missing path", () => { + const toolCall = { + id: "toolu_wtf_3", + name: "write_to_file" as const, + arguments: JSON.stringify({ + content: "console.log('hello')", + }), + } + + const result = NativeToolCallParser.parseToolCall(toolCall) + + expect(result).not.toBeNull() + expect(result?.type).toBe("tool_use") + if (result?.type === "tool_use") { + const nativeArgs = result.nativeArgs as { path?: string; content: string } + expect(nativeArgs.path).toBeUndefined() + expect(nativeArgs.content).toBe("console.log('hello')") + } + }) + }) + + describe("apply_diff tool with missing params", () => { + it("should parse apply_diff with missing diff (truncated response)", () => { + const toolCall = { + id: "toolu_ad_1", + name: "apply_diff" as const, + arguments: JSON.stringify({ + path: "src/test.ts", + }), + } + + const result = NativeToolCallParser.parseToolCall(toolCall) + + expect(result).not.toBeNull() + expect(result?.type).toBe("tool_use") + if (result?.type === "tool_use") { + const nativeArgs = result.nativeArgs as { path: string; diff?: string } + expect(nativeArgs.path).toBe("src/test.ts") + expect(nativeArgs.diff).toBeUndefined() + } + }) + }) + describe("finalizeStreamingToolCall", () => { describe("read_file tool", () => { it("should parse read_file args on finalize", () => {