From 071314e34d7917a714e4651c68efe303236c778c Mon Sep 17 00:00:00 2001 From: 0xMink Date: Wed, 11 Feb 2026 09:19:31 -0500 Subject: [PATCH] fix(native-tools): accept empty attempt_completion.result --- .../assistant-message/NativeToolCallParser.ts | 4 +- .../__tests__/NativeToolCallParser.spec.ts | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/core/assistant-message/NativeToolCallParser.ts b/src/core/assistant-message/NativeToolCallParser.ts index c8b96e35e31..4405d221bf8 100644 --- a/src/core/assistant-message/NativeToolCallParser.ts +++ b/src/core/assistant-message/NativeToolCallParser.ts @@ -449,7 +449,7 @@ export class NativeToolCallParser { break case "attempt_completion": - if (partialArgs.result) { + if (partialArgs.result !== undefined) { nativeArgs = { result: partialArgs.result } } break @@ -790,7 +790,7 @@ export class NativeToolCallParser { break case "attempt_completion": - if (args.result) { + if (args.result !== undefined) { nativeArgs = { result: args.result } as NativeArgsFor } break diff --git a/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts b/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts index db0dc00de41..72bec78b321 100644 --- a/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts +++ b/src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts @@ -293,6 +293,73 @@ describe("NativeToolCallParser", () => { }) }) + describe("parseToolCall - attempt_completion", () => { + it("should produce nativeArgs when result is an empty string", () => { + const toolCall = { + id: "toolu_empty_result", + name: "attempt_completion" as const, + arguments: JSON.stringify({ result: "" }), + } + + const result = NativeToolCallParser.parseToolCall(toolCall) + + expect(result).not.toBeNull() + expect(result?.type).toBe("tool_use") + if (result?.type === "tool_use") { + expect(result.nativeArgs).toBeDefined() + const nativeArgs = result.nativeArgs as { result: string } + expect(nativeArgs.result).toBe("") + } + }) + + it("should produce nativeArgs when result is a non-empty string", () => { + const toolCall = { + id: "toolu_normal_result", + name: "attempt_completion" as const, + arguments: JSON.stringify({ result: "Task completed successfully." }), + } + + const result = NativeToolCallParser.parseToolCall(toolCall) + + expect(result).not.toBeNull() + expect(result?.type).toBe("tool_use") + if (result?.type === "tool_use") { + expect(result.nativeArgs).toBeDefined() + const nativeArgs = result.nativeArgs as { result: string } + expect(nativeArgs.result).toBe("Task completed successfully.") + } + }) + + it("should return null when result is missing entirely", () => { + const toolCall = { + id: "toolu_no_result", + name: "attempt_completion" as const, + arguments: JSON.stringify({}), + } + + const result = NativeToolCallParser.parseToolCall(toolCall) + + expect(result).toBeNull() + }) + }) + + describe("processStreamingChunk - attempt_completion", () => { + it("should produce nativeArgs during streaming when result is an empty string", () => { + const id = "toolu_stream_empty_completion" + NativeToolCallParser.startStreamingToolCall(id, "attempt_completion") + + const result = NativeToolCallParser.processStreamingChunk( + id, + JSON.stringify({ result: "" }), + ) + + expect(result).not.toBeNull() + expect(result?.nativeArgs).toBeDefined() + const nativeArgs = result?.nativeArgs as { result: string } + expect(nativeArgs.result).toBe("") + }) + }) + describe("processStreamingChunk", () => { describe("read_file tool", () => { it("should emit a partial ToolUse with nativeArgs.path during streaming", () => {