rubino-agent 0.3.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 (376) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +115 -0
  4. data/.rubocop_todo.yml +955 -0
  5. data/.ruby-version +1 -0
  6. data/AGENTS.md +97 -0
  7. data/CHANGELOG.md +344 -0
  8. data/CONTRIBUTING.md +69 -0
  9. data/LICENSE +21 -0
  10. data/README.md +200 -0
  11. data/Rakefile +8 -0
  12. data/docs/agents.md +190 -0
  13. data/docs/api/v1.md +414 -0
  14. data/docs/architecture.md +177 -0
  15. data/docs/commands.md +375 -0
  16. data/docs/configuration.md +590 -0
  17. data/docs/getting-started.md +143 -0
  18. data/docs/jobs.md +332 -0
  19. data/docs/mcp.md +128 -0
  20. data/docs/memory.md +98 -0
  21. data/docs/models-and-keys.md +173 -0
  22. data/docs/oauth-providers.md +145 -0
  23. data/docs/plugins.md +195 -0
  24. data/docs/security.md +145 -0
  25. data/docs/skills.md +322 -0
  26. data/docs/tools.md +395 -0
  27. data/docs/troubleshooting.md +73 -0
  28. data/exe/rubino +9 -0
  29. data/install.sh +275 -0
  30. data/lib/rubino/active_skill.rb +50 -0
  31. data/lib/rubino/agent/agent_registry.rb +120 -0
  32. data/lib/rubino/agent/backoff_policy.rb +116 -0
  33. data/lib/rubino/agent/definition.rb +128 -0
  34. data/lib/rubino/agent/degenerate_recovery.rb +271 -0
  35. data/lib/rubino/agent/fallback_chain.rb +194 -0
  36. data/lib/rubino/agent/iteration_budget.rb +50 -0
  37. data/lib/rubino/agent/loop.rb +617 -0
  38. data/lib/rubino/agent/model_call_runner.rb +383 -0
  39. data/lib/rubino/agent/prompts/build.txt +69 -0
  40. data/lib/rubino/agent/prompts/compaction.txt +20 -0
  41. data/lib/rubino/agent/prompts/explore.txt +19 -0
  42. data/lib/rubino/agent/prompts/general.txt +20 -0
  43. data/lib/rubino/agent/prompts/plan.txt +31 -0
  44. data/lib/rubino/agent/response_validator.rb +70 -0
  45. data/lib/rubino/agent/router.rb +65 -0
  46. data/lib/rubino/agent/runner.rb +195 -0
  47. data/lib/rubino/agent/tool_executor.rb +402 -0
  48. data/lib/rubino/agent/truncation_continuation.rb +137 -0
  49. data/lib/rubino/api/middleware/auth.rb +43 -0
  50. data/lib/rubino/api/middleware/error_handler.rb +65 -0
  51. data/lib/rubino/api/middleware/json_parser.rb +100 -0
  52. data/lib/rubino/api/middleware/observability.rb +59 -0
  53. data/lib/rubino/api/middleware/rate_limit.rb +136 -0
  54. data/lib/rubino/api/operations/approvals/decide_operation.rb +49 -0
  55. data/lib/rubino/api/operations/clarifications/decide_operation.rb +44 -0
  56. data/lib/rubino/api/operations/cron_jobs/create_operation.rb +46 -0
  57. data/lib/rubino/api/operations/cron_jobs/delete_operation.rb +36 -0
  58. data/lib/rubino/api/operations/cron_jobs/list_operation.rb +55 -0
  59. data/lib/rubino/api/operations/cron_jobs/pause_operation.rb +34 -0
  60. data/lib/rubino/api/operations/cron_jobs/resume_operation.rb +34 -0
  61. data/lib/rubino/api/operations/cron_jobs/schedule_validation.rb +30 -0
  62. data/lib/rubino/api/operations/cron_jobs/show_operation.rb +32 -0
  63. data/lib/rubino/api/operations/cron_jobs/trigger_operation.rb +38 -0
  64. data/lib/rubino/api/operations/cron_jobs/update_operation.rb +42 -0
  65. data/lib/rubino/api/operations/files/read_operation.rb +40 -0
  66. data/lib/rubino/api/operations/files/upload_operation.rb +175 -0
  67. data/lib/rubino/api/operations/health_operation.rb +46 -0
  68. data/lib/rubino/api/operations/memory/delete_operation.rb +32 -0
  69. data/lib/rubino/api/operations/memory/index_operation.rb +80 -0
  70. data/lib/rubino/api/operations/memory/stats_operation.rb +28 -0
  71. data/lib/rubino/api/operations/metrics_operation.rb +18 -0
  72. data/lib/rubino/api/operations/mode/show_operation.rb +29 -0
  73. data/lib/rubino/api/operations/mode/update_operation.rb +42 -0
  74. data/lib/rubino/api/operations/models/list_operation.rb +45 -0
  75. data/lib/rubino/api/operations/oauth/connections/disconnect_operation.rb +77 -0
  76. data/lib/rubino/api/operations/oauth/connections/list_operation.rb +36 -0
  77. data/lib/rubino/api/operations/oauth/providers/callback_operation.rb +82 -0
  78. data/lib/rubino/api/operations/oauth/providers/connect_operation.rb +44 -0
  79. data/lib/rubino/api/operations/oauth/providers/list_operation.rb +35 -0
  80. data/lib/rubino/api/operations/oauth/serializer.rb +21 -0
  81. data/lib/rubino/api/operations/runs/create_operation.rb +77 -0
  82. data/lib/rubino/api/operations/runs/events_operation.rb +195 -0
  83. data/lib/rubino/api/operations/runs/stop_operation.rb +34 -0
  84. data/lib/rubino/api/operations/sessions/create_operation.rb +46 -0
  85. data/lib/rubino/api/operations/sessions/delete_operation.rb +33 -0
  86. data/lib/rubino/api/operations/sessions/index_operation.rb +82 -0
  87. data/lib/rubino/api/operations/sessions/retry_operation.rb +45 -0
  88. data/lib/rubino/api/operations/sessions/show_operation.rb +59 -0
  89. data/lib/rubino/api/operations/sessions/undo_operation.rb +38 -0
  90. data/lib/rubino/api/operations/skills/list_operation.rb +34 -0
  91. data/lib/rubino/api/operations/skills/toggle_operation.rb +40 -0
  92. data/lib/rubino/api/operations/tasks/index_operation.rb +30 -0
  93. data/lib/rubino/api/operations/tasks/serializer.rb +60 -0
  94. data/lib/rubino/api/operations/tasks/show_operation.rb +33 -0
  95. data/lib/rubino/api/operations/tasks/stop_operation.rb +47 -0
  96. data/lib/rubino/api/request.rb +54 -0
  97. data/lib/rubino/api/responses.rb +64 -0
  98. data/lib/rubino/api/router.rb +72 -0
  99. data/lib/rubino/api/schemas.rb +103 -0
  100. data/lib/rubino/api/server.rb +102 -0
  101. data/lib/rubino/api/tls.rb +108 -0
  102. data/lib/rubino/attachments/classification.rb +16 -0
  103. data/lib/rubino/attachments/classify.rb +171 -0
  104. data/lib/rubino/attachments/defang.rb +47 -0
  105. data/lib/rubino/attachments/policy.rb +36 -0
  106. data/lib/rubino/attachments/preamble.rb +120 -0
  107. data/lib/rubino/boot/encryption_key.rb +32 -0
  108. data/lib/rubino/cli/chat/bang_shell.rb +257 -0
  109. data/lib/rubino/cli/chat/completion_builder.rb +290 -0
  110. data/lib/rubino/cli/chat/idle_card_host.rb +69 -0
  111. data/lib/rubino/cli/chat/image_inbox.rb +168 -0
  112. data/lib/rubino/cli/chat/session_resolver.rb +176 -0
  113. data/lib/rubino/cli/chat_command.rb +1674 -0
  114. data/lib/rubino/cli/commands.rb +250 -0
  115. data/lib/rubino/cli/config_command.rb +96 -0
  116. data/lib/rubino/cli/doctor_command.rb +251 -0
  117. data/lib/rubino/cli/jobs_command.rb +60 -0
  118. data/lib/rubino/cli/memory_command.rb +135 -0
  119. data/lib/rubino/cli/onboarding_wizard.rb +207 -0
  120. data/lib/rubino/cli/server_command.rb +139 -0
  121. data/lib/rubino/cli/session_command.rb +125 -0
  122. data/lib/rubino/cli/setup_command.rb +107 -0
  123. data/lib/rubino/cli/skills_command.rb +85 -0
  124. data/lib/rubino/cli/tools_command.rb +81 -0
  125. data/lib/rubino/cli/trust_gate.rb +71 -0
  126. data/lib/rubino/commands/built_ins.rb +46 -0
  127. data/lib/rubino/commands/command.rb +116 -0
  128. data/lib/rubino/commands/executor.rb +550 -0
  129. data/lib/rubino/commands/handlers/agents.rb +510 -0
  130. data/lib/rubino/commands/handlers/config.rb +88 -0
  131. data/lib/rubino/commands/handlers/help.rb +148 -0
  132. data/lib/rubino/commands/handlers/jobs.rb +71 -0
  133. data/lib/rubino/commands/handlers/mcp.rb +229 -0
  134. data/lib/rubino/commands/handlers/memory.rb +200 -0
  135. data/lib/rubino/commands/handlers/sessions.rb +207 -0
  136. data/lib/rubino/commands/handlers/skills.rb +195 -0
  137. data/lib/rubino/commands/handlers/status.rb +211 -0
  138. data/lib/rubino/commands/loader.rb +90 -0
  139. data/lib/rubino/config/configuration.rb +455 -0
  140. data/lib/rubino/config/defaults.rb +569 -0
  141. data/lib/rubino/config/loader.rb +115 -0
  142. data/lib/rubino/config/reasoning_prefs.rb +67 -0
  143. data/lib/rubino/config/writer.rb +72 -0
  144. data/lib/rubino/context/compressor.rb +149 -0
  145. data/lib/rubino/context/environment_inspector.rb +176 -0
  146. data/lib/rubino/context/file_discovery.rb +45 -0
  147. data/lib/rubino/context/message_boundary.rb +39 -0
  148. data/lib/rubino/context/prompt_assembler.rb +382 -0
  149. data/lib/rubino/context/summary_builder.rb +159 -0
  150. data/lib/rubino/context/token_budget.rb +68 -0
  151. data/lib/rubino/context/tool_pair_sanitizer.rb +70 -0
  152. data/lib/rubino/database/connection.rb +77 -0
  153. data/lib/rubino/database/migrations/001_create_initial_schema.rb +156 -0
  154. data/lib/rubino/database/migrations/002_create_runs.rb +45 -0
  155. data/lib/rubino/database/migrations/003_create_skill_states.rb +15 -0
  156. data/lib/rubino/database/migrations/004_create_cron_jobs.rb +36 -0
  157. data/lib/rubino/database/migrations/005_create_oauth_connections.rb +27 -0
  158. data/lib/rubino/database/migrations/006_create_webhook_deliveries.rb +34 -0
  159. data/lib/rubino/database/migrations/007_create_messages_fts.rb +59 -0
  160. data/lib/rubino/database/migrations/008_create_memory_facts.rb +75 -0
  161. data/lib/rubino/database/migrations/009_create_memory_graph.rb +55 -0
  162. data/lib/rubino/database/migrations/010_add_owner_pid_to_sessions.rb +20 -0
  163. data/lib/rubino/database/migrator.rb +48 -0
  164. data/lib/rubino/documents/converters/csv.rb +79 -0
  165. data/lib/rubino/documents/converters/docx.rb +129 -0
  166. data/lib/rubino/documents/converters/html.rb +28 -0
  167. data/lib/rubino/documents/converters/json.rb +35 -0
  168. data/lib/rubino/documents/converters/pdf.rb +59 -0
  169. data/lib/rubino/documents/converters/plain.rb +68 -0
  170. data/lib/rubino/documents/converters/pptx.rb +64 -0
  171. data/lib/rubino/documents/converters/xlsx.rb +62 -0
  172. data/lib/rubino/documents/converters/xml.rb +45 -0
  173. data/lib/rubino/documents/html.rb +71 -0
  174. data/lib/rubino/documents/registry.rb +68 -0
  175. data/lib/rubino/documents/table.rb +63 -0
  176. data/lib/rubino/documents.rb +50 -0
  177. data/lib/rubino/errors.rb +119 -0
  178. data/lib/rubino/files/workspace.rb +93 -0
  179. data/lib/rubino/interaction/cancel_token.rb +43 -0
  180. data/lib/rubino/interaction/clipboard_image.rb +84 -0
  181. data/lib/rubino/interaction/event_bus.rb +48 -0
  182. data/lib/rubino/interaction/events.rb +101 -0
  183. data/lib/rubino/interaction/image_input.rb +127 -0
  184. data/lib/rubino/interaction/input_queue.rb +117 -0
  185. data/lib/rubino/interaction/lifecycle.rb +299 -0
  186. data/lib/rubino/interaction/probe.rb +65 -0
  187. data/lib/rubino/interaction/state.rb +56 -0
  188. data/lib/rubino/jobs/cron_job_repository.rb +75 -0
  189. data/lib/rubino/jobs/handlers/cleanup_sessions_job.rb +32 -0
  190. data/lib/rubino/jobs/handlers/compact_session_job.rb +21 -0
  191. data/lib/rubino/jobs/handlers/distill_skill_job.rb +186 -0
  192. data/lib/rubino/jobs/handlers/extract_memory_job.rb +37 -0
  193. data/lib/rubino/jobs/handlers/summarize_session_job.rb +21 -0
  194. data/lib/rubino/jobs/queue.rb +184 -0
  195. data/lib/rubino/jobs/registry.rb +45 -0
  196. data/lib/rubino/jobs/runner.rb +79 -0
  197. data/lib/rubino/jobs/scheduler.rb +138 -0
  198. data/lib/rubino/jobs/webhook_delivery.rb +225 -0
  199. data/lib/rubino/jobs/worker.rb +59 -0
  200. data/lib/rubino/llm/adapter_factory.rb +47 -0
  201. data/lib/rubino/llm/adapter_response.rb +65 -0
  202. data/lib/rubino/llm/auxiliary_client.rb +61 -0
  203. data/lib/rubino/llm/bedrock_bearer_client.rb +235 -0
  204. data/lib/rubino/llm/content_builder.rb +55 -0
  205. data/lib/rubino/llm/credential_check.rb +93 -0
  206. data/lib/rubino/llm/error_classifier.rb +364 -0
  207. data/lib/rubino/llm/fake_provider.rb +292 -0
  208. data/lib/rubino/llm/inline_think_filter.rb +58 -0
  209. data/lib/rubino/llm/model_catalog.rb +29 -0
  210. data/lib/rubino/llm/provider_resolver.rb +48 -0
  211. data/lib/rubino/llm/reasoning_manager.rb +100 -0
  212. data/lib/rubino/llm/request.rb +56 -0
  213. data/lib/rubino/llm/ruby_llm_adapter.rb +794 -0
  214. data/lib/rubino/llm/scenario_loader.rb +68 -0
  215. data/lib/rubino/llm/scenario_selector.rb +80 -0
  216. data/lib/rubino/llm/scenarios/agent-creates-cron-failure.yml +29 -0
  217. data/lib/rubino/llm/scenarios/agent-creates-cron.yml +36 -0
  218. data/lib/rubino/llm/scenarios/analysis.yml +501 -0
  219. data/lib/rubino/llm/scenarios/complex-analysis.yml +598 -0
  220. data/lib/rubino/llm/scenarios/failure.yml +65 -0
  221. data/lib/rubino/llm/scenarios/happy-path.yml +24 -0
  222. data/lib/rubino/llm/scenarios/provider-quota-completed.yml +14 -0
  223. data/lib/rubino/llm/scenarios/wide-table.yml +121 -0
  224. data/lib/rubino/llm/scenarios/with-approvals.yml +50 -0
  225. data/lib/rubino/llm/scenarios/with-artifacts.yml +98 -0
  226. data/lib/rubino/llm/scenarios/with-clarify.yml +32 -0
  227. data/lib/rubino/llm/scenarios/with-reasoning.yml +175 -0
  228. data/lib/rubino/llm/scenarios/with-uploads.yml +104 -0
  229. data/lib/rubino/llm/thinking_support.rb +84 -0
  230. data/lib/rubino/llm/tool_bridge.rb +89 -0
  231. data/lib/rubino/logger.rb +99 -0
  232. data/lib/rubino/mcp/manager.rb +180 -0
  233. data/lib/rubino/mcp/mcp_tool_wrapper.rb +69 -0
  234. data/lib/rubino/mcp.rb +57 -0
  235. data/lib/rubino/memory/backend.rb +104 -0
  236. data/lib/rubino/memory/backends/default.rb +101 -0
  237. data/lib/rubino/memory/backends/sqlite.rb +653 -0
  238. data/lib/rubino/memory/backends.rb +53 -0
  239. data/lib/rubino/memory/deduplicator.rb +74 -0
  240. data/lib/rubino/memory/extractor.rb +85 -0
  241. data/lib/rubino/memory/flusher.rb +31 -0
  242. data/lib/rubino/memory/retriever.rb +50 -0
  243. data/lib/rubino/memory/sqlite_extraction_prompt.rb +70 -0
  244. data/lib/rubino/memory/sqlite_graph.rb +154 -0
  245. data/lib/rubino/memory/store.rb +228 -0
  246. data/lib/rubino/memory/threat_scanner.rb +68 -0
  247. data/lib/rubino/metrics.rb +175 -0
  248. data/lib/rubino/modes.rb +93 -0
  249. data/lib/rubino/oauth/connection_repository.rb +95 -0
  250. data/lib/rubino/oauth/provider/github.rb +75 -0
  251. data/lib/rubino/oauth/provider/google.rb +59 -0
  252. data/lib/rubino/oauth/provider.rb +149 -0
  253. data/lib/rubino/oauth/registry.rb +86 -0
  254. data/lib/rubino/oauth/token_encryptor.rb +87 -0
  255. data/lib/rubino/plugins/registry.rb +75 -0
  256. data/lib/rubino/plugins.rb +86 -0
  257. data/lib/rubino/run/approval_gate.rb +243 -0
  258. data/lib/rubino/run/attachment_downloader.rb +166 -0
  259. data/lib/rubino/run/event_store.rb +74 -0
  260. data/lib/rubino/run/executor.rb +383 -0
  261. data/lib/rubino/run/gate_registry.rb +39 -0
  262. data/lib/rubino/run/recorder.rb +69 -0
  263. data/lib/rubino/run/repository.rb +118 -0
  264. data/lib/rubino/run/session_approval_cache.rb +118 -0
  265. data/lib/rubino/security/allowlist_persister.rb +55 -0
  266. data/lib/rubino/security/approval_policy.rb +227 -0
  267. data/lib/rubino/security/command_allowlist.rb +24 -0
  268. data/lib/rubino/security/dangerous_patterns.rb +118 -0
  269. data/lib/rubino/security/deny_persister.rb +73 -0
  270. data/lib/rubino/security/doom_loop_detector.rb +43 -0
  271. data/lib/rubino/security/hardline_guard.rb +105 -0
  272. data/lib/rubino/security/pattern_matcher.rb +62 -0
  273. data/lib/rubino/security/prefix_deriver.rb +124 -0
  274. data/lib/rubino/security/readonly_commands.rb +211 -0
  275. data/lib/rubino/session/exporter.rb +101 -0
  276. data/lib/rubino/session/message.rb +77 -0
  277. data/lib/rubino/session/repository.rb +295 -0
  278. data/lib/rubino/session/store.rb +198 -0
  279. data/lib/rubino/session/summary_store.rb +65 -0
  280. data/lib/rubino/skills/prompt_index.rb +85 -0
  281. data/lib/rubino/skills/registry.rb +208 -0
  282. data/lib/rubino/skills/skill.rb +176 -0
  283. data/lib/rubino/skills/skill_tool.rb +215 -0
  284. data/lib/rubino/skills/state_repository.rb +37 -0
  285. data/lib/rubino/skills/toggle.rb +26 -0
  286. data/lib/rubino/tools/answer_child_tool.rb +83 -0
  287. data/lib/rubino/tools/ask_parent_tool.rb +232 -0
  288. data/lib/rubino/tools/attach_file_tool.rb +120 -0
  289. data/lib/rubino/tools/background_tasks.rb +520 -0
  290. data/lib/rubino/tools/base.rb +222 -0
  291. data/lib/rubino/tools/custom_tool_loader.rb +119 -0
  292. data/lib/rubino/tools/edit_tool.rb +122 -0
  293. data/lib/rubino/tools/git_tool.rb +71 -0
  294. data/lib/rubino/tools/github_tool.rb +233 -0
  295. data/lib/rubino/tools/glob_tool.rb +69 -0
  296. data/lib/rubino/tools/grep_tool.rb +206 -0
  297. data/lib/rubino/tools/memory_tool.rb +184 -0
  298. data/lib/rubino/tools/multi_edit_tool.rb +110 -0
  299. data/lib/rubino/tools/patch_tool.rb +260 -0
  300. data/lib/rubino/tools/probe_tool.rb +175 -0
  301. data/lib/rubino/tools/question_tool.rb +128 -0
  302. data/lib/rubino/tools/read_attachment_tool.rb +180 -0
  303. data/lib/rubino/tools/read_tool.rb +212 -0
  304. data/lib/rubino/tools/read_tracker.rb +98 -0
  305. data/lib/rubino/tools/registry.rb +166 -0
  306. data/lib/rubino/tools/result.rb +113 -0
  307. data/lib/rubino/tools/ruby_tool.rb +0 -0
  308. data/lib/rubino/tools/session_search_tool.rb +103 -0
  309. data/lib/rubino/tools/shell_input_tool.rb +96 -0
  310. data/lib/rubino/tools/shell_kill_tool.rb +76 -0
  311. data/lib/rubino/tools/shell_output_tool.rb +72 -0
  312. data/lib/rubino/tools/shell_registry.rb +158 -0
  313. data/lib/rubino/tools/shell_tail_tool.rb +118 -0
  314. data/lib/rubino/tools/shell_tool.rb +330 -0
  315. data/lib/rubino/tools/steer_tool.rb +118 -0
  316. data/lib/rubino/tools/subagent_probe.rb +89 -0
  317. data/lib/rubino/tools/summarize_file_tool.rb +182 -0
  318. data/lib/rubino/tools/task_result_tool.rb +90 -0
  319. data/lib/rubino/tools/task_stop_tool.rb +80 -0
  320. data/lib/rubino/tools/task_tool.rb +622 -0
  321. data/lib/rubino/tools/test_tool.rb +454 -0
  322. data/lib/rubino/tools/todo_tool.rb +93 -0
  323. data/lib/rubino/tools/tool_call_repository.rb +33 -0
  324. data/lib/rubino/tools/vision_tool.rb +85 -0
  325. data/lib/rubino/tools/webfetch_tool.rb +153 -0
  326. data/lib/rubino/tools/websearch_tool.rb +179 -0
  327. data/lib/rubino/tools/write_tool.rb +61 -0
  328. data/lib/rubino/trust.rb +88 -0
  329. data/lib/rubino/ui/api.rb +296 -0
  330. data/lib/rubino/ui/base.rb +252 -0
  331. data/lib/rubino/ui/bottom_composer.rb +1599 -0
  332. data/lib/rubino/ui/cli.rb +1987 -0
  333. data/lib/rubino/ui/completion_menu.rb +321 -0
  334. data/lib/rubino/ui/completion_source.rb +284 -0
  335. data/lib/rubino/ui/escape_reader.rb +169 -0
  336. data/lib/rubino/ui/indented_io.rb +88 -0
  337. data/lib/rubino/ui/input_history.rb +108 -0
  338. data/lib/rubino/ui/live_region.rb +183 -0
  339. data/lib/rubino/ui/markdown_renderer.rb +506 -0
  340. data/lib/rubino/ui/notifier.rb +163 -0
  341. data/lib/rubino/ui/null.rb +195 -0
  342. data/lib/rubino/ui/paste_store.rb +176 -0
  343. data/lib/rubino/ui/printer_base.rb +79 -0
  344. data/lib/rubino/ui/probe_wait_indicator.rb +75 -0
  345. data/lib/rubino/ui/queued_indicators.rb +66 -0
  346. data/lib/rubino/ui/status_bar.rb +100 -0
  347. data/lib/rubino/ui/stdout_proxy.rb +161 -0
  348. data/lib/rubino/ui/streaming_markdown.rb +186 -0
  349. data/lib/rubino/ui/subagent_cards.rb +134 -0
  350. data/lib/rubino/ui/subagent_view.rb +255 -0
  351. data/lib/rubino/ui.rb +21 -0
  352. data/lib/rubino/update_check.rb +187 -0
  353. data/lib/rubino/util/duration.rb +23 -0
  354. data/lib/rubino/util/hyperlink.rb +105 -0
  355. data/lib/rubino/util/output.rb +145 -0
  356. data/lib/rubino/util/secrets_mask.rb +83 -0
  357. data/lib/rubino/version.rb +5 -0
  358. data/lib/rubino/workspace.rb +85 -0
  359. data/lib/rubino-agent.rb +5 -0
  360. data/lib/rubino.rb +318 -0
  361. data/mise.toml +2 -0
  362. data/rubino-agent.gemspec +103 -0
  363. data/skills/ruby-expert/SKILL.md +67 -0
  364. data/skills/ruby-expert/references/concurrency.md +357 -0
  365. data/skills/ruby-expert/references/datetime-and-encoding.md +363 -0
  366. data/skills/ruby-expert/references/errors-and-types.md +460 -0
  367. data/skills/ruby-expert/references/gem-authoring.md +459 -0
  368. data/skills/ruby-expert/references/language-idioms.md +465 -0
  369. data/skills/ruby-expert/references/metaprogramming.md +339 -0
  370. data/skills/ruby-expert/references/oo-design.md +553 -0
  371. data/skills/ruby-expert/references/performance.md +383 -0
  372. data/skills/ruby-expert/references/rails.md +424 -0
  373. data/skills/ruby-expert/references/security.md +404 -0
  374. data/skills/ruby-expert/references/testing.md +473 -0
  375. data/skills/ruby-expert/references/tooling.md +466 -0
  376. metadata +856 -0
