fix: auto-titler SSE event + SSE reconnection after restart (#125)

* fix: auto-titler SSE event + SSE reconnection after restart

_generate_title() now calls self.ui.on_rename() after persisting the
title, so the tab bar, bridge, and console all update in real time.
Also handles multi-part (vision) content and replaces silent except
with log.debug.

SSE onerror handler now parses the workstreams response, replaces the
stale workstreams map, and switches to the first available workstream
if the current ws_id no longer exists (e.g. after server restart).
Previously it retried the stale ws_id forever.

* fix: address PR #125 review — avoid double reconnect + sync tab bar

Return immediately after switchTab/showDashboard on stale ws_id to
prevent scheduling a redundant connectContentSSE via setTimeout.
Always re-render tab bar after replacing the workstreams map so DOM
stays in sync even when currentWsId is still valid.
This commit is contained in:
Patrick Buckley
2026-03-18 15:14:14 -07:00
committed by GitHub
parent ec3454ee2e
commit e159837b74
2 changed files with 33 additions and 7 deletions
+8 -3
View File
@@ -628,10 +628,14 @@ class ChatSession:
user_msg = ""
asst_msg = ""
for m in self.messages:
content = m.get("content") or ""
# Handle multi-part content (vision messages)
if isinstance(content, list):
content = " ".join(p.get("text", "") for p in content if isinstance(p, dict))
if m["role"] == "user" and not user_msg:
user_msg = (m.get("content") or "")[:300]
user_msg = content[:300]
elif m["role"] == "assistant" and not asst_msg:
asst_msg = (m.get("content") or "")[:200]
asst_msg = content[:200]
if user_msg and asst_msg:
break
if not user_msg:
@@ -667,8 +671,9 @@ class ChatSession:
title = raw.split("\n")[0].strip().strip('"').strip("'")
if title:
update_workstream_title(self._ws_id, title[:80])
self.ui.on_rename(title[:80])
except Exception:
pass # Title generation is non-critical
log.debug("Title generation failed for ws=%s", self._ws_id, exc_info=True)
def resume(self, ws_id: str) -> bool:
"""Load messages from a previous workstream and resume it.
+25 -4
View File
@@ -592,10 +592,31 @@ function connectContentSSE(wsId) {
showLogin();
return;
}
setTimeout(function () {
connectContentSSE(currentWsId);
}, contentRetryDelay);
contentRetryDelay = Math.min(contentRetryDelay * 2, 30000);
return r.json().then(function (data) {
// Replace workstream list — IDs may have changed after restart
var freshIds = {};
workstreams = {};
(data.workstreams || []).forEach(function (ws) {
workstreams[ws.id] = { name: ws.name, state: ws.state };
freshIds[ws.id] = true;
});
// Always re-render tabs since workstreams map was replaced
renderTabBar();
// If current ws_id is stale, switch to first available
if (currentWsId && !freshIds[currentWsId]) {
var ids = Object.keys(freshIds);
if (ids.length) {
switchTab(ids[0]);
} else {
showDashboard();
}
return; // switchTab/showDashboard handles SSE connection
}
setTimeout(function () {
connectContentSSE(currentWsId);
}, contentRetryDelay);
contentRetryDelay = Math.min(contentRetryDelay * 2, 30000);
});
})
.catch(function () {
setTimeout(function () {