From 86982ca1f93c2e18711dd192eb2989f91f6814a1 Mon Sep 17 00:00:00 2001 From: xl-openai Date: Wed, 18 Mar 2026 15:19:29 -0700 Subject: [PATCH] Revert "fix: harden plugin feature gating" (#15102) Reverts openai/codex#15020 I messed up the commit in my PR and accidentally merged changes that were still under review. --- codex-rs/app-server-client/src/lib.rs | 4 - .../schema/json/ClientRequest.json | 1 - .../schema/json/ServerNotification.json | 13 -- .../codex_app_server_protocol.schemas.json | 14 -- .../codex_app_server_protocol.v2.schemas.json | 14 -- .../schema/json/v2/ThreadForkResponse.json | 13 -- .../schema/json/v2/ThreadListParams.json | 1 - .../schema/json/v2/ThreadListResponse.json | 13 -- .../json/v2/ThreadMetadataUpdateResponse.json | 13 -- .../schema/json/v2/ThreadReadResponse.json | 13 -- .../schema/json/v2/ThreadResumeResponse.json | 13 -- .../json/v2/ThreadRollbackResponse.json | 13 -- .../schema/json/v2/ThreadStartResponse.json | 13 -- .../json/v2/ThreadStartedNotification.json | 13 -- .../json/v2/ThreadUnarchiveResponse.json | 13 -- .../schema/typescript/SessionSource.ts | 2 +- .../schema/typescript/v2/SessionSource.ts | 2 +- .../schema/typescript/v2/ThreadSourceKind.ts | 2 +- .../app-server-protocol/src/protocol/v2.rs | 4 - codex-rs/app-server/README.md | 2 +- .../app-server/src/codex_message_processor.rs | 48 ++--- codex-rs/app-server/src/filters.rs | 41 +---- codex-rs/app-server/src/in_process.rs | 4 - codex-rs/app-server/src/lib.rs | 4 +- codex-rs/app-server/src/main.rs | 15 -- codex-rs/app-server/src/message_processor.rs | 2 +- .../app-server/tests/common/mcp_process.rs | 15 +- .../tests/suite/v2/plugin_install.rs | 127 ------------- .../app-server/tests/suite/v2/plugin_list.rs | 173 ------------------ codex-rs/cli/src/main.rs | 24 --- codex-rs/core/src/codex.rs | 20 +- codex-rs/core/src/plugins/manager.rs | 40 +--- codex-rs/core/src/plugins/manager_tests.rs | 86 +-------- codex-rs/core/src/plugins/marketplace.rs | 2 + codex-rs/core/src/skills/mod.rs | 2 - codex-rs/core/src/skills/model.rs | 46 +---- codex-rs/otel/src/events/session_telemetry.rs | 2 +- codex-rs/protocol/src/protocol.rs | 136 -------------- codex-rs/tui/src/app.rs | 2 +- .../src/codex_app_server/generated/v2_all.py | 13 +- 40 files changed, 52 insertions(+), 926 deletions(-) diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index 2b37c3901..1452eb590 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -1033,10 +1033,6 @@ mod tests { for (session_source, expected_source) in [ (SessionSource::Exec, ApiSessionSource::Exec), (SessionSource::Cli, ApiSessionSource::Cli), - ( - SessionSource::Custom("atlas".to_string()), - ApiSessionSource::Custom("atlas".to_string()), - ), ] { let client = start_test_client(session_source).await; let parsed: ThreadStartResponse = client diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index 32b42f517..a6eb901a5 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -2894,7 +2894,6 @@ "vscode", "exec", "appServer", - "custom", "subAgent", "subAgentReview", "subAgentCompact", diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 93557e9c1..8bb9f2548 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -1880,19 +1880,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { 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 b05076081..bc889ef3c 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 @@ -10984,19 +10984,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { @@ -13110,7 +13097,6 @@ "vscode", "exec", "appServer", - "custom", "subAgent", "subAgentReview", "subAgentCompact", diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 794d71999..25155d483 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -8744,19 +8744,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { @@ -10870,7 +10857,6 @@ "vscode", "exec", "appServer", - "custom", "subAgent", "subAgentReview", "subAgentCompact", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index 266741500..04765cf48 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -826,19 +826,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadListParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadListParams.json index d6a3fddfa..c5cf1364c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadListParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadListParams.json @@ -14,7 +14,6 @@ "vscode", "exec", "appServer", - "custom", "subAgent", "subAgentReview", "subAgentCompact", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json index c4410dada..936630400 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json @@ -584,19 +584,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json index 9e4fa363a..57dea225e 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json @@ -584,19 +584,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json index 762c585f6..295938ba8 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json @@ -584,19 +584,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index 556021037..774c3cade 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -826,19 +826,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json index b24218778..518f560a2 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json @@ -584,19 +584,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index 7f3f848e6..a6746e1eb 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -826,19 +826,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json index 70b995b24..a2307578d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json @@ -584,19 +584,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json index ae9ebb57d..64c00271f 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json @@ -584,19 +584,6 @@ ], "type": "string" }, - { - "additionalProperties": false, - "properties": { - "custom": { - "type": "string" - } - }, - "required": [ - "custom" - ], - "title": "CustomSessionSource", - "type": "object" - }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/typescript/SessionSource.ts b/codex-rs/app-server-protocol/schema/typescript/SessionSource.ts index a80b013b2..e5e746e38 100644 --- a/codex-rs/app-server-protocol/schema/typescript/SessionSource.ts +++ b/codex-rs/app-server-protocol/schema/typescript/SessionSource.ts @@ -3,4 +3,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { SubAgentSource } from "./SubAgentSource"; -export type SessionSource = "cli" | "vscode" | "exec" | "mcp" | { "custom": string } | { "subagent": SubAgentSource } | "unknown"; +export type SessionSource = "cli" | "vscode" | "exec" | "mcp" | { "subagent": SubAgentSource } | "unknown"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/SessionSource.ts b/codex-rs/app-server-protocol/schema/typescript/v2/SessionSource.ts index 852e6ded9..b35b421fc 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/SessionSource.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/SessionSource.ts @@ -3,4 +3,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { SubAgentSource } from "../SubAgentSource"; -export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | { "custom": string } | { "subAgent": SubAgentSource } | "unknown"; +export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | { "subAgent": SubAgentSource } | "unknown"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSourceKind.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSourceKind.ts index 6ff9160e1..0a464e3d8 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSourceKind.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSourceKind.ts @@ -2,4 +2,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type ThreadSourceKind = "cli" | "vscode" | "exec" | "appServer" | "custom" | "subAgent" | "subAgentReview" | "subAgentCompact" | "subAgentThreadSpawn" | "subAgentOther" | "unknown"; +export type ThreadSourceKind = "cli" | "vscode" | "exec" | "appServer" | "subAgent" | "subAgentReview" | "subAgentCompact" | "subAgentThreadSpawn" | "subAgentOther" | "unknown"; diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 80c17dc5b..09d891c29 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -1467,7 +1467,6 @@ pub enum SessionSource { VsCode, Exec, AppServer, - Custom(String), SubAgent(CoreSubAgentSource), #[serde(other)] Unknown, @@ -1480,7 +1479,6 @@ impl From for SessionSource { CoreSessionSource::VSCode => SessionSource::VsCode, CoreSessionSource::Exec => SessionSource::Exec, CoreSessionSource::Mcp => SessionSource::AppServer, - CoreSessionSource::Custom(source) => SessionSource::Custom(source), CoreSessionSource::SubAgent(sub) => SessionSource::SubAgent(sub), CoreSessionSource::Unknown => SessionSource::Unknown, } @@ -1494,7 +1492,6 @@ impl From for CoreSessionSource { SessionSource::VsCode => CoreSessionSource::VSCode, SessionSource::Exec => CoreSessionSource::Exec, SessionSource::AppServer => CoreSessionSource::Mcp, - SessionSource::Custom(source) => CoreSessionSource::Custom(source), SessionSource::SubAgent(sub) => CoreSessionSource::SubAgent(sub), SessionSource::Unknown => CoreSessionSource::Unknown, } @@ -2954,7 +2951,6 @@ pub enum ThreadSourceKind { VsCode, Exec, AppServer, - Custom, SubAgent, SubAgentReview, SubAgentCompact, diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 14a39cfff..0b52d2ce6 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -256,7 +256,7 @@ Experimental API: `thread/start`, `thread/resume`, and `thread/fork` accept `per - `limit` — server defaults to a reasonable page size if unset. - `sortKey` — `created_at` (default) or `updated_at`. - `modelProviders` — restrict results to specific providers; unset, null, or an empty array will include all providers. -- `sourceKinds` — restrict results to specific sources; omit or pass `[]` for interactive sessions only (`cli`, `vscode`, and custom product sources). +- `sourceKinds` — restrict results to specific sources; omit or pass `[]` for interactive sessions only (`cli`, `vscode`). - `archived` — when `true`, list archived threads only. When `false` or `null`, list non-archived threads (default). - `cwd` — restrict results to threads whose session cwd exactly matches this path. - `searchTerm` — restrict results to threads whose extracted title contains this substring (case-sensitive). diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index d370c8cbf..c70be7e8e 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -423,10 +423,7 @@ impl CodexMessageProcessor { Ok(config) => self .thread_manager .plugins_manager() - .maybe_start_curated_repo_sync_for_config( - &config, - &self.thread_manager.session_source(), - ), + .maybe_start_curated_repo_sync_for_config(&config), Err(err) => warn!("failed to load latest config for curated plugin sync: {err:?}"), } } @@ -5305,7 +5302,6 @@ impl CodexMessageProcessor { force_reload, per_cwd_extra_user_roots, } = params; - let session_source = self.thread_manager.session_source(); let cwds = if cwds.is_empty() { vec![self.config.cwd.clone()] } else { @@ -5350,12 +5346,9 @@ impl CodexMessageProcessor { let extra_roots = extra_roots_by_cwd .get(&cwd) .map_or(&[][..], std::vec::Vec::as_slice); - let outcome = codex_core::skills::filter_skill_load_outcome_for_session_source( - skills_manager - .skills_for_cwd_with_extra_user_roots(&cwd, force_reload, extra_roots) - .await, - &session_source, - ); + let outcome = skills_manager + .skills_for_cwd_with_extra_user_roots(&cwd, force_reload, extra_roots) + .await; let errors = errors_to_info(&outcome.errors); let skills = skills_to_info(&outcome.skills, &outcome.disabled_paths); data.push(codex_app_server_protocol::SkillsListEntry { @@ -5371,7 +5364,6 @@ impl CodexMessageProcessor { async fn plugin_list(&self, request_id: ConnectionRequestId, params: PluginListParams) { let plugins_manager = self.thread_manager.plugins_manager(); - let session_source = self.thread_manager.session_source(); let PluginListParams { cwds, force_remote_sync, @@ -5425,13 +5417,15 @@ impl CodexMessageProcessor { Ok::, MarketplaceError>( marketplaces .into_iter() - .filter_map(|marketplace| { - let plugins = marketplace + .map(|marketplace| PluginMarketplaceEntry { + name: marketplace.name, + path: marketplace.path, + interface: marketplace.interface.map(|interface| MarketplaceInterface { + display_name: interface.display_name, + }), + plugins: marketplace .plugins .into_iter() - .filter(|plugin| { - session_source.matches_product_restriction(&plugin.policy.products) - }) .map(|plugin| PluginSummary { id: plugin.id, installed: plugin.installed, @@ -5442,18 +5436,7 @@ impl CodexMessageProcessor { auth_policy: plugin.policy.authentication.into(), interface: plugin.interface.map(plugin_interface_to_info), }) - .collect::>(); - - (!plugins.is_empty()).then_some(PluginMarketplaceEntry { - name: marketplace.name, - path: marketplace.path, - interface: marketplace.interface.map(|interface| { - MarketplaceInterface { - display_name: interface.display_name, - } - }), - plugins, - }) + .collect(), }) .collect(), ) @@ -5528,11 +5511,6 @@ impl CodexMessageProcessor { return; } }; - let session_source = self.thread_manager.session_source(); - let plugin_skills = codex_core::skills::filter_skills_for_session_source( - outcome.plugin.skills, - &session_source, - ); let app_summaries = plugin_app_helpers::load_plugin_app_summaries(&config, &outcome.plugin.apps).await; let plugin = PluginDetail { @@ -5549,7 +5527,7 @@ impl CodexMessageProcessor { interface: outcome.plugin.interface.map(plugin_interface_to_info), }, description: outcome.plugin.description, - skills: plugin_skills_to_info(&plugin_skills), + skills: plugin_skills_to_info(&outcome.plugin.skills), apps: app_summaries, mcp_servers: outcome.plugin.mcp_server_names, }; diff --git a/codex-rs/app-server/src/filters.rs b/codex-rs/app-server/src/filters.rs index de6807ab4..a59750961 100644 --- a/codex-rs/app-server/src/filters.rs +++ b/codex-rs/app-server/src/filters.rs @@ -1,24 +1,17 @@ use codex_app_server_protocol::ThreadSourceKind; +use codex_core::INTERACTIVE_SESSION_SOURCES; use codex_protocol::protocol::SessionSource as CoreSessionSource; use codex_protocol::protocol::SubAgentSource as CoreSubAgentSource; -fn interactive_source_kinds() -> Vec { - vec![ - ThreadSourceKind::Cli, - ThreadSourceKind::VsCode, - ThreadSourceKind::Custom, - ] -} - pub(crate) fn compute_source_filters( source_kinds: Option>, ) -> (Vec, Option>) { let Some(source_kinds) = source_kinds else { - return (Vec::new(), Some(interactive_source_kinds())); + return (INTERACTIVE_SESSION_SOURCES.to_vec(), None); }; if source_kinds.is_empty() { - return (Vec::new(), Some(interactive_source_kinds())); + return (INTERACTIVE_SESSION_SOURCES.to_vec(), None); } let requires_post_filter = source_kinds.iter().any(|kind| { @@ -26,7 +19,6 @@ pub(crate) fn compute_source_filters( kind, ThreadSourceKind::Exec | ThreadSourceKind::AppServer - | ThreadSourceKind::Custom | ThreadSourceKind::SubAgent | ThreadSourceKind::SubAgentReview | ThreadSourceKind::SubAgentCompact @@ -46,7 +38,6 @@ pub(crate) fn compute_source_filters( ThreadSourceKind::VsCode => Some(CoreSessionSource::VSCode), ThreadSourceKind::Exec | ThreadSourceKind::AppServer - | ThreadSourceKind::Custom | ThreadSourceKind::SubAgent | ThreadSourceKind::SubAgentReview | ThreadSourceKind::SubAgentCompact @@ -65,7 +56,6 @@ pub(crate) fn source_kind_matches(source: &CoreSessionSource, filter: &[ThreadSo ThreadSourceKind::VsCode => matches!(source, CoreSessionSource::VSCode), ThreadSourceKind::Exec => matches!(source, CoreSessionSource::Exec), ThreadSourceKind::AppServer => matches!(source, CoreSessionSource::Mcp), - ThreadSourceKind::Custom => matches!(source, CoreSessionSource::Custom(_)), ThreadSourceKind::SubAgent => matches!(source, CoreSessionSource::SubAgent(_)), ThreadSourceKind::SubAgentReview => { matches!( @@ -102,16 +92,16 @@ mod tests { fn compute_source_filters_defaults_to_interactive_sources() { let (allowed_sources, filter) = compute_source_filters(None); - assert_eq!(allowed_sources, Vec::new()); - assert_eq!(filter, Some(interactive_source_kinds())); + assert_eq!(allowed_sources, INTERACTIVE_SESSION_SOURCES.to_vec()); + assert_eq!(filter, None); } #[test] fn compute_source_filters_empty_means_interactive_sources() { let (allowed_sources, filter) = compute_source_filters(Some(Vec::new())); - assert_eq!(allowed_sources, Vec::new()); - assert_eq!(filter, Some(interactive_source_kinds())); + assert_eq!(allowed_sources, INTERACTIVE_SESSION_SOURCES.to_vec()); + assert_eq!(filter, None); } #[test] @@ -135,15 +125,6 @@ mod tests { assert_eq!(filter, Some(source_kinds)); } - #[test] - fn compute_source_filters_custom_requires_post_filtering() { - let source_kinds = vec![ThreadSourceKind::Custom]; - let (allowed_sources, filter) = compute_source_filters(Some(source_kinds.clone())); - - assert_eq!(allowed_sources, Vec::new()); - assert_eq!(filter, Some(source_kinds)); - } - #[test] fn source_kind_matches_distinguishes_subagent_variants() { let parent_thread_id = @@ -173,12 +154,4 @@ mod tests { &[ThreadSourceKind::SubAgentReview] )); } - - #[test] - fn source_kind_matches_custom_sources() { - let custom = CoreSessionSource::Custom("atlas".to_string()); - - assert!(source_kind_matches(&custom, &[ThreadSourceKind::Custom])); - assert!(!source_kind_matches(&custom, &[ThreadSourceKind::Cli])); - } } diff --git a/codex-rs/app-server/src/in_process.rs b/codex-rs/app-server/src/in_process.rs index 1716172f2..4288d1539 100644 --- a/codex-rs/app-server/src/in_process.rs +++ b/codex-rs/app-server/src/in_process.rs @@ -808,10 +808,6 @@ mod tests { for (requested_source, expected_source) in [ (SessionSource::Cli, ApiSessionSource::Cli), (SessionSource::Exec, ApiSessionSource::Exec), - ( - SessionSource::Custom("atlas".to_string()), - ApiSessionSource::Custom("atlas".to_string()), - ), ] { let client = start_test_client(requested_source).await; let response = client diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 8b4afc23d..85804098b 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -336,7 +336,6 @@ pub async fn run_main( loader_overrides, default_analytics_enabled, AppServerTransport::Stdio, - SessionSource::VSCode, ) .await } @@ -347,7 +346,6 @@ pub async fn run_main_with_transport( loader_overrides: LoaderOverrides, default_analytics_enabled: bool, transport: AppServerTransport, - session_source: SessionSource, ) -> IoResult<()> { let (transport_event_tx, mut transport_event_rx) = mpsc::channel::(CHANNEL_CAPACITY); @@ -623,7 +621,7 @@ pub async fn run_main_with_transport( feedback: feedback.clone(), log_db, config_warnings, - session_source, + session_source: SessionSource::VSCode, enable_codex_api_key_env: false, }); let mut thread_created_rx = processor.thread_created_receiver(); diff --git a/codex-rs/app-server/src/main.rs b/codex-rs/app-server/src/main.rs index 799522d73..11380154f 100644 --- a/codex-rs/app-server/src/main.rs +++ b/codex-rs/app-server/src/main.rs @@ -4,7 +4,6 @@ use codex_app_server::run_main_with_transport; use codex_arg0::Arg0DispatchPaths; use codex_arg0::arg0_dispatch_or_else; use codex_core::config_loader::LoaderOverrides; -use codex_protocol::protocol::SessionSource; use codex_utils_cli::CliConfigOverrides; use std::path::PathBuf; @@ -22,17 +21,6 @@ struct AppServerArgs { default_value = AppServerTransport::DEFAULT_LISTEN_URL )] listen: AppServerTransport, - - /// Session source stamped into new threads started by this app-server. - /// - /// Known values such as `vscode`, `cli`, `exec`, and `mcp` map to built-in - /// sources. Any other non-empty value is recorded as a custom source. - #[arg( - long = "session-source", - value_name = "SOURCE", - default_value = "vscode" - )] - session_source: String, } fn main() -> anyhow::Result<()> { @@ -44,8 +32,6 @@ fn main() -> anyhow::Result<()> { ..Default::default() }; let transport = args.listen; - let session_source = SessionSource::from_startup_arg(args.session_source.as_str()) - .map_err(|err| anyhow::anyhow!("invalid --session-source: {err}"))?; run_main_with_transport( arg0_paths, @@ -53,7 +39,6 @@ fn main() -> anyhow::Result<()> { loader_overrides, /*default_analytics_enabled*/ false, transport, - session_source, ) .await?; Ok(()) diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 63a8180b8..f7ea2c705 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -231,7 +231,7 @@ impl MessageProcessor { // TODO(xl): Move into PluginManager once this no longer depends on config feature gating. thread_manager .plugins_manager() - .maybe_start_curated_repo_sync_for_config(&config, &thread_manager.session_source()); + .maybe_start_curated_repo_sync_for_config(&config); let cloud_requirements = Arc::new(RwLock::new(cloud_requirements)); let codex_message_processor = CodexMessageProcessor::new(CodexMessageProcessorArgs { auth_manager: auth_manager.clone(), diff --git a/codex-rs/app-server/tests/common/mcp_process.rs b/codex-rs/app-server/tests/common/mcp_process.rs index 32d0e5eba..430a400a2 100644 --- a/codex-rs/app-server/tests/common/mcp_process.rs +++ b/codex-rs/app-server/tests/common/mcp_process.rs @@ -95,11 +95,7 @@ pub const DEFAULT_CLIENT_NAME: &str = "codex-app-server-tests"; impl McpProcess { pub async fn new(codex_home: &Path) -> anyhow::Result { - Self::new_with_env_and_args(codex_home, &[], &[]).await - } - - pub async fn new_with_args(codex_home: &Path, args: &[&str]) -> anyhow::Result { - Self::new_with_env_and_args(codex_home, &[], args).await + Self::new_with_env(codex_home, &[]).await } /// Creates a new MCP process, allowing tests to override or remove @@ -110,14 +106,6 @@ impl McpProcess { pub async fn new_with_env( codex_home: &Path, env_overrides: &[(&str, Option<&str>)], - ) -> anyhow::Result { - Self::new_with_env_and_args(codex_home, env_overrides, &[]).await - } - - pub async fn new_with_env_and_args( - codex_home: &Path, - env_overrides: &[(&str, Option<&str>)], - args: &[&str], ) -> anyhow::Result { let program = codex_utils_cargo_bin::cargo_bin("codex-app-server") .context("should find binary for codex-app-server")?; @@ -130,7 +118,6 @@ impl McpProcess { cmd.env("CODEX_HOME", codex_home); cmd.env("RUST_LOG", "info"); cmd.env_remove(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR); - cmd.args(args); for (k, v) in env_overrides { match v { diff --git a/codex-rs/app-server/tests/suite/v2/plugin_install.rs b/codex-rs/app-server/tests/suite/v2/plugin_install.rs index 74baa2c30..bde356475 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_install.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_install.rs @@ -25,8 +25,6 @@ use codex_app_server_protocol::PluginAuthPolicy; use codex_app_server_protocol::PluginInstallParams; use codex_app_server_protocol::PluginInstallResponse; use codex_app_server_protocol::RequestId; -use codex_app_server_protocol::SkillsListParams; -use codex_app_server_protocol::SkillsListResponse; use codex_core::auth::AuthCredentialsStoreMode; use codex_utils_absolute_path::AbsolutePathBuf; use pretty_assertions::assert_eq; @@ -478,92 +476,6 @@ async fn plugin_install_filters_disallowed_apps_needing_auth() -> Result<()> { Ok(()) } -#[tokio::test] -async fn plugin_install_filters_product_restricted_plugin_skills() -> Result<()> { - let codex_home = TempDir::new()?; - let repo_root = TempDir::new()?; - write_plugins_enabled_config(codex_home.path())?; - write_plugin_marketplace( - repo_root.path(), - "debug", - "sample-plugin", - "./sample-plugin", - None, - None, - )?; - write_plugin_source(repo_root.path(), "sample-plugin", &[])?; - - let plugin_root = repo_root.path().join("sample-plugin"); - write_plugin_skill( - &plugin_root, - "all-products", - "Visible to every product", - &[], - )?; - write_plugin_skill( - &plugin_root, - "chatgpt-only", - "Visible to ChatGPT", - &["CHATGPT"], - )?; - write_plugin_skill(&plugin_root, "atlas-only", "Visible to Atlas", &["ATLAS"])?; - let marketplace_path = - AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; - - let mut mcp = - McpProcess::new_with_args(codex_home.path(), &["--session-source", "chatgpt"]).await?; - timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; - - let request_id = mcp - .send_plugin_install_request(PluginInstallParams { - marketplace_path, - plugin_name: "sample-plugin".to_string(), - force_remote_sync: false, - }) - .await?; - - let response: JSONRPCResponse = timeout( - DEFAULT_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(request_id)), - ) - .await??; - let response: PluginInstallResponse = to_response(response)?; - assert_eq!(response.apps_needing_auth, Vec::::new()); - - let request_id = mcp - .send_skills_list_request(SkillsListParams { - cwds: vec![codex_home.path().to_path_buf()], - force_reload: true, - per_cwd_extra_user_roots: None, - }) - .await?; - - let response: JSONRPCResponse = timeout( - DEFAULT_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(request_id)), - ) - .await??; - let response: SkillsListResponse = to_response(response)?; - - let mut skills = response - .data - .into_iter() - .flat_map(|entry| entry.skills.into_iter()) - .map(|skill| skill.name) - .filter(|name| name.starts_with("sample-plugin:")) - .collect::>(); - skills.sort_unstable(); - - assert_eq!( - skills, - vec![ - "sample-plugin:all-products".to_string(), - "sample-plugin:chatgpt-only".to_string(), - ] - ); - Ok(()) -} - #[derive(Clone)] struct AppsServerState { response: Arc>, @@ -735,16 +647,6 @@ plugins = true ) } -fn write_plugins_enabled_config(codex_home: &std::path::Path) -> std::io::Result<()> { - std::fs::write( - codex_home.join("config.toml"), - r#" -[features] -plugins = true -"#, - ) -} - fn write_plugin_marketplace( repo_root: &std::path::Path, marketplace_name: &str, @@ -814,32 +716,3 @@ fn write_plugin_source( )?; Ok(()) } - -fn write_plugin_skill( - plugin_root: &std::path::Path, - skill_name: &str, - description: &str, - products: &[&str], -) -> Result<()> { - let skill_dir = plugin_root.join("skills").join(skill_name); - std::fs::create_dir_all(&skill_dir)?; - std::fs::write( - skill_dir.join("SKILL.md"), - format!("---\ndescription: {description}\n---\n\n# {skill_name}\n"), - )?; - - if !products.is_empty() { - let products = products - .iter() - .map(|product| format!(" - {product}")) - .collect::>() - .join("\n"); - std::fs::create_dir_all(skill_dir.join("agents"))?; - std::fs::write( - skill_dir.join("agents/openai.yaml"), - format!("policy:\n products:\n{products}\n"), - )?; - } - - Ok(()) -} diff --git a/codex-rs/app-server/tests/suite/v2/plugin_list.rs b/codex-rs/app-server/tests/suite/v2/plugin_list.rs index 278cf00ab..c409fdeb3 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_list.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_list.rs @@ -377,179 +377,6 @@ enabled = false Ok(()) } -#[tokio::test] -async fn plugin_list_filters_plugins_for_custom_session_source_products() -> Result<()> { - let codex_home = TempDir::new()?; - let repo_root = TempDir::new()?; - std::fs::create_dir_all(repo_root.path().join(".git"))?; - std::fs::create_dir_all(repo_root.path().join(".agents/plugins"))?; - std::fs::write( - repo_root.path().join(".agents/plugins/marketplace.json"), - r#"{ - "name": "codex-curated", - "plugins": [ - { - "name": "all-products", - "source": { - "source": "local", - "path": "./all-products" - } - }, - { - "name": "chatgpt-only", - "source": { - "source": "local", - "path": "./chatgpt-only" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL", - "products": ["CHATGPT"] - } - }, - { - "name": "atlas-only", - "source": { - "source": "local", - "path": "./atlas-only" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL", - "products": ["ATLAS"] - } - }, - { - "name": "codex-only", - "source": { - "source": "local", - "path": "./codex-only" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL", - "products": ["CODEX"] - } - } - ] -}"#, - )?; - - let mut mcp = - McpProcess::new_with_args(codex_home.path(), &["--session-source", "chatgpt"]).await?; - timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; - - let request_id = mcp - .send_plugin_list_request(PluginListParams { - cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]), - force_remote_sync: false, - }) - .await?; - - let response: JSONRPCResponse = timeout( - DEFAULT_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(request_id)), - ) - .await??; - let response: PluginListResponse = to_response(response)?; - - let marketplace = response - .marketplaces - .into_iter() - .find(|marketplace| marketplace.name == "codex-curated") - .expect("expected marketplace entry"); - - assert_eq!( - marketplace - .plugins - .into_iter() - .map(|plugin| plugin.name) - .collect::>(), - vec!["all-products".to_string(), "chatgpt-only".to_string()] - ); - Ok(()) -} - -#[tokio::test] -async fn plugin_list_defaults_non_custom_session_source_to_codex_products() -> Result<()> { - let codex_home = TempDir::new()?; - let repo_root = TempDir::new()?; - std::fs::create_dir_all(repo_root.path().join(".git"))?; - std::fs::create_dir_all(repo_root.path().join(".agents/plugins"))?; - std::fs::write( - repo_root.path().join(".agents/plugins/marketplace.json"), - r#"{ - "name": "codex-curated", - "plugins": [ - { - "name": "all-products", - "source": { - "source": "local", - "path": "./all-products" - } - }, - { - "name": "chatgpt-only", - "source": { - "source": "local", - "path": "./chatgpt-only" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL", - "products": ["CHATGPT"] - } - }, - { - "name": "codex-only", - "source": { - "source": "local", - "path": "./codex-only" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL", - "products": ["CODEX"] - } - } - ] -}"#, - )?; - - let mut mcp = McpProcess::new(codex_home.path()).await?; - timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; - - let request_id = mcp - .send_plugin_list_request(PluginListParams { - cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]), - force_remote_sync: false, - }) - .await?; - - let response: JSONRPCResponse = timeout( - DEFAULT_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(request_id)), - ) - .await??; - let response: PluginListResponse = to_response(response)?; - - let marketplace = response - .marketplaces - .into_iter() - .find(|marketplace| marketplace.name == "codex-curated") - .expect("expected marketplace entry"); - - assert_eq!( - marketplace - .plugins - .into_iter() - .map(|plugin| plugin.name) - .collect::>(), - vec!["all-products".to_string(), "codex-only".to_string()] - ); - Ok(()) -} - #[tokio::test] async fn plugin_list_returns_plugin_interface_with_absolute_asset_paths() -> Result<()> { let codex_home = TempDir::new()?; diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 698035fea..05b568b7b 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -331,17 +331,6 @@ struct AppServerCommand { )] listen: codex_app_server::AppServerTransport, - /// Session source stamped into new threads started by this app-server. - /// - /// Known values such as `vscode`, `cli`, `exec`, and `mcp` map to built-in - /// sources. Any other non-empty value is recorded as a custom source. - #[arg( - long = "session-source", - value_name = "SOURCE", - default_value = "vscode" - )] - session_source: String, - /// Controls whether analytics are enabled by default. /// /// Analytics are disabled by default for app-server. Users have to explicitly opt in @@ -654,17 +643,12 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> { None => { reject_remote_mode_for_subcommand(root_remote.as_deref(), "app-server")?; let transport = app_server_cli.listen; - let session_source = codex_protocol::protocol::SessionSource::from_startup_arg( - app_server_cli.session_source.as_str(), - ) - .map_err(|err| anyhow::anyhow!("invalid --session-source: {err}"))?; codex_app_server::run_main_with_transport( arg0_paths.clone(), root_config_overrides, codex_core::config_loader::LoaderOverrides::default(), app_server_cli.analytics_default_enabled, transport, - session_source, ) .await?; } @@ -1631,7 +1615,6 @@ mod tests { app_server.listen, codex_app_server::AppServerTransport::Stdio ); - assert_eq!(app_server.session_source, "vscode"); } #[test] @@ -1641,13 +1624,6 @@ mod tests { assert!(app_server.analytics_default_enabled); } - #[test] - fn app_server_session_source_accepts_custom_value() { - let app_server = - app_server_from_args(["codex", "app-server", "--session-source", "atlas"].as_ref()); - assert_eq!(app_server.session_source, "atlas"); - } - #[test] fn remote_flag_parses_for_interactive_root() { let cli = MultitoolCli::try_parse_from(["codex", "--remote", "ws://127.0.0.1:4500"]) diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 7b947249b..efde7d848 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -2385,12 +2385,11 @@ impl Session { &per_turn_config, ) .await; - let skills_outcome = Arc::new(crate::skills::filter_skill_load_outcome_for_session_source( + let skills_outcome = Arc::new( self.services .skills_manager .skills_for_config(&per_turn_config), - &session_configuration.session_source, - )); + ); let mut turn_context: TurnContext = Self::make_turn_context( Some(Arc::clone(&self.services.auth_manager)), &self.services.session_telemetry, @@ -4774,24 +4773,17 @@ mod handlers { cwds: Vec, force_reload: bool, ) { - let (cwds, session_source) = if cwds.is_empty() { + let cwds = if cwds.is_empty() { let state = sess.state.lock().await; - ( - vec![state.session_configuration.cwd.clone()], - state.session_configuration.session_source.clone(), - ) + vec![state.session_configuration.cwd.clone()] } else { - let state = sess.state.lock().await; - (cwds, state.session_configuration.session_source.clone()) + cwds }; let skills_manager = &sess.services.skills_manager; let mut skills = Vec::new(); for cwd in cwds { - let outcome = crate::skills::filter_skill_load_outcome_for_session_source( - skills_manager.skills_for_cwd(&cwd, force_reload).await, - &session_source, - ); + let outcome = skills_manager.skills_for_cwd(&cwd, force_reload).await; let errors = super::errors_to_info(&outcome.errors); let skills_metadata = super::skills_to_info(&outcome.skills, &outcome.disabled_paths); skills.push(SkillsListEntry { diff --git a/codex-rs/core/src/plugins/manager.rs b/codex-rs/core/src/plugins/manager.rs index 7344e588f..22c536f21 100644 --- a/codex-rs/core/src/plugins/manager.rs +++ b/codex-rs/core/src/plugins/manager.rs @@ -44,7 +44,6 @@ use crate::skills::loader::SkillRoot; use crate::skills::loader::load_skills_from_roots; use codex_app_server_protocol::ConfigValueWriteParams; use codex_app_server_protocol::MergeStrategy; -use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::SkillScope; use codex_utils_absolute_path::AbsolutePathBuf; use serde::Deserialize; @@ -939,11 +938,7 @@ impl PluginsManager { }) } - pub fn maybe_start_curated_repo_sync_for_config( - self: &Arc, - config: &Config, - session_source: &SessionSource, - ) { + pub fn maybe_start_curated_repo_sync_for_config(self: &Arc, config: &Config) { if plugins_feature_enabled_from_stack(&config.config_layer_stack) { let mut configured_curated_plugin_ids = configured_plugins_from_stack(&config.config_layer_stack) @@ -966,15 +961,11 @@ impl PluginsManager { }) .collect::>(); configured_curated_plugin_ids.sort_unstable_by_key(super::store::PluginId::as_key); - self.start_curated_repo_sync(configured_curated_plugin_ids, session_source.clone()); + self.start_curated_repo_sync(configured_curated_plugin_ids); } } - fn start_curated_repo_sync( - self: &Arc, - configured_curated_plugin_ids: Vec, - session_source: SessionSource, - ) { + fn start_curated_repo_sync(self: &Arc, configured_curated_plugin_ids: Vec) { if CURATED_REPO_SYNC_STARTED.swap(true, Ordering::SeqCst) { return; } @@ -989,7 +980,6 @@ impl PluginsManager { codex_home.as_path(), &curated_plugin_version, &configured_curated_plugin_ids, - &session_source, ) { Ok(cache_refreshed) => { if cache_refreshed { @@ -1215,7 +1205,6 @@ fn refresh_curated_plugin_cache( codex_home: &Path, plugin_version: &str, configured_curated_plugin_ids: &[PluginId], - session_source: &SessionSource, ) -> Result { let store = PluginStore::new(codex_home.to_path_buf()); let curated_marketplace_path = AbsolutePathBuf::try_from( @@ -1226,12 +1215,9 @@ fn refresh_curated_plugin_cache( .map_err(|err| format!("failed to load curated marketplace for cache refresh: {err}"))?; let mut plugin_sources = HashMap::::new(); - let mut product_restricted_plugin_names = HashSet::::new(); for plugin in curated_marketplace.plugins { let plugin_name = plugin.name; - if plugin_sources.contains_key(&plugin_name) - || product_restricted_plugin_names.contains(&plugin_name) - { + if plugin_sources.contains_key(&plugin_name) { warn!( plugin = plugin_name, marketplace = OPENAI_CURATED_MARKETPLACE_NAME, @@ -1242,32 +1228,16 @@ fn refresh_curated_plugin_cache( let source_path = match plugin.source { MarketplacePluginSource::Local { path } => path, }; - if session_source.matches_product_restriction(&plugin.policy.products) { - plugin_sources.insert(plugin_name, source_path); - } else { - product_restricted_plugin_names.insert(plugin_name); - } + plugin_sources.insert(plugin_name, source_path); } let mut cache_refreshed = false; for plugin_id in configured_curated_plugin_ids { - // Curated plugin cache entries are intentionally sticky across session source changes. - // Product restrictions gate refresh for this source, but do not retroactively evict an - // already-active cache entry from a shared CODEX_HOME. if store.active_plugin_version(plugin_id).as_deref() == Some(plugin_version) { continue; } let Some(source_path) = plugin_sources.get(&plugin_id.plugin_name).cloned() else { - if product_restricted_plugin_names.contains(&plugin_id.plugin_name) { - info!( - plugin = plugin_id.plugin_name, - marketplace = OPENAI_CURATED_MARKETPLACE_NAME, - session_source = %session_source, - "skipping curated plugin cache refresh for product-restricted plugin" - ); - continue; - } warn!( plugin = plugin_id.plugin_name, marketplace = OPENAI_CURATED_MARKETPLACE_NAME, diff --git a/codex-rs/core/src/plugins/manager_tests.rs b/codex-rs/core/src/plugins/manager_tests.rs index 17b7fd3e7..d113d56a9 100644 --- a/codex-rs/core/src/plugins/manager_tests.rs +++ b/codex-rs/core/src/plugins/manager_tests.rs @@ -9,12 +9,10 @@ use crate::config_loader::ConfigRequirements; use crate::config_loader::ConfigRequirementsToml; use crate::plugins::MarketplacePluginInstallPolicy; use crate::plugins::test_support::TEST_CURATED_PLUGIN_SHA; -use crate::plugins::test_support::write_curated_plugin; use crate::plugins::test_support::write_curated_plugin_sha_with as write_curated_plugin_sha; use crate::plugins::test_support::write_file; use crate::plugins::test_support::write_openai_curated_marketplace; use codex_app_server_protocol::ConfigLayerSource; -use codex_protocol::protocol::SessionSource; use pretty_assertions::assert_eq; use std::fs; use tempfile::TempDir; @@ -1034,12 +1032,6 @@ async fn list_marketplaces_includes_curated_repo_marketplace() { r#"{"name":"linear"}"#, ) .unwrap(); - write_file( - &tmp.path().join(CONFIG_TOML_FILE), - r#"[features] -plugins = true -"#, - ); let config = load_config(tmp.path(), tmp.path()).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) @@ -1635,13 +1627,8 @@ fn refresh_curated_plugin_cache_replaces_existing_local_version_with_sha() { ); assert!( - refresh_curated_plugin_cache( - tmp.path(), - TEST_CURATED_PLUGIN_SHA, - &[plugin_id], - &SessionSource::Cli, - ) - .expect("cache refresh should succeed") + refresh_curated_plugin_cache(tmp.path(), TEST_CURATED_PLUGIN_SHA, &[plugin_id]) + .expect("cache refresh should succeed") ); assert!( @@ -1671,13 +1658,8 @@ fn refresh_curated_plugin_cache_reinstalls_missing_configured_plugin_with_curren .unwrap(); assert!( - refresh_curated_plugin_cache( - tmp.path(), - TEST_CURATED_PLUGIN_SHA, - &[plugin_id], - &SessionSource::Cli, - ) - .expect("cache refresh should recreate missing configured plugin") + refresh_curated_plugin_cache(tmp.path(), TEST_CURATED_PLUGIN_SHA, &[plugin_id]) + .expect("cache refresh should recreate missing configured plugin") ); assert!( @@ -1706,64 +1688,8 @@ fn refresh_curated_plugin_cache_returns_false_when_configured_plugins_are_curren ); assert!( - !refresh_curated_plugin_cache( - tmp.path(), - TEST_CURATED_PLUGIN_SHA, - &[plugin_id], - &SessionSource::Cli, - ) - .expect("cache refresh should be a no-op when configured plugins are current") - ); -} - -#[test] -fn refresh_curated_plugin_cache_skips_product_restricted_plugins_for_session_source() { - let tmp = tempfile::tempdir().unwrap(); - let curated_root = curated_plugins_repo_path(tmp.path()); - write_file( - &curated_root.join(".agents/plugins/marketplace.json"), - &format!( - r#"{{ - "name": "{OPENAI_CURATED_MARKETPLACE_NAME}", - "plugins": [ - {{ - "name": "chatgpt-plugin", - "source": {{ - "source": "local", - "path": "./plugins/chatgpt-plugin" - }}, - "policy": {{ - "products": ["CHATGPT"] - }} - }} - ] -}}"# - ), - ); - write_curated_plugin(&curated_root, "chatgpt-plugin"); - write_curated_plugin_sha(tmp.path(), TEST_CURATED_PLUGIN_SHA); - let plugin_id = PluginId::new( - "chatgpt-plugin".to_string(), - OPENAI_CURATED_MARKETPLACE_NAME.to_string(), - ) - .unwrap(); - - assert!( - !refresh_curated_plugin_cache( - tmp.path(), - TEST_CURATED_PLUGIN_SHA, - &[plugin_id], - &SessionSource::Cli, - ) - .expect("cache refresh should skip disallowed product plugin") - ); - - assert!( - !tmp.path() - .join(format!( - "plugins/cache/openai-curated/chatgpt-plugin/{TEST_CURATED_PLUGIN_SHA}" - )) - .exists() + !refresh_curated_plugin_cache(tmp.path(), TEST_CURATED_PLUGIN_SHA, &[plugin_id]) + .expect("cache refresh should be a no-op when configured plugins are current") ); } diff --git a/codex-rs/core/src/plugins/marketplace.rs b/codex-rs/core/src/plugins/marketplace.rs index e7e0d2e4d..ff6bdecc8 100644 --- a/codex-rs/core/src/plugins/marketplace.rs +++ b/codex-rs/core/src/plugins/marketplace.rs @@ -54,6 +54,8 @@ pub enum MarketplacePluginSource { pub struct MarketplacePluginPolicy { pub installation: MarketplacePluginInstallPolicy, pub authentication: MarketplacePluginAuthPolicy, + // TODO: Surface or enforce product gating at the Codex/plugin consumer boundary instead of + // only carrying it through core marketplace metadata. pub products: Vec, } diff --git a/codex-rs/core/src/skills/mod.rs b/codex-rs/core/src/skills/mod.rs index 2a3975141..8c311c5d3 100644 --- a/codex-rs/core/src/skills/mod.rs +++ b/codex-rs/core/src/skills/mod.rs @@ -20,6 +20,4 @@ pub use model::SkillError; pub use model::SkillLoadOutcome; pub use model::SkillMetadata; pub use model::SkillPolicy; -pub use model::filter_skill_load_outcome_for_session_source; -pub use model::filter_skills_for_session_source; pub use render::render_skills_section; diff --git a/codex-rs/core/src/skills/model.rs b/codex-rs/core/src/skills/model.rs index f4fa1515a..0949300ec 100644 --- a/codex-rs/core/src/skills/model.rs +++ b/codex-rs/core/src/skills/model.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use codex_protocol::models::PermissionProfile; use codex_protocol::protocol::Product; -use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::SkillScope; use serde::Deserialize; @@ -43,18 +42,13 @@ impl SkillMetadata { .and_then(|policy| policy.allow_implicit_invocation) .unwrap_or(true) } - - pub fn matches_product_restriction(&self, session_source: &SessionSource) -> bool { - match &self.policy { - Some(policy) => session_source.matches_product_restriction(&policy.products), - None => true, - } - } } #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct SkillPolicy { pub allow_implicit_invocation: Option, + // TODO: Enforce product gating in Codex skill selection/injection instead of only parsing and + // storing this metadata. pub products: Vec, } @@ -121,39 +115,3 @@ impl SkillLoadOutcome { .map(|skill| (skill, self.is_skill_enabled(skill))) } } - -pub fn filter_skill_load_outcome_for_session_source( - mut outcome: SkillLoadOutcome, - session_source: &SessionSource, -) -> SkillLoadOutcome { - outcome - .skills - .retain(|skill| skill.matches_product_restriction(session_source)); - outcome.implicit_skills_by_scripts_dir = Arc::new( - outcome - .implicit_skills_by_scripts_dir - .iter() - .filter(|(_, skill)| skill.matches_product_restriction(session_source)) - .map(|(path, skill)| (path.clone(), skill.clone())) - .collect(), - ); - outcome.implicit_skills_by_doc_path = Arc::new( - outcome - .implicit_skills_by_doc_path - .iter() - .filter(|(_, skill)| skill.matches_product_restriction(session_source)) - .map(|(path, skill)| (path.clone(), skill.clone())) - .collect(), - ); - outcome -} - -pub fn filter_skills_for_session_source( - skills: Vec, - session_source: &SessionSource, -) -> Vec { - skills - .into_iter() - .filter(|skill| skill.matches_product_restriction(session_source)) - .collect() -} diff --git a/codex-rs/otel/src/events/session_telemetry.rs b/codex-rs/otel/src/events/session_telemetry.rs index 633f68b6a..e2c86a6e6 100644 --- a/codex-rs/otel/src/events/session_telemetry.rs +++ b/codex-rs/otel/src/events/session_telemetry.rs @@ -276,7 +276,7 @@ impl SessionTelemetry { account_email, originator: sanitize_metric_tag_value(originator.as_str()), service_name: None, - session_source: sanitize_metric_tag_value(session_source.to_string().as_str()), + session_source: session_source.to_string(), model: model.to_owned(), slug: slug.to_owned(), log_user_prompts, diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 3965e2872..e5277c16b 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -2272,7 +2272,6 @@ pub enum SessionSource { VSCode, Exec, Mcp, - Custom(String), SubAgent(SubAgentSource), #[serde(other)] Unknown, @@ -2303,7 +2302,6 @@ impl fmt::Display for SessionSource { SessionSource::VSCode => f.write_str("vscode"), SessionSource::Exec => f.write_str("exec"), SessionSource::Mcp => f.write_str("mcp"), - SessionSource::Custom(source) => f.write_str(source), SessionSource::SubAgent(sub_source) => write!(f, "subagent_{sub_source}"), SessionSource::Unknown => f.write_str("unknown"), } @@ -2311,23 +2309,6 @@ impl fmt::Display for SessionSource { } impl SessionSource { - pub fn from_startup_arg(value: &str) -> Result { - let trimmed = value.trim(); - if trimmed.is_empty() { - return Err("session source must not be empty"); - } - - let normalized = trimmed.to_ascii_lowercase(); - Ok(match normalized.as_str() { - "cli" => SessionSource::Cli, - "vscode" => SessionSource::VSCode, - "exec" => SessionSource::Exec, - "mcp" | "appserver" | "app-server" | "app_server" => SessionSource::Mcp, - "unknown" => SessionSource::Unknown, - _ => SessionSource::Custom(trimmed.to_string()), - }) - } - pub fn get_nickname(&self) -> Option { match self { SessionSource::SubAgent(SubAgentSource::ThreadSpawn { agent_nickname, .. }) => { @@ -2351,25 +2332,6 @@ impl SessionSource { _ => None, } } - - pub fn restriction_product(&self) -> Option { - match self { - SessionSource::Custom(source) => Product::from_session_source_name(source), - SessionSource::Cli - | SessionSource::VSCode - | SessionSource::Exec - | SessionSource::Mcp - | SessionSource::SubAgent(_) - | SessionSource::Unknown => Some(Product::Codex), - } - } - - pub fn matches_product_restriction(&self, products: &[Product]) -> bool { - products.is_empty() - || self - .restriction_product() - .is_some_and(|product| products.contains(&product)) - } } impl fmt::Display for SubAgentSource { @@ -2961,18 +2923,6 @@ pub enum Product { #[serde(alias = "ATLAS")] Atlas, } - -impl Product { - pub fn from_session_source_name(value: &str) -> Option { - let normalized = value.trim().to_ascii_lowercase(); - match normalized.as_str() { - "chatgpt" => Some(Self::Chatgpt), - "codex" => Some(Self::Codex), - "atlas" => Some(Self::Atlas), - _ => None, - } - } -} #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)] #[serde(rename_all = "snake_case")] #[ts(rename_all = "snake_case")] @@ -3473,92 +3423,6 @@ mod tests { .any(|root| root.is_path_writable(path)) } - #[test] - fn session_source_from_startup_arg_maps_known_values() { - assert_eq!( - SessionSource::from_startup_arg("vscode").unwrap(), - SessionSource::VSCode - ); - assert_eq!( - SessionSource::from_startup_arg("app-server").unwrap(), - SessionSource::Mcp - ); - } - - #[test] - fn session_source_from_startup_arg_preserves_custom_values() { - assert_eq!( - SessionSource::from_startup_arg("atlas").unwrap(), - SessionSource::Custom("atlas".to_string()) - ); - } - - #[test] - fn session_source_restriction_product_defaults_non_custom_sources_to_codex() { - assert_eq!( - SessionSource::Cli.restriction_product(), - Some(Product::Codex) - ); - assert_eq!( - SessionSource::VSCode.restriction_product(), - Some(Product::Codex) - ); - assert_eq!( - SessionSource::Exec.restriction_product(), - Some(Product::Codex) - ); - assert_eq!( - SessionSource::Mcp.restriction_product(), - Some(Product::Codex) - ); - assert_eq!( - SessionSource::SubAgent(SubAgentSource::Review).restriction_product(), - Some(Product::Codex) - ); - assert_eq!( - SessionSource::Unknown.restriction_product(), - Some(Product::Codex) - ); - } - - #[test] - fn session_source_restriction_product_maps_custom_sources_to_products() { - assert_eq!( - SessionSource::Custom("chatgpt".to_string()).restriction_product(), - Some(Product::Chatgpt) - ); - assert_eq!( - SessionSource::Custom("ATLAS".to_string()).restriction_product(), - Some(Product::Atlas) - ); - assert_eq!( - SessionSource::Custom("codex".to_string()).restriction_product(), - Some(Product::Codex) - ); - assert_eq!( - SessionSource::Custom("atlas-dev".to_string()).restriction_product(), - None - ); - } - - #[test] - fn session_source_matches_product_restriction() { - assert!( - SessionSource::Custom("chatgpt".to_string()) - .matches_product_restriction(&[Product::Chatgpt]) - ); - assert!( - !SessionSource::Custom("chatgpt".to_string()) - .matches_product_restriction(&[Product::Codex]) - ); - assert!(SessionSource::VSCode.matches_product_restriction(&[Product::Codex])); - assert!( - !SessionSource::Custom("atlas-dev".to_string()) - .matches_product_restriction(&[Product::Atlas]) - ); - assert!(SessionSource::Custom("atlas-dev".to_string()).matches_product_restriction(&[])); - } - fn sandbox_policy_probe_paths(policy: &SandboxPolicy, cwd: &Path) -> Vec { let mut paths = vec![cwd.to_path_buf()]; paths.extend( diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 6cfcf8f1c..8db2a940e 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -2018,7 +2018,7 @@ impl App { // TODO(xl): Move into PluginManager once this no longer depends on config feature gating. thread_manager .plugins_manager() - .maybe_start_curated_repo_sync_for_config(&config, &SessionSource::Cli); + .maybe_start_curated_repo_sync_for_config(&config); let mut model = thread_manager .get_models_manager() .get_default_model(&config.model, RefreshStrategy::Offline) diff --git a/sdk/python/src/codex_app_server/generated/v2_all.py b/sdk/python/src/codex_app_server/generated/v2_all.py index d1b02ccc9..0ff2c5897 100644 --- a/sdk/python/src/codex_app_server/generated/v2_all.py +++ b/sdk/python/src/codex_app_server/generated/v2_all.py @@ -3131,7 +3131,6 @@ class ThreadSourceKind(Enum): vscode = "vscode" exec = "exec" app_server = "appServer" - custom = "custom" sub_agent = "subAgent" sub_agent_review = "subAgentReview" sub_agent_compact = "subAgentCompact" @@ -5811,19 +5810,11 @@ class SubAgentSessionSource(BaseModel): sub_agent: Annotated[SubAgentSource, Field(alias="subAgent")] -class CustomSessionSource(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - custom: str - - -class SessionSource(RootModel[SessionSourceValue | CustomSessionSource | SubAgentSessionSource]): +class SessionSource(RootModel[SessionSourceValue | SubAgentSessionSource]): model_config = ConfigDict( populate_by_name=True, ) - root: SessionSourceValue | CustomSessionSource | SubAgentSessionSource + root: SessionSourceValue | SubAgentSessionSource class Thread(BaseModel):