fix: reconcile non-cli approval governance with current dev APIs
This commit is contained in:
parent
1fcf2df28b
commit
d8a1d1d14c
@ -982,6 +982,14 @@ pub(crate) async fn run_tool_call_loop(
|
||||
anyhow::bail!("Agent exceeded maximum tool iterations ({max_iterations})")
|
||||
}
|
||||
|
||||
/// Build the tool instruction block for the system prompt from concrete tool
|
||||
/// specs so the LLM knows how to invoke tools.
|
||||
pub(crate) fn build_tool_instructions(tools_registry: &[Box<dyn Tool>]) -> String {
|
||||
let specs: Vec<crate::tools::ToolSpec> =
|
||||
tools_registry.iter().map(|tool| tool.spec()).collect();
|
||||
build_tool_instructions_from_specs(&specs)
|
||||
}
|
||||
|
||||
/// Build the tool instruction block for the system prompt from concrete tool
|
||||
/// specs so the LLM knows how to invoke tools.
|
||||
pub(crate) fn build_tool_instructions_from_specs(tool_specs: &[crate::tools::ToolSpec]) -> String {
|
||||
|
||||
@ -68,10 +68,11 @@ pub use whatsapp::WhatsAppChannel;
|
||||
pub use whatsapp_web::WhatsAppWebChannel;
|
||||
|
||||
use crate::agent::loop_::{
|
||||
build_shell_policy_instructions, build_tool_instructions, run_tool_call_loop, scrub_credentials,
|
||||
build_shell_policy_instructions, build_tool_instructions_from_specs, run_tool_call_loop,
|
||||
scrub_credentials,
|
||||
};
|
||||
use crate::approval::ApprovalManager;
|
||||
use crate::config::Config;
|
||||
use crate::approval::{ApprovalManager, PendingApprovalError};
|
||||
use crate::config::{Config, NonCliNaturalLanguageApprovalMode};
|
||||
use crate::identity;
|
||||
use crate::memory::{self, Memory};
|
||||
use crate::observability::{self, runtime_trace, Observer};
|
||||
@ -81,7 +82,6 @@ use crate::security::SecurityPolicy;
|
||||
use crate::tools::{self, Tool};
|
||||
use crate::util::truncate_with_ellipsis;
|
||||
use anyhow::{Context, Result};
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Write;
|
||||
@ -244,7 +244,7 @@ struct ChannelRuntimeContext {
|
||||
interrupt_on_new_message: bool,
|
||||
multimodal: crate::config::MultimodalConfig,
|
||||
hooks: Option<Arc<crate::hooks::HookRunner>>,
|
||||
non_cli_excluded_tools: Arc<Vec<String>>,
|
||||
non_cli_excluded_tools: Arc<Mutex<Vec<String>>>,
|
||||
query_classification: crate::config::QueryClassificationConfig,
|
||||
model_routes: Vec<crate::config::ModelRouteConfig>,
|
||||
approval_manager: Arc<ApprovalManager>,
|
||||
@ -691,13 +691,21 @@ fn parse_runtime_command(channel_name: &str, content: &str) -> Option<ChannelRun
|
||||
.next()
|
||||
.unwrap_or(command_token)
|
||||
.to_ascii_lowercase();
|
||||
let args: Vec<&str> = parts.collect();
|
||||
let tail = args.join(" ").trim().to_string();
|
||||
|
||||
match base_command.as_str() {
|
||||
// History reset commands are safe for all channels.
|
||||
"/new" | "/clear" => Some(ChannelRuntimeCommand::NewSession),
|
||||
"/approve-request" => Some(ChannelRuntimeCommand::RequestToolApproval(tail)),
|
||||
"/approve-confirm" => Some(ChannelRuntimeCommand::ConfirmToolApproval(tail)),
|
||||
"/approve-pending" => Some(ChannelRuntimeCommand::ListPendingApprovals),
|
||||
"/approve" => Some(ChannelRuntimeCommand::ApproveTool(tail)),
|
||||
"/unapprove" => Some(ChannelRuntimeCommand::UnapproveTool(tail)),
|
||||
"/approvals" => Some(ChannelRuntimeCommand::ListApprovals),
|
||||
// Provider/model switching remains limited to channels with session routing.
|
||||
"/models" if supports_runtime_model_switch(channel_name) => {
|
||||
if let Some(provider) = parts.next() {
|
||||
if let Some(provider) = args.first() {
|
||||
Some(ChannelRuntimeCommand::SetProvider(
|
||||
provider.trim().to_string(),
|
||||
))
|
||||
@ -706,7 +714,7 @@ fn parse_runtime_command(channel_name: &str, content: &str) -> Option<ChannelRun
|
||||
}
|
||||
}
|
||||
"/model" if supports_runtime_model_switch(channel_name) => {
|
||||
let model = parts.collect::<Vec<_>>().join(" ").trim().to_string();
|
||||
let model = tail;
|
||||
if model.is_empty() {
|
||||
Some(ChannelRuntimeCommand::ShowModel)
|
||||
} else {
|
||||
@ -717,6 +725,76 @@ fn parse_runtime_command(channel_name: &str, content: &str) -> Option<ChannelRun
|
||||
}
|
||||
}
|
||||
|
||||
fn is_runtime_token(value: &str) -> bool {
|
||||
let token = value.trim();
|
||||
!token.is_empty()
|
||||
&& token
|
||||
.chars()
|
||||
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-' | '.' | ':'))
|
||||
}
|
||||
|
||||
fn extract_runtime_tail_token(text: &str, prefixes: &[&str]) -> Option<String> {
|
||||
prefixes.iter().find_map(|prefix| {
|
||||
text.strip_prefix(prefix).and_then(|rest| {
|
||||
let token = rest.trim();
|
||||
if is_runtime_token(token) {
|
||||
Some(token.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_natural_language_runtime_command(content: &str) -> Option<ChannelRuntimeCommand> {
|
||||
let trimmed = content.trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let lower = trimmed.to_ascii_lowercase();
|
||||
if matches!(
|
||||
lower.as_str(),
|
||||
"show pending approvals" | "list pending approvals" | "pending approvals"
|
||||
) {
|
||||
return Some(ChannelRuntimeCommand::ListPendingApprovals);
|
||||
}
|
||||
if trimmed == "查看授权"
|
||||
|| matches!(
|
||||
lower.as_str(),
|
||||
"show approvals" | "list approvals" | "approvals"
|
||||
)
|
||||
{
|
||||
return Some(ChannelRuntimeCommand::ListApprovals);
|
||||
}
|
||||
|
||||
if let Some(request_id) = extract_runtime_tail_token(&lower, &["confirm "]) {
|
||||
return Some(ChannelRuntimeCommand::ConfirmToolApproval(request_id));
|
||||
}
|
||||
if let Some(request_id) = extract_runtime_tail_token(trimmed, &["确认授权 "]) {
|
||||
return Some(ChannelRuntimeCommand::ConfirmToolApproval(request_id));
|
||||
}
|
||||
|
||||
if let Some(tool) =
|
||||
extract_runtime_tail_token(&lower, &["revoke tool ", "unapprove ", "revoke "])
|
||||
{
|
||||
return Some(ChannelRuntimeCommand::UnapproveTool(tool));
|
||||
}
|
||||
if let Some(tool) = extract_runtime_tail_token(trimmed, &["撤销工具 ", "取消授权 "]) {
|
||||
return Some(ChannelRuntimeCommand::UnapproveTool(tool));
|
||||
}
|
||||
|
||||
if let Some(tool) = extract_runtime_tail_token(&lower, &["approve tool ", "approve "]) {
|
||||
return Some(ChannelRuntimeCommand::RequestToolApproval(tool));
|
||||
}
|
||||
if let Some(tool) = extract_runtime_tail_token(trimmed, &["授权工具 ", "请放开 ", "放开 "])
|
||||
{
|
||||
return Some(ChannelRuntimeCommand::RequestToolApproval(tool));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_approval_management_command(command: &ChannelRuntimeCommand) -> bool {
|
||||
matches!(
|
||||
command,
|
||||
@ -4464,7 +4542,9 @@ pub async fn start_channels(config: Config) -> Result<()> {
|
||||
} else {
|
||||
None
|
||||
},
|
||||
non_cli_excluded_tools: Arc::new(config.autonomy.non_cli_excluded_tools.clone()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(
|
||||
config.autonomy.non_cli_excluded_tools.clone(),
|
||||
)),
|
||||
query_classification: config.query_classification.clone(),
|
||||
model_routes: config.model_routes.clone(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&config.autonomy)),
|
||||
@ -4768,7 +4848,7 @@ mod tests {
|
||||
provider_runtime_options: providers::ProviderRuntimeOptions::default(),
|
||||
workspace_dir: Arc::new(std::env::temp_dir()),
|
||||
message_timeout_secs: CHANNEL_MESSAGE_TIMEOUT_SECS,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -4822,7 +4902,7 @@ mod tests {
|
||||
provider_runtime_options: providers::ProviderRuntimeOptions::default(),
|
||||
workspace_dir: Arc::new(std::env::temp_dir()),
|
||||
message_timeout_secs: CHANNEL_MESSAGE_TIMEOUT_SECS,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -4879,7 +4959,7 @@ mod tests {
|
||||
provider_runtime_options: providers::ProviderRuntimeOptions::default(),
|
||||
workspace_dir: Arc::new(std::env::temp_dir()),
|
||||
message_timeout_secs: CHANNEL_MESSAGE_TIMEOUT_SECS,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -5478,6 +5558,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(vec!["mock_price".to_string()])),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
&crate::config::AutonomyConfig::default(),
|
||||
)),
|
||||
@ -5548,14 +5630,14 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
workspace_dir: Arc::new(std::env::temp_dir()),
|
||||
message_timeout_secs: CHANNEL_MESSAGE_TIMEOUT_SECS,
|
||||
interrupt_on_new_message: false,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
&crate::config::AutonomyConfig::default(),
|
||||
)),
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
});
|
||||
|
||||
process_channel_message(
|
||||
@ -5612,14 +5694,14 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
workspace_dir: Arc::new(std::env::temp_dir()),
|
||||
message_timeout_secs: CHANNEL_MESSAGE_TIMEOUT_SECS,
|
||||
interrupt_on_new_message: false,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
&crate::config::AutonomyConfig::default(),
|
||||
)),
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
});
|
||||
|
||||
process_channel_message(
|
||||
@ -5838,7 +5920,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -5902,7 +5984,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -5975,7 +6057,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -6079,6 +6161,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&autonomy_cfg)),
|
||||
});
|
||||
assert_eq!(
|
||||
@ -6212,6 +6296,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&autonomy_cfg)),
|
||||
});
|
||||
assert_eq!(
|
||||
@ -6320,6 +6406,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager,
|
||||
});
|
||||
|
||||
@ -6422,6 +6510,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(vec!["shell".to_string()])),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager,
|
||||
});
|
||||
|
||||
@ -6514,6 +6604,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&autonomy_cfg)),
|
||||
});
|
||||
|
||||
@ -6647,6 +6739,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&autonomy_cfg)),
|
||||
});
|
||||
|
||||
@ -6760,6 +6854,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&autonomy_cfg)),
|
||||
});
|
||||
|
||||
@ -6853,6 +6949,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&autonomy_cfg)),
|
||||
});
|
||||
|
||||
@ -6965,6 +7063,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(&autonomy_cfg)),
|
||||
});
|
||||
|
||||
@ -7077,7 +7177,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7153,7 +7253,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7244,7 +7344,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7385,6 +7485,8 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
&crate::config::AutonomyConfig::default(),
|
||||
)),
|
||||
@ -7479,7 +7581,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7544,7 +7646,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7720,7 +7822,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7805,7 +7907,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: true,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7902,7 +8004,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: true,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -7981,7 +8083,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -8045,7 +8147,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -8566,7 +8668,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -8656,7 +8758,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -8746,7 +8848,7 @@ BTC is currently around $65,000 based on latest tool output."#
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -9377,7 +9479,7 @@ BTC is currently around $65,000 based on latest tool output."#;
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
@ -9448,7 +9550,7 @@ BTC is currently around $65,000 based on latest tool output."#;
|
||||
interrupt_on_new_message: false,
|
||||
multimodal: crate::config::MultimodalConfig::default(),
|
||||
hooks: None,
|
||||
non_cli_excluded_tools: Arc::new(Vec::new()),
|
||||
non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())),
|
||||
query_classification: crate::config::QueryClassificationConfig::default(),
|
||||
model_routes: Vec::new(),
|
||||
approval_manager: Arc::new(ApprovalManager::from_config(
|
||||
|
||||
@ -7,7 +7,7 @@ use directories::UserDirs;
|
||||
use parking_lot::Mutex;
|
||||
use reqwest::multipart::{Form, Part};
|
||||
use std::fmt::Write as _;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
use tokio::fs;
|
||||
|
||||
@ -11,7 +11,7 @@ pub use schema::{
|
||||
DockerRuntimeConfig, EmbeddingRouteConfig, EstopConfig, FeishuConfig, GatewayConfig,
|
||||
GroupReplyConfig, GroupReplyMode, HardwareConfig, HardwareTransport, HeartbeatConfig,
|
||||
HooksConfig, HttpRequestConfig, IMessageConfig, IdentityConfig, LarkConfig, MatrixConfig,
|
||||
MemoryConfig, ModelRouteConfig, MultimodalConfig, NextcloudTalkConfig, ObservabilityConfig,
|
||||
MultimodalConfig, NextcloudTalkConfig, NonCliNaturalLanguageApprovalMode, ObservabilityConfig,
|
||||
OtpConfig, OtpMethod, PeripheralBoardConfig, PeripheralsConfig, ProviderConfig, ProxyConfig,
|
||||
ProxyScope, QdrantConfig, QueryClassificationConfig, ReliabilityConfig, ResearchPhaseConfig,
|
||||
ResearchTrigger, ResourceLimitsConfig, RuntimeConfig, SandboxBackend, SandboxConfig,
|
||||
|
||||
@ -2450,6 +2450,9 @@ impl Default for AutonomyConfig {
|
||||
always_ask: default_always_ask(),
|
||||
allowed_roots: Vec::new(),
|
||||
non_cli_excluded_tools: default_non_cli_excluded_tools(),
|
||||
non_cli_approval_approvers: Vec::new(),
|
||||
non_cli_natural_language_approval_mode: NonCliNaturalLanguageApprovalMode::default(),
|
||||
non_cli_natural_language_approval_mode_by_channel: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -720,6 +720,61 @@ impl BrowserTool {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn resolve_output_path_for_write(
|
||||
&self,
|
||||
key: &str,
|
||||
path: &str,
|
||||
) -> anyhow::Result<PathBuf> {
|
||||
let trimmed = path.trim();
|
||||
self.validate_output_path(key, trimmed)?;
|
||||
|
||||
tokio::fs::create_dir_all(&self.security.workspace_dir).await?;
|
||||
let workspace_root = tokio::fs::canonicalize(&self.security.workspace_dir)
|
||||
.await
|
||||
.unwrap_or_else(|_| self.security.workspace_dir.clone());
|
||||
|
||||
let raw_path = Path::new(trimmed);
|
||||
let output_path = if raw_path.is_absolute() {
|
||||
raw_path.to_path_buf()
|
||||
} else {
|
||||
workspace_root.join(raw_path)
|
||||
};
|
||||
|
||||
let parent = output_path
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow::anyhow!("'{key}' path has no parent directory"))?;
|
||||
tokio::fs::create_dir_all(parent).await?;
|
||||
let resolved_parent = tokio::fs::canonicalize(parent).await?;
|
||||
if !self.security.is_resolved_path_allowed(&resolved_parent) {
|
||||
anyhow::bail!(
|
||||
"{}",
|
||||
self.security
|
||||
.resolved_path_violation_message(&resolved_parent)
|
||||
);
|
||||
}
|
||||
|
||||
match tokio::fs::symlink_metadata(&output_path).await {
|
||||
Ok(meta) => {
|
||||
if meta.file_type().is_symlink() {
|
||||
anyhow::bail!(
|
||||
"Refusing to write browser output through symlink: {}",
|
||||
output_path.display()
|
||||
);
|
||||
}
|
||||
if !meta.is_file() {
|
||||
anyhow::bail!(
|
||||
"Browser output path is not a regular file: {}",
|
||||
output_path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == ErrorKind::NotFound => {}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
|
||||
Ok(output_path)
|
||||
}
|
||||
|
||||
fn validate_computer_use_action(
|
||||
&self,
|
||||
action: &str,
|
||||
@ -1127,7 +1182,7 @@ impl Tool for BrowserTool {
|
||||
});
|
||||
}
|
||||
|
||||
let mut action = match parse_browser_action(action_str, &args) {
|
||||
let action = match parse_browser_action(action_str, &args) {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
return Ok(ToolResult {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user