mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Load user instructions through an injected provider (#27101)
## Why We want to remove implicit use of `$CODEX_HOME` from `codex-core` and make embedders responsible for supplying user-level instructions. This also ensures user instructions load when no primary environment is selected. ## What changed Stacked on #27415, which makes `codex exec` surface thread-scoped runtime warnings. - Added `UserInstructionsProvider` to `codex-extension-api`, with absolute source attribution and recoverable loading warnings. - Added `codex-home` with the filesystem-backed provider for `AGENTS.override.md` and `AGENTS.md`, preserving precedence, fallback, trimming, lossy UTF-8 handling, and the existing uncapped global instruction size. - Removed global instruction loading from `Config` and require `ThreadManager` callers to inject a provider. - Load provider instructions once for each fresh root runtime, including runtimes without a primary environment. Running sessions retain their snapshot, while child agents inherit the parent snapshot without invoking the provider. - Keep provider instructions separate while loading project `AGENTS.md`, then assemble the model-visible instructions with the existing ordering, source attribution, warning, and turn-context behavior. - Wired the Codex home provider through the CLI, app server, MCP server, core facade, and thread-manager sample. ## Validation - `just test -p codex-home -p codex-extension-api` - `just test -p codex-core agents_md` - `just test -p codex-core guardian` - `just test -p codex-app-server thread_start_without_selected_environment_includes_only_global_instruction_source` - `just test -p codex-exec warning` - `just bazel-lock-check`
This commit is contained in:
@@ -6,6 +6,7 @@ use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -24,7 +25,10 @@ use codex_exec_server::CreateDirectoryOptions;
|
||||
use codex_exec_server::ExecutorFileSystem;
|
||||
use codex_exec_server::RemoveOptions;
|
||||
use codex_extension_api::ExtensionRegistry;
|
||||
use codex_extension_api::LoadUserInstructionsFuture;
|
||||
use codex_extension_api::UserInstructionsProvider;
|
||||
use codex_extension_api::empty_extension_registry;
|
||||
use codex_home::CodexHomeUserInstructionsProvider;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_model_provider_info::built_in_model_providers;
|
||||
@@ -72,6 +76,31 @@ const REMOTE_EXEC_SERVER_URL_ENV_VAR: &str = "CODEX_TEST_REMOTE_EXEC_SERVER_URL"
|
||||
static REMOTE_TEST_INSTANCE_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
const SUBMIT_TURN_COMPLETE_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
pub struct RecordingUserInstructionsProvider {
|
||||
inner: Arc<dyn UserInstructionsProvider>,
|
||||
load_count: AtomicUsize,
|
||||
}
|
||||
|
||||
impl RecordingUserInstructionsProvider {
|
||||
pub fn new(inner: Arc<dyn UserInstructionsProvider>) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
load_count: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_count(&self) -> usize {
|
||||
self.load_count.load(Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
impl UserInstructionsProvider for RecordingUserInstructionsProvider {
|
||||
fn load_user_instructions(&self) -> LoadUserInstructionsFuture<'_> {
|
||||
self.load_count.fetch_add(1, Ordering::SeqCst);
|
||||
self.inner.load_user_instructions()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local(cwd: AbsolutePathBuf) -> TurnEnvironmentSelection {
|
||||
TurnEnvironmentSelection {
|
||||
environment_id: codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
@@ -233,6 +262,7 @@ pub struct TestCodexBuilder {
|
||||
user_shell_override: Option<Shell>,
|
||||
exec_server_url: Option<String>,
|
||||
extensions: Arc<ExtensionRegistry<Config>>,
|
||||
user_instructions_provider: Option<Arc<dyn UserInstructionsProvider>>,
|
||||
}
|
||||
|
||||
impl TestCodexBuilder {
|
||||
@@ -322,6 +352,14 @@ impl TestCodexBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_user_instructions_provider(
|
||||
mut self,
|
||||
provider: Arc<dyn UserInstructionsProvider>,
|
||||
) -> Self {
|
||||
self.user_instructions_provider = Some(provider);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_windows_cmd_shell(self) -> Self {
|
||||
if cfg!(windows) {
|
||||
self.with_user_shell(get_shell_by_model_provided_path(&PathBuf::from("cmd.exe")))
|
||||
@@ -510,12 +548,19 @@ impl TestCodexBuilder {
|
||||
let state_db = codex_core::init_state_db(&config).await;
|
||||
let thread_store = thread_store_from_config(&config, state_db.clone());
|
||||
let installation_id = resolve_installation_id(&config.codex_home).await?;
|
||||
let user_instructions_provider =
|
||||
self.user_instructions_provider.clone().unwrap_or_else(|| {
|
||||
Arc::new(CodexHomeUserInstructionsProvider::new(
|
||||
config.codex_home.clone(),
|
||||
))
|
||||
});
|
||||
let thread_manager = ThreadManager::new(
|
||||
&config,
|
||||
codex_core::test_support::auth_manager_from_auth(auth.clone()),
|
||||
SessionSource::Exec,
|
||||
Arc::clone(&environment_manager),
|
||||
Arc::clone(&self.extensions),
|
||||
user_instructions_provider,
|
||||
/*analytics_events_client*/ None,
|
||||
thread_store,
|
||||
state_db.clone(),
|
||||
@@ -1103,6 +1148,7 @@ pub fn test_codex() -> TestCodexBuilder {
|
||||
user_shell_override: None,
|
||||
exec_server_url: None,
|
||||
extensions: empty_extension_registry(),
|
||||
user_instructions_provider: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user