wasmind_cli 0.1.0

Command-line interface for Wasmind AI agent coordination system
Documentation
use std::{collections::HashMap, sync::Arc, time::Duration};
use wasmind::coordinator::WasmindCoordinator;
use wasmind::wasmind_actor_loader::LoadedActor;
use wasmind_actor_utils::STARTING_SCOPE;
use wasmind_actor_utils::common_messages::{
    Scope,
    assistant::{ChatState, ChatStateUpdated, Status, StatusUpdate, WaitReason},
    tools::{
        AwaitingSystemDetails, ToolCallResult, ToolCallStatus, ToolCallStatusUpdate, UIDisplayInfo,
    },
    ui::{NotificationLevel, UserNotification},
};
use wasmind_actor_utils::llm_client_types::{Function, SystemChatMessage, ToolCall};
use wasmind_cli::{TuiResult, tui};

use crate::utils::create_spawn_agent_message;

pub fn spawn_agent(scope: &Scope, coordinator: &mut WasmindCoordinator) -> TuiResult<Scope> {
    let (spawn_agent_message, agent_scope) = create_spawn_agent_message(
        &format!("Sub Manager {}", rand::random::<u64>()),
        Some(scope),
    );
    coordinator.broadcast_common_message(spawn_agent_message, false)?;
    let status = match rand::random_range(0..8) {
        0 => Status::Processing {
            request_id: "Filler".to_string(),
        },
        1 => Status::Done {
            result: Err("Filler".to_string()),
        },
        2 => Status::Wait {
            reason: WaitReason::WaitingForAllActorsReady,
        },
        3 => Status::Wait {
            reason: WaitReason::WaitingForUserInput,
        },
        4 => Status::Wait {
            reason: WaitReason::WaitingForSystemInput {
                required_scope: None,
                interruptible_by_user: true,
            },
        },
        5 => Status::Wait {
            reason: WaitReason::WaitingForAgentCoordination {
                originating_request_id: "Filler".to_string(),
                coordinating_tool_name: "Filler".to_string(),
                target_agent_scope: None,
                user_can_interrupt: true,
            },
        },
        6 => Status::Wait {
            reason: WaitReason::WaitingForTools {
                originating_request_id: "example-request-id".to_string(),
                tool_calls: HashMap::new(),
            },
        },
        7 => Status::Wait {
            reason: WaitReason::WaitingForLiteLLM,
        },
        _ => unreachable!(),
    };
    coordinator.broadcast_common_message_in_scope(StatusUpdate { status }, &agent_scope, false)?;

    if rand::random_bool(0.25) {
        spawn_agent(&agent_scope, coordinator)
    } else {
        Ok(agent_scope)
    }
}

fn create_sample_tool_calls() -> Vec<ToolCall> {
    vec![
        ToolCall {
            id: "tool_call_1_received".to_string(),
            tool_type: "function".to_string(),
            function: Function {
                name: "read_file".to_string(),
                arguments: r#"{"path": "/tmp/test.txt"}"#.to_string(),
            },
            index: Some(0),
        },
        ToolCall {
            id: "tool_call_2_awaiting".to_string(),
            tool_type: "function".to_string(),
            function: Function {
                name: "execute_bash".to_string(),
                arguments: r#"{"command": "ls -la"}"#.to_string(),
            },
            index: Some(1),
        },
        ToolCall {
            id: "tool_call_3_done".to_string(),
            tool_type: "function".to_string(),
            function: Function {
                name: "search_files".to_string(),
                arguments: r#"{"pattern": "TODO", "path": "./src"}"#.to_string(),
            },
            index: Some(2),
        },
        ToolCall {
            id: "tool_call_4_pending".to_string(),
            tool_type: "function".to_string(),
            function: Function {
                name: "write_file".to_string(),
                arguments: r#"{"path": "/tmp/output.md", "content": "Analysis Results: TODO items report"}"#.to_string(),
            },
            index: Some(3),
        },
    ]
}

fn create_tool_status_updates() -> Vec<ToolCallStatusUpdate> {
    vec![
        // First tool call - Received state
        ToolCallStatusUpdate {
            id: "tool_call_1_received".to_string(),
            originating_request_id: "example-request-id".to_string(),
            status: ToolCallStatus::Received {
                display_info: UIDisplayInfo {
                    collapsed: "/tmp/test.txt: Reading in progress...".to_string(),
                    expanded: Some("File: /tmp/test.txt\nOperation: Read file\nStatus: Reading file contents to analyze the data".to_string()),
                },
            },
        },
        // Second tool call - AwaitingSystem state
        ToolCallStatusUpdate {
            id: "tool_call_2_awaiting".to_string(),
            originating_request_id: "example-request-id".to_string(),
            status: ToolCallStatus::AwaitingSystem {
                details: AwaitingSystemDetails {
                    required_scope: Some("bash_executor".to_string()),
                    ui_display_info: UIDisplayInfo {
                        collapsed: "ls -la: Awaiting system approval".to_string(),
                        expanded: Some("Command: ls -la\nStatus: Waiting for system approval to execute\nDescription: This command will list all files in the current directory with detailed information.".to_string()),
                    },
                },
            },
        },
        // Third tool call - Done state (success)
        ToolCallStatusUpdate {
            id: "tool_call_3_done".to_string(),
            originating_request_id: "example-request-id".to_string(),
            status: ToolCallStatus::Done {
                result: Ok(ToolCallResult {
                    content: "Found 42 TODO items in 8 files:\n- src/main.rs: 5 items\n- src/utils.rs: 3 items\n- src/handlers.rs: 12 items\n...".to_string(),
                    ui_display_info: UIDisplayInfo {
                        collapsed: "./src: 42 TODOs found in 8 files".to_string(),
                        expanded: Some("Operation: Search for TODO items\nPattern: TODO\nPath: ./src\n\nResults: Found 42 TODO items across 8 files:\n\n1. src/main.rs (5 items):\n   - Line 23: TODO: Implement error handling\n   - Line 45: TODO: Add logging\n   - Line 67: TODO: Optimize performance\n   - Line 89: TODO: Write tests\n   - Line 101: TODO: Add documentation\n\n2. src/utils.rs (3 items):\n   - Line 12: TODO: Refactor this function\n   - Line 34: TODO: Add input validation\n   - Line 56: TODO: Handle edge cases\n\n[... more results ...]".to_string()),
                    },
                }),
            },
        },
    ]
}

