oxi-cli 0.31.0

Terminal-based AI coding assistant — multi-provider, streaming-first, extensible
Documentation
//! AgentTool wrapper for WASM extension tools.
//!
//! Each tool exported by a WASM extension is wrapped in a `WasmTool` that
//! implements the `AgentTool` trait. Tool execution is delegated to the
//! WASM extension via `WasmExtensionManager::execute_tool`.

use oxi_agent::{AgentTool, AgentToolResult, ToolError};
use serde_json::Value;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use tokio::sync::oneshot;

use super::wasm::WasmExtensionManager;

/// An `AgentTool` backed by a WASM extension.
pub struct WasmTool {
    manager: Arc<WasmExtensionManager>,
    tool_name: String,
    description: String,
    schema: Value,
}

impl WasmTool {
    /// Create a new WasmTool.
    pub fn new(
        manager: Arc<WasmExtensionManager>,
        tool_name: String,
        description: String,
        schema: Value,
    ) -> Self {
        Self {
            manager,
            tool_name,
            description,
            schema,
        }
    }
}

impl AgentTool for WasmTool {
    fn name(&self) -> &str {
        &self.tool_name
    }
    fn label(&self) -> &str {
        &self.description
    }
    fn description(&self) -> &str {
        &self.description
    }
    fn parameters_schema(&self) -> Value {
        self.schema.clone()
    }

    fn execute<'a>(
        &'a self,
        _tool_call_id: &'a str,
        params: Value,
        _signal: Option<oneshot::Receiver<()>>,
        _ctx: &'a oxi_agent::ToolContext,
    ) -> Pin<Box<dyn Future<Output = Result<AgentToolResult, ToolError>> + Send + 'a>> {
        Box::pin(async move {
            let manager = self.manager.clone();
            let tool_name = self.tool_name.clone();

            let result =
                tokio::task::spawn_blocking(move || manager.execute_tool(&tool_name, params))
                    .await
                    .map_err(|e| format!("WASM execution panicked: {}", e))?;

            match result {
                Ok(value) => {
                    let success = value
                        .get("success")
                        .and_then(|v| v.as_bool())
                        .unwrap_or(true);
                    let output = value
                        .get("output")
                        .and_then(|v| v.as_str())
                        .unwrap_or("(no output)")
                        .to_string();

                    if success {
                        let mut tool_result = AgentToolResult::success(output);
                        if let Some(meta) = value.get("metadata").cloned() {
                            tool_result = tool_result.with_metadata(meta);
                        }
                        Ok(tool_result)
                    } else {
                        Ok(AgentToolResult::error(output))
                    }
                }
                Err(e) => Ok(AgentToolResult::error(format!(
                    "WASM tool '{}' error: {}",
                    self.tool_name, e
                ))),
            }
        })
    }
}