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,506 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
from .atomic_io import atomic_write_json, atomic_write_text
|
|
10
|
+
from .config import AppConfig
|
|
11
|
+
from .forge import RunPaths, ensure_execution_dirs, now_iso
|
|
12
|
+
from .knowledge_base import (
|
|
13
|
+
KnowledgeEntry,
|
|
14
|
+
KnowledgeIndexEntry,
|
|
15
|
+
is_effectively_open_status,
|
|
16
|
+
load_knowledge_index,
|
|
17
|
+
rebuild_knowledge_index,
|
|
18
|
+
write_issue_entry_for_task_id,
|
|
19
|
+
)
|
|
20
|
+
from .repo_scan import RepoScanResult
|
|
21
|
+
from .verify_gate import (
|
|
22
|
+
CONFIG_VERIFY_COMMANDS_GENERIC_PRESET_SOURCE,
|
|
23
|
+
ResolvedVerifyCommands,
|
|
24
|
+
VerifyError,
|
|
25
|
+
VerifyRunResult,
|
|
26
|
+
is_generic_fallback_verify_command_selection,
|
|
27
|
+
resolve_verify_command_selection,
|
|
28
|
+
run_task_verification,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
IntegrationVerifyMode = Literal["off", "warn", "strict"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class IntegrationGateError(RuntimeError):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class ResolvedIntegrationCommands:
|
|
40
|
+
commands: tuple[str, ...]
|
|
41
|
+
source: str
|
|
42
|
+
|
|
43
|
+
def to_payload(self) -> dict[str, object]:
|
|
44
|
+
return {
|
|
45
|
+
"source": self.source,
|
|
46
|
+
"commands": list(self.commands),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class IntegrationGateResult:
|
|
52
|
+
batch_index: int
|
|
53
|
+
batch_label: str
|
|
54
|
+
mode: IntegrationVerifyMode
|
|
55
|
+
command_source: str
|
|
56
|
+
commands: tuple[str, ...]
|
|
57
|
+
merged_task_ids: tuple[str, ...]
|
|
58
|
+
merged_paths: tuple[str, ...]
|
|
59
|
+
verify_result: VerifyRunResult
|
|
60
|
+
artifact_dir: Path
|
|
61
|
+
result_path: Path
|
|
62
|
+
commands_path: Path
|
|
63
|
+
stdout_path: Path
|
|
64
|
+
stderr_path: Path
|
|
65
|
+
summary_path: Path
|
|
66
|
+
verify_artifact_path: Path
|
|
67
|
+
phase: str = "post_merge"
|
|
68
|
+
verified_root: Path | None = None
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def passed(self) -> bool:
|
|
72
|
+
return self.verify_result.all_passed
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def summary(self) -> str:
|
|
76
|
+
return self.verify_result.summary
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def policy_outcome(self) -> str:
|
|
80
|
+
if self.passed:
|
|
81
|
+
return "passed"
|
|
82
|
+
if self.mode == "warn":
|
|
83
|
+
return "failed_tolerated_by_warn_policy"
|
|
84
|
+
return "failed_blocking"
|
|
85
|
+
|
|
86
|
+
def to_payload(self, *, root: Path) -> dict[str, object]:
|
|
87
|
+
verified_root = self.verified_root or root
|
|
88
|
+
return {
|
|
89
|
+
"schema_version": 1,
|
|
90
|
+
"generated_at": now_iso(),
|
|
91
|
+
"batch_index": self.batch_index,
|
|
92
|
+
"batch_label": self.batch_label,
|
|
93
|
+
"mode": self.mode,
|
|
94
|
+
"phase": self.phase,
|
|
95
|
+
"passed": self.passed,
|
|
96
|
+
"policy_outcome": self.policy_outcome,
|
|
97
|
+
"tolerated_failure": self.policy_outcome == "failed_tolerated_by_warn_policy",
|
|
98
|
+
"summary": self.summary,
|
|
99
|
+
"failure_category": self.verify_result.failure_category_value,
|
|
100
|
+
"command_source": self.command_source,
|
|
101
|
+
"commands": list(self.commands),
|
|
102
|
+
"merged_task_ids": list(self.merged_task_ids),
|
|
103
|
+
"merged_paths": list(self.merged_paths),
|
|
104
|
+
"verified_root": _display_path(root, verified_root),
|
|
105
|
+
"verify_artifact_path": _repo_rel(root, self.verify_artifact_path),
|
|
106
|
+
"commands_path": _repo_rel(root, self.commands_path),
|
|
107
|
+
"stdout_path": _repo_rel(root, self.stdout_path),
|
|
108
|
+
"stderr_path": _repo_rel(root, self.stderr_path),
|
|
109
|
+
"summary_path": _repo_rel(root, self.summary_path),
|
|
110
|
+
"result_path": _repo_rel(root, self.result_path),
|
|
111
|
+
"command_results": [
|
|
112
|
+
{
|
|
113
|
+
"command": item.command,
|
|
114
|
+
"effective_command": item.effective_command or item.command,
|
|
115
|
+
"exit_code": item.exit_code,
|
|
116
|
+
"ok": item.ok,
|
|
117
|
+
"real_execution": item.real_execution,
|
|
118
|
+
"non_execution_reason": item.non_execution_reason,
|
|
119
|
+
"fallback_used": item.fallback_used,
|
|
120
|
+
"fallback_reason": item.fallback_reason,
|
|
121
|
+
}
|
|
122
|
+
for item in self.verify_result.command_results
|
|
123
|
+
],
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def normalize_integration_verify_mode(mode: str) -> IntegrationVerifyMode:
|
|
128
|
+
value = mode.strip().lower()
|
|
129
|
+
if value not in {"off", "warn", "strict"}:
|
|
130
|
+
raise IntegrationGateError(
|
|
131
|
+
"Invalid integration verify mode. Use one of: off, warn, strict."
|
|
132
|
+
)
|
|
133
|
+
return value # type: ignore[return-value]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def resolve_integration_verify_mode(
|
|
137
|
+
*,
|
|
138
|
+
cfg: AppConfig,
|
|
139
|
+
integration_verify: str | None,
|
|
140
|
+
) -> IntegrationVerifyMode:
|
|
141
|
+
raw = integration_verify if integration_verify is not None else cfg.integration_verify_mode
|
|
142
|
+
return normalize_integration_verify_mode(raw)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def resolve_integration_verify_commands(
|
|
146
|
+
*,
|
|
147
|
+
cfg: AppConfig,
|
|
148
|
+
integration_verify_cmd: list[str] | None,
|
|
149
|
+
verify_cmd: list[str] | None = None,
|
|
150
|
+
root: Path | None = None,
|
|
151
|
+
repo_scan: RepoScanResult | None = None,
|
|
152
|
+
) -> ResolvedIntegrationCommands:
|
|
153
|
+
def _resolve_verify_selection(scan: RepoScanResult | None) -> ResolvedVerifyCommands:
|
|
154
|
+
try:
|
|
155
|
+
return resolve_verify_command_selection(
|
|
156
|
+
cfg=cfg,
|
|
157
|
+
verify_cmd=verify_cmd,
|
|
158
|
+
root=root,
|
|
159
|
+
repo_scan=scan,
|
|
160
|
+
)
|
|
161
|
+
except VerifyError as e:
|
|
162
|
+
if str(e) == "Configured verify_commands is empty.":
|
|
163
|
+
raise IntegrationGateError(
|
|
164
|
+
"No integration verify commands are configured. Set integration_verify_commands or verify_commands."
|
|
165
|
+
) from e
|
|
166
|
+
raise IntegrationGateError(str(e)) from e
|
|
167
|
+
|
|
168
|
+
if integration_verify_cmd:
|
|
169
|
+
commands = tuple(cmd.strip() for cmd in integration_verify_cmd if cmd.strip())
|
|
170
|
+
if not commands:
|
|
171
|
+
raise IntegrationGateError("--integration-verify-cmd values cannot be empty.")
|
|
172
|
+
return ResolvedIntegrationCommands(
|
|
173
|
+
commands=commands,
|
|
174
|
+
source="cli.integration_verify_cmd",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
configured_integration = tuple(
|
|
178
|
+
cmd.strip() for cmd in cfg.integration_verify_commands if cmd.strip()
|
|
179
|
+
)
|
|
180
|
+
if configured_integration:
|
|
181
|
+
return ResolvedIntegrationCommands(
|
|
182
|
+
commands=configured_integration,
|
|
183
|
+
source="config.integration_verify_commands",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
verify_resolution = _resolve_verify_selection(repo_scan)
|
|
187
|
+
|
|
188
|
+
raw_source = verify_resolution.source
|
|
189
|
+
source = {
|
|
190
|
+
"cli.verify_cmd": "cli.verify_cmd_fallback",
|
|
191
|
+
"config.verify_commands": "config.verify_commands_fallback",
|
|
192
|
+
"repo_scan.likely_test_commands": "repo_scan.likely_test_commands_fallback",
|
|
193
|
+
"config.verify_commands_fallback": "config.verify_commands_fallback",
|
|
194
|
+
CONFIG_VERIFY_COMMANDS_GENERIC_PRESET_SOURCE: (
|
|
195
|
+
"config.verify_commands_generic_preset_fallback"
|
|
196
|
+
),
|
|
197
|
+
}.get(raw_source, raw_source)
|
|
198
|
+
if is_generic_fallback_verify_command_selection(verify_resolution) and (
|
|
199
|
+
root is not None or repo_scan is not None
|
|
200
|
+
):
|
|
201
|
+
return ResolvedIntegrationCommands(
|
|
202
|
+
commands=(),
|
|
203
|
+
source="repo_scan.no_authoritative_commands_fallback",
|
|
204
|
+
)
|
|
205
|
+
return ResolvedIntegrationCommands(
|
|
206
|
+
commands=verify_resolution.commands,
|
|
207
|
+
source=source,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def run_integration_gate(
|
|
212
|
+
*,
|
|
213
|
+
paths: RunPaths,
|
|
214
|
+
cfg: AppConfig,
|
|
215
|
+
batch_index: int,
|
|
216
|
+
mode: IntegrationVerifyMode,
|
|
217
|
+
merged_task_ids: list[str],
|
|
218
|
+
merged_paths: list[str],
|
|
219
|
+
integration_verify_cmd: list[str] | None = None,
|
|
220
|
+
verify_cmd: list[str] | None = None,
|
|
221
|
+
root: Path | None = None,
|
|
222
|
+
repo_scan: RepoScanResult | None = None,
|
|
223
|
+
phase: str = "post_merge",
|
|
224
|
+
) -> IntegrationGateResult:
|
|
225
|
+
ensure_execution_dirs(paths)
|
|
226
|
+
resolved_root = (root or paths.root).resolve()
|
|
227
|
+
batch_label = f"batch_{batch_index:03d}"
|
|
228
|
+
artifact_dir = paths.execution_integration_dir / batch_label
|
|
229
|
+
artifact_dir.mkdir(parents=True, exist_ok=True)
|
|
230
|
+
command_resolution = resolve_integration_verify_commands(
|
|
231
|
+
cfg=cfg,
|
|
232
|
+
integration_verify_cmd=integration_verify_cmd,
|
|
233
|
+
verify_cmd=verify_cmd,
|
|
234
|
+
root=resolved_root,
|
|
235
|
+
repo_scan=repo_scan,
|
|
236
|
+
)
|
|
237
|
+
verify_artifact_path = artifact_dir / "verify.txt"
|
|
238
|
+
verify_result = run_task_verification(
|
|
239
|
+
root=resolved_root,
|
|
240
|
+
commands=list(command_resolution.commands),
|
|
241
|
+
artifact_path=verify_artifact_path,
|
|
242
|
+
cfg=cfg,
|
|
243
|
+
)
|
|
244
|
+
commands_path = artifact_dir / "commands.json"
|
|
245
|
+
stdout_path = artifact_dir / "stdout.txt"
|
|
246
|
+
stderr_path = artifact_dir / "stderr.txt"
|
|
247
|
+
summary_path = artifact_dir / "summary.md"
|
|
248
|
+
result_path = artifact_dir / "result.json"
|
|
249
|
+
atomic_write_json(
|
|
250
|
+
commands_path,
|
|
251
|
+
{
|
|
252
|
+
"schema_version": 1,
|
|
253
|
+
"generated_at": now_iso(),
|
|
254
|
+
"batch_index": batch_index,
|
|
255
|
+
"batch_label": batch_label,
|
|
256
|
+
"mode": mode,
|
|
257
|
+
"phase": phase,
|
|
258
|
+
"command_source": command_resolution.source,
|
|
259
|
+
"commands": list(command_resolution.commands),
|
|
260
|
+
"merged_task_ids": list(merged_task_ids),
|
|
261
|
+
"verified_root": _display_path(paths.root, resolved_root),
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
atomic_write_text(stdout_path, _render_command_stream(verify_result, stream_name="stdout"))
|
|
265
|
+
atomic_write_text(stderr_path, _render_command_stream(verify_result, stream_name="stderr"))
|
|
266
|
+
|
|
267
|
+
result = IntegrationGateResult(
|
|
268
|
+
batch_index=batch_index,
|
|
269
|
+
batch_label=batch_label,
|
|
270
|
+
mode=mode,
|
|
271
|
+
command_source=command_resolution.source,
|
|
272
|
+
commands=command_resolution.commands,
|
|
273
|
+
merged_task_ids=tuple(merged_task_ids),
|
|
274
|
+
merged_paths=tuple(_dedupe_preserve_order(merged_paths)),
|
|
275
|
+
verify_result=verify_result,
|
|
276
|
+
artifact_dir=artifact_dir,
|
|
277
|
+
result_path=result_path,
|
|
278
|
+
commands_path=commands_path,
|
|
279
|
+
stdout_path=stdout_path,
|
|
280
|
+
stderr_path=stderr_path,
|
|
281
|
+
summary_path=summary_path,
|
|
282
|
+
verify_artifact_path=verify_artifact_path,
|
|
283
|
+
phase=phase,
|
|
284
|
+
verified_root=resolved_root,
|
|
285
|
+
)
|
|
286
|
+
atomic_write_text(summary_path, _render_integration_summary(result=result, root=paths.root))
|
|
287
|
+
atomic_write_json(result_path, result.to_payload(root=paths.root))
|
|
288
|
+
return result
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def integration_issue_signature_for_commands(commands: tuple[str, ...] | list[str]) -> str:
|
|
292
|
+
normalized_commands = [
|
|
293
|
+
" ".join(str(command).strip().split()) for command in commands if str(command).strip()
|
|
294
|
+
]
|
|
295
|
+
payload = json.dumps(normalized_commands, ensure_ascii=False, separators=(",", ":"))
|
|
296
|
+
digest = hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]
|
|
297
|
+
return f"integration_gate_v1:{digest}"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def write_integration_failure_issue(
|
|
301
|
+
*,
|
|
302
|
+
paths: RunPaths,
|
|
303
|
+
result: IntegrationGateResult,
|
|
304
|
+
) -> KnowledgeEntry:
|
|
305
|
+
title = f"{result.batch_label}: integration verification failed"
|
|
306
|
+
summary = result.summary
|
|
307
|
+
issue = write_issue_entry_for_task_id(
|
|
308
|
+
paths=paths,
|
|
309
|
+
task_id=result.batch_label,
|
|
310
|
+
source="integration_gate",
|
|
311
|
+
title=title,
|
|
312
|
+
summary=summary,
|
|
313
|
+
paths_in_scope=list(result.merged_paths),
|
|
314
|
+
report_path=result.summary_path,
|
|
315
|
+
verify_artifact_path=result.verify_artifact_path,
|
|
316
|
+
related_tasks=list(result.merged_task_ids),
|
|
317
|
+
tags=["integration_gate", "integration_failure", result.mode],
|
|
318
|
+
status="open",
|
|
319
|
+
signature=integration_issue_signature_for_commands(result.commands),
|
|
320
|
+
)
|
|
321
|
+
return issue
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _matching_open_integration_issues(
|
|
325
|
+
*,
|
|
326
|
+
paths: RunPaths,
|
|
327
|
+
signature: str,
|
|
328
|
+
) -> tuple[KnowledgeIndexEntry, ...]:
|
|
329
|
+
index = load_knowledge_index(paths, rebuild=True)
|
|
330
|
+
matches = [
|
|
331
|
+
entry
|
|
332
|
+
for entry in index.entries
|
|
333
|
+
if entry.kind == "issue"
|
|
334
|
+
and entry.source == "integration_gate"
|
|
335
|
+
and entry.signature == signature
|
|
336
|
+
and is_effectively_open_status(entry.effective_status or entry.status)
|
|
337
|
+
]
|
|
338
|
+
return tuple(matches)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def write_integration_resolution_issue(
|
|
342
|
+
*,
|
|
343
|
+
paths: RunPaths,
|
|
344
|
+
result: IntegrationGateResult,
|
|
345
|
+
resolved_issue_ids: tuple[str, ...],
|
|
346
|
+
signature: str,
|
|
347
|
+
) -> KnowledgeEntry:
|
|
348
|
+
return write_issue_entry_for_task_id(
|
|
349
|
+
paths=paths,
|
|
350
|
+
task_id=result.batch_label,
|
|
351
|
+
source="integration_gate",
|
|
352
|
+
title=f"{result.batch_label}: integration verification passed",
|
|
353
|
+
summary=result.summary,
|
|
354
|
+
paths_in_scope=list(result.merged_paths),
|
|
355
|
+
report_path=result.summary_path,
|
|
356
|
+
verify_artifact_path=result.verify_artifact_path,
|
|
357
|
+
related_tasks=list(result.merged_task_ids),
|
|
358
|
+
tags=["integration_gate", "integration_resolution", result.mode],
|
|
359
|
+
status="resolved",
|
|
360
|
+
signature=signature,
|
|
361
|
+
resolves=list(resolved_issue_ids),
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def write_integration_issue_summary(paths: RunPaths) -> Path:
|
|
366
|
+
ensure_execution_dirs(paths)
|
|
367
|
+
index = load_knowledge_index(paths, rebuild=True)
|
|
368
|
+
open_issues = [
|
|
369
|
+
entry
|
|
370
|
+
for entry in index.entries
|
|
371
|
+
if entry.kind == "issue"
|
|
372
|
+
and entry.source == "integration_gate"
|
|
373
|
+
and is_effectively_open_status(entry.effective_status or entry.status)
|
|
374
|
+
]
|
|
375
|
+
lines = [
|
|
376
|
+
"# Integration Issues",
|
|
377
|
+
"",
|
|
378
|
+
f"- Generated At: `{now_iso()}`",
|
|
379
|
+
"",
|
|
380
|
+
]
|
|
381
|
+
if not open_issues:
|
|
382
|
+
lines.append("- No open integration issues.")
|
|
383
|
+
else:
|
|
384
|
+
for entry in open_issues:
|
|
385
|
+
line = f"- `{entry.id}` {entry.title}"
|
|
386
|
+
if entry.related_tasks:
|
|
387
|
+
line += f" related tasks: {', '.join(entry.related_tasks)}"
|
|
388
|
+
if entry.paths:
|
|
389
|
+
line += f" paths: {', '.join(entry.paths[:5])}"
|
|
390
|
+
line += f" file: `{entry.knowledge_file_path}`"
|
|
391
|
+
lines.append(line)
|
|
392
|
+
paths.execution_integration_issues_path.parent.mkdir(parents=True, exist_ok=True)
|
|
393
|
+
atomic_write_text(paths.execution_integration_issues_path, "\n".join(lines).rstrip() + "\n")
|
|
394
|
+
return paths.execution_integration_issues_path
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def record_integration_failure_knowledge(
|
|
398
|
+
*,
|
|
399
|
+
paths: RunPaths,
|
|
400
|
+
result: IntegrationGateResult,
|
|
401
|
+
) -> tuple[KnowledgeEntry, Path]:
|
|
402
|
+
issue = write_integration_failure_issue(paths=paths, result=result)
|
|
403
|
+
rebuild_knowledge_index(paths)
|
|
404
|
+
summary_path = write_integration_issue_summary(paths)
|
|
405
|
+
return issue, summary_path
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def record_integration_resolution_knowledge(
|
|
409
|
+
*,
|
|
410
|
+
paths: RunPaths,
|
|
411
|
+
result: IntegrationGateResult,
|
|
412
|
+
) -> tuple[KnowledgeEntry | None, Path]:
|
|
413
|
+
signature = integration_issue_signature_for_commands(result.commands)
|
|
414
|
+
open_issues = _matching_open_integration_issues(paths=paths, signature=signature)
|
|
415
|
+
resolution_entry: KnowledgeEntry | None = None
|
|
416
|
+
if open_issues:
|
|
417
|
+
resolution_entry = write_integration_resolution_issue(
|
|
418
|
+
paths=paths,
|
|
419
|
+
result=result,
|
|
420
|
+
resolved_issue_ids=tuple(entry.id for entry in open_issues),
|
|
421
|
+
signature=signature,
|
|
422
|
+
)
|
|
423
|
+
rebuild_knowledge_index(paths)
|
|
424
|
+
summary_path = write_integration_issue_summary(paths)
|
|
425
|
+
return resolution_entry, summary_path
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _dedupe_preserve_order(values: list[str]) -> list[str]:
|
|
429
|
+
seen: set[str] = set()
|
|
430
|
+
out: list[str] = []
|
|
431
|
+
for value in values:
|
|
432
|
+
item = str(value).strip()
|
|
433
|
+
if not item or item in seen:
|
|
434
|
+
continue
|
|
435
|
+
seen.add(item)
|
|
436
|
+
out.append(item)
|
|
437
|
+
return out
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _repo_rel(root: Path, path: Path) -> str:
|
|
441
|
+
return path.resolve().relative_to(root.resolve()).as_posix()
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _display_path(root: Path, path: Path) -> str:
|
|
445
|
+
try:
|
|
446
|
+
return _repo_rel(root, path)
|
|
447
|
+
except ValueError:
|
|
448
|
+
return path.resolve().as_posix()
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _render_command_stream(result: VerifyRunResult, *, stream_name: str) -> str:
|
|
452
|
+
lines = [f"# Integration {stream_name.title()}"]
|
|
453
|
+
for index, item in enumerate(result.command_results, start=1):
|
|
454
|
+
text = item.stdout if stream_name == "stdout" else item.stderr
|
|
455
|
+
lines.extend(
|
|
456
|
+
[
|
|
457
|
+
"",
|
|
458
|
+
f"## Command {index}",
|
|
459
|
+
f"requested_command: {item.command}",
|
|
460
|
+
f"effective_command: {item.effective_command or item.command}",
|
|
461
|
+
text.rstrip() or "(no output)",
|
|
462
|
+
]
|
|
463
|
+
)
|
|
464
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _render_integration_summary(*, result: IntegrationGateResult, root: Path) -> str:
|
|
468
|
+
verified_root = result.verified_root or root
|
|
469
|
+
if result.phase == "pre_merge_candidate":
|
|
470
|
+
phase_label = "pre-merge candidate"
|
|
471
|
+
task_heading = "Batch Tasks"
|
|
472
|
+
elif result.phase == "final_repo":
|
|
473
|
+
phase_label = "final repo"
|
|
474
|
+
task_heading = "Final Repo Tasks"
|
|
475
|
+
else:
|
|
476
|
+
phase_label = "post-merge base"
|
|
477
|
+
task_heading = "Merged Tasks"
|
|
478
|
+
lines = [
|
|
479
|
+
"# Integration Gate Summary",
|
|
480
|
+
"",
|
|
481
|
+
f"- Batch: `{result.batch_label}`",
|
|
482
|
+
f"- Phase: `{phase_label}`",
|
|
483
|
+
f"- Mode: `{result.mode}`",
|
|
484
|
+
f"- Passed: `{'yes' if result.passed else 'no'}`",
|
|
485
|
+
f"- Policy Outcome: `{result.policy_outcome}`",
|
|
486
|
+
f"- Summary: {result.summary}",
|
|
487
|
+
f"- Command Source: `{result.command_source}`",
|
|
488
|
+
f"- Verified Root: `{_display_path(root, verified_root)}`",
|
|
489
|
+
f"- Commands Artifact: `{_repo_rel(root, result.commands_path)}`",
|
|
490
|
+
f"- Verify Artifact: `{_repo_rel(root, result.verify_artifact_path)}`",
|
|
491
|
+
f"- Stdout Artifact: `{_repo_rel(root, result.stdout_path)}`",
|
|
492
|
+
f"- Stderr Artifact: `{_repo_rel(root, result.stderr_path)}`",
|
|
493
|
+
"",
|
|
494
|
+
f"## {task_heading}",
|
|
495
|
+
"",
|
|
496
|
+
]
|
|
497
|
+
if result.merged_task_ids:
|
|
498
|
+
lines.extend(f"- `{task_id}`" for task_id in result.merged_task_ids)
|
|
499
|
+
else:
|
|
500
|
+
lines.append("- (none)")
|
|
501
|
+
lines.extend(["", "## Commands", ""])
|
|
502
|
+
if result.commands:
|
|
503
|
+
lines.extend(f"- `{cmd}`" for cmd in result.commands)
|
|
504
|
+
else:
|
|
505
|
+
lines.append("- (none)")
|
|
506
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
|
|
6
|
+
_ACTIVE_PROMPT_DEPTH = 0
|
|
7
|
+
_ACTIVE_PROMPT_TERMINAL_OWNER_DEPTH = 0
|
|
8
|
+
_ACTIVE_PROMPT_LOCK = threading.Lock()
|
|
9
|
+
_ACTIVE_PROMPT_EVENT = threading.Event()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_interactive_prompt_active() -> bool:
|
|
13
|
+
return _ACTIVE_PROMPT_EVENT.is_set()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_interactive_prompt_terminal_owner() -> bool:
|
|
17
|
+
with _ACTIVE_PROMPT_LOCK:
|
|
18
|
+
return _ACTIVE_PROMPT_TERMINAL_OWNER_DEPTH > 0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@contextmanager
|
|
22
|
+
def interactive_prompt_guard(*, owns_terminal: bool = False) -> None:
|
|
23
|
+
global _ACTIVE_PROMPT_DEPTH, _ACTIVE_PROMPT_TERMINAL_OWNER_DEPTH
|
|
24
|
+
with _ACTIVE_PROMPT_LOCK:
|
|
25
|
+
_ACTIVE_PROMPT_DEPTH += 1
|
|
26
|
+
if owns_terminal:
|
|
27
|
+
_ACTIVE_PROMPT_TERMINAL_OWNER_DEPTH += 1
|
|
28
|
+
_ACTIVE_PROMPT_EVENT.set()
|
|
29
|
+
try:
|
|
30
|
+
yield
|
|
31
|
+
finally:
|
|
32
|
+
with _ACTIVE_PROMPT_LOCK:
|
|
33
|
+
_ACTIVE_PROMPT_DEPTH = max(0, _ACTIVE_PROMPT_DEPTH - 1)
|
|
34
|
+
if owns_terminal:
|
|
35
|
+
_ACTIVE_PROMPT_TERMINAL_OWNER_DEPTH = max(
|
|
36
|
+
0, _ACTIVE_PROMPT_TERMINAL_OWNER_DEPTH - 1
|
|
37
|
+
)
|
|
38
|
+
if _ACTIVE_PROMPT_DEPTH == 0:
|
|
39
|
+
_ACTIVE_PROMPT_EVENT.clear()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
INTERACTIVE_PLAN_MODE_SYSTEM_PROMPT = """You are handling one turn inside interactive chat Plan Mode.
|
|
4
|
+
|
|
5
|
+
Interactive chat Plan Mode is persistent readonly analysis/planning.
|
|
6
|
+
|
|
7
|
+
Hard constraints:
|
|
8
|
+
- Use prior conversation context first and stay grounded in the repository context that is already available to you.
|
|
9
|
+
- You may inspect repository context read-only if helpful.
|
|
10
|
+
- The host owns Plan Mode state transitions and execution gating.
|
|
11
|
+
- The normal plan -> approval -> execution flow in chat is /plan <task>; Plan Mode is the secondary readonly overlay.
|
|
12
|
+
- Do not execute the task.
|
|
13
|
+
- Do not write files.
|
|
14
|
+
- Do not run shell commands.
|
|
15
|
+
- Do not run verification commands.
|
|
16
|
+
- Do not claim changes were made.
|
|
17
|
+
- Do not imply that execution already started.
|
|
18
|
+
|
|
19
|
+
Response policy:
|
|
20
|
+
- If the user message is a concrete implementation request (for example a build/fix/change/refactor/add/remove/migrate/update request covering code, tests, docs, or configuration), do not execute it. Respond with a concise numbered implementation plan instead.
|
|
21
|
+
- If the user is asking to stop planning and start execution now (for example "do it", "go ahead", "implement it", or similar approval-style follow-ups), do not restate the plan. Tell them Plan Mode is still readonly, that the host controls approval/exit behavior, and that exact /plan approve or /plan off (or Esc at an empty prompt in interactive chat) must be used before execution can start.
|
|
22
|
+
- If the user message is a question, review request, discussion request, acknowledgement, pleasantry, or unclear ask, respond conversationally instead of switching into a formal plan.
|
|
23
|
+
- Ask at most one concise clarification question only when it is genuinely needed to move the discussion forward.
|
|
24
|
+
- Brief social acknowledgements or pleasantries should get a brief natural reply and should not cause you to restate the previous technical answer.
|
|
25
|
+
- When you provide a numbered implementation plan, keep it concise, actionable, and self-contained because the host may store the latest draft for exact /plan approve.
|
|
26
|
+
"""
|