1use crate::ids::{ModelId, ProviderId};
2use crate::provider::TokenMeasurementSource;
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
7pub enum ProviderError {
8 #[error("authentication failed")]
9 Auth,
10 #[error("rate limited")]
11 RateLimit,
12 #[error("context overflow")]
13 ContextOverflow,
14 #[error("invalid request: {0}")]
15 InvalidRequest(String),
16 #[error("network error: {0}")]
17 Network(String),
18 #[error("protocol error: {0}")]
19 Protocol(String),
20 #[error("cancelled")]
21 Cancelled,
22 #[error("unknown provider error: {0}")]
23 Unknown(String),
24}
25
26#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
27pub enum ToolError {
28 #[error("invalid arguments: {0}")]
29 InvalidArguments(String),
30 #[error("execution failed: {0}")]
31 Execution(String),
32 #[error("cancelled")]
33 Cancelled,
34}
35
36#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
37pub enum CredentialError {
38 #[error("credential resolve failed: {0}")]
39 Resolve(String),
40}
41
42#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
43pub enum PromptError {
44 #[error("prompt render failed: {0}")]
45 Render(String),
46}
47
48#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
49pub enum ContextError {
50 #[error("context load failed: {0}")]
51 Load(String),
52}
53
54#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
55pub enum StoreError {
56 #[error("store io error: {0}")]
57 Io(String),
58 #[error("store internal error: {0}")]
59 Internal(String),
60}
61
62#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
63pub enum PolicyError {
64 #[error("policy evaluation failed: {0}")]
65 Evaluate(String),
66}
67
68#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
69pub enum KaynineError {
70 #[error("invalid request")]
71 InvalidRequest,
72 #[error("session not found")]
73 SessionNotFound,
74 #[error("session busy")]
75 SessionBusy,
76 #[error("run already active")]
77 RunAlreadyActive,
78 #[error("revision conflict")]
79 RevisionConflict,
80 #[error("idempotency conflict")]
81 IdempotencyConflict,
82 #[error("approval expired")]
83 ApprovalExpired,
84 #[error("store error: {0}")]
85 Store(#[from] StoreError),
86 #[error("internal error")]
87 Internal,
88}
89
90#[derive(Clone, Debug, PartialEq, Error, Serialize, Deserialize)]
91pub enum RunFailureReason {
92 #[error("agent definition unavailable")]
93 DefinitionUnavailable,
94 #[error("model capability mismatch: {requirement} (provider {provider}, model {model})")]
95 ModelCapabilityMismatch {
96 requirement: String,
97 provider: ProviderId,
98 model: ModelId,
99 },
100 #[error("context budget exceeded: {measured_tokens} > {available_tokens}")]
101 ContextBudgetExceeded {
102 measured_tokens: u64,
103 available_tokens: u64,
104 source: TokenMeasurementSource,
105 },
106 #[error("provider failure: {0}")]
107 Provider(#[from] ProviderError),
108 #[error("prompt failure: {0}")]
109 Prompt(#[from] PromptError),
110 #[error("context provider failure: {0}")]
111 Context(#[from] ContextError),
112 #[error("store failure: {0}")]
113 Store(#[from] StoreError),
114 #[error("internal error")]
115 Internal,
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn kaynine_error_roundtrips() {
124 let err = KaynineError::SessionBusy;
125 let json = serde_json::to_string(&err).unwrap();
126 let back: KaynineError = serde_json::from_str(&json).unwrap();
127 assert_eq!(back, err);
128 }
129
130 #[test]
131 fn run_failure_reason_roundtrips_with_payload() {
132 let reason = RunFailureReason::ContextBudgetExceeded {
133 measured_tokens: 100,
134 available_tokens: 50,
135 source: TokenMeasurementSource::Heuristic,
136 };
137 let json = serde_json::to_string(&reason).unwrap();
138 let back: RunFailureReason = serde_json::from_str(&json).unwrap();
139 assert_eq!(back, reason);
140 }
141
142 #[test]
143 fn policy_error_roundtrips() {
144 let err = PolicyError::Evaluate("boom".into());
145 let json = serde_json::to_string(&err).unwrap();
146 let back: PolicyError = serde_json::from_str(&json).unwrap();
147 assert_eq!(back, err);
148 }
149
150 #[test]
151 fn provider_error_covers_spec_categories() {
152 let categories = [
153 ProviderError::Auth,
154 ProviderError::RateLimit,
155 ProviderError::ContextOverflow,
156 ProviderError::InvalidRequest("x".into()),
157 ProviderError::Network("x".into()),
158 ProviderError::Protocol("x".into()),
159 ProviderError::Cancelled,
160 ProviderError::Unknown("x".into()),
161 ];
162 for c in categories {
163 let json = serde_json::to_string(&c).unwrap();
164 let back: ProviderError = serde_json::from_str(&json).unwrap();
165 assert_eq!(back, c);
166 }
167 }
168}