use std::path::Path;
use std::process::Command;
use super::app_server::build_codex_app_server_command;
use super::backend::{AgentBackend, AgentBackendError, BuildCommandRequest};
pub(super) struct CodexBackend;
impl AgentBackend for CodexBackend {
fn setup(&self, _folder: &Path) -> Result<(), AgentBackendError> {
Ok(())
}
fn build_command<'request>(
&'request self,
request: BuildCommandRequest<'request>,
) -> Result<Command, AgentBackendError> {
Ok(build_codex_app_server_command(
request.folder,
request.model,
))
}
}
#[cfg(test)]
mod tests {
use tempfile::tempdir;
use super::*;
use crate::channel::AgentRequestKind;
fn session_start_request_kind() -> AgentRequestKind {
AgentRequestKind::SessionStart
}
fn session_resume_request_kind(_replay_transcript: Option<&str>) -> AgentRequestKind {
AgentRequestKind::SessionResume
}
#[test]
fn build_command_builds_app_server_runtime_for_start_requests() {
let temp_directory = tempdir().expect("failed to create temp dir");
let backend = CodexBackend;
let command = AgentBackend::build_command(
&backend,
BuildCommandRequest {
attachments: &[],
folder: temp_directory.path(),
main_checkout_root: None,
replay_transcript: None,
model: "gpt-5.5",
prompt: "Run checks",
reasoning_level: crate::model::agent::ReasoningLevel::High,
request_kind: &session_start_request_kind(),
},
)
.expect("command build should succeed");
let debug_command = format!("{command:?}");
assert!(debug_command.contains("codex"));
assert!(debug_command.contains("app-server"));
assert!(debug_command.contains("stdio://"));
}
#[test]
fn build_command_builds_app_server_runtime_for_resume_requests() {
let temp_directory = tempdir().expect("failed to create temp dir");
let backend = CodexBackend;
let command = AgentBackend::build_command(
&backend,
BuildCommandRequest {
attachments: &[],
folder: temp_directory.path(),
main_checkout_root: None,
replay_transcript: None,
model: "gpt-5.5",
prompt: "Continue edits",
reasoning_level: crate::model::agent::ReasoningLevel::High,
request_kind: &session_resume_request_kind(Some("previous assistant output")),
},
)
.expect("resume command build should succeed");
let arguments = command
.get_args()
.map(|argument| argument.to_string_lossy().into_owned())
.collect::<Vec<_>>();
assert_eq!(
arguments,
vec!["--model", "gpt-5.5", "app-server", "--listen", "stdio://"]
);
}
#[test]
fn build_command_accepts_gpt_54_mini_model() {
let temp_directory = tempdir().expect("failed to create temp dir");
let backend = CodexBackend;
let command = AgentBackend::build_command(
&backend,
BuildCommandRequest {
attachments: &[],
folder: temp_directory.path(),
main_checkout_root: None,
replay_transcript: None,
model: crate::model::agent::AgentModel::Gpt54Mini.as_str(),
prompt: "Run a quick edit",
reasoning_level: crate::model::agent::ReasoningLevel::Medium,
request_kind: &session_start_request_kind(),
},
)
.expect("mini model command build should succeed");
let arguments = command
.get_args()
.map(|argument| argument.to_string_lossy().into_owned())
.collect::<Vec<_>>();
assert_eq!(
arguments,
vec![
"--model",
"gpt-5.4-mini",
"app-server",
"--listen",
"stdio://"
]
);
}
#[test]
fn build_command_builds_app_server_runtime_for_utility_prompts() {
let temp_directory = tempdir().expect("failed to create temp dir");
let backend = CodexBackend;
let command = AgentBackend::build_command(
&backend,
BuildCommandRequest {
attachments: &[],
folder: temp_directory.path(),
main_checkout_root: None,
replay_transcript: None,
model: "gpt-5.5",
prompt: "Generate title",
reasoning_level: crate::model::agent::ReasoningLevel::Low,
request_kind: &AgentRequestKind::UtilityPrompt,
},
)
.expect("utility command build should succeed");
assert_eq!(command.get_current_dir(), Some(temp_directory.path()));
}
}