Skip to content

fix: centralized WebSocket liveness checks and UI status indicator#2

Closed
menny wants to merge 4 commits intomainfrom
fix/infrastructure-stability
Closed

fix: centralized WebSocket liveness checks and UI status indicator#2
menny wants to merge 4 commits intomainfrom
fix/infrastructure-stability

Conversation

@menny
Copy link
Copy Markdown
Owner

@menny menny commented Apr 23, 2026

Summary

This Pull Request addresses several critical stability issues across all LLM providers and adds a visual connection indicator to the UI.

Key Changes

  1. WebSocket Safety: Enhanced WebSocketWriter in server/index.js with readyState checks and try-catch blocks to prevent unhandled exceptions from crashing the server when a client disconnects.
  2. Heartbeat Mechanism: Implemented a server-side ping/pong heartbeat to actively detect and terminate dead connections every 30 seconds.
  3. Automatic Cleanup: Updated the WebSocket disconnect handler to proactively kill orphaned CLI processes (Gemini, Cursor, Codex, Claude) associated with the closed socket.
  4. UTF-8 Streaming Fix: Replaced Buffer.toString() with StringDecoder in gemini-cli.js and cursor-cli.js to correctly handle multi-byte characters split across data chunks.
  5. Syntax & Import Fixes: Resolved duplicate export errors in claude-sdk.js and corrected the Codex SDK import name.
  6. UI Status Dot: Added a small connectivity indicator in the SidebarHeader (Green: Connected, Red: Disconnected).

Impact

  • Prevents the entire Node.js server from crashing due to network flakiness.
  • Ensures background resources are freed when users close their browser tabs.
  • Improves text rendering quality for streamed responses.

menny and others added 4 commits April 22, 2026 18:57
…g requests

SessionManager loads all persisted Gemini sessions from ~/.gemini/sessions/*.json
asynchronously in its constructor via `this.ready = this.init()`. Previously,
`sessionManager.ready` was never awaited anywhere, so the server would start
accepting WebSocket connections before loadSessions() had completed.

This created a race condition on the very first request after a server restart:
if a client tried to resume an existing Gemini session, `sessionManager.getSession(sessionId)`
would return undefined (the session hadn't been loaded from disk yet), causing
the `--resume <cliSessionId>` flag to be silently omitted when spawning the
Gemini CLI process. The CLI would then start a blank new session instead of
continuing the prior conversation, with no error or indication to the user.

The fix adds `await sessionManager.ready` in startServer() immediately after
`await initializeDatabase()`, following the same pattern already established for
database initialization. This guarantees the session store is fully populated
before any request handler can call getSession(), createSession(), or addMessage().

No other providers are affected: Claude, Cursor, and Codex session providers are
stateless per-request (they read from disk or SQLite on demand) and have no
equivalent eager-loading singleton.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This comprehensive update addresses Issue siteboon#12 and enhances overall system stability:

1.  Safe WebSocket Writing: Enhanced WebSocketWriter in server/index.js with readyState checks and try-catch blocks to prevent server crashes on client disconnects.
2.  Server-Side Heartbeat: Implemented a ping/pong mechanism to actively detect and terminate dead connections.
3.  Automatic Process Cleanup: Updated the disconnect handler to proactively kill orphaned CLI processes (Gemini, Cursor, Codex, Claude) when their associated WebSocket closes.
4.  Provider Migration: Migrated all major providers (Gemini, Cursor, Codex, Claude SDK) to use the safe WebSocketWriter instead of raw .send() calls.
5.  UI Connection Indicator: Added a visual status dot in the SidebarHeader to indicate WebSocket connectivity state (Green: Connected, Red: Disconnected).
Used StringDecoder from the string_decoder module instead of Buffer.toString() in
gemini-cli.js and cursor-cli.js. This ensures that multi-byte characters that are
split across process stdout/stderr data chunks are correctly decoded, preventing
garbled text at chunk boundaries in streamed LLM responses.
Cleaned up duplicate export keywords from function definitions that were also
being exported in the centralized export blocks at the end of the files.
This fixes the SyntaxError that was causing the server to crash on startup.
@menny menny closed this Apr 23, 2026
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.

1 participant