ag_agent/agent/
backend.rs1use std::error::Error;
2use std::fmt;
3use std::path::Path;
4use std::process::Command;
5
6use ag_protocol::TurnPromptAttachment;
7
8use crate::channel::AgentRequestKind;
9use crate::model::agent::ReasoningLevel;
10use crate::model::session::SpeedMode;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum AgentTransport {
15 AppServer,
17 Cli,
19}
20
21impl AgentTransport {
22 pub fn uses_app_server(self) -> bool {
24 matches!(self, Self::AppServer)
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub(crate) enum AgentPromptTransport {
31 Argv,
33 Stdin,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub(crate) enum AppServerThoughtPolicy {
40 None,
42 PhaseLabel,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct BuildCommandRequest<'a> {
49 pub attachments: &'a [TurnPromptAttachment],
51 pub folder: &'a Path,
53 pub main_checkout_root: Option<&'a Path>,
56 pub model: &'a str,
58 pub personality_prompt: Option<&'a str>,
60 pub prompt: &'a str,
62 pub reasoning_level: ReasoningLevel,
66 pub request_kind: &'a AgentRequestKind,
68 pub replay_transcript: Option<&'a str>,
70 pub speed_mode: SpeedMode,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum AgentBackendError {
77 Setup(String),
79 CommandBuild(String),
81}
82
83impl fmt::Display for AgentBackendError {
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Self::Setup(message) | Self::CommandBuild(message) => {
87 write!(formatter, "{message}")
88 }
89 }
90 }
91}
92
93impl Error for AgentBackendError {}
94
95#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
97pub trait AgentBackend: Send + Sync {
98 fn setup(&self, folder: &Path) -> Result<(), AgentBackendError>;
103
104 fn build_command<'request>(
114 &'request self,
115 request: BuildCommandRequest<'request>,
116 ) -> Result<Command, AgentBackendError>;
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_agent_transport_app_server_uses_app_server() {
125 let transport = AgentTransport::AppServer;
127
128 let result = transport.uses_app_server();
130
131 assert!(result);
133 }
134
135 #[test]
136 fn test_agent_transport_cli_does_not_use_app_server() {
137 let transport = AgentTransport::Cli;
139
140 let result = transport.uses_app_server();
142
143 assert!(!result);
145 }
146
147 #[test]
148 fn test_agent_backend_error_setup_displays_message() {
149 let error = AgentBackendError::Setup("setup failed".to_string());
151
152 let display = format!("{error}");
154
155 assert_eq!(display, "setup failed");
157 }
158
159 #[test]
160 fn test_agent_backend_error_command_build_displays_message() {
161 let error = AgentBackendError::CommandBuild("build failed".to_string());
163
164 let display = format!("{error}");
166
167 assert_eq!(display, "build failed");
169 }
170
171 #[test]
172 fn test_agent_backend_error_implements_std_error() {
173 let error = AgentBackendError::Setup("test error".to_string());
175
176 let std_error: &dyn Error = &error;
178
179 assert_eq!(std_error.to_string(), "test error");
181 assert!(std_error.source().is_none());
182 }
183
184 #[test]
185 fn test_agent_backend_error_setup_and_command_build_are_distinct() {
186 let setup_error = AgentBackendError::Setup("failure".to_string());
188 let build_error = AgentBackendError::CommandBuild("failure".to_string());
189
190 assert_ne!(setup_error, build_error);
192 }
193}