From 9f34d9f4cc2a38adf6f729174fcc3eb4b741fa21 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Mon, 22 Jun 2026 17:01:46 +0800 Subject: [PATCH 01/15] Implement issue #60 --- codex-rs/core/src/tools/spec_plan_tests.rs | 12 ++++ codex-rs/core/tests/suite/prompt_caching.rs | 69 +++++++++++++++++++ codex-rs/models-manager/src/model_info.rs | 8 ++- .../models-manager/src/model_info_tests.rs | 15 ++++ 4 files changed, 101 insertions(+), 3 deletions(-) diff --git a/codex-rs/core/src/tools/spec_plan_tests.rs b/codex-rs/core/src/tools/spec_plan_tests.rs index 3f9832c857c5..d764c7db70c6 100644 --- a/codex-rs/core/src/tools/spec_plan_tests.rs +++ b/codex-rs/core/src/tools/spec_plan_tests.rs @@ -8,6 +8,7 @@ use codex_mcp::ToolInfo; use codex_model_provider::create_model_provider; use codex_model_provider_info::AMAZON_BEDROCK_PROVIDER_ID; use codex_model_provider_info::ModelProviderInfo; +use codex_models_manager::model_info::model_info_from_slug; use codex_protocol::config_types::WebSearchMode; use codex_protocol::dynamic_tools::DynamicToolSpec; use codex_protocol::openai_models::ApplyPatchToolType; @@ -599,6 +600,17 @@ async fn zsh_fork_unified_exec_keeps_shell_parameter_when_remote_environment_ava )); } +#[tokio::test] +async fn fallback_model_info_exposes_apply_patch_to_model() { + let plan = probe(|turn| { + turn.model_info = model_info_from_slug("gpt-oss:20b"); + }) + .await; + + plan.assert_visible_contains(&["apply_patch"]); + plan.assert_registered_contains(&["apply_patch"]); +} + #[tokio::test] async fn environment_count_controls_environment_backed_tools() { let no_environment = probe(|turn| { diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index 809cb0ea3276..26155af97570 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -107,6 +107,75 @@ fn normalize_newlines(text: &str) -> String { text.replace("\r\n", "\n") } +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn fallback_model_request_exposes_apply_patch_and_parallel_tool_calls() -> anyhow::Result<()> +{ + skip_if_no_network!(Ok(())); + use pretty_assertions::assert_eq; + + let server = start_mock_server().await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + + let TestCodex { codex, .. } = test_codex() + .with_config(|config| { + config.model = Some("gpt-oss:20b".to_string()); + config + .web_search_mode + .set(WebSearchMode::Cached) + .expect("test web_search_mode should satisfy constraints"); + }) + .build(&server) + .await?; + + codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "hello".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + responsesapi_client_metadata: None, + additional_context: Default::default(), + thread_settings: Default::default(), + }) + .await?; + wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; + + let body = req.single_request().body_json(); + let tool_names = body["tools"] + .as_array() + .expect("tools array") + .iter() + .map(|tool| { + tool.get("name") + .and_then(|value| value.as_str()) + .or_else(|| tool.get("type").and_then(|value| value.as_str())) + .expect("tool name or type") + .to_string() + }) + .collect::>(); + let instructions = body["instructions"] + .as_str() + .expect("instructions should be a string"); + + assert_eq!(body["model"], serde_json::json!("gpt-oss:20b")); + assert!( + tool_names.iter().any(|name| name == "apply_patch"), + "expected apply_patch in tools: {tool_names:?}" + ); + assert!( + instructions.contains("multi_tool_use.parallel"), + "expected parallel tool guidance in instructions" + ); + assert_eq!(body["parallel_tool_calls"], serde_json::json!(true)); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); diff --git a/codex-rs/models-manager/src/model_info.rs b/codex-rs/models-manager/src/model_info.rs index 566953dfc2d2..793a94b186d3 100644 --- a/codex-rs/models-manager/src/model_info.rs +++ b/codex-rs/models-manager/src/model_info.rs @@ -1,4 +1,5 @@ use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::openai_models::ApplyPatchToolType; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::ModelInfo; use codex_protocol::openai_models::ModelInstructionsVariables; @@ -18,6 +19,7 @@ const DEFAULT_PERSONALITY_HEADER: &str = "You are Codex, a coding agent based on const LOCAL_FRIENDLY_TEMPLATE: &str = "You optimize for team morale and being a supportive teammate as much as code quality."; const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective software engineer."; +const FALLBACK_PARALLEL_TOOL_INSTRUCTIONS: &str = "- Parallelize tool calls whenever possible - especially file reads, such as `cat`, `rg`, `sed`, `ls`, `git show`, `nl`, `wc`. Use `multi_tool_use.parallel` to parallelize tool calls and only this."; const PERSONALITY_PLACEHOLDER: &str = "{{ personality }}"; pub fn with_config_overrides(mut model: ModelInfo, config: &ModelsManagerConfig) -> ModelInfo { @@ -80,16 +82,16 @@ pub fn model_info_from_slug(slug: &str) -> ModelInfo { default_service_tier: None, availability_nux: None, upgrade: None, - base_instructions: BASE_INSTRUCTIONS.to_string(), + base_instructions: format!("{BASE_INSTRUCTIONS}\n\n{FALLBACK_PARALLEL_TOOL_INSTRUCTIONS}"), model_messages: local_personality_messages_for_slug(slug), supports_reasoning_summaries: false, default_reasoning_summary: ReasoningSummary::Auto, support_verbosity: false, default_verbosity: None, - apply_patch_tool_type: None, + apply_patch_tool_type: Some(ApplyPatchToolType::Freeform), web_search_tool_type: WebSearchToolType::Text, truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000), - supports_parallel_tool_calls: false, + supports_parallel_tool_calls: true, supports_image_detail_original: false, context_window: Some(272_000), max_context_window: Some(272_000), diff --git a/codex-rs/models-manager/src/model_info_tests.rs b/codex-rs/models-manager/src/model_info_tests.rs index 70ad3da8dfb7..1f0e5c5fbd0d 100644 --- a/codex-rs/models-manager/src/model_info_tests.rs +++ b/codex-rs/models-manager/src/model_info_tests.rs @@ -1,7 +1,22 @@ use super::*; use crate::ModelsManagerConfig; +use codex_protocol::openai_models::ApplyPatchToolType; use pretty_assertions::assert_eq; +#[test] +fn fallback_model_info_exposes_local_tool_capabilities() { + let model = model_info_from_slug("gpt-oss:20b"); + + assert!(model.used_fallback_model_metadata); + assert_eq!(model.slug, "gpt-oss:20b"); + assert_eq!(model.display_name, "gpt-oss:20b"); + assert_eq!( + model.apply_patch_tool_type, + Some(ApplyPatchToolType::Freeform) + ); + assert!(model.supports_parallel_tool_calls); +} + #[test] fn reasoning_summaries_override_true_enables_support() { let model = model_info_from_slug("unknown-model"); From ee49afb7bb431d4556dd349d0209db4df4335d4d Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 11:33:48 +0800 Subject: [PATCH 02/15] codex: fix PR #62 formatting check --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 73624f1ef588..6ebbcc665eda 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -294,8 +294,8 @@ This project uses Python 3+. You should not use the `__future__` module. If you need to worry about feature compatibility between different 3.xx point releases, check the closest `pyproject.toml`'s `requires-python` field to see what minimum runtime version is supported. - + ## Consensus R&D Foundational Invariants (managed by consensus-rnd) - FI-001 AI-authored artifacts are untrusted by default; before entering mainline, they must pass independent checks, including the applicable mix of consensus, review, or automated verification. From c4ccf85808408c61d7fbd90847519a362e5cb9cc Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 12:05:41 +0800 Subject: [PATCH 03/15] codex: fix CI clippy failure on PR #62 --- codex-rs/utils/pty/src/process_group.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codex-rs/utils/pty/src/process_group.rs b/codex-rs/utils/pty/src/process_group.rs index d56dd69af55e..ad8eb2824ed9 100644 --- a/codex-rs/utils/pty/src/process_group.rs +++ b/codex-rs/utils/pty/src/process_group.rs @@ -48,7 +48,7 @@ impl ProcessTree { pub fn kill(&self) -> io::Result<()> { #[cfg(target_os = "linux")] { - return kill_linux_process_tree(self.root_pid, &self.members); + kill_linux_process_tree(self.root_pid, &self.members) } #[cfg(all(unix, not(target_os = "linux")))] @@ -69,7 +69,7 @@ impl ProcessTree { pub fn terminate(&self) -> io::Result { #[cfg(target_os = "linux")] { - return terminate_linux_process_tree(self.root_pid, &self.members); + terminate_linux_process_tree(self.root_pid, &self.members) } #[cfg(all(unix, not(target_os = "linux")))] From 3804bae2708185e76f40d2acd8f8efd36c6bfa2b Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 12:32:28 +0800 Subject: [PATCH 04/15] codex: fix pty Bazel test dependency on PR #62 --- codex-rs/Cargo.lock | 1 + codex-rs/utils/pty/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 4ae2d33c4b4a..0cf015bf2515 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -4246,6 +4246,7 @@ dependencies = [ "portable-pty", "pretty_assertions", "shared_library", + "tempfile", "tokio", "winapi", ] diff --git a/codex-rs/utils/pty/Cargo.toml b/codex-rs/utils/pty/Cargo.toml index f38e8f7a63b8..fec89d4c92ae 100644 --- a/codex-rs/utils/pty/Cargo.toml +++ b/codex-rs/utils/pty/Cargo.toml @@ -14,6 +14,7 @@ tokio = { workspace = true, features = ["io-util", "macros", "process", "rt-mult [dev-dependencies] pretty_assertions = { workspace = true } +tempfile = { workspace = true } [target.'cfg(windows)'.dependencies] filedescriptor = "0.8.3" From 138f3bc725300ef040e293bdf3411919c8858b03 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 12:48:16 +0800 Subject: [PATCH 05/15] codex: fix pty test clippy on PR #62 --- codex-rs/utils/pty/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codex-rs/utils/pty/src/tests.rs b/codex-rs/utils/pty/src/tests.rs index 3339467c72a1..5ede9103dc57 100644 --- a/codex-rs/utils/pty/src/tests.rs +++ b/codex-rs/utils/pty/src/tests.rs @@ -553,7 +553,7 @@ async fn pipe_terminate_kills_descendant_that_started_new_session() -> anyhow::R libc::kill(descendant_pid, libc::SIGKILL); } anyhow::anyhow!("escaped pipe descendant pid {descendant_pid} survived terminate()") - })??; + })???; Ok(()) } @@ -599,7 +599,7 @@ async fn pty_terminate_kills_descendant_that_started_new_session() -> anyhow::Re libc::kill(descendant_pid, libc::SIGKILL); } anyhow::anyhow!("escaped PTY descendant pid {descendant_pid} survived terminate()") - })??; + })???; Ok(()) } From 9569b523ccda65ccebba61995cb491a253487002 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 13:58:35 +0800 Subject: [PATCH 06/15] codex: fix CI runner timeouts on PR #62 --- .github/workflows/bazel.yml | 30 ++++++++++++------------------ .github/workflows/rust-ci.yml | 11 +++++------ .github/workflows/sdk.yml | 12 ++++-------- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index ffda87b8a57b..e4a4bb96f29a 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -23,15 +23,17 @@ jobs: # signal without putting PR latency back on the critical path. When # authenticated RBE is available, the Windows-cross shards exercise the # source-built V8/code-mode targets. - timeout-minutes: 30 + timeout-minutes: 90 strategy: fail-fast: false matrix: include: # macOS - os: macos-15-xlarge + runs_on: macos-15 target: aarch64-apple-darwin - os: macos-15-xlarge + runs_on: macos-15-intel target: x86_64-apple-darwin # Linux @@ -46,7 +48,7 @@ jobs: # - os: ubuntu-24.04-arm # target: aarch64-unknown-linux-gnu - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.runs_on || matrix.os }} # Configure a human readable name for each job name: Bazel test on ${{ matrix.os }} for ${{ matrix.target }} @@ -142,7 +144,7 @@ jobs: # Split the Windows Bazel test leg across separate Windows hosts. Jobs with # BuildBuddy credentials use Linux RBE for build actions; test execution # remains on a Windows runner. - timeout-minutes: 30 + timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -151,9 +153,7 @@ jobs: - 2 - 3 - 4 - runs-on: - group: ${{ github.event.repository.name }}-runners - labels: ${{ github.event.repository.name }}-windows-x64 + runs-on: windows-latest name: Bazel test on windows-latest for x86_64-pc-windows-gnullvm shard ${{ matrix.shard }}/4 environment: name: bazel @@ -265,10 +265,8 @@ jobs: # 30-minute PR budget. Run this only for post-merge commits to main and give # it a larger timeout. if: github.event_name == 'push' && github.ref == 'refs/heads/main' - timeout-minutes: 40 - runs-on: - group: ${{ github.event.repository.name }}-runners - labels: ${{ github.event.repository.name }}-windows-x64 + timeout-minutes: 60 + runs-on: windows-latest name: Bazel test on windows-latest for x86_64-pc-windows-gnullvm (native main) environment: name: bazel @@ -341,7 +339,7 @@ jobs: key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} clippy: - timeout-minutes: 30 + timeout-minutes: 90 strategy: fail-fast: false matrix: @@ -353,12 +351,10 @@ jobs: - os: ubuntu-24.04 target: x86_64-unknown-linux-gnu - os: macos-15-xlarge + runs_on: macos-15 target: aarch64-apple-darwin - os: windows-latest target: x86_64-pc-windows-gnullvm - runs_on: - group: ${{ github.event.repository.name }}-runners - labels: ${{ github.event.repository.name }}-windows-x64 runs-on: ${{ matrix.runs_on || matrix.os }} name: Bazel clippy on ${{ matrix.os }} for ${{ matrix.target }} environment: @@ -441,7 +437,7 @@ jobs: key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} verify-release-build: - timeout-minutes: 30 + timeout-minutes: 90 strategy: fail-fast: false matrix: @@ -449,12 +445,10 @@ jobs: - os: ubuntu-24.04 target: x86_64-unknown-linux-gnu - os: macos-15-xlarge + runs_on: macos-15 target: aarch64-apple-darwin - os: windows-latest target: x86_64-pc-windows-gnullvm - runs_on: - group: ${{ github.event.repository.name }}-runners - labels: ${{ github.event.repository.name }}-windows-x64 runs-on: ${{ matrix.runs_on || matrix.os }} name: Verify release build on ${{ matrix.os }} for ${{ matrix.target }} environment: diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 4cf6d6e37a99..f9644fa8b38f 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -168,16 +168,15 @@ jobs: include: - name: Linux runner: ubuntu-24.04 - timeout_minutes: 30 + timeout_minutes: 90 - name: macOS runner: macos-15-xlarge - timeout_minutes: 30 + runs_on: macos-15 + timeout_minutes: 90 - name: Windows runner: windows-x64 - timeout_minutes: 30 - runs_on: - group: ${{ github.event.repository.name }}-runners - labels: ${{ github.event.repository.name }}-windows-x64 + runs_on: windows-latest + timeout_minutes: 60 steps: - name: Check whether argument comment lint should run id: argument_comment_lint_gate diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 3cf6d7c8c3de..4f0e43f4d4fc 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -7,10 +7,8 @@ on: jobs: python-sdk: - runs-on: - group: ${{ github.event.repository.name }}-runners - labels: ${{ github.event.repository.name }}-linux-x64 - timeout-minutes: 10 + runs-on: ubuntu-24.04 + timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -42,10 +40,8 @@ jobs: ' sdks: - runs-on: - group: ${{ github.event.repository.name }}-runners - labels: ${{ github.event.repository.name }}-linux-x64 - timeout-minutes: 10 + runs-on: ubuntu-24.04 + timeout-minutes: 60 environment: name: bazel deployment: false From 7b06200d249a7df2e14d14793e48de957ab70a4e Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 14:04:17 +0800 Subject: [PATCH 07/15] codex: update macOS SDK archive for CI --- MODULE.bazel | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 559516bd1a81..c64c2dbbfb29 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -29,11 +29,11 @@ register_toolchains("@llvm//toolchain:all") osx = use_extension("@llvm//extensions:osx.bzl", "osx") osx.from_archive( - sha256 = "1bde70c0b1c2ab89ff454acbebf6741390d7b7eb149ca2a3ca24cc9203a408b7", - strip_prefix = "Payload/Library/Developer/CommandLineTools/SDKs/MacOSX26.4.sdk", + sha256 = "5f044578cd78a3a9b9c965a42d56bad609ee5d252e1d4e6aa7c42fc3f35fee7b", + strip_prefix = "Payload/Library/Developer/CommandLineTools/SDKs/MacOSX26.5.sdk", type = "pkg", urls = [ - "https://swcdn.apple.com/content/downloads/32/53/047-96692-A_OAHIHT53YB/ybtshxmrcju8m2qvw3w5elr4rajtg1x3y3/CLTools_macOSNMOS_SDK.pkg", + "https://swcdn.apple.com/content/downloads/09/08/047-91568-A_Y1CFZWQCD4/4xekpyz43i26dbp4enxfro8eb1q7wiujh5/CLTools_macOSNMOS_SDK.pkg", ], ) osx.frameworks(names = [ From dc7fa36baed30936a0fed24946e6ac2b65a46afc Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 14:06:21 +0800 Subject: [PATCH 08/15] codex: fix v8 canary macOS runners --- .github/workflows/v8-canary.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/v8-canary.yml b/.github/workflows/v8-canary.yml index 237ed58aba77..f12f0817263e 100644 --- a/.github/workflows/v8-canary.yml +++ b/.github/workflows/v8-canary.yml @@ -100,7 +100,7 @@ jobs: build: name: Build ${{ matrix.variant }} ${{ matrix.target }} needs: metadata - runs-on: ${{ matrix.runner }} + runs-on: ${{ matrix.runs_on || matrix.runner }} permissions: contents: read actions: read @@ -140,6 +140,7 @@ jobs: v8_cpu: arm64 variant: ptrcomp-sandbox - runner: macos-15-xlarge + runs_on: macos-15-intel bazel_config: ci-macos platform: macos_amd64 sandbox: false @@ -147,6 +148,7 @@ jobs: v8_cpu: x64 variant: release - runner: macos-15-xlarge + runs_on: macos-15-intel bazel_config: ci-macos platform: macos_amd64 sandbox: true @@ -154,6 +156,7 @@ jobs: v8_cpu: x64 variant: ptrcomp-sandbox - runner: macos-15-xlarge + runs_on: macos-15 bazel_config: ci-macos platform: macos_arm64 sandbox: false @@ -161,6 +164,7 @@ jobs: v8_cpu: arm64 variant: release - runner: macos-15-xlarge + runs_on: macos-15 bazel_config: ci-macos platform: macos_arm64 sandbox: true From 68878664af63ca148a595199d851b4d9132783da Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 14:19:49 +0800 Subject: [PATCH 09/15] codex: pin Bazelisk for Windows CI --- .github/actions/setup-bazel-ci/action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/setup-bazel-ci/action.yml b/.github/actions/setup-bazel-ci/action.yml index bb757aab91de..658dca2aa43c 100644 --- a/.github/actions/setup-bazel-ci/action.yml +++ b/.github/actions/setup-bazel-ci/action.yml @@ -34,6 +34,10 @@ runs: - name: Set up Bazel uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0 + # Without an explicit Bazelisk version, setup-bazel leaves PATH unchanged + # and Windows can use the runner's standalone Bazel, ignoring .bazelversion. + with: + bazelisk-version: 1.28.1 - name: Configure Bazel repository cache id: configure_bazel_repository_cache From 68333ab81236fa7b6fa8e674bee3859d4c126684 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 14:47:42 +0800 Subject: [PATCH 10/15] codex: keep webrtc cxxbridge outputs hermetic --- patches/webrtc-sys_hermetic_darwin_sysroot.patch | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/patches/webrtc-sys_hermetic_darwin_sysroot.patch b/patches/webrtc-sys_hermetic_darwin_sysroot.patch index c90edbd08e9a..c846f706ec33 100644 --- a/patches/webrtc-sys_hermetic_darwin_sysroot.patch +++ b/patches/webrtc-sys_hermetic_darwin_sysroot.patch @@ -6,6 +6,15 @@ index 3b4a24a..6aa06ff 100644 -use std::path::Path; +use std::fs; +use std::path::Path; +@@ -60,1 +61,7 @@ +- let mut builder = cxx_build::bridges(rust_files); ++ if env::var_os("WEBRTC_SYS_LINK_OUT_DIR").is_some() ++ && env::var_os("CARGO_TARGET_DIR").is_none() ++ { ++ let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); ++ env::set_var("CARGO_TARGET_DIR", out_dir.join("target")); ++ } ++ let mut builder = cxx_build::bridges(rust_files); @@ -118,1 +119,9 @@ - println!("cargo:rustc-link-search=native={}", webrtc_lib.to_str().unwrap()); + let webrtc_link_lib = if env::var_os("WEBRTC_SYS_LINK_OUT_DIR").is_some() { From f30ba74937a2f3f4391207d4dd0b53f1a84bf23a Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 15:22:02 +0800 Subject: [PATCH 11/15] codex: fix Windows pty clippy --- codex-rs/utils/pty/src/process_group.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/codex-rs/utils/pty/src/process_group.rs b/codex-rs/utils/pty/src/process_group.rs index ad8eb2824ed9..dfcfc10eb3f7 100644 --- a/codex-rs/utils/pty/src/process_group.rs +++ b/codex-rs/utils/pty/src/process_group.rs @@ -27,6 +27,7 @@ use tokio::process::Child; /// Other platforms fall back to the existing process-group cleanup behavior. #[derive(Debug)] pub struct ProcessTree { + #[cfg(unix)] root_pid: u32, #[cfg(target_os = "linux")] members: Vec, @@ -36,7 +37,11 @@ pub struct ProcessTree { impl ProcessTree { pub fn capture(root_pid: u32) -> io::Result { + #[cfg(not(unix))] + let _ = root_pid; + Ok(Self { + #[cfg(unix)] root_pid, #[cfg(target_os = "linux")] members: collect_linux_process_tree(root_pid as libc::pid_t)?, From a5b0a5a4acbabd966d5188d6f3f1921f4b6d5989 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 15:54:00 +0800 Subject: [PATCH 12/15] codex: fix webrtc Bazel scratch dir --- MODULE.bazel | 8 ++++ patches/BUILD.bazel | 1 + .../webrtc-sys-build_bazel_scratch_dir.patch | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 patches/webrtc-sys-build_bazel_scratch_dir.patch diff --git a/MODULE.bazel b/MODULE.bazel index c64c2dbbfb29..ad07122c305e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -386,6 +386,14 @@ crate.annotation( inject_repo(crate, "llvm", "llvm-project", "macos_sdk") +crate.annotation( + crate = "webrtc-sys-build", + patch_args = ["-p1"], + patches = [ + "//patches:webrtc-sys-build_bazel_scratch_dir.patch", + ], +) + crate.annotation( # Provide the hermetic SDK path so the build script doesn't try to invoke an unavailable `xcrun --show-sdk-path`. build_script_data = [ diff --git a/patches/BUILD.bazel b/patches/BUILD.bazel index a431c3f3a61a..a1ce44c9169e 100644 --- a/patches/BUILD.bazel +++ b/patches/BUILD.bazel @@ -23,6 +23,7 @@ exports_files([ "v8_bazel_rules.patch", "v8_module_deps.patch", "v8_source_portability.patch", + "webrtc-sys-build_bazel_scratch_dir.patch", "webrtc-sys_hermetic_darwin_sysroot.patch", "windows-link.patch", "xz_windows_stack_args.patch", diff --git a/patches/webrtc-sys-build_bazel_scratch_dir.patch b/patches/webrtc-sys-build_bazel_scratch_dir.patch new file mode 100644 index 000000000000..d66779316158 --- /dev/null +++ b/patches/webrtc-sys-build_bazel_scratch_dir.patch @@ -0,0 +1,41 @@ +diff --git a/src/lib.rs b/src/lib.rs +index 5382300..f61a9a5 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -83,6 +83,15 @@ pub fn custom_dir() -> Option { + None + } + ++fn scratch_dir() -> path::PathBuf { ++ if env::var_os("WEBRTC_SYS_LINK_OUT_DIR").is_some() { ++ let out_dir = env::var("OUT_DIR").unwrap(); ++ return path::Path::new(&out_dir).join(SCRATH_PATH); ++ } ++ ++ scratch::path(SCRATH_PATH) ++} ++ + /// Location of the downloaded webrtc binaries + /// The reason why we don't use OUT_DIR is because we sometimes need to share the same binaries + /// across multiple crates without dependencies constraints +@@ -90,10 +99,10 @@ fn scratch_dir() -> path::PathBuf { + /// This also has the benefit of not re-downloading the binaries for each crate + pub fn prebuilt_dir() -> path::PathBuf { +- let target_dir = scratch::path(SCRATH_PATH); ++ let target_dir = scratch_dir(); + path::Path::new(&target_dir).join(format!( + "livekit/{}-{}/{}", + webrtc_triple(), + WEBRTC_TAG, + webrtc_triple() + )) + } +@@ -197,7 +206,7 @@ pub fn configure_jni_symbols() -> Result<()> { + } + + pub fn download_webrtc() -> Result<()> { +- let dir = scratch::path(SCRATH_PATH); ++ let dir = scratch_dir(); + // temporary fix to avoid github workflow issue + fs::create_dir_all(&dir).context("Failed to create scratch_path")?; + let flock = File::create(dir.join(".lock")) From fcb5843398a70095d69d8ff05401174adf2302e6 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 16:47:53 +0800 Subject: [PATCH 13/15] codex: fix Windows CI failures on PRs --- codex-rs/core-plugins/src/startup_sync.rs | 2 +- codex-rs/file-search/src/lib.rs | 3 ++- codex-rs/thread-manager-sample/src/main.rs | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/codex-rs/core-plugins/src/startup_sync.rs b/codex-rs/core-plugins/src/startup_sync.rs index 923a70b33891..ac7a9f79fc5f 100644 --- a/codex-rs/core-plugins/src/startup_sync.rs +++ b/codex-rs/core-plugins/src/startup_sync.rs @@ -287,7 +287,7 @@ fn lock_curated_plugins_startup_sync(cache_root: &CuratedPluginCacheRoot) -> Res Ok(lock_file) } -#[cfg(test)] +#[cfg(all(test, unix))] fn sync_openai_plugins_repo_via_git(codex_home: &Path, git_binary: &str) -> Result { let cache_root = CuratedPluginCacheRoot::resolve(codex_home, /*protected_workspace*/ None)?; diff --git a/codex-rs/file-search/src/lib.rs b/codex-rs/file-search/src/lib.rs index 9da0df9fe134..601b40b041ab 100644 --- a/codex-rs/file-search/src/lib.rs +++ b/codex-rs/file-search/src/lib.rs @@ -465,8 +465,9 @@ fn walker_worker( return ignore::WalkState::Continue; }; if let Some((_, relative_path)) = get_file_path(path, &search_directories) { + let matcher_path = relative_path.replace(std::path::MAIN_SEPARATOR, "/"); injector.push(Arc::from(full_path), |_, cols| { - cols[0] = Utf32String::from(relative_path); + cols[0] = Utf32String::from(matcher_path.as_str()); }); } n += 1; diff --git a/codex-rs/thread-manager-sample/src/main.rs b/codex-rs/thread-manager-sample/src/main.rs index 5c8aae8b37e7..120ba9c8e8c6 100644 --- a/codex-rs/thread-manager-sample/src/main.rs +++ b/codex-rs/thread-manager-sample/src/main.rs @@ -270,6 +270,7 @@ fn new_config(model: Option, arg0_paths: Arg0DispatchPaths) -> anyhow::R web_search_mode: Constrained::allow_any(WebSearchMode::Disabled), web_search_config: None, experimental_request_user_input_enabled: true, + experimental_request_user_input_disable_auto_resolution: false, code_mode: Default::default(), use_experimental_unified_exec_tool: false, background_terminal_max_timeout: 300_000, From 9baa6533fc581a584f0571974b3016e6b0096533 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Thu, 25 Jun 2026 17:33:17 +0800 Subject: [PATCH 14/15] codex: fix file watcher Windows CI failure --- codex-rs/file-watcher/src/file_watcher_tests.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/codex-rs/file-watcher/src/file_watcher_tests.rs b/codex-rs/file-watcher/src/file_watcher_tests.rs index 8bd7858c215f..ac4468a072aa 100644 --- a/codex-rs/file-watcher/src/file_watcher_tests.rs +++ b/codex-rs/file-watcher/src/file_watcher_tests.rs @@ -4,6 +4,7 @@ use notify::event::AccessMode; use notify::event::CreateKind; use notify::event::ModifyKind; use pretty_assertions::assert_eq; +use tokio::time::Instant; use tokio::time::timeout; const TEST_THROTTLE_INTERVAL: Duration = Duration::from_millis(50); @@ -36,13 +37,19 @@ async fn throttled_receiver_coalesces_within_interval() { }) ); + let start = Instant::now(); + let recv_task = tokio::spawn(async move { throttled.recv().await }); + tokio::task::yield_now().await; tx.add_changed_paths(&[path("b"), path("c")]).await; - let blocked = timeout(TEST_THROTTLE_INTERVAL / 2, throttled.recv()).await; - assert_eq!(blocked.is_err(), true); - let second = timeout(TEST_THROTTLE_INTERVAL * 2, throttled.recv()) + let second = timeout(TEST_THROTTLE_INTERVAL * 2, recv_task) .await - .expect("second emit timeout"); + .expect("second emit timeout") + .expect("second emit task"); + assert!( + start.elapsed() >= TEST_THROTTLE_INTERVAL / 2, + "second emit was not throttled" + ); assert_eq!( second, Some(FileWatcherEvent { From 8666425eeb1876dfe1ffdec1e8aed189d0b4b6d3 Mon Sep 17 00:00:00 2001 From: MarketingChronoAi Date: Fri, 26 Jun 2026 12:42:41 +0800 Subject: [PATCH 15/15] codex: fix CI failure on PR #62 --- codex-rs/app-server-client/src/lib.rs | 7 ++ codex-rs/app-server/src/in_process.rs | 5 +- .../tests/common/test_app_server.rs | 85 ++++++++++++++++++- .../tests/suite/conversation_summary.rs | 2 + .../app-server/tests/suite/v2/mcp_resource.rs | 2 + .../app-server/tests/suite/v2/plugin_list.rs | 9 +- .../tests/suite/v2/remote_thread_store.rs | 2 + .../app-server/tests/suite/v2/thread_read.rs | 4 + .../tests/suite/v2/thread_unarchive.rs | 2 + codex-rs/core/tests/suite/cli_stream.rs | 3 +- codex-rs/exec/src/lib.rs | 11 +++ codex-rs/tui/src/lib.rs | 2 + codex-rs/tui/src/onboarding/auth.rs | 2 + 13 files changed, 129 insertions(+), 7 deletions(-) diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index fef32574f3d0..353cf636f85d 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -27,6 +27,7 @@ use std::path::Path; use std::sync::Arc; use std::time::Duration; +pub use codex_app_server::PluginStartupTasks; pub use codex_app_server::app_server_control_socket_path; pub use codex_app_server::in_process::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY; pub use codex_app_server::in_process::InProcessServerEvent; @@ -342,6 +343,8 @@ pub struct InProcessClientStartArgs { pub config_warnings: Vec, /// Session source recorded in app-server thread metadata. pub session_source: SessionSource, + /// Whether plugin startup tasks should run for this in-process runtime. + pub plugin_startup_tasks: PluginStartupTasks, /// Whether auth loading should honor the `CODEX_API_KEY` environment variable. pub enable_codex_api_key_env: bool, /// Client name reported during initialize. @@ -403,6 +406,7 @@ impl InProcessClientStartArgs { environment_manager: self.environment_manager, config_warnings: self.config_warnings, session_source: self.session_source, + plugin_startup_tasks: self.plugin_startup_tasks, enable_codex_api_key_env: self.enable_codex_api_key_env, initialize, channel_capacity: self.channel_capacity, @@ -1040,6 +1044,7 @@ mod tests { environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source, + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, client_name: "codex-app-server-client-test".to_string(), client_version: "0.0.0-test".to_string(), @@ -2215,6 +2220,7 @@ mod tests { environment_manager: environment_manager.clone(), config_warnings: Vec::new(), session_source: SessionSource::Exec, + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, client_name: "codex-app-server-client-test".to_string(), client_version: "0.0.0-test".to_string(), @@ -2256,6 +2262,7 @@ mod tests { environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Exec, + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, client_name: "codex-app-server-client-test".to_string(), client_version: "0.0.0-test".to_string(), diff --git a/codex-rs/app-server/src/in_process.rs b/codex-rs/app-server/src/in_process.rs index 27191e721538..ad9f49daffad 100644 --- a/codex-rs/app-server/src/in_process.rs +++ b/codex-rs/app-server/src/in_process.rs @@ -142,6 +142,8 @@ pub struct InProcessStartArgs { pub config_warnings: Vec, /// Session source stamped into thread/session metadata. pub session_source: SessionSource, + /// Whether plugin startup tasks should run for this in-process runtime. + pub plugin_startup_tasks: crate::PluginStartupTasks, /// Whether auth loading should honor the `CODEX_API_KEY` environment variable. pub enable_codex_api_key_env: bool, /// Initialize params used for initial handshake. @@ -439,7 +441,7 @@ async fn start_uninitialized(args: InProcessStartArgs) -> IoResult, stdout: BufReader, pending_messages: VecDeque, + #[allow(dead_code)] + current_dir_guard: Option, } pub const DEFAULT_CLIENT_NAME: &str = "codex-app-server-tests"; pub const DISABLE_PLUGIN_STARTUP_TASKS_ARG: &str = "--disable-plugin-startup-tasks-for-tests"; const DISABLE_MANAGED_CONFIG_ENV_VAR: &str = "CODEX_APP_SERVER_DISABLE_MANAGED_CONFIG"; +struct TempWorkspace { + path: PathBuf, +} + +impl TempWorkspace { + fn new() -> std::io::Result { + let path = std::env::temp_dir().join(format!( + "codex-app-server-test-workspace-{}", + uuid::Uuid::new_v4() + )); + std::fs::create_dir_all(&path)?; + Ok(Self { path }) + } + + fn path(&self) -> &Path { + &self.path + } +} + +impl Drop for TempWorkspace { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.path); + } +} + impl TestAppServer { pub async fn wait_for_exit(&mut self) -> std::io::Result { self.process.wait().await @@ -154,6 +182,21 @@ impl TestAppServer { Self::new_with_env_and_args(codex_home, &[], &[]).await } + pub async fn new_with_temp_workspace(codex_home: &Path) -> anyhow::Result { + Self::new_with_temp_workspace_env_and_args( + codex_home, + &[], + &[DISABLE_PLUGIN_STARTUP_TASKS_ARG], + ) + .await + } + + pub async fn new_with_temp_workspace_and_plugin_startup_tasks( + codex_home: &Path, + ) -> anyhow::Result { + Self::new_with_temp_workspace_env_and_args(codex_home, &[], &[]).await + } + pub async fn new_with_env_and_plugin_startup_tasks( codex_home: &Path, env_overrides: &[(&str, Option<&str>)], @@ -213,13 +256,52 @@ impl TestAppServer { program: &Path, env_overrides: &[(&str, Option<&str>)], args: &[&str], + ) -> anyhow::Result { + Self::new_with_program_env_args_and_current_dir( + codex_home, + program, + env_overrides, + args, + codex_home, + None, + ) + .await + } + + async fn new_with_temp_workspace_env_and_args( + codex_home: &Path, + env_overrides: &[(&str, Option<&str>)], + args: &[&str], + ) -> anyhow::Result { + let program = codex_utils_cargo_bin::cargo_bin("codex-app-server") + .context("should find binary for codex-app-server")?; + let current_dir_guard = TempWorkspace::new().context("should create temp workspace")?; + let current_dir = current_dir_guard.path().to_path_buf(); + Self::new_with_program_env_args_and_current_dir( + codex_home, + &program, + env_overrides, + args, + ¤t_dir, + Some(current_dir_guard), + ) + .await + } + + async fn new_with_program_env_args_and_current_dir( + codex_home: &Path, + program: &Path, + env_overrides: &[(&str, Option<&str>)], + args: &[&str], + current_dir: &Path, + current_dir_guard: Option, ) -> anyhow::Result { let mut cmd = Command::new(program); cmd.stdin(Stdio::piped()); cmd.stdout(Stdio::piped()); cmd.stderr(Stdio::piped()); - cmd.current_dir(codex_home); + cmd.current_dir(current_dir); cmd.env("CODEX_HOME", codex_home); cmd.env("RUST_LOG", "warn"); // Keep integration tests isolated from host managed configuration. @@ -271,6 +353,7 @@ impl TestAppServer { stdin: Some(stdin), stdout, pending_messages: VecDeque::new(), + current_dir_guard, }) } diff --git a/codex-rs/app-server/tests/suite/conversation_summary.rs b/codex-rs/app-server/tests/suite/conversation_summary.rs index 6ad9f1c633a7..bc66499919f4 100644 --- a/codex-rs/app-server/tests/suite/conversation_summary.rs +++ b/codex-rs/app-server/tests/suite/conversation_summary.rs @@ -3,6 +3,7 @@ use app_test_support::TestAppServer; use app_test_support::create_fake_rollout; use app_test_support::rollout_path; use app_test_support::to_response; +use codex_app_server::PluginStartupTasks; use codex_app_server::in_process; use codex_app_server::in_process::InProcessStartArgs; use codex_app_server_protocol::ClientInfo; @@ -159,6 +160,7 @@ async fn get_conversation_summary_by_thread_id_reads_pathless_store_thread() -> environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli, + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, initialize: InitializeParams { client_info: ClientInfo { diff --git a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs index 9e91e3e87243..ad99103ea579 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs @@ -7,6 +7,7 @@ use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use axum::Router; +use codex_app_server::PluginStartupTasks; use codex_app_server::in_process; use codex_app_server::in_process::InProcessStartArgs; use codex_app_server_protocol::ClientInfo; @@ -360,6 +361,7 @@ async fn mcp_resource_read_returns_error_for_unknown_thread() -> Result<()> { environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli, + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, initialize: InitializeParams { client_info: ClientInfo { diff --git a/codex-rs/app-server/tests/suite/v2/plugin_list.rs b/codex-rs/app-server/tests/suite/v2/plugin_list.rs index a49c5d3c75ee..8ad2e1894a68 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_list.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_list.rs @@ -145,7 +145,7 @@ enabled = true "#, )?; - let mut mcp = TestAppServer::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new_with_temp_workspace(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -230,7 +230,7 @@ enabled = true mount_remote_installed_plugins(&server, "WORKSPACE", empty_remote_installed_plugins_body()) .await; - let mut app_server = TestAppServer::new(codex_home.path()).await?; + let mut app_server = TestAppServer::new_with_temp_workspace(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, app_server.initialize()).await??; let request_id = app_server @@ -3074,7 +3074,7 @@ async fn plugin_list_fetches_featured_plugin_ids_without_chatgpt_auth() -> Resul .mount(&server) .await; - let mut mcp = TestAppServer::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new_with_temp_workspace(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -3113,7 +3113,8 @@ async fn plugin_list_uses_warmed_featured_plugin_ids_cache_on_first_request() -> .mount(&server) .await; - let mut mcp = TestAppServer::new_with_plugin_startup_tasks(codex_home.path()).await?; + let mut mcp = + TestAppServer::new_with_temp_workspace_and_plugin_startup_tasks(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; wait_for_featured_plugin_request_count(&server, /*expected_count*/ 1).await?; diff --git a/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs b/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs index 18d1dcd43fd5..1662c0fdbce1 100644 --- a/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs +++ b/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs @@ -19,6 +19,7 @@ use std::sync::Arc; use anyhow::Result; use app_test_support::create_mock_responses_server_repeating_assistant; +use codex_app_server::PluginStartupTasks; use codex_app_server::in_process; use codex_app_server::in_process::InProcessClientHandle; use codex_app_server::in_process::InProcessServerEvent; @@ -305,6 +306,7 @@ async fn start_in_process_client( environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli, + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, initialize: InitializeParams { client_info: ClientInfo { diff --git a/codex-rs/app-server/tests/suite/v2/thread_read.rs b/codex-rs/app-server/tests/suite/v2/thread_read.rs index 272ecac79f8a..12dec34deb1a 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_read.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_read.rs @@ -5,6 +5,7 @@ use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::rollout_path; use app_test_support::test_absolute_path; use app_test_support::to_response; +use codex_app_server::PluginStartupTasks; use codex_app_server::in_process; use codex_app_server::in_process::InProcessStartArgs; use codex_app_server_protocol::ClientInfo; @@ -384,6 +385,7 @@ async fn thread_turns_list_reads_store_history_without_rollout_path() -> Result< environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli.into(), + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, initialize: InitializeParams { client_info: ClientInfo { @@ -450,6 +452,7 @@ async fn thread_read_loaded_include_turns_reads_store_history_without_rollout_pa environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli.into(), + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, initialize: InitializeParams { client_info: ClientInfo { @@ -536,6 +539,7 @@ async fn thread_list_includes_store_thread_without_rollout_path() -> Result<()> environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli.into(), + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, initialize: InitializeParams { client_info: ClientInfo { diff --git a/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs b/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs index 7267ab073ac9..ca4789dee2b6 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs @@ -2,6 +2,7 @@ use anyhow::Result; use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; +use codex_app_server::PluginStartupTasks; use codex_app_server::in_process; use codex_app_server::in_process::InProcessStartArgs; use codex_app_server_protocol::ClientInfo; @@ -256,6 +257,7 @@ async fn thread_unarchive_preserves_pathless_store_metadata() -> Result<()> { environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli, + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, initialize: InitializeParams { client_info: ClientInfo { diff --git a/codex-rs/core/tests/suite/cli_stream.rs b/codex-rs/core/tests/suite/cli_stream.rs index 1ef7c82c39bb..7e850a90f130 100644 --- a/codex-rs/core/tests/suite/cli_stream.rs +++ b/codex-rs/core/tests/suite/cli_stream.rs @@ -124,7 +124,8 @@ fn run_cli_command(command: &mut Command) -> io::Result { command .stdin(Stdio::null()) .stdout(Stdio::piped()) - .stderr(Stdio::piped()); + .stderr(Stdio::piped()) + .env("CODEX_EXEC_DISABLE_PLUGIN_STARTUP_TASKS_FOR_TESTS", "1"); let child = command.spawn()?; let _cleanup = ChildProcessCleanupGuard(child.id()); diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 5dce65d715e2..9021b4313e41 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -19,6 +19,7 @@ use codex_app_server_client::ExecServerRuntimePaths; use codex_app_server_client::InProcessAppServerClient; use codex_app_server_client::InProcessClientStartArgs; use codex_app_server_client::InProcessServerEvent; +use codex_app_server_client::PluginStartupTasks; use codex_app_server_protocol::ClientRequest; use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::JSONRPCErrorError; @@ -545,6 +546,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result environment_manager: std::sync::Arc::new(environment_manager), config_warnings, session_source: SessionSource::Exec, + plugin_startup_tasks: plugin_startup_tasks_for_in_process_exec(), enable_codex_api_key_env: true, client_name: "codex_exec".to_string(), client_version: env!("CARGO_PKG_VERSION").to_string(), @@ -1032,6 +1034,15 @@ async fn run_exec_session(args: ExecRunArgs) -> anyhow::Result<()> { Ok(()) } +fn plugin_startup_tasks_for_in_process_exec() -> PluginStartupTasks { + #[cfg(debug_assertions)] + if std::env::var_os("CODEX_EXEC_DISABLE_PLUGIN_STARTUP_TASKS_FOR_TESTS").is_some() { + return PluginStartupTasks::Skip; + } + + PluginStartupTasks::Start +} + fn thread_start_params_from_config(config: &Config) -> ThreadStartParams { let permissions = permissions_selection_from_config(config); let sandbox = permissions.is_none().then(|| { diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 8c07dfa58db7..0c9120e0f550 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -24,6 +24,7 @@ use codex_app_server_client::AppServerClient; use codex_app_server_client::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY; use codex_app_server_client::InProcessAppServerClient; use codex_app_server_client::InProcessClientStartArgs; +use codex_app_server_client::PluginStartupTasks; use codex_app_server_client::RemoteAppServerClient; use codex_app_server_client::RemoteAppServerConnectArgs; pub use codex_app_server_client::RemoteAppServerEndpoint; @@ -629,6 +630,7 @@ where config_warnings, session_source: serde_json::from_value(serde_json::json!("cli")) .unwrap_or_else(|err| panic!("cli session source should deserialize: {err}")), + plugin_startup_tasks: PluginStartupTasks::Start, enable_codex_api_key_env: false, client_name: "codex-tui".to_string(), client_version: env!("CARGO_PKG_VERSION").to_string(), diff --git a/codex-rs/tui/src/onboarding/auth.rs b/codex-rs/tui/src/onboarding/auth.rs index e10700ed823b..b02a0239636a 100644 --- a/codex-rs/tui/src/onboarding/auth.rs +++ b/codex-rs/tui/src/onboarding/auth.rs @@ -1011,6 +1011,7 @@ mod tests { use codex_app_server_client::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY; use codex_app_server_client::InProcessAppServerClient; use codex_app_server_client::InProcessClientStartArgs; + use codex_app_server_client::PluginStartupTasks; use codex_arg0::Arg0DispatchPaths; use codex_cloud_config::cloud_config_bundle_loader_for_storage; use codex_config::types::AuthCredentialsStoreMode; @@ -1049,6 +1050,7 @@ mod tests { config_warnings: Vec::new(), session_source: serde_json::from_value(serde_json::json!("cli")) .expect("cli session source should deserialize"), + plugin_startup_tasks: PluginStartupTasks::Skip, enable_codex_api_key_env: false, client_name: "test".to_string(), client_version: "test".to_string(),