Skip to content

Add BigModel CN account balance to z.ai provider - #3109

Merged
steipete merged 2 commits into
steipete:mainfrom
RunhuaHuang:zai-bigmodel-account-balance
Aug 20, 2026
Merged

Add BigModel CN account balance to z.ai provider#3109
steipete merged 2 commits into
steipete:mainfrom
RunhuaHuang:zai-bigmodel-account-balance

Conversation

@RunhuaHuang

Copy link
Copy Markdown
Contributor

Summary

Adds the BigModel CN (open.bigmodel.cn) pay-as-you-go account balance to the z.ai provider, rendered as a best-effort "Account balance" row inside "Quota details".

  • The bundled zai plugin now calls GET https://www.bigmodel.cn/api/biz/account/query-customer-account-report for bigmodel-cn region accounts: ¥ available balance (availableBalance, falling back to balance), with a secondary line summarizing recharged / granted / spent.
  • Endpoint verified against the live console API (2026-08): it accepts both Bearer <key> and raw-key Authorization, so the plugin's existing bearer broker works unchanged.
  • z.ai global has no documented equivalent — the global region skips the row and the extra request entirely.
  • Balance lookup failures never break quota display (best-effort, same pattern as the existing model-usage detail fetches).
  • New Z_AI_BALANCE_URL environment override (HTTPS-validated via ProviderEndpointOverrideValidator) plus ZaiEndpointRouter.resolveBalanceURL, injected to the plugin as Z_AI_BALANCE_ENDPOINT.

Background: this is the console's internal account-report API (no official docs). The same endpoint powers the "智谱 API 余额" channel in the open-source QuotaX quota dashboard, where the response shape below was captured live:

{
  "code": 200, "msg": "操作成功", "success": true,
  "data": {
    "balance": 42.5, "availableBalance": 40.0, "rechargeAmount": 100.0,
    "giveAmount": 20.0, "totalSpendAmount": 77.5, "frozenBalance": 2.5,
    "creditStatus": "NOT_OPEN", "isKA": false
  }
}

Testing

Commands run:

swift build --build-tests
swift test --filter ZaiPluginBalanceTests        # new suite, 4 passed
swift test --filter Zai                          # 29 passed (6 suites)
swift test --filter "ProviderPluginRuntimeTests|ProviderPluginParityTests|ProviderPluginExtensionParityTests|UserProviderPluginPortableTests|ProviderPluginDetailsParityTests"   # 72 passed (5 suites)
swiftlint --strict <changed paths>               # 0 violations
swiftformat <changed files>

New TestsPlugin/ZaiPluginBalanceTests.swift covers:

  • CN snapshot renders the balance row (value + secondary provenance line) and issues the request to the right host/path
  • Balance endpoint failure (HTTP 500) keeps the quota snapshot intact with no balance row
  • Global region issues no balance request at all
  • Router default / explicit override / nil-for-global resolution

No UI change beyond an additional detail row, so no screenshots.

@clawsweeper

clawsweeper Bot commented Aug 20, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1588c18be

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const balanceEndpoint =
ctx.settings.get("Z_AI_BALANCE_ENDPOINT") ||
"https://www.bigmodel.cn/api/biz/account/query-customer-account-report";
const response = await ctx.http.getJSON(balanceEndpoint, {});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound the best-effort balance request timeout

When the new console endpoint stalls, this call inherits the 15-second request timeout while the entire plugin fetch has a 20-second deadline; because it runs after the required quota request and before two model-usage requests, it can exhaust the global deadline and discard the otherwise valid quota snapshot despite the surrounding try/catch. Pass a short explicit timeoutSeconds for this optional request so an unavailable balance service cannot take down BigModel CN usage.

Useful? React with 👍 / 👎.

