Skip to main content

mermaid_cli/providers/tool/computer_use/
list_windows.rs

1//! `list_windows` — enumerate visible window titles. X11 only;
2//! Wayland's wlr-protocols ecosystem has no portable window
3//! enumeration primitive.
4
5use std::sync::Arc;
6use std::time::Instant;
7
8use async_trait::async_trait;
9use serde_json::Value;
10
11use crate::providers::ctx::ExecContext;
12use mermaid_domain::{ToolDefinition, ToolOutcome};
13
14use super::super::ToolExecutor;
15use super::computer_use_success;
16use super::driver::ComputerUseDriver;
17
18pub struct ListWindowsTool {
19    driver: Arc<ComputerUseDriver>,
20}
21
22impl ListWindowsTool {
23    pub fn new(driver: Arc<ComputerUseDriver>) -> Self {
24        Self { driver }
25    }
26}
27
28#[async_trait]
29impl ToolExecutor for ListWindowsTool {
30    fn name(&self) -> &'static str {
31        "list_windows"
32    }
33
34    fn schema(&self) -> ToolDefinition {
35        ToolDefinition {
36            name: "list_windows".to_string(),
37            description: "List visible window titles. X11 only. On Wayland, use screenshot mode \
38                 'fullscreen' or 'monitor' instead — window enumeration is not portable \
39                 across compositors."
40                .to_string(),
41            input_schema: serde_json::json!({ "type": "object", "properties": {} }),
42        }
43    }
44
45    async fn execute(&self, args: Value, ctx: ExecContext) -> ToolOutcome {
46        let started = Instant::now();
47        if let Err(error) = self.driver.ensure_alive_async().await {
48            return ToolOutcome::error(error, started.elapsed().as_secs_f64());
49        }
50        if let Some(blocked) = super::super::policy_gate::gate_external(
51            &ctx,
52            "list_windows",
53            mermaid_runtime::ToolCategory::ComputerUse,
54            "computer-use: list_windows".to_string(),
55            &args,
56        )
57        .await
58        {
59            return blocked;
60        }
61
62        let windows = tokio::select! {
63            biased;
64            _ = ctx.token.cancelled() => return ToolOutcome::cancelled(),
65            r = self.driver.list_windows(&ctx.token) => match r {
66                Ok(w) => w,
67                Err(e) => return ToolOutcome::error(
68                    format!("list_windows failed: {e}"),
69                    started.elapsed().as_secs_f64(),
70                ),
71            },
72        };
73
74        let output = if windows.is_empty() {
75            "No visible windows found.".to_string()
76        } else {
77            let list = windows
78                .iter()
79                .map(|w| format!("  - {w}"))
80                .collect::<Vec<_>>()
81                .join("\n");
82            format!("Visible windows ({}):\n{}", windows.len(), list)
83        };
84
85        computer_use_success(
86            "list_windows",
87            args,
88            output,
89            started.elapsed().as_secs_f64(),
90        )
91    }
92}