From 701d60dc8c24495a1ebc58c90d85c082d875ce53 Mon Sep 17 00:00:00 2001 From: Prudhvi Vuda <53619858+Prudhvivuda@users.noreply.github.com> Date: Wed, 5 Aug 2026 21:32:02 -0400 Subject: [PATCH] fix(sse): route Poe API-key traffic through DefaultExecutor (#8969) (#9014) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates: typecheck/complexity/cognitive/changelog/vitest — only pre-existing audit.test.ts flake). Evidence: /home/diegosouzapw/dev/proxys/OmniRoute/.claude/worktrees/merge-train-20260805-222248-suite.log --- .../config/providers/registry/poe/index.ts | 72 +++- open-sse/executors/default.ts | 34 ++ open-sse/executors/index.ts | 4 +- .../handlers/chatCore/executionCredentials.ts | 12 + .../chatcore-execution-credentials.test.ts | 22 ++ .../unit/poe-api-executor-regression.test.ts | 356 ++++++++++++++++++ tests/unit/web-cookie-providers-new.test.ts | 4 +- 7 files changed, 500 insertions(+), 4 deletions(-) create mode 100644 tests/unit/poe-api-executor-regression.test.ts diff --git a/open-sse/config/providers/registry/poe/index.ts b/open-sse/config/providers/registry/poe/index.ts index b80d612fa5..85a84d66dc 100644 --- a/open-sse/config/providers/registry/poe/index.ts +++ b/open-sse/config/providers/registry/poe/index.ts @@ -1,4 +1,5 @@ import type { RegistryEntry } from "../../shared.ts"; +import { normalizeBaseUrl } from "../../../../utils/urlSanitize.ts"; // Poe (creator.poe.com) — OpenAI-compatible chat/responses gateway. #8082: the // built-in `poe` provider (NAMED_OPENAI_STYLE_PROVIDERS, passthroughModels:true) @@ -7,19 +8,86 @@ import type { RegistryEntry } from "../../shared.ts"; // for provider" even though credentials/inference worked fine. This base URL is // the single source of truth other Poe code paths should read from (see // src/lib/providers/validation/audioMiscProviders.ts::validatePoeProvider). +// +// #8969: canonical `poe` is the API-key provider (DefaultExecutor → api.poe.com). +// The web-cookie GraphQL transport lives only on `poe-web` / PoeWebExecutor — +// never alias `poe` to that executor (it posts to /api/gql_POST and returns 405). export const POE_DEFAULT_BASE_URL = "https://api.poe.com/v1"; +export const POE_CHAT_COMPLETIONS_URL = `${POE_DEFAULT_BASE_URL}/chat/completions`; +export const POE_RESPONSES_URL = `${POE_DEFAULT_BASE_URL}/responses`; +export const POE_MESSAGES_URL = `${POE_DEFAULT_BASE_URL}/messages`; + +/** Official Claude model ids are the only ones Poe accepts on /v1/messages. */ +export function isPoeMessagesEligibleModel(model: string | null | undefined): boolean { + if (typeof model !== "string" || !model) return false; + return /(?:^|[\/._-])claude(?:[\/._-]|$)/i.test(model); +} + +export type PoeUpstreamProtocol = "chat" | "responses" | "messages"; + +/** + * Normalize an operator-supplied or registry Poe base URL onto one of the three + * documented API surfaces. Accepts bare host, `/v1`, full chat/completions URL, + * and trailing-slash variants. + */ +export function resolvePoeUpstreamUrl(opts: { + protocol: PoeUpstreamProtocol; + configuredBaseUrl?: string | null; + responsesBaseUrl?: string | null; + messagesUrl?: string | null; + defaultChatUrl?: string | null; +}): string { + const defaultChat = opts.defaultChatUrl || POE_CHAT_COMPLETIONS_URL; + const defaultResponses = opts.responsesBaseUrl || POE_RESPONSES_URL; + const defaultMessages = opts.messagesUrl || POE_MESSAGES_URL; + + if (opts.protocol === "responses" && !opts.configuredBaseUrl) { + return defaultResponses; + } + if (opts.protocol === "messages" && !opts.configuredBaseUrl) { + return defaultMessages; + } + if (opts.protocol === "chat" && !opts.configuredBaseUrl) { + return defaultChat; + } + + const raw = normalizeBaseUrl(opts.configuredBaseUrl || defaultChat); + // Strip any known protocol suffix so we can re-append the requested one. + const root = raw + .replace(/\/chat\/completions\/?$/i, "") + .replace(/\/responses\/?$/i, "") + .replace(/\/messages\/?$/i, "") + .replace(/\/$/, ""); + + const withV1 = /\/v1$/i.test(root) ? root : `${root}/v1`; + + if (opts.protocol === "responses") return `${withV1}/responses`; + if (opts.protocol === "messages") return `${withV1}/messages`; + return `${withV1}/chat/completions`; +} + export const poeProvider: RegistryEntry = { id: "poe", alias: "poe", format: "openai", executor: "default", - baseUrl: `${POE_DEFAULT_BASE_URL}/chat/completions`, + baseUrl: POE_CHAT_COMPLETIONS_URL, + responsesBaseUrl: POE_RESPONSES_URL, + // Anthropic-compatible Messages API — official Claude models only + // (https://creator.poe.com/docs/external-applications/anthropic-compatible-api). + // Routed via each claude-* model's targetFormat: "claude" below; GPT/Gemini + // stay on Chat Completions / Responses. + messagesUrl: POE_MESSAGES_URL, authType: "apikey", authHeader: "bearer", models: [ { id: "gpt-5.2", name: "GPT-5.2" }, - { id: "claude-opus-4.8", name: "Claude Opus 4.8" }, + { + id: "claude-opus-4.8", + name: "Claude Opus 4.8", + targetFormat: "claude", + }, { id: "gemini-3.0-pro", name: "Gemini 3.0 Pro" }, ], }; diff --git a/open-sse/executors/default.ts b/open-sse/executors/default.ts index 1820e48a01..34dac8a4af 100644 --- a/open-sse/executors/default.ts +++ b/open-sse/executors/default.ts @@ -40,6 +40,10 @@ import { normalizeOpenAIChatUrl, getOpenRouterConnectionPreset, } from "./default/urlNormalizers.ts"; +import { + isPoeMessagesEligibleModel, + resolvePoeUpstreamUrl, +} from "../config/providers/registry/poe/index.ts"; import { buildMaritalkChatUrl } from "../config/maritalk.ts"; import { LOCAL_PROVIDERS } from "@/shared/constants/providers"; import { isForbiddenCustomHeaderName } from "@/shared/constants/upstreamHeaders"; @@ -285,6 +289,36 @@ export class DefaultExecutor extends BaseExecutor { case "glm-coding-apikey": // #7364: format override extracted to zaiFormatOverride.ts (file-size ratchet). return resolveZaiUrl(credentials, (fallback) => this.resolveBaseUrl(credentials, fallback)); + case "poe": { + // #8969: Poe API-key surfaces — Chat Completions, Responses, and + // Claude-only Messages. Prefer the responses marker from + // resolveExecutionCredentials (incoming /v1/responses), then the + // registry Claude targetFormat → messagesUrl, else chat/completions. + // GPT models must never hit /v1/messages (Poe rejects non-Claude there). + const psd = credentials?.providerSpecificData; + const manualBaseUrl = + typeof psd?.baseUrl === "string" && psd.baseUrl.trim() ? psd.baseUrl.trim() : null; + const forceResponses = psd?._omnirouteForceResponsesUpstream === true; + const modelTarget = getModelTargetFormat("poe", model); + const connectionTarget = + typeof psd?.targetFormat === "string" ? (psd.targetFormat as string) : null; + const effectiveTarget = modelTarget || connectionTarget; + + let protocol: "chat" | "responses" | "messages" = "chat"; + if (forceResponses || effectiveTarget === "openai-responses") { + protocol = "responses"; + } else if (effectiveTarget === "claude" && isPoeMessagesEligibleModel(model)) { + protocol = "messages"; + } + + return resolvePoeUpstreamUrl({ + protocol, + configuredBaseUrl: manualBaseUrl, + responsesBaseUrl: this.config.responsesBaseUrl, + messagesUrl: this.config.messagesUrl, + defaultChatUrl: this.config.baseUrl, + }); + } case "claude": case "glm": case "glmt": diff --git a/open-sse/executors/index.ts b/open-sse/executors/index.ts index 49fe28ee6c..767d087927 100644 --- a/open-sse/executors/index.ts +++ b/open-sse/executors/index.ts @@ -152,7 +152,9 @@ const executors = { "yuanbao-web": new YuanbaoWebExecutor(), ybw: new YuanbaoWebExecutor(), // Alias "poe-web": new PoeWebExecutor(), - poe: new PoeWebExecutor(), // Alias + // #8969: do NOT alias canonical `poe` (API-key / api.poe.com) to PoeWebExecutor. + // Registry declares executor:"default"; the hard-coded map previously won and + // routed API-key traffic to GraphQL /api/gql_POST → HTTP 405. "venice-web": new VeniceWebExecutor(), ven: new VeniceWebExecutor(), // Alias "notion-web": new NotionWebExecutor(), diff --git a/open-sse/handlers/chatCore/executionCredentials.ts b/open-sse/handlers/chatCore/executionCredentials.ts index 1a96ef92ee..c8e4223774 100644 --- a/open-sse/handlers/chatCore/executionCredentials.ts +++ b/open-sse/handlers/chatCore/executionCredentials.ts @@ -118,6 +118,18 @@ export function resolveExecutionCredentials(opts: { providerSpecificData._omnirouteForceResponsesUpstream = true; } + // #8969: Poe's native /v1/responses surface — DefaultExecutor.buildUrl("poe") + // reads this marker so Responses requests do not land on chat/completions. + if (targetFormat === FORMATS.OPENAI_RESPONSES && provider === "poe") { + providerSpecificData._omnirouteForceResponsesUpstream = true; + } + + // #8969: Claude-tagged Poe models speak Anthropic Messages wire format. Keep + // DefaultExecutor from injecting OpenAI stream_options onto that body. + if (targetFormat === FORMATS.CLAUDE && provider === "poe") { + providerSpecificData.disableStreamOptions = true; + } + // #7364: "zai"/"glm-coding-apikey" default to the Anthropic Messages wire format // (registry format:"claude"), but a per-model targetFormat override (custom-model // dropdown, #2905) can resolve targetFormat to "openai" — e.g. for a vision model diff --git a/tests/unit/chatcore-execution-credentials.test.ts b/tests/unit/chatcore-execution-credentials.test.ts index b8faeccf90..cab350fd9b 100644 --- a/tests/unit/chatcore-execution-credentials.test.ts +++ b/tests/unit/chatcore-execution-credentials.test.ts @@ -105,6 +105,28 @@ test("AgentRouter threads the resolved Responses protocol only into execution cr assert.deepEqual(credentials.providerSpecificData, { apiKeyHealth: {} }); }); +test("#8969: poe + responses target sets the responses-upstream marker (no apiType)", () => { + const out = resolveExecutionCredentials({ + ...base, + provider: "poe", + targetFormat: RESPONSES, + }) as Record; + const psd = out.providerSpecificData as Record; + assert.equal(psd.apiType, undefined); + assert.equal(psd._omnirouteForceResponsesUpstream, true); +}); + +test("#8969: poe + claude target disables OpenAI stream_options injection", () => { + const out = resolveExecutionCredentials({ + ...base, + provider: "poe", + targetFormat: "claude", + }) as Record; + const psd = out.providerSpecificData as Record; + assert.equal(psd.disableStreamOptions, true); + assert.equal(psd._omnirouteForceResponsesUpstream, undefined); +}); + test("ccSessionId is threaded into providerSpecificData when present", () => { const out = resolveExecutionCredentials({ ...base, diff --git a/tests/unit/poe-api-executor-regression.test.ts b/tests/unit/poe-api-executor-regression.test.ts new file mode 100644 index 0000000000..52b04a4ebc --- /dev/null +++ b/tests/unit/poe-api-executor-regression.test.ts @@ -0,0 +1,356 @@ +/** + * #8969: Canonical `poe` API-key provider must use DefaultExecutor → api.poe.com, + * not the web-cookie PoeWebExecutor GraphQL path (which returns HTTP 405). + * + * Locks: + * - getExecutor("poe") is DefaultExecutor; getExecutor("poe-web") stays PoeWebExecutor + * - Chat Completions / Responses / Messages URL selection + auth headers + * - GPT models never hit Poe's Claude-only Messages endpoint + * - Operator baseUrl shapes (bare host, /v1/, trailing slash) normalize correctly + * - Upstream 405 is preserved (not swallowed) + */ +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-poe-api-8969-")); +process.env.DATA_DIR = TEST_DATA_DIR; + +const { getExecutor, hasSpecializedExecutor } = await import("../../open-sse/executors/index.ts"); +const { DefaultExecutor } = await import("../../open-sse/executors/default.ts"); +const { PoeWebExecutor } = await import("../../open-sse/executors/poe-web.ts"); +const { getRegistryEntry } = await import("../../open-sse/config/providerRegistry.ts"); +const { resolveExecutionCredentials } = await import( + "../../open-sse/handlers/chatCore/executionCredentials.ts" +); +const { POE_DEFAULT_BASE_URL, resolvePoeUpstreamUrl } = await import( + "../../open-sse/config/providers/registry/poe/index.ts" +); +const core = await import("../../src/lib/db/core.ts"); + +test.after(() => { + try { + core.resetDbInstance(); + } catch { + // ignore + } + fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); +}); + +const CHAT_URL = "https://api.poe.com/v1/chat/completions"; +const RESPONSES_URL = "https://api.poe.com/v1/responses"; +const MESSAGES_URL = "https://api.poe.com/v1/messages"; + +function headerRecord(headers: Record): Record { + const out: Record = {}; + for (const [k, v] of Object.entries(headers)) out[k.toLowerCase()] = v; + return out; +} + +test("#8969: getExecutor(poe) selects DefaultExecutor, not PoeWebExecutor", () => { + assert.equal(hasSpecializedExecutor("poe"), false); + const executor = getExecutor("poe"); + assert.ok(executor instanceof DefaultExecutor); + assert.equal(executor instanceof PoeWebExecutor, false); + assert.equal(executor.provider, "poe"); +}); + +test("#8969: getExecutor(poe-web) still selects PoeWebExecutor", () => { + assert.equal(hasSpecializedExecutor("poe-web"), true); + assert.ok(getExecutor("poe-web") instanceof PoeWebExecutor); +}); + +test("#8969: registry declares API-key executor + all three Poe protocol URLs", () => { + const entry = getRegistryEntry("poe"); + assert.ok(entry); + assert.equal(entry.executor, "default"); + assert.equal(entry.authType, "apikey"); + assert.equal(entry.authHeader, "bearer"); + assert.equal(entry.baseUrl, CHAT_URL); + assert.equal(entry.responsesBaseUrl, RESPONSES_URL); + assert.equal(entry.messagesUrl, MESSAGES_URL); + assert.equal(POE_DEFAULT_BASE_URL, "https://api.poe.com/v1"); + + const claude = entry.models?.find((m) => m.id === "claude-opus-4.8"); + assert.ok(claude, "catalog must include a Claude model"); + assert.equal(claude.targetFormat, "claude"); + + const gpt = entry.models?.find((m) => m.id === "gpt-5.2"); + assert.ok(gpt, "catalog must include a GPT model"); + assert.notEqual(gpt.targetFormat, "claude"); +}); + +test("#8969: buildUrl routes chat / responses / messages correctly", () => { + const executor = getExecutor("poe") as DefaultExecutor; + const creds = { apiKey: "poe-test-key", providerSpecificData: {} }; + + assert.equal(executor.buildUrl("gemma-4-31b", false, 0, creds), CHAT_URL); + assert.equal(executor.buildUrl("gemma-4-31b", true, 0, creds), CHAT_URL); + assert.equal(executor.buildUrl("gpt-5.2", false, 0, creds), CHAT_URL); + + assert.equal( + executor.buildUrl("gpt-5.2", false, 0, { + ...creds, + providerSpecificData: { _omnirouteForceResponsesUpstream: true }, + }), + RESPONSES_URL + ); + assert.equal( + executor.buildUrl("claude-opus-4.8", true, 0, { + ...creds, + providerSpecificData: { _omnirouteForceResponsesUpstream: true }, + }), + RESPONSES_URL + ); + + assert.equal(executor.buildUrl("claude-opus-4.8", false, 0, creds), MESSAGES_URL); + assert.equal(executor.buildUrl("claude-opus-4.8", true, 0, creds), MESSAGES_URL); + + // GPT must never land on Poe's Claude-only Messages endpoint — even if an + // operator incorrectly stamps targetFormat=claude on the connection. + assert.equal( + executor.buildUrl("gpt-5.2", false, 0, { + ...creds, + providerSpecificData: { targetFormat: "claude" }, + }), + CHAT_URL + ); + assert.notEqual( + executor.buildUrl("gpt-5.2", false, 0, { + ...creds, + providerSpecificData: { targetFormat: "claude" }, + }), + MESSAGES_URL + ); +}); + +test("#8969: resolvePoeUpstreamUrl normalizes registry-default / bare-host /v1/ / trailing-slash bases", () => { + const cases: Array<{ base: string | null | undefined; protocol: "chat" | "responses" | "messages"; expected: string }> = + [ + { base: undefined, protocol: "chat", expected: CHAT_URL }, + { base: null, protocol: "chat", expected: CHAT_URL }, + { base: "https://api.poe.com", protocol: "chat", expected: CHAT_URL }, + { base: "https://api.poe.com/", protocol: "chat", expected: CHAT_URL }, + { base: "https://api.poe.com/v1", protocol: "chat", expected: CHAT_URL }, + { base: "https://api.poe.com/v1/", protocol: "chat", expected: CHAT_URL }, + { + base: "https://api.poe.com/v1/chat/completions", + protocol: "chat", + expected: CHAT_URL, + }, + { + base: "https://api.poe.com/v1/chat/completions/", + protocol: "chat", + expected: CHAT_URL, + }, + { base: "https://api.poe.com/v1", protocol: "responses", expected: RESPONSES_URL }, + { base: "https://api.poe.com/", protocol: "messages", expected: MESSAGES_URL }, + { + base: "https://custom.example/v1/chat/completions", + protocol: "responses", + expected: "https://custom.example/v1/responses", + }, + { + base: "https://custom.example/v1", + protocol: "messages", + expected: "https://custom.example/v1/messages", + }, + ]; + + for (const { base, protocol, expected } of cases) { + assert.equal( + resolvePoeUpstreamUrl({ + protocol, + configuredBaseUrl: base, + responsesBaseUrl: RESPONSES_URL, + messagesUrl: MESSAGES_URL, + defaultChatUrl: CHAT_URL, + }), + expected, + `base=${String(base)} protocol=${protocol}` + ); + } +}); + +test("#8969: buildHeaders uses Bearer auth and never sends Cookie", () => { + const executor = getExecutor("poe") as DefaultExecutor; + for (const stream of [false, true]) { + const headers = headerRecord( + executor.buildHeaders({ apiKey: "poe-test-key", providerSpecificData: {} }, stream) + ); + assert.equal(headers.authorization, "Bearer poe-test-key"); + assert.equal(headers.cookie, undefined); + assert.equal(headers.accept, stream ? "text/event-stream" : "application/json"); + } +}); + +test("#8969: resolveExecutionCredentials forces responses upstream for poe", () => { + const out = resolveExecutionCredentials({ + credentials: { providerSpecificData: {} }, + nativeCodexPassthrough: false, + endpointPath: "/v1/responses", + targetFormat: "openai-responses", + provider: "poe", + ccSessionId: null, + }) as { providerSpecificData: Record }; + assert.equal(out.providerSpecificData._omnirouteForceResponsesUpstream, true); +}); + +test("#8969: mocked execute posts Chat Completions with Bearer, no Cookie, stripped model", async () => { + const executor = getExecutor("poe") as DefaultExecutor; + const originalFetch = globalThis.fetch; + const seen: Array<{ + url: string; + method: string; + authorization: string | null; + cookie: string | null; + body: Record; + }> = []; + + globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => { + const headers = new Headers(init?.headers); + const rawBody = typeof init?.body === "string" ? init.body : "{}"; + seen.push({ + url: String(input), + method: (init?.method || "GET").toUpperCase(), + authorization: headers.get("authorization"), + cookie: headers.get("cookie"), + body: JSON.parse(rawBody) as Record, + }); + return Response.json({ + id: "chatcmpl-test", + object: "chat.completion", + choices: [{ index: 0, message: { role: "assistant", content: "OK" }, finish_reason: "stop" }], + }); + }) as typeof fetch; + + try { + for (const stream of [false, true]) { + seen.length = 0; + const result = await executor.execute({ + model: "gemma-4-31b", + body: { + model: "gemma-4-31b", + messages: [{ role: "user", content: "Reply with OK only." }], + max_tokens: 64, + stream, + }, + stream, + credentials: { apiKey: "poe-test-key", providerSpecificData: {} }, + signal: null, + }); + + assert.equal(seen.length, 1, `expected one upstream call (stream=${stream})`); + assert.equal(seen[0].method, "POST"); + assert.equal(seen[0].url, CHAT_URL); + assert.equal(seen[0].authorization, "Bearer poe-test-key"); + assert.equal(seen[0].cookie, null); + assert.equal(seen[0].body.model, "gemma-4-31b"); + assert.equal(seen[0].url.includes("poe.com/api/gql"), false); + assert.ok(result.response instanceof Response); + assert.equal(result.response.status, 200); + } + } finally { + globalThis.fetch = originalFetch; + } +}); + +test("#8969: mocked execute routes Responses + Messages fixtures to the right URLs", async () => { + const executor = getExecutor("poe") as DefaultExecutor; + const originalFetch = globalThis.fetch; + let lastUrl = ""; + + globalThis.fetch = (async (input: RequestInfo | URL) => { + lastUrl = String(input); + return Response.json({ id: "ok", object: "response", output: [] }); + }) as typeof fetch; + + try { + await executor.execute({ + model: "gpt-5.2", + body: { model: "gpt-5.2", input: "hi", stream: false }, + stream: false, + credentials: { + apiKey: "poe-test-key", + providerSpecificData: { _omnirouteForceResponsesUpstream: true }, + }, + signal: null, + }); + assert.equal(lastUrl, RESPONSES_URL); + + await executor.execute({ + model: "claude-opus-4.8", + body: { + model: "claude-opus-4.8", + input: "hi", + stream: true, + }, + stream: true, + credentials: { + apiKey: "poe-test-key", + providerSpecificData: { _omnirouteForceResponsesUpstream: true }, + }, + signal: null, + }); + assert.equal(lastUrl, RESPONSES_URL); + + globalThis.fetch = (async (input: RequestInfo | URL) => { + lastUrl = String(input); + return Response.json({ + id: "msg_ok", + type: "message", + role: "assistant", + content: [{ type: "text", text: "OK" }], + }); + }) as typeof fetch; + + await executor.execute({ + model: "claude-opus-4.8", + body: { + model: "claude-opus-4.8", + messages: [{ role: "user", content: "hi" }], + max_tokens: 16, + stream: false, + }, + stream: false, + credentials: { apiKey: "poe-test-key", providerSpecificData: {} }, + signal: null, + }); + assert.equal(lastUrl, MESSAGES_URL); + } finally { + globalThis.fetch = originalFetch; + } +}); + +test("#8969: mocked upstream 405 is preserved (not swallowed)", async () => { + const executor = getExecutor("poe") as DefaultExecutor; + const originalFetch = globalThis.fetch; + + globalThis.fetch = (async () => { + return new Response(JSON.stringify({ detail: "Method Not Allowed" }), { + status: 405, + headers: { "content-type": "application/json" }, + }); + }) as typeof fetch; + + try { + const result = await executor.execute({ + model: "gemma-4-31b", + body: { + model: "gemma-4-31b", + messages: [{ role: "user", content: "hi" }], + stream: false, + }, + stream: false, + credentials: { apiKey: "poe-test-key", providerSpecificData: {} }, + signal: null, + }); + assert.equal(result.response.status, 405); + const text = await result.response.text(); + assert.match(text, /Method Not Allowed/); + } finally { + globalThis.fetch = originalFetch; + } +}); diff --git a/tests/unit/web-cookie-providers-new.test.ts b/tests/unit/web-cookie-providers-new.test.ts index be1042c0e7..731416db52 100644 --- a/tests/unit/web-cookie-providers-new.test.ts +++ b/tests/unit/web-cookie-providers-new.test.ts @@ -119,9 +119,11 @@ test("HuggingChat executor is registered", () => { test("Poe Web executor is registered", () => { assert.ok(hasSpecializedExecutor("poe-web")); - assert.ok(hasSpecializedExecutor("poe")); const executor = getExecutor("poe-web"); assert.ok(executor instanceof PoeWebExecutor); + // #8969: canonical API-key `poe` must not route through PoeWebExecutor. + assert.equal(hasSpecializedExecutor("poe"), false); + assert.ok(!(getExecutor("poe") instanceof PoeWebExecutor)); }); test("Venice Web executor is registered", () => {