mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
## What changed - Add one-shot composer and status-line transitions when the active reasoning effort changes to Max or Ultra. - Keep a tier-specific prompt accent after the transition, including a distinct Ultra prompt glyph. - Preserve drafts and footer priority, avoid replaying effects for restored or unchanged settings, and disable motion when animations or sufficient terminal color support are unavailable. ## Testing - Add unit and snapshot coverage for animation frames, prompt accents, status-line transitions, narrow layouts, terminal palettes, restored sessions, and reduced-motion behavior. GitOrigin-RevId: 6da1d501d228d0cf1144b3d211d5d1304a06f447
261 lines
10 KiB
Rust
261 lines
10 KiB
Rust
//! Session configuration and thread-header orchestration for `ChatWidget`.
|
||
|
||
use super::*;
|
||
|
||
impl ChatWidget {
|
||
fn on_session_configured_with_display_and_fork_parent_title(
|
||
&mut self,
|
||
session: ThreadSessionState,
|
||
display: SessionConfiguredDisplay,
|
||
fork_parent_title: Option<String>,
|
||
) {
|
||
self.transcript.reset_copy_history();
|
||
let history_metadata = session.message_history.unwrap_or_default();
|
||
self.bottom_pane.set_history_metadata(
|
||
session.thread_id,
|
||
history_metadata.log_id,
|
||
history_metadata.entry_count,
|
||
);
|
||
self.set_skills(/*skills*/ None);
|
||
self.session_network_proxy = session.network_proxy.clone();
|
||
let previous_thread_id = self.thread_id;
|
||
self.thread_id = Some(session.thread_id);
|
||
self.bottom_pane
|
||
.set_queue_submissions(/*queue_submissions*/ false);
|
||
if previous_thread_id != self.thread_id {
|
||
self.review.recent_auto_review_denials = RecentAutoReviewDenials::default();
|
||
}
|
||
self.refresh_plan_mode_nudge();
|
||
self.turn_lifecycle.reset_thread();
|
||
self.clear_safety_buffering();
|
||
self.thread_name = session.thread_name.clone();
|
||
self.current_goal_status_indicator = None;
|
||
self.current_goal_status = None;
|
||
self.update_collaboration_mode_indicator();
|
||
self.forked_from = session.forked_from_id;
|
||
self.current_rollout_path = session.rollout_path.clone();
|
||
self.current_cwd = Some(session.cwd.to_path_buf());
|
||
self.config.cwd = session.cwd.clone();
|
||
let runtime_workspace_roots = session.runtime_workspace_roots.clone();
|
||
self.config.workspace_roots = runtime_workspace_roots.clone();
|
||
self.config
|
||
.permissions
|
||
.set_workspace_roots(runtime_workspace_roots);
|
||
self.effective_service_tier = session.service_tier.clone();
|
||
if let Err(err) = self
|
||
.config
|
||
.permissions
|
||
.approval_policy
|
||
.set(session.approval_policy.to_core())
|
||
{
|
||
tracing::warn!(%err, "failed to sync approval_policy from SessionConfigured");
|
||
self.config.permissions.approval_policy =
|
||
Constrained::allow_only(session.approval_policy.to_core());
|
||
}
|
||
let permission_snapshot = PermissionProfileSnapshot::from_session_snapshot(
|
||
session.permission_profile.clone(),
|
||
session.active_permission_profile.clone(),
|
||
);
|
||
let permission_sync = self
|
||
.config
|
||
.permissions
|
||
.set_permission_profile_from_session_snapshot(permission_snapshot.clone());
|
||
if let Err(err) = permission_sync {
|
||
tracing::warn!(%err, "failed to sync permissions from SessionConfigured");
|
||
if let Err(replace_err) = self
|
||
.config
|
||
.permissions
|
||
.replace_permission_profile_from_session_snapshot(permission_snapshot)
|
||
{
|
||
tracing::error!(
|
||
%replace_err,
|
||
"failed to replace permissions from SessionConfigured after constraint fallback"
|
||
);
|
||
}
|
||
}
|
||
self.config.approvals_reviewer = session.approvals_reviewer;
|
||
self.config.personality = session.personality;
|
||
self.status_line_project_root_name_cache = None;
|
||
let forked_from_id = session.forked_from_id;
|
||
let default_model = session.model.clone();
|
||
self.current_collaboration_mode = self.current_collaboration_mode.with_updates(
|
||
Some(default_model.clone()),
|
||
Some(session.reasoning_effort.clone()),
|
||
/*developer_instructions*/ None,
|
||
);
|
||
if session.reasoning_effort == Some(ReasoningEffortConfig::Ultra) {
|
||
self.set_plan_mode_reasoning_effort(Some(ReasoningEffortConfig::Ultra));
|
||
}
|
||
match session.collaboration_mode.as_deref() {
|
||
Some(collaboration_mode) => {
|
||
self.set_effective_collaboration_mode(collaboration_mode.clone());
|
||
}
|
||
None => {
|
||
self.active_collaboration_mask = Self::initial_collaboration_mask(
|
||
&self.config,
|
||
self.model_catalog.as_ref(),
|
||
Some(&default_model),
|
||
);
|
||
if let Some(mask) = self.active_collaboration_mask.as_mut() {
|
||
mask.reasoning_effort = Some(session.reasoning_effort.clone());
|
||
}
|
||
self.update_collaboration_mode_indicator();
|
||
self.refresh_plan_mode_nudge();
|
||
}
|
||
}
|
||
let effort = self.effective_reasoning_effort();
|
||
self.bottom_pane
|
||
.set_active_reasoning_effort_baseline(effort.as_ref());
|
||
self.refresh_model_display();
|
||
self.refresh_status_surfaces();
|
||
self.sync_service_tier_commands();
|
||
self.sync_personality_command_enabled();
|
||
self.sync_plugins_command_enabled();
|
||
self.sync_goal_command_enabled();
|
||
self.refresh_plugin_mentions();
|
||
let model_for_header = self.current_model().to_string();
|
||
if matches!(
|
||
display,
|
||
SessionConfiguredDisplay::Normal | SessionConfiguredDisplay::PromptEdit
|
||
) {
|
||
let startup_tooltip_override = self.startup_tooltip_override.take();
|
||
let show_fast_status = self
|
||
.should_show_fast_status(&model_for_header, self.effective_service_tier.as_deref());
|
||
let session_info_cell = history_cell::new_session_info(
|
||
&self.config,
|
||
&model_for_header,
|
||
&session,
|
||
self.show_welcome_banner,
|
||
startup_tooltip_override,
|
||
self.plan_type,
|
||
show_fast_status,
|
||
);
|
||
self.apply_session_info_cell(session_info_cell);
|
||
} else if self
|
||
.transcript
|
||
.active_cell
|
||
.as_ref()
|
||
.is_some_and(|cell| cell.as_any().is::<history_cell::SessionHeaderHistoryCell>())
|
||
{
|
||
self.transcript.active_cell = None;
|
||
self.bump_active_cell_revision();
|
||
}
|
||
self.transcript.saw_copy_source_this_turn = false;
|
||
self.refresh_skills_for_current_cwd(/*force_reload*/ true);
|
||
if self.connectors_enabled() {
|
||
self.prefetch_connectors();
|
||
}
|
||
self.submit_initial_user_message_if_pending();
|
||
if display == SessionConfiguredDisplay::Normal
|
||
&& let Some(forked_from_id) = forked_from_id
|
||
{
|
||
self.emit_forked_thread_event(forked_from_id, fork_parent_title);
|
||
}
|
||
if !self.suppress_session_configured_redraw {
|
||
self.request_redraw();
|
||
}
|
||
}
|
||
|
||
pub(crate) fn handle_thread_session(&mut self, session: ThreadSessionState) {
|
||
self.instruction_source_paths = session.instruction_source_paths.clone();
|
||
let fork_parent_title = session.fork_parent_title.clone();
|
||
self.on_session_configured_with_display_and_fork_parent_title(
|
||
session,
|
||
SessionConfiguredDisplay::Normal,
|
||
fork_parent_title,
|
||
);
|
||
}
|
||
|
||
pub(crate) fn handle_thread_session_quiet(&mut self, session: ThreadSessionState) {
|
||
self.instruction_source_paths = session.instruction_source_paths.clone();
|
||
self.on_session_configured_with_display_and_fork_parent_title(
|
||
session,
|
||
SessionConfiguredDisplay::Quiet,
|
||
/*fork_parent_title*/ None,
|
||
);
|
||
}
|
||
|
||
pub(crate) fn handle_prompt_edit_thread_session(&mut self, session: ThreadSessionState) {
|
||
self.instruction_source_paths = session.instruction_source_paths.clone();
|
||
let fork_parent_title = session.fork_parent_title.clone();
|
||
self.on_session_configured_with_display_and_fork_parent_title(
|
||
session,
|
||
SessionConfiguredDisplay::PromptEdit,
|
||
fork_parent_title,
|
||
);
|
||
}
|
||
|
||
pub(crate) fn handle_side_thread_session(&mut self, session: ThreadSessionState) {
|
||
self.instruction_source_paths = session.instruction_source_paths.clone();
|
||
let fork_parent_title = session.fork_parent_title.clone();
|
||
self.on_session_configured_with_display_and_fork_parent_title(
|
||
session,
|
||
SessionConfiguredDisplay::SideConversation,
|
||
fork_parent_title,
|
||
);
|
||
}
|
||
|
||
pub(super) fn emit_forked_thread_event(
|
||
&mut self,
|
||
forked_from_id: ThreadId,
|
||
fork_parent_title: Option<String>,
|
||
) {
|
||
let forked_from_id_text = forked_from_id.to_string();
|
||
let line: Line<'static> = if let Some(name) = fork_parent_title
|
||
&& !name.trim().is_empty()
|
||
{
|
||
vec![
|
||
"• ".dim(),
|
||
"Thread forked from ".into(),
|
||
name.cyan(),
|
||
" (".into(),
|
||
forked_from_id_text.cyan(),
|
||
")".into(),
|
||
]
|
||
.into()
|
||
} else {
|
||
vec![
|
||
"• ".dim(),
|
||
"Thread forked from ".into(),
|
||
forked_from_id_text.cyan(),
|
||
]
|
||
.into()
|
||
};
|
||
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||
PlainHistoryCell::new(vec![line]),
|
||
)));
|
||
}
|
||
|
||
pub(crate) fn emit_prompt_edit_thread_event(&mut self) {
|
||
let line: Line<'static> = vec![
|
||
"• ".dim(),
|
||
"You’re continuing from this point in a new conversation".into(),
|
||
]
|
||
.into();
|
||
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||
PlainHistoryCell::new(vec![line]),
|
||
)));
|
||
}
|
||
|
||
pub(super) fn on_thread_name_updated(
|
||
&mut self,
|
||
thread_id: ThreadId,
|
||
thread_name: Option<String>,
|
||
) {
|
||
if self.thread_id == Some(thread_id) {
|
||
if let Some(name) = thread_name.as_deref() {
|
||
let cell = Self::rename_confirmation_cell(name, self.thread_id);
|
||
self.add_boxed_history(Box::new(cell));
|
||
}
|
||
self.thread_name = thread_name;
|
||
self.refresh_status_surfaces();
|
||
self.request_redraw();
|
||
self.maybe_send_next_queued_input();
|
||
}
|
||
}
|
||
|
||
pub(super) fn set_skills(&mut self, skills: Option<Vec<SkillMetadata>>) {
|
||
self.bottom_pane.set_skills(skills);
|
||
}
|
||
}
|