robot_lab 0.2.1 → 0.2.7
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.
- checksums.yaml +4 -4
- data/.envrc +1 -0
- data/.loki +60 -0
- data/.quality/reek_baseline.txt +43 -0
- data/.rubocop.yml +5 -167
- data/CHANGELOG.md +54 -0
- data/README.md +185 -64
- data/Rakefile +28 -12
- data/docs/api/core/index.md +41 -14
- data/docs/api/core/memory.md +247 -29
- data/docs/api/core/network.md +285 -33
- data/docs/api/core/result.md +120 -32
- data/docs/api/core/robot.md +636 -60
- data/docs/api/core/state.md +87 -197
- data/docs/api/core/tool.md +165 -20
- data/docs/api/errors.md +152 -14
- data/docs/api/hooks.md +469 -0
- data/docs/api/index.md +83 -7
- data/docs/api/mcp/client.md +129 -35
- data/docs/api/mcp/index.md +164 -23
- data/docs/api/mcp/server.md +27 -3
- data/docs/api/mcp/transports.md +94 -22
- data/docs/api/messages/index.md +26 -3
- data/docs/api/messages/text-message.md +33 -11
- data/docs/api/messages/tool-call-message.md +27 -4
- data/docs/api/messages/tool-result-message.md +23 -4
- data/docs/api/messages/user-message.md +45 -8
- data/docs/api/skills.md +519 -0
- data/docs/api/streaming/context.md +28 -5
- data/docs/api/streaming/index.md +57 -11
- data/docs/api/support.md +846 -0
- data/docs/architecture/core-concepts.md +118 -30
- data/docs/architecture/index.md +86 -11
- data/docs/architecture/message-flow.md +66 -29
- data/docs/architecture/network-orchestration.md +153 -38
- data/docs/architecture/robot-execution.md +173 -91
- data/docs/architecture/state-management.md +31 -12
- data/docs/concepts.md +176 -21
- data/docs/examples/basic-chat.md +72 -19
- data/docs/examples/index.md +117 -31
- data/docs/examples/mcp-server.md +154 -45
- data/docs/examples/multi-robot-network.md +91 -21
- data/docs/examples/tool-usage.md +104 -37
- data/docs/getting-started/configuration.md +299 -93
- data/docs/getting-started/installation.md +53 -41
- data/docs/getting-started/quick-start.md +51 -6
- data/docs/guides/building-robots.md +296 -50
- data/docs/guides/creating-networks.md +220 -31
- data/docs/guides/hooks.md +1084 -0
- data/docs/guides/index.md +5 -0
- data/docs/guides/knowledge.md +37 -6
- data/docs/guides/mcp-integration.md +211 -44
- data/docs/guides/memory.md +103 -12
- data/docs/guides/observability.md +166 -51
- data/docs/guides/streaming.md +184 -125
- data/docs/guides/using-tools.md +300 -11
- data/docs/index.md +37 -5
- data/examples/01_simple_robot.rb +2 -2
- data/examples/02_tools.rb +14 -4
- data/examples/03_network.rb +23 -9
- data/examples/04_mcp.rb +11 -4
- data/examples/05_streaming.rb +8 -5
- data/examples/06_prompt_templates.rb +13 -9
- data/examples/07_network_memory.rb +5 -5
- data/examples/08_llm_config.rb +20 -15
- data/examples/09_chaining.rb +4 -4
- data/examples/11_network_introspection.rb +4 -4
- data/examples/12_message_bus.rb +2 -2
- data/examples/13_spawn.rb +2 -2
- data/examples/14_rusty_circuit/README.md +1 -0
- data/examples/14_rusty_circuit/comic.rb +7 -3
- data/examples/14_rusty_circuit/display.rb +14 -0
- data/examples/14_rusty_circuit/heckler.rb +8 -6
- data/examples/14_rusty_circuit/open_mic.rb +17 -6
- data/examples/14_rusty_circuit/scout.rb +17 -10
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +14 -10
- data/examples/15_memory_network_and_bus/linux_writer.rb +2 -2
- data/examples/15_memory_network_and_bus/os_editor.rb +3 -1
- data/examples/15_memory_network_and_bus/os_writer.rb +4 -1
- data/examples/16_writers_room/writer.rb +22 -22
- data/examples/16_writers_room/writers_room.rb +2 -0
- data/examples/17_skills.rb +14 -13
- data/examples/18_rails/README.md +20 -1
- data/examples/18_rails/app/controllers/chat_controller.rb +5 -1
- data/examples/18_rails/app/jobs/robot_run_job.rb +11 -5
- data/examples/18_rails/app/robots/chat_robot.rb +11 -0
- data/examples/18_rails/config/initializers/robot_lab.rb +8 -0
- data/examples/19_token_tracking.rb +25 -9
- data/examples/20_circuit_breaker.rb +10 -7
- data/examples/21_learning_loop.rb +42 -16
- data/examples/22_context_compression.rb +23 -23
- data/examples/23_convergence.rb +24 -17
- data/examples/24_structured_delegation.rb +13 -8
- data/examples/25_history_search.rb +12 -8
- data/examples/27_incident_response/incident_response.rb +31 -13
- data/examples/28_mcp_discovery.rb +17 -13
- data/examples/29_ractor_tools.rb +4 -2
- data/examples/30_ractor_network.rb +22 -17
- data/examples/31_launch_assessment.rb +20 -9
- data/examples/32_newsletter_reader.rb +7 -2
- data/examples/33_stock_predictor.rb +34 -13
- data/examples/34_agentskills.rb +7 -3
- data/examples/35_hooks.rb +266 -0
- data/examples/README.md +203 -42
- data/examples/common.rb +79 -7
- data/examples/xyzzy.rb +97 -0
- data/lib/robot_lab/agent_skill.rb +5 -4
- data/lib/robot_lab/budget/ledger.rb +98 -0
- data/lib/robot_lab/capabilities.rb +84 -0
- data/lib/robot_lab/config/defaults.yml +10 -0
- data/lib/robot_lab/config.rb +14 -9
- data/lib/robot_lab/error.rb +34 -4
- data/lib/robot_lab/errors.rb +45 -0
- data/lib/robot_lab/hook.rb +79 -0
- data/lib/robot_lab/hook_context.rb +194 -0
- data/lib/robot_lab/hook_registry.rb +55 -0
- data/lib/robot_lab/hooks.rb +87 -0
- data/lib/robot_lab/mcp/connection_poller.rb +2 -2
- data/lib/robot_lab/names.rb +402 -0
- data/lib/robot_lab/narrator.rb +87 -0
- data/lib/robot_lab/network.rb +77 -20
- data/lib/robot_lab/robot/agent_skill_matching.rb +1 -3
- data/lib/robot_lab/robot/budget.rb +89 -0
- data/lib/robot_lab/robot/bus_messaging.rb +90 -18
- data/lib/robot_lab/robot/hooking.rb +56 -0
- data/lib/robot_lab/robot/mcp_management.rb +8 -2
- data/lib/robot_lab/robot/template_rendering.rb +18 -5
- data/lib/robot_lab/robot.rb +233 -89
- data/lib/robot_lab/run_config.rb +24 -5
- data/lib/robot_lab/runnable.rb +51 -0
- data/lib/robot_lab/sandbox/null.rb +13 -0
- data/lib/robot_lab/sandbox/seatbelt.rb +104 -0
- data/lib/robot_lab/sandbox.rb +52 -0
- data/lib/robot_lab/script_tool.rb +60 -2
- data/lib/robot_lab/task.rb +26 -20
- data/lib/robot_lab/tool.rb +52 -11
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +70 -15
- data/mkdocs.yml +7 -1
- metadata +41 -99
- data/examples/temp.md +0 -51
- data/site/404.html +0 -2300
- data/site/api/core/index.html +0 -2706
- data/site/api/core/memory/index.html +0 -3793
- data/site/api/core/network/index.html +0 -3500
- data/site/api/core/robot/index.html +0 -4566
- data/site/api/core/state/index.html +0 -3390
- data/site/api/core/tool/index.html +0 -3843
- data/site/api/index.html +0 -2635
- data/site/api/mcp/client/index.html +0 -3435
- data/site/api/mcp/index.html +0 -2783
- data/site/api/mcp/server/index.html +0 -3252
- data/site/api/mcp/transports/index.html +0 -3352
- data/site/api/messages/index.html +0 -2641
- data/site/api/messages/text-message/index.html +0 -3087
- data/site/api/messages/tool-call-message/index.html +0 -3159
- data/site/api/messages/tool-result-message/index.html +0 -3252
- data/site/api/messages/user-message/index.html +0 -3212
- data/site/api/streaming/context/index.html +0 -3282
- data/site/api/streaming/events/index.html +0 -3347
- data/site/api/streaming/index.html +0 -2738
- data/site/architecture/core-concepts/index.html +0 -3757
- data/site/architecture/index.html +0 -2797
- data/site/architecture/message-flow/index.html +0 -3238
- data/site/architecture/network-orchestration/index.html +0 -3433
- data/site/architecture/robot-execution/index.html +0 -3140
- data/site/architecture/state-management/index.html +0 -3498
- data/site/assets/css/custom.css +0 -56
- data/site/assets/images/favicon.png +0 -0
- data/site/assets/images/robot_lab.jpg +0 -0
- data/site/assets/javascripts/bundle.79ae519e.min.js +0 -16
- data/site/assets/javascripts/bundle.79ae519e.min.js.map +0 -7
- data/site/assets/javascripts/lunr/min/lunr.ar.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.da.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.de.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.du.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.el.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.es.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.fi.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.fr.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.he.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.hi.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.hu.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.hy.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.it.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.ja.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.jp.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.kn.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.ko.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.multi.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.nl.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.no.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.pt.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.ro.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.ru.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.sa.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.sv.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.ta.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.te.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.th.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.tr.min.js +0 -18
- data/site/assets/javascripts/lunr/min/lunr.vi.min.js +0 -1
- data/site/assets/javascripts/lunr/min/lunr.zh.min.js +0 -1
- data/site/assets/javascripts/lunr/tinyseg.js +0 -206
- data/site/assets/javascripts/lunr/wordcut.js +0 -6708
- data/site/assets/javascripts/workers/search.2c215733.min.js +0 -42
- data/site/assets/javascripts/workers/search.2c215733.min.js.map +0 -7
- data/site/assets/stylesheets/main.484c7ddc.min.css +0 -1
- data/site/assets/stylesheets/main.484c7ddc.min.css.map +0 -1
- data/site/assets/stylesheets/palette.ab4e12ef.min.css +0 -1
- data/site/assets/stylesheets/palette.ab4e12ef.min.css.map +0 -1
- data/site/concepts/index.html +0 -3455
- data/site/examples/basic-chat/index.html +0 -2880
- data/site/examples/index.html +0 -2907
- data/site/examples/mcp-server/index.html +0 -3018
- data/site/examples/multi-robot-network/index.html +0 -3131
- data/site/examples/rails-application/index.html +0 -3329
- data/site/examples/tool-usage/index.html +0 -3085
- data/site/getting-started/configuration/index.html +0 -3745
- data/site/getting-started/index.html +0 -2572
- data/site/getting-started/installation/index.html +0 -2981
- data/site/getting-started/quick-start/index.html +0 -2942
- data/site/guides/building-robots/index.html +0 -4290
- data/site/guides/creating-networks/index.html +0 -3858
- data/site/guides/index.html +0 -2586
- data/site/guides/mcp-integration/index.html +0 -3581
- data/site/guides/memory/index.html +0 -3586
- data/site/guides/rails-integration/index.html +0 -4019
- data/site/guides/streaming/index.html +0 -3157
- data/site/guides/using-tools/index.html +0 -3802
- data/site/index.html +0 -2671
- data/site/search/search_index.json +0 -1
- data/site/sitemap.xml +0 -183
- data/site/sitemap.xml.gz +0 -0
- data/site/tags.json +0 -1
|
@@ -8,13 +8,18 @@ Configuration values are loaded in priority order (lowest to highest):
|
|
|
8
8
|
|
|
9
9
|
1. **Bundled defaults** -- `lib/robot_lab/config/defaults.yml` (shipped with the gem)
|
|
10
10
|
2. **Environment-specific overrides** -- `development`, `test`, or `production` sections in defaults.yml
|
|
11
|
-
3. **User config file** -- `~/.config/robot_lab/
|
|
11
|
+
3. **User config file** -- `~/.config/robot_lab/robot_lab.yml`
|
|
12
12
|
4. **Project config file** -- `./config/robot_lab.yml`
|
|
13
13
|
5. **Environment variables** -- `ROBOT_LAB_*` prefix
|
|
14
14
|
6. **Runtime attributes** -- e.g., `RobotLab.config.logger = ...`
|
|
15
15
|
|
|
16
16
|
Higher-priority sources override lower-priority ones. You only need to set the values you want to change.
|
|
17
17
|
|
|
18
|
+
> [!IMPORTANT]
|
|
19
|
+
> The user config file is `~/.config/robot_lab/**robot_lab**.yml` — the filename
|
|
20
|
+
> repeats the application name. `~/.config/robot_lab/config.yml` is **never
|
|
21
|
+
> read**, and RobotLab gives no warning when it is present but ignored.
|
|
22
|
+
|
|
18
23
|
## Accessing Configuration
|
|
19
24
|
|
|
20
25
|
Use `RobotLab.config` to access the configuration object:
|
|
@@ -24,8 +29,7 @@ Use `RobotLab.config` to access the configuration object:
|
|
|
24
29
|
RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
|
|
25
30
|
RobotLab.config.ruby_llm.anthropic_api_key #=> "sk-ant-..."
|
|
26
31
|
RobotLab.config.ruby_llm.request_timeout #=> 120
|
|
27
|
-
RobotLab.config.
|
|
28
|
-
RobotLab.config.streaming_enabled #=> true
|
|
32
|
+
RobotLab.config.template_path #=> nil (auto-detected)
|
|
29
33
|
|
|
30
34
|
# Check the environment
|
|
31
35
|
RobotLab.config.development? #=> true/false
|
|
@@ -46,8 +50,7 @@ Environment variables use the `ROBOT_LAB_` prefix. Use double underscores (`__`)
|
|
|
46
50
|
|
|
47
51
|
```bash
|
|
48
52
|
# Top-level settings
|
|
49
|
-
export
|
|
50
|
-
export ROBOT_LAB_STREAMING_ENABLED=false
|
|
53
|
+
export ROBOT_LAB_TEMPLATE_PATH=prompts
|
|
51
54
|
|
|
52
55
|
# Nested ruby_llm settings (note the double underscore)
|
|
53
56
|
export ROBOT_LAB_RUBY_LLM__MODEL=claude-sonnet-4
|
|
@@ -63,71 +66,132 @@ The double underscore convention maps to nested YAML structure:
|
|
|
63
66
|
```
|
|
64
67
|
ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY --> ruby_llm.anthropic_api_key
|
|
65
68
|
ROBOT_LAB_RUBY_LLM__MODEL --> ruby_llm.model
|
|
66
|
-
|
|
69
|
+
ROBOT_LAB_TEMPLATE_PATH --> template_path
|
|
67
70
|
```
|
|
68
71
|
|
|
72
|
+
> [!WARNING]
|
|
73
|
+
> **Nested values arrive as Strings.** Only top-level keys are type-coerced.
|
|
74
|
+
> With `ROBOT_LAB_RUBY_LLM__REQUEST_TIMEOUT=180` set,
|
|
75
|
+
> `RobotLab.config.ruby_llm.request_timeout` returns the String `"180"`, not the
|
|
76
|
+
> Integer `180`. If a numeric nested setting matters to your code, set it in a
|
|
77
|
+
> config file instead, or coerce it yourself with `.to_i` / `.to_f`.
|
|
78
|
+
|
|
69
79
|
## Config Files
|
|
70
80
|
|
|
81
|
+
> [!WARNING]
|
|
82
|
+
> **A `defaults:` wrapper is always ignored in your own files.** The `defaults:`
|
|
83
|
+
> key you see inside the gem's bundled `lib/robot_lab/config/defaults.yml` applies
|
|
84
|
+
> **only to that bundled file**. Wrap your own settings in it and every value
|
|
85
|
+
> silently falls back to the default — no error, no warning.
|
|
86
|
+
>
|
|
87
|
+
> ```yaml
|
|
88
|
+
> # WRONG — silently ignored in a user or project config file
|
|
89
|
+
> defaults:
|
|
90
|
+
> template_path: prompts
|
|
91
|
+
>
|
|
92
|
+
> # RIGHT
|
|
93
|
+
> template_path: prompts
|
|
94
|
+
> ```
|
|
95
|
+
>
|
|
96
|
+
> Sections named for the **current environment** (`development:`, `test:`,
|
|
97
|
+
> `production:`) are a different matter, and the two files disagree:
|
|
98
|
+
>
|
|
99
|
+
> | File | Flat keys | `development:` section |
|
|
100
|
+
> |------|-----------|------------------------|
|
|
101
|
+
> | `~/.config/robot_lab/robot_lab.yml` | honoured | honoured |
|
|
102
|
+
> | `./config/robot_lab.yml` (no Rails) | honoured | ignored |
|
|
103
|
+
> | `./config/robot_lab.yml` (under Rails) | **ignored** | **honoured** |
|
|
104
|
+
>
|
|
105
|
+
> The user file checks for a section matching the current environment and falls
|
|
106
|
+
> back to the file root, so both forms work. The project file is read by
|
|
107
|
+
> anyway_config, which only treats it as environmental once
|
|
108
|
+
> `Anyway::Settings.current_environment` is set — which is precisely what Rails
|
|
109
|
+
> does (it sets it to `Rails.env`). See [Rails Integration](#rails-integration).
|
|
110
|
+
|
|
71
111
|
### Project Config
|
|
72
112
|
|
|
73
|
-
Create `./config/robot_lab.yml` in your project root
|
|
113
|
+
Create `./config/robot_lab.yml` in your project root. Outside Rails, write the
|
|
114
|
+
keys at the top level (under Rails they must be nested under the environment name
|
|
115
|
+
instead — see [Rails Integration](#rails-integration)):
|
|
74
116
|
|
|
75
117
|
```yaml title="config/robot_lab.yml"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
development:
|
|
86
|
-
ruby_llm:
|
|
87
|
-
log_level: :debug
|
|
88
|
-
|
|
89
|
-
test:
|
|
90
|
-
max_iterations: 3
|
|
91
|
-
streaming_enabled: false
|
|
92
|
-
ruby_llm:
|
|
93
|
-
model: claude-haiku-3-5
|
|
94
|
-
request_timeout: 30
|
|
95
|
-
max_retries: 1
|
|
96
|
-
|
|
97
|
-
production:
|
|
98
|
-
max_iterations: 20
|
|
99
|
-
ruby_llm:
|
|
100
|
-
request_timeout: 180
|
|
101
|
-
max_retries: 5
|
|
102
|
-
log_level: :warn
|
|
118
|
+
ruby_llm:
|
|
119
|
+
anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
|
|
120
|
+
model: claude-sonnet-4
|
|
121
|
+
request_timeout: 120
|
|
122
|
+
max_retries: 3
|
|
123
|
+
log_level: info
|
|
124
|
+
|
|
125
|
+
template_path: prompts
|
|
103
126
|
```
|
|
104
127
|
|
|
105
|
-
|
|
106
|
-
|
|
128
|
+
> [!NOTE]
|
|
129
|
+
> **ERB works here, and only here.** The project config file is read through ERB,
|
|
130
|
+
> so `<%= ENV['ANTHROPIC_API_KEY'] %>` is expanded before the YAML is parsed. The
|
|
131
|
+
> user config file described below is **not** — see the warning there.
|
|
132
|
+
|
|
133
|
+
> [!WARNING]
|
|
134
|
+
> **No YAML symbols in the project config file.** It is parsed with an empty
|
|
135
|
+
> permitted-classes list, so `log_level: :info` raises
|
|
136
|
+
> `Psych::DisallowedClass: Tried to load unspecified class: Symbol` and your
|
|
137
|
+
> application fails to boot. Write the plain string `log_level: info`. (Symbols
|
|
138
|
+
> *are* permitted in the bundled `defaults.yml` and in the user config file,
|
|
139
|
+
> which is why you will see `:debug` there.)
|
|
107
140
|
|
|
108
141
|
### User Config
|
|
109
142
|
|
|
110
|
-
Create `~/.config/robot_lab/
|
|
143
|
+
Create `~/.config/robot_lab/robot_lab.yml` for personal defaults that apply
|
|
144
|
+
across all your projects. Keys go at the top level here too:
|
|
111
145
|
|
|
112
|
-
```yaml title="~/.config/robot_lab/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
model: claude-sonnet-4
|
|
146
|
+
```yaml title="~/.config/robot_lab/robot_lab.yml"
|
|
147
|
+
ruby_llm:
|
|
148
|
+
model: claude-sonnet-4
|
|
149
|
+
request_timeout: 120
|
|
117
150
|
```
|
|
118
151
|
|
|
152
|
+
> [!WARNING]
|
|
153
|
+
> **Do not put ERB in the user config file.** It is parsed with
|
|
154
|
+
> `YAML.safe_load` and never passed through ERB, so
|
|
155
|
+
> `anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>` is stored as the literal
|
|
156
|
+
> nine-character-plus string `"<%= ENV['ANTHROPIC_API_KEY'] %>"` and sent to the
|
|
157
|
+
> provider as your API key. Put secrets in environment variables
|
|
158
|
+
> (`ANTHROPIC_API_KEY` or `ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY`), or in the
|
|
159
|
+
> project config file where ERB is evaluated.
|
|
160
|
+
|
|
161
|
+
> [!NOTE]
|
|
162
|
+
> The user config file honours **both** forms: flat keys at the root, or a
|
|
163
|
+
> top-level section named for the current environment (`development:`, `test:`,
|
|
164
|
+
> `production:`). The loader looks for the environment section first and falls back
|
|
165
|
+
> to the root. Only `defaults:` is ignored here.
|
|
166
|
+
|
|
119
167
|
## Configuration Reference
|
|
120
168
|
|
|
121
169
|
### Core Settings
|
|
122
170
|
|
|
123
171
|
| Key | Default | Description |
|
|
124
172
|
|-----|---------|-------------|
|
|
125
|
-
| `max_iterations` | `10` | Maximum robots per network run |
|
|
126
|
-
| `max_tool_iterations` | `10` | Maximum tool calls per robot run |
|
|
127
|
-
| `streaming_enabled` | `true` | Enable streaming by default |
|
|
128
173
|
| `template_path` | `null` (auto-detected) | Directory for prompt templates |
|
|
129
174
|
| `mcp` | `:none` | Global MCP server configuration |
|
|
130
|
-
| `tools` | `:none` | Global tool
|
|
175
|
+
| `tools` | `:none` | Global tool allowlist |
|
|
176
|
+
| `sandbox.*` | see below | Skill-script confinement ceiling |
|
|
177
|
+
|
|
178
|
+
> [!WARNING]
|
|
179
|
+
> **Reserved / not implemented.** `defaults.yml` also ships `max_iterations`,
|
|
180
|
+
> `max_tool_iterations`, `streaming_enabled`, and an entire `chat:` tree
|
|
181
|
+
> (`chat.with_model`, `chat.with_temperature`, `chat.with_tools`,
|
|
182
|
+
> `chat.with_params.*`). These keys resolve — `RobotLab.config.max_iterations`
|
|
183
|
+
> returns `10` — but **nothing in the library reads them**. Setting them has zero
|
|
184
|
+
> effect. They are placeholders; do not build on them.
|
|
185
|
+
>
|
|
186
|
+
> The real equivalents are:
|
|
187
|
+
>
|
|
188
|
+
> | Dead key | Use instead |
|
|
189
|
+
> |----------|-------------|
|
|
190
|
+
> | `max_tool_iterations` | `max_tool_rounds:` on a robot or `RunConfig` |
|
|
191
|
+
> | `streaming_enabled` | `on_content:` callback, or a block passed to `run` |
|
|
192
|
+
> | `chat.with_temperature` | `temperature:` on a robot or `RunConfig` |
|
|
193
|
+
> | `chat.with_params.*` | `top_p:` / `max_tokens:` / etc. on a robot or `RunConfig` |
|
|
194
|
+
> | `max_iterations` | no equivalent — networks are bounded by their task graph |
|
|
131
195
|
|
|
132
196
|
### RubyLLM Settings (`ruby_llm:` section)
|
|
133
197
|
|
|
@@ -152,12 +216,19 @@ All settings under the `ruby_llm:` key are applied to `RubyLLM.configure` automa
|
|
|
152
216
|
|
|
153
217
|
| Key | Default | Description |
|
|
154
218
|
|-----|---------|-------------|
|
|
155
|
-
| `ruby_llm.
|
|
156
|
-
| `ruby_llm.model` | `claude-sonnet-4` | Default model for robots |
|
|
219
|
+
| `ruby_llm.model` | `claude-sonnet-4` | Default model for robots that do not set `model:` |
|
|
157
220
|
| `ruby_llm.default_model` | `null` | RubyLLM default model override |
|
|
158
221
|
| `ruby_llm.default_embedding_model` | `null` | Default embedding model |
|
|
159
222
|
| `ruby_llm.default_image_model` | `null` | Default image model |
|
|
160
223
|
|
|
224
|
+
> [!NOTE]
|
|
225
|
+
> `defaults.yml` also carries `ruby_llm.provider: :anthropic` and
|
|
226
|
+
> `ruby_llm.assume_model_exists: false`, but neither is read. A robot's provider
|
|
227
|
+
> comes from its own `provider:` keyword argument, and `assume_model_exists` is
|
|
228
|
+
> derived from whether that argument was given. Model ids must be full RubyLLM
|
|
229
|
+
> ids — an unknown id raises `RubyLLM::ModelNotFoundError` when the robot is
|
|
230
|
+
> constructed.
|
|
231
|
+
|
|
161
232
|
#### Connection Settings
|
|
162
233
|
|
|
163
234
|
| Key | Default | Description |
|
|
@@ -185,19 +256,39 @@ All settings under the `ruby_llm:` key are applied to `RubyLLM.configure` automa
|
|
|
185
256
|
| `ruby_llm.log_level` | `:info` | Log level (`:debug`, `:info`, `:warn`, `:error`) |
|
|
186
257
|
| `ruby_llm.log_stream_debug` | `false` | Log streaming debug output |
|
|
187
258
|
|
|
188
|
-
### Chat Configuration (`chat:` section)
|
|
259
|
+
### Chat Configuration (`chat:` section) — not implemented
|
|
260
|
+
|
|
261
|
+
The `chat:` tree in `defaults.yml` (`chat.with_temperature`,
|
|
262
|
+
`chat.with_params.top_p`, `chat.with_params.max_tokens`, `chat.with_tools`, …)
|
|
263
|
+
is **reserved and has no consumers**. Values set there are parsed and then
|
|
264
|
+
ignored.
|
|
265
|
+
|
|
266
|
+
To set LLM parameters globally, pass a `RunConfig` to each robot, or set them per
|
|
267
|
+
robot with constructor keyword arguments:
|
|
268
|
+
|
|
269
|
+
```ruby
|
|
270
|
+
robot = RobotLab.build(
|
|
271
|
+
name: "bot",
|
|
272
|
+
system_prompt: "You are helpful.",
|
|
273
|
+
temperature: 0.7,
|
|
274
|
+
max_tokens: 2000,
|
|
275
|
+
top_p: 0.9
|
|
276
|
+
)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Skill-Script Sandboxing (`sandbox:` section)
|
|
189
280
|
|
|
190
|
-
|
|
281
|
+
Opt-in confinement for the scripts a [skill bundle](../guides/using-tools.md#skill-scripts-and-sandboxing) exposes as tools. Disabled by default — scripts run exactly as before until you turn it on:
|
|
191
282
|
|
|
192
283
|
| Key | Default | Description |
|
|
193
284
|
|-----|---------|-------------|
|
|
194
|
-
| `
|
|
195
|
-
| `
|
|
196
|
-
| `
|
|
197
|
-
| `
|
|
198
|
-
| `
|
|
199
|
-
|
|
200
|
-
|
|
285
|
+
| `sandbox.enabled` | `false` | Turn on OS-level confinement for skill scripts |
|
|
286
|
+
| `sandbox.fs_read` | `["."]` | Ceiling: paths any skill script may read (relative to cwd) |
|
|
287
|
+
| `sandbox.fs_write` | `[]` | Ceiling: paths any skill script may write |
|
|
288
|
+
| `sandbox.network` | `false` | Ceiling: whether any skill script may use the network |
|
|
289
|
+
| `sandbox.timeout` | `60` | Ceiling: max seconds a skill script may run |
|
|
290
|
+
|
|
291
|
+
These are a **ceiling**, not a grant — the actual permissions a script runs with are the intersection of this ceiling and what the individual SKILL.md declares for itself. See [Skill Scripts and Sandboxing](../guides/using-tools.md#skill-scripts-and-sandboxing) for how the two combine and which platforms actually enforce confinement.
|
|
201
292
|
|
|
202
293
|
## Runtime-Only Attributes
|
|
203
294
|
|
|
@@ -228,7 +319,8 @@ This clears the cached config and reloads from all sources on next access.
|
|
|
228
319
|
|
|
229
320
|
## Environment-Specific Configuration
|
|
230
321
|
|
|
231
|
-
The `defaults.yml` shipped with RobotLab includes environment-specific overrides
|
|
322
|
+
The `defaults.yml` shipped with RobotLab includes environment-specific overrides.
|
|
323
|
+
This is what the gem actually ships:
|
|
232
324
|
|
|
233
325
|
=== "Development"
|
|
234
326
|
|
|
@@ -242,10 +334,10 @@ The `defaults.yml` shipped with RobotLab includes environment-specific overrides
|
|
|
242
334
|
|
|
243
335
|
```yaml
|
|
244
336
|
test:
|
|
245
|
-
max_iterations: 3
|
|
246
|
-
streaming_enabled: false
|
|
337
|
+
max_iterations: 3 # reserved, no effect
|
|
338
|
+
streaming_enabled: false # reserved, no effect
|
|
247
339
|
ruby_llm:
|
|
248
|
-
model: claude-haiku-
|
|
340
|
+
model: claude-3-haiku-20240307
|
|
249
341
|
request_timeout: 30
|
|
250
342
|
max_retries: 1
|
|
251
343
|
log_level: :warn
|
|
@@ -255,8 +347,8 @@ The `defaults.yml` shipped with RobotLab includes environment-specific overrides
|
|
|
255
347
|
|
|
256
348
|
```yaml
|
|
257
349
|
production:
|
|
258
|
-
streaming_enabled: false
|
|
259
|
-
max_iterations: 20
|
|
350
|
+
streaming_enabled: false # reserved, no effect
|
|
351
|
+
max_iterations: 20 # reserved, no effect
|
|
260
352
|
ruby_llm:
|
|
261
353
|
request_timeout: 180
|
|
262
354
|
max_retries: 5
|
|
@@ -265,26 +357,56 @@ The `defaults.yml` shipped with RobotLab includes environment-specific overrides
|
|
|
265
357
|
|
|
266
358
|
The current environment is determined automatically (via `RAILS_ENV`, `RACK_ENV`, or defaults to `development`).
|
|
267
359
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
360
|
+
> [!NOTE]
|
|
361
|
+
> Under `test`, the effective default model is `claude-3-haiku-20240307` — a full
|
|
362
|
+
> dated model id. Short aliases like `claude-haiku-3-5` are **not** valid RubyLLM
|
|
363
|
+
> model ids and raise `RubyLLM::ModelNotFoundError` at robot construction.
|
|
271
364
|
|
|
272
|
-
|
|
365
|
+
## Rails Integration
|
|
273
366
|
|
|
274
|
-
|
|
275
|
-
|
|
367
|
+
> [!NOTE]
|
|
368
|
+
> Core RobotLab ships **no Railtie and no Engine**. It performs two bare
|
|
369
|
+
> `defined?(::Rails)` checks: the default logger becomes `Rails.logger`, and
|
|
370
|
+
> `template_path` resolves to `Rails.root/app/prompts` when left unset. Generators,
|
|
371
|
+
> `RobotLab::Job`, and Turbo broadcasting live in the separate
|
|
372
|
+
> [robot_lab-rails](https://github.com/MadBomber/robot_lab-rails) gem.
|
|
373
|
+
|
|
374
|
+
> [!WARNING]
|
|
375
|
+
> **Under Rails, `./config/robot_lab.yml` must be environment-sectioned — a flat
|
|
376
|
+
> file is ignored.** Rails' anyway_config integration sets
|
|
377
|
+
> `Anyway::Settings.current_environment` to `Rails.env`, which switches the project
|
|
378
|
+
> config loader into environmental mode. Keys then have to live under
|
|
379
|
+
> `development:` / `test:` / `production:`; anything written at the root of the file
|
|
380
|
+
> is dropped. Outside Rails the rule is exactly inverted — flat keys are read and an
|
|
381
|
+
> environment section is ignored.
|
|
382
|
+
|
|
383
|
+
```yaml title="config/robot_lab.yml (under Rails)"
|
|
384
|
+
development:
|
|
276
385
|
ruby_llm:
|
|
277
386
|
anthropic_api_key: <%= Rails.application.credentials.anthropic_api_key %>
|
|
278
387
|
model: claude-sonnet-4
|
|
279
|
-
|
|
388
|
+
request_timeout: 180
|
|
389
|
+
max_retries: 5
|
|
280
390
|
template_path: null # auto-detects app/prompts in Rails
|
|
281
391
|
|
|
282
392
|
production:
|
|
283
393
|
ruby_llm:
|
|
394
|
+
anthropic_api_key: <%= Rails.application.credentials.anthropic_api_key %>
|
|
395
|
+
model: claude-sonnet-4
|
|
284
396
|
request_timeout: 180
|
|
285
397
|
max_retries: 5
|
|
286
398
|
```
|
|
287
399
|
|
|
400
|
+
The same file outside Rails would instead be written flat:
|
|
401
|
+
|
|
402
|
+
```yaml title="config/robot_lab.yml (no Rails)"
|
|
403
|
+
ruby_llm:
|
|
404
|
+
model: claude-sonnet-4
|
|
405
|
+
request_timeout: 180
|
|
406
|
+
|
|
407
|
+
template_path: prompts
|
|
408
|
+
```
|
|
409
|
+
|
|
288
410
|
You can also use Rails credentials:
|
|
289
411
|
|
|
290
412
|
```bash
|
|
@@ -297,13 +419,10 @@ anthropic_api_key: sk-ant-...
|
|
|
297
419
|
openai_api_key: sk-...
|
|
298
420
|
```
|
|
299
421
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
ruby_llm:
|
|
305
|
-
anthropic_api_key: <%= Rails.application.credentials.anthropic_api_key %>
|
|
306
|
-
```
|
|
422
|
+
`./config/robot_lab.yml` is evaluated through ERB, so credentials can be
|
|
423
|
+
referenced inline as shown above. Note that this only works in the **project**
|
|
424
|
+
config file — the `~/.config/robot_lab/robot_lab.yml` user file is not run
|
|
425
|
+
through ERB.
|
|
307
426
|
|
|
308
427
|
## RunConfig: Shared Operational Defaults
|
|
309
428
|
|
|
@@ -348,13 +467,32 @@ robot = RobotLab.build(
|
|
|
348
467
|
temperature: 0.9 # overrides shared config's 0.5
|
|
349
468
|
)
|
|
350
469
|
|
|
351
|
-
# Network
|
|
470
|
+
# Network-level config
|
|
352
471
|
network = RobotLab.create_network(name: "pipeline", config: shared) do
|
|
353
472
|
task :analyzer, analyzer_robot, depends_on: :none
|
|
354
473
|
task :writer, writer_robot, depends_on: [:analyzer]
|
|
355
474
|
end
|
|
356
475
|
```
|
|
357
476
|
|
|
477
|
+
> [!WARNING]
|
|
478
|
+
> **A network-level `config:` propagates only `mcp` and `tools`** — and only when
|
|
479
|
+
> the member robot opts in by passing `mcp: :inherit` / `tools: :inherit` on its
|
|
480
|
+
> `run`. LLM fields (`model`, `temperature`, `max_tokens`, …) and callbacks
|
|
481
|
+
> (`on_content`, `on_tool_call`, `on_tool_result`) are read from each robot's
|
|
482
|
+
> **own** config at construction time and are never inherited from the network.
|
|
483
|
+
> The only field the network itself consumes is `max_concurrent_robots`.
|
|
484
|
+
>
|
|
485
|
+
> If you want a whole team on one model, pass the same `config:` to each robot:
|
|
486
|
+
>
|
|
487
|
+
> ```ruby
|
|
488
|
+
> shared = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
|
|
489
|
+
> analyst = RobotLab.build(name: "analyst", system_prompt: "...", config: shared)
|
|
490
|
+
> writer = RobotLab.build(name: "writer", system_prompt: "...", config: shared)
|
|
491
|
+
> ```
|
|
492
|
+
>
|
|
493
|
+
> A per-task `config:` is merged into the network config and is subject to the
|
|
494
|
+
> same `mcp`/`tools`-only limitation.
|
|
495
|
+
|
|
358
496
|
### Merging Configs
|
|
359
497
|
|
|
360
498
|
RunConfig supports merge semantics where the more-specific config's values win:
|
|
@@ -373,8 +511,10 @@ effective.temperature #=> 0.9 (overridden)
|
|
|
373
511
|
|----------|--------|
|
|
374
512
|
| **LLM** | `model`, `temperature`, `top_p`, `top_k`, `max_tokens`, `presence_penalty`, `frequency_penalty`, `stop` |
|
|
375
513
|
| **Tools** | `mcp`, `tools` |
|
|
376
|
-
| **Callbacks** | `on_tool_call`, `on_tool_result` |
|
|
377
|
-
| **Infrastructure** | `bus`, `enable_cache`, `max_tool_rounds`, `token_budget`, `ractor_pool_size`, `max_concurrent_robots`, `doom_loop_threshold`, `auto_compact`, `compact_threshold` |
|
|
514
|
+
| **Callbacks** | `on_tool_call`, `on_tool_result`, `on_content` |
|
|
515
|
+
| **Infrastructure** | `bus`, `enable_cache`, `max_tool_rounds`, `token_budget`, `cost_budget`, `max_tools`, `ractor_pool_size`, `max_concurrent_robots`, `doom_loop_threshold`, `auto_compact`, `compact_threshold` |
|
|
516
|
+
|
|
517
|
+
`cost_budget` mirrors `token_budget` for cumulative dollar spend — see [Budgets](../guides/observability.md#budgets-token-cost). `max_tools` overrides the default 128-tool ceiling enforced on every turn before tools are handed to the provider — see [Tool Capping](../guides/using-tools.md#tool-capping-and-per-turn-filtering).
|
|
378
518
|
|
|
379
519
|
### RunConfig vs RobotLab.config
|
|
380
520
|
|
|
@@ -394,41 +534,107 @@ Individual robots can override the global model and other settings:
|
|
|
394
534
|
robot = RobotLab.build(
|
|
395
535
|
name: "fast_bot",
|
|
396
536
|
system_prompt: "You are a quick responder.",
|
|
397
|
-
model: "claude-haiku-
|
|
537
|
+
model: "claude-3-haiku-20240307",
|
|
398
538
|
temperature: 0.3,
|
|
399
539
|
max_tokens: 500
|
|
400
540
|
)
|
|
401
541
|
|
|
402
542
|
# Or use chaining at runtime
|
|
403
|
-
robot.with_temperature(0.9).
|
|
543
|
+
robot.with_temperature(0.9).with_params(max_tokens: 2000).run("Tell me a story.")
|
|
404
544
|
```
|
|
405
545
|
|
|
546
|
+
> [!WARNING]
|
|
547
|
+
> There is **no `with_max_tokens`, `with_top_p`, `with_top_k`, `with_stop`,
|
|
548
|
+
> `with_presence_penalty`, or `with_frequency_penalty`** — calling them raises
|
|
549
|
+
> `NoMethodError`. The complete chainable set is `with_context`, `with_headers`,
|
|
550
|
+
> `with_instructions`, `with_model`, `with_params`, `with_schema`,
|
|
551
|
+
> `with_temperature`, `with_thinking`, `with_tool`, `with_tools`, plus RobotLab's
|
|
552
|
+
> own `with_template` and `with_bus`. For everything else use a constructor
|
|
553
|
+
> keyword argument or `with_params(...)`.
|
|
554
|
+
|
|
406
555
|
## Hierarchical MCP and Tools
|
|
407
556
|
|
|
408
557
|
MCP servers and tools use a hierarchical configuration: `runtime > robot > network > global`. Each level can specify:
|
|
409
558
|
|
|
410
559
|
- `:inherit` -- Use the parent level's configuration
|
|
411
560
|
- `:none` -- No MCP servers or tools at this level
|
|
412
|
-
- An explicit array --
|
|
561
|
+
- An explicit array -- A name **allowlist** (not a local-vs-MCP switch). Entries are
|
|
562
|
+
compared as strings against each attached tool's `name`, so they must match the
|
|
563
|
+
form the tool was attached in: a tool attached as a class matches `[RefundTool]`
|
|
564
|
+
or `%w[RefundTool]`, while one attached as an instance matches `%w[refund]`
|
|
565
|
+
|
|
566
|
+
> [!WARNING]
|
|
567
|
+
> **`tools:` and `mcp:` both default to `:none` — including on `run()` itself.**
|
|
568
|
+
> `Robot#run` is declared `run(message = nil, ..., mcp: :none, tools: :none, ...)`,
|
|
569
|
+
> and an explicit `:none` means "send zero tools this turn". So a plain
|
|
570
|
+
> `robot.run("...")` sends the LLM **no tools and connects no MCP servers**, even
|
|
571
|
+
> when you passed `local_tools:` or `mcp:` at build time.
|
|
572
|
+
>
|
|
573
|
+
> The fix goes on the **run**, not the build:
|
|
574
|
+
>
|
|
575
|
+
> ```ruby
|
|
576
|
+
> robot.run("...", tools: :inherit) # send the attached local tools
|
|
577
|
+
> robot.run("...", mcp: :inherit, tools: :inherit) # connect MCP and send its tools
|
|
578
|
+
> ```
|
|
579
|
+
>
|
|
580
|
+
> `mcp: :inherit` triggers the connection; `tools: :inherit` is additionally
|
|
581
|
+
> required for the MCP tools to actually be handed to the model.
|
|
582
|
+
|
|
583
|
+
> [!IMPORTANT]
|
|
584
|
+
> **For a standalone robot, do not pass `tools: :inherit` at build time.** Build-time
|
|
585
|
+
> `:inherit` resolves against the parent level, and for a standalone robot the parent
|
|
586
|
+
> is the global `:none` — producing an allowlist of `["none"]` that matches nothing
|
|
587
|
+
> and suppresses the tools even when the run asks for `:inherit`. Verified
|
|
588
|
+
> resolution for a standalone robot:
|
|
589
|
+
>
|
|
590
|
+
> | build `tools:` | run `tools:` | tools sent |
|
|
591
|
+
> |---|---|---|
|
|
592
|
+
> | unset | `:none` (default) | none |
|
|
593
|
+
> | unset | `:inherit` | the attached tools ✅ |
|
|
594
|
+
> | `:inherit` | `:inherit` | **none** ❌ |
|
|
595
|
+
> | `:inherit` | `:none` | none |
|
|
596
|
+
> | `:none` | `:inherit` | the attached tools ✅ |
|
|
597
|
+
>
|
|
598
|
+
> Leave `tools:` unset at build time — unless the robot is a member of a network.
|
|
599
|
+
|
|
600
|
+
> [!NOTE]
|
|
601
|
+
> **Inside a network, build-time `:inherit` is the opt-in, not a bug.** The parent
|
|
602
|
+
> is resolved at run time as the network's `config:`, so `:inherit` is how a robot
|
|
603
|
+
> asks for the network-level allowlist. With
|
|
604
|
+
> `RobotLab::RunConfig.new(tools: %w[RefundTool])` on the network and a robot
|
|
605
|
+
> holding `local_tools: [RefundTool, InvoiceTool]`:
|
|
606
|
+
>
|
|
607
|
+
> | build `tools:` | task `tools:` | tools sent |
|
|
608
|
+
> |---|---|---|
|
|
609
|
+
> | unset | omitted (`:none`) | none |
|
|
610
|
+
> | unset | `:inherit` | `refund`, `invoice` — the network allowlist is **not** applied |
|
|
611
|
+
> | `:inherit` | `:inherit` | `refund` — the network allowlist **is** applied ✅ |
|
|
612
|
+
> | `:inherit` | omitted (`:none`) | none |
|
|
613
|
+
>
|
|
614
|
+
> The same reasoning applies to `mcp:`.
|
|
413
615
|
|
|
414
616
|
```ruby
|
|
415
|
-
#
|
|
416
|
-
robot = RobotLab.build(
|
|
417
|
-
name: "agent",
|
|
418
|
-
system_prompt: "You are helpful.",
|
|
419
|
-
mcp: :inherit,
|
|
420
|
-
tools: :inherit
|
|
421
|
-
)
|
|
422
|
-
|
|
423
|
-
# Robot with no MCP, specific tools
|
|
617
|
+
# Correct: attach tools at build time, request them at run time
|
|
424
618
|
robot = RobotLab.build(
|
|
425
619
|
name: "calculator",
|
|
426
620
|
system_prompt: "You solve math problems.",
|
|
427
|
-
mcp: :none,
|
|
428
621
|
local_tools: [Calculator]
|
|
429
622
|
)
|
|
623
|
+
|
|
624
|
+
robot.run("What is 17 * 23?", tools: :inherit)
|
|
625
|
+
|
|
626
|
+
# Robot with MCP servers attached at build time
|
|
627
|
+
robot = RobotLab.build(
|
|
628
|
+
name: "agent",
|
|
629
|
+
system_prompt: "You are helpful.",
|
|
630
|
+
mcp: [{ name: "filesystem", transport: { type: "stdio", command: "mcp-server-filesystem" } }]
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
robot.run("List the files in ./lib", mcp: :inherit, tools: :inherit)
|
|
430
634
|
```
|
|
431
635
|
|
|
636
|
+
See [Runtime Tool Filtering](../guides/using-tools.md#runtime-tool-filtering) for the full `:inherit`/`:none`/array semantics.
|
|
637
|
+
|
|
432
638
|
## Next Steps
|
|
433
639
|
|
|
434
640
|
- [Building Robots](../guides/building-robots.md) - Create custom robots
|