From 8a461765f3d84a6ce828f7be43d2d53879a563f4 Mon Sep 17 00:00:00 2001 From: Dylan Hurd Date: Sat, 31 Jan 2026 17:11:32 -0700 Subject: [PATCH] chore(core) Default to friendly personality (#10305) ## Summary Update default personality to friendly ## Testing - [x] Unit tests pass --- .../tests/suite/v2/thread_resume.rs | 2 +- .../app-server/tests/suite/v2/turn_start.rs | 4 +- codex-rs/core/src/config/mod.rs | 7 ++- codex-rs/core/tests/suite/personality.rs | 46 ++++++++++--------- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/codex-rs/app-server/tests/suite/v2/thread_resume.rs b/codex-rs/app-server/tests/suite/v2/thread_resume.rs index 6ad0a14b30..efa51573cb 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_resume.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_resume.rs @@ -402,7 +402,7 @@ async fn thread_resume_accepts_personality_override() -> Result<()> { .send_thread_resume_request(ThreadResumeParams { thread_id: thread.id.clone(), model: Some("gpt-5.2-codex".to_string()), - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), ..Default::default() }) .await?; diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 99ad1ba839..1b53a5cfef 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -451,7 +451,7 @@ async fn turn_start_accepts_personality_override_v2() -> Result<()> { text: "Hello".to_string(), text_elements: Vec::new(), }], - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), ..Default::default() }) .await?; @@ -556,7 +556,7 @@ async fn turn_start_change_personality_mid_thread_v2() -> Result<()> { text: "Hello again".to_string(), text_elements: Vec::new(), }], - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), ..Default::default() }) .await?; diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 6af91109dc..23d7ef96e5 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1499,7 +1499,12 @@ impl Config { let developer_instructions = developer_instructions.or(cfg.developer_instructions); let model_personality = model_personality .or(config_profile.model_personality) - .or(cfg.model_personality); + .or(cfg.model_personality) + .or_else(|| { + features + .enabled(Feature::Personality) + .then_some(Personality::Friendly) + }); let experimental_compact_prompt_path = config_profile .experimental_compact_prompt_file diff --git a/codex-rs/core/tests/suite/personality.rs b/codex-rs/core/tests/suite/personality.rs index 79e0d36c68..e4fe3371a0 100644 --- a/codex-rs/core/tests/suite/personality.rs +++ b/codex-rs/core/tests/suite/personality.rs @@ -39,6 +39,7 @@ use wiremock::MockServer; const LOCAL_FRIENDLY_TEMPLATE: &str = "You optimize for team morale and being a supportive teammate as much as code quality."; +const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective software engineer."; fn sse_completed(id: &str) -> String { sse(vec![ev_response_created(id), ev_completed(id)]) @@ -223,7 +224,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> effort: None, summary: None, collaboration_mode: None, - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), }) .await?; @@ -264,8 +265,8 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> "expected personality update preamble, got {personality_text:?}" ); assert!( - personality_text.contains(LOCAL_FRIENDLY_TEMPLATE), - "expected personality update to include the local friendly template, got: {personality_text:?}" + personality_text.contains(LOCAL_PRAGMATIC_TEMPLATE), + "expected personality update to include the local pragmatic template, got: {personality_text:?}" ); Ok(()) @@ -335,7 +336,7 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()> effort: None, summary: None, collaboration_mode: None, - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), }) .await?; @@ -403,9 +404,7 @@ async fn ignores_remote_model_personality_if_remote_models_disabled() -> anyhow: upgrade: None, base_instructions: "base instructions".to_string(), model_messages: Some(ModelMessages { - instructions_template: Some( - "Base instructions\n{{ personality_message }}\n".to_string(), - ), + instructions_template: Some("Base instructions\n{{ personality }}\n".to_string()), instructions_variables: Some(ModelInstructionsVariables { personality_default: None, personality_friendly: Some(remote_personality_message.to_string()), @@ -485,7 +484,7 @@ async fn ignores_remote_model_personality_if_remote_models_disabled() -> anyhow: "expected instructions to include the local friendly personality template, got: {instructions_text:?}" ); assert!( - !instructions_text.contains("{{ personality_message }}"), + !instructions_text.contains("{{ personality }}"), "expected legacy personality placeholder to be replaced, got: {instructions_text:?}" ); @@ -493,7 +492,7 @@ async fn ignores_remote_model_personality_if_remote_models_disabled() -> anyhow: } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn remote_model_default_personality_instructions_with_feature() -> anyhow::Result<()> { +async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = MockServer::builder() @@ -503,6 +502,7 @@ async fn remote_model_default_personality_instructions_with_feature() -> anyhow: let remote_slug = "codex-remote-default-personality"; let default_personality_message = "Default from remote template"; + let friendly_personality_message = "Friendly variant"; let remote_model = ModelInfo { slug: remote_slug.to_string(), display_name: "Remote default personality test".to_string(), @@ -522,7 +522,7 @@ async fn remote_model_default_personality_instructions_with_feature() -> anyhow: instructions_template: Some("Base instructions\n{{ personality }}\n".to_string()), instructions_variables: Some(ModelInstructionsVariables { personality_default: Some(default_personality_message.to_string()), - personality_friendly: Some("Friendly variant".to_string()), + personality_friendly: Some(friendly_personality_message.to_string()), personality_pragmatic: Some("Pragmatic variant".to_string()), }), }), @@ -554,6 +554,7 @@ async fn remote_model_default_personality_instructions_with_feature() -> anyhow: config.features.enable(Feature::RemoteModels); config.features.enable(Feature::Personality); config.model = Some(remote_slug.to_string()); + config.model_personality = Some(Personality::Friendly); }); let test = builder.build(&server).await?; @@ -578,7 +579,7 @@ async fn remote_model_default_personality_instructions_with_feature() -> anyhow: effort: test.config.model_reasoning_effort, summary: ReasoningSummary::Auto, collaboration_mode: None, - personality: None, + personality: Some(Personality::Friendly), }) .await?; @@ -588,8 +589,12 @@ async fn remote_model_default_personality_instructions_with_feature() -> anyhow: let instructions_text = request.instructions_text(); assert!( - instructions_text.contains(default_personality_message), - "expected instructions to include the remote default personality template, got: {instructions_text:?}" + instructions_text.contains(friendly_personality_message), + "expected instructions to include the remote friendly personality template, got: {instructions_text:?}" + ); + assert!( + !instructions_text.contains(default_personality_message), + "expected instructions to skip the remote default personality template, got: {instructions_text:?}" ); Ok(()) @@ -606,7 +611,8 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - .await; let remote_slug = "codex-remote-personality"; - let remote_personality_message = "Friendly from remote template"; + let remote_friendly_message = "Friendly from remote template"; + let remote_pragmatic_message = "Pragmatic from remote template"; let remote_model = ModelInfo { slug: remote_slug.to_string(), display_name: "Remote personality test".to_string(), @@ -623,13 +629,11 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - upgrade: None, base_instructions: "base instructions".to_string(), model_messages: Some(ModelMessages { - instructions_template: Some( - "Base instructions\n{{ personality_message }}\n".to_string(), - ), + instructions_template: Some("Base instructions\n{{ personality }}\n".to_string()), instructions_variables: Some(ModelInstructionsVariables { personality_default: None, - personality_friendly: Some(remote_personality_message.to_string()), - personality_pragmatic: None, + personality_friendly: Some(remote_friendly_message.to_string()), + personality_pragmatic: Some(remote_pragmatic_message.to_string()), }), }), supports_reasoning_summaries: false, @@ -704,7 +708,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - effort: None, summary: None, collaboration_mode: None, - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), }) .await?; @@ -744,7 +748,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - "expected personality update preamble, got {personality_text:?}" ); assert!( - personality_text.contains(remote_personality_message), + personality_text.contains(remote_pragmatic_message), "expected personality update to include remote template, got: {personality_text:?}" );