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,362 @@
|
|
|
1
|
+
"""Post-execution completion report for Forge swarm runs.
|
|
2
|
+
|
|
3
|
+
``/execute plan`` historically ended with a one-line headline plus a pointer at
|
|
4
|
+
``swarm_summary.md`` — the run's actual results (what each worker built, where
|
|
5
|
+
the files landed, whether the output is trustworthy, and how to try it) stayed
|
|
6
|
+
buried in run artifacts nothing surfaced. This module turns those artifacts
|
|
7
|
+
into one user-facing markdown report, deterministically on the host side: no
|
|
8
|
+
model call, so it works identically for every provider and costs nothing.
|
|
9
|
+
|
|
10
|
+
Inputs are read best-effort from the run directory (``plan.json`` statuses,
|
|
11
|
+
``execution/worker_results/*.json``, the per-task
|
|
12
|
+
``knowledge_capture/<task>/<ts>/assistant_message.md`` worker narratives) so a
|
|
13
|
+
partially-written run still produces an honest, if sparser, report.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from .verification_repair import TASK_STATUS_COMPLETED_UNVERIFIED
|
|
23
|
+
|
|
24
|
+
_DONE_STATES = frozenset({"done"})
|
|
25
|
+
_NOOP_STATES = frozenset({"already_satisfied"})
|
|
26
|
+
# Its own bucket on purpose: counted as finished (the work landed and was kept), but
|
|
27
|
+
# rendered with the caveat, because nothing authoritative checked it.
|
|
28
|
+
_UNVERIFIED_STATES = frozenset({TASK_STATUS_COMPLETED_UNVERIFIED})
|
|
29
|
+
_FAILURE_STATES = frozenset(
|
|
30
|
+
{
|
|
31
|
+
"failed",
|
|
32
|
+
"verify_failed",
|
|
33
|
+
"candidate_rejected",
|
|
34
|
+
"changes_requested",
|
|
35
|
+
"merge_conflict",
|
|
36
|
+
"blocked_integration",
|
|
37
|
+
"blocked",
|
|
38
|
+
"interrupted",
|
|
39
|
+
"cancelled",
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
_OBSOLETE_STATES = frozenset({"superseded", "invalidated"})
|
|
43
|
+
_MAX_FILES_SHOWN = 12
|
|
44
|
+
_MAX_NOTE_CHARS = 140
|
|
45
|
+
_MAX_REASON_CHARS = 160
|
|
46
|
+
_MAX_HINTS = 3
|
|
47
|
+
_RUN_SCRIPT_PRIORITY = ("dev", "start", "serve", "preview")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _canonical_status(value: object) -> str:
|
|
51
|
+
status = str(value or "").strip().lower()
|
|
52
|
+
try:
|
|
53
|
+
from .swarm_scheduler import canonical_task_status
|
|
54
|
+
|
|
55
|
+
return canonical_task_status(status)
|
|
56
|
+
except Exception: # noqa: BLE001
|
|
57
|
+
return status or "planned"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _current_branch(root: Path) -> str:
|
|
61
|
+
try:
|
|
62
|
+
from .git_ops import current_branch
|
|
63
|
+
|
|
64
|
+
return str(current_branch(root)).strip()
|
|
65
|
+
except Exception: # noqa: BLE001
|
|
66
|
+
return ""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _load_worker_results(paths: Any) -> dict[str, dict[str, Any]]:
|
|
70
|
+
"""Index ``execution/worker_results/*.json`` payloads by task id."""
|
|
71
|
+
execution_dir = getattr(paths, "execution_dir", None)
|
|
72
|
+
if execution_dir is None:
|
|
73
|
+
return {}
|
|
74
|
+
results_dir = Path(execution_dir) / "worker_results"
|
|
75
|
+
indexed: dict[str, dict[str, Any]] = {}
|
|
76
|
+
try:
|
|
77
|
+
entries = sorted(results_dir.glob("*.json"))
|
|
78
|
+
except Exception: # noqa: BLE001
|
|
79
|
+
return {}
|
|
80
|
+
for entry in entries:
|
|
81
|
+
try:
|
|
82
|
+
payload = json.loads(entry.read_text(encoding="utf-8"))
|
|
83
|
+
except Exception: # noqa: BLE001
|
|
84
|
+
continue
|
|
85
|
+
if not isinstance(payload, dict):
|
|
86
|
+
continue
|
|
87
|
+
task_id = str(payload.get("task_id") or "").strip()
|
|
88
|
+
if task_id:
|
|
89
|
+
indexed[task_id] = payload
|
|
90
|
+
return indexed
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _worker_note(worker_payload: dict[str, Any]) -> str:
|
|
94
|
+
"""First meaningful line of the worker's own final answer, if captured."""
|
|
95
|
+
capture_dir = str(worker_payload.get("knowledge_capture_artifact_dir") or "").strip()
|
|
96
|
+
if not capture_dir:
|
|
97
|
+
return ""
|
|
98
|
+
message_path = Path(capture_dir) / "assistant_message.md"
|
|
99
|
+
try:
|
|
100
|
+
text = message_path.read_text(encoding="utf-8")
|
|
101
|
+
except Exception: # noqa: BLE001
|
|
102
|
+
return ""
|
|
103
|
+
for raw_line in text.splitlines():
|
|
104
|
+
line = raw_line.strip()
|
|
105
|
+
if not line or line == "(none captured)":
|
|
106
|
+
continue
|
|
107
|
+
if line.startswith(("#", "```", "---", "KNOWLEDGE")):
|
|
108
|
+
continue
|
|
109
|
+
line = line.lstrip("-*> ").strip()
|
|
110
|
+
if not line:
|
|
111
|
+
continue
|
|
112
|
+
if len(line) > _MAX_NOTE_CHARS:
|
|
113
|
+
line = line[: _MAX_NOTE_CHARS - 1].rstrip() + "…"
|
|
114
|
+
return line
|
|
115
|
+
return ""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _failure_reason(task: dict[str, Any], worker_payload: dict[str, Any]) -> str:
|
|
119
|
+
for candidate in (
|
|
120
|
+
task.get("last_error"),
|
|
121
|
+
worker_payload.get("failure_reason"),
|
|
122
|
+
worker_payload.get("error"),
|
|
123
|
+
worker_payload.get("summary"),
|
|
124
|
+
):
|
|
125
|
+
reason = " ".join(str(candidate or "").split())
|
|
126
|
+
if reason:
|
|
127
|
+
if len(reason) > _MAX_REASON_CHARS:
|
|
128
|
+
reason = reason[: _MAX_REASON_CHARS - 1].rstrip() + "…"
|
|
129
|
+
return reason
|
|
130
|
+
return "see the task report for details"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _shallowest_existing(root: Path, rel_paths: list[str]) -> Path | None:
|
|
134
|
+
for rel in sorted(rel_paths, key=lambda item: (item.count("/"), item)):
|
|
135
|
+
candidate = root / Path(rel)
|
|
136
|
+
try:
|
|
137
|
+
if candidate.is_file():
|
|
138
|
+
return candidate
|
|
139
|
+
except OSError:
|
|
140
|
+
continue
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _package_json_hint(pkg_path: Path) -> str:
|
|
145
|
+
try:
|
|
146
|
+
payload = json.loads(pkg_path.read_text(encoding="utf-8"))
|
|
147
|
+
except Exception: # noqa: BLE001
|
|
148
|
+
return ""
|
|
149
|
+
scripts = payload.get("scripts")
|
|
150
|
+
if not isinstance(scripts, dict):
|
|
151
|
+
return ""
|
|
152
|
+
script_name = next(
|
|
153
|
+
(name for name in _RUN_SCRIPT_PRIORITY if str(scripts.get(name) or "").strip()),
|
|
154
|
+
"",
|
|
155
|
+
)
|
|
156
|
+
if not script_name:
|
|
157
|
+
return ""
|
|
158
|
+
run_cmd = "npm start" if script_name == "start" else f"npm run {script_name}"
|
|
159
|
+
pkg_dir = pkg_path.parent
|
|
160
|
+
needs_install = not (pkg_dir / "node_modules").is_dir()
|
|
161
|
+
command = f"npm install && {run_cmd}" if needs_install else run_cmd
|
|
162
|
+
return f'Node app: `cd "{pkg_dir}"` then `{command}` (package.json `{script_name}` script)'
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def detect_run_hints(root: Path, changed_files: list[str] | None = None) -> list[str]:
|
|
166
|
+
"""Grounded "try it" suggestions: only commands whose target files exist.
|
|
167
|
+
|
|
168
|
+
Prefers files this run actually changed over pre-existing ones, and always
|
|
169
|
+
names absolute directories — the workspace root the swarm merged into is
|
|
170
|
+
not necessarily the directory the user's shell sits in.
|
|
171
|
+
"""
|
|
172
|
+
root = Path(root)
|
|
173
|
+
normalized = [str(item).replace("\\", "/").strip("/") for item in (changed_files or [])]
|
|
174
|
+
hints: list[str] = []
|
|
175
|
+
|
|
176
|
+
index_candidates = [rel for rel in normalized if rel.rsplit("/", 1)[-1].lower() == "index.html"]
|
|
177
|
+
index_path = _shallowest_existing(root, index_candidates)
|
|
178
|
+
if index_path is None:
|
|
179
|
+
try:
|
|
180
|
+
if (root / "index.html").is_file():
|
|
181
|
+
index_path = root / "index.html"
|
|
182
|
+
except OSError:
|
|
183
|
+
index_path = None
|
|
184
|
+
if index_path is not None:
|
|
185
|
+
serve_dir = index_path.parent
|
|
186
|
+
hints.append(
|
|
187
|
+
f"Static site: open `{index_path}` in a browser, or serve it — "
|
|
188
|
+
f'`cd "{serve_dir}"` then `python -m http.server 8000` and visit '
|
|
189
|
+
"http://localhost:8000"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
pkg_candidates = [rel for rel in normalized if rel.rsplit("/", 1)[-1] == "package.json"]
|
|
193
|
+
pkg_path = _shallowest_existing(root, pkg_candidates)
|
|
194
|
+
if pkg_path is None:
|
|
195
|
+
try:
|
|
196
|
+
if (root / "package.json").is_file():
|
|
197
|
+
pkg_path = root / "package.json"
|
|
198
|
+
except OSError:
|
|
199
|
+
pkg_path = None
|
|
200
|
+
if pkg_path is not None:
|
|
201
|
+
pkg_hint = _package_json_hint(pkg_path)
|
|
202
|
+
if pkg_hint:
|
|
203
|
+
hints.append(pkg_hint)
|
|
204
|
+
|
|
205
|
+
return hints[:_MAX_HINTS]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _task_rows(
|
|
209
|
+
tasks: list[dict[str, Any]],
|
|
210
|
+
worker_results: dict[str, dict[str, Any]],
|
|
211
|
+
) -> tuple[list[str], list[str], int, int, int, int]:
|
|
212
|
+
"""Render per-task lines; return (rows, merged files, done, noop, failed, remaining)."""
|
|
213
|
+
rows: list[str] = []
|
|
214
|
+
merged_files: list[str] = []
|
|
215
|
+
done = noop = failed = remaining = 0
|
|
216
|
+
for task in tasks:
|
|
217
|
+
if not isinstance(task, dict):
|
|
218
|
+
remaining += 1
|
|
219
|
+
continue
|
|
220
|
+
task_id = str(task.get("id") or "").strip() or "?"
|
|
221
|
+
title = " ".join(str(task.get("title") or "").split()) or "(untitled task)"
|
|
222
|
+
status = _canonical_status(task.get("status"))
|
|
223
|
+
worker_payload = worker_results.get(task_id) or {}
|
|
224
|
+
changed = [
|
|
225
|
+
str(item).strip()
|
|
226
|
+
for item in (worker_payload.get("changed_files") or [])
|
|
227
|
+
if str(item).strip()
|
|
228
|
+
]
|
|
229
|
+
if status in _DONE_STATES:
|
|
230
|
+
done += 1
|
|
231
|
+
merged_files.extend(changed)
|
|
232
|
+
detail = f"{len(changed)} file{'s' if len(changed) != 1 else ''} changed"
|
|
233
|
+
note = _worker_note(worker_payload)
|
|
234
|
+
row = f"- ✓ **{task_id} · {title}** — {detail}"
|
|
235
|
+
if note:
|
|
236
|
+
row += f" — {note}"
|
|
237
|
+
if bool(worker_payload.get("verify_failed")):
|
|
238
|
+
row += " ⚠ its checks failed but were tolerated (verify mode: warn)"
|
|
239
|
+
rows.append(row)
|
|
240
|
+
elif status in _UNVERIFIED_STATES:
|
|
241
|
+
done += 1
|
|
242
|
+
merged_files.extend(changed)
|
|
243
|
+
detail = f"{len(changed)} file{'s' if len(changed) != 1 else ''} changed"
|
|
244
|
+
note = _worker_note(worker_payload)
|
|
245
|
+
row = (
|
|
246
|
+
f"- ✓ **{task_id} · {title}** — {detail} — ⚠ kept but UNVERIFIED: "
|
|
247
|
+
"no authoritative verification command exists for this workspace"
|
|
248
|
+
)
|
|
249
|
+
if note:
|
|
250
|
+
row += f" — {note}"
|
|
251
|
+
rows.append(row)
|
|
252
|
+
elif status in _NOOP_STATES:
|
|
253
|
+
noop += 1
|
|
254
|
+
rows.append(
|
|
255
|
+
f"- ○ **{task_id} · {title}** — reported no changes needed "
|
|
256
|
+
"(already satisfied); nothing was merged for it"
|
|
257
|
+
)
|
|
258
|
+
elif status in _FAILURE_STATES:
|
|
259
|
+
failed += 1
|
|
260
|
+
rows.append(
|
|
261
|
+
f"- ✗ **{task_id} · {title}** — {status}: {_failure_reason(task, worker_payload)}"
|
|
262
|
+
)
|
|
263
|
+
elif status in _OBSOLETE_STATES:
|
|
264
|
+
rows.append(f"- · **{task_id} · {title}** — {status}")
|
|
265
|
+
else:
|
|
266
|
+
remaining += 1
|
|
267
|
+
rows.append(f"- … **{task_id} · {title}** — {status or 'planned'}")
|
|
268
|
+
return rows, merged_files, done, noop, failed, remaining
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def build_forge_completion_report(
|
|
272
|
+
*,
|
|
273
|
+
paths: Any,
|
|
274
|
+
plan: dict[str, Any],
|
|
275
|
+
run_status: str = "",
|
|
276
|
+
run_clean: bool | None = None,
|
|
277
|
+
exit_code: int = 0,
|
|
278
|
+
) -> str:
|
|
279
|
+
"""Render the post-run answer shown to the user after ``/execute plan``."""
|
|
280
|
+
tasks = list(plan.get("tasks") or [])
|
|
281
|
+
worker_results = _load_worker_results(paths)
|
|
282
|
+
rows, merged_files, done, noop, failed, remaining = _task_rows(tasks, worker_results)
|
|
283
|
+
total = len(tasks)
|
|
284
|
+
root = Path(getattr(paths, "root", "."))
|
|
285
|
+
|
|
286
|
+
finished = done + noop
|
|
287
|
+
if total <= 0:
|
|
288
|
+
headline = "### Forge execution finished — no tasks were in the plan"
|
|
289
|
+
elif failed == 0 and remaining == 0 and exit_code == 0:
|
|
290
|
+
headline = f"### Forge execution complete — {finished}/{total} tasks finished"
|
|
291
|
+
else:
|
|
292
|
+
parts = [f"{finished} finished"]
|
|
293
|
+
if failed:
|
|
294
|
+
parts.append(f"{failed} failed")
|
|
295
|
+
if remaining:
|
|
296
|
+
parts.append(f"{remaining} not finished")
|
|
297
|
+
headline = "### Forge execution finished with issues — " + " · ".join(parts)
|
|
298
|
+
|
|
299
|
+
lines: list[str] = [headline, ""]
|
|
300
|
+
|
|
301
|
+
unique_files = sorted(dict.fromkeys(merged_files))
|
|
302
|
+
branch = _current_branch(root)
|
|
303
|
+
if unique_files:
|
|
304
|
+
where = f"branch `{branch}` in `{root}`" if branch else f"`{root}`"
|
|
305
|
+
lines.append(f"**Where the work landed:** merged into {where}.")
|
|
306
|
+
elif total > 0:
|
|
307
|
+
lines.append(
|
|
308
|
+
"⚠ **No files were changed by this run.** If you expected new files "
|
|
309
|
+
"(for example a website), nothing was actually written to "
|
|
310
|
+
f"`{root}` — check the per-task lines and reports below."
|
|
311
|
+
)
|
|
312
|
+
lines.append("")
|
|
313
|
+
|
|
314
|
+
if rows:
|
|
315
|
+
lines.append("**Tasks:**")
|
|
316
|
+
lines.extend(rows)
|
|
317
|
+
lines.append("")
|
|
318
|
+
|
|
319
|
+
if unique_files:
|
|
320
|
+
shown = unique_files[:_MAX_FILES_SHOWN]
|
|
321
|
+
extra = len(unique_files) - len(shown)
|
|
322
|
+
listing = ", ".join(f"`{item}`" for item in shown)
|
|
323
|
+
if extra > 0:
|
|
324
|
+
listing += f" (+{extra} more)"
|
|
325
|
+
lines.append(f"**Files changed ({len(unique_files)}):** {listing}")
|
|
326
|
+
lines.append("")
|
|
327
|
+
|
|
328
|
+
warn_verified = sum(
|
|
329
|
+
1
|
|
330
|
+
for payload in worker_results.values()
|
|
331
|
+
if bool(payload.get("verify_failed")) and bool(payload.get("success"))
|
|
332
|
+
)
|
|
333
|
+
if run_clean is False and run_status:
|
|
334
|
+
lines.append(
|
|
335
|
+
f"⚠ **Verification:** run status is `{run_status}` — some checks failed, "
|
|
336
|
+
"were tolerated, or never ran; treat completed tasks as unverified."
|
|
337
|
+
)
|
|
338
|
+
lines.append("")
|
|
339
|
+
elif warn_verified:
|
|
340
|
+
lines.append(
|
|
341
|
+
f"⚠ **Verification:** {warn_verified} task(s) merged with failing checks "
|
|
342
|
+
"(verify mode: warn)."
|
|
343
|
+
)
|
|
344
|
+
lines.append("")
|
|
345
|
+
|
|
346
|
+
if unique_files:
|
|
347
|
+
hints = detect_run_hints(root, unique_files)
|
|
348
|
+
if hints:
|
|
349
|
+
lines.append("**Try it:**")
|
|
350
|
+
lines.extend(f"- {hint}" for hint in hints)
|
|
351
|
+
lines.append("")
|
|
352
|
+
|
|
353
|
+
execution_dir = getattr(paths, "execution_dir", None)
|
|
354
|
+
if execution_dir is not None:
|
|
355
|
+
summary_path = Path(execution_dir) / "swarm_summary.md"
|
|
356
|
+
reports_dir = getattr(paths, "execution_reports_dir", Path(execution_dir) / "reports")
|
|
357
|
+
lines.append(f"**Details:** `{summary_path}` · per-task reports in `{reports_dir}`")
|
|
358
|
+
|
|
359
|
+
return "\n".join(lines).strip()
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
__all__ = ["build_forge_completion_report", "detect_run_hints"]
|