Class: TUI::CableClient
- Inherits:
-
Object
- Object
- TUI::CableClient
- Defined in:
- lib/tui/cable_client.rb
Overview
Action Cable WebSocket client for connecting the TUI to the brain server. Runs the WebSocket connection in a background thread and exposes a thread-safe message queue for the TUI render loop to drain.
Implements the actioncable-v1-json protocol: subscribe to a SessionChannel, receive event broadcasts, and send user input via the speak action.
Automatically reconnects with exponential backoff when the connection drops unexpectedly. Detects stale connections via Action Cable ping heartbeat monitoring.
Constant Summary collapse
- MSG_TYPE_CONNECTION =
Message types queued for the TUI render loop via @message_queue
"connection"- STATUS_SUBSCRIBING =
Connection status values sent as MSG_TYPE_CONNECTION messages. These are message-level concepts for the TUI — distinct from the internal @status state machine (:disconnected, :connecting, etc.).
"subscribing"- STATUS_SUBSCRIBED =
"subscribed"- STATUS_REJECTED =
"rejected"- STATUS_DISCONNECTED =
"disconnected"- STATUS_RECONNECTING =
"reconnecting"- STATUS_FAILED =
"failed"
Instance Attribute Summary collapse
-
#host ⇒ String
readonly
Brain server host:port.
-
#reconnect_attempt ⇒ Integer
readonly
Current reconnection attempt (0 when connected).
-
#session_id ⇒ Integer
readonly
Current session ID.
-
#status ⇒ Symbol
readonly
Connection status (:disconnected, :connecting, :connected, :subscribed, :reconnecting).
Instance Method Summary collapse
-
#change_view_mode(mode) ⇒ void
Requests the brain to change the session’s view mode.
-
#connect ⇒ Object
Opens the WebSocket connection in a background thread.
-
#create_session ⇒ Object
Requests the brain to create a new session and switch to it.
-
#disconnect ⇒ Object
Closes the WebSocket connection and cleans up the background thread.
-
#drain_messages ⇒ Array<Hash>
Drains all pending messages from the queue (non-blocking).
-
#initialize(host:, session_id: nil) ⇒ CableClient
constructor
A new instance of CableClient.
-
#interrupt ⇒ void
Requests interruption of the current tool execution.
-
#list_sessions(limit: 10) ⇒ Object
Requests a list of recent sessions from the brain.
-
#recall_pending(pending_message_id) ⇒ Object
Requests the brain to recall (delete) a pending message so the user can edit it before the LLM sees it.
-
#save_token(token) ⇒ Object
Sends an Anthropic subscription token to the brain for validation and storage.
-
#speak(content) ⇒ Object
Sends user input to the brain for processing.
-
#switch_session(session_id) ⇒ Object
Requests the brain to switch to an existing session.
-
#update_session_id(new_id) ⇒ Object
Updates the local session ID reference after a server-side session switch.
Constructor Details
#initialize(host:, session_id: nil) ⇒ CableClient
Returns a new instance of CableClient.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/tui/cable_client.rb', line 56 def initialize(host:, session_id: nil) @host = host @session_id = session_id @subscribed_session_id = session_id @status = :disconnected @message_queue = Thread::Queue.new @mutex = Mutex.new @ws = nil @ws_thread = nil @intentional_disconnect = false @reconnect_attempt = 0 @last_ping_at = nil @connection_generation = 0 end |
Instance Attribute Details
#host ⇒ String (readonly)
Returns brain server host:port.
41 42 43 |
# File 'lib/tui/cable_client.rb', line 41 def host @host end |
#reconnect_attempt ⇒ Integer (readonly)
Returns current reconnection attempt (0 when connected).
52 53 54 |
# File 'lib/tui/cable_client.rb', line 52 def reconnect_attempt @reconnect_attempt end |
#session_id ⇒ Integer (readonly)
Returns current session ID.
44 45 46 |
# File 'lib/tui/cable_client.rb', line 44 def session_id @session_id end |
#status ⇒ Symbol (readonly)
Returns connection status (:disconnected, :connecting, :connected, :subscribed, :reconnecting). Note: the “subscribing” concept exists only as a message-level status (see STATUS_SUBSCRIBING) queued for the TUI, not as an internal state.
49 50 51 |
# File 'lib/tui/cable_client.rb', line 49 def status @status end |
Instance Method Details
#change_view_mode(mode) ⇒ void
This method returns an undefined value.
Requests the brain to change the session’s view mode. The server broadcasts view_mode_changed to all clients on the session, followed by the re-decorated viewport.
118 119 120 |
# File 'lib/tui/cable_client.rb', line 118 def change_view_mode(mode) send_action("change_view_mode", {"view_mode" => mode}) end |
#connect ⇒ Object
Opens the WebSocket connection in a background thread. The connection subscribes to the session channel automatically after receiving the Action Cable welcome message. Reconnects automatically on unexpected disconnection.
75 76 77 78 79 80 81 |
# File 'lib/tui/cable_client.rb', line 75 def connect @mutex.synchronize do @intentional_disconnect = false @status = :connecting end @ws_thread = Thread.new { run_websocket_loop } end |
#create_session ⇒ Object
Requests the brain to create a new session and switch to it. The server responds with a session_changed message followed by history.
92 93 94 |
# File 'lib/tui/cable_client.rb', line 92 def create_session send_action("create_session", {}) end |
#disconnect ⇒ Object
Closes the WebSocket connection and cleans up the background thread. Prevents automatic reconnection.
170 171 172 173 174 175 176 177 |
# File 'lib/tui/cable_client.rb', line 170 def disconnect @mutex.synchronize do @intentional_disconnect = true @status = :disconnected end @ws&.close @ws_thread&.join(Settings.connection_disconnect_timeout) end |
#drain_messages ⇒ Array<Hash>
Drains all pending messages from the queue (non-blocking). Call this from the TUI render loop to process incoming events.
158 159 160 161 162 163 164 165 166 |
# File 'lib/tui/cable_client.rb', line 158 def = [] loop do << @message_queue.pop(true) rescue ThreadError break end end |
#interrupt ⇒ void
This method returns an undefined value.
Requests interruption of the current tool execution. The server sets an interrupt flag that the LLM client checks between tool calls.
134 135 136 |
# File 'lib/tui/cable_client.rb', line 134 def interrupt send_action("interrupt_execution", {}) end |
#list_sessions(limit: 10) ⇒ Object
Requests a list of recent sessions from the brain. The server responds with a sessions_list message.
108 109 110 |
# File 'lib/tui/cable_client.rb', line 108 def list_sessions(limit: 10) send_action("list_sessions", {"limit" => limit}) end |
#recall_pending(pending_message_id) ⇒ Object
Requests the brain to recall (delete) a pending message so the user can edit it before the LLM sees it.
126 127 128 |
# File 'lib/tui/cable_client.rb', line 126 def recall_pending() send_action("recall_pending", {"pending_message_id" => }) end |
#save_token(token) ⇒ Object
Sends an Anthropic subscription token to the brain for validation and storage. The token flows directly from TUI input to the encrypted secrets table — never enters the LLM context window.
143 144 145 |
# File 'lib/tui/cable_client.rb', line 143 def save_token(token) send_action("save_token", {"token" => token}) end |
#speak(content) ⇒ Object
Sends user input to the brain for processing.
86 87 88 |
# File 'lib/tui/cable_client.rb', line 86 def speak(content) send_action("speak", {"content" => content}) end |
#switch_session(session_id) ⇒ Object
Requests the brain to switch to an existing session. The server responds with a session_changed message followed by history.
100 101 102 |
# File 'lib/tui/cable_client.rb', line 100 def switch_session(session_id) send_action("switch_session", {"session_id" => session_id}) end |
#update_session_id(new_id) ⇒ Object
Updates the local session ID reference after a server-side session switch.
150 151 152 |
# File 'lib/tui/cable_client.rb', line 150 def update_session_id(new_id) @mutex.synchronize { @session_id = new_id } end |