@@ -128,6 +128,9 @@ pub struct Config {
128128 /// Base instructions override.
129129 pub base_instructions : Option < String > ,
130130
131+ /// Compact prompt override.
132+ pub compact_prompt : Option < String > ,
133+
131134 /// Optional external notifier command. When set, Codex will spawn this
132135 /// program after each completed *turn* (i.e. when the agent finishes
133136 /// processing a user submission). The value must be the full command
@@ -540,6 +543,8 @@ pub struct ConfigToml {
540543
541544 /// System instructions.
542545 pub instructions : Option < String > ,
546+ /// Compact prompt used for history compaction.
547+ pub compact_prompt : Option < String > ,
543548
544549 /// When set, restricts ChatGPT login to a specific workspace identifier.
545550 #[ serde( default ) ]
@@ -644,6 +649,7 @@ pub struct ConfigToml {
644649
645650 /// Legacy, now use features
646651 pub experimental_instructions_file : Option < PathBuf > ,
652+ pub experimental_compact_prompt_file : Option < PathBuf > ,
647653 pub experimental_use_exec_command_tool : Option < bool > ,
648654 pub experimental_use_unified_exec_tool : Option < bool > ,
649655 pub experimental_use_rmcp_client : Option < bool > ,
@@ -824,6 +830,7 @@ pub struct ConfigOverrides {
824830 pub config_profile : Option < String > ,
825831 pub codex_linux_sandbox_exe : Option < PathBuf > ,
826832 pub base_instructions : Option < String > ,
833+ pub compact_prompt : Option < String > ,
827834 pub include_apply_patch_tool : Option < bool > ,
828835 pub include_view_image_tool : Option < bool > ,
829836 pub show_raw_agent_reasoning : Option < bool > ,
@@ -854,6 +861,7 @@ impl Config {
854861 config_profile : config_profile_key,
855862 codex_linux_sandbox_exe,
856863 base_instructions,
864+ compact_prompt,
857865 include_apply_patch_tool : include_apply_patch_tool_override,
858866 include_view_image_tool : include_view_image_tool_override,
859867 show_raw_agent_reasoning,
@@ -1030,17 +1038,40 @@ impl Config {
10301038 . and_then ( |info| info. auto_compact_token_limit )
10311039 } ) ;
10321040
1041+ let compact_prompt = compact_prompt. or ( cfg. compact_prompt ) . and_then ( |value| {
1042+ let trimmed = value. trim ( ) ;
1043+ if trimmed. is_empty ( ) {
1044+ None
1045+ } else {
1046+ Some ( trimmed. to_string ( ) )
1047+ }
1048+ } ) ;
1049+
10331050 // Load base instructions override from a file if specified. If the
10341051 // path is relative, resolve it against the effective cwd so the
10351052 // behaviour matches other path-like config values.
10361053 let experimental_instructions_path = config_profile
10371054 . experimental_instructions_file
10381055 . as_ref ( )
10391056 . or ( cfg. experimental_instructions_file . as_ref ( ) ) ;
1040- let file_base_instructions =
1041- Self :: get_base_instructions ( experimental_instructions_path, & resolved_cwd) ?;
1057+ let file_base_instructions = Self :: load_override_from_file (
1058+ experimental_instructions_path,
1059+ & resolved_cwd,
1060+ "experimental instructions file" ,
1061+ ) ?;
10421062 let base_instructions = base_instructions. or ( file_base_instructions) ;
10431063
1064+ let experimental_compact_prompt_path = config_profile
1065+ . experimental_compact_prompt_file
1066+ . as_ref ( )
1067+ . or ( cfg. experimental_compact_prompt_file . as_ref ( ) ) ;
1068+ let file_compact_prompt = Self :: load_override_from_file (
1069+ experimental_compact_prompt_path,
1070+ & resolved_cwd,
1071+ "experimental compact prompt file" ,
1072+ ) ?;
1073+ let compact_prompt = compact_prompt. or ( file_compact_prompt) ;
1074+
10441075 // Default review model when not set in config; allow CLI override to take precedence.
10451076 let review_model = override_review_model
10461077 . or ( cfg. review_model )
@@ -1064,6 +1095,7 @@ impl Config {
10641095 notify : cfg. notify ,
10651096 user_instructions,
10661097 base_instructions,
1098+ compact_prompt,
10671099 // The config.toml omits "_mode" because it's a config file. However, "_mode"
10681100 // is important in code to differentiate the mode from the store implementation.
10691101 cli_auth_credentials_store_mode : cfg. cli_auth_credentials_store . unwrap_or_default ( ) ,
@@ -1160,18 +1192,15 @@ impl Config {
11601192 None
11611193 }
11621194
1163- fn get_base_instructions (
1195+ fn load_override_from_file (
11641196 path : Option < & PathBuf > ,
11651197 cwd : & Path ,
1198+ description : & str ,
11661199 ) -> std:: io:: Result < Option < String > > {
1167- let p = match path. as_ref ( ) {
1168- None => return Ok ( None ) ,
1169- Some ( p) => p,
1200+ let Some ( p) = path else {
1201+ return Ok ( None ) ;
11701202 } ;
11711203
1172- // Resolve relative paths against the provided cwd to make CLI
1173- // overrides consistent regardless of where the process was launched
1174- // from.
11751204 let full_path = if p. is_relative ( ) {
11761205 cwd. join ( p)
11771206 } else {
@@ -1181,21 +1210,15 @@ impl Config {
11811210 let contents = std:: fs:: read_to_string ( & full_path) . map_err ( |e| {
11821211 std:: io:: Error :: new (
11831212 e. kind ( ) ,
1184- format ! (
1185- "failed to read experimental instructions file {}: {e}" ,
1186- full_path. display( )
1187- ) ,
1213+ format ! ( "failed to read {description} {}: {e}" , full_path. display( ) ) ,
11881214 )
11891215 } ) ?;
11901216
11911217 let s = contents. trim ( ) . to_string ( ) ;
11921218 if s. is_empty ( ) {
11931219 Err ( std:: io:: Error :: new (
11941220 std:: io:: ErrorKind :: InvalidData ,
1195- format ! (
1196- "experimental instructions file is empty: {}" ,
1197- full_path. display( )
1198- ) ,
1221+ format ! ( "{description} is empty: {}" , full_path. display( ) ) ,
11991222 ) )
12001223 } else {
12011224 Ok ( Some ( s) )
@@ -2653,6 +2676,61 @@ model = "gpt-5-codex"
26532676 }
26542677 }
26552678
2679+ #[ test]
2680+ fn cli_override_sets_compact_prompt ( ) -> std:: io:: Result < ( ) > {
2681+ let codex_home = TempDir :: new ( ) ?;
2682+ let overrides = ConfigOverrides {
2683+ compact_prompt : Some ( "Use the compact override" . to_string ( ) ) ,
2684+ ..Default :: default ( )
2685+ } ;
2686+
2687+ let config = Config :: load_from_base_config_with_overrides (
2688+ ConfigToml :: default ( ) ,
2689+ overrides,
2690+ codex_home. path ( ) . to_path_buf ( ) ,
2691+ ) ?;
2692+
2693+ assert_eq ! (
2694+ config. compact_prompt. as_deref( ) ,
2695+ Some ( "Use the compact override" )
2696+ ) ;
2697+
2698+ Ok ( ( ) )
2699+ }
2700+
2701+ #[ test]
2702+ fn loads_compact_prompt_from_file ( ) -> std:: io:: Result < ( ) > {
2703+ let codex_home = TempDir :: new ( ) ?;
2704+ let workspace = codex_home. path ( ) . join ( "workspace" ) ;
2705+ std:: fs:: create_dir_all ( & workspace) ?;
2706+
2707+ let prompt_path = workspace. join ( "compact_prompt.txt" ) ;
2708+ std:: fs:: write ( & prompt_path, " summarize differently " ) ?;
2709+
2710+ let cfg = ConfigToml {
2711+ experimental_compact_prompt_file : Some ( PathBuf :: from ( "compact_prompt.txt" ) ) ,
2712+ ..Default :: default ( )
2713+ } ;
2714+
2715+ let overrides = ConfigOverrides {
2716+ cwd : Some ( workspace) ,
2717+ ..Default :: default ( )
2718+ } ;
2719+
2720+ let config = Config :: load_from_base_config_with_overrides (
2721+ cfg,
2722+ overrides,
2723+ codex_home. path ( ) . to_path_buf ( ) ,
2724+ ) ?;
2725+
2726+ assert_eq ! (
2727+ config. compact_prompt. as_deref( ) ,
2728+ Some ( "summarize differently" )
2729+ ) ;
2730+
2731+ Ok ( ( ) )
2732+ }
2733+
26562734 fn create_test_fixture ( ) -> std:: io:: Result < PrecedenceTestFixture > {
26572735 let toml = r#"
26582736model = "o3"
@@ -2808,6 +2886,7 @@ model_verbosity = "high"
28082886 model_verbosity: None ,
28092887 chatgpt_base_url: "https://chatgpt.com/backend-api/" . to_string( ) ,
28102888 base_instructions: None ,
2889+ compact_prompt: None ,
28112890 forced_chatgpt_workspace_id: None ,
28122891 forced_login_method: None ,
28132892 include_apply_patch_tool: false ,
@@ -2879,6 +2958,7 @@ model_verbosity = "high"
28792958 model_verbosity : None ,
28802959 chatgpt_base_url : "https://chatgpt.com/backend-api/" . to_string ( ) ,
28812960 base_instructions : None ,
2961+ compact_prompt : None ,
28822962 forced_chatgpt_workspace_id : None ,
28832963 forced_login_method : None ,
28842964 include_apply_patch_tool : false ,
@@ -2965,6 +3045,7 @@ model_verbosity = "high"
29653045 model_verbosity : None ,
29663046 chatgpt_base_url : "https://chatgpt.com/backend-api/" . to_string ( ) ,
29673047 base_instructions : None ,
3048+ compact_prompt : None ,
29683049 forced_chatgpt_workspace_id : None ,
29693050 forced_login_method : None ,
29703051 include_apply_patch_tool : false ,
@@ -3037,6 +3118,7 @@ model_verbosity = "high"
30373118 model_verbosity : Some ( Verbosity :: High ) ,
30383119 chatgpt_base_url : "https://chatgpt.com/backend-api/" . to_string ( ) ,
30393120 base_instructions : None ,
3121+ compact_prompt : None ,
30403122 forced_chatgpt_workspace_id : None ,
30413123 forced_login_method : None ,
30423124 include_apply_patch_tool : false ,
0 commit comments