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, modelsDevCacheRoot: modelsDevCacheRoot) if isPriority, let priorityCost = CostUsagePricing.codexPriorityCostUSD( model: pricedModel, inputTokens: row.input, cachedInputTokens: row.cached, outputTokens: row.output) { priorityCostNanos[row.day, default: [:]][row.model, default: 0] += Int64( (max(priorityCost, baseCost ?? priorityCost) * Self.costScale).rounded()) } else if isPriority, let baseCost { priorityCostNanos[row.day, default: [:]][row.model, default: 0] += Int64( (baseCost * Self.costScale).rounded()) } else if let baseCost { standardCostNanos[row.day, default: [:]][row.model, default: 0] += Int64( (baseCost * Self.costScale).rounded()) } } return ( standardCostNanos.isEmpty ? nil : standardCostNanos, priorityCostNanos.isEmpty ? nil : priorityCostNanos, standardTokens.isEmpty ? nil : standardTokens, priorityTokens.isEmpty ? nil : priorityTokens) } static func codexTurnIDs(rows: [CodexUsageRow]) -> [String]? { let ids = Set(rows.compactMap(\.turnID)) return ids.sorted() } static func mergeCodexTurnIDs(_ existing: [String]?, rows: [CodexUsageRow]) -> [String]? { var ids = Set(existing ?? []) ids.formUnion(rows.compactMap(\.turnID)) return ids.sorted() } static func mergeCostMaps( _ existing: [String: [String: Int64]]?, _ delta: [String: [String: Int64]]?) -> [String: [String: Int64]]? { var out = existing ?? [:] for (day, models) in delta ?? [:] { for (model, value) in models { out[day, default: [:]][model, default: 0] += value } } return out.isEmpty ? nil : out } static func mergeMissingCostMaps( _ existing: [String: [String: Int64]]?, _ delta: [String: [String: Int64]]?) -> [String: [String: Int64]]? { var out = existing ?? [:] for (day, models) in delta ?? [:] { for (model, value) in models where out[day]?[model] == nil { out[day, default: [:]][model] = value } } return out.isEmpty ? nil : out } static func mergeIntMaps( _ existing: [String: [String: Int]]?, _ delta: [String: [String: Int]]?) -> [String: [String: Int]]? { var out = existing ?? [:] for (day, models) in delta ?? [:] { for (model, value) in models { out[day, default: [:]][model, default: 0] += value } } return out.isEmpty ? nil : out } static func mergeMissingIntMaps( _ existing: [String: [String: Int]]?, _ delta: [String: [String: Int]]?) -> [String: [String: Int]]? { var out = existing ?? [:] for (day, models) in delta ?? [:] { for (model, value) in models where out[day]?[model] == nil { out[day, default: [:]][model] = value } } return out.isEmpty ? nil : out } static func costMapOutsideScanWindow( _ map: [String: [String: Int64]]?, range: CostUsageDayRange) -> [String: [String: Int64]]? { let filtered = (map ?? [:]).filter { !CostUsageDayRange.isInRange(dayKey: $0.key, since: range.scanSinceKey, until: range.scanUntilKey) } return filtered.isEmpty ? nil : filtered } static func intMapOutsideScanWindow( _ map: [String: [String: Int]]?, range: CostUsageDayRange) -> [String: [String: Int]]? { let filtered = (map ?? [:]).filter { !CostUsageDayRange.isInRange(dayKey: $0.key, since: range.scanSinceKey, until: range.scanUntilKey) } return filtered.isEmpty ? nil : filtered } // MARK: - File scan orchestration struct CodexFileMetadata { let path: String let mtimeUnixMs: Int64 let size: Int64 let fileId: String? } struct CodexFileScanInput { let fileURL: URL let metadata: CodexFileMetadata let cached: CostUsageFileUsage? } static func codexFileMetadata(fileURL: URL) -> CodexFileMetadata { let path = fileURL.path var info = stat() guard path.withCString({ fstatat(AT_FDCWD, $0, &info, 0) }) == 0 else { return CodexFileMetadata(path: path, mtimeUnixMs: 0, size: 0, fileId: nil) } #if os(Linux) let modifiedSeconds = Int64(info.st_mtim.tv_sec) let modifiedNanoseconds = Int64(info.st_mtim.tv_nsec) #else let modifiedSeconds = Int64(info.st_mtimespec.tv_sec) let modifiedNanoseconds = Int64(info.st_mtimespec.tv_nsec) #endif return CodexFileMetadata( path: path, mtimeUnixMs: modifiedSeconds * 1000 + modifiedNanoseconds / 1_000_000, size: Int64(info.st_size), fileId: "\(info.st_dev):\(info.st_ino)") } static func dropCachedCodexFile( path: String, cached: CostUsageFileUsage?, cache: inout CostUsageCache) { if let cached { self.applyFileDays(cache: &cache, fileDays: cached.days, sign: -1) } cache.files.removeValue(forKey: path) } static func rememberScannedCodexFile( fileURL: URL, metadata: CodexFileMetadata, sessionId: String?, context: CodexFileScanContext, state: inout CodexScanState) { if let sessionId { state.seenSessionIds.insert(sessionId) context.resources.fileIndex.remember(fileURL: fileURL, sessionId: sessionId) } if let fileId = metadata.fileId { state.seenFileIds.insert(fileId) } } static func keepCachedCodexFileIfFresh( input: CodexFileScanInput, context: CodexFileScanContext, cache: inout CostUsageCache, state: inout CodexScanState) -> Bool { guard let cached = input.cached else { return false } let needsSessionId = cached.sessionId == nil guard cached.mtimeUnixMs == input.metadata.mtimeUnixMs, cached.size == input.metadata.size, !needsSessionId, !context.forceFullScan else { return false } guard !Self.cachedCodexFileNeedsPriorityRescan(cached, context: context) else { return false } if Self.needsCodexCostCache(cached, range: context.range) { cache.files[input.metadata.path] = Self.codexFileUsageWithCostCache(cached, context: context) } Self.rememberScannedCodexFile( fileURL: input.fileURL, metadata: input.metadata, sessionId: cached.sessionId, context: context, state: &state) return true } static func cachedCodexFileNeedsPriorityRescan( _ cached: CostUsageFileUsage, context: CodexFileScanContext) -> Bool { if cached.codexTurnIDs == nil { return context.requiresTurnIDCache } guard !context.changedPriorityTurnIDs.isEmpty else { return false } return !(Set(cached.codexTurnIDs ?? []).isDisjoint(with: context.changedPriorityTurnIDs)) } static func appendCodexFileIncrementIfPossible( input: CodexFileScanInput, context: CodexFileScanContext, cache: inout CostUsageCache, state: inout CodexScanState) throws -> Bool { try context.checkCancellation?() guard let cached = input.cached, cached.sessionId != nil, !context.forceFullScan else { return false } guard !Self.cachedCodexFileNeedsPriorityRescan(cached, context: context) else { return false } let startOffset = cached.parsedBytes ?? cached.size let initialCountedTotals = cached.lastCountedTotals ?? cached.lastTotals let initialRawTotalsBaseline = cached.lastRawTotalsBaseline ?? cached.lastTotals let canIncremental = input.metadata.size > cached.size && startOffset > 0 && startOffset <= input.metadata.size && initialCountedTotals != nil && cached.forkedFromId == nil guard canIncremental else { return false } let delta = try Self.parseCodexFileCancellable( fileURL: input.fileURL, range: context.range, startOffset: startOffset, initialModel: cached.lastModel, initialTotals: initialCountedTotals, initialRawTotalsBaseline: initialRawTotalsBaseline, initialHasDivergentTotals: cached.hasDivergentTotals ?? (cached.lastTotals == nil), initialCodexTurnID: cached.lastCodexTurnID, checkCancellation: context.checkCancellation) if delta.forkedFromId != nil { return false } let sessionId = delta.sessionId ?? cached.sessionId if let sessionId, state.seenSessionIds.contains(sessionId) { Self.dropCachedCodexFile(path: input.metadata.path, cached: cached, cache: &cache) return true } let migratedCached = Self.codexFileUsageWithCostCache(cached, context: context) if !delta.days.isEmpty { Self.applyFileDays(cache: &cache, fileDays: delta.days, sign: 1) } var mergedDays = migratedCached.days Self.mergeFileDays(existing: &mergedDays, delta: delta.days) let splitMaps = Self.codexModeSplitMaps( rows: delta.rows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot) cache.files[input.metadata.path] = Self.makeFileUsage( mtimeUnixMs: input.metadata.mtimeUnixMs, size: input.metadata.size, days: mergedDays, parsedBytes: delta.parsedBytes, lastModel: delta.lastModel, lastTotals: delta.lastTotals, lastCountedTotals: delta.lastCountedTotals, lastRawTotalsBaseline: delta.lastRawTotalsBaseline, hasDivergentTotals: delta.hasDivergentTotals, lastCodexTurnID: delta.lastCodexTurnID, sessionId: sessionId, forkedFromId: delta.forkedFromId ?? migratedCached.forkedFromId, codexCostNanos: Self.codexMergedCostMap( migratedCached.codexCostNanos, deltaRows: delta.rows, context: context), codexPrioritySurchargeNanos: Self.codexMergedPrioritySurchargeMap( migratedCached.codexPrioritySurchargeNanos, deltaRows: delta.rows, context: context), codexStandardCostNanos: Self.mergeCostMaps( migratedCached.codexStandardCostNanos, splitMaps.standardCostNanos), codexPriorityCostNanos: Self.mergeCostMaps( migratedCached.codexPriorityCostNanos, splitMaps.priorityCostNanos), codexStandardTokens: Self.mergeIntMaps( migratedCached.codexStandardTokens, splitMaps.standardTokens), codexPriorityTokens: Self.mergeIntMaps( migratedCached.codexPriorityTokens, splitMaps.priorityTokens), codexTurnIDs: Self.mergeCodexTurnIDs(migratedCached.codexTurnIDs, rows: delta.rows), codexRows: migratedCached.codexRows) Self.rememberScannedCodexFile( fileURL: input.fileURL, metadata: input.metadata, sessionId: sessionId, context: context, state: &state) return true } static func rescanCodexFile( input: CodexFileScanInput, context: CodexFileScanContext, cache: inout CostUsageCache, state: inout CodexScanState) throws { try context.checkCancellation?() if let cached = input.cached { self.applyFileDays(cache: &cache, fileDays: cached.days, sign: -1) } let migratedCached = input.cached.map { Self.codexFileUsageWithCostCache($0, context: context) } var usageDays = context.dropDeferredCodexRows ? [:] : Self.fileDaysOutsideScanWindow(migratedCached?.days ?? [:], range: context.range) let parsed = try Self.parseCodexFileCancellable( fileURL: input.fileURL, range: context.range, inheritedTotalsResolver: context.resources.inheritedResolver.inheritedTotals(for:atOrBefore:), checkCancellation: context.checkCancellation) let sessionId = parsed.sessionId ?? input.cached?.sessionId if let sessionId, state.seenSessionIds.contains(sessionId) { cache.files.removeValue(forKey: input.metadata.path) return } Self.mergeFileDays(existing: &usageDays, delta: parsed.days) let splitMaps = Self.codexModeSplitMaps( rows: parsed.rows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot) cache.files[input.metadata.path] = Self.makeFileUsage( mtimeUnixMs: input.metadata.mtimeUnixMs, size: input.metadata.size, days: usageDays, parsedBytes: parsed.parsedBytes, lastModel: parsed.lastModel, lastTotals: parsed.lastTotals, lastCountedTotals: parsed.lastCountedTotals, lastRawTotalsBaseline: parsed.lastRawTotalsBaseline, hasDivergentTotals: parsed.hasDivergentTotals, lastCodexTurnID: parsed.lastCodexTurnID, sessionId: sessionId, forkedFromId: parsed.forkedFromId, codexCostNanos: Self.mergeCostMaps( context.dropDeferredCodexRows ? nil : Self.costMapOutsideScanWindow(migratedCached?.codexCostNanos, range: context.range), Self.codexCostNanos( rows: parsed.rows, range: context.range, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)), codexPrioritySurchargeNanos: Self.mergeCostMaps( context.dropDeferredCodexRows ? nil : Self.costMapOutsideScanWindow(migratedCached?.codexPrioritySurchargeNanos, range: context.range), Self.codexPrioritySurchargeNanos( rows: parsed.rows, range: context.range, priorityTurns: context.resources.priorityTurns, modelsDevCatalog: context.resources.modelsDevCatalog, modelsDevCacheRoot: context.resources.modelsDevCacheRoot)), codexStandardCostNanos: Self.mergeCostMaps( context.dropDeferredCodexRows ? nil : Self.costMapOutsideScanWindow(migratedCached?.codexStandardCostNanos, range: context.range), splitMaps.standardCostNanos), codexPriorityCostNanos: Self.mergeCostMaps( context.dropDeferredCodexRows ? nil : Self.costMapOutsideScanWindow(migratedCached?.codexPriorityCostNanos, range: context.range), splitMaps.priorityCostNanos), codexStandardTokens: Self.mergeIntMaps( context.dropDeferredCodexRows ? nil : Self.intMapOutsideScanWindow(migratedCached?.codexStandardTokens, range: context.range), splitMaps.standardTokens), codexPriorityTokens: Self.mergeIntMaps( context.dropDeferredCodexRows ? nil : Self.intMapOutsideScanWindow(migratedCached?.codexPriorityTokens, range: context.range), splitMaps.priorityTokens), codexTurnIDs: context.dropDeferredCodexRows ? Self.codexTurnIDs(rows: parsed.rows) : Self.mergeCodexTurnIDs(migratedCached?.codexTurnIDs, rows: parsed.rows), codexRows: context.dropDeferredCodexRows ? nil : migratedCached?.codexRows) Self.applyFileDays(cache: &cache, fileDays: cache.files[input.metadata.path]?.days ?? [:], sign: 1) Self.rememberScannedCodexFile( fileURL: input.fileURL, metadata: input.metadata, sessionId: sessionId, context: context, state: &state) } static func mergeFileDays( existing: inout [String: [String: [Int]]], delta: [String: [String: [Int]]]) { for (day, models) in delta { var dayModels = existing[day] ?? [:] for (model, packed) in models { let existingPacked = dayModels[model] ?? [] let merged = self.addPacked(a: existingPacked, b: packed, sign: 1) if merged.allSatisfy({ $0 == 0 }) { dayModels.removeValue(forKey: model) } else { dayModels[model] = merged } } if dayModels.isEmpty { existing.removeValue(forKey: day) } else { existing[day] = dayModels } } } static func fileDaysOutsideScanWindow( _ days: [String: [String: [Int]]], range: CostUsageDayRange) -> [String: [String: [Int]]] { days.filter { !CostUsageDayRange.isInRange(dayKey: $0.key, since: range.scanSinceKey, until: range.scanUntilKey) } } static func applyFileDays(cache: inout CostUsageCache, fileDays: [String: [String: [Int]]], sign: Int) { for (day, models) in fileDays { var dayModels = cache.days[day] ?? [:] for (model, packed) in models { let existing = dayModels[model] ?? [] let merged = self.addPacked(a: existing, b: packed, sign: sign) if merged.allSatisfy({ $0 == 0 }) { dayModels.removeValue(forKey: model) } else { dayModels[model] = merged } } if dayModels.isEmpty { cache.days.removeValue(forKey: day) } else { cache.days[day] = dayModels } } } static func pruneDays(cache: inout CostUsageCache, sinceKey: String, untilKey: String) { for key in cache.days.keys where !CostUsageDayRange.isInRange(dayKey: key, since: sinceKey, until: untilKey) { cache.days.removeValue(forKey: key) } } static func pruneForceRescanFilesOutsideWindow( cache: inout CostUsageCache, range: CostUsageDayRange, isForceRescan: Bool) { guard isForceRescan else { return } for key in cache.files.keys { guard let old = cache.files[key] else { continue } guard !old.touchesCodexScanWindow(sinceKey: range.scanSinceKey, untilKey: range.scanUntilKey) else { continue } Self.applyFileDays(cache: &cache, fileDays: old.days, sign: -1) cache.files.removeValue(forKey: key) } } static func requestedWindowExpandsCache(range: CostUsageDayRange, cache: CostUsageCache) -> Bool { guard let cachedSince = cache.scanSinceKey, let cachedUntil = cache.scanUntilKey else { return cache.lastScanUnixMs != 0 || !cache.files.isEmpty || !cache.days.isEmpty } return range.scanSinceKey < cachedSince || range.scanUntilKey > cachedUntil } static func addPacked(a: [Int], b: [Int], sign: Int) -> [Int] { let len = max(a.count, b.count) var out: [Int] = Array(repeating: 0, count: len) for idx in 0.. CostUsageDailyReport { var entries: [CostUsageDailyReport.Entry] = [] var totalInput = 0 var totalOutput = 0 var totalTokens = 0 var totalCost: Double = 0 var costSeen = false let dayKeys = cache.days.keys.sorted().filter { CostUsageDayRange.isInRange(dayKey: $0, since: range.sinceKey, until: range.untilKey) } let costNanosByDayModel = self.codexCostNanosByDayModel(cache: cache, range: range) let prioritySurchargeNanosByDayModel = self.codexPrioritySurchargeNanosByDayModel(cache: cache, range: range) let standardCostNanosByDayModel = self.codexStandardCostNanosByDayModel(cache: cache, range: range) let priorityCostNanosByDayModel = self.codexPriorityCostNanosByDayModel(cache: cache, range: range) let standardTokensByDayModel = self.codexStandardTokensByDayModel(cache: cache, range: range) let priorityTokensByDayModel = self.codexPriorityTokensByDayModel(cache: cache, range: range) let hasCodexRows = cache.files.values.contains { !($0.codexRows?.isEmpty ?? true) } let rowsByDayModel = hasCodexRows ? self.codexRowsByDayModel(cache: cache, range: range) : [:] for day in dayKeys { guard let models = cache.days[day] else { continue } let modelNames = models.keys.sorted() var dayInput = 0 var dayOutput = 0 var breakdown: [CostUsageDailyReport.ModelBreakdown] = [] var dayCost: Double = 0 var dayCostSeen = false for model in modelNames { let packed = models[model] ?? [0, 0, 0] let input = packed[safe: 0] ?? 0 let cached = packed[safe: 1] ?? 0 let output = packed[safe: 2] ?? 0 let totalTokens = input + output dayInput += input dayOutput += output let rows = rowsByDayModel[day]?[model] let rowCostBreakdown = rows.map { self.codexRowCostBreakdown( rows: $0, priorityTurns: priorityTurns, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) } let cachedBaseCost = costNanosByDayModel[day]?[model].map { Double($0) / Self.costScale } let rowTotalCost = cachedBaseCost == nil ? rowCostBreakdown?.totalCostUSD : nil let standardCost = standardCostNanosByDayModel[day]?[model].map { Double($0) / Self.costScale } ?? (rowCostBreakdown?.hasModeSplit == true ? rowCostBreakdown?.optionalStandardCostUSD : nil) let priorityCost = priorityCostNanosByDayModel[day]?[model].map { Double($0) / Self.costScale } ?? (rowCostBreakdown?.hasModeSplit == true ? rowCostBreakdown?.optionalPriorityCostUSD : nil) let splitTotalCost: Double? = if standardCost != nil || priorityCost != nil { (standardCost ?? 0) + (priorityCost ?? 0) } else { nil } var cost = splitTotalCost ?? cachedBaseCost ?? rowTotalCost ?? CostUsagePricing.codexCostUSD( model: model, inputTokens: input, cachedInputTokens: cached, outputTokens: output, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) if splitTotalCost == nil, let surchargeNanos = prioritySurchargeNanosByDayModel[day]?[model], cachedBaseCost != nil { cost = (cost ?? 0) + (Double(surchargeNanos) / Self.costScale) } else if splitTotalCost == nil, rowTotalCost == nil, !priorityTurns.isEmpty, let rows, let surcharge = self.codexPrioritySurchargeUSD( rows: rows, priorityTurns: priorityTurns, modelsDevCatalog: modelsDevCatalog, modelsDevCacheRoot: modelsDevCacheRoot) { cost = (cost ?? 0) + surcharge } let standardModeTokens = standardTokensByDayModel[day]?[model] ?? (rowCostBreakdown?.hasModeSplit == true ? rowCostBreakdown?.optionalStandardTokens : nil) let priorityModeTokens = priorityTokensByDayModel[day]?[model] ?? (rowCostBreakdown?.hasModeSplit == true ? rowCostBreakdown?.optionalPriorityTokens : nil) let hasModeSplit = priorityCost != nil || priorityModeTokens != nil breakdown.append( CostUsageDailyReport.ModelBreakdown( modelName: model, costUSD: cost, totalTokens: totalTokens, standardCostUSD: hasModeSplit ? standardCost : nil, priorityCostUSD: hasModeSplit ? priorityCost : nil, standardTokens: hasModeSplit ? standardModeTokens : nil, priorityTokens: hasModeSplit ? priorityModeTokens : nil)) if let cost { dayCost += cost dayCostSeen = true } } let dayTotal = dayInput + dayOutput let entryCost = dayCostSeen ? dayCost : nil entries.append(CostUsageDailyReport.Entry( date: day, inputTokens: dayInput, outputTokens: dayOutput, totalTokens: dayTotal, costUSD: entryCost, modelsUsed: modelNames, modelBreakdowns: Self.sortedModelBreakdowns(breakdown))) totalInput += dayInput totalOutput += dayOutput totalTokens += dayTotal if let entryCost { totalCost += entryCost costSeen = true } } let summary: CostUsageDailyReport.Summary? = entries.isEmpty ? nil : CostUsageDailyReport.Summary( totalInputTokens: totalInput, totalOutputTokens: totalOutput, totalTokens: totalTokens, totalCostUSD: costSeen ? totalCost : nil) return CostUsageDailyReport(data: entries, summary: summary) } static func sortedModelBreakdowns(_ breakdowns: [CostUsageDailyReport.ModelBreakdown]) -> [CostUsageDailyReport.ModelBreakdown] { breakdowns.sorted { lhs, rhs in let lhsCost = lhs.costUSD ?? -1 let rhsCost = rhs.costUSD ?? -1 if lhsCost != rhsCost { return lhsCost > rhsCost } let lhsTokens = lhs.totalTokens ?? -1 let rhsTokens = rhs.totalTokens ?? -1 if lhsTokens != rhsTokens { return lhsTokens > rhsTokens } return lhs.modelName > rhs.modelName } } static func parseDayKey(_ key: String) -> Date? { let parts = key.split(separator: "-") guard parts.count == 3 else { return nil } guard let year = Int(parts[0]), let month = Int(parts[1]), let day = Int(parts[2]) else { return nil } var comps = DateComponents() comps.calendar = Calendar.current comps.timeZone = TimeZone.current comps.year = year comps.month = month comps.day = day comps.hour = 12 return comps.date } } extension Data { func containsAscii(_ needle: String) -> Bool { guard let n = needle.data(using: .utf8) else { return false } return self.range(of: n) != nil } } extension [Int] { subscript(safe index: Int) -> Int? { if index < 0 { return nil } if index >= self.count { return nil } return self[index] } } extension [UInt8] { subscript(safe index: Int) -> UInt8? { if index < 0 { return nil } if index >= self.count { return nil } return self[index] } } extension CostUsageFileUsage { func touchesCodexScanWindow(sinceKey: String, untilKey: String) -> Bool { self.days.keys.contains { CostUsageScanner.CostUsageDayRange.isInRange(dayKey: $0, since: sinceKey, until: untilKey) } } }