magi-code 0.96.4

Repository-aware CLI coding agent for terminal work
Documentation
//! Cached-only, bounded provider/model options; no refresh or credential exchange.
use super::{selection::EffortOptions, startup::RuntimeContext};
use crate::{cancellation::AgentCancellation, config::EffectiveConfig};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use sha2::{Digest, Sha256};
use std::time::{Duration, Instant};

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub(super) struct CatalogParams {
    revision: Option<String>,
    cursor: Option<String>,
    #[serde(default = "default_limit")]
    limit: usize,
}
fn default_limit() -> usize {
    100
}
impl CatalogParams {
    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.limit == 0
            || params.limit > 100
            || params
                .revision
                .as_deref()
                .is_some_and(|r| !super::valid_id(r))
            || params
                .cursor
                .as_deref()
                .is_some_and(|c| !super::valid_id(c))
            || params.revision.is_some() != params.cursor.is_some()
        {
            return Err("invalid_params");
        }
        Ok(params)
    }
}
#[derive(Serialize)]
pub(super) struct Choice {
    pub(super) selection_id: String,
    pub(super) provider: String,
    pub(super) model: String,
    provider_name: Option<String>,
    model_name: Option<String>,
    pub(super) availability: &'static str,
    #[serde(flatten)]
    pub(super) effort: EffortOptions,
}
pub(super) struct Snapshot {
    pub(super) revision: String,
    pub(super) fingerprint: [u8; 32],
    pub(super) choices: Vec<Choice>,
    notices: Vec<&'static str>,
    invalidated: bool,
}

