diff --git a/codex-rs/app-server/src/request_processors/user_instructions.rs b/codex-rs/app-server/src/request_processors/user_instructions.rs index 0696b4ad51..ae9a4dce3c 100644 --- a/codex-rs/app-server/src/request_processors/user_instructions.rs +++ b/codex-rs/app-server/src/request_processors/user_instructions.rs @@ -1,6 +1,7 @@ +use codex_core::AgentsMdManager; use codex_core::ThreadManager; use codex_core::config::Config; -use codex_core::load_thread_user_instructions as preload_thread_user_instructions; +use codex_protocol::error::CodexErr; use codex_protocol::error::Result as CodexResult; use codex_protocol::protocol::TurnEnvironmentSelection; @@ -11,10 +12,24 @@ pub(super) async fn load_thread_user_instructions( config: &mut Config, environments: &[TurnEnvironmentSelection], ) -> CodexResult<()> { - preload_thread_user_instructions( - config, - thread_manager.environment_manager().as_ref(), - environments, - ) - .await + let Some(primary_selection) = environments.first() else { + config.user_instructions = None; + return Ok(()); + }; + let environment = thread_manager + .environment_manager() + .get_environment(&primary_selection.environment_id) + .ok_or_else(|| { + CodexErr::InvalidRequest(format!( + "unknown turn environment id `{}`", + primary_selection.environment_id + )) + })?; + let mut warnings = Vec::new(); + let user_instructions = AgentsMdManager::new(config) + .load_user_instructions(environment.as_ref(), &mut warnings) + .await; + config.startup_warnings.extend(warnings); + config.user_instructions = user_instructions; + Ok(()) } diff --git a/codex-rs/core-api/src/lib.rs b/codex-rs/core-api/src/lib.rs index cd7cf2a1c3..4951f1286e 100644 --- a/codex-rs/core-api/src/lib.rs +++ b/codex-rs/core-api/src/lib.rs @@ -42,7 +42,6 @@ pub use codex_core::config::TerminalResizeReflowConfig; pub use codex_core::config::ThreadStoreConfig; pub use codex_core::config::find_codex_home; pub use codex_core::init_state_db; -pub use codex_core::load_thread_user_instructions; pub use codex_core::resolve_installation_id; pub use codex_core::skills::SkillsManager; pub use codex_core::thread_store_from_config; @@ -78,4 +77,36 @@ pub use codex_protocol::protocol::SessionSource; pub use codex_protocol::protocol::TurnEnvironmentSelection; pub use codex_protocol::protocol::W3cTraceContext; pub use codex_protocol::user_input::UserInput; + +/// Preloads AGENTS.md instructions for thread construction from the primary +/// selected environment. +/// +/// Call this after the selected environments have been materialized and before +/// passing `config` to a thread start, resume, or fork operation. When no +/// environment is selected, this clears [`Config::user_instructions`]. +pub async fn load_thread_user_instructions( + config: &mut Config, + environment_manager: &EnvironmentManager, + environments: &[TurnEnvironmentSelection], +) -> CodexResult<()> { + let Some(primary_selection) = environments.first() else { + config.user_instructions = None; + return Ok(()); + }; + let environment = environment_manager + .get_environment(&primary_selection.environment_id) + .ok_or_else(|| { + codex_protocol::error::CodexErr::InvalidRequest(format!( + "unknown turn environment id `{}`", + primary_selection.environment_id + )) + })?; + let mut warnings = Vec::new(); + let user_instructions = codex_core::AgentsMdManager::new(config) + .load_user_instructions(environment.as_ref(), &mut warnings) + .await; + config.startup_warnings.extend(warnings); + config.user_instructions = user_instructions; + Ok(()) +} pub use codex_utils_absolute_path::AbsolutePathBuf; diff --git a/codex-rs/core/src/agents_md.rs b/codex-rs/core/src/agents_md.rs index ba7b59c328..0cba5b4f17 100644 --- a/codex-rs/core/src/agents_md.rs +++ b/codex-rs/core/src/agents_md.rs @@ -22,13 +22,9 @@ use codex_config::default_project_root_markers; use codex_config::merge_toml_values; use codex_config::project_root_markers_from_config; use codex_exec_server::Environment; -use codex_exec_server::EnvironmentManager; use codex_exec_server::ExecutorFileSystem; use codex_features::Feature; use codex_prompts::HIERARCHICAL_AGENTS_MESSAGE; -use codex_protocol::error::CodexErr; -use codex_protocol::error::Result as CodexResult; -use codex_protocol::protocol::TurnEnvironmentSelection; use codex_utils_absolute_path::AbsolutePathBuf; use std::io; use toml::Value as TomlValue; @@ -43,38 +39,6 @@ pub const LOCAL_AGENTS_MD_FILENAME: &str = "AGENTS.override.md"; /// concatenated with the following separator. const AGENTS_MD_SEPARATOR: &str = "\n\n--- project-doc ---\n\n"; -/// Preloads AGENTS.md instructions for thread construction from the primary -/// selected environment. -/// -/// Call this after the selected environments have been materialized and before -/// passing `config` to a thread start, resume, or fork operation. When no -/// environment is selected, this clears [`Config::user_instructions`]. -pub async fn load_thread_user_instructions( - config: &mut Config, - environment_manager: &EnvironmentManager, - environments: &[TurnEnvironmentSelection], -) -> CodexResult<()> { - let Some(primary_selection) = environments.first() else { - config.user_instructions = None; - return Ok(()); - }; - let environment = environment_manager - .get_environment(&primary_selection.environment_id) - .ok_or_else(|| { - CodexErr::InvalidRequest(format!( - "unknown turn environment id `{}`", - primary_selection.environment_id - )) - })?; - let mut warnings = Vec::new(); - let user_instructions = AgentsMdManager::new(config) - .load_user_instructions(environment.as_ref(), &mut warnings) - .await; - config.startup_warnings.extend(warnings); - config.user_instructions = user_instructions; - Ok(()) -} - /// Resolves AGENTS.md files into model-visible user instructions and source /// paths. pub struct AgentsMdManager<'a> { diff --git a/codex-rs/core/src/agents_md_tests.rs b/codex-rs/core/src/agents_md_tests.rs index 477302e8ee..f7b313c4a1 100644 --- a/codex-rs/core/src/agents_md_tests.rs +++ b/codex-rs/core/src/agents_md_tests.rs @@ -1,9 +1,7 @@ use super::*; use crate::config::ConfigBuilder; -use codex_exec_server::EnvironmentManager; use codex_exec_server::LOCAL_FS; use codex_features::Feature; -use codex_protocol::protocol::TurnEnvironmentSelection; use codex_utils_absolute_path::AbsolutePathBuf; use core_test_support::PathBufExt; use core_test_support::TempDirExt; @@ -400,34 +398,6 @@ async fn resolving_again_replaces_project_instructions() { ); } -#[tokio::test] -async fn load_thread_user_instructions_uses_primary_environment() { - let tmp = tempfile::tempdir().expect("tempdir"); - fs::write(tmp.path().join("AGENTS.md"), "project instructions").unwrap(); - let mut config = make_config(&tmp, /*limit*/ 4096, Some("user instructions")).await; - let environment_manager = EnvironmentManager::default_for_tests(); - let environment_id = environment_manager - .default_environment_id() - .expect("default environment") - .to_string(); - let environments = vec![TurnEnvironmentSelection { - environment_id, - cwd: config.cwd.clone(), - }]; - - load_thread_user_instructions(&mut config, &environment_manager, &environments) - .await - .expect("load thread instructions"); - - assert_eq!( - config - .user_instructions - .expect("instructions expected") - .text(), - format!("user instructions{AGENTS_MD_SEPARATOR}project instructions") - ); -} - /// When both the repository root and the working directory contain /// AGENTS.md files, their contents are concatenated from root to cwd. #[tokio::test] diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index 28ae2b62b0..4dc19de7e6 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -131,7 +131,6 @@ pub use agents_md::AgentsMdManager; pub use agents_md::DEFAULT_AGENTS_MD_FILENAME; pub use agents_md::LOCAL_AGENTS_MD_FILENAME; pub use agents_md::LoadedAgentsMd; -pub use agents_md::load_thread_user_instructions; mod rollout; pub(crate) mod safety; mod session_rollout_init_error; diff --git a/codex-rs/memories/write/src/runtime.rs b/codex-rs/memories/write/src/runtime.rs index 3b72a3454e..0c5534c1a2 100644 --- a/codex-rs/memories/write/src/runtime.rs +++ b/codex-rs/memories/write/src/runtime.rs @@ -1,3 +1,4 @@ +use codex_core::AgentsMdManager; use codex_core::CodexThread; use codex_core::ModelClient; use codex_core::NewThread; @@ -7,7 +8,6 @@ use codex_core::StartThreadOptions; use codex_core::ThreadManager; use codex_core::config::Config; use codex_core::content_items_to_text; -use codex_core::load_thread_user_instructions; use codex_core::resolve_installation_id; use codex_features::Feature; use codex_login::AuthManager; @@ -237,12 +237,20 @@ impl MemoryStartupContext { let environments = self .thread_manager .default_environment_selections(&config.cwd); - load_thread_user_instructions( - &mut config, - self.thread_manager.environment_manager().as_ref(), - &environments, - ) - .await?; + let mut warnings = Vec::new(); + config.user_instructions = match environments.first().and_then(|selection| { + self.thread_manager + .environment_manager() + .get_environment(&selection.environment_id) + }) { + Some(environment) => { + AgentsMdManager::new(&config) + .load_user_instructions(environment.as_ref(), &mut warnings) + .await + } + None => None, + }; + config.startup_warnings.extend(warnings); let NewThread { thread_id, thread, .. } = self