Move instruction preload helper out of core

This commit is contained in:
Adam Perry
2026-06-04 21:03:14 -07:00
parent 5975615ab4
commit ffed2cae1c
6 changed files with 69 additions and 82 deletions

View File

@@ -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(())
}

View File

@@ -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;

View File

@@ -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> {

View File

@@ -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]

View File

@@ -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;

View File

@@ -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