class AgentError(Exception): """Base error with a stable public code.""" code = "AGENT_INTERNAL_ERROR" status_code = 500 retryable: bool | None = False def __init__(self, message: str) -> None: super().__init__(message) self.message = message declared = type(self).retryable self.retryable = self.status_code >= 500 if declared is None else declared @property def is_retryable(self) -> bool: return bool(self.retryable) class ValidationAgentError(AgentError): code = "AGENT_INPUT_INVALID" status_code = 422 class UnauthorizedAgentError(AgentError): code = "AUTHENTICATION_REQUIRED" status_code = 401 class AgentTypeNotFoundError(ValidationAgentError): code = "AGENT_TYPE_NOT_FOUND" status_code = 404 class ForbiddenAgentError(AgentError): code = "AGENT_PERMISSION_DENIED" status_code = 403 class OnboardingRequiredError(ForbiddenAgentError): code = "ONBOARDING_REQUIRED" class ConflictAgentError(AgentError): code = "RUN_NOT_CANCELLABLE" status_code = 409 class IdempotencyConflictError(ConflictAgentError): code = "IDEMPOTENCY_CONFLICT" class RecoverableAgentError(AgentError): code = "DEPENDENCY_UNAVAILABLE" status_code = 503 retryable = True class RunLeaseLostError(ConflictAgentError): code = "RUN_NOT_CANCELLABLE" class ResourceNotFoundError(AgentError): code = "SESSION_NOT_FOUND" status_code = 404 class RunNotFoundError(ResourceNotFoundError): code = "RUN_NOT_FOUND" class InvalidCursorError(AgentError): code = "INVALID_CURSOR" status_code = 400 class RateLimitedError(AgentError): code = "RATE_LIMITED" status_code = 429 retryable = True class SseNotAcceptableError(AgentError): code = "SSE_NOT_ACCEPTABLE" status_code = 406