Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions codex-rs/core/src/context/multi_agent_mode_instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ pub(crate) struct MultiAgentModeInstructions {
}

impl MultiAgentModeInstructions {
pub(crate) fn new(multi_agent_mode: MultiAgentMode) -> Self {
Self { multi_agent_mode }
pub(crate) fn from_mode(multi_agent_mode: MultiAgentMode) -> Option<Self> {
if matches!(
&multi_agent_mode,
MultiAgentMode::Custom(hint_text) if hint_text.is_empty()
) {
return None;
}

Some(Self { multi_agent_mode })
}
}

Expand Down
6 changes: 4 additions & 2 deletions codex-rs/core/src/context_manager/updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,11 @@ fn build_multi_agent_mode_update_item(
}

match effective_multi_agent_mode {
Some(multi_agent_mode) => Some(MultiAgentModeInstructions::new(multi_agent_mode).render()),
Some(multi_agent_mode) => MultiAgentModeInstructions::from_mode(multi_agent_mode)
.map(|instructions| instructions.render()),
None if previous.multi_agent_mode == Some(MultiAgentMode::Proactive) => {
Some(MultiAgentModeInstructions::new(MultiAgentMode::ExplicitRequestOnly).render())
MultiAgentModeInstructions::from_mode(MultiAgentMode::ExplicitRequestOnly)
.map(|instructions| instructions.render())
}
None => None,
}
Expand Down
8 changes: 4 additions & 4 deletions codex-rs/core/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3471,10 +3471,10 @@ impl Session {
{
items.push(usage_hint_message);
}
if let Some(multi_agent_mode) = multi_agents::effective_multi_agent_mode(turn_context) {
items.push(ContextualUserFragment::into(
MultiAgentModeInstructions::new(multi_agent_mode),
));
if let Some(multi_agent_mode) = multi_agents::effective_multi_agent_mode(turn_context)
&& let Some(instructions) = MultiAgentModeInstructions::from_mode(multi_agent_mode)
{
items.push(ContextualUserFragment::into(instructions));
}
if let Some(contextual_user_message) =
crate::context_manager::updates::build_contextual_user_message(contextual_user_sections)
Expand Down
59 changes: 57 additions & 2 deletions codex-rs/core/tests/suite/multi_agent_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ async fn configured_mode_hint_uses_custom_mode_across_reasoning_efforts() -> Res
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn empty_configured_mode_hint_suppresses_builtin_text() -> Result<()> {
async fn empty_configured_mode_hint_emits_no_mode_message() -> Result<()> {
skip_if_no_network!(Ok(()));

let server = start_mock_server().await;
Expand All @@ -220,7 +220,62 @@ async fn empty_configured_mode_hint_suppresses_builtin_text() -> Result<()> {
count_containing(&texts, NO_SPAWN_TEXT),
count_containing(&texts, PROACTIVE_TEXT),
),
(1, 0, 0)
(0, 0, 0)
);

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn changing_configured_mode_hint_to_empty_emits_no_update() -> Result<()> {
skip_if_no_network!(Ok(()));

let server = start_mock_server().await;
let responses = mount_sse_sequence(
&server,
(1..=2)
.map(|index| {
sse(vec![
ev_response_created(&format!("resp-{index}")),
ev_completed(&format!("resp-{index}")),
])
})
.collect(),
)
.await;
let initial = test_codex()
.with_config(configure_custom_mode_hint)
.build(&server)
.await?;
let home = initial.home.clone();
let rollout_path = initial
.session_configured
.rollout_path
.clone()
.expect("rollout path");

submit_turn(&initial.codex, "before resume", /*effort*/ None).await?;
drop(initial);

let mut resume_builder = test_codex().with_config(|config| {
configure_multi_agent_v2(config);
config.multi_agent_v2.multi_agent_mode_hint_text = Some(String::new());
});
let resumed = resume_builder.resume(&server, home, rollout_path).await?;
submit_turn(&resumed.codex, "after resume", /*effort*/ None).await?;

let requests = responses.requests();
let first_input = requests[0].input();
let first_texts = developer_texts(&first_input);
let resumed_input = requests[1].input();
let resumed_texts = developer_texts(&resumed_input);
assert_eq!(
(
count_containing(&first_texts, MULTI_AGENT_MODE_OPEN_TAG),
count_containing(&resumed_texts, MULTI_AGENT_MODE_OPEN_TAG),
count_containing(&resumed_texts, CUSTOM_MODE_HINT_TEXT),
),
(1, 1, 1)
);

Ok(())
Expand Down
Loading