diff --git a/site/src/pages/AgentsPage/AgentChatPage.tsx b/site/src/pages/AgentsPage/AgentChatPage.tsx index 1be0dff9d3..9990a482d6 100644 --- a/site/src/pages/AgentsPage/AgentChatPage.tsx +++ b/site/src/pages/AgentsPage/AgentChatPage.tsx @@ -646,17 +646,24 @@ const AgentChatPage: FC = () => { const isRegenerateTitleDisabled = isArchived || isRegeneratingThisChat; const chatLastModelConfigID = chatRecord?.last_model_config_id; - const sendMutation = useMutation( + // Destructure mutation results directly so the React Compiler + // tracks stable primitives/functions instead of the whole result + // object (TanStack Query v5 recreates it every render via object + // spread). Keeping no intermediate variable prevents future code + // from accidentally closing over the unstable object. + const { isPending: isSendPending, mutateAsync: sendMessage } = useMutation( createChatMessage(queryClient, agentId ?? ""), ); - const editMutation = useMutation(editChatMessage(queryClient, agentId ?? "")); - const interruptMutation = useMutation( + const { isPending: isEditPending, mutateAsync: editMessage } = useMutation( + editChatMessage(queryClient, agentId ?? ""), + ); + const { isPending: isInterruptPending, mutateAsync: interrupt } = useMutation( interruptChat(queryClient, agentId ?? ""), ); - const deleteQueuedMutation = useMutation( + const { mutateAsync: deleteQueuedMessage } = useMutation( deleteChatQueuedMessage(queryClient, agentId ?? ""), ); - const promoteQueuedMutation = useMutation( + const { mutateAsync: promoteQueuedMessage } = useMutation( promoteChatQueuedMessage(queryClient, agentId ?? ""), ); @@ -754,9 +761,7 @@ const AgentChatPage: FC = () => { hasUserFixableModelProviders, }); const isSubmissionPending = - sendMutation.isPending || - editMutation.isPending || - interruptMutation.isPending; + isSendPending || isEditPending || isInterruptPending; const isInputDisabled = !hasModelOptions || isArchived; const handleUsageLimitError = (error: unknown): void => { @@ -842,7 +847,7 @@ const AgentChatPage: FC = () => { setPendingEditMessageId(editedMessageID); scrollToBottomRef.current?.(); try { - await editMutation.mutateAsync({ + await editMessage({ messageId: editedMessageID, req: request, }); @@ -873,9 +878,9 @@ const AgentChatPage: FC = () => { // For queued sends the WebSocket status events handle // clearing; for non-queued sends we clear explicitly // below. Clearing eagerly causes a visible cutoff. - let response: Awaited>; + let response: Awaited>; try { - response = await sendMutation.mutateAsync(request); + response = await sendMessage(request); } catch (error) { handleUsageLimitError(error); throw error; @@ -908,10 +913,10 @@ const AgentChatPage: FC = () => { }; const handleInterrupt = () => { - if (!agentId || interruptMutation.isPending) { + if (!agentId || isInterruptPending) { return; } - void interruptMutation.mutateAsync(); + void interrupt(); }; const handleDeleteQueuedMessage = async (id: number) => { @@ -920,7 +925,7 @@ const AgentChatPage: FC = () => { previousQueuedMessages.filter((message) => message.id !== id), ); try { - await deleteQueuedMutation.mutateAsync(id); + await deleteQueuedMessage(id); } catch (error) { store.setQueuedMessages(previousQueuedMessages); throw error; @@ -941,7 +946,7 @@ const AgentChatPage: FC = () => { store.clearStreamError(); store.setChatStatus("pending"); try { - const promotedMessage = await promoteQueuedMutation.mutateAsync(id); + const promotedMessage = await promoteQueuedMessage(id); // Insert the promoted message into the store and cache // immediately so it appears in the timeline without // waiting for the WebSocket to deliver it. @@ -990,7 +995,8 @@ const AgentChatPage: FC = () => { ? `ssh ${workspaceAgent.name}.${workspace.name}.${workspace.owner_name}.${sshConfigQuery.data.hostname_suffix}` : undefined; - const generateKeyMutation = useMutation({ + // See mutation destructuring comment above (React Compiler). + const { mutate: generateKey } = useMutation({ mutationFn: () => API.getApiKey(), }); @@ -1005,7 +1011,7 @@ const AgentChatPage: FC = () => { const repoRoots = Array.from(gitWatcher.repositories.keys()).sort(); const folder = repoRoots[0] ?? workspaceAgent.expanded_directory; - generateKeyMutation.mutate(undefined, { + generateKey(undefined, { onSuccess: ({ key }) => { location.href = getVSCodeHref(editor, { owner: workspace.owner_name, @@ -1141,7 +1147,7 @@ const AgentChatPage: FC = () => { compressionThreshold={compressionThreshold} isInputDisabled={isInputDisabled} isSubmissionPending={isSubmissionPending} - isInterruptPending={interruptMutation.isPending} + isInterruptPending={isInterruptPending} isSidebarCollapsed={isSidebarCollapsed} onToggleSidebarCollapsed={onToggleSidebarCollapsed} showSidebarPanel={showSidebarPanel}