diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 12a4df0639e..bb5df684dc4 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -2473,12 +2473,14 @@ export class Task extends EventEmitter implements TaskLike { // Finalize the streaming tool call const finalToolUse = NativeToolCallParser.finalizeStreamingToolCall(event.id) + // Get the index for this tool call + const toolUseIndex = this.streamingToolCallIndices.get(event.id) + if (finalToolUse) { // Store the tool call ID ;(finalToolUse as any).id = event.id // Get the index and replace partial with final - const toolUseIndex = this.streamingToolCallIndices.get(event.id) if (toolUseIndex !== undefined) { this.assistantMessageContent[toolUseIndex] = finalToolUse } @@ -2491,6 +2493,25 @@ export class Task extends EventEmitter implements TaskLike { // Present the finalized tool call presentAssistantMessage(this) + } else if (toolUseIndex !== undefined) { + // finalizeStreamingToolCall returned null (malformed JSON or missing args) + // We still need to mark the tool as non-partial so it gets executed + // The tool's validation will catch any missing required parameters + const existingToolUse = this.assistantMessageContent[toolUseIndex] + if (existingToolUse && existingToolUse.type === "tool_use") { + existingToolUse.partial = false + // Ensure it has the ID for native protocol + ;(existingToolUse as any).id = event.id + } + + // Clean up tracking + this.streamingToolCallIndices.delete(event.id) + + // Mark that we have new content to process + this.userMessageContentReady = false + + // Present the tool call - validation will handle missing params + presentAssistantMessage(this) } } } @@ -2611,12 +2632,14 @@ export class Task extends EventEmitter implements TaskLike { // Finalize the streaming tool call const finalToolUse = NativeToolCallParser.finalizeStreamingToolCall(event.id) + // Get the index for this tool call + const toolUseIndex = this.streamingToolCallIndices.get(event.id) + if (finalToolUse) { // Store the tool call ID ;(finalToolUse as any).id = event.id // Get the index and replace partial with final - const toolUseIndex = this.streamingToolCallIndices.get(event.id) if (toolUseIndex !== undefined) { this.assistantMessageContent[toolUseIndex] = finalToolUse } @@ -2629,6 +2652,25 @@ export class Task extends EventEmitter implements TaskLike { // Present the finalized tool call presentAssistantMessage(this) + } else if (toolUseIndex !== undefined) { + // finalizeStreamingToolCall returned null (malformed JSON or missing args) + // We still need to mark the tool as non-partial so it gets executed + // The tool's validation will catch any missing required parameters + const existingToolUse = this.assistantMessageContent[toolUseIndex] + if (existingToolUse && existingToolUse.type === "tool_use") { + existingToolUse.partial = false + // Ensure it has the ID for native protocol + ;(existingToolUse as any).id = event.id + } + + // Clean up tracking + this.streamingToolCallIndices.delete(event.id) + + // Mark that we have new content to process + this.userMessageContentReady = false + + // Present the tool call - validation will handle missing params + presentAssistantMessage(this) } } } @@ -2898,9 +2940,11 @@ export class Task extends EventEmitter implements TaskLike { this.assistantMessageContent = parsedBlocks } - // Only present partial blocks that were just completed (from XML parsing) - // Native tool blocks were already presented during streaming, so don't re-present them - if (partialBlocks.length > 0 && partialBlocks.some((block) => block.type !== "tool_use")) { + // Present any partial blocks that were just completed + // For XML protocol: includes both text and tool_use blocks parsed from the text stream + // For native protocol: tool_use blocks were already presented during streaming via + // tool_call_partial events, but we still need to present them if they exist (e.g., malformed) + if (partialBlocks.length > 0) { // If there is content to update then it will complete and // update `this.userMessageContentReady` to true, which we // `pWaitFor` before making the next request.