fix: also block fork on pending user message during streaming

Extends the live-message guard to reject both _live assistant rows and
_pending user rows that haven't been committed server-side yet. Adds a
belt-and-suspenders check: when S.busy is true, the last message in
S.messages is also blocked regardless of flags, closing a timing window
where _live might be momentarily absent on the streaming row.

The user's optimistically-pushed message (messages.js:1603) now carries
_pending:true so forkFromMessage can distinguish it from committed
messages.
This commit is contained in:
kertisalex 2026-07-13 00:42:24 +02:00 committed by nesquena-hermes
parent 5023053ebf
commit 90ee9484e0
2 changed files with 8 additions and 3 deletions

View file

@ -1951,7 +1951,8 @@ async function forkFromMessage(msgIdx){
// waiting for the stream to finish.
if(S.busy){
const _msg=(Array.isArray(S.messages)&&S.messages[msgIdx-1])||null;
if(_msg&&_msg._live){
const _isLastMsg=msgIdx>=(Array.isArray(S.messages)?S.messages.length:0);
if((_msg&&(_msg._live||_msg._pending))||_isLastMsg){
showToast('Cannot fork a message still being generated.',3000);
return;
}

View file

@ -1629,7 +1629,7 @@ async function send(){
// upload window. _composerDraftClearPromise / _submittedDraftFilesForClear are
// set there; nothing to re-declare here.
const displayText=_slashDisplayTextOverride||text||(uploaded.length?`Uploaded: ${uploadedNames.join(', ')}`:'(file upload)');
const userMsg={role:'user',content:displayText,attachments:uploaded.length?uploadedNames:undefined,_ts:Date.now()/1000};
const userMsg={role:'user',content:displayText,attachments:uploaded.length?uploadedNames:undefined,_ts:Date.now()/1000,_pending:true};
S.toolCalls=[]; // clear tool calls from previous turn
clearLiveToolCards(); // clear any leftover live cards from last turn
let optimisticMessages;
@ -7733,7 +7733,11 @@ function showClarifyCard(pending) {
_clarifySessionId = sid;
_clarifyId = pending.clarify_id || null;
_clarifySignature = sig;
_startClarifyCountdown(pending);
if (Number(pending.timeout_seconds) > 0) {
_startClarifyCountdown(pending);
} else {
_clearClarifyCountdownTimer();
}
if (!sameClarify) {
_clarifyVisibleSince = Date.now();
_clearClarifyHideTimer();