mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-15 12:53:31 +08:00
fix(desktop): dedupe replayed active prompt (#755)
Treat user_message_replay as idempotent for the current turn, even after thinking or tool events have already appeared in the live message list. This keeps normal sent prompts and guided queued prompts from rendering a duplicate user bubble when the CLI replays the same prompt. Tested: cd desktop && bun run test -- src/stores/chatStore.test.ts --run Tested: cd desktop && bun run test -- src/components/chat/ChatInput.test.tsx -t "queues prompts submitted while a turn is running until the user guides them" --run Tested: bun run check:desktop Confidence: high Scope-risk: narrow
This commit is contained in:
parent
f9575b9749
commit
0eee5a73b3
@ -3486,6 +3486,42 @@ describe('chatStore history mapping', () => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('does not duplicate the current prompt when CLI replays it after thinking starts', () => {
|
||||
const prompt = '# 角色与目标\n构建一个协同编辑器'
|
||||
useChatStore.setState({
|
||||
sessions: {
|
||||
[TEST_SESSION_ID]: makeSession({
|
||||
messages: [
|
||||
{
|
||||
id: 'live-user',
|
||||
type: 'user_text',
|
||||
content: prompt,
|
||||
timestamp: 1,
|
||||
},
|
||||
],
|
||||
chatState: 'thinking',
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
useChatStore.getState().handleServerMessage(TEST_SESSION_ID, {
|
||||
type: 'thinking',
|
||||
text: 'I need to plan the implementation.',
|
||||
})
|
||||
useChatStore.getState().handleServerMessage(TEST_SESSION_ID, {
|
||||
type: 'user_message_replay',
|
||||
content: prompt,
|
||||
})
|
||||
|
||||
const userMessages = useChatStore.getState().sessions[TEST_SESSION_ID]?.messages
|
||||
.filter((message) => message.type === 'user_text')
|
||||
expect(userMessages).toHaveLength(1)
|
||||
expect(useChatStore.getState().sessions[TEST_SESSION_ID]?.messages).toMatchObject([
|
||||
{ type: 'user_text', content: prompt },
|
||||
{ type: 'thinking', content: 'I need to plan the implementation.' },
|
||||
])
|
||||
})
|
||||
|
||||
it('flushes pending text before appending an error message', () => {
|
||||
vi.useFakeTimers()
|
||||
|
||||
|
||||
@ -2755,24 +2755,17 @@ function appendReplayedUserMessage(
|
||||
if (!displayContent) return messages
|
||||
|
||||
const modelContent = parsed.modelContent ?? content.trim()
|
||||
const optimisticIndex = findOptimisticQueuedUserMessageIndex(messages, modelContent)
|
||||
if (optimisticIndex >= 0) {
|
||||
const optimisticMessage = messages[optimisticIndex]
|
||||
if (optimisticMessage?.type === 'user_text') {
|
||||
const currentTurnUserIndex = findCurrentTurnUserMessageIndex(messages, modelContent)
|
||||
if (currentTurnUserIndex >= 0) {
|
||||
const optimisticMessage = messages[currentTurnUserIndex]
|
||||
if (optimisticMessage?.type === 'user_text' && optimisticMessage.optimisticQueued) {
|
||||
const { optimisticQueued: _optimisticQueued, ...confirmedMessage } = optimisticMessage
|
||||
return [
|
||||
...messages.slice(0, optimisticIndex),
|
||||
...messages.slice(0, currentTurnUserIndex),
|
||||
confirmedMessage,
|
||||
...messages.slice(optimisticIndex + 1),
|
||||
...messages.slice(currentTurnUserIndex + 1),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const last = messages[messages.length - 1]
|
||||
if (
|
||||
last?.type === 'user_text' &&
|
||||
(last.modelContent ?? last.content).trim() === modelContent
|
||||
) {
|
||||
return messages
|
||||
}
|
||||
|
||||
@ -2829,19 +2822,16 @@ function mapQueuedDisplayAttachments(attachments?: AttachmentRef[]): UIAttachmen
|
||||
}))
|
||||
}
|
||||
|
||||
function findOptimisticQueuedUserMessageIndex(
|
||||
function findCurrentTurnUserMessageIndex(
|
||||
messages: UIMessage[],
|
||||
modelContent: string,
|
||||
): number {
|
||||
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
||||
const message = messages[index]
|
||||
if (
|
||||
message?.type === 'user_text' &&
|
||||
message.optimisticQueued &&
|
||||
(message.modelContent ?? message.content).trim() === modelContent
|
||||
) {
|
||||
return index
|
||||
if (message?.type !== 'user_text') {
|
||||
continue
|
||||
}
|
||||
return (message.modelContent ?? message.content).trim() === modelContent ? index : -1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user