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 prompt: &'a str,
59 pub reasoning_level: ReasoningLevel,
63 pub request_kind: &'a AgentRequestKind,
65 pub replay_transcript: Option<&'a str>,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
71pub enum AgentBackendError {
72 Setup(String),
74 CommandBuild(String),
76}
77
78impl fmt::Display for AgentBackendError {
79 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 Self::Setup(message) | Self::CommandBuild(message) => {
82 write!(formatter, "{message}")
83 }
84 }
85 }
86}
87
88impl Error for AgentBackendError {}
89
90#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
92pub trait AgentBackend: Send + Sync {
93 fn setup(&self, folder: &Path) -> Result<(), AgentBackendError>;
98
99 fn build_command<'request>(
109 &'request self,
110 request: BuildCommandRequest<'request>,
111 ) -> Result<Command, AgentBackendError>;
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_agent_transport_app_server_uses_app_server() {
120 let transport = AgentTransport::AppServer;
122
123 let result = transport.uses_app_server();
125
126 assert!(result);
128 }
129
130 #[test]
131 fn test_agent_transport_cli_does_not_use_app_server() {
132 let transport = AgentTransport::Cli;
134
135 let result = transport.uses_app_server();
137
138 assert!(!result);
140 }
141
142 #[test]
143 fn test_agent_backend_error_setup_displays_message() {
144 let error = AgentBackendError::Setup("setup failed".to_string());
146
147 let display = format!("{error}");
149
150 assert_eq!(display, "setup failed");
152 }
153
154 #[test]
155 fn test_agent_backend_error_command_build_displays_message() {
156 let error = AgentBackendError::CommandBuild("build failed".to_string());
158
159 let display = format!("{error}");
161
162 assert_eq!(display, "build failed");
164 }
165
166 #[test]
167 fn test_agent_backend_error_implements_std_error() {
168 let error = AgentBackendError::Setup("test error".to_string());
170
171 let std_error: &dyn Error = &error;
173
174 assert_eq!(std_error.to_string(), "test error");
176 assert!(std_error.source().is_none());
177 }
178
179 #[test]
180 fn test_agent_backend_error_setup_and_command_build_are_distinct() {
181 let setup_error = AgentBackendError::Setup("failure".to_string());
183 let build_error = AgentBackendError::CommandBuild("failure".to_string());
184
185 assert_ne!(setup_error, build_error);
187 }
188}