fix: durable inflight reload snapshots via localStorage (#367)

* fix: persist durable inflight reload snapshots

* fix: remove duplicate loadInflightState stub, update CHANGELOG test count

The stub added in the previous review branch is superseded by the author's
real localStorage-backed implementation in the cherry-picked commit 36051c0.
Remove the duplicate. Update CHANGELOG to 961 tests and document the durable
inflight state feature.

---------

Co-authored-by: Jordan SkyLF <jordan@skylinkfiber.net>
Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
This commit is contained in:
nesquena-hermes
2026-04-13 16:25:31 -07:00
committed by GitHub
parent 9542639a90
commit 54181d1a07
4 changed files with 89 additions and 19 deletions

View File

@@ -723,6 +723,47 @@ function copyMsg(btn){
// ── Reconnect banner (B4/B5: reload resilience) ──
const INFLIGHT_KEY = 'hermes-webui-inflight'; // localStorage key for in-flight session tracking
const INFLIGHT_STATE_KEY = 'hermes-webui-inflight-state'; // localStorage snapshots for mid-stream reload recovery
function _readInflightStateMap(){
try{
const raw=localStorage.getItem(INFLIGHT_STATE_KEY);
const parsed=raw?JSON.parse(raw):{};
return parsed&&typeof parsed==='object'?parsed:{};
}catch(_){
return {};
}
}
function saveInflightState(sid, state){
if(!sid||!state) return;
try{
const all=_readInflightStateMap();
all[sid]={...state,updated_at:Date.now()};
localStorage.setItem(INFLIGHT_STATE_KEY, JSON.stringify(all));
}catch(_){ }
}
function loadInflightState(sid, streamId){
if(!sid) return null;
const all=_readInflightStateMap();
const entry=all[sid];
if(!entry) return null;
if(streamId&&entry.streamId&&entry.streamId!==streamId) return null;
if(entry.updated_at&&Date.now()-entry.updated_at>10*60*1000){
clearInflightState(sid);
return null;
}
return entry;
}
function clearInflightState(sid){
if(!sid) return;
try{
const all=_readInflightStateMap();
if(!(sid in all)) return;
delete all[sid];
if(Object.keys(all).length) localStorage.setItem(INFLIGHT_STATE_KEY, JSON.stringify(all));
else localStorage.removeItem(INFLIGHT_STATE_KEY);
}catch(_){ }
}
function markInflight(sid, streamId) {
localStorage.setItem(INFLIGHT_KEY, JSON.stringify({sid, streamId, ts: Date.now()}));
@@ -815,18 +856,6 @@ function getPendingSessionMessage(session){
_pending:true,
};
}
// loadInflightState — retrieve in-memory inflight state for a session.
// Called by loadSession() when active_stream_id is set on the server session
// but no INFLIGHT[sid] entry exists (e.g. after a session switch back).
// Returns the stored state dict or null. The else-path in loadSession handles
// page reloads directly via attachLiveStream when this returns null.
function loadInflightState(sid, streamId) {
// In-memory store: only survives within the same page load.
// If INFLIGHT[sid] exists but the caller already checked !INFLIGHT[sid],
// this won't be reached. Return null — the else path handles page reloads.
return null;
}
async function checkInflightOnBoot(sid) {
const raw = localStorage.getItem(INFLIGHT_KEY);
if (!raw) return;