robot_lab 0.3.0 → 0.5.0

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/README.md +29 -19
  4. data/lib/robot_lab/errors.rb +4 -0
  5. data/lib/robot_lab/hook_context.rb +9 -2
  6. data/lib/robot_lab/mcp/registry.rb +99 -0
  7. data/lib/robot_lab/mcp/server.rb +47 -83
  8. data/lib/robot_lab/mcp/server_discovery.rb +22 -9
  9. data/lib/robot_lab/mcp.rb +259 -0
  10. data/lib/robot_lab/robot/mcp_management.rb +159 -105
  11. data/lib/robot_lab/robot/tool_call_gating.rb +144 -0
  12. data/lib/robot_lab/robot.rb +72 -99
  13. data/lib/robot_lab/run_config.rb +3 -2
  14. data/lib/robot_lab/tool.rb +8 -23
  15. data/lib/robot_lab/version.rb +1 -1
  16. data/lib/robot_lab.rb +1 -4
  17. metadata +16 -292
  18. data/.architecture/AGENTS.md +0 -32
  19. data/.architecture/config.yml +0 -8
  20. data/.architecture/members.yml +0 -60
  21. data/.architecture/reviews/feature-free-will.md +0 -490
  22. data/.architecture/reviews/overall-codebase.md +0 -427
  23. data/.claude/settings.local.json +0 -57
  24. data/.codex/config.toml +0 -2
  25. data/.envrc +0 -6
  26. data/.github/workflows/deploy-github-pages.yml +0 -52
  27. data/.irbrc +0 -6
  28. data/.loki +0 -65
  29. data/.quality/reek_baseline.txt +0 -43
  30. data/.rubocop.yml +0 -10
  31. data/Archspec.rb +0 -44
  32. data/CLAUDE.md +0 -141
  33. data/COMMITS.md +0 -196
  34. data/Rakefile +0 -193
  35. data/_typos.toml +0 -21
  36. data/agent2agent_review.md +0 -192
  37. data/agentf_improvements.md +0 -253
  38. data/agents.md +0 -14
  39. data/docs/api/core/index.md +0 -147
  40. data/docs/api/core/memory.md +0 -578
  41. data/docs/api/core/network.md +0 -553
  42. data/docs/api/core/result.md +0 -211
  43. data/docs/api/core/robot.md +0 -1667
  44. data/docs/api/core/state.md +0 -145
  45. data/docs/api/core/tool.md +0 -520
  46. data/docs/api/errors.md +0 -323
  47. data/docs/api/hooks.md +0 -469
  48. data/docs/api/index.md +0 -137
  49. data/docs/api/mcp/client.md +0 -387
  50. data/docs/api/mcp/index.md +0 -267
  51. data/docs/api/mcp/server.md +0 -252
  52. data/docs/api/mcp/transports.md +0 -339
  53. data/docs/api/messages/index.md +0 -126
  54. data/docs/api/messages/text-message.md +0 -170
  55. data/docs/api/messages/tool-call-message.md +0 -206
  56. data/docs/api/messages/tool-result-message.md +0 -242
  57. data/docs/api/messages/user-message.md +0 -232
  58. data/docs/api/skills.md +0 -423
  59. data/docs/api/streaming/context.md +0 -280
  60. data/docs/api/streaming/events.md +0 -185
  61. data/docs/api/streaming/index.md +0 -156
  62. data/docs/api/support.md +0 -846
  63. data/docs/architecture/core-concepts.md +0 -575
  64. data/docs/architecture/index.md +0 -252
  65. data/docs/architecture/message-flow.md +0 -366
  66. data/docs/architecture/network-orchestration.md +0 -478
  67. data/docs/architecture/robot-execution.md +0 -396
  68. data/docs/architecture/state-management.md +0 -410
  69. data/docs/assets/css/custom.css +0 -56
  70. data/docs/assets/images/robot_lab.jpg +0 -0
  71. data/docs/concepts.md +0 -641
  72. data/docs/examples/basic-chat.md +0 -258
  73. data/docs/examples/index.md +0 -425
  74. data/docs/examples/mcp-server.md +0 -403
  75. data/docs/examples/multi-robot-network.md +0 -476
  76. data/docs/examples/tool-usage.md +0 -468
  77. data/docs/getting-started/configuration.md +0 -645
  78. data/docs/getting-started/index.md +0 -56
  79. data/docs/getting-started/installation.md +0 -197
  80. data/docs/getting-started/quick-start.md +0 -293
  81. data/docs/guides/building-robots.md +0 -1187
  82. data/docs/guides/creating-networks.md +0 -736
  83. data/docs/guides/hooks.md +0 -1206
  84. data/docs/guides/index.md +0 -78
  85. data/docs/guides/knowledge.md +0 -219
  86. data/docs/guides/mcp-integration.md +0 -740
  87. data/docs/guides/memory.md +0 -458
  88. data/docs/guides/observability.md +0 -733
  89. data/docs/guides/streaming.md +0 -323
  90. data/docs/guides/using-tools.md +0 -780
  91. data/docs/index.md +0 -280
  92. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  93. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  94. data/examples/.envrc +0 -3
  95. data/examples/01_simple_robot.rb +0 -31
  96. data/examples/02_tools.rb +0 -109
  97. data/examples/03_network.rb +0 -118
  98. data/examples/04_mcp.rb +0 -217
  99. data/examples/05_streaming.rb +0 -137
  100. data/examples/06_prompt_templates.rb +0 -325
  101. data/examples/07_network_memory.rb +0 -313
  102. data/examples/08_llm_config.rb +0 -162
  103. data/examples/09_chaining.rb +0 -251
  104. data/examples/10_memory.rb +0 -315
  105. data/examples/11_network_introspection.rb +0 -239
  106. data/examples/12_message_bus.rb +0 -67
  107. data/examples/13_spawn.rb +0 -85
  108. data/examples/14_rusty_circuit/.envrc +0 -1
  109. data/examples/14_rusty_circuit/.gitignore +0 -1
  110. data/examples/14_rusty_circuit/README.md +0 -1
  111. data/examples/14_rusty_circuit/comic.rb +0 -149
  112. data/examples/14_rusty_circuit/display.rb +0 -217
  113. data/examples/14_rusty_circuit/heckler.rb +0 -65
  114. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  115. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  116. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  117. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  118. data/examples/14_rusty_circuit/scout.rb +0 -165
  119. data/examples/14_rusty_circuit/show.log +0 -234
  120. data/examples/15_memory_network_and_bus/.envrc +0 -1
  121. data/examples/15_memory_network_and_bus/README.md +0 -66
  122. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  123. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  124. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  125. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  126. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  127. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  128. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  129. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  130. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  131. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  132. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  133. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  134. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  135. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  136. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  137. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  138. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  139. data/examples/16_writers_room/.envrc +0 -1
  140. data/examples/16_writers_room/display.rb +0 -158
  141. data/examples/16_writers_room/output/.gitignore +0 -4
  142. data/examples/16_writers_room/output/README.md +0 -69
  143. data/examples/16_writers_room/output/opus_001.md +0 -263
  144. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  145. data/examples/16_writers_room/output/opus_002.md +0 -245
  146. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  147. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  148. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  149. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  150. data/examples/16_writers_room/prompts/writer.md +0 -37
  151. data/examples/16_writers_room/room.rb +0 -186
  152. data/examples/16_writers_room/tools.rb +0 -173
  153. data/examples/16_writers_room/writer.rb +0 -121
  154. data/examples/16_writers_room/writers_room.rb +0 -256
  155. data/examples/17_skills.rb +0 -234
  156. data/examples/18_rails/.envrc +0 -3
  157. data/examples/18_rails/.gitignore +0 -5
  158. data/examples/18_rails/Gemfile +0 -11
  159. data/examples/18_rails/README.md +0 -67
  160. data/examples/18_rails/Rakefile +0 -4
  161. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  162. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  163. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  164. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  165. data/examples/18_rails/app/models/application_record.rb +0 -5
  166. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  167. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  168. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  169. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  170. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  171. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  172. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  173. data/examples/18_rails/bin/dev +0 -7
  174. data/examples/18_rails/bin/rails +0 -6
  175. data/examples/18_rails/bin/setup +0 -15
  176. data/examples/18_rails/config/application.rb +0 -33
  177. data/examples/18_rails/config/cable.yml +0 -2
  178. data/examples/18_rails/config/database.yml +0 -5
  179. data/examples/18_rails/config/environment.rb +0 -4
  180. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  181. data/examples/18_rails/config/routes.rb +0 -6
  182. data/examples/18_rails/config.ru +0 -4
  183. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  184. data/examples/19_token_tracking.rb +0 -138
  185. data/examples/20_circuit_breaker.rb +0 -147
  186. data/examples/21_learning_loop.rb +0 -181
  187. data/examples/22_context_compression.rb +0 -172
  188. data/examples/23_convergence.rb +0 -133
  189. data/examples/24_structured_delegation.rb +0 -151
  190. data/examples/25_history_search/conversation.jsonl +0 -30
  191. data/examples/25_history_search.rb +0 -133
  192. data/examples/26_document_store/api_versioning_adr.md +0 -52
  193. data/examples/26_document_store/incident_postmortem.md +0 -46
  194. data/examples/26_document_store/postgres_runbook.md +0 -49
  195. data/examples/26_document_store/redis_caching_guide.md +0 -48
  196. data/examples/26_document_store/sidekiq_guide.md +0 -51
  197. data/examples/26_document_store.rb +0 -140
  198. data/examples/27_incident_response/README.md +0 -65
  199. data/examples/27_incident_response/incident_response.rb +0 -261
  200. data/examples/28_mcp_discovery.rb +0 -113
  201. data/examples/29_ractor_tools.rb +0 -240
  202. data/examples/30_ractor_network.rb +0 -252
  203. data/examples/31_launch_assessment.rb +0 -246
  204. data/examples/32_newsletter_reader.rb +0 -193
  205. data/examples/33_stock_generator.rb +0 -80
  206. data/examples/33_stock_predictor.rb +0 -327
  207. data/examples/34_agentskills.rb +0 -76
  208. data/examples/35_hooks.rb +0 -266
  209. data/examples/README.md +0 -550
  210. data/examples/common.rb +0 -180
  211. data/examples/prompts/assistant.md +0 -5
  212. data/examples/prompts/audit_trail.md +0 -8
  213. data/examples/prompts/billing.md +0 -10
  214. data/examples/prompts/classifier.md +0 -7
  215. data/examples/prompts/comedian.md +0 -6
  216. data/examples/prompts/comedy_critic.md +0 -10
  217. data/examples/prompts/configurable.md +0 -9
  218. data/examples/prompts/dispatcher.md +0 -12
  219. data/examples/prompts/entity_extractor.md +0 -14
  220. data/examples/prompts/escalation.md +0 -42
  221. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  222. data/examples/prompts/frontmatter_named_test.md +0 -5
  223. data/examples/prompts/frontmatter_tools_test.md +0 -6
  224. data/examples/prompts/general.md +0 -7
  225. data/examples/prompts/github_assistant.md +0 -14
  226. data/examples/prompts/helper.md +0 -4
  227. data/examples/prompts/incident_responder.md +0 -18
  228. data/examples/prompts/keyword_extractor.md +0 -11
  229. data/examples/prompts/llm_config_demo.md +0 -20
  230. data/examples/prompts/order_support.md +0 -35
  231. data/examples/prompts/os_advocate.md +0 -13
  232. data/examples/prompts/os_chief.md +0 -13
  233. data/examples/prompts/os_editor.md +0 -13
  234. data/examples/prompts/parameterized_main_test.md +0 -6
  235. data/examples/prompts/pii_redactor.md +0 -7
  236. data/examples/prompts/product_support.md +0 -37
  237. data/examples/prompts/runbook_protocol.md +0 -12
  238. data/examples/prompts/sentiment_analyzer.md +0 -12
  239. data/examples/prompts/skill_a_test.md +0 -4
  240. data/examples/prompts/skill_b_test.md +0 -4
  241. data/examples/prompts/skill_config_test.md +0 -5
  242. data/examples/prompts/skill_cycle_a_test.md +0 -6
  243. data/examples/prompts/skill_cycle_b_test.md +0 -6
  244. data/examples/prompts/skill_description_test.md +0 -4
  245. data/examples/prompts/skill_leaf_test.md +0 -4
  246. data/examples/prompts/skill_nested_test.md +0 -6
  247. data/examples/prompts/skill_refs_main_test.md +0 -6
  248. data/examples/prompts/skill_self_ref_test.md +0 -6
  249. data/examples/prompts/skill_with_mcp_test.md +0 -9
  250. data/examples/prompts/skill_with_params_test.md +0 -6
  251. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  252. data/examples/prompts/skill_with_tools_test.md +0 -6
  253. data/examples/prompts/sre_compliance.md +0 -10
  254. data/examples/prompts/structured_output.md +0 -7
  255. data/examples/prompts/synthesizer.md +0 -17
  256. data/examples/prompts/technical.md +0 -10
  257. data/examples/prompts/template_with_skills_test.md +0 -6
  258. data/examples/prompts/triage.md +0 -22
  259. data/examples/ruboruby.md +0 -423
  260. data/examples/run_all.rb +0 -60
  261. data/examples/xyzzy.rb +0 -97
  262. data/lib/robot_lab/mcp/client.rb +0 -223
  263. data/lib/robot_lab/mcp/connection_poller.rb +0 -195
  264. data/lib/robot_lab/mcp/transports/base.rb +0 -64
  265. data/lib/robot_lab/mcp/transports/sse.rb +0 -120
  266. data/lib/robot_lab/mcp/transports/stdio.rb +0 -170
  267. data/lib/robot_lab/mcp/transports/streamable_http.rb +0 -141
  268. data/lib/robot_lab/mcp/transports/websocket.rb +0 -111
  269. data/logfile +0 -8
  270. data/mkdocs.yml +0 -209
  271. data/robot_concurrency.md +0 -38
  272. data/simple_acp_review.md +0 -298
  273. data/temp.md +0 -6
  274. 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)