robot_lab 0.2.8 → 0.3.1

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 (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +16 -18
  4. data/lib/robot_lab/ask_user.rb +3 -3
  5. data/lib/robot_lab/config/defaults.yml +5 -5
  6. data/lib/robot_lab/config.rb +2 -2
  7. data/lib/robot_lab/mcp/transports/streamable_http.rb +2 -4
  8. data/lib/robot_lab/memory.rb +16 -7
  9. data/lib/robot_lab/robot/hooking.rb +26 -0
  10. data/lib/robot_lab/robot/result_building.rb +119 -0
  11. data/lib/robot_lab/robot.rb +37 -120
  12. data/lib/robot_lab/run_config.rb +52 -20
  13. data/lib/robot_lab/tool.rb +7 -12
  14. data/lib/robot_lab/version.rb +1 -1
  15. data/lib/robot_lab.rb +3 -3
  16. metadata +15 -274
  17. data/.architecture/AGENTS.md +0 -32
  18. data/.architecture/config.yml +0 -8
  19. data/.architecture/members.yml +0 -60
  20. data/.architecture/reviews/feature-free-will.md +0 -490
  21. data/.architecture/reviews/overall-codebase.md +0 -427
  22. data/.claude/settings.local.json +0 -57
  23. data/.codex/config.toml +0 -2
  24. data/.envrc +0 -6
  25. data/.github/workflows/deploy-github-pages.yml +0 -52
  26. data/.irbrc +0 -6
  27. data/.loki +0 -65
  28. data/.quality/reek_baseline.txt +0 -43
  29. data/.rubocop.yml +0 -10
  30. data/Archspec.rb +0 -44
  31. data/CLAUDE.md +0 -139
  32. data/COMMITS.md +0 -196
  33. data/Rakefile +0 -193
  34. data/_typos.toml +0 -21
  35. data/agent2agent_review.md +0 -192
  36. data/agentf_improvements.md +0 -253
  37. data/agents.md +0 -14
  38. data/docs/api/core/index.md +0 -147
  39. data/docs/api/core/memory.md +0 -578
  40. data/docs/api/core/network.md +0 -553
  41. data/docs/api/core/result.md +0 -211
  42. data/docs/api/core/robot.md +0 -1667
  43. data/docs/api/core/state.md +0 -145
  44. data/docs/api/core/tool.md +0 -520
  45. data/docs/api/errors.md +0 -323
  46. data/docs/api/hooks.md +0 -469
  47. data/docs/api/index.md +0 -137
  48. data/docs/api/mcp/client.md +0 -387
  49. data/docs/api/mcp/index.md +0 -267
  50. data/docs/api/mcp/server.md +0 -252
  51. data/docs/api/mcp/transports.md +0 -339
  52. data/docs/api/messages/index.md +0 -126
  53. data/docs/api/messages/text-message.md +0 -170
  54. data/docs/api/messages/tool-call-message.md +0 -206
  55. data/docs/api/messages/tool-result-message.md +0 -242
  56. data/docs/api/messages/user-message.md +0 -232
  57. data/docs/api/skills.md +0 -423
  58. data/docs/api/streaming/context.md +0 -280
  59. data/docs/api/streaming/events.md +0 -185
  60. data/docs/api/streaming/index.md +0 -156
  61. data/docs/api/support.md +0 -846
  62. data/docs/architecture/core-concepts.md +0 -575
  63. data/docs/architecture/index.md +0 -252
  64. data/docs/architecture/message-flow.md +0 -366
  65. data/docs/architecture/network-orchestration.md +0 -478
  66. data/docs/architecture/robot-execution.md +0 -396
  67. data/docs/architecture/state-management.md +0 -410
  68. data/docs/assets/css/custom.css +0 -56
  69. data/docs/assets/images/robot_lab.jpg +0 -0
  70. data/docs/concepts.md +0 -641
  71. data/docs/examples/basic-chat.md +0 -258
  72. data/docs/examples/index.md +0 -425
  73. data/docs/examples/mcp-server.md +0 -403
  74. data/docs/examples/multi-robot-network.md +0 -476
  75. data/docs/examples/tool-usage.md +0 -468
  76. data/docs/getting-started/configuration.md +0 -645
  77. data/docs/getting-started/index.md +0 -56
  78. data/docs/getting-started/installation.md +0 -200
  79. data/docs/getting-started/quick-start.md +0 -293
  80. data/docs/guides/building-robots.md +0 -1187
  81. data/docs/guides/creating-networks.md +0 -736
  82. data/docs/guides/hooks.md +0 -1206
  83. data/docs/guides/index.md +0 -78
  84. data/docs/guides/knowledge.md +0 -219
  85. data/docs/guides/mcp-integration.md +0 -740
  86. data/docs/guides/memory.md +0 -458
  87. data/docs/guides/observability.md +0 -733
  88. data/docs/guides/streaming.md +0 -323
  89. data/docs/guides/using-tools.md +0 -780
  90. data/docs/index.md +0 -280
  91. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  92. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  93. data/examples/.envrc +0 -1
  94. data/examples/01_simple_robot.rb +0 -31
  95. data/examples/02_tools.rb +0 -109
  96. data/examples/03_network.rb +0 -118
  97. data/examples/04_mcp.rb +0 -215
  98. data/examples/05_streaming.rb +0 -137
  99. data/examples/06_prompt_templates.rb +0 -325
  100. data/examples/07_network_memory.rb +0 -313
  101. data/examples/08_llm_config.rb +0 -162
  102. data/examples/09_chaining.rb +0 -251
  103. data/examples/10_memory.rb +0 -315
  104. data/examples/11_network_introspection.rb +0 -239
  105. data/examples/12_message_bus.rb +0 -67
  106. data/examples/13_spawn.rb +0 -85
  107. data/examples/14_rusty_circuit/.envrc +0 -1
  108. data/examples/14_rusty_circuit/.gitignore +0 -1
  109. data/examples/14_rusty_circuit/README.md +0 -1
  110. data/examples/14_rusty_circuit/comic.rb +0 -149
  111. data/examples/14_rusty_circuit/display.rb +0 -217
  112. data/examples/14_rusty_circuit/heckler.rb +0 -65
  113. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  114. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  115. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  116. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  117. data/examples/14_rusty_circuit/scout.rb +0 -165
  118. data/examples/14_rusty_circuit/show.log +0 -234
  119. data/examples/15_memory_network_and_bus/.envrc +0 -1
  120. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  121. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  122. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  123. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  124. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  125. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -13
  126. data/examples/15_memory_network_and_bus/output/final_article.md +0 -13
  127. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  128. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  129. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  130. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -18
  131. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -13
  132. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  133. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  134. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  135. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  136. data/examples/16_writers_room/.envrc +0 -1
  137. data/examples/16_writers_room/display.rb +0 -158
  138. data/examples/16_writers_room/output/.gitignore +0 -4
  139. data/examples/16_writers_room/output/README.md +0 -69
  140. data/examples/16_writers_room/output/opus_001.md +0 -263
  141. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  142. data/examples/16_writers_room/output/opus_002.md +0 -245
  143. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  144. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  145. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  146. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  147. data/examples/16_writers_room/prompts/writer.md +0 -37
  148. data/examples/16_writers_room/room.rb +0 -186
  149. data/examples/16_writers_room/tools.rb +0 -173
  150. data/examples/16_writers_room/writer.rb +0 -121
  151. data/examples/16_writers_room/writers_room.rb +0 -256
  152. data/examples/17_skills.rb +0 -234
  153. data/examples/18_rails/.envrc +0 -3
  154. data/examples/18_rails/.gitignore +0 -5
  155. data/examples/18_rails/Gemfile +0 -11
  156. data/examples/18_rails/README.md +0 -67
  157. data/examples/18_rails/Rakefile +0 -4
  158. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  159. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  160. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  161. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  162. data/examples/18_rails/app/models/application_record.rb +0 -5
  163. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  164. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  165. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  166. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  167. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  168. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  169. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  170. data/examples/18_rails/bin/dev +0 -7
  171. data/examples/18_rails/bin/rails +0 -6
  172. data/examples/18_rails/bin/setup +0 -15
  173. data/examples/18_rails/config/application.rb +0 -33
  174. data/examples/18_rails/config/cable.yml +0 -2
  175. data/examples/18_rails/config/database.yml +0 -5
  176. data/examples/18_rails/config/environment.rb +0 -4
  177. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  178. data/examples/18_rails/config/routes.rb +0 -6
  179. data/examples/18_rails/config.ru +0 -4
  180. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  181. data/examples/19_token_tracking.rb +0 -138
  182. data/examples/20_circuit_breaker.rb +0 -147
  183. data/examples/21_learning_loop.rb +0 -181
  184. data/examples/22_context_compression.rb +0 -172
  185. data/examples/23_convergence.rb +0 -133
  186. data/examples/24_structured_delegation.rb +0 -151
  187. data/examples/25_history_search/conversation.jsonl +0 -30
  188. data/examples/25_history_search.rb +0 -133
  189. data/examples/26_document_store/api_versioning_adr.md +0 -52
  190. data/examples/26_document_store/incident_postmortem.md +0 -46
  191. data/examples/26_document_store/postgres_runbook.md +0 -49
  192. data/examples/26_document_store/redis_caching_guide.md +0 -48
  193. data/examples/26_document_store/sidekiq_guide.md +0 -51
  194. data/examples/26_document_store.rb +0 -140
  195. data/examples/27_incident_response/incident_response.rb +0 -261
  196. data/examples/28_mcp_discovery.rb +0 -113
  197. data/examples/29_ractor_tools.rb +0 -240
  198. data/examples/30_ractor_network.rb +0 -252
  199. data/examples/31_launch_assessment.rb +0 -246
  200. data/examples/32_newsletter_reader.rb +0 -193
  201. data/examples/33_stock_generator.rb +0 -80
  202. data/examples/33_stock_predictor.rb +0 -327
  203. data/examples/34_agentskills.rb +0 -76
  204. data/examples/35_hooks.rb +0 -266
  205. data/examples/README.md +0 -533
  206. data/examples/common.rb +0 -148
  207. data/examples/prompts/assistant.md +0 -5
  208. data/examples/prompts/audit_trail.md +0 -8
  209. data/examples/prompts/billing.md +0 -10
  210. data/examples/prompts/classifier.md +0 -7
  211. data/examples/prompts/comedian.md +0 -6
  212. data/examples/prompts/comedy_critic.md +0 -10
  213. data/examples/prompts/configurable.md +0 -9
  214. data/examples/prompts/dispatcher.md +0 -12
  215. data/examples/prompts/entity_extractor.md +0 -14
  216. data/examples/prompts/escalation.md +0 -42
  217. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  218. data/examples/prompts/frontmatter_named_test.md +0 -5
  219. data/examples/prompts/frontmatter_tools_test.md +0 -6
  220. data/examples/prompts/general.md +0 -7
  221. data/examples/prompts/github_assistant.md +0 -14
  222. data/examples/prompts/helper.md +0 -4
  223. data/examples/prompts/incident_responder.md +0 -18
  224. data/examples/prompts/keyword_extractor.md +0 -11
  225. data/examples/prompts/llm_config_demo.md +0 -20
  226. data/examples/prompts/order_support.md +0 -35
  227. data/examples/prompts/os_advocate.md +0 -13
  228. data/examples/prompts/os_chief.md +0 -13
  229. data/examples/prompts/os_editor.md +0 -13
  230. data/examples/prompts/parameterized_main_test.md +0 -6
  231. data/examples/prompts/pii_redactor.md +0 -7
  232. data/examples/prompts/product_support.md +0 -37
  233. data/examples/prompts/runbook_protocol.md +0 -12
  234. data/examples/prompts/sentiment_analyzer.md +0 -12
  235. data/examples/prompts/skill_a_test.md +0 -4
  236. data/examples/prompts/skill_b_test.md +0 -4
  237. data/examples/prompts/skill_config_test.md +0 -5
  238. data/examples/prompts/skill_cycle_a_test.md +0 -6
  239. data/examples/prompts/skill_cycle_b_test.md +0 -6
  240. data/examples/prompts/skill_description_test.md +0 -4
  241. data/examples/prompts/skill_leaf_test.md +0 -4
  242. data/examples/prompts/skill_nested_test.md +0 -6
  243. data/examples/prompts/skill_refs_main_test.md +0 -6
  244. data/examples/prompts/skill_self_ref_test.md +0 -6
  245. data/examples/prompts/skill_with_mcp_test.md +0 -9
  246. data/examples/prompts/skill_with_params_test.md +0 -6
  247. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  248. data/examples/prompts/skill_with_tools_test.md +0 -6
  249. data/examples/prompts/sre_compliance.md +0 -10
  250. data/examples/prompts/structured_output.md +0 -7
  251. data/examples/prompts/synthesizer.md +0 -17
  252. data/examples/prompts/technical.md +0 -10
  253. data/examples/prompts/template_with_skills_test.md +0 -6
  254. data/examples/prompts/triage.md +0 -22
  255. data/examples/ruboruby.md +0 -423
  256. data/examples/xyzzy.rb +0 -97
  257. data/logfile +0 -8
  258. data/mkdocs.yml +0 -209
  259. data/robot_concurrency.md +0 -38
  260. data/simple_acp_review.md +0 -298
  261. data/temp.md +0 -6
  262. data/tool_manifest_plan.md +0 -155
@@ -1,242 +0,0 @@
1
- # ToolResultMessage
2
-
3
- Result from tool execution.
4
-
5
- ## Class: `RobotLab::ToolResultMessage`
6
-
7
- ```ruby
8
- tool = ToolMessage.new(id: "call_abc123", name: "get_weather", input: { city: "NYC" })
9
-
10
- message = ToolResultMessage.new(
11
- tool: tool,
12
- content: { data: { temperature: 72, conditions: "sunny" } }
13
- )
14
- ```
15
-
16
- ## Constructor
17
-
18
- ```ruby
19
- ToolResultMessage.new(tool:, content:, stop_reason: nil)
20
- ```
21
-
22
- **Parameters:**
23
-
24
- | Name | Type | Description |
25
- |------|------|-------------|
26
- | `tool` | `ToolMessage`, `Hash` | The tool call that was executed; a Hash is normalized via `ToolMessage.from_hash` |
27
- | `content` | `Hash` | Result with `:data` key (success) or `:error` key (failure) |
28
- | `stop_reason` | `String`, `nil` | Stop reason (defaults to "tool") |
29
-
30
- `tool:` accepts a plain Hash as well as a `ToolMessage`; anything else raises
31
- `ArgumentError: Invalid tool: must be ToolMessage or Hash`. As with
32
- `ToolCallMessage`, `from_hash` symbolizes keys and accepts `input:` or
33
- `arguments:` for the parameter hash.
34
-
35
- ```ruby
36
- ToolResultMessage.new(
37
- tool: { id: "call_1", name: "get_weather", arguments: { city: "NYC" } },
38
- content: { data: { temperature: 72 } }
39
- ).tool.input
40
- # => { city: "NYC" }
41
- ```
42
-
43
- `role` is hard-coded to `"tool_result"` — the value you pass for `tool:` has no
44
- bearing on it.
45
-
46
- ## Attributes
47
-
48
- ### tool
49
-
50
- ```ruby
51
- message.tool # => ToolMessage
52
- ```
53
-
54
- The `ToolMessage` representing the tool call that produced this result. Provides access to `tool.id`, `tool.name`, and `tool.input`.
55
-
56
- ### content
57
-
58
- ```ruby
59
- message.content # => Hash
60
- ```
61
-
62
- The result content. Contains either a `:data` key (success) or an `:error` key (failure).
63
-
64
- ### role
65
-
66
- ```ruby
67
- message.role # => "tool_result"
68
- ```
69
-
70
- Always returns `"tool_result"`.
71
-
72
- ### type
73
-
74
- ```ruby
75
- message.type # => "tool_result"
76
- ```
77
-
78
- Always returns `"tool_result"`.
79
-
80
- ### stop_reason
81
-
82
- ```ruby
83
- message.stop_reason # => "tool"
84
- ```
85
-
86
- Defaults to `"tool"`.
87
-
88
- ## Methods
89
-
90
- ### success?
91
-
92
- ```ruby
93
- message.success? # => Boolean
94
- ```
95
-
96
- Returns `true` if `content` is a Hash containing the **symbol** key `:data`.
97
- `content` is stored verbatim — it is not symbolized — so a result built with
98
- `content: { "data" => ... }` reports `success? == false` and `data == nil`.
99
-
100
- ### error?
101
-
102
- ```ruby
103
- message.error? # => Boolean
104
- ```
105
-
106
- Returns `true` if `content` is a Hash containing the **symbol** key `:error`.
107
-
108
- ### data
109
-
110
- ```ruby
111
- message.data # => Object | nil
112
- ```
113
-
114
- Returns the result data if successful, `nil` otherwise.
115
-
116
- ### error
117
-
118
- ```ruby
119
- message.error # => String | nil
120
- ```
121
-
122
- Returns the error message if there was an error, `nil` otherwise.
123
-
124
- ### to_h
125
-
126
- ```ruby
127
- message.to_h # => Hash
128
- ```
129
-
130
- Hash representation. `ToolResultMessage` overrides `Message#to_h` and does
131
- **not** compact, so all five keys are always present.
132
-
133
- **Returns:**
134
-
135
- ```ruby
136
- {
137
- type: "tool_result",
138
- role: "tool_result",
139
- tool: { type: "tool", id: "call_abc123", name: "get_weather", input: { city: "NYC" } },
140
- content: { data: { temperature: 72, conditions: "sunny" } },
141
- stop_reason: "tool"
142
- }
143
- ```
144
-
145
- ### to_json
146
-
147
- ```ruby
148
- message.to_json # => String
149
- ```
150
-
151
- JSON representation.
152
-
153
- ### Predicates
154
-
155
- ```ruby
156
- message.tool_result? # => true
157
- message.tool_call? # => false
158
- message.text? # => false
159
- message.tool_stop? # => true
160
- ```
161
-
162
- ## Examples
163
-
164
- ### Successful Result
165
-
166
- ```ruby
167
- tool = ToolMessage.new(id: "call_1", name: "search_orders", input: { user_id: "123" })
168
-
169
- result = ToolResultMessage.new(
170
- tool: tool,
171
- content: { data: { order_id: "ord_123", status: "shipped" } }
172
- )
173
-
174
- result.success? # => true
175
- result.data # => { order_id: "ord_123", status: "shipped" }
176
- ```
177
-
178
- ### Error Result
179
-
180
- ```ruby
181
- tool = ToolMessage.new(id: "call_order", name: "get_order", input: { id: "bad" })
182
-
183
- result = ToolResultMessage.new(
184
- tool: tool,
185
- content: { error: "Order not found" }
186
- )
187
-
188
- result.error? # => true
189
- result.error # => "Order not found"
190
- result.data # => nil
191
- ```
192
-
193
- ### Accessing Tool Information
194
-
195
- ```ruby
196
- result = ToolResultMessage.new(
197
- tool: ToolMessage.new(id: "call_1", name: "get_weather", input: { city: "Berlin" }),
198
- content: { data: { temperature: 15, unit: "celsius" } }
199
- )
200
-
201
- result.tool.name # => "get_weather"
202
- result.tool.id # => "call_1"
203
- result.tool.input # => { city: "Berlin" }
204
- result.data # => { temperature: 15, unit: "celsius" }
205
- ```
206
-
207
- ### Matching Tool Calls with Results
208
-
209
- ```ruby
210
- # Given a ToolCallMessage and its results
211
- tool_call_msg.tools.each do |tool|
212
- # Find the matching result
213
- matching_result = results.find { |r| r.tool.id == tool.id }
214
-
215
- if matching_result&.success?
216
- puts "#{tool.name}(#{tool.input}) => #{matching_result.data}"
217
- elsif matching_result&.error?
218
- puts "#{tool.name} failed: #{matching_result.error}"
219
- end
220
- end
221
- ```
222
-
223
- ### In Memory History
224
-
225
- ```ruby
226
- # Find all tool results from memory
227
- tool_results = memory.messages.select(&:tool_result?)
228
-
229
- tool_results.each do |tr|
230
- if tr.success?
231
- puts "#{tr.tool.name}: #{tr.data}"
232
- else
233
- puts "#{tr.tool.name} error: #{tr.error}"
234
- end
235
- end
236
- ```
237
-
238
- ## See Also
239
-
240
- - [ToolCallMessage](tool-call-message.md)
241
- - [Tool](../core/tool.md)
242
- - [Using Tools Guide](../../guides/using-tools.md)
@@ -1,232 +0,0 @@
1
- # UserMessage
2
-
3
- User input with conversation metadata.
4
-
5
- ## Class: `RobotLab::UserMessage`
6
-
7
- ```ruby
8
- message = UserMessage.new(
9
- "What's my order status?",
10
- session_id: "session_123",
11
- system_prompt: "Be concise",
12
- metadata: { source: "web" }
13
- )
14
- ```
15
-
16
- **Note:** `UserMessage` is a standalone class, not a subclass of `Message`.
17
-
18
- ## Constructor
19
-
20
- ```ruby
21
- UserMessage.new(content, session_id: nil, system_prompt: nil, metadata: nil, id: nil)
22
- ```
23
-
24
- **Parameters:**
25
-
26
- | Name | Type | Description |
27
- |------|------|-------------|
28
- | `content` | `String` | Message text |
29
- | `session_id` | `String`, `nil` | Conversation session ID |
30
- | `system_prompt` | `String`, `nil` | Override system prompt |
31
- | `metadata` | `Hash`, `nil` | Additional metadata |
32
- | `id` | `String`, `nil` | Unique message ID (defaults to UUID) |
33
-
34
- ## Attributes
35
-
36
- ### content
37
-
38
- ```ruby
39
- message.content # => String
40
- ```
41
-
42
- The message text.
43
-
44
- ### session_id
45
-
46
- ```ruby
47
- message.session_id # => String | nil
48
- ```
49
-
50
- Conversation session identifier for history persistence.
51
-
52
- ### system_prompt
53
-
54
- ```ruby
55
- message.system_prompt # => String | nil
56
- ```
57
-
58
- Optional system prompt override for this message.
59
-
60
- ### metadata
61
-
62
- ```ruby
63
- message.metadata # => Hash
64
- ```
65
-
66
- Arbitrary metadata (source, timestamp, user info, etc.).
67
-
68
- ### id
69
-
70
- ```ruby
71
- message.id # => String (UUID)
72
- ```
73
-
74
- Unique message identifier.
75
-
76
- ### created_at
77
-
78
- ```ruby
79
- message.created_at # => Time
80
- ```
81
-
82
- Message creation timestamp.
83
-
84
- ## Methods
85
-
86
- ### to_h
87
-
88
- ```ruby
89
- message.to_h # => Hash
90
- ```
91
-
92
- Hash representation. The hash ends in `.compact`, so keys whose value is `nil`
93
- are **omitted** — a message built without `session_id:` or `system_prompt:` has
94
- no such keys at all. `metadata` always survives because it defaults to `{}`.
95
-
96
- `created_at` is serialized with `Time#iso8601`, which carries the **local** UTC
97
- offset. It is not normalized to `Z`/UTC.
98
-
99
- **Returns:**
100
-
101
- ```ruby
102
- UserMessage.new(
103
- "What's my order status?",
104
- session_id: "session_123",
105
- system_prompt: "Be concise",
106
- metadata: { source: "web" }
107
- ).to_h
108
- # => {
109
- # content: "What's my order status?",
110
- # session_id: "session_123",
111
- # system_prompt: "Be concise",
112
- # metadata: { source: "web" },
113
- # id: "uuid-here",
114
- # created_at: "2026-07-31T13:04:42-05:00"
115
- # }
116
-
117
- UserMessage.new("hi").to_h
118
- # => {
119
- # content: "hi",
120
- # metadata: {},
121
- # id: "uuid-here",
122
- # created_at: "2026-07-31T13:04:42-05:00"
123
- # }
124
- ```
125
-
126
- ### to_json
127
-
128
- ```ruby
129
- message.to_json # => String
130
- ```
131
-
132
- JSON representation.
133
-
134
- ### to_message
135
-
136
- ```ruby
137
- message.to_message # => TextMessage
138
- ```
139
-
140
- Converts to a `TextMessage` with role `"user"` for use in conversation history.
141
-
142
- ### to_s
143
-
144
- ```ruby
145
- message.to_s # => String
146
- ```
147
-
148
- Returns the content string.
149
-
150
- ### self.from
151
-
152
- ```ruby
153
- UserMessage.from(input) # => UserMessage
154
- ```
155
-
156
- Normalizes any input into a `UserMessage`:
157
-
158
- | Input | Result |
159
- |-------|--------|
160
- | `UserMessage` | Returned unchanged (same object) |
161
- | `String` | `new(input)` |
162
- | `Hash` | Keys symbolized, then `new(content, session_id:, system_prompt:, metadata:, id:)` |
163
- | `TextMessage` | `new(input.content)` — role and stop reason are dropped |
164
- | anything else | `new(input.to_s)` |
165
-
166
- There is no "unsupported input" branch: `from` never raises for an unrecognized
167
- type, it falls through to `to_s`.
168
-
169
- ## Examples
170
-
171
- ### Basic Message
172
-
173
- ```ruby
174
- message = UserMessage.new("Hello!")
175
- ```
176
-
177
- ### With Session ID
178
-
179
- ```ruby
180
- message = UserMessage.new(
181
- "Continue our conversation",
182
- session_id: "session_abc123"
183
- )
184
- ```
185
-
186
- ### With System Prompt Override
187
-
188
- ```ruby
189
- message = UserMessage.new(
190
- "Translate this",
191
- system_prompt: "You are a translator. Respond in Spanish."
192
- )
193
- ```
194
-
195
- ### With Metadata
196
-
197
- ```ruby
198
- message = UserMessage.new(
199
- "Help with my account",
200
- metadata: {
201
- source: "mobile_app",
202
- user_id: "user_123",
203
- session_id: "sess_456",
204
- locale: "en-US"
205
- }
206
- )
207
- ```
208
-
209
- ### Creating from Various Inputs
210
-
211
- ```ruby
212
- # From a string
213
- msg = UserMessage.from("Hello!")
214
-
215
- # From a hash
216
- msg = UserMessage.from(content: "Hello!", session_id: "123")
217
-
218
- # From an existing UserMessage (returns as-is)
219
- msg = UserMessage.from(existing_message)
220
-
221
- # From a TextMessage (only the content is carried over)
222
- msg = UserMessage.from(TextMessage.new(role: "user", content: "Hello!"))
223
- msg.content # => "Hello!"
224
-
225
- # Anything else falls back to to_s
226
- UserMessage.from(42).content # => "42"
227
- ```
228
-
229
- ## See Also
230
-
231
- - [Memory](../core/memory.md)
232
- - [TextMessage](text-message.md)