Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 0.49.3 — Unreleased

### Fixed
- Codex: preserve request-level pricing tiers while reconciling forked usage, preventing day aggregates from triggering long-context rates (#2858). Thanks @thomaschow19!
- CLI: stop standalone version lookup from walking past the filesystem root and hanging with unbounded memory on affected macOS versions (#2856). Thanks @Manwholikespie!
- Cost store: prevent launch-time executor-assumption crashes on macOS 15 by keeping synchronous SQLite cache bridges on their validated serial queue (#2857). Thanks @Manwholikespie!

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Generated by Scripts/regenerate-codex-parser-hash.sh. Do not edit by hand.

enum CodexParserHash {
static let value = "b975eb705f905b9a"
static let value = "43609cc56f76a003"
}
110 changes: 55 additions & 55 deletions Sources/CodexBarCore/Vendored/CostUsage/CostUsagePricing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,51 @@ enum CostUsagePricing {
cacheWriteInputTokens: Int = 0,
modelsDevCatalog: ModelsDevCatalog? = nil,
modelsDevCacheRoot: URL? = nil) -> Double?
{
guard let pricing = self.resolvedCodexPricing(
model: model,
modelsDevCatalog: modelsDevCatalog,
modelsDevCacheRoot: modelsDevCacheRoot)
else { return nil }
return self.codexCostUSD(
pricing: pricing,
inputTokens: inputTokens,
cachedInputTokens: cachedInputTokens,
cacheWriteInputTokens: cacheWriteInputTokens,
outputTokens: outputTokens)
}

static func codexAggregateCostUSD(
model: String,
inputTokens: Int,
cachedInputTokens: Int,
outputTokens: Int,
cacheWriteInputTokens: Int = 0,
modelsDevCatalog: ModelsDevCatalog? = nil,
modelsDevCacheRoot: URL? = nil) -> Double?
{
guard let pricing = self.resolvedCodexPricing(
model: model,
modelsDevCatalog: modelsDevCatalog,
modelsDevCacheRoot: modelsDevCacheRoot)
else { return nil }
if let thresholdTokens = pricing.thresholdTokens,
max(0, inputTokens) > thresholdTokens
{
return nil
}
return self.codexCostUSD(
pricing: pricing,
inputTokens: inputTokens,
cachedInputTokens: cachedInputTokens,
cacheWriteInputTokens: cacheWriteInputTokens,
outputTokens: outputTokens)
}

private static func resolvedCodexPricing(
model: String,
modelsDevCatalog: ModelsDevCatalog?,
modelsDevCacheRoot: URL?) -> CodexPricing?
{
let key = self.normalizeCodexModel(model)
guard key != self.codexUnattributedModel else { return nil }
Expand Down Expand Up @@ -524,32 +569,25 @@ enum CostUsagePricing {
?? lookup.pricing.inputCostPerTokenAboveThreshold
?? lookup.pricing.inputCostPerToken
: bundledLongContext?.cacheWriteInputCostPerTokenAboveThreshold)
return self.codexCostUSD(
pricing: lookup.pricing,
return CodexPricing(
inputCostPerToken: lookup.pricing.inputCostPerToken,
outputCostPerToken: lookup.pricing.outputCostPerToken,
cacheReadInputCostPerToken: lookup.pricing.cacheReadInputCostPerToken
?? bundled?.cacheReadInputCostPerToken,
displayLabel: nil,
cacheWriteInputCostPerToken: lookup.pricing.cacheCreationInputCostPerToken
?? bundled?.cacheWriteInputCostPerToken,
thresholdTokens: bundled?.thresholdTokens ?? lookup.pricing.thresholdTokens,
inputCostPerTokenAboveThreshold: lookup.pricing.inputCostPerTokenAboveThreshold
?? bundledLongContext?.inputCostPerTokenAboveThreshold,
outputCostPerTokenAboveThreshold: lookup.pricing.outputCostPerTokenAboveThreshold
?? bundledLongContext?.outputCostPerTokenAboveThreshold,
cacheReadInputCostPerToken: lookup.pricing.cacheReadInputCostPerToken
?? bundled?.cacheReadInputCostPerToken,
cacheReadInputCostPerTokenAboveThreshold: cacheReadAboveThreshold,
cacheWriteInputCostPerToken: lookup.pricing.cacheCreationInputCostPerToken
?? bundled?.cacheWriteInputCostPerToken,
cacheWriteInputCostPerTokenAboveThreshold: cacheWriteAboveThreshold,
inputTokens: inputTokens,
cachedInputTokens: cachedInputTokens,
cacheWriteInputTokens: cacheWriteInputTokens,
outputTokens: outputTokens)
cacheWriteInputCostPerTokenAboveThreshold: cacheWriteAboveThreshold)
}

guard let pricing = self.codex[key] else { return nil }
return self.codexCostUSD(
pricing: pricing,
inputTokens: inputTokens,
cachedInputTokens: cachedInputTokens,
cacheWriteInputTokens: cacheWriteInputTokens,
outputTokens: outputTokens)
return pricing
}

static func codexPriorityCostUSD(
Expand Down Expand Up @@ -628,44 +666,6 @@ enum CostUsagePricing {
+ (Double(max(0, outputTokens)) * outputRate)
}

private static func codexCostUSD(
pricing: ModelsDevPricingInfo,
thresholdTokens: Int? = nil,
inputCostPerTokenAboveThreshold: Double? = nil,
outputCostPerTokenAboveThreshold: Double? = nil,
cacheReadInputCostPerToken: Double? = nil,
cacheReadInputCostPerTokenAboveThreshold: Double? = nil,
cacheWriteInputCostPerToken: Double? = nil,
cacheWriteInputCostPerTokenAboveThreshold: Double? = nil,
inputTokens: Int,
cachedInputTokens: Int,
cacheWriteInputTokens: Int = 0,
outputTokens: Int) -> Double
{
self.codexCostUSD(
pricing: CodexPricing(
inputCostPerToken: pricing.inputCostPerToken,
outputCostPerToken: pricing.outputCostPerToken,
cacheReadInputCostPerToken: cacheReadInputCostPerToken
?? pricing.cacheReadInputCostPerToken,
displayLabel: nil,
cacheWriteInputCostPerToken: cacheWriteInputCostPerToken
?? pricing.cacheCreationInputCostPerToken,
thresholdTokens: thresholdTokens ?? pricing.thresholdTokens,
inputCostPerTokenAboveThreshold: inputCostPerTokenAboveThreshold
?? pricing.inputCostPerTokenAboveThreshold,
outputCostPerTokenAboveThreshold: outputCostPerTokenAboveThreshold
?? pricing.outputCostPerTokenAboveThreshold,
cacheReadInputCostPerTokenAboveThreshold: cacheReadInputCostPerTokenAboveThreshold
?? pricing.cacheReadInputCostPerTokenAboveThreshold,
cacheWriteInputCostPerTokenAboveThreshold: cacheWriteInputCostPerTokenAboveThreshold
?? pricing.cacheCreationInputCostPerTokenAboveThreshold),
inputTokens: inputTokens,
cachedInputTokens: cachedInputTokens,
cacheWriteInputTokens: cacheWriteInputTokens,
outputTokens: outputTokens)
}

static func claudeCostUSD(
model: String,
inputTokens: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ extension CostUsageScanner {
var priorityTokens: Int = 0
var sawStandardCost = false
var sawPriorityCost = false
var hasUnstableTokenRows = false
var hasTokenOverflow = false
var hasIncompletePricing = false

var optionalStandardCostUSD: Double? {
self.sawStandardCost ? self.standardCostUSD : nil
Expand Down Expand Up @@ -138,7 +141,11 @@ extension CostUsageScanner {

func isTrusted(canonicalTotalTokens: Int) -> Bool {
let (rowTokenTotal, overflow) = self.standardTokens.addingReportingOverflow(self.priorityTokens)
return !overflow && rowTokenTotal <= canonicalTotalTokens
return !self.hasUnstableTokenRows
&& !self.hasTokenOverflow
&& !self.hasIncompletePricing
&& !overflow
&& rowTokenTotal == canonicalTotalTokens
}
}

Expand All @@ -150,20 +157,37 @@ extension CostUsageScanner {
{
var breakdown = CodexRowCostBreakdown()
for row in rows {
let tokenCount = row.input + row.output
let (tokenCount, tokenOverflow) = max(0, row.input).addingReportingOverflow(max(0, row.output))
let hasTokens = row.input > 0 || row.cached > 0 || row.output > 0
if tokenOverflow {
breakdown.hasTokenOverflow = true
}
if hasTokens, row.eventIndex == nil {
breakdown.hasUnstableTokenRows = true
}
if (row.unpricedTokens ?? 0) > 0 {
breakdown.hasIncompletePricing = true
}
let priorityMetadata = row.turnID.flatMap { priorityTurns[$0] }
let isPriority = priorityMetadata != nil || row.pricingMode == "priority"
if isPriority {
breakdown.priorityTokens += tokenCount
let (total, overflow) = breakdown.priorityTokens.addingReportingOverflow(tokenCount)
breakdown.priorityTokens = overflow ? breakdown.priorityTokens : total
breakdown.hasTokenOverflow = breakdown.hasTokenOverflow || overflow
} else {
breakdown.standardTokens += tokenCount
let (total, overflow) = breakdown.standardTokens.addingReportingOverflow(tokenCount)
breakdown.standardTokens = overflow ? breakdown.standardTokens : total
breakdown.hasTokenOverflow = breakdown.hasTokenOverflow || overflow
}
guard let cost = self.codexResolvedCostUSD(
for: row,
priorityTurns: priorityTurns,
modelsDevCatalog: modelsDevCatalog,
modelsDevCacheRoot: modelsDevCacheRoot)
else { continue }
else {
breakdown.hasIncompletePricing = breakdown.hasIncompletePricing || hasTokens
continue
}
if isPriority {
breakdown.priorityCostUSD += cost
breakdown.sawPriorityCost = true
Expand Down Expand Up @@ -359,7 +383,7 @@ extension CostUsageScanner {
output: row.output,
reasoning: row.reasoning,
knownCostNanos: row.knownCostNanos,
unpricedTokens: nil,
unpricedTokens: row.unpricedTokens,
pricingModel: pricedModel,
pricingMode: isPriority ? "priority" : "standard")
}
Expand Down Expand Up @@ -1377,10 +1401,32 @@ extension CostUsageScanner {
var (totalCost, costSeen) = (0.0, false)

let dayKeys = self.codexReportDayKeys(cache: reportCache, range: range)
let authoritativeCostNanosByDayModel = self.codexCostNanosByDayModel(cache: reportCache, range: range)
var rowsByDayModel: [String: [String: [CodexUsageRow]]] = [:]
var unresolvedRowGroups = Set<CodexDayModelKey>()
var modeOwnershipMismatchGroups = Set<CodexDayModelKey>()
var priorityEvidenceGroups = Set<CodexDayModelKey>()
var incompletePricingEvidenceGroups = Set<CodexDayModelKey>()
var authoritativeCostEvidenceGroups = Set<CodexDayModelKey>()
for usage in reportCache.files.values {
for row in self.codexRowsForReadTimePricing(usage) where CostUsageDayRange.isInRange(
let reconciled = self.codexCanonicalPricingRows(usage)
unresolvedRowGroups.formUnion(reconciled.unresolvedGroups)
let modeEvidence = self.codexPricingModeEvidence(
usage: usage,
reconciledRows: reconciled.rows,
range: range,
priorityTurns: priorityTurns)
modeOwnershipMismatchGroups.formUnion(modeEvidence.mismatchGroups)
priorityEvidenceGroups.formUnion(modeEvidence.priorityGroups)
incompletePricingEvidenceGroups.formUnion(self.codexIncompletePricingEvidenceGroups(
usage: usage,
range: range,
priorityTurns: priorityTurns,
modelsDevCatalog: catalogResolver.load(modelsDevCatalogLoader),
modelsDevCacheRoot: modelsDevCacheRoot))
for row in usage.codexRows ?? [] where (row.knownCostNanos ?? 0) != 0 {
authoritativeCostEvidenceGroups.insert(CodexDayModelKey(day: row.day, model: row.model))
}
for row in reconciled.rows where CostUsageDayRange.isInRange(
dayKey: row.day,
since: range.sinceKey,
until: range.untilKey)
Expand Down Expand Up @@ -1417,23 +1463,25 @@ extension CostUsageScanner {
priorityTurns: priorityTurns,
modelsDevCatalog: catalogResolver.load(modelsDevCatalogLoader),
modelsDevCacheRoot: modelsDevCacheRoot)
let rowCostIsTrusted = rowCost?.isTrusted(canonicalTotalTokens: totalTokens) ?? true
let authoritativeCost = authoritativeCostNanosByDayModel[day]?[model].map {
Double($0) / Self.costScale
}
let canonicalCost = CostUsagePricing.codexCostUSD(
model: model,
inputTokens: input,
cachedInputTokens: cached,
outputTokens: output,
modelsDevCatalog: catalogResolver.load(modelsDevCatalogLoader),
modelsDevCacheRoot: modelsDevCacheRoot)
// Physical pricing rows can retain fork-copied usage after canonical ownership
// has deduplicated the day/model totals. Reject the whole row-derived price so
// Fast uplift from the same unowned rows cannot leak into the fallback cost.
let group = CodexDayModelKey(day: day, model: model)
let rowCostIsTrusted = !unresolvedRowGroups.contains(group)
&& !modeOwnershipMismatchGroups.contains(group)
&& rowCost?.isTrusted(canonicalTotalTokens: totalTokens) == true
let aggregateCost = priorityEvidenceGroups.contains(group)
|| incompletePricingEvidenceGroups.contains(group)
|| (unresolvedRowGroups.contains(group) && authoritativeCostEvidenceGroups.contains(group))
|| rowCost?.hasIncompletePricing == true
? nil
: CostUsagePricing.codexAggregateCostUSD(
model: model,
inputTokens: input,
cachedInputTokens: cached,
outputTokens: output,
modelsDevCatalog: catalogResolver.load(modelsDevCatalogLoader),
modelsDevCacheRoot: modelsDevCacheRoot)
let cost = rowCostIsTrusted
? rowCost?.totalCostUSD ?? authoritativeCost ?? canonicalCost
: canonicalCost
? rowCost?.totalCostUSD ?? aggregateCost
: aggregateCost
let hasModeSplit = rowCostIsTrusted && rowCost?.hasModeSplit == true
breakdown.append(
CostUsageDailyReport.ModelBreakdown(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,29 @@ extension CostUsageScanner {
?? ""
let sourceKey = usage.projectPath ?? ""
var accumulator = accumulatorsByProjectPath[projectKey] ?? CodexProjectBreakdownAccumulator()
accumulator.add(report: report, sourcePath: sourceKey)
accumulator.add(filePath: filePath, usage: usage, report: report, sourcePath: sourceKey)
accumulatorsByProjectPath[projectKey] = accumulator
}

return accumulatorsByProjectPath.map { projectPath, accumulator in
let merged = CostUsageDailyReport.merged(accumulator.reports)
var projectCache = CostUsageCache()
projectCache.files = accumulator.files
for usage in accumulator.files.values {
Self.applyFileDays(cache: &projectCache, fileDays: usage.days, sign: 1)
}
let report = Self.buildCodexReportFromCache(
cache: projectCache,
range: range,
modelsDevCatalog: resolvedModelsDevCatalog,
priorityTurns: priorityTurns)
let resolvedPath = projectPath.isEmpty ? nil : projectPath
return CostUsageProjectBreakdown(
name: Self.codexProjectName(path: resolvedPath),
path: resolvedPath,
totalTokens: merged.summary?.totalTokens,
totalCostUSD: merged.summary?.totalCostUSD,
daily: merged.data,
modelBreakdowns: Self.codexProjectModelBreakdowns(from: merged.data),
totalTokens: report.summary?.totalTokens,
totalCostUSD: report.summary?.totalCostUSD,
daily: report.data,
modelBreakdowns: Self.codexProjectModelBreakdowns(from: report.data),
sources: Self.codexProjectSourceBreakdowns(from: accumulator.reportsBySourcePath))
}
.sorted { lhs, rhs in
Expand All @@ -151,11 +160,16 @@ extension CostUsageScanner {
}

private struct CodexProjectBreakdownAccumulator {
var reports: [CostUsageDailyReport] = []
var files: [String: CostUsageFileUsage] = [:]
var reportsBySourcePath: [String: [CostUsageDailyReport]] = [:]

mutating func add(report: CostUsageDailyReport, sourcePath: String) {
self.reports.append(report)
mutating func add(
filePath: String,
usage: CostUsageFileUsage,
report: CostUsageDailyReport,
sourcePath: String)
{
self.files[filePath] = usage
self.reportsBySourcePath[sourcePath, default: []].append(report)
}
}
Expand Down
Loading