@@ -40,9 +40,38 @@ import type {
4040 ModelAuthStatus ,
4141 PermissionPolicy ,
4242 PermissionRules ,
43+ TokenUsage ,
4344 ToolCategory ,
4445} from './types' ;
4546
47+ function createEmptyTokenUsage ( ) : TokenUsage {
48+ return { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
49+ }
50+
51+ function getUsageNumber ( usage : Record < string , unknown > , key : string ) : number | undefined {
52+ const value = usage [ key ] ;
53+ if ( typeof value === 'number' && Number . isFinite ( value ) ) {
54+ return value ;
55+ }
56+ if ( typeof value === 'string' && value . trim ( ) !== '' ) {
57+ const numericValue = Number ( value ) ;
58+ if ( Number . isFinite ( numericValue ) ) {
59+ return numericValue ;
60+ }
61+ }
62+ return undefined ;
63+ }
64+
65+ function addOptionalUsageField (
66+ usage : TokenUsage ,
67+ key : keyof Pick < TokenUsage , 'reasoningTokens' | 'cachedInputTokens' | 'cacheCreationInputTokens' > ,
68+ value : number | undefined ,
69+ ) : void {
70+ if ( value !== undefined ) {
71+ usage [ key ] = ( usage [ key ] ?? 0 ) + value ;
72+ }
73+ }
74+
4675/**
4776 * The Harness orchestrates multiple agent modes, shared state, memory, and storage.
4877 * It's the core abstraction that a TUI (or other UI) controls.
@@ -108,11 +137,7 @@ export class Harness<TState = {}> {
108137 | ( ( ctx : { requestContext : RequestContext } ) => Promise < MastraBrowser | undefined > | MastraBrowser | undefined )
109138 | undefined = undefined ;
110139 private heartbeatTimers = new Map < string , { timer : NodeJS . Timeout ; shutdown ?: ( ) => void | Promise < void > } > ( ) ;
111- private tokenUsage : { promptTokens : number ; completionTokens : number ; totalTokens : number } = {
112- promptTokens : 0 ,
113- completionTokens : 0 ,
114- totalTokens : 0 ,
115- } ;
140+ private tokenUsage : TokenUsage = createEmptyTokenUsage ( ) ;
116141 private sessionGrantedCategories = new Set < string > ( ) ;
117142 private sessionGrantedTools = new Set < string > ( ) ;
118143 private displayState : HarnessDisplayState = defaultDisplayState ( ) ;
@@ -731,7 +756,7 @@ export class Harness<TState = {}> {
731756 void this . setState ( { currentModelId : modelId } as unknown as Partial < TState > ) ;
732757 }
733758
734- this . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
759+ this . tokenUsage = createEmptyTokenUsage ( ) ;
735760 this . emit ( { type : 'thread_created' , thread } ) ;
736761
737762 return thread ;
@@ -770,7 +795,7 @@ export class Harness<TState = {}> {
770795 // Lock release failed; proceed with state cleanup regardless
771796 }
772797 this . currentThreadId = null ;
773- this . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
798+ this . tokenUsage = createEmptyTokenUsage ( ) ;
774799 }
775800
776801 this . emit ( { type : 'thread_deleted' , threadId } ) ;
@@ -844,7 +869,7 @@ export class Harness<TState = {}> {
844869
845870 this . currentThreadId = clonedThread . id ;
846871 await this . loadThreadMetadata ( ) ;
847- this . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
872+ this . tokenUsage = createEmptyTokenUsage ( ) ;
848873 this . emit ( { type : 'thread_created' , thread : clonedThread } ) ;
849874
850875 return clonedThread ;
@@ -957,7 +982,7 @@ export class Harness<TState = {}> {
957982
958983 private async loadThreadMetadata ( ) : Promise < void > {
959984 if ( ! this . currentThreadId || ! this . config . storage ) {
960- this . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
985+ this . tokenUsage = createEmptyTokenUsage ( ) ;
961986 return ;
962987 }
963988
@@ -969,12 +994,14 @@ export class Harness<TState = {}> {
969994 const savedUsage = thread ?. metadata ?. tokenUsage as typeof this . tokenUsage | undefined ;
970995 if ( savedUsage ) {
971996 this . tokenUsage = {
997+ ...createEmptyTokenUsage ( ) ,
998+ ...savedUsage ,
972999 promptTokens : savedUsage . promptTokens ?? 0 ,
9731000 completionTokens : savedUsage . completionTokens ?? 0 ,
9741001 totalTokens : savedUsage . totalTokens ?? 0 ,
9751002 } ;
9761003 } else {
977- this . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
1004+ this . tokenUsage = createEmptyTokenUsage ( ) ;
9781005 }
9791006
9801007 const meta = thread ?. metadata as Record < string , unknown > | undefined ;
@@ -1040,7 +1067,7 @@ export class Harness<TState = {}> {
10401067 }
10411068 }
10421069 } catch {
1043- this . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
1070+ this . tokenUsage = createEmptyTokenUsage ( ) ;
10441071 }
10451072 }
10461073
@@ -1966,16 +1993,40 @@ export class Harness<TState = {}> {
19661993 case 'step-finish' : {
19671994 const usage = chunk . payload ?. output ?. usage ;
19681995 if ( usage ) {
1969- const promptTokens = usage . promptTokens ?? usage . inputTokens ?? 0 ;
1970- const completionTokens = usage . completionTokens ?? usage . outputTokens ?? 0 ;
1971- const totalTokens = promptTokens + completionTokens ;
1996+ const usageRecord = usage as Record < string , unknown > ;
1997+ const promptTokens =
1998+ getUsageNumber ( usageRecord , 'promptTokens' ) ?? getUsageNumber ( usageRecord , 'inputTokens' ) ?? 0 ;
1999+ const completionTokens =
2000+ getUsageNumber ( usageRecord , 'completionTokens' ) ?? getUsageNumber ( usageRecord , 'outputTokens' ) ?? 0 ;
2001+ const totalTokens = getUsageNumber ( usageRecord , 'totalTokens' ) ?? promptTokens + completionTokens ;
2002+ const stepUsage : TokenUsage = {
2003+ promptTokens,
2004+ completionTokens,
2005+ totalTokens,
2006+ } ;
2007+ addOptionalUsageField ( stepUsage , 'reasoningTokens' , getUsageNumber ( usageRecord , 'reasoningTokens' ) ) ;
2008+ addOptionalUsageField ( stepUsage , 'cachedInputTokens' , getUsageNumber ( usageRecord , 'cachedInputTokens' ) ) ;
2009+ addOptionalUsageField (
2010+ stepUsage ,
2011+ 'cacheCreationInputTokens' ,
2012+ getUsageNumber ( usageRecord , 'cacheCreationInputTokens' ) ,
2013+ ) ;
2014+ if ( usageRecord . raw !== undefined ) {
2015+ stepUsage . raw = usageRecord . raw ;
2016+ }
19722017
19732018 this . tokenUsage . promptTokens += promptTokens ;
19742019 this . tokenUsage . completionTokens += completionTokens ;
19752020 this . tokenUsage . totalTokens += totalTokens ;
2021+ addOptionalUsageField ( this . tokenUsage , 'reasoningTokens' , stepUsage . reasoningTokens ) ;
2022+ addOptionalUsageField ( this . tokenUsage , 'cachedInputTokens' , stepUsage . cachedInputTokens ) ;
2023+ addOptionalUsageField ( this . tokenUsage , 'cacheCreationInputTokens' , stepUsage . cacheCreationInputTokens ) ;
2024+ if ( stepUsage . raw !== undefined ) {
2025+ this . tokenUsage . raw = stepUsage . raw ;
2026+ }
19762027
19772028 this . persistTokenUsage ( ) . catch ( ( ) => { } ) ;
1978- this . emit ( { type : 'usage_update' , usage : { promptTokens , completionTokens , totalTokens } } ) ;
2029+ this . emit ( { type : 'usage_update' , usage : stepUsage } ) ;
19792030 }
19802031 break ;
19812032 }
@@ -2959,11 +3010,7 @@ export class Harness<TState = {}> {
29593010
29603011 // ── Token usage ────────────────────────────────────────────────────
29613012 case 'usage_update' :
2962- ds . tokenUsage = {
2963- promptTokens : this . tokenUsage . promptTokens ,
2964- completionTokens : this . tokenUsage . completionTokens ,
2965- totalTokens : this . tokenUsage . totalTokens ,
2966- } ;
3013+ ds . tokenUsage = { ...this . tokenUsage } ;
29673014 break ;
29683015
29693016 // ── Tasks ──────────────────────────────────────────────────────────
@@ -2980,13 +3027,13 @@ export class Harness<TState = {}> {
29803027
29813028 case 'thread_created' :
29823029 this . resetThreadDisplayState ( ) ;
2983- ds . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
3030+ ds . tokenUsage = createEmptyTokenUsage ( ) ;
29843031 break ;
29853032
29863033 case 'thread_deleted' :
29873034 if ( ! this . currentThreadId ) {
29883035 this . resetThreadDisplayState ( ) ;
2989- ds . tokenUsage = { promptTokens : 0 , completionTokens : 0 , totalTokens : 0 } ;
3036+ ds . tokenUsage = createEmptyTokenUsage ( ) ;
29903037 }
29913038 break ;
29923039
@@ -3164,7 +3211,7 @@ export class Harness<TState = {}> {
31643211 // Token Usage
31653212 // ===========================================================================
31663213
3167- getTokenUsage ( ) : { promptTokens : number ; completionTokens : number ; totalTokens : number } {
3214+ getTokenUsage ( ) : TokenUsage {
31683215 return { ...this . tokenUsage } ;
31693216 }
31703217
0 commit comments