ag_agent/agent/
backend.rs1use std::error::Error;
2use std::fmt;
3use std::path::Path;
4use std::process::Command;
5
6use crate::channel::AgentRequestKind;
7use crate::model::agent::ReasoningLevel;
8use crate::model::turn_prompt::TurnPromptAttachment;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum AgentTransport {
13 AppServer,
15 Cli,
17}
18
19impl AgentTransport {
20 pub fn uses_app_server(self) -> bool {
22 matches!(self, Self::AppServer)
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub(crate) enum AgentPromptTransport {
29 Argv,
31 Stdin,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub(crate) enum AppServerThoughtPolicy {
38 None,
40 PhaseLabel,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct BuildCommandRequest<'a> {
47 pub attachments: &'a [TurnPromptAttachment],
49 pub folder: &'a Path,
51 pub prompt: &'a str,
53 pub request_kind: &'a AgentRequestKind,
55 pub model: &'a str,
57 pub reasoning_level: ReasoningLevel,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum AgentBackendError {
66 Setup(String),
68 CommandBuild(String),
70}
71
72impl fmt::Display for AgentBackendError {
73 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
74 match self {
75 Self::Setup(message) | Self::CommandBuild(message) => {
76 write!(formatter, "{message}")
77 }
78 }
79 }
80}
81
82impl Error for AgentBackendError {}
83
84#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
86pub trait AgentBackend: Send + Sync {
87 fn setup(&self, folder: &Path) -> Result<(), AgentBackendError>;
92
93 fn build_command<'request>(
103 &'request self,
104 request: BuildCommandRequest<'request>,
105 ) -> Result<Command, AgentBackendError>;
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_agent_transport_app_server_uses_app_server() {
114 let transport = AgentTransport::AppServer;
116
117 let result = transport.uses_app_server();
119
120 assert!(result);
122 }
123
124 #[test]
125 fn test_agent_transport_cli_does_not_use_app_server() {
126 let transport = AgentTransport::Cli;
128
129 let result = transport.uses_app_server();
131
132 assert!(!result);
134 }
135
136 #[test]
137 fn test_agent_backend_error_setup_displays_message() {
138 let error = AgentBackendError::Setup("setup failed".to_string());
140
141 let display = format!("{error}");
143
144 assert_eq!(display, "setup failed");
146 }
147
148 #[test]
149 fn test_agent_backend_error_command_build_displays_message() {
150 let error = AgentBackendError::CommandBuild("build failed".to_string());
152
153 let display = format!("{error}");
155
156 assert_eq!(display, "build failed");
158 }
159
160 #[test]
161 fn test_agent_backend_error_implements_std_error() {
162 let error = AgentBackendError::Setup("test error".to_string());
164
165 let std_error: &dyn Error = &error;
167
168 assert_eq!(std_error.to_string(), "test error");
170 assert!(std_error.source().is_none());
171 }
172
173 #[test]
174 fn test_agent_backend_error_setup_and_command_build_are_distinct() {
175 let setup_error = AgentBackendError::Setup("failure".to_string());
177 let build_error = AgentBackendError::CommandBuild("failure".to_string());
178
179 assert_ne!(setup_error, build_error);
181 }
182}