use std::time::Duration;
use serde_json::Value;
use url::Url;
use vtcode_core::tools::ApprovalRecorder;
use super::shell_approval::ApprovalLearningTarget;
pub(super) const APPROVAL_RECORD_TIMEOUT: Duration = Duration::from_millis(500);
pub(super) async fn record_approval_blocking(
recorder: &ApprovalRecorder,
approval_target: &ApprovalLearningTarget,
approved: bool,
) {
for (key, label) in approval_target.iter_keys() {
match tokio::time::timeout(APPROVAL_RECORD_TIMEOUT, recorder.record_approval(key, Some(label), approved, None))
.await
{
Ok(Ok(())) => {}
Ok(Err(err)) => {
tracing::debug!(
approval_key = %key,
approved,
error = %err,
"Approval-pattern write failed"
);
}
Err(_) => {
tracing::debug!(
approval_key = %key,
approved,
timeout_ms = APPROVAL_RECORD_TIMEOUT.as_millis(),
"Approval-pattern write timed out"
);
}
}
}
}
pub(super) fn web_fetch_domain(tool_args: Option<&Value>) -> Option<String> {
let url = tool_args?.as_object()?.get("url")?.as_str()?;
let parsed = Url::parse(url).ok()?;
let host = parsed.host_str()?;
if host.is_empty() {
return None;
}
Some(host.to_ascii_lowercase())
}
pub(super) fn cache_key(tool_name: &str, tool_args: Option<&Value>) -> String {
if let Some(suffix) = super::permission_prompt::shell_permission_cache_suffix(tool_name, tool_args) {
return format!("{tool_name}:{suffix}");
}
use vtcode_core::config::constants::tools::{CODE_SEARCH, FETCH_URL, WEB_FETCH};
if tool_name == CODE_SEARCH
&& let Some(args) = tool_args
&& let Some(identity) = vtcode_core::tools::normalised_code_search_identity(args)
{
return format!("{tool_name}:{identity}");
}
if (tool_name == WEB_FETCH || tool_name == FETCH_URL)
&& let Some(domain) = web_fetch_domain(tool_args)
{
return format!("{tool_name}:{domain}");
}
tool_name.to_string()
}