use crate::agent::{AgentConfig, AgentSession};
use crate::api::{Claude, Session as ClaudeSession};
use crate::deepseek::{DeepSeek, Session as DeepSeekSession};
use crate::utils::prettify;
use anyhow::{anyhow, Context, Result};
use std::io::{self, Write};
pub async fn run_agent_cli(
use_deepseek: bool,
use_opus: bool,
use_haiku: bool,
) -> Result<()> {
println!("🤖 Starting Toast Agent...\n");
let config = AgentConfig::default();
let mut session = AgentSession::new(config);
if use_deepseek {
run_with_deepseek(&mut session, use_opus, use_haiku).await
} else {
run_with_claude(&mut session, use_opus, use_haiku).await
}
}
async fn run_with_claude(
session: &mut AgentSession,
use_opus: bool,
use_haiku: bool,
) -> Result<()> {
let config_dir = dirs::config_dir()
.ok_or_else(|| anyhow!("Could not determine config directory"))?
.join("toast");
let cookie = std::fs::read_to_string(config_dir.join("cookie"))
.context("Failed to read cookie")?
.trim()
.to_string();
let org_id = if let Ok(id) = std::fs::read_to_string(config_dir.join("org_id")) {
id.trim().to_string()
} else {
crate::utils::extract_org_id_from_cookie(&cookie)
.ok_or_else(|| anyhow!("Could not extract org_id from cookie"))?
};
let claude_session = ClaudeSession {
cookie,
user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:137.0) Gecko/20100101 Firefox/137.0".to_string(),
organization_id: org_id,
};
let model = if use_opus {
crate::config::OPUS_MODEL
} else if use_haiku {
crate::config::HAIKU_MODEL
} else {
crate::config::SONNET_MODEL
};
let claude = Claude::new(claude_session, model)?;
println!("Connected to Claude ({model})\n");
let chat_id = claude.create_chat().await.context("Failed to create chat")?;
let system_prompt = session.agent().get_system_prompt();
claude.send_message(&chat_id, &system_prompt, &[]).await
.context("Failed to send system prompt")?;
let stdin = io::stdin();
let mut stdout = io::stdout();
loop {
print!("You: ");
stdout.flush()?;
let mut input = String::new();
match stdin.read_line(&mut input) {
Ok(0) => break, Ok(_) => {
let input = input.trim();
if input.is_empty() {
continue;
}
if matches!(input, "exit" | "quit" | "/exit" | "x") {
break;
}
let response = claude.send_message(&chat_id, input, &[]).await
.context("Failed to send message")?;
println!("\nClaude: {}\n", prettify(&response));
process_agent_response_claude(session, &claude, &chat_id, &response).await?;
session.agent().reset();
}
Err(e) => {
eprintln!("Error reading input: {e}");
break;
}
}
}
claude.delete_chat(&chat_id).await.ok();
println!("\n👋 Goodbye!");
Ok(())
}
async fn run_with_deepseek(
session: &mut AgentSession,
use_opus: bool,
use_haiku: bool,
) -> Result<()> {
let config_dir = dirs::config_dir()
.ok_or_else(|| anyhow!("Could not determine config directory"))?
.join("toast")
.join("deepseek");
let auth_token = std::fs::read_to_string(config_dir.join("auth_token"))
.context("Failed to read auth token")?
.trim()
.to_string();
let cookies = serde_json::from_str(
&std::fs::read_to_string(config_dir.join("cookies.json"))
.context("Failed to read cookies")?
)?;
let deepseek_session = DeepSeekSession { auth_token, cookies };
let mut deepseek = DeepSeek::new(deepseek_session)?;
let model = if use_opus {
"deepseek-r1"
} else if use_haiku {
"deepseek-lite"
} else {
"deepseek-r1"
};
println!("Connected to DeepSeek ({model})\n");
let chat_id = deepseek.create_chat_session().await
.context("Failed to create chat session")?;
let thinking_mode = if model == "deepseek-r1" {
crate::deepseek::ThinkingMode::Detailed
} else {
crate::deepseek::ThinkingMode::Simple
};
let search_mode = crate::deepseek::SearchMode::Disabled;
let system_prompt = session.agent().get_system_prompt();
let stdin = io::stdin();
let mut stdout = io::stdout();
let mut first_message = true;
loop {
print!("You: ");
stdout.flush()?;
let mut input = String::new();
match stdin.read_line(&mut input) {
Ok(0) => break, Ok(_) => {
let input = input.trim();
if input.is_empty() {
continue;
}
if matches!(input, "exit" | "quit" | "/exit" | "x") {
break;
}
let system_prompt_opt = if first_message {
first_message = false;
Some(system_prompt.as_str())
} else {
None
};
let response = deepseek.chat_completion(
&chat_id,
input,
None,
thinking_mode,
search_mode,
system_prompt_opt,
).await.context("Failed to send message")?;
println!("\nDeepSeek: {}\n", prettify(&response));
process_agent_response_deepseek(
session,
&mut deepseek,
&chat_id,
&response,
thinking_mode,
search_mode,
).await?;
session.agent().reset();
}
Err(e) => {
eprintln!("Error reading input: {e}");
break;
}
}
}
println!("\n👋 Goodbye!");
Ok(())
}
async fn process_agent_response_claude(
session: &mut AgentSession,
claude: &Claude,
chat_id: &str,
response: &str,
) -> Result<()> {
let tool_results = session.agent().process_tool_calls(response).await?;
if !tool_results.is_empty() {
let mut result_message = String::from("Tool execution results:\n\n");
for (tool_name, output) in tool_results {
result_message.push_str(&format!("[{tool_name}]\n{output}\n\n"));
}
let follow_up = claude.send_message(chat_id, &result_message, &[]).await
.context("Failed to send tool results")?;
println!("Claude: {}\n", prettify(&follow_up));
Box::pin(process_agent_response_claude(session, claude, chat_id, &follow_up)).await?;
}
Ok(())
}
async fn process_agent_response_deepseek(
session: &mut AgentSession,
deepseek: &mut DeepSeek,
chat_id: &str,
response: &str,
thinking_mode: crate::deepseek::ThinkingMode,
search_mode: crate::deepseek::SearchMode,
) -> Result<()> {
let tool_results = session.agent().process_tool_calls(response).await?;
if !tool_results.is_empty() {
let mut result_message = String::from("Tool execution results:\n\n");
for (tool_name, output) in tool_results {
result_message.push_str(&format!("[{tool_name}]\n{output}\n\n"));
}
let follow_up = deepseek.chat_completion(
chat_id,
&result_message,
None,
thinking_mode,
search_mode,
None,
).await.context("Failed to send tool results")?;
println!("DeepSeek: {}\n", prettify(&follow_up));
Box::pin(process_agent_response_deepseek(
session,
deepseek,
chat_id,
&follow_up,
thinking_mode,
search_mode,
)).await?;
}
Ok(())
}