diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 969d914b18c..a926db9109e 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -10518,17 +10518,6 @@ } ] }, - "apps": { - "anyOf": [ - { - "$ref": "#/definitions/v2/AppsConfig" - }, - { - "type": "null" - } - ], - "default": null - }, "compact_prompt": { "type": [ "string", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json index 48af9782717..d934df7f58b 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json @@ -78,17 +78,6 @@ } ] }, - "apps": { - "anyOf": [ - { - "$ref": "#/definitions/AppsConfig" - }, - { - "type": "null" - } - ], - "default": null - }, "compact_prompt": { "type": [ "string", diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts b/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts index 00493b6e68d..aee0ac33838 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts @@ -8,11 +8,10 @@ import type { Verbosity } from "../Verbosity"; import type { WebSearchMode } from "../WebSearchMode"; import type { JsonValue } from "../serde_json/JsonValue"; import type { AnalyticsConfig } from "./AnalyticsConfig"; -import type { AppsConfig } from "./AppsConfig"; import type { AskForApproval } from "./AskForApproval"; import type { ProfileV2 } from "./ProfileV2"; import type { SandboxMode } from "./SandboxMode"; import type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite"; import type { ToolsV2 } from "./ToolsV2"; -export type Config = { model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, analytics: AnalyticsConfig | null, apps: AppsConfig | null, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type Config = {model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, analytics: AnalyticsConfig | null} & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/codex-rs/app-server-protocol/src/export.rs b/codex-rs/app-server-protocol/src/export.rs index 6de7c114ca4..5c4954b3cc0 100644 --- a/codex-rs/app-server-protocol/src/export.rs +++ b/codex-rs/app-server-protocol/src/export.rs @@ -238,7 +238,10 @@ fn filter_client_request_ts(out_dir: &Path, experimental_methods: &[&str]) -> Re .collect(); let new_body = filtered_arms.join(" | "); content = format!("{prefix}{new_body}{suffix}"); - content = prune_unused_type_imports(content, &new_body); + let import_usage_scope = split_type_alias(&content) + .map(|(_, body, _)| body) + .unwrap_or_else(|| new_body.clone()); + content = prune_unused_type_imports(content, &import_usage_scope); fs::write(&path, content).with_context(|| format!("Failed to write {}", path.display()))?; Ok(()) @@ -296,7 +299,10 @@ fn filter_experimental_fields_in_ts_file( let prefix = &content[..open_brace + 1]; let suffix = &content[close_brace..]; content = format!("{prefix}{new_inner}{suffix}"); - content = prune_unused_type_imports(content, &new_inner); + let import_usage_scope = split_type_alias(&content) + .map(|(_, body, _)| body) + .unwrap_or_else(|| new_inner.clone()); + content = prune_unused_type_imports(content, &import_usage_scope); fs::write(path, content).with_context(|| format!("Failed to write {}", path.display()))?; Ok(()) } @@ -1745,6 +1751,50 @@ mod tests { Ok(()) } + #[test] + fn experimental_type_fields_ts_filter_keeps_imports_used_in_intersection_suffix() -> Result<()> + { + let output_dir = std::env::temp_dir().join(format!("codex_ts_filter_{}", Uuid::now_v7())); + fs::create_dir_all(&output_dir)?; + + struct TempDirGuard(PathBuf); + + impl Drop for TempDirGuard { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + + let _guard = TempDirGuard(output_dir.clone()); + let path = output_dir.join("Config.ts"); + let content = r#"import type { JsonValue } from "../serde_json/JsonValue"; +import type { Keep } from "./Keep"; + +export type Config = { stableField: Keep, unstableField: string | null } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +"#; + fs::write(&path, content)?; + + static CUSTOM_FIELD: crate::experimental_api::ExperimentalField = + crate::experimental_api::ExperimentalField { + type_name: "Config", + field_name: "unstableField", + reason: "custom/unstableField", + }; + filter_experimental_type_fields_ts(&output_dir, &[&CUSTOM_FIELD])?; + + let filtered = fs::read_to_string(&path)?; + assert_eq!(filtered.contains("unstableField"), false); + assert_eq!( + filtered.contains(r#"import type { JsonValue } from "../serde_json/JsonValue";"#), + true + ); + assert_eq!( + filtered.contains(r#"import type { Keep } from "./Keep";"#), + true + ); + Ok(()) + } + #[test] fn stable_schema_filter_removes_mock_experimental_method() -> Result<()> { let output_dir = std::env::temp_dir().join(format!("codex_schema_{}", Uuid::now_v7())); diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 630e0395786..67e412ff0b9 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -404,7 +404,7 @@ const fn default_enabled() -> bool { true } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)] #[serde(rename_all = "snake_case")] #[ts(export_to = "v2/")] pub struct Config { @@ -430,6 +430,7 @@ pub struct Config { pub model_reasoning_summary: Option, pub model_verbosity: Option, pub analytics: Option, + #[experimental("config/read.apps")] #[serde(default)] pub apps: Option, #[serde(default, flatten)]