use anyhow::Result;
use cursive::{Cursive, CursiveExt};
use log::{error, info, warn};
pub mod agent;
pub mod session;
pub mod state;
pub mod types;
pub mod ui;
pub mod utils;
pub use agent::AgentCommand;
pub use session::ChatSession;
pub use state::AdvancedChatState;
pub use types::{AgentState, ChatMessage};
pub use ui::{update_ui_displays, AdvancedChatUI};
pub async fn run_advanced_agent_chat() -> Result<()> {
println!("Starting OSVM Advanced Agent Chat Interface...");
let term_var = std::env::var("TERM").unwrap_or_default();
if term_var.is_empty() || term_var == "dumb" {
println!(
"No suitable terminal detected (TERM={}), falling back to demo mode",
term_var
);
return run_advanced_demo_mode().await;
}
println!("Terminal detected: {}", term_var);
let state = AdvancedChatState::new()?;
if let Err(e) = state.initialize().await {
warn!(
"MCP initialization failed: {}, continuing without MCP tools",
e
);
}
let _default_session_id = state.create_session("Main Chat".to_string())?;
let result = tokio::task::spawn_blocking(move || run_advanced_ui_sync(state)).await;
match result {
Ok(Ok(())) => Ok(()),
Ok(Err(e)) => {
eprintln!("UI Error: {}", e);
run_advanced_demo_mode().await
}
Err(e) => {
eprintln!("Failed to run UI: {}", e);
run_advanced_demo_mode().await
}
}
}
fn run_advanced_ui_sync(state: AdvancedChatState) -> Result<()> {
let mut siv = Cursive::default();
let term_size = siv.screen_size();
println!("Terminal size: {}x{}", term_size.x, term_size.y);
if (term_size.x > 0 && term_size.x < 60) || (term_size.y > 0 && term_size.y < 15) {
eprintln!(
"Terminal too small: {}x{} (minimum: 60x15)",
term_size.x, term_size.y
);
eprintln!("Please resize your terminal and try again.");
return Err(anyhow::anyhow!(
"Terminal size too small for advanced chat interface"
));
}
if term_size.x == 0 || term_size.y == 0 {
println!("Unknown terminal size, attempting to initialize anyway...");
}
siv.set_window_title("OSVM Advanced Agent Chat");
siv.set_autorefresh(true);
siv.add_global_callback(cursive::event::Event::WindowResize, |siv| {
if let Err(e) = handle_window_resize(siv) {
eprintln!("⚠️ Resize handling error: {}", e);
}
});
siv.set_user_data(state.clone());
let ui = AdvancedChatUI {
state: state.clone(),
};
state.start_spinner_animation(siv.cb_sink().clone());
start_periodic_updates(&mut siv, state.clone());
ui.setup_far_ui(&mut siv);
ui::handlers::setup_input_suggestions(&mut siv, state.clone());
ui.setup_suggestion_hotkeys(&mut siv);
ui.setup_action_hotkeys(&mut siv);
update_ui_displays(&mut siv);
siv.run();
Ok(())
}
async fn run_advanced_demo_mode() -> Result<()> {
println!("Running Advanced Agent Chat in demo mode");
println!();
let state = AdvancedChatState::new()?;
state.initialize().await?;
println!("Advanced Features:");
println!(" • AI-powered input parsing and tool planning");
println!(" • Multiple chat sessions with background agent execution");
println!(" • Session recording and agent control (run/pause/stop)");
println!(" • Comprehensive MCP tool integration");
println!();
println!("Available MCP Tools:");
let available_tools = state
.available_tools
.read()
.map_err(|e| anyhow::anyhow!("Failed to read tools: {}", e))?;
if available_tools.is_empty() {
println!(" ⚠️ No MCP servers configured");
println!(" Run 'osvm mcp setup' to configure Solana tools");
} else {
for (server_id, tools) in available_tools.iter() {
println!(" 📦 Server: {}", server_id);
if tools.is_empty() {
println!(" Loading tools...");
} else {
for (i, tool) in tools.iter().enumerate() {
if i < 5 {
println!(
" • {}: {}",
tool.name,
tool.description
.as_ref()
.unwrap_or(&"No description".to_string())
);
}
}
if tools.len() > 5 {
println!(" ... and {} more tools", tools.len() - 5);
}
}
}
}
println!();
println!("Advanced FAR-Style Layout:");
println!(" ┌─ Chat Sessions ─┬─ Active Chat History ─────────────────┐");
println!(" | Main Chat | You: What's my balance? |");
println!(" | Analysis | Agent: I'll analyze your request... |");
println!(" | Paused Chat | Plan: Using get_balance tool |");
println!(" | | Executing: get_balance |");
println!(" | + New Chat | Result: 2.5 SOL |");
println!(" | | Agent: Your balance is 2.5 SOL |");
println!(" | Run | |");
println!(" | Pause | Agent Status: Idle |");
println!(" | Stop | |");
println!(" │ │ You: [Input field...] [Send] │");
println!(" | Record | [Clear] [Export] [Settings] [Help] |");
println!(" | Stop Rec | |");
println!(" └─────────────────┴────────────────────────────────────────┘");
println!();
println!("Key Improvements:");
println!(" • AI service integration for intelligent tool selection");
println!(" • Background agent processing with state management");
println!(" • Professional FAR-style interface design");
println!(" • Session recording and replay capabilities");
println!(" • Multi-chat support with independent agent states");
println!();
println!("Reply Suggestions (press 1-5 to insert, ESC to hide):");
println!();
println!("\x1b[44;37m 1. Show recent transactions \x1b[0m");
println!("\x1b[44;37m 2. What's the current SOL price? \x1b[0m");
println!("\x1b[44;37m 3. How do I stake my SOL? \x1b[0m");
println!("\x1b[44;37m 4. Send 0.5 SOL to address \x1b[0m");
println!("\x1b[44;37m 5. Check my staking rewards \x1b[0m");
println!();
println!(" Suggestions Features:");
println!(" • Generated by AI using chat context");
println!(" • Blue background with white text for visibility");
println!(" • Press number key (1-5) to insert at cursor");
println!(" • ESC hides suggestions");
println!();
println!("Note: Full TUI interface requires a proper terminal.");
println!(" Run 'osvm chat --advanced' in a terminal to see the full UI.");
Ok(())
}
fn handle_window_resize(siv: &mut Cursive) -> anyhow::Result<()> {
println!("\n🔄 Window resize detected, refreshing layout...");
let new_size = siv.screen_size();
println!("📐 New terminal size: {}x{}", new_size.x, new_size.y);
if (new_size.x > 0 && new_size.x < 60) || (new_size.y > 0 && new_size.y < 15) {
println!(
"⚠️ Terminal too small after resize: {}x{}",
new_size.x, new_size.y
);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
siv.add_layer(
cursive::views::Dialog::info(format!(
"Terminal too small: {}x{} (minimum: 60x15)\nPlease resize your terminal.",
new_size.x, new_size.y
))
.title("Resize Required")
.button("OK", |s| {
s.pop_layer();
}),
);
}));
if result.is_err() {
println!("⚠️ Could not show resize warning dialog");
}
return Ok(());
}
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
update_ui_displays(siv);
}));
match result {
Ok(_) => {
println!(
"✅ Layout refreshed successfully for size {}x{}",
new_size.x, new_size.y
);
Ok(())
}
Err(_) => {
println!("⚠️ Layout refresh failed, but continuing...");
Err(anyhow::anyhow!("Layout refresh failed during resize"))
}
}
}
fn start_periodic_updates(siv: &mut Cursive, state: AdvancedChatState) {
use std::thread;
use std::time::Duration;
let cb_sink = siv.cb_sink().clone();
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(30));
if cb_sink
.send(Box::new(move |siv| {
update_ui_displays(siv);
}))
.is_err()
{
break; }
}
});
}