Class: Clacky::Channel::ChannelUIController
- Inherits:
-
Object
- Object
- Clacky::Channel::ChannelUIController
- Includes:
- UIInterface
- Defined in:
- lib/clacky/server/channel/channel_ui_controller.rb
Overview
ChannelUIController implements UIInterface for IM platform sessions. It is registered as a subscriber on WebUIController so that every agent output event is forwarded here and sent back to the IM platform.
Design notes:
-
Tool calls / results / diffs / token usage are intentionally suppressed to keep IM chat clean. Only high-signal events are forwarded.
-
Buffering: file/shell previews accumulate in a buffer and are flushed as one message before the next assistant message, avoiding flooding.
-
request_confirmation is not invoked directly on this class — the Web UI handles the blocking wait and only sends show_warning notifications.
Constant Summary collapse
- BUFFER_FLUSH_SIZE =
flush early when buffer is large
5
Instance Attribute Summary collapse
-
#chat_id ⇒ Object
readonly
Returns the value of attribute chat_id.
-
#platform ⇒ Object
readonly
Returns the value of attribute platform.
Instance Method Summary collapse
- #append_output(content) ⇒ Object
- #buffer_line(line) ⇒ Object
-
#clear_input ⇒ Object
Input control / lifecycle (no-ops) ===.
- #clear_progress ⇒ Object
- #flush_buffer ⇒ Object
- #flush_buffer_unlocked ⇒ Object
-
#initialize(event, adapter) ⇒ ChannelUIController
constructor
A new instance of ChannelUIController.
- #log(message, level: :info) ⇒ Object
-
#request_confirmation(message, default: true) ⇒ Object
Blocking interaction === Not called directly — WebUIController handles the blocking wait and only notifies IM via show_warning.
- #send_file(path, name = nil) ⇒ Object
- #send_text(text) ⇒ Object
- #set_idle_status ⇒ Object
- #set_input_tips(message, type: :info) ⇒ Object
- #set_working_status ⇒ Object
- #show_assistant_message(content, files:) ⇒ Object
- #show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false) ⇒ Object
- #show_diff(old_content, new_content, max_lines: 50) ⇒ Object
- #show_error(message) ⇒ Object
- #show_file_edit_preview(path) ⇒ Object
- #show_file_error(error_message) ⇒ Object
- #show_file_write_preview(path, is_new_file:) ⇒ Object
-
#show_info(message, prefix_newline: true) ⇒ Object
Status messages ===.
-
#show_progress(message = nil, prefix_newline: true, output_buffer: nil) ⇒ Object
Progress ===.
- #show_shell_preview(command) ⇒ Object
- #show_success(message) ⇒ Object
- #show_token_usage(token_data) ⇒ Object
- #show_tool_args(formatted_args) ⇒ Object
- #show_tool_call(name, args) ⇒ Object
- #show_tool_error(error) ⇒ Object
- #show_tool_result(result) ⇒ Object
-
#show_user_message(content) ⇒ Object
Forward WebUI user messages to the IM channel so both sides stay in sync.
- #show_warning(message) ⇒ Object
- #stop ⇒ Object
-
#update_message_context(event) ⇒ Object
Update the reply context for the current inbound message.
-
#update_sessionbar(tasks: nil, cost: nil, status: nil) ⇒ Object
State updates (no-ops for IM) ===.
- #update_todos(todos) ⇒ Object
Methods included from UIInterface
Constructor Details
#initialize(event, adapter) ⇒ ChannelUIController
Returns a new instance of ChannelUIController.
25 26 27 28 29 30 31 32 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 25 def initialize(event, adapter) @platform = event[:platform] @chat_id = event[:chat_id] @message_id = event[:message_id] # original message to reply under @adapter = adapter @buffer = [] @mutex = Mutex.new end |
Instance Attribute Details
#chat_id ⇒ Object (readonly)
Returns the value of attribute chat_id.
23 24 25 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 23 def chat_id @chat_id end |
#platform ⇒ Object (readonly)
Returns the value of attribute platform.
23 24 25 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 23 def platform @platform end |
Instance Method Details
#append_output(content) ⇒ Object
111 112 113 114 115 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 111 def append_output(content) return if content.nil? || content.to_s.strip.empty? send_text(content) end |
#buffer_line(line) ⇒ Object
192 193 194 195 196 197 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 192 def buffer_line(line) @mutex.synchronize do @buffer << line flush_buffer_unlocked if @buffer.size >= BUFFER_FLUSH_SIZE end end |
#clear_input ⇒ Object
Input control / lifecycle (no-ops) ===
165 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 165 def clear_input; end |
#clear_progress ⇒ Object
145 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 145 def clear_progress; end |
#flush_buffer ⇒ Object
199 200 201 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 199 def flush_buffer @mutex.synchronize { flush_buffer_unlocked } end |
#flush_buffer_unlocked ⇒ Object
203 204 205 206 207 208 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 203 def flush_buffer_unlocked return if @buffer.empty? send_text(@buffer.join("\n")) @buffer.clear end |
#log(message, level: :info) ⇒ Object
135 136 137 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 135 def log(, level: :info) # Suppress end |
#request_confirmation(message, default: true) ⇒ Object
Blocking interaction ===
Not called directly — WebUIController handles the blocking wait and only notifies IM via show_warning. Implemented as auto-approve as a safety fallback in case this is ever called directly.
158 159 160 161 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 158 def request_confirmation(, default: true) send_text("Confirmation requested (auto-approved): #{}") default end |
#send_file(path, name = nil) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 180 def send_file(path, name = nil) if @adapter.respond_to?(:send_file) @adapter.send_file(@chat_id, path, name: name) else # Fallback for adapters that don't support file sending send_text("File: #{name || File.basename(path)}\n#{path}") end rescue StandardError => e Clacky::Logger.error("[ChannelUI] send_file failed (#{@platform}/#{@chat_id}): #{e.}") send_text("Failed to send file: #{File.basename(path)}\nError: #{e.}") end |
#send_text(text) ⇒ Object
170 171 172 173 174 175 176 177 178 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 170 def send_text(text) text = text.to_s.gsub(/<think>[\s\S]*?<\/think>\n*/i, "").strip return if text.empty? @adapter.send_text(@chat_id, text, reply_to: @message_id) rescue StandardError => e warn "[ChannelUI] send_text failed (#{@platform}/#{@chat_id}): #{e.}" nil end |
#set_idle_status ⇒ Object
152 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 152 def set_idle_status; end |
#set_input_tips(message, type: :info) ⇒ Object
166 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 166 def set_input_tips(, type: :info); end |
#set_working_status ⇒ Object
151 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 151 def set_working_status; end |
#show_assistant_message(content, files:) ⇒ Object
51 52 53 54 55 56 57 58 59 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 51 def (content, files:) flush_buffer Clacky::Logger.info("[ChannelUI] show_assistant_message files=#{files.size} content_len=#{content.to_s.length}") send_text(content) unless content.nil? || content.to_s.strip.empty? files.each do |f| Clacky::Logger.info("[ChannelUI] sending file path=#{f[:path].inspect} name=#{f[:name].inspect}") send_file(f[:path], f[:name]) end end |
#show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false) ⇒ Object
103 104 105 106 107 108 109 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 103 def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false) flush_buffer parts = ["Done", "#{iterations} step#{"s" if iterations != 1}"] parts << "$#{cost.round(4)}" if cost && cost > 0 parts << "#{duration.round(1)}s" if duration send_text(parts.join(" · ")) end |
#show_diff(old_content, new_content, max_lines: 50) ⇒ Object
95 96 97 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 95 def show_diff(old_content, new_content, max_lines: 50) # Diffs are too verbose for IM — suppress end |
#show_error(message) ⇒ Object
127 128 129 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 127 def show_error() send_text("Error: #{}") end |
#show_file_edit_preview(path) ⇒ Object
83 84 85 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 83 def show_file_edit_preview(path) buffer_line("edit: #{path}") end |
#show_file_error(error_message) ⇒ Object
91 92 93 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 91 def show_file_error() send_text("File error: #{}") end |
#show_file_write_preview(path, is_new_file:) ⇒ Object
78 79 80 81 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 78 def show_file_write_preview(path, is_new_file:) action = is_new_file ? "create" : "overwrite" buffer_line("#{action}: #{path}") end |
#show_info(message, prefix_newline: true) ⇒ Object
Status messages ===
119 120 121 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 119 def show_info(, prefix_newline: true) # Suppress informational noise in IM end |
#show_progress(message = nil, prefix_newline: true, output_buffer: nil) ⇒ Object
Progress ===
141 142 143 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 141 def show_progress( = nil, prefix_newline: true, output_buffer: nil) # Suppress — progress spinner has no IM equivalent end |
#show_shell_preview(command) ⇒ Object
87 88 89 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 87 def show_shell_preview(command) buffer_line("$ #{command}") end |
#show_success(message) ⇒ Object
131 132 133 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 131 def show_success() send_text() end |
#show_token_usage(token_data) ⇒ Object
99 100 101 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 99 def show_token_usage(token_data) # Suppress end |
#show_tool_args(formatted_args) ⇒ Object
74 75 76 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 74 def show_tool_args(formatted_args) # Suppress end |
#show_tool_call(name, args) ⇒ Object
61 62 63 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 61 def show_tool_call(name, args) # Suppress — too noisy for IM end |
#show_tool_error(error) ⇒ Object
69 70 71 72 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 69 def show_tool_error(error) msg = error.is_a?(Exception) ? error. : error.to_s send_text("Tool error: #{msg}") end |
#show_tool_result(result) ⇒ Object
65 66 67 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 65 def show_tool_result(result) # Suppress — too noisy for IM end |
#show_user_message(content) ⇒ Object
Forward WebUI user messages to the IM channel so both sides stay in sync. Prefixed with the product/user context so it’s clear who sent it.
45 46 47 48 49 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 45 def (content) return if content.nil? || content.to_s.strip.empty? send_text("[USER] #{content}") end |
#show_warning(message) ⇒ Object
123 124 125 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 123 def show_warning() send_text("Warning: #{}") end |
#stop ⇒ Object
167 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 167 def stop; end |
#update_message_context(event) ⇒ Object
Update the reply context for the current inbound message. Called at the start of each route_message so replies are threaded correctly.
37 38 39 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 37 def (event) @mutex.synchronize { @message_id = event[:message_id] } end |
#update_sessionbar(tasks: nil, cost: nil, status: nil) ⇒ Object
State updates (no-ops for IM) ===
149 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 149 def (tasks: nil, cost: nil, status: nil); end |
#update_todos(todos) ⇒ Object
150 |
# File 'lib/clacky/server/channel/channel_ui_controller.rb', line 150 def update_todos(todos); end |