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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +115 -0
- data/.rubocop_todo.yml +955 -0
- data/.ruby-version +1 -0
- data/AGENTS.md +97 -0
- data/CHANGELOG.md +344 -0
- data/CONTRIBUTING.md +69 -0
- data/LICENSE +21 -0
- data/README.md +200 -0
- data/Rakefile +8 -0
- data/docs/agents.md +190 -0
- data/docs/api/v1.md +414 -0
- data/docs/architecture.md +177 -0
- data/docs/commands.md +375 -0
- data/docs/configuration.md +590 -0
- data/docs/getting-started.md +143 -0
- data/docs/jobs.md +332 -0
- data/docs/mcp.md +128 -0
- data/docs/memory.md +98 -0
- data/docs/models-and-keys.md +173 -0
- data/docs/oauth-providers.md +145 -0
- data/docs/plugins.md +195 -0
- data/docs/security.md +145 -0
- data/docs/skills.md +322 -0
- data/docs/tools.md +395 -0
- data/docs/troubleshooting.md +73 -0
- data/exe/rubino +9 -0
- data/install.sh +275 -0
- data/lib/rubino/active_skill.rb +50 -0
- data/lib/rubino/agent/agent_registry.rb +120 -0
- data/lib/rubino/agent/backoff_policy.rb +116 -0
- data/lib/rubino/agent/definition.rb +128 -0
- data/lib/rubino/agent/degenerate_recovery.rb +271 -0
- data/lib/rubino/agent/fallback_chain.rb +194 -0
- data/lib/rubino/agent/iteration_budget.rb +50 -0
- data/lib/rubino/agent/loop.rb +617 -0
- data/lib/rubino/agent/model_call_runner.rb +383 -0
- data/lib/rubino/agent/prompts/build.txt +69 -0
- data/lib/rubino/agent/prompts/compaction.txt +20 -0
- data/lib/rubino/agent/prompts/explore.txt +19 -0
- data/lib/rubino/agent/prompts/general.txt +20 -0
- data/lib/rubino/agent/prompts/plan.txt +31 -0
- data/lib/rubino/agent/response_validator.rb +70 -0
- data/lib/rubino/agent/router.rb +65 -0
- data/lib/rubino/agent/runner.rb +195 -0
- data/lib/rubino/agent/tool_executor.rb +402 -0
- data/lib/rubino/agent/truncation_continuation.rb +137 -0
- data/lib/rubino/api/middleware/auth.rb +43 -0
- data/lib/rubino/api/middleware/error_handler.rb +65 -0
- data/lib/rubino/api/middleware/json_parser.rb +100 -0
- data/lib/rubino/api/middleware/observability.rb +59 -0
- data/lib/rubino/api/middleware/rate_limit.rb +136 -0
- data/lib/rubino/api/operations/approvals/decide_operation.rb +49 -0
- data/lib/rubino/api/operations/clarifications/decide_operation.rb +44 -0
- data/lib/rubino/api/operations/cron_jobs/create_operation.rb +46 -0
- data/lib/rubino/api/operations/cron_jobs/delete_operation.rb +36 -0
- data/lib/rubino/api/operations/cron_jobs/list_operation.rb +55 -0
- data/lib/rubino/api/operations/cron_jobs/pause_operation.rb +34 -0
- data/lib/rubino/api/operations/cron_jobs/resume_operation.rb +34 -0
- data/lib/rubino/api/operations/cron_jobs/schedule_validation.rb +30 -0
- data/lib/rubino/api/operations/cron_jobs/show_operation.rb +32 -0
- data/lib/rubino/api/operations/cron_jobs/trigger_operation.rb +38 -0
- data/lib/rubino/api/operations/cron_jobs/update_operation.rb +42 -0
- data/lib/rubino/api/operations/files/read_operation.rb +40 -0
- data/lib/rubino/api/operations/files/upload_operation.rb +175 -0
- data/lib/rubino/api/operations/health_operation.rb +46 -0
- data/lib/rubino/api/operations/memory/delete_operation.rb +32 -0
- data/lib/rubino/api/operations/memory/index_operation.rb +80 -0
- data/lib/rubino/api/operations/memory/stats_operation.rb +28 -0
- data/lib/rubino/api/operations/metrics_operation.rb +18 -0
- data/lib/rubino/api/operations/mode/show_operation.rb +29 -0
- data/lib/rubino/api/operations/mode/update_operation.rb +42 -0
- data/lib/rubino/api/operations/models/list_operation.rb +45 -0
- data/lib/rubino/api/operations/oauth/connections/disconnect_operation.rb +77 -0
- data/lib/rubino/api/operations/oauth/connections/list_operation.rb +36 -0
- data/lib/rubino/api/operations/oauth/providers/callback_operation.rb +82 -0
- data/lib/rubino/api/operations/oauth/providers/connect_operation.rb +44 -0
- data/lib/rubino/api/operations/oauth/providers/list_operation.rb +35 -0
- data/lib/rubino/api/operations/oauth/serializer.rb +21 -0
- data/lib/rubino/api/operations/runs/create_operation.rb +77 -0
- data/lib/rubino/api/operations/runs/events_operation.rb +195 -0
- data/lib/rubino/api/operations/runs/stop_operation.rb +34 -0
- data/lib/rubino/api/operations/sessions/create_operation.rb +46 -0
- data/lib/rubino/api/operations/sessions/delete_operation.rb +33 -0
- data/lib/rubino/api/operations/sessions/index_operation.rb +82 -0
- data/lib/rubino/api/operations/sessions/retry_operation.rb +45 -0
- data/lib/rubino/api/operations/sessions/show_operation.rb +59 -0
- data/lib/rubino/api/operations/sessions/undo_operation.rb +38 -0
- data/lib/rubino/api/operations/skills/list_operation.rb +34 -0
- data/lib/rubino/api/operations/skills/toggle_operation.rb +40 -0
- data/lib/rubino/api/operations/tasks/index_operation.rb +30 -0
- data/lib/rubino/api/operations/tasks/serializer.rb +60 -0
- data/lib/rubino/api/operations/tasks/show_operation.rb +33 -0
- data/lib/rubino/api/operations/tasks/stop_operation.rb +47 -0
- data/lib/rubino/api/request.rb +54 -0
- data/lib/rubino/api/responses.rb +64 -0
- data/lib/rubino/api/router.rb +72 -0
- data/lib/rubino/api/schemas.rb +103 -0
- data/lib/rubino/api/server.rb +102 -0
- data/lib/rubino/api/tls.rb +108 -0
- data/lib/rubino/attachments/classification.rb +16 -0
- data/lib/rubino/attachments/classify.rb +171 -0
- data/lib/rubino/attachments/defang.rb +47 -0
- data/lib/rubino/attachments/policy.rb +36 -0
- data/lib/rubino/attachments/preamble.rb +120 -0
- data/lib/rubino/boot/encryption_key.rb +32 -0
- data/lib/rubino/cli/chat/bang_shell.rb +257 -0
- data/lib/rubino/cli/chat/completion_builder.rb +290 -0
- data/lib/rubino/cli/chat/idle_card_host.rb +69 -0
- data/lib/rubino/cli/chat/image_inbox.rb +168 -0
- data/lib/rubino/cli/chat/session_resolver.rb +176 -0
- data/lib/rubino/cli/chat_command.rb +1674 -0
- data/lib/rubino/cli/commands.rb +250 -0
- data/lib/rubino/cli/config_command.rb +96 -0
- data/lib/rubino/cli/doctor_command.rb +251 -0
- data/lib/rubino/cli/jobs_command.rb +60 -0
- data/lib/rubino/cli/memory_command.rb +135 -0
- data/lib/rubino/cli/onboarding_wizard.rb +207 -0
- data/lib/rubino/cli/server_command.rb +139 -0
- data/lib/rubino/cli/session_command.rb +125 -0
- data/lib/rubino/cli/setup_command.rb +107 -0
- data/lib/rubino/cli/skills_command.rb +85 -0
- data/lib/rubino/cli/tools_command.rb +81 -0
- data/lib/rubino/cli/trust_gate.rb +71 -0
- data/lib/rubino/commands/built_ins.rb +46 -0
- data/lib/rubino/commands/command.rb +116 -0
- data/lib/rubino/commands/executor.rb +550 -0
- data/lib/rubino/commands/handlers/agents.rb +510 -0
- data/lib/rubino/commands/handlers/config.rb +88 -0
- data/lib/rubino/commands/handlers/help.rb +148 -0
- data/lib/rubino/commands/handlers/jobs.rb +71 -0
- data/lib/rubino/commands/handlers/mcp.rb +229 -0
- data/lib/rubino/commands/handlers/memory.rb +200 -0
- data/lib/rubino/commands/handlers/sessions.rb +207 -0
- data/lib/rubino/commands/handlers/skills.rb +195 -0
- data/lib/rubino/commands/handlers/status.rb +211 -0
- data/lib/rubino/commands/loader.rb +90 -0
- data/lib/rubino/config/configuration.rb +455 -0
- data/lib/rubino/config/defaults.rb +569 -0
- data/lib/rubino/config/loader.rb +115 -0
- data/lib/rubino/config/reasoning_prefs.rb +67 -0
- data/lib/rubino/config/writer.rb +72 -0
- data/lib/rubino/context/compressor.rb +149 -0
- data/lib/rubino/context/environment_inspector.rb +176 -0
- data/lib/rubino/context/file_discovery.rb +45 -0
- data/lib/rubino/context/message_boundary.rb +39 -0
- data/lib/rubino/context/prompt_assembler.rb +382 -0
- data/lib/rubino/context/summary_builder.rb +159 -0
- data/lib/rubino/context/token_budget.rb +68 -0
- data/lib/rubino/context/tool_pair_sanitizer.rb +70 -0
- data/lib/rubino/database/connection.rb +77 -0
- data/lib/rubino/database/migrations/001_create_initial_schema.rb +156 -0
- data/lib/rubino/database/migrations/002_create_runs.rb +45 -0
- data/lib/rubino/database/migrations/003_create_skill_states.rb +15 -0
- data/lib/rubino/database/migrations/004_create_cron_jobs.rb +36 -0
- data/lib/rubino/database/migrations/005_create_oauth_connections.rb +27 -0
- data/lib/rubino/database/migrations/006_create_webhook_deliveries.rb +34 -0
- data/lib/rubino/database/migrations/007_create_messages_fts.rb +59 -0
- data/lib/rubino/database/migrations/008_create_memory_facts.rb +75 -0
- data/lib/rubino/database/migrations/009_create_memory_graph.rb +55 -0
- data/lib/rubino/database/migrations/010_add_owner_pid_to_sessions.rb +20 -0
- data/lib/rubino/database/migrator.rb +48 -0
- data/lib/rubino/documents/converters/csv.rb +79 -0
- data/lib/rubino/documents/converters/docx.rb +129 -0
- data/lib/rubino/documents/converters/html.rb +28 -0
- data/lib/rubino/documents/converters/json.rb +35 -0
- data/lib/rubino/documents/converters/pdf.rb +59 -0
- data/lib/rubino/documents/converters/plain.rb +68 -0
- data/lib/rubino/documents/converters/pptx.rb +64 -0
- data/lib/rubino/documents/converters/xlsx.rb +62 -0
- data/lib/rubino/documents/converters/xml.rb +45 -0
- data/lib/rubino/documents/html.rb +71 -0
- data/lib/rubino/documents/registry.rb +68 -0
- data/lib/rubino/documents/table.rb +63 -0
- data/lib/rubino/documents.rb +50 -0
- data/lib/rubino/errors.rb +119 -0
- data/lib/rubino/files/workspace.rb +93 -0
- data/lib/rubino/interaction/cancel_token.rb +43 -0
- data/lib/rubino/interaction/clipboard_image.rb +84 -0
- data/lib/rubino/interaction/event_bus.rb +48 -0
- data/lib/rubino/interaction/events.rb +101 -0
- data/lib/rubino/interaction/image_input.rb +127 -0
- data/lib/rubino/interaction/input_queue.rb +117 -0
- data/lib/rubino/interaction/lifecycle.rb +299 -0
- data/lib/rubino/interaction/probe.rb +65 -0
- data/lib/rubino/interaction/state.rb +56 -0
- data/lib/rubino/jobs/cron_job_repository.rb +75 -0
- data/lib/rubino/jobs/handlers/cleanup_sessions_job.rb +32 -0
- data/lib/rubino/jobs/handlers/compact_session_job.rb +21 -0
- data/lib/rubino/jobs/handlers/distill_skill_job.rb +186 -0
- data/lib/rubino/jobs/handlers/extract_memory_job.rb +37 -0
- data/lib/rubino/jobs/handlers/summarize_session_job.rb +21 -0
- data/lib/rubino/jobs/queue.rb +184 -0
- data/lib/rubino/jobs/registry.rb +45 -0
- data/lib/rubino/jobs/runner.rb +79 -0
- data/lib/rubino/jobs/scheduler.rb +138 -0
- data/lib/rubino/jobs/webhook_delivery.rb +225 -0
- data/lib/rubino/jobs/worker.rb +59 -0
- data/lib/rubino/llm/adapter_factory.rb +47 -0
- data/lib/rubino/llm/adapter_response.rb +65 -0
- data/lib/rubino/llm/auxiliary_client.rb +61 -0
- data/lib/rubino/llm/bedrock_bearer_client.rb +235 -0
- data/lib/rubino/llm/content_builder.rb +55 -0
- data/lib/rubino/llm/credential_check.rb +93 -0
- data/lib/rubino/llm/error_classifier.rb +364 -0
- data/lib/rubino/llm/fake_provider.rb +292 -0
- data/lib/rubino/llm/inline_think_filter.rb +58 -0
- data/lib/rubino/llm/model_catalog.rb +29 -0
- data/lib/rubino/llm/provider_resolver.rb +48 -0
- data/lib/rubino/llm/reasoning_manager.rb +100 -0
- data/lib/rubino/llm/request.rb +56 -0
- data/lib/rubino/llm/ruby_llm_adapter.rb +794 -0
- data/lib/rubino/llm/scenario_loader.rb +68 -0
- data/lib/rubino/llm/scenario_selector.rb +80 -0
- data/lib/rubino/llm/scenarios/agent-creates-cron-failure.yml +29 -0
- data/lib/rubino/llm/scenarios/agent-creates-cron.yml +36 -0
- data/lib/rubino/llm/scenarios/analysis.yml +501 -0
- data/lib/rubino/llm/scenarios/complex-analysis.yml +598 -0
- data/lib/rubino/llm/scenarios/failure.yml +65 -0
- data/lib/rubino/llm/scenarios/happy-path.yml +24 -0
- data/lib/rubino/llm/scenarios/provider-quota-completed.yml +14 -0
- data/lib/rubino/llm/scenarios/wide-table.yml +121 -0
- data/lib/rubino/llm/scenarios/with-approvals.yml +50 -0
- data/lib/rubino/llm/scenarios/with-artifacts.yml +98 -0
- data/lib/rubino/llm/scenarios/with-clarify.yml +32 -0
- data/lib/rubino/llm/scenarios/with-reasoning.yml +175 -0
- data/lib/rubino/llm/scenarios/with-uploads.yml +104 -0
- data/lib/rubino/llm/thinking_support.rb +84 -0
- data/lib/rubino/llm/tool_bridge.rb +89 -0
- data/lib/rubino/logger.rb +99 -0
- data/lib/rubino/mcp/manager.rb +180 -0
- data/lib/rubino/mcp/mcp_tool_wrapper.rb +69 -0
- data/lib/rubino/mcp.rb +57 -0
- data/lib/rubino/memory/backend.rb +104 -0
- data/lib/rubino/memory/backends/default.rb +101 -0
- data/lib/rubino/memory/backends/sqlite.rb +653 -0
- data/lib/rubino/memory/backends.rb +53 -0
- data/lib/rubino/memory/deduplicator.rb +74 -0
- data/lib/rubino/memory/extractor.rb +85 -0
- data/lib/rubino/memory/flusher.rb +31 -0
- data/lib/rubino/memory/retriever.rb +50 -0
- data/lib/rubino/memory/sqlite_extraction_prompt.rb +70 -0
- data/lib/rubino/memory/sqlite_graph.rb +154 -0
- data/lib/rubino/memory/store.rb +228 -0
- data/lib/rubino/memory/threat_scanner.rb +68 -0
- data/lib/rubino/metrics.rb +175 -0
- data/lib/rubino/modes.rb +93 -0
- data/lib/rubino/oauth/connection_repository.rb +95 -0
- data/lib/rubino/oauth/provider/github.rb +75 -0
- data/lib/rubino/oauth/provider/google.rb +59 -0
- data/lib/rubino/oauth/provider.rb +149 -0
- data/lib/rubino/oauth/registry.rb +86 -0
- data/lib/rubino/oauth/token_encryptor.rb +87 -0
- data/lib/rubino/plugins/registry.rb +75 -0
- data/lib/rubino/plugins.rb +86 -0
- data/lib/rubino/run/approval_gate.rb +243 -0
- data/lib/rubino/run/attachment_downloader.rb +166 -0
- data/lib/rubino/run/event_store.rb +74 -0
- data/lib/rubino/run/executor.rb +383 -0
- data/lib/rubino/run/gate_registry.rb +39 -0
- data/lib/rubino/run/recorder.rb +69 -0
- data/lib/rubino/run/repository.rb +118 -0
- data/lib/rubino/run/session_approval_cache.rb +118 -0
- data/lib/rubino/security/allowlist_persister.rb +55 -0
- data/lib/rubino/security/approval_policy.rb +227 -0
- data/lib/rubino/security/command_allowlist.rb +24 -0
- data/lib/rubino/security/dangerous_patterns.rb +118 -0
- data/lib/rubino/security/deny_persister.rb +73 -0
- data/lib/rubino/security/doom_loop_detector.rb +43 -0
- data/lib/rubino/security/hardline_guard.rb +105 -0
- data/lib/rubino/security/pattern_matcher.rb +62 -0
- data/lib/rubino/security/prefix_deriver.rb +124 -0
- data/lib/rubino/security/readonly_commands.rb +211 -0
- data/lib/rubino/session/exporter.rb +101 -0
- data/lib/rubino/session/message.rb +77 -0
- data/lib/rubino/session/repository.rb +295 -0
- data/lib/rubino/session/store.rb +198 -0
- data/lib/rubino/session/summary_store.rb +65 -0
- data/lib/rubino/skills/prompt_index.rb +85 -0
- data/lib/rubino/skills/registry.rb +208 -0
- data/lib/rubino/skills/skill.rb +176 -0
- data/lib/rubino/skills/skill_tool.rb +215 -0
- data/lib/rubino/skills/state_repository.rb +37 -0
- data/lib/rubino/skills/toggle.rb +26 -0
- data/lib/rubino/tools/answer_child_tool.rb +83 -0
- data/lib/rubino/tools/ask_parent_tool.rb +232 -0
- data/lib/rubino/tools/attach_file_tool.rb +120 -0
- data/lib/rubino/tools/background_tasks.rb +520 -0
- data/lib/rubino/tools/base.rb +222 -0
- data/lib/rubino/tools/custom_tool_loader.rb +119 -0
- data/lib/rubino/tools/edit_tool.rb +122 -0
- data/lib/rubino/tools/git_tool.rb +71 -0
- data/lib/rubino/tools/github_tool.rb +233 -0
- data/lib/rubino/tools/glob_tool.rb +69 -0
- data/lib/rubino/tools/grep_tool.rb +206 -0
- data/lib/rubino/tools/memory_tool.rb +184 -0
- data/lib/rubino/tools/multi_edit_tool.rb +110 -0
- data/lib/rubino/tools/patch_tool.rb +260 -0
- data/lib/rubino/tools/probe_tool.rb +175 -0
- data/lib/rubino/tools/question_tool.rb +128 -0
- data/lib/rubino/tools/read_attachment_tool.rb +180 -0
- data/lib/rubino/tools/read_tool.rb +212 -0
- data/lib/rubino/tools/read_tracker.rb +98 -0
- data/lib/rubino/tools/registry.rb +166 -0
- data/lib/rubino/tools/result.rb +113 -0
- data/lib/rubino/tools/ruby_tool.rb +0 -0
- data/lib/rubino/tools/session_search_tool.rb +103 -0
- data/lib/rubino/tools/shell_input_tool.rb +96 -0
- data/lib/rubino/tools/shell_kill_tool.rb +76 -0
- data/lib/rubino/tools/shell_output_tool.rb +72 -0
- data/lib/rubino/tools/shell_registry.rb +158 -0
- data/lib/rubino/tools/shell_tail_tool.rb +118 -0
- data/lib/rubino/tools/shell_tool.rb +330 -0
- data/lib/rubino/tools/steer_tool.rb +118 -0
- data/lib/rubino/tools/subagent_probe.rb +89 -0
- data/lib/rubino/tools/summarize_file_tool.rb +182 -0
- data/lib/rubino/tools/task_result_tool.rb +90 -0
- data/lib/rubino/tools/task_stop_tool.rb +80 -0
- data/lib/rubino/tools/task_tool.rb +622 -0
- data/lib/rubino/tools/test_tool.rb +454 -0
- data/lib/rubino/tools/todo_tool.rb +93 -0
- data/lib/rubino/tools/tool_call_repository.rb +33 -0
- data/lib/rubino/tools/vision_tool.rb +85 -0
- data/lib/rubino/tools/webfetch_tool.rb +153 -0
- data/lib/rubino/tools/websearch_tool.rb +179 -0
- data/lib/rubino/tools/write_tool.rb +61 -0
- data/lib/rubino/trust.rb +88 -0
- data/lib/rubino/ui/api.rb +296 -0
- data/lib/rubino/ui/base.rb +252 -0
- data/lib/rubino/ui/bottom_composer.rb +1599 -0
- data/lib/rubino/ui/cli.rb +1987 -0
- data/lib/rubino/ui/completion_menu.rb +321 -0
- data/lib/rubino/ui/completion_source.rb +284 -0
- data/lib/rubino/ui/escape_reader.rb +169 -0
- data/lib/rubino/ui/indented_io.rb +88 -0
- data/lib/rubino/ui/input_history.rb +108 -0
- data/lib/rubino/ui/live_region.rb +183 -0
- data/lib/rubino/ui/markdown_renderer.rb +506 -0
- data/lib/rubino/ui/notifier.rb +163 -0
- data/lib/rubino/ui/null.rb +195 -0
- data/lib/rubino/ui/paste_store.rb +176 -0
- data/lib/rubino/ui/printer_base.rb +79 -0
- data/lib/rubino/ui/probe_wait_indicator.rb +75 -0
- data/lib/rubino/ui/queued_indicators.rb +66 -0
- data/lib/rubino/ui/status_bar.rb +100 -0
- data/lib/rubino/ui/stdout_proxy.rb +161 -0
- data/lib/rubino/ui/streaming_markdown.rb +186 -0
- data/lib/rubino/ui/subagent_cards.rb +134 -0
- data/lib/rubino/ui/subagent_view.rb +255 -0
- data/lib/rubino/ui.rb +21 -0
- data/lib/rubino/update_check.rb +187 -0
- data/lib/rubino/util/duration.rb +23 -0
- data/lib/rubino/util/hyperlink.rb +105 -0
- data/lib/rubino/util/output.rb +145 -0
- data/lib/rubino/util/secrets_mask.rb +83 -0
- data/lib/rubino/version.rb +5 -0
- data/lib/rubino/workspace.rb +85 -0
- data/lib/rubino-agent.rb +5 -0
- data/lib/rubino.rb +318 -0
- data/mise.toml +2 -0
- data/rubino-agent.gemspec +103 -0
- data/skills/ruby-expert/SKILL.md +67 -0
- data/skills/ruby-expert/references/concurrency.md +357 -0
- data/skills/ruby-expert/references/datetime-and-encoding.md +363 -0
- data/skills/ruby-expert/references/errors-and-types.md +460 -0
- data/skills/ruby-expert/references/gem-authoring.md +459 -0
- data/skills/ruby-expert/references/language-idioms.md +465 -0
- data/skills/ruby-expert/references/metaprogramming.md +339 -0
- data/skills/ruby-expert/references/oo-design.md +553 -0
- data/skills/ruby-expert/references/performance.md +383 -0
- data/skills/ruby-expert/references/rails.md +424 -0
- data/skills/ruby-expert/references/security.md +404 -0
- data/skills/ruby-expert/references/testing.md +473 -0
- data/skills/ruby-expert/references/tooling.md +466 -0
- metadata +856 -0
data/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# rubino
|
|
2
|
+
|
|
3
|
+
A coding & automation **agent** — small, self-contained, and built to run *where the work is*: directly on your machine or inside a VM. You drop it onto a box and it works there, reachable over a CLI and an HTTP API. It is not a heavy framework; it's a lightweight agent with persistent memory, sessions, and context compaction. Built on [ruby_llm](https://github.com/crmne/ruby_llm).
|
|
4
|
+
|
|
5
|
+
## Why rubino
|
|
6
|
+
|
|
7
|
+
- **Runs where the work is** — a single gem on the machine (or VM) that holds the code, not a remote service you pipe files to.
|
|
8
|
+
- **Persistent memory** — a tiny SQLite "Zep"-style fact store that learns about you and the project across sessions.
|
|
9
|
+
- **Context compaction** — automatic compression with session lineage when the conversation outgrows the window.
|
|
10
|
+
- **CLI *and* HTTP API** — an interactive terminal session for humans, a bearer-protected JSON + SSE API for programs.
|
|
11
|
+
- **Real tools, gated** — read/write/edit, shell, ruby, git/github, grep/glob, a structured test runner, vision, and more, behind an approval model with a non-bypassable hardline floor.
|
|
12
|
+
- **Built on ruby_llm** — provider-agnostic: MiniMax, OpenAI, Anthropic, Gemini, or an OpenAI-compatible gateway.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
One line, Linux and macOS (x86_64 / arm64). Installs a compatible Ruby, then the gem — all in user space, no sudo:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
curl -fsSL https://raw.githubusercontent.com/Jhonnyr97/rubino-agent/main/install.sh | bash
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
On **Linux** the installer fetches a precompiled Ruby via [`rv`](https://github.com/spinel-coop/rv). On **macOS**, if [Homebrew](https://brew.sh) is present it asks whether to use Homebrew (`brew install ruby`) or `rv`; without Homebrew it uses `rv` directly. Skip the prompt with `RUBINO_INSTALL_METHOD=brew` or `=rv`.
|
|
23
|
+
|
|
24
|
+
> **Review before you pipe.** Piping a script into your shell runs whatever it contains. Read it first:
|
|
25
|
+
> ```bash
|
|
26
|
+
> curl -fsSL https://raw.githubusercontent.com/Jhonnyr97/rubino-agent/main/install.sh -o install.sh
|
|
27
|
+
> less install.sh && bash install.sh
|
|
28
|
+
> ```
|
|
29
|
+
|
|
30
|
+
The installer is idempotent — safe to re-run — and prints the exact `PATH` line for the `rubino` executable plus the next step.
|
|
31
|
+
|
|
32
|
+
**Manual install** (if you'd rather not pipe, or already manage Ruby yourself):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# With rv (https://rv.dev):
|
|
36
|
+
curl -LsSf https://rv.dev/install | sh
|
|
37
|
+
rv ruby install 3.3.3
|
|
38
|
+
rv run --ruby 3.3.3 gem install rubino-agent
|
|
39
|
+
|
|
40
|
+
# Or with any Ruby >= 3.1 already on your PATH:
|
|
41
|
+
gem install rubino-agent
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
rubino setup # guided first-run: pick a provider, paste a key
|
|
48
|
+
rubino chat # start chatting; ask "what does this project do?"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`rubino setup` runs an interactive wizard that picks a provider/model and stores your API key — no hand-editing of YAML to get a first answer. If you skip the wizard, a bare `rubino chat` from a fresh home launches it for you before the first message.
|
|
52
|
+
|
|
53
|
+
New here? Read **[docs/getting-started.md](docs/getting-started.md)** — install → setup → first working message.
|
|
54
|
+
|
|
55
|
+
In development:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/Jhonnyr97/rubino-agent.git
|
|
59
|
+
cd rubino-agent
|
|
60
|
+
bundle install
|
|
61
|
+
bundle exec rubino setup
|
|
62
|
+
bundle exec rubino chat
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Requirements
|
|
66
|
+
|
|
67
|
+
- Ruby >= 3.1
|
|
68
|
+
- SQLite3
|
|
69
|
+
- An LLM provider API key (MiniMax, OpenAI, Anthropic, or Google) — or any OpenAI-compatible gateway.
|
|
70
|
+
|
|
71
|
+
## Essential commands
|
|
72
|
+
|
|
73
|
+
| Command | What it does |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `rubino setup` | Guided first-run: provider/model/key, config + database |
|
|
76
|
+
| `rubino chat` | Interactive session (bare `chat` auto-resumes your last session) |
|
|
77
|
+
| `rubino chat --new` | Start a fresh session instead of resuming |
|
|
78
|
+
| `rubino prompt "..."` | One-shot, non-interactive (alias for `chat -q`) |
|
|
79
|
+
| `rubino server` | Start the JSON API + SSE server |
|
|
80
|
+
| `rubino doctor` | Check config, credentials, and database health |
|
|
81
|
+
| `rubino tools` | List tools and their enabled/disabled state |
|
|
82
|
+
| `rubino memory list` | Inspect stored memories (uses the active backend) |
|
|
83
|
+
| `rubino version` | Print the version |
|
|
84
|
+
| `rubino update` | Update to the latest published version via RubyGems |
|
|
85
|
+
|
|
86
|
+
`rubino update` runs `gem update rubino-agent` under the active interpreter (multi-Ruby safe); for a source/dev checkout it points you back at the installer instead. On interactive boot rubino shows a single dim line when a newer version is available (`▸ rubino vX.Y available — run \`rubino update\``). The check is cached and refreshed out-of-band (once / 24h, short timeout), so it never slows startup and is silent offline. Set `RUBINO_NO_UPDATE_CHECK=1` to disable it (it is also off when not a TTY or under `CI`). It prints nothing until the gem is actually published. See **[docs/commands.md](docs/commands.md)**.
|
|
87
|
+
|
|
88
|
+
Inside a chat, type `/help` for the slash commands (`/status`, `/sessions`, `/memory`, `/agents`, `/skills`, `/mode`, `/commands`, `/new`, …). The full reference is **[docs/commands.md](docs/commands.md)**.
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
Configuration lives in `~/.rubino/config.yml` (created by `rubino setup`); secrets go in `~/.rubino/.env`. Both follow `RUBINO_HOME` if set. A representative slice (defaults shown):
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
model:
|
|
96
|
+
default: "openai/gpt-4.1" # the shipped default — see the note below
|
|
97
|
+
provider: "auto" # auto | openai | anthropic | bedrock | gemini | minimax | gateway
|
|
98
|
+
temperature: 0.3
|
|
99
|
+
|
|
100
|
+
agent:
|
|
101
|
+
max_turns: 90
|
|
102
|
+
max_tool_iterations: 8
|
|
103
|
+
|
|
104
|
+
memory:
|
|
105
|
+
enabled: true
|
|
106
|
+
backend: "sqlite" # tiny-Zep FTS5 + graph-lite recall (default)
|
|
107
|
+
auto_extract: true
|
|
108
|
+
|
|
109
|
+
compression:
|
|
110
|
+
enabled: true
|
|
111
|
+
threshold: 0.50
|
|
112
|
+
|
|
113
|
+
jobs:
|
|
114
|
+
mode: "inline" # inline | manual | worker
|
|
115
|
+
|
|
116
|
+
tools:
|
|
117
|
+
workspace_strict: true # sandbox write/edit/delete to the workspace
|
|
118
|
+
git: true
|
|
119
|
+
shell: true # ON by default; every command is still approval-gated
|
|
120
|
+
ruby: true
|
|
121
|
+
web: false # gates BOTH webfetch and websearch
|
|
122
|
+
memory: true
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
> **Heads-up on the default model.** The shipped `model.default` is `openai/gpt-4.1`, which ruby_llm's registry resolves to **OpenRouter** — so a first run with no OpenAI/OpenRouter key fails fast with guidance instead of hanging. Run `rubino setup` (the wizard defaults to OpenAI gpt-4.1) or set your provider/key explicitly. See **[docs/models-and-keys.md](docs/models-and-keys.md)**.
|
|
126
|
+
|
|
127
|
+
Full reference (every key, env vars, precedence): **[docs/configuration.md](docs/configuration.md)**.
|
|
128
|
+
|
|
129
|
+
## Documentation
|
|
130
|
+
|
|
131
|
+
- **[Getting started](docs/getting-started.md)** — install → setup → first message
|
|
132
|
+
- **[Models & keys](docs/models-and-keys.md)** — which provider/model/key, per-provider setup blocks
|
|
133
|
+
- **[Commands](docs/commands.md)** — CLI subcommands + slash-command reference
|
|
134
|
+
- **[Configuration](docs/configuration.md)** — full config + env vars + precedence
|
|
135
|
+
- **[Tools](docs/tools.md)** — the built-in tool set and approval behavior
|
|
136
|
+
- **[Skills](docs/skills.md)** — reusable instruction packs, the 3-level disclosure, and `SKILL_LOADED` observability
|
|
137
|
+
- **[Memory](docs/memory.md)** — the SQLite tiny-Zep backend
|
|
138
|
+
- **[Security](docs/security.md)** — approval model, hardline floor, TLS
|
|
139
|
+
- **[Troubleshooting](docs/troubleshooting.md)** — keyed on the exact error strings
|
|
140
|
+
- **[HTTP API](docs/api/v1.md)** · **[Jobs & cron](docs/jobs.md)** · **[OAuth providers](docs/oauth-providers.md)** · **[Architecture](docs/architecture.md)**
|
|
141
|
+
- **[Contributing](CONTRIBUTING.md)** · **[Changelog](CHANGELOG.md)**
|
|
142
|
+
|
|
143
|
+
## Built-in tools
|
|
144
|
+
|
|
145
|
+
The agent ships **33 built-in tools**: `read`, `summarize_file`, `write`, `edit`, `multi_edit`, `grep`, `glob`, `git`, `github`, `shell`, `shell_output`, `shell_tail`, `shell_input`, `shell_kill`, `ruby`, `run_tests`, `apply_patch`, `webfetch`, `websearch`, `question`, `todowrite`, `memory`, `session_search`, `attach_file`, `vision`, `skill`, `task`, `task_result`, `task_stop`, `ask_parent`, `steer`, `probe`, `answer_child`. Each is gated by a `tools.<key>` config flag (opt-out) and the approval model. See **[docs/tools.md](docs/tools.md)**.
|
|
146
|
+
|
|
147
|
+
## Skills
|
|
148
|
+
|
|
149
|
+
Skills are reusable instruction packs (a `SKILL.md` plus optional bundled reference files) that the agent pulls into context only when relevant — it sees a short index of available skills up front (Level 1), loads a skill's full body on demand (Level 2, which emits the `SKILL_LOADED` signal), and reads bundled references when needed (Level 3). They live in `.rubino/skills` / `~/.rubino/skills`, are gated by `tools.skill`, and expose usage/creation metrics. See **[docs/skills.md](docs/skills.md)**.
|
|
150
|
+
|
|
151
|
+
## Fake LLM provider
|
|
152
|
+
|
|
153
|
+
rubino ships a built-in **fake LLM provider** for tests, demos, and integration harnesses. Unlike a mocked adapter, the fake provider plugs into the *real* `Agent::Loop`, the real `ToolExecutor`, the real approvals/clarifications pipeline, and the real SSE stream — scenarios fake only what an LLM would produce (content / thinking chunks and `tool_call` requests). Downstream consumers hit the same surface they would against OpenAI/Anthropic, at zero token cost.
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
model:
|
|
157
|
+
default: "fake/happy-path"
|
|
158
|
+
|
|
159
|
+
providers:
|
|
160
|
+
fake:
|
|
161
|
+
scenarios_dir: "~/.rubino/scenarios" # optional; defaults to built-in
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Any `model_id` starting with `fake` is auto-routed to the fake provider. Because it can short-circuit tool decisions, it is **disabled by default** in `server` and `chat` — set `RUBINO_ALLOW_FAKE=1` to opt in. Production deployments must never set this.
|
|
165
|
+
|
|
166
|
+
## HTTP API
|
|
167
|
+
|
|
168
|
+
Start the bearer-protected JSON API server:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
export RUBINO_API_KEY="$(openssl rand -hex 32)"
|
|
172
|
+
export RUBINO_ENCRYPTION_KEY="$(openssl rand -base64 32)" # required for OAuth routes
|
|
173
|
+
rubino server --port 4820
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Every request carries `Authorization: Bearer <RUBINO_API_KEY>` except `GET /v1/health` and `GET /v1/metrics`. The server binds `127.0.0.1` by default — pass `--host 0.0.0.0` (or set `RUBINO_API_HOST`) to expose it, and only do so behind TLS or a trusted segment. For a remote HTTP client the API can serve over a self-signed cert the client pins (`RUBINO_TLS=1`; read it with `rubino tls-cert`).
|
|
177
|
+
|
|
178
|
+
Full request/response shapes, the error envelope, and SSE replay are in **[docs/api/v1.md](docs/api/v1.md)**.
|
|
179
|
+
|
|
180
|
+
## Planned / on the roadmap
|
|
181
|
+
|
|
182
|
+
These are designed-in but not fully wired yet — don't depend on them in production:
|
|
183
|
+
|
|
184
|
+
- **MCP Support** — connect to Model Context Protocol servers via [ruby_llm-mcp](https://github.com/patvice/ruby_llm-mcp) ([docs/mcp.md](docs/mcp.md)).
|
|
185
|
+
- **Multi-Agent** — Build / Plan / Explore agents with `@mention` routing ([docs/agents.md](docs/agents.md)).
|
|
186
|
+
- **Plugin Hooks** — event hooks for extending behavior ([docs/plugins.md](docs/plugins.md)).
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
bundle install
|
|
192
|
+
bundle exec rspec # run tests
|
|
193
|
+
bundle exec rubino doctor # verify setup
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
See **[CONTRIBUTING.md](CONTRIBUTING.md)** for the full dev/test/release flow.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
[MIT](LICENSE).
|
data/Rakefile
ADDED
data/docs/agents.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Agents & Subagents
|
|
2
|
+
|
|
3
|
+
rubino has two distinct multi-agent surfaces. Only the first one ships today:
|
|
4
|
+
|
|
5
|
+
1. **Background subagents** (✅ shipping) — the agent delegates bounded sub-tasks
|
|
6
|
+
to isolated subagent runs via its `task` tool, and you supervise them with
|
|
7
|
+
`/agents` and `/reply`. This is the surface you will actually use.
|
|
8
|
+
2. **Primary-agent switching** (⏳ not yet wired) — Tab-cycling between primary
|
|
9
|
+
agents and `@mention` routing. The machinery exists (`Agent::Router`,
|
|
10
|
+
`Agent::Definition`, `AgentRegistry`) but no call site passes an agent
|
|
11
|
+
definition yet, so the default (build) agent handles every turn. See
|
|
12
|
+
[the last section](#planned-primary-agent-switching--mentions-not-yet-wired).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Background subagents (what ships)
|
|
17
|
+
|
|
18
|
+
### How they start
|
|
19
|
+
|
|
20
|
+
The MODEL spawns subagents with the `task` tool — you don't start them by hand;
|
|
21
|
+
you ask for something parallelizable ("audit these 4 files in parallel") and the
|
|
22
|
+
agent delegates. By default a `task` call runs in the **background**: it returns
|
|
23
|
+
immediately with a task id (`sa_…`) and the subagent works on its own thread
|
|
24
|
+
while the parent keeps going. When it finishes, the parent is notified with a
|
|
25
|
+
`[background-task] <id> completed` message folded into its turn; the parent can
|
|
26
|
+
also poll with `task_result(<id>)` or cancel with `task_stop(<id>)`.
|
|
27
|
+
`background: false` runs the child inline instead (the parent blocks); it goes
|
|
28
|
+
through the same nesting caps and ownership stamping as a background spawn.
|
|
29
|
+
|
|
30
|
+
Each subagent is **isolated**: it gets a fresh session seeded with ONLY the
|
|
31
|
+
prompt string — the parent transcript never leaks into the child, so the parent
|
|
32
|
+
must put every needed file path / error / detail into the prompt.
|
|
33
|
+
|
|
34
|
+
Built-in subagents the model can delegate to:
|
|
35
|
+
|
|
36
|
+
| Subagent | Access | Description |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| **explore** | Read-only tools | Fast codebase search and navigation (max 20 turns) |
|
|
39
|
+
| **general** | Full tools | Complex multi-step tasks (max 50 turns) |
|
|
40
|
+
|
|
41
|
+
Background subagents live only in the current process (nothing is persisted —
|
|
42
|
+
they die with the CLI/server process).
|
|
43
|
+
|
|
44
|
+
### Nesting and caps
|
|
45
|
+
|
|
46
|
+
Subagents keep the `task` tool, so a subagent CAN spawn its own subagents.
|
|
47
|
+
The tree is bounded in one place (`Tools::BackgroundTasks#reserve`) by three
|
|
48
|
+
config caps; when one is hit, the spawn is refused with a reason-specific
|
|
49
|
+
message instead of fanning out unbounded work:
|
|
50
|
+
|
|
51
|
+
| Config key | Default | Meaning |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `tasks.max_depth` | `2` | Max nesting depth (human → child → grandchild) |
|
|
54
|
+
| `tasks.max_children_per_node` | `3` | Max live children per parent |
|
|
55
|
+
| `tasks.max_concurrent_total` | `8` | Max live subagents across the whole tree |
|
|
56
|
+
|
|
57
|
+
### Statuses
|
|
58
|
+
|
|
59
|
+
`/agents` and the live cards show each child's state:
|
|
60
|
+
|
|
61
|
+
| Glyph | Status | Meaning | You act via |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `●` | `running` | Working (last activity shown) | — |
|
|
64
|
+
| `●` | `needs_approval` | A child tool needs your approval | `/agents <id>` |
|
|
65
|
+
| `⛔` | `blocked_on_human` | Asked a question only YOU can answer (`ask_parent` escalated to the human) | `/reply <id> <answer>` |
|
|
66
|
+
| `◷` | `blocked_on_parent` | Asked its agent-parent a question — the PARENT MODEL answers (`answer_child`); not your job unless you choose to step in with `/reply` | (optional) `/reply <id>` |
|
|
67
|
+
| `◌` | `stopping` | Stop requested; unwinding at its next checkpoint | — |
|
|
68
|
+
| `✓` | `done` | Finished; result available | `/agents <id>` |
|
|
69
|
+
| `✗` | `failed` | Errored; error available | `/agents <id>` |
|
|
70
|
+
| `⊘` | `stopped` | Cancelled by you (`--stop`); blocked descendants unwound; tools that completed before the stop may have left side effects | `/agents <id>` |
|
|
71
|
+
|
|
72
|
+
A `⛔ N subagent waiting on you` marker persists until you `/reply`.
|
|
73
|
+
|
|
74
|
+
### Supervising from the CLI: `/agents` and `/reply`
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/agents # list background subagents (status, tools run, activity)
|
|
78
|
+
/agents <id> # drill in: live watch while running, result/error when done
|
|
79
|
+
/agents <id> --stop # cancel a running subagent (blocked descendants unwind too)
|
|
80
|
+
/agents <id> steer "note" # park a note folded into the child's context at its next turn
|
|
81
|
+
/agents <id> probe "question" # ephemeral read-only peek — nothing is saved to the child
|
|
82
|
+
/reply <id> <answer> # answer a child blocked on an ask_parent question
|
|
83
|
+
/reply # bare: list the subagents currently blocked on you
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`/tasks` is an alias for `/agents`. Stopping a node cancels its descendants'
|
|
87
|
+
ask-gates too, so a blocking question anywhere in the subtree unwinds at once.
|
|
88
|
+
|
|
89
|
+
**steer** is a persistent course-correction: the note enters the child's context
|
|
90
|
+
at its next turn boundary and changes its trajectory.
|
|
91
|
+
**probe** is ephemeral: a read-only side-inference over a snapshot of the
|
|
92
|
+
child's transcript; the answer is shown to you and discarded — nothing is
|
|
93
|
+
appended to the child's history.
|
|
94
|
+
|
|
95
|
+
### Parent↔child channels (model-driven)
|
|
96
|
+
|
|
97
|
+
The same three verbs are MODEL-callable tools, so an agent-parent can supervise
|
|
98
|
+
its own children the way you supervise yours. All are gated by `tools.task` and
|
|
99
|
+
**ownership-scoped at call time** — a caller can only touch its own direct
|
|
100
|
+
children (see [tools.md](tools.md) for parameters):
|
|
101
|
+
|
|
102
|
+
- **`steer(task_id, note)`** — park a persistent note on one of your running
|
|
103
|
+
children; it folds into the child's context at its next turn.
|
|
104
|
+
- **`probe(task_id, question, live:)`** — check on a child without disturbing it.
|
|
105
|
+
`live: false` (default) is a FREE registry snapshot (status, tool count, last
|
|
106
|
+
activity, recent lines); `live: true` is a billed one-shot model peek over the
|
|
107
|
+
child's transcript, budgeted per child (`tasks.max_live_probes_per_child`,
|
|
108
|
+
default 5).
|
|
109
|
+
- **`ask_parent(question, blocking:)`** — the child→parent escalation (only
|
|
110
|
+
available to subagents). `blocking: false` (default) keeps the child working
|
|
111
|
+
and folds the answer in later; `blocking: true` parks the child until answered,
|
|
112
|
+
bounded by `tasks.ask_parent_timeout` (default 900s — on expiry the child
|
|
113
|
+
proceeds with its best judgement instead of hanging).
|
|
114
|
+
Routing depends on who spawned the child: an agent-parent gets the question as
|
|
115
|
+
a note and answers with `answer_child` (child shows `◷ blocked_on_parent`); a
|
|
116
|
+
human-spawned child escalates straight to you (`⛔ blocked_on_human`, answered
|
|
117
|
+
via `/reply`). A parent that cannot answer from its own context escalates by
|
|
118
|
+
calling its OWN `ask_parent` — questions bubble up the tree to the human.
|
|
119
|
+
- **`answer_child(task_id, answer)`** — the agent-parent's `/reply`: delivers
|
|
120
|
+
the answer into the asking child's context (unblocks a blocking ask, folds in
|
|
121
|
+
for a non-blocking one).
|
|
122
|
+
|
|
123
|
+
### Approvals inside a background child
|
|
124
|
+
|
|
125
|
+
When a background child's tool needs human approval, the child parks and the
|
|
126
|
+
entry flips to `needs_approval` with the question/command shown on its card;
|
|
127
|
+
resolve it via `/agents <id>`. In `yolo` mode the usual approval-skip rules
|
|
128
|
+
apply (hardline floor still enforced — see [security.md](security.md)).
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Built-in agent definitions
|
|
133
|
+
|
|
134
|
+
These definitions exist in `Agent::AgentRegistry` today. The two *subagents*
|
|
135
|
+
are live as `task` targets; the two *primary* agents are only reachable as the
|
|
136
|
+
default (`build`) or via the plan **mode** (`/mode plan`), not via agent
|
|
137
|
+
switching; the *utility* agents are internal.
|
|
138
|
+
|
|
139
|
+
| Agent | Type | Access | Description |
|
|
140
|
+
|-------|------|--------|-------------|
|
|
141
|
+
| **build** | primary | Full tools | Default development agent. Handles every turn today. |
|
|
142
|
+
| **plan** | primary | Read-only | Analysis/planning definition (the shipping read-only surface is `/mode plan`). |
|
|
143
|
+
| **explore** | subagent | Read-only | Fast codebase search and navigation (`task` target). |
|
|
144
|
+
| **general** | subagent | Full tools | Complex multi-step tasks (`task` target). |
|
|
145
|
+
| **compaction** | utility | None | Internal: compresses context. Hidden. |
|
|
146
|
+
| **title** | utility | None | Internal: generates session titles. Hidden. |
|
|
147
|
+
|
|
148
|
+
### Custom agents (via code)
|
|
149
|
+
|
|
150
|
+
`AgentRegistry#register` accepts custom definitions programmatically:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
Rubino.agent_registry.register(
|
|
154
|
+
Rubino::Agent::Definition.new(
|
|
155
|
+
name: "security",
|
|
156
|
+
type: :subagent,
|
|
157
|
+
description: "Security-focused code review",
|
|
158
|
+
system_prompt: "You are a security expert…",
|
|
159
|
+
tools: %w[read grep glob],
|
|
160
|
+
permissions: { "shell *" => "deny", "write *" => "deny" }
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A registered `:subagent` definition immediately becomes a valid `task` target
|
|
166
|
+
(it is advertised in the `task` tool's description). Each definition can carry
|
|
167
|
+
its own model, system prompt, tool list (`:all`, `:read_only`, or names),
|
|
168
|
+
pattern-based permission overrides (merged over the global rules by
|
|
169
|
+
`ApprovalPolicy`), MCP-server scoping, and a `max_turns` budget.
|
|
170
|
+
|
|
171
|
+
> **Note:** the `agents:` key in `config.yml` is reserved but **not yet read**
|
|
172
|
+
> by the registry — declaring custom agents in config has no effect today.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Planned: primary-agent switching & @mentions (not yet wired)
|
|
177
|
+
|
|
178
|
+
> **Status:** the machinery exists — `Agent::Router` (@mention detection,
|
|
179
|
+
> Tab-cycling, default routing) and the `agent_definition:` plumbing through the
|
|
180
|
+
> runner — but **no call site passes an agent definition**, so Tab and
|
|
181
|
+
> `@explore`/`@plan`/`@general` mentions currently do nothing. Use the
|
|
182
|
+
> background-subagent surface above for real work.
|
|
183
|
+
|
|
184
|
+
The intended design: press **Tab** to cycle through primary agents, or route a
|
|
185
|
+
single message with an `@mention`:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
you > @explore Where is the database connection configured?
|
|
189
|
+
you > @plan How should we restructure the auth module?
|
|
190
|
+
```
|