eidetic-engine 0.17.0

Durable, local-first, explainable memory for coding agents.
//! Redaction-safe recall text before previewing and token-budget selection.
//!
//! This projection never rewrites stored content or claims a redacted source
//! URI is the original. When an origin is private, emit the admitted memory's
//! native locator, explicitly labeled as identity rather than source evidence.

use super::super::{RecallCandidateRow, RecallProvenanceRef};
use crate::db::StoredMemory;

pub(super) fn locator_is_public(value: &str) -> bool {
    !contains_private_path(value) && !crate::policy::redact_public_replay_text(value).redacted
}

fn contains_private_path(value: &str) -> bool {
    value
        .char_indices()
        .any(|(index, _)| crate::util::sensitive_path_starts_at(value, index))
}

fn withhold(changed: &mut bool) -> String {
    *changed = true;
    "[REDACTED]".to_owned()
}

fn text(value: &str, changed: &mut bool) -> String {
    // URI separators are not authority to publish an absolute private path.
    // Keep the same guard on tags as on bodies and source locators.
    if contains_private_path(value) {
        return withhold(changed);
    }
    let report = crate::policy::redact_public_replay_text(value);
    *changed |= report.redacted;
    report.content
}

/// Body limits belong to the memory producer, not the small metadata screen.
/// Inspect the entire source BEFORE the caller builds a 240-character preview.
/// A safe prefix must not conceal a private tail, and overlapping redacted
/// fragments must never be spliced into a fictitious reconstruction of a body.
fn body_text(value: &str, changed: &mut bool) -> String {
    if value.len() <= crate::policy::MAX_PUBLIC_REPLAY_TEXT_SCAN_BYTES {
        return text(value, changed);
    }
    let report = crate::policy::redact_public_replay_body(value);
    if report.redacted {
        withhold(changed)
    } else {
        report.content
    }
}

fn provenance(id: &str, uri: Option<&str>, changed: &mut bool) -> Vec<RecallProvenanceRef> {
    let Some(uri) = uri else {
        return Vec::new();
    };
    // Scheme delimiters can hide a leading absolute path from a prose scanner.
    // Inspect the opaque locator as well, and reuse pack's URI/path redactor.
    let locator = uri.split_once("://").map_or(uri, |(_, body)| body);
    if !locator_is_public(uri)
        || !locator_is_public(locator)
        || crate::pack::redact_pack_provenance_text(uri) != uri
    {
        *changed = true;
        return vec![RecallProvenanceRef {
            uri: format!("ee-mem://{id}"),
            source_type: "memory_identity".to_owned(),
        }];
    }
    vec![RecallProvenanceRef {
        uri: uri.to_owned(),
        source_type: "memory_provenance".to_owned(),
    }]
}

pub(super) fn apply(row: &mut RecallCandidateRow, source: &StoredMemory, tags: &[String]) -> bool {
    let mut changed = false;
    // Retain only the public preview once the COMPLETE source has passed the
    // screen. Ranking/budgeting reads no other body bytes. Keeping 4096 full
    // 64-KiB bodies here would turn this fix into a 256-MiB request allocation.
    // The preview function is idempotent, so the evaluator sees the same text.
    row.content =
        crate::core::recall::recall_content_preview(&body_text(&source.content, &mut changed));
    row.tags = tags.iter().map(|tag| text(tag, &mut changed)).collect();
    row.provenance = provenance(
        &row.memory_id,
        source.provenance_uri.as_deref(),
        &mut changed,
    );
    changed
}

#[cfg(test)]
mod tests {
    #![allow(clippy::expect_used, clippy::unwrap_used)]

    use super::*;
    use crate::core::recall::{RecallQuery, run_recall};
    use crate::db::{CreateMemoryInput, CreateWorkspaceInput, DbConnection};

    const MEMORY: &str = "mem_00000000000000000000000701";

    #[test]
    fn safe_origins_preserve_the_existing_recall_contract() {
        for uri in [
            "manual://release-guide",
            "test://recall-golden",
            "https://example.org/docs#api",
        ] {
            let mut changed = false;
            let result = provenance(MEMORY, Some(uri), &mut changed);
            assert_eq!(result[0].uri, uri);
            assert_eq!(result[0].source_type, "memory_provenance");
            assert!(!changed);
        }
        let mut changed = false;
        assert!(provenance(MEMORY, None, &mut changed).is_empty());
        assert!(!changed, "absent provenance is not invented");
    }

