From a12e22c66fae55bb929adea5478e1f6ba4609894 Mon Sep 17 00:00:00 2001 From: Skyuno Date: Sun, 1 Feb 2026 20:23:13 +0800 Subject: [PATCH] Revert "Merge pull request #150 from PancakeZik/fix/write-tool-truncation-handling" This reverts commit fd5b669c87a97a694c30c3a84599f880b7760f61, reversing changes made to 30d832c9b105d26b05df0fffc7cf7c29c54ccea4. --- internal/runtime/executor/kiro_executor.go | 84 +---------------- .../kiro/claude/kiro_claude_tools.go | 91 ------------------- 2 files changed, 2 insertions(+), 173 deletions(-) diff --git a/internal/runtime/executor/kiro_executor.go b/internal/runtime/executor/kiro_executor.go index 47a04130..f85e68cb 100644 --- a/internal/runtime/executor/kiro_executor.go +++ b/internal/runtime/executor/kiro_executor.go @@ -2442,8 +2442,8 @@ func (e *KiroExecutor) extractEventTypeFromBytes(headers []byte) string { func (e *KiroExecutor) streamToChannel(ctx context.Context, body io.Reader, out chan<- cliproxyexecutor.StreamChunk, targetFormat sdktranslator.Format, model string, originalReq, claudeBody []byte, reporter *usageReporter, thinkingEnabled bool) { reader := bufio.NewReaderSize(body, 20*1024*1024) // 20MB buffer to match other providers var totalUsage usage.Detail - var hasToolUses bool // Track if any tool uses were emitted - var upstreamStopReason string // Track stop_reason from upstream events + var hasToolUses bool // Track if any tool uses were emitted + var upstreamStopReason string // Track stop_reason from upstream events // Tool use state tracking for input buffering and deduplication processedIDs := make(map[string]bool) @@ -3221,92 +3221,12 @@ func (e *KiroExecutor) streamToChannel(ctx context.Context, body io.Reader, out _ = signature // Signature can be used for verification if needed case "toolUseEvent": - // Debug: log raw toolUseEvent payload for large tool inputs - if log.IsLevelEnabled(log.DebugLevel) { - payloadStr := string(payload) - if len(payloadStr) > 500 { - payloadStr = payloadStr[:500] + "...[truncated]" - } - log.Debugf("kiro: raw toolUseEvent payload (%d bytes): %s", len(payload), payloadStr) - } // Handle dedicated tool use events with input buffering completedToolUses, newState := kiroclaude.ProcessToolUseEvent(event, currentToolUse, processedIDs) currentToolUse = newState // Emit completed tool uses for _, tu := range completedToolUses { - // Check for truncated write marker - emit as a Bash tool that echoes the error - // This way Claude Code will execute it, see the error, and the agent can retry - if tu.Name == "__truncated_write__" { - filePath := "" - if fp, ok := tu.Input["file_path"].(string); ok && fp != "" { - filePath = fp - } - - // Create a Bash tool that echoes the error message - // This will be executed by Claude Code and the agent will see the result - var errorMsg string - if filePath != "" { - errorMsg = fmt.Sprintf("echo '[WRITE TOOL ERROR] The file content for \"%s\" is too large to be transmitted by the upstream API. You MUST retry by writing the file in smaller chunks: First use Write to create the file with the first 700 lines, then use multiple Edit operations to append the remaining content in chunks of ~700 lines each.'", filePath) - } else { - errorMsg = "echo '[WRITE TOOL ERROR] The file content is too large to be transmitted by the upstream API. The Write tool input was truncated. You MUST retry by writing the file in smaller chunks: First use Write to create the file with the first 700 lines, then use multiple Edit operations to append the remaining content in chunks of ~700 lines each.'" - } - - log.Warnf("kiro: converting truncated write to Bash echo for file: %s", filePath) - - hasToolUses = true - - // Close text block if open - if isTextBlockOpen && contentBlockIndex >= 0 { - blockStop := kiroclaude.BuildClaudeContentBlockStopEvent(contentBlockIndex) - sseData := sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, blockStop, &translatorParam) - for _, chunk := range sseData { - if chunk != "" { - out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")} - } - } - isTextBlockOpen = false - } - - contentBlockIndex++ - - // Emit as Bash tool_use - blockStart := kiroclaude.BuildClaudeContentBlockStartEvent(contentBlockIndex, "tool_use", tu.ToolUseID, "Bash") - sseData := sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, blockStart, &translatorParam) - for _, chunk := range sseData { - if chunk != "" { - out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")} - } - } - - // Emit the Bash command as input - bashInput := map[string]interface{}{ - "command": errorMsg, - } - inputJSON, err := json.Marshal(bashInput) - if err != nil { - log.Errorf("kiro: failed to marshal bash input for truncated write error: %v", err) - continue - } - inputDelta := kiroclaude.BuildClaudeInputJsonDeltaEvent(string(inputJSON), contentBlockIndex) - sseData = sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, inputDelta, &translatorParam) - for _, chunk := range sseData { - if chunk != "" { - out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")} - } - } - - blockStop := kiroclaude.BuildClaudeContentBlockStopEvent(contentBlockIndex) - sseData = sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, blockStop, &translatorParam) - for _, chunk := range sseData { - if chunk != "" { - out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")} - } - } - - continue // Skip the normal tool_use emission - } - hasToolUses = true // Close text block if open diff --git a/internal/translator/kiro/claude/kiro_claude_tools.go b/internal/translator/kiro/claude/kiro_claude_tools.go index 6020a8a4..93ede875 100644 --- a/internal/translator/kiro/claude/kiro_claude_tools.go +++ b/internal/translator/kiro/claude/kiro_claude_tools.go @@ -395,17 +395,6 @@ func ProcessToolUseEvent(event map[string]interface{}, currentToolUse *ToolUseSt isStop = stop } - // Debug: log when stop event arrives - if isStop { - log.Debugf("kiro: toolUseEvent stop=true received for tool %s (ID: %s), currentToolUse buffer len: %d", - toolName, toolUseID, func() int { - if currentToolUse != nil { - return currentToolUse.InputBuffer.Len() - } - return -1 - }()) - } - // Get input - can be string (fragment) or object (complete) var inputFragment string var inputMap map[string]interface{} @@ -477,92 +466,12 @@ func ProcessToolUseEvent(event map[string]interface{}, currentToolUse *ToolUseSt if isStop && currentToolUse != nil { fullInput := currentToolUse.InputBuffer.String() - // Check for Write tool with empty or missing input - this happens when Kiro API - // completely skips sending input for large file writes - if currentToolUse.Name == "Write" && len(strings.TrimSpace(fullInput)) == 0 { - log.Warnf("kiro: Write tool received no input from upstream API. The file content may be too large to transmit.") - // Return nil to skip this tool use - it will be handled as a truncation error - // The caller should emit a text block explaining the error instead - if processedIDs != nil { - processedIDs[currentToolUse.ToolUseID] = true - } - log.Infof("kiro: skipping Write tool use %s due to empty input (content too large)", currentToolUse.ToolUseID) - // Return a special marker tool use that indicates truncation - toolUse := KiroToolUse{ - ToolUseID: currentToolUse.ToolUseID, - Name: "__truncated_write__", // Special marker name - Input: map[string]interface{}{ - "error": "Write tool input was not transmitted by upstream API. The file content is too large.", - }, - } - toolUses = append(toolUses, toolUse) - return toolUses, nil - } - // Repair and parse the accumulated JSON repairedJSON := RepairJSON(fullInput) var finalInput map[string]interface{} if err := json.Unmarshal([]byte(repairedJSON), &finalInput); err != nil { log.Warnf("kiro: failed to parse accumulated tool input: %v, raw: %s", err, fullInput) finalInput = make(map[string]interface{}) - - // Check if this is a Write tool with truncated input (missing content field) - // This happens when the Kiro API truncates large tool inputs - if currentToolUse.Name == "Write" && strings.Contains(fullInput, "file_path") && !strings.Contains(fullInput, "content") { - log.Warnf("kiro: Write tool input was truncated by upstream API (content field missing). The file content may be too large.") - // Extract file_path if possible for error context - filePath := "" - if idx := strings.Index(fullInput, "file_path"); idx >= 0 { - // Try to extract the file path value - rest := fullInput[idx:] - if colonIdx := strings.Index(rest, ":"); colonIdx >= 0 { - rest = strings.TrimSpace(rest[colonIdx+1:]) - if len(rest) > 0 && rest[0] == '"' { - rest = rest[1:] - if endQuote := strings.Index(rest, "\""); endQuote >= 0 { - filePath = rest[:endQuote] - } - } - } - } - if processedIDs != nil { - processedIDs[currentToolUse.ToolUseID] = true - } - // Return a special marker tool use that indicates truncation - toolUse := KiroToolUse{ - ToolUseID: currentToolUse.ToolUseID, - Name: "__truncated_write__", // Special marker name - Input: map[string]interface{}{ - "error": "Write tool content was truncated by upstream API. The file content is too large.", - "file_path": filePath, - }, - } - toolUses = append(toolUses, toolUse) - return toolUses, nil - } - } - - // Additional check: Write tool parsed successfully but missing content field - if currentToolUse.Name == "Write" { - if _, hasContent := finalInput["content"]; !hasContent { - if filePath, hasPath := finalInput["file_path"]; hasPath { - log.Warnf("kiro: Write tool input missing 'content' field, likely truncated by upstream API") - if processedIDs != nil { - processedIDs[currentToolUse.ToolUseID] = true - } - // Return a special marker tool use that indicates truncation - toolUse := KiroToolUse{ - ToolUseID: currentToolUse.ToolUseID, - Name: "__truncated_write__", // Special marker name - Input: map[string]interface{}{ - "error": "Write tool content field was missing. The file content is too large.", - "file_path": filePath, - }, - } - toolUses = append(toolUses, toolUse) - return toolUses, nil - } - } } toolUse := KiroToolUse{