Something went wrong. Try again.
daemon connecting Claude Code sessions to the JamJum JP-mini via usb or bluetooth
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149//! Context-window fill from Claude Code's statusline payload.
use serde_json::{Value, json};
use crate::session::SessionKey;
#[derive(Clone, Debug, PartialEq)]pub struct ContextFill { pub session: SessionKey, pub fill: f64,}
fn fill_from(cw: &Value) -> Option<f64> { let num = |k: &str| cw.get(k).and_then(Value::as_f64); let fill = match ( num("used_percentage"), num("total_input_tokens"), num("context_window_size"), ) { (Some(pct), _, _) => pct / 100.0, (None, Some(used), Some(size)) if size > 0.0 => used / size, _ => return None, }; Some(fill.clamp(0.0, 1.0))}
impl ContextFill { /// Builds a fill update of `session` from the statusline payload; `None` means send nothing. pub fn from_statusline(stdin: &str, session: SessionKey) -> Option<ContextFill> { let v: Value = serde_json::from_str(stdin).ok()?; Some(ContextFill { session, fill: fill_from(v.get("context_window")?)?, }) }
pub fn to_wire(&self) -> String { let mut v = json!({"context_fill": self.fill}); let (field, id) = self.session.wire_field(); v[field] = json!(id); v.to_string() }
pub fn from_wire(line: &str) -> Option<ContextFill> { let v: Value = serde_json::from_str(line).ok()?; if v.get("hook_event_name").is_some() { return None; } let session = SessionKey::from_wire(&v)?; let fill = v.get("context_fill")?.as_f64()?; fill.is_finite().then_some(())?; Some(ContextFill { session, fill: fill.clamp(0.0, 1.0), }) }}
#[cfg(test)]mod tests { use super::*; use serde_json::json;
const UUID: &str = "5E38C7C8-E1CA-414E-A41F-6AACAC2BF28A";
#[test] fn desktop_fill_wire_roundtrip() { let c = ContextFill { session: SessionKey::Desktop("local_a".to_string()), fill: 0.3, }; assert_eq!(ContextFill::from_wire(&c.to_wire()), Some(c)); }
fn pane() -> SessionKey { SessionKey::ITerm(UUID.to_string()) }
fn fill_of(payload: serde_json::Value) -> Option<f64> { ContextFill::from_statusline(&payload.to_string(), pane()).map(|c| c.fill) }
#[test] fn prefers_used_percentage() { let p = json!({"context_window": {"used_percentage": 42, "total_input_tokens": 1, "context_window_size": 1000}}); assert_eq!(fill_of(p), Some(0.42)); }
#[test] fn falls_back_to_token_counts() { let p = json!({"context_window": {"used_percentage": null, "total_input_tokens": 50000, "context_window_size": 200000}}); assert_eq!(fill_of(p), Some(0.25)); }
#[test] fn clamps_and_rejects_unusable_payloads() { assert_eq!( fill_of(json!({"context_window": {"used_percentage": 150}})), Some(1.0) ); assert_eq!( fill_of(json!({"context_window": {"used_percentage": -3}})), Some(0.0) ); assert_eq!( fill_of(json!({"context_window": {"total_input_tokens": 5, "context_window_size": 0}})), None ); assert_eq!(fill_of(json!({"context_window": {}})), None); assert_eq!(fill_of(json!({})), None); assert_eq!(ContextFill::from_statusline("not json", pane()), None); }
#[test] fn wire_roundtrip_and_validation() { let c = ContextFill { session: pane(), fill: 0.3, }; assert_eq!(ContextFill::from_wire(&c.to_wire()), Some(c.clone())); assert_eq!( crate::event::Event::from_wire(&c.to_wire()), None, "not mistaken for a hook event" ); assert_eq!( ContextFill::from_wire(&json!({"pane": "x", "context_fill": 0.3}).to_string()), None ); assert_eq!( ContextFill::from_wire(&json!({"pane": UUID, "context_fill": 7.0}).to_string()) .unwrap() .fill, 1.0 ); assert_eq!( ContextFill::from_wire( &json!({"pane": UUID, "cloud": "session_x", "context_fill": 0.3}).to_string() ), None, "a cloud session is not a socket session" ); assert_eq!( ContextFill::from_wire(&json!({"pane": UUID, "hook_event_name": "Stop"}).to_string()), None ); }}