feat: persistent token tracking with global stats aggregation

- Add restoreTokens() method to Session class for recovery after restart
- Add GlobalStats type for cumulative usage tracking across all sessions
- Accumulate tokens from deleted sessions into global stats
- Track lifetime session count with incrementSessionsCreated()
- Add /api/stats endpoint for global stats
- Include globalStats in /api/status response
- Frontend shows aggregate tokens + cost in header
- Fix respawn controller config to filter undefined values
- Add 7 new tests for global stats functionality (v0.1343)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-25 07:26:49 +01:00
co-authored by Claude Opus 4.5
parent 0aca55b099
commit e56c8626e4
10 changed files with 322 additions and 19 deletions
+40
View File
@@ -88,6 +88,27 @@ export interface SessionState {
childAgentIds?: string[];
}
// ========== Global Stats Types ==========
/**
* Global statistics across all sessions (including deleted ones).
* Persisted to track cumulative usage over time.
*/
export interface GlobalStats {
/** Total input tokens used across all sessions */
totalInputTokens: number;
/** Total output tokens used across all sessions */
totalOutputTokens: number;
/** Total cost in USD across all sessions */
totalCost: number;
/** Total number of sessions created (lifetime) */
totalSessionsCreated: number;
/** Timestamp when stats were first recorded */
firstRecordedAt: number;
/** Timestamp of last update */
lastUpdatedAt: number;
}
// ========== Task Types ==========
/**
@@ -178,6 +199,8 @@ export interface AppState {
ralphLoop: RalphLoopState;
/** Application configuration */
config: AppConfig;
/** Global statistics (cumulative across all sessions) */
globalStats?: GlobalStats;
}
// ========== Respawn Controller Types ==========
@@ -744,6 +767,23 @@ export function createInitialState(): AppState {
lastCheckAt: null,
},
config: { ...DEFAULT_CONFIG },
globalStats: createInitialGlobalStats(),
};
}
/**
* Creates initial global stats object
* @returns Fresh global stats with zero values
*/
export function createInitialGlobalStats(): GlobalStats {
const now = Date.now();
return {
totalInputTokens: 0,
totalOutputTokens: 0,
totalCost: 0,
totalSessionsCreated: 0,
firstRecordedAt: now,
lastUpdatedAt: now,
};
}