From fb12ee75e4c0c2de43b1699a397b8fb560e94d49 Mon Sep 17 00:00:00 2001 From: charlielz <1540060247@qq.com> Date: Tue, 1 Sep 2026 09:23:37 +0800 Subject: [PATCH 1/6] fix(grok): send weekly usage period and restore SuperGrok Heavy zero usage grok.com's billing API now rejects the empty GetGrokCreditsConfig request (grpc-status 13, "Missing request message."), so the Grok usage fetch always failed and the card never showed a percentage. Send the request with usage_period_type: WEEKLY explicitly. SuperGrok Heavy weekly-limit frames carry a per-period limit but no used amount, so the previous parse rule classified them as unknown usage and the adoption guard rejected the value as fabricated 0%. A limit-declared period is a fully determined zero-usage response on the wire, so publish it as 0% and let the web billing candidate fill the usage bar. --- CHANGELOG.md | 1 + .../Grok/GrokWebBillingFetcher.swift | 26 ++++++++++++++-- .../GrokWebBillingFetcherTests.swift | 31 ++++++++++++++++++- docs/grok.md | 14 +++++++-- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b198aa0042..4d31a69d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Codex: avoid repeated full scans after trace-log pruning, while retaining the latest validated cost history through temporary trace-database failures (#3318). Thanks @brzvsk! ### Fixed +- Grok: restore the grok.com billing fetch, which grok.com now rejects when it receives an empty request message (`grpc-status 13`); the client sends the weekly usage period explicitly. SuperGrok Heavy weekly-limit responses that declare a per-period limit without a used amount are now treated as a wire-published 0% instead of unknown usage, so the menu usage bar returns instead of an unavailable-usage diagnostic. - Menu bar: keep status components and website links scoped to their provider when switching cached tabs, preventing Claude status from appearing under Grok or Codex (#3320). Thanks @gianpaj! - Usage & Spend: keep stalled or failed Codex catch-up paused until explicit Refresh, preventing background synchronization from restarting CPU-heavy scans (partial fix for #3316). Thanks @heyajulia! - Xiaomi MiMo: prevent overlapping local usage tracker updates from colliding on a shared temporary cache file, preserving atomic publication (#3321). Thanks @Lucenx9! diff --git a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift index edf29385ca..827e16797b 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift @@ -116,6 +116,17 @@ public enum GrokWebBillingFetcher { URL(string: "https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig")! private static let requestTimeoutSeconds: TimeInterval = 15 + /// `GetGrokCreditsConfigRequest { usage_period_type: WEEKLY }` carried as a + /// grpc-web frame (flags byte, big-endian payload length, payload `08 02`). + /// + /// grok.com's billing API rejects a zero-length request message with + /// `grpc-status 13` ("Missing request message."), so the empty frame that + /// this client previously sent is refused. The web client serializes the + /// active usage period explicitly; proto3 omits zero-valued fields, so + /// UNSPECIFIED would serialize to an empty message the API also rejects. + /// SuperGrok plans are displayed as weekly limits. + private static let weeklyCreditsConfigRequest = Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x02]) + public static func fetch( credentials: GrokCredentials, session transport: any ProviderHTTPTransport = ProviderHTTPClient.shared, @@ -207,8 +218,7 @@ public enum GrokWebBillingFetcher { { var request = URLRequest(url: endpoint) request.httpMethod = "POST" - request.timeoutInterval = Self.requestTimeoutSeconds - request.httpBody = Data([0x00, 0x00, 0x00, 0x00, 0x00]) + request.httpBody = Self.weeklyCreditsConfigRequest if let authorizationHeader { request.setValue(authorizationHeader, forHTTPHeaderField: "Authorization") } @@ -305,15 +315,25 @@ public enum GrokWebBillingFetcher { field.path.starts(with: [1, 6]) || (field.path == [1, 8, 1] && (field.value == 1 || field.value == 2)) } + // A period that declares a per-period bound with an empty used amount is a fully + // determined zero-usage response (Weekly SuperGrok Heavy limit frame): the wire + // publishes the period limit at [1,4,2]/[1,5,2] and carries no used amount at all. + // That zero is real, unlike a period-only frame with no limit, which stays unknown. + let hasDefinedPeriodLimit = scan.varintFields.contains { field in + (field.path == [1, 4, 2] || field.path == [1, 5, 2]) + && field.value > 0 && field.value < 1_000_000_000_000 + } let noUsageYet = parsedPercent == nil && scan.fixed32Fields.isEmpty && reset != nil && hasUsagePeriod + let zeroUsageIsWirePublished = + parsedPercent != nil || (noUsageYet && hasDefinedPeriodLimit) guard let percent = parsedPercent ?? (noUsageYet ? 0 : nil) else { throw GrokWebBillingError.parseFailed } return GrokWebBillingSnapshot( usedPercent: percent, resetsAt: reset, - usedPercentIsWirePublished: parsedPercent != nil) + usedPercentIsWirePublished: zeroUsageIsWirePublished) } static func looksLikeProtobufPayload(_ data: Data) -> Bool { diff --git a/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift b/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift index 1faa196306..e2b81d1762 100644 --- a/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift +++ b/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift @@ -669,6 +669,35 @@ struct GrokWebBillingFetcherTests { } @Test + func `parses live weekly limit billing response as zero percent`() throws { + // Capture from grok.com on 2026-09-01: `GetGrokCreditsConfigRequest { usage_period_type: + // WEEKLY }` → Weekly SuperGrok Heavy Limit, 0% used, reset Sep 7 2026 12:35:57Z. + // The frame declares a per-period limit with no used amount ([1,4,2]/[1,5,2] > 0), so the + // zero usage is a wire-published value, unlike a period-only frame with unknown usage. + let data = Data([ + 0x00, 0x00, 0x00, 0x00, 0x44, 0x0A, 0x42, 0x12, + 0x00, 0x1A, 0x00, 0x22, 0x0B, 0x08, 0xAD, 0xEA, + 0xD5, 0xD4, 0x06, 0x10, 0xD8, 0xF3, 0xEE, 0x1A, + 0x2A, 0x0B, 0x08, 0xAD, 0xDF, 0xFA, 0xD4, 0x06, + 0x10, 0xD8, 0xF3, 0xEE, 0x1A, 0x42, 0x1C, 0x08, + 0x02, 0x12, 0x0B, 0x08, 0xAD, 0xEA, 0xD5, 0xD4, + 0x06, 0x10, 0xD8, 0xF3, 0xEE, 0x1A, 0x1A, 0x0B, + 0x08, 0xAD, 0xDF, 0xFA, 0xD4, 0x06, 0x10, 0xD8, + 0xF3, 0xEE, 0x1A, 0x58, 0x01, 0x62, 0x00, 0x68, + 0x01, 0x80, 0x00, 0x00, 0x00, 0x0F, 0x67, 0x72, + 0x70, 0x63, 0x2D, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x3A, 0x30, 0x0D, 0x0A, + ]) + + let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse( + data, + now: Date(timeIntervalSince1970: 1_788_000_000)) + + #expect(snapshot.usedPercent == 0) + #expect(snapshot.resetsAt == Date(timeIntervalSince1970: 1_788_784_557)) + #expect(snapshot.subscriptionTier == nil) + #expect(snapshot.usedPercentIsWirePublished) + } func `parses omitted zero percent with current billing period`() throws { let data = Data([ 0x00, 0x00, 0x00, 0x00, 0x2A, 0x0A, 0x28, 0x12, @@ -770,7 +799,7 @@ struct GrokWebBillingFetcherTests { endpoint: endpoint) #expect(GrokWebBillingStubURLProtocol.requests.count == 1) - #expect(GrokWebBillingStubURLProtocol.requestBodies == [Data([0x00, 0x00, 0x00, 0x00, 0x00])]) + #expect(GrokWebBillingStubURLProtocol.requestBodies == [Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x02])]) #expect(snapshot.usedPercent == 55.5) #expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(reset))) } diff --git a/docs/grok.md b/docs/grok.md index 5129130ee3..69ee278248 100644 --- a/docs/grok.md +++ b/docs/grok.md @@ -84,8 +84,12 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. that omit `subscription_tier_display` all drop the plan overlay and fall back to the OIDC SuperGrok label. There is no process-lifetime tier cache. 4) **grok.com billing gRPC-web fallback** (best-effort) - - POSTs an empty gRPC-web protobuf request to + - POSTs `GetGrokCreditsConfigRequest { usage_period_type: WEEKLY }` (gRPC-web + binary frame `00 00 00 00 02 08 02`) to `https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig`. + grok.com now rejects a zero-length request message with `grpc-status 13` + ("Missing request message."); proto3 would omit a zero-valued `UNSPECIFIED` + enum, so the weekly period is sent explicitly. - This endpoint now requires the browser-held Web Key Exchange (WKE) keypair. Cookie-only authentication can fail with gRPC status 16 and `no-credentials`; signing in through Chrome alone cannot provide that proof @@ -109,8 +113,12 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. - Parses the returned protobuf enough to recover used percent and reset timestamp, accepting both gRPC-web frames and the raw protobuf form returned by some successful requests. A current billing period with an - omitted proto3 `credit_usage_percent` is treated as zero usage. This keeps - billing visible when `grok agent stdio` returns `Method not found`. + omitted proto3 `credit_usage_percent` is treated as zero usage. When that + period also declares a per-period limit (varint fields `[1,4,2]`/`[1,5,2]` + with no used amount, the Weekly SuperGrok Heavy limit frame), the zero is + wire-published rather than inferred and is eligible for the step-3 adoption + rule. This keeps billing visible when `grok agent stdio` returns + `Method not found`. 5) **Local session signals** (informational fallback) - Walks `~/.grok/sessions///signals.json` files (last 30 days). - Aggregates `totalTokensBeforeCompaction`, `contextTokensUsed`, `modelsUsed`, From 39fe529ae026298fb3bdf34775df8df7651b05d2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 31 Aug 2026 19:22:32 -0700 Subject: [PATCH 2/6] fix(grok): preserve billing semantics in nonempty requests Use the verified exclude_legacy_monthly_usage boolean default instead of treating it as a weekly selector. Keep inferred usage out of proxy enrichment, retain the timeout, and restore parser regression coverage. Live endpoint recovery remains to be confirmed. Co-authored-by: charlielz <1540060247@qq.com> --- CHANGELOG.md | 2 +- .../Grok/GrokWebBillingFetcher.swift | 28 ++------- .../GrokWebBillingFetcherTests.swift | 61 +++++++++++++++++-- docs/grok.md | 27 ++++---- 4 files changed, 78 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d31a69d24..4b36ca3b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,11 @@ - Codex: avoid repeated full scans after trace-log pruning, while retaining the latest validated cost history through temporary trace-database failures (#3318). Thanks @brzvsk! ### Fixed -- Grok: restore the grok.com billing fetch, which grok.com now rejects when it receives an empty request message (`grpc-status 13`); the client sends the weekly usage period explicitly. SuperGrok Heavy weekly-limit responses that declare a per-period limit without a used amount are now treated as a wire-published 0% instead of unknown usage, so the menu usage bar returns instead of an unavailable-usage diagnostic. - Menu bar: keep status components and website links scoped to their provider when switching cached tabs, preventing Claude status from appearing under Grok or Codex (#3320). Thanks @gianpaj! - Usage & Spend: keep stalled or failed Codex catch-up paused until explicit Refresh, preventing background synchronization from restarting CPU-heavy scans (partial fix for #3316). Thanks @heyajulia! - Xiaomi MiMo: prevent overlapping local usage tracker updates from colliding on a shared temporary cache file, preserving atomic publication (#3321). Thanks @Lucenx9! - Claude: avoid duplicated “Resets Reset” labels when CLI usage supplies a singular reset description (#3317). Thanks @Aternus! +- Grok: send a nonempty billing request while preserving legacy monthly usage and leaving unknown percentages unchanged (#3336). Thanks @CharlieLZ! ## 0.56.2 — 2026-08-31 diff --git a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift index 827e16797b..8f75287d9e 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift @@ -116,16 +116,9 @@ public enum GrokWebBillingFetcher { URL(string: "https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig")! private static let requestTimeoutSeconds: TimeInterval = 15 - /// `GetGrokCreditsConfigRequest { usage_period_type: WEEKLY }` carried as a - /// grpc-web frame (flags byte, big-endian payload length, payload `08 02`). - /// - /// grok.com's billing API rejects a zero-length request message with - /// `grpc-status 13` ("Missing request message."), so the empty frame that - /// this client previously sent is refused. The web client serializes the - /// active usage period explicitly; proto3 omits zero-valued fields, so - /// UNSPECIFIED would serialize to an empty message the API also rejects. - /// SuperGrok plans are displayed as weekly limits. - private static let weeklyCreditsConfigRequest = Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x02]) + /// Explicit field 1 = false (exclude_legacy_monthly_usage) keeps the request nonempty + /// without changing the default billing semantics. This field is not a period selector. + private static let creditsConfigRequest = Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00]) public static func fetch( credentials: GrokCredentials, @@ -218,7 +211,8 @@ public enum GrokWebBillingFetcher { { var request = URLRequest(url: endpoint) request.httpMethod = "POST" - request.httpBody = Self.weeklyCreditsConfigRequest + request.timeoutInterval = Self.requestTimeoutSeconds + request.httpBody = Self.creditsConfigRequest if let authorizationHeader { request.setValue(authorizationHeader, forHTTPHeaderField: "Authorization") } @@ -315,25 +309,15 @@ public enum GrokWebBillingFetcher { field.path.starts(with: [1, 6]) || (field.path == [1, 8, 1] && (field.value == 1 || field.value == 2)) } - // A period that declares a per-period bound with an empty used amount is a fully - // determined zero-usage response (Weekly SuperGrok Heavy limit frame): the wire - // publishes the period limit at [1,4,2]/[1,5,2] and carries no used amount at all. - // That zero is real, unlike a period-only frame with no limit, which stays unknown. - let hasDefinedPeriodLimit = scan.varintFields.contains { field in - (field.path == [1, 4, 2] || field.path == [1, 5, 2]) - && field.value > 0 && field.value < 1_000_000_000_000 - } let noUsageYet = parsedPercent == nil && scan.fixed32Fields.isEmpty && reset != nil && hasUsagePeriod - let zeroUsageIsWirePublished = - parsedPercent != nil || (noUsageYet && hasDefinedPeriodLimit) guard let percent = parsedPercent ?? (noUsageYet ? 0 : nil) else { throw GrokWebBillingError.parseFailed } return GrokWebBillingSnapshot( usedPercent: percent, resetsAt: reset, - usedPercentIsWirePublished: zeroUsageIsWirePublished) + usedPercentIsWirePublished: parsedPercent != nil) } static func looksLikeProtobufPayload(_ data: Data) -> Bool { diff --git a/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift b/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift index e2b81d1762..2c7c78b2fe 100644 --- a/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift +++ b/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift @@ -667,13 +667,13 @@ struct GrokWebBillingFetcherTests { // no-usage-yet reading and must never travel as a published percent. #expect(!snapshot.usedPercentIsWirePublished) } +} +extension GrokWebBillingFetcherTests { @Test - func `parses live weekly limit billing response as zero percent`() throws { - // Capture from grok.com on 2026-09-01: `GetGrokCreditsConfigRequest { usage_period_type: - // WEEKLY }` → Weekly SuperGrok Heavy Limit, 0% used, reset Sep 7 2026 12:35:57Z. - // The frame declares a per-period limit with no used amount ([1,4,2]/[1,5,2] > 0), so the - // zero usage is a wire-published value, unlike a period-only frame with unknown usage. + func `fractional billing timestamps do not publish a usage percent`() async throws { + // Captured frame from #3336. The nonzero [1,4,2]/[1,5,2] varints are timestamp + // nanoseconds, not limits or usage; the frame has no credit_usage_percent field. let data = Data([ 0x00, 0x00, 0x00, 0x00, 0x44, 0x0A, 0x42, 0x12, 0x00, 0x1A, 0x00, 0x22, 0x0B, 0x08, 0xAD, 0xEA, @@ -696,8 +696,49 @@ struct GrokWebBillingFetcherTests { #expect(snapshot.usedPercent == 0) #expect(snapshot.resetsAt == Date(timeIntervalSince1970: 1_788_784_557)) #expect(snapshot.subscriptionTier == nil) + #expect(!snapshot.usedPercentIsWirePublished) + + let proxy = GrokWebBillingSnapshot( + usedPercent: nil, + resetsAt: Date(timeIntervalSince1970: 1_788_700_000), + subscriptionTier: "SuperGrok Heavy") + let result = try await GrokOAuthFetchStrategy.resolvingUnknownUsage( + proxy, + credentials: Self.credentials, + grpcBilling: { _ in snapshot }) + + #expect(result.snapshot == proxy) + #expect(result.sourceLabel == "grok-cli-proxy") + let usage = GrokUsageSnapshot( + billing: nil, + webBilling: result.snapshot, + credentials: Self.credentials, + localSummary: nil, + cliVersion: nil, + updatedAt: Date(timeIntervalSince1970: 1_788_000_000)).toUsageSnapshot() + #expect(usage.primary == nil) + #expect(usage.loginMethod(for: .grok) == "SuperGrok Heavy") + } + + @Test(arguments: [Float(0), Float(20)]) + func `parsed wire percentages replace unknown proxy usage`(percent: Float) async throws { + let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse( + Self.grpcFrame(Self.protobufPayload(usedPercent: percent, resetEpoch: 1_800_604_803)), + now: Date(timeIntervalSince1970: 1_799_000_000)) + let proxyReset = Date(timeIntervalSince1970: 1_800_000_003) + let result = try await GrokOAuthFetchStrategy.resolvingUnknownUsage( + GrokWebBillingSnapshot(usedPercent: nil, resetsAt: proxyReset, subscriptionTier: "SuperGrok Heavy"), + credentials: Self.credentials, + grpcBilling: { _ in snapshot }) + #expect(snapshot.usedPercentIsWirePublished) + #expect(result.snapshot.usedPercent == Double(percent)) + #expect(result.snapshot.resetsAt == proxyReset) + #expect(result.snapshot.subscriptionTier == "SuperGrok Heavy") + #expect(result.sourceLabel == "grok-web") } + + @Test func `parses omitted zero percent with current billing period`() throws { let data = Data([ 0x00, 0x00, 0x00, 0x00, 0x2A, 0x0A, 0x28, 0x12, @@ -799,7 +840,7 @@ struct GrokWebBillingFetcherTests { endpoint: endpoint) #expect(GrokWebBillingStubURLProtocol.requests.count == 1) - #expect(GrokWebBillingStubURLProtocol.requestBodies == [Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x02])]) + #expect(GrokWebBillingStubURLProtocol.requestBodies == [Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00])]) #expect(snapshot.usedPercent == 55.5) #expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(reset))) } @@ -845,6 +886,10 @@ struct GrokWebBillingFetcherTests { #expect(attempts.current() == 2) #expect(GrokWebBillingStubURLProtocol.requests.count == 2) + #expect(GrokWebBillingStubURLProtocol.requests.allSatisfy { $0.timeoutInterval == 15 }) + #expect(GrokWebBillingStubURLProtocol.requestBodies == Array( + repeating: Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00]), + count: 2)) #expect(snapshot.usedPercent == 25) #expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(reset))) } @@ -949,6 +994,7 @@ extension GrokWebBillingFetcherTests { #expect(request.value(forHTTPHeaderField: "Cookie") == "sso=session; sso-rw=session") #expect(request.value(forHTTPHeaderField: "Authorization") == nil) #expect(request.value(forHTTPHeaderField: "x-user-agent") == "connect-es/2.1.1") + #expect(request.timeoutInterval == 15) let response = HTTPURLResponse( url: endpoint, statusCode: 200, @@ -964,6 +1010,7 @@ extension GrokWebBillingFetcherTests { endpoint: endpoint) #expect(snapshot.usedPercent == 9) + #expect(GrokWebBillingStubURLProtocol.requestBodies == [Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00])]) } @Test @@ -984,6 +1031,7 @@ extension GrokWebBillingFetcherTests { GrokWebBillingStubURLProtocol.handler = { request in #expect(request.value(forHTTPHeaderField: "Cookie") == "sso=session") #expect(request.value(forHTTPHeaderField: "Authorization") == "Bearer token-123") + #expect(request.timeoutInterval == 15) let response = HTTPURLResponse( url: endpoint, statusCode: 200, @@ -999,6 +1047,7 @@ extension GrokWebBillingFetcherTests { endpoint: endpoint) #expect(snapshot.usedPercent == 9) + #expect(GrokWebBillingStubURLProtocol.requestBodies == [Data([0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00])]) } @Test diff --git a/docs/grok.md b/docs/grok.md index 69ee278248..eedf42af08 100644 --- a/docs/grok.md +++ b/docs/grok.md @@ -84,12 +84,19 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. that omit `subscription_tier_display` all drop the plan overlay and fall back to the OIDC SuperGrok label. There is no process-lifetime tier cache. 4) **grok.com billing gRPC-web fallback** (best-effort) - - POSTs `GetGrokCreditsConfigRequest { usage_period_type: WEEKLY }` (gRPC-web - binary frame `00 00 00 00 02 08 02`) to + - POSTs `GetGrokCreditsConfigRequest { exclude_legacy_monthly_usage: false }` + (gRPC-web binary frame `00 00 00 00 02 08 00`) to `https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig`. - grok.com now rejects a zero-length request message with `grpc-status 13` - ("Missing request message."); proto3 would omit a zero-valued `UNSPECIFIED` - enum, so the weekly period is sent explicitly. + Explicitly encoding the default boolean supplies a nonempty protobuf message + for the reported `grpc-status 13` ("Missing request message.") rejection, + without excluding legacy monthly usage or selecting a billing period. + The current public web-client descriptor defines field 1 as this boolean, + not a weekly/monthly enum, and the web client requests `{}`. False preserves + that implicit proto3 default; whether the nonempty encoding restores the + affected account's response still requires live confirmation. + Protocol references: the public [billing descriptor](https://cdn.grok.com/_next/static/chunks/32g78bk5hhe1q.js), + [web billing query](https://cdn.grok.com/_next/static/chunks/062ueaj23tfo3.js) + (checked 2026-09-01), and [proto3 field presence](https://protobuf.dev/programming-guides/field_presence/). - This endpoint now requires the browser-held Web Key Exchange (WKE) keypair. Cookie-only authentication can fail with gRPC status 16 and `no-credentials`; signing in through Chrome alone cannot provide that proof @@ -113,12 +120,10 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. - Parses the returned protobuf enough to recover used percent and reset timestamp, accepting both gRPC-web frames and the raw protobuf form returned by some successful requests. A current billing period with an - omitted proto3 `credit_usage_percent` is treated as zero usage. When that - period also declares a per-period limit (varint fields `[1,4,2]`/`[1,5,2]` - with no used amount, the Weekly SuperGrok Heavy limit frame), the zero is - wire-published rather than inferred and is eligible for the step-3 adoption - rule. This keeps billing visible when `grok agent stdio` returns - `Method not found`. + omitted proto3 `credit_usage_percent` is treated as zero usage only on this + surface; it is not wire-published and cannot replace an unknown proxy percent. + Nonzero timestamp nanoseconds at `[1,4,2]`/`[1,5,2]` do not change that rule. + This keeps billing visible when `grok agent stdio` returns `Method not found`. 5) **Local session signals** (informational fallback) - Walks `~/.grok/sessions///signals.json` files (last 30 days). - Aggregates `totalTokensBeforeCompaction`, `contextTokensUsed`, `modelsUsed`, From 93e50b90c45dde6f6f0c9a1b0487dc9fa223ba4d Mon Sep 17 00:00:00 2001 From: charlielz <1540060247@qq.com> Date: Tue, 1 Sep 2026 10:38:50 +0800 Subject: [PATCH 3/6] fix(grok): report unified billing zero on-demand usage as wire published 0% A unified-billing account with onDemandCap 0 / onDemandUsed 0 has no credit usage at all; the grok.com Usage page shows 0% used for the weekly period. The proxy omits creditUsagePercent entirely for this shape, so the fetch pipeline reported unknown usage and the Grok menu card showed the unavailable-usage diagnostic. Treat the 0/0 unified-billing payload as a wire-published zero, keeping the legacy period-only unknown-usage semantics for everything else. --- .../Grok/GrokCreditsProxyFetcher.swift | 17 ++++++ .../GrokCreditsProxyFetcherTests.swift | 52 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift b/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift index 25c5204778..aa618a2933 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift @@ -78,6 +78,22 @@ public enum GrokCreditsProxyFetcher { subscriptionTier: subscriptionTier) } + // A unified-billing account with no on-demand credits (cap 0 / used 0) has no credit + // usage to report — the grok.com Usage page shows 0% used for the weekly period. The + // missing `creditUsagePercent` here is the proto3 zero-omission, not unknown usage, so + // this zero is wire-published and can fill the usage bar. + if config.isUnifiedBillingUser == true, + config.onDemandCap?.val == 0, + config.onDemandUsed?.val == 0, + resetsAt != nil + { + return GrokWebBillingSnapshot( + usedPercent: 0, + resetsAt: resetsAt, + subscriptionTier: subscriptionTier, + usedPercentIsWirePublished: true) + } + if resetsAt != nil { return GrokWebBillingSnapshot( usedPercent: nil, @@ -110,6 +126,7 @@ public enum GrokCreditsProxyFetcher { let onDemandCap: CreditsAmount? let onDemandUsed: CreditsAmount? let subscriptionTier: String? + let isUnifiedBillingUser: Bool? } private struct CurrentPeriod: Decodable { diff --git a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift index cdf229e3ad..369602a27e 100644 --- a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift +++ b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift @@ -53,6 +53,58 @@ struct GrokCreditsProxyFetcherTests { #expect(snapshot.subscriptionTier == nil) } + @Test + func `unified billing zero on demand credits reports wire published zero percent`() throws { + let snapshot = try GrokCreditsProxyFetcher.parseSnapshot( + Data( + """ + { + "config": { + "isUnifiedBillingUser": true, + "currentPeriod": { + "type": "USAGE_PERIOD_TYPE_WEEKLY", + "start": "2026-08-31T12:35:57.056343+00:00", + "end": "2026-09-07T12:35:57.056343+00:00" + }, + "onDemandCap": { "val": 0 }, + "onDemandUsed": { "val": 0 }, + "prepaidBalance": { "val": 0 }, + "topUpMethod": "TOP_UP_METHOD_SAVED_PAYMENT_METHOD", + "billingPeriodEnd": "2026-09-07T12:35:57.056343+00:00" + } + } + """.utf8)) + + let expectedReset = try Self.date("2026-09-07T12:35:57Z") + #expect(snapshot.usedPercent == 0) + #expect(snapshot.usedPercentIsWirePublished) + #expect(snapshot.resetsAt == expectedReset) + #expect(snapshot.subscriptionTier == nil) + } + + @Test + func `period only proxy payload without unified flag stays unknown`() throws { + let snapshot = try GrokCreditsProxyFetcher.parseSnapshot( + Data( + """ + { + "config": { + "currentPeriod": { + "type": "USAGE_PERIOD_TYPE_WEEKLY", + "start": "2026-08-31T12:35:57.056343+00:00", + "end": "2026-09-07T12:35:57.056343+00:00" + }, + "onDemandCap": { "val": 0 }, + "onDemandUsed": { "val": 0 }, + "billingPeriodEnd": "2026-09-07T12:35:57.056343+00:00" + } + } + """.utf8)) + + #expect(snapshot.usedPercent == nil) + #expect(!snapshot.usedPercentIsWirePublished) + } + @Test func `derives percent from on demand cap and usage`() throws { let snapshot = try GrokCreditsProxyFetcher.parseSnapshot( From a6ef5fad7f154c0acc96e492ea7aeab15175aebf Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 31 Aug 2026 21:15:09 -0700 Subject: [PATCH 4/6] fix(grok): keep absent subscription usage unknown Zero on-demand credits do not establish included subscription usage. Preserve the existing unknown-usage enrichment path and retain the new proxy fixtures as negative regression coverage. Co-authored-by: charlielz <1540060247@qq.com> --- .../Grok/GrokCreditsProxyFetcher.swift | 17 ----------------- .../GrokCreditsProxyFetcherTests.swift | 7 ++++--- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift b/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift index aa618a2933..25c5204778 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift @@ -78,22 +78,6 @@ public enum GrokCreditsProxyFetcher { subscriptionTier: subscriptionTier) } - // A unified-billing account with no on-demand credits (cap 0 / used 0) has no credit - // usage to report — the grok.com Usage page shows 0% used for the weekly period. The - // missing `creditUsagePercent` here is the proto3 zero-omission, not unknown usage, so - // this zero is wire-published and can fill the usage bar. - if config.isUnifiedBillingUser == true, - config.onDemandCap?.val == 0, - config.onDemandUsed?.val == 0, - resetsAt != nil - { - return GrokWebBillingSnapshot( - usedPercent: 0, - resetsAt: resetsAt, - subscriptionTier: subscriptionTier, - usedPercentIsWirePublished: true) - } - if resetsAt != nil { return GrokWebBillingSnapshot( usedPercent: nil, @@ -126,7 +110,6 @@ public enum GrokCreditsProxyFetcher { let onDemandCap: CreditsAmount? let onDemandUsed: CreditsAmount? let subscriptionTier: String? - let isUnifiedBillingUser: Bool? } private struct CurrentPeriod: Decodable { diff --git a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift index 369602a27e..6834e62d46 100644 --- a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift +++ b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift @@ -54,7 +54,7 @@ struct GrokCreditsProxyFetcherTests { } @Test - func `unified billing zero on demand credits reports wire published zero percent`() throws { + func `unified billing without a published percent keeps subscription usage unknown`() throws { let snapshot = try GrokCreditsProxyFetcher.parseSnapshot( Data( """ @@ -76,8 +76,9 @@ struct GrokCreditsProxyFetcherTests { """.utf8)) let expectedReset = try Self.date("2026-09-07T12:35:57Z") - #expect(snapshot.usedPercent == 0) - #expect(snapshot.usedPercentIsWirePublished) + // Zero on-demand spending does not establish the included subscription usage. + #expect(snapshot.usedPercent == nil) + #expect(!snapshot.usedPercentIsWirePublished) #expect(snapshot.resetsAt == expectedReset) #expect(snapshot.subscriptionTier == nil) } From ced9e52852768d77540325be736bcca668a89086 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 31 Aug 2026 21:18:34 -0700 Subject: [PATCH 5/6] test(grok): preserve fractional reset fixture precision Match the expected reset with the fractional timestamp in the captured proxy payload. --- Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift index 6834e62d46..9dc072476d 100644 --- a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift +++ b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift @@ -75,7 +75,7 @@ struct GrokCreditsProxyFetcherTests { } """.utf8)) - let expectedReset = try Self.date("2026-09-07T12:35:57Z") + let expectedReset = try Self.date("2026-09-07T12:35:57.056343+00:00") // Zero on-demand spending does not establish the included subscription usage. #expect(snapshot.usedPercent == nil) #expect(!snapshot.usedPercentIsWirePublished) From 10cefdc9c4cbf470ce882d27ef3cc2432b450b69 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 31 Aug 2026 21:38:06 -0700 Subject: [PATCH 6/6] test(grok): verify unknown proxy usage permits enrichment Assert the observable fallback contract instead of the provenance flag, which is not meaningful for a missing percentage. --- .../GrokCreditsProxyFetcherTests.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift index 9dc072476d..2be4f35705 100644 --- a/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift +++ b/Tests/CodexBarTests/GrokCreditsProxyFetcherTests.swift @@ -54,7 +54,7 @@ struct GrokCreditsProxyFetcherTests { } @Test - func `unified billing without a published percent keeps subscription usage unknown`() throws { + func `unified billing without a published percent keeps subscription usage unknown`() async throws { let snapshot = try GrokCreditsProxyFetcher.parseSnapshot( Data( """ @@ -78,9 +78,17 @@ struct GrokCreditsProxyFetcherTests { let expectedReset = try Self.date("2026-09-07T12:35:57.056343+00:00") // Zero on-demand spending does not establish the included subscription usage. #expect(snapshot.usedPercent == nil) - #expect(!snapshot.usedPercentIsWirePublished) #expect(snapshot.resetsAt == expectedReset) #expect(snapshot.subscriptionTier == nil) + + let result = try await GrokOAuthFetchStrategy.resolvingUnknownUsage( + snapshot, + credentials: Self.credentials, + grpcBilling: { _ in GrokWebBillingSnapshot(usedPercent: 35, resetsAt: nil) }) + + #expect(result.snapshot.usedPercent == 35) + #expect(result.snapshot.resetsAt == expectedReset) + #expect(result.sourceLabel == "grok-web") } @Test @@ -103,7 +111,6 @@ struct GrokCreditsProxyFetcherTests { """.utf8)) #expect(snapshot.usedPercent == nil) - #expect(!snapshot.usedPercentIsWirePublished) } @Test