fix: Streaming-Navigation-Bugs (doppelte Messages, client_disconnected), Tool-Result ohne Truncation

- _chatAbandoned-Flag statt AbortController: Backend-Stream läuft bei Navigation weiter, Frontend ignoriert Events
- stopHistoryPolling() während Streaming-Poll aktiv, verhindert parallele Polls
- _pollLock in pollHistory() gegen Race-Conditions bei gleichzeitigen Poll-Aufrufen
- Tool-Result truncate(300) im Live-Chat entfernt, in History beibehalten
- Dashboard Badge-Styling verfeinert
This commit is contained in:
Meleki committed 2026-03-01 00:32:05 +01:00
1 parent 6f800e2d50
commit 7c0e5d180a
5 files changed
+30 -7

No files matched your search

+4 -2
View File
@@ -75,10 +75,12 @@
.badge {
padding: var(--badge-padding);
background: var(--f1-bg);
border: var(--border-width) solid var(--f1-border);
background: var(--f2-bg);
border: var(--border-width) solid var(--f2-border);
white-space: nowrap;
flex-shrink: 0;
font-size: var(--font-size);
border-radius: var(--border-radius);
}
#newAgentCard,
+8 -1
View File
@@ -33,6 +33,7 @@ let isStreaming = false;
let pendingFiles = []; // [{ name, type, base64 }]
let pendingRefs = []; // [{ path, from, to }] - Workspace-Dateireferenzen
let _lastDoneChainData = null; // Chain-Daten vom letzten done-Event
let _chatAbandoned = false; // true wenn User weg-navigiert während Stream läuft
async function clearChat() {
const archive = await apiArchive(getCurrentSessionId());
@@ -102,6 +103,8 @@ async function sendMessage() {
let fullText = '';
let currentToolBox = null;
_chatAbandoned = false;
try {
const resp = await apiChat(messages, getCurrentSessionId());
const reader = resp.body.getReader();
@@ -111,7 +114,7 @@ async function sendMessage() {
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (done || _chatAbandoned) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
@@ -184,6 +187,8 @@ async function sendMessage() {
}
} catch (e) {
// Bei Navigation nichts tun – der neue Page-Init übernimmt
if (_chatAbandoned) return;
endStreamBubble();
// Prüfen ob Backend noch streamt (z.B. bei Netzwerkfehler)
try {
@@ -197,6 +202,8 @@ async function sendMessage() {
} catch (_) {}
addErrorBubble('Netzwerk-Fehler: ' + e.message);
} finally {
// Bei Navigation nichts aufräumen – der neue Page-Init übernimmt
if (_chatAbandoned) return;
if (!isStreaming || !_streamingPollInterval) {
btn.disabled = false;
isStreaming = false;
+7 -1
View File
@@ -143,7 +143,7 @@ async function loadLatestArchive() {
if (lastToolBox && !lastToolBox.querySelector('.tool-result')) {
const rd = document.createElement('div');
rd.className = 'tool-result' + (msg.isError ? ' tool-error' : '');
rd.textContent = truncate(msg.content, 300);
rd.textContent = truncate(msg.content || '', 300);
lastToolBox.appendChild(rd);
}
continue; // kein separates div
@@ -183,6 +183,8 @@ async function loadLatestArchive() {
// ── Polling ──
let _pollLock = false;
function startHistoryPolling(intervalMs = 3000) {
if (historyPollInterval) return;
historyPollInterval = setInterval(pollHistory, intervalMs);
@@ -197,6 +199,8 @@ function stopHistoryPolling() {
async function pollHistory(force = false) {
if (isStreaming && !force) return;
if (_pollLock) return;
_pollLock = true;
try {
const newMessages = await apiHistoryPoll(getCurrentSessionId(), lastKnownMessageId);
@@ -206,5 +210,7 @@ async function pollHistory(force = false) {
}
} catch (e) {
console.error('History poll fehlgeschlagen:', e);
} finally {
_pollLock = false;
}
}
+8
View File
@@ -5,6 +5,8 @@ let _recoveryChainId = null;
function startStreamingPoll(sessionId, sendBtn, chainId) {
_recoveryChainId = chainId;
// History-Polling stoppen – Streaming-Poll übernimmt
stopHistoryPolling();
if (_streamingPollInterval) clearInterval(_streamingPollInterval);
_streamingPollInterval = setInterval(async () => {
try {
@@ -29,6 +31,9 @@ function startStreamingPoll(sessionId, sendBtn, chainId) {
}
setMeta('Bereit');
_recoveryChainId = null;
// History-Polling wieder starten
startHistoryPolling();
}
} catch (e) {
console.error('Streaming-Poll fehlgeschlagen:', e);
@@ -274,6 +279,9 @@ registerPage('chat', {
},
destroy() {
// Stream nicht abbrechen (Backend soll weiterlaufen),
// aber Frontend-Processing stoppen
_chatAbandoned = true;
if (this._closeMenu) {
document.removeEventListener('click', this._closeMenu);
this._closeMenu = null;
+3 -3
View File
@@ -238,7 +238,7 @@ function renderToolBoxContent(div, name, inputJson, result, isError) {
const resultDiv = document.createElement('div');
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
resultDiv.textContent = result ? truncate(result, 300) : '';
resultDiv.textContent = result || '';
resultDiv.hidden = true;
div.appendChild(resultDiv);
foldTarget = resultDiv;
@@ -340,12 +340,12 @@ function appendToolResult(result, isError) {
const existing = lastBox.querySelector('.tool-result');
if (existing) {
existing.textContent = truncate(result, 300);
existing.textContent = result || '';
if (isError) existing.classList.add('tool-error');
} else {
const resultDiv = document.createElement('div');
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
resultDiv.textContent = truncate(result, 300);
resultDiv.textContent = result || '';
resultDiv.hidden = true;
lastBox.appendChild(resultDiv);
}