robot_lab 0.3.0 → 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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +1 -1
  4. data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
  5. data/lib/robot_lab/version.rb +1 -1
  6. metadata +3 -253
  7. data/.architecture/AGENTS.md +0 -32
  8. data/.architecture/config.yml +0 -8
  9. data/.architecture/members.yml +0 -60
  10. data/.architecture/reviews/feature-free-will.md +0 -490
  11. data/.architecture/reviews/overall-codebase.md +0 -427
  12. data/.claude/settings.local.json +0 -57
  13. data/.codex/config.toml +0 -2
  14. data/.envrc +0 -6
  15. data/.github/workflows/deploy-github-pages.yml +0 -52
  16. data/.irbrc +0 -6
  17. data/.loki +0 -65
  18. data/.quality/reek_baseline.txt +0 -43
  19. data/.rubocop.yml +0 -10
  20. data/Archspec.rb +0 -44
  21. data/CLAUDE.md +0 -141
  22. data/COMMITS.md +0 -196
  23. data/Rakefile +0 -193
  24. data/_typos.toml +0 -21
  25. data/agent2agent_review.md +0 -192
  26. data/agentf_improvements.md +0 -253
  27. data/agents.md +0 -14
  28. data/docs/api/core/index.md +0 -147
  29. data/docs/api/core/memory.md +0 -578
  30. data/docs/api/core/network.md +0 -553
  31. data/docs/api/core/result.md +0 -211
  32. data/docs/api/core/robot.md +0 -1667
  33. data/docs/api/core/state.md +0 -145
  34. data/docs/api/core/tool.md +0 -520
  35. data/docs/api/errors.md +0 -323
  36. data/docs/api/hooks.md +0 -469
  37. data/docs/api/index.md +0 -137
  38. data/docs/api/mcp/client.md +0 -387
  39. data/docs/api/mcp/index.md +0 -267
  40. data/docs/api/mcp/server.md +0 -252
  41. data/docs/api/mcp/transports.md +0 -339
  42. data/docs/api/messages/index.md +0 -126
  43. data/docs/api/messages/text-message.md +0 -170
  44. data/docs/api/messages/tool-call-message.md +0 -206
  45. data/docs/api/messages/tool-result-message.md +0 -242
  46. data/docs/api/messages/user-message.md +0 -232
  47. data/docs/api/skills.md +0 -423
  48. data/docs/api/streaming/context.md +0 -280
  49. data/docs/api/streaming/events.md +0 -185
  50. data/docs/api/streaming/index.md +0 -156
  51. data/docs/api/support.md +0 -846
  52. data/docs/architecture/core-concepts.md +0 -575
  53. data/docs/architecture/index.md +0 -252
  54. data/docs/architecture/message-flow.md +0 -366
  55. data/docs/architecture/network-orchestration.md +0 -478
  56. data/docs/architecture/robot-execution.md +0 -396
  57. data/docs/architecture/state-management.md +0 -410
  58. data/docs/assets/css/custom.css +0 -56
  59. data/docs/assets/images/robot_lab.jpg +0 -0
  60. data/docs/concepts.md +0 -641
  61. data/docs/examples/basic-chat.md +0 -258
  62. data/docs/examples/index.md +0 -425
  63. data/docs/examples/mcp-server.md +0 -403
  64. data/docs/examples/multi-robot-network.md +0 -476
  65. data/docs/examples/tool-usage.md +0 -468
  66. data/docs/getting-started/configuration.md +0 -645
  67. data/docs/getting-started/index.md +0 -56
  68. data/docs/getting-started/installation.md +0 -197
  69. data/docs/getting-started/quick-start.md +0 -293
  70. data/docs/guides/building-robots.md +0 -1187
  71. data/docs/guides/creating-networks.md +0 -736
  72. data/docs/guides/hooks.md +0 -1206
  73. data/docs/guides/index.md +0 -78
  74. data/docs/guides/knowledge.md +0 -219
  75. data/docs/guides/mcp-integration.md +0 -740
  76. data/docs/guides/memory.md +0 -458
  77. data/docs/guides/observability.md +0 -733
  78. data/docs/guides/streaming.md +0 -323
  79. data/docs/guides/using-tools.md +0 -780
  80. data/docs/index.md +0 -280
  81. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  82. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  83. data/examples/.envrc +0 -3
  84. data/examples/01_simple_robot.rb +0 -31
  85. data/examples/02_tools.rb +0 -109
  86. data/examples/03_network.rb +0 -118
  87. data/examples/04_mcp.rb +0 -217
  88. data/examples/05_streaming.rb +0 -137
  89. data/examples/06_prompt_templates.rb +0 -325
  90. data/examples/07_network_memory.rb +0 -313
  91. data/examples/08_llm_config.rb +0 -162
  92. data/examples/09_chaining.rb +0 -251
  93. data/examples/10_memory.rb +0 -315
  94. data/examples/11_network_introspection.rb +0 -239
  95. data/examples/12_message_bus.rb +0 -67
  96. data/examples/13_spawn.rb +0 -85
  97. data/examples/14_rusty_circuit/.envrc +0 -1
  98. data/examples/14_rusty_circuit/.gitignore +0 -1
  99. data/examples/14_rusty_circuit/README.md +0 -1
  100. data/examples/14_rusty_circuit/comic.rb +0 -149
  101. data/examples/14_rusty_circuit/display.rb +0 -217
  102. data/examples/14_rusty_circuit/heckler.rb +0 -65
  103. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  104. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  105. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  106. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  107. data/examples/14_rusty_circuit/scout.rb +0 -165
  108. data/examples/14_rusty_circuit/show.log +0 -234
  109. data/examples/15_memory_network_and_bus/.envrc +0 -1
  110. data/examples/15_memory_network_and_bus/README.md +0 -66
  111. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  112. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  113. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  114. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  115. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  116. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  117. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  118. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  119. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  120. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  121. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  122. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  123. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  124. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  125. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  126. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  127. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  128. data/examples/16_writers_room/.envrc +0 -1
  129. data/examples/16_writers_room/display.rb +0 -158
  130. data/examples/16_writers_room/output/.gitignore +0 -4
  131. data/examples/16_writers_room/output/README.md +0 -69
  132. data/examples/16_writers_room/output/opus_001.md +0 -263
  133. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  134. data/examples/16_writers_room/output/opus_002.md +0 -245
  135. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  136. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  137. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  138. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  139. data/examples/16_writers_room/prompts/writer.md +0 -37
  140. data/examples/16_writers_room/room.rb +0 -186
  141. data/examples/16_writers_room/tools.rb +0 -173
  142. data/examples/16_writers_room/writer.rb +0 -121
  143. data/examples/16_writers_room/writers_room.rb +0 -256
  144. data/examples/17_skills.rb +0 -234
  145. data/examples/18_rails/.envrc +0 -3
  146. data/examples/18_rails/.gitignore +0 -5
  147. data/examples/18_rails/Gemfile +0 -11
  148. data/examples/18_rails/README.md +0 -67
  149. data/examples/18_rails/Rakefile +0 -4
  150. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  151. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  152. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  153. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  154. data/examples/18_rails/app/models/application_record.rb +0 -5
  155. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  156. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  157. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  158. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  159. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  160. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  161. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  162. data/examples/18_rails/bin/dev +0 -7
  163. data/examples/18_rails/bin/rails +0 -6
  164. data/examples/18_rails/bin/setup +0 -15
  165. data/examples/18_rails/config/application.rb +0 -33
  166. data/examples/18_rails/config/cable.yml +0 -2
  167. data/examples/18_rails/config/database.yml +0 -5
  168. data/examples/18_rails/config/environment.rb +0 -4
  169. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  170. data/examples/18_rails/config/routes.rb +0 -6
  171. data/examples/18_rails/config.ru +0 -4
  172. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  173. data/examples/19_token_tracking.rb +0 -138
  174. data/examples/20_circuit_breaker.rb +0 -147
  175. data/examples/21_learning_loop.rb +0 -181
  176. data/examples/22_context_compression.rb +0 -172
  177. data/examples/23_convergence.rb +0 -133
  178. data/examples/24_structured_delegation.rb +0 -151
  179. data/examples/25_history_search/conversation.jsonl +0 -30
  180. data/examples/25_history_search.rb +0 -133
  181. data/examples/26_document_store/api_versioning_adr.md +0 -52
  182. data/examples/26_document_store/incident_postmortem.md +0 -46
  183. data/examples/26_document_store/postgres_runbook.md +0 -49
  184. data/examples/26_document_store/redis_caching_guide.md +0 -48
  185. data/examples/26_document_store/sidekiq_guide.md +0 -51
  186. data/examples/26_document_store.rb +0 -140
  187. data/examples/27_incident_response/README.md +0 -65
  188. data/examples/27_incident_response/incident_response.rb +0 -261
  189. data/examples/28_mcp_discovery.rb +0 -113
  190. data/examples/29_ractor_tools.rb +0 -240
  191. data/examples/30_ractor_network.rb +0 -252
  192. data/examples/31_launch_assessment.rb +0 -246
  193. data/examples/32_newsletter_reader.rb +0 -193
  194. data/examples/33_stock_generator.rb +0 -80
  195. data/examples/33_stock_predictor.rb +0 -327
  196. data/examples/34_agentskills.rb +0 -76
  197. data/examples/35_hooks.rb +0 -266
  198. data/examples/README.md +0 -550
  199. data/examples/common.rb +0 -180
  200. data/examples/prompts/assistant.md +0 -5
  201. data/examples/prompts/audit_trail.md +0 -8
  202. data/examples/prompts/billing.md +0 -10
  203. data/examples/prompts/classifier.md +0 -7
  204. data/examples/prompts/comedian.md +0 -6
  205. data/examples/prompts/comedy_critic.md +0 -10
  206. data/examples/prompts/configurable.md +0 -9
  207. data/examples/prompts/dispatcher.md +0 -12
  208. data/examples/prompts/entity_extractor.md +0 -14
  209. data/examples/prompts/escalation.md +0 -42
  210. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  211. data/examples/prompts/frontmatter_named_test.md +0 -5
  212. data/examples/prompts/frontmatter_tools_test.md +0 -6
  213. data/examples/prompts/general.md +0 -7
  214. data/examples/prompts/github_assistant.md +0 -14
  215. data/examples/prompts/helper.md +0 -4
  216. data/examples/prompts/incident_responder.md +0 -18
  217. data/examples/prompts/keyword_extractor.md +0 -11
  218. data/examples/prompts/llm_config_demo.md +0 -20
  219. data/examples/prompts/order_support.md +0 -35
  220. data/examples/prompts/os_advocate.md +0 -13
  221. data/examples/prompts/os_chief.md +0 -13
  222. data/examples/prompts/os_editor.md +0 -13
  223. data/examples/prompts/parameterized_main_test.md +0 -6
  224. data/examples/prompts/pii_redactor.md +0 -7
  225. data/examples/prompts/product_support.md +0 -37
  226. data/examples/prompts/runbook_protocol.md +0 -12
  227. data/examples/prompts/sentiment_analyzer.md +0 -12
  228. data/examples/prompts/skill_a_test.md +0 -4
  229. data/examples/prompts/skill_b_test.md +0 -4
  230. data/examples/prompts/skill_config_test.md +0 -5
  231. data/examples/prompts/skill_cycle_a_test.md +0 -6
  232. data/examples/prompts/skill_cycle_b_test.md +0 -6
  233. data/examples/prompts/skill_description_test.md +0 -4
  234. data/examples/prompts/skill_leaf_test.md +0 -4
  235. data/examples/prompts/skill_nested_test.md +0 -6
  236. data/examples/prompts/skill_refs_main_test.md +0 -6
  237. data/examples/prompts/skill_self_ref_test.md +0 -6
  238. data/examples/prompts/skill_with_mcp_test.md +0 -9
  239. data/examples/prompts/skill_with_params_test.md +0 -6
  240. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  241. data/examples/prompts/skill_with_tools_test.md +0 -6
  242. data/examples/prompts/sre_compliance.md +0 -10
  243. data/examples/prompts/structured_output.md +0 -7
  244. data/examples/prompts/synthesizer.md +0 -17
  245. data/examples/prompts/technical.md +0 -10
  246. data/examples/prompts/template_with_skills_test.md +0 -6
  247. data/examples/prompts/triage.md +0 -22
  248. data/examples/ruboruby.md +0 -423
  249. data/examples/run_all.rb +0 -60
  250. data/examples/xyzzy.rb +0 -97
  251. data/logfile +0 -8
  252. data/mkdocs.yml +0 -209
  253. data/robot_concurrency.md +0 -38
  254. data/simple_acp_review.md +0 -298
  255. data/temp.md +0 -6
  256. data/tool_manifest_plan.md +0 -155
