1use std::time::Duration;
4use thiserror::Error;
5use ucm_core::BlockId;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct AgentSessionId(pub uuid::Uuid);
10
11impl AgentSessionId {
12 pub fn new() -> Self {
13 Self(uuid::Uuid::new_v4())
14 }
15}
16
17impl Default for AgentSessionId {
18 fn default() -> Self {
19 Self::new()
20 }
21}
22
23impl std::fmt::Display for AgentSessionId {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}", self.0)
26 }
27}
28
29#[derive(Debug, Error)]
31pub enum AgentError {
32 #[error("Session not found: {0}")]
34 SessionNotFound(AgentSessionId),
35
36 #[error("Session expired: {0}")]
37 SessionExpired(AgentSessionId),
38
39 #[error("Maximum sessions reached: {max}")]
40 MaxSessionsReached { max: usize },
41
42 #[error("Session already closed: {0}")]
43 SessionClosed(AgentSessionId),
44
45 #[error("Block not found: {0}")]
47 BlockNotFound(BlockId),
48
49 #[error("Navigation blocked: {reason}")]
50 NavigationBlocked { reason: String },
51
52 #[error("Invalid edge type: {edge_type}")]
53 InvalidEdgeType { edge_type: String },
54
55 #[error("No path exists from {from} to {to}")]
56 NoPathExists { from: BlockId, to: BlockId },
57
58 #[error("Cannot navigate: history is empty")]
59 EmptyHistory,
60
61 #[error("Depth limit exceeded: {current} > {max}")]
63 DepthLimitExceeded { current: usize, max: usize },
64
65 #[error("Context size limit exceeded: {current} tokens > {max} tokens")]
66 ContextLimitExceeded { current: usize, max: usize },
67
68 #[error("Block limit exceeded: {current} blocks > {max} blocks")]
69 BlockLimitExceeded { current: usize, max: usize },
70
71 #[error("Operation budget exhausted: {operation_type}")]
72 BudgetExhausted { operation_type: String },
73
74 #[error("Rate limit exceeded")]
75 RateLimitExceeded,
76
77 #[error("Operation timed out after {0:?}")]
78 OperationTimeout(Duration),
79
80 #[error("Circuit breaker open: {reason}")]
82 CircuitOpen { reason: String },
83
84 #[error("RAG provider not configured")]
86 RagNotConfigured,
87
88 #[error("RAG search failed: {0}")]
89 RagSearchFailed(String),
90
91 #[error("Coordination lock timeout")]
93 CoordinationLockTimeout,
94
95 #[error("Agent coordination failed: {0}")]
96 CoordinationFailed(String),
97
98 #[error("Block already claimed by another agent: {block_id}")]
99 BlockAlreadyClaimed { block_id: BlockId },
100
101 #[error("Operation not permitted: {operation}")]
103 OperationNotPermitted { operation: String },
104
105 #[error("No results available from last search/find")]
107 NoResultsAvailable,
108
109 #[error("Focus block not set")]
110 NoFocusBlock,
111
112 #[error("Document error: {0}")]
114 DocumentError(String),
115
116 #[error("Engine error: {0}")]
117 EngineError(String),
118
119 #[error("Parse error: {0}")]
120 ParseError(String),
121
122 #[error("Internal error: {0}")]
123 Internal(String),
124}
125
126impl From<ucm_core::Error> for AgentError {
127 fn from(e: ucm_core::Error) -> Self {
128 AgentError::DocumentError(e.to_string())
129 }
130}
131
132impl From<ucm_engine::error::Error> for AgentError {
133 fn from(e: ucm_engine::error::Error) -> Self {
134 AgentError::EngineError(e.to_string())
135 }
136}
137
138impl From<ucl_parser::ParseError> for AgentError {
139 fn from(e: ucl_parser::ParseError) -> Self {
140 AgentError::ParseError(e.to_string())
141 }
142}
143
144pub type Result<T> = std::result::Result<T, AgentError>;
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_session_id_display() {
153 let id = AgentSessionId::new();
154 let display = format!("{}", id);
155 assert!(!display.is_empty());
156 }
157
158 #[test]
159 fn test_error_display() {
160 let err = AgentError::SessionNotFound(AgentSessionId::new());
161 let msg = format!("{}", err);
162 assert!(msg.contains("Session not found"));
163 }
164}