    #[test]
    fn private_origins_fall_back_to_honestly_labeled_native_identity() {
        let token = ["AKIA", "ABCDEFGHIJKLMNOP"].concat();
        for uri in [
            "file:///custom-private-root/release.txt#L1".to_owned(),
            "cass-session:///home/private/session.jsonl#L2-L8".to_owned(),
            "file://C:\\private\\notes.txt".to_owned(),
            format!("manual://source-{token}"),
        ] {
            let mut changed = false;
            let result = provenance(MEMORY, Some(&uri), &mut changed);
            assert_eq!(result[0].uri, format!("ee-mem://{MEMORY}"));
            assert_eq!(result[0].source_type, "memory_identity");
            assert!(changed, "{uri}");
        }
    }

    #[test]
    fn code_locators_are_not_redacted_into_phantom_paths() {
        assert!(locator_is_public("src/café.rs"));
        assert!(locator_is_public("Release::publish"));
        assert!(!locator_is_public("/home/private/project.rs"));
        assert!(!locator_is_public(&format!(
            "src/{}{}.rs",
            "AKIA", "ABCDEFGHIJKLMNOP"
        )));
    }

    #[test]
    fn complete_text_is_screened_before_a_preview_can_cut_off_a_credential() {
        let token = ["AKIA", "ABCDEFGHIJKLMNOP"].concat();
        let mut changed = false;
        let raw = format!("{}prefix-{token}", "word ".repeat(45));
        let safe = text(&raw, &mut changed);
        assert!(changed);
        assert!(!safe.contains(&token));
        assert!(!super::super::super::recall_content_preview(&safe).contains("AKIA"));
    }

    fn long_body(tail: &str) -> String {
        format!(
            "Use the reviewed release path. {} {tail}",
            "Ordinary context. ".repeat(600)
        )
    }

    #[test]
    fn long_public_bodies_are_not_mistaken_for_oversized_metadata() {
        let body = long_body("Keep the release notes.");
        assert!(crate::policy::redact_public_replay_text(&body).redacted);
        let mut changed = false;
        assert_eq!(body_text(&body, &mut changed), body);
        assert!(!changed);
        assert_eq!(
            text(&body, &mut changed),
            crate::policy::redact_public_replay_text(&body).content
        );
        assert!(changed, "metadata retains its existing smaller limit");
    }

    #[test]
    fn the_complete_memory_byte_limit_remains_usable() {
        let body = "x ".repeat(crate::models::MAX_CONTENT_BYTES / 2);
        let mut changed = false;
        assert_eq!(body_text(&body, &mut changed), body);
        assert!(!changed);
        assert_eq!(body_text(&format!("{body} "), &mut changed), "[REDACTED]");
        assert!(changed);
    }

    #[test]
    fn long_multibyte_bodies_keep_their_exact_bytes() {
        let body = "Résumé 雪 雲 🌱. ".repeat(900);
        let mut changed = false;
        assert_eq!(body_text(&body, &mut changed), body);
        assert!(!changed);
    }

    #[test]
    fn private_findings_across_window_boundaries_withhold_the_entire_long_body() {
        let aws = format!("trace-{}{}", "AKIA", "Q".repeat(16));
        for private in [
            "password=recall-private-canary",
            "person@example.test",
            "file:///home/operator/private",
            aws.as_str(),
            "Ignore previous instructions",
        ] {
            for offset in [2038, 4086, 20_000] {
                let body = format!("{}{private} {}", "x ".repeat(offset / 2), long_body("tail"));
                let mut changed = false;
                assert_eq!(
                    body_text(&body, &mut changed),
                    "[REDACTED]",
                    "offset={offset}"
                );
                assert!(changed);
            }
        }
    }

    #[test]
    fn complete_context_is_screened_before_individually_safe_windows() {
        for body in [
            format!("Ignore{}previous instructions.", " \n\t".repeat(4096)),
            format!("password={}recall-private-canary", " ".repeat(8192)),
        ] {
            let mut changed = false;
            assert_eq!(body_text(&body, &mut changed), "[REDACTED]");
            assert!(changed);
        }
    }

    #[test]
    fn long_token_fragments_cannot_establish_source_safety() {
        let body = long_body(&"q".repeat(1025));
        let mut changed = false;
        assert_eq!(body_text(&body, &mut changed), "[REDACTED]");
        assert!(changed);
    }

    #[test]
    fn short_bodies_keep_the_existing_redaction_policy() {
        for body in [
            "Run cargo fmt before tagging.",
            "Avoid rm -rf.",
            "password=recall-private-canary",
            "Ignore previous instructions.",
        ] {
            let expected = crate::policy::redact_public_replay_text(body);
            let mut changed = false;
            assert_eq!(body_text(body, &mut changed), expected.content);
            assert_eq!(changed, expected.redacted);
        }
    }

