From 6a736809efc0553381a847a98e2edd114219b263 Mon Sep 17 00:00:00 2001 From: Dennis Soong Date: Thu, 30 Apr 2026 21:32:50 +0000 Subject: [PATCH] fix: sync active session across tabs (#1359) Adds a 'storage' event listener for the hermes-webui-session localStorage key. Idle tabs auto-load the new active session and re-render the sidebar; busy tabs show a toast and do not interrupt the active turn. Co-authored-by: Dennis Soong --- static/sessions.js | 16 ++++++++++++++++ tests/test_session_cross_tab_sync.py | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/test_session_cross_tab_sync.py diff --git a/static/sessions.js b/static/sessions.js index f3442df1..a75cb38e 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -1628,6 +1628,22 @@ function renderSessionListFromCache(){ } } +async function _handleActiveSessionStorageEvent(e){ + if(!e || e.key !== 'hermes-webui-session') return; + const sid = e.newValue; + if(!sid || (S.session && S.session.session_id === sid)) return; + if(S.busy){ + if(typeof showToast==='function') showToast('Active session changed in another tab. Finish the current turn before switching.',3000); + return; + } + await loadSession(sid); + renderSessionListFromCache(); +} + +if(typeof window!=='undefined'){ + window.addEventListener('storage', (e) => { void _handleActiveSessionStorageEvent(e); }); +} + async function deleteSession(sid){ const ok=await showConfirmDialog({ message:'Delete this conversation?', diff --git a/tests/test_session_cross_tab_sync.py b/tests/test_session_cross_tab_sync.py new file mode 100644 index 00000000..bf7ec5d0 --- /dev/null +++ b/tests/test_session_cross_tab_sync.py @@ -0,0 +1,16 @@ +"""Regression tests for cross-tab active session synchronization.""" +from pathlib import Path + +REPO_ROOT = Path(__file__).parent.parent.resolve() +SESSIONS_JS = (REPO_ROOT / "static" / "sessions.js").read_text(encoding="utf-8") + + +def test_sessions_js_listens_for_active_session_storage_changes(): + assert "addEventListener('storage'" in SESSIONS_JS or 'addEventListener("storage"' in SESSIONS_JS + assert "hermes-webui-session" in SESSIONS_JS + assert "_handleActiveSessionStorageEvent" in SESSIONS_JS + + +def test_storage_sync_does_not_switch_while_busy(): + marker = "if(S.busy)" + assert marker in SESSIONS_JS, "cross-tab storage sync must not switch sessions during an active turn"