Apply rustfmt
This commit is contained in:
+5
-6
@@ -36,6 +36,7 @@ use crate::models::{
|
||||
use crate::prompts;
|
||||
use crate::rlm::{RlmSession, SharedRlmSession, session_summary as rlm_session_summary};
|
||||
use crate::tools::plan::{SharedPlanState, new_shared_plan_state};
|
||||
use crate::tools::shell::{SharedShellManager, new_shared_shell_manager};
|
||||
use crate::tools::spec::{ApprovalRequirement, ToolError, ToolResult};
|
||||
use crate::tools::subagent::{
|
||||
SharedSubAgentManager, SubAgentRuntime, SubAgentType, new_shared_subagent_manager,
|
||||
@@ -43,7 +44,6 @@ use crate::tools::subagent::{
|
||||
use crate::tools::todo::{SharedTodoList, new_shared_todo_list};
|
||||
use crate::tools::user_input::{UserInputRequest, UserInputResponse};
|
||||
use crate::tools::{ToolContext, ToolRegistryBuilder};
|
||||
use crate::tools::shell::{new_shared_shell_manager, SharedShellManager};
|
||||
use crate::tui::app::AppMode;
|
||||
|
||||
use super::events::Event;
|
||||
@@ -1858,13 +1858,12 @@ impl Engine {
|
||||
if tool_name == REQUEST_USER_INPUT_NAME {
|
||||
let started_at = Instant::now();
|
||||
let result = match UserInputRequest::from_value(&tool_input) {
|
||||
Ok(request) => self
|
||||
.await_user_input(&tool_id, request)
|
||||
.await
|
||||
.and_then(|response| {
|
||||
Ok(request) => self.await_user_input(&tool_id, request).await.and_then(
|
||||
|response| {
|
||||
ToolResult::json(&response)
|
||||
.map_err(|e| ToolError::execution_failed(e.to_string()))
|
||||
}),
|
||||
},
|
||||
),
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -7,8 +7,8 @@ use serde_json::Value;
|
||||
|
||||
use crate::models::Usage;
|
||||
use crate::tools::spec::{ToolError, ToolResult};
|
||||
use crate::tools::user_input::UserInputRequest;
|
||||
use crate::tools::subagent::SubAgentResult;
|
||||
use crate::tools::user_input::UserInputRequest;
|
||||
|
||||
/// Events emitted by the engine to update the UI.
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
+1
-3
@@ -1010,9 +1010,7 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
selected_skills_dir.display()
|
||||
);
|
||||
if !agents_skills_dir.exists() && !local_skills_dir.exists() && !global_skills_dir.exists() {
|
||||
println!(
|
||||
" Run `deepseek setup --skills` (or add --local for ./skills)."
|
||||
);
|
||||
println!(" Run `deepseek setup --skills` (or add --local for ./skills).");
|
||||
}
|
||||
|
||||
// Platform and sandbox checks
|
||||
|
||||
+1
-3
@@ -1177,9 +1177,7 @@ impl McpPool {
|
||||
name.starts_with("mcp_")
|
||||
|| matches!(
|
||||
name,
|
||||
"list_mcp_resources"
|
||||
| "list_mcp_resource_templates"
|
||||
| "read_mcp_resource"
|
||||
"list_mcp_resources" | "list_mcp_resource_templates" | "read_mcp_resource"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Calculator tool for evaluating arithmetic expressions.
|
||||
|
||||
use super::spec::{
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec, optional_str,
|
||||
required_str,
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec,
|
||||
optional_str, required_str,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
|
||||
+7
-11
@@ -88,7 +88,9 @@ impl ToolSpec for FinanceTool {
|
||||
.timeout(Duration::from_millis(TIMEOUT_MS))
|
||||
.user_agent(USER_AGENT)
|
||||
.build()
|
||||
.map_err(|e| ToolError::execution_failed(format!("Failed to build HTTP client: {e}")))?;
|
||||
.map_err(|e| {
|
||||
ToolError::execution_failed(format!("Failed to build HTTP client: {e}"))
|
||||
})?;
|
||||
|
||||
let mut results = Vec::with_capacity(requests.len());
|
||||
for req in requests {
|
||||
@@ -253,9 +255,7 @@ async fn fetch_stooq_price(
|
||||
req: &FinanceRequest,
|
||||
) -> Result<FinanceResult, ToolError> {
|
||||
let symbol = normalize_stooq_symbol(&req.ticker, &req.market);
|
||||
let url = format!(
|
||||
"https://stooq.com/q/l/?s={symbol}&f=sd2t2ohlcv&h&e=csv"
|
||||
);
|
||||
let url = format!("https://stooq.com/q/l/?s={symbol}&f=sd2t2ohlcv&h&e=csv");
|
||||
let resp = client
|
||||
.get(&url)
|
||||
.send()
|
||||
@@ -339,13 +339,9 @@ fn url_encode(input: &str) -> String {
|
||||
let mut encoded = String::new();
|
||||
for ch in input.bytes() {
|
||||
match ch {
|
||||
b'A'..=b'Z'
|
||||
| b'a'..=b'z'
|
||||
| b'0'..=b'9'
|
||||
| b'-'
|
||||
| b'_'
|
||||
| b'.'
|
||||
| b'~' => encoded.push(ch as char),
|
||||
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
|
||||
encoded.push(ch as char)
|
||||
}
|
||||
b' ' => encoded.push('+'),
|
||||
_ => encoded.push_str(&format!("%{ch:02X}")),
|
||||
}
|
||||
|
||||
+8
-8
@@ -5,17 +5,15 @@
|
||||
// === Modules ===
|
||||
|
||||
pub mod apply_patch;
|
||||
pub mod calculator;
|
||||
pub mod diagnostics;
|
||||
pub mod duo;
|
||||
pub mod calculator;
|
||||
pub mod finance;
|
||||
pub mod file;
|
||||
pub mod file_search;
|
||||
pub mod finance;
|
||||
pub mod git;
|
||||
pub mod sports;
|
||||
pub mod time;
|
||||
pub mod plan;
|
||||
pub mod parallel;
|
||||
pub mod plan;
|
||||
pub mod project;
|
||||
pub mod registry;
|
||||
pub mod review;
|
||||
@@ -23,14 +21,16 @@ pub mod rlm;
|
||||
pub mod search;
|
||||
pub mod shell;
|
||||
pub mod spec;
|
||||
pub mod sports;
|
||||
pub mod subagent;
|
||||
pub mod swarm;
|
||||
pub mod test_runner;
|
||||
pub mod time;
|
||||
pub mod todo;
|
||||
pub mod user_input;
|
||||
pub mod web_search;
|
||||
pub mod web_run;
|
||||
pub mod weather;
|
||||
pub mod web_run;
|
||||
pub mod web_search;
|
||||
|
||||
// === Re-exports ===
|
||||
|
||||
@@ -52,8 +52,8 @@ pub use time::TimeTool;
|
||||
pub use weather::WeatherTool;
|
||||
|
||||
// Re-export web search tools
|
||||
pub use web_search::WebSearchTool;
|
||||
pub use web_run::WebRunTool;
|
||||
pub use web_search::WebSearchTool;
|
||||
|
||||
// Re-export patch tools
|
||||
pub use apply_patch::ApplyPatchTool;
|
||||
|
||||
@@ -46,7 +46,11 @@ impl ToolSpec for MultiToolUseParallelTool {
|
||||
ApprovalRequirement::Auto
|
||||
}
|
||||
|
||||
async fn execute(&self, _input: Value, _context: &ToolContext) -> Result<ToolResult, ToolError> {
|
||||
async fn execute(
|
||||
&self,
|
||||
_input: Value,
|
||||
_context: &ToolContext,
|
||||
) -> Result<ToolResult, ToolError> {
|
||||
Err(ToolError::execution_failed(
|
||||
"multi_tool_use.parallel must be handled by the engine",
|
||||
))
|
||||
|
||||
@@ -498,9 +498,15 @@ impl ToolRegistryBuilder {
|
||||
)))
|
||||
.with_tool(Arc::new(AgentSwarmTool::new(manager.clone(), runtime)))
|
||||
.with_tool(Arc::new(AgentResultTool::new(manager.clone())))
|
||||
.with_tool(Arc::new(AgentSendInputTool::new(manager.clone(), "send_input")))
|
||||
.with_tool(Arc::new(AgentSendInputTool::new(
|
||||
manager.clone(),
|
||||
"send_input",
|
||||
)))
|
||||
.with_tool(Arc::new(AgentWaitTool::new(manager.clone(), "wait")))
|
||||
.with_tool(Arc::new(AgentSendInputTool::new(manager.clone(), "agent_send_input")))
|
||||
.with_tool(Arc::new(AgentSendInputTool::new(
|
||||
manager.clone(),
|
||||
"agent_send_input",
|
||||
)))
|
||||
.with_tool(Arc::new(AgentWaitTool::new(manager.clone(), "agent_wait")))
|
||||
.with_tool(Arc::new(AgentCancelTool::new(manager.clone())))
|
||||
.with_tool(Arc::new(AgentListTool::new(manager)))
|
||||
|
||||
+24
-22
@@ -19,7 +19,7 @@ use std::time::{Duration, Instant};
|
||||
use uuid::Uuid;
|
||||
use wait_timeout::ChildExt;
|
||||
|
||||
use portable_pty::{native_pty_system, CommandBuilder, PtySize};
|
||||
use portable_pty::{CommandBuilder, PtySize, native_pty_system};
|
||||
|
||||
use crate::sandbox::{
|
||||
CommandSpec,
|
||||
@@ -120,8 +120,12 @@ impl ShellExitStatus {
|
||||
impl ShellChild {
|
||||
fn try_wait(&mut self) -> std::io::Result<Option<ShellExitStatus>> {
|
||||
match self {
|
||||
ShellChild::Process(child) => child.try_wait().map(|status| status.map(ShellExitStatus::from_std)),
|
||||
ShellChild::Pty(child) => child.try_wait().map(|status| status.map(ShellExitStatus::from_pty)),
|
||||
ShellChild::Process(child) => child
|
||||
.try_wait()
|
||||
.map(|status| status.map(ShellExitStatus::from_std)),
|
||||
ShellChild::Pty(child) => child
|
||||
.try_wait()
|
||||
.map(|status| status.map(ShellExitStatus::from_pty)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,10 +290,8 @@ impl BackgroundShell {
|
||||
}
|
||||
|
||||
fn take_delta(&mut self) -> (String, String, usize, usize, usize, usize) {
|
||||
let (stdout_delta, stdout_total) = take_delta_from_buffer(
|
||||
&self.stdout_buffer,
|
||||
&mut self.stdout_cursor,
|
||||
);
|
||||
let (stdout_delta, stdout_total) =
|
||||
take_delta_from_buffer(&self.stdout_buffer, &mut self.stdout_cursor);
|
||||
let (stderr_delta, stderr_total) = if let Some(buffer) = self.stderr_buffer.as_ref() {
|
||||
take_delta_from_buffer(buffer, &mut self.stderr_cursor)
|
||||
} else {
|
||||
@@ -810,7 +812,10 @@ impl ShellManager {
|
||||
let stderr_handle = child.stderr.take().context("Failed to capture stderr")?;
|
||||
let stdin_handle = child.stdin.take().map(StdinWriter::Pipe);
|
||||
|
||||
let stdout_thread = Some(spawn_reader_thread(stdout_handle, Arc::clone(&stdout_buffer)));
|
||||
let stdout_thread = Some(spawn_reader_thread(
|
||||
stdout_handle,
|
||||
Arc::clone(&stdout_buffer),
|
||||
));
|
||||
let stderr_thread = stderr_buffer
|
||||
.as_ref()
|
||||
.map(|buffer| spawn_reader_thread(stderr_handle, Arc::clone(buffer)));
|
||||
@@ -940,8 +945,14 @@ impl ShellManager {
|
||||
shell.poll();
|
||||
}
|
||||
|
||||
let (stdout_delta, stderr_delta, stdout_delta_len, stderr_delta_len, stdout_total, stderr_total) =
|
||||
shell.take_delta();
|
||||
let (
|
||||
stdout_delta,
|
||||
stderr_delta,
|
||||
stdout_delta_len,
|
||||
stderr_delta_len,
|
||||
stdout_total,
|
||||
stderr_total,
|
||||
) = shell.take_delta();
|
||||
let (stdout, stdout_meta) = truncate_with_meta(&stdout_delta);
|
||||
let (stderr, stderr_meta) = truncate_with_meta(&stderr_delta);
|
||||
let sandboxed = !matches!(shell.sandbox_type, SandboxType::None);
|
||||
@@ -1065,10 +1076,7 @@ fn char_boundary_at_or_before(text: &str, max_bytes: usize) -> usize {
|
||||
last_end.min(text.len())
|
||||
}
|
||||
|
||||
fn take_delta_from_buffer(
|
||||
buffer: &Arc<Mutex<Vec<u8>>>,
|
||||
cursor: &mut usize,
|
||||
) -> (Vec<u8>, usize) {
|
||||
fn take_delta_from_buffer(buffer: &Arc<Mutex<Vec<u8>>>, cursor: &mut usize) -> (Vec<u8>, usize) {
|
||||
let data = buffer.lock().map(|d| d.clone()).unwrap_or_default();
|
||||
let start = (*cursor).min(data.len());
|
||||
let delta = data[start..].to_vec();
|
||||
@@ -1427,10 +1435,7 @@ fn build_shell_delta_tool_result(delta: ShellDeltaResult) -> ToolResult {
|
||||
match result.status {
|
||||
ShellStatus::Running => "Background task running (no new output).".to_string(),
|
||||
ShellStatus::Completed => "(no new output)".to_string(),
|
||||
ShellStatus::Failed => format!(
|
||||
"Command failed (exit code: {:?})",
|
||||
result.exit_code
|
||||
),
|
||||
ShellStatus::Failed => format!("Command failed (exit code: {:?})", result.exit_code),
|
||||
ShellStatus::TimedOut => "Command timed out (no new output).".to_string(),
|
||||
ShellStatus::Killed => "Command killed (no new output).".to_string(),
|
||||
}
|
||||
@@ -1442,10 +1447,7 @@ fn build_shell_delta_tool_result(delta: ShellDeltaResult) -> ToolResult {
|
||||
|
||||
ToolResult {
|
||||
content: output,
|
||||
success: matches!(
|
||||
result.status,
|
||||
ShellStatus::Completed | ShellStatus::Running
|
||||
),
|
||||
success: matches!(result.status, ShellStatus::Completed | ShellStatus::Running),
|
||||
metadata: Some(json!({
|
||||
"exit_code": result.exit_code,
|
||||
"status": format!("{:?}", result.status),
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::tools::shell::{new_shared_shell_manager, SharedShellManager};
|
||||
use crate::tools::shell::{SharedShellManager, new_shared_shell_manager};
|
||||
|
||||
/// Capabilities that a tool may have or require.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
||||
+17
-18
@@ -1,8 +1,8 @@
|
||||
//! Sports tool for schedules and standings (ESPN public APIs).
|
||||
|
||||
use super::spec::{
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec, optional_u64,
|
||||
optional_str, required_str,
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec,
|
||||
optional_str, optional_u64, required_str,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use chrono::{NaiveDate, Utc};
|
||||
@@ -103,7 +103,9 @@ impl ToolSpec for SportsTool {
|
||||
.timeout(Duration::from_millis(TIMEOUT_MS))
|
||||
.user_agent(USER_AGENT)
|
||||
.build()
|
||||
.map_err(|e| ToolError::execution_failed(format!("Failed to build HTTP client: {e}")))?;
|
||||
.map_err(|e| {
|
||||
ToolError::execution_failed(format!("Failed to build HTTP client: {e}"))
|
||||
})?;
|
||||
|
||||
match action.as_str() {
|
||||
"schedule" => {
|
||||
@@ -137,8 +139,14 @@ fn map_league(league: &str) -> Result<(String, String), ToolError> {
|
||||
"nhl" => Ok(("hockey".to_string(), "nhl".to_string())),
|
||||
"mlb" => Ok(("baseball".to_string(), "mlb".to_string())),
|
||||
"epl" => Ok(("soccer".to_string(), "eng.1".to_string())),
|
||||
"ncaamb" => Ok(("basketball".to_string(), "mens-college-basketball".to_string())),
|
||||
"ncaawb" => Ok(("basketball".to_string(), "womens-college-basketball".to_string())),
|
||||
"ncaamb" => Ok((
|
||||
"basketball".to_string(),
|
||||
"mens-college-basketball".to_string(),
|
||||
)),
|
||||
"ncaawb" => Ok((
|
||||
"basketball".to_string(),
|
||||
"womens-college-basketball".to_string(),
|
||||
)),
|
||||
"ipl" => Ok(("cricket".to_string(), "ipl".to_string())),
|
||||
_ => Err(ToolError::invalid_input("Unsupported league")),
|
||||
}
|
||||
@@ -160,9 +168,7 @@ async fn fetch_schedule(
|
||||
"https://site.web.api.espn.com/apis/v2/sports/{sport}/{league}/scoreboard?dates={dates}"
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"https://site.web.api.espn.com/apis/v2/sports/{sport}/{league}/scoreboard"
|
||||
)
|
||||
format!("https://site.web.api.espn.com/apis/v2/sports/{sport}/{league}/scoreboard")
|
||||
};
|
||||
|
||||
let resp = client
|
||||
@@ -215,9 +221,7 @@ async fn fetch_schedule(
|
||||
}
|
||||
}
|
||||
if let Some(opponent_filter) = opponent {
|
||||
if !team_matches(&home, opponent_filter)
|
||||
&& !team_matches(&away, opponent_filter)
|
||||
{
|
||||
if !team_matches(&home, opponent_filter) && !team_matches(&away, opponent_filter) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -275,10 +279,7 @@ fn split_competitors(competitors: &[Value]) -> (Option<SportsGameTeam>, Option<S
|
||||
.get("score")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
let home_away = comp
|
||||
.get("homeAway")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("");
|
||||
let home_away = comp.get("homeAway").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let team_info = SportsGameTeam {
|
||||
name,
|
||||
abbreviation,
|
||||
@@ -303,9 +304,7 @@ async fn fetch_standings(
|
||||
sport: &str,
|
||||
league: &str,
|
||||
) -> Result<SportsStandingsResponse, ToolError> {
|
||||
let url = format!(
|
||||
"https://site.web.api.espn.com/apis/v2/sports/{sport}/{league}/standings"
|
||||
);
|
||||
let url = format!("https://site.web.api.espn.com/apis/v2/sports/{sport}/{league}/standings");
|
||||
let resp = client
|
||||
.get(&url)
|
||||
.send()
|
||||
|
||||
@@ -322,8 +322,7 @@ impl SubAgentManager {
|
||||
|
||||
let tools = build_allowed_tools(&agent_type, allowed_tools, runtime.allow_shell)?;
|
||||
let (input_tx, input_rx) = mpsc::unbounded_channel();
|
||||
let mut agent =
|
||||
SubAgent::new(agent_type.clone(), prompt.clone(), tools.clone(), input_tx);
|
||||
let mut agent = SubAgent::new(agent_type.clone(), prompt.clone(), tools.clone(), input_tx);
|
||||
let agent_id = agent.id.clone();
|
||||
let started_at = agent.started_at;
|
||||
let max_steps = self.max_steps;
|
||||
@@ -1257,7 +1256,10 @@ fn build_allowed_tools(
|
||||
tools.retain(|tool| {
|
||||
!matches!(
|
||||
tool.as_str(),
|
||||
"exec_shell" | "exec_shell_wait" | "exec_shell_interact" | "exec_wait"
|
||||
"exec_shell"
|
||||
| "exec_shell_wait"
|
||||
| "exec_shell_interact"
|
||||
| "exec_wait"
|
||||
| "exec_interact"
|
||||
)
|
||||
});
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ fn parse_offset(raw: &str) -> Result<FixedOffset, ToolError> {
|
||||
_ => {
|
||||
return Err(ToolError::invalid_input(
|
||||
"utc_offset must start with + or -",
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
let hours: i32 = raw[1..3]
|
||||
|
||||
@@ -152,7 +152,11 @@ impl ToolSpec for RequestUserInputTool {
|
||||
ApprovalRequirement::Auto
|
||||
}
|
||||
|
||||
async fn execute(&self, _input: Value, _context: &ToolContext) -> Result<ToolResult, ToolError> {
|
||||
async fn execute(
|
||||
&self,
|
||||
_input: Value,
|
||||
_context: &ToolContext,
|
||||
) -> Result<ToolResult, ToolError> {
|
||||
Err(ToolError::execution_failed(
|
||||
"request_user_input must be handled by the engine",
|
||||
))
|
||||
|
||||
+15
-23
@@ -1,8 +1,8 @@
|
||||
//! Weather tool backed by Open-Meteo (no API key required).
|
||||
|
||||
use super::spec::{
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec, optional_u64,
|
||||
optional_str, required_str,
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec,
|
||||
optional_str, optional_u64, required_str,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use chrono::{NaiveDate, Utc};
|
||||
@@ -101,7 +101,9 @@ impl ToolSpec for WeatherTool {
|
||||
.timeout(Duration::from_millis(TIMEOUT_MS))
|
||||
.user_agent(USER_AGENT)
|
||||
.build()
|
||||
.map_err(|e| ToolError::execution_failed(format!("Failed to build HTTP client: {e}")))?;
|
||||
.map_err(|e| {
|
||||
ToolError::execution_failed(format!("Failed to build HTTP client: {e}"))
|
||||
})?;
|
||||
|
||||
let mut results = Vec::with_capacity(requests.len());
|
||||
for req in requests {
|
||||
@@ -168,7 +170,10 @@ struct GeoResult {
|
||||
longitude: f64,
|
||||
}
|
||||
|
||||
async fn geocode_location(client: &reqwest::Client, location: &str) -> Result<GeoResult, ToolError> {
|
||||
async fn geocode_location(
|
||||
client: &reqwest::Client,
|
||||
location: &str,
|
||||
) -> Result<GeoResult, ToolError> {
|
||||
let encoded = url_encode(location);
|
||||
let url = format!(
|
||||
"https://geocoding-api.open-meteo.com/v1/search?name={encoded}&count=1&language=en&format=json"
|
||||
@@ -296,18 +301,9 @@ async fn fetch_forecast(
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
let max_c = maxes
|
||||
.get(idx)
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.0);
|
||||
let min_c = mins
|
||||
.get(idx)
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.0);
|
||||
let precip = precips
|
||||
.get(idx)
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.0);
|
||||
let max_c = maxes.get(idx).and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||
let min_c = mins.get(idx).and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||
let precip = precips.get(idx).and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||
days.push(WeatherDay {
|
||||
date,
|
||||
temp_max_c: max_c,
|
||||
@@ -329,13 +325,9 @@ fn url_encode(input: &str) -> String {
|
||||
let mut encoded = String::new();
|
||||
for ch in input.bytes() {
|
||||
match ch {
|
||||
b'A'..=b'Z'
|
||||
| b'a'..=b'z'
|
||||
| b'0'..=b'9'
|
||||
| b'-'
|
||||
| b'_'
|
||||
| b'.'
|
||||
| b'~' => encoded.push(ch as char),
|
||||
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
|
||||
encoded.push(ch as char)
|
||||
}
|
||||
b' ' => encoded.push('+'),
|
||||
_ => encoded.push_str(&format!("%{ch:02X}")),
|
||||
}
|
||||
|
||||
+32
-27
@@ -4,8 +4,8 @@
|
||||
//! tool call to perform multiple web actions and cite sources with ref_ids.
|
||||
|
||||
use super::spec::{
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec, optional_u64,
|
||||
required_str,
|
||||
ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec,
|
||||
optional_u64, required_str,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use regex::Regex;
|
||||
@@ -297,7 +297,8 @@ impl ToolSpec for WebRunTool {
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let (entries, warning) = run_search(&query, max_results, timeout_ms, &domains).await?;
|
||||
let (entries, warning) =
|
||||
run_search(&query, max_results, timeout_ms, &domains).await?;
|
||||
let mut warnings = Vec::new();
|
||||
if recency > 0 {
|
||||
warnings.push(format!(
|
||||
@@ -361,15 +362,11 @@ impl ToolSpec for WebRunTool {
|
||||
let page = get_page(&ref_id).ok_or_else(|| {
|
||||
ToolError::invalid_input(format!("Unknown ref_id '{ref_id}'"))
|
||||
})?;
|
||||
let link = page
|
||||
.links
|
||||
.iter()
|
||||
.find(|l| l.id == link_id)
|
||||
.ok_or_else(|| {
|
||||
ToolError::invalid_input(format!(
|
||||
"Link id {link_id} not found for ref_id '{ref_id}'"
|
||||
))
|
||||
})?;
|
||||
let link = page.links.iter().find(|l| l.id == link_id).ok_or_else(|| {
|
||||
ToolError::invalid_input(format!(
|
||||
"Link id {link_id} not found for ref_id '{ref_id}'"
|
||||
))
|
||||
})?;
|
||||
let target = link.url.clone();
|
||||
let fetched = resolve_or_fetch_page(&target, DEFAULT_OPEN_TIMEOUT_MS).await?;
|
||||
click_counter += 1;
|
||||
@@ -623,10 +620,7 @@ fn parse_pdf_page(
|
||||
) -> Result<WebPage, ToolError> {
|
||||
let text = pdf_extract_text(bytes)?;
|
||||
let pages = split_pdf_pages(&text);
|
||||
let lines = pages
|
||||
.get(0)
|
||||
.cloned()
|
||||
.unwrap_or_else(Vec::new);
|
||||
let lines = pages.get(0).cloned().unwrap_or_else(Vec::new);
|
||||
|
||||
Ok(WebPage {
|
||||
url: url.to_string(),
|
||||
@@ -657,7 +651,12 @@ fn split_pdf_pages(text: &str) -> Vec<Vec<String>> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn render_view(ref_id: &str, page: &WebPage, lineno: usize, response: ResponseLength) -> PageViewResult {
|
||||
fn render_view(
|
||||
ref_id: &str,
|
||||
page: &WebPage,
|
||||
lineno: usize,
|
||||
response: ResponseLength,
|
||||
) -> PageViewResult {
|
||||
let total = page.lines.len();
|
||||
let view_lines = response.view_lines();
|
||||
let start = if total == 0 {
|
||||
@@ -735,7 +734,11 @@ fn find_in_page(
|
||||
}
|
||||
}
|
||||
|
||||
fn screenshot_page(ref_id: &str, pageno: usize, page: &WebPage) -> Result<ScreenshotResult, ToolError> {
|
||||
fn screenshot_page(
|
||||
ref_id: &str,
|
||||
pageno: usize,
|
||||
page: &WebPage,
|
||||
) -> Result<ScreenshotResult, ToolError> {
|
||||
let pages = page
|
||||
.pdf_pages
|
||||
.as_ref()
|
||||
@@ -860,7 +863,9 @@ fn replace_links(html: &str, base_url: &str) -> (String, Vec<WebLink>) {
|
||||
for cap in re.captures_iter(html) {
|
||||
let Some(full) = cap.get(0) else { continue };
|
||||
let Some(href) = cap.get(1) else { continue };
|
||||
let Some(text_match) = cap.get(2) else { continue };
|
||||
let Some(text_match) = cap.get(2) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
output.push_str(&html[last..full.start()]);
|
||||
let text = normalize_whitespace(&strip_tags(text_match.as_str()));
|
||||
@@ -965,7 +970,11 @@ fn parse_duckduckgo_results(html: &str, max_results: usize) -> Vec<SearchEntry>
|
||||
.map(|s| s.to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
results.push(SearchEntry { title, url, snippet });
|
||||
results.push(SearchEntry {
|
||||
title,
|
||||
url,
|
||||
snippet,
|
||||
});
|
||||
}
|
||||
|
||||
results
|
||||
@@ -1025,13 +1034,9 @@ fn url_encode(input: &str) -> String {
|
||||
let mut encoded = String::new();
|
||||
for ch in input.bytes() {
|
||||
match ch {
|
||||
b'A'..=b'Z'
|
||||
| b'a'..=b'z'
|
||||
| b'0'..=b'9'
|
||||
| b'-'
|
||||
| b'_'
|
||||
| b'.'
|
||||
| b'~' => encoded.push(ch as char),
|
||||
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
|
||||
encoded.push(ch as char)
|
||||
}
|
||||
b' ' => encoded.push('+'),
|
||||
_ => encoded.push_str(&format!("%{ch:02X}")),
|
||||
}
|
||||
|
||||
@@ -176,7 +176,10 @@ impl ModalView for UserInputView {
|
||||
Span::raw(format!("{prefix} ")),
|
||||
Span::styled(option.label.clone(), style),
|
||||
Span::raw(" - "),
|
||||
Span::styled(option.description.clone(), Style::default().fg(palette::TEXT_MUTED)),
|
||||
Span::styled(
|
||||
option.description.clone(),
|
||||
Style::default().fg(palette::TEXT_MUTED),
|
||||
),
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
+5
-13
@@ -3,8 +3,8 @@ use ratatui::{buffer::Buffer, layout::Rect};
|
||||
use std::fmt;
|
||||
|
||||
use crate::palette;
|
||||
use crate::tools::subagent::{SubAgentResult, SubAgentStatus, SubAgentType};
|
||||
use crate::tools::UserInputResponse;
|
||||
use crate::tools::subagent::{SubAgentResult, SubAgentStatus, SubAgentType};
|
||||
use crate::tui::approval::{ElevationOption, ReviewDecision};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -328,18 +328,10 @@ impl ModalView for HelpView {
|
||||
help_lines.push(Line::from(
|
||||
" multi_tool_use.parallel - Execute multiple tools in parallel",
|
||||
));
|
||||
help_lines.push(Line::from(
|
||||
" weather - Daily forecast for a location",
|
||||
));
|
||||
help_lines.push(Line::from(
|
||||
" finance - Stock/crypto price lookup",
|
||||
));
|
||||
help_lines.push(Line::from(
|
||||
" sports - League schedules/standings",
|
||||
));
|
||||
help_lines.push(Line::from(
|
||||
" time - Current time for UTC offsets",
|
||||
));
|
||||
help_lines.push(Line::from(" weather - Daily forecast for a location"));
|
||||
help_lines.push(Line::from(" finance - Stock/crypto price lookup"));
|
||||
help_lines.push(Line::from(" sports - League schedules/standings"));
|
||||
help_lines.push(Line::from(" time - Current time for UTC offsets"));
|
||||
help_lines.push(Line::from(
|
||||
" calculator - Evaluate arithmetic expressions",
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user