robot_lab-a2a 0.2.7 → 0.2.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35293bad787439bd0df0a795e18cc14422c5e1a9bf513e1482c5cafdb52b11e0
4
- data.tar.gz: fc1830e74ae5570420684c3728652241726484cf5c73e8d2b027d7bfb0f20275
3
+ metadata.gz: 6c741a3c521ca8442b326ef4bff336e8bcc5daaab9075055fda53dba9f57a50c
4
+ data.tar.gz: 06e2356682717b7e281fb738be8923e0cf8b0189b341b0aa9d7f9baacc76b8b6
5
5
  SHA512:
6
- metadata.gz: 7e4cd7143ca99b4df4af69ea6a2ab189cc936eb638b5b0237e68d99ed697a82f70afb7a7856609682980c3069c61dbe0a4454f70a2de1c20282fcfa82732317a
7
- data.tar.gz: ef3cd22a82035f60dfb3c491e552d35b3a73ee169e87dad449d9456442c77f9c60ea4fbd8e5180c188f9e256db5c813196aeba27950213f11ef820b20b41d8b0
6
+ metadata.gz: 34c7333a443240f595631ca7299f0ab3f57f87d0542b2a67c20f92909d12459ab98d5eee0e1aa434eee6041793230817e08a099c0db926fa47c956bff412aae9
7
+ data.tar.gz: a982730ee7ab5c12b2b01f6c5ba90fcf550ee2753a01bd9026a73a2658e9f630af6f6217a74c4aa1fa2c10827d37eae521bb3730ece0d002b56ebef154738c29
data/.envrc CHANGED
@@ -1,3 +1,6 @@
1
+ # robot_lab_project/robot_lab-a2a/.envrc
2
+
1
3
  source_up
4
+
2
5
  export RR=`pwd`
3
6
  export BUNDLE_GEMFILE=Gemfile
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.8] - 2026-09-09
4
+
5
+ Released in lockstep with `robot_lab` core v0.2.8: this gem now resolves the released core gem from RubyGems instead of the local sibling checkout (local-path development remains available via `BUNDLE_GEMFILE=Gemfile.local`). Also in this release: reek warnings triaged to zero, and the gem lifecycle tasks (build/install/release) moved from the Rakefile to the asgard task runner.
6
+
3
7
  ### Added
4
8
  - `.loki` Asgard task file: `test`, `rubocop`, `rubocop_fix`, `flog`, `flay`, `quality`, `build`, `install`, `release`, and `console` tasks via the Asgard task runner
5
9
  - `flay_check` Rake task: structural code duplication gate (mass threshold 50); integrated into the `quality` Rake task
data/CLAUDE.md CHANGED
@@ -10,8 +10,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
10
10
 
11
11
  ```bash
12
12
  rake test # Run full test suite (default rake task)
13
- rake build # Build the gem package
14
- rake install # Install gem locally
13
+ asgard build # Build the gem package
14
+ asgard install # Install gem locally
15
15
 
16
16
  bin/setup # Install dependencies
17
17
  bin/console # IRB session with gem loaded
data/README.md CHANGED
@@ -85,8 +85,8 @@ Full documentation lives in [`docs/`](docs/):
85
85
  ```bash
86
86
  bin/setup # install dependencies
87
87
  rake test # run the full test suite
