diff --git a/app/routes/ai.tsx b/app/routes/ai.tsx index 34dd09d..343b66e 100644 --- a/app/routes/ai.tsx +++ b/app/routes/ai.tsx @@ -378,7 +378,7 @@ function ChatSurface({ if (initialLive.current === null) { initialLive.current = loadLive() ?? [] } - const { messages, setMessages, send, continueChat, abort, isStreaming, reset } = useChat({ + const { messages, setMessages, send, continueChat, abort, isStreaming } = useChat({ system: systemPrompt, initialMessages: initialLive.current, }) @@ -389,11 +389,20 @@ function ChatSurface({ saveLive(messages) }, [messages]) - // Wrap reset so "Clear conversation" also drops the persisted snapshot. + // "Clear conversation" must drop three things in lockstep: + // 1. The in-memory messages (setMessages([])). + // 2. The persisted live snapshot (clearLive()). + // 3. The initialLive ref — otherwise on the next render or hook + // reconciliation, useChat's reset() would re-seed from the + // captured-at-mount initialMessages and the old conversation + // pops back. (This was the bug.) + // We deliberately don't call useChat's reset() here because reset + // restores to opts.initialMessages, which we want to be empty. const resetAndClear = useCallback(() => { - reset() + initialLive.current = [] clearLive() - }, [reset]) + setMessages([]) + }, [setMessages]) // Auto tool-loop using native function calls. Reads run automatically; // writes are held in `pendingConfirm` until the operator clicks Confirm