use crate::dashboard::routes::helpers::detect_project_root_for_dashboard;
pub(super) fn get_route(path: &str) -> Option<(&'static str, &'static str, String)> {
match path {
"/api/context-bounce" => Some(bounce()),
"/api/context-client" => Some(client()),
"/api/context-dynamic-tools" => Some(dynamic_tools()),
"/api/context-introspect" => Some(introspect()),
"/api/context-radar" => Some(radar()),
"/api/context-transcript" => Some(transcript()),
"/api/context-model" => Some(model()),
"/api/context-events" => Some(events()),
"/api/context-triage" => Some(build_triage_response()),
_ => None,
}
}
fn bounce() -> (&'static str, &'static str, String) {
let payload = if let Ok(bt) = crate::core::bounce_tracker::global().lock() {
serde_json::json!({
"summary": bt.format_summary(),
"total_bounces": bt.total_bounces(),
"total_wasted_tokens": bt.total_wasted_tokens(),
"per_extension": bt.per_extension_json(),
})
} else {
serde_json::json!({ "error": "lock failed" })
};
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
}
fn client() -> (&'static str, &'static str, String) {
let caps = crate::core::client_capabilities::load_persisted(86400)
.unwrap_or_else(crate::core::client_capabilities::current);
let payload = serde_json::json!({
"client_id": caps.client_id,
"tier": caps.tier(),
"resources": caps.resources,
"prompts": caps.prompts,
"elicitation": caps.elicitation,
"sampling": caps.sampling,
"dynamic_tools": caps.dynamic_tools,
"max_tools": caps.max_tools,
"summary": caps.format_summary(),
});
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
}
fn dynamic_tools() -> (&'static str, &'static str, String) {
let payload = if let Ok(state) = crate::server::dynamic_tools::global().lock() {
serde_json::json!({
"active_categories": state.active_categories(),
"all_categories": crate::server::dynamic_tools::DynamicToolState::all_categories(),
"supports_list_changed": state.supports_list_changed(),
})
} else {
serde_json::json!({ "error": "lock failed" })
};
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
}
fn introspect() -> (&'static str, &'static str, String) {
let persisted = crate::proxy::introspect::load_persisted(300);
let proxy_port = crate::proxy_setup::default_port();
let addr = std::net::SocketAddr::new(
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
proxy_port,
);
let proxy_running =
std::net::TcpStream::connect_timeout(&addr, std::time::Duration::from_millis(100)).is_ok();
let payload = match persisted {
Some(mut val) => {
if let Some(obj) = val.as_object_mut() {
obj.insert("proxy_running".into(), proxy_running.into());
}
val
}
None => serde_json::json!({
"proxy_active": false,
"proxy_running": proxy_running,
}),
};
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
}
fn radar() -> (&'static str, &'static str, String) {
let data_dir = crate::core::data_dir::lean_ctx_data_dir()
.unwrap_or_else(|_| std::path::PathBuf::from("."));
let client_id = crate::core::client_capabilities::load_persisted(86400)
.map_or_else(|| "cursor".to_string(), |c| c.client_id);
let window = crate::core::context_radar::default_window_for_client(&client_id);
let radar = crate::core::context_radar::ContextRadar::load(&data_dir, window);
let breakdown = radar.budget_breakdown();
let recent_events: Vec<serde_json::Value> = radar
.events
.iter()
.rev()
.take(100)
.map(|e| {
serde_json::json!({
"ts": e.ts,
"event_type": e.event_type,
"tokens": e.tokens,
"tool_name": e.tool_name,
"detail": e.detail,
})
})
.collect();
let rules_files: Vec<serde_json::Value> = radar
.rules_tokens
.files
.iter()
.map(|(path, tokens)| serde_json::json!({ "path": path, "tokens": tokens }))
.collect();
let payload = serde_json::json!({
"breakdown": breakdown,
"rules": {
"files": rules_files,
"total_tokens": radar.rules_tokens.total,
},
"events_total": radar.events.len(),
"recent_events": recent_events,
});
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
}
fn transcript() -> (&'static str, &'static str, String) {
let transcript = crate::hook_handlers::load_active_transcript();
if let Some((path, conv_id)) = transcript {
let tp = std::path::Path::new(&path);
if tp.exists() {
if let Ok(raw) = std::fs::read_to_string(tp) {
let messages: Vec<serde_json::Value> = raw
.lines()
.filter_map(|line| serde_json::from_str::<serde_json::Value>(line).ok())
.filter_map(|entry| {
let role = entry.get("role")?.as_str()?;
let msg = entry.get("message")?;
let content = msg.get("content")?;
let text = if let Some(s) = content.as_str() {
s.to_string()
} else if let Some(arr) = content.as_array() {
let parts: Vec<String> = arr
.iter()
.filter_map(|p| {
if p.get("type").and_then(|t| t.as_str()) == Some("text") {
p.get("text").and_then(|t| t.as_str()).map(String::from)
} else if p.get("type").and_then(|t| t.as_str())
== Some("tool_use")
{
let name = p
.get("name")
.and_then(|n| n.as_str())
.unwrap_or("tool");
Some(format!("[Tool: {name}]"))
} else if p.get("type").and_then(|t| t.as_str())
== Some("tool_result")
{
Some("[Tool Result]".to_string())
} else {
None
}
})
.collect();
if parts.is_empty() {
return None;
}
parts.join("\n")
} else {
content.to_string()
};
if text.is_empty() {
return None;
}
let tokens = text.len() / 4;
let capped = if text.len() > 50000 {
format!("{}…", &text[..text.floor_char_boundary(50000)])
} else {
text
};
Some(serde_json::json!({
"role": role,
"text": capped,
"tokens": tokens,
}))
})
.collect();
let payload = serde_json::json!({
"conversation_id": conv_id,
"transcript_path": path,
"messages": messages,
"total": messages.len(),
});
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
} else {
(
"200 OK",
"application/json",
r#"{"messages":[],"error":"unreadable"}"#.to_string(),
)
}
} else {
(
"200 OK",
"application/json",
r#"{"messages":[],"error":"file_not_found"}"#.to_string(),
)
}
} else {
(
"200 OK",
"application/json",
r#"{"messages":[],"error":"no_transcript"}"#.to_string(),
)
}
}
fn model() -> (&'static str, &'static str, String) {
let detected = crate::hook_handlers::load_detected_model();
let client = crate::core::client_capabilities::load_persisted(86400)
.map_or_else(|| "unknown".to_string(), |c| c.client_id);
let (model, window) = detected.unwrap_or_else(|| {
let w = crate::core::context_radar::default_window_for_client(&client);
("unknown".to_string(), w)
});
let payload = serde_json::json!({
"model": model,
"window_size": window,
"client_id": client,
"source": if model == "unknown" { "client_default" } else { "hook_detected" },
});
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
}
fn events() -> (&'static str, &'static str, String) {
let data_dir = crate::core::data_dir::lean_ctx_data_dir()
.unwrap_or_else(|_| std::path::PathBuf::from("."));
let client_id = crate::core::client_capabilities::load_persisted(86400)
.map_or_else(|| "cursor".to_string(), |c| c.client_id);
let window = crate::core::context_radar::default_window_for_client(&client_id);
let radar = crate::core::context_radar::ContextRadar::load(&data_dir, window);
let events: Vec<serde_json::Value> = radar
.events
.iter()
.rev()
.take(500)
.map(|e| {
serde_json::json!({
"ts": e.ts,
"event_type": e.event_type,
"tokens": e.tokens,
"tool_name": e.tool_name,
"detail": e.detail,
"content": e.content,
"model": e.model,
"conversation_id": e.conversation_id,
})
})
.collect();
let json = serde_json::to_string(&events).unwrap_or_else(|_| "[]".to_string());
("200 OK", "application/json", json)
}
fn budget_band(utilization: f64) -> &'static str {
let yellow = std::env::var("LCTX_BAND_YELLOW")
.ok()
.and_then(|v| v.parse::<f64>().ok())
.unwrap_or(50.0);
let orange = std::env::var("LCTX_BAND_ORANGE")
.ok()
.and_then(|v| v.parse::<f64>().ok())
.unwrap_or(75.0);
let red = std::env::var("LCTX_BAND_RED")
.ok()
.and_then(|v| v.parse::<f64>().ok())
.unwrap_or(90.0);
let pct = utilization * 100.0;
if pct >= red {
"red"
} else if pct >= orange {
"orange"
} else if pct >= yellow {
"yellow"
} else {
"green"
}
}
fn band_recommendation(band: &str) -> &'static str {
match band {
"green" => "Optimal usage — no action needed",
"yellow" => "Moderate pressure — consider compressed reads for new files",
"orange" => "High pressure — review top eviction candidates",
"red" => "Critical pressure — compact or create handoff pack",
_ => "",
}
}
fn compute_eviction_score(
sent_tokens: usize,
max_tokens: usize,
timestamp: i64,
now: i64,
access_count: u32,
pinned: bool,
) -> f64 {
if pinned {
return 0.0;
}
let token_ratio = if max_tokens > 0 {
sent_tokens as f64 / max_tokens as f64
} else {
0.0
};
let age_secs = (now - timestamp).max(0) as f64;
let recency = 1.0 / (1.0 + (age_secs / 600.0));
let frequency = 1.0 / (1.0 + access_count as f64);
(token_ratio * 0.5 + (1.0 - recency) * 0.3 + frequency * 0.2).min(1.0)
}
fn build_triage_response() -> (&'static str, &'static str, String) {
let ledger = crate::core::context_ledger::ContextLedger::load();
let pressure = ledger.pressure();
let heatmap = crate::core::heatmap::HeatMap::load();
let session = crate::core::session::SessionState::load_latest();
let mi = crate::hook_handlers::load_detected_model();
let window_size = mi.as_ref().map_or(ledger.window_size, |m| m.1);
let band = budget_band(pressure.utilization);
let recommendation = band_recommendation(band);
let now = chrono::Utc::now().timestamp();
let project_root = detect_project_root_for_dashboard();
let overlays = crate::core::context_overlay::OverlayStore::load_project(
&std::path::PathBuf::from(&project_root),
);
let pinned_paths: std::collections::HashSet<String> = overlays
.all()
.iter()
.filter(|o| {
matches!(
o.operation,
crate::core::context_overlay::OverlayOp::Pin { .. }
)
})
.map(|o| o.target.as_str().to_string())
.collect();
let edited_paths: Vec<String> = session
.as_ref()
.map(|s| s.files_touched.iter().map(|f| f.path.clone()).collect())
.unwrap_or_default();
let max_tokens = ledger
.entries
.iter()
.map(|e| e.sent_tokens)
.max()
.unwrap_or(1);
let data_dir = crate::core::data_dir::lean_ctx_data_dir()
.unwrap_or_else(|_| std::path::PathBuf::from("."));
let client_id = crate::core::client_capabilities::load_persisted(86400)
.map_or_else(|| "cursor".to_string(), |c| c.client_id);
let radar_window = crate::core::context_radar::default_window_for_client(&client_id);
let radar = crate::core::context_radar::ContextRadar::load(&data_dir, radar_window);
let mut items: Vec<serde_json::Value> = ledger
.entries
.iter()
.map(|entry| {
let heat = heatmap.entries.get(&entry.path);
let access_count = heat.map_or(1, |h| h.access_count);
let last_access_ts = heat
.and_then(|h| chrono::DateTime::parse_from_rfc3339(&h.last_access).ok())
.map_or(entry.timestamp, |dt| dt.timestamp());
let is_pinned = pinned_paths.contains(&entry.path)
|| entry.state == Some(crate::core::context_field::ContextState::Pinned);
let eviction_score = compute_eviction_score(
entry.sent_tokens,
max_tokens,
last_access_ts,
now,
access_count,
is_pinned,
);
let compression_pct = if entry.original_tokens > 0 {
((entry.original_tokens - entry.sent_tokens) as f64 / entry.original_tokens as f64
* 100.0)
.round() as u32
} else {
0
};
let source_trail = build_source_trail(entry, &radar, is_pinned);
let mut risk_flags: Vec<serde_json::Value> = Vec::new();
if entry.mode != "full"
&& edited_paths
.iter()
.any(|ep| entry.path.ends_with(ep) || ep.ends_with(&entry.path))
{
risk_flags.push(serde_json::json!({
"type": "edited_after_compressed",
"message": format!("Edited but only read in '{}' mode", entry.mode),
}));
}
serde_json::json!({
"path": entry.path,
"kind": entry.kind,
"tokens_sent": entry.sent_tokens,
"tokens_original": entry.original_tokens,
"compression_pct": compression_pct,
"mode": entry.mode,
"phi": entry.phi,
"pinned": is_pinned,
"last_accessed_ts": last_access_ts,
"access_count": access_count,
"eviction_score": (eviction_score * 1000.0).round() / 1000.0,
"source_trail": source_trail,
"risk_flags": risk_flags,
"state": entry.state,
})
})
.collect();
items.sort_by(|a, b| {
let sa = a["eviction_score"].as_f64().unwrap_or(0.0);
let sb = b["eviction_score"].as_f64().unwrap_or(0.0);
sb.partial_cmp(&sa).unwrap_or(std::cmp::Ordering::Equal)
});
let actions = build_action_recommendations(&items, band);
let payload = serde_json::json!({
"budget": {
"window_size": window_size,
"used": pressure.utilization * window_size as f64,
"utilization": (pressure.utilization * 1000.0).round() / 1000.0,
"remaining_tokens": pressure.remaining_tokens,
"band": band,
"recommendation": recommendation,
},
"items": items,
"actions": actions,
"summary": {
"total_files": ledger.entries.len(),
"total_tokens_sent": ledger.total_tokens_sent,
"total_tokens_saved": ledger.total_tokens_saved,
"pinned_count": pinned_paths.len(),
"risk_count": items.iter().filter(|i| !i["risk_flags"].as_array().is_none_or(Vec::is_empty)).count(),
},
});
let json = serde_json::to_string(&payload).unwrap_or_else(|_| "{}".to_string());
("200 OK", "application/json", json)
}
fn build_source_trail(
entry: &crate::core::context_ledger::LedgerEntry,
radar: &crate::core::context_radar::ContextRadar,
is_pinned: bool,
) -> Vec<serde_json::Value> {
let mut trail: Vec<serde_json::Value> = Vec::new();
if is_pinned {
trail.push(serde_json::json!({
"type": "pinned",
"detail": "Pinned by user or policy",
}));
}
if let Some(ref prov) = entry.provenance {
trail.push(serde_json::json!({
"type": "provenance",
"tool": prov.tool,
"agent": prov.agent_id,
"client": prov.client_name,
"ts": prov.timestamp,
}));
}
let matching_events: Vec<&crate::core::context_radar::RadarEvent> = radar
.events
.iter()
.filter(|e| {
e.detail.as_deref().is_some_and(|d| d.contains(&entry.path))
|| e.tool_name
.as_deref()
.is_some_and(|t| t == "ctx_read" || t == "ctx_multi_read")
&& e.detail.as_deref().is_some_and(|d| d.contains(&entry.path))
})
.collect();
for ev in matching_events.iter().take(3) {
trail.push(serde_json::json!({
"type": "event",
"event_type": ev.event_type,
"tool": ev.tool_name,
"ts": ev.ts,
"tokens": ev.tokens,
}));
}
trail
}
fn build_action_recommendations(items: &[serde_json::Value], band: &str) -> Vec<serde_json::Value> {
if band == "green" {
return Vec::new();
}
let mut actions: Vec<serde_json::Value> = Vec::new();
let max_actions = match band {
"red" => 5,
"orange" => 3,
_ => 2,
};
for item in items.iter().take(20) {
if actions.len() >= max_actions {
break;
}
let pinned = item["pinned"].as_bool().unwrap_or(false);
if pinned {
continue;
}
let tokens = item["tokens_sent"].as_u64().unwrap_or(0);
let mode = item["mode"].as_str().unwrap_or("");
let path = item["path"].as_str().unwrap_or("");
let eviction_score = item["eviction_score"].as_f64().unwrap_or(0.0);
if mode == "full" && tokens > 500 {
let estimated_savings = (tokens as f64 * 0.6).round() as u64;
actions.push(serde_json::json!({
"type": "compress",
"path": path,
"from_mode": "full",
"to_mode": "map",
"reason": "Full read can be compressed to map mode",
"estimated_savings": estimated_savings,
}));
} else if eviction_score > 0.5 && tokens > 200 {
actions.push(serde_json::json!({
"type": "evict",
"path": path,
"reason": format!("Low relevance (score {:.2}), {} tokens", eviction_score, tokens),
"estimated_savings": tokens,
}));
}
}
for item in items {
if actions.len() >= max_actions {
break;
}
let risk_flags = item["risk_flags"].as_array();
if let Some(flags) = risk_flags {
if !flags.is_empty() {
let path = item["path"].as_str().unwrap_or("");
actions.push(serde_json::json!({
"type": "full_read",
"path": path,
"reason": "File was edited after compressed read — full read recommended",
"estimated_savings": 0,
}));
}
}
}
actions
}