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 main_checkout_root: Option<&'a Path>,
54 pub model: &'a str,
56 pub prompt: &'a str,
58 pub reasoning_level: ReasoningLevel,
62 pub request_kind: &'a AgentRequestKind,
64 pub replay_transcript: Option<&'a str>,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub enum AgentBackendError {
71 Setup(String),
73 CommandBuild(String),
75}
76
77impl fmt::Display for AgentBackendError {
78 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
79 match self {
80 Self::Setup(message) | Self::CommandBuild(message) => {
81 write!(formatter, "{message}")
82 }
83 }
84 }
85}
86
87impl Error for AgentBackendError {}
88
89#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
91pub trait AgentBackend: Send + Sync {
92 fn setup(&self, folder: &Path) -> Result<(), AgentBackendError>;
97
98 fn build_command<'request>(
108 &'request self,
109 request: BuildCommandRequest<'request>,
110 ) -> Result<Command, AgentBackendError>;
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_agent_transport_app_server_uses_app_server() {
119 let transport = AgentTransport::AppServer;
121
122 let result = transport.uses_app_server();
124
125 assert!(result);
127 }
128
129 #[test]
130 fn test_agent_transport_cli_does_not_use_app_server() {
131 let transport = AgentTransport::Cli;
133
134 let result = transport.uses_app_server();
136
137 assert!(!result);
139 }
140
141 #[test]
142 fn test_agent_backend_error_setup_displays_message() {
143 let error = AgentBackendError::Setup("setup failed".to_string());
145
146 let display = format!("{error}");
148
149 assert_eq!(display, "setup failed");
151 }
152
153 #[test]
154 fn test_agent_backend_error_command_build_displays_message() {
155 let error = AgentBackendError::CommandBuild("build failed".to_string());
157
158 let display = format!("{error}");
160
161 assert_eq!(display, "build failed");
163 }
164
165 #[test]
166 fn test_agent_backend_error_implements_std_error() {
167 let error = AgentBackendError::Setup("test error".to_string());
169
170 let std_error: &dyn Error = &error;
172
173 assert_eq!(std_error.to_string(), "test error");
175 assert!(std_error.source().is_none());
176 }
177
178 #[test]
179 fn test_agent_backend_error_setup_and_command_build_are_distinct() {
180 let setup_error = AgentBackendError::Setup("failure".to_string());
182 let build_error = AgentBackendError::CommandBuild("failure".to_string());
183
184 assert_ne!(setup_error, build_error);
186 }
187}