Merge pull request #2928

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
nesquena-hermes committed 2026-05-25 17:18:02 +00:00
commit 4a8b29db9e
3 files changed
+33 -4

No files matched your search

+1
View File
@@ -8,6 +8,7 @@
- When the session index is missing, WebUI now starts a background rebuild while preserving the first sidebar full-scan result, so the index is primed for later requests without temporarily hiding existing sessions.
- Live token-usage hints now cap the cumulative in-flight tool-result prompt estimate per assistant turn, preventing many large tool callbacks from temporarily inflating the context ring before exact provider accounting arrives.
- Streaming checkpoint saves now run under the session's profile environment, so periodic background checkpoints no longer risk falling back to the process-global profile when a WebUI tab is using a non-default profile.
- Chat streaming now keeps a single live EventSource registered per session/stream, preventing reconnect or context-compaction paths from stacking subscribers and rendering one assistant token stream multiple times.
## [v0.51.135] — 2026-05-25 — Release DG (stage-batch17 — 9-PR small-fix batch)
+12 -4
View File
@@ -609,10 +609,11 @@ async function send(){
const LIVE_STREAMS={};
function closeLiveStream(sessionId, streamId){
function closeLiveStream(sessionId, streamId, source){
const live=LIVE_STREAMS[sessionId];
if(!live) return;
if(streamId&&live.streamId!==streamId) return;
if(source&&live.source!==source) return;
try{live.source.close();}catch(_){ }
delete LIVE_STREAMS[sessionId];
}
@@ -747,8 +748,8 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
if(_persistTimer) return;
_persistTimer=setTimeout(()=>{_persistTimer=null;persistInflightState();},2000);
}
function _closeSource(){
closeLiveStream(activeSid, streamId);
function _closeSource(source){
closeLiveStream(activeSid, streamId, source);
}
function _stripLiveVisibleAssistantEchoFromThinking(text, snippets){
let out=String(text||'');
@@ -1455,6 +1456,12 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
}
function _wireSSE(source){
const existingLive=LIVE_STREAMS[activeSid];
if(existingLive&&existingLive.source&&existingLive.source!==source){
try{existingLive.source.close();}catch(_){ }
}
LIVE_STREAMS[activeSid]={streamId,source};
// Note on #631 Bug B: the original PR description stated the server
// "replays buffered token events" on reconnect, and proposed resetting
// the accumulators here so the re-sent tokens wouldn't double the prefix.
@@ -2081,13 +2088,14 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
source.addEventListener('error',async e=>{
if(_terminalStateReached || _streamFinalized){
_closeSource();
_closeSource(source);
return;
}
if(typeof recordClientSSEError==='function') recordClientSSEError('chat-response',{ready_state:source?source.readyState:null,session_id:activeSid,stream_id:streamId,reason:'chat EventSource.onerror'});
source.close();
if(_deferStreamErrorIfOffline()) return;
if(_deferStreamErrorIfPageHidden()) return;
_closeSource(source);
// Attempt one reconnect if the stream is still active server-side
if(!_reconnectAttempted && streamId){
_reconnectAttempted=true;
@@ -121,3 +121,23 @@ def test_done_handler_is_idempotent_for_replay_or_duplicate_done_events():
assert guard_idx != -1 and guard_idx < sound_idx, (
"completion sound must be behind the duplicate-done finalization guard"
)
def test_attach_live_stream_registers_one_source_per_session_stream():
"""Reconnect/compaction paths must not stack same-stream EventSources.
The stream channel broadcasts each token to every subscriber. If the browser
opens four live EventSources for the same run, one assistant paragraph is
appended four times even though the run journal contains it once.
"""
close_body = _function_body("closeLiveStream")
attach_body = _function_body("attachLiveStream")
wire_body = _function_body("_wireSSE")
error_body = _event_body("error")
assert "const LIVE_STREAMS={};" in MESSAGES_JS
assert "LIVE_STREAMS[activeSid]={streamId,source};" in wire_body
assert "existingLive.source.close();" in wire_body
assert "if(source&&live.source!==source) return;" in close_body
assert "existingLive&&existingLive.streamId===streamId" in attach_body
assert "_closeSource(source);" in error_body