Skip to content

Sync upstream v0.54.0 with iOS 1.22.0 - #99

Merged
o1xhack merged 169 commits into
mobile-devfrom
upstream-sync/v0.54.0-mobile.1.22.0
Aug 23, 2026
Merged

Sync upstream v0.54.0 with iOS 1.22.0#99
o1xhack merged 169 commits into
mobile-devfrom
upstream-sync/v0.54.0-mobile.1.22.0

Conversation

@o1xhack

@o1xhack o1xhack commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Merge authoritative upstream releases v0.53.0 through v0.54.0 as one Mac release train.
  • Preserve fork release, README, CI, Production CloudKit, Mac-to-iOS sync, and collision-safe versioning contracts.
  • Add the iOS 1.22 cost provenance, coverage, token-mix, cross-time-zone, and multi-Mac compatibility handling.
  • Prepare Mac 0.54.0.1 build 127.1 with Sparkle version 127.1.1.22.0 and iOS 1.22.0 build 195.

Issue linkage

Verification

  • Mac release build passed.
  • Mac serial full regression: 9,671 tests, 0 failures.
  • iOS unit target: 731 tests, 0 failures; Release simulator build passed after the 1.22 relabel.
  • Full lint passed with 0 SwiftFormat or SwiftLint findings; all four iOS localizations are translated.
  • Production CloudKit schema audit: NO_DEPLOY; providerPayloadVersion remains 1 and new data stays in the existing opaque payload.
  • The 16-case old/new compatibility matrix is recorded as substituted evidence, not physical four-device QA.
  • Local product-source review blockers: 0. GitHub exact-current-head Codex review and PR Fast Checks remain merge gates.

Release boundary

This PR does not publish a tag, Mac release, appcast, TestFlight build, or CloudKit schema deploy.

elijahfriedman and others added 30 commits August 16, 2026 20:52
A nil cost on one subscription no longer wipes the currency group header.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Named models with tokens but no price stay visible as a partial list instead of wiping the whole breakdown.

Co-authored-by: Cursor <cursoragent@cursor.com>
A missing totalCents on one usage event no longer voids that day's priced siblings or the rest of the 30-day window.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep 7-day and 30-day windows, and scan local Codex history out to 365 days so cumulative spend is not truncated at a month.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep menu token publications on costUsageHistoryDays, and refresh a separate 365-day spend snapshot slot so All can include days outside Last 30 days.

Co-authored-by: Cursor <cursoragent@cursor.com>
Usage & Spend 30d vs All renders show MiniMax-M3 only in All, and the share card prefixes tracked tokens with ~ when a peer is unknown.

Co-authored-by: Cursor <cursoragent@cursor.com>
Bare DeepSeek IDs now invalidate Pi pricing caches and trigger models.dev refresh on the same vendor list used for lookup.

Co-authored-by: Cursor <cursoragent@cursor.com>
A negative totalCents now latches the accumulator closed so a later priced sibling cannot restore a lower-bound dollar total.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep Auto-only remapping for selected SuperGrok accounts so CLI/web
stay authoritative. Prefer GROK_OAUTH_TOKEN over a valid auth.json so
a selected pasted bearer cannot display the local grok login account.
Project Cookie: token accounts into the Grok settings snapshot so Auto
and Web fetches use the selected header instead of a configured or
browser cookie.
Preserve the existing Grok Auto fallback: CLI, then browser cookies,
then OAuth. Selected pasted bearers and explicit SuperGrok OAuth still
use the OAuth path only.
Auto is CLI, then SuperGrok OAuth CLI-proxy, then browser cookies.
A failed Auto proxy no longer falls through to bearer gRPC, so cookies
still run next. Explicit SuperGrok OAuth keeps proxy-then-gRPC.
Auto is now CLI, SuperGrok OAuth CLI-proxy, browser cookies, then
bearer gRPC. Explicit SuperGrok OAuth stays cookie-free proxy-then-gRPC.
Co-authored-by: Cursor <cursoragent@cursor.com>
The pricing-file change left CodexParserHash.generated.swift stale and failed lint.

Co-authored-by: Cursor <cursoragent@cursor.com>
steipete and others added 24 commits August 19, 2026 17:46
* fix(claude): keep at-limit claude-swap cards complete

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: cite at-limit claude-swap card PR

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): keep non-limit claude-swap sentinels metrics-less

