From 6505118adb0ae0785f467f4a30e828c06d988813 Mon Sep 17 00:00:00 2001 From: Jolymmiles <75259437+Jolymmiles@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:19:07 +0000 Subject: [PATCH 1/3] fix(server): Claude behind a custom endpoint no longer reports another account's quota --- apps/server/src/provider/ClaudeProvider.ts | 25 +++++++++++-------- .../src/provider/ProviderRegistry.test.ts | 9 ++++++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/apps/server/src/provider/ClaudeProvider.ts b/apps/server/src/provider/ClaudeProvider.ts index 5bc82a41d4eda..b0e90393678c0 100644 --- a/apps/server/src/provider/ClaudeProvider.ts +++ b/apps/server/src/provider/ClaudeProvider.ts @@ -634,17 +634,20 @@ export const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")( apiProviderAuthMetadata(capabilities.apiProvider) ?? (customApiEndpoint ? { type: "customEndpoint", label: "Custom API endpoint" } : undefined); const usageCheckedAt = capabilities.usageCheckedAt ?? checkedAt; - const usageLimits = !capabilities.usage - ? makeUnavailableUsageLimits({ checkedAt, reason: "probeFailed" }) - : scopedLimitNames - ? yield* recordClaudeUsageResponse(scopedLimitNames, { - response: capabilities.usage, - checkedAt: usageCheckedAt, - }) - : claudeUsageResponseToLimits({ - response: capabilities.usage, - checkedAt: usageCheckedAt, - }).limits; + // A custom endpoint answers usage for whichever account it routes to, not this login. + const usageLimits = customApiEndpoint + ? makeUnavailableUsageLimits({ checkedAt, reason: "unsupported" }) + : !capabilities.usage + ? makeUnavailableUsageLimits({ checkedAt, reason: "probeFailed" }) + : scopedLimitNames + ? yield* recordClaudeUsageResponse(scopedLimitNames, { + response: capabilities.usage, + checkedAt: usageCheckedAt, + }) + : claudeUsageResponseToLimits({ + response: capabilities.usage, + checkedAt: usageCheckedAt, + }).limits; const resetCredits = resolveResetCredits && capabilities.subscriptionType && diff --git a/apps/server/src/provider/ProviderRegistry.test.ts b/apps/server/src/provider/ProviderRegistry.test.ts index 1c47a2f31c3fe..98bb0afcbfee1 100644 --- a/apps/server/src/provider/ProviderRegistry.test.ts +++ b/apps/server/src/provider/ProviderRegistry.test.ts @@ -39,7 +39,7 @@ import { createModelCapabilities } from "@t3tools/shared/model"; import { applyServerSettingsPatch } from "@t3tools/shared/serverSettings"; import { checkCodexProviderStatus, type CodexAppServerProviderSnapshot } from "./CodexProvider.ts"; -import { checkClaudeProviderStatus } from "./ClaudeProvider.ts"; +import { checkClaudeProviderStatus, type ClaudeCapabilitiesProbe } from "./ClaudeProvider.ts"; import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; import * as AntigravityInstallation from "./AntigravityInstallation.ts"; import * as ModelManifest from "./ModelManifest.ts"; @@ -166,6 +166,7 @@ type TestClaudeCapabilities = { readonly apiKeySource: string | undefined; readonly apiProvider: string | undefined; readonly slashCommands: ReadonlyArray; + readonly usage?: NonNullable; }; function claudeCapabilities(overrides: Partial = {}) { @@ -2312,6 +2313,10 @@ it.layer( tokenSource: "none", apiKeySource: "none", apiProvider: "firstParty", + usage: { + rate_limits_available: true, + rate_limits: { five_hour: { utilization: 1, resets_at: null } }, + }, }), environment, ); @@ -2319,6 +2324,8 @@ it.layer( assert.strictEqual(status.auth.status, "authenticated"); assert.strictEqual(status.auth.type, "customEndpoint"); assert.strictEqual(status.auth.label, "Custom API endpoint"); + assert.strictEqual(status.usageLimits?.unavailable?.reason, "unsupported"); + assert.deepStrictEqual(status.usageLimits?.windows, []); }).pipe( Effect.provide( layerMockSpawner((args) => { From 2b24759f6f2a2ba8a0efe092f9c7eedc8e74c9ea Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Wed, 4 Nov 2026 04:51:47 +0000 Subject: [PATCH 2/3] fix(server): keep failed custom endpoint quota probes unsupported --- apps/server/src/provider/ClaudeProvider.ts | 24 ++++++++++------- .../src/provider/ProviderRegistry.test.ts | 26 +++++++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/apps/server/src/provider/ClaudeProvider.ts b/apps/server/src/provider/ClaudeProvider.ts index f38a9db8535bb..1d33fd50471a8 100644 --- a/apps/server/src/provider/ClaudeProvider.ts +++ b/apps/server/src/provider/ClaudeProvider.ts @@ -609,6 +609,17 @@ export const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")( const slashCommands = [COMPACT_SLASH_COMMAND, ...(capabilities?.slashCommands ?? [])]; const dedupedSlashCommands = dedupeSlashCommands(slashCommands); + // First-party account fields cannot verify custom endpoint credentials. + const baseUrl = + (yield* readClaudeSettingsEnv(claudeSettings, cwd, resolvedEnvironment)).ANTHROPIC_BASE_URL ?? + resolvedEnvironment.ANTHROPIC_BASE_URL; + const endpoint = URL.parse(baseUrl?.trim() ?? ""); + const customApiEndpoint = + endpoint !== null && + (endpoint.protocol === "http:" || endpoint.protocol === "https:") && + endpoint.hostname !== "" && + endpoint.hostname !== "api.anthropic.com"; + if (!capabilities) { return buildServerProvider({ presentation: CLAUDE_PRESENTATION, @@ -628,20 +639,13 @@ export const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")( ? `Timed out after ${CAPABILITIES_PROBE_TIMEOUT_MS / 1_000}s while checking Claude account status.` : "Claude Agent CLI failed while checking account status." : "Could not verify Claude authentication status from initialization result.", + ...(customApiEndpoint + ? { usageLimits: makeUnavailableUsageLimits({ checkedAt, reason: "unsupported" }) } + : {}), }, }); } - // First-party account fields cannot verify custom endpoint credentials. - const baseUrl = - (yield* readClaudeSettingsEnv(claudeSettings, cwd, resolvedEnvironment)).ANTHROPIC_BASE_URL ?? - resolvedEnvironment.ANTHROPIC_BASE_URL; - const endpoint = URL.parse(baseUrl?.trim() ?? ""); - const customApiEndpoint = - endpoint !== null && - (endpoint.protocol === "http:" || endpoint.protocol === "https:") && - endpoint.hostname !== "" && - endpoint.hostname !== "api.anthropic.com"; if (!customApiEndpoint && claudeAuthStatus(capabilities) === "unauthenticated") { return buildServerProvider({ presentation: CLAUDE_PRESENTATION, diff --git a/apps/server/src/provider/ProviderRegistry.test.ts b/apps/server/src/provider/ProviderRegistry.test.ts index f78b12c97fe13..62b2bd807febd 100644 --- a/apps/server/src/provider/ProviderRegistry.test.ts +++ b/apps/server/src/provider/ProviderRegistry.test.ts @@ -3168,6 +3168,32 @@ it.layer( ), ); + it.effect.each([ + { baseUrl: "https://proxy.example/api", reason: "unsupported" }, + { baseUrl: "https://api.anthropic.com", reason: undefined }, + ])( + "preserves quota classification when the Claude probe fails: $baseUrl", + ({ baseUrl, reason }) => + Effect.gen(function* () { + const status = yield* checkClaudeProviderStatus( + defaultClaudeSettings, + () => Effect.fail(new Error("probe failed")), + { ANTHROPIC_BASE_URL: baseUrl }, + ); + assert.strictEqual(status.status, "warning"); + assert.strictEqual(status.auth.status, "unknown"); + assert.strictEqual(status.usageLimits?.unavailable?.reason, reason); + if (reason) assert.deepStrictEqual(status.usageLimits?.windows, []); + }).pipe( + Effect.provide( + layerMockSpawner((args) => { + if (args.join(" ") === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; + throw new Error("Unexpected command"); + }), + ), + ), + ); + it.effect.each([ { source: "inherited HTTPS endpoint", baseUrl: "https://proxy.example/api", overrides: [] }, { From a7b7ec2fdb9e8e4fd115671295aeef86b635594c Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Wed, 4 Nov 2026 04:56:29 +0000 Subject: [PATCH 3/3] test(server): use the claude probe error type --- apps/server/src/provider/ProviderRegistry.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/ProviderRegistry.test.ts b/apps/server/src/provider/ProviderRegistry.test.ts index 62b2bd807febd..34b84a92f8e5d 100644 --- a/apps/server/src/provider/ProviderRegistry.test.ts +++ b/apps/server/src/provider/ProviderRegistry.test.ts @@ -8,6 +8,7 @@ import * as NodeCrypto from "@effect/platform-node/NodeCrypto"; import * as NodeServices from "@effect/platform-node/NodeServices"; import type { ModelInfo } from "@anthropic-ai/claude-agent-sdk"; import { describe, it, assert, expect } from "@effect/vitest"; +import * as Cause from "effect/Cause"; import * as DateTime from "effect/DateTime"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; @@ -3177,7 +3178,7 @@ it.layer( Effect.gen(function* () { const status = yield* checkClaudeProviderStatus( defaultClaudeSettings, - () => Effect.fail(new Error("probe failed")), + () => Effect.fail(new Cause.UnknownError("probe failed")), { ANTHROPIC_BASE_URL: baseUrl }, ); assert.strictEqual(status.status, "warning");