robot_lab 0.2.1 → 0.2.6
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 +61 -0
- data/Rakefile +28 -12
- data/docs/api/core/index.md +1 -0
- data/docs/api/core/network.md +31 -1
- data/docs/api/core/robot.md +91 -5
- data/docs/api/errors.md +47 -2
- data/docs/api/index.md +3 -0
- data/docs/architecture/core-concepts.md +40 -0
- data/docs/architecture/network-orchestration.md +8 -0
- data/docs/architecture/robot-execution.md +1 -1
- data/docs/getting-started/configuration.md +20 -1
- data/docs/guides/building-robots.md +39 -1
- data/docs/guides/creating-networks.md +6 -1
- data/docs/guides/hooks.md +997 -0
- data/docs/guides/index.md +5 -0
- data/docs/guides/knowledge.md +2 -2
- data/docs/guides/observability.md +74 -7
- data/docs/guides/using-tools.md +69 -0
- data/docs/index.md +1 -1
- data/examples/03_network.rb +11 -2
- data/examples/34_agentskills.rb +0 -0
- data/examples/35_hooks.rb +256 -0
- data/examples/README.md +7 -0
- data/examples/common.rb +7 -3
- data/examples/xyzzy.rb +90 -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 +4 -4
- 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/narrator.rb +87 -0
- data/lib/robot_lab/network.rb +77 -20
- data/lib/robot_lab/robot/budget.rb +89 -0
- data/lib/robot_lab/robot/bus_messaging.rb +76 -12
- 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 +2 -2
- data/lib/robot_lab/robot.rb +211 -88
- 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 +49 -0
- data/mkdocs.yml +1 -0
- metadata +36 -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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d401853bc5fff2646793cfff4fbda7082b18b0e07b833b3f4de6545fca574125
|
|
4
|
+
data.tar.gz: f9964095406885bed329d8f2e7189cbb582e1c4665e3d53781b83371ee9016c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd29d885c9ea4e0b91bffe49b757808213e8011302fac48b4632c37d0cc95315a559f80240fabca92c0a309fead3f1f72b1da3ac7eccdb440059451fc53c5a0e
|
|
7
|
+
data.tar.gz: 518c45909e25437da64e16ad69a5bfe5f361ddd602dc64c0ad475acde1c0b7946a0773fa36960a0baea3294ae7a7cef1835b6d08be2034e3a4a905fa74715eb2
|
data/.envrc
CHANGED
data/.loki
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# robot_lab core — development tasks.
|
|
3
|
+
|
|
4
|
+
import_up "repo_dev.loki"
|
|
5
|
+
|
|
6
|
+
class Tasks
|
|
7
|
+
@@gem_name ||= "robot_lab".freeze
|
|
8
|
+
|
|
9
|
+
header "robot_lab v#{gem_version} — core multi-robot LLM framework"
|
|
10
|
+
|
|
11
|
+
desc "Run integration tests only"
|
|
12
|
+
def integration
|
|
13
|
+
sh "bundle exec rake integration"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "Run a single test file (e.g. asgard test_file test/robot_test.rb)"
|
|
17
|
+
def test_file(path)
|
|
18
|
+
sh "ruby -Ilib:test \"#{path}\""
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class DocsCommands < Tasks
|
|
22
|
+
desc "Build YARD + MkDocs documentation"
|
|
23
|
+
def build
|
|
24
|
+
sh "bundle exec rake docs:build"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc "Serve MkDocs documentation at http://localhost:8000"
|
|
28
|
+
def serve
|
|
29
|
+
sh "mkdocs serve"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
desc "Serve YARD documentation locally"
|
|
33
|
+
def yard
|
|
34
|
+
sh "yard server --reload"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class ExamplesCommands < Tasks
|
|
39
|
+
desc "Run all examples (skips standalone apps and external-service examples)"
|
|
40
|
+
def all
|
|
41
|
+
sh "bundle exec rake examples:all"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc "exec N", "Run a specific example by number (e.g. asgard examples exec 3)"
|
|
45
|
+
def exec(num)
|
|
46
|
+
sh "bundle exec rake examples:run[#{num}]"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
desc "Start the Rails demo app (example 18)"
|
|
50
|
+
def rails
|
|
51
|
+
sh "bundle exec rake examples:rails"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc "docs SUBCOMMAND", "Documentation tasks (build, serve, yard)"
|
|
56
|
+
subcommand "docs", DocsCommands
|
|
57
|
+
|
|
58
|
+
desc "examples SUBCOMMAND", "Example tasks (all, exec N, rails)"
|
|
59
|
+
subcommand "examples", ExamplesCommands
|
|
60
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
4 lib/robot_lab.rb
|
|
2
|
+
1 lib/robot_lab/agent_skill_catalog.rb
|
|
3
|
+
2 lib/robot_lab/ask_user.rb
|
|
4
|
+
2 lib/robot_lab/bus_poller.rb
|
|
5
|
+
4 lib/robot_lab/capabilities.rb
|
|
6
|
+
8 lib/robot_lab/config.rb
|
|
7
|
+
3 lib/robot_lab/doom_loop_detector.rb
|
|
8
|
+
6 lib/robot_lab/history_compressor.rb
|
|
9
|
+
1 lib/robot_lab/hook.rb
|
|
10
|
+
4 lib/robot_lab/hook_context.rb
|
|
11
|
+
1 lib/robot_lab/hook_registry.rb
|
|
12
|
+
4 lib/robot_lab/hooks.rb
|
|
13
|
+
3 lib/robot_lab/mcp/client.rb
|
|
14
|
+
6 lib/robot_lab/mcp/connection_poller.rb
|
|
15
|
+
1 lib/robot_lab/mcp/server_discovery.rb
|
|
16
|
+
3 lib/robot_lab/mcp/transports/sse.rb
|
|
17
|
+
6 lib/robot_lab/mcp/transports/stdio.rb
|
|
18
|
+
4 lib/robot_lab/mcp/transports/streamable_http.rb
|
|
19
|
+
3 lib/robot_lab/mcp/transports/websocket.rb
|
|
20
|
+
16 lib/robot_lab/memory.rb
|
|
21
|
+
1 lib/robot_lab/memory_change.rb
|
|
22
|
+
3 lib/robot_lab/message.rb
|
|
23
|
+
8 lib/robot_lab/network.rb
|
|
24
|
+
70 lib/robot_lab/robot.rb
|
|
25
|
+
2 lib/robot_lab/robot/agent_skill_matching.rb
|
|
26
|
+
4 lib/robot_lab/robot/bus_messaging.rb
|
|
27
|
+
2 lib/robot_lab/robot/history_search.rb
|
|
28
|
+
2 lib/robot_lab/robot/hooking.rb
|
|
29
|
+
9 lib/robot_lab/robot/mcp_management.rb
|
|
30
|
+
8 lib/robot_lab/robot/template_rendering.rb
|
|
31
|
+
4 lib/robot_lab/robot_result.rb
|
|
32
|
+
5 lib/robot_lab/run_config.rb
|
|
33
|
+
1 lib/robot_lab/sandbox.rb
|
|
34
|
+
1 lib/robot_lab/sandbox/seatbelt.rb
|
|
35
|
+
3 lib/robot_lab/script_tool.rb
|
|
36
|
+
1 lib/robot_lab/state_proxy.rb
|
|
37
|
+
1 lib/robot_lab/streaming/context.rb
|
|
38
|
+
1 lib/robot_lab/streaming/events.rb
|
|
39
|
+
2 lib/robot_lab/task.rb
|
|
40
|
+
6 lib/robot_lab/tool.rb
|
|
41
|
+
2 lib/robot_lab/user_message.rb
|
|
42
|
+
2 lib/robot_lab/utils.rb
|
|
43
|
+
2 lib/robot_lab/waiter.rb
|
data/.rubocop.yml
CHANGED
|
@@ -1,172 +1,10 @@
|
|
|
1
|
+
inherit_from: ../.rubocop-base.yml
|
|
2
|
+
|
|
3
|
+
# Core-only overrides. Exclude patterns here are relative to this repo and
|
|
4
|
+
# merge with the shared base's excludes.
|
|
1
5
|
AllCops:
|
|
2
|
-
NewCops: enable
|
|
3
|
-
SuggestExtensions: false
|
|
4
|
-
TargetRubyVersion: 4.0
|
|
5
6
|
Exclude:
|
|
6
|
-
- 'examples/**/*'
|
|
7
|
-
- 'vendor/**/*'
|
|
8
|
-
- 'dead_code/**/*'
|
|
9
7
|
- '_notes/**/*'
|
|
10
8
|
|
|
11
|
-
# ── Style: disabled cops ───────────────────────────────────────────────────
|
|
12
|
-
Style/StringLiterals:
|
|
13
|
-
Enabled: false
|
|
14
|
-
|
|
15
|
-
Style/StringLiteralsInInterpolation:
|
|
16
|
-
Enabled: false
|
|
17
|
-
|
|
18
|
-
Style/Documentation:
|
|
19
|
-
Enabled: false
|
|
20
|
-
|
|
21
|
-
# Ruby 4.0 freezes string literals by default
|
|
22
|
-
Style/FrozenStringLiteralComment:
|
|
23
|
-
Enabled: false
|
|
24
|
-
|
|
25
|
-
Style/IfUnlessModifier:
|
|
26
|
-
Enabled: false
|
|
27
|
-
|
|
28
|
-
Style/RescueModifier:
|
|
29
|
-
Enabled: false
|
|
30
|
-
|
|
31
|
-
Style/TrivialAccessors:
|
|
32
|
-
Enabled: false
|
|
33
|
-
|
|
34
|
-
Style/MultilineTernaryOperator:
|
|
35
|
-
Enabled: false
|
|
36
|
-
|
|
37
|
-
Style/SafeNavigation:
|
|
38
|
-
Enabled: false
|
|
39
|
-
|
|
40
|
-
Style/EmptyClassDefinition:
|
|
41
|
-
Enabled: false
|
|
42
|
-
|
|
43
|
-
Style/ClassAndModuleChildren:
|
|
44
|
-
Enabled: false
|
|
45
|
-
|
|
46
|
-
Style/RescueStandardError:
|
|
47
|
-
Enabled: false
|
|
48
|
-
|
|
49
|
-
Style/OneClassPerFile:
|
|
50
|
-
Enabled: false
|
|
51
|
-
|
|
52
|
-
# Both % and format/sprintf are acceptable
|
|
53
|
-
Style/FormatString:
|
|
54
|
-
Enabled: false
|
|
55
|
-
|
|
56
|
-
# String concatenation and interpolation are both acceptable
|
|
57
|
-
Style/StringConcatenation:
|
|
58
|
-
Enabled: false
|
|
59
|
-
|
|
60
|
-
# ── Layout ─────────────────────────────────────────────────────────────────
|
|
61
|
-
Layout/LineLength:
|
|
62
|
-
Max: 140
|
|
63
|
-
|
|
64
|
-
Layout/ExtraSpacing:
|
|
65
|
-
Enabled: false
|
|
66
|
-
|
|
67
|
-
Layout/HashAlignment:
|
|
68
|
-
Enabled: false
|
|
69
|
-
|
|
70
|
-
Layout/FirstHashElementIndentation:
|
|
71
|
-
Enabled: false
|
|
72
|
-
|
|
73
|
-
Layout/EmptyLineAfterGuardClause:
|
|
74
|
-
Enabled: false
|
|
75
|
-
|
|
76
|
-
# ── Naming ─────────────────────────────────────────────────────────────────
|
|
77
|
-
# Single-char params (c, e, n) are acceptable throughout
|
|
78
|
-
Naming/MethodParameterName:
|
|
79
|
-
Enabled: false
|
|
80
|
-
|
|
81
|
-
Naming/VariableNumber:
|
|
82
|
-
Exclude:
|
|
83
|
-
- 'test/**/*'
|
|
84
|
-
|
|
85
|
-
Naming/RescuedExceptionsVariableName:
|
|
86
|
-
Enabled: false
|
|
87
|
-
|
|
88
|
-
# set_results and similar explicit setters are clear and conventional
|
|
89
|
-
Naming/AccessorMethodName:
|
|
90
|
-
Enabled: false
|
|
91
|
-
|
|
92
|
-
# has_tool_calls? and similar are clear and conventional
|
|
93
|
-
Naming/PredicatePrefix:
|
|
94
|
-
Enabled: false
|
|
95
|
-
|
|
96
|
-
# Test helper methods don't need to follow predicate naming rules
|
|
97
|
-
Naming/PredicateMethod:
|
|
98
|
-
Exclude:
|
|
99
|
-
- 'test/**/*'
|
|
100
|
-
|
|
101
|
-
# ── Lint: relax noisy cops on intentional patterns ─────────────────────────
|
|
102
|
-
Lint/EmptyBlock:
|
|
103
|
-
Exclude:
|
|
104
|
-
- 'test/**/*'
|
|
105
|
-
|
|
106
|
-
# Library and framework methods commonly accept args for API/documentation purposes
|
|
107
|
-
Lint/UnusedMethodArgument:
|
|
108
|
-
Enabled: false
|
|
109
|
-
|
|
110
|
-
Lint/ConstantDefinitionInBlock:
|
|
111
|
-
Exclude:
|
|
112
|
-
- 'Rakefile'
|
|
113
|
-
- 'test/**/*'
|
|
114
|
-
|
|
115
|
-
# ── Gemspec ────────────────────────────────────────────────────────────────
|
|
116
|
-
Gemspec/DevelopmentDependencies:
|
|
117
|
-
EnforcedStyle: Gemfile
|
|
118
|
-
|
|
119
|
-
Gemspec/RequiredRubyVersion:
|
|
120
|
-
Enabled: false
|
|
121
|
-
|
|
122
|
-
Gemspec/OrderedDependencies:
|
|
123
|
-
Enabled: false
|
|
124
|
-
|
|
125
|
-
# ── Metrics ────────────────────────────────────────────────────────────────
|
|
126
|
-
# Framework-level code (routers, parsers, orchestrators) is inherently complex.
|
|
127
|
-
# Flog is the primary complexity gate — these RuboCop thresholds catch only
|
|
128
|
-
# egregious outliers without false-positiving every dispatch method.
|
|
129
|
-
|
|
130
|
-
Metrics/MethodLength:
|
|
131
|
-
Max: 35
|
|
132
|
-
CountAsOne:
|
|
133
|
-
- heredoc
|
|
134
|
-
- array
|
|
135
|
-
- hash
|
|
136
|
-
Exclude:
|
|
137
|
-
- 'test/**/*'
|
|
138
|
-
|
|
139
|
-
Metrics/AbcSize:
|
|
140
|
-
Max: 40
|
|
141
|
-
Exclude:
|
|
142
|
-
- 'test/**/*'
|
|
143
|
-
|
|
144
9
|
Metrics/ClassLength:
|
|
145
|
-
Max:
|
|
146
|
-
Exclude:
|
|
147
|
-
- 'test/**/*'
|
|
148
|
-
|
|
149
|
-
Metrics/ModuleLength:
|
|
150
|
-
Max: 200
|
|
151
|
-
Exclude:
|
|
152
|
-
- 'test/**/*'
|
|
153
|
-
|
|
154
|
-
Metrics/CyclomaticComplexity:
|
|
155
|
-
Max: 20
|
|
156
|
-
Exclude:
|
|
157
|
-
- 'test/**/*'
|
|
158
|
-
|
|
159
|
-
Metrics/PerceivedComplexity:
|
|
160
|
-
Max: 20
|
|
161
|
-
Exclude:
|
|
162
|
-
- 'test/**/*'
|
|
163
|
-
|
|
164
|
-
# Long method signatures with keyword args are a Ruby framework idiom
|
|
165
|
-
Metrics/ParameterLists:
|
|
166
|
-
Enabled: false
|
|
167
|
-
|
|
168
|
-
Metrics/BlockLength:
|
|
169
|
-
Exclude:
|
|
170
|
-
- 'Rakefile'
|
|
171
|
-
- '*.gemspec'
|
|
172
|
-
- 'test/**/*'
|
|
10
|
+
Max: 650 # Robot is the central class; a future refactor could split it further
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
### Added
|
|
12
|
+
- `BusMessaging#respond_to_tasks` and `#serve` — a member auto-answers inbound
|
|
13
|
+
(non-reply) bus tasks and replies to the sender. `serve` runs each task through
|
|
14
|
+
`#run`; `respond_to_tasks { |m| ... }` is the generic form. Makes a Robot a
|
|
15
|
+
first-class bus responder (symmetric with how a Cyborg answers its human)
|
|
16
|
+
without hand-wiring `on_message`.
|
|
17
|
+
- `.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
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- `BusMessaging` now synchronizes its message counter and outbox with a mutex, so
|
|
21
|
+
concurrent sends and poller-thread reply correlation can't clobber each other.
|
|
22
|
+
- `flay_check` Rake task: structural code duplication gate (mass threshold 50); integrated into the `quality` Rake task
|
|
23
|
+
- `flay` gem added to development dependencies
|
|
24
|
+
- `test_output.txt`, `flay_output.txt`, `flog_output.txt`, and `rubocop_output.txt` added to `.gitignore`
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- `test/test_helper.rb`: test output redirected to `test_output.txt` via `$stdout` reassignment; `TerminalSummaryReporter` prints a single PASS/FAIL summary line to the terminal
|
|
28
|
+
- `Rakefile`: `rubocop` and `rubocop_fix` tasks removed (now owned by Asgard); `flay_check` integrated into the `quality` gate
|
|
29
|
+
|
|
30
|
+
## [0.2.6] - 2026-05-23
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **`:compaction` hook family** — fires when `maybe_compact` determines that conversation history should be compressed. Provides `before_compaction`, `around_compaction`, `after_compaction`, and `on_compaction` callbacks.
|
|
35
|
+
- `on_compaction` allows an extension to supply a complete replacement message set via `ctx.compacted_messages`; when set, the core skips its own compaction strategy entirely (`ctx.handled?` returns true).
|
|
36
|
+
- `CompactionHookContext` — carries `robot`, `messages_before` (frozen snapshot), `config`, `strategy` (`:context_window`, `:custom`, or other symbol), `compacted_messages`, and `handled?`.
|
|
37
|
+
- **`:learn` hook family** — fires on every `robot.learn(text)` call with non-empty text. Provides `before_learn`, `around_learn`, `after_learn`, and `on_learn` callbacks.
|
|
38
|
+
- `on_learn` fires after the text has been stored and `ctx.stored = true`, giving extensions a reliable hook point for cross-session persistence.
|
|
39
|
+
- `LearnHookContext` — carries `robot`, `text`, `learnings_before` (frozen snapshot), and `stored`.
|
|
40
|
+
- **`over_compact_threshold?`** private predicate extracted from `compact_if_over_context_window` for independent testability.
|
|
41
|
+
- Hook tests for both new families added to `test/robot_lab/hooks_test.rb` (18 new tests across `RobotLabCompactionHooksTest` and `RobotLabLearnHooksTest`).
|
|
42
|
+
- Documentation for `:compaction` and `:learn` hook families added to `docs/guides/hooks.md`.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **`Robot#on`** now accepts and forwards `context:` to `HookRegistry#on`, allowing extensions to pass per-registration domain config at registration time (e.g. `robot.on(MyHook, context: { domain: "finance" })`). Previously `context:` was silently dropped.
|
|
47
|
+
- **`maybe_compact`** refactored to dispatch through the `:compaction` hook family and delegate to `on_compaction` before falling back to the built-in strategy.
|
|
48
|
+
- **`Robot#learn`** refactored to dispatch through the `:learn` hook family; `on_learn` fires after deduplication with `ctx.stored` reflecting whether the text was actually added.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- **`RobotLab::Hook` base class** (`lib/robot_lab/hook.rb`) — all hook handlers inherit from this class. Subclasses define lifecycle callbacks as `class << self` methods (`before_run`, `after_run`, etc.). Namespace is auto-derived from the class name via snake_case conversion of the final class name segment (e.g. `AuditHook` → `:audit_hook`); override with `self.namespace = :custom`. Ractor-safe by design: handler classes are Ruby constants, not Procs, so registrations are natively serializable across Ractor boundaries.
|
|
53
|
+
- **Hook system** — lifecycle hooks across all execution boundaries. Register handler classes with `RobotLab.on`, `network.on`, or `robot.on` for five families: `:run`, `:llm_generation`, `:tool_call`, `:network_run`, and `:task`, each with `before_*`, `around_*`, `after_*`, and (where applicable) `on_error` variants.
|
|
54
|
+
- `HookRegistry::Registration` — `Data.define(:handler_class, :context)`; stores a handler class reference and optional per-registration default `DotState` values. No Proc, no namespace field — namespace is read from the handler class.
|
|
55
|
+
- `HookContext` and five typed subclasses: `RunHookContext`, `LlmGenerationHookContext`, `ToolCallHookContext`, `NetworkRunHookContext`, `TaskHookContext` — unchanged
|
|
56
|
+
- `DotState` / `ExtensionState` — namespace-isolated dot-access state carried on every context object; `ctx.local` reads/writes state for the active handler's namespace
|
|
57
|
+
- `context:` parameter on `on(handler_class, context: {...})` — sets per-registration default `DotState` values merged in before each callback fires
|
|
58
|
+
- Around hooks chain correctly across handler class registrations; return value is propagated up the chain
|
|
59
|
+
- Per-run hooks via `robot.run("msg", hooks: MyHook)` or `robot.run("msg", hooks: [MyHook, OtherHook])`
|
|
60
|
+
- `on_error` fires for `:run`, `:network_run`, and `:task` families; exception is re-raised after all error handlers complete
|
|
61
|
+
- **`examples/xyzzy.rb`** — updated reference extension: `RobotLab::Xyzzy < RobotLab::Hook` with `class << self` methods for every hook family; logs each callback with a timestamped stdout tagline and a structured logger context snapshot; registered globally with `RobotLab.on(RobotLab::Xyzzy)`
|
|
62
|
+
- **Example 35** (`examples/35_hooks.rb`) — updated full hook pipeline demo: xyzzy extension tracing all hooks, `around_run` perf timer handler class, `around_llm_generation` response cache handler class (cache hits skip the LLM entirely), `before/after_llm_generation` tracer, tool call hooks, network/task hooks, and `on_error`
|
|
63
|
+
- **`examples/common.rb`** — added explicit `openai_api_key`, `openai_organization_id`, and `openai_project_id` to `RubyLLM.configure` to match provider configurator expectations; updated default model to `gpt-4.1-mini`
|
|
64
|
+
|
|
11
65
|
## [0.2.1] - 2026-05-19
|
|
12
66
|
|
|
13
67
|
### Added
|
data/README.md
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
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>
|
|
28
|
+
- <strong>Hook System</strong> - Lifecycle hooks across every execution boundary for instrumentation, caching, and extensions<br>
|
|
28
29
|
- <strong>Layered Configuration</strong> - Cascading YAML, env vars, and RunConfig<br>
|
|
29
30
|
- <strong>Rails Integration</strong> - Generators, background jobs, Turbo Stream broadcasting (via <a href="https://github.com/MadBomber/robot_lab-rails">robot_lab-rails</a>)<br>
|
|
30
31
|
- <strong>Token & Cost Tracking</strong> - Per-run and cumulative token counts on every robot<br>
|
|
@@ -876,6 +877,66 @@ future.robot_name # => "analyst"
|
|
|
876
877
|
future.delegated_by # => "manager"
|
|
877
878
|
```
|
|
878
879
|
|
|
880
|
+
## Hook System
|
|
881
|
+
|
|
882
|
+
RobotLab's hook system lets you intercept any point in a robot's execution pipeline without modifying framework code. Hooks use handler classes — subclasses of `RobotLab::Hook` that define lifecycle callbacks as class methods.
|
|
883
|
+
|
|
884
|
+
```ruby
|
|
885
|
+
class TimerHook < RobotLab::Hook
|
|
886
|
+
# namespace is auto-derived: TimerHook → :timer_hook
|
|
887
|
+
# override with: self.namespace = :timer
|
|
888
|
+
|
|
889
|
+
def self.before_run(ctx)
|
|
890
|
+
ctx.local.start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
def self.after_run(ctx)
|
|
894
|
+
elapsed = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - ctx.local.start) * 1000).round(1)
|
|
895
|
+
puts "[timer] #{ctx.robot.name} #{elapsed}ms"
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
def self.on_error(ctx)
|
|
899
|
+
puts "[timer] #{ctx.robot.name} failed: #{ctx.error.message}"
|
|
900
|
+
end
|
|
901
|
+
end
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
Register at the global, network, or robot level — or for a single call:
|
|
905
|
+
|
|
906
|
+
```ruby
|
|
907
|
+
RobotLab.on(TimerHook) # every robot in this process
|
|
908
|
+
network.on(TimerHook) # robots inside this network only
|
|
909
|
+
robot.on(TimerHook) # this robot only
|
|
910
|
+
robot.run("msg", hooks: TimerHook) # this call only
|
|
911
|
+
robot.run("msg", hooks: [TimerHook, OtherHook])
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
Pass `context:` to set default `DotState` values for the handler's namespace before each callback fires:
|
|
915
|
+
|
|
916
|
+
```ruby
|
|
917
|
+
RobotLab.on(TimerHook, context: { threshold_ms: 500 })
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
Around hooks receive a block and must call it — and return its value — so the actual work executes:
|
|
921
|
+
|
|
922
|
+
```ruby
|
|
923
|
+
class PerfHook < RobotLab::Hook
|
|
924
|
+
self.namespace = :perf
|
|
925
|
+
|
|
926
|
+
def self.around_run(ctx, &block)
|
|
927
|
+
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
928
|
+
result = block.call # executes the run
|
|
929
|
+
ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000).round(1)
|
|
930
|
+
$stderr.puts "[perf] #{ctx.robot.name} #{ms}ms"
|
|
931
|
+
result # must return the result
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
Because handler classes are Ruby constants (not Procs), all hook registrations are natively Ractor-serializable.
|
|
937
|
+
|
|
938
|
+
See the [full Hook System guide](docs/guides/hooks.md) for all hook families, context objects, and extension patterns.
|
|
939
|
+
|
|
879
940
|
## Extension Gems
|
|
880
941
|
|
|
881
942
|
RobotLab's optional capabilities are packaged as separate gems:
|
data/Rakefile
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
4
|
require "rake/testtask"
|
|
5
5
|
|
|
6
|
+
RUBOCOP_ENV = { "RUBOCOP_CACHE_ROOT" => "tmp/rubocop_cache" }.freeze
|
|
7
|
+
|
|
6
8
|
Rake::TestTask.new(:test) do |t|
|
|
7
9
|
t.libs << "test"
|
|
8
10
|
t.libs << "lib"
|
|
@@ -34,16 +36,6 @@ Rake::TestTask.new(:integration) do |t|
|
|
|
34
36
|
t.ruby_opts << "-rtest_helper"
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
desc "Check code style with RuboCop"
|
|
38
|
-
task :rubocop do
|
|
39
|
-
sh "bundle exec rubocop"
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
desc "Auto-correct RuboCop offenses"
|
|
43
|
-
task :rubocop_fix do
|
|
44
|
-
sh "bundle exec rubocop -a"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
39
|
desc "Check code complexity with Flog (warn ≥20, fail ≥50)"
|
|
48
40
|
task :flog_check do
|
|
49
41
|
require 'flog'
|
|
@@ -82,7 +74,26 @@ task :flog_check do
|
|
|
82
74
|
end
|
|
83
75
|
end
|
|
84
76
|
|
|
85
|
-
desc "
|
|
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"
|
|
86
97
|
task :quality do
|
|
87
98
|
results = {}
|
|
88
99
|
|
|
@@ -94,13 +105,18 @@ task :quality do
|
|
|
94
105
|
puts "\n#{'=' * 60}"
|
|
95
106
|
puts "Quality Gate: RuboCop"
|
|
96
107
|
puts '=' * 60
|
|
97
|
-
results[:rubocop] = system("bundle exec rubocop") ? :pass : :fail
|
|
108
|
+
results[:rubocop] = system(RUBOCOP_ENV, "bundle exec rubocop") ? :pass : :fail
|
|
98
109
|
|
|
99
110
|
puts "\n#{'=' * 60}"
|
|
100
111
|
puts "Quality Gate: Flog Complexity"
|
|
101
112
|
puts '=' * 60
|
|
102
113
|
results[:flog] = system("bundle exec rake flog_check") ? :pass : :fail
|
|
103
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
|
+
|
|
104
120
|
puts "\n#{'=' * 60}"
|
|
105
121
|
puts "Quality Summary"
|
|
106
122
|
puts '=' * 60
|
data/docs/api/core/index.md
CHANGED
|
@@ -75,6 +75,7 @@ classDiagram
|
|
|
75
75
|
| [AskUser](tool.md#built-in-askuser) | Built-in tool for terminal-based user interaction |
|
|
76
76
|
| [Memory](memory.md) | Reactive key-value store for sharing data |
|
|
77
77
|
| RobotMessage | Typed envelope for bus-based inter-robot communication |
|
|
78
|
+
| `RobotLab::Runnable` | Shared interface (`crew`, `chief`, `run`) implemented by both `Robot` and `Network` — see [Runnable Protocol](../../architecture/core-concepts.md#runnable-protocol) |
|
|
78
79
|
|
|
79
80
|
## Quick Examples
|
|
80
81
|
|
data/docs/api/core/network.md
CHANGED
|
@@ -56,6 +56,9 @@ result = network.run(
|
|
|
56
56
|
**context
|
|
57
57
|
)
|
|
58
58
|
# => SimpleFlow::Result
|
|
59
|
+
|
|
60
|
+
# Runnable protocol: a positional message works too, just like Robot#run
|
|
61
|
+
result = network.run("Help me", customer_id: 123)
|
|
59
62
|
```
|
|
60
63
|
|
|
61
64
|
Execute the network pipeline.
|
|
@@ -64,7 +67,7 @@ Execute the network pipeline.
|
|
|
64
67
|
|
|
65
68
|
| Name | Type | Description |
|
|
66
69
|
|------|------|-------------|
|
|
67
|
-
| `message` | `String` | The input message |
|
|
70
|
+
| `message` | `String`, `nil` | The input message — as the `message:` keyword, or positionally (folded into `message:` when given) |
|
|
68
71
|
| `**context` | `Hash` | Additional context passed to all robots |
|
|
69
72
|
|
|
70
73
|
**Returns:** `SimpleFlow::Result`
|
|
@@ -108,6 +111,15 @@ network.add_robot(robot)
|
|
|
108
111
|
|
|
109
112
|
Add a robot without creating a pipeline task. Useful for robots referenced by other tasks.
|
|
110
113
|
|
|
114
|
+
### remove_robot
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
network.remove_robot("billing")
|
|
118
|
+
# => Robot, or nil if no robot by that name was present
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Remove a dynamically-added robot from the crew by name. Complements `add_robot`. Only removes the robot from `@robots` — it does **not** rewrite the pipeline, so don't remove a robot that is a pipeline task (a `depends_on` reference to a removed robot's task name would then fail).
|
|
122
|
+
|
|
111
123
|
### robot / []
|
|
112
124
|
|
|
113
125
|
```ruby
|
|
@@ -294,6 +306,24 @@ elsif result.context[:technical]
|
|
|
294
306
|
end
|
|
295
307
|
```
|
|
296
308
|
|
|
309
|
+
## Runnable Protocol
|
|
310
|
+
|
|
311
|
+
`Network` includes `RobotLab::Runnable`, the shared interface it has in common with `Robot` — see [Runnable Protocol](../../architecture/network-orchestration.md#runnable-protocol) for the full picture.
|
|
312
|
+
|
|
313
|
+
| Method | Returns |
|
|
314
|
+
|--------|---------|
|
|
315
|
+
| `crew` | `robots.values` — the constituent robots, as an `Array`, in pipeline order |
|
|
316
|
+
| `chief` | `crew.first` — the lead robot |
|
|
317
|
+
| `robot_count` | `crew.size` |
|
|
318
|
+
| `network?` | `true` |
|
|
319
|
+
| `single?` | `false` |
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
network.crew.map(&:name) # => ["classifier", "billing", "technical"]
|
|
323
|
+
network.chief # => the first robot
|
|
324
|
+
network.network? # => true
|
|
325
|
+
```
|
|
326
|
+
|
|
297
327
|
## See Also
|
|
298
328
|
|
|
299
329
|
- [Creating Networks Guide](../../guides/creating-networks.md)
|