robot_lab-a2a 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +147 -3
- data/CHANGELOG.md +3 -0
- data/CLAUDE.md +57 -0
- data/Rakefile +13 -1
- data/lib/robot_lab/a2a/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06fea066d630fe600e2f5e4aeebb95ef5d36ce1003a7808cf5a2bb6d2abbe52a
|
|
4
|
+
data.tar.gz: a1825a21bb7ad7fda768df531f376cb877a7b668429ef97a164e168b6cabb599
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c387fb5ab2a628f94327ca5d86b96d1f16413c9aa4d76dfd22ed587a467c2ce9af25f530858ab51de6c1a62b83854f800cd7901a7a8c6d5d87b3d186a2ce469
|
|
7
|
+
data.tar.gz: a8a70420bdb2702311677af2b7ff7e231d6d97013e15d94129b9e48b57038664864cc7c95ed0cfe4b2ba082dfdf640edb37dbf6d7d17ff82ebf6dcdb08f4a06f
|
data/.rubocop.yml
CHANGED
|
@@ -1,29 +1,173 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
NewCops: enable
|
|
3
3
|
SuggestExtensions: false
|
|
4
|
+
TargetRubyVersion: 4.0
|
|
4
5
|
Exclude:
|
|
5
6
|
- 'examples/**/*'
|
|
7
|
+
- 'vendor/**/*'
|
|
8
|
+
- 'dead_code/**/*'
|
|
9
|
+
|
|
10
|
+
# ── Style: disabled cops ───────────────────────────────────────────────────
|
|
11
|
+
Style/StringLiterals:
|
|
12
|
+
Enabled: false
|
|
13
|
+
|
|
14
|
+
Style/StringLiteralsInInterpolation:
|
|
15
|
+
Enabled: false
|
|
6
16
|
|
|
7
17
|
Style/Documentation:
|
|
8
18
|
Enabled: false
|
|
9
19
|
|
|
20
|
+
# Ruby 4.0 freezes string literals by default
|
|
21
|
+
Style/FrozenStringLiteralComment:
|
|
22
|
+
Enabled: false
|
|
23
|
+
|
|
24
|
+
Style/IfUnlessModifier:
|
|
25
|
+
Enabled: false
|
|
26
|
+
|
|
27
|
+
Style/RescueModifier:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Style/TrivialAccessors:
|
|
31
|
+
Enabled: false
|
|
32
|
+
|
|
33
|
+
Style/MultilineTernaryOperator:
|
|
34
|
+
Enabled: false
|
|
35
|
+
|
|
36
|
+
Style/SafeNavigation:
|
|
37
|
+
Enabled: false
|
|
38
|
+
|
|
39
|
+
Style/EmptyClassDefinition:
|
|
40
|
+
Enabled: false
|
|
41
|
+
|
|
42
|
+
Style/ClassAndModuleChildren:
|
|
43
|
+
Enabled: false
|
|
44
|
+
|
|
45
|
+
Style/RescueStandardError:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
Style/OneClassPerFile:
|
|
49
|
+
Enabled: false
|
|
50
|
+
|
|
51
|
+
# Both % and format/sprintf are acceptable
|
|
52
|
+
Style/FormatString:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
# String concatenation and interpolation are both acceptable
|
|
56
|
+
Style/StringConcatenation:
|
|
57
|
+
Enabled: false
|
|
58
|
+
|
|
59
|
+
# ── Layout ─────────────────────────────────────────────────────────────────
|
|
60
|
+
Layout/LineLength:
|
|
61
|
+
Max: 140
|
|
62
|
+
|
|
10
63
|
Layout/ExtraSpacing:
|
|
11
64
|
Enabled: false
|
|
12
65
|
|
|
66
|
+
Layout/HashAlignment:
|
|
67
|
+
Enabled: false
|
|
68
|
+
|
|
69
|
+
Layout/FirstHashElementIndentation:
|
|
70
|
+
Enabled: false
|
|
71
|
+
|
|
72
|
+
Layout/EmptyLineAfterGuardClause:
|
|
73
|
+
Enabled: false
|
|
74
|
+
|
|
75
|
+
# ── Naming ─────────────────────────────────────────────────────────────────
|
|
76
|
+
# Single-char params (c, e, n) are acceptable throughout
|
|
77
|
+
Naming/MethodParameterName:
|
|
78
|
+
Enabled: false
|
|
79
|
+
|
|
80
|
+
Naming/VariableNumber:
|
|
81
|
+
Exclude:
|
|
82
|
+
- 'test/**/*'
|
|
83
|
+
|
|
84
|
+
Naming/RescuedExceptionsVariableName:
|
|
85
|
+
Enabled: false
|
|
86
|
+
|
|
87
|
+
# set_results and similar explicit setters are clear and conventional
|
|
88
|
+
Naming/AccessorMethodName:
|
|
89
|
+
Enabled: false
|
|
90
|
+
|
|
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
|
+
# Library and framework methods commonly accept args for API/documentation purposes
|
|
103
|
+
Lint/UnusedMethodArgument:
|
|
104
|
+
Enabled: false
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
Lint/EmptyBlock:
|
|
108
|
+
Exclude:
|
|
109
|
+
- 'test/**/*'
|
|
110
|
+
|
|
111
|
+
Lint/ConstantDefinitionInBlock:
|
|
112
|
+
Exclude:
|
|
113
|
+
- 'Rakefile'
|
|
114
|
+
- 'test/**/*'
|
|
115
|
+
|
|
116
|
+
# ── Gemspec ────────────────────────────────────────────────────────────────
|
|
117
|
+
Gemspec/DevelopmentDependencies:
|
|
118
|
+
EnforcedStyle: Gemfile
|
|
119
|
+
|
|
120
|
+
Gemspec/RequiredRubyVersion:
|
|
121
|
+
Enabled: false
|
|
122
|
+
|
|
123
|
+
Gemspec/OrderedDependencies:
|
|
124
|
+
Enabled: false
|
|
125
|
+
|
|
126
|
+
# ── Metrics ────────────────────────────────────────────────────────────────
|
|
127
|
+
# Framework-level code (routers, parsers, orchestrators) is inherently complex.
|
|
128
|
+
# Flog is the primary complexity gate — these RuboCop thresholds catch only
|
|
129
|
+
# egregious outliers without false-positiving every dispatch method.
|
|
130
|
+
|
|
13
131
|
Metrics/MethodLength:
|
|
14
|
-
Max:
|
|
132
|
+
Max: 35
|
|
15
133
|
CountAsOne:
|
|
16
134
|
- heredoc
|
|
17
135
|
- array
|
|
18
136
|
- hash
|
|
137
|
+
Exclude:
|
|
138
|
+
- 'test/**/*'
|
|
19
139
|
|
|
20
140
|
Metrics/AbcSize:
|
|
21
|
-
Max:
|
|
141
|
+
Max: 40
|
|
142
|
+
Exclude:
|
|
143
|
+
- 'test/**/*'
|
|
22
144
|
|
|
23
145
|
Metrics/ClassLength:
|
|
24
|
-
Max:
|
|
146
|
+
Max: 600
|
|
147
|
+
Exclude:
|
|
148
|
+
- 'test/**/*'
|
|
149
|
+
|
|
150
|
+
Metrics/ModuleLength:
|
|
151
|
+
Max: 200
|
|
152
|
+
Exclude:
|
|
153
|
+
- 'test/**/*'
|
|
154
|
+
|
|
155
|
+
Metrics/CyclomaticComplexity:
|
|
156
|
+
Max: 20
|
|
157
|
+
Exclude:
|
|
158
|
+
- 'test/**/*'
|
|
159
|
+
|
|
160
|
+
Metrics/PerceivedComplexity:
|
|
161
|
+
Max: 20
|
|
162
|
+
Exclude:
|
|
163
|
+
- 'test/**/*'
|
|
164
|
+
|
|
165
|
+
# Long method signatures with keyword args are a Ruby framework idiom
|
|
166
|
+
Metrics/ParameterLists:
|
|
167
|
+
Enabled: false
|
|
25
168
|
|
|
26
169
|
Metrics/BlockLength:
|
|
27
170
|
Exclude:
|
|
28
171
|
- 'Rakefile'
|
|
29
172
|
- '*.gemspec'
|
|
173
|
+
- 'test/**/*'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.1] - 2026-05-19
|
|
4
|
+
|
|
3
5
|
### Added
|
|
4
6
|
|
|
5
7
|
- Demo examples infrastructure: `examples/run` lifecycle script, shared `examples/common_config.rb`, and three complete demos:
|
|
@@ -13,6 +15,7 @@
|
|
|
13
15
|
### Changed
|
|
14
16
|
|
|
15
17
|
- Interactive mode `:acp_tool` renamed to `:a2a_tool` across all source, tests, and documentation
|
|
18
|
+
- Version synchronized with robot_lab core 0.2.1
|
|
16
19
|
|
|
17
20
|
### Fixed
|
|
18
21
|
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What This Gem Does
|
|
6
|
+
|
|
7
|
+
`robot_lab-a2a` is an Agent2Agent (A2A) protocol adapter that exposes RobotLab robots and networks as A2A agents over HTTP+SSE. It bridges RobotLab's terminal-based `AskUser` tool to A2A's `input_required`/resume lifecycle, enabling multi-turn conversational flows without terminal dependency. It delegates HTTP serving to the `simple_a2a` gem.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
rake test # Run full test suite (default rake task)
|
|
13
|
+
rake build # Build the gem package
|
|
14
|
+
rake install # Install gem locally
|
|
15
|
+
|
|
16
|
+
bin/setup # Install dependencies
|
|
17
|
+
bin/console # IRB session with gem loaded
|
|
18
|
+
|
|
19
|
+
# Run a single test file
|
|
20
|
+
ruby -Ilib -Itest test/robot_lab/test_a2a.rb
|
|
21
|
+
|
|
22
|
+
# Run a single test by name
|
|
23
|
+
ruby -Ilib -Itest test/robot_lab/test_a2a.rb -n test_registry_stores_and_retrieves
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
All source lives under `lib/robot_lab/a2a/`. The gem uses an adapter pattern — the two adapters both implement the `A2A::Server::AgentExecutor` interface with a single `call(context)` method.
|
|
29
|
+
|
|
30
|
+
### Key Abstractions
|
|
31
|
+
|
|
32
|
+
**Registry** (`registry.rb`) — Thread-safe singleton tracking active robot sessions across HTTP requests. Entries are `Data.define(:thread, :event_queue, :answer_queue)` stored by session ID. Required because A2A `tasks/send` resumes an in-flight robot thread.
|
|
33
|
+
|
|
34
|
+
**RobotAdapter** (`robot_adapter.rb`) — Wraps a `RobotLab::Robot` as an executor. Supports three interactive modes:
|
|
35
|
+
- `:none` — Synchronous, robot runs to completion with no input prompts.
|
|
36
|
+
- `:acp_tool` — Injects `AskUserTool` into the robot's `@local_tools`, converting terminal blocking to Queue signaling. Restores original tools on teardown.
|
|
37
|
+
- `:io_bridge` — Replaces the robot's I/O streams with an `IoBridge` instance; works with robots that use `puts`/`gets` directly.
|
|
38
|
+
|
|
39
|
+
**AskUserTool** (`ask_user_tool.rb`) — Drop-in replacement for `RobotLab::AskUser`. On `call`, pushes `{type: :ask, prompt:}` to the event_queue then blocks on answer_queue until the A2A client sends a resume.
|
|
40
|
+
|
|
41
|
+
**IoBridge** (`io_bridge.rb`) — IO-compatible object. Buffers all writes; on `gets`, flushes the buffer as the prompt event and blocks on answer_queue. Enables interactive mode for robots that don't use the AskUser tool directly.
|
|
42
|
+
|
|
43
|
+
**NetworkAdapter** (`network_adapter.rb`) — Wraps a `RobotLab::Network`. Currently supports only `:none` mode; interactive mode per network node is not yet implemented.
|
|
44
|
+
|
|
45
|
+
**Server** (`server.rb`) — Fluent builder. `add_robot` / `add_network` register adapters keyed by DNS-safe path labels (underscores→hyphens, RFC 1123). `run(port:)` starts the HTTP server; `to_app` returns a Rack app for embedding in Rails/Puma.
|
|
46
|
+
|
|
47
|
+
### Thread Safety
|
|
48
|
+
|
|
49
|
+
Interactive modes run each robot on its own Ruby Thread. `Registry` uses a `Mutex`. `AskUserTool` and `IoBridge` use Ruby's `Queue` for producer/consumer coordination between the robot thread and the HTTP request handler.
|
|
50
|
+
|
|
51
|
+
### Extension Registration
|
|
52
|
+
|
|
53
|
+
The gem hooks into RobotLab's extension system at require time (`a2a.rb`) and registers itself as the `:a2a` extension.
|
|
54
|
+
|
|
55
|
+
## Tests
|
|
56
|
+
|
|
57
|
+
Tests live in `test/robot_lab/test_a2a.rb` using Minitest with autorun. The test helper is `test/test_helper.rb`. There is no `.rspec` or RuboCop config — no linter is configured.
|
data/Rakefile
CHANGED
|
@@ -6,7 +6,7 @@ require 'rake/testtask'
|
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
|
7
7
|
t.libs << 'test'
|
|
8
8
|
t.libs << 'lib'
|
|
9
|
-
t.test_files = FileList['test/**/*_test.rb']
|
|
9
|
+
t.test_files = FileList['test/**/*_test.rb', 'test/**/test_*.rb'].exclude('**/*_helper.rb')
|
|
10
10
|
t.verbose = true
|
|
11
11
|
t.ruby_opts << '-rtest_helper'
|
|
12
12
|
end
|
|
@@ -102,3 +102,15 @@ task :quality do
|
|
|
102
102
|
abort "\nQuality gate failed" if results.values.any?(:fail)
|
|
103
103
|
puts "\nAll quality gates passed."
|
|
104
104
|
end
|
|
105
|
+
|
|
106
|
+
namespace :docs do
|
|
107
|
+
desc 'Build MkDocs documentation'
|
|
108
|
+
task :build do
|
|
109
|
+
sh 'mkdocs build'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
desc 'Serve MkDocs documentation locally on http://localhost:8000'
|
|
113
|
+
task :serve do
|
|
114
|
+
sh 'mkdocs serve'
|
|
115
|
+
end
|
|
116
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: robot_lab-a2a
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dewayne VanHoozer
|
|
@@ -27,16 +27,16 @@ dependencies:
|
|
|
27
27
|
name: robot_lab
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
32
|
+
version: 0.2.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version:
|
|
39
|
+
version: 0.2.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: simple_a2a
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -65,6 +65,7 @@ files:
|
|
|
65
65
|
- ".github/workflows/deploy-github-pages.yml"
|
|
66
66
|
- ".rubocop.yml"
|
|
67
67
|
- CHANGELOG.md
|
|
68
|
+
- CLAUDE.md
|
|
68
69
|
- COMMITS.md
|
|
69
70
|
- LICENSE.txt
|
|
70
71
|
- README.md
|