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/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.3.3
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# AGENTS.md — rubino
|
|
2
|
+
|
|
3
|
+
Onboarding for anyone (human or AI) working on this codebase.
|
|
4
|
+
|
|
5
|
+
## Vision
|
|
6
|
+
|
|
7
|
+
`rubino` is a **micro agent**: a small, self-contained Ruby gem that runs an LLM-driven coding/automation agent *on the machine where the work happens* — the user's PC or a VM. You drop it onto that machine and it works there, reachable over a clean HTTP API and a CLI. It executes conversation turns with tool calls, streams events, manages skills, and persists sessions/memory in a single SQLite file. It can be embedded as a library, run as a CLI, or run as a small service behind a REST gateway.
|
|
8
|
+
|
|
9
|
+
It is a lightweight agent, **not a heavy framework**. We pick the few primitives that have real value and skip everything else. Scope discipline is part of the product.
|
|
10
|
+
|
|
11
|
+
## Non-goals (explicit)
|
|
12
|
+
|
|
13
|
+
These decisions are permanent. Reopen only with a written justification.
|
|
14
|
+
|
|
15
|
+
- **No dual HTTP APIs.** One server, one versioned prefix (`/v1`). When we break, bump to `/v2`. Never aliases.
|
|
16
|
+
- **No multi-tenant inside one process.** One instance = one workspace = one identity. Multi-tenant deployments run N instances.
|
|
17
|
+
- **No dashboard / theme system / plugin hub.** The dashboard is whatever client consumes our API. We don't ship a UI server.
|
|
18
|
+
- **No skill catalog bundled.** Skills are user-supplied directories. We don't ship a catalog.
|
|
19
|
+
- **No compat layers.** Renamed something? Rename everywhere. Reshaped a response? Bump version.
|
|
20
|
+
- **No auto-update endpoints.** Deploy = gem update + restart. Period.
|
|
21
|
+
- **No backwards-compat shims, no _deprecated suffixes, no "legacy" anything.**
|
|
22
|
+
|
|
23
|
+
## Architectural rules (load-bearing)
|
|
24
|
+
|
|
25
|
+
These are load-bearing. When a PR violates them, it is the PR that gets redone.
|
|
26
|
+
|
|
27
|
+
1. **One interface per concept.** One `LLM::Adapter`, one `Tools::Base`, one `Memory::Store`. Variants are subclasses, not parallels.
|
|
28
|
+
2. **No PORO commands in `lib/`.** Domain logic on models/repositories; orchestration as class methods. No `app/services/Foo::DoThingCommand`.
|
|
29
|
+
3. **No optional-magic params.** Signatures explicit. If long, refactor responsibilities, don't add `**opts`.
|
|
30
|
+
4. **Schema-validated input at boundaries** (HTTP, CLI, MCP). Internals trust types.
|
|
31
|
+
5. **Typed errors only.** `raise NotFoundError, "session"` not `raise "session not found"`.
|
|
32
|
+
6. **Test contracts, not implementation.** Internals change without breaking tests, not vice versa.
|
|
33
|
+
7. **Structured logging from day one.** JSON-line. No `puts`/`pp` outside the UI layer. Logger DI-injected.
|
|
34
|
+
8. **No global singletons.** Config flows from the main loop into everything via constructor.
|
|
35
|
+
9. **Handlers stay thin.** Endpoint >30 LOC → extract `Operation` class.
|
|
36
|
+
10. **Less code, less bugs.** A refactor is good if it deletes net lines. Distrust preparatory abstractions (PORO/Result/custom errors before you need them).
|
|
37
|
+
|
|
38
|
+
## Tech stack
|
|
39
|
+
|
|
40
|
+
| Layer | Choice | Why |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| LLM | `ruby_llm ~> 1.0` (thin wrap) | One mature gem covering OpenAI/Anthropic/Gemini/Bedrock. We never call providers directly. |
|
|
43
|
+
| MCP | `ruby_llm-mcp ~> 0.8` | Pairs natively with `ruby_llm`. |
|
|
44
|
+
| HTTP server | `rack` + `puma` | Standard. WEBrick is gone. |
|
|
45
|
+
| DB | `sequel` + `sqlite3` | One file, zero ops. Postgres later if multi-writer matters. |
|
|
46
|
+
| Schema | `dry-schema` | Boundary validation. |
|
|
47
|
+
| Scheduler | `rufus-scheduler` | Cron syntax, in-process. |
|
|
48
|
+
| Config | `dry-configurable` | Already in use. |
|
|
49
|
+
| CLI | `thor` | Already in use. |
|
|
50
|
+
| Autoload | `zeitwerk` | Already in use. |
|
|
51
|
+
| CLI UI | `tty-*` + `reline` | The interactive `rubino chat` prompt (history, completion, multi-line editing). |
|
|
52
|
+
|
|
53
|
+
`ruby_llm` is the foundation. `LLM::RubyLLMAdapter` is a thin wrapper: it configures `RubyLLM`, delegates `chat`/`stream`/`model_info`, and exposes nothing the underlying gem can't already do. **If you find yourself adding business logic to the adapter, push it into the run loop instead.**
|
|
54
|
+
|
|
55
|
+
## Layout
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
lib/rubino/
|
|
59
|
+
agent/ # run loop, lifecycle, multi-agent routing
|
|
60
|
+
api/ # HTTP gateway (Rack app, middleware, operations)
|
|
61
|
+
cli/ # thor commands
|
|
62
|
+
config/ # dry-configurable
|
|
63
|
+
context/ # compaction, prompt assembly
|
|
64
|
+
interaction/ # conversation lifecycle primitives
|
|
65
|
+
jobs/ # scheduler + worker (cron)
|
|
66
|
+
llm/ # ruby_llm adapter + model registry
|
|
67
|
+
mcp/ # MCP client + tool wrapper
|
|
68
|
+
memory/ # Memory::Store + extractors + retrievers
|
|
69
|
+
oauth/ # OAuth providers + connection storage
|
|
70
|
+
security/ # approval policy, sandboxing
|
|
71
|
+
session/ # repository + persistence
|
|
72
|
+
skills/ # SKILL.md loader
|
|
73
|
+
tools/ # built-in tools
|
|
74
|
+
ui/ # cli/null/api stub UIs
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Surfaces this project exposes
|
|
78
|
+
|
|
79
|
+
- **HTTP API** (`/v1/*`) — the canonical interface. See `docs/api/v1.md`.
|
|
80
|
+
- **CLI** — `rubino {setup,chat,prompt,server,config,memory,sessions,jobs,tools,doctor,version}`.
|
|
81
|
+
- **Library** — `require "rubino"; Rubino.run(...)`.
|
|
82
|
+
|
|
83
|
+
The interactive CLI ships as part of `rubino chat`. Multi-agent routing, MCP, and plugin hooks are designed in but not fully wired yet.
|
|
84
|
+
|
|
85
|
+
## Working on this codebase
|
|
86
|
+
|
|
87
|
+
- Read `docs/architecture.md` for the bigger picture.
|
|
88
|
+
- Read `docs/api/v1.md` for HTTP contract.
|
|
89
|
+
- Read `docs/oauth-providers.md` for OAuth design.
|
|
90
|
+
- Run `bundle exec rspec` before pushing.
|
|
91
|
+
- Don't commit if rubocop fails.
|
|
92
|
+
|
|
93
|
+
## Git/commit conventions
|
|
94
|
+
|
|
95
|
+
- Conventional commits welcome but not required.
|
|
96
|
+
- No AI attribution lines in commit messages.
|
|
97
|
+
- Small commits over big ones. A commit that touches 30 files needs a justification.
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
### Added — CLI redesign & in-chat surfaces
|
|
6
|
+
|
|
7
|
+
A scroll-native `rubino chat` refresh plus several new slash commands and input affordances. All are documented under [docs/commands.md](docs/commands.md) and [docs/configuration.md](docs/configuration.md).
|
|
8
|
+
|
|
9
|
+
- **Rail input + status bar.** The chat input now leads with a red `▍` rail and a clean `❯` caret; a dim status bar pinned under the input shows the session mode (dim `default` / yellow `plan` / red `yolo`), the resolved model id, and context saturation. Configurable via `display.statusbar` (default on), `display.tool_output_preview_lines`, and `display.input_max_rows`.
|
|
10
|
+
- **File-backed paste pipeline.** A multi-line paste collapses to a `[Pasted text #N +M lines]` placeholder that expands on send; a very large paste overflows to `<home>/sessions/<id>/paste_N.txt` with a read-tool pointer. Tuned by `paste.collapse_lines` and `paste.file_threshold_tokens`.
|
|
11
|
+
- **`/model`** — show or switch the live session model (persists `model.default`, retargets the running session).
|
|
12
|
+
- **Context hygiene** — `/compact` (compact now), `/clear` (alias for `/new`), `/export [path]` (write the transcript as markdown).
|
|
13
|
+
- **`Esc Esc` rewind** — at the idle prompt, opens a picker over previous messages and forks the session before the chosen one, pre-filled for editing.
|
|
14
|
+
- **Notifications** (`notifications.*`) — attention signals (terminal bell / iTerm2 OSC 9 / optional `command` hook) on a long turn finishing, an approval prompt, or a blocked subagent.
|
|
15
|
+
- **Auto-allow read-only shell** (`approvals.auto_allow_readonly`, default on; `approvals.readonly_commands` to extend) — provably read-only commands (`ls`, `grep`, `git log`, …) run without a prompt, below the hardline floor and `permissions: deny`. See [docs/security.md](docs/security.md#auto-allowed-read-only-commands).
|
|
16
|
+
- **`!` bang prefix** — run a shell line yourself, no approval gate; output streams into the transcript and is injected so the next turn can act on it.
|
|
17
|
+
- **In-chat management surfaces** — `/mcp` (list/restart/disable MCP servers), `/jobs` (the persistent job queue), and `/config` (read/set effective config in the REPL).
|
|
18
|
+
- **Type-ahead while working** — Enter interrupts and runs your line next; Alt+Enter (or `/queued`) queues it after the current turn.
|
|
19
|
+
|
|
20
|
+
### Fixed — approval-model safety (W3: #152 #144 #143 #147 #151)
|
|
21
|
+
|
|
22
|
+
- **Shift+Tab can no longer blind-cycle into yolo** (#152). The press that
|
|
23
|
+
lands on yolo only ARMS it and shows a confirm toast ("press shift+tab again
|
|
24
|
+
to confirm"); a deliberate second press confirms, a blind mash keeps
|
|
25
|
+
re-arming and never confirms. The toast counts running background subagents
|
|
26
|
+
whose approval gates would drop. An explicit `/mode yolo` stays direct but
|
|
27
|
+
now warns once when live children would start running gated actions
|
|
28
|
+
unprompted.
|
|
29
|
+
- **A background-task event can no longer auto-deny an open child approval
|
|
30
|
+
prompt** (#144). The `/agents <id>` `[o]nce/[a]lways/[n]o` prompt treats an
|
|
31
|
+
empty/aborted read as "ask again" (never as an answer); after repeated empty
|
|
32
|
+
reads it leaves the child parked instead of denying. Card repaints are also
|
|
33
|
+
suppressed while an interactive prompt owns the terminal, so a completion
|
|
34
|
+
fold-in can't paint over (or abort) the blocked read. Denying now requires
|
|
35
|
+
an explicit keypress.
|
|
36
|
+
- **Policy denials are no longer reported to the model as "denied by user"**
|
|
37
|
+
(#143). `Tools::Result.denied` now threads the deny reason: the hardline
|
|
38
|
+
floor, a `permissions: deny` rule and the doom-loop guard each get their own
|
|
39
|
+
message (all stating "not by the user"), and the doom-loop one nudges the
|
|
40
|
+
model to change strategy instead of retrying. Only a real human rejection
|
|
41
|
+
still reads "Tool execution denied by user."
|
|
42
|
+
- **Enter is no longer swallowed by the verb-suggestion dropdown on a complete
|
|
43
|
+
command** (#147). With the menu open, Enter submits when the typed token
|
|
44
|
+
already equals the (sole/selected) candidate — e.g. the exact
|
|
45
|
+
`/agents sa_xxx` the approval hint tells you to run — and when the argument
|
|
46
|
+
slot is empty (`/agents sa_xxx ` with the steer/probe/--stop menu open).
|
|
47
|
+
Arrow-navigating onto a candidate still makes Enter accept it.
|
|
48
|
+
- **read-before-edit is now enforced per SESSION, not per turn** (#151). The
|
|
49
|
+
`ReadTracker` is keyed on the session id, so an edit in a later turn no
|
|
50
|
+
longer forces a redundant re-read (and a second approval round-trip) of an
|
|
51
|
+
unchanged file; any on-disk mtime change still demands a fresh read, and a
|
|
52
|
+
resumed session in a new process still starts conservative.
|
|
53
|
+
|
|
54
|
+
### Added — built-in `ruby-expert` skill
|
|
55
|
+
|
|
56
|
+
Rubino now ships a built-in **`ruby-expert`** skill so every install makes the
|
|
57
|
+
agent a Ruby/Rails expert out of the box — no setup or copy step.
|
|
58
|
+
|
|
59
|
+
- **New skill source: gem-bundled skills.** The skill registry now always scans
|
|
60
|
+
the gem's own `skills/` directory in addition to the user paths
|
|
61
|
+
(`.rubino/skills`, `~/.rubino/skills`). Built-ins are scanned **first**, so a
|
|
62
|
+
same-named user skill still overrides them. Toggle with the new
|
|
63
|
+
`skills.include_builtin` config key (default `true`).
|
|
64
|
+
- **The `ruby-expert` skill** (`skills/ruby-expert/`) is a router `SKILL.md` plus
|
|
65
|
+
twelve bundled references covering: language idioms, metaprogramming, OO design,
|
|
66
|
+
errors & type checking, concurrency, Rails, testing, performance, security,
|
|
67
|
+
tooling, gem authoring, and dates/times/encoding. The agent loads only the
|
|
68
|
+
reference a task needs (3-level progressive disclosure).
|
|
69
|
+
|
|
70
|
+
### Changed — BREAKING: project renamed `ruby-agent` → Rubino
|
|
71
|
+
|
|
72
|
+
The project was rebranded from `ruby-agent` to **Rubino**. This is a clean break with **no backward-compatibility fallbacks** — the old names no longer work and must be updated everywhere they are referenced.
|
|
73
|
+
|
|
74
|
+
- **Gem name:** `ruby_agent` → `rubino-agent` (install with `gem install rubino-agent`). The bare `rubino` name on RubyGems is an unrelated parked gem and is intentionally not used; a thin `lib/rubino-agent.rb` shim lets `require "rubino-agent"` resolve to the canonical `require "rubino"`.
|
|
75
|
+
- **CLI command / executable:** `ruby-agent` → `rubino` (e.g. `rubino setup`, `rubino chat`, `rubino server`).
|
|
76
|
+
- **Ruby module namespace:** `RubyAgent` → `Rubino` (and `RubyAgent::VERSION` → `Rubino::VERSION`).
|
|
77
|
+
- **Config home directory:** `~/.ruby_agent` → `~/.rubino`. No fallback to the old path; move your existing data if you want to keep it.
|
|
78
|
+
- **SQLite database filename:** `ruby_agent.sqlite3` → `rubino.sqlite3` (under the resolved home).
|
|
79
|
+
- **Environment variables:** every `RUBY_AGENT_*` was renamed to `RUBINO_*`. No fallback reads the old names. Full list:
|
|
80
|
+
- `RUBY_AGENT_HOME` → `RUBINO_HOME`
|
|
81
|
+
- `RUBY_AGENT_ENCRYPTION_KEY` → `RUBINO_ENCRYPTION_KEY`
|
|
82
|
+
- `RUBY_AGENT_API_KEY` → `RUBINO_API_KEY`
|
|
83
|
+
- `RUBY_AGENT_API_HOST` → `RUBINO_API_HOST`
|
|
84
|
+
- `RUBY_AGENT_API_PORT` → `RUBINO_API_PORT`
|
|
85
|
+
- `RUBY_AGENT_TLS` → `RUBINO_TLS`
|
|
86
|
+
- `RUBY_AGENT_WEBHOOK_URL` → `RUBINO_WEBHOOK_URL`
|
|
87
|
+
- `RUBY_AGENT_WEBHOOK_SECRET` → `RUBINO_WEBHOOK_SECRET`
|
|
88
|
+
- `RUBY_AGENT_LOG_LEVEL` → `RUBINO_LOG_LEVEL`
|
|
89
|
+
- `RUBY_AGENT_LOG_FORMAT` → `RUBINO_LOG_FORMAT`
|
|
90
|
+
- `RUBY_AGENT_HYPERLINKS` → `RUBINO_HYPERLINKS`
|
|
91
|
+
- `RUBY_AGENT_ALLOW_FAKE` → `RUBINO_ALLOW_FAKE`
|
|
92
|
+
- `RUBY_AGENT_REAL_HOME` → `RUBINO_REAL_HOME`
|
|
93
|
+
- `RUBY_AGENT_GIT_REF` → `RUBINO_GIT_REF`
|
|
94
|
+
- `RUBY_AGENT_RUBY_VERSION` → `RUBINO_RUBY_VERSION`
|
|
95
|
+
|
|
96
|
+
The GitHub repository is `github.com/Jhonnyr97/rubino-agent`. Publishing the renamed gem is **not** done as part of this change.
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
- **Invalid cron schedules can no longer brick the server** (#164): `POST/PATCH /v1/jobs` validates the cron string BEFORE persisting (422 with the canonical validation envelope, nothing committed), and `Jobs::Scheduler` skips + warns on a malformed persisted row instead of crashing boot — existing poisoned DBs recover on restart.
|
|
101
|
+
- **Invalid API keys fail fast** (#126): statusless provider auth rejections (e.g. MiniMax "login fail", "incorrect api key") are classified non-retryable AUTH, surfacing the actionable auth error in one round-trip instead of ~60-90s of silent retries.
|
|
102
|
+
- **`rubino chat --help` / `rubino prompt --help` print usage** (#134): a help flag on any top-level command is intercepted at dispatch and routed to Thor's help — no provider call, no memory writes.
|
|
103
|
+
- **`RUBINO_HOME` relocates skills** (#135): the stock `~/.rubino/skills` entry resolves against the resolved home (same resolver as config/.env/DB/commands), so isolated homes discover their skills.
|
|
104
|
+
- **Order-dependent suite abort** (#163): a spec leaking a pared-down tool registry is cleaned up, and the one-shot exit spec converts an unexpected `SystemExit` into a failing example instead of killing the rspec process.
|
|
105
|
+
|
|
106
|
+
### Documentation
|
|
107
|
+
|
|
108
|
+
- `docs/api/v1.md` aligned to the real API surface (#165, #166, #167): SSE catalogue documents the non-streaming contract (no `message.delta`/`reasoning.delta`), the approval decision enum lists all seven accepted values with semantics, and `GET /v1/sessions`, `/v1/memory*`, `/v1/tasks*` are documented. A doc-drift spec locks the documented route list to the registered routes.
|
|
109
|
+
|
|
110
|
+
## [0.3.0] - 2026-06-06
|
|
111
|
+
|
|
112
|
+
Major capability release: the core conversation loop was ported 1:1 from the reference implementation (formalized LLM boundary, retry/backoff/fallback, degenerate-response recovery), background subagents became the default delegation path, the memory subsystem grew a pluggable backend contract with a tiny-Zep SQLite backend that is now the default, CLI gained image/file input and a scroll-native redesign, and a reference-aligned approval model (hardline floor, dangerous-pattern deny, prefix-derived rules) landed. Consolidated from `feature/subagent-view` (#48) plus #49-#58.
|
|
113
|
+
|
|
114
|
+
### Breaking / upgrade notes
|
|
115
|
+
|
|
116
|
+
- **Default memory backend is now SQLite (tiny-Zep).** `memory.backend` now defaults to `"sqlite"` (previously `"default"`). The new backend reads/writes the `:memory_facts` table; the old `"default"` backend used the `:memories` table. On upgrade, users who were on the previous `"default"` backend and do **not** pin `memory.backend: "default"` in their config will stop reading their prior memory store — the new backend looks only at `:memory_facts`. Your old data in `:memories` is **not deleted**, just no longer read. **No automatic backfill is shipped.** To keep old recall, pin `memory.backend: "default"` in config. (Acceptable for alpha; documented here.)
|
|
117
|
+
|
|
118
|
+
### Added
|
|
119
|
+
|
|
120
|
+
#### Subagents & delegation
|
|
121
|
+
- Background subagents: the `task` tool is now background-by-default (Claude-Code-modeled), so a parent run delegates without blocking on the child (#50). Subagent delegation via the `task` tool is wired on both CLI and API.
|
|
122
|
+
- CLI live nested view of subagent activity (Phase 1).
|
|
123
|
+
|
|
124
|
+
#### CLI image & file input
|
|
125
|
+
- Headless image attachment for one-shot runs (`-q` / prompt) via `--image` and `@image` (#53).
|
|
126
|
+
- Interactive image input: `@image`, drag-drop path, and clipboard paste resolve to `image_paths` (#49). New `ImageInput` path; vision is served via the configured aux model.
|
|
127
|
+
|
|
128
|
+
#### Shell
|
|
129
|
+
- `shell_input` tool to answer interactive prompts of background shells over stdin, enabling interactive subprocesses (#52).
|
|
130
|
+
|
|
131
|
+
#### Memory
|
|
132
|
+
- Pluggable `Memory::Backend` contract + registry (mirrors `Tools::Registry`) and a `memory backend` command to select the active backend.
|
|
133
|
+
- tiny-Zep SQLite memory backend: LLM fact extraction, temporal tracking, and hybrid (FTS5 + best-effort vector) retrieval, with graph-lite entities/edges and 1-hop expansion.
|
|
134
|
+
|
|
135
|
+
#### Approvals (reference-aligned, S1-S7)
|
|
136
|
+
- Non-bypassable hardline deny floor (S1).
|
|
137
|
+
- `DangerousPatterns` with explicit deny-before-allow ordering (S2).
|
|
138
|
+
- `PrefixDeriver` + rule-keyed session approval cache (S3); a `:prefix` rule is only derived for the shell tool.
|
|
139
|
+
- `security.confirm_policy` (`confirm_all` default | `dangerous_only`) (S4).
|
|
140
|
+
- `/v1` enum + enriched approval payload + `always_prefix` persistence (S5).
|
|
141
|
+
- CLI scopes persist derived rules (prefix/command); `always_tool` stays CLI-only (S7).
|
|
142
|
+
|
|
143
|
+
#### Skills
|
|
144
|
+
- Directory-based skills with 3-level progressive disclosure and a registered `SkillTool` (A).
|
|
145
|
+
- Mandatory skill index injected into the system prompt (B).
|
|
146
|
+
- Registry honors `StateRepository` disable on both index and load (C).
|
|
147
|
+
|
|
148
|
+
#### Core loop port (1:1 from the reference)
|
|
149
|
+
- Formalized LLM boundary: normalized `Request` + `Response` (slice 1).
|
|
150
|
+
- `ResponseValidator` + empty-response retry (slice 2).
|
|
151
|
+
- `BackoffPolicy` + `ErrorClassifier` (unknown -> retryable) (slice 3).
|
|
152
|
+
- `ModelCallRunner` inner retry loop (slice 4).
|
|
153
|
+
- `DegenerateResponseRecovery` ladder (prefill-to-continue) (slice 5).
|
|
154
|
+
- `ReasoningManager` (thinking render + echo-back seam) (slice 6).
|
|
155
|
+
- `FallbackChain` (provider/model rotation, restore primary) (slice 7).
|
|
156
|
+
- Max-iterations toolless summary (slice 8).
|
|
157
|
+
- `TruncationContinuation` + dead-branch cleanup (slice 9).
|
|
158
|
+
|
|
159
|
+
#### CLI / UX
|
|
160
|
+
- Scroll-native visual redesign of `rubino chat` (M0 + M2).
|
|
161
|
+
- Bottom-pinned composer (visible input while the agent streams above); steering — type/inject messages mid-turn (queued for next loop boundary).
|
|
162
|
+
- Inline completion dropdown with arrow-key navigation, `@` file picker, and input token highlighting.
|
|
163
|
+
- Assistant markdown rendered while streaming (per-block); markdown tables fit terminal width; `--resume` replays assistant turns as markdown.
|
|
164
|
+
- Built-in fake LLM provider for tests/dev.
|
|
165
|
+
- Multi-arch (x86_64 + arm64) system release image build in CI.
|
|
166
|
+
|
|
167
|
+
### Changed
|
|
168
|
+
|
|
169
|
+
- Memory recall quality: recency and graph signals are demoted to tail supplements so direct FTS/vector matches win and survive single-shot recall (#51).
|
|
170
|
+
- LLM: route MiniMax through the anthropic-compatible endpoint and drop the OpenAI-compat band-aid patches; harden MiniMax-M2.7 (empty-turn retry, unknown-error retry, thinking/temp/max_tokens, overload backoff); recover tool-call turns that close the stream without `[DONE]`.
|
|
171
|
+
- LLM: use ruby_llm `before_message`/`after_message` (`on_new`/`on_end` deprecated); resolve provider once.
|
|
172
|
+
- Human-in-loop: wait on approvals/clarify instead of failing; `shell` tool on by default.
|
|
173
|
+
- `question` tool combines prompt + options into a single `ui.ask` call.
|
|
174
|
+
|
|
175
|
+
### Fixed
|
|
176
|
+
|
|
177
|
+
- **Approval gate**: bounded interruptible wait with auto-deny on expiry (24h -> 15min); an abandoned approval no longer parks a worker thread (which previously froze Puma) (#55, fixes #54). Earlier W1 fix also released the approval-parked worker on cancel/timeout, plus a skill-ref TOCTOU (W3).
|
|
178
|
+
- Interactive prompts work mid-turn (`run_in_terminal`); clarify/question no longer drop prompts on the API path; API accepts image-only runs (blank input + attachments).
|
|
179
|
+
- Per-run `EventBus` isolation (no cross-run event/output bleed); `run.completed` always carries final output on non-streaming runs.
|
|
180
|
+
- SSE idle watchdog no longer kills long silent tool calls.
|
|
181
|
+
- Local Ruby programming errors are classified non-retryable.
|
|
182
|
+
- First-run setup UX; `doctor` is provider-aware and reports migration/provider-key health correctly; tools listing and dropdown/help polish.
|
|
183
|
+
- CLI bughunt batch (B1-B8): reset cancel token each turn so Ctrl-C no longer poisons the session; single `ToolExecutor` sink counts/persists streaming tool results; errored tools render red; loose markdown lists stay one streamed block; `/skills` descriptions word-wrap. Plus Ctrl-C interrupt double-message dedupe, Escape dismisses slash autocomplete, real Available list on unknown command, multi-line paste/resize repaint, and markdown prose word-wrap/headings/table fixes.
|
|
184
|
+
- `grep` accepts a file path (not only a directory); File API workspace rooted at tool cwd so artifacts download.
|
|
185
|
+
|
|
186
|
+
### Security
|
|
187
|
+
|
|
188
|
+
- Universal secure-by-default file attachment handling (#57): classify-by-magic in both directions, typed per-kind preambles, a no-multimodal warning, nonce-framed and defanged inline text, an `attachments.policy` knob, and a unified safety pipeline.
|
|
189
|
+
- SSRF guard always allows loopback hosts for the File API.
|
|
190
|
+
|
|
191
|
+
## [0.2.19] - 2026-06-04
|
|
192
|
+
|
|
193
|
+
Codebase audit follow-up: tool/message integrity, internal-contract fixes, and dead-code removal. Net −1578 LOC. Researched against industry practice (Anthropic/OpenAI tool-pairing, Claude Code, Vercel AI SDK, LangChain, Cline, ruby_llm).
|
|
194
|
+
|
|
195
|
+
### Fixed
|
|
196
|
+
|
|
197
|
+
#### Tool/message integrity
|
|
198
|
+
- Compaction (`Compressor#create_child_session`) and `Session::Forker` no longer drop assistant `metadata[:tool_calls]` / `token_count` when copying messages — strict providers (Anthropic/Bedrock) no longer 400 on orphaned tool pairs after resume. Shared `Session::Store#copy_into` does a faithful copy.
|
|
199
|
+
- `ToolPairSanitizer` predicate fixed: it now detects assistant tool calls via `metadata[:tool_calls]` (was checking `tool_call_id`, which assistant rows never carry — the guard was inert) and is id-aware (a paired trailing call is preserved; an unanswered one is trimmed).
|
|
200
|
+
- `PromptAssembler#build` runs a defensive pre-send tool-pair repair (mirrors Claude Code's pre-call sanitization), recovering sessions already corrupted by the above.
|
|
201
|
+
- `BedrockBearerClient#stream` emits the common chunk contract `{type:, text:, message_id:}` (via `InlineThinkFilter`, with `MESSAGE_COMPLETED` boundaries) and is wrapped in `with_retries`; the `chunk.is_a?(Hash)` fallbacks were removed from the UI now that all adapters are uniform.
|
|
202
|
+
|
|
203
|
+
#### Internal contracts
|
|
204
|
+
- `tools.web` / `tools.browser` now actually gate `webfetch`/`websearch`: tools declare their config gate via `Tools::Base#config_key` (single source of truth shared by the registry and the CLI), instead of name string-munging that never queried the shipped defaults. Closes a "web off but still on" security footgun. Removed the dead `tools.browser` key.
|
|
205
|
+
- `confirm(scope:)` is part of the UI contract on all adapters (`Base`/`CLI`/`Null`/`API`); interactive tool approvals no longer raise `ArgumentError: unknown keyword: :scope`.
|
|
206
|
+
- `RUBINO_HOME` is now the single source of truth for the home path (`config set/get`, `setup`, `doctor` and the server agree); resolution shared in `Config::Loader`.
|
|
207
|
+
- `run.attachments_downloaded` is only emitted when files were actually downloaded (no empty diagnostic event on plain chats).
|
|
208
|
+
- Defensive guard for upstream errors with a string-shaped `error` body (ruby_llm OpenAI streaming `parse_streaming_error`) — the real upstream message surfaces instead of a `TypeError: String does not have #dig method`.
|
|
209
|
+
|
|
210
|
+
### Removed (dead code, audit-verified, −1578 LOC)
|
|
211
|
+
- LSP subsystem (`lsp/`), parallel `Auth` module (superseded by `oauth/`), `Terminal::Composer` (superseded by Reline `UI::LineInput`), `Session::Exporter`, `Memory::ProjectMemory`/`UserProfile` (duplicated `Memory::Retriever`), `Context::CompactionPolicy` (duplicated `TokenBudget`), `Security::RiskClassifier` (per-tool risk is canonical), `Session::Forker` (unused; real fork is the API `parent_session_id` path), `FileSystemTool` + its `file_system` arms, the `snapshots/` subsystem + CLI `undo`/`redo` + config, the unused Recorder live `Queue`, and assorted orphaned methods.
|
|
212
|
+
- Kept (planned features, currently dormant, to be wired later): MCP, multi-agent (Build/Plan/Explore), plugin hooks.
|
|
213
|
+
|
|
214
|
+
## [0.1.0] - 2025-05-11
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
|
|
218
|
+
#### Core
|
|
219
|
+
- Agent loop with iteration budget and tool execution
|
|
220
|
+
- Interaction lifecycle state machine with 14 states
|
|
221
|
+
- Event bus for decoupling core from UI
|
|
222
|
+
- SQLite database with WAL mode (Sequel migrations)
|
|
223
|
+
- Session persistence (messages, tool calls, events)
|
|
224
|
+
- Context compaction with session lineage
|
|
225
|
+
- Summary builder with structured template
|
|
226
|
+
- Token budget management
|
|
227
|
+
- Tool pair sanitizer for compaction integrity
|
|
228
|
+
|
|
229
|
+
#### Agents
|
|
230
|
+
- Multi-agent architecture (Build, Plan, Explore, General)
|
|
231
|
+
- Agent router with @mention support
|
|
232
|
+
- Per-agent model, tools, permissions, and MCP scoping
|
|
233
|
+
- Hidden utility agents (compaction, title)
|
|
234
|
+
|
|
235
|
+
#### Tools (15 built-in)
|
|
236
|
+
- file_system (read/write/list/exists)
|
|
237
|
+
- edit (exact string replacement)
|
|
238
|
+
- grep (ripgrep-backed regex search)
|
|
239
|
+
- glob (file pattern matching)
|
|
240
|
+
- git (status/diff/log/branch/show)
|
|
241
|
+
- github (PRs/issues/reviews via gh CLI or API)
|
|
242
|
+
- shell (command execution with allowlist)
|
|
243
|
+
- ruby (code evaluation)
|
|
244
|
+
- apply_patch (unified diff application)
|
|
245
|
+
- webfetch (URL content retrieval)
|
|
246
|
+
- websearch (Tavily/SearXNG/DuckDuckGo)
|
|
247
|
+
- question (interactive user queries)
|
|
248
|
+
- todowrite (task tracking)
|
|
249
|
+
- lsp (go_to_definition, references, hover, symbols, diagnostics)
|
|
250
|
+
- skill (on-demand skill loading)
|
|
251
|
+
|
|
252
|
+
#### MCP
|
|
253
|
+
- ruby_llm-mcp integration
|
|
254
|
+
- stdio, SSE, and streamable HTTP transports
|
|
255
|
+
- MCPToolWrapper for seamless tool registration
|
|
256
|
+
- Per-agent MCP server scoping
|
|
257
|
+
- OAuth 2.1 with PKCE for remote MCP servers
|
|
258
|
+
|
|
259
|
+
#### Memory
|
|
260
|
+
- Persistent memory store (7 kinds)
|
|
261
|
+
- Auto-extraction from conversations
|
|
262
|
+
- Jaccard similarity deduplication
|
|
263
|
+
- User profile and project memory
|
|
264
|
+
- Memory retriever with char limits
|
|
265
|
+
- Pre-compaction flush
|
|
266
|
+
|
|
267
|
+
#### Jobs
|
|
268
|
+
- SQLite-backed job queue
|
|
269
|
+
- Inline/manual/worker modes
|
|
270
|
+
- Retry with exponential backoff
|
|
271
|
+
- Job run auditing
|
|
272
|
+
- 5 built-in handlers (extract, summarize, compact, cleanup, index)
|
|
273
|
+
|
|
274
|
+
#### Security
|
|
275
|
+
- Pattern-based approval policy (allow/ask/deny)
|
|
276
|
+
- Wildcard matching on tool calls and paths
|
|
277
|
+
- Doom loop detection (3x identical calls)
|
|
278
|
+
- Command allowlist
|
|
279
|
+
- Risk classifier
|
|
280
|
+
|
|
281
|
+
#### Skills
|
|
282
|
+
- SKILL.md files with YAML frontmatter
|
|
283
|
+
- Multi-location discovery
|
|
284
|
+
- Lazy content loading
|
|
285
|
+
- SkillTool for agent access
|
|
286
|
+
|
|
287
|
+
#### Commands
|
|
288
|
+
- Custom slash commands from Markdown files
|
|
289
|
+
- $ARGUMENTS and positional params ($1-$9)
|
|
290
|
+
- Shell output injection (!`command`)
|
|
291
|
+
- File content injection (@path)
|
|
292
|
+
- Built-in: /help, /commands, /skills, /exit
|
|
293
|
+
|
|
294
|
+
#### Plugins
|
|
295
|
+
- 46 hook points across all subsystems
|
|
296
|
+
- File-based plugin loading from .rubino/plugins/
|
|
297
|
+
- Rubino.plugin DSL
|
|
298
|
+
|
|
299
|
+
#### UI
|
|
300
|
+
- CLI adapter (TTY gems)
|
|
301
|
+
- Null adapter (testing)
|
|
302
|
+
- API adapter (structured events)
|
|
303
|
+
- Rich TUI with alternate screen buffer
|
|
304
|
+
- 4 themes (default, dark, light, monokai)
|
|
305
|
+
- Customizable keybinds
|
|
306
|
+
- Input history
|
|
307
|
+
|
|
308
|
+
#### Server
|
|
309
|
+
- JSON API server (WEBrick)
|
|
310
|
+
- REST endpoints for sessions, messages, tools, memory, jobs
|
|
311
|
+
- SSE event streaming (/events)
|
|
312
|
+
- Optional Basic Auth
|
|
313
|
+
|
|
314
|
+
#### Auth
|
|
315
|
+
- OAuth 2.1 client with PKCE
|
|
316
|
+
- Provider authentication (/connect flow)
|
|
317
|
+
- Token persistence (~/.rubino/oauth_tokens.json)
|
|
318
|
+
- GitHub, OpenAI, Anthropic, Google providers
|
|
319
|
+
|
|
320
|
+
#### Configuration
|
|
321
|
+
- YAML config with defaults
|
|
322
|
+
- Enhanced loader: multi-layer precedence
|
|
323
|
+
- Environment variable substitution ({env:VAR})
|
|
324
|
+
- File content inclusion ({file:path})
|
|
325
|
+
- Remote/managed config for enterprise
|
|
326
|
+
- RUBINO_* env var overrides
|
|
327
|
+
|
|
328
|
+
#### LSP
|
|
329
|
+
- JSON-RPC stdio client
|
|
330
|
+
- 37 language servers configured
|
|
331
|
+
- Auto-detection by file extension
|
|
332
|
+
- Operations: definition, references, hover, symbols, diagnostics
|
|
333
|
+
- LspTool for agent access
|
|
334
|
+
|
|
335
|
+
#### Other
|
|
336
|
+
- Session undo/redo via internal git snapshots
|
|
337
|
+
- Session forking at any message
|
|
338
|
+
- Session export (Markdown/JSON)
|
|
339
|
+
- Image support for vision models
|
|
340
|
+
- Custom user-defined tools (Ruby DSL)
|
|
341
|
+
- Code formatters (auto-format after edits)
|
|
342
|
+
- Network proxy support (HTTP/HTTPS/SOCKS)
|
|
343
|
+
- GitHub integration (gh CLI + REST API)
|
|
344
|
+
- Project context file discovery (.rubino.md, AGENTS.md, etc.)
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Contributing to rubino
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve rubino. This is the dev setup, test, and release flow.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
Requirements: Ruby 3.3.3 (see `.ruby-version` / `mise.toml`; the gem supports >= 3.1) and SQLite3.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/Jhonnyr97/rubino-agent.git
|
|
11
|
+
cd rubino-agent
|
|
12
|
+
bundle install
|
|
13
|
+
bundle exec rubino setup # config + database in your home (or set RUBINO_HOME)
|
|
14
|
+
bundle exec rubino doctor # verify
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run the CLI from the checkout with `bundle exec rubino <command>`.
|
|
18
|
+
|
|
19
|
+
> Tip: point `RUBINO_HOME` at a throwaway directory while developing so you don't touch your real `~/.rubino`. For LLM-free work, use the fake provider (`model.default: fake/happy-path` + `RUBINO_ALLOW_FAKE=1`).
|
|
20
|
+
|
|
21
|
+
## Tests
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bundle exec rspec # full suite
|
|
25
|
+
bundle exec rspec path/to/file_spec.rb
|
|
26
|
+
bundle exec rake # default task == spec
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The HTTP boundary is locked by an end-to-end contract suite under `spec/rubino/api/contract/`. When the docs and the contract suite disagree, **the contract suite is canonical** — update the docs to match.
|
|
30
|
+
|
|
31
|
+
## Lint
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bundle exec rubocop # rubocop + rubocop-rspec
|
|
35
|
+
bundle exec rubocop -A # autocorrect
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Pull requests
|
|
39
|
+
|
|
40
|
+
- Branch off `main`; keep changes focused.
|
|
41
|
+
- Add or update specs for behavior changes; touch tests only for what you change.
|
|
42
|
+
- Run `bundle exec rspec` and `bundle exec rubocop` before opening the PR.
|
|
43
|
+
- Update the relevant docs. The slash-command list (`docs/commands.md`) is sourced from `lib/rubino/commands/built_ins.rb` and the config reference (`docs/configuration.md`) from `lib/rubino/config/defaults.rb` — keep them in sync with code.
|
|
44
|
+
- Add a `CHANGELOG.md` entry (the project follows [keep-a-changelog](https://keepachangelog.com/)).
|
|
45
|
+
|
|
46
|
+
## Anti-drift: single sources of truth
|
|
47
|
+
|
|
48
|
+
These enumerations live in code; docs must read from them, not duplicate them by hand:
|
|
49
|
+
|
|
50
|
+
- Slash commands ← `BuiltIns::DESCRIPTIONS` (also drives `/help` and tab-completion).
|
|
51
|
+
- Config keys & defaults ← `Config::Defaults::MODULE_DEFAULTS` (rich inline comments are the descriptions; `Defaults.to_yaml` dumps a commented YAML).
|
|
52
|
+
- Built-in tools ← `Tools::Registry#register_defaults!`.
|
|
53
|
+
|
|
54
|
+
## Releasing
|
|
55
|
+
|
|
56
|
+
The gem version lives in `lib/rubino/version.rb`. Standard Bundler gem tasks are wired (`require "bundler/gem_tasks"`):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# 1. bump lib/rubino/version.rb
|
|
60
|
+
# 2. update CHANGELOG.md (move Unreleased -> the new version)
|
|
61
|
+
# 3. build + tag + push the gem:
|
|
62
|
+
bundle exec rake release
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`rake release` builds the gem, creates the version git tag, pushes commits and the tag, and pushes the `.gem` to the configured host.
|
|
66
|
+
|
|
67
|
+
## Architecture
|
|
68
|
+
|
|
69
|
+
See [docs/architecture.md](docs/architecture.md) and [AGENTS.md](AGENTS.md) for the layer diagram and the core loop overview.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jhon Rojas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|