1#[derive(Debug, thiserror::Error)]
5pub enum AgentError {
6 #[error(transparent)]
7 Llm(#[from] zeph_llm::LlmError),
8
9 #[error(transparent)]
10 Channel(#[from] crate::channel::ChannelError),
11
12 #[error(transparent)]
13 Memory(#[from] zeph_memory::MemoryError),
14
15 #[error(transparent)]
16 Skill(#[from] zeph_skills::SkillError),
17
18 #[error(transparent)]
19 Tool(#[from] zeph_tools::executor::ToolError),
20
21 #[error("I/O error: {0}")]
22 Io(#[from] std::io::Error),
23
24 #[error("agent shut down")]
26 Shutdown,
27
28 #[error("context exhausted: {0}")]
30 ContextExhausted(String),
31
32 #[error("tool timed out: {tool_name}")]
34 ToolTimeout { tool_name: zeph_common::ToolName },
35
36 #[error("schema validation failed: {0}")]
38 SchemaValidation(String),
39
40 #[error("{0}")]
41 Other(String),
42}
43
44impl AgentError {
45 #[must_use]
47 pub fn is_context_length_error(&self) -> bool {
48 if let Self::Llm(e) = self {
49 return e.is_context_length_error();
50 }
51 false
52 }
53
54 #[must_use]
56 pub fn is_beta_header_rejected(&self) -> bool {
57 if let Self::Llm(e) = self {
58 return e.is_beta_header_rejected();
59 }
60 false
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn agent_error_detects_context_length_from_llm() {
70 let e = AgentError::Llm(zeph_llm::LlmError::ContextLengthExceeded);
71 assert!(e.is_context_length_error());
72 }
73
74 #[test]
75 fn agent_error_detects_context_length_from_other_message() {
76 let e = AgentError::Llm(zeph_llm::LlmError::Other("context length exceeded".into()));
77 assert!(e.is_context_length_error());
78 }
79
80 #[test]
81 fn agent_error_non_llm_variant_not_detected() {
82 let e = AgentError::Other("something went wrong".into());
83 assert!(!e.is_context_length_error());
84 }
85
86 #[test]
87 fn shutdown_variant_display() {
88 let e = AgentError::Shutdown;
89 assert_eq!(e.to_string(), "agent shut down");
90 }
91
92 #[test]
93 fn context_exhausted_variant_display() {
94 let e = AgentError::ContextExhausted("no space left".into());
95 assert!(e.to_string().contains("no space left"));
96 }
97
98 #[test]
99 fn tool_timeout_variant_display() {
100 let e = AgentError::ToolTimeout {
101 tool_name: "bash".into(),
102 };
103 assert!(e.to_string().contains("bash"));
104 }
105
106 #[test]
107 fn schema_validation_variant_display() {
108 let e = AgentError::SchemaValidation("missing field".into());
109 assert!(e.to_string().contains("missing field"));
110 }
111
112 #[test]
113 fn agent_error_detects_beta_header_rejected() {
114 let e = AgentError::Llm(zeph_llm::LlmError::BetaHeaderRejected {
115 header: "compact-2026-01-12".into(),
116 });
117 assert!(e.is_beta_header_rejected());
118 }
119
120 #[test]
121 fn agent_error_non_llm_variant_not_beta_rejected() {
122 let e = AgentError::Other("something went wrong".into());
123 assert!(!e.is_beta_header_rejected());
124 }
125}