From 8ee9620bf0542de0ff68c2ae5328e731b389f4ee Mon Sep 17 00:00:00 2001 From: tomekpanek Date: Thu, 4 Jun 2026 00:00:15 +0200 Subject: [PATCH 1/2] fix(chat): bootstrap TUI bridge on demand for chat.send --- server.js | 43 ++++++++++++++++++++++++++++++++++------ src/js/chat/websocket.js | 2 +- src/js/ws-client.js | 4 ++-- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/server.js b/server.js index fd0ed5b..487caee 100644 --- a/server.js +++ b/server.js @@ -6239,14 +6239,45 @@ wss.on('connection', async (socket, req) => { } } if (msg.type === 'chat.stop' && socket.authed && socket.tuiBridge) { - socket.tuiBridge.chatStop(socket.tuiSessionId); + socket.tuiBridge.chatStop(msg.session_id || socket.tuiSessionId); } - if (msg.type === 'chat.send' && socket.authed && socket.tuiBridge) { + if (msg.type === 'chat.send' && socket.authed) { try { - // Use frontend's session_id if provided, else fall back to chat.start session - const sid = msg.session_id || socket.tuiSessionId; - if (msg.session_id) socket.tuiSessionId = msg.session_id; // update for future sends - await socket.tuiBridge.chatSend({ message: msg.message, session_id: sid }); + if (!socket.tuiBridge) { + const profileName = msg.profile || 'default'; + console.log(`[WS] chat.send bootstrapping bridge profile="${profileName}" session_id="${msg.session_id || 'null'}"`); + const bridge = getBridge(profileName); + if (!bridge.proc) { + let startErr = null; + for (let attempt = 1; attempt <= 3; attempt++) { + try { + await bridge.start(); + startErr = null; + break; + } catch (e) { + startErr = e; + console.error(`[WS] Bridge start attempt ${attempt}/3 failed:`, e.message); + if (attempt < 3) await new Promise(r => setTimeout(r, 500)); + } + } + if (startErr) { + socket.send(JSON.stringify({ type: 'chat.error', error: 'TUI gateway unavailable after 3 retries.' })); + return; + } + } + bridge.addClient(socket); + socket.tuiBridge = bridge; + } + if (msg.session_id && msg.session_id !== socket.tuiSessionId) { + const result = await socket.tuiBridge.chatStart({ + message: msg.message, + session_id: msg.session_id, + }); + socket.tuiSessionId = result.session_id; + } else { + const sid = msg.session_id || socket.tuiSessionId; + await socket.tuiBridge.chatSend({ message: msg.message, session_id: sid }); + } } catch (err) { socket.send(JSON.stringify({ type: 'chat.error', error: err.message })); } diff --git a/src/js/chat/websocket.js b/src/js/chat/websocket.js index 754f2ab..96ef64d 100644 --- a/src/js/chat/websocket.js +++ b/src/js/chat/websocket.js @@ -48,7 +48,7 @@ async function sendViaWebSocket(text, profile, sessionId) { // Send via WS — use chatStart for first message, chatSend for subsequent let ok; if (sessionId) { - ok = wsClient.chatSend({ message: text, session_id: sessionId }); + ok = wsClient.chatSend({ message: text, session_id: sessionId, profile }); } else { ok = wsClient.chatStart({ message: text, profile, session_id: sessionId }); } diff --git a/src/js/ws-client.js b/src/js/ws-client.js index fbc2495..8679b8e 100644 --- a/src/js/ws-client.js +++ b/src/js/ws-client.js @@ -89,8 +89,8 @@ class HciWsClient extends EventTarget { chatStart({ message, profile, session_id, model }) { return this.send({ type: 'chat.start', message, profile, session_id, model }); } - chatSend({ message, session_id }) { - return this.send({ type: 'chat.send', message, session_id }); + chatSend({ message, session_id, profile }) { + return this.send({ type: 'chat.send', message, session_id, profile }); } chatStop() { return this.send({ type: 'chat.stop' }); From 013637462af1b874d5e9a1e5dd4ac3f967c85b86 Mon Sep 17 00:00:00 2001 From: tomekpanek Date: Thu, 4 Jun 2026 00:00:31 +0200 Subject: [PATCH 2/2] fix(chat): detach prior bridge when WS sees a profile switch --- server.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 487caee..04e34b5 100644 --- a/server.js +++ b/server.js @@ -6208,7 +6208,13 @@ wss.on('connection', async (socket, req) => { // ── Chat via WebSocket (TUI Gateway) ── if (msg.type === 'chat.start' && socket.authed) { console.log(`[WS] chat.start received profile="${msg.profile || 'default'}" session_id="${msg.session_id || 'null'}"`); - const bridge = getBridge(msg.profile || 'default'); + const profileName = msg.profile || 'default'; + if (socket.tuiBridge && socket.tuiBridge.profile !== profileName) { + socket.tuiBridge.removeClient(socket); + socket.tuiBridge = null; + socket.tuiSessionId = undefined; + } + const bridge = getBridge(profileName); if (!bridge.proc) { let startErr = null; for (let attempt = 1; attempt <= 3; attempt++) { @@ -6243,8 +6249,13 @@ wss.on('connection', async (socket, req) => { } if (msg.type === 'chat.send' && socket.authed) { try { + const profileName = msg.profile || 'default'; + if (socket.tuiBridge && socket.tuiBridge.profile !== profileName) { + socket.tuiBridge.removeClient(socket); + socket.tuiBridge = null; + socket.tuiSessionId = undefined; + } if (!socket.tuiBridge) { - const profileName = msg.profile || 'default'; console.log(`[WS] chat.send bootstrapping bridge profile="${profileName}" session_id="${msg.session_id || 'null'}"`); const bridge = getBridge(profileName); if (!bridge.proc) {