fix: preserve tool-call parts in conversation history (#696)
* fix: preserve tool-call parts in conversation history to prevent "text content blocks must be non-empty" error
When replaying conversation history containing previous tool calls, convertToModelMessages was
silently dropping tool-call content parts from assistant messages. This caused:
1. Assistant messages with only tool calls to have empty content arrays
2. Missing tool-result messages paired with tool-use blocks
Now properly converts tool-call parts to AI SDK ToolCallPart format and generates
corresponding ToolModelMessage for completed tool results.
Closes: user report on v1.20.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: preserve content-part order and skip unresolved tool calls
Address PR review feedback:
1. Preserve original content-part order instead of grouping media before tool-calls
2. Skip tool-call parts with state='call' (unresolved) to prevent orphaned
tool_use blocks that providers would reject
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: split assistant messages around tool-call boundaries and use error-text for failed tools
Address Codex review findings:
1. P1: When assistant contentParts contain [text, tool-call, text], split into proper
sequence: assistant(text+tool-call) → tool(result) → assistant(text). Previously
the entire message was emitted as one assistant block with tool result appended after.
2. P2: Use output.type='error-text' for tool calls with state='error' instead of
'json', so providers correctly interpret failed tool executions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract shared image helper, remove unused imports and narrating comments
- Extract resolveImageData() to deduplicate image storage fetch logic between
convertContentParts and convertAssistantContentParts
- Remove unused imports: AssistantModelMessage, ToolModelMessage
- Fix type lie: use `as JSONValue` instead of `as null` for tool result values
- Remove unnecessary `return await`
- Remove comments that restate what the code does
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add BDD integration tests for tool-use conversation history with Claude
5 scenarios using AI SDK + @ai-sdk/anthropic directly:
- Single tool-call turn then follow-up
- Multiple parallel tool calls in one assistant turn
- Mixed text + tool-call in one assistant message
- Failed tool call with error-text output
- Assistant message with ONLY tool-call (the original bug trigger)
Requires TEST_CLAUDE_API_KEY in .env to run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: properly stringify structured error objects in tool-result error-text
String() on error objects like {name, message, stack} produces "[object Object]".
Extract message/error field first, fall back to JSON.stringify.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
(cherry picked from commit 139f7ba2373a9e1daf30c27e0778ffdbefa2df79) Z
Zeng Xian committed
a4a1097983d41077516c0c6c7f708313dacef717
Parent: ef97136