remem-ai 0.6.93

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use anyhow::{Context, Result};
use rusqlite::{params, Connection};

use crate::memory::activation::{
    ActivationActorKind, ActivationPoisoningVerdict, ActivationProvenanceKind, ActivationRouteKind,
    ActiveMemoryRoute, ActiveMemoryWriteRequest, ExpectedActiveMemory,
};
use crate::memory::poisoning::SourceTrustClass;

use super::super::merge::MergeResult;

pub(super) struct PayloadIdentities {
    pub(super) current: String,
    pub(super) replay_candidates: Vec<String>,
    pub(super) caller_superseded_ids: Vec<i64>,
}

pub(super) fn payload_identities(project: &str, result: &MergeResult) -> Result<PayloadIdentities> {
    let mut seen_ids = std::collections::HashSet::new();
    let ordered_ids = result
        .superseded_ids
        .iter()
        .copied()
        .filter(|id| seen_ids.insert(*id))
        .collect::<Vec<_>>();
    if ordered_ids.iter().any(|id| *id <= 0) {
        anyhow::bail!("dream superseded memory ids must be positive integers");
    }
    let sorted_ids = ordered_ids
        .iter()
        .copied()
        .collect::<std::collections::BTreeSet<_>>();
    let current = payload_sha256_for_ids(project, result, &sorted_ids)?;
    let mut replay_candidates = vec![current.clone()];
    let mut seen_payloads = std::collections::HashSet::from([current.clone()]);
    for excluded in std::iter::once(None).chain(ordered_ids.iter().copied().map(Some)) {
        let legacy_ids = ordered_ids
            .iter()
            .copied()
            .filter(|id| Some(*id) != excluded)
            .collect::<Vec<_>>();
        let payload = payload_sha256_for_ids(project, result, &legacy_ids)?;
        if seen_payloads.insert(payload.clone()) {
            replay_candidates.push(payload);
        }
    }
    Ok(PayloadIdentities {
        current,
        replay_candidates,
        caller_superseded_ids: sorted_ids.into_iter().collect(),
    })
}

fn payload_sha256_for_ids(
    project: &str,
    result: &MergeResult,
    superseded_ids: &impl serde::Serialize,
) -> Result<String> {
    let superseded_json = serde_json::to_string(superseded_ids)?;
    Ok(crate::memory::activation::payload_sha256(&[
        project,
        &result.topic_key,
        &result.title,
        &result.content,
        &result.memory_type,
        &superseded_json,
    ]))
}

pub(super) fn activation_request(
    project: &str,
    payload_sha256: String,
    expected_memory: ExpectedActiveMemory,
    superseded_ids: Vec<i64>,
) -> ActiveMemoryWriteRequest {
    ActiveMemoryWriteRequest {
        activation_id: crate::memory::activation::activation_id_from_key(
            "dream-consolidation",
            &payload_sha256,
        ),
        route_kind: ActivationRouteKind::DreamConsolidation,
        actor_kind: ActivationActorKind::AutomaticWorker,
        source_operation: "dream_consolidation".to_string(),
        source_trust: SourceTrustClass::ExternalContent,
        result_source_trust: SourceTrustClass::ExternalContent,
        source_project: project.to_string(),
        route: ActiveMemoryRoute {
            project: project.to_string(),
            branch: None,
            scope: "project".to_string(),
            owner_scope: "repo".to_string(),
            owner_key: project.to_string(),
            target_project: Some(project.to_string()),
        },
        provenance_kind: ActivationProvenanceKind::Generated,
        provenance_ref: format!("dream-generated:{payload_sha256}"),
        payload_sha256,
        expected_memory,
        poisoning_verdict: ActivationPoisoningVerdict::UpstreamValidated,
        superseded_ids,
    }
}

pub(super) fn reason(activation_id: &str) -> String {
    format!("dream consolidation applied activation={activation_id}")
}

pub(super) fn id_for_activation(
    conn: &Connection,
    memory_id: i64,
    activation_id: &str,
) -> Result<i64> {
    let reason = reason(activation_id);
    conn.query_row(
        "SELECT id FROM memory_operation_log
         WHERE source = 'dream' AND result_memory_id = ?1 AND reason = ?2",
        params![memory_id, reason],
        |row| row.get(0),
    )
    .context("dream replay is missing its activation-bound operation audit")
}