Project usage only for ok and unavailable slots, and retain a previous snapshot only when the email still matches and a 100% window has not reset.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): drop expired windows from retained at-limit snapshots

Keeping the whole previous snapshot after any sibling reset still showed
"Resets now" on lanes that had already recovered.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): retain at-limit swap windows in CLI and dashboard

One-shot cards and dashboard now reuse the last slot windows so an
unavailable cswap row with null usage keeps the exhausted bars.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): bind retained at-limit windows to the slot account

A SHA-256 fingerprint in the retained-usage cache rejects leftover 100% bars after the same slot is reused by a different account.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): prune expired windows on attached unavailable snapshots

Direct cswap unavailable payloads with mixed reset times now drop already-reset lanes the same way retained snapshots do.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): keep retained at-limit bars on dashboard and app restart

Seed the app projection from the retained-usage cache after a relaunch, and render dashboard windows alongside the deferred/limit note instead of returning after the error.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): require an email before retaining at-limit windows

Slot-only fingerprints reused bars after an email-less account was replaced. Decline persistence and reuse unless the row has an email, and move the changelog note to 0.54.1 Unreleased.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): save retained usage only for current refreshes

Stale claude-swap refreshes were writing the on-disk cache before the generation guard, and 100% windows without a future reset were kept forever.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ci): flatten fingerprint matching and expect localized API key details

SwiftFormat rejected the nested guard wrap, and OpenRouter detail titles now follow the #3084 localization pass.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(claude): drop retained windows without a future reset

A non-exhausted session lane with no resetsAt was kept beside a weekly at-limit bar and re-saved until that weekly reset, so a five-hour value could linger for days.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(i18n): cover provider detail edge cases

* test: add provider localization proof

* fix(i18n): localize zai session label

* fix(i18n): scope provider detail localization

* docs: add provider localization screenshots

* docs: justify provider localization scope

* fix(i18n): localize factory rate window label
…86 scoping

#3086's scoping guard returned early for non-DeepSeek/z.ai providers, but its
own test expects generic providers' section titles and row labels to pass
through the shared L() catalog (canonical values untouched). Restore that
path for the generic branch.
* Preserve menu item subclasses during cached swaps

* Add native menu proof for cached shell swaps

---------

Co-authored-by: Kiran Magic <262980978+kiranmagic7@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* Extend menu bar conditionals beyond usage percentages

Conditional predicates could only compare four percent-used windows. They now
compare 18 metrics across four units: percent windows, the direct
primary/secondary/tertiary lanes, four reset countdowns, three pace deltas,
run-out, credit balance, and today/30-day cost. Metrics with two readings
(percent windows, lanes, balance) gain a used/remaining select, so
"session > 50% used and session resets in < 2h" is expressible.

Pace, run-out, balance and cost were only carried as display strings, which
cannot be compared, so MenuBarLayoutRenderMetrics carries their numeric twins
pre-rounded to the same granularity as the text they mirror.

Three refresh gates needed widening for the new data dependencies:
- The title cache key had no component that moves with the clock, so a
  countdown predicate would have served its pre-flip title indefinitely. It now
  keys on the per-conditional outcome, evaluated once per render.
- The four observation signatures gated on display tokens; a predicate on cost
  or balance has no token. They now also read the conditionals' metrics, which
  additionally fixes lane tokens inside conditional branches being invisible to
  the lane signature.
- A reset-countdown predicate flips at an instant nothing else ticks on, so the
  countdown scheduler wakes at `resetsAt - threshold`.

The conditional library is now decoded element-wise: this change makes
forward-incompatible metric values possible for the first time, and one unknown
value would otherwise have wiped the whole library on a downgrade.

Ships an "Auto % / Resets in" default that renders the automatic percentage
while the lane has headroom and the reset countdown once it is spent.

* Sign the readings conditional predicates actually compare

Three observation-signature gaps let a predicate flip without a redraw:

- Cost signatures recorded only the currency-formatted string, so two token-cost
  updates could cross a threshold while both formatted to the same cent. A
  referenced cost metric now signs the unrounded amount losslessly.
- The balance signature recorded only the rendered "Remaining" row, so a
  `balance used` predicate — which reads the "Used" row no token surfaces — was
  entirely unsigned. Both amounts are now signed.
- The lane signature recorded the displayed reading, which follows
  `usageBarsShowUsed` and clamps remaining at zero, while `RateWindow.usedPercent`
  deliberately preserves over-quota values. A used-direction predicate such as
  `primaryLane > 105%` could move 104% -> 106% against a constant `0.000`.

