From 47c0fcbefaebbc95432732317b4ff8ef1290395b Mon Sep 17 00:00:00 2001 From: James Smith Date: Fri, 12 Jun 2026 11:39:39 +0100 Subject: [PATCH] fix: guard capabilities import in agent for stripped-host degradation Matches the agent's established try/except import convention; the agent now starts and reports empty capabilities when utils.capabilities (or its dependency chain) is unavailable. Co-Authored-By: Claude Fable 5 --- intercept_agent.py | 57 ++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/intercept_agent.py b/intercept_agent.py index 207ee86..447636a 100644 --- a/intercept_agent.py +++ b/intercept_agent.py @@ -43,7 +43,12 @@ except ImportError: HAS_DEPENDENCIES_MODULE = False # Shared capability detection (extracted so app and agent cannot drift) -from utils.capabilities import MODE_DEPENDENCY_MAP, detect_interfaces, detect_mode_availability +try: + from utils.capabilities import MODE_DEPENDENCY_MAP, detect_interfaces, detect_mode_availability + + HAS_CAPABILITIES_MODULE = True +except ImportError: + HAS_CAPABILITIES_MODULE = False # Import TSCM modules for consistent analysis (same as local mode) try: @@ -420,34 +425,42 @@ class ModeManager: "tool_details": {}, # Detailed tool status } - # Detect interfaces via shared capability detection - capabilities["interfaces"] = detect_interfaces() + if not HAS_CAPABILITIES_MODULE: + # Degrade gracefully on stripped-down hosts where utils.capabilities + # (or its dependency chain) could not be imported. + logger.warning("capability detection unavailable: utils.capabilities could not be imported") + else: + # Detect interfaces via shared capability detection + capabilities["interfaces"] = detect_interfaces() - # Probe dependencies once; reuse for both mode availability and tool_details. - dep_status: dict | None = None - if HAS_DEPENDENCIES_MODULE: + # Probe dependencies once; reuse for both mode availability and tool_details. + # Note: utils.capabilities imports check_all_dependencies at module level, so if + # utils.dependencies were missing, the capabilities import would have failed and + # HAS_CAPABILITIES_MODULE would be False. Inside this True branch, HAS_DEPENDENCIES_MODULE + # is therefore necessarily True as well — the inner guard is not needed. + dep_status: dict | None = None try: dep_status = check_all_dependencies() except Exception as e: logger.warning(f"Tool detail collection failed: {e}") - # Detect mode availability via shared capability detection - raw_modes = detect_mode_availability(dep_status) - for mode, ready in raw_modes.items(): - # Apply this agent's per-mode config gating on top of tool readiness - capabilities["modes"][mode] = ready if config.modes_enabled.get(mode, True) else False + # Detect mode availability via shared capability detection + raw_modes = detect_mode_availability(dep_status) + for mode, ready in raw_modes.items(): + # Apply this agent's per-mode config gating on top of tool readiness + capabilities["modes"][mode] = ready if config.modes_enabled.get(mode, True) else False - # Populate detailed tool info for modes tracked in dependencies.py - if dep_status is not None: - for dep_mode, cap_mode in MODE_DEPENDENCY_MAP.items(): - if dep_mode in dep_status: - mode_info = dep_status[dep_mode] - capabilities["tool_details"][cap_mode] = { - "name": mode_info["name"], - "ready": mode_info["ready"], - "missing_required": mode_info["missing_required"], - "tools": mode_info["tools"], - } + # Populate detailed tool info for modes tracked in dependencies.py + if dep_status is not None: + for dep_mode, cap_mode in MODE_DEPENDENCY_MAP.items(): + if dep_mode in dep_status: + mode_info = dep_status[dep_mode] + capabilities["tool_details"][cap_mode] = { + "name": mode_info["name"], + "ready": mode_info["ready"], + "missing_required": mode_info["missing_required"], + "tools": mode_info["tools"], + } # Use Intercept's SDR detection sdr_factory = self._get_sdr_factory()