Add a read-only OpenCodex usage.jsonl parser and independent cache - #3018
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
|
🦞👀 Pull request received. I will update this pull request when review starts. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2413d5b3ef
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let cost = Self.listPriceUSD(entry: entry, customPricing: customPricing) | ||
| if let cost { |
There was a problem hiding this comment.
Avoid pricing entries without reported usage
When an unreported or unsupported row names a recognized model but has no usage payload, listPriceUSD prices four zero token counts and returns 0. That sets sawCost, publishes costUSD == 0, and makes the snapshot provenance a list-price estimate even though the source supplied no meterable usage. Gate pricing on a reported/estimated status with usable token data so these rows remain unpriced or unmetered rather than appearing as confirmed zero spend.
Useful? React with 👍 / 👎.
| sessionTokens: daily.last.flatMap(\.totalTokens), | ||
| sessionCostUSD: daily.last.flatMap(\.costUSD), | ||
| sessionRequests: daily.last.flatMap(\.requestCount), |
There was a problem hiding this comment.
Derive session totals from the current day
If the log has historical usage but no entry for today, daily.last returns the most recent prior day, so the menu's session/today cost, tokens, and requests continue showing yesterday's values. Other token snapshot construction explicitly looks up the local day containing now and emits zero when history exists but today does not; this aggregator should follow the same current-day lookup.
Useful? React with 👍 / 👎.
| } else if entry.usageStatus == .reported || entry.usageStatus == .estimated { | ||
| day.unpriced += 1 | ||
| if day.priced > 0 { day.priced -= 1 } |
There was a problem hiding this comment.
Count unpriced estimates only once
For an estimated request whose model cannot be priced, this branch increments unpriced without removing the already incremented estimated count. Because entry(dayKey:day:) defines requestCount as the sum of these buckets, one physical request becomes two requests and also inflates the coverage denominator. Reclassify the estimate into one bucket, or keep request count independent from overlapping coverage dimensions.
Useful? React with 👍 / 👎.
| : CostUsagePricing.codexAggregateCostUSD( | ||
| model: model, | ||
| inputTokens: input, | ||
| cachedInputTokens: cached, | ||
| outputTokens: output, | ||
| modelsDevCatalog: pricing.modelsDevCatalog, | ||
| modelsDevCacheRoot: pricing.modelsDevCacheRoot) |
There was a problem hiding this comment.
Apply custom pricing to aggregate fallback rows
When a cached day lacks trusted canonical rows, this fallback calls codexAggregateCostUSD, which still resolves only models.dev or bundled pricing and never consults the new custom-pricing overlay. Consequently the same model uses the override with complete rows but silently reverts to public rates for legacy or incomplete caches, producing inconsistent spend totals. The aggregate path needs the same overlay handling while preserving its threshold safety check.
Useful? React with 👍 / 👎.
| bypassScannerDebounce: true, | ||
| calendar: self.settings.costUsageBucketCalendar) |
There was a problem hiding this comment.
Use the pinned calendar for every Codex cache read
The pinned calendar is passed only to this foreground refresh. The startup hydration path still calls loadCachedCodexTokenSnapshotResult on the default fetcher, and the Spend Dashboard constructs CostUsageFetcher(cacheRoot:) without a calendar, so after the system timezone changes those paths bucket the same cache using .current while this path uses the pinned zone. This can initially publish different day totals and lets dashboard scans re-bucket history; propagate the pinned calendar through the shared fetcher and cached/dashboard loaders as well.
Useful? React with 👍 / 👎.
|
Codex review: needs real behavior proof before merge. Reviewed August 17, 2026, 3:29 PM ET / 19:29 UTC. ClawSweeper reviewWhat this changesThis stacked PR adds OpenCodex JSONL usage parsing, an independent SQLite cache, and shared spend-accounting support for the planned source. Merge readiness⛔ Blocked until real behavior proof from a real setup is added - 10 items remain Keep open: the new local usage source and cache require maintainer sign-off and real behavior proof, and the accounting path currently omits reasoning tokens when totals or prices must be derived. Priority: P2 Review scores
Verification
How this fits togetherCodexBar converts local usage data into estimated spend for menu and dashboard summaries. The proposed OpenCodex reader would feed that pipeline through a separate cache without changing quota-provider data. flowchart LR
A[OpenCodex usage log] --> B[JSONL parser]
B --> C[Independent SQLite cache]
C --> D[Spend aggregation]
D --> E[Cost snapshot]
E --> F[Menu and dashboard]
Decision needed
Why: VISION.md requires sign-off for a new feature that adds local data storage and privacy behavior; this stacked PR does not establish that approval. Before merge
Findings
Agent review detailsSecurityNone. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Approve an opt-in OpenCodex spend source only after repairing reasoning-token accounting and showing redacted real-log evidence for the complete source-to-summary path. Do we have a high-confidence way to reproduce the issue? Yes for the accounting defect: a valid row with reasoning output but no total reaches the fallback path, which visibly omits those tokens and their estimated output cost. Is this the best way to solve the issue? No: the implementation must include reasoning output in derived totals and output-rate pricing, then receive sign-off for the opt-in source and cache direction. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f7723d33a169. LabelsLabel justifications:
EvidenceAcceptance criteria:
What I checked:
Likely related people:
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (1 earlier review cycle)
|
Pinned calendars now reach cached Codex reads, vendor-reported costs keep their source, and shorter summaries no longer inherit snapshot-wide metered spend. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Cached day totals now honor overlay rates, including explicit free rates, without rereading custom-pricing.json for every usage row. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…imit. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Unreported rows no longer publish zero spend, estimated unpriced requests count once, and duplicate request IDs replace instead of aborting the cache write. Co-authored-by: Cursor <cursoragent@cursor.com>
2413d5b to
a532d41
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a532d413d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { | ||
| return cost | ||
| } | ||
| guard let pricing = self.resolvedCodexPricing( |
There was a problem hiding this comment.
Preserve incomplete matching custom prices as unknown
When custom-pricing.json contains a matching model but intentionally omits a rate for a token class that has usage, estimatedCodexCostUSD returns nil and this fallback silently substitutes models.dev or bundled pricing. That violates the overlay's stated behavior that missing fields remain unknown, and produces a public-list-price total instead of an unpriced result; distinguish “no matching overlay” from “matching but incomplete overlay” before falling back.
Useful? React with 👍 / 👎.
| inputTokens: input, | ||
| cachedInputTokens: cacheRead, | ||
| outputTokens: output, | ||
| cacheWriteInputTokens: cacheWrite) |
There was a problem hiding this comment.
Include exclusive cached tokens in bundled pricing input
For OpenCodex rows with cache reads or writes and no custom price, this passes the separately counted cache tokens to an API whose inputTokens contract treats cached tokens as a subset and subtracts them from input. The OpenCodex model instead adds cache classes to input when resolving totals, so an entry with 100 input and 10 cache-read tokens is priced as 90 regular + 10 cached rather than 100 regular + 10 cached, underestimating spend. Convert the exclusive classes to the total-input representation expected by codexCostUSD.
Useful? React with 👍 / 👎.
| costUSD: entryCost, | ||
| modelsUsed: modelNames, | ||
| modelBreakdowns: Self.sortedModelBreakdowns(breakdown), | ||
| unpricedRequestCount: entryCost == nil && dayTotal > 0 ? 1 : nil, |
There was a problem hiding this comment.
Expose unpriced models on partially priced days
When a day contains at least one priced model and at least one model whose cost is nil, entryCost remains non-nil because any priced model sets dayCostSeen, so this condition records no unpriced coverage. The resulting day carries only the partial dollar sum while coverageCounts classifies it as priced, hiding the omitted model from Spend Dashboard coverage; track missing-cost model groups independently of whether some cost was seen.
Useful? React with 👍 / 👎.
Summary
~/.opencodex/usage.jsonl(orOPENCODEX_HOME) as a spend-only source, not a quota Provider.opencodex-usage.sqlitewith requestId dedupe; never write Codex cost tables.Stacked PR 3/5 (Wave D). Depends on #3015 and #3016.
Review follow-up
$0).entry(in:forLocalDayContaining:)instead ofdaily.last.INSERT OR REPLACEand dedupe byrequestIDbefore insert.costProvenance: .listPriceEstimate.Test plan
Fixtures only. No Keychain, no live accounts, no
~/.opencodex.opencodex-usage.sqlite