The lane signature is now scoped to what the layout renders, and a new
conditional-window signature covers what conditionals read: the raw used percent
(which remaining derives from, so it covers both directions) plus `resetsAt`,
which countdown predicates depend on and no display token contributes.

* Tick clock-derived predicates that no token schedules

`menuBarWeeklyPaceRefreshDelays` is gated on a placed `.pace(.weekly)` token and
only wakes once, at the pace-eligibility boundary. Excluding `runsOutIn` from the
conditional reset schedule on the assumption that scheduler covered it therefore
left a hole: a layout whose only pace or run-out reference is a predicate got no
clock wake-up at all, so it kept rendering the branch that was true when the
value last moved.

Referenced weekly-pace predicates now also trigger the eligibility wake-up, and
any referenced pace or run-out predicate schedules a minute tick. Both numbers
are pre-rounded to the granularity the menu bar shows -- whole percentage points
and whole minutes -- so a minute tick is exactly enough, and it is the cadence a
`.resetCountdown` token already costs. Money predicates deliberately schedule
nothing: they move only when new provider data arrives.

* Keep older releases' conditional libraries readable on downgrade

Decoding the library element-wise only helps builds that already have the
lenient decoder. The build a user actually downgrades to decodes
`menuBarLayoutConditionals` strictly and falls back to `[]`, so one saved rule
using a new metric would empty the entire library there.

The conditional library now dual-writes the way layouts already do:
`menuBarLayoutConditionalsV2` keeps full fidelity, and the original key keeps an
older-readable projection. `loadLibrary` mirrors `preferredLayout` — the current
key wins unless the legacy key disagrees with its own projection, which only
happens when an older release wrote it, and that edit must survive.

The projection drops an entry when any clause uses a metric outside the original
four, and also when any clause uses a non-`.used` direction. The second case is
the subtler one: an older release's synthesized decoder ignores the unknown
`direction` key, so `session remaining > 80` would come back as
`session used > 80` and render the opposite branch. A missing rule is visibly
missing; an inverted one is not.

* Drop cost metrics that could not be converted to USD

`UsageFormatter.convertedCost` returns the source amount unchanged when it has no
rate for the provider's currency, and both cost producers passed that value
straight through as `costTodayUSD`/`cost30dUSD`. A `Cost today > 5 USD` rule
would then compare, say, €6 against a $5 threshold and pick the wrong branch.

Both producers now keep the amount only when the conversion actually landed in
USD. Otherwise the predicate sees no value and evaluates false, which is the
existing contract for a metric the provider does not report. The rendered text is
untouched and still shows the provider's own currency.

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix: prevent RPC pipe writes from aborting after child exit

* test: repin UsageFetcher codex identity anchor after pipe-write refactor
* Publish live Grok tokens and xAI spend into Usage & Spend

Enabled Grok and xAI now join the shared spend catalog instead of only
inflating the unavailable denominator. xAI contributes vendor-metered
daily USD from the Management API chart; Grok contributes local session
tokens. SuperGrok credits and xAI prepaid balance stay quotas, not spend.

* docs: add #3085 to changelog

* Fix lint on Grok and xAI spend messages

Wrap no-data copy under 120 characters and drop a redundant throws
on the prepaid-balance mapping test.

* Fix Grok/xAI spend publication, Today, and coverage

Preserve xAI analytics failures as unavailable instead of known-zero
spend, publish local Grok tokens when remote billing fails, pin Today
to the current UTC/local day, and keep xAI history as a 30-day source.

* Fix Grok and xAI spend edge cases

* Fix OpenRouter localization test after #3086

#3086 scoped localizedProviderDetails to DeepSeek and z.ai, so generic
OpenRouter details keep canonical English. The merge test still expected
zh-Hant "API 金鑰" and failed macOS shard 0.

* Align OpenRouter localization test with main

Peter restored generic title/row L() localization in 84a4ca7 after
#3086 scoped it away. The merge kept the canonical-English assertion
from the earlier CI fix; match the restored shared catalog instead.

* test: reconcile gatekeeper anchors and fingerprints with Grok/xAI spend clusters

* test: include Grok and xAI in the cost-capable dashboard source contract

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
@o1xhack

o1xhack commented Aug 23, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 4535e55bf4

ℹ️ 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".

@o1xhack
o1xhack merged commit d7eddbc into mobile-dev Aug 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.