1use super::{
2 CrpMode, HookPoint, PluginManager, ReadMode, ReadOutput, ReadTuning, SessionCache,
3 count_tokens, dedup_hook, handle_with_options_inner, kernel, protocol,
4};
5const MAX_RELAY_CONTENT_BYTES: usize = 8192;
6
7const RELAY_ELIGIBLE_MODES: &[&str] = &["map", "map:v2", "signatures", "signatures:v2"];
9
10fn relay_eligible_content(result: &ReadOutput) -> (Option<&str>, Option<&str>) {
12 let mode = result.resolved_mode.as_str();
13 if RELAY_ELIGIBLE_MODES.iter().any(|m| mode.starts_with(m))
14 && result.content.len() <= MAX_RELAY_CONTENT_BYTES
15 {
16 (Some(&result.content), Some(mode))
17 } else {
18 (None, None)
19 }
20}
21pub fn handle(cache: &mut SessionCache, path: &str, mode: &str, crp_mode: CrpMode) -> String {
23 handle_with_options(cache, path, mode, false, crp_mode, None)
24}
25
26pub fn handle_fresh(cache: &mut SessionCache, path: &str, mode: &str, crp_mode: CrpMode) -> String {
28 handle_with_options(cache, path, mode, true, crp_mode, None)
29}
30
31pub fn handle_with_task(
33 cache: &mut SessionCache,
34 path: &str,
35 mode: &str,
36 crp_mode: CrpMode,
37 task: Option<&str>,
38) -> String {
39 let mut result = handle_with_options(cache, path, mode, false, crp_mode, task);
40 kernel::enrich_with_kernel(&mut result, task);
41 result
42}
43
44pub fn handle_with_task_resolved(
46 cache: &mut SessionCache,
47 path: &str,
48 mode: &str,
49 crp_mode: CrpMode,
50 task: Option<&str>,
51) -> ReadOutput {
52 handle_with_options_resolved(
53 cache,
54 path,
55 mode,
56 false,
57 crp_mode,
58 task,
59 ReadTuning::resolve(None, &[]),
60 )
61}
62
63pub fn handle_with_task_resolved_tuned(
67 cache: &mut SessionCache,
68 path: &str,
69 mode: &str,
70 crp_mode: CrpMode,
71 task: Option<&str>,
72 aggressiveness: Option<f64>,
73 protect: &[String],
74) -> ReadOutput {
75 handle_with_options_resolved(
76 cache,
77 path,
78 mode,
79 false,
80 crp_mode,
81 task,
82 ReadTuning::resolve(aggressiveness, protect),
83 )
84}
85
86#[allow(clippy::too_many_arguments)]
89pub fn handle_with_preread(
90 cache: &mut SessionCache,
91 path: &str,
92 mode: &str,
93 fresh: bool,
94 crp_mode: CrpMode,
95 task: Option<&str>,
96 aggressiveness: Option<f64>,
97 protect: &[String],
98 preread: String,
99) -> ReadOutput {
100 handle_with_options_resolved_preread(
101 cache,
102 path,
103 mode,
104 fresh,
105 crp_mode,
106 task,
107 ReadTuning::resolve(aggressiveness, protect),
108 Some(preread),
109 )
110}
111
112pub fn handle_fresh_with_task(
114 cache: &mut SessionCache,
115 path: &str,
116 mode: &str,
117 crp_mode: CrpMode,
118 task: Option<&str>,
119) -> String {
120 handle_with_options(cache, path, mode, true, crp_mode, task)
121}
122
123pub fn handle_fresh_with_task_resolved(
125 cache: &mut SessionCache,
126 path: &str,
127 mode: &str,
128 crp_mode: CrpMode,
129 task: Option<&str>,
130) -> ReadOutput {
131 handle_with_options_resolved(
132 cache,
133 path,
134 mode,
135 true,
136 crp_mode,
137 task,
138 ReadTuning::resolve(None, &[]),
139 )
140}
141
142pub fn handle_fresh_with_task_resolved_tuned(
144 cache: &mut SessionCache,
145 path: &str,
146 mode: &str,
147 crp_mode: CrpMode,
148 task: Option<&str>,
149 aggressiveness: Option<f64>,
150 protect: &[String],
151) -> ReadOutput {
152 handle_with_options_resolved(
153 cache,
154 path,
155 mode,
156 true,
157 crp_mode,
158 task,
159 ReadTuning::resolve(aggressiveness, protect),
160 )
161}
162
163fn handle_with_options(
164 cache: &mut SessionCache,
165 path: &str,
166 mode: &str,
167 fresh: bool,
168 crp_mode: CrpMode,
169 task: Option<&str>,
170) -> String {
171 handle_with_options_resolved(
172 cache,
173 path,
174 mode,
175 fresh,
176 crp_mode,
177 task,
178 ReadTuning::resolve(None, &[]),
179 )
180 .content
181}
182
183pub(crate) fn force_fresh_env() -> bool {
186 static FORCE_FRESH: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
187 *FORCE_FRESH.get_or_init(|| {
188 std::env::var("LEAN_CTX_FORCE_FRESH").is_ok_and(|v| v == "1" || v == "true")
189 })
190}
191
192pub(crate) fn is_subagent_context() -> bool {
206 static IS_SUBAGENT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
207 *IS_SUBAGENT.get_or_init(|| {
208 std::env::var("CURSOR_TASK_ID").is_ok_and(|v| !v.is_empty())
209 || std::env::var("CLAUDE_CODE_ENTRYPOINT")
210 .ok()
211 .as_deref()
212 .map(str::trim)
213 == Some("local-agent")
214 })
215}
216
217#[allow(clippy::fn_params_excessive_bools)]
220pub(crate) fn effective_fresh_flags(
221 fresh: bool,
222 force_fresh: bool,
223 subagent_context: bool,
224 delivery_for_subagents: bool,
225) -> (bool, bool) {
226 let effective_fresh_for_cache = fresh || force_fresh || subagent_context;
227 let effective_fresh_for_delivery =
228 fresh || force_fresh || (subagent_context && !delivery_for_subagents);
229 (effective_fresh_for_cache, effective_fresh_for_delivery)
230}
231
232pub(crate) fn effective_fresh_for_delivery(fresh: bool) -> bool {
233 let config = crate::core::config::Config::load();
234 effective_fresh_flags(
235 fresh,
236 force_fresh_env(),
237 is_subagent_context(),
238 config.ocla.delivery.delivery_for_subagents,
239 )
240 .1
241}
242
243fn handle_with_options_resolved(
244 cache: &mut SessionCache,
245 path: &str,
246 mode: &str,
247 fresh: bool,
248 crp_mode: CrpMode,
249 task: Option<&str>,
250 tuning: ReadTuning<'_>,
251) -> ReadOutput {
252 handle_with_options_resolved_preread(cache, path, mode, fresh, crp_mode, task, tuning, None)
253}
254
255fn handle_with_options_resolved_preread(
256 cache: &mut SessionCache,
257 path: &str,
258 mode: &str,
259 fresh: bool,
260 crp_mode: CrpMode,
261 task: Option<&str>,
262 tuning: ReadTuning<'_>,
263 preread: Option<String>,
264) -> ReadOutput {
265 let config = crate::core::config::Config::load();
268 let (effective_fresh_for_cache, effective_fresh_for_delivery) = effective_fresh_flags(
269 fresh,
270 force_fresh_env(),
271 is_subagent_context(),
272 config.ocla.delivery.delivery_for_subagents,
273 );
274
275 let compress_protected = mode != "raw"
276 && !mode.starts_with("lines:")
277 && crate::core::config::Config::load()
278 .proxy
279 .is_path_compress_protected(path);
280
281 let delivery_metadata = config
284 .ocla
285 .delivery_enabled()
286 .then(|| file_blake3_prefix(path))
287 .flatten();
288
289 if !effective_fresh_for_delivery
290 && !compress_protected
291 && let Some((hash, mtime)) = delivery_metadata
292 && let Some(stub) = try_cross_agent_stub(path, mode, hash, mtime)
293 {
294 return stub;
295 }
296
297 if mode == "auto" {
298 let touched: Vec<String> = cache
299 .get_all_entries()
300 .iter()
301 .map(|(p, _)| (*p).clone())
302 .collect();
303 if crate::core::relevance_gate::should_gate(path, mode, task, &touched) {
304 let meta = std::fs::metadata(path);
305 let byte_count = meta.as_ref().map_or(0, std::fs::Metadata::len);
306 let line_count = preread
307 .as_ref()
308 .map_or(0, |c| bytecount::count(c.as_bytes(), b'\n'));
309 let stub = crate::core::relevance_gate::irrelevant_stub(path, line_count, byte_count);
310 let stub_tokens = count_tokens(&stub);
311 return ReadOutput {
312 content: stub,
313 resolved_mode: "auto".into(),
314 output_tokens: stub_tokens,
315 is_cache_hit: false,
316 };
317 }
318 }
319
320 if PluginManager::has_listener("pre_read") {
321 PluginManager::fire_hook_background(HookPoint::PreRead {
322 path: path.to_string(),
323 });
324 }
325
326 if let Ok(mut bt) = crate::core::bounce_tracker::global().lock() {
327 bt.next_seq();
328 }
329 let mut result = handle_with_options_inner(
330 cache,
331 path,
332 mode,
333 effective_fresh_for_cache,
334 crp_mode,
335 task,
336 tuning,
337 preread,
338 );
339
340 if let Some(entry) = cache.get_mut(path) {
341 entry.last_mode.clone_from(&result.resolved_mode);
342 if matches!(result.resolved_mode.as_str(), "full" | "full-compact")
343 && entry.full_content_delivered
344 && result.is_cache_hit
345 && entry.bump_reread() >= crate::core::cache::full_degradation_threshold()
346 {
347 entry.full_content_delivered = false;
348 entry.reset_reread_count();
349 crate::core::auto_mode_resolver::count_source("full_delivery_degraded");
350 }
351 if !matches!(result.resolved_mode.as_str(), "full" | "full-compact") {
357 entry.full_content_delivered = false;
358 entry.reset_reread_count();
359 }
360 }
361
362 if !result.is_cache_hit
363 && let Some((hash, mtime)) = delivery_metadata
364 {
365 let line_count = cache.get(path).map_or(0, |entry| entry.line_count as u32);
366 let relay = relay_eligible_content(&result);
367 record_cross_agent_delivery(
368 path,
369 hash,
370 mtime,
371 line_count,
372 result.output_tokens,
373 relay.0,
374 relay.1,
375 );
376 }
377
378 let dedup_allowed = result
380 .resolved_mode
381 .parse::<ReadMode>()
382 .is_ok_and(|m| m.is_lossy_summary());
383 if dedup_allowed && let Some(deduped) = cache.apply_dedup(path, &result.content) {
384 let new_tokens = count_tokens(&deduped);
385 if new_tokens < result.output_tokens {
386 result.content = deduped;
387 result.output_tokens = new_tokens;
388 }
389 }
390
391 if let Some(stub) = dedup_hook::maybe_dedup(path, &result.content, mode, fresh) {
393 let stub_tokens = count_tokens(&stub);
394 if stub_tokens < result.output_tokens {
395 result.content = stub;
396 result.output_tokens = stub_tokens;
397 result.is_cache_hit = true;
398 crate::core::anti_interrupt::spawn_redundant_read(path);
399 }
400 }
401
402 crate::core::context_kernel::adaptive_hook::update_from_bounce_tracker();
404 if let Ok(mut bt) = crate::core::bounce_tracker::global().lock() {
405 let original_tokens = cache.get(path).map_or(0, |e| e.original_tokens);
406 let bounces_before = bt.total_bounces();
407 let output_tokens = result.output_tokens;
408 bt.record_read(path, &result.resolved_mode, output_tokens, original_tokens);
409
410 if bt.total_bounces() > bounces_before {
411 crate::core::anti_interrupt::spawn_bounce_waste(output_tokens as u64);
412 }
413
414 let compressed = result
424 .resolved_mode
425 .parse::<ReadMode>()
426 .map_or(true, |m| m.counts_as_compressed());
427 if compressed {
428 crate::core::adaptive_thresholds::record_quality_signal(
429 path,
430 crate::core::threshold_learning::QualitySignal::CleanCompressed,
431 );
432 } else if result.resolved_mode == "full"
433 && result.output_tokens > 2000
434 && bt.bounce_rate_for_extension(path).unwrap_or(0.0) < 0.05
435 {
436 crate::core::adaptive_thresholds::record_quality_signal(
437 path,
438 crate::core::threshold_learning::QualitySignal::WastedFull,
439 );
440 }
441 }
442
443 if PluginManager::has_listener("post_compress") {
445 let original_tokens = cache.get(path).map_or(0, |e| e.original_tokens);
446 PluginManager::fire_hook_background(HookPoint::PostCompress {
447 path: path.to_string(),
448 original_tokens,
449 compressed_tokens: result.output_tokens,
450 });
451 }
452
453 {
460 let self_agent = crate::core::scent_field::scent_agent_id();
461 let scent_path = crate::core::pathutil::normalize_tool_path(path);
462 std::thread::spawn(move || {
463 crate::core::scent_field::deposit(
464 self_agent,
465 crate::core::scent_field::ScentKind::Hot,
466 &scent_path,
467 0.3,
468 );
469 });
470 }
471
472 if crate::core::cognitive_gate::full_science_enabled() {
473 let agent_id = crate::core::scent_field::scent_agent_id();
474 let agent_id = if agent_id.is_empty() {
475 "default-agent".to_string()
476 } else {
477 agent_id.to_string()
478 };
479 let signal_path = crate::core::pathutil::normalize_tool_path(path);
480 std::thread::spawn(move || {
481 crate::core::stigmergy::deposit_signal(crate::core::stigmergy::PheromoneSignal {
482 agent_id,
483 kind: crate::core::stigmergy::SignalKind::Exploration,
484 path: signal_path,
485 symbol: None,
486 strength: 0.8,
487 deposited_at: chrono::Utc::now(),
488 note: None,
489 });
490 });
491 }
492
493 crate::core::context_gc::maybe_gc(cache);
494
495 result
496}
497
498pub fn try_stub_hit_readonly(cache: &SessionCache, path: &str) -> Option<ReadOutput> {
509 let current_conversation = crate::core::conversation::current_conversation_id_fresh();
513 try_stub_hit_readonly_scoped(cache, path, current_conversation.as_deref())
514}
515
516pub(crate) fn try_stub_hit_readonly_scoped(
520 cache: &SessionCache,
521 path: &str,
522 current_conversation: Option<&str>,
523) -> Option<ReadOutput> {
524 let no_deg = crate::core::config::Config::load().no_degrade_effective();
525 let prof = crate::core::profiles::active_profile();
526 let force_full = no_deg
527 || (prof.read.default_mode_effective() == "full"
528 && prof.compression.crp_mode_effective() == "off");
529 let policy_allows_stub =
530 crate::server::compaction_sync::effective_cache_policy() != "safe" && !force_full;
531 if !policy_allows_stub {
532 return None;
533 }
534
535 if let Some(file_ref) = cache.get_file_ref_readonly(path) {
537 let (cached_mtime, cached_hash, line_count, delivered_conv) = {
538 let entry = cache.get(path)?;
539 (
540 entry.stored_mtime,
541 entry.hash.clone(),
542 entry.line_count,
543 entry.delivered_conversation.clone(),
544 )
545 };
546 if crate::core::cache::is_cache_entry_stale_verified(path, cached_mtime, &cached_hash)
547 || !cache.is_full_delivered(path)
548 {
549 return None;
550 }
551 if !crate::core::conversation::conversation_allows_stub(
557 current_conversation,
558 delivered_conv.as_deref(),
559 ) {
560 crate::core::cache_telemetry::record_conversation_mismatch();
561 return None;
562 }
563 let original_tokens = cache.record_cache_hit(path)?.original_tokens;
564 crate::core::telemetry::global_metrics().record_cache(true);
565 let stub = render_unchanged_stub(&file_ref, path, line_count);
566 crate::core::stats::record_reread(original_tokens.saturating_sub(stub.output_tokens));
567 return Some(stub);
568 }
569
570 let rec = crate::core::read_stub_index::lookup(path)?;
576 if crate::core::cache::is_cache_entry_stale_verified(path, rec.stored_mtime(), &rec.hash) {
577 return None;
578 }
579 if !crate::core::conversation::conversation_allows_cold_stub(
580 current_conversation,
581 rec.delivered_conversation.as_deref(),
582 ) {
583 crate::core::cache_telemetry::record_conversation_mismatch();
584 return None;
585 }
586 Some(render_unchanged_stub(&rec.file_ref, path, rec.line_count))
587}
588
589fn render_unchanged_stub(file_ref: &str, path: &str, line_count: usize) -> ReadOutput {
597 let short = protocol::shorten_path(path);
598 let out = if crate::core::protocol::meta_visible() {
599 format!(
600 "{file_ref}={short} [unchanged {line_count}L]\nUnchanged on disk. Use fresh=true to force re-read.",
601 )
602 } else {
603 format!("{file_ref}={short} [unchanged {line_count}L · fresh=true to re-read]")
604 };
605 let out = crate::core::redaction::redact_text_if_enabled(&out);
606 let sent = count_tokens(&out);
607 ReadOutput {
608 content: out,
609 resolved_mode: "full".into(),
610 output_tokens: sent,
611 is_cache_hit: true,
612 }
613}
614
615#[derive(Debug, Clone, PartialEq, Eq)]
618pub struct DeltaExplicitDecision {
619 pub mode: String,
622 pub note: Option<String>,
626}
627
628pub fn resolve_explicit_delta_mode(
650 cache: &SessionCache,
651 path: &str,
652 mode: &str,
653 explicit_mode: bool,
654 fresh: bool,
655 enabled: bool,
656) -> DeltaExplicitDecision {
657 let unchanged = DeltaExplicitDecision {
658 mode: mode.to_string(),
659 note: None,
660 };
661 if fresh
662 || !enabled
663 || !explicit_mode
664 || !(mode == "full" || mode == "full-compact" || mode.starts_with("lines:"))
665 {
666 return unchanged;
667 }
668 let Some(entry) = cache.get(path) else {
669 return unchanged;
671 };
672 let stale =
673 crate::core::cache::is_cache_entry_stale_verified(path, entry.stored_mtime, &entry.hash);
674 if stale {
675 if entry.content().is_some() {
679 return DeltaExplicitDecision {
680 mode: "diff".to_string(),
681 note: Some(format!(
682 "[delta-explicit] requested mode={mode} served as a diff: the file \
683 changed since your last read and the diff is the new information. \
684 Pass fresh=true if you need the full content re-emitted."
685 )),
686 };
687 }
688 return unchanged;
689 }
690 if mode.starts_with("lines:") && cache.is_full_delivered(path) {
694 return DeltaExplicitDecision {
695 mode: "full".to_string(),
696 note: None,
697 };
698 }
699 unchanged
700}
701
702pub(crate) fn file_blake3_prefix(path: &str) -> Option<([u8; 12], u64)> {
703 let meta = std::fs::metadata(path).ok()?;
704 let mtime = meta
705 .modified()
706 .ok()?
707 .duration_since(std::time::UNIX_EPOCH)
708 .ok()?
709 .as_secs();
710 let bytes = std::fs::read(path).ok()?;
711 let hash = blake3::hash(&bytes);
712 let full = hash.as_bytes();
713 let mut prefix = [0u8; 12];
714 prefix.copy_from_slice(&full[..12]);
715 Some((prefix, mtime))
716}
717
718pub(crate) fn try_cross_agent_stub(
719 path: &str,
720 mode: &str,
721 hash: [u8; 12],
722 mtime: u64,
723) -> Option<ReadOutput> {
724 if !crate::core::config::Config::load().ocla.delivery_enabled() {
725 return None;
726 }
727 if matches!(mode, "full" | "raw" | "diff") {
728 return None;
729 }
730 let current_agent = std::env::var("CURSOR_TASK_ID")
731 .or_else(|_| std::env::var("CLAUDECODE"))
732 .unwrap_or_else(|_| format!("local-{}", std::process::id()));
733 let current_conversation = crate::core::conversation::current_conversation_id()
734 .unwrap_or_else(|| current_agent.clone());
735 let reg = crate::core::ocla::OclaRegistry::global();
736 let record = crate::daemon_client::try_delivery_check_blocking(
737 &hash,
738 mtime,
739 path,
740 Some(¤t_agent),
741 Some(¤t_conversation),
742 )
743 .or_else(|| {
744 reg.delivery_registry.check_delivery(
745 &hash,
746 mtime,
747 path,
748 Some(¤t_agent),
749 Some(¤t_conversation),
750 )
751 })?;
752
753 let short = protocol::shorten_path(path);
754
755 if let Some(ref content) = record.relay_content {
756 let relay_mode = record.relay_mode.as_deref().unwrap_or("map");
757 let header = format!(
758 "{short} [relayed from {} · {relay_mode} · {}L]",
759 record.agent_id, record.line_count,
760 );
761 let body = format!("{header}\n{content}");
762 let tokens = count_tokens(&body);
763 reg.delivery_registry
764 .record_stub_served(&record, tokens as u64);
765 return Some(ReadOutput {
766 content: body,
767 resolved_mode: "cross-agent-relay".into(),
768 output_tokens: tokens,
769 is_cache_hit: true,
770 });
771 }
772
773 let stub = format!(
774 "{short} [cross-agent · {lines}L · read by {agent} · use fresh=true to force]",
775 lines = record.line_count,
776 agent = record.agent_id,
777 );
778 let tokens = count_tokens(&stub);
779 reg.delivery_registry
780 .record_stub_served(&record, tokens as u64);
781 Some(ReadOutput {
782 content: stub,
783 resolved_mode: "cross-agent-stub".into(),
784 output_tokens: tokens,
785 is_cache_hit: true,
786 })
787}
788
789pub(crate) fn record_cross_agent_delivery(
790 path: &str,
791 hash: [u8; 12],
792 mtime: u64,
793 line_count: u32,
794 tokens: usize,
795 relay_content: Option<&str>,
796 relay_mode: Option<&str>,
797) {
798 if !crate::core::config::Config::load().ocla.delivery_enabled() {
799 return;
800 }
801 let agent_id = std::env::var("CURSOR_TASK_ID")
802 .or_else(|_| std::env::var("CLAUDECODE"))
803 .unwrap_or_else(|_| format!("local-{}", std::process::id()));
804 let conversation_id =
805 crate::core::conversation::current_conversation_id().unwrap_or_else(|| agent_id.clone());
806 let entry = crate::core::ocla::types::DeliveryEntry {
807 blake3: hash,
808 path: path.into(),
809 line_count,
810 token_count: tokens as u64,
811 agent_id,
812 conversation_id,
813 mtime,
814 relay_content: relay_content
815 .filter(|c| c.len() <= MAX_RELAY_CONTENT_BYTES)
816 .map(str::to_string),
817 relay_mode: relay_mode.map(str::to_string),
818 };
819 crate::daemon_client::try_delivery_record_blocking(&entry);
820 let reg = crate::core::ocla::OclaRegistry::global();
821 reg.delivery_registry.record_delivery(entry);
822}
823
824#[cfg(test)]
825mod tests {
826 use super::{
827 SessionCache, effective_fresh_flags, try_cross_agent_stub, try_stub_hit_readonly_scoped,
828 };
829 use std::sync::atomic::Ordering;
830
831 #[test]
832 fn cross_agent_stub_miss_returns_none() {
833 let stub = try_cross_agent_stub("/nonexistent/file.rs", "auto", [0; 12], 0);
834 assert!(stub.is_none());
835 }
836
837 #[test]
838 fn subagent_delivery_policy_keeps_cache_fresh_but_allows_delivery_by_default() {
839 let delivery_for_subagents =
840 crate::core::config::DeliveryConfig::default().delivery_for_subagents;
841 assert!(
842 delivery_for_subagents,
843 "delivery must default to enabled for subagents"
844 );
845 let (cache_fresh, delivery_fresh) =
846 effective_fresh_flags(false, false, true, delivery_for_subagents);
847 assert!(cache_fresh, "subagent cache must remain isolated");
848 assert!(
849 !delivery_fresh,
850 "default policy must allow a cross-agent delivery lookup"
851 );
852 }
853
854 #[test]
855 fn subagent_delivery_policy_can_force_fresh_delivery() {
856 let (cache_fresh, delivery_fresh) = effective_fresh_flags(false, false, true, false);
857 assert!(cache_fresh);
858 assert!(delivery_fresh, "disabled policy must bypass delivery stubs");
859 }
860
861 #[test]
862 fn cross_agent_fallback_is_deterministic() {
863 let _lock = crate::core::data_dir::test_env_lock();
866 crate::test_env::remove_var("CURSOR_TASK_ID");
867 crate::test_env::remove_var("CLAUDECODE");
868 let id1 = std::env::var("CURSOR_TASK_ID")
869 .or_else(|_| std::env::var("CLAUDECODE"))
870 .unwrap_or_else(|_| format!("local-{}", std::process::id()));
871 let id2 = std::env::var("CURSOR_TASK_ID")
872 .or_else(|_| std::env::var("CLAUDECODE"))
873 .unwrap_or_else(|_| format!("local-{}", std::process::id()));
874 assert_eq!(id1, id2, "fallback agent ID must be deterministic");
875 assert!(!id1.contains("proc:"), "must not contain PID");
876 }
877
878 #[test]
879 fn warm_stub_hit_records_central_telemetry() {
880 let dir = tempfile::tempdir().unwrap();
881 let file = dir.path().join("telemetry-hit.rs");
882 std::fs::write(&file, "fn telemetry_hit() {}\n").unwrap();
883 let path = file.to_string_lossy();
884 let mut cache = SessionCache::new();
885 cache.store(&path, "fn telemetry_hit() {}\n");
886 cache.mark_full_delivered(&path);
887
888 let metrics = crate::core::telemetry::global_metrics();
889 let before = metrics.cache_hits.load(Ordering::Relaxed);
890 let output = try_stub_hit_readonly_scoped(&cache, &path, None);
891 let after = metrics.cache_hits.load(Ordering::Relaxed);
892
893 assert!(output.is_some(), "warm re-read must use the stub cache");
894 assert!(
895 after > before,
896 "stub cache hit must increment central telemetry"
897 );
898 }
899
900 #[test]
901 fn relay_does_not_poison_session_cache() {
902 let mut cache = SessionCache::new();
903 let path = "/tmp/test_relay_poison.rs";
904 cache.store(path, "original content");
905 assert_eq!(
906 cache
907 .get(path)
908 .map(|e| e.compressed_outputs.contains_key("cross-agent-relay")),
909 Some(false),
910 "cross-agent-relay must not exist in session cache"
911 );
912 }
913}