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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Repro scenario for the CLI streaming-table live-region trail/accumulation bug.
|
|
2
|
+
# Streams a WIDE (5-column) markdown table emitted as MANY small content deltas
|
|
3
|
+
# so the per-chunk live-region repaint cascade appears. Each data ROW is wider
|
|
4
|
+
# than a narrow terminal AND carries double-width emoji (✅ 🔄 ⚠️) spread across
|
|
5
|
+
# cells so the CLAMPED tail of the in-flight row still contains an emoji — whose
|
|
6
|
+
# display width (2) exceeds its char length (1). With the buggy char-count clamp
|
|
7
|
+
# that emoji pushes the "fits in one row" line past the real terminal width, so
|
|
8
|
+
# it wraps and the single-row clear leaves residue that accumulates downward.
|
|
9
|
+
events:
|
|
10
|
+
- type: content
|
|
11
|
+
text: "Here is the status table:\n\n"
|
|
12
|
+
- type: delay_seconds
|
|
13
|
+
value: 0.30
|
|
14
|
+
- type: content
|
|
15
|
+
text: "| Feature | Status | Priority | Owner | Notes |\n"
|
|
16
|
+
- type: delay_seconds
|
|
17
|
+
value: 0.30
|
|
18
|
+
- type: content
|
|
19
|
+
text: "| --- | --- | --- | --- | --- |\n"
|
|
20
|
+
- type: delay_seconds
|
|
21
|
+
value: 0.30
|
|
22
|
+
- type: content
|
|
23
|
+
text: "| HTTP API ✅ "
|
|
24
|
+
- type: delay_seconds
|
|
25
|
+
value: 0.30
|
|
26
|
+
- type: content
|
|
27
|
+
text: "| Done ✅ | High ⚠️ "
|
|
28
|
+
- type: delay_seconds
|
|
29
|
+
value: 0.30
|
|
30
|
+
- type: content
|
|
31
|
+
text: "| Backend ✅ | Stable in prod ✅ "
|
|
32
|
+
- type: delay_seconds
|
|
33
|
+
value: 0.30
|
|
34
|
+
- type: content
|
|
35
|
+
text: "since release v0.2 ✅ ✅ ✅ |\n"
|
|
36
|
+
- type: delay_seconds
|
|
37
|
+
value: 0.30
|
|
38
|
+
- type: content
|
|
39
|
+
text: "| CLI Chat ✅ "
|
|
40
|
+
- type: delay_seconds
|
|
41
|
+
value: 0.30
|
|
42
|
+
- type: content
|
|
43
|
+
text: "| Done ✅ | High ⚠️ "
|
|
44
|
+
- type: delay_seconds
|
|
45
|
+
value: 0.30
|
|
46
|
+
- type: content
|
|
47
|
+
text: "| Frontend ✅ | Live md blocks ✅ "
|
|
48
|
+
- type: delay_seconds
|
|
49
|
+
value: 0.30
|
|
50
|
+
- type: content
|
|
51
|
+
text: "committed and styled ✅ ✅ ✅ |\n"
|
|
52
|
+
- type: delay_seconds
|
|
53
|
+
value: 0.30
|
|
54
|
+
- type: content
|
|
55
|
+
text: "| TUI Mode 🔄 "
|
|
56
|
+
- type: delay_seconds
|
|
57
|
+
value: 0.30
|
|
58
|
+
- type: content
|
|
59
|
+
text: "| In Progress 🔄 | Medium ⚠️ "
|
|
60
|
+
- type: delay_seconds
|
|
61
|
+
value: 0.30
|
|
62
|
+
- type: content
|
|
63
|
+
text: "| Platform 🔄 | Live region 🔄 "
|
|
64
|
+
- type: delay_seconds
|
|
65
|
+
value: 0.30
|
|
66
|
+
- type: content
|
|
67
|
+
text: "repaint work ongoing 🔄 🔄 🔄 |\n"
|
|
68
|
+
- type: delay_seconds
|
|
69
|
+
value: 0.30
|
|
70
|
+
- type: content
|
|
71
|
+
text: "| MCP Bridge 🔄 "
|
|
72
|
+
- type: delay_seconds
|
|
73
|
+
value: 0.30
|
|
74
|
+
- type: content
|
|
75
|
+
text: "| In Progress 🔄 | Medium ⚠️ "
|
|
76
|
+
- type: delay_seconds
|
|
77
|
+
value: 0.30
|
|
78
|
+
- type: content
|
|
79
|
+
text: "| Integrations 🔄 | Tool schema 🔄 "
|
|
80
|
+
- type: delay_seconds
|
|
81
|
+
value: 0.30
|
|
82
|
+
- type: content
|
|
83
|
+
text: "negotiation pending 🔄 🔄 🔄 |\n"
|
|
84
|
+
- type: delay_seconds
|
|
85
|
+
value: 0.30
|
|
86
|
+
- type: content
|
|
87
|
+
text: "| Web UI ✅ "
|
|
88
|
+
- type: delay_seconds
|
|
89
|
+
value: 0.30
|
|
90
|
+
- type: content
|
|
91
|
+
text: "| Done ✅ | Low ⚠️ "
|
|
92
|
+
- type: delay_seconds
|
|
93
|
+
value: 0.30
|
|
94
|
+
- type: content
|
|
95
|
+
text: "| Frontend ✅ | Rails DOM ✅ "
|
|
96
|
+
- type: delay_seconds
|
|
97
|
+
value: 0.30
|
|
98
|
+
- type: content
|
|
99
|
+
text: "token streaming live ✅ ✅ ✅ |\n"
|
|
100
|
+
- type: delay_seconds
|
|
101
|
+
value: 0.30
|
|
102
|
+
- type: content
|
|
103
|
+
text: "| Memory 🔄 "
|
|
104
|
+
- type: delay_seconds
|
|
105
|
+
value: 0.30
|
|
106
|
+
- type: content
|
|
107
|
+
text: "| In Progress 🔄 | High ⚠️ "
|
|
108
|
+
- type: delay_seconds
|
|
109
|
+
value: 0.30
|
|
110
|
+
- type: content
|
|
111
|
+
text: "| Core 🔄 | SQLite tiny-Zep 🔄 "
|
|
112
|
+
- type: delay_seconds
|
|
113
|
+
value: 0.30
|
|
114
|
+
- type: content
|
|
115
|
+
text: "recall backend 🔄 🔄 🔄 |\n"
|
|
116
|
+
- type: delay_seconds
|
|
117
|
+
value: 0.30
|
|
118
|
+
- type: content
|
|
119
|
+
text: "\nAll tracked features above."
|
|
120
|
+
- type: delay_seconds
|
|
121
|
+
value: 0.30
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# With-approvals scenario: tokenized reasoning, then a shell tool_call that
|
|
2
|
+
# the real ApprovalPolicy will gate on. Once approval lands and the tool
|
|
3
|
+
# executes, the next agent turn re-enters the provider — but for THIS turn
|
|
4
|
+
# the provider just emits thinking + the tool_call.
|
|
5
|
+
#
|
|
6
|
+
# Translation from the reference with-approvals scenario:
|
|
7
|
+
# - reasoning.available {text} → thinking chunks (token-by-token)
|
|
8
|
+
# - __pause_for_approval → DROPPED; the shell tool_call below
|
|
9
|
+
# triggers approval via ApprovalPolicy
|
|
10
|
+
# - tool.started/completed → collapsed into one tool_call
|
|
11
|
+
# - message.delta {delta} → content chunks (will be emitted on the
|
|
12
|
+
# follow-up turn after tool result; here
|
|
13
|
+
# we keep them so the fake turn is rich)
|
|
14
|
+
# - run.completed → DROPPED; fake provider terminates when
|
|
15
|
+
# events are exhausted
|
|
16
|
+
events:
|
|
17
|
+
- type: thinking
|
|
18
|
+
text: "I"
|
|
19
|
+
- type: delay_seconds
|
|
20
|
+
value: 0.02
|
|
21
|
+
- type: thinking
|
|
22
|
+
text: " need"
|
|
23
|
+
- type: delay_seconds
|
|
24
|
+
value: 0.02
|
|
25
|
+
- type: thinking
|
|
26
|
+
text: " your"
|
|
27
|
+
- type: delay_seconds
|
|
28
|
+
value: 0.02
|
|
29
|
+
- type: thinking
|
|
30
|
+
text: " approval"
|
|
31
|
+
- type: delay_seconds
|
|
32
|
+
value: 0.04
|
|
33
|
+
- type: thinking
|
|
34
|
+
text: " for"
|
|
35
|
+
- type: delay_seconds
|
|
36
|
+
value: 0.02
|
|
37
|
+
- type: thinking
|
|
38
|
+
text: " this"
|
|
39
|
+
- type: delay_seconds
|
|
40
|
+
value: 0.02
|
|
41
|
+
- type: thinking
|
|
42
|
+
text: " command."
|
|
43
|
+
- type: delay_seconds
|
|
44
|
+
value: 0.04
|
|
45
|
+
|
|
46
|
+
- type: tool_call
|
|
47
|
+
id: "call_fake_approve_1"
|
|
48
|
+
name: "shell"
|
|
49
|
+
arguments:
|
|
50
|
+
command: "rm -rf /tmp/cache"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# With-artifacts scenario: model writes a markdown report and hands it to
|
|
2
|
+
# the UI as a downloadable artifact. Mirrors the reference UX —
|
|
3
|
+
# token-by-token reasoning, a short content explanation, then two
|
|
4
|
+
# tool calls in one turn:
|
|
5
|
+
# 1. `write` drops the file on disk (workspace-scoped — bin/dev points
|
|
6
|
+
# terminal.cwd at $RUBINO_HOME/workspace so this lands under
|
|
7
|
+
# the strict-workspace check)
|
|
8
|
+
# 2. `attach_file` registers it as an artifact → ARTIFACT_CREATED on
|
|
9
|
+
# the bus → SSE artifact.created → the web UI downloads via
|
|
10
|
+
# GET /v1/files and renders a download link
|
|
11
|
+
#
|
|
12
|
+
# After the tools execute, FakeProvider's post_tool_turn? guard kicks in
|
|
13
|
+
# and emits a closing "Done." so the run terminates without re-replaying.
|
|
14
|
+
events:
|
|
15
|
+
- type: thinking
|
|
16
|
+
text: "Let"
|
|
17
|
+
- type: delay_seconds
|
|
18
|
+
value: 0.02
|
|
19
|
+
- type: thinking
|
|
20
|
+
text: " me"
|
|
21
|
+
- type: delay_seconds
|
|
22
|
+
value: 0.02
|
|
23
|
+
- type: thinking
|
|
24
|
+
text: " put"
|
|
25
|
+
- type: delay_seconds
|
|
26
|
+
value: 0.02
|
|
27
|
+
- type: thinking
|
|
28
|
+
text: " together"
|
|
29
|
+
- type: delay_seconds
|
|
30
|
+
value: 0.02
|
|
31
|
+
- type: thinking
|
|
32
|
+
text: " a"
|
|
33
|
+
- type: delay_seconds
|
|
34
|
+
value: 0.02
|
|
35
|
+
- type: thinking
|
|
36
|
+
text: " short"
|
|
37
|
+
- type: delay_seconds
|
|
38
|
+
value: 0.02
|
|
39
|
+
- type: thinking
|
|
40
|
+
text: " markdown"
|
|
41
|
+
- type: delay_seconds
|
|
42
|
+
value: 0.02
|
|
43
|
+
- type: thinking
|
|
44
|
+
text: " report"
|
|
45
|
+
- type: delay_seconds
|
|
46
|
+
value: 0.02
|
|
47
|
+
- type: thinking
|
|
48
|
+
text: " summarising"
|
|
49
|
+
- type: delay_seconds
|
|
50
|
+
value: 0.02
|
|
51
|
+
- type: thinking
|
|
52
|
+
text: " the"
|
|
53
|
+
- type: delay_seconds
|
|
54
|
+
value: 0.02
|
|
55
|
+
- type: thinking
|
|
56
|
+
text: " findings."
|
|
57
|
+
- type: delay_seconds
|
|
58
|
+
value: 0.04
|
|
59
|
+
|
|
60
|
+
- type: content
|
|
61
|
+
text: "I've put together a short markdown report. "
|
|
62
|
+
- type: delay_seconds
|
|
63
|
+
value: 0.04
|
|
64
|
+
- type: content
|
|
65
|
+
text: "Writing it to `report.md` now and attaching it so you can download it directly from this turn.\n"
|
|
66
|
+
- type: delay_seconds
|
|
67
|
+
value: 0.05
|
|
68
|
+
|
|
69
|
+
- type: tool_call
|
|
70
|
+
id: "call_fake_artifact_write"
|
|
71
|
+
name: "write"
|
|
72
|
+
arguments:
|
|
73
|
+
file_path: "report.md"
|
|
74
|
+
content: |
|
|
75
|
+
# Report
|
|
76
|
+
|
|
77
|
+
Generated by the fake LLM provider for the `with-artifacts` scenario.
|
|
78
|
+
|
|
79
|
+
## Highlights
|
|
80
|
+
|
|
81
|
+
- The agent created this markdown file with the `write` tool.
|
|
82
|
+
- It was then attached to the run via `attach_file` so the web UI
|
|
83
|
+
could surface a download link in the timeline.
|
|
84
|
+
- This is exactly the round-trip the legacy fake provider
|
|
85
|
+
exercised: reasoning → content → tool call → artifact.
|
|
86
|
+
|
|
87
|
+
## Next steps
|
|
88
|
+
|
|
89
|
+
Open the attached file from the session timeline to see the rendered
|
|
90
|
+
output. Real runs would replace this with whatever the user asked
|
|
91
|
+
for (CSV exports, configuration dumps, screenshots, etc.).
|
|
92
|
+
|
|
93
|
+
- type: tool_call
|
|
94
|
+
id: "call_fake_artifact_attach"
|
|
95
|
+
name: "attach_file"
|
|
96
|
+
arguments:
|
|
97
|
+
file_path: "report.md"
|
|
98
|
+
filename: "report.md"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# With-clarify scenario: the LLM emits a question tool_call so the real
|
|
2
|
+
# question pipeline gathers user input; afterwards it streams a short
|
|
3
|
+
# confirmation. __pause_for_clarify is dropped — the question tool_call
|
|
4
|
+
# triggers the clarify flow naturally.
|
|
5
|
+
events:
|
|
6
|
+
- type: thinking
|
|
7
|
+
text: "Let"
|
|
8
|
+
- type: thinking
|
|
9
|
+
text: " me"
|
|
10
|
+
- type: thinking
|
|
11
|
+
text: " ask"
|
|
12
|
+
- type: thinking
|
|
13
|
+
text: " you."
|
|
14
|
+
- type: delay_seconds
|
|
15
|
+
value: 0.04
|
|
16
|
+
- type: tool_call
|
|
17
|
+
id: "call_fake_clarify_1"
|
|
18
|
+
name: "question"
|
|
19
|
+
arguments:
|
|
20
|
+
question: "Which approach do you prefer for the API layer?"
|
|
21
|
+
options:
|
|
22
|
+
- label: "Option A: REST API"
|
|
23
|
+
- label: "Option B: GraphQL"
|
|
24
|
+
- label: "Option C: gRPC"
|
|
25
|
+
- type: thinking
|
|
26
|
+
text: "Great"
|
|
27
|
+
- type: thinking
|
|
28
|
+
text: " choice."
|
|
29
|
+
- type: delay_seconds
|
|
30
|
+
value: 0.04
|
|
31
|
+
- type: content
|
|
32
|
+
text: "Proceeding with your selection."
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# With-reasoning scenario: multi-step reasoning with tool calls and
|
|
2
|
+
# streaming output. Best for exercising the timeline rendering and
|
|
3
|
+
# live-streaming UI — reasoning is emitted token-by-token to mimic a
|
|
4
|
+
# real LLM, and real tools (read, shell) are invoked so the downstream
|
|
5
|
+
# pipeline (approvals, output capture) runs end-to-end.
|
|
6
|
+
events:
|
|
7
|
+
- type: thinking
|
|
8
|
+
text: "Let"
|
|
9
|
+
- type: delay_seconds
|
|
10
|
+
value: 0.02
|
|
11
|
+
- type: thinking
|
|
12
|
+
text: " me"
|
|
13
|
+
- type: delay_seconds
|
|
14
|
+
value: 0.02
|
|
15
|
+
- type: thinking
|
|
16
|
+
text: " analyze"
|
|
17
|
+
- type: delay_seconds
|
|
18
|
+
value: 0.02
|
|
19
|
+
- type: thinking
|
|
20
|
+
text: " the"
|
|
21
|
+
- type: delay_seconds
|
|
22
|
+
value: 0.02
|
|
23
|
+
- type: thinking
|
|
24
|
+
text: " request"
|
|
25
|
+
- type: delay_seconds
|
|
26
|
+
value: 0.02
|
|
27
|
+
- type: thinking
|
|
28
|
+
text: " step"
|
|
29
|
+
- type: delay_seconds
|
|
30
|
+
value: 0.02
|
|
31
|
+
- type: thinking
|
|
32
|
+
text: " by"
|
|
33
|
+
- type: delay_seconds
|
|
34
|
+
value: 0.02
|
|
35
|
+
- type: thinking
|
|
36
|
+
text: " step."
|
|
37
|
+
- type: delay_seconds
|
|
38
|
+
value: 0.02
|
|
39
|
+
- type: thinking
|
|
40
|
+
text: "\n"
|
|
41
|
+
- type: delay_seconds
|
|
42
|
+
value: 0.02
|
|
43
|
+
- type: thinking
|
|
44
|
+
text: "First"
|
|
45
|
+
- type: delay_seconds
|
|
46
|
+
value: 0.04
|
|
47
|
+
- type: thinking
|
|
48
|
+
text: " I"
|
|
49
|
+
- type: delay_seconds
|
|
50
|
+
value: 0.02
|
|
51
|
+
- type: thinking
|
|
52
|
+
text: " need"
|
|
53
|
+
- type: delay_seconds
|
|
54
|
+
value: 0.02
|
|
55
|
+
- type: thinking
|
|
56
|
+
text: " to"
|
|
57
|
+
- type: delay_seconds
|
|
58
|
+
value: 0.02
|
|
59
|
+
- type: thinking
|
|
60
|
+
text: " check"
|
|
61
|
+
- type: delay_seconds
|
|
62
|
+
value: 0.02
|
|
63
|
+
- type: thinking
|
|
64
|
+
text: " the"
|
|
65
|
+
- type: delay_seconds
|
|
66
|
+
value: 0.02
|
|
67
|
+
- type: thinking
|
|
68
|
+
text: " current"
|
|
69
|
+
- type: delay_seconds
|
|
70
|
+
value: 0.02
|
|
71
|
+
- type: thinking
|
|
72
|
+
text: " state."
|
|
73
|
+
- type: delay_seconds
|
|
74
|
+
value: 0.04
|
|
75
|
+
|
|
76
|
+
- type: tool_call
|
|
77
|
+
id: "call_fake_reasoning_1"
|
|
78
|
+
name: "read"
|
|
79
|
+
arguments:
|
|
80
|
+
file_path: "/tmp/example.txt"
|
|
81
|
+
|
|
82
|
+
- type: delay_seconds
|
|
83
|
+
value: 0.5
|
|
84
|
+
|
|
85
|
+
- type: thinking
|
|
86
|
+
text: "Found"
|
|
87
|
+
- type: delay_seconds
|
|
88
|
+
value: 0.04
|
|
89
|
+
- type: thinking
|
|
90
|
+
text: " relevant"
|
|
91
|
+
- type: delay_seconds
|
|
92
|
+
value: 0.04
|
|
93
|
+
- type: thinking
|
|
94
|
+
text: " data."
|
|
95
|
+
- type: delay_seconds
|
|
96
|
+
value: 0.02
|
|
97
|
+
- type: thinking
|
|
98
|
+
text: "\n"
|
|
99
|
+
- type: delay_seconds
|
|
100
|
+
value: 0.02
|
|
101
|
+
- type: thinking
|
|
102
|
+
text: "Now"
|
|
103
|
+
- type: delay_seconds
|
|
104
|
+
value: 0.02
|
|
105
|
+
- type: thinking
|
|
106
|
+
text: " running"
|
|
107
|
+
- type: delay_seconds
|
|
108
|
+
value: 0.04
|
|
109
|
+
- type: thinking
|
|
110
|
+
text: " tests"
|
|
111
|
+
- type: delay_seconds
|
|
112
|
+
value: 0.02
|
|
113
|
+
- type: thinking
|
|
114
|
+
text: " to"
|
|
115
|
+
- type: delay_seconds
|
|
116
|
+
value: 0.02
|
|
117
|
+
- type: thinking
|
|
118
|
+
text: " validate."
|
|
119
|
+
- type: delay_seconds
|
|
120
|
+
value: 0.04
|
|
121
|
+
|
|
122
|
+
- type: tool_call
|
|
123
|
+
id: "call_fake_reasoning_2"
|
|
124
|
+
name: "shell"
|
|
125
|
+
arguments:
|
|
126
|
+
command: "npm test"
|
|
127
|
+
|
|
128
|
+
- type: delay_seconds
|
|
129
|
+
value: 0.8
|
|
130
|
+
|
|
131
|
+
- type: thinking
|
|
132
|
+
text: "Tests"
|
|
133
|
+
- type: delay_seconds
|
|
134
|
+
value: 0.04
|
|
135
|
+
- type: thinking
|
|
136
|
+
text: " pass."
|
|
137
|
+
- type: delay_seconds
|
|
138
|
+
value: 0.02
|
|
139
|
+
- type: thinking
|
|
140
|
+
text: "\n"
|
|
141
|
+
- type: delay_seconds
|
|
142
|
+
value: 0.02
|
|
143
|
+
- type: thinking
|
|
144
|
+
text: "Formulating"
|
|
145
|
+
- type: delay_seconds
|
|
146
|
+
value: 0.04
|
|
147
|
+
- type: thinking
|
|
148
|
+
text: " final"
|
|
149
|
+
- type: delay_seconds
|
|
150
|
+
value: 0.02
|
|
151
|
+
- type: thinking
|
|
152
|
+
text: " response."
|
|
153
|
+
- type: delay_seconds
|
|
154
|
+
value: 0.04
|
|
155
|
+
|
|
156
|
+
- type: content
|
|
157
|
+
text: "Here are the results of my analysis:\n\n"
|
|
158
|
+
- type: delay_seconds
|
|
159
|
+
value: 0.05
|
|
160
|
+
- type: content
|
|
161
|
+
text: "- File content analyzed\n"
|
|
162
|
+
- type: delay_seconds
|
|
163
|
+
value: 0.05
|
|
164
|
+
- type: content
|
|
165
|
+
text: "- Tests passed\n"
|
|
166
|
+
- type: delay_seconds
|
|
167
|
+
value: 0.05
|
|
168
|
+
- type: content
|
|
169
|
+
text: "- All checks complete\n\n"
|
|
170
|
+
- type: delay_seconds
|
|
171
|
+
value: 0.05
|
|
172
|
+
- type: content
|
|
173
|
+
text: "The system is running normally. No issues detected."
|
|
174
|
+
- type: delay_seconds
|
|
175
|
+
value: 0.05
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Scenario with file upload. Mirrors the reference with-uploads but uses the
|
|
2
|
+
# FakeProvider event vocabulary. The agent "reads" an uploaded file and
|
|
3
|
+
# emits a fake analysis as the final assistant message. {{input}} is the
|
|
4
|
+
# user's text plus the uploaded file names/content types.
|
|
5
|
+
events:
|
|
6
|
+
- type: thinking
|
|
7
|
+
text: "I"
|
|
8
|
+
- type: delay_seconds
|
|
9
|
+
value: 0.02
|
|
10
|
+
- type: thinking
|
|
11
|
+
text: " received"
|
|
12
|
+
- type: delay_seconds
|
|
13
|
+
value: 0.04
|
|
14
|
+
- type: thinking
|
|
15
|
+
text: " the"
|
|
16
|
+
- type: delay_seconds
|
|
17
|
+
value: 0.02
|
|
18
|
+
- type: thinking
|
|
19
|
+
text: " uploaded"
|
|
20
|
+
- type: delay_seconds
|
|
21
|
+
value: 0.04
|
|
22
|
+
- type: thinking
|
|
23
|
+
text: " file."
|
|
24
|
+
- type: delay_seconds
|
|
25
|
+
value: 0.02
|
|
26
|
+
- type: thinking
|
|
27
|
+
text: "\n"
|
|
28
|
+
- type: delay_seconds
|
|
29
|
+
value: 0.02
|
|
30
|
+
- type: thinking
|
|
31
|
+
text: "Analyzing"
|
|
32
|
+
- type: delay_seconds
|
|
33
|
+
value: 0.04
|
|
34
|
+
- type: thinking
|
|
35
|
+
text: " its"
|
|
36
|
+
- type: delay_seconds
|
|
37
|
+
value: 0.02
|
|
38
|
+
- type: thinking
|
|
39
|
+
text: " contents..."
|
|
40
|
+
- type: delay_seconds
|
|
41
|
+
value: 0.04
|
|
42
|
+
|
|
43
|
+
- type: delay_seconds
|
|
44
|
+
value: 0.4
|
|
45
|
+
|
|
46
|
+
- type: thinking
|
|
47
|
+
text: "The"
|
|
48
|
+
- type: delay_seconds
|
|
49
|
+
value: 0.02
|
|
50
|
+
- type: thinking
|
|
51
|
+
text: " file"
|
|
52
|
+
- type: delay_seconds
|
|
53
|
+
value: 0.02
|
|
54
|
+
- type: thinking
|
|
55
|
+
text: " appears"
|
|
56
|
+
- type: delay_seconds
|
|
57
|
+
value: 0.04
|
|
58
|
+
- type: thinking
|
|
59
|
+
text: " to"
|
|
60
|
+
- type: delay_seconds
|
|
61
|
+
value: 0.02
|
|
62
|
+
- type: thinking
|
|
63
|
+
text: " contain"
|
|
64
|
+
- type: delay_seconds
|
|
65
|
+
value: 0.04
|
|
66
|
+
- type: thinking
|
|
67
|
+
text: " valid"
|
|
68
|
+
- type: delay_seconds
|
|
69
|
+
value: 0.04
|
|
70
|
+
- type: thinking
|
|
71
|
+
text: " data."
|
|
72
|
+
- type: delay_seconds
|
|
73
|
+
value: 0.02
|
|
74
|
+
- type: thinking
|
|
75
|
+
text: "\n"
|
|
76
|
+
- type: delay_seconds
|
|
77
|
+
value: 0.02
|
|
78
|
+
- type: thinking
|
|
79
|
+
text: "Processing..."
|
|
80
|
+
- type: delay_seconds
|
|
81
|
+
value: 0.06
|
|
82
|
+
|
|
83
|
+
- type: delay_seconds
|
|
84
|
+
value: 0.3
|
|
85
|
+
|
|
86
|
+
- type: content
|
|
87
|
+
text: "File analysis complete:\n\n"
|
|
88
|
+
- type: delay_seconds
|
|
89
|
+
value: 0.05
|
|
90
|
+
# `{{input}}` is the web UI's already-prefixed text (e.g. "uploaded file:
|
|
91
|
+
# report.pdf" when the user attached a file with no message). Echo it
|
|
92
|
+
# straight so the agent reply always names the file the user sent.
|
|
93
|
+
- type: content
|
|
94
|
+
text: "Received {{input}}.\n"
|
|
95
|
+
- type: delay_seconds
|
|
96
|
+
value: 0.05
|
|
97
|
+
- type: content
|
|
98
|
+
text: "- Content validated\n"
|
|
99
|
+
- type: delay_seconds
|
|
100
|
+
value: 0.05
|
|
101
|
+
- type: content
|
|
102
|
+
text: "- No issues detected\n"
|
|
103
|
+
- type: delay_seconds
|
|
104
|
+
value: 0.05
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "provider_resolver"
|
|
4
|
+
|
|
5
|
+
module Rubino
|
|
6
|
+
module LLM
|
|
7
|
+
# Session-scoped memory of providers that rejected an Anthropic-style
|
|
8
|
+
# thinking budget, plus the detector for that rejection (#75), plus the
|
|
9
|
+
# static per-provider capability gate (#2).
|
|
10
|
+
#
|
|
11
|
+
# Process-level (not per-adapter) because Lifecycle rebuilds the adapter
|
|
12
|
+
# every turn — and one CLI process serves one chat session, so this is
|
|
13
|
+
# exactly "remember for the session". RubyLLMAdapter consults it before
|
|
14
|
+
# rendering a budget and marks it on a recognised rejection, so the
|
|
15
|
+
# provider is never sent a budget again this session.
|
|
16
|
+
module ThinkingSupport
|
|
17
|
+
@unsupported = {}
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
def unsupported?(provider)
|
|
22
|
+
@unsupported.key?(provider.to_s)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Per-provider thinking CAPABILITY gate (#2). #unsupported?/#rejection?
|
|
26
|
+
# (#75) handle a provider that REJECTS a budget (hard 400 → retry +
|
|
27
|
+
# session memo); this handles one that ACCEPTS it and then, lacking a
|
|
28
|
+
# separate reasoning channel, dumps its chain-of-thought as plain content
|
|
29
|
+
# deltas — observed live on MiniMax. providers.<name>.supports_thinking
|
|
30
|
+
# (true/false) is the explicit override; unset, MiniMax-family model ids
|
|
31
|
+
# default to false (they return no thinking blocks and leak reasoning
|
|
32
|
+
# when sent a budget), everything else to true.
|
|
33
|
+
def supports?(provider_cfg, model_id)
|
|
34
|
+
configured = provider_cfg["supports_thinking"]
|
|
35
|
+
return configured unless configured.nil?
|
|
36
|
+
|
|
37
|
+
!model_id.to_s.match?(ProviderResolver::PROVIDER_PATTERNS["minimax"])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# providers.<name>.supports_thinking: true is the user's explicit promise
|
|
41
|
+
# that the backend accepts an Anthropic-style thinking block. ruby_llm
|
|
42
|
+
# 1.16 only renders with_thinking when the model's REGISTRY entry
|
|
43
|
+
# declares a budget_tokens reasoning option; an assume-model-exists model
|
|
44
|
+
# (MiniMax-M3 on the anthropic-compatible path) declares none, so
|
|
45
|
+
# with_thinking raised client-side before any request, the #75 rejection
|
|
46
|
+
# detector matched the message, and the documented opt-in silently died
|
|
47
|
+
# every turn (#175). On that path the adapter puts the wire payload on
|
|
48
|
+
# with_params instead, which ruby_llm deep-merges into the request body
|
|
49
|
+
# unconditionally.
|
|
50
|
+
def budget_via_params?(provider_cfg, chat)
|
|
51
|
+
return false unless provider_cfg["supports_thinking"] == true
|
|
52
|
+
|
|
53
|
+
model = chat.respond_to?(:model) ? chat.model : nil
|
|
54
|
+
!(model.respond_to?(:reasoning_option) && model.reasoning_option("budget_tokens"))
|
|
55
|
+
rescue StandardError
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Records the rejection and tells the user once with a dim note (only
|
|
60
|
+
# the marking path emits it). Cosmetic: a UI failure must never break
|
|
61
|
+
# the retried turn.
|
|
62
|
+
def mark_unsupported!(provider, notify: nil)
|
|
63
|
+
@unsupported[provider.to_s] = true
|
|
64
|
+
notify&.note("provider doesn't support thinking — effort off")
|
|
65
|
+
rescue StandardError
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Test seam: forget all recorded rejections (a fresh "session").
|
|
70
|
+
def reset!
|
|
71
|
+
@unsupported = {}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# True when +error+ reads as a provider's "thinking (budget) is not
|
|
75
|
+
# supported" rejection. Kept narrow: the message must name thinking plus
|
|
76
|
+
# a not-supported phrasing.
|
|
77
|
+
def rejection?(error)
|
|
78
|
+
msg = error.message.to_s.downcase
|
|
79
|
+
msg.include?("thinking") &&
|
|
80
|
+
(msg.include?("not support") || msg.include?("unsupported"))
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|