use std::sync::Arc;
use std::time::Instant;
use async_trait::async_trait;
use serde_json::Value;
use crate::domain::{ToolDefinition, ToolOutcome};
use crate::providers::ctx::ExecContext;
use super::super::ToolExecutor;
use super::computer_use_success;
use super::driver::ComputerUseDriver;
pub struct ListWindowsTool {
driver: Arc<ComputerUseDriver>,
}
impl ListWindowsTool {
pub fn new(driver: Arc<ComputerUseDriver>) -> Self {
Self { driver }
}
}
#[async_trait]
impl ToolExecutor for ListWindowsTool {
fn name(&self) -> &'static str {
"list_windows"
}
fn schema(&self) -> ToolDefinition {
ToolDefinition {
name: "list_windows".to_string(),
description: "List visible window titles. X11 only. On Wayland, use screenshot mode \
'fullscreen' or 'monitor' instead — window enumeration is not portable \
across compositors."
.to_string(),
input_schema: serde_json::json!({ "type": "object", "properties": {} }),
}
}
async fn execute(&self, args: Value, ctx: ExecContext) -> ToolOutcome {
let started = Instant::now();
if let Err(error) = self.driver.ensure_alive() {
return ToolOutcome::error(error, started.elapsed().as_secs_f64());
}
let windows = tokio::select! {
biased;
_ = ctx.token.cancelled() => return ToolOutcome::cancelled(),
r = self.driver.list_windows(&ctx.token) => match r {
Ok(w) => w,
Err(e) => return ToolOutcome::error(
format!("list_windows failed: {}", e),
started.elapsed().as_secs_f64(),
),
},
};
let output = if windows.is_empty() {
"No visible windows found.".to_string()
} else {
let list = windows
.iter()
.map(|w| format!(" - {}", w))
.collect::<Vec<_>>()
.join("\n");
format!("Visible windows ({}):\n{}", windows.len(), list)
};
computer_use_success(
"list_windows",
args,
output,
started.elapsed().as_secs_f64(),
)
}
}