88
- rake quality # static analysis (if configured)
89
- rake build # build the gem package
88
+ asgard quality # static analysis quality gates
89
+ asgard build # build the gem package
90
90
  ```
91
91
 
92
92
  Run a single test file:
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
4
3
  require 'rake/testtask'
5
4
 
6
5
  Rake::TestTask.new(:test) do |t|
@@ -18,6 +18,12 @@ module RobotLab
18
18
 
19
19
  attr_writer :event_queue, :answer_queue
20
20
 
21
+ def initialize(robot: nil)
22
+ super
23
+ @event_queue = nil
24
+ @answer_queue = nil
25
+ end
26
+
21
27
  def execute(question:, choices: nil, default: nil)
22
28
  raise 'A2A queues not injected — use RobotLab::A2A::RobotAdapter' unless @event_queue
23
29
 
@@ -56,20 +56,23 @@ module RobotLab
56
56
  # --- input side (robot.input) ---
57
57
 
58
58
  def gets
59
- prompt_text = @buffer_mutex.synchronize do
60
- text = @output_buffer.dup.strip
61
- @output_buffer.clear
62
- text
63
- end
64
-
65
- prompt = ::A2A::Models::Message.agent(
66
- prompt_text.empty? ? 'Input required:' : prompt_text
67
- )
68
- @event_queue.push({ type: :ask, prompt: prompt })
59
+ prompt = ::A2A::Models::Message.agent(drain_prompt_text)
60
+ @event_queue.push({ type: :ask, prompt: })
69
61
 
70
62
  answer = @answer_queue.pop
71
63
  "#{answer}\n"
72
64
  end
65
+
66
+ private
67
+
68
+ # Atomically empty the output buffer and return its text as the prompt.
69
+ def drain_prompt_text
70
+ @buffer_mutex.synchronize do
71
+ text = @output_buffer.dup.strip
72
+ @output_buffer.clear
73
+ text.empty? ? 'Input required:' : text
74
+ end
75
+ end
73
76
  end
74
77
  end
75
78
  end
@@ -8,6 +8,8 @@ module RobotLab
8
8
  # per-robot queue injection across all network robots, planned for a future
9
9
  # release. Wrap individual robots with RobotAdapter for interactive flows.
10
10
  class NetworkAdapter < ::A2A::Server::AgentExecutor
11
+ # :reek:ControlParameter -- interactive is only validated here; anything
12
+ # but :none is rejected with a pointer to RobotAdapter.
11
13
  def initialize(network, interactive: :none)
12
14
  super()
13
15
  if interactive != :none
@@ -19,6 +21,8 @@ module RobotLab
19
21
  @network = network
20
22
  end
21
23
 
24
+ # :reek:FeatureEnvy -- AgentExecutor protocol method: it drives the task
25
+ # lifecycle (start/complete/emit) on the request context handed to it.
22
26
  def call(context)
23
27
  context.task.start!
24
28
  context.emit_status
@@ -4,6 +4,9 @@ module RobotLab
4
4
  module A2A
5
5
  # Tracks live robot threads for in-progress interactive runs.
6
6
  # Keyed by A2A task_id. Entries are removed when the robot thread finishes.
7
+ #
8
+ # :reek:InstanceVariableAssumption -- @mutex and @entries are class-level
9
+ # singleton state, assigned once in the class body; there is no #initialize.
7
10
  class Registry
8
11
  Entry = Data.define(:thread, :event_queue, :answer_queue)
9
12
 
@@ -16,6 +16,11 @@ module RobotLab
16
16
  #
17
17
  # Interactive modes keep the robot thread alive between A2A INPUT_REQUIRED
18
18
  # and resume. Only works with in-process (Memory) storage.
19
+ #
20
+ # :reek:DataClump -- robot plus its event/answer queue pair thread through
21
+ # the per-mode injection helpers; a wrapper object would outlive its one use.
22
+ # :reek:RepeatedConditional -- @interactive is the adapter's mode switch;
23
+ # run, setup and teardown each dispatch on it once.
19
24
  class RobotAdapter < ::A2A::Server::AgentExecutor
20
25
  VALID_MODES = %i[none a2a_tool io_bridge].freeze
21
26
 
@@ -55,6 +60,8 @@ module RobotLab
55
60
 
56
61
  # ── :none mode ────────────────────────────────────────────
57
62
 
63
+ # :reek:FeatureEnvy -- executor lifecycle: drives start/complete/emit on
64
+ # the request context handed to it by the A2A server.
58
65
  def run_simple(context, input_text)
59
66
  context.task.start!
60
67
  context.emit_status
@@ -70,6 +77,8 @@ module RobotLab
70
77
 
71
78
  # ── interactive modes ─────────────────────────────────────
72
79
 
80
+ # :reek:FeatureEnvy -- resume-vs-fresh dispatch on the request context;
81
+ # the fail/emit calls belong to the A2A task lifecycle, not another home.
73
82
  def run_interactive(context, input_text, task_id)
74
83
  if context.is_a?(::A2A::Server::ResumeContext)
75
84
  entry = Registry.fetch(task_id)
@@ -85,6 +94,8 @@ module RobotLab
85
94
  end
86
95
  end
87
96
 
97
+ # :reek:TooManyStatements -- linear bootstrap of one interactive run:
98
+ # build queues, start task, spawn robot thread, register, then monitor.
88
99
  def start_interactive_run(context, input_text, task_id)
89
100
  event_queue = Queue.new
90
101
  answer_queue = Queue.new
@@ -116,21 +127,20 @@ module RobotLab
116
127
 
117
128
  def monitor_task(context, entry, _task_id)
118
129
  event = entry.event_queue.pop
130
+ task = context.task
119
131
  case event[:type]
120
132
  when :ask
121
- context.task.require_input!(message: event[:prompt])
122
- context.emit_status(final: true)
133
+ task.require_input!(message: event[:prompt])
123
134
  when :done
124
135
  artifact = ::A2A::Models::Artifact.new(
125
136
  parts: [::A2A::Models::Part.text(event[:reply].to_s)],
126
137
  name: 'reply'
127
138
  )
128
- context.task.complete!(artifacts: [artifact])
129
- context.emit_status(final: true)
139
+ task.complete!(artifacts: [artifact])
130
140
  when :error
131
- context.task.fail!(message: event[:error].message)
132
- context.emit_status(final: true)
141
+ task.fail!(message: event[:error].message)
133
142
  end
143
+ context.emit_status(final: true)
134
144
  end
135
145
 
136
146
  # ── interactive setup / teardown ──────────────────────────
@@ -29,9 +29,13 @@ module RobotLab
29
29
  # map "/agents" do
30
30
  # run RobotLab::A2A::Server.new.add_robot(my_robot).to_app
31
31
  # end
32
+ #
33
+ # :reek:DataClump -- name/description/path is the agent-card identity trio;
34
+ # add_robot and add_network resolve caller overrides, build_agent_card consumes.
32
35
  class Server
33
36
  DEFAULT_VERSION = '1.0'
34
37
 
38
+ # :reek:ControlParameter -- nil storage means "default to in-process Memory".
35
39
  def initialize(host: 'localhost', port: 9292, storage: nil, interactive: :none)
36
40
  @host = host
37
41
  @port = port
@@ -47,6 +51,8 @@ module RobotLab
47
51
  # @param description [String, nil] overrides robot.description
48
52
  # @param path [String, nil] URL path prefix (defaults to "/dns-label")
49
53
  # @return [self] for chaining
54
+ # :reek:ControlParameter -- nil name/description/path mean "derive from
55
+ # the robot"; each `||` is a default, not a behavior switch.
50
56
  def add_robot(robot, name: nil, description: nil, path: nil)
51
57
  agent_name = (name || robot.name).to_s
52
58
  agent_desc = description || robot.description || agent_name
@@ -65,6 +71,8 @@ module RobotLab
65
71
  # @param description [String, nil]
66
72
  # @param path [String, nil] URL path prefix (defaults to "/dns-label")
67
73
  # @return [self] for chaining
74
+ # :reek:ControlParameter -- nil description/path mean "derive from name";
75
+ # each `||` is a default, not a behavior switch.
68
76
  def add_network(network, name:, description: nil, path: nil)
69
77
  if @interactive != :none
70
78
  raise ArgumentError,
@@ -88,6 +96,8 @@ module RobotLab
88
96
  end
89
97
 
90
98
  # Return a Rack app for embedding in other servers (e.g. Rails, Puma).
99
+ # :reek:FeatureEnvy -- maps each registered agent config into its
100
+ # simple_a2a rack app; the config hashes are this class's own @agents values.
91
101
  def to_app
92
102
  url_map = @agents.transform_values do |cfg|
93
103
  ::A2A.server(
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RobotLab
4
4
  module A2A
5
- VERSION = '0.2.7'
5
+ VERSION = '0.2.8'
6
6
  end
7
7
  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.2.7
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
@@ -125,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubygems_version: 4.0.19
128
+ rubygems_version: 4.0.20
129
129
  specification_version: 4
130
130
  summary: Agent2Agent (A2A) protocol adapter for RobotLab
131
131
  test_files: []