talkops-opscloud 0.3.0b7__tar.gz → 0.3.0b9__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.
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/PKG-INFO +1 -1
- talkops_opscloud-0.3.0b9/docs/issues/MCP_PERFORMANCE_AND_CONTEXT_EXPLOSION_AUDIT.md +275 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/pyproject.toml +1 -1
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/agent/factory.py +41 -10
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/discovery.py +14 -4
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/preload.py +51 -30
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/session_manager.py +49 -28
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/server_hooks.py +1 -1
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/tool_filter.py +185 -98
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/adapters/mcp.py +1 -1
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/models.py +10 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/app.py +231 -10
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/remote_client.py +42 -18
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/textual_adapter.py +43 -12
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/chat_input.py +48 -3
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/messages.py +13 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_mcp.py +17 -0
- talkops_opscloud-0.3.0b9/tests/unit_tests/test_plugin_agent_skill_mcp_scoping.py +498 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_tool_filter.py +133 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_tui_app.py +141 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/uv.lock +1 -1
- talkops_opscloud-0.3.0b7/tests/unit_tests/test_plugin_agent_skill_mcp_scoping.py +0 -228
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/.dockerignore +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/.env.example +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/.github/workflows/ci.yml +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/.github/workflows/release-pypi.yml +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/.gitignore +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/.python-version +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/LICENSE +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/README.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/assets/opscloud-banner.gif +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/Configuration.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/approval-mode.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/cli-reference.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/config.toml.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/credentials.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/goal-and-rubrics.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/hooks.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/mcp-tools.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/memory-and-skills.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/model-providers.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/overview.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/plugins.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/quickstart.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/remote-sandboxes.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/docs/opscloud-docs/subagents.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/scripts/install.sh +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/__main__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/_constants.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/_debug.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/_debug_buffer.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/_textual_patches.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/_version.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/agent/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/agent/config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/approval_mode.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/backend/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/backend/composite.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/backend/local.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/backend/registry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/backend/sandbox_config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/backend/sandbox_factory.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/built_in_skills/remember/SKILL.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/built_in_skills/skill-creator/SKILL.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/commands/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/commands/auth.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/commands/doctor.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/commands/mcp.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/commands/threads.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/interactive.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/main.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/non_interactive.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/cli/server_manager.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/_base.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/_guards.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/_router.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/_types.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/auth.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/bug.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/clear.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/cloud.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/compact.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/config_cmd.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/context.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/cost.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/doctor.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/effort.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/exit_cmd.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/fast.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/help_cmd.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/mcp.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/model.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/permissions.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/plugins.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/pool.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/resume.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/core/skills.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/agents.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/btw.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/copy.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/goal.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/loop.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/memory.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/review.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/rubric.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/runtime.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/skill_creator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/skill_invoke.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/tasks.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/trace.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/ui_toggles.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/power/version.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/commands/registry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/adapters/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/adapters/postgres.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/adapters/sqlite.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/aws.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/cloud_profiles.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/env_vars.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/langsmith.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/manifest.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/metadata.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/paths.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/plugins.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/settings.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/store.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/store_factory.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/config/toml_config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/editor.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/exceptions.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/file_ops.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/env.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/interrupt.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/models/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/models/adapters.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/models/config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/models/domain.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/models/transport.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/hooks/tools.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/input.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/base.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/content_filter.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/event_bus.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/hooks.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/identity.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/notifications.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/stream_bridge.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/integrations/thread_store.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/json_types.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/mcp_info.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/middleware.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/raw_config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/semantic_profiler.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/mcp/trust.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/memory/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/memory/branch.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/memory/guard.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/memory/onboarding.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/memory/registry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/memory/store.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/_repository_bounds.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/ask_user.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/auto_mode.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/auto_mode_hitl.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/compaction.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/configurable_model.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/cost_tracking.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/glm_stall_recovery.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/goal_criteria.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/goal_state_notice.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/goal_tools.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/headless_mcp_guard.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/jev_model_router.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/local_context.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/mcp_context.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/mcp_middleware.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/memory_guard.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/model_retry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/registry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/reliable_rubric.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/resume_state.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/shell_allow_list.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/skills.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/subagent_artifacts.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/subagent_telemetry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/subagents.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/unified_system_message.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/model/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/model/config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/model/factory.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/model/pool.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/model/reasoning.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/offload.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/output.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/_json.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/adapters/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/adapters/agents.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/adapters/commands.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/adapters/skills.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/commands_cli.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/discovery.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/manifest.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/marketplace.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/project_plugins.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/protocol.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/store.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/plugins/substitution.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/project_utils.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/prompts/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/prompts/system.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/prompts/templates/system_prompt.md +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/prompts/types.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/rubrics/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/rubrics/evaluator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/rubrics/evidence_extractor.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/rubrics/generator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/rubrics/jev_compiler.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/rubrics/jev_grader.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/rubrics/middleware.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/schema/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/schema/interrupts.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/approval_mode.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/approval_mode_source.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/cli_ast_evaluator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/jev_classifier.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/shell_safety.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/unicode_security.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/security/url_validation.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/server/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/server/_server_config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/server/server.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/server/server_graph.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/commands.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/invocation.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/load.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/loader.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/merge.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/registry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/sources.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/skills/trust.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/base.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/cloud_context.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/goal_channels.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/goal_state_limits.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/resume_state.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/service_context.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/session.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/state/state_migration.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/subagents/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/subagents/loader.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/subagents/subagents_parser.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/subagents/types.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/catalog.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/display.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/fetch_url.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/goal_tools.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/registry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/thread.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/tools/web_search.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/app.tcss +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/clipboard.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/command_registry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/message_store.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/permission_store.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/preferences.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/prompt_manager.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/theme.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/ui_help.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/_ask_user_types.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/_copy_spans.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/_inline_prompt.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/_js_eval_display.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/_links.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/_tool_stream.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/agent_selector.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/approval.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/ask_user.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/auth.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/auth_manager.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/auto_mode_notice.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/autocomplete.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/cloud_selector.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/config_manager.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/debug_console.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/devops_renderers.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/diff.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/effort_selector.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/goal_review.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/goal_status.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/infra_panel.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/install_confirm.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/loading.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/mcp_viewer.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/model_selector.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/notification_center.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/notification_settings.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/operation_card.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/permissions_manager.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/plugin_manager.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/pool_selector.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/preamble.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/skills_viewer.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/status.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/subagent_panel.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/theme_selector.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/thread_selector.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/toast.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/tool_display.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/tool_renderers.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/tool_widgets.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/welcome.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/ui/widgets/welcome_popup.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/utils/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/utils/cost_estimation.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/utils/git.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/utils/logger.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/utils/session_stats.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/utils/startup_error.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/conftest.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/evals/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/evals/datasets/aws_cloud_ops.yaml +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/evals/evaluators/__init__.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/evals/evaluators/output_evaluator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/evals/evaluators/trajectory_evaluator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/evals/runners/test_eval_runner.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/integration_tests/test_agent_lifecycle.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/integration_tests/test_server_lifecycle.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_agent_factory.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_approval_widget.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_ask_user.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_ast_evaluator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_backend.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_chat_input_responsiveness.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_checkpointer.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_claude_opus_reasoning.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_cli_enhancements.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_cli_rubric.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_cloud_command.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_commands.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_config.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_cost_tracking.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_default_marketplace.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_goal_criteria.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_goal_state_limits.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_goal_state_notice.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_grader_tools.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_hitl.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_integrations_and_hooks.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_interrupt_schemas.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_jev_model_router.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_jev_rubrics.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_local_context.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_logging.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_mcp_middleware.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_mcp_viewer_and_command.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_memory.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_memory_enhancements.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_middleware_stack.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_model_providers.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_model_retry.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_plugin_manager_search.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_plugins.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_pool_persistence.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_project_plugins.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_prompts_and_schema.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_reliable_rubric.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_reload_restart.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_resume_state.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_resumed_thread_latency_fix.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_rubrics.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_security.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_server.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_server_hooks.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_skill_creator.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_skills.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_state.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_subagents.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_tools.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/tests/unit_tests/test_utils.py +0 -0
- {talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/uv-requirements.txt +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Engineering Issue & Architectural Audit: MCP Tool Schema Explosion & Extreme Latency in Plugin Skills Flow
|
|
2
|
+
|
|
3
|
+
**Issue ID:** `ISSUE-OPSCLOUD-MCP-001`
|
|
4
|
+
**Date:** October 8, 2026
|
|
5
|
+
**Severity:** **P0 — Blocker**
|
|
6
|
+
**Component:** Deep Agent Core / Subagent Flow / MCP Session Manager / Plugin Adapters
|
|
7
|
+
**Target Environments:** `src/opscloud` (OpsCloud Orchestrator) vs. `reference/dcode` (Reference Implementation)
|
|
8
|
+
**Authors:** AI Architecture & Core Systems Audit
|
|
9
|
+
**Audience:** Core Engineering Team, Agent Platform Engineers, DevOps & Infrastructure
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Executive Summary & Impact Analysis
|
|
14
|
+
|
|
15
|
+
During testing of skill plugins equipped with MCP (Model Context Protocol) servers (e.g., `aws-containers@talkops-devops-plugins`), the Deep Agent experienced catastrophic response latency and token usage degradation:
|
|
16
|
+
|
|
17
|
+
* **OpsCloud Turn 1 Performance:** **315.79 seconds (5.22 minutes)** total turn duration, processing **211,300 input tokens** ($0.1531 cost) on a simple user command.
|
|
18
|
+
* **Reference `dcode` Performance:** **15.10s P50** across 4 turns (Turn 1: 0.73s, Turn 2: 25.39s [45.37K tokens], Turn 3: 15.09s [46.6K tokens], Turn 4: 40.15s [254.8K tokens]).
|
|
19
|
+
* **Discrepancy Ratio:** **~21x Latency Explosion**, **~4.7x Initial Prompt Context Explosion**.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌───────────────────────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ INITIAL TURN LATENCY & CONTEXT COMPARISON │
|
|
24
|
+
├──────────────────┬─────────────────────────────┬──────────────────────────────┤
|
|
25
|
+
│ Metric │ OpsCloud (Current) │ reference/dcode (Benchmark) │
|
|
26
|
+
├──────────────────┼─────────────────────────────┼──────────────────────────────┤
|
|
27
|
+
│ Turn 1 Latency │ 315.79s (5m 15s) ⚠️ BLOCKER │ 0.73s - 25.39s (P50: 15.10s) │
|
|
28
|
+
│ Turn 1 Tokens │ 211,300 tokens (Context full)│ ~45,370 tokens │
|
|
29
|
+
│ Tool Injection │ Eager / Global / Unscoped │ Scoped / Filtered │
|
|
30
|
+
│ Schema Strategy │ Recursive $ref Dereference │ Protocol Native (Compact) │
|
|
31
|
+
│ Session / Loop │ Ephemeral Thread Loop + Stdio│ Server Loop Router Client │
|
|
32
|
+
└──────────────────┴─────────────────────────────┴──────────────────────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The system became completely unresponsive for 5+ minutes because **the entire LLM context window was inundated with over 211K tokens of recursively inlined JSON schemas for dozens of un-scoped Kubernetes/AWS tools**, which then had to be re-evaluated across every internal graph step over disjointed stdio subprocess loops.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. Trace & Incident Evidence Breakdown
|
|
40
|
+
|
|
41
|
+
### 2.1 OpsCloud Trace (Image 1: `media_1791459244211.png`)
|
|
42
|
+
* **Execution Status:** 1 Turn, Duration: **315.79s**, Context: **211.3K tokens**, Cost: **$0.1531**.
|
|
43
|
+
* **Observed Bound Tools:**
|
|
44
|
+
* Core: `create_temp_artifact`, `delete_temp_artifact`, `fetch_url`.
|
|
45
|
+
* Finch MCP: `plugin__aws-containers_ta_finch_build_container_ima_df47730a85e8`, `plugin__aws-containers_talkops-dev_finch_push_image_6b02bcde1560`, `plugin__aws-containers_talkop_finch_create_ecr_repo_a8f88f0a1f9f`.
|
|
46
|
+
* EKS / Kubernetes MCP: `plugin__aws-containers_talkops-d_list_k8s_resources_5aa672efc864`, `plugin__aws-containers_talkops-devops-_get_pod_logs_a03a76775bc1`, `plugin__aws-containers_talkops-devop_get_k8s_events_465d1e587238`, `plugin__aws-containers_talkops-de_list_api_versions_c0a261010b5e`, `plugin__aws-containers_talkops-_manage_k8s_resource_b877895269a7`, `plugin__aws-containers_talkops-devops-pl_apply_yaml_34e08bc5a89a`, `plugin__aws-containers_talkop_generate_app_manifest_555c8188f2bb`...
|
|
47
|
+
* **Symptom:** Every single tool from all MCP servers declared in the plugin was injected directly into the main agent's LLM prompt, expanding schemas recursively.
|
|
48
|
+
|
|
49
|
+
### 2.2 Reference `dcode` Trace (Image 2: `media_1791459309416.png`)
|
|
50
|
+
* **Execution Status:** 4 Turns, P50: **15.10s**, Cost: **$0.1014**.
|
|
51
|
+
* Turn 1: **0.73s** (Goal initialization)
|
|
52
|
+
* Turn 2: **25.39s** (45.37K tokens)
|
|
53
|
+
* Turn 3: **15.09s** (46.6K tokens)
|
|
54
|
+
* Turn 4: **40.15s** (254.8K tokens after 4 rounds of conversation history accumulation)
|
|
55
|
+
* **Observed Bound Tools:**
|
|
56
|
+
* Core: `update_goal`, `ask_user`, `js_eval`, `create_temp_artifact`, `delete_temp_artifact`, `fetch_url`, `get_current_thread_id`.
|
|
57
|
+
* Compute MCP (`aws-compute`): Only 6 focused tools (`aws__get_regional_availa`, `aws__search_documentatio`, `aws__read_documentation`, `aws__retrieve_skill`, `aws__list_regions`, `aws__get_tasks`).
|
|
58
|
+
* **Symptom:** Fast execution, minimal initial token overhead, and precise tool availability.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 3. Deep Root-Cause Analysis (Forensic Audit)
|
|
63
|
+
|
|
64
|
+
Our cross-codebase audit identified **5 compounding architectural flaws** in OpsCloud that do not exist in `reference/dcode`.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### Root Cause 1: Recursive `$ref` Dereferencing in `_clean_mcp_schema`
|
|
69
|
+
**Location:** [`src/opscloud/mcp/session_manager.py:68–85`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/mcp/session_manager.py#L68-L85)
|
|
70
|
+
|
|
71
|
+
#### What OpsCloud Does:
|
|
72
|
+
```python
|
|
73
|
+
def _clean_mcp_schema(schema: Any) -> dict[str, Any]:
|
|
74
|
+
if not isinstance(schema, dict):
|
|
75
|
+
return {}
|
|
76
|
+
|
|
77
|
+
from langchain_core.utils.json_schema import dereference_refs
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
resolved = dereference_refs(schema)
|
|
81
|
+
except Exception as exc:
|
|
82
|
+
logger.debug("Failed to dereference MCP schema refs: %s", exc)
|
|
83
|
+
resolved = schema
|
|
84
|
+
|
|
85
|
+
cleaned = dict(resolved) if isinstance(resolved, dict) else dict(schema)
|
|
86
|
+
for key in ("$schema", "$id", "$defs", "definitions", "additionalProperties"):
|
|
87
|
+
cleaned.pop(key, None)
|
|
88
|
+
...
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### Why This Explodes Context to 211.3K Tokens:
|
|
92
|
+
* When `awslabs.eks-mcp-server` loads, tools like `apply_yaml`, `manage_k8s_resource`, and `generate_app_manifest` define parameters that accept complete Kubernetes manifests.
|
|
93
|
+
* The underlying JSON schema contains deep OpenAPI definitions (`#/$defs/ObjectMeta`, `#/$defs/PodSpec`, `#/$defs/Container`, `#/$defs/EnvVar`, etc.) with multiple recursive and cross-referencing branches.
|
|
94
|
+
* `dereference_refs()` replaces every `{"$ref": ...}` pointer with a **deep, full copy** of the referenced definition.
|
|
95
|
+
* If a single schema references sub-models 15 times, inlining them causes exponential duplication. A single tool's schema grows from a compact 1.5KB reference graph into a **60KB–100KB JSON behemoth**.
|
|
96
|
+
* Multiplying this by 10+ Kubernetes tools in `aws-containers` results in **150,000+ tokens generated from tool schema definitions alone**.
|
|
97
|
+
|
|
98
|
+
#### What `reference/dcode` Does Differently:
|
|
99
|
+
**Location:** [`reference/dcode/mcp_tools.py:1514–1520`](file:///Users/structbinary/Documents/work/talkops/opscloud/reference/dcode/mcp_tools.py#L1514-L1520)
|
|
100
|
+
* `dcode` passes the raw MCP schema directly to `langchain.mcp.as_langchain_tool(routed_tool, client)`.
|
|
101
|
+
* It **never calls `dereference_refs`**. The schema remains compact and standard, allowing the LLM provider's API client to handle references natively without exponential duplication.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Root Cause 2: Global Eager Tool Injection of Vertical Plugins into Main Coordinator Agent
|
|
106
|
+
**Locations:**
|
|
107
|
+
* [`src/opscloud/plugins/models.py:174–185`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/plugins/models.py#L174-L185)
|
|
108
|
+
* [`src/opscloud/plugins/adapters/mcp.py:200–203`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/plugins/adapters/mcp.py#L200-L203)
|
|
109
|
+
* [`src/opscloud/agent/factory.py:604–660`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/agent/factory.py#L604-L660)
|
|
110
|
+
|
|
111
|
+
#### The Fault Chain:
|
|
112
|
+
1. In `src/opscloud/plugins/models.py`:
|
|
113
|
+
```python
|
|
114
|
+
@property
|
|
115
|
+
def is_agent_plugin(self) -> bool:
|
|
116
|
+
"""Return True if this plugin provides autonomous agents (agent-based plugin)."""
|
|
117
|
+
if self.inventory.agents:
|
|
118
|
+
...
|
|
119
|
+
if self.root and isinstance(self.root, Path):
|
|
120
|
+
agents_dir = self.root / "agents"
|
|
121
|
+
return agents_dir.is_dir() and any(agents_dir.glob("*.md"))
|
|
122
|
+
return False
|
|
123
|
+
```
|
|
124
|
+
2. In `src/opscloud/plugins/adapters/mcp.py`:
|
|
125
|
+
```python
|
|
126
|
+
def plugin_mcp_configs(plugins, *, include_subagents: bool = False):
|
|
127
|
+
for plugin in plugins:
|
|
128
|
+
if not include_subagents and plugin.is_agent_plugin:
|
|
129
|
+
continue # Dynamic subagent plugins isolate their own MCP servers
|
|
130
|
+
```
|
|
131
|
+
3. Vertical plugins (such as `aws-containers`, `aws-networking`, `aws-web-and-mobile`) and skill plugins do **not** define an `agents/*.md` directory. Therefore, `plugin.is_agent_plugin` evaluates to **`False`**.
|
|
132
|
+
4. Consequently, when `factory.py` calls `discover_mcp_configs()` to build the **Main Coordinator Agent**, all MCP servers declared by `aws-containers` (`finch`, `eks`, `ecs`, `aws-mcp`) are treated as global coordinator tools!
|
|
133
|
+
5. In `factory.py` lines 654–657:
|
|
134
|
+
```python
|
|
135
|
+
discovered_tools = build_mcp_tools_from_server_infos(coordinator_infos, mcp_manager)
|
|
136
|
+
if discovered_tools:
|
|
137
|
+
mcp_tools_list.extend(discovered_tools)
|
|
138
|
+
all_tools.extend(discovered_tools) # <-- Injected into all_tools!
|
|
139
|
+
```
|
|
140
|
+
6. In `factory.py` line 1081:
|
|
141
|
+
```python
|
|
142
|
+
graph = create_deep_agent(
|
|
143
|
+
model=active_model,
|
|
144
|
+
tools=all_tools, # <-- ALL vertical plugin tools bound to Main Agent!
|
|
145
|
+
...
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
7. **The result:** The main agent is inundated with 40+ specialized container/Kubernetes tools that should have been isolated to a specialized subagent or dynamically mounted skill.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### Root Cause 3: `ToolFilterMiddleware` Does NOT Filter Tools from the Model Request
|
|
153
|
+
**Location:** [`src/opscloud/middleware/tool_filter.py:412–431`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/middleware/tool_filter.py#L412-L431)
|
|
154
|
+
|
|
155
|
+
#### The Missing Hook:
|
|
156
|
+
* `ToolFilterMiddleware` only implements:
|
|
157
|
+
- `wrap_tool_call(self, request: ToolCallRequest, handler)`
|
|
158
|
+
- `awrap_tool_call(self, request: ToolCallRequest, handler)`
|
|
159
|
+
* It **does not implement** `wrap_model_call`, `awrap_model_call`, or `modify_request`.
|
|
160
|
+
* In LangGraph / DeepAgents, `tools` are passed to `model.bind_tools(self.tools)`.
|
|
161
|
+
* Because `ToolFilterMiddleware` does not intercept the model call request to prune `request.tools`, **100% of all registered tools are sent in the system prompt / tool definitions to the LLM on every single turn**.
|
|
162
|
+
* The filter only functions as an execution-time authorization gate (blocking the tool *after* the LLM decides to call it). It provides **zero context-reduction benefits**.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### Root Cause 4: Multi-Step Compounding Turn Latency (315 Seconds TTFT)
|
|
167
|
+
* Processing **211,300 input tokens** on remote LLM endpoints (Gemini 2.5 / 3.7 Flash, Claude 3.5 Sonnet) incurs an extreme Time-To-First-Token (TTFT) penalty: ~30 to 70 seconds per call.
|
|
168
|
+
* When the agent runs an agentic step (e.g., executing a command or asking for user clarification), LangGraph's Pregel engine executes sequential model nodes (`model` -> `tools` -> `model` -> `tools` -> `model`).
|
|
169
|
+
* Because the tool schema footprint remains at 211K tokens, **each model call must re-send and the LLM must re-process the full 211K tokens**.
|
|
170
|
+
* 4 sequential steps × 75 seconds per step = **300+ seconds (5.22 minutes)**.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### Root Cause 5: Event Loop Mismatch & Stdio Process Locking/Churn
|
|
175
|
+
**Locations:**
|
|
176
|
+
* [`src/opscloud/agent/factory.py:643–646`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/agent/factory.py#L643-L646)
|
|
177
|
+
* [`src/opscloud/mcp/session_manager.py:530–547`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/mcp/session_manager.py#L530-L547)
|
|
178
|
+
|
|
179
|
+
#### Thread & Event Loop Lifecycles in OpsCloud:
|
|
180
|
+
1. In `factory.py`:
|
|
181
|
+
```python
|
|
182
|
+
if loop is not None and loop.is_running():
|
|
183
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
184
|
+
new_infos = pool.submit(asyncio.run, preload_mcp_metadata(missing_configs)).result()
|
|
185
|
+
else:
|
|
186
|
+
new_infos = asyncio.run(preload_mcp_metadata(missing_configs))
|
|
187
|
+
```
|
|
188
|
+
`asyncio.run()` creates a temporary event loop. Preloading connects via stdio, fetches tool names, and when `asyncio.run()` finishes, **that event loop is destroyed**. Any stdio pipes or process handles bound to that loop become dead or detached.
|
|
189
|
+
2. Later, when an agent turn executes on the server's `uvloop`, `get_session(server_name)` finds that no live session exists for that loop and calls `connect_server()` to start a brand new stdio connection via `uvx`.
|
|
190
|
+
3. Running multiple `uvx` instances (`uvx awslabs.eks-mcp-server@latest`, `uvx awslabs.finch-mcp-server@latest`, `uvx ecs-mcp-server`) concurrently causes:
|
|
191
|
+
- Python virtual environment creation / cache resolution latency for each `uvx` invocation.
|
|
192
|
+
- Stdio process blocking and mutex lock contention under `_get_server_lock(server_name)`.
|
|
193
|
+
- Thread stalls while waiting for child processes to initialize their IPC pipes.
|
|
194
|
+
|
|
195
|
+
#### What `reference/dcode` Does Differently:
|
|
196
|
+
**Locations:**
|
|
197
|
+
* [`reference/dcode/server_graph.py:161–168`](file:///Users/structbinary/Documents/work/talkops/opscloud/reference/dcode/server_graph.py#L161-L168)
|
|
198
|
+
* [`reference/dcode/mcp_tools.py:2247–2251`](file:///Users/structbinary/Documents/work/talkops/opscloud/reference/dcode/mcp_tools.py#L2247-L2251)
|
|
199
|
+
* `dcode` initializes a single, shared `MCPSessionManager` directly on the server's running event loop via `_get_mcp_session_manager()`.
|
|
200
|
+
* Backends are mounted onto a single FastMCP router client.
|
|
201
|
+
* Preflight and connection setup are executed concurrently with bounded concurrency:
|
|
202
|
+
```python
|
|
203
|
+
preflight_results = await _gather_bounded(
|
|
204
|
+
[functools.partial(_preflight_and_connect, name, cfg) for name, cfg in server_items],
|
|
205
|
+
limit=_MCP_LOAD_CONCURRENCY,
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
* All tools route through this unified client. There are no detached thread pools or orphaned event loops.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 4. Architectural Comparison: OpsCloud vs. `dcode`
|
|
213
|
+
|
|
214
|
+
| Architectural Dimension | OpsCloud (`src/opscloud`) | Reference Implementation (`reference/dcode`) |
|
|
215
|
+
| :--- | :--- | :--- |
|
|
216
|
+
| **Tool Adapter Layer** | `langchain_mcp_adapters` with custom `StructuredTool` wrapping | `fastmcp.Client` + `langchain.mcp.as_langchain_tool` |
|
|
217
|
+
| **JSON Schema Handling** | **Recursive `dereference_refs()`** expanding all definitions inline | Native schema pass-through without ref dereferencing |
|
|
218
|
+
| **Plugin MCP Scoping** | Only checks `plugin.is_agent_plugin` (`agents/*.md`). Vertical/Skill plugins dump all tools to Main Agent | Strict config discovery; plugins inject scoped configs with user trust gating |
|
|
219
|
+
| **Model Prompt Filtering** | **None.** `ToolFilterMiddleware` only acts at tool execution time | `_apply_tool_filter` prunes tools before agent construction via `allowedTools`/`disabledTools` |
|
|
220
|
+
| **Session Lifetime** | Eager thread preloading with `asyncio.run()`, on-demand reconnect on separate `uvloop` | Singleton `MCPSessionManager` bound to server event loop |
|
|
221
|
+
| **Connection Pooling** | Separate `_MCPSessionEntry` per server with individual locks | Single unified FastMCP router client multiplexing all backends |
|
|
222
|
+
| **Teardown & Cleanup** | `session.invalidate()` per server | Bounded 5s teardown via `AsyncExitStack` |
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 5. Engineering Action Plan & Implementation Guidance
|
|
227
|
+
|
|
228
|
+
To permanently resolve this blocker and achieve the ~15s response latency of `reference/dcode`, the engineering team must implement the following four-phase remediation:
|
|
229
|
+
|
|
230
|
+
### Phase 1: Eliminate Recursive Schema Expansion (Immediate Fix)
|
|
231
|
+
**Target:** [`src/opscloud/mcp/session_manager.py:68–103`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/mcp/session_manager.py#L68-L103)
|
|
232
|
+
1. **Remove `dereference_refs()`**: Cease using `langchain_core.utils.json_schema.dereference_refs` on raw MCP tool schemas.
|
|
233
|
+
2. **Schema Sanitization**: Instead of deep inlining, clean schemas by stripping provider-incompatible root keys (`$schema`, `$id`) while preserving local `$defs` or flattening top-level properties shallowly.
|
|
234
|
+
3. **Verify Schemas**: Ensure parameters for tools like `apply_yaml` and `manage_k8s_resource` pass concise schemas to the model.
|
|
235
|
+
|
|
236
|
+
### Phase 2: Restrict Plugin MCP Tool Scope to Subagents / Skills
|
|
237
|
+
**Target:** [`src/opscloud/plugins/adapters/mcp.py`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/plugins/adapters/mcp.py) & [`src/opscloud/agent/factory.py`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/agent/factory.py)
|
|
238
|
+
1. **Update Plugin Classification**: Plugins that provide skills with MCP servers must not automatically dump their MCP tools into the Main Coordinator Agent (`all_tools`).
|
|
239
|
+
2. **Scope Vertical Plugins**: Tools from vertical plugins (`aws-containers`, `aws-networking`, etc.) should only be attached to their respective specialized subagents or mounted when their specific skill is activated.
|
|
240
|
+
3. **Main Agent Tool Budget**: Ensure the Main Coordinator Agent is initialized with only core tools (`fetch_url`, `web_search`, `read_file`, `run_command`, `task`, `ask_user`), keeping initial context below **15,000 tokens**.
|
|
241
|
+
|
|
242
|
+
### Phase 3: Implement Model-Level Tool Filtering in `ToolFilterMiddleware`
|
|
243
|
+
**Target:** [`src/opscloud/middleware/tool_filter.py`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/middleware/tool_filter.py)
|
|
244
|
+
1. Implement `wrap_model_call` and `awrap_model_call` (or `modify_request`):
|
|
245
|
+
```python
|
|
246
|
+
async def awrap_model_call(
|
|
247
|
+
self,
|
|
248
|
+
request: ModelRequest,
|
|
249
|
+
handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
|
|
250
|
+
) -> ModelResponse:
|
|
251
|
+
if self._allowed_patterns and request.tools:
|
|
252
|
+
filtered_tools = [
|
|
253
|
+
t for t in request.tools
|
|
254
|
+
if self.is_tool_allowed(getattr(t, "name", str(t)), tool_obj=t)
|
|
255
|
+
]
|
|
256
|
+
request = request.override(tools=filtered_tools)
|
|
257
|
+
return await handler(request)
|
|
258
|
+
```
|
|
259
|
+
2. This guarantees that subagents with restricted tool lists (e.g., `allowed_tools: ["aws-finops-*"]`) only send their authorized tools to the LLM prompt.
|
|
260
|
+
|
|
261
|
+
### Phase 4: Align Session Lifecycle with `dcode` Pattern
|
|
262
|
+
**Target:** [`src/opscloud/mcp/session_manager.py`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/mcp/session_manager.py) & [`src/opscloud/server/server_graph.py`](file:///Users/structbinary/Documents/work/talkops/opscloud/src/opscloud/server/server_graph.py)
|
|
263
|
+
1. **Bind Session Manager to Server Loop**: Ensure `MCPSessionManager` is instantiated on the server's primary event loop, eliminating cross-thread `asyncio.run()` preloading.
|
|
264
|
+
2. **Router Client Multiplexing**: Adopt FastMCP router client mounting so all stdio/http transports share a single client context.
|
|
265
|
+
3. **Graceful Subprocess Management**: Ensure bounded subprocess shutdown (5s timeout) to prevent dangling `uvx` / `docker` processes on server reload.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 6. Verification & Acceptance Criteria
|
|
270
|
+
|
|
271
|
+
The fix will be considered complete and verified when:
|
|
272
|
+
1. **Context Benchmark:** Turn 1 prompt context with `aws-containers` installed does not exceed **45,000 tokens** (comparable to `dcode`).
|
|
273
|
+
2. **Latency Benchmark:** Turn 1 execution latency drops from **315 seconds to under 25 seconds** on the same test query.
|
|
274
|
+
3. **No Schema Inlining Bloat:** Inspection of LangSmith tool definitions confirms that Kubernetes OpenAPI definitions are not duplicated into thousands of lines of JSON.
|
|
275
|
+
4. **Subagent Isolation:** Tools from `aws-containers` only appear in turns where the container subagent/skill is explicitly dispatched.
|
|
@@ -415,9 +415,9 @@ def _subagent_cli_middleware(
|
|
|
415
415
|
|
|
416
416
|
server_keys: list[str] = []
|
|
417
417
|
if mcp_config:
|
|
418
|
-
server_keys.extend(mcp_config.keys())
|
|
418
|
+
server_keys.extend(str(k) for k in mcp_config.keys())
|
|
419
419
|
if mcp_server_info:
|
|
420
|
-
server_keys.extend(getattr(s, "name",
|
|
420
|
+
server_keys.extend(str(getattr(s, "name", s)) for s in mcp_server_info)
|
|
421
421
|
|
|
422
422
|
if allowed_tools or capabilities:
|
|
423
423
|
middleware.append(
|
|
@@ -603,13 +603,16 @@ def create_opscloud_agent(
|
|
|
603
603
|
|
|
604
604
|
# 5. MCP Discovery
|
|
605
605
|
mcp_tools_list: list[BaseTool] = []
|
|
606
|
-
cached_infos: list[Any] | None =
|
|
607
|
-
mcp_configs: dict[str, Any] | None =
|
|
606
|
+
cached_infos: list[Any] | None = kwargs.get("mcp_server_info")
|
|
607
|
+
mcp_configs: dict[str, Any] | None = kwargs.get("mcp_config")
|
|
608
608
|
if mcp_tools is not None:
|
|
609
609
|
mcp_tools_list = list(mcp_tools)
|
|
610
610
|
all_tools.extend(mcp_tools)
|
|
611
|
+
if mcp_configs is None:
|
|
612
|
+
mcp_configs = discover_mcp_configs(project_scope_root)
|
|
611
613
|
else:
|
|
612
|
-
mcp_configs
|
|
614
|
+
if mcp_configs is None:
|
|
615
|
+
mcp_configs = discover_mcp_configs(project_scope_root)
|
|
613
616
|
from opscloud.mcp.preload import (
|
|
614
617
|
get_cached_mcp_server_infos,
|
|
615
618
|
preload_mcp_metadata,
|
|
@@ -707,6 +710,7 @@ def create_opscloud_agent(
|
|
|
707
710
|
from deepagents.middleware.subagents import GENERAL_PURPOSE_SUBAGENT, SubAgent
|
|
708
711
|
|
|
709
712
|
compiled_subagents: list[SubAgent] = []
|
|
713
|
+
base_subagent_tools = [t for t in all_tools if t not in mcp_tools_list]
|
|
710
714
|
for name, subagent_meta in subagent_by_name.items():
|
|
711
715
|
model_spec = subagent_meta.get("model")
|
|
712
716
|
has_explicit_model = bool(model_spec)
|
|
@@ -723,6 +727,18 @@ def create_opscloud_agent(
|
|
|
723
727
|
subagent_path = subagent_meta.get("path")
|
|
724
728
|
custom_mw = subagent_meta.get("middleware")
|
|
725
729
|
|
|
730
|
+
# Build per-subagent MCP config and tools for this subagent
|
|
731
|
+
from opscloud.plugins.adapters.mcp import prepare_subagent_mcp
|
|
732
|
+
|
|
733
|
+
servers, subagent_mcp_tools, sub_mcp_server_infos = prepare_subagent_mcp(
|
|
734
|
+
subagent_name, subagent_meta, project_scope_root
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
if subagent_mcp_tools:
|
|
738
|
+
subagent_dict["tools"] = [*base_subagent_tools, *subagent_mcp_tools]
|
|
739
|
+
else:
|
|
740
|
+
subagent_dict["tools"] = list(base_subagent_tools)
|
|
741
|
+
|
|
726
742
|
sub_middleware = _subagent_cli_middleware(
|
|
727
743
|
has_explicit_model=has_explicit_model,
|
|
728
744
|
assistant_id=assistant_id,
|
|
@@ -738,6 +754,9 @@ def create_opscloud_agent(
|
|
|
738
754
|
worktree_root=effective_cwd,
|
|
739
755
|
subagent_path=subagent_path if subagent_path else None,
|
|
740
756
|
custom_middleware=custom_mw if isinstance(custom_mw, list) else None,
|
|
757
|
+
mcp_server_info=sub_mcp_server_infos or None,
|
|
758
|
+
mcp_config=servers or None,
|
|
759
|
+
mcp_tools=subagent_mcp_tools or None,
|
|
741
760
|
backend=composite_backend,
|
|
742
761
|
model=active_model,
|
|
743
762
|
)
|
|
@@ -748,9 +767,10 @@ def create_opscloud_agent(
|
|
|
748
767
|
|
|
749
768
|
compiled_subagents.append(cast(SubAgent, subagent_dict))
|
|
750
769
|
logger.info(
|
|
751
|
-
"
|
|
770
|
+
"Compiled subagent '%s' (tools: %d)",
|
|
752
771
|
subagent_name,
|
|
753
|
-
|
|
772
|
+
len(subagent_dict.get("tools", [])),
|
|
773
|
+
extra={"subagent": subagent_name, "tools_count": len(subagent_dict.get("tools", []))},
|
|
754
774
|
)
|
|
755
775
|
|
|
756
776
|
if not any(sub.get("name") == GENERAL_PURPOSE_SUBAGENT["name"] for sub in compiled_subagents):
|
|
@@ -773,6 +793,7 @@ def create_opscloud_agent(
|
|
|
773
793
|
"name": gp_name,
|
|
774
794
|
"description": gp_description,
|
|
775
795
|
"system_prompt": gp_system_prompt,
|
|
796
|
+
"tools": list(base_subagent_tools),
|
|
776
797
|
"middleware": gp_middleware,
|
|
777
798
|
}
|
|
778
799
|
if interrupt_on is not None:
|
|
@@ -832,12 +853,22 @@ def create_opscloud_agent(
|
|
|
832
853
|
agent_middleware.append(AskUserMiddleware())
|
|
833
854
|
|
|
834
855
|
# 10.5 MCP tool arguments and error handling
|
|
835
|
-
if mcp_tools_list:
|
|
836
|
-
|
|
856
|
+
if mcp_tools_list or cached_infos or mcp_configs:
|
|
857
|
+
mcp_cfg_for_mw = mcp_configs
|
|
858
|
+
if not mcp_cfg_for_mw and not cached_infos and mcp_tools_list:
|
|
859
|
+
synth_cfg: dict[str, Any] = {}
|
|
860
|
+
for t in mcp_tools_list:
|
|
861
|
+
srv = getattr(t, "metadata", {}).get("_mcp_server") if isinstance(getattr(t, "metadata", None), dict) else None
|
|
862
|
+
if srv and srv not in synth_cfg:
|
|
863
|
+
synth_cfg[srv] = {"command": "custom", "args": []}
|
|
864
|
+
if synth_cfg:
|
|
865
|
+
mcp_cfg_for_mw = synth_cfg
|
|
866
|
+
|
|
867
|
+
if cached_infos or mcp_cfg_for_mw:
|
|
837
868
|
from opscloud.middleware.mcp_context import MCPContextMiddleware
|
|
838
869
|
|
|
839
870
|
agent_middleware.append(
|
|
840
|
-
MCPContextMiddleware(mcp_server_info=cached_infos, mcp_config=
|
|
871
|
+
MCPContextMiddleware(mcp_server_info=cached_infos, mcp_config=mcp_cfg_for_mw)
|
|
841
872
|
)
|
|
842
873
|
agent_middleware.append(MCPToolMiddleware())
|
|
843
874
|
|
|
@@ -117,7 +117,11 @@ class MCPDiscovery:
|
|
|
117
117
|
"""
|
|
118
118
|
self._store = store
|
|
119
119
|
|
|
120
|
-
def discover(
|
|
120
|
+
def discover(
|
|
121
|
+
self,
|
|
122
|
+
project_root: Path | None = None,
|
|
123
|
+
include_subagents: bool = False,
|
|
124
|
+
) -> dict[str, MCPServerConfig]:
|
|
121
125
|
"""Discover and merge MCP server configs across global, user, plugin, and project files.
|
|
122
126
|
|
|
123
127
|
Precedence (lowest to highest):
|
|
@@ -158,7 +162,10 @@ class MCPDiscovery:
|
|
|
158
162
|
try:
|
|
159
163
|
from opscloud.plugins.adapters.mcp import discover_plugin_mcp_configs
|
|
160
164
|
|
|
161
|
-
plugin_configs = discover_plugin_mcp_configs(
|
|
165
|
+
plugin_configs = discover_plugin_mcp_configs(
|
|
166
|
+
project_dir=effective_project_root,
|
|
167
|
+
include_subagents=include_subagents,
|
|
168
|
+
)
|
|
162
169
|
if isinstance(plugin_configs, dict):
|
|
163
170
|
for name, config in plugin_configs.items():
|
|
164
171
|
if isinstance(config, dict):
|
|
@@ -224,6 +231,9 @@ class MCPDiscovery:
|
|
|
224
231
|
return results
|
|
225
232
|
|
|
226
233
|
|
|
227
|
-
def discover_mcp_configs(
|
|
234
|
+
def discover_mcp_configs(
|
|
235
|
+
project_root: Path | None = None,
|
|
236
|
+
include_subagents: bool = False,
|
|
237
|
+
) -> dict[str, MCPServerConfig]:
|
|
228
238
|
"""Discover active MCP server configs from disk."""
|
|
229
|
-
return MCPDiscovery().discover(project_root)
|
|
239
|
+
return MCPDiscovery().discover(project_root, include_subagents=include_subagents)
|
|
@@ -77,42 +77,63 @@ def _filter_tool_names(
|
|
|
77
77
|
return True
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
def _clean_mcp_schema(schema: Any) -> dict[str, Any]:
|
|
80
|
+
def _clean_mcp_schema(schema: Any, max_ref_depth: int = 3) -> dict[str, Any]:
|
|
81
81
|
"""Clean JSON Schema for compatibility with LLM provider tool schemas (Gemini, Anthropic, OpenAI).
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
Safely inlines local `$ref` pointers (such as `#/$defs/...` or `#/definitions/...`)
|
|
84
|
+
up to a bounded ref depth, strips provider-incompatible metadata keys (`$schema`, `$id`,
|
|
85
|
+
`additionalProperties`), and prevents recursive bloat or corrupted empty keys.
|
|
86
86
|
"""
|
|
87
87
|
if not isinstance(schema, dict):
|
|
88
88
|
return {}
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
90
|
+
defs: dict[str, Any] = {}
|
|
91
|
+
if isinstance(schema.get("$defs"), dict):
|
|
92
|
+
defs.update(schema["$defs"])
|
|
93
|
+
if isinstance(schema.get("definitions"), dict):
|
|
94
|
+
defs.update(schema["definitions"])
|
|
95
|
+
|
|
96
|
+
def _resolve(node: Any, ref_depth: int, seen_refs: set[str]) -> Any:
|
|
97
|
+
if isinstance(node, dict):
|
|
98
|
+
if "$ref" in node:
|
|
99
|
+
ref = node["$ref"]
|
|
100
|
+
if (
|
|
101
|
+
ref_depth >= max_ref_depth
|
|
102
|
+
or not isinstance(ref, str)
|
|
103
|
+
or not ref.startswith("#/")
|
|
104
|
+
or ref in seen_refs
|
|
105
|
+
):
|
|
106
|
+
return {"type": "object"}
|
|
107
|
+
|
|
108
|
+
parts = ref.lstrip("#/").split("/")
|
|
109
|
+
target = None
|
|
110
|
+
if len(parts) >= 2 and parts[0] in ("$defs", "definitions"):
|
|
111
|
+
target = defs.get(parts[1])
|
|
112
|
+
elif len(parts) == 1 and parts[0] in defs:
|
|
113
|
+
target = defs.get(parts[0])
|
|
114
|
+
|
|
115
|
+
if isinstance(target, dict):
|
|
116
|
+
merged = dict(target)
|
|
117
|
+
for k, v in node.items():
|
|
118
|
+
if k != "$ref":
|
|
119
|
+
merged[k] = v
|
|
120
|
+
return _resolve(merged, ref_depth + 1, seen_refs | {ref})
|
|
121
|
+
return {"type": "object"}
|
|
122
|
+
|
|
123
|
+
res = {}
|
|
124
|
+
for k, v in node.items():
|
|
125
|
+
if k in ("$schema", "$id", "additionalProperties", "$defs", "definitions"):
|
|
126
|
+
continue
|
|
127
|
+
res[k] = _resolve(v, ref_depth, seen_refs)
|
|
128
|
+
return res
|
|
129
|
+
|
|
130
|
+
if isinstance(node, list):
|
|
131
|
+
return [_resolve(item, ref_depth, seen_refs) for item in node]
|
|
132
|
+
|
|
133
|
+
return node
|
|
134
|
+
|
|
135
|
+
result = _resolve(schema, 0, set())
|
|
136
|
+
return result if isinstance(result, dict) else {}
|
|
116
137
|
|
|
117
138
|
|
|
118
139
|
def _clean_stderr_diagnostic(stderr_text: str | None) -> str | None:
|
|
@@ -65,42 +65,63 @@ def _normalize_mcp_arguments(arguments: dict[str, Any], input_schema: Any) -> di
|
|
|
65
65
|
return cleaned
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _clean_mcp_schema(schema: Any) -> dict[str, Any]:
|
|
68
|
+
def _clean_mcp_schema(schema: Any, max_ref_depth: int = 3) -> dict[str, Any]:
|
|
69
69
|
"""Clean JSON Schema for compatibility with LLM provider tool schemas (Gemini, Anthropic, OpenAI).
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
Safely inlines local `$ref` pointers (such as `#/$defs/...` or `#/definitions/...`)
|
|
72
|
+
up to a bounded ref depth, strips provider-incompatible metadata keys (`$schema`, `$id`,
|
|
73
|
+
`additionalProperties`), and prevents recursive bloat or corrupted empty keys.
|
|
74
74
|
"""
|
|
75
75
|
if not isinstance(schema, dict):
|
|
76
76
|
return {}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
defs: dict[str, Any] = {}
|
|
79
|
+
if isinstance(schema.get("$defs"), dict):
|
|
80
|
+
defs.update(schema["$defs"])
|
|
81
|
+
if isinstance(schema.get("definitions"), dict):
|
|
82
|
+
defs.update(schema["definitions"])
|
|
83
|
+
|
|
84
|
+
def _resolve(node: Any, ref_depth: int, seen_refs: set[str]) -> Any:
|
|
85
|
+
if isinstance(node, dict):
|
|
86
|
+
if "$ref" in node:
|
|
87
|
+
ref = node["$ref"]
|
|
88
|
+
if (
|
|
89
|
+
ref_depth >= max_ref_depth
|
|
90
|
+
or not isinstance(ref, str)
|
|
91
|
+
or not ref.startswith("#/")
|
|
92
|
+
or ref in seen_refs
|
|
93
|
+
):
|
|
94
|
+
return {"type": "object"}
|
|
95
|
+
|
|
96
|
+
parts = ref.lstrip("#/").split("/")
|
|
97
|
+
target = None
|
|
98
|
+
if len(parts) >= 2 and parts[0] in ("$defs", "definitions"):
|
|
99
|
+
target = defs.get(parts[1])
|
|
100
|
+
elif len(parts) == 1 and parts[0] in defs:
|
|
101
|
+
target = defs.get(parts[0])
|
|
102
|
+
|
|
103
|
+
if isinstance(target, dict):
|
|
104
|
+
merged = dict(target)
|
|
105
|
+
for k, v in node.items():
|
|
106
|
+
if k != "$ref":
|
|
107
|
+
merged[k] = v
|
|
108
|
+
return _resolve(merged, ref_depth + 1, seen_refs | {ref})
|
|
109
|
+
return {"type": "object"}
|
|
110
|
+
|
|
111
|
+
res = {}
|
|
112
|
+
for k, v in node.items():
|
|
113
|
+
if k in ("$schema", "$id", "additionalProperties", "$defs", "definitions"):
|
|
114
|
+
continue
|
|
115
|
+
res[k] = _resolve(v, ref_depth, seen_refs)
|
|
116
|
+
return res
|
|
79
117
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
except Exception as exc:
|
|
83
|
-
logger.debug("Failed to dereference MCP schema refs: %s", exc)
|
|
84
|
-
resolved = schema
|
|
85
|
-
|
|
86
|
-
cleaned = dict(resolved) if isinstance(resolved, dict) else dict(schema)
|
|
87
|
-
for key in ("$schema", "$id", "$defs", "definitions", "additionalProperties"):
|
|
88
|
-
cleaned.pop(key, None)
|
|
89
|
-
|
|
90
|
-
properties = cleaned.get("properties")
|
|
91
|
-
if isinstance(properties, dict):
|
|
92
|
-
new_props = {}
|
|
93
|
-
for prop_name, prop_val in properties.items():
|
|
94
|
-
if isinstance(prop_val, dict):
|
|
95
|
-
p_clean = dict(prop_val)
|
|
96
|
-
p_clean.pop("additionalProperties", None)
|
|
97
|
-
p_clean.pop("$schema", None)
|
|
98
|
-
new_props[prop_name] = p_clean
|
|
99
|
-
else:
|
|
100
|
-
new_props[prop_name] = prop_val
|
|
101
|
-
cleaned["properties"] = new_props
|
|
118
|
+
if isinstance(node, list):
|
|
119
|
+
return [_resolve(item, ref_depth, seen_refs) for item in node]
|
|
102
120
|
|
|
103
|
-
|
|
121
|
+
return node
|
|
122
|
+
|
|
123
|
+
result = _resolve(schema, 0, set())
|
|
124
|
+
return result if isinstance(result, dict) else {}
|
|
104
125
|
|
|
105
126
|
|
|
106
127
|
def _filter_tool_name(
|
{talkops_opscloud-0.3.0b7 → talkops_opscloud-0.3.0b9}/src/opscloud/middleware/server_hooks.py
RENAMED
|
@@ -919,7 +919,7 @@ def _mcp_server_from_tool(tool: object | None) -> str | None:
|
|
|
919
919
|
metadata = getattr(tool, "metadata", None)
|
|
920
920
|
if not isinstance(metadata, Mapping):
|
|
921
921
|
return None
|
|
922
|
-
for key in ("mcp_server", "mcp_server_name", "server_name"):
|
|
922
|
+
for key in ("_mcp_server", "mcp_server", "mcp_server_name", "server_name"):
|
|
923
923
|
value = metadata.get(key)
|
|
924
924
|
if isinstance(value, str) and value:
|
|
925
925
|
return value
|