diff --git a/src/gateway/server-methods/models-auth-status-usage-cache.ts b/src/gateway/server-methods/models-auth-status-usage-cache.ts index 3a6847a49a3c..2d8c8eefd897 100644 --- a/src/gateway/server-methods/models-auth-status-usage-cache.ts +++ b/src/gateway/server-methods/models-auth-status-usage-cache.ts @@ -36,7 +36,7 @@ export type ProviderUsageStatus = Pick< type ProviderUsageCacheEntry = { agentDir: string; - configRef: object; + configRef: OpenClawConfig; credentialKey: string; providerKey: string; refreshedAt: number; @@ -46,7 +46,7 @@ type ProviderUsageCacheEntry = { type ProviderUsageRefresh = { agentDir: string; - configRef: object; + configRef: OpenClawConfig; credentialKey: string; providerKey: string; promise: Promise; @@ -158,7 +158,7 @@ function mapProviderUsage(usage: Awaited { @@ -222,7 +223,7 @@ function scheduleProviderUsageRefresh(params: { type ProviderUsageCacheParams = { agentId: string; agentDir: string; - configRef: object; + configRef: OpenClawConfig; credentialKey: string; forceRefresh?: boolean; providerIds: UsageProviderId[]; diff --git a/src/gateway/server-methods/models-auth-status.test.ts b/src/gateway/server-methods/models-auth-status.test.ts index e7a4188b0a04..cec791bb7995 100644 --- a/src/gateway/server-methods/models-auth-status.test.ts +++ b/src/gateway/server-methods/models-auth-status.test.ts @@ -1010,6 +1010,8 @@ describe("models.authStatus", () => { }); it("routes claude-cli OAuth profiles to Anthropic usage with plan and billing", async () => { + const runtimeConfig = {}; + mocks.getRuntimeConfig.mockReturnValue(runtimeConfig); const profile = { profileId: "claude-cli", provider: "claude-cli", @@ -1044,6 +1046,7 @@ describe("models.authStatus", () => { expect(mocks.loadProviderUsageSummary).toHaveBeenCalledWith({ providers: ["anthropic"], agentDir: "/tmp/agent", + config: runtimeConfig, timeoutMs: 3500, }); let result: ModelAuthStatusResult | undefined; @@ -1087,6 +1090,7 @@ describe("models.authStatus", () => { expect(mocks.loadProviderUsageSummary).toHaveBeenCalledWith({ providers: ["deepseek"], agentDir: "/tmp/agent", + config: expect.any(Object), timeoutMs: 3500, }); let result: ModelAuthStatusResult | undefined; @@ -1225,6 +1229,7 @@ describe("models.authStatus", () => { expect(mocks.loadProviderUsageSummary).toHaveBeenLastCalledWith({ providers: ["openai"], agentDir: "/tmp/rebound-agent", + config: expect.any(Object), timeoutMs: 3500, }); }); diff --git a/src/gateway/server-methods/usage.status-cache.test.ts b/src/gateway/server-methods/usage.status-cache.test.ts index 6fb20eb98d79..ff8c1f7c054d 100644 --- a/src/gateway/server-methods/usage.status-cache.test.ts +++ b/src/gateway/server-methods/usage.status-cache.test.ts @@ -114,6 +114,28 @@ describe("usage.status provider usage cache", () => { vi.restoreAllMocks(); }); + it("loads the cached provider snapshot from the exact runtime config", async () => { + mocks.loadProviderUsageSummary.mockImplementation(async (options) => ({ + updatedAt: now, + providers: + options.config === config + ? [ + { + provider: "openai", + displayName: "OpenAI", + windows: [{ label: "5h", usedPercent: 25 }], + accountEmail: "configured@example.com", + }, + ] + : [], + })); + + const result = (await runUsageStatus()) as { + providers: Array<{ accountEmail?: string }>; + }; + expect(result.providers[0]?.accountEmail).toBe("configured@example.com"); + }); + it("reuses byte-identical results within 60s and refreshes stale data in the background", async () => { const first = await runUsageStatus(); const repeated = await runUsageStatus();