Comment on lines +80 to +83
if let raw = self.cleaned(environment[balanceURLKey]),
self.balanceURL(environment: environment) == nil
{
throw ZaiSettingsError.invalidEndpointOverride(self.balanceURLKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate balance overrides on the region-aware path

When CodexBar performs a provider fetch, ZaiProviderDescriptor calls validateEndpointOverrides(region:environment:), but the new balance check exists only in the overload without a region. Consequently, a malformed or non-HTTPS Z_AI_BALANCE_URL is silently ignored and a CN fetch falls back to the production balance endpoint instead of rejecting the invalid override as it does for the other endpoint settings; move or share this check with the region-aware overload.

Useful? React with 👍 / 👎.

Comment on lines +220 to +222
const available = Number(data.availableBalance);
const current = Number(data.balance);
const value = Number.isFinite(available) ? available : current;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the balance fallback for null values

When the API returns availableBalance: null alongside a valid balance, Number(null) becomes 0, which is finite, so the advertised fallback is skipped and the UI incorrectly reports ¥0.00; nullable secondary amount fields similarly become misleading zero-valued entries. Check for null/undefined before numeric conversion so only actual numeric values participate in the fallback and detail rows.

Useful? React with 👍 / 👎.

@RunhuaHuang

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all three findings addressed in 72d762a:

  • P1 (timeout): the balance request now passes an explicit timeoutSeconds: 5, well below the fetch deadline, so a stalling balance service can no longer delay the model-usage requests or discard the fetched quota snapshot. Covered by a new assertion on the recorded request's timeoutInterval.
  • P2 (region-aware validation): extracted validateBalanceEndpointOverride and wired it into both validateEndpointOverrides overloads, so the region-aware path used by the provider fetch pipeline now rejects malformed / non-HTTPS Z_AI_BALANCE_URL like the other endpoint overrides. New test exercises both overloads with an http:// override.
  • P2 (null amounts): numeric conversion now goes through a null/undefined guard, so availableBalance: null correctly falls back to balance (previously rendered ¥0.00 because Number(null) is 0), and null secondary fields no longer produce misleading zero-valued rows. New fixture test covers both.

Verified: swift test --filter Zai (31 passed), ZaiPluginBalanceTests (6 passed), plugin infra suites (72 passed), swiftlint --strict 0 violations, swiftformat applied.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 20, 2026
@clawsweeper

clawsweeper Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed August 20, 2026, 9:16 AM ET / 13:16 UTC.

ClawSweeper review

What this changes

This PR adds a BigModel CN account-balance row to z.ai quota details, with a configurable HTTPS balance endpoint and fixture coverage.

Merge readiness

Blocked until real behavior proof from a real setup is added - 7 items remain

Keep open. The prior timeout, validation, and null-handling findings are addressed, but the new sequential balance request can still consume the shared 20-second plugin deadline and lose an otherwise valid quota snapshot; the undocumented bearer-authenticated console route also needs maintainer approval and real after-fix proof.

Priority: P1
Reviewed head: 72d762ac24d1d3c1ff20d2609a2fc0958142f15c
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) The patch is focused and covers fixtures, but a deadline regression, undocumented credential boundary, and lack of real behavior proof keep it below merge-ready quality.
Proof confidence 🦪 silver shellfish (2/6) Needs real behavior proof before merge: Fixture transport tests are useful but no redacted live request/response, terminal output, or runtime log demonstrates the new console endpoint after the change. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦐 gold shrimp (3/6) Security review found an item that needs attention.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: Fixture transport tests are useful but no redacted live request/response, terminal output, or runtime log demonstrates the new console endpoint after the change. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 5 items Current main lacks this balance capability: The current-main plugin declares only api.z.ai and open.bigmodel.cn endpoints and has no balance request; this PR adds the www.bigmodel.cn console route and balance row.
Shared deadline can still discard the snapshot: The runtime has a 20-second whole-fetch deadline, while the newly inserted awaited balance request runs after quota parsing and before the existing optional model-usage requests; its five-second request timeout is clipped by, but can consume, the remaining whole-fetch budget.
New bearer-authenticated console origin: The PR adds www.bigmodel.cn as an allowed fixed endpoint; the provider engine attaches the manifest bearer secret to allowed requests. The PR description identifies the account-report API as an internal endpoint without official documentation.
Findings 1 actionable finding [P1] Keep the balance lookup inside the remaining fetch budget
Security Needs attention Require approval for the undocumented console bearer route: The manifest newly permits www.bigmodel.cn and the shared HTTP broker attaches Z_AI_API_KEY as bearer authorization to allowed requests; the PR describes this endpoint as an internal API with no official documentation.

How this fits together

CodexBar resolves the selected z.ai region and credentials into a bundled JavaScript provider probe. The probe fetches quota data and optional details, then returns a usage snapshot used by the menu-bar quota display.

flowchart LR
A[Region and API token] --> B[Endpoint resolver]
B --> C[z.ai provider probe]
C --> D[Required quota request]
C --> E[Optional balance request]
D --> F[Usage snapshot]
E --> F
F --> G[Menu bar quota details]
Loading

Decision needed

Question Recommendation
Should CodexBar automatically send BigModel CN API tokens to the undocumented www.bigmodel.cn console endpoint to display account balances? Keep the lookup opt-in: Require an explicit user configuration before contacting the undocumented console endpoint, preserving existing BigModel CN network behavior by default.

Why: The repository can prove the new bearer route, but cannot establish the third-party endpoint’s supported credential contract or whether default collection of financial balance data is acceptable.

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: Fixture transport tests are useful but no redacted live request/response, terminal output, or runtime log demonstrates the new console endpoint after the change. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Keep the balance lookup inside the remaining fetch budget (P1) - The runtime aborts the entire provider fetch after 20 seconds, while this awaited request runs after the required quota request and before both model-usage requests. A five-second per-request timeout can still exhaust the remaining budget, so the outer timeout can discard the otherwise valid quota snapshot despite this catch; add a delayed-transport regression and isolate optional enrichment from the mandatory quota result.
  • Resolve security concern: Require approval for the undocumented console bearer route - The manifest newly permits www.bigmodel.cn and the shared HTTP broker attaches Z_AI_API_KEY as bearer authorization to allowed requests; the PR describes this endpoint as an internal API with no official documentation.
  • Resolve merge risk (P1) - A slow balance service can consume the remaining 20-second provider deadline and make BigModel CN quota refresh fail despite the local catch.
  • Resolve merge risk (P1) - Merging would send an existing z.ai API token to an undocumented BigModel console endpoint by default, expanding the credential’s network boundary without an authoritative API contract.
  • Complete next step (P2) - A maintainer must decide the new bearer-token boundary and require real proof; the remaining deadline defect should be resolved before merge.

Findings

  • [P1] Keep the balance lookup inside the remaining fetch budget — Sources/CodexBarCore/Resources/Plugins/zai.js:219
  • [medium] Require approval for the undocumented console bearer route — Sources/CodexBarCore/Resources/Plugins/zai.js:7
Agent review details

Security

Needs attention: The patch expands the bearer-token network boundary to an undocumented BigModel console endpoint without inspectable provider-contract evidence.

Review metrics

Metric Value Why it matters
Production versus test growth production +97, tests +169 The provider feature has substantial fixture coverage, but its live endpoint and whole-fetch deadline behavior still need proof.

Merge-risk options

Maintainer options:

  1. Protect the quota snapshot under balance stalls (recommended)
    Adjust the optional-enrichment flow and add a delayed-transport regression proving the quota snapshot still returns when the balance request reaches its timeout.
  2. Approve the new credential boundary explicitly
    If retaining the default console query, record maintainer approval of sending the bearer token to that undocumented endpoint and document the behavior.
  3. Pause the balance addition
    Do not merge the feature if the console endpoint cannot be accepted as a stable credential-bearing provider route.

Technical review

Best possible solution:

Preserve quota refresh under a stalled balance service, and make the undocumented console lookup opt-in or merge it only after explicit approval of its bearer-token contract and a documented user-facing setup path.

Do we have a high-confidence way to reproduce the issue?

No high-confidence real-account reproduction is attached. Source inspection establishes a deterministic delayed-transport scenario in which the new five-second request can consume the remaining 20-second fetch budget.

Is this the best way to solve the issue?

No. Reducing the request timeout to five seconds does not guarantee that an already-built quota snapshot returns before the shared deadline, and defaulting to an undocumented bearer-authenticated endpoint requires a maintainer choice.

Full review comments:

  • [P1] Keep the balance lookup inside the remaining fetch budget — Sources/CodexBarCore/Resources/Plugins/zai.js:219
    The runtime aborts the entire provider fetch after 20 seconds, while this awaited request runs after the required quota request and before both model-usage requests. A five-second per-request timeout can still exhaust the remaining budget, so the outer timeout can discard the otherwise valid quota snapshot despite this catch; add a delayed-transport regression and isolate optional enrichment from the mandatory quota result.
    Confidence: 0.91

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 2cb9efda1d1a.

Labels

Label changes:

  • add P1: The added awaited request can make an existing BigModel CN quota refresh fail when it exhausts the shared provider deadline.
  • add merge-risk: 🚨 security-boundary: The patch adds a fixed undocumented console origin that receives the provider bearer credential.
  • add merge-risk: 🚨 availability: A new sequential request is added inside a 20-second end-to-end provider fetch budget.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Fixture transport tests are useful but no redacted live request/response, terminal output, or runtime log demonstrates the new console endpoint after the change. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P1: The added awaited request can make an existing BigModel CN quota refresh fail when it exhausts the shared provider deadline.
  • merge-risk: 🚨 availability: A new sequential request is added inside a 20-second end-to-end provider fetch budget.
  • merge-risk: 🚨 security-boundary: The patch adds a fixed undocumented console origin that receives the provider bearer credential.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Fixture transport tests are useful but no redacted live request/response, terminal output, or runtime log demonstrates the new console endpoint after the change. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

Security concerns:

  • [medium] Require approval for the undocumented console bearer route — Sources/CodexBarCore/Resources/Plugins/zai.js:7
    The manifest newly permits www.bigmodel.cn and the shared HTTP broker attaches Z_AI_API_KEY as bearer authorization to allowed requests; the PR describes this endpoint as an internal API with no official documentation.
    Confidence: 0.93

What I checked:

Likely related people:

  • Peter Steinberger: Introduced the China/GLM regional routing, credential routing, and z.ai bundled-plugin conversion that this change extends. (role: regional provider and plugin architecture owner; confidence: high; commits: 7002b5782053, 898df4c11ec7, 8fc67d7f04ad; files: Sources/CodexBarCore/Providers/Zai/ZaiAPIRegion.swift, Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift, Sources/CodexBarCore/Resources/Plugins/zai.js)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Add a delayed-transport regression proving a balance timeout cannot suppress the quota snapshot.
  • Provide redacted live after-fix output showing the BigModel CN balance row and request result.
  • Obtain maintainer direction on the undocumented bearer-authenticated console endpoint.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete
steipete merged commit cdc456a into steipete:main Aug 20, 2026
1 check passed
steipete added a commit that referenced this pull request Aug 20, 2026
steipete added a commit that referenced this pull request Aug 20, 2026
* feat(kiro): show overage credits against their cap

kiro-cli /usage states plan credits alone and omits the overage section
for organization accounts, so a spent plan looks like the account is out.
Read GetUsageLimits with the CLI's own token (read-only) and surface
overage as a second credit window plus charges against the overage budget.

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

* fix(kiro): reject impossible usage counters from GetUsageLimits

An overage larger than total usage would clamp planUsed to zero and
overwrite valid CLI numbers. Honor API-disabled overage over a stale
CLI Enabled line so enrichment cannot resurrect a cap the API says is off.

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

* fix(kiro): reject GetUsageLimits payloads that exceed the plan

Drop enrichment when plan usage is above the plan ceiling, and treat an unrecognized overage status as unknown so the CLI overage line can still stand.

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

* fix: import FoundationNetworking for Linux URLSession types

* fix(kiro): resolve the CLI state database on Linux

GetUsageLimits enrichment always looked under macOS Application Support,
so Linux refreshes never found data.sqlite3.

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

* fix(kiro): reject overage above cap and honor API currency

Best-effort GetUsageLimits data should not present a spend window
above its ceiling or format non-USD charges as dollars.

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

* fix(kiro): propagate cancellation from usage-limits enrichment

A cancelled GetUsageLimits call now fails the refresh instead of publishing a CLI-only snapshot as success.

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

* fix(kiro): drop CLI USD overage estimate when the API currency is not USD

A missing overageCharges field no longer lets a dollar CLI fallback render in the API's non-USD currency.

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

* fix(kiro): add locale entries for overage copy

Translate the new Overage window title and Overage credits left detail row in every app catalog so non-English UIs no longer fall back to English.

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

* fix(kiro): honor API-disabled overage and localize cap phrases

GetUsageLimits DISABLED now replaces a stale CLI Enabled status, and Kiro "of N" detail values go through the of %@ localization key.

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

* fix(i18n): expect localized OpenRouter API key details in zh-Hant

Provider detail titles and catalogued labels go through L after #3084, so the popup test must assert the Traditional Chinese strings.

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

* fix(kiro): clamp overage that runs slightly past the cap

Rejecting over-cap counters dropped the whole GetUsageLimits payload and hid overage on organization accounts, so the gauge now clamps to the cap instead.

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

* fix(kiro): keep over-cap overage and localize credit units

Clamping hid the amount Kiro actually billed, and overage usage still rendered the English "credits" suffix in other locales.

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

* fix(kiro): keep CLI overage when API omits the cap

ENABLED without overageCapWithPrecision is incomplete enrichment, not a disabled account.

* fix(kiro): mark API-enabled overage when the CLI omits it

Organization accounts skip the CLI overage section, so the menu-bar modes never saw an Enabled row.

* fix(kiro): keep CLI plan usage when API bonuses are present

GetUsageLimits folds bonus spend into currentUsage, so overwriting the plan gauge would double-count bonus credits.

* fix(kiro): parse bonus-inclusive usage above the plan limit

GetUsageLimits folds bonus spend into currentUsage, so rejecting planUsed > planLimit dropped overage enrichment for those accounts.

* fix: preserve stacked menu bar layout line breaks (#3094)

* Preserve card menu-item subclass during cached swaps (#3093)

* 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>

* docs: changelog for #3093

* Extend menu bar conditionals beyond usage percentages (#3088)

* 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>

* Prevent RPC pipe writes from aborting after child exit (#3095)

* 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 (#3085)

* 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>

* docs: credit #3085 and #3088 changelog entries

* chore: finalize 0.54.0 changelog and bump build to 127

* docs: update appcast for 0.54.0

* chore: open 0.54.1 unreleased changelog section

* Clarify five-hour quota wording in Simplified Chinese (#3070)

* Clarify Simplified Chinese five-hour quota label

* Derive Simplified Chinese session quota labels from duration

---------

Co-authored-by: UNGETSU <ungetsu@UNGETSUdeMacBook-Air.local>

* Fix agent session menu width (#3096)

* fix(alibaba): resolve mainland Personal/Solo sec_token from the console shell (#3098)

Mainland Personal/Solo Token Plan (cn-personal) fails with a 200
`BailianGateway.Login.NotLogined` body ("Alibaba Token Plan login required")
even with fresh, valid aliyun cookies, because the request lacks the
`sec_token` the OneConsole gateway requires. #2533 already forwards the token
when present, but it was never resolvable for this path for two reasons:

- The console shell only server-renders `window.ALIYUN_CONSOLE_CONFIG.SEC_TOKEN`
  for a genuine same-origin document navigation; a bare GET receives a
  token-less shell. Send the browser-navigation headers (Referer, Sec-Fetch-*,
  Accept-Language) so the shell includes the token.
- The shell embeds it as an upper-case, unquoted key (`SEC_TOKEN: "..."`), but
  `extractSECToken` only matched the lower-case `secToken`/`sec_token` shapes.
  Add the `SEC_TOKEN` pattern.

With both, the scraper resolves the Personal `sec_token`, the gateway returns
real usage, and the mainland Personal/Solo card renders. Verified end-to-end
on a real cn-personal account: `secTokenSource=resolved`, body `message=Success`,
5-hour/weekly windows populated (was "login required").

Adds AlibabaTokenPlanSECTokenScrapeTests covering the upper-case shell format,
the existing lower-case shapes, and the no-token case.

Refs #2500, #2349, #2370, #2533.

Co-authored-by: LeoLin <leolin990405@gmail.com>

* Add BigModel CN account balance to z.ai provider (#3109)

* Add BigModel CN account balance to z.ai provider

* Bound balance timeout, region-aware override validation, null-safe amounts

---------

Co-authored-by: RunhuaHuang <RunhuaHuang@users.noreply.github.com>

* docs: credit #3070 #3096 #3098 #3109 changelog entries

* test: repair gatekeeper anchors after #3096 menu refactor

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: kiranmagic7 <kiranmagic@proton.me>
Co-authored-by: Kiran Magic <262980978+kiranmagic7@users.noreply.github.com>
Co-authored-by: William Mitchell <13799071+wdmitchelluk@users.noreply.github.com>
Co-authored-by: Alec Gutman, Chip <44984861+Chipagosfinest@users.noreply.github.com>
Co-authored-by: UNGETSU <44204222+YunyueLi@users.noreply.github.com>
Co-authored-by: UNGETSU <ungetsu@UNGETSUdeMacBook-Air.local>
Co-authored-by: KaranocaVe <39995482+KaranocaVe@users.noreply.github.com>
Co-authored-by: Zhongyue Lin <101193087+LeoLin990405@users.noreply.github.com>
Co-authored-by: LeoLin <leolin990405@gmail.com>
Co-authored-by: Xigua <rh2897@columbia.edu>
Co-authored-by: RunhuaHuang <RunhuaHuang@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants