fix(chat): keep active follow-up actionable after non-followup messages

This commit is contained in:
Hannes Rudolph 2026-02-09 14:53:08 -07:00
parent 43ba36b42d
commit 745f817c94
2 changed files with 50 additions and 3 deletions

View file

@ -277,13 +277,27 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
}, [])
const activeFollowUpTs = useMemo<number | null>(() => {
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 => {

View file

@ -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) => {