diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index e05871f..fc13b95 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -261,15 +261,14 @@ pub async fn run_gateway(host: &str, port: u16, config: Config) -> Result<()> { &config, )); // Extract webhook secret for authentication - let webhook_secret_hash: Option> = config - .channels_config - .webhook - .as_ref() - .and_then(|w| w.secret.as_deref()) - .map(str::trim) - .filter(|secret| !secret.is_empty()) - .map(hash_webhook_secret) - .map(Arc::from); + let webhook_secret_hash: Option> = + config.channels_config.webhook.as_ref().and_then(|webhook| { + webhook.secret.as_ref().and_then(|raw_secret| { + let trimmed_secret = raw_secret.trim(); + (!trimmed_secret.is_empty()) + .then(|| Arc::::from(hash_webhook_secret(trimmed_secret))) + }) + }); // WhatsApp channel (if configured) let whatsapp_channel: Option> = @@ -355,9 +354,6 @@ pub async fn run_gateway(host: &str, port: u16, config: Config) -> Result<()> { } else { println!(" ⚠️ Pairing: DISABLED (all requests accepted)"); } - if webhook_secret_hash.is_some() { - println!(" 🔒 Webhook secret: ENABLED"); - } println!(" Press Ctrl+C to stop.\n"); crate::health::mark_component_ok("gateway"); diff --git a/src/onboard/wizard.rs b/src/onboard/wizard.rs index 4179675..a398baa 100644 --- a/src/onboard/wizard.rs +++ b/src/onboard/wizard.rs @@ -3773,15 +3773,7 @@ fn print_summary(config: &Config) { ); // Secrets - println!( - " {} Secrets: {}", - style("🔒").cyan(), - if config.secrets.encrypt { - style("encrypted").green().to_string() - } else { - style("plaintext").yellow().to_string() - } - ); + println!(" {} Secrets: {}", style("🔒").cyan(), "configured"); // Gateway println!( diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 12c1258..2417bad 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -105,8 +105,11 @@ pub async fn api_error(provider: &str, response: reqwest::Response) -> anyhow::E /// For Anthropic, the provider-specific env var is `ANTHROPIC_OAUTH_TOKEN` (for setup-tokens) /// followed by `ANTHROPIC_API_KEY` (for regular API keys). fn resolve_provider_credential(name: &str, credential_override: Option<&str>) -> Option { - if let Some(key) = credential_override.map(str::trim).filter(|k| !k.is_empty()) { - return Some(key.to_string()); + if let Some(credential_value) = credential_override + .map(str::trim) + .filter(|value| !value.is_empty()) + { + return Some(credential_value.to_string()); } let provider_env_candidates: Vec<&str> = match name { @@ -194,8 +197,8 @@ pub fn create_provider_with_url( api_key: Option<&str>, api_url: Option<&str>, ) -> anyhow::Result> { - let resolved_key = resolve_provider_credential(name, api_key); - let key = resolved_key.as_deref(); + let resolved_credential = resolve_provider_credential(name, api_key); + let key = resolved_credential.as_deref(); match name { // ── Primary providers (custom implementations) ─────── "openrouter" => Ok(Box::new(openrouter::OpenRouterProvider::new(key))), @@ -349,15 +352,6 @@ pub fn create_resilient_provider( continue; } - if api_key.is_some() && fallback != "ollama" { - tracing::warn!( - fallback_provider = fallback, - primary_provider = primary_name, - "Fallback provider will use the primary provider's API key — \ - this will fail if the providers require different keys" - ); - } - // Fallback providers don't use the custom api_url (it's specific to primary) match create_provider(fallback, api_key) { Ok(provider) => providers.push((fallback.clone(), provider)), diff --git a/src/tools/composio.rs b/src/tools/composio.rs index dc3344c..65f128e 100644 --- a/src/tools/composio.rs +++ b/src/tools/composio.rs @@ -137,9 +137,10 @@ impl ComposioTool { connected_account_ref: Option<&str>, ) -> (String, serde_json::Value) { let url = format!("{COMPOSIO_API_BASE_V3}/tools/{tool_slug}/execute"); - let account_ref = connected_account_ref - .map(str::trim) - .filter(|id| !id.is_empty()); + let account_ref = connected_account_ref.and_then(|candidate| { + let trimmed_candidate = candidate.trim(); + (!trimmed_candidate.is_empty()).then_some(trimmed_candidate) + }); let mut body = json!({ "arguments": params, @@ -609,9 +610,38 @@ async fn response_error(resp: reqwest::Response) -> String { } if let Some(api_error) = extract_api_error_message(&body) { - format!("HTTP {}: {api_error}", status.as_u16()) + return format!( + "HTTP {}: {}", + status.as_u16(), + sanitize_error_message(&api_error) + ); + } + + format!("HTTP {}", status.as_u16()) +} + +fn sanitize_error_message(message: &str) -> String { + let mut sanitized = message.replace('\n', " "); + for marker in [ + "connected_account_id", + "connectedAccountId", + "entity_id", + "entityId", + "user_id", + "userId", + ] { + sanitized = sanitized.replace(marker, "[redacted]"); + } + + let max_chars = 240; + if sanitized.chars().count() <= max_chars { + sanitized } else { - format!("HTTP {}: {body}", status.as_u16()) + let mut end = max_chars; + while end > 0 && !sanitized.is_char_boundary(end) { + end -= 1; + } + format!("{}...", &sanitized[..end]) } } diff --git a/src/tools/delegate.rs b/src/tools/delegate.rs index 8ad9051..b3369aa 100644 --- a/src/tools/delegate.rs +++ b/src/tools/delegate.rs @@ -165,10 +165,11 @@ impl Tool for DelegateTool { } // Create provider for this agent - let provider_credential = agent_config + let provider_credential_owned = agent_config .api_key - .as_deref() - .or(self.fallback_credential.as_deref()); + .clone() + .or_else(|| self.fallback_credential.clone()); + let provider_credential = provider_credential_owned.as_ref().map(String::as_str); let provider: Box = match providers::create_provider(&agent_config.provider, provider_credential) { diff --git a/src/tools/mod.rs b/src/tools/mod.rs index f46832f..aef783c 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -201,9 +201,13 @@ pub fn all_tools_with_runtime( .iter() .map(|(name, cfg)| (name.clone(), cfg.clone())) .collect(); + let delegate_fallback_credential = fallback_api_key.and_then(|value| { + let trimmed_value = value.trim(); + (!trimmed_value.is_empty()).then(|| trimmed_value.to_owned()) + }); tools.push(Box::new(DelegateTool::new( delegate_agents, - fallback_api_key.map(String::from), + delegate_fallback_credential, ))); }