mod common; use std::sync::Arc; use std::time::Duration; use common::MockTransport; use sgclaw::agent::handle_browser_message; use sgclaw::pipe::{Action, AgentMessage, BrowserMessage, BrowserPipeTool, Timing}; use sgclaw::security::MacPolicy; fn test_policy() -> MacPolicy { MacPolicy::from_json_str( r#"{ "version": "1.0", "domains": { "allowed": ["oa.example.com", "www.baidu.com", "www.zhihu.com", "zhuanlan.zhihu.com"] }, "pipe_actions": { "allowed": ["click", "type", "navigate", "getText", "getHtml", "waitForSelector", "scrollTo"], "blocked": ["eval", "executeJsInPage"] } }"#, ) .unwrap() } #[test] fn submit_task_sends_three_commands_and_finishes_with_task_complete() { let transport = Arc::new(MockTransport::new(vec![ BrowserMessage::Response { seq: 1, success: true, data: serde_json::json!({ "navigated": true }), aom_snapshot: vec![], timing: Timing { queue_ms: 1, exec_ms: 20, }, }, BrowserMessage::Response { seq: 2, success: true, data: serde_json::json!({ "typed": true }), aom_snapshot: vec![], timing: Timing { queue_ms: 1, exec_ms: 20, }, }, BrowserMessage::Response { seq: 3, success: true, data: serde_json::json!({ "clicked": true }), aom_snapshot: vec![], timing: Timing { queue_ms: 1, exec_ms: 20, }, }, ])); let tool = BrowserPipeTool::new( transport.clone(), test_policy(), vec![1, 2, 3, 4, 5, 6, 7, 8], ) .with_response_timeout(Duration::from_secs(1)); handle_browser_message( transport.as_ref(), &tool, BrowserMessage::SubmitTask { instruction: "打开百度搜索天气".to_string(), conversation_id: String::new(), messages: vec![], page_url: String::new(), page_title: String::new(), }, ) .unwrap(); let sent = transport.sent_messages(); assert_eq!(sent.len(), 8); assert!(matches!( &sent[0], AgentMessage::LogEntry { level, message } if level == "mode" && message == "deterministic_planner" )); assert!(matches!( &sent[1], AgentMessage::LogEntry { level, message } if level == "info" && message == "navigate https://www.baidu.com" )); assert!(matches!( &sent[2], AgentMessage::Command { seq, action, .. } if *seq == 1 && action == &Action::Navigate )); assert!(matches!( &sent[3], AgentMessage::LogEntry { level, message } if level == "info" && message == "type 天气 into #kw" )); assert!(matches!( &sent[4], AgentMessage::Command { seq, action, .. } if *seq == 2 && action == &Action::Type )); assert!(matches!( &sent[5], AgentMessage::LogEntry { level, message } if level == "info" && message == "click #su" )); assert!(matches!( &sent[6], AgentMessage::Command { seq, action, .. } if *seq == 3 && action == &Action::Click )); assert!(matches!( &sent[7], AgentMessage::TaskComplete { success, summary } if *success && summary == "已在百度搜索天气" )); } #[test] fn explicit_zhihu_skill_short_circuits_before_planner_fallback() { let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response { seq: 1, success: true, data: serde_json::json!({ "url": "https://www.zhihu.com/creator/analytics/work/all" }), aom_snapshot: vec![], timing: Timing { queue_ms: 1, exec_ms: 20, }, }])); let tool = BrowserPipeTool::new( transport.clone(), test_policy(), vec![1, 2, 3, 4, 5, 6, 7, 8], ) .with_response_timeout(Duration::from_secs(1)); handle_browser_message( transport.as_ref(), &tool, BrowserMessage::SubmitTask { instruction: r#"skill:zhihu_navigate {"page":"content_analysis","ensure_loaded":false}"# .to_string(), conversation_id: String::new(), messages: vec![], page_url: String::new(), page_title: String::new(), }, ) .unwrap(); let sent = transport.sent_messages(); assert_eq!(sent.len(), 3); assert!(matches!( &sent[0], AgentMessage::LogEntry { level, message } if level == "info" && message == "navigate https://www.zhihu.com/creator/analytics/work/all" )); assert!(matches!( &sent[1], AgentMessage::Command { seq, action, security, .. } if *seq == 1 && action == &Action::Navigate && security.expected_domain == "www.zhihu.com" )); assert!(matches!( &sent[2], AgentMessage::TaskComplete { success, summary } if *success && summary == "知乎页面已打开:内容分析 (https://www.zhihu.com/creator/analytics/work/all)" )); } #[test] fn natural_language_zhihu_navigation_short_circuits_before_planner_fallback() { let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response { seq: 1, success: true, data: serde_json::json!({ "url": "https://www.zhihu.com/" }), aom_snapshot: vec![], timing: Timing { queue_ms: 1, exec_ms: 20, }, }])); let tool = BrowserPipeTool::new( transport.clone(), test_policy(), vec![1, 2, 3, 4, 5, 6, 7, 8], ) .with_response_timeout(Duration::from_secs(1)); handle_browser_message( transport.as_ref(), &tool, BrowserMessage::SubmitTask { instruction: "打开知乎首页".to_string(), conversation_id: String::new(), messages: vec![], page_url: String::new(), page_title: String::new(), }, ) .unwrap(); let sent = transport.sent_messages(); assert_eq!(sent.len(), 3); assert!(matches!( &sent[0], AgentMessage::LogEntry { level, message } if level == "info" && message == "navigate https://www.zhihu.com/" )); assert!(matches!( &sent[1], AgentMessage::Command { seq, action, security, .. } if *seq == 1 && action == &Action::Navigate && security.expected_domain == "www.zhihu.com" )); assert!(matches!( &sent[2], AgentMessage::TaskComplete { success, summary } if *success && summary == "知乎页面已打开:首页 (https://www.zhihu.com/)" )); }