diff --git a/src/services/api/openaiShim.test.ts b/src/services/api/openaiShim.test.ts index e84ff27c..b47d84f6 100644 --- a/src/services/api/openaiShim.test.ts +++ b/src/services/api/openaiShim.test.ts @@ -1420,7 +1420,7 @@ test('does not normalize incomplete streamed Bash commands when finish_reason is .map(event => (event.delta as Record).partial_json) .join('') - expect(streamedInput).toBe('{"command":"rg --fi"}') + expect(streamedInput).toBe('rg --fi') }) test('does not repair truncated Bash objects that do not contain command', async () => { diff --git a/src/services/api/openaiShim.ts b/src/services/api/openaiShim.ts index c781563c..f1100e0e 100644 --- a/src/services/api/openaiShim.ts +++ b/src/services/api/openaiShim.ts @@ -692,16 +692,22 @@ async function* openaiStreamToAnthropic( // Close active tool calls for (const [, tc] of activeToolCalls) { if (tc.normalizeAtStop) { - const repairedStructuredJson = repairPossiblyTruncatedObjectJson( - tc.jsonBuffer, - ) let partialJson: string - if (repairedStructuredJson) { - partialJson = repairedStructuredJson + if (choice.finish_reason === 'length') { + // Truncated by max tokens — preserve raw buffer to avoid + // turning an incomplete tool call into an executable command + partialJson = tc.jsonBuffer } else { - partialJson = JSON.stringify( - normalizeToolArguments(tc.name, tc.jsonBuffer), + const repairedStructuredJson = repairPossiblyTruncatedObjectJson( + tc.jsonBuffer, ) + if (repairedStructuredJson) { + partialJson = repairedStructuredJson + } else { + partialJson = JSON.stringify( + normalizeToolArguments(tc.name, tc.jsonBuffer), + ) + } } yield {