magi-code 0.96.4

Repository-aware CLI coding agent for terminal work
Documentation
//! Explicit session-scoped selection; validate fully, persist, then replace config.
use super::{catalog::provider_ready, startup::RuntimeContext};
use crate::{
    cancellation::AgentCancellation,
    config::EffectiveConfig,
    model_catalog::ModelCatalogEntry,
    sessions::{SessionAppendBudget, SessionAppendOutcome, SessionEvent, SessionEventKind},
    thinking::{CatalogThinkingMetadata, ThinkingCapabilityScope, ThinkingLevel},
};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};

#[derive(Clone, Serialize)]
pub(super) struct EffortOptions {
    pub(super) effort_support: &'static str,
    pub(super) permitted_efforts: Vec<ThinkingLevel>,
}
impl EffortOptions {
    pub(super) fn for_model(
        provider: &str,
        model: &str,
        entry: Option<&ModelCatalogEntry>,
        scope: ThinkingCapabilityScope,
    ) -> Self {
        let metadata = entry.map(|entry| CatalogThinkingMetadata {
            reasoning_efforts: entry.reasoning_efforts.clone(),
            supports_reasoning: entry
                .supports_reasoning
                .or(entry.legacy_supports_reasoning_effort),
        });
        let levels =
            crate::thinking::available_thinking_levels(provider, model, metadata.as_ref(), scope);
        let support = if levels.len() > 1 {
            "supported"
        } else if metadata
            .as_ref()
            .and_then(|m| m.supports_reasoning)
            .is_some()
            || metadata
                .as_ref()
                .is_some_and(|m| m.reasoning_efforts.as_ref().is_some_and(|e| !e.is_empty()))
        {
            "unsupported"
        } else {
            "unknown"
        };
        Self {
            effort_support: support,
            permitted_efforts: levels,
        }
    }
    fn status(&self, config: &EffectiveConfig, revision: &str) -> Value {
        let selected = crate::providers::ProviderSelection::from_config_without_auth(config);
        let effective =
            crate::thinking::resolve_thinking_level(&self.permitted_efforts, config.thinking_level);
        json!({"revision":revision, "provider":super::safe_name(&selected.provider), "model":super::safe_name(&selected.model),
            "requested_effort":config.thinking_level, "effective_effort":if self.effort_support=="supported" {Some(effective)} else {None},
            "effort_support":self.effort_support, "permitted_efforts":self.permitted_efforts})
    }
}
pub(super) fn initial_status(
    config: &EffectiveConfig,
    settings: &crate::config::Settings,
    revision: &str,
) -> Value {
    let selected = crate::providers::ProviderSelection::from_config_without_auth(config);
    let catalog = crate::model_catalog::read_selection_catalog(
        &config.paths,
        &selected.provider,
        settings,
        1024 * 1024,
    )
    .ok();
    // Oversized/unsafe cache is unavailable, not evidence for a guessed effective effort.
    if catalog.is_none() {
        return EffortOptions {
            effort_support: "unknown",
            permitted_efforts: vec![ThinkingLevel::Default],
        }
        .status(config, revision);
    }
    let entry = catalog
        .as_ref()
        .and_then(|c| c.entries.as_ref())
        .and_then(|entries| entries.iter().find(|e| e.model == selected.model));
    EffortOptions::for_model(
        &selected.provider,
        &selected.model,
        entry,
        crate::thinking::capability_scope_for_provider(
            &settings.custom_providers,
            &selected.provider,
        ),
    )
    .status(config, revision)
}

impl RuntimeContext {
    pub(super) fn sync_selection_effort(&mut self) -> bool {
        let mut details = initial_status(
            self.effective_config(),
            &self.launch.settings,
            &self.selection_revision,
        );
        if details == self.selection_details {
            return false;
        }
        self.selection_revision = uuid::Uuid::new_v4().to_string();
        details["revision"] = json!(self.selection_revision);
        self.selection_details = details;
        true
    }