pub async fn run() -> TuiResult<()> {
    let tui_config = wasmind_cli::config::TuiConfig::default().parse()?;

    let context = Arc::new(wasmind::context::WasmindContext::new::<LoadedActor>(vec![]));
    let mut coordinator: WasmindCoordinator = WasmindCoordinator::new(context.clone());

    let tui = tui::Tui::new(
        tui_config,
        coordinator.get_sender(),
        Some("Filler user prompt...".to_string()),
        context.clone(),
    );

    coordinator
        .start_wasmind(&[], "Root Agent".to_string())
        .await?;

    tui.run();

    // Spawn some agents
    for _ in 0..100 {
        spawn_agent(&STARTING_SCOPE.to_string(), &mut coordinator)?;
    }

    // Create VERY large sample chat state for testing scrolling performance
    let large_system_content = format!(
        "{}{}{}{}{}{}{}",
        "You are an advanced AI assistant with extensive capabilities in software development, data analysis, creative writing, and problem-solving. ".repeat(50),
        "\n\n[MARKER-1] Your responses should be thorough, well-structured, and demonstrate deep understanding of the topics discussed. ".repeat(3),
        "[MARKER-2] When providing code examples, include detailed explanations and consider edge cases. ".repeat(4),
        "\n\n[MARKER-3] For complex problems, break them down into manageable steps and provide clear reasoning for your approach. ".repeat(3),
        "[MARKER-4] This is a very long system message designed to test the scrolling performance of the new efficient chat history implementation. ",
        "\n\n[MARKER-5] Additional context about your capabilities and how you should respond to queries goes here. ".repeat(4),
        "[MARKER-6] Remember to always be helpful, harmless, and honest in your responses. [END-MARKER]".repeat(6)
    );
    let user_content = format!(
        "{}{}{}{}{}{}",
        "I'm working on a complex software architecture project that involves microservices, event-driven design, and distributed systems. ".repeat(4),
        "\n\nCould you help me understand the best practices for implementing resilient communication patterns between services? ".repeat(5),
        "I'm particularly interested in how to handle failures, implement circuit breakers, and ensure data consistency across service boundaries. ".repeat(8),
        "\n\nThis message is intentionally long to test scrolling behavior with realistic user input. ".repeat(5),
        "I also need help with understanding how to implement proper logging, monitoring, and observability in a distributed system. ".repeat(7),
        "\n\nCan you provide detailed examples with code snippets in multiple languages? ".repeat(9)
    );
    let assistant_content = format!(
        "{}{}{}{}{}{}{}{}{}",
        "I'd be happy to help you with implementing resilient communication patterns in your microservices architecture! Let me break this down into several key areas:\n\n".repeat(2),
        "## 1. Circuit Breaker Pattern\n\nThe circuit breaker pattern helps prevent cascading failures by monitoring service calls and temporarily stopping requests to failing services. ".repeat(8),
        "\n\n## 2. Retry Strategies\n\nImplement exponential backoff with jitter to avoid thundering herd problems. Consider different retry policies for different types of failures. ".repeat(6),
        "\n\n## 3. Data Consistency\n\nFor distributed systems, consider using the Saga pattern for managing transactions across multiple services. Event sourcing can also help maintain consistency. ".repeat(7),
        "\n\n## 4. Example Implementation\n\n```rust\npub struct CircuitBreaker {\n    state: Arc<Mutex<State>>,\n    failure_threshold: u32,\n    success_threshold: u32,\n    timeout: Duration,\n}\n\nimpl CircuitBreaker {\n    // Implementation details here...\n}\n```\n\n".repeat(5),
        "\n\n## 5. Monitoring and Observability\n\nUse distributed tracing with tools like Jaeger or Zipkin. Implement structured logging with correlation IDs. ".repeat(8),
        "\n\nThis is a comprehensive response that demonstrates how assistant messages with technical content, code examples, and detailed explanations would appear in the chat interface during scrolling. ".repeat(2),
        "\n\n## 6. Best Practices Summary\n\n* Always implement timeouts\n* Use circuit breakers for external calls\n* Implement proper retry logic with backoff\n* Use distributed tracing\n* Log all important events\n".repeat(7),
        "\n\nI hope this helps with your distributed systems architecture! Let me know if you need more specific examples or have questions about any of these patterns. "
    );
    // Create sample tool calls for the second assistant message
    let tool_calls = create_sample_tool_calls();

    let chat_state = ChatState {
        system: SystemChatMessage {
            content: large_system_content,
        },
        tools: vec![],
        messages: vec![
            wasmind_actor_utils::llm_client_types::ChatMessageWithRequestId::User(
                wasmind_actor_utils::llm_client_types::UserChatMessage {
                    content: user_content,
                }
            ),
            wasmind_actor_utils::llm_client_types::ChatMessageWithRequestId::assistant_with_request_id(
                assistant_content,
                "example-request-id",
            ),
            wasmind_actor_utils::llm_client_types::ChatMessageWithRequestId::assistant_with_request_id_with_tools(
                tool_calls,
                "example-request-id",
            ),
        ],
    };

    let chat_update = ChatStateUpdated { chat_state };
    coordinator.broadcast_common_message(chat_update, false)?;

    // Broadcast tool call status updates to show different states
    let tool_status_updates = create_tool_status_updates();
    for status_update in tool_status_updates {
        coordinator.broadcast_common_message(status_update, false)?;
    }

    // Wait a moment before starting toast notifications
    tokio::time::sleep(Duration::from_secs(2)).await;

    // Broadcast 6 test toast notifications to demonstrate the toast system

    // 1. Info notification - Preview started
    coordinator.broadcast_common_message(
        UserNotification {
            level: NotificationLevel::Info,
            title: "Preview Started".to_string(),
            message: "TUI preview is now running with sample data".to_string(),
            source: Some("System".to_string()),
        },
        false,
    )?;

    tokio::time::sleep(Duration::from_millis(800)).await;

    // 2. Warning notification - High memory usage
    coordinator.broadcast_common_message(
        UserNotification {
            level: NotificationLevel::Warning,
            title: "High Memory Usage".to_string(),
            message: "Memory usage is approaching 85% capacity".to_string(),
            source: Some("Monitor".to_string()),
        },
        false,
    )?;

    tokio::time::sleep(Duration::from_millis(800)).await;

    // 3. Error notification - Connection failed
    coordinator.broadcast_common_message(
        UserNotification {
            level: NotificationLevel::Error,
            title: "Connection Failed".to_string(),
            message: "Unable to connect to external service after 3 retry attempts".to_string(),
            source: Some("Network".to_string()),
        },
        false,
    )?;

    tokio::time::sleep(Duration::from_millis(800)).await;

    // 4. Info notification - Data loaded
    coordinator.broadcast_common_message(
        UserNotification {
            level: NotificationLevel::Info,
            title: "Data Loaded".to_string(),
            message: "Successfully loaded 1,247 records from database".to_string(),
            source: Some("Database".to_string()),
        },
        false,
    )?;

    tokio::time::sleep(Duration::from_millis(800)).await;

    // 5. Warning notification - API deprecation
    coordinator.broadcast_common_message(
        UserNotification {
            level: NotificationLevel::Warning,
            title: "Deprecated API".to_string(),
            message: "Using deprecated v1 API, please upgrade to v2 by end of month".to_string(),
            source: Some("API".to_string()),
        },
        false,
    )?;

    tokio::time::sleep(Duration::from_millis(800)).await;

    // 6. Error notification - Timeout occurred
    coordinator.broadcast_common_message(
        UserNotification {
            level: NotificationLevel::Error,
            title: "Timeout Occurred".to_string(),
            message: "Request timed out after 30 seconds, operation cancelled".to_string(),
            source: Some("Agent".to_string()),
        },
        false,
    )?;

    tokio::time::sleep(Duration::from_secs(140)).await;

    Ok(())
}