diff --git a/crates/tui/src/tools/approval_cache.rs b/crates/tui/src/tools/approval_cache.rs index 937626c7..c574d79a 100644 --- a/crates/tui/src/tools/approval_cache.rs +++ b/crates/tui/src/tools/approval_cache.rs @@ -13,7 +13,7 @@ //! | `apply_patch` | `patch:` | //! | `exec_shell` | `shell:` | //! | `fetch_url` | `net:` | -//! | everything else| `tool:` | +//! | everything else| `tool::` | //! //! The cache is **session‑keyed**: entries carry an //! `ApprovedForSession` flag. When true, the approval is reused for the @@ -136,7 +136,10 @@ pub fn build_approval_key(tool_name: &str, input: &serde_json::Value) -> Approva let host = parse_host(input); format!("net:{host}") } - _ => format!("tool:{tool_name}"), + _ => { + let input_hash = hash_json_input(input); + format!("tool:{tool_name}:{input_hash}") + } }; ApprovalKey(fingerprint) } @@ -190,6 +193,17 @@ fn hash_patch_paths(input: &serde_json::Value) -> String { format!("{:x}", hasher.finish()) } +fn hash_json_input(input: &serde_json::Value) -> String { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + serde_json::to_string(input) + .unwrap_or_default() + .hash(&mut hasher); + format!("{:x}", hasher.finish()) +} + /// Parse the host portion from a URL input. fn parse_host(input: &serde_json::Value) -> String { let url = input.get("url").and_then(|v| v.as_str()).unwrap_or(""); @@ -271,10 +285,18 @@ mod tests { } #[test] - fn generic_tool_uses_tool_name() { + fn generic_tool_keys_include_arguments() { let key_a = build_approval_key("read_file", &json!({"path": "a.txt"})); let key_b = build_approval_key("read_file", &json!({"path": "b.txt"})); + assert_ne!(key_a, key_b); + assert!(key_a.0.starts_with("tool:read_file:")); + } + + #[test] + fn generic_tool_same_arguments_reuse_key() { + let input = json!({"path": "a.txt"}); + let key_a = build_approval_key("edit_file", &input); + let key_b = build_approval_key("edit_file", &input); assert_eq!(key_a, key_b); - assert_eq!(key_a.0, "tool:read_file"); } }