From 61c38dca66985ee934fc0f7a54d9fa7b604d9c05 Mon Sep 17 00:00:00 2001 From: AnyBot Dev Date: Mon, 9 Nov 2026 21:11:11 +0800 Subject: [PATCH] Live e2e wiring: real-brain shell + browser turns (env-gated test, ANYBOT_BROWSER_FLAGS dev flags) - agent live test drives real OpenAI-compatible endpoints (ANYBOT_LIVE_BASE/KEY/MODEL/PROMPT) - specs assembled like the app (web::specs_for) so the model truly sees the browser tool - botbrowser honors ANYBOT_BROWSER_FLAGS (containers: --no-sandbox --headless=new) --- src/agent.rs | 17 +++++++++++++++-- src/botbrowser.rs | 4 ++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index e77e43c..5fabc78 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -2432,6 +2432,10 @@ mod tests { let model = std::env::var("ANYBOT_LIVE_MODEL").unwrap_or_else(|_| "husky".into()); let own = crate::model::BOT_COMPUTER_NAME.to_string(); let _ = std::fs::remove_dir_all("/tmp/anybot-live"); + let mut specs = tools::specs_for(&[own.clone()], &own); + if crate::botbrowser::available() { + specs.extend(crate::web::specs_for(&[own.clone()], &own)); + } let job = Job { req: ChatRequest { kind: ProviderKind::Custom, @@ -2439,8 +2443,10 @@ mod tests { api_key: key, model, system: "你是 AnyBot。用工具完成使用者的要求,最後用一句中文回答。".into(), - history: vec![Message::user("在 /tmp/anybot-live 建立 hello.txt,內容寫 anybot-was-here,然後用 shell 讀回來,告訴我市面檔裡寫了什麼。")], - tools: tools::specs(), + history: vec![Message::user( + &std::env::var("ANYBOT_LIVE_PROMPT").unwrap_or_else(|_| "在 /tmp/anybot-live 建立 hello.txt,內容寫 anybot-was-here,然後用 shell 讀回來,告訴我市面檔裡寫了什麼。".into()) + )], + tools: specs, session: String::new(), }, computers: vec![tools::Target { name: own.clone(), place: tools::Place::Local { workdir: "/tmp".into() } }], @@ -2462,12 +2468,19 @@ mod tests { tools_seen.push(format!("done:{}", m.tool_name)); } (_, StreamEvent::Text(t)) => answer.push_str(&t), + (_, StreamEvent::Thinking(t)) => answer.push_str(&t), (_, StreamEvent::Done) => break, (_, StreamEvent::Error(e)) => panic!("model error: {e}"), _ => {} } } println!("tools: {tools_seen:?}\nanswer: {answer}"); + println!("tools: {tools_seen:?}\nanswer: {answer}"); + if std::env::var("ANYBOT_LIVE_PROMPT").is_ok() { + // free-form scenario: the harness ran the model's tools; judge by hand. + assert!(!tools_seen.is_empty(), "the model used no tools at all"); + return; + } assert!(tools_seen.iter().any(|t| t.contains("shell")), "the model never ran a shell tool: {tools_seen:?}"); assert!(answer.contains("anybot-was-here") || std::fs::read_to_string("/tmp/anybot-live/hello.txt").unwrap_or_default().contains("anybot-was-here"), "the file never appeared; answer was: {answer}"); } diff --git a/src/botbrowser.rs b/src/botbrowser.rs index 02edf75..d74b9e7 100644 --- a/src/botbrowser.rs +++ b/src/botbrowser.rs @@ -648,6 +648,10 @@ pub fn ensure(profile: &str, first_url: Option<&str>) -> Result<(&'static str, u None => "about:blank", }; let mut c = Command::new(bin); + // Developer escape hatch (containers where the Chrome sandbox cannot start): extra flags. + for flag in std::env::var("ANYBOT_BROWSER_FLAGS").unwrap_or_default().split_whitespace() { + c.arg(flag); + } c.arg(format!("--user-data-dir={}", dir.display())) .arg(format!("--remote-debugging-port={launch_port}")) .arg("--remote-allow-origins=http://127.0.0.1")