robot_lab 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +6 -2
  3. data/.loki +5 -0
  4. data/Archspec.rb +44 -0
  5. data/CHANGELOG.md +70 -1
  6. data/CLAUDE.md +4 -2
  7. data/README.md +15 -17
  8. data/Rakefile +6 -111
  9. data/_typos.toml +21 -0
  10. data/docs/api/index.md +2 -2
  11. data/docs/api/skills.md +53 -149
  12. data/docs/api/support.md +1 -1
  13. data/docs/architecture/core-concepts.md +1 -1
  14. data/docs/architecture/index.md +5 -4
  15. data/docs/architecture/state-management.md +4 -4
  16. data/docs/concepts.md +1 -1
  17. data/docs/getting-started/configuration.md +4 -1
  18. data/docs/getting-started/installation.md +1 -4
  19. data/docs/guides/hooks.md +122 -0
  20. data/docs/guides/memory.md +2 -2
  21. data/docs/guides/using-tools.md +10 -1
  22. data/examples/.envrc +2 -0
  23. data/examples/02_tools.rb +8 -8
  24. data/examples/03_network.rb +1 -1
  25. data/examples/04_mcp.rb +7 -5
  26. data/examples/08_llm_config.rb +5 -5
  27. data/examples/09_chaining.rb +3 -3
  28. data/examples/14_rusty_circuit/comic.rb +8 -8
  29. data/examples/14_rusty_circuit/scout.rb +4 -4
  30. data/examples/15_memory_network_and_bus/README.md +66 -0
  31. data/examples/15_memory_network_and_bus/output/combined_article.md +5 -7
  32. data/examples/15_memory_network_and_bus/output/final_article.md +5 -10
  33. data/examples/15_memory_network_and_bus/output/linux_draft.md +3 -3
  34. data/examples/15_memory_network_and_bus/output/mac_draft.md +3 -3
  35. data/examples/15_memory_network_and_bus/output/memory.json +6 -6
  36. data/examples/15_memory_network_and_bus/output/revision_1.md +21 -10
  37. data/examples/15_memory_network_and_bus/output/revision_2.md +43 -6
  38. data/examples/15_memory_network_and_bus/output/revision_3.md +8 -0
  39. data/examples/15_memory_network_and_bus/output/windows_draft.md +3 -3
  40. data/examples/16_writers_room/tools.rb +14 -14
  41. data/examples/19_token_tracking.rb +2 -2
  42. data/examples/20_circuit_breaker.rb +3 -3
  43. data/examples/22_context_compression.rb +1 -1
  44. data/examples/27_incident_response/README.md +65 -0
  45. data/examples/28_mcp_discovery.rb +2 -2
  46. data/examples/29_ractor_tools.rb +4 -4
  47. data/examples/30_ractor_network.rb +2 -2
  48. data/examples/33_stock_predictor.rb +8 -8
  49. data/examples/35_hooks.rb +3 -3
  50. data/examples/README.md +17 -0
  51. data/examples/common.rb +55 -23
  52. data/examples/run_all.rb +60 -0
  53. data/lib/robot_lab/agent_skill_catalog.rb +1 -0
  54. data/lib/robot_lab/ask_user.rb +5 -3
  55. data/lib/robot_lab/bus_poller.rb +2 -0
  56. data/lib/robot_lab/capabilities.rb +4 -0
  57. data/lib/robot_lab/config/defaults.yml +5 -5
  58. data/lib/robot_lab/config.rb +10 -2
  59. data/lib/robot_lab/doom_loop_detector.rb +6 -3
  60. data/lib/robot_lab/history_compressor.rb +5 -0
  61. data/lib/robot_lab/hook.rb +1 -0
  62. data/lib/robot_lab/hook_context.rb +4 -0
  63. data/lib/robot_lab/hook_registry.rb +1 -0
  64. data/lib/robot_lab/hooks.rb +6 -3
  65. data/lib/robot_lab/mcp/client.rb +2 -2
  66. data/lib/robot_lab/mcp/connection_poller.rb +16 -8
  67. data/lib/robot_lab/mcp/server_discovery.rb +1 -0
  68. data/lib/robot_lab/mcp/transports/sse.rb +3 -0
  69. data/lib/robot_lab/mcp/transports/stdio.rb +5 -0
  70. data/lib/robot_lab/mcp/transports/streamable_http.rb +5 -3
  71. data/lib/robot_lab/mcp/transports/websocket.rb +3 -0
  72. data/lib/robot_lab/memory.rb +39 -13
  73. data/lib/robot_lab/memory_change.rb +1 -0
  74. data/lib/robot_lab/message.rb +3 -0
  75. data/lib/robot_lab/names.rb +2 -4
  76. data/lib/robot_lab/network.rb +13 -6
  77. data/lib/robot_lab/robot/agent_skill_matching.rb +2 -0
  78. data/lib/robot_lab/robot/bus_messaging.rb +3 -0
  79. data/lib/robot_lab/robot/history_search.rb +2 -0
  80. data/lib/robot_lab/robot/hooking.rb +29 -0
  81. data/lib/robot_lab/robot/mcp_management.rb +11 -3
  82. data/lib/robot_lab/robot/result_building.rb +119 -0
  83. data/lib/robot_lab/robot/template_rendering.rb +13 -4
  84. data/lib/robot_lab/robot.rb +88 -130
  85. data/lib/robot_lab/robot_result.rb +3 -0
  86. data/lib/robot_lab/run_config.rb +57 -20
  87. data/lib/robot_lab/script_tool.rb +20 -39
  88. data/lib/robot_lab/state_proxy.rb +1 -0
  89. data/lib/robot_lab/streaming/context.rb +1 -0
  90. data/lib/robot_lab/streaming/events.rb +1 -0
  91. data/lib/robot_lab/task.rb +2 -0
  92. data/lib/robot_lab/tool.rb +11 -12
  93. data/lib/robot_lab/user_message.rb +1 -0
  94. data/lib/robot_lab/utils.rb +2 -0
  95. data/lib/robot_lab/version.rb +1 -1
  96. data/lib/robot_lab/waiter.rb +3 -0
  97. data/lib/robot_lab.rb +9 -6
  98. metadata +23 -32
  99. data/lib/robot_lab/sandbox/null.rb +0 -13
  100. data/lib/robot_lab/sandbox/seatbelt.rb +0 -104
  101. data/lib/robot_lab/sandbox.rb +0 -52
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36b770d963e004a951cdf35d07a2e867e4e661a9e342e8047184cfd9dafd6e15
4
- data.tar.gz: f53bb5c52efcd6169bf36afc69306a9c03e9cfcabdaf5b0a94871802e2909f55
3
+ metadata.gz: 411c496dec788609f2ac0d2e1eb02ce7fd0523f40424b9437de0ffb24c00c644
4
+ data.tar.gz: 9fb9bb55b865734dc7c9ae1ac8d1994685575c1ee206d6cf9865c0af3d3a522c
5
5
  SHA512:
