fix(opencode): strip trailing assistant messages to prevent prefill 400
Problem
rewriteRequestBody() never removed trailing assistant-role messages. A resumed or compacted session that ends on an assistant turn (e.g. after a failed tool round, or a session restored mid-response) reached Anthropic with assistant-message prefill. Claude Code OAuth models reject this:
This model does not support assistant message prefill.
The conversation must end with a user message. (HTTP 400)
This recurs in practice on session resume/compaction and forces the user to manually send another message to recover.
Fix
Add stripTrailingAssistantMessages() and call it right after JSON.parse(body), so trailing assistant messages are popped before billing-header computation and signing. This mirrors the behavior of the built-in @ai-sdk/anthropic provider and the packages/pi conversion path, which already strip trailing assistant messages.
Only messages after the last user message are removed — the active user turn and any assistant message followed by a user message are always preserved. The alternative (no strip) is a hard 400 that returns nothing.
Tests
Adds 5 tests:
- strips single trailing assistant message
- strips multiple trailing assistant messages
- preserves assistant message followed by user message
- preserves assistant
tool_usefollowed by usertool_result - strips trailing assistant after
tool_result+ assistant
Updates existing fixtures that previously ended on assistant turns to end on user messages. All opencode tests pass (264), typecheck clean, biome clean.
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.
Summary by cubic
Strips trailing assistant messages during request rewrite so conversations end with a user message, preventing Anthropic Claude Code OAuth 400 “assistant message prefill” errors. Aligns behavior with @ai-sdk/anthropic and packages/pi.
- Bug Fixes
- Added
stripTrailingAssistantMessages()and called it inrewriteRequestBody()right afterJSON.parse. - Only removes assistant messages after the last user message; preserves assistant→user and
tool_use→tool_resultflows. - Added 5 tests for strip/preserve scenarios and updated fixtures to end on user messages.
- Added