feat: add websocket browser service runtime

Wire the service/browser runtime onto the websocket-driven execution path and add the new browser/service modules needed for the submit flow and runtime integration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
木炎
2026-04-04 23:42:27 +08:00
parent 2ae71fb1c9
commit 3e18350320
33 changed files with 4993 additions and 327 deletions

View File

@@ -1,5 +1,6 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use async_trait::async_trait;
use futures_util::{stream, StreamExt};
@@ -8,6 +9,7 @@ use zeroclaw::config::Config as ZeroClawConfig;
use zeroclaw::providers::traits::{ProviderCapabilities, StreamEvent, StreamOptions, StreamResult};
use zeroclaw::providers::{self, ChatMessage, ChatRequest, ChatResponse, Provider};
use crate::browser::{BrowserBackend, PipeBrowserBackend};
use crate::compat::browser_script_skill_tool::build_browser_script_skill_tools;
use crate::compat::browser_tool_adapter::ZeroClawBrowserTool;
use crate::compat::config_adapter::{
@@ -47,6 +49,32 @@ pub fn execute_task<T: Transport + 'static>(
)
}
pub fn execute_task_with_browser_backend(
transport: &dyn crate::agent::AgentEventSink,
browser_backend: Arc<dyn BrowserBackend>,
instruction: &str,
task_context: &CompatTaskContext,
workspace_root: &Path,
settings: &SgClawSettings,
) -> Result<String, PipeError> {
let config = build_zeroclaw_config_from_sgclaw_settings(workspace_root, settings);
let skills_dir = resolve_skills_dir_from_sgclaw_settings(workspace_root, settings);
let provider = build_provider(&config)?;
let runtime = tokio::runtime::Runtime::new()
.map_err(|err| PipeError::Protocol(format!("failed to create tokio runtime: {err}")))?;
runtime.block_on(execute_task_with_provider(
transport,
browser_backend,
provider,
instruction,
task_context,
config,
skills_dir,
settings.clone(),
))
}
pub fn execute_task_with_sgclaw_settings<T: Transport + 'static>(
transport: &T,
browser_tool: BrowserPipeTool<T>,
@@ -63,7 +91,7 @@ pub fn execute_task_with_sgclaw_settings<T: Transport + 'static>(
runtime.block_on(execute_task_with_provider(
transport,
browser_tool,
Arc::new(PipeBrowserBackend::from_inner(browser_tool)),
provider,
instruction,
task_context,
@@ -73,9 +101,9 @@ pub fn execute_task_with_sgclaw_settings<T: Transport + 'static>(
))
}
pub async fn execute_task_with_provider<T: Transport + 'static>(
transport: &T,
browser_tool: BrowserPipeTool<T>,
pub async fn execute_task_with_provider(
transport: &dyn crate::agent::AgentEventSink,
browser_backend: Arc<dyn BrowserBackend>,
provider: Box<dyn Provider>,
instruction: &str,
task_context: &CompatTaskContext,
@@ -116,11 +144,13 @@ pub async fn execute_task_with_provider<T: Transport + 'static>(
message: format!("loaded skills: {}", loaded_skill_labels.join(", ")),
})?;
}
let browser_tool_for_scripts = browser_tool.clone();
let browser_tool_for_scripts = browser_backend.clone();
let browser_tool_for_superrpa = browser_backend.clone();
let browser_tool_for_browser_action = browser_backend;
let mut tools: Vec<Box<dyn zeroclaw::tools::Tool>> = if browser_surface_present {
vec![
Box::new(ZeroClawBrowserTool::new_superrpa(browser_tool.clone())),
Box::new(ZeroClawBrowserTool::new(browser_tool)),
Box::new(ZeroClawBrowserTool::new_superrpa(browser_tool_for_superrpa)),
Box::new(ZeroClawBrowserTool::new(browser_tool_for_browser_action)),
]
} else {
Vec::new()