Merge pull request #150 from PancakeZik/fix/write-tool-truncation-handling

fix: handle Write tool truncation when content exceeds API limits
This commit is contained in:
Luis Pater
2026-01-30 21:15:31 +08:00
committed by GitHub
2 changed files with 173 additions and 2 deletions

View File

@@ -2372,8 +2372,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)
@@ -3151,12 +3151,92 @@ 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