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;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum AgentTransport {
14 AppServer,
16 Cli,
18}
19
20impl AgentTransport {
21 pub fn uses_app_server(self) -> bool {
23 matches!(self, Self::AppServer)
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub(crate) enum AgentPromptTransport {
30 Argv,
32 Stdin,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub(crate) enum AppServerThoughtPolicy {
39 None,
41 PhaseLabel,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct BuildCommandRequest<'a> {
48 pub attachments: &'a [TurnPromptAttachment],
50 pub folder: &'a Path,
52 pub main_checkout_root: Option<&'a Path>,
55 pub model: &'a str,
57 pub personality_prompt: Option<&'a str>,
59 pub prompt: &'a str,
61 pub reasoning_level: ReasoningLevel,
65 pub request_kind: &'a AgentRequestKind,
67 pub replay_transcript: Option<&'a str>,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
73pub enum AgentBackendError {
74 Setup(String),
76 CommandBuild(String),
78}
79
80impl fmt::Display for AgentBackendError {
81 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 Self::Setup(message) | Self::CommandBuild(message) => {
84 write!(formatter, "{message}")
85 }
86 }
87 }
88}
89
90impl Error for AgentBackendError {}
91
92#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
94pub trait AgentBackend: Send + Sync {
95 fn setup(&self, folder: &Path) -> Result<(), AgentBackendError>;
100
101 fn build_command<'request>(
111 &'request self,
112 request: BuildCommandRequest<'request>,
113 ) -> Result<Command, AgentBackendError>;
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_agent_transport_app_server_uses_app_server() {
122 let transport = AgentTransport::AppServer;
124
125 let result = transport.uses_app_server();
127
128 assert!(result);
130 }
131
132 #[test]
133 fn test_agent_transport_cli_does_not_use_app_server() {
134 let transport = AgentTransport::Cli;
136
137 let result = transport.uses_app_server();
139
140 assert!(!result);
142 }
143
144 #[test]
145 fn test_agent_backend_error_setup_displays_message() {
146 let error = AgentBackendError::Setup("setup failed".to_string());
148
149 let display = format!("{error}");
151
152 assert_eq!(display, "setup failed");
154 }
155
156 #[test]
157 fn test_agent_backend_error_command_build_displays_message() {
158 let error = AgentBackendError::CommandBuild("build failed".to_string());
160
161 let display = format!("{error}");
163
164 assert_eq!(display, "build failed");
166 }
167
168 #[test]
169 fn test_agent_backend_error_implements_std_error() {
170 let error = AgentBackendError::Setup("test error".to_string());
172
173 let std_error: &dyn Error = &error;
175
176 assert_eq!(std_error.to_string(), "test error");
178 assert!(std_error.source().is_none());
179 }
180
181 #[test]
182 fn test_agent_backend_error_setup_and_command_build_are_distinct() {
183 let setup_error = AgentBackendError::Setup("failure".to_string());
185 let build_error = AgentBackendError::CommandBuild("failure".to_string());
186
187 assert_ne!(setup_error, build_error);
189 }
190}