Skip to content

Fix Grok CLI billing: use _x.ai/billing ACP extension method - #4595

Merged
steipete merged 6 commits into
steipete:mainfrom
noisy:fix/grok-acp-billing-method
Oct 19, 2026
Merged

steipete merged 6 commits into
steipete:mainfrom
noisy:fix/grok-acp-billing-method

Conversation

@noisy

@noisy noisy commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Summary

codexbar usage --provider grok --source cli fails with Grok request failed: Method not found on current Grok CLI builds. Grok's ACP server exposes billing as the extension method _x.ai/billing (leading underscore, per the ACP extension-method convention); x.ai/billing returns JSON-RPC -32601.

This PR requests _x.ai/billing first and retries the legacy x.ai/billing once, only when the existing GrokStatusProbe.isBillingMethodUnavailable classifier reports -32601, so older CLIs keep working.

Refs #3716 (the 2026-10-09 comment there reports the same -32601 on CodexBar 0.73 / Grok 1.0.50).

Evidence

Driving grok agent stdio directly on Grok CLI 1.0.60 and 1.0.62, after the same initialize:

  • x.ai/billing → {"error":{"code":-32601,"message":"Method not found"}}
  • _x.ai/billing → success (~0.25 s):
    {"config":{"creditUsagePercent":5.0,"currentPeriod":{"type":"USAGE_PERIOD_TYPE_WEEKLY","start":"2026-10-15T17:24:18Z","end":"2026-10-22T17:24:18Z"},"onDemandCap":{"val":0},"onDemandUsed":{"val":0},"prepaidBalance":{"val":0},"isUnifiedBillingUser":true,"billingPeriodStart":"…","billingPeriodEnd":"…"},"subscription_tier":"SuperGrok"}

Live CLI check, same account, usage --provider grok --source cli --format json:

Build Result
CodexBar 0.77.0 (release) error: "Grok request failed: Method not found"
This branch no error, source: grok-cli, loginMethod: SuperGrok

Known gap (not addressed here)

With the call now succeeding, primary is still null: the RPC result is shaped as {config: {creditUsagePercent, currentPeriod, …}, subscription_tier} — the same shape GrokCreditsProxyFetcher already parses for GetGrokCreditsConfig — but GrokBillingResponse (RPC path) doesn't map it. Routing the RPC config through the proxy parsing would surface the weekly percent and reset. Kept out of this PR to keep it minimal; happy to follow up.

