Skip to content

fix(chat): bootstrap TUI bridge on demand for chat.send + detach on profile switch#73

Merged
xaspx merged 2 commits into
xaspx:mainfrom
tomekpanek:fix/chat-send-bootstrap-bridge
Jun 4, 2026
Merged

fix(chat): bootstrap TUI bridge on demand for chat.send + detach on profile switch#73
xaspx merged 2 commits into
xaspx:mainfrom
tomekpanek:fix/chat-send-bootstrap-bridge

Conversation

@tomekpanek

Copy link
Copy Markdown
Contributor

Two related fixes for the WebSocket chat path, both surfaced by the same
test sequence.

Bug A — chat.send silently drops when the WS connection hasn't seen chat.start yet

Repro:

  1. Restart node server.js (cold WS connection, socket.tuiBridge === null)
  2. Open HCI, without sending a new chat first, click an existing session in the sidebar
  3. Send a message

Expected: message routes to the gateway, response streams back.
Actual: nothing happens. User bubble + blinking cursor, but no response, ever. Stop button is also dead.

Cause: PR #68 (1419564) introduced chat.send as a lightweight follow-up to chat.start. The handler is gated on socket.tuiBridge. The frontend (sendViaWebSocket) uses wsClient.chatSend whenever session_id is set — including the first message of a session opened directly from the sidebar. With no prior chat.start, socket.tuiBridge === null, the guard short-circuits, and the message is dropped server-side with no acknowledgement.

Fix: drop the && socket.tuiBridge gate, bootstrap the bridge in-place (same retry loop chat.start uses), and when msg.session_id !== socket.tuiSessionId route through bridge.chatStart so the gateway runs session.resume + prompt.submit (the lightweight bridge.chatSend can only target sessions already mapped in _sidMap).

Frontend changes (ws-client.js, chat/websocket.js) pass profile on chat.send so the server knows which gateway port to bootstrap.

Bug B — switching profile mid-session spawns a new thread in the wrong profile

Repro (with Bug A fix applied, otherwise can't get this far):

  1. Open HCI on profile default, start a chat
  2. Switch chat-profile dropdown to a non-default profile (e.g. tomek)
  3. Click one of its existing sessions in the sidebar
  4. Send a message

Expected: response streams in the clicked session.
Actual: response arrives, but a new entry appears in the default profile's session list. Subsequent messages keep spawning fresh threads in default.

Server log shows the smoking gun:

[TUI:default] session.resume(20260603_180322_d0f300)
[TUI:default] session.resume FAILED (session not found), creating new session

Cause: chat.start binds the socket to profile A's bridge subprocess. Switching the dropdown to B and clicking a B-session sends chat.send with msg.profile = "B", but socket.tuiBridge is still A. The session_id lookup happens in A's DB, fails, and the bridge's auto-resume fallback in chatStart silently spawns a new session via session.create → new thread in A.

Fix: on both chat.start and chat.send, when socket.tuiBridge.profile !== msg.profile, removeClient and null out the binding so the next getBridge / bootstrap routes through the right subprocess.

Commits

  1. fix(chat): bootstrap TUI bridge on demand for chat.send — Bug A
  2. fix(chat): detach prior bridge when WS sees a profile switch — Bug B

Verified

Tested locally on top of main (204e0e5, v3.6.0): both repros eliminated, normal chat flow unaffected. Two separate commits so each fix can be reviewed in isolation.

@xaspx xaspx merged commit d7bba84 into xaspx:main Jun 4, 2026
0 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants