1- use crate :: types:: { DailyUsageRow , ModelUsage , ProjectUsage , SessionDetailRow } ;
1+ use crate :: types:: {
2+ DailyUsageRow , ModelUsage , ProjectUsage , SessionDailyUsageRow , SessionDetailRow ,
3+ } ;
24use rusqlite:: { params, Connection } ;
35use std:: { collections:: BTreeMap , path:: Path } ;
46
@@ -447,6 +449,7 @@ pub fn query_session_details(db: &Connection) -> Result<Vec<SessionDetailRow>, S
447449 let mut cost_usd = 0.0 ;
448450 let mut models = std:: collections:: BTreeSet :: new ( ) ;
449451 let mut projects = std:: collections:: BTreeSet :: new ( ) ;
452+ let mut daily_usage = Vec :: with_capacity ( daily_rows. len ( ) ) ;
450453
451454 for r in daily_rows {
452455 input_tokens += r. input_tokens ;
@@ -461,6 +464,17 @@ pub fn query_session_details(db: &Connection) -> Result<Vec<SessionDetailRow>, S
461464 for project in r. projects . keys ( ) {
462465 projects. insert ( project. clone ( ) ) ;
463466 }
467+ daily_usage. push ( SessionDailyUsageRow {
468+ date : r. date ,
469+ input_tokens : r. input_tokens ,
470+ cached_input_tokens : r. cached_input_tokens ,
471+ output_tokens : r. output_tokens ,
472+ reasoning_output_tokens : r. reasoning_output_tokens ,
473+ total_tokens : r. total_tokens ,
474+ cost_usd : r. cost_usd ,
475+ models : r. models . into_keys ( ) . collect ( ) ,
476+ projects : r. projects . into_keys ( ) . collect ( ) ,
477+ } ) ;
464478 }
465479
466480 let session_id = Path :: new ( & path)
@@ -483,6 +497,7 @@ pub fn query_session_details(db: &Connection) -> Result<Vec<SessionDetailRow>, S
483497 cost_usd,
484498 models : models. into_iter ( ) . collect ( ) ,
485499 projects : projects. into_iter ( ) . collect ( ) ,
500+ daily_usage,
486501 } )
487502 } )
488503 . map_err ( |error| error. to_string ( ) ) ?;
@@ -595,6 +610,66 @@ mod tests {
595610 let _ = std:: fs:: remove_file ( path) ;
596611 }
597612
613+ #[ test]
614+ fn session_details_preserve_daily_usage_for_resumed_sessions ( ) {
615+ let path = std:: env:: temp_dir ( ) . join ( format ! (
616+ "codex-usage-db-session-daily-{}.sqlite" ,
617+ Utc :: now( ) . timestamp_nanos_opt( ) . unwrap_or_default( )
618+ ) ) ;
619+ let mut db = open_database ( & path) . unwrap ( ) ;
620+ let model_usage = ModelUsage {
621+ input_tokens : 100 ,
622+ output_tokens : 40 ,
623+ total_tokens : 140 ,
624+ ..Default :: default ( )
625+ } ;
626+ let project_usage = ProjectUsage {
627+ input_tokens : 100 ,
628+ output_tokens : 40 ,
629+ total_tokens : 140 ,
630+ models : BTreeMap :: from ( [ ( "gpt-5" . to_string ( ) , model_usage. clone ( ) ) ] ) ,
631+ ..Default :: default ( )
632+ } ;
633+ let daily_row = |date : & str , multiplier : i64 | DailyUsageRow {
634+ date : date. to_string ( ) ,
635+ input_tokens : 100 * multiplier,
636+ cached_input_tokens : 20 * multiplier,
637+ output_tokens : 40 * multiplier,
638+ reasoning_output_tokens : 10 * multiplier,
639+ total_tokens : 150 * multiplier,
640+ cost_usd : 0.01 * multiplier as f64 ,
641+ models : BTreeMap :: from ( [ ( "gpt-5" . to_string ( ) , model_usage. clone ( ) ) ] ) ,
642+ projects : BTreeMap :: from ( [ ( "/repo/app" . to_string ( ) , project_usage. clone ( ) ) ] ) ,
643+ updated_at : "2026-07-02T00:00:00.000Z" . to_string ( ) ,
644+ } ;
645+ upsert_session_file_rollups (
646+ & mut db,
647+ & [ SessionFileRollup {
648+ path : "/tmp/resumed.jsonl" . to_string ( ) ,
649+ modified_at_ms : 2 ,
650+ size_bytes : 200 ,
651+ rows : vec ! [ daily_row( "2026-07-01" , 1 ) , daily_row( "2026-07-02" , 2 ) ] ,
652+ prompt_title : None ,
653+ } ] ,
654+ "2026-07-02T00:00:00.000Z" ,
655+ )
656+ . unwrap ( ) ;
657+
658+ let sessions = query_session_details ( & db) . unwrap ( ) ;
659+
660+ assert_eq ! ( sessions. len( ) , 1 ) ;
661+ assert_eq ! ( sessions[ 0 ] . total_tokens, 450 ) ;
662+ assert ! ( ( sessions[ 0 ] . cost_usd - 0.03 ) . abs( ) < f64 :: EPSILON ) ;
663+ assert_eq ! ( sessions[ 0 ] . daily_usage. len( ) , 2 ) ;
664+ assert_eq ! ( sessions[ 0 ] . daily_usage[ 0 ] . date, "2026-07-01" ) ;
665+ assert_eq ! ( sessions[ 0 ] . daily_usage[ 0 ] . total_tokens, 150 ) ;
666+ assert_eq ! ( sessions[ 0 ] . daily_usage[ 1 ] . date, "2026-07-02" ) ;
667+ assert_eq ! ( sessions[ 0 ] . daily_usage[ 1 ] . total_tokens, 300 ) ;
668+ assert_eq ! ( sessions[ 0 ] . daily_usage[ 1 ] . models, [ "gpt-5" ] ) ;
669+ assert_eq ! ( sessions[ 0 ] . daily_usage[ 1 ] . projects, [ "/repo/app" ] ) ;
670+ let _ = std:: fs:: remove_file ( path) ;
671+ }
672+
598673 #[ test]
599674 fn reset_usage_state_clears_cached_tables ( ) {
600675 let path = std:: env:: temp_dir ( ) . join ( format ! (
0 commit comments