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:
committed by
GitHub
Unverified
parent
b2a4e3be27
commit
236b50125d
@@ -23,6 +23,8 @@ use codex_core_plugins::PluginsManager;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_extension_api::ExtensionDataInit;
|
||||
use codex_extension_api::ExtensionRegistry;
|
||||
use codex_extension_api::LoadedUserInstructions;
|
||||
use codex_extension_api::UserInstructionsProvider;
|
||||
use codex_extension_api::empty_extension_registry;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
@@ -209,6 +211,7 @@ pub(crate) struct ThreadManagerState {
|
||||
plugins_manager: Arc<PluginsManager>,
|
||||
mcp_manager: Arc<McpManager>,
|
||||
extensions: Arc<ExtensionRegistry<Config>>,
|
||||
user_instructions_provider: Arc<dyn UserInstructionsProvider>,
|
||||
thread_store: Arc<dyn ThreadStore>,
|
||||
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||
session_source: SessionSource,
|
||||
@@ -259,6 +262,7 @@ impl ThreadManager {
|
||||
session_source: SessionSource,
|
||||
environment_manager: Arc<EnvironmentManager>,
|
||||
extensions: Arc<ExtensionRegistry<Config>>,
|
||||
user_instructions_provider: Arc<dyn UserInstructionsProvider>,
|
||||
analytics_events_client: Option<AnalyticsEventsClient>,
|
||||
thread_store: Arc<dyn ThreadStore>,
|
||||
state_db: Option<StateDbHandle>,
|
||||
@@ -292,6 +296,7 @@ impl ThreadManager {
|
||||
plugins_manager,
|
||||
mcp_manager,
|
||||
extensions,
|
||||
user_instructions_provider,
|
||||
thread_store,
|
||||
attestation_provider,
|
||||
auth_manager,
|
||||
@@ -394,6 +399,9 @@ impl ThreadManager {
|
||||
plugins_manager,
|
||||
mcp_manager,
|
||||
extensions: empty_extension_registry(),
|
||||
user_instructions_provider: Arc::new(
|
||||
crate::test_support::EmptyUserInstructionsProvider,
|
||||
),
|
||||
thread_store,
|
||||
attestation_provider: None,
|
||||
auth_manager,
|
||||
@@ -1091,6 +1099,56 @@ impl ThreadManagerState {
|
||||
resolve_multi_agent_version(initial_history, inherited_multi_agent_version)
|
||||
}
|
||||
|
||||
/// Resolves the provider snapshot for a newly spawned runtime.
|
||||
///
|
||||
/// Loads a fresh provider snapshot for:
|
||||
/// - fresh root threads;
|
||||
/// - cold resumes;
|
||||
/// - root forks.
|
||||
///
|
||||
/// Uses an existing snapshot for:
|
||||
/// - subagents, which inherit from their parent without invoking the
|
||||
/// provider;
|
||||
/// - running resumes and compaction paths, which retain the live session.
|
||||
///
|
||||
/// Provider warnings only apply to fresh loads. If a parent runtime is no
|
||||
/// longer available, its child starts without provider instructions rather
|
||||
/// than loading independently.
|
||||
async fn user_instructions_for_spawn(
|
||||
&self,
|
||||
session_source: &SessionSource,
|
||||
parent_thread_id: Option<ThreadId>,
|
||||
forked_from_thread_id: Option<ThreadId>,
|
||||
) -> LoadedUserInstructions {
|
||||
let is_root_agent = !session_source.is_non_root_agent();
|
||||
if is_root_agent {
|
||||
return self
|
||||
.user_instructions_provider
|
||||
.load_user_instructions()
|
||||
.await;
|
||||
}
|
||||
|
||||
let inherited_thread_id = match session_source {
|
||||
SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
parent_thread_id, ..
|
||||
}) => Some(*parent_thread_id),
|
||||
_ => parent_thread_id.or(forked_from_thread_id),
|
||||
};
|
||||
let instructions = match inherited_thread_id {
|
||||
// The spawn path retains only thread IDs, so look up the live
|
||||
// runtime again here to inherit its user instructions.
|
||||
Some(thread_id) => match self.get_thread(thread_id).await {
|
||||
Ok(thread) => thread.codex.session.user_instructions().await,
|
||||
Err(_) => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
LoadedUserInstructions {
|
||||
instructions,
|
||||
warnings: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a new thread with no history using a provided config.
|
||||
pub(crate) async fn spawn_new_thread(
|
||||
&self,
|
||||
@@ -1308,6 +1366,9 @@ impl ThreadManagerState {
|
||||
}
|
||||
let environment_selections =
|
||||
resolve_environment_selections(self.environment_manager.as_ref(), &environments)?;
|
||||
let user_instructions = self
|
||||
.user_instructions_for_spawn(&session_source, parent_thread_id, forked_from_thread_id)
|
||||
.await;
|
||||
let parent_rollout_thread_trace = self
|
||||
.parent_rollout_thread_trace_for_source(&session_source, &initial_history)
|
||||
.await;
|
||||
@@ -1324,6 +1385,7 @@ impl ThreadManagerState {
|
||||
codex, thread_id, ..
|
||||
} = Box::pin(Codex::spawn(CodexSpawnArgs {
|
||||
config,
|
||||
user_instructions,
|
||||
installation_id: self.installation_id.clone(),
|
||||
auth_manager,
|
||||
models_manager: Arc::clone(&self.models_manager),
|
||||
|
||||
Reference in New Issue
Block a user