robot_lab 0.1.0 → 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.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/.architecture/AGENTS.md +32 -0
  3. data/.architecture/config.yml +8 -0
  4. data/.architecture/members.yml +60 -0
  5. data/.architecture/reviews/feature-free-will.md +490 -0
  6. data/.architecture/reviews/overall-codebase.md +427 -0
  7. data/.claude/settings.local.json +57 -0
  8. data/.codex/config.toml +2 -0
  9. data/.irbrc +2 -2
  10. data/.rubocop.yml +172 -0
  11. data/CHANGELOG.md +72 -0
  12. data/CLAUDE.md +139 -0
  13. data/README.md +91 -95
  14. data/Rakefile +109 -3
  15. data/agent2agent_review.md +192 -0
  16. data/agentf_improvements.md +253 -0
  17. data/agents.md +14 -0
  18. data/docs/examples/index.md +37 -2
  19. data/docs/getting-started/configuration.md +20 -7
  20. data/docs/guides/index.md +16 -16
  21. data/docs/guides/knowledge.md +7 -1
  22. data/docs/guides/observability.md +132 -0
  23. data/docs/index.md +30 -3
  24. data/docs/superpowers/plans/2026-05-06-agentskills.md +1303 -0
  25. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +247 -0
  26. data/examples/.envrc +1 -0
  27. data/examples/01_simple_robot.rb +5 -9
  28. data/examples/02_tools.rb +5 -9
  29. data/examples/03_network.rb +8 -9
  30. data/examples/04_mcp.rb +21 -29
  31. data/examples/05_streaming.rb +12 -18
  32. data/examples/06_prompt_templates.rb +11 -19
  33. data/examples/07_network_memory.rb +16 -31
  34. data/examples/08_llm_config.rb +10 -22
  35. data/examples/09_chaining.rb +16 -27
  36. data/examples/10_memory.rb +12 -28
  37. data/examples/11_network_introspection.rb +15 -29
  38. data/examples/12_message_bus.rb +5 -12
  39. data/examples/13_spawn.rb +5 -10
  40. data/examples/14_rusty_circuit/.envrc +1 -0
  41. data/examples/14_rusty_circuit/comic.rb +2 -0
  42. data/examples/14_rusty_circuit/heckler.rb +1 -1
  43. data/examples/14_rusty_circuit/open_mic.rb +1 -3
  44. data/examples/14_rusty_circuit/scout.rb +2 -0
  45. data/examples/15_memory_network_and_bus/.envrc +1 -0
  46. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +6 -3
  47. data/examples/15_memory_network_and_bus/linux_writer.rb +1 -1
  48. data/examples/15_memory_network_and_bus/output/combined_article.md +6 -6
  49. data/examples/15_memory_network_and_bus/output/final_article.md +6 -8
  50. data/examples/15_memory_network_and_bus/output/linux_draft.md +4 -2
  51. data/examples/15_memory_network_and_bus/output/mac_draft.md +3 -3
  52. data/examples/15_memory_network_and_bus/output/memory.json +6 -6
  53. data/examples/15_memory_network_and_bus/output/revision_1.md +10 -11
  54. data/examples/15_memory_network_and_bus/output/revision_2.md +6 -8
  55. data/examples/15_memory_network_and_bus/output/windows_draft.md +3 -3
  56. data/examples/16_writers_room/.envrc +1 -0
  57. data/examples/16_writers_room/writers_room.rb +2 -4
  58. data/examples/17_skills.rb +8 -17
  59. data/examples/18_rails/Gemfile +1 -0
  60. data/examples/19_token_tracking.rb +9 -15
  61. data/examples/20_circuit_breaker.rb +10 -19
  62. data/examples/21_learning_loop.rb +11 -20
  63. data/examples/22_context_compression.rb +6 -13
  64. data/examples/23_convergence.rb +6 -17
  65. data/examples/24_structured_delegation.rb +11 -15
  66. data/examples/25_history_search.rb +5 -12
  67. data/examples/26_document_store.rb +6 -13
  68. data/examples/27_incident_response/incident_response.rb +4 -5
  69. data/examples/28_mcp_discovery.rb +8 -11
  70. data/examples/29_ractor_tools.rb +4 -9
  71. data/examples/30_ractor_network.rb +10 -19
  72. data/examples/31_launch_assessment.rb +10 -23
  73. data/examples/32_newsletter_reader.rb +188 -0
  74. data/examples/33_stock_generator.rb +80 -0
  75. data/examples/33_stock_predictor.rb +306 -0
  76. data/examples/34_agentskills.rb +72 -0
  77. data/examples/README.md +1 -1
  78. data/examples/common.rb +76 -0
  79. data/examples/ruboruby.md +423 -0
  80. data/examples/temp.md +51 -0
  81. data/lib/robot_lab/agent_skill.rb +63 -0
  82. data/lib/robot_lab/agent_skill_catalog.rb +74 -0
  83. data/lib/robot_lab/ask_user.rb +2 -2
  84. data/lib/robot_lab/bus_poller.rb +12 -5
  85. data/lib/robot_lab/config.rb +1 -12
  86. data/lib/robot_lab/delegation_future.rb +1 -1
  87. data/lib/robot_lab/doom_loop_detector.rb +98 -0
  88. data/lib/robot_lab/history_compressor.rb +4 -10
  89. data/lib/robot_lab/mcp/client.rb +1 -2
  90. data/lib/robot_lab/mcp/connection_poller.rb +3 -3
  91. data/lib/robot_lab/mcp/server.rb +1 -1
  92. data/lib/robot_lab/mcp/server_discovery.rb +0 -2
  93. data/lib/robot_lab/memory.rb +32 -27
  94. data/lib/robot_lab/memory_change.rb +2 -2
  95. data/lib/robot_lab/message.rb +4 -4
  96. data/lib/robot_lab/network.rb +11 -6
  97. data/lib/robot_lab/robot/agent_skill_matching.rb +99 -0
  98. data/lib/robot_lab/robot/bus_messaging.rb +9 -27
  99. data/lib/robot_lab/robot/history_search.rb +4 -1
  100. data/lib/robot_lab/robot/mcp_management.rb +5 -11
  101. data/lib/robot_lab/robot/template_rendering.rb +60 -40
  102. data/lib/robot_lab/robot.rb +323 -206
  103. data/lib/robot_lab/robot_result.rb +6 -5
  104. data/lib/robot_lab/run_config.rb +5 -11
  105. data/lib/robot_lab/script_tool.rb +76 -0
  106. data/lib/robot_lab/state_proxy.rb +7 -5
  107. data/lib/robot_lab/tool.rb +3 -3
  108. data/lib/robot_lab/tool_config.rb +1 -1
  109. data/lib/robot_lab/tool_manifest.rb +5 -7
  110. data/lib/robot_lab/user_message.rb +2 -2
  111. data/lib/robot_lab/version.rb +1 -1
  112. data/lib/robot_lab/waiter.rb +1 -1
  113. data/lib/robot_lab.rb +41 -52
  114. data/logfile +8 -0
  115. data/mkdocs.yml +2 -3
  116. data/robot_concurrency.md +38 -0
  117. data/simple_acp_review.md +298 -0
  118. data/site/404.html +2300 -0
  119. data/site/api/core/index.html +2706 -0
  120. data/site/api/core/memory/index.html +3793 -0
  121. data/site/api/core/network/index.html +3500 -0
  122. data/site/api/core/robot/index.html +4566 -0
  123. data/site/api/core/state/index.html +3390 -0
  124. data/site/api/core/tool/index.html +3843 -0
  125. data/site/api/index.html +2635 -0
  126. data/site/api/mcp/client/index.html +3435 -0
  127. data/site/api/mcp/index.html +2783 -0
  128. data/site/api/mcp/server/index.html +3252 -0
  129. data/site/api/mcp/transports/index.html +3352 -0
  130. data/site/api/messages/index.html +2641 -0
  131. data/site/api/messages/text-message/index.html +3087 -0
  132. data/site/api/messages/tool-call-message/index.html +3159 -0
  133. data/site/api/messages/tool-result-message/index.html +3252 -0
  134. data/site/api/messages/user-message/index.html +3212 -0
  135. data/site/api/streaming/context/index.html +3282 -0
  136. data/site/api/streaming/events/index.html +3347 -0
  137. data/site/api/streaming/index.html +2738 -0
  138. data/site/architecture/core-concepts/index.html +3757 -0
  139. data/site/architecture/index.html +2797 -0
  140. data/site/architecture/message-flow/index.html +3238 -0
  141. data/site/architecture/network-orchestration/index.html +3433 -0
  142. data/site/architecture/robot-execution/index.html +3140 -0
  143. data/site/architecture/state-management/index.html +3498 -0
  144. data/site/assets/css/custom.css +56 -0
  145. data/site/assets/images/favicon.png +0 -0
  146. data/site/assets/images/robot_lab.jpg +0 -0
  147. data/site/assets/javascripts/bundle.79ae519e.min.js +16 -0
  148. data/site/assets/javascripts/bundle.79ae519e.min.js.map +7 -0
  149. data/site/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
  150. data/site/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
  151. data/site/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
  152. data/site/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
  153. data/site/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
  154. data/site/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
  155. data/site/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
  156. data/site/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
  157. data/site/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
  158. data/site/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
  159. data/site/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
  160. data/site/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
  161. data/site/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
  162. data/site/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
  163. data/site/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
  164. data/site/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
  165. data/site/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
  166. data/site/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
  167. data/site/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
  168. data/site/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
  169. data/site/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
  170. data/site/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
  171. data/site/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
  172. data/site/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
  173. data/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
  174. data/site/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
  175. data/site/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
  176. data/site/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
  177. data/site/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
  178. data/site/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
  179. data/site/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
  180. data/site/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
  181. data/site/assets/javascripts/lunr/tinyseg.js +206 -0
  182. data/site/assets/javascripts/lunr/wordcut.js +6708 -0
  183. data/site/assets/javascripts/workers/search.2c215733.min.js +42 -0
  184. data/site/assets/javascripts/workers/search.2c215733.min.js.map +7 -0
  185. data/site/assets/stylesheets/main.484c7ddc.min.css +1 -0
  186. data/site/assets/stylesheets/main.484c7ddc.min.css.map +1 -0
  187. data/site/assets/stylesheets/palette.ab4e12ef.min.css +1 -0
  188. data/site/assets/stylesheets/palette.ab4e12ef.min.css.map +1 -0
  189. data/site/concepts/index.html +3455 -0
  190. data/site/examples/basic-chat/index.html +2880 -0
  191. data/site/examples/index.html +2907 -0
  192. data/site/examples/mcp-server/index.html +3018 -0
  193. data/site/examples/multi-robot-network/index.html +3131 -0
  194. data/site/examples/rails-application/index.html +3329 -0
  195. data/site/examples/tool-usage/index.html +3085 -0
  196. data/site/getting-started/configuration/index.html +3745 -0
  197. data/site/getting-started/index.html +2572 -0
  198. data/site/getting-started/installation/index.html +2981 -0
  199. data/site/getting-started/quick-start/index.html +2942 -0
  200. data/site/guides/building-robots/index.html +4290 -0
  201. data/site/guides/creating-networks/index.html +3858 -0
  202. data/site/guides/index.html +2586 -0
  203. data/site/guides/mcp-integration/index.html +3581 -0
  204. data/site/guides/memory/index.html +3586 -0
  205. data/site/guides/rails-integration/index.html +4019 -0
  206. data/site/guides/streaming/index.html +3157 -0
  207. data/site/guides/using-tools/index.html +3802 -0
  208. data/site/index.html +2671 -0
  209. data/site/search/search_index.json +1 -0
  210. data/site/sitemap.xml +183 -0
  211. data/site/sitemap.xml.gz +0 -0
  212. data/site/tags.json +1 -0
  213. data/temp.md +6 -0
  214. data/tool_manifest_plan.md +155 -0
  215. metadata +154 -92
  216. data/docs/examples/rails-application.md +0 -419
  217. data/docs/guides/ractor-parallelism.md +0 -364
  218. data/docs/guides/rails-integration.md +0 -681
  219. data/docs/superpowers/plans/2026-04-14-ractor-integration.md +0 -1538
  220. data/docs/superpowers/specs/2026-04-14-ractor-integration-design.md +0 -258
  221. data/lib/generators/robot_lab/install_generator.rb +0 -90
  222. data/lib/generators/robot_lab/job_generator.rb +0 -40
  223. data/lib/generators/robot_lab/robot_generator.rb +0 -55
  224. data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -42
  225. data/lib/generators/robot_lab/templates/job.rb.tt +0 -21
  226. data/lib/generators/robot_lab/templates/migration.rb.tt +0 -32
  227. data/lib/generators/robot_lab/templates/result_model.rb.tt +0 -52
  228. data/lib/generators/robot_lab/templates/robot.rb.tt +0 -31
  229. data/lib/generators/robot_lab/templates/robot_job.rb.tt +0 -18
  230. data/lib/generators/robot_lab/templates/robot_test.rb.tt +0 -34
  231. data/lib/generators/robot_lab/templates/routing_robot.rb.tt +0 -59
  232. data/lib/generators/robot_lab/templates/thread_model.rb.tt +0 -40
  233. data/lib/robot_lab/document_store.rb +0 -155
  234. data/lib/robot_lab/ractor_boundary.rb +0 -42
  235. data/lib/robot_lab/ractor_job.rb +0 -37
  236. data/lib/robot_lab/ractor_memory_proxy.rb +0 -85
  237. data/lib/robot_lab/ractor_network_scheduler.rb +0 -154
  238. data/lib/robot_lab/ractor_worker_pool.rb +0 -117
  239. data/lib/robot_lab/rails_integration/engine.rb +0 -29
  240. data/lib/robot_lab/rails_integration/job.rb +0 -158
  241. data/lib/robot_lab/rails_integration/railtie.rb +0 -51
  242. data/lib/robot_lab/rails_integration/turbo_stream_callbacks.rb +0 -72
@@ -0,0 +1,298 @@
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`.