Changes

  • GrokRPCClient.fetchBilling(): _x.ai/billing first, legacy fallback on -32601; doc comments updated.
  • GrokFailedBillingWorkTests: new legacyBillingSuccess scenario plus assertions on the ordered billing methods sent (underscore success sends no legacy request; -32601 retries once; non--32601 errors don't retry; existing method-unavailable behavior unchanged).

Commands run

  • swift build --target CodexBarCore ✅
  • swift build --product CodexBarCLI ✅ + the live check above
  • swift test --filter 'GrokFailedBillingWorkTests|…' ⚠️ could not run locally: this machine has Command Line Tools only, and the test target depends on the SwiftUI app (SwiftUIMacros/PreviewsMacros plugins require Xcode). Relying on PR CI.
  • swiftformat/swiftlint not installed locally; git diff --check clean, lines ≤ 120 cols.

🤖 Generated with Claude Code

Grok's ACP server exposes billing as the extension method `_x.ai/billing`;
`x.ai/billing` returns JSON-RPC -32601 on Grok CLI 1.0.60 and 1.0.62.
Request the underscore name first and retry the legacy name once when the
method is unavailable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@clawsweeper

clawsweeper Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

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

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T07:24:57.505414Z 0323e22 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 16ace1a66d

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

let message = try await self.request(method: "x.ai/billing", params: [:])
let message: [String: Any]
do {
message = try await self.request(method: "_x.ai/billing", params: [:])

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 Decode the nested _x.ai/billing result before accepting it

For current Grok CLI builds, this new method returns {config:{creditUsagePercent,currentPeriod,...}, subscription_tier:...} rather than the legacy top-level monthlyLimit/usage schema. decodeResult still decodes that payload as GrokBillingResponse, whose fields are all optional, so it silently creates a non-nil empty billing object; GrokStatusProbe.fetch then considers the CLI strategy successful, Auto never reaches the OAuth/web fallbacks, and toUsageSnapshot() emits no primary usage or reset. Parse/map the nested config (the existing GrokCreditsProxyFetcher already handles this shape) before returning success.

Useful? React with 👍 / 👎.

message = try await self.request(method: "_x.ai/billing", params: [:])
} catch {
guard GrokStatusProbe.isBillingMethodUnavailable(error) else { throw error }
message = try await self.request(method: "x.ai/billing", params: [:])

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 Teach the account fixture to answer the fallback request

When _x.ai/billing returns -32601, this new request is now always sent, but the fixture in Tests/CodexBarTests/GrokAccountContextTests.swift:282-286 emits only response id 2 and immediately exits. The retry therefore fails with closed stdin/EOF instead of the original “Method not found”, making that suite's errorDescription assertion at line 314 fail on every run; have the fixture read id 3 and return a second -32601, as the changed scenario fixture does.

AGENTS.md reference: AGENTS.md:L5-L5

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. labels Oct 16, 2026
@clawsweeper

clawsweeper Bot commented Oct 16, 2026

Copy link
Copy Markdown

Codex review: needs changes before merge.

What this changes

This PR requests Grok billing through _x.ai/billing and retries x.ai/billing only after JSON-RPC error -32601.

Example: Grok CLI 1.0.62 returns 5% weekly usage through _x.ai/billing.

  • Before: Explicit CLI usage fails with Method not found.
  • After: The request succeeds, but the current decoder discards the nested quota and returns primary: null.

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) Live CLI evidence supports the dispatch change, but the introduced quota regression and broken fixture block merge.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (live_output): The contributor reports direct Grok 1.0.60/1.0.62 responses and a newly built usage CLI comparison against 0.77.0, demonstrating successful new-method dispatch and the resulting null primary usage.
Patch quality 🦪 silver shellfish (2/6) 2 actionable review findings remain.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Partial
User problem: Current Grok CLI users receive Method not found instead of billing usage.
Reason: Restoring the supported CLI billing call has clear value, but returning an empty successful snapshot does not restore quota reporting.

Merge readiness

⛔ Needs changes before merge - 2 items remain

Keep open: this PR addresses a real billing-method mismatch, but accepting the new response can suppress working quota fallbacks, and the retry breaks an existing test fixture.

Priority: P2
Reviewed head: 16ace1a66d9525ff16e0261d1a1b0c1757efb762

Before merge

  • Decode the nested billing payload before accepting CLI success (P1) - On the reported Grok 1.0.60/1.0.62 payload, _x.ai/billing returns config.creditUsagePercent and config.currentPeriod. GrokBillingResponse recognizes neither field and silently decodes an empty object because every legacy field is optional. GrokStatusProbe accepts that nonnil object, and Auto stops before OAuth/browser strategies that previously supplied quota. Users with working proxy billing therefore lose their usage bar and reset. Reuse the existing nested credits parser and carry its quota into the snapshot before returning success, while retaining the legacy decoder.
  • Answer both billing requests in the account-context fixture (P2) - The new legacy retry sends request id 3 after the fixture returns -32601 for id 2. The fixture in GrokAccountContextTests exits immediately after id 2, so the first strategy now reports closed stdin or stdout instead of Method not found, failing its assertion at line 314. Extend this fixture to read the retry and return a second -32601, preserving the intended proxy-fallback regression coverage.

Findings

  • [P1] Decode the nested billing payload before accepting CLI success — Sources/CodexBarCore/Providers/Grok/GrokRPCClient.swift:84-89
  • [P2] Answer both billing requests in the account-context fixture — Tests/CodexBarTests/GrokAccountContextTests.swift:284-285

Tests

  • Missing end-to-end proof: No Auto-mode run with the real nested response or executed base-fails/head-passes regression is supplied.
Agent review details

How this fits together

The Grok RPC client exchanges JSON-RPC with a local CLI subprocess, then supplies billing to the usage snapshot and provider fallback pipeline.

flowchart TD
    A[App or usage CLI] --> B[Grok provider pipeline]
    B --> C[Grok RPC client]
    C --> D[Local Grok subprocess]
    D --> E[Billing decoder]
    E --> F[Usage snapshot]
    B --> G[OAuth and browser fallbacks]
Loading

Technical review

Best possible solution:

Decode the nested billing response through shared parsing, preserve legacy and Auto fallbacks, and update the existing subprocess fixture.

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

The contributor supplies affected-release and live CLI results, and source inspection establishes both introduced failure paths; this review executed no target code.

Is this the best way to solve the issue?

The method fallback is appropriately narrow, but dispatch and decoding must change together so a successful RPC preserves usable quota and fallback behavior.

Full review comments:

  • [P1] Decode the nested billing payload before accepting CLI success — Sources/CodexBarCore/Providers/Grok/GrokRPCClient.swift:84-89
    On the reported Grok 1.0.60/1.0.62 payload, _x.ai/billing returns config.creditUsagePercent and config.currentPeriod. GrokBillingResponse recognizes neither field and silently decodes an empty object because every legacy field is optional. GrokStatusProbe accepts that nonnil object, and Auto stops before OAuth/browser strategies that previously supplied quota. Users with working proxy billing therefore lose their usage bar and reset. Reuse the existing nested credits parser and carry its quota into the snapshot before returning success, while retaining the legacy decoder.
    Confidence: 0.99
  • [P2] Answer both billing requests in the account-context fixture — Tests/CodexBarTests/GrokAccountContextTests.swift:284-285
    The new legacy retry sends request id 3 after the fixture returns -32601 for id 2. The fixture in GrokAccountContextTests exits immediately after id 2, so the first strategy now reports closed stdin or stdout instead of Method not found, failing its assertion at line 314. Extend this fixture to read the retry and return a second -32601, preserving the intended proxy-fallback regression coverage.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.98

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against a91669f2ff97.

Provenance checked

  • RPC method, legacy schema, and slash serialization changes intended behavior with a stated reason (0348028: The original integration expected the legacy billing schema and documented unavailable agent billing plus literal slash handling; the PR supplies newer CLI responses explaining the method change.)
  • Missing-method classification and terminal-work tests keeps the original intent (fix(grok): classify missing billing methods by RPC code #3947: Code -32601, rather than English wording, controls the established team fallback; earlier terminal-work coverage avoids discarded history scans.)

Testing

Proof path: shipped entry point.

Security

None.

Evidence

Acceptance criteria:

  • swift test --filter 'GrokFailedBillingWorkTests|GrokAccountContextTests|GrokBillingResponseTests|GrokCreditsProxyFetcherTests'
  • make check

What I checked:

Likely related people:

  • taibaran: Raw commit 0348028 adds Sources/CodexBarCore/Providers/Grok/GrokRPCClient.swift:113 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 0348028dad91; files: Sources/CodexBarCore/Providers/Grok/GrokRPCClient.swift)

Labels

Label changes:

  • add P2: This is a focused single-provider billing repair with a concrete fallback regression that should be corrected before merge.
  • add proof: sufficient: Contributor real behavior proof is sufficient.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🐚 platinum hermit and patch quality is 🦪 silver shellfish.
  • add status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action.

Label justifications:

  • P2: This is a focused single-provider billing repair with a concrete fallback regression that should be corrected before merge.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🐚 platinum hermit and patch quality is 🦪 silver shellfish.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action.
  • proof: sufficient: Contributor real behavior proof is sufficient.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

Reviewed October 16, 2026, 4:42 PM ET / 20:42 UTC.

`_x.ai/billing` returns `{config: {creditUsagePercent, currentPeriod, ...}}`.
Reuse the nested credits parser for it, keep the legacy top-level decoder,
and reject results with no usage percentage so the pipeline can fall back.
Teach the account-context fixture to answer the legacy retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@noisy

noisy commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review — both findings are addressed in the latest commit.

  • P1: CLI billing now reuses GrokCreditsProxyFetcher.parseSnapshot for the nested config result (currentPeriod.end, falling back to billingPeriodEnd). The legacy top-level decoder stays for older CLIs. A result with no usable usage percentage now throws, so Auto falls through to OAuth/web instead of stopping on an empty snapshot.
  • P2: The account-context fixture now reads the legacy retry and answers id 3 with a second -32601, so the "Method not found" assertion holds.

New synthetic scenarios: nested 5% usage with reset 2026-10-22T17:24:18Z, reset-date fallback to billingPeriodEnd, and rejection of an unrecognized result. The existing legacy RPC proof test was updated for the typed result.

Live check (Grok CLI 1.0.62, SuperGrok), usage --provider grok --source cli --format json:

Build Result
CodexBar 0.77.0 Grok request failed: Method not found
previous head 16ace1a no error, primary: null
this head source: grok-cli, primary: {usedPercent: 9, windowMinutes: 10080, resetsAt: 2026-10-22T17:24:18Z}

Commands run: swift build --product CodexBarCLI ✅ and the live check above. swift test still can't run on this machine (Command Line Tools only; the test target needs the SwiftUI macro plugins from Xcode), so the test suites rely on CI. git diff --check is clean.

🤖 Generated with Claude Code

steipete added a commit that referenced this pull request Oct 19, 2026
@steipete
steipete merged commit d82bddb into steipete:main Oct 19, 2026
1 check passed
@steipete

Copy link
Copy Markdown
Owner

Merged in d82bddb, thanks @noisy! Grok CLI billing now works through the _x.ai/billing ACP method. We verified the leading-underscore method against Grok's handler and pinned transport, kept current and legacy payload decoding, added coverage for plan metadata and prepaid-only responses, and removed the unverified unprefixed retry. It will ship in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants