//! Tests that `ToolSummary` is indexed as kind `summary` and found by `recall`. Do not edit. #[path = "support/logs.rs"] mod logs; #[path = "support/tmp.rs"] mod tmp; use bxcore::{CallId, DataClass, LogRecord, RecallArgs, Timestamp}; use logs::{assistant, result, user, write_log}; use memoryd::files::Store; use memoryd::index::Index; use memoryd::recall::recall; use tmp::TempDir; fn now() -> Timestamp { Timestamp::parse("2026-09-24T21:00:00.000Z").unwrap() } fn summary(minute: u32, content: &str, untrusted: bool) -> LogRecord { LogRecord::ToolSummary { time: logs::at(minute), call: CallId(1), tool_call_id: "c0".to_string(), content: content.to_string(), class: DataClass::Public, untrusted, } } struct Setup { dir: TempDir, store: Store, index: Index, } fn setup(tag: &str) -> Setup { let dir = TempDir::new(tag); let starter = dir.write("etc/soul.md", "Soul.\n"); let store = Store::new(&dir.path().join("memory")); store.init(&starter).unwrap(); let index = Index::open(&dir.path().join("index/memory.sqlite3"), DataClass::Private).unwrap(); Setup { dir, store, index } } fn ask(s: &Setup, query: &str) -> memoryd::recall::Recalled { let args = RecallArgs { query: query.to_string(), k: 10, }; recall(&s.index, &s.store, &args, "mm-now", now()).unwrap() } #[test] fn a_tool_summary_is_found_as_kind_summary() { let s = setup("summary-found"); write_log( s.dir.path(), "mm-a", &[ user(2, "fetch the launch page"), assistant( 3, None, &[("http_fetch", r#"{"url":"https://example.org"}"#)], ), result(4, "raw html body about tea", DataClass::Public, false), summary(5, "the page is about tea and the launch window", false), ], ); s.index .catch_up(&s.dir.path().join("sessions"), &s.store.path("notes.md")) .unwrap(); let got = ask(&s, "tea"); assert!( got.text .contains("the page is about tea and the launch window"), "{got:?}" ); assert!( got.text.contains("memory · session mm-a"), "tagged as a session hit: {}", got.text ); assert!(!got.untrusted); } #[test] fn the_raw_tool_result_is_still_not_indexed() { let s = setup("summary-no-raw"); write_log( s.dir.path(), "mm-a", &[ user(2, "fetch"), assistant(3, None, &[("http_fetch", "{}")]), result(4, "UNIQUE_RAW_BODY_XYZ", DataClass::Public, false), summary(5, "a short account of the page", false), ], ); s.index .catch_up(&s.dir.path().join("sessions"), &s.store.path("notes.md")) .unwrap(); let got = ask(&s, "UNIQUE_RAW_BODY_XYZ"); assert!( got.text.contains("(no matches"), "raw tool result stays unindexed: {}", got.text ); } #[test] fn an_untrusted_summary_taints_the_recall() { let s = setup("summary-taint"); write_log( s.dir.path(), "mm-a", &[ user(2, "fetch"), assistant(3, None, &[("http_fetch", "{}")]), result(4, "page", DataClass::Public, true), summary(5, "the fetched page mentions the launch window", true), ], ); s.index .catch_up(&s.dir.path().join("sessions"), &s.store.path("notes.md")) .unwrap(); let got = ask(&s, "launch"); assert!(got.untrusted, "{got:?}"); assert!(got.text.contains("memory-tainted"), "{}", got.text); } #[test] fn a_trusted_summary_does_not_taint() { let s = setup("summary-trusted"); write_log( s.dir.path(), "mm-a", &[ user(2, "fetch"), assistant(3, None, &[("http_fetch", "{}")]), result(4, "page", DataClass::Public, false), summary(5, "the fetched page mentions the launch window", false), ], ); s.index .catch_up(&s.dir.path().join("sessions"), &s.store.path("notes.md")) .unwrap(); let got = ask(&s, "launch"); assert!(!got.untrusted, "{got:?}"); assert!(got.text.contains("memory · session mm-a"), "{}", got.text); assert!(!got.text.contains("memory-tainted"), "{}", got.text); } #[test] fn a_secret_summary_is_skipped_when_the_index_is_private() { let s = setup("summary-secret"); write_log( s.dir.path(), "mm-a", &[ user(2, "fetch"), assistant(3, None, &[("http_fetch", "{}")]), LogRecord::ToolResult { time: logs::at(4), call: CallId(1), tool_call_id: "c0".to_string(), content: "secret page".to_string(), class: DataClass::Secret, untrusted: false, truncated: false, }, LogRecord::ToolSummary { time: logs::at(5), call: CallId(1), tool_call_id: "c0".to_string(), content: "SECRET_SUMMARY_PHRASE".to_string(), class: DataClass::Secret, untrusted: false, }, ], ); s.index .catch_up(&s.dir.path().join("sessions"), &s.store.path("notes.md")) .unwrap(); let got = ask(&s, "SECRET_SUMMARY_PHRASE"); assert!( got.text.contains("(no matches"), "secret summary above max_class: {}", got.text ); }