From 212cd3c8fa51260a578650a123d7c168834eb96f Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Wed, 10 Jun 2026 04:13:31 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Persist upstream Radar review artifacts","authority":"manual"} --- .../github/bundles/openai-codex-pr-26486.json | 125 +++ .../github/bundles/openai-codex-pr-26687.json | 839 ++++++++++++++++++ .../github/bundles/openai-codex-pr-26840.json | 223 +++++ .../github/bundles/openai-codex-pr-26934.json | 60 ++ .../github/impact/openai-codex-pr-26486.json | 45 + .../github/impact/openai-codex-pr-26687.json | 45 + .../github/impact/openai-codex-pr-26840.json | 45 + .../github/impact/openai-codex-pr-26934.json | 45 + .../review-queue/openai-codex-latest.json | 576 ++++++------ .../reviews/openai-codex-pr-26486.review.json | 76 ++ .../reviews/openai-codex-pr-26687.review.json | 91 ++ .../reviews/openai-codex-pr-26840.review.json | 84 ++ .../reviews/openai-codex-pr-26934.review.json | 59 ++ .../openai-codex-pr-26486.json | 54 ++ .../openai-codex-pr-26687.json | 54 ++ .../openai-codex-pr-26840.json | 55 ++ .../openai-codex-pr-26934.json | 54 ++ 17 files changed, 2244 insertions(+), 286 deletions(-) create mode 100644 artifacts/github/bundles/openai-codex-pr-26486.json create mode 100644 artifacts/github/bundles/openai-codex-pr-26687.json create mode 100644 artifacts/github/bundles/openai-codex-pr-26840.json create mode 100644 artifacts/github/bundles/openai-codex-pr-26934.json create mode 100644 artifacts/github/impact/openai-codex-pr-26486.json create mode 100644 artifacts/github/impact/openai-codex-pr-26687.json create mode 100644 artifacts/github/impact/openai-codex-pr-26840.json create mode 100644 artifacts/github/impact/openai-codex-pr-26934.json create mode 100644 artifacts/github/reviews/openai-codex-pr-26486.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-26687.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-26840.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-26934.review.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-26486.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-26687.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-26840.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-26934.json diff --git a/artifacts/github/bundles/openai-codex-pr-26486.json b/artifacts/github/bundles/openai-codex-pr-26486.json new file mode 100644 index 000000000..3a572f327 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-26486.json @@ -0,0 +1,125 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "won-openai", + "committed_at": "2026-06-04T23:20:22Z", + "message": "draft", + "sha": "6ca9ccf9cd12de3a88ffa2a001eb78a0b2f97c10", + "url": "https://github.com/openai/codex/commit/6ca9ccf9cd12de3a88ffa2a001eb78a0b2f97c10" + }, + { + "author": "won-openai", + "committed_at": "2026-06-04T23:44:07Z", + "message": "fix", + "sha": "0165dbf6eda0ccb61187df6752a4e08de93f520c", + "url": "https://github.com/openai/codex/commit/0165dbf6eda0ccb61187df6752a4e08de93f520c" + }, + { + "author": "won-openai", + "committed_at": "2026-06-05T22:30:15Z", + "message": "megaturd", + "sha": "9457965868264ba8838440a6d9ff539d830ee0af", + "url": "https://github.com/openai/codex/commit/9457965868264ba8838440a6d9ff539d830ee0af" + }, + { + "author": "won-openai", + "committed_at": "2026-06-08T16:18:14Z", + "message": "comment", + "sha": "2ff147fc6a4721000547e8b0d6b8ea8d97d58f24", + "url": "https://github.com/openai/codex/commit/2ff147fc6a4721000547e8b0d6b8ea8d97d58f24" + }, + { + "author": "won-openai", + "committed_at": "2026-06-08T16:33:39Z", + "message": "Merge remote-tracking branch 'origin/main' into imggenext_tool", + "sha": "c5a021feb1b677c105f0f51044311b62b18925af", + "url": "https://github.com/openai/codex/commit/c5a021feb1b677c105f0f51044311b62b18925af" + }, + { + "author": "won-openai", + "committed_at": "2026-06-08T20:55:06Z", + "message": "ci fix", + "sha": "034835589958076a97792994e22e724ad6d9a496", + "url": "https://github.com/openai/codex/commit/034835589958076a97792994e22e724ad6d9a496" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "URL", + "RESULT", + "TINY_PNG_BYTES", + "V2U", + "OPENAI_API_KEY", + "DEFAULT_READ_TIMEOUT", + "POST", + "ANYTHING", + "IMAGE_GEN_NAMESPACE", + "IMAGEGEN_TOOL_NAME", + "DEFAULT_IMAGE_DETAIL", + "API", + "IMAGE_MODEL", + "MAX_EDIT_IMAGES" + ], + "files": [ + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -3106,6 +3106,7 @@ dependencies = [\n \"codex-protocol\",\n \"codex-tools\",\n \"codex-utils-absolute-path\",\n+ \"codex-utils-image\",\n \"http 1.4.0\",\n \"pretty_assertions\",\n \"schemars 0.8.22\",", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 154, + "deletions": 6, + "patch_excerpt": "@@ -29,6 +29,11 @@ use wiremock::matchers::method;\n use wiremock::matchers::path;\n \n const RESULT: &str = \"cG5n\";\n+const TINY_PNG_BYTES: &[u8] = &[\n+ 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0,\n+ 0, 0, 31, 21, 196, 137, 0, 0, 0, 11, 73, 68, 65, 84, 120, 156, 99, 96, 0, 2, 0, 0, 5, 0, 1,\n+ 122, 94, 171, 63, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,\n+];\n \n #[derive(Clone, Copy)]\n enum ImagegenTestMode {\n@@ -59,7 +64,6 @@ async fn standalone_image_generation_returns_saved_path_hint_to_model() -> Resul\n \"image_gen\",\n \"imagegen\",\n &json!({\n- \"action\": \"generate\",\n \"prompt\": \"paint a blue whale\",\n })\n .to_string(),\n@@ -142,6 +146,67 @@ async fn standalone_image_generation_returns_saved_...", + "path": "codex-rs/app-server/tests/suite/v2/imagegen_extension.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -23,6 +23,7 @@ codex-model-provider-info = { workspace = true }\n codex-protocol = { workspace = true }\n codex-tools = { workspace = true }\n codex-utils-absolute-path = { workspace = true }\n+codex-utils-image = { workspace = true }\n http = { workspace = true }\n schemars = { workspace = true }\n serde = { workspace = true, features = [\"derive\"] }", + "path": "codex-rs/ext/image-generation/Cargo.toml", + "status": "modified" + }, + { + "additions": 8, + "deletions": 3, + "patch_excerpt": "@@ -5,8 +5,13 @@ The `image_gen.imagegen` tool enables image generation from descriptions and edi\n \n Guidelines:\n - In code mode, pass the result to `generatedImage(result)`.\n-- Set `action` to `generate` when the user asks for a brand new image.\n-- Set `action` to `edit` when the user asks to modify an existing image from the conversation history.\n-- Directly generate the image without reconfirmation or clarification.\n+- Omit both `referenced_image_paths` and `num_last_images_to_include` when generating a brand new image.\n+- For edits, use `referenced_image_paths` when every target image has a local file path.\n+- If you have not seen a local image yet, use `view_image` to inspect it before editing.\n+- Use `num_last_images_to_include` only when at least one target image has no local file path.\n+- Set `num_last_images_to_include` to the smallest number of recent conversation images that i...", + "path": "codex-rs/ext/image-generation/imagegen_description.md", + "status": "modified" + }, + { + "additions": 160, + "deletions": 212, + "patch_excerpt": "@@ -19,10 +19,9 @@ use pretty_assertions::assert_eq;\n \n use super::GeneratedImageOutput;\n use super::ImageRequest;\n-use super::ImagegenAction;\n use super::ImagegenArgs;\n use super::imagegen_tool_spec;\n-use super::request_for_action;\n+use super::request_for_args;\n use crate::IMAGE_GEN_NAMESPACE;\n use crate::IMAGEGEN_TOOL_NAME;\n \n@@ -39,10 +38,17 @@ fn uses_reserved_image_gen_namespace() {\n }\n \n #[test]\n-fn generate_uses_fixed_request_defaults() {\n+fn omitted_references_generate_with_fixed_defaults() {\n assert_eq!(\n- request_for_action(&args(ImagegenAction::Generate, \"paint a moonlit lake\"), &[])\n- .expect(\"generation request should build\"),\n+ request_for_args(\n+ &ImagegenArgs {\n+ prompt: \"paint a moonlit lake\".to_string(),\n+ referenced_image_paths: None,\n+ num_last_images_to_include: None,\n+ },...", + "path": "codex-rs/ext/image-generation/src/tests.rs", + "status": "modified" + }, + { + "additions": 145, + "deletions": 103, + "patch_excerpt": "@@ -1,3 +1,5 @@\n+use std::collections::HashSet;\n+\n use codex_api::ImageBackground;\n use codex_api::ImageEditRequest;\n use codex_api::ImageGenerationRequest;\n@@ -28,6 +30,8 @@ use codex_tools::ResponsesApiTool;\n use codex_tools::ToolExposure;\n use codex_tools::default_namespace_description;\n use codex_utils_absolute_path::AbsolutePathBuf;\n+use codex_utils_image::PromptImageMode;\n+use codex_utils_image::load_for_prompt_bytes;\n use schemars::JsonSchema;\n use schemars::r#gen::SchemaSettings;\n use serde::Deserialize;\n@@ -68,14 +72,10 @@ impl ImageGenerationTool {\n #[serde(deny_unknown_fields)]\n struct ImagegenArgs {\n prompt: String,\n- action: ImagegenAction,\n-}\n-\n-#[derive(Debug, Deserialize, JsonSchema)]\n-#[serde(rename_all = \"lowercase\")]\n-enum ImagegenAction {\n- Generate,\n- Edit,\n+ #[schemars(length(max = 5))]\n+ referenced_image_paths: Option>,\n+ ...", + "path": "codex-rs/ext/image-generation/src/tool.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\r\n\r\nImage edits should use the exact images selected by the model instead of inferring edit inputs from conversation history.\r\n\r\n## What changed\r\n\r\n- Replaced the image tool's `action` argument with optional `referenced_image_paths`.\r\n- Treats omitted or empty references as generation and populated references as editing.\r\n- Reads referenced absolute image paths and packages them as image data URLs for the edit request.\r\n- Removed the previous history-selection and image-count heuristics.\r\n- Updated direct and code-mode tool instructions and calls.\r\n- Added an app-server integration test covering an attached image routed to the image edit endpoint.\r\n\r\n## Validation\r\n- Tested end-to-end on local `just codex` with copy pasted image, attached image, etc. \r\n- `just test -p codex-image-generation-extension`\r\n- `just test -p codex-app-server standalone_image_edit_uses_attached_model_visible_image`\r\n- `just fix -p codex-image-generation-extension`\r\n- `just bazel-lock-check`", + "labels": [], + "merged_at": "2026-06-08T21:23:56Z", + "number": 26486, + "state": "merged", + "title": "Route image edits through referenced file paths", + "url": "https://github.com/openai/codex/pull/26486" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-26687.json b/artifacts/github/bundles/openai-codex-pr-26687.json new file mode 100644 index 000000000..8b827e0cf --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-26687.json @@ -0,0 +1,839 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "pakrym-oai", + "committed_at": "2026-06-05T22:20:49Z", + "message": "Pair thread environment settings", + "sha": "534befc48bc2eec3bd002a31f5ac2c3e23a1f29a", + "url": "https://github.com/openai/codex/commit/534befc48bc2eec3bd002a31f5ac2c3e23a1f29a" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-05T22:28:24Z", + "message": "Tidy local environment selection tests", + "sha": "bf26b031e535e3fb925bfacf41daf44ef87231ee", + "url": "https://github.com/openai/codex/commit/bf26b031e535e3fb925bfacf41daf44ef87231ee" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-05T22:42:22Z", + "message": "Merge remote-tracking branch 'origin/main' into pakrym/full-ci-thread-environment-settings", + "sha": "e0f10fa7e41d0cbfe3504b38c735e1de866cab27", + "url": "https://github.com/openai/codex/commit/e0f10fa7e41d0cbfe3504b38c735e1de866cab27" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-05T23:48:57Z", + "message": "Tidy environment settings test updates", + "sha": "09dccdf652b9c4c4995517a0191679836d69b026", + "url": "https://github.com/openai/codex/commit/09dccdf652b9c4c4995517a0191679836d69b026" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-05T23:49:48Z", + "message": "Merge remote-tracking branch 'origin/main' into pakrym/full-ci-thread-environment-settings", + "sha": "4e459c4fcc4b6c10438e8e5e12430e64f3dd43ba", + "url": "https://github.com/openai/codex/commit/4e459c4fcc4b6c10438e8e5e12430e64f3dd43ba" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T00:05:41Z", + "message": "Keep unrelated test expectations unchanged", + "sha": "68060899b6a45f5d67c84938ffc906545c5e3b41", + "url": "https://github.com/openai/codex/commit/68060899b6a45f5d67c84938ffc906545c5e3b41" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:01:05Z", + "message": "Move turn environment selections to protocol", + "sha": "e239da0a36a20367e88e0f9d7d71b5cfb6cb5ca0", + "url": "https://github.com/openai/codex/commit/e239da0a36a20367e88e0f9d7d71b5cfb6cb5ca0" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:04:28Z", + "message": "Delete obsolete cwd update test", + "sha": "505dbae2c71a5886d6e12ff1082279fef26d94fd", + "url": "https://github.com/openai/codex/commit/505dbae2c71a5886d6e12ff1082279fef26d94fd" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:11:31Z", + "message": "Remove unused environment retarget helper", + "sha": "d63b1a125f6e97fd1d4f26232455343207521b89", + "url": "https://github.com/openai/codex/commit/d63b1a125f6e97fd1d4f26232455343207521b89" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:14:33Z", + "message": "Pair core thread environment overrides", + "sha": "d98e9b984c0325d31fb1b58969e279fbedd09aca", + "url": "https://github.com/openai/codex/commit/d98e9b984c0325d31fb1b58969e279fbedd09aca" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:20:19Z", + "message": "Pair thread settings build environments", + "sha": "fc6f77e19b0b41235d88a4879609a28eed97bb2b", + "url": "https://github.com/openai/codex/commit/fc6f77e19b0b41235d88a4879609a28eed97bb2b" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:32:23Z", + "message": "Remove unused thread read test helper", + "sha": "6046b245a3f0efe30ca4012dec10c06c6fa32445", + "url": "https://github.com/openai/codex/commit/6046b245a3f0efe30ca4012dec10c06c6fa32445" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:57:44Z", + "message": "Delete default environments startup test", + "sha": "6a420d41ac70f172bb6deb9b75b6c2f5c93cdfa8", + "url": "https://github.com/openai/codex/commit/6a420d41ac70f172bb6deb9b75b6c2f5c93cdfa8" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T02:59:44Z", + "message": "codex: address PR review feedback (#26687)", + "sha": "4923381143d028955a8542cd8c390f1505e83851", + "url": "https://github.com/openai/codex/commit/4923381143d028955a8542cd8c390f1505e83851" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T03:07:15Z", + "message": "codex: address PR review feedback (#26687)", + "sha": "d10fb7bc65892d496a9e9ae0e0eed585ddd21d5f", + "url": "https://github.com/openai/codex/commit/d10fb7bc65892d496a9e9ae0e0eed585ddd21d5f" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T03:45:38Z", + "message": "Use local environment id for fallback cwd", + "sha": "acb9e264576e9d76f8438129f3eb321ff9ac2fd5", + "url": "https://github.com/openai/codex/commit/acb9e264576e9d76f8438129f3eb321ff9ac2fd5" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-06T04:47:17Z", + "message": "Fix clippy and argument lint failures", + "sha": "1c883fdb9219aff8ed78464669480a58f7745ce6", + "url": "https://github.com/openai/codex/commit/1c883fdb9219aff8ed78464669480a58f7745ce6" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-08T16:06:44Z", + "message": "Merge remote-tracking branch 'origin/main' into pakrym/full-ci-thread-environment-settings", + "sha": "12e18e679d1dc46c712b87fc8f21c04ff95a8310", + "url": "https://github.com/openai/codex/commit/12e18e679d1dc46c712b87fc8f21c04ff95a8310" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-08T16:12:03Z", + "message": "codex: fix guardian review cwd clippy", + "sha": "c1205d9c1a9d6fd80f6d4cea7198805c243fb9f6", + "url": "https://github.com/openai/codex/commit/c1205d9c1a9d6fd80f6d4cea7198805c243fb9f6" + }, + { + "author": "pakrym-oai", + "committed_at": "2026-06-08T18:46:30Z", + "message": "codex: fix stale turn environment test override", + "sha": "f6e390602e97aa867a228c399846440d54d93cb3", + "url": "https://github.com/openai/codex/commit/f6e390602e97aa867a228c399846440d54d93cb3" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "API", + "MCP", + "--check", + "LOCAL_ENVIRONMENT_ID", + "LOCAL_FS", + "FEATURES", + "USER_MESSAGE_BEGIN", + "MAX_USER_INPUT_TEXT_CHARS", + "CARGO_PKG_VERSION", + "JSONRPCE", + "TODO", + "REMOTE_EXEC_SERVER_URL_ENV_VAR", + "CODEX_TEST_REMOTE_EXEC_SERVER_URL", + "REMOTE_TEST_INSTANCE_COUNTER", + "SUBMIT_TURN_COMPLETE_TIMEOUT", + "REMOTE_ENVIRONMENT_ID", + "THIRD_USER_MSG", + "USER_ONE", + "FIRST_AUTO_MSG", + "SECOND_AUTO_MSG", + "POST_AUTO_USER_MSG", + "MULTI_AUTO_MSG", + "FUNCTION_CALL_LIMIT_MSG", + "USER_TWO", + "PRETURN_CONTEXT_DIFF_CWD", + "AFTER_MANUAL_EMPTY_COMPACT", + "TURN_ONE_USER", + "TURN_TWO_PREFIX", + "TURN_THREE_TOOL_USER", + "TURN_FIVE_USER", + "SETUP_USER", + "BEFORE_SWITCH_USER", + "AFTER_SWITCH_USER", + "SCHEMA", + "ROOT_PROMPT", + "SAMPLE_PLUGIN_CONFIG_NAME", + "USER", + "UNIX_EPOCH", + "INTERNAL_SUBAGENT_PROMPT", + "JSON" + ], + "files": [ + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -76,7 +76,7 @@ pub struct TurnStartParams {\n #[experimental(\"turn/start.additionalContext\")]\n #[ts(optional = nullable)]\n pub additional_context: Option>,\n- /// Optional turn-scoped environments.\n+ /// Optional environments for this turn and subsequent turns.\n ///\n /// Omitted uses the thread sticky environments. Empty disables\n /// environment access for this turn. Non-empty selects the first", + "path": "codex-rs/app-server-protocol/src/protocol/v2/turn.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -767,7 +767,7 @@ pub(crate) async fn apply_bespoke_event_handling(\n let requested_permissions = request.permissions.clone();\n let request_cwd = match request.cwd.clone() {\n Some(cwd) => cwd,\n- None => conversation.config_snapshot().await.cwd,\n+ None => conversation.config_snapshot().await.cwd().clone(),\n };\n let params = PermissionsRequestApprovalParams {\n thread_id: conversation_id.to_string(),\n@@ -1161,7 +1161,7 @@ pub(crate) async fn apply_bespoke_event_handling(\n return;\n }\n };\n- let fallback_cwd = conversation.config_snapshot().await.cwd;\n+ let fallback_cwd = conversation.config_snapshot().await.cwd().clone();\n let stored_thread = match conversation\n ...", + "path": "codex-rs/app-server/src/bespoke_event_handling.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -330,6 +330,7 @@ use codex_core_plugins::remote::RemotePluginShareContext as RemoteCatalogPluginS\n use codex_core_plugins::remote::RemotePluginShareSummary as RemoteCatalogPluginShareSummary;\n use codex_core_plugins::remote::RemotePluginSummary as RemoteCatalogPluginSummary;\n use codex_exec_server::EnvironmentManager;\n+use codex_exec_server::LOCAL_ENVIRONMENT_ID;\n use codex_exec_server::LOCAL_FS;\n use codex_features::FEATURES;\n use codex_features::Feature;\n@@ -400,6 +401,7 @@ use codex_protocol::protocol::SessionConfiguredEvent;\n #[cfg(test)]\n use codex_protocol::protocol::SessionMetaLine;\n use codex_protocol::protocol::TurnEnvironmentSelection;\n+use codex_protocol::protocol::TurnEnvironmentSelections;\n use codex_protocol::protocol::USER_MESSAGE_BEGIN;\n use codex_protocol::protocol::W3cTraceContext;\n use codex_protocol::user_input::MAX_USER_INPUT_TEXT_CHARS;", + "path": "codex-rs/app-server/src/request_processors.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -53,7 +53,7 @@ impl AppsRequestProcessor {\n None\n };\n let fallback_cwd = match thread.as_ref() {\n- Some(thread) => Some(thread.config_snapshot().await.cwd.to_path_buf()),\n+ Some(thread) => Some(thread.config_snapshot().await.cwd().to_path_buf()),\n None => None,\n };\n let mut config = self.load_latest_config(fallback_cwd).await?;", + "path": "codex-rs/app-server/src/request_processors/apps_processor.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 2, + "patch_excerpt": "@@ -627,6 +627,8 @@ pub(super) async fn handle_pending_thread_resume_request(\n }\n }\n \n+ let config_snapshot = pending.config_snapshot;\n+ let cwd = config_snapshot.cwd().clone();\n let ThreadConfigSnapshot {\n model,\n model_provider_id,\n@@ -635,11 +637,10 @@ pub(super) async fn handle_pending_thread_resume_request(\n approvals_reviewer,\n permission_profile,\n active_permission_profile,\n- cwd,\n workspace_roots,\n reasoning_effort,\n ..\n- } = pending.config_snapshot;\n+ } = config_snapshot;\n let instruction_sources = pending.instruction_sources;\n let sandbox = thread_response_sandbox_policy(&permission_profile, cwd.as_path());\n let active_permission_profile =", + "path": "codex-rs/app-server/src/request_processors/thread_lifecycle.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 7, + "patch_excerpt": "@@ -47,11 +47,11 @@ fn collect_resume_override_mismatches(\n }\n if let Some(requested_cwd) = request.cwd.as_deref() {\n let requested_cwd_path = std::path::PathBuf::from(requested_cwd);\n- if requested_cwd_path != config_snapshot.cwd.as_path() {\n+ if requested_cwd_path != config_snapshot.cwd().as_path() {\n mismatch_details.push(format!(\n \"cwd requested={} active={}\",\n requested_cwd_path.display(),\n- config_snapshot.cwd.display()\n+ config_snapshot.cwd().display()\n ));\n }\n }\n@@ -1154,8 +1154,9 @@ impl ThreadRequestProcessor {\n \n let sandbox = thread_response_sandbox_policy(\n &config_snapshot.permission_profile,\n- config_snapshot.cwd.as_path(),\n+ config_snapshot.cwd().as_path(),\n );\n+ let cwd = config_snapshot.c...", + "path": "codex-rs/app-server/src/request_processors/thread_processor.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -77,6 +77,7 @@ mod thread_processor_behavior_tests {\n use codex_protocol::protocol::AskForApproval;\n use codex_protocol::protocol::SessionSource;\n use codex_protocol::protocol::SubAgentSource;\n+ use codex_protocol::protocol::TurnEnvironmentSelections;\n use codex_state::ThreadMetadataBuilder;\n use codex_thread_store::StoredThread;\n use codex_utils_absolute_path::test_support::PathBufExt;\n@@ -685,7 +686,7 @@ mod thread_processor_behavior_tests {\n approvals_reviewer: codex_protocol::config_types::ApprovalsReviewer::User,\n permission_profile: codex_protocol::models::PermissionProfile::Disabled,\n active_permission_profile: None,\n- cwd,\n+ environments: TurnEnvironmentSelections::new(cwd, Vec::new()),\n workspace_roots: Vec::new(),\n profile_workspace_roots: Vec::new(),\n eph...", + "path": "codex-rs/app-server/src/request_processors/thread_processor_tests.rs", + "status": "modified" + }, + { + "additions": 29, + "deletions": 17, + "patch_excerpt": "@@ -190,12 +190,12 @@ pub(crate) fn thread_settings_from_config_snapshot(\n config_snapshot: &ThreadConfigSnapshot,\n ) -> ThreadSettings {\n ThreadSettings {\n- cwd: config_snapshot.cwd.clone(),\n+ cwd: config_snapshot.cwd().clone(),\n approval_policy: config_snapshot.approval_policy.into(),\n approvals_reviewer: config_snapshot.approvals_reviewer.into(),\n sandbox_policy: thread_response_sandbox_policy(\n &config_snapshot.permission_profile,\n- config_snapshot.cwd.as_path(),\n+ config_snapshot.cwd().as_path(),\n ),\n active_permission_profile: thread_response_active_permission_profile(\n config_snapshot.active_permission_profile.clone(),\n@@ -213,24 +213,36 @@ pub(crate) fn thread_settings_from_config_snapshot(\n pub(crate) fn thread_settings_from_core_snapshot(\n snapshot: codex_protocol::pr...", + "path": "codex-rs/app-server/src/request_processors/thread_summary.rs", + "status": "modified" + }, + { + "additions": 45, + "deletions": 10, + "patch_excerpt": "@@ -43,7 +43,7 @@ fn map_additional_context(\n \n struct ThreadSettingsBuildParams {\n method: &'static str,\n- cwd: Option,\n+ environments: Option,\n runtime_workspace_roots: Option>,\n approval_policy: Option,\n approvals_reviewer: Option,\n@@ -406,12 +406,14 @@ impl TurnRequestProcessor {\n let additional_context = map_additional_context(params.additional_context);\n let turn_has_input = !mapped_items.is_empty();\n let cwd = resolve_request_cwd(params.cwd)?;\n+ let environments =\n+ Self::build_environment_override(thread.as_ref(), cwd, environment_selections).await;\n let thread_settings = self\n .build_thread_settings_overrides(\n thread.as_ref(),\n ...", + "path": "codex-rs/app-server/src/request_processors/turn_processor.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 3, + "patch_excerpt": "@@ -458,7 +458,6 @@ async fn send_input_submits_user_message() {\n let expected = (\n thread_id,\n Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello from tests\".to_string(),\n text_elements: Vec::new(),\n@@ -797,7 +796,6 @@ async fn spawn_agent_creates_thread_and_sends_prompt() {\n let expected = (\n thread_id,\n Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"spawned\".to_string(),\n text_elements: Vec::new(),\n@@ -1017,7 +1015,6 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {\n let expected = (\n child_thread_id,\n Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"child task\"...", + "path": "codex-rs/core/src/agent/control_tests.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -196,7 +196,6 @@ pub(crate) async fn run_codex_thread_one_shot(\n \n // Send the initial input to kick off the one-shot turn.\n io.submit(Op::UserInput {\n- environments: None,\n items: input,\n final_output_json_schema,\n responsesapi_client_metadata: None,", + "path": "codex-rs/core/src/codex_delegate.rs", + "status": "modified" + }, + { + "additions": 14, + "deletions": 5, + "patch_excerpt": "@@ -32,6 +32,7 @@ use codex_protocol::protocol::ThreadMemoryMode;\n use codex_protocol::protocol::ThreadSource;\n use codex_protocol::protocol::TokenUsageInfo;\n use codex_protocol::protocol::TurnEnvironmentSelection;\n+use codex_protocol::protocol::TurnEnvironmentSelections;\n use codex_protocol::protocol::W3cTraceContext;\n use codex_protocol::user_input::UserInput;\n use codex_thread_store::StoredThread;\n@@ -59,7 +60,7 @@ pub struct ThreadConfigSnapshot {\n pub approvals_reviewer: ApprovalsReviewer,\n pub permission_profile: PermissionProfile,\n pub active_permission_profile: Option,\n- pub cwd: AbsolutePathBuf,\n+ pub environments: TurnEnvironmentSelections,\n pub workspace_roots: Vec,\n pub profile_workspace_roots: Vec,\n pub ephemeral: bool,\n@@ -114,18 +115,26 @@ impl TryStartTurnIfIdleError {\n }\n \n impl Thre...", + "path": "codex-rs/core/src/codex_thread.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 3, + "patch_excerpt": "@@ -721,19 +721,27 @@ async fn run_review_on_session(\n .await\n .unwrap_or_default();\n let guardian_permission_profile = PermissionProfile::read_only();\n+ let parent_turn_environments = params.parent_turn.environments.to_selections();\n+ let parent_turn_legacy_fallback_cwd = params\n+ .parent_turn\n+ .environments\n+ .primary()\n+ .map(|environment| environment.cwd.clone())\n+ .unwrap_or_else(|| params.parent_turn.config.cwd.clone());\n \n let submit_result = run_before_review_deadline(\n deadline,\n params.external_cancel.as_ref(),\n Box::pin(review_session.codex.submit(Op::UserInput {\n items: prompt_items.items,\n- environments: None,\n final_output_json_schema: Some(params.schema.clone()),\n responsesapi_client_metadata: None,\n additional_context: Defaul...", + "path": "codex-rs/core/src/guardian/review_session.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 5, + "patch_excerpt": "@@ -122,7 +122,7 @@ async fn thread_settings_update(\n thread_settings: ThreadSettingsOverrides,\n ) -> SessionSettingsUpdate {\n let ThreadSettingsOverrides {\n- cwd,\n+ environments,\n workspace_roots,\n profile_workspace_roots,\n approval_policy,\n@@ -151,7 +151,7 @@ async fn thread_settings_update(\n }\n };\n SessionSettingsUpdate {\n- cwd,\n+ environments,\n workspace_roots,\n profile_workspace_roots,\n approval_policy,\n@@ -173,6 +173,7 @@ async fn thread_settings_applied_event(sess: &Session) -> EventMsg {\n let state = sess.state.lock().await;\n state.session_configuration.thread_config_snapshot()\n };\n+ let cwd = snapshot.cwd().clone();\n EventMsg::ThreadSettingsApplied(ThreadSettingsAppliedEvent {\n thread_settings: ThreadSettingsSnapshot {\n model: snapsho...", + "path": "codex-rs/core/src/session/handlers.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 6, + "patch_excerpt": "@@ -116,6 +116,7 @@ use codex_protocol::protocol::TurnAbortReason;\n use codex_protocol::protocol::TurnContextItem;\n use codex_protocol::protocol::TurnContextNetworkItem;\n use codex_protocol::protocol::TurnEnvironmentSelection;\n+use codex_protocol::protocol::TurnEnvironmentSelections;\n use codex_protocol::protocol::W3cTraceContext;\n use codex_protocol::request_permissions::PermissionGrantScope;\n use codex_protocol::request_permissions::RequestPermissionProfile;\n@@ -602,11 +603,13 @@ impl Codex {\n approvals_reviewer: config.approvals_reviewer,\n permission_profile_state: session_permission_profile_state_from_config(&config)?,\n windows_sandbox_level: WindowsSandboxLevel::from_config(&config),\n- cwd: config.cwd.clone(),\n+ environments: TurnEnvironmentSelections::new(\n+ config.cwd.clone(),\n+ environment_sel...", + "path": "codex-rs/core/src/session/mod.rs", + "status": "modified" + }, + { + "additions": 37, + "deletions": 33, + "patch_excerpt": "@@ -12,6 +12,7 @@ use codex_protocol::permissions::FileSystemSpecialPath;\n use codex_protocol::protocol::MultiAgentVersion;\n use codex_protocol::protocol::ThreadSource;\n use codex_protocol::protocol::TurnEnvironmentSelection;\n+use codex_protocol::protocol::TurnEnvironmentSelections;\n use std::sync::OnceLock;\n use tokio::sync::Semaphore;\n \n@@ -75,20 +76,16 @@ pub(crate) struct SessionConfiguration {\n pub(super) permission_profile_state: PermissionProfileState,\n pub(super) windows_sandbox_level: WindowsSandboxLevel,\n \n- /// Absolute working directory that should be treated as the *root* of the\n- /// session. All relative paths supplied by the model as well as the\n- /// execution sandbox are resolved against this directory **instead** of\n- /// the process-wide current working directory.\n- pub(super) cwd: AbsolutePathBuf,\n+ /// Sticky thread-level environment select...", + "path": "codex-rs/core/src/session/session.rs", + "status": "modified" + }, + { + "additions": 135, + "deletions": 127, + "patch_excerpt": "@@ -22,6 +22,7 @@ use codex_config::RequirementSource;\n use codex_config::Sourced;\n use codex_config::loader::project_trust_key;\n use codex_config::types::ToolSuggestDisabledTool;\n+use core_test_support::test_codex::local_selections;\n \n use codex_features::Feature;\n use codex_login::CodexAuth;\n@@ -53,6 +54,7 @@ use codex_protocol::permissions::FileSystemSpecialPath;\n use codex_protocol::protocol::NonSteerableTurnKind;\n use codex_protocol::protocol::SandboxPolicy;\n use codex_protocol::protocol::TurnEnvironmentSelection;\n+use codex_protocol::protocol::TurnEnvironmentSelections;\n use codex_protocol::request_permissions::PermissionGrantScope;\n use codex_protocol::request_permissions::RequestPermissionProfile;\n use tracing::Span;\n@@ -142,6 +144,7 @@ use core_test_support::responses::ev_response_created;\n use core_test_support::responses::mount_sse_once;\n use core_test_support::responses::sse;...", + "path": "codex-rs/core/src/session/tests.rs", + "status": "modified" + }, + { + "additions": 13, + "deletions": 38, + "patch_excerpt": "@@ -441,7 +441,7 @@ impl Session {\n session_configuration: &SessionConfiguration,\n ) -> Config {\n let mut config =\n- Self::build_per_turn_config(session_configuration, session_configuration.cwd.clone());\n+ Self::build_per_turn_config(session_configuration, session_configuration.cwd().clone());\n config.model = Some(session_configuration.collaboration_mode.model().to_string());\n config.permissions.approval_policy = session_configuration.approval_policy.clone();\n config.workspace_roots = session_configuration.workspace_roots.clone();\n@@ -587,19 +587,9 @@ impl Session {\n let mut state = self.state.lock().await;\n match state.session_configuration.clone().apply(&updates) {\n Ok(next) => {\n- let mut effective_environments = updates\n- .environments\n- ...", + "path": "codex-rs/core/src/session/turn_context.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 94, + "patch_excerpt": "@@ -21,7 +21,6 @@ use codex_protocol::protocol::SessionSource;\n use codex_protocol::protocol::ThreadSource;\n use codex_protocol::protocol::TurnStartedEvent;\n use codex_protocol::protocol::UserMessageEvent;\n-use codex_protocol::user_input::UserInput;\n use core_test_support::PathBufExt;\n use core_test_support::PathExt;\n use core_test_support::responses::mount_models_once;\n@@ -343,99 +342,6 @@ async fn start_thread_rejects_explicit_local_environment_when_default_provider_i\n assert!(manager.list_thread_ids().await.is_empty());\n }\n \n-#[tokio::test]\n-async fn start_thread_uses_all_default_environments_from_codex_home() {\n- let temp_dir = tempdir().expect(\"tempdir\");\n- let mut config = test_config().await;\n- config.codex_home = temp_dir.path().join(\"codex-home\").abs();\n- config.cwd = config.codex_home.abs();\n- std::fs::create_dir_all(&config.codex_home).expect(\"create codex h...", + "path": "codex-rs/core/src/thread_manager_tests.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -2671,7 +2671,6 @@ async fn send_input_accepts_structured_items() {\n .expect(\"send_input should succeed\");\n \n let expected = Op::UserInput {\n- environments: None,\n items: vec![\n UserInput::Mention {\n name: \"drive\".to_string(),", + "path": "codex-rs/core/src/tools/handlers/multi_agents_tests.rs", + "status": "modified" + }, + { + "additions": 16, + "deletions": 2, + "patch_excerpt": "@@ -40,6 +40,7 @@ use codex_protocol::protocol::SandboxPolicy;\n use codex_protocol::protocol::SessionConfiguredEvent;\n use codex_protocol::protocol::SessionSource;\n use codex_protocol::protocol::TurnEnvironmentSelection;\n+use codex_protocol::protocol::TurnEnvironmentSelections;\n use codex_protocol::user_input::UserInput;\n use codex_utils_absolute_path::AbsolutePathBuf;\n use futures::future::BoxFuture;\n@@ -70,6 +71,17 @@ const REMOTE_EXEC_SERVER_URL_ENV_VAR: &str = \"CODEX_TEST_REMOTE_EXEC_SERVER_URL\"\n static REMOTE_TEST_INSTANCE_COUNTER: AtomicU64 = AtomicU64::new(0);\n const SUBMIT_TURN_COMPLETE_TIMEOUT: Duration = Duration::from_secs(30);\n \n+pub fn local(cwd: AbsolutePathBuf) -> TurnEnvironmentSelection {\n+ TurnEnvironmentSelection {\n+ environment_id: codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(),\n+ cwd,\n+ }\n+}\n+\n+pub fn local_selections(cwd: AbsolutePathBuf) ->...", + "path": "codex-rs/core/tests/common/test_codex.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 5, + "patch_excerpt": "@@ -46,7 +46,6 @@ async fn interrupt_long_running_tool_emits_turn_aborted() {\n // Kick off a turn that triggers the function call.\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"start sleep\".into(),\n text_elements: Vec::new(),\n@@ -104,7 +103,6 @@ async fn interrupt_tool_records_history_entries() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"start history recording\".into(),\n text_elements: Vec::new(),\n@@ -126,7 +124,6 @@ async fn interrupt_tool_records_history_entries() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"follow up\".into(),\n text_el...", + "path": "codex-rs/core/tests/suite/abort_tasks.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 9, + "patch_excerpt": "@@ -37,7 +37,6 @@ async fn additional_context_is_model_visible_but_not_a_user_message_item() -> Re\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"inspect the active tab\".to_string(),\n text_elements: Vec::new(),\n@@ -136,7 +135,6 @@ async fn external_context_like_user_text_remains_a_user_message_item() -> Result\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![user_input.clone()],\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n@@ -182,7 +180,6 @@ async fn additional_context_trust_controls_message_role() -> Result<()> {\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: ...", + "path": "codex-rs/core/tests/suite/additional_context.rs", + "status": "modified" + }, + { + "additions": 14, + "deletions": 13, + "patch_excerpt": "@@ -7,6 +7,7 @@ use core_test_support::responses::ev_apply_patch_custom_tool_call;\n use core_test_support::responses::ev_apply_patch_shell_command_call_via_heredoc;\n use core_test_support::responses::ev_shell_command_call;\n use core_test_support::test_codex::ApplyPatchModelOutput;\n+use core_test_support::test_codex::local_selections;\n use pretty_assertions::assert_eq;\n use std::fs;\n use std::path::PathBuf;\n@@ -52,6 +53,7 @@ use core_test_support::skip_if_no_network;\n use core_test_support::skip_if_remote;\n use core_test_support::test_codex::TestCodexBuilder;\n use core_test_support::test_codex::TestCodexHarness;\n+use core_test_support::test_codex::local;\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@@ -100,12 +102,11 @@ async fn submit_without_wait_with_turn_permissions(\n ...", + "path": "codex-rs/core/tests/suite/apply_patch_cli.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 8, + "patch_excerpt": "@@ -39,6 +39,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -658,12 +659,11 @@ async fn submit_turn(\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Some(lo...", + "path": "codex-rs/core/tests/suite/approvals.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -34,6 +34,7 @@ use core_test_support::responses::sse;\n use core_test_support::skip_if_no_network;\n use core_test_support::skip_if_sandbox;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -160,12 +161,11 @@ async fn remote_model_override_uses_catalog_model_for_strict_auto_review() -> Re\n text: \"run the Guardian model override check\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- ...", + "path": "codex-rs/core/tests/suite/auto_review.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 37, + "patch_excerpt": "@@ -66,6 +66,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::sse_failed;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use dunce::canonicalize as normalize_path;\n@@ -386,7 +387,6 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {\n // 2) Submit new input; the request body must include the prior items, then initial context, then new user input.\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -755,7 +755,6 @@ async fn includes_session_id_thread_id_and_model_headers_in_request() {\n \n codex\n ...", + "path": "codex-rs/core/tests/suite/client.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 2, + "patch_excerpt": "@@ -1231,7 +1231,6 @@ async fn responses_websocket_usage_limit_error_emits_rate_limit_event() {\n let submission_id = test\n .codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -1322,7 +1321,6 @@ async fn responses_websocket_invalid_request_error_with_status_is_forwarded() {\n let submission_id = test\n .codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/client_websockets.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 2, + "patch_excerpt": "@@ -3199,12 +3199,14 @@ text(\n text: \"use exec to inspect and call hidden tools\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd),\n+ environments: Some(codex_protocol::protocol::TurnEnvironmentSelections::new(\n+ cwd,\n+ Vec::new(),\n+ )),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(sandbox_policy),\n permission_profile,", + "path": "codex-rs/core/tests/suite/code_mode.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 20, + "patch_excerpt": "@@ -13,6 +13,7 @@ use core_test_support::responses::mount_sse_once;\n use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use pretty_assertions::assert_eq;\n@@ -72,7 +73,6 @@ async fn no_collaboration_instructions_by_default() -> Result<()> {\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -127,7 +127,6 @@ async fn user_input_includes_collaboration_instructions_after_override() -> Resu\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::...", + "path": "codex-rs/core/tests/suite/collaboration_instructions.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 34, + "patch_excerpt": "@@ -31,6 +31,7 @@ use core_test_support::hooks::trust_discovered_hooks;\n use core_test_support::responses::ev_reasoning_item;\n use core_test_support::responses::mount_models_once;\n use core_test_support::skip_if_no_network;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::test_codex::turn_permission_fields;\n use core_test_support::test_path_buf;\n@@ -96,12 +97,11 @@ fn disabled_permission_user_turn(text: impl Into, cwd: PathBuf, model: S\n text: text.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd.abs()),\n+ ...", + "path": "codex-rs/core/tests/suite/compact.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 59, + "patch_excerpt": "@@ -1,5 +1,6 @@\n #![allow(clippy::expect_used)]\n \n+use core_test_support::test_codex::local_selections;\n use std::fs;\n \n use anyhow::Result;\n@@ -324,7 +325,6 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello remote compact\".into(),\n text_elements: Vec::new(),\n@@ -342,7 +342,6 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"after compact\".into(),\n text_elements: Vec::new(),\n@@ -585,7 +584,6 @@ async fn assert_remote_manual_compact_request_parity(\n \n codex\n .submit(Op::UserInput {\n- environ...", + "path": "codex-rs/core/tests/suite/compact_remote.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -605,7 +605,6 @@ async fn capture_from_requests(\n async fn submit_user_input(codex: &codex_core::CodexThread, items: Vec) -> Result<()> {\n codex\n .submit(Op::UserInput {\n- environments: None,\n items,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,", + "path": "codex-rs/core/tests/suite/compact_remote_parity.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -34,6 +34,7 @@ use core_test_support::responses::ev_response_created;\n use core_test_support::responses::mount_sse_once_match;\n use core_test_support::responses::mount_sse_sequence;\n use core_test_support::responses::sse;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use pretty_assertions::assert_eq;\n@@ -551,7 +552,7 @@ async fn snapshot_rollback_followup_turn_trims_context_updates() -> Result<()> {\n core_test_support::submit_thread_settings(\n &conversation,\n codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(override_cwd.clone()),\n+ environments: Some(local_selections(override_cwd.clone())),\n collaboration_mode: Some(CollaborationMode {\n mode: ModeKind::Default,\n settings: Settings {\n@@ -774...", + "path": "codex-rs/core/tests/suite/compact_resume_fork.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 4, + "patch_excerpt": "@@ -17,6 +17,7 @@ use core_test_support::responses::ev_response_created;\n use core_test_support::responses::mount_sse_once;\n use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n+use core_test_support::test_codex::local_selections;\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@@ -51,12 +52,11 @@ async fn submit_user_turn(\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ en...", + "path": "codex-rs/core/tests/suite/exec_policy.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 2, + "patch_excerpt": "@@ -50,7 +50,6 @@ async fn fork_thread_twice_drops_to_first_message() {\n for text in [\"first\", \"second\", \"third\"] {\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: text.to_string(),\n text_elements: Vec::new(),\n@@ -173,7 +172,6 @@ async fn fork_thread_from_history_does_not_require_source_rollout_path() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"fork me from stored history\".to_string(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/fork_thread.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -19,6 +19,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::skip_if_sandbox;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use pretty_assertions::assert_eq;\n@@ -111,7 +112,6 @@ printf '%s\\n' \"${@: -1}\" >> \"${payload_path}\"\"#,\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"run a command that requires Guardian review\".into(),\n text_elements: Vec::new(),\n@@ -120,7 +120,7 @@ printf '%s\\n' \"${@: -1}\" >> \"${payload_path}\"\"#,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_pro...", + "path": "codex-rs/core/tests/suite/guardian_review.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 2, + "patch_excerpt": "@@ -1843,7 +1843,6 @@ async fn blocked_queued_prompt_does_not_strand_earlier_accepted_prompt() -> Resu\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"initial prompt\".to_string(),\n text_elements: Vec::new(),\n@@ -1863,7 +1862,6 @@ async fn blocked_queued_prompt_does_not_strand_earlier_accepted_prompt() -> Resu\n for text in [\"accepted queued prompt\", \"blocked queued prompt\"] {\n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: text.to_string(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/hooks.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 4, + "patch_excerpt": "@@ -18,6 +18,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -125,12 +126,11 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu\n text_elements: Vec::new(),\n },\n ],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd.abs()),\n+ ...", + "path": "codex-rs/core/tests/suite/image_rollout.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 11, + "patch_excerpt": "@@ -35,6 +35,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -56,12 +57,11 @@ fn disabled_plan_turn(\n text: text.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd),\n+ environments: Some(local_selections(cwd)),\n approval_policy: Some(AskFor...", + "path": "codex-rs/core/tests/suite/items.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -8,6 +8,7 @@ use codex_protocol::user_input::UserInput;\n use core_test_support::responses;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -81,12 +82,11 @@ async fn codex_returns_json_result(model: String) -> anyhow::Result<()> {\n text: \"hello world\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: Some(serde_json::from_str(SCHEMA)?),\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd...", + "path": "codex-rs/core/tests/suite/json_result.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -32,6 +32,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::turn_permission_fields;\n use core_test_support::wait_for_event;\n use core_test_support::wait_for_event_match;\n@@ -73,12 +74,11 @@ async fn submit_user_turn(\n text: text.to_string(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Som...", + "path": "codex-rs/core/tests/suite/mcp_turn_metadata.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 2, + "patch_excerpt": "@@ -127,7 +127,6 @@ async fn response_body_for_remote_model(\n text: \"list tools\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n@@ -277,7 +276,6 @@ async fn remote_multi_agent_selector_uses_model_selected_before_first_turn() ->\n text: ROOT_PROMPT.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),", + "path": "codex-rs/core/tests/suite/model_runtime_selectors.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -32,6 +32,7 @@ use core_test_support::responses::sse_completed;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -45,12 +46,11 @@ fn read_only_user_turn(test: &TestCodex, items: Vec, model: String) -\n turn_permission_fields(PermissionProfile::read_only(), test.cwd_path());\n Op::UserInput {\n items,\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(t...", + "path": "codex-rs/core/tests/suite/model_switching.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 14, + "patch_excerpt": "@@ -1,5 +1,6 @@\n #![allow(clippy::expect_used)]\n \n+use core_test_support::test_codex::local_selections;\n use std::fs;\n use std::sync::Arc;\n \n@@ -124,12 +125,11 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> {\n text: \"first turn\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(first_turn_cwd),\n+ environments: Some(local_selections(first_turn_cwd)),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(first_sandbox_policy),\n permission_profile: first_permission_profi...", + "path": "codex-rs/core/tests/suite/model_visible_layout.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -1,3 +1,4 @@\n+use core_test_support::test_codex::local_selections;\n use std::path::Path;\n use std::sync::Arc;\n \n@@ -97,12 +98,11 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> {\n text: \"hi\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Some(local_selections(test.config.cwd.clone())),\n approval_policy: Some(codex_protocol::protocol::AskForApproval::Never),\n sandbox_policy: Some(sandbox_policy),\n permission_profile,", + "path": "codex-rs/core/tests/suite/models_cache_ttl.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -1,5 +1,6 @@\n #![cfg(not(target_os = \"windows\"))]\n \n+use core_test_support::test_codex::local_selections;\n use std::sync::Arc;\n use std::time::Duration;\n \n@@ -110,12 +111,11 @@ async fn refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch\n text: \"please run a tool\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd_path),\n+ environments: Some(local_selections(cwd_path)),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(sandbox_policy),\n permission_profile,", + "path": "codex-rs/core/tests/suite/models_etag_responses.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 22, + "patch_excerpt": "@@ -120,7 +120,6 @@ async fn responses_api_emits_api_request_event() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -167,7 +166,6 @@ async fn process_sse_emits_tracing_for_output_item() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -214,7 +212,6 @@ async fn process_sse_emits_failed_event_on_parse_error() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -262,7 +259,6 @@ async fn process_sse_records_failed_even...", + "path": "codex-rs/core/tests/suite/otel.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -9,6 +9,7 @@ use codex_protocol::protocol::Op;\n use core_test_support::TempDirExt;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use tempfile::TempDir;\n@@ -68,7 +69,7 @@ async fn thread_settings_update_without_user_turn_does_not_record_environment_up\n core_test_support::submit_thread_settings(\n &test.codex,\n codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(new_cwd.abs()),\n+ environments: Some(local_selections(new_cwd.abs())),\n ..Default::default()\n },\n )", + "path": "codex-rs/core/tests/suite/override_updates.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 5, + "patch_excerpt": "@@ -1,3 +1,4 @@\n+use core_test_support::test_codex::local_selections;\n use std::sync::Arc;\n \n use codex_core::CodexThread;\n@@ -96,7 +97,6 @@ async fn build_codex(server: &StreamingSseServer) -> Arc {\n async fn submit_user_input(codex: &CodexThread, text: &str) {\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: text.to_string(),\n text_elements: Vec::new(),\n@@ -119,12 +119,11 @@ async fn submit_danger_full_access_user_turn(test: &TestCodex, text: &str) {\n text: text.to_string(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_pr...", + "path": "codex-rs/core/tests/suite/pending_input.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 15, + "patch_excerpt": "@@ -51,7 +51,6 @@ async fn permissions_message_sent_once_on_start() -> Result<()> {\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -92,7 +91,6 @@ async fn permissions_message_added_on_override_change() -> Result<()> {\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello 1\".into(),\n text_elements: Vec::new(),\n@@ -116,7 +114,6 @@ async fn permissions_message_added_on_override_change() -> Result<()> {\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello 2\".into(),\n text_elements: Vec::new(),\n...", + "path": "codex-rs/core/tests/suite/permissions_messages.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -26,6 +26,7 @@ use core_test_support::responses::sse_completed;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -65,12 +66,11 @@ fn read_only_text_turn_with_personality(\n text: text.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Some(local_selections(...", + "path": "codex-rs/core/tests/suite/personality.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 3, + "patch_excerpt": "@@ -180,7 +180,6 @@ async fn capability_sections_render_in_developer_message_in_order() -> Result<()\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![codex_protocol::user_input::UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),\n@@ -261,7 +260,6 @@ async fn explicit_plugin_mentions_inject_plugin_guidance() -> Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![codex_protocol::user_input::UserInput::Mention {\n name: \"sample\".into(),\n path: format!(\"plugin://{SAMPLE_PLUGIN_CONFIG_NAME}\"),\n@@ -345,7 +343,6 @@ async fn explicit_plugin_mentions_track_plugin_used_analytics() -> Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![co...", + "path": "codex-rs/core/tests/suite/plugins.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 20, + "patch_excerpt": "@@ -25,6 +25,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -153,7 +154,6 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello 1\".into(),\n text_elements: Vec::new(),\n@@ -168,7 +168,6 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n ...", + "path": "codex-rs/core/tests/suite/prompt_caching.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -41,7 +41,6 @@ async fn quota_exceeded_emits_single_error_event() -> Result<()> {\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"quota?\".into(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/quota_exceeded.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 3, + "patch_excerpt": "@@ -2158,7 +2158,6 @@ async fn conversation_user_text_turn_is_sent_to_realtime_when_active() -> Result\n let prefixed_user_text = format!(\"[USER] {user_text}\");\n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: user_text.to_string(),\n text_elements: Vec::new(),\n@@ -2294,7 +2293,6 @@ async fn conversation_user_text_turn_is_capped_when_mirrored_to_realtime() -> Re\n );\n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: user_text.clone(),\n text_elements: Vec::new(),\n@@ -3491,7 +3489,6 @@ async fn inbound_handoff_request_steers_active_turn() -> Result<()> {\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec...", + "path": "codex-rs/core/tests/suite/realtime_conversation.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 22, + "patch_excerpt": "@@ -41,6 +41,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local;\n use core_test_support::test_codex::test_codex;\n use core_test_support::test_codex::test_env;\n use core_test_support::wait_for_event;\n@@ -70,18 +71,21 @@ async fn submit_turn_with_approval_and_environments(\n prompt: &str,\n environments: Vec,\n ) -> Result<()> {\n+ let turn_environment_selections = codex_protocol::protocol::TurnEnvironmentSelections::new(\n+ test.config.cwd.clone(),\n+ environments,\n+ );\n test.codex\n .submit(Op::UserInput {\n items: vec![UserInput::Text {\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- ...", + "path": "codex-rs/core/tests/suite/remote_env.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 9, + "patch_excerpt": "@@ -37,6 +37,7 @@ use core_test_support::responses::sse;\n use core_test_support::skip_if_no_network;\n use core_test_support::skip_if_sandbox;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -158,7 +159,6 @@ async fn remote_models_config_context_window_override_clamps_to_max_context_wind\n text: \"check context window\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n@@ -226,7 +226,6 @@ async fn remote_models_config_override_above_max_uses_max_context_window() -> Re\n ...", + "path": "codex-rs/core/tests/suite/remote_models.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 2, + "patch_excerpt": "@@ -40,7 +40,6 @@ async fn request_body_is_zstd_compressed_for_codex_backend_when_enabled() -> any\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"compress me\".into(),\n text_elements: Vec::new(),\n@@ -91,7 +90,6 @@ async fn request_body_is_not_compressed_for_api_key_auth_even_when_enabled() ->\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"do not compress\".into(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/request_compression.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -31,6 +31,7 @@ use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::skip_if_sandbox;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -195,12 +196,11 @@ async fn submit_turn(\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Some(l...", + "path": "codex-rs/core/tests/suite/request_permissions.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -28,6 +28,7 @@ use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::skip_if_sandbox;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -147,12 +148,11 @@ async fn submit_turn(\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Some(l...", + "path": "codex-rs/core/tests/suite/request_permissions_tool.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 6, + "patch_excerpt": "@@ -1,5 +1,6 @@\n #![allow(clippy::unwrap_used)]\n \n+use core_test_support::test_codex::local_selections;\n use std::collections::HashMap;\n \n use codex_features::Feature;\n@@ -142,12 +143,11 @@ async fn request_user_input_round_trip_for_mode(mode: ModeKind) -> anyhow::Resul\n text: \"please confirm\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd.abs()),\n+ environments: Some(local_selections(cwd.abs())),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(sandbox_policy),\n permission_profile...", + "path": "codex-rs/core/tests/suite/request_user_input.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -20,6 +20,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::test_codex::turn_permission_fields;\n use pretty_assertions::assert_eq;\n@@ -150,12 +151,11 @@ async fn submit_turn_with_timeout(test: &TestCodex, prompt: &str) -> Result<()>\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd),\n+ ...", + "path": "codex-rs/core/tests/suite/responses_api_proxy_headers.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 7, + "patch_excerpt": "@@ -86,7 +86,6 @@ async fn resume_includes_initial_messages_from_rollout_events() -> Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"Record some messages\".into(),\n text_elements: text_elements.clone(),\n@@ -173,7 +172,6 @@ async fn resume_includes_initial_messages_from_reasoning_events() -> Result<()>\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"Record reasoning messages\".into(),\n text_elements: Vec::new(),\n@@ -264,7 +262,6 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"Re...", + "path": "codex-rs/core/tests/suite/resume.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -23,6 +23,7 @@ use core_test_support::responses::ResponseMock;\n use core_test_support::responses::mount_sse_sequence;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use pretty_assertions::assert_eq;\n@@ -705,7 +706,6 @@ async fn review_history_surfaces_in_parent_session() {\n let followup = \"back to parent\".to_string();\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: followup.clone(),\n text_elements: Vec::new(),\n@@ -821,7 +821,7 @@ async fn review_uses_overridden_cwd_for_base_branch_merge_base() {\n core_test_support::submit_thread_settings(\n &codex,\n codex_protoco...", + "path": "codex-rs/core/tests/suite/review.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -2,6 +2,7 @@\n \n use anyhow::Context as _;\n use anyhow::ensure;\n+use core_test_support::test_codex::local_selections;\n use std::collections::HashMap;\n use std::ffi::OsStr;\n use std::ffi::OsString;\n@@ -128,12 +129,11 @@ fn user_turn_with_permission_profile(\n text: text.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd),\n+ environments: Some(local_selections(cwd)),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(sandbox_policy),\n permission_profile,", + "path": "codex-rs/core/tests/suite/rmcp_client.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -21,6 +21,7 @@ use core_test_support::responses::sse_response;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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@@ -42,12 +43,11 @@ fn disabled_text_turn(test: &TestCodex, text: &str) -> Op {\n text: text.to_string(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments:...", + "path": "codex-rs/core/tests/suite/safety_check_downgrade.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 4, + "patch_excerpt": "@@ -524,7 +524,6 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -\n let test = builder.build(&server).await?;\n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"Find the calendar create tool\".to_string(),\n text_elements: Vec::new(),\n@@ -934,7 +933,6 @@ async fn tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call() -\n \n test.codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"Use the automation tool\".to_string(),\n text_elements: Vec::new(),\n@@ -1245,7 +1243,6 @@ async fn tool_search_surfaced_mcp_tool_errors_are_returned_to_model() -> Result<\n \n test.codex\n .submit(Op::UserInput {\n- environments: ...", + "path": "codex-rs/core/tests/suite/search_tool.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 8, + "patch_excerpt": "@@ -14,6 +14,7 @@ use core_test_support::responses::ev_response_created;\n use core_test_support::responses::mount_sse_sequence;\n use core_test_support::responses::sse;\n use core_test_support::test_codex::TestCodexHarness;\n+use core_test_support::test_codex::local_selections;\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@@ -164,12 +165,11 @@ async fn run_snapshot_command_with_options(\n text: \"run unified exec with shell snapshot\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- ...", + "path": "codex-rs/core/tests/suite/shell_snapshot.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -13,6 +13,7 @@ use core_test_support::responses::mount_function_call_agent_response;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::turn_permission_fields;\n use core_test_support::wait_for_event;\n use core_test_support::wait_for_event_match;\n@@ -51,12 +52,11 @@ async fn submit_turn_with_policies(\n text: prompt.to_string(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.cl...", + "path": "codex-rs/core/tests/suite/skill_approval.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -16,6 +16,7 @@ use core_test_support::responses::mount_sse_once;\n use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::test_codex::turn_permission_fields;\n use std::sync::Arc;\n@@ -85,12 +86,11 @@ async fn user_turn_includes_skill_instructions() -> Result<()> {\n path: skill_path.clone(),\n },\n ],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environment...", + "path": "codex-rs/core/tests/suite/skills.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 3, + "patch_excerpt": "@@ -25,6 +25,7 @@ use core_test_support::responses::mount_sse_sequence;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::stdio_server_bin;\n+use core_test_support::test_codex::local_selections;\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@@ -138,7 +139,6 @@ async fn resume_restores_dynamic_tools_from_rollout_with_sqlite_enabled() -> Res\n started\n .thread\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"persist this thread\".to_string(),\n text_elements: Vec::new(),\n@@ -472,12 +472,11 @@ async fn mcp_call_marks_thread_memory_mode_polluted_when_configured() -> Result<\n text: \"call the rmcp ech...", + "path": "codex-rs/core/tests/suite/sqlite_state.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 2, + "patch_excerpt": "@@ -94,7 +94,6 @@ async fn continue_after_stream_error() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"first message\".into(),\n text_elements: Vec::new(),\n@@ -117,7 +116,6 @@ async fn continue_after_stream_error() {\n // error above, this submission would be rejected/queued indefinitely.\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"follow up\".into(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/stream_error_allows_next_turn.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -76,7 +76,6 @@ async fn retries_on_early_close() {\n \n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello\".into(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/stream_no_completed.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -30,6 +30,7 @@ use core_test_support::responses::sse_response;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\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_match;\n@@ -768,12 +769,11 @@ async fn subagent_stop_replaces_stop_and_skips_internal_subagents() -> Result<()\n text: INTERNAL_SUBAGENT_PROMPT.to_string(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverri...", + "path": "codex-rs/core/tests/suite/subagent_notifications.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 10, + "patch_excerpt": "@@ -1,5 +1,6 @@\n #![cfg(not(target_os = \"windows\"))]\n \n+use core_test_support::test_codex::local_selections;\n use std::fs;\n \n use assert_matches::assert_matches;\n@@ -108,12 +109,11 @@ async fn shell_command_tool_executes_command_and_streams_output() -> anyhow::Res\n text: \"please run the shell command\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd_path),\n+ environments: Some(local_selections(cwd_path)),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(sandbox_policy),\n permission_p...", + "path": "codex-rs/core/tests/suite/tool_harness.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 4, + "patch_excerpt": "@@ -1,6 +1,7 @@\n #![cfg(not(target_os = \"windows\"))]\n #![allow(clippy::unwrap_used)]\n \n+use core_test_support::test_codex::local_selections;\n use std::fs;\n use std::time::Duration;\n use std::time::Instant;\n@@ -42,12 +43,11 @@ async fn run_turn(test: &TestCodex, prompt: &str) -> anyhow::Result<()> {\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Some(local_selections(test.config.cwd.clone())),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(sandb...", + "path": "codex-rs/core/tests/suite/tool_parallelism.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 5, + "patch_excerpt": "@@ -16,7 +16,6 @@ use codex_protocol::permissions::FileSystemSandboxEntry;\n use codex_protocol::permissions::FileSystemSandboxPolicy;\n use codex_protocol::permissions::NetworkSandboxPolicy;\n use codex_protocol::protocol::AskForApproval;\n-use codex_protocol::protocol::TurnEnvironmentSelection;\n use core_test_support::assert_regex_match;\n use core_test_support::responses::ev_assistant_message;\n use core_test_support::responses::ev_completed;\n@@ -29,6 +28,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::skip_if_sandbox;\n+use core_test_support::test_codex::local;\n use core_test_support::test_codex::test_codex;\n use regex_lite::Regex;\n use serde_json::Value;\n@@ -117,10 +117,7 @@ async fn turn_environment_selection_keeps_environment_backed_tools() -> Result<(\n \n test.submit_...", + "path": "codex-rs/core/tests/suite/tools.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -22,6 +22,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::stdio_server_bin;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use core_test_support::wait_for_mcp_server;\n@@ -524,12 +525,11 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> {\n text: \"call the rmcp image tool\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(fixtur...", + "path": "codex-rs/core/tests/suite/truncation.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 14, + "patch_excerpt": "@@ -1,3 +1,4 @@\n+use core_test_support::test_codex::local_selections;\n use std::collections::HashMap;\n use std::ffi::OsStr;\n use std::fs;\n@@ -198,12 +199,11 @@ async fn submit_unified_exec_turn(\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Some(local_selections(test.config.cwd.clone())),\n approval_policy: Some(AskForApproval::Never),\n sandbox_policy: Some(sandbox_policy),\n permission_profile,\n@@ -290,12 +290,11 @@ async fn unified_exec_intercepts_a...", + "path": "codex-rs/core/tests/suite/unified_exec.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -31,6 +31,7 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::turn_permission_fields;\n use core_test_support::wait_for_event;\n use core_test_support::wait_for_event_with_timeout;\n@@ -406,12 +407,11 @@ async fn submit_turn_with_session_permissions(\n text: prompt.into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: ThreadSettingsOverrides {\n- cwd: Some(test.config.cwd.clone()),\n+ environments: Som...", + "path": "codex-rs/core/tests/suite/unified_exec_zsh_fork_approvals.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -57,7 +57,6 @@ mv \"${tmp_path}\" \"${payload_path}\"\"#,\n // 1) Normal user input – should hit server once.\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: \"hello world\".into(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/user_notification.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -21,6 +21,7 @@ use core_test_support::responses::mount_sse_once;\n use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n+use core_test_support::test_codex::local_selections;\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@@ -179,12 +180,11 @@ async fn user_shell_command_does_not_replace_active_turn() -> anyhow::Result<()>\n text: \"run model shell command\".to_string(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- ...", + "path": "codex-rs/core/tests/suite/user_shell_cmd.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 10, + "patch_excerpt": "@@ -42,6 +42,8 @@ use core_test_support::responses::sse;\n use core_test_support::responses::start_mock_server;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local;\n+use core_test_support::test_codex::local_selections;\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_with_timeout;\n@@ -74,12 +76,11 @@ fn disabled_user_turn(test: &TestCodex, items: Vec, model: String) ->\n turn_permission_fields(PermissionProfile::Disabled, test.config.cwd.as_path());\n Op::UserInput {\n items,\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protoco...", + "path": "codex-rs/core/tests/suite/view_image.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -14,6 +14,7 @@ use core_test_support::responses::mount_sse_sequence;\n use core_test_support::responses::sse;\n use core_test_support::skip_if_no_network;\n use core_test_support::test_codex::TestCodex;\n+use core_test_support::test_codex::local_selections;\n use core_test_support::test_codex::test_codex;\n use core_test_support::test_codex::turn_permission_fields;\n use pretty_assertions::assert_eq;\n@@ -158,12 +159,11 @@ async fn websocket_fallback_hides_first_websocket_retry_stream_error() -> Result\n text: \"hello\".into(),\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),\n thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {\n- cwd: Some(cwd.abs(...", + "path": "codex-rs/core/tests/suite/websocket_fallback.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -104,7 +104,6 @@ async fn window_id_advances_after_compact_persists_on_resume_and_resets_on_fork(\n async fn submit_user_turn(codex: &Arc, text: &str) -> Result<()> {\n codex\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: text.to_string(),\n text_elements: Vec::new(),", + "path": "codex-rs/core/tests/suite/window_headers.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 2, + "patch_excerpt": "@@ -103,7 +103,6 @@ pub async fn run_codex_tool_session(\n let submission = Submission {\n id: sub_id.clone(),\n op: Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: initial_prompt.clone(),\n // MCP tool prompts are plain text with no UI element ranges.\n@@ -155,7 +154,6 @@ pub async fn run_codex_tool_session_reply(\n .insert(request_id.clone(), thread_id);\n if let Err(e) = thread\n .submit(Op::UserInput {\n- environments: None,\n items: vec![UserInput::Text {\n text: prompt,\n // MCP tool prompts are plain text with no UI element ranges.", + "path": "codex-rs/mcp-server/src/codex_tool_runner.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -259,7 +259,6 @@ impl MemoryStartupContext {\n .thread\n .submit(Op::UserInput {\n items: prompt,\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),", + "path": "codex-rs/memories/write/src/runtime.rs", + "status": "modified" + }, + { + "additions": 30, + "deletions": 5, + "patch_excerpt": "@@ -112,6 +112,34 @@ pub struct TurnEnvironmentSelection {\n pub cwd: AbsolutePathBuf,\n }\n \n+#[derive(Debug, Clone, PartialEq)]\n+pub struct TurnEnvironmentSelections {\n+ pub legacy_fallback_cwd: AbsolutePathBuf,\n+ pub environments: Vec,\n+}\n+\n+impl TurnEnvironmentSelections {\n+ pub fn new(\n+ legacy_fallback_cwd: AbsolutePathBuf,\n+ environments: Vec,\n+ ) -> Self {\n+ let mut settings = Self {\n+ legacy_fallback_cwd,\n+ environments,\n+ };\n+ settings.sync_primary_environment_cwd();\n+ settings\n+ }\n+\n+ fn sync_primary_environment_cwd(&mut self) {\n+ if let Some(turn_environment) = self.environments.first_mut()\n+ && turn_environment.cwd != self.legacy_fallback_cwd\n+ {\n+ turn_environment.cwd = self.legacy_fallback_cwd.clone();\n+ ...", + "path": "codex-rs/protocol/src/protocol.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -296,7 +296,6 @@ async fn run_turn(thread: &CodexThread, thread_id: &str, prompt: String) -> anyh\n text: prompt,\n text_elements: Vec::new(),\n }],\n- environments: None,\n final_output_json_schema: None,\n responsesapi_client_metadata: None,\n additional_context: Default::default(),", + "path": "codex-rs/thread-manager-sample/src/main.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#26687" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nThread cwd and environment selections are a single logical setting in core: updating one without the other can silently desynchronize the next-turn execution context. This change makes that relationship explicit in the internal thread settings flow while preserving the existing app-server public API shape.\n\n## What changed\n\n- Moved the cwd/environment pair through internal `ThreadSettingsOverrides.environment_settings` instead of a top-level internal `cwd` field.\n- Kept `thread/settings/update` public params unchanged, with app-server translating top-level `cwd` into the paired internal settings shape.\n- Moved `Op::UserInput` environment overrides into thread settings so user turns and settings updates use the same core path.\n- Updated core, app-server, MCP, memories, sample, and test callsites to construct the paired settings shape.\n\n## Verification\n\n- `git diff --check`\n- Local test run starting after PR creation.", + "labels": [], + "merged_at": "2026-06-08T20:55:15Z", + "number": 26687, + "state": "merged", + "title": "Pair thread environment settings", + "url": "https://github.com/openai/codex/pull/26687" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-26840.json b/artifacts/github/bundles/openai-codex-pr-26840.json new file mode 100644 index 000000000..f2a0b1550 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-26840.json @@ -0,0 +1,223 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "anp-oai", + "committed_at": "2026-06-06T23:52:09Z", + "message": "feat: add typed path URI support", + "sha": "3c842f79d482b0f96fcf9a039264ef1896345d17", + "url": "https://github.com/openai/codex/commit/3c842f79d482b0f96fcf9a039264ef1896345d17" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-07T00:00:30Z", + "message": "test: harden path URI normalization", + "sha": "758e981a1d26ae66fd2c1ff530873e378705b45b", + "url": "https://github.com/openai/codex/commit/758e981a1d26ae66fd2c1ff530873e378705b45b" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-07T01:20:32Z", + "message": "fix: harden path URI round trips", + "sha": "882a9b4991823dd85cfc97ebf3557bfcf6623f4d", + "url": "https://github.com/openai/codex/commit/882a9b4991823dd85cfc97ebf3557bfcf6623f4d" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-07T01:31:37Z", + "message": "codex: fix CI failure on PR #26840", + "sha": "e03cce53589b3ad050d3d251741014fef5e88940", + "url": "https://github.com/openai/codex/commit/e03cce53589b3ad050d3d251741014fef5e88940" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-07T01:42:57Z", + "message": "codex: address review on PR #26840", + "sha": "ffe52eeeeb6e01460d784b230f9a260e9a89e488", + "url": "https://github.com/openai/codex/commit/ffe52eeeeb6e01460d784b230f9a260e9a89e488" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-07T01:52:23Z", + "message": "codex: address follow-up review on PR #26840", + "sha": "cf89de2c574b750f864b0f9a44fb2e136ce9e093", + "url": "https://github.com/openai/codex/commit/cf89de2c574b750f864b0f9a44fb2e136ce9e093" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T15:46:20Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "43038a2be12ee9e09bbb23e494a069213a12ea49", + "url": "https://github.com/openai/codex/commit/43038a2be12ee9e09bbb23e494a069213a12ea49" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T18:39:45Z", + "message": "codex: expose environment ID max length", + "sha": "80f6b9758cd4938041cdf619b06c72657f13abcb", + "url": "https://github.com/openai/codex/commit/80f6b9758cd4938041cdf619b06c72657f13abcb" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T18:58:54Z", + "message": "codex: keep environment IDs inside path URIs", + "sha": "18883637213c269c4e589668169cc91b52f3b38e", + "url": "https://github.com/openai/codex/commit/18883637213c269c4e589668169cc91b52f3b38e" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T19:53:20Z", + "message": "codex: scope path URIs to file URLs", + "sha": "9fba3263e8f0b6ff53ca995c40de0259cb0452a5", + "url": "https://github.com/openai/codex/commit/9fba3263e8f0b6ff53ca995c40de0259cb0452a5" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T21:42:58Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "2cfc5a19b265c1860e9bd4eb2519cf38b984464b", + "url": "https://github.com/openai/codex/commit/2cfc5a19b265c1860e9bd4eb2519cf38b984464b" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T22:06:51Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "4fc4e5baeb335cd5b6faca38e4a1bae6f623d66d", + "url": "https://github.com/openai/codex/commit/4fc4e5baeb335cd5b6faca38e4a1bae6f623d66d" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T22:10:59Z", + "message": "codex: fix CI failure on PR #26840", + "sha": "6cf3f6526a586c33b27ce11ad50f54aaf366462c", + "url": "https://github.com/openai/codex/commit/6cf3f6526a586c33b27ce11ad50f54aaf366462c" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T22:14:54Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "8a99473f3a265925e8b94e7f652c8584bc67084a", + "url": "https://github.com/openai/codex/commit/8a99473f3a265925e8b94e7f652c8584bc67084a" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T22:17:58Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "bd2279b23436fa40f838d9c469c8c6029cf7cffc", + "url": "https://github.com/openai/codex/commit/bd2279b23436fa40f838d9c469c8c6029cf7cffc" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T22:26:55Z", + "message": "codex: assign path URI crate ownership (#26840)", + "sha": "14a6f970c14aa660e94dbb5085e6a3e8fd3ad211", + "url": "https://github.com/openai/codex/commit/14a6f970c14aa660e94dbb5085e6a3e8fd3ad211" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T22:35:15Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "3eb69a100fb57eacb88073eb944149315aab00c1", + "url": "https://github.com/openai/codex/commit/3eb69a100fb57eacb88073eb944149315aab00c1" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T22:51:28Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "1bf23e23a4360abe0be9b536a9b84ca24a13c92c", + "url": "https://github.com/openai/codex/commit/1bf23e23a4360abe0be9b536a9b84ca24a13c92c" + }, + { + "author": "anp-oai", + "committed_at": "2026-06-08T23:14:19Z", + "message": "codex: address PR review feedback (#26840)", + "sha": "ad09d4f63e134d7bdb6f7dad8d726229f6669b06", + "url": "https://github.com/openai/codex/commit/ad09d4f63e134d7bdb6f7dad8d726229f6669b06" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "URI", + "URL", + "POSIX", + "UNC", + "AKV", + "FILE_SCHEME", + "RFC", + "UTF", + "API", + "COM1", + "FILE", + "LOCALHOST" + ], + "files": [ + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -2,6 +2,7 @@\n /codex-rs/core/ @openai/codex-core-agent-team\n /codex-rs/ext/extension-api/ @openai/codex-core-agent-team\n /codex-rs/prompts/ @openai/codex-core-agent-team\n+/codex-rs/utils/path-uri/ @openai/codex-core-agent-team\n \n # Keep macOS AKV signing changes reviewed by Codex maintainers.\n /.github/actions/setup-akv-pkcs11-codesigning/ @openai/codex-core-agent-team", + "path": ".github/CODEOWNERS", + "status": "modified" + }, + { + "additions": 15, + "deletions": 0, + "patch_excerpt": "@@ -4151,6 +4151,21 @@ dependencies = [\n \"tempfile\",\n ]\n \n+[[package]]\n+name = \"codex-utils-path-uri\"\n+version = \"0.0.0\"\n+dependencies = [\n+ \"codex-utils-absolute-path\",\n+ \"pretty_assertions\",\n+ \"schemars 0.8.22\",\n+ \"serde\",\n+ \"serde_json\",\n+ \"thiserror 2.0.18\",\n+ \"ts-rs\",\n+ \"url\",\n+ \"urlencoding\",\n+]\n+\n [[package]]\n name = \"codex-utils-plugins\"\n version = \"0.0.0\"", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -83,6 +83,7 @@ members = [\n \"tools\",\n \"v8-poc\",\n \"utils/absolute-path\",\n+ \"utils/path-uri\",\n \"utils/cargo-bin\",\n \"git-utils\",\n \"utils/cache\",", + "path": "codex-rs/Cargo.toml", + "status": "modified" + }, + { + "additions": 6, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,6 @@\n+load(\"//:defs.bzl\", \"codex_rust_crate\")\n+\n+codex_rust_crate(\n+ name = \"path-uri\",\n+ crate_name = \"codex_utils_path_uri\",\n+)", + "path": "codex-rs/utils/path-uri/BUILD.bazel", + "status": "added" + }, + { + "additions": 24, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,24 @@\n+[package]\n+name = \"codex-utils-path-uri\"\n+version.workspace = true\n+edition.workspace = true\n+license.workspace = true\n+\n+[lints]\n+workspace = true\n+\n+[dependencies]\n+codex-utils-absolute-path = { workspace = true }\n+schemars = { workspace = true }\n+serde = { workspace = true, features = [\"derive\"] }\n+thiserror = { workspace = true }\n+ts-rs = { workspace = true, features = [\"no-serde-warnings\"] }\n+url = { workspace = true }\n+urlencoding = { workspace = true }\n+\n+[dev-dependencies]\n+pretty_assertions = { workspace = true }\n+serde_json = { workspace = true }\n+\n+[lib]\n+doctest = false", + "path": "codex-rs/utils/path-uri/Cargo.toml", + "status": "added" + }, + { + "additions": 327, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,327 @@\n+//! Typed, immutable `file:` URIs with cross-platform path inspection.\n+//!\n+//! See [`PathUri`] for scheme, normalization, and serialization behavior.\n+\n+use codex_utils_absolute_path::AbsolutePathBuf;\n+use schemars::JsonSchema;\n+use serde::Deserialize;\n+use serde::Deserializer;\n+use serde::Serialize;\n+use serde::Serializer;\n+use std::fmt;\n+use std::str::FromStr;\n+use thiserror::Error;\n+use ts_rs::TS;\n+use url::Url;\n+\n+pub const FILE_SCHEME: &str = \"file\";\n+\n+/// An immutable, cross-platform representation of a `file:` URI.\n+///\n+/// Only the `file:` scheme is currently accepted. Construction validates the\n+/// URL, and the URI cannot be mutated after construction. [`Self::basename`],\n+/// [`Self::parent`], and [`Self::join`] operate on URI path segments without\n+/// interpreting them using the operating system running Codex.\n+///\n+/// `file:` paths retain their URI s...", + "path": "codex-rs/utils/path-uri/src/lib.rs", + "status": "added" + }, + { + "additions": 343, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,343 @@\n+use super::*;\n+use codex_utils_absolute_path::AbsolutePathBufGuard;\n+use pretty_assertions::assert_eq;\n+#[cfg(unix)]\n+use std::os::unix::ffi::OsStringExt;\n+#[cfg(unix)]\n+use std::path::PathBuf;\n+\n+#[test]\n+fn file_uri_round_trips_an_absolute_path() {\n+ let path = AbsolutePathBuf::current_dir()\n+ .expect(\"current directory\")\n+ .join(\"a path/file.rs\");\n+\n+ let uri = PathUri::from_file_path(&path).expect(\"path should convert to a file URI\");\n+\n+ let uri_string = uri.to_string();\n+ assert!(uri_string.starts_with(\"file:\"));\n+ assert!(uri_string.ends_with(\"/a%20path/file.rs\"));\n+ assert_eq!(\n+ PathUri::parse(&uri_string).expect(\"serialized URI should parse\"),\n+ uri\n+ );\n+ assert_eq!(\n+ uri.to_native_path()\n+ .expect(\"local file URI should convert to a native path\"),\n+ path\n+ );\n+}\n+\n+#[test]\n+fn ...", + "path": "codex-rs/utils/path-uri/src/tests.rs", + "status": "added" + } + ], + "linked_issues": [ + "#26840" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nCodex needs stable `file:` URI identifiers that can cross process and operating-system boundaries without eagerly interpreting them as native paths. Existing fields also need to keep accepting absolute path strings during migration.\n\n## What changed\n\n- Add `codex-utils-path-uri` with a validated, immutable `PathUri` wrapper that currently accepts only `file:` URLs.\n- Expose URI-level `basename`, `parent`, and `join` operations that preserve authorities and percent encoding without guessing the source operating system.\n- Keep native conversion explicit through `AbsolutePathBuf` and the current host rules.\n- Serialize as canonical URI text while accepting both URI text and legacy absolute native paths during deserialization.\n- Add adversarial coverage for Windows-looking and POSIX paths, UNC authorities, encoded metadata characters, non-UTF-8 POSIX paths, URI hierarchy operations, and legacy serde round trips.", + "labels": [], + "merged_at": "2026-06-08T23:33:42Z", + "number": 26840, + "state": "merged", + "title": "Add typed file URIs", + "url": "https://github.com/openai/codex/pull/26840" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-26934.json b/artifacts/github/bundles/openai-codex-pr-26934.json new file mode 100644 index 000000000..54c3d0934 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-26934.json @@ -0,0 +1,60 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "xl-openai", + "committed_at": "2026-06-08T02:23:10Z", + "message": "Prune stale curated plugin caches", + "sha": "a65d2d5a2bc1987b53a3afab765f1240da867060", + "url": "https://github.com/openai/codex/commit/a65d2d5a2bc1987b53a3afab765f1240da867060" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "--check", + "REMOTE_GLOBAL_MARKETPLACE_NAME", + "OPENAI_CURATED_MARKETPLACE_NAME", + "TEST_CURATED_PLUGIN_CACHE_VERSION", + "TEST_CURATED_PLUGIN_SHA" + ], + "files": [ + { + "additions": 27, + "deletions": 18, + "patch_excerpt": "@@ -5,6 +5,7 @@ use crate::manifest::load_plugin_manifest;\n use crate::marketplace::MarketplacePluginSource;\n use crate::marketplace::list_marketplaces;\n use crate::marketplace::load_marketplace;\n+use crate::marketplace::load_raw_marketplace_plugin_names;\n use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;\n use crate::remote::RemoteInstalledPlugin;\n use crate::store::PluginStore;\n@@ -307,6 +308,10 @@ pub fn refresh_curated_plugin_cache(\n .join(\".agents/plugins/marketplace.json\"),\n )\n .map_err(|_| \"local curated marketplace is not available\".to_string())?;\n+ let marketplace_plugin_names = load_raw_marketplace_plugin_names(&curated_marketplace_path)\n+ .map_err(|err| {\n+ format!(\"failed to load curated marketplace plugin names for cache refresh: {err}\")\n+ })?;\n let curated_marketplace = load_marketplace(&curated_marketplace_path)\n ...", + "path": "codex-rs/core-plugins/src/loader.rs", + "status": "modified" + }, + { + "additions": 27, + "deletions": 0, + "patch_excerpt": "@@ -3039,6 +3039,33 @@ fn refresh_curated_plugin_cache_reinstalls_missing_configured_plugin_with_curren\n );\n }\n \n+#[test]\n+fn refresh_curated_plugin_cache_removes_cache_for_plugin_removed_from_marketplace() {\n+ let tmp = tempfile::tempdir().unwrap();\n+ let curated_root = curated_plugins_repo_path(tmp.path());\n+ write_openai_curated_marketplace(&curated_root, &[]);\n+ let plugin_id = PluginId::new(\n+ \"google-sheets\".to_string(),\n+ OPENAI_CURATED_MARKETPLACE_NAME.to_string(),\n+ )\n+ .unwrap();\n+ let plugin_cache_root = tmp\n+ .path()\n+ .join(\"plugins/cache/openai-curated/google-sheets\");\n+ write_plugin(\n+ &tmp.path().join(\"plugins/cache/openai-curated\"),\n+ &format!(\"google-sheets/{TEST_CURATED_PLUGIN_CACHE_VERSION}\"),\n+ \"google-sheets\",\n+ );\n+\n+ assert!(\n+ refresh_curated_plugin_cache(tmp.path(), TEST_CU...", + "path": "codex-rs/core-plugins/src/manager_tests.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 0, + "patch_excerpt": "@@ -9,6 +9,7 @@ use codex_protocol::protocol::Product;\n use codex_utils_absolute_path::AbsolutePathBuf;\n use serde::Deserialize;\n use serde_json::Value as JsonValue;\n+use std::collections::HashSet;\n use std::fs;\n use std::io;\n use std::path::Component;\n@@ -326,6 +327,16 @@ pub fn load_marketplace(path: &AbsolutePathBuf) -> Result Result, MarketplaceError> {\n+ Ok(load_raw_marketplace_manifest(path)?\n+ .plugins\n+ .into_iter()\n+ .map(|plugin| plugin.name)\n+ .collect())\n+}\n+\n #[doc(hidden)]\n pub fn list_marketplaces_with_home(\n additional_roots: &[AbsolutePathBuf],", + "path": "codex-rs/core-plugins/src/marketplace.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "Curated plugin startup refresh now removes cached plugins whose names no longer appear in the raw openai-curated marketplace. This prevents users with the old standalone Google Sheets plugin selected locally from continuing to load its stale cache after the curated repo drops it.\n\nExisting config is left untouched, and plugins still present in the marketplace continue to refresh from local curated sources.\n\nValidation:\n- `just fmt`\n- `just test -p codex-core-plugins`\n- `git diff --check`", + "labels": [], + "merged_at": "2026-06-08T21:46:59Z", + "number": 26934, + "state": "merged", + "title": "[codex] Prune stale curated plugin caches", + "url": "https://github.com/openai/codex/pull/26934" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-26486.json b/artifacts/github/impact/openai-codex-pr-26486.json new file mode 100644 index 000000000..193d45cd3 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-26486.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-26486", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Route image edits through referenced file paths", + "url": "https://github.com/openai/codex/pull/26486", + "meta": "Merged 2026-06-08T21:23:56Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/26486", + "meta": "artifacts/github/reviews/openai-codex-pr-26486.review.json" + } + ] + }, + "observed_change": "Codex image generation extension replaces the old `action` argument with explicit image references: omitted references generate a new image, while `referenced_image_paths` or a bounded recent-image fallback drive edits.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "practical_explainer", + "confidence": "confirmed", + "evidence": [ + "The upstream review records removal of the `ImagegenAction` enum and addition of `referenced_image_paths` and `num_last_images_to_include`.", + "The imagegen tool description now tells the model to omit references for generation and prefer `referenced_image_paths` for edits.", + "The app-server integration test covers an attached model-visible image routed to the edit endpoint.", + "`ImagegenArgs` denies unknown fields, so action-based callers need to update." + ], + "candidate_followups": [ + "Audit Decodex image-generation instructions for old `action` references.", + "Prefer local referenced image paths when Decodex passes image edit requests through Codex.", + "Keep fallback recent-image inclusion bounded and only use it when no local path exists." + ], + "social_notes": [ + "Public copy can explain the new generate-versus-edit convention.", + "Mention that old action-based image tool calls need updating." + ], + "caveats": [ + "The exact edit behavior depends on target images having local file paths or recent conversation image fallback.", + "This is an image-generation extension schema change, not a general Codex CLI flag." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-26687.json b/artifacts/github/impact/openai-codex-pr-26687.json new file mode 100644 index 000000000..09b05f5c9 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-26687.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-26687", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Pair thread environment settings", + "url": "https://github.com/openai/codex/pull/26687", + "meta": "Merged 2026-06-08T20:55:15Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/26687", + "meta": "artifacts/github/reviews/openai-codex-pr-26687.review.json" + } + ] + }, + "observed_change": "Codex now treats cwd and environment selection as one paired thread setting while preserving the app-server public `thread/settings/update` cwd shape.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records that `turn/start` environments now apply to this turn and subsequent turns.", + "`TurnEnvironmentSelections` carries a legacy fallback cwd and synchronizes the primary environment cwd.", + "Core session configuration now stores sticky thread-level environment selections rather than a standalone cwd field.", + "The PR body says public app-server params are preserved while top-level cwd is translated internally." + ], + "candidate_followups": [ + "Audit Decodex retained-lane resume and dashboard code for any separate cwd/environment storage.", + "Use paired environment settings when displaying execution context for app-server threads.", + "Keep compatibility messaging clear: public app-server request params did not remove top-level cwd." + ], + "social_notes": [ + "Public copy should frame this as an app-server execution-context semantics change.", + "Avoid saying the public app-server `cwd` field was removed." + ], + "caveats": [ + "Public app-server params are preserved.", + "The risk is primarily semantic drift for app-server consumers and internal integrations." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-26840.json b/artifacts/github/impact/openai-codex-pr-26840.json new file mode 100644 index 000000000..31af5ed09 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-26840.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-26840", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Add typed file URIs", + "url": "https://github.com/openai/codex/pull/26840", + "meta": "Merged 2026-06-08T23:33:42Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/26840", + "meta": "artifacts/github/reviews/openai-codex-pr-26840.review.json" + } + ] + }, + "observed_change": "Codex adds a `codex-utils-path-uri` crate for immutable typed `file:` URIs that serialize as canonical URI text while still accepting legacy absolute native paths during deserialization.", + "public_signal_decision": "defer", + "control_plane_impact": "watch", + "publisher_angle": "watch_note", + "confidence": "confirmed", + "evidence": [ + "The upstream review records the new `PathUri` type and its `file:`-only validation boundary.", + "The PR body says the type is intended for cross-process and cross-OS identifiers without eager native path interpretation.", + "Serde migration accepts both canonical URI text and legacy absolute native paths.", + "Tests cover URI authorities, encoded metadata, non-UTF-8 POSIX paths, and legacy round trips." + ], + "candidate_followups": [ + "Watch future app-server and plugin payloads for adoption of `PathUri`.", + "Keep Decodex path handling explicit about URI strings versus native local filesystem paths.", + "Prefer URI-preserving storage where Decodex crosses process or OS boundaries." + ], + "social_notes": [ + "Use defer or watch-note framing unless a later PR wires `PathUri` into user-visible protocol fields.", + "Do not imply a public app-server field changed in this PR." + ], + "caveats": [ + "This PR adds infrastructure only.", + "Legacy absolute native path input remains accepted during migration." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-26934.json b/artifacts/github/impact/openai-codex-pr-26934.json new file mode 100644 index 000000000..35371b8cc --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-26934.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-26934", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Prune stale curated plugin caches", + "url": "https://github.com/openai/codex/pull/26934", + "meta": "Merged 2026-06-08T21:46:59Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/26934", + "meta": "artifacts/github/reviews/openai-codex-pr-26934.review.json" + } + ] + }, + "observed_change": "Codex curated plugin cache refresh now removes cached plugin directories whose names are no longer present in the raw openai-curated marketplace manifest.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records raw marketplace-name loading before curated cache refresh.", + "The loader removes stale caches for plugin names that no longer appear in the raw curated marketplace.", + "The PR body says user config is left untouched.", + "Manager tests cover a stale `google-sheets` cache being removed after the marketplace omits it." + ], + "candidate_followups": [ + "Make Decodex plugin inventory distinguish stale cache pruning from user config deletion.", + "Show curated marketplace removal as cache state, not as a failed plugin install.", + "Keep diagnostics tolerant of removed openai-curated plugin names." + ], + "social_notes": [ + "Public copy should say config stays untouched.", + "Avoid implying all remote or local plugin caches are pruned; this is openai-curated refresh." + ], + "caveats": [ + "The change is scoped to curated plugin cache refresh.", + "Existing user config remains in place even when stale cache directories are removed." + ] +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index 197adfbdb..b8b00b649 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": 14, - "high": 8, + "critical": 17, + "high": 9, "low": 2, - "normal": 16, + "normal": 12, "published_subjects_seen": 0, "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-09T14:04:59.634102Z", + "generated_at": "2026-06-09T20:05:04.380793Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,73 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "deprecated_removed", - "new_feature", - "release_packaging", - "security_policy" - ], - "changed_file_count": 2, - "commit_shas": [ - "d93f759a07dc3c9b685930233f441fe2fbce1c10" - ], - "committed_at": "2026-06-08T09:39:08Z", - "next_step": "ai_review_required", - "pr_number": 26974, - "pr_url": "https://github.com/openai/codex/pull/26974", - "review_priority": "critical", - "review_reason": "Needs AI review for deprecated_removed, new_feature, release_packaging, security_policy.", - "sample_paths": [ - "codex-rs/.cargo/audit.toml", - "codex-rs/deny.toml" - ], - "source_state": "merged", - "subject_id": "26974", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks" - ], - "title": "Ignore proc-macro-error2 advisory", - "url": "https://github.com/openai/codex/pull/26974" - }, - { - "attention_flags": [ - "deprecated_removed", - "new_feature", - "release_packaging" - ], - "changed_file_count": 4, - "commit_shas": [ - "95202ed2120c490f12f954daeb9ca8167f4e6dc8", - "c7f7ad74df9090665b12869705c54268794d289d", - "69c1858bc0c90ef389b581719229395e42676ed3", - "0fd91f10f38d70146f7b1d6ed6a3df34207c97cd", - "c7a47e5b23d8f0bf2742a052139eda48ab4982d6" - ], - "committed_at": "2026-06-08T17:16:36Z", - "next_step": "ai_review_required", - "pr_number": 26202, - "pr_url": "https://github.com/openai/codex/pull/26202", - "review_priority": "critical", - "review_reason": "Needs AI review for deprecated_removed, new_feature, release_packaging.", - "sample_paths": [ - ".github/scripts/archive-release-symbols-and-strip-binaries.sh", - ".github/workflows/rust-release-windows.yml", - ".github/workflows/rust-release.yml", - "codex-rs/Cargo.toml" - ], - "source_state": "merged", - "subject_id": "26202", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks", - "release_packaging", - "tests_ci" - ], - "title": "[codex] Restore release symbol artifacts with line tables", - "url": "https://github.com/openai/codex/pull/26202" - }, { "attention_flags": [ "breaking_change", @@ -598,75 +531,219 @@ }, { "attention_flags": [ - "auth_account", + "breaking_change", + "deprecated_removed", "new_feature", "protocol_change" ], - "changed_file_count": 5, + "changed_file_count": 7, "commit_shas": [ - "0fa7186cf9f9db8b60e308d369381ab4f076905b", - "42fb873e7ca5bc1d7661414db1299490198d52bd", - "6c10ca4be59c4270fcb9f4a03a73baa794b28da2", - "0c649e5492e4ee174f5b4942c9a26ee3e561a322" + "900347bbffae41f08467ae2ff7552ea3564847fe", + "80f3514fe4fa2d0c811797571ac8e813ed28c10d", + "95d77fadad869b547af91f67bebdfc40d9927675", + "7f8f4e5d1a414d102075fa0fe87acdfff7f12f46", + "375aab3440badf14d4f94ce5bb672fa800fea228", + "061e1ad76c24a244534a5f27f70ae713b7d610b2", + "bbdb159af09fb5fbf4c672c7e9679a41725f6e7c" + ], + "committed_at": "2026-06-09T16:41:58Z", + "next_step": "ai_review_required", + "pr_number": 22879, + "pr_url": "https://github.com/openai/codex/pull/22879", + "review_priority": "critical", + "review_reason": "Needs AI review for breaking_change, deprecated_removed, new_feature, protocol_change.", + "sample_paths": [ + "codex-rs/tui/src/app.rs", + "codex-rs/tui/src/app/tests.rs", + "codex-rs/tui/src/app/thread_routing.rs", + "codex-rs/tui/src/app_server_session.rs", + "codex-rs/tui/src/chatwidget/interaction.rs", + "codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__review_submission_warning_snapshot.snap", + "codex-rs/tui/src/chatwidget/tests/review_mode.rs" + ], + "source_state": "merged", + "subject_id": "22879", + "subject_kind": "pr", + "surface_hints": [ + "app_server_protocol", + "cli_tui", + "tests_ci" + ], + "title": "fix: Prevent /review crash when entering Esc on steer message", + "url": "https://github.com/openai/codex/pull/22879" + }, + { + "attention_flags": [ + "auth_account", + "breaking_change", + "deprecated_removed", + "new_feature", + "protocol_change", + "security_policy" ], - "committed_at": "2026-06-08T17:20:54Z", + "changed_file_count": 46, + "commit_shas": [ + "5d7823925c88f09568bafda637fccd92b56a9b6a", + "0f384f1b4d7e7542f1202fc966b8c6475fa27d0f", + "f0df7cb06b999489e989767092ecc767fa7c30cc", + "fec64281b4558807c592629485958b02f0869d55", + "f607c8696bc512aefbcf3fc038d6c53d77d4663c", + "2400fd9198b798764a5090410c8761a69fd61734", + "ed85bf407760a9dcc0ebb6971dc0505c1d4f3a1a" + ], + "committed_at": "2026-06-09T17:51:54Z", "next_step": "ai_review_required", - "pr_number": 26852, - "pr_url": "https://github.com/openai/codex/pull/26852", - "review_priority": "high", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change.", + "pr_number": 27184, + "pr_url": "https://github.com/openai/codex/pull/27184", + "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/src/connection_cleanup.rs", - "codex-rs/app-server/src/connection_rpc_gate.rs", - "codex-rs/app-server/src/lib.rs", - "codex-rs/app-server/src/message_processor.rs", - "codex-rs/app-server/src/request_serialization.rs" + "codex-rs/Cargo.lock", + "codex-rs/Cargo.toml", + "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/json/v2/ThreadStartParams.json", + "codex-rs/app-server-protocol/schema/typescript/v2/CapabilityRootLocation.ts", + "codex-rs/app-server-protocol/schema/typescript/v2/SelectedCapabilityRoot.ts", + "codex-rs/app-server-protocol/schema/typescript/v2/index.ts", + "codex-rs/app-server-protocol/src/protocol/v2/thread.rs", + "codex-rs/app-server/Cargo.toml", + "codex-rs/app-server/README.md" ], "source_state": "merged", - "subject_id": "26852", + "subject_id": "27184", "subject_kind": "pr", "surface_hints": [ - "app_server_protocol" + "app_server_protocol", + "auth_accounts", + "cli_tui", + "config_hooks", + "docs_examples", + "mcp_plugins", + "model_provider", + "tests_ci" ], - "title": "fix(app-server): avoid blocking connection cleanup", - "url": "https://github.com/openai/codex/pull/26852" + "title": "Load selected executor skills through extensions", + "url": "https://github.com/openai/codex/pull/27184" }, { "attention_flags": [ "auth_account", + "deprecated_removed", "new_feature", "protocol_change", "security_policy" ], "changed_file_count": 5, "commit_shas": [ - "c6e9b6f312775a7013c56050f5ecf38a3c4ac476", - "bdd975fb8c969040ca1647f23555641ae9b7b76b", - "219baef3c22457b3febaeaf3af1bef44fb5f7ef2" + "4f1183c281d837c81ee3f13f4b9207fc4bbd7115", + "4678bad085f9dac6876e0466c5f922635926d7db", + "583f8cca1bd6d6a4ea2762ad6ec1e39d646a8d51", + "3a804efa84757c84da11729a04e04995d5c04c8c" ], - "committed_at": "2026-06-08T17:49:59Z", + "committed_at": "2026-06-09T18:30:24Z", "next_step": "ai_review_required", - "pr_number": 26923, - "pr_url": "https://github.com/openai/codex/pull/26923", - "review_priority": "high", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, security_policy.", + "pr_number": 26835, + "pr_url": "https://github.com/openai/codex/pull/26835", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, security_policy.", "sample_paths": [ - "codex-rs/core/src/client.rs", - "codex-rs/core/tests/responses_headers.rs", - "codex-rs/core/tests/suite/compact_remote.rs", - "codex-rs/core/tests/suite/snapshots/all__suite__compact_remote__remote_manual_compact_api_auth_prompt_cache_key_request_diff.snap", - "codex-rs/core/tests/suite/snapshots/all__suite__compact_remote__remote_manual_compact_chatgpt_auth_service_tier_prompt_cache_key_request_diff.snap" + "codex-rs/Cargo.lock", + "codex-rs/ext/extension-api/Cargo.toml", + "codex-rs/ext/extension-api/tests/capabilities.rs", + "codex-rs/ext/extension-api/tests/registry.rs", + "codex-rs/ext/extension-api/tests/state.rs" ], "source_state": "merged", - "subject_id": "26923", + "subject_id": "26835", "subject_kind": "pr", "surface_hints": [ - "auth_accounts", + "config_hooks", + "tests_ci" + ], + "title": "[codex] Test extension API contracts", + "url": "https://github.com/openai/codex/pull/26835" + }, + { + "attention_flags": [ + "breaking_change", + "deprecated_removed", + "new_feature", + "protocol_change", + "release_packaging", + "security_policy" + ], + "changed_file_count": 32, + "commit_shas": [ + "a3c15ecaa97c2ce5624f1a3590ad500c3a5a0052", + "41ac94de1c00074094e20bcf17b44027e5be2154" + ], + "committed_at": "2026-06-09T19:27:10Z", + "next_step": "ai_review_required", + "pr_number": 27063, + "pr_url": "https://github.com/openai/codex/pull/27063", + "review_priority": "critical", + "review_reason": "Needs AI review for breaking_change, deprecated_removed, new_feature, protocol_change, release_packaging, security_policy.", + "sample_paths": [ + "codex-rs/analytics/src/analytics_client_tests.rs", + "codex-rs/analytics/src/reducer.rs", + "codex-rs/app-server-protocol/schema/json/ClientRequest.json", + "codex-rs/app-server-protocol/schema/json/ServerNotification.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/json/v2/ThreadForkParams.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json" + ], + "source_state": "merged", + "subject_id": "27063", + "subject_kind": "pr", + "surface_hints": [ + "app_server_protocol", "cli_tui", + "docs_examples", + "model_provider", + "tests_ci" + ], + "title": "[codex-analytics] add extensible feature thread sources", + "url": "https://github.com/openai/codex/pull/27063" + }, + { + "attention_flags": [ + "deprecated_removed", + "new_feature", + "protocol_change", + "security_policy" + ], + "changed_file_count": 2, + "commit_shas": [ + "e1ac564bb27512e94753613903c48977af502139", + "3ba65c149377ae8d5e15a0090ba36f80c6fdd1b6", + "bfa7824e0e6a74de4d1a8141744bef165622dd9a" + ], + "committed_at": "2026-06-09T19:48:04Z", + "next_step": "ai_review_required", + "pr_number": 26479, + "pr_url": "https://github.com/openai/codex/pull/26479", + "review_priority": "critical", + "review_reason": "Needs AI review for deprecated_removed, new_feature, protocol_change, security_policy.", + "sample_paths": [ + "codex-rs/.config/nextest.toml", + "justfile" + ], + "source_state": "merged", + "subject_id": "26479", + "subject_kind": "pr", + "surface_hints": [ + "config_hooks", "tests_ci" ], - "title": "Add HTTP window ID to Responses client metadata", - "url": "https://github.com/openai/codex/pull/26923" + "title": "[codex] Speed up local nextest runs", + "url": "https://github.com/openai/codex/pull/26479" }, { "attention_flags": [ @@ -883,198 +960,64 @@ }, { "attention_flags": [ - "auth_account", - "deprecated_removed", - "new_feature", - "protocol_change", - "release_packaging" - ], - "changed_file_count": 5, - "commit_shas": [ - "46e2da8bc5461a85aff9c060b4213372eb6992b3", - "5a56d7ecee1b27a94f1112e1b5e74f45a1f985ea" - ], - "committed_at": "2026-06-08T08:24:48Z", - "next_step": "ai_review_required", - "pr_number": 26632, - "pr_url": "https://github.com/openai/codex/pull/26632", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, release_packaging.", - "sample_paths": [ - "codex-rs/core/src/agent/control.rs", - "codex-rs/core/src/agent/control/legacy.rs", - "codex-rs/core/src/agent/control/residency.rs", - "codex-rs/core/src/agent/control/residency_tests.rs", - "codex-rs/core/src/agent/control/spawn.rs" - ], - "source_state": "merged", - "subject_id": "26632", - "subject_kind": "pr", - "surface_hints": [ - "tests_ci" - ], - "title": "feat: add v2 agent residency lru", - "url": "https://github.com/openai/codex/pull/26632" - }, - { - "attention_flags": [ - "auth_account", - "breaking_change", - "new_feature", - "protocol_change", - "release_packaging", - "security_policy" - ], - "changed_file_count": 11, - "commit_shas": [ - "a66741fc0468c7ffa97c690a9aa737a75068699d", - "d3e65b3c8f142c79be0a510094edc46e6bf17953", - "142344dacca9ff1c6d7ceedc89310800946d81dd", - "773a29a9f89f9f36ba0bf9700f9ef6e706d189f4", - "4fcd8712ac7358e8b9676e9aac9fabb48538260a", - "28d5be36730f14ffe5d949384e91d3b993d03fa0", - "37a985e98fcef7f8c6166bdef6766d59d490ce39", - "36dcfa4bf3030fb93717714712884d7a4ffef55f", - "751112ad6f92aee1e7103dc7dc6c8e088f51a622" - ], - "committed_at": "2026-06-08T12:21:28Z", - "next_step": "ai_review_required", - "pr_number": 26969, - "pr_url": "https://github.com/openai/codex/pull/26969", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, breaking_change, new_feature, protocol_change, release_packaging, security_policy.", - "sample_paths": [ - "codex-rs/core/src/agent/control.rs", - "codex-rs/core/src/agent/control/execution.rs", - "codex-rs/core/src/agent/control/execution_tests.rs", - "codex-rs/core/src/agent/control/spawn.rs", - "codex-rs/core/src/codex_thread.rs", - "codex-rs/core/src/session/session.rs", - "codex-rs/core/src/session/tests.rs", - "codex-rs/core/src/state/turn.rs", - "codex-rs/core/src/tasks/mod.rs", - "codex-rs/core/tests/suite/agent_execution.rs", - "codex-rs/core/tests/suite/mod.rs" - ], - "source_state": "merged", - "subject_id": "26969", - "subject_kind": "pr", - "surface_hints": [ - "tests_ci" - ], - "title": "feat: count V2 concurrency by active execution", - "url": "https://github.com/openai/codex/pull/26969" - }, - { - "attention_flags": [ - "breaking_change", "new_feature", "protocol_change" ], - "changed_file_count": 8, + "changed_file_count": 3, "commit_shas": [ - "2f9beaccb897de72f762966b71307da28298b72b" + "6f3624d244baecbe138a41dc6d7d744daac7ed07", + "09439a8433bf575a5e1d4741602378db27bd1357" ], - "committed_at": "2026-06-08T12:46:35Z", + "committed_at": "2026-06-09T16:16:27Z", "next_step": "ai_review_required", - "pr_number": 26994, - "pr_url": "https://github.com/openai/codex/pull/26994", - "review_priority": "normal", - "review_reason": "Needs AI review for breaking_change, new_feature, protocol_change.", + "pr_number": 27031, + "pr_url": "https://github.com/openai/codex/pull/27031", + "review_priority": "high", + "review_reason": "Needs AI review for new_feature, protocol_change.", "sample_paths": [ - "codex-rs/core/src/agent/control.rs", - "codex-rs/core/src/tools/handlers/multi_agents_spec.rs", - "codex-rs/core/src/tools/handlers/multi_agents_tests.rs", - "codex-rs/core/src/tools/handlers/multi_agents_v2.rs", - "codex-rs/core/src/tools/handlers/multi_agents_v2/interrupt_agent.rs", - "codex-rs/core/src/tools/handlers/multi_agents_v2/message_tool.rs", - "codex-rs/core/src/tools/spec_plan.rs", - "codex-rs/core/src/tools/spec_plan_tests.rs" + "codex-rs/app-server/src/request_processors/thread_processor.rs", + "codex-rs/app-server/tests/suite/v2/remote_thread_store.rs", + "codex-rs/thread-store/src/in_memory.rs" ], "source_state": "merged", - "subject_id": "26994", + "subject_id": "27031", "subject_kind": "pr", "surface_hints": [ + "app_server_protocol", "tests_ci" ], - "title": "Rename multi-agent v2 close_agent to interrupt_agent", - "url": "https://github.com/openai/codex/pull/26994" + "title": "Avoid rereading rollout history during cold resume", + "url": "https://github.com/openai/codex/pull/27031" }, { "attention_flags": [ - "auth_account", "new_feature", "protocol_change", "release_packaging" ], - "changed_file_count": 4, - "commit_shas": [ - "ab28c845a6a812549468c13e6d0022137c2ec52b", - "e61af26e42492f46a5178d5d023e0d6110d3ba0d", - "0b007fe89fad0510f6d110dd4e8a5e06d79a7214", - "74b3fb4726959890deb0dbe821842dbf9fe578be", - "b123fcecff1d293c20af985c9046b7e80ee7fb23", - "cd299c10b1200f8ceb0dc87bc40c7670363e96fe", - "9bf5709bb08e9448010ea67be362f9eb4cc97f2c", - "6bd602db71ecce6f4d549ce823f34668931e7085", - "fb4e3d143345e7baa9f8d324bebc130d725550e2" - ], - "committed_at": "2026-06-08T14:44:50Z", - "next_step": "ai_review_required", - "pr_number": 26997, - "pr_url": "https://github.com/openai/codex/pull/26997", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, release_packaging.", - "sample_paths": [ - "codex-rs/core/src/agent/control/residency.rs", - "codex-rs/core/src/agent/control/residency_tests.rs", - "codex-rs/core/src/agent/control/spawn.rs", - "codex-rs/core/src/agent/control_tests.rs" - ], - "source_state": "merged", - "subject_id": "26997", - "subject_kind": "pr", - "surface_hints": [ - "tests_ci" - ], - "title": "Avoid reopening v2 descendants on resume", - "url": "https://github.com/openai/codex/pull/26997" - }, - { - "attention_flags": [ - "auth_account", - "deprecated_removed", - "new_feature", - "protocol_change", - "security_policy" - ], - "changed_file_count": 4, + "changed_file_count": 2, "commit_shas": [ - "f23db2d6caf90384ede49d91ed7c95dbe31f2970", - "d9c87f3d713dd5bf146b7194e484e149c1809b0d", - "3cfd7f95deed730ac107b61a709300a08842135d", - "66dc1f9e1617d1932832c2fa0ee9ddaf51c788ad" + "1b7ca3095c65076bf1b8face4b554b1997f6e984" ], - "committed_at": "2026-06-08T16:53:04Z", + "committed_at": "2026-06-09T17:40:40Z", "next_step": "ai_review_required", - "pr_number": 26821, - "pr_url": "https://github.com/openai/codex/pull/26821", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, security_policy.", + "pr_number": 27173, + "pr_url": "https://github.com/openai/codex/pull/27173", + "review_priority": "high", + "review_reason": "Needs AI review for new_feature, protocol_change, release_packaging.", "sample_paths": [ - "codex-rs/core/src/tools/registry.rs", - "codex-rs/core/tests/suite/sqlite_state.rs", - "codex-rs/ext/web-search/src/output.rs", - "codex-rs/tools/src/tool_output.rs" + "codex-rs/app-server/src/request_processors/turn_processor.rs", + "codex-rs/app-server/tests/suite/v2/turn_start.rs" ], "source_state": "merged", - "subject_id": "26821", + "subject_id": "27173", "subject_kind": "pr", "surface_hints": [ + "app_server_protocol", "tests_ci" ], - "title": "[codex] Exclude external tool output from memories", - "url": "https://github.com/openai/codex/pull/26821" + "title": "app-server: reject direct input to multi-agent v2 sub-agents", + "url": "https://github.com/openai/codex/pull/27173" }, { "attention_flags": [ @@ -1082,33 +1025,31 @@ "new_feature", "protocol_change" ], - "changed_file_count": 6, + "changed_file_count": 2, "commit_shas": [ - "78ec3d1c6525f83653a2d63ccc6e9e4a8fadc664" + "f0566d8a72018d542d5cd745825a478d4d21b401" ], - "committed_at": "2026-06-08T17:52:31Z", + "committed_at": "2026-06-09T19:52:05Z", "next_step": "ai_review_required", - "pr_number": 26680, - "pr_url": "https://github.com/openai/codex/pull/26680", - "review_priority": "normal", + "pr_number": 27223, + "pr_url": "https://github.com/openai/codex/pull/27223", + "review_priority": "high", "review_reason": "Needs AI review for auth_account, new_feature, protocol_change.", "sample_paths": [ - "codex-rs/analytics/src/analytics_client_tests.rs", - "codex-rs/analytics/src/events.rs", - "codex-rs/analytics/src/facts.rs", - "codex-rs/core/src/compact.rs", - "codex-rs/core/src/compact_remote.rs", - "codex-rs/core/src/compact_remote_v2.rs" + "codex-rs/app-server/tests/suite/v2/plugin_uninstall.rs", + "codex-rs/core-plugins/src/remote.rs" ], "source_state": "merged", - "subject_id": "26680", + "subject_id": "27223", "subject_kind": "pr", "surface_hints": [ - "cli_tui", + "app_server_protocol", + "mcp_plugins", + "release_packaging", "tests_ci" ], - "title": "[codex-analytics] report compaction analytics details", - "url": "https://github.com/openai/codex/pull/26680" + "title": "fix: use plugin service route for remote uninstall", + "url": "https://github.com/openai/codex/pull/27223" }, { "attention_flags": [ @@ -1434,6 +1375,69 @@ "title": "[codex] preserve fsmonitor for worktree Git reads", "url": "https://github.com/openai/codex/pull/26880" }, + { + "attention_flags": [ + "auth_account", + "breaking_change", + "deprecated_removed", + "new_feature", + "protocol_change" + ], + "changed_file_count": 5, + "commit_shas": [ + "c0585a40a8aca901dea5d4d04c19188fa084a681" + ], + "committed_at": "2026-06-09T15:29:32Z", + "next_step": "ai_review_required", + "pr_number": 27080, + "pr_url": "https://github.com/openai/codex/pull/27080", + "review_priority": "normal", + "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, new_feature, protocol_change.", + "sample_paths": [ + ".codex/skills/babysit-pr/SKILL.md", + ".codex/skills/babysit-pr/agents/openai.yaml", + ".codex/skills/babysit-pr/references/github-api-notes.md", + ".codex/skills/babysit-pr/scripts/gh_pr_watch.py", + ".codex/skills/babysit-pr/scripts/test_gh_pr_watch.py" + ], + "source_state": "merged", + "subject_id": "27080", + "subject_kind": "pr", + "surface_hints": [ + "tests_ci" + ], + "title": "[codex] Ignore pending PR review comments", + "url": "https://github.com/openai/codex/pull/27080" + }, + { + "attention_flags": [ + "deprecated_removed", + "new_feature" + ], + "changed_file_count": 2, + "commit_shas": [ + "a1b5b9e05a52a2a13e19a20e39592fe4c1b42ae7", + "df9c1d596b469d3be2a38ee36ce3329ad3c952d6" + ], + "committed_at": "2026-06-09T15:50:13Z", + "next_step": "ai_review_required", + "pr_number": 26420, + "pr_url": "https://github.com/openai/codex/pull/26420", + "review_priority": "normal", + "review_reason": "Needs AI review for deprecated_removed, new_feature.", + "sample_paths": [ + "codex-rs/state/src/runtime.rs", + "codex-rs/state/src/runtime/backfill.rs" + ], + "source_state": "merged", + "subject_id": "26420", + "subject_kind": "pr", + "surface_hints": [ + "internal_churn" + ], + "title": "Avoid no-op backfill state writes", + "url": "https://github.com/openai/codex/pull/26420" + }, { "attention_flags": [ "auth_account" diff --git a/artifacts/github/reviews/openai-codex-pr-26486.review.json b/artifacts/github/reviews/openai-codex-pr-26486.review.json new file mode 100644 index 000000000..a55b104fc --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-26486.review.json @@ -0,0 +1,76 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-26486", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "26486", + "commit_shas": [ + "6ca9ccf9cd12de3a88ffa2a001eb78a0b2f97c10", + "0165dbf6eda0ccb61187df6752a4e08de93f520c", + "9457965868264ba8838440a6d9ff539d830ee0af", + "2ff147fc6a4721000547e8b0d6b8ea8d97d58f24", + "c5a021feb1b677c105f0f51044311b62b18925af", + "034835589958076a97792994e22e724ad6d9a496" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Route image edits through referenced file paths", + "url": "https://github.com/openai/codex/pull/26486", + "meta": "Merged 2026-06-08T21:23:56Z" + }, + { + "kind": "commit", + "title": "draft", + "url": "https://github.com/openai/codex/commit/6ca9ccf9cd12de3a88ffa2a001eb78a0b2f97c10" + }, + { + "kind": "commit", + "title": "fix", + "url": "https://github.com/openai/codex/commit/0165dbf6eda0ccb61187df6752a4e08de93f520c" + } + ] + }, + "reviewed_at": "2026-06-09T20:07:10Z", + "observed_change": "Codex image generation extension replaces the old `action` argument with explicit image references: omitted references generate a new image, while `referenced_image_paths` or a bounded recent-image fallback drive edits.", + "changed_surfaces": [ + "image generation extension tool schema", + "image generation extension instructions", + "image edit request construction", + "app-server v2 imagegen integration tests", + "codex-utils-image prompt image loading" + ], + "user_visible_path": "When the model edits an attached or local image, Codex can pass exact referenced file paths to the edit request instead of inferring inputs from conversation history.", + "control_plane_relevance": "Decodex tool-routing prompts and any automation that invokes Codex image editing should stop sending `action` and prefer explicit local image paths when available.", + "compatibility_risk": "High for callers or prompt instructions that still emit `action = generate` or `action = edit`, because `ImagegenArgs` denies unknown fields and the schema now exposes `referenced_image_paths` and `num_last_images_to_include` instead.", + "adoption_opportunity": "Audit Decodex image-tool instructions and app-server attachment plumbing so referenced local images are passed as paths and fallback history inclusion stays minimal.", + "community_value": "High for Codex image users because edit inputs become explicit and less dependent on fragile recent-image heuristics.", + "deprecated_or_breaking_notes": "The `action` enum is removed from the imagegen tool schema; generation is represented by omitted image references, and edits are represented by populated references.", + "confidence": "confirmed", + "evidence": [ + "PR #26486 says image edits should use exact selected images and replaces `action` with optional `referenced_image_paths`.", + "codex-rs/ext/image-generation/src/tool.rs removes `ImagegenAction`, adds `referenced_image_paths` and `num_last_images_to_include`, and loads referenced absolute image paths through `codex-utils-image`.", + "codex-rs/ext/image-generation/imagegen_description.md tells the model to omit references for generation, use `referenced_image_paths` for edits with local paths, and use recent-image fallback only when a target lacks a local path.", + "codex-rs/ext/image-generation/src/tests.rs renames generation tests around omitted references and adds reference-path edit request coverage.", + "codex-rs/app-server/tests/suite/v2/imagegen_extension.rs adds an integration test for an attached model-visible image routed through the image edit endpoint.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-26486.json records 6 changed files for the merged PR." + ], + "caveats": "The PR body also mentions `num_last_images_to_include`; the durable public value is strongest when local file paths are available.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The image tool schema change can break old action-based callers and affects Decodex tool instructions." + }, + { + "type": "social_candidate", + "reason": "The explicit image edit input path has a clear practical explainer angle for Codex users." + }, + { + "type": "linear_followup", + "reason": "Decodex should audit image-edit tool prompts and attachment routing for the new schema." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-26687.review.json b/artifacts/github/reviews/openai-codex-pr-26687.review.json new file mode 100644 index 000000000..9216c0a3a --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-26687.review.json @@ -0,0 +1,91 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-26687", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "26687", + "commit_shas": [ + "534befc48bc2eec3bd002a31f5ac2c3e23a1f29a", + "bf26b031e535e3fb925bfacf41daf44ef87231ee", + "e0f10fa7e41d0cbfe3504b38c735e1de866cab27", + "09dccdf652b9c4c4995517a0191679836d69b026", + "4e459c4fcc4b6c10438e8e5e12430e64f3dd43ba", + "68060899b6a45f5d67c84938ffc906545c5e3b41", + "e239da0a36a20367e88e0f9d7d71b5cfb6cb5ca0", + "505dbae2c71a5886d6e12ff1082279fef26d94fd", + "d63b1a125f6e97fd1d4f26232455343207521b89", + "d98e9b984c0325d31fb1b58969e279fbedd09aca", + "fc6f77e19b0b41235d88a4879609a28eed97bb2b", + "6046b245a3f0efe30ca4012dec10c06c6fa32445", + "6a420d41ac70f172bb6deb9b75b6c2f5c93cdfa8", + "4923381143d028955a8542cd8c390f1505e83851", + "d10fb7bc65892d496a9e9ae0e0eed585ddd21d5f", + "acb9e264576e9d76f8438129f3eb321ff9ac2fd5", + "1c883fdb9219aff8ed78464669480a58f7745ce6", + "12e18e679d1dc46c712b87fc8f21c04ff95a8310", + "c1205d9c1a9d6fd80f6d4cea7198805c243fb9f6", + "f6e390602e97aa867a228c399846440d54d93cb3" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Pair thread environment settings", + "url": "https://github.com/openai/codex/pull/26687", + "meta": "Merged 2026-06-08T20:55:15Z" + }, + { + "kind": "commit", + "title": "Pair thread environment settings", + "url": "https://github.com/openai/codex/commit/534befc48bc2eec3bd002a31f5ac2c3e23a1f29a" + }, + { + "kind": "commit", + "title": "Move turn environment selections to protocol", + "url": "https://github.com/openai/codex/commit/e239da0a36a20367e88e0f9d7d71b5cfb6cb5ca0" + } + ] + }, + "reviewed_at": "2026-06-09T20:07:10Z", + "observed_change": "Codex now treats cwd and environment selection as one paired thread setting while preserving the app-server public `thread/settings/update` cwd shape.", + "changed_surfaces": [ + "app-server v2 turn start environment semantics", + "app-server thread settings translation", + "core thread configuration snapshots", + "Codex user-input and guardian review submission paths", + "MCP, memory, and thread-manager callsites that submit user turns" + ], + "user_visible_path": "App-server clients can keep sending top-level `cwd`, but `turn/start` environments now describe the current and subsequent turns, reducing silent cwd/environment desynchronization after settings updates.", + "control_plane_relevance": "Decodex app-server consumers should treat cwd and selected execution environments as a single sticky thread setting when replaying, resuming, or displaying retained-lane execution context.", + "compatibility_risk": "Moderate for strict app-server or internal Codex integrations that assumed turn environment selections were only turn-scoped or that patched an internal `ThreadSettingsOverrides.cwd` field directly; the PR says the public app-server params are unchanged.", + "adoption_opportunity": "Use the paired environment settings shape for Decodex retained-lane cwd readback, thread resume diagnostics, and future remote-environment displays instead of storing cwd independently.", + "community_value": "Medium-high for Codex operators because it clarifies that cwd and environment selection are one execution-context setting and closes a class of next-turn context drift.", + "deprecated_or_breaking_notes": "The internal top-level cwd override is replaced by `ThreadSettingsOverrides.environments`; public `thread/settings/update` params remain translated by app-server.", + "confidence": "confirmed", + "evidence": [ + "PR #26687 says cwd and environment selections are a single logical setting and that app-server keeps `thread/settings/update` public params unchanged while translating top-level `cwd` into the paired internal shape.", + "codex-rs/app-server-protocol/src/protocol/v2/turn.rs changes the environment comment from turn-scoped environments to environments for this turn and subsequent turns.", + "codex-rs/protocol/src/protocol.rs adds `TurnEnvironmentSelections` with a legacy fallback cwd and synchronization of the primary environment cwd.", + "codex-rs/core/src/session/session.rs replaces the session configuration cwd field with sticky thread-level environment selections.", + "codex-rs/app-server/src/request_processors/turn_processor.rs builds environment overrides before constructing thread settings overrides.", + "Tests and callsites across core, app-server, MCP, memories, and thread-manager sample stop passing `Op::UserInput.environments` directly and instead use thread settings overrides.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-26687.json records 90 changed files for the merged PR." + ], + "caveats": "The public app-server request shape is preserved; the risk is mainly semantic and internal-integration drift rather than a removed public field.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "This changes app-server execution-context semantics that Decodex Control Plane consumers should model." + }, + { + "type": "social_candidate", + "reason": "The paired cwd/environment behavior is an operator-facing app-server concept worth a concise Publisher handoff." + }, + { + "type": "linear_followup", + "reason": "Decodex should verify retained-lane resume and dashboard readback do not store cwd separately from environment selections." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-26840.review.json b/artifacts/github/reviews/openai-codex-pr-26840.review.json new file mode 100644 index 000000000..ff73d80fe --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-26840.review.json @@ -0,0 +1,84 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-26840", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "26840", + "commit_shas": [ + "3c842f79d482b0f96fcf9a039264ef1896345d17", + "758e981a1d26ae66fd2c1ff530873e378705b45b", + "882a9b4991823dd85cfc97ebf3557bfcf6623f4d", + "e03cce53589b3ad050d3d251741014fef5e88940", + "ffe52eeeeb6e01460d784b230f9a260e9a89e488", + "cf89de2c574b750f864b0f9a44fb2e136ce9e093", + "43038a2be12ee9e09bbb23e494a069213a12ea49", + "80f6b9758cd4938041cdf619b06c72657f13abcb", + "18883637213c269c4e589668169cc91b52f3b38e", + "9fba3263e8f0b6ff53ca995c40de0259cb0452a5", + "2cfc5a19b265c1860e9bd4eb2519cf38b984464b", + "4fc4e5baeb335cd5b6faca38e4a1bae6f623d66d", + "6cf3f6526a586c33b27ce11ad50f54aaf366462c", + "8a99473f3a265925e8b94e7f652c8584bc67084a", + "bd2279b23436fa40f838d9c469c8c6029cf7cffc", + "14a6f970c14aa660e94dbb5085e6a3e8fd3ad211", + "3eb69a100fb57eacb88073eb944149315aab00c1", + "1bf23e23a4360abe0be9b536a9b84ca24a13c92c", + "ad09d4f63e134d7bdb6f7dad8d726229f6669b06" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Add typed file URIs", + "url": "https://github.com/openai/codex/pull/26840", + "meta": "Merged 2026-06-08T23:33:42Z" + }, + { + "kind": "commit", + "title": "feat: add typed path URI support", + "url": "https://github.com/openai/codex/commit/3c842f79d482b0f96fcf9a039264ef1896345d17" + }, + { + "kind": "commit", + "title": "codex: keep environment IDs inside path URIs", + "url": "https://github.com/openai/codex/commit/18883637213c269c4e589668169cc91b52f3b38e" + } + ] + }, + "reviewed_at": "2026-06-09T20:07:10Z", + "observed_change": "Codex adds a `codex-utils-path-uri` crate for immutable typed `file:` URIs that serialize as canonical URI text while still accepting legacy absolute native paths during deserialization.", + "changed_surfaces": [ + "new codex-utils-path-uri crate", + "Cargo workspace membership and lockfile", + "Bazel crate definition", + "CODEOWNERS ownership for the path-uri utility" + ], + "user_visible_path": "There is no direct user-facing command or app-server field in this PR, but future Codex payloads can use typed `file:` URI identifiers without eagerly interpreting them as local native paths.", + "control_plane_relevance": "Decodex should prepare app-server, plugin, and artifact paths to accept source-preserving `file:` URI strings and convert to native paths only at explicit local filesystem boundaries.", + "compatibility_risk": "Low immediate risk because the new type accepts legacy absolute native paths during deserialization; future fields that adopt it may change path-string assumptions.", + "adoption_opportunity": "Use `PathUri`-style semantics for cross-process path identifiers, especially where Decodex needs to carry remote or OS-specific file references without losing URI authority or encoding.", + "community_value": "Medium as a watch item for Codex integrators because it signals a migration toward stable file URI identifiers across process and operating-system boundaries.", + "deprecated_or_breaking_notes": "No public field is removed in this PR; migration support preserves legacy absolute native path input.", + "confidence": "confirmed", + "evidence": [ + "PR #26840 says Codex needs stable `file:` URI identifiers that can cross process and OS boundaries without eagerly interpreting them as native paths.", + "codex-rs/utils/path-uri/src/lib.rs defines `PathUri`, accepts only `file:` URLs, preserves URI path operations, serializes as canonical URI text, and keeps native conversion explicit.", + "The PR body says deserialization accepts both URI text and legacy absolute native paths during migration.", + "codex-rs/utils/path-uri/src/tests.rs covers POSIX paths, Windows-looking paths, UNC authorities, encoded characters, non-UTF-8 POSIX paths, hierarchy operations, and serde round trips.", + "codex-rs/Cargo.toml and Cargo.lock add `codex-utils-path-uri` as a workspace crate, and .github/CODEOWNERS assigns the utility path.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-26840.json records 7 changed files for the merged PR." + ], + "caveats": "This is enabling infrastructure; downstream behavior depends on later PRs adopting `PathUri` in protocol, plugin, or app-server payloads.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Typed file URI support is relevant to future Decodex path and app-server compatibility work." + }, + { + "type": "social_candidate", + "reason": "A defer/watch Publisher handoff can preserve the migration signal without overclaiming shipped user behavior." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-26934.review.json b/artifacts/github/reviews/openai-codex-pr-26934.review.json new file mode 100644 index 000000000..5d8bb705b --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-26934.review.json @@ -0,0 +1,59 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-26934", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "26934", + "commit_shas": [ + "a65d2d5a2bc1987b53a3afab765f1240da867060" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Prune stale curated plugin caches", + "url": "https://github.com/openai/codex/pull/26934", + "meta": "Merged 2026-06-08T21:46:59Z" + }, + { + "kind": "commit", + "title": "Prune stale curated plugin caches", + "url": "https://github.com/openai/codex/commit/a65d2d5a2bc1987b53a3afab765f1240da867060" + } + ] + }, + "reviewed_at": "2026-06-09T20:07:10Z", + "observed_change": "Codex curated plugin cache refresh now removes cached plugin directories whose names are no longer present in the raw openai-curated marketplace manifest.", + "changed_surfaces": [ + "core plugin curated-cache refresh", + "marketplace manifest loading", + "plugin manager regression tests" + ], + "user_visible_path": "Users with an old cached curated plugin, such as the standalone Google Sheets plugin named in the PR body, will no longer keep loading that stale cache after the curated marketplace drops it.", + "control_plane_relevance": "Decodex plugin inventory and install-cache mirrors should treat curated marketplace absence as cache-pruning evidence, not as proof that user config was deleted.", + "compatibility_risk": "Moderate for operators relying on stale curated plugin cache entries after a plugin is removed from the marketplace; existing config remains untouched, but the stale cached implementation is pruned.", + "adoption_opportunity": "Reflect curated-cache pruning in Decodex plugin status and explain removed marketplace entries separately from user-selected config.", + "community_value": "Medium for Codex plugin operators because it clarifies why a previously cached curated plugin may disappear from loadable cache after marketplace changes.", + "deprecated_or_breaking_notes": "No user config key is removed, but stale plugin caches for names absent from the raw curated marketplace are deleted during refresh.", + "confidence": "confirmed", + "evidence": [ + "PR #26934 says curated plugin startup refresh removes cached plugins whose names no longer appear in the raw openai-curated marketplace and leaves existing config untouched.", + "codex-rs/core-plugins/src/marketplace.rs adds `load_raw_marketplace_plugin_names` to read names directly from the raw marketplace manifest.", + "codex-rs/core-plugins/src/loader.rs loads those raw marketplace names before refreshing curated plugin cache.", + "codex-rs/core-plugins/src/manager_tests.rs adds `refresh_curated_plugin_cache_removes_cache_for_plugin_removed_from_marketplace` covering a stale `google-sheets` cache.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-26934.json records 3 changed files for the merged PR." + ], + "caveats": "The change is scoped to openai-curated plugin cache refresh; it does not remove the user's config selection.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Curated plugin cache pruning affects plugin inventory and operator diagnostics." + }, + { + "type": "social_candidate", + "reason": "The stale-cache behavior is useful public operator guidance for plugin users." + } + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-26486.json b/artifacts/github/social-candidates/openai-codex-pr-26486.json new file mode 100644 index 000000000..0242a40c3 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-26486.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-26486", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "practical_explainer", + "priority": "critical", + "audience": "Codex users and image-tool integrators", + "candidate_text": [ + "Codex image edits now use explicit `referenced_image_paths` instead of an `action` flag, with `num_last_images_to_include` only as fallback for images without local paths. Old action-based calls need updating. PR: https://github.com/openai/codex/pull/26486" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-26486.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-26486.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/26486" + ] + }, + "evidence_notes": [ + "The imagegen tool schema removes `action` and adds `referenced_image_paths` plus `num_last_images_to_include`.", + "Tool instructions say to omit image references for generation and use referenced paths for edits when available.", + "The app-server integration test covers attached images routed to image edit.", + "`ImagegenArgs` denies unknown fields, so old action-based payloads need updating." + ], + "claims": [ + { + "text": "Codex image edits now prefer explicit referenced image paths.", + "evidence": "artifacts/github/reviews/openai-codex-pr-26486.review.json", + "confidence": "confirmed" + }, + { + "text": "Old `action`-based imagegen calls need updating for the new schema.", + "evidence": "artifacts/github/impact/openai-codex-pr-26486.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes a concrete image tool schema and gives users a clearer edit-input path.", + "idempotency_key": "x:decodexspace:openai-codex-pr-26486:practical_explainer" + }, + "caveats": [ + "The fallback recent-image path should stay minimal and only apply when no local file path exists.", + "This applies to the image-generation extension schema." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-26687.json b/artifacts/github/social-candidates/openai-codex-pr-26687.json new file mode 100644 index 000000000..6e043b003 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-26687.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-26687", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "critical", + "audience": "Codex app-server and Control Plane operators", + "candidate_text": [ + "Codex now pairs thread cwd with environment selections: `turn/start` environments can become sticky for later turns while app-server keeps the public `cwd` shape. Control-plane clients should model them as one setting. PR: https://github.com/openai/codex/pull/26687" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-26687.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-26687.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/26687" + ] + }, + "evidence_notes": [ + "`turn/start` environments are documented as applying to this turn and subsequent turns.", + "`TurnEnvironmentSelections` pairs fallback cwd with selected environments.", + "The PR body says public app-server params are unchanged while app-server translates top-level cwd internally.", + "Core session configuration stores sticky thread-level environment selections." + ], + "claims": [ + { + "text": "Codex pairs cwd and environment selection as a sticky thread setting.", + "evidence": "artifacts/github/reviews/openai-codex-pr-26687.review.json", + "confidence": "confirmed" + }, + { + "text": "The public app-server cwd request shape remains translated rather than removed.", + "evidence": "artifacts/github/impact/openai-codex-pr-26687.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes concrete app-server execution-context semantics used by Control Plane operators.", + "idempotency_key": "x:decodexspace:openai-codex-pr-26687:operator_impact" + }, + "caveats": [ + "Public app-server request params are preserved.", + "The main risk is semantic drift for app-server consumers and internal integrations." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-26840.json b/artifacts/github/social-candidates/openai-codex-pr-26840.json new file mode 100644 index 000000000..e1e47d690 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-26840.json @@ -0,0 +1,55 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-26840", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "watch_note", + "priority": "normal", + "audience": "Codex app-server, plugin, and path-handling integrators", + "candidate_text": [ + "Watch item: Codex added a typed `PathUri` crate for immutable `file:` URI identifiers and legacy absolute-path deserialization. No public protocol field changed yet, but this points to future path-safe app-server/plugin payloads. PR: https://github.com/openai/codex/pull/26840" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-26840.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-26840.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/26840" + ] + }, + "evidence_notes": [ + "The PR body frames `PathUri` as stable `file:` URI identifiers across processes and operating systems.", + "`PathUri` validates `file:` URLs and serializes as canonical URI text.", + "Serde migration accepts legacy absolute native paths.", + "No app-server or public protocol field is wired to `PathUri` in this PR." + ], + "claims": [ + { + "text": "Codex added a typed `PathUri` utility for immutable `file:` URI identifiers.", + "evidence": "artifacts/github/reviews/openai-codex-pr-26840.review.json", + "confidence": "confirmed" + }, + { + "text": "The PR does not yet change a public protocol field.", + "evidence": "artifacts/github/impact/openai-codex-pr-26840.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "defer", + "reason": "watch_note_only: the source-backed migration signal is useful, but the PR is infrastructure until a later payload adopts `PathUri`.", + "idempotency_key": "x:decodexspace:openai-codex-pr-26840:watch_note:defer" + }, + "caveats": [ + "This is not a publication request.", + "Future behavior depends on later PRs adopting `PathUri` in protocol or app-server payloads." + ], + "next_steps": [ + "Do not hand this defer candidate directly to Publisher for posting.", + "Use it as release-rollup context if a later source-reviewed PR wires `PathUri` into user-visible behavior." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-26934.json b/artifacts/github/social-candidates/openai-codex-pr-26934.json new file mode 100644 index 000000000..004f6ebfe --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-26934.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-26934", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "high", + "audience": "Codex plugin operators", + "candidate_text": [ + "Codex curated plugin refresh now prunes stale cached plugins whose names disappeared from the openai-curated marketplace, instead of continuing to load old cache entries. Config stays untouched. PR: https://github.com/openai/codex/pull/26934" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-26934.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-26934.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/26934" + ] + }, + "evidence_notes": [ + "The PR body says stale curated plugin caches are removed when names disappear from the raw marketplace.", + "The loader reads raw marketplace plugin names before cache refresh.", + "The marketplace helper extracts raw plugin names from the manifest.", + "Regression coverage uses a stale `google-sheets` cache and verifies it is removed." + ], + "claims": [ + { + "text": "Curated plugin refresh prunes stale cache entries that disappeared from the openai-curated marketplace.", + "evidence": "artifacts/github/reviews/openai-codex-pr-26934.review.json", + "confidence": "confirmed" + }, + { + "text": "Existing user config is left untouched by the cache-pruning change.", + "evidence": "artifacts/github/impact/openai-codex-pr-26934.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes a visible plugin-cache behavior operators may need to diagnose.", + "idempotency_key": "x:decodexspace:openai-codex-pr-26934:operator_impact" + }, + "caveats": [ + "The change is scoped to openai-curated plugin cache refresh.", + "User config is not deleted." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +}