robot_lab 0.3.0 → 0.3.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/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
- data/lib/robot_lab/version.rb +1 -1
- metadata +3 -253
- data/.architecture/AGENTS.md +0 -32
- data/.architecture/config.yml +0 -8
- data/.architecture/members.yml +0 -60
- data/.architecture/reviews/feature-free-will.md +0 -490
- data/.architecture/reviews/overall-codebase.md +0 -427
- data/.claude/settings.local.json +0 -57
- data/.codex/config.toml +0 -2
- data/.envrc +0 -6
- data/.github/workflows/deploy-github-pages.yml +0 -52
- data/.irbrc +0 -6
- data/.loki +0 -65
- data/.quality/reek_baseline.txt +0 -43
- data/.rubocop.yml +0 -10
- data/Archspec.rb +0 -44
- data/CLAUDE.md +0 -141
- data/COMMITS.md +0 -196
- data/Rakefile +0 -193
- data/_typos.toml +0 -21
- data/agent2agent_review.md +0 -192
- data/agentf_improvements.md +0 -253
- data/agents.md +0 -14
- data/docs/api/core/index.md +0 -147
- data/docs/api/core/memory.md +0 -578
- data/docs/api/core/network.md +0 -553
- data/docs/api/core/result.md +0 -211
- data/docs/api/core/robot.md +0 -1667
- data/docs/api/core/state.md +0 -145
- data/docs/api/core/tool.md +0 -520
- data/docs/api/errors.md +0 -323
- data/docs/api/hooks.md +0 -469
- data/docs/api/index.md +0 -137
- data/docs/api/mcp/client.md +0 -387
- data/docs/api/mcp/index.md +0 -267
- data/docs/api/mcp/server.md +0 -252
- data/docs/api/mcp/transports.md +0 -339
- data/docs/api/messages/index.md +0 -126
- data/docs/api/messages/text-message.md +0 -170
- data/docs/api/messages/tool-call-message.md +0 -206
- data/docs/api/messages/tool-result-message.md +0 -242
- data/docs/api/messages/user-message.md +0 -232
- data/docs/api/skills.md +0 -423
- data/docs/api/streaming/context.md +0 -280
- data/docs/api/streaming/events.md +0 -185
- data/docs/api/streaming/index.md +0 -156
- data/docs/api/support.md +0 -846
- data/docs/architecture/core-concepts.md +0 -575
- data/docs/architecture/index.md +0 -252
- data/docs/architecture/message-flow.md +0 -366
- data/docs/architecture/network-orchestration.md +0 -478
- data/docs/architecture/robot-execution.md +0 -396
- data/docs/architecture/state-management.md +0 -410
- data/docs/assets/css/custom.css +0 -56
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +0 -641
- data/docs/examples/basic-chat.md +0 -258
- data/docs/examples/index.md +0 -425
- data/docs/examples/mcp-server.md +0 -403
- data/docs/examples/multi-robot-network.md +0 -476
- data/docs/examples/tool-usage.md +0 -468
- data/docs/getting-started/configuration.md +0 -645
- data/docs/getting-started/index.md +0 -56
- data/docs/getting-started/installation.md +0 -197
- data/docs/getting-started/quick-start.md +0 -293
- data/docs/guides/building-robots.md +0 -1187
- data/docs/guides/creating-networks.md +0 -736
- data/docs/guides/hooks.md +0 -1206
- data/docs/guides/index.md +0 -78
- data/docs/guides/knowledge.md +0 -219
- data/docs/guides/mcp-integration.md +0 -740
- data/docs/guides/memory.md +0 -458
- data/docs/guides/observability.md +0 -733
- data/docs/guides/streaming.md +0 -323
- data/docs/guides/using-tools.md +0 -780
- data/docs/index.md +0 -280
- data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
- data/examples/.envrc +0 -3
- data/examples/01_simple_robot.rb +0 -31
- data/examples/02_tools.rb +0 -109
- data/examples/03_network.rb +0 -118
- data/examples/04_mcp.rb +0 -217
- data/examples/05_streaming.rb +0 -137
- data/examples/06_prompt_templates.rb +0 -325
- data/examples/07_network_memory.rb +0 -313
- data/examples/08_llm_config.rb +0 -162
- data/examples/09_chaining.rb +0 -251
- data/examples/10_memory.rb +0 -315
- data/examples/11_network_introspection.rb +0 -239
- data/examples/12_message_bus.rb +0 -67
- data/examples/13_spawn.rb +0 -85
- data/examples/14_rusty_circuit/.envrc +0 -1
- data/examples/14_rusty_circuit/.gitignore +0 -1
- data/examples/14_rusty_circuit/README.md +0 -1
- data/examples/14_rusty_circuit/comic.rb +0 -149
- data/examples/14_rusty_circuit/display.rb +0 -217
- data/examples/14_rusty_circuit/heckler.rb +0 -65
- data/examples/14_rusty_circuit/open_mic.rb +0 -132
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
- data/examples/14_rusty_circuit/scout.rb +0 -165
- data/examples/14_rusty_circuit/show.log +0 -234
- data/examples/15_memory_network_and_bus/.envrc +0 -1
- data/examples/15_memory_network_and_bus/README.md +0 -66
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
- data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
- data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
- data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
- data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
- data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/memory.json +0 -13
- data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
- data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
- data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
- data/examples/16_writers_room/.envrc +0 -1
- data/examples/16_writers_room/display.rb +0 -158
- data/examples/16_writers_room/output/.gitignore +0 -4
- data/examples/16_writers_room/output/README.md +0 -69
- data/examples/16_writers_room/output/opus_001.md +0 -263
- data/examples/16_writers_room/output/opus_001_notes.log +0 -470
- data/examples/16_writers_room/output/opus_002.md +0 -245
- data/examples/16_writers_room/output/opus_002_notes.log +0 -546
- data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
- data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
- data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
- data/examples/16_writers_room/prompts/writer.md +0 -37
- data/examples/16_writers_room/room.rb +0 -186
- data/examples/16_writers_room/tools.rb +0 -173
- data/examples/16_writers_room/writer.rb +0 -121
- data/examples/16_writers_room/writers_room.rb +0 -256
- data/examples/17_skills.rb +0 -234
- data/examples/18_rails/.envrc +0 -3
- data/examples/18_rails/.gitignore +0 -5
- data/examples/18_rails/Gemfile +0 -11
- data/examples/18_rails/README.md +0 -67
- data/examples/18_rails/Rakefile +0 -4
- data/examples/18_rails/app/controllers/application_controller.rb +0 -4
- data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
- data/examples/18_rails/app/jobs/application_job.rb +0 -4
- data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
- data/examples/18_rails/app/models/application_record.rb +0 -5
- data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
- data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
- data/examples/18_rails/app/robots/chat_robot.rb +0 -25
- data/examples/18_rails/app/tools/time_tool.rb +0 -9
- data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
- data/examples/18_rails/app/views/chat/index.html.erb +0 -67
- data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
- data/examples/18_rails/bin/dev +0 -7
- data/examples/18_rails/bin/rails +0 -6
- data/examples/18_rails/bin/setup +0 -15
- data/examples/18_rails/config/application.rb +0 -33
- data/examples/18_rails/config/cable.yml +0 -2
- data/examples/18_rails/config/database.yml +0 -5
- data/examples/18_rails/config/environment.rb +0 -4
- data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
- data/examples/18_rails/config/routes.rb +0 -6
- data/examples/18_rails/config.ru +0 -4
- data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
- data/examples/19_token_tracking.rb +0 -138
- data/examples/20_circuit_breaker.rb +0 -147
- data/examples/21_learning_loop.rb +0 -181
- data/examples/22_context_compression.rb +0 -172
- data/examples/23_convergence.rb +0 -133
- data/examples/24_structured_delegation.rb +0 -151
- data/examples/25_history_search/conversation.jsonl +0 -30
- data/examples/25_history_search.rb +0 -133
- data/examples/26_document_store/api_versioning_adr.md +0 -52
- data/examples/26_document_store/incident_postmortem.md +0 -46
- data/examples/26_document_store/postgres_runbook.md +0 -49
- data/examples/26_document_store/redis_caching_guide.md +0 -48
- data/examples/26_document_store/sidekiq_guide.md +0 -51
- data/examples/26_document_store.rb +0 -140
- data/examples/27_incident_response/README.md +0 -65
- data/examples/27_incident_response/incident_response.rb +0 -261
- data/examples/28_mcp_discovery.rb +0 -113
- data/examples/29_ractor_tools.rb +0 -240
- data/examples/30_ractor_network.rb +0 -252
- data/examples/31_launch_assessment.rb +0 -246
- data/examples/32_newsletter_reader.rb +0 -193
- data/examples/33_stock_generator.rb +0 -80
- data/examples/33_stock_predictor.rb +0 -327
- data/examples/34_agentskills.rb +0 -76
- data/examples/35_hooks.rb +0 -266
- data/examples/README.md +0 -550
- data/examples/common.rb +0 -180
- data/examples/prompts/assistant.md +0 -5
- data/examples/prompts/audit_trail.md +0 -8
- data/examples/prompts/billing.md +0 -10
- data/examples/prompts/classifier.md +0 -7
- data/examples/prompts/comedian.md +0 -6
- data/examples/prompts/comedy_critic.md +0 -10
- data/examples/prompts/configurable.md +0 -9
- data/examples/prompts/dispatcher.md +0 -12
- data/examples/prompts/entity_extractor.md +0 -14
- data/examples/prompts/escalation.md +0 -42
- data/examples/prompts/frontmatter_mcp_test.md +0 -9
- data/examples/prompts/frontmatter_named_test.md +0 -5
- data/examples/prompts/frontmatter_tools_test.md +0 -6
- data/examples/prompts/general.md +0 -7
- data/examples/prompts/github_assistant.md +0 -14
- data/examples/prompts/helper.md +0 -4
- data/examples/prompts/incident_responder.md +0 -18
- data/examples/prompts/keyword_extractor.md +0 -11
- data/examples/prompts/llm_config_demo.md +0 -20
- data/examples/prompts/order_support.md +0 -35
- data/examples/prompts/os_advocate.md +0 -13
- data/examples/prompts/os_chief.md +0 -13
- data/examples/prompts/os_editor.md +0 -13
- data/examples/prompts/parameterized_main_test.md +0 -6
- data/examples/prompts/pii_redactor.md +0 -7
- data/examples/prompts/product_support.md +0 -37
- data/examples/prompts/runbook_protocol.md +0 -12
- data/examples/prompts/sentiment_analyzer.md +0 -12
- data/examples/prompts/skill_a_test.md +0 -4
- data/examples/prompts/skill_b_test.md +0 -4
- data/examples/prompts/skill_config_test.md +0 -5
- data/examples/prompts/skill_cycle_a_test.md +0 -6
- data/examples/prompts/skill_cycle_b_test.md +0 -6
- data/examples/prompts/skill_description_test.md +0 -4
- data/examples/prompts/skill_leaf_test.md +0 -4
- data/examples/prompts/skill_nested_test.md +0 -6
- data/examples/prompts/skill_refs_main_test.md +0 -6
- data/examples/prompts/skill_self_ref_test.md +0 -6
- data/examples/prompts/skill_with_mcp_test.md +0 -9
- data/examples/prompts/skill_with_params_test.md +0 -6
- data/examples/prompts/skill_with_robot_name_test.md +0 -5
- data/examples/prompts/skill_with_tools_test.md +0 -6
- data/examples/prompts/sre_compliance.md +0 -10
- data/examples/prompts/structured_output.md +0 -7
- data/examples/prompts/synthesizer.md +0 -17
- data/examples/prompts/technical.md +0 -10
- data/examples/prompts/template_with_skills_test.md +0 -6
- data/examples/prompts/triage.md +0 -22
- data/examples/ruboruby.md +0 -423
- data/examples/run_all.rb +0 -60
- data/examples/xyzzy.rb +0 -97
- data/logfile +0 -8
- data/mkdocs.yml +0 -209
- data/robot_concurrency.md +0 -38
- data/simple_acp_review.md +0 -298
- data/temp.md +0 -6
- data/tool_manifest_plan.md +0 -155
data/examples/xyzzy.rb
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# xyzzy.rb — single-file RobotLab hook extension demo
|
|
4
|
-
#
|
|
5
|
-
# Demonstrates the Hook handler class pattern: implement class methods for
|
|
6
|
-
# every hook under a dedicated namespace and log each callback with its
|
|
7
|
-
# context snapshot. Useful as a live trace of the full hook pipeline during
|
|
8
|
-
# development.
|
|
9
|
-
#
|
|
10
|
-
# Fully Ractor-safe: no class-level mutable state. LOG_PATH is a frozen
|
|
11
|
-
# string constant (shareable); each call opens its own file handle.
|
|
12
|
-
#
|
|
13
|
-
# stdout : one tagline per hook call → [xyzzy] HH:MM:SS.mmm hook_name
|
|
14
|
-
# logfile: full context snapshot written to LOG_PATH via PP.pp
|
|
15
|
-
#
|
|
16
|
-
# The log destination is read from XYZZY_LOG_PATH at load time and frozen
|
|
17
|
-
# into a constant. Set it BEFORE requiring this file to redirect the log;
|
|
18
|
-
# there is deliberately no writer method, so the constant stays shareable.
|
|
19
|
-
#
|
|
20
|
-
# Usage (from any example that requires common):
|
|
21
|
-
# ENV["XYZZY_LOG_PATH"] = "/tmp/xyzzy.log" # optional, must precede require
|
|
22
|
-
# require_relative "xyzzy"
|
|
23
|
-
|
|
24
|
-
require "pp"
|
|
25
|
-
require "fileutils"
|
|
26
|
-
|
|
27
|
-
module RobotLab
|
|
28
|
-
class Xyzzy < Hook
|
|
29
|
-
self.namespace = :xyzzy
|
|
30
|
-
|
|
31
|
-
LOG_PATH = File.expand_path(
|
|
32
|
-
ENV.fetch("XYZZY_LOG_PATH", "~/.robot_lab/xyzzy_hooks.log")
|
|
33
|
-
).freeze
|
|
34
|
-
FileUtils.mkdir_p(File.dirname(LOG_PATH))
|
|
35
|
-
|
|
36
|
-
class << self
|
|
37
|
-
def before_run(ctx) = log_hook(:before_run, ctx)
|
|
38
|
-
def after_run(ctx) = log_hook(:after_run, ctx)
|
|
39
|
-
def on_error(ctx) = log_hook(:on_error, ctx)
|
|
40
|
-
def before_llm_generation(ctx) = log_hook(:before_llm_generation, ctx)
|
|
41
|
-
def after_llm_generation(ctx) = log_hook(:after_llm_generation, ctx)
|
|
42
|
-
def before_tool_call(ctx) = log_hook(:before_tool_call, ctx)
|
|
43
|
-
def after_tool_call(ctx) = log_hook(:after_tool_call, ctx)
|
|
44
|
-
def before_network_run(ctx) = log_hook(:before_network_run, ctx)
|
|
45
|
-
def after_network_run(ctx) = log_hook(:after_network_run, ctx)
|
|
46
|
-
def before_task(ctx) = log_hook(:before_task, ctx)
|
|
47
|
-
def after_task(ctx) = log_hook(:after_task, ctx)
|
|
48
|
-
|
|
49
|
-
def around_run(ctx, &block)
|
|
50
|
-
log_hook(:around_run, ctx)
|
|
51
|
-
block.call
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def around_llm_generation(ctx, &block)
|
|
55
|
-
log_hook(:around_llm_generation, ctx)
|
|
56
|
-
block.call
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def around_tool_call(ctx, &block)
|
|
60
|
-
log_hook(:around_tool_call, ctx)
|
|
61
|
-
block.call
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def around_network_run(ctx, &block)
|
|
65
|
-
log_hook(:around_network_run, ctx)
|
|
66
|
-
block.call
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def around_task(ctx, &block)
|
|
70
|
-
log_hook(:around_task, ctx)
|
|
71
|
-
block.call
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
private
|
|
75
|
-
|
|
76
|
-
def log_hook(hook_name, ctx)
|
|
77
|
-
ts = Time.now.strftime('%H:%M:%S.%3N')
|
|
78
|
-
snapshot = context_snapshot(ctx)
|
|
79
|
-
$stdout.puts " [xyzzy] #{ts} #{hook_name}"
|
|
80
|
-
File.open(LOG_PATH, "a") { |f| f.puts "#{ts} #{hook_name} | #{PP.pp(snapshot, +"").chomp}" }
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def context_snapshot(ctx)
|
|
84
|
-
{
|
|
85
|
-
robot: ctx.respond_to?(:robot) ? ctx.robot&.name : nil,
|
|
86
|
-
request: ctx.respond_to?(:request) ? ctx.request : nil,
|
|
87
|
-
network: ctx.respond_to?(:network) ? ctx.network&.name : nil,
|
|
88
|
-
task: ctx.respond_to?(:task) ? ctx.task : nil,
|
|
89
|
-
error: ctx.respond_to?(:error) ? ctx.error&.message : nil
|
|
90
|
-
}.compact
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
RobotLab.register_extension(:xyzzy, RobotLab::Xyzzy) if RobotLab.respond_to?(:register_extension)
|
|
97
|
-
RobotLab.on(RobotLab::Xyzzy)
|
data/logfile
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
2026-02-25 20:56:30.644 CST [47152] LOG: starting PostgreSQL 18.2 (Homebrew) on aarch64-apple-darwin25.2.0, compiled by Apple clang version 17.0.0 (clang-1700.6.3.2), 64-bit
|
|
2
|
-
2026-02-25 20:56:30.646 CST [47152] LOG: could not bind IPv6 address "::1": Address already in use
|
|
3
|
-
2026-02-25 20:56:30.646 CST [47152] HINT: Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
|
|
4
|
-
2026-02-25 20:56:30.646 CST [47152] LOG: could not bind IPv4 address "127.0.0.1": Address already in use
|
|
5
|
-
2026-02-25 20:56:30.646 CST [47152] HINT: Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
|
|
6
|
-
2026-02-25 20:56:30.646 CST [47152] WARNING: could not create listen socket for "localhost"
|
|
7
|
-
2026-02-25 20:56:30.646 CST [47152] FATAL: could not create any TCP/IP sockets
|
|
8
|
-
2026-02-25 20:56:30.646 CST [47152] LOG: database system is shut down
|
data/mkdocs.yml
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# MkDocs Configuration for RobotLab Documentation
|
|
2
|
-
site_name: RobotLab
|
|
3
|
-
site_description: Multi-robot LLM workflow orchestration for Ruby
|
|
4
|
-
site_author: Dewayne VanHoozer
|
|
5
|
-
site_url: https://madbomber.github.io/robot_lab
|
|
6
|
-
copyright: Copyright © 2025 Dewayne VanHoozer
|
|
7
|
-
|
|
8
|
-
# Repository information
|
|
9
|
-
repo_name: madbomber/robot_lab
|
|
10
|
-
repo_url: https://github.com/madbomber/robot_lab
|
|
11
|
-
edit_uri: edit/main/docs/
|
|
12
|
-
|
|
13
|
-
# Configuration
|
|
14
|
-
theme:
|
|
15
|
-
name: material
|
|
16
|
-
|
|
17
|
-
# Color scheme
|
|
18
|
-
palette:
|
|
19
|
-
- scheme: default
|
|
20
|
-
primary: deep purple
|
|
21
|
-
accent: amber
|
|
22
|
-
toggle:
|
|
23
|
-
icon: material/brightness-7
|
|
24
|
-
name: Switch to dark mode
|
|
25
|
-
|
|
26
|
-
- scheme: slate
|
|
27
|
-
primary: deep purple
|
|
28
|
-
accent: amber
|
|
29
|
-
toggle:
|
|
30
|
-
icon: material/brightness-4
|
|
31
|
-
name: Switch to light mode
|
|
32
|
-
|
|
33
|
-
# Typography
|
|
34
|
-
font:
|
|
35
|
-
text: Roboto
|
|
36
|
-
code: Roboto Mono
|
|
37
|
-
|
|
38
|
-
# Logo and icon
|
|
39
|
-
icon:
|
|
40
|
-
repo: fontawesome/brands/github
|
|
41
|
-
logo: material/robot
|
|
42
|
-
|
|
43
|
-
# Theme features
|
|
44
|
-
features:
|
|
45
|
-
- navigation.instant
|
|
46
|
-
- navigation.tracking
|
|
47
|
-
- navigation.tabs
|
|
48
|
-
- navigation.tabs.sticky
|
|
49
|
-
- navigation.path
|
|
50
|
-
- navigation.indexes
|
|
51
|
-
- navigation.top
|
|
52
|
-
- navigation.footer
|
|
53
|
-
- toc.follow
|
|
54
|
-
- search.suggest
|
|
55
|
-
- search.highlight
|
|
56
|
-
- search.share
|
|
57
|
-
- header.autohide
|
|
58
|
-
- content.code.copy
|
|
59
|
-
- content.code.annotate
|
|
60
|
-
- content.tabs.link
|
|
61
|
-
- content.tooltips
|
|
62
|
-
- content.action.edit
|
|
63
|
-
- content.action.view
|
|
64
|
-
|
|
65
|
-
# Plugins
|
|
66
|
-
plugins:
|
|
67
|
-
- search:
|
|
68
|
-
separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])'
|
|
69
|
-
- tags
|
|
70
|
-
|
|
71
|
-
# Extensions
|
|
72
|
-
markdown_extensions:
|
|
73
|
-
- abbr
|
|
74
|
-
- admonition
|
|
75
|
-
- attr_list
|
|
76
|
-
- def_list
|
|
77
|
-
- footnotes
|
|
78
|
-
- md_in_html
|
|
79
|
-
- tables
|
|
80
|
-
- toc:
|
|
81
|
-
permalink: true
|
|
82
|
-
title: On this page
|
|
83
|
-
|
|
84
|
-
- pymdownx.arithmatex:
|
|
85
|
-
generic: true
|
|
86
|
-
- pymdownx.betterem:
|
|
87
|
-
smart_enable: all
|
|
88
|
-
- pymdownx.caret
|
|
89
|
-
- pymdownx.critic
|
|
90
|
-
- pymdownx.details
|
|
91
|
-
- pymdownx.emoji:
|
|
92
|
-
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
|
93
|
-
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
|
94
|
-
- pymdownx.highlight:
|
|
95
|
-
anchor_linenums: true
|
|
96
|
-
line_spans: __span
|
|
97
|
-
pygments_lang_class: true
|
|
98
|
-
- pymdownx.inlinehilite
|
|
99
|
-
- pymdownx.keys
|
|
100
|
-
- pymdownx.magiclink:
|
|
101
|
-
repo_url_shorthand: true
|
|
102
|
-
user: madbomber
|
|
103
|
-
repo: robot_lab
|
|
104
|
-
normalize_issue_symbols: true
|
|
105
|
-
- pymdownx.mark
|
|
106
|
-
- pymdownx.smartsymbols
|
|
107
|
-
- pymdownx.snippets:
|
|
108
|
-
check_paths: true
|
|
109
|
-
- pymdownx.superfences:
|
|
110
|
-
custom_fences:
|
|
111
|
-
- name: mermaid
|
|
112
|
-
class: mermaid
|
|
113
|
-
format: !!python/name:pymdownx.superfences.fence_code_format
|
|
114
|
-
- pymdownx.tabbed:
|
|
115
|
-
alternate_style: true
|
|
116
|
-
- pymdownx.tasklist:
|
|
117
|
-
custom_checkbox: true
|
|
118
|
-
- pymdownx.tilde
|
|
119
|
-
|
|
120
|
-
# Extra CSS
|
|
121
|
-
extra_css:
|
|
122
|
-
- assets/css/custom.css
|
|
123
|
-
|
|
124
|
-
# Social media and extra configuration
|
|
125
|
-
extra:
|
|
126
|
-
social:
|
|
127
|
-
- icon: fontawesome/brands/github
|
|
128
|
-
link: https://github.com/madbomber/robot_lab
|
|
129
|
-
name: RobotLab on GitHub
|
|
130
|
-
- icon: fontawesome/solid/gem
|
|
131
|
-
link: https://rubygems.org/gems/robot_lab
|
|
132
|
-
name: RobotLab on RubyGems
|
|
133
|
-
|
|
134
|
-
analytics:
|
|
135
|
-
feedback:
|
|
136
|
-
title: Was this page helpful?
|
|
137
|
-
ratings:
|
|
138
|
-
- icon: material/emoticon-happy-outline
|
|
139
|
-
name: This page was helpful
|
|
140
|
-
data: 1
|
|
141
|
-
note: Thanks for your feedback!
|
|
142
|
-
- icon: material/emoticon-sad-outline
|
|
143
|
-
name: This page could be improved
|
|
144
|
-
data: 0
|
|
145
|
-
note: Thanks for your feedback! Help us improve by creating an issue.
|
|
146
|
-
|
|
147
|
-
# Navigation
|
|
148
|
-
nav:
|
|
149
|
-
- Home:
|
|
150
|
-
- Overview: index.md
|
|
151
|
-
- Concepts: concepts.md
|
|
152
|
-
- Getting Started:
|
|
153
|
-
- getting-started/index.md
|
|
154
|
-
- Installation: getting-started/installation.md
|
|
155
|
-
- Quick Start: getting-started/quick-start.md
|
|
156
|
-
- Configuration: getting-started/configuration.md
|
|
157
|
-
- Architecture:
|
|
158
|
-
- architecture/index.md
|
|
159
|
-
- Core Concepts: architecture/core-concepts.md
|
|
160
|
-
- Robot Execution: architecture/robot-execution.md
|
|
161
|
-
- Network Orchestration: architecture/network-orchestration.md
|
|
162
|
-
- State Management: architecture/state-management.md
|
|
163
|
-
- Message Flow: architecture/message-flow.md
|
|
164
|
-
- Guides:
|
|
165
|
-
- guides/index.md
|
|
166
|
-
- Building Robots: guides/building-robots.md
|
|
167
|
-
- Creating Networks: guides/creating-networks.md
|
|
168
|
-
- Using Tools: guides/using-tools.md
|
|
169
|
-
- MCP Integration: guides/mcp-integration.md
|
|
170
|
-
- Streaming Responses: guides/streaming.md
|
|
171
|
-
- Memory System: guides/memory.md
|
|
172
|
-
- Observability & Safety: guides/observability.md
|
|
173
|
-
- Hook System: guides/hooks.md
|
|
174
|
-
- Knowledge Search: guides/knowledge.md
|
|
175
|
-
- API Reference:
|
|
176
|
-
- api/index.md
|
|
177
|
-
- Core Classes:
|
|
178
|
-
- api/core/index.md
|
|
179
|
-
- Robot: api/core/robot.md
|
|
180
|
-
- RobotResult: api/core/result.md
|
|
181
|
-
- Network: api/core/network.md
|
|
182
|
-
- Tool: api/core/tool.md
|
|
183
|
-
- Memory: api/core/memory.md
|
|
184
|
-
- StateProxy: api/core/state.md
|
|
185
|
-
- Hooks: api/hooks.md
|
|
186
|
-
- Skills: api/skills.md
|
|
187
|
-
- Support Classes: api/support.md
|
|
188
|
-
- Errors: api/errors.md
|
|
189
|
-
- Messages:
|
|
190
|
-
- api/messages/index.md
|
|
191
|
-
- UserMessage: api/messages/user-message.md
|
|
192
|
-
- TextMessage: api/messages/text-message.md
|
|
193
|
-
- ToolCallMessage: api/messages/tool-call-message.md
|
|
194
|
-
- ToolResultMessage: api/messages/tool-result-message.md
|
|
195
|
-
- MCP:
|
|
196
|
-
- api/mcp/index.md
|
|
197
|
-
- Client: api/mcp/client.md
|
|
198
|
-
- Server: api/mcp/server.md
|
|
199
|
-
- Transports: api/mcp/transports.md
|
|
200
|
-
- Streaming:
|
|
201
|
-
- api/streaming/index.md
|
|
202
|
-
- Context: api/streaming/context.md
|
|
203
|
-
- Events: api/streaming/events.md
|
|
204
|
-
- Examples:
|
|
205
|
-
- examples/index.md
|
|
206
|
-
- Basic Chat: examples/basic-chat.md
|
|
207
|
-
- Multi-Robot Network: examples/multi-robot-network.md
|
|
208
|
-
- Tool Usage: examples/tool-usage.md
|
|
209
|
-
- MCP Server: examples/mcp-server.md
|
data/robot_concurrency.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Ruby Concurrency Review — RobotLab
|
|
2
|
-
|
|
3
|
-
Source: https://paolino.me/ruby-concurrency-what-actually-happens/
|
|
4
|
-
|
|
5
|
-
## What Applies to RobotLab
|
|
6
|
-
|
|
7
|
-
### 1. You're Already in the Right Model — Lean Into It
|
|
8
|
-
|
|
9
|
-
RobotLab uses `async (~> 2.0)`, which is exactly the fiber-based scheduler the article describes. LLM calls are pure I/O-bound work (HTTP streaming), so fibers are the correct and lowest-cost primitive. The article validates this architecture choice. No changes needed here — but be deliberate about not mixing blocking thread-style calls into the `Async` reactor unnecessarily.
|
|
10
|
-
|
|
11
|
-
### 2. The Ractor Caution Is Real (Recent Commit: `feat(ractor-parallelism)`)
|
|
12
|
-
|
|
13
|
-
The article is clear: Ractors in Ruby 4.0 are still experimental and practically incompatible with gems that use global state. RobotLab's dependencies (`ruby_llm`, `prompt_manager`, `zeitwerk`) almost certainly use global state. Worth auditing whether the new Ractor code hits `Ractor::IsolationError` under realistic load. The article's recommendation: **use Processes** for CPU parallelism if Rails-style gems are involved.
|
|
14
|
-
|
|
15
|
-
### 3. ~~The `Waiter` Class May Be Misfit in Fiber Context~~ — RESOLVED
|
|
16
|
-
|
|
17
|
-
`lib/robot_lab/waiter.rb` was updated to use `IO.pipe` + `IO.select` instead of `ConditionVariable`. `IO.select` is hooked by the Ruby 3.1+ fiber scheduler protocol, so it correctly yields to the Async scheduler when called from within an Async task, and correctly blocks the calling thread when used outside one. `Async::Condition` was considered but rejected: it only works inside an Async block, whereas `Memory` is intentionally usable from both plain threads and Async fibers.
|
|
18
|
-
|
|
19
|
-
### 4. Parallel Network Execution — Check the Primitive
|
|
20
|
-
|
|
21
|
-
`call_parallel()` in `Network` (built on `SimpleFlow::Pipeline`) — if it spawns OS threads rather than async tasks, you get heavier overhead than necessary for I/O-bound robot calls. The article's guidance: for I/O work, fiber-based `Async::Barrier` with concurrent tasks is 10-20x cheaper than threads.
|
|
22
|
-
|
|
23
|
-
### 5. Resource Semaphores for Parallel Networks
|
|
24
|
-
|
|
25
|
-
When a Network runs multiple robots concurrently, each making LLM API calls, you can exhaust API rate limits or connection pools with no back-pressure. The article's semaphore pattern applies directly — cap in-flight robot tasks to match your API tier's concurrency limit. This is worth adding to `RunConfig` as a `max_concurrent_robots` infra field.
|
|
26
|
-
|
|
27
|
-
### 6. Colorless Concurrency — This Is Already Your Story
|
|
28
|
-
|
|
29
|
-
The article's main point about Ruby fibers being "colorless" (no `async/await` propagation required) is directly reflected in how `robot.run("...")` stays synchronous-looking whether run standalone or in a parallel network. This is an underappreciated design strength worth highlighting in docs/README.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Summary
|
|
34
|
-
|
|
35
|
-
The async/fiber architecture is correct for LLM I/O work. The two actionable concerns are:
|
|
36
|
-
|
|
37
|
-
1. **Audit the Ractor code** for isolation errors with gem dependencies (`ruby_llm`, `prompt_manager`, `zeitwerk`).
|
|
38
|
-
2. **Check `Waiter`** (condition variable in `lib/robot_lab/waiter.rb`) — if called inside an `Async` reactor block, replace with `Async::Condition` to avoid stalling the reactor.
|
data/simple_acp_review.md
DELETED
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
# Review: `simple_acp` Ruby Library
|
|
2
|
-
|
|
3
|
-
**Path:** `/Users/dewayne/sandbox/git_repos/madbomber/simple_acp`
|
|
4
|
-
**Version:** 0.0.1
|
|
5
|
-
**Protocol:** ACP (Agent Communication Protocol) — Bee AI community standard, not Google's A2A
|
|
6
|
-
**Reviewed:** 2026-05-05
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## What It Is
|
|
11
|
-
|
|
12
|
-
A complete Ruby implementation of ACP — an HTTP+SSE protocol for communication between AI agents, applications, and humans. Provides both a server (Roda/Falcon) and a client (Faraday), with pluggable storage backends, streaming, stateful sessions, and an await/resume pattern for interactive multi-turn flows.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## Wire Protocol
|
|
17
|
-
|
|
18
|
-
| Endpoint | Purpose |
|
|
19
|
-
|---|---|
|
|
20
|
-
| `GET /ping` | Health check |
|
|
21
|
-
| `GET /agents` | List registered agents (params: limit, offset) |
|
|
22
|
-
| `GET /agents/:name` | Agent manifest (capabilities, MIME types) |
|
|
23
|
-
| `POST /runs` | Create a run (body: agent_name, input[], mode, session_id) |
|
|
24
|
-
| `GET /runs/:id` | Run status |
|
|
25
|
-
| `POST /runs/:id` | Resume an awaited run (body: await_resume, mode) |
|
|
26
|
-
| `POST /runs/:id/cancel` | Cancel a run |
|
|
27
|
-
| `GET /runs/:id/events` | Full SSE event history (params: limit, offset) |
|
|
28
|
-
| `GET /session/:id` | Session state and history |
|
|
29
|
-
|
|
30
|
-
### Run Modes
|
|
31
|
-
|
|
32
|
-
- `SYNC` — wait for completion, return finished run
|
|
33
|
-
- `ASYNC` — return immediately with run ID, poll for completion
|
|
34
|
-
- `STREAM` — SSE event stream for real-time progress
|
|
35
|
-
|
|
36
|
-
### Task State Machine
|
|
37
|
-
|
|
38
|
-
`CREATED → IN_PROGRESS → AWAITING → COMPLETED / FAILED / CANCELLED / CANCELLING`
|
|
39
|
-
|
|
40
|
-
### Message Format (JSON)
|
|
41
|
-
|
|
42
|
-
```json
|
|
43
|
-
{
|
|
44
|
-
"role": "user|agent|agent/name",
|
|
45
|
-
"parts": [
|
|
46
|
-
{
|
|
47
|
-
"name": "optional",
|
|
48
|
-
"content_type": "text/plain|application/json|image/*",
|
|
49
|
-
"content": "string or base64",
|
|
50
|
-
"content_encoding": "plain|base64",
|
|
51
|
-
"content_url": "https://...",
|
|
52
|
-
"metadata": { "kind": "citation|trajectory", ... }
|
|
53
|
-
}
|
|
54
|
-
],
|
|
55
|
-
"created_at": "ISO8601",
|
|
56
|
-
"completed_at": "ISO8601"
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Run Response (JSON)
|
|
61
|
-
|
|
62
|
-
```json
|
|
63
|
-
{
|
|
64
|
-
"run_id": "uuid",
|
|
65
|
-
"agent_name": "string",
|
|
66
|
-
"session_id": "uuid|null",
|
|
67
|
-
"status": "created|in-progress|awaiting|completed|failed|cancelled|cancelling",
|
|
68
|
-
"output": [{ "role": "agent", "parts": [...] }],
|
|
69
|
-
"error": { "code": "server_error|invalid_input|not_found", "message": "...", "data": null },
|
|
70
|
-
"await_request": { "type": "message", "message": { ... } },
|
|
71
|
-
"created_at": "ISO8601",
|
|
72
|
-
"finished_at": "ISO8601"
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### SSE Event Types (11)
|
|
77
|
-
|
|
78
|
-
`message.created`, `message.part`, `message.completed`, `run.created`, `run.in_progress`, `run.awaiting`, `run.completed`, `run.cancelled`, `run.failed`, `generic`, `error`
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## Key Classes and Their Roles
|
|
83
|
-
|
|
84
|
-
### Server
|
|
85
|
-
|
|
86
|
-
| Class | Role |
|
|
87
|
-
|---|---|
|
|
88
|
-
| `SimpleAcp::Server::Base` | Main server; registers agents, drives all execution modes |
|
|
89
|
-
| `SimpleAcp::Server::App` | Roda HTTP router — maps endpoints to server methods |
|
|
90
|
-
| `SimpleAcp::Server::Agent` | Wraps handler block; normalizes return types (String/Message/Array/Enumerator → consistent enumerable) |
|
|
91
|
-
| `SimpleAcp::Server::Context` | Passed to handler blocks; provides `await_message`, `cancel!`, `history`, `state`, `log` |
|
|
92
|
-
| `SimpleAcp::Server::FalconRunner` | Fiber-based Falcon server runner |
|
|
93
|
-
|
|
94
|
-
### Client
|
|
95
|
-
|
|
96
|
-
| Class | Role |
|
|
97
|
-
|---|---|
|
|
98
|
-
| `SimpleAcp::Client::Base` | Faraday-based; all endpoints as snake_case methods |
|
|
99
|
-
| `SimpleAcp::Client::SSE` | SSE event stream parser (event type + JSON data) |
|
|
100
|
-
|
|
101
|
-
**Client methods:** `ping`, `agents`, `agent(name)`, `run_sync`, `run_async`, `run_stream`, `run_resume_sync`, `run_resume_stream`, `run_status`, `run_events`, `run_cancel`, `wait_for_run`, `use_session`, `clear_session`, `session(id)`
|
|
102
|
-
|
|
103
|
-
### Models
|
|
104
|
-
|
|
105
|
-
| Class | Role |
|
|
106
|
-
|---|---|
|
|
107
|
-
| `SimpleAcp::Models::Message` | Role + array of `MessagePart`s; factory: `Message.user(...)`, `Message.agent(...)` |
|
|
108
|
-
| `SimpleAcp::Models::MessagePart` | Content unit: text, JSON, image, URL; factory: `.text(s)`, `.json(h)`, `.image(data, mime)`, `.from_url(url, ct)` |
|
|
109
|
-
| `SimpleAcp::Models::Run` | Execution lifecycle with state transitions: `start!`, `await!`, `complete!`, `fail!`, `cancel!` |
|
|
110
|
-
| `SimpleAcp::Models::Session` | Conversation history (Message array) + arbitrary state dict; `add_to_history`, `set_state` |
|
|
111
|
-
| `SimpleAcp::Models::AgentManifest` | Discovery: name, description, input/output MIME types, status, metadata |
|
|
112
|
-
| `SimpleAcp::Models::AwaitRequest` | Prompt to display when agent pauses |
|
|
113
|
-
| `SimpleAcp::Models::AwaitResume` | Client's response when resuming a paused run |
|
|
114
|
-
| `SimpleAcp::Models::Events` | SSE event hierarchy (all 11 types); `sse_format` for wire encoding |
|
|
115
|
-
| `SimpleAcp::Models::Error` | Structured error: code, message, data; factory: `.server_error`, `.invalid_input`, `.not_found` |
|
|
116
|
-
| `SimpleAcp::Models::Metadata` | Agent metadata: citations, trajectory, authors, links, dependencies, capabilities |
|
|
117
|
-
|
|
118
|
-
### Storage (pluggable — duck-typed interface)
|
|
119
|
-
|
|
120
|
-
| Class | Backend |
|
|
121
|
-
|---|---|
|
|
122
|
-
| `SimpleAcp::Storage::Memory` | `Concurrent::Map` — default, thread-safe |
|
|
123
|
-
| `SimpleAcp::Storage::Redis` | Redis backend |
|
|
124
|
-
| `SimpleAcp::Storage::PostgreSQL` | PostgreSQL via Sequel |
|
|
125
|
-
|
|
126
|
-
**Interface:** `get_run`, `save_run`, `delete_run`, `list_runs`, `get_session`, `save_session`, `delete_session`, `add_event`, `get_events`, `close`, `ping`
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## Agent DSL
|
|
131
|
-
|
|
132
|
-
```ruby
|
|
133
|
-
server = SimpleAcp::Server::Base.new
|
|
134
|
-
|
|
135
|
-
server.agent("assistant",
|
|
136
|
-
description: "A helpful assistant",
|
|
137
|
-
input_content_types: ["text/plain"],
|
|
138
|
-
output_content_types: ["text/plain"]
|
|
139
|
-
) do |context|
|
|
140
|
-
input = context.input.text_content
|
|
141
|
-
result = my_robot.run(input).reply
|
|
142
|
-
SimpleAcp::Models::Message.agent(result)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
server.run(port: 9292)
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Streaming Agent
|
|
149
|
-
|
|
150
|
-
```ruby
|
|
151
|
-
server.agent("streamer", description: "Streams responses") do |context|
|
|
152
|
-
Enumerator.new do |yielder|
|
|
153
|
-
my_robot.run(context.input.text_content) do |chunk|
|
|
154
|
-
yielder << SimpleAcp::Models::MessagePart.text(chunk.content)
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Await/Resume Agent (interactive)
|
|
161
|
-
|
|
162
|
-
```ruby
|
|
163
|
-
server.agent("interactive", description: "Asks for clarification") do |context|
|
|
164
|
-
clarification = context.await_message("Please clarify your intent:")
|
|
165
|
-
answer = clarification.text_content
|
|
166
|
-
result = my_robot.run("#{context.input.text_content} (clarification: #{answer})").reply
|
|
167
|
-
SimpleAcp::Models::Message.agent(result)
|
|
168
|
-
end
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Client Usage
|
|
172
|
-
|
|
173
|
-
```ruby
|
|
174
|
-
client = SimpleAcp::Client::Base.new("http://localhost:9292")
|
|
175
|
-
|
|
176
|
-
# Synchronous
|
|
177
|
-
run = client.run_sync("assistant", input: "Hello")
|
|
178
|
-
puts run.output.first.text_content
|
|
179
|
-
|
|
180
|
-
# Streaming
|
|
181
|
-
client.run_stream("streamer", input: "Tell me a story") do |event|
|
|
182
|
-
print event.part.content if event.is_a?(SimpleAcp::Models::Events::MessagePartEvent)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Async with polling
|
|
186
|
-
run = client.run_async("assistant", input: "Long task")
|
|
187
|
-
completed = client.wait_for_run(run.run_id, timeout: 60)
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
## Dependencies
|
|
193
|
-
|
|
194
|
-
**Runtime:**
|
|
195
|
-
- `roda (~> 3.0)` — Rack web framework
|
|
196
|
-
- `falcon (~> 0.47)` — Fiber-based async HTTP server
|
|
197
|
-
- `async (~> 2.0)` — Async I/O primitives
|
|
198
|
-
- `async-http (~> 0.66)` — Async HTTP adapter
|
|
199
|
-
- `faraday (~> 2.0)` — HTTP client
|
|
200
|
-
- `concurrent-ruby (~> 1.2)` — Thread-safe data structures
|
|
201
|
-
- `json_schemer (~> 2.0)` — JSON schema validation
|
|
202
|
-
- `base64`, `uri` — stdlib backports for Ruby 3.4+
|
|
203
|
-
|
|
204
|
-
**Optional storage:**
|
|
205
|
-
- `redis (~> 5.0)`, `pg (~> 1.5)`, `sequel (~> 5.0)`
|
|
206
|
-
|
|
207
|
-
**Development:**
|
|
208
|
-
- `minitest (~> 5.0)`, `minitest-reporters`, `rack-test (~> 2.0)`, `webmock (~> 3.0)`
|
|
209
|
-
- `rubocop (~> 1.0)`, `rake`, `bundler`, `debug_me`, `aigcm`
|
|
210
|
-
|
|
211
|
-
---
|
|
212
|
-
|
|
213
|
-
## Comparison: `simple_acp` vs. `agent2agent` (GIS gem)
|
|
214
|
-
|
|
215
|
-
| Feature | `simple_acp` (yours) | `agent2agent` (GIS) |
|
|
216
|
-
|---|---|---|
|
|
217
|
-
| **Protocol** | ACP (Bee AI community) | A2A (Google) |
|
|
218
|
-
| **Await/Resume** | First-class — agents pause, client resumes | `INPUT_REQUIRED` state only |
|
|
219
|
-
| **Session state** | Arbitrary state dict + full history | Task-scoped only |
|
|
220
|
-
| **Storage backends** | Memory, Redis, PostgreSQL | Memory, SQLite |
|
|
221
|
-
| **HTTP framework** | Roda + Falcon | Pure Rack + Falcon |
|
|
222
|
-
| **Client** | Faraday-based, full API surface | `async-http`, fiber-only |
|
|
223
|
-
| **Content types** | Multimodal: text, JSON, image, URL refs | Text/JSON focused |
|
|
224
|
-
| **Agent DSL** | Block-based, normalized return types | Block-based |
|
|
225
|
-
| **Tests** | Minitest in `test/` | `scampi` inline |
|
|
226
|
-
| **Proto/Schema** | JSON Schema via `json_schemer` | `.proto` file + 47-type JSON schema |
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## Applicability to RobotLab
|
|
231
|
-
|
|
232
|
-
### High Value — Natural Fit
|
|
233
|
-
|
|
234
|
-
**Robot-as-ACP-Agent:**
|
|
235
|
-
Each `Robot` maps directly to an ACP agent registration:
|
|
236
|
-
|
|
237
|
-
```ruby
|
|
238
|
-
server.agent(robot.name, description: robot.description) do |context|
|
|
239
|
-
result = robot.run(context.input.text_content)
|
|
240
|
-
SimpleAcp::Models::Message.agent(result.reply)
|
|
241
|
-
end
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
**Streaming robot output:**
|
|
245
|
-
`run_stream` + SSE `message.part` events maps onto RubyLLM's streaming callbacks — emit one `MessagePartEvent` per chunk. Real-time progress for free.
|
|
246
|
-
|
|
247
|
-
**Await/Resume ↔ AskUser:**
|
|
248
|
-
`ctx.await_message(prompt)` / `ctx.resume_message` is a network-protocol equivalent of RobotLab's `AskUser` tool. Exposes interactive robot flows to web and API clients without a terminal dependency. This is the most direct protocol-level replacement for `AskUser`.
|
|
249
|
-
|
|
250
|
-
**Session ↔ Memory:**
|
|
251
|
-
`Session#state` (arbitrary dict) mirrors `Memory#data` (StateProxy). `Session#history` mirrors `Memory#messages`. A thin bridge adapter could keep them in sync, giving RobotLab robots persistent cross-request state without Redis.
|
|
252
|
-
|
|
253
|
-
**Async runs ↔ background jobs:**
|
|
254
|
-
`run_async` returns a run ID immediately; `wait_for_run` polls. Maps cleanly onto `RobotLab::Job` (ActiveJob) — the job creates the run, Turbo Streams broadcast `message.part` events as SSE.
|
|
255
|
-
|
|
256
|
-
**Network-as-single-agent:**
|
|
257
|
-
Expose an entire `Network` pipeline as one ACP agent — clients send one message, the network orchestrates internally across all robots, the final result comes back as one response. Callers don't need to know the internal topology.
|
|
258
|
-
|
|
259
|
-
**PostgreSQL storage:**
|
|
260
|
-
`SimpleAcp::Storage::PostgreSQL` aligns with RobotLab's Rails integration and existing ActiveRecord setup. No new infrastructure required.
|
|
261
|
-
|
|
262
|
-
**Multimodal inputs:**
|
|
263
|
-
`MessagePart` supports images, JSON blobs, and URL references out of the box — richer than plain text, useful for vision-capable robots or structured-data workflows.
|
|
264
|
-
|
|
265
|
-
### Integration Pattern
|
|
266
|
-
|
|
267
|
-
```ruby
|
|
268
|
-
# Expose a RobotLab Network as an ACP server
|
|
269
|
-
network = RobotLab.create_network(name: "support") { ... }
|
|
270
|
-
|
|
271
|
-
server = SimpleAcp::Server::Base.new(
|
|
272
|
-
storage: SimpleAcp::Storage::PostgreSQL.new(db_url)
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
server.agent("support",
|
|
276
|
-
description: "Multi-robot support pipeline",
|
|
277
|
-
input_content_types: ["text/plain"],
|
|
278
|
-
output_content_types: ["text/plain"]
|
|
279
|
-
) do |context|
|
|
280
|
-
memory = RobotLab.create_memory(
|
|
281
|
-
data: { session_id: context.session_id }
|
|
282
|
-
)
|
|
283
|
-
result = network.run(message: context.input.text_content, memory: memory)
|
|
284
|
-
SimpleAcp::Models::Message.agent(result.reply)
|
|
285
|
-
end
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
## Bottom Line
|
|
291
|
-
|
|
292
|
-
`simple_acp` is a cleaner fit for RobotLab than the `agent2agent` gem for three reasons:
|
|
293
|
-
|
|
294
|
-
1. **Await/resume directly replaces `AskUser`'s terminal dependency** with a proper HTTP protocol state — robots can request human input without blocking a terminal session
|
|
295
|
-
2. **PostgreSQL backend** aligns with the existing Rails integration — no SQLite impedance mismatch
|
|
296
|
-
3. **Multimodal message parts** (image, JSON, URL) give RobotLab robots richer input/output than plain text
|
|
297
|
-
|
|
298
|
-
The most impactful integration would be a `RobotLab::ACPServer` adapter class that wraps any `Robot` or `Network` as an ACP agent, mapping `AskUser` calls to `await_message`, streaming callbacks to `message.part` events, and `RobotResult` to a completed `Message`.
|