diff --git a/artifacts/github/bundles/openai-codex-pr-26835.json b/artifacts/github/bundles/openai-codex-pr-26835.json new file mode 100644 index 000000000..b469a625d --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-26835.json @@ -0,0 +1,94 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "anp-oai", + "committed_at": "2026-06-06T22:56:17Z", + "message": "test extension API contracts", + "sha": "4f1183c281d837c81ee3f13f4b9207fc4bbd7115", + "url": "https://github.com/openai/codex/commit/4f1183c281d837c81ee3f13f4b9207fc4bbd7115" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T15:34:12Z", + "message": "codex: address PR review feedback (#26835)", + "sha": "4678bad085f9dac6876e0466c5f922635926d7db", + "url": "https://github.com/openai/codex/commit/4678bad085f9dac6876e0466c5f922635926d7db" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T15:54:06Z", + "message": "test: explain forced initializer overlap", + "sha": "583f8cca1bd6d6a4ea2762ad6ec1e39d646a8d51", + "url": "https://github.com/openai/codex/commit/583f8cca1bd6d6a4ea2762ad6ec1e39d646a8d51" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-09T18:22:46Z", + "message": "codex: address PR review feedback (#26835)", + "sha": "3a804efa84757c84da11729a04e04995d5c04c8c", + "url": "https://github.com/openai/codex/commit/3a804efa84757c84da11729a04e04995d5c04c8c" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "API", + "CALLER_COUNT" + ], + "files": [ + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -2923,6 +2923,8 @@ dependencies = [\n \"codex-context-fragments\",\n \"codex-protocol\",\n \"codex-tools\",\n+ \"pretty_assertions\",\n+ \"tokio\",\n ]\n \n [[package]]", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 4, + "deletions": 0, + "patch_excerpt": "@@ -18,3 +18,7 @@ async-trait = { workspace = true }\n codex-context-fragments = { workspace = true }\n codex-protocol = { workspace = true }\n codex-tools = { workspace = true }\n+\n+[dev-dependencies]\n+pretty_assertions = { workspace = true }\n+tokio = { workspace = true, features = [\"macros\", \"rt-multi-thread\"] }", + "path": "codex-rs/ext/extension-api/Cargo.toml", + "status": "modified" + }, + { + "additions": 56, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,56 @@\n+use std::sync::Arc;\n+use std::sync::Mutex;\n+\n+use codex_extension_api::AgentSpawnFuture;\n+use codex_extension_api::AgentSpawner;\n+use codex_extension_api::NoopResponseItemInjector;\n+use codex_extension_api::ResponseItemInjector;\n+use codex_protocol::ThreadId;\n+use codex_protocol::models::ContentItem;\n+use codex_protocol::models::ResponseInputItem;\n+use pretty_assertions::assert_eq;\n+\n+#[tokio::test]\n+async fn noop_response_item_injector_returns_original_items() {\n+ let items = vec![ResponseInputItem::Message {\n+ role: \"user\".to_string(),\n+ content: vec![ContentItem::InputText {\n+ text: \"keep this input\".to_string(),\n+ }],\n+ phase: None,\n+ }];\n+\n+ let returned_items = NoopResponseItemInjector\n+ .inject_response_items(items.clone())\n+ .await\n+ .expect_err(\"noop injector should reject same-turn injection\"...", + "path": "codex-rs/ext/extension-api/tests/capabilities.rs", + "status": "added" + }, + { + "additions": 370, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,370 @@\n+use std::future::Future;\n+use std::pin::Pin;\n+use std::sync::Arc;\n+use std::sync::Mutex;\n+\n+use codex_extension_api::ApprovalReviewContributor;\n+use codex_extension_api::ConfigContributor;\n+use codex_extension_api::ContextContributor;\n+use codex_extension_api::ContextualUserFragment;\n+use codex_extension_api::ExtensionData;\n+use codex_extension_api::ExtensionEventSink;\n+use codex_extension_api::ExtensionRegistryBuilder;\n+use codex_extension_api::PromptFragment;\n+use codex_extension_api::ThreadLifecycleContributor;\n+use codex_extension_api::TokenUsageContributor;\n+use codex_extension_api::ToolCall;\n+use codex_extension_api::ToolContributor;\n+use codex_extension_api::ToolExecutor;\n+use codex_extension_api::ToolLifecycleContributor;\n+use codex_extension_api::TurnInputContext;\n+use codex_extension_api::TurnInputContributor;\n+use codex_extension_api::TurnItemContributor;\n+u...", + "path": "codex-rs/ext/extension-api/tests/registry.rs", + "status": "added" + }, + { + "additions": 109, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,109 @@\n+use std::panic::AssertUnwindSafe;\n+use std::sync::Arc;\n+use std::sync::atomic::AtomicUsize;\n+use std::sync::atomic::Ordering;\n+\n+use codex_extension_api::ExtensionData;\n+use pretty_assertions::assert_eq;\n+\n+#[test]\n+fn typed_values_can_be_inserted_replaced_and_removed() {\n+ let data = ExtensionData::new(\"thread-1\");\n+\n+ assert_eq!(data.insert(/*value*/ 41_u64), None);\n+ assert_eq!(data.insert(\"alpha\".to_string()), None);\n+ assert_eq!(data.get::().as_deref(), Some(&41));\n+ assert_eq!(\n+ data.get::().map(|value| value.as_str().to_string()),\n+ Some(\"alpha\".to_string())\n+ );\n+\n+ assert_eq!(data.insert(/*value*/ 42_u64).as_deref(), Some(&41));\n+ assert_eq!(data.get::().as_deref(), Some(&42));\n+ assert_eq!(\n+ data.remove::()\n+ .map(|value| value.as_str().to_string()),\n+ Some(\"alpha\".t...", + "path": "codex-rs/ext/extension-api/tests/state.rs", + "status": "added" + } + ], + "linked_issues": [ + "#26835" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\n`codex-extension-api` defines contracts shared by extension crates and their hosts, but it had no direct test suite. Host and feature tests cover downstream behavior, while regressions in the API crate's own typed state, registry ordering, and capability adapters could go unnoticed.\n\n## What\n\n- Add public-surface integration tests for `ExtensionData`, including concurrent initialization and poison recovery.\n- Cover contributor registration order, approval short-circuiting, event sink retention, no-op response injection, and closure-based agent spawning.\n- Add the test-only dependencies used by the suite.\n\n## Validation\n\n- `just test -p codex-extension-api`\n- `just argument-comment-lint -p codex-extension-api`\n- `just bazel-lock-check`\n", + "labels": [], + "merged_at": "2026-06-09T18:30:24Z", + "number": 26835, + "state": "merged", + "title": "[codex] Test extension API contracts", + "url": "https://github.com/openai/codex/pull/26835" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27085.json b/artifacts/github/bundles/openai-codex-pr-27085.json new file mode 100644 index 000000000..7b2c3a3d3 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27085.json @@ -0,0 +1,61 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "xl-openai", + "committed_at": "2026-06-08T23:03:21Z", + "message": "Use server app auth requirements for remote plugin install", + "sha": "df5b449689130d0e0dbd65ab882aa784a9c02028", + "url": "https://github.com/openai/codex/commit/df5b449689130d0e0dbd65ab882aa784a9c02028" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "REMOTE_PLUGIN_ID", + "TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS", + "DEFAULT_TIMEOUT", + "JSONRPCR", + "GET", + "POST" + ], + "files": [ + { + "additions": 38, + "deletions": 11, + "patch_excerpt": "@@ -1519,7 +1519,7 @@ impl PluginRequestProcessor {\n // Cache first so a backend install cannot succeed when local materialization fails.\n // If this backend call fails, the cache entry is harmless because remote installed state\n // is still backend-gated.\n- codex_core_plugins::remote::install_remote_plugin(\n+ let install_result = codex_core_plugins::remote::install_remote_plugin(\n &remote_plugin_service_config,\n auth.as_ref(),\n &actual_remote_marketplace_name,\n@@ -1538,7 +1538,7 @@ impl PluginRequestProcessor {\n \n let mut plugin_metadata =\n plugin_telemetry_metadata_from_root(&result.plugin_id, &result.installed_path).await;\n- plugin_metadata.remote_plugin_id = Some(remote_plugin_id);\n+ plugin_metadata.remote_plugin_id = Some(remote_plugin_id.clone());\n self.analytics_even...", + "path": "codex-rs/app-server/src/request_processors/plugins.rs", + "status": "modified" + }, + { + "additions": 102, + "deletions": 0, + "patch_excerpt": "@@ -288,6 +288,59 @@ async fn plugin_install_writes_remote_plugin_to_cloud_and_cache() -> Result<()>\n Ok(())\n }\n \n+#[tokio::test]\n+async fn plugin_install_uses_remote_apps_needing_auth_response() -> Result<()> {\n+ let codex_home = TempDir::new()?;\n+ let server = MockServer::start().await;\n+ let bundle_url = mount_remote_plugin_bundle(\n+ &server,\n+ /*status_code*/ 200,\n+ remote_plugin_bundle_tar_gz_bytes(\"linear\")?,\n+ )\n+ .await;\n+ configure_remote_plugin_with_apps_test(codex_home.path(), &server)?;\n+ mount_remote_plugin_detail(&server, REMOTE_PLUGIN_ID, \"1.2.3\", Some(&bundle_url)).await;\n+ mount_empty_remote_installed_plugins(&server).await;\n+ mount_remote_plugin_install_with_apps_needing_auth(&server, REMOTE_PLUGIN_ID, &[\"alpha\"]).await;\n+\n+ let mut mcp = TestAppServer::new_with_env(\n+ codex_home.path(),\n+ &[(TEST_ALLO...", + "path": "codex-rs/app-server/tests/suite/v2/plugin_install.rs", + "status": "modified" + }, + { + "additions": 16, + "deletions": 3, + "patch_excerpt": "@@ -550,6 +550,12 @@ struct RemotePluginInstalledResponse {\n struct RemotePluginMutationResponse {\n id: String,\n enabled: bool,\n+ app_ids_needing_auth: Option>,\n+}\n+\n+#[derive(Debug, Clone, PartialEq, Eq)]\n+pub struct RemotePluginInstallResult {\n+ pub app_ids_needing_auth: Option>,\n }\n \n pub async fn fetch_remote_marketplaces(\n@@ -1071,15 +1077,20 @@ pub async fn install_remote_plugin(\n auth: Option<&CodexAuth>,\n _marketplace_name: &str,\n plugin_id: &str,\n-) -> Result<(), RemotePluginCatalogError> {\n+) -> Result {\n let auth = ensure_chatgpt_auth(auth)?;\n // Remote plugin IDs uniquely identify remote plugins, so the caller-provided\n // marketplace name is not validated before sending the install mutation.\n \n let base_url = config.chatgpt_base_url.trim_end_matches('/');\n ...", + "path": "codex-rs/core-plugins/src/remote.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Summary\n- request `includeAppsNeedingAuth=true` when installing remote plugins\n- return backend-provided `app_ids_needing_auth` from the remote install client\n- use those app IDs to populate `appsNeedingAuth` without refetching accessible apps, with fallback for older responses\n\n## Testing\n- `just fmt`\n- `just test -p codex-app-server`\n- `just test -p codex-core-plugins`\n- real app-server install/uninstall check with Notion remote plugin\n- subagent review found no blocking issues\n", + "labels": [], + "merged_at": "2026-06-09T04:39:35Z", + "number": 27085, + "state": "merged", + "title": "Use server app auth requirements for remote plugin install", + "url": "https://github.com/openai/codex/pull/27085" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27191.json b/artifacts/github/bundles/openai-codex-pr-27191.json new file mode 100644 index 000000000..1f4e319f0 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27191.json @@ -0,0 +1,256 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-09T14:21:13Z", + "message": "Route hosted Apps MCP through extensions", + "sha": "04552f5663aa09b2363f8245b8d814d8bdfdc557", + "url": "https://github.com/openai/codex/commit/04552f5663aa09b2363f8245b8d814d8bdfdc557" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T15:10:49Z", + "message": "Fix runtime MCP API consistency", + "sha": "20b507e3783731f6ecda1ea1ed8197e286891158", + "url": "https://github.com/openai/codex/commit/20b507e3783731f6ecda1ea1ed8197e286891158" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T18:36:26Z", + "message": "Fix runtime MCP server consistency", + "sha": "648f66285f4ce14fd12e11c53386f19476b0d44b", + "url": "https://github.com/openai/codex/commit/648f66285f4ce14fd12e11c53386f19476b0d44b" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "CCA", + "HTTP", + "MCP", + "URL", + "SKU", + "API", + "CLI", + "JSONRPCE", + "CODEX_APPS_MCP_SERVER_NAME", + "CONNECTORS_READY_TIMEOUT_ON_EMPTY_TOOLS" + ], + "files": [ + { + "additions": 19, + "deletions": 0, + "patch_excerpt": "@@ -1982,6 +1982,7 @@ dependencies = [\n \"codex-image-generation-extension\",\n \"codex-login\",\n \"codex-mcp\",\n+ \"codex-mcp-extension\",\n \"codex-memories-extension\",\n \"codex-memories-write\",\n \"codex-model-provider\",\n@@ -2920,6 +2921,7 @@ name = \"codex-extension-api\"\n version = \"0.0.0\"\n dependencies = [\n \"async-trait\",\n+ \"codex-config\",\n \"codex-context-fragments\",\n \"codex-protocol\",\n \"codex-tools\",\n@@ -3245,6 +3247,23 @@ dependencies = [\n \"url\",\n ]\n \n+[[package]]\n+name = \"codex-mcp-extension\"\n+version = \"0.0.0\"\n+dependencies = [\n+ \"async-trait\",\n+ \"codex-config\",\n+ \"codex-core\",\n+ \"codex-core-plugins\",\n+ \"codex-extension-api\",\n+ \"codex-features\",\n+ \"codex-login\",\n+ \"codex-mcp\",\n+ \"pretty_assertions\",\n+ \"tempfile\",\n+ \"tokio\",\n+]\n+\n [[package]]\n name = \"codex-mcp-server\"\n version = \"0.0.0\"", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -49,6 +49,7 @@ members = [\n \"ext/guardian\",\n \"ext/image-generation\",\n \"ext/memories\",\n+ \"ext/mcp\",\n \"ext/skills\",\n \"ext/web-search\",\n \"external-agent-migration\",\n@@ -191,6 +192,7 @@ codex-web-search-extension = { path = \"ext/web-search\" }\n codex-memories-read = { path = \"memories/read\" }\n codex-memories-write = { path = \"memories/write\" }\n codex-mcp = { path = \"codex-mcp\" }\n+codex-mcp-extension = { path = \"ext/mcp\" }\n codex-mcp-server = { path = \"mcp-server\" }\n codex-model-provider-info = { path = \"model-provider-info\" }\n codex-models-manager = { path = \"models-manager\" }", + "path": "codex-rs/Cargo.toml", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -61,6 +61,7 @@ codex-memories-extension = { workspace = true }\n codex-web-search-extension = { workspace = true }\n codex-memories-write = { workspace = true }\n codex-mcp = { workspace = true }\n+codex-mcp-extension = { workspace = true }\n codex-model-provider = { workspace = true }\n codex-models-manager = { workspace = true }\n codex-protocol = { workspace = true }", + "path": "codex-rs/app-server/Cargo.toml", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -50,6 +50,7 @@ where\n }\n codex_guardian::install(&mut builder, guardian_agent_spawner);\n codex_memories_extension::install(&mut builder, codex_otel::global());\n+ codex_mcp_extension::install(&mut builder);\n codex_web_search_extension::install(&mut builder, auth_manager.clone());\n codex_image_generation_extension::install(&mut builder, auth_manager);\n codex_skills_extension::install_with_providers(", + "path": "codex-rs/app-server/src/extensions.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 4, + "patch_excerpt": "@@ -67,10 +67,7 @@ async fn build_refresh_config(\n let config = config_manager\n .load_latest_config_for_thread(thread_config.as_ref())\n .await?;\n- let mcp_servers = thread_manager\n- .mcp_manager()\n- .configured_servers(&config)\n- .await;\n+ let mcp_servers = thread_manager.mcp_manager().runtime_servers(&config).await;\n Ok(McpServerRefreshConfig {\n mcp_servers: serde_json::to_value(mcp_servers).map_err(io::Error::other)?,\n mcp_oauth_credentials_store_mode: serde_json::to_value(", + "path": "codex-rs/app-server/src/mcp_refresh.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -280,6 +280,7 @@ use codex_config::types::McpServerTransportConfig;\n use codex_core::CodexThread;\n use codex_core::CodexThreadSettingsOverrides;\n use codex_core::ForkSnapshot;\n+use codex_core::McpManager;\n use codex_core::NewThread;\n #[cfg(test)]\n use codex_core::SessionMeta;", + "path": "codex-rs/app-server/src/request_processors.rs", + "status": "modified" + }, + { + "additions": 24, + "deletions": 10, + "patch_excerpt": "@@ -88,11 +88,19 @@ impl AppsRequestProcessor {\n let request = request_id.clone();\n let outgoing = Arc::clone(&self.outgoing);\n let environment_manager = self.thread_manager.environment_manager();\n+ let mcp_manager = self.thread_manager.mcp_manager();\n let shutdown_token = self.shutdown_token.child_token();\n tokio::spawn(async move {\n tokio::select! {\n _ = shutdown_token.cancelled() => {}\n- _ = Self::apps_list_task(outgoing, request, params, config, environment_manager) => {}\n+ _ = Self::apps_list_task(\n+ outgoing,\n+ request,\n+ params,\n+ config,\n+ environment_manager,\n+ mcp_manager,\n+ ) => {}\n }\n });\n Ok(None)\n@@ -108,11 +116,15 @...", + "path": "codex-rs/app-server/src/request_processors/apps_processor.rs", + "status": "modified" + }, + { + "additions": 15, + "deletions": 7, + "patch_excerpt": "@@ -120,12 +120,16 @@ impl McpRequestProcessor {\n timeout_secs,\n } = params;\n \n- let configured_servers = self\n+ let auth = self.auth_manager.auth().await;\n+ let effective_servers = self\n .thread_manager\n .mcp_manager()\n- .configured_servers(&config)\n+ .effective_servers(&config, auth.as_ref())\n .await;\n- let Some(server) = configured_servers.get(&name) else {\n+ let Some(server) = effective_servers\n+ .get(&name)\n+ .and_then(codex_mcp::EffectiveMcpServer::configured_config)\n+ else {\n return Err(invalid_request(format!(\n \"No MCP server named '{name}' found.\"\n )));\n@@ -210,8 +214,10 @@ impl McpRequestProcessor {\n }\n None => self.load_latest_config(/*fallback_cwd*/ None).await?,\n }...", + "path": "codex-rs/app-server/src/request_processors/mcp_processor.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 3, + "patch_excerpt": "@@ -1036,6 +1036,7 @@ impl PluginRequestProcessor {\n &config,\n &outcome.plugin.apps,\n Arc::clone(&environment_manager),\n+ self.thread_manager.mcp_manager(),\n )\n .await;\n let visible_skills = outcome\n@@ -1118,6 +1119,7 @@ impl PluginRequestProcessor {\n &config,\n &plugin_apps,\n Arc::clone(&environment_manager),\n+ self.thread_manager.mcp_manager(),\n )\n .await;\n remote_plugin_detail_to_info(remote_detail, app_summaries)\n@@ -1611,10 +1613,11 @@ impl PluginRequestProcessor {\n let environment_manager = self.thread_manager.environment_manager();\n let (all_connectors_result, accessible_connectors_result) = tokio::join!(\n ...", + "path": "codex-rs/app-server/src/request_processors/plugins.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -14,6 +14,7 @@ use codex_connectors::merge::merge_plugin_connectors;\n use codex_core::config::Config;\n pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools;\n pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager;\n+pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_mcp_manager;\n pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_options;\n pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_options_and_status;\n pub use codex_core::connectors::list_cached_accessible_connectors_from_mcp_tools;", + "path": "codex-rs/chatgpt/src/connectors.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -24,6 +24,7 @@ pub use auth_elicitation::build_auth_elicitation_plan;\n pub use auth_elicitation::connector_auth_failure_from_tool_result;\n pub use codex_apps::CodexAppsToolsCacheKey;\n pub use codex_apps::codex_apps_tools_cache_key;\n+pub use mcp::codex_apps_mcp_server_config;\n pub use mcp::configured_mcp_servers;\n pub use mcp::effective_mcp_servers;\n pub use mcp::effective_mcp_servers_from_configured;", + "path": "codex-rs/codex-mcp/src/lib.rs", + "status": "modified" + }, + { + "additions": 24, + "deletions": 12, + "patch_excerpt": "@@ -131,6 +131,11 @@ pub struct McpConfig {\n /// ChatGPT auth is checked separately at runtime before the host-owned apps\n /// MCP server is added.\n pub apps_enabled: bool,\n+ /// Whether to synthesize the legacy host-owned Apps MCP server.\n+ ///\n+ /// Hosts that install an MCP extension for this server disable the legacy\n+ /// loader and contribute the server through the normal runtime overlay.\n+ pub legacy_apps_mcp_loader_enabled: bool,\n /// Whether model-visible MCP tool namespaces should keep the legacy\n /// `mcp__` prefix.\n pub prefix_mcp_tool_names: bool,\n@@ -218,10 +223,20 @@ pub fn with_codex_apps_mcp(\n auth: Option<&CodexAuth>,\n config: &McpConfig,\n ) -> HashMap {\n+ if !config.legacy_apps_mcp_loader_enabled {\n+ if !host_owned_codex_apps_enabled(config, auth) {\n+ servers.remove(CODEX_AP...", + "path": "codex-rs/codex-mcp/src/mcp/mod.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 10, + "patch_excerpt": "@@ -27,6 +27,7 @@ fn test_mcp_config(codex_home: PathBuf) -> McpConfig {\n codex_linux_sandbox_exe: None,\n use_legacy_landlock: false,\n apps_enabled: false,\n+ legacy_apps_mcp_loader_enabled: true,\n prefix_mcp_tool_names: true,\n client_elicitation_capability: ElicitationCapability::default(),\n configured_mcp_servers: HashMap::new(),\n@@ -206,16 +207,6 @@ fn codex_apps_mcp_url_for_base_url_keeps_existing_paths() {\n );\n }\n \n-#[test]\n-fn codex_apps_mcp_url_uses_legacy_codex_apps_path() {\n- let config = test_mcp_config(PathBuf::from(\"/tmp\"));\n-\n- assert_eq!(\n- codex_apps_mcp_url(&config),\n- \"https://chatgpt.com/backend-api/wham/apps\"\n- );\n-}\n-\n #[test]\n fn codex_apps_server_config_uses_legacy_codex_apps_path() {\n let mut config = test_mcp_config(PathBuf::from(\"/tmp\"));", + "path": "codex-rs/codex-mcp/src/mcp/mod_tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -1428,6 +1428,7 @@ impl Config {\n codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.clone(),\n use_legacy_landlock: self.features.use_legacy_landlock(),\n apps_enabled: self.features.enabled(Feature::Apps),\n+ legacy_apps_mcp_loader_enabled: true,\n prefix_mcp_tool_names: self.prefix_mcp_tool_names(),\n client_elicitation_capability: if self.features.enabled(Feature::AuthElicitation) {\n ElicitationCapability {", + "path": "codex-rs/core/src/config/mod.rs", + "status": "modified" + }, + { + "additions": 21, + "deletions": 6, + "patch_excerpt": "@@ -1,4 +1,3 @@\n-use std::collections::HashMap;\n use std::collections::HashSet;\n use std::sync::Arc;\n use std::sync::LazyLock;\n@@ -41,8 +40,8 @@ use codex_mcp::ToolInfo;\n use codex_mcp::ToolPluginProvenance;\n use codex_mcp::codex_apps_tools_cache_key;\n use codex_mcp::compute_auth_statuses;\n+use codex_mcp::effective_mcp_servers;\n use codex_mcp::host_owned_codex_apps_enabled;\n-use codex_mcp::with_codex_apps_mcp;\n \n const CONNECTORS_READY_TIMEOUT_ON_EMPTY_TOOLS: Duration = Duration::from_secs(30);\n \n@@ -220,6 +219,23 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_environment_manager(\n config: &Config,\n force_refetch: bool,\n environment_manager: Arc,\n+) -> anyhow::Result {\n+ let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.to_path_buf()));\n+ let mcp_manager = Arc::new(McpManager::new(plugins...", + "path": "codex-rs/core/src/connectors.rs", + "status": "modified" + }, + { + "additions": 68, + "deletions": 2, + "patch_excerpt": "@@ -4,8 +4,12 @@ use std::sync::Arc;\n use crate::config::Config;\n use codex_config::McpServerConfig;\n use codex_core_plugins::PluginsManager;\n+use codex_extension_api::ExtensionRegistry;\n+use codex_extension_api::McpServerContribution;\n use codex_login::CodexAuth;\n+use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n use codex_mcp::EffectiveMcpServer;\n+use codex_mcp::McpConfig;\n use codex_mcp::ToolPluginProvenance;\n use codex_mcp::configured_mcp_servers;\n use codex_mcp::effective_mcp_servers;\n@@ -14,29 +18,91 @@ use codex_mcp::tool_plugin_provenance as collect_tool_plugin_provenance;\n #[derive(Clone)]\n pub struct McpManager {\n plugins_manager: Arc,\n+ extensions: Arc>,\n }\n \n impl McpManager {\n pub fn new(plugins_manager: Arc) -> Self {\n- Self { plugins_manager }\n+ Self {\n+ plugins_manager,\n+ ex...", + "path": "codex-rs/core/src/mcp.rs", + "status": "modified" + }, + { + "additions": 14, + "deletions": 8, + "patch_excerpt": "@@ -56,7 +56,7 @@ pub(crate) async fn maybe_prompt_and_install_mcp_dependencies(\n let installed = sess\n .services\n .mcp_manager\n- .configured_servers(config.as_ref())\n+ .runtime_servers(config.as_ref())\n .await;\n let missing = collect_missing_mcp_dependencies(mentioned_skills, &installed);\n if missing.is_empty() {\n@@ -98,7 +98,7 @@ pub(crate) async fn maybe_install_mcp_dependencies(\n }\n \n let codex_home = config.codex_home.clone();\n- let installed = sess.services.mcp_manager.configured_servers(config).await;\n+ let installed = sess.services.mcp_manager.runtime_servers(config).await;\n let missing = collect_missing_mcp_dependencies(mentioned_skills, &installed);\n if missing.is_empty() {\n return;\n@@ -190,16 +190,22 @@ pub(crate) async fn maybe_install_mcp_dependencies(\n }\n }\n \n- // Refresh from the...", + "path": "codex-rs/core/src/mcp_skill_dependencies.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 2, + "patch_excerpt": "@@ -311,8 +311,10 @@ impl Session {\n ) {\n let auth = self.services.auth_manager.auth().await;\n let config = self.get_config().await;\n- let mcp_config = config\n- .to_mcp_config(self.services.plugins_manager.as_ref())\n+ let mcp_config = self\n+ .services\n+ .mcp_manager\n+ .runtime_config(config.as_ref())\n .await;\n let tool_plugin_provenance = self\n .services", + "path": "codex-rs/core/src/session/mcp.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 1, + "patch_excerpt": "@@ -272,7 +272,10 @@ impl ThreadManager {\n codex_home.to_path_buf(),\n restriction_product,\n ));\n- let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));\n+ let mcp_manager = Arc::new(McpManager::new_with_extensions(\n+ Arc::clone(&plugins_manager),\n+ Arc::clone(&extensions),\n+ ));\n let skills_manager = Arc::new(SkillsManager::new_with_restriction_product(\n codex_home,\n config.bundled_skills_enabled(),", + "path": "codex-rs/core/src/thread_manager.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -15,6 +15,7 @@ workspace = true\n \n [dependencies]\n async-trait = { workspace = true }\n+codex-config = { workspace = true }\n codex-context-fragments = { workspace = true }\n codex-protocol = { workspace = true }\n codex-tools = { workspace = true }", + "path": "codex-rs/ext/extension-api/Cargo.toml", + "status": "modified" + }, + { + "additions": 14, + "deletions": 0, + "patch_excerpt": "@@ -10,12 +10,14 @@ use codex_tools::ToolExecutor;\n \n use crate::ExtensionData;\n \n+mod mcp;\n mod prompt;\n mod thread_lifecycle;\n mod tool_lifecycle;\n mod turn_input;\n mod turn_lifecycle;\n \n+pub use mcp::McpServerContribution;\n pub use prompt::PromptFragment;\n pub use prompt::PromptSlot;\n pub use thread_lifecycle::ThreadIdleInput;\n@@ -34,6 +36,18 @@ pub use turn_lifecycle::TurnErrorInput;\n pub use turn_lifecycle::TurnStartInput;\n pub use turn_lifecycle::TurnStopInput;\n \n+/// Extension contribution that resolves runtime MCP servers from host config.\n+///\n+/// Contributors run in registration order. Later contributions for the same\n+/// name replace earlier ones. Implementations must contribute only names they\n+/// own and must apply any source-specific policy before returning a server.\n+/// Plugin-owned servers and their provenance continue to be resolved by the\n+/// plugin manager until t...", + "path": "codex-rs/ext/extension-api/src/contributors.rs", + "status": "modified" + }, + { + "additions": 22, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,22 @@\n+use codex_config::McpServerConfig;\n+\n+/// One extension-owned overlay for the runtime MCP server configuration.\n+#[derive(Clone, Debug)]\n+pub enum McpServerContribution {\n+ /// Adds or replaces a named MCP server.\n+ Set {\n+ name: String,\n+ config: Box,\n+ },\n+ /// Removes a named MCP server.\n+ Remove { name: String },\n+}\n+\n+impl McpServerContribution {\n+ /// Returns the stable server name owned by this contribution.\n+ pub fn name(&self) -> &str {\n+ match self {\n+ Self::Set { name, .. } | Self::Remove { name } => name,\n+ }\n+ }\n+}", + "path": "codex-rs/ext/extension-api/src/contributors/mcp.rs", + "status": "added" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -31,6 +31,8 @@ pub use codex_tools::parse_tool_input_schema_without_compaction;\n pub use contributors::ApprovalReviewContributor;\n pub use contributors::ConfigContributor;\n pub use contributors::ContextContributor;\n+pub use contributors::McpServerContribution;\n+pub use contributors::McpServerContributor;\n pub use contributors::PromptFragment;\n pub use contributors::PromptSlot;\n pub use contributors::ThreadIdleInput;", + "path": "codex-rs/ext/extension-api/src/lib.rs", + "status": "modified" + }, + { + "additions": 15, + "deletions": 0, + "patch_excerpt": "@@ -7,6 +7,7 @@ use crate::ConfigContributor;\n use crate::ContextContributor;\n use crate::ExtensionData;\n use crate::ExtensionEventSink;\n+use crate::McpServerContributor;\n use crate::NoopExtensionEventSink;\n use crate::ThreadLifecycleContributor;\n use crate::TokenUsageContributor;\n@@ -24,6 +25,7 @@ pub struct ExtensionRegistryBuilder {\n config_contributors: Vec>>,\n token_usage_contributors: Vec>,\n context_contributors: Vec>,\n+ mcp_server_contributors: Vec>>,\n turn_input_contributors: Vec>,\n tool_contributors: Vec>,\n tool_lifecycle_contributors: Vec>,\n@@ -41,6 +43,7 @@ impl Default for ExtensionRegistryBuilder {\n token_usage...", + "path": "codex-rs/ext/extension-api/src/registry.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,6 @@\n+load(\"//:defs.bzl\", \"codex_rust_crate\")\n+\n+codex_rust_crate(\n+ name = \"mcp\",\n+ crate_name = \"codex_mcp_extension\",\n+)", + "path": "codex-rs/ext/mcp/BUILD.bazel", + "status": "added" + }, + { + "additions": 29, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,29 @@\n+[package]\n+edition.workspace = true\n+license.workspace = true\n+name = \"codex-mcp-extension\"\n+version.workspace = true\n+\n+[lib]\n+name = \"codex_mcp_extension\"\n+path = \"src/lib.rs\"\n+test = false\n+doctest = false\n+\n+[lints]\n+workspace = true\n+\n+[dependencies]\n+async-trait = { workspace = true }\n+codex-core = { workspace = true }\n+codex-extension-api = { workspace = true }\n+codex-features = { workspace = true }\n+codex-mcp = { workspace = true }\n+\n+[dev-dependencies]\n+codex-config = { workspace = true }\n+codex-core-plugins = { workspace = true }\n+codex-login = { workspace = true }\n+pretty_assertions = { workspace = true }\n+tempfile = { workspace = true }\n+tokio = { workspace = true, features = [\"macros\", \"rt-multi-thread\"] }", + "path": "codex-rs/ext/mcp/Cargo.toml", + "status": "added" + }, + { + "additions": 31, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,31 @@\n+use codex_core::config::Config;\n+use codex_extension_api::ExtensionRegistryBuilder;\n+use codex_extension_api::McpServerContribution;\n+use codex_extension_api::McpServerContributor;\n+use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n+use codex_mcp::codex_apps_mcp_server_config;\n+\n+struct HostedAppsMcpExtension;\n+\n+#[async_trait::async_trait]\n+impl McpServerContributor for HostedAppsMcpExtension {\n+ async fn contribute(&self, config: &Config) -> Vec {\n+ let name = CODEX_APPS_MCP_SERVER_NAME.to_string();\n+ if !config.features.enabled(codex_features::Feature::Apps) {\n+ return vec![McpServerContribution::Remove { name }];\n+ }\n+\n+ vec![McpServerContribution::Set {\n+ name,\n+ config: Box::new(codex_apps_mcp_server_config(\n+ &config.chatgpt_base_url,\n+ config.app...", + "path": "codex-rs/ext/mcp/src/lib.rs", + "status": "added" + }, + { + "additions": 93, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,93 @@\n+use std::sync::Arc;\n+\n+use codex_config::McpServerTransportConfig;\n+use codex_core::McpManager;\n+use codex_core::config::Config;\n+use codex_core::config::ConfigBuilder;\n+use codex_core_plugins::PluginsManager;\n+use codex_extension_api::ExtensionRegistryBuilder;\n+use codex_login::CodexAuth;\n+use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;\n+use pretty_assertions::assert_eq;\n+\n+#[tokio::test]\n+async fn contributes_hosted_apps_mcp_without_an_executor() -> Result<(), Box>\n+{\n+ let codex_home = tempfile::tempdir()?;\n+ let config = ConfigBuilder::default()\n+ .codex_home(codex_home.path().to_path_buf())\n+ .fallback_cwd(Some(codex_home.path().to_path_buf()))\n+ .cli_overrides(vec![\n+ (\"features.apps\".to_string(), true.into()),\n+ (\"features.apps_mcp_path_override\".to_string(), true.into()),\n+ (\"chatgpt_b...", + "path": "codex-rs/ext/mcp/tests/hosted_apps_mcp.rs", + "status": "added" + } + ], + "linked_issues": [ + "#27184" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Stack\n\n- Base: #27184\n- This PR is the second vertical and should be reviewed against `jif/external-plugins-1`, not `main`.\n\n## Why\n\nCCA is moving toward a split runtime where the orchestrator may have no filesystem or executor, but it still needs to activate remotely hosted plugin components. HTTP MCP servers are the simplest complete example: they need configuration and host authentication, but they do not need an executor process.\n\nThe Apps MCP endpoint is currently synthesized by a special-purpose loader inside the MCP runtime. That works locally, but it leaves hosted MCP activation outside the extension model being established in #27184. It also makes the Apps path a poor foundation for plugins whose skills, MCP servers, connectors, and hooks may come from different sources or execute in different places.\n\nThis PR moves that one behavior behind an extension-owned contribution while preserving the existing local fallback. It deliberately does not introduce a generic plugin activation framework.\n\n## What changed\n\n### MCP extension contribution\n\n`codex-extension-api` gains an ordered `McpServerContributor` contract. A contributor returns typed `Set` or `Remove` overlays for MCP server configuration; later contributors win for the names they own.\n\nThe contract stays at the existing MCP configuration boundary. Extensions do not create a second connection manager or transport abstraction.\n\n### Hosted Apps MCP extension\n\nA new `codex-mcp-extension` contributes the reserved `codex_apps` server from the existing Apps feature, ChatGPT base URL, path override, and product SKU configuration.\n\nWhen `apps_mcp_path_override` is enabled for `https://chatgpt.com`, the resulting streamable HTTP endpoint is `https://chatgpt.com/backend-api/ps/mcp`. The existing ChatGPT-auth gate remains authoritative, so this server can run in an orchestrator-only process without being exposed for API-key sessions.\n\n### One resolved runtime view\n\n`McpManager` now distinguishes three views:\n\n- **configured:** config- and plugin-backed servers before extension overlays;\n- **runtime:** configured servers plus host-installed extension contributions;\n- **effective:** runtime servers after auth gating and compatibility built-ins.\n\nApp-server installs the hosted MCP extension and uses the runtime view for thread startup, refresh, status, threadless resource reads, connector discovery, and MCP OAuth lookup. This keeps `mcpServer/oauth/login` consistent with the servers exposed by the other MCP APIs. The hosted Apps server itself continues to use existing ChatGPT host authentication rather than MCP OAuth.\n\n## Compatibility\n\nHosts that do not install the MCP extension retain the existing Apps MCP synthesis path. This preserves current local-only, CLI, and standalone-host behavior while app-server exercises the extension path.\n\nDisabling Apps removes the reserved `codex_apps` entry, and losing ChatGPT auth removes it from the effective runtime view. Executor availability is not consulted for this HTTP transport.\n\n## Follow-ups\n\nThe next vertical will resolve a manifest-declared stdio MCP server from an executor-selected plugin root and execute it in the environment that owns that root. Later verticals can add backend-owned skills, connector metadata, hooks, durable selection semantics, and incremental local convergence without changing the component-specific runtime boundaries introduced here.\n\n## Verification\n\nFocused coverage was added for:\n\n- contributing the hosted Apps MCP at `/backend-api/ps/mcp` without an executor;\n- requiring ChatGPT auth in the effective runtime view;\n- removing a reserved configured Apps server when the Apps feature is disabled.\n\n`cargo check -p codex-app-server -p codex-mcp-extension -p codex-extension-api -p codex-mcp` passed. Tests and Clippy were not run locally under the current development instruction; CI provides the full validation pass.\n", + "labels": [], + "merged_at": "2026-06-09T20:44:16Z", + "number": 27191, + "state": "merged", + "title": "Route hosted Apps MCP through extensions", + "url": "https://github.com/openai/codex/pull/27191" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27223.json b/artifacts/github/bundles/openai-codex-pr-27223.json new file mode 100644 index 000000000..cdf8220f4 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27223.json @@ -0,0 +1,51 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "ericning-o", + "committed_at": "2026-06-09T17:34:45Z", + "message": "fix: use plugin service route for remote uninstall", + "sha": "f0566d8a72018d542d5cd745825a478d4d21b401", + "url": "https://github.com/openai/codex/commit/f0566d8a72018d542d5cd745825a478d4d21b401" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "POST", + "REMOTE_PLUGIN_ID", + "WORKSPACE_REMOTE_PLUGIN_ID" + ], + "files": [ + { + "additions": 9, + "deletions": 9, + "patch_excerpt": "@@ -206,7 +206,7 @@ async fn plugin_uninstall_writes_remote_plugin_to_cloud_when_remote_plugin_enabl\n \n Mock::given(method(\"POST\"))\n .and(path(format!(\n- \"/backend-api/plugins/{REMOTE_PLUGIN_ID}/uninstall\"\n+ \"/backend-api/ps/plugins/{REMOTE_PLUGIN_ID}/uninstall\"\n )))\n .and(header(\"authorization\", \"Bearer chatgpt-token\"))\n .and(header(\"chatgpt-account-id\", \"account-123\"))\n@@ -249,7 +249,7 @@ async fn plugin_uninstall_writes_remote_plugin_to_cloud_when_remote_plugin_enabl\n wait_for_remote_plugin_request_count(\n &server,\n \"POST\",\n- &format!(\"/plugins/{REMOTE_PLUGIN_ID}/uninstall\"),\n+ &format!(\"/ps/plugins/{REMOTE_PLUGIN_ID}/uninstall\"),\n /*expected_count*/ 1,\n )\n .await?;\n@@ -278,7 +278,7 @@ async fn plugin_uninstall_uses_detail_scope_for_cache_namespace() -> Result<()>\n \n Mock::g...", + "path": "codex-rs/app-server/tests/suite/v2/plugin_uninstall.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -1124,7 +1124,7 @@ pub async fn uninstall_remote_plugin(\n let plugin_name = plugin.name;\n \n let base_url = config.chatgpt_base_url.trim_end_matches('/');\n- let url = format!(\"{base_url}/plugins/{plugin_id}/uninstall\");\n+ let url = format!(\"{base_url}/ps/plugins/{plugin_id}/uninstall\");\n let client = build_reqwest_client();\n let request = authenticated_request(client.post(&url), auth)?;\n let response: RemotePluginMutationResponse = send_and_decode(request, &url).await?;", + "path": "codex-rs/core-plugins/src/remote.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\r\n\r\nRemote plugin uninstall requests were sent to `/plugins/{plugin_id}/uninstall`, but the plugin service expects these mutations under `/ps/plugins`.\r\n\r\n## What changed\r\n\r\n- Send remote uninstall requests to `/ps/plugins/{plugin_id}/uninstall`.\r\n- Update app-server uninstall tests and request assertions to use the corrected route.\r\n\r\n## Testing\r\n\r\n- `just test -p codex-core-plugins` (203 passed)\r\n- Verified against production using the locally built app-server: the uninstall succeeded, and a follow-up plugin list reported `installed: false` and `enabled: false`.", + "labels": [], + "merged_at": "2026-06-09T19:52:06Z", + "number": 27223, + "state": "merged", + "title": "fix: use plugin service route for remote uninstall", + "url": "https://github.com/openai/codex/pull/27223" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-27085.json b/artifacts/github/impact/openai-codex-pr-27085.json new file mode 100644 index 000000000..43a9f2e0e --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27085.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27085", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Use server app auth requirements for remote plugin install", + "url": "https://github.com/openai/codex/pull/27085", + "meta": "Merged 2026-06-09T04:39:35Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27085", + "meta": "artifacts/github/reviews/openai-codex-pr-27085.review.json" + } + ] + }, + "observed_change": "Codex remote plugin install now asks the plugin service for app IDs requiring authentication and returns those IDs through the app-server install response.", + "public_signal_decision": "publish", + "control_plane_impact": "candidate", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records `includeAppsNeedingAuth=true` in the install request path.", + "Remote install now parses optional backend `app_ids_needing_auth` into a `RemotePluginInstallResult`.", + "App-server plugin install uses backend app IDs to populate `appsNeedingAuth` while retaining fallback behavior.", + "App-server tests cover install responses that use remote apps-needing-auth metadata." + ], + "candidate_followups": [ + "Prefer server-provided app IDs when Decodex guides post-install plugin auth prompts.", + "Keep fallback handling for older backend responses that omit `app_ids_needing_auth`.", + "Avoid redundant accessible-app refetches when install response metadata is already present." + ], + "social_notes": [ + "Public copy should frame this as more direct post-install auth guidance for remote plugins.", + "Do not claim that every remote plugin install will need auth; the field is backend-provided and optional." + ], + "caveats": [ + "The backend field may be absent for older responses.", + "The source review confirms Codex client handling, not every production backend rollout detail." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27191.json b/artifacts/github/impact/openai-codex-pr-27191.json new file mode 100644 index 000000000..33b84a1e1 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27191.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27191", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Route hosted Apps MCP through extensions", + "url": "https://github.com/openai/codex/pull/27191", + "meta": "Merged 2026-06-09T20:44:16Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27191", + "meta": "artifacts/github/reviews/openai-codex-pr-27191.review.json" + } + ] + }, + "observed_change": "Codex app-server now installs a hosted Apps MCP extension and resolves MCP APIs through runtime and effective MCP views instead of relying only on configured servers or the legacy Apps loader.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records a new `McpServerContributor` contract and hosted Apps MCP extension.", + "App-server installs `codex_mcp_extension` and uses runtime/effective MCP views across refresh, status, connector, resource, OAuth, and thread-start paths.", + "The hosted Apps MCP contribution is gated by Apps and ChatGPT auth, and hosts without the extension retain the legacy loader.", + "Tests cover hosted Apps MCP contribution without an executor, auth gating, and Apps-disabled removal." + ], + "candidate_followups": [ + "Audit Decodex Control Plane code for assumptions that configured MCP servers equal runtime-visible MCP servers.", + "Treat extension-contributed MCP servers as first-class app-server state in status, refresh, OAuth, and connector readback.", + "Keep legacy Apps MCP fallback assumptions scoped to hosts that do not install the MCP extension." + ], + "social_notes": [ + "Public copy should emphasize the runtime/effective MCP view split and hosted Apps MCP extension path.", + "Avoid implying that this PR ships a generic plugin activation framework; the PR explicitly says that is a follow-up." + ], + "caveats": [ + "The change is one vertical in an external-plugins stack.", + "Generic manifest-declared stdio MCP activation remains a future vertical." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27223.json b/artifacts/github/impact/openai-codex-pr-27223.json new file mode 100644 index 000000000..9844ad59c --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27223.json @@ -0,0 +1,44 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27223", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "fix: use plugin service route for remote uninstall", + "url": "https://github.com/openai/codex/pull/27223", + "meta": "Merged 2026-06-09T19:52:06Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27223", + "meta": "artifacts/github/reviews/openai-codex-pr-27223.review.json" + } + ] + }, + "observed_change": "Codex remote plugin uninstall now posts to the plugin-service route under `/ps/plugins/{plugin_id}/uninstall` instead of `/plugins/{plugin_id}/uninstall`.", + "public_signal_decision": "publish", + "control_plane_impact": "candidate", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records the route change in `codex-rs/core-plugins/src/remote.rs`.", + "App-server uninstall tests now expect `/backend-api/ps/plugins/{REMOTE_PLUGIN_ID}/uninstall`.", + "The PR body says production verification showed uninstall succeeded and follow-up plugin list reported disabled/uninstalled state.", + "The client-facing app-server uninstall command shape is unchanged." + ], + "candidate_followups": [ + "Update Decodex remote-plugin mock servers or diagnostics that reference the old `/plugins/{id}/uninstall` backend route.", + "Treat uninstall failures against the old route as stale-client evidence.", + "Keep public messaging narrow: this is a remote uninstall route fix, not a new plugin capability." + ], + "social_notes": [ + "Public copy should be concise and route-specific.", + "Avoid overstating this as a protocol change for app-server clients." + ], + "caveats": [ + "The behavior change is backend-route-only from the app-server client's perspective." + ] +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index ec1f398f7..591c220d8 100644 --- a/artifacts/github/review-queue/openai-codex-latest.json +++ b/artifacts/github/review-queue/openai-codex-latest.json @@ -1,14 +1,14 @@ { "counts": { "critical": 19, - "high": 10, + "high": 12, "low": 0, - "normal": 11, + "normal": 9, "published_subjects_seen": 0, "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-10T02:06:16.841927Z", + "generated_at": "2026-06-10T08:04:41.120407Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,61 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "auth_account", - "breaking_change", - "new_feature", - "protocol_change", - "release_packaging" - ], - "changed_file_count": 7, - "commit_shas": [ - "3c842f79d482b0f96fcf9a039264ef1896345d17", - "758e981a1d26ae66fd2c1ff530873e378705b45b", - "882a9b4991823dd85cfc97ebf3557bfcf6623f4d", - "e03cce53589b3ad050d3d251741014fef5e88940", - "ffe52eeeeb6e01460d784b230f9a260e9a89e488", - "cf89de2c574b750f864b0f9a44fb2e136ce9e093", - "43038a2be12ee9e09bbb23e494a069213a12ea49", - "80f6b9758cd4938041cdf619b06c72657f13abcb", - "18883637213c269c4e589668169cc91b52f3b38e", - "9fba3263e8f0b6ff53ca995c40de0259cb0452a5", - "2cfc5a19b265c1860e9bd4eb2519cf38b984464b", - "4fc4e5baeb335cd5b6faca38e4a1bae6f623d66d", - "6cf3f6526a586c33b27ce11ad50f54aaf366462c", - "8a99473f3a265925e8b94e7f652c8584bc67084a", - "bd2279b23436fa40f838d9c469c8c6029cf7cffc", - "14a6f970c14aa660e94dbb5085e6a3e8fd3ad211", - "3eb69a100fb57eacb88073eb944149315aab00c1", - "1bf23e23a4360abe0be9b536a9b84ca24a13c92c", - "ad09d4f63e134d7bdb6f7dad8d726229f6669b06" - ], - "committed_at": "2026-06-08T23:33:41Z", - "next_step": "ai_review_required", - "pr_number": 26840, - "pr_url": "https://github.com/openai/codex/pull/26840", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, breaking_change, new_feature, protocol_change, release_packaging.", - "sample_paths": [ - ".github/CODEOWNERS", - "codex-rs/Cargo.lock", - "codex-rs/Cargo.toml", - "codex-rs/utils/path-uri/BUILD.bazel", - "codex-rs/utils/path-uri/Cargo.toml", - "codex-rs/utils/path-uri/src/lib.rs", - "codex-rs/utils/path-uri/src/tests.rs" - ], - "source_state": "merged", - "subject_id": "26840", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks", - "tests_ci" - ], - "title": "Add typed file URIs", - "url": "https://github.com/openai/codex/pull/26840" - }, { "attention_flags": [ "deprecated_removed", @@ -849,6 +794,45 @@ "title": "[codex-analytics] emit goal lifecycle analytics", "url": "https://github.com/openai/codex/pull/27078" }, + { + "attention_flags": [ + "auth_account", + "deprecated_removed", + "protocol_change", + "release_packaging" + ], + "changed_file_count": 5, + "commit_shas": [ + "e2348d4ce90e1991f9ea47acea2d2139e8d19c0c", + "3cb6016b3ea47d94f0966e50c18f6d6b8d3b6d2c", + "ea56e2180a47bb14d9914f800e8e726a8b593414", + "28e836c8888fbc001fceb444e2fccfff52e0ff01", + "675cdc7a2696bf763f5e18cb226c325ea61d1d5b" + ], + "committed_at": "2026-06-10T03:52:09Z", + "next_step": "ai_review_required", + "pr_number": 27285, + "pr_url": "https://github.com/openai/codex/pull/27285", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, deprecated_removed, protocol_change, release_packaging.", + "sample_paths": [ + "codex-rs/analytics/src/reducer.rs", + "codex-rs/app-server/src/extensions.rs", + "codex-rs/app-server/src/mcp_refresh.rs", + "codex-rs/app-server/src/message_processor.rs", + "codex-rs/app-server/tests/suite/v2/turn_start.rs" + ], + "source_state": "merged", + "subject_id": "27285", + "subject_kind": "pr", + "surface_hints": [ + "app_server_protocol", + "mcp_plugins", + "tests_ci" + ], + "title": "[codex] Fix post-merge analytics integration failures", + "url": "https://github.com/openai/codex/pull/27285" + }, { "attention_flags": [ "new_feature", @@ -1193,65 +1177,90 @@ }, { "attention_flags": [ - "auth_account", "new_feature", "protocol_change" ], - "changed_file_count": 2, + "changed_file_count": 4, "commit_shas": [ - "0d1f9d3e58fcf073ad1840a7bcf364ebb414ec26" + "d047caf12e4d0670a3abf28a4f2915fa3f70732d", + "f73d37d6769e342190b234e4d84c91e3a5fc8832" ], - "committed_at": "2026-06-08T23:07:56Z", + "committed_at": "2026-06-10T04:41:06Z", "next_step": "ai_review_required", - "pr_number": 27084, - "pr_url": "https://github.com/openai/codex/pull/27084", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change.", + "pr_number": 27107, + "pr_url": "https://github.com/openai/codex/pull/27107", + "review_priority": "high", + "review_reason": "Needs AI review for new_feature, protocol_change.", "sample_paths": [ - "codex-rs/tools/src/json_schema.rs", - "codex-rs/tools/src/json_schema_tests.rs" + "codex-rs/core/src/hook_runtime.rs", + "codex-rs/core/src/mcp_tool_exposure.rs", + "codex-rs/core/src/session/mod.rs", + "codex-rs/core/src/session/turn.rs" ], "source_state": "merged", - "subject_id": "27084", + "subject_id": "27107", "subject_kind": "pr", "surface_hints": [ - "tests_ci" + "config_hooks", + "mcp_plugins" ], - "title": "chore: preserve one more schema layer during large tool compaction", - "url": "https://github.com/openai/codex/pull/27084" + "title": "Add spans to run_turn", + "url": "https://github.com/openai/codex/pull/27107" }, { "attention_flags": [ "auth_account", - "breaking_change", - "new_feature" + "new_feature", + "protocol_change", + "security_policy" ], - "changed_file_count": 5, + "changed_file_count": 8, "commit_shas": [ - "b7fac96d42dbee0dc1941fd6322afe6868c4ead4" - ], - "committed_at": "2026-06-09T00:02:36Z", + "c504b328b7d835c0b33fea762d9a3d796ec336dc", + "6b4aa397aae0f0283f11b0d8da02da2e2ac88711", + "67da7c5e3b76018780d3cbc7ef3119c56ce5e32f", + "eb7002afa00aecddc79b7b2b032a15a78e336101", + "34ec33192dfec437986360b687d9a0a888ca521d", + "41dda768e5f3d9d339982a94b8d6a21399d68c01", + "abfe7ab2d0ad692c724a49bddfd64e40c836307b", + "e5c5f0dca5345d8c3e611d4fdaeb2de8b6bdd08c", + "6385c6b3b2620c233e771995c328a62a4ef5ceb6", + "dfca54a28e98ef7df5407e66ef45ec53bf93eff6", + "71772262f3766a508575724d94cec2322ca06796", + "a4439ecce64c51bd084dca079c150294a2f44394", + "cc2754bbba0afeac1bb785bc1577c6349ab38da5", + "a48cb410f214216e21c198be03aa601c01ab19c3", + "04b6ef2a7812f427e1f88790e3f4dc3a5b24415b", + "0588a59e00333db60c45a80602c47061df309690", + "4052ad0f2d8095406635faefa30522324067cd14", + "c4e6566a709a825200b021c42952decc6a398895", + "6fa2495bc95331e9f06340cd2011e8aad794a384" + ], + "committed_at": "2026-06-10T07:17:58Z", "next_step": "ai_review_required", - "pr_number": 27088, - "pr_url": "https://github.com/openai/codex/pull/27088", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, breaking_change, new_feature.", + "pr_number": 27261, + "pr_url": "https://github.com/openai/codex/pull/27261", + "review_priority": "high", + "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, security_policy.", "sample_paths": [ - "codex-rs/tui/src/lib.rs", - "codex-rs/tui/src/markdown_render.rs", - "codex-rs/tui/src/markdown_render_tests.rs", - "codex-rs/tui/src/markdown_text_merge.rs", - "codex-rs/tui/src/snapshots/codex_tui__markdown_render__markdown_render_tests__bare_url_with_tilde_keeps_complete_hyperlink.snap" + "codex-rs/codex-mcp/src/connection_manager.rs", + "codex-rs/codex-mcp/src/connection_manager_tests.rs", + "codex-rs/codex-mcp/src/mcp/mod.rs", + "codex-rs/core/src/connectors.rs", + "codex-rs/core/src/mcp_tool_call_tests.rs", + "codex-rs/core/src/session/mcp.rs", + "codex-rs/core/src/session/session.rs", + "codex-rs/core/src/state/service.rs" ], "source_state": "merged", - "subject_id": "27088", + "subject_id": "27261", "subject_kind": "pr", "surface_hints": [ - "cli_tui", + "mcp_plugins", "tests_ci" ], - "title": "fix(tui): linkify complete bare URLs with tildes", - "url": "https://github.com/openai/codex/pull/27088" + "title": "[codex] Make MCP connection startup fallible", + "url": "https://github.com/openai/codex/pull/27261" }, { "attention_flags": [ diff --git a/artifacts/github/reviews/openai-codex-pr-26835.review.json b/artifacts/github/reviews/openai-codex-pr-26835.review.json new file mode 100644 index 000000000..067abe2c1 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-26835.review.json @@ -0,0 +1,74 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-26835", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "26835", + "commit_shas": [ + "4f1183c281d837c81ee3f13f4b9207fc4bbd7115", + "4678bad085f9dac6876e0466c5f922635926d7db", + "583f8cca1bd6d6a4ea2762ad6ec1e39d646a8d51", + "3a804efa84757c84da11729a04e04995d5c04c8c" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Test extension API contracts", + "url": "https://github.com/openai/codex/pull/26835", + "meta": "Merged 2026-06-09T18:30:24Z" + }, + { + "kind": "commit", + "title": "test extension API contracts", + "url": "https://github.com/openai/codex/commit/4f1183c281d837c81ee3f13f4b9207fc4bbd7115" + }, + { + "kind": "commit", + "title": "codex: address PR review feedback (#26835)", + "url": "https://github.com/openai/codex/commit/4678bad085f9dac6876e0466c5f922635926d7db" + }, + { + "kind": "commit", + "title": "test: explain forced initializer overlap", + "url": "https://github.com/openai/codex/commit/583f8cca1bd6d6a4ea2762ad6ec1e39d646a8d51" + }, + { + "kind": "commit", + "title": "codex: address PR review feedback (#26835)", + "url": "https://github.com/openai/codex/commit/3a804efa84757c84da11729a04e04995d5c04c8c" + } + ] + }, + "reviewed_at": "2026-06-10T08:05:59Z", + "observed_change": "Codex added direct integration tests for the extension API public contract without changing the runtime extension API behavior.", + "changed_surfaces": [ + "codex-extension-api integration tests", + "ExtensionData typed-state test coverage", + "extension registry ordering and contributor behavior tests", + "test-only dependency metadata" + ], + "user_visible_path": "None; this PR adds regression coverage for extension API contracts rather than a new CLI, TUI, app-server, plugin, or SDK behavior path.", + "control_plane_relevance": "Useful as watch evidence that upstream is stabilizing the extension API contract, but it does not require a Decodex runtime or Control Plane change by itself.", + "compatibility_risk": "None identified from the source bundle; the changed files are tests, Cargo metadata for dev-dependencies, and lockfile entries.", + "adoption_opportunity": "Keep the tested extension API contracts in mind when Decodex evaluates future extension-backed plugin or MCP adoption work.", + "community_value": "Low as a standalone public signal because the PR is mostly validation coverage, not user-observable behavior.", + "deprecated_or_breaking_notes": "No deprecated, removed, migration, or breaking behavior was found in the reviewed source evidence.", + "confidence": "confirmed", + "evidence": [ + "PR #26835 says the API crate had no direct test suite and adds public-surface integration tests for `ExtensionData`, registry ordering, capability adapters, and contributor behavior.", + "codex-rs/ext/extension-api/tests/capabilities.rs adds tests for `NoopResponseItemInjector` and closure-based agent spawning.", + "codex-rs/ext/extension-api/tests/registry.rs adds contributor ordering, approval short-circuiting, event sink retention, and registry behavior tests.", + "codex-rs/ext/extension-api/tests/state.rs adds typed-state insert, replace, remove, concurrency, and poison-recovery coverage.", + "The only non-test source changes in the bundle are dev-dependency additions in Cargo metadata and the lockfile." + ], + "caveats": "The PR is valuable for confidence in later extension API work, but it should not be promoted as a shipped feature on its own.", + "next_actions": [ + { + "type": "none", + "reason": "Review trace is enough; no impact, signal, social, or Linear follow-up is warranted from this test-only PR alone." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27085.review.json b/artifacts/github/reviews/openai-codex-pr-27085.review.json new file mode 100644 index 000000000..b76225a45 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27085.review.json @@ -0,0 +1,60 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27085", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27085", + "commit_shas": [ + "df5b449689130d0e0dbd65ab882aa784a9c02028" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Use server app auth requirements for remote plugin install", + "url": "https://github.com/openai/codex/pull/27085", + "meta": "Merged 2026-06-09T04:39:35Z" + }, + { + "kind": "commit", + "title": "Use server app auth requirements for remote plugin install", + "url": "https://github.com/openai/codex/commit/df5b449689130d0e0dbd65ab882aa784a9c02028" + } + ] + }, + "reviewed_at": "2026-06-10T08:05:59Z", + "observed_change": "Codex remote plugin install now asks the plugin service for app IDs requiring authentication and returns those IDs through the app-server install response.", + "changed_surfaces": [ + "remote plugin install mutation request", + "remote plugin install response parsing", + "app-server plugin install `appsNeedingAuth` response behavior", + "remote plugin install tests" + ], + "user_visible_path": "App-server clients installing a remote plugin can receive backend-provided `appsNeedingAuth` values without a separate accessible-apps refetch, while older backend responses still fall back to the previous behavior.", + "control_plane_relevance": "High for Decodex plugin install flows because auth-required app IDs can come directly from the install mutation response.", + "compatibility_risk": "Low to moderate; clients that already accept `appsNeedingAuth` should benefit, but install-result handling should tolerate both populated and absent backend `app_ids_needing_auth` fields.", + "adoption_opportunity": "Use the server-provided app IDs to guide post-install auth prompts and avoid redundant connector/app refetches when upstream provides the field.", + "community_value": "Medium to high for remote plugin operators because it makes auth follow-up after install more direct and backend-authoritative.", + "deprecated_or_breaking_notes": "No removal was found; the PR keeps a fallback for older backend responses that omit `app_ids_needing_auth`.", + "confidence": "confirmed", + "evidence": [ + "PR #27085 says remote install requests set `includeAppsNeedingAuth=true`, return backend-provided `app_ids_needing_auth`, and use those IDs to populate `appsNeedingAuth` with fallback for older responses.", + "codex-rs/core-plugins/src/remote.rs adds `app_ids_needing_auth` to the remote mutation response and returns `RemotePluginInstallResult` from `install_remote_plugin`.", + "codex-rs/app-server/src/request_processors/plugins.rs captures the install result and uses backend app IDs when building the install response.", + "codex-rs/app-server/tests/suite/v2/plugin_install.rs adds `plugin_install_uses_remote_apps_needing_auth_response` coverage.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27085.json records the app-server and core-plugin changed files for the merged PR." + ], + "caveats": "The public source bundle does not show the production backend contract beyond the PR and tests; confidence is confirmed for client-side request/response handling in Codex.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Remote plugin install auth metadata affects Decodex Control Plane plugin install UX and follow-up prompts." + }, + { + "type": "social_candidate", + "reason": "The install response behavior is concrete enough for a public operator note." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27191.review.json b/artifacts/github/reviews/openai-codex-pr-27191.review.json new file mode 100644 index 000000000..33cb84553 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27191.review.json @@ -0,0 +1,77 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27191", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27191", + "commit_shas": [ + "04552f5663aa09b2363f8245b8d814d8bdfdc557", + "20b507e3783731f6ecda1ea1ed8197e286891158", + "648f66285f4ce14fd12e11c53386f19476b0d44b" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Route hosted Apps MCP through extensions", + "url": "https://github.com/openai/codex/pull/27191", + "meta": "Merged 2026-06-09T20:44:16Z" + }, + { + "kind": "commit", + "title": "Route hosted Apps MCP through extensions", + "url": "https://github.com/openai/codex/commit/04552f5663aa09b2363f8245b8d814d8bdfdc557" + }, + { + "kind": "commit", + "title": "Fix runtime MCP API consistency", + "url": "https://github.com/openai/codex/commit/20b507e3783731f6ecda1ea1ed8197e286891158" + }, + { + "kind": "commit", + "title": "Fix runtime MCP server consistency", + "url": "https://github.com/openai/codex/commit/648f66285f4ce14fd12e11c53386f19476b0d44b" + } + ] + }, + "reviewed_at": "2026-06-10T08:05:59Z", + "observed_change": "Codex app-server now installs a hosted Apps MCP extension and resolves MCP APIs through runtime and effective MCP views instead of relying only on configured servers or the legacy Apps loader.", + "changed_surfaces": [ + "codex-extension-api MCP contributor contract", + "new codex-mcp-extension crate", + "app-server extension installation", + "MCP runtime, effective, and configured server view selection", + "Apps, plugin, connector, OAuth, refresh, and thread-start MCP request paths" + ], + "user_visible_path": "App-server users with Apps and ChatGPT auth can get the reserved `codex_apps` streamable HTTP MCP server through extension contribution, including the `/backend-api/ps/mcp` endpoint when the Apps MCP path override is enabled.", + "control_plane_relevance": "High; Decodex app-server and plugin orchestration should distinguish configured, runtime, and effective MCP views and should expect extension-contributed MCP servers to affect thread startup, refresh, status, connector discovery, resource reads, and OAuth lookup.", + "compatibility_risk": "Moderate to high for integrations that inspect only configured MCP servers or assume the Apps MCP server is always synthesized by the legacy loader; app-server paths now use runtime or effective views depending on auth gating.", + "adoption_opportunity": "Adopt the runtime/effective MCP view split in Decodex Control Plane logic and treat extension-contributed hosted MCP servers as first-class app-server state.", + "community_value": "High for operators tracking hosted plugin and MCP activation because this moves Apps MCP behind the extension model while preserving the local fallback.", + "deprecated_or_breaking_notes": "No user-facing field removal was found, but the behavior boundary changes: hosts that install the MCP extension disable the legacy Apps MCP loader for that server, while hosts without the extension retain the previous synthesis path.", + "confidence": "confirmed", + "evidence": [ + "PR #27191 states `codex-extension-api` gains an ordered `McpServerContributor` contract and that app-server installs the hosted MCP extension.", + "codex-rs/ext/extension-api/src/contributors/mcp.rs adds `McpServerContribution::Set` and `Remove` overlays keyed by MCP server name.", + "codex-rs/ext/mcp/src/lib.rs contributes the reserved `CODEX_APPS_MCP_SERVER_NAME` when Apps is enabled and removes it when Apps is disabled.", + "codex-rs/app-server/src/extensions.rs installs `codex_mcp_extension` into the app-server extension registry.", + "codex-rs/core/src/mcp.rs adds extension-backed `runtime_servers`, `effective_servers`, and runtime config handling on `McpManager`.", + "codex-rs/app-server/src/mcp_refresh.rs switches refresh config from `configured_servers` to `runtime_servers`.", + "codex-rs/app-server/src/request_processors/mcp_processor.rs uses `effective_servers` for resource access and OAuth lookup so auth-gated servers are consistent.", + "codex-rs/ext/mcp/tests/hosted_apps_mcp.rs covers hosted Apps MCP contribution without an executor, ChatGPT auth gating, and Apps-disabled removal.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27191.json records 28 changed files for the merged PR." + ], + "caveats": "The PR deliberately does not introduce a generic plugin activation framework; later verticals are expected to cover manifest-declared stdio MCP servers and additional plugin component types.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The runtime/effective MCP view split and extension-contributed Apps MCP server affect Decodex Control Plane compatibility and adoption planning." + }, + { + "type": "social_candidate", + "reason": "The hosted Apps MCP extension path has a clear operator-facing public angle." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27223.review.json b/artifacts/github/reviews/openai-codex-pr-27223.review.json new file mode 100644 index 000000000..bfe716bb4 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27223.review.json @@ -0,0 +1,59 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27223", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27223", + "commit_shas": [ + "f0566d8a72018d542d5cd745825a478d4d21b401" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "fix: use plugin service route for remote uninstall", + "url": "https://github.com/openai/codex/pull/27223", + "meta": "Merged 2026-06-09T19:52:06Z" + }, + { + "kind": "commit", + "title": "fix: use plugin service route for remote uninstall", + "url": "https://github.com/openai/codex/commit/f0566d8a72018d542d5cd745825a478d4d21b401" + } + ] + }, + "reviewed_at": "2026-06-10T08:05:59Z", + "observed_change": "Codex remote plugin uninstall now posts to the plugin-service route under `/ps/plugins/{plugin_id}/uninstall` instead of `/plugins/{plugin_id}/uninstall`.", + "changed_surfaces": [ + "remote plugin uninstall mutation route", + "app-server remote plugin uninstall request assertions", + "core plugin remote uninstall client" + ], + "user_visible_path": "App-server remote plugin uninstall requests should now reach the backend route expected by the plugin service, allowing remote uninstall state to update successfully.", + "control_plane_relevance": "High for Decodex remote plugin management because uninstall success depends on the upstream service route used by the Codex client.", + "compatibility_risk": "Low for app-server clients because the client-facing uninstall command is unchanged; the risk is mainly for code or tests that mirrored the old backend route directly.", + "adoption_opportunity": "Align any Decodex remote-plugin mocks, diagnostics, or backend-route assumptions with the `/ps/plugins/{plugin_id}/uninstall` route.", + "community_value": "Medium for remote plugin operators because it fixes a concrete uninstall failure path.", + "deprecated_or_breaking_notes": "The old backend mutation route is replaced in Codex client code and tests; no app-server client method removal was found.", + "confidence": "confirmed", + "evidence": [ + "PR #27223 says remote plugin uninstall requests were sent to `/plugins/{plugin_id}/uninstall` but the plugin service expects `/ps/plugins/{plugin_id}/uninstall`.", + "codex-rs/core-plugins/src/remote.rs changes the uninstall URL format to `{base_url}/ps/plugins/{plugin_id}/uninstall`.", + "codex-rs/app-server/tests/suite/v2/plugin_uninstall.rs updates mock expectations and request-count assertions to `/backend-api/ps/plugins/{REMOTE_PLUGIN_ID}/uninstall`.", + "The PR body reports production verification with a locally built app-server where uninstall succeeded and the follow-up plugin list showed `installed: false` and `enabled: false`.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27223.json records the core remote client and app-server uninstall test changes for the merged PR." + ], + "caveats": "The source-backed behavior change is narrow: it fixes the backend route for uninstall and does not change the app-server public command shape.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Remote plugin management diagnostics and mocks should track the corrected plugin-service route." + }, + { + "type": "social_candidate", + "reason": "The uninstall route fix has a concise operator-facing public angle." + } + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27085.json b/artifacts/github/social-candidates/openai-codex-pr-27085.json new file mode 100644 index 000000000..31afdefc1 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27085.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27085", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "normal", + "audience": "Codex remote plugin operators", + "candidate_text": [ + "Codex remote plugin install now asks the plugin service for app IDs that need auth and returns them as `appsNeedingAuth`, with fallback for older backend responses. PR: https://github.com/openai/codex/pull/27085" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27085.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27085.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27085" + ] + }, + "evidence_notes": [ + "Remote install requests include backend app-auth requirement metadata.", + "The remote install client returns optional `app_ids_needing_auth`.", + "App-server uses backend-provided IDs to populate `appsNeedingAuth`.", + "Tests cover the remote apps-needing-auth install response." + ], + "claims": [ + { + "text": "Remote plugin install can return server-provided app IDs that need auth.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27085.review.json", + "confidence": "confirmed" + }, + { + "text": "Older backend responses are still handled through fallback behavior.", + "evidence": "artifacts/github/impact/openai-codex-pr-27085.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR gives remote plugin operators a more direct post-install auth path.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27085:operator_impact" + }, + "caveats": [ + "The backend field is optional.", + "Do not imply that every remote plugin install requires app auth." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27191.json b/artifacts/github/social-candidates/openai-codex-pr-27191.json new file mode 100644 index 000000000..809dfab4c --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27191.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27191", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "high", + "audience": "Codex MCP, plugin, and app-server operators", + "candidate_text": [ + "Codex is routing hosted Apps MCP through extension contributions: app-server installs `codex-mcp-extension`, resolves runtime/effective MCP views, and keeps ChatGPT auth gating for `codex_apps`. PR: https://github.com/openai/codex/pull/27191" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27191.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27191.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27191" + ] + }, + "evidence_notes": [ + "`McpServerContributor` adds extension-owned set/remove overlays for MCP server config.", + "App-server installs `codex_mcp_extension` and uses runtime/effective MCP views.", + "The hosted Apps MCP contribution remains Apps and ChatGPT-auth gated.", + "Tests cover contribution without an executor, auth gating, and Apps-disabled removal." + ], + "claims": [ + { + "text": "Hosted Apps MCP is now contributed through the extension model in app-server.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27191.review.json", + "confidence": "confirmed" + }, + { + "text": "App-server MCP APIs now distinguish configured, runtime, and effective server views.", + "evidence": "artifacts/github/impact/openai-codex-pr-27191.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes a concrete hosted MCP/app-server operator path and carries Control Plane compatibility risk.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27191:operator_impact" + }, + "caveats": [ + "This PR does not introduce a generic plugin activation framework.", + "Generic manifest-declared stdio MCP activation is left to a later vertical." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27223.json b/artifacts/github/social-candidates/openai-codex-pr-27223.json new file mode 100644 index 000000000..de4f0a129 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27223.json @@ -0,0 +1,53 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27223", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "normal", + "audience": "Codex remote plugin operators", + "candidate_text": [ + "Codex remote plugin uninstall now targets the plugin service route `/ps/plugins/{id}/uninstall`, matching backend expectations and app-server tests. PR: https://github.com/openai/codex/pull/27223" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27223.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27223.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27223" + ] + }, + "evidence_notes": [ + "The remote uninstall client now posts to `/ps/plugins/{plugin_id}/uninstall`.", + "App-server uninstall tests assert the corrected backend route.", + "The PR body reports production verification of uninstall success.", + "The app-server client-facing uninstall command shape is unchanged." + ], + "claims": [ + { + "text": "Remote plugin uninstall now uses the plugin-service backend route.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27223.review.json", + "confidence": "confirmed" + }, + { + "text": "The app-server public uninstall command shape is unchanged.", + "evidence": "artifacts/github/impact/openai-codex-pr-27223.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR fixes a concrete remote plugin uninstall path operators may hit.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27223:operator_impact" + }, + "caveats": [ + "This is a backend-route fix, not a new app-server client command." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +}