magi-code 0.105.1

Repository-aware CLI coding agent for terminal work
Documentation
use crate::{
    context::ConversationReplayLimits,
    providers::ProviderRequest,
    sessions::{Session, SessionEvent, SessionEventKind},
};
use sha2::{Digest, Sha256};

/// One in-memory continuation snapshot per session. Never persisted or shared with children.
pub(crate) struct CachedCompactionRequest {
    provider: String,
    request: ProviderRequest,
    history_digest: [u8; 32],
}

impl CachedCompactionRequest {
    pub(crate) fn remember(session: &Session, provider: &str, request: ProviderRequest) {
        let generation = session.replay_generation();
        let limits = ConversationReplayLimits::default();
        let Ok(snapshot) = session.read_events_tolerant_bounded(limits.max_lines, limits.max_bytes)
        else {
            return;
        };
        let Some(history_digest) = history_digest(&snapshot.events) else {
            return;
        };
        if !snapshot.diagnostics.is_empty() || session.replay_generation() != generation {
            return;
        }
        if let Ok(mut cached) = session.compaction_request.lock() {
            *cached = Some(Self {
                provider: provider.to_string(),
                request,
                history_digest,
            });
        }
    }

    pub(super) fn take_matching(
        session: &Session,
        provider: &str,
        model: &str,
        events: &[SessionEvent],
    ) -> Option<ProviderRequest> {
        let cached = session.compaction_request.lock().ok()?.take()?;
        (cached.provider == provider
            && cached.request.model == model
            && Some(cached.history_digest) == history_digest(events))
        .then_some(cached.request)
    }
}

fn history_digest(events: &[SessionEvent]) -> Option<[u8; 32]> {
    let mut digest = Sha256::new();
    for event in events {
        // Usage/title/display appends can follow a settled turn. Keep all execution,
        // assessment, scope and recovery records in the compatibility fingerprint.
        if matches!(
            event.kind(),
            Some(
                SessionEventKind::SessionUsage
                    | SessionEventKind::SessionActiveTime
                    | SessionEventKind::SessionTitle
                    | SessionEventKind::ContextCache
                    | SessionEventKind::ProviderStreamTrace
                    | SessionEventKind::HookLifecycle
                    | SessionEventKind::HookDiagnostic
                    | SessionEventKind::ToolDisplayResult
                    | SessionEventKind::ToolAppliedPreview
            )
        ) {
            continue;
        }
        digest.update(serde_json::to_vec(event).ok()?);
        digest.update(b"\n");
    }
    Some(digest.finalize().into())
}