telos_agent/tools/builtin/browser/tool_impls/
lifecycle.rs1use async_trait::async_trait;
2use serde_json::{Value, json};
3
4use crate::error::AgentError;
5use crate::tools::api::{Tool, ToolContext, ToolDefinition, ToolOutput};
6use crate::tools::browser::manager::BrowserManager;
7use crate::tools::browser::util::*;
8use crate::tools::display_relative;
9
10pub struct BrowserStartTool {
11 manager: BrowserManager,
12}
13
14impl BrowserStartTool {
15 pub fn new(manager: BrowserManager) -> Self {
16 Self { manager }
17 }
18}
19
20#[async_trait]
21impl Tool for BrowserStartTool {
22 fn definition(&self) -> ToolDefinition {
23 ToolDefinition {
24 name: "BrowserStart".into(),
25 description: "Start or reuse an isolated managed Chromium browser session.".into(),
26 input_schema: json!({
27 "type": "object",
28 "properties": {
29 "browser_session_id": { "type": "string" },
30 "headless": { "type": "boolean", "default": true },
31 "width": { "type": "integer", "default": 1280 },
32 "height": { "type": "integer", "default": 900 },
33 "allowed_domains": { "type": "array", "items": { "type": "string" } },
34 "prohibited_domains": { "type": "array", "items": { "type": "string" } },
35 "no_sandbox": { "type": "boolean", "description": "Only set when Chromium cannot run in the current sandbox." }
36 }
37 }),
38 }
39 }
40
41 fn prompt_text(&self) -> Option<&'static str> {
42 Some(
43 "Use BrowserStart for dynamic page or full browser automation. Uses isolated profile. \
44Don't bypass CAPTCHA, bot checks, paywalls, or access controls.",
45 )
46 }
47
48 async fn invoke(
49 &self,
50 arguments: Value,
51 context: ToolContext,
52 ) -> Result<ToolOutput, AgentError> {
53 let (key, session, started) = self.manager.start_or_update(&arguments, &context).await?;
54 let session = session.lock().await;
55 Ok(ToolOutput::json(json!({
56 "browser_session_id": key,
57 "started": started,
58 "port": session.port,
59 "artifact_dir": display_relative(&context.cwd, &session.artifact_dir),
60 "headless": optional_bool(&arguments, "headless").unwrap_or(true),
61 "viewport": { "width": session.viewport.width, "height": session.viewport.height },
62 "allowed_domains": session.allowed_domains,
63 "prohibited_domains": session.prohibited_domains
64 })))
65 }
66}
67pub struct BrowserCloseTool {
68 manager: BrowserManager,
69}
70
71impl BrowserCloseTool {
72 pub fn new(manager: BrowserManager) -> Self {
73 Self { manager }
74 }
75}
76
77#[async_trait]
78impl Tool for BrowserCloseTool {
79 fn definition(&self) -> ToolDefinition {
80 ToolDefinition {
81 name: "BrowserClose".into(),
82 description: "Close a managed browser session.".into(),
83 input_schema: json!({
84 "type": "object",
85 "properties": { "browser_session_id": { "type": "string" } }
86 }),
87 }
88 }
89
90 async fn invoke(
91 &self,
92 arguments: Value,
93 context: ToolContext,
94 ) -> Result<ToolOutput, AgentError> {
95 let key = browser_session_key(&arguments, &context);
96 let Some(session) = self.manager.remove(&key).await else {
97 return Ok(ToolOutput::json(json!({
98 "browser_session_id": key,
99 "closed": false,
100 "reason": "session not found"
101 })));
102 };
103 let mut session = session.lock().await;
104 Ok(ToolOutput::json(session.close().await?))
105 }
106}