opencrabs 0.3.75

The autonomous, self-improving AI agent. Single Rust binary. Every channel. Install with: cargo install opencrabs
//! browser_close — Close the current session's browser page.
//!
//! Without this, pages persisted in the manager's HashMap forever. The
//! CDP session stayed open, the Chrome tab stayed visible on the user's
//! desktop, and a fresh agent invocation reused the stale page from a
//! prior task. The tool exposes `BrowserManager::close_page_for_session`
//! to the agent so it can clean up explicitly when a browser task is
//! complete.

use super::manager::BrowserManager;
use crate::brain::tools::error::Result;
use crate::brain::tools::r#trait::{Tool, ToolCapability, ToolExecutionContext, ToolResult};
use async_trait::async_trait;
use serde_json::Value;
use std::sync::Arc;

pub struct BrowserCloseTool {
    manager: Arc<BrowserManager>,
}

impl BrowserCloseTool {
    pub fn new(manager: Arc<BrowserManager>) -> Self {
        Self { manager }
    }
}

#[async_trait]
impl Tool for BrowserCloseTool {
    fn name(&self) -> &str {
        "browser_close"
    }

    fn description(&self) -> &str {
        "Close the current session's browser tab and free its CDP session. \
         Call this when you're done with a browser task — otherwise the tab \
         stays open and a future browser action in the same session will \
         reuse the stale page. Idempotent: safe to call when no tab is open."
    }

    fn input_schema(&self) -> Value {
        serde_json::json!({
            "type": "object",
            "properties": {}
        })
    }

    fn capabilities(&self) -> Vec<ToolCapability> {
        vec![]
    }

    fn requires_approval(&self) -> bool {
        false
    }

    async fn execute(&self, _input: Value, context: &ToolExecutionContext) -> Result<ToolResult> {
        use super::manager::CloseOutcome;
        match self
            .manager
            .close_page_for_session(context.session_id)
            .await
        {
            CloseOutcome::Closed => Ok(ToolResult::success(
                "Browser tab closed for this session.".to_string(),
            )),
            // Idempotent: nothing to close is a clean success, not an error, so
            // the agent doesn't retry.
            CloseOutcome::NothingOpen => Ok(ToolResult::success(
                "No browser tab was open for this session — nothing to close.".to_string(),
            )),
            // The handle was dropped so the next automation gets a fresh tab,
            // but the CDP close was not confirmed — report it truthfully (#739).
            CloseOutcome::Failed => Ok(ToolResult::success(
                "Browser tab detached for this session; the OS tab may linger (close not \
                 confirmed). The next browser action will open a fresh tab."
                    .to_string(),
            )),
        }
    }
}