From d45a644c69ca14e01021b36c60074b56941048c8 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sun, 14 Jun 2026 02:13:41 +0800 Subject: [PATCH 1/2] {"schema":"decodex/commit/1","summary":"Persist upstream Radar review artifacts","authority":"manual"} --- .../github/bundles/openai-codex-pr-27541.json | 253 +++++++++++ .../github/bundles/openai-codex-pr-27830.json | 404 ++++++++++++++++++ .../github/bundles/openai-codex-pr-27961.json | 279 ++++++++++++ .../github/impact/openai-codex-pr-27541.json | 48 +++ .../github/impact/openai-codex-pr-27830.json | 47 ++ .../github/impact/openai-codex-pr-27961.json | 47 ++ .../review-queue/openai-codex-latest.json | 279 ++++++------ .../reviews/openai-codex-pr-27541.review.json | 67 +++ .../reviews/openai-codex-pr-27830.review.json | 80 ++++ .../reviews/openai-codex-pr-27961.review.json | 75 ++++ .../openai-codex-pr-27541.json | 53 +++ .../openai-codex-pr-27830.json | 53 +++ .../openai-codex-pr-27961.json | 53 +++ 13 files changed, 1612 insertions(+), 126 deletions(-) create mode 100644 artifacts/github/bundles/openai-codex-pr-27541.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27830.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27961.json create mode 100644 artifacts/github/impact/openai-codex-pr-27541.json create mode 100644 artifacts/github/impact/openai-codex-pr-27830.json create mode 100644 artifacts/github/impact/openai-codex-pr-27961.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27541.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27830.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27961.review.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27541.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27830.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27961.json diff --git a/artifacts/github/bundles/openai-codex-pr-27541.json b/artifacts/github/bundles/openai-codex-pr-27541.json new file mode 100644 index 000000000..652a948a0 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27541.json @@ -0,0 +1,253 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "celia-oai", + "committed_at": "2026-06-11T04:35:26Z", + "message": "Use encrypted local secrets for MCP OAuth", + "sha": "bc08fad8d62d48b248a86b6183ebb1ecc44d786c", + "url": "https://github.com/openai/codex/commit/bc08fad8d62d48b248a86b6183ebb1ecc44d786c" + }, + { + "author": "celia-oai", + "committed_at": "2026-06-12T21:42:18Z", + "message": "changes", + "sha": "78daf298b0ea9ea7a5fddad51af4545cb05d2236", + "url": "https://github.com/openai/codex/commit/78daf298b0ea9ea7a5fddad51af4545cb05d2236" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "MCP", + "CLI", + "CONFIG_TOML_FILE", + "URI", + "INITIAL_SUBMIT_ID", + "DISCOVERY_TIMEOUT", + "CODEX_RMCP_CLIENT_AUTH_STATUS_TEST_TOKEN", + "KEYRING_SERVICE", + "MCP_OAUTH_SECRET_PREFIX", + "MCP_OAUTH", + "REFRESH_SKEW_MILLIS", + "FALLBACK_FILENAME", + "AUTHORIZATION", + "UNIX_EPOCH", + "SERVER_NAME", + "UNREFRESHABLE_SERVER_URL", + "UNEXPIRED_SERVER_URL", + "REFRESHABLE_SERVER_URL" + ], + "files": [ + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -3660,6 +3660,7 @@ dependencies = [\n \"codex-exec-server\",\n \"codex-keyring-store\",\n \"codex-protocol\",\n+ \"codex-secrets\",\n \"codex-utils-cargo-bin\",\n \"codex-utils-home-dir\",\n \"codex-utils-pty\",", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 39, + "deletions": 0, + "patch_excerpt": "@@ -71,6 +71,8 @@ async fn build_refresh_config(\n config.mcp_oauth_credentials_store_mode,\n )\n .map_err(io::Error::other)?,\n+ auth_keyring_backend_kind: serde_json::to_value(config.auth_keyring_backend_kind())\n+ .map_err(io::Error::other)?,\n })\n }\n \n@@ -104,6 +106,7 @@ mod tests {\n use codex_config::ThreadConfigLoadErrorCode;\n use codex_config::ThreadConfigLoader;\n use codex_config::ThreadConfigSource;\n+ use codex_config::types::AuthKeyringBackendKind;\n use codex_core::config::ConfigOverrides;\n use codex_core::init_state_db;\n use codex_core::thread_store_from_config;\n@@ -142,6 +145,38 @@ mod tests {\n Ok(())\n }\n \n+ #[tokio::test]\n+ async fn refresh_config_uses_latest_auth_keyring_backend() -> anyhow::Result<()> {\n+ let (temp_dir, thread_manager, config_manager, _loader) = refresh_test_sta...", + "path": "codex-rs/app-server/src/mcp_refresh.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -161,6 +161,7 @@ impl McpRequestProcessor {\n &name,\n &url,\n config.mcp_oauth_credentials_store_mode,\n+ config.auth_keyring_backend_kind(),\n http_headers,\n env_http_headers,\n &resolved_scopes.scopes,", + "path": "codex-rs/app-server/src/request_processors/mcp_processor.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -1701,6 +1701,7 @@ impl PluginRequestProcessor {\n );\n \n let store_mode = config.mcp_oauth_credentials_store_mode;\n+ let keyring_backend_kind = config.auth_keyring_backend_kind();\n let callback_port = config.mcp_oauth_callback_port;\n let callback_url = config.mcp_oauth_callback_url.clone();\n let outgoing = Arc::clone(&self.outgoing);\n@@ -1712,6 +1713,7 @@ impl PluginRequestProcessor {\n &name,\n &oauth_config.url,\n store_mode,\n+ keyring_backend_kind,\n oauth_config.http_headers.clone(),\n oauth_config.env_http_headers.clone(),\n &resolved_scopes.scopes,\n@@ -1728,6 +1730,7 @@ impl PluginRequestProcessor {\n &name,\n &oauth_config.ur...", + "path": "codex-rs/app-server/src/request_processors/plugins.rs", + "status": "modified" + }, + { + "additions": 12, + "deletions": 1, + "patch_excerpt": "@@ -211,6 +211,7 @@ async fn perform_oauth_login_retry_without_scopes(\n name: &str,\n url: &str,\n store_mode: codex_config::types::OAuthCredentialsStoreMode,\n+ keyring_backend_kind: codex_config::types::AuthKeyringBackendKind,\n http_headers: Option>,\n env_http_headers: Option>,\n resolved_scopes: &ResolvedMcpOAuthScopes,\n@@ -223,6 +224,7 @@ async fn perform_oauth_login_retry_without_scopes(\n name,\n url,\n store_mode,\n+ keyring_backend_kind,\n http_headers.clone(),\n env_http_headers.clone(),\n &resolved_scopes.scopes,\n@@ -240,6 +242,7 @@ async fn perform_oauth_login_retry_without_scopes(\n name,\n url,\n store_mode,\n+ keyring_backend_kind,\n http_headers,\n env_http_headers,\n ...", + "path": "codex-rs/cli/src/mcp_cmd.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -42,6 +42,7 @@ use anyhow::anyhow;\n use async_channel::Sender;\n use codex_config::Constrained;\n use codex_config::McpServerTransportConfig;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n use codex_login::CodexAuth;\n use codex_protocol::mcp::CallToolResult;\n@@ -119,6 +120,7 @@ impl McpConnectionManager {\n pub async fn new(\n mcp_servers: &HashMap,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_kind: AuthKeyringBackendKind,\n auth_entries: HashMap,\n approval_policy: &Constrained,\n submit_id: String,\n@@ -198,6 +200,7 @@ impl McpConnectionManager {\n server_name.clone(),\n server,\n store_mode,\n+ keyring_backend_kind,\n cancel_to...", + "path": "codex-rs/codex-mcp/src/connection_manager.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -20,6 +20,7 @@ use crate::tools::normalize_tools_for_model_with_prefix;\n use crate::tools::tool_with_model_visible_input_schema;\n use codex_config::Constrained;\n use codex_config::McpServerConfig;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_exec_server::EnvironmentManager;\n use codex_protocol::ToolName;\n use codex_protocol::mcp::McpServerInfo;\n@@ -1213,6 +1214,7 @@ async fn no_local_runtime_fails_local_stdio_but_keeps_local_http_server() {\n let manager = McpConnectionManager::new(\n &mcp_servers,\n OAuthCredentialsStoreMode::default(),\n+ AuthKeyringBackendKind::default(),\n HashMap::new(),\n &approval_policy,\n String::new(),", + "path": "codex-rs/codex-mcp/src/connection_manager_tests.rs", + "status": "modified" + }, + { + "additions": 13, + "deletions": 1, + "patch_excerpt": "@@ -3,6 +3,7 @@ use std::collections::HashMap;\n use anyhow::Result;\n use codex_config::McpServerConfig;\n use codex_config::McpServerTransportConfig;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n use codex_login::CodexAuth;\n use codex_protocol::protocol::McpAuthStatus;\n@@ -130,6 +131,7 @@ pub fn should_retry_without_scopes(scopes: &ResolvedMcpOAuthScopes, error: &anyh\n pub async fn compute_auth_statuses<'a, I>(\n servers: I,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_kind: AuthKeyringBackendKind,\n auth: Option<&CodexAuth>,\n ) -> HashMap\n where\n@@ -152,7 +154,15 @@ where\n async move {\n let auth_status = match config.as_ref() {\n Some(config) => {\n- match compute_auth_status(&name, config, store_mode, has_runtime_auth).await...", + "path": "codex-rs/codex-mcp/src/mcp/auth.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 0, + "patch_excerpt": "@@ -21,6 +21,7 @@ use codex_config::Constrained;\n use codex_config::McpServerConfig;\n use codex_config::McpServerTransportConfig;\n use codex_config::types::AppToolApproval;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n use codex_login::CodexAuth;\n use codex_plugin::PluginCapabilitySummary;\n@@ -114,6 +115,8 @@ pub struct McpConfig {\n pub codex_home: PathBuf,\n /// Preferred credential store for MCP OAuth tokens.\n pub mcp_oauth_credentials_store_mode: OAuthCredentialsStoreMode,\n+ /// Backend used when MCP OAuth storage is configured for keyring-backed persistence.\n+ pub auth_keyring_backend_kind: AuthKeyringBackendKind,\n /// Optional fixed localhost callback port for MCP OAuth login.\n pub mcp_oauth_callback_port: Option,\n /// Optional OAuth redirect URI override for MCP login.\n@@ -262,6 +265,7 @@ pu...", + "path": "codex-rs/codex-mcp/src/mcp/mod.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -2,6 +2,7 @@ use super::*;\n use crate::McpServerRegistration;\n use codex_config::Constrained;\n use codex_config::types::AppToolApproval;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_login::CodexAuth;\n use codex_plugin::AppConnectorId;\n use codex_plugin::PluginCapabilitySummary;\n@@ -20,6 +21,7 @@ fn test_mcp_config(codex_home: PathBuf) -> McpConfig {\n apps_mcp_product_sku: None,\n codex_home,\n mcp_oauth_credentials_store_mode: OAuthCredentialsStoreMode::default(),\n+ auth_keyring_backend_kind: AuthKeyringBackendKind::default(),\n mcp_oauth_callback_port: None,\n mcp_oauth_callback_url: None,\n skill_mcp_dependency_install_enabled: true,", + "path": "codex-rs/codex-mcp/src/mcp/mod_tests.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 0, + "patch_excerpt": "@@ -45,6 +45,7 @@ use codex_async_utils::CancelErr;\n use codex_async_utils::OrCancelExt;\n use codex_config::McpServerConfig;\n use codex_config::McpServerTransportConfig;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n use codex_exec_server::HttpClient;\n use codex_exec_server::ReqwestHttpClient;\n@@ -141,6 +142,7 @@ impl AsyncManagedClient {\n server_name: String,\n server: EffectiveMcpServer,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_kind: AuthKeyringBackendKind,\n cancel_token: CancellationToken,\n tx_event: Sender,\n elicitation_requests: ElicitationRequestManager,\n@@ -179,6 +181,7 @@ impl AsyncManagedClient {\n &server_name,\n server.clone(),\n store_mode,\n+ keyring_ba...", + "path": "codex-rs/codex-mcp/src/rmcp_client.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -1443,6 +1443,7 @@ impl Config {\n apps_mcp_product_sku: self.apps_mcp_product_sku.clone(),\n codex_home: self.codex_home.to_path_buf(),\n mcp_oauth_credentials_store_mode: self.mcp_oauth_credentials_store_mode,\n+ auth_keyring_backend_kind: self.auth_keyring_backend_kind(),\n mcp_oauth_callback_port: self.mcp_oauth_callback_port,\n mcp_oauth_callback_url: self.mcp_oauth_callback_url.clone(),\n skill_mcp_dependency_install_enabled: self", + "path": "codex-rs/core/src/config/mod.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -280,6 +280,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_mcp_manager(\n let auth_status_entries = compute_auth_statuses(\n mcp_servers.iter(),\n config.mcp_oauth_credentials_store_mode,\n+ config.auth_keyring_backend_kind(),\n auth.as_ref(),\n )\n .await;\n@@ -291,6 +292,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_mcp_manager(\n let mcp_connection_manager = McpConnectionManager::new(\n &mcp_servers,\n config.mcp_oauth_credentials_store_mode,\n+ config.auth_keyring_backend_kind(),\n auth_status_entries,\n &config.permissions.approval_policy,\n INITIAL_SUBMIT_ID.to_owned(),", + "path": "codex-rs/core/src/connectors.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -152,6 +152,7 @@ pub(crate) async fn maybe_install_mcp_dependencies(\n &name,\n &oauth_config.url,\n config.mcp_oauth_credentials_store_mode,\n+ config.auth_keyring_backend_kind(),\n oauth_config.http_headers.clone(),\n oauth_config.env_http_headers.clone(),\n &resolved_scopes.scopes,\n@@ -168,6 +169,7 @@ pub(crate) async fn maybe_install_mcp_dependencies(\n &name,\n &oauth_config.url,\n config.mcp_oauth_credentials_store_mode,\n+ config.auth_keyring_backend_kind(),\n oauth_config.http_headers,\n oauth_config.env_http_headers,\n &[],\n@@ -202,6 +204,7 @@ pub(crate) async fn maybe_install_mcp_dependencies(\n turn_context,\n refresh_servers,\n config.mcp_oauth_cre...", + "path": "codex-rs/core/src/mcp_skill_dependencies.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -1260,6 +1260,7 @@ async fn install_host_owned_codex_apps_manager(session: &Session, turn_context:\n let manager = codex_mcp::McpConnectionManager::new(\n &HashMap::new(),\n turn_context.config.mcp_oauth_credentials_store_mode,\n+ turn_context.config.auth_keyring_backend_kind(),\n HashMap::new(),\n &turn_context.approval_policy,\n turn_context.sub_id.clone(),", + "path": "codex-rs/core/src/mcp_tool_call_tests.rs", + "status": "modified" + }, + { + "additions": 35, + "deletions": 6, + "patch_excerpt": "@@ -299,6 +299,7 @@ impl Session {\n turn_context: &TurnContext,\n mcp_servers: HashMap,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_kind: AuthKeyringBackendKind,\n elicitation_reviewer: Option,\n ) {\n let auth = self.services.auth_manager.auth().await;\n@@ -309,8 +310,13 @@ impl Session {\n effective_mcp_servers_from_configured(mcp_servers, &mcp_config, auth.as_ref());\n let host_owned_codex_apps_enabled =\n host_owned_codex_apps_enabled(&mcp_config, auth.as_ref());\n- let auth_statuses =\n- compute_auth_statuses(mcp_servers.iter(), store_mode, auth.as_ref()).await;\n+ let auth_statuses = compute_auth_statuses(\n+ mcp_servers.iter(),\n+ store_mode,\n+ keyring_backend_kind,\n+ auth.as_ref...", + "path": "codex-rs/core/src/session/mcp.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -50,6 +50,7 @@ use codex_analytics::SubAgentThreadStartedInput;\n use codex_analytics::TurnCodexErrorFact;\n use codex_app_server_protocol::McpServerElicitationRequest;\n use codex_app_server_protocol::McpServerElicitationRequestParams;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n use codex_exec_server::Environment;\n use codex_exec_server::EnvironmentManager;", + "path": "codex-rs/core/src/session/mod.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -614,6 +614,7 @@ impl Session {\n let auth_statuses = compute_auth_statuses(\n mcp_servers.iter(),\n config_for_mcp.mcp_oauth_credentials_store_mode,\n+ config_for_mcp.auth_keyring_backend_kind(),\n auth.as_ref(),\n )\n .await;\n@@ -1145,6 +1146,7 @@ impl Session {\n let mcp_connection_manager = McpConnectionManager::new(\n &mcp_servers,\n config.mcp_oauth_credentials_store_mode,\n+ config.auth_keyring_backend_kind(),\n auth_statuses,\n &session_configuration.approval_policy,\n INITIAL_SUBMIT_ID.to_owned(),", + "path": "codex-rs/core/src/session/session.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -7106,9 +7106,12 @@ async fn refresh_mcp_servers_is_deferred_until_next_turn() {\n \n let mcp_oauth_credentials_store_mode =\n serde_json::to_value(OAuthCredentialsStoreMode::Auto).expect(\"serialize store mode\");\n+ let auth_keyring_backend_kind =\n+ serde_json::to_value(AuthKeyringBackendKind::Secrets).expect(\"serialize keyring backend\");\n let refresh_config = McpServerRefreshConfig {\n mcp_servers: json!({}),\n mcp_oauth_credentials_store_mode,\n+ auth_keyring_backend_kind,\n };\n {\n let mut guard = session.pending_mcp_server_refresh_config.lock().await;", + "path": "codex-rs/core/src/session/tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -181,6 +181,7 @@ pub struct McpServerRefreshConfig {\n pub mcp_servers: Value,\n /// OAuth credential store mode to use with this server snapshot.\n pub mcp_oauth_credentials_store_mode: Value,\n+ pub auth_keyring_backend_kind: Value,\n }\n \n #[derive(Debug, Clone, PartialEq)]", + "path": "codex-rs/protocol/src/protocol.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -21,6 +21,7 @@ codex-config = { workspace = true }\n codex-exec-server = { workspace = true }\n codex-keyring-store = { workspace = true }\n codex-protocol = { workspace = true }\n+codex-secrets = { workspace = true }\n codex-utils-pty = { workspace = true }\n codex-utils-home-dir = { workspace = true }\n bytes = { workspace = true }", + "path": "codex-rs/rmcp-client/Cargo.toml", + "status": "modified" + }, + { + "additions": 5, + "deletions": 1, + "patch_excerpt": "@@ -16,6 +16,7 @@ use crate::oauth::StoredOAuthTokenStatus;\n use crate::oauth::oauth_token_status;\n use crate::utils::apply_default_headers;\n use crate::utils::build_default_headers;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n \n const DISCOVERY_TIMEOUT: Duration = Duration::from_secs(5);\n@@ -35,6 +36,7 @@ pub async fn determine_streamable_http_auth_status(\n http_headers: Option>,\n env_http_headers: Option>,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_kind: AuthKeyringBackendKind,\n ) -> Result {\n if bearer_token_env_var.is_some() {\n return Ok(McpAuthStatus::BearerToken);\n@@ -45,7 +47,7 @@ pub async fn determine_streamable_http_auth_status(\n return Ok(McpAuthStatus::BearerToken);\n }\n \n- match oauth_token_status(se...", + "path": "codex-rs/rmcp-client/src/auth_status.rs", + "status": "modified" + }, + { + "additions": 368, + "deletions": 23, + "patch_excerpt": "@@ -19,7 +19,13 @@\n use anyhow::Context;\n use anyhow::Error;\n use anyhow::Result;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n+use codex_secrets::LocalSecretsNamespace;\n+use codex_secrets::SecretName;\n+use codex_secrets::SecretScope;\n+use codex_secrets::SecretsBackendKind;\n+use codex_secrets::SecretsManager;\n use oauth2::AccessToken;\n use oauth2::RefreshToken;\n use oauth2::Scope;\n@@ -51,6 +57,7 @@ use tokio::sync::Mutex;\n use codex_utils_home_dir::find_codex_home;\n \n const KEYRING_SERVICE: &str = \"Codex MCP Credentials\";\n+const MCP_OAUTH_SECRET_PREFIX: &str = \"MCP_OAUTH\";\n const REFRESH_SKEW_MILLIS: u64 = 30_000;\n \n #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\n@@ -87,15 +94,19 @@ pub(crate) fn load_oauth_tokens(\n server_name: &str,\n url: &str,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_...", + "path": "codex-rs/rmcp-client/src/oauth.rs", + "status": "modified" + }, + { + "additions": 18, + "deletions": 1, + "patch_excerpt": "@@ -29,6 +29,7 @@ use crate::oauth::compute_expires_at_millis;\n use crate::save_oauth_tokens;\n use crate::utils::apply_default_headers;\n use crate::utils::build_default_headers;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n \n struct OauthHeaders {\n@@ -81,6 +82,7 @@ pub async fn perform_oauth_login(\n server_name: &str,\n server_url: &str,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_kind: AuthKeyringBackendKind,\n http_headers: Option>,\n env_http_headers: Option>,\n scopes: &[String],\n@@ -93,6 +95,7 @@ pub async fn perform_oauth_login(\n server_name,\n server_url,\n store_mode,\n+ keyring_backend_kind,\n http_headers,\n env_http_headers,\n scopes,\n@@ -110,6 +113,7 @@ pub async fn perform_oauth_login_s...", + "path": "codex-rs/rmcp-client/src/perform_oauth_login.rs", + "status": "modified" + }, + { + "additions": 9, + "deletions": 1, + "patch_excerpt": "@@ -13,6 +13,7 @@ use anyhow::Result;\n use anyhow::anyhow;\n use codex_api::SharedAuthProvider;\n use codex_client::maybe_build_rustls_client_config_with_custom_ca;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::McpServerEnvVar;\n use codex_exec_server::HttpClient;\n use futures::FutureExt;\n@@ -124,6 +125,7 @@ enum TransportRecipe {\n http_headers: Option>,\n env_http_headers: Option>,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring_backend_kind: AuthKeyringBackendKind,\n http_client: Arc,\n auth_provider: Option,\n },\n@@ -371,6 +373,7 @@ impl RmcpClient {\n http_headers: Option>,\n env_http_headers: Option>,\n store_mode: OAuthCredentialsStoreMode,\n+ keyring...", + "path": "codex-rs/rmcp-client/src/rmcp_client.rs", + "status": "modified" + }, + { + "additions": 27, + "deletions": 4, + "patch_excerpt": "@@ -4,6 +4,7 @@ use std::time::Duration;\n use std::time::SystemTime;\n use std::time::UNIX_EPOCH;\n \n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n use codex_exec_server::Environment;\n use codex_rmcp_client::McpAuthStatus;\n@@ -157,7 +158,12 @@ async fn persisted_credentials_auth_status_child() -> anyhow::Result<()> {\n token_response: WrappedOAuthTokenResponse(response),\n expires_at: Some(0),\n };\n- save_oauth_tokens(SERVER_NAME, &tokens, OAuthCredentialsStoreMode::File)?;\n+ save_oauth_tokens(\n+ SERVER_NAME,\n+ &tokens,\n+ OAuthCredentialsStoreMode::File,\n+ AuthKeyringBackendKind::default(),\n+ )?;\n \n let status = auth_status(UNREFRESHABLE_SERVER_URL).await?;\n assert_eq!(status, McpAuthStatus::NotLoggedIn);\n@@ -178,7 +184,12 @@ async fn persisted_credentials_auth_status_chil...", + "path": "codex-rs/rmcp-client/tests/streamable_http_oauth_startup.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -17,6 +17,7 @@ use std::time::Duration;\n use std::time::Instant;\n \n use anyhow::Context as _;\n+use codex_config::types::AuthKeyringBackendKind;\n use codex_config::types::OAuthCredentialsStoreMode;\n use codex_exec_server::Environment;\n use codex_exec_server::ExecServerClient;\n@@ -93,6 +94,7 @@ pub(crate) async fn create_client_with_http_client(\n /*http_headers*/ None,\n /*env_http_headers*/ None,\n OAuthCredentialsStoreMode::File,\n+ AuthKeyringBackendKind::default(),\n http_client,\n /*auth_provider*/ None,\n )\n@@ -136,6 +138,7 @@ pub(crate) async fn create_remote_client(\n /*http_headers*/ None,\n /*env_http_headers*/ None,\n OAuthCredentialsStoreMode::File,\n+ AuthKeyringBackendKind::default(),\n Arc::new(http_client),\n /*auth_provider*/ None,\n )", + "path": "codex-rs/rmcp-client/tests/streamable_http_test_support.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#27504", + "#27535", + "#27539", + "#17931" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Summary\n\n- store MCP OAuth credentials in the configured auth credential backend\n- support encrypted-local OAuth storage, including legacy keyring migration\n- propagate the credential backend through MCP refresh, session, CLI, and app-server paths\n\n## Stack\n\n1. #27504 — config and feature flag\n2. #27535 — auth-specific secret namespaces\n3. #27539 — encrypted CLI auth storage\n4. this PR — encrypted MCP OAuth storage\n\nThis is a parallel review stack; the original #17931 remains unchanged.\n\n## Tests\n\n- `just test -p codex-rmcp-client` (the transport round-trip test passed after building the required `codex` binary and retrying)\n- `just test -p codex-mcp`\n- `just test -p codex-app-server refresh_config_uses_latest_auth_keyring_backend`\n- `just test -p codex-core refresh_mcp_servers_is_deferred_until_next_turn`\n- `just test -p codex-cli mcp`\n- `just fix -p codex-rmcp-client -p codex-mcp -p codex-core -p codex-cli -p codex-app-server -p codex-protocol`\n- `just bazel-lock-check`\n", + "labels": [], + "merged_at": "2026-06-12T22:03:51Z", + "number": 27541, + "state": "merged", + "title": "feat: use encrypted local secrets for MCP OAuth", + "url": "https://github.com/openai/codex/pull/27541" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27830.json b/artifacts/github/bundles/openai-codex-pr-27830.json new file mode 100644 index 000000000..a718e18e1 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27830.json @@ -0,0 +1,404 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-12T09:37:44Z", + "message": "Support plaintext agent messages", + "sha": "175e330c022da6cb1b49d20351464a8e99b77d29", + "url": "https://github.com/openai/codex/commit/175e330c022da6cb1b49d20351464a8e99b77d29" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T11:58:05Z", + "message": "Narrow plaintext agent messages to requests", + "sha": "dea1baa7a97b7a072a00dadb87d54bce4858dade", + "url": "https://github.com/openai/codex/commit/dea1baa7a97b7a072a00dadb87d54bce4858dade" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T13:15:49Z", + "message": "Store inter-agent messages as typed history", + "sha": "73dde0c84f6cf2da38ec71a094b6c7a6282bf645", + "url": "https://github.com/openai/codex/commit/73dde0c84f6cf2da38ec71a094b6c7a6282bf645" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T14:09:58Z", + "message": "Preserve inter-agent messages in memory and traces", + "sha": "e62505d9da1a30546003ca7af0ca25f67bbf502b", + "url": "https://github.com/openai/codex/commit/e62505d9da1a30546003ca7af0ca25f67bbf502b" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T14:31:55Z", + "message": "Simplify typed agent message plumbing", + "sha": "a1d47af3a817a03973e53d7eed6467660981651a", + "url": "https://github.com/openai/codex/commit/a1d47af3a817a03973e53d7eed6467660981651a" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T14:39:58Z", + "message": "Merge branch 'main' into jif/plaintext-agent-messages", + "sha": "3d56c1105db26de4c47d7d0f890ddd6bc97f4f97", + "url": "https://github.com/openai/codex/commit/3d56c1105db26de4c47d7d0f890ddd6bc97f4f97" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T14:58:34Z", + "message": "Update pending input reasoning snapshot", + "sha": "029c6755406e172c41d4eb8456f265054968983a", + "url": "https://github.com/openai/codex/commit/029c6755406e172c41d4eb8456f265054968983a" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-12T15:30:56Z", + "message": "Preserve agent messages across transcript projections", + "sha": "9a685d4cabb0fa6370135ef2737ed731d5759033", + "url": "https://github.com/openai/codex/commit/9a685d4cabb0fa6370135ef2737ed731d5759033" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "JSON", + "LOCAL_FS", + "MAX", + "POST", + "ENVIRONMENT_CONTEXT", + "CWD", + "TURN_1_PROMPT", + "SPAWN_CALL_ID", + "TURN_2_NO_WAIT_PROMPT", + "ASSISTANT_ROLE", + "REDACTED_SECRET", + "API", + "FIRST" + ], + "files": [ + { + "additions": 20, + "deletions": 0, + "patch_excerpt": "@@ -36,6 +36,26 @@\n },\n \"AgentMessageInputContent\": {\n \"oneOf\": [\n+ {\n+ \"properties\": {\n+ \"text\": {\n+ \"type\": \"string\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"input_text\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContentType\",\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"text\",\n+ \"type\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContent\",\n+ \"type\": \"object\"\n+ },\n {\n \"properties\": {\n \"encrypted_content\": {", + "path": "codex-rs/app-server-protocol/schema/json/ClientRequest.json", + "status": "modified" + }, + { + "additions": 20, + "deletions": 0, + "patch_excerpt": "@@ -6054,6 +6054,26 @@\n },\n \"AgentMessageInputContent\": {\n \"oneOf\": [\n+ {\n+ \"properties\": {\n+ \"text\": {\n+ \"type\": \"string\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"input_text\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContentType\",\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"text\",\n+ \"type\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContent\",\n+ \"type\": \"object\"\n+ },\n {\n \"properties\": {\n \"encrypted_content\": {", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", + "status": "modified" + }, + { + "additions": 20, + "deletions": 0, + "patch_excerpt": "@@ -323,6 +323,26 @@\n },\n \"AgentMessageInputContent\": {\n \"oneOf\": [\n+ {\n+ \"properties\": {\n+ \"text\": {\n+ \"type\": \"string\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"input_text\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContentType\",\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"text\",\n+ \"type\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContent\",\n+ \"type\": \"object\"\n+ },\n {\n \"properties\": {\n \"encrypted_content\": {", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", + "status": "modified" + }, + { + "additions": 20, + "deletions": 0, + "patch_excerpt": "@@ -3,6 +3,26 @@\n \"definitions\": {\n \"AgentMessageInputContent\": {\n \"oneOf\": [\n+ {\n+ \"properties\": {\n+ \"text\": {\n+ \"type\": \"string\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"input_text\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContentType\",\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"text\",\n+ \"type\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContent\",\n+ \"type\": \"object\"\n+ },\n {\n \"properties\": {\n \"encrypted_content\": {", + "path": "codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json", + "status": "modified" + }, + { + "additions": 20, + "deletions": 0, + "patch_excerpt": "@@ -7,6 +7,26 @@\n },\n \"AgentMessageInputContent\": {\n \"oneOf\": [\n+ {\n+ \"properties\": {\n+ \"text\": {\n+ \"type\": \"string\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"input_text\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContentType\",\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"text\",\n+ \"type\"\n+ ],\n+ \"title\": \"InputTextAgentMessageInputContent\",\n+ \"type\": \"object\"\n+ },\n {\n \"properties\": {\n \"encrypted_content\": {", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -2,4 +2,4 @@\n \n // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.\n \n-export type AgentMessageInputContent = { \"type\": \"encrypted_content\", encrypted_content: string, };\n+export type AgentMessageInputContent = { \"type\": \"input_text\", text: string, } | { \"type\": \"encrypted_content\", encrypted_content: string, };", + "path": "codex-rs/app-server-protocol/schema/typescript/AgentMessageInputContent.ts", + "status": "modified" + }, + { + "additions": 3, + "deletions": 1, + "patch_excerpt": "@@ -241,7 +241,9 @@ impl ThreadHistoryBuilder {\n RolloutItem::EventMsg(event) => self.handle_event(event),\n RolloutItem::Compacted(payload) => self.handle_compacted(payload),\n RolloutItem::ResponseItem(item) => self.handle_response_item(item),\n- RolloutItem::TurnContext(_) | RolloutItem::SessionMeta(_) => {}\n+ RolloutItem::InterAgentCommunication(_)\n+ | RolloutItem::TurnContext(_)\n+ | RolloutItem::SessionMeta(_) => {}\n }\n }", + "path": "codex-rs/app-server-protocol/src/protocol/thread_history.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -55,6 +55,7 @@ fn keep_forked_rollout_item(item: &RolloutItem, preserve_reference_context_item:\n | ResponseItem::ContextCompaction { .. }\n | ResponseItem::Other,\n ) => false,\n+ RolloutItem::InterAgentCommunication(_) => false,\n // Full-history forks preserve the cached prompt prefix and can keep diffing\n // from the parent's durable baseline. Truncated forks drop part of that prompt,\n // so they must rebuild context on their first child turn.", + "path": "codex-rs/core/src/agent/control/spawn.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 21, + "patch_excerpt": "@@ -5,7 +5,6 @@ use codex_protocol::models::BaseInstructions;\n use codex_protocol::models::ContentItem;\n use codex_protocol::models::FunctionCallOutputContentItem;\n use codex_protocol::models::ResponseItem;\n-use codex_protocol::protocol::InterAgentCommunication;\n use codex_tools::ToolSpec;\n use futures::Stream;\n use serde_json::Value;\n@@ -55,30 +54,11 @@ impl Default for Prompt {\n }\n \n impl Prompt {\n- pub(crate) fn get_formatted_input(&self) -> Vec {\n- self.input\n- .iter()\n- .cloned()\n- .map(|item| {\n- let ResponseItem::Message { role, content, .. } = &item else {\n- return item;\n- };\n- if role != \"assistant\" {\n- return item;\n- }\n- InterAgentCommunication::from_message_content(content)\n- .filter(|commun...", + "path": "codex-rs/core/src/client_common.rs", + "status": "modified" + }, + { + "additions": 17, + "deletions": 0, + "patch_excerpt": "@@ -1,5 +1,6 @@\n use std::collections::HashMap;\n \n+use codex_protocol::models::AgentMessageInputContent;\n use codex_protocol::models::ResponseItem;\n use codex_protocol::protocol::GuardianRiskLevel;\n use codex_protocol::protocol::GuardianUserAuthorization;\n@@ -452,6 +453,22 @@ pub(crate) fn collect_guardian_transcript_entries(\n ResponseItem::Message { role, content, .. } if role == \"assistant\" => {\n content_entry(GuardianTranscriptEntryKind::Assistant, content)\n }\n+ ResponseItem::AgentMessage {\n+ author, content, ..\n+ } => {\n+ let text = content\n+ .iter()\n+ .filter_map(|content| match content {\n+ AgentMessageInputContent::InputText { text } => Some(text.as_str()),\n+ AgentMessageInputContent::EncryptedContent { .. } ...", + "path": "codex-rs/core/src/guardian/prompt.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 0, + "patch_excerpt": "@@ -528,6 +528,10 @@ pub(crate) async fn inspect_pending_input(\n should_stop: false,\n additional_contexts: Vec::new(),\n },\n+ TurnInput::InterAgentCommunication(_) => HookRuntimeOutcome {\n+ should_stop: false,\n+ additional_contexts: Vec::new(),\n+ },\n }\n }\n \n@@ -550,6 +554,10 @@ pub(crate) async fn record_pending_input(\n sess.record_conversation_items(turn_context, std::slice::from_ref(&item))\n .await;\n }\n+ TurnInput::InterAgentCommunication(communication) => {\n+ sess.record_inter_agent_communication(turn_context, communication)\n+ .await;\n+ }\n }\n record_additional_contexts(sess, turn_context, additional_contexts).await;\n }", + "path": "codex-rs/core/src/hook_runtime.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -98,5 +98,5 @@ pub(crate) async fn build_prompt_input_from_session(\n base_instructions,\n );\n \n- Ok(prompt.get_formatted_input())\n+ Ok(prompt.input)\n }", + "path": "codex-rs/core/src/prompt_debug.rs", + "status": "modified" + }, + { + "additions": 18, + "deletions": 0, + "patch_excerpt": "@@ -4,6 +4,7 @@ use crate::session::session::Session;\n use chrono::Utc;\n use codex_exec_server::LOCAL_FS;\n use codex_git_utils::resolve_root_git_project_for_trust;\n+use codex_protocol::models::AgentMessageInputContent;\n use codex_protocol::models::ResponseItem;\n use codex_thread_store::ListThreadsParams;\n use codex_thread_store::SortDirection;\n@@ -238,6 +239,23 @@ fn build_current_thread_section(items: &[ResponseItem]) -> Option {\n }\n current_assistant.push(text);\n }\n+ ResponseItem::AgentMessage {\n+ author, content, ..\n+ } => {\n+ let text = content\n+ .iter()\n+ .filter_map(|content| match content {\n+ AgentMessageInputContent::InputText { text } => Some(text.as_str()),\n+ AgentMessageInputContent::Encrypte...", + "path": "codex-rs/core/src/realtime_context.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -273,7 +273,7 @@ pub(super) async fn user_input_or_turn_inner(\n }\n }\n \n-/// Records an inter-agent assistant envelope, then lets the shared pending-work scheduler\n+/// Queues an inter-agent message, then lets the shared pending-work scheduler\n /// decide whether an idle session should start a regular turn.\n pub async fn inter_agent_communication(\n sess: &Arc,", + "path": "codex-rs/core/src/session/handlers.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 10, + "patch_excerpt": "@@ -16,6 +16,7 @@ pub(crate) enum TurnInput {\n client_id: Option,\n },\n ResponseItem(ResponseItem),\n+ InterAgentCommunication(InterAgentCommunication),\n }\n \n /// Turn-local pending input storage owned by the input queue flow.\n@@ -70,12 +71,12 @@ impl InputQueue {\n .any(|mail| mail.trigger_turn)\n }\n \n- pub(crate) async fn drain_mailbox_input_items(&self) -> Vec {\n+ pub(crate) async fn drain_mailbox_input_items(&self) -> Vec {\n self.mailbox_pending_mails\n .lock()\n .await\n .drain(..)\n- .map(|mail| ResponseItem::from(mail.to_response_input_item()))\n+ .map(TurnInput::InterAgentCommunication)\n .collect()\n }\n \n@@ -189,11 +190,7 @@ impl InputQueue {\n if !accepts_mailbox_delivery {\n return pending_input;\n }\n- ...", + "path": "codex-rs/core/src/session/input_queue.rs", + "status": "modified" + }, + { + "additions": 20, + "deletions": 0, + "patch_excerpt": "@@ -2659,6 +2659,26 @@ impl Session {\n self.send_raw_response_items(turn_context, items).await;\n }\n \n+ pub(crate) async fn record_inter_agent_communication(\n+ &self,\n+ turn_context: &TurnContext,\n+ communication: InterAgentCommunication,\n+ ) {\n+ let response_item = communication.to_model_input_item();\n+ let items = self.prepare_conversation_items_for_history(\n+ turn_context,\n+ std::slice::from_ref(&response_item),\n+ );\n+ let items = items.as_ref();\n+ {\n+ let mut state = self.state.lock().await;\n+ state.record_items(items.iter(), turn_context.truncation_policy);\n+ }\n+ self.persist_rollout_items(&[RolloutItem::InterAgentCommunication(communication)])\n+ .await;\n+ self.send_raw_response_items(turn_context, items).await;\n+ }\n+\n async fn m...", + "path": "codex-rs/core/src/session/mod.rs", + "status": "modified" + }, + { + "additions": 12, + "deletions": 0, + "patch_excerpt": "@@ -220,6 +220,11 @@ impl Session {\n active_segment.get_or_insert_with(ActiveReplaySegment::default);\n active_segment.counts_as_user_turn |= is_user_turn_boundary(response_item);\n }\n+ RolloutItem::InterAgentCommunication(_) => {\n+ let active_segment =\n+ active_segment.get_or_insert_with(ActiveReplaySegment::default);\n+ active_segment.counts_as_user_turn = true;\n+ }\n RolloutItem::EventMsg(_) | RolloutItem::SessionMeta(_) => {}\n }\n \n@@ -269,6 +274,13 @@ impl Session {\n turn_context.truncation_policy,\n );\n }\n+ RolloutItem::InterAgentCommunication(communication) => {\n+ let response_item = communication.to_model_input_item()...", + "path": "codex-rs/core/src/session/rollout_reconstruction.rs", + "status": "modified" + }, + { + "additions": 25, + "deletions": 0, + "patch_excerpt": "@@ -52,6 +52,31 @@ fn inter_agent_assistant_message(text: &str) -> ResponseItem {\n }\n }\n \n+#[tokio::test]\n+async fn record_initial_history_reconstructs_typed_inter_agent_message() {\n+ let (session, _turn_context) = make_session_and_context().await;\n+ let communication = InterAgentCommunication::new(\n+ AgentPath::root().join(\"worker\").expect(\"worker path\"),\n+ AgentPath::root(),\n+ Vec::new(),\n+ \"child done\".to_string(),\n+ /*trigger_turn*/ false,\n+ );\n+\n+ session\n+ .record_initial_history(InitialHistory::Resumed(ResumedHistory {\n+ conversation_id: ThreadId::default(),\n+ history: vec![RolloutItem::InterAgentCommunication(communication.clone())],\n+ rollout_path: Some(PathBuf::from(\"/tmp/resume.jsonl\")),\n+ }))\n+ .await;\n+\n+ assert_eq!(\n+ session.state.lock().await.clone_history()....", + "path": "codex-rs/core/src/session/rollout_reconstruction_tests.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 8, + "patch_excerpt": "@@ -9213,9 +9213,7 @@ async fn queue_only_mailbox_mail_waits_for_next_turn_after_answer_boundary() {\n \n assert_eq!(\n sess.input_queue.get_pending_input(&sess.active_turn).await,\n- vec![TurnInput::ResponseItem(ResponseItem::from(\n- communication.to_response_input_item()\n- ))],\n+ vec![TurnInput::InterAgentCommunication(communication)],\n );\n }\n \n@@ -9304,7 +9302,7 @@ async fn steered_input_reopens_mailbox_delivery_for_current_turn() {\n }],\n client_id: None\n },\n- TurnInput::ResponseItem(ResponseItem::from(communication.to_response_input_item())),\n+ TurnInput::InterAgentCommunication(communication),\n ],\n );\n }\n@@ -9362,7 +9360,7 @@ async fn stale_defer_mailbox_delivery_does_not_override_steered_input() {\n }],\n client_id: None\n ...", + "path": "codex-rs/core/src/session/tests.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -466,7 +466,7 @@ async fn build_skills_and_plugins(\n .iter()\n .filter_map(|item| match item {\n TurnInput::UserInput { content, .. } => Some(content.as_slice()),\n- TurnInput::ResponseItem(_) => None,\n+ TurnInput::ResponseItem(_) | TurnInput::InterAgentCommunication(_) => None,\n })\n .flatten()\n .cloned()\n@@ -685,7 +685,7 @@ async fn track_turn_resolved_config_analytics(\n .iter()\n .filter_map(|item| match item {\n TurnInput::UserInput { content, .. } => Some(content.as_slice()),\n- TurnInput::ResponseItem(_) => None,\n+ TurnInput::ResponseItem(_) | TurnInput::InterAgentCommunication(_) => None,\n })\n .flatten()\n .filter(|item| {", + "path": "codex-rs/core/src/session/turn.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -65,7 +65,7 @@ impl SessionTask for ReviewTask {\n for item in input {\n match item {\n TurnInput::UserInput { mut content, .. } => user_input.append(&mut content),\n- TurnInput::ResponseItem(_) => {}\n+ TurnInput::ResponseItem(_) | TurnInput::InterAgentCommunication(_) => {}\n }\n }", + "path": "codex-rs/core/src/tasks/review.rs", + "status": "modified" + }, + { + "additions": 9, + "deletions": 1, + "patch_excerpt": "@@ -19,6 +19,7 @@ pub(crate) fn initial_history_has_prior_user_turns(conversation_history: &Initia\n fn rollout_item_is_user_turn_boundary(item: &RolloutItem) -> bool {\n match item {\n RolloutItem::ResponseItem(item) => is_user_turn_boundary(item),\n+ RolloutItem::InterAgentCommunication(_) => true,\n _ => false,\n }\n }\n@@ -58,7 +59,8 @@ pub(crate) fn user_message_positions_in_rollout(items: &[RolloutItem]) -> Vec ResponseItem {\n communication.to_response_input_item().into()\n }\n \n+fn inter_agent_communication(text: &str, trigger_turn: bool) -> RolloutItem {\n+ RolloutItem::InterAgentCommunication(InterAgentCommunication::new(\n+ AgentPath::root(),\n+ AgentPath::try_from(\"/root/worker\").expect(\"agent path\"),\n+ Vec::new(),\n+ text.to_string(),\n+ trigger_turn,\n+ ))\n+}\n+\n #[test]\n fn truncates_rollout_from_start_before_nth_user_only() {\n let items = [\n@@ -208,6 +218,20 @@ fn truncates_rollout_to_last_n_fork_turns_counts_trigger_turn_messages() {\n );\n }\n \n+#[test]\n+fn fork_turn_positions_use_inter_agent_delivery_metadata() {\n+ let rollout = vec![\n+ RolloutItem::ResponseItem(user_msg(\"user task\")),\n+ inter_agent_communication(\"queued during user turn\", /*trigger_turn...", + "path": "codex-rs/core/src/thread_rollout_truncation_tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -14,4 +14,4 @@ Scenario: /responses POST bodies (input only, redacted like other suite snapshot\n 01:message/user:>\n 02:message/user:first prompt\n 03:message/assistant:first answer\n-04:message/assistant:{\"author\":\"/root/worker\",\"recipient\":\"/root\",\"other_recipients\":[],\"content\":\"queued child update\",\"trigger_turn\":false}\n+04:agent_message", + "path": "codex-rs/core/tests/suite/snapshots/all__suite__pending_input__pending_input_queued_mail_after_commentary.snap", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -14,4 +14,4 @@ Scenario: /responses POST bodies (input only, redacted like other suite snapshot\n 01:message/user:>\n 02:message/user:first prompt\n 03:reasoning:summary=thinking:encrypted=true\n-04:message/assistant:{\"author\":\"/root/worker\",\"recipient\":\"/root\",\"other_recipients\":[],\"content\":\"queued child update\",\"trigger_turn\":false}\n+04:agent_message", + "path": "codex-rs/core/tests/suite/snapshots/all__suite__pending_input__pending_input_queued_mail_after_reasoning.snap", + "status": "modified" + }, + { + "additions": 108, + "deletions": 0, + "patch_excerpt": "@@ -1102,6 +1102,114 @@ async fn encrypted_multi_agent_v2_spawn_sends_agent_message_to_child() -> Result\n Ok(())\n }\n \n+#[tokio::test(flavor = \"multi_thread\", worker_threads = 2)]\n+async fn plaintext_multi_agent_v2_completion_sends_agent_message() -> Result<()> {\n+ let server = start_mock_server().await;\n+ let spawn_args = serde_json::to_string(&json!({\n+ \"message\": \"opaque-encrypted-message\",\n+ \"task_name\": \"worker\",\n+ }))?;\n+ mount_sse_once_match(\n+ &server,\n+ |req: &wiremock::Request| body_contains(req, TURN_1_PROMPT),\n+ sse(vec![\n+ ev_response_created(\"resp-parent-1\"),\n+ ev_function_call(SPAWN_CALL_ID, \"spawn_agent\", &spawn_args),\n+ ev_completed(\"resp-parent-1\"),\n+ ]),\n+ )\n+ .await;\n+ let child_request = mount_response_once_match(\n+ &server,\n+ |req: &wiremock::Request| bod...", + "path": "codex-rs/core/tests/suite/subagent_notifications.rs", + "status": "modified" + }, + { + "additions": 23, + "deletions": 0, + "patch_excerpt": "@@ -1,6 +1,7 @@\n use codex_api::SearchInput;\n use codex_core::parse_turn_item;\n use codex_protocol::items::TurnItem;\n+use codex_protocol::models::AgentMessageInputContent;\n use codex_protocol::models::ContentItem;\n use codex_protocol::models::ResponseItem;\n use codex_tools::retain_tail_from_last_n_user_messages;\n@@ -30,6 +31,28 @@ fn push_visible_message(messages: &mut Vec, item: &ResponseItem) {\n ResponseItem::Message { role, .. } if role == ASSISTANT_ROLE => {\n messages.push(item.clone());\n }\n+ ResponseItem::AgentMessage {\n+ author, content, ..\n+ } => {\n+ let text = content\n+ .iter()\n+ .filter_map(|content| match content {\n+ AgentMessageInputContent::InputText { text } => Some(text.as_str()),\n+ AgentMessageInputContent::EncryptedContent { .. ...", + "path": "codex-rs/ext/web-search/src/history.rs", + "status": "modified" + }, + { + "additions": 41, + "deletions": 5, + "patch_excerpt": "@@ -405,12 +405,15 @@ mod job {\n ) -> codex_protocol::error::Result {\n let filtered = items\n .iter()\n- .filter_map(|item| {\n- if let RolloutItem::ResponseItem(item) = item {\n- sanitize_response_item_for_memories(item)\n- } else {\n- None\n+ .filter_map(|item| match item {\n+ RolloutItem::ResponseItem(item) => sanitize_response_item_for_memories(item),\n+ RolloutItem::InterAgentCommunication(communication) => {\n+ Some(communication.to_model_input_item())\n }\n+ RolloutItem::SessionMeta(_)\n+ | RolloutItem::Compacted(_)\n+ | RolloutItem::TurnContext(_)\n+ | RolloutItem::EventMsg(_) => None,\n })\n .collect::>();\n let ser...", + "path": "codex-rs/memories/write/src/phase1.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -719,6 +719,7 @@ pub enum ContentItem {\n #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)]\n #[serde(tag = \"type\", rename_all = \"snake_case\")]\n pub enum AgentMessageInputContent {\n+ InputText { text: String },\n EncryptedContent { encrypted_content: String },\n }", + "path": "codex-rs/protocol/src/models.rs", + "status": "modified" + }, + { + "additions": 15, + "deletions": 9, + "patch_excerpt": "@@ -526,7 +526,7 @@ pub enum Op {\n thread_settings: ThreadSettingsOverrides,\n },\n \n- /// Inter-agent communication that should be recorded as assistant history\n+ /// Inter-agent communication that should be recorded as agent-message history\n /// while still using the normal thread submission lifecycle.\n InterAgentCommunication {\n communication: InterAgentCommunication,\n@@ -712,15 +712,18 @@ impl InterAgentCommunication {\n }\n \n pub fn to_model_input_item(&self) -> ResponseItem {\n- match &self.encrypted_content {\n- Some(encrypted_content) => ResponseItem::AgentMessage {\n- author: self.author.to_string(),\n- recipient: self.recipient.to_string(),\n- content: vec![AgentMessageInputContent::EncryptedContent {\n- encrypted_content: encrypted_content.clone(),\n- ...", + "path": "codex-rs/protocol/src/protocol.rs", + "status": "modified" + }, + { + "additions": 13, + "deletions": 0, + "patch_excerpt": "@@ -3,6 +3,7 @@ use serde::Serialize;\n \n use crate::payload::RawPayloadId;\n \n+use super::AgentPath;\n use super::AgentThreadId;\n use super::CodeCellId;\n use super::CodexTurnId;\n@@ -32,13 +33,25 @@ pub struct ConversationItem {\n /// Codex channel for assistant/tool content, when the item is channel-specific.\n pub channel: Option,\n pub kind: ConversationItemKind,\n+ /// Routing metadata carried by a Responses `agent_message` item.\n+ #[serde(default, skip_serializing_if = \"Option::is_none\")]\n+ pub agent_message: Option,\n pub body: ConversationBody,\n /// Protocol/model `call_id` for function/custom tool call and output items.\n pub call_id: Option,\n /// Runtime or control-plane objects that caused this conversation item to exist.\n pub produced_by: Vec,\n }\n \n+/// Sender and destin...", + "path": "codex-rs/rollout-trace/src/model/conversation.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -322,6 +322,7 @@ impl TraceReducer {\n role: ConversationRole::Assistant,\n channel: None,\n kind: ConversationItemKind::CompactionMarker,\n+ agent_message: None,\n // The summary is a separate model/provider-visible item. Keep the marker body\n // empty so transcript renderers cannot mistake the boundary for prompt content.\n body: ConversationBody { parts: Vec::new() },\n@@ -419,6 +420,7 @@ impl TraceReducer {\n role: item.role,\n channel: item.channel,\n kind: item.kind,\n+ agent_message: item.agent_message,\n body: item.body,\n call_id: item.call_id,\n produced_by,\n@@ -579,6 +581,7 @@ fn conversation_item_matches(\n item.role == normalized.role\n && item.channel == no...", + "path": "codex-rs/rollout-trace/src/reducer/conversation.rs", + "status": "modified" + }, + { + "additions": 58, + "deletions": 0, + "patch_excerpt": "@@ -1,9 +1,13 @@\n //! Normalization from Responses-shaped JSON items into conversation item data.\n \n+use anyhow::Context;\n use anyhow::Result;\n use anyhow::bail;\n+use codex_protocol::models::AgentMessageInputContent;\n+use codex_protocol::models::ResponseItem;\n use serde_json::Value;\n \n+use crate::model::AgentMessageMetadata;\n use crate::model::ConversationBody;\n use crate::model::ConversationChannel;\n use crate::model::ConversationItemKind;\n@@ -22,6 +26,7 @@ pub(super) struct NormalizedConversationItem {\n pub(super) role: ConversationRole,\n pub(super) channel: Option,\n pub(super) kind: ConversationItemKind,\n+ pub(super) agent_message: Option,\n pub(super) body: ConversationBody,\n pub(super) call_id: Option,\n }\n@@ -58,11 +63,13 @@ fn normalize_model_item(\n };\n match item_type {\n \"message\" => normalize...", + "path": "codex-rs/rollout-trace/src/reducer/conversation/normalize.rs", + "status": "modified" + }, + { + "additions": 87, + "deletions": 0, + "patch_excerpt": "@@ -2,9 +2,12 @@ use pretty_assertions::assert_eq;\n use serde_json::json;\n use tempfile::TempDir;\n \n+use crate::model::AgentMessageMetadata;\n+use crate::model::ConversationBody;\n use crate::model::ConversationChannel;\n use crate::model::ConversationItemKind;\n use crate::model::ConversationPart;\n+use crate::model::ConversationRole;\n use crate::model::ExecutionStatus;\n use crate::model::ProducerRef;\n use crate::model::ToolCallKind;\n@@ -107,6 +110,90 @@ fn response_outputs_enter_thread_conversation_on_completion() -> anyhow::Result<\n Ok(())\n }\n \n+#[test]\n+fn agent_messages_preserve_routing_and_content() -> anyhow::Result<()> {\n+ let temp = TempDir::new()?;\n+ let writer = create_started_writer(&temp)?;\n+ start_turn(&writer, \"turn-1\")?;\n+\n+ let request = writer.write_json_payload(\n+ RawPayloadKind::InferenceRequest,\n+ &json!({\n+ \"input\": [\n+ ...", + "path": "codex-rs/rollout-trace/src/reducer/conversation_tests.rs", + "status": "modified" + }, + { + "additions": 59, + "deletions": 11, + "patch_excerpt": "@@ -33,6 +33,7 @@ pub(in crate::reducer) struct PendingAgentInteractionEdge {\n pub(in crate::reducer) kind: InteractionEdgeKind,\n pub(in crate::reducer) source: TraceAnchor,\n pub(in crate::reducer) target_thread_id: String,\n+ pub(in crate::reducer) message_author: String,\n pub(in crate::reducer) message_content: String,\n /// Spawn-only fallback for children that fail before their task message is model-visible.\n pub(in crate::reducer) unresolved_spawn_thread_id: Option,\n@@ -254,6 +255,7 @@ impl TraceReducer {\n format!(\"agent activity referenced unknown tool call {tool_call_id}\")\n })?;\n let started_at_unix_ms = tool_call.execution.started_at_unix_ms;\n+ let message_author = self.agent_path_for_thread(&tool_call.thread_id)?;\n let message_content = self.agent_message_content_from_invocation(tool_call_id)?;\n ...", + "path": "codex-rs/rollout-trace/src/reducer/tool/agents.rs", + "status": "modified" + }, + { + "additions": 21, + "deletions": 11, + "patch_excerpt": "@@ -246,18 +246,17 @@ fn sub_agent_started_activity_creates_spawn_edge() -> anyhow::Result<()> {\n )?;\n start_thread(&writer, child_thread_id, \"/root/reviewer\")?;\n start_turn_for_thread(&writer, child_thread_id, \"turn-child-1\")?;\n- let delivered = inter_agent_message(\n- \"/root\",\n- \"/root/reviewer\",\n- \"review this\",\n- /*trigger_turn*/ true,\n- );\n append_inference_request(\n &writer,\n child_thread_id,\n \"turn-child-1\",\n \"inference-child-1\",\n- vec![message(\"assistant\", &delivered)],\n+ vec![json!({\n+ \"type\": \"agent_message\",\n+ \"author\": \"/root\",\n+ \"recipient\": \"/root/reviewer\",\n+ \"content\": [{\"type\": \"input_text\", \"text\": \"review this\"}]\n+ })],\n )?;\n \n let replayed = replay_bundle(temp.path())?;\n@@ -773,10 +772,9 @@ fn agent_result_edge_falls...", + "path": "codex-rs/rollout-trace/src/reducer/tool/agents_tests.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 4, + "patch_excerpt": "@@ -1117,11 +1117,10 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result {\n- summary.created_at = summary\n+ RolloutItem::ResponseItem(_) | RolloutItem::InterAgentCommunication(_) => {\n+ summary\n .created_at\n- .clone()\n- .or_else(|| Some(rollout_line.timestamp.clone()));\n+ .get_or_insert_with(|| rollout_line.timestamp.clone());\n }\n RolloutItem::TurnContext(_) => {\n // Not included in `head`; skip.\n@@ -1180,6 +1179,11 @@ pub async fn read_head_for_summary(path: &Path) -> io::Result Some(meta_line),\n RolloutItem::ResponseItem(_)\n+ | RolloutItem::InterAgentCommunication(_)\n | RolloutItem::Compacted(_)\n | RolloutItem::TurnContext(_)\n | RolloutItem::EventMsg(_) => None,\n@@ -120,6 +121,7 @@ pub async fn extract_metadata_from_rollout(\n memory_mode: items.iter().rev().find_map(|item| match item {\n RolloutItem::SessionMeta(meta_line) => meta_line.meta.memory_mode.clone(),\n RolloutItem::ResponseItem(_)\n+ | RolloutItem::InterAgentCommunication(_)\n | RolloutItem::Compacted(_)\n | RolloutItem::TurnContext(_)\n | RolloutItem::EventMsg(_) => None,", + "path": "codex-rs/rollout/src/metadata.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -6,6 +6,7 @@ use codex_protocol::models::ResponseItem;\n pub fn is_persisted_rollout_item(item: &RolloutItem) -> bool {\n match item {\n RolloutItem::ResponseItem(item) => should_persist_response_item(item),\n+ RolloutItem::InterAgentCommunication(_) => true,\n RolloutItem::EventMsg(ev) => should_persist_event_msg(ev),\n // Persist Codex executive markers so we can analyze flows (e.g., compaction, API turns).\n RolloutItem::Compacted(_) | RolloutItem::TurnContext(_) | RolloutItem::SessionMeta(_) => {", + "path": "codex-rs/rollout/src/policy.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 21, + "patch_excerpt": "@@ -868,28 +868,17 @@ impl RolloutRecorder {\n \n // Parse the rollout line structure\n match serde_json::from_value::(v.clone()) {\n- Ok(rollout_line) => match rollout_line.item {\n- RolloutItem::SessionMeta(session_meta_line) => {\n- // Use the FIRST SessionMeta encountered in the file as the canonical\n- // thread id and main session information. Keep all items intact.\n- if thread_id.is_none() {\n- thread_id = Some(session_meta_line.meta.id);\n- }\n- items.push(RolloutItem::SessionMeta(session_meta_line));\n+ Ok(rollout_line) => {\n+ let item = rollout_line.item;\n+ // Use the FIRST SessionMeta encountered in the file as the canonical\n+...", + "path": "codex-rs/rollout/src/recorder.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -283,6 +283,7 @@ fn conversation_text_from_item(item: &RolloutItem) -> Option {\n | RolloutItem::TurnContext(_)\n | RolloutItem::EventMsg(_)\n | RolloutItem::ResponseItem(_)\n+ | RolloutItem::InterAgentCommunication(_)\n | RolloutItem::Compacted(_) => None,\n }\n }", + "path": "codex-rs/rollout/src/search.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 3, + "patch_excerpt": "@@ -22,6 +22,7 @@ pub fn apply_rollout_item(\n RolloutItem::TurnContext(turn_ctx) => apply_turn_context(metadata, turn_ctx),\n RolloutItem::EventMsg(event) => apply_event_msg(metadata, event),\n RolloutItem::ResponseItem(item) => apply_response_item(metadata, item),\n+ RolloutItem::InterAgentCommunication(_) => {}\n RolloutItem::Compacted(_) => {}\n }\n if metadata.model_provider.is_empty() {\n@@ -36,9 +37,10 @@ pub fn rollout_item_affects_thread_metadata(item: &RolloutItem) -> bool {\n RolloutItem::EventMsg(\n EventMsg::TokenCount(_) | EventMsg::UserMessage(_) | EventMsg::ThreadGoalUpdated(_),\n ) => true,\n- RolloutItem::EventMsg(_) | RolloutItem::ResponseItem(_) | RolloutItem::Compacted(_) => {\n- false\n- }\n+ RolloutItem::EventMsg(_)\n+ | RolloutItem::ResponseItem(_)\n+ | RolloutI...", + "path": "codex-rs/state/src/extract.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -1078,6 +1078,7 @@ pub(super) fn extract_memory_mode(items: &[RolloutItem]) -> Option {\n items.iter().rev().find_map(|item| match item {\n RolloutItem::SessionMeta(meta_line) => meta_line.meta.memory_mode.clone(),\n RolloutItem::ResponseItem(_)\n+ | RolloutItem::InterAgentCommunication(_)\n | RolloutItem::Compacted(_)\n | RolloutItem::TurnContext(_)\n | RolloutItem::EventMsg(_) => None,", + "path": "codex-rs/state/src/runtime/threads.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -272,6 +272,7 @@ impl ThreadMetadataSync {\n RolloutItem::SessionMeta(_)\n | RolloutItem::EventMsg(_)\n | RolloutItem::ResponseItem(_)\n+ | RolloutItem::InterAgentCommunication(_)\n | RolloutItem::Compacted(_) => {}\n }\n }", + "path": "codex-rs/thread-store/src/thread_metadata_sync.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nMulti-agent v2 `send_message` deliveries already reach the receiving model as typed `agent_message` items with encrypted content. Child-completion notifications are generated by Codex itself, so their content is plaintext and previously fell back to a serialized JSON envelope inside an assistant message.\n\nWith plaintext `input_text` supported for `agent_message`, both delivery paths can use the same model-visible type while preserving explicit author and recipient metadata.\n\n## What changed\n\n- add plaintext `input_text` support to `AgentMessageInputContent` and regenerate the affected app-server schemas\n- preserve `InterAgentCommunication` as structured mailbox input instead of converting it to assistant text\n- record delivered communications as typed `agent_message` history items\n- persist a dedicated rollout item so local delivery metadata such as `trigger_turn` remains available without leaking into the Responses request\n- reconstruct typed agent messages on resume and preserve fork-turn truncation behavior\n- remove request-time assistant-content parsing\n- preserve plaintext and encrypted inter-agent deliveries in stage-one memory inputs\n- normalize and link plaintext and encrypted agent messages in rollout traces without treating inbound messages as child results\n- cover the real MultiAgent V2 child-completion path end to end with deterministic mailbox synchronization\n\n## Verification\n\n- `just test -p codex-core plaintext_multi_agent_v2_completion_sends_agent_message`\n- `just test -p codex-core input_queue_drains_mailbox_in_delivery_order record_initial_history_reconstructs_typed_inter_agent_message fork_turn_positions_use_inter_agent_delivery_metadata`\n- `just test -p codex-memories-write serializes_inter_agent_communications_for_memory`\n- `just test -p codex-rollout-trace agent_messages_preserve_routing_and_content sub_agent_started_activity_creates_spawn_edge`\n- `just test -p codex-rollout-trace agent_result_edge_falls_back_to_child_thread_without_result_message`\n- `just test -p codex-protocol -p codex-rollout -p codex-app-server-protocol`\n", + "labels": [], + "merged_at": "2026-06-12T20:50:04Z", + "number": 27830, + "state": "merged", + "title": "Support plaintext agent messages", + "url": "https://github.com/openai/codex/pull/27830" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27961.json b/artifacts/github/bundles/openai-codex-pr-27961.json new file mode 100644 index 000000000..ba9337f79 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27961.json @@ -0,0 +1,279 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "apanasenko-oai", + "committed_at": "2026-06-12T22:00:10Z", + "message": "feat(app-server): enforce managed remote control disable", + "sha": "a1f9d5e195dbf73c5ac756e90224f2e4af3a6adf", + "url": "https://github.com/openai/codex/commit/a1f9d5e195dbf73c5ac756e90224f2e4af3a6adf" + }, + { + "author": "apanasenko-oai", + "committed_at": "2026-06-12T22:48:50Z", + "message": "test(tui): snapshot debug config requirements", + "sha": "69a4db602847e1c1269016243061b76b0687a6da", + "url": "https://github.com/openai/codex/commit/69a4db602847e1c1269016243061b76b0687a6da" + }, + { + "author": "apanasenko-oai", + "committed_at": "2026-06-12T23:59:16Z", + "message": "refactor(app-server): use normal remote control request validation", + "sha": "d3918160167a6eaa5a33982ef79d9ccb63636d7b", + "url": "https://github.com/openai/codex/commit/d3918160167a6eaa5a33982ef79d9ccb63636d7b" + }, + { + "author": "apanasenko-oai", + "committed_at": "2026-06-13T00:34:12Z", + "message": "fix(app-server): fail explicit remote control under managed disable", + "sha": "f4ef520440ce2a1d6b9ea9dcae6bbc272c2c181c", + "url": "https://github.com/openai/codex/commit/f4ef520440ce2a1d6b9ea9dcae6bbc272c2c181c" + } + ], + "default_branch": "main", + "docs_refs": [ + "codex-rs/app-server/README.md" + ], + "examples_refs": [], + "extracted_flags": [ + "RPC", + "JSON", + "SQL", + "CHANNEL_CAPACITY", + "REMOTE_CONTROL_DISABLED_ENV_VAR", + "TEST_REMOTE_CONTROL_URL", + "TEST_INSTALLATION_ID", + "MDM", + "--listen", + "EXTERNAL_AUTH_REFRESH_TIMEOUT", + "CONNECTION_RPC_DRAIN_TIMEOUT", + "JSONRPCR", + "JSONRPCE", + "DEFAULT_READ_TIMEOUT", + "DEFAULT_TIMEOUT", + "STARTUP_TIMEOUT", + "REMOTE_CONTROL_DISABLED_BY_REQUIREMENTS_MESSAGE", + "CODEX_HOME", + "VSC" + ], + "files": [ + { + "additions": 6, + "deletions": 0, + "patch_excerpt": "@@ -8119,6 +8119,12 @@\n \"null\"\n ]\n },\n+ \"allowRemoteControl\": {\n+ \"type\": [\n+ \"boolean\",\n+ \"null\"\n+ ]\n+ },\n \"allowedApprovalPolicies\": {\n \"items\": {\n \"$ref\": \"#/definitions/v2/AskForApproval\"", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", + "status": "modified" + }, + { + "additions": 6, + "deletions": 0, + "patch_excerpt": "@@ -4432,6 +4432,12 @@\n \"null\"\n ]\n },\n+ \"allowRemoteControl\": {\n+ \"type\": [\n+ \"boolean\",\n+ \"null\"\n+ ]\n+ },\n \"allowedApprovalPolicies\": {\n \"items\": {\n \"$ref\": \"#/definitions/AskForApproval\"", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", + "status": "modified" + }, + { + "additions": 6, + "deletions": 0, + "patch_excerpt": "@@ -85,6 +85,12 @@\n \"null\"\n ]\n },\n+ \"allowRemoteControl\": {\n+ \"type\": [\n+ \"boolean\",\n+ \"null\"\n+ ]\n+ },\n \"allowedApprovalPolicies\": {\n \"items\": {\n \"$ref\": \"#/definitions/AskForApproval\"", + "path": "codex-rs/app-server-protocol/schema/json/v2/ConfigRequirementsReadResponse.json", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -8,4 +8,4 @@ import type { ResidencyRequirement } from \"./ResidencyRequirement\";\n import type { SandboxMode } from \"./SandboxMode\";\n import type { WindowsSandboxSetupMode } from \"./WindowsSandboxSetupMode\";\n \n-export type ConfigRequirements = {allowedApprovalPolicies: Array | null, allowedSandboxModes: Array | null, allowedWindowsSandboxImplementations: Array | null, allowedPermissionProfiles: { [key in string]?: boolean } | null, defaultPermissions: string | null, allowedWebSearchModes: Array | null, allowManagedHooksOnly: boolean | null, allowAppshots: boolean | null, computerUse: ComputerUseRequirements | null, featureRequirements: { [key in string]?: boolean } | null, enforceResidency: ResidencyRequirement | null};\n+export type ConfigRequirements = {allowedApprovalPolicies: Array | null, allowedSa...", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/ConfigRequirements.ts", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -381,6 +381,7 @@ pub struct ConfigRequirements {\n pub allowed_web_search_modes: Option>,\n pub allow_managed_hooks_only: Option,\n pub allow_appshots: Option,\n+ pub allow_remote_control: Option,\n pub computer_use: Option,\n pub feature_requirements: Option>,\n #[experimental(\"configRequirements/read.hooks\")]", + "path": "codex-rs/app-server-protocol/src/protocol/v2/config.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -1703,6 +1703,7 @@ fn config_requirements_granular_allowed_approval_policy_is_marked_experimental()\n allowed_web_search_modes: None,\n allow_managed_hooks_only: None,\n allow_appshots: None,\n+ allow_remote_control: None,\n computer_use: None,\n feature_requirements: None,\n hooks: None,", + "path": "codex-rs/app-server-protocol/src/protocol/v2/tests.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -12,7 +12,10 @@ pub use transport::AppServerTransportParseError;\n pub use transport::CHANNEL_CAPACITY;\n pub use transport::ConnectionOrigin;\n pub use transport::REMOTE_CONTROL_DISABLED_ENV_VAR;\n+pub use transport::RemoteControlDisabledByRequirements;\n+pub use transport::RemoteControlEnableError;\n pub use transport::RemoteControlHandle;\n+pub use transport::RemoteControlPolicy;\n pub use transport::RemoteControlStartConfig;\n pub use transport::RemoteControlStartupMode;\n pub use transport::RemoteControlUnavailable;", + "path": "codex-rs/app-server-transport/src/lib.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -31,7 +31,10 @@ mod unix_socket_tests;\n mod websocket;\n \n pub use remote_control::REMOTE_CONTROL_DISABLED_ENV_VAR;\n+pub use remote_control::RemoteControlDisabledByRequirements;\n+pub use remote_control::RemoteControlEnableError;\n pub use remote_control::RemoteControlHandle;\n+pub use remote_control::RemoteControlPolicy;\n pub use remote_control::RemoteControlStartConfig;\n pub use remote_control::RemoteControlStartupMode;\n pub use remote_control::RemoteControlUnavailable;", + "path": "codex-rs/app-server-transport/src/transport/mod.rs", + "status": "modified" + }, + { + "additions": 15, + "deletions": 2, + "patch_excerpt": "@@ -1,3 +1,4 @@\n+use super::RemoteControlEnableError;\n use super::RemoteControlHandle;\n use super::RemoteControlUnavailable;\n use super::enroll::update_persisted_remote_control_enrollment;\n@@ -51,6 +52,9 @@ impl RemoteControlHandle {\n &self,\n app_server_client_name: Option<&str>,\n ) -> io::Result {\n+ if self.ensure_remote_control_allowed().is_err() {\n+ return Ok(false);\n+ }\n let _transition = self\n .desired_state_rpc_lock\n .acquire()\n@@ -93,6 +97,8 @@ impl RemoteControlHandle {\n &self,\n app_server_client_name: Option<&str>,\n ) -> io::Result {\n+ self.ensure_remote_control_allowed()\n+ .map_err(|err| io::Error::new(io::ErrorKind::PermissionDenied, err))?;\n let _transition = self\n .desired_state_rpc_lock\n ...", + "path": "codex-rs/app-server-transport/src/transport/remote_control/desired_state.rs", + "status": "modified" + }, + { + "additions": 75, + "deletions": 14, + "patch_excerpt": "@@ -64,6 +64,14 @@ use tracing::warn;\n pub struct RemoteControlStartConfig {\n pub remote_control_url: String,\n pub installation_id: String,\n+ pub policy: RemoteControlPolicy,\n+}\n+\n+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\n+pub enum RemoteControlPolicy {\n+ #[default]\n+ Allowed,\n+ DisabledByRequirements,\n }\n \n #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n@@ -95,6 +103,7 @@ pub(super) struct QueuedServerEnvelope {\n \n #[derive(Clone)]\n pub struct RemoteControlHandle {\n+ policy: RemoteControlPolicy,\n desired_state_tx: Arc>,\n desired_state_rpc_lock: Arc,\n desired_state_persistence_lock: Arc,\n@@ -195,20 +204,64 @@ impl fmt::Display for RemoteControlUnavailable {\n \n impl Error for RemoteControlUnavailable {}\n \n+#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n+pub struct RemoteControlDisabl...", + "path": "codex-rs/app-server-transport/src/transport/remote_control/mod.rs", + "status": "modified" + }, + { + "additions": 119, + "deletions": 1, + "patch_excerpt": "@@ -229,6 +229,7 @@ async fn explicit_disabled_start_ignores_persisted_enable() {\n RemoteControlStartConfig {\n remote_control_url: TEST_REMOTE_CONTROL_URL.to_string(),\n installation_id: TEST_INSTALLATION_ID.to_string(),\n+ policy: RemoteControlPolicy::Allowed,\n },\n Some(state_db.clone()),\n remote_control_auth_manager(),\n@@ -260,6 +261,105 @@ async fn explicit_disabled_start_ignores_persisted_enable() {\n remote_task.await.expect(\"remote control task should join\");\n }\n \n+#[tokio::test]\n+async fn managed_disable_overrides_startup_and_persisted_enablement() {\n+ let listener = TcpListener::bind(\"127.0.0.1:0\")\n+ .await\n+ .expect(\"listener should bind\");\n+ let remote_control_url = remote_control_url_for_listener(&listener);\n+ let codex_home = TempDir::new().expect(\"temp dir should create\");\n+ let sta...", + "path": "codex-rs/app-server-transport/src/transport/remote_control/tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -21,6 +21,7 @@ fn client_management_handle(\n environment_id: None,\n });\n RemoteControlHandle {\n+ policy: RemoteControlPolicy::Allowed,\n desired_state_tx: Arc::new(desired_state_tx),\n desired_state_rpc_lock: Arc::new(Semaphore::new(1)),\n desired_state_persistence_lock: Arc::new(Semaphore::new(1)),", + "path": "codex-rs/app-server-transport/src/transport/remote_control/tests/clients_tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -234,7 +234,7 @@ Example with notification opt-out:\n - `externalAgentConfig/import` — apply selected external-agent migration items by passing explicit `migrationItems` with `cwd` (`null` for home) and any plugin/session `details` returned by detect. When a request includes migration items, the server emits `externalAgentConfig/import/completed` once after the full import finishes (immediately after the response when everything completed synchronously, or after background imports finish).\n - `config/value/write` — write a single config key/value to the user's config.toml on disk; dotted paths such as `desktop.someKey` use the same generic write surface.\n - `config/batchWrite` — apply multiple config edits atomically to the user's config.toml on disk, with optional `reloadUserConfig: true` to hot-reload loaded threads, including multiple `desktop.*` edits.\n-- `configRequirements/read` ...", + "path": "codex-rs/app-server/README.md", + "status": "modified" + }, + { + "additions": 35, + "deletions": 6, + "patch_excerpt": "@@ -32,6 +32,7 @@ use crate::outgoing_message::QueuedOutgoingMessage;\n use crate::transport::CHANNEL_CAPACITY;\n use crate::transport::ConnectionState;\n use crate::transport::OutboundConnectionState;\n+use crate::transport::RemoteControlPolicy;\n use crate::transport::RemoteControlStartConfig;\n use crate::transport::TransportEvent;\n use crate::transport::acquire_app_server_startup_lock;\n@@ -672,6 +673,28 @@ pub async fn run_main_with_transport_options(\n None => error!(\"{}\", warning.summary),\n }\n }\n+ let remote_control_policy = if config\n+ .config_layer_stack\n+ .requirements()\n+ .allow_remote_control\n+ .as_ref()\n+ .is_some_and(|requirement| !requirement.value)\n+ {\n+ RemoteControlPolicy::DisabledByRequirements\n+ } else {\n+ RemoteControlPolicy::Allowed\n+ };\n+ let remote_control_startup_mode = runtime_option...", + "path": "codex-rs/app-server/src/lib.rs", + "status": "modified" + }, + { + "additions": 12, + "deletions": 6, + "patch_excerpt": "@@ -94,6 +94,17 @@ use tracing::Instrument;\n const EXTERNAL_AUTH_REFRESH_TIMEOUT: Duration = Duration::from_secs(10);\n const CONNECTION_RPC_DRAIN_TIMEOUT: Duration = Duration::from_secs(/*secs*/ 30);\n \n+fn deserialize_client_request(\n+ request: &JSONRPCRequest,\n+) -> Result {\n+ serde_json::to_value(request)\n+ .map_err(|err| invalid_request(format!(\"Invalid request: {err}\")))\n+ .and_then(|request_json| {\n+ serde_json::from_value(request_json)\n+ .map_err(|err| invalid_request(format!(\"Invalid request: {err}\")))\n+ })\n+}\n+\n #[derive(Clone)]\n struct ExternalAuthRefreshBridge {\n outgoing: Arc,\n@@ -582,12 +593,7 @@ impl MessageProcessor {\n Arc::clone(&self.outgoing),\n request_context.clone(),\n async {\n- let codex_request = serde_jso...", + "path": "codex-rs/app-server/src/message_processor.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 0, + "patch_excerpt": "@@ -364,6 +364,7 @@ fn map_requirements_toml_to_api(requirements: ConfigRequirementsToml) -> ConfigR\n }),\n allow_managed_hooks_only: requirements.allow_managed_hooks_only,\n allow_appshots: requirements.allow_appshots,\n+ allow_remote_control: requirements.allow_remote_control,\n computer_use: requirements\n .computer_use\n .map(map_computer_use_requirements_to_api),\n@@ -618,6 +619,16 @@ mod tests {\n assert_eq!(mapped.hooks, None);\n }\n \n+ #[test]\n+ fn requirements_api_includes_allow_remote_control() {\n+ let mapped = map_requirements_toml_to_api(ConfigRequirementsToml {\n+ allow_remote_control: Some(false),\n+ ..ConfigRequirementsToml::default()\n+ });\n+\n+ assert_eq!(mapped.allow_remote_control, Some(false));\n+ }\n+\n #[test]\n fn requirements_api_includes_computer_u...", + "path": "codex-rs/app-server/src/request_processors/config_processor.rs", + "status": "modified" + }, + { + "additions": 22, + "deletions": 5, + "patch_excerpt": "@@ -1,5 +1,6 @@\n use crate::error_code::internal_error;\n use crate::error_code::invalid_request;\n+use crate::transport::RemoteControlEnableError;\n use crate::transport::RemoteControlHandle;\n use crate::transport::RemoteControlUnavailable;\n use codex_app_server_protocol::JSONRPCErrorError;\n@@ -35,7 +36,7 @@ impl RemoteControlRequestProcessor {\n ) -> Result {\n let handle = self.handle()?;\n let status = if ephemeral {\n- handle.enable_ephemeral().map_err(map_unavailable)?\n+ handle.enable_ephemeral().map_err(map_enable_error)?\n } else {\n handle\n .enable(app_server_client_name)\n@@ -88,7 +89,8 @@ impl RemoteControlRequestProcessor {\n params: RemoteControlPairingStatusParams,\n ) -> Result {\n valida...", + "path": "codex-rs/app-server/src/request_processors/remote_control_processor.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -18,7 +18,9 @@ pub(crate) use codex_app_server_transport::ConnectionId;\n pub(crate) use codex_app_server_transport::ConnectionOrigin;\n pub(crate) use codex_app_server_transport::OutgoingMessage;\n pub(crate) use codex_app_server_transport::QueuedOutgoingMessage;\n+pub(crate) use codex_app_server_transport::RemoteControlEnableError;\n pub(crate) use codex_app_server_transport::RemoteControlHandle;\n+pub(crate) use codex_app_server_transport::RemoteControlPolicy;\n pub(crate) use codex_app_server_transport::RemoteControlStartConfig;\n pub use codex_app_server_transport::RemoteControlStartupMode;\n pub(crate) use codex_app_server_transport::RemoteControlUnavailable;", + "path": "codex-rs/app-server/src/transport.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 0, + "patch_excerpt": "@@ -1133,6 +1133,11 @@ impl TestAppServer {\n self.send_request(\"config/read\", params).await\n }\n \n+ pub async fn send_config_requirements_read_request(&mut self) -> anyhow::Result {\n+ self.send_request(\"configRequirements/read\", /*params*/ None)\n+ .await\n+ }\n+\n pub async fn send_config_value_write_request(\n &mut self,\n params: ConfigValueWriteParams,", + "path": "codex-rs/app-server/tests/common/test_app_server.rs", + "status": "modified" + }, + { + "additions": 28, + "deletions": 0, + "patch_excerpt": "@@ -14,6 +14,7 @@ use codex_app_server_protocol::ConfigEdit;\n use codex_app_server_protocol::ConfigLayerSource;\n use codex_app_server_protocol::ConfigReadParams;\n use codex_app_server_protocol::ConfigReadResponse;\n+use codex_app_server_protocol::ConfigRequirementsReadResponse;\n use codex_app_server_protocol::ConfigValueWriteParams;\n use codex_app_server_protocol::ConfigWriteResponse;\n use codex_app_server_protocol::ForcedChatgptWorkspaceIds;\n@@ -47,6 +48,33 @@ fn write_config(codex_home: &TempDir, contents: &str) -> Result<()> {\n )?)\n }\n \n+#[tokio::test(flavor = \"multi_thread\", worker_threads = 2)]\n+async fn config_requirements_read_includes_allow_remote_control() -> Result<()> {\n+ let codex_home = TempDir::new()?;\n+ std::fs::write(\n+ codex_home.path().join(\"requirements.toml\"),\n+ \"allow_remote_control = false\\n\",\n+ )?;\n+ let mut mcp = TestAppServer::new(cod...", + "path": "codex-rs/app-server/tests/suite/v2/config_rpc.rs", + "status": "modified" + }, + { + "additions": 235, + "deletions": 0, + "patch_excerpt": "@@ -1,3 +1,6 @@\n+use std::ffi::OsStr;\n+use std::ffi::OsString;\n+use std::io::ErrorKind;\n use std::time::Duration;\n \n use anyhow::Context;\n@@ -8,6 +11,13 @@ use app_test_support::TestAppServer;\n use app_test_support::to_response;\n use app_test_support::write_chatgpt_auth;\n use app_test_support::write_mock_responses_config_toml_with_chatgpt_base_url;\n+use codex_app_server::AppServerRuntimeOptions;\n+use codex_app_server::AppServerTransport;\n+use codex_app_server::AppServerWebsocketAuthSettings;\n+use codex_app_server::PluginStartupTasks;\n+use codex_app_server::RemoteControlStartupMode;\n+use codex_app_server::run_main_with_transport_options;\n+use codex_app_server_protocol::JSONRPCError;\n use codex_app_server_protocol::JSONRPCResponse;\n use codex_app_server_protocol::RemoteControlClient;\n use codex_app_server_protocol::RemoteControlClientsListOrder;\n@@ -22,12 +32,18 @@ use codex_app_server_pro...", + "path": "codex-rs/app-server/tests/suite/v2/remote_control.rs", + "status": "modified" + }, + { + "additions": 34, + "deletions": 0, + "patch_excerpt": "@@ -149,6 +149,7 @@ pub struct ConfigRequirements {\n pub web_search_mode: ConstrainedWithSource,\n pub allow_managed_hooks_only: Option>,\n pub allow_appshots: Option>,\n+ pub allow_remote_control: Option>,\n pub computer_use: Option>,\n pub feature_requirements: Option>,\n pub managed_hooks: Option>,\n@@ -189,6 +190,7 @@ impl Default for ConfigRequirements {\n ),\n allow_managed_hooks_only: None,\n allow_appshots: None,\n+ allow_remote_control: None,\n computer_use: None,\n feature_requirements: None,\n managed_hooks: None,\n@@ -828,6 +830,7 @@ pub struct ConfigRequirementsToml {\n pub allowed_web_search_modes: Opt...", + "path": "codex-rs/config/src/config_requirements.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -182,6 +182,7 @@ fn populate_merged_regular_fields_with_sources(\n allowed_web_search_modes,\n allow_managed_hooks_only,\n allow_appshots,\n+ allow_remote_control,\n computer_use,\n windows,\n feature_requirements,\n@@ -210,6 +211,7 @@ fn populate_merged_regular_fields_with_sources(\n set_sourced!(allowed_web_search_modes, &[\"allowed_web_search_modes\"]);\n set_sourced!(allow_managed_hooks_only, &[\"allow_managed_hooks_only\"]);\n set_sourced!(allow_appshots, &[\"allow_appshots\"]);\n+ set_sourced!(allow_remote_control, &[\"allow_remote_control\"]);\n set_sourced!(computer_use, &[\"computer_use\"]);\n set_sourced!(windows, &[\"windows\"]);\n set_sourced!(feature_requirements, &[\"features\", \"feature_requirements\"]);", + "path": "codex-rs/config/src/requirements_layers/stack.rs", + "status": "modified" + }, + { + "additions": 14, + "deletions": 1, + "patch_excerpt": "@@ -63,6 +63,7 @@ fn top_level_values_use_toml_priority() {\n allowed_approval_policies = [\"on-request\"]\n allowed_sandbox_modes = [\"workspace-write\"]\n default_permissions = \":workspace\"\n+allow_remote_control = true\n \n [allowed_permission_profiles]\n \":read-only\" = true\n@@ -76,6 +77,7 @@ default_permissions = \":workspace\"\n allowed_approval_policies = [\"never\"]\n allowed_sandbox_modes = [\"read-only\"]\n default_permissions = \":read-only\"\n+allow_remote_control = false\n \n [allowed_permission_profiles]\n \":danger-full-access\" = false\n@@ -93,6 +95,7 @@ default_permissions = \":read-only\"\n allowed_approval_policies = [\"never\"]\n allowed_sandbox_modes = [\"read-only\"]\n default_permissions = \":read-only\"\n+allow_remote_control = false\n \n [allowed_permission_profiles]\n \":danger-full-access\" = false\n@@ -135,6 +138,7 @@ fn composition_strategy_applies_to_non_cloud_layers() {\n format!(\n ...", + "path": "codex-rs/config/src/requirements_layers/stack_tests.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 1, + "patch_excerpt": "@@ -73,14 +73,18 @@ impl LoaderOverrides {\n }\n }\n \n- /// Returns overrides with host MDM disabled and managed config loaded from `managed_config_path`.\n+ /// Returns overrides with host MDM disabled and managed config loaded from\n+ /// `managed_config_path`. System requirements are loaded from a sibling\n+ /// `requirements.toml` fixture.\n ///\n /// This is intended for tests that supply an explicit managed config fixture.\n pub fn with_managed_config_path_for_tests(managed_config_path: PathBuf) -> Self {\n+ let system_requirements_path = managed_config_path.with_file_name(\"requirements.toml\");\n Self {\n user_config_path: None,\n user_config_profile: None,\n managed_config_path: Some(managed_config_path),\n+ system_requirements_path: Some(system_requirements_path),\n ..Self::without_manag...", + "path": "codex-rs/config/src/state.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -8304,6 +8304,7 @@ async fn test_requirements_web_search_mode_allowlist_does_not_warn_when_unset()\n allowed_web_search_modes: Some(vec![codex_config::WebSearchModeRequirement::Cached]),\n allow_managed_hooks_only: None,\n allow_appshots: None,\n+ allow_remote_control: None,\n computer_use: None,\n windows: None,\n feature_requirements: None,", + "path": "codex-rs/core/src/config/config_tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -2625,6 +2625,7 @@ impl Config {\n web_search_mode: mut constrained_web_search_mode,\n allow_managed_hooks_only: _,\n allow_appshots: _,\n+ allow_remote_control: _,\n computer_use: _,\n feature_requirements,\n managed_hooks: _,", + "path": "codex-rs/core/src/config/mod.rs", + "status": "modified" + }, + { + "additions": 23, + "deletions": 0, + "patch_excerpt": "@@ -196,6 +196,17 @@ fn render_debug_config_lines(\n ));\n }\n \n+ if let Some(allow_remote_control) = requirements_toml.allow_remote_control {\n+ requirement_lines.push(requirement_line(\n+ \"allow_remote_control\",\n+ allow_remote_control.to_string(),\n+ requirements\n+ .allow_remote_control\n+ .as_ref()\n+ .map(|sourced| &sourced.source),\n+ ));\n+ }\n+\n if requirements_toml.guardian_policy_config.is_some() {\n requirement_lines.push(requirement_line(\n \"guardian_policy_config\",\n@@ -712,6 +723,10 @@ mod tests {\n /*value*/ false,\n RequirementSource::LegacyManagedConfigTomlFromMdm,\n )),\n+ allow_remote_control: Some(Sourced::new(\n+ /*value*/ false,\n+ RequirementSource::LegacyManagedConfigTo...", + "path": "codex-rs/tui/src/debug_config.rs", + "status": "modified" + }, + { + "additions": 23, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,23 @@\n+---\n+source: tui/src/debug_config.rs\n+expression: rendered.as_str()\n+---\n+/debug-config\n+\n+Config layer stack (lowest precedence first):\n+ 1. user (/home/alice/.codex/config.toml) (enabled)\n+\n+Requirements:\n+ - allowed_approval_policies: on-request (source: MDM managed_config.toml (legacy))\n+ - allowed_approvals_reviewers: auto_review (source: MDM managed_config.toml (legacy))\n+ - allowed_sandbox_modes: read-only (source: /etc/codex/requirements.toml)\n+ - allowed_web_search_modes: cached, disabled (source: MDM managed_config.toml (legacy))\n+ - allow_managed_hooks_only: true (source: MDM managed_config.toml (legacy))\n+ - allow_appshots: false (source: MDM managed_config.toml (legacy))\n+ - allow_remote_control: false (source: MDM managed_config.toml (legacy))\n+ - guardian_policy_config: configured (source: MDM managed_config.toml (legacy))\n+ - features: guardian...", + "path": "codex-rs/tui/src/snapshots/codex_tui__debug_config__tests__debug_config_requirement_sources.snap", + "status": "added" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nManaged deployments need a reliable deny gate for remote control. Persisted enablement and explicit startup requests currently remain able to start the transport, while the removed `features.remote_control` key is intentionally only a compatibility no-op.\n\nThis adds a dedicated requirement that administrators can use to force remote control off without deleting the user's persisted preference. Removing the requirement and restarting restores the prior choice.\n\n## What Changed\n\n- Added top-level `allow_remote_control` requirements parsing, sourced layer precedence, debug output, and `configRequirements/read` exposure as `allowRemoteControl`.\n- Added a typed transport policy captured from the startup requirements snapshot. Managed disable forces the initial state to disabled and prevents enrollment, refresh, connection, and persisted-preference mutation.\n- Rejected every `remoteControl/*` RPC before parameter deserialization with JSON-RPC `-32600` and `remote control is disabled by managed requirements`.\n- Preserved the existing disabled status notification and the previous behavior when the requirement is `true` or omitted.\n- Regenerated app-server protocol schemas and documented the new requirement.\n\n## Verification\n\n- Confirmed all remote-control RPCs, including a malformed request, return the managed-policy error while the initial status notification remains `disabled`.\n- Confirmed explicit ephemeral startup and persisted enablement make no backend connection and leave the SQLite preference unchanged.\n- Confirmed `allow_remote_control = true` does not enable or block remote control and `configRequirements/read` returns `allowRemoteControl: false` for the deny policy.\n\nRelated issue: N/A (managed-policy hardening).\n", + "labels": [], + "merged_at": "2026-06-13T03:10:12Z", + "number": 27961, + "state": "merged", + "title": "feat(app-server): enforce managed remote control disable", + "url": "https://github.com/openai/codex/pull/27961" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-27541.json b/artifacts/github/impact/openai-codex-pr-27541.json new file mode 100644 index 000000000..c13eacb63 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27541.json @@ -0,0 +1,48 @@ +{ + "candidate_followups": [ + "Audit Decodex MCP/plugin auth handling for assumptions that OAuth credentials live in one legacy file or keyring namespace.", + "When mirroring Codex app-server refresh, carry the auth keyring backend kind through MCP refresh and auth-status reads.", + "Treat this as part of the larger secret-storage stack before filing implementation work." + ], + "caveats": [ + "The PR is stacked on earlier auth-storage changes.", + "The public claim should stay scoped to MCP OAuth credential storage and backend propagation.", + "Exact Decodex changes need a separate issue if local code reads Codex MCP OAuth secrets directly." + ], + "confidence": "confirmed", + "control_plane_impact": "compat_risk", + "evidence": [ + "MCP OAuth token load/save now receives `AuthKeyringBackendKind`.", + "Encrypted-local secret namespace support is wired into rmcp-client OAuth storage.", + "MCP CLI login passes the configured backend into OAuth login.", + "App-server MCP refresh serializes the latest auth keyring backend kind.", + "Core session and codex-mcp auth-status computation pass the backend through.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-27541.review.json`." + ], + "observed_change": "Codex routes MCP OAuth credentials through the configured auth credential backend, including encrypted-local storage support and legacy keyring migration.", + "public_signal_decision": "publish", + "publisher_angle": "practical_explainer", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27541", + "social_notes": [ + "Frame this as MCP OAuth credential-backend behavior, not a generic secrets claim.", + "Mention encrypted-local storage and legacy keyring migration only within the source-backed MCP OAuth scope." + ], + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-12T22:03:51Z", + "title": "feat: use encrypted local secrets for MCP OAuth", + "url": "https://github.com/openai/codex/pull/27541" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-27541.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27541" + } + ] + } +} diff --git a/artifacts/github/impact/openai-codex-pr-27830.json b/artifacts/github/impact/openai-codex-pr-27830.json new file mode 100644 index 000000000..274c78770 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27830.json @@ -0,0 +1,47 @@ +{ + "candidate_followups": [ + "Audit Decodex transcript and rollout-trace readers for `AgentMessageInputContent` assumptions that only accept encrypted content.", + "Preserve plaintext inter-agent message metadata when summarizing retained-lane child results or memories.", + "Check any app-server history consumers that previously looked for assistant JSON envelopes for child-completion messages." + ], + "caveats": [ + "Plaintext support is scoped to typed agent-message inputs and does not remove encrypted content.", + "This is a multi-agent protocol/history behavior change, not a general user chat message change.", + "Exact Decodex changes need a separate implementation issue if local readers reject plaintext agent-message content." + ], + "confidence": "confirmed", + "control_plane_impact": "compat_risk", + "evidence": [ + "AgentMessageInputContent now allows plaintext `input_text` as well as encrypted content.", + "Inter-agent mailbox deliveries remain structured `InterAgentCommunication` items.", + "Session history persists dedicated inter-agent communication rollout items and reconstructs them on resume.", + "Memory and rollout-trace reducers preserve plaintext and encrypted inter-agent deliveries.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-27830.review.json`." + ], + "observed_change": "Codex adds plaintext `input_text` support to typed `agent_message` content and preserves inter-agent communications across history, resume, memory, and rollout-trace paths.", + "public_signal_decision": "publish", + "publisher_angle": "operator_impact", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27830", + "social_notes": [ + "Frame this for Multi-Agent v2 and transcript/tooling consumers.", + "Do not imply encrypted agent messages were removed." + ], + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-12T20:50:04Z", + "title": "Support plaintext agent messages", + "url": "https://github.com/openai/codex/pull/27830" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-27830.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27830" + } + ] + } +} diff --git a/artifacts/github/impact/openai-codex-pr-27961.json b/artifacts/github/impact/openai-codex-pr-27961.json new file mode 100644 index 000000000..1dc108b10 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27961.json @@ -0,0 +1,47 @@ +{ + "candidate_followups": [ + "Add a Decodex compatibility check for `configRequirements/read.allowRemoteControl` before remote-control enrollment or RPC attempts.", + "Treat JSON-RPC `-32600` remote-control disabled errors as a managed-policy state, not as a transient transport failure.", + "Keep persisted user preferences separate from managed deny policy when explaining operator status." + ], + "caveats": [ + "The disabled policy does not erase persisted user preference.", + "The PR preserves previous behavior when `allow_remote_control` is omitted or true.", + "Decodex implementation work still needs a separate issue if local Control Plane code assumes remote-control enablement is always requestable." + ], + "confidence": "confirmed", + "control_plane_impact": "compat_risk", + "evidence": [ + "App-server `ConfigRequirements` now exposes nullable `allow_remote_control` as `allowRemoteControl`.", + "Remote-control transport startup captures `RemoteControlPolicy::DisabledByRequirements`.", + "Managed disable forces disabled state and prevents enrollment, refresh, connection, and persisted-preference mutation.", + "Remote-control RPCs are rejected with JSON-RPC `-32600` under managed disable.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-27961.review.json`." + ], + "observed_change": "Codex adds a managed `allow_remote_control` requirement that exposes `allowRemoteControl` through app-server config requirements and blocks remote-control startup and RPCs when disabled.", + "public_signal_decision": "publish", + "publisher_angle": "operator_impact", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27961", + "social_notes": [ + "Frame this as managed remote-control policy and app-server compatibility.", + "Do not imply remote control was removed or that user preference is deleted." + ], + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-13T03:10:12Z", + "title": "feat(app-server): enforce managed remote control disable", + "url": "https://github.com/openai/codex/pull/27961" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-27961.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27961" + } + ] + } +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index d3e2c12df..b3b4c8704 100644 --- a/artifacts/github/review-queue/openai-codex-latest.json +++ b/artifacts/github/review-queue/openai-codex-latest.json @@ -8,7 +8,7 @@ "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-13T02:07:18.76819Z", + "generated_at": "2026-06-13T18:05:12.69079Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,38 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "deprecated_removed", - "release_packaging", - "security_policy" - ], - "changed_file_count": 2, - "commit_shas": [ - "cb142c49ffb7b172a9da1e75854b8e5c0618715d", - "8050d6acbd3f4849f23abe51020d1670f4fb8226", - "b1d8c54465e9bf2a28dd8f845d8abd3949761b09" - ], - "committed_at": "2026-06-12T17:21:00Z", - "next_step": "ai_review_required", - "pr_number": 27901, - "pr_url": "https://github.com/openai/codex/pull/27901", - "review_priority": "critical", - "review_reason": "Needs AI review for deprecated_removed, release_packaging, security_policy.", - "sample_paths": [ - "sdk/python/pyproject.toml", - "sdk/python/tests/test_artifact_workflow_and_binaries.py" - ], - "source_state": "merged", - "subject_id": "27901", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks", - "tests_ci" - ], - "title": "Use uv as Python SDK build backend", - "url": "https://github.com/openai/codex/pull/27901" - }, { "attention_flags": [ "auth_account", @@ -944,85 +912,52 @@ }, { "attention_flags": [ + "auth_account", + "breaking_change", + "deprecated_removed", "new_feature", "protocol_change", "security_policy" ], - "changed_file_count": 12, + "changed_file_count": 29, "commit_shas": [ - "514332328d8921a89660a83cba137ff5dd904717", - "5e98dd7a4f3a2cf349caec88449c400f282bde3a" + "a1f9d5e195dbf73c5ac756e90224f2e4af3a6adf", + "69a4db602847e1c1269016243061b76b0687a6da", + "d3918160167a6eaa5a33982ef79d9ccb63636d7b", + "f4ef520440ce2a1d6b9ea9dcae6bbc272c2c181c" ], - "committed_at": "2026-06-12T16:06:58Z", + "committed_at": "2026-06-13T03:10:12Z", "next_step": "ai_review_required", - "pr_number": 27075, - "pr_url": "https://github.com/openai/codex/pull/27075", - "review_priority": "high", - "review_reason": "Needs AI review for new_feature, protocol_change, security_policy.", + "pr_number": 27961, + "pr_url": "https://github.com/openai/codex/pull/27961", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, new_feature, protocol_change, security_policy.", "sample_paths": [ "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", - "codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json", - "codex-rs/app-server-protocol/schema/typescript/v2/AppsDefaultConfig.ts", + "codex-rs/app-server-protocol/schema/json/v2/ConfigRequirementsReadResponse.json", + "codex-rs/app-server-protocol/schema/typescript/v2/ConfigRequirements.ts", "codex-rs/app-server-protocol/src/protocol/v2/config.rs", - "codex-rs/app-server/README.md", - "codex-rs/app-server/tests/suite/v2/config_rpc.rs", - "codex-rs/config/src/types.rs", - "codex-rs/core/config.schema.json", - "codex-rs/core/src/connectors.rs", - "codex-rs/core/src/connectors_tests.rs", - "codex-rs/core/tests/suite/mcp_turn_metadata.rs" + "codex-rs/app-server-protocol/src/protocol/v2/tests.rs", + "codex-rs/app-server-transport/src/lib.rs", + "codex-rs/app-server-transport/src/transport/mod.rs", + "codex-rs/app-server-transport/src/transport/remote_control/desired_state.rs", + "codex-rs/app-server-transport/src/transport/remote_control/mod.rs", + "codex-rs/app-server-transport/src/transport/remote_control/tests.rs", + "codex-rs/app-server-transport/src/transport/remote_control/tests/clients_tests.rs" ], "source_state": "merged", - "subject_id": "27075", + "subject_id": "27961", "subject_kind": "pr", "surface_hints": [ "app_server_protocol", + "cli_tui", "config_hooks", "docs_examples", - "mcp_plugins", - "tests_ci" - ], - "title": "[ez][codex-rs] Support approvals reviewer in app defaults", - "url": "https://github.com/openai/codex/pull/27075" - }, - { - "attention_flags": [ - "release_packaging" - ], - "changed_file_count": 8, - "commit_shas": [ - "4cda8ee60cad306ee3fd5313098d7435b1de6789", - "126848293a2773a2423528aa33c19bb8fff6cacd", - "8919a0099d65fec3af09bf4b3e7e2784142d9cf1" - ], - "committed_at": "2026-06-12T16:10:07Z", - "next_step": "ai_review_required", - "pr_number": 27538, - "pr_url": "https://github.com/openai/codex/pull/27538", - "review_priority": "high", - "review_reason": "Needs AI review for release_packaging.", - "sample_paths": [ - ".github/workflows/python-sdk-release.yml", - ".github/workflows/sdk.yml", - "scripts/format.py", - "sdk/python/docs/getting-started.md", - "sdk/python/examples/README.md", - "sdk/python/pyproject.toml", - "sdk/python/tests/test_artifact_workflow_and_binaries.py", - "sdk/python/uv.lock" - ], - "source_state": "merged", - "subject_id": "27538", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks", - "docs_examples", - "release_packaging", "tests_ci" ], - "title": "Use dependency groups for Python SDK tooling", - "url": "https://github.com/openai/codex/pull/27538" + "title": "feat(app-server): enforce managed remote control disable", + "url": "https://github.com/openai/codex/pull/27961" }, { "attention_flags": [ @@ -1132,64 +1067,87 @@ { "attention_flags": [ "auth_account", - "release_packaging" + "new_feature", + "protocol_change", + "security_policy" ], - "changed_file_count": 4, + "changed_file_count": 6, "commit_shas": [ - "41408389e4bd9c8b937b0ca10eb305d43ea6439b", - "29ac1384a5af6fb0aa9790e6af4e7aa18a8f12c0", - "0b508a84e79acc7bd298050f99565e9083d22323" + "c3f075e16a688d207f4b688c4f40f456a2377f2b" ], - "committed_at": "2026-06-12T16:26:08Z", + "committed_at": "2026-06-13T02:42:11Z", "next_step": "ai_review_required", - "pr_number": 27783, - "pr_url": "https://github.com/openai/codex/pull/27783", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, release_packaging.", + "pr_number": 27459, + "pr_url": "https://github.com/openai/codex/pull/27459", + "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/updates.rs", - "codex-rs/tui/src/updates_cache.rs", - "codex-rs/tui/src/updates_cache_tests.rs" + "codex-rs/app-server-protocol/src/protocol/common.rs", + "codex-rs/core-plugins/src/manager.rs", + "codex-rs/core-plugins/src/manager_tests.rs", + "codex-rs/core/src/plugins/discoverable_tests.rs", + "codex-rs/core/tests/suite/plugins.rs", + "codex-rs/login/src/auth/manager.rs" ], "source_state": "merged", - "subject_id": "27783", + "subject_id": "27459", "subject_kind": "pr", "surface_hints": [ - "cli_tui", + "app_server_protocol", + "auth_accounts", + "mcp_plugins", "tests_ci" ], - "title": "Persist update dismissal without cache", - "url": "https://github.com/openai/codex/pull/27783" + "title": "[codex] Gate plugin MCP servers by auth route", + "url": "https://github.com/openai/codex/pull/27459" }, { "attention_flags": [ - "auth_account", - "new_feature" + "new_feature", + "protocol_change", + "release_packaging", + "security_policy" ], - "changed_file_count": 2, + "changed_file_count": 10, "commit_shas": [ - "0b53736536efdca3894f9e5f972da1b87be48e91" - ], - "committed_at": "2026-06-12T16:30:25Z", + "7ae8a2b96ff40507131cf85220c3e7d0ce8903e2", + "9defc3f62bababfd16dfc0c09e42d7a826c5edf8", + "fa4da9b809e22a9e0535bf13a69424b1c8e549bf", + "573477f944be51be60e7ef6c19630b4d7d1c5d92", + "81ce551c37fac7e15b40872f7006d9b0decfbb28", + "8f11fbee94dd4381c44a5fec3b3838ae63cb235e", + "92f022d5294f79cfafa0eabc9e4e751b2611ee12", + "152d97daf01d01dcc41b6a37e7ab2f9f27ca2740" + ], + "committed_at": "2026-06-13T03:20:23Z", "next_step": "ai_review_required", - "pr_number": 27814, - "pr_url": "https://github.com/openai/codex/pull/27814", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, new_feature.", + "pr_number": 27937, + "pr_url": "https://github.com/openai/codex/pull/27937", + "review_priority": "high", + "review_reason": "Needs AI review for new_feature, protocol_change, release_packaging, security_policy.", "sample_paths": [ - "codex-rs/tui/src/bottom_pane/slash_commands.rs", - "codex-rs/tui/src/chatwidget/tests/slash_commands.rs" + "bazel/rules/testing/wine.bzl", + "codex-rs/core/Cargo.toml", + "codex-rs/core/tests/remote_env_windows/BUILD.bazel", + "codex-rs/core/tests/remote_env_windows/README.md", + "codex-rs/core/tests/remote_env_windows/remote_env_windows_test.rs", + "codex-rs/exec-server/testing/BUILD.bazel", + "codex-rs/exec-server/testing/README.md", + "codex-rs/exec-server/testing/windows_exec_server.rs", + "scripts/list-bazel-release-targets.sh", + "tools/argument-comment-lint/list-bazel-targets.sh" ], "source_state": "merged", - "subject_id": "27814", + "subject_id": "27937", "subject_kind": "pr", "surface_hints": [ - "cli_tui", + "config_hooks", + "docs_examples", + "release_packaging", "tests_ci" ], - "title": "tui: Allow extra o's in /goal command", - "url": "https://github.com/openai/codex/pull/27814" + "title": "[codex] Add hermetic Wine exec-server test", + "url": "https://github.com/openai/codex/pull/27937" }, { "attention_flags": [ @@ -1574,6 +1532,75 @@ ], "title": "[codex] Add hermetic Wine test support", "url": "https://github.com/openai/codex/pull/27964" + }, + { + "attention_flags": [ + "auth_account", + "new_feature", + "protocol_change", + "security_policy" + ], + "changed_file_count": 5, + "commit_shas": [ + "00c8016dd084973c4fd2844236781872cba16120" + ], + "committed_at": "2026-06-13T07:44:39Z", + "next_step": "ai_review_required", + "pr_number": 27996, + "pr_url": "https://github.com/openai/codex/pull/27996", + "review_priority": "normal", + "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, security_policy.", + "sample_paths": [ + "codex-rs/codex-api/src/endpoint/responses_websocket.rs", + "codex-rs/codex-api/src/sse/responses.rs", + "codex-rs/core/src/client.rs", + "codex-rs/core/src/client_tests.rs", + "codex-rs/core/tests/suite/turn_state.rs" + ], + "source_state": "merged", + "subject_id": "27996", + "subject_kind": "pr", + "surface_hints": [ + "cli_tui", + "tests_ci" + ], + "title": "[codex] Send request-scoped turn state over WebSocket", + "url": "https://github.com/openai/codex/pull/27996" + }, + { + "attention_flags": [ + "auth_account", + "new_feature", + "protocol_change", + "release_packaging", + "security_policy" + ], + "changed_file_count": 5, + "commit_shas": [ + "a568cdbec99e2f66947cc072a8824031eeaa25dd" + ], + "committed_at": "2026-06-13T08:27:58Z", + "next_step": "ai_review_required", + "pr_number": 28002, + "pr_url": "https://github.com/openai/codex/pull/28002", + "review_priority": "normal", + "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, release_packaging, security_policy.", + "sample_paths": [ + "codex-rs/codex-api/src/endpoint/compact.rs", + "codex-rs/core/src/client.rs", + "codex-rs/core/src/compact_remote.rs", + "codex-rs/core/src/session/turn.rs", + "codex-rs/core/tests/suite/compact_remote.rs" + ], + "source_state": "merged", + "subject_id": "28002", + "subject_kind": "pr", + "surface_hints": [ + "cli_tui", + "tests_ci" + ], + "title": "[codex] Send turn state through compact requests", + "url": "https://github.com/openai/codex/pull/28002" } ] } diff --git a/artifacts/github/reviews/openai-codex-pr-27541.review.json b/artifacts/github/reviews/openai-codex-pr-27541.review.json new file mode 100644 index 000000000..36a3566c2 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27541.review.json @@ -0,0 +1,67 @@ +{ + "adoption_opportunity": "When Decodex inspects or drives MCP OAuth flows, pass the configured auth credential backend through refresh, app-server, CLI, and session paths instead of assuming file-only or legacy keyring storage.", + "caveats": "This PR is the fourth item in a stacked auth-storage series. It depends on earlier config, namespace, and CLI auth-storage changes, so adoption should consider the stack as a whole.", + "changed_surfaces": [ + "MCP OAuth token load/save path", + "encrypted-local and legacy keyring OAuth credential migration", + "MCP CLI login flow", + "app-server MCP refresh configuration", + "app-server plugin and MCP request processors", + "core session MCP auth status computation", + "codex-mcp auth status and connection manager", + "rmcp-client OAuth startup tests" + ], + "community_value": "High for MCP users and plugin operators because OAuth credentials can follow the configured auth backend, including encrypted-local storage and legacy migration.", + "compatibility_risk": "Medium-high. Integrations that read or expect MCP OAuth credentials in a fixed legacy location may miss credentials once Codex routes them through the configured backend.", + "confidence": "confirmed", + "control_plane_relevance": "High. Decodex plugin and MCP orchestration depends on Codex MCP auth status, refresh behavior, app-server plugin metadata, and credential backend handling.", + "deprecated_or_breaking_notes": "No MCP OAuth login path is removed, but credential storage moves behind the configured auth backend and includes legacy keyring migration behavior.", + "evidence": [ + "PR #27541 says MCP OAuth credentials are stored in the configured auth credential backend with encrypted-local storage and legacy keyring migration.", + "`codex-rs/rmcp-client/src/oauth.rs` threads `AuthKeyringBackendKind` through OAuth token load/save and adds secret-manager namespace support.", + "`codex-rs/rmcp-client/src/perform_oauth_login.rs` and `codex-rs/cli/src/mcp_cmd.rs` pass the auth keyring backend into MCP OAuth login.", + "`codex-rs/app-server/src/mcp_refresh.rs` serializes the latest `auth_keyring_backend_kind` into refresh config and adds coverage for backend refresh.", + "`codex-rs/core/src/session/mcp.rs` and `codex-rs/codex-mcp/src/mcp/auth.rs` pass the backend into MCP auth-status computation.", + "`codex-rs/rmcp-client/tests/streamable_http_oauth_startup.rs` updates persisted credential tests to pass the backend kind.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27541.json` records 27 changed files for the merged PR." + ], + "next_actions": [ + { + "reason": "MCP OAuth credential backend routing can affect Decodex plugin auth and app-server refresh compatibility.", + "type": "upstream_impact" + }, + { + "reason": "The change has a practical public angle for MCP OAuth credential storage and managed/plugin environments.", + "type": "social_candidate" + } + ], + "observed_change": "Codex routes MCP OAuth credentials through the configured auth credential backend, including encrypted-local storage support and legacy keyring migration.", + "repo": "openai/codex", + "reviewed_at": "2026-06-13T18:07:16Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27541", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-12T22:03:51Z", + "title": "feat: use encrypted local secrets for MCP OAuth", + "url": "https://github.com/openai/codex/pull/27541" + }, + { + "kind": "commit", + "title": "Use encrypted local secrets for MCP OAuth", + "url": "https://github.com/openai/codex/commit/bc08fad8d62d48b248a86b6183ebb1ecc44d786c" + } + ] + }, + "subject": { + "commit_shas": [ + "bc08fad8d62d48b248a86b6183ebb1ecc44d786c", + "78daf298b0ea9ea7a5fddad51af4545cb05d2236" + ], + "subject_id": "27541", + "subject_kind": "pr" + }, + "user_visible_path": "Users and clients using MCP OAuth can keep credentials in the configured auth backend, including encrypted-local storage, while MCP refresh, CLI login, and app-server paths preserve the backend choice." +} diff --git a/artifacts/github/reviews/openai-codex-pr-27830.review.json b/artifacts/github/reviews/openai-codex-pr-27830.review.json new file mode 100644 index 000000000..0372612e2 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27830.review.json @@ -0,0 +1,80 @@ +{ + "adoption_opportunity": "Update Decodex rollout, memory, trace, and app-server history consumers to preserve typed plaintext `agent_message` items instead of treating child-completion notifications as assistant JSON envelopes.", + "caveats": "The PR narrows plaintext support to request-side agent messages. It does not remove encrypted agent-message support.", + "changed_surfaces": [ + "app-server protocol `AgentMessageInputContent` union", + "thread history and resume projections", + "inter-agent mailbox input queue", + "rollout item persistence and reconstruction", + "stage-one memory input serialization", + "rollout trace conversation normalization", + "multi-agent v2 child-completion synchronization tests" + ], + "community_value": "High for multi-agent users and tooling authors because Codex now preserves typed author/recipient metadata for plaintext child-completion messages across history, resume, memories, and traces.", + "compatibility_risk": "High for clients that parse app-server history, rollout traces, or agent-message content and assumed `AgentMessageInputContent` only contained encrypted content.", + "confidence": "confirmed", + "control_plane_relevance": "High. Decodex depends on multi-agent transcript, memory, and rollout-trace semantics when monitoring retained work and explaining agent communication.", + "deprecated_or_breaking_notes": "Encrypted `agent_message` content remains supported. The compatibility-sensitive change is that plaintext `input_text` is now valid for agent messages and request-time assistant-content parsing is removed.", + "evidence": [ + "PR #27830 says plaintext `input_text` support lets child-completion notifications use the same typed `agent_message` path as encrypted multi-agent deliveries.", + "`codex-rs/app-server-protocol/schema/typescript/AgentMessageInputContent.ts` changes the type from encrypted-only to a union of plaintext `input_text` and encrypted content.", + "`codex-rs/protocol/src/protocol.rs` records inter-agent communication as agent-message history and emits plaintext content when encrypted content is absent.", + "`codex-rs/core/src/session/input_queue.rs` preserves mailbox deliveries as `TurnInput::InterAgentCommunication` instead of pre-converting them to assistant text.", + "`codex-rs/core/src/session/mod.rs` records inter-agent communication into history and persists a dedicated `RolloutItem::InterAgentCommunication`.", + "`codex-rs/core/src/session/rollout_reconstruction.rs` reconstructs typed inter-agent messages from rollout items.", + "`codex-rs/memories/write/src/phase1.rs` includes inter-agent communication in sanitized memory inputs.", + "`codex-rs/rollout-trace/src/reducer/conversation/normalize.rs` adds agent-message metadata normalization.", + "`codex-rs/core/tests/suite/subagent_notifications.rs` adds an end-to-end plaintext Multi-Agent v2 child-completion test.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27830.json` records 44 changed files for the merged PR." + ], + "next_actions": [ + { + "reason": "Typed plaintext agent messages can affect Decodex transcript, memory, and rollout-trace compatibility.", + "type": "upstream_impact" + }, + { + "reason": "The change has a clear public operator angle for Multi-Agent v2 communication semantics.", + "type": "social_candidate" + } + ], + "observed_change": "Codex adds plaintext `input_text` support to typed `agent_message` content and preserves inter-agent communications across history, resume, memory, and rollout-trace paths.", + "repo": "openai/codex", + "reviewed_at": "2026-06-13T18:07:16Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27830", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-12T20:50:04Z", + "title": "Support plaintext agent messages", + "url": "https://github.com/openai/codex/pull/27830" + }, + { + "kind": "commit", + "title": "Support plaintext agent messages", + "url": "https://github.com/openai/codex/commit/175e330c022da6cb1b49d20351464a8e99b77d29" + }, + { + "kind": "commit", + "title": "Preserve agent messages across transcript projections", + "url": "https://github.com/openai/codex/commit/9a685d4cabb0fa6370135ef2737ed731d5759033" + } + ] + }, + "subject": { + "commit_shas": [ + "175e330c022da6cb1b49d20351464a8e99b77d29", + "dea1baa7a97b7a072a00dadb87d54bce4858dade", + "73dde0c84f6cf2da38ec71a094b6c7a6282bf645", + "e62505d9da1a30546003ca7af0ca25f67bbf502b", + "a1d47af3a817a03973e53d7eed6467660981651a", + "3d56c1105db26de4c47d7d0f890ddd6bc97f4f97", + "029c6755406e172c41d4eb8456f265054968983a", + "9a685d4cabb0fa6370135ef2737ed731d5759033" + ], + "subject_id": "27830", + "subject_kind": "pr" + }, + "user_visible_path": "Multi-Agent v2 child-completion messages can appear as typed plaintext `agent_message` content with explicit author and recipient metadata instead of assistant-message JSON envelopes." +} diff --git a/artifacts/github/reviews/openai-codex-pr-27961.review.json b/artifacts/github/reviews/openai-codex-pr-27961.review.json new file mode 100644 index 000000000..459ad2e7b --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27961.review.json @@ -0,0 +1,75 @@ +{ + "adoption_opportunity": "Teach Decodex Control Plane and operator UI paths to read `configRequirements/read.allowRemoteControl` before attempting remote-control enrollment or RPCs, and preserve the disabled status/error as an expected managed-policy outcome.", + "caveats": "The policy does not delete the user's persisted remote-control preference. Removing the managed requirement and restarting restores the prior choice.", + "changed_surfaces": [ + "app-server v2 `configRequirements/read` response", + "top-level config requirements parsing and layer precedence", + "remote-control transport startup policy", + "remote-control enrollment, refresh, connection, and persisted-preference mutation", + "remoteControl RPC request validation", + "TUI debug config requirement display", + "app-server protocol JSON and TypeScript schemas", + "remote-control and config RPC tests" + ], + "community_value": "High for managed Codex operators because the PR creates a source-backed remote-control deny gate and observable app-server requirement.", + "compatibility_risk": "High. App-server clients and control planes that assume remote-control RPCs can always enable transport must now handle managed-policy denial and the `allowRemoteControl` requirement field.", + "confidence": "confirmed", + "control_plane_relevance": "High. Decodex Control Plane remote-control compatibility depends on app-server startup policy, request denial semantics, and config requirement readback.", + "deprecated_or_breaking_notes": "Remote control is not removed, but managed `allow_remote_control = false` forces disabled initial state and rejects `remoteControl/*` RPCs with JSON-RPC `-32600` before parameter deserialization.", + "evidence": [ + "PR #27961 says managed deployments need a reliable deny gate for remote control and adds `allow_remote_control` requirements parsing plus `configRequirements/read` exposure as `allowRemoteControl`.", + "`codex-rs/app-server-protocol/src/protocol/v2/config.rs` adds nullable `allow_remote_control` to app-server `ConfigRequirements`.", + "`codex-rs/app-server/src/request_processors/config_processor.rs` maps `allow_remote_control` into the API response and adds a focused test.", + "`codex-rs/app-server-transport/src/transport/remote_control/mod.rs` adds `RemoteControlPolicy::DisabledByRequirements` and policy-aware handle state.", + "`codex-rs/app-server-transport/src/transport/remote_control/desired_state.rs` refuses enablement and persisted-state mutation when the policy disables remote control.", + "`codex-rs/app-server/src/request_processors/remote_control_processor.rs` maps managed-policy enable errors into JSON-RPC request errors.", + "`codex-rs/app-server/tests/suite/v2/remote_control.rs` adds managed-disable coverage for startup, malformed RPCs, explicit enablement, persisted preference, and disabled status notification behavior.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27961.json` records 29 changed files for the merged PR." + ], + "next_actions": [ + { + "reason": "Managed remote-control denial directly affects Decodex app-server and Control Plane compatibility.", + "type": "upstream_impact" + }, + { + "reason": "The change has a clear operator-facing public angle around managed remote-control policy and app-server readback.", + "type": "social_candidate" + } + ], + "observed_change": "Codex adds a managed `allow_remote_control` requirement that exposes `allowRemoteControl` through app-server config requirements and blocks remote-control startup and RPCs when disabled.", + "repo": "openai/codex", + "reviewed_at": "2026-06-13T18:07:16Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27961", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-13T03:10:12Z", + "title": "feat(app-server): enforce managed remote control disable", + "url": "https://github.com/openai/codex/pull/27961" + }, + { + "kind": "commit", + "title": "feat(app-server): enforce managed remote control disable", + "url": "https://github.com/openai/codex/commit/a1f9d5e195dbf73c5ac756e90224f2e4af3a6adf" + }, + { + "kind": "commit", + "title": "fix(app-server): fail explicit remote control under managed disable", + "url": "https://github.com/openai/codex/commit/f4ef520440ce2a1d6b9ea9dcae6bbc272c2c181c" + } + ] + }, + "subject": { + "commit_shas": [ + "a1f9d5e195dbf73c5ac756e90224f2e4af3a6adf", + "69a4db602847e1c1269016243061b76b0687a6da", + "d3918160167a6eaa5a33982ef79d9ccb63636d7b", + "f4ef520440ce2a1d6b9ea9dcae6bbc272c2c181c" + ], + "subject_id": "27961", + "subject_kind": "pr" + }, + "user_visible_path": "Managed Codex deployments can force remote control off with `allow_remote_control = false`; app-server clients can observe `allowRemoteControl: false` and receive explicit remote-control disabled errors instead of starting transport." +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27541.json b/artifacts/github/social-candidates/openai-codex-pr-27541.json new file mode 100644 index 000000000..fb5e21f68 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27541.json @@ -0,0 +1,53 @@ +{ + "audience": "Codex MCP users, plugin operators, and managed-environment maintainers", + "candidate_text": [ + "Codex MCP OAuth can now store credentials in the configured auth backend, including encrypted-local storage and legacy keyring migration. Useful for plugin auth and managed environments. PR: https://github.com/openai/codex/pull/27541" + ], + "caveats": [ + "Keep the claim scoped to MCP OAuth credential storage.", + "Do not imply every Codex secret path changed from this PR alone." + ], + "channel": "x", + "claims": [ + { + "confidence": "confirmed", + "evidence": "artifacts/github/reviews/openai-codex-pr-27541.review.json", + "text": "MCP OAuth credentials now follow the configured auth credential backend." + }, + { + "confidence": "confirmed", + "evidence": "artifacts/github/impact/openai-codex-pr-27541.json", + "text": "The backend routing can affect plugin and MCP auth compatibility." + } + ], + "decision": { + "idempotency_key": "x:decodexspace:openai-codex-pr-27541:practical_explainer", + "reason": "The change has a concrete MCP OAuth storage explanation with source-backed operator value.", + "worthiness": "publish" + }, + "evidence_notes": [ + "MCP OAuth load/save receives the auth keyring backend kind.", + "Encrypted-local storage and legacy keyring migration are wired through rmcp-client.", + "App-server MCP refresh carries the latest backend kind." + ], + "mode": "practical_explainer", + "next_steps": [ + "Let Publisher automation decide whether to reserve or publish this candidate." + ], + "priority": "critical", + "repo": "openai/codex", + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27541", + "source_refs": { + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27541.json" + ], + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27541.review.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27541" + ] + }, + "target_account": "decodexspace" +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27830.json b/artifacts/github/social-candidates/openai-codex-pr-27830.json new file mode 100644 index 000000000..709e37dcc --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27830.json @@ -0,0 +1,53 @@ +{ + "audience": "Multi-Agent v2 users and Control Plane transcript tooling maintainers", + "candidate_text": [ + "Codex Multi-Agent v2 now supports plaintext `agent_message` input alongside encrypted content, preserving author/recipient metadata across history, resume, memories, and traces. PR: https://github.com/openai/codex/pull/27830" + ], + "caveats": [ + "Do not imply encrypted agent-message support was removed.", + "Keep the claim scoped to Multi-Agent v2 typed inter-agent communication." + ], + "channel": "x", + "claims": [ + { + "confidence": "confirmed", + "evidence": "artifacts/github/reviews/openai-codex-pr-27830.review.json", + "text": "Typed agent messages now support plaintext input_text content as well as encrypted content." + }, + { + "confidence": "confirmed", + "evidence": "artifacts/github/impact/openai-codex-pr-27830.json", + "text": "The change matters for transcript, memory, and rollout-trace consumers." + } + ], + "decision": { + "idempotency_key": "x:decodexspace:openai-codex-pr-27830:operator_impact", + "reason": "The change has a clear Multi-Agent v2 transcript and Control Plane tooling angle.", + "worthiness": "publish" + }, + "evidence_notes": [ + "AgentMessageInputContent adds plaintext input_text support.", + "Inter-agent communication is persisted as a dedicated rollout item.", + "Memory and rollout-trace paths preserve agent-message metadata." + ], + "mode": "operator_impact", + "next_steps": [ + "Let Publisher automation decide whether to reserve or publish this candidate." + ], + "priority": "critical", + "repo": "openai/codex", + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27830", + "source_refs": { + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27830.json" + ], + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27830.review.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27830" + ] + }, + "target_account": "decodexspace" +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27961.json b/artifacts/github/social-candidates/openai-codex-pr-27961.json new file mode 100644 index 000000000..09d739b06 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27961.json @@ -0,0 +1,53 @@ +{ + "audience": "Control Plane operators managing Codex remote-control policy", + "candidate_text": [ + "Codex now lets managed deployments force remote control off with `allow_remote_control = false`; app-server exposes `allowRemoteControl` and rejects `remoteControl/*` RPCs under policy. Control Plane operators should gate on it. PR: https://github.com/openai/codex/pull/27961" + ], + "caveats": [ + "Do not imply remote control was removed.", + "Keep the claim scoped to managed requirements and app-server remote-control policy." + ], + "channel": "x", + "claims": [ + { + "confidence": "confirmed", + "evidence": "artifacts/github/reviews/openai-codex-pr-27961.review.json", + "text": "Codex exposes managed remote-control allow/deny policy through app-server config requirements." + }, + { + "confidence": "confirmed", + "evidence": "artifacts/github/impact/openai-codex-pr-27961.json", + "text": "Managed remote-control denial is a Control Plane compatibility risk." + } + ], + "decision": { + "idempotency_key": "x:decodexspace:openai-codex-pr-27961:operator_impact", + "reason": "The change has a concrete managed-deployment Control Plane compatibility angle with direct source links.", + "worthiness": "publish" + }, + "evidence_notes": [ + "Config requirements add allowRemoteControl.", + "Remote-control startup captures managed disabled policy.", + "RemoteControl RPCs are rejected under managed disable." + ], + "mode": "operator_impact", + "next_steps": [ + "Let Publisher automation decide whether to reserve or publish this candidate." + ], + "priority": "critical", + "repo": "openai/codex", + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27961", + "source_refs": { + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27961.json" + ], + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27961.review.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27961" + ] + }, + "target_account": "decodexspace" +} From 3e4b923387c2db7166a8268bc00e158b723743b1 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sun, 14 Jun 2026 08:10:31 +0800 Subject: [PATCH 2/2] {"schema":"decodex/commit/1","summary":"Refresh upstream Radar review artifacts","authority":"manual"} --- .../github/bundles/openai-codex-pr-28032.json | 154 ++++++++++++++++ .../github/impact/openai-codex-pr-28032.json | 47 +++++ .../review-queue/openai-codex-latest.json | 164 ++++++++---------- .../reviews/openai-codex-pr-28032.review.json | 72 ++++++++ .../openai-codex-pr-28032.json | 53 ++++++ 5 files changed, 402 insertions(+), 88 deletions(-) create mode 100644 artifacts/github/bundles/openai-codex-pr-28032.json create mode 100644 artifacts/github/impact/openai-codex-pr-28032.json create mode 100644 artifacts/github/reviews/openai-codex-pr-28032.review.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-28032.json diff --git a/artifacts/github/bundles/openai-codex-pr-28032.json b/artifacts/github/bundles/openai-codex-pr-28032.json new file mode 100644 index 000000000..44bf128bb --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-28032.json @@ -0,0 +1,154 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "anp-oai", + "committed_at": "2026-06-13T04:55:04Z", + "message": "exec-server: carry cwd as PathUri", + "sha": "362785f70469ae7c968c3ef49aae53772e5b9d9e", + "url": "https://github.com/openai/codex/commit/362785f70469ae7c968c3ef49aae53772e5b9d9e" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-13T20:50:07Z", + "message": "codex: fix CI failure on PR #28032", + "sha": "f21ad4deaed11404daa5bdb1ac210ddcb205f73c", + "url": "https://github.com/openai/codex/commit/f21ad4deaed11404daa5bdb1ac210ddcb205f73c" + } + ], + "default_branch": "main", + "docs_refs": [ + "codex-rs/exec-server/README.md" + ], + "examples_refs": [], + "extracted_flags": [ + "URI", + "UNIFIED_EXEC_ENV", + "NO_COLOR", + "CALL_ID", + "COMMAND", + "WINE_BAZEL_OK", + "CODEX_HOME", + "PATH", + "PTY", + "IFS", + "ONLY_THIS", + "STANDARD", + "BASE64_STANDARD", + "JSONRPCR", + "DELAYED_OUTPUT_AFTER_EXIT_PARENT_ARG", + "INT", + "NUL", + "MCP" + ], + "files": [ + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -3665,6 +3665,7 @@ dependencies = [\n \"codex-secrets\",\n \"codex-utils-cargo-bin\",\n \"codex-utils-home-dir\",\n+ \"codex-utils-path-uri\",\n \"codex-utils-pty\",\n \"futures\",\n \"keyring\",", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -57,6 +57,7 @@ use codex_protocol::protocol::ExecCommandSource;\n use codex_tools::ToolName;\n use codex_utils_absolute_path::AbsolutePathBuf;\n use codex_utils_output_truncation::approx_token_count;\n+use codex_utils_path_uri::PathUri;\n \n const UNIFIED_EXEC_ENV: [(&str, &str); 10] = [\n (\"NO_COLOR\", \"1\"),\n@@ -156,7 +157,7 @@ fn exec_server_params_for_request(\n codex_exec_server::ExecParams {\n process_id: exec_server_process_id(process_id).into(),\n argv: request.command.clone(),\n- cwd: request.cwd.to_path_buf(),\n+ cwd: PathUri::from_abs_path(&request.cwd),\n env_policy,\n env,\n tty,", + "path": "codex-rs/core/src/unified_exec/process_manager.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -66,7 +66,7 @@ fn env_overlay_for_exec_server_keeps_runtime_changes_only() {\n }\n \n #[test]\n-fn exec_server_params_use_env_policy_overlay_contract() {\n+fn exec_server_params_use_path_uri_and_env_policy_overlay_contract() {\n let cwd: codex_utils_absolute_path::AbsolutePathBuf = std::env::current_dir()\n .expect(\"current dir\")\n .try_into()\n@@ -115,6 +115,7 @@ fn exec_server_params_use_env_policy_overlay_contract() {\n exec_server_params_for_request(/*process_id*/ 123, &request, /*tty*/ true);\n \n assert_eq!(params.process_id.as_str(), \"123\");\n+ assert_eq!(params.cwd, PathUri::from_abs_path(&request.cwd));\n assert!(params.env_policy.is_some());\n assert_eq!(\n params.env,", + "path": "codex-rs/core/src/unified_exec/process_manager_tests.rs", + "status": "modified" + }, + { + "additions": 12, + "deletions": 58, + "patch_excerpt": "@@ -7,8 +7,6 @@ use codex_features::Feature;\n use codex_protocol::models::PermissionProfile;\n use codex_protocol::protocol::AskForApproval;\n use codex_protocol::protocol::EventMsg;\n-use codex_protocol::protocol::ExecCommandSource;\n-use codex_protocol::protocol::ExecCommandStatus;\n use codex_protocol::protocol::Op;\n use codex_protocol::protocol::TurnEnvironmentSelection;\n use codex_protocol::protocol::TurnEnvironmentSelections;\n@@ -23,7 +21,6 @@ use core_test_support::responses::start_mock_server;\n use core_test_support::test_codex::test_codex;\n use core_test_support::test_codex::turn_permission_fields;\n use core_test_support::wait_for_event;\n-use pretty_assertions::assert_eq;\n use serde_json::json;\n use tokio::io::AsyncBufReadExt;\n use tokio::io::BufReader;\n@@ -33,7 +30,7 @@ const CALL_ID: &str = \"wine-cmd-smoke\";\n const COMMAND: &str = \"echo WINE_BAZEL_OK&&cd\";\n \n #[tokio::test(flavor =...", + "path": "codex-rs/core/tests/remote_env_windows/remote_env_windows_test.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 3, + "patch_excerpt": "@@ -157,7 +157,7 @@ Request params:\n {\n \"processId\": \"proc-1\",\n \"argv\": [\"bash\", \"-lc\", \"printf 'hello\\\\n'\"],\n- \"cwd\": \"/absolute/working/directory\",\n+ \"cwd\": \"file:///absolute/working/directory\",\n \"env\": {\n \"PATH\": \"/usr/bin:/bin\"\n },\n@@ -171,7 +171,7 @@ Field definitions:\n \n - `processId`: caller-chosen stable id for this process within the connection.\n - `argv`: command vector. It must be non-empty.\n-- `cwd`: absolute working directory used for the child process.\n+- `cwd`: `file:` URI for the child process working directory.\n - `env`: environment variables passed to the child process.\n - `tty`: when `true`, spawn a PTY-backed interactive process.\n - `pipeStdin`: when `true`, keep non-PTY stdin writable via `process/write`.\n@@ -409,7 +409,7 @@ Initialize:\n Start a process:\n \n ```json\n-{\"id\":2,\"method\":\"process/start\",\"params\":{\"processId\":\"proc-1\",\"argv\":[\"bash\",\"-lc\",\"pr...", + "path": "codex-rs/exec-server/README.md", + "status": "modified" + }, + { + "additions": 3, + "deletions": 1, + "patch_excerpt": "@@ -498,6 +498,7 @@ mod tests {\n use crate::ProcessId;\n use crate::environment_provider::EnvironmentDefault;\n use crate::environment_provider::EnvironmentProviderSnapshot;\n+ use codex_utils_path_uri::PathUri;\n use pretty_assertions::assert_eq;\n \n fn test_runtime_paths() -> ExecServerRuntimePaths {\n@@ -862,7 +863,8 @@ mod tests {\n .start(crate::ExecParams {\n process_id: ProcessId::from(\"default-env-proc\"),\n argv: vec![\"true\".to_string()],\n- cwd: std::env::current_dir().expect(\"read current dir\"),\n+ cwd: PathUri::from_path(std::env::current_dir().expect(\"read current dir\"))\n+ .expect(\"cwd URI\"),\n env_policy: None,\n env: Default::default(),\n tty: false,", + "path": "codex-rs/exec-server/src/environment.rs", + "status": "modified" + }, + { + "additions": 35, + "deletions": 4, + "patch_excerpt": "@@ -156,6 +156,12 @@ impl LocalProcess {\n .argv\n .split_first()\n .ok_or_else(|| invalid_params(\"argv must not be empty\".to_string()))?;\n+ let native_cwd = params.cwd.to_abs_path().map_err(|err| {\n+ invalid_params(format!(\n+ \"cwd URI `{}` is not valid on this exec-server host: {err}\",\n+ params.cwd\n+ ))\n+ })?;\n \n {\n let mut process_map = self.inner.processes.lock().await;\n@@ -172,7 +178,7 @@ impl LocalProcess {\n codex_utils_pty::spawn_pty_process(\n program,\n args,\n- params.cwd.as_path(),\n+ native_cwd.as_path(),\n &env,\n ¶ms.arg0,\n TerminalSize::default(),\n@@ -182,7 +188,7 @@ impl LocalProcess {\n codex_utils_pty::spawn_pipe_process(\n ...", + "path": "codex-rs/exec-server/src/local_process.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 3, + "patch_excerpt": "@@ -1,5 +1,4 @@\n use std::collections::HashMap;\n-use std::path::PathBuf;\n \n use crate::FileSystemSandboxContext;\n use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;\n@@ -77,7 +76,8 @@ pub struct EnvironmentInfo {\n pub struct ShellInfo {\n /// Stable shell name, for example `zsh`, `bash`, `powershell`, `sh`, or `cmd`.\n pub name: String,\n- /// Path the exec server would use for that shell.\n+ /// Target-native shell executable path or command name. Fallbacks such as `cmd.exe` need not\n+ /// be absolute, so this is not a [`PathUri`].\n pub path: String,\n }\n \n@@ -88,14 +88,17 @@ pub struct ExecParams {\n /// This is a protocol key, not an OS pid.\n pub process_id: ProcessId,\n pub argv: Vec,\n- pub cwd: PathBuf,\n+ /// Working directory URI, interpreted using the exec-server host's path rules at launch time.\n+ pub cwd: PathUri,\n #[s...", + "path": "codex-rs/exec-server/src/protocol.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -2,6 +2,7 @@ use std::collections::HashMap;\n use std::sync::Arc;\n use std::time::Duration;\n \n+use codex_utils_path_uri::PathUri;\n use pretty_assertions::assert_eq;\n use tokio::sync::mpsc;\n use uuid::Uuid;\n@@ -26,7 +27,7 @@ fn exec_params_with_argv(process_id: &str, argv: Vec) -> ExecParams {\n ExecParams {\n process_id: ProcessId::from(process_id),\n argv,\n- cwd: std::env::current_dir().expect(\"cwd\"),\n+ cwd: PathUri::from_path(std::env::current_dir().expect(\"cwd\")).expect(\"cwd URI\"),\n env_policy: None,\n env: inherited_path_env(),\n tty: false,", + "path": "codex-rs/exec-server/src/server/handler/tests.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -195,6 +195,7 @@ mod tests {\n use codex_app_server_protocol::JSONRPCRequest;\n use codex_app_server_protocol::JSONRPCResponse;\n use codex_app_server_protocol::RequestId;\n+ use codex_utils_path_uri::PathUri;\n use serde::Serialize;\n use serde::de::DeserializeOwned;\n use tokio::io::AsyncBufReadExt;\n@@ -396,7 +397,7 @@ mod tests {\n ExecParams {\n process_id,\n argv: sleep_then_print_argv(),\n- cwd: std::env::current_dir().expect(\"cwd\"),\n+ cwd: PathUri::from_path(std::env::current_dir().expect(\"cwd\")).expect(\"cwd URI\"),\n env_policy: None,\n env,\n tty: false,", + "path": "codex-rs/exec-server/src/server/processor.rs", + "status": "modified" + }, + { + "additions": 13, + "deletions": 12, + "patch_excerpt": "@@ -15,6 +15,7 @@ use codex_exec_server::ProcessSignal;\n use codex_exec_server::ReadResponse;\n use codex_exec_server::StartedExecProcess;\n use codex_exec_server::WriteStatus;\n+use codex_utils_path_uri::PathUri;\n use pretty_assertions::assert_eq;\n use tempfile::TempDir;\n use test_case::test_case;\n@@ -72,7 +73,7 @@ async fn assert_exec_process_starts_and_exits(use_remote: bool) -> Result<()> {\n .start(ExecParams {\n process_id: ProcessId::from(\"proc-1\"),\n argv: vec![\"true\".to_string()],\n- cwd: std::env::current_dir()?,\n+ cwd: PathUri::from_path(std::env::current_dir()?)?,\n env_policy: /*env_policy*/ None,\n env: Default::default(),\n tty: false,\n@@ -213,7 +214,7 @@ async fn assert_exec_process_streams_output(use_remote: bool) -> Result<()> {\n \"-c\".to_string(),\n \"sleep 0.05...", + "path": "codex-rs/exec-server/tests/exec_process.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -22,6 +22,7 @@ codex-exec-server = { workspace = true }\n codex-keyring-store = { workspace = true }\n codex-protocol = { workspace = true }\n codex-secrets = { workspace = true }\n+codex-utils-path-uri = { workspace = true }\n codex-utils-pty = { workspace = true }\n codex-utils-home-dir = { workspace = true }\n bytes = { workspace = true }", + "path": "codex-rs/rmcp-client/Cargo.toml", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -35,6 +35,7 @@ use codex_exec_server::ExecEnvPolicy;\n use codex_exec_server::ExecParams;\n use codex_exec_server::ExecProcess;\n use codex_protocol::config_types::ShellEnvironmentPolicyInherit;\n+use codex_utils_path_uri::PathUri;\n #[cfg(unix)]\n use codex_utils_pty::process_group::kill_process_group;\n #[cfg(unix)]\n@@ -487,6 +488,7 @@ impl ExecutorStdioServerLauncher {\n // before sending an executor request.\n let argv = Self::process_api_argv(&program, &args).map_err(io::Error::other)?;\n let env = Self::process_api_env(envs).map_err(io::Error::other)?;\n+ let cwd = PathUri::from_path(cwd)?;\n let process_id = ExecutorProcessTransport::next_process_id();\n // Start the MCP server process on the executor with raw pipes. `tty=false`\n // keeps stdout as a clean protocol stream, while `pipe_stdin=true` lets", + "path": "codex-rs/rmcp-client/src/stdio_server_launcher.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#28032" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\r\n\r\nThis is the second-to-last place in the exec-server protocol that needs to migrate to URIs to support cross-OS operation.\r\n\r\n## What\r\n\r\n- Change `ExecParams.cwd` to `PathUri`.\r\n- Keep the cwd URI-shaped through core and rmcp producers, converting it to `AbsolutePathBuf` only in `LocalProcess::start_process`.\r\n- Reject non-native cwd URIs before launch and update the affected protocol documentation and call sites.", + "labels": [], + "merged_at": "2026-06-13T20:56:42Z", + "number": 28032, + "state": "merged", + "title": "[codex] Carry exec-server cwd as PathUri", + "url": "https://github.com/openai/codex/pull/28032" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-28032.json b/artifacts/github/impact/openai-codex-pr-28032.json new file mode 100644 index 000000000..f1ae6f058 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-28032.json @@ -0,0 +1,47 @@ +{ + "candidate_followups": [ + "Audit Decodex remote execution and MCP launcher paths for raw cwd strings and convert them to target-native `file:` URI handling when exec-server protocol support is consumed.", + "Surface non-native cwd URI rejection as a cross-host path compatibility state instead of retrying process launch as a transient failure.", + "Keep shell path and argv0 handling separate from cwd URI migration because upstream intentionally leaves them as plain strings." + ], + "caveats": [ + "The PR is scoped to exec-server cwd handling and does not migrate shell path or argv0 fields to PathUri.", + "The compatibility risk applies to clients that speak the exec-server process/start protocol directly or through remote executor plumbing.", + "Decodex implementation work still needs a separate issue if local Control Plane code sends raw cwd paths into an upstream exec-server bridge." + ], + "confidence": "confirmed", + "control_plane_impact": "compat_risk", + "evidence": [ + "Exec-server `ExecParams.cwd` is now a `PathUri` rather than `PathBuf`.", + "Core unified exec and rmcp-client executor startup both convert cwd into `PathUri` before sending exec-server params.", + "Local process startup rejects cwd URIs that cannot be converted to a native absolute path on the exec-server host.", + "Exec-server README examples now use `file:` cwd URIs in `process/start` requests.", + "The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-28032.review.json`." + ], + "observed_change": "Codex changes exec-server `process/start.cwd` from a raw path to a `PathUri`, keeps cwd URI-shaped through core and rmcp producers, and rejects non-native cwd URIs before process launch.", + "public_signal_decision": "publish", + "publisher_angle": "operator_impact", + "repo": "openai/codex", + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-28032", + "social_notes": [ + "Frame this as a remote execution and cross-OS cwd compatibility change.", + "Do not imply all exec-server path fields are now URI-backed; the PR explicitly leaves shell path and argv0 as strings." + ], + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-13T20:56:42Z", + "title": "[codex] Carry exec-server cwd as PathUri", + "url": "https://github.com/openai/codex/pull/28032" + }, + { + "kind": "pull_request", + "meta": "artifacts/github/reviews/openai-codex-pr-28032.review.json", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/28032" + } + ] + } +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index b3b4c8704..a340e71a3 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": 21, + "critical": 20, "high": 5, "low": 0, - "normal": 14, + "normal": 15, "published_subjects_seen": 0, "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-13T18:05:12.69079Z", + "generated_at": "2026-06-14T00:05:30.172833Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,58 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "auth_account", - "breaking_change", - "new_feature", - "protocol_change", - "release_packaging", - "security_policy" - ], - "changed_file_count": 22, - "commit_shas": [ - "37efa545e8004b930a496c91c10ccc860937c29d", - "efd9be5adb25da27764e9e5a2221d14356ce7838", - "44dee73a25a44376823a6c3015779f6c476d47af", - "ebad839a70f8719b40fa78586fc4fa5f5e130e4b", - "3a9e2d6f25149e2b2e3fe707ce682b211c007349", - "c8f27780ed88df644693301f7fb79c661a6995d4", - "66a18813f2cded55678c34de157ca9143f8a432b", - "fa3b65a7e43afac7a10f8c052539b133f2d6dd1f" - ], - "committed_at": "2026-06-12T18:11:13Z", - "next_step": "ai_review_required", - "pr_number": 27720, - "pr_url": "https://github.com/openai/codex/pull/27720", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, breaking_change, new_feature, protocol_change, release_packaging, security_policy.", - "sample_paths": [ - "codex-rs/app-server-protocol/schema/json/ClientRequest.json", - "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", - "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", - "codex-rs/app-server-protocol/schema/typescript/RealtimeConversationArchitecture.ts", - "codex-rs/app-server-protocol/schema/typescript/index.ts", - "codex-rs/app-server-protocol/src/protocol/common.rs", - "codex-rs/app-server-protocol/src/protocol/v2/realtime.rs", - "codex-rs/app-server/src/request_processors/turn_processor.rs", - "codex-rs/app-server/tests/suite/v2/experimental_api.rs", - "codex-rs/app-server/tests/suite/v2/realtime_conversation.rs", - "codex-rs/codex-api/src/endpoint/realtime_call.rs", - "codex-rs/config/src/config_toml.rs" - ], - "source_state": "merged", - "subject_id": "27720", - "subject_kind": "pr", - "surface_hints": [ - "app_server_protocol", - "cli_tui", - "config_hooks", - "tests_ci" - ], - "title": "realtime: add AVAS architecture override", - "url": "https://github.com/openai/codex/pull/27720" - }, { "attention_flags": [ "auth_account", @@ -959,39 +907,6 @@ "title": "feat(app-server): enforce managed remote control disable", "url": "https://github.com/openai/codex/pull/27961" }, - { - "attention_flags": [ - "auth_account", - "protocol_change", - "security_policy" - ], - "changed_file_count": 4, - "commit_shas": [ - "3b96f165c11c5ad348600e2a4a665c4d976b4ef4", - "bf374d31fffb7542c394893a819bb723a4a7f01d" - ], - "committed_at": "2026-06-12T18:17:12Z", - "next_step": "ai_review_required", - "pr_number": 27919, - "pr_url": "https://github.com/openai/codex/pull/27919", - "review_priority": "high", - "review_reason": "Needs AI review for auth_account, protocol_change, security_policy.", - "sample_paths": [ - "codex-rs/core/src/config/mod.rs", - "codex-rs/core/src/tools/handlers/multi_agents_spec.rs", - "codex-rs/core/tests/suite/search_tool.rs", - "codex-rs/core/tests/suite/spawn_agent_description.rs" - ], - "source_state": "merged", - "subject_id": "27919", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks", - "tests_ci" - ], - "title": "chore: prompt MAv2", - "url": "https://github.com/openai/codex/pull/27919" - }, { "attention_flags": [ "protocol_change" @@ -1149,6 +1064,52 @@ "title": "[codex] Add hermetic Wine exec-server test", "url": "https://github.com/openai/codex/pull/27937" }, + { + "attention_flags": [ + "auth_account", + "new_feature", + "protocol_change", + "security_policy" + ], + "changed_file_count": 13, + "commit_shas": [ + "362785f70469ae7c968c3ef49aae53772e5b9d9e", + "f21ad4deaed11404daa5bdb1ac210ddcb205f73c" + ], + "committed_at": "2026-06-13T20:56:42Z", + "next_step": "ai_review_required", + "pr_number": 28032, + "pr_url": "https://github.com/openai/codex/pull/28032", + "review_priority": "high", + "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, security_policy.", + "sample_paths": [ + "codex-rs/Cargo.lock", + "codex-rs/core/src/unified_exec/process_manager.rs", + "codex-rs/core/src/unified_exec/process_manager_tests.rs", + "codex-rs/core/tests/remote_env_windows/remote_env_windows_test.rs", + "codex-rs/exec-server/README.md", + "codex-rs/exec-server/src/environment.rs", + "codex-rs/exec-server/src/local_process.rs", + "codex-rs/exec-server/src/protocol.rs", + "codex-rs/exec-server/src/server/handler/tests.rs", + "codex-rs/exec-server/src/server/processor.rs", + "codex-rs/exec-server/tests/exec_process.rs", + "codex-rs/rmcp-client/Cargo.toml" + ], + "source_state": "merged", + "subject_id": "28032", + "subject_kind": "pr", + "surface_hints": [ + "app_server_protocol", + "cli_tui", + "config_hooks", + "docs_examples", + "mcp_plugins", + "tests_ci" + ], + "title": "[codex] Carry exec-server cwd as PathUri", + "url": "https://github.com/openai/codex/pull/28032" + }, { "attention_flags": [ "deprecated_removed", @@ -1601,6 +1562,33 @@ ], "title": "[codex] Send turn state through compact requests", "url": "https://github.com/openai/codex/pull/28002" + }, + { + "attention_flags": [ + "release_packaging" + ], + "changed_file_count": 1, + "commit_shas": [ + "4b3fd5e359221031616a66cad36299c4becd810b" + ], + "committed_at": "2026-06-13T19:55:53Z", + "next_step": "ai_review_required", + "pr_number": 28001, + "pr_url": "https://github.com/openai/codex/pull/28001", + "review_priority": "normal", + "review_reason": "Needs AI review for release_packaging.", + "sample_paths": [ + ".github/workflows/rust-release-windows.yml" + ], + "source_state": "merged", + "subject_id": "28001", + "subject_kind": "pr", + "surface_hints": [ + "release_packaging", + "tests_ci" + ], + "title": "[codex] package Windows ARM64 on x64", + "url": "https://github.com/openai/codex/pull/28001" } ] } diff --git a/artifacts/github/reviews/openai-codex-pr-28032.review.json b/artifacts/github/reviews/openai-codex-pr-28032.review.json new file mode 100644 index 000000000..eaa93f66a --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-28032.review.json @@ -0,0 +1,72 @@ +{ + "adoption_opportunity": "Teach Decodex Control Plane and any remote executor integration to preserve `cwd` as a `PathUri`, send target-native `file:` URIs for exec-server `process/start`, and classify non-native cwd rejection as a platform/path mismatch instead of a generic process-launch failure.", + "caveats": "This PR changes the exec-server protocol shape for cwd only; shell paths and argv0 intentionally remain plain strings because they may be command names rather than absolute paths.", + "changed_surfaces": [ + "exec-server `ExecParams.cwd` protocol field", + "exec-server README process/start examples", + "local exec-server process launch validation", + "core unified exec request conversion", + "rmcp-client executor stdio launcher request conversion", + "remote Windows exec-server test expectations", + "exec-server process tests" + ], + "community_value": "High for remote executor and MCP users because the PR makes cwd transport explicit across OS boundaries and gives a clear rejection path for non-native cwd URIs.", + "compatibility_risk": "High. Exec-server clients that still send a raw absolute path for `process/start.cwd` must switch to a target-native `file:` URI, and clients must handle JSON-RPC invalid-params rejection when the URI is not native to the exec-server host.", + "confidence": "confirmed", + "control_plane_relevance": "High. Decodex Control Plane compatibility depends on remote execution, MCP launcher plumbing, and cross-host cwd handling behaving predictably.", + "deprecated_or_breaking_notes": "The exec-server `process/start.cwd` field is no longer modeled as a raw path buffer; it is a `PathUri` and is converted to a native absolute path only inside `LocalProcess::start_process`.", + "evidence": [ + "PR #28032 says `ExecParams.cwd` changes to `PathUri`, cwd remains URI-shaped through core and rmcp producers, and non-native cwd URIs are rejected before launch.", + "`codex-rs/exec-server/src/protocol.rs` changes `ExecParams.cwd` from `PathBuf` to `PathUri` and documents it as a working directory URI interpreted with exec-server host path rules.", + "`codex-rs/core/src/unified_exec/process_manager.rs` now sends `PathUri::from_abs_path(&request.cwd)` into exec-server params.", + "`codex-rs/rmcp-client/src/stdio_server_launcher.rs` converts the launcher cwd with `PathUri::from_path(cwd)?` before starting an executor process.", + "`codex-rs/exec-server/src/local_process.rs` converts `params.cwd.to_abs_path()` at launch and returns invalid params when the URI is not valid on the exec-server host.", + "`codex-rs/exec-server/README.md` updates `process/start` examples from raw cwd paths to `file:` URIs.", + "`codex-rs/core/tests/remote_env_windows/remote_env_windows_test.rs` now expects a non-native cwd URI to be rejected before process lifecycle events and surfaced to the model as an exec-server rejected request.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-28032.json` records 13 changed files for the merged PR." + ], + "next_actions": [ + { + "reason": "The exec-server cwd protocol change can break Control Plane or MCP launcher paths that still send raw paths or do not distinguish non-native cwd rejection.", + "type": "upstream_impact" + }, + { + "reason": "The change has a clear operator-facing public angle around cross-OS remote execution and cwd URI compatibility.", + "type": "social_candidate" + } + ], + "observed_change": "Codex changes exec-server `process/start.cwd` from a raw path to a `PathUri`, keeps cwd URI-shaped through core and rmcp producers, and rejects non-native cwd URIs before process launch.", + "repo": "openai/codex", + "reviewed_at": "2026-06-14T00:06:54Z", + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-28032", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "meta": "Merged 2026-06-13T20:56:42Z", + "title": "[codex] Carry exec-server cwd as PathUri", + "url": "https://github.com/openai/codex/pull/28032" + }, + { + "kind": "commit", + "title": "exec-server: carry cwd as PathUri", + "url": "https://github.com/openai/codex/commit/362785f70469ae7c968c3ef49aae53772e5b9d9e" + }, + { + "kind": "commit", + "title": "codex: fix CI failure on PR #28032", + "url": "https://github.com/openai/codex/commit/f21ad4deaed11404daa5bdb1ac210ddcb205f73c" + } + ] + }, + "subject": { + "commit_shas": [ + "362785f70469ae7c968c3ef49aae53772e5b9d9e", + "f21ad4deaed11404daa5bdb1ac210ddcb205f73c" + ], + "subject_id": "28032", + "subject_kind": "pr" + }, + "user_visible_path": "Remote exec-server and MCP launcher clients must send cwd as a target-native `file:` URI in `process/start`; if the cwd URI is not native to the exec-server host, Codex rejects it before emitting process lifecycle events." +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-28032.json b/artifacts/github/social-candidates/openai-codex-pr-28032.json new file mode 100644 index 000000000..f3e8112b9 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-28032.json @@ -0,0 +1,53 @@ +{ + "audience": "Control Plane operators and remote exec-server integrators", + "candidate_text": [ + "Codex exec-server now expects `process/start.cwd` as a `file:` URI, carries cwd as `PathUri`, and rejects non-native cwd before launch. Remote executor and MCP launcher clients should send target-native cwd URIs. PR: https://github.com/openai/codex/pull/28032" + ], + "caveats": [ + "Do not imply every exec-server path-like field moved to PathUri.", + "Keep the claim scoped to cwd handling in exec-server process/start and remote executor plumbing." + ], + "channel": "x", + "claims": [ + { + "confidence": "confirmed", + "evidence": "artifacts/github/reviews/openai-codex-pr-28032.review.json", + "text": "Codex exec-server now represents process/start cwd as a PathUri." + }, + { + "confidence": "confirmed", + "evidence": "artifacts/github/impact/openai-codex-pr-28032.json", + "text": "Raw-path cwd clients or cross-host cwd mismatches are a Control Plane compatibility risk." + } + ], + "decision": { + "idempotency_key": "x:decodexspace:openai-codex-pr-28032:operator_impact", + "reason": "The change has a concrete remote-execution compatibility angle with direct source links.", + "worthiness": "publish" + }, + "evidence_notes": [ + "ExecParams.cwd changed from PathBuf to PathUri.", + "Core and rmcp-client producers now convert cwd to PathUri.", + "Local process startup rejects non-native cwd URIs before launch." + ], + "mode": "operator_impact", + "next_steps": [ + "Let Publisher automation decide whether to reserve or publish this candidate." + ], + "priority": "high", + "repo": "openai/codex", + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-28032", + "source_refs": { + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-28032.json" + ], + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-28032.review.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/28032" + ] + }, + "target_account": "decodexspace" +}