devops-bot-sdk 1.6.40__tar.gz → 1.6.41__tar.gz
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.
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/devops_bot_sdk.egg-info/SOURCES.txt +1 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/__init__.py +2 -2
- devops_bot_sdk-1.6.41/tests/test_mcp_route_contract.py +209 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/README.md +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/cli.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/client.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/api.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/app.js +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/tests/test_write_hook.py +0 -0
|
@@ -161,6 +161,7 @@ tests/test_intake_job_outcome.py
|
|
|
161
161
|
tests/test_intake_plan_review.py
|
|
162
162
|
tests/test_journal.py
|
|
163
163
|
tests/test_local_exec_provider.py
|
|
164
|
+
tests/test_mcp_route_contract.py
|
|
164
165
|
tests/test_memory_guard.py
|
|
165
166
|
tests/test_phase_timer.py
|
|
166
167
|
tests/test_plan_new_command.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.41
|
|
4
4
|
|
|
5
5
|
Public surface:
|
|
6
6
|
BackendClient.from_config() — create client from ~/.agentos/config.toml
|
|
@@ -34,7 +34,7 @@ Rules:
|
|
|
34
34
|
- All data egress through submit_webhook only
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
__version__ = "1.6.
|
|
37
|
+
__version__ = "1.6.41" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
|
|
38
38
|
__author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
|
|
39
39
|
|
|
40
40
|
from sdk.client import BackendClient
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""The `/api/ui/*` routes an external MCP client drives, frozen as a contract.
|
|
2
|
+
|
|
3
|
+
``devops-bot-ai`` exposes 17 of this panel's routes as Model Context Protocol
|
|
4
|
+
tools (`modules/mcp/catalog.py`, design in that repo's
|
|
5
|
+
`docs/PRD_MCP_VPS_TOOLS.md`). Its catalogue hard-codes the bare path suffix, the
|
|
6
|
+
HTTP verb and the query-parameter NAMES of each one, because ``_call_sdk`` builds
|
|
7
|
+
``f"{sdk_url}/api/ui/{path}"`` and forwards ``params`` verbatim.
|
|
8
|
+
|
|
9
|
+
That makes an ordinary, reasonable-looking rename here — ``loop-status`` to
|
|
10
|
+
``loop_status``, ``lines`` to ``limit``, ``GET /tasks`` to ``GET /task-files`` —
|
|
11
|
+
a production break in a DIFFERENT repository, per user, with no failing test on
|
|
12
|
+
either side. The panel's own UI would be updated in the same commit and look
|
|
13
|
+
fine; the first symptom would be an AI agent telling someone their VPS cannot be
|
|
14
|
+
reached.
|
|
15
|
+
|
|
16
|
+
So this file is a tripwire, not a behaviour test. It asserts only what the remote
|
|
17
|
+
catalogue actually depends on. Changing one of these is allowed — it is a
|
|
18
|
+
coordinated change with `modules/mcp/catalog.py`, and this test failing by name
|
|
19
|
+
is how you find that out before shipping.
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import pytest
|
|
24
|
+
|
|
25
|
+
fastapi = pytest.importorskip("fastapi")
|
|
26
|
+
|
|
27
|
+
from sdk import config # noqa: E402
|
|
28
|
+
from sdk.ui import security # noqa: E402
|
|
29
|
+
from sdk.ui.api import require_token, router # noqa: E402
|
|
30
|
+
|
|
31
|
+
#: (method, path) exactly as `modules/mcp/catalog.py` spells it, `/api/ui/`
|
|
32
|
+
#: prefix stripped. 10 reads then 7 writes, matching the catalogue's own order.
|
|
33
|
+
MCP_CONSUMED_ROUTES: tuple[tuple[str, str], ...] = (
|
|
34
|
+
("GET", "/health"),
|
|
35
|
+
("GET", "/status"),
|
|
36
|
+
("GET", "/loop-status"),
|
|
37
|
+
("GET", "/logs"),
|
|
38
|
+
("GET", "/system/inventory"),
|
|
39
|
+
("GET", "/dashboard"),
|
|
40
|
+
("GET", "/jobs/{job_id}"),
|
|
41
|
+
("GET", "/agents/all"),
|
|
42
|
+
("GET", "/tasks"),
|
|
43
|
+
("GET", "/agents/schedules"),
|
|
44
|
+
("POST", "/agents/run"),
|
|
45
|
+
("POST", "/agents/chat"),
|
|
46
|
+
("POST", "/run"),
|
|
47
|
+
("POST", "/resume"),
|
|
48
|
+
("POST", "/stop"),
|
|
49
|
+
("POST", "/git"), # both open_vps_pull_request and push_vps_branch
|
|
50
|
+
("POST", "/git"),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
#: Query parameters the remote catalogue sends BY NAME. A rename here is
|
|
54
|
+
#: silently dropped by FastAPI (the default is used instead), so the tool keeps
|
|
55
|
+
#: "working" while ignoring everything the model asked for — the worst failure
|
|
56
|
+
#: shape available, because nothing errors.
|
|
57
|
+
MCP_QUERY_PARAMS: tuple[tuple[str, str, object], ...] = (
|
|
58
|
+
("/logs", "lines", 200),
|
|
59
|
+
("/dashboard", "period", "7d"),
|
|
60
|
+
("/agents/all", "department_id", ""),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
#: The router carries its own `/api/ui` prefix, so `route.path` is the full
|
|
65
|
+
#: path. The table above holds the bare suffix the remote catalogue stores.
|
|
66
|
+
_PREFIX = router.prefix
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _routes() -> dict[tuple[str, str], object]:
|
|
70
|
+
"""(method, bare suffix) -> route, with the `/api/ui` prefix stripped."""
|
|
71
|
+
out = {}
|
|
72
|
+
for route in router.routes:
|
|
73
|
+
path = getattr(route, "path", "")
|
|
74
|
+
suffix = path[len(_PREFIX):] if path.startswith(_PREFIX) else path
|
|
75
|
+
for method in getattr(route, "methods", set()) or set():
|
|
76
|
+
out[(method, suffix)] = route
|
|
77
|
+
return out
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _client():
|
|
81
|
+
app = fastapi.FastAPI()
|
|
82
|
+
app.include_router(router)
|
|
83
|
+
app.dependency_overrides[require_token] = lambda: None
|
|
84
|
+
from fastapi.testclient import TestClient
|
|
85
|
+
|
|
86
|
+
return TestClient(app)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TestRoutesExist:
|
|
90
|
+
@pytest.mark.parametrize("method,path", sorted(set(MCP_CONSUMED_ROUTES)))
|
|
91
|
+
def test_the_route_is_still_served_at_this_exact_spelling(self, method, path):
|
|
92
|
+
assert (method, path) in _routes(), (
|
|
93
|
+
f"{method} /api/ui{path} is consumed as an MCP tool by devops-bot-ai "
|
|
94
|
+
f"(modules/mcp/catalog.py). Renaming or removing it breaks that tool "
|
|
95
|
+
f"in production with no error on either side — update the catalogue "
|
|
96
|
+
f"in the same change."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def test_the_count_has_not_silently_changed(self):
|
|
100
|
+
"""17 tools, 16 distinct routes — the two git tools share POST /git."""
|
|
101
|
+
assert len(MCP_CONSUMED_ROUTES) == 17
|
|
102
|
+
assert len(set(MCP_CONSUMED_ROUTES)) == 16
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class TestQueryParameterNames:
|
|
106
|
+
@pytest.mark.parametrize("path,name,default", MCP_QUERY_PARAMS)
|
|
107
|
+
def test_the_parameter_name_and_default_are_unchanged(self, path, name, default):
|
|
108
|
+
route = _routes()[("GET", path)]
|
|
109
|
+
params = {
|
|
110
|
+
p.name: p for p in route.dependant.query_params # type: ignore[attr-defined]
|
|
111
|
+
}
|
|
112
|
+
assert name in params, (
|
|
113
|
+
f"GET /api/ui{path} no longer accepts '{name}'. The MCP catalogue "
|
|
114
|
+
f"sends it by name; FastAPI drops an unknown query param silently, "
|
|
115
|
+
f"so the tool would keep answering while ignoring its arguments."
|
|
116
|
+
)
|
|
117
|
+
assert params[name].default == default
|
|
118
|
+
|
|
119
|
+
def test_the_logs_bound_the_catalogue_publishes_is_the_one_enforced(self):
|
|
120
|
+
"""The remote JSON Schema advertises 1..5000 to the model. If the route
|
|
121
|
+
narrows that, a model asking for a legal value gets a 422 it cannot
|
|
122
|
+
interpret."""
|
|
123
|
+
client = _client()
|
|
124
|
+
assert client.get("/api/ui/logs?lines=5000").status_code != 422
|
|
125
|
+
assert client.get("/api/ui/logs?lines=5001").status_code == 422
|
|
126
|
+
assert client.get("/api/ui/logs?lines=0").status_code == 422
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class TestJobPolling:
|
|
130
|
+
def test_the_job_id_is_a_path_parameter_named_job_id(self):
|
|
131
|
+
"""`get_vps_job` interpolates the id into the path, and validates it
|
|
132
|
+
against `^[0-9a-f]{32}$` precisely because it lands there."""
|
|
133
|
+
route = _routes()[("GET", "/jobs/{job_id}")]
|
|
134
|
+
names = {p.name for p in route.dependant.path_params} # type: ignore[attr-defined]
|
|
135
|
+
assert names == {"job_id"}
|
|
136
|
+
|
|
137
|
+
def test_job_ids_are_still_32_char_lowercase_hex(self):
|
|
138
|
+
"""The remote side validates the shape before interpolating it. A change
|
|
139
|
+
to uuid4() *with* dashes, or to an incrementing integer, would make every
|
|
140
|
+
legitimate poll fail argument validation before it ever left that repo."""
|
|
141
|
+
from sdk.ui import jobs
|
|
142
|
+
|
|
143
|
+
async def _noop():
|
|
144
|
+
return {"ok": True}
|
|
145
|
+
|
|
146
|
+
import asyncio
|
|
147
|
+
|
|
148
|
+
async def _run():
|
|
149
|
+
job_id = jobs.start(_noop())
|
|
150
|
+
await asyncio.sleep(0)
|
|
151
|
+
return job_id
|
|
152
|
+
|
|
153
|
+
job_id = asyncio.run(_run())
|
|
154
|
+
assert len(job_id) == 32
|
|
155
|
+
assert job_id == job_id.lower()
|
|
156
|
+
assert all(c in "0123456789abcdef" for c in job_id)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class TestBodylessStop:
|
|
160
|
+
def test_stop_accepts_a_request_with_no_body_at_all(self):
|
|
161
|
+
"""`action_router._build_empty` returns None and the proxy sends
|
|
162
|
+
`json=None`. A required request model here would 422 every stop."""
|
|
163
|
+
route = _routes()[("POST", "/stop")]
|
|
164
|
+
assert route.dependant.body_params == [] # type: ignore[attr-defined]
|
|
165
|
+
assert _client().post("/api/ui/stop").status_code != 422
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class TestEveryConsumedRouteIsStillTokenGated:
|
|
169
|
+
@pytest.mark.parametrize("method,path", sorted(set(MCP_CONSUMED_ROUTES)))
|
|
170
|
+
def test_the_route_still_requires_the_api_token(self, method, path):
|
|
171
|
+
"""These are now reachable by an external product, not just the panel's
|
|
172
|
+
own page. A route that loses `_auth` becomes readable by anything that
|
|
173
|
+
can reach port 8765 from an allowlisted address."""
|
|
174
|
+
route = _routes()[(method, path)]
|
|
175
|
+
names = {
|
|
176
|
+
getattr(d.call, "__name__", "") for d in route.dependant.dependencies # type: ignore[attr-defined]
|
|
177
|
+
}
|
|
178
|
+
assert "require_token" in names, f"{method} /api/ui{path} is no longer token-gated"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class TestTokenTransport:
|
|
182
|
+
def test_a_bearer_prefixed_token_is_accepted(self):
|
|
183
|
+
"""The MCP consumer (`ai-draftsman-ml-b2b`) sends `Authorization: Bearer
|
|
184
|
+
co_…` by default — it does not know about this panel's bare-token
|
|
185
|
+
convention, and the backend forwards the header as it received it."""
|
|
186
|
+
assert security.extract_token({"authorization": "Bearer co_abc_123"}, {}) == "co_abc_123"
|
|
187
|
+
|
|
188
|
+
def test_a_bare_token_is_still_accepted(self):
|
|
189
|
+
"""The backend's own proxies forward the caller's token verbatim, with
|
|
190
|
+
no prefix. Both forms must keep working — this is the one place they
|
|
191
|
+
meet."""
|
|
192
|
+
assert security.extract_token({"authorization": "co_abc_123"}, {}) == "co_abc_123"
|
|
193
|
+
|
|
194
|
+
def test_the_prefix_match_is_case_insensitive(self):
|
|
195
|
+
assert security.extract_token({"authorization": "bearer co_abc_123"}, {}) == "co_abc_123"
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class TestBackendReachability:
|
|
199
|
+
def test_the_backend_egress_ips_stay_permanently_allowlisted(self):
|
|
200
|
+
"""Every MCP tool call arrives FROM the backend, so these addresses are
|
|
201
|
+
the whole path. They are permanent (not removable through the API) on
|
|
202
|
+
purpose: a user disabling one through the panel would cut off a product
|
|
203
|
+
integration they never configured from here and cannot see."""
|
|
204
|
+
permanent = config.permanent_allowed_ips()
|
|
205
|
+
for ip in config._DEFAULT_ALLOWED_IPS:
|
|
206
|
+
assert ip in permanent, (
|
|
207
|
+
f"{ip} is a devops-bot-ai egress address; dropping it stops every "
|
|
208
|
+
f"backend-driven call, including all MCP tools."
|
|
209
|
+
)
|
|
File without changes
|
{devops_bot_sdk-1.6.40 → devops_bot_sdk-1.6.41}/devops_bot_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|