alysis-code 0.13.0__py3-none-any.whl
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.
- alysis_code/__init__.py +3 -0
- alysis_code/__main__.py +4 -0
- alysis_code/_build_info.py +14 -0
- alysis_code/account_login.py +468 -0
- alysis_code/agent/README.md +35 -0
- alysis_code/agent/__init__.py +11 -0
- alysis_code/agent/acceptance_contract.py +2217 -0
- alysis_code/agent/blast_radius.py +1403 -0
- alysis_code/agent/cache_keepalive.py +227 -0
- alysis_code/agent/completion_certificate.py +366 -0
- alysis_code/agent/completion_gate.py +306 -0
- alysis_code/agent/empty_response_stall.py +403 -0
- alysis_code/agent/errors.py +28 -0
- alysis_code/agent/llm_calls.py +475 -0
- alysis_code/agent/mutation_classification.py +227 -0
- alysis_code/agent/prompt_context.py +2508 -0
- alysis_code/agent/read_ledger.py +253 -0
- alysis_code/agent/regression_baseline.py +642 -0
- alysis_code/agent/reproduction_first.py +610 -0
- alysis_code/agent/sensitive_output.py +629 -0
- alysis_code/agent/session.py +3218 -0
- alysis_code/agent/steering.py +191 -0
- alysis_code/agent/subagent_execution.py +5177 -0
- alysis_code/agent/subagent_workspace.py +666 -0
- alysis_code/agent/tools_assembly.py +4728 -0
- alysis_code/agent/turn/__init__.py +101 -0
- alysis_code/agent/turn/core.py +8483 -0
- alysis_code/agent/turn/events.py +113 -0
- alysis_code/agent/turn/exploration.py +590 -0
- alysis_code/agent/turn/interventions.py +65 -0
- alysis_code/agent/turn/read_cache.py +420 -0
- alysis_code/agent/turn/snapshot.py +179 -0
- alysis_code/agent/turn_contract.py +661 -0
- alysis_code/agent/turn_path.py +129 -0
- alysis_code/agent/verification.py +2885 -0
- alysis_code/agent/verification_commands.py +512 -0
- alysis_code/agent/verification_evidence.py +738 -0
- alysis_code/agent_loop.py +693 -0
- alysis_code/agent_runtimes/__init__.py +51 -0
- alysis_code/agent_runtimes/base.py +114 -0
- alysis_code/agent_runtimes/builtins.py +129 -0
- alysis_code/agent_runtimes/codex_cli.py +664 -0
- alysis_code/agent_runtimes/host.py +263 -0
- alysis_code/agent_runtimes/registry.py +64 -0
- alysis_code/agent_runtimes/service.py +150 -0
- alysis_code/agentbox_client.py +416 -0
- alysis_code/agentbox_integration.py +310 -0
- alysis_code/alysis_cloud.py +152 -0
- alysis_code/approval_scope.py +276 -0
- alysis_code/assets/README.md +33 -0
- alysis_code/assets/__init__.py +126 -0
- alysis_code/assets/asset_read_core.py +281 -0
- alysis_code/assets/budget_allocator.py +456 -0
- alysis_code/assets/comprehender.py +759 -0
- alysis_code/assets/index.py +654 -0
- alysis_code/assets/ingestion.py +275 -0
- alysis_code/assets/legacy_migration.py +413 -0
- alysis_code/assets/models.py +263 -0
- alysis_code/assets/ocr.py +239 -0
- alysis_code/assets/owl/ascii/f-000.txt +13 -0
- alysis_code/assets/owl/ascii/f-001.txt +13 -0
- alysis_code/assets/owl/ascii/f-002.txt +13 -0
- alysis_code/assets/owl/ascii/f-003.txt +13 -0
- alysis_code/assets/owl/ascii/f-004.txt +13 -0
- alysis_code/assets/owl/ascii/f-005.txt +13 -0
- alysis_code/assets/owl/ascii/f-006.txt +13 -0
- alysis_code/assets/owl/ascii/f-007.txt +13 -0
- alysis_code/assets/owl/ascii/f-008.txt +13 -0
- alysis_code/assets/owl/ascii/f-009.txt +13 -0
- alysis_code/assets/owl/ascii/f-010.txt +13 -0
- alysis_code/assets/owl/ascii/f-011.txt +13 -0
- alysis_code/assets/owl/ascii/f-012.txt +13 -0
- alysis_code/assets/owl/ascii/f-013.txt +13 -0
- alysis_code/assets/owl/ascii/f-014.txt +13 -0
- alysis_code/assets/owl/ascii/f-015.txt +13 -0
- alysis_code/assets/owl/ascii/f-016.txt +13 -0
- alysis_code/assets/owl/ascii/f-017.txt +13 -0
- alysis_code/assets/owl/ascii/f-018.txt +13 -0
- alysis_code/assets/owl/ascii/f-019.txt +13 -0
- alysis_code/assets/owl/ascii/f-020.txt +13 -0
- alysis_code/assets/owl/index.html +98 -0
- alysis_code/assets/owl/show-owl.sh +761 -0
- alysis_code/assets/paths.py +49 -0
- alysis_code/assets/plan_binding.py +326 -0
- alysis_code/assets/planner_context.py +466 -0
- alysis_code/assets/planner_tools.py +184 -0
- alysis_code/assets/prompts.py +101 -0
- alysis_code/assets/replanner_context.py +239 -0
- alysis_code/assets/surface.py +521 -0
- alysis_code/assets/untrusted_content.py +48 -0
- alysis_code/assets/usage_logger.py +94 -0
- alysis_code/assets/worker_mirror.py +428 -0
- alysis_code/assets/worker_section.py +303 -0
- alysis_code/assets/worker_tools.py +468 -0
- alysis_code/atomic_io.py +83 -0
- alysis_code/auth_diagnostics.py +272 -0
- alysis_code/background_runner.py +366 -0
- alysis_code/branding.py +270 -0
- alysis_code/budget_policy.py +390 -0
- alysis_code/build_identity.py +465 -0
- alysis_code/builtin_hooks/__init__.py +7 -0
- alysis_code/builtin_hooks/notify_done_windows.py +65 -0
- alysis_code/bwrap_etc.py +76 -0
- alysis_code/cancellation.py +41 -0
- alysis_code/capabilities.py +137 -0
- alysis_code/chatgpt_codex_static_provider.py +133 -0
- alysis_code/cli.py +51 -0
- alysis_code/cli_impl/__init__.py +1 -0
- alysis_code/cli_impl/assets_cli.py +537 -0
- alysis_code/cli_impl/assets_modal.py +412 -0
- alysis_code/cli_impl/chat/__init__.py +156 -0
- alysis_code/cli_impl/chat/commands.py +2616 -0
- alysis_code/cli_impl/chat/loop.py +4508 -0
- alysis_code/cli_impl/chat/mid_turn_policy.py +125 -0
- alysis_code/cli_impl/chat/rendering.py +444 -0
- alysis_code/cli_impl/chat/state.py +124 -0
- alysis_code/cli_impl/chat_resume.py +830 -0
- alysis_code/cli_impl/chat_slash_completer.py +258 -0
- alysis_code/cli_impl/commands/__init__.py +11 -0
- alysis_code/cli_impl/commands/_shared.py +89 -0
- alysis_code/cli_impl/commands/auth.py +623 -0
- alysis_code/cli_impl/commands/chat_resume_helpers.py +1531 -0
- alysis_code/cli_impl/commands/chat_state.py +158 -0
- alysis_code/cli_impl/commands/chat_status.py +1248 -0
- alysis_code/cli_impl/commands/chat_terminal.py +942 -0
- alysis_code/cli_impl/commands/chat_tui_panels.py +1018 -0
- alysis_code/cli_impl/commands/cli_common.py +1223 -0
- alysis_code/cli_impl/commands/cli_surface.py +77 -0
- alysis_code/cli_impl/commands/config.py +131 -0
- alysis_code/cli_impl/commands/conventions.py +85 -0
- alysis_code/cli_impl/commands/execution_helpers.py +350 -0
- alysis_code/cli_impl/commands/extensions.py +401 -0
- alysis_code/cli_impl/commands/forge.py +1282 -0
- alysis_code/cli_impl/commands/forge_asset_view.py +121 -0
- alysis_code/cli_impl/commands/forge_helpers.py +1215 -0
- alysis_code/cli_impl/commands/hooks.py +737 -0
- alysis_code/cli_impl/commands/ide_bridge.py +31 -0
- alysis_code/cli_impl/commands/mcp.py +700 -0
- alysis_code/cli_impl/commands/profile.py +453 -0
- alysis_code/cli_impl/commands/prompt_helpers.py +307 -0
- alysis_code/cli_impl/commands/report.py +88 -0
- alysis_code/cli_impl/commands/root.py +1118 -0
- alysis_code/cli_impl/commands/sandbox.py +184 -0
- alysis_code/cli_impl/commands/server.py +54 -0
- alysis_code/cli_impl/commands/sessions.py +252 -0
- alysis_code/cli_impl/commands/skills.py +404 -0
- alysis_code/cli_impl/commands/startup.py +946 -0
- alysis_code/cli_impl/commands/tools.py +335 -0
- alysis_code/cli_impl/commands/update.py +364 -0
- alysis_code/cli_impl/commands/welcome.py +972 -0
- alysis_code/cli_impl/config_menu.py +3882 -0
- alysis_code/cli_impl/forge.py +4509 -0
- alysis_code/cli_impl/forge_recovery.py +485 -0
- alysis_code/cli_impl/setup_wizard.py +2409 -0
- alysis_code/cli_impl/tui/__init__.py +58 -0
- alysis_code/cli_impl/tui/app.py +4551 -0
- alysis_code/cli_impl/tui/config.py +32 -0
- alysis_code/cli_impl/tui/config_flow.py +2754 -0
- alysis_code/cli_impl/tui/config_overlay.py +566 -0
- alysis_code/cli_impl/tui/content.py +78 -0
- alysis_code/cli_impl/tui/footer.py +218 -0
- alysis_code/cli_impl/tui/forge_status.py +136 -0
- alysis_code/cli_impl/tui/markdown.py +244 -0
- alysis_code/cli_impl/tui/owl.py +109 -0
- alysis_code/cli_impl/tui/plan_meta.py +477 -0
- alysis_code/cli_impl/tui/setup_app.py +519 -0
- alysis_code/cli_impl/tui/setup_flow.py +1622 -0
- alysis_code/cli_impl/tui/state.py +101 -0
- alysis_code/cli_impl/tui/subagent_identity.py +66 -0
- alysis_code/cli_impl/tui/subagent_panel.py +186 -0
- alysis_code/cli_impl/tui/surface.py +796 -0
- alysis_code/cli_impl/tui/transcript.py +514 -0
- alysis_code/cli_impl/tui/update_prompt.py +79 -0
- alysis_code/cli_impl/tui/workspace_guard.py +384 -0
- alysis_code/clipboard.py +172 -0
- alysis_code/code_review.py +1211 -0
- alysis_code/compaction/__init__.py +28 -0
- alysis_code/compaction/conversation_compactor.py +2932 -0
- alysis_code/compaction/importance.py +177 -0
- alysis_code/compaction/settings.py +297 -0
- alysis_code/compaction/tool_output_offload.py +447 -0
- alysis_code/config.py +3509 -0
- alysis_code/conflict_auto_resolver.py +895 -0
- alysis_code/context/__init__.py +1 -0
- alysis_code/context/tool_schema_budgeter.py +220 -0
- alysis_code/crash_diagnostics.py +282 -0
- alysis_code/custom_tools/README.md +34 -0
- alysis_code/custom_tools/__init__.py +43 -0
- alysis_code/custom_tools/discovery.py +903 -0
- alysis_code/custom_tools/runtime.py +1516 -0
- alysis_code/custom_tools/session.py +227 -0
- alysis_code/custom_tools/trust.py +232 -0
- alysis_code/diff_paths.py +113 -0
- alysis_code/direction_change.py +293 -0
- alysis_code/dispatch_timing.py +306 -0
- alysis_code/durable_service_manager.py +1236 -0
- alysis_code/edit_discipline.py +659 -0
- alysis_code/error_text.py +73 -0
- alysis_code/execution_budget.py +411 -0
- alysis_code/execution_context.py +915 -0
- alysis_code/execution_deadline.py +1065 -0
- alysis_code/execution_shared.py +1904 -0
- alysis_code/extensions/README.md +30 -0
- alysis_code/extensions/__init__.py +93 -0
- alysis_code/extensions/activation.py +138 -0
- alysis_code/extensions/install.py +1436 -0
- alysis_code/extensions/manifest.py +487 -0
- alysis_code/extensions/models.py +74 -0
- alysis_code/extensions/paths.py +56 -0
- alysis_code/extensions/registry.json +4 -0
- alysis_code/extensions/registry.py +52 -0
- alysis_code/extensions/state.py +83 -0
- alysis_code/extensions/workspace_trust.py +101 -0
- alysis_code/failed_task_evidence.py +369 -0
- alysis_code/failure_category.py +315 -0
- alysis_code/feedback_report.py +1647 -0
- alysis_code/file_classification.py +485 -0
- alysis_code/forge.py +2064 -0
- alysis_code/forge_completion.py +362 -0
- alysis_code/forge_events.py +475 -0
- alysis_code/frontmatter_utils.py +95 -0
- alysis_code/git_evidence.py +1181 -0
- alysis_code/git_ops.py +560 -0
- alysis_code/git_safe.py +62 -0
- alysis_code/git_worktrees.py +190 -0
- alysis_code/hooks/README.md +33 -0
- alysis_code/hooks/__init__.py +67 -0
- alysis_code/hooks/audit.py +171 -0
- alysis_code/hooks/config.py +225 -0
- alysis_code/hooks/dispatcher.py +1110 -0
- alysis_code/hooks/models.py +447 -0
- alysis_code/hooks/trust.py +202 -0
- alysis_code/host_actions.py +543 -0
- alysis_code/host_browser.py +103 -0
- alysis_code/ide/__init__.py +5 -0
- alysis_code/ide/activity_events.py +399 -0
- alysis_code/ide/approvals.py +337 -0
- alysis_code/ide/artifacts.py +153 -0
- alysis_code/ide/browser_egress_proxy.py +1076 -0
- alysis_code/ide/cdp_websocket_transport.py +1192 -0
- alysis_code/ide/change_ledger.py +1721 -0
- alysis_code/ide/context_blocks.py +979 -0
- alysis_code/ide/event_stream.py +531 -0
- alysis_code/ide/forge_protocol.py +3112 -0
- alysis_code/ide/forge_request_ledger.py +737 -0
- alysis_code/ide/health.py +965 -0
- alysis_code/ide/managed_browser.py +2251 -0
- alysis_code/ide/management_protocol.py +3414 -0
- alysis_code/ide/mcp_oauth_coordinator.py +744 -0
- alysis_code/ide/mcp_oauth_lifecycle.py +1504 -0
- alysis_code/ide/prompt_queue.py +1070 -0
- alysis_code/ide/protocol.py +191 -0
- alysis_code/ide/resumable_swarm.py +1543 -0
- alysis_code/ide/session_search.py +295 -0
- alysis_code/ide/stdio_bridge.py +9935 -0
- alysis_code/ide/structured_state.py +1579 -0
- alysis_code/ide/swarm_protocol.py +816 -0
- alysis_code/integration_gate.py +506 -0
- alysis_code/interactive_input_guard.py +39 -0
- alysis_code/interactive_plan_mode.py +26 -0
- alysis_code/internal_artifacts.py +179 -0
- alysis_code/knowledge_base.py +1409 -0
- alysis_code/knowledge_capture.py +1190 -0
- alysis_code/knowledge_librarian.py +605 -0
- alysis_code/language_policy.py +34 -0
- alysis_code/litellm_static_provider.py +535 -0
- alysis_code/llm/__init__.py +1 -0
- alysis_code/llm/anthropic_messages.py +2288 -0
- alysis_code/llm/base.py +71 -0
- alysis_code/llm/cache_capabilities.py +985 -0
- alysis_code/llm/cache_control_blocks.py +244 -0
- alysis_code/llm/cache_policy.py +388 -0
- alysis_code/llm/factory.py +373 -0
- alysis_code/llm/gemini_generate_content.py +2652 -0
- alysis_code/llm/gemini_interactions.py +739 -0
- alysis_code/llm/metadata.py +450 -0
- alysis_code/llm/openai_compat.py +2947 -0
- alysis_code/llm/openai_responses.py +2604 -0
- alysis_code/llm/protocols.py +609 -0
- alysis_code/llm/provider_limits.py +525 -0
- alysis_code/llm/request_plan.py +389 -0
- alysis_code/llm/request_shape.py +238 -0
- alysis_code/llm/streaming.py +108 -0
- alysis_code/llm/temperature_compat.py +78 -0
- alysis_code/llm/types.py +195 -0
- alysis_code/llm/usage_normalization.py +222 -0
- alysis_code/llm_error_display.py +315 -0
- alysis_code/logging_redaction.py +326 -0
- alysis_code/managed_host_deadline.py +196 -0
- alysis_code/mcp/README.md +33 -0
- alysis_code/mcp/__init__.py +24 -0
- alysis_code/mcp/client.py +1137 -0
- alysis_code/mcp/config.py +597 -0
- alysis_code/mcp/errors.py +113 -0
- alysis_code/mcp/forge_scope.py +154 -0
- alysis_code/mcp/jsonrpc.py +214 -0
- alysis_code/mcp/manager.py +2308 -0
- alysis_code/mcp/models.py +666 -0
- alysis_code/mcp/oauth.py +972 -0
- alysis_code/mcp/oauth_runtime.py +310 -0
- alysis_code/mcp/oauth_store.py +276 -0
- alysis_code/mcp/prompts.py +329 -0
- alysis_code/mcp/resources.py +295 -0
- alysis_code/mcp/roots.py +106 -0
- alysis_code/mcp/server_requests.py +75 -0
- alysis_code/mcp/token_store.py +859 -0
- alysis_code/mcp/transport_http.py +1338 -0
- alysis_code/mcp/transport_stdio.py +1267 -0
- alysis_code/mcp/untrusted_content.py +119 -0
- alysis_code/merge_conflict_reviewer.py +729 -0
- alysis_code/model_catalog/__init__.py +1 -0
- alysis_code/model_catalog/chatgpt_codex_subscription_snapshot.json +186 -0
- alysis_code/model_catalog/litellm_model_prices_snapshot.json +44715 -0
- alysis_code/model_catalog/litellm_model_prices_snapshot.meta.json +17 -0
- alysis_code/model_metadata_policy.py +223 -0
- alysis_code/model_metadata_utils.py +103 -0
- alysis_code/model_registry.py +1420 -0
- alysis_code/model_router.py +147 -0
- alysis_code/permission_policy.py +1016 -0
- alysis_code/personas.py +451 -0
- alysis_code/pipeline_facts.py +233 -0
- alysis_code/plan_assistant.py +4763 -0
- alysis_code/plan_mode.py +393 -0
- alysis_code/plan_reconciliation.py +1228 -0
- alysis_code/plan_repair.py +652 -0
- alysis_code/plan_validation.py +1099 -0
- alysis_code/planning_constraints.py +904 -0
- alysis_code/policy.py +95 -0
- alysis_code/preview_server.py +457 -0
- alysis_code/process_reaping.py +566 -0
- alysis_code/profile_presets.py +1834 -0
- alysis_code/profiles.py +666 -0
- alysis_code/provider_auth/__init__.py +29 -0
- alysis_code/provider_auth/base.py +99 -0
- alysis_code/provider_auth/openai_codex.py +951 -0
- alysis_code/provider_auth/registry.py +76 -0
- alysis_code/provider_auth/store.py +125 -0
- alysis_code/provider_diagnostics.py +1209 -0
- alysis_code/provider_model_catalog.py +685 -0
- alysis_code/provider_telemetry.py +1699 -0
- alysis_code/provider_url.py +75 -0
- alysis_code/reasoning_contracts.py +911 -0
- alysis_code/remote_sync.py +350 -0
- alysis_code/replanning.py +1195 -0
- alysis_code/repo_scan.py +1152 -0
- alysis_code/request_estimation.py +296 -0
- alysis_code/review_gate.py +617 -0
- alysis_code/run_lock.py +1141 -0
- alysis_code/run_outcome.py +58 -0
- alysis_code/run_provenance.py +774 -0
- alysis_code/run_state.py +445 -0
- alysis_code/runtime_artifacts.py +116 -0
- alysis_code/runtime_context_features.py +78 -0
- alysis_code/runtime_kind.py +52 -0
- alysis_code/safety/__init__.py +11 -0
- alysis_code/safety/mcp_sanitize.py +29 -0
- alysis_code/safety/safe_http.py +297 -0
- alysis_code/safety/subagent_report.py +184 -0
- alysis_code/sandbox_doctor.py +682 -0
- alysis_code/sandbox_runner.py +1025 -0
- alysis_code/sandbox_settings.py +423 -0
- alysis_code/serialized_paths.py +355 -0
- alysis_code/server/__init__.py +3 -0
- alysis_code/server/app.py +367 -0
- alysis_code/server/auth.py +34 -0
- alysis_code/server/job_config.py +30 -0
- alysis_code/server/settings.py +215 -0
- alysis_code/server/store.py +193 -0
- alysis_code/server/worker_runner.py +657 -0
- alysis_code/service_persistence.py +355 -0
- alysis_code/session_artifacts.py +108 -0
- alysis_code/session_metrics.py +331 -0
- alysis_code/session_store.py +624 -0
- alysis_code/skills/README.md +34 -0
- alysis_code/skills/__init__.py +104 -0
- alysis_code/skills/conventions.py +84 -0
- alysis_code/skills/discovery.py +176 -0
- alysis_code/skills/eval_models.py +232 -0
- alysis_code/skills/eval_runner.py +372 -0
- alysis_code/skills/evals.py +1344 -0
- alysis_code/skills/install.py +293 -0
- alysis_code/skills/loader.py +118 -0
- alysis_code/skills/matching.py +103 -0
- alysis_code/skills/models.py +71 -0
- alysis_code/skills/paths.py +56 -0
- alysis_code/skills/prompting.py +500 -0
- alysis_code/skills/scaffold.py +142 -0
- alysis_code/skills/state.py +441 -0
- alysis_code/skills/transactions.py +125 -0
- alysis_code/skills/validation.py +304 -0
- alysis_code/step_budget.py +238 -0
- alysis_code/subagent_labels.py +49 -0
- alysis_code/subagents.py +1072 -0
- alysis_code/surface/__init__.py +80 -0
- alysis_code/surface/base.py +305 -0
- alysis_code/surface/console.py +387 -0
- alysis_code/surface/events.py +372 -0
- alysis_code/surface/hidden_surface.py +529 -0
- alysis_code/surface/noop_surface.py +219 -0
- alysis_code/surface/rich_surface.py +1555 -0
- alysis_code/surface/styles.py +67 -0
- alysis_code/surface/theme.py +455 -0
- alysis_code/surface/types.py +100 -0
- alysis_code/swarm_backend.py +926 -0
- alysis_code/swarm_orchestrator.py +4020 -0
- alysis_code/swarm_scheduler.py +441 -0
- alysis_code/swarm_trace.py +429 -0
- alysis_code/swarm_worker.py +2119 -0
- alysis_code/swarm_write_guard.py +348 -0
- alysis_code/task_dependencies.py +170 -0
- alysis_code/task_readiness.py +992 -0
- alysis_code/task_scope.py +2148 -0
- alysis_code/terminal_manager.py +762 -0
- alysis_code/terminal_ownership.py +460 -0
- alysis_code/text_normalization.py +30 -0
- alysis_code/token_budget.py +97 -0
- alysis_code/tools/README.md +34 -0
- alysis_code/tools/__init__.py +1 -0
- alysis_code/tools/artifacts.py +127 -0
- alysis_code/tools/availability.py +188 -0
- alysis_code/tools/fs.py +1456 -0
- alysis_code/tools/git.py +461 -0
- alysis_code/tools/history.py +229 -0
- alysis_code/tools/http_timeout.py +78 -0
- alysis_code/tools/image_generation.py +552 -0
- alysis_code/tools/registry.py +2936 -0
- alysis_code/tools/repo_map.py +476 -0
- alysis_code/tools/search.py +563 -0
- alysis_code/tools/shell.py +135 -0
- alysis_code/tools/symbols.py +1350 -0
- alysis_code/tools/test_discovery.py +643 -0
- alysis_code/tools/web.py +482 -0
- alysis_code/tools/web_search.py +2012 -0
- alysis_code/tools/web_search_dashscope.py +557 -0
- alysis_code/tools/web_search_ddgs.py +221 -0
- alysis_code/tools/web_search_provider_adapters.py +1429 -0
- alysis_code/tools/web_search_tavily.py +194 -0
- alysis_code/updates.py +933 -0
- alysis_code/usage_tracker.py +1990 -0
- alysis_code/verification_command_analysis.py +1004 -0
- alysis_code/verification_contract.py +574 -0
- alysis_code/verification_failure_summary.py +273 -0
- alysis_code/verification_repair.py +385 -0
- alysis_code/verify_gate.py +3129 -0
- alysis_code/web_research.py +1872 -0
- alysis_code/web_search_adapters.py +66 -0
- alysis_code/web_search_policy.py +27 -0
- alysis_code/workspace_binding.py +389 -0
- alysis_code/workspace_binding_ui.py +408 -0
- alysis_code/workspace_context.py +273 -0
- alysis_code/workspace_isolation.py +138 -0
- alysis_code/workspace_provisioning.py +455 -0
- alysis_code-0.13.0.dist-info/METADATA +507 -0
- alysis_code-0.13.0.dist-info/RECORD +458 -0
- alysis_code-0.13.0.dist-info/WHEEL +4 -0
- alysis_code-0.13.0.dist-info/entry_points.txt +3 -0
- alysis_code-0.13.0.dist-info/licenses/LICENSE +176 -0
- alysis_code-0.13.0.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
# ruff: noqa: F401,F403,F405,I001
|
|
2
|
+
# Legacy split module: dependencies are synced by cli_surface.py.
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ...surface.theme import (
|
|
6
|
+
_hex_to_8bit,
|
|
7
|
+
_load_jsonc_file,
|
|
8
|
+
_strip_json_comments,
|
|
9
|
+
_theme_from_colorfgbg,
|
|
10
|
+
_theme_from_hex_background,
|
|
11
|
+
_theme_from_osc11,
|
|
12
|
+
_theme_from_windows_terminal_settings,
|
|
13
|
+
_windows_terminal_builtin_scheme_theme,
|
|
14
|
+
_windows_terminal_scheme_name,
|
|
15
|
+
_windows_terminal_settings_paths,
|
|
16
|
+
detect_terminal_theme as _detect_owl_theme,
|
|
17
|
+
)
|
|
18
|
+
from ...branding import env_get
|
|
19
|
+
from .cli_common import *
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def stripAnsi(value: str) -> str:
|
|
23
|
+
return re.sub(r"\x1b\[[0-9;]*m", "", value)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def visibleLength(value: str) -> int:
|
|
27
|
+
return len(stripAnsi(value))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def padLine(content: str, width: int) -> str:
|
|
31
|
+
current = visibleLength(content)
|
|
32
|
+
if current >= width:
|
|
33
|
+
return content
|
|
34
|
+
return content + (" " * (width - current))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _clip_visible_line(content: str, width: int) -> str:
|
|
38
|
+
if width <= 0:
|
|
39
|
+
return ""
|
|
40
|
+
if visibleLength(content) <= width:
|
|
41
|
+
return content
|
|
42
|
+
cells = _ansi_visible_cells(content)
|
|
43
|
+
clipped = "".join(cells[:width])
|
|
44
|
+
if "\x1b[" in clipped:
|
|
45
|
+
clipped += "\x1b[0m"
|
|
46
|
+
return clipped
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _welcome_stream(console: Console | None) -> Any | None:
|
|
50
|
+
if console is None:
|
|
51
|
+
return None
|
|
52
|
+
return console.file if getattr(console, "file", None) is not None else sys.stdout
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _welcome_stream_is_tty(stream: Any | None) -> bool:
|
|
56
|
+
isatty = getattr(stream, "isatty", None)
|
|
57
|
+
if not callable(isatty):
|
|
58
|
+
return False
|
|
59
|
+
try:
|
|
60
|
+
return bool(isatty())
|
|
61
|
+
except Exception:
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _truthy_env(name: str) -> bool:
|
|
66
|
+
return str(env_get(name) or "").strip().lower() in {"1", "true", "yes", "on"}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _owl_assets_dir() -> Path:
|
|
70
|
+
return Path(__file__).resolve().parents[2] / "assets" / "owl"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _should_animate_owl_logo(stream: Any | None) -> bool:
|
|
74
|
+
if _truthy_env("ALYSIS_NO_OWL") or env_get("ALYSIS_NO_INTRO"):
|
|
75
|
+
return False
|
|
76
|
+
if os.environ.get("CI") or _truthy_env("ALYSIS_CI"):
|
|
77
|
+
return False
|
|
78
|
+
if os.environ.get("PYTEST_CURRENT_TEST"):
|
|
79
|
+
return False
|
|
80
|
+
if not _welcome_stream_is_tty(stream):
|
|
81
|
+
return False
|
|
82
|
+
stdout = getattr(sys, "stdout", None)
|
|
83
|
+
if stream is not stdout:
|
|
84
|
+
return False
|
|
85
|
+
if not _welcome_stream_is_tty(getattr(sys, "stdin", None)):
|
|
86
|
+
return False
|
|
87
|
+
return _welcome_stream_is_tty(stdout)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _load_owl_logo_frames(
|
|
91
|
+
*,
|
|
92
|
+
stream: Any | None,
|
|
93
|
+
color_enabled: bool,
|
|
94
|
+
theme: str | None = None,
|
|
95
|
+
) -> list[list[str]]:
|
|
96
|
+
if _truthy_env("ALYSIS_NO_OWL"):
|
|
97
|
+
return []
|
|
98
|
+
theme = theme or _detect_owl_theme(stream)
|
|
99
|
+
frame_dir_name = "ascii"
|
|
100
|
+
frame_dir = _owl_assets_dir() / frame_dir_name
|
|
101
|
+
try:
|
|
102
|
+
frames = [
|
|
103
|
+
path.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
104
|
+
for path in sorted(frame_dir.glob("f-*.txt"))
|
|
105
|
+
]
|
|
106
|
+
except Exception:
|
|
107
|
+
frames = []
|
|
108
|
+
if not frames:
|
|
109
|
+
return []
|
|
110
|
+
if not color_enabled or theme == "neutral":
|
|
111
|
+
# With no reliable background signal, use the terminal's default
|
|
112
|
+
# foreground. Terminal profiles are expected to keep that readable on
|
|
113
|
+
# their own background; retaining the asset's fixed gray ramp would
|
|
114
|
+
# instead make the owl disappear on one side of the light/dark divide.
|
|
115
|
+
return [[stripAnsi(line) for line in frame] for frame in frames]
|
|
116
|
+
return frames
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _paint_owl_light_panel(frames: list[list[str]]) -> list[list[str]]:
|
|
120
|
+
if not frames:
|
|
121
|
+
return frames
|
|
122
|
+
bg = "\x1b[48;5;231m"
|
|
123
|
+
reset = "\x1b[0m"
|
|
124
|
+
horizontal_padding = 2
|
|
125
|
+
# Runtime-only contrast remap for light owl grays on the white panel.
|
|
126
|
+
panel_gray_map = {
|
|
127
|
+
"238": "16",
|
|
128
|
+
"239": "16",
|
|
129
|
+
"240": "16",
|
|
130
|
+
"241": "234",
|
|
131
|
+
"242": "234",
|
|
132
|
+
"243": "234",
|
|
133
|
+
"244": "236",
|
|
134
|
+
"245": "236",
|
|
135
|
+
"246": "236",
|
|
136
|
+
"247": "238",
|
|
137
|
+
"248": "238",
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
def remap_panel_grays(row: str) -> str:
|
|
141
|
+
return re.sub(
|
|
142
|
+
r"\x1b\[38;5;(238|239|240|241|242|243|244|245|246|247|248)m",
|
|
143
|
+
lambda match: f"\x1b[38;5;{panel_gray_map[match.group(1)]}m",
|
|
144
|
+
row,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
width = max((visibleLength(row) for frame in frames for row in frame), default=0)
|
|
148
|
+
if width <= 0:
|
|
149
|
+
return frames
|
|
150
|
+
side_pad = " " * horizontal_padding
|
|
151
|
+
blank = f"{bg}{' ' * (width + (horizontal_padding * 2))}{reset}"
|
|
152
|
+
painted_frames: list[list[str]] = []
|
|
153
|
+
for frame in frames:
|
|
154
|
+
painted = [blank]
|
|
155
|
+
for row in frame:
|
|
156
|
+
contrast_row = remap_panel_grays(row)
|
|
157
|
+
safe_row = contrast_row.replace(reset, f"{reset}{bg}")
|
|
158
|
+
painted.append(f"{bg}{side_pad}{padLine(safe_row, width)}{side_pad}{reset}")
|
|
159
|
+
painted.append(blank)
|
|
160
|
+
painted_frames.append(painted)
|
|
161
|
+
return painted_frames
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _ansi_visible_cells(line: str) -> list[str]:
|
|
165
|
+
cells: list[str] = []
|
|
166
|
+
active_style = ""
|
|
167
|
+
index = 0
|
|
168
|
+
for match in re.finditer(r"\x1b\[[0-9;]*m", line):
|
|
169
|
+
for char in line[index : match.start()]:
|
|
170
|
+
cells.append(f"{active_style}{char}" if active_style else char)
|
|
171
|
+
token = match.group(0)
|
|
172
|
+
code = match.group(0)[2:-1]
|
|
173
|
+
active_style = "" if code in {"0", "39"} else token
|
|
174
|
+
index = match.end()
|
|
175
|
+
for char in line[index:]:
|
|
176
|
+
cells.append(f"{active_style}{char}" if active_style else char)
|
|
177
|
+
return cells
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _crop_owl_logo_frames(frames: list[list[str]]) -> list[list[str]]:
|
|
181
|
+
if not frames:
|
|
182
|
+
return frames
|
|
183
|
+
plain_frames = [[stripAnsi(line) for line in frame] for frame in frames]
|
|
184
|
+
height = max((len(frame) for frame in plain_frames), default=0)
|
|
185
|
+
width = max((len(line) for frame in plain_frames for line in frame), default=0)
|
|
186
|
+
if height <= 0 or width <= 0:
|
|
187
|
+
return frames
|
|
188
|
+
|
|
189
|
+
occupied: list[tuple[int, int]] = []
|
|
190
|
+
for frame in plain_frames:
|
|
191
|
+
for row_index in range(height):
|
|
192
|
+
line = frame[row_index] if row_index < len(frame) else ""
|
|
193
|
+
for col_index, char in enumerate(line.ljust(width)):
|
|
194
|
+
if char != " ":
|
|
195
|
+
occupied.append((row_index, col_index))
|
|
196
|
+
if not occupied:
|
|
197
|
+
return frames
|
|
198
|
+
|
|
199
|
+
top = min(row for row, _ in occupied)
|
|
200
|
+
bottom = max(row for row, _ in occupied) + 1
|
|
201
|
+
left = min(col for _, col in occupied)
|
|
202
|
+
right = max(col for _, col in occupied) + 1
|
|
203
|
+
|
|
204
|
+
cropped_frames: list[list[str]] = []
|
|
205
|
+
for frame in frames:
|
|
206
|
+
cropped_frame: list[str] = []
|
|
207
|
+
for row_index in range(top, bottom):
|
|
208
|
+
line = frame[row_index] if row_index < len(frame) else ""
|
|
209
|
+
cells = _ansi_visible_cells(line)
|
|
210
|
+
cells.extend(" " for _ in range(max(width - len(cells), 0)))
|
|
211
|
+
row = "".join(cells[left:right])
|
|
212
|
+
if "\x1b[" in row:
|
|
213
|
+
row += "\x1b[0m"
|
|
214
|
+
cropped_frame.append(row)
|
|
215
|
+
cropped_frames.append(cropped_frame)
|
|
216
|
+
return cropped_frames
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _welcome_style(value: str, code: str, *, enabled: bool) -> str:
|
|
220
|
+
if not enabled or not code:
|
|
221
|
+
return value
|
|
222
|
+
return f"\x1b[{code}m{value}\x1b[0m"
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _welcome_palette(theme: str) -> dict[str, str]:
|
|
226
|
+
if theme == "dark":
|
|
227
|
+
return {
|
|
228
|
+
"primary": "1;97",
|
|
229
|
+
"muted": "97",
|
|
230
|
+
"label": "97",
|
|
231
|
+
"value": "97",
|
|
232
|
+
"rule": "97",
|
|
233
|
+
}
|
|
234
|
+
if theme == "light":
|
|
235
|
+
return {
|
|
236
|
+
"primary": "1;30",
|
|
237
|
+
"muted": "30",
|
|
238
|
+
"label": "30",
|
|
239
|
+
"value": "30",
|
|
240
|
+
"rule": "30",
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
"primary": "1",
|
|
244
|
+
"muted": "",
|
|
245
|
+
"label": "",
|
|
246
|
+
"value": "",
|
|
247
|
+
"rule": "",
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _welcome_workspace_value(workspace: str | os.PathLike[str] | None) -> str:
|
|
252
|
+
if workspace is None:
|
|
253
|
+
return ""
|
|
254
|
+
raw = os.fspath(workspace).strip()
|
|
255
|
+
if not raw:
|
|
256
|
+
return ""
|
|
257
|
+
try:
|
|
258
|
+
resolved = Path(raw).expanduser().resolve()
|
|
259
|
+
home = Path.home().resolve()
|
|
260
|
+
try:
|
|
261
|
+
rel_home = resolved.relative_to(home)
|
|
262
|
+
display = "~" if os.fspath(rel_home) == "." else f"~/{rel_home.as_posix()}"
|
|
263
|
+
except ValueError:
|
|
264
|
+
display = os.fspath(resolved)
|
|
265
|
+
except Exception:
|
|
266
|
+
display = raw
|
|
267
|
+
if len(display) > 30:
|
|
268
|
+
basename = Path(display).name
|
|
269
|
+
if basename:
|
|
270
|
+
return basename
|
|
271
|
+
return display
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _welcome_model_value(model: str | None) -> str:
|
|
275
|
+
clean = str(model or "").strip()
|
|
276
|
+
if not clean:
|
|
277
|
+
return ""
|
|
278
|
+
if "/" in clean:
|
|
279
|
+
clean = clean.rsplit("/", 1)[-1].strip()
|
|
280
|
+
return clean
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _welcome_context_line(
|
|
284
|
+
*,
|
|
285
|
+
workspace: str | os.PathLike[str] | None,
|
|
286
|
+
model: str | None,
|
|
287
|
+
version: str | None,
|
|
288
|
+
color_enabled: bool,
|
|
289
|
+
palette: dict[str, str],
|
|
290
|
+
) -> str:
|
|
291
|
+
def label_text(value: str) -> str:
|
|
292
|
+
return _welcome_style(value, palette["label"], enabled=color_enabled)
|
|
293
|
+
|
|
294
|
+
def value_text(value: str) -> str:
|
|
295
|
+
return _welcome_style(value, palette["value"], enabled=color_enabled)
|
|
296
|
+
|
|
297
|
+
parts: list[str] = []
|
|
298
|
+
workspace_value = _welcome_workspace_value(workspace)
|
|
299
|
+
model_value = _welcome_model_value(model)
|
|
300
|
+
version_value = str(version or "").strip()
|
|
301
|
+
if workspace_value:
|
|
302
|
+
parts.append(f"{label_text('workspace')} {value_text(workspace_value)}")
|
|
303
|
+
if model_value:
|
|
304
|
+
parts.append(f"{label_text('model')} {value_text(model_value)}")
|
|
305
|
+
if version_value:
|
|
306
|
+
parts.append(f"{label_text('version')} {value_text(version_value)}")
|
|
307
|
+
return " ".join(parts)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _welcome_context_lines(
|
|
311
|
+
*,
|
|
312
|
+
workspace: str | os.PathLike[str] | None,
|
|
313
|
+
model: str | None,
|
|
314
|
+
version: str | None,
|
|
315
|
+
color_enabled: bool,
|
|
316
|
+
palette: dict[str, str],
|
|
317
|
+
max_width: int,
|
|
318
|
+
) -> list[str]:
|
|
319
|
+
line = _welcome_context_line(
|
|
320
|
+
workspace=workspace,
|
|
321
|
+
model=model,
|
|
322
|
+
version=version,
|
|
323
|
+
color_enabled=color_enabled,
|
|
324
|
+
palette=palette,
|
|
325
|
+
)
|
|
326
|
+
if not line:
|
|
327
|
+
return []
|
|
328
|
+
if visibleLength(line) <= max_width:
|
|
329
|
+
return [line]
|
|
330
|
+
|
|
331
|
+
workspace_line = _welcome_context_line(
|
|
332
|
+
workspace=workspace,
|
|
333
|
+
model=None,
|
|
334
|
+
version=None,
|
|
335
|
+
color_enabled=color_enabled,
|
|
336
|
+
palette=palette,
|
|
337
|
+
)
|
|
338
|
+
model_version_line = _welcome_context_line(
|
|
339
|
+
workspace=None,
|
|
340
|
+
model=model,
|
|
341
|
+
version=version,
|
|
342
|
+
color_enabled=color_enabled,
|
|
343
|
+
palette=palette,
|
|
344
|
+
)
|
|
345
|
+
if model_version_line and visibleLength(model_version_line) > max_width:
|
|
346
|
+
model_line = _welcome_context_line(
|
|
347
|
+
workspace=None,
|
|
348
|
+
model=model,
|
|
349
|
+
version=None,
|
|
350
|
+
color_enabled=color_enabled,
|
|
351
|
+
palette=palette,
|
|
352
|
+
)
|
|
353
|
+
version_line = _welcome_context_line(
|
|
354
|
+
workspace=None,
|
|
355
|
+
model=None,
|
|
356
|
+
version=version,
|
|
357
|
+
color_enabled=color_enabled,
|
|
358
|
+
palette=palette,
|
|
359
|
+
)
|
|
360
|
+
return [line for line in (workspace_line, model_line, version_line) if line]
|
|
361
|
+
return [line for line in (workspace_line, model_version_line) if line]
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def printWelcome(
|
|
365
|
+
console: Console | None = None,
|
|
366
|
+
*,
|
|
367
|
+
workspace: str | os.PathLike[str] | None = None,
|
|
368
|
+
model: str | None = None,
|
|
369
|
+
version: str | None = __version__,
|
|
370
|
+
) -> str:
|
|
371
|
+
stream = _welcome_stream(console)
|
|
372
|
+
terminal_width = shutil.get_terminal_size((80, 20)).columns
|
|
373
|
+
safe_line_width = max(1, terminal_width - 1)
|
|
374
|
+
no_color = bool(os.environ.get("NO_COLOR"))
|
|
375
|
+
color_enabled = not no_color and (stream is None or _welcome_stream_is_tty(stream))
|
|
376
|
+
indent = " "
|
|
377
|
+
gap = " "
|
|
378
|
+
welcome_theme = _detect_owl_theme(stream)
|
|
379
|
+
palette = _welcome_palette(welcome_theme)
|
|
380
|
+
owl_frames = _load_owl_logo_frames(
|
|
381
|
+
stream=stream,
|
|
382
|
+
color_enabled=color_enabled,
|
|
383
|
+
theme=welcome_theme,
|
|
384
|
+
)
|
|
385
|
+
owl_frames = _crop_owl_logo_frames(owl_frames)
|
|
386
|
+
if welcome_theme == "dark" and color_enabled:
|
|
387
|
+
owl_frames = _paint_owl_light_panel(owl_frames)
|
|
388
|
+
fallback_icon_frames = [
|
|
389
|
+
[
|
|
390
|
+
" ◇ ",
|
|
391
|
+
" ╱ ╲ ",
|
|
392
|
+
" ◇ ◇ ",
|
|
393
|
+
" ╲ ╱ ",
|
|
394
|
+
" ◇ ",
|
|
395
|
+
]
|
|
396
|
+
]
|
|
397
|
+
logo_frames = owl_frames or fallback_icon_frames
|
|
398
|
+
logo_height = max((len(frame) for frame in logo_frames), default=0)
|
|
399
|
+
logo_width = max(
|
|
400
|
+
(visibleLength(row) for frame in logo_frames for row in frame),
|
|
401
|
+
default=0,
|
|
402
|
+
)
|
|
403
|
+
logo_frames = [
|
|
404
|
+
[padLine(row, logo_width) for row in frame]
|
|
405
|
+
+ [(" " * logo_width) for _ in range(max(logo_height - len(frame), 0))]
|
|
406
|
+
for frame in logo_frames
|
|
407
|
+
]
|
|
408
|
+
logo = logo_frames[0]
|
|
409
|
+
if terminal_width <= logo_width + visibleLength(indent):
|
|
410
|
+
indent = ""
|
|
411
|
+
narrow = safe_line_width < visibleLength(indent) + logo_width + visibleLength(gap) + 31
|
|
412
|
+
|
|
413
|
+
def primary_text(value: str) -> str:
|
|
414
|
+
return _welcome_style(value, palette["primary"], enabled=color_enabled)
|
|
415
|
+
|
|
416
|
+
def rule_text(value: str) -> str:
|
|
417
|
+
return _welcome_style(value, palette["rule"], enabled=color_enabled)
|
|
418
|
+
|
|
419
|
+
def body_text(value: str) -> str:
|
|
420
|
+
return _welcome_style(value, palette["muted"], enabled=color_enabled)
|
|
421
|
+
|
|
422
|
+
brand = f"{primary_text('Alysis Code')}{rule_text(' · ')}{primary_text('AlysisAI')}"
|
|
423
|
+
tagline = body_text("The autonomous coding agent")
|
|
424
|
+
|
|
425
|
+
def welcome_detail_lines(max_width: int) -> list[str]:
|
|
426
|
+
context_lines = _welcome_context_lines(
|
|
427
|
+
workspace=workspace,
|
|
428
|
+
model=model,
|
|
429
|
+
version=version,
|
|
430
|
+
color_enabled=color_enabled,
|
|
431
|
+
palette=palette,
|
|
432
|
+
max_width=max_width,
|
|
433
|
+
)
|
|
434
|
+
detail = [
|
|
435
|
+
brand,
|
|
436
|
+
tagline,
|
|
437
|
+
"",
|
|
438
|
+
*context_lines,
|
|
439
|
+
rule_text("─" * min(max(max_width, 1), 64)),
|
|
440
|
+
"",
|
|
441
|
+
f"{primary_text('/forge')} {body_text('begin an autonomous run')}",
|
|
442
|
+
f"{primary_text('/status')} {body_text('view run state and usage')}",
|
|
443
|
+
f"{primary_text('/help')} {body_text('show all commands')}",
|
|
444
|
+
]
|
|
445
|
+
return detail
|
|
446
|
+
|
|
447
|
+
def welcome_lines_for_logo(logo_frame: list[str]) -> list[str]:
|
|
448
|
+
if narrow:
|
|
449
|
+
return [
|
|
450
|
+
*(f"{indent}{row.rstrip()}" for row in logo_frame),
|
|
451
|
+
"",
|
|
452
|
+
f"{indent}{brand}",
|
|
453
|
+
f"{indent}{tagline}",
|
|
454
|
+
"",
|
|
455
|
+
f"{indent}{primary_text('/forge')} {primary_text('/status')} {primary_text('/help')}",
|
|
456
|
+
]
|
|
457
|
+
|
|
458
|
+
if owl_frames:
|
|
459
|
+
right_width = max(
|
|
460
|
+
safe_line_width - visibleLength(indent) - logo_width - visibleLength(gap),
|
|
461
|
+
1,
|
|
462
|
+
)
|
|
463
|
+
detail_lines = welcome_detail_lines(right_width)
|
|
464
|
+
required_side_by_side_width = visibleLength(indent) + logo_width + visibleLength(gap)
|
|
465
|
+
required_side_by_side_width += max(
|
|
466
|
+
(visibleLength(line) for line in detail_lines),
|
|
467
|
+
default=0,
|
|
468
|
+
)
|
|
469
|
+
if safe_line_width >= required_side_by_side_width:
|
|
470
|
+
right_start = max((len(logo_frame) - len(detail_lines)) // 2, 0)
|
|
471
|
+
rendered_lines = []
|
|
472
|
+
total_rows = max(len(logo_frame), right_start + len(detail_lines))
|
|
473
|
+
for row_index in range(total_rows):
|
|
474
|
+
row = logo_frame[row_index] if row_index < len(logo_frame) else ""
|
|
475
|
+
line = f"{indent}{padLine(row, logo_width)}"
|
|
476
|
+
right_index = row_index - right_start
|
|
477
|
+
if 0 <= right_index < len(detail_lines):
|
|
478
|
+
line = f"{line}{gap}{detail_lines[right_index]}"
|
|
479
|
+
rendered_lines.append(_clip_visible_line(line.rstrip(), safe_line_width))
|
|
480
|
+
return rendered_lines
|
|
481
|
+
else:
|
|
482
|
+
rendered_lines = []
|
|
483
|
+
rendered_lines.extend(
|
|
484
|
+
_clip_visible_line(f"{indent}{row.rstrip()}".rstrip(), safe_line_width)
|
|
485
|
+
for row in logo_frame
|
|
486
|
+
)
|
|
487
|
+
rendered_lines.append("")
|
|
488
|
+
full_detail_width = max(safe_line_width - visibleLength(indent), 1)
|
|
489
|
+
full_detail_lines = welcome_detail_lines(full_detail_width)
|
|
490
|
+
rendered_lines.extend(
|
|
491
|
+
_clip_visible_line(f"{indent}{line}".rstrip(), safe_line_width)
|
|
492
|
+
for line in full_detail_lines
|
|
493
|
+
)
|
|
494
|
+
return rendered_lines
|
|
495
|
+
|
|
496
|
+
context_line = _welcome_context_line(
|
|
497
|
+
workspace=workspace,
|
|
498
|
+
model=model,
|
|
499
|
+
version=version,
|
|
500
|
+
color_enabled=color_enabled,
|
|
501
|
+
palette=palette,
|
|
502
|
+
)
|
|
503
|
+
rendered_lines = [
|
|
504
|
+
f"{indent}{logo_frame[0]}{gap}{brand}",
|
|
505
|
+
f"{indent}{logo_frame[1].rstrip()}",
|
|
506
|
+
f"{indent}{logo_frame[2]}{gap}{tagline}",
|
|
507
|
+
f"{indent}{logo_frame[3].rstrip()}",
|
|
508
|
+
f"{indent}{logo_frame[4].rstrip()}",
|
|
509
|
+
"",
|
|
510
|
+
]
|
|
511
|
+
if context_line:
|
|
512
|
+
rendered_lines.append(f"{indent}{context_line}")
|
|
513
|
+
rendered_lines.extend(
|
|
514
|
+
[
|
|
515
|
+
f"{indent}{rule_text('─' * min(max(terminal_width - 4, 1), 64))}",
|
|
516
|
+
"",
|
|
517
|
+
f"{indent}{primary_text('/forge')} {body_text('begin an autonomous run')}",
|
|
518
|
+
f"{indent}{primary_text('/status')} {body_text('view run state and usage')}",
|
|
519
|
+
f"{indent}{primary_text('/help')} {body_text('show all commands')}",
|
|
520
|
+
]
|
|
521
|
+
)
|
|
522
|
+
return rendered_lines
|
|
523
|
+
|
|
524
|
+
lines = welcome_lines_for_logo(logo)
|
|
525
|
+
lines = [_clip_visible_line(line, safe_line_width) for line in lines]
|
|
526
|
+
|
|
527
|
+
display_lines = ["", *lines, ""]
|
|
528
|
+
banner = "\n".join(display_lines)
|
|
529
|
+
if console is not None and stream is not None:
|
|
530
|
+
try:
|
|
531
|
+
should_animate = (
|
|
532
|
+
bool(owl_frames)
|
|
533
|
+
and _welcome_stream_is_tty(stream)
|
|
534
|
+
and not no_color
|
|
535
|
+
and _patchable("_should_animate_owl_logo", _should_animate_owl_logo)(stream)
|
|
536
|
+
)
|
|
537
|
+
if should_animate:
|
|
538
|
+
animation_frames = [
|
|
539
|
+
welcome_lines_for_logo(frame)
|
|
540
|
+
for frame in (logo_frames[0], *logo_frames[1:], logo_frames[0])
|
|
541
|
+
]
|
|
542
|
+
block_height = max(
|
|
543
|
+
(len(frame_lines) for frame_lines in animation_frames), default=0
|
|
544
|
+
)
|
|
545
|
+
block_height += 1
|
|
546
|
+
|
|
547
|
+
def write_animation_block(frame_lines: list[str]) -> None:
|
|
548
|
+
for row_index in range(block_height - 1):
|
|
549
|
+
line = frame_lines[row_index] if row_index < len(frame_lines) else ""
|
|
550
|
+
line = _clip_visible_line(line, safe_line_width)
|
|
551
|
+
stream.write(f"\r\x1b[2K{line}\n")
|
|
552
|
+
stream.write("\r\x1b[2K\n")
|
|
553
|
+
|
|
554
|
+
stream.write("\n")
|
|
555
|
+
write_animation_block(animation_frames[0])
|
|
556
|
+
stream.flush()
|
|
557
|
+
for frame_lines in animation_frames[1:]:
|
|
558
|
+
time.sleep(0.10)
|
|
559
|
+
stream.write(f"\x1b[{block_height}A\r")
|
|
560
|
+
write_animation_block(frame_lines)
|
|
561
|
+
stream.flush()
|
|
562
|
+
stream.flush()
|
|
563
|
+
else:
|
|
564
|
+
stream.write(banner + "\n")
|
|
565
|
+
stream.flush()
|
|
566
|
+
except Exception:
|
|
567
|
+
console.print(banner, markup=False, highlight=False, soft_wrap=True)
|
|
568
|
+
return banner
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _chat_session_info_line(*, workspace: Path, model: str, mode: str) -> str:
|
|
572
|
+
workspace_name = workspace.name or os.fspath(workspace)
|
|
573
|
+
parts = [workspace_name, str(model or "?").strip() or "?", str(mode or "?").strip() or "?"]
|
|
574
|
+
return " · ".join(parts)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def _render_command_sections_panel(
|
|
578
|
+
*,
|
|
579
|
+
title: str,
|
|
580
|
+
sections: list[tuple[str, list[tuple[str, str]]]],
|
|
581
|
+
) -> Panel:
|
|
582
|
+
if _patchable("_is_narrow_terminal", _is_narrow_terminal)():
|
|
583
|
+
lines: list[str] = []
|
|
584
|
+
for section_name, rows in sections:
|
|
585
|
+
lines.append(f"\n[bold]{section_name}[/bold]")
|
|
586
|
+
for cmd, desc in rows:
|
|
587
|
+
lines.append(f" {cmd} {desc}")
|
|
588
|
+
return _Panel("\n".join(lines).strip(), title=title, border_style="bright_black")
|
|
589
|
+
|
|
590
|
+
table = _Table(show_header=False, box=None, expand=True, padding=(0, 1), collapse_padding=True)
|
|
591
|
+
table.add_column("command", style=STYLE_EMPHASIS, no_wrap=True, ratio=2)
|
|
592
|
+
table.add_column("description", style="dim", no_wrap=False, ratio=5, overflow="fold")
|
|
593
|
+
for idx, (section_name, rows) in enumerate(sections):
|
|
594
|
+
table.add_row(f"[bold]{section_name}[/bold]", "")
|
|
595
|
+
for cmd, desc in rows:
|
|
596
|
+
table.add_row(cmd, desc)
|
|
597
|
+
if idx < len(sections) - 1:
|
|
598
|
+
table.add_row("", "")
|
|
599
|
+
return _Panel(table, title=title, border_style="bright_black")
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _forge_help_footer_lines() -> list[str]:
|
|
603
|
+
return [
|
|
604
|
+
"Plain /forge starts a fresh run for a new chat session.",
|
|
605
|
+
"/forge resume explicitly attaches to the current run pointer.",
|
|
606
|
+
"After /back, plain /forge resumes this chat session's run only in the same workspace.",
|
|
607
|
+
"Same-workspace re-entry keeps the same run id and tracks the chat's current focus.",
|
|
608
|
+
"Changing workspaces starts a fresh run instead of resuming prior session-local state.",
|
|
609
|
+
"Type freely to add requirements or talk to the planner.",
|
|
610
|
+
"Chat Plan Mode is unavailable.",
|
|
611
|
+
"In Forge, use:",
|
|
612
|
+
"/back",
|
|
613
|
+
"Forge plan commands:",
|
|
614
|
+
"/show",
|
|
615
|
+
"/plan markdown",
|
|
616
|
+
"/plan edit",
|
|
617
|
+
]
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def _is_forge_ui_mode(ui_mode: str) -> bool:
|
|
621
|
+
return str(ui_mode or "").strip().lower() == "forge"
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def _forge_commands_panel() -> Panel:
|
|
625
|
+
sections = _chat_command_sections(ui_mode="forge")
|
|
626
|
+
footer_lines = _forge_help_footer_lines()
|
|
627
|
+
if _patchable("_is_narrow_terminal", _is_narrow_terminal)():
|
|
628
|
+
lines: list[str] = []
|
|
629
|
+
for section_name, rows in sections:
|
|
630
|
+
lines.append(f"\n[bold]{section_name}[/bold]")
|
|
631
|
+
for cmd, desc in rows:
|
|
632
|
+
lines.append(f" {cmd} {desc}")
|
|
633
|
+
lines.append("")
|
|
634
|
+
for line in footer_lines:
|
|
635
|
+
lines.append(f"[dim]{line}[/dim]")
|
|
636
|
+
return _Panel("\n".join(lines).strip(), title="Forge Commands", border_style="bright_black")
|
|
637
|
+
|
|
638
|
+
table = _Table(show_header=False, box=None, expand=True, padding=(0, 1), collapse_padding=True)
|
|
639
|
+
table.add_column("command", style=STYLE_EMPHASIS, no_wrap=True, ratio=2)
|
|
640
|
+
table.add_column("description", style="dim", no_wrap=False, ratio=5, overflow="fold")
|
|
641
|
+
for idx, (section_name, rows) in enumerate(sections):
|
|
642
|
+
table.add_row(f"[bold]{section_name}[/bold]", "")
|
|
643
|
+
for cmd, desc in rows:
|
|
644
|
+
table.add_row(cmd, desc)
|
|
645
|
+
if idx < len(sections) - 1:
|
|
646
|
+
table.add_row("", "")
|
|
647
|
+
|
|
648
|
+
content = _table_grid(expand=True)
|
|
649
|
+
content.add_row(table)
|
|
650
|
+
content.add_row("")
|
|
651
|
+
for line in footer_lines:
|
|
652
|
+
content.add_row(_forge_bar_text(text=line, style="dim"))
|
|
653
|
+
return _Panel(content, title="Forge Commands", border_style="bright_black")
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def _chat_command_sections(*, ui_mode: str = "chat") -> list[tuple[str, list[tuple[str, str]]]]:
|
|
657
|
+
if _is_forge_ui_mode(ui_mode):
|
|
658
|
+
return [
|
|
659
|
+
(
|
|
660
|
+
"Forge",
|
|
661
|
+
[
|
|
662
|
+
("/help", "show Forge & chat commands"),
|
|
663
|
+
("/show", "show the current plan summary"),
|
|
664
|
+
("/plan markdown|md", "preview PLAN.md for the current run"),
|
|
665
|
+
("/plan edit", "edit plan.json and reload Forge state"),
|
|
666
|
+
("/execute plan", "save, validate, and run scoped planned tasks"),
|
|
667
|
+
("/goal <text>", "set project goal"),
|
|
668
|
+
(
|
|
669
|
+
"/task <title>",
|
|
670
|
+
"add a task; ambiguous/mutating work must name repo-relative file paths",
|
|
671
|
+
),
|
|
672
|
+
("/done", "save + validate the plan, then return to chat"),
|
|
673
|
+
("/back", "return to chat without that final save/validate step"),
|
|
674
|
+
],
|
|
675
|
+
),
|
|
676
|
+
(
|
|
677
|
+
"Session",
|
|
678
|
+
[
|
|
679
|
+
("/status", "session details"),
|
|
680
|
+
("/subagents", "open an active subagent run"),
|
|
681
|
+
("/terminals", "list/read/kill background processes"),
|
|
682
|
+
("/pwd", "show active workdir, focus dir, and workspace root"),
|
|
683
|
+
("/usage", "token count & cost; /usage hud on|off toggles HUD"),
|
|
684
|
+
("/mode", "change execution mode"),
|
|
685
|
+
("/persona", "switch persona (code, architect, ask, debug)"),
|
|
686
|
+
("/stream", "toggle streaming"),
|
|
687
|
+
("/trace", "reasoning detail (off/compact/full)"),
|
|
688
|
+
],
|
|
689
|
+
),
|
|
690
|
+
(
|
|
691
|
+
"Context",
|
|
692
|
+
[
|
|
693
|
+
("/context", "context window left"),
|
|
694
|
+
("/compact [focus]", "force compaction"),
|
|
695
|
+
("/resume [id]", "continue previous session"),
|
|
696
|
+
("/report [text]", "create feedback bundle + issue draft"),
|
|
697
|
+
("/feedback [text]", "alias for /report"),
|
|
698
|
+
],
|
|
699
|
+
),
|
|
700
|
+
(
|
|
701
|
+
"Tools & Config",
|
|
702
|
+
[
|
|
703
|
+
("/login", "choose Alysis Code or an AI subscription connection"),
|
|
704
|
+
(
|
|
705
|
+
"/skill",
|
|
706
|
+
"no args lists; <name> shows info; <name> <task> attaches",
|
|
707
|
+
),
|
|
708
|
+
("/image [path]", "add image (path, clipboard, Ctrl+Alt+V)"),
|
|
709
|
+
("/assets", "open assets for the active Forge run"),
|
|
710
|
+
("/config", "inline config menu; /config show|set|clear for model metadata"),
|
|
711
|
+
("/toolbar", "customize toolbar items"),
|
|
712
|
+
("/exit", "quit"),
|
|
713
|
+
],
|
|
714
|
+
),
|
|
715
|
+
]
|
|
716
|
+
|
|
717
|
+
return [
|
|
718
|
+
(
|
|
719
|
+
"Getting Started",
|
|
720
|
+
[
|
|
721
|
+
("/help", "commands & config"),
|
|
722
|
+
("/status", "session details"),
|
|
723
|
+
("/subagents", "open an active subagent run"),
|
|
724
|
+
("/terminals", "list/read/kill background processes"),
|
|
725
|
+
("/pwd", "show active workdir, focus dir, and workspace root"),
|
|
726
|
+
("/usage", "token count & cost; /usage hud on|off toggles HUD"),
|
|
727
|
+
],
|
|
728
|
+
),
|
|
729
|
+
(
|
|
730
|
+
"Execution",
|
|
731
|
+
[
|
|
732
|
+
("/mode", "change execution mode"),
|
|
733
|
+
("/persona", "switch persona (code, architect, ask, debug)"),
|
|
734
|
+
("/ask <question>", "one read-only turn; mode restored afterwards"),
|
|
735
|
+
(
|
|
736
|
+
"/plan <task>",
|
|
737
|
+
"default planning path: draft, review, approve, then execute; bare /plan shows usage",
|
|
738
|
+
),
|
|
739
|
+
(
|
|
740
|
+
"/plan mode",
|
|
741
|
+
"secondary persistent readonly planning overlay; it does not execute by itself",
|
|
742
|
+
),
|
|
743
|
+
(
|
|
744
|
+
"/plan approve",
|
|
745
|
+
"while Plan Mode is on, leave readonly planning and execute the stored draft",
|
|
746
|
+
),
|
|
747
|
+
("/trace", "reasoning detail (off/compact/full)"),
|
|
748
|
+
],
|
|
749
|
+
),
|
|
750
|
+
(
|
|
751
|
+
"Context",
|
|
752
|
+
[
|
|
753
|
+
("/context", "context window left"),
|
|
754
|
+
("/compact [focus]", "force compaction"),
|
|
755
|
+
("/clear", "wipe conversation (keeps session id + log; Ctrl+L clears terminal)"),
|
|
756
|
+
("/resume [id]", "continue previous session"),
|
|
757
|
+
("/report [text]", "create feedback bundle + issue draft"),
|
|
758
|
+
("/feedback [text]", "alias for /report"),
|
|
759
|
+
],
|
|
760
|
+
),
|
|
761
|
+
(
|
|
762
|
+
"Tools & Subagents",
|
|
763
|
+
[
|
|
764
|
+
(
|
|
765
|
+
"/skill",
|
|
766
|
+
"no args lists; <name> shows info; <name> <task> attaches",
|
|
767
|
+
),
|
|
768
|
+
("/image [path]", "add image (path, clipboard, Ctrl+Alt+V)"),
|
|
769
|
+
("/assets", "open assets for the current Forge run pointer"),
|
|
770
|
+
(
|
|
771
|
+
"/forge [resume]",
|
|
772
|
+
"fresh run by default; same-workspace re-entry resumes session-local state and tracks the current focus; resume explicitly loads the current pointer",
|
|
773
|
+
),
|
|
774
|
+
],
|
|
775
|
+
),
|
|
776
|
+
(
|
|
777
|
+
"Configuration",
|
|
778
|
+
[
|
|
779
|
+
("/login", "choose Alysis Code or an AI subscription connection"),
|
|
780
|
+
("/config", "inline config menu; /config show|set|clear for model metadata"),
|
|
781
|
+
("/toolbar", "customize toolbar items"),
|
|
782
|
+
("/exit", "quit"),
|
|
783
|
+
],
|
|
784
|
+
),
|
|
785
|
+
]
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def _chat_commands_panel(*, ui_mode: str = "chat") -> Panel:
|
|
789
|
+
if _is_forge_ui_mode(ui_mode):
|
|
790
|
+
return _forge_commands_panel()
|
|
791
|
+
title = "Forge Commands" if _is_forge_ui_mode(ui_mode) else "Commands"
|
|
792
|
+
return _render_command_sections_panel(
|
|
793
|
+
title=title,
|
|
794
|
+
sections=_chat_command_sections(ui_mode=ui_mode),
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def _chat_quick_commands_panel(*, ui_mode: str = "chat") -> Panel:
|
|
799
|
+
return _chat_commands_panel(ui_mode=ui_mode)
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
def _chat_visible_commands(*, ui_mode: str = "chat") -> list[str]:
|
|
803
|
+
if _is_forge_ui_mode(ui_mode):
|
|
804
|
+
return list(_FORGE_SUGGESTION_COMMANDS)
|
|
805
|
+
return list(_CHAT_GLOBAL_VISIBLE_COMMANDS)
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _chat_completer_commands(*, ui_mode: str = "chat") -> list[str]:
|
|
809
|
+
if _is_forge_ui_mode(ui_mode):
|
|
810
|
+
return list(_FORGE_COMPLETER_COMMANDS)
|
|
811
|
+
return _ordered_unique_strings(
|
|
812
|
+
_CHAT_GLOBAL_VISIBLE_COMMANDS
|
|
813
|
+
+ [
|
|
814
|
+
"/forge resume",
|
|
815
|
+
"/usage hud",
|
|
816
|
+
"/usage hud on",
|
|
817
|
+
"/usage hud off",
|
|
818
|
+
"/usage hud status",
|
|
819
|
+
"/terminals list",
|
|
820
|
+
"/terminals show",
|
|
821
|
+
"/terminals kill",
|
|
822
|
+
"/terminals help",
|
|
823
|
+
"/plan mode",
|
|
824
|
+
"/plan approve",
|
|
825
|
+
]
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def _suggest_chat_command(raw_command: str, *, ui_mode: str = "chat") -> str | None:
|
|
830
|
+
candidate = raw_command.strip().lower()
|
|
831
|
+
if not candidate.startswith("/"):
|
|
832
|
+
return None
|
|
833
|
+
if candidate in _CHAT_RETIRED_COMMANDS:
|
|
834
|
+
return None
|
|
835
|
+
if candidate in _CHAT_COMMANDS:
|
|
836
|
+
return None
|
|
837
|
+
matches = get_close_matches(candidate, _chat_visible_commands(ui_mode=ui_mode), n=1, cutoff=0.6)
|
|
838
|
+
return matches[0] if matches else None
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def _rebuild_session_tools_for_mode(*, session: Any, mode: str) -> None:
|
|
842
|
+
scheduler_holder: dict[str, Any] = {}
|
|
843
|
+
build_kwargs = _session_build_tools_kwargs(session=session, mode=mode)
|
|
844
|
+
build_kwargs["child_scheduler_sink"] = lambda scheduler: scheduler_holder.__setitem__(
|
|
845
|
+
"scheduler", scheduler
|
|
846
|
+
)
|
|
847
|
+
tools = build_tools(**build_kwargs)
|
|
848
|
+
previous_scheduler = getattr(session, "child_scheduler", None)
|
|
849
|
+
replacement_scheduler = scheduler_holder.get("scheduler")
|
|
850
|
+
lifecycle_listener = getattr(previous_scheduler, "lifecycle_listener", None)
|
|
851
|
+
set_lifecycle_listener = getattr(
|
|
852
|
+
replacement_scheduler,
|
|
853
|
+
"set_lifecycle_listener",
|
|
854
|
+
None,
|
|
855
|
+
)
|
|
856
|
+
if callable(set_lifecycle_listener) and callable(lifecycle_listener):
|
|
857
|
+
set_lifecycle_listener(lifecycle_listener)
|
|
858
|
+
session.tools = tools
|
|
859
|
+
session.tool_list = [tool.as_openai_tool() for tool in tools.values()]
|
|
860
|
+
session.child_scheduler = replacement_scheduler
|
|
861
|
+
if previous_scheduler is not None and previous_scheduler is not replacement_scheduler:
|
|
862
|
+
clear_lifecycle_listener = getattr(
|
|
863
|
+
previous_scheduler,
|
|
864
|
+
"set_lifecycle_listener",
|
|
865
|
+
None,
|
|
866
|
+
)
|
|
867
|
+
if callable(clear_lifecycle_listener):
|
|
868
|
+
clear_lifecycle_listener(None)
|
|
869
|
+
notify_replaced = getattr(session, "on_child_scheduler_replaced", None)
|
|
870
|
+
if callable(notify_replaced):
|
|
871
|
+
notify_replaced("settings applied")
|
|
872
|
+
shutdown = getattr(previous_scheduler, "shutdown", None)
|
|
873
|
+
if callable(shutdown):
|
|
874
|
+
shutdown(cancel_pending=True)
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
def _session_build_tools_kwargs(*, session: Any, mode: str) -> dict[str, Any]:
|
|
878
|
+
cfg = getattr(session, "cfg", None)
|
|
879
|
+
max_steps = int(
|
|
880
|
+
getattr(session, "max_steps", getattr(cfg, "max_steps", DEFAULT_CHAT_MAX_STEPS))
|
|
881
|
+
or DEFAULT_CHAT_MAX_STEPS
|
|
882
|
+
)
|
|
883
|
+
authoritative_verification_commands = getattr(
|
|
884
|
+
session,
|
|
885
|
+
"authoritative_verification_commands",
|
|
886
|
+
None,
|
|
887
|
+
)
|
|
888
|
+
verification_selection = _agent_loop_module()._session_verify_command_selection(session)
|
|
889
|
+
effective_verification_commands = getattr(
|
|
890
|
+
session,
|
|
891
|
+
"effective_verification_commands",
|
|
892
|
+
None,
|
|
893
|
+
)
|
|
894
|
+
return {
|
|
895
|
+
"root": Path(getattr(session, "root", Path("."))),
|
|
896
|
+
"console": getattr(session, "console", None),
|
|
897
|
+
"surface": getattr(session, "surface", None),
|
|
898
|
+
"store": session.store,
|
|
899
|
+
# Carried across tool rebuilds (/mode and plan-mode transitions):
|
|
900
|
+
# dropping it would silently stop tracking the verifier's process groups
|
|
901
|
+
# for the rest of the session.
|
|
902
|
+
"process_group_registry": getattr(session, "process_group_registry", None),
|
|
903
|
+
"mode": mode,
|
|
904
|
+
"yes": bool(getattr(session, "yes", False)),
|
|
905
|
+
"cfg": cfg,
|
|
906
|
+
# Carried across tool rebuilds so an active switch_mode coordination
|
|
907
|
+
# cell (and the tool itself) survives /mode and persona transitions.
|
|
908
|
+
"persona_switch_state": getattr(session, "persona_switch_state", None),
|
|
909
|
+
"api_key": str(getattr(session, "api_key", "") or "") or None,
|
|
910
|
+
"max_steps": max_steps,
|
|
911
|
+
"no_log": bool(getattr(session, "no_log", False)),
|
|
912
|
+
"usage_role": str(getattr(session, "usage_role", "main") or "main"),
|
|
913
|
+
"usage_summary": getattr(session, "usage_summary", None),
|
|
914
|
+
"model_registry": getattr(session, "model_registry", None),
|
|
915
|
+
"deny_write_prefixes": getattr(session, "deny_write_prefixes", None),
|
|
916
|
+
"allow_write_globs": getattr(session, "allow_write_globs", None),
|
|
917
|
+
"persona_allow_write_globs": getattr(session, "persona_allow_write_globs", None),
|
|
918
|
+
"non_interactive": bool(getattr(session, "non_interactive", False)),
|
|
919
|
+
"shell_runner": getattr(session, "shell_runner", None),
|
|
920
|
+
"terminal_manager": getattr(session, "terminal_manager", None),
|
|
921
|
+
# Carried across tool rebuilds for the same reason as the process-group
|
|
922
|
+
# registry: the rewrite guard is per-run and fires once per file, so
|
|
923
|
+
# dropping it here would silently stop warning for the rest of the
|
|
924
|
+
# session and would forget which files it had already warned about.
|
|
925
|
+
"edit_discipline": getattr(session, "edit_discipline", None),
|
|
926
|
+
# Carried for the same reason again: the registry is what remembers
|
|
927
|
+
# which durable services this run started, so dropping it would leave
|
|
928
|
+
# the finalization liveness check blind to every service started before
|
|
929
|
+
# the rebuild and silently stop reporting a dead one.
|
|
930
|
+
"persistent_service_registry": getattr(session, "persistent_service_registry", None),
|
|
931
|
+
"verification_enabled": bool(getattr(session, "verification_enabled", True)),
|
|
932
|
+
"authoritative_verification_commands": (
|
|
933
|
+
list(authoritative_verification_commands)
|
|
934
|
+
if authoritative_verification_commands is not None
|
|
935
|
+
else None
|
|
936
|
+
),
|
|
937
|
+
"effective_verification_commands": list(effective_verification_commands or []),
|
|
938
|
+
"verify_command_selection": verification_selection,
|
|
939
|
+
"get_verify_command_selection": lambda: (
|
|
940
|
+
_agent_loop_module()._session_verify_command_selection(session)
|
|
941
|
+
),
|
|
942
|
+
"one_shot_execution": bool(getattr(session, "one_shot_execution", False)),
|
|
943
|
+
"skills_enabled": bool(getattr(session, "skills_enabled", True)),
|
|
944
|
+
"skill_registry": getattr(session, "skill_registry", None),
|
|
945
|
+
"subagents_enabled": bool(getattr(session, "subagents_enabled", False)),
|
|
946
|
+
"subagent_depth": int(getattr(session, "subagent_depth", 0) or 0),
|
|
947
|
+
"subagent_registry": getattr(session, "subagent_registry", None),
|
|
948
|
+
"parent_steer_inbox": getattr(session, "steer_inbox", None),
|
|
949
|
+
"session_log_dir_override": getattr(session, "session_log_dir_override", None),
|
|
950
|
+
"step_budget_runtime": getattr(session, "step_budget_runtime", None),
|
|
951
|
+
"runtime_kind": getattr(session, "runtime_kind", "interactive_chat"),
|
|
952
|
+
"mcp_manager": getattr(session, "mcp_manager", None),
|
|
953
|
+
"custom_tool_session_state": getattr(session, "custom_tool_session_state", None),
|
|
954
|
+
"managed_browser_service": getattr(session, "managed_browser_service", None),
|
|
955
|
+
"managed_browser_owner_id": getattr(session, "managed_browser_owner_id", None),
|
|
956
|
+
"managed_browser_cancel_check": getattr(session, "managed_browser_cancel_check", None),
|
|
957
|
+
"get_active_workdir_relpath": lambda: resolve_session_active_workdir_relpath(session),
|
|
958
|
+
"set_active_workdir_callback": (
|
|
959
|
+
lambda raw_path, source: set_session_active_workdir(
|
|
960
|
+
session,
|
|
961
|
+
raw_path,
|
|
962
|
+
source=source,
|
|
963
|
+
)
|
|
964
|
+
),
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
def _chat_help_panel(*, ui_mode: str = "chat") -> Panel:
|
|
969
|
+
return _chat_commands_panel(ui_mode=ui_mode)
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
__all__ = [name for name in globals() if (not name.startswith("__") or name == "__version__")]
|