From e357e2506e13d96aa6d4d5f66e09d418a236660d Mon Sep 17 00:00:00 2001 From: Filip Kujawa Date: Tue, 28 Jul 2026 14:11:02 -0700 Subject: [PATCH] fix(desktop): export configured OpenAI key to every harness Keep Buzz's existing OPENAI_COMPAT_API_KEY configuration for buzz-agent, and mirror it to the standard OPENAI_API_KEY at the shared spawn boundary so Goose and future harnesses receive the credential they expect. Preserve an explicitly configured OPENAI_API_KEY. Signed-off-by: Michael Neale --- .../src-tauri/src/managed_agents/runtime.rs | 7 ++- .../src/managed_agents/runtime/metadata.rs | 61 ++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index ec804869c4..7b359a3564 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -22,8 +22,8 @@ pub(crate) use super::access_policy::{build_respond_to_env_with_policy, RespondT mod metadata; pub(crate) use metadata::{ - apply_agent_display_env, resolve_session_title, runtime_metadata_env_vars, - DISPLAY_NAME_ENV_VAR, SESSION_TITLE_ENV_VAR, + apply_agent_display_env, apply_openai_api_key, resolve_session_title, + runtime_metadata_env_vars, DISPLAY_NAME_ENV_VAR, SESSION_TITLE_ENV_VAR, }; mod stop; @@ -809,6 +809,9 @@ pub fn spawn_agent_child( for (key, value) in &descriptor.env { command.env(key, value); } + + apply_openai_api_key(&mut command, &descriptor.env); + configure_runtime_cli(&mut command, runtime_meta); // Buzz shared compute is stored as a native provider; derive the OpenAI-compatible diff --git a/desktop/src-tauri/src/managed_agents/runtime/metadata.rs b/desktop/src-tauri/src/managed_agents/runtime/metadata.rs index 5aef424ea6..68454720a7 100644 --- a/desktop/src-tauri/src/managed_agents/runtime/metadata.rs +++ b/desktop/src-tauri/src/managed_agents/runtime/metadata.rs @@ -1,3 +1,33 @@ +use std::collections::BTreeMap; + +const OPENAI_API_KEY: &str = "OPENAI_API_KEY"; +const OPENAI_COMPAT_API_KEY: &str = "OPENAI_COMPAT_API_KEY"; + +fn configured_openai_api_key(env: &BTreeMap) -> Option<&str> { + if env.contains_key(OPENAI_API_KEY) { + return None; + } + env.get(OPENAI_COMPAT_API_KEY) + .map(String::as_str) + .filter(|value| !value.trim().is_empty()) +} + +/// Ensure every harness receives an explicitly configured OpenAI credential +/// under OpenAI's standard environment variable name. +/// +/// Buzz's built-in agent still reads `OPENAI_COMPAT_API_KEY`, so Desktop keeps +/// that configured value and mirrors it to `OPENAI_API_KEY` at the common spawn +/// boundary. An explicit `OPENAI_API_KEY` in the layered agent environment is +/// never replaced, including when it was deliberately set to an empty value. +pub(crate) fn apply_openai_api_key( + command: &mut std::process::Command, + env: &BTreeMap, +) { + if let Some(value) = configured_openai_api_key(env) { + command.env(OPENAI_API_KEY, value); + } +} + /// Returns the (key, value) env var pairs that should be forwarded to the /// agent process for model and provider selection. /// @@ -76,7 +106,36 @@ pub(crate) fn resolve_session_title(display_name: Option<&str>, name: &str) -> O #[cfg(test)] mod tests { - use super::resolve_session_title; + use std::collections::BTreeMap; + + use super::{configured_openai_api_key, resolve_session_title}; + + #[test] + fn configured_compat_key_is_exposed_under_openai_standard_name() { + let env = BTreeMap::from([( + "OPENAI_COMPAT_API_KEY".to_string(), + "configured-key".to_string(), + )]); + assert_eq!(configured_openai_api_key(&env), Some("configured-key")); + } + + #[test] + fn explicit_standard_openai_key_is_preserved() { + let env = BTreeMap::from([ + ( + "OPENAI_COMPAT_API_KEY".to_string(), + "configured-key".to_string(), + ), + ("OPENAI_API_KEY".to_string(), "explicit-key".to_string()), + ]); + assert_eq!(configured_openai_api_key(&env), None); + } + + #[test] + fn blank_compat_key_is_not_exported() { + let env = BTreeMap::from([("OPENAI_COMPAT_API_KEY".to_string(), " ".to_string())]); + assert_eq!(configured_openai_api_key(&env), None); + } #[test] fn resolve_session_title_prefers_display_name() {