pub(super) fn provider_ready(config: &EffectiveConfig) -> bool {
    if config.provider_id() == crate::providers::OPENAI_CODEX_PROVIDER {
        crate::auth::read_auth_store(&config.paths)
            .ok()
            .is_some_and(|store| {
                crate::auth::classify_provider_auth_record(
                    config.provider_id(),
                    store.auth().providers.get(config.provider_id()),
                    chrono::Utc::now().timestamp(),
                )
                .is_ready()
            })
    } else {
        config.auth_state().is_ready()
    }
}
fn display_name(name: &str) -> Option<String> {
    if name.len() > 256
        || name
            .split_whitespace()
            .any(crate::config::looks_like_secret_value)
    {
        return None;
    }
    let sanitized = crate::output::sanitize_display_text(name);
    (!sanitized.trim().is_empty() && sanitized.len() <= 256).then_some(sanitized)
}
impl RuntimeContext {
    pub(super) fn scan_catalog(
        &self,
        cancellation: &AgentCancellation,
    ) -> Result<Snapshot, &'static str> {
        let deadline = Instant::now() + Duration::from_secs(2);
        let settings = &self.launch.settings;
        let mut providers = vec![
            crate::providers::OPENAI_CODEX_PROVIDER.to_owned(),
            crate::providers::ANTHROPIC_PROVIDER.to_owned(),
            crate::providers::CLAUDE_SUBSCRIPTION_PROVIDER.to_owned(),
        ];
        providers.extend(settings.custom_providers.keys().cloned());
        providers.sort();
        providers.dedup();
        if providers.len() > 32 {
            return Err("catalog_limit");
        }
        let enabled = crate::config::enabled_model_ids_from_settings(settings);
        let mut remaining = 8 * 1024 * 1024;
        let mut scanned_entries = 0;
        let mut choices = Vec::new();
        let mut notices = Vec::new();
        let mut digest = Sha256::new();
        for provider in providers {
            cancellation.check().map_err(|_| "operation_cancelled")?;
            if Instant::now() >= deadline {
                return Err("catalog_limit");
            }
            digest.update(provider.len().to_le_bytes());
            digest.update(&provider);
            let cached = crate::model_catalog::read_selection_catalog(
                &self.launch.config.paths,
                &provider,
                settings,
                remaining.min(1024 * 1024),
            )?;
            remaining -= cached.bytes_read;
            digest.update(cached.fingerprint);
            let Some(entries) = cached.entries else {
                if !notices.contains(&"provider_catalog_unavailable") {
                    notices.push("provider_catalog_unavailable");
                }
                continue;
            };
            scanned_entries += entries.len();
            if scanned_entries > 2048 {
                return Err("catalog_limit");
            }
            let config = crate::config::load_effective_provider_selection_with_settings(
                &self.launch.config.paths,
                &provider,
                "catalog-readiness",
                settings,
            );
            let ready = config.as_ref().is_ok_and(provider_ready);
            digest.update([u8::from(ready)]);
            for entry in entries {
                cancellation.check().map_err(|_| "operation_cancelled")?;
                if !enabled.contains(&entry.id)
                    || crate::config::reject_retired_provider_selection(
                        &entry.provider,
                        &entry.model,
                    )
                    .is_err()
                {
                    continue;
                }
                if entry.provider.len() > 256 || entry.model.len() > 256 {
                    return Err("catalog_limit");
                }
                if super::safe_name(&entry.provider).is_none()
                    || super::safe_name(&entry.model).is_none()
                {
                    if !notices.contains(&"catalog_entries_omitted_unsafe") {
                        notices.push("catalog_entries_omitted_unsafe");
                    }
                    continue;
                }
                let scope = crate::thinking::capability_scope_for_provider(
                    &settings.custom_providers,
                    &entry.provider,
                );
                let effort =
                    EffortOptions::for_model(&entry.provider, &entry.model, Some(&entry), scope);
                let selection_id = crate::hex::lower_hex(Sha256::digest(
                    serde_json::to_vec(&(&entry.provider, &entry.model))
                        .map_err(|_| "catalog_unavailable")?,
                ));
                let provider_name = settings
                    .custom_providers
                    .get(&entry.provider)
                    .and_then(|p| display_name(&p.label));
                choices.push(Choice {
                    selection_id,
                    provider: entry.provider,
                    model: entry.model,
                    provider_name,
                    model_name: entry.display_name.as_deref().and_then(display_name),
                    availability: if ready {
                        "available"
                    } else {
                        "authentication_unavailable"
                    },
                    effort,
                });
            }
        }
        choices.sort_by(|a, b| (&a.provider, &a.model).cmp(&(&b.provider, &b.model)));
        if choices
            .windows(2)
            .any(|pair| pair[0].selection_id == pair[1].selection_id)
        {
            return Err("catalog_unavailable");
        }
        // Bound stored projection as well as individual pages; never silently trim models.
        if serde_json::to_vec(&choices)
            .map_err(|_| "catalog_limit")?
            .len()
            > 1024 * 1024
        {
            return Err("catalog_limit");
        }
        cancellation.check().map_err(|_| "operation_cancelled")?;
        if Instant::now() >= deadline {
            return Err("catalog_limit");
        }
        Ok(Snapshot {
            revision: uuid::Uuid::new_v4().to_string(),
            fingerprint: digest.finalize().into(),
            choices,
            notices,
            invalidated: false,
        })
    }
    pub(super) fn catalog_page(
        &mut self,
        params: CatalogParams,
        cancellation: &AgentCancellation,
    ) -> Result<Value, &'static str> {
        let scan = self.scan_catalog(cancellation);
        if params.cursor.is_some() {
            let stored = self.catalog.as_mut().ok_or("stale_catalog")?;
            let scanned = match scan {
                Ok(scanned) => scanned,
                Err("operation_cancelled") => return Err("operation_cancelled"),
                Err(_) => {
                    stored.invalidated = true;
                    return Err("stale_catalog");
                }
            };
            if stored.invalidated
                || Some(&stored.revision) != params.revision.as_ref()
                || stored.fingerprint != scanned.fingerprint
            {
                stored.invalidated = true;
                return Err("stale_catalog");
            }
        } else {
            self.catalog = None;
            self.catalog = Some(scan?);
        }
        let stored = self.catalog.as_ref().ok_or("stale_catalog")?;
        let start = match params.cursor {
            None => 0,
            Some(cursor) => cursor
                .strip_prefix(&format!("{}.", stored.revision))
                .and_then(|v| v.parse::<usize>().ok())
                .ok_or("stale_catalog")?,
        };
        if start > stored.choices.len() {
            return Err("stale_catalog");
        }
        let mut end = start;
        let mut bytes = 512;
        for entry in stored.choices[start..].iter().take(params.limit) {
            let cost = serde_json::to_vec(entry)
                .map_err(|_| "catalog_limit")?
                .len()
                + 1;
            if bytes + cost > 32_768 {
                break;
            }
            bytes += cost;
            end += 1;
        }
        if end == start && start < stored.choices.len() {
            return Err("catalog_limit");
        }
        let next = (end < stored.choices.len()).then(|| format!("{}.{}", stored.revision, end));
        let result = json!({"revision":stored.revision,"choices":&stored.choices[start..end],
            "next_cursor":next,"complete":next.is_none(),"notices":stored.notices,"source":"cache_only"});
        if serde_json::to_vec(&result)
            .map_err(|_| "catalog_limit")?
            .len()
            > 32_768
        {
            return Err("catalog_limit");
        }
        self.sync_selection_effort();
        Ok(result)
    }
    pub(super) fn validate_catalog_revision(
        &mut self,
        revision: &str,
        cancellation: &AgentCancellation,
    ) -> Result<(), &'static str> {
        let stored = self.catalog.as_ref().ok_or("stale_catalog")?;
        if stored.invalidated || stored.revision != revision {
            return Err("stale_catalog");
        }
        let scanned = match self.scan_catalog(cancellation) {
            Ok(scanned) => scanned,
            Err("operation_cancelled") => return Err("operation_cancelled"),
            Err(_) => {
                if let Some(stored) = self.catalog.as_mut() {
                    stored.invalidated = true;
                }
                return Err("stale_catalog");
            }
        };
        if self
            .catalog
            .as_ref()
            .is_none_or(|stored| stored.fingerprint != scanned.fingerprint)
        {
            if let Some(stored) = self.catalog.as_mut() {
                stored.invalidated = true;
            }
            return Err("stale_catalog");
        }
        Ok(())
    }
}