fix(team): inject /setup-saved provider config for sub-agents
Sub-agents failed because the main CLI calls inject_config_as_env_fallbacks()
to set CLAWCUSTOMOPENAI_API_KEY / CLAWCUSTOMOPENAI_BASE_URL (and other
provider env vars) before building its provider client, but the sub-agent
spawn path in the tools crate never did. Custom providers set up via /setup
were invisible to spawned agents, so they fell through to the Anthropic
provider and died with 'missing Anthropic credentials'.
Move inject_config_as_env_fallbacks from main.rs private fn into
runtime/src/config.rs as a pub fn, export it via runtime::lib.rs, and call
it at the top of run_agent_job() before the agent runtime is built.
- runtime::inject_config_as_env_fallbacks(): idempotent (only sets env vars
that aren't already present), reads provider config saved by /setup,
maps provider kind -> env var names (including custom-openai ->
CLAWCUSTOMOPENAI_*), injects api_key/base_url/model.
- main.rs now calls runtime::inject_config_as_env_fallbacks() instead of
the local private version.
- tools::run_agent_job calls runtime::inject_config_as_env_fallbacks()
before building the agent runtime so custom/exotic providers work for
spawned agents too.
Together with the earlier model-resolution and chain-fallback fixes
(d8716efb), sub-agents now: (1) use the session's model by default,
(2) inject the /setup-saved provider credentials, and (3) fall through
to the next configured chain entry on a model-not-found 404.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
d8716efb8e
commit
7dc422de9c
|
|
@ -2608,6 +2608,49 @@ fn deep_merge_objects(
|
|||
}
|
||||
}
|
||||
|
||||
/// Read the provider config saved by `/setup` and inject its credentials
|
||||
/// into the environment so `ProviderClient::from_model()` can find them via
|
||||
/// the env-var-based provider dispatch. Only sets vars that aren't already
|
||||
/// present (preserves explicit env). Idempotent.
|
||||
///
|
||||
/// Called by the main CLI at startup and by sub-agent threads in the tools
|
||||
/// crate so that custom/ exotic providers work for spawned agents too.
|
||||
pub fn inject_config_as_env_fallbacks() {
|
||||
let cwd = std::env::current_dir().unwrap_or_default();
|
||||
let Ok(config) = ConfigLoader::default_for(&cwd).load() else {
|
||||
return;
|
||||
};
|
||||
let provider = config.provider();
|
||||
|
||||
// Map provider kind to the expected env var names
|
||||
let (api_key_env, base_url_env) = match provider.kind().unwrap_or("anthropic") {
|
||||
"anthropic" => ("ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"),
|
||||
"xai" => ("XAI_API_KEY", "XAI_BASE_URL"),
|
||||
"openai" => ("OPENAI_API_KEY", "OPENAI_BASE_URL"),
|
||||
"dashscope" => ("DASHSCOPE_API_KEY", "DASHSCOPE_BASE_URL"),
|
||||
"custom-openai" => ("CLAWCUSTOMOPENAI_API_KEY", "CLAWCUSTOMOPENAI_BASE_URL"),
|
||||
_ => return, // unknown provider kind — don't inject
|
||||
};
|
||||
|
||||
// Only set env vars that aren't already set (preserve user's explicit env)
|
||||
if let Some(api_key) = provider.api_key() {
|
||||
if std::env::var(api_key_env).is_err() {
|
||||
std::env::set_var(api_key_env, api_key);
|
||||
}
|
||||
}
|
||||
if let Some(base_url) = provider.base_url() {
|
||||
if std::env::var(base_url_env).is_err() {
|
||||
std::env::set_var(base_url_env, base_url);
|
||||
}
|
||||
}
|
||||
// Also inject the saved model so resolve_model_alias sees it
|
||||
if let Some(model) = provider.model() {
|
||||
if std::env::var("CLAWD_PROVIDER_MODEL").is_err() {
|
||||
std::env::set_var("CLAWD_PROVIDER_MODEL", model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@ pub use compact::{
|
|||
get_compact_continuation_message, should_compact, CompactionConfig, CompactionResult,
|
||||
};
|
||||
pub use config::{
|
||||
clear_user_provider_settings, default_config_home, save_user_provider_settings,
|
||||
suppress_config_warnings_for_json_mode, ApiTimeoutConfig, ConfigEntry, ConfigError,
|
||||
ConfigFileReport, ConfigFileStatus, ConfigInspection, ConfigLoader, ConfigSource,
|
||||
clear_user_provider_settings, default_config_home, inject_config_as_env_fallbacks,
|
||||
save_user_provider_settings, suppress_config_warnings_for_json_mode, ApiTimeoutConfig, ConfigEntry,
|
||||
ConfigError, ConfigFileReport, ConfigFileStatus, ConfigInspection, ConfigLoader, ConfigSource,
|
||||
McpConfigCollection, McpInvalidServerConfig, McpManagedProxyServerConfig, McpOAuthConfig,
|
||||
McpRemoteServerConfig, McpSdkServerConfig, McpServerConfig, McpStdioServerConfig, McpTransport,
|
||||
McpWebSocketServerConfig, OAuthConfig, ProviderFallbackConfig, ResolvedPermissionMode,
|
||||
|
|
|
|||
|
|
@ -1018,7 +1018,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// vars are preserved — resolution order remains: env var > .env file >
|
||||
// stored config. This only runs in the real binary (run() is not called by
|
||||
// unit tests) to avoid leaking user config into the test suite.
|
||||
inject_config_as_env_fallbacks();
|
||||
runtime::inject_config_as_env_fallbacks();
|
||||
match parse_args(&args)? {
|
||||
CliAction::DumpManifests {
|
||||
output_format,
|
||||
|
|
|
|||
|
|
@ -5523,6 +5523,13 @@ fn teardown_agent_worktree(agent_id: &str, worktree_path: &std::path::Path) -> R
|
|||
}
|
||||
|
||||
fn run_agent_job(job: &AgentJob) -> Result<(), String> {
|
||||
// Inject provider credentials from /setup-saved config into env vars so
|
||||
// sub-agents can reach the same endpoint the user connected the main session
|
||||
// with (e.g. custom-openai's CLAWCUSTOMOPENAI_API_KEY / BASE_URL). Without
|
||||
// this, sub-agents can't find custom/ or exotic providers that were set up
|
||||
// via /setup rather than via explicit env vars.
|
||||
runtime::inject_config_as_env_fallbacks();
|
||||
|
||||
// Claim task if task_id is set (prevents duplicate work)
|
||||
if let Some(ref task_id) = job.task_id {
|
||||
if let Some(ref team_id) = job.team_id {
|
||||
|
|
|
|||
Loading…
Reference in New Issue