From 5a64b90910414793a16003417a6866931ddb9270 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 15 Mar 2026 14:06:19 -0500 Subject: [PATCH] fix(cli): remove side-effect from agent:end state updater (#133) Use a ref to track current stream text so agent:end can commit the final message without calling setMessages inside a setCurrentStreamText updater function, which violates React's rule that state updaters must be pure. Co-Authored-By: Claude Sonnet 4.6 --- packages/cli/src/tui/app.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/tui/app.tsx b/packages/cli/src/tui/app.tsx index 56372be..4174322 100644 --- a/packages/cli/src/tui/app.tsx +++ b/packages/cli/src/tui/app.tsx @@ -52,6 +52,7 @@ export function TuiApp({ const [availableModels, setAvailableModels] = useState([]); const socketRef = useRef(null); + const currentStreamTextRef = useRef(''); // Fetch available models on mount useEffect(() => { @@ -102,20 +103,22 @@ export function TuiApp({ socket.on('agent:start', () => { setIsStreaming(true); + currentStreamTextRef.current = ''; setCurrentStreamText(''); }); socket.on('agent:text', (data: { text: string }) => { - setCurrentStreamText((prev) => prev + data.text); + currentStreamTextRef.current += data.text; + setCurrentStreamText(currentStreamTextRef.current); }); socket.on('agent:end', () => { - setCurrentStreamText((prev) => { - if (prev) { - setMessages((msgs) => [...msgs, { role: 'assistant', content: prev }]); - } - return ''; - }); + const finalText = currentStreamTextRef.current; + currentStreamTextRef.current = ''; + setCurrentStreamText(''); + if (finalText) { + setMessages((msgs) => [...msgs, { role: 'assistant', content: finalText }]); + } setIsStreaming(false); });