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 @@
|
|
|
1
|
+
"""Context planning and request-size diagnostics."""
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ..token_budget import estimate_tokens
|
|
10
|
+
|
|
11
|
+
DEFAULT_TOOL_SCHEMA_BUDGET_TOKENS = 8_000
|
|
12
|
+
DEFAULT_LARGEST_TOOL_COUNT = 5
|
|
13
|
+
DEFAULT_CUSTOM_MCP_DESCRIPTION_MAX_CHARS = 800
|
|
14
|
+
|
|
15
|
+
CUSTOM_MCP_SCHEMA_FAMILIES = frozenset({"custom", "custom_tool", "mcp", "mcp_tool"})
|
|
16
|
+
MODEL_FACING_SCHEMA_ANNOTATION_KEYS = frozenset(
|
|
17
|
+
{
|
|
18
|
+
"$comment",
|
|
19
|
+
"default",
|
|
20
|
+
"description",
|
|
21
|
+
"example",
|
|
22
|
+
"examples",
|
|
23
|
+
"markdownDescription",
|
|
24
|
+
"title",
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
JSON_SCHEMA_NAMED_SCHEMA_MAP_KEYS = frozenset(
|
|
28
|
+
{
|
|
29
|
+
"$defs",
|
|
30
|
+
"definitions",
|
|
31
|
+
"dependentSchemas",
|
|
32
|
+
"patternProperties",
|
|
33
|
+
"properties",
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
JSON_SCHEMA_OPAQUE_DATA_KEYS = frozenset(
|
|
37
|
+
{
|
|
38
|
+
"const",
|
|
39
|
+
"default",
|
|
40
|
+
"enum",
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class ToolSchemaBudgetEntry:
|
|
47
|
+
name: str
|
|
48
|
+
token_estimate: int
|
|
49
|
+
family: str
|
|
50
|
+
|
|
51
|
+
def to_payload(self) -> dict[str, Any]:
|
|
52
|
+
return {
|
|
53
|
+
"name": self.name,
|
|
54
|
+
"token_estimate": self.token_estimate,
|
|
55
|
+
"family": self.family,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_payload(cls, payload: Any) -> ToolSchemaBudgetEntry | None:
|
|
60
|
+
if not isinstance(payload, dict):
|
|
61
|
+
return None
|
|
62
|
+
name = str(payload.get("name") or "").strip()
|
|
63
|
+
if not name:
|
|
64
|
+
return None
|
|
65
|
+
return cls(
|
|
66
|
+
name=name,
|
|
67
|
+
token_estimate=_as_non_negative_int(payload.get("token_estimate")),
|
|
68
|
+
family=_tool_family(name, payload.get("family")),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class ToolSchemaBudgetReport:
|
|
74
|
+
tool_count: int
|
|
75
|
+
total_tokens: int
|
|
76
|
+
budget_tokens: int
|
|
77
|
+
over_budget_tokens: int
|
|
78
|
+
signature: str
|
|
79
|
+
largest_tools: tuple[ToolSchemaBudgetEntry, ...] = ()
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def over_budget(self) -> bool:
|
|
83
|
+
return self.over_budget_tokens > 0
|
|
84
|
+
|
|
85
|
+
def to_payload(self) -> dict[str, Any]:
|
|
86
|
+
return {
|
|
87
|
+
"tool_count": self.tool_count,
|
|
88
|
+
"total_tokens": self.total_tokens,
|
|
89
|
+
"budget_tokens": self.budget_tokens,
|
|
90
|
+
"over_budget_tokens": self.over_budget_tokens,
|
|
91
|
+
"over_budget": self.over_budget,
|
|
92
|
+
"signature": self.signature,
|
|
93
|
+
"largest_tools": [entry.to_payload() for entry in self.largest_tools],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_payload(cls, payload: Any) -> ToolSchemaBudgetReport | None:
|
|
98
|
+
if not isinstance(payload, dict):
|
|
99
|
+
return None
|
|
100
|
+
signature = str(payload.get("signature") or "").strip()
|
|
101
|
+
entries: list[ToolSchemaBudgetEntry] = []
|
|
102
|
+
raw_entries = payload.get("largest_tools")
|
|
103
|
+
if isinstance(raw_entries, list):
|
|
104
|
+
for raw_entry in raw_entries:
|
|
105
|
+
entry = ToolSchemaBudgetEntry.from_payload(raw_entry)
|
|
106
|
+
if entry is not None:
|
|
107
|
+
entries.append(entry)
|
|
108
|
+
return cls(
|
|
109
|
+
tool_count=_as_non_negative_int(payload.get("tool_count")),
|
|
110
|
+
total_tokens=_as_non_negative_int(payload.get("total_tokens")),
|
|
111
|
+
budget_tokens=_as_non_negative_int(payload.get("budget_tokens")),
|
|
112
|
+
over_budget_tokens=_as_non_negative_int(payload.get("over_budget_tokens")),
|
|
113
|
+
signature=signature,
|
|
114
|
+
largest_tools=tuple(entries),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def analyze_tool_schema_budget(
|
|
119
|
+
tool_list: list[dict[str, Any]] | None,
|
|
120
|
+
*,
|
|
121
|
+
budget_tokens: int = DEFAULT_TOOL_SCHEMA_BUDGET_TOKENS,
|
|
122
|
+
largest_count: int = DEFAULT_LARGEST_TOOL_COUNT,
|
|
123
|
+
) -> ToolSchemaBudgetReport:
|
|
124
|
+
tools = [tool for tool in (tool_list or []) if isinstance(tool, dict)]
|
|
125
|
+
entries: list[ToolSchemaBudgetEntry] = []
|
|
126
|
+
for index, tool in enumerate(tools, start=1):
|
|
127
|
+
name = _tool_name(tool, fallback=f"tool_{index}")
|
|
128
|
+
entries.append(
|
|
129
|
+
ToolSchemaBudgetEntry(
|
|
130
|
+
name=name,
|
|
131
|
+
token_estimate=_estimate_single_tool_schema_tokens(tool),
|
|
132
|
+
family=_tool_family(name, None),
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
total = estimate_tokens(_stable_json(tools)) if tools else 0
|
|
136
|
+
budget = max(0, int(budget_tokens or 0))
|
|
137
|
+
largest_n = max(0, int(largest_count or 0))
|
|
138
|
+
largest_tools = tuple(
|
|
139
|
+
sorted(entries, key=lambda entry: (-entry.token_estimate, entry.name.casefold()))[
|
|
140
|
+
:largest_n
|
|
141
|
+
]
|
|
142
|
+
)
|
|
143
|
+
return ToolSchemaBudgetReport(
|
|
144
|
+
tool_count=len(tools),
|
|
145
|
+
total_tokens=total,
|
|
146
|
+
budget_tokens=budget,
|
|
147
|
+
over_budget_tokens=max(0, total - budget) if budget > 0 else 0,
|
|
148
|
+
signature=tool_schema_signature(tools),
|
|
149
|
+
largest_tools=largest_tools,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def tool_schema_signature(tool_list: list[dict[str, Any]] | None) -> str:
|
|
154
|
+
payload = _stable_json([tool for tool in (tool_list or []) if isinstance(tool, dict)])
|
|
155
|
+
return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def compact_custom_mcp_tool_parameters(
|
|
159
|
+
value: Any,
|
|
160
|
+
*,
|
|
161
|
+
_inside_named_schema_map: bool = False,
|
|
162
|
+
) -> Any:
|
|
163
|
+
"""Drop annotation-only JSON Schema prose from custom/MCP model-facing schemas."""
|
|
164
|
+
if isinstance(value, dict):
|
|
165
|
+
compacted: dict[str, Any] = {}
|
|
166
|
+
for key, item in value.items():
|
|
167
|
+
normalized_key = str(key)
|
|
168
|
+
if not _inside_named_schema_map:
|
|
169
|
+
if normalized_key in MODEL_FACING_SCHEMA_ANNOTATION_KEYS:
|
|
170
|
+
continue
|
|
171
|
+
if normalized_key in JSON_SCHEMA_OPAQUE_DATA_KEYS:
|
|
172
|
+
# enum/const/default hold constraint data, not subschemas.
|
|
173
|
+
compacted[key] = copy.deepcopy(item)
|
|
174
|
+
continue
|
|
175
|
+
compacted[key] = compact_custom_mcp_tool_parameters(
|
|
176
|
+
item,
|
|
177
|
+
_inside_named_schema_map=normalized_key in JSON_SCHEMA_NAMED_SCHEMA_MAP_KEYS,
|
|
178
|
+
)
|
|
179
|
+
return compacted
|
|
180
|
+
if isinstance(value, list):
|
|
181
|
+
return [compact_custom_mcp_tool_parameters(item) for item in value]
|
|
182
|
+
return value
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _estimate_single_tool_schema_tokens(tool: dict[str, Any]) -> int:
|
|
186
|
+
return estimate_tokens(_stable_json(tool))
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _stable_json(value: Any) -> str:
|
|
190
|
+
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _tool_name(tool: dict[str, Any], *, fallback: str) -> str:
|
|
194
|
+
function = tool.get("function")
|
|
195
|
+
if isinstance(function, dict):
|
|
196
|
+
name = str(function.get("name") or "").strip()
|
|
197
|
+
if name:
|
|
198
|
+
return name
|
|
199
|
+
name = str(tool.get("name") or "").strip()
|
|
200
|
+
return name or fallback
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _tool_family(name: str, raw_family: Any) -> str:
|
|
204
|
+
family = str(raw_family or "").strip().lower()
|
|
205
|
+
if family:
|
|
206
|
+
return family
|
|
207
|
+
normalized = str(name or "").strip().lower()
|
|
208
|
+
if normalized.startswith("mcp__") or normalized.startswith("mcp_"):
|
|
209
|
+
return "mcp"
|
|
210
|
+
if normalized.startswith("custom__") or normalized.startswith("custom_"):
|
|
211
|
+
return "custom"
|
|
212
|
+
return "builtin"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _as_non_negative_int(value: Any) -> int:
|
|
216
|
+
try:
|
|
217
|
+
parsed = int(value)
|
|
218
|
+
except (TypeError, ValueError):
|
|
219
|
+
return 0
|
|
220
|
+
return max(0, parsed)
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import math
|
|
5
|
+
import os
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
import warnings
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from datetime import UTC, datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from .error_text import sanitize_error_summary
|
|
15
|
+
from .failure_category import classify_failure_category, extract_status_code
|
|
16
|
+
from .logging_redaction import redact_log_text
|
|
17
|
+
|
|
18
|
+
CRASH_DIAGNOSTIC_SCHEMA_VERSION = 1
|
|
19
|
+
|
|
20
|
+
_PATH_LOCKS_GUARD = threading.Lock()
|
|
21
|
+
_PATH_LOCKS: dict[str, threading.Lock] = {}
|
|
22
|
+
|
|
23
|
+
_ALLOWED_FIELDS = {
|
|
24
|
+
"budget_seconds",
|
|
25
|
+
"checkpoint_fraction",
|
|
26
|
+
"completion_gate_decision",
|
|
27
|
+
"deadline_config_source",
|
|
28
|
+
"deadline",
|
|
29
|
+
"deadline_configured_seconds",
|
|
30
|
+
"deadline_exhausted",
|
|
31
|
+
"deadline_finalization_reason",
|
|
32
|
+
"deadline_finalization_reserve_seconds",
|
|
33
|
+
"deadline_normal_work_remaining_seconds",
|
|
34
|
+
"deadline_phase",
|
|
35
|
+
"deadline_prevented_launch",
|
|
36
|
+
"deadline_remaining_seconds",
|
|
37
|
+
"deadline_start_decision",
|
|
38
|
+
"duration_ms",
|
|
39
|
+
"elapsed_fraction",
|
|
40
|
+
"elapsed_seconds",
|
|
41
|
+
"error_summary",
|
|
42
|
+
"error_type",
|
|
43
|
+
"exit_code",
|
|
44
|
+
"failure_category",
|
|
45
|
+
"grace_seconds",
|
|
46
|
+
"material_edit_count",
|
|
47
|
+
"max_steps",
|
|
48
|
+
"model",
|
|
49
|
+
"operation",
|
|
50
|
+
"provider_status_code",
|
|
51
|
+
"reason",
|
|
52
|
+
"remaining_seconds",
|
|
53
|
+
"runtime_kind",
|
|
54
|
+
"session_source",
|
|
55
|
+
"status",
|
|
56
|
+
"step",
|
|
57
|
+
"steps_attempted",
|
|
58
|
+
"steps_completed",
|
|
59
|
+
# Machine-readable terminal marker. Without this entry the key is silently
|
|
60
|
+
# dropped by the allowlist filter below and a budget stop stays
|
|
61
|
+
# indistinguishable from a crash in the crash log.
|
|
62
|
+
"stop_reason",
|
|
63
|
+
"subagent",
|
|
64
|
+
"subagent_role",
|
|
65
|
+
"subagent_session_id",
|
|
66
|
+
"success",
|
|
67
|
+
"termination_kind",
|
|
68
|
+
"tool_name",
|
|
69
|
+
"usage_counts",
|
|
70
|
+
"verification_attempt_count",
|
|
71
|
+
}
|
|
72
|
+
_NESTED_ALLOWED_FIELDS = {
|
|
73
|
+
"allowed",
|
|
74
|
+
"average_seconds",
|
|
75
|
+
"configured_seconds",
|
|
76
|
+
"count",
|
|
77
|
+
"deadline_monotonic",
|
|
78
|
+
"dispatch_overhead_seconds",
|
|
79
|
+
"duration_observations",
|
|
80
|
+
"elapsed_seconds",
|
|
81
|
+
"enabled",
|
|
82
|
+
"estimated_duration_seconds",
|
|
83
|
+
"exhausted",
|
|
84
|
+
"finalization_directive_sent",
|
|
85
|
+
"finalization_llm_started",
|
|
86
|
+
"finalization_reason",
|
|
87
|
+
"finalization_reserve_seconds",
|
|
88
|
+
"adaptive_retry_llm",
|
|
89
|
+
"compaction_llm",
|
|
90
|
+
"exploration_tool",
|
|
91
|
+
"local_final_summary",
|
|
92
|
+
"main_llm",
|
|
93
|
+
"main_llm_retry",
|
|
94
|
+
"max_seconds",
|
|
95
|
+
"minimum_required_seconds",
|
|
96
|
+
"mutation_tool",
|
|
97
|
+
"normal_work_remaining_seconds",
|
|
98
|
+
"operation",
|
|
99
|
+
"phase",
|
|
100
|
+
"provider_retry_sleep",
|
|
101
|
+
"remaining_seconds",
|
|
102
|
+
"shell_background",
|
|
103
|
+
"shell_tool",
|
|
104
|
+
"source",
|
|
105
|
+
"subagent",
|
|
106
|
+
"tool_dispatch",
|
|
107
|
+
"verification",
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_HIGH_VALUE_EVENTS = {
|
|
111
|
+
"run_started",
|
|
112
|
+
"terminal_error",
|
|
113
|
+
"deadline_exhausted",
|
|
114
|
+
"progress_checkpoint_failed",
|
|
115
|
+
"run_budget_watchdog_fired",
|
|
116
|
+
"run_finished",
|
|
117
|
+
"turn_finished",
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _lock_for_path(path: Path) -> threading.Lock:
|
|
122
|
+
key = os.fspath(path.resolve())
|
|
123
|
+
with _PATH_LOCKS_GUARD:
|
|
124
|
+
lock = _PATH_LOCKS.get(key)
|
|
125
|
+
if lock is None:
|
|
126
|
+
lock = threading.Lock()
|
|
127
|
+
_PATH_LOCKS[key] = lock
|
|
128
|
+
return lock
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _now_ts() -> str:
|
|
132
|
+
return datetime.now(UTC).isoformat()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _safe_scalar(value: Any) -> Any:
|
|
136
|
+
if isinstance(value, bool) or value is None:
|
|
137
|
+
return value
|
|
138
|
+
if isinstance(value, int):
|
|
139
|
+
return value
|
|
140
|
+
if isinstance(value, float):
|
|
141
|
+
return round(value, 6) if math.isfinite(value) else None
|
|
142
|
+
if isinstance(value, str):
|
|
143
|
+
return value[:240]
|
|
144
|
+
if isinstance(value, dict):
|
|
145
|
+
return {
|
|
146
|
+
str(key)[:80]: _safe_scalar(item)
|
|
147
|
+
for key, item in value.items()
|
|
148
|
+
if str(key) in _ALLOWED_FIELDS or str(key) in _NESTED_ALLOWED_FIELDS
|
|
149
|
+
}
|
|
150
|
+
if isinstance(value, (list, tuple)):
|
|
151
|
+
return [_safe_scalar(item) for item in list(value)[:20]]
|
|
152
|
+
return str(value)[:120]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@dataclass
|
|
156
|
+
class CrashDiagnosticLogger:
|
|
157
|
+
path: Path | None
|
|
158
|
+
run_id: str
|
|
159
|
+
session_id: str
|
|
160
|
+
runtime_kind: str = ""
|
|
161
|
+
_started_at_monotonic: float = field(default_factory=time.monotonic)
|
|
162
|
+
write_failures: int = 0
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def disabled(cls) -> CrashDiagnosticLogger:
|
|
166
|
+
return cls(path=None, run_id="", session_id="", runtime_kind="")
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def enabled(self) -> bool:
|
|
170
|
+
return self.path is not None
|
|
171
|
+
|
|
172
|
+
def event(
|
|
173
|
+
self,
|
|
174
|
+
event_type: str,
|
|
175
|
+
payload: dict[str, Any] | None = None,
|
|
176
|
+
*,
|
|
177
|
+
durable: bool = False,
|
|
178
|
+
) -> None:
|
|
179
|
+
if self.path is None:
|
|
180
|
+
return
|
|
181
|
+
event_type = str(event_type or "").strip()
|
|
182
|
+
if not event_type:
|
|
183
|
+
return
|
|
184
|
+
raw_payload = dict(payload or {})
|
|
185
|
+
safe_payload = {
|
|
186
|
+
key: _safe_scalar(value) for key, value in raw_payload.items() if key in _ALLOWED_FIELDS
|
|
187
|
+
}
|
|
188
|
+
if self.runtime_kind and "runtime_kind" not in safe_payload:
|
|
189
|
+
safe_payload["runtime_kind"] = self.runtime_kind
|
|
190
|
+
event = {
|
|
191
|
+
"schema_version": CRASH_DIAGNOSTIC_SCHEMA_VERSION,
|
|
192
|
+
"event_type": event_type,
|
|
193
|
+
"ts": _now_ts(),
|
|
194
|
+
"monotonic_elapsed_seconds": round(
|
|
195
|
+
max(0.0, time.monotonic() - self._started_at_monotonic),
|
|
196
|
+
6,
|
|
197
|
+
),
|
|
198
|
+
"run_id": self.run_id,
|
|
199
|
+
"session_id": self.session_id,
|
|
200
|
+
"payload": safe_payload,
|
|
201
|
+
}
|
|
202
|
+
self._write_line(event, durable=durable or event_type in _HIGH_VALUE_EVENTS)
|
|
203
|
+
|
|
204
|
+
def _write_line(self, event: dict[str, Any], *, durable: bool) -> None:
|
|
205
|
+
assert self.path is not None
|
|
206
|
+
try:
|
|
207
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
208
|
+
line = redact_log_text(json.dumps(event, ensure_ascii=True, sort_keys=True) + "\n")
|
|
209
|
+
lock = _lock_for_path(self.path)
|
|
210
|
+
with lock:
|
|
211
|
+
with self.path.open("a", encoding="utf-8") as fh:
|
|
212
|
+
fh.write(line)
|
|
213
|
+
fh.flush()
|
|
214
|
+
if durable:
|
|
215
|
+
os.fsync(fh.fileno())
|
|
216
|
+
except Exception as exc:
|
|
217
|
+
# A broken diagnostic sink must not crash the run, but it must not be
|
|
218
|
+
# silent either: an autonomous fix-loop that "captured nothing" while
|
|
219
|
+
# believing it captured everything is worse than a loud one-time warning.
|
|
220
|
+
self.write_failures += 1
|
|
221
|
+
if self.write_failures == 1:
|
|
222
|
+
warnings.warn(
|
|
223
|
+
f"crash diagnostic write to {self.path} failed "
|
|
224
|
+
f"({type(exc).__name__}: {exc}); subsequent write failures are counted "
|
|
225
|
+
"in write_failures but not re-warned.",
|
|
226
|
+
RuntimeWarning,
|
|
227
|
+
stacklevel=2,
|
|
228
|
+
)
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def build_crash_diagnostic_logger(
|
|
233
|
+
*,
|
|
234
|
+
path: str | os.PathLike[str] | None,
|
|
235
|
+
run_id: str,
|
|
236
|
+
session_id: str,
|
|
237
|
+
runtime_kind: str = "",
|
|
238
|
+
) -> CrashDiagnosticLogger:
|
|
239
|
+
raw = str(path or "").strip()
|
|
240
|
+
if not raw:
|
|
241
|
+
return CrashDiagnosticLogger.disabled()
|
|
242
|
+
return CrashDiagnosticLogger(
|
|
243
|
+
path=Path(raw),
|
|
244
|
+
run_id=run_id,
|
|
245
|
+
session_id=session_id,
|
|
246
|
+
runtime_kind=runtime_kind,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def build_error_event_fields(
|
|
251
|
+
error: BaseException | str | None,
|
|
252
|
+
*,
|
|
253
|
+
operation: str | None = None,
|
|
254
|
+
step: int | None = None,
|
|
255
|
+
) -> dict[str, Any]:
|
|
256
|
+
"""Build the redacted, joinable error block for a terminal/diagnostic event.
|
|
257
|
+
|
|
258
|
+
The block answers the question an autonomous fix-loop actually needs after a
|
|
259
|
+
failed build — *why* did it die — without ever persisting a secret:
|
|
260
|
+
|
|
261
|
+
- ``error_type``: the exception class name (e.g. ``LLMError``),
|
|
262
|
+
- ``error_summary``: the message, secret-redacted and length-bounded via the
|
|
263
|
+
shared :func:`sanitize_error_summary`,
|
|
264
|
+
- ``failure_category``: a real :class:`FailureCategory` (never the legacy
|
|
265
|
+
``"llm_error"`` literal) so the cause joins across chat/run and Forge artifacts,
|
|
266
|
+
- ``provider_status_code``: the HTTP status when one can be recovered.
|
|
267
|
+
|
|
268
|
+
Safe to call from inside a failure path: it never raises.
|
|
269
|
+
"""
|
|
270
|
+
fields: dict[str, Any] = {
|
|
271
|
+
"error_type": type(error).__name__,
|
|
272
|
+
"error_summary": sanitize_error_summary(str(error or "")),
|
|
273
|
+
"failure_category": classify_failure_category(error).value,
|
|
274
|
+
}
|
|
275
|
+
status_code = extract_status_code(error)
|
|
276
|
+
if status_code is not None:
|
|
277
|
+
fields["provider_status_code"] = status_code
|
|
278
|
+
if operation:
|
|
279
|
+
fields["operation"] = str(operation)
|
|
280
|
+
if step is not None:
|
|
281
|
+
fields["step"] = int(step)
|
|
282
|
+
return fields
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Custom Tools
|
|
2
|
+
|
|
3
|
+
This package implements Alysis Code's single-file Python custom tools. A custom
|
|
4
|
+
tool is user- or workspace-provided code with a literal manifest and a
|
|
5
|
+
`run(args)` entrypoint.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- `discovery.py` validates candidate tool files without importing them.
|
|
10
|
+
- `trust.py` records project-tool trust by workspace, relative path, and file
|
|
11
|
+
hash.
|
|
12
|
+
- `session.py` builds the effective custom-tool catalog for a session.
|
|
13
|
+
- `runtime.py` executes trusted tools and applies declared capability checks.
|
|
14
|
+
|
|
15
|
+
## Scope
|
|
16
|
+
|
|
17
|
+
Project tools are not trusted by default. Editing or moving a trusted project
|
|
18
|
+
tool invalidates trust.
|
|
19
|
+
|
|
20
|
+
Discovery must remain import-free. Candidate code should not run while the host
|
|
21
|
+
is deciding whether the tool is valid or trusted.
|
|
22
|
+
|
|
23
|
+
Capability checks are not a complete sandbox. Treat custom tools as trusted
|
|
24
|
+
code and keep the documentation precise about that boundary.
|
|
25
|
+
|
|
26
|
+
## Development
|
|
27
|
+
|
|
28
|
+
Manifest, trust, and runtime changes should test valid and rejected tools, plus
|
|
29
|
+
the structured errors users will see.
|
|
30
|
+
|
|
31
|
+
## See Also
|
|
32
|
+
|
|
33
|
+
- [Custom tools](../../../docs/custom_tools.md)
|
|
34
|
+
- [Security model](../../../docs/security_model.md)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from .discovery import (
|
|
2
|
+
CustomToolCapabilities,
|
|
3
|
+
CustomToolDiscoveryResult,
|
|
4
|
+
CustomToolIssue,
|
|
5
|
+
CustomToolSpec,
|
|
6
|
+
discover_custom_tools,
|
|
7
|
+
global_custom_tools_root,
|
|
8
|
+
project_custom_tools_root,
|
|
9
|
+
)
|
|
10
|
+
from .runtime import run_custom_tool
|
|
11
|
+
from .session import (
|
|
12
|
+
CustomToolCatalogEntry,
|
|
13
|
+
CustomToolSessionState,
|
|
14
|
+
build_custom_tool_session_state,
|
|
15
|
+
)
|
|
16
|
+
from .trust import (
|
|
17
|
+
ProjectToolTrustKey,
|
|
18
|
+
ProjectToolTrustState,
|
|
19
|
+
is_project_tool_trusted,
|
|
20
|
+
load_trust_state,
|
|
21
|
+
trust_project_tool,
|
|
22
|
+
untrust_project_tool,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"CustomToolCatalogEntry",
|
|
27
|
+
"CustomToolCapabilities",
|
|
28
|
+
"CustomToolDiscoveryResult",
|
|
29
|
+
"CustomToolIssue",
|
|
30
|
+
"CustomToolSessionState",
|
|
31
|
+
"CustomToolSpec",
|
|
32
|
+
"ProjectToolTrustKey",
|
|
33
|
+
"ProjectToolTrustState",
|
|
34
|
+
"build_custom_tool_session_state",
|
|
35
|
+
"discover_custom_tools",
|
|
36
|
+
"global_custom_tools_root",
|
|
37
|
+
"is_project_tool_trusted",
|
|
38
|
+
"load_trust_state",
|
|
39
|
+
"project_custom_tools_root",
|
|
40
|
+
"run_custom_tool",
|
|
41
|
+
"trust_project_tool",
|
|
42
|
+
"untrust_project_tool",
|
|
43
|
+
]
|