robot_lab 0.3.0 → 0.5.0

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/README.md +29 -19
  4. data/lib/robot_lab/errors.rb +4 -0
  5. data/lib/robot_lab/hook_context.rb +9 -2
  6. data/lib/robot_lab/mcp/registry.rb +99 -0
  7. data/lib/robot_lab/mcp/server.rb +47 -83
  8. data/lib/robot_lab/mcp/server_discovery.rb +22 -9
  9. data/lib/robot_lab/mcp.rb +259 -0
  10. data/lib/robot_lab/robot/mcp_management.rb +159 -105
  11. data/lib/robot_lab/robot/tool_call_gating.rb +144 -0
  12. data/lib/robot_lab/robot.rb +72 -99
  13. data/lib/robot_lab/run_config.rb +3 -2
  14. data/lib/robot_lab/tool.rb +8 -23
  15. data/lib/robot_lab/version.rb +1 -1
  16. data/lib/robot_lab.rb +1 -4
  17. metadata +16 -292
  18. data/.architecture/AGENTS.md +0 -32
  19. data/.architecture/config.yml +0 -8
  20. data/.architecture/members.yml +0 -60
  21. data/.architecture/reviews/feature-free-will.md +0 -490
  22. data/.architecture/reviews/overall-codebase.md +0 -427
  23. data/.claude/settings.local.json +0 -57
  24. data/.codex/config.toml +0 -2
  25. data/.envrc +0 -6
  26. data/.github/workflows/deploy-github-pages.yml +0 -52
  27. data/.irbrc +0 -6
  28. data/.loki +0 -65
  29. data/.quality/reek_baseline.txt +0 -43
  30. data/.rubocop.yml +0 -10
  31. data/Archspec.rb +0 -44
  32. data/CLAUDE.md +0 -141
  33. data/COMMITS.md +0 -196
  34. data/Rakefile +0 -193
  35. data/_typos.toml +0 -21
  36. data/agent2agent_review.md +0 -192
  37. data/agentf_improvements.md +0 -253
  38. data/agents.md +0 -14
  39. data/docs/api/core/index.md +0 -147
  40. data/docs/api/core/memory.md +0 -578
  41. data/docs/api/core/network.md +0 -553
  42. data/docs/api/core/result.md +0 -211
  43. data/docs/api/core/robot.md +0 -1667
  44. data/docs/api/core/state.md +0 -145
  45. data/docs/api/core/tool.md +0 -520
  46. data/docs/api/errors.md +0 -323
  47. data/docs/api/hooks.md +0 -469
  48. data/docs/api/index.md +0 -137
  49. data/docs/api/mcp/client.md +0 -387
  50. data/docs/api/mcp/index.md +0 -267
  51. data/docs/api/mcp/server.md +0 -252
  52. data/docs/api/mcp/transports.md +0 -339
  53. data/docs/api/messages/index.md +0 -126
  54. data/docs/api/messages/text-message.md +0 -170
  55. data/docs/api/messages/tool-call-message.md +0 -206
  56. data/docs/api/messages/tool-result-message.md +0 -242
  57. data/docs/api/messages/user-message.md +0 -232
  58. data/docs/api/skills.md +0 -423
  59. data/docs/api/streaming/context.md +0 -280
  60. data/docs/api/streaming/events.md +0 -185
  61. data/docs/api/streaming/index.md +0 -156
  62. data/docs/api/support.md +0 -846
  63. data/docs/architecture/core-concepts.md +0 -575
  64. data/docs/architecture/index.md +0 -252
  65. data/docs/architecture/message-flow.md +0 -366
  66. data/docs/architecture/network-orchestration.md +0 -478
  67. data/docs/architecture/robot-execution.md +0 -396
  68. data/docs/architecture/state-management.md +0 -410
  69. data/docs/assets/css/custom.css +0 -56
  70. data/docs/assets/images/robot_lab.jpg +0 -0
  71. data/docs/concepts.md +0 -641
  72. data/docs/examples/basic-chat.md +0 -258
  73. data/docs/examples/index.md +0 -425
  74. data/docs/examples/mcp-server.md +0 -403
  75. data/docs/examples/multi-robot-network.md +0 -476
  76. data/docs/examples/tool-usage.md +0 -468
  77. data/docs/getting-started/configuration.md +0 -645
  78. data/docs/getting-started/index.md +0 -56
  79. data/docs/getting-started/installation.md +0 -197
  80. data/docs/getting-started/quick-start.md +0 -293
  81. data/docs/guides/building-robots.md +0 -1187
  82. data/docs/guides/creating-networks.md +0 -736
  83. data/docs/guides/hooks.md +0 -1206
  84. data/docs/guides/index.md +0 -78
  85. data/docs/guides/knowledge.md +0 -219
  86. data/docs/guides/mcp-integration.md +0 -740
  87. data/docs/guides/memory.md +0 -458
  88. data/docs/guides/observability.md +0 -733
  89. data/docs/guides/streaming.md +0 -323
  90. data/docs/guides/using-tools.md +0 -780
  91. data/docs/index.md +0 -280
  92. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  93. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  94. data/examples/.envrc +0 -3
  95. data/examples/01_simple_robot.rb +0 -31
  96. data/examples/02_tools.rb +0 -109
  97. data/examples/03_network.rb +0 -118
  98. data/examples/04_mcp.rb +0 -217
  99. data/examples/05_streaming.rb +0 -137
  100. data/examples/06_prompt_templates.rb +0 -325
  101. data/examples/07_network_memory.rb +0 -313
  102. data/examples/08_llm_config.rb +0 -162
  103. data/examples/09_chaining.rb +0 -251
  104. data/examples/10_memory.rb +0 -315
  105. data/examples/11_network_introspection.rb +0 -239
  106. data/examples/12_message_bus.rb +0 -67
  107. data/examples/13_spawn.rb +0 -85
  108. data/examples/14_rusty_circuit/.envrc +0 -1
  109. data/examples/14_rusty_circuit/.gitignore +0 -1
  110. data/examples/14_rusty_circuit/README.md +0 -1
  111. data/examples/14_rusty_circuit/comic.rb +0 -149
  112. data/examples/14_rusty_circuit/display.rb +0 -217
  113. data/examples/14_rusty_circuit/heckler.rb +0 -65
  114. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  115. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  116. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  117. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  118. data/examples/14_rusty_circuit/scout.rb +0 -165
  119. data/examples/14_rusty_circuit/show.log +0 -234
  120. data/examples/15_memory_network_and_bus/.envrc +0 -1
  121. data/examples/15_memory_network_and_bus/README.md +0 -66
  122. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  123. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  124. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  125. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  126. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  127. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  128. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  129. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  130. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  131. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  132. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  133. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  134. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  135. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  136. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  137. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  138. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  139. data/examples/16_writers_room/.envrc +0 -1
  140. data/examples/16_writers_room/display.rb +0 -158
  141. data/examples/16_writers_room/output/.gitignore +0 -4
  142. data/examples/16_writers_room/output/README.md +0 -69
  143. data/examples/16_writers_room/output/opus_001.md +0 -263
  144. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  145. data/examples/16_writers_room/output/opus_002.md +0 -245
  146. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  147. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  148. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  149. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  150. data/examples/16_writers_room/prompts/writer.md +0 -37
  151. data/examples/16_writers_room/room.rb +0 -186
  152. data/examples/16_writers_room/tools.rb +0 -173
  153. data/examples/16_writers_room/writer.rb +0 -121
  154. data/examples/16_writers_room/writers_room.rb +0 -256
  155. data/examples/17_skills.rb +0 -234
  156. data/examples/18_rails/.envrc +0 -3
  157. data/examples/18_rails/.gitignore +0 -5
  158. data/examples/18_rails/Gemfile +0 -11
  159. data/examples/18_rails/README.md +0 -67
  160. data/examples/18_rails/Rakefile +0 -4
  161. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  162. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  163. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  164. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  165. data/examples/18_rails/app/models/application_record.rb +0 -5
  166. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  167. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  168. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  169. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  170. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  171. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  172. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  173. data/examples/18_rails/bin/dev +0 -7
  174. data/examples/18_rails/bin/rails +0 -6
  175. data/examples/18_rails/bin/setup +0 -15
  176. data/examples/18_rails/config/application.rb +0 -33
  177. data/examples/18_rails/config/cable.yml +0 -2
  178. data/examples/18_rails/config/database.yml +0 -5
  179. data/examples/18_rails/config/environment.rb +0 -4
  180. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  181. data/examples/18_rails/config/routes.rb +0 -6
  182. data/examples/18_rails/config.ru +0 -4
  183. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  184. data/examples/19_token_tracking.rb +0 -138
  185. data/examples/20_circuit_breaker.rb +0 -147
  186. data/examples/21_learning_loop.rb +0 -181
  187. data/examples/22_context_compression.rb +0 -172
  188. data/examples/23_convergence.rb +0 -133
  189. data/examples/24_structured_delegation.rb +0 -151
  190. data/examples/25_history_search/conversation.jsonl +0 -30
  191. data/examples/25_history_search.rb +0 -133
  192. data/examples/26_document_store/api_versioning_adr.md +0 -52
  193. data/examples/26_document_store/incident_postmortem.md +0 -46
  194. data/examples/26_document_store/postgres_runbook.md +0 -49
  195. data/examples/26_document_store/redis_caching_guide.md +0 -48
  196. data/examples/26_document_store/sidekiq_guide.md +0 -51
  197. data/examples/26_document_store.rb +0 -140
  198. data/examples/27_incident_response/README.md +0 -65
  199. data/examples/27_incident_response/incident_response.rb +0 -261
  200. data/examples/28_mcp_discovery.rb +0 -113
  201. data/examples/29_ractor_tools.rb +0 -240
  202. data/examples/30_ractor_network.rb +0 -252
  203. data/examples/31_launch_assessment.rb +0 -246
  204. data/examples/32_newsletter_reader.rb +0 -193
  205. data/examples/33_stock_generator.rb +0 -80
  206. data/examples/33_stock_predictor.rb +0 -327
  207. data/examples/34_agentskills.rb +0 -76
  208. data/examples/35_hooks.rb +0 -266
  209. data/examples/README.md +0 -550
  210. data/examples/common.rb +0 -180
  211. data/examples/prompts/assistant.md +0 -5
  212. data/examples/prompts/audit_trail.md +0 -8
  213. data/examples/prompts/billing.md +0 -10
  214. data/examples/prompts/classifier.md +0 -7
  215. data/examples/prompts/comedian.md +0 -6
  216. data/examples/prompts/comedy_critic.md +0 -10
  217. data/examples/prompts/configurable.md +0 -9
  218. data/examples/prompts/dispatcher.md +0 -12
  219. data/examples/prompts/entity_extractor.md +0 -14
  220. data/examples/prompts/escalation.md +0 -42
  221. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  222. data/examples/prompts/frontmatter_named_test.md +0 -5
  223. data/examples/prompts/frontmatter_tools_test.md +0 -6
  224. data/examples/prompts/general.md +0 -7
  225. data/examples/prompts/github_assistant.md +0 -14
  226. data/examples/prompts/helper.md +0 -4
  227. data/examples/prompts/incident_responder.md +0 -18
  228. data/examples/prompts/keyword_extractor.md +0 -11
  229. data/examples/prompts/llm_config_demo.md +0 -20
  230. data/examples/prompts/order_support.md +0 -35
  231. data/examples/prompts/os_advocate.md +0 -13
  232. data/examples/prompts/os_chief.md +0 -13
  233. data/examples/prompts/os_editor.md +0 -13
  234. data/examples/prompts/parameterized_main_test.md +0 -6
  235. data/examples/prompts/pii_redactor.md +0 -7
  236. data/examples/prompts/product_support.md +0 -37
  237. data/examples/prompts/runbook_protocol.md +0 -12
  238. data/examples/prompts/sentiment_analyzer.md +0 -12
  239. data/examples/prompts/skill_a_test.md +0 -4
  240. data/examples/prompts/skill_b_test.md +0 -4
  241. data/examples/prompts/skill_config_test.md +0 -5
  242. data/examples/prompts/skill_cycle_a_test.md +0 -6
  243. data/examples/prompts/skill_cycle_b_test.md +0 -6
  244. data/examples/prompts/skill_description_test.md +0 -4
  245. data/examples/prompts/skill_leaf_test.md +0 -4
  246. data/examples/prompts/skill_nested_test.md +0 -6
  247. data/examples/prompts/skill_refs_main_test.md +0 -6
  248. data/examples/prompts/skill_self_ref_test.md +0 -6
  249. data/examples/prompts/skill_with_mcp_test.md +0 -9
  250. data/examples/prompts/skill_with_params_test.md +0 -6
  251. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  252. data/examples/prompts/skill_with_tools_test.md +0 -6
  253. data/examples/prompts/sre_compliance.md +0 -10
  254. data/examples/prompts/structured_output.md +0 -7
  255. data/examples/prompts/synthesizer.md +0 -17
  256. data/examples/prompts/technical.md +0 -10
  257. data/examples/prompts/template_with_skills_test.md +0 -6
  258. data/examples/prompts/triage.md +0 -22
  259. data/examples/ruboruby.md +0 -423
  260. data/examples/run_all.rb +0 -60
  261. data/examples/xyzzy.rb +0 -97
  262. data/lib/robot_lab/mcp/client.rb +0 -223
  263. data/lib/robot_lab/mcp/connection_poller.rb +0 -195
  264. data/lib/robot_lab/mcp/transports/base.rb +0 -64
  265. data/lib/robot_lab/mcp/transports/sse.rb +0 -120
  266. data/lib/robot_lab/mcp/transports/stdio.rb +0 -170
  267. data/lib/robot_lab/mcp/transports/streamable_http.rb +0 -141
  268. data/lib/robot_lab/mcp/transports/websocket.rb +0 -111
  269. data/logfile +0 -8
  270. data/mkdocs.yml +0 -209
  271. data/robot_concurrency.md +0 -38
  272. data/simple_acp_review.md +0 -298
  273. data/temp.md +0 -6
  274. data/tool_manifest_plan.md +0 -155
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`.
data/temp.md DELETED
@@ -1,6 +0,0 @@
1
-
2
- You: /llms gpt-5 openai
3
-
4
- You: /llms gpt-5 openai 5.4
5
-
6
- You: /llms gpt-5 openai 5.4 -openrouter
@@ -1,155 +0,0 @@
1
- # ToolManifest Evolution Plan
2
-
3
- ## Concept: Dynamic Tool Selection in Pipelines
4
-
5
- There is a pattern where the total number of tools available within RobotLab numbers in the hundreds -- too many to send to any single LLM call. A pipeline uses a "selector" robot that reviews the next robot's prompt and determines which tools should be made available. The selected subset is then applied to the target robot via `with_tools(...)`.
6
-
7
- ### Example Flow
8
-
9
- ```
10
- Selector Robot (step 1)
11
- - Receives the user prompt destined for step 2
12
- - Has access to a summary of all available tools (name + description)
13
- - Outputs a list of tool names relevant to the task
14
-
15
- Target Robot (step 2)
16
- - Receives the selected tools via with_tools(...)
17
- - Runs with only the relevant subset (e.g., 5-10 tools instead of hundreds)
18
- ```
19
-
20
- ## Current State
21
-
22
- `ToolManifest` (lib/robot_lab/tool_manifest.rb, 218 lines) is a name-keyed Hash wrapper with:
23
- - Lookup by name (`[]`, `fetch`)
24
- - `Enumerable` support
25
- - Merge/replace operations
26
- - Serialization (`to_h`, `to_json`, `from_hash`)
27
-
28
- It is not currently used by any production code path. `Robot` stores tools as plain arrays (`@local_tools`, `@mcp_tools`) and `all_tools` concatenates them.
29
-
30
- ## What It Needs to Become
31
-
32
- ### 1. Global Registry Entry Point
33
-
34
- A singleton or module-level registry where all available tools accumulate:
35
-
36
- ```ruby
37
- RobotLab.tool_registry # => ToolManifest (global instance)
38
- RobotLab.tool_registry.add(MyTool)
39
- RobotLab.tool_registry.names # => ["my_tool", "weather", "calculator", ...]
40
- ```
41
-
42
- ### 2. Metadata for Selection
43
-
44
- Categories, tags, or other metadata so the selector robot can reason about which tools are relevant without seeing full schemas:
45
-
46
- ```ruby
47
- # Registration with metadata
48
- RobotLab.tool_registry.add(MyTool, tags: [:database, :read_only])
49
-
50
- # Filtering
51
- RobotLab.tool_registry.tagged(:database) # => subset manifest
52
- ```
53
-
54
- ### 3. Search-Friendly Summary Output
55
-
56
- The selector robot needs a compact summary it can reason over -- name + description pairs, not full JSON schemas:
57
-
58
- ```ruby
59
- RobotLab.tool_registry.summary
60
- # => [{ name: "get_weather", description: "Get current weather for a location" },
61
- # { name: "calculate", description: "Performs basic arithmetic" }, ...]
62
- ```
63
-
64
- ### 4. Subset Extraction by Name
65
-
66
- Resolve a list of tool names (the selector robot's output) into actual tool objects:
67
-
68
- ```ruby
69
- names = ["get_weather", "calculate"] # from selector robot
70
- tools = RobotLab.tool_registry.select_by_names(names)
71
- target_robot.with_tools(tools)
72
- ```
73
-
74
- ### 5. Integration with Robot
75
-
76
- `Robot#all_tools` and `with_tools(...)` should accept a ToolManifest or be able to pull from the global registry.
77
-
78
- ## Design Decisions Still Needed
79
-
80
- - Should MCP-discovered tools auto-register in the global manifest?
81
- - How does the selector robot receive the tool summary -- as part of its system prompt, injected into the user message, or via a tool call?
82
- - Should categories/tags be defined on the Tool class itself (e.g., `Tool.create(name: ..., tags: [...])`), or only at registration time?
83
- - Does the manifest need versioning or hot-reload support for long-running processes?
84
-
85
- ---
86
-
87
- ## Review Notes
88
-
89
- ### The Problem Statement is Strong
90
-
91
- The "selector robot" pattern — an LLM choosing tools from hundreds based on a compact summary — is a real need. Sending 200+ tool schemas in every API call is both expensive and degrades LLM accuracy. The plan correctly identifies that `ToolManifest` as it exists today is a passive container that nothing actually uses.
92
-
93
- ### What's Solid
94
-
95
- - **Global registry** (section 1) — The right call. Tools currently scatter across `@local_tools` and `@mcp_tools` per-robot with no central view. `RobotLab.tool_registry` is the obvious home.
96
- - **Summary output** (section 3) — Essential for the selector pattern. Name + description pairs are the right granularity for LLM reasoning.
97
- - **Subset extraction** (section 4) — `select_by_names` maps cleanly to what `ToolConfig.filter_tools` already does, but at the registry level.
98
-
99
- ### Gaps and Concerns
100
-
101
- **1. Tags/metadata may be premature.**
102
-
103
- Section 2 proposes tags (`tags: [:database, :read_only]`) and `.tagged(:database)` filtering. This adds a classification system that someone has to maintain. In the selector-robot pattern, the LLM is doing the classification — that's the whole point. If you also hand-tag tools, you're duplicating the selector's job.
104
-
105
- A simpler alternative: let `summary` be the only interface the selector sees. If you later want pre-filtering (e.g., "only show the selector database-related tools"), add it then. Right now it's YAGNI.
106
-
107
- **2. MCP tool auto-registration is the key decision, not a footnote.**
108
-
109
- The plan lists "Should MCP-discovered tools auto-register in the global manifest?" as an open question. This is actually the central architectural decision. Looking at `MCPManagement#discover_mcp_tools`, MCP tools are created dynamically per-robot, per-connection. They hold closures over their specific `mcp_client` instance. If you auto-register them globally, you need to handle:
110
-
111
- - **Lifecycle** — MCP tools are only valid while their client is connected. The global registry would hold dead references after `disconnect`.
112
- - **Duplicates** — Two robots connecting to the same MCP server would register the same tool names with different client closures.
113
- - **Scope** — An MCP tool created for robot A shouldn't be callable by robot B unless they share the client.
114
-
115
- Suggestion: MCP tools should **not** auto-register. The global registry should hold "tool definitions" (name, description, schema) — not live callable instances. The selector robot picks names; the target robot resolves those names against its own live tools.
116
-
117
- **3. The plan doesn't address the split between "tool definition" and "tool instance."**
118
-
119
- Currently `Tool` is both the definition (name, description, parameters) and the callable (the `execute` method with its closure). For a global registry, you want the definition without the closure. The selector robot doesn't need to call tools — it just needs to read summaries and output names.
120
-
121
- This suggests `ToolManifest` should store lightweight descriptors, and `Robot#filtered_tools` continues resolving names to live instances from `all_tools`.
122
-
123
- **4. Integration with `Robot#with_tools` needs more thought.**
124
-
125
- The plan says "`with_tools(...)` should accept a ToolManifest." But `with_tools` currently delegates to `@chat.with_tools` from RubyLLM, which expects tool class/instance objects. Accepting a manifest means either:
126
-
127
- - Converting manifest entries back to tool instances (requires the live tool lookup), or
128
- - Passing tool names and letting the robot resolve internally (which is what `filtered_tools` already does)
129
-
130
- The second path is simpler and already mostly works. The plan could just say: "The selector robot outputs tool names. Pass them to `robot.run(..., tools: names)`." That's the existing `tools:` parameter.
131
-
132
- **5. Missing: how the selector robot itself gets built.**
133
-
134
- The plan describes the pattern but not the API. Is this a special network task type? A built-in robot template? A method on `ToolManifest`? Something like:
135
-
136
- ```ruby
137
- network = RobotLab.create_network(name: "smart_pipeline") do
138
- task :selector, selector_robot, depends_on: :none
139
- task :worker, worker_robot, tools: :from_selector, depends_on: [:selector]
140
- end
141
- ```
142
-
143
- The `tools: :from_selector` (or similar) is the glue that needs designing.
144
-
145
- ### Suggested Simplification
146
-
147
- Instead of evolving ToolManifest into a full registry with tags, a narrower v1:
148
-
149
- 1. **`RobotLab.tool_registry`** — Global `ToolManifest` that accumulates tool descriptors (name + description only, no live instances)
150
- 2. **`ToolManifest#summary`** — Returns the compact format for selector prompts
151
- 3. **`ToolManifest#select_by_names(names)`** — Returns a sub-manifest (already close to what exists)
152
- 4. **Auto-registration of local tools at `Robot.new`** — Push descriptors into the registry when robots are created
153
- 5. **No auto-registration of MCP tools** — Too coupled to connection lifecycle
154
-
155
- Skip tags, skip `with_tools(manifest)` integration, skip hot-reload. Those can come later if needed.