use super::{SessionEvent, SessionEventKind};
use crate::{config::EffectiveConfig, thinking::ThinkingLevel};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct SessionPreferences {
pub(crate) provider: String,
pub(crate) model: String,
pub(crate) requested_thinking: ThinkingLevel,
}
pub(crate) fn observe(preferences: &mut Option<SessionPreferences>, event: &SessionEvent) {
let value = match event.kind() {
Some(SessionEventKind::SessionPreferences) => Some(&event.payload),
Some(SessionEventKind::Compaction) => event.payload.get("session_preferences"),
_ => None,
};
if let Some(value) = value
&& let Ok(saved) = serde_json::from_value::<SessionPreferences>(value.clone())
&& !saved.provider.trim().is_empty()
&& !saved.model.trim().is_empty()
{
*preferences = Some(saved);
}
}
#[derive(Debug, Clone)]
pub(crate) struct RestoredSessionPreferences {
pub(crate) saved: SessionPreferences,
pub(crate) config: EffectiveConfig,
pub(crate) notice: Option<String>,
#[cfg_attr(
not(unix),
expect(dead_code, reason = "local-agent safe selection notice")
)]
pub(crate) selection_restored: bool,
}
pub(crate) fn restore(
destination: &EffectiveConfig,
saved: SessionPreferences,
) -> RestoredSessionPreferences {
match crate::config::read_settings(&destination.paths) {
Ok(settings) => restore_with_settings(destination, saved, &settings),
Err(error) => finish_restoration(destination, saved, Err(error)),
}
}
pub(crate) fn restore_with_settings(
destination: &EffectiveConfig,
saved: SessionPreferences,
settings: &crate::config::Settings,
) -> RestoredSessionPreferences {
let selection = crate::model_catalog::resolve_model_with_settings(
&destination.paths,
&destination.auth_state(),
&format!("{}/{}", saved.provider, saved.model),
settings,
)
.and_then(|(provider, model, notice, _)| {
let config = crate::config::load_effective_provider_selection_with_settings(
&destination.paths,
&provider,
&model,
settings,
)?;
anyhow::ensure!(
config.auth_state().is_ready(),
"provider '{provider}' is not ready"
);
Ok((config, notice))
});
finish_restoration(destination, saved, selection)
}
fn finish_restoration(
destination: &EffectiveConfig,
saved: SessionPreferences,
selection: anyhow::Result<(EffectiveConfig, Option<String>)>,
) -> RestoredSessionPreferences {
let selection_restored = selection.is_ok();
let (mut config, notice) = match selection {
Ok(selection) => selection,
Err(error) => (
destination.clone(),
Some(format!(
"Saved selection {}/{} could not be restored: {error}. Using {}/{}; saved preference retained.",
saved.provider,
saved.model,
destination.provider_id(),
destination.model.as_deref().unwrap_or_default(),
)),
),
};
config.thinking_level = saved.requested_thinking;
RestoredSessionPreferences {
saved,
config,
notice,
selection_restored,
}
}