6
- metadata.gz: 850bde0e9ac1b7d65176bd067f42751d6d9db64517ca665d4765837fc5493e204f9b333cac08019cb451962d4d95789f986dee2ff08e55ae5e6a0d3d034f4f50
7
- data.tar.gz: 97eb4dc66458a9a43f861e3f9c927eb6cfa63d33ddfff150b4181a45dc2c0db9570095f2e71d1182b76b4ee85e7086a6014b5536e805d9956f0860f9b4a03f50
6
+ metadata.gz: b34ce72ebe6443c5f25423a595c0ffd468416ea1be02531b125efed33289ae8ea898c5413697fe373cebd658bddb510f322ef09623bb7b74f7587cf58b9ad064
7
+ data.tar.gz: e9884f0c8b501bfc57eafdc6c5492fea7455eb807267c370fc68b59e7322f97c0360e3af6f7e61b7c2493df3e2026f72095b9c287e65e6bf5f1ec5274948ef7d
data/.envrc CHANGED
@@ -1,2 +1,6 @@
1
- export RR=`pwd`
2
- export BUNDLE_GEMFILE=Gemfile
1
+ # robot_lab_project/robot_lab/.envrc
2
+
3
+ source_up
4
+
5
+ export RR=$(pwd)
6
+ # BUNDLE_GEMFILE is inherited from the project root .envrc (asgard dev|prod)
data/.loki CHANGED
@@ -18,6 +18,11 @@ class Tasks
18
18
  sh "ruby -Ilib:test \"#{path}\""
