fix(chat): make resend message non-destructive

Keep normal resend from truncating session history while preserving replace-from-here behavior for regenerate flows.
This commit is contained in:
osmanakkawi
2026-06-15 09:02:48 +03:00
committed by GitHub
parent b20cea347a
commit 71ccd59b54
3 changed files with 67 additions and 19 deletions
+23 -18
View File
@@ -3876,9 +3876,11 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
}
/**
* Resend a user message — truncates history to that point and resubmits.
* Resend a user message. Normal resend appends a fresh copy at the end of
* the current thread; regenerate flows can opt into replacing from here.
*/
export async function resendUserMessage(userMsgElement) {
export async function resendUserMessage(userMsgElement, opts = {}) {
const replaceFromHere = Boolean(opts && opts.replaceFromHere);
const box = document.getElementById('chat-history');
const allMsgs = Array.from(box.querySelectorAll('.msg'));
const msgIndex = allMsgs.indexOf(userMsgElement);
@@ -3924,25 +3926,28 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
const sessionId = sessionModule.getCurrentSessionId();
if (!sessionId) return;
// Truncate backend to keep everything before this user message
const keepCount = msgIndex;
try {
await fetch(`${API_BASE}/api/session/${sessionId}/truncate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keep_count: keepCount })
});
if (replaceFromHere) {
// Regenerate flows intentionally trim history to this point before
// resubmitting. The plain "Resend message" action must not do this.
const keepCount = msgIndex;
await fetch(`${API_BASE}/api/session/${sessionId}/truncate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keep_count: keepCount })
});
// Drop the AI replies after the user message but KEEP the user bubble
// itself (so its photo stays visible). Then suppress the new user
// bubble that send would otherwise add — same pattern as regenerate.
let sibling = userMsgElement.nextSibling;
while (sibling) {
const next = sibling.nextSibling;
sibling.remove();
sibling = next;
// Drop the AI replies after the user message but KEEP the user bubble
// itself (so its photo stays visible). Then suppress the new user
// bubble that send would otherwise add — same pattern as regenerate.
let sibling = userMsgElement.nextSibling;
while (sibling) {
const next = sibling.nextSibling;
sibling.remove();
sibling = next;
}
_hideUserBubble = true;
}
_hideUserBubble = true;
_pendingRegenAttachments = _ids;
// Resubmit
+1 -1
View File
@@ -362,7 +362,7 @@ function _openVisionEditor(att, userMsgEl) {
await _saveVisionText();
_closeVisionEditor();
if (userMsgEl && window.chatModule?.resendUserMessage) {
window.chatModule.resendUserMessage(userMsgEl);
window.chatModule.resendUserMessage(userMsgEl, { replaceFromHere: true });
} else if (uiModule?.showToast) {
uiModule.showToast('Saved');
}