use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use serde_json::Value;
const EXPAND_OPEN: &str = "<lc_expand:";
const EXPAND_CLOSE: char = '>';
pub(crate) const MIN_TEE_BYTES: usize = 512;
const CLEANUP_INTERVAL_SECS: u64 = 600;
fn tee_path(content: &str) -> Option<PathBuf> {
let dir = crate::core::paths::state_dir().ok()?.join("tee");
let hash = crate::core::hasher::hash_short(content);
Some(dir.join(format!("proxy_{hash}.log")))
}
fn maybe_cleanup(tee_dir: &Path) {
static LAST: AtomicU64 = AtomicU64::new(0);
let Ok(now) = SystemTime::now().duration_since(UNIX_EPOCH) else {
return;
};
let now = now.as_secs();
let last = LAST.load(Ordering::Relaxed);
if now.saturating_sub(last) < CLEANUP_INTERVAL_SECS {
return;
}
if LAST
.compare_exchange(last, now, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
crate::shell::cleanup_old_tee_logs(tee_dir);
}
}
pub(crate) fn persist(content: &str) -> Option<String> {
if content.len() < MIN_TEE_BYTES {
return None;
}
let path = tee_path(content)?;
let handle = path.to_string_lossy().to_string();
if !path.exists() {
if let Some(dir) = path.parent()
&& std::fs::create_dir_all(dir).is_ok()
{
maybe_cleanup(dir);
}
let masked = crate::core::redaction::redact_text(content);
let (redacted, _) = crate::core::secret_detection::scan_and_redact_from_config(&masked);
if std::fs::write(&path, redacted).is_ok() {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let _ = std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600));
}
}
}
Some(handle)
}
pub(crate) fn resolve_tee(id: &str) -> Option<PathBuf> {
let name = Path::new(id)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(id);
let hash = name.strip_prefix("proxy_").unwrap_or(name);
let hash = hash.strip_suffix(".log").unwrap_or(hash);
if hash.len() != 16 || !hash.bytes().all(|b| b.is_ascii_hexdigit()) {
return None;
}
let path = crate::core::paths::state_dir()
.ok()?
.join("tee")
.join(format!("proxy_{hash}.log"));
path.is_file().then_some(path)
}
pub(crate) fn inband_marker(handle: &str) -> Option<String> {
let name = Path::new(handle).file_name().and_then(|n| n.to_str())?;
let hash = name.strip_prefix("proxy_")?.strip_suffix(".log")?;
(hash.len() == 16 && hash.bytes().all(|b| b.is_ascii_hexdigit()))
.then(|| format!("{EXPAND_OPEN}{hash}{EXPAND_CLOSE}"))
}
pub(crate) fn inband_locator(handle: &str) -> Option<String> {
crate::core::config::Config::load()
.proxy
.ccr_inband_enabled()
.then(|| inband_marker(handle))
.flatten()
}
fn recover(hash: &str) -> Option<String> {
if hash.len() != 16 || !hash.bytes().all(|b| b.is_ascii_hexdigit()) {
return None;
}
std::fs::read_to_string(resolve_tee(hash)?).ok()
}
fn splice_str(s: &str) -> Option<String> {
if !s.contains(EXPAND_OPEN) {
return None;
}
let mut out = String::with_capacity(s.len());
let mut rest = s;
let mut changed = false;
while let Some(pos) = rest.find(EXPAND_OPEN) {
let after = &rest[pos + EXPAND_OPEN.len()..];
match after.find(EXPAND_CLOSE) {
Some(end) => {
let hash = &after[..end];
if let Some(original) = recover(hash) {
out.push_str(&rest[..pos]);
out.push_str(&original);
rest = &after[end + EXPAND_CLOSE.len_utf8()..];
changed = true;
} else {
out.push_str(&rest[..pos + EXPAND_OPEN.len()]);
rest = after;
}
}
None => break,
}
}
out.push_str(rest);
changed.then_some(out)
}
pub(crate) fn splice_inband_in_place(value: &mut Value) -> bool {
match value {
Value::String(s) => {
if let Some(spliced) = splice_str(s) {
*s = spliced;
true
} else {
false
}
}
Value::Array(items) => {
let mut changed = false;
for item in items {
changed |= splice_inband_in_place(item);
}
changed
}
Value::Object(map) => {
let mut changed = false;
for (_, v) in map.iter_mut() {
changed |= splice_inband_in_place(v);
}
changed
}
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn big(seed: &str) -> String {
format!("{seed}\n").repeat(40)
}
#[test]
fn handle_is_content_addressed_and_deterministic() {
let _lock = crate::core::data_dir::test_env_lock();
let content = big("file body line");
let a = persist(&content).expect("persisted");
let b = persist(&content).expect("persisted again");
assert_eq!(
a, b,
"same content must map to the same handle (cache-safe)"
);
assert!(a.contains("proxy_"), "handle is a proxy tee path: {a}");
let other = persist(&big("different body")).expect("persisted");
assert_ne!(a, other, "different content must get a different handle");
}
#[test]
fn persisted_original_is_recoverable() {
let _lock = crate::core::data_dir::test_env_lock();
let content = big("recoverable verbatim line");
let handle = persist(&content).expect("persisted");
let on_disk = std::fs::read_to_string(&handle).expect("tee file readable");
assert!(
on_disk.contains("recoverable verbatim line"),
"the verbatim original must be retrievable from the handle"
);
}
#[test]
fn small_content_gets_no_handle() {
let _lock = crate::core::data_dir::test_env_lock();
assert!(
persist("too small to bother").is_none(),
"below MIN_TEE_BYTES there is no handle (the caller keeps its plain stub)"
);
}
#[test]
fn resolve_tee_accepts_every_stub_form() {
let _lock = crate::core::data_dir::test_env_lock();
let content = big("resolvable tee body");
let handle = persist(&content).expect("persisted");
let hash = crate::core::hasher::hash_short(&content);
for form in [
handle.clone(),
format!("proxy_{hash}.log"),
format!("proxy_{hash}"),
hash.clone(),
] {
let resolved = resolve_tee(&form).unwrap_or_else(|| panic!("must resolve {form}"));
assert_eq!(
resolved.to_string_lossy(),
handle,
"form {form} -> {handle}"
);
}
}
#[test]
fn resolve_tee_rejects_nontee_and_traversal_ids() {
let _lock = crate::core::data_dir::test_env_lock();
assert!(resolve_tee("/etc/passwd").is_none());
assert!(resolve_tee("../../secret").is_none());
assert!(resolve_tee("proxy_nothex0000000.log").is_none());
assert!(resolve_tee("deadbeefdeadbeef").is_none());
}
#[test]
fn inband_marker_is_derived_from_handle() {
let _lock = crate::core::data_dir::test_env_lock();
let content = big("inband marker body");
let handle = persist(&content).expect("persisted");
let hash = crate::core::hasher::hash_short(&content);
assert_eq!(inband_marker(&handle), Some(format!("<lc_expand:{hash}>")));
assert!(inband_marker("/tmp/not-a-tee.txt").is_none());
}
#[test]
fn splice_replaces_marker_with_verbatim_original() {
let _lock = crate::core::data_dir::test_env_lock();
let content = big("the historical verbatim line");
let handle = persist(&content).expect("persisted");
let marker = inband_marker(&handle).expect("marker");
let mut doc = serde_json::json!({
"messages": [{ "role": "assistant", "content": format!("recall {marker} please") }]
});
assert!(splice_inband_in_place(&mut doc), "a marker must splice");
let spliced = doc["messages"][0]["content"].as_str().unwrap();
assert!(
spliced.contains("the historical verbatim line"),
"verbatim original must be spliced in: {spliced}"
);
assert!(
!spliced.contains("<lc_expand:"),
"the marker must be consumed, not left behind"
);
}
#[test]
fn splice_is_byte_identical_no_op_without_marker() {
let _lock = crate::core::data_dir::test_env_lock();
let mut doc = serde_json::json!({
"messages": [{ "role": "user", "content": "no marker here" }],
"system": "plain"
});
let before = doc.clone();
assert!(
!splice_inband_in_place(&mut doc),
"no marker → must report no change"
);
assert_eq!(
doc, before,
"marker-less body must stay byte-identical (cache-safe)"
);
}
#[test]
fn splice_keeps_unresolvable_marker_verbatim() {
let _lock = crate::core::data_dir::test_env_lock();
let mut doc = serde_json::json!({ "t": "before <lc_expand:deadbeefdeadbeef> after" });
assert!(!splice_inband_in_place(&mut doc));
assert_eq!(
doc["t"].as_str().unwrap(),
"before <lc_expand:deadbeefdeadbeef> after"
);
}
#[test]
fn splice_recurses_and_handles_multiple_markers() {
let _lock = crate::core::data_dir::test_env_lock();
let a = big("first recovered body");
let b = big("second recovered body");
let ma = inband_marker(&persist(&a).unwrap()).unwrap();
let mb = inband_marker(&persist(&b).unwrap()).unwrap();
let mut doc = serde_json::json!({
"contents": [
{ "parts": [{ "text": format!("{ma} and {mb}") }] }
]
});
assert!(splice_inband_in_place(&mut doc));
let text = doc["contents"][0]["parts"][0]["text"].as_str().unwrap();
assert!(text.contains("first recovered body"));
assert!(text.contains("second recovered body"));
assert!(!text.contains("<lc_expand:"));
}
}