@@ -307,6 +307,57 @@ test('parity: ccusage codex dedupes consecutive identical last_token_usage', asy
307307 assert . equal ( usageEvents ( events ) . length , 1 )
308308} )
309309
310+ test ( 'parity: ccusage codex skips repeated last_token_usage when the cumulative total is unchanged' , async ( ) => {
311+ // From ccusage adapter/codex/loader.rs
312+ // skips_repeated_last_usage_when_cumulative_total_is_unchanged (#1435).
313+ const info = {
314+ model : 'gpt-5.5' ,
315+ last_token_usage : { input_tokens : 100 , cached_input_tokens : 20 , output_tokens : 10 , total_tokens : 110 } ,
316+ total_token_usage : { input_tokens : 100 , cached_input_tokens : 20 , output_tokens : 10 , total_tokens : 110 } ,
317+ }
318+ const events = await parse ( [
319+ { timestamp : '2026-07-10T08:00:00.000Z' , type : 'session_meta' , payload : { id : 'session-a' , cwd : '/w' , model_provider : 'gpt-5.5' } } ,
320+ { timestamp : '2026-07-10T08:00:01.000Z' , type : 'event_msg' , payload : { type : 'token_count' , info } } ,
321+ { timestamp : '2026-07-10T08:00:02.000Z' , type : 'event_msg' , payload : { type : 'token_count' , info } } ,
322+ ] )
323+
324+ assert . equal ( usageEvents ( events ) . length , 1 )
325+ } )
326+
327+ test ( 'parity: ccusage codex skips a stale last_token_usage the cumulative does not back' , async ( ) => {
328+ // The cumulative check catches what the consecutive-duplicate dedup cannot:
329+ // a re-emitted snapshot whose last_token_usage differs from the record right
330+ // before it, while total_token_usage stands still. The differing value slips
331+ // past lastTokenUsageKey, so only the cumulative proves no tokens were spent.
332+ const events = await parse ( [
333+ { timestamp : '2026-07-10T08:00:00.000Z' , type : 'session_meta' , payload : { id : 'session-a' , cwd : '/w' , model_provider : 'gpt-5.5' } } ,
334+ // Turn 1.
335+ { timestamp : '2026-07-10T08:00:01.000Z' , type : 'event_msg' , payload : { type : 'token_count' , info : { model : 'gpt-5.5' , last_token_usage : { input_tokens : 100 , cached_input_tokens : 20 , output_tokens : 10 , total_tokens : 110 } , total_token_usage : { input_tokens : 100 , cached_input_tokens : 20 , output_tokens : 10 , total_tokens : 110 } } } } ,
336+ // Turn 2 — cumulative advances.
337+ { timestamp : '2026-07-10T08:00:02.000Z' , type : 'event_msg' , payload : { type : 'token_count' , info : { model : 'gpt-5.5' , last_token_usage : { input_tokens : 40 , cached_input_tokens : 5 , output_tokens : 7 , total_tokens : 47 } , total_token_usage : { input_tokens : 140 , cached_input_tokens : 25 , output_tokens : 17 , total_tokens : 157 } } } } ,
338+ // Turn 1's snapshot re-emitted against turn 2's cumulative: no new tokens.
339+ { timestamp : '2026-07-10T08:00:03.000Z' , type : 'event_msg' , payload : { type : 'token_count' , info : { model : 'gpt-5.5' , last_token_usage : { input_tokens : 100 , cached_input_tokens : 20 , output_tokens : 10 , total_tokens : 110 } , total_token_usage : { input_tokens : 140 , cached_input_tokens : 25 , output_tokens : 17 , total_tokens : 157 } } } } ,
340+ ] )
341+
342+ const usages = usageEvents ( events )
343+ assert . equal ( usages . length , 2 )
344+ assert . equal ( usages [ 0 ] . metrics ?. tokensTotal , 110 )
345+ assert . equal ( usages [ 1 ] . metrics ?. tokensTotal , 47 )
346+ } )
347+
348+ test ( 'parity: ccusage codex counts the first snapshot even when the cumulative is all zeros' , async ( ) => {
349+ // ccusage tracks previous_totals as an Option, so the first event of a file is
350+ // never mistaken for a repeat of a zeroed baseline.
351+ const events = await parse ( [
352+ { timestamp : '2026-07-10T08:00:00.000Z' , type : 'session_meta' , payload : { id : 'session-a' , cwd : '/w' , model_provider : 'gpt-5.5' } } ,
353+ { timestamp : '2026-07-10T08:00:01.000Z' , type : 'event_msg' , payload : { type : 'token_count' , info : { model : 'gpt-5.5' , last_token_usage : { input_tokens : 100 , cached_input_tokens : 20 , output_tokens : 10 , total_tokens : 110 } , total_token_usage : { input_tokens : 0 , cached_input_tokens : 0 , output_tokens : 0 , total_tokens : 0 } } } } ,
354+ ] )
355+
356+ const usages = usageEvents ( events )
357+ assert . equal ( usages . length , 1 )
358+ assert . equal ( usages [ 0 ] . metrics ?. tokensTotal , 110 )
359+ } )
360+
310361// ── headless `codex exec` parity (turn.completed / result / bare data.usage) ──
311362
312363test ( 'parity: ccusage codex loads_saved_codex_exec_json_usage' , async ( ) => {
0 commit comments