Clear archived saved session on boot restore

This commit is contained in:
Paladin173 authored and nesquena-hermes committed 2026-06-28 06:51:36 +00:00
1 parent a51e58efc0
commit aceb5411ee
2 files changed
+38 -6

No files matched your search

+16 -3
View File
@@ -81,13 +81,20 @@ async function cancelSessionStream(session){
} }
async function _savedSessionShouldStaySidebarOnly(sid){ async function _savedSessionShouldStaySidebarOnly(sid){
const state = await _savedSessionSidebarOnlyState(sid);
return !!(state&&state.sidebarOnly);
}
async function _savedSessionSidebarOnlyState(sid){
if(!sid) return false; if(!sid) return false;
try{ try{
const data = await api(`/api/session?session_id=${encodeURIComponent(sid)}&messages=0&resolve_model=0`); const data = await api(`/api/session?session_id=${encodeURIComponent(sid)}&messages=0&resolve_model=0`);
const session = data&&data.session; const session = data&&data.session;
return !!(session&&(session.active_stream_id||session.pending_user_message||session.archived)); const archived = !!(session&&session.archived);
const running = !!(session&&(session.active_stream_id||session.pending_user_message));
return {sidebarOnly:archived||running, archived};
}catch(e){ }catch(e){
return false; return null;
} }
} }
@@ -2708,7 +2715,13 @@ window._applyTitlebarProfileVisibility=_applyTitlebarProfileVisibility;
const saved=urlSession||savedLocal; const saved=urlSession||savedLocal;
if(saved){ if(saved){
try{ try{
if(!urlSession&&savedLocal&&await _savedSessionShouldStaySidebarOnly(savedLocal)){ const savedSidebarOnlyState=(!urlSession&&savedLocal)
? await _savedSessionSidebarOnlyState(savedLocal)
: null;
if(savedSidebarOnlyState&&savedSidebarOnlyState.sidebarOnly){
if(savedSidebarOnlyState.archived){
try{localStorage.removeItem('hermes-webui-session');}catch(_){}
}
S.session=null; S.messages=[]; S.activeStreamId=null; S.busy=false; S.session=null; S.messages=[]; S.activeStreamId=null; S.busy=false;
S._bootReady=true; S._bootReady=true;
syncTopbar();syncWorkspacePanelState(); syncTopbar();syncWorkspacePanelState();
+22 -3
View File
@@ -55,14 +55,14 @@ def test_root_saved_running_session_is_checked_before_load_session_projection():
"saved running-session root guard must run before loadSession(saved), " "saved running-session root guard must run before loadSession(saved), "
"otherwise loadSession already projects the session into the active pane" "otherwise loadSession already projects the session into the active pane"
) )
assert "_savedSessionShouldStaySidebarOnly" in block, ( assert "_savedSessionSidebarOnlyState" in block, (
"boot should delegate the saved-running metadata check to a named helper" "boot should delegate the saved-running metadata check to a named helper"
) )
def test_saved_running_session_helper_uses_metadata_only_and_runtime_markers(): def test_saved_running_session_helper_uses_metadata_only_and_runtime_markers():
"""The helper should inspect metadata without loading messages or attaching SSE.""" """The helper should inspect metadata without loading messages or attaching SSE."""
helper_idx = BOOT_JS.find("async function _savedSessionShouldStaySidebarOnly") helper_idx = BOOT_JS.find("async function _savedSessionSidebarOnlyState")
assert helper_idx > 0, "saved-running root policy helper not found" assert helper_idx > 0, "saved-running root policy helper not found"
helper = BOOT_JS[helper_idx:helper_idx + 1200] helper = BOOT_JS[helper_idx:helper_idx + 1200]
assert "/api/session?session_id=" in helper, ( assert "/api/session?session_id=" in helper, (
@@ -76,6 +76,10 @@ def test_saved_running_session_helper_uses_metadata_only_and_runtime_markers():
"helper must skip auto-opening archived localStorage sessions so root " "helper must skip auto-opening archived localStorage sessions so root "
"boot lands on the empty state instead of reopening archived chats" "boot lands on the empty state instead of reopening archived chats"
) )
assert "sidebarOnly:archived||running" in helper.replace(" ", ""), (
"helper must report the sidebar-only decision without conflating archived "
"sessions with running sessions"
)
assert "loadSession(" not in helper, ( assert "loadSession(" not in helper, (
"helper must not call loadSession(), because that would already project " "helper must not call loadSession(), because that would already project "
"the saved session into the active pane" "the saved session into the active pane"
@@ -85,7 +89,7 @@ def test_saved_running_session_helper_uses_metadata_only_and_runtime_markers():
def test_root_saved_running_sidebar_only_path_renders_empty_state_and_sidebar(): def test_root_saved_running_sidebar_only_path_renders_empty_state_and_sidebar():
"""Skipping projection should still leave the app usable and sidebar visible.""" """Skipping projection should still leave the app usable and sidebar visible."""
block = _boot_saved_session_block() block = _boot_saved_session_block()
helper_pos = block.find("_savedSessionShouldStaySidebarOnly") helper_pos = block.find("_savedSessionSidebarOnlyState")
render_pos = block.find("await renderSessionList()", helper_pos) render_pos = block.find("await renderSessionList()", helper_pos)
empty_pos = block.find("$('emptyState').style.display=''", helper_pos) empty_pos = block.find("$('emptyState').style.display=''", helper_pos)
return_pos = block.find("return;", helper_pos) return_pos = block.find("return;", helper_pos)
@@ -93,3 +97,18 @@ def test_root_saved_running_sidebar_only_path_renders_empty_state_and_sidebar():
assert empty_pos > helper_pos, "sidebar-only path must show the empty state" assert empty_pos > helper_pos, "sidebar-only path must show the empty state"
assert render_pos > helper_pos, "sidebar-only path must render the session list" assert render_pos > helper_pos, "sidebar-only path must render the session list"
assert return_pos > render_pos, "sidebar-only path should return before loadSession(saved)" assert return_pos > render_pos, "sidebar-only path should return before loadSession(saved)"
def test_root_archived_saved_session_clears_stale_localstorage_pointer():
"""Archived root-restore skips projection and clears stale saved-session state."""
block = _boot_saved_session_block()
helper_pos = block.find("_savedSessionSidebarOnlyState")
clear_guard = "if(savedSidebarOnlyState.archived)"
guard_pos = block.find(clear_guard, helper_pos)
clear_pos = block.find("localStorage.removeItem('hermes-webui-session')", guard_pos)
render_pos = block.find("await renderSessionList()", helper_pos)
load_pos = block.find("await loadSession(saved, {preserveActiveInput:true})")
assert guard_pos > helper_pos, "archived sidebar-only path must be distinguished"
assert clear_pos > guard_pos, "archived saved session must clear stale localStorage pointer"
assert clear_pos < render_pos, "stale pointer should be cleared before the sidebar-only return"
assert render_pos < load_pos, "archived saved session must return before loadSession(saved)"