rubik 0.1.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/ATTRIBUTION.md +139 -0
- data/CHANGELOG.md +236 -0
- data/LICENSE +21 -0
- data/README.md +269 -0
- data/bin/rubik +82 -0
- data/lib/rubik/adapter/base.rb +195 -0
- data/lib/rubik/adapter/c.rb +36 -0
- data/lib/rubik/adapter/crystal.rb +78 -0
- data/lib/rubik/adapter/generic.rb +86 -0
- data/lib/rubik/adapter/go.rb +36 -0
- data/lib/rubik/adapter/node.rb +146 -0
- data/lib/rubik/adapter/process.rb +78 -0
- data/lib/rubik/adapter/python.rb +30 -0
- data/lib/rubik/adapter/registry.rb +81 -0
- data/lib/rubik/adapter/ruby.rb +30 -0
- data/lib/rubik/adapter/rust.rb +36 -0
- data/lib/rubik/adapter/zig.rb +36 -0
- data/lib/rubik/adapter.rb +18 -0
- data/lib/rubik/ast/body_loader.rb +19 -0
- data/lib/rubik/ast/c_extractor.rb +114 -0
- data/lib/rubik/ast/cache_store.rb +191 -0
- data/lib/rubik/ast/extractor.rb +59 -0
- data/lib/rubik/ast/fallback_cleaner.rb +145 -0
- data/lib/rubik/ast/go_extractor.rb +148 -0
- data/lib/rubik/ast/graph.rb +272 -0
- data/lib/rubik/ast/python_extractor.rb +120 -0
- data/lib/rubik/ast/ruby_extractor.rb +260 -0
- data/lib/rubik/ast/rust_extractor.rb +127 -0
- data/lib/rubik/ast/symbol.rb +103 -0
- data/lib/rubik/ast/treesitter/base_walker.rb +109 -0
- data/lib/rubik/ast/treesitter/c_walker.rb +122 -0
- data/lib/rubik/ast/treesitter/go_walker.rb +120 -0
- data/lib/rubik/ast/treesitter/python_walker.rb +107 -0
- data/lib/rubik/ast/treesitter/rust_walker.rb +130 -0
- data/lib/rubik/ast/treesitter/typescript_walker.rb +182 -0
- data/lib/rubik/ast/treesitter/zig_walker.rb +109 -0
- data/lib/rubik/ast/treesitter_extractor.rb +93 -0
- data/lib/rubik/ast/typescript_extractor.rb +283 -0
- data/lib/rubik/ast/zig_extractor.rb +116 -0
- data/lib/rubik/ast.rb +13 -0
- data/lib/rubik/benchmark/runner.rb +224 -0
- data/lib/rubik/benchmark/synth_repo.rb +59 -0
- data/lib/rubik/benchmark.rb +9 -0
- data/lib/rubik/buffer/diff.rb +43 -0
- data/lib/rubik/buffer/manager.rb +427 -0
- data/lib/rubik/buffer/path_validator.rb +60 -0
- data/lib/rubik/buffer/snapshot.rb +99 -0
- data/lib/rubik/buffer.rb +10 -0
- data/lib/rubik/coeffects.rb +129 -0
- data/lib/rubik/concurrency/boundary.rb +74 -0
- data/lib/rubik/concurrency/dns_pool.rb +68 -0
- data/lib/rubik/concurrency/errors.rb +17 -0
- data/lib/rubik/concurrency/event_loop.rb +441 -0
- data/lib/rubik/concurrency/join.rb +55 -0
- data/lib/rubik/concurrency/process_waiter.rb +55 -0
- data/lib/rubik/concurrency/scheduler.rb +84 -0
- data/lib/rubik/concurrency/timers.rb +109 -0
- data/lib/rubik/concurrency.rb +132 -0
- data/lib/rubik/config/deep_ops.rb +80 -0
- data/lib/rubik/config/defaults.rb +248 -0
- data/lib/rubik/config/model_catalog.rb +93 -0
- data/lib/rubik/config/store.rb +155 -0
- data/lib/rubik/config.rb +216 -0
- data/lib/rubik/context/git_info.rb +84 -0
- data/lib/rubik/context/git_lister.rb +37 -0
- data/lib/rubik/context/index.rb +134 -0
- data/lib/rubik/context/repo_context_reader.rb +121 -0
- data/lib/rubik/context/scanner.rb +439 -0
- data/lib/rubik/context.rb +11 -0
- data/lib/rubik/diagnostics/collector.rb +200 -0
- data/lib/rubik/diagnostics/entry.rb +60 -0
- data/lib/rubik/diagnostics/parser.rb +107 -0
- data/lib/rubik/diagnostics.rb +10 -0
- data/lib/rubik/event_bus.rb +56 -0
- data/lib/rubik/kernel/dispatcher.rb +171 -0
- data/lib/rubik/kernel/dmesg.rb +116 -0
- data/lib/rubik/kernel/init.rb +177 -0
- data/lib/rubik/kernel/record.rb +85 -0
- data/lib/rubik/kernel/unit.rb +160 -0
- data/lib/rubik/llm/anthropic.rb +202 -0
- data/lib/rubik/llm/chunk_assembler.rb +285 -0
- data/lib/rubik/llm/client.rb +526 -0
- data/lib/rubik/llm/formatter.rb +450 -0
- data/lib/rubik/llm/message_sanitizer.rb +87 -0
- data/lib/rubik/llm/ollama.rb +167 -0
- data/lib/rubik/llm/openai.rb +21 -0
- data/lib/rubik/llm/openai_compatible.rb +216 -0
- data/lib/rubik/llm/provider.rb +125 -0
- data/lib/rubik/llm/registry.rb +336 -0
- data/lib/rubik/llm/router.rb +100 -0
- data/lib/rubik/llm.rb +18 -0
- data/lib/rubik/log.rb +38 -0
- data/lib/rubik/lsp/client.rb +475 -0
- data/lib/rubik/lsp/manager.rb +366 -0
- data/lib/rubik/lsp.rb +9 -0
- data/lib/rubik/mcp/boot_lifecycle.rb +114 -0
- data/lib/rubik/mcp/client.rb +169 -0
- data/lib/rubik/mcp/errors.rb +11 -0
- data/lib/rubik/mcp/manager.rb +619 -0
- data/lib/rubik/mcp/proxy_tool.rb +104 -0
- data/lib/rubik/mcp/transport/stdio.rb +255 -0
- data/lib/rubik/mcp.rb +12 -0
- data/lib/rubik/orchestrator/activity_log.rb +75 -0
- data/lib/rubik/orchestrator/auto_apply.rb +166 -0
- data/lib/rubik/orchestrator/budget_manager.rb +97 -0
- data/lib/rubik/orchestrator/continuation_manager.rb +161 -0
- data/lib/rubik/orchestrator/finish_message_selector.rb +55 -0
- data/lib/rubik/orchestrator/goal_ledger/drift.rb +71 -0
- data/lib/rubik/orchestrator/goal_ledger/ledger.rb +399 -0
- data/lib/rubik/orchestrator/goal_ledger/observation_projection.rb +179 -0
- data/lib/rubik/orchestrator/goal_ledger/patch.rb +268 -0
- data/lib/rubik/orchestrator/goal_ledger/schema.rb +123 -0
- data/lib/rubik/orchestrator/goal_ledger/validator.rb +361 -0
- data/lib/rubik/orchestrator/goal_ledger/verifier.rb +129 -0
- data/lib/rubik/orchestrator/goal_ledger/verify_spec.rb +50 -0
- data/lib/rubik/orchestrator/goal_ledger.rb +65 -0
- data/lib/rubik/orchestrator/goal_state.rb +184 -0
- data/lib/rubik/orchestrator/goal_wait.rb +125 -0
- data/lib/rubik/orchestrator/loop.rb +388 -0
- data/lib/rubik/orchestrator/lsp_feedback.rb +55 -0
- data/lib/rubik/orchestrator/maestro_gate.rb +65 -0
- data/lib/rubik/orchestrator/provider_recovery.rb +90 -0
- data/lib/rubik/orchestrator/provider_resolver.rb +69 -0
- data/lib/rubik/orchestrator/shell_deps.rb +85 -0
- data/lib/rubik/orchestrator/step_executor.rb +328 -0
- data/lib/rubik/orchestrator/stream_manager.rb +118 -0
- data/lib/rubik/orchestrator/subagent/activation.rb +65 -0
- data/lib/rubik/orchestrator/subagent/budget.rb +108 -0
- data/lib/rubik/orchestrator/subagent/completion.rb +61 -0
- data/lib/rubik/orchestrator/subagent/concurrency_gate.rb +37 -0
- data/lib/rubik/orchestrator/subagent/conversation_id.rb +67 -0
- data/lib/rubik/orchestrator/subagent/dependency.rb +398 -0
- data/lib/rubik/orchestrator/subagent/instance.rb +384 -0
- data/lib/rubik/orchestrator/subagent/lifecycle.rb +118 -0
- data/lib/rubik/orchestrator/subagent/messaging.rb +61 -0
- data/lib/rubik/orchestrator/subagent/narration.rb +125 -0
- data/lib/rubik/orchestrator/subagent/notices.rb +209 -0
- data/lib/rubik/orchestrator/subagent/questioner.rb +80 -0
- data/lib/rubik/orchestrator/subagent/reaper.rb +98 -0
- data/lib/rubik/orchestrator/subagent/resilience.rb +70 -0
- data/lib/rubik/orchestrator/subagent/reuse.rb +127 -0
- data/lib/rubik/orchestrator/subagent/scoped_registry.rb +79 -0
- data/lib/rubik/orchestrator/subagent/step_runner.rb +240 -0
- data/lib/rubik/orchestrator/subagent/storage_id.rb +26 -0
- data/lib/rubik/orchestrator/subagent/supervisor.rb +327 -0
- data/lib/rubik/orchestrator/subagent/transcript_formatter.rb +141 -0
- data/lib/rubik/orchestrator/subagent/type_registry.rb +199 -0
- data/lib/rubik/orchestrator/subagent_notice_inbox.rb +80 -0
- data/lib/rubik/orchestrator/tool_call_id.rb +47 -0
- data/lib/rubik/orchestrator/tool_circuit_breaker.rb +91 -0
- data/lib/rubik/orchestrator/tool_history.rb +39 -0
- data/lib/rubik/orchestrator/turn_executor/subagent_channel.rb +102 -0
- data/lib/rubik/orchestrator/turn_executor/user_channel.rb +172 -0
- data/lib/rubik/orchestrator/turn_executor.rb +376 -0
- data/lib/rubik/orchestrator.rb +11 -0
- data/lib/rubik/platform/filesystem.rb +64 -0
- data/lib/rubik/platform/io_drain.rb +53 -0
- data/lib/rubik/platform/path.rb +210 -0
- data/lib/rubik/platform/process.rb +38 -0
- data/lib/rubik/platform/sanitize.rb +157 -0
- data/lib/rubik/platform.rb +113 -0
- data/lib/rubik/plugin/dsl.rb +51 -0
- data/lib/rubik/plugin/manager.rb +247 -0
- data/lib/rubik/plugin/storage.rb +195 -0
- data/lib/rubik/plugin.rb +10 -0
- data/lib/rubik/prompt/context_builder.rb +377 -0
- data/lib/rubik/prompt/context_compactor.rb +326 -0
- data/lib/rubik/prompt/conversation_rows.rb +74 -0
- data/lib/rubik/prompt/doctrine.rb +160 -0
- data/lib/rubik/prompt/emergency_compactor.rb +81 -0
- data/lib/rubik/prompt/services.rb +60 -0
- data/lib/rubik/prompt/token_estimator.rb +63 -0
- data/lib/rubik/prompt/tool_teachings.rb +78 -0
- data/lib/rubik/prompt.rb +13 -0
- data/lib/rubik/repair/loop.rb +95 -0
- data/lib/rubik/repair.rb +8 -0
- data/lib/rubik/repl/ast_warmup.rb +129 -0
- data/lib/rubik/repl/command_context.rb +214 -0
- data/lib/rubik/repl/command_parser.rb +48 -0
- data/lib/rubik/repl/commands/agent.rb +30 -0
- data/lib/rubik/repl/commands/agent_context.rb +289 -0
- data/lib/rubik/repl/commands/agent_execution.rb +342 -0
- data/lib/rubik/repl/commands/agent_models.rb +212 -0
- data/lib/rubik/repl/commands/agent_providers.rb +232 -0
- data/lib/rubik/repl/commands/base.rb +55 -0
- data/lib/rubik/repl/commands/buffer.rb +124 -0
- data/lib/rubik/repl/commands/dmesg.rb +87 -0
- data/lib/rubik/repl/commands/lsp.rb +235 -0
- data/lib/rubik/repl/commands/mcp.rb +194 -0
- data/lib/rubik/repl/commands/plugins.rb +208 -0
- data/lib/rubik/repl/commands/pruner.rb +71 -0
- data/lib/rubik/repl/commands/session.rb +183 -0
- data/lib/rubik/repl/commands/skills.rb +111 -0
- data/lib/rubik/repl/commands/storage.rb +85 -0
- data/lib/rubik/repl/commands/subagents.rb +195 -0
- data/lib/rubik/repl/commands/system.rb +85 -0
- data/lib/rubik/repl/commands/tasks.rb +120 -0
- data/lib/rubik/repl/commands/workspace.rb +443 -0
- data/lib/rubik/repl/commands.rb +44 -0
- data/lib/rubik/repl/composition.rb +32 -0
- data/lib/rubik/repl/config_wiring.rb +142 -0
- data/lib/rubik/repl/confirmations.rb +56 -0
- data/lib/rubik/repl/hooks.rb +47 -0
- data/lib/rubik/repl/input_execution.rb +323 -0
- data/lib/rubik/repl/input_loop.rb +213 -0
- data/lib/rubik/repl/interrupt_handler.rb +217 -0
- data/lib/rubik/repl/lifecycle.rb +410 -0
- data/lib/rubik/repl/paste_support.rb +88 -0
- data/lib/rubik/repl/pending_notice_queue.rb +60 -0
- data/lib/rubik/repl/presentation.rb +132 -0
- data/lib/rubik/repl/pruner/brief.rb +101 -0
- data/lib/rubik/repl/pruner/gate.rb +279 -0
- data/lib/rubik/repl/pruner/harness.rb +236 -0
- data/lib/rubik/repl/pruner/rules.rb +316 -0
- data/lib/rubik/repl/pruner/service.rb +82 -0
- data/lib/rubik/repl/pruner.rb +39 -0
- data/lib/rubik/repl/service_catalog.rb +374 -0
- data/lib/rubik/repl/setup_factories.rb +66 -0
- data/lib/rubik/repl/shell.rb +231 -0
- data/lib/rubik/repl/step_formatter.rb +324 -0
- data/lib/rubik/repl/tee_io.rb +105 -0
- data/lib/rubik/repl/terminal_guard.rb +157 -0
- data/lib/rubik/repl/turn_recorder.rb +234 -0
- data/lib/rubik/repl.rb +14 -0
- data/lib/rubik/runtime/ast_service.rb +78 -0
- data/lib/rubik/runtime/context.rb +335 -0
- data/lib/rubik/runtime/diagnostic_engine.rb +188 -0
- data/lib/rubik/runtime/discovery.rb +342 -0
- data/lib/rubik/runtime/fiber.rb +142 -0
- data/lib/rubik/runtime/fiber_ordering.rb +52 -0
- data/lib/rubik/runtime/fiber_retry_gate.rb +84 -0
- data/lib/rubik/runtime/index_service.rb +58 -0
- data/lib/rubik/runtime/loop_service.rb +73 -0
- data/lib/rubik/runtime/manager.rb +241 -0
- data/lib/rubik/runtime/overlay_storage.rb +481 -0
- data/lib/rubik/runtime/service_definition.rb +54 -0
- data/lib/rubik/runtime/service_registration.rb +33 -0
- data/lib/rubik/runtime/task_manager.rb +302 -0
- data/lib/rubik/runtime.rb +19 -0
- data/lib/rubik/sandbox/driver/base.rb +30 -0
- data/lib/rubik/sandbox/driver/bubblewrap.rb +111 -0
- data/lib/rubik/sandbox/driver/docker.rb +113 -0
- data/lib/rubik/sandbox/driver/podman.rb +98 -0
- data/lib/rubik/sandbox/driver/process.rb +190 -0
- data/lib/rubik/sandbox/driver.rb +91 -0
- data/lib/rubik/sandbox/runner.rb +218 -0
- data/lib/rubik/sandbox/shell_resolver.rb +178 -0
- data/lib/rubik/sandbox.rb +8 -0
- data/lib/rubik/security/blocked_guidance.rb +187 -0
- data/lib/rubik/security/classifier/classifier.rb +440 -0
- data/lib/rubik/security/classifier/commands/agent-browser.yaml +9 -0
- data/lib/rubik/security/classifier/commands/apt.yaml +25 -0
- data/lib/rubik/security/classifier/commands/awk.yaml +10 -0
- data/lib/rubik/security/classifier/commands/basename.yaml +7 -0
- data/lib/rubik/security/classifier/commands/bash-classify.yaml +8 -0
- data/lib/rubik/security/classifier/commands/bash.yaml +20 -0
- data/lib/rubik/security/classifier/commands/black.yaml +12 -0
- data/lib/rubik/security/classifier/commands/brew.yaml +31 -0
- data/lib/rubik/security/classifier/commands/builtin.yaml +8 -0
- data/lib/rubik/security/classifier/commands/cargo.yaml +114 -0
- data/lib/rubik/security/classifier/commands/cat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/chgrp.yaml +10 -0
- data/lib/rubik/security/classifier/commands/chmod.yaml +10 -0
- data/lib/rubik/security/classifier/commands/chown.yaml +10 -0
- data/lib/rubik/security/classifier/commands/column.yaml +7 -0
- data/lib/rubik/security/classifier/commands/comm.yaml +7 -0
- data/lib/rubik/security/classifier/commands/command.yaml +12 -0
- data/lib/rubik/security/classifier/commands/cp.yaml +7 -0
- data/lib/rubik/security/classifier/commands/crystal.yaml +63 -0
- data/lib/rubik/security/classifier/commands/curl.yaml +30 -0
- data/lib/rubik/security/classifier/commands/cut.yaml +7 -0
- data/lib/rubik/security/classifier/commands/date.yaml +10 -0
- data/lib/rubik/security/classifier/commands/df.yaml +7 -0
- data/lib/rubik/security/classifier/commands/diff.yaml +7 -0
- data/lib/rubik/security/classifier/commands/dig.yaml +7 -0
- data/lib/rubik/security/classifier/commands/dirname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/docker.yaml +177 -0
- data/lib/rubik/security/classifier/commands/du.yaml +7 -0
- data/lib/rubik/security/classifier/commands/echo.yaml +7 -0
- data/lib/rubik/security/classifier/commands/env.yaml +15 -0
- data/lib/rubik/security/classifier/commands/eslint.yaml +12 -0
- data/lib/rubik/security/classifier/commands/eval.yaml +8 -0
- data/lib/rubik/security/classifier/commands/exec.yaml +12 -0
- data/lib/rubik/security/classifier/commands/expand.yaml +7 -0
- data/lib/rubik/security/classifier/commands/false.yaml +7 -0
- data/lib/rubik/security/classifier/commands/file.yaml +7 -0
- data/lib/rubik/security/classifier/commands/find.yaml +25 -0
- data/lib/rubik/security/classifier/commands/fmt.yaml +7 -0
- data/lib/rubik/security/classifier/commands/fold.yaml +7 -0
- data/lib/rubik/security/classifier/commands/free.yaml +7 -0
- data/lib/rubik/security/classifier/commands/gawk.yaml +10 -0
- data/lib/rubik/security/classifier/commands/gcloud.yaml +979 -0
- data/lib/rubik/security/classifier/commands/gh.yaml +615 -0
- data/lib/rubik/security/classifier/commands/git.yaml +524 -0
- data/lib/rubik/security/classifier/commands/glab-discussion.yaml +21 -0
- data/lib/rubik/security/classifier/commands/glab.yaml +807 -0
- data/lib/rubik/security/classifier/commands/go.yaml +44 -0
- data/lib/rubik/security/classifier/commands/gog.yaml +983 -0
- data/lib/rubik/security/classifier/commands/gradle.yaml +92 -0
- data/lib/rubik/security/classifier/commands/gradlew.yaml +92 -0
- data/lib/rubik/security/classifier/commands/grep.yaml +7 -0
- data/lib/rubik/security/classifier/commands/groups.yaml +7 -0
- data/lib/rubik/security/classifier/commands/gunzip.yaml +8 -0
- data/lib/rubik/security/classifier/commands/gws.yaml +211 -0
- data/lib/rubik/security/classifier/commands/gzip.yaml +7 -0
- data/lib/rubik/security/classifier/commands/hadolint.yaml +9 -0
- data/lib/rubik/security/classifier/commands/halt.yaml +8 -0
- data/lib/rubik/security/classifier/commands/head.yaml +7 -0
- data/lib/rubik/security/classifier/commands/helm.yaml +129 -0
- data/lib/rubik/security/classifier/commands/hostname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/id.yaml +7 -0
- data/lib/rubik/security/classifier/commands/install.yaml +7 -0
- data/lib/rubik/security/classifier/commands/isort.yaml +14 -0
- data/lib/rubik/security/classifier/commands/java.yaml +35 -0
- data/lib/rubik/security/classifier/commands/javac.yaml +39 -0
- data/lib/rubik/security/classifier/commands/join.yaml +7 -0
- data/lib/rubik/security/classifier/commands/journalctl.yaml +14 -0
- data/lib/rubik/security/classifier/commands/jq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/kill.yaml +7 -0
- data/lib/rubik/security/classifier/commands/killall.yaml +7 -0
- data/lib/rubik/security/classifier/commands/kubectl.yaml +211 -0
- data/lib/rubik/security/classifier/commands/less.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ln.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ls.yaml +7 -0
- data/lib/rubik/security/classifier/commands/make.yaml +21 -0
- data/lib/rubik/security/classifier/commands/md5sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mise.yaml +579 -0
- data/lib/rubik/security/classifier/commands/mkdir.yaml +8 -0
- data/lib/rubik/security/classifier/commands/more.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mv.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mvn.yaml +83 -0
- data/lib/rubik/security/classifier/commands/mvnw.yaml +83 -0
- data/lib/rubik/security/classifier/commands/mypy.yaml +9 -0
- data/lib/rubik/security/classifier/commands/netstat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/nice.yaml +11 -0
- data/lib/rubik/security/classifier/commands/nl.yaml +7 -0
- data/lib/rubik/security/classifier/commands/node.yaml +20 -0
- data/lib/rubik/security/classifier/commands/nohup.yaml +9 -0
- data/lib/rubik/security/classifier/commands/npm.yaml +71 -0
- data/lib/rubik/security/classifier/commands/npx.yaml +42 -0
- data/lib/rubik/security/classifier/commands/nslookup.yaml +7 -0
- data/lib/rubik/security/classifier/commands/paste.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ping.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pip.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pip3.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pipx.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pkill.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pnpm.yaml +346 -0
- data/lib/rubik/security/classifier/commands/poetry.yaml +97 -0
- data/lib/rubik/security/classifier/commands/poweroff.yaml +8 -0
- data/lib/rubik/security/classifier/commands/prettier.yaml +13 -0
- data/lib/rubik/security/classifier/commands/printenv.yaml +7 -0
- data/lib/rubik/security/classifier/commands/printf.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ps.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pup.yaml +882 -0
- data/lib/rubik/security/classifier/commands/pwd.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pytest.yaml +7 -0
- data/lib/rubik/security/classifier/commands/python.yaml +9 -0
- data/lib/rubik/security/classifier/commands/python3.yaml +9 -0
- data/lib/rubik/security/classifier/commands/read.yaml +7 -0
- data/lib/rubik/security/classifier/commands/readlink.yaml +7 -0
- data/lib/rubik/security/classifier/commands/realpath.yaml +7 -0
- data/lib/rubik/security/classifier/commands/reboot.yaml +8 -0
- data/lib/rubik/security/classifier/commands/rev.yaml +7 -0
- data/lib/rubik/security/classifier/commands/rg.yaml +7 -0
- data/lib/rubik/security/classifier/commands/rm.yaml +8 -0
- data/lib/rubik/security/classifier/commands/rsync.yaml +12 -0
- data/lib/rubik/security/classifier/commands/rtk.yaml +140 -0
- data/lib/rubik/security/classifier/commands/ruff.yaml +29 -0
- data/lib/rubik/security/classifier/commands/rustfmt.yaml +14 -0
- data/lib/rubik/security/classifier/commands/scp.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sed.yaml +14 -0
- data/lib/rubik/security/classifier/commands/seq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sh.yaml +14 -0
- data/lib/rubik/security/classifier/commands/sha1sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sha256sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/shellcheck.yaml +10 -0
- data/lib/rubik/security/classifier/commands/shutdown.yaml +8 -0
- data/lib/rubik/security/classifier/commands/slack.yaml +113 -0
- data/lib/rubik/security/classifier/commands/sleep.yaml +8 -0
- data/lib/rubik/security/classifier/commands/snip.yaml +79 -0
- data/lib/rubik/security/classifier/commands/sort.yaml +10 -0
- data/lib/rubik/security/classifier/commands/ss.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ssh.yaml +41 -0
- data/lib/rubik/security/classifier/commands/stat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/stdbuf.yaml +15 -0
- data/lib/rubik/security/classifier/commands/strace.yaml +20 -0
- data/lib/rubik/security/classifier/commands/strings.yaml +11 -0
- data/lib/rubik/security/classifier/commands/sudo.yaml +24 -0
- data/lib/rubik/security/classifier/commands/systemctl.yaml +58 -0
- data/lib/rubik/security/classifier/commands/systemd-run.yaml +30 -0
- data/lib/rubik/security/classifier/commands/tac.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tail.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tar.yaml +25 -0
- data/lib/rubik/security/classifier/commands/tee.yaml +8 -0
- data/lib/rubik/security/classifier/commands/terraform.yaml +70 -0
- data/lib/rubik/security/classifier/commands/test.yaml +7 -0
- data/lib/rubik/security/classifier/commands/time.yaml +8 -0
- data/lib/rubik/security/classifier/commands/timeout.yaml +18 -0
- data/lib/rubik/security/classifier/commands/top.yaml +7 -0
- data/lib/rubik/security/classifier/commands/touch.yaml +8 -0
- data/lib/rubik/security/classifier/commands/tr.yaml +7 -0
- data/lib/rubik/security/classifier/commands/traceroute.yaml +7 -0
- data/lib/rubik/security/classifier/commands/true.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tsc.yaml +16 -0
- data/lib/rubik/security/classifier/commands/type.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/unexpand.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uniq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/unzip.yaml +10 -0
- data/lib/rubik/security/classifier/commands/uptime.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uv.yaml +156 -0
- data/lib/rubik/security/classifier/commands/wait.yaml +11 -0
- data/lib/rubik/security/classifier/commands/wc.yaml +7 -0
- data/lib/rubik/security/classifier/commands/wget.yaml +7 -0
- data/lib/rubik/security/classifier/commands/whereis.yaml +7 -0
- data/lib/rubik/security/classifier/commands/which.yaml +7 -0
- data/lib/rubik/security/classifier/commands/whoami.yaml +7 -0
- data/lib/rubik/security/classifier/commands/xargs.yaml +26 -0
- data/lib/rubik/security/classifier/commands/xmllint.yaml +7 -0
- data/lib/rubik/security/classifier/commands/yarn.yaml +72 -0
- data/lib/rubik/security/classifier/commands/yes.yaml +7 -0
- data/lib/rubik/security/classifier/commands/yq.yaml +10 -0
- data/lib/rubik/security/classifier/commands/zip.yaml +8 -0
- data/lib/rubik/security/classifier/commands/zsh.yaml +14 -0
- data/lib/rubik/security/classifier/database.rb +216 -0
- data/lib/rubik/security/classifier/matcher/delegation.rb +190 -0
- data/lib/rubik/security/classifier/matcher/options.rb +245 -0
- data/lib/rubik/security/classifier/matcher/subcommands.rb +103 -0
- data/lib/rubik/security/classifier/matcher.rb +249 -0
- data/lib/rubik/security/classifier/models.rb +170 -0
- data/lib/rubik/security/classifier/parser.rb +23 -0
- data/lib/rubik/security/classifier/parsers/bash.rb +280 -0
- data/lib/rubik/security/classifier/parsers/bash_extractors.rb +221 -0
- data/lib/rubik/security/classifier/parsers/powershell.rb +185 -0
- data/lib/rubik/security/classifier/pwsh_rules.rb +224 -0
- data/lib/rubik/security/classifier.rb +25 -0
- data/lib/rubik/security/errors.rb +39 -0
- data/lib/rubik/security/guard.rb +954 -0
- data/lib/rubik/security/redactor.rb +89 -0
- data/lib/rubik/security.rb +11 -0
- data/lib/rubik/session/manager.rb +331 -0
- data/lib/rubik/session/recorder.rb +149 -0
- data/lib/rubik/session/replayer.rb +65 -0
- data/lib/rubik/session.rb +10 -0
- data/lib/rubik/skills/frontmatter.rb +41 -0
- data/lib/rubik/skills/manager.rb +223 -0
- data/lib/rubik/skills/registry.rb +64 -0
- data/lib/rubik/skills/skill.rb +33 -0
- data/lib/rubik/skills/storage.rb +77 -0
- data/lib/rubik/skills/validator.rb +116 -0
- data/lib/rubik/skills.rb +20 -0
- data/lib/rubik/storage/folding.rb +23 -0
- data/lib/rubik/storage/layout.rb +145 -0
- data/lib/rubik/storage/render.rb +97 -0
- data/lib/rubik/storage/service.rb +42 -0
- data/lib/rubik/storage/store.rb +514 -0
- data/lib/rubik/storage/transcript_indexer.rb +116 -0
- data/lib/rubik/storage/uri_reader.rb +168 -0
- data/lib/rubik/storage/vakanuvis/annal_writer.rb +92 -0
- data/lib/rubik/storage/vakanuvis/distill_gate.rb +323 -0
- data/lib/rubik/storage/vakanuvis/gate.rb +250 -0
- data/lib/rubik/storage/vakanuvis/harness.rb +421 -0
- data/lib/rubik/storage/vakanuvis/memory_steward.rb +163 -0
- data/lib/rubik/storage/vakanuvis/service.rb +69 -0
- data/lib/rubik/storage/vakanuvis/vocabulary.rb +99 -0
- data/lib/rubik/storage/vakanuvis/wiki_skeleton.rb +67 -0
- data/lib/rubik/storage/vakanuvis.rb +77 -0
- data/lib/rubik/storage.rb +29 -0
- data/lib/rubik/tools/base.rb +107 -0
- data/lib/rubik/tools/builtins/dmesg.rb +74 -0
- data/lib/rubik/tools/builtins/exec.rb +293 -0
- data/lib/rubik/tools/builtins/files.rb +354 -0
- data/lib/rubik/tools/builtins/goal_ledger.rb +89 -0
- data/lib/rubik/tools/builtins/grep.rb +281 -0
- data/lib/rubik/tools/builtins/interactive.rb +175 -0
- data/lib/rubik/tools/builtins/lifecycle.rb +359 -0
- data/lib/rubik/tools/builtins/lsp.rb +283 -0
- data/lib/rubik/tools/builtins/pruner.rb +88 -0
- data/lib/rubik/tools/builtins/runtime.rb +120 -0
- data/lib/rubik/tools/builtins/send_message.rb +109 -0
- data/lib/rubik/tools/builtins/show_file.rb +84 -0
- data/lib/rubik/tools/builtins/skill.rb +72 -0
- data/lib/rubik/tools/builtins/storage.rb +232 -0
- data/lib/rubik/tools/builtins/subagent.rb +356 -0
- data/lib/rubik/tools/builtins/subagent_lifecycle.rb +72 -0
- data/lib/rubik/tools/builtins/symbols.rb +209 -0
- data/lib/rubik/tools/builtins/synthesis.rb +314 -0
- data/lib/rubik/tools/builtins/task.rb +168 -0
- data/lib/rubik/tools/builtins/unsynthesize.rb +111 -0
- data/lib/rubik/tools/builtins/vakanuvis.rb +161 -0
- data/lib/rubik/tools/builtins.rb +30 -0
- data/lib/rubik/tools/catalog.rb +70 -0
- data/lib/rubik/tools/declaration.rb +90 -0
- data/lib/rubik/tools/diagnostic_reporter.rb +283 -0
- data/lib/rubik/tools/fuzzy_matcher.rb +172 -0
- data/lib/rubik/tools/native_search.rb +180 -0
- data/lib/rubik/tools/output.rb +54 -0
- data/lib/rubik/tools/preflight_runner.rb +209 -0
- data/lib/rubik/tools/registry.rb +247 -0
- data/lib/rubik/tools/result.rb +71 -0
- data/lib/rubik/tools/services.rb +96 -0
- data/lib/rubik/tools/truncator.rb +78 -0
- data/lib/rubik/tools/uri_resolver.rb +195 -0
- data/lib/rubik/tools.rb +14 -0
- data/lib/rubik/ui/approvals.rb +52 -0
- data/lib/rubik/ui/banner.rb +43 -0
- data/lib/rubik/ui/capability.rb +122 -0
- data/lib/rubik/ui/dialog.rb +106 -0
- data/lib/rubik/ui/diff_viewer.rb +78 -0
- data/lib/rubik/ui/frame.rb +95 -0
- data/lib/rubik/ui/markdown_stream.rb +107 -0
- data/lib/rubik/ui/panel.rb +98 -0
- data/lib/rubik/ui/renderer.rb +206 -0
- data/lib/rubik/ui/spinner.rb +120 -0
- data/lib/rubik/ui/statusline.rb +134 -0
- data/lib/rubik/ui/theme.rb +118 -0
- data/lib/rubik/ui.rb +57 -0
- data/lib/rubik/uris.rb +101 -0
- data/lib/rubik/version.rb +5 -0
- data/lib/rubik.rb +65 -0
- metadata +770 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../prompt"
|
|
4
|
+
require_relative "provider_recovery"
|
|
5
|
+
require_relative "goal_ledger"
|
|
6
|
+
|
|
7
|
+
module Rubik
|
|
8
|
+
module Orchestrator
|
|
9
|
+
# Step-budget warnings, token accounting and context-compaction decisions
|
|
10
|
+
# (Plan 54 D-6). Stateless collaborator over Loop-held state: counters are
|
|
11
|
+
# read through accessors at call time (tests may poke them between calls)
|
|
12
|
+
# and the total_tokens hash is mutated in place so Loop's reader keeps its
|
|
13
|
+
# object identity.
|
|
14
|
+
class BudgetManager
|
|
15
|
+
# Plan 87 E-1: state is reached through the ShellDeps façade. Accepts a
|
|
16
|
+
# bare loop for compatibility with existing test doubles (wrapped).
|
|
17
|
+
def initialize(loop_or_deps)
|
|
18
|
+
@deps = ShellDeps.wrap(loop_or_deps)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def evaluate_step_budget_warning!(warned)
|
|
22
|
+
# Threshold computed at call time: a live `!steps <n>` extension raises
|
|
23
|
+
# max_steps mid-run, and the next evaluation must use the new budget's
|
|
24
|
+
# 75% point instead of a stale pre-extension threshold.
|
|
25
|
+
warn_step = (@deps.max_steps * @deps.warn_at_ratio).ceil
|
|
26
|
+
return warned if warned || @deps.step_count < warn_step || @deps.step_count >= @deps.max_steps
|
|
27
|
+
|
|
28
|
+
remaining = @deps.max_steps - @deps.step_count
|
|
29
|
+
# Plan 61 D-14: send as its own user message — concatenating onto
|
|
30
|
+
# @history.last can weld the XML onto a tool result or a
|
|
31
|
+
# content-less assistant message, which provider formatters mangle.
|
|
32
|
+
warning_xml = "<step_budget current=\"#{@deps.step_count}\" max=\"#{@deps.max_steps}\" remaining=\"#{remaining}\" " \
|
|
33
|
+
"alert=\"Step budget nearing limit (#{remaining} steps left). Please begin concluding your work, " \
|
|
34
|
+
"verify with tests/diagnostics, and call finish. If the task genuinely needs more room, you may " \
|
|
35
|
+
"raise the live step budget with exec_repl_command('!steps <n>') where n is the new total.\" />"
|
|
36
|
+
@deps.history << { role: "user", content: warning_xml }
|
|
37
|
+
@deps.on_step&.call(type: :step_warning, current: @deps.step_count, max: @deps.max_steps, remaining: remaining)
|
|
38
|
+
true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def evaluate_context_compaction!
|
|
42
|
+
return unless @deps.context_limit
|
|
43
|
+
|
|
44
|
+
context_cfg = ProviderRecovery.compaction_config(@deps.config)
|
|
45
|
+
needs = Rubik::Prompt::ContextCompactor.needs_compaction?(
|
|
46
|
+
@deps.history, context_limit: @deps.context_limit,
|
|
47
|
+
max_output_tokens: @deps.max_output_tokens, config: context_cfg
|
|
48
|
+
)
|
|
49
|
+
return unless needs
|
|
50
|
+
|
|
51
|
+
# compact_history is pure and returns a new array, so the history
|
|
52
|
+
# reference itself must be rebound on Loop. absorb (plan D): the
|
|
53
|
+
# directive is nil unless goal-mode + knobs + coeffect + rev ≥ 1
|
|
54
|
+
# all hold — otherwise compaction stays byte-identical.
|
|
55
|
+
@deps.replace_history!(Rubik::Prompt::ContextCompactor.compact_history(
|
|
56
|
+
@deps.history, context_limit: @deps.context_limit,
|
|
57
|
+
max_output_tokens: @deps.max_output_tokens, config: context_cfg,
|
|
58
|
+
absorb: GoalLedger::ObservationProjection.directive_for(deps: @deps)
|
|
59
|
+
))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def track_token_usage!(res)
|
|
63
|
+
return unless res.respond_to?(:usage) && res.usage
|
|
64
|
+
|
|
65
|
+
in_tokens = res.usage[:input_tokens] || res.usage["input_tokens"] || 0
|
|
66
|
+
@deps.total_tokens[:input] += in_tokens
|
|
67
|
+
@deps.total_tokens[:output] += res.usage[:output_tokens] || res.usage["output_tokens"] || 0
|
|
68
|
+
warn_context_truncation!(in_tokens)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Plan 61 D-1: usage input_tokens is the server's ground truth. When
|
|
72
|
+
# it approaches the configured window the provider (not the compactor)
|
|
73
|
+
# is clipping the prompt head — surface it once so silent context loss
|
|
74
|
+
# becomes visible instead of reading as "the model forgot everything".
|
|
75
|
+
def warn_context_truncation!(in_tokens)
|
|
76
|
+
return if @deps.context_limit.nil? || in_tokens.to_i <= 0
|
|
77
|
+
return if in_tokens.to_i < (@deps.context_limit * 0.9)
|
|
78
|
+
return if @deps.context_warning_emitted
|
|
79
|
+
|
|
80
|
+
@deps.flag_context_warning!
|
|
81
|
+
@deps.on_step&.call(
|
|
82
|
+
type: :context_truncation_warning,
|
|
83
|
+
input_tokens: in_tokens.to_i,
|
|
84
|
+
context_limit: @deps.context_limit,
|
|
85
|
+
step: @deps.step_count,
|
|
86
|
+
max_steps: @deps.max_steps
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def estimate_tokens_from_history
|
|
91
|
+
# Use the same estimator as the compaction math (Plan 61 D-1): the
|
|
92
|
+
# usage fallback must agree with the window the request was sized for.
|
|
93
|
+
Rubik::Prompt::TokenEstimator.estimate_history(@deps.history)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Orchestrator
|
|
5
|
+
# Manages auto-continuation when an LLM response is truncated due to
|
|
6
|
+
# reaching max_output_tokens (finish_reason == "length" or "max_tokens").
|
|
7
|
+
#
|
|
8
|
+
# Responsible for:
|
|
9
|
+
# 1. Distinguishing between reasoning/thinking exhaustion (empty text)
|
|
10
|
+
# and content truncation (mid-sentence cut-off).
|
|
11
|
+
# 2. Buffering and stitching partial text across continuation turns.
|
|
12
|
+
# 3. Generating targeted, non-repetitive continuation prompts.
|
|
13
|
+
# 4. Enforcing the max_auto_continues safety ceiling to prevent runaways.
|
|
14
|
+
class ContinuationManager
|
|
15
|
+
TRUNCATION_REASONS = ["length", "max_tokens"].freeze
|
|
16
|
+
|
|
17
|
+
attr_reader :auto_continue_count, :continuation_buffer, :max_auto_continues
|
|
18
|
+
|
|
19
|
+
def initialize(config: nil, max_auto_continues: nil)
|
|
20
|
+
@config = config
|
|
21
|
+
@max_auto_continues = (max_auto_continues || safe_config("llm.max_auto_continues", 3)).to_i
|
|
22
|
+
@auto_continue_count = 0
|
|
23
|
+
@continuation_buffer = +""
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def enabled?
|
|
27
|
+
return true if @config.nil?
|
|
28
|
+
|
|
29
|
+
safe_config("llm.auto_continue_on_length", true) == true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def truncation?(finish_reason)
|
|
33
|
+
TRUNCATION_REASONS.include?(finish_reason.to_s.downcase)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def ceiling_reached?
|
|
37
|
+
@auto_continue_count >= @max_auto_continues
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Classifies the truncation scenario:
|
|
41
|
+
# - :thinking_exhausted when text and tools are empty (budget spent on thought)
|
|
42
|
+
# - :text_truncated when partial text was produced
|
|
43
|
+
# - :tool_truncated when partial tool call was being assembled
|
|
44
|
+
def classify_truncation(text_resp, tool_calls, _res = nil)
|
|
45
|
+
if tool_calls.empty?
|
|
46
|
+
if text_resp.to_s.strip.empty?
|
|
47
|
+
:thinking_exhausted
|
|
48
|
+
else
|
|
49
|
+
:text_truncated
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
# Tool call was partially assembled or arguments broken due to cut-off
|
|
53
|
+
:tool_truncated
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Handles a truncation event.
|
|
58
|
+
# Returns a Hash:
|
|
59
|
+
# { continue: true, nudge: "...", step_event: {...} }
|
|
60
|
+
# or
|
|
61
|
+
# { continue: false, error: "..." } if ceiling reached or disabled.
|
|
62
|
+
def handle_truncation(finish_reason:, text_resp:, tool_calls:, res: nil, max_tokens: 4096)
|
|
63
|
+
return { continue: false } unless truncation?(finish_reason)
|
|
64
|
+
|
|
65
|
+
unless enabled?
|
|
66
|
+
rec = (max_tokens.to_i * 2).positive? ? max_tokens.to_i * 2 : 8192
|
|
67
|
+
diag = "⚠️ Model çıktısı maksimum token sınırına (max_output_tokens: #{max_tokens}) ulaştığı için tamamlanamadan kesildi.\n" \
|
|
68
|
+
"Çözüm: Çıktı token bütçesini artırmak için şu komutu çalıştırabilirsiniz:\n !config set llm.default_max_output_tokens #{rec}"
|
|
69
|
+
return { continue: false, error: diag }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if ceiling_reached?
|
|
73
|
+
rec = (max_tokens.to_i * 2).positive? ? max_tokens.to_i * 2 : 8192
|
|
74
|
+
diag = "⚠️ Model çıktısı maksimum token sınırına (max_output_tokens: #{max_tokens}) ulaştığı için " \
|
|
75
|
+
"ve #{@max_auto_continues} otomatik devam denemesi tükendiği için durduruldu.\n" \
|
|
76
|
+
"Çözüm: Çıktı token bütçesini artırmak için şu komutu çalıştırabilirsiniz:\n !config set llm.default_max_output_tokens #{rec}"
|
|
77
|
+
return { continue: false, error: diag }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
scenario = classify_truncation(text_resp, tool_calls, res)
|
|
81
|
+
@auto_continue_count += 1
|
|
82
|
+
|
|
83
|
+
case scenario
|
|
84
|
+
when :thinking_exhausted
|
|
85
|
+
{
|
|
86
|
+
continue: true,
|
|
87
|
+
scenario: :thinking_exhausted,
|
|
88
|
+
nudge: "Your thinking process reached the output token limit before producing a response or tool call. " \
|
|
89
|
+
"Do not repeat your reasoning; immediately provide your final response or invoke the necessary tool call concisely.",
|
|
90
|
+
thought: res.respond_to?(:thought) ? res.thought : nil,
|
|
91
|
+
step_event: { type: :auto_continue, reason: :thinking_exhausted, count: @auto_continue_count, max: @max_auto_continues }
|
|
92
|
+
}
|
|
93
|
+
when :text_truncated
|
|
94
|
+
@continuation_buffer << text_resp.to_s
|
|
95
|
+
{
|
|
96
|
+
continue: true,
|
|
97
|
+
scenario: :text_truncated,
|
|
98
|
+
partial_text: text_resp.to_s,
|
|
99
|
+
nudge: "Your previous response was cut off because it reached the output token limit. " \
|
|
100
|
+
"Continue directly and seamlessly from the exact character/code line where you stopped. " \
|
|
101
|
+
"Do not repeat previous text or restart.",
|
|
102
|
+
step_event: { type: :auto_continue, reason: :text_truncated, count: @auto_continue_count, max: @max_auto_continues }
|
|
103
|
+
}
|
|
104
|
+
when :tool_truncated
|
|
105
|
+
{
|
|
106
|
+
continue: true,
|
|
107
|
+
scenario: :tool_truncated,
|
|
108
|
+
nudge: "Your tool call was cut off due to the output token limit. Please re-invoke the tool call directly with concise arguments.",
|
|
109
|
+
step_event: { type: :auto_continue, reason: :tool_truncated, count: @auto_continue_count, max: @max_auto_continues }
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Stitches any accumulated continuation buffer with the final response
|
|
115
|
+
# text and resets the continuation state.
|
|
116
|
+
def stitch_and_reset(final_text)
|
|
117
|
+
stitched = if @continuation_buffer.empty?
|
|
118
|
+
final_text.to_s
|
|
119
|
+
else
|
|
120
|
+
@continuation_buffer + final_text.to_s
|
|
121
|
+
end
|
|
122
|
+
reset!
|
|
123
|
+
stitched
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def reset!
|
|
127
|
+
@auto_continue_count = 0
|
|
128
|
+
@continuation_buffer = +""
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def safe_config(key, default)
|
|
134
|
+
return default unless @config
|
|
135
|
+
|
|
136
|
+
if @config.respond_to?(:fetch)
|
|
137
|
+
begin
|
|
138
|
+
val = @config.fetch(key)
|
|
139
|
+
return val unless val.nil?
|
|
140
|
+
rescue KeyError, ArgumentError
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
if key.include?(".") && @config.respond_to?(:dig)
|
|
146
|
+
val = @config.dig(*key.split("."))
|
|
147
|
+
return val unless val.nil?
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if @config.respond_to?(:[])
|
|
151
|
+
val = @config[key]
|
|
152
|
+
return val unless val.nil?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
default
|
|
156
|
+
rescue KeyError, TypeError, NoMethodError
|
|
157
|
+
default
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Orchestrator
|
|
5
|
+
# Selects the final message shown to the user when the agent finishes.
|
|
6
|
+
# Extracted from StepExecutor#handle_finish_tool (review Finding 5): the
|
|
7
|
+
# heuristic was inline, undocumented, and its magic substring
|
|
8
|
+
# `!clean_text_resp.include?("tool")` had no explanation of intent.
|
|
9
|
+
#
|
|
10
|
+
# Inputs (both already normalized by the caller):
|
|
11
|
+
# narration — the turn's free text with JSON tool-call blocks stripped
|
|
12
|
+
# (StepExecutor#clean_llm_text), stripped and possibly empty.
|
|
13
|
+
# tool_msg — the finish tool's `message` argument, stripped.
|
|
14
|
+
#
|
|
15
|
+
# Selection (behavior preserved exactly from the inline version):
|
|
16
|
+
# 1. tool_msg non-empty and NOT a generic filler ("completed", "done",
|
|
17
|
+
# "finished", "ok", "task_completed"):
|
|
18
|
+
# - narration and message don't subsume each other -> both, joined.
|
|
19
|
+
# - otherwise keep the richer (longer) one, unless the narration is
|
|
20
|
+
# mere tool-usage narration (see NARRATION_TOOL_HINT below).
|
|
21
|
+
# 2. tool_msg empty or generic filler: prefer the narration if present.
|
|
22
|
+
# 3. Neither has content: fallback constant.
|
|
23
|
+
#
|
|
24
|
+
# The narration-skip heuristic: a narration that merely mentions "tool"
|
|
25
|
+
# ("I'll use the run_tests tool next...") is process talk, not an answer;
|
|
26
|
+
# the finish tool's message is the better user-facing summary, so the
|
|
27
|
+
# message wins even when the narration is longer.
|
|
28
|
+
module FinishMessageSelector
|
|
29
|
+
GENERIC_FILLERS = ["completed", "done", "finished", "ok", "task_completed"].freeze
|
|
30
|
+
NARRATION_TOOL_HINT = "tool"
|
|
31
|
+
DEFAULT_MESSAGE = "Completed"
|
|
32
|
+
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
def select(narration, tool_msg)
|
|
36
|
+
narration = narration.to_s
|
|
37
|
+
tool_msg = tool_msg.to_s
|
|
38
|
+
|
|
39
|
+
if !tool_msg.empty? && !GENERIC_FILLERS.include?(tool_msg.downcase)
|
|
40
|
+
if !narration.empty? && !narration.include?(tool_msg) && !tool_msg.include?(narration)
|
|
41
|
+
"#{narration}\n\n#{tool_msg}"
|
|
42
|
+
elsif narration.length > tool_msg.length && !narration.include?(NARRATION_TOOL_HINT)
|
|
43
|
+
narration
|
|
44
|
+
else
|
|
45
|
+
tool_msg
|
|
46
|
+
end
|
|
47
|
+
elsif !narration.empty?
|
|
48
|
+
narration
|
|
49
|
+
else
|
|
50
|
+
tool_msg.empty? ? DEFAULT_MESSAGE : tool_msg
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Orchestrator
|
|
5
|
+
module GoalLedger
|
|
6
|
+
# Drift işleme (goal-ledger planı B): failed-doğrulamaların iç mutasyona
|
|
7
|
+
# eşlenmesi (flip + last_check annotation), sessiz :ledger_drift
|
|
8
|
+
# emission'u ve reinjection taşıyıcısı DriftNote.
|
|
9
|
+
#
|
|
10
|
+
# Sessizlik sözleşmesi: :ledger_drift WAKE_KINDS DIŞINDADIR —
|
|
11
|
+
# notice/wake üretmez, vakanüvis abone değildir (yalnız kronik
|
|
12
|
+
# bus'da kaybolur); GoalState#notify_finish mold'ü: post fire-and-
|
|
13
|
+
# forget, rescue → Log.warn. Bus yoksa tamamen sessiz atlanır.
|
|
14
|
+
module Drift
|
|
15
|
+
EVENT = :ledger_drift
|
|
16
|
+
MAX_NOTE_ENTRIES = 8
|
|
17
|
+
MAX_NOTE_CHARS = 300
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
def emit(trigger:, ledger:, flips:, unknowns:, rev:)
|
|
22
|
+
bus = ledger.event_bus
|
|
23
|
+
unless bus.respond_to?(:post)
|
|
24
|
+
# Dürüst degradasyon (review 2026-10-10): bus yokken drift kaydı
|
|
25
|
+
# tamamen kaybolmasın — Log'a tek satır düşer.
|
|
26
|
+
Rubik::Log.warn(:orchestrator, "event bus unavailable — ledger drift dropped " \
|
|
27
|
+
"(#{flips.size} flip(s), #{unknowns.size} unknown(s), rev #{rev})")
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
bus.post(EVENT,
|
|
32
|
+
{ trigger: trigger.to_s, rev: rev, flipped: flips.size,
|
|
33
|
+
unknowns: unknowns.map { |u| "unknown #{u[:collection]}[#{u[:index]}] #{u[:id]}: #{u[:reason]}" },
|
|
34
|
+
entries: flips.map { |f| "#{f[:collection]}[#{f[:index]}] #{f[:id]}: #{f[:reason]}" } },
|
|
35
|
+
subsystem: :orchestrator, level: :warn)
|
|
36
|
+
rescue StandardError => e
|
|
37
|
+
Rubik::Log.warn(:orchestrator, "ledger drift event post failed: #{e.class}: #{e.message}")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Reinjection taşıyıcısı: son doğrulamada FAILED olan (o an stale)
|
|
42
|
+
# girdilerden en yeni MAX_NOTE_ENTRIES tanesi, girdi başına
|
|
43
|
+
# MAX_NOTE_CHARS. Boşsa HİÇ render edilmez (OFF/drift'siz koşu
|
|
44
|
+
# byte-özdeş). Tüm metin xml_escape'ten geçer.
|
|
45
|
+
module DriftNote
|
|
46
|
+
module_function
|
|
47
|
+
|
|
48
|
+
def render(snap:)
|
|
49
|
+
entries = []
|
|
50
|
+
Array(snap["tasks"]).each do |task|
|
|
51
|
+
check = task.is_a?(Hash) && task["verify"].is_a?(Hash) ? task["verify"]["last_check"] : nil
|
|
52
|
+
next unless check.is_a?(Hash) && check["state"] == "failed"
|
|
53
|
+
|
|
54
|
+
entries << "task #{task['id']} (was #{check['from']}) -> stale: #{check['reason']} at #{check['at']}"
|
|
55
|
+
end
|
|
56
|
+
Array(snap["environment_notes"]).each do |note|
|
|
57
|
+
check = note.is_a?(Hash) && note["verify"].is_a?(Hash) ? note["verify"]["last_check"] : nil
|
|
58
|
+
next unless check.is_a?(Hash) && check["state"] == "failed"
|
|
59
|
+
|
|
60
|
+
entries << "environment_note #{note['id']} -> verify failed: #{check['reason']} at #{check['at']}"
|
|
61
|
+
end
|
|
62
|
+
return nil if entries.empty?
|
|
63
|
+
|
|
64
|
+
esc = Rubik::Platform.method(:xml_escape)
|
|
65
|
+
body = entries.last(Drift::MAX_NOTE_ENTRIES).map { |line| esc.call(line.to_s.slice(0, Drift::MAX_NOTE_CHARS)) }
|
|
66
|
+
%(<ledger_drift rev="#{snap['rev']}" entries="#{body.size}">\n #{body.join("\n ")}\n</ledger_drift>)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|