@@ -0,0 +1,598 @@
1
+ # Complex-analysis scenario: interleaved content + token-streamed reasoning +
2
+ # multi-tool. Ported from the reference complex-analysis scenario.
3
+ # Each `message.delta` → content, each `reasoning.available` → thinking,
4
+ # each tool.started/tool.completed pair collapses into one tool_call,
5
+ # `__delay` → delay_seconds, `artifact.created` becomes a real `write`
6
+ # tool_call so the downstream pipeline actually persists the artifact.
7
+ events:
8
+ - type: content
9
+ text: "Let me analyze the codebase.\n\n"
10
+ - type: delay_seconds
11
+ value: 0.05
12
+
13
+ - type: thinking
14
+ text: "First,"
15
+ - type: delay_seconds
16
+ value: 0.02
17
+ - type: thinking
18
+ text: " I"
19
+ - type: delay_seconds
20
+ value: 0.02
21
+ - type: thinking
22
+ text: " need"
23
+ - type: delay_seconds
24
+ value: 0.02
25
+ - type: thinking
26
+ text: " to"
27
+ - type: delay_seconds
28
+ value: 0.02
29
+ - type: thinking
30
+ text: " understand"
31
+ - type: delay_seconds
32
+ value: 0.02
33
+ - type: thinking
34
+ text: " the"
35
+ - type: delay_seconds
36
+ value: 0.02
37
+ - type: thinking
38
+ text: " current"
39
+ - type: delay_seconds
40
+ value: 0.02
41
+ - type: thinking
42
+ text: " architecture."
43
+ - type: delay_seconds
44
+ value: 0.02
45
+ - type: thinking
46
+ text: "\n"
47
+ - type: delay_seconds
48
+ value: 0.02
49
+ - type: thinking
50
+ text: "Let"
51
+ - type: delay_seconds
52
+ value: 0.02
53
+ - type: thinking
54
+ text: " me"
55
+ - type: delay_seconds
56
+ value: 0.02
57
+ - type: thinking
58
+ text: " check"
59
+ - type: delay_seconds
60
+ value: 0.02
61
+ - type: thinking
62
+ text: " the"
63
+ - type: delay_seconds
64
+ value: 0.04
65
+ - type: thinking
66
+ text: " models"
67
+ - type: delay_seconds
68
+ value: 0.04
69
+ - type: thinking
70
+ text: " first."
71
+ - type: delay_seconds
72
+ value: 0.02
73
+
74
+ - type: tool_call
75
+ id: "call_complex_read_1"
76
+ name: "read"
77
+ arguments:
78
+ file_path: "app/models/run.rb"
79
+ - type: delay_seconds
80
+ value: 0.25
81
+
82
+ - type: content
83
+ text: "I found the core model. The Run class defines the execution lifecycle with status transitions and event relationships.\n\n"
84
+ - type: delay_seconds
85
+ value: 0.05
86
+
87
+ - type: thinking
88
+ text: "Now"
89
+ - type: delay_seconds
90
+ value: 0.02
91
+ - type: thinking
92
+ text: " checking"
93
+ - type: delay_seconds
94
+ value: 0.02
95
+ - type: thinking
96
+ text: " the"
97
+ - type: delay_seconds
98
+ value: 0.02
99
+ - type: thinking
100
+ text: " presenter"
101
+ - type: delay_seconds
102
+ value: 0.02
103
+ - type: thinking
104
+ text: " layer"
105
+ - type: delay_seconds
106
+ value: 0.02
107
+ - type: thinking
108
+ text: " —"
109
+ - type: delay_seconds
110
+ value: 0.02
111
+ - type: thinking
112
+ text: " this"
113
+ - type: delay_seconds
114
+ value: 0.02
115
+ - type: thinking
116
+ text: " is"
117
+ - type: delay_seconds
118
+ value: 0.02
119
+ - type: thinking
120
+ text: " where"
121
+ - type: delay_seconds
122
+ value: 0.02
123
+ - type: thinking
124
+ text: " timeline"
125
+ - type: delay_seconds
126
+ value: 0.02
127
+ - type: thinking
128
+ text: " items"
129
+ - type: delay_seconds
130
+ value: 0.04
131
+ - type: thinking
132
+ text: " are"
133
+ - type: delay_seconds
134
+ value: 0.02
135
+ - type: thinking
136
+ text: " built."
137
+ - type: delay_seconds
138
+ value: 0.04
139
+
140
+ - type: tool_call
141
+ id: "call_complex_read_2"
142
+ name: "read"
143
+ arguments:
144
+ file_path: "app/presenters/run_timeline_presenter.rb"
145
+ - type: delay_seconds
146
+ value: 0.3
147
+
148
+ - type: content
149
+ text: "The presenter builds timeline items from RunEvents. It currently handles tool, reasoning, and message types — but not artifact yet.\n\n"
150
+ - type: delay_seconds
151
+ value: 0.05
152
+
153
+ - type: thinking
154
+ text: "I"
155
+ - type: delay_seconds
156
+ value: 0.02
157
+ - type: thinking
158
+ text: " can"
159
+ - type: delay_seconds
160
+ value: 0.02
161
+ - type: thinking
162
+ text: " see"
163
+ - type: delay_seconds
164
+ value: 0.02
165
+ - type: thinking
166
+ text: " the"
167
+ - type: delay_seconds
168
+ value: 0.02
169
+ - type: thinking
170
+ text: " pattern"
171
+ - type: delay_seconds
172
+ value: 0.02
173
+ - type: thinking
174
+ text: " is"
175
+ - type: delay_seconds
176
+ value: 0.02
177
+ - type: thinking
178
+ text: " clear"
179
+ - type: delay_seconds
180
+ value: 0.02
181
+ - type: thinking
182
+ text: " —"
183
+ - type: delay_seconds
184
+ value: 0.02
185
+ - type: thinking
186
+ text: " add"
187
+ - type: delay_seconds
188
+ value: 0.02
189
+ - type: thinking
190
+ text: " an"
191
+ - type: delay_seconds
192
+ value: 0.02
193
+ - type: thinking
194
+ text: " :artifact"
195
+ - type: delay_seconds
196
+ value: 0.04
197
+ - type: thinking
198
+ text: " type"
199
+ - type: delay_seconds
200
+ value: 0.02
201
+ - type: thinking
202
+ text: " for"
203
+ - type: delay_seconds
204
+ value: 0.02
205
+ - type: thinking
206
+ text: " artifact.created"
207
+ - type: delay_seconds
208
+ value: 0.04
209
+ - type: thinking
210
+ text: " events."
211
+ - type: delay_seconds
212
+ value: 0.04
213
+ - type: thinking
214
+ text: "\n"
215
+ - type: delay_seconds
216
+ value: 0.02
217
+ - type: thinking
218
+ text: "Also"
219
+ - type: delay_seconds
220
+ value: 0.02
221
+ - type: thinking
222
+ text: " need"
223
+ - type: delay_seconds
224
+ value: 0.02
225
+ - type: thinking
226
+ text: " to"
227
+ - type: delay_seconds
228
+ value: 0.02
229
+ - type: thinking
230
+ text: " update"
231
+ - type: delay_seconds
232
+ value: 0.02
233
+ - type: thinking
234
+ text: " the"
235
+ - type: delay_seconds
236
+ value: 0.02
237
+ - type: thinking
238
+ text: " view"
239
+ - type: delay_seconds
240
+ value: 0.02
241
+ - type: thinking
242
+ text: " template."
243
+ - type: delay_seconds
244
+ value: 0.04
245
+
246
+ - type: tool_call
247
+ id: "call_complex_read_3"
248
+ name: "read"
249
+ arguments:
250
+ file_path: "app/views/chats/_timeline_item.html.erb"
251
+ - type: delay_seconds
252
+ value: 0.3
253
+
254
+ - type: content
255
+ text: "Got it. The view renders each timeline item with conditional formatting. I'll need to add artifact rendering there too.\n\n"
256
+ - type: delay_seconds
257
+ value: 0.05
258
+
259
+ - type: thinking
260
+ text: "Ready"
261
+ - type: delay_seconds
262
+ value: 0.02
263
+ - type: thinking
264
+ text: " to"
265
+ - type: delay_seconds
266
+ value: 0.02
267
+ - type: thinking
268
+ text: " apply"
269
+ - type: delay_seconds
270
+ value: 0.02
271
+ - type: thinking
272
+ text: " changes"
273
+ - type: delay_seconds
274
+ value: 0.02
275
+ - type: thinking
276
+ text: " —"
277
+ - type: delay_seconds
278
+ value: 0.02
279
+ - type: thinking
280
+ text: " starting"
281
+ - type: delay_seconds
282
+ value: 0.04
283
+ - type: thinking
284
+ text: " with"
285
+ - type: delay_seconds
286
+ value: 0.02
287
+ - type: thinking
288
+ text: " the"
289
+ - type: delay_seconds
290
+ value: 0.02
291
+ - type: thinking
292
+ text: " presenter."
293
+ - type: delay_seconds
294
+ value: 0.04
295
+
296
+ - type: tool_call
297
+ id: "call_complex_patch_1"
298
+ name: "apply_patch"
299
+ arguments:
300
+ patch: |
301
+ --- a/app/presenters/run_timeline_presenter.rb
302
+ +++ b/app/presenters/run_timeline_presenter.rb
303
+ @@ -1,2 +1,3 @@
304
+ # presenter
305
+ +# adds :artifact handling for artifact.created events
306
+ - type: delay_seconds
307
+ value: 0.5
308
+
309
+ - type: content
310
+ text: "Presenter updated. Now emits :artifact items for artifact.created events.\n\n"
311
+ - type: delay_seconds
312
+ value: 0.05
313
+
314
+ - type: thinking
315
+ text: "Next"
316
+ - type: delay_seconds
317
+ value: 0.02
318
+ - type: thinking
319
+ text: " up:"
320
+ - type: delay_seconds
321
+ value: 0.02
322
+ - type: thinking
323
+ text: " update"
324
+ - type: delay_seconds
325
+ value: 0.02
326
+ - type: thinking
327
+ text: " the"
328
+ - type: delay_seconds
329
+ value: 0.02
330
+ - type: thinking
331
+ text: " view"
332
+ - type: delay_seconds
333
+ value: 0.02
334
+ - type: thinking
335
+ text: " template"
336
+ - type: delay_seconds
337
+ value: 0.04
338
+ - type: thinking
339
+ text: " to"
340
+ - type: delay_seconds
341
+ value: 0.02
342
+ - type: thinking
343
+ text: " render"
344
+ - type: delay_seconds
345
+ value: 0.02
346
+ - type: thinking
347
+ text: " artifacts"
348
+ - type: delay_seconds
349
+ value: 0.04
350
+ - type: thinking
351
+ text: " inline."
352
+ - type: delay_seconds
353
+ value: 0.04
354
+
355
+ - type: tool_call
356
+ id: "call_complex_patch_2"
357
+ name: "apply_patch"
358
+ arguments:
359
+ patch: |
360
+ --- a/app/views/chats/_timeline_item.html.erb
361
+ +++ b/app/views/chats/_timeline_item.html.erb
362
+ @@ -1,2 +1,3 @@
363
+ <%# timeline item %>
364
+ +<%# renders artifacts inline with download links %>
365
+ - type: delay_seconds
366
+ value: 0.4
367
+
368
+ - type: content
369
+ text: "View updated. Artifacts now render inline with download links.\n\n"
370
+ - type: delay_seconds
371
+ value: 0.05
372
+
373
+ - type: thinking
374
+ text: "Now"
375
+ - type: delay_seconds
376
+ value: 0.02
377
+ - type: thinking
378
+ text: " I"
379
+ - type: delay_seconds
380
+ value: 0.02
381
+ - type: thinking
382
+ text: " need"
383
+ - type: delay_seconds
384
+ value: 0.02
385
+ - type: thinking
386
+ text: " to"
387
+ - type: delay_seconds
388
+ value: 0.02
389
+ - type: thinking
390
+ text: " wire"
391
+ - type: delay_seconds
392
+ value: 0.02
393
+ - type: thinking
394
+ text: " the"
395
+ - type: delay_seconds
396
+ value: 0.02
397
+ - type: thinking
398
+ text: " download"
399
+ - type: delay_seconds
400
+ value: 0.04
401
+ - type: thinking
402
+ text: " logic"
403
+ - type: delay_seconds
404
+ value: 0.02
405
+ - type: thinking
406
+ text: " in"
407
+ - type: delay_seconds
408
+ value: 0.02
409
+ - type: thinking
410
+ text: " the"
411
+ - type: delay_seconds
412
+ value: 0.02
413
+ - type: thinking
414
+ text: " job."
415
+ - type: delay_seconds
416
+ value: 0.04
417
+ - type: thinking
418
+ text: "\n"
419
+ - type: delay_seconds
420
+ value: 0.02
421
+ - type: thinking
422
+ text: "The"
423
+ - type: delay_seconds
424
+ value: 0.02
425
+ - type: thinking
426
+ text: " artifact.created"
427
+ - type: delay_seconds
428
+ value: 0.04
429
+ - type: thinking
430
+ text: " handler"
431
+ - type: delay_seconds
432
+ value: 0.02
433
+ - type: thinking
434
+ text: " must"
435
+ - type: delay_seconds
436
+ value: 0.02
437
+ - type: thinking
438
+ text: " store"
439
+ - type: delay_seconds
440
+ value: 0.02
441
+ - type: thinking
442
+ text: " the"
443
+ - type: delay_seconds
444
+ value: 0.02
445
+ - type: thinking
446
+ text: " blob"
447
+ - type: delay_seconds
448
+ value: 0.04
449
+ - type: thinking
450
+ text: " reference"
451
+ - type: delay_seconds
452
+ value: 0.04
453
+ - type: thinking
454
+ text: " on"
455
+ - type: delay_seconds
456
+ value: 0.02
457
+ - type: thinking
458
+ text: " the"
459
+ - type: delay_seconds
460
+ value: 0.02
461
+ - type: thinking
462
+ text: " RunEvent."
463
+ - type: delay_seconds
464
+ value: 0.04
465
+
466
+ - type: tool_call
467
+ id: "call_complex_write_1"
468
+ name: "write"
469
+ arguments:
470
+ file_path: "app/jobs/acme_run_job.rb"
471
+ content: |
472
+ # frozen_string_literal: true
473
+
474
+ # AcmeRunJob orchestrates the run lifecycle.
475
+ # The download handler saves blob_signed_id directly on the RunEvent payload.
476
+ class AcmeRunJob < ApplicationJob
477
+ queue_as :default
478
+
479
+ def perform(run_id)
480
+ # ... existing orchestration ...
481
+ end
482
+ end
483
+ - type: delay_seconds
484
+ value: 0.6
485
+
486
+ - type: content
487
+ text: "Job updated. The download handler now saves blob_signed_id directly on the RunEvent payload.\n\n"
488
+ - type: delay_seconds
489
+ value: 0.05
490
+
491
+ - type: thinking
492
+ text: "Verifying"
493
+ - type: delay_seconds
494
+ value: 0.04
495
+ - type: thinking
496
+ text: " the"
497
+ - type: delay_seconds
498
+ value: 0.02
499
+ - type: thinking
500
+ text: " complete"
501
+ - type: delay_seconds
502
+ value: 0.04
503
+ - type: thinking
504
+ text: " pipeline"
505
+ - type: delay_seconds
506
+ value: 0.04
507
+ - type: thinking
508
+ text: " before"
509
+ - type: delay_seconds
510
+ value: 0.02
511
+ - type: thinking
512
+ text: " finishing."
513
+ - type: delay_seconds
514
+ value: 0.04
515
+
516
+ - type: tool_call
517
+ id: "call_complex_read_4"
518
+ name: "read"
519
+ arguments:
520
+ file_path: "app/jobs/acme_run_job.rb"
521
+ - type: delay_seconds
522
+ value: 0.3
523
+
524
+ - type: content
525
+ text: "All changes verified. Let me generate the summary report.\n\n"
526
+ - type: delay_seconds
527
+ value: 0.05
528
+
529
+ - type: thinking
530
+ text: "Everything"
531
+ - type: delay_seconds
532
+ value: 0.04
533
+ - type: thinking
534
+ text: " looks"
535
+ - type: delay_seconds
536
+ value: 0.02
537
+ - type: thinking
538
+ text: " correct."
539
+ - type: delay_seconds
540
+ value: 0.04
541
+ - type: thinking
542
+ text: "\n"
543
+ - type: delay_seconds
544
+ value: 0.02
545
+ - type: thinking
546
+ text: "Ready"
547
+ - type: delay_seconds
548
+ value: 0.02
549
+ - type: thinking
550
+ text: " to"
551
+ - type: delay_seconds
552
+ value: 0.02
553
+ - type: thinking
554
+ text: " produce"
555
+ - type: delay_seconds
556
+ value: 0.04
557
+ - type: thinking
558
+ text: " the"
559
+ - type: delay_seconds
560
+ value: 0.02
561
+ - type: thinking
562
+ text: " final"
563
+ - type: delay_seconds
564
+ value: 0.02
565
+ - type: thinking
566
+ text: " report."
567
+ - type: delay_seconds
568
+ value: 0.04
569
+
570
+ - type: tool_call
571
+ id: "call_complex_artifact_1"
572
+ name: "write"
573
+ arguments:
574
+ file_path: "/tmp/acme-refactor-report.md"
575
+ content: |
576
+ # Acme Timeline Refactor Report
577
+
578
+ ## Summary
579
+ Added inline artifact rendering to the run timeline.
580
+
581
+ ## Changes
582
+ - `app/presenters/run_timeline_presenter.rb` — emits `:artifact` items for `artifact.created` events.
583
+ - `app/views/chats/_timeline_item.html.erb` — renders artifacts inline with download links.
584
+ - `app/jobs/acme_run_job.rb` — download handler stores `blob_signed_id` on the RunEvent payload.
585
+
586
+ ## Verification
587
+ Re-read the job file end-to-end; pipeline wires up cleanly from `artifact.created` → presenter → view.
588
+ - type: delay_seconds
589
+ value: 0.3
590
+
591
+ - type: content
592
+ text: "Done! The refactored timeline now shows artifacts inline with download links.\n\n"
593
+ - type: delay_seconds
594
+ value: 0.05
595
+ - type: content
596
+ text: "Here's a report summarizing all changes.\n"
597
+ - type: delay_seconds
598
+ value: 0.05
@@ -0,0 +1,65 @@
1
+ # Failure scenario: model reasons, tries to read a nonexistent file via the
2
+ # real `read` tool (which returns an error), then explains it cannot proceed.
3
+ # No terminal tool_call — the fake provider stops when events run out.
4
+ events:
5
+ - type: thinking
6
+ text: "Attempting"
7
+ - type: delay_seconds
8
+ value: 0.02
9
+ - type: thinking
10
+ text: " to"
11
+ - type: delay_seconds
12
+ value: 0.02
13
+ - type: thinking
14
+ text: " process"
15
+ - type: delay_seconds
16
+ value: 0.04
17
+ - type: thinking
18
+ text: " the"
19
+ - type: delay_seconds
20
+ value: 0.02
21
+ - type: thinking
22
+ text: " request"
23
+ - type: delay_seconds
24
+ value: 0.04
25
+ - type: thinking
26
+ text: "..."
27
+ - type: delay_seconds
28
+ value: 0.04
29
+
30
+ - type: tool_call
31
+ name: read
32
+ arguments:
33
+ file_path: "/tmp/nonexistent-file.txt"
34
+ - type: delay_seconds
35
+ value: 0.3
36
+
37
+ - type: thinking
38
+ text: "File"
39
+ - type: delay_seconds
40
+ value: 0.04
41
+ - type: thinking
42
+ text: " not"
43
+ - type: delay_seconds
44
+ value: 0.02
45
+ - type: thinking
46
+ text: " found."
47
+ - type: delay_seconds
48
+ value: 0.02
49
+ - type: thinking
50
+ text: "\n"
51
+ - type: delay_seconds
52
+ value: 0.02
53
+ - type: thinking
54
+ text: "Cannot"
55
+ - type: delay_seconds
56
+ value: 0.04
57
+ - type: thinking
58
+ text: " proceed."
59
+ - type: delay_seconds
60
+ value: 0.04
61
+
62
+ - type: content
63
+ text: "File not found: /tmp/nonexistent-file.txt. "
64
+ - type: content
65
+ text: "The requested operation cannot be completed."
@@ -0,0 +1,24 @@
1
+ # Default scenario used by the FakeProvider when no keyword routes to a
2
+ # more specific scenario. Ports the reference happy-path scenario into
3
+ # the FakeProvider event vocabulary (type/text/value).
4
+ events:
5
+ - type: thinking
6
+ text: "Processing"
7
+ - type: delay_seconds
8
+ value: 0.02
9
+ - type: thinking
10
+ text: " your"
11
+ - type: delay_seconds
12
+ value: 0.02
13
+ - type: thinking
14
+ text: " request"
15
+ - type: delay_seconds
16
+ value: 0.02
17
+ - type: thinking
18
+ text: "..."
19
+ - type: delay_seconds
20
+ value: 0.04
21
+ - type: content
22
+ text: "Request processed successfully. This is a test response from the Fake provider."
23
+ - type: delay_seconds
24
+ value: 0.02
@@ -0,0 +1,14 @@
1
+ # Provider quota failure scenario:
2
+ # Simulates the real provider failure shape observed in production where
3
+ # the run is emitted as completed, but the output is a provider quota error and
4
+ # usage is zero. The fake provider emits plain content chunks explaining the
5
+ # failure; no terminal tool_call.
6
+ events:
7
+ - type: thinking
8
+ text: "Attempting to call the provider..."
9
+ - type: content
10
+ text: "API call failed after 3 retries: "
11
+ - type: content
12
+ text: "You exceeded your current quota, please check your plan and billing details. "
13
+ - type: content
14
+ text: "For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors."