feat: restore zhihu browser skills

Reconnect the recovered Zhihu skill flows to the live browser runtime and resolve their resources relative to the executable so they work outside the repo root.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
木炎
2026-03-27 14:29:38 +08:00
parent b87968632a
commit 6aad2ce48e
32 changed files with 7607 additions and 146 deletions

View File

@@ -12,9 +12,9 @@ fn test_policy() -> MacPolicy {
MacPolicy::from_json_str(
r#"{
"version": "1.0",
"domains": { "allowed": ["oa.example.com", "www.baidu.com"] },
"domains": { "allowed": ["oa.example.com", "www.baidu.com", "www.zhihu.com", "zhuanlan.zhihu.com"] },
"pipe_actions": {
"allowed": ["click", "type", "navigate", "getText"],
"allowed": ["click", "type", "navigate", "getText", "getHtml", "waitForSelector", "scrollTo"],
"blocked": ["eval", "executeJsInPage"]
}
}"#,
@@ -120,3 +120,116 @@ fn submit_task_sends_three_commands_and_finishes_with_task_complete() {
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/)"
));
}