Skip to main content

telos_agent/tools/builtin/browser/tool_impls/
find_url.rs

1use async_trait::async_trait;
2use serde_json::{Value, json};
3
4use crate::error::AgentError;
5use crate::tools::api::{PermissionDecision, Tool, ToolContext, ToolDefinition, ToolOutput};
6use crate::tools::browser::util::*;
7
8pub struct BrowserFindUrlTool;
9
10#[async_trait]
11impl Tool for BrowserFindUrlTool {
12    fn definition(&self) -> ToolDefinition {
13        ToolDefinition {
14            name: "BrowserFindUrl".into(),
15            description: "Search local browser bookmarks/history metadata for likely URLs. Requires explicit approval.".into(),
16            input_schema: json!({
17                "type": "object",
18                "properties": {
19                    "query": { "type": "string" },
20                    "limit": { "type": "integer", "default": 10 }
21                },
22                "required": ["query"]
23            }),
24        }
25    }
26
27    async fn check_permission(
28        &self,
29        _arguments: &Value,
30        _context: &ToolContext,
31    ) -> Result<PermissionDecision, AgentError> {
32        Ok(PermissionDecision::Ask {
33            reason: "reading local browser bookmarks/history metadata requires approval".into(),
34        })
35    }
36
37    async fn invoke(
38        &self,
39        arguments: Value,
40        _context: ToolContext,
41    ) -> Result<ToolOutput, AgentError> {
42        let query = arguments
43            .get("query")
44            .and_then(Value::as_str)
45            .ok_or_else(|| AgentError::Validation("missing string `query`".into()))?;
46        let limit = optional_u32(&arguments, "limit").unwrap_or(10).min(50) as usize;
47        let mut results = Vec::new();
48        for path in candidate_bookmark_paths() {
49            if results.len() >= limit {
50                break;
51            }
52            let Ok(content) = tokio::fs::read_to_string(&path).await else {
53                continue;
54            };
55            collect_bookmark_matches(&content, query, limit, &mut results);
56        }
57        Ok(ToolOutput::json(json!({
58            "query": query,
59            "count": results.len(),
60            "results": results,
61            "note": "Only bookmark metadata is read in v1; browser history databases are intentionally not opened yet."
62        })))
63    }
64}