mirror of
https://github.com/openai/codex.git
synced 2026-05-18 02:02:30 +00:00
Use ChatGPT base URL for agent identity registration
This commit is contained in:
@@ -271,15 +271,6 @@ pub struct ConfigToml {
|
||||
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
|
||||
pub chatgpt_base_url: Option<String>,
|
||||
|
||||
/// Base URL for agent identity registration requests.
|
||||
pub agent_identity_base_url: Option<String>,
|
||||
|
||||
/// Base URL for minting human biscuits used by agent identity registration requests.
|
||||
pub agent_identity_biscuit_base_url: Option<String>,
|
||||
|
||||
/// Target URL used when minting the human biscuit for agent identity registration.
|
||||
pub agent_identity_biscuit_target_url: Option<String>,
|
||||
|
||||
/// Base URL override for the built-in `openai` model provider.
|
||||
pub openai_base_url: Option<String>,
|
||||
|
||||
|
||||
@@ -2049,18 +2049,6 @@
|
||||
},
|
||||
"description": "Base config deserialized from ~/.codex/config.toml.",
|
||||
"properties": {
|
||||
"agent_identity_base_url": {
|
||||
"description": "Base URL for agent identity registration requests.",
|
||||
"type": "string"
|
||||
},
|
||||
"agent_identity_biscuit_base_url": {
|
||||
"description": "Base URL for minting human biscuits used by agent identity registration requests.",
|
||||
"type": "string"
|
||||
},
|
||||
"agent_identity_biscuit_target_url": {
|
||||
"description": "Target URL used when minting the human biscuit for agent identity registration.",
|
||||
"type": "string"
|
||||
},
|
||||
"agents": {
|
||||
"allOf": [
|
||||
{
|
||||
|
||||
@@ -35,16 +35,12 @@ use crate::config::Config;
|
||||
const AGENT_IDENTITY_SECRET_NAME: &str = "AGENT_IDENTITY";
|
||||
const AGENT_REGISTRATION_TIMEOUT: Duration = Duration::from_secs(15);
|
||||
const AGENT_IDENTITY_BISCUIT_TIMEOUT: Duration = Duration::from_secs(15);
|
||||
#[cfg(test)]
|
||||
const AGENT_IDENTITY_BISCUIT_TARGET_URL: &str = "https://api.openai.com/v1/responses";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct AgentIdentityManager {
|
||||
auth_manager: Arc<AuthManager>,
|
||||
secrets_manager: SecretsManager,
|
||||
agent_identity_base_url: String,
|
||||
agent_identity_biscuit_base_url: String,
|
||||
agent_identity_biscuit_target_url: String,
|
||||
chatgpt_base_url: String,
|
||||
feature_enabled: bool,
|
||||
abom: AgentBillOfMaterials,
|
||||
ensure_lock: Arc<Mutex<()>>,
|
||||
@@ -53,15 +49,7 @@ pub(crate) struct AgentIdentityManager {
|
||||
impl std::fmt::Debug for AgentIdentityManager {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AgentIdentityManager")
|
||||
.field("agent_identity_base_url", &self.agent_identity_base_url)
|
||||
.field(
|
||||
"agent_identity_biscuit_base_url",
|
||||
&self.agent_identity_biscuit_base_url,
|
||||
)
|
||||
.field(
|
||||
"agent_identity_biscuit_target_url",
|
||||
&self.agent_identity_biscuit_target_url,
|
||||
)
|
||||
.field("chatgpt_base_url", &self.chatgpt_base_url)
|
||||
.field("feature_enabled", &self.feature_enabled)
|
||||
.field("abom", &self.abom)
|
||||
.finish_non_exhaustive()
|
||||
@@ -124,9 +112,7 @@ impl AgentIdentityManager {
|
||||
config.codex_home.clone(),
|
||||
SecretsBackendKind::Local,
|
||||
),
|
||||
agent_identity_base_url: config.agent_identity_base_url.clone(),
|
||||
agent_identity_biscuit_base_url: config.agent_identity_biscuit_base_url.clone(),
|
||||
agent_identity_biscuit_target_url: config.agent_identity_biscuit_target_url.clone(),
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
feature_enabled: config.features.enabled(Feature::UseAgentIdentity),
|
||||
abom: build_abom(session_source),
|
||||
ensure_lock: Arc::new(Mutex::new(())),
|
||||
@@ -181,9 +167,9 @@ impl AgentIdentityManager {
|
||||
capabilities: Vec::new(),
|
||||
};
|
||||
|
||||
let human_biscuit = self.mint_human_biscuit(binding).await?;
|
||||
let url = agent_registration_url(&self.chatgpt_base_url);
|
||||
let human_biscuit = self.mint_human_biscuit(binding, &url).await?;
|
||||
let client = create_client();
|
||||
let url = agent_registration_url(&self.agent_identity_base_url);
|
||||
let response = client
|
||||
.post(&url)
|
||||
.header("X-OpenAI-Authorization", human_biscuit)
|
||||
@@ -223,12 +209,12 @@ impl AgentIdentityManager {
|
||||
anyhow::bail!("agent identity registration failed with status {status} from {url}: {body}")
|
||||
}
|
||||
|
||||
async fn mint_human_biscuit(&self, binding: &AgentIdentityBinding) -> Result<String> {
|
||||
anyhow::ensure!(
|
||||
!self.agent_identity_biscuit_base_url.trim().is_empty(),
|
||||
"`agent_identity_biscuit_base_url` must be configured when `features.use_agent_identity` is enabled"
|
||||
);
|
||||
let url = agent_identity_biscuit_url(&self.agent_identity_biscuit_base_url);
|
||||
async fn mint_human_biscuit(
|
||||
&self,
|
||||
binding: &AgentIdentityBinding,
|
||||
target_url: &str,
|
||||
) -> Result<String> {
|
||||
let url = agent_identity_biscuit_url(&self.chatgpt_base_url);
|
||||
let request_id = agent_identity_request_id()?;
|
||||
let client = create_client();
|
||||
let response = client
|
||||
@@ -236,7 +222,7 @@ impl AgentIdentityManager {
|
||||
.bearer_auth(&binding.access_token)
|
||||
.header("X-Request-Id", request_id.clone())
|
||||
.header("X-Original-Method", "GET")
|
||||
.header("X-Original-Url", &self.agent_identity_biscuit_target_url)
|
||||
.header("X-Original-Url", target_url)
|
||||
.timeout(AGENT_IDENTITY_BISCUIT_TIMEOUT)
|
||||
.send()
|
||||
.await
|
||||
@@ -331,16 +317,14 @@ impl AgentIdentityManager {
|
||||
fn new_for_tests(
|
||||
auth_manager: Arc<AuthManager>,
|
||||
feature_enabled: bool,
|
||||
agent_identity_base_url: String,
|
||||
chatgpt_base_url: String,
|
||||
session_source: SessionSource,
|
||||
secrets_manager: SecretsManager,
|
||||
) -> Self {
|
||||
Self {
|
||||
auth_manager,
|
||||
secrets_manager,
|
||||
agent_identity_biscuit_base_url: agent_identity_base_url.clone(),
|
||||
agent_identity_base_url,
|
||||
agent_identity_biscuit_target_url: AGENT_IDENTITY_BISCUIT_TARGET_URL.to_string(),
|
||||
chatgpt_base_url,
|
||||
feature_enabled,
|
||||
abom: build_abom(session_source),
|
||||
ensure_lock: Arc::new(Mutex::new(())),
|
||||
@@ -458,13 +442,13 @@ fn secret_scope(binding: &AgentIdentityBinding) -> Result<SecretScope> {
|
||||
.context("agent identity binding must be a valid secrets scope")
|
||||
}
|
||||
|
||||
fn agent_registration_url(agent_identity_base_url: &str) -> String {
|
||||
let trimmed = agent_identity_base_url.trim_end_matches('/');
|
||||
fn agent_registration_url(chatgpt_base_url: &str) -> String {
|
||||
let trimmed = chatgpt_base_url.trim_end_matches('/');
|
||||
format!("{trimmed}/v1/agent/register")
|
||||
}
|
||||
|
||||
fn agent_identity_biscuit_url(agent_identity_biscuit_base_url: &str) -> String {
|
||||
let trimmed = agent_identity_biscuit_base_url.trim_end_matches('/');
|
||||
fn agent_identity_biscuit_url(chatgpt_base_url: &str) -> String {
|
||||
let trimmed = chatgpt_base_url.trim_end_matches('/');
|
||||
format!("{trimmed}/authenticate_app_v2")
|
||||
}
|
||||
|
||||
@@ -545,7 +529,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn ensure_registered_identity_registers_and_reuses_cached_identity() {
|
||||
let server = MockServer::start().await;
|
||||
mount_human_biscuit(&server).await;
|
||||
let chatgpt_base_url = server.uri();
|
||||
mount_human_biscuit(&server, &chatgpt_base_url).await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/v1/agent/register"))
|
||||
.and(header("x-openai-authorization", "human-biscuit"))
|
||||
@@ -568,7 +553,7 @@ mod tests {
|
||||
let manager = AgentIdentityManager::new_for_tests(
|
||||
auth_manager,
|
||||
/*feature_enabled*/ true,
|
||||
server.uri(),
|
||||
chatgpt_base_url,
|
||||
SessionSource::Cli,
|
||||
secrets_manager,
|
||||
);
|
||||
@@ -594,7 +579,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn ensure_registered_identity_deletes_invalid_cached_identity_and_reregisters() {
|
||||
let server = MockServer::start().await;
|
||||
mount_human_biscuit(&server).await;
|
||||
let chatgpt_base_url = server.uri();
|
||||
mount_human_biscuit(&server, &chatgpt_base_url).await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/v1/agent/register"))
|
||||
.and(header("x-openai-authorization", "human-biscuit"))
|
||||
@@ -617,7 +603,7 @@ mod tests {
|
||||
let manager = AgentIdentityManager::new_for_tests(
|
||||
auth_manager,
|
||||
/*feature_enabled*/ true,
|
||||
server.uri(),
|
||||
chatgpt_base_url,
|
||||
SessionSource::Cli,
|
||||
secrets_manager.clone(),
|
||||
);
|
||||
@@ -649,11 +635,12 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ensure_registered_identity_uses_agent_identity_base_url() {
|
||||
async fn ensure_registered_identity_uses_chatgpt_base_url() {
|
||||
let server = MockServer::start().await;
|
||||
mount_human_biscuit(&server).await;
|
||||
let chatgpt_base_url = format!("{}/backend-api", server.uri());
|
||||
mount_human_biscuit(&server, &chatgpt_base_url).await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/v1/agent/register"))
|
||||
.and(path("/backend-api/v1/agent/register"))
|
||||
.and(header("x-openai-authorization", "human-biscuit"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
|
||||
"agent_runtime_id": "agent_canonical",
|
||||
@@ -674,7 +661,7 @@ mod tests {
|
||||
let manager = AgentIdentityManager::new_for_tests(
|
||||
auth_manager,
|
||||
/*feature_enabled*/ true,
|
||||
server.uri(),
|
||||
chatgpt_base_url,
|
||||
SessionSource::Cli,
|
||||
secrets_manager,
|
||||
);
|
||||
@@ -687,12 +674,18 @@ mod tests {
|
||||
assert_eq!(stored.agent_runtime_id, "agent_canonical");
|
||||
}
|
||||
|
||||
async fn mount_human_biscuit(server: &MockServer) {
|
||||
async fn mount_human_biscuit(server: &MockServer, chatgpt_base_url: &str) {
|
||||
let biscuit_url = agent_identity_biscuit_url(chatgpt_base_url);
|
||||
let biscuit_path = reqwest::Url::parse(&biscuit_url)
|
||||
.expect("biscuit URL parses")
|
||||
.path()
|
||||
.to_string();
|
||||
let target_url = agent_registration_url(chatgpt_base_url);
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/authenticate_app_v2"))
|
||||
.and(path(biscuit_path))
|
||||
.and(header("authorization", "Bearer access-token-account-123"))
|
||||
.and(header("x-original-method", "GET"))
|
||||
.and(header("x-original-url", AGENT_IDENTITY_BISCUIT_TARGET_URL))
|
||||
.and(header("x-original-url", target_url))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200).insert_header("x-openai-authorization", "human-biscuit"),
|
||||
)
|
||||
|
||||
@@ -4550,9 +4550,6 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
model_verbosity: None,
|
||||
personality: Some(Personality::Pragmatic),
|
||||
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
|
||||
agent_identity_base_url: "https://auth.openai.com/api/accounts".to_string(),
|
||||
agent_identity_biscuit_base_url: String::new(),
|
||||
agent_identity_biscuit_target_url: "https://api.openai.com/v1/responses".to_string(),
|
||||
realtime_audio: RealtimeAudioConfig::default(),
|
||||
experimental_realtime_start_instructions: None,
|
||||
experimental_realtime_ws_base_url: None,
|
||||
@@ -4699,9 +4696,6 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
model_verbosity: None,
|
||||
personality: Some(Personality::Pragmatic),
|
||||
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
|
||||
agent_identity_base_url: "https://auth.openai.com/api/accounts".to_string(),
|
||||
agent_identity_biscuit_base_url: String::new(),
|
||||
agent_identity_biscuit_target_url: "https://api.openai.com/v1/responses".to_string(),
|
||||
realtime_audio: RealtimeAudioConfig::default(),
|
||||
experimental_realtime_start_instructions: None,
|
||||
experimental_realtime_ws_base_url: None,
|
||||
@@ -4846,9 +4840,6 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
model_verbosity: None,
|
||||
personality: Some(Personality::Pragmatic),
|
||||
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
|
||||
agent_identity_base_url: "https://auth.openai.com/api/accounts".to_string(),
|
||||
agent_identity_biscuit_base_url: String::new(),
|
||||
agent_identity_biscuit_target_url: "https://api.openai.com/v1/responses".to_string(),
|
||||
realtime_audio: RealtimeAudioConfig::default(),
|
||||
experimental_realtime_start_instructions: None,
|
||||
experimental_realtime_ws_base_url: None,
|
||||
@@ -4979,9 +4970,6 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
model_verbosity: Some(Verbosity::High),
|
||||
personality: Some(Personality::Pragmatic),
|
||||
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
|
||||
agent_identity_base_url: "https://auth.openai.com/api/accounts".to_string(),
|
||||
agent_identity_biscuit_base_url: String::new(),
|
||||
agent_identity_biscuit_target_url: "https://api.openai.com/v1/responses".to_string(),
|
||||
realtime_audio: RealtimeAudioConfig::default(),
|
||||
experimental_realtime_start_instructions: None,
|
||||
experimental_realtime_ws_base_url: None,
|
||||
|
||||
@@ -471,15 +471,6 @@ pub struct Config {
|
||||
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
|
||||
pub chatgpt_base_url: String,
|
||||
|
||||
/// Base URL for agent identity registration requests.
|
||||
pub agent_identity_base_url: String,
|
||||
|
||||
/// Base URL for minting human biscuits used by agent identity registration requests.
|
||||
pub agent_identity_biscuit_base_url: String,
|
||||
|
||||
/// Target URL used when minting the human biscuit for agent identity registration.
|
||||
pub agent_identity_biscuit_target_url: String,
|
||||
|
||||
/// Machine-local realtime audio device preferences used by realtime voice.
|
||||
pub realtime_audio: RealtimeAudioConfig,
|
||||
|
||||
@@ -2088,15 +2079,6 @@ impl Config {
|
||||
.chatgpt_base_url
|
||||
.or(cfg.chatgpt_base_url)
|
||||
.unwrap_or("https://chatgpt.com/backend-api/".to_string()),
|
||||
agent_identity_base_url: cfg
|
||||
.agent_identity_base_url
|
||||
.unwrap_or("https://auth.openai.com/api/accounts".to_string()),
|
||||
agent_identity_biscuit_base_url: cfg
|
||||
.agent_identity_biscuit_base_url
|
||||
.unwrap_or_default(),
|
||||
agent_identity_biscuit_target_url: cfg
|
||||
.agent_identity_biscuit_target_url
|
||||
.unwrap_or("https://api.openai.com/v1/responses".to_string()),
|
||||
realtime_audio: cfg
|
||||
.audio
|
||||
.map_or_else(RealtimeAudioConfig::default, |audio| RealtimeAudioConfig {
|
||||
|
||||
Reference in New Issue
Block a user