From eee692e35167e9c6b7eec459fd8f4b0c347a4458 Mon Sep 17 00:00:00 2001 From: arnavdugar-openai Date: Wed, 25 Mar 2026 15:41:29 -0700 Subject: [PATCH] Treat ChatGPT `hc` plan as Enterprise (#15789) --- codex-rs/cloud-requirements/src/lib.rs | 28 ++++++++++++++++++++++++++ codex-rs/login/src/token_data.rs | 3 ++- codex-rs/login/src/token_data_tests.rs | 15 ++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/codex-rs/cloud-requirements/src/lib.rs b/codex-rs/cloud-requirements/src/lib.rs index fb0f62a34..12e62d880 100644 --- a/codex-rs/cloud-requirements/src/lib.rs +++ b/codex-rs/cloud-requirements/src/lib.rs @@ -1153,6 +1153,34 @@ mod tests { ); } + #[tokio::test] + async fn fetch_cloud_requirements_allows_hc_plan_as_enterprise() { + let codex_home = tempdir().expect("tempdir"); + let service = CloudRequirementsService::new( + auth_manager_with_plan("hc"), + Arc::new(StaticFetcher { + contents: Some("allowed_approval_policies = [\"never\"]".to_string()), + }), + codex_home.path().to_path_buf(), + CLOUD_REQUIREMENTS_TIMEOUT, + ); + assert_eq!( + service.fetch().await, + Ok(Some(ConfigRequirementsToml { + allowed_approval_policies: Some(vec![AskForApproval::Never]), + allowed_sandbox_modes: None, + allowed_web_search_modes: None, + guardian_developer_instructions: None, + feature_requirements: None, + mcp_servers: None, + apps: None, + rules: None, + enforce_residency: None, + network: None, + })) + ); + } + #[tokio::test] async fn fetch_cloud_requirements_handles_missing_contents() { let result = parse_for_fetch(None); diff --git a/codex-rs/login/src/token_data.rs b/codex-rs/login/src/token_data.rs index 1056115a3..a05b153a8 100644 --- a/codex-rs/login/src/token_data.rs +++ b/codex-rs/login/src/token_data.rs @@ -72,7 +72,7 @@ impl PlanType { "pro" => Self::Known(KnownPlan::Pro), "team" => Self::Known(KnownPlan::Team), "business" => Self::Known(KnownPlan::Business), - "enterprise" => Self::Known(KnownPlan::Enterprise), + "enterprise" | "hc" => Self::Known(KnownPlan::Enterprise), "education" | "edu" => Self::Known(KnownPlan::Edu), _ => Self::Unknown(raw.to_string()), } @@ -88,6 +88,7 @@ pub enum KnownPlan { Pro, Team, Business, + #[serde(alias = "hc")] Enterprise, Edu, } diff --git a/codex-rs/login/src/token_data_tests.rs b/codex-rs/login/src/token_data_tests.rs index 7f77ee5ea..336f1032b 100644 --- a/codex-rs/login/src/token_data_tests.rs +++ b/codex-rs/login/src/token_data_tests.rs @@ -53,6 +53,21 @@ fn id_token_info_parses_go_plan() { assert_eq!(info.get_chatgpt_plan_type().as_deref(), Some("Go")); } +#[test] +fn id_token_info_parses_hc_plan_as_enterprise() { + let fake_jwt = fake_jwt(serde_json::json!({ + "email": "user@example.com", + "https://api.openai.com/auth": { + "chatgpt_plan_type": "hc" + } + })); + + let info = parse_chatgpt_jwt_claims(&fake_jwt).expect("should parse"); + assert_eq!(info.email.as_deref(), Some("user@example.com")); + assert_eq!(info.get_chatgpt_plan_type().as_deref(), Some("Enterprise")); + assert_eq!(info.is_workspace_account(), true); +} + #[test] fn id_token_info_handles_missing_fields() { let fake_jwt = fake_jwt(serde_json::json!({ "sub": "123" }));