diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 98567296aa..a98be15772 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -277,13 +277,27 @@ const ChatViewComponent: React.ForwardRefRenderFunction(() => { - const latestMessage = messages.at(-1) + const latestFollowUpIndex = messages.findLastIndex( + (message) => message.type === "ask" && message.ask === "followup", + ) - if (latestMessage?.type !== "ask" || latestMessage.ask !== "followup") { + if (latestFollowUpIndex === -1) { return null } - return latestMessage.ts + const hasFollowUpResolutionSignal = messages + .slice(latestFollowUpIndex + 1) + .some( + (message) => + (message.type === "ask" && message.ask !== "followup") || + (message.type === "say" && message.say === "user_feedback"), + ) + + if (hasFollowUpResolutionSignal) { + return null + } + + return messages[latestFollowUpIndex].ts }, [messages]) const isCurrentSubtaskCompleted = useCallback((): boolean => { diff --git a/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx b/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx index aead964a76..ffed15e05d 100644 --- a/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatView.spec.tsx @@ -2085,6 +2085,39 @@ describe("ChatView - Follow-up Responsiveness Guards", () => { }) }) + it("keeps the active follow-up actionable when a non-followup say is the latest message", async () => { + const { getByTestId } = renderChatView() + + const followUpTs = 2_090 + + mockPostMessage({ + clineMessages: [ + { + type: "say", + say: "task", + ts: 1_000, + text: "Initial task", + }, + { + type: "ask", + ask: "followup", + ts: followUpTs, + text: "Should I continue?", + }, + { + type: "say", + say: "text", + ts: 2_091, + text: "Interleaved non-followup message", + }, + ], + }) + + await waitFor(() => { + expect(getByTestId(`chat-row-${followUpTs}`)).toHaveAttribute("data-followup-answered", "false") + }) + }) + it("emits deterministic pending/settle/clear markers for follow-up answer lifecycle", async () => { const markers: FollowUpInteractionMarker[] = [] setFollowUpInteractionInstrumentationSink((marker) => {