@@ -36,31 +36,23 @@ interface EditorUsage {
3636 } ;
3737}
3838
39+ interface PeriodStats {
40+ tokens : number ;
41+ sessions : number ;
42+ avgInteractionsPerSession : number ;
43+ avgTokensPerSession : number ;
44+ modelUsage : ModelUsage ;
45+ editorUsage : EditorUsage ;
46+ co2 : number ;
47+ treesEquivalent : number ;
48+ waterUsage : number ;
49+ estimatedCost : number ;
50+ }
51+
3952interface DetailedStats {
40- today : {
41- tokens : number ;
42- sessions : number ;
43- avgInteractionsPerSession : number ;
44- avgTokensPerSession : number ;
45- modelUsage : ModelUsage ;
46- editorUsage : EditorUsage ;
47- co2 : number ;
48- treesEquivalent : number ;
49- waterUsage : number ;
50- estimatedCost : number ;
51- } ;
52- month : {
53- tokens : number ;
54- sessions : number ;
55- avgInteractionsPerSession : number ;
56- avgTokensPerSession : number ;
57- modelUsage : ModelUsage ;
58- editorUsage : EditorUsage ;
59- co2 : number ;
60- treesEquivalent : number ;
61- waterUsage : number ;
62- estimatedCost : number ;
63- } ;
53+ today : PeriodStats ;
54+ month : PeriodStats ;
55+ lastMonth : PeriodStats ;
6456 lastUpdated : Date ;
6557}
6658
@@ -368,7 +360,9 @@ class CopilotTokenTracker implements vscode.Disposable {
368360
369361 public async clearCache ( ) : Promise < void > {
370362 try {
371- this . log ( '[DEBUG] clearCache() called' ) ;
363+ // Show the output channel so users can see what's happening
364+ this . outputChannel . show ( true ) ;
365+ this . log ( 'DEBUG clearCache() called' ) ;
372366 this . log ( 'Clearing session file cache...' ) ;
373367
374368 const cacheSize = this . sessionFileCache . size ;
@@ -377,6 +371,8 @@ class CopilotTokenTracker implements vscode.Disposable {
377371 // Reset diagnostics loaded flag so the diagnostics view will reload files
378372 this . diagnosticsHasLoadedFiles = false ;
379373 this . diagnosticsCachedFiles = [ ] ;
374+ // Clear cached computed stats so details panel doesn't show stale data
375+ this . lastDetailedStats = undefined ;
380376
381377 this . log ( `Cache cleared successfully. Removed ${ cacheSize } entries.` ) ;
382378 vscode . window . showInformationMessage ( 'Cache cleared successfully. Reloading statistics...' ) ;
@@ -596,9 +592,13 @@ class CopilotTokenTracker implements vscode.Disposable {
596592 const now = new Date ( ) ;
597593 const todayStart = new Date ( now . getFullYear ( ) , now . getMonth ( ) , now . getDate ( ) ) ;
598594 const monthStart = new Date ( now . getFullYear ( ) , now . getMonth ( ) , 1 ) ;
595+ // Calculate last month boundaries
596+ const lastMonthEnd = new Date ( now . getFullYear ( ) , now . getMonth ( ) , 0 , 23 , 59 , 59 , 999 ) ; // Last day of previous month
597+ const lastMonthStart = new Date ( lastMonthEnd . getFullYear ( ) , lastMonthEnd . getMonth ( ) , 1 ) ;
599598
600599 const todayStats = { tokens : 0 , sessions : 0 , interactions : 0 , modelUsage : { } as ModelUsage , editorUsage : { } as EditorUsage } ;
601600 const monthStats = { tokens : 0 , sessions : 0 , interactions : 0 , modelUsage : { } as ModelUsage , editorUsage : { } as EditorUsage } ;
601+ const lastMonthStats = { tokens : 0 , sessions : 0 , interactions : 0 , modelUsage : { } as ModelUsage , editorUsage : { } as EditorUsage } ;
602602
603603 try {
604604 // Clean expired cache entries
@@ -626,9 +626,9 @@ class CopilotTokenTracker implements vscode.Disposable {
626626 // Fast check: Get file stats first to avoid processing old files
627627 const fileStats = fs . statSync ( sessionFile ) ;
628628
629- // Skip files modified before the current month (quick filter)
629+ // Skip files modified before last month (quick filter)
630630 // This is the main performance optimization - filters out old sessions without reading file content
631- if ( fileStats . mtime < monthStart ) {
631+ if ( fileStats . mtime < lastMonthStart ) {
632632 skippedFiles ++ ;
633633 continue ;
634634 }
@@ -708,18 +708,46 @@ class CopilotTokenTracker implements vscode.Disposable {
708708 }
709709 }
710710 }
711+ else if ( lastActivity >= lastMonthStart && lastActivity <= lastMonthEnd ) {
712+ // Session is from last month - only track lastMonth stats
713+ if ( wasCached ) {
714+ cacheHits ++ ;
715+ } else {
716+ cacheMisses ++ ;
717+ }
718+
719+ lastMonthStats . tokens += tokens ;
720+ lastMonthStats . sessions += 1 ;
721+ lastMonthStats . interactions += interactions ;
722+
723+ // Add editor usage to last month stats
724+ if ( ! lastMonthStats . editorUsage [ editorType ] ) {
725+ lastMonthStats . editorUsage [ editorType ] = { tokens : 0 , sessions : 0 } ;
726+ }
727+ lastMonthStats . editorUsage [ editorType ] . tokens += tokens ;
728+ lastMonthStats . editorUsage [ editorType ] . sessions += 1 ;
729+
730+ // Add model usage to last month stats
731+ for ( const [ model , usage ] of Object . entries ( modelUsage ) ) {
732+ if ( ! lastMonthStats . modelUsage [ model ] ) {
733+ lastMonthStats . modelUsage [ model ] = { inputTokens : 0 , outputTokens : 0 } ;
734+ }
735+ lastMonthStats . modelUsage [ model ] . inputTokens += usage . inputTokens ;
736+ lastMonthStats . modelUsage [ model ] . outputTokens += usage . outputTokens ;
737+ }
738+ }
711739 else {
712- // Session is too old (no activity in current month), skip it
740+ // Session is too old (no activity in current or last month), skip it
713741 skippedFiles ++ ;
714742 }
715743 } catch ( fileError ) {
716744 this . warn ( `Error processing session file ${ sessionFile } : ${ fileError } ` ) ;
717745 }
718746 }
719747
720- this . log ( `✅ Analysis complete: Today ${ todayStats . sessions } sessions, Month ${ monthStats . sessions } sessions` ) ;
748+ this . log ( `✅ Analysis complete: Today ${ todayStats . sessions } sessions, Month ${ monthStats . sessions } sessions, Last Month ${ lastMonthStats . sessions } sessions ` ) ;
721749 if ( skippedFiles > 0 ) {
722- this . log ( `⏭️ Skipped ${ skippedFiles } session file(s) (empty or no activity in current month )` ) ;
750+ this . log ( `⏭️ Skipped ${ skippedFiles } session file(s) (empty or no activity in recent months )` ) ;
723751 }
724752 const totalCacheAccesses = cacheHits + cacheMisses ;
725753 this . log ( `💾 Cache performance: ${ cacheHits } hits, ${ cacheMisses } misses (${ totalCacheAccesses > 0 ? ( ( cacheHits / totalCacheAccesses ) * 100 ) . toFixed ( 1 ) : 0 } % hit rate)` ) ;
@@ -729,12 +757,15 @@ class CopilotTokenTracker implements vscode.Disposable {
729757
730758 const todayCo2 = ( todayStats . tokens / 1000 ) * this . co2Per1kTokens ;
731759 const monthCo2 = ( monthStats . tokens / 1000 ) * this . co2Per1kTokens ;
760+ const lastMonthCo2 = ( lastMonthStats . tokens / 1000 ) * this . co2Per1kTokens ;
732761
733762 const todayWater = ( todayStats . tokens / 1000 ) * this . waterUsagePer1kTokens ;
734763 const monthWater = ( monthStats . tokens / 1000 ) * this . waterUsagePer1kTokens ;
764+ const lastMonthWater = ( lastMonthStats . tokens / 1000 ) * this . waterUsagePer1kTokens ;
735765
736766 const todayCost = this . calculateEstimatedCost ( todayStats . modelUsage ) ;
737767 const monthCost = this . calculateEstimatedCost ( monthStats . modelUsage ) ;
768+ const lastMonthCost = this . calculateEstimatedCost ( lastMonthStats . modelUsage ) ;
738769
739770 const result : DetailedStats = {
740771 today : {
@@ -761,6 +792,18 @@ class CopilotTokenTracker implements vscode.Disposable {
761792 waterUsage : monthWater ,
762793 estimatedCost : monthCost
763794 } ,
795+ lastMonth : {
796+ tokens : lastMonthStats . tokens ,
797+ sessions : lastMonthStats . sessions ,
798+ avgInteractionsPerSession : lastMonthStats . sessions > 0 ? Math . round ( lastMonthStats . interactions / lastMonthStats . sessions ) : 0 ,
799+ avgTokensPerSession : lastMonthStats . sessions > 0 ? Math . round ( lastMonthStats . tokens / lastMonthStats . sessions ) : 0 ,
800+ modelUsage : lastMonthStats . modelUsage ,
801+ editorUsage : lastMonthStats . editorUsage ,
802+ co2 : lastMonthCo2 ,
803+ treesEquivalent : lastMonthCo2 / this . co2AbsorptionPerTreePerYear ,
804+ waterUsage : lastMonthWater ,
805+ estimatedCost : lastMonthCost
806+ } ,
764807 lastUpdated : now
765808 } ;
766809
@@ -773,7 +816,8 @@ class CopilotTokenTracker implements vscode.Disposable {
773816
774817 private async calculateDailyStats ( ) : Promise < DailyTokenStats [ ] > {
775818 const now = new Date ( ) ;
776- const monthStart = new Date ( now . getFullYear ( ) , now . getMonth ( ) , 1 ) ;
819+ // Use last 30 days instead of current month for better chart visibility
820+ const thirtyDaysAgo = new Date ( now . getFullYear ( ) , now . getMonth ( ) , now . getDate ( ) - 30 ) ;
777821
778822 // Map to store daily stats by date string (YYYY-MM-DD)
779823 const dailyStatsMap = new Map < string , DailyTokenStats > ( ) ;
@@ -786,8 +830,8 @@ class CopilotTokenTracker implements vscode.Disposable {
786830 try {
787831 const fileStats = fs . statSync ( sessionFile ) ;
788832
789- // Only process files modified in the current month
790- if ( fileStats . mtime >= monthStart ) {
833+ // Only process files modified in the last 30 days
834+ if ( fileStats . mtime >= thirtyDaysAgo ) {
791835 const tokens = await this . estimateTokensFromSessionCached ( sessionFile , fileStats . mtime . getTime ( ) ) ;
792836 const interactions = await this . countInteractionsInSessionCached ( sessionFile , fileStats . mtime . getTime ( ) ) ;
793837 const modelUsage = await this . getModelUsageFromSessionCached ( sessionFile , fileStats . mtime . getTime ( ) ) ;
@@ -840,42 +884,39 @@ class CopilotTokenTracker implements vscode.Disposable {
840884 // Convert map to array and sort by date
841885 let dailyStatsArray = Array . from ( dailyStatsMap . values ( ) ) . sort ( ( a , b ) => a . date . localeCompare ( b . date ) ) ;
842886
843- // Fill in missing dates between the first date and today
844- if ( dailyStatsArray . length > 0 ) {
845- const firstDate = new Date ( dailyStatsArray [ 0 ] . date ) ;
846- const today = new Date ( ) ;
847-
848- // Create a set of existing dates for quick lookup
849- const existingDates = new Set ( dailyStatsArray . map ( s => s . date ) ) ;
850-
851- // Generate all dates from first date to today
852- const allDates : string [ ] = [ ] ;
853- const currentDate = new Date ( firstDate ) ;
854-
855- while ( currentDate <= today ) {
856- const dateKey = this . formatDateKey ( currentDate ) ;
857- allDates . push ( dateKey ) ;
858- currentDate . setDate ( currentDate . getDate ( ) + 1 ) ;
859- }
860-
861- // Add missing dates with zero values
862- for ( const dateKey of allDates ) {
863- if ( ! existingDates . has ( dateKey ) ) {
864- dailyStatsMap . set ( dateKey , {
865- date : dateKey ,
866- tokens : 0 ,
867- sessions : 0 ,
868- interactions : 0 ,
869- modelUsage : { } ,
870- editorUsage : { }
871- } ) ;
872- }
887+ // Always fill in all 30 days to show complete chart
888+ const today = new Date ( ) ;
889+
890+ // Create a set of existing dates for quick lookup
891+ const existingDates = new Set ( dailyStatsArray . map ( s => s . date ) ) ;
892+
893+ // Generate all dates from 30 days ago to today
894+ const allDates : string [ ] = [ ] ;
895+ const currentDate = new Date ( thirtyDaysAgo ) ;
896+
897+ while ( currentDate <= today ) {
898+ const dateKey = this . formatDateKey ( currentDate ) ;
899+ allDates . push ( dateKey ) ;
900+ currentDate . setDate ( currentDate . getDate ( ) + 1 ) ;
901+ }
902+
903+ // Add missing dates with zero values
904+ for ( const dateKey of allDates ) {
905+ if ( ! existingDates . has ( dateKey ) ) {
906+ dailyStatsMap . set ( dateKey , {
907+ date : dateKey ,
908+ tokens : 0 ,
909+ sessions : 0 ,
910+ interactions : 0 ,
911+ modelUsage : { } ,
912+ editorUsage : { }
913+ } ) ;
873914 }
874-
875- // Re-convert map to array and sort by date
876- dailyStatsArray = Array . from ( dailyStatsMap . values ( ) ) . sort ( ( a , b ) => a . date . localeCompare ( b . date ) ) ;
877915 }
878916
917+ // Re-convert map to array and sort by date
918+ dailyStatsArray = Array . from ( dailyStatsMap . values ( ) ) . sort ( ( a , b ) => a . date . localeCompare ( b . date ) ) ;
919+
879920 return dailyStatsArray ;
880921 }
881922
@@ -3192,7 +3233,7 @@ class CopilotTokenTracker implements vscode.Disposable {
31923233
31933234 // Handle messages from the webview
31943235 this . diagnosticsPanel . webview . onDidReceiveMessage ( async ( message ) => {
3195- this . log ( `Diagnostics webview message: ${ JSON . stringify ( message ) } ` ) ;
3236+ this . log ( `DEBUG Diagnostics webview message: ${ JSON . stringify ( message ) } ` ) ;
31963237 switch ( message . command ) {
31973238 case 'copyReport' :
31983239 await vscode . env . clipboard . writeText ( report ) ;
@@ -3252,7 +3293,7 @@ class CopilotTokenTracker implements vscode.Disposable {
32523293 await this . showUsageAnalysis ( ) ;
32533294 break ;
32543295 case 'clearCache' :
3255- this . log ( '[ DEBUG] clearCache message received from diagnostics webview' ) ;
3296+ this . log ( 'DEBUG clearCache message received from diagnostics webview' ) ;
32563297 await this . clearCache ( ) ;
32573298 // After clearing cache, refresh the diagnostic report if it's open
32583299 if ( this . diagnosticsPanel ) {
0 commit comments