Skip to main content

loong_contracts/
fault.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::contracts::Capability;
6use crate::errors::{KernelError, PolicyError};
7
8/// Structured error type for kernel dispatch failures.
9///
10/// Unlike `KernelError` (which covers all kernel operations including setup),
11/// `Fault` represents runtime dispatch failures that callers can match on
12/// to decide recovery strategy.
13#[non_exhaustive]
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub enum Fault {
16    Panic {
17        message: String,
18    },
19    CapabilityViolation {
20        token_id: String,
21        capability: Capability,
22    },
23    TokenExpired {
24        token_id: String,
25        expires_at_epoch_s: u64,
26    },
27    ProtocolViolation {
28        detail: String,
29    },
30    PolicyDenied {
31        reason: String,
32    },
33}
34
35impl fmt::Display for Fault {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            Self::Panic { message } => write!(f, "panic: {message}"),
39            Self::CapabilityViolation {
40                token_id,
41                capability,
42            } => {
43                write!(
44                    f,
45                    "capability violation: token {token_id} missing {capability:?}"
46                )
47            }
48            Self::TokenExpired {
49                token_id,
50                expires_at_epoch_s,
51            } => {
52                write!(f, "token {token_id} expired at {expires_at_epoch_s}")
53            }
54            Self::ProtocolViolation { detail } => write!(f, "protocol violation: {detail}"),
55            Self::PolicyDenied { reason } => write!(f, "policy denied: {reason}"),
56        }
57    }
58}
59
60impl std::error::Error for Fault {}
61
62impl Fault {
63    pub fn from_policy_error(err: PolicyError) -> Self {
64        match err {
65            PolicyError::ExpiredToken {
66                token_id,
67                expires_at_epoch_s,
68            } => Self::TokenExpired {
69                token_id,
70                expires_at_epoch_s,
71            },
72            PolicyError::MissingCapability {
73                token_id,
74                capability,
75            } => Self::CapabilityViolation {
76                token_id,
77                capability,
78            },
79            PolicyError::RevokedToken { token_id } => Self::PolicyDenied {
80                reason: format!("token {token_id} revoked"),
81            },
82            PolicyError::PackMismatch {
83                token_pack_id,
84                runtime_pack_id,
85            } => Self::PolicyDenied {
86                reason: format!("pack mismatch: token={token_pack_id} runtime={runtime_pack_id}"),
87            },
88            PolicyError::ExtensionDenied { extension, reason } => Self::PolicyDenied {
89                reason: format!("extension {extension}: {reason}"),
90            },
91            PolicyError::ToolCallDenied { tool_name, reason } => Self::PolicyDenied {
92                reason: format!("tool {tool_name}: {reason}"),
93            },
94        }
95    }
96
97    pub fn from_kernel_error(err: KernelError) -> Self {
98        match err {
99            KernelError::Policy(policy_err) => Self::from_policy_error(policy_err),
100            KernelError::PackCapabilityBoundary {
101                capability,
102                pack_id,
103            } => Self::CapabilityViolation {
104                token_id: format!("pack:{pack_id}"),
105                capability,
106            },
107            KernelError::PackNotFound(_)
108            | KernelError::DuplicatePack(_)
109            | KernelError::ConnectorNotAllowed { .. }
110            | KernelError::Pack(_)
111            | KernelError::Harness(_)
112            | KernelError::Connector(_)
113            | KernelError::RuntimePlane(_)
114            | KernelError::ToolPlane(_)
115            | KernelError::MemoryPlane(_)
116            | KernelError::Integration(_)
117            | KernelError::Audit(_) => Self::Panic {
118                message: err.to_string(),
119            },
120        }
121    }
122}