remem-ai 0.6.81

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use anyhow::{Context, Result};
use tokio::time::{Duration, Instant};

use super::admission::IntervalAdmission;
use crate::{db, maintenance};

pub(super) const CLEANUP_PROBE_INTERVAL: Duration = Duration::from_secs(60);

pub(super) fn enqueue_if_due(
    conn: &rusqlite::Connection,
    schedule: &mut IntervalAdmission,
    now: Instant,
) -> Result<Option<db::CleanupEnqueueDecision>> {
    if !schedule.is_due(now) {
        return Ok(None);
    }
    schedule.record_attempt(now);
    db::maybe_enqueue_cleanup_job(conn)
        .map(Some)
        .context("schedule automatic lifecycle cleanup")
}

pub(super) async fn execute_claimed(
    job: &db::Job,
    lease_owner: &str,
) -> Result<maintenance::CleanupExecution> {
    anyhow::ensure!(
        job.job_type == db::JobType::Cleanup,
        "dedicated cleanup executor received {} job",
        job.job_type.as_str()
    );
    let job_id = job.id;
    let lease_owner = lease_owner.to_string();
    tokio::task::spawn_blocking(move || {
        let conn = db::open_db().context("open database for automatic lifecycle cleanup")?;
        maintenance::execute_automatic_cleanup_job(
            &conn,
            job_id,
            &lease_owner,
            chrono::Utc::now().timestamp(),
        )
    })
    .await
    .context("join automatic lifecycle cleanup task")?
}

pub(super) fn safe_failure_message(error: &anyhow::Error) -> String {
    maintenance::safe_cleanup_error(error)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn once_schedule_probes_only_once() {
        let started = Instant::now();
        let mut schedule = IntervalAdmission::new(true, started, CLEANUP_PROBE_INTERVAL);

        assert!(schedule.is_due(started));
        schedule.record_attempt(started);
        assert!(!schedule.is_due(started + CLEANUP_PROBE_INTERVAL));
    }

    #[test]
    fn daemon_schedule_probes_once_per_minute() {
        let started = Instant::now();
        let mut schedule = IntervalAdmission::new(false, started, CLEANUP_PROBE_INTERVAL);

        assert!(schedule.is_due(started));
        schedule.record_attempt(started);
        assert!(!schedule.is_due(started + CLEANUP_PROBE_INTERVAL - Duration::from_millis(1)));
        assert!(schedule.is_due(started + CLEANUP_PROBE_INTERVAL));
    }

    #[test]
    fn cleanup_failure_message_is_bounded_and_redacted() {
        let error = anyhow::anyhow!("api_key=super-secret-{}", "x".repeat(2_000));

        let message = safe_failure_message(&error);

        assert!(message.contains("[REDACTED]"));
        assert!(!message.contains("super-secret"));
        assert!(message.len() <= 1_000);
    }
}