    pub(super) fn validate_effort_for_turn(&mut self) -> Result<(), &'static str> {
        let selected =
            crate::providers::ProviderSelection::from_config_without_auth(self.effective_config());
        crate::model_catalog::read_selection_catalog(
            &self.launch.config.paths,
            &selected.provider,
            &self.launch.settings,
            1024 * 1024,
        )?;
        if self.sync_selection_effort() {
            return Err("stale_selection");
        }
        Ok(())
    }
}
#[derive(Default, Deserialize)]
#[serde(rename_all = "snake_case")]
enum EffortMode {
    #[default]
    Keep,
    Default,
    Set,
}
#[derive(Default, Deserialize)]
#[serde(deny_unknown_fields)]
struct EffortRequest {
    mode: EffortMode,
    #[serde(default, deserialize_with = "deserialize_effort_value")]
    value: Option<ThinkingLevel>,
}
fn deserialize_effort_value<'de, D: serde::Deserializer<'de>>(
    deserializer: D,
) -> Result<Option<ThinkingLevel>, D::Error> {
    ThinkingLevel::deserialize(deserializer).map(Some)
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub(super) struct SelectionParams {
    session_id: String,
    selection_revision: String,
    catalog_revision: String,
    provider: String,
    model: String,
    #[serde(default)]
    effort: EffortRequest,
}
impl SelectionParams {
    pub(super) fn parse(params: &serde_json::value::RawValue) -> Result<Self, &'static str> {
        let params: Self = serde_json::from_str(params.get()).map_err(|_| "invalid_params")?;
        if params.session_id.len() > 64
            || crate::sessions::validate_session_id(params.session_id.clone()).is_err()
            || !super::valid_id(&params.selection_revision)
            || !super::valid_id(&params.catalog_revision)
            || params.provider.is_empty()
            || params.model.is_empty()
            || super::safe_name(&params.provider).is_none()
            || super::safe_name(&params.model).is_none()
            || !matches!(
                (&params.effort.mode, params.effort.value),
                (EffortMode::Keep | EffortMode::Default, None)
                    | (
                        EffortMode::Set,
                        Some(
                            ThinkingLevel::Low
                                | ThinkingLevel::Medium
                                | ThinkingLevel::High
                                | ThinkingLevel::XHigh
                                | ThinkingLevel::Max
                        )
                    )
            )
        {
            return Err("invalid_params");
        }
        Ok(params)
    }
}
impl RuntimeContext {
    pub(super) fn refresh_selection_status(&mut self) {
        self.selection_revision = uuid::Uuid::new_v4().to_string();
        self.selection_details = initial_status(
            self.effective_config(),
            &self.launch.settings,
            &self.selection_revision,
        );
    }
    pub(super) fn select_model(
        &mut self,
        params: SelectionParams,
        cancellation: &AgentCancellation,
    ) -> Result<Value, &'static str> {
        let attachment = self.attachment.as_ref().ok_or("session_missing")?;
        if params.session_id != attachment.session.id() {
            return Err("stale_session");
        }
        if params.selection_revision != self.selection_revision {
            return Err("stale_selection");
        }
        self.validate_catalog_revision(&params.catalog_revision, cancellation)?;
        let catalog = self.catalog.as_ref().ok_or("stale_catalog")?;
        let choice = catalog
            .choices
            .iter()
            .find(|c| c.provider == params.provider && c.model == params.model)
            .ok_or("selection_invalid")?;
        if choice.availability != "available" {
            return Err("provider_unavailable");
        }
        let requested = match (params.effort.mode, params.effort.value) {
            (EffortMode::Keep, None) => self.effective_config().thinking_level,
            (EffortMode::Default, None) => ThinkingLevel::Default,
            (EffortMode::Set, Some(value)) => value,
            _ => return Err("invalid_params"),
        };
        if !choice.effort.permitted_efforts.contains(&requested) {
            return Err("effort_unsupported");
        }
        let effort = choice.effort.clone();
        // Normal cached-model admission: no global/project writes and no refresh/fallback.
        let (provider, model, notice, _) = crate::model_catalog::resolve_model_with_settings(
            &self.launch.config.paths,
            &self.effective_config().auth_state(),
            &format!("{}/{}", params.provider, params.model),
            &self.launch.settings,
        )
        .map_err(|_| "selection_invalid")?;
        if provider != params.provider || model != params.model {
            return Err("selection_invalid");
        }
        let mut config = crate::config::load_effective_provider_selection_with_settings(
            &self.launch.config.paths,
            &provider,
            &model,
            &self.launch.settings,
        )
        .map_err(|_| "provider_unavailable")?;
        crate::providers::supported_custom_provider(
            &config,
            &crate::providers::ProviderSelection::from_config_without_auth(&config),
        )
        .map_err(|_| "selection_invalid")?;
        if !provider_ready(&config) {
            return Err("provider_unavailable");
        }
        config.thinking_level = requested;
        self.validate_catalog_revision(&params.catalog_revision, cancellation)?;
        let attachment = self.attachment.as_mut().ok_or("session_missing")?;
        attachment.validate_for_turn()?;
        let saved = crate::sessions::preferences::SessionPreferences {
            provider,
            model,
            requested_thinking: requested,
        };
        let event = SessionEvent::new_kind(
            SessionEventKind::SessionPreferences,
            params.session_id,
            self.launch.cwd.clone(),
            serde_json::to_value(saved).map_err(|_| "selection_invalid")?,
        );
        cancellation.check().map_err(|_| "operation_cancelled")?;
        let outcome = attachment
            .session
            .append_with_budget(
                event,
                &mut SessionAppendBudget {
                    remaining_bytes: 4096,
                    max_session_bytes: crate::context::REPLAY_JSONL_MAX_BYTES as u64,
                },
            )
            .map_err(|_| "persistence_failed")?;
        // Durable source of truth before in-memory replacement. No fallible validation after this point.
        attachment.config = config;
        let mut notices = Vec::new();
        if notice.is_some() {
            notices.push("selection_normalized");
        }
        if matches!(
            outcome,
            SessionAppendOutcome::DurableJsonlMetadataUpdateFailed(_)
        ) {
            notices.push("metadata_update_failed");
        }
        let history_revision = match attachment.refresh_history() {
            Ok(()) => Some(attachment.revision().to_owned()),
            Err(_) => {
                notices.push("history_unavailable");
                None
            }
        };
        self.selection_revision = uuid::Uuid::new_v4().to_string();
        self.selection_details = effort.status(self.effective_config(), &self.selection_revision);
        Ok(
            json!({"session_id":self.attachment.as_ref().map(|a|a.session.id()),"selection":self.selection_details,
            "catalog_revision":params.catalog_revision,"scope":"session","durable":true,"history_revision":history_revision,"notices":notices}),
        )
    }
}