diff --git a/AGENTS.md b/AGENTS.md
index ce665709a4..289ed00296 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -3,12 +3,12 @@
## Project
Unified AI proxy/router — route any LLM through one endpoint. Multi-provider support
-with **237 provider entries** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks,
+with **236 provider entries** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks,
Cohere, NVIDIA, Cerebras, Pollinations, Puter, Cloudflare AI, HuggingFace, DeepInfra,
SambaNova, Meta Llama API, Moonshot AI, AI21 Labs, Databricks, Snowflake, and many more)
with **MCP Server** (94 tools), **A2A v0.3 Protocol**, and **Electron desktop app**.
-> **Live counts (v3.8.40)**: providers 237 · MCP tools 94 · MCP scopes 30 · A2A skills 6 ·
+> **Live counts (v3.8.40)**: providers 236 · MCP tools 94 · MCP scopes 30 · A2A skills 6 ·
> open-sse services 298 · routing strategies 17 · auto-combo scoring factors 12 ·
> DB modules 94 · DB migrations 106 · base tables 17 · search providers 11 ·
> i18n locales 42. **Refresh with `npm run check:docs-all`.**
diff --git a/CLAUDE.md b/CLAUDE.md
index 261bcb4de4..b5b377e4bd 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -35,7 +35,7 @@ For full test matrix, see `CONTRIBUTING.md` → "Running Tests". For deep archit
## Project at a Glance
-**OmniRoute** — unified AI proxy/router. One endpoint, 237 LLM providers, auto-fallback.
+**OmniRoute** — unified AI proxy/router. One endpoint, 236 LLM providers, auto-fallback.
| Layer | Location | Purpose |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
diff --git a/README.md b/README.md
index 985180b87b..d17a351455 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
# 🚀 OmniRoute — The Free AI Gateway
-### Never stop coding. Connect every AI tool to **237 providers** — **50+ free** — through one endpoint.
+### Never stop coding. Connect every AI tool to **236 providers** — **50+ free** — through one endpoint.
**Plug Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini. Auto-fallback.**
@@ -138,11 +138,11 @@
-> One endpoint. **237 providers.** Never stop building — and let OmniRoute pick the cheapest one that works.
+> One endpoint. **236 providers.** Never stop building — and let OmniRoute pick the cheapest one that works.
- 🚫 Never hit limits Auto-fallback across 237 providers in milliseconds. Quota out? Next provider takes over — zero downtime. |
+ 🚫 Never hit limits Auto-fallback across 236 providers in milliseconds. Quota out? Next provider takes over — zero downtime. |
💸 Save up to 95% tokens RTK + Caveman stacked compression cuts 15–95% of eligible tokens (~89% avg on tool-heavy sessions). |
🆓 $0 to start 50+ providers with a free tier, 11 free forever (Kiro, Qoder, Pollinations, LongCat…). No card needed. |
@@ -301,7 +301,7 @@ Result: 4 layers of fallback = zero downtime
- **💸 Cost telemetry everywhere** — `X-OmniRoute-*` cost/usage headers on every endpoint (including media), a non-token cost engine, a cache-HIT `X-OmniRoute-Cost-Saved` header, and per-key USD spend quotas. → [API Reference](docs/reference/API_REFERENCE.md)
- **🧠 Memory you control** — opt-in int8 vector quantization (Qdrant + sqlite-vec), memory off by default, and a per-request `x-omniroute-no-memory` header. → [Memory](docs/frameworks/MEMORY.md)
- **🛡️ Security** — a prompt-injection guard across every LLM route (backed by a red-team suite), plus a free DuckDuckGo last-resort web search. → [Guardrails](docs/security/GUARDRAILS.md)
-- **🤝 More providers & agents** — Cursor Cloud Agent (a 4th cloud agent), CodeBuddy CN (`copilot.tencent.com`), a Google Flow video-generation provider, new gateways **DGrid** and **Pioneer AI** (Fastino Labs), inbound **xAI Grok** translators plus **Grok Build (xAI)** with an OAuth import-token flow, GPT-4 / GPT-4o-mini on the GitHub Copilot provider, multi-model **Factory Droid**, **ZenMux Free** (session-cookie free tier), **Alibaba DashScope** text-to-video (`wan2.7-t2v`), a refreshed 237-provider catalog (OrcaRouter, Wafer AI, OpenAdapter, dit.ai, TokenRouter, …), Vertex AI media generation (speech / transcription / music / video), and one-click account import from CLIProxyAPI (`~/.cli-proxy-api/`). → [Providers](docs/reference/PROVIDER_REFERENCE.md)
+- **🤝 More providers & agents** — Cursor Cloud Agent (a 4th cloud agent), CodeBuddy CN (`copilot.tencent.com`), a Google Flow video-generation provider, new gateways **DGrid** and **Pioneer AI** (Fastino Labs), inbound **xAI Grok** translators plus **Grok Build (xAI)** with an OAuth import-token flow, GPT-4 / GPT-4o-mini on the GitHub Copilot provider, multi-model **Factory Droid**, **ZenMux Free** (session-cookie free tier), **Alibaba DashScope** text-to-video (`wan2.7-t2v`), a refreshed 236-provider catalog (OrcaRouter, Wafer AI, OpenAdapter, dit.ai, TokenRouter, …), Vertex AI media generation (speech / transcription / music / video), and one-click account import from CLIProxyAPI (`~/.cli-proxy-api/`). → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **⚡ Local performance & infra** — a one-click local Redis launcher (`omniroute redis up`, plus a dashboard Redis panel), one-click **Cloudflare Workers** and **Deno Deploy** relay deployers wired into the proxy pool, and an optional Bifrost Go sidecar that offloads the hottest relay path (`BIFROST_BASE_URL`, with automatic fallback to the TypeScript path on timeout) — now with a relay-backend selector (`OMNIROUTE_RELAY_BACKEND=ts|bifrost|auto`) so the `/v1/relay` endpoint stays the stable surface while choosing the fastest backend internally. → [Environment](docs/reference/ENVIRONMENT.md)
@@ -348,7 +348,7 @@ Result: 4 layers of fallback = zero downtime
-> The most complete catalog of any open-source router: **237 providers**, **50+ with a free tier**, **11 free forever**.
+> The most complete catalog of any open-source router: **236 providers**, **50+ with a free tier**, **11 free forever**.
@@ -804,7 +804,7 @@ Compression: aggressive (~50%) → double your free quota · Cost: $0/mo
**Will I be charged by OmniRoute?** No — it's free, open-source software on your machine. You only pay paid providers directly. OmniRoute has no billing system.
**Are FREE providers really unlimited?** Mostly — Qoder, Pollinations, LongCat, and Cloudflare are free with no per-account credit cap. Kiro is free too but capped at ~50 credits/month per account. Stack multiple free providers in a combo and auto-fallback keeps you serving for $0.
**Will compression hurt quality?** No — it only compresses the **input**; code, URLs, JSON are always protected.
-**Does it work where AI is blocked?** Yes — 3-level proxy + 1proxy marketplace reach all 237 providers.
+**Does it work where AI is blocked?** Yes — 3-level proxy + 1proxy marketplace reach all 236 providers.
📖 [User Guide](docs/guides/USER_GUIDE.md) · [API Reference](docs/reference/API_REFERENCE.md) · [Environment Config](docs/reference/ENVIRONMENT.md)
diff --git a/docs/reference/FREE_TIERS.md b/docs/reference/FREE_TIERS.md
index 5951e1b1d7..5651dd658f 100644
--- a/docs/reference/FREE_TIERS.md
+++ b/docs/reference/FREE_TIERS.md
@@ -159,7 +159,6 @@ A 50-agent web-research pass (official docs + last-7-days news, adversarially ve
| `freemodel-dev` | unknown | The Terms of Service page (freemodel.dev/terms) returned only a header with no readable content via WebFetch; no clause… |
| `gitlawb` | unknown | No ToS or acceptable-use policy found; proxy/resale restrictions unknown — assume caution for self-hosted proxy use. |
| `liquid` | unknown | No hosted API exists to proxy; open-source model commercial use is free for orgs under $10M annual revenue. No self-hos… |
-| `phind` | unknown | Service is discontinued; ToS is no longer relevant. No proxy use is possible. |
| `theoldllm` | unknown | No terms of service document was found on the site; proxying, resale, or self-hosted use policy is entirely undocumente… |
| `yi` | unknown | ToS not publicly accessible without login; no proxy/resale clauses could be reviewed. Self-hosted personal proxy use st… |
| `comfyui` | ok | GPL-3.0 open-source license explicitly permits self-hosted personal proxy use; Comfy Org ToS confirms commercial use of… |
@@ -308,7 +307,7 @@ A 50-agent web-research pass (official docs + last-7-days news, adversarially ve
- **`nvidia`** — The "40 RPM, 70+ models" rate limit element matches the catalog, but the freeNote framing as a simple dev-access tier undersells that the old one-time credit pool has been removed — access is now tru…
- **`ollama-cloud`** — Our shipped freeNote is "(none)" — this is stale. Ollama Cloud launched a cloud inference product with a genuine free tier that provides light weekly GPU-time-based access to hosted open models.
- **`openrouter`** — RPD tightened from 200 to 50 for zero-credit accounts (RPM unchanged at 20). The catalog note was accurate on RPM but overstated the RPD by 4x for the no-credits baseline tier.
-- **`phind`** — Our shipped freeNote describes an active free chat/code-search service — but Phind is fully discontinued as of January 16, 2026. The catalog entry should be marked discontinued and removed from activ…
+- **`phind`** — Phind shut down on January 16, 2026. The provider has now been **fully removed** from the catalog (registry, executor, and both the web-cookie and API-key catalog entries) — matching the dead-service-removal precedent (#5246 Gemini CLI).
- **`pollinations`** — Partially matches — the "no API key required" claim is still true for anonymous access, but the catalog freeNote omits that: (1) rate limits do apply (interval throttle of ~1 req/6-15s for anonymous …
- **`predibase`** — The shipped freeNote ($25 free trial credits, 30-day validity) still matches current documentation. However, the catalog omits the concurrent 20,000 tokens/day serverless rate limit that applies duri…
- **`publicai`** — The shipped freeNote ("Free community inference tier") is broadly accurate but understates the specificity: the 20 RPM rate limit is now documented. No major tightening found; the service remains fre…
diff --git a/docs/reference/PROVIDER_REFERENCE.md b/docs/reference/PROVIDER_REFERENCE.md
index f95ab0ff09..dbdee596c2 100644
--- a/docs/reference/PROVIDER_REFERENCE.md
+++ b/docs/reference/PROVIDER_REFERENCE.md
@@ -10,7 +10,7 @@ lastUpdated: 2026-06-30
> Regenerate with: `npm run gen:provider-reference`
> **Last generated:** 2026-06-30
-Total providers: **237**. See category breakdown below.
+Total providers: **236**. See category breakdown below.
## Categories
@@ -56,7 +56,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `windsurf` | `ws` | Windsurf (Devin CLI) | OAuth | [link](https://windsurf.com) | In the Windsurf / VS Code IDE, open the command palette and run `Windsurf: Provide Auth Token` (or click the Jupyter "Get Windsurf Authentication Token" button), then copy the shown token and paste it here. Note: opening windsurf.com/show-auth-token directly only renders a "Redirecting" page — the IDE must initiate the flow (it adds a `?state=...` param) for the token to appear. |
| `zed` | `zd` | Zed IDE | OAuth | [link](https://zed.dev) | Zed stores LLM provider credentials (OpenAI, Anthropic, Google, Mistral, xAI) in the OS keychain. Use the Import button below to discover and import them automatically. |
-## Web Cookie Providers (24)
+## Web Cookie Providers (23)
| ID | Alias | Name | Tags | Website | Notes |
|----|-------|------|------|---------|-------|
@@ -75,9 +75,8 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `inner-ai` | `in-ai` | Inner.ai (Subscription) | Web cookie | [link](https://app.innerai.com) | Paste your token cookie and email separated by a space: open DevTools → Application → Cookies → .innerai.com, copy the token value, then append a space and your Inner.ai login email. Example: eyJhbG... user@example.com |
| `kimi-web` | `kimi-web` | Kimi Web (Moonshot AI) | Web cookie | [link](https://kimi.moonshot.cn) | Paste your session cookie from kimi.moonshot.cn (DevTools → Application → Cookies) |
| `lmarena` | `lma` | LMArena (Free) | Web cookie | [link](https://lmarena.ai) | Paste the full Cookie header from lmarena.ai (DevTools → Network → request → Cookie). The session is now split across arena-auth-prod-v1.0, .1, … — copy the whole header. Optional — works with free tier for basic comparisons. |
-| `muse-spark-web` | `ms-web` | Muse Spark Web (Meta AI) | Web cookie | [link](https://www.meta.ai) | Paste your abra_sess value or full cookie header from meta.ai |
+| `muse-spark-web` | `ms-web` | Muse Spark Web (Meta AI) | Web cookie | [link](https://www.meta.ai) | Paste your ecto_1_sess value or full cookie header from meta.ai |
| `perplexity-web` | `pplx-web` | Perplexity Web (Pro/Max) | Web cookie | [link](https://www.perplexity.ai) | Paste your __Secure-next-auth.session-token cookie value from perplexity.ai |
-| `phind` | `ph` | Phind (Free) | Web cookie | [link](https://www.phind.com) | ⚠️ **DEPRECATED.** Phind shut down its API (2026-01); the /api/chat endpoint no longer serves (sweep 2026-06-19). |
| `poe-web` | `poe` | Poe Web (Subscription) | Web cookie | [link](https://poe.com) | Paste your p-b cookie value from poe.com (DevTools → Application → Cookies → p-b) |
| `qwen-web` | `qwen-web` | Qwen Web (Free) | Web cookie | [link](https://chat.qwen.ai) | Open chat.qwen.ai, log in, then open DevTools → Application → Local Storage → copy the "token" value (or use tongyi_sso_ticket cookie as Bearer token). |
| `t3-web` | `t3chat` | t3.chat (Pro/Free) | Web cookie | [link](https://t3.chat) | Open t3.chat in your browser, log in, then open DevTools → Application → Local Storage → https://t3.chat. Copy the value of 'convex-session-id'. Also open DevTools → Network, copy the Cookie header from any request. Paste both values here. See provider setup docs for a step-by-step guide. |
@@ -85,7 +84,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `venice-web` | `ven` | Venice Web (Privacy) | Web cookie | [link](https://venice.ai) | Paste your session cookie from venice.ai (DevTools → Application → Cookies) |
| `zenmux-free` | `zmf` | ZenMux Free (Web) | Web cookie | [link](https://zenmux.ai) | Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days. |
-## API Key Providers (paid / paid-with-free-credits) (160)
+## API Key Providers (paid / paid-with-free-credits) (158)
| ID | Alias | Name | Tags | Website | Notes |
|----|-------|------|------|---------|-------|
@@ -155,7 +154,6 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `hackclub` | `hc` | Hackclub AI | API key, aggregator | [link](https://ai.hackclub.com) | Sign in with your Hack Club account at ai.hackclub.com. |
| `haiper` | `hp` | Haiper | API key, video | [link](https://haiper.ai) | Get API key at haiper.ai/haiper-api |
| `heroku` | `heroku` | Heroku AI | API key, enterprise | [link](https://www.heroku.com) | — |
-| `huggingchat` | `huggingchat` | HuggingChat | API key | [link](https://huggingface.co/chat) | No API key required for basic access. |
| `huggingface` | `hf` | HuggingFace | API key | [link](https://huggingface.co) | Free Inference API for thousands of models (Whisper, VITS, SDXL…) |
| `hyperbolic` | `hyp` | Hyperbolic | API key | [link](https://hyperbolic.xyz) | $1-5 trial credits on signup for serverless inference |
| `ideogram` | `ideo` | Ideogram | API key | [link](https://ideogram.ai) | Get API key at ideogram.ai/docs/api |
@@ -203,7 +201,6 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `orcarouter` | `orcarouter` | OrcaRouter | API key | [link](https://www.orcarouter.ai) | — |
| `ovhcloud` | `ovh` | OVHcloud AI | API key | [link](https://www.ovhcloud.com) | — |
| `perplexity` | `pplx` | Perplexity | API key | [link](https://www.perplexity.ai) | — |
-| `phind` | `phind` | Phind | API key | [link](https://phind.com) | Get API key at phind.com |
| `piapi` | `pi` | PiAPI | API key, aggregator | [link](https://piapi.ai) | — |
| `pioneer` | `pn` | Pioneer AI | API key | [link](https://pioneer.ai) | $75 free usage credits — no credit card required |
| `poe` | `poe` | Poe | API key, aggregator | [link](https://creator.poe.com/api-reference) | Bearer API key for the Poe OpenAI-compatible API. |
diff --git a/open-sse/config/providers/index.ts b/open-sse/config/providers/index.ts
index 062572fb68..ac9847e86d 100644
--- a/open-sse/config/providers/index.ts
+++ b/open-sse/config/providers/index.ts
@@ -81,7 +81,6 @@ import { monsterapiProvider } from "./registry/monsterapi/index.ts";
import { sensenovaProvider } from "./registry/sensenova/index.ts";
import { hyperbolicProvider } from "./registry/hyperbolic/index.ts";
import { lambda_aiProvider } from "./registry/lambda-ai/index.ts";
-import { phindProvider } from "./registry/phind/index.ts";
import { t3_webProvider } from "./registry/t3-web/index.ts";
import { klusterProvider } from "./registry/kluster/index.ts";
import { iflytekProvider } from "./registry/iflytek/index.ts";
@@ -253,7 +252,6 @@ export const REGISTRY: Record = {
sensenova: sensenovaProvider,
hyperbolic: hyperbolicProvider,
"lambda-ai": lambda_aiProvider,
- phind: phindProvider,
"t3-web": t3_webProvider,
kluster: klusterProvider,
iflytek: iflytekProvider,
diff --git a/open-sse/config/providers/registry/phind/index.ts b/open-sse/config/providers/registry/phind/index.ts
deleted file mode 100644
index 5ebb35c207..0000000000
--- a/open-sse/config/providers/registry/phind/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { RegistryEntry } from "../../shared.ts";
-
-export const phindProvider: RegistryEntry = {
- id: "phind",
- alias: "ph",
- format: "openai",
- executor: "phind",
- baseUrl: "https://www.phind.com/api/chat",
- authType: "apikey",
- authHeader: "cookie",
- models: [
- { id: "phind-model", name: "Phind Model (Auto)" },
- { id: "gpt-4o", name: "GPT-4o (via Phind)" },
- { id: "claude-3.5-sonnet", name: "Claude 3.5 Sonnet (via Phind)" },
- ],
-};
diff --git a/open-sse/executors/index.ts b/open-sse/executors/index.ts
index 7da5c49b9b..65d09889e3 100644
--- a/open-sse/executors/index.ts
+++ b/open-sse/executors/index.ts
@@ -40,7 +40,6 @@ import { T3ChatWebExecutor } from "./t3-chat-web.ts";
import { ClaudeWebExecutor } from "./claude-web.ts";
import { InnerAiExecutor } from "./inner-ai.ts";
import { HuggingChatExecutor } from "./huggingchat.ts";
-import { PhindExecutor } from "./phind.ts";
import { PoeWebExecutor } from "./poe-web.ts";
import { VeniceWebExecutor } from "./venice-web.ts";
import { V0VercelWebExecutor } from "./v0-vercel-web.ts";
@@ -128,8 +127,6 @@ const executors = {
"in-ai": new InnerAiExecutor(), // Alias
huggingchat: new HuggingChatExecutor(),
hc: new HuggingChatExecutor(), // Alias
- phind: new PhindExecutor(),
- ph: new PhindExecutor(), // Alias
"poe-web": new PoeWebExecutor(),
poe: new PoeWebExecutor(), // Alias
"venice-web": new VeniceWebExecutor(),
diff --git a/open-sse/executors/phind.ts b/open-sse/executors/phind.ts
deleted file mode 100644
index 778178414a..0000000000
--- a/open-sse/executors/phind.ts
+++ /dev/null
@@ -1,250 +0,0 @@
-/**
- * PhindExecutor — Free Dev-Focused AI Chat via phind.com
- *
- * Routes requests through Phind's chat API.
- * Free tier available. Uses session cookie for auth.
- *
- * Endpoint: POST https://www.phind.com/api/agent
- * Auth: Session cookie from phind.com
- * SSE response with data: prefixed JSON chunks (OpenAI-compatible delta format)
- */
-import { BaseExecutor, type ExecuteInput } from "./base.ts";
-import { makeExecutorErrorResult as makeErrorResult, normalizeCookie } from "../utils/error.ts";
-
-const BASE_URL = "https://www.phind.com";
-const CHAT_URL = `${BASE_URL}/api/agent`;
-const USER_AGENT =
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
-
-export class PhindExecutor extends BaseExecutor {
- constructor() {
- super("phind", { id: "phind", baseUrl: "https://www.phind.com" });
- }
-
- async execute(input: ExecuteInput) {
- const { body, credentials, signal, stream: wantStream } = input;
- const bodyObj = (body || {}) as Record;
- const rawCookie = String(credentials?.apiKey ?? "").trim();
- const cookie = normalizeCookie(rawCookie);
-
- // Build Phind-format messages
- const messages = (bodyObj.messages as Array<{ role: string; content: string }>) || [];
- const phindMessages = messages.map((m) => ({ role: m.role, content: m.content }));
- const modelId = (bodyObj.model as string) || "phind-model";
- // Last user message as userInput (Phind expects this field)
- const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
- const userInput = lastUserMsg?.content || "";
-
- const reqBody = {
- userInput,
- messages: phindMessages,
- requestedModel: modelId,
- webSearchMode: "auto",
- isChromeExtension: false,
- language: "en-US",
- date: new Date().toISOString(),
- };
-
- const reqHeaders: Record = {
- "Content-Type": "application/json;charset=UTF-8",
- "User-Agent": USER_AGENT,
- Accept: "text/event-stream",
- Referer: `${BASE_URL}/`,
- Origin: BASE_URL,
- "Sec-Fetch-Dest": "empty",
- "Sec-Fetch-Mode": "cors",
- "Sec-Fetch-Site": "same-origin",
- };
- if (cookie) reqHeaders.Cookie = cookie;
-
- let upstream: Response;
- try {
- upstream = await fetch(CHAT_URL, {
- method: "POST",
- headers: reqHeaders,
- body: JSON.stringify(reqBody),
- signal,
- });
- } catch (err) {
- return makeErrorResult(
- 502,
- `Phind fetch failed: ${err instanceof Error ? err.message : "unknown"}`,
- body,
- CHAT_URL
- );
- }
-
- if (!upstream.ok) {
- const errText = await upstream.text().catch(() => "");
- return makeErrorResult(upstream.status, `Phind error: ${errText}`, body, CHAT_URL);
- }
-
- // Phind always returns SSE — parse it for both streaming and non-streaming
- if (!upstream.body) {
- return makeErrorResult(502, "Phind returned empty response body", body, CHAT_URL);
- }
-
- if (!wantStream) {
- // Collect all SSE chunks into a single response
- const reader = upstream.body.getReader();
- const decoder = new TextDecoder();
- let buffer = "";
- let fullText = "";
-
- try {
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- buffer += decoder.decode(value, { stream: true });
- const lines = buffer.split("\n");
- buffer = lines.pop() || "";
- for (const line of lines) {
- if (!line.startsWith("data:")) continue;
- const data = line.slice(5).trim();
- if (data === "[DONE]") continue;
- try {
- const parsed = JSON.parse(data);
- const text = parsed.choices?.[0]?.delta?.content || parsed.content || "";
- if (text) fullText += text;
- } catch {
- // Skip unparseable chunks
- }
- }
- }
- } catch (err) {
- if (!signal?.aborted) {
- return makeErrorResult(
- 502,
- `Phind stream read failed: ${err instanceof Error ? err.message : "unknown"}`,
- body,
- CHAT_URL
- );
- }
- }
-
- return {
- response: new Response(
- JSON.stringify({
- id: `chatcmpl-ph-${Date.now()}`,
- object: "chat.completion",
- created: Math.floor(Date.now() / 1000),
- model: modelId,
- choices: [
- {
- index: 0,
- message: { role: "assistant", content: fullText },
- finish_reason: "stop",
- },
- ],
- usage: {
- prompt_tokens: Math.ceil((userInput || "").length / 4),
- completion_tokens: Math.ceil(fullText.length / 4),
- total_tokens: Math.ceil(((userInput || "").length + fullText.length) / 4),
- },
- }),
- { headers: { "Content-Type": "application/json" } }
- ),
- url: CHAT_URL,
- headers: reqHeaders,
- transformedBody: reqBody,
- };
- }
-
- // Streaming: transform Phind SSE to OpenAI format
- const encoder = new TextEncoder();
- const decoder = new TextDecoder();
- const sseStream = new ReadableStream({
- async start(controller) {
- const reader = upstream.body!.getReader();
- let buffer = "";
- try {
- // Initial role chunk
- controller.enqueue(
- encoder.encode(
- `data: ${JSON.stringify({
- id: `chatcmpl-ph-${Date.now()}`,
- object: "chat.completion.chunk",
- created: Math.floor(Date.now() / 1000),
- model: modelId,
- choices: [{ index: 0, delta: { role: "assistant" }, finish_reason: null }],
- })}\n\n`
- )
- );
-
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
-
- buffer += decoder.decode(value, { stream: true });
- const lines = buffer.split("\n");
- buffer = lines.pop() || "";
-
- for (const line of lines) {
- if (!line.startsWith("data:")) continue;
- const data = line.slice(5).trim();
- if (data === "[DONE]") {
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
- continue;
- }
- try {
- const parsed = JSON.parse(data);
- const text = parsed.choices?.[0]?.delta?.content || parsed.content || "";
- if (text) {
- const chunk = {
- id: `chatcmpl-ph-${Date.now()}`,
- object: "chat.completion.chunk",
- created: Math.floor(Date.now() / 1000),
- model: modelId,
- choices: [{ index: 0, delta: { content: text }, finish_reason: null }],
- };
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
- }
- } catch {
- // Skip unparseable chunks
- }
- }
- }
- } catch (err) {
- if (!signal?.aborted) {
- controller.enqueue(
- encoder.encode(
- `data: ${JSON.stringify({
- id: `chatcmpl-ph-${Date.now()}`,
- object: "chat.completion.chunk",
- created: Math.floor(Date.now() / 1000),
- model: modelId,
- choices: [
- {
- index: 0,
- delta: {
- content: `[Stream error: ${err instanceof Error ? err.message : String(err)}]`,
- },
- finish_reason: "stop",
- },
- ],
- })}\n\n`
- )
- );
- }
- } finally {
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
- controller.close();
- }
- },
- });
-
- return {
- response: new Response(sseStream, {
- status: 200,
- headers: {
- "Content-Type": "text/event-stream",
- "Cache-Control": "no-cache",
- "X-Accel-Buffering": "no",
- },
- }),
- url: CHAT_URL,
- headers: reqHeaders,
- transformedBody: reqBody,
- };
- }
-}
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index 4baa54f63a..12a34a0ab4 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -4818,8 +4818,6 @@
"checkWebToken": "Check token",
"huggingchatLabel": "HuggingChat (Free)",
"huggingchatDesc": "Free LLM chat via huggingface.co/chat",
- "phindLabel": "Phind (Free)",
- "phindDesc": "Free dev-focused AI chat",
"poeWebLabel": "Poe Web",
"poeWebDesc": "Multi-model chat via poe.com",
"veniceWebLabel": "Venice Web",
diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json
index 90da32b420..b8a544b12f 100644
--- a/src/i18n/messages/it.json
+++ b/src/i18n/messages/it.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Controlla il gettone",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json
index 9ea5764886..418afb8712 100644
--- a/src/i18n/messages/ja.json
+++ b/src/i18n/messages/ja.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "トークンをチェックする",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json
index 2856459b1a..3185cf6f7d 100644
--- a/src/i18n/messages/ko.json
+++ b/src/i18n/messages/ko.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "토큰 확인",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/mr.json b/src/i18n/messages/mr.json
index f32ca30f44..619f356739 100644
--- a/src/i18n/messages/mr.json
+++ b/src/i18n/messages/mr.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "टोकन तपासा",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/ms.json b/src/i18n/messages/ms.json
index 7a8bfabdfb..f77759ef3a 100644
--- a/src/i18n/messages/ms.json
+++ b/src/i18n/messages/ms.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Semak token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json
index 3d7b367ddf..9c2d5c7a2e 100644
--- a/src/i18n/messages/nl.json
+++ b/src/i18n/messages/nl.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Controleer token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/no.json b/src/i18n/messages/no.json
index 9251bbb62f..1d2a66826a 100644
--- a/src/i18n/messages/no.json
+++ b/src/i18n/messages/no.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Sjekk token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/phi.json b/src/i18n/messages/phi.json
index d185e38eeb..20903e812e 100644
--- a/src/i18n/messages/phi.json
+++ b/src/i18n/messages/phi.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Suriin ang token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/pl.json b/src/i18n/messages/pl.json
index 0d85a35810..105eabf5d0 100644
--- a/src/i18n/messages/pl.json
+++ b/src/i18n/messages/pl.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Sprawdź token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json
index 6a176be8e5..805d8c03a9 100644
--- a/src/i18n/messages/pt-BR.json
+++ b/src/i18n/messages/pt-BR.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Verifique o token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json
index 6970574c44..8bcae514c7 100644
--- a/src/i18n/messages/pt.json
+++ b/src/i18n/messages/pt.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Verifique o token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/ro.json b/src/i18n/messages/ro.json
index 20d7e28ed0..2668ee7132 100644
--- a/src/i18n/messages/ro.json
+++ b/src/i18n/messages/ro.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Verificați jetonul",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json
index e35c7ed60c..2c5c0f7568 100644
--- a/src/i18n/messages/ru.json
+++ b/src/i18n/messages/ru.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Проверить токен",
"huggingchatLabel": "HuggingChat (Бесплатно)",
"huggingchatDesc": "Бесплатный LLM чат через huggingface.co/chat",
- "phindLabel": "Phind (Бесплатно)",
- "phindDesc": "Бесплатный AI-чат для разработчиков",
"poeWebLabel": "Poe Web",
"poeWebDesc": "Многофункциональный чат через poe.com",
"veniceWebLabel": "Венеция Веб",
diff --git a/src/i18n/messages/sk.json b/src/i18n/messages/sk.json
index 5bcb0b2e13..bf043d2359 100644
--- a/src/i18n/messages/sk.json
+++ b/src/i18n/messages/sk.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Kontrolný token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/sv.json b/src/i18n/messages/sv.json
index 566b27dcd0..3639ea65d6 100644
--- a/src/i18n/messages/sv.json
+++ b/src/i18n/messages/sv.json
@@ -4804,8 +4804,6 @@
"checkWebToken": "Kontrollera token",
"huggingchatLabel": "__MISSING__:HuggingChat (Free)",
"huggingchatDesc": "__MISSING__:Free LLM chat via huggingface.co/chat",
- "phindLabel": "__MISSING__:Phind (Free)",
- "phindDesc": "__MISSING__:Free dev-focused AI chat",
"poeWebLabel": "__MISSING__:Poe Web",
"poeWebDesc": "__MISSING__:Multi-model chat via poe.com",
"veniceWebLabel": "__MISSING__:Venice Web",
diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json
index 6b22afbf50..df4cedc1ad 100644
--- a/src/i18n/messages/zh-CN.json
+++ b/src/i18n/messages/zh-CN.json
@@ -4660,8 +4660,6 @@
"checkWebToken": "检查令牌",
"huggingchatLabel": "HuggingChat(免费)",
"huggingchatDesc": "通过 huggingface.co/chat 免费使用 LLM 聊天",
- "phindLabel": "Phind(免费)",
- "phindDesc": "免费的开发者专用 AI 聊天",
"poeWebLabel": "Poe 网页",
"poeWebDesc": "通过 poe.com 进行多模型聊天",
"veniceWebLabel": "威尼斯网络",
diff --git a/src/lib/arenaEloSync.ts b/src/lib/arenaEloSync.ts
index c57b1106e9..1e24148716 100644
--- a/src/lib/arenaEloSync.ts
+++ b/src/lib/arenaEloSync.ts
@@ -132,7 +132,6 @@ const VENDOR_PREFIXES = [
"alibaba/",
"nvidia/",
"01-ai/",
- "phind/",
"zerox/",
"together/",
"fireworks/",
diff --git a/src/shared/components/lobeProviderIcons.ts b/src/shared/components/lobeProviderIcons.ts
index e37bf7f5e3..d709e2a5c4 100644
--- a/src/shared/components/lobeProviderIcons.ts
+++ b/src/shared/components/lobeProviderIcons.ts
@@ -118,7 +118,6 @@ import OpenCodeMonoIcon from "@lobehub/icons/es/OpenCode/components/Mono";
import OpenRouterMonoIcon from "@lobehub/icons/es/OpenRouter/components/Mono";
import PerplexityColorIcon from "@lobehub/icons/es/Perplexity/components/Color";
import PerplexityMonoIcon from "@lobehub/icons/es/Perplexity/components/Mono";
-import PhindMonoIcon from "@lobehub/icons/es/Phind/components/Mono";
import PoeColorIcon from "@lobehub/icons/es/Poe/components/Color";
import PoeMonoIcon from "@lobehub/icons/es/Poe/components/Mono";
import PollinationsMonoIcon from "@lobehub/icons/es/Pollinations/components/Mono";
@@ -268,7 +267,6 @@ const LOBE_ICON_COMPONENTS = {
OpenCode: { mono: OpenCodeMonoIcon },
OpenRouter: { mono: OpenRouterMonoIcon },
Perplexity: { mono: PerplexityMonoIcon, color: PerplexityColorIcon },
- Phind: { mono: PhindMonoIcon },
Poe: { mono: PoeMonoIcon, color: PoeColorIcon },
Pollinations: { mono: PollinationsMonoIcon },
Qoder: { mono: QoderMonoIcon, color: QoderColorIcon },
@@ -426,7 +424,6 @@ const LOBE_PROVIDER_ALIASES = {
"perplexity-search": "Perplexity",
"perplexity-web": "Perplexity",
poe: "Poe",
- phind: "Phind",
pollinations: "Pollinations",
qoder: "Qoder",
qwen: "Qwen",
diff --git a/src/shared/constants/providers/apikey/frontier-labs.ts b/src/shared/constants/providers/apikey/frontier-labs.ts
index dc99ff4be1..ca3783c995 100644
--- a/src/shared/constants/providers/apikey/frontier-labs.ts
+++ b/src/shared/constants/providers/apikey/frontier-labs.ts
@@ -223,19 +223,6 @@ export const APIKEY_PROVIDERS_FRONTIER = {
hasFree: true,
freeNote: "Free tier: 50 RPM, 500,000 TPM — no credit card",
},
- phind: {
- id: "phind",
- alias: "phind",
- name: "Phind",
- icon: "search",
- color: "#EC4899",
- textIcon: "PH",
- website: "https://phind.com",
- hasFree: false,
- freeNote: "Discontinued 2026 — phind.com shut down (2026-01); no free tier.",
- passthroughModels: true,
- authHint: "Get API key at phind.com",
- },
"arcee-ai": {
id: "arcee-ai",
alias: "arcee",
diff --git a/src/shared/constants/providers/apikey/inference-hosts.ts b/src/shared/constants/providers/apikey/inference-hosts.ts
index fa6c7b30db..67af6afe56 100644
--- a/src/shared/constants/providers/apikey/inference-hosts.ts
+++ b/src/shared/constants/providers/apikey/inference-hosts.ts
@@ -249,19 +249,6 @@ export const APIKEY_PROVIDERS_INFERENCE = {
hasFree: true,
freeNote: "$1 free credits, refreshes every 4 weeks",
},
- huggingchat: {
- id: "huggingchat",
- alias: "huggingchat",
- name: "HuggingChat",
- icon: "chat",
- color: "#FFD21E",
- textIcon: "HC",
- website: "https://huggingface.co/chat",
- hasFree: true,
- freeNote: "Free chat with open models (Llama, Mistral, etc.).",
- passthroughModels: true,
- authHint: "No API key required for basic access.",
- },
monsterapi: {
id: "monsterapi",
alias: "monster",
diff --git a/src/shared/constants/providers/web-cookie.ts b/src/shared/constants/providers/web-cookie.ts
index 901996777e..17ef4a1f6d 100644
--- a/src/shared/constants/providers/web-cookie.ts
+++ b/src/shared/constants/providers/web-cookie.ts
@@ -200,24 +200,6 @@ export const WEB_COOKIE_PROVIDERS = {
"Paste your hf-chat cookie value from huggingface.co/chat (DevTools → Application → Cookies → hf-chat). Optional — works without auth for basic use.",
riskNoticeVariant: "webCookie",
},
- phind: {
- id: "phind",
- alias: "ph",
- name: "Phind (Free)",
- icon: "auto_awesome",
- color: "#000000",
- textIcon: "PH",
- website: "https://www.phind.com",
- hasFree: false,
- freeNote: "Discontinued 2026 — phind.com shut down (2026-01); no free tier.",
- authHint:
- "Paste your session cookie from phind.com (DevTools → Application → Cookies). Optional — works with free tier.",
- subscriptionRisk: true,
- riskNoticeVariant: "deprecated",
- deprecated: true,
- deprecationReason:
- "Phind shut down its API (2026-01); the /api/chat endpoint no longer serves (sweep 2026-06-19).",
- },
"poe-web": {
id: "poe-web",
alias: "poe",
diff --git a/src/shared/providers/webSessionCredentials.ts b/src/shared/providers/webSessionCredentials.ts
index 84990e6ac2..e8eebb03fe 100644
--- a/src/shared/providers/webSessionCredentials.ts
+++ b/src/shared/providers/webSessionCredentials.ts
@@ -129,13 +129,6 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "hf-chat"],
},
- phind: {
- kind: "cookie",
- credentialName: "phind_session",
- placeholder: "phind_session=... or full Cookie header from phind.com",
- acceptsFullCookieHeader: true,
- storageKeys: ["cookie", "phind_session"],
- },
"poe-web": {
kind: "cookie",
credentialName: "p-b",
diff --git a/tests/snapshots/provider/translate-path.json b/tests/snapshots/provider/translate-path.json
index e1952f24d3..a2139d4a66 100644
--- a/tests/snapshots/provider/translate-path.json
+++ b/tests/snapshots/provider/translate-path.json
@@ -3175,29 +3175,6 @@
"stream": "https://www.perplexity.ai/rest/sse/perplexity_ask"
}
},
- "phind": {
- "format": "openai",
- "headers": {
- "apiKey": {
- "Accept": "text/event-stream",
- "Authorization": "Bearer ",
- "Content-Type": "application/json"
- },
- "nonStream": {
- "Authorization": "Bearer ",
- "Content-Type": "application/json"
- },
- "oauth": {
- "Accept": "text/event-stream",
- "Authorization": "Bearer ",
- "Content-Type": "application/json"
- }
- },
- "url": {
- "nonStream": "https://www.phind.com/api/chat",
- "stream": "https://www.phind.com/api/chat"
- }
- },
"pioneer": {
"format": "openai",
"headers": {
diff --git a/tests/unit/discontinued-providers-2026.test.ts b/tests/unit/discontinued-providers-2026.test.ts
index 0af6bf5e2f..79fef1ead5 100644
--- a/tests/unit/discontinued-providers-2026.test.ts
+++ b/tests/unit/discontinued-providers-2026.test.ts
@@ -9,18 +9,23 @@ import assert from "node:assert";
describe("2026 discontinued free tiers — providers.ts hasFree reconciliation", () => {
it("APIKEY_PROVIDERS dead tiers no longer advertise a free tier", async () => {
const { APIKEY_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
- for (const id of ["chutes", "kluster", "glhf", "phind", "gitlawb", "gitlawb-gmi", "aimlapi", "yi"]) {
+ // These providers still operate (an API key works) but lost their free tier, so
+ // they are KEPT with hasFree:false. phind is NOT here: the whole phind.com service
+ // shut down 2026-01-16, so it was removed entirely (registry/executor/catalogs),
+ // matching the dead-service-removal precedent (#5246 Gemini CLI).
+ for (const id of ["chutes", "kluster", "glhf", "gitlawb", "gitlawb-gmi", "aimlapi", "yi"]) {
const p = (APIKEY_PROVIDERS as Record)[id];
assert.ok(p, `${id} should still exist in APIKEY_PROVIDERS (provider not removed, only its free flag)`);
assert.strictEqual(p.hasFree, false, `${id} should have hasFree:false (discontinued in 2026)`);
}
});
- it("WEB_COOKIE_PROVIDERS phind (web/cookie path) no longer advertises a free tier", async () => {
- const { WEB_COOKIE_PROVIDERS } = await import("../../src/shared/constants/providers.ts");
- const p = (WEB_COOKIE_PROVIDERS as Record)["phind"];
- assert.ok(p, "phind should still exist in WEB_COOKIE_PROVIDERS");
- assert.strictEqual(p.hasFree, false, "phind web/cookie should have hasFree:false (phind.com shut down 2026-01)");
+ it("phind is fully removed (service shut down 2026-01) from both catalogs", async () => {
+ const { APIKEY_PROVIDERS, WEB_COOKIE_PROVIDERS } = await import(
+ "../../src/shared/constants/providers.ts"
+ );
+ assert.ok(!("phind" in APIKEY_PROVIDERS), "phind must not be in APIKEY_PROVIDERS");
+ assert.ok(!("phind" in WEB_COOKIE_PROVIDERS), "phind must not be in WEB_COOKIE_PROVIDERS");
});
it("intentionally-kept providers still advertise free (genuinely free / ToS-flagged, not flipped)", async () => {
diff --git a/tests/unit/executor-phind.test.ts b/tests/unit/executor-phind.test.ts
deleted file mode 100644
index da1a844cdc..0000000000
--- a/tests/unit/executor-phind.test.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { describe, it } from "node:test";
-import assert from "node:assert/strict";
-
-const mod = await import("../../open-sse/executors/phind.ts");
-
-describe("PhindExecutor", () => {
- it("can be instantiated", () => {
- const executor = new mod.PhindExecutor();
- assert.ok(executor);
- });
-
- it("execute returns proper shape on missing cookie (fetch fails)", async () => {
- const executor = new mod.PhindExecutor();
- try {
- const result = await executor.execute({
- model: "phind-model",
- body: { messages: [{ role: "user", content: "hi" }] },
- stream: false,
- credentials: { apiKey: "" },
- signal: null,
- });
- assert.ok(result.response instanceof Response);
- assert.ok(typeof result.url === "string");
- assert.ok(typeof result.headers === "object");
- } catch {
- // Network error expected in test env
- }
- });
-
- it("execute builds correct URL", async () => {
- const executor = new mod.PhindExecutor();
- try {
- const result = await executor.execute({
- model: "test",
- body: { messages: [{ role: "user", content: "hi" }] },
- stream: false,
- credentials: { apiKey: "fake" },
- signal: null,
- });
- assert.ok(result.url.includes("phind.com/api/agent"));
- } catch {
- // expected
- }
- });
-});
diff --git a/tests/unit/executor-web-cookie-sweep.test.ts b/tests/unit/executor-web-cookie-sweep.test.ts
index 49050e4a8d..94bcd67f63 100644
--- a/tests/unit/executor-web-cookie-sweep.test.ts
+++ b/tests/unit/executor-web-cookie-sweep.test.ts
@@ -60,7 +60,6 @@ const FAKE_CREDS: Record = {
"inner-ai": "fake-audit-sweep user@example.com",
"adapta-web": "__client=fake-audit-sweep",
huggingchat: "hf-chat=fake-audit-sweep",
- phind: "fake-audit-sweep",
"poe-web": "p-b=fake-audit-sweep",
"venice-web": "fake-audit-sweep",
"v0-vercel-web": "fake-audit-sweep",
diff --git a/tests/unit/provider-alias-uniqueness.test.ts b/tests/unit/provider-alias-uniqueness.test.ts
index 7aeb68bdae..25628a84cb 100644
--- a/tests/unit/provider-alias-uniqueness.test.ts
+++ b/tests/unit/provider-alias-uniqueness.test.ts
@@ -18,7 +18,12 @@ import test from "node:test";
import assert from "node:assert/strict";
import { PROVIDER_ID_TO_ALIAS } from "../../open-sse/config/providerModels.ts";
-import { resolveProviderId, getProviderAlias } from "../../src/shared/constants/providers.ts";
+import {
+ resolveProviderId,
+ getProviderAlias,
+ APIKEY_PROVIDERS,
+ WEB_COOKIE_PROVIDERS,
+} from "../../src/shared/constants/providers.ts";
test("no two provider IDs share the same alias in the open-sse registry", () => {
const aliasToIds = new Map();
@@ -62,3 +67,19 @@ test("src/shared providers map resolves the same aliases unambiguously", () => {
assert.equal(getProviderAlias("kimi"), "kimi");
assert.equal(getProviderAlias("hackclub"), "hc");
});
+
+test("no provider id is registered in both the API-key and web-cookie catalogs", () => {
+ // A provider belongs to exactly one auth category; the same id in both catalogs
+ // renders the provider twice in the dashboard (once per section). huggingchat
+ // regressed this way (its API-key counterpart is the separate `huggingface`
+ // Inference API id), so it must live ONLY in WEB_COOKIE_PROVIDERS.
+ const apikeyIds = new Set(Object.keys(APIKEY_PROVIDERS));
+ const overlap = Object.keys(WEB_COOKIE_PROVIDERS).filter((id) => apikeyIds.has(id));
+ assert.deepEqual(overlap, [], `Providers duplicated across catalogs: ${overlap.join(", ")}`);
+
+ assert.ok("huggingchat" in WEB_COOKIE_PROVIDERS, "huggingchat must be in the web-cookie catalog");
+ assert.ok(
+ !("huggingchat" in APIKEY_PROVIDERS),
+ "huggingchat must NOT be in the API-key catalog (use `huggingface` for the API key path)"
+ );
+});
diff --git a/tests/unit/providers-constants-split.test.ts b/tests/unit/providers-constants-split.test.ts
index 5888538492..eb4e425673 100644
--- a/tests/unit/providers-constants-split.test.ts
+++ b/tests/unit/providers-constants-split.test.ts
@@ -31,12 +31,12 @@ test("barrel still exports every catalog + key helpers", () => {
}
});
-test("APIKEY_PROVIDERS merges the 6 family files into 160 entries (no loss / no dup)", async () => {
+test("APIKEY_PROVIDERS merges the 6 family files into 158 entries (no loss / no dup)", async () => {
const keys = Object.keys((P as Record).APIKEY_PROVIDERS);
- assert.equal(keys.length, 160);
- assert.equal(new Set(keys).size, 160, "duplicate keys after spread-merge");
+ assert.equal(keys.length, 158);
+ assert.equal(new Set(keys).size, 158, "duplicate keys after spread-merge");
// the merged object's entry-count equals the sum of the 6 semantic family files; families are a
- // strict partition (every provider in exactly one), so the sum must be exactly 160.
+ // strict partition (every provider in exactly one), so the sum must be exactly 158.
const families: [string, string][] = [
["gateways", "APIKEY_PROVIDERS_GATEWAYS"],
["frontier-labs", "APIKEY_PROVIDERS_FRONTIER"],
@@ -56,7 +56,7 @@ test("APIKEY_PROVIDERS merges the 6 family files into 160 entries (no loss / no
seen.add(k);
}
}
- assert.equal(famTotal, 160, "families must partition all 160 providers");
+ assert.equal(famTotal, 158, "families must partition all 158 providers");
});
test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => {
diff --git a/tests/unit/web-cookie-providers-new.test.ts b/tests/unit/web-cookie-providers-new.test.ts
index 2baa240f33..fcaff2a212 100644
--- a/tests/unit/web-cookie-providers-new.test.ts
+++ b/tests/unit/web-cookie-providers-new.test.ts
@@ -2,7 +2,6 @@ import test from "node:test";
import assert from "node:assert/strict";
const { HuggingChatExecutor } = await import("../../open-sse/executors/huggingchat.ts");
-const { PhindExecutor } = await import("../../open-sse/executors/phind.ts");
const { PoeWebExecutor } = await import("../../open-sse/executors/poe-web.ts");
const { VeniceWebExecutor } = await import("../../open-sse/executors/venice-web.ts");
const { V0VercelWebExecutor } = await import("../../open-sse/executors/v0-vercel-web.ts");
@@ -97,13 +96,6 @@ test("HuggingChat executor is registered", () => {
assert.ok(executor instanceof HuggingChatExecutor);
});
-test("Phind executor is registered", () => {
- assert.ok(hasSpecializedExecutor("phind"));
- assert.ok(hasSpecializedExecutor("ph"));
- const executor = getExecutor("phind");
- assert.ok(executor instanceof PhindExecutor);
-});
-
test("Poe Web executor is registered", () => {
assert.ok(hasSpecializedExecutor("poe-web"));
assert.ok(hasSpecializedExecutor("poe"));
@@ -149,11 +141,6 @@ test("HuggingChat sets correct provider", () => {
assert.equal(executor.getProvider(), "huggingchat");
});
-test("Phind sets correct provider", () => {
- const executor = new PhindExecutor();
- assert.equal(executor.getProvider(), "phind");
-});
-
test("Poe Web sets correct provider", () => {
const executor = new PoeWebExecutor();
assert.equal(executor.getProvider(), "poe-web");
@@ -314,41 +301,6 @@ test("HuggingChat: fetch failure returns 502", async () => {
}
});
-// ── Phind Execution Tests ────────────────────────────────────────────────────
-
-test("Phind: streaming returns SSE chunks", async () => {
- const sseData = [
- 'data: {"choices":[{"delta":{"content":"Hello "}}]}',
- 'data: {"choices":[{"delta":{"content":"world"}}]}',
- ];
- const restore = mockFetchCapture(200, mockSSEStream(sseData));
- try {
- const executor = new PhindExecutor();
- const result = await executor.execute({
- ...noopExecuteInput,
- model: "phind-model",
- });
- assert.ok(result.response instanceof Response);
- assert.ok(result.url.includes("phind.com"));
- const text = await result.response.text();
- assert.ok(text.includes("data:"));
- } finally {
- restore.restore();
- }
-});
-
-test("Phind: error response returns error result", async () => {
- const restore = mockFetchCapture(403, "Forbidden");
- try {
- const executor = new PhindExecutor();
- const result = await executor.execute(noopExecuteInput);
- assert.ok(result.response instanceof Response);
- assert.equal(result.response.status, 403);
- } finally {
- restore.restore();
- }
-});
-
// ── Poe Web Execution Tests ──────────────────────────────────────────────────
test("Poe Web: non-streaming returns JSON completion", async () => {
@@ -515,7 +467,6 @@ test("Doubao Web: error response returns error result", async () => {
test("All executors handle Cookie: prefix", async () => {
const executors = [
new HuggingChatExecutor(),
- new PhindExecutor(),
new PoeWebExecutor(),
new VeniceWebExecutor(),
new V0VercelWebExecutor(),
@@ -553,7 +504,6 @@ test("All executors handle Cookie: prefix", async () => {
test("All executors handle bare cookie value", async () => {
const executors = [
new HuggingChatExecutor(),
- new PhindExecutor(),
new PoeWebExecutor(),
new VeniceWebExecutor(),
new V0VercelWebExecutor(),