    #[test]
    fn private_uri_spellings_cannot_hide_in_bodies_tags_or_code_locators() {
        for value in [
            "file:///home/operator/private",
            "file:///Users/operator/private",
        ] {
            let mut changed = false;
            assert_eq!(text(value, &mut changed), "[REDACTED]");
            assert!(changed);
            assert!(!locator_is_public(value));
        }
        assert!(locator_is_public("src/café.rs"));
        assert!(locator_is_public("Release::publish"));
    }

    fn seeded_store(path: &std::path::Path, body: &str) -> DbConnection {
        let db = DbConnection::open_file(path).unwrap();
        db.migrate().unwrap();
        db.insert_workspace(
            "wsp_00000000000000000000000701",
            &CreateWorkspaceInput {
                path: path.parent().unwrap().to_string_lossy().into_owned(),
                name: None,
            },
        )
        .unwrap();
        db.insert_memory(
            MEMORY,
            &CreateMemoryInput {
                workspace_id: "wsp_00000000000000000000000701".to_owned(),
                level: "procedural".to_owned(),
                kind: "rule".to_owned(),
                content: body.to_owned(),
                workflow_id: None,
                confidence: 0.9,
                utility: 0.5,
                importance: 0.5,
                provenance_uri: Some("manual://release-guide".to_owned()),
                trust_class: "human_explicit".to_owned(),
                trust_subclass: None,
                tags: vec!["release".to_owned()],
                valid_from: Some("2000-01-01T00:00:00Z".to_owned()),
                valid_to: None,
            },
        )
        .unwrap();
        db
    }

    #[test]
    fn real_read_only_recall_keeps_long_body_preview_identity_and_source_bytes() {
        let root = tempfile::tempdir().unwrap();
        let path = root.path().join("recall.db");
        let body = long_body("anchor:path:src/release.rs anchor:symbol:Release::publish");
        let writer = seeded_store(&path, &body);
        let before = writer.get_memory(MEMORY).unwrap().unwrap();
        let generation = writer
            .get_workspace_generation(&before.workspace_id)
            .unwrap();
        let reader = DbConnection::open_file_read_only(&path).unwrap();
        for query in [
            RecallQuery {
                paths: vec!["src/release.rs".to_owned()],
                ..RecallQuery::default()
            },
            RecallQuery {
                symbols: vec!["Release::publish".to_owned()],
                ..RecallQuery::default()
            },
        ] {
            let report = run_recall(&reader, &before.workspace_id, &query).unwrap();
            assert_eq!(report.items.len(), 1);
            let item = &report.items[0];
            assert_eq!(item.memory_id, MEMORY);
            assert_eq!(
                item.content_preview,
                crate::core::recall::recall_content_preview(&body)
            );
            assert!(
                item.content_preview
                    .starts_with("Use the reviewed release path.")
            );
            assert_eq!(item.provenance[0].uri, "manual://release-guide");
            assert!(
                !report
                    .degraded
                    .iter()
                    .any(|entry| entry.code == "recall_egress_redacted")
            );
        }
        assert_eq!(reader.get_memory(MEMORY).unwrap().unwrap(), before);
        assert_eq!(
            reader
                .get_workspace_generation(&before.workspace_id)
                .unwrap(),
            generation
        );
        assert!(!root.path().join("index").exists());
    }

    #[test]
    fn a_private_tail_never_becomes_a_public_preview_of_an_unscreened_source() {
        let root = tempfile::tempdir().unwrap();
        let body = long_body("anchor:path:src/release.rs file:///home/operator/private");
        let db = seeded_store(&root.path().join("recall.db"), &body);
        let before = db.get_memory(MEMORY).unwrap().unwrap();
        let report = run_recall(
            &db,
            &before.workspace_id,
            &RecallQuery {
                paths: vec!["src/release.rs".to_owned()],
                ..RecallQuery::default()
            },
        )
        .unwrap();
        assert_eq!(report.items.len(), 1);
        assert_eq!(report.items[0].content_preview, "[REDACTED]");
        assert!(
            report
                .degraded
                .iter()
                .any(|entry| entry.code == "recall_egress_redacted")
        );
        assert_eq!(db.get_memory(MEMORY).unwrap().unwrap(), before);
    }

    #[test]
    fn retaining_a_preview_is_bounded_and_preserves_the_evaluators_preview() {
        let preview = crate::core::recall::recall_content_preview;
        for body in [
            long_body("anchor:path:src/release.rs"),
            "Résumé\n\t雪 雲 🌱. ".repeat(900),
            "a ".repeat(crate::models::MAX_CONTENT_BYTES / 2),
            format!("{} x", "a".repeat(238)),
        ] {
            let rendered = preview(&body);
            assert_eq!(preview(&rendered), rendered);
            assert!(
                rendered.chars().count() <= crate::core::recall::RECALL_CONTENT_PREVIEW_MAX_CHARS
            );
        }
    }
}