19
19
  end
20
20
 
21
+ desc "Check architecture boundaries with ArchSpec"
22
+ def archspec
23
+ sh "bundle exec archspec check"
24
+ end
25
+
21
26
  class DocsCommands < Tasks
22
27
  desc "Build YARD + MkDocs documentation"
23
28
  def build
data/Archspec.rb ADDED
@@ -0,0 +1,44 @@
1
+ component :robot, in: "lib/robot_lab/robot.rb"
2
+ component :robot_support, in: "lib/robot_lab/robot/**/*.rb"
3
+ component :network, in: ["lib/robot_lab/network.rb", "lib/robot_lab/bus_poller.rb"]
4
+ component :memory, in: ["lib/robot_lab/memory.rb", "lib/robot_lab/memory_change.rb"]
5
+ component :mcp, in: "lib/robot_lab/mcp/**/*.rb"
6
+ component :sandbox, in: ["lib/robot_lab/sandbox.rb", "lib/robot_lab/sandbox/**/*.rb"]
7
+ component :streaming, in: "lib/robot_lab/streaming/**/*.rb"
8
+ component :budget, in: "lib/robot_lab/budget/**/*.rb"
9
+ component :hooks, in: %w[
10
+ lib/robot_lab/hook.rb
11
+ lib/robot_lab/hook_context.rb
12
+ lib/robot_lab/hook_registry.rb
13
+ lib/robot_lab/hooks.rb
14
+ ]
15
+ component :tools, in: %w[
16
+ lib/robot_lab/tool.rb
17
+ lib/robot_lab/tool_config.rb
18
+ lib/robot_lab/tool_manifest.rb
19
+ lib/robot_lab/script_tool.rb
20
+ ]
21
+ component :config, in: ["lib/robot_lab/config.rb", "lib/robot_lab/run_config.rb"]
22
+
23
+ # NOTE: every file here reopens `module RobotLab`, so a `dependencies.forbid`
24
+ # rule keyed on components (e.g. `memory.cannot_use :robot`) treats a plain
25
+ # `RobotLab.config` call as "depends on every component" — every file
26
+ # "defines" the bare RobotLab constant. Naming the actual target constants
27
+ # instead of components sidesteps that ambiguity.
28
+
29
+ # Memory is a low-level primitive shared by Robot and Network; it must not
30
+ # depend back on the things that depend on it.
31
+ memory.cannot_reference_constants "RobotLab::Robot", "RobotLab::Network"
32
+
33
+ # MCP is an external-tool integration layer that Robot/Network consume; it
34
+ # must not reach back into the objects that use it.
35
+ mcp.cannot_reference_constants "RobotLab::Robot", "RobotLab::Network"
36
+
37
+ # Sandbox runs untrusted/generated code paths and should have the smallest
38
+ # possible surface area — no reaching into orchestration or memory internals.
39
+ sandbox.cannot_reference_constants "RobotLab::Robot", "RobotLab::Network", "RobotLab::Memory"
40
+
41
+ # Config/RunConfig sits at the bottom of the configuration cascade
42
+ # (RobotLab.config -> Network -> Robot -> template -> task -> runtime) and
43
+ # must not depend upward on the objects it configures.
44
+ config.cannot_reference_constants "RobotLab::Robot", "RobotLab::Network"
data/CHANGELOG.md CHANGED
@@ -6,7 +6,60 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
- ## [Unreleased]
9
+ ## [0.3.0] - 2026-09-18
10
+
11
+ The ruby_llm 2.0 release. Core moves from ruby_llm 1.x to the official 2.0.0
12
+ release; every `robot_lab-*` extension gem releases in lockstep at 0.3.0 and
13
+ now requires `robot_lab ~> 0.3.0`.
14
+
15
+ ### Changed (BREAKING)
16
+ - **ruby_llm 1.x → 2.0.** The gemspec now requires `ruby_llm "~> 2.0"`
17
+ (official release). Robot tools written with the 1.x DSL must migrate:
18
+ `param` → `parameter`, `desc:` → `description:`; `halt` is gone (the caller
19
+ controls the loop). Token counts moved to `response.tokens.input/.output`,
20
+ and `finish_reason` (Symbol) replaces `stop_reason`. The `claude-sonnet-4`
21
+ registry alias no longer resolves; the default models are now
22
+ `claude-sonnet-4-6` (production) and `claude-haiku-4-5` (test).
23
+ - **Dropped hard dependencies** on `ruby_llm-mcp`, `ruby_llm-schema`, and
24
+ `ruby_llm-semantic_cache`. Semantic caching is now optional: `Memory#cache`
25
+ returns `nil` when `ruby_llm-semantic_cache` is not installed and Memory
26
+ runs with caching disabled. (Its released versions still pin ruby_llm 1.x,
27
+ so it cannot co-resolve with 2.0 yet.)
28
+
29
+ ### Added
30
+ - `ruby_llm-providers-apfel` and `ruby_llm-providers-lms` (`~> 0.2.1`) as
31
+ runtime dependencies, consumed as released gems — the local `path:`
32
+ overrides for sibling checkouts are gone from the Gemfiles.
33
+ - `Robot::ResultBuilding` — extracted module that adapts a ruby_llm response
34
+ into a `RobotResult` (token accounting, stop-reason normalization, message
35
+ coercion).
36
+ - `examples/run_all.rb` — runs every executable `NN_*.rb` demo serially with
37
+ a banner between each; works from any cwd, and Ctrl-C terminates only the
38
+ demo currently running (the runner notes it and moves on).
39
+ - READMEs for the subdirectory demos: `15_memory_network_and_bus`,
40
+ `26_document_store` (corpus), and `27_incident_response`.
41
+ - Fasterer quality gate now actually runs: `fasterer` added to the
42
+ development group, with a `.fasterer.yml` documenting the disabled
43
+ speed-over-readability checks.
44
+
45
+ ### Changed
46
+ - Robot, Tool, RunConfig, and the hook system adapted to the ruby_llm 2.0
47
+ API; docs and examples synced to match.
48
+ - Examples run against a local LM Studio server through the `:lms` provider;
49
+ `examples/common.rb` anchors `BUNDLE_GEMFILE` to the gem root so demos run
50
+ from any directory, and `examples/README.md` notes that demo speed and
51
+ answer quality depend on the provider/model in use.
52
+ - `.envrc` no longer exports `BUNDLE_GEMFILE`; it is inherited from the
53
+ project root (`asgard dev` / `asgard prod`).
54
+
55
+ ### Fixed
56
+ - amazing_print 3.0 crash at demo startup (`undefined method 'try' for
57
+ module ActiveSupport`): robot_lab's dependencies load parts of
58
+ ActiveSupport, which tricked amazing_print into loading its Rails
59
+ extension; `examples/common.rb` now preloads
60
+ `active_support/core_ext/object/try` and `active_support/log_subscriber`.
61
+
62
+ ## [0.2.8] - 2026-09-09
10
63
 
