import Foundation#if canImport(Musl)import Musl#elseif canImport(Glibc)import Glibc#elseimport Darwin#endif extension CostUsageScanner { static func codexRowsByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: [CodexUsageRow]]] { var rowsByDayModel: [String: [String: [CodexUsageRow]]] = [:] for usage in cache.files.values { for row in usage.codexRows ?? [] { guard CostUsageDayRange.isInRange(dayKey: row.day, since: range.sinceKey, until: range.untilKey) else { continue } rowsByDayModel[row.day, default: [:]][row.model, default: []].append(row) } } return rowsByDayModel } static func codexRowsByDayModel( rows: [CodexUsageRow], range: CostUsageDayRange) -> [String: [String: [CodexUsageRow]]] { var rowsByDayModel: [String: [String: [CodexUsageRow]]] = [:] for row in rows { guard CostUsageDayRange.isInRange(dayKey: row.day, since: range.sinceKey, until: range.untilKey) else { continue } rowsByDayModel[row.day, default: [:]][row.model, default: []].append(row) } return rowsByDayModel } static func codexCostNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexCostNanos } } static func codexPrioritySurchargeNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexPrioritySurchargeNanos } } static func codexStandardCostNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexStandardCostNanos } } static func codexPriorityCostNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexPriorityCostNanos } } static func codexStandardTokensByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int]] { self.codexIntByDayModel(cache: cache, range: range) { $0.codexStandardTokens } } static func codexPriorityTokensByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int]] { self.codexIntByDayModel(cache: cache, range: range) { $0.codexPriorityTokens } } static func codexNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange, keyPath: (CostUsageFileUsage) -> [String: [String: Int64]]?) -> [String: [String: Int64]] { var out: [String: [String: Int64]] = [:] for usage in cache.files.values { for (day, models) in keyPath(usage) ?? [:] { guard CostUsageDayRange.isInRange(dayKey: day, since: range.sinceKey, until: range.untilKey) else { continue } for (model, value) in models { out[day, default: [:]][model, default: 0] += value } } } return out } static func codexIntByDayModel( cache: CostUsageCache, range: CostUsageDayRange, keyPath: (CostUsageFileUsage) -> [String: [String: Int]]?) -> [String: [String: Int]] { var out: [String: [String: Int]] = [:] for usage in cache.files.values { for (day, models) in keyPath(usage) ?? [:] { guard CostUsageDayRange.isInRange(dayKey: day, since: range.sinceKey, until: range.untilKey) else { continue } for (model, value) in models { out[day, default: [:]][model, default: 0] += value } } } return out } static func codexRowsCostUSD( rows: [CodexUsageRow], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> Double? { var total: Double = 0 var seen = false for row in rows { guard let cost = CostUsagePricing.codexCostUSD( model: row.model, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) else { continue } total += cost seen = true } return seen ? total : nil } static func codexPrioritySurchargeUSD( rows: [CodexUsageRow], priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> Double? { var total: Double = 0 var seen = false for row in rows { guard let turnID = row.turnID, let priorityMetadata = priorityTurns[turnID] else { continue } let pricedModel = Self.codexPriorityPricingModel(for: row, priorityMetadata: priorityMetadata) guard let baseCost = CostUsagePricing.codexCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot), let priorityCost = CostUsagePricing.codexPriorityCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output) else { continue } total += max(priorityCost - baseCost, 0) seen = true } return seen ? total : nil } private static func codexPriorityPricingModel( for row: CodexUsageRow, priorityMetadata: CodexPriorityTurnMetadata) -> String { guard let model = priorityMetadata.model, CostUsagePricing.codexPriorityCostUSD( model: model, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output) != nil else { return row.model } return model } struct CodexRowCostBreakdown { var standardCostUSD: Double = 0 var priorityCostUSD: Double = 0 var standardTokens: Int = 0 var priorityTokens: Int = 0 var sawStandardCost = false var sawPriorityCost = false var optionalStandardCostUSD: Double? { self.sawStandardCost ? self.standardCostUSD : nil } var optionalPriorityCostUSD: Double? { self.sawPriorityCost ? self.priorityCostUSD : nil } var optionalStandardTokens: Int? { self.standardTokens > 0 ? self.standardTokens : nil } var optionalPriorityTokens: Int? { self.priorityTokens > 0 ? self.priorityTokens : nil } var totalCostUSD: Double? { guard self.sawStandardCost || self.sawPriorityCost else { return nil } return self.standardCostUSD + self.priorityCostUSD } var hasModeSplit: Bool { self.sawPriorityCost || self.priorityTokens > 0 } } static func codexRowCostBreakdown( rows: [CodexUsageRow], priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> CodexRowCostBreakdown { var breakdown = CodexRowCostBreakdown() for row in rows { let tokenCount = row.input + row.output let priorityMetadata = row.turnID.flatMap { priorityTurns[$0] } let isPriority = priorityMetadata != nil if isPriority { breakdown.priorityTokens += tokenCount } else { breakdown.standardTokens += tokenCount } let pricedModel = priorityMetadata.map { Self.codexPriorityPricingModel(for: row, priorityMetadata: $0) } ?? row.model let baseCost = CostUsagePricing.codexCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) if isPriority, let priorityCost = CostUsagePricing.codexPriorityCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output) { breakdown.priorityCostUSD += max(priorityCost, baseCost ?? priorityCost) breakdown.sawPriorityCost = true } else if isPriority, let baseCost { breakdown.priorityCostUSD += baseCost breakdown.sawPriorityCost = true } else if let baseCost { breakdown.standardCostUSD += baseCost breakdown.sawStandardCost = true } } return breakdown } // MARK: - File cache construction static func makeFileUsage( mtimeUnixMs: Int64, size: Int64, days: [String: [String: [Int]]], parsedBytes: Int64?, lastModel: String? = nil, lastTotals: CostUsageCodexTotals? = nil, lastCountedTotals: CostUsageCodexTotals? = nil, lastRawTotalsBaseline: CostUsageCodexTotals? = nil, hasDivergentTotals: Bool? = nil, lastCodexTurnID: String? = nil, sessionId: String? = nil, forkedFromId: String? = nil, codexCostNanos: [String: [String: Int64]]? = nil, codexPrioritySurchargeNanos: [String: [String: Int64]]? = nil, codexStandardCostNanos: [String: [String: Int64]]? = nil, codexPriorityCostNanos: [String: [String: Int64]]? = nil, codexStandardTokens: [String: [String: Int]]? = nil, codexPriorityTokens: [String: [String: Int]]? = nil, codexTurnIDs: [String]? = nil, codexRows: [CodexUsageRow]? = nil, claudeRows: [ClaudeUsageRow]? = nil) -> CostUsageFileUsage { CostUsageFileUsage( mtimeUnixMs: mtimeUnixMs, size: size, days: days, parsedBytes: parsedBytes, lastModel: lastModel, lastTotals: lastTotals, lastCountedTotals: lastCountedTotals, lastRawTotalsBaseline: lastRawTotalsBaseline, hasDivergentTotals: hasDivergentTotals, lastCodexTurnID: lastCodexTurnID, sessionId: sessionId, forkedFromId: forkedFromId, codexCostNanos: codexCostNanos, codexPrioritySurchargeNanos: codexPrioritySurchargeNanos, codexStandardCostNanos: codexStandardCostNanos, codexPriorityCostNanos: codexPriorityCostNanos, codexStandardTokens: codexStandardTokens, codexPriorityTokens: codexPriorityTokens, codexTurnIDs: codexTurnIDs, codexRows: codexRows, claudeRows: claudeRows) } static func needsCodexCostCache(_ usage: CostUsageFileUsage) -> Bool { !(usage.codexRows?.isEmpty ?? true) && (usage.codexCostNanos == nil || self.needsCodexModeSplitCache(usage)) } static func needsCodexCostCache(_ usage: CostUsageFileUsage, range: CostUsageDayRange) -> Bool { guard let rows = usage.codexRows, !rows.isEmpty else { return false } return rows.contains { CostUsageDayRange.isInRange(dayKey: $0.day, since: range.sinceKey, until: range.untilKey) } && (usage.codexCostNanos == nil || Self.needsCodexModeSplitCache(usage)) } static func needsCodexModeSplitCache(_ usage: CostUsageFileUsage) -> Bool { usage.codexStandardCostNanos == nil || usage.codexPriorityCostNanos == nil || usage.codexStandardTokens == nil || usage.codexPriorityTokens == nil } static func codexFileUsageWithCostCache( _ usage: CostUsageFileUsage, context: CodexFileScanContext) -> CostUsageFileUsage { guard let rows = usage.codexRows, !rows.isEmpty else { return usage } var migratedRows: [CodexUsageRow] = [] var retainedRows: [CodexUsageRow] = [] for row in rows { if CostUsageDayRange.isInRange( dayKey: row.day, since: context.range.scanSinceKey, until: context.range.scanUntilKey) { migratedRows.append(row) } else { retainedRows.append(row) } } guard !migratedRows.isEmpty else { return usage } let splitMaps = Self.codexModeSplitMaps( rows: migratedRows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot) var updated = usage updated.codexCostNanos = Self.mergeMissingCostMaps( usage.codexCostNanos, Self.codexCostNanos( rows: migratedRows, range: context.range, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) updated.codexPrioritySurchargeNanos = Self.mergeMissingCostMaps( usage.codexPrioritySurchargeNanos, Self.codexPrioritySurchargeNanos( rows: migratedRows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) updated.codexStandardCostNanos = Self.mergeMissingCostMaps( usage.codexStandardCostNanos, splitMaps.standardCostNanos) updated.codexPriorityCostNanos = Self.mergeMissingCostMaps( usage.codexPriorityCostNanos, splitMaps.priorityCostNanos) updated.codexStandardTokens = Self.mergeMissingIntMaps( usage.codexStandardTokens, splitMaps.standardTokens) updated.codexPriorityTokens = Self.mergeMissingIntMaps( usage.codexPriorityTokens, splitMaps.priorityTokens) updated.codexTurnIDs = Self.mergeCodexTurnIDs(usage.codexTurnIDs, rows: migratedRows) updated.codexRows = retainedRows.isEmpty ? nil : retainedRows return updated } static func codexMergedCostMap( _ existing: [String: [String: Int64]]?, deltaRows: [CodexUsageRow], context: CodexFileScanContext) -> [String: [String: Int64]]? { self.mergeCostMaps( existing, self.codexCostNanos( rows: deltaRows, range: context.range, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) } static func codexMergedPrioritySurchargeMap( _ existing: [String: [String: Int64]]?, deltaRows: [CodexUsageRow], context: CodexFileScanContext) -> [String: [String: Int64]]? { self.mergeCostMaps( existing, self.codexPrioritySurchargeNanos( rows: deltaRows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) } static func codexCostNanos( rows: [CodexUsageRow], range: CostUsageDayRange, modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> [String: [String: Int64]]? { let rowsByDayModel = Self.codexRowsByDayModel(rows: rows, range: range) var out: [String: [String: Int64]] = [:] for (day, models) in rowsByDayModel { for (model, rows) in models { guard let cost = Self.codexRowsCostUSD( rows: rows, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) else { continue } out[day, default: [:]][model] = Int64((cost * Self.costScale).rounded()) } } return out.isEmpty ? nil : out } static func codexPrioritySurchargeNanos( rows: [CodexUsageRow], range: CostUsageDayRange, priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> [String: [String: Int64]]? { guard !priorityTurns.isEmpty else { return nil } let rowsByDayModel = Self.codexRowsByDayModel(rows: rows, range: range) var out: [String: [String: Int64]] = [:] for (day, models) in rowsByDayModel { for (model, rows) in models { guard let surcharge = Self.codexPrioritySurchargeUSD( rows: rows, priorityTurns: priorityTurns, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) else { continue } out[day, default: [:]][model] = Int64((surcharge * Self.costScale).rounded()) } } return out.isEmpty ? nil : out } static func codexModeSplitMaps( rows: [CodexUsageRow], range: CostUsageDayRange, priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> ( standardCostNanos: [String: [String: Int64]]?, priorityCostNanos: [String: [String: Int64]]?, standardTokens: [String: [String: Int]]?, priorityTokens: [String: [String: Int]]?) { var standardCostNanos: [String: [String: Int64]] = [:] var priorityCostNanos: [String: [String: Int64]] = [:] var standardTokens: [String: [String: Int]] = [:] var priorityTokens: [String: [String: Int]] = [:] for row in rows { guard CostUsageDayRange.isInRange(dayKey: row.day, since: range.sinceKey, until: range.untilKey) else { continue } let tokenCount = row.input + row.output let priorityMetadata = row.turnID.flatMap { priorityTurns[$0] } let pricedModel = priorityMetadata.map { Self.codexPriorityPricingModel(for: row, priorityMetadata: $0) } ?? row.model let isPriority = priorityMetadata != nil if isPriority { priorityTokens[row.day, default: [:]][row.model, default: 0] += tokenCount } else { standardTokens[row.day, default: [:]][row.model, default: 0] += tokenCount } let baseCost = CostUsagePricing.codexCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog,Lines 1–500 of 1212. Load more
import Foundation #if canImport(Musl) import Musl #elseif canImport(Glibc) import Glibc #else import Darwin #endif extension CostUsageScanner { static func codexRowsByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: [CodexUsageRow]]] { var rowsByDayModel: [String: [String: [CodexUsageRow]]] = [:] for usage in cache.files.values { for row in usage.codexRows ?? [] { guard CostUsageDayRange.isInRange(dayKey: row.day, since: range.sinceKey, until: range.untilKey) else { continue } rowsByDayModel[row.day, default: [:]][row.model, default: []].append(row) } } return rowsByDayModel } static func codexRowsByDayModel( rows: [CodexUsageRow], range: CostUsageDayRange) -> [String: [String: [CodexUsageRow]]] { var rowsByDayModel: [String: [String: [CodexUsageRow]]] = [:] for row in rows { guard CostUsageDayRange.isInRange(dayKey: row.day, since: range.sinceKey, until: range.untilKey) else { continue } rowsByDayModel[row.day, default: [:]][row.model, default: []].append(row) } return rowsByDayModel } static func codexCostNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexCostNanos } } static func codexPrioritySurchargeNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexPrioritySurchargeNanos } } static func codexStandardCostNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexStandardCostNanos } } static func codexPriorityCostNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int64]] { self.codexNanosByDayModel(cache: cache, range: range) { $0.codexPriorityCostNanos } } static func codexStandardTokensByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int]] { self.codexIntByDayModel(cache: cache, range: range) { $0.codexStandardTokens } } static func codexPriorityTokensByDayModel( cache: CostUsageCache, range: CostUsageDayRange) -> [String: [String: Int]] { self.codexIntByDayModel(cache: cache, range: range) { $0.codexPriorityTokens } } static func codexNanosByDayModel( cache: CostUsageCache, range: CostUsageDayRange, keyPath: (CostUsageFileUsage) -> [String: [String: Int64]]?) -> [String: [String: Int64]] { var out: [String: [String: Int64]] = [:] for usage in cache.files.values { for (day, models) in keyPath(usage) ?? [:] { guard CostUsageDayRange.isInRange(dayKey: day, since: range.sinceKey, until: range.untilKey) else { continue } for (model, value) in models { out[day, default: [:]][model, default: 0] += value } } } return out } static func codexIntByDayModel( cache: CostUsageCache, range: CostUsageDayRange, keyPath: (CostUsageFileUsage) -> [String: [String: Int]]?) -> [String: [String: Int]] { var out: [String: [String: Int]] = [:] for usage in cache.files.values { for (day, models) in keyPath(usage) ?? [:] { guard CostUsageDayRange.isInRange(dayKey: day, since: range.sinceKey, until: range.untilKey) else { continue } for (model, value) in models { out[day, default: [:]][model, default: 0] += value } } } return out } static func codexRowsCostUSD( rows: [CodexUsageRow], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> Double? { var total: Double = 0 var seen = false for row in rows { guard let cost = CostUsagePricing.codexCostUSD( model: row.model, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) else { continue } total += cost seen = true } return seen ? total : nil } static func codexPrioritySurchargeUSD( rows: [CodexUsageRow], priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> Double? { var total: Double = 0 var seen = false for row in rows { guard let turnID = row.turnID, let priorityMetadata = priorityTurns[turnID] else { continue } let pricedModel = Self.codexPriorityPricingModel(for: row, priorityMetadata: priorityMetadata) guard let baseCost = CostUsagePricing.codexCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot), let priorityCost = CostUsagePricing.codexPriorityCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output) else { continue } total += max(priorityCost - baseCost, 0) seen = true } return seen ? total : nil } private static func codexPriorityPricingModel( for row: CodexUsageRow, priorityMetadata: CodexPriorityTurnMetadata) -> String { guard let model = priorityMetadata.model, CostUsagePricing.codexPriorityCostUSD( model: model, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output) != nil else { return row.model } return model } struct CodexRowCostBreakdown { var standardCostUSD: Double = 0 var priorityCostUSD: Double = 0 var standardTokens: Int = 0 var priorityTokens: Int = 0 var sawStandardCost = false var sawPriorityCost = false var optionalStandardCostUSD: Double? { self.sawStandardCost ? self.standardCostUSD : nil } var optionalPriorityCostUSD: Double? { self.sawPriorityCost ? self.priorityCostUSD : nil } var optionalStandardTokens: Int? { self.standardTokens > 0 ? self.standardTokens : nil } var optionalPriorityTokens: Int? { self.priorityTokens > 0 ? self.priorityTokens : nil } var totalCostUSD: Double? { guard self.sawStandardCost || self.sawPriorityCost else { return nil } return self.standardCostUSD + self.priorityCostUSD } var hasModeSplit: Bool { self.sawPriorityCost || self.priorityTokens > 0 } } static func codexRowCostBreakdown( rows: [CodexUsageRow], priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> CodexRowCostBreakdown { var breakdown = CodexRowCostBreakdown() for row in rows { let tokenCount = row.input + row.output let priorityMetadata = row.turnID.flatMap { priorityTurns[$0] } let isPriority = priorityMetadata != nil if isPriority { breakdown.priorityTokens += tokenCount } else { breakdown.standardTokens += tokenCount } let pricedModel = priorityMetadata.map { Self.codexPriorityPricingModel(for: row, priorityMetadata: $0) } ?? row.model let baseCost = CostUsagePricing.codexCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) if isPriority, let priorityCost = CostUsagePricing.codexPriorityCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output) { breakdown.priorityCostUSD += max(priorityCost, baseCost ?? priorityCost) breakdown.sawPriorityCost = true } else if isPriority, let baseCost { breakdown.priorityCostUSD += baseCost breakdown.sawPriorityCost = true } else if let baseCost { breakdown.standardCostUSD += baseCost breakdown.sawStandardCost = true } } return breakdown } // MARK: - File cache construction static func makeFileUsage( mtimeUnixMs: Int64, size: Int64, days: [String: [String: [Int]]], parsedBytes: Int64?, lastModel: String? = nil, lastTotals: CostUsageCodexTotals? = nil, lastCountedTotals: CostUsageCodexTotals? = nil, lastRawTotalsBaseline: CostUsageCodexTotals? = nil, hasDivergentTotals: Bool? = nil, lastCodexTurnID: String? = nil, sessionId: String? = nil, forkedFromId: String? = nil, codexCostNanos: [String: [String: Int64]]? = nil, codexPrioritySurchargeNanos: [String: [String: Int64]]? = nil, codexStandardCostNanos: [String: [String: Int64]]? = nil, codexPriorityCostNanos: [String: [String: Int64]]? = nil, codexStandardTokens: [String: [String: Int]]? = nil, codexPriorityTokens: [String: [String: Int]]? = nil, codexTurnIDs: [String]? = nil, codexRows: [CodexUsageRow]? = nil, claudeRows: [ClaudeUsageRow]? = nil) -> CostUsageFileUsage { CostUsageFileUsage( mtimeUnixMs: mtimeUnixMs, size: size, days: days, parsedBytes: parsedBytes, lastModel: lastModel, lastTotals: lastTotals, lastCountedTotals: lastCountedTotals, lastRawTotalsBaseline: lastRawTotalsBaseline, hasDivergentTotals: hasDivergentTotals, lastCodexTurnID: lastCodexTurnID, sessionId: sessionId, forkedFromId: forkedFromId, codexCostNanos: codexCostNanos, codexPrioritySurchargeNanos: codexPrioritySurchargeNanos, codexStandardCostNanos: codexStandardCostNanos, codexPriorityCostNanos: codexPriorityCostNanos, codexStandardTokens: codexStandardTokens, codexPriorityTokens: codexPriorityTokens, codexTurnIDs: codexTurnIDs, codexRows: codexRows, claudeRows: claudeRows) } static func needsCodexCostCache(_ usage: CostUsageFileUsage) -> Bool { !(usage.codexRows?.isEmpty ?? true) && (usage.codexCostNanos == nil || self.needsCodexModeSplitCache(usage)) } static func needsCodexCostCache(_ usage: CostUsageFileUsage, range: CostUsageDayRange) -> Bool { guard let rows = usage.codexRows, !rows.isEmpty else { return false } return rows.contains { CostUsageDayRange.isInRange(dayKey: $0.day, since: range.sinceKey, until: range.untilKey) } && (usage.codexCostNanos == nil || Self.needsCodexModeSplitCache(usage)) } static func needsCodexModeSplitCache(_ usage: CostUsageFileUsage) -> Bool { usage.codexStandardCostNanos == nil || usage.codexPriorityCostNanos == nil || usage.codexStandardTokens == nil || usage.codexPriorityTokens == nil } static func codexFileUsageWithCostCache( _ usage: CostUsageFileUsage, context: CodexFileScanContext) -> CostUsageFileUsage { guard let rows = usage.codexRows, !rows.isEmpty else { return usage } var migratedRows: [CodexUsageRow] = [] var retainedRows: [CodexUsageRow] = [] for row in rows { if CostUsageDayRange.isInRange( dayKey: row.day, since: context.range.scanSinceKey, until: context.range.scanUntilKey) { migratedRows.append(row) } else { retainedRows.append(row) } } guard !migratedRows.isEmpty else { return usage } let splitMaps = Self.codexModeSplitMaps( rows: migratedRows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot) var updated = usage updated.codexCostNanos = Self.mergeMissingCostMaps( usage.codexCostNanos, Self.codexCostNanos( rows: migratedRows, range: context.range, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) updated.codexPrioritySurchargeNanos = Self.mergeMissingCostMaps( usage.codexPrioritySurchargeNanos, Self.codexPrioritySurchargeNanos( rows: migratedRows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) updated.codexStandardCostNanos = Self.mergeMissingCostMaps( usage.codexStandardCostNanos, splitMaps.standardCostNanos) updated.codexPriorityCostNanos = Self.mergeMissingCostMaps( usage.codexPriorityCostNanos, splitMaps.priorityCostNanos) updated.codexStandardTokens = Self.mergeMissingIntMaps( usage.codexStandardTokens, splitMaps.standardTokens) updated.codexPriorityTokens = Self.mergeMissingIntMaps( usage.codexPriorityTokens, splitMaps.priorityTokens) updated.codexTurnIDs = Self.mergeCodexTurnIDs(usage.codexTurnIDs, rows: migratedRows) updated.codexRows = retainedRows.isEmpty ? nil : retainedRows return updated } static func codexMergedCostMap( _ existing: [String: [String: Int64]]?, deltaRows: [CodexUsageRow], context: CodexFileScanContext) -> [String: [String: Int64]]? { self.mergeCostMaps( existing, self.codexCostNanos( rows: deltaRows, range: context.range, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) } static func codexMergedPrioritySurchargeMap( _ existing: [String: [String: Int64]]?, deltaRows: [CodexUsageRow], context: CodexFileScanContext) -> [String: [String: Int64]]? { self.mergeCostMaps( existing, self.codexPrioritySurchargeNanos( rows: deltaRows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)) } static func codexCostNanos( rows: [CodexUsageRow], range: CostUsageDayRange, modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> [String: [String: Int64]]? { let rowsByDayModel = Self.codexRowsByDayModel(rows: rows, range: range) var out: [String: [String: Int64]] = [:] for (day, models) in rowsByDayModel { for (model, rows) in models { guard let cost = Self.codexRowsCostUSD( rows: rows, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) else { continue } out[day, default: [:]][model] = Int64((cost * Self.costScale).rounded()) } } return out.isEmpty ? nil : out } static func codexPrioritySurchargeNanos( rows: [CodexUsageRow], range: CostUsageDayRange, priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> [String: [String: Int64]]? { guard !priorityTurns.isEmpty else { return nil } let rowsByDayModel = Self.codexRowsByDayModel(rows: rows, range: range) var out: [String: [String: Int64]] = [:] for (day, models) in rowsByDayModel { for (model, rows) in models { guard let surcharge = Self.codexPrioritySurchargeUSD( rows: rows, priorityTurns: priorityTurns, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) else { continue } out[day, default: [:]][model] = Int64((surcharge * Self.costScale).rounded()) } } return out.isEmpty ? nil : out } static func codexModeSplitMaps( rows: [CodexUsageRow], range: CostUsageDayRange, priorityTurns: [String: CodexPriorityTurnMetadata], modelsDevCatalog: ModelsDevCatalog?, modelsDevCacheRoot: URL?) -> ( standardCostNanos: [String: [String: Int64]]?, priorityCostNanos: [String: [String: Int64]]?, standardTokens: [String: [String: Int]]?, priorityTokens: [String: [String: Int]]?) { var standardCostNanos: [String: [String: Int64]] = [:] var priorityCostNanos: [String: [String: Int64]] = [:] var standardTokens: [String: [String: Int]] = [:] var priorityTokens: [String: [String: Int]] = [:] for row in rows { guard CostUsageDayRange.isInRange(dayKey: row.day, since: range.sinceKey, until: range.untilKey) else { continue } let tokenCount = row.input + row.output let priorityMetadata = row.turnID.flatMap { priorityTurns[$0] } let pricedModel = priorityMetadata.map { Self.codexPriorityPricingModel(for: row, priorityMetadata: $0) } ?? row.model let isPriority = priorityMetadata != nil if isPriority { priorityTokens[row.day, default: [:]][row.model, default: 0] += tokenCount } else { standardTokens[row.day, default: [:]][row.model, default: 0] += tokenCount } let baseCost = CostUsagePricing.codexCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output, modelsDevCatalog: modelsDevCatalog,