smart_message 0.0.10 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +38 -0
  3. data/.gitignore +5 -0
  4. data/CHANGELOG.md +64 -0
  5. data/Gemfile.lock +35 -4
  6. data/README.md +169 -71
  7. data/Rakefile +29 -4
  8. data/docs/assets/images/ddq_architecture.svg +130 -0
  9. data/docs/assets/images/dlq_architecture.svg +115 -0
  10. data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
  11. data/docs/assets/images/enhanced-fluent-api.svg +149 -0
  12. data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
  13. data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
  14. data/docs/assets/images/fluent-api-demo.svg +59 -0
  15. data/docs/assets/images/performance-comparison.svg +161 -0
  16. data/docs/assets/images/redis-basic-architecture.svg +53 -0
  17. data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
  18. data/docs/assets/images/redis-queue-architecture.svg +101 -0
  19. data/docs/assets/images/smart_message.jpg +0 -0
  20. data/docs/assets/images/smart_message_walking.jpg +0 -0
  21. data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
  22. data/docs/assets/images/transport-comparison-matrix.svg +171 -0
  23. data/docs/assets/javascripts/mathjax.js +17 -0
  24. data/docs/assets/stylesheets/extra.css +51 -0
  25. data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
  26. data/docs/{architecture.md → core-concepts/architecture.md} +80 -145
  27. data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
  28. data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
  29. data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
  30. data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
  31. data/docs/{examples.md → getting-started/examples.md} +103 -89
  32. data/docs/{getting-started.md → getting-started/quick-start.md} +47 -23
  33. data/docs/index.md +64 -0
  34. data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
  35. data/docs/{logging.md → reference/logging.md} +1 -1
  36. data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
  37. data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
  38. data/docs/reference/serializers.md +245 -0
  39. data/docs/{transports.md → reference/transports.md} +9 -11
  40. data/docs/transports/memory-transport.md +374 -0
  41. data/docs/transports/redis-transport-comparison.md +361 -0
  42. data/docs/transports/redis-transport.md +490 -0
  43. data/examples/README.md +104 -14
  44. data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
  45. data/examples/city_scenario/README.md +515 -0
  46. data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
  47. data/examples/city_scenario/citizen.rb +195 -0
  48. data/examples/city_scenario/city_diagram.svg +125 -0
  49. data/examples/city_scenario/common/health_monitor.rb +80 -0
  50. data/examples/city_scenario/common/logger.rb +30 -0
  51. data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
  52. data/examples/city_scenario/fire_department.rb +446 -0
  53. data/examples/city_scenario/fire_emergency_flow.svg +95 -0
  54. data/examples/city_scenario/health_department.rb +100 -0
  55. data/examples/city_scenario/health_monitoring_system.svg +130 -0
  56. data/examples/city_scenario/house.rb +244 -0
  57. data/examples/city_scenario/local_bank.rb +217 -0
  58. data/examples/city_scenario/messages/emergency_911_message.rb +80 -0
  59. data/examples/city_scenario/messages/emergency_resolved_message.rb +42 -0
  60. data/examples/city_scenario/messages/fire_dispatch_message.rb +42 -0
  61. data/examples/city_scenario/messages/fire_emergency_message.rb +44 -0
  62. data/examples/city_scenario/messages/health_check_message.rb +21 -0
  63. data/examples/city_scenario/messages/health_status_message.rb +34 -0
  64. data/examples/city_scenario/messages/police_dispatch_message.rb +45 -0
  65. data/examples/city_scenario/messages/silent_alarm_message.rb +37 -0
  66. data/examples/city_scenario/police_department.rb +316 -0
  67. data/examples/city_scenario/redis_monitor.rb +129 -0
  68. data/examples/city_scenario/redis_stats.rb +743 -0
  69. data/examples/city_scenario/room_for_improvement.md +240 -0
  70. data/examples/city_scenario/security_emergency_flow.svg +95 -0
  71. data/examples/city_scenario/service_internal_architecture.svg +154 -0
  72. data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
  73. data/examples/city_scenario/start_demo.sh +236 -0
  74. data/examples/city_scenario/stop_demo.sh +106 -0
  75. data/examples/city_scenario/visitor.rb +631 -0
  76. data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -3
  77. data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +10 -40
  78. data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -3
  79. data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +1 -2
  80. data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +1 -4
  81. data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
  82. data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +1 -2
  83. data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +13 -14
  84. data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +1 -4
  85. data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +2 -8
  86. data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +2 -6
  87. data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +1 -2
  88. data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +1 -6
  89. data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +17 -8
  90. data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -2
  91. data/examples/memory/README.md +163 -0
  92. data/examples/memory/memory_transport_architecture.svg +90 -0
  93. data/examples/memory/point_to_point_pattern.svg +94 -0
  94. data/examples/memory/publish_subscribe_pattern.svg +125 -0
  95. data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +1 -5
  96. data/examples/redis/README.md +228 -0
  97. data/examples/redis/alert_system_flow.svg +127 -0
  98. data/examples/redis/dashboard_status_flow.svg +107 -0
  99. data/examples/redis/device_command_flow.svg +113 -0
  100. data/examples/redis/redis_transport_architecture.svg +115 -0
  101. data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
  102. data/examples/redis/smart_home_system_architecture.svg +133 -0
  103. data/ideas/README.md +41 -0
  104. data/ideas/agents.md +1001 -0
  105. data/ideas/database_transport.md +980 -0
  106. data/ideas/improvement.md +359 -0
  107. data/ideas/meshage.md +1788 -0
  108. data/ideas/message_discovery.md +178 -0
  109. data/ideas/message_schema.md +1381 -0
  110. data/lib/smart_message/.idea/.gitignore +8 -0
  111. data/lib/smart_message/.idea/markdown.xml +6 -0
  112. data/lib/smart_message/.idea/misc.xml +4 -0
  113. data/lib/smart_message/.idea/modules.xml +8 -0
  114. data/lib/smart_message/.idea/smart_message.iml +16 -0
  115. data/lib/smart_message/.idea/vcs.xml +6 -0
  116. data/lib/smart_message/addressing.rb +15 -0
  117. data/lib/smart_message/base.rb +19 -12
  118. data/lib/smart_message/configuration.rb +2 -23
  119. data/lib/smart_message/dead_letter_queue.rb +1 -1
  120. data/lib/smart_message/logger.rb +15 -4
  121. data/lib/smart_message/messaging.rb +3 -62
  122. data/lib/smart_message/plugins.rb +6 -44
  123. data/lib/smart_message/serializer.rb +14 -0
  124. data/lib/smart_message/transport/base.rb +42 -8
  125. data/lib/smart_message/transport/memory_transport.rb +23 -4
  126. data/lib/smart_message/transport/redis_transport.rb +11 -0
  127. data/lib/smart_message/transport/stdout_transport.rb +28 -10
  128. data/lib/smart_message/transport.rb +33 -1
  129. data/lib/smart_message/version.rb +1 -1
  130. data/lib/smart_message.rb +5 -52
  131. data/mkdocs.yml +184 -0
  132. data/p2p_plan.md +326 -0
  133. data/p2p_roadmap.md +287 -0
  134. data/smart_message.gemspec +2 -0
  135. data/smart_message.svg +51 -0
  136. metadata +145 -45
  137. data/docs/README.md +0 -57
  138. data/docs/serializers.md +0 -575
  139. data/examples/dead_letters.jsonl +0 -12
  140. data/examples/temp.txt +0 -94
  141. data/examples/tmux_chat/README.md +0 -283
  142. data/examples/tmux_chat/bot_agent.rb +0 -278
  143. data/examples/tmux_chat/human_agent.rb +0 -199
  144. data/examples/tmux_chat/room_monitor.rb +0 -160
  145. data/examples/tmux_chat/shared_chat_system.rb +0 -328
  146. data/examples/tmux_chat/start_chat_demo.sh +0 -190
  147. data/examples/tmux_chat/stop_chat_demo.sh +0 -22
  148. /data/docs/{properties.md → core-concepts/properties.md} +0 -0
  149. /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -1,199 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # examples/tmux_chat/human_agent.rb