@@ -1,578 +0,0 @@
1
- # Memory
2
-
3
- Reactive key-value store for sharing data between robots.
4
-
5
- ## Class: `RobotLab::Memory`
6
-
7
- ```ruby
8
- memory = robot.memory
9
-
10
- memory.set(:key, "value")
11
- value = memory.get(:key)
12
- ```
13
-
14
- ## Constants
15
-
16
- ### RESERVED_KEYS
17
-
18
- ```ruby
19
- Memory::RESERVED_KEYS # => [:data, :results, :messages, :session_id, :cache]
20
- ```
21
-
22
- Reserved keys with special accessors and behavior.
23
-
24
- ## Constructor
25
-
26
- ```ruby
27
- memory = Memory.new(
28
- data: {},
29
- results: [],
30
- messages: [],
31
- session_id: nil,
32
- backend: :auto,
33
- enable_cache: true,
34
- network_name: nil
35
- )
36
- ```
37
-
38
- **Parameters:**
39
-
40
- | Name | Type | Default | Description |
41
- |------|------|---------|-------------|
42
- | `data` | `Hash` | `{}` | Initial runtime data |
43
- | `results` | `Array` | `[]` | Pre-loaded robot results |
44
- | `messages` | `Array` | `[]` | Pre-loaded conversation messages |
45
- | `session_id` | `String, nil` | `nil` | Session identifier |
46
- | `backend` | `Symbol` | `:auto` | Storage backend (`:auto`, `:redis`, `:hash`) |
47
- | `enable_cache` | `Boolean` | `true` | Whether to enable semantic caching |
48
- | `network_name` | `String, nil` | `nil` | Network this memory belongs to |
49
-
50
- ## Factory Method
51
-
52
- ```ruby
53
- memory = RobotLab.create_memory(data: { user_id: 123 })
54
- ```
55
-
56
- ## Methods
57
-
58
- ### set
59
-
60
- ```ruby
61
- memory.set(:key, value)
62
- ```
63
-
64
- Store a value and notify subscribers asynchronously.
65
-
66
- **Parameters:**
67
-
68
- | Name | Type | Description |
69
- |------|------|-------------|
70
- | `key` | `Symbol`, `String` | Storage key |
71
- | `value` | `Object` | Value to store |
72
-
73
- ### get
74
-
75
- ```ruby
76
- memory.get(:key) # => value or nil
77
- memory.get(:key, wait: true) # Block until available
78
- memory.get(:key, wait: 30) # Block up to 30 seconds
79
- memory.get(:a, :b, :c, wait: 60) # Multiple keys, returns Hash
80
- ```
81
-
82
- Retrieve one or more values, optionally waiting until they exist.
83
-
84
- **Parameters:**
85
-
86
- | Name | Type | Default | Description |
87
- |------|------|---------|-------------|
88
- | `*keys` | `Symbol`, `String` | — | One or more keys to retrieve; flattened and symbolized |
89
- | `wait` | `Boolean`, `Numeric` | `false` | `false`: return immediately (nil if missing). `true`: block indefinitely. `Numeric`: block up to that many seconds |
90
-
91
- **Returns:** the single value for one key, a `Hash` keyed by symbol for multiple keys.
92
-
93
- **Raises:** `RobotLab::AwaitTimeout` — `"Timeout waiting for :<key> after <N> seconds"`.
94
-
95
- !!! warning "Blocking semantics"
96
- - On expiry `get` **raises `RobotLab::AwaitTimeout`**; it does not return nil.
97
- Rescue it if a missing value is acceptable.
98
- - With multiple keys the timeout is applied **per missing key**, not to the
99
- call as a whole. `get(:a, :b, :c, wait: 60)` can block for up to 180
100
- seconds if all three are missing.
101
- - `wait: true` blocks with no deadline and can hang forever. Prefer a numeric
102
- timeout in production.
103
- - Waiting is implemented with a pipe (`Waiter`, using `IO#wait_readable`), so
104
- a blocked reader does not spin.
105
-
106
- ```ruby
107
- value = begin
108
- memory.get(:sentiment, wait: 30)
109
- rescue RobotLab::AwaitTimeout
110
- nil
111
- end
112
- ```
113
-
114
- ### key?
115
-
116
- ```ruby
117
- memory.key?(:key) # => Boolean
118
- ```
119
-
120
- Check if key exists. Aliases: `has_key?`, `include?`.
121
-
122
- ### delete
123
-
124
- ```ruby
125
- memory.delete(:key) # => deleted value
126
- ```
127
-
128
- Remove a key. Cannot delete reserved keys.
129
-
130
- ### keys
131
-
132
- ```ruby
133
- memory.keys # => Array<Symbol>
134
- ```
135
-
136
- All keys **excluding** the reserved ones.
137
-
138
- ### all_keys
139
-
140
- ```ruby
141
- memory.all_keys # => Array<Symbol>
142
- ```
143
-
144
- All keys **including** the reserved ones (`:data`, `:results`, `:messages`,
145
- `:session_id`, `:cache`).
146
-
147
- ### clear
148
-
149
- ```ruby
150
- memory.clear
151
- ```
152
-
153
- Clear all non-reserved keys.
154
-
155
- ### reset
156
-
157
- ```ruby
158
- memory.reset
159
- ```
160
-
161
- Reset memory to its initial state: clears the backend, restores `:data` to `{}`,
162
- `:results` and `:messages` to `[]`, `:session_id` to `nil`, and re-installs the
163
- existing cache object. The `StateProxy` returned by `data` is discarded and
164
- rebuilt on next access.
165
-
166
- This resets the **key-value store only**. It has nothing to do with a robot's
167
- chat history — use `robot.clear_messages(keep_system: true)` for that. The two
168
- are independent.
169
-
170
- ### subscribe
171
-
172
- ```ruby
173
- sub_id = memory.subscribe(:key1, :key2) do |change|
174
- puts "#{change.key} changed: #{change.value}"
175
- end
176
- ```
177
-
178
- Subscribe to changes on one or more keys. Callback receives a `MemoryChange` object.
179
- Returns a subscription ID for [`unsubscribe`](#unsubscribe).
180
-
181
- **Raises:** `ArgumentError` if no block is given.
182
-
183
- Only [`set`](#set) notifies subscribers. Writing a reserved key
184
- (`memory[:data] = ...`, `session_id=`, `append_result`) bypasses notification
185
- entirely, as does mutating `memory.data`.
186
-
187
- Callbacks are dispatched through `Async { }`. **Outside a running reactor the
188
- callback runs synchronously on the writer's thread**, so a slow subscriber blocks
189
- the `set` that triggered it.
190
-
191
- **`RobotLab::MemoryChange` attributes:**
192
-
193
- | Attribute | Type | Description |
194
- |-----------|------|-------------|
195
- | `key` | `Symbol` | The changed key |
196
- | `value` | `Object` | New value |
197
- | `previous` | `Object, nil` | Previous value |
198
- | `writer` | `String, nil` | `memory.current_writer` at the time of the write — set to the robot's name for the duration of each `run` |
199
- | `network_name` | `String, nil` | Network name |
200
- | `timestamp` | `Time` | When the change occurred |
201
- | `correlation_id` | `String, nil` | Optional tracing ID |
202
-
203
- **Predicates** — note that each tests *both* sides, so all three are false when
204
- `previous` and `value` are both nil, and all three are false for a nil→nil write:
205
-
206
- | Predicate | Exact definition |
207
- |-----------|------------------|
208
- | `created?` | `previous.nil? && !value.nil?` |
209
- | `updated?` | `!previous.nil? && !value.nil?` |
210
- | `deleted?` | `value.nil? && !previous.nil?` |
211
-
212
- Also available: `to_h` (`.compact`ed, `timestamp` rendered as ISO-8601),
213
- `to_json`, and `MemoryChange.from_hash`.
214
-
215
- ### subscribe_pattern
216
-
217
- ```ruby
218
- sub_id = memory.subscribe_pattern("analysis:*") do |change|
219
- puts "Analysis key #{change.key} updated"
220
- end
221
- ```
222
-
223
- Subscribe to keys matching a glob pattern (`*` and `?` supported). Returns a
224
- subscription ID. **Raises:** `ArgumentError` if no block is given.
225
-
226
- ### unsubscribe
227
-
228
- ```ruby
229
- memory.unsubscribe(sub_id) # => Boolean
230
- ```
231
-
232
- Remove a subscription by its ID (works for both `subscribe` and
233
- `subscribe_pattern`). Returns `true` when something was removed.
234
-
235
- ### unsubscribe_keys
236
-
237
- ```ruby
238
- memory.unsubscribe_keys(:status, :progress) # => self
239
- ```
240
-
241
- Drop **all** key subscriptions for the named keys at once, without needing their
242
- IDs. Pattern subscriptions are unaffected.
243
-
244
- ### subscribed?
245
-
246
- ```ruby
247
- memory.subscribed?(:status) # => Boolean
248
- ```
249
-
250
- Whether any subscriber — key-based or pattern-based — would be notified for `key`.
251
-
252
- ### merge!
253
-
254
- ```ruby
255
- memory.merge!(key1: "value1", key2: "value2") # => self
256
- ```
257
-
258
- Merge multiple key-value pairs into memory. Each pair is assigned through `[]=`,
259
- so non-reserved keys go through the reactive `set` path and do notify subscribers.
260
-
261
- ## Results and History
262
-
263
- ### append_result
264
-
265
- ```ruby
266
- memory.append_result(robot_result) # => self
267
- ```
268
-
269
- Push a `RobotResult` onto the accumulated `:results` array. Bypasses subscriber
270
- notification.
271
-
272
- ### set_results
273
-
274
- ```ruby
275
- memory.set_results(array_of_results) # => self
276
- ```
277
-
278
- Replace the whole `:results` array (used when loading from persistence).
279
-
280
- ### results_from
281
-
282
- ```ruby
283
- memory.results_from(5) # => Array<RobotResult>
284
- ```
285
-
286
- Results from the given index onward — for incremental saves. Returns `[]` when
287
- the index is past the end.
288
-
289
- ### format_history
290
-
291
- ```ruby
292
- memory.format_history(formatter: nil) # => Array<Message>
293
- ```
294
-
295
- `messages` followed by every result flat-mapped through `formatter`. Pass a
296
- `Proc` for `formatter:` to control how a `RobotResult` becomes messages; the
297
- default formatter is used when omitted.
298
-
299
- ## Backend
300
-
301
- ### redis?
302
-
303
- ```ruby
304
- memory.redis? # => Boolean
305
- ```
306
-
307
- Whether this memory is backed by Redis rather than the in-process Hash. The
308
- `backend: :auto` default tries Redis and falls back to a Hash; `backend: :hash`
309
- forces the Hash.
310
-
311
- ### network_name
312
-
313
- ```ruby
314
- memory.network_name # => String, nil
315
- ```
316
-
317
- The network this memory belongs to, set once at construction and read-only
318
- thereafter. `Network` creates its shared memory as `Memory.new(network_name: name)`;
319
- a standalone robot's inherent memory has `nil`. It is copied onto every
320
- `MemoryChange` so a subscriber can tell which network a write came from, and it is
321
- preserved by `clone`/`dup` — but **not** by `from_hash`.
322
-
323
- ### current_writer / current_writer=
324
-
325
- ```ruby
326
- memory.current_writer # => String, nil
327
- memory.current_writer = "bot"
328
- ```
329
-
330
- The name attributed to writes, surfaced as `MemoryChange#writer`. `Robot#run`
331
- sets this to the robot's name for the duration of the run and restores the
332
- previous value in an `ensure` block, so nested and concurrent runs attribute
333
- correctly.
334
-
335
- ## Document Store
336
-
337
- These four methods require the **`robot_lab-document_store`** extension gem. Without
338
- it every one of them raises
339
- `RobotLab::DependencyError: document storage requires the robot_lab-document_store gem.`
340
-
341
- | Method | Returns | Description |
342
- |--------|---------|-------------|
343
- | `store_document(key, text)` | `self` | Embed `text` and store it under `key` |
344
- | `search_documents(query, limit: 5)` | `Array<Hash>` | Hits sorted by score descending; each hash has `:key`, `:text`, `:score` |
345
- | `document_keys` | `Array<Symbol>` | Keys of all stored documents |
346
- | `delete_document(key)` | `self` | Remove a document |
347
-
348
- ```ruby
349
- memory.store_document(:readme, File.read("README.md"))
350
- memory.search_documents("how to configure redis", limit: 3).each do |hit|
351
- puts "#{hit[:key]} (#{hit[:score].round(3)})"
352
- end
353
- ```
354
-
355
- Documents live in a separate store, not in the key-value backend — they do not
356
- appear in `keys`, `all_keys`, or `to_h`.
357
-
358
- ## Reserved Key Accessors
359
-
360
- ### data
361
-
362
- ```ruby
363
- memory.data # => StateProxy
364
- memory.data[:user_id] # Hash access
365
- memory.data.user_id # Method access
366
- memory.data[:status] = "active"
367
- ```
368
-
369
- Runtime data accessed through a [`StateProxy`](state.md) for method-style access.
370
- Writes made through the proxy are **not** reactive — they bypass `set`, so they
371
- wake no blocking readers and notify no subscribers.
372
-
373
- ### results
374
-
375
- ```ruby
376
- memory.results # => Array<RobotResult>
377
- ```
378
-
379
- Accumulated robot results (returns a copy).
380
-
381
- ### messages
382
-
383
- ```ruby
384
- memory.messages # => Array<Message>
385
- ```
386
-
387
- Conversation messages (returns a copy).
388
-
389
- ### session_id
390
-
391
- ```ruby
392
- memory.session_id # => String | nil
393
- memory.session_id = "abc" # Set session identifier
394
- ```
395
-
396
- ### cache
397
-
398
- ```ruby
399
- memory.cache # => RubyLLM::SemanticCache (the module itself), or nil
400
- ```
401
-
402
- Read-only after initialization — assigning it raises
403
- `ArgumentError: Cannot reassign cache - it is initialized automatically`.
404
-
405
- !!! warning "`cache` is `nil` when caching is disabled"
406
- The value stored is the `RubyLLM::SemanticCache` **module**, not an instance.
407
- When constructed with `enable_cache: false` it is `nil`, so
408
- `memory.cache.fetch(...)` raises `NoMethodError`. Guard on
409
- `memory.cache` before use, or leave `enable_cache` at its `true` default.
410
-
411
- ```ruby
412
- RobotLab::Memory.new.cache # => RubyLLM::SemanticCache
413
- RobotLab::Memory.new(enable_cache: false).cache # => nil
414
- ```
415
-
416
- ## Serialization
417
-
418
- ### to_h
419
-
420
- ```ruby
421
- memory.to_h
422
- # => { data: {...}, results: [...], messages: [...], session_id: "...", custom: {...} }
423
- ```
424
-
425
- Keys: `data` (from the `StateProxy`), `results` (each via `RobotResult#export`),
426
- `messages` (each via `to_h`), `session_id`, and `custom` (every non-reserved key).
427
- `cache` is never serialized.
428
-
429
- !!! warning "`to_h` is `.compact`ed"
430
- Nil values are dropped, so `session_id` disappears entirely when unset:
431
-
432
- ```ruby
433
- RobotLab::Memory.new.to_h
434
- # => { data: {}, results: [], messages: [], custom: {} } -- no :session_id
435
- ```
436
-
437
- Consumers must tolerate the missing key. `custom` is always present (it is
438
- `{}` rather than nil when there are no custom keys).
439
-
440
- ### to_json
441
-
442
- ```ruby
443
- memory.to_json # => String
444
- ```
445
-
446
- Serializes `to_h`.
447
-
448
- ### from_hash
449
-
450
- ```ruby
451
- memory = RobotLab::Memory.from_hash(hash)
452
- ```
453
-
454
- Reconstructs `data`, `results`, `messages`, and `session_id`, then re-applies
455
- every entry from `custom`. A fresh cache is created; subscriptions, backend
456
- choice, and `network_name` are **not** restored.
457
-
458
- ### clone / dup
459
-
460
- ```ruby
461
- new_memory = memory.clone
462
- new_memory = memory.dup # alias for clone
463
- ```
464
-
465
- Deep copy of `data` plus copies of `results`, `messages`, `session_id`, and all
466
- non-reserved keys. The `enable_cache` setting and `network_name` are preserved.
467
- Subscriptions are **not** copied — the clone starts with none. Copying custom
468
- keys uses the internal non-reactive setter, so no notifications fire.
469
-
470
- ## Examples
471
-
472
- ### Basic Usage
473
-
474
- ```ruby
475
- robot.memory.set(:user_name, "Alice")
476
- robot.memory.set(:order_count, 5)
477
-
478
- name = robot.memory.get(:user_name) # => "Alice"
479
- count = robot.memory.get(:order_count) # => 5
480
- ```
481
-
482
- ### Bracket Access
483
-
484
- ```ruby
485
- robot.memory[:user_id] = 123
486
- robot.memory[:user_id] # => 123
487
- ```
488
-
489
- ### Storing Objects
490
-
491
- ```ruby
492
- robot.memory.set(:user, {
493
- id: 123,
494
- name: "Alice",
495
- plan: "pro"
496
- })
497
-
498
- user = robot.memory.get(:user)
499
- user[:plan] # => "pro"
500
- ```
501
-
502
- ### Blocking Reads (Network Parallel Execution)
503
-
504
- ```ruby
505
- # In robot A (writer)
506
- network.memory.set(:sentiment, { score: 0.8, confidence: 0.95 })
507
-
508
- # In robot B (reader, may run concurrently)
509
- result = network.memory.get(:sentiment, wait: true) # Block indefinitely
510
- result = network.memory.get(:sentiment, wait: 30) # Block up to 30s, then raise
511
-
512
- # Multiple keys — the timeout applies PER MISSING KEY, so this can block 180s
513
- results = network.memory.get(:sentiment, :entities, :keywords, wait: 60)
514
- # => { sentiment: {...}, entities: [...], keywords: [...] }
515
-
516
- # Treat a timeout as "not available"
517
- sentiment = begin
518
- network.memory.get(:sentiment, wait: 30)
519
- rescue RobotLab::AwaitTimeout
520
- nil
521
- end
522
- ```
523
-
524
- ### Reactive Subscriptions
525
-
526
- ```ruby
527
- # Subscribe to a key
528
- memory.subscribe(:raw_data) do |change|
529
- enriched = enrich(change.value)
530
- memory.set(:enriched, enriched)
531
- end
532
-
533
- # Subscribe with pattern
534
- memory.subscribe_pattern("user:*") do |change|
535
- puts "User key #{change.key} updated by #{change.writer}"
536
- end
537
- ```
538
-
539
- ### Cross-Robot Communication via Network Memory
540
-
541
- ```ruby
542
- # In classifier robot
543
- network.memory.set(:intent, "billing")
544
- network.memory.set(:entities, ["order", "refund"])
545
-
546
- # In handler robot
547
- intent = network.memory.get(:intent)
548
- entities = network.memory.get(:entities)
549
- ```
550
-
551
- ### Data Proxy
552
-
553
- ```ruby
554
- memory = RobotLab.create_memory(
555
- data: { user: { name: "Alice", plan: "pro" } }
556
- )
557
-
558
- memory.data[:user][:name] # => "Alice"
559
- memory.data.to_h # => { user: { name: "Alice", plan: "pro" } }
560
- ```
561
-
562
- ### Serialization
563
-
564
- ```ruby
565
- # Save memory
566
- json = memory.to_json
567
- File.write("memory.json", json)
568
-
569
- # Restore memory
570
- data = JSON.parse(File.read("memory.json"))
571
- memory = Memory.from_hash(data)
572
- ```
573
-
574
- ## See Also
575
-
576
- - [Memory Guide](../../guides/memory.md)
577
- - [StateProxy](state.md) — the wrapper returned by `memory.data`
578
- - [State Management Architecture](../../architecture/state-management.md)