Two bugs caused the same AI response to appear twice:
1. handleSelectConversation would fetch conversation history even while
a stream was in progress (isLoading). If the fetch resolved mid-stream,
fetchConversation overwrote the conversation messages unconditionally,
writing the server-persisted AI message (with a real UUID) alongside the
still-streaming client-side message (with a temp ID), producing a duplicate.
Fix: guard the fetch with !isLoading, and add a second check inside the
.then callback so setConversations only writes history if the messages
array is still empty (protects against slow-resolving network requests).
2. onDone in streamChat could theoretically be called a second time if the
ReadableStream completed without a "done" SSE event. Fix: call
reader.cancel() immediately after onDone so the while loop exits cleanly.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Add _extract_output_str() to unwrap MCP ToolMessage content objects
instead of calling str() on the raw object
- Handle read_url url param when LLM passes a list instead of a string
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Replace the three pill buttons (Auto/Flash/Pro) in GeminiInput with a
single shadcn/ui Select component. Selection logic and onSelectedModelChange
callback are unchanged; only the UI widget is swapped.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>