use std::borrow::Cow;
const TEXT_CLOSE: &str = "</text>";
const TAIL_FOLLOW_MARKERS: &[&str] = &[
"<parameter name=",
"<parameter",
"<invoke name=",
"<invoke",
"</invoke>",
"</function_calls>",
"<function_calls>",
];
const BARE_TAIL_MARKERS: &[&str] = &["<parameter name=", "<invoke name=", "<function_calls>"];
const MAX_BARE_TAIL: usize = 256;
pub(crate) fn sanitize_tool_call_artifacts(text: &str) -> Cow<'_, str> {
match artifact_cut_index(text) {
Some(cut) => {
let cleaned = text[..cut].trim_end();
tracing::warn!(
target: "yantrikdb::audit::ingest",
original_len = text.len(),
cleaned_len = cleaned.len(),
stripped_bytes = text.len() - cleaned.len(),
"stripped leaked tool-call serialization artifact from write",
);
Cow::Owned(cleaned.to_string())
}
None => Cow::Borrowed(text),
}
}
pub(crate) fn has_tool_call_artifact(text: &str) -> bool {
artifact_cut_index(text).is_some()
}
fn artifact_cut_index(text: &str) -> Option<usize> {
let mut best: Option<usize> = None;
let mut from = 0;
while let Some(rel) = text[from..].find(TEXT_CLOSE) {
let idx = from + rel;
let after = text[idx + TEXT_CLOSE.len()..].trim_start();
if TAIL_FOLLOW_MARKERS.iter().any(|m| after.starts_with(m)) {
best = Some(idx);
break;
}
from = idx + TEXT_CLOSE.len();
}
for marker in BARE_TAIL_MARKERS {
if let Some(idx) = text.find(marker) {
let earlier_than_best = best.map_or(true, |b| idx < b);
if earlier_than_best && is_short_trailing_tail(&text[idx..]) {
best = Some(idx);
}
}
}
best
}
fn is_short_trailing_tail(tail: &str) -> bool {
tail.len() <= MAX_BARE_TAIL && !tail.contains("\n\n")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clean_text_is_borrowed_unchanged() {
let s = "Alice is the engineering lead. Deadline is March 30.";
let out = sanitize_tool_call_artifacts(s);
assert!(
matches!(out, Cow::Borrowed(_)),
"clean text must not allocate"
);
assert_eq!(out, s);
assert!(!has_tool_call_artifact(s));
}
#[test]
fn strips_exact_corpus_signature() {
let s = "Real memory content here.</text>\n<parameter name=\"memory_type\">episodic";
let out = sanitize_tool_call_artifacts(s);
assert_eq!(out, "Real memory content here.");
assert!(has_tool_call_artifact(s));
}
#[test]
fn strips_multiple_trailing_parameters() {
let s = "Decision: use Postgres.</text>\n\
<parameter name=\"memory_type\">semantic<parameter name=\"importance\">0.8";
let out = sanitize_tool_call_artifacts(s);
assert_eq!(out, "Decision: use Postgres.");
}
#[test]
fn strips_bare_trailing_parameter_without_text_close() {
let s = "Some note worth keeping\n<parameter name=\"memory_type\">episodic";
let out = sanitize_tool_call_artifacts(s);
assert_eq!(out, "Some note worth keeping");
}
#[test]
fn strips_invoke_form() {
let s = "Content before the leak</text>\n<invoke name=\"remember\">";
let out = sanitize_tool_call_artifacts(s);
assert_eq!(out, "Content before the leak");
}
#[test]
fn tolerates_whitespace_between_close_and_marker() {
let s = "content</text> \n <parameter name=\"x\">y";
let out = sanitize_tool_call_artifacts(s);
assert_eq!(out, "content");
}
#[test]
fn preserves_legit_midbody_mention_of_marker() {
let s = "To fix the bug, strip <parameter name= fragments at write time.\n\n\
This second paragraph explains why the engine is the right boundary.";
let out = sanitize_tool_call_artifacts(s);
assert!(matches!(out, Cow::Borrowed(_)));
assert_eq!(out, s);
}
#[test]
fn preserves_standalone_text_close_in_prose() {
let s = "In the lesson I typed </text> to close the element, then continued.";
let out = sanitize_tool_call_artifacts(s);
assert!(matches!(out, Cow::Borrowed(_)));
assert_eq!(out, s);
}
#[test]
fn cuts_at_earliest_qualifying_close() {
let s = "I wrote </text> earlier as an example.</text>\n<parameter name=\"x\">y";
let out = sanitize_tool_call_artifacts(s);
assert_eq!(out, "I wrote </text> earlier as an example.");
}
#[test]
fn fully_mangled_input_collapses_to_empty() {
let s = "<invoke name=\"remember\">";
let out = sanitize_tool_call_artifacts(s);
assert_eq!(out, "");
}
#[test]
fn long_trailing_region_is_not_treated_as_artifact() {
let long_prose = "x".repeat(MAX_BARE_TAIL + 10);
let s = format!("Notes about <parameter name= in the protocol: {long_prose}");
let out = sanitize_tool_call_artifacts(&s);
assert_eq!(out, s);
}
}