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::permission::PermissionMode;
11use crate::model::session::SpeedMode;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum AgentTransport {
16 AppServer,
18 Cli,
20}
21
22impl AgentTransport {
23 pub fn uses_app_server(self) -> bool {
25 matches!(self, Self::AppServer)
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub(crate) enum AgentPromptTransport {
32 Argv,
34 Stdin,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub(crate) enum AppServerThoughtPolicy {
41 None,
43 PhaseLabel,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub struct BuildCommandRequest<'a> {
50 pub attachments: &'a [TurnPromptAttachment],
52 pub folder: &'a Path,
54 pub main_checkout_root: Option<&'a Path>,
57 pub model: &'a str,
59 pub permission_mode: PermissionMode,
61 pub personality_prompt: Option<&'a str>,
63 pub prompt: &'a str,
65 pub reasoning_level: ReasoningLevel,
69 pub request_kind: &'a AgentRequestKind,
71 pub replay_transcript: Option<&'a str>,
73 pub speed_mode: SpeedMode,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum AgentBackendError {
80 Setup(String),
82 CommandBuild(String),
84}
85
86impl fmt::Display for AgentBackendError {
87 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
88 match self {
89 Self::Setup(message) | Self::CommandBuild(message) => {
90 write!(formatter, "{message}")
91 }
92 }
93 }
94}
95
96impl Error for AgentBackendError {}
97
98#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
100pub trait AgentBackend: Send + Sync {
101 fn setup(&self, folder: &Path) -> Result<(), AgentBackendError>;
106
107 fn build_command<'request>(
117 &'request self,
118 request: BuildCommandRequest<'request>,
119 ) -> Result<Command, AgentBackendError>;
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_agent_transport_app_server_uses_app_server() {
128 let transport = AgentTransport::AppServer;
130
131 let result = transport.uses_app_server();
133
134 assert!(result);
136 }
137
138 #[test]
139 fn test_agent_transport_cli_does_not_use_app_server() {
140 let transport = AgentTransport::Cli;
142
143 let result = transport.uses_app_server();
145
146 assert!(!result);
148 }
149
150 #[test]
151 fn test_agent_backend_error_setup_displays_message() {
152 let error = AgentBackendError::Setup("setup failed".to_string());
154
155 let display = format!("{error}");
157
158 assert_eq!(display, "setup failed");
160 }
161
162 #[test]
163 fn test_agent_backend_error_command_build_displays_message() {
164 let error = AgentBackendError::CommandBuild("build failed".to_string());
166
167 let display = format!("{error}");
169
170 assert_eq!(display, "build failed");
172 }
173
174 #[test]
175 fn test_agent_backend_error_implements_std_error() {
176 let error = AgentBackendError::Setup("test error".to_string());
178
179 let std_error: &dyn Error = &error;
181
182 assert_eq!(std_error.to_string(), "test error");
184 assert!(std_error.source().is_none());
185 }
186
187 #[test]
188 fn test_agent_backend_error_setup_and_command_build_are_distinct() {
189 let setup_error = AgentBackendError::Setup("failure".to_string());
191 let build_error = AgentBackendError::CommandBuild("failure".to_string());
192
193 assert_ne!(setup_error, build_error);
195 }
196}