fix: session URL hash lost when sending message mid-stream

Two independent bugs caused the session hash to disappear from the URL:

Bug 1 — ReferenceError in catch block silently killed error recovery
  In handleChatSubmit, two const variables (streamingTTS at line 1922 and
  abortCtrl at line 1741) were declared inside the try block but referenced
  in the catch block. Since const is block-scoped in JavaScript, they were
  undefined in catch, causing a ReferenceError that silently aborted the
  error handler. This prevented materializePendingSession() from ever being
  called, so no hash was written to the URL.
  Fix: Hoisted both as let declarations before the try { block.

Bug 2 — Dual sessions.js ES module instances with mismatched state
  app.js imported sessions.js with a version query string
  (?v=20260722ctxheader4) while every other module imported ./sessions.js
  without one. The browser treated them as different URLs, creating two
  separate module instances with independent _pendingChat and
  currentSessionId state. createDirectChat() set pending on one instance
  while handleChatSubmit() checked hasPendingChat() on the other — so the
  pending session never materialized.
  Fix: Removed the version query string from the sessions.js import in
  app.js and from the modulepreload + script tags in index.html. All
  modules now share a single sessions.js instance.

Bonus guard: _adoptOpenedSessionBeforeAutoCreate() now checks
hasPendingChat() before adopting a stale DOM-active session, preventing
the send path from landing in the wrong session when a New Chat is pending.
This commit is contained in:
samy 2026-08-01 23:04:22 -04:00
parent 1de596efb4
commit 6058be7088
3 changed files with 13 additions and 8 deletions

View file

@ -10,14 +10,14 @@ import modelsModule from './js/models.js?v=20260715startupcalm2';
import ragModule from './js/rag.js'; import ragModule from './js/rag.js';
import presetsModule from './js/presets.js'; import presetsModule from './js/presets.js';
import searchModule from './js/search.js'; import searchModule from './js/search.js';
import chatModule from './js/chat.js?v=20260722ctxheader4'; import chatModule from './js/chat.js?v=20260801fix1';
import compareModule from './js/compare/index.js?v=20260723compareicon2'; import compareModule from './js/compare/index.js?v=20260723compareicon2';
import documentModule from './js/document.js?v=20260722emailfastindex1'; import documentModule from './js/document.js?v=20260722emailfastindex1';
import searchChatModule from './js/search-chat.js'; import searchChatModule from './js/search-chat.js';
import { makeWindowDraggable } from './js/windowDrag.js'; import { makeWindowDraggable } from './js/windowDrag.js';
import markdownModule from './js/markdown.js'; import markdownModule from './js/markdown.js';
import chatRenderer from './js/chatRenderer.js?v=20260722emailfastindex1'; import chatRenderer from './js/chatRenderer.js?v=20260722emailfastindex1';
import sessionModule from './js/sessions.js?v=20260722ctxheader4'; import sessionModule from './js/sessions.js';
import memoryModule from './js/memory.js?v=20260722memoryloading1'; import memoryModule from './js/memory.js?v=20260722memoryloading1';
import voiceRecorderModule from './js/voiceRecorder.js'; import voiceRecorderModule from './js/voiceRecorder.js';
import censorModule from './js/censor.js'; import censorModule from './js/censor.js';

View file

@ -250,9 +250,9 @@
</script> </script>
<link rel="stylesheet" href="/static/style.css?v=20260723tasksbulkfeedback1"> <link rel="stylesheet" href="/static/style.css?v=20260723tasksbulkfeedback1">
<link rel="modulepreload" href="/static/app.js?v=20260723tasksbulkfeedback1"> <link rel="modulepreload" href="/static/app.js?v=20260723tasksbulkfeedback1">
<link rel="modulepreload" href="/static/js/chat.js?v=20260722ctxheader4"> <link rel="modulepreload" href="/static/js/chat.js?v=20260801fix1">
<link rel="modulepreload" href="/static/js/ui.js"> <link rel="modulepreload" href="/static/js/ui.js">
<link rel="modulepreload" href="/static/js/sessions.js?v=20260722ctxheader4"> <link rel="modulepreload" href="/static/js/sessions.js">
<link rel="modulepreload" href="/static/js/markdown.js"> <link rel="modulepreload" href="/static/js/markdown.js">
</head> </head>
<body> <body>
@ -2504,7 +2504,7 @@
<script type="module" src="/static/js/ui.js"></script> <script type="module" src="/static/js/ui.js"></script>
<script type="module" src="/static/js/markdown.js"></script> <script type="module" src="/static/js/markdown.js"></script>
<script type="module" src="/static/js/dragSort.js"></script> <script type="module" src="/static/js/dragSort.js"></script>
<script type="module" src="/static/js/sessions.js?v=20260722ctxheader4"></script> <script type="module" src="/static/js/sessions.js"></script>
<script type="module" src="/static/js/memory.js?v=20260722memoryloading1"></script> <script type="module" src="/static/js/memory.js?v=20260722memoryloading1"></script>
<script type="module" src="/static/js/skills.js"></script> <script type="module" src="/static/js/skills.js"></script>
<script type="module" src="/static/js/tourHints.js"></script> <script type="module" src="/static/js/tourHints.js"></script>
@ -2522,7 +2522,7 @@
<script type="module" src="/static/js/chatRenderer.js?v=20260722emailfastindex1"></script> <script type="module" src="/static/js/chatRenderer.js?v=20260722emailfastindex1"></script>
<script type="module" src="/static/js/codeRunner.js"></script> <script type="module" src="/static/js/codeRunner.js"></script>
<script type="module" src="/static/js/chatStream.js?v=20260722emailfastindex1"></script> <script type="module" src="/static/js/chatStream.js?v=20260722emailfastindex1"></script>
<script type="module" src="/static/js/chat.js?v=20260722ctxheader4"></script> <script type="module" src="/static/js/chat.js?v=20260801fix1"></script>
<script type="module" src="/static/js/cookbook.js"></script> <script type="module" src="/static/js/cookbook.js"></script>
<script src="/static/js/cookbookSchedule.js"></script> <script src="/static/js/cookbookSchedule.js"></script>
<script type="module" src="/static/js/search-chat.js"></script> <script type="module" src="/static/js/search-chat.js"></script>

View file

@ -349,6 +349,9 @@ import { wireArrowUpRecall, getUserMessagesFromChatHistory } from './composerArr
async function _adoptOpenedSessionBeforeAutoCreate() { async function _adoptOpenedSessionBeforeAutoCreate() {
if (!sessionModule || !sessionModule.getCurrentSessionId || sessionModule.getCurrentSessionId()) return true; if (!sessionModule || !sessionModule.getCurrentSessionId || sessionModule.getCurrentSessionId()) return true;
// Don't adopt a stale session when the user explicitly started a New Chat
// (pending state set) — the send path must materialize the pending session.
if (sessionModule.hasPendingChat && sessionModule.hasPendingChat()) return false;
const activeRowId = document.querySelector('.list-item.active-session[data-session-id], .session-item.active[data-session-id]')?.dataset?.sessionId || ''; const activeRowId = document.querySelector('.list-item.active-session[data-session-id], .session-item.active[data-session-id]')?.dataset?.sessionId || '';
const hashId = _hashSessionCandidate(); const hashId = _hashSessionCandidate();
const lastSelectedId = String(window.__odysseusLastSelectedSessionId || '').trim(); const lastSelectedId = String(window.__odysseusLastSelectedSessionId || '').trim();
@ -1403,6 +1406,8 @@ import { wireArrowUpRecall, getUserMessagesFromChatHistory } from './composerArr
currentAccumulated = ''; currentAccumulated = '';
currentHolder = null; currentHolder = null;
let abortCtrl = null;
let streamingTTS = false;
try { try {
// Re-enable auto-scroll when user sends a message // Re-enable auto-scroll when user sends a message
uiModule.setAutoScroll(true); uiModule.setAutoScroll(true);
@ -1716,7 +1721,7 @@ import { wireArrowUpRecall, getUserMessagesFromChatHistory } from './composerArr
} }
const abortCtrl = new AbortController(); abortCtrl = new AbortController();
abortCtrl._reason = ''; abortCtrl._reason = '';
currentAbort = abortCtrl; currentAbort = abortCtrl;
@ -1897,7 +1902,7 @@ import { wireArrowUpRecall, getUserMessagesFromChatHistory } from './composerArr
let isThinking = false; let isThinking = false;
let thinkingStartTime = null; let thinkingStartTime = null;
// Streaming TTS: synthesize sentence-by-sentence during streaming // Streaming TTS: synthesize sentence-by-sentence during streaming
const streamingTTS = !!(window.aiTTSManager && window.aiTTSManager.autoPlay && window.aiTTSManager.available); streamingTTS = !!(window.aiTTSManager && window.aiTTSManager.autoPlay && window.aiTTSManager.available);
if (streamingTTS) window.aiTTSManager.streamingStart(); if (streamingTTS) window.aiTTSManager.streamingStart();
// Multi-bubble agent tracking // Multi-bubble agent tracking
let roundHolder = holder; // Current AI text bubble (changes per round) let roundHolder = holder; // Current AI text bubble (changes per round)