diff --git a/src/agents/provider-attribution.test.ts b/src/agents/provider-attribution.test.ts index df7956800f8..1faf2964941 100644 --- a/src/agents/provider-attribution.test.ts +++ b/src/agents/provider-attribution.test.ts @@ -1,5 +1,14 @@ import { describe, expect, it, vi } from "vitest"; +function expectRecordFields(record: unknown, expected: Record) { + expect(record).toBeDefined(); + const actual = record as Record; + for (const [key, value] of Object.entries(expected)) { + expect(actual[key]).toEqual(value); + } + return actual; +} + const providerEndpointPlugins = vi.hoisted(() => [ { providerEndpoints: [ @@ -198,7 +207,7 @@ describe("provider attribution", () => { }); it("authorizes hidden OpenAI attribution only on verified native hosts", () => { - expect( + expectRecordFields( resolveProviderRequestPolicy( { provider: "openai", @@ -209,16 +218,17 @@ describe("provider attribution", () => { }, { OPENCLAW_VERSION: "2026.3.22" }, ), - ).toMatchObject({ - endpointClass: "openai-public", - attributionProvider: "openai", - allowsHiddenAttribution: true, - usesKnownNativeOpenAIEndpoint: true, - usesVerifiedOpenAIAttributionHost: true, - usesExplicitProxyLikeEndpoint: false, - }); + { + endpointClass: "openai-public", + attributionProvider: "openai", + allowsHiddenAttribution: true, + usesKnownNativeOpenAIEndpoint: true, + usesVerifiedOpenAIAttributionHost: true, + usesExplicitProxyLikeEndpoint: false, + }, + ); - expect( + expectRecordFields( resolveProviderRequestPolicy( { provider: "openai", @@ -229,32 +239,34 @@ describe("provider attribution", () => { }, { OPENCLAW_VERSION: "2026.3.22" }, ), - ).toMatchObject({ - endpointClass: "custom", - attributionProvider: undefined, - allowsHiddenAttribution: false, - usesKnownNativeOpenAIEndpoint: false, - usesVerifiedOpenAIAttributionHost: false, - usesExplicitProxyLikeEndpoint: true, - }); + { + endpointClass: "custom", + attributionProvider: undefined, + allowsHiddenAttribution: false, + usesKnownNativeOpenAIEndpoint: false, + usesVerifiedOpenAIAttributionHost: false, + usesExplicitProxyLikeEndpoint: true, + }, + ); }); it("classifies OpenAI-family default, codex, and Azure routes distinctly", () => { - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openai", api: "openai-responses", transport: "stream", capability: "llm", }), - ).toMatchObject({ - endpointClass: "default", - attributionProvider: undefined, - usesKnownNativeOpenAIRoute: true, - usesExplicitProxyLikeEndpoint: false, - }); + { + endpointClass: "default", + attributionProvider: undefined, + usesKnownNativeOpenAIRoute: true, + usesExplicitProxyLikeEndpoint: false, + }, + ); - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openai-codex", api: "openai-responses", @@ -262,13 +274,14 @@ describe("provider attribution", () => { transport: "stream", capability: "llm", }), - ).toMatchObject({ - endpointClass: "openai-codex", - attributionProvider: "openai-codex", - allowsHiddenAttribution: true, - }); + { + endpointClass: "openai-codex", + attributionProvider: "openai-codex", + allowsHiddenAttribution: true, + }, + ); - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "azure-openai", api: "azure-openai-responses", @@ -276,21 +289,22 @@ describe("provider attribution", () => { transport: "stream", capability: "llm", }), - ).toMatchObject({ - endpointClass: "azure-openai", - attributionProvider: undefined, - allowsHiddenAttribution: false, - usesKnownNativeOpenAIEndpoint: true, - }); + { + endpointClass: "azure-openai", + attributionProvider: undefined, + allowsHiddenAttribution: false, + usesKnownNativeOpenAIEndpoint: true, + }, + ); }); it("classifies native Mistral hosts centrally", () => { - expect(resolveProviderEndpoint("https://api.mistral.ai/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.mistral.ai/v1"), { endpointClass: "mistral-public", hostname: "api.mistral.ai", }); - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "mistral", api: "openai-completions", @@ -298,50 +312,51 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "mistral-public", - isKnownNativeEndpoint: true, - knownProviderFamily: "mistral", - }); + { + endpointClass: "mistral-public", + isKnownNativeEndpoint: true, + knownProviderFamily: "mistral", + }, + ); }); it("classifies native OpenAI-compatible vendor hosts centrally", () => { - expect(resolveProviderEndpoint("https://api.x.ai/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.x.ai/v1"), { endpointClass: "xai-native", hostname: "api.x.ai", }); - expect(resolveProviderEndpoint("https://api.grok.x.ai/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.grok.x.ai/v1"), { endpointClass: "xai-native", hostname: "api.grok.x.ai", }); - expect(resolveProviderEndpoint("https://api.z.ai/api/coding/paas/v4")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.z.ai/api/coding/paas/v4"), { endpointClass: "zai-native", hostname: "api.z.ai", }); - expect(resolveProviderEndpoint("https://api.deepseek.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.deepseek.com"), { endpointClass: "deepseek-native", hostname: "api.deepseek.com", }); - expect(resolveProviderEndpoint("https://llm.chutes.ai/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://llm.chutes.ai/v1"), { endpointClass: "chutes-native", hostname: "llm.chutes.ai", }); - expect(resolveProviderEndpoint("https://api.groq.com/openai/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.groq.com/openai/v1"), { endpointClass: "groq-native", hostname: "api.groq.com", }); - expect(resolveProviderEndpoint("https://api.cerebras.ai/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.cerebras.ai/v1"), { endpointClass: "cerebras-native", hostname: "api.cerebras.ai", }); - expect(resolveProviderEndpoint("https://opencode.ai/api")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://opencode.ai/api"), { endpointClass: "opencode-native", hostname: "opencode.ai", }); }); it("treats OpenRouter-hosted Responses routes as explicit proxy-like endpoints", () => { - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openrouter", api: "openai-responses", @@ -349,15 +364,16 @@ describe("provider attribution", () => { transport: "stream", capability: "llm", }), - ).toMatchObject({ - endpointClass: "openrouter", - usesExplicitProxyLikeEndpoint: true, - attributionProvider: "openrouter", - }); + { + endpointClass: "openrouter", + usesExplicitProxyLikeEndpoint: true, + attributionProvider: "openrouter", + }, + ); }); it("gates documented OpenRouter attribution to known OpenRouter endpoints", () => { - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openrouter", api: "openai-responses", @@ -365,11 +381,12 @@ describe("provider attribution", () => { transport: "stream", capability: "llm", }), - ).toMatchObject({ - endpointClass: "openrouter", - attributionProvider: "openrouter", - allowsHiddenAttribution: false, - }); + { + endpointClass: "openrouter", + attributionProvider: "openrouter", + allowsHiddenAttribution: false, + }, + ); expect( resolveProviderRequestAttributionHeaders({ @@ -453,163 +470,167 @@ describe("provider attribution", () => { }); it("models other provider families without enabling hidden attribution", () => { - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "google", baseUrl: "https://generativelanguage.googleapis.com", transport: "http", capability: "image", }), - ).toMatchObject({ - knownProviderFamily: "google", - attributionProvider: undefined, - allowsHiddenAttribution: false, - }); + { + knownProviderFamily: "google", + attributionProvider: undefined, + allowsHiddenAttribution: false, + }, + ); - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "github-copilot", transport: "http", capability: "llm", }), - ).toMatchObject({ - knownProviderFamily: "github-copilot", - attributionProvider: undefined, - allowsHiddenAttribution: false, - }); + { + knownProviderFamily: "github-copilot", + attributionProvider: undefined, + allowsHiddenAttribution: false, + }, + ); }); it("classifies native Anthropic endpoints separately from custom hosts", () => { - expect(resolveProviderEndpoint("https://api.anthropic.com/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.anthropic.com/v1"), { endpointClass: "anthropic-public", hostname: "api.anthropic.com", }); - expect(resolveProviderEndpoint("https://proxy.example.com/anthropic")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://proxy.example.com/anthropic"), { endpointClass: "custom", hostname: "proxy.example.com", }); }); it("classifies Google Gemini and Vertex endpoints separately from custom hosts", () => { - expect(resolveProviderEndpoint("https://generativelanguage.googleapis.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://generativelanguage.googleapis.com"), { endpointClass: "google-generative-ai", hostname: "generativelanguage.googleapis.com", }); - expect( + expectRecordFields( resolveProviderEndpoint("https://europe-west4-aiplatform.googleapis.com/v1/projects/test"), - ).toMatchObject({ - endpointClass: "google-vertex", - hostname: "europe-west4-aiplatform.googleapis.com", - googleVertexRegion: "europe-west4", - }); + { + endpointClass: "google-vertex", + hostname: "europe-west4-aiplatform.googleapis.com", + googleVertexRegion: "europe-west4", + }, + ); - expect(resolveProviderEndpoint("https://aiplatform.googleapis.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://aiplatform.googleapis.com"), { endpointClass: "google-vertex", hostname: "aiplatform.googleapis.com", googleVertexRegion: "global", }); - expect(resolveProviderEndpoint("https://proxy.example.com/google")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://proxy.example.com/google"), { endpointClass: "custom", hostname: "proxy.example.com", }); }); it("classifies native Moonshot and ModelStudio endpoints separately from custom hosts", () => { - expect(resolveProviderEndpoint("https://api.moonshot.ai/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.moonshot.ai/v1"), { endpointClass: "moonshot-native", hostname: "api.moonshot.ai", }); - expect(resolveProviderEndpoint("https://api.moonshot.cn/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.moonshot.cn/v1"), { endpointClass: "moonshot-native", hostname: "api.moonshot.cn", }); - expect( + expectRecordFields( resolveProviderEndpoint("https://dashscope-intl.aliyuncs.com/compatible-mode/v1"), - ).toMatchObject({ - endpointClass: "modelstudio-native", - hostname: "dashscope-intl.aliyuncs.com", - }); + { + endpointClass: "modelstudio-native", + hostname: "dashscope-intl.aliyuncs.com", + }, + ); - expect(resolveProviderEndpoint("https://proxy.example.com/v1")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://proxy.example.com/v1"), { endpointClass: "custom", hostname: "proxy.example.com", }); }); it("classifies native GitHub Copilot endpoints separately from custom hosts", () => { - expect(resolveProviderEndpoint("https://api.individual.githubcopilot.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.individual.githubcopilot.com"), { endpointClass: "github-copilot-native", hostname: "api.individual.githubcopilot.com", }); - expect(resolveProviderEndpoint("https://api.enterprise.githubcopilot.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.enterprise.githubcopilot.com"), { endpointClass: "github-copilot-native", hostname: "api.enterprise.githubcopilot.com", }); - expect(resolveProviderEndpoint("https://api.githubcopilot.example.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://api.githubcopilot.example.com"), { endpointClass: "custom", hostname: "api.githubcopilot.example.com", }); }); it("does not classify malformed or embedded Google host strings as native endpoints", () => { - expect(resolveProviderEndpoint("proxy/generativelanguage.googleapis.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("proxy/generativelanguage.googleapis.com"), { endpointClass: "custom", hostname: "proxy", }); - expect(resolveProviderEndpoint("https://xgenerativelanguage.googleapis.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://xgenerativelanguage.googleapis.com"), { endpointClass: "custom", hostname: "xgenerativelanguage.googleapis.com", }); - expect(resolveProviderEndpoint("proxy/aiplatform.googleapis.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("proxy/aiplatform.googleapis.com"), { endpointClass: "custom", hostname: "proxy", }); - expect(resolveProviderEndpoint("https://xaiplatform.googleapis.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("https://xaiplatform.googleapis.com"), { endpointClass: "custom", hostname: "xaiplatform.googleapis.com", }); }); it("does not trust schemeless or embedded trusted-provider substrings", () => { - expect(resolveProviderEndpoint("api.anthropic.com.attacker.example")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("api.anthropic.com.attacker.example"), { endpointClass: "custom", hostname: "api.anthropic.com.attacker.example", }); - expect(resolveProviderEndpoint("api.openai.com.attacker.example")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("api.openai.com.attacker.example"), { endpointClass: "custom", hostname: "api.openai.com.attacker.example", }); - expect(resolveProviderEndpoint("attacker.example/?target=api.openai.com")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("attacker.example/?target=api.openai.com"), { endpointClass: "custom", hostname: "attacker.example", }); - expect(resolveProviderEndpoint("openrouter.ai.attacker.example")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("openrouter.ai.attacker.example"), { endpointClass: "custom", hostname: "openrouter.ai.attacker.example", }); }); it("ignores non-http schemes when normalizing native comparable base URLs", () => { - expect(resolveProviderEndpoint("javascript:alert(1)")).toMatchObject({ + expectRecordFields(resolveProviderEndpoint("javascript:alert(1)"), { endpointClass: "invalid", }); }); it("applies OpenAI attribution to every verified native capability", () => { - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openai", api: "openai-audio-transcriptions", @@ -617,12 +638,13 @@ describe("provider attribution", () => { transport: "media-understanding", capability: "audio", }), - ).toMatchObject({ - attributionProvider: "openai", - allowsHiddenAttribution: true, - }); + { + attributionProvider: "openai", + allowsHiddenAttribution: true, + }, + ); - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openai", api: "openai-responses", @@ -630,38 +652,41 @@ describe("provider attribution", () => { transport: "media-understanding", capability: "audio", }), - ).toMatchObject({ - attributionProvider: "openai", - allowsHiddenAttribution: true, - }); + { + attributionProvider: "openai", + allowsHiddenAttribution: true, + }, + ); - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openai", baseUrl: "https://api.openai.com/v1", transport: "http", capability: "image", }), - ).toMatchObject({ - attributionProvider: "openai", - allowsHiddenAttribution: true, - }); + { + attributionProvider: "openai", + allowsHiddenAttribution: true, + }, + ); - expect( + expectRecordFields( resolveProviderRequestPolicy({ provider: "openai", baseUrl: "https://api.openai.com/v1", transport: "websocket", capability: "audio", }), - ).toMatchObject({ - attributionProvider: "openai", - allowsHiddenAttribution: true, - }); + { + attributionProvider: "openai", + allowsHiddenAttribution: true, + }, + ); }); it("resolves centralized request capabilities for native and proxied routes", () => { - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "openai", api: "openai-responses", @@ -669,28 +694,30 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "openai-public", - allowsOpenAIServiceTier: true, - supportsOpenAIReasoningCompatPayload: true, - allowsResponsesStore: true, - supportsResponsesStoreField: true, - shouldStripResponsesPromptCache: false, - }); + { + endpointClass: "openai-public", + allowsOpenAIServiceTier: true, + supportsOpenAIReasoningCompatPayload: true, + allowsResponsesStore: true, + supportsResponsesStoreField: true, + shouldStripResponsesPromptCache: false, + }, + ); - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "anthropic", api: "anthropic-messages", capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "default", - allowsAnthropicServiceTier: true, - }); + { + endpointClass: "default", + allowsAnthropicServiceTier: true, + }, + ); - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "custom-proxy", api: "openai-responses", @@ -698,20 +725,21 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "custom", - allowsOpenAIServiceTier: false, - supportsOpenAIReasoningCompatPayload: false, - allowsResponsesStore: false, - supportsResponsesStoreField: true, - shouldStripResponsesPromptCache: true, - }); + { + endpointClass: "custom", + allowsOpenAIServiceTier: false, + supportsOpenAIReasoningCompatPayload: false, + allowsResponsesStore: false, + supportsResponsesStoreField: true, + shouldStripResponsesPromptCache: true, + }, + ); }); it("respects compat.supportsPromptCacheKey override on prompt cache stripping", () => { // compat.supportsPromptCacheKey = true disables the strip even on a // proxy-like endpoint that would otherwise trigger it. - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "custom-proxy", api: "openai-responses", @@ -720,14 +748,15 @@ describe("provider attribution", () => { transport: "stream", compat: { supportsPromptCacheKey: true }, }), - ).toMatchObject({ - endpointClass: "custom", - shouldStripResponsesPromptCache: false, - }); + { + endpointClass: "custom", + shouldStripResponsesPromptCache: false, + }, + ); // compat.supportsPromptCacheKey = false forces the strip even on a // native OpenAI endpoint that would otherwise forward the field. - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "openai", api: "openai-responses", @@ -736,15 +765,16 @@ describe("provider attribution", () => { transport: "stream", compat: { supportsPromptCacheKey: false }, }), - ).toMatchObject({ - endpointClass: "openai-public", - shouldStripResponsesPromptCache: true, - }); + { + endpointClass: "openai-public", + shouldStripResponsesPromptCache: true, + }, + ); // compat.supportsPromptCacheKey unset preserves the existing default // (strip on proxy-like responses endpoints, preserving the fix from // #48155 for providers that reject the field). - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "custom-proxy", api: "openai-responses", @@ -752,13 +782,14 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - shouldStripResponsesPromptCache: true, - }); + { + shouldStripResponsesPromptCache: true, + }, + ); }); it("resolves shared compat families and native streaming-usage gates", () => { - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "moonshot", api: "openai-completions", @@ -766,13 +797,14 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "moonshot-native", - supportsNativeStreamingUsageCompat: true, - compatibilityFamily: "moonshot", - }); + { + endpointClass: "moonshot-native", + supportsNativeStreamingUsageCompat: true, + compatibilityFamily: "moonshot", + }, + ); - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "qwen", api: "openai-completions", @@ -780,12 +812,13 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "modelstudio-native", - supportsNativeStreamingUsageCompat: true, - }); + { + endpointClass: "modelstudio-native", + supportsNativeStreamingUsageCompat: true, + }, + ); - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "generic", api: "openai-completions", @@ -793,12 +826,13 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "modelstudio-native", - supportsNativeStreamingUsageCompat: true, - }); + { + endpointClass: "modelstudio-native", + supportsNativeStreamingUsageCompat: true, + }, + ); - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "custom-local", api: "openai-completions", @@ -806,14 +840,15 @@ describe("provider attribution", () => { capability: "llm", transport: "stream", }), - ).toMatchObject({ - endpointClass: "local", - supportsNativeStreamingUsageCompat: false, - }); + { + endpointClass: "local", + supportsNativeStreamingUsageCompat: false, + }, + ); }); it("treats native GitHub Copilot base URLs as known native endpoints", () => { - expect( + expectRecordFields( resolveProviderRequestCapabilities({ provider: "github-copilot", api: "openai-responses", @@ -821,11 +856,12 @@ describe("provider attribution", () => { capability: "llm", transport: "http", }), - ).toMatchObject({ - endpointClass: "github-copilot-native", - knownProviderFamily: "github-copilot", - isKnownNativeEndpoint: true, - }); + { + endpointClass: "github-copilot-native", + knownProviderFamily: "github-copilot", + isKnownNativeEndpoint: true, + }, + ); }); it("resolves a provider capability matrix for representative native and proxied routes", () => { @@ -1072,9 +1108,7 @@ describe("provider attribution", () => { ]; for (const testCase of cases) { - expect(resolveProviderRequestCapabilities(testCase.input), testCase.name).toMatchObject( - testCase.expected, - ); + expectRecordFields(resolveProviderRequestCapabilities(testCase.input), testCase.expected); } }); });