From 893cb8254fec2f37ad64432f7660914415f0b143 Mon Sep 17 00:00:00 2001 From: Giuseppe Date: Sat, 6 Jun 2026 11:36:30 +0200 Subject: [PATCH] fix(sessions): retry resumeStream in poll loop when chatModule loads late MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sessions.js executes before chat.js in ES module order, so window.chatModule is not yet set when _checkServerStream runs on page load. The resumeStream guard evaluates false and the spinner fallback kicks in; that fallback only polls stream_status and never retries the live-resume path, leaving the user with a dead spinner for the entire duration of the detached agent run. Fix: add a one-shot retry in the polling loop. On the first tick where window.chatModule.resumeStream is available, attempt to attach. If it succeeds, clear the interval and remove the spinner — live SSE streaming takes over. If the run has already finished (404), the loop continues to poll status and calls selectSession on completion. Fixes #3048 Co-authored-by: Claude Sonnet 4.6 --- static/js/sessions.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/static/js/sessions.js b/static/js/sessions.js index 23310d36c..15dfde08a 100644 --- a/static/js/sessions.js +++ b/static/js/sessions.js @@ -2184,6 +2184,10 @@ async function _checkServerStream(sessionId) { box.appendChild(holder); uiModule.scrollHistory(); + // sessions.js executes before chat.js in module order, so window.chatModule + // may not be set yet when _checkServerStream first runs. Retry resumeStream + // on the first poll tick where it becomes available. + let _resumeRetried = false; const pollId = setInterval(async () => { if (getCurrentSessionId() !== sessionId) { clearInterval(pollId); @@ -2191,6 +2195,16 @@ async function _checkServerStream(sessionId) { if (holder.parentNode) holder.remove(); return; } + if (!_resumeRetried && window.chatModule && window.chatModule.resumeStream) { + _resumeRetried = true; + const attached = await window.chatModule.resumeStream(sessionId); + if (attached) { + clearInterval(pollId); + spinner.destroy(); + if (holder.parentNode) holder.remove(); + return; + } + } try { const r = await fetch(`${API_BASE}/api/chat/stream_status/${sessionId}`); if (!r.ok || (await r.json()).status !== 'streaming') {