3
- #
4
- # Human chat agent for tmux visualization
5
-
6
- require_relative 'shared_chat_system'
7
-
8
- begin
9
- require 'io/console'
10
- rescue LoadError
11
- # Console methods may not be available, handled gracefully
12
- end
13
-
14
- class HumanChatAgent < BaseAgent
15
- def initialize(user_id:, name:)
16
- @message_counter = 0
17
- super(agent_id: user_id, name: name, agent_type: 'human')
18
-
19
- log_display("👤 Human agent #{@name} ready!")
20
- log_display("Commands: /join <room>, /leave <room>, /list, /quit")
21
- log_display("Type messages to send to current rooms")
22
- log_display("")
23
- end
24
-
25
- def setup_subscriptions
26
- # Subscribe to chat messages
27
- ChatMessage.subscribe("HumanChatAgent.handle_chat_message_#{@agent_id}")
28
- SystemNotificationMessage.subscribe("HumanChatAgent.handle_system_notification_#{@agent_id}")
29
-
30
- # Register this instance for method dispatch
31
- @@agents ||= {}
32
- @@agents[@agent_id] = self
33
- end
34
-
35
- def start_interactive_session
36
- while true
37
- begin
38
- print "> "
39
- input = STDIN.gets&.chomp
40
- break if input.nil?
41
-
42
- next if input.strip.empty?
43
-
44
- if input.start_with?('/')
45
- handle_command(input)
46
- else
47
- send_to_active_rooms(input)
48
- end
49
- rescue Interrupt
50
- break
51
- end
52
- end
53
-
54
- shutdown
55
- end
56
-
57
- # Class method routing for SmartMessage
58
- def self.method_missing(method_name, *args)
59
- if method_name.to_s.start_with?('handle_chat_message_')
60
- user_id = method_name.to_s.split('_').last
61
- agent = (@@agents ||= {})[user_id]
62
- agent&.handle_chat_message(*args)
63
- elsif method_name.to_s.start_with?('handle_system_notification_')
64
- user_id = method_name.to_s.split('_').last
65
- agent = (@@agents ||= {})[user_id]
66
- agent&.handle_system_notification(*args)
67
- else
68
- super
69
- end
70
- end
71
-
72
- def handle_chat_message(wrapper)
73
- message_header, message_payload = wrapper.split
74
- chat_data = JSON.parse(message_payload)
75
-
76
- # Only process messages from rooms we're in and not our own messages
77
- return unless @active_rooms.include?(chat_data['room_id'])
78
- return if chat_data['sender_id'] == @agent_id
79
-
80
- sender_emoji = case chat_data['message_type']
81
- when 'bot' then '🤖'
82
- when 'system' then '🔔'
83
- else '👤'
84
- end
85
-
86
- log_display("#{sender_emoji} [#{chat_data['room_id']}] #{chat_data['sender_name']}: #{chat_data['content']}")
87
-
88
- # Auto-respond if mentioned
89
- if chat_data['mentions']&.include?(@name.downcase) || chat_data['content'].include?("@#{@name}")
90
- respond_to_mention(chat_data)
91
- end
92
- end
93
-
94
- def handle_system_notification(wrapper)
95
- message_header, message_payload = wrapper.split
96
- notif_data = JSON.parse(message_payload)
97
-
98
- # Only process notifications from rooms we're in
99
- return unless @active_rooms.include?(notif_data['room_id'])
100
-
101
- log_display("🔔 [#{notif_data['room_id']}] #{notif_data['content']}")
102
- end
103
-
104
- private
105
-
106
- def handle_command(input)
107
- parts = input[1..-1].split(' ')
108
- command = parts[0]
109
- args = parts[1..-1]
110
-
111
- case command
112
- when 'join'
113
- if args.empty?
114
- log_display("❌ Usage: /join <room_id>")
115
- else
116
- join_room(args[0])
117
- update_display_header
118
- end
119
- when 'leave'
120
- if args.empty?
121
- log_display("❌ Usage: /leave <room_id>")
122
- else
123
- leave_room(args[0])
124
- update_display_header
125
- end
126
- when 'list'
127
- log_display("📋 Active rooms: #{@active_rooms.empty? ? 'none' : @active_rooms.join(', ')}")
128
- when 'quit', 'exit'
129
- log_display("👋 Goodbye!")
130
- exit(0)
131
- when 'help'
132
- log_display("📖 Commands:")
133
- log_display(" /join <room> - Join a chat room")
134
- log_display(" /leave <room> - Leave a chat room")
135
- log_display(" /list - List active rooms")
136
- log_display(" /quit - Exit the chat")
137
- else
138
- log_display("❌ Unknown command: /#{command}. Type /help for help.")
139
- end
140
- end
141
-
142
- def send_to_active_rooms(message)
143
- if @active_rooms.empty?
144
- log_display("❌ You're not in any rooms. Use /join <room> to join a room.")
145
- return
146
- end
147
-
148
- @active_rooms.each do |room_id|
149
- send_message(room_id: room_id, content: message)
150
- end
151
- end
152
-
153
- def respond_to_mention(chat_data)
154
- responses = [
155
- "Thanks for mentioning me!",
156
- "I'm here, what's up?",
157
- "How can I help?",
158
- "Yes, I saw that!",
159
- "Interesting point!"
160
- ]
161
-
162
- # Delay response slightly to make it feel natural
163
- Thread.new do
164
- sleep(0.5 + rand)
165
- send_message(
166
- room_id: chat_data['room_id'],
167
- content: responses.sample
168
- )
169
- end
170
- end
171
-
172
- def update_display_header
173
- # Move cursor to update rooms line
174
- print "\033[4;9H" # Move to line 4, column 9
175
- print "#{@active_rooms.join(', ').ljust(42)}"
176
- # Move cursor to bottom (default to 40 lines if console unavailable)
177
- max_lines = begin
178
- IO.console&.winsize&.first || 40
179
- rescue
180
- 40
181
- end
182
- print "\033[#{max_lines};1H"
183
- end
184
- end
185
-
186
- # Main execution
187
- if __FILE__ == $0
188
- if ARGV.length < 2
189
- puts "Usage: #{$0} <user_id> <name>"
190
- puts "Example: #{$0} alice Alice"
191
- exit 1
192
- end
193
-
194
- user_id = ARGV[0]
195
- name = ARGV[1]
196
-
197
- agent = HumanChatAgent.new(user_id: user_id, name: name)
198
- agent.start_interactive_session
199
- end
@@ -1,160 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # examples/tmux_chat/room_monitor.rb
3
- #
4
- # Room activity monitor for tmux chat visualization
5
-
6
- require_relative 'shared_chat_system'
7
-
8
- begin
9
- require 'io/console'
10
- rescue LoadError
11
- # Console methods may not be available, handled gracefully
12
- end
13
-
14
- class RoomMonitor < BaseAgent
15
- def initialize(room_id)
16
- @room_id = room_id
17
- @message_count = 0
18
- @user_count = 0
19
- @last_activity = Time.now
20
- @participants = Set.new
21
-
22
- super(agent_id: "monitor-#{room_id}", name: "Room Monitor", agent_type: 'monitor')
23
-
24
- join_room(@room_id)
25
- log_display("🏠 Monitoring room: #{@room_id}")
26
- log_display("📊 Waiting for activity...")
27
- log_display("")
28
- end
29
-
30
- def setup_subscriptions
31
- ChatMessage.subscribe("RoomMonitor.handle_chat_message_#{@agent_id}")
32
- SystemNotificationMessage.subscribe("RoomMonitor.handle_system_notification_#{@agent_id}")
33
-
34
- @@monitors ||= {}
35
- @@monitors[@agent_id] = self
36
- end
37
-
38
- def run
39
- # Update display every few seconds
40
- begin
41
- while true
42
- sleep(2)
43
- update_stats_display
44
- end
45
- rescue Interrupt
46
- shutdown
47
- end
48
- end
49
-
50
- # Class method routing
51
- def self.method_missing(method_name, *args)
52
- if method_name.to_s.start_with?('handle_chat_message_')
53
- monitor_id = method_name.to_s.split('_', 4).last
54
- monitor = (@@monitors ||= {})[monitor_id]
55
- monitor&.handle_chat_message(*args)
56
- elsif method_name.to_s.start_with?('handle_system_notification_')
57
- monitor_id = method_name.to_s.split('_', 4).last
58
- monitor = (@@monitors ||= {})[monitor_id]
59
- monitor&.handle_system_notification(*args)
60
- else
61
- super
62
- end
63
- end
64
-
65
- def handle_chat_message(wrapper)
66
- message_header, message_payload = wrapper.split
67
- chat_data = JSON.parse(message_payload)
68
-
69
- return unless chat_data['room_id'] == @room_id
70
- return if chat_data['sender_id'] == @agent_id
71
-
72
- @message_count += 1
73
- @last_activity = Time.now
74
- @participants.add(chat_data['sender_name'])
75
-
76
- sender_emoji = case chat_data['message_type']
77
- when 'bot' then '🤖'
78
- when 'system' then '🔔'
79
- else '👤'
80
- end
81
-
82
- # Show the message with metadata
83
- timestamp = Time.now.strftime("%H:%M:%S")
84
- log_display("#{sender_emoji} [#{timestamp}] #{chat_data['sender_name']}: #{chat_data['content']}")
85
-
86
- # Check for commands
87
- if chat_data['content'].start_with?('/')
88
- log_display("⚡ Command detected: #{chat_data['content']}")
89
- end
90
-
91
- # Check for mentions
92
- if chat_data['mentions'] && !chat_data['mentions'].empty?
93
- log_display("🏷️ Mentions: #{chat_data['mentions'].join(', ')}")
94
- end
95
- end
96
-
97
- def handle_system_notification(wrapper)
98
- message_header, message_payload = wrapper.split
99
- notif_data = JSON.parse(message_payload)
100
-
101
- return unless notif_data['room_id'] == @room_id
102
-
103
- case notif_data['notification_type']
104
- when 'user_joined'
105
- @user_count += 1
106
- log_display("📥 #{notif_data['content']}")
107
- when 'user_left'
108
- @user_count = [@user_count - 1, 0].max
109
- log_display("📤 #{notif_data['content']}")
110
- else
111
- log_display("🔔 #{notif_data['content']}")
112
- end
113
-
114
- @last_activity = Time.now
115
- end
116
-
117
- private
118
-
119
- def update_stats_display
120
- # Move cursor to update stats
121
- time_since_activity = Time.now - @last_activity
122
- activity_status = if time_since_activity < 10
123
- "🟢 Active"
124
- elsif time_since_activity < 60
125
- "🟡 Quiet (#{time_since_activity.to_i}s ago)"
126
- else
127
- "🔴 Inactive (#{(time_since_activity / 60).to_i}m ago)"
128
- end
129
-
130
- # Update the header area with stats
131
- print "\033[2;2H" # Move to line 2
132
- room_info = " ROOM: #{@room_id} | #{activity_status} "
133
- print room_info.center(50)
134
-
135
- print "\033[4;2H" # Move to line 4
136
- stats_info = " Messages: #{@message_count} | Participants: #{@participants.size} "
137
- print stats_info.center(50)
138
-
139
- # Move cursor back to bottom (default to 40 lines if console unavailable)
140
- max_lines = begin
141
- IO.console&.winsize&.first || 40
142
- rescue
143
- 40
144
- end
145
- print "\033[#{max_lines};1H"
146
- end
147
- end
148
-
149
- # Main execution
150
- if __FILE__ == $0
151
- if ARGV.empty?
152
- puts "Usage: #{$0} <room_id>"
153
- puts "Example: #{$0} general"
154
- exit 1
155
- end
156
-
157
- room_id = ARGV[0]
158
- monitor = RoomMonitor.new(room_id)
159
- monitor.run
160
- end
@@ -1,328 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # examples/tmux_chat/shared_chat_system.rb
3
- #
4
- # Shared messaging system for the tmux chat visualization
5
- # This provides the core message types and file-based transport for inter-pane communication
6
-
7
- require_relative '../../lib/smart_message'
8
- require 'fileutils'
9
-
10
- # Create shared message queues directory
11
- SHARED_DIR = '/tmp/smart_message_chat'
12
- FileUtils.mkdir_p(SHARED_DIR)
13
-
14
- # File-based transport for tmux communication
15
- class FileTransport < SmartMessage::Transport::Base
16
- def default_options
17
- {
18
- queue_dir: SHARED_DIR,
19
- poll_interval: 0.1
20
- }
21
- end
22
-
23
- def configure
24
- @queue_dir = @options[:queue_dir]
25
- @poll_interval = @options[:poll_interval]
26
- @running = false
27
- @subscriber_thread = nil
28
-
29
- FileUtils.mkdir_p(@queue_dir)
30
- end
31
-
32
- def publish(message_header, message_payload)
33
- message_data = {
34
- header: message_header.to_h,
35
- payload: message_payload,
36
- timestamp: Time.now.to_f
37
- }
38
-
39
- # Write to room-specific queue file
40
- room_id = extract_room_id(message_payload)
41
- queue_file = File.join(@queue_dir, "#{room_id}.queue")
42
-
43
- File.open(queue_file, 'a') do |f|
44
- f.puts(JSON.generate(message_data))
45
- f.flush
46
- end
47
- end
48
-
49
- def start_subscriber
50
- return if @running
51
-
52
- @running = true
53
- @subscriber_thread = Thread.new do
54
- processed_positions = {}
55
-
56
- while @running
57
- Dir.glob(File.join(@queue_dir, "*.queue")).each do |queue_file|
58
- room_id = File.basename(queue_file, '.queue')
59
-
60
- if File.exist?(queue_file)
61
- lines = File.readlines(queue_file)
62
- start_pos = processed_positions[room_id] || 0
63
-
64
- lines[start_pos..-1].each do |line|
65
- begin
66
- message_data = JSON.parse(line.strip)
67
- header = SmartMessage::Header.new(message_data['header'])
68
- receive(header, message_data['payload'])
69
- rescue JSON::ParserError
70
- # Skip malformed lines
71
- end
72
- end
73
-
74
- processed_positions[room_id] = lines.length
75
- end
76
- end
77
-
78
- sleep(@poll_interval)
79
- end
80
- end
81
- end
82
-
83
- def stop_subscriber
84
- @running = false
85
- @subscriber_thread&.join
86
- end
87
-
88
- private
89
-
90
- def extract_room_id(payload)
91
- begin
92
- data = JSON.parse(payload)
93
- data['room_id'] || 'global'
94
- rescue
95
- 'global'
96
- end
97
- end
98
- end
99
-
100
- # Register the file transport
101
- SmartMessage::Transport.register(:file, FileTransport)
102
-
103
- # Define the Chat Message
104
- class ChatMessage < SmartMessage::Base
105
- description "Chat messages for tmux-based multi-pane chat demonstration"
106
-
107
- property :message_id,
108
- description: "Unique identifier for this chat message"
109
- property :room_id,
110
- description: "Chat room identifier for message routing"
111
- property :sender_id,
112
- description: "Unique ID of the user or bot sending the message"
113
- property :sender_name,
114
- description: "Display name of the message sender"
115
- property :content,
116
- description: "The actual text content of the chat message"
117
- property :message_type,
118
- description: "Message type: 'user', 'bot', or 'system'"
119
- property :timestamp,
120
- description: "ISO8601 timestamp when message was sent"
121
- property :mentions,
122
- description: "Array of user IDs mentioned in the message"
123
- property :metadata,
124
- description: "Additional message metadata for tmux display"
125
-
126
- config do
127
- transport SmartMessage::Transport.create(:file)
128
- serializer SmartMessage::Serializer::JSON.new
129
- end
130
-
131
- def self.process(wrapper)
132
- message_header, message_payload = wrapper.split
133
- # Default processing - agents will override this
134
- end
135
- end
136
-
137
- # Define Bot Command Message
138
- class BotCommandMessage < SmartMessage::Base
139
- description "Commands sent to chat bots in the tmux chat system"
140
-
141
- property :command_id,
142
- description: "Unique identifier for this bot command"
143
- property :room_id,
144
- description: "Chat room where the command was issued"
145
- property :user_id,
146
- description: "User who issued the bot command"
147
- property :user_name,
148
- description: "Display name of the user issuing the command"
149
- property :command,
150
- description: "Bot command name (help, joke, weather, etc.)"
151
- property :parameters,
152
- description: "Array of parameters for the bot command"
153
- property :timestamp,
154
- description: "ISO8601 timestamp when command was issued"
155
-
156
- config do
157
- transport SmartMessage::Transport.create(:file)
158
- serializer SmartMessage::Serializer::JSON.new
159
- end
160
-
161
- def self.process(wrapper)
162
- message_header, message_payload = wrapper.split
163
- # Default processing - bots will override this
164
- end
165
- end
166
-
167
- # Define System Notification Message
168
- class SystemNotificationMessage < SmartMessage::Base
169
- description "System notifications for tmux chat room events and status updates"
170
-
171
- property :notification_id,
172
- description: "Unique identifier for this system notification"
173
- property :room_id,
174
- description: "Chat room affected by this notification"
175
- property :notification_type,
176
- description: "Type of notification (user_joined, user_left, etc.)"
177
- property :content,
178
- description: "Human-readable description of the system event"
179
- property :timestamp,
180
- description: "ISO8601 timestamp when the event occurred"
181
- property :metadata,
182
- description: "Additional system event metadata for tmux display"
183
-
184
- config do
185
- transport SmartMessage::Transport.create(:file)
186
- serializer SmartMessage::Serializer::JSON.new
187
- end
188
-
189
- def self.process(wrapper)
190
- message_header, message_payload = wrapper.split
191
- # Default processing
192
- end
193
- end
194
-
195
- # Base Agent class with display utilities
196
- class BaseAgent
197
- attr_reader :agent_id, :name, :active_rooms
198
-
199
- def initialize(agent_id:, name:, agent_type: 'agent')
200
- @agent_id = agent_id
201
- @name = name
202
- @agent_type = agent_type
203
- @active_rooms = []
204
- @display_buffer = []
205
- @transport = SmartMessage::Transport.create(:file)
206
-
207
- setup_display
208
- setup_subscriptions
209
- start_message_processing
210
- end
211
-
212
- def join_room(room_id)
213
- return if @active_rooms.include?(room_id)
214
-
215
- @active_rooms << room_id
216
- log_display("📥 Joined room: #{room_id}")
217
-
218
- send_system_notification(
219
- room_id: room_id,
220
- notification_type: 'user_joined',
221
- content: "#{@name} joined the room"
222
- )
223
- end
224
-
225
- def leave_room(room_id)
226
- return unless @active_rooms.include?(room_id)
227
-
228
- @active_rooms.delete(room_id)
229
- log_display("📤 Left room: #{room_id}")
230
-
231
- send_system_notification(
232
- room_id: room_id,
233
- notification_type: 'user_left',
234
- content: "#{@name} left the room"
235
- )
236
- end
237
-
238
- def send_message(room_id:, content:, message_type: 'user')
239
- return unless @active_rooms.include?(room_id)
240
-
241
- message = ChatMessage.new(
242
- message_id: generate_message_id,
243
- room_id: room_id,
244
- sender_id: @agent_id,
245
- sender_name: @name,
246
- content: content,
247
- message_type: message_type,
248
- timestamp: Time.now.iso8601,
249
- mentions: extract_mentions(content),
250
- metadata: { agent_type: @agent_type },
251
- from: @agent_id
252
- )
253
-
254
- log_display("💬 [#{room_id}] #{@name}: #{content}")
255
- message.publish
256
- end
257
-
258
- def shutdown
259
- @active_rooms.dup.each { |room_id| leave_room(room_id) }
260
- @transport.stop_subscriber
261
- log_display("🔴 #{@name} shutdown")
262
- end
263
-
264
- protected
265
-
266
- def setup_display
267
- puts "\033[2J\033[H" # Clear screen
268
- puts "┌─" + "─" * 50 + "┐"
269
- puts "│ #{@agent_type.upcase}: #{@name.center(44)} │"
270
- puts "├─" + "─" * 50 + "┤"
271
- puts "│ Rooms: #{@active_rooms.join(', ').ljust(42)} │"
272
- puts "├─" + "─" * 50 + "┤"
273
- puts "│ Messages:".ljust(51) + " │"
274
- puts "└─" + "─" * 50 + "┘"
275
- puts
276
- end
277
-
278
- def log_display(message)
279
- timestamp = Time.now.strftime("%H:%M:%S")
280
- puts "[#{timestamp}] #{message}"
281
- end
282
-
283
- def setup_subscriptions
284
- # Override in subclasses
285
- end
286
-
287
- def start_message_processing
288
- @transport.start_subscriber
289
- end
290
-
291
- def send_system_notification(room_id:, notification_type:, content:)
292
- notification = SystemNotificationMessage.new(
293
- notification_id: "NOTIF-#{Time.now.to_i}-#{rand(1000)}",
294
- room_id: room_id,
295
- notification_type: notification_type,
296
- content: content,
297
- timestamp: Time.now.iso8601,
298
- metadata: { triggered_by: @agent_id },
299
- from: @agent_id
300
- )
301
-
302
- notification.publish
303
- end
304
-
305
- def generate_message_id
306
- "MSG-#{@agent_id}-#{Time.now.to_i}-#{rand(1000)}"
307
- end
308
-
309
- def extract_mentions(content)
310
- content.scan(/@(\w+)/).flatten
311
- end
312
- end
313
-
314
- # Cleanup utility
315
- def cleanup_shared_queues
316
- FileUtils.rm_rf(SHARED_DIR) if Dir.exist?(SHARED_DIR)
317
- end
318
-
319
- # Signal handling for cleanup
320
- trap('INT') do
321
- cleanup_shared_queues
322
- exit
323
- end
324
-
325
- trap('TERM') do
326
- cleanup_shared_queues
327
- exit
328
- end