From 7e0895a1eeabd047fb893ce9288f4548be85f489 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 2 Mar 2026 16:31:12 -0500 Subject: [PATCH] fix(site): roll back optimistic message when server queues it (#22522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem When a user sends a message while the agent is busy, the message appears in the chat timeline as if it was sent and being processed (with the "Thinking..." shimmer), instead of appearing in the queued messages list above the input. ## Root Cause `handleSend` in `AgentDetail.tsx` unconditionally injects an optimistic user message into the conversation timeline and sets chat status to `"pending"` **before** awaiting the server response. However, the server can respond with `{ queued: true, queued_message: {...} }` (via `CreateChatMessageResponse`) when the agent is already busy — meaning the message was queued, not processed. The client never inspected `response.queued` after the request succeeded, so the optimistic message stayed in the timeline even though the server queued it. ## Fix After `sendMutation.mutateAsync(request)` resolves, check `response.queued`. If true, roll back the optimistic message and restore the previous chat status. The `queue_update` SSE event from the WebSocket stream handles adding it to the queued messages list. ## Changes - **`site/src/pages/AgentsPage/AgentDetail.tsx`**: Capture the response from `sendMutation.mutateAsync` and roll back the optimistic message + status when `response.queued === true`. --- site/src/pages/AgentsPage/AgentDetail.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/site/src/pages/AgentsPage/AgentDetail.tsx b/site/src/pages/AgentsPage/AgentDetail.tsx index 03988447d8..54de270475 100644 --- a/site/src/pages/AgentsPage/AgentDetail.tsx +++ b/site/src/pages/AgentsPage/AgentDetail.tsx @@ -718,7 +718,16 @@ const AgentDetail: FC = () => { store.setChatStatus("pending"); try { - await sendMutation.mutateAsync(request); + const response = await sendMutation.mutateAsync(request); + if (response.queued) { + // The server queued the message instead of processing + // it immediately (the agent is already busy). Roll back + // the optimistic timeline message so it doesn't appear + // as a sent message. The queue_update SSE event will + // add it to the queued messages list. + store.replaceMessages(previousMessages); + store.setChatStatus(previousChatStatus); + } } catch (error) { // Roll back the optimistic message so the timeline // returns to its previous state.