Merge remote-tracking branch 'origin/main' into jif/error-on-image-origin

# Conflicts:
#	codex-rs/core/src/tools/handlers/view_image.rs
#	codex-rs/core/tests/suite/view_image.rs
This commit is contained in:
jif-oai
2026-01-12 09:03:12 +00:00
127 changed files with 4094 additions and 778 deletions
+7
View File
@@ -0,0 +1,7 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "common",
crate_name = "core_test_support",
crate_srcs = glob(["*.rs"]),
)
@@ -0,0 +1,71 @@
use codex_core::features::Feature;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses::mount_sse_once;
use core_test_support::responses::start_mock_server;
use core_test_support::test_codex::test_codex;
const HIERARCHICAL_AGENTS_SNIPPET: &str =
"Files called AGENTS.md commonly appear in many places inside a container";
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn hierarchical_agents_appends_to_project_doc_in_user_instructions() {
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let mut builder = test_codex().with_config(|config| {
config.features.enable(Feature::HierarchicalAgents);
std::fs::write(config.cwd.join("AGENTS.md"), "be nice").expect("write AGENTS.md");
});
let test = builder.build(&server).await.expect("build test codex");
test.submit_turn("hello").await.expect("submit turn");
let request = resp_mock.single_request();
let user_messages = request.message_input_texts("user");
let instructions = user_messages
.iter()
.find(|text| text.starts_with("# AGENTS.md instructions for "))
.expect("instructions message");
assert!(
instructions.contains("be nice"),
"expected AGENTS.md text included: {instructions}"
);
let snippet_pos = instructions
.find(HIERARCHICAL_AGENTS_SNIPPET)
.expect("expected hierarchical agents snippet");
let base_pos = instructions
.find("be nice")
.expect("expected AGENTS.md text");
assert!(
snippet_pos > base_pos,
"expected hierarchical agents message appended after base instructions: {instructions}"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn hierarchical_agents_emits_when_no_project_doc() {
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let mut builder = test_codex().with_config(|config| {
config.features.enable(Feature::HierarchicalAgents);
});
let test = builder.build(&server).await.expect("build test codex");
test.submit_turn("hello").await.expect("submit turn");
let request = resp_mock.single_request();
let user_messages = request.message_input_texts("user");
let instructions = user_messages
.iter()
.find(|text| text.starts_with("# AGENTS.md instructions for "))
.expect("instructions message");
assert!(
instructions.contains(HIERARCHICAL_AGENTS_SNIPPET),
"expected hierarchical agents message appended: {instructions}"
);
}
+1
View File
@@ -30,6 +30,7 @@ mod exec;
mod exec_policy;
mod fork_thread;
mod grep_files;
mod hierarchical_agents;
mod items;
mod json_result;
mod list_dir;
+39 -77
View File
@@ -4,9 +4,7 @@ use std::sync::Arc;
use anyhow::Result;
use codex_core::CodexAuth;
use codex_core::CodexThread;
use codex_core::ModelProviderInfo;
use codex_core::ThreadManager;
use codex_core::built_in_model_providers;
use codex_core::config::Config;
use codex_core::error::CodexErr;
@@ -39,6 +37,8 @@ use core_test_support::responses::mount_sse_sequence;
use core_test_support::responses::sse;
use core_test_support::skip_if_no_network;
use core_test_support::skip_if_sandbox;
use core_test_support::test_codex::TestCodex;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
use core_test_support::wait_for_event_match;
use pretty_assertions::assert_eq;
@@ -98,19 +98,19 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
)
.await;
let harness = build_remote_models_harness(&server, |config| {
config.features.enable(Feature::RemoteModels);
config.model = Some("gpt-5.1".to_string());
})
.await?;
let RemoteModelsHarness {
let mut builder = test_codex()
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_config(|config| {
config.features.enable(Feature::RemoteModels);
config.model = Some("gpt-5.1".to_string());
});
let TestCodex {
codex,
cwd,
config,
thread_manager,
..
} = harness;
} = builder.build(&server).await?;
let models_manager = thread_manager.get_models_manager();
let available_model =
@@ -214,16 +214,19 @@ async fn remote_models_truncation_policy_without_override_preserves_remote() ->
)
.await;
let harness = build_remote_models_harness(&server, |config| {
config.model = Some("gpt-5.1".to_string());
})
.await?;
let mut builder = test_codex()
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_config(|config| {
config.features.enable(Feature::RemoteModels);
config.model = Some("gpt-5.1".to_string());
});
let test = builder.build(&server).await?;
let models_manager = harness.thread_manager.get_models_manager();
wait_for_model_available(&models_manager, slug, &harness.config).await;
let models_manager = test.thread_manager.get_models_manager();
wait_for_model_available(&models_manager, slug, &test.config).await;
let model_info = models_manager
.construct_model_info(slug, &harness.config)
.construct_model_info(slug, &test.config)
.await;
assert_eq!(
model_info.truncation_policy,
@@ -258,17 +261,20 @@ async fn remote_models_truncation_policy_with_tool_output_override() -> Result<(
)
.await;
let harness = build_remote_models_harness(&server, |config| {
config.model = Some("gpt-5.1".to_string());
config.tool_output_token_limit = Some(50);
})
.await?;
let mut builder = test_codex()
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_config(|config| {
config.features.enable(Feature::RemoteModels);
config.model = Some("gpt-5.1".to_string());
config.tool_output_token_limit = Some(50);
});
let test = builder.build(&server).await?;
let models_manager = harness.thread_manager.get_models_manager();
wait_for_model_available(&models_manager, slug, &harness.config).await;
let models_manager = test.thread_manager.get_models_manager();
wait_for_model_available(&models_manager, slug, &test.config).await;
let model_info = models_manager
.construct_model_info(slug, &harness.config)
.construct_model_info(slug, &test.config)
.await;
assert_eq!(
model_info.truncation_policy,
@@ -335,19 +341,19 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
)
.await;
let harness = build_remote_models_harness(&server, |config| {
config.features.enable(Feature::RemoteModels);
config.model = Some("gpt-5.1".to_string());
})
.await?;
let RemoteModelsHarness {
let mut builder = test_codex()
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_config(|config| {
config.features.enable(Feature::RemoteModels);
config.model = Some("gpt-5.1".to_string());
});
let TestCodex {
codex,
cwd,
config,
thread_manager,
..
} = harness;
} = builder.build(&server).await?;
let models_manager = thread_manager.get_models_manager();
wait_for_model_available(&models_manager, model, &config).await;
@@ -577,50 +583,6 @@ async fn wait_for_model_available(
}
}
struct RemoteModelsHarness {
codex: Arc<CodexThread>,
cwd: Arc<TempDir>,
config: Config,
thread_manager: Arc<ThreadManager>,
}
// todo(aibrahim): move this to with_model_provier in test_codex
async fn build_remote_models_harness<F>(
server: &MockServer,
mutate_config: F,
) -> Result<RemoteModelsHarness>
where
F: FnOnce(&mut Config),
{
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
let home = Arc::new(TempDir::new()?);
let cwd = Arc::new(TempDir::new()?);
let mut config = load_default_config_for_test(&home).await;
config.cwd = cwd.path().to_path_buf();
config.features.enable(Feature::RemoteModels);
let provider = ModelProviderInfo {
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
config.model_provider = provider.clone();
mutate_config(&mut config);
let thread_manager = ThreadManager::with_models_provider(auth, provider);
let thread_manager = Arc::new(thread_manager);
let new_conversation = thread_manager.start_thread(config.clone()).await?;
Ok(RemoteModelsHarness {
codex: new_conversation.thread,
cwd,
config,
thread_manager,
})
}
fn test_remote_model(slug: &str, visibility: ModelVisibility, priority: i32) -> ModelInfo {
test_remote_model_with_policy(
slug,