11
64
  ### Added
12
65
  - `BusMessaging#respond_to_tasks` and `#serve` — a member auto-answers inbound
@@ -16,6 +69,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
69
  without hand-wiring `on_message`.
17
70
  - `.loki` Asgard task file: `test`, `rubocop`, `rubocop_fix`, `flog`, `flay`, `quality`, `build`, `install`, `release`, `integration`, `docs`, and `examples` tasks via the Asgard task runner
18
71
 
72
+ ### Changed (BREAKING)
73
+ - **Sandboxing extracted to `robot_lab-sandbox`.** `RobotLab::Sandbox`,
74
+ `RobotLab::Sandbox::Seatbelt`, and `RobotLab::Sandbox::Null` no longer live
75
+ in core; they moved verbatim to the new `robot_lab-sandbox` gem. Core has no
76
+ execution limitations of its own: `ScriptTool.execute` now delegates to an
77
+ optional `ScriptTool.executor` (`nil` by default, in which case it runs the
78
+ unconfined `Open3.capture2e` path with no timeout exactly as before).
79
+ Applications that want confined skill-script execution must add
80
+ `gem "robot_lab-sandbox"` and `require "robot_lab/sandbox"`, which installs
81
+ itself as the executor; `config.sandbox.*` behavior is unchanged from there.
82
+ `RobotLab::Capabilities` (the declarative fs_read/fs_write/network/timeout/
83
+ trust manifest parsed from `SKILL.md`) stays in core since `AgentSkill` needs
84
+ it regardless of whether sandboxing is enforced.
85
+ - `ScriptTool.run_with_timeout` and `ScriptTool.terminate` moved to
86
+ `RobotLab::Sandbox::Executor` in `robot_lab-sandbox`.
87
+
19
88
  ### Fixed
20
89
  - `BusMessaging` now synchronizes its message counter and outbox with a mutex, so
21
90
  concurrent sends and poller-thread reply correlation can't clobber each other.
data/CLAUDE.md CHANGED
@@ -55,7 +55,7 @@ bundle exec rake examples:run[1]
55
55
 
56
56
  ### Memory System
57
57
 
58
- - **`Memory`** (`lib/robot_lab/memory.rb`): Key-value store with reserved keys (`:data`, `:results`, `:messages`, `:session_id`, `:cache`). Supports Redis backend. Includes semantic caching via RubyLLM::SemanticCache
58
+ - **`Memory`** (`lib/robot_lab/memory.rb`): Key-value store with reserved keys (`:data`, `:results`, `:messages`, `:session_id`, `:cache`). Supports Redis backend. Semantic caching via RubyLLM::SemanticCache when the optional ruby_llm-semantic_cache gem is installed (`memory.cache` is nil otherwise)
59
59
 
60
60
  ### MCP (Model Context Protocol)
61
61
 
@@ -119,7 +119,9 @@ Router receives `Router::Args` with: `context`, `network`, `stack`, `call_count`
119
119
 
120
120
  ## Dependencies
121
121
 
122
- Core: zeitwerk, ruby_llm (~> 1.12), ruby_llm-mcp, prompt_manager, ruby_llm-schema, ruby_llm-semantic_cache, async, simple_flow, state_machines
122
+ Core: zeitwerk, ruby_llm (~> 2.0.0.rc3), prompt_manager, async, simple_flow, state_machines
123
+
124
+ Optional: ruby_llm-semantic_cache (Memory#cache; no ruby_llm 2.0-compatible release yet — Memory runs with caching disabled when absent), classifier (compress_history), ruby_llm-providers-lms (local LM Studio provider used by the examples: qwen/qwen3.8-27b for complex work, openai/gpt-oss-20b for simple demos)
123
125
 
124
126
  ### Templates
125
127
 
data/README.md CHANGED
@@ -21,7 +21,7 @@
21
21
  - <strong>Human-in-the-Loop</strong> - AskUser tool for interactive prompting<br>
22
22
  - <strong>Content Streaming</strong> - Stored callbacks, per-call blocks, or both<br>
23
23
  - <strong>MCP Integration</strong> - Connect to external tool servers with timeouts and retry<br>
24
- - <strong>Local LLM Providers</strong> - Ollama and GPUStack via provider passthrough<br>
24
+ - <strong>Local LLM Providers</strong> - LM Studio (via ruby_llm-providers-lms), Ollama, and GPUStack via provider passthrough<br>
25
25
  - <strong>Shared Memory</strong> - Reactive key-value store with subscriptions<br>
26
26
  - <strong>Message Bus</strong> - Bidirectional robot communication via TypedBus<br>
27
27
  - <strong>Dynamic Spawning</strong> - Robots create new robots at runtime<br>
@@ -82,17 +82,15 @@ puts result.last_text_content
82
82
 
83
83
  ### Local LLM Providers
84
84
 
85
- For local LLM providers, use the `provider:` parameter. RubyLLM's provider list is the authority on valid values — `:ollama` and `:gpustack` are the local ones (alongside `:anthropic`, `:azure`, `:bedrock`, `:deepseek`, `:gemini`, `:mistral`, `:openai`, `:openrouter`, `:perplexity`, `:vertexai`, and `:xai`). The provider's API base must be configured first, or building the robot raises `RubyLLM::ConfigurationError`:
86
-
87
- ```bash
88
- export ROBOT_LAB_RUBY_LLM__OLLAMA_API_BASE=http://localhost:11434/v1
89
- ```
85
+ For local LLM providers, use the `provider:` parameter. RubyLLM's provider list is the authority on valid values — `:ollama` and `:gpustack` are built in, and provider gems add more (alongside `:anthropic`, `:azure`, `:bedrock`, `:deepseek`, `:gemini`, `:mistral`, `:openai`, `:openrouter`, `:perplexity`, `:vertexai`, and `:xai`). The examples in this repo use LM Studio through the [ruby_llm-providers-lms](https://github.com/madbomber/ruby_llm-providers-lms) gem, which registers the `:lms` provider (defaults to `http://localhost:1234/v1`, no API key needed):
90
86
 
91
87
  ```ruby
88
+ require "ruby_llm/providers/lms"
89
+
92
90
  robot = RobotLab.build(
93
91
  name: "local_bot",
94
- model: "llama3.2",
95
- provider: :ollama,
92
+ model: "qwen/qwen3.8-27b", # complex work; use "openai/gpt-oss-20b" for simple chat
93
+ provider: :lms,
96
94
  system_prompt: "You are a helpful assistant."
97
95
  )
98
96
  ```
@@ -113,12 +111,12 @@ RobotLab uses [MywayConfig](https://github.com/MadBomber/myway_config) for layer
113
111
  # Set API keys via environment variables (double underscore for nesting)
114
112
  export ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY=sk-ant-...
115
113
  export ROBOT_LAB_RUBY_LLM__OPENAI_API_KEY=sk-...
116
- export ROBOT_LAB_RUBY_LLM__MODEL=claude-sonnet-4
114
+ export ROBOT_LAB_RUBY_LLM__MODEL=claude-sonnet-4-6
117
115
  ```
118
116
 
119
117
  ```ruby
120
118
  # Access configuration values
121
- RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
119
+ RobotLab.config.ruby_llm.model #=> "claude-sonnet-4-6"
122
120
  RobotLab.config.ruby_llm.request_timeout #=> 120
123
121
  ```
124
122
 
@@ -126,7 +124,7 @@ Or create a project config file at `./config/robot_lab.yml`:
126
124
 
127
125
  ```yaml
128
126
  ruby_llm:
129
- model: claude-sonnet-4
127
+ model: claude-sonnet-4-6
130
128
  anthropic_api_key: sk-ant-...
131
129
  request_timeout: 180
132
130
  ```
@@ -202,7 +200,7 @@ mcp:
202
200
  type: stdio
203
201
  command: npx
204
202
  args: ["-y", "@modelcontextprotocol/server-github"]
205
- model: claude-sonnet-4
203
+ model: claude-sonnet-4-6
206
204
  ---
207
205
  You are a GitHub assistant. Use available tools to help with repository tasks.
208
206
  ```
@@ -265,7 +263,7 @@ robot = RobotLab.build(
265
263
  ```ruby
266
264
  # Create a shared config
267
265
  shared = RobotLab::RunConfig.new(
268
- model: "claude-sonnet-4",
266
+ model: "claude-sonnet-4-6",
269
267
  temperature: 0.7,
270
268
  max_tokens: 2000
271
269
  )
@@ -302,16 +300,16 @@ RunConfig supports keyword construction, block DSL, and merge semantics:
302
300
  ```ruby
303
301
  # Block DSL
304
302
  config = RobotLab::RunConfig.new do |c|
305
- c.model "claude-sonnet-4"
303
+ c.model "claude-sonnet-4-6"
306
304
  c.temperature 0.7
307
305
  end
308
306
 
309
307
  # Merge (more-specific wins)
310
- network_config = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
308
+ network_config = RobotLab::RunConfig.new(model: "claude-sonnet-4-6", temperature: 0.5)
311
309
  robot_config = RobotLab::RunConfig.new(temperature: 0.9)
312
310
  effective = network_config.merge(robot_config)
313
311
  effective.temperature #=> 0.9
314
- effective.model #=> "claude-sonnet-4"
312
+ effective.model #=> "claude-sonnet-4-6"
315
313
  ```
316
314
 
317
315
  ### Chaining Configuration
@@ -323,7 +321,7 @@ robot = RobotLab.build(name: "writer", system_prompt: "You are a creative writer
323
321
 
324
322
  result = robot
325
323
  .with_temperature(0.9)
326
- .with_model("claude-sonnet-4")
324
+ .with_model("claude-sonnet-4-6")
327
325
  .run("Write a haiku about Ruby programming")
328
326
  ```
329
327
 
data/Rakefile CHANGED
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rake/testtask"
3
+ # Quality gates (quality, rubocop_check, flog_check, flay_check,
4
+ # archspec_check, ...), doc *serving*, and the gem lifecycle (build, install,
5
+ # release) live in asgard — see .loki and the shared dev/*.loki files it
6
+ # imports. This Rakefile keeps the tasks asgard delegates to (tests,
7
+ # integration, examples, docs:build).
5
8
 
6
- RUBOCOP_ENV = { "RUBOCOP_CACHE_ROOT" => "tmp/rubocop_cache" }.freeze
9
+ require "rake/testtask"
7
10
 
8
11
  Rake::TestTask.new(:test) do |t|
9
12
  t.libs << "test"
@@ -36,100 +39,6 @@ Rake::TestTask.new(:integration) do |t|
36
39
  t.ruby_opts << "-rtest_helper"
37
40
  end
38
41
 
39
- desc "Check code complexity with Flog (warn ≥20, fail ≥50)"
40
- task :flog_check do
41
- require 'flog'
42
-
43
- # Target to work toward; methods above this are warned but don't fail the gate.
44
- METHOD_WARN = 20.0
45
- # Current baseline floor — established from first run. Reduce incrementally.
46
- METHOD_FAIL = 50.0
47
-
48
- flogger = Flog.new(all: true)
49
- flogger.flog(*Dir.glob('lib/**/*.rb'))
50
-
51
- warnings = []
52
- failures = []
53
-
54
- flogger.each_by_score do |method, score|
55
- next if method.end_with?('#none') # skip file-level non-method code
56
- if score > METHOD_FAIL
57
- failures << "#{'%.1f' % score}: #{method}"
58
- elsif score > METHOD_WARN
59
- warnings << "#{'%.1f' % score}: #{method}"
60
- end
61
- end
62
-
63
- unless warnings.empty?
64
- puts "\nFlog warnings (#{METHOD_WARN}–#{METHOD_FAIL}) — target for future refactoring:"
65
- warnings.each { |v| puts " #{v}" }
66
- end
67
-
68
- if failures.empty?
69
- puts "\nFlog: no methods exceed the failure threshold (≥#{METHOD_FAIL})"
70
- else
71
- puts "\nFlog failures (≥#{METHOD_FAIL}) — must be refactored:"
72
- failures.each { |v| puts " #{v}" }
73
- abort "\nFlog quality gate failed: #{failures.size} method(s) exceed #{METHOD_FAIL}"
74
- end
75
- end
76
-
77
- desc "Check for structural code duplication with Flay (mass >= 50)"
78
- task :flay_check do
79
- require 'flay'
80
-
81
- mass_threshold = 50
82
-
83
- flay = Flay.new({ mass: mass_threshold, diff: false, verbose: false, summary: false, timeout: 60 })
84
- flay.process(*Dir.glob('lib/**/*.rb'))
85
- flay.analyze
86
-
87
- if flay.hashes.empty?
88
- puts "\nFlay: no structural duplication detected (mass >= #{mass_threshold})"
89
- else
90
- puts "\nFlay found structural duplication (mass >= #{mass_threshold}):"
91
- flay.report
92
- abort "\nFlay quality gate failed: #{flay.hashes.length} pattern(s) detected"
93
- end
94
- end
95
-
96
- desc "Run all quality checks: tests (with coverage), RuboCop, Flog, and Flay"
97
- task :quality do
98
- results = {}
99
-
100
- puts "\n#{'=' * 60}"
101
- puts "Quality Gate: Tests + Coverage"
102
- puts '=' * 60
103
- results[:tests] = system("bundle exec rake test") ? :pass : :fail
104
-
105
- puts "\n#{'=' * 60}"
106
- puts "Quality Gate: RuboCop"
107
- puts '=' * 60
108
- results[:rubocop] = system(RUBOCOP_ENV, "bundle exec rubocop") ? :pass : :fail
109
-
110
- puts "\n#{'=' * 60}"
111
- puts "Quality Gate: Flog Complexity"
112
- puts '=' * 60
113
- results[:flog] = system("bundle exec rake flog_check") ? :pass : :fail
114
-
115
- puts "\n#{'=' * 60}"
116
- puts "Quality Gate: Flay Duplication"
117
- puts '=' * 60
118
- results[:flay] = system("bundle exec rake flay_check") ? :pass : :fail
119
-
120
- puts "\n#{'=' * 60}"
121
- puts "Quality Summary"
122
- puts '=' * 60
123
- results.each do |gate, status|
124
- icon = status == :pass ? 'PASS' : 'FAIL'
125
- puts " [#{icon}] #{gate}"
126
- end
127
- puts '=' * 60
128
-
129
- abort "\nQuality gate failed" if results.values.any?(:fail)
130
- puts "\nAll quality gates passed."
131
- end
132
-
133
42
  namespace :examples do
134
43
  # Map of subdirectory-based demos to their entry point scripts
135
44
  SUBDIR_ENTRY_POINTS = {
@@ -277,22 +186,8 @@ namespace :docs do
277
186
  sh "yard doc"
278
187
  end
279
188
 
280
- namespace :yard do
281
- desc "Serve YARD documentation locally"
282
- task :serve do
283
- sh "yard server --reload"
284
- end
285
- end
286
-
287
189
  desc "Build MkDocs documentation"
288
190
  task :mkdocs do
289
191
  sh "mkdocs build"
290
192
  end
291
-
292
- namespace :mkdocs do
293
- desc "Serve MkDocs documentation locally"
294
- task :serve do
295
- sh "mkdocs serve"
296
- end
297
- end
298
193
  end
data/_typos.toml ADDED
@@ -0,0 +1,21 @@
1
+ # False-positive suppressions for the typos quality gate (asgard).
2
+ # Everything here was reviewed by hand — see git history for context.
3
+
4
+ [files]
5
+ extend-exclude = [
6
+ # Example transcripts / articles: prose quotes real names ("Damon Clark")
7
+ # and SQL-ish plurals ("SELECTs") that typos mis-parses.
8
+ "examples/ruboruby.md",
9
+ "examples/25_history_search/conversation.jsonl",
10
+ ]
11
+
12
+ [default.extend-identifiers]
13
+ # Robot names in lib/robot_lab/names.rb
14
+ BATs = "BATs"
15
+ Damon = "Damon"
16
+ Mata_Nui = "Mata_Nui"
17
+ Pris = "Pris"
18
+ S1MONE = "S1MONE"
19
+ # Test-local variable names
20
+ ba = "ba" # b→a similarity (convergence_test.rb)
21
+ thr = "thr" # thread handle (connection_poller_test.rb)
data/docs/api/index.md CHANGED
@@ -41,8 +41,8 @@ confines them:
41
41
  | [AgentSkill](skills.md#robotlabagentskill) | One skill bundle: instructions + `scripts/` |
42
42
  | [AgentSkillCatalog](skills.md#robotlabagentskillcatalog) | Lazy registry over `~/.prompts/skills/` |
43
43
  | [Capabilities](skills.md#robotlabcapabilities) | Declared vs. ceiling filesystem / network / timeout grant |
44
- | [ScriptTool](skills.md#robotlabscripttool) | Wraps an executable script as a `Tool` |
45
- | [Sandbox](skills.md#robotlabsandbox) | macOS Seatbelt confinement, or a passthrough |
44
+ | [ScriptTool](skills.md#robotlabscripttool) | Wraps an executable script as a `Tool`; `ScriptTool.executor` is the confinement extension point |
45
+ | [Sandbox](skills.md#confinement-robot_lab-sandbox) | macOS Seatbelt confinement, or a passthrough — ships in the optional `robot_lab-sandbox` gem |
46
46
 
47
47
  ## Support Classes
48
48