diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index a2633475d..e2203d9f4 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -33,11 +33,14 @@ pub struct Prompt { /// the "fully qualified" tool name (i.e., prefixed with the server name), /// which should be reported to the model in place of Tool::name. pub extra_tools: HashMap, + + /// Allow agents to override the prompt instructions + pub agent_instructions: Option, } impl Prompt { pub(crate) fn get_full_instructions(&self, model: &str) -> Cow { - let mut sections: Vec<&str> = vec![BASE_INSTRUCTIONS]; + let mut sections: Vec<&str> = vec![self.agent_instructions.as_ref().map_or(BASE_INSTRUCTIONS, |z| z.as_str())]; if let Some(ref user) = self.user_instructions { sections.push(user); } diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 2837dd032..97de32968 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -116,6 +116,7 @@ impl Codex { disable_response_storage: config.disable_response_storage, notify: config.notify.clone(), cwd: config.cwd.clone(), + agent_instructions: config.agent_instructions.clone(), }; let config = Arc::new(config); @@ -191,6 +192,9 @@ pub(crate) struct Session { rollout: Mutex>, state: Mutex, codex_linux_sandbox_exe: Option, + + /// This session's current agent instructions. + agent_instructions: Option, } impl Session { @@ -564,6 +568,7 @@ async fn submission_loop( disable_response_storage, notify, cwd, + agent_instructions, } => { info!("Configuring session: model={model}; provider={provider:?}"); if !cwd.is_absolute() { @@ -667,6 +672,7 @@ async fn submission_loop( state: Mutex::new(state), rollout: Mutex::new(rollout_recorder), codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(), + agent_instructions, })); // Gather history metadata for SessionConfiguredEvent. @@ -1010,6 +1016,7 @@ async fn run_turn( user_instructions: sess.instructions.clone(), store, extra_tools, + agent_instructions: sess.agent_instructions.clone(), }; let mut retries = 0; diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index 74798129b..b7e1e05ce 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -122,6 +122,10 @@ pub struct Config { /// If not "none", the value to use for `reasoning.summary` when making a /// request using the Responses API. pub model_reasoning_summary: ReasoningSummary, + + /// Agent Instructions defines how the agent should function. + /// This defaults to the prompt.md contents located within codex-rs. + pub agent_instructions: Option, } impl Config { @@ -332,6 +336,10 @@ pub struct ConfigOverrides { pub model_provider: Option, pub config_profile: Option, pub codex_linux_sandbox_exe: Option, + + /// Default agent instructions to use when configuring codex. + /// When None this will be set to the default agent instructions + pub agent_instructions: Option, } impl Config { @@ -353,6 +361,7 @@ impl Config { model_provider, config_profile: config_profile_key, codex_linux_sandbox_exe, + agent_instructions } = overrides; let config_profile = match config_profile_key.or(cfg.profile) { @@ -459,6 +468,7 @@ impl Config { hide_agent_reasoning: cfg.hide_agent_reasoning.unwrap_or(false), model_reasoning_effort: cfg.model_reasoning_effort.unwrap_or_default(), model_reasoning_summary: cfg.model_reasoning_summary.unwrap_or_default(), + agent_instructions, }; Ok(config) } @@ -803,6 +813,7 @@ disable_response_storage = true hide_agent_reasoning: false, model_reasoning_effort: ReasoningEffort::default(), model_reasoning_summary: ReasoningSummary::default(), + agent_instructions: None, }, o3_profile_config ); @@ -845,6 +856,7 @@ disable_response_storage = true hide_agent_reasoning: false, model_reasoning_effort: ReasoningEffort::default(), model_reasoning_summary: ReasoningSummary::default(), + agent_instructions: None, }; assert_eq!(expected_gpt3_profile_config, gpt3_profile_config); @@ -902,6 +914,7 @@ disable_response_storage = true hide_agent_reasoning: false, model_reasoning_effort: ReasoningEffort::default(), model_reasoning_summary: ReasoningSummary::default(), + agent_instructions: None, }; assert_eq!(expected_zdr_profile_config, zdr_profile_config); diff --git a/codex-rs/core/src/protocol.rs b/codex-rs/core/src/protocol.rs index 737acc773..97f4ac1db 100644 --- a/codex-rs/core/src/protocol.rs +++ b/codex-rs/core/src/protocol.rs @@ -68,6 +68,9 @@ pub enum Op { /// `ConfigureSession` operation so that the business-logic layer can /// operate deterministically. cwd: std::path::PathBuf, + + /// defines the system instructions for the codex agent + agent_instructions: Option, }, /// Abort current task. diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 925e25d67..0e7afb504 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -101,6 +101,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> any cwd: cwd.map(|p| p.canonicalize().unwrap_or(p)), model_provider: None, codex_linux_sandbox_exe, + agent_instructions: None, }; // Parse `-c` overrides. let cli_kv_overrides = match config_overrides.parse_overrides() { diff --git a/codex-rs/mcp-server/src/codex_tool_config.rs b/codex-rs/mcp-server/src/codex_tool_config.rs index 03e723444..17738bab7 100644 --- a/codex-rs/mcp-server/src/codex_tool_config.rs +++ b/codex-rs/mcp-server/src/codex_tool_config.rs @@ -169,6 +169,7 @@ impl CodexToolCallParam { sandbox_policy, model_provider: None, codex_linux_sandbox_exe, + agent_instructions: None, }; let cli_overrides = cli_overrides diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 5f3e2d69b..ebea2fd38 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -66,6 +66,7 @@ pub fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> std::io:: model_provider: None, config_profile: cli.config_profile.clone(), codex_linux_sandbox_exe, + agent_instructions: None, }; // Parse `-c` overrides from the CLI. let cli_kv_overrides = match cli.config_overrides.parse_overrides() {