telos_agent/tools/builtin/browser/tool_impls/
input.rs1use 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::manager::BrowserManager;
7use crate::tools::browser::tool_impls::require_session;
8use crate::tools::browser::util::*;
9
10pub struct BrowserClickTool {
11 manager: BrowserManager,
12}
13
14impl BrowserClickTool {
15 pub fn new(manager: BrowserManager) -> Self {
16 Self { manager }
17 }
18}
19
20#[async_trait]
21impl Tool for BrowserClickTool {
22 fn definition(&self) -> ToolDefinition {
23 ToolDefinition {
24 name: "BrowserClick".into(),
25 description:
26 "Click a browser element. Prefer element_id from BrowserState. The selector field is CSS, with text=... and xpath=... accepted as locator shorthands."
27 .into(),
28 input_schema: selector_schema(json!({})),
29 }
30 }
31
32 async fn check_permission(
33 &self,
34 arguments: &Value,
35 _context: &ToolContext,
36 ) -> Result<PermissionDecision, AgentError> {
37 sensitive_action_permission("browser click", arguments)
38 }
39
40 async fn invoke(
41 &self,
42 arguments: Value,
43 context: ToolContext,
44 ) -> Result<ToolOutput, AgentError> {
45 let key = browser_session_key(&arguments, &context);
46 let session = require_session(&self.manager, &key, "BrowserClick").await?;
47 let mut session = session.lock().await;
48 Ok(ToolOutput::json(session.click(&arguments, &context).await?))
49 }
50}
51
52pub struct BrowserTypeTool {
53 manager: BrowserManager,
54}
55
56impl BrowserTypeTool {
57 pub fn new(manager: BrowserManager) -> Self {
58 Self { manager }
59 }
60}
61
62#[async_trait]
63impl Tool for BrowserTypeTool {
64 fn definition(&self) -> ToolDefinition {
65 ToolDefinition {
66 name: "BrowserType".into(),
67 description:
68 "Type text into a browser input, textarea, or contenteditable element. Prefer element_id from BrowserState. The selector field is CSS, with text=... and xpath=... accepted as locator shorthands."
69 .into(),
70 input_schema: selector_schema(json!({
71 "text": { "type": "string" },
72 "clear": { "type": "boolean", "default": true }
73 })),
74 }
75 }
76
77 async fn validate(&self, arguments: &Value, _context: &ToolContext) -> Result<(), AgentError> {
78 if arguments.get("text").and_then(Value::as_str).is_none() {
79 return Err(AgentError::Validation("missing string `text`".into()));
80 }
81 Ok(())
82 }
83
84 async fn check_permission(
85 &self,
86 arguments: &Value,
87 _context: &ToolContext,
88 ) -> Result<PermissionDecision, AgentError> {
89 sensitive_action_permission("browser typing", arguments)
90 }
91
92 async fn invoke(
93 &self,
94 arguments: Value,
95 context: ToolContext,
96 ) -> Result<ToolOutput, AgentError> {
97 let key = browser_session_key(&arguments, &context);
98 let session = require_session(&self.manager, &key, "BrowserType").await?;
99 let mut session = session.lock().await;
100 Ok(ToolOutput::json(session.type_text(&arguments, &context).await?))
101 }
102}
103
104pub struct BrowserSelectTool {
105 manager: BrowserManager,
106}
107
108impl BrowserSelectTool {
109 pub fn new(manager: BrowserManager) -> Self {
110 Self { manager }
111 }
112}
113
114#[async_trait]
115impl Tool for BrowserSelectTool {
116 fn definition(&self) -> ToolDefinition {
117 ToolDefinition {
118 name: "BrowserSelect".into(),
119 description:
120 "Select a value in a browser select element. Prefer element_id from BrowserState. The selector field is CSS, with text=... and xpath=... accepted as locator shorthands."
121 .into(),
122 input_schema: selector_schema(json!({
123 "value": { "type": "string" }
124 })),
125 }
126 }
127
128 async fn validate(&self, arguments: &Value, _context: &ToolContext) -> Result<(), AgentError> {
129 if arguments.get("value").and_then(Value::as_str).is_none() {
130 return Err(AgentError::Validation("missing string `value`".into()));
131 }
132 Ok(())
133 }
134
135 async fn check_permission(
136 &self,
137 arguments: &Value,
138 _context: &ToolContext,
139 ) -> Result<PermissionDecision, AgentError> {
140 sensitive_action_permission("browser select", arguments)
141 }
142
143 async fn invoke(
144 &self,
145 arguments: Value,
146 context: ToolContext,
147 ) -> Result<ToolOutput, AgentError> {
148 let key = browser_session_key(&arguments, &context);
149 let session = require_session(&self.manager, &key, "BrowserSelect").await?;
150 let mut session = session.lock().await;
151 Ok(ToolOutput::json(session.select(&arguments, &context).await?))
152 }
153}