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,895 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from .agent_loop import run_agent
|
|
12
|
+
from .branding import env_get_from
|
|
13
|
+
from .config import AppConfig, ConfigError, clone_cfg
|
|
14
|
+
from .execution_shared import (
|
|
15
|
+
build_task_execution_instruction_bundle,
|
|
16
|
+
mirror_plan_into_worktree,
|
|
17
|
+
mirror_selected_knowledge_into_worktree,
|
|
18
|
+
prepare_task_execution_knowledge,
|
|
19
|
+
resolve_managed_task_step_budget,
|
|
20
|
+
safe_task_file_component,
|
|
21
|
+
snapshot_runtime_tree,
|
|
22
|
+
snapshot_workspace_tree,
|
|
23
|
+
)
|
|
24
|
+
from .forge import RunPaths, now_iso
|
|
25
|
+
from .git_ops import (
|
|
26
|
+
DEFAULT_COMMIT_AUTHOR_EMAIL,
|
|
27
|
+
DEFAULT_COMMIT_AUTHOR_NAME,
|
|
28
|
+
GitOpsError,
|
|
29
|
+
delete_branch,
|
|
30
|
+
ensure_not_staged_prefixes,
|
|
31
|
+
ensure_not_staged_runtime_artifacts,
|
|
32
|
+
format_patch_stdout,
|
|
33
|
+
merge_no_ff,
|
|
34
|
+
stage_all,
|
|
35
|
+
staged_files,
|
|
36
|
+
unstage_staged_prefixes,
|
|
37
|
+
unstage_staged_runtime_artifacts,
|
|
38
|
+
)
|
|
39
|
+
from .git_safe import build_git_cmd
|
|
40
|
+
from .git_worktrees import ensure_task_worktree, remove_task_worktree
|
|
41
|
+
from .knowledge_base import rebuild_knowledge_index, write_issue_entry, write_task_attempt_entry
|
|
42
|
+
from .knowledge_capture import (
|
|
43
|
+
RecordingSurface,
|
|
44
|
+
mark_knowledge_capture_promotion_skipped,
|
|
45
|
+
persist_execution_knowledge_capture,
|
|
46
|
+
promote_validated_knowledge_capture,
|
|
47
|
+
)
|
|
48
|
+
from .merge_conflict_reviewer import list_unmerged_files, try_abort_merge
|
|
49
|
+
from .model_router import (
|
|
50
|
+
ROLE_CODING,
|
|
51
|
+
ROLE_CONFLICT_RESOLVE,
|
|
52
|
+
resolve_model_for_role,
|
|
53
|
+
)
|
|
54
|
+
from .runtime_artifacts import has_grounded_rust_target_runtime_artifacts
|
|
55
|
+
from .runtime_kind import RuntimeKind
|
|
56
|
+
from .surface import NoopSurface
|
|
57
|
+
from .surface.console import make_console
|
|
58
|
+
from .task_scope import list_changed_files_including_untracked
|
|
59
|
+
from .verify_gate import (
|
|
60
|
+
ResolvedVerifyCommands,
|
|
61
|
+
VerifyError,
|
|
62
|
+
resolve_task_aware_verify_command_selection,
|
|
63
|
+
resolve_verify_command_selection,
|
|
64
|
+
resolve_verify_commands,
|
|
65
|
+
run_task_verification,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
_VERIFY_MODES = {"off", "warn", "strict"}
|
|
69
|
+
|
|
70
|
+
CONFLICT_RESOLVER_SYSTEM_PROMPT = """You are CONFLICT_RESOLVER. You are an autonomous coding agent operating inside a dedicated conflict-resolution worktree.
|
|
71
|
+
|
|
72
|
+
Mission
|
|
73
|
+
- Resolve git merge conflicts safely and correctly.
|
|
74
|
+
- Resolve merge conflicts only. Do not implement unrelated features.
|
|
75
|
+
- Only modify files that are part of the merge conflict set / allowed scope.
|
|
76
|
+
- Ensure there are no conflict markers left and the repo is in a clean, merge-completable state.
|
|
77
|
+
|
|
78
|
+
Rules
|
|
79
|
+
- Start by inspecting git status and identifying unmerged files.
|
|
80
|
+
- Edit ONLY the conflicted files (and only within the allowed write scope).
|
|
81
|
+
- Remove all conflict markers (<<<<<<<, =======, >>>>>>>) and produce valid code.
|
|
82
|
+
- Prefer minimal changes: preserve intended behavior from both sides when possible.
|
|
83
|
+
- Do not refactor unrelated code.
|
|
84
|
+
- Do not modify .alysis/ or other denied prefixes unless explicitly instructed.
|
|
85
|
+
|
|
86
|
+
Tool usage
|
|
87
|
+
- Prefer search_rg plus fs_read_lines for focused conflict inspection; use fs_read when you need broader file context.
|
|
88
|
+
- Prefer fs_edit for deterministic localized edits in one existing conflicted file.
|
|
89
|
+
- Prefer git_apply_patch for broader or context-heavy conflict edits where unified diff context matters.
|
|
90
|
+
- After edits, search for remaining conflict markers.
|
|
91
|
+
- Use git_status to ensure no unmerged paths remain.
|
|
92
|
+
- If verification tools/commands are available in this run, use the most relevant targeted checks to ensure the resolution is sound.
|
|
93
|
+
|
|
94
|
+
Docs and tests
|
|
95
|
+
- If conflict resolution changes user-facing behavior, update README/docs.
|
|
96
|
+
- If the conflict touches logic likely to break, ensure tests are present and propose/execute relevant verification.
|
|
97
|
+
|
|
98
|
+
Final response
|
|
99
|
+
- Summarize which files were resolved and any remaining risks/assumptions.
|
|
100
|
+
- Report verification commands run (or proposed) and results.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dataclass(frozen=True)
|
|
105
|
+
class ConflictAutoResolveSettings:
|
|
106
|
+
enabled: bool = True
|
|
107
|
+
verify_mode: str = "strict"
|
|
108
|
+
max_attempts: int = 1
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass(frozen=True)
|
|
112
|
+
class AutoResolveOutcome:
|
|
113
|
+
success: bool
|
|
114
|
+
task_id: str
|
|
115
|
+
conflict_branch: str
|
|
116
|
+
worktree_repo_path: Path
|
|
117
|
+
result_json_path: Path
|
|
118
|
+
report_path: Path
|
|
119
|
+
patch_path: Path
|
|
120
|
+
merge_commit_hash: str | None
|
|
121
|
+
verify_summary: str | None
|
|
122
|
+
warnings: list[str]
|
|
123
|
+
error: str | None
|
|
124
|
+
agent_exit_code: int | None = None
|
|
125
|
+
salvaged_nonzero_exit: bool = False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _parse_bool(raw: object, *, default: bool) -> bool:
|
|
129
|
+
if raw is None:
|
|
130
|
+
return default
|
|
131
|
+
value = str(raw).strip().lower()
|
|
132
|
+
if value in {"1", "true", "yes", "on"}:
|
|
133
|
+
return True
|
|
134
|
+
if value in {"0", "false", "no", "off"}:
|
|
135
|
+
return False
|
|
136
|
+
return default
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _parse_verify_mode(raw: object, *, default: str) -> str:
|
|
140
|
+
value = str(raw).strip().lower() if raw is not None else default
|
|
141
|
+
if value in _VERIFY_MODES:
|
|
142
|
+
return value
|
|
143
|
+
return default
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _parse_positive_int(raw: object, *, default: int) -> int:
|
|
147
|
+
try:
|
|
148
|
+
parsed = int(str(raw).strip())
|
|
149
|
+
except (TypeError, ValueError):
|
|
150
|
+
return default
|
|
151
|
+
if parsed <= 0:
|
|
152
|
+
return default
|
|
153
|
+
return parsed
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def load_conflict_auto_resolve_settings(
|
|
157
|
+
*,
|
|
158
|
+
cfg: AppConfig,
|
|
159
|
+
env: Mapping[str, str] | None = None,
|
|
160
|
+
) -> ConflictAutoResolveSettings:
|
|
161
|
+
source = env if env is not None else os.environ
|
|
162
|
+
enabled = True
|
|
163
|
+
verify_mode = "strict"
|
|
164
|
+
max_attempts = 1
|
|
165
|
+
|
|
166
|
+
cfg_raw = cfg.extra_fields.get("conflict_auto_resolve")
|
|
167
|
+
if isinstance(cfg_raw, dict):
|
|
168
|
+
enabled = _parse_bool(cfg_raw.get("enabled"), default=enabled)
|
|
169
|
+
verify_mode = _parse_verify_mode(cfg_raw.get("verify_mode"), default=verify_mode)
|
|
170
|
+
max_attempts = _parse_positive_int(cfg_raw.get("max_attempts"), default=max_attempts)
|
|
171
|
+
|
|
172
|
+
enabled = _parse_bool(
|
|
173
|
+
env_get_from(source, "ALYSIS_CONFLICT_AUTO_RESOLVE"),
|
|
174
|
+
default=enabled,
|
|
175
|
+
)
|
|
176
|
+
verify_mode = _parse_verify_mode(
|
|
177
|
+
env_get_from(source, "ALYSIS_CONFLICT_AUTO_RESOLVE_VERIFY"),
|
|
178
|
+
default=verify_mode,
|
|
179
|
+
)
|
|
180
|
+
max_attempts = _parse_positive_int(
|
|
181
|
+
env_get_from(source, "ALYSIS_CONFLICT_AUTO_RESOLVE_MAX_ATTEMPTS"),
|
|
182
|
+
default=max_attempts,
|
|
183
|
+
)
|
|
184
|
+
return ConflictAutoResolveSettings(
|
|
185
|
+
enabled=enabled,
|
|
186
|
+
verify_mode=verify_mode,
|
|
187
|
+
max_attempts=max_attempts,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def task_conflict_attempts(task: dict[str, Any]) -> int:
|
|
192
|
+
raw = task.get("conflict_attempts")
|
|
193
|
+
try:
|
|
194
|
+
attempts = int(raw) if raw is not None else 0
|
|
195
|
+
except (TypeError, ValueError):
|
|
196
|
+
return 0
|
|
197
|
+
return max(0, attempts)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def can_attempt_conflict_auto_resolve(
|
|
201
|
+
*,
|
|
202
|
+
task: dict[str, Any],
|
|
203
|
+
settings: ConflictAutoResolveSettings,
|
|
204
|
+
) -> bool:
|
|
205
|
+
return settings.enabled and task_conflict_attempts(task) < settings.max_attempts
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def bump_conflict_attempt(task: dict[str, Any]) -> int:
|
|
209
|
+
next_attempt = task_conflict_attempts(task) + 1
|
|
210
|
+
task["conflict_attempts"] = next_attempt
|
|
211
|
+
return next_attempt
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _run_git(
|
|
215
|
+
root: Path,
|
|
216
|
+
args: list[str],
|
|
217
|
+
*,
|
|
218
|
+
extra_config: dict[str, str] | None = None,
|
|
219
|
+
) -> subprocess.CompletedProcess[str]:
|
|
220
|
+
cmd = build_git_cmd(root, args, extra_config=extra_config)
|
|
221
|
+
return subprocess.run(
|
|
222
|
+
cmd,
|
|
223
|
+
check=False,
|
|
224
|
+
capture_output=True,
|
|
225
|
+
text=True,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _run_git_checked(
|
|
230
|
+
root: Path,
|
|
231
|
+
args: list[str],
|
|
232
|
+
*,
|
|
233
|
+
extra_config: dict[str, str] | None = None,
|
|
234
|
+
error_message: str,
|
|
235
|
+
) -> subprocess.CompletedProcess[str]:
|
|
236
|
+
cp = _run_git(root, args, extra_config=extra_config)
|
|
237
|
+
if cp.returncode != 0:
|
|
238
|
+
detail = (cp.stderr or cp.stdout).strip()
|
|
239
|
+
raise GitOpsError(f"{error_message}: {detail or 'unknown error'}")
|
|
240
|
+
return cp
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _git_head(root: Path) -> str:
|
|
244
|
+
cp = _run_git_checked(
|
|
245
|
+
root,
|
|
246
|
+
["rev-parse", "HEAD"],
|
|
247
|
+
error_message="failed to resolve commit hash",
|
|
248
|
+
)
|
|
249
|
+
return cp.stdout.strip()
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _truncate(text: str, *, max_chars: int = 4000) -> str:
|
|
253
|
+
if len(text) <= max_chars:
|
|
254
|
+
return text
|
|
255
|
+
return text[:max_chars] + "...(truncated)"
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _format_agent_exception_summary(exc: Exception) -> str:
|
|
259
|
+
name = exc.__class__.__name__
|
|
260
|
+
message = str(exc).strip()
|
|
261
|
+
return f"{name}: {message}" if message else name
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _merge_changed_files(*path_groups: list[str]) -> list[str]:
|
|
265
|
+
seen: set[str] = set()
|
|
266
|
+
merged: list[str] = []
|
|
267
|
+
for group in path_groups:
|
|
268
|
+
for raw in group:
|
|
269
|
+
value = str(raw).strip().replace("\\", "/")
|
|
270
|
+
while value.startswith("./"):
|
|
271
|
+
value = value[2:]
|
|
272
|
+
if not value or value in seen:
|
|
273
|
+
continue
|
|
274
|
+
seen.add(value)
|
|
275
|
+
merged.append(value)
|
|
276
|
+
return merged
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
_CONFLICT_MARKER_PREFIXES = ("<<<<<<<", "=======", ">>>>>>>")
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _paths_with_conflict_markers(root: Path, paths: list[str]) -> list[str]:
|
|
283
|
+
marker_paths: list[str] = []
|
|
284
|
+
seen: set[str] = set()
|
|
285
|
+
for raw_path in paths:
|
|
286
|
+
rel_path = str(raw_path or "").strip()
|
|
287
|
+
if not rel_path or rel_path in seen:
|
|
288
|
+
continue
|
|
289
|
+
seen.add(rel_path)
|
|
290
|
+
target = root / rel_path
|
|
291
|
+
if not target.is_file():
|
|
292
|
+
continue
|
|
293
|
+
try:
|
|
294
|
+
lines = target.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
295
|
+
except OSError:
|
|
296
|
+
continue
|
|
297
|
+
if any(line.startswith(_CONFLICT_MARKER_PREFIXES) for line in lines):
|
|
298
|
+
marker_paths.append(rel_path)
|
|
299
|
+
return marker_paths
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _build_conflict_instruction(
|
|
303
|
+
*,
|
|
304
|
+
instruction_bundle: Any,
|
|
305
|
+
) -> str:
|
|
306
|
+
return str(instruction_bundle.instruction)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _conflict_instruction_sections(*, unmerged_files: list[str]) -> list[str]:
|
|
310
|
+
files_block = (
|
|
311
|
+
"\n".join(f"- `{path}`" for path in unmerged_files) if unmerged_files else "- (none)"
|
|
312
|
+
)
|
|
313
|
+
return [
|
|
314
|
+
"\n".join(
|
|
315
|
+
[
|
|
316
|
+
"## Conflict Resolution Scope",
|
|
317
|
+
"",
|
|
318
|
+
"- Resolve only the merge conflict.",
|
|
319
|
+
"- Do not expand scope beyond the conflict resolution itself.",
|
|
320
|
+
"- Edit only the currently unmerged files listed below.",
|
|
321
|
+
"- Preserve the task intent, but do not treat plan context as permission to broaden scope.",
|
|
322
|
+
"",
|
|
323
|
+
"### Unmerged Files",
|
|
324
|
+
"",
|
|
325
|
+
files_block,
|
|
326
|
+
]
|
|
327
|
+
)
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _write_auto_resolve_artifacts(
|
|
332
|
+
*,
|
|
333
|
+
conflict_dir: Path,
|
|
334
|
+
result_payload: dict[str, Any],
|
|
335
|
+
report_text: str,
|
|
336
|
+
patch_text: str,
|
|
337
|
+
context_text: str | None = None,
|
|
338
|
+
budget_payload: dict[str, Any] | None = None,
|
|
339
|
+
) -> tuple[Path, Path, Path]:
|
|
340
|
+
conflict_dir.mkdir(parents=True, exist_ok=True)
|
|
341
|
+
result_path = conflict_dir / "auto_resolve_result.json"
|
|
342
|
+
report_path = conflict_dir / "auto_resolve_report.md"
|
|
343
|
+
patch_path = conflict_dir / "auto_resolve_patch.diff"
|
|
344
|
+
result_path.write_text(
|
|
345
|
+
json.dumps(result_payload, indent=2, sort_keys=True) + "\n",
|
|
346
|
+
encoding="utf-8",
|
|
347
|
+
)
|
|
348
|
+
report_path.write_text(report_text.rstrip() + "\n", encoding="utf-8")
|
|
349
|
+
patch_path.write_text(patch_text if patch_text else "(no patch generated)\n", encoding="utf-8")
|
|
350
|
+
if context_text is not None:
|
|
351
|
+
(conflict_dir / "auto_resolve_context.md").write_text(
|
|
352
|
+
context_text.rstrip() + "\n",
|
|
353
|
+
encoding="utf-8",
|
|
354
|
+
)
|
|
355
|
+
if budget_payload is not None:
|
|
356
|
+
(conflict_dir / "auto_resolve_budget.json").write_text(
|
|
357
|
+
json.dumps(budget_payload, indent=2, sort_keys=True) + "\n",
|
|
358
|
+
encoding="utf-8",
|
|
359
|
+
)
|
|
360
|
+
return result_path, report_path, patch_path
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def attempt_auto_resolve_conflict(
|
|
364
|
+
*,
|
|
365
|
+
paths: RunPaths,
|
|
366
|
+
plan: dict[str, Any],
|
|
367
|
+
task: dict[str, Any],
|
|
368
|
+
cfg: AppConfig,
|
|
369
|
+
api_key_override: str | None,
|
|
370
|
+
base_branch: str,
|
|
371
|
+
task_branch: str,
|
|
372
|
+
keep_worktrees: bool,
|
|
373
|
+
settings: ConflictAutoResolveSettings | None = None,
|
|
374
|
+
verify_commands: list[str] | None = None,
|
|
375
|
+
verify_command_selection: ResolvedVerifyCommands | None = None,
|
|
376
|
+
) -> AutoResolveOutcome:
|
|
377
|
+
effective_settings = settings or load_conflict_auto_resolve_settings(cfg=cfg)
|
|
378
|
+
task_id = str(task.get("id") or "").strip()
|
|
379
|
+
safe_task = safe_task_file_component(task_id)
|
|
380
|
+
conflict_branch = f"conflict/{safe_task.lower()}"
|
|
381
|
+
worktree_repo_path = paths.run_dir / "conflict_worktrees" / safe_task / "repo"
|
|
382
|
+
conflict_dir = paths.execution_dir / "conflicts" / safe_task
|
|
383
|
+
verify_artifact_path = conflict_dir / "auto_resolve_verify.txt"
|
|
384
|
+
|
|
385
|
+
started_at = now_iso()
|
|
386
|
+
warnings: list[str] = []
|
|
387
|
+
errors: list[str] = []
|
|
388
|
+
verify_summary: str | None = None
|
|
389
|
+
merge_commit_hash: str | None = None
|
|
390
|
+
patch_text = "(no patch generated)\n"
|
|
391
|
+
agent_model = ""
|
|
392
|
+
unresolved_after: list[str] = []
|
|
393
|
+
worktree_kept = True
|
|
394
|
+
conflict_instruction_text: str | None = None
|
|
395
|
+
conflict_budget_payload: dict[str, Any] | None = None
|
|
396
|
+
conflict_paths: list[str] = []
|
|
397
|
+
recording_surface = RecordingSurface(NoopSurface())
|
|
398
|
+
resolved_verify_selection: ResolvedVerifyCommands | None = None
|
|
399
|
+
resolved_verify_commands: list[str] = []
|
|
400
|
+
prompt_verification_enabled = False
|
|
401
|
+
agent_exit_code: int | None = None
|
|
402
|
+
salvaged_nonzero_exit = False
|
|
403
|
+
runtime_artifacts_changed = False
|
|
404
|
+
material_changed_files: list[str] = []
|
|
405
|
+
if effective_settings.verify_mode != "off":
|
|
406
|
+
if verify_command_selection is not None:
|
|
407
|
+
resolved_verify_selection = verify_command_selection
|
|
408
|
+
if verify_commands is not None:
|
|
409
|
+
normalized_commands = tuple(
|
|
410
|
+
resolve_verify_commands(
|
|
411
|
+
cfg=cfg,
|
|
412
|
+
verify_cmd=verify_commands,
|
|
413
|
+
root=worktree_repo_path,
|
|
414
|
+
)
|
|
415
|
+
)
|
|
416
|
+
if normalized_commands != resolved_verify_selection.commands:
|
|
417
|
+
resolved_verify_selection = resolve_verify_command_selection(
|
|
418
|
+
cfg=cfg,
|
|
419
|
+
verify_cmd=verify_commands,
|
|
420
|
+
root=worktree_repo_path,
|
|
421
|
+
)
|
|
422
|
+
resolved_verify_selection = resolve_task_aware_verify_command_selection(
|
|
423
|
+
cfg=cfg,
|
|
424
|
+
verify_cmd=(verify_commands if resolved_verify_selection is None else None),
|
|
425
|
+
task=task,
|
|
426
|
+
root=worktree_repo_path,
|
|
427
|
+
selection=resolved_verify_selection,
|
|
428
|
+
plan_requirements=[
|
|
429
|
+
str(item).strip() for item in (plan.get("requirements") or []) if str(item).strip()
|
|
430
|
+
]
|
|
431
|
+
if isinstance(plan, dict)
|
|
432
|
+
else None,
|
|
433
|
+
)
|
|
434
|
+
resolved_verify_commands = list(resolved_verify_selection.commands)
|
|
435
|
+
prompt_verification_enabled = bool(resolved_verify_commands)
|
|
436
|
+
|
|
437
|
+
try:
|
|
438
|
+
ensure_task_worktree(
|
|
439
|
+
root=paths.root,
|
|
440
|
+
worktree_repo_path=worktree_repo_path,
|
|
441
|
+
branch=conflict_branch,
|
|
442
|
+
base_branch=base_branch,
|
|
443
|
+
)
|
|
444
|
+
mirror_plan_into_worktree(run_paths=paths, worktree_repo_path=worktree_repo_path)
|
|
445
|
+
|
|
446
|
+
merge_cp = _run_git(
|
|
447
|
+
worktree_repo_path,
|
|
448
|
+
["merge", "--no-ff", task_branch, "-m", f"Resolve {task_id}"],
|
|
449
|
+
)
|
|
450
|
+
unmerged_files = list_unmerged_files(worktree_repo_path)
|
|
451
|
+
if merge_cp.returncode != 0 and not unmerged_files:
|
|
452
|
+
detail = (merge_cp.stderr or merge_cp.stdout).strip()
|
|
453
|
+
raise GitOpsError(
|
|
454
|
+
f"failed to initialize conflict resolution merge: {detail or 'unknown error'}"
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
if unmerged_files:
|
|
458
|
+
conflict_paths = list(unmerged_files)
|
|
459
|
+
run_cfg = clone_cfg(cfg)
|
|
460
|
+
if effective_settings.verify_mode != "off":
|
|
461
|
+
run_cfg.verify_commands = list(resolved_verify_commands)
|
|
462
|
+
try:
|
|
463
|
+
run_cfg.model = resolve_model_for_role(
|
|
464
|
+
cfg=cfg,
|
|
465
|
+
role=ROLE_CONFLICT_RESOLVE,
|
|
466
|
+
plan=plan,
|
|
467
|
+
fallback_to_default=False,
|
|
468
|
+
prefer_context="forge",
|
|
469
|
+
)
|
|
470
|
+
except ConfigError:
|
|
471
|
+
run_cfg.model = resolve_model_for_role(
|
|
472
|
+
cfg=cfg,
|
|
473
|
+
role=ROLE_CODING,
|
|
474
|
+
plan=plan,
|
|
475
|
+
prefer_context="forge",
|
|
476
|
+
)
|
|
477
|
+
agent_model = run_cfg.model
|
|
478
|
+
prepared_knowledge = prepare_task_execution_knowledge(
|
|
479
|
+
run_paths=paths,
|
|
480
|
+
task=task,
|
|
481
|
+
selection_label="conflict_auto_resolve",
|
|
482
|
+
extra_paths=unmerged_files,
|
|
483
|
+
)
|
|
484
|
+
mirror_selected_knowledge_into_worktree(
|
|
485
|
+
materialized=prepared_knowledge,
|
|
486
|
+
run_paths=paths,
|
|
487
|
+
worktree_repo_path=worktree_repo_path,
|
|
488
|
+
)
|
|
489
|
+
instruction_bundle = build_task_execution_instruction_bundle(
|
|
490
|
+
plan=plan,
|
|
491
|
+
task=task,
|
|
492
|
+
root=worktree_repo_path,
|
|
493
|
+
cfg=run_cfg,
|
|
494
|
+
role_model=run_cfg.model,
|
|
495
|
+
mode="auto",
|
|
496
|
+
yes=True,
|
|
497
|
+
deny_write_prefixes=[".alysis"],
|
|
498
|
+
allow_write_globs=unmerged_files,
|
|
499
|
+
non_interactive=True,
|
|
500
|
+
verification_enabled=prompt_verification_enabled,
|
|
501
|
+
authoritative_verification_commands=(
|
|
502
|
+
resolved_verify_commands if prompt_verification_enabled else None
|
|
503
|
+
),
|
|
504
|
+
trusted_system_prompt_override=CONFLICT_RESOLVER_SYSTEM_PROMPT,
|
|
505
|
+
api_key=api_key_override,
|
|
506
|
+
subagents_enabled=False,
|
|
507
|
+
leading_sections=[
|
|
508
|
+
*_conflict_instruction_sections(unmerged_files=unmerged_files),
|
|
509
|
+
prepared_knowledge.prompt_section,
|
|
510
|
+
],
|
|
511
|
+
)
|
|
512
|
+
conflict_instruction_text = instruction_bundle.instruction
|
|
513
|
+
task_image_paths = list(instruction_bundle.image_paths) or None
|
|
514
|
+
conflict_attempt_count = task_conflict_attempts(task)
|
|
515
|
+
if conflict_attempt_count <= 0:
|
|
516
|
+
conflict_attempt_count = 1
|
|
517
|
+
conflict_file_count = len(unmerged_files)
|
|
518
|
+
conflict_step_budget = resolve_managed_task_step_budget(
|
|
519
|
+
cfg=run_cfg,
|
|
520
|
+
plan=plan,
|
|
521
|
+
task=task,
|
|
522
|
+
kind="conflict_resolution",
|
|
523
|
+
mode="auto",
|
|
524
|
+
verification_enabled=effective_settings.verify_mode != "off",
|
|
525
|
+
max_steps_override=None,
|
|
526
|
+
attempt_count=conflict_attempt_count,
|
|
527
|
+
image_count=len(task_image_paths or []),
|
|
528
|
+
conflict_file_count=conflict_file_count,
|
|
529
|
+
)
|
|
530
|
+
conflict_budget_payload = instruction_bundle.to_budget_artifact_payload()
|
|
531
|
+
conflict_budget_payload["step_budget"] = conflict_step_budget.to_payload()
|
|
532
|
+
instruction = _build_conflict_instruction(
|
|
533
|
+
instruction_bundle=instruction_bundle,
|
|
534
|
+
)
|
|
535
|
+
before_runtime_snapshot = snapshot_runtime_tree(worktree_repo_path)
|
|
536
|
+
before_workspace_snapshot = snapshot_workspace_tree(worktree_repo_path)
|
|
537
|
+
run_agent_exception_summary: str | None = None
|
|
538
|
+
try:
|
|
539
|
+
agent_exit_code = run_agent(
|
|
540
|
+
cfg=run_cfg,
|
|
541
|
+
root=worktree_repo_path,
|
|
542
|
+
instruction=instruction,
|
|
543
|
+
mode="auto",
|
|
544
|
+
runtime_kind=RuntimeKind.CONFLICT_AUTO_RESOLVE,
|
|
545
|
+
yes=True,
|
|
546
|
+
max_steps=conflict_step_budget.resolved_max_steps,
|
|
547
|
+
no_log=False,
|
|
548
|
+
api_key_override=api_key_override,
|
|
549
|
+
console=make_console(),
|
|
550
|
+
surface=recording_surface,
|
|
551
|
+
deny_write_prefixes=[".alysis"],
|
|
552
|
+
allow_write_globs=unmerged_files,
|
|
553
|
+
non_interactive=True,
|
|
554
|
+
session_log_dir_override=conflict_dir / "sessions",
|
|
555
|
+
session_id_override=f"{safe_task}-conflict",
|
|
556
|
+
usage_role=f"conflict_resolve:{task_id}",
|
|
557
|
+
enable_compaction=False,
|
|
558
|
+
enable_tool_output_offload=True,
|
|
559
|
+
enable_conversation_summarization=True,
|
|
560
|
+
compaction_profile="execution",
|
|
561
|
+
enable_chat_turn_step_budget=False,
|
|
562
|
+
one_shot_execution=True,
|
|
563
|
+
trusted_system_prompt_override=CONFLICT_RESOLVER_SYSTEM_PROMPT,
|
|
564
|
+
verification_enabled=prompt_verification_enabled,
|
|
565
|
+
authoritative_verification_commands=(
|
|
566
|
+
resolved_verify_commands if prompt_verification_enabled else None
|
|
567
|
+
),
|
|
568
|
+
subagents_enabled=False,
|
|
569
|
+
)
|
|
570
|
+
except Exception as e: # noqa: BLE001
|
|
571
|
+
run_agent_exception_summary = _format_agent_exception_summary(e)
|
|
572
|
+
agent_exit_code = None
|
|
573
|
+
|
|
574
|
+
after_runtime_snapshot = snapshot_runtime_tree(worktree_repo_path)
|
|
575
|
+
after_workspace_snapshot = snapshot_workspace_tree(worktree_repo_path)
|
|
576
|
+
runtime_artifacts_changed = before_runtime_snapshot != after_runtime_snapshot
|
|
577
|
+
workspace_delta_files = {
|
|
578
|
+
path
|
|
579
|
+
for path in (set(before_workspace_snapshot) | set(after_workspace_snapshot))
|
|
580
|
+
if before_workspace_snapshot.get(path) != after_workspace_snapshot.get(path)
|
|
581
|
+
}
|
|
582
|
+
current_changed_files = list_changed_files_including_untracked(worktree_repo_path)
|
|
583
|
+
material_changed_files = [
|
|
584
|
+
path for path in current_changed_files if path in workspace_delta_files
|
|
585
|
+
]
|
|
586
|
+
material_changed_files = _merge_changed_files(material_changed_files)
|
|
587
|
+
material_changes_detected = bool(material_changed_files)
|
|
588
|
+
nonzero_agent_exit = (
|
|
589
|
+
agent_exit_code is not None
|
|
590
|
+
and agent_exit_code != 0
|
|
591
|
+
and run_agent_exception_summary is None
|
|
592
|
+
)
|
|
593
|
+
if run_agent_exception_summary is not None:
|
|
594
|
+
raise RuntimeError(
|
|
595
|
+
"conflict resolver agent raised before returning normally: "
|
|
596
|
+
f"{run_agent_exception_summary}"
|
|
597
|
+
)
|
|
598
|
+
if agent_exit_code is None:
|
|
599
|
+
raise RuntimeError("conflict resolver agent did not return an exit code")
|
|
600
|
+
if nonzero_agent_exit:
|
|
601
|
+
if runtime_artifacts_changed:
|
|
602
|
+
raise RuntimeError(
|
|
603
|
+
"conflict resolver agent failed with exit code "
|
|
604
|
+
f"{agent_exit_code}; protected runtime "
|
|
605
|
+
"artifacts under .alysis changed"
|
|
606
|
+
)
|
|
607
|
+
if material_changes_detected:
|
|
608
|
+
raise RuntimeError(
|
|
609
|
+
"conflict resolver agent failed with exit code "
|
|
610
|
+
f"{agent_exit_code} after producing material conflict-resolution changes; "
|
|
611
|
+
"refusing to accept partial conflict auto-resolve result"
|
|
612
|
+
)
|
|
613
|
+
raise RuntimeError(
|
|
614
|
+
"conflict resolver agent failed with exit code "
|
|
615
|
+
f"{agent_exit_code} and produced no material conflict-resolution changes"
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
marker_paths = _paths_with_conflict_markers(worktree_repo_path, conflict_paths)
|
|
619
|
+
if marker_paths:
|
|
620
|
+
raise RuntimeError(
|
|
621
|
+
"conflict markers remain unresolved in: " + ", ".join(marker_paths[:20])
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
stage_all(worktree_repo_path)
|
|
625
|
+
unstage_staged_prefixes(
|
|
626
|
+
worktree_repo_path,
|
|
627
|
+
[".alysis", ".alysis_images", "alysis-feedback"],
|
|
628
|
+
)
|
|
629
|
+
ensure_not_staged_prefixes(
|
|
630
|
+
worktree_repo_path,
|
|
631
|
+
[".alysis", ".alysis_images", "alysis-feedback"],
|
|
632
|
+
)
|
|
633
|
+
if has_grounded_rust_target_runtime_artifacts(worktree_repo_path):
|
|
634
|
+
staged_now = staged_files(worktree_repo_path)
|
|
635
|
+
unstage_staged_runtime_artifacts(
|
|
636
|
+
worktree_repo_path,
|
|
637
|
+
current_paths=staged_now,
|
|
638
|
+
)
|
|
639
|
+
staged_now = staged_files(worktree_repo_path)
|
|
640
|
+
ensure_not_staged_runtime_artifacts(
|
|
641
|
+
worktree_repo_path,
|
|
642
|
+
current_paths=staged_now,
|
|
643
|
+
)
|
|
644
|
+
unresolved_after = list_unmerged_files(worktree_repo_path)
|
|
645
|
+
if unresolved_after:
|
|
646
|
+
raise RuntimeError(
|
|
647
|
+
"conflict paths remain unmerged after staging resolved files: "
|
|
648
|
+
+ ", ".join(unresolved_after[:20])
|
|
649
|
+
)
|
|
650
|
+
_run_git_checked(
|
|
651
|
+
worktree_repo_path,
|
|
652
|
+
["commit", "--no-edit"],
|
|
653
|
+
extra_config={
|
|
654
|
+
"user.name": DEFAULT_COMMIT_AUTHOR_NAME,
|
|
655
|
+
"user.email": DEFAULT_COMMIT_AUTHOR_EMAIL,
|
|
656
|
+
},
|
|
657
|
+
error_message="failed to commit conflict resolution",
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
if effective_settings.verify_mode != "off" and resolved_verify_commands:
|
|
661
|
+
verify_result = run_task_verification(
|
|
662
|
+
root=worktree_repo_path,
|
|
663
|
+
commands=resolved_verify_commands,
|
|
664
|
+
artifact_path=verify_artifact_path,
|
|
665
|
+
cfg=cfg,
|
|
666
|
+
)
|
|
667
|
+
verify_summary = verify_result.summary
|
|
668
|
+
if not verify_result.all_passed:
|
|
669
|
+
if effective_settings.verify_mode == "strict":
|
|
670
|
+
raise RuntimeError(f"strict conflict verify failed: {verify_result.summary}")
|
|
671
|
+
warnings.append(f"Conflict verify warning: {verify_result.summary}")
|
|
672
|
+
elif effective_settings.verify_mode == "strict":
|
|
673
|
+
verify_summary = "verification skipped: no authoritative commands available"
|
|
674
|
+
raise RuntimeError(
|
|
675
|
+
"strict conflict verify could not run: no authoritative commands available"
|
|
676
|
+
)
|
|
677
|
+
elif effective_settings.verify_mode != "off":
|
|
678
|
+
verify_summary = "verification skipped: no authoritative commands available"
|
|
679
|
+
else:
|
|
680
|
+
verify_summary = "verification disabled (conflict auto-resolve verify=off)"
|
|
681
|
+
|
|
682
|
+
merge_commit_hash = _git_head(worktree_repo_path)
|
|
683
|
+
patch_text = format_patch_stdout(worktree_repo_path, base_branch=base_branch)
|
|
684
|
+
|
|
685
|
+
_run_git_checked(
|
|
686
|
+
paths.root,
|
|
687
|
+
["checkout", base_branch],
|
|
688
|
+
error_message=f"failed to checkout base branch {base_branch}",
|
|
689
|
+
)
|
|
690
|
+
ff_cp = _run_git(paths.root, ["merge", "--ff-only", conflict_branch])
|
|
691
|
+
if ff_cp.returncode != 0:
|
|
692
|
+
try:
|
|
693
|
+
merge_commit_hash = merge_no_ff(
|
|
694
|
+
paths.root,
|
|
695
|
+
base_branch=base_branch,
|
|
696
|
+
task_branch=conflict_branch,
|
|
697
|
+
message=f"Merge {task_id}: auto-resolved conflict",
|
|
698
|
+
)
|
|
699
|
+
except GitOpsError as e:
|
|
700
|
+
cleanup_ok, cleanup_log = try_abort_merge(paths.root, base_branch=base_branch)
|
|
701
|
+
errors.append(f"failed to merge conflict branch into base: {e}")
|
|
702
|
+
if not cleanup_ok:
|
|
703
|
+
errors.append(
|
|
704
|
+
"failed to cleanup repository after merge error; "
|
|
705
|
+
f"cleanup log: {_truncate(cleanup_log, max_chars=600)}"
|
|
706
|
+
)
|
|
707
|
+
raise
|
|
708
|
+
else:
|
|
709
|
+
merge_commit_hash = _git_head(paths.root)
|
|
710
|
+
|
|
711
|
+
if not keep_worktrees:
|
|
712
|
+
try:
|
|
713
|
+
remove_task_worktree(
|
|
714
|
+
root=paths.root,
|
|
715
|
+
worktree_repo_path=worktree_repo_path,
|
|
716
|
+
force=True,
|
|
717
|
+
)
|
|
718
|
+
worktree_kept = False
|
|
719
|
+
except Exception as e: # noqa: BLE001
|
|
720
|
+
warnings.append(f"conflict worktree cleanup failed: {e}")
|
|
721
|
+
try:
|
|
722
|
+
delete_branch(paths.root, conflict_branch)
|
|
723
|
+
except Exception as e: # noqa: BLE001
|
|
724
|
+
warnings.append(f"conflict branch cleanup failed: {e}")
|
|
725
|
+
else:
|
|
726
|
+
worktree_kept = True
|
|
727
|
+
|
|
728
|
+
success = True
|
|
729
|
+
error_message: str | None = None
|
|
730
|
+
except (GitOpsError, RuntimeError, VerifyError, ConfigError) as e:
|
|
731
|
+
success = False
|
|
732
|
+
error_message = str(e)
|
|
733
|
+
errors.append(str(e))
|
|
734
|
+
|
|
735
|
+
finished_at = now_iso()
|
|
736
|
+
result_payload = {
|
|
737
|
+
"task_id": task_id,
|
|
738
|
+
"base_branch": base_branch,
|
|
739
|
+
"task_branch": task_branch,
|
|
740
|
+
"conflict_branch": conflict_branch,
|
|
741
|
+
"worktree_repo_path": os.fspath(worktree_repo_path),
|
|
742
|
+
"worktree_kept": worktree_kept,
|
|
743
|
+
"success": success,
|
|
744
|
+
"merge_commit_hash": merge_commit_hash,
|
|
745
|
+
"verify_mode": effective_settings.verify_mode,
|
|
746
|
+
"verify_summary": verify_summary,
|
|
747
|
+
"verify_artifact_path": (
|
|
748
|
+
os.fspath(verify_artifact_path) if verify_artifact_path.exists() else None
|
|
749
|
+
),
|
|
750
|
+
"context_artifact_path": (
|
|
751
|
+
os.fspath(conflict_dir / "auto_resolve_context.md")
|
|
752
|
+
if conflict_instruction_text is not None
|
|
753
|
+
else None
|
|
754
|
+
),
|
|
755
|
+
"budget_artifact_path": (
|
|
756
|
+
os.fspath(conflict_dir / "auto_resolve_budget.json")
|
|
757
|
+
if conflict_budget_payload is not None
|
|
758
|
+
else None
|
|
759
|
+
),
|
|
760
|
+
"agent_model": agent_model,
|
|
761
|
+
"agent_exit_code": agent_exit_code,
|
|
762
|
+
"salvaged_nonzero_exit": salvaged_nonzero_exit,
|
|
763
|
+
"warnings": warnings,
|
|
764
|
+
"errors": errors,
|
|
765
|
+
"unresolved_after": unresolved_after,
|
|
766
|
+
"started_at": started_at,
|
|
767
|
+
"finished_at": finished_at,
|
|
768
|
+
}
|
|
769
|
+
report_lines = [
|
|
770
|
+
f"# Conflict Auto-Resolve: {task_id}",
|
|
771
|
+
"",
|
|
772
|
+
f"- Success: {'yes' if success else 'no'}",
|
|
773
|
+
f"- Base Branch: `{base_branch}`",
|
|
774
|
+
f"- Task Branch: `{task_branch}`",
|
|
775
|
+
f"- Conflict Branch: `{conflict_branch}`",
|
|
776
|
+
f"- Worktree: `{worktree_repo_path}`",
|
|
777
|
+
f"- Verify Mode: `{effective_settings.verify_mode}`",
|
|
778
|
+
f"- Verify Summary: {verify_summary or '(none)'}",
|
|
779
|
+
f"- Merge Commit: `{merge_commit_hash or '-'}`",
|
|
780
|
+
f"- Agent Exit Code: `{agent_exit_code if agent_exit_code is not None else '-'}`",
|
|
781
|
+
f"- Salvaged Non-Zero Exit: {'yes' if salvaged_nonzero_exit else 'no'}",
|
|
782
|
+
"",
|
|
783
|
+
]
|
|
784
|
+
if warnings:
|
|
785
|
+
report_lines.append("## Warnings")
|
|
786
|
+
report_lines.append("")
|
|
787
|
+
report_lines.extend(f"- {item}" for item in warnings)
|
|
788
|
+
report_lines.append("")
|
|
789
|
+
if errors:
|
|
790
|
+
report_lines.append("## Errors")
|
|
791
|
+
report_lines.append("")
|
|
792
|
+
report_lines.extend(f"- {item}" for item in errors)
|
|
793
|
+
report_lines.append("")
|
|
794
|
+
if not warnings and not errors:
|
|
795
|
+
report_lines.append("No warnings or errors recorded.")
|
|
796
|
+
report_lines.append("")
|
|
797
|
+
report_text = "\n".join(report_lines).rstrip() + "\n"
|
|
798
|
+
|
|
799
|
+
result_path, report_path, patch_path = _write_auto_resolve_artifacts(
|
|
800
|
+
conflict_dir=conflict_dir,
|
|
801
|
+
result_payload=result_payload,
|
|
802
|
+
report_text=report_text,
|
|
803
|
+
patch_text=patch_text,
|
|
804
|
+
context_text=conflict_instruction_text,
|
|
805
|
+
budget_payload=conflict_budget_payload,
|
|
806
|
+
)
|
|
807
|
+
persisted_capture = persist_execution_knowledge_capture(
|
|
808
|
+
paths=paths,
|
|
809
|
+
task=task,
|
|
810
|
+
source="conflict_auto_resolve",
|
|
811
|
+
assistant_message=recording_surface.final_assistant_message,
|
|
812
|
+
artifact_dir=conflict_dir / "knowledge_capture" / safe_task_file_component(started_at),
|
|
813
|
+
report_path=report_path,
|
|
814
|
+
patch_path=patch_path,
|
|
815
|
+
verify_artifact_path=verify_artifact_path if verify_artifact_path.exists() else None,
|
|
816
|
+
budget_artifact_path=(conflict_dir / "auto_resolve_budget.json")
|
|
817
|
+
if conflict_budget_payload is not None
|
|
818
|
+
else None,
|
|
819
|
+
session_artifact_dir=(conflict_dir / "sessions")
|
|
820
|
+
if (conflict_dir / "sessions").exists()
|
|
821
|
+
else None,
|
|
822
|
+
)
|
|
823
|
+
if success:
|
|
824
|
+
promote_validated_knowledge_capture(
|
|
825
|
+
paths=paths,
|
|
826
|
+
task=task,
|
|
827
|
+
artifact_dir=persisted_capture.artifact_dir,
|
|
828
|
+
)
|
|
829
|
+
else:
|
|
830
|
+
mark_knowledge_capture_promotion_skipped(
|
|
831
|
+
artifact_dir=persisted_capture.artifact_dir,
|
|
832
|
+
reason="conflict auto-resolve outcome was not accepted",
|
|
833
|
+
)
|
|
834
|
+
if success:
|
|
835
|
+
attempt_summary = "Conflict auto-resolve succeeded."
|
|
836
|
+
else:
|
|
837
|
+
attempt_summary = f"Conflict auto-resolve failed: {error_message or 'unknown error'}"
|
|
838
|
+
reported_changed_files = material_changed_files or conflict_paths or unresolved_after
|
|
839
|
+
write_task_attempt_entry(
|
|
840
|
+
paths=paths,
|
|
841
|
+
task=task,
|
|
842
|
+
source="conflict_auto_resolve",
|
|
843
|
+
result="success" if success else "failure",
|
|
844
|
+
summary=attempt_summary,
|
|
845
|
+
changed_files=reported_changed_files,
|
|
846
|
+
verify_summary=verify_summary,
|
|
847
|
+
report_path=report_path,
|
|
848
|
+
patch_path=patch_path,
|
|
849
|
+
verify_artifact_path=verify_artifact_path if verify_artifact_path.exists() else None,
|
|
850
|
+
budget_artifact_path=(conflict_dir / "auto_resolve_budget.json")
|
|
851
|
+
if conflict_budget_payload is not None
|
|
852
|
+
else None,
|
|
853
|
+
session_artifact_dir=(conflict_dir / "sessions")
|
|
854
|
+
if (conflict_dir / "sessions").exists()
|
|
855
|
+
else None,
|
|
856
|
+
acceptance_state="accepted" if success else "rejected",
|
|
857
|
+
extra_tags=[
|
|
858
|
+
"conflict_auto_resolve",
|
|
859
|
+
],
|
|
860
|
+
)
|
|
861
|
+
if not success or unresolved_after:
|
|
862
|
+
write_issue_entry(
|
|
863
|
+
paths=paths,
|
|
864
|
+
task=task,
|
|
865
|
+
source="conflict_auto_resolve",
|
|
866
|
+
title=f"{task_id}: conflict auto-resolve failed",
|
|
867
|
+
summary=error_message or "Conflict auto-resolve did not finish cleanly.",
|
|
868
|
+
paths_in_scope=unresolved_after or reported_changed_files,
|
|
869
|
+
report_path=report_path,
|
|
870
|
+
patch_path=patch_path,
|
|
871
|
+
verify_artifact_path=verify_artifact_path if verify_artifact_path.exists() else None,
|
|
872
|
+
budget_artifact_path=(conflict_dir / "auto_resolve_budget.json")
|
|
873
|
+
if conflict_budget_payload is not None
|
|
874
|
+
else None,
|
|
875
|
+
session_artifact_dir=(conflict_dir / "sessions")
|
|
876
|
+
if (conflict_dir / "sessions").exists()
|
|
877
|
+
else None,
|
|
878
|
+
tags=["conflict_auto_resolve", "merge_conflict"],
|
|
879
|
+
)
|
|
880
|
+
rebuild_knowledge_index(paths)
|
|
881
|
+
return AutoResolveOutcome(
|
|
882
|
+
success=success,
|
|
883
|
+
task_id=task_id,
|
|
884
|
+
conflict_branch=conflict_branch,
|
|
885
|
+
worktree_repo_path=worktree_repo_path,
|
|
886
|
+
result_json_path=result_path,
|
|
887
|
+
report_path=report_path,
|
|
888
|
+
patch_path=patch_path,
|
|
889
|
+
merge_commit_hash=merge_commit_hash,
|
|
890
|
+
verify_summary=verify_summary,
|
|
891
|
+
warnings=warnings,
|
|
892
|
+
error=error_message,
|
|
893
|
+
agent_exit_code=agent_exit_code,
|
|
894
|
+
salvaged_nonzero_exit=salvaged_nonzero_exit,
|
|
895
|
+
)
|