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,252 +0,0 @@
1
- # Architecture Overview
2
-
3
- RobotLab is designed around a few core architectural principles that enable flexible, composable AI workflows.
4
-
5
- ## Design Philosophy
6
-
7
- ### 1. Separation of Concerns
8
-
9
- Each component has a single, well-defined responsibility:
10
-
11
- - **Robot**: LLM-powered agent (subclass of `RubyLLM::Agent`) with personality, tools, and memory
12
- - **Network**: Orchestrates robot execution as a DAG pipeline via SimpleFlow
13
- - **Memory**: Reactive key-value store for robot and network data
14
- - **Tool**: Provides external capabilities to robots (inherits from `RubyLLM::Tool`)
15
- - **Task**: Wraps a robot for pipeline execution with per-task configuration
16
-
17
- ### 2. Composability
18
-
19
- Components are designed to be mixed and matched:
20
-
21
- - Robots can be used standalone or within networks
22
- - Tools can be shared across robots or scoped per-robot via `local_tools:`
23
- - Networks define DAG pipelines with sequential, parallel, and optional execution
24
- - Memory can be standalone (per-robot) or shared (per-network)
25
- - `with_*` methods return `self` for fluent chaining
26
-
27
- ### 3. Provider Agnostic
28
-
29
- RobotLab abstracts away LLM provider differences through RubyLLM:
30
-
31
- - Unified interface across Anthropic, OpenAI, Gemini, DeepSeek, Mistral, and others
32
- - Consistent tool calling interface
33
- - Automatic provider detection from model names
34
- - Easy switching between providers via configuration
35
-
36
- ## System Architecture
37
-
38
- ```mermaid
39
- graph TB
40
- subgraph "Application Layer"
41
- A[Your Application]
42
- end
43
-
44
- subgraph "RobotLab Core"
45
- B[Network]
46
- C[Task]
47
- D[Robot < RubyLLM::Agent]
48
- E[Memory]
49
- F[RobotResult]
50
- end
51
-
52
- subgraph "Configuration"
53
- G[Config < MywayConfig::Base]
54
- R[RunConfig]
55
- end
56
-
57
- subgraph "Cross-Cutting"
58
- HK[Hooks<br/>HookRegistry x3]
59
- end
60
-
61
- subgraph "Integration Layer"
62
- H[MCP Client]
63
- I[Tools &lt; RubyLLM::Tool]
64
- J[Templates / prompt_manager]
65
- SK[AgentSkills<br/>+ optional Sandbox]
66
- end
67
-
68
- subgraph "Execution Layer"
69
- K[SimpleFlow::Pipeline]
70
- L[RubyLLM Chat]
71
- TB[TypedBus + BusPoller]
72
- end
73
-
74
- subgraph "Provider Layer"
75
- M[Anthropic]
76
- N[OpenAI]
77
- O[Gemini]
78
- P[MCP Servers]
79
- end
80
-
81
- A --> B
82
- A --> D
83
- B --> C
84
- C --> D
85
- B --> K
86
- B --> E
87
- B --> TB
88
- D --> E
89
- D --> L
90
- D --> TB
91
- D --> H
92
- D --> I
93
- D --> J
94
- D --> SK
95
- D --> F
96
- SK --> I
97
- G --> R
98
- R --> B
99
- R --> C
100
- R --> D
101
- G --> D
102
- G --> L
103
- HK -.wraps.-> B
104
- HK -.wraps.-> C
105
- HK -.wraps.-> D
106
- HK -.wraps.-> I
107
- L --> M
108
- L --> N
109
- L --> O
110
- H --> P
111
- ```
112
-
113
- The dotted `wraps` edges are the hook system: `Hooks.run` brackets every network
114
- run, task, robot run, LLM generation, tool call, compaction, and `learn` call. It
115
- is the framework's extension seam — the extension gems (`robot_lab-audit`,
116
- `robot_lab-durable`, …) attach here rather than subclassing core objects. See the
117
- [Hooks API](../api/hooks.md).
118
-
119
- ### Robot subsystems
120
-
121
- A `Robot` composes several small collaborators, each documented on the
122
- [Support](../api/support.md) and [Skills](../api/skills.md) API pages:
123
-
124
- ```mermaid
125
- graph LR
126
- D[Robot]
127
-
128
- D --> BM[BusMessaging<br/>RobotMessage envelopes]
129
- BM --> BP[BusPoller<br/>per-robot serialization]
130
- D --> BD[Budget::Ledger<br/>token_budget / cost_budget]
131
- D --> DL[DoomLoopDetector<br/>always installed per run]
132
- D --> HC[HistoryCompressor<br/>compress_history / auto_compact]
133
- D --> DF[DelegationFuture<br/>delegate async: true]
134
- D --> HS[HistorySearch<br/>search_history]
135
- D --> ASM[AgentSkillMatching<br/>prepended around run]
136
-
137
- ASM --> AS[AgentSkill<br/>SKILL.md bundle]
138
- AS --> CAT[AgentSkillCatalog]
139
- AS --> CAP[Capabilities]
140
- AS --> ST[ScriptTool]
141
- ST --> EX{ScriptTool.executor}
142
- EX -. "installed by<br/>robot_lab-sandbox" .-> SB[Sandbox<br/>Seatbelt or Null]
143
- CAP -. "used if installed" .-> SB
144
-
145
- HC --> TA[TextAnalysis<br/>TF / TF-IDF]
146
- HS --> TA
147
- CV[Convergence] --> TA
148
- SD[MCP::ServerDiscovery] --> TA
149
- ```
150
-
151
- `TextAnalysis` is the shared floor under every similarity feature — history
152
- compression, history search, convergence detection, and MCP server discovery all
153
- route through it, which is why they share the one optional `classifier`
154
- dependency.
155
-
156
- ## Core Components
157
-
158
- | Component | Description | Documentation |
159
- |-----------|-------------|---------------|
160
- | **Robot** | LLM agent (subclass of `RubyLLM::Agent`) with template-based prompts, tools, and memory | [Core Concepts](core-concepts.md) |
161
- | **Network** | Orchestrates multiple robots as a SimpleFlow pipeline | [Network Orchestration](network-orchestration.md) |
162
- | **Memory** | Reactive key-value store with pub/sub and blocking reads | [Memory Management](state-management.md) |
163
- | **Task** | Wraps a robot for pipeline execution with per-task config | [Network Orchestration](network-orchestration.md) |
164
- | **RobotResult** | Captures LLM output, tool calls, and metadata from a run | [Message Flow](message-flow.md) |
165
- | **RunConfig** | Per-run settings object (LLM fields, `mcp`/`tools`, callbacks, infrastructure) that cascades global → network → task → robot | [Core Concepts](core-concepts.md) |
166
- | **Config** | MywayConfig-based global configuration with env var and file support | [Configuration](#configuration) |
167
- | **Hook** | Handler base class for the seven hook families — the framework's extension seam | [Hooks API](../api/hooks.md) |
168
- | **AgentSkill** | A `SKILL.md` bundle whose instructions and `scripts/` become prompt text and tools | [Skills API](../api/skills.md) |
169
- | **Sandbox** | Opt-in OS-level confinement (macOS Seatbelt) for skill scripts, derived from `Capabilities`; ships in the optional `robot_lab-sandbox` gem, not core | [Skills API](../api/skills.md) |
170
- | **RobotMessage** | Immutable envelope for TypedBus inter-robot messaging, serialized per robot by `BusPoller` | [Support API](../api/support.md) |
171
- | **Budget::Ledger** | Thread-safe reserve/reconcile ledger behind `token_budget` / `cost_budget` | [Support API](../api/support.md) |
172
-
173
- ## Configuration
174
-
175
- Global configuration is a MywayConfig subclass (`Config < MywayConfig::Base`). It is loaded from multiple sources in priority order:
176
-
177
- 1. **Bundled defaults** (`lib/robot_lab/config/defaults.yml`)
178
- 2. **Environment overrides** (development, test, production)
179
- 3. **XDG user config** (`~/.config/robot_lab/robot_lab.yml` — the filename repeats the app name; `config.yml` is never read)
180
- 4. **Project config** (`./config/robot_lab.yml` — the only file with an ERB pass)
181
- 5. **Environment variables** (`ROBOT_LAB_*` prefix, double underscore for nesting)
182
- 6. **Constructor params**
183
-
184
- Top-level wrappers behave differently per file. In the **XDG user config**, a section named for the current environment is honoured — the loader looks for `parsed.key?(env)` (`Anyway::Settings.current_environment`, else `RAILS_ENV`, else `RACK_ENV`, else `"development"`) and falls back to the root when absent — so both `development:` and a flat file work there. A `defaults:` wrapper is not an environment name, so it is ignored. The **project config** must be flat outside Rails (all wrappers ignored); under Rails, `anyway_config` sets `current_environment` to `Rails.env` and the project file becomes environmental, so a flat file is ignored and keys must sit under `development:` / `test:` / `production:`. See [Core Concepts](core-concepts.md#configuration) for details.
185
-
186
- ```ruby
187
- # Access configuration
188
- RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
189
- RobotLab.config.ruby_llm.request_timeout #=> 120
190
-
191
- # Block form (yields the same Config object)
192
- RobotLab.configure do |c|
193
- c.logger = Logger.new($stdout)
194
- end
195
- ```
196
-
197
- ## Data Flow
198
-
199
- 1. **Input**: User calls `robot.run("message")` or `network.run(message: "...")`
200
- 2. **Memory**: Robot resolves active memory (standalone or network-shared)
201
- 3. **MCP**: Robot resolves MCP servers from hierarchical config and connects clients. `run` defaults to `mcp: :none`, so a plain `run` connects nothing
202
- 4. **Tools**: Robot resolves and filters tools from hierarchical config. `run` defaults to `tools: :none`, so a plain `run` sends the LLM zero tools; pass `tools: :inherit` to send the robot's attached tools
203
- 5. **Execution**: Robot delegates to `Agent#ask` which calls `@chat.ask` on RubyLLM
204
- 6. **Tool Loop**: LLM may invoke tools; RubyLLM handles the tool call/result loop
205
- 7. **Result**: Robot builds and returns a `RobotResult`
206
- 8. **Network**: If in a network, result flows to dependent tasks via SimpleFlow
207
-
208
- ## Key Patterns
209
-
210
- ### Factory Methods
211
-
212
- Robots and networks are created via factory methods on the `RobotLab` module:
213
-
214
- ```ruby
215
- robot = RobotLab.build(
216
- name: "assistant",
217
- template: :assistant,
218
- context: { tone: "friendly" }
219
- )
220
-
221
- network = RobotLab.create_network(name: "pipeline") do
222
- task :analyst, analyst_robot, depends_on: :none
223
- task :writer, writer_robot, depends_on: [:analyst]
224
- end
225
- ```
226
-
227
- ### Fluent Chaining
228
-
229
- `with_*` methods on Robot delegate to the underlying `@chat` and return `self`:
230
-
231
- ```ruby
232
- robot = RobotLab.build(name: "bot")
233
- .with_instructions("Be concise.")
234
- .with_temperature(0.3)
235
- .with_model("gpt-4o")
236
- ```
237
-
238
- ### Hierarchical Configuration
239
-
240
- Tools and MCP servers use hierarchical resolution: `run()/task > robot build-time > network > global config`. Values can be `:none`, `:inherit`, or an explicit array (which filters the already-attached tools by name — entries must match how the tool was attached, class or instance). `run()` defaults both to `:none`, so the runtime level is an explicit "send nothing" unless you override it.
241
-
242
- ### SimpleFlow Pipeline
243
-
244
- Networks are thin wrappers around `SimpleFlow::Pipeline`. Each robot is wrapped in a `Task` that implements the `call(result)` interface. Tasks define dependencies (`:none`, `[:task_names]`, or `:optional`) to control execution order.
245
-
246
- ## Next Steps
247
-
248
- - [Core Concepts](core-concepts.md) - Deep dive into robots and tools
249
- - [Robot Execution](robot-execution.md) - How robots process messages
250
- - [Network Orchestration](network-orchestration.md) - Multi-robot workflows
251
- - [Memory Management](state-management.md) - Managing memory and reactive features
252
- - [Message Flow](message-flow.md) - How messages move through the system
@@ -1,366 +0,0 @@
1
- # Message Flow
2
-
3
- This page explains how messages move through RobotLab, from user input to LLM response.
4
-
5
- ## Message Types
6
-
7
- `RobotLab::Message` is the abstract base for three conversation message classes. `ToolMessage` is a plain value object that they reference, and two further message classes — `UserMessage` and `RobotMessage` — live outside the hierarchy entirely:
8
-
9
- ```mermaid
10
- classDiagram
11
- class Message {
12
- <<abstract>>
13
- +type: String
14
- +role: String
15
- +content
16
- +stop_reason: String
17
- +text?() bool
18
- +tool_call?() bool
19
- +tool_result?() bool
20
- +system?() bool
21
- +user?() bool
22
- +assistant?() bool
23
- +stopped?() bool
24
- +tool_stop?() bool
25
- +to_h() Hash
26
- }
27
-
28
- class TextMessage {
29
- +content: String
30
- }
31
-
32
- class ToolMessage {
33
- <<PORO>>
34
- +id: String
35
- +name: String
36
- +input: Hash
37
- +to_h() Hash
38
- +to_json() String
39
- }
40
-
41
- class ToolCallMessage {
42
- +tools: Array~ToolMessage~
43
- }
44
-
45
- class ToolResultMessage {
46
- +tool: ToolMessage
47
- +content: Hash
48
- +success?() bool
49
- +error?() bool
50
- +data()
51
- +error()
52
- }
53
-
54
- Message <|-- TextMessage
55
- Message <|-- ToolCallMessage
56
- Message <|-- ToolResultMessage
57
- ToolMessage -- ToolCallMessage
58
- ToolMessage -- ToolResultMessage
59
- ```
60
-
61
- `ToolMessage` does **not** inherit from `Message` — its superclass is `Object`. It has no `type`, `role`, or predicate methods; only `id`, `name`, `input`, `to_h`, and `to_json`. The `tool_call?` predicate lives on `Message`, so it answers for `ToolCallMessage`, not for `ToolMessage`.
62
-
63
- The two classes outside the hierarchy:
64
-
65
- | Class | Role |
66
- |-------|------|
67
- | `UserMessage` | Envelope for user input carrying `session_id`, an extra `system_prompt`, `metadata`, `id`, `created_at`. `UserMessage.from` normalizes a String/Hash/TextMessage; `#to_message` converts to a `TextMessage` |
68
- | `RobotMessage` | Immutable `Data.define(:id, :from, :content, :in_reply_to)` envelope for robot-to-robot traffic on the message bus, with `#key` (`"from:id"`) and `#reply?` |
69
-
70
- ### TextMessage
71
-
72
- Regular text content from users or assistants:
73
-
74
- ```ruby
75
- TextMessage.new(
76
- role: "user",
77
- content: "What's the weather in Paris?"
78
- )
79
-
80
- TextMessage.new(
81
- role: "assistant",
82
- content: "The weather in Paris is sunny and 22 degrees C.",
83
- stop_reason: "stop"
84
- )
85
- ```
86
-
87
- ### ToolMessage
88
-
89
- A standalone value object (not a `Message`) representing a tool invocation and its parameters:
90
-
91
- ```ruby
92
- tool = ToolMessage.new(
93
- id: "tool_123",
94
- name: "get_weather",
95
- input: { location: "Paris" }
96
- )
97
-
98
- tool.to_h #=> { type: "tool", id: "tool_123", name: "get_weather", input: { location: "Paris" } }
99
- ```
100
-
101
- Note that the `type: "tool"` key appears only in `to_h`; there is no `type` reader and no predicate methods.
102
-
103
- ### ToolCallMessage
104
-
105
- LLM's request to execute one or more tools:
106
-
107
- ```ruby
108
- ToolCallMessage.new(
109
- role: "assistant",
110
- tools: [
111
- ToolMessage.new(id: "call_1", name: "get_weather", input: { location: "Paris" })
112
- ],
113
- stop_reason: "tool"
114
- )
115
- ```
116
-
117
- The signature is `initialize(role:, tools:, stop_reason: nil)` — `role` and
118
- `tools` are required and there is **no** `content:` keyword. Passing one raises
119
- `ArgumentError`.
120
-
121
- ### ToolResultMessage
122
-
123
- Result from tool execution:
124
-
125
- ```ruby
126
- ToolResultMessage.new(
127
- tool: tool_message,
128
- content: { data: { temp: 22, condition: "sunny" } }
129
- )
130
- ```
131
-
132
- ## Message Flow: Standalone Robot
133
-
134
- The primary execution path is `robot.run("message")`:
135
-
136
- ```mermaid
137
- sequenceDiagram
138
- participant User
139
- participant Robot
140
- participant Memory
141
- participant MCP
142
- participant Tools
143
- participant Agent
144
- participant Chat
145
- participant LLM
146
-
147
- User->>Robot: robot.run("message")
148
- Robot->>Memory: resolve_active_memory
149
- Memory-->>Robot: active memory
150
-
151
- Robot->>MCP: resolve_mcp_hierarchy
152
- MCP-->>Robot: resolved MCP config
153
- Robot->>Robot: ensure_mcp_clients
154
-
155
- Robot->>Tools: resolve_tools_hierarchy
156
- Tools-->>Robot: filtered + capped tools
157
- Robot->>Chat: @chat.with_tools(*filtered, replace: true)
158
-
159
- Robot->>Agent: ask("message")
160
- Agent->>Chat: @chat.ask("message")
161
- Chat->>LLM: Provider API call
162
-
163
- loop Tool Loop (handled by RubyLLM)
164
- LLM-->>Chat: Tool call response
165
- Chat->>Tools: Execute tool
166
- Tools-->>Chat: Tool result
167
- Chat->>LLM: Send tool result
168
- end
169
-
170
- LLM-->>Chat: Final response
171
- Chat-->>Agent: RubyLLM::Response
172
- Agent-->>Robot: response
173
-
174
- Robot->>Robot: build_result(response, memory)
175
- Robot-->>User: RobotResult
176
- ```
177
-
178
- ### Step-by-Step
179
-
180
- 1. **`robot.run("message")`**: Entry point. Accepts a positional string argument.
181
-
182
- 2. **Resolve Memory**: Determines which memory to use:
183
- - `network_memory` if provided (network execution)
184
- - `network.memory` if in a network context
185
- - `robot.memory` (standalone, the default)
186
-
187
- 3. **Merge Runtime Memory**: If a `memory:` keyword argument is passed, it is merged into the active memory.
188
-
189
- 4. **Set Current Writer**: Sets `memory.current_writer = robot.name` so subscription callbacks know which robot wrote a value.
190
-
191
- 5. **Resolve MCP Hierarchy**: Resolves MCP server configuration through the hierarchy: `runtime (run/task) > robot build-time > network > global config`. `run` defaults `mcp:` to `:none`, so nothing is connected unless you pass `mcp: :inherit` or an explicit list.
192
-
193
- 6. **Ensure MCP Clients**: Initializes or updates MCP client connections and discovers tools from them. Connection failures are logged and recorded in `failed_mcp_server_names`, never raised.
194
-
195
- 7. **Resolve Tools Hierarchy**: Resolves which tools are available through the same hierarchy. `run` defaults `tools:` to `:none`, which means "send zero tools this turn" — pass `tools: :inherit` to send the robot's attached tools.
196
-
197
- 8. **Filter Tools**: Filters by the resolved allowlist, clamps to `max_tools` (128 by default), and applies the set with `@chat.with_tools(*filtered, replace: true)` so the persistent chat holds exactly this turn's tools.
198
-
199
- 9. **Agent#ask**: Delegates to the parent class `RubyLLM::Agent#ask`, which calls `@chat.ask(message)`.
200
-
201
- 10. **LLM Interaction**: RubyLLM handles the provider-specific API call, including the tool call/result loop.
202
-
203
- 11. **Build Result**: Wraps the LLM response in a `RobotResult` containing output messages, tool calls, and metadata.
204
-
205
- 12. **Return**: Returns the `RobotResult` to the caller.
206
-
207
- ## Message Flow: Network Execution
208
-
209
- When running through a network, the flow adds pipeline orchestration:
210
-
211
- ```mermaid
212
- sequenceDiagram
213
- participant User
214
- participant Network
215
- participant Pipeline
216
- participant Task
217
- participant Robot
218
- participant LLM
219
-
220
- User->>Network: network.run(message: "...")
221
- Network->>Network: Inject network_memory into run_context
222
- Network->>Pipeline: SimpleFlow::Pipeline.call_parallel(initial_result)
223
-
224
- loop For each ready task
225
- Pipeline->>Task: task.call(result)
226
- Task->>Task: Deep merge task context with run_params
227
- Task->>Robot: robot.call(enhanced_result)
228
- Robot->>Robot: extract_run_context(result)
229
- Robot->>Robot: run(message, network_memory: ...)
230
- Robot->>LLM: Agent#ask -> @chat.ask
231
- LLM-->>Robot: Response
232
- Robot-->>Task: result.with_context(:name, robot_result).continue(robot_result)
233
- Task-->>Pipeline: SimpleFlow::Result
234
- end
235
-
236
- Pipeline-->>Network: Final SimpleFlow::Result
237
- Network-->>User: result
238
- ```
239
-
240
- ### Key Points
241
-
242
- - **Network creates initial result**: `SimpleFlow::Result.new(run_context, context: { run_params: run_context })`, after injecting `network_memory`, `network`, and (when non-empty) `network_config` into the run context, all wrapped in the `:network_run` hook
243
- - **Task wraps robot**: Each `Task` runs the `:task` hook and deep-merges its own context, `mcp`, `tools`, `memory`, and `config` into the run params before delegating to the robot
244
- - **Robot extracts context**: `extract_run_context(result)` pulls the message, MCP, tools, memory, network, and task out of the SimpleFlow result and passes them to `run` as keyword arguments
245
- - **Shared memory**: All robots use `network.memory` during network execution
246
- - **Result accumulation**: Each robot stores its `RobotResult` in `result.context[:robot_name]` — the key comes from `@name.to_sym` in `Robot#call`, so it is the **robot's** name, not the task name. They match only when the two are spelled the same way
247
-
248
- ## RobotResult
249
-
250
- The return value of `robot.run("message")`:
251
-
252
- ```ruby
253
- result = robot.run("What is Ruby?")
254
-
255
- result.last_text_content #=> "Ruby is a dynamic programming language..."
256
- result.reply #=> alias for last_text_content
257
- result.has_tool_calls? #=> false
258
- result.robot_name #=> "assistant"
259
- result.output #=> [TextMessage(role: "assistant", content: "...")]
260
- result.tool_calls #=> []
261
- result.stop_reason #=> nil (always — see below)
262
- result.created_at #=> Time
263
- result.id #=> "uuid"
264
- result.checksum #=> "sha256-hex"
265
- result.input_tokens #=> 42
266
- result.output_tokens #=> 128
267
- result.duration #=> Float, nil
268
- ```
269
-
270
- `stop_reason` is always `nil` on a `Robot#run` result: `build_result` reads it as `response.respond_to?(:stop_reason) ? response.stop_reason : nil`, and `RubyLLM::Message` does not define the method. It is consequently dropped from `export`, and `result.stopped?` reduces to `!result.has_tool_calls?`.
271
-
272
- `output` always holds exactly one synthesized `TextMessage` built from the final response text — it is not a transcript of the turn, and it never contains `ToolCallMessage` or `ToolResultMessage` entries. `tool_calls` is read off the final assistant message, which carries no tool calls once RubyLLM's tool loop has finished, so it is effectively always empty. To observe tool activity, use the `on_tool_call` / `on_tool_result` callbacks or the tool hooks.
273
-
274
- ### Result Serialization
275
-
276
- ```ruby
277
- # Export for persistence (excludes debug fields)
278
- hash = result.export
279
-
280
- # Full hash including debug fields
281
- hash = result.to_h
282
-
283
- # JSON
284
- json = result.to_json
285
-
286
- # Reconstruct from hash
287
- result = RobotResult.from_hash(hash)
288
- ```
289
-
290
- ## Message Predicates
291
-
292
- These are defined on `Message`, so every `TextMessage`, `ToolCallMessage`, and `ToolResultMessage` responds to all of them. `ToolMessage` responds to none of them:
293
-
294
- ```ruby
295
- message.text? # Is it a TextMessage?
296
- message.tool_call? # Is it a ToolCallMessage?
297
- message.tool_result? # Is it a ToolResultMessage?
298
-
299
- message.user? # Is role "user"?
300
- message.assistant? # Is role "assistant"?
301
- message.system? # Is role "system"?
302
-
303
- message.stopped? # Is stop_reason "stop"?
304
- message.tool_stop? # Is stop_reason "tool"?
305
- ```
306
-
307
- Valid values are constrained at construction: `type` must be one of `text`, `tool_call`, `tool_result`; `role` one of `system`, `user`, `assistant`, `tool_result`; `stop_reason` one of `tool`, `stop`. Anything else raises `ArgumentError`.
308
-
309
- ## Creating Messages
310
-
311
- ### From Strings
312
-
313
- ```ruby
314
- TextMessage.new(role: "user", content: "Hello")
315
- ```
316
-
317
- ### From Hashes
318
-
319
- ```ruby
320
- Message.from_hash(
321
- type: "text",
322
- role: "user",
323
- content: "Hello"
324
- )
325
- ```
326
-
327
- ## Serialization
328
-
329
- Messages can be serialized:
330
-
331
- ```ruby
332
- # To hash
333
- hash = message.to_h
334
- #=> { type: "text", role: "user", content: "Hello" }
335
-
336
- # To JSON
337
- json = message.to_json
338
-
339
- # From hash
340
- message = Message.from_hash(hash)
341
- ```
342
-
343
- ## Template Resolution
344
-
345
- When a robot has a template, it is resolved at build time via prompt_manager:
346
-
347
- ```ruby
348
- robot = RobotLab.build(
349
- name: "helper",
350
- template: :helper,
351
- context: { tone: "friendly" }
352
- )
353
- ```
354
-
355
- The template resolution process:
356
- 1. `PM.parse(:helper)` loads the template file from the configured prompts directory
357
- 2. Non-LLM front matter (`robot_name`, `description`, `tools`, `mcp`, `skills`) is applied to the robot, filling in only what the constructor did not provide
358
- 3. The LLM front-matter keys become a `RunConfig` that the robot's own `@config` merges over — front matter is the base, constructor kwargs win — and `apply_to` dispatches `chat.with_<field>` for each field the chat supports. In practice only `model` and `temperature` reach the chat this way; `top_p`, `top_k`, `max_tokens`, `presence_penalty`, `frequency_penalty`, and `stop` are parsed and silently dropped because `RubyLLM::Chat` defines no `with_` method for them. Set those via constructor kwargs instead
359
- 4. The template body is rendered with ERB (`<%= var %>`; `{{ var }}` is not interpolated) and set as system instructions via `@chat.with_instructions(rendered)`
360
-
361
- If both `template:` and `system_prompt:` are provided, the system prompt is appended to the rendered template, producing one combined system message — and it is re-appended on every template re-render, so supplying run-time context never silently drops it.
362
-
363
- ## Next Steps
364
-
365
- - [Memory Management](state-management.md) - How memory stores conversation data
366
- - [Network Orchestration](network-orchestration.md) - Multi-robot pipeline execution