Source code for ralph.recovery.classified_failure

"""Structured classified failure model."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .failure_category import FailureCategory
    from .unavailability_reason import UnavailabilityReason


[docs] @dataclass(frozen=True) class ClassifiedFailure: """A failure with its category, attribution, and budget-counting decision.""" category: FailureCategory reason: str attributed_agent: str | None attributed_phase: str counts_against_budget: bool original_exception: BaseException | None raw_message: str reset_session: bool = field(default=False) # When True, the next recovery attempt should call # `RestartAwareMcpBridge.reset_tool_registry()` to rebuild the # visible tool list. Set by the failure classifier when the live # MCP server reports a missing tool via the # "No such tool available: mcp__<server>__<tool>" string (the # post-tool-result wedge failure mode) or when a runtime # `ToolDispatchError` is raised with an "is not registered" message. reset_tool_registry: bool = field(default=False) # When True, the agent is considered temporarily unavailable (e.g. out # of credits) and the recovery controller should skip it with exponential # backoff instead of retrying immediately. is_unavailable: bool = field(default=False) watchdog_reason: str | None = field(default=None) unavailability_reason: UnavailabilityReason | None = field(default=None) # The transport-level session id the killed agent was running # under. Captured per-line by # ``_run_subprocess_and_read_lines`` and threaded through # ``AgentInactivityTimeoutError.opts.resumable_session_id`` AND # through ``IdleWatchdogKilledError.resumable_session_id`` on the # ``__cause__`` chain. Populated by ``FailureClassifier.classify`` # when the watchdog kill surfaces a usable session id; consumed # by ``RecoveryController.handle`` to populate # ``state.last_agent_session_id`` so the existing # ``_apply_chain_retry`` resume path emits a resume intent with # the captured id (instead of starting a fresh session). None # means ``unknown / not captured``; the controller MUST NOT set # ``last_agent_session_id`` when this is None. resumable_session_id: str | None = field(default=None)