mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
change collaboration mode to struct (#9793)
Shouldn't cause behavioral change
This commit is contained in:
@@ -17,6 +17,7 @@ use codex_app_server_protocol::JSONRPCResponse;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_core::models_manager::test_builtin_collaboration_mode_presets;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
use tokio::time::timeout;
|
||||
@@ -57,7 +58,7 @@ fn plan_preset() -> CollaborationMode {
|
||||
let presets = test_builtin_collaboration_mode_presets();
|
||||
presets
|
||||
.into_iter()
|
||||
.find(|p| matches!(p, CollaborationMode::Plan(_)))
|
||||
.find(|p| p.mode == ModeKind::Plan)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ fn pair_programming_preset() -> CollaborationMode {
|
||||
let presets = test_builtin_collaboration_mode_presets();
|
||||
presets
|
||||
.into_iter()
|
||||
.find(|p| matches!(p, CollaborationMode::PairProgramming(_)))
|
||||
.find(|p| p.mode == ModeKind::PairProgramming)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -81,6 +82,6 @@ fn execute_preset() -> CollaborationMode {
|
||||
let presets = test_builtin_collaboration_mode_presets();
|
||||
presets
|
||||
.into_iter()
|
||||
.find(|p| matches!(p, CollaborationMode::Execute(_)))
|
||||
.find(|p| p.mode == ModeKind::Execute)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use codex_app_server_protocol::TurnStartParams;
|
||||
use codex_app_server_protocol::TurnStartResponse;
|
||||
use codex_app_server_protocol::UserInput as V2UserInput;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use tokio::time::timeout;
|
||||
@@ -54,11 +55,14 @@ async fn request_user_input_round_trip() -> Result<()> {
|
||||
}],
|
||||
model: Some("mock-model".to_string()),
|
||||
effort: Some(ReasoningEffort::Medium),
|
||||
collaboration_mode: Some(CollaborationMode::Plan(Settings {
|
||||
model: "mock-model".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::Medium),
|
||||
developer_instructions: None,
|
||||
})),
|
||||
collaboration_mode: Some(CollaborationMode {
|
||||
mode: ModeKind::Plan,
|
||||
settings: Settings {
|
||||
model: "mock-model".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::Medium),
|
||||
developer_instructions: None,
|
||||
},
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -38,6 +38,7 @@ use codex_core::features::FEATURES;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::protocol_config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
@@ -363,11 +364,14 @@ async fn turn_start_accepts_collaboration_mode_override_v2() -> Result<()> {
|
||||
.await??;
|
||||
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(thread_resp)?;
|
||||
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model: "mock-model-collab".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: "mock-model-collab".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
|
||||
let turn_req = mcp
|
||||
.send_turn_start_request(TurnStartParams {
|
||||
|
||||
@@ -35,6 +35,7 @@ use async_channel::Receiver;
|
||||
use async_channel::Sender;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::items::TurnItem;
|
||||
@@ -295,11 +296,14 @@ impl Codex {
|
||||
|
||||
// TODO (aibrahim): Consolidate config.model and config.model_reasoning_effort into config.collaboration_mode
|
||||
// to avoid extracting these fields separately and constructing CollaborationMode here.
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model: model.clone(),
|
||||
reasoning_effort: config.model_reasoning_effort,
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: model.clone(),
|
||||
reasoning_effort: config.model_reasoning_effort,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
let session_configuration = SessionConfiguration {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
@@ -2215,6 +2219,7 @@ mod handlers {
|
||||
|
||||
use crate::context_manager::is_user_turn_boundary;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use codex_rmcp_client::ElicitationAction;
|
||||
@@ -2291,11 +2296,14 @@ mod handlers {
|
||||
personality,
|
||||
} => {
|
||||
let collaboration_mode = collaboration_mode.or_else(|| {
|
||||
Some(CollaborationMode::Custom(Settings {
|
||||
model: model.clone(),
|
||||
reasoning_effort: effort,
|
||||
developer_instructions: None,
|
||||
}))
|
||||
Some(CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: model.clone(),
|
||||
reasoning_effort: effort,
|
||||
developer_instructions: None,
|
||||
},
|
||||
})
|
||||
});
|
||||
(
|
||||
items,
|
||||
@@ -3878,11 +3886,14 @@ mod tests {
|
||||
let model = ModelsManager::get_model_offline(config.model.as_deref());
|
||||
let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config);
|
||||
let reasoning_effort = config.model_reasoning_effort;
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
let session_configuration = SessionConfiguration {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
@@ -3954,11 +3965,14 @@ mod tests {
|
||||
let model = ModelsManager::get_model_offline(config.model.as_deref());
|
||||
let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config);
|
||||
let reasoning_effort = config.model_reasoning_effort;
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
let session_configuration = SessionConfiguration {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
@@ -4214,11 +4228,14 @@ mod tests {
|
||||
let model = ModelsManager::get_model_offline(config.model.as_deref());
|
||||
let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config);
|
||||
let reasoning_effort = config.model_reasoning_effort;
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
let session_configuration = SessionConfiguration {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
@@ -4319,11 +4336,14 @@ mod tests {
|
||||
let model = ModelsManager::get_model_offline(config.model.as_deref());
|
||||
let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config);
|
||||
let reasoning_effort = config.model_reasoning_effort;
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model,
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
let session_configuration = SessionConfiguration {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
|
||||
@@ -18,25 +19,34 @@ pub fn test_builtin_collaboration_mode_presets() -> Vec<CollaborationMode> {
|
||||
}
|
||||
|
||||
fn plan_preset() -> CollaborationMode {
|
||||
CollaborationMode::Plan(Settings {
|
||||
model: "gpt-5.2-codex".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: Some(COLLABORATION_MODE_PLAN.to_string()),
|
||||
})
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Plan,
|
||||
settings: Settings {
|
||||
model: "gpt-5.2-codex".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: Some(COLLABORATION_MODE_PLAN.to_string()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pair_programming_preset() -> CollaborationMode {
|
||||
CollaborationMode::PairProgramming(Settings {
|
||||
model: "gpt-5.2-codex".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::Medium),
|
||||
developer_instructions: Some(COLLABORATION_MODE_PAIR_PROGRAMMING.to_string()),
|
||||
})
|
||||
CollaborationMode {
|
||||
mode: ModeKind::PairProgramming,
|
||||
settings: Settings {
|
||||
model: "gpt-5.2-codex".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::Medium),
|
||||
developer_instructions: Some(COLLABORATION_MODE_PAIR_PROGRAMMING.to_string()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_preset() -> CollaborationMode {
|
||||
CollaborationMode::Execute(Settings {
|
||||
model: "gpt-5.2-codex".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: Some(COLLABORATION_MODE_EXECUTE.to_string()),
|
||||
})
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Execute,
|
||||
settings: Settings {
|
||||
model: "gpt-5.2-codex".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: Some(COLLABORATION_MODE_EXECUTE.to_string()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::request_user_input::RequestUserInputArgs;
|
||||
|
||||
pub struct RequestUserInputHandler;
|
||||
@@ -36,9 +36,9 @@ impl ToolHandler for RequestUserInputHandler {
|
||||
}
|
||||
};
|
||||
|
||||
let disallowed_mode = match session.collaboration_mode().await {
|
||||
CollaborationMode::Execute(_) => Some("Execute"),
|
||||
CollaborationMode::Custom(_) => Some("Custom"),
|
||||
let disallowed_mode = match session.collaboration_mode().await.mode {
|
||||
ModeKind::Execute => Some("Execute"),
|
||||
ModeKind::Custom => Some("Custom"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(mode_name) = disallowed_mode {
|
||||
|
||||
@@ -23,6 +23,7 @@ use codex_core::protocol::SessionSource;
|
||||
use codex_otel::OtelManager;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
@@ -887,11 +888,14 @@ async fn user_turn_collaboration_mode_overrides_model_and_effort() -> anyhow::Re
|
||||
.build(&server)
|
||||
.await?;
|
||||
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
|
||||
codex
|
||||
.submit(Op::UserTurn {
|
||||
|
||||
@@ -4,6 +4,7 @@ use codex_core::protocol::COLLABORATION_MODE_OPEN_TAG;
|
||||
use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use core_test_support::responses::ev_completed;
|
||||
@@ -22,11 +23,14 @@ fn sse_completed(id: &str) -> String {
|
||||
}
|
||||
|
||||
fn collab_mode_with_instructions(instructions: Option<&str>) -> CollaborationMode {
|
||||
CollaborationMode::Custom(Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: instructions.map(str::to_string),
|
||||
})
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: instructions.map(str::to_string),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn developer_texts(input: &[Value]) -> Vec<String> {
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_core::protocol::RolloutItem;
|
||||
use codex_core::protocol::RolloutLine;
|
||||
use codex_core::protocol::ENVIRONMENT_CONTEXT_OPEN_TAG;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -23,11 +24,14 @@ use std::time::Duration;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn collab_mode_with_instructions(instructions: Option<&str>) -> CollaborationMode {
|
||||
CollaborationMode::Custom(Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: instructions.map(str::to_string),
|
||||
})
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: instructions.map(str::to_string),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn collab_xml(text: &str) -> String {
|
||||
|
||||
@@ -12,6 +12,7 @@ use codex_core::protocol_config_types::ReasoningSummary;
|
||||
use codex_core::shell::Shell;
|
||||
use codex_core::shell::default_user_shell;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
@@ -412,11 +413,14 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul
|
||||
|
||||
let TestCodex { codex, .. } = test_codex().build(&server).await?;
|
||||
|
||||
let collaboration_mode = CollaborationMode::Custom(Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: None,
|
||||
});
|
||||
let collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: "gpt-5.1".to_string(),
|
||||
reasoning_effort: Some(ReasoningEffort::High),
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
|
||||
codex
|
||||
.submit(Op::OverrideTurnContext {
|
||||
|
||||
@@ -8,6 +8,7 @@ use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::request_user_input::RequestUserInputAnswer;
|
||||
@@ -132,11 +133,14 @@ async fn request_user_input_round_trip_resolves_pending() -> anyhow::Result<()>
|
||||
model: session_model,
|
||||
effort: None,
|
||||
summary: ReasoningSummary::Auto,
|
||||
collaboration_mode: Some(CollaborationMode::Plan(Settings {
|
||||
model: session_configured.model.clone(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
})),
|
||||
collaboration_mode: Some(CollaborationMode {
|
||||
mode: ModeKind::Plan,
|
||||
settings: Settings {
|
||||
model: session_configured.model.clone(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
},
|
||||
}),
|
||||
personality: None,
|
||||
})
|
||||
.await?;
|
||||
@@ -269,24 +273,26 @@ where
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn request_user_input_rejected_in_execute_mode() -> anyhow::Result<()> {
|
||||
assert_request_user_input_rejected("Execute", |model| {
|
||||
CollaborationMode::Execute(Settings {
|
||||
assert_request_user_input_rejected("Execute", |model| CollaborationMode {
|
||||
mode: ModeKind::Execute,
|
||||
settings: Settings {
|
||||
model,
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
})
|
||||
},
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn request_user_input_rejected_in_custom_mode() -> anyhow::Result<()> {
|
||||
assert_request_user_input_rejected("Custom", |model| {
|
||||
CollaborationMode::Custom(Settings {
|
||||
assert_request_user_input_rejected("Custom", |model| CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model,
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
})
|
||||
},
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -163,31 +163,24 @@ pub enum ModeKind {
|
||||
|
||||
/// Collaboration mode for a Codex session.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, JsonSchema, TS)]
|
||||
#[serde(tag = "mode", rename_all = "lowercase")]
|
||||
pub enum CollaborationMode {
|
||||
Plan(Settings),
|
||||
PairProgramming(Settings),
|
||||
Execute(Settings),
|
||||
Custom(Settings),
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct CollaborationMode {
|
||||
pub mode: ModeKind,
|
||||
pub settings: Settings,
|
||||
}
|
||||
|
||||
impl CollaborationMode {
|
||||
/// Returns a reference to the settings, regardless of variant.
|
||||
fn settings(&self) -> &Settings {
|
||||
match self {
|
||||
CollaborationMode::Plan(settings)
|
||||
| CollaborationMode::PairProgramming(settings)
|
||||
| CollaborationMode::Execute(settings)
|
||||
| CollaborationMode::Custom(settings) => settings,
|
||||
}
|
||||
/// Returns a reference to the settings.
|
||||
fn settings_ref(&self) -> &Settings {
|
||||
&self.settings
|
||||
}
|
||||
|
||||
pub fn model(&self) -> &str {
|
||||
self.settings().model.as_str()
|
||||
self.settings_ref().model.as_str()
|
||||
}
|
||||
|
||||
pub fn reasoning_effort(&self) -> Option<ReasoningEffort> {
|
||||
self.settings().reasoning_effort
|
||||
self.settings_ref().reasoning_effort
|
||||
}
|
||||
|
||||
/// Updates the collaboration mode with new model and/or effort values.
|
||||
@@ -196,14 +189,14 @@ impl CollaborationMode {
|
||||
/// - `effort`: `Some(Some(e))` to set effort to `e`, `Some(None)` to clear effort, `None` to keep current effort
|
||||
/// - `developer_instructions`: `Some(s)` to update developer instructions, `None` to keep current
|
||||
///
|
||||
/// Returns a new `CollaborationMode` with updated values, preserving the variant.
|
||||
/// Returns a new `CollaborationMode` with updated values, preserving the mode.
|
||||
pub fn with_updates(
|
||||
&self,
|
||||
model: Option<String>,
|
||||
effort: Option<Option<ReasoningEffort>>,
|
||||
developer_instructions: Option<String>,
|
||||
) -> Self {
|
||||
let settings = self.settings();
|
||||
let settings = self.settings_ref();
|
||||
let updated_settings = Settings {
|
||||
model: model.unwrap_or_else(|| settings.model.clone()),
|
||||
reasoning_effort: effort.unwrap_or(settings.reasoning_effort),
|
||||
@@ -211,13 +204,9 @@ impl CollaborationMode {
|
||||
.or_else(|| settings.developer_instructions.clone()),
|
||||
};
|
||||
|
||||
match self {
|
||||
CollaborationMode::Plan(_) => CollaborationMode::Plan(updated_settings),
|
||||
CollaborationMode::PairProgramming(_) => {
|
||||
CollaborationMode::PairProgramming(updated_settings)
|
||||
}
|
||||
CollaborationMode::Execute(_) => CollaborationMode::Execute(updated_settings),
|
||||
CollaborationMode::Custom(_) => CollaborationMode::Custom(updated_settings),
|
||||
CollaborationMode {
|
||||
mode: self.mode,
|
||||
settings: updated_settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,13 +263,8 @@ impl DeveloperInstructions {
|
||||
|
||||
/// Returns developer instructions from a collaboration mode if they exist and are non-empty.
|
||||
pub fn from_collaboration_mode(collaboration_mode: &CollaborationMode) -> Option<Self> {
|
||||
let settings = match collaboration_mode {
|
||||
CollaborationMode::Plan(settings)
|
||||
| CollaborationMode::PairProgramming(settings)
|
||||
| CollaborationMode::Execute(settings)
|
||||
| CollaborationMode::Custom(settings) => settings,
|
||||
};
|
||||
settings
|
||||
collaboration_mode
|
||||
.settings
|
||||
.developer_instructions
|
||||
.as_ref()
|
||||
.filter(|instructions| !instructions.is_empty())
|
||||
|
||||
@@ -910,7 +910,7 @@ impl ChatWidget {
|
||||
if !self.queued_user_messages.is_empty() {
|
||||
return;
|
||||
}
|
||||
if !matches!(self.stored_collaboration_mode, CollaborationMode::Plan(_)) {
|
||||
if self.stored_collaboration_mode.mode != ModeKind::Plan {
|
||||
return;
|
||||
}
|
||||
let has_message = last_agent_message.is_some_and(|message| !message.trim().is_empty());
|
||||
@@ -1943,7 +1943,10 @@ impl ChatWidget {
|
||||
config.experimental_mode,
|
||||
)
|
||||
} else {
|
||||
CollaborationMode::Custom(fallback_custom)
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: fallback_custom,
|
||||
}
|
||||
};
|
||||
|
||||
let active_cell = Some(Self::placeholder_session_header_cell(&config));
|
||||
@@ -2061,7 +2064,10 @@ impl ChatWidget {
|
||||
config.experimental_mode,
|
||||
)
|
||||
} else {
|
||||
CollaborationMode::Custom(fallback_custom)
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: fallback_custom,
|
||||
}
|
||||
};
|
||||
|
||||
let active_cell = Some(Self::placeholder_session_header_cell(&config));
|
||||
@@ -2182,7 +2188,10 @@ impl ChatWidget {
|
||||
config.experimental_mode,
|
||||
)
|
||||
} else {
|
||||
CollaborationMode::Custom(fallback_custom)
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: fallback_custom,
|
||||
}
|
||||
};
|
||||
|
||||
let mut widget = Self {
|
||||
@@ -3552,11 +3561,11 @@ impl ChatWidget {
|
||||
let items: Vec<SelectionItem> = presets
|
||||
.into_iter()
|
||||
.map(|preset| {
|
||||
let name = match preset {
|
||||
CollaborationMode::Plan(_) => "Plan",
|
||||
CollaborationMode::PairProgramming(_) => "Pair Programming",
|
||||
CollaborationMode::Execute(_) => "Execute",
|
||||
CollaborationMode::Custom(_) => "Custom",
|
||||
let name = match preset.mode {
|
||||
ModeKind::Plan => "Plan",
|
||||
ModeKind::PairProgramming => "Pair Programming",
|
||||
ModeKind::Execute => "Execute",
|
||||
ModeKind::Custom => "Custom",
|
||||
};
|
||||
let is_current =
|
||||
collaboration_modes::same_variant(&self.stored_collaboration_mode, &preset);
|
||||
@@ -4538,12 +4547,7 @@ impl ChatWidget {
|
||||
}
|
||||
if feature == Feature::CollaborationModes {
|
||||
self.bottom_pane.set_collaboration_modes_enabled(enabled);
|
||||
let settings = match &self.stored_collaboration_mode {
|
||||
CollaborationMode::Plan(settings)
|
||||
| CollaborationMode::PairProgramming(settings)
|
||||
| CollaborationMode::Execute(settings)
|
||||
| CollaborationMode::Custom(settings) => settings.clone(),
|
||||
};
|
||||
let settings = self.stored_collaboration_mode.settings.clone();
|
||||
let fallback_custom = settings.clone();
|
||||
self.stored_collaboration_mode = if enabled {
|
||||
initial_collaboration_mode(
|
||||
@@ -4552,7 +4556,10 @@ impl ChatWidget {
|
||||
self.config.experimental_mode,
|
||||
)
|
||||
} else {
|
||||
CollaborationMode::Custom(settings)
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings,
|
||||
}
|
||||
};
|
||||
self.update_collaboration_mode_indicator();
|
||||
}
|
||||
@@ -4632,11 +4639,11 @@ impl ChatWidget {
|
||||
if !self.collaboration_modes_enabled() {
|
||||
return None;
|
||||
}
|
||||
match &self.stored_collaboration_mode {
|
||||
CollaborationMode::Plan(_) => Some("Plan"),
|
||||
CollaborationMode::PairProgramming(_) => Some("Pair Programming"),
|
||||
CollaborationMode::Execute(_) => Some("Execute"),
|
||||
CollaborationMode::Custom(_) => None,
|
||||
match self.stored_collaboration_mode.mode {
|
||||
ModeKind::Plan => Some("Plan"),
|
||||
ModeKind::PairProgramming => Some("Pair Programming"),
|
||||
ModeKind::Execute => Some("Execute"),
|
||||
ModeKind::Custom => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4644,13 +4651,11 @@ impl ChatWidget {
|
||||
if !self.collaboration_modes_enabled() {
|
||||
return None;
|
||||
}
|
||||
match &self.stored_collaboration_mode {
|
||||
CollaborationMode::Plan(_) => Some(CollaborationModeIndicator::Plan),
|
||||
CollaborationMode::PairProgramming(_) => {
|
||||
Some(CollaborationModeIndicator::PairProgramming)
|
||||
}
|
||||
CollaborationMode::Execute(_) => Some(CollaborationModeIndicator::Execute),
|
||||
CollaborationMode::Custom(_) => None,
|
||||
match self.stored_collaboration_mode.mode {
|
||||
ModeKind::Plan => Some(CollaborationModeIndicator::Plan),
|
||||
ModeKind::PairProgramming => Some(CollaborationModeIndicator::PairProgramming),
|
||||
ModeKind::Execute => Some(CollaborationModeIndicator::Execute),
|
||||
ModeKind::Custom => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4681,7 +4686,8 @@ impl ChatWidget {
|
||||
if !self.collaboration_modes_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let old_model = self.stored_collaboration_mode.model().to_string();
|
||||
let mode = mode.with_updates(Some(old_model), None, None);
|
||||
self.stored_collaboration_mode = mode;
|
||||
self.update_collaboration_mode_indicator();
|
||||
self.request_redraw();
|
||||
@@ -5304,15 +5310,20 @@ fn initial_collaboration_mode(
|
||||
) -> CollaborationMode {
|
||||
if let Some(kind) = desired_mode {
|
||||
if kind == ModeKind::Custom {
|
||||
return CollaborationMode::Custom(fallback_custom);
|
||||
return CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: fallback_custom,
|
||||
};
|
||||
}
|
||||
if let Some(mode) = collaboration_modes::mode_for_kind(models_manager, kind) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
||||
collaboration_modes::default_mode(models_manager)
|
||||
.unwrap_or(CollaborationMode::Custom(fallback_custom))
|
||||
collaboration_modes::default_mode(models_manager).unwrap_or(CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: fallback_custom,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_rate_limits(base_url: String, auth: CodexAuth) -> Option<RateLimitSnapshot> {
|
||||
|
||||
@@ -64,6 +64,8 @@ use codex_otel::OtelManager;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::account::PlanType;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ReasoningEffortPreset;
|
||||
use codex_protocol::parse_command::ParsedCommand;
|
||||
@@ -779,18 +781,24 @@ async fn make_chatwidget_manual(
|
||||
let reasoning_effort = None;
|
||||
let stored_collaboration_mode = if collaboration_modes_enabled {
|
||||
collaboration_modes::default_mode(models_manager.as_ref()).unwrap_or_else(|| {
|
||||
CollaborationMode::Custom(Settings {
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: resolved_model.clone(),
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
},
|
||||
}
|
||||
})
|
||||
} else {
|
||||
CollaborationMode {
|
||||
mode: ModeKind::Custom,
|
||||
settings: Settings {
|
||||
model: resolved_model.clone(),
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
CollaborationMode::Custom(Settings {
|
||||
model: resolved_model.clone(),
|
||||
reasoning_effort,
|
||||
developer_instructions: None,
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
let widget = ChatWidget {
|
||||
app_event_tx,
|
||||
@@ -1206,7 +1214,7 @@ async fn plan_implementation_popup_yes_emits_submit_message_event() {
|
||||
panic!("expected SubmitUserMessageWithMode, got {event:?}");
|
||||
};
|
||||
assert_eq!(text, PLAN_IMPLEMENTATION_EXECUTE_MESSAGE);
|
||||
assert!(matches!(collaboration_mode, CollaborationMode::Execute(_)));
|
||||
assert_eq!(collaboration_mode.mode, ModeKind::Execute);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1221,7 +1229,11 @@ async fn submit_user_message_with_mode_sets_execute_collaboration_mode() {
|
||||
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn {
|
||||
collaboration_mode: Some(CollaborationMode::Execute(_)),
|
||||
collaboration_mode:
|
||||
Some(CollaborationMode {
|
||||
mode: ModeKind::Execute,
|
||||
..
|
||||
}),
|
||||
personality: None,
|
||||
..
|
||||
} => {}
|
||||
@@ -1235,11 +1247,14 @@ async fn submit_user_message_with_mode_sets_execute_collaboration_mode() {
|
||||
async fn plan_implementation_popup_skips_replayed_turn_complete() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
chat.stored_collaboration_mode = CollaborationMode::Plan(Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
});
|
||||
chat.stored_collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Plan,
|
||||
settings: Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
|
||||
chat.replay_initial_messages(vec![EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
last_agent_message: Some("Plan details".to_string()),
|
||||
@@ -1256,11 +1271,14 @@ async fn plan_implementation_popup_skips_replayed_turn_complete() {
|
||||
async fn plan_implementation_popup_skips_when_messages_queued() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
chat.stored_collaboration_mode = CollaborationMode::Plan(Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
});
|
||||
chat.stored_collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Plan,
|
||||
settings: Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
chat.queue_user_message("Queued message".into());
|
||||
|
||||
@@ -1277,11 +1295,14 @@ async fn plan_implementation_popup_skips_when_messages_queued() {
|
||||
async fn plan_implementation_popup_shows_on_plan_update_without_message() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
chat.stored_collaboration_mode = CollaborationMode::Plan(Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
});
|
||||
chat.stored_collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Plan,
|
||||
settings: Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
|
||||
chat.on_task_started();
|
||||
chat.on_plan_update(UpdatePlanArgs {
|
||||
@@ -1306,11 +1327,14 @@ async fn plan_implementation_popup_skips_when_rate_limit_prompt_pending() {
|
||||
chat.auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
chat.stored_collaboration_mode = CollaborationMode::Plan(Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
});
|
||||
chat.stored_collaboration_mode = CollaborationMode {
|
||||
mode: ModeKind::Plan,
|
||||
settings: Settings {
|
||||
model: chat.current_model().to_string(),
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
|
||||
chat.on_task_started();
|
||||
chat.on_plan_update(UpdatePlanArgs {
|
||||
@@ -2205,16 +2229,10 @@ async fn collab_mode_shift_tab_cycles_only_when_enabled_and_idle() {
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::BackTab));
|
||||
assert!(matches!(
|
||||
chat.stored_collaboration_mode,
|
||||
CollaborationMode::Execute(_)
|
||||
));
|
||||
assert_eq!(chat.stored_collaboration_mode.mode, ModeKind::Execute);
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::BackTab));
|
||||
assert!(matches!(
|
||||
chat.stored_collaboration_mode,
|
||||
CollaborationMode::Plan(_)
|
||||
));
|
||||
assert_eq!(chat.stored_collaboration_mode.mode, ModeKind::Plan);
|
||||
|
||||
chat.on_task_started();
|
||||
let before = chat.stored_collaboration_mode.clone();
|
||||
@@ -2247,7 +2265,11 @@ async fn collab_slash_command_opens_picker_and_updates_mode() {
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn {
|
||||
collaboration_mode: Some(CollaborationMode::PairProgramming(_)),
|
||||
collaboration_mode:
|
||||
Some(CollaborationMode {
|
||||
mode: ModeKind::PairProgramming,
|
||||
..
|
||||
}),
|
||||
personality: None,
|
||||
..
|
||||
} => {}
|
||||
@@ -2261,7 +2283,11 @@ async fn collab_slash_command_opens_picker_and_updates_mode() {
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn {
|
||||
collaboration_mode: Some(CollaborationMode::PairProgramming(_)),
|
||||
collaboration_mode:
|
||||
Some(CollaborationMode {
|
||||
mode: ModeKind::PairProgramming,
|
||||
..
|
||||
}),
|
||||
personality: None,
|
||||
..
|
||||
} => {}
|
||||
@@ -2282,7 +2308,11 @@ async fn collab_mode_defaults_to_pair_programming_when_enabled() {
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn {
|
||||
collaboration_mode: Some(CollaborationMode::PairProgramming(_)),
|
||||
collaboration_mode:
|
||||
Some(CollaborationMode {
|
||||
mode: ModeKind::PairProgramming,
|
||||
..
|
||||
}),
|
||||
personality: None,
|
||||
..
|
||||
} => {}
|
||||
@@ -2296,10 +2326,10 @@ async fn collab_mode_defaults_to_pair_programming_when_enabled() {
|
||||
async fn collab_mode_enabling_sets_pair_programming_default() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
assert!(matches!(
|
||||
chat.stored_collaboration_mode,
|
||||
CollaborationMode::PairProgramming(_)
|
||||
));
|
||||
assert_eq!(
|
||||
chat.stored_collaboration_mode.mode,
|
||||
ModeKind::PairProgramming
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -3,19 +3,14 @@ use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
|
||||
fn mode_kind(mode: &CollaborationMode) -> ModeKind {
|
||||
match mode {
|
||||
CollaborationMode::Plan(_) => ModeKind::Plan,
|
||||
CollaborationMode::PairProgramming(_) => ModeKind::PairProgramming,
|
||||
CollaborationMode::Execute(_) => ModeKind::Execute,
|
||||
CollaborationMode::Custom(_) => ModeKind::Custom,
|
||||
}
|
||||
mode.mode
|
||||
}
|
||||
|
||||
pub(crate) fn default_mode(models_manager: &ModelsManager) -> Option<CollaborationMode> {
|
||||
let presets = models_manager.list_collaboration_modes();
|
||||
presets
|
||||
.iter()
|
||||
.find(|preset| matches!(preset, CollaborationMode::PairProgramming(_)))
|
||||
.find(|preset| preset.mode == ModeKind::PairProgramming)
|
||||
.cloned()
|
||||
.or_else(|| presets.into_iter().next())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user