-
Notifications
You must be signed in to change notification settings - Fork 2.6k
fix: preserve user messages at choice prompts in history (fixes #7316) #7317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ export interface UsePromptHistoryReturn { | |
| ) => boolean | ||
| resetHistoryNavigation: () => void | ||
| resetOnInputChange: () => void | ||
| addToLocalHistory: (message: string) => void | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding JSDoc comments for the new |
||
| } | ||
|
|
||
| export const usePromptHistory = ({ | ||
|
|
@@ -38,6 +39,8 @@ export const usePromptHistory = ({ | |
| const [historyIndex, setHistoryIndex] = useState(-1) | ||
| const [tempInput, setTempInput] = useState("") | ||
| const [promptHistory, setPromptHistory] = useState<string[]>([]) | ||
| // Local sent messages that haven't been confirmed by backend yet | ||
| const [localSentMessages, setLocalSentMessages] = useState<string[]>([]) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential memory concern here. The |
||
|
|
||
| // Initialize prompt history with hybrid approach: conversation messages if in task, otherwise task history | ||
| const filteredPromptHistory = useMemo(() => { | ||
|
|
@@ -46,9 +49,30 @@ export const usePromptHistory = ({ | |
| ?.filter((message) => message.type === "say" && message.say === "user_feedback" && message.text?.trim()) | ||
| .map((message) => message.text!) | ||
|
|
||
| // If we have conversation messages, use those (newest first when navigating up) | ||
| // Combine conversation prompts with local sent messages (deduplicated) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This deduplication logic (lines 52-71) could be extracted into a utility function for better readability and potential reuse. Something like |
||
| const allPrompts: string[] = [] | ||
| const seen = new Set<string>() | ||
|
|
||
| // Add conversation prompts first | ||
| if (conversationPrompts?.length) { | ||
| return conversationPrompts.slice(-MAX_PROMPT_HISTORY_SIZE).reverse() | ||
| conversationPrompts.forEach((prompt) => { | ||
| if (!seen.has(prompt)) { | ||
| seen.add(prompt) | ||
| allPrompts.push(prompt) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| // Add local sent messages that aren't in conversation yet | ||
| localSentMessages.forEach((msg) => { | ||
| if (!seen.has(msg)) { | ||
| allPrompts.push(msg) | ||
| } | ||
| }) | ||
|
|
||
| // If we have any prompts, use those (newest first when navigating up) | ||
| if (allPrompts.length) { | ||
| return allPrompts.slice(-MAX_PROMPT_HISTORY_SIZE).reverse() | ||
| } | ||
|
|
||
| // If we have clineMessages array (meaning we're in an active task), don't fall back to task history | ||
|
|
@@ -67,7 +91,7 @@ export const usePromptHistory = ({ | |
| .filter((item) => item.task?.trim() && (!item.workspace || item.workspace === cwd)) | ||
| .map((item) => item.task) | ||
| .slice(0, MAX_PROMPT_HISTORY_SIZE) | ||
| }, [clineMessages, taskHistory, cwd]) | ||
| }, [clineMessages, taskHistory, cwd, localSentMessages]) | ||
|
|
||
| // Update prompt history when filtered history changes and reset navigation | ||
| useEffect(() => { | ||
|
|
@@ -175,6 +199,21 @@ export const usePromptHistory = ({ | |
| setTempInput("") | ||
| }, []) | ||
|
|
||
| // Add a message to local sent history | ||
| const addToLocalHistory = useCallback((message: string) => { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing test coverage for this new functionality. Could we add tests to verify that messages are properly saved to local history when Enter is pressed at choice prompts? The existing tests don't cover the |
||
| if (message?.trim()) { | ||
| setLocalSentMessages((prev) => [...prev, message].slice(-MAX_PROMPT_HISTORY_SIZE)) | ||
| } | ||
| }, []) | ||
|
|
||
| // Clear local sent messages when task changes | ||
| useEffect(() => { | ||
| // When clineMessages changes significantly (new task), clear local messages | ||
| if (!clineMessages?.length) { | ||
| setLocalSentMessages([]) | ||
| } | ||
| }, [clineMessages?.length]) | ||
|
|
||
| return { | ||
| historyIndex, | ||
| setHistoryIndex, | ||
|
|
@@ -184,5 +223,6 @@ export const usePromptHistory = ({ | |
| handleHistoryNavigation, | ||
| resetHistoryNavigation, | ||
| resetOnInputChange, | ||
| addToLocalHistory, | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a potential race condition between adding to local history and the actual send? If
onSend()fails or is delayed, the message is already in local history. Is this intentional to preserve the message even on send failure, or should we only add after confirming the send was initiated?