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:
@@ -14,6 +14,7 @@ use crate::agent::AgentControl;
|
||||
use crate::agent::AgentStatus;
|
||||
use crate::agent::agent_status_from_event;
|
||||
use crate::agent::status::is_final;
|
||||
use crate::agents_md::LoadedAgentsMd;
|
||||
use crate::attestation::AttestationProvider;
|
||||
use crate::build_available_skills;
|
||||
use crate::compact;
|
||||
@@ -54,6 +55,7 @@ use codex_exec_server::Environment;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::FileSystemSandboxContext;
|
||||
use codex_extension_api::ExtensionDataInit;
|
||||
use codex_extension_api::LoadedUserInstructions;
|
||||
use codex_extension_api::PromptSlot;
|
||||
use codex_features::FEATURES;
|
||||
use codex_features::Feature;
|
||||
@@ -291,7 +293,7 @@ use crate::SkillLoadOutcome;
|
||||
#[cfg(test)]
|
||||
use crate::SkillMetadata;
|
||||
use crate::SkillsManager;
|
||||
use crate::agents_md::AgentsMdManager;
|
||||
use crate::agents_md::load_project_instructions;
|
||||
use crate::context::UserInstructions;
|
||||
use crate::exec_policy::ExecPolicyUpdateError;
|
||||
use crate::guardian::GuardianReviewSessionManager;
|
||||
@@ -399,6 +401,7 @@ pub struct CodexSpawnOk {
|
||||
|
||||
pub(crate) struct CodexSpawnArgs {
|
||||
pub(crate) config: Config,
|
||||
pub(crate) user_instructions: LoadedUserInstructions,
|
||||
pub(crate) installation_id: String,
|
||||
pub(crate) auth_manager: Arc<AuthManager>,
|
||||
pub(crate) models_manager: SharedModelsManager,
|
||||
@@ -484,6 +487,7 @@ impl Codex {
|
||||
async fn spawn_internal(args: CodexSpawnArgs) -> CodexResult<CodexSpawnOk> {
|
||||
let CodexSpawnArgs {
|
||||
mut config,
|
||||
user_instructions,
|
||||
installation_id,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
@@ -515,16 +519,21 @@ impl Codex {
|
||||
let (tx_sub, rx_sub) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
|
||||
let (tx_event, rx_event) = async_channel::unbounded();
|
||||
|
||||
let LoadedUserInstructions {
|
||||
instructions: user_instructions,
|
||||
warnings: user_instruction_provider_warnings,
|
||||
} = user_instructions;
|
||||
// TODO(anp) pull startup_warnings out of Config
|
||||
config
|
||||
.startup_warnings
|
||||
.extend(user_instruction_provider_warnings);
|
||||
// TODO(anp) assemble instructions from multiple environments
|
||||
let primary_environment = environment_selections.primary_environment();
|
||||
let mut user_instruction_warnings = Vec::new();
|
||||
let user_instructions = if let Some(primary_environment) = primary_environment {
|
||||
AgentsMdManager::new(&config)
|
||||
.user_instructions(primary_environment.as_ref(), &mut user_instruction_warnings)
|
||||
.await
|
||||
} else {
|
||||
None
|
||||
};
|
||||
config.startup_warnings.extend(user_instruction_warnings);
|
||||
let primary_fs = primary_environment
|
||||
.as_ref()
|
||||
.map(|environment| environment.get_filesystem());
|
||||
let loaded_agents_md =
|
||||
load_project_instructions(&mut config, user_instructions, primary_fs.as_deref()).await;
|
||||
|
||||
let exec_policy = if crate::guardian::is_guardian_reviewer_source(&session_source) {
|
||||
// Guardian review should rely on the built-in shell safety checks,
|
||||
@@ -604,7 +613,7 @@ impl Codex {
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
service_tier,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
user_instructions,
|
||||
loaded_agents_md,
|
||||
personality: config.personality,
|
||||
base_instructions,
|
||||
compact_prompt: config.compact_prompt.clone(),
|
||||
@@ -818,7 +827,7 @@ impl Codex {
|
||||
let state = self.session.state.lock().await;
|
||||
state
|
||||
.session_configuration
|
||||
.user_instructions
|
||||
.loaded_agents_md
|
||||
.as_ref()
|
||||
.map_or_else(Vec::new, |instructions| {
|
||||
instructions.sources().cloned().collect()
|
||||
@@ -1507,6 +1516,16 @@ impl Session {
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub(crate) async fn user_instructions(&self) -> Option<codex_extension_api::UserInstructions> {
|
||||
let state = self.state.lock().await;
|
||||
state
|
||||
.session_configuration
|
||||
.loaded_agents_md
|
||||
.as_ref()
|
||||
.and_then(LoadedAgentsMd::user_instructions)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub(crate) async fn provider(&self) -> ModelProviderInfo {
|
||||
let state = self.state.lock().await;
|
||||
state.session_configuration.provider.clone()
|
||||
|
||||
Reference in New Issue
Block a user