use anyhow::Result;
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Instant;
use tokio::process::Command;
use tracing::{info, warn};
use crate::agent::{Task, TaskResult};
use crate::config::ClaudeConfig;
use crate::identity::AgentIdentity;
#[derive(Debug, Clone)]
pub struct PersistentClaudeSession {
pub identity: AgentIdentity,
pub working_dir: PathBuf,
pub claude_config: ClaudeConfig,
pub session_id: String,
pub env_vars: HashMap<String, String>,
}
impl PersistentClaudeSession {
pub async fn new(
identity: AgentIdentity,
working_dir: PathBuf,
claude_config: ClaudeConfig,
) -> Result<Self> {
let session_id = format!("session-{}-{}", identity.agent_id, uuid::Uuid::new_v4());
let mut env_vars = crate::utils::common::collections::new_hashmap();
env_vars.insert("AGENT_ID".to_string(), identity.agent_id.clone());
env_vars.insert(
"AGENT_TYPE".to_string(),
identity.specialization.name().to_string(),
);
env_vars.insert("SESSION_ID".to_string(), session_id.clone());
Ok(Self {
identity,
working_dir,
claude_config,
session_id,
env_vars,
})
}
pub async fn initialize(&mut self) -> Result<()> {
info!("🚀 Initializing session: {}", self.session_id);
tokio::fs::create_dir_all(&self.working_dir).await?;
Ok(())
}
pub async fn execute_task(&mut self, task: Task) -> Result<TaskResult> {
info!("📋 Executing task {}: {}", task.id, task.description);
let start = Instant::now();
let prompt = self.generate_task_prompt(&task);
let response = self.execute_prompt(&prompt).await?;
let duration = start.elapsed();
Ok(TaskResult {
success: true,
output: serde_json::json!({
"task_id": task.id,
"agent_id": self.identity.agent_id.clone(),
"response": response,
"files_created": [],
"files_modified": [],
}),
error: None,
duration,
})
}
pub async fn execute_command(&mut self, command: &str) -> Result<String> {
info!("🔧 Executing command: {}", command);
let output = Command::new("sh")
.arg("-c")
.arg(command)
.current_dir(&self.working_dir)
.output()
.await?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
if !output.status.success() {
warn!("Command failed: {}", stderr);
}
Ok(format!("{}\n{}", stdout, stderr))
}
async fn execute_prompt(&self, prompt: &str) -> Result<String> {
self.execute_prompt_real_api(prompt).await
}
async fn execute_prompt_real_api(&self, prompt: &str) -> Result<String> {
use crate::providers::claude_api::{ClaudeApiClient, Message};
info!("🤖 Claude prompt (real API): {}", prompt);
let client = ClaudeApiClient::new(None)?;
let system_prompt = format!(
"You are a {} specialist agent. Workspace: {}.",
self.identity.specialization.name(),
self.working_dir.display()
);
let messages = vec![Message {
role: "user".to_string(),
content: prompt.to_string(),
}];
let response = client
.create_completion(
&self.claude_config.model,
messages,
4096,
Some(0.0),
Some(system_prompt),
)
.await?;
let text = response
.content
.into_iter()
.map(|block| block.text)
.collect::<Vec<_>>()
.join("");
Ok(text)
}
fn generate_task_prompt(&self, task: &Task) -> String {
format!(
"As a {} agent, please complete the following task:\n\n\
Task: {}\n\
Priority: {:?}\n\
Type: {:?}\n\
{}\n\n\
Please implement this task according to best practices for {}.",
self.identity.specialization.name(),
task.description,
task.priority,
task.task_type,
task.details.as_deref().unwrap_or(""),
self.identity.specialization.name()
)
}
}