fix: accept empty string result in attempt_completion tool

Fixes #11404 (partial)

The attempt_completion tool's nativeArgs parsing used a truthy check
(if (args.result)) which rejected empty strings as invalid. Changed
to an existence check (!== undefined) in both streaming and finalize
parse paths.

Added regression tests for:
- Empty string result in parseToolCall
- Non-empty result in parseToolCall
- Empty string result in streaming path
This commit is contained in:
Ritwik 2026-02-19 22:15:44 -08:00
parent 0d5b932d2e
commit 9b3e7ba70a
2 changed files with 57 additions and 2 deletions

View file

@ -449,7 +449,7 @@ export class NativeToolCallParser {
break
case "attempt_completion":
if (partialArgs.result) {
if (partialArgs.result !== undefined) {
nativeArgs = { result: partialArgs.result }
}
break
@ -778,7 +778,7 @@ export class NativeToolCallParser {
break
case "attempt_completion":
if (args.result) {
if (args.result !== undefined) {
nativeArgs = { result: args.result } as NativeArgsFor<TName>
}
break

View file

@ -343,4 +343,59 @@ describe("NativeToolCallParser", () => {
})
})
})
describe("attempt_completion tool", () => {
it("should parse attempt_completion with empty string result", () => {
// Regression test for issue #11404: empty string result should be valid
const toolCall = {
id: "toolu_completion_empty",
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 parse attempt_completion with non-empty result", () => {
const toolCall = {
id: "toolu_completion_full",
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 handle streaming attempt_completion with empty string result", () => {
const id = "toolu_streaming_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("")
})
})
})