use super::docker::DockerError;
use aether_core::events::AgentMessage;
use std::future::Future;
use std::path::{Path, PathBuf};
use thiserror::Error;
use tokio::sync::mpsc::Sender;
pub struct AgentConfig<'a> {
pub workspace_root: &'a Path,
pub relative_cwd: Option<&'a Path>,
pub task_prompt: &'a str,
}
impl AgentConfig<'_> {
pub fn workspace(&self) -> PathBuf {
match self.relative_cwd {
Some(relative) => self.workspace_root.join(relative),
None => self.workspace_root.to_path_buf(),
}
}
}
pub(crate) fn build_task_prompt(task_prompt: &str, workspace: &Path) -> String {
[
"Complete the following task:".to_string(),
format!("<task>{task_prompt}</task>"),
format!(
"CRITICAL INSTRUCTIONS: when working on this task, you MUST only operate within this directory: {}",
workspace.display()
),
]
.join("\n")
}
pub trait Agent: Send + Sync {
fn run(
&self,
config: AgentConfig<'_>,
tx: Sender<AgentMessage>,
) -> impl Future<Output = Result<(), RunError>> + Send;
}
#[derive(Debug, Error)]
pub enum RunError {
#[error("Agent execution failed: {0}")]
ExecutionFailed(String),
#[error("Failed to send event: {0}")]
ChannelSendFailed(String),
#[error("Agent configuration error: {0}")]
ConfigurationError(String),
#[error(transparent)]
Docker(#[from] DockerError),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_task_prompt_wraps_prompt_and_pins_workspace() {
let prompt = build_task_prompt("write hello.txt", Path::new("/tmp/work"));
assert!(prompt.contains("<task>write hello.txt</task>"));
assert!(prompt.contains("/tmp/work"));
}
}