mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Wire shared runtime authentication through threads and API clients (#59177)
## What changed - Share a lazily registered runtime across threads, model discovery, and extension clients when `runtime_agent_instances` is enabled, while honoring each thread's authentication mode and credential owner. - Bind model catalogs to the effective provider and authentication context while preserving cache policy and live API-key discovery settings. - Resolve credentials, routing, and transport policy together for each request. Preserve proxy and TLS settings, validate runtime account selectors, and prevent pooled connections from crossing runtime identities. - Accept supported ChatGPT subdomains when selecting the registration environment. ## Testing Add integration coverage for shared runtime registration, dual-header and assertion-only modes, disabled and third-party providers, credential replacement, subagents, and delegated review. Add coverage for catalog cache policy and runtime-aware connection reuse. GitOrigin-RevId: 99dd9d987c29c93597a648f6586f98ac6a932c3c
This commit is contained in:
@@ -9,6 +9,7 @@ use crate::RuntimeAuth;
|
||||
use crate::RuntimeRegistration;
|
||||
use crate::RuntimeRegistrationClient;
|
||||
use crate::RuntimeRegistrationMetadata;
|
||||
use codex_agent_identity::ChatGptEnvironment;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_http_client::NetworkPolicyDenied;
|
||||
use codex_login::AuthChangeState;
|
||||
@@ -23,12 +24,52 @@ use url::Url;
|
||||
/// Owned alongside an AuthManager, not created for every thread or request.
|
||||
pub struct RuntimeAuthManager {
|
||||
credentials: Arc<AuthManager>,
|
||||
host: AgentHostAuthorization,
|
||||
registration: RegistrationTarget,
|
||||
metadata: RuntimeRegistrationMetadata,
|
||||
registration_url: Url,
|
||||
state: Mutex<Option<CachedRuntime>>,
|
||||
}
|
||||
|
||||
enum RegistrationTarget {
|
||||
Explicit {
|
||||
host: AgentHostAuthorization,
|
||||
url: Url,
|
||||
},
|
||||
ChatGpt {
|
||||
installation_id: String,
|
||||
base_url: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl RegistrationTarget {
|
||||
fn host(&self) -> Result<AgentHostAuthorization, RegistrationError> {
|
||||
match self {
|
||||
Self::Explicit { host, .. } => Ok(host.clone()),
|
||||
Self::ChatGpt {
|
||||
installation_id, ..
|
||||
} => Ok(AgentHostAuthorization::InstallationId {
|
||||
installation_id: installation_id
|
||||
.parse()
|
||||
.map_err(|_| RegistrationError::InvalidRequest)?,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn url(&self) -> Result<Url, RegistrationError> {
|
||||
match self {
|
||||
Self::Explicit { url, .. } => Ok(url.clone()),
|
||||
Self::ChatGpt { base_url, .. } => {
|
||||
let url = match ChatGptEnvironment::from_chatgpt_base_url(base_url)
|
||||
.map_err(|_| RegistrationError::InvalidEndpoint)?
|
||||
{
|
||||
ChatGptEnvironment::Production => crate::DEFAULT_REGISTRATION_URL,
|
||||
ChatGptEnvironment::Staging => "https://auth.api.openai.org/agent/register",
|
||||
};
|
||||
url.parse().map_err(|_| RegistrationError::InvalidEndpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CachedRuntime {
|
||||
source_generation: u64,
|
||||
selection: PlatformResourceSelection,
|
||||
@@ -61,9 +102,29 @@ impl RuntimeAuthManager {
|
||||
) -> Self {
|
||||
Self {
|
||||
credentials,
|
||||
host,
|
||||
registration: RegistrationTarget::Explicit {
|
||||
host,
|
||||
url: registration_url,
|
||||
},
|
||||
metadata,
|
||||
state: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the ChatGPT registration origin only when a first-party request needs a runtime.
|
||||
pub fn for_chatgpt(
|
||||
credentials: Arc<AuthManager>,
|
||||
installation_id: String,
|
||||
base_url: String,
|
||||
metadata: RuntimeRegistrationMetadata,
|
||||
) -> Self {
|
||||
Self {
|
||||
credentials,
|
||||
registration: RegistrationTarget::ChatGpt {
|
||||
installation_id,
|
||||
base_url,
|
||||
},
|
||||
metadata,
|
||||
registration_url,
|
||||
state: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
@@ -149,7 +210,7 @@ impl RuntimeAuthManager {
|
||||
state: RuntimeState::Pending {
|
||||
registration: RuntimeRegistration::new(
|
||||
actor_auth.clone(),
|
||||
self.host.clone(),
|
||||
self.registration.host()?,
|
||||
self.metadata.clone(),
|
||||
)
|
||||
.map_err(|_| RuntimeAuthError::KeyGeneration)?,
|
||||
@@ -172,10 +233,8 @@ impl RuntimeAuthManager {
|
||||
registration
|
||||
}
|
||||
};
|
||||
let client = RuntimeRegistrationClient::new(
|
||||
http_client_factory.clone(),
|
||||
self.registration_url.clone(),
|
||||
)?;
|
||||
let client =
|
||||
RuntimeRegistrationClient::new(http_client_factory.clone(), self.registration.url()?)?;
|
||||
let result = client.register(registration).await;
|
||||
if self.auth_state(&auth)?.source_generation != change_state.source_generation {
|
||||
// A late response must not publish an old identity after a credential switch.
|
||||
|
||||
Reference in New Issue
Block a user