import type { UsageFilter, UsageRepository } from '../db/repositories/usage-repository.js'; import type { ClientId } from '../models/usage-record.js'; import type { CostService } from './cost-service.js'; /** Context above this is carried, not needed: a compacted session restarts well below it. */ export declare const CARRY_THRESHOLD_TOKENS = 100000; export type Verdict = 'healthy' | 'some-waste' | 'high-waste' | 'no-data'; export interface ReportSession { sessionId: string; client: ClientId; title?: string; gitBranch?: string; projectPath?: string; turns: number; startedAt: string; endedAt: string; /** API-equivalent estimate of the session's turns; the weight used for shares. */ estimatedCost: number; share: number; /** Largest context any one turn carried (input + cache read + cache write). */ peakContextTokens: number; /** Weight spent re-reading context above {@link CARRY_THRESHOLD_TOKENS}. */ carryCost: number; rebuilds: number; } export interface ReportBranch { gitBranch: string; sessions: number; turns: number; estimatedCost: number; share: number; } export interface UsageReport { period: string; turns: number; sessions: number; totalTokens: number; /** * The weight every share is a share OF: the API-equivalent estimate of the * turns that have one. On a subscription this is not money spent; it is a * relative measure of how heavy each turn was (model, token class, cache tier). */ weight: { estimatedCost: number; weightedTurns: number; unweightedTurns: number; }; contextCarry: { thresholdTokens: number; estimatedCost: number; share: number; /** Turns that carried more than the threshold. */ turns: number; sessionsOverLarge: number; largestContextTokens: number; }; cacheRebuilds: { count: number; afterIdle: number; estimatedCost: number; share: number; /** Rebuilds on a model with no price, excluded from estimatedCost. */ unpriced: number; }; /** Context carry plus cache rebuilds, as a share of the weight. Avoidable in part, not whole. */ wasteShare: number; verdict: Verdict; topSessions: ReportSession[]; topBranches: ReportBranch[]; fixes: string[]; caveats: string[]; } /** * Where a period's usage went, and how much of it did no new work. * * Two kinds of waste are measured, both from token counts alone: * - **context carry**: every message re-sends the whole conversation, so a turn * at 300k context re-reads 300k tokens to do one step of work. The part above * {@link CARRY_THRESHOLD_TOKENS} is what compacting earlier would not have * carried. * - **cache rebuilds**: turns where the cached prefix was lost and the whole * context was re-written at cache-write rates (see `detectCacheBreaks`). * * Neither is claimed as wholly avoidable -- some tasks need a large context -- * and the report says so. */ export declare class ReportService { private readonly repo; private readonly costService; constructor(repo: UsageRepository, costService: CostService); report(filter: UsageFilter, period: string): UsageReport; }